fix(c3): 扑空转 not_met 前 requireOrder 校验(44003)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
liuyun 2026-10-15 00:33:56 +08:00
parent 5bfd0f4748
commit 2e4c916949
2 changed files with 42 additions and 4 deletions

View File

@ -1,6 +1,7 @@
import { describe, expect, it, vi } from 'vitest'; import { describe, expect, it, vi } from 'vitest';
import { AbnormalService } from './abnormal.service.js'; import { AbnormalService } from './abnormal.service.js';
import type { PrismaService } from '../prisma/prisma.service.js'; import type { PrismaService } from '../prisma/prisma.service.js';
import { ErrorCodes } from '../common/error-codes.js';
import { runWithRequestContext } from '../common/context/request-context.js'; import { runWithRequestContext } from '../common/context/request-context.js';
import type { RequestContext } from '../common/context/request-context.js'; import type { RequestContext } from '../common/context/request-context.js';
@ -20,12 +21,14 @@ function makeService(
recordAbnormalFindMany?: ReturnType<typeof vi.fn>; recordAbnormalFindMany?: ReturnType<typeof vi.fn>;
recordAbnormalCount?: ReturnType<typeof vi.fn>; recordAbnormalCount?: ReturnType<typeof vi.fn>;
orderUpdate?: ReturnType<typeof vi.fn>; orderUpdate?: ReturnType<typeof vi.fn>;
orderFindUnique?: ReturnType<typeof vi.fn>;
} = {}, } = {},
) { ) {
const recordAbnormalCreate = overrides.recordAbnormalCreate ?? vi.fn().mockResolvedValue({ id: 'a1' }); const recordAbnormalCreate = overrides.recordAbnormalCreate ?? vi.fn().mockResolvedValue({ id: 'a1' });
const recordAbnormalFindMany = overrides.recordAbnormalFindMany ?? vi.fn().mockResolvedValue([]); const recordAbnormalFindMany = overrides.recordAbnormalFindMany ?? vi.fn().mockResolvedValue([]);
const recordAbnormalCount = overrides.recordAbnormalCount ?? vi.fn().mockResolvedValue(0); const recordAbnormalCount = overrides.recordAbnormalCount ?? vi.fn().mockResolvedValue(0);
const orderUpdate = overrides.orderUpdate ?? vi.fn().mockResolvedValue({ id: 'o1', orderStatus: 'not_met' }); const orderUpdate = overrides.orderUpdate ?? vi.fn().mockResolvedValue({ id: 'o1', orderStatus: 'not_met' });
const orderFindUnique = overrides.orderFindUnique ?? vi.fn().mockResolvedValue({ id: 'o1', orderStatus: 'assigned' });
const prisma = { const prisma = {
client: { client: {
@ -34,11 +37,11 @@ function makeService(
findMany: recordAbnormalFindMany, findMany: recordAbnormalFindMany,
count: recordAbnormalCount, count: recordAbnormalCount,
}, },
order: { update: orderUpdate }, order: { update: orderUpdate, findUnique: orderFindUnique },
}, },
}; };
const service = new AbnormalService(prisma as unknown as PrismaService); const service = new AbnormalService(prisma as unknown as PrismaService);
return { service, recordAbnormalCreate, recordAbnormalFindMany, recordAbnormalCount, orderUpdate }; return { service, recordAbnormalCreate, recordAbnormalFindMany, recordAbnormalCount, orderUpdate, orderFindUnique };
} }
describe('AbnormalService', () => { describe('AbnormalService', () => {
@ -149,6 +152,29 @@ describe('AbnormalService', () => {
expect(recordAbnormalCreate).not.toHaveBeenCalled(); expect(recordAbnormalCreate).not.toHaveBeenCalled();
}); });
it('report 扑空 order 不存在 → ORDER_NOT_FOUND(44003),不写工单/异常', async () => {
const orderFindUnique = vi.fn().mockResolvedValue(null);
const { service, recordAbnormalCreate, orderUpdate } = makeService({ orderFindUnique });
await expect(
runWithRequestContext(ctx, () =>
service.report({
abnormalType: 'not_met',
elderId: 'e1',
orderId: 'o-missing',
abnormalDesc: '老人不在家',
visitTime: '2026-10-15T09:30:00',
visitLat: 25.01,
visitLng: 102.7,
}),
),
).rejects.toMatchObject({ response: { code: ErrorCodes.ORDER_NOT_FOUND } });
expect(orderFindUnique).toHaveBeenCalledWith({ where: { id: 'o-missing' } });
expect(orderUpdate).not.toHaveBeenCalled();
expect(recordAbnormalCreate).not.toHaveBeenCalled();
});
it('list 按 abnormal_type/notify_status/handle_status/老人 筛选', async () => { it('list 按 abnormal_type/notify_status/handle_status/老人 筛选', async () => {
const recordAbnormalFindMany = vi.fn().mockResolvedValue([{ id: 'a1' }]); const recordAbnormalFindMany = vi.fn().mockResolvedValue([{ id: 'a1' }]);
const recordAbnormalCount = vi.fn().mockResolvedValue(1); const recordAbnormalCount = vi.fn().mockResolvedValue(1);

View File

@ -1,6 +1,8 @@
import { BadRequestException, Injectable } from '@nestjs/common'; import { BadRequestException, HttpStatus, Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service.js'; import { PrismaService } from '../prisma/prisma.service.js';
import { getRequestContext } from '../common/context/request-context.js'; import { getRequestContext } from '../common/context/request-context.js';
import { BusinessException } from '../common/business-exception.js';
import { ErrorCodes } from '../common/error-codes.js';
import { ListAbnormalQuery, NOT_MET, ReportAbnormalDto } from './dto.js'; import { ListAbnormalQuery, NOT_MET, ReportAbnormalDto } from './dto.js';
/** ISO datetime 字符串 → Date。业务时区为北京时间(UTC+8),与 order.service 口径一致。 */ /** ISO datetime 字符串 → Date。业务时区为北京时间(UTC+8),与 order.service 口径一致。 */
@ -35,7 +37,8 @@ export class AbnormalService {
if (dto.abnormalType === NOT_MET) { if (dto.abnormalType === NOT_MET) {
this.assertNotMetEvidence(dto); this.assertNotMetEvidence(dto);
// 扑空:工单转 not_met(原因 + 到访时间)。 // 扑空:工单须存在,再转 not_met(原因 + 到访时间)。
await this.requireOrder(dto.orderId!);
await this.prisma.client.order.update({ await this.prisma.client.order.update({
where: { id: dto.orderId! }, where: { id: dto.orderId! },
data: { data: {
@ -93,4 +96,13 @@ export class AbnormalService {
throw new BadRequestException('扑空须填写到访时间、定位、原因与关联工单(3003)'); throw new BadRequestException('扑空须填写到访时间、定位、原因与关联工单(3003)');
} }
} }
/** 工单须存在(扑空转 not_met 前校验,44003),循 order.service 的 requireOrder。 */
private async requireOrder(id: string): Promise<Record<string, unknown>> {
const order = await this.prisma.client.order.findUnique({ where: { id } });
if (!order) {
throw new BusinessException(ErrorCodes.ORDER_NOT_FOUND, '工单不存在', HttpStatus.NOT_FOUND);
}
return order;
}
} }