Handles receipt processing, spending analysis, and goal generation via a Python FastAPI microservice. It integrates OCR, Google’s generative AI model, and a simple merchant-category learner.
Defines the Python container build.
FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
RUN apt-get update \
&& apt-get install -y libgl1 libglib2.0-0 tesseract-ocr \
&& rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]- Base image: Python 3.10 slim
- Installs: Tesseract OCR & OpenCV libs
- Runs:
uvicorn main:app
Lists Python dependencies:
- fastapi
- uvicorn
- python-multipart
- opencv-python
- pytesseract
- google-generativeai
- numpy
- pandas
- scikit-learn
- python-dotenv
- requests
Wraps Google Gemini for:
- analyze_receipt: Extracts JSON from image
- analyze_spending: Generates advice
- generate_goals: Proposes savings goals
from typing import Dict, Any
import google.generativeai as genai
import os, json
class AIClient:
def __init__(self):
api_key = os.getenv("GOOGLE_API_KEY")
if api_key:
genai.configure(api_key=api_key)
self.model = genai.GenerativeModel('gemini-2.0-flash')
def analyze_receipt(self, image_data: bytes, mime_type: str) -> Dict[str, Any]:
prompt = """Analyze receipt and extract JSON..."""
response = self.model.generate_content([prompt, {"mime_type": mime_type, "data": image_data}])
return json.loads(response.text.strip().strip("```json").strip("```"))
# analyze_spending and generate_goals similarly...- Initializes Gemini-2.0
Maintains and updates merchant→category mappings.
class CategoryLearner:
def __init__(self):
self.merchant_categories = {}
self.load_mappings()
def load_mappings(self):
self.merchant_categories = {
# Food
"starbucks": "Food", "mcdonald's": "Food",
# Transport
"shell": "Transport",
# Utilities
"comcast": "Utilities",
# Entertainment
"netflix": "Entertainment",
# Shopping
"amazon": "Shopping",
# Health
"cvs": "Health"
}
def get_category(self, merchant_name: str) -> str:
if not merchant_name: return "Other"
name = merchant_name.lower().strip()
if name in self.merchant_categories:
return self.merchant_categories[name]
for m, cat in self.merchant_categories.items():
if m in name or name in m:
return cat
return "Other"- learn_mapping adds new pairs
Performs OCR preprocessing and text extraction.
import cv2, pytesseract, numpy as np
from fastapi import UploadFile
class OCRService:
async def extract_text(self, file: UploadFile) -> str:
contents = await file.read()
img = cv2.imdecode(np.frombuffer(contents, np.uint8), cv2.IMREAD_COLOR)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
denoised = cv2.fastNlMeansDenoising(gray)
_, thresh = cv2.threshold(denoised, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
return pytesseract.image_to_string(thresh)- Converts image → grayscale → threshold → Tesseract
Defines FastAPI endpoints:
- GET /: Health check
- POST /process_receipt: Runs OCR & AI, applies merchant-category overrides
- POST /analyze_spending: Spending advice
- POST /generate_goals: AI-driven goals
app = FastAPI()
ocr = OCRService()
ai_client = AIClient()
learner = CategoryLearner()
@app.post("/process_receipt")
async def process_receipt(file: UploadFile = File(...)):
data = ai_client.analyze_receipt(await file.read(), file.content_type)
cat = learner.get_category(data["merchant_name"])
# override categories...
return {"status": "success", "data": data}- Coordinates OCR, AIClient, CategoryLearner
sequenceDiagram
participant FE as Frontend
participant BE as Backend
participant AI as AI Service
participant OCR as OCRService
FE->>BE: POST /api/receipts/upload
BE->>AI: POST /process_receipt
AI->>OCR: extract_text()
AI->>AI: analyze_receipt()
AI->>BE: JSON data
BE->>DB: save Receipt/Transaction/Items
BE-->>FE: Receipt object
Implements REST API, persistence, and AI service integration.
Two-stage build: Maven → Corretto
FROM maven:3.9-eclipse-temurin-17 AS build
WORKDIR /app
COPY pom.xml . & src ./src
RUN mvn clean package -DskipTests
FROM amazoncorretto:17
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
ENTRYPOINT ["java","-jar","app.jar"]- Packages Spring Boot JAR
Defines dependencies:
| Artifact | Purpose |
|---|---|
| spring-boot-starter-data-jpa | JPA & Hibernate |
| spring-boot-starter-web | REST controllers |
| spring-boot-starter-validation | Request validation |
| postgresql | JDBC driver (runtime) |
| lombok (optional) | Boilerplate reduction |
| spring-boot-starter-test (test) | Testing |
Spring Boot entrypoint.
@SpringBootApplication
public class BackendApplication {
public static void main(String[] args) {
SpringApplication.run(BackendApplication.class, args);
}
}Serves uploaded files under /uploads/**.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry reg) {
reg.addResourceHandler("/uploads/**")
.addResourceLocations("file:uploads/");
}
}Manages spending data and AI suggestions.
@RestController
@RequestMapping("/api/analytics")
@CrossOrigin(origins="*")
public class AnalyticsController {
@Autowired private AnalyticsService analyticsService;
@GetMapping("/spending") …
@GetMapping("/categories") …
@GetMapping("/suggestions") …
}- Delegates to
AnalyticsService
{
"title": "Get Spending Trends",
"description": "Fetch daily, monthly, or yearly spending data for a user",
"method": "GET",
"baseUrl": "http://localhost:8080",
"endpoint": "/api/analytics/spending",
"queryParams": [
{"key": "userId", "value": "User ID", "required": true},
{"key": "period", "value": "day|month|year", "required": true}
],
"responses": {
"200": {
"description": "Spending list",
"body": "[{\"date\":\"2025-01-01\",\"total\":120.50}, …]"
},
"400": {"description": "Invalid period"}
}
}
{
"title": "Get Category Spending",
"description": "Fetch spending breakdown by category",
"method": "GET",
"baseUrl": "http://localhost:8080",
"endpoint": "/api/analytics/categories",
"queryParams": [{"key":"userId","value":"User ID","required":true}],
"responses": {
"200": {"description":"Category totals","body":"[{\"category\":\"Food\",\"total\":300.00}, …]"}
}
}
{
"title": "Get AI Spending Advice",
"description": "Retrieve AI-generated spending advice",
"method": "GET",
"baseUrl": "http://localhost:8080",
"endpoint": "/api/analytics/suggestions",
"queryParams": [{"key":"userId","value":"User ID","required":true}],
"responses": {
"200": {
"description":"Advice and tips",
"body":"{\"analysis\":\"You spent…\",\"tips\":[\"Reduce…\",\"Consider…\"]}"
}
}
}
CRUD & AI-powered goal generation.
@RestController
@RequestMapping("/api/goals")
@CrossOrigin(origins="*")
public class GoalController { … }
``]
- Uses `GoalService`
#### GET /api/goals
```api
{
"title": "List User Goals",
"description": "Get all goals for a user",
"method": "GET",
"baseUrl": "http://localhost:8080",
"endpoint": "/api/goals",
"queryParams":[{"key":"userId","value":"User ID","required":true}],
"responses":{"200":{"body":"[{\"id\":1,\"title\":\"Save\",\"targetAmount\":500.0,…}]"}}
}{
"title": "Create Goal",
"description": "Add a new savings goal",
"method": "POST",
"baseUrl": "http://localhost:8080",
"endpoint": "/api/goals",
"queryParams":[{"key":"userId","value":"User ID","required":true}],
"bodyType":"json",
"requestBody":"{\"title\":\"Vacation\",\"targetAmount\":1000.0,\"deadline\":\"2025-06-01\"}",
"responses":{"200":{"body":"{\"id\":2,\"title\":\"Vacation\",…}" }}
}
{
"title": "Update Goal",
"description": "Modify existing goal amounts or status",
"method":"PUT",
"baseUrl":"http://localhost:8080",
"endpoint":"/api/goals/2",
"pathParams":[{"key":"id","value":"Goal ID","required":true}],
"bodyType":"json",
"requestBody":"{\"currentAmount\":200.0}",
"responses":{"200":{"body":"{\"id\":2,\"currentAmount\":200.0,…}" }}
}
{
"title":"AI Generate Goals",
"description":"Generate personalized goals via AI",
"method":"POST",
"baseUrl":"http://localhost:8080",
"endpoint":"/api/goals/generate",
"queryParams":[{"key":"userId","value":"User ID","required":true}],
"responses":{"200":{"body":"{\"category_breakdown\":[…],\"monthly_trend\":[…]}" }}
}
Handles receipt upload & retrieval.
@RestController
@RequestMapping("/api/receipts")
@CrossOrigin(origins="*")
public class ReceiptController { … }- Relies on
ReceiptServiceImpl
{
"title":"Upload Receipt",
"description":"Upload an image for AI processing",
"method":"POST",
"baseUrl":"http://localhost:8080",
"endpoint":"/api/receipts/upload",
"formData":[
{"key":"userId","value":"User ID","required":true},
{"key":"file","value":"Image file","required":true}
],
"responses":{
"200":{"body":"{\"id\":5,\"status\":\"PROCESSING\",\"imageUrl\":\"uploads/…\"}"}
}
}
{
"title":"List Receipts",
"description":"Get all receipts for a user",
"method":"GET",
"baseUrl":"http://localhost:8080",
"endpoint":"/api/receipts/user/1",
"pathParams":[{"key":"userId","value":"User ID","required":true}],
"responses":{"200":{"body":"[{\"id\":5,…}, …]"}}
}
{
"title":"Get Receipt",
"description":"Retrieve one receipt and its transaction",
"method":"GET",
"baseUrl":"http://localhost:8080",
"endpoint":"/api/receipts/5",
"pathParams":[{"key":"id","value":"Receipt ID","required":true}],
"responses":{"200":{"body":"{\"id\":5,\"transaction\":{…}}" }}
}
Holds AI output for receipts:
status: success|failedraw_text: textual OCR fallbackdata:merchant_name,date,total_amount,currency,items[]
| Class | Table | Relationships |
|---|---|---|
| User | users | 1—* Receipts, 1—* Goals |
| Receipt | receipts | *—1 User, 1—1 Transaction |
| Transaction | transactions | 1—* Items, 1—1 Receipt |
| Item | items | *—1 Transaction |
| Goal | goals | *—1 User, tracks progress & status |
| ReceiptStatus | — | Enum: PROCESSING, COMPLETED, FAILED |
| GoalStatus | — | Enum: ACTIVE, COMPLETED, CANCELLED |
erDiagram
USERS ||--o{ RECEIPTS : has
RECEIPTS ||--o{ TRANSACTIONS : links
TRANSACTIONS ||--o{ ITEMS : contains
USERS ||--o{ GOALS : sets
Spring Data JPA repos for CRUD & custom queries:
UserRepositoryReceiptRepository(findByUser_Id)TransactionRepository(daily/monthly/yearly/category queries)ItemRepositoryGoalRepository(findByUser_Id)
| Service | Role |
|---|---|
| ReceiptServiceImpl | Saves files, calls AI, builds entities |
| PythonAIServiceClient | Bridges backend ↔ AI Service via REST |
| AnalyticsService | Aggregates spending data, delegates AI suggestions |
| GoalService | CRUD goals, triggers AI-generated goals |
Configures DB & AI service URL:
spring.datasource.url=jdbc:postgresql://localhost:5432/finance_db
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.jpa.hibernate.ddl-auto=update
# AI Service
ai.service.url=http://ai-service:8000/process_receiptBuilt with Vite + React + Tailwind; consumes the backend REST API.
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 5173
CMD ["npm","run","dev","--","--host"]Defines scripts, React, Recharts, Framer Motion, Lucide icons, Vite:
{
"scripts": {"dev":"vite","build":"tsc - b && vite build"},
"dependencies": {
"react": "^18.x","react-dom":"^18.x","react-router-dom":"^7.x",
"recharts":"^3.x","framer-motion":"^11.x","lucide-react":"^0.x"
}
}Mounts React <div id="root"> and loads main.tsx.
Enables Tailwind + autoprefixer; custom stripe color palette and animations.
Sets up React plugin:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({ plugins: [react()] });Centralizes API calls and TypeScript interfaces:
uploadReceipt(userId, file)getUserReceipts(userId)getSpending(userId, period)getCategorySpending(userId)getSuggestions(userId)getGoals,createGoal,updateGoal,generateGoals
export interface Receipt { id:number; status:string; imageUrl:string; transaction?:Transaction; }
export const uploadReceipt = async (...) => { /* FormData POST */ }Bootstraps React app:
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css';
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode><App/></React.StrictMode>
);Defines routing layout:
/: Upload & Dashboard/analytics: AnalyticsPage/insights: InsightsPage/goals: GoalsPage
Nav uses react-router-dom with active styles.
Drag-and-drop / file-picker upload UI with status indicators. Calls uploadReceipt and triggers refresh on success.
Displays recent receipts in a responsive grid. Shows status badges, thumbnails, transaction summary.
Renders spending trends via recharts Line/Bar charts. Switches between day/month/year.
Shows category pie chart and AI suggestions. Fetches both in parallel.
Lists user goals, lets users create manually or via AI. Includes progress bars and deadlines.
C4Container
title Personal Finance AI System
Person(user, "End User", "Uses the web UI")
System(frontend, "Frontend (React + Vite)", "Renders UI, calls API")
System(backend, "Backend (Spring Boot)", "Handles business logic, persistence")
System(aiservice, "AI Service (FastAPI)", "OCR + Generative AI")
SystemDb(db, "PostgreSQL", "Stores receipts, transactions, goals, users")
Rel(user, frontend, "Interacts with")
Rel(frontend, backend, "REST API", "JSON/HTTP")
Rel(backend, aiservice, "AI calls", "JSON/HTTP")
Rel(backend, db, "JPA", "SQL")
This documentation covers all listed files, their roles, and interdependencies across AI service, backend, and frontend layers.
This section adds UML 2.5 compliant PlantUML diagrams for the whole system. It complements the previous Mermaid views with a different notation.
This diagram shows how a user interacts with the system and its main features. It focuses on user goals rather than implementation details.
@startuml
left to right direction
actor User
rectangle "Personal Finance AI System" {
usecase "Upload Receipt" as UC_Upload
usecase "View Receipts Dashboard" as UC_Dashboard
usecase "View Spending Analytics" as UC_Analytics
usecase "View AI Insights" as UC_Insights
usecase "Manage Savings Goals" as UC_Goals
usecase "Generate AI Goals" as UC_AI_Goals
}
User --> UC_Upload
User --> UC_Dashboard
User --> UC_Analytics
User --> UC_Insights
User --> UC_Goals
User --> UC_AI_Goals
@endumlThis diagram captures the main backend and AI service classes. It emphasizes domain entities, services, controllers, and repositories.
@startuml
skinparam packageStyle rectangle
package "backend.model" {
class User {
+Long id
+String username
+String passwordHash
+String email
}
class Receipt {
+Long id
+String imageUrl
+ReceiptStatus status
+LocalDateTime createdAt
+Transaction transaction
+User user
}
enum ReceiptStatus {
PROCESSING
COMPLETED
FAILED
}
class Transaction {
+Long id
+String merchantName
+LocalDate transactionDate
+BigDecimal totalAmount
+String currency
+List<Item> items
+Receipt receipt
}
class Item {
+Long id
+String description
+Integer quantity
+BigDecimal unitPrice
+String category
+Transaction transaction
}
class Goal {
+Long id
+String title
+Double targetAmount
+Double currentAmount
+String category
+LocalDate deadline
+GoalStatus status
+User user
}
enum GoalStatus {
ACTIVE
COMPLETED
CANCELLED
}
}
package "backend.dto" {
class AIAnalysisResponse {
+String status
+String raw_text
+ExtractedData data
}
class ExtractedData {
+String merchant_name
+String date
+BigDecimal total_amount
+String currency
+List<ExtractedItem> items
}
class ExtractedItem {
+String description
+Integer quantity
+BigDecimal unit_price
+String category
}
}
package "backend.repository" {
interface UserRepository
interface ReceiptRepository
interface TransactionRepository
interface ItemRepository
interface GoalRepository
}
package "backend.service" {
class ReceiptServiceImpl
interface ReceiptService
class AnalyticsService
class GoalService
class PythonAIServiceClient
}
package "backend.controller" {
class ReceiptController
class AnalyticsController
class GoalController
}
package "ai-service" {
class AIClient
class OCRService
class CategoryLearner
}
User "1" -- "0..*" Receipt
User "1" -- "0..*" Goal
Receipt "1" -- "0..1" Transaction
Transaction "1" -- "0..*" Item
ReceiptRepository "1" ..> Receipt : manages
TransactionRepository "1" ..> Transaction : manages
ItemRepository "1" ..> Item : manages
GoalRepository "1" ..> Goal : manages
UserRepository "1" ..> User : manages
ReceiptServiceImpl ..|> ReceiptService
ReceiptServiceImpl --> ReceiptRepository
ReceiptServiceImpl --> UserRepository
ReceiptServiceImpl --> PythonAIServiceClient
AnalyticsService --> TransactionRepository
AnalyticsService --> PythonAIServiceClient
GoalService --> GoalRepository
GoalService --> UserRepository
GoalService --> AnalyticsService
GoalService --> PythonAIServiceClient
ReceiptController --> ReceiptService
AnalyticsController --> AnalyticsService
GoalController --> GoalService
PythonAIServiceClient ..> AIAnalysisResponse
PythonAIServiceClient ..> "ai-service" : HTTP
AIClient ..> CategoryLearner
AIClient ..> OCRService
@endumlThis sequence diagram details the flow when a user uploads a receipt. It shows interactions across frontend, backend, AI service, and database.
@startuml
actor User
participant "React Frontend" as FE
participant "ReceiptController" as RC
participant "ReceiptServiceImpl" as RS
participant "PythonAIServiceClient" as PY
participant "AI Service\n(FastAPI)" as AIS
participant "AIClient" as AIC
database "PostgreSQL" as DB
User -> FE : Select receipt image
FE -> RC : POST /api/receipts/upload\nmultipart(userId, file)
RC -> RS : uploadReceipt(userId, file)
RS -> RS : Store file on disk\nuploads/<uuid>_file.jpg
RS -> RS : Create Receipt(status=PROCESSING)
RS -> PY : analyzeReceipt(imagePath)
PY -> AIS : POST /process_receipt\nmultipart(file)
AIS -> AIC : analyze_receipt(bytes, mime)
AIC -> AIC : Call Gemini model\nparse JSON
AIC --> AIS : Structured JSON
AIS --> PY : AIAnalysisResponse JSON
PY --> RS : AIAnalysisResponse
RS -> RS : Map AI data to\nTransaction and Items
RS -> RS : Update Receipt\nstatus COMPLETED or FAILED
RS -> DB : save(Receipt, Transaction, Items)
RS --> RC : Receipt DTO
RC --> FE : 200 OK + Receipt JSON
FE --> User : Show processing state\nand new card
@endumlThis activity diagram describes the receipt processing workflow. It focuses on decisions and actions from upload through AI enrichment.
@startuml
start
:User uploads receipt\nvia frontend;
:Backend receives\nmultipart request;
:Store file on disk;
:Create Receipt\nstatus=PROCESSING;
:Call PythonAIServiceClient\nanalyzeReceipt;
if (AI service call successful?) then (yes)
if (AI status == success?) then (yes)
:Create Transaction\nfrom AIAnalysisResponse;
:Create Items for\neach AI item;
:Attach Transaction\nto Receipt;
:Set Receipt status\nCOMPLETED;
else (no)
:Set Receipt status\nFAILED;
endif
else (no)
:Set Receipt status\nFAILED;
endif
:Persist Receipt,\nTransaction, Items;
stop
@endumlThis state machine models the lifecycle of a Receipt. It uses the ReceiptStatus enum and transitions based on processing outcomes.
@startuml
[*] --> PROCESSING : uploadReceipt()
state PROCESSING {
}
PROCESSING --> COMPLETED : AI analysis success
PROCESSING --> FAILED : AI analysis failure
PROCESSING --> FAILED : AI service error
COMPLETED --> COMPLETED : read-only usage
FAILED --> FAILED : read-only usage
COMPLETED --> [*]
FAILED --> [*]
@endumlThis diagram shows the main deployable components and their interfaces. It abstracts away internal classes and focuses on services.
@startuml
rectangle "User Browser" as Browser
component "React Frontend\n(Vite SPA)" as Frontend
component "Spring Boot Backend\nREST API" as Backend
component "AI Service\nFastAPI + Gemini" as AISvc
database "PostgreSQL\nDatabase" as DB
Browser --> Frontend : HTTPS\nSPA assets
Frontend --> Backend : HTTP JSON\n/api/...
Backend --> AISvc : HTTP JSON\n/process_receipt\n/analyze_spending\n/generate_goals
Backend --> DB : JDBC\nJPA queries
@endumlThis deployment diagram represents runtime nodes and their deployed artifacts. It reflects the docker-compose setup with separate containers.
@startuml
node "User Device" {
artifact "Web Browser" as Browser
}
node "Docker Host" {
node "frontend container\nnode:18-alpine" as FrontendNode {
artifact "Vite Dev Server\nReact SPA" as FrontendApp
}
node "backend container\namazoncorretto:17" as BackendNode {
artifact "Spring Boot JAR\nbackend-0.0.1.jar" as BackendJar
}
node "ai-service container\npython:3.10-slim" as AINode {
artifact "FastAPI app\nmain.py" as AIApp
}
node "db container\npostgres:15-alpine" as DBNode {
database "PostgreSQL\nfinance_db" as DB
}
}
Browser --> FrontendApp : HTTPS 5173
FrontendApp --> BackendJar : HTTP 8080
BackendJar --> AIApp : HTTP 8000
BackendJar --> DB : JDBC 5432
@endumlThis section explains the architecture and codebase structure. It describes each layer, its classes, and their interactions.
The system follows a classic three-tier architecture with an additional AI microservice. The tiers are frontend, backend, and database, with AI as an external service.
- The frontend is a React single-page application using Vite and Tailwind CSS.
- The backend is a Spring Boot REST API exposing endpoints for receipts, analytics, and goals.
- The AI service is a Python FastAPI app wrapping OCR and the Gemini model.
- The database is PostgreSQL, accessed through Spring Data JPA repositories.
The AI service encapsulates all machine intelligence. It combines OCR, heuristic categorization, and generative AI.
main.pydefines the FastAPI application and three endpoints./process_receiptreceives an image, callsAIClient.analyze_receipt, and appliesCategoryLearner./analyze_spendingand/generate_goalsaccept JSON and delegate toAIClientmethods.AIClientconfigures the Gemini client, sends prompts, and parses structured JSON.OCRServiceprepares images and extracts text using OpenCV and Tesseract, though current receipt processing is multimodal.CategoryLearnermaps merchant names to spending categories using simple string matching.
The backend layer exposes REST APIs, persists data, and coordinates AI invocations. Configuration files define environment-specific behavior.
BackendApplicationis the Spring Boot entry point and triggers component scanning.WebConfigmaps the/uploads/**URL path to the localuploads/directory.application.propertiesconfigures PostgreSQL connection and AI service URL.
The domain model represents users, receipts, transactions, items, and goals. JPA annotations map each class to a table.
Userrepresents application users with unique username and email.Receiptstores metadata for uploaded receipt images, including file path and status.Transactionrepresents a financial transaction derived from a receipt and holds merchant, date, amount, and currency.Itemmodels individual line items associated with a transaction, including quantity and per-unit price.Goalcaptures a savings goal with target, current amount, category, deadline, and status.ReceiptStatusandGoalStatusenumerate possible lifecycle states for receipts and goals.
JPA relationships wire these entities.
- Each
Userhas manyReceiptandGoalinstances. - Each
Receipthas at most oneTransaction. - Each
Transactioncan have manyItementities. - JSON back-reference annotations prevent infinite recursion during serialization.
The repository layer abstracts persistence details with Spring Data JPA. Each repository interface exposes CRUD and custom queries.
UserRepositoryprovides basic user queries and afindByUsernamemethod.ReceiptRepositoryoffers CRUD for receipts andfindByUser_Id.TransactionRepositorycontains JPQL queries for daily, monthly, yearly, and category spending aggregations.ItemRepositoryhandles item persistence with generic JPA methods.GoalRepositoryenables CRUD andfindByUser_Idfor goals.
These repositories are injected into services. They allow services to remain persistence-agnostic.
The backend uses a dedicated DTO to isolate AI response shape. A separate service handles HTTP calls to the Python AI service.
AIAnalysisResponsemirrors the JSON returned by/process_receipt.- It contains nested
ExtractedDataandExtractedItemclasses matching the AI schema. PythonAIServiceClientusesRestTemplateto call AI endpoints.analyzeReceiptsends a multipart request with a file and maps JSON toAIAnalysisResponse.getSpendingAdviceandgenerateGoalspost JSON to other AI endpoints and receive generic maps.
This design decouples the backend from direct HTTP handling. It centralizes AI communication.
Service classes implement business logic and orchestrate repositories and external calls. They hide complexity from controllers.
ReceiptServiceImplimplementsReceiptService.- It validates the user, stores the uploaded file, and creates a
Receiptmarked asPROCESSING. - It uses
PythonAIServiceClientto analyze the image and receivesAIAnalysisResponse. - On success, it builds a
Transactionand associatedItementities from AI output. - It sets the
Receiptstatus toCOMPLETEDorFAILEDbased on AI results. - Finally, it persists everything using
ReceiptRepository.
AnalyticsService focuses on reporting and AI insights.
- It calls
TransactionRepositoryaggregate queries for different periods and categories. - It combines results into a data map with
category_breakdownandmonthly_trend. - It passes the map to
PythonAIServiceClient.getSpendingAdvicefor narrative tips.
GoalService manages user goals.
- It uses
GoalRepositoryandUserRepositoryfor CRUD operations. - It updates only the mutable fields
currentAmountandstatus. - It composes analytics data and calls
PythonAIServiceClient.generateGoals. - The AI service returns suggestions, which the frontend can turn into actual goals.
Controllers expose API endpoints and delegate to services. They define request mappings, parameters, and response bodies.
ReceiptControllermaps under/api/receipts.- It exposes
POST /upload,GET /user/{userId}, andGET /{id}. - It uses
@RequestParamforuserIdandMultipartFilefor images.
AnalyticsController exposes analytics related endpoints.
- It handles
GET /api/analytics/spendingwithuserIdandperiodquery parameters. - It uses a switch statement to return daily, monthly, or yearly data.
- It also defines
GET /api/analytics/categoriesandGET /api/analytics/suggestions.
GoalController manages goals.
- It defines
GET /api/goalsto list goals for a user. - It exposes
POST /api/goalsto create a goal andPUT /api/goals/{id}to update one. POST /api/goals/generatetriggers AI goal generation based on analytics data.
The frontend is a Vite-powered React application. It uses modern React, TypeScript, and Tailwind CSS.
main.tsxbootstraps the React application and mountsApp.App.tsxsets up routing usingreact-router-dom.- It defines navigation between dashboard, analytics, insights, and goals pages.
- It also fetches receipts on mount and passes them to
Dashboard.
api.ts centralizes HTTP calls and types.
- It defines interfaces for
Receipt,Transaction,Item, andGoal. - It provides helper functions for uploads, analytics, suggestions, and goals.
- All calls use the same
API_BASE_URL, which matches the backend port.
UI components are presentational and rely on api.ts for data. Framer Motion and Recharts provide animations and charts.
ReceiptUploadpresents a drag-and-drop upload panel with camera capture support.- It calls
uploadReceiptand notifies the parent on success. Dashboardrenders receipt cards with thumbnails, statuses, and summary values.
Pages organize higher-level workflows.
AnalyticsPagevisualizes spending trends using Recharts line and bar charts.- It switches period state and refetches data accordingly.
InsightsPageshows a category pie chart and AI advisor panel with textual tips.GoalsPageretrieves goals, shows progress bars, and allows AI-driven goal generation.
The system has a clear data flow from user actions to AI and back. Each step maps to code structures.
- The user uploads a receipt, triggering frontend
uploadReceipt. - The backend stores the file, calls AI, and saves structured data.
- The frontend polls or reloads
getUserReceiptsto show updated receipts and transactions. - Analytics views call dedicated endpoints that reuse aggregated transaction data.
- The AI service enriches analytics with narrative insights and recommended goals.
- These interactions use JSON over HTTP and map to strongly typed DTOs and entities.
This completes the formal architecture description and aligns with the provided UML diagrams.