From 170fc17c4356db30187c118aa35d547a84c0f7aa Mon Sep 17 00:00:00 2001 From: 283375 Date: Sun, 23 Aug 2026 01:13:20 +0800 Subject: [PATCH] =?UTF-8?q?fix(server):=20=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E5=AD=97=E6=AE=B5=E6=94=B9=E7=94=A8=20BigInt?= =?UTF-8?q?=20=E4=BB=A5=E4=BF=AE=E5=A4=8D=E6=AF=AB=E7=A7=92=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E6=88=B3=E6=BA=A2=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migration.sql | 12 ++++++++++++ server/prisma/schema.prisma | 15 ++++++++------- server/src/services/auth.ts | 2 +- server/src/utils/mappers.ts | 12 ++++++------ server/src/utils/time.ts | 9 +++++++-- 5 files changed, 34 insertions(+), 16 deletions(-) create mode 100644 server/prisma/migrations/20260822163542_time_fields_bigint/migration.sql diff --git a/server/prisma/migrations/20260822163542_time_fields_bigint/migration.sql b/server/prisma/migrations/20260822163542_time_fields_bigint/migration.sql new file mode 100644 index 0000000..72833a7 --- /dev/null +++ b/server/prisma/migrations/20260822163542_time_fields_bigint/migration.sql @@ -0,0 +1,12 @@ +-- AlterTable +ALTER TABLE "application" ALTER COLUMN "created_at" SET DATA TYPE BIGINT, +ALTER COLUMN "updated_at" SET DATA TYPE BIGINT; + +-- AlterTable +ALTER TABLE "position" ALTER COLUMN "deadline" SET DATA TYPE BIGINT, +ALTER COLUMN "created_at" SET DATA TYPE BIGINT, +ALTER COLUMN "updated_at" SET DATA TYPE BIGINT; + +-- AlterTable +ALTER TABLE "user" ALTER COLUMN "created_at" SET DATA TYPE BIGINT, +ALTER COLUMN "updated_at" SET DATA TYPE BIGINT; diff --git a/server/prisma/schema.prisma b/server/prisma/schema.prisma index 8fb6c59..b8f4bb7 100644 --- a/server/prisma/schema.prisma +++ b/server/prisma/schema.prisma @@ -1,6 +1,7 @@ // recruit-server 数据模型 // Node 侧驼峰, PostgreSQL 侧下划线, 通过 @map / @@map 映射 // 枚举英文标识与 @qnxg-recruit/shared 的 enums 保持一致 +// 时间字段用 BigInt 存入毫秒级时间戳, 默认 Int -> INT4 会溢出, 契约与转换见 utils/time.ts 约定 generator client { provider = "prisma-client-js" @@ -40,8 +41,8 @@ model User { // 简历 / 个人描述字段待业务补充 - createdAt Int @map("created_at") - updatedAt Int @map("updated_at") + createdAt BigInt @map("created_at") + updatedAt BigInt @map("updated_at") applications Application[] admin Admin? @@ -63,14 +64,14 @@ model Position { title String description String /// 展示用招聘截止日期 (原 expiredAt 改名) - deadline Int + deadline BigInt hot Boolean @default(false) status PositionStatus @default(recruiting) /// 软删除标记 disabled Boolean @default(false) - createdAt Int @map("created_at") - updatedAt Int @map("updated_at") + createdAt BigInt @map("created_at") + updatedAt BigInt @map("updated_at") applications Application[] @@map("position") @@ -85,8 +86,8 @@ model Application { /// 软删除标记 disabled Boolean @default(false) - createdAt Int @map("created_at") - updatedAt Int @map("updated_at") + createdAt BigInt @map("created_at") + updatedAt BigInt @map("updated_at") user User @relation(fields: [userID], references: [id]) position Position @relation(fields: [positionID], references: [id]) diff --git a/server/src/services/auth.ts b/server/src/services/auth.ts index 404a35e..fc079e9 100644 --- a/server/src/services/auth.ts +++ b/server/src/services/auth.ts @@ -29,6 +29,6 @@ export const authService = { if (!user) throw new TRPCError({ code: "UNAUTHORIZED", message: "用户不存在" }) const isAdmin = await adminRepo.isAdmin(userID) - return { ...toUserDto(user), isAdmin, createdAt: user.createdAt } + return { ...toUserDto(user), isAdmin, createdAt: Number(user.createdAt) } }, } diff --git a/server/src/utils/mappers.ts b/server/src/utils/mappers.ts index ee426d4..34cd013 100644 --- a/server/src/utils/mappers.ts +++ b/server/src/utils/mappers.ts @@ -3,8 +3,8 @@ import type { ApplicationDto, PositionDto, UserDto } from "@qnxg-recruit/shared" import type { ApplicationWithRelations } from "@/repos/application" /** - * Prisma 实体 → 对外 DTO 的映射. 移除内部字段 (外键 / 软删标记), - * 时间已是 ms 时间戳 (Int), 直接透传 + * Prisma 实体 → 对外 DTO 的映射. 移除内部字段 (外键 / 软删标记). + * 时间字段 Prisma 返回 bigint, 按时间约定 (见 utils/time) 转回 number 毫秒 */ export function toUserDto(user: User): UserDto { @@ -20,8 +20,8 @@ export function toPositionDto(position: Position): PositionDto { id: position.id, title: position.title, description: position.description, - createdAt: position.createdAt, - deadline: position.deadline, + createdAt: Number(position.createdAt), + deadline: Number(position.deadline), hot: position.hot, status: position.status, } @@ -31,8 +31,8 @@ export function toApplicationDto(app: ApplicationWithRelations): ApplicationDto return { id: app.id, status: app.status, - createdAt: app.createdAt, - updatedAt: app.updatedAt, + createdAt: Number(app.createdAt), + updatedAt: Number(app.updatedAt), user: toUserDto(app.user), position: toPositionDto(app.position), } diff --git a/server/src/utils/time.ts b/server/src/utils/time.ts index 4f4fb1a..56db074 100644 --- a/server/src/utils/time.ts +++ b/server/src/utils/time.ts @@ -1,6 +1,11 @@ /** - * 时间约定: 对外 / DTO 用毫秒级 Unix 时间戳 (number), DB 也存 Int (ms). - * 集中在此转换, 便于日后若改回 DateTime 只动一处 + * 时间约定: 对外 / DTO 统一毫秒级 Unix 时间戳 (number), DB 存 BigInt (ms). + * + * DTO 选 number 而非 bigint 的理由: + * - 前端 `new Date(ms)` 直接按本地时区展示, 不涉 UTC 转换 + * - bigint 无法被 JSON 序列化 (trpc 走默认 JSON, 无 superjson) + * - 毫秒量级远小于 number 安全上限 2^53, 精度无损 + * 因此 Prisma 实体返回的 bigint 在 mapper 层 `Number()` 转回, 见 utils/mappers. */ /**