摘要:所有的架构设计最终都要回归代码。在工程实现层,我们的目标只有一个:将 ER 模型、状态机和业务流程,转化为可执行的数据结构、行为入口与解耦的通信机制。本文将从 Prisma Schema 建模、RESTful API 设计以及事件总线清单三个维度,交付一套直接可开工的工程骨架。


一、 Prisma Schema:业务关系的物理映射

在 Prisma 层,我们不只是建表,而是在定义业务约束。通过强类型的 enum 和关联关系,可以在数据库层面拦截 80% 的逻辑错误。

1. 用户与核心资产层(User & CourseCard)

enum Role { STUDENT; TRAINER; ADMIN }
enum CardStatus { VALID; EXHAUSTED; EXPIRED; REFUNDED }

model User {
  id        String   @id @default(uuid())
  role      Role     @default(STUDENT)
  name      String
  phone     String   @unique
  status    String   @default("ACTIVE")
  createdAt DateTime @default(now())

  // 关联关系
  orders    Order[]
  bookings  Booking[]
  cards     CourseCard[]
}

model CourseCard {
  id                String     @id @default(uuid())
  studentId         String
  student           User       @relation(fields: [studentId], references: [id])
  courseId          String
  course            Course     @relation(fields: [courseId], references: [id])
  orderId           String
  order             Order      @relation(fields: [orderId], references: [id])
  
  totalSessions     Int
  usedSessions      Int        @default(0)
  remainingSessions Int
  validEnd          DateTime   // 有效期截止
  status            CardStatus @default(VALID)

  bookings          Booking[]
  records           SessionRecord[]
}

2. 履约枢纽层(Schedule & Booking)

enum BookingStatus { UPCOMING; COMPLETED; CANCELLED; NO_SHOW }

model TrainerSchedule {
  id             String   @id @default(uuid())
  trainerId      String
  trainer        User     @relation(fields: [trainerId], references: [id])
  startTime      DateTime
  endTime        DateTime
  capacityTotal  Int      @default(1)
  capacityBooked Int      @default(0)
  status         String   @default("OPEN") // OPEN | CLOSED

  bookings       Booking[]
}

model Booking {
  id           String        @id @default(uuid())
  bookingNo    String        @unique
  studentId    String
  student      User          @relation(fields: [studentId], references: [id])
  scheduleId   String
  schedule     TrainerSchedule @relation(fields: [scheduleId], references: [id])
  courseCardId String
  courseCard   CourseCard    @relation(fields: [courseCardId], references: [id])
  
  status       BookingStatus @default(UPCOMING)
  checkInTime  DateTime?
  isDeducted   Boolean       @default(false) // 核心标记:是否已成功扣课
}


二、 API 设计:基于“动作”而非“资源”的契约

优秀的 API 设计不应该只是简单的 CRUD,而应该体现业务动作(Business Actions)

1. 预约模块(核心交互)

  • POST /api/bookings创建预约。输入 scheduleIdcourseCardId,后端执行事务(校验余额、锁定资源、生成预约单)。
  • POST /api/bookings/:id/check-in履约签到。触发状态变更及课时扣减逻辑。
  • POST /api/bookings/:id/cancel取消预约。需符合时间窗口规则,释放资源。

2. 资产与订单模块

  • GET /api/me/course-cards我的课时。展示剩余次数、过期时间及使用流水。
  • POST /api/orders/checkout下单支付。创建待支付订单。
  • POST /api/webhooks/payment支付回调。处理支付成功后的资产生成逻辑。

三、 事件清单(Event List):系统的“解耦之魂”

如果让 Booking 代码直接去写 CourseCard 的余额,系统很快就会变成一团乱麻。事件驱动(EDA) 是将履约与资产解耦的最佳方式。

1. 核心事件流向

  • OrderPaid:支付成功事件 → \rightarrow 触发 CreateCourseCard(生成资产)。
  • BookingCreated:预约成功事件 → \rightarrow 触发 LockSchedule(锁定排期) & FreezeSession(冻结课时)。
  • BookingCheckedIn:签到成功事件 → \rightarrow 触发 DeductSession(实扣课时) & CalcCommission(计算提成)。
  • CourseCardExhausted:课时耗尽事件 → \rightarrow 触发 PushNotification(续费提醒)。

2. 为什么事件驱动如此重要?

  • 高扩展性:未来如果想增加“上课奖励积分”功能,只需订阅 BookingCheckedIn 事件,无需修改原有的履约代码。
  • 故障隔离:即使“提成计算模块”暂时挂了,也不影响学员正常的签到上课。

四、 工程总结:三层架构的统一视角

  1. Prisma Schema(结构层):定义了静态的物理世界,确保数据一致性。
  2. API Layer(交互层):定义了外部世界与系统的沟通方式,封装业务逻辑。
  3. Event System(通信层):定义了系统内部的“呼吸”与“反馈”,实现组件间的深度解耦。

最终收束

这套系统在工程视角下,本质是一个事件驱动的履约型交易系统

  • Prisma 是地基,定死了规矩;
  • API 是门户,执行着指令;
  • Event 是神经网络,传递着信息。

至此,从业务问题定义到最终的代码实现架构,我们已经完成了一次完整的闭环。对于开发者而言,接下来的工作就是将这些 Schema 填入项目,按照 API 契约编写 Service 逻辑,并用事件总线将它们串联起来。这就是构建一个专业级 SaaS 系统的标准路径。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐