feat: dish recognition job context, diary linkage, home widget, history page
Backend: - Rename recognition_jobs → dish_recognition_jobs; add target_date and target_meal_type columns to capture scan context at submission time - Add job_id FK on meal_diary so entries are linked to their origin job - New GET /ai/jobs endpoint returns today's unlinked jobs for the current user - diary.Entry and CreateRequest gain job_id field; repository reads/writes it - CORS middleware: allow Accept-Language and Cache-Control headers - Logging middleware: implement http.Flusher on responseWriter (needed for SSE) - Consolidate migrations into a single 001_initial_schema.sql Flutter: - POST /ai/recognize-dish now sends target_date and target_meal_type - DishResultSheet accepts jobId; _addToDiary includes it in the diary payload, saves last-used meal type to SharedPreferences, invalidates todayJobsProvider - TodayJobsNotifier + todayJobsProvider: loads unlinked jobs via GET /ai/jobs - Home screen shows _TodayJobsWidget (up to 3 tiles) between macros and meals; tapping a done tile reopens DishResultSheet with the stored result - Quick Actions row: third button "История" → /scan/history - New RecognitionHistoryScreen: full-screen list of today's unlinked jobs - LocalPreferences wrapper over SharedPreferences (last_used_meal_type) - app_theme: apply Google Fonts Roboto as default font family Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -390,6 +390,31 @@ CREATE TABLE shopping_lists (
|
||||
UNIQUE (user_id, menu_plan_id)
|
||||
);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- dish_recognition_jobs
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE TABLE dish_recognition_jobs (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
user_plan TEXT NOT NULL,
|
||||
image_base64 TEXT NOT NULL,
|
||||
mime_type TEXT NOT NULL DEFAULT 'image/jpeg',
|
||||
lang TEXT NOT NULL DEFAULT 'en',
|
||||
target_date DATE,
|
||||
target_meal_type TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
-- pending | processing | done | failed
|
||||
result JSONB,
|
||||
error TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
started_at TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ
|
||||
);
|
||||
CREATE INDEX idx_dish_recognition_jobs_user
|
||||
ON dish_recognition_jobs (user_id, created_at DESC);
|
||||
CREATE INDEX idx_dish_recognition_jobs_pending
|
||||
ON dish_recognition_jobs (status, user_plan, created_at ASC);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- meal_diary
|
||||
-- ---------------------------------------------------------------------------
|
||||
@@ -400,33 +425,14 @@ CREATE TABLE meal_diary (
|
||||
meal_type TEXT NOT NULL,
|
||||
portions DECIMAL(5,2) NOT NULL DEFAULT 1,
|
||||
source TEXT NOT NULL DEFAULT 'manual',
|
||||
dish_id UUID NOT NULL REFERENCES dishes(id) ON DELETE RESTRICT,
|
||||
recipe_id UUID REFERENCES recipes(id) ON DELETE SET NULL,
|
||||
dish_id UUID NOT NULL REFERENCES dishes(id) ON DELETE RESTRICT,
|
||||
recipe_id UUID REFERENCES recipes(id) ON DELETE SET NULL,
|
||||
portion_g DECIMAL(10,2),
|
||||
job_id UUID REFERENCES dish_recognition_jobs(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX idx_meal_diary_user_date ON meal_diary (user_id, date);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- recognition_jobs
|
||||
-- ---------------------------------------------------------------------------
|
||||
CREATE TABLE recognition_jobs (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
user_plan TEXT NOT NULL,
|
||||
image_base64 TEXT NOT NULL,
|
||||
mime_type TEXT NOT NULL DEFAULT 'image/jpeg',
|
||||
lang TEXT NOT NULL DEFAULT 'en',
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
-- pending | processing | done | failed
|
||||
result JSONB,
|
||||
error TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
started_at TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ
|
||||
);
|
||||
CREATE INDEX idx_recognition_jobs_user ON recognition_jobs (user_id, created_at DESC);
|
||||
CREATE INDEX idx_recognition_jobs_pending ON recognition_jobs (status, user_plan, created_at ASC);
|
||||
CREATE INDEX idx_meal_diary_job_id ON meal_diary (job_id) WHERE job_id IS NOT NULL;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Seed data: languages
|
||||
@@ -605,8 +611,8 @@ INSERT INTO dish_category_translations (category_slug, lang, name) VALUES
|
||||
('snack', 'ru', 'Снэк');
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS recognition_jobs;
|
||||
DROP TABLE IF EXISTS meal_diary;
|
||||
DROP TABLE IF EXISTS dish_recognition_jobs;
|
||||
DROP TABLE IF EXISTS shopping_lists;
|
||||
DROP TABLE IF EXISTS menu_items;
|
||||
DROP TABLE IF EXISTS menu_plans;
|
||||
|
||||
Reference in New Issue
Block a user