Add transfer tracking and secure admin access
This commit is contained in:
@@ -83,19 +83,22 @@ public class ParseController(AppDbContext db, ILlmClient llm, AgentService agent
|
||||
"expense"));
|
||||
}
|
||||
|
||||
var bill = drafts[0];
|
||||
var category = await db.Categories.FirstAsync(
|
||||
c => c.Id == bill.CategoryId && c.Type == bill.Type,
|
||||
ct);
|
||||
var bill = drafts[0];
|
||||
var category = await db.Categories.FirstAsync(
|
||||
c => c.Id == bill.CategoryId &&
|
||||
c.Type == bill.Type.CategoryType(bill.TransferDirection),
|
||||
ct);
|
||||
return Ok(new OcrParseResponse(
|
||||
true,
|
||||
category.Id,
|
||||
category.Name,
|
||||
category.IconKey,
|
||||
bill.Amount,
|
||||
bill.PaymentMethod,
|
||||
bill.Note,
|
||||
bill.Type == TransactionType.Income ? "income" : "expense"));
|
||||
bill.PaymentMethod,
|
||||
bill.Note,
|
||||
bill.Type.ToWire(),
|
||||
bill.TransferDirection.ToWire(),
|
||||
bill.Counterparty));
|
||||
}
|
||||
|
||||
/// <summary>上传截屏或小票,提取其中全部独立交易。</summary>
|
||||
@@ -171,18 +174,24 @@ public class ParseController(AppDbContext db, ILlmClient llm, AgentService agent
|
||||
foreach (var result in results.Take(20))
|
||||
{
|
||||
var normalizedType = result.Type.Trim().ToLowerInvariant();
|
||||
if (normalizedType is not ("income" or "expense"))
|
||||
var transferDirection = result.TransferDirection is "in" or "out"
|
||||
? result.TransferDirection
|
||||
: null;
|
||||
if (normalizedType is not ("income" or "expense" or "transfer") ||
|
||||
normalizedType == "transfer" && transferDirection is null)
|
||||
{
|
||||
items.Add(new ImageParseItemResponse(
|
||||
false, 0, "待确认", "tag", "unknown",
|
||||
result.Amount, result.PaymentMethod, result.Note,
|
||||
result.OccurredAt));
|
||||
result.OccurredAt,
|
||||
transferDirection,
|
||||
result.Counterparty));
|
||||
continue;
|
||||
}
|
||||
var type = normalizedType == "income"
|
||||
? TransactionType.Income
|
||||
: TransactionType.Expense;
|
||||
var category = FindCategory(categories, type, result.CategoryName);
|
||||
var categoryType = normalizedType == "income" || transferDirection == "in"
|
||||
? TransactionType.Income
|
||||
: TransactionType.Expense;
|
||||
var category = FindCategory(categories, categoryType, result.CategoryName);
|
||||
items.Add(new ImageParseItemResponse(
|
||||
true,
|
||||
category.Id,
|
||||
@@ -192,7 +201,9 @@ public class ParseController(AppDbContext db, ILlmClient llm, AgentService agent
|
||||
result.Amount,
|
||||
result.PaymentMethod,
|
||||
result.Note,
|
||||
result.OccurredAt));
|
||||
result.OccurredAt,
|
||||
transferDirection,
|
||||
result.Counterparty));
|
||||
}
|
||||
|
||||
var first = items[0];
|
||||
@@ -206,7 +217,9 @@ public class ParseController(AppDbContext db, ILlmClient llm, AgentService agent
|
||||
first.Note,
|
||||
first.Type,
|
||||
items,
|
||||
first.OccurredAt));
|
||||
first.OccurredAt,
|
||||
first.TransferDirection,
|
||||
first.Counterparty));
|
||||
}
|
||||
|
||||
[HttpPost("recognition-batch")]
|
||||
@@ -307,7 +320,12 @@ public class ParseController(AppDbContext db, ILlmClient llm, AgentService agent
|
||||
candidate.RecognitionKind,
|
||||
candidate.CategoryHint,
|
||||
candidate.Confidence,
|
||||
candidate.EvidenceIds ?? [])).ToList();
|
||||
candidate.EvidenceIds ?? [],
|
||||
candidate.TransferDirection,
|
||||
candidate.Counterparty,
|
||||
candidate.ProviderTransactionId,
|
||||
candidate.RecognitionOccurrenceId,
|
||||
candidate.IdentityConfidence)).ToList();
|
||||
|
||||
IReadOnlyList<RecognitionBatchModelAction>? modelActions;
|
||||
try
|
||||
@@ -366,11 +384,29 @@ public class ParseController(AppDbContext db, ILlmClient llm, AgentService agent
|
||||
action = "keep";
|
||||
reason = "撤销证据不足,已保留本地结果";
|
||||
}
|
||||
var type = model?.Type is "income" or "expense" ? model.Type : candidate.Type;
|
||||
var type = model?.Type is "income" or "expense" or "transfer"
|
||||
? model.Type
|
||||
: candidate.Type;
|
||||
var transferDirection = type == "transfer"
|
||||
? model?.TransferDirection is "in" or "out"
|
||||
? model.TransferDirection
|
||||
: candidate.TransferDirection is "in" or "out"
|
||||
? candidate.TransferDirection
|
||||
: null
|
||||
: null;
|
||||
if (type == "transfer" && transferDirection is null)
|
||||
{
|
||||
action = "keep";
|
||||
type = candidate.Type;
|
||||
transferDirection = candidate.TransferDirection;
|
||||
reason = "转账方向不明确,已保留本地结果";
|
||||
}
|
||||
var amount = model?.Amount is > 0 ? model.Amount.Value : candidate.Amount;
|
||||
var category = FindCategory(
|
||||
categories,
|
||||
type == "income" ? TransactionType.Income : TransactionType.Expense,
|
||||
type == "income" || transferDirection == "in"
|
||||
? TransactionType.Income
|
||||
: TransactionType.Expense,
|
||||
model?.CategoryName ?? candidate.CategoryHint ?? "其他");
|
||||
result.Add(new RecognitionBatchActionResponse(
|
||||
action,
|
||||
@@ -385,7 +421,11 @@ public class ParseController(AppDbContext db, ILlmClient llm, AgentService agent
|
||||
string.IsNullOrWhiteSpace(model?.Note) ? candidate.Merchant : model.Note,
|
||||
model?.OccurredAt ?? candidate.OccurredAt,
|
||||
confidence,
|
||||
reason));
|
||||
reason,
|
||||
transferDirection,
|
||||
string.IsNullOrWhiteSpace(model?.Counterparty)
|
||||
? candidate.Counterparty
|
||||
: model.Counterparty));
|
||||
}
|
||||
|
||||
var usedEvidence = new HashSet<string>();
|
||||
@@ -395,13 +435,16 @@ public class ParseController(AppDbContext db, ILlmClient llm, AgentService agent
|
||||
!evidenceById.TryGetValue(model.EvidenceId, out var evidence) ||
|
||||
!string.IsNullOrWhiteSpace(evidence.CandidateId) ||
|
||||
!usedEvidence.Add(model.EvidenceId) ||
|
||||
model.Type is not ("income" or "expense") ||
|
||||
model.Type is not ("income" or "expense" or "transfer") ||
|
||||
model.Type == "transfer" && model.TransferDirection is not ("in" or "out") ||
|
||||
model.Amount is not > 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var type = model.Type == "income" ? TransactionType.Income : TransactionType.Expense;
|
||||
var category = FindCategory(categories, type, model.CategoryName ?? "其他");
|
||||
var categoryType = model.Type == "income" || model.TransferDirection == "in"
|
||||
? TransactionType.Income
|
||||
: TransactionType.Expense;
|
||||
var category = FindCategory(categories, categoryType, model.CategoryName ?? "其他");
|
||||
result.Add(new RecognitionBatchActionResponse(
|
||||
"create",
|
||||
model.ActionId,
|
||||
@@ -415,7 +458,9 @@ public class ParseController(AppDbContext db, ILlmClient llm, AgentService agent
|
||||
model.Note,
|
||||
model.OccurredAt ?? evidence.CapturedAt,
|
||||
model.Confidence,
|
||||
model.Reason));
|
||||
model.Reason,
|
||||
model.TransferDirection,
|
||||
model.Counterparty));
|
||||
}
|
||||
return result.Take(20).ToList();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user