Files
danyang-match/核心业务规则、状态机与异常处理设计.md

2852 lines
35 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 丹阳公益婚恋服务平台
# V1.0 核心业务规则、状态机与异常处理设计
版本:V1.0
适用范围:微信小程序用户端、公益红娘工作台、运营管理后台
目标:保证关键业务状态一致、规则明确、异常可恢复、并发不重复、前后台行为统一
---
# 第一章 文档目标
本文件重点解决以下问题:
- 用户什么时候可以进入匹配
- 审核状态怎么流转
- 喜欢和双向喜欢如何生成
- 什么情况下可以聊天
- 拉黑、解除匹配后发生什么
- “正在了解”和“成功牵手”如何处理
- 活动报名、审核、签到如何流转
- 红娘服务如何分配和结束
- 举报、风控、封禁如何处理
- 并发操作时如何避免重复
- 异常发生后如何恢复
- 前端、后端、后台如何保持同一状态
核心原则:
> **所有关键业务都必须有明确状态机,不能通过页面临时逻辑决定业务结果。**
---
# 第二章 状态机设计总原则
所有核心状态都遵循:
```text
当前状态
↓
校验操作权限
↓
校验业务前置条件
↓
执行状态迁移
↓
写数据库
↓
触发副作用
↓
通知前端/用户
```
禁止:
```text
页面点按钮
↓
前端直接改状态
↓
后端被动接受
```
---
# 第三章 状态迁移必须服务端控制
例如:
用户不能直接提交:
```json
{
"relationshipStatus": "matched_successfully"
}
```
服务端必须验证:
- 当前是否存在有效Match
- 是否有成功牵手请求
- 对方是否确认
- 双方账号是否正常
满足后才允许状态改变。
---
# 第四章 用户总状态体系
用户平台状态由四个维度共同决定:
```text
账号状态 AccountStatus
实名状态 VerificationStatus
资料审核状态 ReviewStatus
婚恋状态 RelationshipStatus
```
四者彼此独立,但共同决定用户能力。
---
# 第五章 AccountStatus
状态:
```text
normal
reviewing_risk
limited
suspended
banned
closed
```
---
# 第六章 AccountStatus能力矩阵
| 状态 | 登录 | 查看自己 | 推荐 | 喜欢 | 聊天 | 活动 | 红娘 |
|---|---:|---:|---:|---:|---:|---:|---:|
| normal | 是 | 是 | 是 | 是 | 是 | 是 | 是 |
| reviewing_risk | 是 | 是 | 否/受限 | 否 | 受限 | 受限 | 受限 |
| limited | 是 | 是 | 受限 | 否 | 受限 | 视策略 | 视策略 |
| suspended | 是 | 是 | 否 | 否 | 否 | 否 | 否 |
| banned | 是 | 是 | 否 | 否 | 否 | 否 | 否 |
| closed | 否/仅注销提示 | 否 | 否 | 否 | 否 | 否 | 否 |
实际限制由RiskAction进一步决定。
---
# 第七章 VerificationStatus
```text
not_verified
verifying
verified
failed
manual_review
```
只有:
```text
verified
```
才能进入资料完整填写和正式匹配流程。
---
# 第八章 Profile Review状态
```text
not_submitted
pending
processing
approved
need_edit
rejected
```
---
# 第九章 审核状态机
```text
not_submitted
↓ 提交
pending
↓ 领取
processing
├──────────────┐
↓ ↓
approved need_edit
↓ 用户修改重提
pending
processing
↓ 严重违规
rejected
```
---
# 第十章 审核允许迁移
允许:
```text
not_submitted → pending
pending → processing
processing → approved
processing → need_edit
processing → rejected
need_edit → pending
```
---
# 第十一章 审核禁止迁移
禁止:
```text
approved → pending
```
直接由普通前端操作触发。
如果用户修改敏感资料:
应创建:
> 新的ReviewTask
而不是把旧审核记录改回pending。
---
# 第十二章 敏感资料修改规则
以下修改必须重新审核:
- 主头像
- 生活照片
- 自我介绍
- 婚姻状态
- 其他人工审核字段
新流程:
```text
原资料 approved
↓
用户修改
↓
创建 profile_change review
↓
新内容待审核
```
---
# 第十三章 修改期间展示规则
建议V1采用:
> 已审核旧版本继续对外展示,新版本审核通过后替换。
这样避免用户修改一句自我介绍后:
> 整个账号退出匹配池。
但以下情况例外:
### 婚姻状态变更
如果从:
> 未婚 → 离异等重大变化
建议立即暂停推荐,待重新审核。
---
# 第十四章 审核领取并发规则
两个审核员同时点击领取。
仅第一个成功。
数据库:
```text
UPDATE review_tasks
SET status='processing'
WHERE id=?
AND status='pending'
```
影响行数:
```text
1 = 成功
0 = 已被别人领取
```
前端提示:
> 该任务已由其他工作人员领取。
---
# 第十五章 审核超时规则
如果审核员领取后长时间未处理,例如:
> 30分钟或运营配置时间
可以:
- 提醒审核员
- 管理员重新分配
不要自动直接把任务变回pending导致重复操作。
---
# 第十六章 RelationshipStatus
```text
searching
getting_to_know
paused
matched_successfully
service_exited
```
---
# 第十七章 婚恋状态迁移
```text
searching
├→ paused
├→ getting_to_know
└→ service_exited
paused
├→ searching
└→ service_exited
getting_to_know
├→ searching
├→ paused
├→ matched_successfully
└→ service_exited
matched_successfully
└→ 特殊人工恢复/关系结束流程
service_exited
└→ 用户重新启用婚恋服务(后续规则)
```
---
# 第十八章 searching规则
用户在searching状态:
允许:
- 出现在推荐池
- 主动找对象
- 喜欢别人
- 被别人喜欢
- 接受红娘推荐
- 参加活动
前提:
> 账号和审核状态正常。
---
# 第十九章 paused规则
paused时:
```text
recommendation_enabled = false
visible_in_recommendations = false
```
默认:
> 不进入推荐池。
已有聊天:
> 保留。
已有Match:
> 保留。
---
# 第二十章 getting_to_know规则
用户设置:
> 正在了解
时必须明确是否:
```text
pauseOtherRecommendations = true/false
```
如果true:
- 不再生成新推荐
- 不再出现在找对象列表
- 已有匹配关系保留
如果false:
> 仍允许继续使用匹配功能。
---
# 第二十一章 matched_successfully规则
双方确认成功牵手后:
```text
relationship_status = matched_successfully
recommendation_enabled = false
visible_in_recommendations = false
```
双方:
- 不再收到新推荐
- 不再出现在找对象
- 不再接受普通红娘推荐
- 已有聊天保留
---
# 第二十二章 成功牵手不可单方完成
流程:
```text
A发起成功牵手
↓
status = pending
↓
B收到确认
├→ 确认
│ ↓
│ successful
│
└→ 拒绝
↓
declined
```
只有确认后:
> 双方婚恋状态一起更新。
---
# 第二十三章 成功牵手并发规则
A和B可能同时发起。
系统必须识别:
> 同一个Match已有pending成功请求。
第二个请求:
可以直接合并成确认。
不要生成两条独立成功请求。
---
# 第二十四章 成功牵手取消问题
V1不建议用户随意一键取消“成功牵手”。
如果关系结束并重新寻找:
建议走:
> 重新开启婚恋服务
并记录历史。
不能删除之前成功记录。
---
# 第二十五章 用户关系基础状态
单向关系:
```text
neutral
liked
passed
blocked
```
双向关系:
```text
mutual_match
```
---
# 第二十六章 Like状态机
初始:
```text
neutral
```
A喜欢B:
```text
neutral → liked
```
如果B已经喜欢A:
```text
liked + reverse liked
↓
mutual_match
```
---
# 第二十七章 Like前置条件
必须同时满足:
- A账号正常
- B账号正常
- A资料审核通过
- B资料审核通过
- A当前允许寻找
- B当前允许被发现
- A没有拉黑B
- B没有拉黑A
- A != B
---
# 第二十八章 Like禁止场景
禁止:
- 喜欢自己
- 喜欢已封禁用户
- 喜欢被自己拉黑用户
- 喜欢已经拉黑自己的用户
- 审核未通过用户点击喜欢
- 已退出婚恋服务用户点击喜欢
---
# 第二十九章 重复喜欢
A已经liked B。
再次请求:
> 不创建新数据。
返回:
```text
ALREADY_LIKED
```
或者幂等返回:
```text
relationStatus = liked
```
推荐采用:
> 幂等成功。
前端体验更稳定。
---
# 第三十章 取消喜欢
仅适用于:
> 尚未形成mutual_match。
如果已经双向匹配:
不能:
> 取消喜欢来解除关系。
必须:
> 解除匹配。
---
# 第三十一章 Pass规则
A点击不合适:
```text
neutral / liked未匹配
→ passed
```
如果A之前liked但尚未匹配:
可以:
> 覆盖为passed
并清除/失效liked。
实现上建议保留历史事件,但当前关系以passed为准。
---
# 第三十二章 Pass冷却
用户pass后:
系统在配置周期内不再推荐。
例如:
> 180天。
后续可以允许重新出现。
但blocked:
> 永久排除,直到解除拉黑。
---
# 第三十三章 Mutual Match状态机
当双方liked:
```text
no_match
↓
active
```
之后:
```text
active
├→ getting_to_know
├→ unmatched
└→ successful
```
---
# 第三十四章 Mutual Match唯一规则
一对用户只能存在:
> 一个当前Match实体。
标准化:
```text
userA = min(user1,user2)
userB = max(user1,user2)
```
数据库唯一约束。
---
# 第三十五章 同时点赞并发
A和B同一毫秒点击喜欢。
两边事务都可能检测。
最终依赖:
- user_relation唯一约束
- match pair唯一约束
- duplicate key捕获
保证:
> 只创建一个Match。
---
# 第三十六章 Match创建后副作用
Match成功后:
主事务必须完成:
- Match记录
- Conversation记录
- ConversationMembers
事务提交后:
- 创建系统通知
- WebSocket通知
- 统计+1
通知失败:
> 不能回滚Match。
---
# 第三十七章 Chat开启规则
只有:
```text
match.status = active
OR getting_to_know
```
且:
- 双方账号可用
- 双方未拉黑
- conversation.can_send = true
才能聊天。
---
# 第三十八章 没有双向喜欢不能聊天
无论用户是否:
- 搜索到对方
- 被红娘推荐
- 活动认识
- 单向喜欢
都不能直接聊天。
必须形成:
> 双方明确同意。
---
# 第三十九章 红娘推荐与聊天关系
红娘推荐:
```text
红娘 → 用户A推荐B
```
不创建:
- like
- match
- conversation
A点击喜欢后:
仍然等B喜欢A。
---
# 第四十章 活动互选与聊天
活动结束:
A选择B感兴趣。
B也选择A。
系统可以直接视为:
> 双方明确意向
因此可以生成Match。
但前端应明确告知:
> 你们在本次活动中互相选择,可以开始交流。
---
# 第四十一章 Conversation状态
建议:
```text
active
closed
```
不需要V1搞复杂。
---
# 第四十二章 Conversation关闭场景
以下事件关闭发送能力:
- 解除匹配
- 任一方拉黑
- 任一方账号封禁
- 账号关闭
历史聊天可以保留。
---
# 第四十三章 Unmatch规则
任何一方可以解除匹配。
请求:
```text
match.status = active/getting_to_know
```
执行:
```text
match.status → unmatched
conversation.can_send → false
```
---
# 第四十四章 解除匹配后的推荐
默认:
双方进入较长排除期。
建议:
> 不再主动推荐。
是否未来重新出现,可配置。
V1建议:
> 默认长期排除。
---
# 第四十五章 解除匹配不等于拉黑
解除匹配:
> 不继续了解。
拉黑:
> 不希望对方再看到/联系自己。
两者必须分开。
---
# 第四十六章 Block状态机
```text
neutral / liked / mutual_match / unmatched
↓
blocked
```
拉黑优先级最高。
---
# 第四十七章 拉黑后的即时副作用
必须立即:
- 停止双方聊天
- 移除推荐
- 移除搜索可见关系
- 移除喜欢列表展示
- 红娘候选排除
- 活动推荐关系排除
---
# 第四十八章 解除拉黑
解除后:
```text
blocked → neutral
```
但:
- 不恢复旧Like
- 不恢复旧Match
- 不恢复旧Conversation发送能力
如果以后重新匹配:
> 重新走喜欢流程。
---
# 第四十九章 举报与拉黑关系
举报不自动拉黑。
用户可以:
> 举报后选择是否同时拉黑。
严重风险场景前端可以提示:
> 是否同时拉黑该用户?
---
# 第五十章 Message状态机
客户端消息:
```text
local
↓
sending
├→ sent
└→ failed
```
服务端消息:
```text
accepted
↓
persisted
↓
delivered(可选)
↓
read
```
V1数据库可只保留:
> sent/read相关必要状态。
---
# 第五十一章 消息幂等规则
每条发送请求:
```text
sender_id + client_message_id
```
唯一。
弱网重发:
返回原消息。
不能生成重复文本。
---
# 第五十二章 消息发送前置校验
发送前必须校验:
1. Conversation存在
2. 当前用户属于Conversation
3. Conversation可发送
4. Match有效
5. 双方未拉黑
6. 双方账号正常
7. 内容合法
8. 频率未超限
---
# 第五十三章 Message发送异常恢复
情况:
> 数据库已经保存,但WebSocket返回失败。
客户端重试同一clientMessageId:
服务端查询原消息并返回。
这样避免:
> 用户看到重复消息。
---
# 第五十四章 未读数规则
接收方新消息:
```text
unread_count + 1
```
进入会话并读到:
```text
last_read_message_id
```
服务端计算/更新:
> unread_count。
---
# 第五十五章 已读并发
客户端可能重复调用read。
接口必须幂等。
只允许:
> lastReadMessage位置向前推进。
不能因为旧请求晚到:
> 把已读位置回退。
---
# 第五十六章 聊天风险提示规则
检测到普通风险关键词:
> 提醒,不拦截。
例如:
- 转账
- 借钱
- 投资
系统插入:
> risk_notice。
---
# 第五十七章 严重消息风险
如果命中明显诈骗模板、高频群发等:
可以:
- 创建RiskEvent
- 提高风险分
- 临时限速
- 进入人工关注
但V1不建议:
> 单凭一个关键词直接永久封禁。
---
# 第五十八章 Activity状态机
```text
draft
↓ 发布
registration_open
↓ 截止
registration_closed
↓ 开始
ongoing
↓ 结束
ended
```
另有:
```text
draft / registration_open
↓
cancelled
```
---
# 第五十九章 活动状态迁移规则
允许:
```text
draft → registration_open
registration_open → registration_closed
registration_closed → ongoing
ongoing → ended
```
管理员取消:
```text
draft → cancelled
registration_open → cancelled
registration_closed → cancelled
```
---
# 第六十章 活动禁止迁移
禁止普通操作:
```text
ended → registration_open
cancelled → ongoing
```
如果运营误操作:
> 应复制创建新活动,而不是倒退旧活动。
---
# 第六十一章 ActivityRegistration状态机
```text
not_applied
↓
pending
├→ approved
├→ rejected
└→ cancelled
```
若活动无需审核:
```text
not_applied → approved
```
---
# 第六十二章 报名前置条件
必须:
- 用户实名
- 用户审核通过
- 账号正常
- 活动报名中
- 年龄符合
- 未重复报名
- 名额可用
---
# 第六十三章 活动年龄规则
年龄必须:
> 服务端根据birth_date计算。
不能相信前端传age。
---
# 第六十四章 活动报名并发
如果最后一个名额:
多人同时报名。
必须在事务中:
- 锁定活动或计数资源
- 检查剩余名额
- 写报名
最终:
> 不能超额。
---
# 第六十五章 待审核是否占名额
V1必须明确规则。
建议:
> pending暂不占正式名额,approved才占名额。
原因:
工作人员需要审核。
但这样可能:
> 待审核人数大于剩余名额。
后台应提示:
> 通过时按剩余名额校验。
---
# 第六十六章 无需审核活动
如果:
```text
need_review = false
```
报名事务中直接:
> approved并占名额。
---
# 第六十七章 报名拒绝
拒绝后:
```text
pending → rejected
```
必须记录原因。
用户收到通知。
---
# 第六十八章 活动取消
活动取消后:
- 所有用户收到通知
- 报名记录保留
- 签到关闭
- 不物理删除报名
状态:
```text
activity = cancelled
```
---
# 第六十九章 签到前置条件
只有:
- activity有效
- registration = approved
- 未签到
- token有效
才可签到。
---
# 第七十章 重复签到
重复扫码:
返回:
> 已签到 + 原签到时间。
不要报系统错误。
---
# 第七十一章 签到Token规则
签到二维码Token:
- 短时有效
- 签名
- 不直接暴露userId
- 与activity绑定
过期:
> 客户端重新获取。
---
# 第七十二章 活动反馈规则
只有:
- 已报名
- 活动已结束
才允许提交反馈。
是否必须签到:
建议:
> 只有已签到用户可提交完整活动反馈。
未签到用户可提交:
> 未参与原因(以后可加)。
---
# 第七十三章 活动互选规则
用户只能选择:
- 同一活动
- 实际允许被选择的参与者
- 未被拉黑
- 账号正常
不能通过修改userId选择任意平台用户。
---
# 第七十四章 MatchmakerService状态机
```text
submitted
↓ 分配
assigned
↓ 开始服务
in_service
├→ waiting_user
│ ↓
│ in_service
└→ completed
submitted/assigned
→ cancelled
```
---
# 第七十五章 红娘服务申请规则
用户可以申请:
- 推荐对象
- 调整择偶条件
- 联系意向对象
- 活动帮助
- 匹配困难
- 沟通帮助
同类型服务若已有:
> in_service
建议限制重复提交。
返回:
> SERVICE_ALREADY_ACTIVE
---
# 第七十六章 红娘接单并发
两位红娘同时接单。
采用数据库原子更新。
只允许一个成功。
失败:
> 该服务已由其他红娘接单。
---
# 第七十七章 红娘服务权限
红娘只能操作:
- 自己被分配的服务
- 或所在机构授权服务
不能通过修改serviceId查看其他机构数据。
---
# 第七十八章 红娘推荐前置条件
推荐B给A前必须:
- A服务有效
- B审核通过
- B账号正常
- B可被推荐
- 双方无拉黑
- 双方required条件不冲突
---
# 第七十九章 红娘不能突破硬条件
即便红娘认为“很合适”。
如果用户设置:
> 必须符合
而候选人不符合:
系统不允许推荐。
除非:
> 用户本人先修改条件。
---
# 第八十章 红娘服务完成
完成前建议至少:
- 有跟进记录
- 或有明确处理结果
例如:
```text
已推荐3位
用户暂时暂停服务
用户已成功牵手
未找到合适对象
```
---
# 第八十一章 红娘超时提醒
如果服务:
> 3天/配置时间未跟进
后台显示:
> 超时待跟进。
不自动结束服务。
---
# 第八十二章 Report状态机
```text
pending
↓ 受理
processing
├→ confirmed
├→ rejected
└→ insufficient_evidence
```
---
# 第八十三章 举报结果与处置分离
Report结果:
> 举报是否成立。
RiskAction:
> 对用户采取什么措施。
不要把二者混成一个status。
例如:
```text
report = confirmed
riskAction = warning
```
或:
```text
report = confirmed
riskAction = ban
```
---
# 第八十四章 RiskLevel状态
```text
normal
watch
high
critical
```
风险等级用于:
> 排序和关注。
不是直接等于账号状态。
---
# 第八十五章 风险处置状态
RiskAction:
```text
warning
feature_limit
temporary_suspend
ban
manual_investigation
```
---
# 第八十六章 风控副作用
例如 temporary_suspend:
必须同步:
- AccountStatus
- Match推荐排除
- Like禁止
- Chat发送禁止
- 活动报名禁止
- 红娘服务限制
---
# 第八十七章 风控处置事务
建议主事务:
```text
创建RiskAction
↓
更新User AccountStatus
↓
写AuditLog
```
事务后:
- WebSocket通知客户端
- Notification通知用户
- 缓存失效
---
# 第八十八章 风控状态实时生效
不能等用户下次登录。
如果用户正在聊天时被封禁:
下一次:
- WebSocket状态事件
- 或任何API响应
必须让客户端更新状态并停止发送。
---
# 第八十九章 Appeal申诉状态机
```text
pending
↓
processing
├→ approved
└→ rejected
```
approved:
可能:
- 撤销RiskAction
- 恢复AccountStatus
但必须保留原始风险记录。
---
# 第九十章 风控误判恢复
撤销封禁:
```text
risk_action.revoked_at = now
```
更新用户:
> 根据仍然生效的其他RiskAction重新计算最终AccountStatus。
不能简单:
```text
account_status = normal
```
因为可能还有其他限制。
---
# 第九十一章 Notification规则
业务状态变化后产生通知。
典型事件:
- 审核通过
- 审核退回
- 双向喜欢
- 红娘推荐
- 活动审核通过
- 活动取消
- 风险处置
- 成功牵手确认
---
# 第九十二章 通知失败不影响主业务
例如:
> 活动报名已审核通过
Notification服务失败。
不能:
> 把报名状态回滚。
应:
- 业务成功
- 通知进入补偿队列/任务
---
# 第九十三章 通知去重
相同业务事件:
例如同一match:
> 不得重复发送10次“互相喜欢”。
建议:
```text
event_type + business_id + user_id
```
做去重Key。
---
# 第九十四章 用户删除/注销状态机
V1建议:
```text
normal account
↓ 用户申请注销
closing(可选)
↓ 完成
closed
```
如果不做等待期:
可以直接:
> closed
但仍需服务端完成数据退出流程。
---
# 第九十五章 注销后的副作用
必须:
- 停止推荐
- 隐藏资料
- 禁止Like
- 禁止新消息
- 停止红娘服务
- 取消未开始活动报名视业务处理
- 关闭登录会话
---
# 第九十六章 注销不删除历史关系
保留:
- Match历史
- 举报
- 审核
- 审计
- 活动记录
展示时:
> 已注销用户
而不是查询失败导致数据链断裂。
---
# 第九十七章 Session状态异常
Token过期:
```text
AUTH_EXPIRED
```
前端:
- 保存安全草稿
- 尝试refresh
- refresh失败才回登录
---
# 第九十八章 服务端状态比前端新
例如前端显示:
> 审核通过
但后台刚封禁。
任何关键API:
> 必须重新校验数据库/缓存中的最新账号状态。
不能因为前端旧状态继续执行。
---
# 第九十九章 缓存一致性规则
关键状态变化:
必须主动失效相关缓存。
例如拉黑:
```text
推荐缓存
用户详情缓存
关系缓存
红娘候选缓存
```
都需要失效。
---
# 第一百章 推荐缓存不能绕过状态校验
即使推荐列表已经缓存:
返回给客户端之前仍需快速检查:
- target账号
- target审核状态
- 是否拉黑
- 是否暂停曝光
---
# 第一百零一章 用户详情失效
用户打开详情时:
如果对方刚被封禁。
返回:
```text
TARGET_UNAVAILABLE
```
前端:
> 该用户当前不可查看。
---
# 第一百零二章 关键业务错误分类
分为:
```text
可重试错误
不可重试业务错误
权限错误
状态冲突
系统错误
```
---
# 第一百零三章 可重试错误
例如:
- 网络超时
- 短暂数据库连接失败
- 对象存储临时失败
前端:
> 可以重试。
---
# 第一百零四章 不可重试业务错误
例如:
```text
PROFILE_NOT_APPROVED
ACTIVITY_FULL
BLOCKED_RELATION
TARGET_UNAVAILABLE
```
前端不能无限重试。
应改变页面状态。
---
# 第一百零五章 状态冲突
例如:
审核员操作:
> 任务已被别人处理。
返回:
```text
STATE_CONFLICT
```
前端:
> 刷新当前数据。
---
# 第一百零六章 系统错误
后端500:
前端统一:
> 操作暂时未完成,请稍后重试。
不能展示:
- SQL异常
- Java堆栈
- Node错误对象
---
# 第一百零七章 幂等业务总览
必须幂等:
```text
Like
Pass
Block
提交审核
活动报名
活动签到
红娘申请
消息发送
举报
成功牵手请求
```
---
# 第一百零八章 幂等成功优先
如果同一请求重复到达,但第一次已经成功:
推荐返回:
> 原成功结果
而不是:
> “重复错误”。
这样弱网体验更好。
---
# 第一百零九章 数据库唯一约束兜底
关键唯一约束:
- user relation
- match pair
- activity registration
- message client ID
- conversation match ID
应用逻辑不能代替数据库约束。
---
# 第一百一十章 事务与副作用边界
原则:
> 核心业务数据在事务内。
> 通知、统计、推送等副作用在事务后。
---
# 第一百一十一章 双向喜欢事务
事务内:
```text
写liked
检查reverse liked
创建match
创建conversation
创建members
```
事务后:
```text
通知
WebSocket
统计
```
---
# 第一百一十二章 活动报名事务
事务内:
```text
检查活动
检查名额
创建/更新registration
```
事务后:
```text
发送报名通知
统计
```
---
# 第一百一十三章 举报处理事务
事务内:
```text
更新report
创建risk event
创建risk action
更新account status
audit
```
事务后:
```text
通知
WebSocket
缓存失效
```
---
# 第一百一十四章 超时补偿任务
建议至少有:
- 通知补发
- 活动状态补正
- 审核超时提醒
- 红娘跟进超时提醒
- 风险状态重算
- 缓存重建
---
# 第一百一十五章 失败补偿原则
如果副作用失败:
> 可重试。
如果核心事务失败:
> 整体回滚。
不要出现:
> Match没创建,但发了“互相喜欢”通知。
---
# 第一百一十六章 软删除业务规则
用户照片删除:
> soft delete。
活动删除:
> 如果草稿可soft delete。
已发布活动:
> 优先cancelled。
不能物理删除造成历史断裂。
---
# 第一百一十七章 状态操作审计范围
后台以下动作必须Audit:
- 审核通过/退回
- 用户封禁/解封
- 风控处置
- 红娘分配
- 活动取消
- 权限调整
- 查看敏感数据
- 匹配规则修改
---
# 第一百一十八章 普通用户操作日志
不需要所有点击都做Audit。
但关键业务事件应留业务记录:
- Like
- Match
- Unmatch
- Block
- 举报
- 成功牵手
---
# 第一百一十九章 异常场景一:喜欢时对方被封禁
流程:
```text
A打开旧详情
↓
B刚被封禁
↓
A点击喜欢
↓
服务端重新校验
↓
TARGET_UNAVAILABLE
```
前端:
> 该用户当前不可继续了解。
并移除卡片。
---
# 第一百二十章 异常场景二:双方同时解除/拉黑
A解除匹配,B同时拉黑A。
最终:
- Match = unmatched
- Block关系存在
- Conversation关闭
Block优先级高。
---
# 第一百二十一章 异常场景三:活动最后一个名额
A和B同时审核通过。
数据库事务保证:
> 只有一个通过。
另一个:
```text
ACTIVITY_FULL
```
工作人员界面刷新。
---
# 第一百二十二章 异常场景四:报名时活动被取消
客户端打开旧页面。
提交报名。
服务端:
> 检查activity.status
返回:
```text
ACTIVITY_NOT_OPEN
```
前端刷新活动详情。
---
# 第一百二十三章 异常场景五:聊天中对方拉黑
A正在输入。
B拉黑A。
A发送:
```text
BLOCKED_RELATION
```
前端:
- 停止发送
- 关闭输入框
- 显示“当前无法继续聊天”
---
# 第一百二十四章 异常场景六:审核中用户修改资料
V1建议:
审核pending/processing期间:
> 禁止修改正在审核的关键字段。
提示:
> 资料正在审核中,审核完成后可继续修改。
避免审核版本混乱。
---
# 第一百二十五章 异常场景七:红娘候选失效
红娘打开候选B。
B随后暂停寻找。
红娘点击推荐:
服务端重新校验。
返回:
```text
TARGET_UNAVAILABLE
```
候选从列表移除。
---
# 第一百二十六章 异常场景八:成功牵手确认时Match已解除
A发起成功。
期间双方解除匹配。
B再点击确认。
服务端:
```text
MATCH_NOT_ACTIVE
```
成功请求标记:
> cancelled/invalid。
---
# 第一百二十七章 异常场景九:用户注销时有未开始活动
V1建议:
注销前提示:
> 你有已报名活动,注销后报名将自动取消。
用户确认注销后:
- 未开始活动报名 → cancelled
- 已结束活动记录保留
---
# 第一百二十八章 异常场景十:管理员误重复处理举报
第二次resolve:
如果Report已经final状态:
返回:
```text
REPORT_ALREADY_RESOLVED
```
不能创建第二个重复RiskAction。
---
# 第一百二十九章 业务优先级冲突规则
当多个状态冲突时:
优先级建议:
```text
账号安全状态
>
拉黑关系
>
审核状态
>
婚恋状态
>
普通Match关系
>
UI偏好
```
---
# 第一百三十章 示例:用户审核通过但被封禁
即使:
```text
review = approved
```
但:
```text
account = banned
```
最终:
> 禁止匹配和聊天。
AccountStatus优先。
---
# 第一百三十一章 示例:双方Match但一方拉黑
即使:
```text
match = active
```
但:
```text
block = true
```
最终:
> 不能聊天。
Block优先。
---
# 第一百三十二章 示例:正在了解但用户主动暂停
如果:
```text
relationship = getting_to_know
```
并且:
```text
recommendation_enabled = false
```
最终:
> 不推荐新人,但保留已有聊天。
---
# 第一百三十三章 状态最终来源
最终业务真相来自:
> PostgreSQL。
Redis:
> 缓存。
前端Store:
> 展示。
任何冲突:
> 以服务端数据库状态为准。
---
# 第一百三十四章 状态更新时间
所有状态变化必须包含:
```text
updated_at
```
关键状态应额外记录:
```text
approved_at
matched_at
unmatched_at
successful_at
processed_at
```
方便审计和统计。
---
# 第一百三十五章 状态变更原因
以下状态必须记录原因:
- 审核need_edit/rejected
- 解除匹配
- 举报处理
- 风控处置
- 活动拒绝
- 账号封禁
- 申诉处理
不能只有状态没有原因。
---
# 第一百三十六章 用户可见原因与后台原因分离
例如封禁:
后台内部:
> 多次举报+疑似诈骗话术。
用户前台:
> 账号因安全风险暂时受限。
必要时再显示合规的具体说明。
避免泄露举报人信息。
---
# 第一百三十七章 状态机测试策略
每一个状态机至少测试:
- 正常前向迁移
- 非法逆向迁移
- 重复请求
- 并发请求
- 权限不足
- 对象失效
- 网络重试
---
# 第一百三十八章 审核状态测试
必须覆盖:
```text
not_submitted → pending
pending → processing
processing → approved
processing → need_edit
need_edit → pending
```
同时测试:
```text
approved → pending
```
被拒绝。
---
# 第一百三十九章 Match测试
必须覆盖:
- 单向Like
- 双向Like
- 同时Like
- 重复Like
- 拉黑后Like
- Unmatch后聊天
- 成功牵手确认
---
# 第一百四十章 活动测试
必须覆盖:
- 正常报名
- 重复报名
- 最后名额并发
- 审核通过
- 活动取消
- 重复签到
- Token过期
---
# 第一百四十一章 红娘测试
必须覆盖:
- 重复申请
- 两人同时接单
- 推荐失效用户
- 红娘越权查看
- 已完成服务再次操作
---
# 第一百四十二章 风控测试
必须覆盖:
- 举报成立
- 举报不成立
- 多次举报
- 封禁实时生效
- 解封
- 申诉通过
- 多个RiskAction同时存在
---
# 第一百四十三章 前后端状态码统一
前端不要自己推断:
> “按钮灰了所以应该是审核中”。
后端明确返回:
```text
reviewStatus
accountStatus
relationshipStatus
relationStatus
activityStatus
serviceStatus
```
---
# 第一百四十四章 状态字典统一
所有枚举建议放共享文档/代码包。
例如:
```text
AccountStatus
ReviewStatus
RelationshipStatus
MatchStatus
RegistrationStatus
MatchmakerServiceStatus
ReportStatus
RiskLevel
```
避免前后端分别定义不同字符串。
---
# 第一百四十五章 V1.0不需要复杂状态机框架
无需引入:
- Temporal
- Camunda
- 工作流引擎
- BPMN平台
当前业务使用:
> 枚举 + Service规则 + 数据库约束
足够。
---
# 第一百四十六章 什么时候未来考虑工作流引擎
只有当未来出现:
- 多级审批
- 跨机构复杂审批
- 大量动态流程
- 可配置工作流
才考虑。
V1不需要。
---
# 第一百四十七章 核心业务规则汇总
## 用户进入推荐的必要条件
```text
已登录
+
已绑定手机号
+
已实名
+
资料审核通过
+
账号正常
+
婚恋状态允许
+
隐私允许
```
---
# 第一百四十八章 用户聊天必要条件
```text
双方互相喜欢
+
Match有效
+
Conversation有效
+
双方账号正常
+
双方未拉黑
```
---
# 第一百四十九章 红娘推荐必要条件
```text
服务有效
+
候选人审核通过
+
双方账号正常
+
双方未拉黑
+
双方Required条件不冲突
```
---
# 第一百五十章 活动报名必要条件
```text
活动开放
+
用户实名
+
资料审核通过
+
账号正常
+
年龄符合
+
未重复报名
+
名额可用
```
---
# 第一百五十一章 成功牵手必要条件
```text
有效Match
+
一方发起
+
另一方确认
+
双方账号有效
```
---
# 第一百五十二章 风控最高优先级原则
任何业务规则都必须让位于:
> 用户安全。
即使用户:
- 已匹配
- 已报名活动
- 已有红娘服务
一旦确认严重风险:
平台有权:
- 暂停功能
- 停止聊天
- 停止活动参与
- 进入人工处理
同时保留申诉机制。
---
# 第一百五十三章 用户自主决定原则
任何系统和工作人员都不能替用户:
- 喜欢某人
- 开启聊天
- 确认成功牵手
红娘和算法:
> 只能推荐与辅助。
最终决定:
> 永远属于用户本人。
---
# 第一百五十四章 隐私优先原则
如果隐私设置与推荐规则冲突:
> 隐私设置优先。
例如:
用户关闭:
```text
visible_in_search = false
```
即使匹配分100:
> 也不能出现在主动搜索结果。
---
# 第一百五十五章 V1.0最终状态机总图
```text
用户注册
↓
实名
↓
完善资料
↓
人工审核
↓
审核通过
↓
正在寻找
↓
系统推荐/主动搜索/红娘/活动
↓
喜欢
↓
双向喜欢
↓
聊天
↓
正在了解
↓
成功牵手
```
任何阶段都有安全旁路:
```text
举报
↓
风险审核
↓
限制 / 暂停 / 封禁
```
任何阶段用户都可以:
```text
暂停寻找
或
退出婚恋服务
```
---
# 第一百五十六章 最终设计原则
整个V1.0必须做到:
> **每一个状态都有明确来源。**
> **每一次状态迁移都有前置条件。**
> **每一个关键动作都能防重复。**
> **每一种异常都有明确恢复方式。**
> **任何并发都不能制造重复Match、重复报名、重复消息。**
> **任何后台状态变化都能及时影响用户端。**
> **红娘、活动、系统推荐不能突破用户本人设置的硬条件和自主决定权。**
> **账号安全与隐私规则永远高于普通业务便利性。**
最终目标不是把状态机设计得复杂,而是保证:
> **任何一个异常情况发生时,系统都知道“现在是什么状态、下一步允许做什么、禁止做什么、怎么恢复”。**