}
- variant="primary"
- onClick={onCreate}
- >
- 添加配置
-
+ canUpdate ? (
+
}
+ variant="primary"
+ onClick={onCreate}
+ >
+ 添加配置
+
+ ) : null
}
filters={
`${item.app_code}:${item.pool_id}:${item.rule_version}`}
+ minWidth="1540px"
+ rowKey={(item) => `${luckyGiftPoolKey(item)}:${item.rule_version}`}
title="幸运礼物配置"
total={items.length}
/>
@@ -61,8 +97,8 @@ export function ConfigsView({ apps, luckyGifts, onCreate, onEdit, saving }) {
);
}
-function buildColumns({ onEdit, saving }) {
- return [
+function buildColumns({ canCredit, canDebit, canUpdate, disabled, onCredit, onDebit, onEdit, saving }) {
+ const columns = [
{ key: "app_code", label: "应用", width: "minmax(88px, 0.7fr)" },
{ key: "pool_id", label: "奖池", width: "minmax(110px, 0.9fr)" },
{
@@ -79,6 +115,12 @@ function buildColumns({ onEdit, saving }) {
render: (item) => item.strategy_version || "fixed_v2",
width: "minmax(112px, 0.8fr)",
},
+ {
+ key: "pool_balance",
+ label: "奖池余额",
+ render: (item) => (item.pool_balance ? formatNumber(item.pool_balance.balance) : "-"),
+ width: "minmax(120px, 0.9fr)",
+ },
{
key: "enabled",
label: "状态",
@@ -115,14 +157,46 @@ function buildColumns({ onEdit, saving }) {
render: (item) => (item.is_default ? "-" : ),
width: "minmax(176px, 1.1fr)",
},
- {
+ ];
+
+ if (canUpdate || canCredit || canDebit) {
+ columns.push({
+ fixed: "right",
key: "actions",
label: "操作",
- render: (item) => (
-
- ),
- },
- ];
+ render: (item) => {
+ const identity = `${item.app_code} / ${item.pool_id} / ${item.strategy_version}`;
+ const actionPool = item.pool_balance || {
+ app_code: item.app_code,
+ pool_id: item.pool_id,
+ strategy_version: item.strategy_version,
+ };
+ return (
+
+
+ {canUpdate ? (
+
+ ) : null}
+
+ );
+ },
+ width: `${(canCredit ? 110 : 0) + (canDebit ? 110 : 0) + (canUpdate ? 110 : 0) + 20}px`,
+ });
+ }
+
+ return columns;
}
diff --git a/ops-center/src/components/LuckyGiftConfigDialog.jsx b/ops-center/src/components/LuckyGiftConfigDialog.jsx
index 54bc3b1..c017168 100644
--- a/ops-center/src/components/LuckyGiftConfigDialog.jsx
+++ b/ops-center/src/components/LuckyGiftConfigDialog.jsx
@@ -444,7 +444,14 @@ function DynamicStrategySections({ form, onChange }) {
动态水位
-
+
+ {canCredit ? (
+
+ ) : null}
+ {canDebit ? (
+
+ ) : null}
+
+ );
+}
diff --git a/ops-center/src/components/LuckyGiftPoolAdjustmentDialog.jsx b/ops-center/src/components/LuckyGiftPoolAdjustmentDialog.jsx
new file mode 100644
index 0000000..43d4f39
--- /dev/null
+++ b/ops-center/src/components/LuckyGiftPoolAdjustmentDialog.jsx
@@ -0,0 +1,132 @@
+import Dialog from "@mui/material/Dialog";
+import DialogActions from "@mui/material/DialogActions";
+import DialogContent from "@mui/material/DialogContent";
+import DialogTitle from "@mui/material/DialogTitle";
+import TextField from "@mui/material/TextField";
+import { useState } from "react";
+import { Button } from "@/shared/ui/Button.jsx";
+import { formatNumber } from "../format.js";
+
+export function LuckyGiftPoolAdjustmentDialog({ adjustmentId, direction, onClose, onSubmit, pool, submitting }) {
+ const [amount, setAmount] = useState("");
+ const [reason, setReason] = useState("");
+ const [errors, setErrors] = useState({});
+ const debit = direction === "out";
+ const appCode = pool.app_code;
+ const poolID = pool.pool_id;
+ const strategyVersion = pool.strategy_version;
+ const availableBalance = Number(pool.available_balance || 0);
+ const title = debit ? "扣减奖池水位" : "添加奖池水位";
+
+ const submit = (event) => {
+ event.preventDefault();
+ const nextErrors = validateAdjustment({ amount, availableBalance, debit, reason });
+ setErrors(nextErrors);
+ if (Object.keys(nextErrors).length) {
+ return;
+ }
+
+ // adjustmentId 在打开弹窗时创建,此处只原样透传。请求失败后组件不会卸载,因此用户修正或直接重试时
+ // 仍使用同一幂等键,网络超时场景不会把已成功的资金动作重复执行。
+ onSubmit({
+ adjustmentId,
+ amountCoins: Number(amount),
+ appCode,
+ direction,
+ poolId: poolID,
+ reason: reason.trim(),
+ strategyVersion,
+ });
+ };
+
+ return (
+
+ );
+}
+
+function PoolMetric({ label, value }) {
+ return (
+
+ {label}
+ {value || "-"}
+
+ );
+}
+
+function validateAdjustment({ amount, availableBalance, debit, reason }) {
+ const errors = {};
+ const rawAmount = String(amount || "").trim();
+ if (!/^[1-9]\d*$/.test(rawAmount) || !Number.isSafeInteger(Number(rawAmount))) {
+ errors.amount = "请输入大于 0 的整数金币数量";
+ } else if (debit && Number(rawAmount) > availableBalance) {
+ errors.amount = `扣减不能超过可用余额 ${formatNumber(availableBalance)}`;
+ }
+ if (!String(reason || "").trim()) {
+ errors.reason = "请输入调整原因";
+ }
+ return errors;
+}
diff --git a/ops-center/src/components/OverviewView.jsx b/ops-center/src/components/OverviewView.jsx
index 8f98126..ed64516 100644
--- a/ops-center/src/components/OverviewView.jsx
+++ b/ops-center/src/components/OverviewView.jsx
@@ -2,123 +2,168 @@ import AppsOutlined from "@mui/icons-material/AppsOutlined";
import CardGiftcardOutlined from "@mui/icons-material/CardGiftcardOutlined";
import CasinoOutlined from "@mui/icons-material/CasinoOutlined";
import SavingsOutlined from "@mui/icons-material/SavingsOutlined";
+import { useMemo } from "react";
import { DataTable } from "@/shared/ui/DataTable.jsx";
import { KpiCard } from "@/shared/ui/KpiCard.jsx";
import { TimeText } from "@/shared/ui/TimeText.jsx";
+import { luckyGiftPoolKey } from "../api.js";
import { formatNumber, formatPercentFromPPM } from "../format.js";
+import { LuckyGiftPoolActions } from "./LuckyGiftPoolActions.jsx";
import { OpsStatusBadge } from "./OpsStatusBadge.jsx";
-export function OverviewView({ data }) {
- const { appSummaries, poolBalances, summary } = data;
+export function OverviewView({ canCredit, canDebit, data, disabled, onCredit, onDebit }) {
+ const { appSummaries, poolBalances, summary } = data;
+ const poolBalanceColumns = useMemo(
+ () => buildPoolBalanceColumns({ canCredit, canDebit, disabled, onCredit, onDebit }),
+ [canCredit, canDebit, disabled, onCredit, onDebit],
+ );
- return (
-
-
-
-
-
-
-
+ return (
+
+
+
+
+
+
+
-
`${item.app_code ?? item.appCode}:${item.pool_id ?? item.poolId}`}
- title="奖池实时水位"
- total={poolBalances.length}
- />
+
- item.app_code}
- title="各应用抽奖汇总"
- total={appSummaries.length}
- />
-
- );
+
item.app_code}
+ title="各应用抽奖汇总"
+ total={appSummaries.length}
+ />
+
+ );
}
-const poolBalanceColumns = [
- { key: "app_code", label: "应用", render: (item) => item.app_code ?? item.appCode, width: "minmax(96px, 0.8fr)" },
- { key: "pool_id", label: "奖池", render: (item) => item.pool_id ?? item.poolId, width: "minmax(110px, 1fr)" },
- {
- key: "materialized",
- label: "水位状态",
- // materialized=false 表示该奖池还没有真实入账流水,余额是规则推导的初始水位,不能当作可支配资金。
- render: (item) =>
- item.materialized === false ? : ,
- width: "minmax(132px, 0.9fr)"
- },
- { key: "balance", label: "当前余额", render: (item) => formatNumber(item.balance ?? item.Balance) },
- { key: "available_balance", label: "可用余额", render: (item) => formatNumber(item.available_balance ?? item.availableBalance) },
- { key: "reserve_floor", label: "保底线", render: (item) => formatNumber(item.reserve_floor ?? item.reserveFloor), width: "minmax(96px, 0.8fr)" },
- { key: "total_in", label: "累计流入", render: (item) => formatNumber(item.total_in ?? item.totalIn) },
- { key: "total_out", label: "累计流出", render: (item) => formatNumber(item.total_out ?? item.totalOut) },
- {
- key: "updated_at_ms",
- label: "更新时间",
- render: (item) =>
- item.materialized === false ? "-" : ,
- width: "minmax(150px, 1fr)"
- }
-];
+function buildPoolBalanceColumns({ canCredit, canDebit, disabled, onCredit, onDebit }) {
+ const columns = [
+ { key: "app_code", label: "应用", width: "minmax(96px, 0.8fr)" },
+ { key: "pool_id", label: "奖池", width: "minmax(110px, 1fr)" },
+ { key: "strategy_version", label: "策略", width: "minmax(112px, 0.9fr)" },
+ {
+ key: "materialized",
+ label: "水位状态",
+ // materialized=false 是 owner 根据当前策略推导的初始水位;仍允许补水或扣水,首次调整由 owner 原子物化该策略奖池。
+ render: (item) =>
+ item.materialized === false ? (
+
+ ) : (
+
+ ),
+ width: "minmax(132px, 0.9fr)",
+ },
+ { key: "balance", label: "当前余额", render: (item) => formatNumber(item.balance) },
+ { key: "available_balance", label: "可用余额", render: (item) => formatNumber(item.available_balance) },
+ {
+ key: "reserve_floor",
+ label: "保底线",
+ render: (item) => formatNumber(item.reserve_floor),
+ width: "minmax(96px, 0.8fr)",
+ },
+ { key: "total_in", label: "业务流入", render: (item) => formatNumber(item.total_in) },
+ { key: "total_out", label: "返奖流出", render: (item) => formatNumber(item.total_out) },
+ { key: "manual_credit_total", label: "人工添加", render: (item) => formatNumber(item.manual_credit_total) },
+ { key: "manual_debit_total", label: "人工扣减", render: (item) => formatNumber(item.manual_debit_total) },
+ {
+ key: "updated_at_ms",
+ label: "更新时间",
+ render: (item) => (item.materialized === false ? "-" : ),
+ width: "minmax(150px, 1fr)",
+ },
+ ];
+
+ if (canCredit || canDebit) {
+ columns.push({
+ fixed: "right",
+ key: "actions",
+ label: "操作",
+ render: (item) => (
+
+ ),
+ width: canCredit && canDebit ? "220px" : "120px",
+ });
+ }
+ return columns;
+}
const appSummaryColumns = [
- { key: "app_code", label: "应用", width: "minmax(96px, 0.8fr)" },
- { key: "total_draws", label: "抽奖次数", render: (item) => formatNumber(item.total_draws) },
- { key: "unique_users", label: "参与用户", render: (item) => formatNumber(item.unique_users) },
- { key: "unique_rooms", label: "参与房间", render: (item) => formatNumber(item.unique_rooms) },
- { key: "total_spent_coins", label: "消耗金币", render: (item) => formatNumber(item.total_spent_coins) },
- { key: "total_reward_coins", label: "返还金币", render: (item) => formatNumber(item.total_reward_coins) },
- {
- key: "actual_rtp_ppm",
- label: "实际 RTP",
- render: (item) => (item.total_draws > 0 ? formatPercentFromPPM(item.actual_rtp_ppm) : "-")
- },
- {
- key: "exceptions",
- label: "发放异常",
- // 待发放和失败合并成一列告警:正常应为 0,非 0 说明发奖链路有积压或失败,需要跟进抽奖记录。
- render: (item) => {
- const pending = Number(item.pending_draws || 0);
- const failed = Number(item.failed_draws || 0);
- if (!pending && !failed) {
- return ;
- }
- return ;
+ { key: "app_code", label: "应用", width: "minmax(96px, 0.8fr)" },
+ { key: "total_draws", label: "抽奖次数", render: (item) => formatNumber(item.total_draws) },
+ { key: "unique_users", label: "参与用户", render: (item) => formatNumber(item.unique_users) },
+ { key: "unique_rooms", label: "参与房间", render: (item) => formatNumber(item.unique_rooms) },
+ { key: "total_spent_coins", label: "消耗金币", render: (item) => formatNumber(item.total_spent_coins) },
+ { key: "total_reward_coins", label: "返还金币", render: (item) => formatNumber(item.total_reward_coins) },
+ {
+ key: "actual_rtp_ppm",
+ label: "实际 RTP",
+ render: (item) => (item.total_draws > 0 ? formatPercentFromPPM(item.actual_rtp_ppm) : "-"),
+ },
+ {
+ key: "exceptions",
+ label: "发放异常",
+ // 待发放和失败合并成一列告警:正常应为 0,非 0 说明发奖链路有积压或失败,需要跟进抽奖记录。
+ render: (item) => {
+ const pending = Number(item.pending_draws || 0);
+ const failed = Number(item.failed_draws || 0);
+ if (!pending && !failed) {
+ return ;
+ }
+ return (
+
+ );
+ },
+ width: "minmax(170px, 1.2fr)",
},
- width: "minmax(170px, 1.2fr)"
- }
];
function countBySource(apps = [], source) {
- return apps.filter((item) => (item.source || "registry") === source).length;
+ return apps.filter((item) => (item.source || "registry") === source).length;
}
diff --git a/ops-center/src/configForm.js b/ops-center/src/configForm.js
index f814777..bc7f3cf 100644
--- a/ops-center/src/configForm.js
+++ b/ops-center/src/configForm.js
@@ -21,7 +21,8 @@ const dynamicDefaults = {
device_daily_payout_cap: 0,
high_water_nonzero_factor_percent: 130,
high_watermark_coins: 20_000_000,
- initial_pool_coins: 1_000_000,
+ // dynamic_v3 的运行资金只能通过带审计的奖池水位接口注入,发布规则不再隐式 seed。
+ initial_pool_coins: 0,
jackpot_global_rtp_max_percent: 98,
jackpot_multipliers: [200, 500, 1000],
jackpot_spend_threshold_coins: 0,
@@ -113,6 +114,11 @@ export function configToForm(config = {}) {
form[field] = targetRTPPercent;
continue;
}
+ if (field === "initial_pool_coins" && strategyVersion === DYNAMIC_STRATEGY) {
+ // 旧 dynamic_v3 快照可能仍保存 1,000,000;新增版本必须显式归零,不能把历史隐式注资语义继续复制。
+ form[field] = "0";
+ continue;
+ }
form[field] = formConfigNumber(config, field, snakeToCamel(field), defaultNumber(field, strategyVersion));
}
return form;
@@ -140,7 +146,10 @@ export function formToConfig(config, form) {
strategy_version: form.strategy_version,
};
for (const field of numericFields) {
- output[field] = numberFromForm(form[field]);
+ output[field] =
+ field === "initial_pool_coins" && form.strategy_version === DYNAMIC_STRATEGY
+ ? 0
+ : numberFromForm(form[field]);
}
return output;
}
@@ -270,7 +279,7 @@ export function validateLuckyGiftForm(form) {
PPM_SCALE
)
errors.push("奖池、平台盈利和主播收益比例合计必须等于 100%");
- if (number("initial_pool_coins") <= 0) errors.push("初始奖池必须大于 0");
+ if (number("initial_pool_coins") !== 0) errors.push("dynamic_v3 初始奖池必须为 0,请通过水位操作注资");
if (number("loss_streak_guarantee") <= 0) errors.push("连续未中奖保护次数必须大于 0");
if (number("low_watermark_coins") <= 0 || number("high_watermark_coins") <= number("low_watermark_coins"))
errors.push("高水位必须大于正数低水位");
diff --git a/ops-center/src/configForm.test.js b/ops-center/src/configForm.test.js
index df9118d..a20ec5c 100644
--- a/ops-center/src/configForm.test.js
+++ b/ops-center/src/configForm.test.js
@@ -8,7 +8,7 @@ describe("ops center lucky gift dynamic_v3 form", () => {
expect(form).toMatchObject({
anchor_rate_percent: "1",
enabled: false,
- initial_pool_coins: "1000000",
+ initial_pool_coins: "0",
jackpot_multipliers: "200, 500, 1000",
max_single_payout: "0",
pool_rate_percent: "98",
@@ -49,6 +49,15 @@ describe("ops center lucky gift dynamic_v3 form", () => {
});
});
+ test("drops legacy dynamic initial seed when loading and publishing a new version", () => {
+ const legacy = completeDynamicConfig();
+ legacy.initial_pool_coins = 1_000_000;
+
+ const form = configToForm(legacy);
+ expect(form.initial_pool_coins).toBe("0");
+ expect(formToConfig(legacy, form).initial_pool_coins).toBe(0);
+ });
+
test("allows disabled drafts but blocks enabled dynamic rules until app monetary limits are filled", () => {
const draft = configToForm({ app_code: "lalu", pool_id: "default", strategy_version: "dynamic_v3" });
expect(validateLuckyGiftForm(draft)).toEqual([]);
@@ -98,7 +107,12 @@ describe("ops center lucky gift dynamic_v3 form", () => {
});
const dynamic = applyDynamicStrategyDefaults(legacy);
- expect(dynamic).toMatchObject({ enabled: false, pool_rate_percent: "98", strategy_version: "dynamic_v3" });
+ expect(dynamic).toMatchObject({
+ enabled: false,
+ initial_pool_coins: "0",
+ pool_rate_percent: "98",
+ strategy_version: "dynamic_v3",
+ });
expect(dynamic.stages.map((stage) => stage.tiers.map((tier) => Number(tier.multiplier)))).toEqual([
[0, 0.5, 1, 2],
[0, 0.5, 1, 2],
@@ -144,7 +158,7 @@ function completeDynamicConfig() {
gift_price_reference: 100,
high_water_nonzero_factor_percent: 130,
high_watermark_coins: 20_000_000,
- initial_pool_coins: 1_000_000,
+ initial_pool_coins: 0,
jackpot_global_rtp_max_percent: 98,
jackpot_multipliers: [200, 500, 1000],
jackpot_spend_threshold_coins: 50_000,
diff --git a/ops-center/src/main.jsx b/ops-center/src/main.jsx
index 44f4414..205ab47 100644
--- a/ops-center/src/main.jsx
+++ b/ops-center/src/main.jsx
@@ -2,21 +2,24 @@ import CssBaseline from "@mui/material/CssBaseline";
import { ThemeProvider } from "@mui/material/styles";
import React from "react";
import { createRoot } from "react-dom/client";
+import { AuthProvider } from "@/app/auth/AuthProvider.jsx";
import { ToastProvider } from "@/shared/ui/ToastProvider.jsx";
import { theme } from "@/theme.js";
-import { OpsCenterApp } from "./OpsCenterApp.jsx";
+import { AuthenticatedOpsCenter } from "./OpsCenterEntry.jsx";
import "@/styles/tokens.css";
import "@/styles/shared-ui.css";
import "@/styles/responsive.css";
import "./styles/index.css";
createRoot(document.getElementById("ops-center-root")).render(
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ ,
);
diff --git a/ops-center/src/styles/index.css b/ops-center/src/styles/index.css
index 14d2863..277fc65 100644
--- a/ops-center/src/styles/index.css
+++ b/ops-center/src/styles/index.css
@@ -147,6 +147,43 @@ body {
padding: var(--space-3) var(--space-4);
}
+.ops-pool-actions,
+.ops-config-actions {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ white-space: nowrap;
+}
+
+.ops-config-actions {
+ justify-content: flex-end;
+}
+
+.ops-pool-adjustment-form {
+ display: contents;
+}
+
+.ops-pool-adjustment-content {
+ display: grid;
+ gap: var(--space-4);
+ padding-top: var(--space-4) !important;
+}
+
+.ops-pool-adjustment-metrics {
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ margin-bottom: 0;
+}
+
+.ops-pool-adjustment-note {
+ border: 1px solid var(--info-border);
+ border-radius: var(--radius-control);
+ background: var(--info-surface);
+ color: var(--info);
+ font-size: 13px;
+ font-weight: 700;
+ padding: var(--space-3);
+}
+
/* ---- 幸运礼物配置弹窗 ---- */
.ops-config-form {
@@ -421,6 +458,7 @@ body {
.ops-nav,
.ops-kpi-grid,
.ops-config-metrics,
+ .ops-pool-adjustment-metrics,
.ops-form-grid,
.ops-stage-thresholds,
.ops-tier-row {
diff --git a/src/app/permissions.ts b/src/app/permissions.ts
index 9fcf5ec..1c90d76 100644
--- a/src/app/permissions.ts
+++ b/src/app/permissions.ts
@@ -187,6 +187,8 @@ export const PERMISSIONS = {
sevenDayCheckInUpdate: "seven-day-checkin:update",
luckyGiftView: "lucky-gift:view",
luckyGiftUpdate: "lucky-gift:update",
+ luckyGiftPoolCredit: "lucky-gift:pool-credit",
+ luckyGiftPoolDebit: "lucky-gift:pool-debit",
wheelView: "wheel:view",
wheelUpdate: "wheel:update",
roomRocketView: "room-rocket:view",
diff --git a/src/shared/api/generated/endpoints.ts b/src/shared/api/generated/endpoints.ts
index 5f54e3e..005f3c8 100644
--- a/src/shared/api/generated/endpoints.ts
+++ b/src/shared/api/generated/endpoints.ts
@@ -80,9 +80,11 @@ export const API_OPERATIONS = {
createUser: "createUser",
createWeeklyStarCycle: "createWeeklyStarCycle",
creditCoinSellerStock: "creditCoinSellerStock",
+ creditLuckyGiftPool: "creditLuckyGiftPool",
dashboardOverview: "dashboardOverview",
dashboardUserProfileOverview: "dashboardUserProfileOverview",
debitCoinSellerStock: "debitCoinSellerStock",
+ debitLuckyGiftPool: "debitLuckyGiftPool",
deleteAchievementDefinition: "deleteAchievementDefinition",
deleteActivityTemplate: "deleteActivityTemplate",
deleteAgency: "deleteAgency",
@@ -871,6 +873,13 @@ export const API_ENDPOINTS: Record = {
permission: "coin-seller:stock-credit",
permissions: ["coin-seller:stock-credit"]
},
+ creditLuckyGiftPool: {
+ method: "POST",
+ operationId: API_OPERATIONS.creditLuckyGiftPool,
+ path: "/v1/admin/ops-center/lucky-gifts/pools/credit",
+ permission: "lucky-gift:pool-credit",
+ permissions: ["lucky-gift:pool-credit"]
+ },
dashboardOverview: {
method: "GET",
operationId: API_OPERATIONS.dashboardOverview,
@@ -892,6 +901,13 @@ export const API_ENDPOINTS: Record = {
permission: "coin-seller:stock-credit",
permissions: ["coin-seller:stock-credit"]
},
+ debitLuckyGiftPool: {
+ method: "POST",
+ operationId: API_OPERATIONS.debitLuckyGiftPool,
+ path: "/v1/admin/ops-center/lucky-gifts/pools/debit",
+ permission: "lucky-gift:pool-debit",
+ permissions: ["lucky-gift:pool-debit"]
+ },
deleteAchievementDefinition: {
method: "DELETE",
operationId: API_OPERATIONS.deleteAchievementDefinition,
diff --git a/src/shared/api/generated/schema.d.ts b/src/shared/api/generated/schema.d.ts
index be01785..3b59929 100644
--- a/src/shared/api/generated/schema.d.ts
+++ b/src/shared/api/generated/schema.d.ts
@@ -324,6 +324,38 @@ export interface paths {
patch?: never;
trace?: never;
};
+ "/admin/ops-center/lucky-gifts/pools/credit": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ post: operations["creditLuckyGiftPool"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/admin/ops-center/lucky-gifts/pools/debit": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ post: operations["debitLuckyGiftPool"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
"/admin/activity/wheel/config": {
parameters: {
query?: never;
@@ -4680,6 +4712,64 @@ export interface paths {
export type webhooks = Record;
export interface components {
schemas: {
+ LuckyGiftPoolAdjustmentInput: {
+ adjustment_id: string;
+ /** Format: int64 */
+ amount_coins: number;
+ reason: string;
+ };
+ LuckyGiftPoolBalance: {
+ app_code: string;
+ pool_id: string;
+ /** @enum {string} */
+ strategy_version: "fixed_v2" | "dynamic_v3";
+ /** Format: int64 */
+ balance: number;
+ /** Format: int64 */
+ reserve_floor: number;
+ /** Format: int64 */
+ available_balance: number;
+ /** Format: int64 */
+ total_in: number;
+ /** Format: int64 */
+ total_out: number;
+ /** Format: int64 */
+ manual_credit_total: number;
+ /** Format: int64 */
+ manual_debit_total: number;
+ materialized: boolean;
+ /** Format: int64 */
+ updated_at_ms: number;
+ };
+ LuckyGiftPoolAdjustment: {
+ adjustment_id: string;
+ app_code: string;
+ pool_id: string;
+ /** @enum {string} */
+ strategy_version: "fixed_v2" | "dynamic_v3";
+ /** @enum {string} */
+ direction: "in" | "out";
+ /** Format: int64 */
+ amount_coins: number;
+ reason: string;
+ /** Format: int64 */
+ operator_admin_id: number;
+ /** Format: int64 */
+ balance_before: number;
+ /** Format: int64 */
+ balance_after: number;
+ /** Format: int64 */
+ created_at_ms: number;
+ };
+ LuckyGiftPoolAdjustmentResult: {
+ adjustment: components["schemas"]["LuckyGiftPoolAdjustment"];
+ pool: components["schemas"]["LuckyGiftPoolBalance"];
+ idempotent_replay: boolean;
+ };
+ ApiResponseLuckyGiftPoolAdjustmentResult: components["schemas"]["Envelope"] & {
+ requestId?: string;
+ data?: components["schemas"]["LuckyGiftPoolAdjustmentResult"];
+ };
ApiResponseAdminAppList: components["schemas"]["Envelope"] & {
data?: components["schemas"]["AdminAppList"];
};
@@ -7086,6 +7176,15 @@ export interface components {
"application/json": components["schemas"]["ApiResponseEmpty"];
};
};
+ /** @description 奖池水位调整成功或同幂等请求的首次结果 */
+ LuckyGiftPoolAdjustmentResponse: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["ApiResponseLuckyGiftPoolAdjustmentResult"];
+ };
+ };
/** @description OK */
RechargeBillPageResponse: {
headers: {
@@ -8007,6 +8106,74 @@ export interface operations {
200: components["responses"]["EmptyResponse"];
};
};
+ creditLuckyGiftPool: {
+ parameters: {
+ query: {
+ app_code: string;
+ pool_id: string;
+ strategy_version: "fixed_v2" | "dynamic_v3";
+ };
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["LuckyGiftPoolAdjustmentInput"];
+ };
+ };
+ responses: {
+ 200: components["responses"]["LuckyGiftPoolAdjustmentResponse"];
+ /** @description 调整参数不正确 */
+ 400: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description 幂等键冲突或可用水位不足 */
+ 409: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ };
+ };
+ debitLuckyGiftPool: {
+ parameters: {
+ query: {
+ app_code: string;
+ pool_id: string;
+ strategy_version: "fixed_v2" | "dynamic_v3";
+ };
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["LuckyGiftPoolAdjustmentInput"];
+ };
+ };
+ responses: {
+ 200: components["responses"]["LuckyGiftPoolAdjustmentResponse"];
+ /** @description 调整参数不正确 */
+ 400: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description 幂等键冲突或可用水位不足 */
+ 409: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ };
+ };
getWheelConfig: {
parameters: {
query?: never;