Files
henan-backend/docs/幸运升级接口文档.md
T

30 KiB
Raw Blame History

幸运升级接口文档(Cocos 客户端对接)

适用版本:当前 master。 2026-09-23 起随机数改由「全局水位」动态决定(不再是每个饰品位的固定幸运区间);三身份的进度与随机数完全隔离; 幸运值展示值改为三身份合计(累加口径仍按玩家在前端拉的值,中奖判定恒用 min(概率,75))。 2026-09-24 起水位配置简化为「只填一个目标水位(%)」 —— 随机数中心由它自动推算, 取值范围仍由每个饰品自己的区间限定(见 §1.2 / §1.3 / §6.4)。 方案、换算表、口径推导与算例见 docs/修改记录.md 的 2026-09-24 条(前情见 2026-09-23 条);数据库变更见 docs/updata_sql.md。 所有接口路径前缀 /api/luckyUpgrade(另有 1 个关联接口在 /api/userPackSack)。 代码位置:skins-service/service-playingmethod/.../ApiLuckyUpgradeController.java、ApiLuckyUpgradeServiceImpl.java、ApiUpgradeRecordServiceImpl.java。


一、玩法机制(实现口径)

1.1 一局流程

选饰品位(upgradeId) + 选成功率(probability)
   ↓ 后端按实时价算钱:price = 饰品价 × probability / 100
   ↓ 校验(身份 / 封禁 / 删除 / 未充值 703 / 余额 702)
   ↓ 加锁(饰品位 + 奖池类型)→ 扣款(source=40 幸运升级消费)
   ↓ 判定(见 1.2)
   ├─ 成功:获得该饰品位对应的整件饰品,**该身份**的进度与随机数清 0 / 重抽(其他身份不受影响)
   └─ 失败:获得后台配置的「失败奖励」饰品(安慰奖,可多件),**该身份**的进度累加
   ↓ 写 tt_upgrade_record 记录(含各项快照)+ 背包 tt_box_records(source=4 幸运升级)+ 站内消息
   ↓ 返回升级记录(含 gainOrnamentList,可用于结果页立即分解)

1.2 消耗与判定公式

项 公式 / 取值
单次消耗 price = 饰品实时价(tt_ornament.use_price) × probability / 100,保留 2 位小数(前端不传价格,由后端算,防 0 元 100% 中奖)
判定概率 eff = min(probability, 75)(封顶 75:选 90% 也只按 75% 判定,但钱按 90% 收)
本轮空间 fullV:由当前全局水位自动推算 —— 先按目标水位反推中心,再在该饰品自己的区间 [min, max] 内做偏斜随机(支撑集是整个区间,水位高时下限附近的数更频繁)。水位见 1.3
判定 每拉一次 进度 += eff;进度 + eff >= fullV 即中奖,中奖后只刷新当前身份的 fullV、该身份进度清 0;其他身份的进度与随机数一律不受影响;否则失败继续累加
推论 玩家为一件饰品付出的总成本 ≈ 饰品价 × fullV / 100,即 理论 RTP = 100 / fullV、水位 = 1 − 100/fullV。⇒ 想让水位能长期停在某个值,饰品的随机数区间必须包含该水位对应的中心随机数

⚠️ 名义 ≠ 实测:判定是「跨过阈值即出货、最后一拉整段 eff 计入并丢弃溢出」,实测等效需求是名义值的 118%~120%。所以不要按 100/fullV 公式拍参数,按后台「奖池水位」页的实测 RTP 与标定系数来调。

1.3 幸运值、水位与三个身份池

水位(全局,2026-09-23 起):

水位% = (Σ下注 − Σ返奖 + 人工调整) / Σ下注 × 100     # 统计范围 = 整个幸运升级,排除白号

水位越高说明平台已经净赚越多 → 本轮可出的随机数越低(放水);水位不足 → 随机数越高(收紧)。 后台只配一个目标水位 T(%),随机数中心由它自动反推:

V*   = 100 / ((1 − T/100) × k)                    k = 实测标定系数(1.18~1.20,样本不足按 1.19)
中心 = V* × (1 − (当前水位 − T)/100 × gain)        gain 默认 2
t    = (中心 − min) / (max − min)                 中心在区间内的相对位置,夹到 [0.10, 0.90]
V    = min + (max − min) × u^p,   p = 1/t − 1     u ~ U(0,1)

整个区间始终都可能出,水位只改变概率重心:水位 = 目标时 p = 1(区间内均匀分布); 水位高于目标 ⇒ p > 1 ⇒ 下限附近更频繁;低于目标则相反。 E[V] = 中心 ⇒ 水位会长期自动收敛回目标值(仿真实测偏差 < 0.05 个点)。 ⚠️ t 的夹取范围意味着中心的可表达范围 = min + [10%, 90%] × 区间宽度: 区间太窄或下限太高时,高水位目标就实现不了(例:[100,160] 只能维持约 [20.7%, 45.4%] 的水位)。

虚拟水位(抽随机数实际用的是它,不是上面那个真实水位):真实水位只反映已经结算的下注/返奖, 一批饰品同时开新轮时若都用同一个真实水位,会连着出好几个同侧偏斜的随机数。所以每抽一个随机数, 就把这一轮的预估交易预留进 pending:

虚拟水位 = (真实bet + pending_bet − 真实win − pending_win + 调整) / (真实bet + pending_bet)
pending_bet += 饰品价 × k × 随机数 / 100        pending_win += 饰品价

出货过程中产生的实际流水会逐步把预留扣回去(两边口径一致 ⇒ 真正出货时归零,不会长期挂账)。 白号轮次不进水位口径,不参与预留;未抽过数时 pending = 0,退化为真实水位。

水位从 0 起步,可用后台「幸运升级 → 奖池水位」人工注水/抽水。策略与人工调整见 §6.4。

三个身份池:

池 userType 幸运值字段(累计进度) 本轮已投金额字段
普通用户 02 probability_invested amount_invested / effective_amount_invested
主播 01 anchor_probability_invested anchor_amount_invested / anchor_effective_amount_invested
白号 06(is_blank_account=1 优先判定) blank_probability_invested blank_amount_invested / blank_effective_amount_invested
  • 三池完全隔离:进度与随机数缓存在 Redis upgrade_range_fixed:{饰品位}:{池类型},锁也是按「饰品位 + 池类型」。 白号中奖只刷新白号自己的随机数;普通玩家的进度和随机数原样保留 —— 一个奖池但不会互相污染。
  • 随机数的中心由全局水位决定,取值范围由每个饰品自己的区间限定:luck_section / anchor_luck_section / blank_luck_section 三列仍按身份分别生效,但语义从改造前的「固定随机数区间」 变为「允许的取值范围」—— 区间要包含水位推算出的中心值,否则中心被夹在边界上、水位会停在别处。 策略关闭(enabled=false)时这三列恢复成改造前的「固定随机数区间」用法。
  • 幸运值的累加口径:默认按「玩家在前端拉的那个值」累加(原始 probability,选 90% 就 +90),与历史记录口径一致; 中奖判定固定用 min(probability, 75) —— 两者刻意分开(75% 封顶是风控手段,不改展示数字)。 后台开关 luckyValueMode 可切成 eff(按 min(概率,75) 累加),只影响展示值与落库快照。
  • 幸运值 = 三个身份共享的一个累计器:getOrnamentsList.luckyValue 就是这个值,所以所有人看到的数完全一致; 任一身份(含白号)中奖都把它整体归零,且中奖那一拉的增量仍计入(出货那一刻看到的是含本次的数,下一拉才从 0 重新累计)。 ⚠️ 这与「判定进度」不是一回事:判定进度与随机数按身份完全独立,白号中奖不会动普通玩家的进度。 另有 myLuckyValue = 查看者所属身份的贡献(自上次任一身份中奖以来),排查用。

1.4 获得与失败奖励

  • 成功:获得 tt_upgrade_ornaments.ornaments_id 对应的整件饰品,gain_ornaments_price = 该饰品实时价。
  • 失败:获得后台「幸运升级 → 失败奖励」为该饰品位配置的饰品(tt_upgrade_fail_ornaments,可多件,按 ornament_number 计数),gain_ornaments_price = 安慰奖总价值;该饰品位没配失败奖励时啥都不给(gainOrnamentList 为 []、金额 0)。
  • 所有获得物都写入背包 tt_box_records(source = 4 幸运升级、状态 = 在背包)。

1.5 流水与返佣口径

场景 流水 source
升级消费 40(GOLD_DEVICE,「幸运升级消费」,输出)
游戏内分解(结果页立即分解) 41(GOLD_DEVICE_REWARD,「幸运升级奖励」,输入)
背包里分解(事后在背包页分解) 10(DECOMPOSE_ORNAMENT,输入)
  • 消费 source 40 计入:今日/历史投注流水、综合消费排行榜、流水抽奖资格、上级返佣(按 classicGameCommissionRate 口径)、VIP 流水返利。
  • 白号例外:白号消费不触发上级返佣、不触发 VIP 流水返利;后台充值/流水/记录类页面普遍按 is_blank_account=1 排除白号。

二、通用约定

  • 域名:{host}(例:http://103.108.67.60:28081)。
  • Content-Type:application/json;鉴权:除 getOrnamentsList 外都需要请求头带 token,401 时按项目统一逻辑弹登录。
  • 统一响应:
{ "code": 200, "msg": "操作成功", "data": { } }
code 含义
200 成功(客户端以 code === 200 判定)
401 未登录 / 登录过期,msg「登录过期,请重新登录。」
702 账户余额不足
703 玩家未充值不能游玩任何游戏
500 业务失败,msg 直接给玩家看
  • 维护开关(sys_config,后台「网站设置」):websiteMaintenance=1 → 「网站维护中......」;luckyUpgradeMaintenance=1 → 「幸运升级维护中......」。两个开关都先于登录校验返回。
  • 通用拦截(upgrade 接口):非 主播(01)/普通(02)/白号 → 「非法的用户类型」;status=1 → 「账号已被封禁,无法参与游戏」;del_flag != 0 → 「账号状态异常,无法参与游戏」。
  • 幸运升级相关也有 /api/luckyUpgrade/** 其他接口,本文件只覆盖下表这些。

三、接口清单

接口 说明 鉴权 分页
POST /api/luckyUpgrade/getOrnamentsList 可升级饰品列表(含当前幸运值) 否(未登录按普通用户口径) 无
POST /api/luckyUpgrade/upgrade 拉取一次(扣款 + 判定 + 发奖) 是 -
POST /api/luckyUpgrade/recordPage 记录页:全服/我的 × 当前饰品/全部饰品 是 真分页
POST /api/luckyUpgrade/recentPulls 某饰品位最近 50 次拉取流水 是 无(固定 50)
POST /api/luckyUpgrade/myHistoryRecord 我的记录(全部饰品,成败都返回) 是 真分页
POST /api/luckyUpgrade/getUpgradeRecord 通用条件查询(内部/后台复用) 是 真分页
POST /api/luckyUpgrade/tenTopUpgradeRecord 十佳掉落榜(按实际获得价值 Top10) 是 无(固定 10)
POST /api/userPackSack/decompose 分解获得的饰品(结果页或背包) 是 -

四、接口详情

4.1 POST /api/luckyUpgrade/getOrnamentsList — 可升级饰品列表

请求(全部可选,无分页、无排序参数,返回按 use_price 升序)

字段 类型 说明
id Integer 可直接取某一个饰品位(tt_upgrade_ornaments.id)
type String 饰品类型数字字典码(1匕首 2手枪 3步枪 4微型冲锋枪 5重型武器 6手套 7印花 8其它);空串按不传处理
itemName String 饰品名称模糊匹配
priceMin / priceMax BigDecimal 按价格区间过滤(只在传了 priceMax 时生效;不传 priceMin 默认 0)
userType String 后端注入,客户端不要传(用登录用户身份决定返回哪个池的幸运值)
{ "type": "3", "itemName": "", "priceMax": 5000 }

响应:data 为数组,仅返回 status='0'(启用)的饰品位:

字段 类型 说明
id Integer 可升级饰品ID(后续 upgrade.upgradeId、recordPage.upgradeId、recentPulls.upgradeRecordId 都传它)
ornamentId Long 饰品ID(tt_ornament.id)
name String 饰品全名(优先展示)
itemName String 名称(与 name 同源字段)
usePrice BigDecimal 实时价格(= 计算消耗、展示目标金额的基准)
imageUrl String 饰品图片
shortName / typeName / rarityName / exteriorName / levelImg String 简称 / 类型 / 稀有度 / 磨损度 / 品质图
luckyValue Integer 当前幸运值(三个身份共享的一个累计值,所有人看到的数一致);口径见 1.3。任一身份中奖即整体归零
myLuckyValue Integer 查看者所属身份对共享值的贡献(自上次任一身份中奖以来);排查用,一般不用

4.2 POST /api/luckyUpgrade/upgrade — 拉取一次

请求

字段 类型 必填 说明
upgradeId Integer 是 可升级饰品ID(getOrnamentsList 的 id)
probability Integer 是 本次选择的成功率,1~100;判定按 min(概率,75),扣款按传入值
{ "upgradeId": 25, "probability": 10 }

响应:data = 本条升级记录(tt_upgrade_record),字段如下:

字段 类型 说明
id Long 升级记录ID
userId / nickName / userType / userAvatar - 玩家信息(昵称是升级时快照)
probability Integer 本次选择的成功率(原始概率快照)
luckyValue Integer 本次升级时的幸运值快照(中奖为 0)
amountConsumed BigDecimal 本次实际消耗
amountRequired BigDecimal 本轮目标金额快照 = 饰品价 × fullV%
amountInvested / effectiveAmountInvested BigDecimal 本轮已投金额(实际扣款口径 / 真实计算口径)快照
effectiveAmount BigDecimal 真实计算金额 = 饰品价 × min(概率,75)%
targetUpgradeId Integer 可升级饰品ID
targetOrnamentId Long 目标饰品ID
targetOrnamentPrice BigDecimal 目标饰品价格
isVictory Boolean 成败:true=升级成功
gainOrnamentsPrice BigDecimal 本次获得饰品总价值(成功=目标饰品价,失败=安慰奖总价,可能为 0)
gainOrnamentList Array 本次实际获得物(JSON 数组,不是字符串),每项:id=背包记录ID(tt_box_records.id)、ornamentId、ornamentName、ornamentHashName、ornamentPrice、ornamentImgUrl、ornamentLevelId、ornamentLevelImg、ornamentNumber;无获得时为 []
openTime String 升级时间 yyyy-MM-dd HH:mm:ss
{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "id": 93, "userId": 34052018, "probability": 10, "luckyValue": 0,
    "amountConsumed": 181.95, "amountRequired": 1819.52, "isVictory": true,
    "gainOrnamentsPrice": 1819.52,
    "gainOrnamentList": [
      { "id": 11530, "ornamentId": 1312995578, "ornamentName": "AK-47 | 表面淬火 (略有磨损)",
        "ornamentPrice": 1819.52, "ornamentImgUrl": "/uploads/xxx.png", "ornamentLevelImg": "/profile/xxx.png" }
    ],
    "openTime": "2026-09-11 18:24:01"
  }
}

失败返回示例:{"code":500,"msg":"系统繁忙,请稍后重试。"}(同池并发抢锁失败,客户端可直接重试);{"code":703,"msg":"玩家未充值不能游玩任何游戏"};{"code":702,"msg":"账户余额不足"};{"code":500,"msg":"该饰品未在幸运升级开放。"}。

结果页立即分解:取 gainOrnamentList[i].id 作为 packSackIds,调 4.8 接口(decomposeType="game")。不分解就等于饰品留在背包里。

4.3 POST /api/luckyUpgrade/recordPage — 记录页(主力接口)

请求(二维组合,共 4 种场景,均需登录)

字段 类型 必填 默认 说明
scope Integer 否 0 0=全服(不过滤用户) 1=我的
itemScope Integer 否 0 0=全部饰品 1=当前饰品
upgradeId Long 条件必填 - 可升级饰品ID;itemScope=1 时必填,为空返回 500;itemScope=0 时传了也忽略
page Integer 否 1 页码(≥1)
size Integer 否 10 每页条数(1~20,超限参数错误)
{ "scope": 1, "itemScope": 1, "upgradeId": 25, "page": 1, "size": 10 }

响应:data 为分页对象 { records, total, current, size, pages },records 按 openTime 倒序:

字段 类型 说明
userId / avatar / nickName / vipLevel - 玩家信息(昵称是升级时快照)
probability Integer 成功率(原始概率,如 89 表示 89%)
luckyValue Integer 幸运值快照(中奖记录记的是「中奖那一刻」的值,含本次),与 getOrnamentsList.luckyValue 同口径
amountConsumed BigDecimal 消耗
gainOrnamentsPrice BigDecimal 获得饰品总价值
isVictory Boolean 成败:true=升级成功(前端胜负样式用)
openTime String 升级时间
targetOrnamentId Long 目标饰品ID
name / imageUrl / usePrice / exteriorName / levelImg - 本次实际获得的饰品:成功=目标饰品,失败=安慰奖饰品(gainOrnamentList 首件);未配安慰奖的失败记录会回退成目标饰品且金额为 0,胜负请用 isVictory 判断,不要看图片
  • 全服(scope=0)不过滤白号、机器人、封禁用户,白号可正常上榜。
  • 旧的 recentPulls / myHistoryRecord 场景都被本接口覆盖,可继续用但建议逐步切到本接口。

4.4 POST /api/luckyUpgrade/recentPulls — 某饰品位最近 50 次拉取流水

请求:{ "upgradeRecordId": 25 }

字段 类型 必填 说明
upgradeRecordId Long 是 ⚠️ 字段名有历史误导性:实际语义是「可升级饰品ID」tt_upgrade_ornaments.id(getOrnamentsList 的 id)。为空返回 500「upgradeRecordId(升级饰品ID)不能为空」
其他字段 - 否 ornamentId/userType/userId/page/size/limit 全部忽略
  • 无分页:固定最近 50 条(SQL LIMIT 50),没有 page/total。
  • 不过滤:全服所有人(含白号、机器人、封禁/删除用户),成功与失败都返回。
  • nickName/avatar/vipLevel 可能为 null(该记录的用户已不在 tt_user),客户端要能容错。

响应:data 直接是数组(不是分页对象):

字段 类型 说明
avatar / nickName / vipLevel - 拉取人信息
probability Integer 本次拉取选择的成功率(不是中奖率)
luckyValue Integer 本次拉取时的幸运值快照(含本次的增量;中奖那一拉记的就是出货那一刻的值),与 recordPage 同口径
isVictory Boolean 成败:true=升级成功
name / imageUrl / usePrice / exteriorName / levelImg - 目标饰品("谁在拉这个饰品",不是获得物)
openTime String 拉取时间

4.5 POST /api/luckyUpgrade/myHistoryRecord — 我的记录(全部饰品)

字段 类型 必填 默认 说明
page Integer 否 1 页码
size Integer 否 10 1~20

响应:data 为分页对象,records 是 tt_upgrade_record(字段同 4.2 的响应,含 gainOrnamentList),仅当前登录用户、不含他人记录、成败都返回、按时间倒序;昵称/头像会补齐。

4.6 POST /api/luckyUpgrade/getUpgradeRecord — 通用条件查询

字段 类型 必填 说明
page / size Integer 必传 不传会 500(内部直接用于构造分页对象)
upgradeRecordId Long 否 按记录ID精确查
ornamentId Long 否 按目标饰品ID过滤
userType String 否 按用户类型过滤(01/02/06…)
userId Integer 否 按用户过滤

响应:data 为分页对象(records 为 tt_upgrade_record,userAvatar 已补齐,gainOrnamentList 空值兜底为 [])。主要用于内部/后台复用,客户端记录页请用 recordPage。

4.7 POST /api/luckyUpgrade/tenTopUpgradeRecord — 十佳掉落榜

字段 类型 必填 说明
isVictory Boolean 否 true 只看成功、false 只看失败;不传则全部记录一起排

响应:data 为分页对象(固定第 1 页、10 条),按 gainOrnamentsPrice(实际获得价值)倒序取 Top10 —— 即"实际掉落榜",不是目标饰品价值榜。

4.8 POST /api/userPackSack/decompose — 分解饰品(结果页/背包)

字段 类型 必填 说明
packSackIds Long[] 条件必填 背包记录ID(tt_box_records.id,即 gainOrnamentList[i].id);isAll=true 时可不传
isAll Boolean 是 是否全选
decomposeType String 否 game=游戏结果页立即分解(按饰品来源记 GOLD_DEVICE_REWARD(41));backpack=背包分解(记 DECOMPOSE_ORNAMENT(10))

返回 {"code":200,"msg":"成功分解N件饰品"}。受 websiteMaintenance 开关拦截。


五、前端对接要点

  1. upgradeId 语义:所有接口里的 upgradeId / recentPulls.upgradeRecordId 都是可升级饰品ID(tt_upgrade_ornaments.id),来自 getOrnamentsList 的 id,不是 tt_ornament.id,也不是升级记录ID。
  2. 扣款以服务端为准:不要传价格;展示价格用 usePrice × probability / 100(与后端同公式,后端保留 2 位小数)。
  3. 概率封顶 75:UI 上若提供 >75% 的档位,要提示"更高概率只加钱、不提升判定"。
  4. 幸运值怎么用:主界面实时幸运值取 getOrnamentsList.luckyValue(三个身份合计的全服累计值,所有人一致);拉取成功后用 upgrade 响应里的 luckyValue 立即覆盖,不必等列表刷新。记录里的幸运值是历史快照。
  5. 分池与水位:进度与随机数按 普通(02) / 主播(01) / 白号(06) 三套完全独立 —— 别人(含白号)中奖不会动你的进度和随机数。随机数的中心由全局水位统一决定(水位越高越容易出货),取值范围仍由该饰品自己的随机数区间限定。
  6. 分页:recordPage/myHistoryRecord 的 size 上限 20;切换 tab(我的/全服、当前饰品/全部饰品)时 page 重置为 1。
  7. 失败记录展示:以 isVictory 判断胜负,name/imageUrl/... 是"实际获得物"(失败=安慰奖);无安慰奖时金额为 0。
  8. 结果页分解:gainOrnamentList 里的 id 就是分解入参;不调用 = 饰品留在背包。
  9. 并发:同一饰品位 + 同一奖池同时拉取会串行,抢不到锁返回 500「系统繁忙,请稍后重试。」,可直接提示重试。

六、后台配置与调参

6.1 「幸运升级 → 饰品列表」(tt_upgrade_ornaments)

后台列 字段 说明
普通用户 / 随机数区间 luck_section 随机数的允许取值范围([min,max] JSON),水位推算出的中心必须落在里面
普通用户 / 本轮已投金额 amount_invested 普通池当前累计投入(运行时值,后台不可编辑回写)
主播用户 / 随机数区间 anchor_luck_section 同上,主播身份独立配置
白号用户 / 随机数区间 blank_luck_section 同上,白号身份独立配置;未配则回退 luck_section
状态 status 0=启用(=可升级、会出现在客户端列表) 1=禁用
  • 随机数的中心不在这里配:中心由「目标水位」自动推算(页面顶部水位条显示 当前水位 / 目标水位 / 实测 RTP / 推算随机数中心),这里只配取值范围。调难度请改目标水位(见 6.4)。
  • ⚠️ 区间必须包含推算出的中心值,且中心离两端各留约 10% 区间宽度,否则中心被夹住、水位会停在附近 而不是目标值(仿真实测可偏 16~20 个点)。区间还限定了能维持的水位范围:[100,160] 只能维持约 [20.7%, 45.4%]。
  • 整个区间始终都可能出(偏斜随机),水位只改变概率重心;水位 = 目标时是均匀分布。 区间越宽,单轮随机数波动越大、越难被玩家归纳出规律。
  • 区间改完下一轮立即生效,不用清 Redis(水位驱动下直接读实体字段,不走区间缓存)。
  • ⚠️ 旧版本文档里「上线前清空三个区间列」的做法已作废 —— 现在这三列是必填项。

6.2 「幸运升级 → 失败奖励」(tt_upgrade_fail_ornaments)

每个饰品位可配多件失败奖励(ornament_name / ornament_price / ornament_number / ornament_level_id),玩家升级失败即发放这些饰品;没配就不发(记录里 gainOrnamentList=[]、gainOrnamentsPrice=0)。

6.3 维护开关(后台「网站设置」,sys_config)

key 值 效果
websiteMaintenance 1 全站维护:upgrade 与分解接口都拦截
luckyUpgradeMaintenance 1 只拦幸运升级:upgrade 返回「幸运升级维护中......」

6.4 「幸运升级 → 奖池水位」(目标水位 / 注水抽水)—— 后台接口

Base = /admin/upgradeWatermark,均需后台登录。

接口 说明
GET /info 当前水位快照 + 目标水位策略 + 按目标水位推算的随机数中心
POST /setting 保存策略:targetWatermark(必填)+ gain(可选,默认 2)+ enabled + luckyValueMode
POST /adjust 注水 / 抽水(type 1注水 2抽水;调整原因必填)
POST /adjustReset 重置人工调整(插入抵消日志,历史保留)
GET /adjustLog 调整历史分页
POST /recalc 按历史流水全量重算累计量 + 刷新标定系数(纠正 Redis 计数漂移)

/info 返回(节选):

字段 说明
enabled 策略开关;false = 回退到每个饰品位的固定随机数区间
targetWatermark 目标水位(%) —— 后台唯一要配的运营参数
gain 响应灵敏度(水位每偏离目标 1 个点,随机数中心反向调整 gain%),默认 2
watermark / rtp 当前(真实)水位% / 实测 RTP%(均排除白号)
watermarkWithPending 虚拟水位 —— 抽随机数实际用的那个;与 watermark 的差 = 已抽走未出货轮次的影响
pendingBet / pendingWin 已抽走随机数、还没出货的那些轮次的剩余预估下注 / 返奖
targetGap 当前水位 − 目标水位(正 = 赚得比目标多 ⇒ 下一轮放水)
targetCenter 按目标水位推算的随机数中心 V*(饰品区间要包含它)
centerNow 下一轮新开会用到的随机数中心(按虚拟水位算,与实际抽数口径一致)
calibration 实测标定系数 k;样本不足时为 null
effectiveCalibration 实际参与推算的系数(样本不足时 = 1.19 兜底)
totalBet / totalWin / blankBet / blankWin / adjust 水位口径的累计量(白号不进水位,仅并列展示)

策略配置(sys_config: luckyUpgradeWatermark):

{
  "enabled": true,
  "targetWatermark": 30,
  "gain": 2,
  "luckyValueMode": "raw"
}
字段 说明
enabled false = 逐字节回退改造前的固定幸运区间行为,不发版即可回滚
targetWatermark 目标水位(%):平台希望长期维持的净收益比例。30 ⇒ 长期平均 RTP ≈ 70%。合法范围 −50 ~ 95
gain 响应灵敏度,默认 2:水位每高出目标 1 个点,随机数中心降 2%(放水);低于目标则反之
luckyValueMode 幸运值展示口径:raw(默认,累加原始 probability)/ eff(累加 min(概率,75))。不影响中奖判定

目标水位 → 随机数中心换算表(按 k = 1.19 兜底系数算):

目标水位 0% 10% 20% 30% 40% 45% 50% 60%
中心 V* 84 93 105 120 140 153 168 210

保存时强校验:targetWatermark 必填且在 −50% ~ 95% 之间;gain 若填写需在 0.5 ~ 10 之间。

⚠️ 标定:判定溢出让「实测等效需求」是名义值的 118%~120%,所以中心公式必须带上实测标定系数 (V* = 100 / ((1 − 目标水位/100) × k)),不能按 100/(1 − 目标水位/100) 直接算。 k 由系统按真实流水滚动计算(5 分钟内存缓存),样本不足时按 1.19 兜底。 ⚠️ V* 必须落在饰品的随机数区间内,且离两端各留约 10% 区间宽度,否则中心被夹住、水位停不到目标值。 ⚠️ 区间同时限定了能维持的水位范围(中心可表达范围 = 区间下界 + [10%, 90%] 宽度): 例 [100,160] 对应水位约 [20.7%, 45.4%];想把水位开到 60%,区间上限得提到 210 以上。 完整推导、仿真数据与算例见 docs/修改记录.md 的 2026-09-24 条。

6.5 数据落库

表 说明
tt_upgrade_record 每条拉取记录(成败、消耗、获得总额、各项快照、gain_ornament_list JSON、lucky_value、watermark 水位快照、round_target 本轮随机数快照)
tt_box_records 获得物入背包(source=4 幸运升级),id 即结果页分解入参
tt_user_blend_ercash 余额流水:消费 40、游戏内分解 41、背包分解 10
tt_upgrade_ornaments 饰品位配置 + 三个身份的运行进度(各身份累计幸运值/已投金额)
tt_upgrade_reward_log 水位人工调整日志(注水/抽水/重置;delta 为带符号影响,水位调整额 = SUM(delta))

字段变更历史见 docs/updata_sql.md(含白号三列的 ALTER)。