fix(c5): agency.update 改 upsert 补齐 provisioning

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
liuyun 2026-10-15 07:57:36 +08:00
parent b09015151f
commit 5cba51abb8
2 changed files with 89 additions and 5 deletions

View File

@ -55,6 +55,66 @@ describe('AgencyService', () => {
data: { qcThreshold: 48, dockingTier: 'A', newNationalNotify: false },
});
});
it('无 agency 行时 PATCH 自动创建(最小默认值 + dto 字段,locCode 置空)', async () => {
const findUnique = vi.fn().mockResolvedValue(null);
const create = vi.fn().mockResolvedValue({ code: 'T001', orgName: '新机构' });
const update = vi.fn();
const prisma = { client: { agency: { findUnique, create, update } } };
const service = new AgencyService(prisma as unknown as PrismaService);
await runWithRequestContext(ctx, () =>
service.update({ orgName: '新机构', qcThreshold: 48, dockingTier: 'A', newNationalNotify: false }),
);
expect(create).toHaveBeenCalledWith({
data: {
code: 'T001',
orgName: '新机构',
orgType: '',
creditCode: '',
locCode: '',
locName: '',
svcSource: '',
address: '',
contactName: '',
contactPhone: '',
qcThreshold: 48,
dockingTier: 'A',
newNationalNotify: false,
},
});
expect(update).not.toHaveBeenCalled();
});
it('无 agency 行且 dto 为空时,create 落最小默认值(24/B/true)', async () => {
const findUnique = vi.fn().mockResolvedValue(null);
const create = vi.fn().mockResolvedValue({ code: 'T001' });
const update = vi.fn();
const prisma = { client: { agency: { findUnique, create, update } } };
const service = new AgencyService(prisma as unknown as PrismaService);
await runWithRequestContext(ctx, () => service.update({}));
expect(create).toHaveBeenCalledWith({
data: {
code: 'T001',
orgName: '',
orgType: '',
creditCode: '',
locCode: '',
locName: '',
svcSource: '',
address: '',
contactName: '',
contactPhone: '',
qcThreshold: 24,
dockingTier: 'B',
newNationalNotify: true,
},
});
expect(update).not.toHaveBeenCalled();
});
});
describe('UpdateAgencyDto.dockingTier', () => {

View File

@ -1,10 +1,27 @@
import { HttpStatus, Injectable } from '@nestjs/common';
import { 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 { UpdateAgencyDto } from './dto.js';
/** 无 agency 行时(R5:无 provisioning 路径)创建行的最小默认值。 */
function agencyDefaults(code: string): Record<string, unknown> {
return {
code,
orgName: '',
orgType: '',
creditCode: '',
locCode: '',
locName: '',
svcSource: '',
address: '',
contactName: '',
contactPhone: '',
qcThreshold: 24,
dockingTier: 'B',
newNationalNotify: true,
};
}
/**
* 机构(=租户)信息读写。Agency 是 TENANT_FREE 模型,主键 code = 当前租户 code,
* 因此读写一律按 `ctx.tenantCode` 定位,而非 BaseCrudService 的 `findById(id)`。
@ -19,13 +36,20 @@ export class AgencyService {
return this.prisma.client.agency.findUnique({ where: { code: ctx.tenantCode } });
}
/** 更新当前机构信息;写操作同样按 code = ctx.tenantCode 定位,禁止按 id。 */
/**
* 更新当前机构信息(upsert);写操作同样按 code = ctx.tenantCode 定位,禁止按 id。
* R5:agency 无 provisioning 路径(空表 + 无 create),故无行时先建最小默认值行再落 dto 字段。
* 参保地 locCode/locName 只读,不随 PATCH 更新(建行时置空串,dto 不含这两字段)。
*/
async update(dto: UpdateAgencyDto) {
const ctx = getRequestContext()!;
const existing = await this.getCurrent();
if (!existing) {
throw new BusinessException(ErrorCodes.ORG_NOT_FOUND, '机构不存在', HttpStatus.NOT_FOUND);
return this.prisma.client.agency.create({
data: { ...agencyDefaults(ctx.tenantCode), ...dto } as any,
});
}
return this.prisma.client.agency.update({ where: { code: ctx.tenantCode }, data: dto });
}
}