---
title: FRIDAY 推送数据契约
description: FRIDAY SaaS 按形态 A 推送的 15 个 source 及字段定义;与已验证的存量导出形状一致,push 无缝衔接。
icon: FileJson
---

> 本文是 [Push 通道](/docs/integration/channel-push)**形态 A** 在 FRIDAY SaaS 宿主的具体化:每个 `source` 的 JSON 字段清单。
> 字段形状与 PAC 已完成的存量摄入(2026-07,10.7 万患者 / 36 万事实)**完全一致**——宿主按此推送,与存量自动衔接:
> 同行同 `updated_gmt_at` 幂等去重,行更新(`updated_gmt_at` 变)自动版本演进。**重叠无害,宁多勿漏**。

---

## 1. 通用约定(所有 source 适用)

```jsonc
// POST /pac/v1/push/rows
{
  "source": "med_emr_info",     // 本文各节的 source 名
  "rows": [ { /* 原生行,字段见各节 */ } ]
}
```

| 约定 | 说明 |
|---|---|
| **id 类字段一律字符串** | `patient_id` / `id` / `appointment_id` 等统一 String(数值型会被 PAC 校验拒收——存量摄入实测踩过的坑) |
| **时间格式** | 统一传 naive 北京墙钟 `"YYYY-MM-DD HH:mm:ss"`(PAC 按 `Asia/Shanghai` 解释)。实证(2026-07-20):MySQL `datetime` 存的就是北京墙钟(`_gmt_` 命名系惯例误导),直出即可;**Mongo `Date` 是北京墙钟伪装成 UTC 存储**(小时分布双证)——从 Mongo 读出后**须直读 UTC 字段还原墙钟**,切勿 `toISOString()` 带 `Z` 推送(会被按真 UTC 多加 8 小时) |
| **`updated_gmt_at` 必须随行更新 bump** | 它是幂等键的一半:不 bump,该行的更新会被永久去重丢弃 |
| **空值** | 传 `null` 或省略字段;不要传 `"NULL"` 字符串 |
| **金额单位** | **元**(decimal 原值,实证:洁治单价均值 ¥310/已结算客单均值 ¥1,730);PAC 侧转分 |
| **投递语义** | at-least-once:未收到 2xx 确认就重推;重复由 PAC 幂等键吸收 |

---

## 2. 患者与关系(3 个 source)

### `customer_basic_info` — 患者主档

> ✅ 患者主档已支持 push(2026-07-20):**部分更新语义** —— 只更新本次提供的字段,
> 未提供的字段不覆盖存量(如只推 `customer_basic_info` 时 phone 不在场 → 保留库内号码);
> push 无"清空字段"语义,需要清空走全量通道。`customer_referee_circle`(关系边)同样可 push。

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `id` | string | ✅ | 患者主键(= PAC externalId;其他表 `patient_id`/`customer_id` 指向它) |
| `name` | string | | 姓名 |
| `sex` | string | | `1`=男 `2`=女,其余置空 |
| `birthday` | string(date) | | 生日 |
| `file_number` | string | | 病历号/档案号(客服沟通用) |
| `tenant_id` | string | ✅ | 品牌 GUID(宿主原生列) |
| `organization_id` | string | | 建档诊所 |
| `created_gmt_at` / `updated_gmt_at` | string(datetime) | ✅ | 建档/更新时间 |

### `customer_contacts` — 联系方式(1:N)

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `id` | string | ✅ | 行主键 |
| `customer_id` | string | ✅ | → 患者 id |
| `contacts_tel` | string | ✅ | 电话号码 |
| `is_default` | string/number | | 默认号标记(PAC 挑默认号:is_default↓ → contacts_type↑ → id↑) |
| `contacts_type` | string/number | | 联系类型(`1` 本人号候选优先) |
| `tel_type` | string/number | | 号码类型 |
| `relationship` | string/number | | 持号人与患者的关系(官方枚举 `PhoneRelationshipEnum`:`1`本人 `2`爸爸 `3`妈妈 `4`爷爷 `5`奶奶 `6`朋友 `7`配偶 `8`子女 `9`其他) |

### `customer_referee_circle` — 转介绍圈(患者-患者关系)

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `id` | string | ✅ | 行主键 |
| `tenant_id` | string | ✅ | 品牌 GUID |
| `organization_id` | string | | 诊所 |
| `customer_id` | string | ✅ | 本人患者 id |
| `referee_patient_id` | string | ✅ | 关系人患者 id(也是在册患者) |
| `referee_relationship` | string/number | | 关系码(官方枚举 `RecommendRelationshipEnum`:`1`配偶 `2`爸爸 `3`子女 `4`兄弟 `5`爷爷 `6`孙子女 `7`朋友 `8`其他亲属 `9`姐妹 `10`妈妈 `11`奶奶 `12`姐弟 `13`兄妹 `14`其他 `15`外公 `16`外婆 `17`外孙子女;**语义=本行 customer 是 referee 的 X**) |
| `type` | string/number | | 关系类型标记 |
| `created_gmt_at` / `updated_gmt_at` | string(datetime) | | |

---

## 3. 预约(1 个 source)

### `appointment_base` — 预约(全状态推,PAC 侧过滤)

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `id` | string | ✅ | 预约主键 |
| `patient_appointment_id` | string | ✅ | → 患者 id |
| `organization_id` | string | ✅ | 诊所 |
| `tenant_id` | string | ✅ | 品牌 GUID |
| `appointment_date` | string(date) | ✅ | 预约日期 |
| `appointment_start` | string(time) | ✅ | 开始时刻(与 date 拼成预约时刻) |
| `appointment_status` | string | ✅ | `1`预约正常 `2`改约 `3`取消 `4`已到诊 `5`已就诊 `6`已结算 `7`到诊患者变更 `8`walk-in变更 `9`已爽约 `10`草稿(官方注释;PAC 当前消费 1-6/8/9,7 与 10 丢弃) |
| `doctor_user_id` | string | | 医生 id |
| `appointment_time_length` | string/number | | 时长(分钟) |
| `in_time` | string(datetime) | | 到诊时刻(状态 4/5/6 时,PAC 据此产接诊事件) |
| `created_gmt_at` / `updated_gmt_at` | string(datetime) | ✅ | |

---

## 4. 病历链(3 个 source)

### `med_emr_info` — 病历正文(Mongo 平铺;一张表喂诊断/治疗/建议/复查/病历全链)

**推送范围:`status ∈ {3, 4}`(已完成/归档);`status=2` 未完成草稿不推。**

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `emr_sub_id` | string | ✅ | 小病历号 uuid(病历主键;临床事实同诊聚合锚) |
| `emr_id` | string | ✅ | 病历号(如 `JY0A000231`;影像 `med_check.emr_id` 指向它) |
| `appointment_id` | string | | 关联预约 id(有则挂回预约链) |
| `tenant_id` | string | ✅ | 品牌 GUID(宿主原生 tenantId) |
| `organization_id` | string | ✅ | 诊所 |
| `organization_name` | string | | 诊所名称 —— **诊所展示名的主要来源**(PAC 据此派生 id→名字典;缺失则前端显示 GUID) |
| `patient_id` | string | ✅ | → 患者 id |
| `user_id` / `user_name` | string | | 接诊医生 id / 姓名 |
| `clinic_time` | string(datetime) | ✅ | 就诊时刻 |
| `visit_indicator` | number | | `1`初诊 `2`复诊 |
| `status` | number | ✅ | `3`已完成 `4`归档(推送范围) |
| `diag` | array | | 诊断数组,元素 `{ "toothPosition": "41;42", "value": "慢性牙髓炎", "linkCode": "<std_diag.diag_code 或空>" }` |
| `treat` | array | | 本次治疗数组,元素同上结构(`value`=治疗名) |
| `dispose` | array | | 处置叙述数组,元素 `{ "toothPosition", "value" }` |
| `examine` | array | | 检查所见数组,元素同上 |
| `dispose_text` / `examine_text` | string | | 上两数组的 JSON 字符串形态(病历自由文本位;数组非空时必带) |
| `illness_desc` | string | | 主诉 |
| `pre_illness` / `past_hist` / `gen_cond` | string | | 现病史 / 既往史 / 全身情况 |
| `auxi_exam` | string | | 辅助检查 |
| `doc_order` | string | | 医嘱 |
| `created_gmt_at` / `updated_gmt_at` | string(datetime) | ✅ | |

> 牙位格式:FDI 牙位号,多牙分号分隔,可带牙面字母(`"42 L;43 D"` / `"11;12;13"`)。

### `std_diag` — 诊断字典(低频;变更时全量重推)

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `diag_code` | string | ✅ | 字典主键(= `diag[].linkCode` 指向) |
| `diag_name` | string | ✅ | 诊断名 |
| `std_code` | string | | 标准码(`K02.901` 等;K 开头 PAC 截 K 大类,非 K 按自由文本处理) |

### `med_check` — 影像档案(metadata,不含文件本体)

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `id` | string | ✅ | 行主键 |
| `tenant_id` | string | ✅ | 品牌 GUID |
| `organization_id` | string | ✅ | 诊所 |
| `organization_name` | string | | 诊所名称(同上,诊所名字典来源之一) |
| `patient_id` | string | ✅ | → 患者 id |
| `emr_id` | string | | → 病历号(`med_emr_info.emr_id`) |
| `class_code` | string | | 影像类型 → `std_check_class` |
| `file_name` / `file_type` | string | | 文件名 / 类型 |
| `file_url` | string | | OSS 相对路径 |
| `files_size` | number | | 字节数 |
| `shooting_time` | string(datetime) | ✅ | 拍摄时间 |
| `created_gmt_at` / `updated_gmt_at` | string(datetime) | ✅ | |

配套字典 **`std_check_class`**(低频):`class_code`(主键) / `class_name`(口内像照片、小牙片、全景片…) / `tenant_id`。

---

## 5. 结算链(4 个 source)

> **拆分规则(宿主侧按 status/金额分流成两个 source 推)**。`status` 官方语义
> (`PatientSettlementEntity` 实体注释,比枚举类更全):`0`未结算 `1`已结算 `2`只生成uuid
> `3`退款 `4`插入的退款数据 `5`重新结算数据(废弃历史行) `6`医生未提交 `7`流程结束
> `8`欠款补缴插入数据。**0/2/5/6/7/8 均不推**——其中 `8` 是原单克隆(receivable_this
> 原样复制,`payment_arrears`=本次补缴额,ref 挂原欠费单),入消费会双计应收;`7` 零金额流程关单。

### `patient_settlement` — 消费单

**推送范围:`status ∈ {1, 3}` 且 `receivable_this >= 0`。**

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `uuid` | string | ✅ | 结算单主键 |
| `tenant_id` | string | ✅ | 品牌 GUID |
| `organization_id` | string | ✅ | 诊所 |
| `patient_id` | string | ✅ | → 患者 id |
| `doctor_id` | string | | 开单医生 |
| `status` | string/number | ✅ | 见上 |
| `receivable_this` | number(元) | ✅ | 应收(PAC 计患者价值/LTV 用) |
| `net_receipts_this` | number(元) | | 实收 |
| `billing_date` | string(datetime) | | 开单时间 |
| `registration_id` | string | | 关联接诊号 |
| `ref_settlement_id` | string | | (消费单一般为空) |
| `settlement_serial_num` | string | | 结算流水号 |
| `reason` | string | | 备注/原因 |
| `created_gmt_at` / `updated_gmt_at` | string(datetime) | ✅ | |

### `patient_settlement_refund` — 整单退费

**推送范围:`status = 4` 或(`status = 3` 且 `receivable_this < 0`)。** 字段同 `patient_settlement`;其中:

- `net_receipts_this` 负值 = 冲减金额(PAC 取绝对值)
- `ref_settlement_id` = **被退的原结算单 uuid**(status=4 必带;PAC 据此把退费挂回原消费)

### `patient_settlement_spec_refund` — 行级部分退费

**推送范围:结算明细行中 `is_refund = 1` 的行。**

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `id` | string | ✅ | 明细行主键 |
| `tenant_id` / `organization_id` / `patient_id` | string | ✅ | 同上 |
| `settlement_id` | string | ✅ | 所属结算单 uuid(退费挂回原消费) |
| `cure_name` / `service_project_name` | string | | 被退项目名 |
| `receivable_this` / `net_receipts_this` | number(元) | ✅ | 被退金额(正值) |
| `is_refund` | string/number | ✅ | 恒为 `1` |
| `created_gmt_at` / `updated_gmt_at` | string(datetime) | ✅ | |

### `settlement_modes` — 支付通道明细(每单×通道一行)

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `id` | string | ✅ | 行主键 |
| `tenant_id` | string | ✅ | 品牌 GUID |
| `settlement_id` | string | ✅ | → 结算单 uuid |
| `modes_name` | string | ✅ | 通道名(现金支付/微信支付/刷卡支付/医保支付…原样即可) |
| `money` | number(元) | ✅ | 该通道金额(PAC 取金额最大者为主导通道) |
| `created_gmt_at` / `updated_gmt_at` | string(datetime) | | |

---

## 6. 治疗计划与咨询(3 个 source)

### `customer_treat_plan` — 治疗计划(头)

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `id` | string | ✅ | 计划主键 |
| `tenant_id` | string | ✅ | 品牌 GUID |
| `organization_id` | string | ✅ | 诊所(**行项靠头表拿诊所,缺失该行会被丢弃**) |
| `customer_id` | string | ✅ | → 患者 id |
| `plan_group_id` / `plan_group_num` | string | | 方案分组(多方案对比) |
| `plan_name` | string | | 方案名 |
| `desired_effect` / `emergency_urgency` | string/number | | 期望效果 / 紧迫度 |
| `expect_cost` | string | | 期望费用 |
| `created_gmt_at` / `updated_gmt_at` | string(datetime) | ✅ | |

### `customer_treat_plan_item` — 治疗计划(行)

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `id` | string | ✅ | 行主键 |
| `tenant_id` | string | ✅ | 品牌 GUID |
| `customer_id` | string | ✅ | → 患者 id |
| `treat_plan_id` | string | ✅ | → 计划头 id |
| `tooth_position` | string | | 牙位 |
| `mode_name` | string | ✅ | 术式名(PAC 翻 11 类治疗类别) |
| `charge_min` / `charge_max` | number(元) | | 价格区间 |
| `created_gmt_at` / `updated_gmt_at` | string(datetime) | ✅ | |

### `customer_consult` — 咨询

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `id` | string | ✅ | 咨询主键 |
| `tenant_id` | string | ✅ | 品牌 GUID |
| `organization_id` | string | ✅ | 诊所 |
| `customer_id` | string | ✅ | → 患者 id |
| `task_director_id` / `task_director` | string | | 跟进人 id / 姓名 |
| `treatment_plan` | string | | 咨询中聊到的治疗方案 |
| `preferential_way` | string | | 优惠方式 |
| `unsettled_reason` | string | | **未成交原因**(强召回信号) |
| `unsettled_reason_description` | string | | 未成交原因补充 |
| `other_conditions` | string | | 其他情况 |
| `potential_treatment` | string | | 患者意向(潜在治疗) |
| `data_sources` | string/number | | 数据来源标记 |
| `created_gmt_at` / `updated_gmt_at` | string(datetime) | ✅ | |

---

## 7. 语义澄清记录(2026-07,源码 + 数据双证)

以下曾是开放问题,现已定案 —— 推送方无需再确认,列此备查:

| 项 | 结论 | 依据 |
|---|---|---|
| 时间口径 | MySQL `datetime` = 北京墙钟(`_gmt_` 命名系惯例误导);**Mongo `Date` 是北京墙钟伪装成 UTC** | `clinicTime`/`createdGmtAt` 小时分布呈营业双峰;按真 UTC 解释则半数病历落深夜 |
| 金额单位 | 元(`decimal(9,2)`) | 洁治单价均值 ¥310、已结算客单均值 ¥1,730 |
| `contacts.relationship` | `PhoneRelationshipEnum` 1-9(见 §2) | customer 服务枚举类 |
| `referee_relationship` | `RecommendRelationshipEnum` 17 码,**语义 = 本行 customer 是 referee 的 X**(PAC 侧按逆关系映射) | 枚举类 `reverseValue()` + 双方年龄差数据 |
| `med_emr_info.treat` vs `dispose` | `treat` = 本次治疗(→ 结构化治疗事实);`dispose` = 处置叙述(→ 病历自由文本) | EMR 接口 DTO 字段注释 |
| `patient_settlement.status` 全 9 值 | `0`未结算 `1`已结算 `2`只生成uuid `3`含退费行 `4`整单反向冲减 `5`重新结算(废弃历史) `6`医生未提交 `7`流程结束 `8`欠款补缴克隆单 | 实体注释 + `savePatientSettlementQk()` 实现 |
| 为何不收 `7`/`8` | `8` 是原单克隆(`receivable_this` 原样复制、`payment_arrears` 才是本次补缴额),入消费会**双计应收**;`7` 为零金额流程关单 | 同上 |

### 仍需推送方留意(非阻塞)

| 项 | 说明 |
|---|---|
| `organization_name` 覆盖 | 目前仅病历/影像表带诊所名,**只覆盖有病历记录的诊所**;其余诊所前端回退显示 GUID。若贵方有组织树接口(如 `regional_nodes_structure`),提供 `诊所 id → 名称` 全量表可补齐 |
| 品牌名 | PAC 当前**不需要**品牌中文名(无展示位,内部按品牌 id 标识)。将来若有展示需求,再约定推品牌主档(`tenant_apply.tenant_info` 的 `tenant_id`/`name`)——贵方 `tenant_name` 未冗余进业务表,只能走主档 |
| 字典表推送频率 | `std_diag` / `std_check_class` 变更不频繁,按"变更时全量重推"即可,无需增量 |
| 测试环境数据特征 | 部分租户为开发沙盒(诊所名如"XX专用诊所勿动"),字典类映射(治疗类别关键词等)待**生产数据**回流后再校准一轮 |
