feat: AI 驱动的配方研发智能平台 v0.1

核心功能:
- M3 配方记录: 创建/编辑/详情/可视化编辑/AI提取/版本历史/版本对比
- M1 颜色引擎: D3.js 色相环/滑条微调/ΔE计算/取色棒/AI配色推荐
- M2 可视化编辑器: ECharts饼图/成分滑条/AI预测/雷达图/仪表盘
- M4 配方推演: 约束设置/SSE推演/方案对比/散点图
- 平台: NL智能搜索/项目管理/CSV导出/JWT认证/全局搜索

技术栈:
- 前端: React + Vite + Tailwind CSS 4 + Zustand + TanStack Query
- 后端: Fastify 5 + Prisma 7 + PostgreSQL + pgvector
- AI: OpenAI/DeepSeek API 调用 + Prompt模板 + 缓存/降级/限流
- 测试: Vitest 42 tests (26 API集成 + 16 色彩模块)
This commit is contained in:
qichi.liang
2026-05-20 17:50:37 +08:00
commit 23e5cb4006
125 changed files with 14454 additions and 0 deletions

View File

@@ -0,0 +1,198 @@
-- CreateSchema
CREATE SCHEMA IF NOT EXISTS "public";
CREATE EXTENSION IF NOT EXISTS vector;
-- CreateEnum
CREATE TYPE "UserRole" AS ENUM ('engineer', 'admin');
-- CreateEnum
CREATE TYPE "IngredientCategory" AS ENUM ('emulsifier', 'humectant', 'thickener', 'preservative', 'antioxidant', 'fragrance', 'colorant', 'ph_adjuster', 'sunscreen', 'surfactant', 'emollient', 'other');
-- CreateTable
CREATE TABLE "users" (
"id" TEXT NOT NULL,
"username" TEXT NOT NULL,
"password_hash" TEXT NOT NULL,
"role" "UserRole" NOT NULL DEFAULT 'engineer',
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "users_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "projects" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT,
"created_by" TEXT NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "projects_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "formulas" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT,
"project_id" TEXT,
"current_version" INTEGER NOT NULL DEFAULT 1,
"created_by" TEXT NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
"embedding" vector(1536),
CONSTRAINT "formulas_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "formula_versions" (
"id" TEXT NOT NULL,
"formula_id" TEXT NOT NULL,
"version_number" INTEGER NOT NULL,
"description" TEXT,
"snapshot_data" JSONB NOT NULL,
"created_by" TEXT NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "formula_versions_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ingredients" (
"id" TEXT NOT NULL,
"inci_name" TEXT NOT NULL,
"chinese_name" TEXT NOT NULL,
"function_category" "IngredientCategory" NOT NULL,
"supplier" TEXT,
"unit" TEXT NOT NULL DEFAULT 'kg',
"unit_price" DECIMAL(10,2),
"description" TEXT,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "ingredients_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "phases" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"formula_id" TEXT NOT NULL,
"sort_order" INTEGER NOT NULL DEFAULT 0,
CONSTRAINT "phases_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "formula_ingredients" (
"id" TEXT NOT NULL,
"formula_version_id" TEXT NOT NULL,
"phase_id" TEXT,
"ingredient_id" TEXT NOT NULL,
"percentage" DECIMAL(5,2) NOT NULL,
"process_notes" TEXT,
CONSTRAINT "formula_ingredients_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "color_formulas" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"target_lab" JSONB NOT NULL,
"actual_lab" JSONB,
"delta_e" DOUBLE PRECISION,
"colorant_composition" JSONB,
"formula_id" TEXT,
"created_by" TEXT NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "color_formulas_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ai_audit_logs" (
"id" TEXT NOT NULL,
"capability" TEXT NOT NULL,
"model_name" TEXT NOT NULL,
"prompt_hash" TEXT NOT NULL,
"tokens_used" INTEGER,
"duration_ms" INTEGER,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "ai_audit_logs_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "users_username_key" ON "users"("username");
-- CreateIndex
CREATE INDEX "formulas_project_id_idx" ON "formulas"("project_id");
-- CreateIndex
CREATE INDEX "formulas_created_by_idx" ON "formulas"("created_by");
-- CreateIndex
CREATE INDEX "formula_versions_formula_id_idx" ON "formula_versions"("formula_id");
-- CreateIndex
CREATE UNIQUE INDEX "formula_versions_formula_id_version_number_key" ON "formula_versions"("formula_id", "version_number");
-- CreateIndex
CREATE INDEX "ingredients_function_category_idx" ON "ingredients"("function_category");
-- CreateIndex
CREATE INDEX "phases_formula_id_idx" ON "phases"("formula_id");
-- CreateIndex
CREATE INDEX "formula_ingredients_formula_version_id_idx" ON "formula_ingredients"("formula_version_id");
-- CreateIndex
CREATE INDEX "formula_ingredients_ingredient_id_idx" ON "formula_ingredients"("ingredient_id");
-- CreateIndex
CREATE INDEX "color_formulas_formula_id_idx" ON "color_formulas"("formula_id");
-- CreateIndex
CREATE INDEX "color_formulas_created_by_idx" ON "color_formulas"("created_by");
-- CreateIndex
CREATE INDEX "ai_audit_logs_capability_idx" ON "ai_audit_logs"("capability");
-- CreateIndex
CREATE INDEX "ai_audit_logs_created_at_idx" ON "ai_audit_logs"("created_at");
-- AddForeignKey
ALTER TABLE "projects" ADD CONSTRAINT "projects_created_by_fkey" FOREIGN KEY ("created_by") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "formulas" ADD CONSTRAINT "formulas_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "projects"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "formulas" ADD CONSTRAINT "formulas_created_by_fkey" FOREIGN KEY ("created_by") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "formula_versions" ADD CONSTRAINT "formula_versions_formula_id_fkey" FOREIGN KEY ("formula_id") REFERENCES "formulas"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "formula_versions" ADD CONSTRAINT "formula_versions_created_by_fkey" FOREIGN KEY ("created_by") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "phases" ADD CONSTRAINT "phases_formula_id_fkey" FOREIGN KEY ("formula_id") REFERENCES "formula_versions"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "formula_ingredients" ADD CONSTRAINT "formula_ingredients_formula_version_id_fkey" FOREIGN KEY ("formula_version_id") REFERENCES "formula_versions"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "formula_ingredients" ADD CONSTRAINT "formula_ingredients_phase_id_fkey" FOREIGN KEY ("phase_id") REFERENCES "phases"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "formula_ingredients" ADD CONSTRAINT "formula_ingredients_ingredient_id_fkey" FOREIGN KEY ("ingredient_id") REFERENCES "ingredients"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "color_formulas" ADD CONSTRAINT "color_formulas_formula_id_fkey" FOREIGN KEY ("formula_id") REFERENCES "formulas"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "color_formulas" ADD CONSTRAINT "color_formulas_created_by_fkey" FOREIGN KEY ("created_by") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@@ -0,0 +1 @@
CREATE INDEX IF NOT EXISTS formulas_embedding_idx ON formulas USING hnsw (embedding vector_cosine_ops);

View File

@@ -0,0 +1,2 @@
# Please do not edit this file manually
provider = "postgresql"