版本:v0.1(评审稿,已按官方文档核验) 日期:2026-09-11 范围:后端自动推送 · 企微自建应用消息 · 集团单一企微(单 corpId)
项目(养老院管理系统,若依 RuoYi 体系:前端 kyj-yanglao-web-new + Java 服务端)当前的审核/审批:
/bpm/task/approve、/bpm/task/reject、/bpm/task/return 触发「通过/拒绝/退回」。/system/notify-message/getBpmListCount、标记已读 /system/notify-message/updateReadById。问题:站内通知仅在 Web 端可见,审核通过/拒绝/待办变更无法及时触达相关人员(经办人、发起人、审批人)。
目标:在审核结果产生时(后端),通过企业微信自建应用消息,将审核通知实时推送到关联人员的企业微信;采用后端自动推送,不依赖前端唤起页面。
关键前提:集团为单一企业微信(单 corpId),userid 全集团唯一,因此可按 userid 精准推送给对应人员,一套 corpId/secret/agentId 全局复用,无需按租户/机构拆分多套企微配置。
| 能力 | 现状 | 位置(前端) |
|---|---|---|
| 审核动作入口 | 通过/拒绝/退回 | src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue(handleAudit) |
| 审核接口 | /bpm/task/approve、/bpm/task/reject、/bpm/task/return |
src/api/bpm/task/index.ts |
| 待办/已办列表 | 是 | src/views/bpm/task/todo/index.vue |
| 站内通知 | 未读数 + 已读 | /system/notify-message/* |
| 后端技术栈 | 若依 RuoYi(Spring Boot) | 不在本仓库 |
前端审核动作只是把结果提交给后端,最终的审批状态流转和通知都在服务端。因此企微推送必须落在后端。
申请人 审批人 企微 App(单 corpId)
│ │ │
│ 提交单据(BPM启动) │ │
│ ───────────────────► │ │
│ │ │
│ 审批通过/拒绝 │ │
│ │ ─────────────────► │
│ 审核结果完成 │
│ └── 后端流程引擎触发通知
│ │
│ ▼
│ ┌─────────────────────────┐
│ │ NotifyMessage 记录站内通知│
│ └─────────────────────────┘
│ │
│ ▼
│ ┌─────────────────────────┐
│ │ QyWeixinClient │
│ │ - 取 access_token(缓存) │
│ │ - 按 userid 精准发应用消息 │
│ └─────────────────────────┘
│ │
│ ▼
│ 企微 App 推给对应人员
不直接改 approve/reject 各自的方法,而是在审核完成时统一产生的通知出口处理,做到一次接入、覆盖所有审核场景:
notify-message)的地方,追加企微推送。所有通过/拒绝/退回都经过此处。两种方案都满足「后端自动推送、前端零改动」。方案 A 复用既有通知点位更简单;方案 B 更符合流程事件化、可扩展(未来其它消息也可复用)。
| 配置项 | 含义 | 获取位置 |
|---|---|---|
corpId |
企业 ID(全集团唯一) | 企微管理后台 → 我的企业 → 企业信息 |
corpSecret |
自建应用的密钥 | 应用管理 → 目标应用 → Secret |
agentId |
应用 AgentId | 应用管理 → 目标应用 → AgentId |
GET https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={corpid}&corpsecret={secret}发送应用消息(给指定 userid,可多选):
POST https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={token}
body:
{
"touser": "zhangsan|lisi",
"msgtype": "markdown",
"agentid": 1000002,
"markdown": { "content": "..." },
"safe": 0
}
touser 用其 userid(非手机号/姓名)。userid 全集团唯一,可直接按 userid 精准推送给对应人员,一套配置全局有效,无需按租户/机构拆分。后端不在本仓库,此处给出落地清单;实际编码在拿到后端仓库后进行。
application.ymlwecom:
app:
enabled: true
corp-id: wx1234567890abcdef
agent-id: 1000002
corp-secret: YOUR_CORP_SECRET
# userid 前缀/映射可选,若系统用户表存的是手机号而非 userid
user-id-fallback: mobile
QyWeixinClient@Component
public class QyWeixinClient {
// 1. getAccessToken():按 agentId 维度 Redis 缓存 7200s,失效再拉取(单 corpId 下基本一份 token)
// 2. sendMarkdown(targetUseridSet, mdContent):非空 userid 才发,多个用 "|" 拼接
// 3. 记录返回的 invaliduser/unlicenseduser 日志,便于排查 userid 映射
// 异常处理:RestClientException 记日志,不向上抛,保证不影响主审批流程
}
enabled=true、存在接收人 userid 时才发送。touser):
user-id-fallback: mobile,用手机号在企微通讯录换取 userid(/cgi-bin/user/getuserid)。手机号必须开通并唯一。在服务端「BPM 审批写站内通知(notify-message)」的方法内,审批结果确定后追加:
// 伪代码
notifyMessageService.createBpm(...); // 既有站内通知,保留不动
if (wecomProperties.getApp().isEnabled()) {
Set<String> userIds = resolveReceiverUserIds(instance, task);
if (!userIds.isEmpty()) {
wecomClient.sendMarkdown(userIds, buildApprovalMd(instance, task, result));
}
}
**养老院审批通知**
> 单据:退住申请
> 编号:DS-2026-0001
> 发起人:张三
> 审批人:李四
> 结果:✅ 已通过 / ❌ 已拒绝 / ↩️ 已退回
审批意见:……
时间:2026-09-11 14:30
@Async)。enable_duplicate_check=1 防重复推送(默认 1800s 内同内容去重)。enabled:false 一键关闭,灰度可配置按 tenant 或单据类型白名单。| 步骤 | 内容 | 负责人 |
|---|---|---|
| 1 | 企微后台创建自建应用,取得 corpId / agentId / secret | 企微管理员 |
| 2 | 应用可见范围勾选需被通知人员 | 企微管理员 |
| 3 | 服务器出口 IP 加入「企业可信 IP」 | 运维 + 企微管理员 |
| 4 | 确认用户表具备 userid(或手机号可映射) | 后端开发 |
| 5 | 后端接配置 + QyWeixinClient + 通知出口挂接 | 后端开发 |
| 6 | 联调:发起一单审批,验证审批人/申请人企微能收消息 | 后端 + 测试 |
| 7 | 灰度与开关 | 运维 |
invaliduser)。需在联调阶段核对映射。单 corpId 下全集团 userid 唯一,映射相对清晰。已根据企微官方开发文档逐项核对,当前方案整体无误,并补充以下细节:
| 方案写法 | 官方结论 | 是否一致 |
|---|---|---|
gettoken?corpid=&corpsecret= 取 token |
GET https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRET,需应用为启用状态 |
✅ |
| access_token 有效期 7200s,需缓存复用 | expires_in: 7200(2 小时),须缓存、不能频繁调用否则被频率拦截 |
✅ |
message/send?access_token= 发消息 |
POST https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=ACCESS_TOKEN |
✅ |
touser 用 userid,多个用 \| 分隔 |
最多 1000 个;也支持 toparty/totag/@all |
✅ |
agentid 为整型必填 |
企业内部应用在设置页查看 | ✅ |
msgtype: markdown |
官方支持 markdown 消息(有语法子集) | ✅ |
| 后端自动推送 | 官方明示「请勿将 access_token 返回给前端,全部由后台发起企微 API 请求」——印证后端方案正确 | ✅ |
access_token 全局一份即可(仍按 agentId 语义缓存,便于扩展)。多企业场景才需按企业隔离。invaliduser;若全部无权限则 errcode=81013。发送方法需记录 invaliduser/unlicenseduser 便于排查。enable_duplicate_check=1(默认 1800s 同内容去重),避免重复推送。