"""运营可配置项注册表:默认值(= 原 rewards 常量)+ 元信息(label/group/type)。 配置项的 single source of truth: - 业务读配置时 fallback 到这里的 default(空 DB = 原行为不变)。 - admin 配置页用它展示"有哪些可配项 + 当前值 + 默认值 + 说明"。 新增可配项 = 这里加一条 + 业务处改用 app_config.get_value(db, key) 读。 """ from __future__ import annotations from typing import Any from app.core import rewards as r # type 约定(给前端渲染编辑控件用):int / int_list / dict_str_int / bool / enum # hidden=True:仍是合法可配项(业务照常 get_value / admin 可经专用端点读写),但**不在通用 # 「系统配置」页渲染**(admin/routers/config.py:list_config 按此过滤)。用于把已下线/已改由 # 专用页管理的项从福利页 Tab 收起,同时保留后端默认值与写入能力。 CONFIG_DEFS: dict[str, dict[str, Any]] = { "signin_rewards": { "default": list(r.SIGNIN_REWARDS), "label": "签到 7 天金币档位", "group": "签到", "type": "int_list", "help": "第 1~7 天每天签到发的金币;断签重置回第 1 天。长度需为 7。", }, "min_exchange_coin": { "default": r.MIN_EXCHANGE_COIN, "label": "最低兑换金币", "group": "钱包", "type": "int", "help": "金币兑现金的单次最低金币(10000 金币=1 元)。", }, "withdraw_min_cents": { "default": r.WITHDRAW_MIN_CENTS, "label": "提现最低额(分)", "group": "钱包", "type": "int", "help": "单次提现最低;微信商家转账最低 10 分(0.1 元)。", }, "withdraw_max_cents": { "default": r.WITHDRAW_MAX_CENTS, "label": "提现最高额(分)", "group": "钱包", "type": "int", }, # 从福利页 Tab 收起(hidden)的项:任务 / 里程碑 整组 + 看广告组里的「单次金币(遗留展示值)/ # 每轮次数 / 比价领券信息流广告开关 comparing_ad_enabled」。key 与默认值保留、后端业务照常读取, # 仅不在配置页渲染。看广告组保留可见的:每日上限 / 单次金币上限 / 关闭后冷却。 "task_rewards": { "default": dict(r.TASK_REWARDS), "label": "一次性任务奖励", "group": "任务", "type": "dict_str_int", "help": "task_key → 金币。", "hidden": True, }, "record_milestones": { "default": list(r.RECORD_MILESTONES), "label": "比价里程碑金币档位", "group": "里程碑", "type": "int_list", "hidden": True, "help": "累计成功比价第 1~N 档解锁发的金币。", }, "ad_reward_coin": { "default": r.AD_REWARD_COIN, "label": "看广告单次金币", "group": "看广告", "type": "int", "hidden": True, "help": "历史兼容/测试展示值;正式发放按 eCPM 公式计算。", }, "ad_daily_limit": { "default": r.DAILY_AD_REWARD_LIMIT, "label": "看广告每日上限(次)", "group": "看广告", "type": "int", "help": "福利页激励视频每日可发奖次数上限,默认 500。", }, "ad_max_coin": { "default": r.MAX_AD_REWARD_COIN, "label": "看广告单次金币上限", "group": "看广告", "type": "int", "help": "历史 reward_amount 口径保留项;正式发放按 eCPM 公式计算。", }, "ad_round_count": { "default": r.VIDEO_ROUND_REQUIRED_COUNT, "label": "每轮看广告次数", "group": "看广告", "type": "int", "hidden": True, "help": "当前为 1,表示每次广告关闭后触发短冷却。", }, "ad_cooldown_sec": { "default": r.VIDEO_ROUND_COOLDOWN_SECONDS, "label": "广告关闭后冷却(秒)", "group": "看广告", "type": "int", "help": "点击退出广告后,下次点击观看前的冷却时间,默认 3 秒。", }, "signin_boost_coin": { "default": r.SIGNIN_BOOST_COIN, "label": "签到膨胀固定金币", "group": "签到", "type": "int", "help": "Day1-Day6 签到后看完激励视频额外发放的固定金币;Day7 不展示也不允许膨胀。", }, "comparing_ad_enabled": { "default": True, "label": "比价/领券期信息流广告", "group": "看广告", "type": "bool", "hidden": True, "help": ( "开启后,比价进行中 + 领券等候期会在悬浮窗展示穿山甲信息流广告(变现行为);" "关闭则全程不出广告。客户端按 app 启动 / 每场比价开始时拉取并缓存,故为「最终一致」的" "远程开关(下一场比价生效),用于出问题时无需发版即可快速止血。debug 包可用本地开关覆盖。" ), }, "withdraw_auto_reconcile_enabled": { "default": True, "label": "提现自动对账", "group": "钱包", "type": "bool", "help": ( "开启后后台 worker 每隔一段时间自动扫描超时仍「打款中」的提现单并归一化" "(查微信/退款/撤单)。需部署侧 env WITHDRAW_AUTO_RECONCILE_ENABLED=true 启动 worker " "进程后此开关才起效;扫描间隔/超时阈值仍由 env 控制。关掉只停自动扫描," "提现页「批量对账」手动按钮不受影响。" ), }, # 首页轮播数据源(hidden:不进通用配置 Tab,由「首页轮播种子」页的专用端点 /marquee-seeds/mode 读写)。 "marquee_feed_mode": { "default": "mixed", "label": "首页轮播数据源", "group": "首页轮播", "type": "enum", "hidden": True, "help": "mixed=真实优先+种子补位(默认);real=只用真实比价记录;seed=只用种子/合成(演示)。", }, # 比价 LLM 调用成本计价。值是嵌套 JSON(非 str→int),借 dict_str_int 类型在配置页走原始 JSON # 编辑框;set_value 不校验类型,嵌套 JSON 照存。 "llm_token_price": { "default": { "per_model": {"qwen3.5-flash": {"input_per_1m": 0.8, "output_per_1m": 2.0}}, "default": {"input_per_1m": 3.0, "output_per_1m": 15.0}, "currency": "CNY", "unit": "per_1m_tokens", }, "label": "LLM 模型单价(元/百万 token)", "group": "LLM 成本", "type": "dict_str_int", "help": ( "比价 LLM 调用成本计价。JSON:per_model 按模型配 input/output 单价(元/1M token)," "default 兜底未登记的模型。改价只影响之后回填的新记录,历史记录用当时价格快照。" ), }, }