好感度系统功能与规则设计
文档范围:本文档描述洛天依管理系统中「好感度系统」的全部功能模块与规则设计,用于产品对齐、联调对接与后续服务端落地。
当前状态:管理后台 UI 已完成(见 qy-lty-admin/app/affinity/page.tsx),后端模型骨架已就绪(见 qy_lty/userapp/models.py 第 79–115 行),但前端尚未实际调用真实接口,设备端/手机端事件尚未接入好感度逻辑。
一、系统定位
好感度系统用于刻画用户与洛天依之间的亲密度关系。其核心价值:
- 让用户的互动行为产生持续、可感知的数值反馈
- 通过等级 + 解锁内容形成长期陪伴动机
- 对不活跃用户通过衰减机制保留流失预警与召回空间
好感度是一个 [0, max_affinity] 区间的整数(默认上限 100),每个用户独立维护,记录在 ParadiseUser.favorability 字段。
二、功能模块总览
管理后台分 4 个标签页,对应 5 块功能:
| 模块 |
页签 |
功能说明 |
| 系统概览 |
系统概览 |
关键指标卡片 + 全局基础参数设置 |
| 互动规则 |
互动规则 |
管理各类互动行为的好感度变化规则 |
| 衰减规则 |
互动规则页下半 |
配置不活跃用户的好感度衰减策略 |
| 等级奖励 |
等级奖励 |
管理好感度等级划分与奖励发放 |
| 数据统计 |
数据统计 |
好感度分布、互动分析、趋势监控 |
三、模块 1 — 系统概览与全局参数
3.1 关键指标卡片
| 指标 |
含义 |
数据源 |
| 平均好感度 |
所有用户 favorability 平均值 |
聚合 ParadiseUser |
| 最高好感度 |
系统中达到上限的用户数 |
favorability = max_affinity 计数 |
| 互动次数/日 |
当天触发的所有规则次数总和 |
聚合 AffinityLog(待建) |
| 活跃用户比例 |
近 N 天有互动的用户占比 |
聚合用户活跃状态 |
3.2 全局参数(AffinitySetting)
| 字段 |
默认值 |
说明 |
initial_affinity |
10 |
新用户创建时的初始好感度 |
max_affinity |
100 |
好感度上限(所有规则累计不会超过此值) |
daily_cap |
20 |
单用户每日好感度净增长上限(跨规则汇总) |
decay_rate |
2 点/天 |
全局默认衰减速率(衰减规则可覆盖) |
decay_threshold |
3 天 |
不互动多少天后开始衰减 |
enable_notify |
true |
好感度变化是否推送通知 |
enable_rewards |
true |
是否启用等级奖励发放 |
规则:
initial_affinity 只影响新用户,不回溯修改已有用户
max_affinity 调低后,已超过的用户保留原值,但不再增加
daily_cap 是跨所有正向规则的顶层限制,触达后当日所有增益无效(衰减不受此限)
四、模块 2 — 互动规则
4.1 规则字段定义
| 字段 |
类型 |
示例 |
说明 |
id |
string |
rule-2 |
规则唯一 ID |
name |
string |
对话 |
规则展示名 |
type / rule_key |
enum |
chat |
代码标识,服务端事件通过它匹配规则,不可重复 |
description |
string |
与洛天依进行对话 |
展示描述 |
minChange |
int |
1 |
单次好感度变化最小值 |
maxChange |
int |
5 |
单次好感度变化最大值(最终取 [min, max] 的随机整数) |
singleCap |
int |
5 |
单次变化绝对值上限(保护性钳位,防止数据异常) |
dailyCap |
int |
15 |
本规则每日累计变化上限(绝对值) |
isNegative |
bool |
false |
是否负向规则。正向用正数,负向用负数 |
isEnabled |
bool |
true |
是否启用,禁用后事件不触发 |
4.2 默认规则集(8 条)
| rule_key |
名称 |
范围 |
单次/日 |
正负 |
触发来源 |
card |
使用卡片 |
+1 ~ +3 |
3 / 10 |
正 |
手机端(使用卡片 API) |
chat |
对话 |
+1 ~ +5 |
5 / 15 |
正 |
设备端 + 手机端(聊天消息) |
feed |
喂食 |
+2 ~ +8 |
8 / 16 |
正 |
手机端(喂食动作) |
touch |
抚摸 |
+1 ~ +3 |
3 / 9 |
正 |
手机端 / 设备端(抚摸信号) |
dress |
换装 |
+2 ~ +6 |
6 / 12 |
正 |
手机端(换装 API) |
prop |
使用道具 |
+1 ~ +4 |
4 / 12 |
正 |
手机端(道具使用) |
gift |
送礼物 |
+5 ~ +15 |
15 / 20 |
正 |
手机端(赠礼 API) |
decay |
无互动衰减 |
−1 ~ −3 |
3 / 5 |
负 |
定时任务 |
4.3 触发计算流程
收到事件 (user_id, rule_key, 来源上下文)
│
▼
① 取规则 → 若 is_enabled=false,丢弃
│
▼
② 冷却检查(Redis:affinity:cd:{user}:{rule_key})
│ 未到冷却 → 丢弃
▼
③ 本规则日上限检查(affinity:daily:{user}:{rule_key}:{date})
│ 已满 → 丢弃
▼
④ 全局日上限检查(正向事件才检查)
│ 已满 → 丢弃
▼
⑤ 计算变化值 = random(min, max)
↓ 按 single_cap 钳位
│
▼
⑥ 原子更新 ParadiseUser.favorability
↓ 钳位到 [0, max_affinity]
│
▼
⑦ 写 AffinityLog、更新计数器
│
▼
⑧ 判断是否跨越等级边界
│ 是 → 触发等级变更事件(发奖励 + 推通知)
▼
⑨ 通过 WebSocket 向用户的所有在线端推送 affinity_update
4.4 规则设计约定
- 单一写入入口:所有好感度变化必须经由服务端统一入口,客户端不能直接增减。
- rule_key 即契约:客户端事件不携带分值,只报「我触发了
gift 规则」,具体加多少由服务端按规则算。规则可被管理员随时调整,客户端无需改动。
- 幂等防护:同一
rule_key + 同一设备事件 ID 在冷却窗口内只生效一次,防抖防重复。
- 禁用规则的兜底:管理员禁用某规则后,客户端若继续上报该事件,服务端静默丢弃(不报错、不扣冷却)。
五、模块 3 — 衰减规则
衰减是本质为 rule_key=decay 的负向规则,但由于业务语义特殊,在后台独立配置。
5.1 衰减字段
| 字段 |
默认值 |
说明 |
decay_start_days |
3 |
不互动多少天后开始衰减 |
decay_rate_per_day |
2 点/天 |
平均每日衰减点数 |
min_decay |
1 |
单日衰减最小值 |
max_decay |
3 |
单日衰减最大值 |
decay_cap |
5 点/天 |
单日衰减上限(保护性) |
min_floor |
0 |
衰减下限,好感度不会低于此值 |
notify_decay |
true |
是否通知用户「好感度下降了」 |
5.2 衰减执行
- 频次:每日 00:30 由定时任务统一跑一次
- 命中对象:
last_active_at < now - decay_start_days 的用户
- 落库:衰减也写
AffinityLog,source='system_decay'
- 下限保护:若用户当前好感度 ≤
min_floor 则跳过
- 与互动的关系:用户当天有互动即重置不活跃计数,次日不衰减
5.3 设计权衡
- 衰减不占用
daily_cap 全局日上限(因为它是扣减,不是增益)
- 衰减日志会产生大量记录,可考虑按天合并写一条,减少
AffinityLog 膨胀
- 若「当日互动」和「当日衰减」同时命中,先执行衰减再执行互动(让用户感受到「我回来了 → 好感度止跌回升」)
六、模块 4 — 等级奖励
6.1 等级字段
| 字段 |
示例 |
说明 |
level |
3 |
等级序号,唯一 |
name |
熟悉 |
等级名 |
minAffinity / maxAffinity |
41 / 60 |
等级好感度区间(闭区间) |
unlockContent |
更多服装、特殊对话 |
文案描述,前端展示 |
rewardType |
unlock / item / currency / mixed |
奖励类型 |
rewardCurrency |
100 |
虚拟货币数量(rewardType 含 currency 时生效) |
rewardItems |
[{item_id, qty}] |
道具列表(rewardType 含 item 时生效) |
isEnabled |
true |
是否启用该等级 |
6.2 默认等级(5 档)
| 等级 |
名称 |
区间 |
解锁内容 |
| 1 |
初识 |
0 ~ 20 |
基础对话功能 |
| 2 |
相识 |
21 ~ 40 |
基础服装、道具使用 |
| 3 |
熟悉 |
41 ~ 60 |
更多服装、特殊对话 |
| 4 |
亲密 |
61 ~ 80 |
限定服装、特殊互动 |
| 5 |
挚友 |
81 ~ 100 |
专属内容、特殊剧情 |
6.3 等级变化规则
- 等级由好感度区间自动映射,不是独立字段
- 跨级判定:每次好感度变动后,取当前值所属区间,与上一次等级比较
- 升级 → 发放目标等级的奖励(只发最终落点等级,跳级不补发中间等级)
- 降级(衰减导致) → 不追回奖励,但取消解锁内容访问权限
- 奖励幂等:同一用户同一等级的「首次达到奖励」只发一次;降级后再升级不重复发放
6.4 区间约束
- 区间不得重叠,管理端保存时做校验
- 区间不得有空隙(如等级 2 的 max=40,则等级 3 的 min 必须是 41)
- 最低等级
min=0,最高等级 max=max_affinity
七、模块 5 — 数据统计
7.1 概览指标
- 平均好感度 / 中位数 / 最高好感度
- 活跃用户数(近 7 日有互动)
- 今日互动次数、今日新增好感度总量
7.2 分布分析
- 好感度区间分布(0-20、21-40…)
- 各等级用户数占比
- 各互动规则触发频次 Top N
7.3 趋势分析
- 日/周/月的平均好感度变化曲线
- 日互动量趋势
- 衰减命中用户数趋势
7.4 用户级查询
- 按用户 ID 查询其好感度当前值、等级、近期变化日志
- 管理员手动调整(加减好感度,走
source='admin_adjust' 记 log)
八、多端触发点一览
好感度变化可由以下端点触发,所有端都走同一个服务端入口:
| 触发来源 |
规则 |
通道 |
位置参考 |
| 设备端上报「用户发起对话」 |
chat |
WebSocket |
device_interaction/consumers.py chat_message |
| 设备端对话结束(陪伴时长) |
chat 或独立 companion_time |
WebSocket |
conversation_status 的 begin/end |
| 手机端点击「唱歌/跳舞/抚摸」 |
对应 rule_key |
WebSocket |
consumers.py sing / dance / touch |
| 手机端赠礼 / 喂食 / 换装 / 用道具 |
对应 rule_key |
HTTP |
对应业务 ViewSet 钩子 |
| 管理员手动调整 |
无 rule |
HTTP Admin API |
管理后台 |
| 衰减定时任务 |
decay |
后台任务 |
定时调度 |
身份识别:
- 设备端:MAC 登录获取 token → 服务端通过
UserDevice 找到 user_id
- 手机端:Redis token 认证 → 直接拿到
user_id
- 服务端只认
user_id,与触发端无关
九、数据契约(接口层摘要)
9.1 管理端
| 接口 |
方法 |
用途 |
/api/admin/affinity/rules/ |
GET/POST/PATCH/DELETE |
互动规则 CRUD |
/api/admin/affinity/levels/ |
GET/POST/PATCH/DELETE |
等级 CRUD |
/api/admin/affinity/settings/ |
GET/PUT |
全局参数(单例) |
/api/admin/affinity/logs/ |
GET |
变化日志查询(可按 user、rule、时间过滤) |
/api/admin/affinity/stats/ |
GET |
统计聚合 |
/api/admin/affinity/adjust/ |
POST |
管理员手动调整(必须留审计) |
9.2 客户端(手机端 / 设备端共用)
| 接口 |
方法 |
用途 |
/api/user/me/affinity/ |
GET |
当前好感度、等级、下一级进度、近期变化 |
/api/user/me/affinity/claim-reward/ |
POST |
领取等级奖励 |
9.3 WebSocket 实时推送
| 事件 |
方向 |
payload |
affinity_update |
服务端 → 用户 |
{change, before, after, rule_key, source} |
level_up |
服务端 → 用户 |
{old_level, new_level, reward} |
level_down |
服务端 → 用户 |
{old_level, new_level}(衰减导致降级) |
十、待开发拼板
| 模块 |
前端 |
后端模型 |
后端接口 |
触发埋点 |
| 互动规则 CRUD |
✅ UI 完成 |
⚠️ 缺字段 |
⚠️ 需扩展 |
— |
| 等级 CRUD |
✅ UI 完成 |
⚠️ 缺字段 |
⚠️ 需扩展 |
— |
| 全局设置 |
✅ UI 完成 |
❌ 无表 |
❌ 无接口 |
— |
| 衰减配置 |
✅ UI 完成 |
❌ 无表 |
❌ 无接口 |
❌ 无定时任务 |
| 变化日志 |
❌ 无 UI |
❌ 无表 |
❌ 无接口 |
— |
| 数据统计 |
⚠️ Mock 展示 |
— |
❌ 无聚合接口 |
— |
| 客户端查询 |
— |
— |
❌ 无接口 |
— |
| WS 实时推送 |
— |
— |
— |
❌ 未接入 |
| 设备/手机事件埋点 |
— |
— |
— |
❌ 未接入 |
图例:✅ 已完成 ⚠️ 部分完成 ❌ 未开始
十一、术语表
| 术语 |
含义 |
| rule_key |
互动规则的代码级标识(如 chat),客户端事件通过它匹配规则 |
| single_cap |
单次变化绝对值上限(保护性钳位) |
| daily_cap |
单规则每日累计变化上限 |
| 全局日上限 |
跨所有正向规则的顶层日增长上限(AffinitySetting.daily_cap) |
| 冷却 |
同一用户同一规则的最小触发间隔 |
| source |
变化来源:device_event / mobile_event / system_decay / admin_adjust |
| 跨级 |
好感度变化使得用户从一个等级区间移动到另一个 |