--- name: kdshrpmsg description: >- Design and implement Kingdee EAS/SHR workflow todo and to-be-read message push to external portals or Weaver/Fanwei via MessageWebServiceDao. Use for 待办, 待阅, PORTAL-002/003/006/007/011/012/013, OAMessageWebServiceDao, WeaverMessageWebServiceDao, BMCMessage, assignReadRecord, receiveOAConfig, BaoYPro, Weaver, ReceiveTodoRequestByJson, weaverTodoConfig, pcurl entryPath, or ESB portal message integration on Kingdee SHR. BEFORE coding: ask intake questions in this skill (channel, API, identity, deep link, config file). When user says 上线/切生产/go-live: follow Go-live section with kdshrsso; update weaverTodoConfig host/credentials/shrServerName/sso.appId. --- # Kingdee SHR Portal Message Push (待办 / 待阅) Reusable pattern from **YHPro** (玉湖,已测通) and **BaoYPro** (报业 / 泛微统一待办). Do not copy secrets; adapt per customer. Related SSO deep-link entry: skill **kdshrsso** (BaoYPro: Option B-qy `/qy/auth/weaver/app.do`). Coding discipline (do not guess platform getters / uncertain fields): skill **kdshr-dev**. --- ## Agent intake — ask before coding (mandatory) When implementing **待办 / 待阅** for a new project or new channel, do **not** start coding until goals are confirmed. Ask in one batch. If SSO is in the same delivery, run **kdshrsso** intake first (or together). ### Must confirm (block if unanswered) 1. **渠道**: 泛微统一待办 / YH 门户 PORTAL-* / 钉钉 / 飞书 / 其他?**接口文档**是否已提供? 2. **范围**: 只要待办,还是待办+待阅(BMC)?是否只要推送、SSO 已另做? 3. **鉴权**: OpenAPI token / HTTP Basic / 其他?凭证放哪份 properties(勿与无关渠道混文件)? 4. **人员**: 推送接收人字段 = 工号 / 手机 / 对方 userid?与 sHR 哪一列对齐? 5. **幂等键**: 对方如何判重(如 Weaver:`syscode+flowid+workflow+receiver`;YH:`taskId`)?`flowid` 是否用 **procInstId**? 6. **深链**: 绝对 URL 还是相对路径?SSO 入口(须与 kdshrsso 一致,如 `/qy/auth/weaver/app.do`)?审批页 path? 7. **SPI**: 新建独立 `*MessageWebServiceDao`,还是 Router fan-out 多渠道?默认 **独立 Dao + enabled 开关**,少动已上线渠道。 ### Should confirm (sensible defaults) 8. 配置命名:泛微继续 `weaverTodoConfig.properties`;钉钉/飞书用 `dingtalkTodoConfig` / `feishuTodoConfig`,**不要**改名合并成一个大文件。 9. 状态机:待办/已办/删除/办结/待阅 与对方字段映射(Weaver `isremark` 等)——无文档则先按 weaver-baoy-plan 草案请用户改。 10. CallLog / 失败补推是否要做;WF 延迟(如 `wf.delayMs`)经验值。 11. 与 SSO 是否同期:深链入口必须已存在或同期交付(否则只推链接无法点开登录)。 12. **配置文件是否齐**:对照下方「配置文件清单」——`set-server-env`、`integration-config`、`weaverTodoConfig`、`WSConfig.xml`、login/logout JS、DDL/jar;缺哪项就问「本项目要不要配」。 ### Output before coding 给出 **目标确认表**:渠道、配置文件名(含清单勾选)、SPI 策略、深链模板、人员字段、状态映射来源。用户确认后再改代码。用户说「按报业/按 BaoYPro」时可复述默认假设请一次确认。 --- ## 配置文件清单(必配 / 防漏)— 新项目与切环境通用 做 **待办/待阅**(尤其 Weaver + SSO)时,Agent **必须**对照下表逐项核对;其他项目若漏配,要**主动列出并问用户「是否需要配置」**,不得默认跳过。 与 SSO 重叠项同时对照 skill **kdshrsso** Go-live。总清单见项目 `docs/integration/go-live-prod.html`。 ### A. 服务端配置(按环境区分 test/prod) | # | 文件(相对 `EAS_HOME`) | 用途 | 关键须改的关键项 | 新项目是否要 | |---|------------------------|------|------------------|--------------| | 1 | `server/bin/set-server-env.sh`(或 Windows 等价) | 选读哪份 integration JSON | `ENV=test\|prod` → `INTEGRATION_CONFIG_PATH` | **要**(有 SSO/qy 时) | | 2 | `server/properties/integration-config-{env}.json` | SSO OAuth、SHR、泛微 ecology | `shrServerName`、`dataCenter`、`allowedPrefixes`、`appId`/`appSecret`、`callbackUrl`、`weaver.ecologyUrl` | **要**(有 SSO 时) | | 3 | `server/properties/{客户}/weaverTodoConfig.properties` | 待办 OpenAPI + 待阅 sendMsg | `openapi.host`、凭证、`syscode`、`shrServerName`、`sso.appId`、`notify.moduleId`/`eventId`/`tenantKey` | **要**(有 Weaver 待办/待阅时) | | 4 | `server/profiles/server1/config/portalConfig/WSConfig.xml` | 平台消息 SPI 注册 | ``=Dao 全类名;``=账套(与 json **一致**) | **要**(走 MessageWebServiceDao 时) | | 5 | `server/properties/{客户}/weaverTodo-ddl.sql` | CallLog / assignReadRecord | 生产库是否已建表 | 首次上线要执行 | 报业示例目录名 `baoye/`;其他客户换自己的 properties 子目录,**不要**把多渠道揉进一个文件。 ### B. 前端 / 门户静态脚本(有 SSO 联邦登录/退出时) | # | 文件 | 用途 | 关键项 | |---|------|------|--------| | 6 | `server/deploy/eas.ear/sso_web.war/common/js/login_withForm.js` | 门户进 SHR SSO | `ENV_CONFIG.prod.*`;`getCurrentEnv()` 识别生产 Host | | 7 | `server/deploy/easweb.ear/shr_web.war/scripts/gui/logoutHR.js` | 退出转泛微 | `currentEnv` / 生产 ecologyUrl(或按 host) | ### C. 部署物(非 properties,但常漏) | # | 项 | 说明 | |---|------|------| | 8 | 二开 jar(如 weaverTodo / qy 相关) | 放入 `lib`/`sp` 等,仅改配置不部署代码无效 | | 9 | `qy.war` context=`/qy` | SSO 入口与 callback 依赖 | | 10 | 泛微后台 | callback 注册、退出白名单、异构 syscode/IP、消息中心 module/event 规则 | ### Agent 防漏话术(强制) - **新项目 intake**:在确认表里列出上表 A+B,标「本项目需要 / 不需要 / 待确认」。用户未表态的,**先问再写代码**。 - **切生产 / 覆盖包**:逐文件核对是否仍为测试值(常见漏项:`WSConfig.xml` 的 `dataCenter`、`notify.moduleId/eventId`、`ENV=prod`、`logoutHR currentEnv`)。 - **只做待办不做 SSO**:仍要问 WSConfig + weaverTodoConfig;可标 login/logout/integration-config 为「本期不做」。 - **只做 SSO 不做待办**:转 kdshrsso;仍提醒若后续接待办需补 3、4、5。 ### 报业(BaoYPro)正式环境快照(参考,勿照抄到别的客户) | 项 | 值 | |----|-----| | SHR | `https://hr.szpgm.com/shr` | | 泛微 OpenAPI | `http://172.16.37.23:20600` | | dataCenter | `SNG`(json **与** WSConfig 一致) | | notify | tenant=`t6srq5l4vh` module=`100011` event=`241221` | | 覆盖包 | `D:\workcode\baoye\patch\正式单点部署`(测试包:`...\test单点部署`) | --- ## Go-live / 切生产 (with kdshrsso) When user says **上线 / 切生产**, coordinate with **kdshrsso** Go-live, and walk the **配置文件清单** above (do not only edit `weaverTodoConfig`). Key production fields for Weaver todo/notify (`weaverTodoConfig.properties`): | Key | Set to production | |-----|-------------------| | `weaver.openapi.host` | 生产 OpenAPI/ecology | | `weaver.corpid` / `app_key` / `app_secret` | 生产开放平台 | | `weaver.syscode` | 生产已注册标识 | | `weaver.shrServerName` | 生产 SHR(redirect) | | `weaver.sso.appId` | 与 `integration-config-prod.json` 中 appId 一致 | | `weaver.sso.entryPath` | 通常仍 `/qy/auth/weaver/app.do` | | `weaver.notify.tenantKey` | 按客户(报业 `t6srq5l4vh`) | | `weaver.notify.moduleId` / `eventId` | **按环境**(报业生产 `100011` / `241221`;测试 `139141` / `287602`) | Also must set: `ENV=prod`、`integration-config-prod.json`、`WSConfig.xml` ``、login/logout JS。 Full checklist: project `docs/integration/go-live-prod.html`。 报业生产 SHR:`https://hr.szpgm.com/shr`。 --- ## When to use | Scenario | Fit | |----------|-----| | Push workflow **待办** to enterprise portal / OA | Yes | | Push BMC **通知/待阅** to portal | Yes | | Portal APIs match YH PORTAL-* REST + HTTP Basic + ESB envelope | Yes | | Weaver/Fanwei/统一待办中心 (BaoYPro) via ReceiveTodo API | Yes — see weaver-baoy-plan.md | | Only DingTalk/Feishu, no portal | Partial — separate channel + config file; skip PORTAL-* / Weaver receive | | Replace product message center entirely | No | ## Quick decision ``` Need portal sync for SHR workflow/messages? │ ├─ Implement MessageWebServiceDao (platform SPI) — NEVER invent a parallel push framework │ ├─ Todo: addMessage / addCompletedMessage / removeMessage │ └─ ToBeRead: updateMessages(id, MsgStatus, ctx) │ ├─ Downstream? │ ├─ YH-style ESB PORTAL-* → TodoMessageServer + receiveOAConfig │ └─ Weaver E10: │ ├─ 待办 → receiveRequestInfo(weaverTodo + weaverTodoConfig) │ └─ BMC 通知/待阅 → 消息中心 sendMsg(勿再用 isremark=8) │ ├─ Deep link back to SHR? │ ├─ Weaver: relative /qy/auth/weaver/app.do?appId&redirect=approveAbs (kdshrsso B-qy) │ ├─ Future DingTalk/Feishu: that platform SSO entry + same approveAbs │ └─ Never rely on Unified /auth/app + UA for PC Weaver todos │ └─ Config naming ├─ Keep weaverTodoConfig.properties for Weaver only (do not mega-rename for BY) └─ Later: dingtalkTodoConfig.properties / feishuTodoConfig.properties ``` --- ## Adapter targets (YH vs BaoY) | Project | Downstream | API style | |---------|------------|-----------| | YHPro | Enterprise portal ESB | Multiple PORTAL-* URLs | | BaoYPro | Weaver / Fanwei / eteams | One receiveRequestInfo (+ OpenAPI oauth) | Reuse YHPro SPI/hooks idea; replace HTTP payload and status fields. Detail: [weaver-baoy-plan.md](weaver-baoy-plan.md) ### BaoYPro:BMC 通知 / 待阅 → 消息中心 sendMsg(**测试已通过**) 不再走统一待办 `receiveRequestInfo` / `isremark=8`。实现类:`WeaverToBeReadMessageServer` + `WeaverSendMsgClient`。 | 项 | 约定 | |----|------| | 接口 | `/papi/openapi/api/mc/msg/sendMsg`(`weaver.openapi.sendMsgPath`) | | 人员 | **只要 `workCode`,不传 `employeeId`** | | 发送人 | `BMCMessageInfo.getSenderId()` → `getUserById` → `UserInfo.getNumber()` / `getName()` | | 接收人 | `getReceiver()` → `resolveReceiverWorkcode` | | title | `getTitle()` | | text | **`getBody()`**;空则回退 title | | todo | `false`;已读不回写泛微 | | entity.pcUrl | 有 BMC url 才传(SSO 相对入口) | | channels | 默认 `[1]`(IM) | | 配置 | `weaver.notify.tenantKey` / `moduleId` / `eventId` / `channels` | | 环境固定参数 | 测试:tenant=`t6srq5l4vh` module=`139141` event=`287602`;生产:tenant=`t6srq5l4vh` module=`100011` event=`241221`(切环境只改 properties,勿混用) | | 配置热加载 | `WeaverTodoConfig` 按文件 mtime 重载,**改 notify 参数一般不用重启** | | 日志前缀 | 待办 `[WEAVER-TODO]`;待阅 `[WEAVER-TOREAD]`(含 `WeaverSendMsgClient`) | | CallLog | 表 `OAMsgCallLog`;`OperType=通知发送`;`RequestUrl`=host+path(**不含 token**);`ResponseResult`=对方完整 JSON | | OpenAPI token | **JVM 级缓存**(跨多次 new Dao);authorize 遇 Sentinel/`-10` 有限次退避重试 | SPI:`updateMessages(UNREADED)` → sendMsg;`addMessage` 且 `type` 含「通知」→ **跳过**(避免再推 isremark=0)。 ### BaoYPro:新增待办与 `wf.delayMs` 执行顺序(**勿颠倒**) 旧错法:先 `sleep(wf.delayMs)` 再查 AssignmentId —— sleep 期间若任务撤回,AssignRead 已空,会误报 assignId 为空。 正确顺序(`WeaverTodoMessageServer.addMessage`): 1. **短轮询**拿 AssignmentId(`waitAssignIdByMsgId`,预算约 `wf.delayMs`,步进 500ms) 2. assignId / procInstId 为空则直接放弃并打 CallLog 3. 解析人、标题、拼 `pcurl` 4. **`saveAssignRecord`** 先落本地关联 5. 再 **`delayForWf()`**(整段 `wf.delayMs`),让「待办转已办」等并发先落定 6. 最后 `receiveRequestInfo` 推 isremark=0 已办/删除路径可先 `delayForWf()` 再查库(与新增不同)。配置项:`weaver.wf.delayMs`(报业常用 8000)。 Coding discipline:skill **kdshr-dev**。 --- ## Architecture (reference layout) ### YHPro ``` platform workflow / BMC → XxxMessageWebServiceDao → Todo / ToBeRead servers → PORTAL-* → MessageCommonUtil (Basic + JSON) ``` ### BaoYPro (Weaver) ``` platform workflow → WeaverMessageWebServiceDao → WeaverTodoMessageServer → WeaverReceiveTodoClient (OpenAPI token + receiveRequestInfo) → pcurl from WeaverTodoSupport (entryPath + appId + redirect) platform BMC 通知 → WeaverMessageWebServiceDao.updateMessages(UNREADED) → WeaverToBeReadMessageServer → WeaverSendMsgClient (同 OpenAPI token + sendMsg) ``` Config: `{EAS_HOME}/server/properties/baoye/weaverTodoConfig.properties` Key: `weaver.sso.entryPath` (default `/qy/auth/weaver/app.do`) + `weaver.sso.appId`;通知另见 `weaver.notify.*`。 --- ## Platform SPI map (MessageWebServiceDao) | Method | YH PORTAL | Weaver 待办 isremark | BaoYPro BMC 通知 | |--------|-----------|----------------------|------------------| | `addMessage` | 002 | 0(type 含「通知」则跳过) | — | | `addCompletedMessage` | 006 then 003 | 2; flow end → 4 | — | | `removeMessage` | 007 | -1 | — | | `updateMessages` | 011 / 012 | (历史方案 isremark=8,BaoY **不用**) | **sendMsg**(未读);已读不回写 | --- ## Implementation checklist (new project) ``` - [ ] 1. Downstream API doc + env + auth - [ ] 2. Person mapping field (工号 / cfidmusercode / …) - [ ] 3. MessageWebServiceDao facade + servers - [ ] 4. Per-channel properties (weaverTodoConfig / receiveOAConfig / …) - [ ] 5. assignReadRecord / CallLog - [ ] 6. Register SPI in product - [ ] 7. BMC hook if 待阅 needed - [ ] 8. PC URL: SSO wrap (kdshrsso) + approve page - [ ] 9. Mobile URL if required - [ ] 10. E2E create → approve → complete/delete ``` --- ## Thinking patterns 1. **SPI first** — Extend `MessageWebServiceDao`. 2. **Idempotency keys** — Decide before coding (YH taskId vs Weaver flowid+receiver). 3. **Order** — Complete/delete tasks before terminal flow update. 4. **Identity** — Same code across SSO + push + SHR person. 5. **Deep link + SSO** — Always SSO entry then target (kdshrsso); platform-specific entry URL. 6. **Latency** — WF DB lag; delay/retry. 7. **Observability** — CallLog; empty requestparams often = exception before body write. 8. **Channels** — Separate configs/clients per IdP; fan-out later; don’t block one channel on another. 9. **Config externalization** — No secrets in git; hot-reload when possible. 10. **Weaver links** — Relative path only; configurable `sso.entryPath`. --- ## Anti-patterns | Avoid | Why | |-------|-----| | Bypass MessageWebServiceDao | Missed callbacks | | Absolute pcurl to Weaver | API rejects / 外链规则不符 | | Unified `/auth/app` for Weaver PC todo | UA → unsupported_client | | Rename weaverTodoConfig to “todoConfig” mid-project | Risk / little gain | | One config file for Weaver OpenAPI + DingTalk push | Secrets and APIs differ | | Soft-fail empty person mapping | Wrong assignee | --- ## Search terms ``` MessageWebServiceDao WeaverMessageWebServiceDao WeaverTodoMessageServer WeaverTodoSupport WeaverTodoConfig weaver.sso.entryPath receiveRequestInfo syscode s-HRCloud assignReadRecord OAMsgCallLog receiveOAConfig PORTAL-002 ``` ## References | Doc | Content | |-----|---------| | [yhpro-reference.md](yhpro-reference.md) | YHPro package + field mapping | | [portal-api-map.md](portal-api-map.md) | PORTAL field quick map | | [weaver-baoy-plan.md](weaver-baoy-plan.md) | BaoYPro Weaver todo (implemented notes) | | [weaver-receive-todo-api.md](weaver-receive-todo-api.md) | Weaver E10 field extract | | kdshrsso Option B-qy | SSO entry / logout / AttackFilter |