// 按「方法名 → 注解」显式白名单写入 @Log(不做 URL 猜配,避免错位) // // node bin/checks/set-known-audit-logs.js 干跑 // node bin/checks/set-known-audit-logs.js --apply 落盘 // // 按钮文案全部取自页面源码原文(每条的来源写在本表注释里,格式:views/xxx.vue:行号): // 工具条按钮(生成招商账号/一键上架/发放CDK)优先于弹窗里的通用【确 定】—— // 运营的心智是"我点了那颗有名字的按钮",【确 定】记进日志等于没信息。 const fs = require('fs'); const path = require('path'); const ROOT = path.resolve(__dirname, '..', '..'); const apply = process.argv.includes('--apply'); const A = 'skins-service/service-admin/src/main/java/com/ruoyi/admin/controller/'; const W = 'skins-service/game-wheel-service/src/main/java/com/ruoyi/game/wheel/controller/'; const O = 'skins-service/game-olympus-service/src/main/java/com/ruoyi/game/olympus/controller/'; const TARGETS = [ // ---- 用户列表(ttuser/index.vue:工具条 113/121/129,行内 86/96,下拉 212/218/224)---- [A + 'TtUserController.java', 'generateAccount', { menu: '用户列表', action: '生成招商账号', type: 'INSERT' }], [A + 'TtUserController.java', 'generateRobot', { menu: '用户列表', action: '生成机器人', type: 'INSERT' }], [A + 'TtUserController.java', 'generateBlankAccount', { menu: '用户列表', action: '生成白号', type: 'INSERT' }], [A + 'TtUserController.java', 'generateCardMerchant', { menu: '用户列表', action: '生成卡商账号', type: 'INSERT' }], [A + 'TtUserController.java', 'edit', { menu: '用户列表', action: '修改', type: 'UPDATE', entity: 'TtUser', mapper: 'TtUserMapper' }], [A + 'TtUserController.java', 'editInvitationCode', { menu: '用户列表', action: '修改邀请码', type: 'UPDATE', entity: 'TtUser', mapper: 'TtUserMapper' }], [A + 'TtUserController.java', 'remove', { menu: '用户列表', action: '删除', type: 'DELETE' }], [A + 'TtUserController.java', 'removeUserPackSackData', { menu: '背包列表', action: '删除', type: 'DELETE' }], [A + 'TtUserController.java', 'resetUserBalance', { menu: '用户列表', action: '重置余额积分', type: 'CLEAN', entity: 'TtUser', mapper: 'TtUserMapper', idField: 'userId' }], [A + 'TtUserController.java', 'forceDelete', { menu: '用户列表', action: '真删除', type: 'FORCE' }], // ---- 亏损返水(ttredPacket/lossRebate.vue:31 保存上限 / 112 发放CDK)---- [A + 'LossRebateController.java', 'grant', { menu: '亏损返水', action: '发放CDK', type: 'GRANT', entity: 'TtLossRebate', mapper: 'TtLossRebateMapper', idField: 'id' }], [A + 'LossRebateController.java', 'saveConfig', { menu: '亏损返水', action: '保存上限', type: 'UPDATE' }], // ---- 卡商充值(ttCardMerchantRecharge/index.vue:126 确认充值 / 160 确认修改)---- [A + 'TtCardMerchantController.java', 'cardMerchantRecharge', { menu: '卡商充值', action: '确认充值', type: 'GRANT', entity: 'TtUser', mapper: 'TtUserMapper', idField: 'userId' }], [A + 'TtCardMerchantController.java', 'cardMerchantUpdateBalance', { menu: '卡商充值', action: '确认修改', type: 'UPDATE', entity: 'TtUser', mapper: 'TtUserMapper', idField: 'userId' }], // ---- 生成空白卡密(blankCardKey/index.vue:5 生成空白卡密 开弹窗)---- [A + 'AdminBlankCardKeyController.java', 'generate', { menu: '生成空白卡密', action: '生成空白卡密', type: 'INSERT' }], // ---- 疯狂转盘 游戏配置(game/wheel/config/index.vue:8 重置奖池 / 44 调整奖池 / 241 保存)---- [W + 'AdminWheelController.java', 'resetPool', { menu: '游戏配置', action: '重置奖池', type: 'CLEAN' }], [W + 'AdminWheelController.java', 'adjustPool', { menu: '游戏配置', action: '调整奖池', type: 'GRANT' }], [W + 'AdminWheelController.java', 'updateGameConfig', { menu: '游戏配置', action: '保存', type: 'UPDATE' }], // ---- 疯狂转盘 奖励矩阵(game/wheel/prize/index.vue:79 保存 / 89 全部保存 → saveMatrix)---- [W + 'AdminWheelController.java', 'matrixSave', { menu: '奖励矩阵', action: '保存', type: 'UPDATE' }], // ---- 商城列表(ttShopping/index.vue:75 一键下架 / 82 一键上架)---- [A + 'ShoppingController.java', 'putAwayAll', { menu: '商城列表', action: '一键上架', type: 'UPDATE' }], [A + 'ShoppingController.java', 'soldOutAll', { menu: '商城列表', action: '一键下架', type: 'UPDATE' }], // ---- Roll房列表(ttRoll/index.vue:222 新 增 / 223 修 改 / 120 删除)---- [A + 'TtRollController.java', 'add', { menu: 'Roll房列表', action: '新增', type: 'INSERT', entity: 'TtRoll', mapper: 'TtRollMapper' }], [A + 'TtRollController.java', 'edit', { menu: 'Roll房列表', action: '修改', type: 'UPDATE', entity: 'TtRoll', mapper: 'TtRollMapper' }], [A + 'TtRollController.java', 'remove', { menu: 'Roll房列表', action: '删除', type: 'DELETE' }], // ---- 抽奖活动(turnoverLottery/list.vue:43 新增抽奖活动 / 86 修改 / 92 删除)---- [A + 'TurnoverLotteryAdminController.java', 'create', { menu: '抽奖活动', action: '新增抽奖活动', type: 'INSERT' }], [A + 'TurnoverLotteryAdminController.java', 'update', { menu: '抽奖活动', action: '修改', type: 'UPDATE' }], [A + 'TurnoverLotteryAdminController.java', 'delete', { menu: '抽奖活动', action: '删除', type: 'DELETE' }], // ---- 奥林匹斯 奖池水位(game/olympus/rewardPool/index.vue:101/106 批量注水|抽水、111 重置调整、172/175 行内图标→弹窗 213 确定注水|抽水)---- [O + 'OlympusAdminController.java', 'rewardPoolAdjust', { menu: '奖池水位', action: '注水/抽水', type: 'GRANT' }], [O + 'OlympusAdminController.java', 'rewardPoolAdjustBatch', { menu: '奖池水位', action: '批量注水/抽水', type: 'GRANT' }], [O + 'OlympusAdminController.java', 'rewardPoolAdjustReset', { menu: '奖池水位', action: '重置调整', type: 'CLEAN' }], // ---- 宝箱列表(ttBox/index.vue:48 新增 / 59 修改 / 70 删除 / 282 重置宝箱)---- [A + 'TtBoxController.java', 'add', { menu: '宝箱列表', action: '新增', type: 'INSERT', entity: 'TtBox', mapper: 'TtBoxMapper' }], [A + 'TtBoxController.java', 'edit', { menu: '宝箱列表', action: '修改', type: 'UPDATE' }], [A + 'TtBoxController.java', 'remove', { menu: '宝箱列表', action: '删除', type: 'DELETE' }], // 注意:resetBox 是 @GetMapping,但它改数据(GET 不在常规扫描范围内,这里显式补上) [A + 'TtBoxController.java', 'resetBox', { menu: '宝箱列表', action: '重置宝箱', type: 'CLEAN', entity: 'TtBox', mapper: 'TtBoxMapper', idField: 'boxId' }], // ---- Roll 奖品/房间(awarddetali.vue:22 指定获奖者 / 23 取消奖品;rollUser.vue:10 加入机器人 / 18 加入用户)---- [A + 'TtRollController.java', 'namedWinner', { menu: '装备列表', action: '指定获奖者', type: 'INSERT' }], [A + 'TtRollController.java', 'cancelNamedWinner', { menu: '装备列表', action: '取消奖品', type: 'DELETE' }], [A + 'TtRollController.java', 'addRobot', { menu: '房间用户', action: '加入机器人', type: 'INSERT' }], [A + 'TtRollController.java', 'addUser', { menu: '房间用户', action: '加入用户', type: 'INSERT' }], // ---- 奥林匹斯 箱子配置(game/olympus/box/index.vue:53 编辑 / 180 保存 / 15+141 一键重算分布 / 179 保存为模板 / 214 应用选中模板 / 207 删除模板)---- [O + 'OlympusAdminController.java', 'boxSave', { menu: '箱子配置', action: '编辑', type: 'UPDATE' }], [O + 'OlympusAdminController.java', 'boxOrnamentsSave', { menu: '箱子配置', action: '保存', type: 'UPDATE' }], [O + 'OlympusAdminController.java', 'recalcBatch', { menu: '箱子配置', action: '一键重算分布', type: 'CLEAN' }], [O + 'OlympusAdminController.java', 'templateSave', { menu: '箱子配置', action: '保存为模板', type: 'INSERT' }], [O + 'OlympusAdminController.java', 'templateApply', { menu: '箱子配置', action: '应用选中模板', type: 'UPDATE' }], [O + 'OlympusAdminController.java', 'templateDelete', { menu: '箱子配置', action: '删除', type: 'DELETE' }], // ---- 奥林匹斯 奖品管理(game/olympus/prize/index.vue:44 编辑 → 87 确定 → prizeSave)---- [O + 'OlympusAdminController.java', 'prizeSave', { menu: '奖品管理', action: '编辑', type: 'UPDATE' }], // ---- 消费排行榜奖励(rechargeRankingReward.vue:5 生成消费奖励 / 23 修改 / 8 重置→handleReset:165)---- [A + 'TtRechargeRankingRewardController.java', 'generatePromotionLevel', { menu: '消费排行榜奖励', action: '生成消费奖励', type: 'UPDATE' }], [A + 'TtRechargeRankingRewardController.java', 'edit', { menu: '消费排行榜奖励', action: '修改', type: 'UPDATE' }], [A + 'TtRechargeRankingRewardController.java', 'truncateRankingReward', { menu: '消费排行榜奖励', action: '重置', type: 'CLEAN' }], // ---- 对战排行榜奖励(fightRankingReward.vue:5 生成排名奖励 / 23 修改 / 8 重置→handleReset:167)---- [A + 'TtFightRankingRewardController.java', 'generatePromotionLevel', { menu: '对战排行榜奖励', action: '生成排名奖励', type: 'UPDATE' }], [A + 'TtFightRankingRewardController.java', 'edit', { menu: '对战排行榜奖励', action: '修改', type: 'UPDATE' }], [A + 'TtFightRankingRewardController.java', 'truncateRankingReward', { menu: '对战排行榜奖励', action: '重置', type: 'CLEAN' }], // ---- 饰品列表(ttOrnaments/index.vue:58 新增饰品 / 123 编辑 / 125=77 删除 / 117 发放饰品)---- [A + 'TtOrnamentsController.java', 'add', { menu: '饰品列表', action: '新增饰品', type: 'INSERT', entity: 'TtOrnament', mapper: 'TtOrnamentMapper' }], [A + 'TtOrnamentsController.java', 'edit', { menu: '饰品列表', action: '编辑', type: 'UPDATE', entity: 'TtOrnament', mapper: 'TtOrnamentMapper' }], [A + 'TtOrnamentsController.java', 'delete', { menu: '饰品列表', action: '删除', type: 'DELETE' }], [A + 'TtOrnamentsController.java', 'grantOrnaments', { menu: '饰品列表', action: '发放饰品', type: 'GRANT' }], // ================= 以下为"存量旧 JSON 格式"转人话(按钮文案已逐页核实:都是标准 新增/修改/删除)================= // ---- 福利列表 / 福利开箱记录(ttWelfare/index.vue、welfareBoxRecords.vue)---- [A + 'TtWelfareController.java', 'add', { menu: '福利列表', action: '新增', type: 'INSERT', auto: true }], [A + 'TtWelfareController.java', 'edit', { menu: '福利列表', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtWelfareController.java', 'remove', { menu: '福利列表', action: '删除', type: 'DELETE' }], [A + 'TtWelfareController.java', 'removeBoxRecords', { menu: '福利开箱记录', action: '删除', type: 'DELETE' }], [A + 'TtWelfareController.java', 'clearBoxRecords', { menu: '福利开箱记录', action: '清空全部记录', type: 'CLEAN' }], // ---- 每月充值福利(monthlyRecharges.vue)---- [A + 'TtWelfareMonthlyRechargesController.java', 'add', { menu: '每月充值福利', action: '新增', type: 'INSERT', auto: true }], [A + 'TtWelfareMonthlyRechargesController.java', 'edit', { menu: '每月充值福利', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtWelfareMonthlyRechargesController.java', 'remove', { menu: '每月充值福利', action: '删除', type: 'DELETE' }], // ---- 首充赠送(ttBonus/firstRecharge.vue)---- [A + 'TtFirstRechargeController.java', 'add', { menu: '首充赠送', action: '新增', type: 'INSERT', auto: true }], [A + 'TtFirstRechargeController.java', 'edit', { menu: '首充赠送', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtFirstRechargeController.java', 'remove', { menu: '首充赠送', action: '删除', type: 'DELETE' }], // ---- CDK管理(ttRedPack/index.vue)---- [A + 'TtRedPackController.java', 'add', { menu: 'CDK管理', action: '新增', type: 'INSERT', auto: true }], [A + 'TtRedPackController.java', 'edit', { menu: 'CDK管理', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtRedPackController.java', 'remove', { menu: 'CDK管理', action: '删除', type: 'DELETE' }], // ---- 汰换记录(ttReplacementRecord/index.vue)---- [A + 'TtReplacementRecordController.java', 'add', { menu: '汰换记录', action: '新增', type: 'INSERT', auto: true }], [A + 'TtReplacementRecordController.java', 'edit', { menu: '汰换记录', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtReplacementRecordController.java', 'remove', { menu: '汰换记录', action: '删除', type: 'DELETE' }], // ---- 机器人对战分组(ttFight/robotFightGroup.vue)---- [A + 'TtRobotFightGroupController.java', 'add', { menu: '机器人对战分组', action: '新增', type: 'INSERT', auto: true }], [A + 'TtRobotFightGroupController.java', 'edit', { menu: '机器人对战分组', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtRobotFightGroupController.java', 'remove', { menu: '机器人对战分组', action: '删除', type: 'DELETE' }], // ---- 机器人对战分组宝箱(ttFight/robotFightGroupBox.vue)---- [A + 'TtRobotFightGroupBoxController.java', 'add', { menu: '机器人对战分组宝箱', action: '新增', type: 'INSERT', auto: true }], [A + 'TtRobotFightGroupBoxController.java', 'edit', { menu: '机器人对战分组宝箱', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtRobotFightGroupBoxController.java', 'remove', { menu: '机器人对战分组宝箱', action: '删除', type: 'DELETE' }], // ---- 机器人对战配置(ttFight/robotFightConfig.vue,两个 controller 共用一页)---- [A + 'TtRobotFightJoinConfigController.java', 'add', { menu: '机器人对战配置', action: '新增', type: 'INSERT', auto: true }], [A + 'TtRobotFightJoinConfigController.java', 'edit', { menu: '机器人对战配置', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtRobotFightJoinConfigController.java', 'remove', { menu: '机器人对战配置', action: '删除', type: 'DELETE' }], [A + 'TtRobotFightPriceConfigController.java', 'add', { menu: '机器人对战配置', action: '新增', type: 'INSERT', auto: true }], [A + 'TtRobotFightPriceConfigController.java', 'edit', { menu: '机器人对战配置', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtRobotFightPriceConfigController.java', 'remove', { menu: '机器人对战配置', action: '删除', type: 'DELETE' }], // ---- 转赠申请(delivery/transferApply.vue:同意 / 驳回 / 确定同意)---- [A + 'TtTransferApplyController.java', 'approve', { menu: '转赠申请', action: '同意', type: 'GRANT' }], [A + 'TtTransferApplyController.java', 'reject', { menu: '转赠申请', action: '驳回', type: 'GRANT' }], [A + 'TtTransferApplyController.java', 'approveBatch', { menu: '转赠申请', action: '确定同意', type: 'GRANT' }], [A + 'TtTransferApplyController.java', 'rejectBatch', { menu: '转赠申请', action: '驳回', type: 'GRANT' }], // ---- 任务列表(ttTaskCenter/index.vue)---- [A + 'TtTaskCenterController.java', 'add', { menu: '任务列表', action: '新增', type: 'INSERT', auto: true }], [A + 'TtTaskCenterController.java', 'edit', { menu: '任务列表', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtTaskCenterController.java', 'remove', { menu: '任务列表', action: '删除', type: 'DELETE' }], // ---- 开箱机会(ttOrnaments/boxOpenChance.vue;库里无同名菜单,用页面功能名)---- [A + 'TtBoxOpenChanceController.java', 'add', { menu: '开箱机会', action: '新增', type: 'INSERT', auto: true }], [A + 'TtBoxOpenChanceController.java', 'edit', { menu: '开箱机会', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtBoxOpenChanceController.java', 'remove', { menu: '开箱机会', action: '删除', type: 'DELETE' }], // ================= 其余有按钮证据的页面(标签来自 evidence.js:直接调用方的按钮文案,CRUD 页按工具条 新增/修改/删除)================= // ---- 公告(ttSetting/announcement.vue)---- [A + 'TtAnnouncementController.java', 'addAnnouncement', { menu: '公告', action: '新增', type: 'INSERT', auto: true }], [A + 'TtAnnouncementController.java', 'editAnnouncement', { menu: '公告', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtAnnouncementController.java', 'removeAnnouncementByAnnouncementId', { menu: '公告', action: '删除', type: 'DELETE' }], // ---- Banner设置 / 彩金 / 文章 / 文章类型 / 宝箱分类(ttSetting/banner.vue, ttBonus/index.vue, contentList, contentType, ttBox/boxType)---- [A + 'TtBannerController.java', 'add', { menu: 'Banner设置', action: '新增', type: 'INSERT', auto: true }], [A + 'TtBannerController.java', 'edit', { menu: 'Banner设置', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtBannerController.java', 'remove', { menu: 'Banner设置', action: '删除', type: 'DELETE' }], [A + 'TtBonusController.java', 'add', { menu: '彩金', action: '新增', type: 'INSERT', auto: true }], [A + 'TtBonusController.java', 'edit', { menu: '彩金', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtBonusController.java', 'remove', { menu: '彩金', action: '删除', type: 'DELETE' }], [A + 'TtContentController.java', 'add', { menu: '文章', action: '新增', type: 'INSERT', auto: true }], [A + 'TtContentController.java', 'edit', { menu: '文章', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtContentController.java', 'remove', { menu: '文章', action: '删除', type: 'DELETE' }], [A + 'TtContentTypeController.java', 'add', { menu: '文章类型', action: '新增', type: 'INSERT', auto: true }], [A + 'TtContentTypeController.java', 'edit', { menu: '文章类型', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtContentTypeController.java', 'remove', { menu: '文章类型', action: '删除', type: 'DELETE' }], [A + 'TtBoxTypeController.java', 'add', { menu: '宝箱分类', action: '新增', type: 'INSERT', auto: true }], [A + 'TtBoxTypeController.java', 'edit', { menu: '宝箱分类', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtBoxTypeController.java', 'remove', { menu: '宝箱分类', action: '删除', type: 'DELETE' }], // ---- 宝箱饰品(挂在宝箱列表下;批量填货按钮见 ttBox/index.vue:283)---- [A + 'TtBoxOrnamentsController.java', 'add', { menu: '宝箱列表', action: '新增', type: 'INSERT', auto: true }], [A + 'TtBoxOrnamentsController.java', 'edit', { menu: '宝箱列表', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtBoxOrnamentsController.java', 'remove', { menu: '宝箱列表', action: '删除', type: 'DELETE' }], [A + 'TtBoxOrnamentsController.java', 'batchAdd', { menu: '宝箱列表', action: '批量填货', type: 'INSERT' }], // ---- 等级类(ttOrnamentsLevel/index.vue, ttSetting/promotionLevel.vue, ttSetting/vips.vue)---- [A + 'TtOrnamentsLevelController.java', 'edit', { menu: '饰品等级', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtOrnamentsLevelController.java', 'generateOrnamentsLevel', { menu: '饰品等级', action: '新增', type: 'INSERT' }], [A + 'TtPromotionLevelController.java', 'edit', { menu: '推广等级设置', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtPromotionLevelController.java', 'generatePromotionLevel', { menu: '推广等级设置', action: '生成推广列表', type: 'INSERT' }], [A + 'TtPromotionLevelController.java', 'truncateVipLevel', { menu: '推广等级设置', action: '重置', type: 'CLEAN' }], [A + 'TtVipLevelController.java', 'edit', { menu: 'VIP等级设置', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtVipLevelController.java', 'generateVipLevel', { menu: 'VIP等级设置', action: '生成VIP等级', type: 'INSERT' }], [A + 'TtVipLevelController.java', 'truncateVipLevel', { menu: 'VIP等级设置', action: '重置', type: 'CLEAN' }], // ---- 充值/卡密(ttRechargeList/index.vue, ttRechargeRecord? , ttGenerateCard/index.vue:135)---- [A + 'TtRechargeListController.java', 'add', { menu: '货币列表', action: '新增', type: 'INSERT', auto: true }], [A + 'TtRechargeListController.java', 'edit', { menu: '货币列表', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtRechargeListController.java', 'remove', { menu: '货币列表', action: '删除', type: 'DELETE' }], [A + 'TtRechargeConfigController.java', 'add', { menu: '网站货币管理', action: '新增', type: 'INSERT', auto: true }], [A + 'TtRechargeConfigController.java', 'edit', { menu: '网站货币管理', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtRechargeConfigController.java', 'remove', { menu: '网站货币管理', action: '删除', type: 'DELETE' }], [A + 'TtRechargeCardController.java', 'generateCard', { menu: '卡密', action: '生成卡密', type: 'INSERT' }], [A + 'TtRechargeCardController.java', 'generateBlankCard', { menu: '卡密', action: '生成空白卡密', type: 'INSERT' }], // ---- CDK口令 / Roll奖池 / 限时Roll / 官方周期Roll配置 / 奖项配置 ---- [A + 'TtRedPacketController.java', 'add', { menu: 'CDK口令', action: '新增', type: 'INSERT', auto: true }], [A + 'TtRedPacketController.java', 'edit', { menu: 'CDK口令', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtRedPacketController.java', 'remove', { menu: 'CDK口令', action: '删除', type: 'DELETE' }], [A + 'TtRollJackpotController.java', 'add', { menu: '奖池列表', action: '新增', type: 'INSERT', auto: true }], [A + 'TtRollJackpotController.java', 'edit', { menu: '奖池列表', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtRollJackpotController.java', 'remove', { menu: '奖池列表', action: '删除', type: 'DELETE' }], [A + 'TtRollJackpotOrnamentsController.java', 'add', { menu: '奖池列表', action: '新增', type: 'INSERT', auto: true }], [A + 'TtRollJackpotOrnamentsController.java', 'edit', { menu: '奖池列表', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtRollJackpotOrnamentsController.java', 'remove', { menu: '奖池列表', action: '删除', type: 'DELETE' }], [A + 'TtTimeRollController.java', 'add', { menu: '时间roll房', action: '新增', type: 'INSERT', auto: true }], [A + 'TtTimeRollController.java', 'edit', { menu: '时间roll房', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtTimeRollController.java', 'remove', { menu: '时间roll房', action: '删除', type: 'DELETE' }], [A + 'TtOfficialRollConfigController.java', 'add', { menu: '官方周期Roll配置', action: '新增', type: 'INSERT', auto: true }], [A + 'TtOfficialRollConfigController.java', 'edit', { menu: '官方周期Roll配置', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtOfficialRollConfigController.java', 'remove', { menu: '官方周期Roll配置', action: '删除', type: 'DELETE' }], [A + 'TurnoverLotteryPrizeController.java', 'add', { menu: '奖项配置', action: '新增', type: 'INSERT', auto: true }], [A + 'TurnoverLotteryPrizeController.java', 'edit', { menu: '奖项配置', action: '修改', type: 'UPDATE', auto: true }], [A + 'TurnoverLotteryPrizeController.java', 'remove', { menu: '奖项配置', action: '删除', type: 'DELETE' }], // ---- 升级饰品 / 失败奖励(ttUpgrade/index.vue, upgradeFall.vue)---- [A + 'TtUpgradeOrnamentsController.java', 'batchAdd', { menu: '饰品列表', action: '新增', type: 'INSERT' }], [A + 'TtUpgradeOrnamentsController.java', 'edit', { menu: '饰品列表', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtUpgradeOrnamentsController.java', 'remove', { menu: '饰品列表', action: '删除', type: 'DELETE' }], [A + 'TtUpgradeFailOrnamentsController.java', 'batchAdd', { menu: '失败奖励', action: '新增', type: 'INSERT' }], [A + 'TtUpgradeFailOrnamentsController.java', 'edit', { menu: '失败奖励', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtUpgradeFailOrnamentsController.java', 'remove', { menu: '失败奖励', action: '删除', type: 'DELETE' }], // ---- 头像设置 / 网站道具 / 发货记录 / 商城上下架 / 站点参数 ---- [A + 'TtUserAvatarController.java', 'add', { menu: '头像设置', action: '新增', type: 'INSERT', auto: true }], [A + 'TtUserAvatarController.java', 'edit', { menu: '头像设置', action: '修改', type: 'UPDATE', auto: true }], [A + 'TtUserAvatarController.java', 'remove', { menu: '头像设置', action: '删除', type: 'DELETE' }], [A + 'WebsitePropertyController.java', 'add', { menu: '网站道具', action: '新增', type: 'INSERT', auto: true }], [A + 'WebsitePropertyController.java', 'edit', { menu: '网站道具', action: '修改', type: 'UPDATE', auto: true }], [A + 'WebsitePropertyController.java', 'remove', { menu: '网站道具', action: '删除', type: 'DELETE' }], [A + 'TtDeliveryRecordController.java', 'deliveryApprove', { menu: '发货记录', action: '确认发货', type: 'GRANT' }], [A + 'ShoppingController.java', 'batchPutAwayOrSoldOut', { menu: '商城列表', action: '上架/下架', type: 'UPDATE' }], [A + 'WebsiteSetupController.java', 'updateParameterSetting', { menu: '网站设置', action: '保存', type: 'UPDATE' }], // ---- 疯狂转盘 奖励矩阵删除 ---- [W + 'AdminWheelController.java', 'matrixDelete', { menu: '奖励矩阵', action: '删除', type: 'DELETE' }], ]; const IMPORTS = { TtUser: 'com.ruoyi.domain.entity.sys.TtUser', TtUserMapper: 'com.ruoyi.admin.mapper.TtUserMapper', TtLossRebate: 'com.ruoyi.domain.entity.TtLossRebate', TtLossRebateMapper: 'com.ruoyi.admin.mapper.TtLossRebateMapper', TtRoll: 'com.ruoyi.domain.entity.roll.TtRoll', TtRollMapper: 'com.ruoyi.admin.mapper.TtRollMapper', TtBox: 'com.ruoyi.domain.other.TtBox', TtBoxMapper: 'com.ruoyi.admin.mapper.TtBoxMapper', TtOrnament: 'com.ruoyi.domain.entity.TtOrnament', TtOrnamentMapper: 'com.ruoyi.admin.mapper.TtOrnamentMapper', }; const isBlockMember = (l) => /^\s*(\/\/|\*|\/\*|@)/.test(l) || (/^\s*[\w.]+\s*=/.test(l) && /[,)]\s*$/.test(l)); // ---- 自动解析实体/Mapper:attrs.auto = true 时,按方法入参里的实体类型去认 ---- function collectFiles(dir, out, suffix) { if (!fs.existsSync(dir)) return out; for (const e of fs.readdirSync(dir, { withFileTypes: true })) { const p = path.join(dir, e.name); if (e.isDirectory()) collectFiles(p, out, suffix); else if (e.isFile() && e.name.endsWith(suffix)) out.push(p); } return out; } const ENTITY = new Map(); // simpleName -> fqn(skins-model 全部类;真正用的时候还要求有 Mapper) for (const f of collectFiles(path.join(ROOT, 'skins-model'), [], '.java')) { const src = fs.readFileSync(f, 'utf8'); const cls = (src.match(/public\s+class\s+(\w+)/) || [, null])[1]; const pkg = (src.match(/^package\s+([\w.]+);/m) || [, ''])[1]; if (cls && !ENTITY.has(cls)) ENTITY.set(cls, `${pkg}.${cls}`); } const MAPPER = new Map(); // entitySimple -> {simple, fqn}(BaseMapper 与普通 MyBatis 接口都要认) for (const f of collectFiles(path.join(ROOT, 'skins-service'), [], 'Mapper.java')) { const simple = path.basename(f, '.java'); const entityName = simple.endsWith('Mapper') ? simple.slice(0, -6) : null; if (!entityName || !ENTITY.has(entityName) || MAPPER.has(entityName)) continue; const pkg = (fs.readFileSync(f, 'utf8').match(/^package\s+([\w.]+);/m) || [, ''])[1]; MAPPER.set(entityName, { simple, fqn: `${pkg}.${simple}` }); } /** 取方法参数里的类型名(优先 @RequestBody 那个) */ function methodTypes(lines, sig) { let text = ''; for (let i = sig; i < Math.min(sig + 10, lines.length); i++) { text += lines[i] + ' '; if (/\)\s*\{/.test(lines[i]) || /\)\s*$/.test(lines[i])) break; } const inner = (text.match(/\(([\s\S]*?)\)/) || [, ''])[1]; const types = []; const rb = inner.match(/@RequestBody\s+(?:@\w+(?:\([^)]*\))?\s+)*(\w+)/); if (rb) types.push(rb[1]); for (const m of inner.matchAll(/(?:^|,)\s*(?:final\s+)?([A-Z]\w+)\s+\w+/g)) if (!types.includes(m[1])) types.push(m[1]); return types; } function render(a, indent) { const parts = [`title = "${a.menu}"`, `action = "${a.action}"`, `businessType = BusinessType.${a.type}`]; if (a.entity) parts.push(`entityClass = ${a.entity}.class`); if (a.mapper) parts.push(`mapperClass = ${a.mapper}.class`); if (a.idField) parts.push(`idField = "${a.idField}"`); const one = `${indent}@Log(${parts.join(', ')})`; if (one.length <= 130) return [one]; return [`${indent}@Log(${parts.slice(0, 3).join(', ')},`, `${indent} ${parts.slice(3).join(', ')})`]; } function insertImports(lines, entries) { for (const e of entries) { // 集合里既有全限定名(com.ruoyi...),也有短名(TtUser),分开处理 const fqn = e.includes('.') ? e : IMPORTS[e]; if (!fqn) continue; const simple = fqn.split('.').pop(); if (lines.some((l) => new RegExp(`^import\\s+[\\w.]*\\b${simple};`).test(l.trim()))) continue; let at = lines.findIndex((l) => /^import com\.ruoyi\./.test(l)); if (at < 0) at = lines.findIndex((l) => /^import /.test(l)); if (at >= 0) lines.splice(at, 0, `import ${fqn};`); } } const byFile = new Map(); for (const [file, method, attrs] of TARGETS) { if (!byFile.has(file)) byFile.set(file, []); byFile.get(file).push({ method, attrs }); } if (process.argv.includes('--debug')) { console.log(`实体索引 ${ENTITY.size},Mapper 索引 ${MAPPER.size}`); for (const t of ['TtWelfare', 'TtRedPack', 'TtTaskCenter', 'TtBox']) { console.log(` ${t}: entity=${ENTITY.get(t) || '无'} mapper=${MAPPER.has(t) ? MAPPER.get(t).fqn : '无'}`); } } let done = 0, skipped = 0; for (const [rel, list] of byFile) { const abs = path.join(ROOT, rel); const lines = fs.readFileSync(abs, 'utf8').split(/\r?\n/); const edits = []; const imports = new Set(['com.ruoyi.common.annotation.Log', 'com.ruoyi.common.enums.BusinessType']); for (const { method, attrs } of list) { // 找方法签名 let sig = -1; for (let i = 0; i < lines.length; i++) { if (new RegExp(`^\\s*(?:public|protected|private)\\s+[\\w<>\\[\\],\\.\\? ]+\\s+${method}\\s*\\(`).test(lines[i])) { sig = i; break; } } if (sig < 0) { console.log(` [跳过] 找不到方法 ${path.basename(rel)}.${method}`); skipped++; continue; } // 注解块顶部 let top = sig; for (let j = sig - 1; j >= 0; j--) { if (lines[j].trim() === '' || !isBlockMember(lines[j])) break; top = j; } // 已有 @Log 就整块删掉(支持多行) let start = -1, end = -1; for (let j = top; j < sig; j++) { if (/^\s*@Log\(/.test(lines[j])) { start = j; let depth = 0; outer: for (let k = j; k < sig; k++) { for (const ch of lines[k]) { if (ch === '(') depth++; else if (ch === ')') { depth--; if (depth === 0) { end = k; break outer; } } } } break; } } const indent = (lines[top].match(/^\s*/) || [' '])[0]; // 自动认实体:入参里第一个"有 @TableName 且有对应 Mapper"的类型 if (attrs.auto && !attrs.entity) { for (const t of methodTypes(lines, sig)) { if (ENTITY.has(t) && MAPPER.has(t)) { attrs.entity = t; attrs.mapper = MAPPER.get(t).simple; break; } } } edits.push({ start, end, top, newLines: render(attrs, indent), method, attrs }); if (attrs.entity && ENTITY.has(attrs.entity)) imports.add(ENTITY.get(attrs.entity)); if (attrs.entity && MAPPER.has(attrs.entity)) imports.add(MAPPER.get(attrs.entity).fqn); if (attrs.mapper && IMPORTS[attrs.mapper]) imports.add(IMPORTS[attrs.mapper]); } for (const ed of edits.sort((a, b) => b.top - a.top)) { if (ed.start >= 0) lines.splice(ed.start, ed.end - ed.start + 1, ...ed.newLines); else lines.splice(ed.top, 0, ...ed.newLines); console.log(` ${path.basename(rel)}.${ed.method} → ${ed.attrs.menu} / ${ed.attrs.action}` + `${ed.attrs.entity ? ' diff=' + ed.attrs.entity : ''}${ed.attrs.idField ? ' idField=' + ed.attrs.idField : ''}`); done++; } insertImports(lines, imports); if (apply) fs.writeFileSync(abs, lines.join('\r\n'), 'utf8'); } console.log(`\n${apply ? 'APPLIED' : 'DRY-RUN'}: 写入 ${done} 个方法,跳过 ${skipped}`);