refactor(api): 移除手动 softDelete 配置,改为基于 schema 自动检测

- 从 CrudServiceOptions 中移除 softDelete 属性
- CrudService 基于 Prisma schema 自动检测模型是否支持软删除
- 新增 supportsSoftDelete getter 用于判断软删除支持
- 更新 findDeleted/restore 方法使用自动检测
- PrismaService 底层已自动处理软删除逻辑

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
charilezhou
2026-01-19 12:33:46 +08:00
parent 73db0eb809
commit 57456be013
3 changed files with 25 additions and 18 deletions

View File

@@ -14,14 +14,8 @@ export const CRUD_OPTIONS_KEY = 'crud:options';
* class UserService extends CrudService<User, CreateUserDto, UpdateUserDto> {} * class UserService extends CrudService<User, CreateUserDto, UpdateUserDto> {}
* *
* @example * @example
* // 启用软删除
* @CrudOptions({ softDelete: true })
* class UserService extends CrudService<User, CreateUserDto, UpdateUserDto> {}
*
* @example
* // 自定义分页配置 * // 自定义分页配置
* @CrudOptions({ * @CrudOptions({
* softDelete: true,
* defaultPageSize: 10, * defaultPageSize: 10,
* maxPageSize: 50, * maxPageSize: 50,
* defaultSortBy: 'name', * defaultSortBy: 'name',

View File

@@ -1,4 +1,5 @@
import { NotFoundException } from '@nestjs/common'; import { NotFoundException } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { PaginatedResponse } from '@seclusion/shared'; import { PaginatedResponse } from '@seclusion/shared';
import { getCrudOptions } from './crud.decorator'; import { getCrudOptions } from './crud.decorator';
@@ -6,6 +7,13 @@ import { CrudServiceOptions, FindAllParams, PrismaDelegate, SoftDeletable } from
import { PrismaService } from '@/prisma/prisma.service'; import { PrismaService } from '@/prisma/prisma.service';
// 自动检测支持软删除的模型(有 deletedAt 字段的模型)
const SOFT_DELETE_MODELS = new Set(
Prisma.dmmf.datamodel.models
.filter((model) => model.fields.some((field) => field.name === 'deletedAt'))
.map((model) => model.name.toLowerCase())
);
/** /**
* CRUD 泛型基类 * CRUD 泛型基类
* *
@@ -19,7 +27,6 @@ import { PrismaService } from '@/prisma/prisma.service';
* import { User, Prisma } from '@prisma/client'; * import { User, Prisma } from '@prisma/client';
* *
* @Injectable() * @Injectable()
* @CrudOptions({ softDelete: true })
* export class UserService extends CrudService< * export class UserService extends CrudService<
* User, * User,
* Prisma.UserCreateInput, * Prisma.UserCreateInput,
@@ -40,11 +47,13 @@ export abstract class CrudService<
WhereUniqueInput = { id: string }, WhereUniqueInput = { id: string },
> { > {
protected readonly options: Required<CrudServiceOptions>; protected readonly options: Required<CrudServiceOptions>;
protected readonly prisma: PrismaService;
protected constructor( protected constructor(
protected readonly prisma: PrismaService, prisma: PrismaService,
protected readonly modelName: string protected readonly modelName: string
) { ) {
this.prisma = prisma;
// 从装饰器获取配置,如果没有则使用默认配置 // 从装饰器获取配置,如果没有则使用默认配置
this.options = getCrudOptions(this); this.options = getCrudOptions(this);
} }
@@ -216,18 +225,25 @@ export abstract class CrudService<
return { message: this.getDeletedMessage(id) }; return { message: this.getDeletedMessage(id) };
} }
// ============ 软删除相关方法(仅当 softDelete: true 时可用)============ // ============ 软删除相关方法(仅当模型有 deletedAt 字段时可用)============
/**
* 判断当前模型是否支持软删除
*/
protected get supportsSoftDelete(): boolean {
return SOFT_DELETE_MODELS.has(this.modelName.toLowerCase());
}
/** /**
* 查询已删除的记录 * 查询已删除的记录
* 仅当 softDelete: true 时有效 * 仅当模型有 deletedAt 字段时有效
* pageSize <= 0 时返回所有数据(不分页) * pageSize <= 0 时返回所有数据(不分页)
*/ */
async findDeleted( async findDeleted(
params: FindAllParams<WhereInput> = {} params: FindAllParams<WhereInput> = {}
): Promise<PaginatedResponse<Entity & SoftDeletable>> { ): Promise<PaginatedResponse<Entity & SoftDeletable>> {
if (!this.options.softDelete) { if (!this.supportsSoftDelete) {
throw new Error('此服务未启用软删除功能'); throw new Error('此模型不支持软删除功能');
} }
const { const {
@@ -288,12 +304,12 @@ export abstract class CrudService<
/** /**
* 恢复已删除的记录 * 恢复已删除的记录
* 仅当 softDelete: true 时有效 * 仅当模型有 deletedAt 字段时有效
* @throws NotFoundException 当已删除的记录不存在时 * @throws NotFoundException 当已删除的记录不存在时
*/ */
async restore(id: string): Promise<Entity> { async restore(id: string): Promise<Entity> {
if (!this.options.softDelete) { if (!this.supportsSoftDelete) {
throw new Error('此服务未启用软删除功能'); throw new Error('此模型不支持软删除功能');
} }
// 查询已删除的记录 // 查询已删除的记录

View File

@@ -4,8 +4,6 @@ import { PaginationParams } from '@seclusion/shared';
* CRUD 服务配置选项 * CRUD 服务配置选项
*/ */
export interface CrudServiceOptions { export interface CrudServiceOptions {
/** 是否启用软删除(默认 false */
softDelete?: boolean;
/** 默认分页大小 */ /** 默认分页大小 */
defaultPageSize?: number; defaultPageSize?: number;
/** 最大分页大小 */ /** 最大分页大小 */
@@ -22,7 +20,6 @@ export interface CrudServiceOptions {
* 默认配置 * 默认配置
*/ */
export const DEFAULT_CRUD_OPTIONS: Required<CrudServiceOptions> = { export const DEFAULT_CRUD_OPTIONS: Required<CrudServiceOptions> = {
softDelete: false,
defaultPageSize: 20, defaultPageSize: 20,
maxPageSize: 100, maxPageSize: 100,
defaultSortBy: 'createdAt', defaultSortBy: 'createdAt',