# 新实体创建流程规范 ## 流程概述 实体创建流程分为两种模式,根据业务复杂度选择合适的实现方式: - **标准通用CRUD**:适用于简单数据模型,使用`GenericCrudService`和`createCrudRoutes`快速生成基础CRUD接口 - **自定义复杂CRUD**:适用于包含复杂业务逻辑的实体,需要手动实现服务方法和路由处理 ## 适用场景选择 | 类型 | 适用场景 | 技术选型 | |------|----------|----------| | 标准通用CRUD | 简单数据管理、无复杂业务逻辑、基础CRUD操作 | `GenericCrudService` + `createCrudRoutes` | | 自定义复杂CRUD | 复杂业务规则、多表关联操作、特殊权限控制、非标准数据处理 | 自定义Service + 手动路由实现 | ## 标准通用CRUD开发流程 ### 1. **创建实体** - 位置: `src/server/modules/[模块名]/[实体名].entity.ts` - 参考已有实体文件如`user.entity.ts` - 注意: 必须包含Zod Schema定义 ### 2. **注册实体到数据源** - 在`src/server/data-source.ts`中添加实体导入和注册: ```typescript // 实体类导入 import { YourEntity } from "./modules/[模块名]/[实体名].entity" export const AppDataSource = new DataSource({ // ...其他配置 entities: [ User, Role, YourEntity // 添加新实体到数组 ], // ...其他配置 }); ``` ### 3. **创建Service** - 位置: `src/server/modules/[模块名]/[实体名].service.ts` - 继承`GenericCrudService` - 通过构造函数注入DataSource ```typescript import { GenericCrudService } from '@/server/utils/generic-crud.service'; import { DataSource } from 'typeorm'; import { YourEntity } from './your-entity.entity'; export class YourEntityService extends GenericCrudService { constructor(dataSource: DataSource) { super(dataSource, YourEntity); } } ``` ### 4. **创建API路由** - 使用`createCrudRoutes`快速生成CRUD路由: ```typescript import { createCrudRoutes } from '@/server/utils/generic-crud.routes'; import { YourEntity } from '@/server/modules/your-module/your-entity.entity'; import { YourEntitySchema, CreateYourEntityDto, UpdateYourEntityDto } from '@/server/modules/your-module/your-entity.entity'; import { authMiddleware } from '@/server/middleware/auth.middleware'; const yourEntityRoutes = createCrudRoutes({ entity: YourEntity, createSchema: CreateYourEntityDto, updateSchema: UpdateYourEntityDto, getSchema: YourEntitySchema, listSchema: YourEntitySchema, searchFields: ['name', 'description'], // 可选,指定搜索字段 middleware: [authMiddleware] // 可选,添加中间件 }); export default yourEntityRoutes; ``` ### 5. **注册路由** - 在`src/server/api.ts`中添加路由注册: ```typescript import yourEntityRoutes from '@/server/api/your-entity/index'; // 注册路由 api.route('/api/v1/your-entities', yourEntityRoutes); ``` ### 6. **创建客户端API** - 在`src/client/api.ts`中添加客户端定义: ```typescript import { hc } from 'hono/client'; import { YourEntityRoutes } from '@/server/api'; export const yourEntityClient = hc('/api/v1', { fetch: axiosFetch, }).api.v1['your-entities']; ``` ### 7. **前端调用** - 在页面组件(如`pages_users.tsx`)中: - 使用`InferResponseType`提取响应类型 - 使用`InferRequestType`提取请求类型 - 示例: ```typescript type EntityResponse = InferResponseType; type CreateRequest = InferRequestType['json']; ``` ### 8. **注册管理后台路由和菜单** - **注册路由**:在`src/client/admin/routes.tsx`中添加路由配置: ```typescript import YourEntityList from './pages/YourEntityList'; import YourEntityDetail from './pages/YourEntityDetail'; export const routes = [ // ...其他路由 { path: '/your-entities', element: }, { path: '/your-entities/:id', element: } ]; ``` - **注册菜单**:在`src/client/admin/menu.tsx`中添加菜单配置: ```typescript import { TableOutlined } from '@ant-design/icons'; export const menuItems = [ // ...其他菜单项 { key: 'your-entities', icon: , label: '实体管理', path: '/your-entities' } ]; ``` ## 自定义复杂CRUD开发流程 当实体需要复杂业务逻辑或非标准CRUD操作时,采用以下完整流程: ### 1. **创建实体** - 位置: `src/server/modules/[模块名]/[实体名].entity.ts` - 定义实体类和Zod Schema - 示例: ```typescript import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'; import { z } from '@hono/zod-openapi'; @Entity('your_entity') export class YourEntity { @PrimaryGeneratedColumn({ unsigned: true }) id!: number; @Column({ name: 'name', type: 'varchar', length: 255 }) name!: string; // 其他业务字段... } // Zod Schema定义 export const YourEntitySchema = z.object({ id: z.number().int().positive().openapi({ description: '实体ID' }), name: z.string().max(255).openapi({ description: '名称', example: '示例名称' }) // 其他字段Schema... }); export const CreateYourEntityDto = z.object({ name: z.string().max(255).openapi({ description: '名称', example: '示例名称' }) // 其他创建字段... }); export const UpdateYourEntityDto = z.object({ name: z.string().max(255).optional().openapi({ description: '名称', example: '示例名称' }) // 其他更新字段... }); ``` ### 2. **注册实体到数据源** - 在`src/server/data-source.ts`中添加实体导入和注册: ```typescript // 实体类导入 import { YourEntity } from "./modules/[模块名]/[实体名].entity" export const AppDataSource = new DataSource({ // ...其他配置 entities: [ User, Role, YourEntity // 添加新实体到数组 ], // ...其他配置 }); ``` ### 3. **创建自定义Service** - 位置: `src/server/modules/[模块名]/[实体名].service.ts` - 实现自定义业务逻辑和数据访问 - 示例: ```typescript import { DataSource, Repository } from 'typeorm'; import { YourEntity } from './your-entity.entity'; import { CreateYourEntityDto, UpdateYourEntityDto } from './your-entity.entity'; import { AppError } from '@/server/utils/errorHandler'; export class YourEntityService { private repository: Repository; constructor(dataSource: DataSource) { this.repository = dataSource.getRepository(YourEntity); } /** * 获取实体列表(带复杂过滤条件) */ async findAll(filters: any): Promise<[YourEntity[], number]> { const query = this.repository.createQueryBuilder('entity'); // 添加复杂业务过滤逻辑 if (filters.status) { query.andWhere('entity.status = :status', { status: filters.status }); } // 多表关联查询示例 if (filters.includeRelated) { query.leftJoinAndSelect('entity.relatedEntity', 'related'); } const [items, total] = await query.getManyAndCount(); return [items, total]; } /** * 根据ID获取单个实体 */ async findById(id: number): Promise { const entity = await this.repository.findOneBy({ id }); if (!entity) { throw new AppError('实体不存在', 404); } return entity; } /** * 创建实体(带业务规则验证) */ async create(data: CreateYourEntityDto): Promise { // 业务规则验证示例 const existing = await this.repository.findOneBy({ name: data.name }); if (existing) { throw new AppError('名称已存在', 400); } const entity = this.repository.create(data); return this.repository.save(entity); } /** * 更新实体(带业务逻辑处理) */ async update(id: number, data: UpdateYourEntityDto): Promise { const entity = await this.findById(id); // 业务逻辑处理示例 if (data.name && data.name !== entity.name) { // 记录名称变更日志等业务操作 } Object.assign(entity, data); return this.repository.save(entity); } /** * 删除实体(带权限检查) */ async delete(id: number, userId: number): Promise { const entity = await this.findById(id); // 权限检查示例 if (entity.createdBy !== userId) { throw new AppError('没有删除权限', 403); } await this.repository.remove(entity); return true; } /** * 自定义业务方法示例 */ async customBusinessOperation(params: any): Promise { // 实现复杂业务逻辑 // 可能包含事务、多表操作等 } } ``` ### 4. **创建自定义API路由** - 目录结构: ``` src/server/api/[实体名]/ ├── get.ts # 列表查询 ├── post.ts # 创建实体 ├── [id]/ │ ├── get.ts # 获取单个实体 │ ├── put.ts # 更新实体 │ └── delete.ts # 删除实体 └── index.ts # 路由聚合 ``` - **列表查询路由示例** (get.ts): ```typescript import { createRoute, OpenAPIHono } from '@hono/zod-openapi'; import { z } from 'zod'; import { YourEntitySchema } from '@/server/modules/your-module/your-entity.entity'; import { ErrorSchema } from '@/server/utils/errorHandler'; import { AppDataSource } from '@/server/data-source'; import { YourEntityService } from '@/server/modules/your-module/your-entity.service'; import { AuthContext } from '@/server/types/context'; import { authMiddleware } from '@/server/middleware/auth.middleware'; // 查询参数Schema const ListQuery = z.object({ page: z.coerce.number().int().positive().default(1).openapi({ description: '页码', example: 1 }), pageSize: z.coerce.number().int().positive().default(10).openapi({ description: '每页条数', example: 10 }), status: z.coerce.number().optional().openapi({ description: '状态过滤', example: 1 }) }); // 响应Schema const ListResponse = z.object({ data: z.array(YourEntitySchema), 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 routeDef = createRoute({ method: 'get', path: '/', middleware: [authMiddleware], request: { query: ListQuery }, responses: { 200: { description: '成功获取实体列表', content: { 'application/json': { schema: ListResponse } } }, 400: { description: '请求参数错误', content: { 'application/json': { schema: ErrorSchema } } }, 500: { description: '服务器错误', content: { 'application/json': { schema: ErrorSchema } } } } }); // 路由实现 const app = new OpenAPIHono().openapi(routeDef, async (c) => { try { const query = c.req.valid('query'); const service = new YourEntityService(AppDataSource); const [data, total] = await service.findAll({ status: query.status, // 其他过滤条件 }); return c.json({ data, pagination: { total, current: query.page, pageSize: query.pageSize } }, 200); } catch (error) { const { code = 500, message = '获取列表失败' } = error as Error & { code?: number }; return c.json({ code, message }, code); } }); export default app; ``` - **创建实体路由示例** (post.ts): ```typescript import { createRoute, OpenAPIHono } from '@hono/zod-openapi'; import { CreateYourEntityDto, YourEntitySchema } from '@/server/modules/your-module/your-entity.entity'; import { ErrorSchema } from '@/server/utils/errorHandler'; import { AppDataSource } from '@/server/data-source'; import { YourEntityService } from '@/server/modules/your-module/your-entity.service'; import { AuthContext } from '@/server/types/context'; import { authMiddleware } from '@/server/middleware/auth.middleware'; // 路由定义 const routeDef = createRoute({ method: 'post', path: '/', middleware: [authMiddleware], request: { body: { content: { 'application/json': { schema: CreateYourEntityDto } } } }, responses: { 200: { description: '成功创建实体', content: { 'application/json': { schema: YourEntitySchema } } }, 400: { description: '请求参数错误', content: { 'application/json': { schema: ErrorSchema } } }, 500: { description: '服务器错误', content: { 'application/json': { schema: ErrorSchema } } } } }); // 路由实现 const app = new OpenAPIHono().openapi(routeDef, async (c) => { try { const data = await c.req.json(); const service = new YourEntityService(AppDataSource); const result = await service.create(data); return c.json(result, 200); } catch (error) { const { code = 500, message = '创建实体失败' } = error as Error & { code?: number }; return c.json({ code, message }, code); } }); export default app; ``` - **路由聚合示例** (index.ts): ```typescript import { OpenAPIHono } from '@hono/zod-openapi'; import listRoute from './get'; import createRoute from './post'; import getByIdRoute from './[id]/get'; import updateRoute from './[id]/put'; import deleteRoute from './[id]/delete'; const app = new OpenAPIHono() .route('/', listRoute) .route('/', createRoute) .route('/', getByIdRoute) .route('/', updateRoute) .route('/', deleteRoute); export default app; ``` ### 5. **注册路由** - 在`src/server/api.ts`中添加路由注册: ```typescript import yourEntityRoutes from '@/server/api/your-entity/index'; // 注册路由 api.route('/api/v1/your-entities', yourEntityRoutes); ``` ### 6. **创建客户端API** - 在`src/client/api.ts`中添加客户端定义: ```typescript import { hc } from 'hono/client'; import { YourEntityRoutes } from '@/server/api'; export const yourEntityClient = hc('/api/v1', { fetch: axiosFetch, }).api.v1['your-entities']; ``` ### 7. **前端调用** - 在页面组件(如`pages_users.tsx`)中: - 使用`InferResponseType`提取响应类型 - 使用`InferRequestType`提取请求类型 - 示例: ```typescript type EntityResponse = InferResponseType; type CreateRequest = InferRequestType['json']; ``` ### 8. **注册管理后台路由和菜单** - **注册路由**:在`src/client/admin/routes.tsx`中添加路由配置: ```typescript import YourEntityList from './pages/YourEntityList'; import YourEntityDetail from './pages/YourEntityDetail'; export const routes = [ // ...其他路由 { path: '/your-entities', element: }, { path: '/your-entities/:id', element: } ]; ``` - **注册菜单**:在`src/client/admin/menu.tsx`中添加菜单配置: ```typescript import { TableOutlined } from '@ant-design/icons'; export const menuItems = [ // ...其他菜单项 { key: 'your-entities', icon: , label: '实体管理', path: '/your-entities' } ]; ``` ## 注意事项 1. 实体Schema必须在实体文件中定义,路由中直接引用,不要重复定义 2. 前端表格/表单字段必须与实体定义保持一致 3. 确保所有API调用都有正确的类型推断 4. 参考现有模块实现保持风格一致