77 KiB
居家上门服务系统 — 可执行实现方案(补齐可开工版)
生成日期:2026-05-15 补齐版本:V1.1 生产可交付补强版 基于:V2.0 工程落地增强版 + V2.1 行业经验增强版 + V2.2 边界限制增强版 项目状态:全新仓库,从零搭建
本版结论:可以进入实现,但必须按本补齐版执行。本文已补齐幂等表、任务执行日志、Outbox 并发安全、delivery 最小闭环、支付退款、异常监管、完整 OpenAPI 契约、E2E/并发/失败注入测试、灰度回滚与上线验收门槛。
0. 补齐版执行口径
本补齐版用于替代原执行方案中不完整或容易产生歧义的部分。若本文后续章节与原章节存在冲突,以本补齐版中的以下规则为准:
- delivery 最小闭环必须进入 MVP 第一批,不能整体后移。
- 幂等不能只依赖 Redis 或状态流转表,必须新增
hss_idempotency_records。 - 定时任务必须有执行日志,必须新增
hss_job_execution_logs。 - Outbox 必须具备并发抢占、重试、死信能力,不能只做简单轮询。
- 支付、退款、冲正必须独立建模,不能只放在结算表字段里。
- 异常处理、监管整改、投诉反馈必须独立闭环,不能只在工单备注里处理。
- 状态机必须拆为规则判断与动作编排两层,不能只返回目标状态。
- OpenAPI 契约必须覆盖消费者端、管理端、delivery 端和内部任务接口。
- 测试必须覆盖并发、重复提交、Outbox 失败、支付重复回调、任务重跑、弱网补传。
- 上线采用 roll-forward + feature flag + dry-run + shadow 模式,不能依赖直接删字段或强回滚。
1. 需求与约束摘要
| 项 | 内容 |
|---|---|
| 任务类型 | 新接口 / 新能力开发(全新系统) |
| 目标 | 实现居家上门服务全链路闭环系统:需求受理 → 评估定级 → 方案制定 → 派单调度 → 上门执行 → 过程监管 → 验收反馈 → 结算归档 |
| 硬边界 | Spring Boot / Java 17+、PostgreSQL、Redis、MQTT(通知)、对象存储(文件)、RESTful+action、OpenAPI 3.1、hss_表前缀、Outbox通知、四层状态机、幂等写、前端只传动作不传状态、敏感数据授权脱敏审计 |
| 范围 | 全部模块 + delivery 端小程序 |
2. 代码依据
本项目为全新仓库,无现有后端代码。以下为架构文档依据:
| 依据文件 | 路径 | 职责 |
|---|---|---|
| V2.0 工程落地增强版 | docs/ architecture/居家上门服务闭环流程文档_V2工程落地增强版.md |
业务闭环流程、四层状态机、数据模型、调度算法、通知设计、合规安全、结算对账 |
| V2.1 行业经验增强版 | docs/ architecture/居家上门服务闭环流程文档_V2.1_行业经验增强版.md |
美团三层调度、ETA预测、VRPTW建模、delivery智能助手、运力运营中心、主数据平台、SRE稳定性 |
| V2.2 边界限制增强版 | /home/akoo/居家服务/居家上门服务闭环流程文档_V2.2_边界限制增强版.md |
12条硬约束、技术栈锁定、DDL边界、接口风格、部署环境、协作边界 |
3. DDL 依据
当前仓库无 db/init.sql 与 db/update.sql。以下设计基于 V2.0 文档 4.1 节数据域划分(第 939-958 行),结合 V2.2 第 16.3 节 DDL 边界约束。
| 数据域 | 设计表名(hss_前缀) | 来源 |
|---|---|---|
| 申请域 | hss_service_applications |
V2.0 §4.1 |
| 用户画像域 | hss_patient_profiles |
V2.0 §4.1 |
| 评估域 | hss_assessment_tasks、hss_assessment_reports |
V2.0 §4.1 |
| 异议域 | hss_objections |
V2.0 §4.2 |
| 方案域 | hss_service_plans、hss_service_plan_items、hss_plan_versions |
V2.0 §4.1 |
| 计划域 | hss_service_schedules |
V2.0 §4.1 |
| 工单域 | hss_work_orders、hss_work_order_items |
V2.0 §4.1 |
| 执行域 | hss_checkins、hss_execution_records、hss_evidence_files |
V2.0 §4.1 |
| 异常域 | hss_exceptions、hss_exception_actions |
V2.0 §4.1 |
| 监管域 | hss_spot_checks、hss_violations、hss_corrections |
V2.0 §4.1 |
| 验收域 | hss_acceptances、hss_complaints |
V2.0 §4.1 |
| 结算域 | hss_settlements、hss_settlement_items、hss_payments、hss_refunds |
V2.0 §4.1 |
| 归档域 | hss_ledgers、hss_archive_files |
V2.0 §4.1 |
| 通知域 | hss_notification_outbox、hss_notification_receipts |
V2.0 §7.2 |
| 审计域 | hss_audit_logs、hss_state_transitions |
V2.0 §3.6、§8.4 |
| 合规域 | hss_consent_records、hss_data_access_logs |
V2.0 §8.2 |
| 调度域(V2.1扩展) | hss_service_grids、hss_grid_capacity_daily、hss_staff_skill_capacity、hss_capacity_forecasts、hss_capacity_alerts |
V2.1 §15.2.1 |
| 主数据域(V2.1扩展) | hss_md_orgs、hss_md_staff、hss_md_qualifications、hss_md_service_items、hss_md_regions、hss_md_price_rules |
V2.1 §15.7 |
4. 改动范围清单
全部为新增文件。
| 路径 | 增/删/改 | 摘要 |
|---|---|---|
pom.xml |
增 | Maven 构建配置,Spring Boot 3.x + Java 17 |
src/main/java/com/meizhou/hss/ |
增 | 根包目录 |
src/main/java/.../common/ |
增 | 公共模块:响应体、异常、工具类、幂等注解、审计切面 |
src/main/java/.../config/ |
增 | Spring 配置:Security、Redis、MQTT、对象存储、定时任务、OpenAPI |
src/main/java/.../statemachine/ |
增 | 四层状态机引擎:申请、方案、工单、结算 |
src/main/java/.../module/application/ |
增 | 申请域:controller/service/repository/entity/dto |
src/main/java/.../module/assessment/ |
增 | 评估域:controller/service/repository/entity/dto |
src/main/java/.../module/plan/ |
增 | 方案域:controller/service/repository/entity/dto |
src/main/java/.../module/schedule/ |
增 | 计划与调度域 |
src/main/java/.../module/workorder/ |
增 | 工单域 |
src/main/java/.../module/execution/ |
增 | 执行域:签到、执行记录、证据链 |
src/main/java/.../module/exception/ |
增 | 异常域:异常上报与处理 |
src/main/java/.../module/supervision/ |
增 | 监管域:抽查、违规、整改 |
src/main/java/.../module/acceptance/ |
增 | 验收域:验收、评价、投诉 |
src/main/java/.../module/settlement/ |
增 | 结算域:结算、支付、退款 |
src/main/java/.../module/archive/ |
增 | 归档域:台账、归档文件 |
src/main/java/.../module/notification/ |
增 | 通知域:Outbox 与回执 |
src/main/java/.../module/audit/ |
增 | 审计域:审计日志、状态流转 |
src/main/java/.../module/compliance/ |
增 | 合规域:授权同意、数据访问日志 |
src/main/java/.../module/masterdata/ |
增 | 主数据域:机构、人员、资质、服务项目、区域、价格 |
src/main/java/.../module/capacity/ |
增 | 运力域:服务网格、容量、预测 |
src/main/resources/db/migration/ |
增 | Flyway 迁移脚本 |
src/main/resources/application.yml |
增 | 主配置 |
src/test/java/.../ |
增 | 单元测试与集成测试 |
delivery-miniapp/ |
增 | delivery 端小程序(uni-app) |
5. 方案概述
5.1 分层架构
┌──────────────────────────────────────────────┐
│ Controller 层(REST + action-style REST) │
│ - 参数校验(Spring Validation) │
│ - 幂等拦截(Idempotency-Key) │
│ - 权限注解(@PreAuthorize) │
│ - OpenAPI 注解(SpringDoc) │
└──────────────────┬───────────────────────────┘
│
┌──────────────────▼───────────────────────────┐
│ Service 层(业务编排) │
│ - 状态机调用(不跳过状态检查) │
│ - 事务边界(@Transactional) │
│ - Outbox 写入(同事务) │
│ - 审计日志写入 │
└──────────────────┬───────────────────────────┘
│
┌──────────────────▼───────────────────────────┐
│ Repository 层(数据访问) │
│ - MyBatis-Plus BaseMapper │
│ - 自定义 SQL(复杂查询/统计) │
│ - 乐观锁 version 字段 │
└──────────────────┬───────────────────────────┘
│
┌──────────────────▼───────────────────────────┐
│ Domain 层(领域对象) │
│ - Entity(映射表) │
│ - DTO / VO / Request / Response │
│ - 状态枚举 │
│ - 业务异常 │
└──────────────────────────────────────────────┘
5.2 核心数据流
POST /applications → 创建申请(DRAFT)
POST /applications/{id}/submit → 提交申请(PENDING_ACCEPTANCE)
POST /applications/{id}/accept → 受理通过(PENDING_ASSESSMENT)
POST /assessments/{id}/assign → 派发评估(ASSESSING)
POST /assessments/{id}/submit → 提交评估(ASSESSMENT_PASSED)
POST /plans → 创建方案(PLAN_DRAFT)
POST /plans/{id}/submit → 提交签署(PLAN_PENDING_SIGN)
POST /plans/{id}/sign → 签署通过(PLAN_EFFECTIVE)
POST /schedules/{id}/generate → 生成服务计划
POST /work-orders/{id}/dispatch → 派单(ORDER_ASSIGNED)
POST /work-orders/{id}/accept → 接单(ORDER_ACCEPTED)
POST /work-orders/{id}/check-in → 签到(ORDER_CHECKED_IN)
POST /work-orders/{id}/start → 开始服务(ORDER_IN_SERVICE)
POST /execution-records → 逐项记录执行
POST /work-orders/{id}/finish → 完成服务(ORDER_COMPLETED)
POST /acceptances → 发起验收(ACCEPTANCE_PENDING)
POST /acceptances/{id}/confirm → 验收通过(ACCEPTED)
POST /settlements/{id}/generate → 生成结算单
POST /settlements/{id}/approve → 审核通过
POST /settlements/{id}/pay → 支付完成(SETTLEMENT_PAID)
POST /settlements/{id}/archive → 归档(ARCHIVED)
5.3 异常流
任何非终态 → POST /{entity}/{id}/cancel → CANCELLED(需原因)
评估中 → POST /assessments/{id}/object → REVIEWING → 维持/重评
方案待签署 → POST /plans/{id}/reject → PLAN_REJECTED → 重新编制
工单已派单 → POST /work-orders/{id}/reassign → 改派
工单服务中 → POST /work-orders/{id}/report-exception → ORDER_EXCEPTION → 协调/改派/关闭
验收拒绝 → POST /acceptances/{id}/reject → 创建问题处理单
结算不通过 → POST /settlements/{id}/return → 退回修改
6. 代码级修改说明(核心)
6.1 项目初始化
文件:pom.xml(新增)
当前逻辑:不存在。 拟议改动:创建 Maven 父 POM,管理所有模块依赖。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.0</version>
</parent>
<groupId>com.meizhou</groupId>
<artifactId>hss-home-service</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>HSS Home Service</name>
<properties>
<java.version>17</java.version>
<mybatis-plus.version>3.5.6</mybatis-plus.version>
<springdoc.version>2.5.0</springdoc.version>
<flyway.version>10.11.0</flyway.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-database-postgresql</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
6.2 公共模块
目录:src/main/java/com/meizhou/hss/common/(新增)
6.2.1 统一响应体
文件:ApiResponse.java(新增)
当前逻辑:不存在。 拟议改动:所有接口统一使用此响应格式。
// 新增
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ApiResponse<T> {
private int code; // 200 成功
private String message;
private T data;
private String requestId; // UUID
private long timestamp; // System.currentTimeMillis()
public static <T> ApiResponse<T> ok(T data) { ... }
public static <T> ApiResponse<T> fail(int code, String message) { ... }
}
6.2.2 幂等拦截
文件:@Idempotent.java + IdempotentAspect.java(新增)
当前逻辑:不存在。
拟议改动:基于 Redis 实现 request_id 幂等。相同 requestId + action + entityId 在 TTL 内重复提交返回首次结果。
// 新增注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
String prefix() default "idempotent";
long ttlSeconds() default 300;
}
// 新增切面
@Aspect
@Component
public class IdempotentAspect {
@Around("@annotation(idempotent)")
public Object around(ProceedingJoinPoint pjp, Idempotent idempotent) {
// 从请求头获取 Idempotency-Key
// 拼接 key = prefix + ":" + idempotencyKey
// Redis SET NX EX,成功则执行,失败则返回缓存结果
}
}
6.2.3 审计字段基类
文件:BaseEntity.java(新增)
// 新增 - 所有 hss_ 表 Entity 继承此类
@Data
public abstract class BaseEntity {
@TableId(type = IdType.AUTO)
private Long id;
private Long tenantId;
private Long orgId;
private String status;
private Integer version;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private Long createdBy;
private Long updatedBy;
@TableLogic
private Integer deleted; // 0 正常, 1 已删除
}
6.3 状态机引擎
目录:src/main/java/com/meizhou/hss/statemachine/(新增)
文件清单
| 文件 | 职责 |
|---|---|
StateMachine.java |
通用状态机接口:transition(entity, action, context) → targetState |
StateMachineConfig.java |
状态转换规则配置(从 YAML/DB 加载) |
TransitionRule.java |
单条转换规则:fromStatus + action + role → toStatus + constraints |
ApplicationStateMachine.java |
申请状态机实现 |
PlanStateMachine.java |
方案状态机实现 |
WorkOrderStateMachine.java |
工单状态机实现 |
SettlementStateMachine.java |
结算状态机实现 |
StateTransitionException.java |
非法状态转换异常 |
当前逻辑:不存在。 拟议改动:基于 V2.0 文档 §3.2~3.5 的状态转换表实现。
// 新增 - TransitionRule
public record TransitionRule(
String entityType, // application / plan / work_order / settlement
String fromStatus,
String action, // submit / accept / reject / check-in / finish / approve / ...
String requiredRole, // 允许执行此动作的角色
String toStatus,
List<String> constraints // 约束条件:如 "hasAssessmentReport"、"amountMatched"
) {}
// 新增 - StateMachine 核心方法
public class StateMachine {
// 校验转换是否合法
public boolean canTransition(String entityType, String fromStatus,
String action, String role);
// 执行转换(不通过抛 StateTransitionException)
public String transition(String entityType, String fromStatus,
String action, String role, Map<String, Object> context);
// 记录状态流转到 hss_state_transitions
private void recordTransition(...);
}
状态转换规则初始化(基于文档 §3.2~3.5,部分示例):
// 新增 - 申请状态机规则
rules.add(new TransitionRule("application", "DRAFT", "submit", "APPLICANT",
"PENDING_ACCEPTANCE", List.of("materialsComplete")));
rules.add(new TransitionRule("application", "PENDING_ACCEPTANCE", "accept", "RECEPTIONIST",
"PENDING_ASSESSMENT", List.of("ageCheck", "qualificationCheck", "duplicateCheck")));
rules.add(new TransitionRule("application", "PENDING_ACCEPTANCE", "return", "RECEPTIONIST",
"RETURNED", List.of("returnReasonRequired")));
// ... 等全部规则
6.4 申请域模块
目录:src/main/java/com/meizhou/hss/module/application/
文件清单
| 文件 | 职责 |
|---|---|
entity/ServiceApplicationEntity.java |
映射 hss_service_applications |
dto/ApplicationCreateRequest.java |
创建申请请求 DTO |
dto/ApplicationSubmitRequest.java |
提交申请请求 DTO |
dto/ApplicationResponse.java |
申请响应 DTO |
dto/ApplicationListQuery.java |
列表查询参数 |
repository/ServiceApplicationMapper.java |
MyBatis-Plus Mapper |
service/ApplicationService.java |
业务逻辑 |
service/ApplicationValidationService.java |
自动校验(材料完整性、资格、重复申请) |
controller/ApplicationController.java |
REST 接口 |
当前逻辑:不存在。
拟议改动:
ApplicationController.java:
// 新增
@RestController
@RequestMapping("/api/hss/applications")
@Tag(name = "服务申请")
public class ApplicationController {
@PostMapping
@Idempotent(prefix = "app:create")
public ApiResponse<ApplicationResponse> create(
@Valid @RequestBody ApplicationCreateRequest req) {
// Service: 校验 → 创建 DRAFT 状态申请 → 记录状态流转
}
@PostMapping("/{id}/submit")
@Idempotent(prefix = "app:submit")
public ApiResponse<ApplicationResponse> submit(
@PathVariable Long id, @Valid @RequestBody ApplicationSubmitRequest req) {
// Service: 状态机 DRAFT→PENDING_ACCEPTANCE → 写 Outbox 通知受理员
}
@PostMapping("/{id}/accept")
@Idempotent(prefix = "app:accept")
@PreAuthorize("hasRole('RECEPTIONIST')")
public ApiResponse<ApplicationResponse> accept(@PathVariable Long id) {
// Service: 校验(材料+资格+重复)→ 状态机 PENDING_ACCEPTANCE→PENDING_ASSESSMENT
}
@PostMapping("/{id}/return")
@Idempotent(prefix = "app:return")
@PreAuthorize("hasRole('RECEPTIONIST')")
public ApiResponse<ApplicationResponse> returnApplication(
@PathVariable Long id, @Valid @RequestBody ReturnRequest req) {
// 约束:必须填写退回原因
}
@PostMapping("/{id}/cancel")
@Idempotent(prefix = "app:cancel")
public ApiResponse<ApplicationResponse> cancel(@PathVariable Long id) {
// 约束:申请人本人或授权家属
}
@GetMapping
public ApiResponse<Page<ApplicationResponse>> list(ApplicationListQuery query) {
// 数据范围:按 tenant_id、org_id、角色过滤
}
@GetMapping("/{id}")
public ApiResponse<ApplicationResponse> getById(@PathVariable Long id) {
// 脱敏:非授权角色隐藏完整地址、电话
}
}
注:其余模块(评估、方案、调度、工单、执行、异常、监管、验收、结算、归档、通知、审计、合规、主数据、运力)的代码结构遵循相同模式,此处为避免文档过度膨胀,仅输出申请域作为样板。完整代码在实施阶段逐模块编写。
6.5 通知 Outbox 模块
目录:src/main/java/com/meizhou/hss/module/notification/
关键设计:
// 新增 - 通知创建(在业务事务内调用)
@Service
public class NotificationService {
// 与业务在同一事务中写入 Outbox
@Transactional
public void createNotification(Long entityId, String entityType,
String channel, String template, Map<String, Object> params) {
NotificationOutboxEntity outbox = new NotificationOutboxEntity();
outbox.setEntityId(entityId);
outbox.setChannel(channel); // MQTT / WECHAT / SMS
outbox.setStatus("CREATED");
outbox.setRequestId(IdempotencyContext.getRequestId());
outboxMapper.insert(outbox);
}
}
// 新增 - 定时任务:扫描 Outbox 并异步发送
@Component
public class NotificationSender {
@Scheduled(fixedDelay = 2000) // 每2秒扫描
public void sendPending() {
// SELECT * FROM hss_notification_outbox WHERE status='CREATED' LIMIT 100
// 发送 → 更新 status=SENT → 记录 receipt
// 失败 → status=FAILED, retry_count+1
}
}
6.6 审计模块
目录:src/main/java/com/meizhou/hss/module/audit/
// 新增 - AOP 切面自动记录审计
@Aspect
@Component
public class AuditAspect {
@Around("@annotation(auditable)")
public Object around(ProceedingJoinPoint pjp, Auditable auditable) {
// 记录:操作人、时间、IP、设备、操作类型、业务对象ID、前后值
}
}
// 新增 - 状态流转记录(状态机内部调用)
// 写入 hss_state_transitions:
// entity_type, entity_id, from_status, action, to_status,
// operator_id, operator_role, reason, request_id, created_at
6.7 调度算法模块
目录:src/main/java/com/meizhou/hss/module/schedule/algorithm/
// 新增 - 两阶段调度
@Service
public class DispatchAlgorithm {
// 阶段1:硬约束过滤
public List<StaffCandidate> hardFilter(List<Staff> allStaff, WorkOrder order) {
return allStaff.stream()
.filter(s -> s.getStatus() == StaffStatus.ACTIVE)
.filter(s -> s.hasQualification(order.getRequiredSkills()))
.filter(s -> s.getRegion().contains(order.getServiceAddress().getRegion()))
.filter(s -> !hasTimeConflict(s, order))
.filter(s -> !s.isBlacklisted())
.filter(s -> s.getTodayOrderCount() < s.getMaxDailyOrders())
.toList();
}
// 阶段2:软约束评分
public List<ScoredStaff> scoreAndRank(List<StaffCandidate> candidates, WorkOrder order) {
// score = distance * 0.25 + skill * 0.25 + workload * 0.20
// + rating * 0.15 + response * 0.10 + familiarity * 0.05
// 返回 Top5,附解释
}
}
7. 接口设计方案(任务类型=1 强制)
7.1 核心接口清单
按主链路阶段组织,以下为核心接口设计。
7.1.1 服务申请接口组
接口1:创建服务申请
| 接口名称 | 创建服务申请 |
|---|---|
| 接口地址 | /api/hss/applications |
| 请求方式 | POST |
| 接口描述 | 服务对象/家属提交服务申请,创建后状态为 DRAFT |
| 参数名 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| patientId | string | 是 | 服务对象ID |
| serviceType | string | 是 | 服务类型枚举:HOME_CARE / REHABILITATION / BATH_ASSIST / ASSESSMENT |
| channel | string | 是 | 申请渠道:APP / WECHAT / PHONE / COMMUNITY / HOSPITAL |
| contactName | string | 是 | 联系人姓名 |
| contactPhone | string | 是 | 联系人电话 |
| address | string | 是 | 服务地址 |
| regionCode | string | 是 | 区域编码 |
| notes | string | 否 | 备注说明 |
| attachmentIds | array | 否 | 附件ID列表 |
| 字段路径 | 类型 | 示例 | 说明 |
|---|---|---|---|
| code | int | 200 | 状态码 |
| success | boolean | true | 是否成功 |
| message | string | 申请创建成功 | 响应消息 |
| data.id | string | 1001 | 申请ID |
| data.status | string | DRAFT | 申请状态 |
| data.createdAt | string | 2026-05-15T10:30:00 | 创建时间 |
请求示例:
{
"patientId": "2001",
"serviceType": "HOME_CARE",
"channel": "WECHAT",
"contactName": "张三",
"contactPhone": "138****5678",
"address": "梅江区金山街道XX小区3栋201",
"regionCode": "441402001",
"notes": "老人独居,需要日常护理",
"attachmentIds": ["att_001", "att_002"]
}
返回示例:
{
"code": 200,
"message": "申请创建成功",
"data": {
"id": "1001",
"status": "DRAFT",
"createdAt": "2026-05-15T10:30:00"
},
"requestId": "req-uuid-abc123",
"timestamp": 1715747400000
}
接口2:提交服务申请
| 接口名称 | 提交服务申请 |
|---|---|
| 接口地址 | /api/hss/applications/{id}/submit |
| 请求方式 | POST |
| 接口描述 | 将草稿状态申请提交为待受理,触发自动校验 |
| 参数名 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| id | string | 是 | 路径参数,申请ID |
| 字段路径 | 类型 | 示例 | 说明 |
|---|---|---|---|
| code | int | 200 | 状态码 |
| data.status | string | PENDING_ACCEPTANCE | 新状态 |
| data.validationResult.passed | boolean | true | 自动校验结果 |
接口3:受理通过
| 接口名称 | 受理通过 |
|---|---|
| 接口地址 | /api/hss/applications/{id}/accept |
| 请求方式 | POST |
| 接口描述 | 受理员审核通过,申请进入待评估状态 |
| 参数名 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| id | string | 是 | 路径参数,申请ID |
| reason | string | 否 | 审核备注 |
接口4:受理退回
| 接口名称 | 受理退回 |
|---|---|
| 接口地址 | /api/hss/applications/{id}/return |
| 请求方式 | POST |
| 接口描述 | 受理员退回申请,需填写退回原因 |
| 参数名 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| id | string | 是 | 路径参数 |
| reason | string | 是 | 退回原因(必填) |
7.1.2 工单执行接口组
接口5:GPS 签到
| 接口名称 | GPS签到 |
|---|---|
| 接口地址 | /api/hss/work-orders/{id}/check-in |
| 请求方式 | POST |
| 接口描述 | 服务人员在服务地址200米内签到,需GPS+照片+对象确认 |
| 参数名 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| id | string | 是 | 工单ID |
| latitude | number | 是 | 纬度 |
| longitude | number | 是 | 经度 |
| photoFileId | string | 是 | 现场照片文件ID |
| patientConfirmed | boolean | 是 | 服务对象是否确认 |
| patientSignatureId | string | 否 | 对象签名文件ID |
| 字段路径 | 类型 | 示例 | 说明 |
|---|---|---|---|
| code | int | 200 | 状态码 |
| data.status | string | ORDER_CHECKED_IN | 新状态 |
| data.distance | number | 85.5 | 签到位置距服务地址距离(米) |
| data.checkinTime | string | 2026-05-15T09:05:00 | 签到时间 |
请求示例:
{
"latitude": 24.2878,
"longitude": 116.1271,
"photoFileId": "file_checkin_001",
"patientConfirmed": true,
"patientSignatureId": "sig_001"
}
返回示例:
{
"code": 200,
"message": "签到成功",
"data": {
"id": "5001",
"status": "ORDER_CHECKED_IN",
"distance": 85.5,
"checkinTime": "2026-05-15T09:05:00"
},
"requestId": "req-uuid-def456",
"timestamp": 1715747700000
}
接口6:服务完成提交
| 接口名称 | 服务完成提交 |
|---|---|
| 接口地址 | /api/hss/work-orders/{id}/finish |
| 请求方式 | POST |
| 接口描述 | 服务人员完成所有必做项目后提交,工单进入已完成状态 |
| 参数名 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| id | string | 是 | 工单ID |
| executionRecords | array | 是 | 项目执行记录列表 |
| executionRecords[].planItemId | string | 是 | 方案项目ID |
| executionRecords[].status | string | 是 | COMPLETED / PARTIAL / NOT_COMPLETED / SKIPPED / USER_REFUSED |
| executionRecords[].actualStartTime | string | 是 | 实际开始时间 |
| executionRecords[].actualEndTime | string | 是 | 实际结束时间 |
| executionRecords[].evidenceFileIds | array | 否 | 证据文件ID列表 |
| executionRecords[].notes | string | 否 | 执行备注 |
| signOffLatitude | number | 是 | 签退纬度 |
| signOffLongitude | number | 是 | 签退经度 |
| serviceSummary | string | 否 | 服务总结 |
7.1.3 结算接口组
接口7:生成结算单
| 接口名称 | 生成结算单 |
|---|---|
| 接口地址 | /api/hss/settlements/generate |
| 请求方式 | POST |
| 接口描述 | 基于已验收工单生成结算单,自动汇总金额与抵扣 |
| 参数名 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| workOrderIds | array | 是 | 已验收工单ID列表 |
| periodStart | string | 是 | 结算周期开始日期 |
| periodEnd | string | 是 | 结算周期结束日期 |
| 字段路径 | 类型 | 示例 | 说明 |
|---|---|---|---|
| data.id | string | 7001 | 结算单ID |
| data.totalAmount | number | 1200.00 | 总金额 |
| data.insuranceDeduction | number | 800.00 | 长护险抵扣 |
| data.selfPayAmount | number | 400.00 | 自费金额 |
| data.items | array | [...] | 结算明细 |
7.2 接口契约约定
- 所有写接口必须带
Idempotency-Key请求头 - 所有分页接口参数:
page(默认1)、size(默认20,最大100)、sort(如createdAt,desc) - 状态动作接口路径格式:
POST /api/hss/{resource}/{id}/{action} - 资源 CRUD:
GET/POST/PUT/DELETE /api/hss/{resource}和/{id} - 统一错误响应:
{
"code": 40001,
"message": "参数校验失败",
"details": [
{"field": "contactPhone", "message": "联系电话格式不正确"}
],
"requestId": "req-uuid-err123"
}
7.3 错误码规划
| 错误码范围 | 含义 |
|---|---|
| 200 | 成功 |
| 40001-40099 | 参数校验错误 |
| 40101-40199 | 认证/授权错误 |
| 40301-40399 | 权限不足 |
| 40401-40499 | 资源不存在 |
| 40901-40999 | 状态冲突(非法状态转换) |
| 40910-40919 | 幂等冲突(重复提交) |
| 42201-42299 | 业务规则校验失败 |
| 50001-50099 | 服务器内部错误 |
| 50301-50399 | 依赖服务不可用 |
8. 数据库与迁移计划
8.1 DDL 变更明细
迁移脚本位于 src/main/resources/db/migration/,采用 Flyway 管理。
V1__baseline.sql(初始基线)
核心表结构(部分示例):
-- ==========================================
-- 基线迁移 V1:创建居家服务核心业务表
-- ==========================================
-- 1. 申请域
CREATE TABLE hss_service_applications (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL,
org_id BIGINT NOT NULL,
patient_id BIGINT NOT NULL,
service_type VARCHAR(32) NOT NULL, -- HOME_CARE / REHABILITATION / BATH_ASSIST / ASSESSMENT
channel VARCHAR(32) NOT NULL, -- APP / WECHAT / PHONE / COMMUNITY / HOSPITAL
contact_name VARCHAR(64) NOT NULL,
contact_phone VARCHAR(20) NOT NULL,
address TEXT NOT NULL,
region_code VARCHAR(20) NOT NULL,
status VARCHAR(32) NOT NULL DEFAULT 'DRAFT',
notes TEXT,
version INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by BIGINT,
updated_by BIGINT,
deleted SMALLINT NOT NULL DEFAULT 0
);
CREATE INDEX idx_hss_app_status ON hss_service_applications(status);
CREATE INDEX idx_hss_app_patient ON hss_service_applications(patient_id);
CREATE INDEX idx_hss_app_created ON hss_service_applications(created_at);
-- 2. 评估域
CREATE TABLE hss_assessment_tasks (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL,
org_id BIGINT NOT NULL,
application_id BIGINT NOT NULL REFERENCES hss_service_applications(id),
assessor_id BIGINT,
status VARCHAR(32) NOT NULL DEFAULT 'PENDING_ASSIGNMENT',
-- 评估结果快照
care_level VARCHAR(16), -- LEVEL_1 ~ LEVEL_5
risk_level VARCHAR(16), -- LOW / MEDIUM / HIGH / CRITICAL
report_content JSONB,
version INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by BIGINT,
updated_by BIGINT,
deleted SMALLINT NOT NULL DEFAULT 0
);
CREATE INDEX idx_hss_at_status ON hss_assessment_tasks(status);
CREATE INDEX idx_hss_at_app ON hss_assessment_tasks(application_id);
CREATE INDEX idx_hss_at_assessor ON hss_assessment_tasks(assessor_id);
CREATE TABLE hss_assessment_reports (
id BIGSERIAL PRIMARY KEY,
task_id BIGINT NOT NULL REFERENCES hss_assessment_tasks(id),
version INTEGER NOT NULL DEFAULT 1,
content JSONB NOT NULL, -- 评估指标详情
conclusion TEXT,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by BIGINT
);
-- 3. 方案域
CREATE TABLE hss_service_plans (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL,
org_id BIGINT NOT NULL,
application_id BIGINT NOT NULL,
assessment_task_id BIGINT NOT NULL,
status VARCHAR(32) NOT NULL DEFAULT 'PLAN_DRAFT',
version_number INTEGER NOT NULL DEFAULT 1,
total_amount DECIMAL(12,2), -- 方案总金额
insurance_deduction DECIMAL(12,2), -- 长护险抵扣
self_pay_amount DECIMAL(12,2), -- 自费金额
signed_at TIMESTAMP,
signed_by BIGINT,
effective_at TIMESTAMP,
terminated_at TIMESTAMP,
version INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by BIGINT,
updated_by BIGINT,
deleted SMALLINT NOT NULL DEFAULT 0
);
CREATE TABLE hss_service_plan_items (
id BIGSERIAL PRIMARY KEY,
plan_id BIGINT NOT NULL REFERENCES hss_service_plans(id),
service_item_id BIGINT NOT NULL, -- 引用主数据服务项目
item_name VARCHAR(128) NOT NULL, -- 版本化快照
unit_price DECIMAL(10,2) NOT NULL, -- 版本化快照
frequency INTEGER NOT NULL, -- 服务频次
standard_duration INTEGER, -- 标准服务时长(分钟)
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE TABLE hss_plan_versions (
id BIGSERIAL PRIMARY KEY,
plan_id BIGINT NOT NULL,
version_number INTEGER NOT NULL,
snapshot_data JSONB NOT NULL, -- 完整方案快照
change_reason TEXT,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by BIGINT
);
-- 4. 服务计划域
CREATE TABLE hss_service_schedules (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL,
plan_id BIGINT NOT NULL REFERENCES hss_service_plans(id),
scheduled_date DATE NOT NULL,
time_window_start TIME, -- 预约时间窗开始
time_window_end TIME, -- 预约时间窗结束
status VARCHAR(32) NOT NULL DEFAULT 'SCHEDULED',
version INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_hss_ss_date ON hss_service_schedules(scheduled_date);
CREATE INDEX idx_hss_ss_plan ON hss_service_schedules(plan_id);
-- 5. 工单域
CREATE TABLE hss_work_orders (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL,
org_id BIGINT NOT NULL,
schedule_id BIGINT REFERENCES hss_service_schedules(id),
plan_id BIGINT NOT NULL,
patient_id BIGINT NOT NULL,
staff_id BIGINT,
status VARCHAR(32) NOT NULL DEFAULT 'ORDER_CREATED',
service_date DATE NOT NULL,
scheduled_start TIME,
scheduled_end TIME,
risk_level VARCHAR(16),
is_high_risk BOOLEAN DEFAULT FALSE,
version INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by BIGINT,
updated_by BIGINT,
deleted SMALLINT NOT NULL DEFAULT 0
);
CREATE INDEX idx_hss_wo_status ON hss_work_orders(status);
CREATE INDEX idx_hss_wo_staff ON hss_work_orders(staff_id);
CREATE INDEX idx_hss_wo_date ON hss_work_orders(service_date);
CREATE INDEX idx_hss_wo_patient ON hss_work_orders(patient_id);
CREATE TABLE hss_work_order_items (
id BIGSERIAL PRIMARY KEY,
work_order_id BIGINT NOT NULL REFERENCES hss_work_orders(id),
plan_item_id BIGINT NOT NULL,
item_name VARCHAR(128) NOT NULL,
unit_price DECIMAL(10,2) NOT NULL,
required BOOLEAN DEFAULT TRUE, -- 是否必做
status VARCHAR(32) DEFAULT 'PENDING', -- COMPLETED/PARTIAL/NOT_COMPLETED/SKIPPED/USER_REFUSED
evidence_required BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
-- 6. 执行域
CREATE TABLE hss_checkins (
id BIGSERIAL PRIMARY KEY,
work_order_id BIGINT NOT NULL REFERENCES hss_work_orders(id),
checkin_type VARCHAR(16) NOT NULL, -- CHECKIN / CHECKOUT
latitude DECIMAL(10,7) NOT NULL,
longitude DECIMAL(10,7) NOT NULL,
distance_meters INTEGER, -- 距目标地址距离
photo_file_id VARCHAR(64),
patient_confirmed BOOLEAN,
patient_signature_id VARCHAR(64),
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by BIGINT
);
CREATE TABLE hss_execution_records (
id BIGSERIAL PRIMARY KEY,
work_order_id BIGINT NOT NULL,
work_order_item_id BIGINT NOT NULL,
status VARCHAR(32) NOT NULL, -- COMPLETED/PARTIAL/NOT_COMPLETED/SKIPPED/USER_REFUSED
actual_start TIMESTAMP,
actual_end TIMESTAMP,
notes TEXT,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by BIGINT
);
CREATE TABLE hss_evidence_files (
id BIGSERIAL PRIMARY KEY,
entity_type VARCHAR(32) NOT NULL, -- CHECKIN / EXECUTION / EXCEPTION / ACCEPTANCE
entity_id BIGINT NOT NULL,
file_type VARCHAR(16) NOT NULL, -- PHOTO / VIDEO / AUDIO
file_key VARCHAR(256) NOT NULL, -- 对象存储 key
file_size BIGINT,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by BIGINT
);
-- 7. 异常域
CREATE TABLE hss_exceptions (
id BIGSERIAL PRIMARY KEY,
work_order_id BIGINT NOT NULL REFERENCES hss_work_orders(id),
exception_type VARCHAR(32) NOT NULL, -- PATIENT_ABSENT / PATIENT_REFUSE / WRONG_ADDRESS / ...
description TEXT NOT NULL,
evidence_file_ids TEXT, -- JSON 数组
status VARCHAR(32) NOT NULL DEFAULT 'REPORTED',
handled_by BIGINT,
resolution TEXT,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by BIGINT
);
-- 8. 验收域
CREATE TABLE hss_acceptances (
id BIGSERIAL PRIMARY KEY,
work_order_id BIGINT NOT NULL REFERENCES hss_work_orders(id),
status VARCHAR(32) NOT NULL DEFAULT 'ACCEPTANCE_PENDING',
result VARCHAR(16), -- ACCEPTED / REJECTED
rating SMALLINT, -- 1-5 星
tags TEXT, -- JSON 标签数组
comment TEXT,
rejected_reason TEXT,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by BIGINT
);
-- 9. 结算域
CREATE TABLE hss_settlements (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL,
org_id BIGINT NOT NULL,
patient_id BIGINT NOT NULL,
status VARCHAR(32) NOT NULL DEFAULT 'SETTLEMENT_READY',
period_start DATE NOT NULL,
period_end DATE NOT NULL,
total_amount DECIMAL(12,2),
insurance_deduction DECIMAL(12,2),
self_pay_amount DECIMAL(12,2),
paid_at TIMESTAMP,
payment_channel VARCHAR(32),
payment_transaction_id VARCHAR(64),
version INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by BIGINT,
updated_by BIGINT,
deleted SMALLINT NOT NULL DEFAULT 0
);
CREATE TABLE hss_settlement_items (
id BIGSERIAL PRIMARY KEY,
settlement_id BIGINT NOT NULL REFERENCES hss_settlements(id),
work_order_id BIGINT NOT NULL,
execution_record_id BIGINT,
item_name VARCHAR(128),
unit_price DECIMAL(10,2),
actual_amount DECIMAL(10,2),
deduction DECIMAL(10,2),
self_pay DECIMAL(10,2),
status VARCHAR(32)
);
-- 10. 通知域
CREATE TABLE hss_notification_outbox (
id BIGSERIAL PRIMARY KEY,
entity_type VARCHAR(32) NOT NULL,
entity_id BIGINT NOT NULL,
channel VARCHAR(16) NOT NULL, -- MQTT / WECHAT / SMS
template_code VARCHAR(64),
params JSONB,
recipient_id BIGINT,
status VARCHAR(16) NOT NULL DEFAULT 'CREATED',
retry_count INTEGER DEFAULT 0,
max_retries INTEGER DEFAULT 3,
next_retry_at TIMESTAMP,
request_id VARCHAR(64),
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_hss_nf_status ON hss_notification_outbox(status, next_retry_at);
CREATE TABLE hss_notification_receipts (
id BIGSERIAL PRIMARY KEY,
outbox_id BIGINT NOT NULL,
status VARCHAR(16) NOT NULL, -- SENT / DELIVERED / READ / CONFIRMED / FAILED / EXPIRED
sent_at TIMESTAMP,
delivered_at TIMESTAMP,
read_at TIMESTAMP,
error_message TEXT,
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
-- 11. 审计域
CREATE TABLE hss_state_transitions (
id BIGSERIAL PRIMARY KEY,
entity_type VARCHAR(32) NOT NULL, -- application / plan / work_order / settlement
entity_id BIGINT NOT NULL,
from_status VARCHAR(32),
action VARCHAR(32) NOT NULL,
to_status VARCHAR(32) NOT NULL,
operator_id BIGINT,
operator_role VARCHAR(32),
reason TEXT,
request_id VARCHAR(64),
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_hss_st_entity ON hss_state_transitions(entity_type, entity_id);
CREATE INDEX idx_hss_st_request ON hss_state_transitions(request_id);
CREATE TABLE hss_audit_logs (
id BIGSERIAL PRIMARY KEY,
entity_type VARCHAR(32),
entity_id BIGINT,
action VARCHAR(32) NOT NULL,
operator_id BIGINT,
operator_name VARCHAR(64),
operator_role VARCHAR(32),
client_ip VARCHAR(45),
device_info VARCHAR(256),
detail JSONB,
is_sensitive BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_hss_al_entity ON hss_audit_logs(entity_type, entity_id);
CREATE INDEX idx_hss_al_operator ON hss_audit_logs(operator_id);
CREATE INDEX idx_hss_al_time ON hss_audit_logs(created_at);
-- 12. 合规域
CREATE TABLE hss_consent_records (
id BIGSERIAL PRIMARY KEY,
patient_id BIGINT NOT NULL,
consent_type VARCHAR(32) NOT NULL, -- GPS / PHOTO / HEALTH / AUDIO_VIDEO
consent_status VARCHAR(16) NOT NULL, -- GRANTED / REVOKED / EXPIRED
granted_at TIMESTAMP,
revoked_at TIMESTAMP,
expires_at TIMESTAMP,
created_by BIGINT,
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE TABLE hss_data_access_logs (
id BIGSERIAL PRIMARY KEY,
operator_id BIGINT NOT NULL,
data_type VARCHAR(32) NOT NULL, -- PERSONAL / HEALTH / LOCATION / FINANCIAL
entity_type VARCHAR(32),
entity_id BIGINT,
access_action VARCHAR(16) NOT NULL, -- VIEW / EXPORT / DOWNLOAD
client_ip VARCHAR(45),
reason TEXT,
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
-- 唯一约束:幂等键
CREATE UNIQUE INDEX uk_hss_st_request ON hss_state_transitions(request_id, entity_type, entity_id);
8.2 执行顺序与窗口建议
| 阶段 | 脚本 | 内容 | 建议窗口 |
|---|---|---|---|
| 1 | V1__baseline.sql | 全部核心表创建 | 首次部署(停机窗口 10min) |
| 2 | V2__seed_master_data.sql | 主数据初始化(区域、服务项目、资质字典) | 紧随 V1 |
| 3 | V3__add_indexes.sql | 性能索引补充 | 在线(CREATE INDEX CONCURRENTLY) |
| 后续 | V4+ | 增量变更 | 按需,遵循 flyway 版本号 |
8.3 回滚策略
- 每次迁移对应一个
U版本回滚脚本(如U1__baseline_rollback.sql) - 回滚原则:非生产环境可直接 DROP;生产环境采用
migrate down + data restore - 关键变更必须备份受影响数据:
CREATE TABLE xxx_backup AS SELECT * FROM xxx WHERE ... - 禁止
DROP COLUMN直接执行:先标记废弃 → 观察一个版本 → 再删除
9. 接口契约与兼容性
9.1 兼容性说明
| 方面 | 策略 |
|---|---|
| 账号/登录 | 兼容现有平台 JWT Token 格式 |
| 角色/权限 | 新增居家服务角色(受理员、评估员、方案制定员、调度员、服务人员、监管员、结算员),不影响现有角色 |
| 机构/用户 | 复用现有 org_id / user_id 体系 |
| 文件上传 | 复用现有对象存储接口,居家服务文件单独 folder |
| 业务表 | 全新 hss_ 前缀,不影响现有业务表 |
9.2 版本管理
- API 路径不加版本号,通过 Header
Accept-Version管理 - 破坏性变更走新接口 + 旧接口保留一个版本后废弃
10. 自测与回归
10.1 单元测试范围
| 测试对象 | 覆盖内容 |
|---|---|
| 状态机 | 所有合法转换、非法转换拦截、重复提交幂等 |
| 校验服务 | 年龄校验、资格校验、重复申请校验 |
| 调度算法 | 硬约束过滤逻辑、软约束评分计算、Top5排序 |
| 结算计算 | 金额汇总、抵扣计算、部分完成折算 |
10.2 集成测试范围
| 测试场景 | 验证点 |
|---|---|
| 主链路 | 申请→评估→方案→工单→执行→验收→结算→归档 全流程 |
| 异常链路 | 退回、异议、拒签、改派、异常上报、拒绝验收、结算退回 |
| 通知链路 | Outbox 写入→定时发送→回执记录 |
| 权限链路 | 角色隔离、数据范围过滤、字段脱敏 |
| 并发场景 | 同 request_id 重复提交、派单并发冲突 |
10.3 关键回归场景
- 状态机规则变更后,所有上游调用方不受影响(规则集中管理)
- 结算公式变更后,历史结算单金额不变(版本化快照)
- 服务项目价格调整后,已签署方案不受影响(版本冻结)
11. 工程目录结构总览
hss-home-service/
├── pom.xml
├── src/
│ ├── main/
│ │ ├── java/com/meizhou/hss/
│ │ │ ├── HssApplication.java # Spring Boot 启动类
│ │ │ ├── common/
│ │ │ │ ├── ApiResponse.java # 统一响应
│ │ │ │ ├── BaseEntity.java # 审计字段基类
│ │ │ │ ├── ErrorCode.java # 错误码枚举
│ │ │ │ ├── BusinessException.java # 业务异常
│ │ │ │ ├── Idempotent.java # 幂等注解
│ │ │ │ ├── IdempotentAspect.java # 幂等切面
│ │ │ │ ├── Auditable.java # 审计注解
│ │ │ │ └── AuditAspect.java # 审计切面
│ │ │ ├── config/
│ │ │ │ ├── SecurityConfig.java
│ │ │ │ ├── RedisConfig.java
│ │ │ │ ├── MqttConfig.java
│ │ │ │ ├── ObjectStorageConfig.java
│ │ │ │ ├── SchedulerConfig.java
│ │ │ │ ├── OpenApiConfig.java
│ │ │ │ └── MyBatisPlusConfig.java
│ │ │ ├── statemachine/
│ │ │ │ ├── StateMachine.java
│ │ │ │ ├── TransitionRule.java
│ │ │ │ ├── StateTransitionException.java
│ │ │ │ ├── ApplicationStateMachine.java
│ │ │ │ ├── PlanStateMachine.java
│ │ │ │ ├── WorkOrderStateMachine.java
│ │ │ │ └── SettlementStateMachine.java
│ │ │ └── module/
│ │ │ ├── application/
│ │ │ │ ├── controller/ApplicationController.java
│ │ │ │ ├── service/ApplicationService.java
│ │ │ │ ├── service/ApplicationValidationService.java
│ │ │ │ ├── repository/ServiceApplicationMapper.java
│ │ │ │ ├── entity/ServiceApplicationEntity.java
│ │ │ │ └── dto/{CreateRequest,SubmitRequest,Response,ListQuery}.java
│ │ │ ├── assessment/
│ │ │ │ ├── controller/AssessmentController.java
│ │ │ │ ├── service/AssessmentService.java
│ │ │ │ ├── repository/{AssessmentTaskMapper,AssessmentReportMapper}.java
│ │ │ │ ├── entity/{AssessmentTaskEntity,AssessmentReportEntity}.java
│ │ │ │ └── dto/{...}.java
│ │ │ ├── plan/
│ │ │ │ └── ... (同模式)
│ │ │ ├── schedule/
│ │ │ │ ├── controller/ScheduleController.java
│ │ │ │ ├── service/ScheduleService.java
│ │ │ │ ├── service/DispatchService.java
│ │ │ │ ├── algorithm/DispatchAlgorithm.java
│ │ │ │ ├── algorithm/HardConstraintFilter.java
│ │ │ │ ├── algorithm/SoftScoringRanker.java
│ │ │ │ └── ...
│ │ │ ├── workorder/
│ │ │ ├── execution/
│ │ │ ├── exception/
│ │ │ ├── supervision/
│ │ │ ├── acceptance/
│ │ │ ├── settlement/
│ │ │ ├── archive/
│ │ │ ├── notification/
│ │ │ │ ├── service/NotificationService.java
│ │ │ │ ├── sender/NotificationSender.java
│ │ │ │ ├── sender/MqttSender.java
│ │ │ │ ├── sender/WechatSender.java
│ │ │ │ └── sender/SmsSender.java
│ │ │ ├── audit/
│ │ │ ├── compliance/
│ │ │ ├── masterdata/
│ │ │ └── capacity/
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── application-dev.yml
│ │ ├── application-test.yml
│ │ ├── application-prod.yml
│ │ └── db/migration/
│ │ ├── V1__baseline.sql
│ │ ├── V2__seed_master_data.sql
│ │ └── V3__add_indexes.sql
│ └── test/java/com/meizhou/hss/
│ ├── statemachine/
│ │ ├── ApplicationStateMachineTest.java
│ │ ├── PlanStateMachineTest.java
│ │ ├── WorkOrderStateMachineTest.java
│ │ └── SettlementStateMachineTest.java
│ ├── module/
│ │ ├── application/ApplicationControllerTest.java
│ │ ├── workorder/WorkOrderIntegrationTest.java
│ │ └── settlement/SettlementCalculationTest.java
│ └── algorithm/DispatchAlgorithmTest.java
└── delivery-miniapp/
├── pages/delivery/
│ ├── login/
│ ├── index/ # 工作台
│ ├── orders/ # 工单列表
│ ├── order-detail/ # 工单详情
│ ├── accept/ # 接单确认
│ ├── checkin/ # GPS签到
│ ├── execute/ # 服务执行
│ ├── exception/ # 异常上报
│ ├── finish/ # 签退完成
│ ├── offline-sync/ # 离线补传
│ ├── messages/ # 通知消息
│ └── profile/ # 我的资质
└── ...
12. 实施优先级与分批计划(补齐版)
12.1 第一批(MVP 可验收闭环)- 预计 4~6 周
第一批目标不是做全量美化和智能化,而是完成一条真实可验收的居家上门服务闭环:
申请 → 评估 → 方案 → 签署 → 服务计划 → 工单 → 派单 → delivery 接单 → 签到 → 项目执行 → 完成 → 验收 → 结算 → 归档
必须包含:
- 项目初始化:
pom.xml、application.yml、启动类、环境配置。 - 公共模块:
ApiResponse、BaseEntity、统一异常、错误码、分页、租户上下文。 - 数据库基线:Flyway migration,包含 P0/P1 核心表。
- 状态机:申请、方案、工单、结算四层状态机。
- 状态动作编排:
ActionHandler,完成状态流转 + 副作用写入。 - 幂等模块:
hss_idempotency_records+Idempotency-Key拦截器。 - Outbox 模块:写入、并发抢占、重试、死信、回执。
- 审计模块:状态流转、敏感数据访问、高风险操作审计。
- 申请域:创建、提交、受理通过、退回、取消。
- 评估域:派发、签到、提交报告、异议。
- 方案域:创建、编辑、提交签署、签署、拒签、版本管理。
- 计划域:方案生效后生成服务计划。
- 工单域:工单创建、派单、接单、拒单、改派。
- 执行域:GPS 签到、项目级执行记录、证据元数据、完成。
- 异常域:异常上报、异常处理动作、紧急事件基础流程。
- 验收域:验收确认、拒绝验收、评价。
- 结算域:结算单、结算明细、审核、支付确认、归档。
- 支付退款域:支付流水、重复回调幂等、退款/冲正基础表。
- 定时任务:Outbox 重试、派单超时扫描、方案待签提醒、验收超时扫描。
- delivery 最小闭环页面:登录、工作台、今日工单、详情、接单、签到、执行、异常、完成、证据上传、消息。
- OpenAPI 契约:覆盖 MVP 全部接口。
- E2E 测试:完成至少 1 条从申请到归档的完整链路。
12.2 第二批(增强能力)- 预计 3~4 周
- 调度算法:硬约束过滤 + 软约束评分 + Top5 推荐解释。
- 监管域:抽查计划、违规记录、整改跟踪。
- 合规域:授权同意、敏感数据访问审计、脱敏策略完善。
- 主数据域:机构、人员、资质、服务项目、区域、价格、规则版本。
- 文件证据增强:断点/失败补传、文件 hash、签名 URL、下载审计。
- 定时任务增强:未来 1~7 天工单生成、次日排班预计算、批量结算。
- delivery 离线能力:本地缓存、失败补传、重复提交防护。
- 管理端调度台:改派、撤单、异常处理、超时预警。
- 权限矩阵落地:接口级权限、字段级脱敏、数据范围过滤。
- Testcontainers 集成测试:PostgreSQL、Redis、MQTT/Mock、对象存储。
12.3 第三批(智能化与运营)- 后续迭代
- 运力域:服务网格、容量预测、网格容量看板。
- ETA 预测:到达时间、完成时间、风险缓冲。
- 调度优化:局部搜索、插入成本、OR-Tools 批量预排。
- 数据看板:质量分析、异常热力图、服务连续性、人员负载。
- 人员绩效结算:服务质量、完成率、投诉率、违规率、评分。
- 智能助手:delivery 端风险提醒、证据缺失提醒、异常处理建议。
13. 开始实现 Prompt(补齐版)
13.1 第一阶段开工 Prompt
你现在作为资深 Spring Boot 后端工程师,在全新仓库中实现“居家上门服务系统”的 MVP 第一批。
必须遵守:
1. 技术栈:Spring Boot 3.x + Java 17 + PostgreSQL + MyBatis-Plus/Spring Data JDBC + Flyway + Redis + MQTT + 对象存储。
2. 架构:模块化单体,不拆微服务,但按 domain/module 分包。
3. 表规范:所有业务表使用 hss_ 前缀,必须包含 tenant_id、org_id、status、created_by、updated_by、created_at、updated_at、deleted_at、version。
4. 状态机:申请、方案、工单、结算四层状态机;前端只传 action,不传目标状态。
5. 状态动作:状态机只判断合法性,ActionHandler 负责编排副作用,例如写业务表、状态流转、Outbox、审计。
6. 幂等:所有写接口支持 Idempotency-Key,并落库 hss_idempotency_records。
7. 通知:所有通知必须先写 hss_notification_outbox,异步发送;Outbox 必须支持并发锁、重试、dead-letter。
8. 结算:结算金额由后端基于方案快照和执行记录计算,禁止使用前端传入金额作为最终金额。
9. 文件:证据文件进入对象存储,数据库只保存元数据、hash、storage_key、访问审计。
10. delivery 端:MVP 必须包含登录、今日工单、详情、接单、签到、执行、异常、完成、证据上传、消息。
11. 测试:必须包含状态机单测、金额计算单测、幂等测试、Outbox 失败重试测试、支付重复回调测试、定时任务重复执行测试。
12. 输出:Flyway DDL、Java 代码、OpenAPI 契约、状态机文档、数据字典、测试用例。
第一阶段只做 MVP 可验收闭环,不做复杂 ETA、OR-Tools、智能推荐、完整数据看板。
先生成工程目录、pom.xml、application.yml、Flyway V1~V10 migration、BaseEntity、ApiResponse、错误码、幂等拦截器、状态机骨架、Outbox 骨架,然后再实现申请域、评估域、方案域、计划域、工单域、执行域、验收域、结算域。
14. 生产可交付补强清单
14.1 P0 必须补齐项
| 编号 | 补齐项 | 必须完成的内容 | 不完成的风险 |
|---|---|---|---|
| P0-01 | 幂等落库 | 新增 hss_idempotency_records,所有写接口接入 |
重复提交、重复支付、重复生成工单 |
| P0-02 | 定时任务日志 | 新增 hss_job_execution_logs |
定时任务失败不可追溯、不可重跑 |
| P0-03 | Outbox 并发安全 | locked_by、locked_until、next_retry_at、dead-letter |
多实例重复发送、通知堆积 |
| P0-04 | delivery MVP | 登录、今日工单、详情、接单、签到、执行、异常、完成、证据上传 | 无法验证真实上门履约 |
| P0-05 | 方案签署事务 | 签署记录、方案生效、服务计划生成、状态流转、Outbox 同事务 | 方案已生效但没有计划 |
| P0-06 | 证据元数据 | hss_evidence_files 完整字段、对象存储、hash、签名访问 |
服务执行证据链不可信 |
| P0-07 | 支付幂等 | 支付流水号 + 结算单号幂等 | 重复支付回调导致账务错误 |
| P0-08 | 状态动作编排 | StateMachine + ActionHandler 双层结构 | service 逻辑膨胀,状态副作用失控 |
14.2 P1 必须补齐项
| 编号 | 补齐项 | 必须完成的内容 |
|---|---|---|
| P1-01 | 异议闭环 | hss_objections,评估异议、方案异议、复核记录 |
| P1-02 | 异常动作 | hss_exception_actions,每一次处理动作留痕 |
| P1-03 | 监管整改 | hss_spot_checks、hss_violations、hss_corrections |
| P1-04 | 投诉反馈 | hss_complaints,与验收拒绝区分 |
| P1-05 | 支付退款 | hss_payments、hss_refunds、冲正记录 |
| P1-06 | 归档台账 | hss_ledgers、hss_archive_files |
| P1-07 | 主数据 | 服务项目、资质、区域、价格、机构、人员 |
| P1-08 | OpenAPI 完整契约 | 消费者端、管理端、delivery 端、定时任务管理接口 |
15. 完整 DDL 补齐范围
15.1 必须进入 baseline 的核心表
第一版 Flyway 不建议只建少量表再频繁补核心表。MVP 可以不实现全部业务逻辑,但核心表结构应尽量一次性建立,避免后续状态和数据迁移困难。
| 数据域 | 表名 |
|---|---|
| 申请 | hss_service_applications |
| 画像 | hss_patient_profiles |
| 评估 | hss_assessment_tasks、hss_assessment_reports |
| 异议 | hss_objections |
| 方案 | hss_service_plans、hss_service_plan_items、hss_plan_versions |
| 计划 | hss_service_schedules |
| 工单 | hss_work_orders、hss_work_order_items |
| 执行 | hss_checkins、hss_execution_records、hss_evidence_files |
| 异常 | hss_exceptions、hss_exception_actions |
| 监管 | hss_spot_checks、hss_violations、hss_corrections |
| 验收 | hss_acceptances、hss_complaints |
| 结算 | hss_settlements、hss_settlement_items |
| 支付 | hss_payments、hss_refunds |
| 归档 | hss_ledgers、hss_archive_files |
| 通知 | hss_notification_outbox、hss_notification_receipts |
| 幂等 | hss_idempotency_records |
| 任务 | hss_job_execution_logs |
| 审计 | hss_state_transitions、hss_audit_logs、hss_data_access_logs |
| 合规 | hss_consent_records |
| 主数据 | hss_md_organizations、hss_md_staff、hss_md_staff_qualifications、hss_md_service_items、hss_md_price_rules、hss_md_regions |
| 运力 | hss_service_grids、hss_grid_capacity_daily、hss_staff_skill_capacity |
15.2 新增关键表字段建议
hss_idempotency_records
CREATE TABLE hss_idempotency_records (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL,
idempotency_key VARCHAR(128) NOT NULL,
business_key VARCHAR(256) NOT NULL,
request_hash VARCHAR(128) NOT NULL,
response_code VARCHAR(64),
response_body TEXT,
status VARCHAR(32) NOT NULL,
locked_until TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (tenant_id, idempotency_key, business_key)
);
hss_notification_outbox
CREATE TABLE hss_notification_outbox (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL,
business_type VARCHAR(64) NOT NULL,
business_id BIGINT NOT NULL,
event_type VARCHAR(64) NOT NULL,
receiver_id BIGINT NOT NULL,
channel VARCHAR(32) NOT NULL,
payload JSONB NOT NULL,
status VARCHAR(32) NOT NULL,
retry_count INT NOT NULL DEFAULT 0,
max_retry_count INT NOT NULL DEFAULT 5,
next_retry_at TIMESTAMPTZ,
locked_by VARCHAR(128),
locked_until TIMESTAMPTZ,
sent_at TIMESTAMPTZ,
last_error TEXT,
dedupe_key VARCHAR(256) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (tenant_id, dedupe_key)
);
hss_job_execution_logs
CREATE TABLE hss_job_execution_logs (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL,
job_name VARCHAR(128) NOT NULL,
biz_date DATE NOT NULL,
shard_key VARCHAR(128) NOT NULL DEFAULT 'default',
status VARCHAR(32) NOT NULL,
started_at TIMESTAMPTZ NOT NULL,
finished_at TIMESTAMPTZ,
success_count INT NOT NULL DEFAULT 0,
failure_count INT NOT NULL DEFAULT 0,
error_message TEXT,
idempotency_key VARCHAR(256) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (tenant_id, job_name, biz_date, shard_key)
);
16. 状态机与动作编排补齐设计
16.1 为什么不能只有 transition 方法
状态机不能只做:
from_status + action = to_status
因为每个动作都有副作用。例如“方案签署”必须同时完成:
- 校验方案版本。
- 校验签署人。
- 写签署记录。
- 冻结方案快照。
- 生成服务计划。
- 写状态流转。
- 写 Outbox。
- 写审计日志。
16.2 推荐双层结构
StateMachine:判断动作是否合法
ActionHandler:执行业务副作用
目录建议:
statemachine/
StateMachine.java
TransitionRule.java
TransitionContext.java
TransitionResult.java
ApplicationStateMachine.java
PlanStateMachine.java
WorkOrderStateMachine.java
SettlementStateMachine.java
action/
ApplicationSubmitHandler.java
ApplicationAcceptHandler.java
AssessmentSubmitHandler.java
PlanSubmitSignHandler.java
PlanSignHandler.java
WorkOrderAssignHandler.java
WorkOrderAcceptHandler.java
WorkOrderCheckInHandler.java
WorkOrderFinishHandler.java
WorkOrderExceptionHandler.java
AcceptanceConfirmHandler.java
SettlementApproveHandler.java
PaymentCallbackHandler.java
16.3 ActionHandler 标准流程
1. 加载业务对象
2. 校验租户、机构、角色、数据范围
3. 校验 Idempotency-Key
4. 调用 StateMachine 校验动作合法性
5. 执行业务副作用
6. 更新业务状态
7. 写 hss_state_transitions
8. 写 hss_audit_logs
9. 写 hss_notification_outbox
10. 提交事务
11. 返回动作结果
17. Outbox 并发安全与补偿机制
17.1 Outbox 发送状态
| 状态 | 含义 |
|---|---|
| CREATED | 已创建,待发送 |
| LOCKED | 已被某个实例抢占 |
| SENT | 已发送成功 |
| FAILED | 本次发送失败,等待重试 |
| DEAD | 超过最大重试次数,进入死信 |
17.2 多实例抢占策略
PostgreSQL 推荐使用 FOR UPDATE SKIP LOCKED:
SELECT *
FROM hss_notification_outbox
WHERE status IN ('CREATED', 'FAILED')
AND (next_retry_at IS NULL OR next_retry_at <= now())
ORDER BY created_at
LIMIT 100
FOR UPDATE SKIP LOCKED;
发送任务拿到记录后更新为 LOCKED,设置 locked_by 和 locked_until。发送成功后更新为 SENT,失败则增加 retry_count 并设置 next_retry_at。超过最大重试次数进入 DEAD。
17.3 失败补偿
FAILED可自动重试。DEAD必须进入运营或技术告警。- 支持按业务 ID 手动重发。
- 支持按时间区间批量重发。
- 通知通道失败不能回滚业务主状态。
- 消费者端和 delivery 端必须能通过主动查询拿到真实业务状态。
18. delivery 端 MVP 最小闭环
18.1 MVP 页面范围
| 页面 | 是否 MVP 必须 | 说明 |
|---|---|---|
| 登录页 | 是 | 校验服务人员角色、机构、状态 |
| 工作台 | 是 | 今日任务、待接单、待签到、服务中、异常 |
| 今日工单 | 是 | 查询本人任务 |
| 工单详情 | 是 | 地址、对象脱敏信息、服务项目、风险提示 |
| 接单/拒单 | 是 | 触发工单状态机 |
| 签到 | 是 | GPS、拍照、对象确认 |
| 执行 | 是 | 项目级执行记录 |
| 异常 | 是 | 对象不在家、拒绝服务、身体异常、地址错误等 |
| 完成 | 是 | 提交服务总结、证据校验 |
| 证据上传 | 是 | 文件上传、hash、元数据 |
| 消息页 | 是 | 派单、改派、异常处理、验收反馈 |
| 离线补传 | 第二批 | MVP 可先做失败重试,不做完整离线队列 |
| 智能助手 | 第三批 | 风险提醒、路径建议、语音输入 |
18.2 delivery 聚合接口
GET /api/hss/delivery/workbench
GET /api/hss/delivery/work-orders/today
GET /api/hss/delivery/work-orders/{id}/detail
POST /api/hss/delivery/work-orders/{id}/accept
POST /api/hss/delivery/work-orders/{id}/reject
POST /api/hss/delivery/work-orders/{id}/check-in
POST /api/hss/delivery/work-orders/{id}/start-service
POST /api/hss/delivery/work-orders/{id}/execution-records
POST /api/hss/delivery/work-orders/{id}/finish
POST /api/hss/delivery/work-orders/{id}/exception
POST /api/hss/delivery/evidence/upload
GET /api/hss/delivery/messages
POST /api/hss/delivery/messages/{id}/read
19. 完整 OpenAPI 接口补齐范围
19.1 消费者端接口
POST /api/hss/applications
POST /api/hss/applications/{id}/submit
POST /api/hss/applications/{id}/cancel
GET /api/hss/applications/{id}
GET /api/hss/service-plans/{id}/for-sign
POST /api/hss/service-plans/{id}/sign
POST /api/hss/service-plans/{id}/reject
GET /api/hss/work-orders/{id}/acceptance
POST /api/hss/work-orders/{id}/acceptance/confirm
POST /api/hss/work-orders/{id}/acceptance/reject
POST /api/hss/work-orders/{id}/rating
GET /api/hss/settlements/{id}
POST /api/hss/settlements/{id}/pay
19.2 管理端接口
GET /api/hss/admin/applications
POST /api/hss/admin/applications/{id}/accept
POST /api/hss/admin/applications/{id}/return
POST /api/hss/admin/assessment-tasks/{id}/assign
POST /api/hss/admin/service-plans
PUT /api/hss/admin/service-plans/{id}
POST /api/hss/admin/service-plans/{id}/submit-sign
POST /api/hss/admin/service-plans/{id}/generate-schedule
POST /api/hss/admin/work-orders/{id}/assign
POST /api/hss/admin/work-orders/{id}/reassign
POST /api/hss/admin/work-orders/{id}/cancel
POST /api/hss/admin/exceptions/{id}/handle
POST /api/hss/admin/settlements/generate
POST /api/hss/admin/settlements/{id}/approve
POST /api/hss/admin/settlements/{id}/reject
GET /api/hss/admin/audit-logs
19.3 文件与证据接口
POST /api/hss/files/presign-upload
POST /api/hss/files/commit
GET /api/hss/files/{id}/signed-url
GET /api/hss/evidence-files/by-work-order/{workOrderId}
19.4 主数据接口
GET /api/hss/master/service-items
GET /api/hss/master/price-rules
GET /api/hss/master/regions
GET /api/hss/master/staff
GET /api/hss/master/staff/{id}/qualifications
20. 定时任务补齐设计
20.1 必须实现的 MVP 任务
| 任务 | 频率 | MVP 是否必须 |
|---|---|---|
| Outbox 重试 | 每 1 分钟 | 是 |
| 派单超时扫描 | 每 5 分钟 | 是 |
| 方案待签提醒 | 每 30 分钟 | 是 |
| 服务开始前提醒 | 每 10 分钟 | 是 |
| 验收超时扫描 | 每小时 | 是 |
| 未来工单生成 | 每晚 00:30 | 第二批也可,但表和接口先预留 |
| 次日排班预计算 | 每晚 01:00 | 第二批 |
| 批量结算生成 | 每日/每月 | 第二批 |
| 审计日志归档 | 每月 | 第二批 |
20.2 任务执行规则
- 所有任务写
hss_job_execution_logs。 - 所有任务按
job_name + biz_date + shard_key幂等。 - 所有任务支持 dry-run。
- 所有任务支持手动重跑。
- 所有任务不得绕过状态机直接改状态。
- 多实例部署必须使用分布式锁或数据库唯一键防重复。
21. 测试与验收补齐
21.1 新增必测用例
| 类型 | 用例 |
|---|---|
| 幂等 | 同一 Idempotency-Key 重复提交返回同一结果 |
| 幂等冲突 | 同一 Idempotency-Key 不同 body 返回冲突 |
| 并发派单 | 两个调度员同时派同一工单,只能成功一次 |
| 并发接单 | 服务人员重复点击接单,只生成一次接单记录 |
| 并发完成 | 重复完成提交不生成重复执行记录 |
| Outbox 失败 | MQTT 失败后 Outbox 进入 FAILED 并可重试 |
| Outbox 死信 | 超过最大次数进入 DEAD |
| 支付回调 | 同一支付流水重复回调只入账一次 |
| 事务回滚 | 服务计划生成失败时方案签署整体回滚 |
| 文件补传 | 文件上传成功但业务提交失败,可重新绑定 |
| 权限 | 服务人员不能查看非本人工单 |
| 脱敏 | 非授权角色看不到完整手机号、地址、健康信息 |
| 任务重跑 | 同一天重复生成工单不产生重复数据 |
| 弱网 | delivery 端重复点击、网络失败、恢复补传 |
21.2 MVP 验收门槛
- 状态机核心分支单测覆盖率 ≥ 90%。
- 结算金额计算单测覆盖率 ≥ 90%。
- 所有写接口必须有幂等测试。
- 所有状态动作必须有非法状态测试。
- delivery 端完成 5 条 E2E:接单、签到、执行、异常、完成。
- Outbox 完成失败重试和死信测试。
- 支付回调完成重复回调测试。
- 定时任务完成重复执行测试。
- 权限和脱敏至少覆盖服务人员、调度员、结算员、监管员四类角色。
- OpenAPI 与实际接口保持一致。
22. 灰度发布与回滚补齐
22.1 发布策略
| 能力 | 发布方式 |
|---|---|
| 状态机 | 测试环境全量验证后上线,不做灰度状态 |
| 调度算法 | shadow 模式 → 人工确认 → 小范围自动 |
| 自动验收 | 默认关闭,试点机构开启 |
| 批量结算 | dry-run → 人工审核 → 自动生成 |
| delivery 新页面 | 按机构/角色灰度 |
| 定时任务 | 小范围 biz_date dry-run 后开启 |
| Outbox | 先发送测试通道,再切真实通道 |
22.2 回滚原则
- 数据库以 roll-forward 为主,不依赖直接 down migration。
- 已上线的 migration 不修改,新增 migration 修正。
- 删除字段必须两阶段:先停用,后删除。
- 新状态上线前必须确认旧代码能兼容或不会读取。
- 功能必须有 feature flag。
- 回滚后不能产生孤儿状态。
- 任务必须可暂停、可恢复、可重跑。
23. 最终开工检查清单
开始实现前,必须确认以下事项:
| 检查项 | 是否必须 |
|---|---|
| 已确认 Spring Boot / Java 17 / PostgreSQL / Redis / MQTT / 对象存储 | 是 |
| 已确认 hss_ 表前缀、统一审计字段、软删除、乐观锁 | 是 |
| 已确认方案、计划、工单分离 | 是 |
| 已确认工单项目级执行记录 | 是 |
| 已确认所有写接口 Idempotency-Key | 是 |
已确认新增 hss_idempotency_records |
是 |
已确认新增 hss_job_execution_logs |
是 |
| 已确认 Outbox 支持锁、重试、死信 | 是 |
| 已确认 delivery MVP 进入第一批 | 是 |
| 已确认支付、退款独立表 | 是 |
| 已确认异常、监管、投诉独立闭环 | 是 |
| 已确认 OpenAPI 全契约先行 | 是 |
| 已确认 Testcontainers/真实依赖集成测试 | 是 |
| 已确认灰度开关和 roll-forward 回滚策略 | 是 |
24. 最终结论
这份补齐版已经可以作为正式开工文档使用。实现时不要再扩大需求范围,第一阶段只围绕“真实可验收闭环”推进:
后端状态机可信
数据库链路完整
通知可靠可补偿
delivery 可真实履约
结算可追溯
异常可兜底
权限与审计可验收
测试能证明系统可靠
第一阶段完成后,系统应能真实跑通:
家属提交申请
→ 受理员受理
→ 评估员评估
→ 管理端生成方案
→ 家属签署方案
→ 系统生成服务计划和工单
→ 调度员派单
→ delivery 服务人员接单
→ GPS 签到
→ 项目级执行
→ 上传证据
→ 完成服务
→ 家属验收
→ 系统生成结算
→ 支付确认
→ 归档
只要严格按本补齐版执行,就可以开始实现。