新增拍脸活动:通用模板

适用范围:LetsGo 当前 PushFaceModel 配表驱动拍脸框架。
核心原则:先分类,再查重,再配表,再写 Item;不要先改 PushFaceModel。
禁止事项:禁止手改导出的 PushFaceData_Total.txt、.pbin 或自动生成 Lua;必须修改 letsgo_common/excel/xls/P_拍脸.xlsx,再执行 Compile → Pbin。


0. 先判断属于哪种新增

0.1 类型决策表

类型 识别条件 是否改 PushFaceModel 配表改动 Lua Item 目录 额外动作
1. 主玩法/大厅/普通活动拍脸 使用已有 SceneId;featureName 为空;在大厅、回大厅、停留大厅等已有时机触发 通常不改 拍脸内容 新增一行;选择已有 sceneIds LetsGo/Content/LetsGo/Script/PushFace/Items/PushFace_<codeName>.lua 查重 ID、codeName、SceneId、同 Scene 排序与频率
2. 已有副玩法 Feature 拍脸 目标业务已有 featureName,且 Feature 包内已有拍脸目录/加载条件 通常不改 新增行并填写已有 featureName、已有 SceneId Feature/<featureName>/Script/PushFace/Items/PushFace_<codeName>.lua 对应的工程落盘目录 确认 Feature 包实际加载、路径大小写及文件可进入包体
3. 复用已有 SceneId 的新业务拍脸 新业务触发时机与已有 SceneId 完全一致 不改 新增行;复用已确认的 sceneIds 按 featureName 是否为空选择目录 验证 SceneId 的 checkFunc、触发入口和 UI 环境均满足需求
4. 新增全新 SceneId 现有触发时机和场景检查均无法表达需求 通常要改:新增/复用场景检查函数,并接入触发点;配置本身由 Scene 表加载 拍脸SceneId 新增合法行;拍脸内容 引用新 ID 按业务归属选择 先评审 SceneId、sceneKey、checkFunc、触发事件;不得只配表不接触发链路
5. 新增全新副玩法 featureName 新 Feature 尚无拍脸 Item 目录或包加载约定 通用加载器通常不改 新增行并填写新 featureName Feature/<featureName>/Script/PushFace/Items/ 建目录、确认 Feature 名与 require 路径一致、确认资源/脚本随 Feature 加载;必要时由 Feature 负责人接入
6. 打开已有 UI 目标 Window 已存在且当前场景允许直接打开 不改 新增一行 按主玩法/Feature 归属 HandleShow 调用已确认的打开 API;不要虚构窗口名或参数
7. 通过 ActivityModel 打开活动页 已有正式活动 ID,且该活动已被 ShowPushFaceActivity 支持 不改 新增行并填写已确认 activityId 通常主玩法 Items;以业务归属为准 HandleShow 返回 ShowPushFaceActivity(activityId) 的实际结果;先确认活动映射存在
8. 异步资源/数据准备 UI 依赖协议、资源包或异步数据;触发时数据可能尚未就绪 通常不改 与所属类型相同 与所属类型相同 优先在业务层准备完成后触发/重收集;NeedShow 只读就绪态,避免在其中启动不可控异步;设计失败后的队列推进
9. 纯逻辑,不打开 UI HandleShow 只执行打点、发事件、刷新等,不产生可关闭窗口 不改 与所属 Scene 相同 与所属类型相同 成功完成纯逻辑后必须 return false,使框架立即推进下一个拍脸
10. 同一配置需多次弹出 一次入队后仍有多条待弹内容,下一次推进还要处理同一配置 不改 通常无需新增字段 与所属类型相同 实现 ShouldKeepInQueue;仅在确有剩余内容时返回 true,防止死循环

PushFaceModel 当前已按 featureName 动态拼接路径:空值走 LetsGo.Script.PushFace.Items;非空走 Feature.<featureName>.Script.PushFace.Items。因此“新增 Item”本身不等于“要改 Model”。

0.2 快速决策树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
需求要在某时机执行拍脸
├─ 现有 SceneId 的触发时机、场景检查是否完全匹配?
│ ├─ 是
│ │ ├─ 是否属于副玩法 Feature?
│ │ │ ├─ 是 → 填已有 featureName → Item 放 Feature 对应目录
│ │ │ └─ 否 → featureName 留空 → Item 放主玩法 Items
│ │ └─ HandleShow 做什么?
│ │ ├─ 直接打开已有 Window → 使用“已有 UI”模板
│ │ ├─ 按活动 ID 打开 → 使用 ActivityModel 模板
│ │ ├─ 异步准备 → 先设计数据就绪与失败推进
│ │ └─ 不开 UI → 使用纯逻辑模板并 return false
│ └─ 否 → 是否只是缺少一个已有检查函数可表达的新 SceneId?
│ ├─ 是 → 评审新增 SceneId、触发点,尽量复用 checkFunc
│ └─ 否 → 新增 SceneId + 新 checkFunc + 新触发链路
└─ 同一配置一次入队是否要弹多次?
├─ 是 → 增加 ShouldKeepInQueue
└─ 否 → 不实现该函数

0.3 开工前必须拿到的需求输入

项目 必填内容 禁止做法
业务目标 为什么弹、弹什么、成功标准 只说“加一个拍脸”
触发时机 登录完成、回大厅、停留大厅、副玩法内等 凭 SceneId 数字猜时机
展示条件 活动开启、奖励可领、数据就绪、平台、等级、时间 正式代码中永久 return true
打开方式 已有 Window / ActivityModel / 业务 Model / 纯逻辑 虚构 API 或窗口名
频率 总是 / 每日一次 / 总共一次 只填数字、不查枚举含义
排序 与同 Scene 现有项的先后关系 直接照抄其他 Scene 的 sortId
归属 主玩法或明确 featureName 文件目录与 featureName 不一致
失败策略 未打开 UI 时是否立即推进 所有 HandleShow 都写死 true

1. 通用流程概览

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
需求确认
↓
类型判断(本页 §0)
↓
查重:id / codeName / Item / SceneId / 活动 ID
↓
确认 SceneId、频率、排序、featureName
↓
修改 P_拍脸.xlsx
↓
新增 PushFace_<codeName>.lua
↓
Compile → Pbin
↓
静态验证:导出结果 + require 路径 + 接口返回语义
↓
本地测试:入队 → 排序 → NeedShow → HandleShow → 关闭/立即推进
↓
回归:同 Scene 前后项、频率、平台、等级、时间、异常分支
↓
正式化:删除测试恒 true、测试 ID/日志/开关
↓
提交与可回滚检查

1.1 当前框架的三个接口

接口 作用 返回值约定
NeedShow(SceneId, activityId, id) 收集队列时判断业务条件 true 入队;false/nil 不入队。正式逻辑必须基于真实条件
HandleShow(SceneId, activityId, id) 消费队列时打开 UI 或执行逻辑 nil / true / 无返回:一般表示 UI 已打开,等待关闭事件推进;false:未打开 UI或纯逻辑完成,框架立即推进。若打开 API 自身返回有意义结果,可原样返回;不可统一写死 true
ShouldKeepInQueue(SceneId, activityId, id) 当前配置消费后是否仍保留 true 保留,下一轮仍处理;false/nil 移除。必须有可终止条件

2. 各类型的完整新增 SOP

2.1 Step 1:需求确认

复制并填完下表;未确认项写“待确认”,不要自行补值。

字段 本次值
中文业务名 <待填写>
codeName <PascalCase,待填写>
新配置 ID <查重后确定>
触发时机 <待填写>
SceneId / sceneKey <查证后填写>
featureName <空或真实 Feature 名>
activityId <仅活动类型填写>
目标 Window / 打开 API <源码确认后填写>
NeedShow 正式条件 <待填写>
频率 <Always / OneDayOnce / Once>
同 Scene 展示先后 <与哪些配置前后相邻>
平台、等级、时间 <待填写>
异步依赖 <无 / 协议 / 资源 / 业务数据>
是否多次弹出 <否 / 是,终止条件是什么>

2.2 Step 2:查重与查证

查重对象

1
2
3
4
5
6
7
1. P_拍脸.xlsx 的 id 是否占用
2. PushFaceData_Total.txt 中 id 是否已导出
3. codeName 是否已有配置
4. PushFace_<codeName>.lua 是否已存在
5. activityId 是否已被其他拍脸复用
6. SceneId 是否已有且触发语义一致
7. featureName 对应路径是否真实存在并可加载

推荐只读检索

1
2
3
rg -n "<候选ID>|<codeName>" letsgo_common/excel/Export/pbin/PushFaceData_Total.txt LetsGo/Content/LetsGo/Script
rg -n "sceneKey:|checkFunc:" letsgo_common/excel/Export/pbin/PushFaceSceneData.txt
rg -n "<目标Window或打开API>" LetsGo/Content

导出 txt 只用于核对,不是编辑入口。

2.3 Step 3:配表

编辑:

1
2
3
letsgo_common/excel/xls/P_拍脸.xlsx
├─ sheet:拍脸内容 # 新增/修改拍脸项
└─ sheet:拍脸SceneId # 仅全新 SceneId 时修改

拍脸内容 关键字段

字段 规则
id 全表唯一;必须查重;不复用本地示例或已删除业务的历史 ID,除非配置规范明确允许
sortId 当前实现先按降序排列,再从队尾消费,因此同一 Scene 内较小值先消费。正式值必须比较同 Scene 配置后确定
codeName 与文件名 PushFace_<codeName>.lua 完全一致,区分大小写风险
fequency 字段名当前就是拼写 fequency;1=Always、2=OneDayOnce、3=Once
open 总开关;关闭时不会入队
activityId 仅在业务确实需要且已有真实活动 ID 时填写
needLevel 等级前置;不要为绕过测试随意降低正式值
sceneIds 可多值;必须来自有效 Scene 配置,并与真实触发时机一致
loginPlatArr 为空表示不限制;非空必须使用项目已有平台枚举
beginTime/endTime/showPeriod 仅有明确运营时间要求时填写,并验证服务器时间语义
featureName 空值走主玩法目录;非空决定 Feature require 路径

排序必须这样查

1
2
3
4
5
6
7
选定 SceneId
→ 列出该 Scene 下所有配置的 id / codeName / sortId
→ 明确新项应在谁之前、谁之后
→ 牢记:sortId 降序排列表,但从队尾消费
→ 所以:较小 sortId 先消费
→ 相同 sortId 不应依赖稳定顺序
→ 写入正式值并记录比较依据

2.4 Step 4:确定 Item 路径

1
2
3
4
5
6
7
featureName 为空:
LetsGo/Content/LetsGo/Script/PushFace/Items/PushFace_<codeName>.lua
require: LetsGo.Script.PushFace.Items.PushFace_<codeName>

featureName = <FeatureName>:
Feature/<FeatureName>/Script/PushFace/Items/PushFace_<codeName>.lua
require: Feature.<FeatureName>.Script.PushFace.Items.PushFace_<codeName>

检查项:

  • 配表 codeName 与文件名完全一致。
  • 配表 featureName 与 Feature 路径段完全一致。
  • 文件返回 table,且至少有 HandleShow。
  • NeedShow 不存在时当前收集逻辑不会入队,因此正常 Item 应明确实现 NeedShow。
  • 不要把 Helper 类误放成同名 Item。

2.5 Step 5:编写 Item——按类型选模板

模板 A:打开已有 UI

1
2
3
4
5
6
7
8
9
10
11
12
return {
NeedShow = function(SceneId, activityId, id)
-- TODO:替换为已确认的正式业务条件
return <BusinessModel>:<CanShowMethod>(activityId)
end,

HandleShow = function(SceneId, activityId, id)
_MOE.Models.PushFaceModel:AddPushFaceItemCount()
-- OpenWindow 当前通常无有意义返回值;已打开后可不显式 return
_MOE.UIManager:OpenWindow(<已确认的_WindowName>, <已确认的参数>)
end
}

模板 B:通过 ActivityModel 打开活动页

1
2
3
4
5
6
7
8
9
10
11
return {
NeedShow = function(_, activityId)
return <已确认的正式活动条件>(activityId)
end,

HandleShow = function(_, activityId)
_MOE.Models.PushFaceModel:AddPushFaceItemCount()
-- 该 API 找不到活动映射时会返回 false;成功打开时通常无返回
return _MOE.Models.ActivityModel:ShowPushFaceActivity(activityId)
end
}

模板 C:已有 Feature 内的 Item

1
2
3
4
5
6
7
8
9
10
11
12
13
return {
NeedShow = function(SceneId, activityId, id)
return <Feature业务模型>:<CanShow>(activityId)
end,

HandleShow = function(SceneId, activityId, id)
local result = <Feature业务模型>:<OpenPushFace>(activityId)
if result ~= false then
_MOE.Models.PushFaceModel:AddPushFaceItemCount()
end
return result
end
}

模板 D:异步资源/数据准备

推荐链路:

1
2
3
4
5
业务数据/资源开始准备
→ 准备成功并缓存“已就绪”状态
→ 由业务事件触发对应 SceneId 的收集
→ NeedShow 只读取“已就绪 + 业务可展示”
→ HandleShow 打开 UI
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
return {
NeedShow = function(SceneId, activityId, id)
local model = <BusinessModel>
return model:IsDataReady() and model:CanShowPushFace(activityId)
end,

HandleShow = function(SceneId, activityId, id)
local model = <BusinessModel>
if not model:IsDataReady() then
return false -- 未打开 UI,立即推进,避免卡队列
end
local result = model:OpenPushFace(activityId)
if result ~= false then
_MOE.Models.PushFaceModel:AddPushFaceItemCount()
end
return result
end
}

不建议在 NeedShow 内启动异步请求。若必须在 HandleShow 内异步,必须保证最终会打开可触发关闭事件的 UI,或显式补偿推进;否则返回 nil 会让队列等待一个不存在的关闭事件。

模板 E:纯逻辑,不打开 UI

1
2
3
4
5
6
7
8
9
10
return {
NeedShow = function(SceneId, activityId, id)
return <正式条件>
end,

HandleShow = function(SceneId, activityId, id)
<执行纯逻辑>
return false -- 没有 UI 可关闭,立即推进下一个拍脸
end
}

模板 F:同一配置多次弹出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
return {
NeedShow = function(SceneId, activityId, id)
return <BusinessModel>:HasPendingPushFace()
end,

ShouldKeepInQueue = function(SceneId, activityId, id)
-- HandleShow 前调用;必须准确表达“本次之后仍需再次处理”所需状态
return <BusinessModel>:ShouldKeepPushFaceInQueue()
end,

HandleShow = function(SceneId, activityId, id)
local result = <BusinessModel>:ShowNextPushFace()
if result ~= false then
_MOE.Models.PushFaceModel:AddPushFaceItemCount()
end
return result
end
}

风险:ShouldKeepInQueue 永久为 true 会使同一项反复被处理;必须覆盖“最后一条”“数据异常”“打开失败”三种终止分支。

本地测试条件(只允许临时使用)

1
2
3
NeedShow = function()
return true
end

NeedShow 恒 true 只能用于本地链路测试;提交前必须删除,或替换为已评审的正式业务条件。

2.6 Step 6:导表

1
2
3
4
P_拍脸.xlsx
→ 表工具 Compile
→ Pbin
→ 生成/刷新 PushFaceData_Total、PushFaceSceneData 等产物

必须遵守:

  1. 只编辑 letsgo_common/excel/xls/P_拍脸.xlsx。
  2. 使用团队现行表工具执行 Compile → Pbin。
  3. 不手改:
    • letsgo_common/excel/Export/pbin/PushFaceData_Total.txt
    • letsgo_common/excel/client/data/PushFaceData_Total.txt
    • 对应 .pbin
  4. 导出后用 txt 核对结果;若不一致,回到 Excel 或编译流程修正。

2.7 Step 7:静态验证

检查 通过标准
ID Excel 与导出结果中唯一;不是已占用示例 ID
codeName 配表、文件名、日志标签完全一致
require 路径 按 featureName 规则可定位到返回 table 的 Lua 文件
接口 有 NeedShow、HandleShow;多弹才有 ShouldKeepInQueue
SceneId PushFaceSceneData 中为有效行,sceneKey/checkFunc 非空且函数存在
频率 fequency 数字与正式需求一致
排序 已与同 Scene 配置比较;较小值会先消费
返回值 打开 UI 不统一写死 true;未打开 UI/纯逻辑完成返回 false
测试代码 恒 true、测试 ID、临时窗口、超量日志均有清理计划

推荐检索:

1
2
rg -n "<新ID>|<codeName>" letsgo_common/excel/Export/pbin/PushFaceData_Total.txt LetsGo/Content
rg -n "return true|return false|HandleShow|ShouldKeepInQueue" <新增Item绝对路径>

2.8 Step 8:本地测试

最小测试矩阵

场景 操作 预期
条件满足 进入目标触发时机 配置入队并按预期顺序消费
条件不满足 关闭活动/清空奖励/模拟数据未就绪 NeedShow=false,不弹
打开成功 触发 HandleShow 目标 UI 唯一打开;关闭后下一个拍脸推进
打开失败 让业务打开 API 返回 false 当前项不阻塞,立即推进下一项
同 Scene 多项 同时满足前后相邻配置 较小 sortId 先消费,正式顺序符合需求
频率 重复进入 Scene、跨日或清缓存后测试 行为符合 1/2/3 频率定义
平台/等级/时间 命中与不命中边界 配表前置正确拦截
多次弹出 准备 0/1/多条待弹数据 队列保留次数正确,最终退出

关键日志/断点

1
2
3
4
5
6
7
PushFaceModel:TriggerPushFaceListBySceneId
PushFaceModel:CollectPushFaceListBySceneId
PushFaceModel:_checkPreConditions
PushFaceModel:_getConfigByCodeName
PushFaceModel:HandleShowByList
PushFaceModel:TryShowNextAutoShowWindow
业务 Item 的 NeedShow / HandleShow / ShouldKeepInQueue

2.9 Step 9:回归

  • 同一 Scene 的前一项、后一项仍可展示。
  • 目标 UI 关闭后队列继续推进。
  • 强引导、Loading、回流屏蔽、基础资源未就绪时不会异常弹出。
  • 切场景、清层、关闭全部窗口后不会残留旧队列。
  • featureName 为空与非空的加载路径互不污染。
  • ActivityModel 类型在活动映射缺失时返回 false,不会卡住。
  • ShouldKeepInQueue 不会死循环。
  • 测试恒 true 已移除。

2.10 Step 10:提交与回滚

建议提交内容

1
2
3
4
5
[必需] P_拍脸.xlsx
[必需] Compile → Pbin 产生的团队规范要求提交的配置产物
[必需] PushFace_<codeName>.lua
[条件] 新 SceneId 的触发/检查代码
[条件] 新 Feature 的目录或接入文件

不应出现

1
2
3
4
5
手改的 txt/pbin
未说明来源的真实 ID
NeedShow 永久 return true
临时测试窗口或测试 codeName
与本需求无关的 PushFaceModel 重构

回滚顺序

1
2
3
4
先关闭配置 open(若发布流程允许紧急止血)
→ 回滚 Excel 源配置并重新 Compile → Pbin
→ 回滚 Item/Scene 接入代码
→ 验证旧配置、旧队列顺序和原活动恢复

3. 完整运行时链路

3.1 收集与消费

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
业务事件/场景入口
→ TriggerPushFaceListBySceneId(SceneId, ClearOtherList)
→ PushFaceTable.LoadWebConfig(callback)
→ CollectPushFaceListBySceneId(SceneId)
→ GetPushfaceConfigBySceneId(SceneId)
→ _checkPreConditions
├─ open
├─ 队列查重
├─ needLevel
├─ loginPlatArr
└─ CheckCanShow(时间范围)
→ _getConfigByCodeName(codeName, featureName)
├─ 主玩法:LetsGo.Script.PushFace.Items.PushFace_<codeName>
└─ Feature:Feature.<featureName>.Script.PushFace.Items.PushFace_<codeName>
→ NeedShow(SceneId, activityId, id)
→ _checkFrequencyLimit
→ table.insert(PushFaceList, config)
→ sortId 降序排序
→ m_PushFaceListMap[SceneId] = PushFaceList
→ TryShowNextAutoShowWindow()
→ 按 SceneId ProcessOrder 检查场景
→ HandleShowByList(SceneId)
→ 读取队尾(最小 sortId)
→ ShouldKeepInQueue(可选)
→ HandleShow
→ SavePushFaceTime
├─ 返回 false → 立即 TryShowNextAutoShowWindow
└─ nil/true/无返回 → 等 UI 关闭事件推进

3.2 UI 关闭推进

1
2
3
4
5
目标 UI 关闭
→ ON_CLOSE_WINDOW_IMMEDIATELY
→ PushFaceModel:OnCloseWindowImmediately
→ TryShowNextAutoShowWindow
→ 下一个 Scene/下一个队尾 Item

3.3 两种顺序不要混淆

顺序 当前实现 影响
Scene 处理顺序 有效 SceneId 加入 ProcessOrder 后按 ID 升序 不同 Scene 队列的扫描顺序
同 Scene Item 顺序 sortId 降序排列,队尾消费 较小 sortId 先消费

4. SceneId、频率与排序的查证方法

4.1 SceneId

查证源:

1
2
3
源表:letsgo_common/excel/xls/P_拍脸.xlsx / 拍脸SceneId
只读导出:letsgo_common/excel/Export/pbin/PushFaceSceneData.txt
运行代码:LetsGo/Content/LetsGo/Script/PushFace/PushFaceModel.lua

必须同时确认:

  1. id > 0。
  2. sceneKey 非空。
  3. checkFunc 非空。
  4. PushFaceModel[checkFunc] 是函数。
  5. 有真实入口调用 TriggerPushFaceListBySceneId。
  6. 检查函数允许目标 UI 所需的当前场景与窗口状态。

不能只看数字名称。当前初始化会跳过无效 Scene 行;“表里有 ID”不等于“运行时有效”。

4.2 频率

当前枚举事实:

fequency 枚举 含义 保存/检查行为
1 Always 总是 仍受“今日不再显示”等其他条件影响
2 OneDayOnce 每日一次 按当天展示时间拦截
3 Once 总共一次 有历史展示时间即拦截

测试频率时必须区分:配表频率缓存、业务 NeedShow、用户“今日不再显示”状态。

4.3 排序

当前源码事实:

1
2
3
table.sort(PushFaceList, function(a, b) return a.sortId > b.sortId end)
local CheckListNum = #CheckList
local CheckCell = CheckList[CheckListNum]

结论:

1
降序排列 + 队尾消费 = 较小 sortId 先消费

正式配置流程:导出同 Scene 全量 → 明确相邻项 → 再定值。本文不提供可直接照抄的正式 sortId。


5. 常见问题与排查

现象 优先检查 修复方向
完全不进 Item Scene 未触发、Scene 行无效、open=false、等级/平台/时间不满足 从 Trigger、Collect 和 _checkPreConditions 逐层断点
日志提示加载失败 codeName/featureName/路径不一致,文件未进包 按动态 require 规则核对
NeedShow=true 仍不弹 频率限制、同 ID 已在队列、CheckCanNextShow 被拦截 查频率缓存、Loading、强引导、回流屏蔽、资源状态
当前项后不继续 HandleShow 未打开 UI 却返回 nil/true 失败或纯逻辑分支返回 false
UI 打开后立刻串弹 错误返回 false,或目标 UI 未进入统一关闭链路 成功打开时返回打开 API 的真实结果或不返回
顺序与直觉相反 把 sortId 当成越大越先 牢记降序排、队尾取,较小先消费
每次都弹 NeedShow 恒 true 或 fequency=1 替换正式条件,复核频率需求
永久处理同一项 ShouldKeepInQueue 始终 true 修正剩余数量判断与失败终止条件
ActivityModel 不打开 activityId 不在其活动映射中 先查映射;该 API 找不到映射会返回 false
改 txt 后本地有效、重导失效 修改了导出产物 回到 P_拍脸.xlsx 修改并 Compile → Pbin

6. 验收 Checklist

类型与需求

  • 已完成 §0 类型判断。
  • 已确认是复用 SceneId 还是新增 SceneId。
  • 已确认主玩法还是 Feature,以及 featureName。
  • 已确认打开已有 UI、ActivityModel、异步或纯逻辑。
  • 已确认正式 NeedShow 条件和失败推进方式。

配表

  • 新 ID 已在 Excel 和导出结果查重。
  • codeName 与文件名完全一致。
  • sceneIds 有效且触发语义匹配。
  • fequency 与需求一致。
  • sortId 已比较同 Scene 配置;没有照抄无关项。
  • 平台、等级、时间、活动 ID 均来自真实需求。
  • 修改的是 P_拍脸.xlsx,不是 txt/pbin。
  • 已执行 Compile → Pbin 并核对导出结果。

Item

  • 返回 table,包含 NeedShow、HandleShow。
  • 成功打开 UI 时未无脑写死 true。
  • 打开 API 有意义返回值时已原样返回。
  • 未打开 UI/纯逻辑完成时返回 false。
  • 多次弹出才实现 ShouldKeepInQueue,且有终止条件。
  • AddPushFaceItemCount 仅在按业务定义确实展示时调用。
  • NeedShow 恒 true 已删除或替换。

测试与提交

  • 命中/不命中条件均测试。
  • 同 Scene 前后顺序测试。
  • UI 关闭推进和打开失败立即推进均测试。
  • 频率、平台、等级、时间边界测试。
  • 原有同 Scene 拍脸回归通过。
  • 提交中无手改导出物、临时 ID、测试窗口和恒 true。
  • 已准备可执行回滚方案。

7. 风险与依据

7.1 主要风险

风险 后果 控制措施
错用 SceneId 错时机弹出或永不触发 同时查 Scene 表、checkFunc、触发入口
排序理解反了 抢占其他重要拍脸 同 Scene 对比;按“降序排、队尾取”验收
HandleShow 返回值错误 队列卡死或串弹 按是否真正打开 UI 决定;传播有意义结果
永久 NeedShow=true 正式环境反复弹 仅本地临时使用,提交前强制扫描
手改导出物 重导覆盖、环境不一致 只改 Excel,Compile → Pbin
Feature 路径错误 动态 require 失败 featureName、目录、文件名三方一致
异步未设计完成 等待不存在的关闭事件 数据先准备;失败返回 false;必要时补偿触发
ShouldKeepInQueue 无终止 死循环 剩余数量、失败、异常均能返回 false

7.2 当前源码依据

事实 已核对位置
主玩法与 Feature 的动态 Item 路径 LetsGo/Content/LetsGo/Script/PushFace/PushFaceModel.lua:_getConfigBasePath、_getConfigByCodeName
收集前置、NeedShow、频率检查 同文件:CollectPushFaceListBySceneId
sortId 降序、队尾消费 同文件:CollectPushFaceListBySceneId、HandleShowByList
HandleShow=false 立即推进;nil/true/无返回等待关闭 同文件:HandleShowByList
ShouldKeepInQueue 的保留时机 同文件:HandleShowByList;实例可参考 PushFace_PandoraNotice.lua、PushFace_PartnerReturnInvited.lua
频率枚举 1/2/3 同文件:Enum_PushFrequency
SceneId 来自配置且会校验 sceneKey/checkFunc 同文件:InitSceneIdConfig、_isValidSceneConfigRow
ActivityModel 打开成功通常无返回,找不到映射返回 false LetsGo/Content/Feature/System/Script/System/Activity/Public/Model/ActivityModel_MiscActivity.lua
当前配置导出来源是 P_拍脸.xlsx letsgo_common/excel/Export/pbin/PushFaceData_Total.txt、PushFaceSceneData.txt 文件头

本文不给出可直接用于正式环境的新 ID、SceneId、sortId、活动 ID或目标 Window;这些值必须在本次需求和同 Scene 配置中查证后填写。


本文从 LearnByCompany 原始文档 自动同步。