284 lines
6.8 KiB
Markdown
284 lines
6.8 KiB
Markdown
# API / 模块合约
|
|
|
|
> 本文定义第三方 API、统一响应和内部模块合约的目标形状。实现前可细化,但不要在代码里另起一套不兼容接口。
|
|
|
|
## 通用约定
|
|
|
|
- 传输:HTTPS JSON。
|
|
- 编码:UTF-8 JSON。
|
|
- 时间格式:`YYYY-MM-DD` 用于业务日期,时间戳使用 ISO 8601。
|
|
- 第三方鉴权:待定,推荐 `Authorization: Bearer <token>` 或 `X-App-Key` + `X-Signature`。
|
|
- 每个响应包含 `trace_id`。
|
|
- 每个 API 请求都生成一个 archive 文件,保存 API 和 CHIS 请求 / 响应,详见 [架构设计](04-architecture.md) 的请求归档文件章节。
|
|
|
|
成功响应:
|
|
|
|
```json
|
|
{
|
|
"ok": true,
|
|
"trace_id": "TRACE_ID",
|
|
"data": {}
|
|
}
|
|
```
|
|
|
|
错误响应:
|
|
|
|
```json
|
|
{
|
|
"ok": false,
|
|
"trace_id": "TRACE_ID",
|
|
"error": {
|
|
"code": "bad_request",
|
|
"message": "请求参数不正确"
|
|
}
|
|
}
|
|
```
|
|
|
|
## 第三方 API
|
|
|
|
### `POST /api/v1/health-checks/query-detail`
|
|
|
|
用途:查询 CHIS 中某次体检详情。第一版用于验证 CHIS 登录、Redis 会话、通用请求和查询链路。
|
|
|
|
第一版请求要求调用方传全参数:
|
|
|
|
```json
|
|
{
|
|
"account_ref": "chis-account-alias",
|
|
"healthCheck": "0000000000481718",
|
|
"phrId": "44162511100102877",
|
|
"idCard": "440000********1234",
|
|
"checkDate": "2014-06-05"
|
|
}
|
|
```
|
|
|
|
说明:
|
|
|
|
- `healthCheck + phrId + idCard` 用于调用 CHIS `getHMNIListOfHTML`。
|
|
- `idCard + checkDate` 用于调用 CHIS `getCheckInfoDetail`。
|
|
- 当前资料不能证明只传 `healthCheck` 就能查完整详情;后续任务会验证反查链路。
|
|
|
|
成功响应:
|
|
|
|
```json
|
|
{
|
|
"ok": true,
|
|
"trace_id": "TRACE_ID",
|
|
"data": {
|
|
"healthCheck": "0000000000481718",
|
|
"detail": {},
|
|
"check_info": {}
|
|
}
|
|
}
|
|
```
|
|
|
|
### `POST /api/v1/health-checks/submit`
|
|
|
|
用途:提交单条体检数据到 CHIS。
|
|
|
|
请求示例(字段待后续业务样例细化):
|
|
|
|
```json
|
|
{
|
|
"request_id": "third-party-unique-id",
|
|
"source": "third-party-system",
|
|
"account_ref": "chis-account-alias",
|
|
"person": {
|
|
"id_card": "440000********1234",
|
|
"name": "张三"
|
|
},
|
|
"check_date": "2026-07-04",
|
|
"health_check": {
|
|
"height": 170,
|
|
"weight": 65
|
|
}
|
|
}
|
|
```
|
|
|
|
成功响应:
|
|
|
|
```json
|
|
{
|
|
"ok": true,
|
|
"trace_id": "TRACE_ID",
|
|
"data": {
|
|
"request_id": "third-party-unique-id",
|
|
"idempotent": false,
|
|
"chis_result": {
|
|
"code": 200,
|
|
"message": "success"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
重复提交响应:
|
|
|
|
```json
|
|
{
|
|
"ok": true,
|
|
"trace_id": "TRACE_ID",
|
|
"data": {
|
|
"request_id": "third-party-unique-id",
|
|
"idempotent": true,
|
|
"chis_result": {
|
|
"code": 200,
|
|
"message": "success"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## 错误码
|
|
|
|
| code | HTTP | 说明 |
|
|
| --- | --- | --- |
|
|
| `unauthorized` | 401 | 第三方鉴权失败 |
|
|
| `forbidden` | 403 | 第三方无权限使用该账号或机构 |
|
|
| `bad_request` | 400 | 请求体格式错误或必填字段缺失 |
|
|
| `invalid_health_check_data` | 400 | 体检字段校验失败 |
|
|
| `missing_health_check_query_params` | 400 | 查询体检详情缺少 `healthCheck`、`phrId`、`idCard` 或 `checkDate` |
|
|
| `chis_login_failed` | 502 | CHIS 登录失败 |
|
|
| `chis_session_invalid` | 502 | CHIS 会话无效且重登失败 |
|
|
| `chis_request_failed` | 502 | CHIS 业务接口失败 |
|
|
| `chis_timeout` | 504 | CHIS 请求超时 |
|
|
| `idempotency_conflict` | 409 | 相同幂等键对应不同请求体 |
|
|
|
|
## 内部模块合约
|
|
|
|
### `ChisLoginClient.from_config(config).login(username, password)`
|
|
|
|
输入:
|
|
|
|
```python
|
|
username: str
|
|
password: str
|
|
```
|
|
|
|
输出:
|
|
|
|
```python
|
|
ChisLoginSession(
|
|
cookie=str,
|
|
jsessionid=str,
|
|
role_id=str,
|
|
role_name=str,
|
|
user_name=str,
|
|
roles_result=dict,
|
|
apps_result=dict,
|
|
)
|
|
```
|
|
|
|
职责:
|
|
|
|
- 从 `CHIS_BASE_URL`、`CHIS_PUBLIC_KEY`、`CHIS_PROXY` 创建登录客户端。
|
|
- 调用 `logon/myRoles`,使用 SM2 加密密码和时间戳 `d`。
|
|
- 优先选择 `责任医生助理` 或 `责任医生` 角色。
|
|
- 调用 `logon/myApps` 获取应用上下文。
|
|
- 从 `Set-Cookie` 提取 `JSESSIONID`,组装 CHIS cookie。
|
|
- 登录失败、缺少允许角色、缺少 cookie 时抛出 `ChisLoginError`,错误对象包含稳定 `code`。
|
|
|
|
### `ChisLoginClient.get_lander_info(cookie)`
|
|
|
|
输入:
|
|
|
|
```python
|
|
cookie: str
|
|
```
|
|
|
|
输出:CHIS `chis.myPageService / getLanderInfo` 原始字典响应。
|
|
|
|
职责:
|
|
|
|
- 使用已有 CHIS cookie 调用 `*.jsonRequest?`。
|
|
- 请求体固定为 `serviceId=chis.myPageService`、`serviceAction=getLanderInfo`、`method=execute`。
|
|
- 成功时返回当前账号信息响应。
|
|
- CHIS 返回非 200 业务码时抛出 `ChisLoginError(code="chis_session_invalid")`,用于判断会话无效。
|
|
|
|
### `RedisChisSessionStore`
|
|
|
|
职责:
|
|
|
|
- 使用 Redis key `chis:session:{account_ref}` 保存 CHIS 会话对象。
|
|
- `save(account_ref, session)` 按 `expires_at` 计算 TTL 并调用 Redis `setex`。
|
|
- `get(account_ref)` 只返回未过期会话;发现过期会话时删除缓存并返回 `None`。
|
|
- `delete(account_ref)` 清理指定账号引用的缓存。
|
|
- 会话 JSON 包含 `base_url`、`uid`、`role_id`、`manage_unit`、`cookies`、`login_at`、`expires_at`、`last_validated_at`,不得包含明文密码。
|
|
|
|
### `ChisSessionManager.ensure_session(account_ref)`
|
|
|
|
输入:
|
|
|
|
```python
|
|
account_ref: str
|
|
```
|
|
|
|
输出:
|
|
|
|
```python
|
|
ChisSession(
|
|
cookies=dict,
|
|
uid=str,
|
|
role_id=str | None,
|
|
manage_unit=str | None,
|
|
expires_at=datetime,
|
|
)
|
|
```
|
|
|
|
职责:
|
|
|
|
- 优先读取 Redis 有效会话。
|
|
- 会话不存在或验证失败时登录 CHIS。
|
|
- 登录成功后缓存会话。
|
|
- 不返回明文密码。
|
|
|
|
### `ChisCrypto.sm2_encrypt(public_key, raw_text)`
|
|
|
|
职责:
|
|
|
|
- 使用配置中的 CHIS public key 加密密码和时间戳 `d`。
|
|
- 兼容已有 `hans_chis.sm2.sm2_encrypt` 行为:`gmssl.sm2.CryptSM2(mode=0)`,返回带 `04` 前缀的十六进制密文。
|
|
- 不记录明文和密文到普通业务日志。
|
|
|
|
### `HealthCheckQueryClient.get_detail(session, health_check, phr_id, id_card)`
|
|
|
|
输出:CHIS `getHMNIListOfHTML` 的标准化结果。
|
|
|
|
职责:
|
|
|
|
- 组装 `chis.healthCheckService / getHMNIListOfHTML` 请求。
|
|
- 不做第三方鉴权。
|
|
- 不保存数据。
|
|
|
|
### `HealthCheckQueryClient.get_check_info(session, id_card, check_date)`
|
|
|
|
输出:CHIS `getCheckInfoDetail` 的标准化结果。
|
|
|
|
职责:
|
|
|
|
- 组装 `chis.healthCheckService / getCheckInfoDetail` 请求。
|
|
- 身份证号日志脱敏。
|
|
|
|
### `HealthCheckMapper.to_chis_payload(input_model, session_context)`
|
|
|
|
输入:内部体检标准模型和 CHIS 会话上下文。
|
|
|
|
输出:CHIS `*.jsonRequest` 请求体。
|
|
|
|
职责:
|
|
|
|
- 字段映射。
|
|
- 字典值转换。
|
|
- 默认值和空值处理。
|
|
- 不访问网络,不访问 Redis。
|
|
|
|
### `ChisClient.json_request(session, payload)`
|
|
|
|
职责:
|
|
|
|
- 发送 CHIS `*.jsonRequest`。
|
|
- 携带 cookie 和 header。
|
|
- 根据配置处理出站代理:`CHIS_PROXY` 为空直连;有值时为 CHIS HTTP 请求设置同一个 SOCKS5 代理。
|
|
- 处理 timeout、CHIS 错误、未登录。
|
|
- 返回标准化 CHIS 响应对象。
|