feat: 后台抽奖活动管理(Stage 1)

- 服务层:12 个 /v1/admin/lottery 接口封装与 API 类型定义
- 活动列表:状态筛选、上架/暂停(带确认)、编辑、管理、发次数
- 奖品管理:3×3 九宫格可视化编辑、概率预览、保底奖警告、佣金元↔分转换、无限库存
- 规则设置:chance_sources 动态编辑,eligibility 简单/高级(JSON)双模式,本地 rulecaps 校验(深度≤8/节点≤64/≤8KB),Stage 1 未接线警示
- 手动发次数:自动生成幂等 source_ref
- 路由 /dashboard/lottery、Commerce 菜单项、en-US/zh-CN 双语文案
- 对接文档见 vpn/aaaa.txt(Stage 1)
This commit is contained in:
2026-07-09 13:35:35 -07:00
parent 39bc0c5c7b
commit 01591adb86
18 changed files with 3315 additions and 0 deletions
+287
View File
@@ -0,0 +1,287 @@
/* eslint-disable */
import request from "@workspace/ui/lib/request";
/**
* 创建抽奖活动
* POST /v1/admin/lottery/activities
* @param body - 创建抽奖活动请求参数
* @param options - 可选的请求配置
* @returns 创建结果
*/
export async function createLotteryActivity(
body: API.CreateLotteryActivityRequest,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: API.LotteryActivity }>(
`${import.meta.env.VITE_API_PREFIX || ""}/v1/admin/lottery/activities`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
data: body,
...(options || {}),
}
);
}
/**
* 更新抽奖活动
* PUT /v1/admin/lottery/activities
* @param body - 更新抽奖活动请求参数
* @param options - 可选的请求配置
* @returns 更新结果
*/
export async function updateLotteryActivity(
body: API.UpdateLotteryActivityRequest,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: API.LotteryActivity }>(
`${import.meta.env.VITE_API_PREFIX || ""}/v1/admin/lottery/activities`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
data: body,
...(options || {}),
}
);
}
/**
* 获取抽奖活动分页列表
* GET /v1/admin/lottery/activities
* @param params - 分页及筛选参数
* @param options - 可选的请求配置
* @returns 抽奖活动列表及总数
*/
export async function getLotteryActivityList(
params: API.GetLotteryActivityListParams,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: API.GetLotteryActivityListResponse }>(
`${import.meta.env.VITE_API_PREFIX || ""}/v1/admin/lottery/activities`,
{
method: "GET",
params: {
...params,
},
...(options || {}),
}
);
}
/**
* 获取抽奖活动详情
* GET /v1/admin/lottery/activities/detail
* @param params - 活动 ID 参数
* @param options - 可选的请求配置
* @returns 抽奖活动详情
*/
export async function getLotteryActivityDetail(
params: API.GetLotteryActivityDetailParams,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: API.LotteryActivity }>(
`${import.meta.env.VITE_API_PREFIX || ""}/v1/admin/lottery/activities/detail`,
{
method: "GET",
params: {
...params,
},
...(options || {}),
}
);
}
/**
* 上架抽奖活动(draft → running
* POST /v1/admin/lottery/activities/publish
* @param body - 上架活动请求参数
* @param options - 可选的请求配置
* @returns 上架结果
*/
export async function publishLotteryActivity(
body: API.PublishLotteryActivityRequest,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: any }>(
`${import.meta.env.VITE_API_PREFIX || ""}/v1/admin/lottery/activities/publish`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
data: body,
...(options || {}),
}
);
}
/**
* 暂停抽奖活动
* POST /v1/admin/lottery/activities/pause
* @param body - 暂停活动请求参数
* @param options - 可选的请求配置
* @returns 暂停结果
*/
export async function pauseLotteryActivity(
body: API.PauseLotteryActivityRequest,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: any }>(
`${import.meta.env.VITE_API_PREFIX || ""}/v1/admin/lottery/activities/pause`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
data: body,
...(options || {}),
}
);
}
/**
* 更新抽奖活动规则(eligibility + chance_sources
* PUT /v1/admin/lottery/activities/rules
* @param body - 更新活动规则请求参数
* @param options - 可选的请求配置
* @returns 更新结果
*/
export async function updateLotteryActivityRules(
body: API.UpdateLotteryActivityRulesRequest,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: any }>(
`${import.meta.env.VITE_API_PREFIX || ""}/v1/admin/lottery/activities/rules`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
data: body,
...(options || {}),
}
);
}
/**
* 创建抽奖奖品
* POST /v1/admin/lottery/prizes
* @param body - 创建奖品请求参数
* @param options - 可选的请求配置
* @returns 创建结果
*/
export async function createLotteryPrize(
body: API.CreateLotteryPrizeRequest,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: API.LotteryPrize }>(
`${import.meta.env.VITE_API_PREFIX || ""}/v1/admin/lottery/prizes`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
data: body,
...(options || {}),
}
);
}
/**
* 更新抽奖奖品
* PUT /v1/admin/lottery/prizes
* @param body - 更新奖品请求参数
* @param options - 可选的请求配置
* @returns 更新结果
*/
export async function updateLotteryPrize(
body: API.UpdateLotteryPrizeRequest,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: API.LotteryPrize }>(
`${import.meta.env.VITE_API_PREFIX || ""}/v1/admin/lottery/prizes`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
data: body,
...(options || {}),
}
);
}
/**
* 删除抽奖奖品
* DELETE /v1/admin/lottery/prizes
* @param body - 删除奖品请求参数
* @param options - 可选的请求配置
* @returns 删除结果
*/
export async function deleteLotteryPrize(
body: API.DeleteLotteryPrizeRequest,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: any }>(
`${import.meta.env.VITE_API_PREFIX || ""}/v1/admin/lottery/prizes`,
{
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
data: body,
...(options || {}),
}
);
}
/**
* 获取抽奖奖品列表
* GET /v1/admin/lottery/prizes
* @param params - 活动 ID 筛选参数
* @param options - 可选的请求配置
* @returns 奖品列表
*/
export async function getLotteryPrizeList(
params: API.GetLotteryPrizeListParams,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: API.GetLotteryPrizeListResponse }>(
`${import.meta.env.VITE_API_PREFIX || ""}/v1/admin/lottery/prizes`,
{
method: "GET",
params: {
...params,
},
...(options || {}),
}
);
}
/**
* 手动发放抽奖次数
* POST /v1/admin/lottery/chances/grant
* @param body - 发放次数请求参数(source_ref 为幂等键)
* @param options - 可选的请求配置
* @returns 发放结果
*/
export async function grantLotteryChances(
body: API.GrantLotteryChancesRequest,
options?: { [key: string]: any }
) {
return request<API.Response & { data?: any }>(
`${import.meta.env.VITE_API_PREFIX || ""}/v1/admin/lottery/chances/grant`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
data: body,
...(options || {}),
}
);
}
+115
View File
@@ -3293,4 +3293,119 @@ declare namespace API {
};
type AdminWithdrawalLog = WithdrawalLog;
type LotteryPrizeType =
| 'vpn_duration'
| 'commission'
| 'none'
| 'crypto'
| 'physical'
| 'manual_other'
| 'coupon'
| 'points';
type LotteryActivityStatus = 'draft' | 'running' | 'paused' | 'ended';
type LotteryChanceSourceType =
| 'daily_signin'
| 'new_subscription'
| 'invite_success'
| 'manual_grant';
type LotteryEligibilityNode = {
op?: 'AND' | 'OR';
children?: LotteryEligibilityNode[];
type?: string;
params?: Record<string, any>;
};
type LotteryChanceSource = {
source: LotteryChanceSourceType;
amount: number;
daily_limit?: number;
};
type LotteryPrize = {
id: number;
activity_id: number;
slot: number;
type: LotteryPrizeType;
name: string;
icon_url: string;
config: Record<string, any>;
weight: number;
total_stock: number | null;
is_fallback: boolean;
sold_out?: boolean;
};
type LotteryActivity = {
id: number;
title: string;
description: string;
start_at: number; // Unix 秒
end_at: number; // Unix 秒
status: LotteryActivityStatus;
grid_size: number;
eligibility?: LotteryEligibilityNode | Record<string, never> | null;
chance_sources?: LotteryChanceSource[];
unmet_action?: string;
prizes?: LotteryPrize[];
};
type CreateLotteryActivityRequest = {
title: string;
description: string;
start_at: number;
end_at: number;
grid_size: number;
eligibility: LotteryEligibilityNode | Record<string, never> | null;
chance_sources: LotteryChanceSource[];
unmet_action: string;
};
type UpdateLotteryActivityRequest = CreateLotteryActivityRequest & { id: number };
type GetLotteryActivityListParams = { page: number; size: number; status?: string };
type GetLotteryActivityListResponse = { total: number; list: LotteryActivity[] };
type GetLotteryActivityDetailParams = { id: number };
type PublishLotteryActivityRequest = { id: number };
type PauseLotteryActivityRequest = { id: number };
type UpdateLotteryActivityRulesRequest = {
id: number;
eligibility: LotteryEligibilityNode | Record<string, never> | null;
chance_sources: LotteryChanceSource[];
};
type CreateLotteryPrizeRequest = {
activity_id: number;
slot: number;
type: LotteryPrizeType;
name: string;
icon_url: string;
config: Record<string, any>;
weight: number;
total_stock: number | null;
is_fallback: boolean;
};
type UpdateLotteryPrizeRequest = CreateLotteryPrizeRequest & { id: number };
type DeleteLotteryPrizeRequest = { id: number };
type GetLotteryPrizeListParams = { activity_id: number };
type GetLotteryPrizeListResponse = { total?: number; list: LotteryPrize[] };
type GrantLotteryChancesRequest = {
activity_id: number;
user_id: number;
amount: number;
source_ref: string;
};
}