Refactor: Simplify receipt storage path handling
Store only filename in database instead of full relative path. GetReceiptPhysicalPath now combines config base path with filename. AIReceiptParser uses ReceiptManager for consistent path resolution. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,10 +13,12 @@ namespace MoneyMap.Services
|
|||||||
public class AIReceiptParser : IReceiptParser
|
public class AIReceiptParser : IReceiptParser
|
||||||
{
|
{
|
||||||
private readonly MoneyMapContext _db;
|
private readonly MoneyMapContext _db;
|
||||||
private readonly IWebHostEnvironment _environment;
|
private readonly IReceiptManager _receiptManager;
|
||||||
private readonly IPdfToImageConverter _pdfConverter;
|
private readonly IPdfToImageConverter _pdfConverter;
|
||||||
private readonly OpenAIVisionClient _openAIClient;
|
private readonly OpenAIVisionClient _openAIClient;
|
||||||
private readonly ClaudeVisionClient _claudeClient;
|
private readonly ClaudeVisionClient _claudeClient;
|
||||||
|
private readonly OllamaVisionClient _ollamaClient;
|
||||||
|
private readonly LlamaCppVisionClient _llamaCppClient;
|
||||||
private readonly IMerchantService _merchantService;
|
private readonly IMerchantService _merchantService;
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
private readonly ILogger<AIReceiptParser> _logger;
|
private readonly ILogger<AIReceiptParser> _logger;
|
||||||
@@ -24,19 +26,23 @@ namespace MoneyMap.Services
|
|||||||
|
|
||||||
public AIReceiptParser(
|
public AIReceiptParser(
|
||||||
MoneyMapContext db,
|
MoneyMapContext db,
|
||||||
IWebHostEnvironment environment,
|
IReceiptManager receiptManager,
|
||||||
IPdfToImageConverter pdfConverter,
|
IPdfToImageConverter pdfConverter,
|
||||||
OpenAIVisionClient openAIClient,
|
OpenAIVisionClient openAIClient,
|
||||||
ClaudeVisionClient claudeClient,
|
ClaudeVisionClient claudeClient,
|
||||||
|
OllamaVisionClient ollamaClient,
|
||||||
|
LlamaCppVisionClient llamaCppClient,
|
||||||
IMerchantService merchantService,
|
IMerchantService merchantService,
|
||||||
IServiceProvider serviceProvider,
|
IServiceProvider serviceProvider,
|
||||||
ILogger<AIReceiptParser> logger)
|
ILogger<AIReceiptParser> logger)
|
||||||
{
|
{
|
||||||
_db = db;
|
_db = db;
|
||||||
_environment = environment;
|
_receiptManager = receiptManager;
|
||||||
_pdfConverter = pdfConverter;
|
_pdfConverter = pdfConverter;
|
||||||
_openAIClient = openAIClient;
|
_openAIClient = openAIClient;
|
||||||
_claudeClient = claudeClient;
|
_claudeClient = claudeClient;
|
||||||
|
_ollamaClient = ollamaClient;
|
||||||
|
_llamaCppClient = llamaCppClient;
|
||||||
_merchantService = merchantService;
|
_merchantService = merchantService;
|
||||||
_serviceProvider = serviceProvider;
|
_serviceProvider = serviceProvider;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -52,10 +58,12 @@ namespace MoneyMap.Services
|
|||||||
return ReceiptParseResult.Failure("Receipt not found.");
|
return ReceiptParseResult.Failure("Receipt not found.");
|
||||||
|
|
||||||
var selectedModel = model ?? "gpt-4o-mini";
|
var selectedModel = model ?? "gpt-4o-mini";
|
||||||
|
var isLlamaCpp = selectedModel.StartsWith("llamacpp:");
|
||||||
|
var isOllama = selectedModel.StartsWith("ollama:");
|
||||||
var isClaude = selectedModel.StartsWith("claude-");
|
var isClaude = selectedModel.StartsWith("claude-");
|
||||||
var provider = isClaude ? "Anthropic" : "OpenAI";
|
var provider = isLlamaCpp ? "LlamaCpp" : (isOllama ? "Ollama" : (isClaude ? "Anthropic" : "OpenAI"));
|
||||||
|
|
||||||
var filePath = Path.Combine(_environment.WebRootPath, receipt.StoragePath.Replace("/", Path.DirectorySeparatorChar.ToString()));
|
var filePath = _receiptManager.GetReceiptPhysicalPath(receipt);
|
||||||
|
|
||||||
if (!File.Exists(filePath))
|
if (!File.Exists(filePath))
|
||||||
return ReceiptParseResult.Failure("Receipt file not found on disk.");
|
return ReceiptParseResult.Failure("Receipt file not found on disk.");
|
||||||
@@ -96,7 +104,10 @@ namespace MoneyMap.Services
|
|||||||
promptText += "\n\nRespond ONLY with valid JSON, no other text.";
|
promptText += "\n\nRespond ONLY with valid JSON, no other text.";
|
||||||
|
|
||||||
// Call appropriate vision API
|
// Call appropriate vision API
|
||||||
var client = isClaude ? (IAIVisionClient)_claudeClient : _openAIClient;
|
IAIVisionClient client = isLlamaCpp ? _llamaCppClient
|
||||||
|
: isOllama ? _ollamaClient
|
||||||
|
: isClaude ? _claudeClient
|
||||||
|
: _openAIClient;
|
||||||
var visionResult = await client.AnalyzeImageAsync(base64Data, mediaType, promptText, selectedModel);
|
var visionResult = await client.AnalyzeImageAsync(base64Data, mediaType, promptText, selectedModel);
|
||||||
|
|
||||||
if (!visionResult.IsSuccess)
|
if (!visionResult.IsSuccess)
|
||||||
|
|||||||
@@ -132,9 +132,8 @@ namespace MoneyMap.Services
|
|||||||
await file.CopyToAsync(fileStream);
|
await file.CopyToAsync(fileStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store relative path in database
|
// Store just the filename in database (base path comes from config)
|
||||||
var relativePath = _configuration["Receipts:StoragePath"] ?? "receipts";
|
var relativeStoragePath = storedFileName;
|
||||||
var relativeStoragePath = $"{relativePath}/{storedFileName}";
|
|
||||||
|
|
||||||
// Create receipt record
|
// Create receipt record
|
||||||
var receipt = new Receipt
|
var receipt = new Receipt
|
||||||
@@ -327,8 +326,8 @@ namespace MoneyMap.Services
|
|||||||
|
|
||||||
public string GetReceiptPhysicalPath(Receipt receipt)
|
public string GetReceiptPhysicalPath(Receipt receipt)
|
||||||
{
|
{
|
||||||
// StoragePath is like "receipts/filename.jpg"
|
// StoragePath is just the filename, combine with configured base path
|
||||||
return Path.Combine(_environment.WebRootPath, receipt.StoragePath.Replace("/", Path.DirectorySeparatorChar.ToString()));
|
return Path.Combine(GetReceiptsBasePath(), receipt.StoragePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Receipt?> GetReceiptAsync(long receiptId)
|
public async Task<Receipt?> GetReceiptAsync(long receiptId)
|
||||||
|
|||||||
Reference in New Issue
Block a user