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> {}
*
* @example
* // 启用软删除
* @CrudOptions({ softDelete: true })
* class UserService extends CrudService<User, CreateUserDto, UpdateUserDto> {}
*
* @example
* // 自定义分页配置
* @CrudOptions({
* softDelete: true,
* defaultPageSize: 10,
* maxPageSize: 50,
* defaultSortBy: 'name',

View File

@@ -1,4 +1,5 @@
import { NotFoundException } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { PaginatedResponse } from '@seclusion/shared';
import { getCrudOptions } from './crud.decorator';
@@ -6,6 +7,13 @@ import { CrudServiceOptions, FindAllParams, PrismaDelegate, SoftDeletable } from
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 泛型基类
*
@@ -19,7 +27,6 @@ import { PrismaService } from '@/prisma/prisma.service';
* import { User, Prisma } from '@prisma/client';
*
* @Injectable()
* @CrudOptions({ softDelete: true })
* export class UserService extends CrudService<
* User,
* Prisma.UserCreateInput,
@@ -40,11 +47,13 @@ export abstract class CrudService<
WhereUniqueInput = { id: string },
> {
protected readonly options: Required<CrudServiceOptions>;
protected readonly prisma: PrismaService;
protected constructor(
protected readonly prisma: PrismaService,
prisma: PrismaService,
protected readonly modelName: string
) {
this.prisma = prisma;
// 从装饰器获取配置,如果没有则使用默认配置
this.options = getCrudOptions(this);
}
@@ -216,18 +225,25 @@ export abstract class CrudService<
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 时返回所有数据(不分页)
*/
async findDeleted(
params: FindAllParams<WhereInput> = {}
): Promise<PaginatedResponse<Entity & SoftDeletable>> {
if (!this.options.softDelete) {
throw new Error('此服务未启用软删除功能');
if (!this.supportsSoftDelete) {
throw new Error('此模型不支持软删除功能');
}
const {
@@ -288,12 +304,12 @@ export abstract class CrudService<
/**
* 恢复已删除的记录
* 仅当 softDelete: true 时有效
* 仅当模型有 deletedAt 字段时有效
* @throws NotFoundException 当已删除的记录不存在时
*/
async restore(id: string): Promise<Entity> {
if (!this.options.softDelete) {
throw new Error('此服务未启用软删除功能');
if (!this.supportsSoftDelete) {
throw new Error('此模型不支持软删除功能');
}
// 查询已删除的记录

View File

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