From 2a89a1ab9d5b2df1df6d36c7182d43833a6aca8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E6=96=B9=E6=88=90?= Date: Wed, 19 Nov 2025 01:35:55 +0800 Subject: [PATCH] =?UTF-8?q?feat(admin):=20=E6=9B=B4=E6=96=B0=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=E8=B5=84=E6=BA=90=E6=96=87=E4=BB=B6=E5=8F=8A=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E7=9B=B8=E5=85=B3=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refactor(service): 修改banner和guild删除逻辑为软删除 fix(service): 修复删除操作使用软删除而非物理删除 build: 添加SQLite测试仓库实现 docs: 新增奖励管理字段拆分和批量抽奖UI改造文档 ci: 更新CI忽略文件 style: 清理无用资源文件 --- .DS_Store | Bin 10244 -> 10244 bytes .trae/.ignore | 0 .../修复活动创建向导步骤逻辑并统一全局风格.md | 49 +++ .trae/documents/删除级联逻辑优化方案.md | 86 ++++++ ...表批量操作与活动创建优化及仪表盘修复方案.md | 93 ++++++ .trae/documents/实现回收站与软删除机制.md | 68 ++++ ...误弹与统一“新建活动”UI为 Element Plus 官方风格.md | 49 +++ .../活动创建“下一步”无响应的排查与改造方案.md | 31 ++ .trae/documents/活动创建流程重构方案.md | 87 ++++++ ...奖品数量、登录态与奖励管理价格增强改造方案.md | 51 +++ .../统一活动创建页面UI样式为一致的主题规范.md | 39 +++ .../补齐删除级联遗漏清单与改造计划.md | 41 +++ ...活动创建向导样式为 Element Plus 官方风格.md | 51 +++ build/resources/.DS_Store | Bin 6148 -> 6148 bytes ...e_type_script_setup_true_lang-M6sE6E-D.js} | 2 +- ...e_type_script_setup_true_lang-BRyONWq_.js} | 2 +- ...CDk-bJ.js => EffectEditDialog-CKoMjh_c.js} | 2 +- .../assets/EffectEditDialog-CKoMjh_c.js.gz | Bin 0 -> 4209 bytes .../assets/EffectEditDialog-DVCDk-bJ.js.gz | Bin 4209 -> 0 bytes .../assets/EffectManagerDialog-B8hoH5eF.js | 1 + .../assets/EffectManagerDialog-BOpJq-as.js | 1 - ...{Iframe-Bfhx1JL3.js => Iframe-Bt6u-iQX.js} | 2 +- ...-DqS6laCq.js => LoginLeftView-DQuNwejA.js} | 2 +- ...o2cxQg.js => RuleConfigDialog-DC9G2ww3.js} | 2 +- .../assets/RuleConfigDialog-DC9G2ww3.js.gz | Bin 0 -> 8922 bytes .../assets/RuleConfigDialog-XFo2cxQg.js.gz | Bin 8916 -> 0 bytes .../admin/assets/TitleEditDialog-BoomnsCf.js | 1 + .../admin/assets/TitleEditDialog-Cw8i3HNt.js | 1 - ...JK.js => UserAssignmentDialog-C3ancW_U.js} | 2 +- .../UserAssignmentDialog-C3ancW_U.js.gz | Bin 0 -> 4299 bytes .../UserAssignmentDialog-CgQlEmJK.js.gz | Bin 4296 -> 0 bytes .../admin/assets/_baseIteratee-DRiXPtOg.js | 1 - .../admin/assets/_baseIteratee-iUx42eT-.js | 1 + .../admin/assets/_initCloneObject-7oWMW-Ot.js | 1 - .../admin/assets/_initCloneObject-DfOnBHdn.js | 1 + .../admin/assets/active-user-CdPYh29t.js | 1 - .../admin/assets/active-user-CqefX-qJ.js | 1 + ...e_type_script_setup_true_lang-C_Ah812_.js} | 2 +- ...ivity-0udS8Goz.js => activity-B4-0dphm.js} | 2 +- ...ztZZvI.js => activity-lottery-D_9KIAZu.js} | 2 +- .../activity-prize-analysis-BbWF0RUj.js | 1 - .../activity-prize-analysis-DLw5P596.js | 1 + ...e_type_script_setup_true_lang-Be3zdQ_a.js} | 2 +- .../admin/assets/activity-search-61nDqo2V.js | 1 - .../admin/assets/activity-search-p-0MbIUg.js | 1 + .../assets/add-coupon-dialog-DW1AyPdi.js | 1 - .../assets/add-coupon-dialog-DhXVbOMW.js | 1 + ...e_type_script_setup_true_lang-CYcx2nJS.js} | 2 +- .../assets/add-item-card-dialog-C6lD4OlI.js | 1 - .../assets/add-item-card-dialog-CZ8UomRh.js | 1 + ...ue_type_script_setup_true_lang-S5Y5BQPs.js | 1 + ...ue_type_script_setup_true_lang-r-95eO1C.js | 1 - .../assets/add-points-dialog-BOqGK02x.js | 1 - .../assets/add-points-dialog-CocGMOL9.js | 1 + ...ue_type_script_setup_true_lang-B99Uz4mM.js | 1 - ...ue_type_script_setup_true_lang-CDG3qGoH.js | 1 + ...7lwI2HP.js => adminActivities-BFodRb8S.js} | 2 +- .../assets/assign-title-dialog-BBBwnMYv.js | 1 + .../assets/assign-title-dialog-JfcY-TAw.js | 1 - ...e_type_script_setup_true_lang-lCpMfr54.js} | 2 +- .../assets/batch-draw-dialog-KCzFry5O.js | 1 - .../assets/batch-draw-dialog-wyt1sOed.js | 1 + .../assets/batch-draw-dialog-wyt1sOed.js.gz | Bin 0 -> 6181 bytes ...ue_type_script_setup_true_lang-D6SWKrJK.js | 1 - ...type_script_setup_true_lang-D6SWKrJK.js.gz | Bin 5871 -> 0 bytes .../admin/assets/card-list-Dkk_zimK.js | 1 - .../admin/assets/card-list-DxDGPL3Q.js | 1 + ...e_type_script_setup_true_lang-qY_uPixj.js} | 2 +- .../admin/assets/category-search-CRg9E3ZY.js | 1 + .../admin/assets/category-search-CzNFlBJ5.js | 1 - .../admin/assets/cloneDeep-tG3tBIiW.js | 1 + .../admin/assets/cloneDeep-zs6l5dp3.js | 1 - .../admin/assets/coupon-dialog-CGVfD7e9.js | 1 + .../admin/assets/coupon-dialog-CyPFKgwn.js | 1 - ...oupons-BX1h_qVI.js => coupons-B3KVevR-.js} | 2 +- ...oard-efoygTEr.js => dashboard-B-GY2jng.js} | 2 +- .../admin/assets/debounce-BWBxuUbE.js | 1 + .../admin/assets/debounce-CkazOn_9.js | 1 - .../admin/assets/dynamic-stats-ByZg4VmD.js | 1 + .../admin/assets/dynamic-stats-Cz6aWDJ4.js | 1 - ...ue_type_script_setup_true_lang-CjWh3ewK.js | 1 - ...ue_type_script_setup_true_lang-Cu6h40HW.js | 1 + .../admin/assets/el-alert-BBo7e8Ze.js | 1 - .../admin/assets/el-alert-PelqwNJs.js | 1 + ...der-CCAdTkUu.js => el-divider--3NMYJzN.js} | 2 +- .../assets/el-dropdown-item-5vxq5ITH.js.gz | Bin 5598 -> 0 bytes ...xq5ITH.js => el-dropdown-item-DcxLwUbk.js} | 2 +- .../assets/el-dropdown-item-DcxLwUbk.js.gz | Bin 0 -> 5593 bytes .../admin/assets/el-pagination-CkwA8__y.js.gz | Bin 4105 -> 0 bytes ...-CkwA8__y.js => el-pagination-CqMqz367.js} | 2 +- .../admin/assets/el-pagination-CqMqz367.js.gz | Bin 0 -> 4106 bytes ...ver-B4M9MUPY.js => el-popover-F7amLv8q.js} | 2 +- ...ss-DcK0UPpw.js => el-progress-Bnk5TVLR.js} | 2 +- ...l-step-DZviJjCE.js => el-step-eKA640ze.js} | 2 +- ...WU1JSNJ.js => el-table-column-CioBjwcM.js} | 4 +- .../assets/el-table-column-CioBjwcM.js.gz | Bin 0 -> 24920 bytes .../assets/el-table-column-DWU1JSNJ.js.gz | Bin 24921 -> 0 bytes .../admin/assets/el-upload-CrHSYrsJ.js.gz | Bin 4964 -> 0 bytes ...load-CrHSYrsJ.js => el-upload-D-fp67h1.js} | 2 +- .../admin/assets/el-upload-D-fp67h1.js.gz | Bin 0 -> 4966 bytes ...Fdy.js => grant-reward-dialog-BGrUrjgq.js} | 2 +- .../{guild-D8g6fzam.js => guild-LXpMSfGK.js} | 2 +- ...h-B_ELFUE3.js => guild-search-B0AVYsIX.js} | 2 +- .../admin/assets/index--eaLyHlv.js.gz | Bin 7468 -> 0 bytes .../{index-BRaTjzgN.js => index-0iyKkqJT.js} | 6 +- .../admin/assets/index-0iyKkqJT.js.gz | Bin 0 -> 26731 bytes .../admin/assets/index-14nZLIa4.js.gz | Bin 4284 -> 0 bytes .../resources/admin/assets/index-2DiCqjwl.js | 1 - .../resources/admin/assets/index-2lSN6hkg.js | 4 + .../admin/assets/index-2lSN6hkg.js.gz | Bin 0 -> 113169 bytes .../{index-DuC2vu0m.js => index-2mJ3Csno.js} | 2 +- .../{index-qSQlj0RO.js => index-6-f3xsy2.js} | 2 +- .../admin/assets/index-6-f3xsy2.js.gz | Bin 0 -> 4441 bytes .../resources/admin/assets/index-8eO7cJP8.js | 1 - .../resources/admin/assets/index-B39kdrN1.js | 1 - .../admin/assets/index-B99ckkYy.js.gz | Bin 13718 -> 0 bytes .../resources/admin/assets/index-B9K9kfRp.js | 1 + .../resources/admin/assets/index-BAxJRwfZ.js | 1 - .../{index-Bg1OD8Bs.js => index-BDxmiZp6.js} | 2 +- .../admin/assets/index-BDxmiZp6.js.gz | Bin 0 -> 9705 bytes .../{index-BkQRY49V.js => index-BEyq4Ilv.js} | 2 +- .../admin/assets/index-BEyq4Ilv.js.gz | Bin 0 -> 9328 bytes .../resources/admin/assets/index-BG7ouMoR.js | 1 - .../admin/assets/index-BG7ouMoR.js.gz | Bin 4992 -> 0 bytes .../{index-DuJg5BLX.js => index-BGJrg12N.js} | 2 +- .../admin/assets/index-BGJrg12N.js.gz | Bin 0 -> 4079 bytes .../{index-Bi8tRqjc.js => index-BJHoISGQ.js} | 2 +- .../resources/admin/assets/index-BM6WKX9s.js | 1 - .../{index-D2YpA_om.js => index-BQ-mpgoy.js} | 2 +- .../resources/admin/assets/index-BRJHxNKW.js | 1 - .../admin/assets/index-BRaTjzgN.js.gz | Bin 26732 -> 0 bytes .../resources/admin/assets/index-BUJvLmJH.js | 1 - .../resources/admin/assets/index-BV-TixeS.js | 1 - .../resources/admin/assets/index-BVmWje4g.js | 1 - .../admin/assets/index-BVmWje4g.js.gz | Bin 5193 -> 0 bytes .../{index-nZra831X.js => index-BW22oPP6.js} | 2 +- .../resources/admin/assets/index-BXGdDPsD.js | 1 - .../{index-DKaQAdbW.js => index-BZS0BBj4.js} | 2 +- .../resources/admin/assets/index-BZZ0zvaS.js | 1 + .../resources/admin/assets/index-BcHOyVRF.js | 1 + .../admin/assets/index-BcHOyVRF.js.gz | Bin 0 -> 4832 bytes .../{index-DZdOSEpz.js => index-BcTwaKy5.js} | 2 +- .../{index-y_oew012.js => index-BcimyWJU.js} | 2 +- .../{index-BwFl4-YZ.js => index-BeKnQIs2.js} | 2 +- .../resources/admin/assets/index-Bf3rd-cc.js | 1 - .../admin/assets/index-Bg1OD8Bs.js.gz | Bin 9710 -> 0 bytes .../{index-DyK_irbZ.js => index-BhoBG2Is.js} | 2 +- .../{index-B4jq9Ja2.js => index-BiRLqYIK.js} | 2 +- .../{index-sqhzrW2O.js => index-BierC7rd.js} | 2 +- .../admin/assets/index-BkQRY49V.js.gz | Bin 9328 -> 0 bytes .../resources/admin/assets/index-BlgPu7T-.js | 1 + .../{index-DlCL-Hkf.js => index-BmNFsZMI.js} | 2 +- .../{index-CaM1nrgM.js => index-BpO8hpa7.js} | 2 +- .../{index-CCBa3FXn.js => index-BrSRceHS.js} | 2 +- .../resources/admin/assets/index-BtaKOTN0.js | 1 + .../resources/admin/assets/index-BuRHjZzC.js | 1 + .../resources/admin/assets/index-Bv2Kv2tu.js | 1 - .../resources/admin/assets/index-By9Yefvq.js | 1 - .../{index-D-BXifOM.js => index-ByKu-xq9.js} | 2 +- .../{index-BAsecYV9.js => index-ByZGqFa8.js} | 2 +- .../{index-CT9nVfjb.js => index-C33v3ixY.js} | 2 +- .../{index-CGm7Wp2k.js => index-C5-SmfXa.js} | 2 +- .../resources/admin/assets/index-C5Dz4WQo.js | 1 - .../{index-B99ckkYy.js => index-C6fEXl-e.js} | 2 +- .../admin/assets/index-C6fEXl-e.js.gz | Bin 0 -> 13725 bytes .../{index-CO9hDcFt.js => index-CCGS-X2U.js} | 4 +- .../admin/assets/index-CCGS-X2U.js.gz | Bin 0 -> 16575 bytes .../{index-CfCUoMYm.js => index-CDi70Xln.js} | 4 +- .../admin/assets/index-CDi70Xln.js.gz | Bin 0 -> 21919 bytes .../{index-CGAEnb_M.js => index-CGCYo4NE.js} | 2 +- .../admin/assets/index-CHZ9Ls9_.js.gz | Bin 9597 -> 0 bytes .../resources/admin/assets/index-CMy9KSx6.js | 1 + .../admin/assets/index-CMy9KSx6.js.gz | Bin 0 -> 5771 bytes .../{index-QGDr5H_c.js => index-CN5ZTm92.js} | 2 +- .../admin/assets/index-CO9hDcFt.js.gz | Bin 16574 -> 0 bytes .../admin/assets/index-CS6jHv1e.js.gz | Bin 26734 -> 0 bytes .../resources/admin/assets/index-CT-3Dhr0.js | 1 - .../resources/admin/assets/index-CU0EyVFa.js | 1 + .../{index-cuvv_mxy.js => index-CUiyhpsI.js} | 2 +- .../admin/assets/index-CUiyhpsI.js.gz | Bin 0 -> 9662 bytes .../resources/admin/assets/index-CUjTSF-G.js | 1 - .../admin/assets/index-CUjTSF-G.js.gz | Bin 4534 -> 0 bytes .../resources/admin/assets/index-CVOtshlJ.js | 1 + .../admin/assets/index-CVOtshlJ.js.gz | Bin 0 -> 4995 bytes .../resources/admin/assets/index-CVT5yXr_.js | 1 - .../{index-r0EsCkBw.js => index-CWBrZtwn.js} | 2 +- .../admin/assets/index-CWBrZtwn.js.gz | Bin 0 -> 6925 bytes .../resources/admin/assets/index-CZ0Lvzl5.js | 54 ++++ .../admin/assets/index-CZ0Lvzl5.js.gz | Bin 0 -> 451892 bytes .../resources/admin/assets/index-CeZ0RA1b.js | 1 - .../resources/admin/assets/index-Ceepcot3.js | 1 - .../admin/assets/index-CfCUoMYm.js.gz | Bin 21924 -> 0 bytes .../resources/admin/assets/index-CfCur9wC.js | 1 + .../resources/admin/assets/index-CfGIn9fk.js | 1 + .../{index-DeCbFeva.js => index-CgoUqbdT.js} | 2 +- .../resources/admin/assets/index-CjovhUt6.js | 1 + .../resources/admin/assets/index-CqowZTsR.css | 1 + .../{index-ChN4mcal.js => index-Crfa0tbz.js} | 2 +- .../resources/admin/assets/index-CrmfMdEP.js | 1 + .../resources/admin/assets/index-CuWUnlSZ.js | 1 - .../resources/admin/assets/index-CyCgOSE_.js | 1 + .../{index-BIIDnOvF.js => index-D11sKPUb.js} | 2 +- .../resources/admin/assets/index-D4Dcg2yM.js | 1 + .../{index-Bh7aAHNM.js => index-D9lGuZWz.js} | 2 +- .../resources/admin/assets/index-DBHnxsy6.js | 54 ---- .../admin/assets/index-DBHnxsy6.js.gz | Bin 451313 -> 0 bytes .../admin/assets/index-DC0TSDX9.css.gz | Bin 20761 -> 0 bytes .../resources/admin/assets/index-DCkemKV7.css | 1 + .../{index-DZO7eApA.js => index-DH_vX4BK.js} | 2 +- .../{index-C_e2vaW2.js => index-DJslzamN.js} | 2 +- .../resources/admin/assets/index-DO1zYXDS.js | 1 + .../admin/assets/index-DO1zYXDS.js.gz | Bin 0 -> 4296 bytes .../{index-CtUPMuT5.js => index-DO8sDDAh.js} | 2 +- .../resources/admin/assets/index-DOBGotGG.js | 1 + .../admin/assets/index-DOBGotGG.js.gz | Bin 0 -> 4543 bytes .../resources/admin/assets/index-DPc7H4FI.js | 1 - .../{index-B5f0OwDI.js => index-DR7AB6IM.js} | 2 +- .../resources/admin/assets/index-DRgpM3bD.css | 1 + .../resources/admin/assets/index-DU6QdUDu.js | 1 + .../admin/assets/index-DU6QdUDu.js.gz | Bin 0 -> 7617 bytes .../{index-hZY7MWyN.js => index-DYFv-jPN.js} | 4 +- .../admin/assets/index-DYFv-jPN.js.gz | Bin 0 -> 4397 bytes .../resources/admin/assets/index-D_rsMSDL.js | 1 + .../{index-DvlZWusB.js => index-DaNWXg3-.js} | 2 +- .../{index-14nZLIa4.js => index-DfojFKfd.js} | 2 +- .../admin/assets/index-DfojFKfd.js.gz | Bin 0 -> 4282 bytes .../{index-zIv9y_XZ.js => index-DhBIlDpg.js} | 2 +- .../admin/assets/index-DhBIlDpg.js.gz | Bin 0 -> 10139 bytes .../resources/admin/assets/index-DjWpmqb7.js | 1 - .../admin/assets/index-DjWpmqb7.js.gz | Bin 4287 -> 0 bytes ...{index-DeoolEue.css => index-Dn-JN8DA.css} | 2 +- .../resources/admin/assets/index-DoEf72eA.js | 1 - .../resources/admin/assets/index-DoHEahHh.js | 1 - .../resources/admin/assets/index-Dr092ZhG.css | 1 + ...{index-DC0TSDX9.css => index-DsAg9WZP.css} | 2 +- .../admin/assets/index-DsAg9WZP.css.gz | Bin 0 -> 20735 bytes .../resources/admin/assets/index-DuAYykmG.js | 1 + .../admin/assets/index-DuJg5BLX.js.gz | Bin 4080 -> 0 bytes .../resources/admin/assets/index-DvNrrFBd.js | 1 - .../resources/admin/assets/index-E7mvnH88.js | 1 + .../resources/admin/assets/index-GiwAPXUL.js | 1 + .../{index-BV4E8Lz1.js => index-HkMdJUrV.js} | 2 +- .../{index--eaLyHlv.js => index-JwZOOP-N.js} | 2 +- .../admin/assets/index-JwZOOP-N.js.gz | Bin 0 -> 7472 bytes .../{index-MOpuiu9Z.js => index-K2zzami3.js} | 2 +- .../{index-DfWQjCuY.js => index-MV2EqMi7.js} | 2 +- .../resources/admin/assets/index-MrSfJpXQ.js | 1 + .../{index-Yn4qS2ew.js => index-P5E0zw8h.js} | 2 +- .../resources/admin/assets/index-QZ-VxAjY.js | 1 - .../{index-CS6jHv1e.js => index-R-jt7LR_.js} | 2 +- .../admin/assets/index-R-jt7LR_.js.gz | Bin 0 -> 26730 bytes .../{index-BC2pRl3S.js => index-ZgXgJNHL.js} | 2 +- .../resources/admin/assets/index-_GlmnzWM.js | 1 + .../resources/admin/assets/index-_aFOeltJ.js | 1 + .../admin/assets/index-cuvv_mxy.js.gz | Bin 9662 -> 0 bytes .../admin/assets/index-hZY7MWyN.js.gz | Bin 4395 -> 0 bytes .../resources/admin/assets/index-llGlcBvz.css | 1 - .../resources/admin/assets/index-nuyWwy-z.js | 1 + .../{index-CxsIPycV.js => index-o8DV6baU.js} | 2 +- .../resources/admin/assets/index-oOhs76pg.js | 1 + .../{index-CHZ9Ls9_.js => index-pVAXVlCV.js} | 2 +- .../admin/assets/index-pVAXVlCV.js.gz | Bin 0 -> 9599 bytes .../admin/assets/index-qSQlj0RO.js.gz | Bin 4438 -> 0 bytes .../admin/assets/index-r0EsCkBw.js.gz | Bin 6924 -> 0 bytes .../admin/assets/index-zIv9y_XZ.js.gz | Bin 10136 -> 0 bytes ...e_type_script_setup_true_lang-BKMZ6sUc.js} | 2 +- ...ue_type_script_setup_true_lang-BvPXNSi5.js | 1 + ...ue_type_script_setup_true_lang-C-eRvstH.js | 1 - ...type_script_setup_true_lang-DUyHB5jc.js.gz | Bin 7224 -> 0 bytes ...e_type_script_setup_true_lang-DkYjV9Ul.js} | 2 +- ...type_script_setup_true_lang-DkYjV9Ul.js.gz | Bin 0 -> 7226 bytes .../assets/isArrayLikeObject-BJDhNHaT.js | 1 - .../assets/isArrayLikeObject-CQAE3P4Q.js | 1 + ...K_Js5q.js => item-card-dialog-BLfIvYMZ.js} | 2 +- ...ards-BHUYJ5ez.js => itemCards-QB81Evem.js} | 2 +- .../{md5-BDd5Ze2N.js => md5-FaX5g_uo.js} | 2 +- .../admin/assets/menu-dialog-BPsiSVWz.js | 1 - .../admin/assets/menu-dialog-enpyfDv5.js | 1 + ...type_script_setup_true_lang-KTN5b8tT.js.gz | Bin 4436 -> 0 bytes ...e_type_script_setup_true_lang-e7mKRWRQ.js} | 2 +- ...type_script_setup_true_lang-e7mKRWRQ.js.gz | Bin 0 -> 4440 bytes .../admin/assets/message-box-CHGPmvsv.js.gz | Bin 4286 -> 0 bytes ...ox-CHGPmvsv.js => message-box-mfsaE52U.js} | 2 +- .../admin/assets/message-box-mfsaE52U.js.gz | Bin 0 -> 4290 bytes .../admin/assets/new-user-C0sObr0V.js | 1 + .../admin/assets/new-user-c5Y64LbE.js | 1 - ...ons-CsHdqaV1.js => operations-BDlSOaTf.js} | 2 +- ...l-DkeAQ_Xa.js => order-funnel-lFnxfUnB.js} | 2 +- .../assets/player-detail-drawer-gKf3Owlo.js | 1 - .../player-detail-drawer-gKf3Owlo.js.gz | Bin 6644 -> 0 bytes .../assets/player-detail-drawer-jpdh6q1M.js | 1 + .../player-detail-drawer-jpdh6q1M.js.gz | Bin 0 -> 6640 bytes .../admin/assets/player-search-CV64a3i5.js | 1 + .../admin/assets/player-search-D2VXDU2D.js | 1 - ...roduct-njVTTbuM.js => product-FfcA_cLd.js} | 2 +- .../admin/assets/product-search-CA-a7G4m.js | 1 + .../admin/assets/product-search-DpH35CXa.js | 1 - build/resources/admin/assets/raf-C-iJO6ki.js | 1 + build/resources/admin/assets/raf-CXqF1NB1.js | 1 - .../admin/assets/role-edit-dialog-Cm1NPJkR.js | 1 + .../admin/assets/role-edit-dialog-HX4f6dK1.js | 1 - ...e_type_script_setup_true_lang-BJbX2A59.js} | 2 +- .../assets/role-permission-dialog-DreZDJMZ.js | 1 - .../assets/role-permission-dialog-ewu9RAvj.js | 1 + ...e_type_script_setup_true_lang-Cr_LJRAQ.js} | 2 +- .../admin/assets/role-search-BZWT66Sc.js | 1 - .../admin/assets/role-search-CVOVdRto.js | 1 + ...e_type_script_setup_true_lang-BerzXeNX.js} | 2 +- .../admin/assets/sales-overview-CChODLAb.js | 1 + .../admin/assets/sales-overview-RF4W6ern.js | 1 - ...e_type_script_setup_true_lang-DGkuAMN0.js} | 2 +- ...{scroll-Cceui3yC.js => scroll-bFiZhqzI.js} | 2 +- ...{slider-CFh6w1Nh.js => slider-C0BHCDqQ.js} | 2 +- .../admin/assets/slider-C0BHCDqQ.js.gz | Bin 0 -> 5292 bytes .../admin/assets/slider-CFh6w1Nh.js.gz | Bin 5293 -> 0 bytes ...{titles-Bs2uXvDP.js => titles-BnzYQ5A4.js} | 2 +- .../admin/assets/todo-list-B1wap53h.js | 1 - .../admin/assets/todo-list-kXDrs5Fd.js | 1 + .../admin/assets/tree-select-DNitEO0k.js.gz | Bin 11220 -> 0 bytes ...ct-DNitEO0k.js => tree-select-_Xz6f9er.js} | 2 +- .../admin/assets/tree-select-_Xz6f9er.js.gz | Bin 0 -> 11220 bytes .../admin/assets/use-dialog-BDM4AbF-.js | 1 + .../admin/assets/use-dialog-yhiLZsn9.js | 1 - ...R.js => use-form-common-props-U6f9D53_.js} | 2 +- ...-ByasanrX.js => use-form-item-BsnpemL_.js} | 2 +- ...Chart-MKcUOr4d.js => useChart-CqKOtsKK.js} | 2 +- .../admin/assets/useChart-CqKOtsKK.js.gz | Bin 0 -> 246242 bytes .../admin/assets/useChart-MKcUOr4d.js.gz | Bin 246241 -> 0 bytes ...r-DUbpCIQq.js => useHeaderBar-B-Loh-tq.js} | 2 +- ...Table-MDpDGlZA.js => useTable-BIumkxFC.js} | 2 +- .../admin/assets/useTable-BIumkxFC.js.gz | Bin 0 -> 6917 bytes .../admin/assets/useTable-MDpDGlZA.js.gz | Bin 6915 -> 0 bytes ...3dG-7w1.js => useTableColumns-BjNnGKmd.js} | 2 +- .../admin/assets/user-dialog-BpyqYfU8.js | 1 - .../admin/assets/user-dialog-FK0h2O40.js | 1 + ...type_script_setup_true_lang-DU6JD3Hu.js.gz | Bin 11619 -> 0 bytes ...e_type_script_setup_true_lang-VAEhSDaq.js} | 2 +- ...type_script_setup_true_lang-VAEhSDaq.js.gz | Bin 0 -> 11618 bytes .../admin/assets/user-search-CGsgEu3y.js | 1 - .../admin/assets/user-search-PL6UsLdr.js | 1 + ...e_type_script_setup_true_lang-sPKa3nOb.js} | 2 +- .../admin/assets/validator-BHWhHngj.js | 1 + .../admin/assets/validator-D_3Fyw1a.js | 1 - .../resources/admin/assets/vnode-C4RX5enz.js | 1 + .../resources/admin/assets/vnode-CqxKQBn0.js | 1 - build/resources/admin/index.html | 4 +- docs/删除操作交互流程整改计划.md | 290 ++++++++++++++++++ .../ALIGNMENT_奖励管理字段拆分.md | 46 +++ .../CONSENSUS_奖励管理字段拆分.md | 69 +++++ .../DESIGN_奖励管理字段拆分.md | 153 +++++++++ .../奖励管理字段拆分/TASK_奖励管理字段拆分.md | 158 ++++++++++ docs/开发规范.md | 26 ++ internal/api/activity/rewards_app.go | 120 +++++--- internal/api/admin/admin.go | 66 ++-- internal/api/admin/auth_refresh.go | 50 +++ internal/api/admin/issues_admin.go | 33 +- internal/api/admin/item_cards_admin.go | 28 +- internal/api/admin/product_batch.go | 62 ++++ internal/api/admin/system_coupons.go | 28 +- internal/api/admin/system_menu.go | 53 ++-- internal/api/admin/system_recycle.go | 63 ++++ internal/api/admin/system_role.go | 57 ++-- internal/repository/mysql/plugin.go | 69 ++++- internal/repository/mysql/testrepo_sqlite.go | 23 ++ internal/router/interceptor/admin_rbac.go | 58 ++++ .../router/interceptor/admin_rbac_test.go | 154 ++++++++++ internal/router/interceptor/interceptor.go | 7 +- internal/router/router.go | 149 ++++----- internal/service/activity/activity_delete.go | 118 ++++++- internal/service/activity/issue_delete.go | 54 +++- internal/service/banner/banner.go | 3 +- internal/service/guild/guild_delete.go | 9 +- internal/service/product/product.go | 48 ++- internal/service/recycle/recycle_service.go | 188 ++++++++++++ internal/service/user/batch_user.go | 116 ++++++- migrations/2025-11-18_soft_delete.sql | 17 + 376 files changed, 2925 insertions(+), 517 deletions(-) create mode 100644 .trae/.ignore create mode 100644 .trae/documents/修复活动创建向导步骤逻辑并统一全局风格.md create mode 100644 .trae/documents/删除级联逻辑优化方案.md create mode 100644 .trae/documents/商品列表批量操作与活动创建优化及仪表盘修复方案.md create mode 100644 .trae/documents/实现回收站与软删除机制.md create mode 100644 .trae/documents/彻底修复批量抽奖误弹与统一“新建活动”UI为 Element Plus 官方风格.md create mode 100644 .trae/documents/活动创建“下一步”无响应的排查与改造方案.md create mode 100644 .trae/documents/活动创建流程重构方案.md create mode 100644 .trae/documents/活动期数奖品数量、登录态与奖励管理价格增强改造方案.md create mode 100644 .trae/documents/统一活动创建页面UI样式为一致的主题规范.md create mode 100644 .trae/documents/补齐删除级联遗漏清单与改造计划.md create mode 100644 .trae/documents/重构活动创建向导样式为 Element Plus 官方风格.md rename build/resources/admin/assets/{ArtException.vue_vue_type_script_setup_true_lang-Dpzx4yaO.js => ArtException.vue_vue_type_script_setup_true_lang-M6sE6E-D.js} (79%) rename build/resources/admin/assets/{ArtResultPage.vue_vue_type_script_setup_true_lang-D9Aa0T_L.js => ArtResultPage.vue_vue_type_script_setup_true_lang-BRyONWq_.js} (91%) rename build/resources/admin/assets/{EffectEditDialog-DVCDk-bJ.js => EffectEditDialog-CKoMjh_c.js} (50%) create mode 100644 build/resources/admin/assets/EffectEditDialog-CKoMjh_c.js.gz delete mode 100644 build/resources/admin/assets/EffectEditDialog-DVCDk-bJ.js.gz create mode 100644 build/resources/admin/assets/EffectManagerDialog-B8hoH5eF.js delete mode 100644 build/resources/admin/assets/EffectManagerDialog-BOpJq-as.js rename build/resources/admin/assets/{Iframe-Bfhx1JL3.js => Iframe-Bt6u-iQX.js} (87%) rename build/resources/admin/assets/{LoginLeftView-DqS6laCq.js => LoginLeftView-DQuNwejA.js} (91%) rename build/resources/admin/assets/{RuleConfigDialog-XFo2cxQg.js => RuleConfigDialog-DC9G2ww3.js} (58%) create mode 100644 build/resources/admin/assets/RuleConfigDialog-DC9G2ww3.js.gz delete mode 100644 build/resources/admin/assets/RuleConfigDialog-XFo2cxQg.js.gz create mode 100644 build/resources/admin/assets/TitleEditDialog-BoomnsCf.js delete mode 100644 build/resources/admin/assets/TitleEditDialog-Cw8i3HNt.js rename build/resources/admin/assets/{UserAssignmentDialog-CgQlEmJK.js => UserAssignmentDialog-C3ancW_U.js} (67%) create mode 100644 build/resources/admin/assets/UserAssignmentDialog-C3ancW_U.js.gz delete mode 100644 build/resources/admin/assets/UserAssignmentDialog-CgQlEmJK.js.gz delete mode 100644 build/resources/admin/assets/_baseIteratee-DRiXPtOg.js create mode 100644 build/resources/admin/assets/_baseIteratee-iUx42eT-.js delete mode 100644 build/resources/admin/assets/_initCloneObject-7oWMW-Ot.js create mode 100644 build/resources/admin/assets/_initCloneObject-DfOnBHdn.js delete mode 100644 build/resources/admin/assets/active-user-CdPYh29t.js create mode 100644 build/resources/admin/assets/active-user-CqefX-qJ.js rename build/resources/admin/assets/{active-user.vue_vue_type_script_setup_true_lang-19Sadsu3.js => active-user.vue_vue_type_script_setup_true_lang-C_Ah812_.js} (86%) rename build/resources/admin/assets/{activity-0udS8Goz.js => activity-B4-0dphm.js} (93%) rename build/resources/admin/assets/{activity-lottery-DMztZZvI.js => activity-lottery-D_9KIAZu.js} (75%) delete mode 100644 build/resources/admin/assets/activity-prize-analysis-BbWF0RUj.js create mode 100644 build/resources/admin/assets/activity-prize-analysis-DLw5P596.js rename build/resources/admin/assets/{activity-prize-analysis.vue_vue_type_script_setup_true_lang-bThIk24K.js => activity-prize-analysis.vue_vue_type_script_setup_true_lang-Be3zdQ_a.js} (79%) delete mode 100644 build/resources/admin/assets/activity-search-61nDqo2V.js create mode 100644 build/resources/admin/assets/activity-search-p-0MbIUg.js delete mode 100644 build/resources/admin/assets/add-coupon-dialog-DW1AyPdi.js create mode 100644 build/resources/admin/assets/add-coupon-dialog-DhXVbOMW.js rename build/resources/admin/assets/{add-coupon-dialog.vue_vue_type_script_setup_true_lang-C0dOKfzY.js => add-coupon-dialog.vue_vue_type_script_setup_true_lang-CYcx2nJS.js} (53%) delete mode 100644 build/resources/admin/assets/add-item-card-dialog-C6lD4OlI.js create mode 100644 build/resources/admin/assets/add-item-card-dialog-CZ8UomRh.js create mode 100644 build/resources/admin/assets/add-item-card-dialog.vue_vue_type_script_setup_true_lang-S5Y5BQPs.js delete mode 100644 build/resources/admin/assets/add-item-card-dialog.vue_vue_type_script_setup_true_lang-r-95eO1C.js delete mode 100644 build/resources/admin/assets/add-points-dialog-BOqGK02x.js create mode 100644 build/resources/admin/assets/add-points-dialog-CocGMOL9.js delete mode 100644 build/resources/admin/assets/add-points-dialog.vue_vue_type_script_setup_true_lang-B99Uz4mM.js create mode 100644 build/resources/admin/assets/add-points-dialog.vue_vue_type_script_setup_true_lang-CDG3qGoH.js rename build/resources/admin/assets/{adminActivities-C7lwI2HP.js => adminActivities-BFodRb8S.js} (96%) create mode 100644 build/resources/admin/assets/assign-title-dialog-BBBwnMYv.js delete mode 100644 build/resources/admin/assets/assign-title-dialog-JfcY-TAw.js rename build/resources/admin/assets/{assign-title-dialog.vue_vue_type_script_setup_true_lang-ChRt4Uun.js => assign-title-dialog.vue_vue_type_script_setup_true_lang-lCpMfr54.js} (65%) delete mode 100644 build/resources/admin/assets/batch-draw-dialog-KCzFry5O.js create mode 100644 build/resources/admin/assets/batch-draw-dialog-wyt1sOed.js create mode 100644 build/resources/admin/assets/batch-draw-dialog-wyt1sOed.js.gz delete mode 100644 build/resources/admin/assets/batch-draw-dialog.vue_vue_type_script_setup_true_lang-D6SWKrJK.js delete mode 100644 build/resources/admin/assets/batch-draw-dialog.vue_vue_type_script_setup_true_lang-D6SWKrJK.js.gz delete mode 100644 build/resources/admin/assets/card-list-Dkk_zimK.js create mode 100644 build/resources/admin/assets/card-list-DxDGPL3Q.js rename build/resources/admin/assets/{card-list.vue_vue_type_script_setup_true_lang-CibUUuFD.js => card-list.vue_vue_type_script_setup_true_lang-qY_uPixj.js} (60%) create mode 100644 build/resources/admin/assets/category-search-CRg9E3ZY.js delete mode 100644 build/resources/admin/assets/category-search-CzNFlBJ5.js create mode 100644 build/resources/admin/assets/cloneDeep-tG3tBIiW.js delete mode 100644 build/resources/admin/assets/cloneDeep-zs6l5dp3.js create mode 100644 build/resources/admin/assets/coupon-dialog-CGVfD7e9.js delete mode 100644 build/resources/admin/assets/coupon-dialog-CyPFKgwn.js rename build/resources/admin/assets/{coupons-BX1h_qVI.js => coupons-B3KVevR-.js} (77%) rename build/resources/admin/assets/{dashboard-efoygTEr.js => dashboard-B-GY2jng.js} (88%) create mode 100644 build/resources/admin/assets/debounce-BWBxuUbE.js delete mode 100644 build/resources/admin/assets/debounce-CkazOn_9.js create mode 100644 build/resources/admin/assets/dynamic-stats-ByZg4VmD.js delete mode 100644 build/resources/admin/assets/dynamic-stats-Cz6aWDJ4.js delete mode 100644 build/resources/admin/assets/dynamic-stats.vue_vue_type_script_setup_true_lang-CjWh3ewK.js create mode 100644 build/resources/admin/assets/dynamic-stats.vue_vue_type_script_setup_true_lang-Cu6h40HW.js delete mode 100644 build/resources/admin/assets/el-alert-BBo7e8Ze.js create mode 100644 build/resources/admin/assets/el-alert-PelqwNJs.js rename build/resources/admin/assets/{el-divider-CCAdTkUu.js => el-divider--3NMYJzN.js} (94%) delete mode 100644 build/resources/admin/assets/el-dropdown-item-5vxq5ITH.js.gz rename build/resources/admin/assets/{el-dropdown-item-5vxq5ITH.js => el-dropdown-item-DcxLwUbk.js} (81%) create mode 100644 build/resources/admin/assets/el-dropdown-item-DcxLwUbk.js.gz delete mode 100644 build/resources/admin/assets/el-pagination-CkwA8__y.js.gz rename build/resources/admin/assets/{el-pagination-CkwA8__y.js => el-pagination-CqMqz367.js} (75%) create mode 100644 build/resources/admin/assets/el-pagination-CqMqz367.js.gz rename build/resources/admin/assets/{el-popover-B4M9MUPY.js => el-popover-F7amLv8q.js} (80%) rename build/resources/admin/assets/{el-progress-DcK0UPpw.js => el-progress-Bnk5TVLR.js} (83%) rename build/resources/admin/assets/{el-step-DZviJjCE.js => el-step-eKA640ze.js} (69%) rename build/resources/admin/assets/{el-table-column-DWU1JSNJ.js => el-table-column-CioBjwcM.js} (88%) create mode 100644 build/resources/admin/assets/el-table-column-CioBjwcM.js.gz delete mode 100644 build/resources/admin/assets/el-table-column-DWU1JSNJ.js.gz delete mode 100644 build/resources/admin/assets/el-upload-CrHSYrsJ.js.gz rename build/resources/admin/assets/{el-upload-CrHSYrsJ.js => el-upload-D-fp67h1.js} (76%) create mode 100644 build/resources/admin/assets/el-upload-D-fp67h1.js.gz rename build/resources/admin/assets/{grant-reward-dialog-LD26CFdy.js => grant-reward-dialog-BGrUrjgq.js} (51%) rename build/resources/admin/assets/{guild-D8g6fzam.js => guild-LXpMSfGK.js} (92%) rename build/resources/admin/assets/{guild-search-B_ELFUE3.js => guild-search-B0AVYsIX.js} (65%) delete mode 100644 build/resources/admin/assets/index--eaLyHlv.js.gz rename build/resources/admin/assets/{index-BRaTjzgN.js => index-0iyKkqJT.js} (91%) create mode 100644 build/resources/admin/assets/index-0iyKkqJT.js.gz delete mode 100644 build/resources/admin/assets/index-14nZLIa4.js.gz delete mode 100644 build/resources/admin/assets/index-2DiCqjwl.js create mode 100644 build/resources/admin/assets/index-2lSN6hkg.js create mode 100644 build/resources/admin/assets/index-2lSN6hkg.js.gz rename build/resources/admin/assets/{index-DuC2vu0m.js => index-2mJ3Csno.js} (74%) rename build/resources/admin/assets/{index-qSQlj0RO.js => index-6-f3xsy2.js} (61%) create mode 100644 build/resources/admin/assets/index-6-f3xsy2.js.gz delete mode 100644 build/resources/admin/assets/index-8eO7cJP8.js delete mode 100644 build/resources/admin/assets/index-B39kdrN1.js delete mode 100644 build/resources/admin/assets/index-B99ckkYy.js.gz create mode 100644 build/resources/admin/assets/index-B9K9kfRp.js delete mode 100644 build/resources/admin/assets/index-BAxJRwfZ.js rename build/resources/admin/assets/{index-Bg1OD8Bs.js => index-BDxmiZp6.js} (76%) create mode 100644 build/resources/admin/assets/index-BDxmiZp6.js.gz rename build/resources/admin/assets/{index-BkQRY49V.js => index-BEyq4Ilv.js} (91%) create mode 100644 build/resources/admin/assets/index-BEyq4Ilv.js.gz delete mode 100644 build/resources/admin/assets/index-BG7ouMoR.js delete mode 100644 build/resources/admin/assets/index-BG7ouMoR.js.gz rename build/resources/admin/assets/{index-DuJg5BLX.js => index-BGJrg12N.js} (74%) create mode 100644 build/resources/admin/assets/index-BGJrg12N.js.gz rename build/resources/admin/assets/{index-Bi8tRqjc.js => index-BJHoISGQ.js} (92%) delete mode 100644 build/resources/admin/assets/index-BM6WKX9s.js rename build/resources/admin/assets/{index-D2YpA_om.js => index-BQ-mpgoy.js} (74%) delete mode 100644 build/resources/admin/assets/index-BRJHxNKW.js delete mode 100644 build/resources/admin/assets/index-BRaTjzgN.js.gz delete mode 100644 build/resources/admin/assets/index-BUJvLmJH.js delete mode 100644 build/resources/admin/assets/index-BV-TixeS.js delete mode 100644 build/resources/admin/assets/index-BVmWje4g.js delete mode 100644 build/resources/admin/assets/index-BVmWje4g.js.gz rename build/resources/admin/assets/{index-nZra831X.js => index-BW22oPP6.js} (80%) delete mode 100644 build/resources/admin/assets/index-BXGdDPsD.js rename build/resources/admin/assets/{index-DKaQAdbW.js => index-BZS0BBj4.js} (84%) create mode 100644 build/resources/admin/assets/index-BZZ0zvaS.js create mode 100644 build/resources/admin/assets/index-BcHOyVRF.js create mode 100644 build/resources/admin/assets/index-BcHOyVRF.js.gz rename build/resources/admin/assets/{index-DZdOSEpz.js => index-BcTwaKy5.js} (52%) rename build/resources/admin/assets/{index-y_oew012.js => index-BcimyWJU.js} (85%) rename build/resources/admin/assets/{index-BwFl4-YZ.js => index-BeKnQIs2.js} (63%) delete mode 100644 build/resources/admin/assets/index-Bf3rd-cc.js delete mode 100644 build/resources/admin/assets/index-Bg1OD8Bs.js.gz rename build/resources/admin/assets/{index-DyK_irbZ.js => index-BhoBG2Is.js} (67%) rename build/resources/admin/assets/{index-B4jq9Ja2.js => index-BiRLqYIK.js} (63%) rename build/resources/admin/assets/{index-sqhzrW2O.js => index-BierC7rd.js} (81%) delete mode 100644 build/resources/admin/assets/index-BkQRY49V.js.gz create mode 100644 build/resources/admin/assets/index-BlgPu7T-.js rename build/resources/admin/assets/{index-DlCL-Hkf.js => index-BmNFsZMI.js} (98%) rename build/resources/admin/assets/{index-CaM1nrgM.js => index-BpO8hpa7.js} (52%) rename build/resources/admin/assets/{index-CCBa3FXn.js => index-BrSRceHS.js} (56%) create mode 100644 build/resources/admin/assets/index-BtaKOTN0.js create mode 100644 build/resources/admin/assets/index-BuRHjZzC.js delete mode 100644 build/resources/admin/assets/index-Bv2Kv2tu.js delete mode 100644 build/resources/admin/assets/index-By9Yefvq.js rename build/resources/admin/assets/{index-D-BXifOM.js => index-ByKu-xq9.js} (84%) rename build/resources/admin/assets/{index-BAsecYV9.js => index-ByZGqFa8.js} (65%) rename build/resources/admin/assets/{index-CT9nVfjb.js => index-C33v3ixY.js} (69%) rename build/resources/admin/assets/{index-CGm7Wp2k.js => index-C5-SmfXa.js} (87%) delete mode 100644 build/resources/admin/assets/index-C5Dz4WQo.js rename build/resources/admin/assets/{index-B99ckkYy.js => index-C6fEXl-e.js} (80%) create mode 100644 build/resources/admin/assets/index-C6fEXl-e.js.gz rename build/resources/admin/assets/{index-CO9hDcFt.js => index-CCGS-X2U.js} (76%) create mode 100644 build/resources/admin/assets/index-CCGS-X2U.js.gz rename build/resources/admin/assets/{index-CfCUoMYm.js => index-CDi70Xln.js} (90%) create mode 100644 build/resources/admin/assets/index-CDi70Xln.js.gz rename build/resources/admin/assets/{index-CGAEnb_M.js => index-CGCYo4NE.js} (63%) delete mode 100644 build/resources/admin/assets/index-CHZ9Ls9_.js.gz create mode 100644 build/resources/admin/assets/index-CMy9KSx6.js create mode 100644 build/resources/admin/assets/index-CMy9KSx6.js.gz rename build/resources/admin/assets/{index-QGDr5H_c.js => index-CN5ZTm92.js} (54%) delete mode 100644 build/resources/admin/assets/index-CO9hDcFt.js.gz delete mode 100644 build/resources/admin/assets/index-CS6jHv1e.js.gz delete mode 100644 build/resources/admin/assets/index-CT-3Dhr0.js create mode 100644 build/resources/admin/assets/index-CU0EyVFa.js rename build/resources/admin/assets/{index-cuvv_mxy.js => index-CUiyhpsI.js} (64%) create mode 100644 build/resources/admin/assets/index-CUiyhpsI.js.gz delete mode 100644 build/resources/admin/assets/index-CUjTSF-G.js delete mode 100644 build/resources/admin/assets/index-CUjTSF-G.js.gz create mode 100644 build/resources/admin/assets/index-CVOtshlJ.js create mode 100644 build/resources/admin/assets/index-CVOtshlJ.js.gz delete mode 100644 build/resources/admin/assets/index-CVT5yXr_.js rename build/resources/admin/assets/{index-r0EsCkBw.js => index-CWBrZtwn.js} (88%) create mode 100644 build/resources/admin/assets/index-CWBrZtwn.js.gz create mode 100644 build/resources/admin/assets/index-CZ0Lvzl5.js create mode 100644 build/resources/admin/assets/index-CZ0Lvzl5.js.gz delete mode 100644 build/resources/admin/assets/index-CeZ0RA1b.js delete mode 100644 build/resources/admin/assets/index-Ceepcot3.js delete mode 100644 build/resources/admin/assets/index-CfCUoMYm.js.gz create mode 100644 build/resources/admin/assets/index-CfCur9wC.js create mode 100644 build/resources/admin/assets/index-CfGIn9fk.js rename build/resources/admin/assets/{index-DeCbFeva.js => index-CgoUqbdT.js} (82%) create mode 100644 build/resources/admin/assets/index-CjovhUt6.js create mode 100644 build/resources/admin/assets/index-CqowZTsR.css rename build/resources/admin/assets/{index-ChN4mcal.js => index-Crfa0tbz.js} (56%) create mode 100644 build/resources/admin/assets/index-CrmfMdEP.js delete mode 100644 build/resources/admin/assets/index-CuWUnlSZ.js create mode 100644 build/resources/admin/assets/index-CyCgOSE_.js rename build/resources/admin/assets/{index-BIIDnOvF.js => index-D11sKPUb.js} (95%) create mode 100644 build/resources/admin/assets/index-D4Dcg2yM.js rename build/resources/admin/assets/{index-Bh7aAHNM.js => index-D9lGuZWz.js} (91%) delete mode 100644 build/resources/admin/assets/index-DBHnxsy6.js delete mode 100644 build/resources/admin/assets/index-DBHnxsy6.js.gz delete mode 100644 build/resources/admin/assets/index-DC0TSDX9.css.gz create mode 100644 build/resources/admin/assets/index-DCkemKV7.css rename build/resources/admin/assets/{index-DZO7eApA.js => index-DH_vX4BK.js} (86%) rename build/resources/admin/assets/{index-C_e2vaW2.js => index-DJslzamN.js} (74%) create mode 100644 build/resources/admin/assets/index-DO1zYXDS.js create mode 100644 build/resources/admin/assets/index-DO1zYXDS.js.gz rename build/resources/admin/assets/{index-CtUPMuT5.js => index-DO8sDDAh.js} (60%) create mode 100644 build/resources/admin/assets/index-DOBGotGG.js create mode 100644 build/resources/admin/assets/index-DOBGotGG.js.gz delete mode 100644 build/resources/admin/assets/index-DPc7H4FI.js rename build/resources/admin/assets/{index-B5f0OwDI.js => index-DR7AB6IM.js} (78%) create mode 100644 build/resources/admin/assets/index-DRgpM3bD.css create mode 100644 build/resources/admin/assets/index-DU6QdUDu.js create mode 100644 build/resources/admin/assets/index-DU6QdUDu.js.gz rename build/resources/admin/assets/{index-hZY7MWyN.js => index-DYFv-jPN.js} (51%) create mode 100644 build/resources/admin/assets/index-DYFv-jPN.js.gz create mode 100644 build/resources/admin/assets/index-D_rsMSDL.js rename build/resources/admin/assets/{index-DvlZWusB.js => index-DaNWXg3-.js} (95%) rename build/resources/admin/assets/{index-14nZLIa4.js => index-DfojFKfd.js} (89%) create mode 100644 build/resources/admin/assets/index-DfojFKfd.js.gz rename build/resources/admin/assets/{index-zIv9y_XZ.js => index-DhBIlDpg.js} (97%) create mode 100644 build/resources/admin/assets/index-DhBIlDpg.js.gz delete mode 100644 build/resources/admin/assets/index-DjWpmqb7.js delete mode 100644 build/resources/admin/assets/index-DjWpmqb7.js.gz rename build/resources/admin/assets/{index-DeoolEue.css => index-Dn-JN8DA.css} (59%) delete mode 100644 build/resources/admin/assets/index-DoEf72eA.js delete mode 100644 build/resources/admin/assets/index-DoHEahHh.js create mode 100644 build/resources/admin/assets/index-Dr092ZhG.css rename build/resources/admin/assets/{index-DC0TSDX9.css => index-DsAg9WZP.css} (87%) create mode 100644 build/resources/admin/assets/index-DsAg9WZP.css.gz create mode 100644 build/resources/admin/assets/index-DuAYykmG.js delete mode 100644 build/resources/admin/assets/index-DuJg5BLX.js.gz delete mode 100644 build/resources/admin/assets/index-DvNrrFBd.js create mode 100644 build/resources/admin/assets/index-E7mvnH88.js create mode 100644 build/resources/admin/assets/index-GiwAPXUL.js rename build/resources/admin/assets/{index-BV4E8Lz1.js => index-HkMdJUrV.js} (62%) rename build/resources/admin/assets/{index--eaLyHlv.js => index-JwZOOP-N.js} (91%) create mode 100644 build/resources/admin/assets/index-JwZOOP-N.js.gz rename build/resources/admin/assets/{index-MOpuiu9Z.js => index-K2zzami3.js} (91%) rename build/resources/admin/assets/{index-DfWQjCuY.js => index-MV2EqMi7.js} (62%) create mode 100644 build/resources/admin/assets/index-MrSfJpXQ.js rename build/resources/admin/assets/{index-Yn4qS2ew.js => index-P5E0zw8h.js} (58%) delete mode 100644 build/resources/admin/assets/index-QZ-VxAjY.js rename build/resources/admin/assets/{index-CS6jHv1e.js => index-R-jt7LR_.js} (83%) create mode 100644 build/resources/admin/assets/index-R-jt7LR_.js.gz rename build/resources/admin/assets/{index-BC2pRl3S.js => index-ZgXgJNHL.js} (93%) create mode 100644 build/resources/admin/assets/index-_GlmnzWM.js create mode 100644 build/resources/admin/assets/index-_aFOeltJ.js delete mode 100644 build/resources/admin/assets/index-cuvv_mxy.js.gz delete mode 100644 build/resources/admin/assets/index-hZY7MWyN.js.gz delete mode 100644 build/resources/admin/assets/index-llGlcBvz.css create mode 100644 build/resources/admin/assets/index-nuyWwy-z.js rename build/resources/admin/assets/{index-CxsIPycV.js => index-o8DV6baU.js} (80%) create mode 100644 build/resources/admin/assets/index-oOhs76pg.js rename build/resources/admin/assets/{index-CHZ9Ls9_.js => index-pVAXVlCV.js} (80%) create mode 100644 build/resources/admin/assets/index-pVAXVlCV.js.gz delete mode 100644 build/resources/admin/assets/index-qSQlj0RO.js.gz delete mode 100644 build/resources/admin/assets/index-r0EsCkBw.js.gz delete mode 100644 build/resources/admin/assets/index-zIv9y_XZ.js.gz rename build/resources/admin/assets/{index.vue_vue_type_script_setup_true_lang-DNzkYHJK.js => index.vue_vue_type_script_setup_true_lang-BKMZ6sUc.js} (86%) create mode 100644 build/resources/admin/assets/index.vue_vue_type_script_setup_true_lang-BvPXNSi5.js delete mode 100644 build/resources/admin/assets/index.vue_vue_type_script_setup_true_lang-C-eRvstH.js delete mode 100644 build/resources/admin/assets/index.vue_vue_type_script_setup_true_lang-DUyHB5jc.js.gz rename build/resources/admin/assets/{index.vue_vue_type_script_setup_true_lang-DUyHB5jc.js => index.vue_vue_type_script_setup_true_lang-DkYjV9Ul.js} (96%) create mode 100644 build/resources/admin/assets/index.vue_vue_type_script_setup_true_lang-DkYjV9Ul.js.gz delete mode 100644 build/resources/admin/assets/isArrayLikeObject-BJDhNHaT.js create mode 100644 build/resources/admin/assets/isArrayLikeObject-CQAE3P4Q.js rename build/resources/admin/assets/{item-card-dialog-CJK_Js5q.js => item-card-dialog-BLfIvYMZ.js} (63%) rename build/resources/admin/assets/{itemCards-BHUYJ5ez.js => itemCards-QB81Evem.js} (82%) rename build/resources/admin/assets/{md5-BDd5Ze2N.js => md5-FaX5g_uo.js} (99%) delete mode 100644 build/resources/admin/assets/menu-dialog-BPsiSVWz.js create mode 100644 build/resources/admin/assets/menu-dialog-enpyfDv5.js delete mode 100644 build/resources/admin/assets/menu-dialog.vue_vue_type_script_setup_true_lang-KTN5b8tT.js.gz rename build/resources/admin/assets/{menu-dialog.vue_vue_type_script_setup_true_lang-KTN5b8tT.js => menu-dialog.vue_vue_type_script_setup_true_lang-e7mKRWRQ.js} (68%) create mode 100644 build/resources/admin/assets/menu-dialog.vue_vue_type_script_setup_true_lang-e7mKRWRQ.js.gz delete mode 100644 build/resources/admin/assets/message-box-CHGPmvsv.js.gz rename build/resources/admin/assets/{message-box-CHGPmvsv.js => message-box-mfsaE52U.js} (82%) create mode 100644 build/resources/admin/assets/message-box-mfsaE52U.js.gz create mode 100644 build/resources/admin/assets/new-user-C0sObr0V.js delete mode 100644 build/resources/admin/assets/new-user-c5Y64LbE.js rename build/resources/admin/assets/{operations-CsHdqaV1.js => operations-BDlSOaTf.js} (94%) rename build/resources/admin/assets/{order-funnel-DkeAQ_Xa.js => order-funnel-lFnxfUnB.js} (90%) delete mode 100644 build/resources/admin/assets/player-detail-drawer-gKf3Owlo.js delete mode 100644 build/resources/admin/assets/player-detail-drawer-gKf3Owlo.js.gz create mode 100644 build/resources/admin/assets/player-detail-drawer-jpdh6q1M.js create mode 100644 build/resources/admin/assets/player-detail-drawer-jpdh6q1M.js.gz create mode 100644 build/resources/admin/assets/player-search-CV64a3i5.js delete mode 100644 build/resources/admin/assets/player-search-D2VXDU2D.js rename build/resources/admin/assets/{product-njVTTbuM.js => product-FfcA_cLd.js} (89%) create mode 100644 build/resources/admin/assets/product-search-CA-a7G4m.js delete mode 100644 build/resources/admin/assets/product-search-DpH35CXa.js create mode 100644 build/resources/admin/assets/raf-C-iJO6ki.js delete mode 100644 build/resources/admin/assets/raf-CXqF1NB1.js create mode 100644 build/resources/admin/assets/role-edit-dialog-Cm1NPJkR.js delete mode 100644 build/resources/admin/assets/role-edit-dialog-HX4f6dK1.js rename build/resources/admin/assets/{role-edit-dialog.vue_vue_type_script_setup_true_lang-Dq-R3tNQ.js => role-edit-dialog.vue_vue_type_script_setup_true_lang-BJbX2A59.js} (90%) delete mode 100644 build/resources/admin/assets/role-permission-dialog-DreZDJMZ.js create mode 100644 build/resources/admin/assets/role-permission-dialog-ewu9RAvj.js rename build/resources/admin/assets/{role-permission-dialog.vue_vue_type_script_setup_true_lang-CXStYEU5.js => role-permission-dialog.vue_vue_type_script_setup_true_lang-Cr_LJRAQ.js} (68%) delete mode 100644 build/resources/admin/assets/role-search-BZWT66Sc.js create mode 100644 build/resources/admin/assets/role-search-CVOVdRto.js rename build/resources/admin/assets/{role-search.vue_vue_type_script_setup_true_lang-DDg8elr1.js => role-search.vue_vue_type_script_setup_true_lang-BerzXeNX.js} (92%) create mode 100644 build/resources/admin/assets/sales-overview-CChODLAb.js delete mode 100644 build/resources/admin/assets/sales-overview-RF4W6ern.js rename build/resources/admin/assets/{sales-overview.vue_vue_type_script_setup_true_lang-D1zk2Zjj.js => sales-overview.vue_vue_type_script_setup_true_lang-DGkuAMN0.js} (84%) rename build/resources/admin/assets/{scroll-Cceui3yC.js => scroll-bFiZhqzI.js} (94%) rename build/resources/admin/assets/{slider-CFh6w1Nh.js => slider-C0BHCDqQ.js} (80%) create mode 100644 build/resources/admin/assets/slider-C0BHCDqQ.js.gz delete mode 100644 build/resources/admin/assets/slider-CFh6w1Nh.js.gz rename build/resources/admin/assets/{titles-Bs2uXvDP.js => titles-BnzYQ5A4.js} (90%) delete mode 100644 build/resources/admin/assets/todo-list-B1wap53h.js create mode 100644 build/resources/admin/assets/todo-list-kXDrs5Fd.js delete mode 100644 build/resources/admin/assets/tree-select-DNitEO0k.js.gz rename build/resources/admin/assets/{tree-select-DNitEO0k.js => tree-select-_Xz6f9er.js} (61%) create mode 100644 build/resources/admin/assets/tree-select-_Xz6f9er.js.gz create mode 100644 build/resources/admin/assets/use-dialog-BDM4AbF-.js delete mode 100644 build/resources/admin/assets/use-dialog-yhiLZsn9.js rename build/resources/admin/assets/{use-form-common-props-BOfz9xGR.js => use-form-common-props-U6f9D53_.js} (79%) rename build/resources/admin/assets/{use-form-item-ByasanrX.js => use-form-item-BsnpemL_.js} (83%) rename build/resources/admin/assets/{useChart-MKcUOr4d.js => useChart-CqKOtsKK.js} (99%) create mode 100644 build/resources/admin/assets/useChart-CqKOtsKK.js.gz delete mode 100644 build/resources/admin/assets/useChart-MKcUOr4d.js.gz rename build/resources/admin/assets/{useHeaderBar-DUbpCIQq.js => useHeaderBar-B-Loh-tq.js} (98%) rename build/resources/admin/assets/{useTable-MDpDGlZA.js => useTable-BIumkxFC.js} (89%) create mode 100644 build/resources/admin/assets/useTable-BIumkxFC.js.gz delete mode 100644 build/resources/admin/assets/useTable-MDpDGlZA.js.gz rename build/resources/admin/assets/{useTableColumns-B3dG-7w1.js => useTableColumns-BjNnGKmd.js} (94%) delete mode 100644 build/resources/admin/assets/user-dialog-BpyqYfU8.js create mode 100644 build/resources/admin/assets/user-dialog-FK0h2O40.js delete mode 100644 build/resources/admin/assets/user-dialog.vue_vue_type_script_setup_true_lang-DU6JD3Hu.js.gz rename build/resources/admin/assets/{user-dialog.vue_vue_type_script_setup_true_lang-DU6JD3Hu.js => user-dialog.vue_vue_type_script_setup_true_lang-VAEhSDaq.js} (98%) create mode 100644 build/resources/admin/assets/user-dialog.vue_vue_type_script_setup_true_lang-VAEhSDaq.js.gz delete mode 100644 build/resources/admin/assets/user-search-CGsgEu3y.js create mode 100644 build/resources/admin/assets/user-search-PL6UsLdr.js rename build/resources/admin/assets/{user-search.vue_vue_type_script_setup_true_lang-HKbP4LzM.js => user-search.vue_vue_type_script_setup_true_lang-sPKa3nOb.js} (92%) create mode 100644 build/resources/admin/assets/validator-BHWhHngj.js delete mode 100644 build/resources/admin/assets/validator-D_3Fyw1a.js create mode 100644 build/resources/admin/assets/vnode-C4RX5enz.js delete mode 100644 build/resources/admin/assets/vnode-CqxKQBn0.js create mode 100644 docs/删除操作交互流程整改计划.md create mode 100644 docs/奖励管理字段拆分/ALIGNMENT_奖励管理字段拆分.md create mode 100644 docs/奖励管理字段拆分/CONSENSUS_奖励管理字段拆分.md create mode 100644 docs/奖励管理字段拆分/DESIGN_奖励管理字段拆分.md create mode 100644 docs/奖励管理字段拆分/TASK_奖励管理字段拆分.md create mode 100644 internal/api/admin/auth_refresh.go create mode 100644 internal/api/admin/product_batch.go create mode 100644 internal/api/admin/system_recycle.go create mode 100644 internal/repository/mysql/testrepo_sqlite.go create mode 100644 internal/router/interceptor/admin_rbac.go create mode 100644 internal/router/interceptor/admin_rbac_test.go create mode 100644 internal/service/recycle/recycle_service.go create mode 100644 migrations/2025-11-18_soft_delete.sql diff --git a/.DS_Store b/.DS_Store index 36c4879c1e3dc0c3b0c977c8239db491442c4a1e..8ea65f89ce2054da60056cb4672fde4e4df8af32 100644 GIT binary patch delta 32 ocmZn(XbG6$OBU^hRb+GZYsrJS3eN-D8UY%tl(uJD%~0J!f98UO$Q delta 67 zcmZn(XbG6$mJU^hRb`eq)1rJSl<3?&SiK%B#n%Ag0NQyGdGN<4G&laq4tlNcBn U1Q-|?e{NnRX~?#jUEwb~0BkZ65&!@I diff --git a/.trae/.ignore b/.trae/.ignore new file mode 100644 index 0000000..e69de29 diff --git a/.trae/documents/修复活动创建向导步骤逻辑并统一全局风格.md b/.trae/documents/修复活动创建向导步骤逻辑并统一全局风格.md new file mode 100644 index 0000000..986e9b8 --- /dev/null +++ b/.trae/documents/修复活动创建向导步骤逻辑并统一全局风格.md @@ -0,0 +1,49 @@ +## 问题确认 +- 症状:在“创建活动”向导中点击“下一步”后,出现“批量抽奖”界面,用户认为逻辑错误。 +- 初步研判:批量抽奖来自 `BatchDrawDialog`(src/views/activity/rewards/modules/batch-draw-dialog.vue)。该组件在管理页(src/views/activity/manage/index.vue)中常驻渲染,可能因不当条件渲染或状态误触发而在向导流程中弹出。 +- 路由侧现状:列表页有 `router.push({ name: 'ActivityWizard' })`,但路由模块未注册对应路由,存在跳转异常风险。 + +## 目标 +1. 保证向导“下一步”仅在步骤间顺序前进:活动 → 期数 → 奖品,绝不触发批量抽奖。 +2. 统一界面风格为项目全局规范:颜色、圆角、阴影、按钮、表单、布局与动画。 +3. 保留现有功能,避免回归,完成自测与构建验证。 + +## 技术方案 +### 1. 逻辑修复 +- 向导页面(两处)严格限制导航: + - `src/views/activity/wizard/index.vue`:`next()` 仅在成功创建活动后 `active.value = 1`,在成功创建期数后 `active.value = 2`,移除或校验任何非最终提交阶段的 `router.push` 与其他弹窗触发。 + - `src/views/activity/manage/index.vue`:`nextWizard()` 仅递进 `wizardActive`。确保 `BatchDrawDialog` 的 `v-model` 只由 `openBatchDraw()` 显式开启。 +- 隔离批量抽奖弹窗: + - 在管理页将 `` 增加条件渲染 `v-if="!showWizard"`,确保向导弹窗期间不渲染批量抽奖组件,从根本杜绝误触发。 +- 路由补全:在 `src/router/modules/activity.ts` 注册 `ActivityWizard` 路由(path `/activity/wizard`,component `'/activity/wizard'`),避免列表页“创建活动”跳转异常导致意外页面状态。 + +### 2. 风格统一 +- 去除自定义渐变与玻璃拟态,改为全局样式: + - 主容器采用 `art-card` 与项目标准阴影、圆角(卡片 16px,按钮/输入 12px)。 + - 步骤指示器使用项目的圆形数字样式与轻量过渡;颜色使用主题色 `#5D87FF` 及状态色变量。 + - 表单与按钮风格遵循项目统一尺寸、高度与交互(36px 高度,标准阴影与过渡)。 +- 统一 CSS 变量与工具类: + - 使用项目内的 CSS 变量(颜色、圆角、间距),移除局部硬编码样式。 + - 采用现有 `art-card`、栅格与间距体系,保证一致性。 + +### 3. 验证与测试 +- 开发验证: + - 在两处向导中执行完整流程(创建活动→创建期数→配置奖品),确认“下一步”不触发 `BatchDrawDialog`。 + - 构建检查:`npm run build` 无 TS/构建错误。 +- 回归验证: + - 管理页列表的“模拟(批量抽奖)”按钮仍可正常打开 `BatchDrawDialog`。 + - 列表页“创建活动”跳转到向导路由正常。 + +## 交付物 +- 修复后的向导逻辑代码(两处),隔离批量抽奖弹窗的条件渲染。 +- 新增路由项 `ActivityWizard`。 +- 去除非统一风格的样式,替换为项目全局风格实现。 +- 构建与运行验证结果说明。 + +## 验收标准 +- 向导点击“下一步”始终进入下一步骤,不出现批量抽奖。 +- 界面风格与全局统一(颜色、圆角、阴影、按钮、表单风格一致)。 +- 列表页“创建活动”跳转向导正常;管理页“模拟”按钮打开批量抽奖正常。 +- 构建通过,无新错误。 + +如确认以上方案,我将开始实施并同步每一步的修复与验证结果。 \ No newline at end of file diff --git a/.trae/documents/删除级联逻辑优化方案.md b/.trae/documents/删除级联逻辑优化方案.md new file mode 100644 index 0000000..78129b2 --- /dev/null +++ b/.trae/documents/删除级联逻辑优化方案.md @@ -0,0 +1,86 @@ +## 背景 +- 当前不启用数据库外键约束(FK),删除由业务逻辑驱动。 +- 现有删除实现多为单表删除,未做级联:如删除活动 `internal/service/activity/activity_delete.go:9`、删除期 `internal/service/activity/issue_delete.go:9`、删除用户 `internal/service/user/batch_user.go:32`。 +- GORM 初始化未声明外键或级联:`internal/repository/mysql/mysql.go:97`。 + +## 目标 +- 在业务层实现“逻辑级联删除”,保证删除主实体时,同步清理其关联业务数据,且性能与可控性优于 FK 级联。 + +## 级联清单 +### 删除活动(Activities.ID) +- 删除活动期:`activity_issues` 按 `activity_id`(`internal/repository/mysql/model/activity_issues.gen.go:18`) +- 期下配置与承诺: + - 奖励配置 `activity_reward_settings` 按 `issue_id`(`internal/repository/mysql/model/activity_reward_settings.gen.go:18`) + - 随机承诺 `issue_random_commitments` 按 `issue_id`(`internal/repository/mysql/model/issue_random_commitments.gen.go:18`) +- 期下抽奖相关: + - 抽奖日志 `activity_draw_logs` 按 `issue_id`(`internal/repository/mysql/model/activity_draw_logs.gen.go:18`) + - 抽奖效果 `activity_draw_effects` 按 `draw_log_id`/`issue_id`(`internal/repository/mysql/model/activity_draw_effects.gen.go:17,29`) + - 抽奖凭据 `activity_draw_receipts` 按 `draw_log_id`(`internal/repository/mysql/model/activity_draw_receipts.gen.go:17`) +- 活动范围效果与道具: + - 用户道具使用记录 `user_item_cards` 按 `used_activity_id` / `used_issue_id` / `used_draw_log_id`(`internal/repository/mysql/model/user_item_cards.gen.go:24,25,23`) + - 抽奖效果快照 `activity_draw_effects` 按 `activity_id`/`issue_id`(`internal/repository/mysql/model/activity_draw_effects.gen.go:28,29`) +- 资产与日志: + - 用户资产 `user_inventory` 按 `activity_id`(`internal/repository/mysql/model/user_inventory.gen.go:21`) + - 公会贡献日志 `guild_contribute_logs` 按 `activity_id` / `issues_id`(`internal/repository/mysql/model/guild_contribute_logs.gen.go:19,20`) +- 系统模板: + - 系统道具卡 `system_item_cards` 按 `activity_id` / `issue_id`(`internal/repository/mysql/model/system_item_cards.gen.go:23,24`) + - 系统优惠券 `system_coupons` 按 `activity_id`(`internal/repository/mysql/model/system_coupons.gen.go:20`) +- 最后删除活动主表:`activities`(`internal/repository/mysql/model/activities.gen.go:15`) + +### 删除用户(Users.ID) +- 用户身份与权益: + - 头衔 `user_titles` 按 `user_id`(`internal/repository/mysql/model/user_titles.gen.go:16`) + - 领取型权益限流 `user_title_effect_claims` 按 `user_id`(`internal/repository/mysql/model/user_title_effect_claims.gen.go:16`) +- 用户账户与地址: + - 地址 `user_addresses` 按 `user_id`(`internal/repository/mysql/model/user_addresses.gen.go:18`) + - 积分余额 `user_points` 按 `user_id`(`internal/repository/mysql/model/user_points.gen.go:18`) + - 积分流水 `user_points_ledger` 按 `user_id`(`internal/repository/mysql/model/user_points_ledger.gen.go:17`) +- 用户订单与优惠: + - 订单 `orders` 按 `user_id`(`internal/repository/mysql/model/orders.gen.go:18`) + - 用户优惠券 `user_coupons` 按 `user_id`(`internal/repository/mysql/model/user_coupons.gen.go:18`) +- 用户资产与道具: + - 用户资产 `user_inventory` 按 `user_id`(`internal/repository/mysql/model/user_inventory.gen.go:18`) + - 用户道具卡 `user_item_cards` 按 `user_id`(`internal/repository/mysql/model/user_item_cards.gen.go:18`) +- 与抽奖相关: + - 抽奖效果 `activity_draw_effects` 按 `user_id` 或关联 `draw_log_id`(`internal/repository/mysql/model/activity_draw_effects.gen.go:18,17`) + - 抽奖凭据 `activity_draw_receipts` 关联 `draw_log_id`(`internal/repository/mysql/model/activity_draw_receipts.gen.go:17`) + - 抽奖日志 `activity_draw_logs` 按 `user_id`(`internal/repository/mysql/model/activity_draw_logs.gen.go:17`) +- 公会关联: + - 公会成员 `guild_members` 按 `user_id`(`internal/repository/mysql/model/guild_members.gen.go:18`) + - 公会贡献日志 `guild_contribute_logs` 按 `user_id`(`internal/repository/mysql/model/guild_contribute_logs.gen.go:18`) +- 履约/发货: + - 发货记录 `shipping_records` 按 `user_id`(`internal/repository/mysql/model/shipping_records.gen.go:18`) + - 运营发货统计 `ops_shipping_stats` 按 `user_id`(`internal/repository/mysql/model/ops_shipping_stats.gen.go:21`) +- 最后软删用户:`users.deleted_at`(`internal/repository/mysql/model/users.gen.go:20`) + +## 删除顺序(事务内) +- 统一采用“从叶到根”的顺序: + 1) 以日志/效果/凭据等子表为先(`activity_draw_effects`、`activity_draw_receipts`、`activity_draw_logs`) + 2) 再清理资产/权益/模板(`user_inventory`、`user_item_cards`、`user_titles`、`system_*`) + 3) 清理期与期下配置(`activity_issues`、`activity_reward_settings`、`issue_random_commitments`) + 4) 删除根实体(`activities` 或软删 `users`) +- 全过程包裹在单事务中,任何一步失败则回滚。 + +## 实现策略 +- 新增业务服务方法: + - `DeleteActivityCascade(ctx, activityID)`:按“删除活动”清单顺序删除 + - `DeleteUserCascade(ctx, userID)`:按“删除用户”清单顺序删除 +- 技术要点: + - 批量删除使用分批(如 5k/批)避免长事务与大锁;必要时按时间/ID 片段迭代 + - 统一 `WHERE` 条件与索引列(`user_id`/`activity_id`/`issue_id`/`draw_log_id`)确保扫描性能 + - 软删与硬删:保留用户软删(合规与审计),其余按当前表定义硬删;如需统一软删,可后续逐表补充 `gorm.DeletedAt` + - 幂等性:每个子删除操作按条件删除,无记录时直接通过;重复调用不报错 + - 审计:记录操作日志(操作者、对象ID、影响行数),便于回溯 + +## 验收标准 +- 删除活动时,任一期及其抽奖日志/效果/凭据、奖励配置、承诺、资产、贡献日志与相关模板均被清理;根活动删除成功。 +- 删除用户时,地址/订单/优惠券/积分(余额+流水)/资产/道具/抽奖相关/公会关系/发货记录及统计均被清理;根用户软删成功。 +- 全流程事务保障、失败回滚;批量删除性能稳定,无显著锁表或超时。 + +## 需要改造的现有入口(参考) +- 活动删除入口:`internal/service/activity/activity_delete.go:9` → 升级为调用 `DeleteActivityCascade` +- 期删除入口:`internal/service/activity/issue_delete.go:9` → 被 `DeleteActivityCascade` 内部调用(或保留独立级联) +- 用户删除入口:`internal/service/user/batch_user.go:32` → 升级为调用 `DeleteUserCascade` + +## 后续动作 +- 我将基于以上清单与顺序,补充两套事务级联删除实现,并为关键入口替换调用;同时补充单元测试覆盖正常/边界/异常三类用例,验证幂等与性能。 \ No newline at end of file diff --git a/.trae/documents/商品列表批量操作与活动创建优化及仪表盘修复方案.md b/.trae/documents/商品列表批量操作与活动创建优化及仪表盘修复方案.md new file mode 100644 index 0000000..3a22339 --- /dev/null +++ b/.trae/documents/商品列表批量操作与活动创建优化及仪表盘修复方案.md @@ -0,0 +1,93 @@ +## 总览 +- 覆盖 5 个问题:商品列表批量操作、活动创建奖品价格与总价、仪表盘图表重叠修复、活动创建性能、弹窗点击关闭问题。 +- 技术栈:管理端前端 Vue3 + Element Plus(路径 `web/admin`),后端 Go(路径 `internal/*`)。 +- 目标:补齐商品批量接口、完善前端交互与缓存、修正布局与加载时序、增强性能与统一弹窗行为。 + +## 关键位置 +- 商品前端:`web/admin/src/views/product/list/index.vue`,API:`web/admin/src/api/product.ts` +- 商品后端:`internal/api/admin/product_create.go`、Service:`internal/service/product/product.go` +- 活动向导:`web/admin/src/views/activity/wizard/index.vue` +- 奖励管理:`web/admin/src/views/activity/rewards/index.vue` +- 仪表盘容器:`web/admin/src/views/dashboard/console/index.vue` +- 抽奖量图表:`web/admin/src/views/dashboard/console/modules/sales-overview.vue` + `components/core/charts/art-line-chart` +- 中奖率分析:`web/admin/src/views/dashboard/console/modules/activity-prize-analysis.vue` +- 弹窗示例:`web/admin/src/views/player-manage/modules/*Dialog.vue`、`components/core/layouts/*` + +## 1. 商品列表功能优化 +- 后端批量接口 + - 新增 `PUT /api/admin/products/batch`:请求体 `{ ids: number[], stock?: number, status?: 1|2 }`,返回 `{ updated_count, failed: [ {id, reason} ] }`。 + - Controller:在 `internal/api/admin/product_create.go` 或新文件 `product_batch.go` 增加路由与校验(限制 `ids` 1–1000,`stock >= 0`)。 + - Service:`internal/service/product/product.go` 增加 `BatchUpdate(ctx, ids, stock?, status?)`,事务更新;支持“仅库存”“仅状态”“二者皆有”。 + - DAO:使用批量更新(`UPDATE products SET stock=?, status=? WHERE id IN (...)`),对无权限/不存在的 `id` 记录失败原因。 + - 文档:更新 swagger(`docs/swagger.yaml`)。 +- 前端批量操作 + - 列表顶部添加“全选本页”复选框:基于 `ElTable` 的选择事件维护 `selectedIds: number[]`,`checkAll` 同步当页数据。 + - 批量工具栏:按钮“批量改库存”“批量上架”“批量下架”。每次操作弹出确认框(库存输入/操作确认)。 + - API:在 `web/admin/src/api/product.ts` 新增 `batchUpdateProducts(payload)` 调用上游接口;失败项以通知/表格高亮反馈。 + - 交互:操作成功后刷新当页并清空选中;支持 loading 与禁用态;无选中时禁用按钮。 + +## 2. 活动创建流程优化(奖品价格与总价) +- 单价展示 + - 远程商品搜索返回 `price` 字段(已有),选择时在 `ElSelect` 的 `option` 右侧显示“¥单价”。 + - 在奖励编辑卡片/表格中追加“单价”列(从缓存读取)。 +- 总价计算 + - 公式:`总价 = Σ(price(product_id) * quantity)`;监听 `product_id/quantity` 变更实时更新。 + - 缓存:在 `wizard/index.vue` 与 `rewards/index.vue` 维护 `priceCache: Map`,首次选择命中即用,未命中时调用 `fetchProducts` 补齐并写入。 + - 同步:商品价格变更时(重新打开或重新拉取),更新缓存并触发总价重算;保留两位小数。 + - 展示:步骤3顶部显示“选中奖品总成本:¥xxx.xx”。 + +## 3. 仪表盘数据展示修复(重叠问题) +- 布局与间距 + - 在 `console/index.vue` 的 `ElRow` 增加统一 `gutter`(如 `20`),保证模块间距。 + - 调整 `ElCol` 断点:避免同一行 `ActivityPrizeAnalysis` 与其他模块在窄屏并排压缩;在 `md/sm` 断点落到 `24` 独占一行。 +- 组件容器 + - 为 `sales-overview.vue` 与 `activity-prize-analysis.vue` 外层卡片设置 `position: relative; z-index: 0`;图表内部设 `z-index: 1`,避免 canvas/svg 溢出覆盖。 + - 统一卡片最小高度,防止加载时高度为 0 导致重叠。 +- 加载时序 + - 图表组件在 `dataReady` 后再渲染;`v-if="dataReady"` 避免空容器渲染。 + - 保留/增加骨架或加载态,防止内容突变挤压。 + +## 4. 活动创建性能优化(步骤1) +- 加载指示 + - `wizard/index.vue` 步骤1添加 `v-loading` 与骨架;按钮禁用在加载中。 +- 请求超时 + - 前端 axios 层设置接口超时 `30s`,在 `web/admin/src/api/_http.ts`(或全局实例)设置,并对超时给出 Toast 提示与重试入口。 +- 慢请求日志 + - Axios 响应拦截记录 `duration > 2000ms` 的接口,打印到控制台并上报(如有日志上报端点)。 +- 初始化优化 + - 并行拉取必要数据(活动分类、默认配置、组织信息),减少串行等待;命中表格缓存的接口优先读缓存(已有 `tableCache`)。 +- 错误恢复 + - 失败时保留已填写表单,显示错误提示与“重试/刷新数据”按钮;网络恢复后自动重载。 + +## 5. 弹窗点击关闭没有反应(统一与修复) +- 审核现状 + - 业务型对话框普遍设置 `:close-on-click-modal="false"`(避免误关);全局搜索/锁屏等未禁用遮罩关闭且部分隐藏关闭按钮。 +- 统一策略 + - 业务表单:保留 `close-on-click-modal=false`,确保“关闭按钮/取消按钮/ESC”都能关闭;统一通过 `@update:model-value` 与 `@closed` 重置。 + - 全局弹窗:明确设置 `:close-on-click-modal="false"` 并显示关闭按钮或提供显式关闭入口(避免遮罩点击不生效引起困惑)。 +- 修复点 + - 核查所有 `ElDialog/ElDrawer`:确保存在 `@update:model-value` 或 `v-model` 双向绑定;为关闭图标绑定 `emit('update:visible', false)`;在有 `before-close` 时正确调用 `done()`。 + - 统一 ESC 关闭(可选):在布局层面监听 ESC 并广播关闭事件。 + +## 验收标准 +- 商品批量: + - 在商品列表勾选 N 个,批量改库存/上下架成功,返回计数与失败明细;刷新后状态一致。 +- 奖品选择: + - 选择奖品即显示单价,总价随数量/选择实时更新;切换商品价格后总价同步。 +- 仪表盘: + - “活动抽奖量”与“活动中奖率分析”在所有断点无重叠,布局稳定;数据加载时不挤压。 +- 性能与超时: + - 步骤1加载显示进度,超时 30s 有提示与重试;>2s 请求有日志可见。 +- 弹窗关闭: + - 所有弹窗可通过关闭按钮/取消按钮/ESC 关闭;遮罩行为与设计一致,不出现“点击关闭没有反应”。 + +## 交付变更清单 +- 后端:新增批量接口(Controller+Service+DAO+Swagger)。 +- 前端: + - 商品列表批量选择与工具栏;新增批量 API 调用与确认反馈。 + - 向导步骤3与奖励管理页显示单价与总价;缓存价格。 + - 仪表盘栅格与 z-index 调整;图表渲染时序保护。 + - Axios 超时与慢请求日志;向导加载骨架与错误恢复。 + - 弹窗双向绑定与关闭逻辑统一。 + +如确认本方案,我将按上述清单依次实现并提交改动,过程中为关键函数添加函数级注释与必要单元/交互测试。 \ No newline at end of file diff --git a/.trae/documents/实现回收站与软删除机制.md b/.trae/documents/实现回收站与软删除机制.md new file mode 100644 index 0000000..02a64bb --- /dev/null +++ b/.trae/documents/实现回收站与软删除机制.md @@ -0,0 +1,68 @@ +## 总体方案 +- 引入统一软删除机制:所有支持回收站的业务表增加 `deleted_at DATETIME NULL`、`deleted_by BIGINT NULL` 字段,并在 ORM 层启用 GORM Soft Delete 以默认过滤已删除记录。 +- 提供中心化回收站接口与界面:集中列出各类型的软删除数据,支持恢复与彻底删除(二次确认)。 +- 权限与菜单对齐:仅管理员可访问回收站;侧边栏在“系统管理”下新增“回收站”菜单,配置角色可见与按钮权限。 + +## 数据库改造 +- 目标表(根据现有模型与端点清单):`activities`、`activity_issues`、`activity_reward_settings`、`products`、`product_categories`、`banner`、`guild`、`system_titles`、`system_title_effects`、`system_item_cards`、`system_coupons`、`menus`、`menu_actions`、`roles`、`role_users`。 +- 变更内容: + - 添加列:`deleted_at DATETIME NULL`(建立索引以提升查询效率)、`deleted_by BIGINT NULL`。 + - 后续通过 `cmd/gormgen` 重新生成 `internal/repository/mysql/{model,dao}/*.gen.go`,使模型包含 `gorm.DeletedAt` 与 `DeletedBy` 字段。 +- 迁移策略:当前仓库未集成迁移工具,计划新增 `migrations/` 与 SQL 脚本(或集成 `goose`),在测试/生产库执行列新增与索引建立,完成后重新生成 ORM 代码。 + +## 后端改造 +- 技术栈:`gin` + `gorm`(已存在),读写库封装见 `internal/repository/mysql/mysql.go`,路由集中在 `internal/router/router.go`。 +- 删除逻辑切换为软删除: + - 将所有 `DELETE /api/...` 对应的处理器由物理删除改为 `gorm` 软删除:`db.Delete(&model)`,自动写入 `deleted_at`;同时通过上下文注入当前用户 ID 写入 `deleted_by`(更新列)。 + - 涉及端点示例(集中定义于 `internal/router/router.go` 对应 handler):活动、期数、奖励、工会、商品、分类、轮播图、称号/特效、道具卡、优惠券、系统菜单/动作、角色、角色成员等。 +- 查询默认过滤: + - 使用含 `gorm.DeletedAt` 的模型,`gorm` 默认生成 `WHERE deleted_at IS NULL`;确保服务与 DAO 层不使用 `Unscoped()`,避免误返回已删除数据。 +- 回收站接口:新增 `internal/api/admin/system_recycle.go` 与服务层 `internal/service/recycle/recycle_service.go`: + - `GET /api/admin/recycle`:参数 `type`(枚举:activity、issue、reward、product...)、分页;实现为 `Unscoped().Where("deleted_at IS NOT NULL")` 列表,返回原始字段与 `deleted_at`、`deleted_by`。 + - `POST /api/admin/recycle/restore`:参数 `type,id`;`Unscoped()` 更新目标记录 `deleted_at=NULL, deleted_by=NULL`,恢复数据。 + - `DELETE /api/admin/recycle`:参数 `type,id`;`Unscoped().Delete(&model)` 执行物理删除。 +- 关联完整性: + - 软删除不级联子表;外键仍保持,恢复后关联自然可用。 + - 若业务场景需要父子一致软删除(可选扩展),在服务层增加按类型的联动策略,但默认不做级联以降低风险。 +- 权限拦截: + - 以上回收站接口全部走管理员认证中间件 `internal/router/interceptor/admin_auth.go::AdminTokenAuthVerify`。 + - 彻底删除在服务层进行额外二次确认校验(配合前端确认对话框),后端也校验 `role` 或 `action` 权限。 + +## 前端改造(web/admin) +- 框架:`vue3` + `vue-router` + `pinia` + `element-plus`,侧边栏位于 `web/admin/src/components/core/layouts/art-menus/art-sidebar-menu`,菜单数据由路由模块/后端菜单驱动。 +- 路由与页面: + - 新增路由模块 `web/admin/src/router/modules/system/recycle.ts`,`path:'/system/recycle'`,`meta.roles:['admin']`,`meta.authList:['recycle:list','recycle:restore','recycle:forceDelete']`。 + - 新增页面 `web/admin/src/views/system/recycle/index.vue`: + - 顶部类型筛选(下拉:活动、商品、工会…)。 + - 表格列出删除数据(含关键原字段、`deleted_at`、`deleted_by`),分页。 + - 行操作:`恢复`(调用 `POST /api/admin/recycle/restore`)、`彻底删除`(`ElMessageBox.confirm` 二次确认后调用 `DELETE /api/admin/recycle`)。 +- API 封装:`web/admin/src/api/system/recycle.ts`(使用现有 Axios 封装 `web/admin/src/utils/http/index.ts`):`listDeleted(type,page,size)`、`restore(type,id)`、`forceDelete(type,id)`。 +- 菜单项: + - 后端在 `menus` 表增加“系统管理/回收站”,`path:'/system/recycle'`,并在角色分配接口 `POST /api/role/:role_id/menus` 仅赋予管理员;前端 `ListSimpleMenus` 拉取后自动渲染。 +- 权限指令与守卫: + - 按按钮权限 `meta.authList` 与角色 `meta.roles` 控制显示;普通用户不显示菜单,无法访问路由或调用接口。 + +## 权限控制细则 +- 仅管理员角色拥有“回收站”菜单与相关按钮权限;普通用户删除操作也走软删除,但无法访问回收站接口。 +- 彻底删除在前端强制二次确认,并在后端再次校验权限与保护条件(如必须管理员)。 + +## 测试方案 +- 后端(Go `testing`): + - `internal/api/admin/system_recycle_test.go`: + - 删除后记录存在于回收站接口,原查询不可见(验证 `deleted_at` 与默认过滤)。 + - 恢复后记录重新可见,`deleted_at`/`deleted_by` 为空。 + - 权限:非管理员访问回收站接口返回 `401/403`;管理员正常。 + - 关联完整性:恢复后关联查询正常(父子外键未破坏)。 +- 前端(Vitest): + - `web/admin/src/tests/system/recycle/recycle.test.ts`: + - 菜单与路由权限:管理员可见“回收站”,普通用户不可见。 + - 列表加载与分页、恢复按钮触发成功提示与刷新、彻底删除二次确认弹窗与成功流程。 + - 401/403 拦截按现有拦截器工作(参考 `src/tests/auth/401-error-handling.test.ts`)。 + +## 交付与验证 +- 执行数据库迁移并生成 ORM 代码 → 修改删除逻辑到软删除 → 新增回收站接口与前端页面 → 配置菜单与角色 → 编写并运行测试。 +- 验收标准: + - 所有默认查询不返回已删除记录。 + - 回收站能列出各类删除记录,恢复/彻底删除工作正常。 + - 权限严格生效;普通用户删除进入回收站但不可访问回收站界面。 + - 恢复后关联数据完整、功能正常。 \ No newline at end of file diff --git a/.trae/documents/彻底修复批量抽奖误弹与统一“新建活动”UI为 Element Plus 官方风格.md b/.trae/documents/彻底修复批量抽奖误弹与统一“新建活动”UI为 Element Plus 官方风格.md new file mode 100644 index 0000000..dcd8c7a --- /dev/null +++ b/.trae/documents/彻底修复批量抽奖误弹与统一“新建活动”UI为 Element Plus 官方风格.md @@ -0,0 +1,49 @@ +## 目标 + +* 在活动模块中彻底移除“批量测试/批量抽奖”入口与弹窗,避免在创建流程中出现。 + +* 保留其他模块(如“抽奖模拟盈亏”页面)的相似功能,不影响运营侧工具。 + +* 统一“新建活动”UI为 [https://www.artd.pro/docs/zh/ ](https://www.artd.pro/docs/zh/) 官方风格,保持与项目其他新建页面一致。 + +## 变更范围 + +* 移除组件引用与入口: + + * `web/admin/src/views/activity/manage/index.vue`:删除 `BatchDrawDialog` 引用与渲染、`openBatchDraw` 按钮及逻辑。 + + * `web/admin/src/views/activity/list/index.vue`:删除批量抽奖按钮与 `BatchDrawDialog` 渲染。 + +* 组件保留但不再使用: + + * `web/admin/src/views/activity/rewards/modules/batch-draw-dialog.vue` 保留于项目(供其他模块复用),但活动模块不再引用。 + +* 路由与流程: + + * 无需调整路由;保留 `Operations/LotterySimulation` 等运营侧工具。 + + * 确保“创建活动 → 期数 → 奖品”流程不再出现任何测试入口。 + +## UI统一 + +* 两处向导页面完全采用官方 EP 风格(已部分完成): + + * `ElSteps` simple 模式; + + * `ElCard` 内容区; + + * `ElForm` 默认尺寸与标准栅格; + + * 移除自定义渐变、玻璃拟态与重阴影。 + +## 验收标准 + +* 活动模块中不再出现“批量测试/批量抽奖”按钮或弹窗。 + +* 创建流程不再被任何测试功能打断。 + +* 新建活动页面的视觉与项目其他新建保持一致(EP 官方风格)。 + +* 构建与运行通过,页面与流程验证无误。 + +如确认,我将执行上述移除与统一改造,并完成构建与流程验证。 diff --git a/.trae/documents/活动创建“下一步”无响应的排查与改造方案.md b/.trae/documents/活动创建“下一步”无响应的排查与改造方案.md new file mode 100644 index 0000000..8b7791b --- /dev/null +++ b/.trae/documents/活动创建“下一步”无响应的排查与改造方案.md @@ -0,0 +1,31 @@ +## 现状确认 +- 后端接口已对接:`POST /api/admin/activities`(`internal/router/router.go:78`),控制器 `CreateActivity`(`internal/api/admin/activities_admin.go:41`)。 +- 管理端前端已调用:`createActivity()` 指向 `admin/activities`(`web/admin/src/api/adminActivities.ts:13`)。 +- 向导有两种: + - 独立向导页 `wizard/index.vue`:`下一步`仅切换步骤(`next()`),提交动作分别由页面内的`提交`按钮触发(`submitActivity/submitIssue/submitRewards`)。 + - 管理页弹窗向导 `manage/index.vue`:`下一步`会调用创建接口然后推进步骤(`nextWizard()`)。 + +## 可能原因 +- 使用了独立向导页,点击`下一步`未触发API(预期行为需用`提交`按钮)。 +- 请求未通过权限:后端要求超级管理员(`ctx.SessionUserInfo().IsSuper == 1`,`internal/api/admin/activities_admin.go:56-59`)。 +- 请求参数不合法:`name`必填、`activity_category_id`不可为0(同文件 15-18, 51-54)。 +- 环境未配置或认证头缺失:`VITE_API_URL` 未指向后端 `/api` 前缀,或 `Authorization` 未带登录后的纯 Token(不加 `Bearer`)。 + +## 排查步骤 +1) 打开浏览器 Network,在点击`下一步`或`提交`后确认是否发起 `POST {VITE_API_URL}/api/admin/activities`;查看响应码与返回体。 +2) 确认当前页面是弹窗向导还是独立向导: + - 若是独立向导,请使用每步的`提交`按钮;`下一步`只换页不提交。 +3) 验证权限与参数: + - 登录账号需 `is_super=1`;否则返回 400 `禁止操作`。 + - `name`与`activity_category_id`需填充;分类可通过 `GET /api/admin/activity_categories` 获取。 +4) 检查前端环境: + - `VITE_API_URL` 应指向后端根(例如 `http://localhost:8000`),请求工具会拼接 `admin/...` 成 `/api/admin/...`。 + - `Authorization` 头为登录获得的 Token 字符串(无 `Bearer` 前缀)。 + +## 改造建议(可选) +- 为独立向导页 `wizard/index.vue` 增强交互:在 `next()` 中根据 `active` 自动调用对应的 `submitActivity/submitIssue`,使“下一步”即提交。 +- 在按钮文案上提示“提交并进入下一步”,并在未填必填项时给出校验提示(阻止推进)。 + +## 验收标准 +- 在管理端,创建活动流程点击`提交`或(改造后)点击`下一步`能成功发起请求并进入下一步;错误时有明确提示。 +- 非超级管理员或参数缺失时,前端能正确显示后端返回的错误信息。 \ No newline at end of file diff --git a/.trae/documents/活动创建流程重构方案.md b/.trae/documents/活动创建流程重构方案.md new file mode 100644 index 0000000..e9f0d91 --- /dev/null +++ b/.trae/documents/活动创建流程重构方案.md @@ -0,0 +1,87 @@ +## 目标 +- 将活动创建流程改造成“清晰、可操作、可回退”的向导式体验,显著降低填写负担、减少出错率,并确保与现有后端接口完全对齐。 +- 统一字段与交互:活动、期、奖励配置的字段、校验与排序一致;消除重复输入与歧义(如奖励名称以选品自动回填)。 +- 杜绝常见错误:时间格式、权限校验、表单必填项、动态模块加载等问题在交互层面被提前防护。 + +## 现状与痛点 +- 后端接口已齐备(管理端): + - 创建活动:`POST /api/admin/activities`(`internal/router/router.go:78`;控制器 `internal/api/admin/activities_admin.go:41`;服务层 `internal/service/activity/activity_create.go:12-41`) + - 创建期:`POST /api/admin/activities/:activity_id/issues`(`internal/router/router.go:83`;控制器 `internal/api/admin/issues_admin.go`;服务层 `internal/service/activity/issue_create.go:13-32`) + - 创建奖励:`POST /api/admin/activities/:activity_id/issues/:issue_id/rewards` +- 前端存在两套创建入口: + - 独立向导页:`web/admin/src/views/activity/wizard/index.vue`(“下一步”仅切换步骤,提交由各步的“提交”触发) + - 管理页弹窗向导:`web/admin/src/views/activity/manage/index.vue`(“下一步”实际调用创建并推进) +- 痛点汇总: + - 两处入口的字段命名、排序不一致;奖励有的手填名称,有的选商品 + - 时间未填时易触发数据库零日期错误(`TRADITIONAL SQL` 模式下) + - “下一步”与“提交”含义不统一,用户易误操作 + - 表格行式编辑密度过高,填写多项参数不友好 + - 动态导入失败来源于 SFC 结构不合法、重复脚本块或未声明变量导致编译失败 + +## 重构设计(交互与结构) +### 向导统一(单入口) +- 入口:统一从“管理页弹窗向导”打开;支持跳转到独立页,但独立页沿用同一套逻辑(避免两套逻辑分歧) +- 步骤: + 1. 基本信息(活动):名称、分类、状态、门票价格、Boss、开始/结束时间 + 2. 期信息(活动期):期号、状态、排序 + 3. 奖励配置:按商品选择与参数设置(权重、数量、原始数量、等级、排序、Boss) + 4. 确认提交:汇总校验与最终提交 +- 导航: + - “下一步”即执行当前步的提交(成功后推进),失败则停留并有明确错误提示 + - “上一步”回退不丢数据;每步保存草稿状态在组件内存 + +### 表单布局与输入体验 +- 两列栅格布局(`ElRow/ElCol`):提升可读性与并行填写效率 +- 奖励配置交互: + - 改为“卡片列表 + 弹窗表单”,卡片上显示商品名称及关键参数;“新增奖励/编辑奖励”在弹窗中填写,分两列布局 + - 商品选择:远程搜索 `admin/products`(`web/admin/src/api/product.ts`),选择后自动回填奖励名称(传递给后端以兼容) + - 支持复制卡片、删除卡片、批量导入(CSV/JSON)作为扩展项(初版可不做批量) +- 字段统一:奖励行统一字段为 `product_id、weight、quantity、original_qty、level、sort、is_boss`;`name`由选品自动补齐,不再手填 +- 权限与提示:检测 `SessionUserInfo.IsSuper`(控制器内已做),前端在 403/400 的返回文案时专门弹出“权限不足”提示 + +### 校验与防错 +- 实时校验规则: + - 活动:名称与分类必填;开始/结束时间支持空,但若为空则前端默认以 `ISO8601` 当前时间传入或后端统一允许 `NULL` + - 期:期号必填;排序与状态为数值校验 + - 奖励:`product_id` 必填;数值项均为非负或正数;`level` 合法枚举 +- 时间防错: + - 方案优先级A:数据库将 `start_time/end_time` 改为可 `NULL` 且默认 `NULL`(推荐); + - 方案优先级B:前端在未选择时默认传当前时间的 `toISOString()`; + - 后端保留 `Omit` 逻辑(`internal/service/activity/activity_create.go:29-35`),避免强行写零日期 + +### 代码结构与可维护性 +- 组件结构:每个步骤独立子组件(`ActivityStepBasic.vue`、`ActivityStepIssue.vue`、`ActivityStepRewards.vue`)+ 容器(`ActivityWizard.vue`) +- 状态管理:使用本地状态(`ref/reactive`)即可;如需跨页保持,用 `pinia` 记录向导草稿 +- API层:沿用 `web/admin/src/api/adminActivities.ts`,奖励提交前统一映射自动补齐 `name` +- 动态导入稳定性: + - 确保每个 SFC 仅一个 `