64 lines
3.0 KiB
TypeScript
64 lines
3.0 KiB
TypeScript
import { HttpStatus, Injectable } from '@nestjs/common';
|
||
import { PrismaService } from '../prisma/prisma.service.js';
|
||
import { getRequestContext } from '../common/context/request-context.js';
|
||
import { ErrorCodes } from '../common/error-codes.js';
|
||
import { BusinessException } from '../common/business-exception.js';
|
||
import { ConfigSvcOrgDto } from './dto.js';
|
||
|
||
/**
|
||
* 服务目录四层(L1 国家基准库 / L2 属地目录 / L3 映射(机构不可见) / L4 机构项目库)。
|
||
*
|
||
* - SvcNational/SvcLocal/SvcMapping 是全局表(TENANT_FREE),读取不会注入 tenantCode;
|
||
* - SvcOrg 是租户级表(读自动注入 tenantCode + 软删),tenantCode 由本 service 显式写入
|
||
* (读操作再次被 PrismaService 扩展注入同值 tenantCode,幂等)。
|
||
*/
|
||
@Injectable()
|
||
export class CatalogService {
|
||
constructor(private readonly prisma: PrismaService) {}
|
||
|
||
/** 按属地加载目录:优先 SvcLocal;无本地目录(新建制州市)回退 SvcNational 全量。 */
|
||
async listLocal(locCode: string) {
|
||
const locals = await this.prisma.client.svcLocal.findMany({ where: { locCode } });
|
||
if (locals.length > 0) return locals;
|
||
return this.prisma.client.svcNational.findMany();
|
||
}
|
||
|
||
/** 当前租户的机构项目库(SvcOrg)。 */
|
||
listOrg() {
|
||
const ctx = getRequestContext()!;
|
||
return this.prisma.client.svcOrg.findMany({ where: { tenantCode: ctx.tenantCode } });
|
||
}
|
||
|
||
/** 勾选+配价:只允许改 isEnabled/price,夹带 srcType/srcId 等业务字段一律拒绝。 */
|
||
async config(dto: ConfigSvcOrgDto) {
|
||
this.assertOnlyMutableFields(dto);
|
||
const ctx = getRequestContext()!;
|
||
const existing = await this.prisma.client.svcOrg.findFirst({
|
||
where: { id: dto.id, tenantCode: ctx.tenantCode },
|
||
});
|
||
if (!existing) {
|
||
throw new BusinessException(ErrorCodes.SVC_ORG_NOT_FOUND, '机构项目不存在', HttpStatus.NOT_FOUND);
|
||
}
|
||
const data: Record<string, unknown> = {};
|
||
if (dto.isEnabled !== undefined) data.isEnabled = dto.isEnabled;
|
||
if (dto.price !== undefined) data.price = dto.price;
|
||
return this.prisma.client.svcOrg.update({ where: { id: dto.id }, data });
|
||
}
|
||
|
||
/** 国家新增项(昆明目录缺、国家目录有的 5 项,机构勾选界面需显式提示)。 */
|
||
newNational() {
|
||
// TODO(C1): SvcNational 无 is_new_national 列,「国家新增项」= 昆明 L2 缺而国家 L1 有的
|
||
// 差集(L3 映射 national_only)。C1 需按 L3 映射补齐并标记 is_new_national,此处暂返回全量。
|
||
return this.prisma.client.svcNational.findMany();
|
||
}
|
||
|
||
/** 白名单校验:config 只允许 id/isEnabled/price,夹带其他业务字段抛 SVC_LOC_IMMUTABLE。 */
|
||
private assertOnlyMutableFields(dto: ConfigSvcOrgDto) {
|
||
const allowed = new Set(['id', 'isEnabled', 'price']);
|
||
const extra = Object.keys(dto).filter((key) => !allowed.has(key));
|
||
if (extra.length > 0) {
|
||
throw new BusinessException(ErrorCodes.SVC_LOC_IMMUTABLE, '仅可调整启用状态与价格', HttpStatus.BAD_REQUEST);
|
||
}
|
||
}
|
||
}
|