|
|
@@ -0,0 +1,443 @@
|
|
|
+import { OpenAPIHono, createRoute, z } from '@hono/zod-openapi';
|
|
|
+import { AppDataSource, ErrorSchema, parseWithAwait, createZodErrorResponse } from '@d8d/shared-utils';
|
|
|
+import { tenantAuthMiddleware } from '@d8d/tenant-module-mt';
|
|
|
+import { UnifiedFileService } from '../../services/unified-file.service';
|
|
|
+import {
|
|
|
+ UnifiedFileSchema,
|
|
|
+ CreateUnifiedFileDto,
|
|
|
+ UpdateUnifiedFileDto
|
|
|
+} from '../../schemas/unified-file.schema';
|
|
|
+
|
|
|
+interface AdminContext {
|
|
|
+ Variables: {
|
|
|
+ superAdminId?: number;
|
|
|
+ user?: any;
|
|
|
+ token?: string;
|
|
|
+ tenantId?: number;
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+const CommonErrorSchema = z.object({
|
|
|
+ code: z.number(),
|
|
|
+ message: z.string()
|
|
|
+});
|
|
|
+
|
|
|
+const getService = () => {
|
|
|
+ return new UnifiedFileService(AppDataSource);
|
|
|
+};
|
|
|
+
|
|
|
+const listRoute = createRoute({
|
|
|
+ method: 'get',
|
|
|
+ path: '/',
|
|
|
+ middleware: [tenantAuthMiddleware] as const,
|
|
|
+ request: {
|
|
|
+ query: z.object({
|
|
|
+ page: z.coerce.number<number>().int().positive().default(1).openapi({
|
|
|
+ example: 1,
|
|
|
+ description: '页码'
|
|
|
+ }),
|
|
|
+ pageSize: z.coerce.number<number>().int().positive().default(10).openapi({
|
|
|
+ example: 10,
|
|
|
+ description: '每页数量'
|
|
|
+ }),
|
|
|
+ keyword: z.string().optional().openapi({
|
|
|
+ example: 'banner',
|
|
|
+ description: '搜索关键词'
|
|
|
+ }),
|
|
|
+ status: z.coerce.number<number>().int().min(0).max(1).optional().openapi({
|
|
|
+ example: 1,
|
|
|
+ description: '状态筛选'
|
|
|
+ })
|
|
|
+ })
|
|
|
+ },
|
|
|
+ responses: {
|
|
|
+ 200: {
|
|
|
+ description: '成功获取文件列表',
|
|
|
+ content: {
|
|
|
+ 'application/json': {
|
|
|
+ schema: z.object({
|
|
|
+ code: z.number(),
|
|
|
+ message: z.string(),
|
|
|
+ data: z.object({
|
|
|
+ list: z.array(UnifiedFileSchema),
|
|
|
+ total: z.number(),
|
|
|
+ page: z.number(),
|
|
|
+ pageSize: z.number()
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ 400: {
|
|
|
+ description: '验证错误',
|
|
|
+ content: {
|
|
|
+ 'application/json': {
|
|
|
+ schema: z.object({
|
|
|
+ code: z.number(),
|
|
|
+ message: z.string(),
|
|
|
+ errors: z.array(z.object({
|
|
|
+ path: z.array(z.union([z.string(), z.number()])),
|
|
|
+ message: z.string(),
|
|
|
+ code: z.string()
|
|
|
+ })).optional()
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ 500: {
|
|
|
+ description: '服务器错误',
|
|
|
+ content: {
|
|
|
+ 'application/json': {
|
|
|
+ schema: CommonErrorSchema
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+const getRoute = createRoute({
|
|
|
+ method: 'get',
|
|
|
+ path: '/:id',
|
|
|
+ middleware: [tenantAuthMiddleware] as const,
|
|
|
+ request: {
|
|
|
+ params: z.object({
|
|
|
+ id: z.coerce.number<number>().openapi({
|
|
|
+ param: { name: 'id', in: 'path' },
|
|
|
+ example: 1,
|
|
|
+ description: '文件ID'
|
|
|
+ })
|
|
|
+ })
|
|
|
+ },
|
|
|
+ responses: {
|
|
|
+ 200: {
|
|
|
+ description: '成功获取文件详情',
|
|
|
+ content: {
|
|
|
+ 'application/json': {
|
|
|
+ schema: z.object({
|
|
|
+ code: z.number(),
|
|
|
+ message: z.string(),
|
|
|
+ data: UnifiedFileSchema
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ 400: {
|
|
|
+ description: '验证错误',
|
|
|
+ content: {
|
|
|
+ 'application/json': {
|
|
|
+ schema: z.object({
|
|
|
+ code: z.number(),
|
|
|
+ message: z.string()
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ 404: {
|
|
|
+ description: '文件不存在',
|
|
|
+ content: {
|
|
|
+ 'application/json': {
|
|
|
+ schema: ErrorSchema
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ 500: {
|
|
|
+ description: '服务器错误',
|
|
|
+ content: {
|
|
|
+ 'application/json': {
|
|
|
+ schema: CommonErrorSchema
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+const createRouteDef = createRoute({
|
|
|
+ method: 'post',
|
|
|
+ path: '/',
|
|
|
+ middleware: [tenantAuthMiddleware] as const,
|
|
|
+ request: {
|
|
|
+ body: {
|
|
|
+ content: {
|
|
|
+ 'application/json': {
|
|
|
+ schema: CreateUnifiedFileDto
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ responses: {
|
|
|
+ 201: {
|
|
|
+ description: '成功创建文件',
|
|
|
+ content: {
|
|
|
+ 'application/json': {
|
|
|
+ schema: z.object({
|
|
|
+ code: z.number(),
|
|
|
+ message: z.string(),
|
|
|
+ data: UnifiedFileSchema
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ 400: {
|
|
|
+ description: '验证错误',
|
|
|
+ content: {
|
|
|
+ 'application/json': {
|
|
|
+ schema: z.object({
|
|
|
+ code: z.number(),
|
|
|
+ message: z.string()
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ 500: {
|
|
|
+ description: '服务器错误',
|
|
|
+ content: {
|
|
|
+ 'application/json': {
|
|
|
+ schema: CommonErrorSchema
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+const updateRoute = createRoute({
|
|
|
+ method: 'put',
|
|
|
+ path: '/:id',
|
|
|
+ middleware: [tenantAuthMiddleware] as const,
|
|
|
+ request: {
|
|
|
+ params: z.object({
|
|
|
+ id: z.coerce.number<number>().openapi({
|
|
|
+ param: { name: 'id', in: 'path' },
|
|
|
+ example: 1,
|
|
|
+ description: '文件ID'
|
|
|
+ })
|
|
|
+ }),
|
|
|
+ body: {
|
|
|
+ content: {
|
|
|
+ 'application/json': {
|
|
|
+ schema: UpdateUnifiedFileDto
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ responses: {
|
|
|
+ 200: {
|
|
|
+ description: '成功更新文件',
|
|
|
+ content: {
|
|
|
+ 'application/json': {
|
|
|
+ schema: z.object({
|
|
|
+ code: z.number(),
|
|
|
+ message: z.string(),
|
|
|
+ data: UnifiedFileSchema
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ 400: {
|
|
|
+ description: '验证错误',
|
|
|
+ content: {
|
|
|
+ 'application/json': {
|
|
|
+ schema: z.object({
|
|
|
+ code: z.number(),
|
|
|
+ message: z.string()
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ 404: {
|
|
|
+ description: '文件不存在',
|
|
|
+ content: {
|
|
|
+ 'application/json': {
|
|
|
+ schema: ErrorSchema
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ 500: {
|
|
|
+ description: '服务器错误',
|
|
|
+ content: {
|
|
|
+ 'application/json': {
|
|
|
+ schema: CommonErrorSchema
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+const deleteRoute = createRoute({
|
|
|
+ method: 'delete',
|
|
|
+ path: '/:id',
|
|
|
+ middleware: [tenantAuthMiddleware] as const,
|
|
|
+ request: {
|
|
|
+ params: z.object({
|
|
|
+ id: z.coerce.number<number>().openapi({
|
|
|
+ param: { name: 'id', in: 'path' },
|
|
|
+ example: 1,
|
|
|
+ description: '文件ID'
|
|
|
+ })
|
|
|
+ })
|
|
|
+ },
|
|
|
+ responses: {
|
|
|
+ 200: {
|
|
|
+ description: '成功删除文件',
|
|
|
+ content: {
|
|
|
+ 'application/json': {
|
|
|
+ schema: z.object({
|
|
|
+ code: z.number(),
|
|
|
+ message: z.string()
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ 404: {
|
|
|
+ description: '文件不存在',
|
|
|
+ content: {
|
|
|
+ 'application/json': {
|
|
|
+ schema: ErrorSchema
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ 500: {
|
|
|
+ description: '服务器错误',
|
|
|
+ content: {
|
|
|
+ 'application/json': {
|
|
|
+ schema: CommonErrorSchema
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+const app = new OpenAPIHono<AdminContext>()
|
|
|
+ .openapi(listRoute, async (c) => {
|
|
|
+ try {
|
|
|
+ const query = c.req.valid('query');
|
|
|
+ const { page, pageSize, keyword, status } = query;
|
|
|
+ const service = getService();
|
|
|
+
|
|
|
+ const searchFields = ['name', 'description'];
|
|
|
+ const where = status !== undefined ? { status } : undefined;
|
|
|
+
|
|
|
+ const [list, total] = await service.getList(
|
|
|
+ page,
|
|
|
+ pageSize,
|
|
|
+ keyword,
|
|
|
+ searchFields,
|
|
|
+ where,
|
|
|
+ [],
|
|
|
+ { createdAt: 'DESC' }
|
|
|
+ );
|
|
|
+
|
|
|
+ const validatedList = await parseWithAwait(z.array(UnifiedFileSchema), list);
|
|
|
+
|
|
|
+ return c.json({
|
|
|
+ code: 200,
|
|
|
+ message: 'success',
|
|
|
+ data: {
|
|
|
+ list: validatedList,
|
|
|
+ total,
|
|
|
+ page,
|
|
|
+ pageSize
|
|
|
+ }
|
|
|
+ }, 200);
|
|
|
+ } catch (error) {
|
|
|
+ if ((error as Error).name === 'ZodError') {
|
|
|
+ return c.json(createZodErrorResponse(error as Error), 400);
|
|
|
+ }
|
|
|
+ return c.json({ code: 500, message: error instanceof Error ? error.message : 'Internal Server Error' }, 500);
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .openapi(getRoute, async (c) => {
|
|
|
+ try {
|
|
|
+ const { id } = c.req.valid('param');
|
|
|
+ const service = getService();
|
|
|
+
|
|
|
+ const file = await service.getById(id);
|
|
|
+
|
|
|
+ if (!file) {
|
|
|
+ return c.json({ code: 404, message: 'File not found' }, 404);
|
|
|
+ }
|
|
|
+
|
|
|
+ const validatedData = await parseWithAwait(UnifiedFileSchema, file);
|
|
|
+
|
|
|
+ return c.json({
|
|
|
+ code: 200,
|
|
|
+ message: 'success',
|
|
|
+ data: validatedData
|
|
|
+ }, 200);
|
|
|
+ } catch (error) {
|
|
|
+ if ((error as Error).name === 'ZodError') {
|
|
|
+ return c.json(createZodErrorResponse(error as Error), 400);
|
|
|
+ }
|
|
|
+ return c.json({ code: 500, message: error instanceof Error ? error.message : 'Internal Server Error' }, 500);
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .openapi(createRouteDef, async (c) => {
|
|
|
+ try {
|
|
|
+ const body = c.req.valid('json');
|
|
|
+ const superAdminId = c.get('superAdminId') || 1;
|
|
|
+ const service = getService();
|
|
|
+
|
|
|
+ const file = await service.create(body, superAdminId);
|
|
|
+
|
|
|
+ const validatedData = await parseWithAwait(UnifiedFileSchema, file);
|
|
|
+
|
|
|
+ return c.json({
|
|
|
+ code: 201,
|
|
|
+ message: 'File created successfully',
|
|
|
+ data: validatedData
|
|
|
+ }, 201);
|
|
|
+ } catch (error) {
|
|
|
+ if ((error as Error).name === 'ZodError') {
|
|
|
+ return c.json(createZodErrorResponse(error as Error), 400);
|
|
|
+ }
|
|
|
+ return c.json({ code: 500, message: error instanceof Error ? error.message : 'Internal Server Error' }, 500);
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .openapi(updateRoute, async (c) => {
|
|
|
+ try {
|
|
|
+ const { id } = c.req.valid('param');
|
|
|
+ const body = c.req.valid('json');
|
|
|
+ const superAdminId = c.get('superAdminId') || 1;
|
|
|
+ const service = getService();
|
|
|
+
|
|
|
+ const file = await service.update(id, body, superAdminId);
|
|
|
+
|
|
|
+ if (!file) {
|
|
|
+ return c.json({ code: 404, message: 'File not found' }, 404);
|
|
|
+ }
|
|
|
+
|
|
|
+ const validatedData = await parseWithAwait(UnifiedFileSchema, file);
|
|
|
+
|
|
|
+ return c.json({
|
|
|
+ code: 200,
|
|
|
+ message: 'File updated successfully',
|
|
|
+ data: validatedData
|
|
|
+ }, 200);
|
|
|
+ } catch (error) {
|
|
|
+ if ((error as Error).name === 'ZodError') {
|
|
|
+ return c.json(createZodErrorResponse(error as Error), 400);
|
|
|
+ }
|
|
|
+ return c.json({ code: 500, message: error instanceof Error ? error.message : 'Internal Server Error' }, 500);
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .openapi(deleteRoute, async (c) => {
|
|
|
+ try {
|
|
|
+ const { id } = c.req.valid('param');
|
|
|
+ const superAdminId = c.get('superAdminId') || 1;
|
|
|
+ const service = getService();
|
|
|
+
|
|
|
+ const success = await service.delete(id, superAdminId);
|
|
|
+
|
|
|
+ if (!success) {
|
|
|
+ return c.json({ code: 404, message: 'File not found' }, 404);
|
|
|
+ }
|
|
|
+
|
|
|
+ return c.json({
|
|
|
+ code: 200,
|
|
|
+ message: 'File deleted successfully'
|
|
|
+ }, 200);
|
|
|
+ } catch (error) {
|
|
|
+ return c.json({ code: 500, message: error instanceof Error ? error.message : 'Internal Server Error' }, 500);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+export default app;
|