2025-10-24 00:09:45 +08:00
|
|
|
|
package admin
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"net/http"
|
2025-10-26 14:48:02 +08:00
|
|
|
|
"networkDev/controllers"
|
2025-10-24 00:09:45 +08:00
|
|
|
|
"networkDev/models"
|
|
|
|
|
|
"networkDev/utils"
|
|
|
|
|
|
"strings"
|
2025-10-26 14:48:02 +08:00
|
|
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
2025-10-24 00:09:45 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
2025-10-27 23:12:15 +08:00
|
|
|
|
// ============================================================================
|
|
|
|
|
|
// 全局变量
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
2025-10-26 14:48:02 +08:00
|
|
|
|
// 创建基础控制器实例
|
|
|
|
|
|
var baseController = controllers.NewBaseController()
|
|
|
|
|
|
|
2025-10-27 23:12:15 +08:00
|
|
|
|
// ============================================================================
|
|
|
|
|
|
// 页面处理器
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
2025-10-24 00:09:45 +08:00
|
|
|
|
// UserFragmentHandler 个人资料片段渲染
|
|
|
|
|
|
// - 渲染个人资料与修改密码表单
|
2025-10-26 14:48:02 +08:00
|
|
|
|
func UserFragmentHandler(c *gin.Context) {
|
|
|
|
|
|
c.HTML(http.StatusOK, "user.html", gin.H{})
|
2025-10-24 00:09:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-27 23:12:15 +08:00
|
|
|
|
// ============================================================================
|
|
|
|
|
|
// API处理器
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
2025-10-26 09:35:07 +08:00
|
|
|
|
// UserProfileQueryHandler 获取当前登录管理员的用户名
|
|
|
|
|
|
// - 返回 JSON: {username}
|
|
|
|
|
|
// - 直接从JWT获取用户名信息
|
2025-10-26 14:48:02 +08:00
|
|
|
|
func UserProfileQueryHandler(c *gin.Context) {
|
|
|
|
|
|
claims, _, err := GetCurrentAdminUserWithRefresh(c)
|
2025-10-24 00:09:45 +08:00
|
|
|
|
if err != nil {
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleValidationError(c, "未登录或会话已过期")
|
2025-10-24 00:09:45 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleSuccess(c, "ok", gin.H{
|
2025-10-26 09:35:07 +08:00
|
|
|
|
"username": claims.Username,
|
2025-10-24 00:09:45 +08:00
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// UserPasswordUpdateHandler 修改当前登录管理员的密码
|
|
|
|
|
|
// - 接收 JSON: {old_password, new_password, confirm_password}
|
|
|
|
|
|
// - 校验旧密码正确性、新密码与确认一致性
|
|
|
|
|
|
// - 成功后更新密码哈希
|
|
|
|
|
|
// - 自动刷新接近过期的JWT令牌
|
2025-10-26 14:48:02 +08:00
|
|
|
|
func UserPasswordUpdateHandler(c *gin.Context) {
|
|
|
|
|
|
claims, _, err := GetCurrentAdminUserWithRefresh(c)
|
2025-10-24 00:09:45 +08:00
|
|
|
|
if err != nil {
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleValidationError(c, "未登录或会话已过期")
|
2025-10-24 00:09:45 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var body struct {
|
|
|
|
|
|
OldPassword string `json:"old_password"`
|
|
|
|
|
|
NewPassword string `json:"new_password"`
|
|
|
|
|
|
ConfirmPassword string `json:"confirm_password"`
|
|
|
|
|
|
}
|
2025-10-27 23:12:15 +08:00
|
|
|
|
|
2025-10-26 14:48:02 +08:00
|
|
|
|
if !baseController.BindJSON(c, &body) {
|
2025-10-24 00:09:45 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 基础校验
|
2025-10-26 14:48:02 +08:00
|
|
|
|
if !baseController.ValidateRequired(c, map[string]interface{}{
|
2025-10-27 23:12:15 +08:00
|
|
|
|
"旧密码": body.OldPassword,
|
|
|
|
|
|
"新密码": body.NewPassword,
|
2025-10-26 14:48:02 +08:00
|
|
|
|
"确认密码": body.ConfirmPassword,
|
|
|
|
|
|
}) {
|
2025-10-24 00:09:45 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-10-27 23:12:15 +08:00
|
|
|
|
|
2025-10-24 00:09:45 +08:00
|
|
|
|
if len(body.NewPassword) < 6 {
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleValidationError(c, "新密码长度不能少于6位")
|
2025-10-24 00:09:45 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if body.NewPassword != body.ConfirmPassword {
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleValidationError(c, "两次输入的新密码不一致")
|
2025-10-24 00:09:45 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if body.NewPassword == body.OldPassword {
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleValidationError(c, "新密码不能与旧密码相同")
|
2025-10-24 00:09:45 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-26 11:57:31 +08:00
|
|
|
|
// 注释:由于使用了AdminAuthRequired中间件,已确保是管理员用户
|
2025-10-26 09:35:07 +08:00
|
|
|
|
|
|
|
|
|
|
// 获取数据库连接
|
2025-10-26 14:48:02 +08:00
|
|
|
|
db, ok := baseController.GetDB(c)
|
|
|
|
|
|
if !ok {
|
2025-10-24 00:09:45 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-26 09:35:07 +08:00
|
|
|
|
// 通过前缀匹配一次性获取所有管理员相关设置
|
|
|
|
|
|
var adminSettings []models.Settings
|
|
|
|
|
|
if err = db.Where("name LIKE ?", "admin_%").Find(&adminSettings).Error; err != nil {
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleInternalError(c, "获取管理员设置失败", err)
|
2025-10-26 09:35:07 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 将设置转换为map便于查找
|
|
|
|
|
|
settingsMap := make(map[string]string)
|
|
|
|
|
|
for _, setting := range adminSettings {
|
|
|
|
|
|
settingsMap[setting.Name] = setting.Value
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查必要的设置是否存在
|
|
|
|
|
|
adminPassword, hasPassword := settingsMap["admin_password"]
|
|
|
|
|
|
adminPasswordSalt, hasSalt := settingsMap["admin_password_salt"]
|
|
|
|
|
|
if !hasPassword || !hasSalt {
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleInternalError(c, "管理员密码设置不完整", nil)
|
2025-10-24 00:09:45 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-26 09:35:07 +08:00
|
|
|
|
// 校验旧密码
|
|
|
|
|
|
if !utils.VerifyPasswordWithSalt(body.OldPassword, adminPasswordSalt, adminPassword) {
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleValidationError(c, "旧密码不正确")
|
2025-10-24 00:09:45 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 生成新的密码盐值
|
|
|
|
|
|
newSalt, err := utils.GenerateRandomSalt()
|
|
|
|
|
|
if err != nil {
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleInternalError(c, "生成密码盐失败", err)
|
2025-10-24 00:09:45 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-26 09:35:07 +08:00
|
|
|
|
// 生成新密码哈希
|
|
|
|
|
|
newPasswordHash, err := utils.HashPasswordWithSalt(body.NewPassword, newSalt)
|
2025-10-24 00:09:45 +08:00
|
|
|
|
if err != nil {
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleInternalError(c, "生成密码哈希失败", err)
|
2025-10-24 00:09:45 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-26 09:35:07 +08:00
|
|
|
|
// 更新settings中的管理员密码和盐值
|
|
|
|
|
|
if err = db.Model(&models.Settings{}).Where("name = ?", "admin_password").Update("value", newPasswordHash).Error; err != nil {
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleInternalError(c, "更新密码失败", err)
|
2025-10-24 00:09:45 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-10-26 09:35:07 +08:00
|
|
|
|
if err = db.Model(&models.Settings{}).Where("name = ?", "admin_password_salt").Update("value", newSalt).Error; err != nil {
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleInternalError(c, "更新密码盐值失败", err)
|
2025-10-26 01:51:25 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 重新生成JWT令牌(包含新的密码哈希摘要)
|
2025-10-26 09:35:07 +08:00
|
|
|
|
adminUser := models.User{
|
|
|
|
|
|
Username: claims.Username,
|
|
|
|
|
|
Password: newPasswordHash,
|
|
|
|
|
|
PasswordSalt: newSalt,
|
|
|
|
|
|
}
|
|
|
|
|
|
newToken, err := generateJWTTokenForAdmin(adminUser)
|
2025-10-26 01:51:25 +08:00
|
|
|
|
if err != nil {
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleInternalError(c, "生成新令牌失败", err)
|
2025-10-26 01:51:25 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-26 03:05:27 +08:00
|
|
|
|
// 更新Cookie(使用安全配置)
|
2025-10-26 14:48:02 +08:00
|
|
|
|
c.SetCookie("admin_session", newToken, utils.GetDefaultCookieMaxAge(), "/", "", false, true)
|
2025-10-26 01:51:25 +08:00
|
|
|
|
|
|
|
|
|
|
// 密码修改成功,已重新生成JWT令牌
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleSuccess(c, "密码修改成功", nil)
|
2025-10-24 00:09:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// UserProfileUpdateHandler 修改当前登录管理员的用户名
|
|
|
|
|
|
// - 接收 JSON: {username}
|
|
|
|
|
|
// - 校验用户名非空、长度与唯一性
|
|
|
|
|
|
// - 更新数据库后重新签发JWT并写入 Cookie,保持前端展示的一致性
|
|
|
|
|
|
// - 自动刷新接近过期的JWT令牌
|
2025-10-26 14:48:02 +08:00
|
|
|
|
func UserProfileUpdateHandler(c *gin.Context) {
|
|
|
|
|
|
_, _, err := GetCurrentAdminUserWithRefresh(c)
|
2025-10-24 00:09:45 +08:00
|
|
|
|
if err != nil {
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleValidationError(c, "未登录或会话已过期")
|
2025-10-24 00:09:45 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var body struct {
|
|
|
|
|
|
Username string `json:"username"`
|
|
|
|
|
|
OldPassword string `json:"old_password"`
|
|
|
|
|
|
}
|
2025-10-26 14:48:02 +08:00
|
|
|
|
if !baseController.BindJSON(c, &body) {
|
2025-10-24 00:09:45 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
username := strings.TrimSpace(body.Username)
|
|
|
|
|
|
if username == "" {
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleValidationError(c, "用户名不能为空")
|
2025-10-24 00:09:45 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if len(username) > 64 {
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleValidationError(c, "用户名长度不能超过64字符")
|
2025-10-24 00:09:45 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-26 14:48:02 +08:00
|
|
|
|
db, ok := baseController.GetDB(c)
|
|
|
|
|
|
if !ok {
|
2025-10-24 00:09:45 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-26 11:57:31 +08:00
|
|
|
|
// 注释:由于使用了AdminAuthRequired中间件,已确保是管理员用户
|
2025-10-26 09:35:07 +08:00
|
|
|
|
|
|
|
|
|
|
// 获取所有管理员相关设置
|
|
|
|
|
|
var adminSettings []models.Settings
|
|
|
|
|
|
if dbErr := db.Where("name LIKE ?", "admin_%").Find(&adminSettings).Error; dbErr != nil {
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleInternalError(c, "获取管理员设置失败", dbErr)
|
2025-10-26 09:35:07 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 转换为map便于查找
|
|
|
|
|
|
settingsMap := make(map[string]string)
|
|
|
|
|
|
for _, setting := range adminSettings {
|
|
|
|
|
|
settingsMap[setting.Name] = setting.Value
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
adminUsername, exists := settingsMap["admin_username"]
|
|
|
|
|
|
if !exists {
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleInternalError(c, "管理员用户名设置不存在", nil)
|
2025-10-26 09:35:07 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
adminPassword, exists := settingsMap["admin_password"]
|
|
|
|
|
|
if !exists {
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleInternalError(c, "管理员密码设置不存在", nil)
|
2025-10-24 00:09:45 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-10-26 09:35:07 +08:00
|
|
|
|
|
|
|
|
|
|
adminPasswordSalt, exists := settingsMap["admin_password_salt"]
|
|
|
|
|
|
if !exists {
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleInternalError(c, "管理员密码盐值设置不存在", nil)
|
2025-10-24 00:09:45 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-26 09:35:07 +08:00
|
|
|
|
// 如果用户名未变化则直接返回成功(无需校验旧密码)
|
|
|
|
|
|
if strings.EqualFold(username, adminUsername) {
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleSuccess(c, "保存成功", gin.H{
|
2025-10-24 00:09:45 +08:00
|
|
|
|
"username": username,
|
|
|
|
|
|
})
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 修改用户名需要进行当前密码校验
|
|
|
|
|
|
if strings.TrimSpace(body.OldPassword) == "" {
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleValidationError(c, "修改用户名需要提供当前密码")
|
2025-10-24 00:09:45 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-10-26 09:35:07 +08:00
|
|
|
|
|
2025-10-24 00:09:45 +08:00
|
|
|
|
// 使用盐值验证当前密码
|
2025-10-26 09:35:07 +08:00
|
|
|
|
if !utils.VerifyPasswordWithSalt(body.OldPassword, adminPasswordSalt, adminPassword) {
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleValidationError(c, "当前密码不正确")
|
2025-10-24 00:09:45 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-26 09:35:07 +08:00
|
|
|
|
// 更新管理员用户名设置
|
|
|
|
|
|
if dbErr := db.Model(&models.Settings{}).Where("name = ?", "admin_username").Update("value", username).Error; dbErr != nil {
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleInternalError(c, "更新管理员用户名失败", dbErr)
|
2025-10-24 00:09:45 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 重新签发JWT并写入Cookie
|
2025-10-26 09:35:07 +08:00
|
|
|
|
// 创建虚拟用户对象用于生成JWT令牌
|
|
|
|
|
|
adminUser := models.User{
|
2025-10-27 23:12:15 +08:00
|
|
|
|
Username: username, // 使用新的用户名
|
2025-10-26 09:35:07 +08:00
|
|
|
|
Password: adminPassword,
|
|
|
|
|
|
PasswordSalt: adminPasswordSalt,
|
|
|
|
|
|
}
|
|
|
|
|
|
token, err := generateJWTTokenForAdmin(adminUser)
|
2025-10-24 00:09:45 +08:00
|
|
|
|
if err != nil {
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleInternalError(c, "生成新令牌失败", err)
|
2025-10-24 00:09:45 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-10-26 14:48:02 +08:00
|
|
|
|
c.SetCookie("admin_session", token, utils.GetDefaultCookieMaxAge(), "/", "", false, true)
|
2025-10-24 00:09:45 +08:00
|
|
|
|
|
2025-10-26 14:48:02 +08:00
|
|
|
|
baseController.HandleSuccess(c, "保存成功", gin.H{
|
2025-10-24 00:09:45 +08:00
|
|
|
|
"username": username,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|