| 能力 | 说明 |
|---|---|
| 发起活动 | 创建草稿 → 编辑 → 发布 |
| 推送给成员 | 通过企业微信应用消息推送,成员点消息里的按钮直达报名页 |
| 报名二维码 | 后端生成 PNG,可后台展示/下载打印 |
| 扫码报名 | 成员扫码打开报名页,填姓名+手机号提交,**免登录** |
所有接口前缀 /admin-api。
| 方法 | 路径 | 权限标识 | 说明 |
|---|---|---|---|
| POST | /wxcp/activity/create |
wxcp:activity:create |
创建活动(存为草稿) |
| PUT | /wxcp/activity/update |
wxcp:activity:update |
修改活动 |
| DELETE | /wxcp/activity/delete |
wxcp:activity:delete |
删除活动 |
| GET | /wxcp/activity/get |
wxcp:activity:query |
活动详情 |
| GET | /wxcp/activity/page |
wxcp:activity:query |
活动分页 |
| PUT | /wxcp/activity/publish |
wxcp:activity:update |
发布活动 |
| PUT | /wxcp/activity/finish |
wxcp:activity:update |
结束活动 |
| PUT | /wxcp/activity/cancel |
wxcp:activity:update |
取消活动 |
活动分页请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| pageNo | Integer | 是 | 页码 |
| pageSize | Integer | 是 | 每页条数 |
| title | String | 否 | 活动标题,模糊匹配 |
| status | Integer | 否 | 状态:0草稿 1已发布 2已结束 3已取消 |
| pushStatus | Integer | 否 | 推送状态:0未推送 1已推送 |
| startTime | DateTime[] | 否 | 活动开始时间范围,格式 yyyy-MM-dd HH:mm:ss |
创建/修改请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | Long | 修改时必填 | 编号 |
| title | String | 是 | 活动标题,≤128 字符 |
| content | String | 否 | 活动详情 |
| blobIds | Long[] | 否 | 封面图文件编号,最多 1 个;不传 = 不改动封面,传 [] = 清空封面 |
| location | String | 否 | 活动地点,≤255 字符 |
| startTime | DateTime | 否 | 活动开始时间 |
| endTime | DateTime | 否 | 活动结束时间 |
| signupStartTime | DateTime | 否 | 报名开始时间 |
| signupEndTime | DateTime | 否 | 报名结束时间 |
| signupLimit | Integer | 是 | 报名上限,**不限传 0** |
| pushScope | Integer | 是 | 推送范围:0全员 1指定部门 2指定成员 |
| pushTarget | String | 否 | 推送目标,逗号分隔;范围选 1 或 2 时必填 |
| remark | String | 否 | 备注 |
活动响应新增字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| statusName | String | 状态名,如「已发布」 |
| pushScopeName | String | 推送范围名,如「全员」 |
| signupCount | Integer | 已报名人数 |
| pushStatus | Integer | 0未推送 1已推送 |
| pushTime | DateTime | 最近推送时间 |
| attachmentList | Object[] | 封面图附件列表(最多 1 条),字段同报名附件:id、name、url、previewURL、downloadURL。列表缩略图、表单回显都从这里取 |
| 方法 | 路径 | 权限标识 | 说明 |
|---|---|---|---|
| POST | /wxcp/activity/push |
wxcp:activity:push |
推送给企业微信成员 |
| GET | /wxcp/activity/qrcode |
wxcp:activity:query |
获取报名二维码(返回 PNG) |
| GET | /wxcp/activity/signup-url |
wxcp:activity:query |
获取报名页地址 |
推送请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | Long | 是 | 活动编号 |
| pushScope | Integer | 否 | 覆盖活动上的推送范围,不传则沿用 |
| pushTarget | String | 否 | 覆盖活动上的推送目标,不传则沿用 |
推送响应:
{ "code": 0, "data": { "success": true, "errCode": 0, "message": "推送成功" } }
二维码接口: 直接返回 image/png 字节流,不是 JSON。
页面里把这个 URL 当图片地址用即可(如 <img :src="...">),需要带令牌时自行拼查询参数。
| 方法 | 路径 | 权限标识 | 说明 |
|---|---|---|---|
| GET | /wxcp/activity/signup/page |
wxcp:signup:query |
报名记录分页 |
| PUT | /wxcp/activity/signup/cancel |
wxcp:signup:update |
取消报名 |
| PUT | /wxcp/activity/signup/check-in |
wxcp:signup:update |
签到 |
分页请求参数: activityId、name(模糊)、mobile(模糊)、status
响应字段: id、activityId、activityTitle、wxUserId、name、mobile、deptName、status、statusName、signupTime、checkInTime、remark
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /wxcp/activity/signup-page?id={活动编号} |
报名落地页 HTML,由后端渲染 |
| POST | /wxcp/activity/signup |
提交报名(支持 content 文字 + blobIds 附件) |
提交报名请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| activityId | Long | 是 | 活动编号 |
| name | String | 否 | 姓名,≤64 字符 |
| mobile | String | 否 | 手机号;**填了才校验** 11 位格式,留空通过 |
| wxUserId | String | 否 | 企业微信成员账号 |
| deptName | String | 否 | 部门名称 |
| content | String | 否 | 参与说明 |
| blobIds | Long[] | 否 | 附件文件编号 |
| 方法 | 路径 | 权限标识 | 说明 |
|---|---|---|---|
| GET | /wxcp/activity/stats?id= |
wxcp:activity:query |
报名统计 |
| GET | /wxcp/activity/signup/attachments?signupId= |
wxcp:signup:query |
某条报名上传的图片/附件 |
统计响应:
{
"activityId": 1, "activityTitle": "2026春季团建活动",
"signupLimit": 50, "totalSignup": 1,
"signedUpCount": 1, "checkedInCount": 0, "canceledCount": 0,
"remaining": 49, "signupPercent": 2, "checkInPercent": 0,
"deptStats": [{ "name": "技术部", "count": 3 }],
"dateStats": [{ "name": "2026-09-29", "count": 1 }],
"sourceStats": [{ "name": "企业微信身份", "count": 0 }, { "name": "表单填写", "count": 1 }]
}
remaining/signupPercent在活动**不限名额**(signupLimit=0)时为null,前端要显示"不限"。
统计**不含已取消**的报名,所以totalSignup ≠ 报名记录总条数。
| 方法 | 路径 | 权限标识 | 说明 |
|---|---|---|---|
| POST | /wxcp/notice/create |
wxcp:notice:create |
创建通知(存为草稿) |
| PUT | /wxcp/notice/update |
wxcp:notice:update |
修改通知 |
| DELETE | /wxcp/notice/delete |
wxcp:notice:delete |
删除通知 |
| GET | /wxcp/notice/get |
wxcp:notice:query |
通知详情 |
| GET | /wxcp/notice/page |
wxcp:notice:query |
通知分页 |
| PUT | /wxcp/notice/publish |
wxcp:notice:update |
发布通知 |
| PUT | /wxcp/notice/revoke |
wxcp:notice:update |
撤回通知 |
| POST | /wxcp/notice/push |
wxcp:notice:push |
推送通知 |
通知与活动的差异:
| 项 | 活动 | 通知 |
|---|---|---|
| 推送消息类型 | 文本卡片(带「立即报名」按钮跳报名页) | 纯文本 |
| 正文长度 | 由卡片模板限制 | 超过 600 字**自动截断**并提示到系统内查看 |
| 状态 | 草稿/已发布/已结束/已取消 | 草稿/已发布/已撤回 |
| 推送前提 | 必须「已发布」 | 必须「已发布」 |
| 方法 | 路径 | 权限标识 | 说明 |
|---|---|---|---|
| GET | /wxcp/member/department/tree |
wxcp:member:query |
部门树(含 children) |
| GET | /wxcp/member/department/list |
wxcp:member:query |
部门平铺列表(做下拉更方便) |
| GET | /wxcp/member/list?departmentId=&fetchChild= |
wxcp:member:query |
成员列表 |
成员响应字段: userId(推送时填这个)、name、mobile、email、avatar、position、gender、status、departmentIds、departmentNames
这几个接口是**实时调用企业微信**的,企业微信对通讯录接口有频率限制。前端请做缓存(如打开选择器时拉一次),**不要**在搜索框里每输入一个字符就请求一次。
signupCount 自动 +1数据带入关系:
| 来源 | 目标 | 带入内容 |
|---|---|---|
| 活动编辑保存 | 推送消息 | 标题、时间、地点、报名截止、名额 |
| 活动编号 | 二维码内容 | {signupUrlPrefix}/admin-api/wxcp/activity/signup-page?id={id} |
| 扫码报名 | 报名记录 | 活动编号 |
| 字段 | 展示位置 | 说明 |
|---|---|---|
| title | 列表、详情 | 活动标题,点击进详情 |
| statusName | 列表、详情 | 用后端返回的中文名,前端不要自己映射 |
| startTime ~ endTime | 列表、详情 | 活动时间范围 |
| signupCount / signupLimit | 列表、详情 | 0/50 这种形式;上限为 0 时显示「不限」 |
| pushStatus / pushTime | 列表、详情 | 未推送 / 已推送 + 时间 |
| pushScopeName | 详情 | 推送范围中文名 |
| qrcode(图片) | 详情 | 报名二维码,需支持下载 |
| statusName(报名) | 报名列表 | 已报名 / 已取消 / 已签到 |
| 场景 | 规则 |
|---|---|
| 创建活动 | 一律存为草稿,不可直接创建为已发布 |
| 修改活动 | 已结束、已取消的活动不可修改;草稿和已发布可改 |
| 推送前提 | 活动必须是「已发布」状态,草稿状态推送会报「活动尚未发布,请先发布后再推送」 |
| 推送范围=全员 | 使用企业微信 @all,即应用可见范围内所有人 |
| 推送范围=指定成员 | pushTarget 填成员账号(UserId),多个用**英文逗号**分隔 |
| 推送范围=指定部门 | pushTarget 填部门 ID,多个用英文逗号分隔 |
| 推送失败 | 接口返回 success=false + 企业微信错误码,**不会抛异常**;前端应把 message 直接提示给用户 |
| 推送成功后 | 活动 pushStatus 置 1、pushTime 记录时间;重复推送会覆盖这两个字段 |
| 报名时间窗口 | 未到 signupStartTime 或已过 signupEndTime 均不能报名 |
| 报名名额 | signupLimit=0 表示不限;已满时提示「报名人数已满」 |
| 报名姓名/手机号 | 均为选填。手机号填了才校验格式,留空允许提交 |
| 重复报名 | 有企业微信身份按成员账号判重;没有账号但有手机号则按手机号判重;**两者都没有时不做判重**(无法判定身份,每次提交都新建一条) |
| 取消后重新报名 | 复用原记录,不会产生第二条历史 |
| 取消报名 | 活动 signupCount 自动 -1 |
| 封面图 | 不存业务表字段,存 system_storage_attachment(record_type = wxcp_activity)。删除活动时一并清掉附件关系 |
| 封面图展示 | 报名页顶部以 object-fit: cover 展示;管理后台列表在标题左侧显示缩略图 |
signupLimit 必填,不限要传 0 而不是留空,否则会校验失败。pushTarget 不能为空,否则报「推送目标不能为空」。content 字段在报名页是**按纯文本渲染**的(做了 HTML 转义防 XSS)。如果活动详情需要富文本排版,需要另行接入富文本白名单过滤,当前版本不支持。wx.cp.signup-url-prefix(当前为 http://wcf.viphk.nnhk.cc)。该地址必须是**成员手机能访问到的外网地址**,否则二维码扫出来打不开。wxUserId 只有在**企业微信内打开报名页且带上了参数**时才有值;普通浏览器扫码报名时为空,此时按手机号去重。userId(成员账号,如 LiuYi),不是姓名、也不是手机号。 从通讯录接口取 userId 填进 pushTarget。POST /admin-api/system/storage-blob/public-upload(表单字段名 files)拿到 blobId,再把 blobIds 随报名提交,由**服务端**完成与报名记录的绑定。管理后台查附件用 /wxcp/activity/signup/attachments。recordType 固定为 wxcp_activity_signup,已注册进 system 模块的 StorageRecordTypeEnum 白名单。以后新增别的附件关联类型,必须同步注册,否则绑定会报「无效的记录类型」。recordType 为 wxcp_activity,同样已注册进白名单。封面**不再有 coverUrl 字段**,POST/PUT /wxcp/activity/create|update 提交 blobIds,GET 读 attachmentList。yudao.storage.domain(一般是内网地址),而手机扫码是从公网域名打开页面的,直连内网地址加载不出图片,所以后端只保留 /admin-api/... 路径交给同源代理。**如果部署时管理后台与报名页不同源,这个处理需要重新评估。**ImageUpload 组件(maxNumber: 1)时,组件返回的是**单值而不是数组**,提交前必须包成数组(blobIds 后端签名是 List<Long>)。报名页 GET /admin-api/wxcp/activity/signup-page?id=xxx 由**后端直接渲染 HTML**(含内联样式和少量 JS),前端**无需开发**。
这样做的原因:二维码必须指向一个成员手机能直接打开的页面,而后端渲染可以立即跑通「推送 → 扫码 → 报名」闭环,不必等前端排期。若后续希望改为前端 SPA 页面,把二维码内容改成前端路由地址即可,后端接口(/wxcp/activity/signup)不用动。
该页面的路径刻意放在
/admin-api下(而非/app-api),因为前端 Vite 开发代理只转发/admin-api,放/app-api会导致扫码后请求打到前端静态服务返回 404。