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:
198
backend/prisma/migrations/0001_init/migration.sql
Normal file
198
backend/prisma/migrations/0001_init/migration.sql
Normal 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;
|
||||
|
||||
1
backend/prisma/migrations/0002_pgvector/migration.sql
Normal file
1
backend/prisma/migrations/0002_pgvector/migration.sql
Normal file
@@ -0,0 +1 @@
|
||||
CREATE INDEX IF NOT EXISTS formulas_embedding_idx ON formulas USING hnsw (embedding vector_cosine_ops);
|
||||
2
backend/prisma/migrations/migration_lock.toml
Normal file
2
backend/prisma/migrations/migration_lock.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
# Please do not edit this file manually
|
||||
provider = "postgresql"
|
||||
176
backend/prisma/schema.prisma
Normal file
176
backend/prisma/schema.prisma
Normal file
@@ -0,0 +1,176 @@
|
||||
generator client {
|
||||
provider = "prisma-client"
|
||||
output = "../src/generated/prisma"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
}
|
||||
|
||||
enum UserRole {
|
||||
engineer
|
||||
admin
|
||||
}
|
||||
|
||||
enum IngredientCategory {
|
||||
emulsifier
|
||||
humectant
|
||||
thickener
|
||||
preservative
|
||||
antioxidant
|
||||
fragrance
|
||||
colorant
|
||||
ph_adjuster
|
||||
sunscreen
|
||||
surfactant
|
||||
emollient
|
||||
other
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(uuid())
|
||||
username String @unique
|
||||
passwordHash String @map("password_hash")
|
||||
role UserRole @default(engineer)
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
formulas Formula[] @relation("FormulaCreator")
|
||||
colorFormulas ColorFormula[] @relation("ColorFormulaCreator")
|
||||
formulaVersions FormulaVersion[]
|
||||
projects Project[]
|
||||
|
||||
@@map("users")
|
||||
}
|
||||
|
||||
model Project {
|
||||
id String @id @default(uuid())
|
||||
name String
|
||||
description String?
|
||||
createdBy String @map("created_by")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
creator User @relation(fields: [createdBy], references: [id])
|
||||
formulas Formula[]
|
||||
|
||||
@@map("projects")
|
||||
}
|
||||
|
||||
model Formula {
|
||||
id String @id @default(uuid())
|
||||
name String
|
||||
description String?
|
||||
projectId String? @map("project_id")
|
||||
currentVersion Int @default(1) @map("current_version")
|
||||
createdBy String @map("created_by")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
embedding Unsupported("vector(1536)")?
|
||||
|
||||
project Project? @relation(fields: [projectId], references: [id])
|
||||
creator User @relation("FormulaCreator", fields: [createdBy], references: [id])
|
||||
versions FormulaVersion[]
|
||||
colorFormulas ColorFormula[]
|
||||
|
||||
@@index([projectId])
|
||||
@@index([createdBy])
|
||||
@@map("formulas")
|
||||
}
|
||||
|
||||
model FormulaVersion {
|
||||
id String @id @default(uuid())
|
||||
formulaId String @map("formula_id")
|
||||
versionNumber Int @map("version_number")
|
||||
description String?
|
||||
snapshotData Json @map("snapshot_data")
|
||||
createdBy String @map("created_by")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
formula Formula @relation(fields: [formulaId], references: [id])
|
||||
creator User @relation(fields: [createdBy], references: [id])
|
||||
phases Phase[]
|
||||
ingredients FormulaIngredient[]
|
||||
|
||||
@@unique([formulaId, versionNumber])
|
||||
@@index([formulaId])
|
||||
@@map("formula_versions")
|
||||
}
|
||||
|
||||
model Ingredient {
|
||||
id String @id @default(uuid())
|
||||
inciName String @map("inci_name")
|
||||
chineseName String @map("chinese_name")
|
||||
functionCategory IngredientCategory @map("function_category")
|
||||
supplier String?
|
||||
unit String @default("kg")
|
||||
unitPrice Decimal? @map("unit_price") @db.Decimal(10, 2)
|
||||
description String?
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
formulaIngredients FormulaIngredient[]
|
||||
|
||||
@@index([functionCategory])
|
||||
@@map("ingredients")
|
||||
}
|
||||
|
||||
model Phase {
|
||||
id String @id @default(uuid())
|
||||
name String
|
||||
formulaId String @map("formula_id")
|
||||
sortOrder Int @default(0) @map("sort_order")
|
||||
|
||||
formulaVersion FormulaVersion @relation(fields: [formulaId], references: [id])
|
||||
ingredients FormulaIngredient[]
|
||||
|
||||
@@index([formulaId])
|
||||
@@map("phases")
|
||||
}
|
||||
|
||||
model FormulaIngredient {
|
||||
id String @id @default(uuid())
|
||||
formulaVersionId String @map("formula_version_id")
|
||||
phaseId String? @map("phase_id")
|
||||
ingredientId String @map("ingredient_id")
|
||||
percentage Decimal @db.Decimal(5, 2)
|
||||
processNotes String? @map("process_notes")
|
||||
|
||||
formulaVersion FormulaVersion @relation(fields: [formulaVersionId], references: [id])
|
||||
phase Phase? @relation(fields: [phaseId], references: [id])
|
||||
ingredient Ingredient @relation(fields: [ingredientId], references: [id])
|
||||
|
||||
@@index([formulaVersionId])
|
||||
@@index([ingredientId])
|
||||
@@map("formula_ingredients")
|
||||
}
|
||||
|
||||
model ColorFormula {
|
||||
id String @id @default(uuid())
|
||||
name String
|
||||
targetLab Json @map("target_lab")
|
||||
actualLab Json? @map("actual_lab")
|
||||
deltaE Float? @map("delta_e")
|
||||
colorantComposition Json? @map("colorant_composition")
|
||||
formulaId String? @map("formula_id")
|
||||
createdBy String @map("created_by")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
formula Formula? @relation(fields: [formulaId], references: [id])
|
||||
creator User @relation("ColorFormulaCreator", fields: [createdBy], references: [id])
|
||||
|
||||
@@index([formulaId])
|
||||
@@index([createdBy])
|
||||
@@map("color_formulas")
|
||||
}
|
||||
|
||||
model AiAuditLog {
|
||||
id String @id @default(uuid())
|
||||
capability String
|
||||
modelName String @map("model_name")
|
||||
promptHash String @map("prompt_hash")
|
||||
tokensUsed Int? @map("tokens_used")
|
||||
durationMs Int? @map("duration_ms")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
@@index([capability])
|
||||
@@index([createdAt])
|
||||
@@map("ai_audit_logs")
|
||||
}
|
||||
66
backend/prisma/seed.ts
Normal file
66
backend/prisma/seed.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { prisma } from '../src/lib/prisma.js'
|
||||
import { IngredientCategory } from '../src/generated/prisma/enums.js'
|
||||
|
||||
const ingredients = [
|
||||
{ inciName: 'Glycerin', chineseName: '甘油', functionCategory: IngredientCategory.humectant, supplier: '丰益油脂', unitPrice: 15.00, description: '最常用的保湿剂,吸湿性强' },
|
||||
{ inciName: 'Butylene Glycol', chineseName: '丁二醇', functionCategory: IngredientCategory.humectant, supplier: '大赛璐', unitPrice: 35.00, description: '多功能保湿剂,兼有防腐增效作用' },
|
||||
{ inciName: 'Propylene Glycol', chineseName: '丙二醇', functionCategory: IngredientCategory.humectant, supplier: '陶氏化学', unitPrice: 20.00, description: '保湿剂和溶剂' },
|
||||
{ inciName: 'Sodium Hyaluronate', chineseName: '透明质酸钠', functionCategory: IngredientCategory.humectant, supplier: '华熙生物', unitPrice: 2800.00, description: '高分子保湿剂,锁水能力极强' },
|
||||
{ inciName: 'Panthenol', chineseName: '泛醇', functionCategory: IngredientCategory.humectant, supplier: '巴斯夫', unitPrice: 180.00, description: '维生素B5前体,保湿修复' },
|
||||
{ inciName: 'Trehalose', chineseName: '海藻糖', functionCategory: IngredientCategory.humectant, supplier: '林原', unitPrice: 120.00, description: '天然保湿因子,保护细胞膜' },
|
||||
{ inciName: 'Betaine', chineseName: '甜菜碱', functionCategory: IngredientCategory.humectant, supplier: '杜邦', unitPrice: 60.00, description: '天然氨基酸保湿剂' },
|
||||
{ inciName: 'Cetearyl Alcohol', chineseName: '鲸蜡硬脂醇', functionCategory: IngredientCategory.emulsifier, supplier: '巴斯夫', unitPrice: 25.00, description: 'O/W型乳化剂和增稠剂' },
|
||||
{ inciName: 'Glyceryl Stearate', chineseName: '甘油硬脂酸酯', functionCategory: IngredientCategory.emulsifier, supplier: '禾大', unitPrice: 30.00, description: '温和乳化剂,自乳化型' },
|
||||
{ inciName: 'Ceteareth-20', chineseName: '鲸蜡硬脂醇聚醚-20', functionCategory: IngredientCategory.emulsifier, supplier: '巴斯夫', unitPrice: 40.00, description: 'O/W型高效乳化剂' },
|
||||
{ inciName: 'Polysorbate 60', chineseName: '聚山梨醇酯-60', functionCategory: IngredientCategory.emulsifier, supplier: '禾大', unitPrice: 35.00, description: '亲水性乳化剂' },
|
||||
{ inciName: 'Lecithin', chineseName: '卵磷脂', functionCategory: IngredientCategory.emulsifier, supplier: '嘉吉', unitPrice: 200.00, description: '天然磷脂乳化剂,仿生亲肤' },
|
||||
{ inciName: 'Carbomer', chineseName: '卡波姆', functionCategory: IngredientCategory.thickener, supplier: '路博润', unitPrice: 80.00, description: '高效增稠剂,需中和' },
|
||||
{ inciName: 'Xanthan Gum', chineseName: '黄原胶', functionCategory: IngredientCategory.thickener, supplier: 'CP Kelco', unitPrice: 65.00, description: '天然多糖增稠剂,耐电解质' },
|
||||
{ inciName: 'Hydroxyethylcellulose', chineseName: '羟乙基纤维素', functionCategory: IngredientCategory.thickener, supplier: '亚什兰', unitPrice: 90.00, description: '非离子增稠剂,透明度好' },
|
||||
{ inciName: 'Phenoxyethanol', chineseName: '苯氧乙醇', functionCategory: IngredientCategory.preservative, supplier: '巴斯夫', unitPrice: 55.00, description: '广谱防腐剂,最常用之一' },
|
||||
{ inciName: 'Sodium Benzoate', chineseName: '苯甲酸钠', functionCategory: IngredientCategory.preservative, supplier: '帝斯曼', unitPrice: 18.00, description: '食品级防腐剂,酸性环境有效' },
|
||||
{ inciName: 'Ethylhexylglycerin', chineseName: '乙基己基甘油', functionCategory: IngredientCategory.preservative, supplier: '舒美', unitPrice: 220.00, description: '多功能防腐增效剂' },
|
||||
{ inciName: 'Tocopherol', chineseName: '生育酚(维生素E)', functionCategory: IngredientCategory.antioxidant, supplier: '帝斯曼', unitPrice: 320.00, description: '天然抗氧化剂,油溶性' },
|
||||
{ inciName: 'Ascorbic Acid', chineseName: '抗坏血酸(维生素C)', functionCategory: IngredientCategory.antioxidant, supplier: '帝斯曼', unitPrice: 450.00, description: '强效抗氧化剂,美白功效' },
|
||||
{ inciName: 'BHT', chineseName: '丁羟甲苯', functionCategory: IngredientCategory.antioxidant, supplier: '伊士曼', unitPrice: 40.00, description: '合成抗氧化剂,油脂保护' },
|
||||
{ inciName: 'Dimethicone', chineseName: '聚二甲基硅氧烷', functionCategory: IngredientCategory.emollient, supplier: '道康宁', unitPrice: 45.00, description: '硅油,丝滑肤感' },
|
||||
{ inciName: 'Caprylic/Capric Triglyceride', chineseName: '辛酸/癸酸甘油三酯', functionCategory: IngredientCategory.emollient, supplier: '巴斯夫', unitPrice: 50.00, description: '轻质润肤酯,铺展性好' },
|
||||
{ inciName: 'Squalane', chineseName: '角鲨烷', functionCategory: IngredientCategory.emollient, supplier: 'Amyris', unitPrice: 280.00, description: '植物角鲨烷,亲肤极佳' },
|
||||
{ inciName: 'Isopropyl Myristate', chineseName: '肉豆蔻酸异丙酯', functionCategory: IngredientCategory.emollient, supplier: '禾大', unitPrice: 30.00, description: '渗透促进剂和润肤酯' },
|
||||
{ inciName: 'Jojoba Oil', chineseName: '霍霍巴籽油', functionCategory: IngredientCategory.emollient, supplier: '德之馨', unitPrice: 150.00, description: '液态蜡酯,与皮脂相似' },
|
||||
{ inciName: 'Sodium Laureth Sulfate', chineseName: '月桂醇聚醚硫酸酯钠', functionCategory: IngredientCategory.surfactant, supplier: '巴斯夫', unitPrice: 12.00, description: '阴离子表面活性剂,起泡清洁' },
|
||||
{ inciName: 'Cocamidopropyl Betaine', chineseName: '椰油酰胺丙基甜菜碱', functionCategory: IngredientCategory.surfactant, supplier: '索尔维', unitPrice: 22.00, description: '两性表面活性剂,温和清洁' },
|
||||
{ inciName: 'Decyl Glucoside', chineseName: '癸基葡糖苷', functionCategory: IngredientCategory.surfactant, supplier: '巴斯夫', unitPrice: 70.00, description: '非离子APG表活,极温和' },
|
||||
{ inciName: 'CI 77491', chineseName: '氧化铁红', functionCategory: IngredientCategory.colorant, supplier: '森馨', unitPrice: 85.00, description: '无机颜料,红色系' },
|
||||
{ inciName: 'CI 77492', chineseName: '氧化铁黄', functionCategory: IngredientCategory.colorant, supplier: '森馨', unitPrice: 85.00, description: '无机颜料,黄色系' },
|
||||
{ inciName: 'CI 77499', chineseName: '氧化铁黑', functionCategory: IngredientCategory.colorant, supplier: '森馨', unitPrice: 85.00, description: '无机颜料,黑色系' },
|
||||
{ inciName: 'CI 77891', chineseName: '二氧化钛', functionCategory: IngredientCategory.colorant, supplier: '科慕', unitPrice: 40.00, description: '白色颜料和物理防晒剂' },
|
||||
{ inciName: 'Mica', chineseName: '云母', functionCategory: IngredientCategory.colorant, supplier: '默克', unitPrice: 120.00, description: '珠光颜料基材' },
|
||||
{ inciName: 'Citric Acid', chineseName: '柠檬酸', functionCategory: IngredientCategory.ph_adjuster, supplier: '英轩', unitPrice: 10.00, description: '酸性pH调节剂,果酸类' },
|
||||
{ inciName: 'Sodium Hydroxide', chineseName: '氢氧化钠', functionCategory: IngredientCategory.ph_adjuster, supplier: '万华化学', unitPrice: 8.00, description: '碱性pH调节剂' },
|
||||
{ inciName: 'Triethanolamine', chineseName: '三乙醇胺', functionCategory: IngredientCategory.ph_adjuster, supplier: '陶氏化学', unitPrice: 20.00, description: 'pH调节剂和中和剂' },
|
||||
{ inciName: 'Ethylhexyl Methoxycinnamate', chineseName: '甲氧基肉桂酸乙基己酯', functionCategory: IngredientCategory.sunscreen, supplier: '巴斯夫', unitPrice: 65.00, description: 'UVB化学防晒剂' },
|
||||
{ inciName: 'Bis-Ethylhexyloxyphenol Methoxyphenyl Triazine', chineseName: '双-乙基己氧苯酚甲氧苯基三嗪', functionCategory: IngredientCategory.sunscreen, supplier: '巴斯夫', unitPrice: 550.00, description: '广谱UVA/UVB防晒剂(Tinosorb S)' },
|
||||
{ inciName: 'Parfum', chineseName: '香精', functionCategory: IngredientCategory.fragrance, supplier: '奇华顿', unitPrice: 300.00, description: '调配香精' },
|
||||
]
|
||||
|
||||
async function main() {
|
||||
console.log('开始导入成分种子数据...')
|
||||
|
||||
await prisma.ingredient.createMany({
|
||||
data: ingredients,
|
||||
skipDuplicates: true,
|
||||
})
|
||||
|
||||
const count = await prisma.ingredient.count()
|
||||
console.log(`成分种子数据导入完成!共 ${count} 条记录`)
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((e) => {
|
||||
console.error('种子数据导入失败:', e)
|
||||
process.exit(1)
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect()
|
||||
})
|
||||
Reference in New Issue
Block a user