178 lines
11 KiB
Markdown
178 lines
11 KiB
Markdown
# 数据库变更台账
|
||
|
||
> **数据库改动只写这里**:中文标题 + 完整可执行 SQL。不再单独建 `docs/*.sql`(全量基线除外)。
|
||
> 只记**变更 SQL**;**不写回滚 SQL** —— 已执行的条目即为既成事实,要撤销请从本文件的 git 历史取旧版本。
|
||
> 若某条变更依赖**尚未执行**的前置对象(建表、种子数据等),必须在条目标注清楚,别让后来者踩空。
|
||
|
||
---
|
||
|
||
## 头像库「是否默认」唯一化:`tt_user_avatar` 存量数据清洗
|
||
|
||
**背景**:`tt_user_avatar.is_default` 的语义是「新用户 / 白号注册时分配的初始头像」,
|
||
**全表必须最多只有一条 `is_default='1'`**。但后台「设置 → 头像管理」的开关写成默认时,
|
||
后端只做了 `updateById`,没有把其他记录的默认标记清掉 ⇒ 库中一度累积出 **7 条默认**(id 63~69),
|
||
`id=62` 反而是「否」。后果:后台看到一排「是」容易误解(用户 2026-09-24 反馈),
|
||
且 C 端注册处(`ApiRegisterServiceImpl` / `PromoLoginServiceImpl`)用的是
|
||
`list(is_default='1')` + `RandomUtil.choice(...)` **随机挑一条**,导致默认头像实际不唯一、每次注册拿到的都不同。
|
||
|
||
**代码侧修复**(非 SQL,见 `docs/修改记录.md` 同日条目):`TtUserAvatarServiceImpl`
|
||
在 `updateUserAvatarById` / `saveAvatar` 中增加互斥逻辑 —— 任何把某条置为 `is_default='1'` 的写入,
|
||
都先把其余记录清成 `'0'`,两步同一事务。
|
||
|
||
**本次数据库变更 1 处**(2026-09-24 已在库 `henan` 执行):把多余的默认标记收敛为**只保留 id 最小的一条**(id=63)。
|
||
|
||
**变更 SQL**:
|
||
|
||
```sql
|
||
UPDATE `tt_user_avatar`
|
||
SET `is_default` = '0'
|
||
WHERE `is_default` = '1'
|
||
AND `id` <> (SELECT `min_id` FROM (
|
||
SELECT MIN(`id`) AS `min_id` FROM `tt_user_avatar` WHERE `is_default` = '1'
|
||
) AS `t`);
|
||
```
|
||
|
||
> ⚠️ MySQL 不允许在 `UPDATE` 的子查询里直接查同一张表,故此处包了一层派生表 `t` 绕过;
|
||
> 该写法的坑已记在 `.workbuddy/memory/` 当日日志里,后续同类 SQL 照此办理。
|
||
|
||
**验证口径**:执行后 `SELECT is_default, COUNT(*) FROM tt_user_avatar GROUP BY is_default;`
|
||
应当只有两行 —— `0 → N`(N = 总数-1)、`1 → 1`。
|
||
|
||
---
|
||
|
||
## 幸运升级·水位策略简化为「单一目标水位」:`sys_config` 配置结构变更
|
||
|
||
**背景**:上一版把水位策略做成了 **4 档波段表**(每档 = 水位下界/上界 + 随机数下界/上界),
|
||
后台要维护 16 个数、还要保证档间单调且留有重叠,运营难维护也容易配错。本次简化为**只填一个「目标水位(%)」**:
|
||
随机数的**中心**由目标水位自动反推,**取值范围**仍由每个饰品自己的「随机数区间」限定
|
||
(水位高于目标 → 落点靠近区间下限)。**口径推导、换算表、仿真数据与算例见 `docs/修改记录.md` 的 2026-09-24 条。**
|
||
|
||
**本次数据库变更 1 处**(2026-09-24 已在库 `henan` 执行):`sys_config.config_key = 'luckyUpgradeWatermark'` 的
|
||
`config_value` 由「波段表」改为「目标水位 + 灵敏度」,`bands` 字段废弃(后端已不再读取,留着不报错)。
|
||
|
||
> ⚠️ **前置依赖**:本条的 `UPDATE` 依赖下一节「幸运升级·水位驱动改造」插入的那行 `sys_config`
|
||
> —— 全新库必须先执行那一条,再执行本条。
|
||
>
|
||
> ⚠️ **同时恢复**(代码侧,非 SQL):饰品列表页的「随机数区间」编辑入口**重新启用**,
|
||
> `tt_upgrade_ornaments.luck_section` / `anchor_luck_section` / `blank_luck_section` 现在**必须配置**,
|
||
> 且要**包含按目标水位推算出的中心值**,否则中心被夹在边界上、水位停不到目标值。
|
||
> ⇒ 下一节里「上线前清空三个区间列」的说法**已作废**,不要再执行那两条清空 SQL。
|
||
|
||
**变更 SQL**:
|
||
|
||
```sql
|
||
UPDATE `sys_config`
|
||
SET `config_value` = '{"enabled":true,"targetWatermark":30,"gain":2,"luckyValueMode":"raw"}',
|
||
`remark` = '幸运升级水位策略:目标水位%(平台希望长期维持的净收益比例)、灵敏度 gain、幸运值展示口径 raw/eff。随机数中心 = 100/((1-目标水位/100)*实测标定系数),再由每个饰品的随机数区间限定取值范围;中奖判定恒用 min(概率,75)。enabled=false 逐字节回退固定区间',
|
||
`update_time` = NOW()
|
||
WHERE `config_key` = 'luckyUpgradeWatermark';
|
||
```
|
||
|
||
**字段口径**:
|
||
|
||
| 字段 | 含义 |
|
||
|---|---|
|
||
| `enabled` | `true` = 按水位动态出随机数;`false` = 逐字节回退「每个饰品位固定 `luck_section`」 |
|
||
| `targetWatermark` | **目标水位(%)**:平台希望长期维持的净收益比例。30 ⇒ 长期平均 RTP ≈ 70% |
|
||
| `gain` | 响应灵敏度:水位每偏离目标 1 个点,随机数中心反向调整 `gain`%。默认 2,后台不展示(调参用) |
|
||
| `luckyValueMode` | 幸运值**展示**口径:`raw`(默认,累加前端原始概率)/ `eff`(累加 `min(概率,75)`);**不影响判定** |
|
||
|
||
---
|
||
|
||
## 幸运升级·水位驱动改造:水位日志表、拉取记录快照列、水位策略配置与「奖池水位」菜单
|
||
|
||
**背景**:幸运升级原来每个饰品位配一套固定幸运区间,随机数永远落在区间内(如 `[100,160)`),
|
||
玩家能看出「每次都要投到 1 倍原价以上才出」。本次改为**按全局水位动态决定本轮的随机数区间**:
|
||
`水位 = (Σ下注 − Σ返奖 + 人工调整) / Σ下注`(排除白号),水位越高 → 可出的随机数越低。
|
||
同时新增后台人工注水/抽水。**方案、换算表、口径推导与算例全部汇总在 `docs/修改记录.md` 的 2026-09-23 条**(不另建单独文档)。
|
||
|
||
**本次数据库变更 4 处**(2026-09-23 已在库 `henan` 执行):
|
||
|
||
| 对象 | 变更 | 说明 |
|
||
|---|---|---|
|
||
| `tt_upgrade_reward_log` | **新建表** | 注水/抽水审计日志,`delta` 为带符号影响,水位调整额 = `SUM(delta)`(DB 为真相,Redis 只是短缓存) |
|
||
| `tt_upgrade_record` | 新增 **2 列** | `watermark`(本次拉取时的水位%)、`round_target`(本轮抽到的随机数快照)|
|
||
| `sys_config` | 新增 **1 行** | `config_id=155`、`config_key='luckyUpgradeWatermark'`,水位波段表 + 幸运值展示口径;`enabled=false` 可逐字节回退旧行为 |
|
||
| `sys_menu` / `sys_role_menu` | 各新增 **1 / 2 行** | `menu_id=3208`「幸运升级 → 奖池水位」,授权给 `role_id` 101(三恒)、103(Fdily)|
|
||
|
||
> **为什么 `menu_id` 用 `3208`**:`3200~3207` 是麻将胡了的号段,按顺序往后取,不复用已下架玩法的旧号段。
|
||
|
||
**变更 SQL**:
|
||
|
||
```sql
|
||
CREATE TABLE IF NOT EXISTS `tt_upgrade_reward_log` (
|
||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'PK',
|
||
`type` tinyint NOT NULL COMMENT '1注水 2抽水 3重置',
|
||
`amount` decimal(14,2) NOT NULL DEFAULT '0.00' COMMENT '调整金额(绝对值,展示用)',
|
||
`delta` decimal(14,2) NOT NULL DEFAULT '0.00' COMMENT '对水位的带符号影响(注水正/抽水负/重置为抵消值)',
|
||
`watermark_before` decimal(8,2) DEFAULT NULL COMMENT '调整前水位%',
|
||
`watermark_after` decimal(8,2) DEFAULT NULL COMMENT '调整后水位%',
|
||
`operator` varchar(64) DEFAULT NULL COMMENT '操作人',
|
||
`remark` varchar(200) DEFAULT NULL COMMENT '调整原因(必填,前端保证)',
|
||
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
|
||
PRIMARY KEY (`id`),
|
||
KEY `idx_create` (`create_time`)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='幸运升级奖池水位调整日志';
|
||
|
||
ALTER TABLE `tt_upgrade_record`
|
||
ADD COLUMN `watermark` decimal(8,2) NULL COMMENT '本次拉取时的水位%(1-RTP,排除白号,含人工调整)',
|
||
ADD COLUMN `round_target` int NULL COMMENT '本轮抽到的随机数 fullV 快照';
|
||
|
||
INSERT INTO `sys_config`
|
||
(`config_id`, `config_name`, `config_key`, `config_value`, `config_type`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`)
|
||
VALUES
|
||
(155, '幸运升级水位策略', 'luckyUpgradeWatermark',
|
||
'{"enabled":true,"luckyValueMode":"raw","bands":[{"from":-999,"to":10,"vMin":95,"vMax":130},{"from":10,"to":26,"vMin":115,"vMax":150},{"from":26,"to":36,"vMin":75,"vMax":175},{"from":36,"to":999,"vMin":85,"vMax":128}]}',
|
||
'Y', 'admin', NOW(), '', NULL, '幸运升级水位策略:波段表(水位%→随机数区间) + 幸运值展示口径 raw(原始概率)/eff(与判定同口径);中奖判定恒用 min(概率,75)。enabled=false 逐字节回退固定区间');
|
||
|
||
INSERT INTO `sys_menu`
|
||
(`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`)
|
||
VALUES
|
||
(3208, '奖池水位', 2075, 3, 'rewardPool', 'skins/luckyUpgrade/rewardPool/index', NULL, 1, 0, 'C', '0', '0', 'skins:upgrade:pool', 'money', 'admin', NOW(), '', NULL, '幸运升级·奖池水位(全局水位/人工注水抽水/调整历史)');
|
||
|
||
INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (101, 3208), (103, 3208);
|
||
```
|
||
|
||
**验证**(2026-09-23 实测结果):
|
||
|
||
```sql
|
||
SELECT COUNT(*) FROM information_schema.tables
|
||
WHERE table_schema='henan' AND table_name='tt_upgrade_reward_log'; -- 期望 1,实测 1
|
||
SELECT COUNT(*) FROM information_schema.columns
|
||
WHERE table_schema='henan' AND table_name='tt_upgrade_record'
|
||
AND column_name IN ('watermark','round_target'); -- 期望 2,实测 2
|
||
SELECT COUNT(*) FROM sys_config WHERE config_key='luckyUpgradeWatermark'; -- 期望 1,实测 1
|
||
SELECT COUNT(*) FROM sys_menu WHERE menu_id=3208; -- 期望 1,实测 1
|
||
SELECT COUNT(*) FROM sys_role_menu WHERE menu_id=3208; -- 期望 2,实测 2
|
||
```
|
||
|
||
后台左侧「幸运升级」目录下出现「奖池水位」页(`/luckyUpgrade/rewardPool`)。
|
||
|
||
**注意:水位策略上线后的运营动作**
|
||
|
||
> ⚠️ **本节第 1 条已被本文件顶部「水位策略简化为单一目标水位」那条取代**:
|
||
> `luck_section` / `anchor_luck_section` / `blank_luck_section` **重新成为必填项**
|
||
> (语义 = 随机数的允许取值范围),**不要清空**;而且要配成**包含「按目标水位推算出的中心值」**的区间。
|
||
> 清空后后端会用兜底区间 `[80,160]`,能用但不受控。
|
||
|
||
1. ~~两个已存在的饰品位(`tt_upgrade_ornaments.id` 18 / 25)的 `luck_section` 要清空,才能完全跟随全局波段~~
|
||
—— **已作废**,见上方提示。
|
||
2. 水位从 0 起步。`enabled=true` 时前几件货会落在收紧档(水位低 → 随机数高),属预期行为;
|
||
需要立刻放水用后台「奖池水位 → 注水」。
|
||
|
||
-----------------------对战接口优化--------------------------
|
||
|
||
SHOW INDEX FROM tt_fight;
|
||
|
||
ALTER TABLE tt_fight ADD INDEX idx_ttf_status_create (status, create_time);
|
||
|
||
ALTER TABLE tt_fight
|
||
ADD COLUMN total_ornaments_price DECIMAL(18,2) NULL COMMENT '本场开箱饰品总价值(百佳榜排序用)',
|
||
ADD INDEX idx_ttf_status_total (status, total_ornaments_price);
|
||
|
||
ALTER TABLE tt_fight_user ADD INDEX idx_tfu_fight_user (fight_id, user_id);
|
||
|
||
ALTER TABLE tt_fight ADD INDEX idx_ttf_create_time (create_time);
|
||
|
||
-----------------------------------------------------------
|