yanlongqi 45299b2d7e 优化UI体验和修复Markdown渲染问题
## 主要改进

### 1. 修复AI解析Markdown渲染混乱
- 安装remark-gfm插件支持GitHub Flavored Markdown
- 优化ReactMarkdown组件样式配置
- 支持表格、代码块、引用、列表等完整Markdown语法
- 改进代码块样式,区分行内代码和代码块
- 优化排版,提升可读性

### 2. 用户界面优化
- 修改个人信息中"昵称"改为"姓名"
- 调整答题设置提示位置到第一行
- 去除答题设置提示标题,使界面更简洁

### 3. 新增用户功能
- 添加修改个人信息功能(姓名、身份类型)
- 添加修改密码功能
- 优化用户信息显示和编辑体验

### 4. 技术改进
- 修复Home.tsx中handleLogout函数声明顺序问题
- 添加更完善的Markdown样式定义
- 优化流式渲染体验

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 23:14:03 +08:00

389 lines
8.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package handlers
import (
"ankao/internal/database"
"ankao/internal/models"
"crypto/sha256"
"encoding/hex"
"fmt"
"net/http"
"time"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
// generateToken 生成简单的token实际项目应使用JWT
func generateToken(username string) string {
data := fmt.Sprintf("%s:%d", username, time.Now().Unix())
hash := sha256.Sum256([]byte(data))
return hex.EncodeToString(hash[:])
}
// Login 用户登录处理
func Login(c *gin.Context) {
var req models.LoginRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "请求参数错误",
"error": err.Error(),
})
return
}
// 从数据库查找用户
var user models.User
db := database.GetDB()
result := db.Where("username = ?", req.Username).First(&user)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "用户名或密码错误",
})
return
}
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "服务器错误",
"error": result.Error.Error(),
})
return
}
// 验证密码
if !user.CheckPassword(req.Password) {
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "用户名或密码错误",
})
return
}
// 生成token
token := generateToken(req.Username)
// 保存token到数据库
user.Token = token
if err := db.Save(&user).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "token保存失败",
})
return
}
// 返回用户信息(不包含密码)
userInfo := models.UserInfoResponse{
Username: user.Username,
Avatar: user.Avatar,
Nickname: user.Nickname,
UserType: user.UserType, // 返回用户类型
}
// 检查用户类型是否为空,如果为空,标识需要补充
needUserType := user.UserType == ""
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "登录成功",
"data": models.LoginResponse{
Token: token,
User: userInfo,
},
"need_user_type": needUserType, // 添加标识,前端根据此标识显示补充弹窗
})
}
// Register 用户注册处理
func Register(c *gin.Context) {
var req models.RegisterRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "请求参数错误",
"error": err.Error(),
})
return
}
db := database.GetDB()
// 检查用户名是否已存在
var existingUser models.User
result := db.Where("username = ?", req.Username).First(&existingUser)
if result.Error == nil {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "用户名已存在",
})
return
}
// 创建新用户
newUser := models.User{
Username: req.Username,
Nickname: req.Nickname,
UserType: req.UserType, // 保存用户类型
Avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=" + req.Username, // 使用用户名生成默认头像
}
// 如果没有提供昵称,使用用户名
if newUser.Nickname == "" {
newUser.Nickname = req.Username
}
// 加密密码
if err := newUser.HashPassword(req.Password); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "密码加密失败",
"error": err.Error(),
})
return
}
// 生成token
token := generateToken(req.Username)
// 设置token
newUser.Token = token
// 保存到数据库
if err := db.Create(&newUser).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "注册失败",
"error": err.Error(),
})
return
}
// 返回用户信息
userInfo := models.UserInfoResponse{
Username: newUser.Username,
Avatar: newUser.Avatar,
Nickname: newUser.Nickname,
UserType: newUser.UserType, // 返回用户类型
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "注册成功",
"data": models.LoginResponse{
Token: token,
User: userInfo,
},
})
}
// UpdateUserTypeRequest 更新用户类型请求
type UpdateUserTypeRequest struct {
UserType string `json:"user_type" binding:"required,oneof=ordinary-person management-person"`
}
// UpdateUserType 更新用户类型
func UpdateUserType(c *gin.Context) {
var req UpdateUserTypeRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "请求参数错误,用户类型必须是 ordinary-person 或 management-person",
"error": err.Error(),
})
return
}
// 从上下文获取用户信息(由认证中间件设置)
username, exists := c.Get("username")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "未授权访问",
})
return
}
db := database.GetDB()
var user models.User
if err := db.Where("username = ?", username).First(&user).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{
"success": false,
"message": "用户不存在",
})
return
}
// 更新用户类型
user.UserType = req.UserType
if err := db.Save(&user).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "更新用户类型失败",
"error": err.Error(),
})
return
}
// 返回更新后的用户信息
userInfo := models.UserInfoResponse{
Username: user.Username,
Avatar: user.Avatar,
Nickname: user.Nickname,
UserType: user.UserType,
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "用户类型更新成功",
"data": userInfo,
})
}
// UpdateProfileRequest 更新用户信息请求
type UpdateProfileRequest struct {
Nickname string `json:"nickname" binding:"required"`
UserType string `json:"user_type" binding:"required,oneof=ordinary-person management-person"`
}
// UpdateProfile 更新用户信息
func UpdateProfile(c *gin.Context) {
var req UpdateProfileRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "请求参数错误",
"error": err.Error(),
})
return
}
// 从上下文获取用户信息(由认证中间件设置)
username, exists := c.Get("username")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "未授权访问",
})
return
}
db := database.GetDB()
var user models.User
if err := db.Where("username = ?", username).First(&user).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{
"success": false,
"message": "用户不存在",
})
return
}
// 更新用户信息
user.Nickname = req.Nickname
user.UserType = req.UserType
if err := db.Save(&user).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "更新用户信息失败",
"error": err.Error(),
})
return
}
// 返回更新后的用户信息
userInfo := models.UserInfoResponse{
Username: user.Username,
Avatar: user.Avatar,
Nickname: user.Nickname,
UserType: user.UserType,
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "用户信息更新成功",
"data": userInfo,
})
}
// ChangePasswordRequest 修改密码请求
type ChangePasswordRequest struct {
OldPassword string `json:"old_password" binding:"required"`
NewPassword string `json:"new_password" binding:"required,min=6"`
}
// ChangePassword 修改密码
func ChangePassword(c *gin.Context) {
var req ChangePasswordRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": "请求参数错误新密码长度至少为6位",
"error": err.Error(),
})
return
}
// 从上下文获取用户信息(由认证中间件设置)
username, exists := c.Get("username")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "未授权访问",
})
return
}
db := database.GetDB()
var user models.User
if err := db.Where("username = ?", username).First(&user).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{
"success": false,
"message": "用户不存在",
})
return
}
// 验证旧密码
if !user.CheckPassword(req.OldPassword) {
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "当前密码错误",
})
return
}
// 更新密码
if err := user.HashPassword(req.NewPassword); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "密码加密失败",
"error": err.Error(),
})
return
}
// 清除旧的token强制重新登录
user.Token = ""
if err := db.Save(&user).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"success": false,
"message": "密码更新失败",
"error": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "密码修改成功,请重新登录",
})
}