区分斗鱼充值审计方式

This commit is contained in:
yml2213
2026-08-14 13:29:54 +08:00
parent baed0e61ff
commit 644a9b118f
4 changed files with 46 additions and 4 deletions
+14 -1
View File
@@ -52,7 +52,20 @@ def _audit_log_out(db: Session, row: AuditLog) -> dict:
for task, account in accounts
]
detail["recharge_accounts_truncated"] = len(accounts) == 100
detail_text = json.dumps(detail, ensure_ascii=False, separators=(",", ":"))
if isinstance(detail, dict) and not detail.get("payment_method"):
batch_id = row.target.removeprefix("douyu_batch:")
task = (
db.query(DouyuTask)
.filter(DouyuTask.batch_id == batch_id, DouyuTask.task_type == "create_gold_qr")
.order_by(DouyuTask.id.asc())
.first()
)
task_result = task.result if task and isinstance(task.result, dict) else {}
recharge_channel = str(task_result.get("recharge_channel") or "wechat_qr")
detail["recharge_channel"] = recharge_channel
detail["payment_method"] = "API 直充支付" if recharge_channel == "supplier_api" else "微信扫码支付"
if isinstance(detail, dict):
detail_text = json.dumps(detail, ensure_ascii=False, separators=(",", ":"))
return {
"id": row.id,
"user_id": row.user_id,
+7
View File
@@ -497,6 +497,11 @@ async def create_task_batch(
raise HTTPException(status_code=400, detail="没有可执行的斗鱼账号,请先登录获取 Cookie")
if req.task_type == "create_gold_qr":
config = ensure_douyu_config(db)
recharge_channel = str(
douyu_config_value("gold_recharge_channel", config.gold_recharge_channel)
).strip()
payment_method = "API 直充支付" if recharge_channel == "supplier_api" else "微信扫码支付"
recharge_accounts = (
db.query(DouyuTask, Account)
.join(Account, Account.id == DouyuTask.account_id)
@@ -510,6 +515,8 @@ async def create_task_batch(
detail={
"batch_id": batch_id, "task_type": req.task_type, "count": count,
"account_count": len(req.account_ids), "handbook_scope": req.handbook_scope,
"recharge_channel": recharge_channel,
"payment_method": payment_method,
# 仅记录充值身份,禁止在审计中写入 Cookie、密码等凭据。
"recharge_accounts": [
{
+23 -3
View File
@@ -15,7 +15,7 @@ const ACTION_OPTIONS = [
{ value: 'recharge:yyb:payment', label: '应用宝生成付款码' },
{ value: 'recharge:yyb:payment_check', label: '应用宝检测到账' },
{ value: 'recharge:yyb:stop', label: '应用宝停止任务' },
{ value: 'recharge:douyu:create', label: '斗鱼创建充批次' },
{ value: 'recharge:douyu:create', label: '斗鱼创建充批次' },
{ value: 'recharge:douyu:callback', label: '斗鱼供应商回调' },
{ value: 'recharge:douyu:stop', label: '斗鱼停止直充批次' },
{ value: 'recharge:douyu:config', label: '斗鱼直充配置' },
@@ -24,10 +24,25 @@ const ACTION_OPTIONS = [
{ value: 'recharge:huya:config', label: '虎牙充值配置' },
];
const ACTION_LABELS = new Map(ACTION_OPTIONS.map((item) => [item.value, item.label]));
function formatTime(value: string): string {
return new Date(value).toLocaleString('zh-CN', { hour12: false });
}
function actionLabel(action: string): string {
return ACTION_LABELS.get(action) || action;
}
function paymentMethod(detail: string): string {
try {
const data = JSON.parse(detail) as Record<string, unknown>;
return typeof data.payment_method === 'string' ? data.payment_method : '-';
} catch {
return '-';
}
}
function detailSummary(detail: string): string {
try {
const data = JSON.parse(detail) as Record<string, unknown>;
@@ -90,8 +105,12 @@ export default function AuditLogsPage() {
const columns: ColumnsType<AuditLogEntry> = [
{ title: '时间', dataIndex: 'created_at', width: 180, render: formatTime },
{ title: '操作者', dataIndex: 'username', width: 120, render: (value) => value || '-' },
{ title: '操作', dataIndex: 'action', width: 210 },
{ title: '操作', dataIndex: 'action', width: 180, render: actionLabel },
{ title: '目标', dataIndex: 'target', width: 210, ellipsis: true },
{
title: '充值方式', dataIndex: 'detail', width: 130,
render: (detail: string) => paymentMethod(detail),
},
{
title: '结果', dataIndex: 'success', width: 88,
render: (value: boolean | null) => (
@@ -144,8 +163,9 @@ export default function AuditLogsPage() {
<Descriptions column={1} size="small" bordered>
<Descriptions.Item label="时间">{formatTime(selected.created_at)}</Descriptions.Item>
<Descriptions.Item label="操作者">{selected.username || '-'}</Descriptions.Item>
<Descriptions.Item label="操作">{selected.action}</Descriptions.Item>
<Descriptions.Item label="操作">{actionLabel(selected.action)}</Descriptions.Item>
<Descriptions.Item label="目标">{selected.target || '-'}</Descriptions.Item>
<Descriptions.Item label="充值方式">{paymentMethod(selected.detail)}</Descriptions.Item>
<Descriptions.Item label="结果">{selected.success === null ? '历史记录' : selected.success ? '成功' : '失败'}</Descriptions.Item>
<Descriptions.Item label="详情"><pre style={{ margin: 0, whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>{formatDetail(selected.detail)}</pre></Descriptions.Item>
</Descriptions>