瀏覽代碼

♻️ refactor(users): 分离用户和角色的schema定义到独立文件

- 从user.entity.ts中移除以UserSchema为代表的schema定义到新文件user.schema.ts
- 从role.entity.ts中移除以RoleSchema为代表的schema定义到新文件role.schema.ts
- 在用户相关API中更新schema导入路径,使用新的schema文件

✨ feat(users): 优化用户相关数据传输对象定义

- 创建CreateUserDto和UpdateUserDto,替代之前在API文件中定义的临时schema
- 统一用户列表响应格式为UserListResponse,提高代码复用性
- 优化用户和角色相关schema的结构和注释,增强可维护性

💄 style(users): 格式化JSON响应代码

- 统一错误响应对象的格式和缩进
- 移除多余空行,保持代码风格一致性
yourname 4 月之前
父節點
當前提交
9675067b9a

+ 4 - 4
src/server/api/users/[id]/get.ts

@@ -5,7 +5,7 @@ import { authMiddleware } from '@/server/middleware/auth.middleware';
 import { ErrorSchema } from '@/server/utils/errorHandler';
 import { AppDataSource } from '@/server/data-source';
 import { AuthContext } from '@/server/types/context';
-import { UserSchema } from '@/server/modules/users/user.entity';
+import { UserSchema } from '@/server/modules/users/user.schema';
 
 const userService = new UserService(AppDataSource);
 
@@ -49,9 +49,9 @@ const app = new OpenAPIHono<AuthContext>().openapi(routeDef, async (c) => {
     }
     return c.json(user, 200);
   } catch (error) {
-    return c.json({ 
-      code: 500, 
-      message: error instanceof Error ? error.message : '获取用户详情失败' 
+    return c.json({
+      code: 500,
+      message: error instanceof Error ? error.message : '获取用户详情失败'
     }, 500);
   }
 });

+ 5 - 11
src/server/api/users/[id]/put.ts

@@ -5,7 +5,7 @@ import { authMiddleware } from '@/server/middleware/auth.middleware';
 import { ErrorSchema } from '@/server/utils/errorHandler';
 import { AppDataSource } from '@/server/data-source';
 import { AuthContext } from '@/server/types/context';
-import { UserSchema } from '@/server/modules/users/user.entity';
+import { UserSchema, UpdateUserDto } from '@/server/modules/users/user.schema';
 
 const userService = new UserService(AppDataSource);
 
@@ -17,12 +17,6 @@ const UpdateParams = z.object({
   })
 });
 
-const UpdateUserSchema = UserSchema.omit({ 
-  id: true,
-  createdAt: true,
-  updatedAt: true 
-}).partial();
-
 const routeDef = createRoute({
   method: 'put',
   path: '/{id}',
@@ -32,7 +26,7 @@ const routeDef = createRoute({
     body: {
       content: {
         'application/json': {
-          schema: UpdateUserSchema
+          schema: UpdateUserDto
         }
       }
     }
@@ -67,9 +61,9 @@ const app = new OpenAPIHono<AuthContext>().openapi(routeDef, async (c) => {
     }
     return c.json(user, 200);
   } catch (error) {
-    return c.json({ 
-      code: 500, 
-      message: error instanceof Error ? error.message : '更新用户失败' 
+    return c.json({
+      code: 500,
+      message: error instanceof Error ? error.message : '更新用户失败'
     }, 500);
   }
 });

+ 4 - 22
src/server/api/users/get.ts

@@ -5,7 +5,7 @@ import { authMiddleware } from '../../middleware/auth.middleware';
 import { ErrorSchema } from '../../utils/errorHandler';
 import { AppDataSource } from '../../data-source';
 import { AuthContext } from '../../types/context';
-import { UserSchema } from '../../modules/users/user.entity';
+import { UserListResponse } from '../../modules/users/user.schema';
 
 const userService = new UserService(AppDataSource);
 
@@ -24,24 +24,6 @@ const PaginationQuery = z.object({
   })
 });
 
-const UserListResponse = z.object({
-  data: z.array(UserSchema),
-  pagination: z.object({
-    total: z.number().openapi({
-      example: 100,
-      description: '总记录数'
-    }),
-    current: z.number().openapi({
-      example: 1,
-      description: '当前页码'
-    }),
-    pageSize: z.number().openapi({
-      example: 10,
-      description: '每页数量'
-    })
-  })
-});
-
 const listUsersRoute = createRoute({
   method: 'get',
   path: '/',
@@ -98,9 +80,9 @@ const app = new OpenAPIHono<AuthContext>().openapi(listUsersRoute, async (c) =>
     if (error instanceof z.ZodError) {
       return c.json({ code: 400, message: '参数错误' }, 400);
     }
-    return c.json({ 
-      code: 500, 
-      message: error instanceof Error ? error.message : '获取用户列表失败' 
+    return c.json({
+      code: 500,
+      message: error instanceof Error ? error.message : '获取用户列表失败'
     }, 500);
   }
 });

+ 2 - 16
src/server/api/users/post.ts

@@ -1,27 +1,13 @@
 import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
 import { UserService } from '../../modules/users/user.service';
-import { z } from '@hono/zod-openapi';
 import { authMiddleware } from '../../middleware/auth.middleware';
 import { ErrorSchema } from '../../utils/errorHandler';
 import { AppDataSource } from '../../data-source';
 import { AuthContext } from '../../types/context';
-import { UserSchema } from '@/server/modules/users/user.entity';
+import { UserSchema, CreateUserDto } from '@/server/modules/users/user.schema';
 
 const userService = new UserService(AppDataSource);
 
-
-const CreateUserSchema = UserSchema.omit({
-  id: true,
-  createdAt: true,
-  updatedAt: true,
-}).extend({
-  phone: UserSchema.shape.phone.optional(),
-  email: UserSchema.shape.email.optional(),
-  nickname: UserSchema.shape.nickname.optional(),
-  name: UserSchema.shape.name.optional(),
-  avatar: UserSchema.shape.avatar.optional(),
-})
-
 const createUserRoute = createRoute({
   method: 'post',
   path: '/',
@@ -30,7 +16,7 @@ const createUserRoute = createRoute({
     body: {
       content: {
         'application/json': {
-          schema: CreateUserSchema
+          schema: CreateUserDto
         }
       }
     }

+ 1 - 27
src/server/modules/users/role.entity.ts

@@ -1,31 +1,5 @@
 import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';
-import { z } from '@hono/zod-openapi';
-
-export type Permission = string;
-
-export const RoleSchema = z.object({
-  id: z.number().int().positive().openapi({
-    description: '角色ID',
-    example: 1
-  }),
-  name: z.string().max(50).openapi({
-    description: '角色名称,唯一标识',
-    example: 'admin'
-  }),
-  description: z.string().max(500).nullable().openapi({
-    description: '角色描述',
-    example: '系统管理员角色'
-  }),
-  permissions: z.array(z.string()).min(1).openapi({
-    description: '角色权限列表',
-    example: ['user:create', 'user:delete']
-  }),
-  createdAt: z.date().openapi({ description: '创建时间' }),
-  updatedAt: z.date().openapi({ description: '更新时间' })
-});
-
-export const CreateRoleDto = RoleSchema.omit({ id: true , createdAt: true, updatedAt: true });
-export const UpdateRoleDto = RoleSchema.partial();
+import { Permission } from './role.schema';
 
 @Entity({ name: 'role' })
 export class Role {

+ 27 - 0
src/server/modules/users/role.schema.ts

@@ -0,0 +1,27 @@
+import { z } from '@hono/zod-openapi';
+
+export type Permission = string;
+
+export const RoleSchema = z.object({
+  id: z.number().int().positive().openapi({
+    description: '角色ID',
+    example: 1
+  }),
+  name: z.string().max(50).openapi({
+    description: '角色名称,唯一标识',
+    example: 'admin'
+  }),
+  description: z.string().max(500).nullable().openapi({
+    description: '角色描述',
+    example: '系统管理员角色'
+  }),
+  permissions: z.array(z.string()).min(1).openapi({
+    description: '角色权限列表',
+    example: ['user:create', 'user:delete']
+  }),
+  createdAt: z.date().openapi({ description: '创建时间' }),
+  updatedAt: z.date().openapi({ description: '更新时间' })
+});
+
+export const CreateRoleDto = RoleSchema.omit({ id: true , createdAt: true, updatedAt: true });
+export const UpdateRoleDto = RoleSchema.partial();

+ 3 - 52
src/server/modules/users/user.entity.ts

@@ -1,6 +1,5 @@
-import { Entity, PrimaryGeneratedColumn, Column, ManyToMany, JoinTable, CreateDateColumn, UpdateDateColumn, OneToMany } from 'typeorm';
-import { Role, RoleSchema } from './role.entity';
-import { z } from '@hono/zod-openapi';
+import { Entity, PrimaryGeneratedColumn, Column, ManyToMany, JoinTable, CreateDateColumn, UpdateDateColumn } from 'typeorm';
+import { Role } from './role.entity';
 import { DeleteStatus, DisabledStatus } from '@/share/types';
 
 @Entity({ name: 'users' })
@@ -48,52 +47,4 @@ export class UserEntity {
   constructor(partial?: Partial<UserEntity>) {
     Object.assign(this, partial);
   }
-}
-
-export const UserSchema = z.object({
-  id: z.number().int().positive().openapi({ description: '用户ID' }),
-  username: z.string().min(3).max(255).openapi({
-    example: 'admin',
-    description: '用户名,3-255个字符'
-  }),
-  password: z.string().min(6).max(255).openapi({
-    example: 'password123',
-    description: '密码,最少6位'
-  }),
-  phone: z.string().max(255).nullable().openapi({
-    example: '13800138000',
-    description: '手机号'
-  }),
-  email: z.string().email().max(255).nullable().openapi({
-    example: 'user@example.com',
-    description: '邮箱'
-  }),
-  nickname: z.string().max(255).nullable().openapi({
-    example: '昵称',
-    description: '用户昵称'
-  }),
-  name: z.string().max(255).nullable().openapi({
-    example: '张三',
-    description: '真实姓名'
-  }),
-  avatar: z.string().max(255).nullable().openapi({
-    example: 'https://example.com/avatar.jpg',
-    description: '用户头像'
-  }),
-  isDisabled: z.number().int().min(0).max(1).default(DisabledStatus.ENABLED).openapi({
-    example: DisabledStatus.ENABLED,
-    description: '是否禁用(0:启用,1:禁用)'
-  }),
-  isDeleted: z.number().int().min(0).max(1).default(DeleteStatus.NOT_DELETED).openapi({
-    example: DeleteStatus.NOT_DELETED,
-    description: '是否删除(0:未删除,1:已删除)'
-  }),
-  roles: z.array(RoleSchema).optional().openapi({
-    example: [
-      { id: 1, name: 'admin',description:'管理员', permissions: ['user:create'] ,createdAt: new Date(), updatedAt: new Date() }
-    ],
-    description: '用户角色列表'
-  }),
-  createdAt: z.date().openapi({ description: '创建时间' }),
-  updatedAt: z.date().openapi({ description: '更新时间' })
-});
+}

+ 156 - 0
src/server/modules/users/user.schema.ts

@@ -0,0 +1,156 @@
+import { z } from '@hono/zod-openapi';
+import { DeleteStatus, DisabledStatus } from '@/share/types';
+import { RoleSchema } from './role.schema';
+
+// 基础用户 schema(包含所有字段)
+export const UserSchema = z.object({
+  id: z.number().int().positive().openapi({ description: '用户ID' }),
+  username: z.string().min(3).max(255).openapi({
+    example: 'admin',
+    description: '用户名,3-255个字符'
+  }),
+  password: z.string().min(6).max(255).openapi({
+    example: 'password123',
+    description: '密码,最少6位'
+  }),
+  phone: z.string().max(255).nullable().openapi({
+    example: '13800138000',
+    description: '手机号'
+  }),
+  email: z.string().email().max(255).nullable().openapi({
+    example: 'user@example.com',
+    description: '邮箱'
+  }),
+  nickname: z.string().max(255).nullable().openapi({
+    example: '昵称',
+    description: '用户昵称'
+  }),
+  name: z.string().max(255).nullable().openapi({
+    example: '张三',
+    description: '真实姓名'
+  }),
+  avatar: z.string().max(255).nullable().openapi({
+    example: 'https://example.com/avatar.jpg',
+    description: '用户头像'
+  }),
+  isDisabled: z.number().int().min(0).max(1).default(DisabledStatus.ENABLED).openapi({
+    example: DisabledStatus.ENABLED,
+    description: '是否禁用(0:启用,1:禁用)'
+  }),
+  isDeleted: z.number().int().min(0).max(1).default(DeleteStatus.NOT_DELETED).openapi({
+    example: DeleteStatus.NOT_DELETED,
+    description: '是否删除(0:未删除,1:已删除)'
+  }),
+  roles: z.array(RoleSchema).optional().openapi({
+    example: [
+      { 
+        id: 1, 
+        name: 'admin',
+        description: '管理员', 
+        permissions: ['user:create'],
+        createdAt: new Date(), 
+        updatedAt: new Date() 
+      }
+    ],
+    description: '用户角色列表'
+  }),
+  createdAt: z.date().openapi({ description: '创建时间' }),
+  updatedAt: z.date().openapi({ description: '更新时间' })
+});
+
+// 创建用户请求 schema
+export const CreateUserDto = z.object({
+  username: z.string().min(3).max(255).openapi({
+    example: 'admin',
+    description: '用户名,3-255个字符'
+  }),
+  password: z.string().min(6).max(255).openapi({
+    example: 'password123',
+    description: '密码,最少6位'
+  }),
+  phone: z.string().max(255).nullable().optional().openapi({
+    example: '13800138000',
+    description: '手机号'
+  }),
+  email: z.string().email().max(255).nullable().optional().openapi({
+    example: 'user@example.com',
+    description: '邮箱'
+  }),
+  nickname: z.string().max(255).nullable().optional().openapi({
+    example: '昵称',
+    description: '用户昵称'
+  }),
+  name: z.string().max(255).nullable().optional().openapi({
+    example: '张三',
+    description: '真实姓名'
+  }),
+  avatar: z.string().max(255).nullable().optional().openapi({
+    example: 'https://example.com/avatar.jpg',
+    description: '用户头像'
+  }),
+  isDisabled: z.number().int().min(0).max(1).default(DisabledStatus.ENABLED).optional().openapi({
+    example: DisabledStatus.ENABLED,
+    description: '是否禁用(0:启用,1:禁用)'
+  })
+});
+
+// 更新用户请求 schema
+export const UpdateUserDto = z.object({
+  username: z.string().min(3).max(255).optional().openapi({
+    example: 'admin',
+    description: '用户名,3-255个字符'
+  }),
+  password: z.string().min(6).max(255).optional().openapi({
+    example: 'password123',
+    description: '密码,最少6位'
+  }),
+  phone: z.string().max(255).nullable().optional().openapi({
+    example: '13800138000',
+    description: '手机号'
+  }),
+  email: z.string().email().max(255).nullable().optional().openapi({
+    example: 'user@example.com',
+    description: '邮箱'
+  }),
+  nickname: z.string().max(255).nullable().optional().openapi({
+    example: '昵称',
+    description: '用户昵称'
+  }),
+  name: z.string().max(255).nullable().optional().openapi({
+    example: '张三',
+    description: '真实姓名'
+  }),
+  avatar: z.string().max(255).nullable().optional().openapi({
+    example: 'https://example.com/avatar.jpg',
+    description: '用户头像'
+  }),
+  isDisabled: z.number().int().min(0).max(1).optional().openapi({
+    example: DisabledStatus.ENABLED,
+    description: '是否禁用(0:启用,1:禁用)'
+  })
+});
+
+// 用户列表响应 schema
+export const UserListResponse = z.object({
+  data: z.array(UserSchema),
+  pagination: z.object({
+    total: z.number().openapi({
+      example: 100,
+      description: '总记录数'
+    }),
+    current: z.number().openapi({
+      example: 1,
+      description: '当前页码'
+    }),
+    pageSize: z.number().openapi({
+      example: 10,
+      description: '每页数量'
+    })
+  })
+});
+
+// 类型导出
+export type User = z.infer<typeof UserSchema>;
+export type CreateUserRequest = z.infer<typeof CreateUserDto>;
+export type UpdateUserRequest = z.infer<typeof UpdateUserDto>;
+export type UserListResponseType = z.infer<typeof UserListResponse>;