|
|
@@ -3,7 +3,7 @@ import { z } from 'zod';
|
|
|
import type { ZodError } from 'zod';
|
|
|
import { ObjectLiteral } from 'typeorm';
|
|
|
import { CrudOptions } from '../services/generic-crud.service';
|
|
|
-import { ErrorSchema } from '@d8d/shared-utils';
|
|
|
+import { ErrorSchema, AppDataSource } from '@d8d/shared-utils';
|
|
|
import { AuthContext } from '@d8d/shared-types';
|
|
|
import { parseWithAwait } from '@d8d/shared-utils';
|
|
|
import { ConcreteCrudService } from '../services/concrete-crud.service';
|
|
|
@@ -20,11 +20,32 @@ export function createCrudRoutes<
|
|
|
>(
|
|
|
options: CrudOptions<T, CreateSchema, UpdateSchema, GetSchema, ListSchema>
|
|
|
) {
|
|
|
- const { entity, createSchema, updateSchema, getSchema, listSchema, searchFields, relations, middleware = [], userTracking, relationFields, readOnly = false, dataPermission, defaultFilters, tenantOptions } = options;
|
|
|
+ const { entity, createSchema, updateSchema, getSchema, listSchema, searchFields, relations, middleware = [], userTracking, relationFields, readOnly = false, dataPermission, defaultFilters, listFilters, detailFilters, tenantOptions, serviceFactory } = options;
|
|
|
|
|
|
// 创建路由实例
|
|
|
const app = new OpenAPIHono<AuthContext>();
|
|
|
|
|
|
+ // 辅助函数:创建CRUD服务实例
|
|
|
+ const createServiceInstance = () => {
|
|
|
+ if (serviceFactory) {
|
|
|
+ // 使用自定义服务工厂
|
|
|
+ return serviceFactory(AppDataSource, entity, {
|
|
|
+ userTracking,
|
|
|
+ relationFields,
|
|
|
+ dataPermission,
|
|
|
+ tenantOptions
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ // 使用默认的ConcreteCrudService
|
|
|
+ return new ConcreteCrudService(entity, {
|
|
|
+ userTracking,
|
|
|
+ relationFields,
|
|
|
+ dataPermission,
|
|
|
+ tenantOptions
|
|
|
+ });
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
// 分页查询路由
|
|
|
const listRoute = createRoute({
|
|
|
method: 'get',
|
|
|
@@ -263,22 +284,26 @@ export function createCrudRoutes<
|
|
|
}
|
|
|
|
|
|
// 解析筛选条件
|
|
|
- let parsedFilters: any = { ...defaultFilters };
|
|
|
+ // 优先使用listFilters,如果没有则使用defaultFilters(向后兼容)
|
|
|
+ const effectiveListFilters = listFilters || defaultFilters;
|
|
|
+ let parsedFilters: any = { ...effectiveListFilters };
|
|
|
if (filters) {
|
|
|
try {
|
|
|
const userFilters = JSON.parse(filters);
|
|
|
// 合并默认过滤条件和用户传入的过滤条件
|
|
|
+ // 用户传入的值会覆盖默认值
|
|
|
parsedFilters = { ...parsedFilters, ...userFilters };
|
|
|
+
|
|
|
+ // 特殊处理:如果用户显式传入spuId: null,则移除spuId过滤
|
|
|
+ // 这样用户可以显示所有商品(包括子商品)
|
|
|
+ if (userFilters.spuId === null) {
|
|
|
+ delete parsedFilters.spuId;
|
|
|
+ }
|
|
|
} catch (e) {
|
|
|
return c.json({ code: 400, message: '筛选条件格式错误' }, 400);
|
|
|
}
|
|
|
}
|
|
|
- const crudService = new ConcreteCrudService(entity, {
|
|
|
- userTracking: userTracking,
|
|
|
- relationFields: relationFields,
|
|
|
- dataPermission: dataPermission,
|
|
|
- tenantOptions: tenantOptions
|
|
|
- });
|
|
|
+ const crudService = createServiceInstance();
|
|
|
|
|
|
// 设置租户上下文
|
|
|
const tenantId = c.get('tenantId');
|
|
|
@@ -340,12 +365,7 @@ export function createCrudRoutes<
|
|
|
const data = c.req.valid('json');
|
|
|
const user = c.get('user');
|
|
|
|
|
|
- const crudService = new ConcreteCrudService(entity, {
|
|
|
- userTracking: userTracking,
|
|
|
- relationFields: relationFields,
|
|
|
- dataPermission: dataPermission,
|
|
|
- tenantOptions: tenantOptions
|
|
|
- });
|
|
|
+ const crudService = createServiceInstance();
|
|
|
|
|
|
// 设置租户上下文
|
|
|
const tenantId = c.get('tenantId');
|
|
|
@@ -394,12 +414,7 @@ export function createCrudRoutes<
|
|
|
const { id } = c.req.valid('param');
|
|
|
const user = c.get('user');
|
|
|
|
|
|
- const crudService = new ConcreteCrudService(entity, {
|
|
|
- userTracking: userTracking,
|
|
|
- relationFields: relationFields,
|
|
|
- dataPermission: dataPermission,
|
|
|
- tenantOptions: tenantOptions
|
|
|
- });
|
|
|
+ const crudService = createServiceInstance();
|
|
|
|
|
|
// 设置租户上下文
|
|
|
const tenantId = c.get('tenantId');
|
|
|
@@ -419,9 +434,11 @@ export function createCrudRoutes<
|
|
|
return c.json({ code: 404, message: '资源不存在' }, 404);
|
|
|
}
|
|
|
|
|
|
- // 应用默认过滤条件
|
|
|
- if (defaultFilters && Object.keys(defaultFilters).length > 0) {
|
|
|
- const shouldFilter = Object.entries(defaultFilters).some(([key, value]) => {
|
|
|
+ // 应用detailFilters(如果提供),否则使用defaultFilters
|
|
|
+ // 这是为了向后兼容:当没有提供detailFilters时,使用defaultFilters
|
|
|
+ const effectiveDetailFilters = detailFilters !== undefined ? detailFilters : defaultFilters;
|
|
|
+ if (effectiveDetailFilters && Object.keys(effectiveDetailFilters).length > 0) {
|
|
|
+ const shouldFilter = Object.entries(effectiveDetailFilters).some(([key, value]) => {
|
|
|
return result[key as keyof T] !== value;
|
|
|
});
|
|
|
if (shouldFilter) {
|
|
|
@@ -429,8 +446,13 @@ export function createCrudRoutes<
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // return c.json(await getSchema.parseAsync(result), 200);
|
|
|
- return c.json(await parseWithAwait(getSchema, result), 200);
|
|
|
+ try {
|
|
|
+ const validatedResult = await parseWithAwait(getSchema, result);
|
|
|
+ return c.json(validatedResult, 200);
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Schema验证失败:', error);
|
|
|
+ return c.json({ code: 500, message: '数据验证失败' }, 500);
|
|
|
+ }
|
|
|
} catch (error) {
|
|
|
if (error instanceof z.ZodError) {
|
|
|
const zodError = error as ZodError;
|
|
|
@@ -453,12 +475,7 @@ export function createCrudRoutes<
|
|
|
const data = c.req.valid('json');
|
|
|
const user = c.get('user');
|
|
|
|
|
|
- const crudService = new ConcreteCrudService(entity, {
|
|
|
- userTracking: userTracking,
|
|
|
- relationFields: relationFields,
|
|
|
- dataPermission: dataPermission,
|
|
|
- tenantOptions: tenantOptions
|
|
|
- });
|
|
|
+ const crudService = createServiceInstance();
|
|
|
|
|
|
// 设置租户上下文
|
|
|
const tenantId = c.get('tenantId');
|
|
|
@@ -506,12 +523,7 @@ export function createCrudRoutes<
|
|
|
const { id } = c.req.valid('param');
|
|
|
const user = c.get('user');
|
|
|
|
|
|
- const crudService = new ConcreteCrudService(entity, {
|
|
|
- userTracking: userTracking,
|
|
|
- relationFields: relationFields,
|
|
|
- dataPermission: dataPermission,
|
|
|
- tenantOptions: tenantOptions
|
|
|
- });
|
|
|
+ const crudService = createServiceInstance();
|
|
|
|
|
|
// 设置租户上下文
|
|
|
const tenantId = c.get('tenantId');
|
|
|
@@ -572,22 +584,26 @@ export function createCrudRoutes<
|
|
|
}
|
|
|
|
|
|
// 解析筛选条件
|
|
|
- let parsedFilters: any = { ...defaultFilters };
|
|
|
+ // 优先使用listFilters,如果没有则使用defaultFilters(向后兼容)
|
|
|
+ const effectiveListFilters = listFilters || defaultFilters;
|
|
|
+ let parsedFilters: any = { ...effectiveListFilters };
|
|
|
if (filters) {
|
|
|
try {
|
|
|
const userFilters = JSON.parse(filters);
|
|
|
// 合并默认过滤条件和用户传入的过滤条件
|
|
|
+ // 用户传入的值会覆盖默认值
|
|
|
parsedFilters = { ...parsedFilters, ...userFilters };
|
|
|
+
|
|
|
+ // 特殊处理:如果用户显式传入spuId: null,则移除spuId过滤
|
|
|
+ // 这样用户可以显示所有商品(包括子商品)
|
|
|
+ if (userFilters.spuId === null) {
|
|
|
+ delete parsedFilters.spuId;
|
|
|
+ }
|
|
|
} catch (e) {
|
|
|
return c.json({ code: 400, message: '筛选条件格式错误' }, 400);
|
|
|
}
|
|
|
}
|
|
|
- const crudService = new ConcreteCrudService(entity, {
|
|
|
- userTracking: userTracking,
|
|
|
- relationFields: relationFields,
|
|
|
- dataPermission: dataPermission,
|
|
|
- tenantOptions: tenantOptions
|
|
|
- });
|
|
|
+ const crudService = createServiceInstance();
|
|
|
|
|
|
// 设置租户上下文
|
|
|
const tenantId = c.get('tenantId');
|
|
|
@@ -644,12 +660,7 @@ export function createCrudRoutes<
|
|
|
const { id } = c.req.valid('param');
|
|
|
const user = c.get('user');
|
|
|
|
|
|
- const crudService = new ConcreteCrudService(entity, {
|
|
|
- userTracking: userTracking,
|
|
|
- relationFields: relationFields,
|
|
|
- dataPermission: dataPermission,
|
|
|
- tenantOptions: tenantOptions
|
|
|
- });
|
|
|
+ const crudService = createServiceInstance();
|
|
|
|
|
|
// 设置租户上下文
|
|
|
const tenantId = c.get('tenantId');
|
|
|
@@ -669,9 +680,11 @@ export function createCrudRoutes<
|
|
|
return c.json({ code: 404, message: '资源不存在' }, 404);
|
|
|
}
|
|
|
|
|
|
- // 应用默认过滤条件
|
|
|
- if (defaultFilters && Object.keys(defaultFilters).length > 0) {
|
|
|
- const shouldFilter = Object.entries(defaultFilters).some(([key, value]) => {
|
|
|
+ // 应用detailFilters(如果提供),否则使用defaultFilters
|
|
|
+ // 这是为了向后兼容:当没有提供detailFilters时,使用defaultFilters
|
|
|
+ const effectiveDetailFilters = detailFilters !== undefined ? detailFilters : defaultFilters;
|
|
|
+ if (effectiveDetailFilters && Object.keys(effectiveDetailFilters).length > 0) {
|
|
|
+ const shouldFilter = Object.entries(effectiveDetailFilters).some(([key, value]) => {
|
|
|
return result[key as keyof T] !== value;
|
|
|
});
|
|
|
if (shouldFilter) {
|
|
|
@@ -679,7 +692,13 @@ export function createCrudRoutes<
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return c.json(await parseWithAwait(getSchema, result), 200);
|
|
|
+ try {
|
|
|
+ const validatedResult = await parseWithAwait(getSchema, result);
|
|
|
+ return c.json(validatedResult, 200);
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Schema验证失败:', error);
|
|
|
+ return c.json({ code: 500, message: '数据验证失败' }, 500);
|
|
|
+ }
|
|
|
} catch (error) {
|
|
|
if (error instanceof z.ZodError) {
|
|
|
const zodError = error as ZodError;
|