外观
D15 · 福利副本扫荡控件恢复 + 不占共享扫荡池
本轮来源:2026-09-23 策划报障「福利副本的扫荡好像不见了」,随即追加定案「福利副本扫荡不占公共次数」。 前半件是前端误隐藏(后端功能一直在,纯 UI 回归);后半件是玩法口径变更(原实现确与材料副本抢额度)。
1. 差异现象
现象一:控件不可见
- 福利副本详情页看不到扫荡次数输入框、「指定扫荡」、「扫全部」三者中的任何一个,而其余可扫荡副本(进阶石/坐骑/翅膀/灵兽/肉身/藏经)都正常显示。
- 同一页的「今日次数」卡里却写着「首次挑战胜利后解锁该副本的指定扫荡」——提示在,被提示的控件却不存在。
- 大厅福利副本卡片上也有「指定扫荡:可扫 N 次」的高亮读数(
sweepHighlight(limit)),进详情页后无处可点。
现象二:额度与材料副本互相挤占
- 福利副本每日 100 次,指定扫荡走的是材料副本共享扫荡池(虚拟道具 5199,按材料副本总次数 ×80% 派生): 扫一整天福利副本会先把共享池吃掉 100 次,材料副本(进阶石/坐骑/翅膀/灵兽/肉身/藏经)当天可扫次数同比减少。
- 反过来,材料副本把共享池扫空后,福利副本即使自身还有 99 次剩余,也会被
7410 扫荡次数不能超过今日共享扫荡剩余次数拦住(前端activeSweepRemaining = min(副本剩余, 共享池剩余)直接显示为 0,按钮置灰)。
2. 代码事实(文件:行号 + 数值)
后端从来支持福利副本扫荡(现象一是纯前端回归)
emberfall-server/src/main/java/com/emberfall/domain/DungeonType.java:75——WELFARE("福利副本", ..., 100, 0, 0, new int[]{}, **true**, 4001L, "福利点券", 1, 0.0D, 0.0D, 0):第 9 个构造参数即sweepable = true,每日基础 100 次。service/DungeonLimitService.java:517——resolveSweepTarget:mapId == MapService.WELFARE_MAP_ID ? DungeonType.WELFARE : MapService.limitedDungeonType(mapId),mapId=0 认福利副本。service/DungeonLimitService.java:584-590——grantWelfareSweepReward:按ShopBalanceRules.ticketsPerChallenge(privilege) × consumed发福利点券,是专用结算分支(非材料路径)。service/SettlementService.java:899-906—— 福利图结算分支内注释「福利副本的每次点击都是主动挑战(无挂机进入路径),真实击杀即首通(扫荡门禁用)」并调用recordDungeonFirstClear(playerId, mapId),即打一局就满足 7414 门禁。service/DungeonLimitService.java:226——hasSweepableTarget的case WELFARE -> clearedMapIds.contains(MapService.WELFARE_MAP_ID)。- 单测
src/test/java/com/emberfall/service/FirstClearSweepTest.java:147——assertThat(service.hasSweepableTarget(PLAYER_ID, DungeonType.WELFARE)).isTrue();(首通表含 mapId 0 时)。 - 前端隐藏点(改动前):
emberfall-web/src/views/MapView.vue详情页扫荡行的input.monster-sweep-input、两个button.sweep-btn各带v-if="!detailMap.welfare";引入提交e53c59c5(2026-09-18「副本二级UI重构」),当时 CSS 注释写「扫荡控件只在非福利副本出现,列数随内容伸缩,避免留下空列」。 - 前端读数(改动前):同一文件
sweepHighlight(limit)用Math.min(limit.remaining, limit.sweepQuotaRemaining)生成大厅分类卡的「指定扫荡:可扫 N 次」,且remaining > 0而池子为 0 时刻意显示「额度已用完」——福利副本同样吃这套读数。
共享池口径(现象二)
service/DungeonLimitService.java:44-46——SWEEP_COVERAGE_PERCENT = 80、SWEEP_QUOTA_ITEM_ID = 5_199L、SWEEP_QUOTA_ITEM_NAME = "每日扫荡次数"。- (改动前)
sweep()对所有sweepable副本统一执行:读getSweepQuota(playerId)→count > remaining抛7410→ 扣consumeDailyLimit(SWEEP_QUOTA_ITEM_ID, ...),失败抛7411。福利副本无豁免。 - (改动前)
calculateSweepQuotaTotal()的累加条件type.isSweepable() && type.isMaterialDungeon()已把福利副本排除在池子总量之外(isMaterialDungeon()对 WELFARE 返回 false),于是形成「算池子不含福利、扣池子却扣福利」的单向错配。 service/DungeonLimitService.java:57-63——SWEEP_ALL_PRIORITY只有ADVANCEMENT_STONE / BODY_REFINE / MOUNT / WING / PET / MENTAL_METHOD,一键扫荡本就不含福利副本(与docs/扫荡系统与副本限次重构设计.md§16.2 一致),本次未改。
3. 旧文档说法
docs/扫荡系统与副本限次重构设计.md§16.2 原文:「扫荡同时扣减副本次数和共享扫荡次数,单次上限为min(副本剩余次数, 共享扫荡剩余次数)」——未区分副本类型,字面上福利副本也受共享池约束;同节另一条只写「福利副本不纳入一键扫荡优先列表」。§13.2 另注「福利副本保持 100 次/天不变,不纳入本次重构」。docs/18_游戏核心系统重构落地设计文档.md:11、docs/23_全局数值重构设计_300日版本.md:764—— 只登记共享池 ID5199与总量(560/1680 或 605/1815),池子总量本就不含福利副本,数值无需变动。- 设定站
emberfall-docs/docs/settings/combat/map-idle.md§5 —— 仅有「福利副本:每日 100 次挑战额度,额度用完当日无法再进入」,完全没有扫荡与共享池的描述(福利副本此前在设定站属「可挂机地图」,扫荡口径只活在设计文档里)。 - 设定站
tech/implementations/combat/map-idle.md§5「已知差异」原为「无」。
4. 影响范围
- 玩家影响(正向):
- 福利副本重新出现「指定扫荡 / 扫全部」,0点击成本拿点券的日常路径恢复;未首通时按钮置灰并提示先打一局。
- 福利副本扫荡不再消耗共享扫荡池,于是每日可稳定「福利 100 次全扫 + 材料副本按共享池额度扫」——两边互不挤占。原先材料副本扫空共享池(免费 404 次池、满配 1452 次池)后福利副本只能手动打满 100 次的场景消失。
- 产出影响:无。福利点券单次产出口径(
ticketsPerChallenge)完全未动,福利副本日上限仍是 100 次;变的只是额度来自哪里(自身次数池 vs 共享池),不产生额外点券。材料副本的共享池总量也未变(calculateSweepQuotaTotal的累加结果与改动前逐位相同,因为福利副本本来就未被累加),所以材料副本的扫荡能力一点没少。 - 契约影响:
POST /api/dungeon/sweep请求/响应结构不变;DungeonLimitDto字段不变(福利副本仍下发sweepQuotaTotal/Produced/Remaining,只是前端详情页不再展示这一行)。错误码 7410/7411 对福利副本不再触发,对材料副本行为不变。 - 数据库影响:无迁移。
daily_limit_pool中 5199 的存量数据不变,福利副本改后不再写该池。
5. 处理结论
- 恢复控件:
emberfall-web/src/views/MapView.vue删掉扫荡行三处v-if="!detailMap.welfare",并补注释说明福利副本为何必须有扫荡(后端 sweepable + 真实击杀即首通),避免后续 UI 重构再被顺手隐藏;同步修正原 CSS 注释。 - 口径单点化:
DungeonType.java:195-197新增usesSharedSweepQuota()(sweepable && isMaterialDungeon()),把「谁吃共享池」定义在一处。 - 后端豁免:
DungeonLimitService.java:161的calculateSweepQuotaTotal()改用该判定(结果与旧条件逐位相同,纯口径收敛);sweep()在:305-320改为boolean sharedQuota = type.usesSharedSweepQuota(),为 false 时不读池、不校验 7410、不扣 5199,自身次数照扣(7404/7405/7406 不变)。 - 前端额度与文案:
MapView.vue新增口径函数usesSharedSweepQuota(categoryKey)(:346-348,全局唯一字面判断'welfare'的地方);activeSweepRemaining(:536-543)对福利副本只取limit.remaining(不再min共享池);大厅分类卡的「指定扫荡」读数sweepHighlight(:350-362)补sharedQuota形参、调用处(:414)传分类口径——否则大厅仍会按共享池显示「可扫 N 次」甚至误报「额度已用完」;详情页limit-card-sweep(:1044-1048)对福利副本显示「扫荡不占共享额度」取代「共享扫荡 X/Y」(避免误导成会挤占材料副本),该标记加了正向色样式(:2185-2187);扫荡控件恢复于:1165-1194。 - 回归测试:
DungeonLimitServiceTest新增welfareSweepDoesNotConsumeSharedSweepQuota——一次抹 100 次必须成功,并verify(never)断言共享池的 5 参getDailyLimit与 5199 的consumeDailyLimit全程未被触碰;同文件材料副本用例的池子总量镜像口径改用usesSharedSweepQuota()。emberfall-web/tests/dungeon-redesign.spec.ts福利副本用例补断言:扫荡行存在、两个按钮、未首通置灰 + 提示文案、免额标记存在且不再出现「共享扫荡」。 - 文档同步:
docs/扫荡系统与副本限次重构设计.md§16.2 增加「福利副本例外(2026-09-23 定案)」条目,写明口径与usesSharedSweepQuota()的单点定义;设定站settings/combat/map-idle.md§5 补扫荡口径说明并挂<DiffNote id="d15" />;实现页tech/implementations/combat/map-idle.md§5 登记本条。 - 未改动项:一键扫荡继续不含福利副本(设计如此);福利副本的首通徽标与「挑战首通」面板继续隐藏(福利副本无独立首通挑战入口,打一局即首通);福利副本挑战次数仍为固定 100 次、不吃月卡/VIP/冠名次数加成。