ソースを参照

♻️ refactor(schema): enhance type definitions and add new fields to user card schemas

- 为所有数字类型添加显式类型注解,提高类型安全性
- 优化关联数据定义,使用Schema.pick替代重复的对象定义
- 引入UserSchema和AgentSchema依赖,统一关联数据结构

✨ feat(user-card): add new fields to user card schema

- 添加sjtCardNo字段,支持盛京通卡卡号存储
- 添加authCode字段,支持付款码存储
- 优化agentId字段定义,增加optional()约束

♻️ refactor(user-card-balance-record): improve schema definitions

- 添加user和userCard关联字段,支持嵌套查询返回
- 统一数字类型定义,确保类型一致性和安全性
yourname 4 ヶ月 前
コミット
77c09f4142

+ 28 - 24
src/server/modules/user-card-balance-records/user-card-balance-record.schema.ts

@@ -1,4 +1,6 @@
 import { z } from '@hono/zod-openapi';
+import { UserSchema } from '@/server/modules/users/user.schema';
+import { UserCardSchema } from '@/server/modules/user-cards/user-card.schema';
 
 // 基础用户卡余额记录Schema
 const UserCardBalanceRecordSchema = z.object({
@@ -14,15 +16,15 @@ const UserCardBalanceRecordSchema = z.object({
     description: '卡号',
     example: '12345678901234567890'
   }),
-  amount: z.coerce.number().multipleOf(0.01).min(0).max(999999.99).openapi({
+  amount: z.coerce.number<number>().multipleOf(0.01).min(0).max(999999.99).openapi({
     description: '变动金额',
     example: 50.50
   }),
-  amountBefore: z.coerce.number().multipleOf(0.01).min(0).max(999999.99).openapi({
+  amountBefore: z.coerce.number<number>().multipleOf(0.01).min(0).max(999999.99).openapi({
     description: '变动前金额',
     example: 100.00
   }),
-  amountAfter: z.coerce.number().multipleOf(0.01).min(0).max(999999.99).openapi({
+  amountAfter: z.coerce.number<number>().multipleOf(0.01).min(0).max(999999.99).openapi({
     description: '变动后金额',
     example: 150.50
   }),
@@ -30,7 +32,7 @@ const UserCardBalanceRecordSchema = z.object({
     description: '订单号',
     example: 'ORD20240101120000'
   }),
-  type: z.coerce.number().int().min(1).max(2).openapi({
+  type: z.coerce.number<number>().int().min(1).max(2).openapi({
     description: '类型 1消费 2退款',
     example: 1
   }),
@@ -53,7 +55,9 @@ const UserCardBalanceRecordSchema = z.object({
   updatedAt: z.coerce.date().openapi({
     description: '更新时间',
     example: '2024-01-01T12:00:00Z'
-  })
+  }),
+  user: UserSchema.optional(),
+  userCard: UserCardSchema.optional()
 });
 
 // 创建用户卡余额记录DTO
@@ -67,13 +71,13 @@ export const CreateUserCardBalanceRecordDto = UserCardBalanceRecordSchema.pick({
   type: true,
   remark: true
 }).extend({
-  userId: z.coerce.number().int().positive('用户ID必须是正整数'),
+  userId: z.coerce.number<number>().int().positive('用户ID必须是正整数'),
   cardNo: z.string().min(1, '卡号不能为空').max(20, '卡号最多20个字符'),
-  amount: z.coerce.number().multipleOf(0.01).min(0).max(999999.99),
-  amountBefore: z.coerce.number().multipleOf(0.01).min(0).max(999999.99),
-  amountAfter: z.coerce.number().multipleOf(0.01).min(0).max(999999.99),
+  amount: z.coerce.number<number>().multipleOf(0.01).min(0).max(999999.99),
+  amountBefore: z.coerce.number<number>().multipleOf(0.01).min(0).max(999999.99),
+  amountAfter: z.coerce.number<number>().multipleOf(0.01).min(0).max(999999.99),
   orderNo: z.string().min(1, '订单号不能为空').max(32, '订单号最多32个字符'),
-  type: z.coerce.number().int().min(1).max(2),
+  type: z.coerce.number<number>().int().min(1).max(2),
   remark: z.string().max(32, '备注最多32个字符').nullable()
 });
 
@@ -88,28 +92,28 @@ export const UpdateUserCardBalanceRecordDto = UserCardBalanceRecordSchema.pick({
   type: true,
   remark: true
 }).partial().extend({
-  userId: z.coerce.number().int().positive('用户ID必须是正整数').optional(),
+  userId: z.coerce.number<number>().int().positive('用户ID必须是正整数').optional(),
   cardNo: z.string().min(1, '卡号不能为空').max(20, '卡号最多20个字符').optional(),
-  amount: z.coerce.number().multipleOf(0.01).min(0).max(999999.99).optional(),
-  amountBefore: z.coerce.number().multipleOf(0.01).min(0).max(999999.99).optional(),
-  amountAfter: z.coerce.number().multipleOf(0.01).min(0).max(999999.99).optional(),
+  amount: z.coerce.number<number>().multipleOf(0.01).min(0).max(999999.99).optional(),
+  amountBefore: z.coerce.number<number>().multipleOf(0.01).min(0).max(999999.99).optional(),
+  amountAfter: z.coerce.number<number>().multipleOf(0.01).min(0).max(999999.99).optional(),
   orderNo: z.string().min(1, '订单号不能为空').max(32, '订单号最多32个字符').optional(),
-  type: z.coerce.number().int().min(1).max(2).optional(),
+  type: z.coerce.number<number>().int().min(1).max(2).optional(),
   remark: z.string().max(32, '备注最多32个字符').nullable().optional()
 });
 
 // 响应Schema(包含关联数据)
 export const UserCardBalanceRecordResponseSchema = UserCardBalanceRecordSchema.extend({
-  user: z.object({
-    id: z.number().int().positive(),
-    username: z.string(),
-    name: z.string().nullable(),
-    phone: z.string().nullable()
+  user: UserSchema.pick({
+    id: true,
+    username: true,
+    name: true,
+    phone: true
   }).optional(),
-  userCard: z.object({
-    id: z.number().int().positive(),
-    cardNo: z.string(),
-    balance: z.number()
+  userCard: UserCardSchema.pick({
+    id: true,
+    cardNo: true,
+    balance: true
   }).optional()
 });
 

+ 28 - 20
src/server/modules/user-cards/user-card.schema.ts

@@ -1,4 +1,6 @@
 import { z } from '@hono/zod-openapi';
+import { UserSchema } from '@/server/modules/users/user.schema';
+import { AgentSchema } from '@/server/modules/agent/agent.schema';
 
 // 基础用户卡Schema
 const UserCardSchema = z.object({
@@ -10,7 +12,7 @@ const UserCardSchema = z.object({
     description: '用户ID',
     example: 1001
   }),
-  agentId: z.number().int().positive().nullable().openapi({
+  agentId: z.coerce.number().int().positive().nullable().openapi({
     description: '代理商ID',
     example: 5
   }),
@@ -57,7 +59,9 @@ const UserCardSchema = z.object({
   updatedAt: z.coerce.date().openapi({
     description: '更新时间',
     example: '2024-01-01T12:00:00Z'
-  })
+  }),
+  user: UserSchema.optional(),
+  agent: AgentSchema.nullable().optional()
 });
 
 // 创建用户卡DTO
@@ -72,13 +76,15 @@ export const CreateUserCardDto = UserCardSchema.pick({
   balance: true,
   isDefault: true
 }).extend({
-  userId: z.coerce.number().int().positive('用户ID必须是正整数'),
-  agentId: z.coerce.number().int().positive('代理商ID必须是正整数').nullable(),
+  userId: z.coerce.number<number>().int().positive('用户ID必须是正整数'),
+  agentId: z.coerce.number<number>().int().positive('代理商ID必须是正整数').nullable().optional(),
   cardNo: z.string().min(1, '卡号不能为空').max(20, '卡号最多20个字符'),
   password: z.string().min(1, '密码不能为空').max(255, '密码最多255个字符'),
-  state: z.coerce.number().int().min(1).max(2).default(1),
-  balance: z.coerce.number().multipleOf(0.01).min(0).max(999999.99).default(0),
-  isDefault: z.coerce.number().int().min(1).max(2).default(2)
+  sjtCardNo: z.string().max(20, '盛京通卡卡号最多20个字符').nullable().optional(),
+  authCode: z.string().max(16, '付款码最多16个字符').nullable().optional(),
+  state: z.coerce.number<number>().int().min(1).max(2).default(1),
+  balance: z.coerce.number<number>().multipleOf(0.01).min(0).max(999999.99).default(0),
+  isDefault: z.coerce.number<number>().int().min(1).max(2).default(2)
 });
 
 // 更新用户卡DTO
@@ -93,26 +99,28 @@ export const UpdateUserCardDto = UserCardSchema.pick({
   balance: true,
   isDefault: true
 }).partial().extend({
-  userId: z.coerce.number().int().positive('用户ID必须是正整数').optional(),
-  agentId: z.coerce.number().int().positive('代理商ID必须是正整数').nullable().optional(),
+  userId: z.coerce.number<number>().int().positive('用户ID必须是正整数').optional(),
+  agentId: z.coerce.number<number>().int().positive('代理商ID必须是正整数').nullable().optional(),
   cardNo: z.string().min(1, '卡号不能为空').max(20, '卡号最多20个字符').optional(),
   password: z.string().min(1, '密码不能为空').max(255, '密码最多255个字符').optional(),
-  state: z.coerce.number().int().min(1).max(2).optional(),
-  balance: z.coerce.number().multipleOf(0.01).min(0).max(999999.99).optional(),
-  isDefault: z.coerce.number().int().min(1).max(2).optional()
+  sjtCardNo: z.string().max(20, '盛京通卡卡号最多20个字符').nullable().optional(),
+  authCode: z.string().max(16, '付款码最多16个字符').nullable().optional(),
+  state: z.coerce.number<number>().int().min(1).max(2).optional(),
+  balance: z.coerce.number<number>().multipleOf(0.01).min(0).max(999999.99).optional(),
+  isDefault: z.coerce.number<number>().int().min(1).max(2).optional()
 });
 
 // 响应Schema(包含关联数据)
 export const UserCardResponseSchema = UserCardSchema.extend({
-  user: z.object({
-    id: z.number().int().positive(),
-    username: z.string(),
-    name: z.string().nullable(),
-    phone: z.string().nullable()
+  user: UserSchema.pick({
+    id: true,
+    username: true,
+    name: true,
+    phone: true
   }).optional(),
-  agent: z.object({
-    id: z.number().int().positive(),
-    name: z.string()
+  agent: AgentSchema.pick({
+    id: true,
+    name: true
   }).nullable().optional()
 });