From 025ee7ffa66f9307c00afc56ca993655ecc8835e Mon Sep 17 00:00:00 2001 From: jiashumeng <1051000378@qq.com> Date: Tue, 15 Sep 2026 20:18:43 +0930 Subject: [PATCH] feat(sync): expose contract display context --- .../doc/crm-integration-guide.md | 1 + .../doc/crm-sync-integration-progress.md | 16 ++++++++-------- .../iiv/agent/mcp/sync/ContractsSyncEntity.java | 2 ++ .../me/iiv/agent/mcp/sync/SyncChangeSchemas.java | 4 ++++ .../java/me/iiv/agent/mcp/tool/ChangesTools.java | 3 ++- .../me/iiv/agent/mcp/sync/SyncEntitiesTest.java | 8 ++++++++ 6 files changed, 25 insertions(+), 9 deletions(-) diff --git a/xiaoqu-agent-service/doc/crm-integration-guide.md b/xiaoqu-agent-service/doc/crm-integration-guide.md index b51e43c..2ba665d 100644 --- a/xiaoqu-agent-service/doc/crm-integration-guide.md +++ b/xiaoqu-agent-service/doc/crm-integration-guide.md @@ -100,6 +100,7 @@ arguments: {"contractIds":["1001","1002"]} - 每批 1—200 个稳定合同 ID,返回 `checked_at/count/items/not_found_ids`。 - `items` 使用与合同变化流相同的公开字段;软删合同返回删除标记,查无记录进入 `not_found_ids`。 +- 合同展示上下文包含`contract_no/contract_name/property_name/customer_no/customer_name/owner_name`;这些字段用于页面、AI解释和人工核对,关联、授权和接收人选择只能使用稳定ID。 - CRM 必须逐项复核状态、到期日、负责人、客户归属、提醒配置和续约结果。任一变化、缺失、删除、响应不完整、证据时间异常或服务不可用,对应合同本次都不得发送。 - 该工具属于 SYNC 能力和限流档,调用方传 `tenant_id/tenantId/scope` 会被拒绝;数据域只能来自服务 Token。 diff --git a/xiaoqu-agent-service/doc/crm-sync-integration-progress.md b/xiaoqu-agent-service/doc/crm-sync-integration-progress.md index 42b5c2f..485bbdf 100644 --- a/xiaoqu-agent-service/doc/crm-sync-integration-progress.md +++ b/xiaoqu-agent-service/doc/crm-sync-integration-progress.md @@ -1,6 +1,6 @@ # SaaS MCP → 小趣 AI 经营系统对接进度 -> MCP 系统独立进度入口。最后核对:2026-09-15;代码位于`codex/crm-contract-sync-review`本地评审分支。 +> MCP 系统独立进度入口。最后核对:2026-09-15;当前字段扩展位于`codex/mcp-source-domain-identity`分支。 > > 本文只记录 `xiaoqu_mcp_service` 的责任和状态。小趣 AI / CRM 的规则、事项、AI、页面与推送进度,以其仓库 `PROJECT.md` 为准。 @@ -15,9 +15,9 @@ ## 2. 当前结论 -当前状态:**协议可连接,公开字段Schema已在本地修正并通过回归;可靠同步与服务身份仍未通过,不能用于真实销售合同到期推送。** +当前状态:**协议可连接,基础公开字段Schema已部署;合同展示上下文字段已完成代码和回归、待合并部署;可靠同步与服务身份仍未通过,不能用于真实销售合同到期推送。** -交付状态:修正位于独立分支`codex/crm-contract-sync-review`,本地提交已形成;当前执行环境没有`wangheng/xiaoqu_mcp_service`写权限,尚未推送、合并或部署,生产服务仍是旧行为。 +交付状态:三条变化流和发送前复核代码已经合入主线并部署;生产`sync`作用域尚未绑定`contracts.current.batch_get`。本轮新增的`property_name/customer_no/customer_name/owner_name`仍待合并部署。 已经具备: @@ -58,9 +58,9 @@ |---|---|---| | users | `mobile`;数值型`user_id/organization_ids` | `verified_mobile`;字符串型稳定ID;状态使用公开枚举 | | properties | 数值型`property_id` | 字符串型稳定ID;状态使用公开枚举 | -| contracts | 数值型`contract_id/property_id/owner_user_id`、数值状态`0..4`、数值`expire_remind`,缺`remind_days` | 字符串型稳定ID、`PENDING/REJECTED/ACTIVE/EXPIRED/TERMINATED/VOID`、布尔提醒标记、实际提醒天数 | +| contracts | 数值型`contract_id/property_id/owner_user_id`、数值状态`0..4`、数值`expire_remind`,缺`remind_days`及客户/物业/负责人展示上下文 | 字符串型稳定ID、`PENDING/REJECTED/ACTIVE/EXPIRED/TERMINATED/VOID`、布尔提醒标记、实际提醒天数,以及`property_name/customer_no/customer_name/owner_name`展示字段 | -已完成:新增独立公开DTO,将三类稳定ID统一为字符串;`mobile`改为`verified_mobile`;合同状态转换为公开枚举;`expire_remind`转换为布尔值;补齐`remind_days`;变化页使用统一`stream`字段。未知合同状态按`DATA_INTEGRITY_ERROR`失败关闭,不伪造业务语义。 +已完成:新增独立公开DTO,将三类稳定ID统一为字符串;`mobile`改为`verified_mobile`;合同状态转换为公开枚举;`expire_remind`转换为布尔值;补齐`remind_days`;变化页使用统一`stream`字段。合同DTO继续补齐`property_name/customer_no/customer_name/owner_name`,这些字段只用于展示、AI解释和人工核对,身份关联与权限仍只认稳定ID。未知合同状态按`DATA_INTEGRITY_ERROR`失败关闭,不伪造业务语义。 验证:合并最新主分支后,Java编译、管理前端构建及MCP服务全量`229`项测试通过,`0`失败、`0`错误。尚需部署后的真实数据样例和小趣Gateway严格消费者契约联调,完成前仍不算生产通过。 @@ -102,7 +102,7 @@ 小趣AI侧已经按同一契约实现短时发送凭证:只有状态、到期日、负责人、客户归属、提醒字段及续约结果与本地候选一致才允许R04进入推送;缺失、删除、字段变化、响应不完整或MCP不可用均失败关闭,R03不受影响。 -尚未完成:本分支未推送和部署;生产`sync`作用域还必须绑定该新工具;需要用真实合同验证负责人变更、删除、到期日修改和服务不可用四类反向场景。 +尚未完成:生产`sync`作用域还必须绑定该新工具;需要用真实合同验证负责人变更、删除、到期日修改和服务不可用四类反向场景。 ## 4. 依赖顺序 @@ -121,8 +121,8 @@ | MCP协议与工具发现 | 已完成 | MCP | 保持兼容并纳入回归 | | 服务密钥与Token基础生命周期 | 已完成 | MCP | 在租户模型完成后升级幂等键 | | 源数据域标识与服务主体隔离 | 未完成,P0阻塞 | MCP | 增加稳定部署数据域标识和Open Client关联 | -| 三流公开字段Schema | 本地代码完成,待部署联调 | MCP | 部署后用真实三流样例跑Gateway严格契约测试 | -| 发送前合同当前状态批量复核 | 本地代码完成,待部署联调 | MCP + 小趣AI | 绑定sync作用域并完成四类真实反向用例 | +| 三流公开字段Schema | 基础字段已部署;合同展示字段待合并部署 | MCP | 部署后用真实三流样例跑Gateway严格契约测试 | +| 发送前合同当前状态批量复核 | 代码已部署、生产scope未开放 | MCP + 小趣AI | 绑定sync作用域并完成四类真实反向用例 | | 可靠变化日志与稳定快照 | 未完成,P0阻塞 | MCP | 设计Outbox/CDC及保留策略 | | 权限版本与交接位点生命周期 | 未完成,P0阻塞 | MCP | 与租户/变化日志模型一起实现 | | 小趣AI连接与消费代码 | 已完成,待部署联调 | 小趣AI | 见小趣AI仓库`PROJECT.md` | diff --git a/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/sync/ContractsSyncEntity.java b/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/sync/ContractsSyncEntity.java index c1b6920..65e5d8d 100644 --- a/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/sync/ContractsSyncEntity.java +++ b/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/sync/ContractsSyncEntity.java @@ -114,10 +114,12 @@ public class ContractsSyncEntity extends AbstractSyncEntity implements SyncEntit Date effective = row.getModifyTime() != null ? row.getModifyTime() : row.getCreateTime(); return SyncChangeSchemas.toMap(new SyncChangeSchemas.ContractChange( stableId(row.getId()), row.getContractNo(), row.getContractName(), + row.getPropertyName(), row.getCustomerNo(), row.getCustomerName(), publicStatus(row.getContractStatus()), ChangesEngine.iso(row.getStartDate()), ChangesEngine.iso(row.getEndDate()), toMinor(row.getAmount()), row.getPropertyId() == null ? null : customerIds.get(row.getPropertyId()), stableId(row.getPropertyId()), stableId(row.getSaleId()), + row.getSaleName(), Integer.valueOf(1).equals(row.getExpireRemind()), row.getRemindDays(), ChangesEngine.iso(effective), mt, deleted ? ChangesEngine.iso(effective) : null)); } diff --git a/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/sync/SyncChangeSchemas.java b/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/sync/SyncChangeSchemas.java index 62235de..c68708d 100644 --- a/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/sync/SyncChangeSchemas.java +++ b/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/sync/SyncChangeSchemas.java @@ -57,6 +57,9 @@ final class SyncChangeSchemas { @JsonProperty("contract_id") String contractId, @JsonProperty("contract_no") String contractNo, @JsonProperty("contract_name") String contractName, + @JsonProperty("property_name") String propertyName, + @JsonProperty("customer_no") String customerNo, + @JsonProperty("customer_name") String customerName, String status, @JsonProperty("start_at") String startAt, @JsonProperty("end_at") String endAt, @@ -64,6 +67,7 @@ final class SyncChangeSchemas { @JsonProperty("customer_id") String customerId, @JsonProperty("property_id") String propertyId, @JsonProperty("owner_user_id") String ownerUserId, + @JsonProperty("owner_name") String ownerName, @JsonProperty("expire_remind") boolean expireRemind, @JsonProperty("remind_days") Integer remindDays, @JsonProperty("updated_at") String updatedAt, diff --git a/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/tool/ChangesTools.java b/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/tool/ChangesTools.java index 7028b36..d4657e1 100644 --- a/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/tool/ChangesTools.java +++ b/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/tool/ChangesTools.java @@ -79,7 +79,8 @@ public class ChangesTools implements ToolObject { @Tool(name = "contracts.changes.list", description = "增量同步合同主数据(m2.contract_new)。" + PROTOCOL_DESC + "items 字段:contract_id/contract_no/contract_name/status(PENDING/REJECTED/ACTIVE/EXPIRED/TERMINATED)/" + "start_at/end_at(带时区)/amount_minor(金额,分)/customer_id/property_id/" - + "owner_user_id(负责人稳定 user.id)/expire_remind/remind_days/updated_at/version/deleted_at。" + + "property_name/customer_no/customer_name/owner_user_id(负责人稳定 user.id)/owner_name/" + + "expire_remind/remind_days/updated_at/version/deleted_at。" + "行级权限跟随 Token 绑定的人(与 contract.list.query 同一 ACL)。") public Map contractsChangesList( @ToolParam(required = false, description = SYNC_TOKEN_DESC) String syncToken, diff --git a/xiaoqu-agent-service/src/test/java/me/iiv/agent/mcp/sync/SyncEntitiesTest.java b/xiaoqu-agent-service/src/test/java/me/iiv/agent/mcp/sync/SyncEntitiesTest.java index a044612..8e14d4c 100644 --- a/xiaoqu-agent-service/src/test/java/me/iiv/agent/mcp/sync/SyncEntitiesTest.java +++ b/xiaoqu-agent-service/src/test/java/me/iiv/agent/mcp/sync/SyncEntitiesTest.java @@ -182,9 +182,13 @@ class SyncEntitiesTest { contract.setId(9L); contract.setContractNo("452542C001"); contract.setContractName("望京SOHO厕纸服务合同"); + contract.setPropertyName("望京SOHO"); + contract.setCustomerNo("452542"); + contract.setCustomerName("金蝶客户名称"); contract.setContractStatus(2); contract.setPropertyId(1L); contract.setSaleId(1001L); + contract.setSaleName("张销售"); contract.setExpireRemind(1); contract.setRemindDays(90); contract.setAmount(new BigDecimal("120000.00")); @@ -205,6 +209,9 @@ class SyncEntitiesTest { verify(permissionService).applyContractRowFilter(any(), any()); assertEquals("9", rows.get(0).item().get("contract_id")); assertEquals("452542C001", rows.get(0).item().get("contract_no")); + assertEquals("望京SOHO", rows.get(0).item().get("property_name")); + assertEquals("452542", rows.get(0).item().get("customer_no")); + assertEquals("金蝶客户名称", rows.get(0).item().get("customer_name")); assertEquals("ACTIVE", rows.get(0).item().get("status")); assertEquals("2026-01-01T00:00:00+08:00", rows.get(0).item().get("start_at")); assertEquals("2026-12-31T00:00:00+08:00", rows.get(0).item().get("end_at")); @@ -212,6 +219,7 @@ class SyncEntitiesTest { assertEquals("KD10086", rows.get(0).item().get("customer_id")); assertEquals("1", rows.get(0).item().get("property_id")); assertEquals("1001", rows.get(0).item().get("owner_user_id"), "负责人=稳定 user.id"); + assertEquals("张销售", rows.get(0).item().get("owner_name")); assertEquals(true, rows.get(0).item().get("expire_remind")); assertEquals(90, rows.get(0).item().get("remind_days")); assertEquals(Timestamp.valueOf("2026-09-01 13:54:21").getTime() / 1000,