区分斗鱼充值审计方式
This commit is contained in:
@@ -97,6 +97,8 @@ class AuditLogTests(unittest.TestCase):
|
||||
detail = json.loads(result["items"][0]["detail"])
|
||||
self.assertEqual(detail["recharge_accounts"][0]["username"], "douyu-login")
|
||||
self.assertEqual(detail["recharge_accounts"][0]["douyu_uid"], "10001")
|
||||
self.assertEqual(detail["recharge_channel"], "wechat_qr")
|
||||
self.assertEqual(detail["payment_method"], "微信扫码支付")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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": [
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user