From 143a1b4507811306c34be68f01d62c9621191644 Mon Sep 17 00:00:00 2001 From: ntnt Date: Tue, 17 Mar 2026 22:33:13 +0800 Subject: [PATCH] fix: bugs-03 --- frontend/src/pages/review/Review.tsx | 110 ++++++++++- .../src/pages/screenshots/Screenshots.tsx | 183 ++++++++++-------- .../src/pages/transactions/Transactions.tsx | 65 ++++++- frontend/src/services/api.ts | 35 ++++ 4 files changed, 312 insertions(+), 81 deletions(-) diff --git a/frontend/src/pages/review/Review.tsx b/frontend/src/pages/review/Review.tsx index d505d46..7a86cf2 100644 --- a/frontend/src/pages/review/Review.tsx +++ b/frontend/src/pages/review/Review.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useMemo, useState } from 'react'; import { useParams } from 'react-router-dom'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { @@ -40,6 +40,7 @@ import { fetchInquirySuggestions, triggerAnalysis, fetchImageDetail, + updateTransaction, } from '../../services/api'; type ReviewAction = 'confirmed' | 'rejected' | 'needs_info'; @@ -146,6 +147,41 @@ const splitTradeTime = (raw: string): { date: string; time: string } => { return { date: datePart || '-', time: cleanedTime }; }; +const formatTradeTimeForRecord = (raw: string): string => { + const { date, time } = splitTradeTime(raw); + if (date === '-' && time === '-') return '时间不详'; + if (time === '-' || !time) return date; + return `${date} ${time}`; +}; + +const appTextMap: Record = { + wechat: '微信', + alipay: '支付宝', + bank: '银行', + digital_wallet: '数字钱包', + other: '其他', +}; + +const buildRecordAnswer = (assessments: FraudAssessment[]): string => { + if (!assessments.length) return '目前暂无可确认的转账信息,建议先完成截图上传、OCR识别及案件分析。'; + const sorted = [...assessments].sort((a, b) => + a.transaction.tradeTime.localeCompare(b.transaction.tradeTime), + ); + const lines = sorted.map((item, idx) => { + const tx = item.transaction; + const tradeTimeText = formatTradeTimeForRecord(tx.tradeTime); + const amount = Number(tx.amount || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2 }); + const direction = tx.direction === 'out' ? '转出' : '转入'; + const appName = appTextMap[tx.sourceApp] || '其他'; + const cpName = tx.counterpartyName || '未知对手方'; + const cpAccount = tx.counterpartyAccount ? `,对方账号${tx.counterpartyAccount}` : ''; + const orderNo = tx.orderNo ? `,订单号${tx.orderNo}` : ''; + const remark = tx.remark ? `,备注“${tx.remark}”` : ''; + return `第${idx + 1}笔是${tradeTimeText}通过${appName}${direction}人民币${amount}元至${cpName}${cpAccount}${orderNo}${remark}。`; + }); + return lines.join(''); +}; + const Review: React.FC = () => { const { id = '1' } = useParams(); const qc = useQueryClient(); @@ -224,6 +260,20 @@ const Review: React.FC = () => { content: '案件分析提交失败', }), }); + const saveTxMutation = useMutation({ + mutationFn: (params: { + txId: string; + body: Parameters[1]; + }) => updateTransaction(params.txId, params.body), + onSuccess: () => { + message.success('交易详情修改已保存'); + qc.invalidateQueries({ queryKey: ['assessments', id] }); + qc.invalidateQueries({ queryKey: ['transactions', id] }); + qc.invalidateQueries({ queryKey: ['flows', id] }); + qc.invalidateQueries({ queryKey: ['case', id] }); + }, + onError: () => message.error('交易详情保存失败'), + }); const data = filterLevel === 'all' @@ -242,6 +292,11 @@ const Review: React.FC = () => { const confirmedCount = allAssessments.filter( (a) => a.reviewStatus === 'confirmed', ).length; + const recordQAText = useMemo(() => { + const question = '问:具体的转账信息?'; + const answer = `答:${buildRecordAnswer(allAssessments)}`; + return `${question}\n${answer}`; + }, [allAssessments]); const openSupplementDialog = (assessment: FraudAssessment, initialNote?: string) => { setSupplementModal(assessment); @@ -661,6 +716,36 @@ const Review: React.FC = () => { )} + + + 笔录问答草稿 + + } + style={{ marginTop: 16 }} + extra={ + + } + > + + 根据当前账单明细自动生成,可直接用于笔录记录后再人工校对。 + + + + { > 关闭 +