四端上传
This commit is contained in:
1 parent
25bbb5f9c8
commit
c6e2cb4d5b
276 files changed
+17812
-3
No files matched your search
@@ -0,0 +1,270 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE `users` (
|
||||
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`openid` VARCHAR(64) NOT NULL,
|
||||
`nickname` VARCHAR(32) NULL,
|
||||
`headicon_id` INTEGER UNSIGNED NOT NULL DEFAULT 0,
|
||||
`wx_session_key` VARCHAR(255) NULL,
|
||||
`session_key_updated_at` DATETIME(3) NULL,
|
||||
`status` TINYINT NOT NULL DEFAULT 1,
|
||||
`registered_at` DATETIME(3) NOT NULL,
|
||||
`last_login_at` DATETIME(3) NULL,
|
||||
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updated_at` DATETIME(3) NOT NULL,
|
||||
|
||||
UNIQUE INDEX `users_openid_key`(`openid`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `courses` (
|
||||
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`code` VARCHAR(64) NOT NULL,
|
||||
`title` VARCHAR(128) NOT NULL,
|
||||
`intro` VARCHAR(255) NULL,
|
||||
`cover_image` VARCHAR(255) NULL,
|
||||
`price_fen` INTEGER UNSIGNED NOT NULL DEFAULT 0,
|
||||
`status` TINYINT NOT NULL DEFAULT 1,
|
||||
`sort_order` INTEGER NOT NULL DEFAULT 0,
|
||||
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updated_at` DATETIME(3) NOT NULL,
|
||||
|
||||
UNIQUE INDEX `courses_code_key`(`code`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `lessons` (
|
||||
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`course_id` INTEGER UNSIGNED NOT NULL,
|
||||
`play_id` VARCHAR(64) NOT NULL,
|
||||
`chapter_id` VARCHAR(64) NOT NULL,
|
||||
`chapter_title` VARCHAR(255) NULL,
|
||||
`lesson_title` VARCHAR(255) NULL,
|
||||
`duration` VARCHAR(16) NULL,
|
||||
`published_at` DATETIME(3) NULL,
|
||||
`cover_image` VARCHAR(255) NULL,
|
||||
`sort_order` INTEGER NOT NULL DEFAULT 0,
|
||||
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
|
||||
INDEX `lessons_course_id_sort_order_idx`(`course_id`, `sort_order`),
|
||||
UNIQUE INDEX `lessons_course_id_chapter_id_play_id_key`(`course_id`, `chapter_id`, `play_id`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `free_lessons` (
|
||||
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`series_no` INTEGER NOT NULL,
|
||||
`sort_order` INTEGER NOT NULL DEFAULT 0,
|
||||
`course_name` VARCHAR(255) NOT NULL,
|
||||
`video_title` VARCHAR(255) NULL,
|
||||
`intro` VARCHAR(255) NULL,
|
||||
`publisher` VARCHAR(255) NULL,
|
||||
`published_at` DATETIME(3) NULL,
|
||||
`duration` VARCHAR(16) NULL,
|
||||
`video_m3u8` VARCHAR(255) NULL,
|
||||
`cover_image` VARCHAR(255) NULL,
|
||||
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
|
||||
INDEX `free_lessons_series_no_sort_order_idx`(`series_no`, `sort_order`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `home_courses` (
|
||||
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`course_name` VARCHAR(255) NOT NULL,
|
||||
`intro` VARCHAR(255) NULL,
|
||||
`published_at` DATETIME(3) NULL,
|
||||
`cover_image` VARCHAR(255) NULL,
|
||||
`content` LONGTEXT NULL,
|
||||
`course_id` INTEGER UNSIGNED NULL,
|
||||
`sort_order` INTEGER NOT NULL DEFAULT 0,
|
||||
`status` TINYINT NOT NULL DEFAULT 1,
|
||||
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updated_at` DATETIME(3) NOT NULL,
|
||||
|
||||
INDEX `home_courses_status_sort_order_idx`(`status`, `sort_order`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `home_free_courses` (
|
||||
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`series_no` INTEGER NOT NULL,
|
||||
`course_name` VARCHAR(255) NOT NULL,
|
||||
`intro` VARCHAR(255) NULL,
|
||||
`published_at` DATETIME(3) NULL,
|
||||
`cover_image` VARCHAR(255) NULL,
|
||||
`sort_order` INTEGER NOT NULL DEFAULT 0,
|
||||
`status` TINYINT NOT NULL DEFAULT 1,
|
||||
|
||||
INDEX `home_free_courses_status_sort_order_idx`(`status`, `sort_order`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `banners` (
|
||||
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`filename` VARCHAR(255) NOT NULL,
|
||||
`sort_order` INTEGER NOT NULL DEFAULT 0,
|
||||
`status` TINYINT NOT NULL DEFAULT 1,
|
||||
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updated_at` DATETIME(3) NOT NULL,
|
||||
|
||||
INDEX `banners_status_sort_order_idx`(`status`, `sort_order`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `articles` (
|
||||
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`legacy_page_id` VARCHAR(32) NULL,
|
||||
`title` VARCHAR(255) NOT NULL,
|
||||
`intro` VARCHAR(255) NULL,
|
||||
`publisher` VARCHAR(255) NULL,
|
||||
`published_at` DATETIME(3) NULL,
|
||||
`content` LONGTEXT NULL,
|
||||
`sort_order` INTEGER NOT NULL DEFAULT 0,
|
||||
`status` TINYINT NOT NULL DEFAULT 1,
|
||||
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updated_at` DATETIME(3) NOT NULL,
|
||||
|
||||
INDEX `articles_status_published_at_idx`(`status`, `published_at`),
|
||||
INDEX `articles_legacy_page_id_idx`(`legacy_page_id`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `course_tips` (
|
||||
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`tip_key` INTEGER NOT NULL,
|
||||
`content` TEXT NULL,
|
||||
`title` TINYTEXT NULL,
|
||||
|
||||
UNIQUE INDEX `course_tips_tip_key_key`(`tip_key`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `settings` (
|
||||
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`key` VARCHAR(64) NOT NULL,
|
||||
`value` VARCHAR(255) NULL,
|
||||
`remark` VARCHAR(255) NULL,
|
||||
|
||||
UNIQUE INDEX `settings_key_key`(`key`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `entitlements` (
|
||||
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`user_id` INTEGER UNSIGNED NOT NULL,
|
||||
`course_id` INTEGER UNSIGNED NOT NULL,
|
||||
`source` TINYINT NOT NULL,
|
||||
`ref_id` INTEGER UNSIGNED NULL,
|
||||
`granted_at` DATETIME(3) NOT NULL,
|
||||
`expire_at` DATETIME(3) NULL,
|
||||
`note` VARCHAR(255) NULL,
|
||||
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
|
||||
INDEX `entitlements_course_id_idx`(`course_id`),
|
||||
UNIQUE INDEX `entitlements_user_id_course_id_key`(`user_id`, `course_id`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `orders` (
|
||||
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`out_trade_no` VARCHAR(32) NOT NULL,
|
||||
`user_id` INTEGER UNSIGNED NOT NULL,
|
||||
`course_id` INTEGER UNSIGNED NOT NULL,
|
||||
`product_id` VARCHAR(64) NOT NULL,
|
||||
`quantity` INTEGER UNSIGNED NOT NULL DEFAULT 1,
|
||||
`amount_fen` INTEGER UNSIGNED NOT NULL,
|
||||
`status` TINYINT NOT NULL,
|
||||
`env` TINYINT NOT NULL DEFAULT 0,
|
||||
`wx_order_id` VARCHAR(64) NULL,
|
||||
`attach` VARCHAR(255) NULL,
|
||||
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`paid_at` DATETIME(3) NULL,
|
||||
`delivered_at` DATETIME(3) NULL,
|
||||
`refunded_at` DATETIME(3) NULL,
|
||||
|
||||
UNIQUE INDEX `orders_out_trade_no_key`(`out_trade_no`),
|
||||
UNIQUE INDEX `orders_wx_order_id_key`(`wx_order_id`),
|
||||
INDEX `orders_user_id_created_at_idx`(`user_id`, `created_at`),
|
||||
INDEX `orders_status_created_at_idx`(`status`, `created_at`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `payment_notify_log` (
|
||||
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`wx_order_id` VARCHAR(64) NULL,
|
||||
`out_trade_no` VARCHAR(32) NULL,
|
||||
`event` VARCHAR(64) NULL,
|
||||
`raw_body` MEDIUMTEXT NULL,
|
||||
`processed` TINYINT NOT NULL DEFAULT 0,
|
||||
`err_msg` VARCHAR(255) NULL,
|
||||
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
|
||||
INDEX `payment_notify_log_wx_order_id_idx`(`wx_order_id`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `products` (
|
||||
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`course_id` INTEGER UNSIGNED NOT NULL,
|
||||
`wx_product_id` VARCHAR(64) NOT NULL,
|
||||
`name` VARCHAR(128) NOT NULL,
|
||||
`price_fen` INTEGER UNSIGNED NOT NULL,
|
||||
`status` TINYINT NOT NULL DEFAULT 1,
|
||||
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updated_at` DATETIME(3) NOT NULL,
|
||||
|
||||
UNIQUE INDEX `products_wx_product_id_key`(`wx_product_id`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `admin_users` (
|
||||
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`username` VARCHAR(64) NOT NULL,
|
||||
`password_hash` VARCHAR(255) NOT NULL,
|
||||
`display_name` VARCHAR(64) NULL,
|
||||
`role` TINYINT NOT NULL DEFAULT 2,
|
||||
`totp_secret` VARCHAR(64) NULL,
|
||||
`status` TINYINT NOT NULL DEFAULT 1,
|
||||
`last_login_at` DATETIME(3) NULL,
|
||||
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updated_at` DATETIME(3) NOT NULL,
|
||||
|
||||
UNIQUE INDEX `admin_users_username_key`(`username`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `lessons` ADD CONSTRAINT `lessons_course_id_fkey` FOREIGN KEY (`course_id`) REFERENCES `courses`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `home_courses` ADD CONSTRAINT `home_courses_course_id_fkey` FOREIGN KEY (`course_id`) REFERENCES `courses`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `entitlements` ADD CONSTRAINT `entitlements_user_id_fkey` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `entitlements` ADD CONSTRAINT `entitlements_course_id_fkey` FOREIGN KEY (`course_id`) REFERENCES `courses`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `orders` ADD CONSTRAINT `orders_user_id_fkey` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `orders` ADD CONSTRAINT `orders_course_id_fkey` FOREIGN KEY (`course_id`) REFERENCES `courses`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `products` ADD CONSTRAINT `products_course_id_fkey` FOREIGN KEY (`course_id`) REFERENCES `courses`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
provider = "mysql"
|
||||
@@ -0,0 +1,306 @@
|
||||
// 叶雷讲数学 — 数据模型
|
||||
//
|
||||
// 约定:
|
||||
// - 表名与字段名统一英文 snake_case,模型名用 PascalCase 并 @@map 到表名
|
||||
// - 主键统一 id INT UNSIGNED AUTO_INCREMENT(本量级无需 BIGINT,且避免 JS BigInt 序列化问题)
|
||||
// - 时间统一 DATETIME,存 UTC,展示层转 Asia/Shanghai
|
||||
// - 金额统一 INT UNSIGNED,单位「分」,全程不换算
|
||||
// - 状态字段统一 TINYINT + 代码内枚举(不使用数据库 ENUM,便于演进)
|
||||
// - 不建外键约束,关系由应用层保证
|
||||
//
|
||||
// 数据库:MySQL 5.7
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "mysql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
/// 用户(对应旧表「用户信息」)
|
||||
model User {
|
||||
id Int @id @default(autoincrement()) @db.UnsignedInt
|
||||
openid String @unique @db.VarChar(64)
|
||||
nickname String? @db.VarChar(32)
|
||||
headicon_id Int @default(0) @map("headicon_id") @db.UnsignedInt
|
||||
/// 微信 sessionKey,AES-256-GCM 加密后存储;虚拟支付用户态签名必需
|
||||
wx_session_key String? @map("wx_session_key") @db.VarChar(255)
|
||||
session_key_updated_at DateTime? @map("session_key_updated_at")
|
||||
/// 1 正常 / 0 封禁
|
||||
status Int @default(1) @db.TinyInt
|
||||
registered_at DateTime @map("registered_at")
|
||||
last_login_at DateTime? @map("last_login_at")
|
||||
created_at DateTime @default(now()) @map("created_at")
|
||||
updated_at DateTime @updatedAt @map("updated_at")
|
||||
|
||||
entitlements Entitlement[]
|
||||
orders Order[]
|
||||
|
||||
@@map("users")
|
||||
}
|
||||
|
||||
/// 付费课程(由旧表「课程口令码读取信息」升级而来)
|
||||
model Course {
|
||||
id Int @id @default(autoincrement()) @db.UnsignedInt
|
||||
/// 旧 64 位 sha256 串,原值保留。
|
||||
/// 同时是视频目录名 Course/{code}/... 与封面名 Course/{code}.png,绝对不可改写。
|
||||
code String @unique @db.VarChar(64)
|
||||
title String @db.VarChar(128)
|
||||
intro String? @db.VarChar(255)
|
||||
cover_image String? @map("cover_image") @db.VarChar(255)
|
||||
price_fen Int @default(0) @map("price_fen") @db.UnsignedInt
|
||||
/// 0 下架 / 1 上架
|
||||
status Int @default(1) @db.TinyInt
|
||||
sort_order Int @default(0) @map("sort_order")
|
||||
created_at DateTime @default(now()) @map("created_at")
|
||||
updated_at DateTime @updatedAt @map("updated_at")
|
||||
|
||||
lessons Lesson[]
|
||||
entitlements Entitlement[]
|
||||
orders Order[]
|
||||
products Product[]
|
||||
home_courses HomeCourse[]
|
||||
|
||||
@@map("courses")
|
||||
}
|
||||
|
||||
/// 付费课时(对应旧表「视频课程数据」)
|
||||
model Lesson {
|
||||
id Int @id @default(autoincrement()) @db.UnsignedInt
|
||||
course_id Int @map("course_id") @db.UnsignedInt
|
||||
/// 原值保留,参与视频路径 Course/{code}/{chapter_id}/{play_id}/playlist.m3u8
|
||||
play_id String @map("play_id") @db.VarChar(64)
|
||||
chapter_id String @map("chapter_id") @db.VarChar(64)
|
||||
chapter_title String? @map("chapter_title") @db.VarChar(255)
|
||||
lesson_title String? @map("lesson_title") @db.VarChar(255)
|
||||
duration String? @db.VarChar(16)
|
||||
published_at DateTime? @map("published_at")
|
||||
cover_image String? @map("cover_image") @db.VarChar(255)
|
||||
sort_order Int @default(0) @map("sort_order")
|
||||
created_at DateTime @default(now()) @map("created_at")
|
||||
|
||||
course Course @relation(fields: [course_id], references: [id])
|
||||
|
||||
@@unique([course_id, chapter_id, play_id])
|
||||
@@index([course_id, sort_order])
|
||||
@@map("lessons")
|
||||
}
|
||||
|
||||
/// 免费试听课时(对应旧表「免费视频课程数据」)
|
||||
model FreeLesson {
|
||||
id Int @id @default(autoincrement()) @db.UnsignedInt
|
||||
/// 旧「序号」字段,对应首页试听入口
|
||||
series_no Int @map("series_no")
|
||||
sort_order Int @default(0) @map("sort_order")
|
||||
course_name String @map("course_name") @db.VarChar(255)
|
||||
video_title String? @map("video_title") @db.VarChar(255)
|
||||
intro String? @db.VarChar(255)
|
||||
publisher String? @db.VarChar(255)
|
||||
published_at DateTime? @map("published_at")
|
||||
duration String? @db.VarChar(16)
|
||||
/// 相对路径,如 FreeVideos/711/playlist.m3u8
|
||||
video_m3u8 String? @map("video_m3u8") @db.VarChar(255)
|
||||
cover_image String? @map("cover_image") @db.VarChar(255)
|
||||
created_at DateTime @default(now()) @map("created_at")
|
||||
|
||||
@@index([series_no, sort_order])
|
||||
@@map("free_lessons")
|
||||
}
|
||||
|
||||
/// 首页「新课上线」长图页(对应旧表「首页课程列表」)
|
||||
model HomeCourse {
|
||||
id Int @id @default(autoincrement()) @db.UnsignedInt
|
||||
/// 原「课程名称」原值保留:首页图片路径为 Images/NewCourse/{course_name}.png
|
||||
course_name String @map("course_name") @db.VarChar(255)
|
||||
intro String? @db.VarChar(255)
|
||||
published_at DateTime? @map("published_at")
|
||||
cover_image String? @map("cover_image") @db.VarChar(255)
|
||||
/// 长图 HTML。注意:内容中含绝对 URL,迁移时需做域名改写。
|
||||
content String? @db.LongText
|
||||
/// 关联可购买课程,用于挂载购买入口(旧数据可能为空)
|
||||
course_id Int? @map("course_id") @db.UnsignedInt
|
||||
sort_order Int @default(0) @map("sort_order")
|
||||
status Int @default(1) @db.TinyInt
|
||||
created_at DateTime @default(now()) @map("created_at")
|
||||
updated_at DateTime @updatedAt @map("updated_at")
|
||||
|
||||
course Course? @relation(fields: [course_id], references: [id])
|
||||
|
||||
@@index([status, sort_order])
|
||||
@@map("home_courses")
|
||||
}
|
||||
|
||||
/// 首页免费试听入口(对应旧表「首页免费课程列表」)
|
||||
model HomeFreeCourse {
|
||||
id Int @id @default(autoincrement()) @db.UnsignedInt
|
||||
/// 旧「序号」,前端按此跳转 /v1/free-lessons?series_no=
|
||||
series_no Int @map("series_no")
|
||||
/// 原值保留:图片路径为 Images/FreeCourse/{course_name}.png
|
||||
course_name String @map("course_name") @db.VarChar(255)
|
||||
intro String? @db.VarChar(255)
|
||||
published_at DateTime? @map("published_at")
|
||||
cover_image String? @map("cover_image") @db.VarChar(255)
|
||||
sort_order Int @default(0) @map("sort_order")
|
||||
status Int @default(1) @db.TinyInt
|
||||
|
||||
@@index([status, sort_order])
|
||||
@@map("home_free_courses")
|
||||
}
|
||||
|
||||
/// 首页轮播图(对应旧表「首页横幅图片」)
|
||||
model Banner {
|
||||
id Int @id @default(autoincrement()) @db.UnsignedInt
|
||||
/// 原「图片名」原值保留:图片路径为 Images/Banner/{filename}
|
||||
filename String @db.VarChar(255)
|
||||
sort_order Int @default(0) @map("sort_order")
|
||||
status Int @default(1) @db.TinyInt
|
||||
created_at DateTime @default(now()) @map("created_at")
|
||||
updated_at DateTime @updatedAt @map("updated_at")
|
||||
|
||||
@@index([status, sort_order])
|
||||
@@map("banners")
|
||||
}
|
||||
|
||||
/// 知识速通文章(对应旧表「网页列表」)
|
||||
model Article {
|
||||
id Int @id @default(autoincrement()) @db.UnsignedInt
|
||||
/// 旧「网页id」原值保留:首页缩略图路径为 WebPages/{legacy_page_id}.png
|
||||
legacy_page_id String? @map("legacy_page_id") @db.VarChar(32)
|
||||
title String @db.VarChar(255)
|
||||
intro String? @db.VarChar(255)
|
||||
publisher String? @db.VarChar(255)
|
||||
published_at DateTime? @map("published_at")
|
||||
/// 正文 HTML。注意:内容中含绝对 URL,迁移时需做域名改写。
|
||||
content String? @db.LongText
|
||||
sort_order Int @default(0) @map("sort_order")
|
||||
status Int @default(1) @db.TinyInt
|
||||
created_at DateTime @default(now()) @map("created_at")
|
||||
updated_at DateTime @updatedAt @map("updated_at")
|
||||
|
||||
@@index([status, published_at])
|
||||
@@index([legacy_page_id])
|
||||
@@map("articles")
|
||||
}
|
||||
|
||||
/// 课程页文案(对应旧表「课程页面Tips」)
|
||||
model CourseTip {
|
||||
id Int @id @default(autoincrement()) @db.UnsignedInt
|
||||
tip_key Int @unique @map("tip_key")
|
||||
content String? @db.Text
|
||||
title String? @db.TinyText
|
||||
|
||||
@@map("course_tips")
|
||||
}
|
||||
|
||||
/// 站点配置(对应旧表 cloudconfig,数字 ID 改为字符串 key)
|
||||
model Setting {
|
||||
id Int @id @default(autoincrement()) @db.UnsignedInt
|
||||
key String @unique @db.VarChar(64)
|
||||
value String? @db.VarChar(255)
|
||||
remark String? @db.VarChar(255)
|
||||
|
||||
@@map("settings")
|
||||
}
|
||||
|
||||
/// 权益 — 播放鉴权的唯一判断入口
|
||||
model Entitlement {
|
||||
id Int @id @default(autoincrement()) @db.UnsignedInt
|
||||
user_id Int @map("user_id") @db.UnsignedInt
|
||||
course_id Int @map("course_id") @db.UnsignedInt
|
||||
/// 1 订单 / 2 老数据(历史已购) / 3 后台发放
|
||||
source Int @db.TinyInt
|
||||
ref_id Int? @map("ref_id") @db.UnsignedInt
|
||||
granted_at DateTime @map("granted_at")
|
||||
expire_at DateTime? @map("expire_at")
|
||||
note String? @db.VarChar(255)
|
||||
created_at DateTime @default(now()) @map("created_at")
|
||||
|
||||
user User @relation(fields: [user_id], references: [id])
|
||||
course Course @relation(fields: [course_id], references: [id])
|
||||
|
||||
@@unique([user_id, course_id])
|
||||
@@index([course_id])
|
||||
@@map("entitlements")
|
||||
}
|
||||
|
||||
/// 订单
|
||||
model Order {
|
||||
id Int @id @default(autoincrement()) @db.UnsignedInt
|
||||
out_trade_no String @unique @map("out_trade_no") @db.VarChar(32)
|
||||
user_id Int @map("user_id") @db.UnsignedInt
|
||||
course_id Int @map("course_id") @db.UnsignedInt
|
||||
product_id String @map("product_id") @db.VarChar(64)
|
||||
quantity Int @default(1) @db.UnsignedInt
|
||||
amount_fen Int @map("amount_fen") @db.UnsignedInt
|
||||
/// 0 待支付 / 1 已支付 / 2 已发货 / 3 已退款 / 4 已关闭
|
||||
status Int @db.TinyInt
|
||||
/// 固定 0(正式环境)
|
||||
env Int @default(0) @db.TinyInt
|
||||
/// 微信平台单号,发货幂等依据
|
||||
wx_order_id String? @unique @map("wx_order_id") @db.VarChar(64)
|
||||
/// 下单时透传字段,发货通知原样带回
|
||||
attach String? @db.VarChar(255)
|
||||
created_at DateTime @default(now()) @map("created_at")
|
||||
paid_at DateTime? @map("paid_at")
|
||||
delivered_at DateTime? @map("delivered_at")
|
||||
refunded_at DateTime? @map("refunded_at")
|
||||
|
||||
user User @relation(fields: [user_id], references: [id])
|
||||
course Course @relation(fields: [course_id], references: [id])
|
||||
|
||||
@@index([user_id, created_at])
|
||||
@@index([status, created_at])
|
||||
@@map("orders")
|
||||
}
|
||||
|
||||
/// 发货推送原始记录,用于幂等、重放与排查
|
||||
model PaymentNotifyLog {
|
||||
id Int @id @default(autoincrement()) @db.UnsignedInt
|
||||
wx_order_id String? @map("wx_order_id") @db.VarChar(64)
|
||||
out_trade_no String? @map("out_trade_no") @db.VarChar(32)
|
||||
event String? @db.VarChar(64)
|
||||
raw_body String? @map("raw_body") @db.MediumText
|
||||
processed Int @default(0) @db.TinyInt
|
||||
err_msg String? @map("err_msg") @db.VarChar(255)
|
||||
created_at DateTime @default(now()) @map("created_at")
|
||||
|
||||
@@index([wx_order_id])
|
||||
@@map("payment_notify_log")
|
||||
}
|
||||
|
||||
/// 虚拟支付道具映射(course ↔ MP 后台道具)
|
||||
model Product {
|
||||
id Int @id @default(autoincrement()) @db.UnsignedInt
|
||||
course_id Int @map("course_id") @db.UnsignedInt
|
||||
wx_product_id String @unique @map("wx_product_id") @db.VarChar(64)
|
||||
name String @db.VarChar(128)
|
||||
/// 必须与 MP 后台道具价格一致,单位「分」
|
||||
price_fen Int @map("price_fen") @db.UnsignedInt
|
||||
status Int @default(1) @db.TinyInt
|
||||
created_at DateTime @default(now()) @map("created_at")
|
||||
updated_at DateTime @updatedAt @map("updated_at")
|
||||
|
||||
course Course @relation(fields: [course_id], references: [id])
|
||||
|
||||
@@map("products")
|
||||
}
|
||||
|
||||
/// 管理员(与微信用户体系完全隔离)
|
||||
model AdminUser {
|
||||
id Int @id @default(autoincrement()) @db.UnsignedInt
|
||||
username String @unique @db.VarChar(64)
|
||||
password_hash String @map("password_hash") @db.VarChar(255)
|
||||
display_name String? @map("display_name") @db.VarChar(64)
|
||||
/// 1 超管 / 2 运营
|
||||
role Int @default(2) @db.TinyInt
|
||||
totp_secret String? @map("totp_secret") @db.VarChar(64)
|
||||
status Int @default(1) @db.TinyInt
|
||||
last_login_at DateTime? @map("last_login_at")
|
||||
created_at DateTime @default(now()) @map("created_at")
|
||||
updated_at DateTime @updatedAt @map("updated_at")
|
||||
|
||||
@@map("admin_users")
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user