Files
ai-contract-platform/project-docs/V2-P1-04.8.1-domestic-enterprise-calendar-dingtalk-wecom-2026-08-24.md
yuqei 1d85329a6a
Build and Test Quality Gate / Contract Web typecheck and build (pull_request) Successful in 9m12s
Build and Test Quality Gate / Isolated Alibaba and Tencent KMS gateway (pull_request) Failing after 4m53s
Build and Test Quality Gate / Python tests and Contract API migration (pull_request) Failing after 24m55s
Security Quality Gate / npm audit, Bandit, pip-audit, detect-secrets (pull_request) Failing after 8m42s
feat: deliver governed contract platform and async AI tasks
2026-08-26 12:53:47 +08:00

10 KiB
Raw Permalink Blame History

P1-04.8.1 国内企业日历:钉钉与企业微信交付报告

日期:2026-08-24
状态:已实现、已迁移、已完成自动化与 Chrome 登录态 UAT;真实厂商凭据联调待预发布环境完成

1. 阶段结论

在主要面向中国大陆企业的合同治理产品中,只提供 Google Calendar 和 Microsoft 365 会形成明显的采购与可用性缺口。P1-04.8.1 已增加两类国内企业日历源:

  • dingtalk:钉钉企业内部应用,使用 AppKey/AppSecret 获取短期访问令牌,以用户 UnionID 和 Calendar ID 查询日程,并保存厂商 syncToken 做增量同步;
  • wecom:企业微信自建应用,使用 CorpID/CorpSecret 获取短期访问令牌,以 cal_id 查询日程。由于该接口没有等价增量游标,平台执行完整快照、生成稳定摘要并在本地计算差异。

两者均接入现有租户 SLA 工作日历、同步运行证据、定时治理 Worker、失败退避和租户隔离体系。未引入一套平行的“国内版日历子系统”,因此产品操作模型和运维边界保持一致。

2. 产品流程与体验

管理员在“治理配置 → 企业同步日历”中可以选择:日期清单、ICS、钉钉、企业微信、Google 或 Microsoft。

2.1 钉钉

表单要求:日历名称、Calendar ID、日历所属用户 UnionID、AppKey、AppSecret、手动/定时方式、间隔和是否启用增量游标。

创建流程:

  1. 先创建状态为 pending 的租户日历源;
  2. 服务端向钉钉固定令牌端点校验 AppKey/AppSecret,失败时不保存凭据;
  3. 校验成功后加密保存 AppSecret、access token,并将连接状态改为 connected;
  4. 初次同步按时间窗口建立全量快照,后续携带加密保存的 syncToken;
  5. 厂商返回游标失效类 400/410 时清除旧游标并重建全量快照,形成新的运行证据;
  6. 撤销连接会停用源并清除凭据、令牌、游标和当前物化事件,历史同步运行证据仍保留。

2.2 企业微信

表单要求:日历名称、cal_id、CorpID、CorpSecret、手动/定时方式和间隔。页面明确显示“完整日程快照 + 平台本地差异”,不展示增量游标开关,定时选项使用“定时同步”而不是“定时增量”。

该接口只允许读取由当前自建应用创建的日历,不能读取企业微信中的任意个人/共享日历;界面已明确提示这一产品限制。服务端分页拉取日程,以空 schedule_list 作为结束条件,排除 status 非正常的取消日程,只将正常的全天事件纳入 SLA 节假日物化,并按事件 ID、日期、标题和删除状态生成 SHA-256 快照摘要。每轮是 full 运行,但物化层仍计算新增、删除和未变化数量,因此不会把供应商不提供的能力包装成伪增量。

2.3 失败恢复

凭据校验失败时,已创建源保持 pending,不会假装连接成功,也不会静默删除管理员刚填写的源。管理员修正后可使用卡片上的“使用下方凭据重连”;按钮只有在下方选择同一提供方且凭据完整时才启用,防止钉钉/企微凭据误投。

3. 架构与安全实现

3.1 适配器边界

新增国内连接器模块,复用现有 CalendarIncrementalResult、远程事件物化和治理 Worker。所有外部 URL 固定在代码中:

  • 钉钉:api.dingtalk.com;
  • 企业微信:qyapi.weixin.qq.com。

用户不能配置令牌端点或 API 主机;HTTP 重定向关闭,单页和总分页数设置安全上限,外部错误转换为有限长度的业务错误,不回传请求体或密钥。

3.2 凭据保护

  • AppSecret/CorpSecret、短期 access token 和同步游标使用现有专用 Fernet 密钥加密;
  • AppKey/CorpID 作为应用标识明文保存,secret 从不出现在查询响应;
  • API 只返回 credential_configured、connection_method、连接状态和令牌过期时间;
  • 连接、重连和撤销只允许 Playbook 管理员;
  • httpx/httpcore INFO 日志被提升到 WARNING,防止企业微信必须放在查询参数中的 CorpSecret 或 access token 被完整 URL 日志泄漏;
  • 当前本地 UAT 使用独立的非生产开发密钥;生产必须改用 Secret Manager、KMS/Vault 或只读 secret file,并在接入真实凭据前完成轮换与恢复演练。

3.3 数据与迁移

Alembic 版本:20260824_0060。

playbook_calendar_sources 新增:

  • principal_id:钉钉用户 UnionID;
  • enterprise_client_id:AppKey 或 CorpID;
  • enterprise_client_secret_ciphertext:加密后的应用 secret;
  • enterprise_credentials_updated_at:凭据更新时间及索引。

现有 OAuth access-token、过期时间、同步游标、租约、失败次数和同步运行表继续复用。运行时 schema-check 已覆盖上述关键字段。

3.4 API 与前端

新增:

  • POST /api/v1/enterprise-playbook/governance/calendar-sources/{source_id}/enterprise-credentials/connect;
  • POST /api/v1/enterprise-playbook/governance/calendar-sources/{source_id}/enterprise-credentials/revoke;
  • Contract Web 两个同源代理路由;
  • 管理端连接状态、国内凭据表单、失败重连、撤销入口和供应商真实同步语义提示。

OpenAPI 已更新到 105 个路径、158 个 schema,0 个悬空 schema 引用;secret 字段标记为 writeOnly。

4. 验证结果

  • 后端收集 352 项:全量回归 348 项通过、4 项按既有环境条件跳过、0 项失败;最后的企业微信官方语义修正后,受影响专项再次 15/15 通过;
  • 国内日历适配器与 Playbook operations 专项:15/15 通过;
  • 钉钉断言:令牌请求结构、固定主机、访问令牌头、syncToken 推进、全量到增量、加密持久化;
  • 企业微信断言:corpid/corpsecret 请求结构、cal_id 空页终止、取消日程过滤、全天日程解析、完整快照摘要、强制 full 语义、撤销清理;
  • PostgreSQL 正向迁移到 20260824_0060 (head);
  • 全新 SQLite 完成 base → 0060 → 0059 → 0060 往返;
  • 前端单元测试 45/45 通过;
  • TypeScript 类型检查通过;
  • Next.js 生产构建通过,生成/收集 135 个页面;
  • Contract Web 3000、Contract API 8100、AI API 8101 和治理 Worker 均保持运行;
  • Contract API 运行时日历集成检查为 ok。

5. Chrome 超级管理员 UAT

工作空间:Playbook 隔离 UAT
账号:uat.legal.20260823@example.test
页面:http://127.0.0.1:3000/admin/playbook?tab=operations

已确认:

  • 页面显示 Enterprise governance · P1-04.8.1;
  • 钉钉入口显示 Calendar ID、UnionID、AppKey、只提交不回显的 AppSecret、增量游标和手动/定时增量;
  • 企业微信入口显示 cal_id、CorpID、只提交不回显的 CorpSecret、手动/定时同步、仅支持应用自建日历的限制及完整快照说明;
  • 必填信息为空时“添加日历源”保持禁用;
  • 实际 Chrome 视口无控件重叠、文本截断或不可达按钮;
  • 生产构建后刷新仍保持登录态,数据加载完成,浏览器控制台 0 条错误。

本地没有用户企业的真实钉钉/企业微信应用凭据,因此本轮没有向厂商发送真实 secret、没有创建虚假成功连接,也没有对企业日历写入或删除任何数据。外部成功链路由隔离 MockTransport、数据库集成和 Worker 测试覆盖;真实授权范围、应用可见范围和企业安全设置仍必须在预发布租户验证。

6. 市场与架构评估

对中国大陆市场,这项扩展应视为可采购性补齐,而不是可选装饰。企业合同治理中的 SLA 截止日若不能读取客户日常使用的办公协同日历,管理员最终仍会回到手工维护节假日,自动化价值显著下降。钉钉和企业微信覆盖了两类主流企业协同生态,优先级高于继续深化 Google 特有体验。

继续使用“统一日历领域模型 + 提供方适配器 + 事务物化 + 治理 Worker”是合理架构:

  • SLA 只依赖标准化全天日期,不把厂商对象泄露到合同/冲突领域;
  • 提供方差异被限制在令牌和拉取适配器,避免前后端处处出现厂商条件分支;
  • 短期令牌与同步状态和租户治理事实保持事务一致;
  • 当前吞吐量不需要拆出独立日历微服务,未来可将调度迁到队列而不改变 API;
  • 对企业微信保留真实的全量语义,牺牲一点网络效率换取可解释性和数据正确性,比虚构游标更符合合同治理产品的审计要求。

7. 上线门槛与下一步

正式开放真实租户前必须完成:

  1. 分别创建最小只读权限的钉钉内部应用和企业微信自建应用,确认日历所有者、应用可见范围、厂商安全策略和 cal_id/UnionID 的获取流程;企业微信需由该应用创建专用 SLA 日历;
  2. 在两个隔离预发布租户验证:初次全量、增加/修改/取消全天事件、钉钉增量、游标失效回退、企微快照差异、令牌过期刷新、撤销、租户隔离;
  3. 将本地开发密钥替换为生产 secret file/KMS,并演练密钥轮换、备份恢复和旧密文不可用告警;
  4. 建立提供方错误码分类、连续失败告警、同步延迟 SLO、配额/限流监控和失败运行回放;
  5. 以真实节假日与调休日历做业务验收,明确“全天事件才进入 SLA”的产品规则,防止普通会议误作节假日;
  6. 收集目标客户办公生态数据,再决定 P1-04.8.2 是否优先增加飞书,而不是仅凭技术可接入性扩张提供方数量。

8. 设计依据