修改河南项目的管理后台ui

This commit is contained in:
2026-09-27 19:12:59 +08:00
parent 34c7fa9d72
commit 3248ee4b83
31 changed files with 1605 additions and 558 deletions
+127
View File
@@ -160,6 +160,133 @@ SELECT COUNT(*) FROM sys_role_menu WHERE menu_id=3208;
2. 水位从 0 起步。`enabled=true` 时前几件货会落在收紧档(水位低 → 随机数高),属预期行为;
需要立刻放水用后台「奖池水位 → 注水」。
## 管理后台按钮级权限补全:`sys_menu` 补 24 行 `F`(按钮)记录
**背景**:后台按钮的显隐走前端 `v-hasPermi`(`ruoyi-ui/src/directive/permission/hasPermi.js`),
接口放行走后端 `@PreAuthorize("@ss.hasPermi('...')")`,**两边比对的是同一个 `sys_menu.perms`**
(超管 `user_id=1` 拿 `*:*:*`,其他人拿「已授权菜单行的 `perms` 去重集合」,见
`ruoyi-framework/.../SysPermissionService.java:55`)。
历史遗留:**从 `menu_id` 31xx 段开始,新增页面只建了 `C` 行、没建 `F` 行**
(`F` 行分布:1000 段 61 行、2000 段 18 行、3100 段仅 9 行、3200+ 段 0 行)。
后果是这些页面的按钮在角色授权树里**根本勾不到** ⇒ 除超管外谁都看不见;
更严重的是部分 **列表/查询接口也会 403**,页面进去是空的。
盘点结果:代码在用、但 `sys_menu` 未登记的权限串共 **43 个**。其中 **23 个属于 4 个无菜单入口的孤儿页面**
(`ttOrnaments/boxOpenChance`、`ttBonus/firstRecharge`、`ttWelfare/monthlyRecharges`、`ttReplacementRecord/index`
—— 已核实无菜单、无组件引用、无路由、无接口复用方,**不可达,本期不动**);
**其余 20 个 + 复用组补 4 行 = 24 行**,本次补齐。
> 为什么 `menu_id` 用 `3209~3232`:`MAX(menu_id)=3208`(麻将胡了 3200~3207、幸运升级 3208),按顺序往后取。
**本次数据库变更 3 处**(2026-09-27 已在库 `henan` 执行):
| 对象 | 变更 | 说明 |
|---|---|---|
| `sys_menu` | 新增 **24 行** | `menu_id=3209~3232`,`menu_type='F'`,挂到 6 个 C 行下(2035/2038/2086/2092/2123/3118)|
| `sys_menu` | 修正 **3 行** | `menu_id=3160~3162` 的 `parent_id`:`3156`(目录 `M`「流水抽奖」)→ `3157`(页面 `C`「抽奖活动」)。**同属 F 行挂载错误**,不改的话勾「抽奖活动」联动带不到这 3 个按钮 |
| `sys_role_menu` | 新增 **24 行** | 把上述 F 行补授权给「**已授权其父 C 行**」的角色(动态 SQL,执行时按实际授权现状生成)。执行时这些父 C 行**仅 `role_id=104`(测试人员)**已授权 ⇒ 24 行全落给 104(`sys_role_menu` 总行数 222 → 246)|
> 关于 `3160~3162`:这三个串(`system:turnoverLottery:add/edit/remove`)**当前代码里无人使用**
> (`turnoverLottery/list.vue` 的新增/修改/删除按钮没写 `v-hasPermi`,后端也没有对应 `@PreAuthorize`),
> 所以本次修正只是把菜单树层级摆正、消除「F 行挂在目录下」的隐患,不影响线上行为。
> 若将来给该页面补按钮权限,直接复用这三个串即可。
**变更 SQL**:
```sql
/* ---- 1. 24 行按钮权限(F)---- */
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
/* 奖池详情(ttRoll/prizedetail)—— 该页「删除」按钮历史上绑的就是 admin:prize:edit */
(3209, '修改奖池', 2038, 1, '', NULL, NULL, 1, 0, 'F', '0', '0', 'admin:prize:edit', '#', 'admin', NOW(), '', NULL, '奖池详情·修改'),
/* CDK管理(ttRedPack/index)—— 整套新增,此前 0 行 F */
(3210, '新增红包', 2086, 1, '', NULL, NULL, 1, 0, 'F', '0', '0', 'skinsback:ttRedPack:add', '#', 'admin', NOW(), '', NULL, 'CDK管理·新增'),
(3211, '删除红包', 2086, 2, '', NULL, NULL, 1, 0, 'F', '0', '0', 'skinsback:ttRedPack:remove', '#', 'admin', NOW(), '', NULL, 'CDK管理·删除'),
(3212, '修改红包', 2086, 3, '', NULL, NULL, 1, 0, 'F', '0', '0', 'skinsback:ttRedPack:edit', '#', 'admin', NOW(), '', NULL, 'CDK管理·修改'),
(3213, '导出红包', 2086, 4, '', NULL, NULL, 1, 0, 'F', '0', '0', 'skinsback:ttRedPack:export', '#', 'admin', NOW(), '', NULL, 'CDK管理·导出'),
(3214, '红包查询', 2086, 5, '', NULL, NULL, 1, 0, 'F', '0', '0', 'skinsback:ttRedPack:query', '#', 'admin', NOW(), '', NULL, 'CDK管理·详情'),
(3215, '红包列表', 2086, 6, '', NULL, NULL, 1, 0, 'F', '0', '0', 'skinsback:ttRedPack:list', '#', 'admin', NOW(), '', NULL, 'CDK管理·列表接口'),
(3216, '红包全部列表', 2086, 7, '', NULL, NULL, 1, 0, 'F', '0', '0', 'skinsback:ttRedPack:listAll', '#', 'admin', NOW(), '', NULL, 'CDK管理·不分页列表接口'),
/* 对战排行榜奖励(ttFight/fightRankingReward)—— 此前 0 行 F */
(3217, '新增奖励', 2092, 1, '', NULL, NULL, 1, 0, 'F', '0', '0', 'admin:reward:add', '#', 'admin', NOW(), '', NULL, '对战排行榜奖励·生成'),
(3218, '删除奖励', 2092, 2, '', NULL, NULL, 1, 0, 'F', '0', '0', 'admin:reward:remove', '#', 'admin', NOW(), '', NULL, '对战排行榜奖励·清空'),
(3219, '修改奖励', 2092, 3, '', NULL, NULL, 1, 0, 'F', '0', '0', 'admin:reward:edit', '#', 'admin', NOW(), '', NULL, '对战排行榜奖励·修改'),
(3220, '奖励查询', 2092, 4, '', NULL, NULL, 1, 0, 'F', '0', '0', 'admin:reward:query', '#', 'admin', NOW(), '', NULL, '对战排行榜奖励·详情'),
(3221, '奖励列表', 2092, 5, '', NULL, NULL, 1, 0, 'F', '0', '0', 'admin:reward:list', '#', 'admin', NOW(), '', NULL, '对战排行榜奖励·列表接口'),
/* 机器人对战配置(ttFight/robotFightConfig)—— 该 C 行下原有 4 行(3119~3122),价格配置这套是漏的,故 order 从 5 起 */
(3222, '新增价格配置', 3118, 5, '', NULL, NULL, 1, 0, 'F', '0', '0', 'admin:robotFightPriceConfig:add', '#', 'admin', NOW(), '', NULL, '机器人对战配置·价格配置新增'),
(3223, '删除价格配置', 3118, 6, '', NULL, NULL, 1, 0, 'F', '0', '0', 'admin:robotFightPriceConfig:remove', '#', 'admin', NOW(), '', NULL, '机器人对战配置·价格配置删除'),
(3224, '修改价格配置', 3118, 7, '', NULL, NULL, 1, 0, 'F', '0', '0', 'admin:robotFightPriceConfig:edit', '#', 'admin', NOW(), '', NULL, '机器人对战配置·价格配置修改'),
(3225, '价格配置查询', 3118, 8, '', NULL, NULL, 1, 0, 'F', '0', '0', 'admin:robotFightPriceConfig:query', '#', 'admin', NOW(), '', NULL, '机器人对战配置·价格配置详情'),
(3226, '价格配置列表', 3118, 9, '', NULL, NULL, 1, 0, 'F', '0', '0', 'admin:robotFightPriceConfig:list', '#', 'admin', NOW(), '', NULL, '机器人对战配置·价格配置列表接口'),
/* 货币列表(ttRechargeList/index)—— 该页只有详情接口校验;列表接口本身无 @PreAuthorize */
(3227, '货币查询', 2035, 1, '', NULL, NULL, 1, 0, 'F', '0', '0', 'admin:rechargeList:query', '#', 'admin', NOW(), '', NULL, '货币列表·详情'),
/* 机器人对战分组宝箱(ttFight/robotFightGroupBox)
⚠️ 复用组:该页前端按钮用 admin:box:{add,edit,remove}、后端接口用 admin:box:{list,query},
而这 4 个串原先只挂在 2019「宝箱列表」下 —— 父子联动带不过来,
所以必须在 2123 下再挂一遍,才能做到「勾这一个页面 = 该页完整可用」。*/
(3228, '新增分组宝箱', 2123, 1, '', NULL, NULL, 1, 0, 'F', '0', '0', 'admin:box:add', '#', 'admin', NOW(), '', NULL, '机器人对战分组宝箱·新增(与 2020 同 perms,刻意重复)'),
(3229, '删除分组宝箱', 2123, 2, '', NULL, NULL, 1, 0, 'F', '0', '0', 'admin:box:remove', '#', 'admin', NOW(), '', NULL, '机器人对战分组宝箱·删除(与 2021 同 perms,刻意重复)'),
(3230, '修改分组宝箱', 2123, 3, '', NULL, NULL, 1, 0, 'F', '0', '0', 'admin:box:edit', '#', 'admin', NOW(), '', NULL, '机器人对战分组宝箱·修改(与 2022 同 perms,刻意重复)'),
(3231, '分组宝箱查询', 2123, 4, '', NULL, NULL, 1, 0, 'F', '0', '0', 'admin:box:query', '#', 'admin', NOW(), '', NULL, '机器人对战分组宝箱·详情接口'),
(3232, '分组宝箱列表', 2123, 5, '', NULL, NULL, 1, 0, 'F', '0', '0', 'admin:box:list', '#', 'admin', NOW(), '', NULL, '机器人对战分组宝箱·列表接口(与 2019/3139 同 perms,刻意重复)');
/* ---- 2. 授权补齐:把新 F 行补给「已授权其父 C 行」的角色 ----
动态写法的好处:不依赖执行时刻的角色授权现状,也不用在角色界面逐个重存。
注意:只补 F 行;C 行(页面本身)是否可见仍由业务在「角色管理」里决定。*/
INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
SELECT DISTINCT `rm`.`role_id`, `f`.`menu_id`
FROM `sys_menu` `f`
JOIN `sys_role_menu` `rm` ON `rm`.`menu_id` = `f`.`parent_id`
WHERE `f`.`menu_id` BETWEEN 3209 AND 3232
AND NOT EXISTS (SELECT 1 FROM `sys_role_menu` `x`
WHERE `x`.`role_id` = `rm`.`role_id` AND `x`.`menu_id` = `f`.`menu_id`);
/* ---- 3. 修正 3 行挂错父节点的 F(3160~3162:目录 3156 → 页面 3157)---- */
UPDATE `sys_menu`
SET `parent_id` = 3157
WHERE `menu_id` BETWEEN 3160 AND 3162
AND `menu_type` = 'F'
AND `parent_id` = 3156;
```
**验证**:
```sql
-- ① 24 行 F 都进去了、且 perms 无重复录入
SELECT COUNT(*) FROM sys_menu WHERE menu_id BETWEEN 3209 AND 3232 AND menu_type='F'; -- 期望 24
SELECT perms, COUNT(*) FROM sys_menu WHERE menu_id BETWEEN 3209 AND 3232
GROUP BY perms HAVING COUNT(*) > 1; -- 期望 0 行(2123 组与 2019 组不同 id,不算重复)
-- ② 全库 F 行的 parent 必须是指向 C 行的 menu_id(防挂错;本次修掉了 3160~3162 这组)
SELECT f.menu_id, f.menu_name, f.perms, f.parent_id, p.menu_type
FROM sys_menu f LEFT JOIN sys_menu p ON p.menu_id = f.parent_id
WHERE f.menu_type = 'F' AND (p.menu_id IS NULL OR p.menu_type <> 'C'); -- 期望 0 行
-- ③ 授权补齐后:每个新 F 行,其父 C 行的每个授权角色都应能查到
SELECT f.menu_id, f.perms, rm.role_id
FROM sys_menu f
JOIN sys_role_menu rm ON rm.menu_id = f.parent_id
WHERE f.menu_id BETWEEN 3209 AND 3232
AND NOT EXISTS (SELECT 1 FROM sys_role_menu x
WHERE x.role_id = rm.role_id AND x.menu_id = f.menu_id); -- 期望 0 行
-- ④ 端到端:模拟某角色的 perms 集合,确认缺串已消失
SELECT DISTINCT m.perms FROM sys_menu m
JOIN sys_role_menu rm ON m.menu_id = rm.menu_id
WHERE rm.role_id = <角色ID> AND m.perms IS NOT NULL AND m.perms <> '';
```
**代码侧配套(非 SQL)**:新增 `.workbuddy/verify-perms.cjs`,做「代码在用的权限串 ↔ `sys_menu.perms`」双向差集,
防止后续新增页面再次只建 C 行、漏建 F 行。跑法见脚本头部注释。
**注意:权限生效需要用户重新登录**(`permissions` 是登录时从 `/getInfo` 拉取的,存在前端 store 里,不会热更新)。
---
-----------------------对战接口优化--------------------------
SHOW INDEX FROM tt_fight;
+251
View File
@@ -7,6 +7,257 @@
---
## 2026-09-27
- **管理后台按钮级权限补全:`sys_menu` 补 24 行 `F` + 防漂移校验脚本**
**起因**:用户问「管理后台会不会有只有超管能看到、非超管看不见的页面内按钮」。核查结论:**会,而且比按钮更严重**。
**机制**:按钮显隐走前端 `v-hasPermi`、接口放行走后端 `@PreAuthorize("@ss.hasPermi(...)")`,
两边比对的是**同一份 `sys_menu.perms`**;超管(`user_id==1`)拿 `*:*:*` 所以永远看得见。
⇒ **某权限串只要在 `sys_menu` 里没有对应行,就永远授权不到任何非超管角色**:按钮看不见、部分列表/详情接口直接 403。
**盘出的问题**(代码在用、库里未登记,并集 43 串):
- 4 个孤儿页面(开箱概率 / 首充奖励 / 月度充值 / 换装记录)占 23 串 —— 已核实无菜单、无组件引用、无路由、无接口复用方,
**不可达,本期不动**(手输 URL 也 404,因 RuoYi 路由由 `/getRouters` 动态注册)。
- 其余 20 串 + `admin:box:*` 复用组补 4 行 = **24 行**,本次补齐。
- 结构性漂移:**从 `menu_id` 31xx 段起,新增页面只建 `C` 行、不建 `F` 行**(F 行分布:1000 段 61、2000 段 18、3100 段 9、3200+ 段 0)。
**改动清单**
1. `sys_menu` 新增 24 行 `F`(`menu_id 3209~3232`,挂 2035/2038/2086/2092/2123/3118 六个 C 行下)。
2. `sys_role_menu` 动态补齐:把新 F 行补给「已授权其父 C 行」的角色(本次实写 24 行,全给 `role_id=104`)。
3. `sys_menu` 修正 3 行(`3160~3162`)`parent_id`:目录 `3156` → 页面 `3157`(F 行挂错层级,父子联动带不到)。
4. **代码侧配套**:新增 `.workbuddy/verify-perms.cjs` —— 扫前端 `v-hasPermi` + 后端 `@PreAuthorize`,
与 `sys_menu.perms` 做双向差集 + 校验全库 F 行 parent 必须指向 C 行。非 0 退出码,建议挂 `publish.sh` 前防复发。
**验证**:库层 5 项全对(24 行 / 0 重复 / 0 挂错父节点 / 0 未授权 / `role_menu` 222→246);
`node .workbuddy/verify-perms.cjs` ⇒ **ALL_OK 6 passed / 0 failed**。
**注意**:① 权限生效需用户**重新登录**(`permissions` 登录时从 `/getInfo` 拉取,不热更新)。
② 执行时库内授权已被重排 —— 只有 `role_id=104`(测试人员)持有这些页面,`三恒` 等角色**0 菜单**,
且多个启用用户(三恒/国荣/s1/zzyzzy123/Fdily)**未分配任何角色**,这是业务配置问题,本次未动。
细节 SQL 见 `docs/updata_sql.md`「管理后台按钮级权限补全」,过程与踩坑见 `.workbuddy/memory/2026-09-27.md`。
- **推广后台「佣金明细记录」等 3 张报表右侧留白 → 全列 `width` 改 `min-width`**
**起因**:用户截图 `promo-ui2/src/views/CommissionReport/index.vue` 的「佣金明细记录」表,说「这里表格填充不满」。
**根因**(读 Element Plus 源码定论,不是猜):`element-plus/es/components/table/src/table-layout.mjs:100-171`
的 `updateColumnsWidth()` 只在 **存在弹性列** 时才把表格撑满容器
(`flexColumns = flattenColumns.filter(c => !isNumber(c.width))`);
全列都是数字 `width` 时走 else 分支 ⇒ `bodyWidth = Σ realWidth` ⇒ **表格宽恒等于列宽之和,容器再宽右侧也留白**。
⚠️ 关键陷阱:`width="170"` 这种**静态字符串**也会被 `table-column/render-helper.mjs:39` 的
`parseWidth()`(= `Number.parseInt`)转成数字 170 ⇒ 那是**固定列**;
反过来只写 `min-width` 时 `setColumnWidth()` 会执行 `column.width = void 0` ⇒ 那才是**弹性列**。
**改动清单**(`promo-ui2/src/views/CommissionReport/index.vue`,3 张表共 16 列;另有 6 张表见本节末):
1. 「佣金明细记录」6 列(本地时间170 / 手机号130 / 该次消费120 / 佣金比例90 / 佣金金额100 / 来源120)
2. 「佣金日报」5 列(手机号130 / 今日消费120 / 来源120 / 佣金金额120 / 日期130)
3. 「佣金汇总」5 列(手机号130 / 来源120 / 消费金额120 / 本次佣金120 / 参加游戏次数140)
—— **一律 `width` → `min-width`,数值不变**。选「全列都变弹性」而不是「只挑一列变弹性」:
弹性的那一列会独吞全部余量(1280 视口下 170px 的时间列会涨到 ≈590px),全列按 min-width 等比摊才均匀。
模板顶部加了注释说明为什么不能改回 `width`。脚本/接口/分页/过滤逻辑一行未动。
**验证**(3 件,全部 ALL_OK):
- SFC 编译:`node .workbuddy/verify-promo-vue-compile.cjs src/views/CommissionReport/index.vue` ⇒ **ALL_OK 4/0**
(连带跑 Home ⇒ 11/0,未受影响)。
- 算法级多视口回归:`node .workbuddy/verify-ep-table-stretch.cjs src/views/CommissionReport/index.vue`
⇒ **ALL_OK 15/0**。1280/1440/1600/1920/2560 五个视口下 3 张表**留白全为 0px、`scrollX` 全 false**。
- 真产物:`node .workbuddy/check-promo-modules.cjs src/views/CommissionReport/index.vue --has=min-width --prop=min-width:16`
⇒ **ALL_OK**(用户常驻的 `:4000` dev server 上主模块 200、`"min-width"` prop 恰好 **16** 次、0 处编译错误串)。
- 全站审计:`node .workbuddy/audit-table-fill-ep.cjs` ⇒ CommissionReport 3 张表从「✗ 不能撑满」转为有弹性列。
**新增脚本(3 个,都是 promo-ui2 / Element Plus 侧原先缺的)**:
`.workbuddy/audit-table-fill-ep.cjs`(全站扫「全固定列 ⇒ 留白」)、
`.workbuddy/verify-ep-table-stretch.cjs`(复现 `updateColumnsWidth` 做多视口定量回归)、
`.workbuddy/check-promo-modules.cjs`(从 `:4000` 拉真实编译产物做断言)。
原有的 `sim-table-width.cjs` / `verify-table-fill.cjs` 只适用 ruoyi-ui(Element UI 2.x),**不要混用**。
**注意 / 一并修的另外 6 张**:审计出全站共 **9 张**同样会留白的表,用户拍板「6 张全改」,已一并完成:
`BusinessManage/index.vue` 3 张(招商 Σ1128 / 主播 Σ828 / 普通用户 Σ920,后两张在展开行内)、
`CardMerchant/index.vue:44`(Σ1210)、`subFlow/index.vue:20`(Σ1570)、`SubordinateRecharge/index.vue:23`(Σ760)。
- 改法一致:**数据列 `width` → `min-width`**;**「操作」列保持 `width`**(CardMerchant 100、subFlow 120)——
操作列固定宽是设计意图,不该被拉宽。BusinessManage 的 `type="expand"` 列也不动(`cellStarts` 强制 48px 固定)。
- **嵌套子表(BusinessManage 二/三级)**:容器宽度由父级展开行决定,**不能用「视口 − 288」推算**。
`verify-ep-table-stretch.cjs` 已加 `depth` 判定,嵌套表只做「有没有弹性列」的定性断言,不套用视口断言。
- **两张宽表仍会横向滚动**(改 min-width 治不了,属列宽总量问题):`CardMerchant` Σ1210 ⇒ 视口 ≤1440 横滚(差 58px);
`subFlow` Σ1570 ⇒ 视口 ≤1600 横滚。**这两个视口下改前改后都是横滚**,未新增;≥1600 / ≥1920 起改为撑满(改前是留白)。
若要 1440 也不横滚,得再压列宽(`使用时间/创建时间` 180→165、`卡密` 200→190 之类),本次未做,等确认。
- 复验:全站审计 **不能撑满 0 / 34**;SFC 编译 **19/0 ALL_OK**;列宽回归 **22/0 ALL_OK**(6 张表 × 视口);
真产物(`:4000`)4 个文件主模块全 200、`"min-width"` prop 计数 **22 / 8 / 6 / 13** 逐一吻合、0 错误串。
⚠️ **未经浏览器实测**(本机 Chrome 起不来,见 `.workbuddy/memory/theme-skin.md`)⇒ 效果请自己刷 `:4000` 确认。
- **修复:幸运升级「水位驱动」因 mapper 里 `SUM(x, 0)` 语法错而整条不可用**
**现象**:启动日志 `TtUpgradeRewardLogMapper.xml` → `sumRecordTotals` 报
`java.sql.SQLSyntaxErrorException: ... right syntax to use near '' at line 4`,
外层 WARN「幸运升级水位驱动初始化失败(不阻断启动,首次拉取时会自动重试)」。
**根因**:`TtUpgradeRewardLogMapper.xml` 的 `blankBet` / `blankWin` 两行把外层 `COALESCE(..., 0)` 的写法抄进了 `SUM()` ——
`SUM(CASE WHEN is_blank THEN r.amount_consumed END, 0)`。**`SUM()` 只接受一个参数**,第二个 `, 0` 直接语法错。
(同文件 `totalBet` / `totalWin` 两行写的是 `CASE WHEN is_blank THEN 0 ELSE ... END`,是对的,可作对照。)
**改动**:删掉 `SUM(...)` 里多余的 `, 0`(外层 `COALESCE(SUM(...), 0)` 保留,仍是 null-safe)。全仓库仅此一处。
**影响面(不是"只是个 WARN")**:
- `ensureInit(false)`(`@PostConstruct` 预热)异常 → Redis 累计量 `KEY_BET/KEY_WIN/KEY_BLANK_*` **从未写入**,
`KEY_INIT_AT` 也没落 ⇒ 每次调用都重新全表扫并再次失败,**水位恒为 0**,水位驱动实际等于停摆。
- 后台「奖池水位 → 按历史重算」`/recalc` 调 `ensureInit(true)` 且**没有 try/catch** ⇒ 该按钮**必 500**。
- `calibrationFactor()` 有 try/catch → 失败返回 0 → 回落兜底系数 1.19(这条不受影响)。
**验证**:库里实测 `SELECT SUM(CASE WHEN 1 THEN 2 END, 0)` 复现同一 `ERROR 1064`;
改成 `SUM(CASE WHEN ... END)` 后整条 `sumRecordTotals` 跑通出数。XML 重新解析通过。
**上线动作**:`./ry.sh stop` → `mvn clean install`(见 `USER.md` 的「必须 clean install,否则嵌套旧 jar」)→ `./ry.sh start`。
- **推广后台登录页新增「开发环境账号密码登录」**
**起因**:推广后台唯一登录入口是 `/promo/smsLogin`(手机号 + 4 位短信验证码 + 滑块),本地联调要连短信通道、拖滑块;
后端此前已明确下线账号密码登录(`PromoLoginServiceImpl` 注释:「账号密码登录已下线……避免用密码绕过短信校验被撞库」)。
**改动清单**(前后端配合,**默认关闭**):
1. **后端新增 `POST /promo/devLogin`**(`@Anonymous`):账号解析直接复用 `createUserToken` 的 `promo_user_` 入口
⇒ 推广账号(`tt_user` 的主播/招商/卡商,登录名=手机号)与超管(`promo_admin` 的 `username` 或 `phone_number`)
**都能登录**,区别只是「短信验证码」换成了 `SecurityUtils.matchesPassword` 的 BCrypt 密码校验。
账号不存在 / 停用 / 密码错统一返回「账号或密码错误」(防账号枚举,真实原因走 `log.warn`)。
2. **开关走配置文件而非数据库**:新增 `promo.dev-login.enabled` —— `application.yml` 默认 `false`,`application-dev.yml` 置 `true`,
生产 profile 不覆盖 ⇒ 接口恒返回「账号密码登录未开启」,且后台管理员无法误开。
3. `PromoWebConfig` 放行 `/promo/devLogin`(维护拦截器不拦登录类接口,与 smsLogin/register 同口径)。
4. **前端** `LoginForm.vue` 只在 `import.meta.env.DEV`(即 `pnpm run dev`)下渲染「短信验证码 / 账号密码」切换与密码表单;
登录成功后复用原有的 `completeLogin` + `enterSystem` 落地流程。`api/login` 增加 `devLoginApi` 与 `DevLoginType`。
**验证**:`vue-tsc --noEmit` 改动文件 0 报错(仓库其余 30+ 条为既存错误);`mvn -B -DskipTests compile` ⇒ `BUILD SUCCESS`。
**无需数据库变更**:开关是配置文件项,不是 `sys_config`(相对 `promoMaintenance` 的取舍见 `.workbuddy/memory/2026-09-27.md`)。
**使用注意**:密码按 BCrypt 校验,测试账号若不知明文,查 `tt_user.remark`(格式 `明文密码:xxx`)或 `promo_admin` 表。
- **管理后台首页改排版:11 张等宽统计卡 → 「左窄轨 + 右主区」两栏**
**起因**:用户反馈首页「排版还是和原来一样,只是改了 UI 的风格」——换肤把配色圆角阴影改了,但骨架仍是
「页头 → 卡片网格 → 区块 → 区块」的垂直堆叠模板,要求**排版差异化、风格照旧**。
**改动清单**(`ruoyi-ui/src/views/index.vue`,单文件)
1. **骨架拆两栏**:`grid-template-columns: 300px minmax(0,1fr)` + `align-items:start`。
- **左窄轨**(`position:sticky; top:16px`,跟随滚动):3 个核心大数字(在线用户 / 总充值 / 总流水,字号 28px)
+ 待办区(待处理提货申请 / 提货余额)。待处理 >0 时雏菊金描边 + 数值变色。
- **右侧主区**:实时 RTP → 「经营数据」紧凑指标带(6 项 `mini-stat`,小图标 + 标签 + 数值)→ 流水统计图
(顺序按用户看完后的要求定:图表 400px 最高,放最底部滚到底正好)。
2. **指标一项不丢**:原 `statCards` 11 项拆成 `railMetrics`(3) + `miniStats`(6) + 待办区硬编码 2 项 = 11。
原「图标 + 大数值 + 标签」的等权卡片形态取消,改为「大数字 / 紧凑条」两级,主次拉开。
3. **RTP 网格** `repeat(5,1fr)` → `repeat(auto-fit, minmax(160px,1fr))`:右区宽度随屏幕浮动(左轨占了 300px),
写死 5 列会在 1440 以下挤出 4+1。
4. 旧骨架 CSS(`.stat-grid` / `.stat-card` / `.stat-icon` / `.stat-info` / `.stat-arrow`)与 `statCards` computed **一并删除**。
**风格**:色板 / 圆角 / 阴影 / 图标浅底规则全部沿用,**未新增任何色值** —— cottagecore 配色纪律不动。
**验证**:`node .workbuddy/verify-home-layout.cjs`(新增,cwd=ruoyi-ui)⇒ **ALL_OK 28 条 0 失败**
(含 vue-template-compiler 编译模板、dart-sass 编译样式、新旧骨架核对、区块顺序、指标总数 11、交互绑定未丢);
`node .workbuddy/audit-palette.cjs` ⇒ **离版文件 0/185、离版色值 0 次**;
dev server(`:80`)`/static/js/app.js` ⇒ 200 / 7.1MB / 0 处 `Module build failed`。
⚠️ 本机助手拉不起 Chrome(安全策略)⇒ **渲染后实际观感未经实测**,需用户刷新页面肉眼确认。
**后续补丁(用户看完截图提的)**:左轨「待处理提货申请 / **提货余额**」那格里,提货余额写的是 `$9259.64`
(**美元符号**),而页面其余金额(总充值 `¥1800`、总流水 `¥0.00`、RTP 投注/奖励 `¥0.00`、经营数据 6 项 `¥…`)
一律 `¥` ⇒ 统一为 `¥{{ balance }}`(`ruoyi-ui/src/views/index.vue:55`,全站唯一一处 `$` 货币写法,已 grep 确认)。
`verify-home-layout.cjs` 补 2 条断言(模板内不得出现 `$` 紧跟插值;提货余额那行前缀必须是 `¥`)⇒ 现 **30 条 ALL_OK**。
真产物复核(新增 `.workbuddy/check-ruoyi-bundle.cjs`,从 `:80` 拉 `app.js`):编译后为
`_vm._v("¥" + _vm._s(_vm.balance))`,`$` 版本 0 处。
- **推广后台首页改排版:「基本信息 / 5 张返利卡 / 汇总表」三段堆叠 → 「推广工具置顶」**
**起因**:与上一轮管理端首页同类问题(换肤只改配色,骨架没动)。用户在 3 轮高保真稿里选定 **版本 3 · 推广工具置顶**。
**改动清单**(`promo-ui2/src/views/Home/index.vue`,单文件)
1. **① 我的推广(置顶通栏)**:`hero` 行放**推广链接**(15px 加粗、超长省略、整条可复制)+ 主色「复制链接」按钮(无链接时 `disabled`);
下面一条虚线分隔的三格:**我的邀请码**(复制 / 修改 / 已修改)、**手机号码**(绑 `phoneNumber`)、**上级邀请码**。
2. **② 返利 KPI 横带**(4 格,19px 数字):**累计返利 / 今日返利 / 本周返利 / 累计下级** ——
全部从已有的 `getGameCommissionData()` 的 `periods` 里取(`总`/`今日`/`本周` 行),**不新增任何接口**。
3. **③ 底部两栏** `260px minmax(0,1fr)`:左 = **返利比例**(5 行「玩法名 ⋯⋯ 百分比」点线清单,替掉原来 5 张等宽卡);
右 = **周期返利汇总**表格(列一项没删,仍是 10 列)。`@media(max-width:1200px)` 收成一栏让表格拿满宽度。
4. **④ 账号信息降级到最底部**:用户账号 / 用户类型 / 金币余额 / 积分余额 / 注册时间 / Steam交易链接(6 项)。
样式上**做成非卡片**(`background/border` 透明、去阴影),避免和上面的数据抢注意力。
5. 旧骨架(`.section` / `.section-title` / `.game-rate-cards` / `.rate-card*`)与那两条**从未命中过**的
`.el-table__row tr:nth-child(odd)` 伪条纹规则一并删除(Element 的 `stripe` 已负责隔行)。
6. 颜色从残留的旧 Element 灰(`#f5f7fa` / `#e4e7ed` / `#303133` / `#67c23a` / `#606266` / `#909399` / `#fafafa` / `#e6a23c`)
收敛到田园核白名单 —— 该页 scoped 样式是换肤时漏掉的一份。
**一字未动的**:`username: userInfo.userName`(改邀请码的**入参**,后端拿它做「只能改自己」校验,与展示用的 `phoneNumber` 语义不同);
返利比例出处仍是 `getInfo()` 的三个 `*CommissionRate`;表格 10 列 / 弹窗 / 复制 / `onMounted` 双并发加载全部保留。
**验证**(新增 2 个脚本)
- `node .workbuddy/verify-promo-home.cjs` ⇒ **ALL_OK 54 条 0 失败**:新骨架 12 项、旧骨架 4 项须绝迹、区块顺序、
表格 10 列 / 账号 6 项 / KPI 4 项 / 玩法 5 项齐全、交互绑定 7 项、旧 Element 色 0 处、色值全在白名单、8 组对比度 ≥4.5:1。
- `node .workbuddy/verify-promo-vue-compile.cjs`(新增,用 promo 自带的 `vue/compiler-sfc` 走
parse → compileScript → compileTemplate → compileStyle)⇒ **ALL_OK 7 条 0 失败**。
- `node .workbuddy/verify-home-page.cjs` ⇒ **ALL_OK 5 条 0 失败**(该脚本①的结构性正则随「手机号码」移位已改写为与结构无关的定位)。
- **真机 dev 产物**(用户的 vite dev server 正在 `:4000` 跑,热更新):`/src/views/Home/index.vue` 的
style / template / script-setup / main 四个模块全 200,0 处 `Transform failed` / `Internal server error`;
编译后 15/15 个新骨架选择器都带上了 `[data-v-a763a6ae]` 前缀,旧类名 0 残留。
⚠️ 两点未做实测:**渲染后观感**(本机 Chrome 拉不起来);**窄屏横向滚动** —— 底部两栏时表格可用宽度 ≈ 屏宽-200(侧栏)-80,
10 列 `min-width` 合计 1090px,**视口 < 约 1600px 时右侧表格会出现横向滚动**(≥1600 正常),已在 `<1200px` 断点收成一栏规避。
**数据全是 0 是账号本身无下级/无消费所致**,不是渲染问题(`periodData` 的 4 行都在,只是值都为 0.00)。
过程与踩坑见 `.workbuddy/memory/2026-09-27.md`。
- **管理后台站点名「河南项目」→「凯科佰娱」(ruoyi-ui)**
**起因**:用户截图(`localhost/#/index`)圈出左下角 Logo 旁标题与浏览器 tab 标题,要求改成「凯科佰娱」。
**定位**:截图端口 80 + hash 路由 ⇒ 是 **`ruoyi-ui`(Vue2 管理后台,`vue.config.js` 默认 port 80)**,不是 promo-ui2(:4000)。
站点名只有一个源:`VUE_APP_TITLE`(`.env.*`)。消费方两处 ——
`src/layout/components/Sidebar/Logo.vue:23`(侧栏标题)、`src/App.vue:20-29` 的 `metaInfo.titleTemplate`
(`${路由名} - ${VUE_APP_TITLE}`);`public/index.html` 的 `<title>` 由 `vue.config.js:10` 的 `name` 注入。
⇒ **改一处 env 即可同时改掉侧栏 + tab**。另有 login/register 两页是硬编码同名,一并改。
**改动清单(6 处,全是字符串替换)**
1. `.env.development` / `.env.production` / `.env.staging`:`VUE_APP_TITLE = 河南项目` → `凯科佰娱`。
2. `vue.config.js:10` 兜底值 `|| '河南项目'` → `|| '凯科佰娱'`。
3. `src/views/login.vue:5`、`src/views/register.vue:4` 的 `<h3 class="title">`。
**注意**:① `.env` 在**进程启动时**注入 `process.env`,**改完必须重启 dev server**(webpack 不会热更 env)。
② `ruoyi-ui/dist/` 是构建产物,仍含旧串,需 `npm run build:prod` 重新生成才会更新(未手动改 dist)。
③ 后端 `application-*.yml` 里的「河南」是注释(控制台账号备注),与站点名无关,未动;
`docs/修改记录.md:910` 等处的「河南项目」是历史叙述,保留。
过程见 `.workbuddy/memory/2026-09-27.md`。
- **推广后台顶栏「分享链接」删除(与首页「我的推广链接」重复)**
**起因**:用户截图 `promo-ui2` 首页(`localhost:4000/#/home/index`),圈出**顶栏**的 `分享链接:https://…?code=…`
与**页面里**的「我的推广链接」hero 卡是同一份内容,要求「把上面的去掉」。
**定位**:两处不同来源 ——
① **顶栏**:`src/layout/components/ToolHeader.vue:45-47` 的 `<span>分享链接:{userStore.getUserInfo?.shareLink}</span>`
(**布局层组件,所有页面都有**,`role !== '99' && role !== '05'` 才显示);
② **页面**:`src/views/Home/index.vue:4-21` 的 hero 卡 —— 本次改版(版本 3「推广工具置顶」)**新加的**。
⇒ hero 一上线,顶栏那份就成了重复。按用户口径删**顶栏**(上面的)。
**改动清单**
1. `ToolHeader.vue`:删掉该 `<span>`(原地留注释说明为何删)。
2. 同文件连带清理:`useUserStore` 的 import 与 `const userStore = useUserStore()` —— 全文件只有这一处在用,
不删会变成未使用变量(`vue-tsc` / eslint 会报)。
3. `Home/index.vue`:删掉「改邀请码成功后同步顶部 header 分享链接」的 `userStore.setUserInfo({...})` 块 + 其 import/实例——
该块**存在的唯一目的**就是喂被删掉的顶栏,属同一改动的死代码,不是重构。
**验证**:给 `.workbuddy/check-promo-modules.cjs` **新增 `--absent=<子串>`**(原先只有 `--has` 用于"必须有",
删元素没法验),从用户常驻的 `:4000` 拉 **vite 真产物**断言:
- `layout/components/ToolHeader.vue` → 主模块 200、style 子模块 200、`useUserStore` / `shareLink` / `getUserInfo` **全部已消失**,**ALL_OK 6/0**。
- `views/Home/index.vue` → 主模块 200、`useUserStore` / `setUserInfo` **已消失**,**ALL_OK 4/0**。
⚠️ `--absent` 的坑:dev 构建**保留模板注释** ⇒ 注释里**别写被删的那个词**,否则 `_createCommentVNode("…")` 会命中假 FAIL。
**影响面**:改的是**布局层** ⇒ 所有页面顶栏都不再显示分享链接(不只首页)。
`userStore.userInfo.shareLink` 仍由 `Login/completeLogin.ts` 与 `views/User/index.vue` 写入,只是**不再有展示方**(无其他读取处,已 grep 确认);
改邀请码后 store 里那份不再自动刷新,因无消费方,无影响。
过程见 `.workbuddy/memory/2026-09-27.md`。
---
## 2026-09-24
- **头像管理:「是否默认」可同时多条为「是」的缺陷修复(唯一性约束 + 存量数据清洗 + 开关交互)**