# 架构设计 > 本文讲系统结构、职责划分、数据模型、技术难点和开发顺序。具体技术选型见 [技术栈](03-tech-stack.md)。 ## 一、系统结构 ```text 第三方系统 | | HTTPS JSON v Flask API / middleware | | create_app 注册 blueprint / middleware / config | | 参数校验、鉴权、幂等 v Application service | | 查询编排 / 上报编排 / 标准业务模型 v Mapper / transformer | | CHIS 保存请求模型 v CHIS client + auth manager | | cookies / session cache / optional CHIS_PROXY v Redis | v 基卫 CHIS *.jsonRequest / 登录接口 API / service / CHIS client | +--> logs/ # 每天一个综合运行日志,保留 1 年 | +--> archives/ # 每个 API 请求一个完整原始归档 JSON,不脱敏 ``` ## 二、职责划分 **Flask application factory** - `app/__init__.py` 暴露 `create_app(config_object=None)`。 - 在 factory 内加载配置、注册 blueprint、注册 middleware / error handler。 - 不在模块导入时读取真实 CHIS 凭证、连接 Redis 或发起外部请求。 - 测试通过 factory 注入测试配置和 fake / mock 组件。 **API / view** - 接收第三方请求。 - 完成第三方鉴权、trace_id、基础参数校验。 - 调用 application service。 - 返回统一响应。 - 不写 CHIS 字段映射和登录细节。 **middleware** - 可以负责第三方鉴权、trace_id、请求体大小限制、日志上下文。 - 不建议每个请求无脑登录 CHIS。 - 如果做 CHIS 会话准备,也应调用 `ChisSessionManager.ensure_session()`,由 session manager 决定复用或重登。 **application service** - 编排单条体检上报流程。 - 编排 CHIS 只读查询验证流程。 - 处理幂等查询、业务校验、调用 mapper、调用 CHIS client。 - 将 CHIS 错误翻译为本项目统一错误。 **mapper / transformer** - 将第三方输入转换为内部标准模型。 - 将内部标准模型转换为 CHIS 请求体。 - 管理字段默认值、字典映射、日期格式、空值策略。 - 重点参考 `HealthCheckHtmlForm.js` 中 `getSaveRequest` 和 `saveToServer` 的行为。 **CHIS auth manager** - 负责登录 CHIS、获取角色 / 应用 / 会话上下文。 - 负责查询当前账号信息或等价接口,判断会话是否有效。 - 负责 Redis 会话缓存、清理和重登。 - 登录链路参考 `D:\hans\chupd\chis\login_client_v2.py`,但不能原样依赖 Django model/cache。 - CHIS public key 从 Flask 配置或环境变量读取,不从代码硬编码。 - 当前 T-101 已落地 `app/chis/auth.py:ChisLoginClient`:负责 `myRoles`、角色选择、`myApps`、JSESSIONID 提取和 cookie 组装;Redis 会话缓存与有效性验证留给 T-102 / T-103。 **CHIS client** - 封装 `*.jsonRequest` 和其他 CHIS HTTP 请求。 - 自动携带会话 cookie 和必要 header。 - 读取 `CHIS_PROXY`:配置为空时直连;配置有值时对 CHIS 登录、账号信息查询和 `*.jsonRequest` 统一使用代理。 - 代理只作用于出站访问 CHIS,不代理第三方系统访问本项目的入站请求。 - 处理超时、重试、登录失效、错误码、日志脱敏。 - 先用 `getLanderInfo`、`getEncryType`、`getHMNIListOfHTML` 等只读接口验证通道,再接入保存接口。 **Redis repository** - 保存 CHIS 会话对象。 - 保存幂等键和处理结果摘要。 - 可保存短期锁,避免同一账号并发重登。 **logging / archive** - `logs/` 保存运行日志:每天一个综合日志文件,包含 INFO、WARNING、ERROR、EXCEPTION 等不同级别记录,保留 1 年。 - 运行日志只记录摘要:`trace_id`、接口、耗时、状态、错误码、CHIS service/action、必要定位字段。 - `archives/` 保存请求归档:一个 API 请求一个 archive JSON 文件,保存该次 API 请求 / 响应以及期间所有 CHIS 请求 / 响应。 - archive 文件不脱敏,用于部署在内网前置机后的本地审计和故障排查。 - archive 写入不应影响主业务成功 / 失败判定;写入失败必须记入运行日志。 - archive 文件必须原子写入:先写 `.tmp`,完整落盘后 rename 为 `.json`。 - `logs/` 与 `archives/` 必须加入 `.gitignore`,不得进入代码仓库。 - 上线前必须评估磁盘容量;保留 1 年需要有清理任务或运维策略。 ## 三、建议项目结构 ```text chisup/ ├── app/ │ ├── __init__.py # create_app(config_object=None) │ ├── api/ │ │ └── health_check.py │ ├── middleware/ │ ├── services/ │ │ ├── health_check_query.py │ │ └── health_check_submit.py │ ├── mappers/ │ │ └── health_check.py │ ├── chis/ │ │ ├── auth.py │ │ ├── client.py │ │ ├── crypto.py │ │ ├── session_store.py │ │ └── errors.py │ ├── repositories/ │ ├── validators/ │ └── config.py ├── tests/ ├── docs/ ├── logs/ # 运行时生成,不提交 git ├── archives/ # 运行时生成,不提交 git,保存原始敏感归档 ├── reverse_file/ ├── requirements.txt └── README.md ``` ## 四、核心数据对象 ### 4.1 第三方上报请求 具体字段待定,建议至少包含: | 字段 | 说明 | | --- | --- | | `request_id` | 第三方业务流水号,用于幂等 | | `account_ref` | CHIS 账号或机构配置引用;不推荐直接传明文密码 | | `person.id_card` | 居民身份证号,日志必须脱敏 | | `check_date` | 体检日期 | | `health_check` | 体检主体数据 | | `source` | 第三方来源系统 | ### 4.2 Redis CHIS 会话对象 建议结构: ```json { "base_url": "CHIS_BASE_URL_ALIAS", "uid": "CHIS_USER_ID", "role_id": "CHIS_ROLE_ID", "manage_unit": "CHIS_MANAGE_UNIT", "cookies": {}, "login_at": "2026-07-04T00:00:00+08:00", "expires_at": "2026-07-04T02:00:00+08:00", "last_validated_at": "2026-07-04T00:10:00+08:00" } ``` 不要保存明文密码。确需保存账号凭证时,应使用安全配置或密钥管理,并在文档中明确加密和权限边界。 ### 4.3 CHIS 体检保存请求 从逆向脚本可确认体检保存不是单表直传,至少涉及: - `hcData` - 生活方式数据 - 查体数据 - 辅助检查数据 - 健康评价 / 指导数据 - 用药、住院、非免疫规划接种等列表数据 最终字段以真实 CHIS 请求和 schema 验证为准。 ### 4.4 CHIS 体检详情查询请求 已确认 `reverse_file/20260704_query_health_check.har` 中的体检详情查询需要: ```json { "serviceId": "chis.healthCheckService", "method": "execute", "serviceAction": "getHMNIListOfHTML", "schema": "chis.application.hc.schemas.HC_HealthCheck", "body": { "healthCheck": "体检主键", "phrId": "健康档案号", "idCard": "身份证号" } } ``` 检查报告信息查询需要: ```json { "serviceId": "chis.healthCheckService", "serviceAction": "getCheckInfoDetail", "method": "execute", "body": { "idCard": "身份证号", "checkDate": "体检日期" } } ``` 注意:当前资料不能证明“只传 `healthCheck` 就能查详情”。若外部 API 要支持只传体检 id,需要先验证如何由 `healthCheck` 反查 `phrId`、`idCard`、`checkDate`、`empiId`、`createUser`。 ### 4.5 请求归档文件 archive 文件按日期和接口分层保存,建议路径: ```text archives/ └── YYYY/ └── MM/ └── DD/ └── api-name/ └── YYYYMMDD_HHMMSS_trace__req_.json ``` 文件名至少包含: - 日期时间。 - API 名称或安全化后的接口标识。 - `trace_id`。 - 第三方 `request_id`;没有时使用服务端生成的唯一值。 - 可选:`healthCheck` 等业务定位字段。 文件内容格式: ```json { "meta": { "trace_id": "TRACE_ID", "api": "POST /api/v1/health-checks/query-detail", "request_id": "third-party-request-id", "created_at": "2026-07-04T21:15:30+08:00", "duration_ms": 1234, "result": "success" }, "api": [ { "request": {}, "response": {} } ], "chis": [ { "service_id": "chis.healthCheckService", "service_action": "getHMNIListOfHTML", "request": {}, "response": {}, "duration_ms": 456 } ] } ``` 约束: - archive 保存完整原始数据,不脱敏。 - archive 目录只能在内网前置机本地受控访问,禁止通过 API 静态暴露。 - archive 中允许保存 CHIS 请求和响应原文,但不得提交到 git、测试快照或公开文档。 - archive 文件保留 1 年,过期清理策略后续在部署任务中落地。 ## 五、关键技术难点 | 难点 | 说明 | 应对 | | --- | --- | --- | | CHIS 登录链路 | 涉及公钥、SM2 加密、角色、应用、JSESSIONID 和 cookie 组装 | T-101 已迁移为 `ChisLoginClient`,后续用真实账号做联调 | | CHIS 网络代理 | 部署在内网前置机时,访问 CHIS 可能必须走 SOCKS5 | 使用可选 `CHIS_PROXY` 配置;空值直连,有值时为 `http` / `https` 同时设置 requests proxies | | 会话有效性 | cookie 存在不代表 CHIS 会话仍有效 | 用账号信息查询接口确认,失败则清理缓存并重登 | | 只传体检 id 查询详情 | `getHMNIListOfHTML` 还需要 `phrId` 和 `idCard` | 先要求调用方传全参数,后续验证反查链路 | | 体检字段转换 | CHIS 前端保存逻辑复杂,数据块多 | 先做最小样例 mapper,逐步补字段测试 | | 幂等 | 第三方重试可能导致重复体检记录 | 使用 `source + request_id` 或业务唯一键保存结果 | | 隐私与日志 / 归档 | 日志只存摘要,archive 保存完整原始敏感数据 | `logs/` 每天滚动且摘要化;`archives/` 不脱敏但本地受控、原子写入、保留 1 年、不进 git | | CHIS 接口变化 | 逆向接口可能因版本变化失效 | client 层集中封装,转换层有测试样例 | ## 六、推荐开发顺序 1. 初始化 Flask 项目骨架和测试命令。 2. 接入 CHIS 登录代码,验证能获取有效会话。 3. 实现账号信息查询 / 会话有效性确认。 4. 实现 Redis 会话缓存、锁和失效重登。 5. 实现通用 CHIS `*.jsonRequest` client。 6. 先跑通体检详情只读查询,验证登录、会话、角色/机构、SM2 加密和错误处理。 7. 定义第三方体检上报 API 和统一响应。 8. 实现最小体检 mapper。 9. 通过通用 CHIS request 提交最小样例。 10. 补幂等、日志 / archive、错误码和测试。