活动模块升级设计(报名状态 / 付费 / 状态机 / 签到身份 / 审核发布 / 总结资料)
日期: 2026-08-01
状态: 设计定稿 v1(需求逐项确认完成)
前置文档: 2026-07-31-activity-registration-form-design.md(报名表单,已实施)
1. 概述
活动模块 7 项需求改造,覆盖报名体验、收费模型、状态机、签到身份校验与普通用户发布审核流程。核心变化:
- 活动卡片显示报名状态(此前硬编码「进行中」)
- 报名弹窗删除重复的取消/确认按钮(uni-app 内置
<modal> 自带按钮与插槽按钮重复)
- 报名时间窗口:新增「报名截止时间」字段,活动发布即开放报名,截止后停止
- 付费报名:先支付后报名;双费用为 0 时直接报名成功
- 签到身份校验:有手机号孩子只能本人签到;无手机号孩子家长可代签(校验家庭关系 + 报名记录)
- 收费模型升级:孩子/家长双收费 × 会员/非会员双价格(4 个费用字段)
- 卡片状态机:报名中 / 已报名 / 进行中 / 已签到 / 总结中 / 已结束
补充(用户确认):普通用户也可发布活动,但必须经管理员审核后才能正式发布;活动结束后进入「总结中」,发布人可上传活动资料、参与者可点评,手动结束或 7 天未结束自动结束。
2. 需求与决策记录
| # |
需求 |
决策 |
| 1 |
「已经报过名的未显示」 |
根因:卡片硬编码「进行中」,后端 list 不返回当前用户报名状态 → 卡片显示报名状态(displayStatus) |
| 2 |
报名弹窗重复确认/取消按钮 |
删除 uni-app 内置 <modal> 底部「确定/取消」按钮,改自定义 view 遮罩弹窗(三个弹窗统一改造:报名 / 签到惩罚 / 评价) |
| 3 |
报名时间窗口 |
新增 registration_deadline 字段,仅按截止时间控制(发布即开放);签到仅活动进行中;按钮缩小 |
| 4 |
付费报名 |
先支付后报名;双费用均为 0 → 直接报名成功 |
| 5 |
签到身份 |
有手机号孩子 → 只能本人签到;无手机号孩子 → 家长可代签(校验家庭关系 + 该孩子 approved 报名记录) |
| 6 |
收费模型 |
活动级 child_fee / parent_fee × 会员/非会员 → 4 个费用字段(会员价可免费=0),替代 price / memberPrice |
| 7 |
状态机 |
报名中 / 已报名 / 进行中 / 已签到 / 总结中 / 已结束(已结束不在列表,历史记录可查) |
| 8 |
发布人范围 |
普通用户可创建活动(draft)→ 提交审核(pending)→ 管理员审核(approved=published / rejected=驳回+原因)→ 驳回可 re-draft 修改再提交 |
| 9 |
总结中阶段 |
活动结束后自动进入「总结中」:发布人上传活动资料(图片/PDF/链接,多条,时间排序);参与者(已签到)可点评;发布人手动「结束」或 7 天未结束自动结束 → 已结束 |
| 10 |
未报名签到 |
保留现逻辑:扣能量(energy_penalty_unregistered)放行 |
3. 状态机
3.1 发布状态(activities.status,后端落库)
draft → pending(提交审核)→ published(审核通过)/ rejected(驳回)→ withdrawn(撤回)→ ended(结束)
publish(draft 直接发布)仅保留给 admin/activity_admin 直发;普通用户走 submit-review → audit
- 现有审核接口已具备:
/api/admin/activity/submit-review、/audit、/re-draft、/withdraw、/review
3.2 展示状态(displayStatus,卡片用,按用户视角计算)
优先级自上而下(单个活动只命中一个):
| 优先级 |
displayStatus |
条件 |
displayLabel |
| 1 |
ended |
status = ended |
已结束(列表过滤,不在活动列表,历史可查) |
| 2 |
summarizing |
status = published 且 now > endTime |
总结中(发布人可传资料,参与者可点评) |
| 3 |
checked_in |
当前用户该活动存在签到记录 |
已签到 |
| 4 |
ongoing |
startTime <= now <= endTime |
进行中 |
| 5 |
registered |
当前用户存在 approved 报名记录 |
已报名 |
| 6 |
pending_start |
now > registration_deadline(未报名) |
待开始 |
| 7 |
open |
now <= registration_deadline(未报名) |
报名中 |
- 计算输入:
activity.status / startTime / endTime / registration_deadline + 当前用户 userId 的报名与签到状态
- 7 天自动结束:
status = published 且 now > endTime + 7 天 → 惰性更新 status = ended(list 查询时批量处理,无需定时任务)
3.3 报名状态(registrationStatus,详情页用)
not_registered / registered(approved)/ pending(报名审核中)/ rejected(被拒)/ cancelled / checked_in
4. 数据库设计
4.1 activities 表新增 6 列(迁移 142)
| 列名 |
类型 |
说明 |
registration_deadline |
DATETIME NULL |
报名截止时间(NULL=不限制,默认=活动开始时间) |
child_fee |
INT DEFAULT 0 |
孩子·非会员价(分) |
child_member_fee |
INT DEFAULT 0 |
孩子·会员价(分),0=免费 |
parent_fee |
INT DEFAULT 0 |
家长·非会员价(分) |
parent_member_fee |
INT DEFAULT 0 |
家长·会员价(分),0=免费 |
creator_id |
BIGINT NULL |
创建人 userId(普通用户发布者),admin 创建可为空 |
price / member_price 字段废弃不再使用(保留列不删,避免破坏历史数据)
4.2 新表 activity_orders(迁移 143)— 活动报名订单
| 列名 |
类型 |
说明 |
id |
BIGINT PK AUTO |
主键 |
order_no |
VARCHAR(64) UNIQUE |
订单号(ACT + 时间戳 + 随机) |
activity_id |
BIGINT |
活动 ID |
user_id |
BIGINT |
下单人 userId |
total_amount |
INT |
实付金额(分) |
registrants_json |
TEXT |
报名人快照 JSON([{name, phone, childId, type: child/parent, fee}]) |
status |
VARCHAR(20) |
pending / paid / cancelled |
transaction_id |
VARCHAR(64) NULL |
微信支付单号 |
paid_at |
DATETIME NULL |
支付时间 |
created_at |
DATETIME |
创建时间 |
- 支付成功回调 →
status = paid + 写 activity_registrations 报名记录(同一事务/回调幂等:order_no 唯一,已处理直接返回成功)
4.3 新表 activity_materials(迁移 144)— 活动总结资料
| 列名 |
类型 |
说明 |
id |
BIGINT PK AUTO |
主键 |
activity_id |
BIGINT |
活动 ID |
title |
VARCHAR(200) NULL |
标题 |
file_url |
VARCHAR(500) |
文件地址(图片/PDF/外链 URL) |
file_type |
VARCHAR(20) |
image / pdf / link |
created_at |
DATETIME |
创建时间 |
- 仅发布人(creator 或 admin/activity_admin)可上传/删除;活动进入「总结中」后开放
- 列表按
created_at 倒序(最新在前)
5. 后端 API 设计
5.1 改动接口
| 接口 |
变更 |
POST /api/activity/create |
放开普通用户(parent 可创建,status=draft,记录 creatorId);admin/activity_admin 行为不变 |
POST /api/activity/update |
同上放开;仅创建人/admin 可改 |
POST /api/activity/list |
返回 displayStatus / displayLabel / registrationStatus(按当前 userId);过滤 ended;惰性处理 7 天自动结束 |
POST /api/activity/detail |
返回 displayStatus / displayLabel / registrationStatus / 4 个费用字段 / registrationDeadline / 资料列表 materials |
POST /api/activity/register |
校验 registration_deadline;手机号可选(无手机号孩子允许报名);付费活动必须先有已支付订单(校验 activity_orders) |
POST /api/activity/checkin |
时间窗校验(仅活动进行中);身份校验:有手机号孩子=本人签,无手机号孩子=家长代签(家庭关系 + 该孩子 approved 报名) |
POST /api/activity/end |
保持(手动结束),admin/activity_admin/创建人可结束 |
POST /api/activity/submit-review |
普通用户提交审核:draft → pending(仅创建人/admin 可提交自己的活动)——复用 ActivityAdminService.submitForReview 能力,需加 creatorId 校验 |
5.2 新接口
| 接口 |
说明 |
POST /api/activity/order/create |
创建活动报名订单:入参 {activityId, registrants:[{name, phone, childId}]};校验截止时间/名额/重复报名;按下单人会员等级(MembershipService.getMemberLevel,FAMILY/PROVIDER=会员)选择费用;总额=Σ(人数×单价);0 元直接走报名成功不建订单 |
POST /api/activity/order/pay |
调 PaymentService.createWechatPrepay 返回 wechatPayParams |
POST /api/activity/order/handlePaymentSuccess |
前端支付成功回调:幂等置 paid + 写报名记录(仿照 ProductOrderController.handlePaymentSuccess) |
POST /api/activity/order/notify |
微信支付异步通知:复用 PaymentService.handleWechatNotify,加入 JwtInterceptor 公开白名单 |
POST /api/activity/order/status |
查询订单状态(支付页轮询/回调兜底) |
POST /api/activity/material/upload |
上传资料(仅发布人,总结中阶段) |
POST /api/activity/material/list |
资料列表(按 activityId,倒序) |
POST /api/activity/material/delete |
删除资料(仅发布人) |
- 费用计算复用现有会员判定:
ActivityDTO.from(activity, isGuest, memberLevel) 已有会员价格解析先例,新逻辑按下单人 memberLevel 取对应 4 个字段
- 回调幂等:
order_no 唯一索引,status=paid 直接返回成功
6. 小程序前端改动
6.1 activity-detail.vue(核心改造)
- 弹窗改造:报名 / 签到惩罚 / 评价三个
<modal> → 自定义 view 遮罩弹窗(去掉内置「确定/取消」按钮,保留插槽内自定义操作按钮);报名弹窗底部「确认报名」按钮缩小
- 报名截止:
报名截止: MM-DD HH:mm(用 registrationDeadline,不再误用 endTime)
- 付费流程:点击报名 → 若费用合计 > 0 →
order/create → 支付页/requestPayment → 成功回调 → 刷新为已报名;合计 = 0 → 直接 register 成功
- 费用展示:孩子价 / 家长价(区分会员与非会员),当前用户为会员时展示会员价(含「会员免费」)
- 手机号可选:报名表单手机号非必填;无手机号孩子可报名
- 总结中:
displayStatus=summarizing 时展示资料区(发布人可上传/删除,参与者可查看)+ 评价入口(已签到者)
- 签到按钮:仅
ongoing 显示;签到后显示已签到
6.2 index.vue
- 卡片状态改用后端
displayLabel(删除硬编码「进行中」)
- 管理 tab:「发布」按钮 → 普通用户调
submit-review(提交审核),admin/activity_admin 保留直发
- 列表自动过滤已结束活动
6.3 create.vue
- 新增字段:报名截止时间、孩子非会员价、孩子会员价、家长非会员价、家长会员价(单位:元,前端转分)
- 发布人角色放开(普通用户可进入创建页)
7. Web 管理端改动
7.1 ActivityEdit.vue
- 新增:报名截止时间 + 4 个费用字段(孩子费/孩子会员费/家长费/家长会员费,单位元)
- 价格展示不再使用
price / memberPrice
7.2 审核流(已存在,无需大改)
ActivityReview.vue 已支持审核(approved/rejected+原因);普通用户提交审核后进入待审核列表
8. 验收标准