276 lines
8.3 KiB
Go
276 lines
8.3 KiB
Go
|
|
package handler
|
||
|
|
|
||
|
|
import (
|
||
|
|
"net/http"
|
||
|
|
"strconv"
|
||
|
|
|
||
|
|
"github.com/gin-gonic/gin"
|
||
|
|
"github.com/Nanqipro/YunQue-Tech-Projects/ai_english_learning/serve/internal/services"
|
||
|
|
"github.com/Nanqipro/YunQue-Tech-Projects/ai_english_learning/serve/internal/models"
|
||
|
|
)
|
||
|
|
|
||
|
|
type SpeakingHandler struct {
|
||
|
|
speakingService *services.SpeakingService
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewSpeakingHandler(speakingService *services.SpeakingService) *SpeakingHandler {
|
||
|
|
return &SpeakingHandler{speakingService: speakingService}
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetSpeakingScenarios 获取口语场景列表
|
||
|
|
func (h *SpeakingHandler) GetSpeakingScenarios(c *gin.Context) {
|
||
|
|
level := c.Query("level")
|
||
|
|
category := c.Query("category")
|
||
|
|
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||
|
|
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
|
||
|
|
|
||
|
|
scenarios, total, err := h.speakingService.GetSpeakingScenarios(level, category, page, pageSize)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.JSON(http.StatusOK, gin.H{"scenarios": scenarios, "total": total})
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetSpeakingScenario 获取单个口语场景
|
||
|
|
func (h *SpeakingHandler) GetSpeakingScenario(c *gin.Context) {
|
||
|
|
id := c.Param("id")
|
||
|
|
|
||
|
|
scenario, err := h.speakingService.GetSpeakingScenario(id)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "Speaking scenario not found"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.JSON(http.StatusOK, gin.H{"scenario": scenario})
|
||
|
|
}
|
||
|
|
|
||
|
|
// CreateSpeakingScenario 创建口语场景
|
||
|
|
func (h *SpeakingHandler) CreateSpeakingScenario(c *gin.Context) {
|
||
|
|
var scenario models.SpeakingScenario
|
||
|
|
if err := c.ShouldBindJSON(&scenario); err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := h.speakingService.CreateSpeakingScenario(&scenario); err != nil {
|
||
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.JSON(http.StatusCreated, gin.H{"scenario": scenario})
|
||
|
|
}
|
||
|
|
|
||
|
|
// UpdateSpeakingScenario 更新口语场景
|
||
|
|
func (h *SpeakingHandler) UpdateSpeakingScenario(c *gin.Context) {
|
||
|
|
id := c.Param("id")
|
||
|
|
|
||
|
|
var updates models.SpeakingScenario
|
||
|
|
if err := c.ShouldBindJSON(&updates); err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := h.speakingService.UpdateSpeakingScenario(id, &updates); err != nil {
|
||
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
scenario, err := h.speakingService.GetSpeakingScenario(id)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.JSON(http.StatusOK, gin.H{"scenario": scenario})
|
||
|
|
}
|
||
|
|
|
||
|
|
// DeleteSpeakingScenario 删除口语场景
|
||
|
|
func (h *SpeakingHandler) DeleteSpeakingScenario(c *gin.Context) {
|
||
|
|
id := c.Param("id")
|
||
|
|
|
||
|
|
if err := h.speakingService.DeleteSpeakingScenario(id); err != nil {
|
||
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.JSON(http.StatusOK, gin.H{"message": "Speaking scenario deleted successfully"})
|
||
|
|
}
|
||
|
|
|
||
|
|
// SearchSpeakingScenarios 搜索口语场景
|
||
|
|
func (h *SpeakingHandler) SearchSpeakingScenarios(c *gin.Context) {
|
||
|
|
keyword := c.Query("keyword")
|
||
|
|
level := c.Query("level")
|
||
|
|
category := c.Query("category")
|
||
|
|
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||
|
|
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
|
||
|
|
|
||
|
|
scenarios, total, err := h.speakingService.SearchSpeakingScenarios(keyword, level, category, page, pageSize)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.JSON(http.StatusOK, gin.H{"scenarios": scenarios, "total": total})
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetRecommendedScenarios 获取推荐口语场景
|
||
|
|
func (h *SpeakingHandler) GetRecommendedScenarios(c *gin.Context) {
|
||
|
|
userID := c.GetString("user_id")
|
||
|
|
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "5"))
|
||
|
|
|
||
|
|
scenarios, err := h.speakingService.GetRecommendedScenarios(userID, limit)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.JSON(http.StatusOK, gin.H{"scenarios": scenarios})
|
||
|
|
}
|
||
|
|
|
||
|
|
// CreateSpeakingRecord 创建口语记录
|
||
|
|
func (h *SpeakingHandler) CreateSpeakingRecord(c *gin.Context) {
|
||
|
|
var record models.SpeakingRecord
|
||
|
|
if err := c.ShouldBindJSON(&record); err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
record.UserID = c.GetString("user_id")
|
||
|
|
if err := h.speakingService.CreateSpeakingRecord(&record); err != nil {
|
||
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.JSON(http.StatusCreated, gin.H{"record": record})
|
||
|
|
}
|
||
|
|
|
||
|
|
// UpdateSpeakingRecord 更新口语记录
|
||
|
|
func (h *SpeakingHandler) UpdateSpeakingRecord(c *gin.Context) {
|
||
|
|
id := c.Param("id")
|
||
|
|
|
||
|
|
var updates models.SpeakingRecord
|
||
|
|
if err := c.ShouldBindJSON(&updates); err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if err := h.speakingService.UpdateSpeakingRecord(id, &updates); err != nil {
|
||
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
record, err := h.speakingService.GetSpeakingRecord(id)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.JSON(http.StatusOK, gin.H{"record": record})
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetSpeakingRecord 获取口语记录详情
|
||
|
|
func (h *SpeakingHandler) GetSpeakingRecord(c *gin.Context) {
|
||
|
|
id := c.Param("id")
|
||
|
|
|
||
|
|
record, err := h.speakingService.GetSpeakingRecord(id)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "Speaking record not found"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.JSON(http.StatusOK, gin.H{"record": record})
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetUserSpeakingRecords 获取用户口语记录列表
|
||
|
|
func (h *SpeakingHandler) GetUserSpeakingRecords(c *gin.Context) {
|
||
|
|
userID := c.GetString("user_id")
|
||
|
|
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||
|
|
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
|
||
|
|
|
||
|
|
records, total, err := h.speakingService.GetUserSpeakingRecords(userID, page, pageSize)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.JSON(http.StatusOK, gin.H{
|
||
|
|
"records": records,
|
||
|
|
"total": total,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
// SubmitSpeaking 提交口语练习
|
||
|
|
func (h *SpeakingHandler) SubmitSpeaking(c *gin.Context) {
|
||
|
|
id := c.Param("id")
|
||
|
|
|
||
|
|
var req struct {
|
||
|
|
AudioURL string `json:"audio_url"`
|
||
|
|
Transcript string `json:"transcript"`
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := h.speakingService.SubmitSpeaking(id, req.AudioURL, req.Transcript); err != nil {
|
||
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.JSON(http.StatusOK, gin.H{"message": "Speaking submitted successfully"})
|
||
|
|
}
|
||
|
|
|
||
|
|
// GradeSpeaking AI评分口语练习
|
||
|
|
func (h *SpeakingHandler) GradeSpeaking(c *gin.Context) {
|
||
|
|
id := c.Param("id")
|
||
|
|
|
||
|
|
var req struct {
|
||
|
|
Pronunciation float64 `json:"pronunciation"`
|
||
|
|
Fluency float64 `json:"fluency"`
|
||
|
|
Accuracy float64 `json:"accuracy"`
|
||
|
|
Score float64 `json:"score"`
|
||
|
|
Feedback string `json:"feedback"`
|
||
|
|
Suggestions string `json:"suggestions"`
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := h.speakingService.GradeSpeaking(id, req.Pronunciation, req.Fluency, req.Accuracy, req.Score, req.Feedback, req.Suggestions); err != nil {
|
||
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.JSON(http.StatusOK, gin.H{"message": "Speaking graded successfully"})
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetUserSpeakingStats 获取用户口语统计
|
||
|
|
func (h *SpeakingHandler) GetUserSpeakingStats(c *gin.Context) {
|
||
|
|
userID := c.GetString("user_id")
|
||
|
|
|
||
|
|
stats, err := h.speakingService.GetUserSpeakingStats(userID)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.JSON(http.StatusOK, gin.H{"stats": stats})
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetSpeakingProgress 获取口语进度
|
||
|
|
func (h *SpeakingHandler) GetSpeakingProgress(c *gin.Context) {
|
||
|
|
userID := c.GetString("user_id")
|
||
|
|
scenarioID := c.Param("scenario_id")
|
||
|
|
|
||
|
|
progress, err := h.speakingService.GetSpeakingProgress(userID, scenarioID)
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "Speaking progress not found"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.JSON(http.StatusOK, gin.H{"progress": progress})
|
||
|
|
}
|