feat(douyu): 拆分鱼翅积分与兑换记录查询

This commit is contained in:
yml2213
2026-10-23 10:42:42 +08:00
parent 2c87deed78
commit 2de5d374df
5 changed files with 359 additions and 105 deletions
+98
View File
@@ -19,6 +19,8 @@ class FakeClient:
self.points = points self.points = points
self.fish = fish self.fish = fish
self.records = records if records is not None else [] self.records = records if records is not None else []
self.records_calls = 0
self.orders_calls = 0
def elite_user_info(self, **_): def elite_user_info(self, **_):
return { return {
@@ -55,9 +57,11 @@ class FakeClient:
} }
def exchange_records(self, **_): def exchange_records(self, **_):
self.records_calls += 1
return {"records": self.records} return {"records": self.records}
def exchange_orders(self, **_): def exchange_orders(self, **_):
self.orders_calls += 1
return {"orders": []} return {"orders": []}
@@ -215,6 +219,96 @@ class TestDouyuCollabOrders:
assert result["points_reason"] == "shortage" assert result["points_reason"] == "shortage"
assert result["order"].status == collab.STATUS_POINTS_ERROR assert result["order"].status == collab.STATUS_POINTS_ERROR
def test_balance_query_skips_records_and_never_changes_status(self):
order = collab.create_order(
self.session,
self.admin,
order_no="DY-106",
shop="店铺A",
account_username="douyu-account-a",
gift_name="礼包A",
required_points=100,
home_images=["collab/home-5.png"],
client=FakeClient(),
)
collab.set_status(
self.session,
self.admin,
order,
status=collab.STATUS_RECHARGED,
reason="鱼翅已充值",
)
order.points_threshold = 10
self.session.commit()
client = FakeClient(points=50, records=[{"commodityName": "礼包A"}])
result = collab.query_activity(
self.session,
self.admin,
order,
client=client,
source="balance_batch",
include_records=False,
)
assert client.records_calls == 0
assert client.orders_calls == 0
assert result["scope"] == "balances"
assert result["verdict"] == collab.VERDICT_UNKNOWN
assert result["points_reason"] == "shortage"
assert result["order"].status == collab.STATUS_RECHARGED
assert result["metric"].source == "balance_batch"
assert result["metric"].records_total == 0
def test_balance_query_keeps_last_exchange_verdict_visible(self):
order = collab.create_order(
self.session,
self.admin,
order_no="DY-107",
shop="店铺A",
account_username="douyu-account-a",
gift_name="礼包A",
required_points=100,
home_images=["collab/home-6.png"],
client=FakeClient(),
)
collab.set_status(
self.session,
self.admin,
order,
status=collab.STATUS_RECHARGED,
reason="鱼翅已充值",
)
collab.set_status(
self.session,
self.admin,
order,
status=collab.STATUS_SCRIPTED,
reason="脚本已上",
)
record_result = collab.query_activity(
self.session,
self.admin,
order,
client=FakeClient(records=[{"commodityName": "礼包A"}]),
)
assert record_result["order"].status == collab.STATUS_DONE
collab.query_activity(
self.session,
self.admin,
order,
client=FakeClient(),
source="balance_batch",
include_records=False,
)
payload = collab.serialize_one(self.session, order)
assert payload["status"] == collab.STATUS_DONE
assert payload["last_verdict"] == collab.VERDICT_SUCCESS
assert payload["last_records_matched"] == 1
assert payload["last_metric_message"] == "仅查询鱼翅与积分,未查询兑换记录"
def test_draft_can_be_created_before_account_resolution(self): def test_draft_can_be_created_before_account_resolution(self):
order = collab.create_order( order = collab.create_order(
self.session, self.session,
@@ -579,6 +673,10 @@ class TestDouyuCollabOrders:
assert "/api/douyu/external-orders/ck/export" in paths assert "/api/douyu/external-orders/ck/export" in paths
assert "/api/douyu/external-orders/{order_id}/ck/copy" in paths assert "/api/douyu/external-orders/{order_id}/ck/copy" in paths
assert "/api/douyu/external-orders/usernames/export" in paths assert "/api/douyu/external-orders/usernames/export" in paths
assert "/api/douyu/external-orders/balances/query" in paths
assert "/api/douyu/external-orders/{order_id}/balances/query" in paths
assert "/api/douyu/external-orders/activity/query" in paths
assert "/api/douyu/external-orders/{order_id}/activity/query" in paths
assert "/api/douyu/collab-shops/{shop_id}" in paths assert "/api/douyu/collab-shops/{shop_id}" in paths
assert "put" in paths["/api/douyu/collab-shops/{shop_id}"] assert "put" in paths["/api/douyu/collab-shops/{shop_id}"]
assert "delete" in paths["/api/douyu/collab-shops/{shop_id}"] assert "delete" in paths["/api/douyu/collab-shops/{shop_id}"]
+75 -12
View File
@@ -79,6 +79,22 @@ def _publish_change(current: User, *, scope: str, action: str) -> None:
collab_events.publish(scope=scope, action=action, actor_id=int(current.id)) collab_events.publish(scope=scope, action=action, actor_id=int(current.id))
def _query_result_out(db: Session, result: dict) -> dict:
return {
"order": _one(db, result["order"]),
"metric": collab.serialize_metric(result["metric"]),
"scope": result["scope"],
"verdict": result["verdict"],
"message": result["message"],
"points_reason": result["points_reason"],
"fish_fin_balance": result["fish_fin_balance"],
"points_balance": result["points_balance"],
"required_total": result["required_total"],
"surplus": result["surplus"],
"errors": result["errors"],
}
@router.websocket("/external-orders/ws") @router.websocket("/external-orders/ws")
async def ws_external_orders(websocket: WebSocket): async def ws_external_orders(websocket: WebSocket):
"""协作台账实时失效通知:前端收到后按自己的筛选静默重拉。""" """协作台账实时失效通知:前端收到后按自己的筛选静默重拉。"""
@@ -400,18 +416,29 @@ def query_external_order_activity(
order = collab.get_order(db, order_id) order = collab.get_order(db, order_id)
result = collab.query_activity(db, current, order, source="manual") result = collab.query_activity(db, current, order, source="manual")
_publish_change(current, scope="orders", action="order.activity") _publish_change(current, scope="orders", action="order.activity")
return { return _query_result_out(db, result)
"order": _one(db, result["order"]), except collab.CollabError as exc:
"metric": collab.serialize_metric(result["metric"]), raise _http_error(exc) from exc
"verdict": result["verdict"],
"message": result["message"],
"points_reason": result["points_reason"], @router.post("/external-orders/{order_id}/balances/query")
"fish_fin_balance": result["fish_fin_balance"], def query_external_order_balances(
"points_balance": result["points_balance"], order_id: int,
"required_total": result["required_total"], db: Session = Depends(get_db),
"surplus": result["surplus"], current: User = Depends(require_permission("douyu:collab:edit")),
"errors": result["errors"], ):
} """只刷新鱼翅和手册积分,不查兑换记录、不判定、不改状态。"""
try:
order = collab.get_order(db, order_id)
result = collab.query_activity(
db,
current,
order,
source="balance_manual",
include_records=False,
)
_publish_change(current, scope="orders", action="order.balances")
return _query_result_out(db, result)
except collab.CollabError as exc: except collab.CollabError as exc:
raise _http_error(exc) from exc raise _http_error(exc) from exc
@@ -467,6 +494,42 @@ def set_external_order_status_batch(
return {"results": results, "success": all(item["success"] for item in results)} return {"results": results, "success": all(item["success"] for item in results)}
@router.post("/external-orders/balances/query")
def query_external_orders_balances(
ids: list[int],
db: Session = Depends(get_db),
current: User = Depends(require_permission("douyu:collab:edit")),
):
"""批量刷新鱼翅和手册积分,不查兑换记录、不判定、不改状态。"""
results: list[dict] = []
for order_id in ids:
try:
order = collab.get_order(db, order_id)
result = collab.query_activity(
db,
current,
order,
source="balance_batch",
include_records=False,
)
results.append(
{
"id": order_id,
"success": True,
"message": result["message"],
"fish_fin_balance": result["fish_fin_balance"],
"points_balance": result["points_balance"],
"points_reason": result["points_reason"],
}
)
except Exception as exc: # noqa: BLE001
db.rollback()
results.append({"id": order_id, "success": False, "message": str(exc)})
if any(item["success"] for item in results):
_publish_change(current, scope="orders", action="order.balances_batch")
return {"results": results, "success": all(item["success"] for item in results)}
@router.post("/external-orders/activity/query") @router.post("/external-orders/activity/query")
def query_external_orders_activity( def query_external_orders_activity(
ids: list[int], ids: list[int],
+88 -67
View File
@@ -1042,9 +1042,10 @@ def query_activity(
*, *,
client: Any | None = None, client: Any | None = None,
source: str = "manual", source: str = "manual",
include_records: bool = True,
commit: bool = True, commit: bool = True,
) -> dict[str, Any]: ) -> dict[str, Any]:
"""查询鱼翅、积分和当前账号兑换记录,落一条快照。""" """查询鱼翅、积分;``include_records`` 时同时查兑换记录并判定。"""
account = account_by_id(db, order.account_id) or account_by_username( account = account_by_id(db, order.account_id) or account_by_username(
db, order.account_username db, order.account_username
) )
@@ -1067,24 +1068,25 @@ def query_activity(
gold = active_client.gold_account() gold = active_client.gold_account()
except Exception as exc: # noqa: BLE001 except Exception as exc: # noqa: BLE001
errors.append(f"鱼翅: {exc}") errors.append(f"鱼翅: {exc}")
try: if include_records:
records = ( try:
active_client.exchange_records( records = (
manual_id=str(config.manual_id or ""), limit=100, offset=0 active_client.exchange_records(
).get("records") manual_id=str(config.manual_id or ""), limit=100, offset=0
or [] ).get("records")
) or []
except Exception as exc: # noqa: BLE001 )
errors.append(f"兑换记录: {exc}") except Exception as exc: # noqa: BLE001
try: errors.append(f"兑换记录: {exc}")
orders = ( try:
active_client.exchange_orders( orders = (
manual_id=str(config.manual_id or ""), page=1, page_size=10 active_client.exchange_orders(
).get("orders") manual_id=str(config.manual_id or ""), page=1, page_size=10
or [] ).get("orders")
) or []
except Exception as exc: # noqa: BLE001 )
errors.append(f"锁单: {exc}") except Exception as exc: # noqa: BLE001
errors.append(f"锁单: {exc}")
manual_score = _to_int(elite.get("manual_score")) manual_score = _to_int(elite.get("manual_score"))
fish = _to_int(gold.get("gold")) fish = _to_int(gold.get("gold"))
@@ -1107,7 +1109,11 @@ def query_activity(
threshold = normalize_threshold(order.points_threshold) threshold = normalize_threshold(order.points_threshold)
order.points_threshold = threshold order.points_threshold = threshold
if errors: matched = 0
if not include_records:
verdict = VERDICT_UNKNOWN
message = ";".join(errors) if errors else "仅查询鱼翅与积分,未查询兑换记录"
elif errors:
verdict = VERDICT_UNKNOWN verdict = VERDICT_UNKNOWN
message = ";".join(errors) message = ";".join(errors)
elif not order.gift_name: elif not order.gift_name:
@@ -1160,7 +1166,7 @@ def query_activity(
points_required_total=required_total, points_required_total=required_total,
points_surplus=surplus, points_surplus=surplus,
records_total=len(records), records_total=len(records),
records_matched=_match_gift(records, order.gift_name), records_matched=matched,
verdict=verdict, verdict=verdict,
message=message, message=message,
source=_text(source, max_length=16) or "manual", source=_text(source, max_length=16) or "manual",
@@ -1183,6 +1189,7 @@ def query_activity(
action="douyu:collab:activity", action="douyu:collab:activity",
target=_status_audit_target(order), target=_status_audit_target(order),
detail={ detail={
"scope": "activity" if include_records else "balances",
"verdict": verdict, "verdict": verdict,
"message": message, "message": message,
"fish_fin_balance": fish, "fish_fin_balance": fish,
@@ -1193,40 +1200,41 @@ def query_activity(
}, },
) )
# 已上脚本的单,兑换记录命中/未命中是业务事实;其它状态只记快照。 # 只有查兑换记录时才做业务判定;余额查询保持只读,不动状态。
if order.status == STATUS_SCRIPTED and verdict == VERDICT_SUCCESS: if include_records:
set_status( if order.status == STATUS_SCRIPTED and verdict == VERDICT_SUCCESS:
db, set_status(
current, db,
order, current,
status=STATUS_DONE, order,
reason=message, status=STATUS_DONE,
expected_version=None, reason=message,
commit=False, expected_version=None,
) commit=False,
elif order.status == STATUS_SCRIPTED and verdict == VERDICT_FAILED: )
set_status( elif order.status == STATUS_SCRIPTED and verdict == VERDICT_FAILED:
db, set_status(
current, db,
order, current,
status=STATUS_FAILED, order,
reason=message, status=STATUS_FAILED,
expected_version=None, reason=message,
commit=False, expected_version=None,
) commit=False,
elif order.status in (STATUS_RECHARGED, STATUS_SCRIPTED) and reason in ( )
"shortage", elif order.status in (STATUS_RECHARGED, STATUS_SCRIPTED) and reason in (
"excess", "shortage",
): "excess",
set_status( ):
db, set_status(
current, db,
order, current,
status=STATUS_POINTS_ERROR, order,
reason=points_message, status=STATUS_POINTS_ERROR,
expected_version=None, reason=points_message,
commit=False, expected_version=None,
) commit=False,
)
if commit: if commit:
db.commit() db.commit()
@@ -1235,6 +1243,7 @@ def query_activity(
return { return {
"order": order, "order": order,
"metric": metric, "metric": metric,
"scope": "activity" if include_records else "balances",
"verdict": verdict, "verdict": verdict,
"message": message, "message": message,
"points_reason": reason, "points_reason": reason,
@@ -1487,9 +1496,14 @@ def serialized_goods(db: Session) -> list[dict[str, Any]]:
def _metric_views( def _metric_views(
db: Session, order_ids: list[int] db: Session, order_ids: list[int]
) -> dict[int, DouyuExternalOrderMetric]: ) -> tuple[dict[int, DouyuExternalOrderMetric], dict[int, DouyuExternalOrderMetric]]:
"""返回最近一条快照,以及最近一条带兑换判定的快照。
余额查询也会落快照,但它的 verdict 是 unknown;列表上的兑换结论应沿用
最近一次「查兑换记录」的结果,不能被余额刷新覆盖。
"""
if not order_ids: if not order_ids:
return {} return {}, {}
rows = ( rows = (
db.query(DouyuExternalOrderMetric) db.query(DouyuExternalOrderMetric)
.filter(DouyuExternalOrderMetric.order_id.in_(order_ids)) .filter(DouyuExternalOrderMetric.order_id.in_(order_ids))
@@ -1499,10 +1513,14 @@ def _metric_views(
) )
.all() .all()
) )
result: dict[int, DouyuExternalOrderMetric] = {} latest: dict[int, DouyuExternalOrderMetric] = {}
verdict_latest: dict[int, DouyuExternalOrderMetric] = {}
for row in rows: for row in rows:
result.setdefault(int(row.order_id), row) order_id = int(row.order_id)
return result latest.setdefault(order_id, row)
if not str(row.source or "").startswith("balance"):
verdict_latest.setdefault(order_id, row)
return latest, verdict_latest
def account_clusters( def account_clusters(
@@ -1556,6 +1574,7 @@ def serialize_order(
accounts: Mapping[int, Account] | None = None, accounts: Mapping[int, Account] | None = None,
cluster: Mapping[str, Any] | None = None, cluster: Mapping[str, Any] | None = None,
metric: DouyuExternalOrderMetric | None = None, metric: DouyuExternalOrderMetric | None = None,
verdict_metric: DouyuExternalOrderMetric | None = None,
) -> dict[str, Any]: ) -> dict[str, Any]:
users = users or {} users = users or {}
accounts = accounts or {} accounts = accounts or {}
@@ -1630,16 +1649,16 @@ def serialize_order(
"account_cluster": dict(cluster) if cluster else None, "account_cluster": dict(cluster) if cluster else None,
"last_metric_at": metric.created_at if metric else None, "last_metric_at": metric.created_at if metric else None,
"last_metric_message": metric.message if metric else "", "last_metric_message": metric.message if metric else "",
"last_verdict": metric.verdict if metric else "", "last_verdict": verdict_metric.verdict if verdict_metric else "",
"last_verdict_label": { "last_verdict_label": {
VERDICT_SUCCESS: "兑换成功", VERDICT_SUCCESS: "兑换成功",
VERDICT_FAILED: "兑换失败", VERDICT_FAILED: "兑换失败",
VERDICT_UNKNOWN: "无法判定", VERDICT_UNKNOWN: "无法判定",
}.get(metric.verdict, "") }.get(verdict_metric.verdict, "")
if metric if verdict_metric
else "", else "",
"last_records_total": metric.records_total if metric else 0, "last_records_total": verdict_metric.records_total if verdict_metric else 0,
"last_records_matched": metric.records_matched if metric else 0, "last_records_matched": verdict_metric.records_matched if verdict_metric else 0,
} }
return payload return payload
@@ -1761,7 +1780,7 @@ def list_orders(
db, {int(order.account_id) for order in orders if order.account_id} db, {int(order.account_id) for order in orders if order.account_id}
) )
clusters = account_clusters(db, orders) clusters = account_clusters(db, orders)
metrics = _metric_views(db, [int(order.id) for order in orders]) metrics, verdict_metrics = _metric_views(db, [int(order.id) for order in orders])
items = [ items = [
serialize_order( serialize_order(
order, order,
@@ -1769,6 +1788,7 @@ def list_orders(
accounts=accounts, accounts=accounts,
cluster=clusters.get(int(order.account_id)) if order.account_id else None, cluster=clusters.get(int(order.account_id)) if order.account_id else None,
metric=metrics.get(int(order.id)), metric=metrics.get(int(order.id)),
verdict_metric=verdict_metrics.get(int(order.id)),
) )
for order in orders for order in orders
] ]
@@ -1802,13 +1822,14 @@ def serialize_one(db: Session, order: DouyuExternalOrder) -> dict[str, Any]:
db, {int(order.account_id)} if order.account_id else set() db, {int(order.account_id)} if order.account_id else set()
) )
clusters = account_clusters(db, [order]) clusters = account_clusters(db, [order])
metrics = _metric_views(db, [int(order.id)]) metrics, verdict_metrics = _metric_views(db, [int(order.id)])
return serialize_order( return serialize_order(
order, order,
users=users, users=users,
accounts=accounts, accounts=accounts,
cluster=clusters.get(int(order.account_id)) if order.account_id else None, cluster=clusters.get(int(order.account_id)) if order.account_id else None,
metric=metrics.get(int(order.id)), metric=metrics.get(int(order.id)),
verdict_metric=verdict_metrics.get(int(order.id)),
) )
+13 -2
View File
@@ -260,6 +260,7 @@ export interface DouyuCollabTimeline {
export interface DouyuCollabQueryResult { export interface DouyuCollabQueryResult {
order: DouyuCollabOrder; order: DouyuCollabOrder;
metric: DouyuCollabMetric; metric: DouyuCollabMetric;
scope: 'activity' | 'balances';
verdict: string; verdict: string;
message: string; message: string;
points_reason: string; points_reason: string;
@@ -347,9 +348,13 @@ export const douyuCollabApi = {
{ results: Array<{ id: number; success: boolean; ck_status?: string; role?: string; message?: string }>; success: boolean }, { results: Array<{ id: number; success: boolean; ck_status?: string; role?: string; message?: string }>; success: boolean },
{ results: Array<{ id: number; success: boolean; ck_status?: string; role?: string; message?: string }>; success: boolean } { results: Array<{ id: number; success: boolean; ck_status?: string; role?: string; message?: string }>; success: boolean }
>('/douyu/external-orders/verify/batch', ids), >('/douyu/external-orders/verify/batch', ids),
queryActivity: (id: number) => queryActivity: (id: number) => api.post<DouyuCollabQueryResult, DouyuCollabQueryResult>(
`/douyu/external-orders/${id}/activity/query`,
),
/** 只刷新鱼翅与手册积分,不查兑换记录、不判定、不改状态 */
queryBalances: (id: number) =>
api.post<DouyuCollabQueryResult, DouyuCollabQueryResult>( api.post<DouyuCollabQueryResult, DouyuCollabQueryResult>(
`/douyu/external-orders/${id}/activity/query`, `/douyu/external-orders/${id}/balances/query`,
), ),
setStatus: (id: number, data: { setStatus: (id: number, data: {
status: DouyuCollabStatus; status: DouyuCollabStatus;
@@ -371,6 +376,12 @@ export const douyuCollabApi = {
'/douyu/external-orders/activity/query', '/douyu/external-orders/activity/query',
ids, ids,
), ),
/** 批量只刷新鱼翅/积分,不查兑换记录、不判定、不改状态 */
batchBalances: (ids: number[]) =>
api.post<{ results: Array<Record<string, unknown>>; success: boolean }, { results: Array<Record<string, unknown>>; success: boolean }>(
'/douyu/external-orders/balances/query',
ids,
),
updateImages: (id: number, home_images: string[], expected_version?: number) => updateImages: (id: number, home_images: string[], expected_version?: number) =>
api.post<DouyuCollabOrder, DouyuCollabOrder>(`/douyu/external-orders/${id}/images`, { api.post<DouyuCollabOrder, DouyuCollabOrder>(`/douyu/external-orders/${id}/images`, {
home_images, home_images,
+85 -24
View File
@@ -38,6 +38,7 @@ import {
PlusOutlined, PlusOutlined,
ReloadOutlined, ReloadOutlined,
SafetyCertificateOutlined, SafetyCertificateOutlined,
SearchOutlined,
ShopOutlined, ShopOutlined,
SyncOutlined, SyncOutlined,
TagOutlined, TagOutlined,
@@ -214,6 +215,8 @@ export default function DouyuCollabPage() {
const [gifts, setGifts] = useState<string[]>([]); const [gifts, setGifts] = useState<string[]>([]);
const [goods, setGoods] = useState<DouyuCollabGoods[]>([]); const [goods, setGoods] = useState<DouyuCollabGoods[]>([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [balanceQuerying, setBalanceQuerying] = useState(false);
const [recordQuerying, setRecordQuerying] = useState(false);
const [goodsLoading, setGoodsLoading] = useState(false); const [goodsLoading, setGoodsLoading] = useState(false);
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(20); const [pageSize, setPageSize] = useState(20);
@@ -631,18 +634,35 @@ export default function DouyuCollabPage() {
} }
}; };
const queryOrder = async (order: DouyuCollabOrder) => { const queryOneBalances = async (order: DouyuCollabOrder) => {
try { try {
message.loading({ content: '正在查询鱼翅、积分与兑换记录…', key: 'dy-collab-query' }); message.loading({ content: '正在查询鱼翅、积分…', key: 'dy-collab-balance' });
const result = await douyuCollabApi.queryActivity(order.id); const result = await douyuCollabApi.queryBalances(order.id);
message.success({ message.success({
content: `${result.message}${result.points_reason ? `;${pointsReasonMeta(result.points_reason).label}` : ''}`, content: `鱼翅 ${result.fish_fin_balance ?? '-'} / 积分 ${result.points_balance ?? '-'}${
key: 'dy-collab-query', result.points_reason ? `;${pointsReasonMeta(result.points_reason).label}` : ''
}`,
key: 'dy-collab-balance',
}); });
refreshAll(); refreshAll();
if (detail?.id === order.id) void openDetail(order); if (detail?.id === order.id) void openDetail(order);
} catch (error) { } catch (error) {
message.error({ content: getErrorMessage(error), key: 'dy-collab-query' }); message.error({ content: getErrorMessage(error), key: 'dy-collab-balance' });
}
};
const queryOneRecords = async (order: DouyuCollabOrder) => {
try {
message.loading({ content: '正在查询兑换记录…', key: 'dy-collab-records' });
const result = await douyuCollabApi.queryActivity(order.id);
message.success({
content: `${result.message}${result.points_reason ? `;${pointsReasonMeta(result.points_reason).label}` : ''}`,
key: 'dy-collab-records',
});
refreshAll();
if (detail?.id === order.id) void openDetail(order);
} catch (error) {
message.error({ content: getErrorMessage(error), key: 'dy-collab-records' });
} }
}; };
@@ -716,21 +736,39 @@ export default function DouyuCollabPage() {
} }
}; };
const batchQuery = async () => { const batchQueryBalances = async () => {
if (selectedIds.length === 0) { if (selectedIds.length === 0) {
message.warning('请先勾选协作单'); message.warning('请先勾选协作单');
return; return;
} }
setLoading(true); setBalanceQuerying(true);
try { try {
const result = await douyuCollabApi.batchActivity(selectedIds); const result = await douyuCollabApi.batchBalances(selectedIds);
const ok = result.results.filter((item) => item.success).length; const ok = result.results.filter((item) => item.success).length;
message.success(`查询完成:${ok}/${result.results.length}`); message.success(`鱼翅/积分查询完成:${ok}/${result.results.length}`);
refreshAll(); refreshAll();
} catch (error) { } catch (error) {
message.error(getErrorMessage(error)); message.error(getErrorMessage(error));
} finally { } finally {
setLoading(false); setBalanceQuerying(false);
}
};
const batchQueryRecords = async () => {
if (selectedIds.length === 0) {
message.warning('请先勾选协作单');
return;
}
setRecordQuerying(true);
try {
const result = await douyuCollabApi.batchActivity(selectedIds);
const ok = result.results.filter((item) => item.success).length;
message.success(`兑换记录查询完成:${ok}/${result.results.length}`);
refreshAll();
} catch (error) {
message.error(getErrorMessage(error));
} finally {
setRecordQuerying(false);
} }
}; };
@@ -1158,7 +1196,7 @@ export default function DouyuCollabPage() {
}, },
{ {
title: '操作', title: '操作',
width: 400, width: 500,
fixed: 'right', fixed: 'right',
align: 'center', align: 'center',
render: (_, row) => ( render: (_, row) => (
@@ -1196,9 +1234,16 @@ export default function DouyuCollabPage() {
校验 校验
</Button> </Button>
</Tooltip> </Tooltip>
<Button size="small" icon={<SyncOutlined />} onClick={() => void queryOrder(row)}> <Tooltip title="只刷新鱼翅与手册积分,不查兑换记录,也不会改状态">
查询 <Button size="small" icon={<SyncOutlined />} onClick={() => void queryOneBalances(row)}>
</Button> 查鱼翅/积分
</Button>
</Tooltip>
<Tooltip title="查询当前账号兑换记录,命中礼包名后自动推进订单状态">
<Button size="small" icon={<SearchOutlined />} onClick={() => void queryOneRecords(row)}>
查兑换记录
</Button>
</Tooltip>
<Dropdown <Dropdown
menu={{ menu={{
items: [ items: [
@@ -1501,13 +1546,26 @@ export default function DouyuCollabPage() {
<Button icon={<CheckCircleOutlined />} disabled={selectedCount === 0} onClick={openBatchStatus}> <Button icon={<CheckCircleOutlined />} disabled={selectedCount === 0} onClick={openBatchStatus}>
批量改状态{selectedCount > 0 ? `(${selectedCount})` : ''} 批量改状态{selectedCount > 0 ? `(${selectedCount})` : ''}
</Button> </Button>
<Button <Tooltip title="只刷新鱼翅与手册积分,不查兑换记录,也不会改状态">
icon={<SyncOutlined />} <Button
disabled={selectedCount === 0 || loading} icon={<SyncOutlined />}
onClick={() => void batchQuery()} disabled={selectedCount === 0 || loading || balanceQuerying}
> loading={balanceQuerying}
批量查鱼翅/积分{selectedCount > 0 ? `(${selectedCount})` : ''} onClick={() => void batchQueryBalances()}
</Button> >
批量查鱼翅/积分{selectedCount > 0 ? `(${selectedCount})` : ''}
</Button>
</Tooltip>
<Tooltip title="查询勾选订单的兑换记录;命中礼包名后自动改状态,积分短缺/超额也会标记">
<Button
icon={<SearchOutlined />}
disabled={selectedCount === 0 || loading || recordQuerying}
loading={recordQuerying}
onClick={() => void batchQueryRecords()}
>
批量查兑换记录{selectedCount > 0 ? `(${selectedCount})` : ''}
</Button>
</Tooltip>
<Button <Button
icon={<InboxOutlined />} icon={<InboxOutlined />}
disabled={!canArchiveSelected || loading} disabled={!canArchiveSelected || loading}
@@ -1552,7 +1610,7 @@ export default function DouyuCollabPage() {
loading={loading} loading={loading}
columns={columns} columns={columns}
dataSource={orders} dataSource={orders}
scroll={{ x: 2090 }} scroll={{ x: 2190 }}
rowSelection={{ rowSelection={{
selectedRowKeys, selectedRowKeys,
onChange: (keys) => setSelectedRowKeys(keys), onChange: (keys) => setSelectedRowKeys(keys),
@@ -1933,9 +1991,12 @@ export default function DouyuCollabPage() {
<Button icon={<SafetyCertificateOutlined />} onClick={() => void verifyOrder(detail)}> <Button icon={<SafetyCertificateOutlined />} onClick={() => void verifyOrder(detail)}>
校验 校验
</Button> </Button>
<Button icon={<SyncOutlined />} onClick={() => void queryOrder(detail)}> <Button icon={<SyncOutlined />} onClick={() => void queryOneBalances(detail)}>
查鱼翅/积分 查鱼翅/积分
</Button> </Button>
<Button icon={<SearchOutlined />} onClick={() => void queryOneRecords(detail)}>
查兑换记录
</Button>
</Space> </Space>
) : undefined ) : undefined
} }