---
title: FRIDAY 推送数据契约
description: FRIDAY SaaS 按形态 A 推送的 14 个 source 及字段定义;字段形状与已验证的存量导出一致(宿主推原表,消费/退费等 WHERE 全在 PAC 侧切分)。
icon: FileJson
---

> 本文是 [Push 通道](/docs/integration/channel-push)**形态 A** 在 FRIDAY SaaS 宿主的具体化:每个 `source` 的 JSON 字段清单。
> 字段形状与 PAC 已完成的存量摄入**完全一致**——宿主按此推送,与存量自动衔接:
> 同行按幂等键去重,行更新(`updated_gmt_at` 变)自动版本演进。**重叠无害,宁多勿漏**。
> 时间字段的可空语义与 `created_gmt_at` 兜底见 §1 通用约定。

---

## 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`** | **可空,缺失不影响变更捕获**。PAC 的变更识别是**内容驱动**的:按 `(subjectId + 内容 hash + 状态 + 时间锚)` 判定,不看 `updated_gmt_at`——同一 `subjectId` 内容变了就升版本,没变就幂等去重。所以只要 `subjectId` 稳定、内容如实推,时间字段缺不缺都不丢更新。`updated_gmt_at` 仅用于**乱序防护**(补推的旧快照晚到时不覆盖新版本);为 null 时该保护退回"按到达顺序",对正常单向推送无影响。**无需宿主侧 COALESCE 兜底,重推安全。** |
| **空值** | 传 `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. 结算链(3 个 source)

> **结算原表照抄推送,宿主不做任何 status/金额过滤或分流**——消费 / 整单退费 / 行级退费明细的
> 切分全部由 PAC transforms 完成(单一真理源,与 file / cold-import 同口径)。宿主只推 3 张原表:
> `patient_settlement`(结算头,全 status)、`patient_settlement_spec`(结算明细,全量)、`settlement_modes`。
>
> `status` 全值照推,PAC 侧识别(`PatientSettlementEntity` 实体注释):`0`未结算 `1`已结算
> `2`只生成uuid `3`含退费行 `4`整单反向冲减 `5`重新结算(废弃历史) `6`医生未提交 `7`流程结束
> `8`欠款补缴克隆单。PAC 取:**消费**=`status∈{1,3}` 且 `receivable_this≥0`;**整单退费**=`status=4`
> 或(`status=3` 且 `receivable_this<0`);**退费明细**=`patient_settlement_spec.is_refund=1`。
> 其余(`0/2/5/6/7/8`)PAC 丢弃(`8` 原单克隆入消费会双计应收、`7` 零金额流程关单)。

### `patient_settlement` — 结算头原表(全 status 照推,含消费与整单退费)

**推送范围:全部 `status` 照推**(含退费的 `status=4`、`receivable_this` 负值行);消费/整单退费的切分由 PAC 完成。

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `uuid` | string | ✅ | 结算单主键 |
| `tenant_id` | string | ✅ | 品牌 GUID |
| `organization_id` | string | ✅ | 诊所 |
| `patient_id` | string | ✅ | → 患者 id |
| `doctor_id` | string | | 开单医生 |
| `status` | string/number | ✅ | 全值照推(见 §5 引言;PAC 侧切分) |
| `receivable_this` | number(元) | ✅ | 应收(PAC 计患者价值/LTV 用);**可为负 = 退费表达**(status=4 或部分品牌 status=3 负额) |
| `net_receipts_this` | number(元) | | 实收(退费行为负,PAC 取绝对值) |
| `billing_date` | string(datetime) | | 开单时间 |
| `registration_id` | string | | 关联接诊号 |
| `ref_settlement_id` | string | | **退费行(status=4)= 被退的原结算单 uuid**(PAC 据此把退费挂回原消费);消费行一般空 |
| `settlement_serial_num` | string | | 结算流水号 |
| `reason` | string | | 备注/原因 |
| `created_gmt_at` / `updated_gmt_at` | string(datetime) | ✅ | |

### `patient_settlement_spec` — 结算明细原表(全量照推,含行级退费)

**推送范围:结算明细全量照推**(含 `is_refund` 各值);行级退费(`is_refund=1`)由 PAC 侧切分。

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `id` | string | ✅ | 明细行主键 |
| `tenant_id` / `organization_id` | string | ✅ | 品牌 / 诊所 |
| `settlement_id` | string | ✅ | 所属结算单 uuid(退费挂回原消费;**也是 PAC 认患者的依据**) |
| `patient_id` | string | | ⚠️ **部分品牌此列为诊所本地 id、不可信**(实测元和王永品牌全列与结算头不符);PAC 不用它认患者,而是按 `settlement_id` 从 `patient_settlement` 头单继承真实 `patient_id` |
| `cure_name` / `service_project_name` | string | | 项目名(退费行=被退项目) |
| `receivable_this` / `net_receipts_this` | number(元) | | 金额(`is_refund=1` 时为被退金额,正值) |
| `is_refund` | string/number | ✅ | `1`=退费行(PAC 只取此值切退费明细) `0`=正常明细 `2`=其他 |
| `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专用诊所勿动"),字典类映射(治疗类别关键词等)待**生产数据**回流后再校准一轮 |
