Browse Source

✨ feat(api): 添加广告列表API接口
- 创建广告公共API路由文件,支持查询和获取单个广告功能
- 设置readOnly模式,仅开放GET请求接口

♻️ refactor(utils): 增强通用CRUD路由功能
- 添加readOnly选项,支持只读模式路由配置
- 根据readOnly状态动态注册路由,只读模式下仅注册GET路由
- 在CrudOptions类型定义中添加readOnly属性

yourname 3 months ago
parent
commit
8d7a0c80e6

+ 16 - 0
src/server/api/public/advertisements/index.ts

@@ -0,0 +1,16 @@
+import { createCrudRoutes } from '@/server/utils/generic-crud.routes';
+import { Advertisement } from '@/server/modules/advertisements/advertisement.entity';
+import { AdvertisementSchema, CreateAdvertisementDto, UpdateAdvertisementDto } from '@/server/modules/advertisements/advertisement.schema';
+
+const advertisementRoutes = createCrudRoutes({
+  entity: Advertisement,
+  createSchema: CreateAdvertisementDto,
+  updateSchema: UpdateAdvertisementDto,
+  getSchema: AdvertisementSchema,
+  listSchema: AdvertisementSchema,
+  searchFields: ['title', 'description'],
+  relations: ['imageFile'],
+  readOnly: true,
+});
+
+export default advertisementRoutes;

+ 201 - 123
src/server/utils/generic-crud.routes.ts

@@ -14,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服务实例
   // 抽象类不能直接实例化,需要创建具体实现类
@@ -218,135 +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),
-          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);
-      }
-    })
-    .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);
-      }
-    })
-    .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(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);
+      })
+      .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: JSON.parse(error.message) }, 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: JSON.parse(error.message) }, 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;
 }

+ 1 - 0
src/server/utils/generic-crud.service.ts

@@ -289,4 +289,5 @@ export type CrudOptions<
   middleware?: any[];
   userTracking?: UserTrackingOptions;
   relationFields?: RelationFieldOptions;
+  readOnly?: boolean;
 };