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:
@@ -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',
|
||||||
|
|||||||
@@ -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('此模型不支持软删除功能');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询已删除的记录
|
// 查询已删除的记录
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
Reference in New Issue
Block a user