docs(openspec): C3 履约留痕 change 提案
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
45aaeb9d43
commit
9f789fccb5
2
openspec/changes/svc-org-c3-record/.openspec.yaml
Normal file
2
openspec/changes/svc-org-c3-record/.openspec.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-10-15
|
||||
42
openspec/changes/svc-org-c3-record/design.md
Normal file
42
openspec/changes/svc-org-c3-record/design.md
Normal file
@ -0,0 +1,42 @@
|
||||
## Context
|
||||
|
||||
C2 已有 order 八态、plan/plan_item、staff、MinIO StorageService。C3 新增履约留痕域,纯新增、不侵入脚手架。设计背景见 `docs/superpowers/specs/2026-10-15-服务机构端-C3履约留痕-design.md`。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:** ho_record/ho_record_item/ho_record_photo/ho_record_abnormal 四表 + 录入 API + 垫付台账与结转 + admin 工单留痕/异常列表 + settlement/report 预留空表。
|
||||
|
||||
**Non-Goals:** 质控放行/退回(qc 表 + 4001,C4)、补正(C4)、离线队列+自动补传(APP 期)、家属端、结算对账(二期)。
|
||||
|
||||
## Decisions
|
||||
|
||||
1. **表命名**:`ho_record`/`ho_record_item`/`ho_record_photo`/`ho_record_abnormal`(前缀 ho,禁用 checkin/clockin/punch);`settlement`/`report` 无前缀预留。
|
||||
2. **模块划分**:`src/record/`(record + item + photo + 签到签退离线 + 垫付结转)+ `src/abnormal/`。
|
||||
3. **record 生命周期**:serving(签到)→ submitted(签退);returned/passed 留 C4。
|
||||
4. **服务器取时**:checkin_time/checkout_time/duration_min 系统取,接口不接受。
|
||||
5. **is_prepaid**:record 创建时 elder.treat_status = waiting_period → is_prepaid=1、billable=0。
|
||||
6. **结转**:复制垫付 record → 正式 record(is_prepaid=0 + source_prepaid_of),原记录保留只读、不改原始时间戳。
|
||||
7. **扑空**:工单转 not_met + 写 record_abnormal(到访/定位/原因),不产生 record。
|
||||
8. **exec_result**:done/undo_not_done/undo_refused。
|
||||
9. **照片**:MinIO 复用,≤500KB,visible_to_family 恒 0,keep_until ≥7 天。
|
||||
10. **client_md5 去重**:离线补传幂等。
|
||||
11. **主键**:UUID v7 VarChar(36)(循代码库,PRD 的 varchar(32) 归一)。
|
||||
|
||||
## 数据模型
|
||||
|
||||
通用字段 + UUID v7 + tenantCode + 软删(仅 record;子表循 ElderStatusLog/PlanItem 先例:带 tenant_code、无 deleted_at、不入 SOFT_DELETE)。6 表字段见 docs/superpowers 设计文档;record_abnormal 增加 order_id(扑空指向工单,PRD 最小字段未含)。
|
||||
|
||||
## 接口
|
||||
|
||||
record:checkin / item/save / photo/upload / checkout / offline/sync / detail / list / prepaid-list / prepaid/carryover。abnormal:report / list。权限码 record.*/abnormal.*;错误码域 45;规则号 3001/3002/3003/3004 写 message。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- 离线幂等 → client_md5 唯一约束 + upsert。
|
||||
- 扑空 vs 逾期未做 → 有到访证据 vs 无记录。
|
||||
- 结转复制 vs 改标记 → 复制(两套并存合规)。
|
||||
- 必拍清单 source of truth → order.item_list 快照落 record_item.photo_required。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
新增 6 表(utf8mb4_general_ci 手改)。迁移链已损坏:用 `migrate diff --from-schema-datasource` + 手工迁移目录 + `migrate deploy`。
|
||||
32
openspec/changes/svc-org-c3-record/proposal.md
Normal file
32
openspec/changes/svc-org-c3-record/proposal.md
Normal file
@ -0,0 +1,32 @@
|
||||
## Why
|
||||
|
||||
C2 已落地派单工单(八态 order)+ 老人个人项目库。C3 交付 PRD 第三块「履约留痕」:护理员签到/签退/项目执行/拍照/异常上报 + 垫付台账与结转(is_prepaid)+ admin 工单留痕/异常列表。为 C4 质控闸门提供「已提交留痕」这一 L4/L5 前提。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 新增 `ho_record`/`ho_record_item`/`ho_record_photo`/`ho_record_abnormal` 四张表 + `settlement`/`report` 二期预留空表。
|
||||
- 履约留痕:签到(服务器取时)、项目执行(exec_result 枚举)、拍照(≤500KB)、签退(必拍 3001 硬拒 + 拒拍出路)、离线补传(client_md5 去重)。
|
||||
- 异常上报:`abnormal_type` 必选、其余可选;扑空(未遇)专门形态(工单转 not_met + 到访证据,不产生 record)。
|
||||
- 垫付台账与结转:`waiting_period` 留痕 `is_prepaid=1`、`billable=0`;档案页「结转」按钮勾选垫付记录生成正式记录(原记录保留只读、不改原始时间戳)。
|
||||
- L3 硬锁落地:留痕必须关联有效工单;L1 例外:`waiting_period` 走垫付不误拦。
|
||||
- 前端「工单留痕」页 + 「异常列表」页 + 服务对象页结转入口。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `backend/record`: 履约留痕——签到/签退/项目/照片/离线/列表/垫付台账/结转。
|
||||
- `backend/abnormal`: 异常上报/列表 + 扑空专门形态。
|
||||
- `frontend/ad/record`: admin 工单留痕页 + 异常列表页 + 服务对象页结转入口。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `shared/data-model`: 新增 `ho_record`/`ho_record_item`/`ho_record_photo`/`ho_record_abnormal` + `settlement`/`report` 六实体。
|
||||
- `shared/api-contract`: 新增 record/abnormal/prepaid 接口。
|
||||
|
||||
## Impact
|
||||
|
||||
- 后端 ec-nest:新增 record/abnormal 模块(纯新增)。
|
||||
- 前端 ec-admin:工单留痕页 + 异常列表页 + 服务对象页扩展。
|
||||
- 数据:新增 6 张表(迁移,utf8mb4_general_ci)。
|
||||
- 依赖:C2 的 order 八态、plan/plan_item、staff(护理员)、MinIO StorageService。
|
||||
@ -0,0 +1,26 @@
|
||||
## Purpose
|
||||
|
||||
定义服务机构端异常上报域的后端行为——异常上报(9 宫格)与扑空(未遇)专门形态。
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 异常上报
|
||||
`POST /abnormal/report` MUST 仅 abnormal_type 必选,其余(desc/voice/photo/关联)可选;关联 record 或 order(扑空)。
|
||||
|
||||
#### Scenario: 分类必选其余可选
|
||||
- **WHEN** 上报异常只选分类、其余留空
|
||||
- **THEN** 允许提交(宁要一条没人名的异常也不要漏报)
|
||||
|
||||
### Requirement: 扑空专门形态
|
||||
扑空(未遇)MUST:工单转 not_met(not_met_reason/not_met_at)+ 写 record_abnormal(到访时间/定位/原因必留);不产生 record、不计费。与「逾期未做」(到点无记录)严格区分。
|
||||
|
||||
#### Scenario: 扑空留证据
|
||||
- **WHEN** 护理员登记未遇(老人不在/拒绝/改期)
|
||||
- **THEN** 到访时间+定位+原因必留,工单转 not_met,不产生服务记录
|
||||
|
||||
### Requirement: 异常列表
|
||||
`GET /abnormal/list` MUST 返回异常列表(后台 B4),按 abnormal_type/notify_status/handle_status/老人 筛选。
|
||||
|
||||
#### Scenario: 质控查看异常
|
||||
- **WHEN** 质控员打开异常列表
|
||||
- **THEN** 看到异常分类、状态、关联老人
|
||||
@ -0,0 +1,62 @@
|
||||
## Purpose
|
||||
|
||||
定义服务机构端履约留痕域的后端行为——签到/签退/项目执行/照片/离线补传/垫付台账与结转。
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 签到
|
||||
`POST /record/checkin` MUST 校验 L3(order 存在、order_status ∈ {assigned, serving}、elder/assignee 匹配);checkin_time 服务器取时(不接受客户端传入);定位失败 3002 不阻塞(locate_status=failed/denied);建 record(serving)并返回 record_id。
|
||||
|
||||
#### Scenario: 服务器取时
|
||||
- **WHEN** 签到
|
||||
- **THEN** checkin_time 由服务端生成,客户端传入被忽略
|
||||
|
||||
#### Scenario: 定位失败不阻塞
|
||||
- **WHEN** 签到但定位失败
|
||||
- **THEN** 标记 locate_status=failed,照常提交(3002)
|
||||
|
||||
### Requirement: 签退提交
|
||||
`POST /record/checkout` MUST 校验必拍未拍(3001 硬拒 + 拒拍出路 is_refused_photo=1);checkout_time/duration_min 服务器取时;record_status 转 submitted;waiting_period 老人 is_prepaid=1、billable=0(3004 仅标记)。
|
||||
|
||||
#### Scenario: 必拍未拍硬拒
|
||||
- **WHEN** 必拍项未拍照且未走拒拍出路
|
||||
- **THEN** 返回 3001 拒绝签退
|
||||
|
||||
#### Scenario: 垫付标记
|
||||
- **WHEN** 等待期老人签退
|
||||
- **THEN** record.is_prepaid=1、billable=0,不计费
|
||||
|
||||
### Requirement: 项目执行
|
||||
`POST /record/item/save` MUST exec_result ∈ {done, undo_not_done, undo_refused};svc_id 落本机构 L4;photo_required 快照自 order.item_list。
|
||||
|
||||
#### Scenario: 措辞纪律
|
||||
- **WHEN** 项目未执行
|
||||
- **THEN** exec_result 用 undo_not_done(非 not_done)
|
||||
|
||||
### Requirement: 照片上传
|
||||
`POST /record/photo/upload` MUST 校验文件 ≤500KB;visible_to_family=0;keep_until 补传后 ≥7 天。
|
||||
|
||||
#### Scenario: 超限拒绝
|
||||
- **WHEN** 上传超过 500KB 的照片
|
||||
- **THEN** 拒绝并提示压缩
|
||||
|
||||
### Requirement: 离线补传
|
||||
`POST /record/offline/sync` MUST 按 client_md5 幂等去重(重复提交返回已存在,静默不弹窗)。
|
||||
|
||||
#### Scenario: 去重
|
||||
- **WHEN** 同 client_md5 重复补传
|
||||
- **THEN** 幂等返回,不重复落库
|
||||
|
||||
### Requirement: 垫付台账与结转
|
||||
`waiting_period` 老人留痕 MUST `is_prepaid=1`、`billable=0`;`POST /record/prepaid/carryover` MUST 勾选若干 is_prepaid=1 记录 → 生成对应正式记录(复制、is_prepaid=0、source_prepaid_of=原 id),原记录保留只读、不改原始时间戳(签到时间/定位/照片仍是原始值)。
|
||||
|
||||
#### Scenario: 结转不改原始时间戳
|
||||
- **WHEN** 结转垫付记录
|
||||
- **THEN** 新记录签到时间/定位/照片仍为原始值,仅标记来源
|
||||
|
||||
### Requirement: 留痕列表与详情
|
||||
`GET /record/list`(admin B3,按 elder/assignee/日期/状态筛选)与 `GET /record/detail` MUST 返回留痕列表/详情(含项目、照片、异常、定位、垫付标记)。
|
||||
|
||||
#### Scenario: 质控查看留痕
|
||||
- **WHEN** 质控员查询留痕列表与详情
|
||||
- **THEN** 返回时间/项目/照片/异常/定位/垫付标记
|
||||
@ -0,0 +1,26 @@
|
||||
## Purpose
|
||||
|
||||
定义服务机构端「工单留痕」「异常列表」页面与服务对象页结转入口的交互。
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 工单留痕页
|
||||
admin「工单留痕」页 MUST 提供记录列表 + 详情(签到/签退时间、时长、项目 exec_result、照片、异常、定位、垫付标记);质控员/院长可见,护理员看自己。
|
||||
|
||||
#### Scenario: 查看留痕
|
||||
- **WHEN** 质控员打开工单留痕
|
||||
- **THEN** 看到记录列表与详情(时间/项目/照片/异常/定位)
|
||||
|
||||
### Requirement: 异常列表页
|
||||
admin「异常列表」页 MUST 展示异常(分类、状态、关联老人、语音/照片入口)。
|
||||
|
||||
#### Scenario: 查看异常
|
||||
- **WHEN** 质控员打开异常列表
|
||||
- **THEN** 看到异常分类与状态
|
||||
|
||||
### Requirement: 结转入口
|
||||
服务对象页 MUST 在待遇转「正常享受」后提供「结转垫付期服务」按钮:勾选 is_prepaid=1 记录 → 生成正式记录,原记录保留只读。
|
||||
|
||||
#### Scenario: 结转垫付
|
||||
- **WHEN** 待遇已生效,点「结转垫付期服务」
|
||||
- **THEN** 勾选垫付记录生成正式记录,原记录保留只读
|
||||
@ -0,0 +1,26 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 履约留痕接口
|
||||
`POST /record/checkin`、`POST /record/item/save`、`POST /record/photo/upload`、`POST /record/checkout`、`POST /record/offline/sync`、`GET /record/detail`、`GET /record/list`、`GET /record/prepaid-list`、`POST /record/prepaid/carryover` MUST 存在,遵循统一响应。
|
||||
|
||||
#### Scenario: 签退必拍未拍硬拒
|
||||
- **WHEN** 必拍项未拍照且未走拒拍出路时签退
|
||||
- **THEN** 返回 3001,拒绝签退
|
||||
|
||||
#### Scenario: 签到服务器取时
|
||||
- **WHEN** 签到
|
||||
- **THEN** checkin_time 由服务端生成,客户端传入被忽略
|
||||
|
||||
### Requirement: 异常接口
|
||||
`POST /abnormal/report`、`GET /abnormal/list` MUST 存在,遵循统一响应。
|
||||
|
||||
#### Scenario: 扑空
|
||||
- **WHEN** 护理员登记未遇(扑空)
|
||||
- **THEN** 工单转 not_met、到访时间/定位/原因必留、不产生 record
|
||||
|
||||
### Requirement: 垫付结转接口
|
||||
`GET /record/prepaid-list` MUST 返回老人垫付台账(is_prepaid=1);`POST /record/prepaid/carryover` MUST 勾选垫付记录生成正式记录,原记录保留只读。
|
||||
|
||||
#### Scenario: 结转不改原始时间戳
|
||||
- **WHEN** 结转垫付记录
|
||||
- **THEN** 新记录签到时间/定位/照片仍为原始值,仅标记来源=垫付台账
|
||||
@ -0,0 +1,36 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 履约留痕实体 ho_record
|
||||
`ho_record` 表 MUST 存在(order_id、elder_id、assignee_id、checkin_time、checkout_time、duration_min、checkin_lat/lng、locate_status、record_status、voice_file、offline_flag、client_md5、submit_time、actual_duration_tier、biz_line、is_prepaid、billable、source_prepaid_of)。`record_status` MUST ∈ {serving, submitted}(returned/passed 留 C4)。签到签退时间与时长由系统生成,不接受客户端传入。
|
||||
|
||||
#### Scenario: 留痕关联有效工单
|
||||
- **WHEN** 创建一条留痕
|
||||
- **THEN** 必须带 order_id 且工单有效(L3 硬锁)
|
||||
|
||||
### Requirement: 留痕项目明细 ho_record_item
|
||||
`ho_record_item` 表 MUST 存在(record_id、svc_id 引 L4、svc_name_snapshot、exec_result、undo_reason、extra_flag、photo_required、is_refused_photo、refuse_reason)。`exec_result` MUST ∈ {done, undo_not_done, undo_refused}。
|
||||
|
||||
#### Scenario: 项目只能从 L4 选
|
||||
- **WHEN** 保存留痕项目
|
||||
- **THEN** svc_id 必须在本机构 svc_org 内,否则拒绝
|
||||
|
||||
### Requirement: 留痕照片 ho_record_photo
|
||||
`ho_record_photo` 表 MUST 存在(record_id、svc_id、file_path、file_size、visible_to_family、upload_batch、keep_until)。`visible_to_family` 本期恒 0;文件压缩后 ≤500KB。
|
||||
|
||||
#### Scenario: 家属端不可见
|
||||
- **WHEN** 上传留痕照片
|
||||
- **THEN** visible_to_family = 0
|
||||
|
||||
### Requirement: 异常上报 ho_record_abnormal
|
||||
`ho_record_abnormal` 表 MUST 存在(record_id 可空、order_id 可空、elder_id、reporter_id、abnormal_type、abnormal_desc、voice_file、has_photo、notify_status、handle_status、handle_result、link_plan_change)。`abnormal_type` MUST 必填,其余可选。
|
||||
|
||||
#### Scenario: 扑空无留痕
|
||||
- **WHEN** 登记未遇(扑空)
|
||||
- **THEN** record_id 为空、order_id 指向工单、abnormal_type 为扑空分类
|
||||
|
||||
### Requirement: 二期预留表 settlement / report
|
||||
`settlement`/`report` 表 MUST 建空表不写业务数据(settlement 含 amount_fund/amount_self_pay 个人自付拆分字段)。
|
||||
|
||||
#### Scenario: 预留表空建
|
||||
- **WHEN** 迁移完成后
|
||||
- **THEN** settlement/report 表存在且无业务数据
|
||||
39
openspec/changes/svc-org-c3-record/tasks.md
Normal file
39
openspec/changes/svc-org-c3-record/tasks.md
Normal file
@ -0,0 +1,39 @@
|
||||
# Tasks — svc-org-c3-record
|
||||
|
||||
## 1. 数据层
|
||||
|
||||
- [ ] 1.1 `schema.prisma` 新增 `Record`/`RecordItem`/`RecordPhoto`/`RecordAbnormal`/`Settlement`/`Report` 模型(表名 ho_record/ho_record_item/ho_record_photo/ho_record_abnormal/settlement/report)。
|
||||
- [ ] 1.2 `prisma.service.ts`:`Record` 加入 SOFT_DELETE;子表(item/photo/abnormal)不入。
|
||||
- [ ] 1.3 迁移 `c3_record`(utf8mb4_general_ci,用 migrate diff + deploy)+ `prisma generate`。
|
||||
|
||||
## 2. 权限码 + 错误码 + APIS
|
||||
|
||||
- [ ] 2.1 `permissions.ts` 新增 `record.*`/`abnormal.*`(checkin/itemSave/photoUpload/checkout/offlineSync/detail/list/prepaidList/prepaidCarryover/report/list)。
|
||||
- [ ] 2.2 `error-codes.ts` 补域 45:RECORD_NOT_FOUND、RECORD_ORDER_INVALID、RECORD_PHOTO_REQUIRED(3001)、RECORD_STATUS_INVALID、ABNORMAL_NOT_FOUND 等。
|
||||
- [ ] 2.3 `tenant-template.ts` APIS 登记 record/abnormal 接口资源。
|
||||
|
||||
## 3. record 模块(履约留痕 + 垫付结转)
|
||||
|
||||
- [ ] 3.1 `src/record/` 模块:checkin(L3 + 服务器取时 + 3002)、item/save(exec_result + L4)、photo/upload(≤500KB)、checkout(3001 必拍 + 服务器时长 + is_prepaid)、offline/sync(client_md5 去重)、detail、list。
|
||||
- [ ] 3.2 垫付台账与结转:prepaid-list、prepaid/carryover(复制 + source_prepaid_of + 不改原始时间戳)。
|
||||
- [ ] 3.3 TDD:签到 L3/取时/定位、签退 3001/垫付、exec_result 枚举、结转不改时间戳、离线去重;`pnpm test` + `tsc --noEmit`。
|
||||
|
||||
## 4. abnormal 模块(异常 + 扑空)
|
||||
|
||||
- [ ] 4.1 `src/abnormal/` 模块:report(abnormal_type 必选)、list、扑空(工单转 not_met + record_abnormal,不产生 record)。
|
||||
- [ ] 4.2 TDD:分类必选其余可选、扑空转 not_met + 留证据、列表筛选;`pnpm test` + `tsc --noEmit`。
|
||||
|
||||
## 5. 前端
|
||||
|
||||
- [ ] 5.1 `src/api/record.ts`、`src/api/abnormal.ts`。
|
||||
- [ ] 5.2 工单留痕页(`ltc/record`):列表 + 详情(时间/项目/照片/异常/定位/垫付标记)。
|
||||
- [ ] 5.3 异常列表页(`ltc/qc` 或独立):分类/状态/关联老人。
|
||||
- [ ] 5.4 服务对象页:垫付台账 + 结转入口。
|
||||
- [ ] 5.5 `pnpm type-check` 通过。
|
||||
|
||||
## 6. 验证
|
||||
|
||||
- [ ] 6.1 冒烟:签到 → 项目 → 照片 → 签退(3001 必拍)→ 详情;waiting_period 老人垫付 is_prepaid=1。
|
||||
- [ ] 6.2 扑空:工单转 not_met + 到访证据,不产生 record。
|
||||
- [ ] 6.3 结转:勾选垫付记录生成正式记录,原始时间戳不变。
|
||||
- [ ] 6.4 离线补传 client_md5 去重幂等;审计落库。
|
||||
Loading…
x
Reference in New Issue
Block a user