Bläddra i källkod

已完成 ExcelTemplate 实体的 CRUD 路由开发,包括:
1. 创建 ExcelTemplateService 服务类
2. 实现以下路由:
- GET /excel-templates - 获取模板列表
- POST /excel-templates - 创建模板
- GET /excel-templates/{id} - 获取单个模板
- PUT /excel-templates/{id} - 更新模板
- DELETE /excel-templates/{id} - 删除模板
3. 路由已聚合并注册到主 API
4. 所有实现符合项目规范和 OpenAPI 标准

yourname 5 månader sedan
förälder
incheckning
afab686e9c

+ 4 - 0
src/server/api.ts

@@ -53,10 +53,14 @@ if(!import.meta.env.PROD){
 
 
 
+import excelTemplatesRoute from './api/excel-templates/index'
+
 const userRoutes = api.route('/api/v1/users', usersRouter)
 const authRoutes = api.route('/api/v1/auth', authRoute)
+const excelTemplatesRoutes = api.route('/api/v1/excel-templates', excelTemplatesRoute)
 
 export type AuthRoutes = typeof authRoutes
 export type UserRoutes = typeof userRoutes
+export type ExcelTemplatesRoutes = typeof excelTemplatesRoutes
 
 export default api

+ 80 - 0
src/server/api/excel-templates/[id]/delete.ts

@@ -0,0 +1,80 @@
+import { z } from '@hono/zod-openapi';
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { ExcelTemplateService } from '@/server/modules/excel/excel-template.service';
+import { AppDataSource } from '@/server/data-source';
+import { ErrorSchema } from '@/server/utils/errorHandler';
+import { AuthContext } from '@/server/types/context';
+import { authMiddleware } from '@/server/middleware/auth.middleware';
+
+const excelTemplateService = new ExcelTemplateService(AppDataSource);
+
+const GetParams = z.object({
+  id: z.string().openapi({
+    param: { name: 'id', in: 'path' },
+    example: '1',
+    description: '模板ID'
+  })
+});
+
+const routeDef = createRoute({
+  method: 'delete',
+  path: '/{id}',
+  middleware: [authMiddleware],
+  request: {
+    params: GetParams
+  },
+  responses: {
+    200: {
+      description: '成功删除Excel模板',
+      content: {
+        'application/json': {
+          schema: z.object({
+            success: z.boolean().openapi({
+              example: true,
+              description: '是否删除成功'
+            })
+          })
+        }
+      }
+    },
+    404: {
+      description: '模板不存在',
+      content: {
+        'application/json': {
+          schema: ErrorSchema
+        }
+      }
+    },
+    500: {
+      description: '服务器错误',
+      content: {
+        'application/json': {
+          schema: ErrorSchema
+        }
+      }
+    }
+  }
+});
+
+const app = new OpenAPIHono<AuthContext>().openapi(routeDef, async (c) => {
+  try {
+    const { id } = c.req.valid('param');
+    const result = await excelTemplateService.delete(Number(id));
+    
+    if (!result.affected) {
+      return c.json({ 
+        code: 404, 
+        message: '模板不存在'
+      }, 404);
+    }
+
+    return c.json({ success: true }, 200);
+  } catch (error) {
+    return c.json({ 
+      code: 500, 
+      message: error instanceof Error ? error.message : '删除模板失败'
+    }, 500);
+  }
+});
+
+export default app;

+ 76 - 0
src/server/api/excel-templates/[id]/get.ts

@@ -0,0 +1,76 @@
+import { z } from '@hono/zod-openapi';
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { ExcelTemplateService } from '@/server/modules/excel/excel-template.service';
+import { AppDataSource } from '@/server/data-source';
+import { ExcelTemplateSchema } from '@/server/modules/excel/excel-template.entity';
+import { ErrorSchema } from '@/server/utils/errorHandler';
+import { AuthContext } from '@/server/types/context';
+import { authMiddleware } from '@/server/middleware/auth.middleware';
+
+const excelTemplateService = new ExcelTemplateService(AppDataSource);
+
+const GetParams = z.object({
+  id: z.string().openapi({
+    param: { name: 'id', in: 'path' },
+    example: '1',
+    description: '模板ID'
+  })
+});
+
+const routeDef = createRoute({
+  method: 'get',
+  path: '/{id}',
+  middleware: [authMiddleware],
+  request: {
+    params: GetParams
+  },
+  responses: {
+    200: {
+      description: '成功获取Excel模板',
+      content: {
+        'application/json': {
+          schema: ExcelTemplateSchema
+        }
+      }
+    },
+    404: {
+      description: '模板不存在',
+      content: {
+        'application/json': {
+          schema: ErrorSchema
+        }
+      }
+    },
+    500: {
+      description: '服务器错误',
+      content: {
+        'application/json': {
+          schema: ErrorSchema
+        }
+      }
+    }
+  }
+});
+
+const app = new OpenAPIHono<AuthContext>().openapi(routeDef, async (c) => {
+  try {
+    const { id } = c.req.valid('param');
+    const template = await excelTemplateService.findOne(Number(id));
+    
+    if (!template) {
+      return c.json({ 
+        code: 404, 
+        message: '模板不存在'
+      }, 404);
+    }
+
+    return c.json(template, 200);
+  } catch (error) {
+    return c.json({ 
+      code: 500, 
+      message: error instanceof Error ? error.message : '获取模板失败'
+    }, 500);
+  }
+});
+
+export default app;

+ 90 - 0
src/server/api/excel-templates/[id]/put.ts

@@ -0,0 +1,90 @@
+import { z } from '@hono/zod-openapi';
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { ExcelTemplateService } from '@/server/modules/excel/excel-template.service';
+import { AppDataSource } from '@/server/data-source';
+import { ExcelTemplateSchema } from '@/server/modules/excel/excel-template.entity';
+import { ErrorSchema } from '@/server/utils/errorHandler';
+import { AuthContext } from '@/server/types/context';
+import { authMiddleware } from '@/server/middleware/auth.middleware';
+
+const excelTemplateService = new ExcelTemplateService(AppDataSource);
+
+const GetParams = z.object({
+  id: z.string().openapi({
+    param: { name: 'id', in: 'path' },
+    example: '1',
+    description: '模板ID'
+  })
+});
+
+const routeDef = createRoute({
+  method: 'put',
+  path: '/{id}',
+  middleware: [authMiddleware],
+  request: {
+    params: GetParams,
+    body: {
+      content: {
+        'application/json': {
+          schema: ExcelTemplateSchema.omit({ 
+            id: true,
+            createdBy: true,
+            createdAt: true,
+            updatedAt: true,
+            isDeleted: true 
+          })
+        }
+      }
+    }
+  },
+  responses: {
+    200: {
+      description: '成功更新Excel模板',
+      content: {
+        'application/json': {
+          schema: ExcelTemplateSchema
+        }
+      }
+    },
+    404: {
+      description: '模板不存在',
+      content: {
+        'application/json': {
+          schema: ErrorSchema
+        }
+      }
+    },
+    500: {
+      description: '服务器错误',
+      content: {
+        'application/json': {
+          schema: ErrorSchema
+        }
+      }
+    }
+  }
+});
+
+const app = new OpenAPIHono<AuthContext>().openapi(routeDef, async (c) => {
+  try {
+    const { id } = c.req.valid('param');
+    const templateData = c.req.valid('json');
+    const updatedTemplate = await excelTemplateService.update(Number(id), templateData);
+    
+    if (!updatedTemplate) {
+      return c.json({ 
+        code: 404, 
+        message: '模板不存在'
+      }, 404);
+    }
+
+    return c.json(updatedTemplate, 200);
+  } catch (error) {
+    return c.json({ 
+      code: 500, 
+      message: error instanceof Error ? error.message : '更新模板失败'
+    }, 500);
+  }
+});
+
+export default app;

+ 91 - 0
src/server/api/excel-templates/get.ts

@@ -0,0 +1,91 @@
+import { z } from '@hono/zod-openapi';
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { ExcelTemplateService } from '@/server/modules/excel/excel-template.service';
+import { AppDataSource } from '@/server/data-source';
+import { ExcelTemplateSchema } from '@/server/modules/excel/excel-template.entity';
+import { ErrorSchema } from '@/server/utils/errorHandler';
+import { AuthContext } from '@/server/types/context';
+
+const excelTemplateService = new ExcelTemplateService(AppDataSource);
+
+const routeDef = createRoute({
+  method: 'get',
+  path: '/',
+  request: {
+    query: 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
+      })
+    })
+  },
+  responses: {
+    200: {
+      description: '成功获取Excel模板列表',
+      content: {
+        'application/json': {
+          schema: z.object({
+            data: z.array(ExcelTemplateSchema),
+            pagination: z.object({
+              total: z.number().openapi({
+                example: 100,
+                description: '总记录数'
+              }),
+              current: z.number().openapi({
+                example: 1,
+                description: '当前页码'
+              }),
+              pageSize: z.number().openapi({
+                example: 10,
+                description: '每页数量'
+              })
+            })
+          })
+        }
+      }
+    },
+    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 { page, pageSize } = c.req.valid('query');
+    const { templates, total } = await excelTemplateService.findAll(page, pageSize);
+    
+    return c.json({
+      data: templates,
+      pagination: {
+        total,
+        current: page,
+        pageSize
+      }
+    }, 200);
+  } catch (error) {
+    return c.json({ 
+      code: 500, 
+      message: error instanceof Error ? error.message : '获取模板列表失败'
+    }, 500);
+  }
+});
+
+export default app;

+ 15 - 0
src/server/api/excel-templates/index.ts

@@ -0,0 +1,15 @@
+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;

+ 76 - 0
src/server/api/excel-templates/post.ts

@@ -0,0 +1,76 @@
+import { z } from '@hono/zod-openapi';
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { ExcelTemplateService } from '@/server/modules/excel/excel-template.service';
+import { AppDataSource } from '@/server/data-source';
+import { ExcelTemplateSchema } from '@/server/modules/excel/excel-template.entity';
+import { ErrorSchema } from '@/server/utils/errorHandler';
+import { AuthContext } from '@/server/types/context';
+import { authMiddleware } from '@/server/middleware/auth.middleware';
+
+const excelTemplateService = new ExcelTemplateService(AppDataSource);
+
+const routeDef = createRoute({
+  method: 'post',
+  path: '/',
+  middleware: [authMiddleware],
+  request: {
+    body: {
+      content: {
+        'application/json': {
+          schema: ExcelTemplateSchema.omit({ 
+            id: true,
+            createdAt: true,
+            updatedAt: true,
+            isDeleted: true 
+          })
+        }
+      }
+    }
+  },
+  responses: {
+    200: {
+      description: '成功创建Excel模板',
+      content: {
+        'application/json': {
+          schema: ExcelTemplateSchema
+        }
+      }
+    },
+    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 user = c.get('user');
+    const templateData = c.req.valid('json');
+    const newTemplate = await excelTemplateService.create({
+      ...templateData,
+      createdBy: user.id
+    });
+
+    return c.json(newTemplate, 200);
+  } catch (error) {
+    return c.json({ 
+      code: 500, 
+      message: error instanceof Error ? error.message : '创建模板失败'
+    }, 500);
+  }
+});
+
+export default app;

+ 40 - 0
src/server/modules/excel/excel-template.service.ts

@@ -0,0 +1,40 @@
+import { DataSource, Repository } from 'typeorm';
+import { ExcelTemplate } from './excel-template.entity';
+import { ExcelTemplateSchema } from './excel-template.entity';
+
+export class ExcelTemplateService {
+  private repository: Repository<ExcelTemplate>;
+
+  constructor(private dataSource: DataSource) {
+    this.repository = dataSource.getRepository(ExcelTemplate);
+  }
+
+  async findAll(page: number, pageSize: number) {
+    const [templates, total] = await this.repository.findAndCount({
+      where: { isDeleted: 0 },
+      skip: (page - 1) * pageSize,
+      take: pageSize
+    });
+    return { templates, total };
+  }
+
+  async findOne(id: number) {
+    return this.repository.findOne({
+      where: { id, isDeleted: 0 }
+    });
+  }
+
+  async create(template: Omit<ExcelTemplate, 'id' | 'createdAt' | 'updatedAt' | 'isDeleted'>) {
+    const newTemplate = this.repository.create(template);
+    return this.repository.save(newTemplate);
+  }
+
+  async update(id: number, template: Partial<ExcelTemplate>) {
+    await this.repository.update(id, template);
+    return this.findOne(id);
+  }
+
+  async delete(id: number) {
+    return this.repository.update(id, { isDeleted: 1 });
+  }
+}