Browse Source

feat: realign customer persistence with db schema

ChenYu 4 tháng trước cách đây
mục cha
commit
daad739c14

+ 33 - 35
backend/prisma/migrations/20260628120000_add_customer_table/migration.sql

@@ -1,37 +1,35 @@
 CREATE TABLE `customer` (
-  `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
-  `hairstylist_id` BIGINT UNSIGNED NOT NULL,
-  `name` VARCHAR(100) NOT NULL,
-  `kana` VARCHAR(100) NOT NULL DEFAULT '',
-  `phone` VARCHAR(32) NOT NULL,
-  `email` VARCHAR(100) NOT NULL DEFAULT '',
-  `birthday` DATE NULL,
-  `avatar_url` VARCHAR(500) NOT NULL,
-  `contact_type` ENUM('LINE','PHONE','EMAIL') NOT NULL DEFAULT 'LINE',
-  `line_status` ENUM('LINKED','UNLINKED','INVITED','KEEP') NULL,
-  `last_visit_date` DATE NULL,
-  `next_visit_date` DATE NULL,
-  `visit_cycle` VARCHAR(64) NOT NULL DEFAULT '',
-  `hair_length` ENUM('SHORT','MEDIUM','LONG') NULL,
-  `hair_type` VARCHAR(64) NOT NULL DEFAULT '',
-  `hair_condition` VARCHAR(255) NOT NULL DEFAULT '',
-  `concern` TEXT NULL,
-  `desired_image` TEXT NULL,
-  `formula_note` TEXT NULL,
-  `allergy_note` TEXT NULL,
-  `private_note` TEXT NULL,
-  `follow_status` ENUM('NONE','NEED_FOLLOW_UP','SENT') NOT NULL DEFAULT 'NONE',
-  `follow_timing` VARCHAR(64) NOT NULL DEFAULT '',
-  `follow_message` TEXT NULL,
-  `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
-  `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
-  `is_deleted` TINYINT UNSIGNED NOT NULL DEFAULT 0,
+  `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '顾客主键ID',
+  `hairstylist_id` BIGINT UNSIGNED NOT NULL COMMENT '所属发型师ID,JWT 隔离必需字段',
+  `name` VARCHAR(30) NOT NULL COMMENT '顾客姓名(必填)',
+  `name_pronunciation` VARCHAR(50) DEFAULT '' COMMENT '姓名读音',
+  `phone` VARCHAR(20) NOT NULL COMMENT '联系电话(必填)',
+  `email` VARCHAR(100) DEFAULT '' COMMENT '邮箱',
+  `birthday` DATE NULL COMMENT '生日',
+  `avatar` VARCHAR(500) DEFAULT '' COMMENT '顾客头像图片URL地址',
+  `expect_contact` ENUM('LINE','PHONE','EMAIL','NONE') NOT NULL DEFAULT 'NONE' COMMENT '期望联系方式:LINE/电话/邮箱/未设置',
+  `hair_length` ENUM('SHORT','MEDIUM','LONG','UNCONFIRM') NOT NULL DEFAULT 'UNCONFIRM' COMMENT '当前头发长度:短发/中长发/长发/未确认',
+  `hair_texture` ENUM('UNCONFIRM','THIN','NORMAL','HARD_CRUDE','CURLY') NOT NULL DEFAULT 'UNCONFIRM' COMMENT '发质:未确认/细/普通/粗硬/有自然卷',
+  `notice` TEXT NULL COMMENT '发型护理注意事项',
+  `allergy` VARCHAR(500) DEFAULT '' COMMENT '过敏史/过敏产品',
+  `inner_remark` TEXT NULL COMMENT '门店内部备注(仅内部查看)',
+  `next_follow_date` DATE NULL COMMENT '下次跟进日期',
+  `recommend_visit_date` DATE NULL COMMENT '推荐到店日',
+  `first_suggest_hair` ENUM('AUTO','LONG','MEDIUM','SHORT') NOT NULL DEFAULT 'AUTO' COMMENT '首次建议发型模板:自动推荐/长发/中长发/短发',
+  `plan_date` DATE NULL COMMENT '到店计划日',
+  `is_first_register` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否首次登记:0否,1是',
+  `visit_purpose` ENUM('CUT_DYE','CUT','DYE','CONSULT_SIM') NULL COMMENT '到店目的:剪发+染发/剪发/染发/模拟咨询',
+  `trouble` TEXT NULL COMMENT '发质/发型烦恼',
+  `consult_content` TEXT NULL COMMENT '详细咨询内容',
+  `expect_look` TEXT NULL COMMENT '期望发型形象描述',
+  `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  `is_deleted` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '逻辑删除 0正常 1删除',
   PRIMARY KEY (`id`),
-  KEY `idx_hairstylist_id` (`hairstylist_id`),
-  KEY `idx_phone` (`phone`),
-  KEY `idx_next_visit_date` (`next_visit_date`),
-  KEY `idx_follow_status` (`follow_status`),
-  KEY `idx_hair_length` (`hair_length`),
-  KEY `idx_is_deleted` (`is_deleted`),
-  KEY `idx_hairstylist_is_deleted` (`hairstylist_id`, `is_deleted`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='顾客档案主表';
+  UNIQUE KEY `uk_phone_del` (`phone`,`is_deleted`) COMMENT '手机号唯一,删除后可重录',
+  KEY `idx_hairstylist_is_deleted` (`hairstylist_id`,`is_deleted`) COMMENT '按发型师隔离顾客数据',
+  KEY `idx_is_deleted` (`is_deleted`) COMMENT '逻辑删除筛选',
+  KEY `idx_next_follow` (`next_follow_date`,`is_deleted`) COMMENT '跟进日期筛选索引',
+  KEY `idx_recommend_visit` (`recommend_visit_date`,`is_deleted`) COMMENT '推荐到店日期索引',
+  KEY `idx_plan_date` (`plan_date`,`is_deleted`) COMMENT '到店计划日筛选索引'
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='顾客信息+咨询登记表(合并单表)';

+ 54 - 45
backend/prisma/schema.prisma

@@ -21,29 +21,40 @@ enum TemplateTaskStatus {
   FAILED
 }
 
-enum CustomerContactType {
+enum CustomerExpectContact {
   LINE
   PHONE
   EMAIL
-}
-
-enum CustomerLineStatus {
-  LINKED
-  UNLINKED
-  INVITED
-  KEEP
+  NONE
 }
 
 enum CustomerHairLength {
   SHORT
   MEDIUM
   LONG
+  UNCONFIRM
 }
 
-enum CustomerFollowStatus {
-  NONE
-  NEED_FOLLOW_UP
-  SENT
+enum CustomerHairTexture {
+  UNCONFIRM
+  THIN
+  NORMAL
+  HARD_CRUDE
+  CURLY
+}
+
+enum CustomerFirstSuggestHair {
+  AUTO
+  LONG
+  MEDIUM
+  SHORT
+}
+
+enum CustomerVisitPurpose {
+  CUT_DYE
+  CUT
+  DYE
+  CONSULT_SIM
 }
 
 model HairstylistUser {
@@ -59,41 +70,39 @@ model HairstylistUser {
 }
 
 model Customer {
-  id            BigInt              @id @default(autoincrement()) @db.UnsignedBigInt
-  hairstylistId BigInt              @map("hairstylist_id") @db.UnsignedBigInt
-  name          String              @db.VarChar(100)
-  kana          String              @default("") @db.VarChar(100)
-  phone         String              @db.VarChar(32)
-  email         String              @default("") @db.VarChar(100)
-  birthday      DateTime?           @db.Date
-  avatarUrl     String              @map("avatar_url") @db.VarChar(500)
-  contactType   CustomerContactType @default(LINE) @map("contact_type")
-  lineStatus    CustomerLineStatus? @map("line_status")
-  lastVisitDate DateTime?           @map("last_visit_date") @db.Date
-  nextVisitDate DateTime?           @map("next_visit_date") @db.Date
-  visitCycle    String              @default("") @map("visit_cycle") @db.VarChar(64)
-  hairLength    CustomerHairLength? @map("hair_length")
-  hairType      String              @default("") @map("hair_type") @db.VarChar(64)
-  hairCondition String              @default("") @map("hair_condition") @db.VarChar(255)
-  concern       String?             @db.Text
-  desiredImage  String?             @map("desired_image") @db.Text
-  formulaNote   String?             @map("formula_note") @db.Text
-  allergyNote   String?             @map("allergy_note") @db.Text
-  privateNote   String?             @map("private_note") @db.Text
-  followStatus  CustomerFollowStatus @default(NONE) @map("follow_status")
-  followTiming  String              @default("") @map("follow_timing") @db.VarChar(64)
-  followMessage String?             @map("follow_message") @db.Text
-  createTime    DateTime            @default(now()) @map("create_time")
-  updateTime    DateTime            @updatedAt @map("update_time")
-  isDeleted     Int                 @default(0) @map("is_deleted") @db.UnsignedTinyInt
-
-  @@index([hairstylistId], map: "idx_hairstylist_id")
-  @@index([phone], map: "idx_phone")
-  @@index([nextVisitDate], map: "idx_next_visit_date")
-  @@index([followStatus], map: "idx_follow_status")
-  @@index([hairLength], map: "idx_hair_length")
+  id                 BigInt                   @id @default(autoincrement()) @db.UnsignedBigInt
+  hairstylistId      BigInt                   @map("hairstylist_id") @db.UnsignedBigInt
+  name               String                   @db.VarChar(30)
+  namePronunciation  String                   @default("") @map("name_pronunciation") @db.VarChar(50)
+  phone              String                   @db.VarChar(20)
+  email              String                   @default("") @db.VarChar(100)
+  birthday           DateTime?                @db.Date
+  avatar             String                   @default("") @db.VarChar(500)
+  expectContact      CustomerExpectContact    @default(NONE) @map("expect_contact")
+  hairLength         CustomerHairLength       @default(UNCONFIRM) @map("hair_length")
+  hairTexture        CustomerHairTexture      @default(UNCONFIRM) @map("hair_texture")
+  notice             String?                  @db.Text
+  allergy            String                   @default("") @db.VarChar(500)
+  innerRemark        String?                  @map("inner_remark") @db.Text
+  nextFollowDate     DateTime?                @map("next_follow_date") @db.Date
+  recommendVisitDate DateTime?                @map("recommend_visit_date") @db.Date
+  firstSuggestHair   CustomerFirstSuggestHair @default(AUTO) @map("first_suggest_hair")
+  planDate           DateTime?                @map("plan_date") @db.Date
+  isFirstRegister    Int                      @default(0) @map("is_first_register") @db.UnsignedTinyInt
+  visitPurpose       CustomerVisitPurpose?    @map("visit_purpose")
+  trouble            String?                  @db.Text
+  consultContent     String?                  @map("consult_content") @db.Text
+  expectLook         String?                  @map("expect_look") @db.Text
+  createTime         DateTime                 @default(now()) @map("create_time")
+  updateTime         DateTime                 @updatedAt @map("update_time")
+  isDeleted          Int                      @default(0) @map("is_deleted") @db.UnsignedTinyInt
+
+  @@unique([phone, isDeleted], map: "uk_phone_del")
   @@index([isDeleted], map: "idx_is_deleted")
   @@index([hairstylistId, isDeleted], map: "idx_hairstylist_is_deleted")
+  @@index([nextFollowDate, isDeleted], map: "idx_next_follow")
+  @@index([recommendVisitDate, isDeleted], map: "idx_recommend_visit")
+  @@index([planDate, isDeleted], map: "idx_plan_date")
   @@map("customer")
 }