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:
2026-01-11 16:54:27 -05:00
parent de5ee33a77
commit 541bbf01af
2 changed files with 21 additions and 11 deletions

View File

@@ -13,10 +13,12 @@ namespace MoneyMap.Services
public class AIReceiptParser : IReceiptParser
{
private readonly MoneyMapContext _db;
private readonly IWebHostEnvironment _environment;
private readonly IReceiptManager _receiptManager;
private readonly IPdfToImageConverter _pdfConverter;
private readonly OpenAIVisionClient _openAIClient;
private readonly ClaudeVisionClient _claudeClient;
private readonly OllamaVisionClient _ollamaClient;
private readonly LlamaCppVisionClient _llamaCppClient;
private readonly IMerchantService _merchantService;
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<AIReceiptParser> _logger;
@@ -24,19 +26,23 @@ namespace MoneyMap.Services
public AIReceiptParser(
MoneyMapContext db,
IWebHostEnvironment environment,
IReceiptManager receiptManager,
IPdfToImageConverter pdfConverter,
OpenAIVisionClient openAIClient,
ClaudeVisionClient claudeClient,
OllamaVisionClient ollamaClient,
LlamaCppVisionClient llamaCppClient,
IMerchantService merchantService,
IServiceProvider serviceProvider,
ILogger<AIReceiptParser> logger)
{
_db = db;
_environment = environment;
_receiptManager = receiptManager;
_pdfConverter = pdfConverter;
_openAIClient = openAIClient;
_claudeClient = claudeClient;
_ollamaClient = ollamaClient;
_llamaCppClient = llamaCppClient;
_merchantService = merchantService;
_serviceProvider = serviceProvider;
_logger = logger;
@@ -52,10 +58,12 @@ namespace MoneyMap.Services
return ReceiptParseResult.Failure("Receipt not found.");
var selectedModel = model ?? "gpt-4o-mini";
var isLlamaCpp = selectedModel.StartsWith("llamacpp:");
var isOllama = selectedModel.StartsWith("ollama:");
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))
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.";
// 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);
if (!visionResult.IsSuccess)

View File

@@ -132,9 +132,8 @@ namespace MoneyMap.Services
await file.CopyToAsync(fileStream);
}
// Store relative path in database
var relativePath = _configuration["Receipts:StoragePath"] ?? "receipts";
var relativeStoragePath = $"{relativePath}/{storedFileName}";
// Store just the filename in database (base path comes from config)
var relativeStoragePath = storedFileName;
// Create receipt record
var receipt = new Receipt
@@ -327,8 +326,8 @@ namespace MoneyMap.Services
public string GetReceiptPhysicalPath(Receipt receipt)
{
// StoragePath is like "receipts/filename.jpg"
return Path.Combine(_environment.WebRootPath, receipt.StoragePath.Replace("/", Path.DirectorySeparatorChar.ToString()));
// StoragePath is just the filename, combine with configured base path
return Path.Combine(GetReceiptsBasePath(), receipt.StoragePath);
}
public async Task<Receipt?> GetReceiptAsync(long receiptId)