|
@@ -16,456 +16,40 @@
|
|
|
|
|
|
|
|
## 标准通用CRUD开发流程
|
|
## 标准通用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<YourEntity> {
|
|
|
|
|
- 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<YourEntityRoutes>('/api/v1', {
|
|
|
|
|
- fetch: axiosFetch,
|
|
|
|
|
- }).api.v1['your-entities'];
|
|
|
|
|
- ```
|
|
|
|
|
-
|
|
|
|
|
-6. **前端调用**
|
|
|
|
|
- - 在页面组件(如`pages_users.tsx`)中:
|
|
|
|
|
- - 使用`InferResponseType`提取响应类型
|
|
|
|
|
- - 使用`InferRequestType`提取请求类型
|
|
|
|
|
- - 示例:
|
|
|
|
|
- ```typescript
|
|
|
|
|
- type EntityResponse = InferResponseType<typeof entityClient.$get, 200>;
|
|
|
|
|
- type CreateRequest = InferRequestType<typeof entityClient.$post>['json'];
|
|
|
|
|
- ```
|
|
|
|
|
-
|
|
|
|
|
|
|
+适用于简单数据模型,无复杂业务逻辑,仅需基础CRUD操作的场景。采用`GenericCrudService`和`createCrudRoutes`快速生成接口。
|
|
|
|
|
+
|
|
|
|
|
+### 开发步骤概要
|
|
|
|
|
+
|
|
|
|
|
+1. **创建实体**:在`src/server/modules/[模块名]/[实体名].entity.ts`定义实体类和Zod Schema
|
|
|
|
|
+2. **注册实体**:在`src/server/data-source.ts`中注册新实体
|
|
|
|
|
+3. **创建Service**:继承`GenericCrudService`实现基础CRUD操作
|
|
|
|
|
+4. **创建API路由**:使用`createCrudRoutes`快速生成CRUD路由
|
|
|
|
|
+5. **注册路由**:在`src/server/api.ts`中注册路由
|
|
|
|
|
+6. **创建客户端API**:在`src/client/api.ts`中定义客户端调用方法
|
|
|
|
|
+7. **前端调用**:在页面组件中使用类型化API调用
|
|
|
|
|
+8. **注册路由和菜单**:
|
|
|
|
|
+ - 管理后台:在`src/client/admin/routes.tsx`中添加路由配置,在`src/client/admin/menu.tsx`中添加菜单配置
|
|
|
|
|
+ - 前台:在`src/client/home/routes.tsx`中添加路由配置
|
|
|
|
|
+
|
|
|
|
|
+详细流程请参见[标准通用CRUD开发流程规范](./11-standard-crud.md)
|
|
|
## 自定义复杂CRUD开发流程
|
|
## 自定义复杂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<YourEntity>;
|
|
|
|
|
-
|
|
|
|
|
- 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<YourEntity> {
|
|
|
|
|
- const entity = await this.repository.findOneBy({ id });
|
|
|
|
|
- if (!entity) {
|
|
|
|
|
- throw new AppError('实体不存在', 404);
|
|
|
|
|
- }
|
|
|
|
|
- return entity;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 创建实体(带业务规则验证)
|
|
|
|
|
- */
|
|
|
|
|
- async create(data: CreateYourEntityDto): Promise<YourEntity> {
|
|
|
|
|
- // 业务规则验证示例
|
|
|
|
|
- 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<YourEntity> {
|
|
|
|
|
- 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<boolean> {
|
|
|
|
|
- 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<any> {
|
|
|
|
|
- // 实现复杂业务逻辑
|
|
|
|
|
- // 可能包含事务、多表操作等
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- ```
|
|
|
|
|
-
|
|
|
|
|
-### 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<AuthContext>().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<AuthContext>().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<YourEntityRoutes>('/api/v1', {
|
|
|
|
|
- fetch: axiosFetch,
|
|
|
|
|
- }).api.v1['your-entities'];
|
|
|
|
|
- ```
|
|
|
|
|
|
|
+### 开发步骤概要
|
|
|
|
|
|
|
|
-### 7. **前端调用**
|
|
|
|
|
- - 在页面组件(如`pages_users.tsx`)中:
|
|
|
|
|
- - 使用`InferResponseType`提取响应类型
|
|
|
|
|
- - 使用`InferRequestType`提取请求类型
|
|
|
|
|
- - 示例:
|
|
|
|
|
- ```typescript
|
|
|
|
|
- type EntityResponse = InferResponseType<typeof entityClient.$get, 200>;
|
|
|
|
|
- type CreateRequest = InferRequestType<typeof entityClient.$post>['json'];
|
|
|
|
|
- ```
|
|
|
|
|
|
|
+1. **创建实体**:在`src/server/modules/[模块名]/[实体名].entity.ts`定义实体类和Zod Schema
|
|
|
|
|
+2. **注册实体**:在`src/server/data-source.ts`中注册新实体
|
|
|
|
|
+3. **创建自定义Service**:实现包含复杂业务逻辑的数据访问方法
|
|
|
|
|
+4. **创建自定义API路由**:手动实现CRUD路由及处理逻辑
|
|
|
|
|
+5. **注册路由**:在`src/server/api.ts`中注册路由
|
|
|
|
|
+6. **创建客户端API**:在`src/client/api.ts`中定义客户端调用方法
|
|
|
|
|
+7. **前端调用**:在页面组件中使用类型化API调用
|
|
|
|
|
+8. **注册路由和菜单**:
|
|
|
|
|
+ - 管理后台:在`src/client/admin/routes.tsx`中添加路由配置,在`src/client/admin/menu.tsx`中添加菜单配置
|
|
|
|
|
+ - 前台:在`src/client/home/routes.tsx`中添加路由配置
|
|
|
|
|
|
|
|
|
|
+详细流程请参见[自定义复杂CRUD开发流程规范](./11-custom-crud.md)
|
|
|
## 注意事项
|
|
## 注意事项
|
|
|
|
|
|
|
|
1. 实体Schema必须在实体文件中定义,路由中直接引用,不要重复定义
|
|
1. 实体Schema必须在实体文件中定义,路由中直接引用,不要重复定义
|