import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common'; import { PrismaClient } from '@prisma/client'; import { getRequestContext } from '../common/context/request-context.js'; /** 不带 tenantCode 的模型(租户表自身)。 */ const TENANT_FREE = new Set(['Tenant', 'Agency', 'SvcNational', 'SvcLocal', 'SvcMapping', 'LocConfig']); /** 软删模型:读操作统一注入 deletedAt IS NULL。 */ const SOFT_DELETE = new Set(['User', 'Role', 'Resource', 'Org', 'DictType', 'DictItem', 'Config', 'Notice', 'Staff', 'SvcOrg', 'Elder', 'Plan', 'Order', 'ElderSvc']); const READ_OPS = new Set(['findMany', 'findFirst', 'findUnique', 'count', 'aggregate', 'groupBy']); /** 没有 where 子句的操作(create 的租户由 service 在 data 里显式写入)。 */ const NO_WHERE_OPS = new Set(['create', 'createMany']); /** * Prisma 客户端封装:统一注入多租户过滤(tenantCode)与软删过滤(deletedAt)。 * 通过 `this.prisma.client` 访问扩展后的客户端。 */ @Injectable() export class PrismaService implements OnModuleInit, OnModuleDestroy { readonly client: PrismaClient; /** 未扩展的裸客户端(无租户/软删注入),供 seed/租户模板等跨租户初始化使用。 */ readonly raw: PrismaClient; constructor() { this.raw = new PrismaClient(); this.client = this.raw.$extends({ query: { $allModels: { async $allOperations({ model, operation, args, query }: any) { const ctx = getRequestContext(); if (ctx && !TENANT_FREE.has(model) && !NO_WHERE_OPS.has(operation)) { args.where = { ...(args.where ?? {}), tenantCode: ctx.tenantCode }; } if (SOFT_DELETE.has(model) && READ_OPS.has(operation)) { args.where = { ...(args.where ?? {}), deletedAt: null }; } return query(args); }, }, }, }) as unknown as PrismaClient; } async onModuleInit(): Promise { await this.client.$connect(); } async onModuleDestroy(): Promise { await this.client.$disconnect(); } }