fix(c1): elder 日期用 UTC 零点,修复 @db.Date 提前一天的 off-by-one

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
liuyun 2026-10-14 21:27:26 +08:00
parent a949acbc3b
commit 64c3efd931
2 changed files with 26 additions and 16 deletions

View File

@ -1,5 +1,5 @@
import { describe, expect, it, vi } from 'vitest';
import { ElderService, maskIdCard } from './elder.service.js';
import { ElderService, maskIdCard, toDateOnly } from './elder.service.js';
import type { PrismaService } from '../prisma/prisma.service.js';
import type { StorageService } from '../storage/storage.service.js';
import type { AuditLogsService } from '../audit-logs/audit-logs.service.js';
@ -109,6 +109,18 @@ describe('maskIdCard', () => {
});
});
describe('toDateOnly', () => {
it('date-only 字符串 → UTC 零点 Date(不提前一天)', () => {
expect(toDateOnly('1990-01-01')?.toISOString()).toBe('1990-01-01T00:00:00.000Z');
expect(toDateOnly('2025-01-01')?.toISOString()).toBe('2025-01-01T00:00:00.000Z');
});
it('空值返回 undefined', () => {
expect(toDateOnly(undefined)).toBeUndefined();
expect(toDateOnly('')).toBeUndefined();
});
});
describe('ElderService', () => {
it('create 有等级无评估结论副本 → 抛 ELDER_LEVEL_REQUIRES_EVIDENCE', async () => {
const { service, elderCreate } = makeService();
@ -144,7 +156,7 @@ describe('ElderService', () => {
});
});
it('合法建档(含日期字段)成功创建,日期字段转为 Date', async () => {
it('合法建档(含日期字段)成功创建,日期字段转为 UTC 零点 Date 且值正确', async () => {
const { service, elderCreate } = makeService();
await runWithRequestContext(ctx, () =>
@ -159,17 +171,15 @@ describe('ElderService', () => {
}),
);
expect(elderCreate).toHaveBeenCalledWith({
data: expect.objectContaining({
birthDate: expect.any(Date),
benefitStart: expect.any(Date),
benefitEnd: expect.any(Date),
assessDate: expect.any(Date),
insurVerifyDate: expect.any(Date),
hospStart: expect.any(Date),
hospEnd: expect.any(Date),
}),
});
const data = elderCreate.mock.calls[0][0].data as Record<string, unknown>;
expect(data.birthDate).toBeInstanceOf(Date);
expect((data.birthDate as Date).toISOString()).toBe('1990-01-01T00:00:00.000Z');
expect((data.benefitStart as Date).toISOString()).toBe('2025-01-01T00:00:00.000Z');
expect((data.benefitEnd as Date).toISOString()).toBe('2025-12-31T00:00:00.000Z');
expect((data.assessDate as Date).toISOString()).toBe('2025-01-15T00:00:00.000Z');
expect((data.insurVerifyDate as Date).toISOString()).toBe('2025-01-10T00:00:00.000Z');
expect((data.hospStart as Date).toISOString()).toBe('2025-02-01T00:00:00.000Z');
expect((data.hospEnd as Date).toISOString()).toBe('2025-02-05T00:00:00.000Z');
});
it('同机构重复 id_card_no → 抛 ELDER_ID_CARD_EXISTS', async () => {

View File

@ -52,10 +52,10 @@ function resolveInitialStatus(benefitStart?: string, benefitEnd?: string): strin
return 'waiting_period';
}
/** YYYY-MM-DD → 本地零点 Date(供 Prisma DateTime @db.Date 列写入);空值返回 undefined。 */
function toDateOnly(v?: string): Date | undefined {
/** YYYY-MM-DD → UTC 零点 Date(Prisma @db.Date 按 UTC instant 取日期,须用 UTC 零点避免时区回退一天);空值返回 undefined。 */
export function toDateOnly(v?: string): Date | undefined {
if (!v) return undefined;
return new Date(`${v}T00:00:00`);
return new Date(`${v}T00:00:00Z`);
}
/**