From d9f94bb1614a11f5fb2b81dd02b8598341afb755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=B3=89=E4=BD=B3?= <1107186916@qq.com> Date: Fri, 18 Sep 2026 20:01:36 +0800 Subject: [PATCH] =?UTF-8?q?:memo:=20=E6=96=B0=E5=A2=9E=E5=AE=A2=E5=95=86?= =?UTF-8?q?=E6=A1=A3=E6=A1=88=E5=90=8C=E6=AD=A5=E9=87=91=E8=9D=B6=E6=8A=80?= =?UTF-8?q?=E6=9C=AF=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...Œ步金蝶(A902-1查询回填+A902-2审批推送)技术文档.md | 311 ++++++++++++++++++ 1 file changed, 311 insertions(+) create mode 100644 doc/docs/业务流程/2026-09-17-客商档案同步金蝶(A902-1查询回填+A902-2审批推送)技术文档.md diff --git a/doc/docs/业务流程/2026-09-17-客商档案同步金蝶(A902-1查询回填+A902-2审批推送)技术文档.md b/doc/docs/业务流程/2026-09-17-客商档案同步金蝶(A902-1查询回填+A902-2审批推送)技术文档.md new file mode 100644 index 00000000..46a2093a --- /dev/null +++ b/doc/docs/业务流程/2026-09-17-客商档案同步金蝶(A902-1查询回填+A902-2审批推送)技术文档.md @@ -0,0 +1,311 @@ +# 客商档案同步金蝶(星瀚)技术文档:A902-1 查询回填 + A902-2 审批推送 + +> 适用系统:TMS(BladeX 微服务平台,服务:blade-transport)、前端 Saber3(页面:客商档案) +> 对接系统:金蝶云·星瀚(业财融合 BFI,经 iPaaS 网关) +> 关联文档:`doc/docs/TMS业财融合-集成流程图文字版与业务流程梳理.md`(图 01 客户新增与查询)、`doc/docs/TMS业财融合方案.docx` +> 最后更新:2026-09-17 + +--- + +## 1. 背景与范围 + +按业财融合「SC-01 客商主数据协同」流程,TMS 新增客商时须先查询金蝶是否已有该客商(A902-1),已有则回填金蝶维护的工商信息;TMS 审批通过后,将金蝶不存在的客商推送至金蝶完成创建(A902-2),金蝶校验工商信息后由机器人自动审核,业务系统方可使用该客商。 + +**现状结论**:现有客商新增交互(前端 `tms-erp-web/src/views/vehicle/customer-archive.vue`、后端 `CustomerArchiveServiceImpl` 的 `submit → submitApproval → approve/reject` 链路)**均无金蝶调用**;blade-transport 仅暴露汇率查询 `BfiExchangeRateController`。底层能力已就绪:`blade-bfi-api` 的 `IBfiService.queryCustomer`(A902-1)、`saveCustomer`(A902-2)及配套 DTO/VO/Feign。 + +**本期范围**: + +- 新增客商时按统一社会信用代码查询金蝶并自动回填(A902-1)。 +- 客商准入审批通过后自动推送金蝶(A902-2),失败可重推。 +- 金蝶已存在(已审核)的客商:回填建档,审批通过后**跳过推送**。 + +**不在本期范围**: + +- 客商更新(工商/个人信息变更走人工流程,非接口)。 +- 客商数据定时同步子流程(A902-1 / A901-1 定时任务)。 +- 合同合规系统相对方同步。 + +**关键约定**(已与业务确认): + +| 约定 | 决策 | +| --- | --- | +| A902-1 触发时机 | 统一社会信用代码输入满 18 位且通过本地格式校验后自动查询(A902-1 按信用代码查询,名称/法人为表单必填项但不参与查询) | +| A902-2 触发时机 | 审批通过(`approve`)后自动推送,推送失败不影响审批结果,支持列表重推 | +| 金蝶已存在(已审核) | 回填建档,审批通过后跳过 A902-2 推送 | + +--- + +## 2. 业务流程 + +### 2.1 业财融合整体链路(SC-01 客商主数据协同) + +``` +TMS 新增客商,录入统一社会信用代码 + → A902-1 客户查询(本文档 3.1) + ├─ 金蝶已审核(C):提示并回填金蝶工商信息(工商信息只允许金蝶维护,TMS 锁定不可改) + ├─ 金蝶暂存/已提交(A/B):提示"金蝶审核中,请稍后重新添加",流程终止 + └─ 金蝶不存在:正常填写(现有营业执照 OCR 逻辑不变) + → 填写客商业务信息 → 提交客商准入审批(TMS 内部审批,不调金蝶) + → 审批通过 → A902-2 客商保存推送金蝶(本文档 3.2,金蝶机器人自动审核) + → 审批通过前 A902-1 复查到金蝶已审核的 → 跳过推送,仅本地建档 + → 金蝶客户新增成功后自动下推供应商(金蝶侧行为,TMS 无感知) +``` + +### 2.2 客商档案金蝶协同状态(`kingdeeStatus`) + +| 值 | 含义 | 设置时机 | 后续动作 | +| --- | --- | --- | --- | +| `none` | 未推送 | 新增客商默认值 | 审批通过后参与推送 | +| `exist` | 金蝶已有档案 | A902-1 查到状态=已审核(C) | 审批通过后跳过推送(终态) | +| `synced` | 已推送成功 | A902-2 返回成功,回写金蝶客户编码 | 终态,重推时幂等跳过 | +| `failed` | 推送失败 / 金蝶审核中 | A902-2 失败,或复查到 A/B 状态 | 列表「同步金蝶」按钮重推 | + +### 2.3 前端查询回填时序(新增态) + +``` +customer-archive.vue blade-transport 星瀚(BFI) + │ 统一社会信用代码输入满18位 │ │ + │ 且通过格式校验(防抖500ms) │ │ + │ GET /bfi/customer/query-by-credit-code│ │ + │──────────────────────────────────────>│ A902-1:pageNo=1,pageSize=1, │ + │ │ societycreditcode=[code] ───────>│ + │ │ <── rows[](含status/工商信息) ──│ + │ <── R ────────────│ │ + │ status=C:提示"金蝶已创建该客户, │ │ + │ 将获取金蝶的工商信息",回填并锁定 │ │ + │ status=A/B:提示"金蝶审核中,请稍后重新添加",阻断保存 │ + │ 不存在:正常填写 │ │ +``` + +### 2.4 审批通过后推送时序 + +``` +CustomerArchiveController CustomerArchiveServiceImpl 星瀚(BFI) + │ POST /customer-archive/approve │ │ + │──────────────────────────────────────>│ approve() 落库审批通过 │ + │ <── R 成功 ────────────────────────────│ │ + │ POST /customer-archive/sync-kingdee │(审核通过后前端自动触发;失败亦可为 │ + │──────────────────────────────────────>│ 人工在列表重推) │ + │ │ 1. 校验 approvalStatus=approved │ + │ │ 2. kingdeeStatus∈{synced,exist}→幂等返回 │ + │ │ 3. A902-1 复查: │ + │ │ 已审核(C)→置exist,跳过推送 │ + │ │ 暂存/已提交(A/B)→置failed(金蝶审核中) │ + │ │ 4. 不存在→组装DTO推 A902-2 ─────────────>│ + │ │ <── result[]{billStatus,id/number} ─────│ + │ │ 5. 成功→kingdeeNumber+synced │ + │ │ 失败→failed+失败原因 │ + │ <── R<推送结果> ───────────────────────│ │ +``` + +> 实施说明:推送由后端 `/customer-archive/approve` 端点成功后**内部触发**(try-catch,推送失败不影响审批结果);`/sync-kingdee` 端点仅作为 `failed` 状态的人工重推入口,前端无需在审批成功后单独调用。 + +### 2.5 前置条件与幂等约定 + +| 约定 | 说明 | +| --- | --- | +| 推送前置状态 | 仅 `approvalStatus = approved` 允许推送金蝶 | +| 幂等跳过 | `kingdeeStatus ∈ {synced, exist}` 直接返回,不重复推送 | +| 防重键 | 星瀚侧以统一社会信用代码(`societycreditcode`)为候选键;TMS 侧 `validateUnique` 已保证信用代码本地唯一 | +| 失败重推 | `kingdeeStatus = failed` 时列表「同步金蝶」按钮可重推,权限码 `customer_archive_sync` | +| 推送失败不回滚审批 | 审核通过落库成功后推送;推送环节异常只置 `failed`,不影响审批结果 | +| 工商信息锁定 | 来源金蝶回填的工商字段 TMS 不允许变更,与「工商信息只在金蝶维护」规则一致 | + +--- + +## 3. 接口定义 + +### 3.1 A902-1 客户查询 + +- URL:`GET {thirdParty.bfi.baseUrl}/ierp/kapi/v2/awrp/basedata/bd_customer/query`(可用 `thirdParty.bfi.customerQueryUrl` 覆盖) +- 鉴权:`access_token` 由 `BfiServiceImpl` 自动获取与缓存(OAuth2,有效期 2 小时),业务代码无需关心 +- 本次调用参数:`pageNo=1`、`pageSize=1`、`societycreditcode=[统一社会信用代码]`;**不传 `status` 过滤**(需按返回状态区分三分支) +- 返回:`BfiPageVO`,取 `rows` 第一条;关键返回字段见 4.1 回填映射表 +- TMS 内部封装端点:`GET /blade-transport/bfi/customer/query-by-credit-code?creditCode={code}`,返回 `R`(无数据返回 `R.data(null)`) + +### 3.2 A902-2 客户保存 + +- URL:`POST {thirdParty.bfi.baseUrl}/ierp/kapi/v2/awrp/basedata/bd_customer/save`(可用 `thirdParty.bfi.customerSaveUrl` 覆盖) +- 请求体:`{"data": [ {…BfiCustomerSaveDTO 字段} ]}`(单条) +- 校验(`BfiServiceImpl.saveCustomer` 已内置):`name` 必填;`societycreditcode` 与 `idno` 不能同时为空;`entry_groupstandard` 必填 +- 返回结构: + +```json +{ + "data": { + "failCount": "0", + "successCount": "1", + "result": [ + { + "billIndex": 0, + "billStatus": true, + "errors": [], + "id": "2395327167211785216", + "number": "Cus-20260917-0001" + } + ] + }, + "errorCode": "0", + "status": true +} +``` + +- 成功判定:`errorCode=0` 且 `failCount=0` 且 `result[0].billStatus=true`;取 `number`(金蝶客户编码)回写 `kingdee_number`,取不到 `number` 时兜底取 `id` +- `status` 固定传 `B`(已提交),金蝶校验工商信息通过后由机器人自动审核为已审核 + +--- + +## 4. 字段映射与转换 + +### 4.1 A902-1 回填映射(金蝶 → TMS 表单,工商信息锁定) + +| TMS 表单字段 | 金蝶字段(BfiCustomerVO) | 转换/回填规则 | +| --- | --- | --- | +| unifiedCreditCode | societycreditcode | 即查询入参,不覆盖 | +| fullName(客商名称) | name | 直接回填,锁定 | +| legalPerson(企业法人) | artificialperson | 直接回填,锁定 | +| registeredRegionName / registeredDetailAddress | bizpartner_address | 文本回填至详细地址;省市区无法结构化解析时不回填,锁定已回填项 | +| businessScope(经营范围) | businessscope | 直接回填,锁定 | +| registeredCapital(注册资金,万元) | regcapital | 正则提取首个数值按「万元」处理(如 `1000万元人民币`→`1000`);无法解析不回填 | +| businessTermType / businessEndDate | businessterm | 含「长期/无固定」→ `长期`;提取文本中最后一个 `yyyy-MM-dd` 日期为截止日 → `固定期限`;均无法解析不回填 | +| shortName(客商简称) | simplename | 仅当表单为空时回填,**不锁定**(业务信息,TMS 可改) | +| —(回填来源标记) | status | `status=C` 时置 `kingdeeStatus=exist`,随表单提交落库;金蝶客户编码与 TMS 客商编号一致,无需单独存储 | + +**锁定字段集合**(`kingdeeStatus=exist` 时表单置灰):统一社会信用代码、客商名称、企业法人、注册地址(省市区+详细)、经营范围、注册资金、营业期限。客商分类、客商性质、所属组织、开票税点、信用额度、负责人、联系电话、客商材料等业务信息正常编辑。 + +TMS 无对应字段的金蝶返回项(注册资本原文、成立日期 establishdate、纳税人识别号等)不回填、不入库。 + +### 4.2 A902-2 推送转换表(TMS → 金蝶) + +| 金蝶字段 | 来源 | 转换规则 | +| --- | --- | --- | +| status | 固定 | `B` 已提交(常量 `BfiConstant.BILL_STATUS_SUBMITTED`) | +| name | fullName | 直传 | +| societycreditcode | unifiedCreditCode | 直传(必填,候选键) | +| tx_register_no | unifiedCreditCode | 与统一社会信用代码一致时推送 | +| artificialperson | legalPerson | 直传 | +| type(伙伴类型) | customerNature(客商性质) | **常量映射表**,见 4.3;映射缺失时兜底默认值 | +| bizpartner_address | registeredAddress | `prepare()` 已拼接的完整地址直传 | +| businessscope | businessScope | 多选编码 → 选项 label 文本、以「、」拼接 | +| businessterm | businessTermType + businessEndDate | `长期` → `长期`;`固定期限` → `至 yyyy-MM-dd`(TMS 未采集起始日) | +| bizpartner_phone | contactPhone | 直传 | +| fcusbeizhu | remark | 直传,空串传 `""` | +| bizfunction | 固定 | `1,2,3,4` | +| awrp_datasourcesys | 配置 | `@Value("${thirdParty.bfi.customer.srcSys:WLTMS}")`,默认 `WLTMS`(运输TMS) | +| awrp_cusfortrade | 固定 | `A` 是(`BfiConstant.CUS_FOR_TRADE_YES`) | +| entry_groupstandard | 固定 | `[{standardid_number: JBFLBZ, groupid_number: 1102}]`(外部客户) | +| createorg_number | 配置 | `@Value("${thirdParty.bfi.customer.createOrg:WLTMS}")`,与财务确认后可改 | +| number | customerCode | 传 TMS 客商编号,**金蝶客户编码与 TMS 客商编号保持一致**,无需本地冗余存储 | +| entry_bank / entry_linkman | 不传 | 本期不推银行与联系人分录 | +| 注册资金 regcapital | — | **A902-2 无此字段**,新客商推送不含;仅 A902-1 回填使用 | + +### 4.3 客商性质 → 金蝶伙伴类型映射(常量类) + +映射与全部固定值集中放在 `blade-transport` 常量类 `org.springblade.transport.constant.CustomerBfiConstants`(**不走 Nacos 配置**): + +```java +public class CustomerBfiConstants { + + /** 推送金蝶固定值:业务职能 全选(1销售,2结算,3付款,4收货) */ + public static final String BIZ_FUNCTION_ALL = "1,2,3,4"; + + /** 推送金蝶固定值:伙伴类型兜底编码(民营企业),客商性质未命中映射时使用 */ + public static final String DEFAULT_PARTNER_TYPE = "6"; + + /** + * 客商性质(nature_of_client 字典 dictKey)→ 金蝶伙伴类型编码 + * 键名以数据库字典 nature_of_client 实际 dictKey 为准,实施时核对;未命中走 DEFAULT_PARTNER_TYPE + */ + public static final Map NATURE_TYPE_MAPPING = Map.of( + "企业法人", "1", // 法人企业 + "个人", "4", // 个人 + "个体工商户", "5", // 个体户 + "民营企业", "6", // 民营企业 + "国有企业", "8", // 我区国有企业 + "政府事业单位", "9", // 政府事业单位等 + "其他", "12" // 其他 + ); +} +``` + +> 金蝶伙伴类型全集见 `BfiCustomerSaveDTO.type` 注释(1 法人企业 / 2 非法人企业 / 3 非企业单位 / 4 个人 / 5 个体户 / 6 民营企业 / 7 非我区国有企业 / 8 我区国有企业 / 9 政府事业单位等 / 10 农民专业合作社 / 11 民政部门 / 12 其他 / 13 境外客商 / …)。初始映射实施前需与财务核对一遍。 + +--- + +## 5. 改动清单 + +### 5.1 后端(tms-api,blade-transport) + +| 文件 | 动作 | 说明 | +| --- | --- | --- | +| `controller/BfiCustomerController.java` | 新增 | 仿 `BfiExchangeRateController`:`GET /bfi/customer/query-by-credit-code`,`pageNo=1/pageSize=1/societycreditcode=[code]` 调 `bfiService.queryCustomer`,返回 `R` | +| `constant/CustomerBfiConstants.java` | 新增 | 见 4.3 | +| `service/impl/CustomerBfiConverter.java` | 新增 | ① `buildSaveDTO(CustomerArchiveVO)`:按 4.2 组装 `BfiCustomerSaveDTO`;② `resolvePartnerType(customerNature)`;③ `buildBusinessterm(...)`、`parseRegcapital(...)` 等解析工具 | +| `pojo/entity/CustomerArchive.java`(blade-transport-api) | 修改 | 新增 `kingdeeNumber`、`kingdeeStatus`、`kingdeeFailReason`、`kingdeeSyncTime` 字段 | +| `pojo/vo/CustomerArchiveVO.java`、`excel/CustomerArchiveExcel.java` | 修改 | 同步新增字段(Excel 增加「金蝶客户编码」「金蝶同步状态」列) | +| `service/impl/CustomerArchiveServiceImpl.java` | 修改 | ① 新增 `syncKingdee(Long id)`:2.4 时序 1-5 步;② `approve()` 落库后**由 Controller 调用** `syncKingdee`(推送异常内部消化不影响审批);③ 复用 `addChangeRecord` 记录「推送金蝶成功/失败」 | +| `controller/CustomerArchiveController.java` | 修改 | ① `approve` 端点成功后调用 `syncKingdee`;② 新增 `POST /customer-archive/sync-kingdee`(`@PreAuth` 权限码 `customer_archive_sync`) | +| `wrapper/CustomerArchiveWrapper.java` | 无改动 | BeanUtil.copyProperties 自动携带新字段 | + +依赖说明:`blade-transport` 已依赖 `blade-bfi-api`(`BfiExchangeRateController`、`PaymentApplicationServiceImpl` 在用),无需新增 pom 依赖。 + +### 5.2 数据库(7 种数据库脚本同步,`doc/sql/`) + +```sql +ALTER TABLE blade_customer_archive ADD COLUMN kingdee_status varchar(20) NOT NULL DEFAULT 'none' COMMENT '金蝶协同状态:none未推送/exist金蝶已有/synced已推送/failed推送失败'; +ALTER TABLE blade_customer_archive ADD COLUMN kingdee_fail_reason varchar(500) DEFAULT NULL COMMENT '金蝶推送失败原因'; +ALTER TABLE blade_customer_archive ADD COLUMN kingdee_sync_time datetime DEFAULT NULL COMMENT '金蝶推送时间'; +CREATE INDEX idx_kingdee_status ON blade_customer_archive (kingdee_status); +``` + +> 库存量数据无需刷数:存量客商默认 `none`,仅在再次触发推送场景(重推按钮)才会与金蝶交互。 + +### 5.3 菜单按钮 SQL(权限码) + +```sql +-- parent_id 取「客商档案」菜单实际 id +INSERT INTO blade_menu (id, parent_id, code, name, alias, path, source, sort, category, action, is_open, component, remark, is_deleted) +VALUES (2075431800000000001, {客商档案菜单id}, 'customer_archive_sync', '同步金蝶', 'customer_archive_sync', '', '', 1, 2, 0, 1, '', '', 0); +``` + +### 5.4 前端(tms-erp-web) + +| 文件 | 动作 | 说明 | +| --- | --- | --- | +| `src/api/vehicle/customer-archive.js` | 修改 | 新增 `queryKingdeeCustomer(creditCode)`(GET `/blade-transport/bfi/customer/query-by-credit-code`)、`syncKingdee(id)`(POST `/blade-transport/customer-archive/sync-kingdee`) | +| `src/views/vehicle/customer-archive.vue` | 修改 | ① 新增态 watch `unifiedCreditCode`:满 18 位且通过现有格式校验(18 位字符 + 校验码)后防抖 500ms 自动查询;② 三分支处理(2.3);③ `kingdeeStatus==='exist'` 时锁定 4.1 字段集合(disabled);④ `normalizeArchive()` 携带 `kingdeeStatus/kingdeeNumber`;⑤ 列表新增「同步金蝶」按钮(仅 `approved + failed` 可点)与「金蝶客户编码」「金蝶同步状态」列;⑥ 编辑态不触发查询;⑦ 审批通过推送由后端 approve 端点内部触发,前端无需额外调用 | + +--- + +## 6. 异常处理 + +| 场景 | 处理 | +| --- | --- | +| 前端 A902-1 查询超时/异常 | `$message.warning('金蝶查询失败,可继续填写,审批通过后系统将自动复查')`,不阻断填写;后端推送前复查兜底 | +| A902-1 查到金蝶暂存/已提交 | 弹窗提示「金蝶审核中,请稍后重新添加」,前端阻断保存(流程终止,对齐流程图) | +| 推送时复查到金蝶已审核 | 置 `exist` + `kingdeeNumber`,跳过推送,返回「金蝶已有该客商档案,无需推送」 | +| 推送时复查到金蝶暂存/已提交 | 置 `failed`,`kingdee_fail_reason='金蝶侧该客商审核中,请稍后重推'` | +| A902-2 返回 `billStatus=false` / `failCount>0` | 置 `failed`,`errors` 拼接写入 `kingdee_fail_reason`(截断 500 字),日志 `@Slf4j` 占位符输出含客商 id 与信用代码 | +| A902-2 网关异常(超时/5xx/token 失效) | `BfiException` 统一上抛,同上置 `failed`;token 由 `BfiServiceImpl` 自动续期 | +| 重推仍失败 | 保持 `failed`,可继续人工重推;连续失败转人工核对金蝶侧该信用代码客商状态 | + +--- + +## 7. 实施核对清单 + +1. [ ] 与财务核对 4.3 客商性质初始映射(键 = `nature_of_client` 字典实际 value)与 `createorg_number` 取值。 +2. [ ] DDL 按 `doc/sql/` 各数据库方言同步(MySQL / PostgreSQL / Oracle / SQL Server / 达梦 / 人大金仓 / 崖山),排序规则 `utf8mb4_general_ci`。 +3. [ ] 前端锁定字段集合与 4.1 一致;`exist` 状态下后端不额外强校验锁定字段是否被改(前端已置灰,以提交值为准,工商信息实际以金蝶为准)。 +4. [ ] 「同步金蝶」按钮权限码 `customer_archive_sync` 与菜单 SQL 一致。 +5. [ ] A902-1/A902-2 联调环境验证:三分支提示文案、回填解析(regcapital/businessterm)、A902-2 成功回写编码。 +6. [ ] 回归:现有新增/编辑/审批/导入导出不受影响(`kingdeeStatus` 默认 `none` 不参与既有校验)。 + +--- + +## 8. 实施记录 + +- 2026-09-17:按本文档完成后端(`CustomerArchive` 实体/Excel/Mapper 扩展、`CustomerBfiConstants`、`CustomerBfiConverter`、`CustomerArchiveServiceImpl.syncKingdee`、`BfiCustomerController`、`CustomerArchiveController` approve 挂推送 + `/sync-kingdee`)与前端(`customer-archive.js` 两个 API、`customer-archive.vue` 查询回填/锁定/阻断、列表「同步金蝶」按钮与「金蝶同步状态」列);`mvn clean compile -DskipTests` 与 `pnpm run build` 均通过。 +- 2026-09-17(第二轮调整): + - **取消 `kingdee_number` 字段**:A902-2 `number` 直接传 TMS 客商编号,金蝶客户编码与客商编号保持一致;DDL 缩减为 3 字段,前端移除「金蝶客户编码」列及相关引用。 + - **客商数据定时同步子流程本期不做**:经确认 ERP/TMS 无此既有逻辑,且金蝶 SDK(blade-bfi-api)暂未提供 A901-1 供应商查询;该子流程不阻塞核心链路,待 A901-1 能力补齐后按 §2.3 步骤9(ERP 定时调 A902-1/A901-1 更新客商数据与使用状态)另行立项。推送失败(`failed`)由列表「同步金蝶」按钮人工重推。 +- 待人工执行:`doc/sql/transport/blade_customer_archive_kingdee_20260917.sql`(含「同步金蝶」按钮菜单 INSERT);与财务核对客商性质映射键与 `createorg_number` 取值。