296 lines
8.1 KiB
Go
296 lines
8.1 KiB
Go
package handlers
|
||
|
||
import (
|
||
"net/http"
|
||
"strconv"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
"github.com/go-playground/validator/v10"
|
||
|
||
"github.com/Nanqipro/YunQue-Tech-Projects/ai_english_learning/serve/internal/common"
|
||
"github.com/Nanqipro/YunQue-Tech-Projects/ai_english_learning/serve/internal/services"
|
||
"github.com/Nanqipro/YunQue-Tech-Projects/ai_english_learning/serve/internal/utils"
|
||
)
|
||
|
||
// UserHandler 用户处理器
|
||
type UserHandler struct {
|
||
userService *services.UserService
|
||
validator *validator.Validate
|
||
}
|
||
|
||
// NewUserHandler 创建用户处理器实例
|
||
func NewUserHandler(userService *services.UserService) *UserHandler {
|
||
return &UserHandler{
|
||
userService: userService,
|
||
validator: validator.New(),
|
||
}
|
||
}
|
||
|
||
// UpdateUserRequest 更新用户信息请求结构
|
||
type UpdateUserRequest struct {
|
||
Username string `json:"username" validate:"omitempty,min=3,max=20"`
|
||
Email string `json:"email" validate:"omitempty,email"`
|
||
Nickname string `json:"nickname" validate:"omitempty,min=1,max=50"`
|
||
Avatar string `json:"avatar" validate:"omitempty,url"`
|
||
Timezone string `json:"timezone" validate:"omitempty"`
|
||
Language string `json:"language" validate:"omitempty"`
|
||
}
|
||
|
||
// UpdateUserPreferencesRequest 更新用户偏好设置请求结构
|
||
type UpdateUserPreferencesRequest struct {
|
||
DailyGoal int `json:"daily_goal" validate:"omitempty,min=1,max=1000"`
|
||
WeeklyGoal int `json:"weekly_goal" validate:"omitempty,min=1,max=7000"`
|
||
ReminderEnabled bool `json:"reminder_enabled"`
|
||
DifficultyLevel string `json:"difficulty_level" validate:"omitempty,oneof=beginner intermediate advanced"`
|
||
LearningMode string `json:"learning_mode" validate:"omitempty,oneof=casual intensive exam"`
|
||
}
|
||
|
||
// UserStatsResponse 用户学习统计响应结构
|
||
type UserStatsResponse struct {
|
||
TotalWords int `json:"total_words"`
|
||
LearnedWords int `json:"learned_words"`
|
||
MasteredWords int `json:"mastered_words"`
|
||
StudyDays int `json:"study_days"`
|
||
ConsecutiveDays int `json:"consecutive_days"`
|
||
TotalStudyTime int `json:"total_study_time"` // 分钟
|
||
}
|
||
|
||
// GetUserProfile 获取用户信息
|
||
func (h *UserHandler) GetUserProfile(c *gin.Context) {
|
||
userID, exists := utils.GetUserIDFromContext(c)
|
||
if !exists {
|
||
common.BadRequestResponse(c, "请先登录")
|
||
return
|
||
}
|
||
|
||
user, err := h.userService.GetUserByID(userID)
|
||
if err != nil {
|
||
if businessErr, ok := err.(*common.BusinessError); ok {
|
||
common.ErrorResponse(c, http.StatusBadRequest, businessErr.Message)
|
||
return
|
||
}
|
||
common.InternalServerErrorResponse(c, "获取用户信息失败")
|
||
return
|
||
}
|
||
|
||
// 获取用户偏好设置
|
||
preferences, err := h.userService.GetUserPreferences(userID)
|
||
if err != nil {
|
||
// 偏好设置获取失败不影响用户信息返回,记录日志即可
|
||
preferences = nil
|
||
}
|
||
|
||
// 构造响应数据
|
||
response := map[string]interface{}{
|
||
"id": user.ID,
|
||
"username": user.Username,
|
||
"email": user.Email,
|
||
"nickname": user.Nickname,
|
||
"avatar": user.Avatar,
|
||
"timezone": user.Timezone,
|
||
"language": user.Language,
|
||
"status": user.Status,
|
||
"created_at": user.CreatedAt,
|
||
"updated_at": user.UpdatedAt,
|
||
}
|
||
|
||
if preferences != nil {
|
||
response["preferences"] = map[string]interface{}{
|
||
"daily_goal": preferences.DailyGoal,
|
||
"weekly_goal": preferences.WeeklyGoal,
|
||
"reminder_enabled": preferences.ReminderEnabled,
|
||
"difficulty_level": preferences.DifficultyLevel,
|
||
"learning_mode": preferences.LearningMode,
|
||
}
|
||
}
|
||
|
||
common.SuccessResponse(c, response)
|
||
}
|
||
|
||
// UpdateUserProfile 更新用户信息
|
||
func (h *UserHandler) UpdateUserProfile(c *gin.Context) {
|
||
userID, exists := utils.GetUserIDFromContext(c)
|
||
if !exists {
|
||
common.BadRequestResponse(c, "请先登录")
|
||
return
|
||
}
|
||
|
||
var req UpdateUserRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
common.ValidationErrorResponse(c, err)
|
||
return
|
||
}
|
||
|
||
// 验证请求参数
|
||
if err := h.validator.Struct(&req); err != nil {
|
||
common.ValidationErrorResponse(c, err)
|
||
return
|
||
}
|
||
|
||
// 构造更新数据
|
||
updates := make(map[string]interface{})
|
||
if req.Username != "" {
|
||
updates["username"] = req.Username
|
||
}
|
||
if req.Email != "" {
|
||
updates["email"] = req.Email
|
||
}
|
||
if req.Nickname != "" {
|
||
updates["nickname"] = req.Nickname
|
||
}
|
||
if req.Avatar != "" {
|
||
updates["avatar"] = req.Avatar
|
||
}
|
||
if req.Timezone != "" {
|
||
updates["timezone"] = req.Timezone
|
||
}
|
||
if req.Language != "" {
|
||
updates["language"] = req.Language
|
||
}
|
||
|
||
if len(updates) == 0 {
|
||
common.BadRequestResponse(c, "没有需要更新的字段")
|
||
return
|
||
}
|
||
|
||
// 更新用户信息
|
||
user, err := h.userService.UpdateUser(userID, updates)
|
||
if err != nil {
|
||
if businessErr, ok := err.(*common.BusinessError); ok {
|
||
common.ErrorResponse(c, http.StatusBadRequest, businessErr.Message)
|
||
return
|
||
}
|
||
common.InternalServerErrorResponse(c, "更新用户信息失败")
|
||
return
|
||
}
|
||
|
||
common.SuccessResponse(c, user)
|
||
}
|
||
|
||
// UpdateUserPreferences 更新用户偏好设置
|
||
func (h *UserHandler) UpdateUserPreferences(c *gin.Context) {
|
||
userID, exists := utils.GetUserIDFromContext(c)
|
||
if !exists {
|
||
common.BadRequestResponse(c, "请先登录")
|
||
return
|
||
}
|
||
|
||
var req UpdateUserPreferencesRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
common.ValidationErrorResponse(c, err)
|
||
return
|
||
}
|
||
|
||
// 验证请求参数
|
||
if err := h.validator.Struct(&req); err != nil {
|
||
common.ValidationErrorResponse(c, err)
|
||
return
|
||
}
|
||
|
||
// 构造更新数据
|
||
updates := make(map[string]interface{})
|
||
if req.DailyGoal > 0 {
|
||
updates["daily_goal"] = req.DailyGoal
|
||
}
|
||
if req.WeeklyGoal > 0 {
|
||
updates["weekly_goal"] = req.WeeklyGoal
|
||
}
|
||
updates["reminder_enabled"] = req.ReminderEnabled
|
||
if req.DifficultyLevel != "" {
|
||
updates["difficulty_level"] = req.DifficultyLevel
|
||
}
|
||
if req.LearningMode != "" {
|
||
updates["learning_mode"] = req.LearningMode
|
||
}
|
||
|
||
// 更新用户偏好设置
|
||
preferences, err := h.userService.UpdateUserPreferences(userID, updates)
|
||
if err != nil {
|
||
if businessErr, ok := err.(*common.BusinessError); ok {
|
||
common.ErrorResponse(c, http.StatusBadRequest, businessErr.Message)
|
||
return
|
||
}
|
||
common.InternalServerErrorResponse(c, "更新偏好设置失败")
|
||
return
|
||
}
|
||
|
||
common.SuccessResponse(c, preferences)
|
||
}
|
||
|
||
// GetUserStats 获取用户学习统计
|
||
func (h *UserHandler) GetUserStats(c *gin.Context) {
|
||
userID, exists := utils.GetUserIDFromContext(c)
|
||
if !exists {
|
||
common.BadRequestResponse(c, "请先登录")
|
||
return
|
||
}
|
||
|
||
// 获取时间范围参数
|
||
timeRange := c.DefaultQuery("time_range", "all") // all, week, month, year
|
||
|
||
// 这里需要实现具体的统计逻辑,暂时返回模拟数据
|
||
// TODO: 实现真实的统计查询,使用userID和timeRange参数
|
||
_ = userID // 避免未使用变量错误
|
||
_ = timeRange // 避免未使用变量错误
|
||
stats := &UserStatsResponse{
|
||
TotalWords: 1000,
|
||
LearnedWords: 750,
|
||
MasteredWords: 500,
|
||
StudyDays: 30,
|
||
ConsecutiveDays: 7,
|
||
TotalStudyTime: 1800, // 30小时
|
||
}
|
||
|
||
common.SuccessResponse(c, stats)
|
||
}
|
||
|
||
// GetUserLearningProgress 获取用户学习进度
|
||
func (h *UserHandler) GetUserLearningProgress(c *gin.Context) {
|
||
userID, exists := utils.GetUserIDFromContext(c)
|
||
if !exists {
|
||
common.BadRequestResponse(c, "请先登录")
|
||
return
|
||
}
|
||
|
||
// 获取分页参数
|
||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20"))
|
||
if page < 1 {
|
||
page = 1
|
||
}
|
||
if limit < 1 || limit > 100 {
|
||
limit = 20
|
||
}
|
||
|
||
// 获取过滤参数
|
||
masteryLevel := c.Query("mastery_level")
|
||
categoryID := c.Query("category_id")
|
||
|
||
// 调用词汇服务获取用户的学习进度
|
||
progressList, total, err := h.userService.GetUserLearningProgress(utils.Int64ToString(userID), page, limit)
|
||
if err != nil {
|
||
common.InternalServerErrorResponse(c, "获取学习进度失败")
|
||
return
|
||
}
|
||
|
||
totalPages := int64(0)
|
||
if limit > 0 {
|
||
totalPages = (total + int64(limit) - 1) / int64(limit)
|
||
}
|
||
|
||
response := map[string]interface{}{
|
||
"progress": progressList,
|
||
"pagination": map[string]interface{}{
|
||
"page": page,
|
||
"limit": limit,
|
||
"total": total,
|
||
"total_page": totalPages,
|
||
},
|
||
"filters": map[string]interface{}{
|
||
"mastery_level": masteryLevel,
|
||
"category_id": categoryID,
|
||
},
|
||
}
|
||
|
||
common.SuccessResponse(c, response)
|
||
} |