增加兑换

This commit is contained in:
yml2213
2026-07-28 18:12:11 +08:00
parent 624198bca4
commit 8de269f958
11 changed files with 618 additions and 14 deletions
+1
View File
@@ -16,6 +16,7 @@ export const douyuApi = {
getConfig: () => api.get<DouyuConfig, DouyuConfig>('/douyu/config'),
updateConfig: (data: Partial<DouyuConfig>) => api.put<DouyuConfig, DouyuConfig>('/douyu/config', data),
listGoods: () => api.get<DouyuGoodsItem[], DouyuGoodsItem[]>('/douyu/goods'),
listEsportsGoods: () => api.get<DouyuGoodsItem[], DouyuGoodsItem[]>('/douyu/esports-goods'),
createTasks: (data: DouyuTaskBatchRequest) =>
api.post<DouyuTaskBatchResult, DouyuTaskBatchResult>('/douyu/tasks/batch', data),
listTasks: (batchId?: string) =>
+4
View File
@@ -217,6 +217,10 @@ export interface DouyuConfig {
esports_manual_id: string;
esports_act_alias: string;
esports_amount: number;
esports_chicken_gift_id: string;
esports_chicken_skin_id: string;
esports_firework_gift_id: string;
esports_firework_skin_id: string;
gold_pay_type: number;
gift_id: string;
skin_id: string;
+100 -7
View File
@@ -44,6 +44,13 @@ const ESPORTS_QUICK_ACTIONS = [
{ key: 'prepare_esports_bind', icon: <QrcodeOutlined /> },
{ key: 'query_esports_game_name', icon: <SearchOutlined /> },
{ key: 'create_esports_qr', icon: <CreditCardOutlined /> },
{ key: 'query_esports_points', icon: <SearchOutlined /> },
{ key: 'query_gold_balance', icon: <SearchOutlined /> },
{ key: 'refresh_esports_goods', icon: <ReloadOutlined /> },
{ key: 'exchange_esports_goods', icon: <ShoppingOutlined /> },
{ key: 'create_gold_qr', icon: <CreditCardOutlined /> },
{ key: 'donate_esports_chicken_gift', icon: <GiftOutlined /> },
{ key: 'donate_esports_firework_gift', icon: <GiftOutlined /> },
];
const ELITE_BIND_ACTION_KEYS = ['get_bind_qr', 'confirm_bind', 'query_game_name', 'query_change_bind_time'];
@@ -70,6 +77,13 @@ const ESPORTS_TASK_TYPES = new Set([
'query_esports_game_name',
'confirm_esports_bind',
'create_esports_qr',
'query_esports_points',
'query_gold_balance',
'refresh_esports_goods',
'exchange_esports_goods',
'create_gold_qr',
'donate_esports_chicken_gift',
'donate_esports_firework_gift',
]);
const DOUYU_GOLD_AMOUNT_STORAGE_KEY = 'douyu_task_gold_amount';
const DOUYU_GIFT_COUNT_STORAGE_KEY = 'douyu_task_gift_count';
@@ -97,6 +111,10 @@ const BIND_STATUS_LABELS: Record<string, string> = {
esports_bound: '电竞已绑定',
esports_qr_created: '电竞待开通',
esports_opened: '电竞已开通',
esports_points_queried: '已查电竞积分',
esports_gift_donated: '已赠电竞礼物',
esports_goods_refreshed: '已刷电竞皮肤',
esports_goods_exchanged: '已兑电竞皮肤',
change_time_queried: '已查换绑',
};
@@ -309,7 +327,7 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
try {
const [accResult, goodsResult, taskResult, cfgResult, typeResult] = await Promise.allSettled([
douyuApi.listAccounts(),
douyuApi.listGoods(),
isEsportsHandbook ? douyuApi.listEsportsGoods() : douyuApi.listGoods(),
douyuApi.listTasks(),
canConfig ? douyuApi.getConfig() : Promise.resolve(null),
douyuApi.taskTypes(),
@@ -475,14 +493,16 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
message.warning('当前已有批次在运行,请先停止或等待结束');
return;
}
if (taskType === 'exchange_goods' && !selectedGoodsId) {
if (['exchange_goods', 'exchange_esports_goods'].includes(taskType) && !selectedGoodsId) {
message.warning('请先选择兑换商品');
return;
}
const payload: Record<string, unknown> = { ...extraPayload };
if (taskType === 'exchange_goods') payload.commodity_id = selectedGoodsId;
if (['exchange_goods', 'exchange_esports_goods'].includes(taskType)) payload.commodity_id = selectedGoodsId;
if (taskType === 'create_gold_qr') payload.amount = goldAmount;
if (taskType === 'donate_elite_gift') payload.gift_count = giftCount;
if (['donate_elite_gift', 'donate_esports_chicken_gift', 'donate_esports_firework_gift'].includes(taskType)) {
payload.gift_count = giftCount;
}
try {
const result = await douyuApi.createTasks({
account_ids: ids,
@@ -798,10 +818,10 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
width: 80,
render: (v) => v ?? <Text type="secondary">-</Text>,
},
...(isEsportsHandbook ? [] : [{
{
title: '鱼翅', dataIndex: 'gold_balance', width: 70,
render: (v: number | null) => v ?? <Text type="secondary">-</Text>,
}]),
},
{
title: '换绑时间', dataIndex: 'change_role_wait_time', width: 130,
render: (_, record) => {
@@ -1001,7 +1021,7 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
const actionByKey = useMemo(() => new Map(quickActions.map((item) => [item.key, item])), [quickActions]);
const actionDisabled = (taskType: string) => {
if (batchBusy || selectedIds.length === 0) return true;
if (taskType === 'exchange_goods') return !selectedGoodsId;
if (['exchange_goods', 'exchange_esports_goods'].includes(taskType)) return !selectedGoodsId;
return false;
};
const renderActionButton = (taskType: string, type: 'default' | 'primary' = 'default') => {
@@ -1166,6 +1186,75 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
</div>
{renderActionButton('create_esports_qr', 'primary')}
</div>
<div style={sectionStyle}>
<div style={sectionTitleStyle}>
<SearchOutlined />
<span></span>
</div>
<div style={actionGridStyle}>
{renderActionButton('query_esports_points')}
{renderActionButton('query_gold_balance')}
</div>
</div>
<div style={sectionStyle}>
<div style={sectionTitleStyle}>
<ShoppingOutlined />
<span></span>
</div>
<Space direction="vertical" style={{ width: '100%' }} size={6}>
<Select
size="small"
value={selectedGoodsId || undefined}
onChange={setSelectedGoodsId}
options={goods.map((item) => ({ value: item.commodity_id, label: goodsLabel(item) }))}
placeholder="选择电竞皮肤"
showSearch
optionFilterProp="label"
style={{ width: '100%' }}
/>
<div style={actionGridStyle}>
{renderActionButton('refresh_esports_goods')}
{renderActionButton('exchange_esports_goods', 'primary')}
</div>
</Space>
</div>
<div style={sectionStyle}>
<div style={sectionTitleStyle}>
<CreditCardOutlined />
<span></span>
</div>
<Space direction="vertical" style={{ width: '100%' }} size={6}>
<Space.Compact style={{ width: '100%' }}>
<InputNumber
size="small"
min={1}
value={goldAmount}
onChange={(v) => setGoldAmount(v || 1)}
style={{ width: '100%' }}
/>
<Button
size="small"
icon={<CreditCardOutlined />}
onClick={() => startTask('create_gold_qr')}
disabled={actionDisabled('create_gold_qr')}
>
</Button>
</Space.Compact>
<InputNumber
size="small"
min={1}
value={giftCount}
onChange={(v) => setGiftCount(v || 1)}
addonAfter="赠送数量"
style={{ width: '100%' }}
/>
<div style={actionGridStyle}>
{renderActionButton('donate_esports_chicken_gift', 'primary')}
{renderActionButton('donate_esports_firework_gift', 'primary')}
</div>
</Space>
</div>
</>
) : (
<>
@@ -1354,6 +1443,10 @@ export default function DouyuTasksPage({ handbook }: { handbook: HandbookKind })
{ label: '电竞手册 manualID', key: 'esports_manual_id' },
{ label: '电竞手册活动', key: 'esports_act_alias' },
{ label: '房间 ID', key: 'room_id' },
{ label: '冠军鸡腿礼物 ID', key: 'esports_chicken_gift_id' },
{ label: '冠军鸡腿皮肤 ID', key: 'esports_chicken_skin_id' },
{ label: '冠军烟花礼物 ID', key: 'esports_firework_gift_id' },
{ label: '冠军烟花皮肤 ID', key: 'esports_firework_skin_id' },
] : [
{ label: 'manualID', key: 'manual_id' },
{ label: 'RID', key: 'rid' },