All checks were successful
continuous-integration/drone/push Build is passing
- 新增数据库表: growth_path_nodes, user_growth_path_progress, user_node_completions - 新增 Model: GrowthPathNode, UserGrowthPathProgress, UserNodeCompletion - 新增 Service: GrowthPathService(管理端CRUD、学员端进度追踪) - 新增 API: 学员端获取成长路径、管理端CRUD - 前端学员端从API动态加载成长路径数据 - 更新管理端API接口定义
514 lines
11 KiB
TypeScript
514 lines
11 KiB
TypeScript
/**
|
||
* 学员功能模块 API
|
||
*/
|
||
import request from '../request'
|
||
|
||
// 课程信息
|
||
export interface CourseInfo {
|
||
id: number
|
||
title: string
|
||
name?: string // 与title兼容
|
||
description?: string
|
||
coverImage?: string
|
||
category: string
|
||
difficulty: 'beginner' | 'intermediate' | 'advanced'
|
||
difficulty_level?: string // 与difficulty的字符串版本兼容
|
||
duration: number // 分钟
|
||
duration_hours?: number // 与duration的小时版本兼容
|
||
learner_count?: number // 学习人数
|
||
materialCount: number
|
||
progress: number // 0-100
|
||
rating: number
|
||
status: 'published' | 'draft' | 'archived'
|
||
tags: string[]
|
||
allow_download?: boolean // 是否允许下载资料
|
||
createdAt: string
|
||
updatedAt: string
|
||
}
|
||
|
||
// 课程材料
|
||
export interface CourseMaterial {
|
||
id: number
|
||
courseId: number
|
||
title: string
|
||
type: 'video' | 'audio' | 'document' | 'pdf' | 'ppt' | 'image' | 'text' | 'download' | 'html' | 'other'
|
||
url: string
|
||
duration?: number
|
||
size?: number
|
||
order: number
|
||
isCompleted: boolean
|
||
createdAt: string
|
||
}
|
||
|
||
// 成长路径节点(学员端)
|
||
export interface TraineeGrowthPathNode {
|
||
id: number
|
||
course_id: number
|
||
title: string
|
||
description?: string
|
||
stage_name?: string
|
||
is_required: boolean
|
||
estimated_days: number
|
||
order_num: number
|
||
status: 'locked' | 'unlocked' | 'in_progress' | 'completed'
|
||
progress: number
|
||
course_name?: string
|
||
course_cover?: string
|
||
}
|
||
|
||
// 成长路径阶段
|
||
export interface TraineeGrowthPathStage {
|
||
name: string
|
||
description?: string
|
||
completed: number
|
||
total: number
|
||
nodes: TraineeGrowthPathNode[]
|
||
}
|
||
|
||
// 成长路径(学员端响应)
|
||
export interface TraineeGrowthPath {
|
||
id: number
|
||
name: string
|
||
description?: string
|
||
position_id?: number
|
||
position_name?: string
|
||
total_progress: number
|
||
completed_nodes: number
|
||
total_nodes: number
|
||
status: 'not_started' | 'in_progress' | 'completed'
|
||
started_at?: string
|
||
estimated_completion_days?: number
|
||
stages: TraineeGrowthPathStage[]
|
||
}
|
||
|
||
// 兼容旧接口
|
||
export interface GrowthPathNode {
|
||
id: number
|
||
courseId: number
|
||
courseName: string
|
||
courseDescription?: string
|
||
isRequired: boolean
|
||
isCompleted: boolean
|
||
progress: number
|
||
prerequisiteIds: number[]
|
||
estimatedDays: number
|
||
order: number
|
||
}
|
||
|
||
// 兼容旧接口
|
||
export interface GrowthPath {
|
||
id: number
|
||
name: string
|
||
description?: string
|
||
positionId: number
|
||
positionName: string
|
||
totalCourses: number
|
||
completedCourses: number
|
||
requiredCourses: number
|
||
optionalCourses: number
|
||
estimatedDays: number
|
||
progress: number
|
||
nodes: GrowthPathNode[]
|
||
}
|
||
|
||
// 能力评估数据
|
||
export interface AbilityAssessment {
|
||
categories: Array<{
|
||
name: string
|
||
score: number
|
||
maxScore: number
|
||
skills: Array<{
|
||
name: string
|
||
score: number
|
||
maxScore: number
|
||
}>
|
||
}>
|
||
overallScore: number
|
||
maxOverallScore: number
|
||
lastUpdated: string
|
||
}
|
||
|
||
// AI陪练场景
|
||
export interface PracticeScene {
|
||
id: number
|
||
name: string
|
||
description: string
|
||
category: string
|
||
difficulty: 'easy' | 'medium' | 'hard'
|
||
estimatedDuration: number // 分钟
|
||
usageCount: number
|
||
averageRating: number
|
||
status: 'active' | 'inactive'
|
||
tags: string[]
|
||
aiRole: string
|
||
objectives: string[]
|
||
keywords: string[]
|
||
backgroundInfo: string
|
||
createdAt: string
|
||
}
|
||
|
||
// 陪练记录
|
||
export interface PracticeRecord {
|
||
id: string
|
||
sceneId: number
|
||
sceneName: string
|
||
sceneCategory: string
|
||
duration: number // 秒
|
||
messageCount: number
|
||
overallScore: number
|
||
result: 'excellent' | 'good' | 'average' | 'needs_improvement'
|
||
startTime: string
|
||
endTime: string
|
||
createdAt: string
|
||
}
|
||
|
||
// 陪练会话消息
|
||
export interface PracticeMessage {
|
||
id: string
|
||
role: 'user' | 'assistant'
|
||
content: string
|
||
timestamp: string
|
||
audioUrl?: string
|
||
}
|
||
|
||
// 陪练分析报告
|
||
export interface PracticeReport {
|
||
id: string
|
||
recordId: string
|
||
overallScore: number
|
||
detailedScores: {
|
||
communication: number
|
||
professionalism: number
|
||
problemSolving: number
|
||
knowledge: number
|
||
}
|
||
strengths: string[]
|
||
improvements: string[]
|
||
suggestions: string[]
|
||
keyMoments: Array<{
|
||
timestamp: string
|
||
description: string
|
||
score: number
|
||
}>
|
||
summary: string
|
||
nextSteps: string[]
|
||
}
|
||
|
||
// 考试记录
|
||
export interface ExamRecord {
|
||
id: string
|
||
examName: string
|
||
examType: string
|
||
subject: string
|
||
totalScore: number
|
||
userScore: number
|
||
accuracy: number
|
||
duration: number // 秒
|
||
status: 'completed' | 'in_progress' | 'abandoned'
|
||
startTime: string
|
||
endTime?: string
|
||
createdAt: string
|
||
}
|
||
|
||
// 错题信息
|
||
export interface MistakeQuestion {
|
||
id: string
|
||
questionId: string
|
||
question: string
|
||
questionType: 'single' | 'multiple' | 'judge' | 'fill'
|
||
correctAnswer: string
|
||
userAnswer: string
|
||
explanation: string
|
||
mistakeCount: number
|
||
lastMistakeTime: string
|
||
isMastered: boolean
|
||
subject: string
|
||
difficulty: string
|
||
tags: string[]
|
||
}
|
||
|
||
/**
|
||
* 获取课程列表
|
||
*/
|
||
export const getCourseList = (params: {
|
||
page?: number
|
||
size?: number
|
||
category?: string
|
||
difficulty?: string
|
||
keyword?: string
|
||
} = {}) => {
|
||
return request.get<{
|
||
items: CourseInfo[]
|
||
total: number
|
||
page: number
|
||
size: number
|
||
}>('/api/v1/courses', { params })
|
||
}
|
||
|
||
/**
|
||
* 获取课程详情
|
||
*/
|
||
export const getCourseDetail = (courseId: number) => {
|
||
return request.get<CourseInfo>(`/api/v1/courses/${courseId}`)
|
||
}
|
||
|
||
/**
|
||
* 获取课程材料列表
|
||
*/
|
||
export const getCourseMaterials = (courseId: number) => {
|
||
return request.get<CourseMaterial[]>(`/api/v1/trainee/courses/${courseId}/materials`)
|
||
}
|
||
|
||
/**
|
||
* 标记材料为已完成
|
||
*/
|
||
export const markMaterialCompleted = (materialId: number) => {
|
||
return request.post(`/api/v1/trainee/materials/${materialId}/complete`)
|
||
}
|
||
|
||
/**
|
||
* 获取成长路径(学员端)
|
||
*/
|
||
export const getGrowthPath = (positionId?: number) => {
|
||
return request.get<TraineeGrowthPath>('/api/v1/trainee/growth-path', {
|
||
params: positionId ? { position_id: positionId } : {}
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 开始学习成长路径
|
||
*/
|
||
export const startGrowthPath = (growthPathId: number) => {
|
||
return request.post<{ success: boolean; message: string; progress_id: number }>('/api/v1/trainee/growth-path/start', {
|
||
growth_path_id: growthPathId
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 完成成长路径节点
|
||
*/
|
||
export const completeGrowthPathNode = (nodeId: number) => {
|
||
return request.post<{
|
||
completed: boolean
|
||
total_progress: number
|
||
is_path_completed: boolean
|
||
unlocked_nodes: number[]
|
||
}>('/api/v1/trainee/growth-path/node/complete', {
|
||
node_id: nodeId
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 获取能力评估数据
|
||
*/
|
||
export const getAbilityAssessment = () => {
|
||
return request.get<AbilityAssessment>('/api/v1/trainee/ability-assessment')
|
||
}
|
||
|
||
/**
|
||
* 获取AI陪练场景列表
|
||
*/
|
||
export const getPracticeScenes = (params: {
|
||
category?: string
|
||
difficulty?: string
|
||
keyword?: string
|
||
} = {}) => {
|
||
return request.get<PracticeScene[]>('/api/v1/trainee/practice-scenes', { params })
|
||
}
|
||
|
||
/**
|
||
* 获取陪练场景详情
|
||
*/
|
||
export const getPracticeSceneDetail = (sceneId: number) => {
|
||
return request.get<PracticeScene>(`/api/v1/trainee/practice-scenes/${sceneId}`)
|
||
}
|
||
|
||
/**
|
||
* 开始AI陪练
|
||
*/
|
||
export const startPractice = (sceneId: number) => {
|
||
return request.post<{ sessionId: string }>('/api/v1/trainee/practice/start', { sceneId })
|
||
}
|
||
|
||
/**
|
||
* 结束AI陪练
|
||
*/
|
||
export const endPractice = (sessionId: string) => {
|
||
return request.post<{ recordId: string }>('/api/v1/trainee/practice/end', { sessionId })
|
||
}
|
||
|
||
/**
|
||
* 获取陪练记录列表
|
||
*/
|
||
export const getPracticeRecords = (params: {
|
||
page?: number
|
||
size?: number
|
||
sceneId?: number
|
||
result?: string
|
||
startDate?: string
|
||
endDate?: string
|
||
keyword?: string
|
||
} = {}) => {
|
||
return request.get<{
|
||
items: PracticeRecord[]
|
||
total: number
|
||
page: number
|
||
size: number
|
||
statistics: {
|
||
totalSessions: number
|
||
averageScore: number
|
||
totalDuration: number
|
||
monthlyProgress: number
|
||
}
|
||
}>('/api/v1/trainee/practice-records', { params })
|
||
}
|
||
|
||
/**
|
||
* 获取陪练记录详情(对话回放)
|
||
*/
|
||
export const getPracticeRecordDetail = (recordId: string) => {
|
||
return request.get<{
|
||
record: PracticeRecord
|
||
messages: PracticeMessage[]
|
||
}>(`/api/v1/trainee/practice-records/${recordId}`)
|
||
}
|
||
|
||
/**
|
||
* 获取陪练分析报告
|
||
*/
|
||
export const getPracticeReport = (recordId: string) => {
|
||
return request.get<PracticeReport>(`/api/v1/trainee/practice-reports/${recordId}`)
|
||
}
|
||
|
||
/**
|
||
* 获取考试记录列表
|
||
*/
|
||
export const getExamRecords = (params: {
|
||
page?: number
|
||
size?: number
|
||
examType?: string
|
||
subject?: string
|
||
minScore?: number
|
||
maxScore?: number
|
||
startDate?: string
|
||
endDate?: string
|
||
} = {}) => {
|
||
return request.get<{
|
||
items: ExamRecord[]
|
||
total: number
|
||
page: number
|
||
size: number
|
||
statistics: {
|
||
totalExams: number
|
||
averageScore: number
|
||
passRate: number
|
||
ranking: number
|
||
}
|
||
}>('/api/v1/trainee/exam-records', { params })
|
||
}
|
||
|
||
/**
|
||
* 获取考试记录详情
|
||
*/
|
||
export const getExamRecordDetail = (recordId: string) => {
|
||
return request.get<{
|
||
record: ExamRecord
|
||
questions: Array<{
|
||
questionId: string
|
||
question: string
|
||
options?: string[]
|
||
correctAnswer: string
|
||
userAnswer: string
|
||
isCorrect: boolean
|
||
score: number
|
||
}>
|
||
scoreByType: Array<{
|
||
type: string
|
||
totalQuestions: number
|
||
correctAnswers: number
|
||
accuracy: number
|
||
score: number
|
||
}>
|
||
}>(`/api/v1/trainee/exam-records/${recordId}`)
|
||
}
|
||
|
||
/**
|
||
* 获取错题列表
|
||
*/
|
||
export const getMistakeQuestions = (params: {
|
||
page?: number
|
||
size?: number
|
||
subject?: string
|
||
difficulty?: string
|
||
isMastered?: boolean
|
||
keyword?: string
|
||
} = {}) => {
|
||
return request.get<{
|
||
items: MistakeQuestion[]
|
||
total: number
|
||
page: number
|
||
size: number
|
||
statistics: {
|
||
totalMistakes: number
|
||
masteredCount: number
|
||
recentMistakes: number
|
||
}
|
||
}>('/api/v1/trainee/mistakes', { params })
|
||
}
|
||
|
||
/**
|
||
* 标记错题为已掌握
|
||
*/
|
||
export const markQuestionMastered = (questionId: string) => {
|
||
return request.post(`/api/v1/trainee/mistakes/${questionId}/mastered`)
|
||
}
|
||
|
||
/**
|
||
* 错题重练
|
||
*/
|
||
export const practicemistake = (questionIds: string[]) => {
|
||
return request.post<{ examId: string }>('/api/v1/trainee/mistakes/practice', { questionIds })
|
||
}
|
||
|
||
/**
|
||
* 创建课程对话会话
|
||
*/
|
||
export const createCourseChat = (courseId: number) => {
|
||
return request.post<{ sessionId: string }>('/api/v1/trainee/course-chat/sessions', { courseId })
|
||
}
|
||
|
||
/**
|
||
* 发送课程对话消息
|
||
*/
|
||
export const sendCourseMessage = (sessionId: string, content: string) => {
|
||
return request.post<{ messageId: string; response: string }>('/api/v1/trainee/course-chat/messages', {
|
||
sessionId,
|
||
content
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 分析智能工牌数据
|
||
* 调用后端API分析言迹智能工牌的录音数据,生成能力评估报告和课程推荐
|
||
*/
|
||
export const analyzeYanjiBadge = () => {
|
||
return request.post<{
|
||
assessment_id: number
|
||
total_score: number
|
||
dimensions: Array<{
|
||
name: string
|
||
score: number
|
||
feedback: string
|
||
}>
|
||
recommended_courses: Array<{
|
||
course_id: number
|
||
course_name: string
|
||
recommendation_reason: string
|
||
priority: string
|
||
match_score: number
|
||
}>
|
||
conversation_count: number
|
||
analyzed_at: string
|
||
}>('/api/v1/ability/analyze-yanji')
|
||
}
|