|
|
@@ -5,6 +5,7 @@ import { ErrorSchema } from './errorHandler';
|
|
|
import { AuthContext } from '../types/context';
|
|
|
import { ObjectLiteral } from 'typeorm';
|
|
|
import { AppDataSource } from '../data-source';
|
|
|
+import { parseWithAwait } from './parseWithAwait';
|
|
|
|
|
|
export function createCrudRoutes<
|
|
|
T extends ObjectLiteral,
|
|
|
@@ -13,7 +14,7 @@ export function createCrudRoutes<
|
|
|
GetSchema extends z.ZodSchema = z.ZodSchema,
|
|
|
ListSchema extends z.ZodSchema = z.ZodSchema
|
|
|
>(options: CrudOptions<T, CreateSchema, UpdateSchema, GetSchema, ListSchema>) {
|
|
|
- const { entity, createSchema, updateSchema, getSchema, listSchema, searchFields, relations, middleware = [], userTracking, relationFields } = options;
|
|
|
+ const { entity, createSchema, updateSchema, getSchema, listSchema, searchFields, relations, middleware = [], userTracking, relationFields, readOnly = false } = options;
|
|
|
|
|
|
// 创建CRUD服务实例
|
|
|
// 抽象类不能直接实例化,需要创建具体实现类
|
|
|
@@ -34,11 +35,11 @@ export function createCrudRoutes<
|
|
|
middleware,
|
|
|
request: {
|
|
|
query: z.object({
|
|
|
- page: z.coerce.number().int().positive().default(1).openapi({
|
|
|
+ page: z.coerce.number<number>().int().positive().default(1).openapi({
|
|
|
example: 1,
|
|
|
description: '页码,从1开始'
|
|
|
}),
|
|
|
- pageSize: z.coerce.number().int().positive().default(10).openapi({
|
|
|
+ pageSize: z.coerce.number<number>().int().positive().default(10).openapi({
|
|
|
example: 10,
|
|
|
description: '每页数量'
|
|
|
}),
|
|
|
@@ -217,133 +218,213 @@ export function createCrudRoutes<
|
|
|
});
|
|
|
|
|
|
// 注册路由处理函数
|
|
|
- const routes = app
|
|
|
- .openapi(listRoute, async (c) => {
|
|
|
- try {
|
|
|
- const query = c.req.valid('query') as any;
|
|
|
- const { page, pageSize, keyword, sortBy, sortOrder, filters } = query;
|
|
|
-
|
|
|
- // 构建排序对象
|
|
|
- const order: any = {};
|
|
|
- if (sortBy) {
|
|
|
- order[sortBy] = sortOrder || 'DESC';
|
|
|
- } else {
|
|
|
- order['id'] = 'DESC';
|
|
|
- }
|
|
|
-
|
|
|
- // 解析筛选条件
|
|
|
- let parsedFilters: any = undefined;
|
|
|
- if (filters) {
|
|
|
- try {
|
|
|
- parsedFilters = JSON.parse(filters);
|
|
|
- } catch (e) {
|
|
|
- return c.json({ code: 400, message: '筛选条件格式错误' }, 400);
|
|
|
+
|
|
|
+ // 只读模式下只注册 GET 路由
|
|
|
+ if (!readOnly) {
|
|
|
+ // 完整 CRUD 路由
|
|
|
+ const routes = app
|
|
|
+ .openapi(listRoute, async (c) => {
|
|
|
+ try {
|
|
|
+ const query = c.req.valid('query') as any;
|
|
|
+ const { page, pageSize, keyword, sortBy, sortOrder, filters } = query;
|
|
|
+
|
|
|
+ // 构建排序对象
|
|
|
+ const order: any = {};
|
|
|
+ if (sortBy) {
|
|
|
+ order[sortBy] = sortOrder || 'DESC';
|
|
|
+ } else {
|
|
|
+ order['id'] = 'DESC';
|
|
|
}
|
|
|
+
|
|
|
+ // 解析筛选条件
|
|
|
+ let parsedFilters: any = undefined;
|
|
|
+ if (filters) {
|
|
|
+ try {
|
|
|
+ parsedFilters = JSON.parse(filters);
|
|
|
+ } catch (e) {
|
|
|
+ return c.json({ code: 400, message: '筛选条件格式错误' }, 400);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const [data, total] = await crudService.getList(
|
|
|
+ page,
|
|
|
+ pageSize,
|
|
|
+ keyword,
|
|
|
+ searchFields,
|
|
|
+ undefined,
|
|
|
+ relations || [],
|
|
|
+ order,
|
|
|
+ parsedFilters
|
|
|
+ );
|
|
|
+
|
|
|
+ return c.json({
|
|
|
+ // data: z.array(listSchema).parse(data),
|
|
|
+ data: await parseWithAwait(z.array(listSchema), data),
|
|
|
+ pagination: { total, current: page, pageSize }
|
|
|
+ }, 200);
|
|
|
+ } catch (error) {
|
|
|
+ if (error instanceof z.ZodError) {
|
|
|
+ return c.json({ code: 400, message: '参数验证失败', errors: JSON.parse(error.message) }, 400);
|
|
|
+ }
|
|
|
+ return c.json({
|
|
|
+ code: 500,
|
|
|
+ message: error instanceof Error ? error.message : '获取列表失败'
|
|
|
+ }, 500);
|
|
|
}
|
|
|
-
|
|
|
- const [data, total] = await crudService.getList(
|
|
|
- page,
|
|
|
- pageSize,
|
|
|
- keyword,
|
|
|
- searchFields,
|
|
|
- undefined,
|
|
|
- relations || [],
|
|
|
- order,
|
|
|
- parsedFilters
|
|
|
- );
|
|
|
-
|
|
|
- return c.json({
|
|
|
- data: z.array(listSchema).parse(data),
|
|
|
- pagination: { total, current: page, pageSize }
|
|
|
- }, 200);
|
|
|
- } catch (error) {
|
|
|
- if (error instanceof z.ZodError) {
|
|
|
- return c.json({ code: 400, message: '参数验证失败', errors: error.errors }, 400);
|
|
|
- }
|
|
|
- return c.json({
|
|
|
- code: 500,
|
|
|
- message: error instanceof Error ? error.message : '获取列表失败'
|
|
|
- }, 500);
|
|
|
- }
|
|
|
- })
|
|
|
- .openapi(createRouteDef, async (c: any) => {
|
|
|
- try {
|
|
|
- const data = c.req.valid('json');
|
|
|
- const user = c.get('user');
|
|
|
- const result = await crudService.create(data, user?.id);
|
|
|
- return c.json(result, 201);
|
|
|
- } catch (error) {
|
|
|
- if (error instanceof z.ZodError) {
|
|
|
- return c.json({ code: 400, message: '参数验证失败', errors: error.errors }, 400);
|
|
|
- }
|
|
|
- return c.json({
|
|
|
- code: 500,
|
|
|
- message: error instanceof Error ? error.message : '创建资源失败'
|
|
|
- }, 500);
|
|
|
- }
|
|
|
- })
|
|
|
- .openapi(getRouteDef, async (c: any) => {
|
|
|
- try {
|
|
|
- const { id } = c.req.valid('param');
|
|
|
- const result = await crudService.getById(id, relations || []);
|
|
|
-
|
|
|
- if (!result) {
|
|
|
- return c.json({ code: 404, message: '资源不存在' }, 404);
|
|
|
+ })
|
|
|
+ .openapi(createRouteDef, async (c: any) => {
|
|
|
+ try {
|
|
|
+ const data = c.req.valid('json');
|
|
|
+ const user = c.get('user');
|
|
|
+ const result = await crudService.create(data, user?.id);
|
|
|
+ return c.json(result, 201);
|
|
|
+ } catch (error) {
|
|
|
+ if (error instanceof z.ZodError) {
|
|
|
+ return c.json({ code: 400, message: '参数验证失败', errors: JSON.parse(error.message) }, 400);
|
|
|
+ }
|
|
|
+ return c.json({
|
|
|
+ code: 500,
|
|
|
+ message: error instanceof Error ? error.message : '创建资源失败'
|
|
|
+ }, 500);
|
|
|
}
|
|
|
-
|
|
|
- return c.json(getSchema.parse(result), 200);
|
|
|
- } catch (error) {
|
|
|
- if (error instanceof z.ZodError) {
|
|
|
- return c.json({ code: 400, message: '参数验证失败', errors: error.errors }, 400);
|
|
|
+ })
|
|
|
+ .openapi(getRouteDef, async (c: any) => {
|
|
|
+ try {
|
|
|
+ const { id } = c.req.valid('param');
|
|
|
+ const result = await crudService.getById(id, relations || []);
|
|
|
+
|
|
|
+ if (!result) {
|
|
|
+ return c.json({ code: 404, message: '资源不存在' }, 404);
|
|
|
+ }
|
|
|
+
|
|
|
+ // return c.json(await getSchema.parseAsync(result), 200);
|
|
|
+ return c.json(await parseWithAwait(getSchema, result), 200);
|
|
|
+ } catch (error) {
|
|
|
+ if (error instanceof z.ZodError) {
|
|
|
+ return c.json({ code: 400, message: '参数验证失败', errors: JSON.parse(error.message) }, 400);
|
|
|
+ }
|
|
|
+ return c.json({
|
|
|
+ code: 500,
|
|
|
+ message: error instanceof Error ? error.message : '获取资源失败'
|
|
|
+ }, 500);
|
|
|
}
|
|
|
- return c.json({
|
|
|
- code: 500,
|
|
|
- message: error instanceof Error ? error.message : '获取资源失败'
|
|
|
- }, 500);
|
|
|
- }
|
|
|
- })
|
|
|
- .openapi(updateRouteDef, async (c: any) => {
|
|
|
- try {
|
|
|
- const { id } = c.req.valid('param');
|
|
|
- const data = c.req.valid('json');
|
|
|
- const user = c.get('user');
|
|
|
- const result = await crudService.update(id, data, user?.id);
|
|
|
-
|
|
|
- if (!result) {
|
|
|
- return c.json({ code: 404, message: '资源不存在' }, 404);
|
|
|
+ })
|
|
|
+ .openapi(updateRouteDef, async (c: any) => {
|
|
|
+ try {
|
|
|
+ const { id } = c.req.valid('param');
|
|
|
+ const data = c.req.valid('json');
|
|
|
+ const user = c.get('user');
|
|
|
+ const result = await crudService.update(id, data, user?.id);
|
|
|
+
|
|
|
+ if (!result) {
|
|
|
+ return c.json({ code: 404, message: '资源不存在' }, 404);
|
|
|
+ }
|
|
|
+
|
|
|
+ return c.json(result, 200);
|
|
|
+ } catch (error) {
|
|
|
+ if (error instanceof z.ZodError) {
|
|
|
+ return c.json({ code: 400, message: '参数验证失败', errors: JSON.parse(error.message) }, 400);
|
|
|
+ }
|
|
|
+ return c.json({
|
|
|
+ code: 500,
|
|
|
+ message: error instanceof Error ? error.message : '更新资源失败'
|
|
|
+ }, 500);
|
|
|
}
|
|
|
-
|
|
|
- return c.json(result, 200);
|
|
|
- } catch (error) {
|
|
|
- if (error instanceof z.ZodError) {
|
|
|
- return c.json({ code: 400, message: '参数验证失败', errors: error.errors }, 400);
|
|
|
+ })
|
|
|
+ .openapi(deleteRouteDef, async (c: any) => {
|
|
|
+ try {
|
|
|
+ const { id } = c.req.valid('param');
|
|
|
+ const success = await crudService.delete(id);
|
|
|
+
|
|
|
+ if (!success) {
|
|
|
+ return c.json({ code: 404, message: '资源不存在' }, 404);
|
|
|
+ }
|
|
|
+
|
|
|
+ return c.body(null, 204);
|
|
|
+ } catch (error) {
|
|
|
+ if (error instanceof z.ZodError) {
|
|
|
+ return c.json({ code: 400, message: '参数验证失败', errors: JSON.parse(error.message) }, 400);
|
|
|
+ }
|
|
|
+ return c.json({
|
|
|
+ code: 500,
|
|
|
+ message: error instanceof Error ? error.message : '删除资源失败'
|
|
|
+ }, 500);
|
|
|
}
|
|
|
- return c.json({
|
|
|
- code: 500,
|
|
|
- message: error instanceof Error ? error.message : '更新资源失败'
|
|
|
- }, 500);
|
|
|
- }
|
|
|
- })
|
|
|
- .openapi(deleteRouteDef, async (c: any) => {
|
|
|
- try {
|
|
|
- const { id } = c.req.valid('param');
|
|
|
- const success = await crudService.delete(id);
|
|
|
-
|
|
|
- if (!success) {
|
|
|
- return c.json({ code: 404, message: '资源不存在' }, 404);
|
|
|
+ });
|
|
|
+
|
|
|
+ return routes;
|
|
|
+ } else {
|
|
|
+ // 只读模式,只注册 GET 路由
|
|
|
+ const routes = app
|
|
|
+ .openapi(listRoute, async (c) => {
|
|
|
+ try {
|
|
|
+ const query = c.req.valid('query') as any;
|
|
|
+ const { page, pageSize, keyword, sortBy, sortOrder, filters } = query;
|
|
|
+
|
|
|
+ // 构建排序对象
|
|
|
+ const order: any = {};
|
|
|
+ if (sortBy) {
|
|
|
+ order[sortBy] = sortOrder || 'DESC';
|
|
|
+ } else {
|
|
|
+ order['id'] = 'DESC';
|
|
|
+ }
|
|
|
+
|
|
|
+ // 解析筛选条件
|
|
|
+ let parsedFilters: any = undefined;
|
|
|
+ if (filters) {
|
|
|
+ try {
|
|
|
+ parsedFilters = JSON.parse(filters);
|
|
|
+ } catch (e) {
|
|
|
+ return c.json({ code: 400, message: '筛选条件格式错误' }, 400);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const [data, total] = await crudService.getList(
|
|
|
+ page,
|
|
|
+ pageSize,
|
|
|
+ keyword,
|
|
|
+ searchFields,
|
|
|
+ undefined,
|
|
|
+ relations || [],
|
|
|
+ order,
|
|
|
+ parsedFilters
|
|
|
+ );
|
|
|
+
|
|
|
+ return c.json({
|
|
|
+ data: await parseWithAwait(z.array(listSchema), data),
|
|
|
+ pagination: { total, current: page, pageSize }
|
|
|
+ }, 200);
|
|
|
+ } catch (error) {
|
|
|
+ if (error instanceof z.ZodError) {
|
|
|
+ return c.json({ code: 400, message: '参数验证失败', errors: JSON.parse(error.message) }, 400);
|
|
|
+ }
|
|
|
+ return c.json({
|
|
|
+ code: 500,
|
|
|
+ message: error instanceof Error ? error.message : '获取列表失败'
|
|
|
+ }, 500);
|
|
|
}
|
|
|
-
|
|
|
- return c.body(null, 204);
|
|
|
- } catch (error) {
|
|
|
- if (error instanceof z.ZodError) {
|
|
|
- return c.json({ code: 400, message: '参数验证失败', errors: error.errors }, 400);
|
|
|
+ })
|
|
|
+ .openapi(getRouteDef, async (c: any) => {
|
|
|
+ try {
|
|
|
+ const { id } = c.req.valid('param');
|
|
|
+ const result = await crudService.getById(id, relations || []);
|
|
|
+
|
|
|
+ if (!result) {
|
|
|
+ return c.json({ code: 404, message: '资源不存在' }, 404);
|
|
|
+ }
|
|
|
+
|
|
|
+ return c.json(await parseWithAwait(getSchema, result), 200);
|
|
|
+ } catch (error) {
|
|
|
+ if (error instanceof z.ZodError) {
|
|
|
+ return c.json({ code: 400, message: '参数验证失败', errors: JSON.parse(error.message) }, 400);
|
|
|
+ }
|
|
|
+ return c.json({
|
|
|
+ code: 500,
|
|
|
+ message: error instanceof Error ? error.message : '获取资源失败'
|
|
|
+ }, 500);
|
|
|
}
|
|
|
- return c.json({
|
|
|
- code: 500,
|
|
|
- message: error instanceof Error ? error.message : '删除资源失败'
|
|
|
- }, 500);
|
|
|
- }
|
|
|
- });
|
|
|
+ });
|
|
|
+ return routes;
|
|
|
+ }
|
|
|
|
|
|
- return routes;
|
|
|
}
|