系列第五篇。上一篇讲数据怎么算出来(数据体系),这一篇讲人怎么用:运营、投放、客服每天实际在哪个界面上干活。

依然四段:业务背景 → 方案设计 → 关键取舍 → 真实经验。


一、业务背景

后台是发行平台的”驾驶舱”

前面几章讲的东西——账号、支付、包、数据——最终都要落在一个后台界面上被人操作:

  • 运营:开游戏、配开关、上主包、建推广包、发落地页
  • 投放:看各计划的消耗与回收,决定加预算还是停
  • 客服:查某个玩家的订单和账号状态
  • 管理:分配权限、看谁改了什么

核心矛盾

一套后台要服务好几种角色,他们的权限、数据范围、关注模块完全不同;但底下的数据和接口只有一套。

这带来两个必须回答的问题:

  1. 功能可见性:谁能看到哪些菜单、点得动哪些按钮?
  2. 数据可见性:同一个报表,A 运营只能看自己的游戏,B 运营能看全部——这个差别在哪一层实现?

还有一个容易被忽略的前提:后台是内部系统,但它改的是线上数据。一次误操作(关掉某游戏的支付、改错支付配置)就是线上事故。所以后台的”权限”和”审计”不是管理需求,是安全需求。


二、方案设计

2.1 功能地图:按角色切,而不是按技术切

后台模块按”谁在用”组织:

flowchart TB
    A["发行配置<br/>游戏 / 推广包 / 素材 / 落地页"] --> X["发行后台"]
    B["数据报表<br/>日报 / 留存 / LTV / 投放 ROI / 三方对比"] --> X
    C["业务查询<br/>订单 / 平台用户 / 游戏用户 / 事件流水"] --> X
    D["系统管理<br/>管理员 / 角色 / 权限 / 审计日志"] --> X
  • 发行配置:运营的主战场,对应第二、三章讲的那些对象
  • 数据报表:投放的主战场,对应第四章的数据体系
  • 业务查询:客服的主战场。订单、平台用户、游戏用户、激活/登录/创角事件流水——这一类页面看起来最”没技术含量”,但客服的响应速度全靠它们
  • 系统管理:权限和审计,下面单独讲

2.2 权限模型:三层结构,只有第三层是真权限

权限设计成三层:

层级 内容 是否可分配
第一层 模块 / 集合 否(仅用于组织)
第二层 页面 / 资源 否(仅用于组织)
第三层 动作 是——这才是真正的权限

为什么前两层不给分配?因为”能进这个页面”本身没有意义——有意义的是”能在这个页面里做什么”(查看、创建、修改、导出)。

所以:

  • 登录后返回的扁平权限清单里只有第三层动作
  • 菜单树由第一、二层派生,但只有用户在该页面下拥有至少一个动作权限时,这个菜单才显示

第二条是经验性的修正。早期的实现是”有页面权限就显示菜单”,结果是:用户看得见菜单、点进去 403——这种”看得见进不去”的体验,比菜单不显示更让人困惑,还会产生”系统坏了”的反馈。

2.3 权限名从路由自动派生

动作权限的命名不手工登记,而是从后台路由自动派生:

/{资源}:{动作}

比如一个推广包列表接口,对应的权限名就是 资源:list;创建就是 资源:create。

好处是”约定优于配置”:新增一个接口,权限名自动就有,不需要同步维护一份权限清单;权限清单和接口永远对得上,不会出现”接口有了但权限忘了加”。

代价必须记住:路由即权限名。改路由 = 改权限名,而生产环境的权限数据是人工维护的——所以调整路由前,必须先把角色上的旧权限迁移掉,否则部署完线上直接 403。

这不是理论风险,是实际发生过的部署事故(后面”真实经验”里展开)。

另外,共享的选项类接口(多个页面共用的筛选下拉数据)保留兼容别名——同一个选项接口可以被多个页面的权限名访问。这类”小妥协”是必要的,否则要么复制接口,要么把权限颗粒度搞乱。

2.4 数据范围:和权限是两个问题

这是后台设计里最值得单独说清楚的一层:

动作权限回答”能不能调这个接口”,数据范围回答”能看/能改哪些游戏和推广包”。

两者是正交的:

  • 有”看报表”的权限,但没被授权任何游戏 → 报表能打开,数据是空的
  • 有”改游戏配置”的权限,但只被授权了 3 个游戏 → 只能改这 3 个

实现上是两张授权表:管理员 ↔ 游戏、管理员 ↔ 推广包。三条规则:

  1. 继承:授权一个主游戏,自动可见它下面的子游戏(层级继承)——因为实际业务里游戏是树状结构(主游戏 → 各平台子游戏)
  2. 角色级覆盖:特定角色可以带”全游戏”“全推广包”标记,直接放开
  3. 超管绕过:系统管理员不受权限和数据范围约束(但要接受审计)

把数据范围独立成一层,而不是塞进权限模型里,好处是两边都可以单独演进:权限体系继续按”动作”细化,数据范围继续按”游戏/推广包”扩展,互不干扰。

2.5 对账:每天的第一件事

数据管道是异步的(业务库 → 日志表 → 队列 → 分析库事实表),任何一环都可能丢数据:进程崩了、队列堆积、同步延迟。

对”收入”这种数据来说,丢一笔就是错。所以设计了一个每日订单对账任务:

flowchart TB
    T["每天 00:00<br/>第一件事"] --> Q1["查业务库订单<br/>(待支付 + 已支付)"]
    Q1 --> Q2["查分析库事实表<br/>这批订单是否存在"]
    Q2 --> Q3{"有缺失?"}
    Q3 -->|有| Q4["重新投递到日志管道<br/>(走正常同步链路)"]
    Q3 -->|无| Q5["完成"]
    Q4 --> Q6["幂等保证:<br/>同一事件 ID 重复写入无副作用"]

三个设计要点:

① 顺序是设计的一部分

对账排在 00:00,必须在留存计算(凌晨)和 LTV 计算之前。因为留存和 LTV 都依赖完整的订单数据——如果对账排在分析任务后面,那这一天的报表就是建立在残缺数据上的,而且它不会报错,数字只是偏低。 这种”安静的错误”最难发现。

② 修复方式不是直接写目标表,而是”重走正常链路”

发现缺失后,不是直接往分析库里补一行,而是把订单重新投递到日志管道,让它走和其他数据完全一样的同步路径。

为什么?因为直接写目标表意味着两条写入路径,将来任何一条路径的字段口径变了,就会出现”正常写入的数据和对账补的数据长得不一样”。修复走主链路,是数据一致性的基本纪律。

③ 幂等是前提

重发必须安全,所以日志管道有唯一事件 ID + upsert 语义:重复投递不会产生重复数据。没有这个前提,对账任务本身就成了风险源。

另外,对账命令支持 dry-run(只报告不修改)和按日期回补(历史数据也能修)——“能安全地重放”,是这类补偿任务最重要的工程属性。

2.6 审计日志:内部系统也要留痕

所有后台操作统一记审计日志:操作路由、请求参数、操作人、IP、User-Agent、响应状态码。

关键在于用中间件统一记录,而不是靠业务代码自觉:

  • 业务代码写日志,一定会漏(新接口忘了写、异常分支没覆盖)
  • 中间件挂在路由层,新增接口自动被覆盖,不需要任何额外工作

审计日志的价值只在出事的时候体现:某个游戏支付被关了一天,第一个问题永远是”谁在什么时候改的”。如果系统答不出来,那这个系统就没有可治理性。

2.7 事件查询页:把事实表变成可检索的

客服和运营最常用的页面之一,是各类事件流水查询:激活事件、登录事件、创角事件、订单。

这些页面直接读分析库的事实表(明细),但展示字段用权威表覆盖:

  • 事实行提供”发生了什么”(时间、设备、归因、金额)
  • 权威表提供”现在长什么样”(角色名、等级、VIP——这些会变,事实行里存的是当时的值)

这就是上一章”明细用于分析、权威用于判定“那条原则在界面上的落地。

2.8 三方数据对比

发行经常要同时看”我们算的数据”和”合作方/渠道算的数据”。

做法是把合作方的产品列表和每日报表同步进来(按产品维度、渠道维度两种粒度),做成一个对比报表页:

  • 支持按日期范围、产品、渠道筛选
  • 支持”按渠道分组 / 按产品分组”切换
  • 第一页带一行汇总行(所有指标求和),方便先看总量再看结构

这个页面的真正用途不是”看数据”,是”找差异”:两边数字差得多的地方,就是归因、口径或结算需要核对的地方。


三、关键取舍

决策 选择 放弃的方案 理由
权限模型 自建三层动作模型 直接用现成 RBAC 包 需要”路由派生 + 数据范围”两层绑定,现成包改造成本更高
权限命名 从路由自动派生 手工登记权限清单 接口与权限永远对得上;代价是改路由要同步权限数据
菜单可见性 有动作权限才显示 有页面权限就显示 避免”看得见、点不进”的 403 体验
数据范围 独立一层(游戏 / 推广包授权) 并入权限模型 两个正交问题,混在一起两边都无法独立演进
范围继承 主游戏授权自动继承子游戏 逐个游戏授权 游戏天然是树,逐个授权会漏、会累
审计日志 中间件统一记录 业务代码各自记录 业务代码一定会漏;中间件自动覆盖新接口
数据一致性 每日对账 + 重走主链路 直接补写目标表 双写路径会导致口径分叉;修复必须走同一条路
配置表删除 不提供删除,只可停用 软删除 / 物理删除 推广包是历史归因和订单的锚点,删了就断链
对账时机 分析任务之前(00:00 第一位) 任意时间 顺序错了,报表会安静地算错
权限数据维护 生产手工维护 随代码发布自动同步 生产权限数据是业务资产,不能跟着部署被覆盖(代价:删路由要先改数据)

四、真实经验

1. 菜单能看见,点进去 403

权限体系上线后收到过”系统坏了”的反馈:菜单在,点进去报无权限。

根因:菜单树从页面节点派生,而页面节点不是真权限——用户的角色里有页面节点,但没有任何一个动作权限。

修法就是前面说的:菜单可见性由”该页面下是否有任意动作权限”决定。

这个 bug 的教训是通用的:可见性和可操作性必须由同一个判据驱动。 只要两者用了不同的数据源,就一定会出现”看得见但用不了”或者”能用但看不见”。

2. 改路由名,等于改权限名

权限名从路由派生,所以一次”看起来很安全”的路由重命名(比如把两个报表接口合并成一个),上线后相关角色的权限全部失效——因为权限名从旧 URI 派生,新 URI 派生出的权限名在角色数据里不存在。

修法有两步:

  1. 部署前先把生产角色数据从旧权限迁到新权限
  2. 共享的选项类接口保留兼容别名,避免多个页面互相牵连

“权限数据手工维护”这个选择的代价就在这里。 它不是缺陷,是权衡:换来的是”部署不会覆盖生产权限配置”。但代价必须写进部署清单,否则每次动路由都要重新踩一遍。

3. 数据范围问题会被误判成权限问题

客服反馈”订单查不到”,第一反应是权限配错了。实际是:动作权限有,但该管理员没有被授权这个游戏的订单范围。

这两类问题的排查路径完全不同:

  • 权限问题 → 查角色 → 查动作权限
  • 范围问题 → 查该管理员的游戏 / 推广包授权

报错信息如果只有一句”无权限”,就会把所有排查都导向错误的方向。 后来在报错里区分了”缺少动作权限”和”缺少数据范围”,排查效率立刻提升。

4. 对账任务必须排在分析任务前面

这个教训是”设计顺序”的价值:对账任务本身很简单(查差异、重发),但它的执行时间点决定了当天所有分析数据的质量。

当时的排期是:00:00 对账 → 凌晨留存计算 → LTV 计算。为什么对账要第一个跑?因为后面两个都读订单事实数据——对账补进来的数据,必须能被当天的计算看到。

如果反过来,缺失的订单要到第二天才会进入留存/LTV 的口径,而这两张报表都是”按注册日期分群”的——补数据的时间差会直接导致两天的数字都不对。

5. 配置表不提供删除

推广包这类配置记录,最终把删除功能整个去掉了(前后端都不再提供删除),改为只允许停用。

原因:推广包是历史数据的锚点。订单、事件、归因都引用推广包 ID——一旦删掉,”上个月那批订单是哪个计划带来的”就永远无法回答了。

配置类数据的生命周期,通常比创建它的人想象的长得多。 判断一张表能不能删,标准不是”还有没有人用它”,而是”将来还要不要解释历史”。

6. 后台的每一次点击都是线上操作

最后一条件值得写在这里:后台页面看起来是”内部工具”,但”关闭游戏支付开关”和”发布一次线上变更”是同一件事。

所以后台的设计标准应该向生产系统看齐:权限分层、数据范围、操作审计、危险操作二次确认,一个都不能省。


下一篇

(六)稳定性与运维:双机部署、全量日志、监控告警、降级策略与成本规划——这套系统怎么在无人值守的夜里保持可用 → 继续阅读


本文基于实际项目整理,已脱敏。文中不含任何公司业务数据、配置或凭据。