P8 平台轻量化改造 + SSG修复 + 编程导师 + 文档完善

- Prisma: Tool 模型加 affiliateLink;免费用户沙盒 10→5 次/日
- 后端: Tools API + /admin/tools CRUD 5 端点;Practices 完整模块
- 导航: 主菜单隐藏企业版/社区(URL 可访问)
- 首页: 重定位为 AI 工具指南;新增精选工具区块;Feature 重写
- 工具页: affiliateLink 绿色推荐 Badge
- SSG 修复: config.ts 构建时直连 localhost:4000,页面 108→127
- 沙盒: 新增编程导师场景(苏格拉底教学法)
- 练习系统: Practices 多场景练习(含结构化评分)
- 技能广场: 6 个付费 Skill(标题大师/回款助手等)
- 管理后台: Models/Posts/Practices CRUD 页面
- 文档: README + progress.md 全面更新;AGENTS.md 同步定位
- 清理: .env.example 移除;tsbuildinfo gitignore
This commit is contained in:
yuzhiran-dev
2026-06-18 18:14:07 +08:00
parent fb8152401b
commit 0f215d2aad
103 changed files with 5240 additions and 778 deletions
+75 -5
View File
@@ -44,7 +44,8 @@ model User {
status UserStatus @default(ACTIVE)
memberPlan MemberPlan @default(FREE)
memberExpire DateTime?
sandboxDaily Int @default(10)
sandboxDaily Int @default(5)
sandboxExtra Int @default(0)
followerCount Int @default(0)
followingCount Int @default(0)
postCount Int @default(0)
@@ -68,6 +69,8 @@ model User {
circleMemberships CircleMember[]
organizationMemberships OrganizationMember[]
notifications Notification[]
practiceSubmissions PracticeSubmission[]
userSkills UserSkill[]
@@map("users")
}
@@ -132,7 +135,7 @@ model Lesson {
id Int @id @default(autoincrement())
chapterId Int
title String
content String?
content String? @db.Text
videoUrl String?
duration Int?
sortOrder Int @default(0)
@@ -207,6 +210,7 @@ model Tool {
description String?
url String
icon String?
affiliateLink String?
categoryId Int?
tags String?
isFeatured Boolean @default(false)
@@ -225,7 +229,7 @@ model Content {
id Int @id @default(autoincrement())
title String
summary String?
content String?
content String? @db.Text
cover String?
categoryId Int?
tags String?
@@ -276,11 +280,14 @@ model Skill {
tasks String @db.Text
tags String
prerequisiteIds String?
price Float? @default(0)
sortOrder Int @default(0)
status String @default("ACTIVE")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
purchasedBy UserSkill[]
@@map("skills")
}
@@ -313,17 +320,34 @@ model Order {
payChannel String?
transactionId String?
gatewayOrderId String?
metadata String?
paidAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id])
user User @relation(fields: [userId], references: [id])
userSkills UserSkill[]
@@index([userId, status])
@@index([status, createdAt])
@@map("orders")
}
model UserSkill {
id Int @id @default(autoincrement())
userId Int
skillId String
orderId Int?
purchasedAt DateTime @default(now())
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
skill Skill @relation(fields: [skillId], references: [id])
order Order? @relation(fields: [orderId], references: [id])
@@unique([userId, skillId])
@@map("user_skills")
}
model Subscription {
id Int @id @default(autoincrement())
userId Int
@@ -384,7 +408,7 @@ model Post {
id Int @id @default(autoincrement())
userId Int
title String
content String
content String @db.Text
tags String?
circleId Int?
status String @default("PUBLISHED")
@@ -547,6 +571,52 @@ model Notification {
@@map("notifications")
}
model PracticeQuestion {
id Int @id @default(autoincrement())
skillId String?
scenario String @db.Text
title String
description String @db.Text
instructions String @db.Text
difficulty String @default("BEGINNER")
category String @default("general")
expectedCriteria String @db.Text
starterCode String? @db.Text
hint String? @db.Text
sortOrder Int @default(0)
status String @default("PUBLISHED")
attemptCount Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
submissions PracticeSubmission[]
@@map("practice_questions")
}
model PracticeSubmission {
id Int @id @default(autoincrement())
userId Int
questionId Int
answer String @db.Text
score Int?
maxScore Int @default(100)
feedback String? @db.Text
criteriaScores String? @db.Text
duration Int @default(0)
status String @default("SUBMITTED")
submittedAt DateTime @default(now())
scoredAt DateTime?
question PracticeQuestion @relation(fields: [questionId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@index([questionId])
@@index([userId, status])
@@map("practice_submissions")
}
model Banner {
id Int @id @default(autoincrement())
title String