28 KiB
28 KiB
技术选型与项目初始化
创建时间:2025-02-04 基于之前的设计文档,确定技术栈并搭建项目框架
一、技术选型确认
1.1 前端技术栈
| 技术 | 选择 | 版本 | 说明 |
|---|---|---|---|
| 框架 | Vue 3 | ^3.4.0 | 渐进式框架,生态完善 |
| 构建工具 | Vite | ^5.0.0 | 快速的开发服务器 |
| UI组件库 | Ant Design Vue | ^4.0.0 | 企业级UI组件 |
| 状态管理 | Pinia | ^2.1.0 | Vue官方推荐 |
| 路由 | Vue Router | ^4.2.0 | 官方路由管理 |
| HTTP客户端 | Axios | ^1.6.0 | Promise-based HTTP |
| 工具库 | Lodash-es | ^4.17.0 | 实用工具函数 |
| 日期处理 | Day.js | ^1.11.0 | 轻量级日期库 |
1.2 后端技术栈
| 技术 | 选择 | 版本 | 说明 |
|---|---|---|---|
| 框架 | NestJS | ^10.0.0 | Node.js企业级框架 |
| 语言 | TypeScript | ^5.0.0 | 类型安全 |
| ORM | Prisma | ^5.0.0 | 现代化ORM |
| 数据库 | PostgreSQL | ^15.0.0 | 开源关系数据库 |
| 缓存 | Redis | ^7.0.0 | 内存数据库 |
| 认证 | JWT + Passport | - | Token认证 |
| 文件存储 | 阿里云OSS / MinIO | - | 对象存储 |
| 验证 | class-validator | - | 数据验证 |
1.3 开发工具
| 工具 | 用途 |
|---|---|
| VS Code | 代码编辑器 |
| Git | 版本控制 |
| Docker | 容器化部署 |
| Postman | API测试 |
二、项目目录结构
2.1 整体结构
reading-platform/
├── frontend/ # 前端项目(三端统一)
│ ├── src/
│ │ ├── views/
│ │ │ ├── admin/ # 超管端页面
│ │ │ ├── school/ # 学校端页面
│ │ │ ├── teacher/ # 教师端页面
│ │ │ └── auth/ # 认证页面
│ │ ├── components/ # 公共组件
│ │ ├── stores/ # Pinia状态管理
│ │ ├── api/ # API请求
│ │ ├── router/ # 路由配置
│ │ ├── utils/ # 工具函数
│ │ └── types/ # TypeScript类型
│ ├── package.json
│ └── vite.config.ts
│
├── backend/ # 后端项目
│ ├── src/
│ │ ├── modules/
│ │ │ ├── auth/ # 认证模块
│ │ │ ├── admin/ # 超管端模块
│ │ │ ├── school/ # 学校端模块
│ │ │ ├── teacher/ # 教师端模块
│ │ │ ├── course/ # 课程包模块
│ │ │ ├── tenant/ # 租户模块
│ │ │ ├── common/ # 公共模块
│ │ │ └── upload/ # 文件上传模块
│ │ ├── common/
│ │ │ ├── decorators/ # 装饰器
│ │ │ ├── guards/ # 守卫
│ │ │ ├── filters/ # 过滤器
│ │ │ └── interceptors/ # 拦截器
│ │ ├── config/ # 配置
│ │ └── database/ # 数据库相关
│ ├── prisma/
│ │ └── schema.prisma # Prisma模型
│ ├── package.json
│ └── nest-cli.json
│
├── docs/ # 文档
│ └── ... # 已有的设计文档
│
├── docker-compose.yml # Docker编排
└── README.md
三、数据库模型(Prisma Schema)
3.1 核心模型定义
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
// ==================== 租户相关 ====================
model Tenant {
id BigInt @id @default(autoincrement())
name String
address String?
contactPerson String? @map("contact_person")
contactPhone String? @map("contact_phone")
logoUrl String? @map("logo_url")
packageType PackageType @map("package_type")
teacherQuota Int @default(20) @map("teacher_quota")
studentQuota Int @default(200) @map("student_quota")
storageQuota BigInt @default(5368709120) @map("storage_quota") // 5GB
startDate DateTime @map("start_date") @db.Date
expireDate DateTime @map("expire_date") @db.Date
teacherCount Int @default(0) @map("teacher_count")
studentCount Int @default(0) @map("student_count")
storageUsed BigInt @default(0) @map("storage_used")
status TenantStatus @default(ACTIVE)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
teachers Teacher[]
students Student[]
classes Class[]
lessons Lesson[]
tenantCourses TenantCourse[]
@@map("tenants")
}
enum PackageType {
BASIC
STANDARD
ADVANCED
}
enum TenantStatus {
ACTIVE
SUSPENDED
EXPIRED
}
// ==================== 教师相关 ====================
model Teacher {
id BigInt @id @default(autoincrement())
tenantId BigInt @map("tenant_id")
name String
phone String
email String?
loginAccount String @unique @map("login_account")
passwordHash String @map("password_hash")
classIds Json? @map("class_ids") // 存储负责的班级ID列表
status TeacherStatus @default(ACTIVE)
lessonCount Int @default(0) @map("lesson_count")
feedbackCount Int @default(0) @map("feedback_count")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
lastLoginAt DateTime? @map("last_login_at")
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
lessons Lesson[]
feedbacks LessonFeedback[]
@@map("teachers")
}
enum TeacherStatus {
ACTIVE
SUSPENDED
}
// ==================== 班级相关 ====================
model Class {
id BigInt @id @default(autoincrement())
tenantId BigInt @map("tenant_id")
name String
grade Grade
teacherId BigInt? @map("teacher_id")
studentCount Int @default(0) @map("student_count")
lessonCount Int @default(0) @map("lesson_count")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
teacher Teacher? @relation(fields: [teacherId], references: [id])
students Student[]
lessons Lesson[]
@@map("classes")
}
enum Grade {
SMALL // 小班
MIDDLE // 中班
BIG // 大班
}
// ==================== 学生相关 ====================
model Student {
id BigInt @id @default(autoincrement())
tenantId BigInt @map("tenant_id")
classId BigInt @map("class_id")
name String
gender Gender?
birthDate DateTime? @map("birth_date") @db.Date
parentPhone String? @map("parent_phone")
parentName String? @map("parent_name")
readingCount Int @default(0) @map("reading_count")
lessonCount Int @default(0) @map("lesson_count")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
class Class @relation(fields: [classId], references: [id], onDelete: Cascade)
records StudentRecord[]
@@map("students")
}
enum Gender {
MALE
FEMALE
}
// ==================== 课程包相关 ====================
model Course {
id BigInt @id @default(autoincrement())
name String
description String? @db.Text
pictureBookId BigInt? @map("picture_book_id")
pictureBookName String? @map("picture_book_name")
gradeTags Json @map("grade_tags") // ["small", "middle"]
domainTags Json @map("domain_tags") // [{"level1":"语言", "level2":"阅读与书写准备"}]
duration Int @default(25) // 分钟
status CourseStatus @default(DRAFT)
version String @default("1.0")
usageCount Int @default(0) @map("usage_count")
teacherCount Int @default(0) @map("teacher_count")
avgRating Decimal @default(0) @map("avg_rating") @db.Decimal(3, 2)
createdBy BigInt? @map("created_by")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
publishedAt DateTime? @map("published_at")
resources CourseResource[]
scripts CourseScript[]
activities CourseActivity[]
lessons Lesson[]
tenantCourses TenantCourse[]
@@map("courses")
}
enum CourseStatus {
DRAFT
REVIEWING
PUBLISHED
ARCHIVED
}
// ==================== 课程资源 ====================
model CourseResource {
id BigInt @id @default(autoincrement())
courseId BigInt @map("course_id")
resourceType ResourceType @map("resource_type")
resourceName String @map("resource_name")
fileUrl String @map("file_url")
fileSize BigInt? @map("file_size")
mimeType String? @map("mime_type")
metadata Json? // 其他元数据(时长、分辨率等)
sortOrder Int @default(0) @map("sort_order")
createdAt DateTime @default(now()) @map("created_at")
course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
@@map("course_resources")
}
enum ResourceType {
EBOOK
AUDIO
VIDEO
PPT
ANIMATION
INTERACTIVE
}
// ==================== 课程脚本 ====================
model CourseScript {
id BigInt @id @default(autoincrement())
courseId BigInt @map("course_id")
stepIndex Int @map("step_index")
stepName String @map("step_name")
stepType StepType @map("step_type")
duration Int // 分钟
objective String? @db.Text
teacherScript String? @map("teacher_script") @db.Text
interactionPoints Json? @map("interaction_points")
resourceIds Json? @map("resource_ids") // 关联的资源ID列表
sortOrder Int @default(0) @map("sort_order")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
pages CourseScriptPage[]
@@unique([courseId, stepIndex])
@@map("course_scripts")
}
enum StepType {
INTRO // 导入
GUIDE // 引导观察
READING // 师幼共读
ACTIVITY // 延伸活动
ENDING // 活动结束
}
// ==================== 逐页配置 ====================
model CourseScriptPage {
id BigInt @id @default(autoincrement())
scriptId BigInt @map("script_id")
pageNumber Int @map("page_number")
questions Json? // 提问列表
interactionComponent Json? @map("interaction_component")
teacherNotes String? @map("teacher_notes") @db.Text
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
script CourseScript @relation(fields: [scriptId], references: [id], onDelete: Cascade)
@@unique([scriptId, pageNumber])
@@map("course_script_pages")
}
// ==================== 延伸活动 ====================
model CourseActivity {
id BigInt @id @default(autoincrement())
courseId BigInt @map("course_id")
name String
domain String? // 所属领域
domainTagId BigInt? @map("domain_tag_id")
activityType ActivityType @map("activity_type")
duration Int? // 分钟
onlineMaterials Json? @map("online_materials")
offlineMaterials String? @map("offline_materials") @db.Text
activityGuide String? @map("activity_guide") @db.Text
objectives String? @db.Text
sortOrder Int @default(0) @map("sort_order")
createdAt DateTime @default(now()) @map("created_at")
course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
@@map("course_activities")
}
enum ActivityType {
HANDS_ON // 动手操作
COMMUNICATION // 交流讨论
GAME // 游戏互动
OTHER
}
// ==================== 授课记录 ====================
model Lesson {
id BigInt @id @default(autoincrement())
tenantId BigInt @map("tenant_id")
teacherId BigInt @map("teacher_id")
classId BigInt @map("class_id")
courseId BigInt @map("course_id")
plannedDatetime DateTime? @map("planned_datetime")
startDatetime DateTime? @map("start_datetime")
endDatetime DateTime? @map("end_datetime")
actualDuration Int? @map("actual_duration") // 分钟
status LessonStatus @default(PLANNED)
overallRating OverallRating? @map("overall_rating")
participationRating ParticipationRating? @map("participation_rating")
completionNote CompletionNote? @map("completion_note")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
tenant Tenant @relation(fields: [tenantId], references: [id])
teacher Teacher @relation(fields: [teacherId], references: [id])
class Class @relation(fields: [classId], references: [id])
course Course @relation(fields: [courseId], references: [id])
feedbacks LessonFeedback[]
records StudentRecord[]
@@map("lessons")
}
enum LessonStatus {
PLANNED
IN_PROGRESS
COMPLETED
CANCELLED
}
enum OverallRating {
EXCELLENT
GOOD
AVERAGE
NEEDS_IMPROVEMENT
}
enum ParticipationRating {
VERY_HIGH
HIGH
MEDIUM
LOW
}
enum CompletionNote {
ALL_COMPLETED
ADJUSTED
PARTIAL
}
// ==================== 课程反馈 ====================
model LessonFeedback {
id BigInt @id @default(autoincrement())
lessonId BigInt @map("lesson_id")
teacherId BigInt @map("teacher_id")
designQuality Int? @map("design_quality") // 1-5
participation Int? // 1-5
goalAchievement Int? @map("goal_achievement") // 1-5
stepFeedbacks Json? @map("step_feedbacks")
pros String? @db.Text
suggestions String? @db.Text
activitiesDone Json? @map("activities_done")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
lesson Lesson @relation(fields: [lessonId], references: [id], onDelete: Cascade)
teacher Teacher @relation(fields: [teacherId], references: [id])
@@unique([lessonId, teacherId])
@@map("lesson_feedbacks")
}
// ==================== 学生测评记录 ====================
model StudentRecord {
id BigInt @id @default(autoincrement())
lessonId BigInt @map("lesson_id")
studentId BigInt @map("student_id")
focus Int? // 1-5
participation Int?
interest Int?
understanding Int?
domainAchievements Json? @map("domain_achievements")
notes String? @db.Text
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
lesson Lesson @relation(fields: [lessonId], references: [id], onDelete: Cascade)
student Student @relation(fields: [studentId], references: [id])
@@unique([lessonId, studentId])
@@map("student_records")
}
// ==================== 标签体系 ====================
model Tag {
id BigInt @id @default(autoincrement())
level Int // 1-4
code String @unique
name String
parentId BigInt? @map("parent_id")
description String? @db.Text
metadata Json?
sortOrder Int @default(0) @map("sort_order")
createdAt DateTime @default(now()) @map("created_at")
@@index([parentId])
@@index([level])
@@map("tags")
}
// ==================== 租户课程授权 ====================
model TenantCourse {
id BigInt @id @default(autoincrement())
tenantId BigInt @map("tenant_id")
courseId BigInt @map("course_id")
authorized Boolean @default(true)
authorizedAt DateTime? @map("authorized_at")
createdAt DateTime @default(now()) @map("created_at")
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
@@unique([tenantId, courseId])
@@map("tenant_courses")
}
四、后端核心模块
4.1 项目初始化
# 创建后端项目
mkdir reading-platform-backend
cd reading-platform-backend
npm init -y
# 安装依赖
npm install @nestjs/common@^10.0.0 @nestjs/core@^10.0.0 @nestjs/platform-express@^10.0.0
npm install @nestjs/config@^3.0.0 @nestjs/jwt@^10.0.0 @nestjs/passport@^10.0.0
npm install @prisma/client@^5.0.0 passport@^0.6.0 passport-jwt@^4.0.0
npm install class-validator@^0.14.0 class-transformer@^0.5.1
npm install bcrypt@^5.1.0
npm install @nestjs/throttler@^5.0.0 # 限流
npm install -D @nestjs/cli@^10.0.0
npm install -D prisma@^5.0.0
npm install -D typescript@^5.0.0 @types/node@^20.0.0 @types/passport-jwt@^4.0.0 @types/bcrypt@^5.0.0
# 安装额外依赖
npm install @nestjs/swagger@^7.0.0 # API文档
npm install rxjs@^7.8.0
npm install reflect-metadata@^0.1.0
4.2 NestJS主模块
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 全局验证管道
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
}),
);
// CORS
app.enableCors({
origin: process.env.FRONTEND_URL || 'http://localhost:5173',
credentials: true,
});
// API前缀
app.setGlobalPrefix('api/v1');
const port = process.env.PORT || 3000;
await app.listen(port);
console.log(`🚀 Application is running on: http://localhost:${port}`);
console.log(`📚 API documentation: http://localhost:${port}/api/docs`);
}
bootstrap();
// src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { ThrottlerModule } from '@nestjs/throttler';
import { PrismaModule } from './database/prisma.module';
import { AuthModule } from './modules/auth/auth.module';
import { AdminModule } from './modules/admin/admin.module';
import { SchoolModule } from './modules/school/school.module';
import { TeacherModule } from './modules/teacher/teacher.module';
import { CourseModule } from './modules/course/course.module';
import { TenantModule } from './modules/tenant/tenant.module';
import { CommonModule } from './modules/common/common.module';
import { UploadModule } from './modules/upload/upload.module';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: `.env.${process.env.NODE_ENV || 'development'}`,
}),
ThrottlerModule.forRoot([{
ttl: 60000, // 60秒
limit: 100, // 最多100个请求
}]),
PrismaModule,
AuthModule,
AdminModule,
SchoolModule,
TeacherModule,
CourseModule,
TenantModule,
CommonModule,
UploadModule,
],
})
export class AppModule {}
五、前端项目初始化
5.1 项目创建
# 使用Vite创建Vue3项目
npm create vite@latest reading-platform-frontend -- --template vue-ts
cd reading-platform-frontend
# 安装依赖
npm install
npm install vue-router@^4.2.0 pinia@^2.1.0
npm install ant-design-vue@^4.0.0
npm install axios@^1.6.0 dayjs@^1.11.0 lodash-es@^4.17.0
npm install @ant-design/icons-vue@^7.0.0
# 开发依赖
npm install -D @types/lodash-es@^4.17.0 @types/node@^20.0.0
npm install -D unplugin-vue-components@^0.26.0 # 组件自动导入
npm install -D unplugin-auto-import@^0.17.0 # API自动导入
5.2 Vite配置
// vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers';
export default defineConfig({
plugins: [
vue(),
AutoImport({
imports: [
'vue',
'vue-router',
'pinia',
{
'ant-design-vue': [
'message',
'notification',
'Modal',
],
},
],
dts: 'src/auto-imports.d.ts',
}),
Components({
resolvers: [
AntDesignVueResolver({
importStyle: false,
}),
],
dts: 'src/components.d.ts',
}),
],
resolve: {
alias: {
'@': resolve(__dirname, 'src'),
},
},
server: {
port: 5173,
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
},
},
},
});
5.3 路由配置
// src/router/index.ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
const routes: RouteRecordRaw[] = [
{
path: '/login',
name: 'Login',
component: () => import('@/views/auth/LoginView.vue'),
meta: { requiresAuth: false },
},
{
path: '/admin',
name: 'Admin',
component: () => import('@/views/admin/LayoutView.vue'),
meta: { requiresAuth: true, role: 'admin' },
children: [
{
path: '',
redirect: '/admin/dashboard',
},
{
path: 'dashboard',
name: 'AdminDashboard',
component: () => import('@/views/admin/DashboardView.vue'),
},
{
path: 'courses',
name: 'AdminCourses',
component: () => import('@/views/admin/courses/CourseListView.vue'),
},
// ... 其他超管端路由
],
},
{
path: '/school',
name: 'School',
component: () => import('@/views/school/LayoutView.vue'),
meta: { requiresAuth: true, role: 'school' },
children: [
{
path: '',
redirect: '/school/dashboard',
},
{
path: 'dashboard',
name: 'SchoolDashboard',
component: () => import('@/views/school/DashboardView.vue'),
},
// ... 其他学校端路由
],
},
{
path: '/teacher',
name: 'Teacher',
component: () => import('@/views/teacher/LayoutView.vue'),
meta: { requiresAuth: true, role: 'teacher' },
children: [
{
path: '',
redirect: '/teacher/dashboard',
},
{
path: 'dashboard',
name: 'TeacherDashboard',
component: () => import('@/views/teacher/DashboardView.vue'),
},
{
path: 'courses',
name: 'TeacherCourses',
component: () => import('@/views/teacher/courses/CourseListView.vue'),
},
{
path: 'courses/:id',
name: 'TeacherCourseDetail',
component: () => import('@/views/teacher/courses/CourseDetailView.vue'),
},
{
path: 'courses/:id/prepare',
name: 'TeacherCoursePrepare',
component: () => import('@/views/teacher/courses/PrepareModeView.vue'),
},
{
path: 'lessons/:id',
name: 'TeacherLesson',
component: () => import('@/views/teacher/lessons/LessonView.vue'),
},
// ... 其他教师端路由
],
},
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: () => import('@/views/NotFoundView.vue'),
},
];
const router = createRouter({
history: createWebHistory(),
routes,
});
// 路由守卫
router.beforeEach((to, from, next) => {
const token = localStorage.getItem('token');
if (to.meta.requiresAuth && !token) {
next('/login');
} else if (to.path === '/login' && token) {
// 根据用户角色跳转
const role = localStorage.getItem('role');
next(`/${role}/dashboard`);
} else {
next();
}
});
export default router;
六、Docker配置
6.1 docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:15-alpine
container_name: reading-platform-db
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: password
POSTGRES_DB: reading_platform
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
container_name: reading-platform-redis
ports:
- "6379:6379"
volumes:
- redis_data:/data
backend:
build:
context: ./backend
dockerfile: Dockerfile
container_name: reading-platform-backend
environment:
NODE_ENV: development
DATABASE_URL: postgresql://admin:password@postgres:5432/reading_platform
REDIS_URL: redis://redis:6379
JWT_SECRET: your-secret-key
ports:
- "3000:3000"
depends_on:
- postgres
- redis
volumes:
- ./backend:/app
- /app/node_modules
command: npm run start:dev
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
container_name: reading-platform-frontend
ports:
- "5173:5173"
volumes:
- ./frontend:/app
- /app/node_modules
command: npm run dev
volumes:
postgres_data:
redis_data:
七、环境变量配置
7.1 后端环境变量
# backend/.env.development
NODE_ENV=development
PORT=3000
# 数据库
DATABASE_URL="postgresql://admin:password@localhost:5432/reading_platform"
# Redis
REDIS_URL="redis://localhost:6379"
# JWT
JWT_SECRET="your-super-secret-jwt-key-change-in-production"
JWT_EXPIRES_IN="7d"
# 文件上传
UPLOAD_DIR="./uploads"
MAX_FILE_SIZE=104857600 # 100MB
# OSS (阿里云)
OSS_REGION="oss-cn-hangzhou"
OSS_ACCESS_KEY_ID="your-access-key"
OSS_ACCESS_KEY_SECRET="your-secret"
OSS_BUCKET="reading-platform"
# 前端URL
FRONTEND_URL="http://localhost:5173"
7.2 前端环境变量
# frontend/.env.development
VITE_API_BASE_URL=http://localhost:3000/api/v1
VITE_APP_TITLE=幼儿阅读教学服务平台
八、下一步开发计划
更新时间: 2026-02-13 项目状态: 开发中 - 超管端课程包核心功能开发中
Phase 1: 基础框架(第1-2周)✅ 已完成
- 技术选型确认
- 项目初始化
- 数据库设计与迁移
- 认证系统实现
- 权限管理实现
- 标签体系初始化
Phase 2: 超管端核心(第3-6周)🔄 进行中
- 课程包制作工作台
- 课程包管理(草稿/发布/审核)
- 课程包审核流程
- 租户管理(基础功能)
- 资源库管理(第二阶段开发)
- 数据统计看板
Phase 3: 教师端核心(第7-10周)🔄 进行中
- 课程中心
- 备课模式
- 上课模式
- 班级管理
- 反馈记录
Phase 4: 学校端(第11-12周)🔄 框架完成
- 教师管理
- 学生管理
- 课程授权查看
- 数据报表
Phase 5: 反馈与数据(第13-14周)⬜ 待开发
- 教师反馈收集
- 测评记录
- 数据统计优化
- 性能优化
九、资源库开发策略(2026-02-13 确认)
根据MVP优先级,资源库功能采用三阶段开发策略:
| 阶段 | 内容 | 状态 |
|---|---|---|
| 第一阶段 | 完成课程包核心功能,关联绘本使用文本输入 | ✅ 进行中 |
| 第二阶段 | 开发资源库完整管理功能 | ⬜ 待开发 |
| 第三阶段 | 重构资源选择逻辑,整合课程包与资源库 | ⬜ 待开发 |
详见 开发进展记录.md 资源库开发策略部分。
现在让我开始创建具体的项目文件...