2025-11-28 23:30:49 +08:00
|
|
|
|
package service
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"carrotskin/internal/model"
|
|
|
|
|
|
"carrotskin/internal/repository"
|
|
|
|
|
|
"carrotskin/pkg/auth"
|
|
|
|
|
|
"errors"
|
|
|
|
|
|
"strings"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// RegisterUser 用户注册
|
|
|
|
|
|
func RegisterUser(jwtService *auth.JWTService, username, password, email, avatar string) (*model.User, string, error) {
|
|
|
|
|
|
// 检查用户名是否已存在
|
|
|
|
|
|
existingUser, err := repository.FindUserByUsername(username)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, "", err
|
|
|
|
|
|
}
|
|
|
|
|
|
if existingUser != nil {
|
|
|
|
|
|
return nil, "", errors.New("用户名已存在")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查邮箱是否已存在
|
|
|
|
|
|
existingEmail, err := repository.FindUserByEmail(email)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, "", err
|
|
|
|
|
|
}
|
|
|
|
|
|
if existingEmail != nil {
|
|
|
|
|
|
return nil, "", errors.New("邮箱已被注册")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加密密码
|
|
|
|
|
|
hashedPassword, err := auth.HashPassword(password)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, "", errors.New("密码加密失败")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 确定头像URL:优先使用用户提供的头像,否则使用默认头像
|
|
|
|
|
|
avatarURL := avatar
|
|
|
|
|
|
if avatarURL == "" {
|
|
|
|
|
|
avatarURL = getDefaultAvatar()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建用户
|
|
|
|
|
|
user := &model.User{
|
|
|
|
|
|
Username: username,
|
|
|
|
|
|
Password: hashedPassword,
|
|
|
|
|
|
Email: email,
|
|
|
|
|
|
Avatar: avatarURL,
|
|
|
|
|
|
Role: "user",
|
|
|
|
|
|
Status: 1,
|
|
|
|
|
|
Points: 0, // 初始积分可以从配置读取
|
2025-11-30 18:56:56 +08:00
|
|
|
|
// Properties 字段使用 datatypes.JSON,默认为 nil,数据库会存储 NULL
|
2025-11-28 23:30:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err := repository.CreateUser(user); err != nil {
|
|
|
|
|
|
return nil, "", err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 生成JWT Token
|
|
|
|
|
|
token, err := jwtService.GenerateToken(user.ID, user.Username, user.Role)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, "", errors.New("生成Token失败")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: 添加注册奖励积分
|
|
|
|
|
|
|
|
|
|
|
|
return user, token, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// LoginUser 用户登录(支持用户名或邮箱登录)
|
|
|
|
|
|
func LoginUser(jwtService *auth.JWTService, usernameOrEmail, password, ipAddress, userAgent string) (*model.User, string, error) {
|
|
|
|
|
|
// 查找用户:判断是用户名还是邮箱
|
|
|
|
|
|
var user *model.User
|
|
|
|
|
|
var err error
|
|
|
|
|
|
|
|
|
|
|
|
if strings.Contains(usernameOrEmail, "@") {
|
|
|
|
|
|
// 包含@符号,认为是邮箱
|
|
|
|
|
|
user, err = repository.FindUserByEmail(usernameOrEmail)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 否则认为是用户名
|
|
|
|
|
|
user, err = repository.FindUserByUsername(usernameOrEmail)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, "", err
|
|
|
|
|
|
}
|
|
|
|
|
|
if user == nil {
|
|
|
|
|
|
// 记录失败日志
|
|
|
|
|
|
logFailedLogin(0, ipAddress, userAgent, "用户不存在")
|
|
|
|
|
|
return nil, "", errors.New("用户名/邮箱或密码错误")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查用户状态
|
|
|
|
|
|
if user.Status != 1 {
|
|
|
|
|
|
logFailedLogin(user.ID, ipAddress, userAgent, "账号已被禁用")
|
|
|
|
|
|
return nil, "", errors.New("账号已被禁用")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证密码
|
|
|
|
|
|
if !auth.CheckPassword(user.Password, password) {
|
|
|
|
|
|
logFailedLogin(user.ID, ipAddress, userAgent, "密码错误")
|
|
|
|
|
|
return nil, "", errors.New("用户名/邮箱或密码错误")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 生成JWT Token
|
|
|
|
|
|
token, err := jwtService.GenerateToken(user.ID, user.Username, user.Role)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, "", errors.New("生成Token失败")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新最后登录时间
|
|
|
|
|
|
now := time.Now()
|
|
|
|
|
|
user.LastLoginAt = &now
|
|
|
|
|
|
_ = repository.UpdateUserFields(user.ID, map[string]interface{}{
|
|
|
|
|
|
"last_login_at": now,
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 记录成功登录日志
|
|
|
|
|
|
logSuccessLogin(user.ID, ipAddress, userAgent)
|
|
|
|
|
|
|
|
|
|
|
|
return user, token, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetUserByID 根据ID获取用户
|
|
|
|
|
|
func GetUserByID(id int64) (*model.User, error) {
|
|
|
|
|
|
return repository.FindUserByID(id)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// UpdateUserInfo 更新用户信息
|
|
|
|
|
|
func UpdateUserInfo(user *model.User) error {
|
|
|
|
|
|
return repository.UpdateUser(user)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// UpdateUserAvatar 更新用户头像
|
|
|
|
|
|
func UpdateUserAvatar(userID int64, avatarURL string) error {
|
|
|
|
|
|
return repository.UpdateUserFields(userID, map[string]interface{}{
|
|
|
|
|
|
"avatar": avatarURL,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ChangeUserPassword 修改密码
|
|
|
|
|
|
func ChangeUserPassword(userID int64, oldPassword, newPassword string) error {
|
|
|
|
|
|
// 获取用户
|
|
|
|
|
|
user, err := repository.FindUserByID(userID)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return errors.New("用户不存在")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证旧密码
|
|
|
|
|
|
if !auth.CheckPassword(user.Password, oldPassword) {
|
|
|
|
|
|
return errors.New("原密码错误")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加密新密码
|
|
|
|
|
|
hashedPassword, err := auth.HashPassword(newPassword)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return errors.New("密码加密失败")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新密码
|
|
|
|
|
|
return repository.UpdateUserFields(userID, map[string]interface{}{
|
|
|
|
|
|
"password": hashedPassword,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ResetUserPassword 重置密码(通过邮箱)
|
|
|
|
|
|
func ResetUserPassword(email, newPassword string) error {
|
|
|
|
|
|
// 查找用户
|
|
|
|
|
|
user, err := repository.FindUserByEmail(email)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return errors.New("用户不存在")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加密新密码
|
|
|
|
|
|
hashedPassword, err := auth.HashPassword(newPassword)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return errors.New("密码加密失败")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新密码
|
|
|
|
|
|
return repository.UpdateUserFields(user.ID, map[string]interface{}{
|
|
|
|
|
|
"password": hashedPassword,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ChangeUserEmail 更换邮箱
|
|
|
|
|
|
func ChangeUserEmail(userID int64, newEmail string) error {
|
|
|
|
|
|
// 检查新邮箱是否已被使用
|
|
|
|
|
|
existingUser, err := repository.FindUserByEmail(newEmail)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
if existingUser != nil && existingUser.ID != userID {
|
|
|
|
|
|
return errors.New("邮箱已被其他用户使用")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新邮箱
|
|
|
|
|
|
return repository.UpdateUserFields(userID, map[string]interface{}{
|
|
|
|
|
|
"email": newEmail,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// logSuccessLogin 记录成功登录
|
|
|
|
|
|
func logSuccessLogin(userID int64, ipAddress, userAgent string) {
|
|
|
|
|
|
log := &model.UserLoginLog{
|
|
|
|
|
|
UserID: userID,
|
|
|
|
|
|
IPAddress: ipAddress,
|
|
|
|
|
|
UserAgent: userAgent,
|
|
|
|
|
|
LoginMethod: "PASSWORD",
|
|
|
|
|
|
IsSuccess: true,
|
|
|
|
|
|
}
|
|
|
|
|
|
_ = repository.CreateLoginLog(log)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// logFailedLogin 记录失败登录
|
|
|
|
|
|
func logFailedLogin(userID int64, ipAddress, userAgent, reason string) {
|
|
|
|
|
|
log := &model.UserLoginLog{
|
|
|
|
|
|
UserID: userID,
|
|
|
|
|
|
IPAddress: ipAddress,
|
|
|
|
|
|
UserAgent: userAgent,
|
|
|
|
|
|
LoginMethod: "PASSWORD",
|
|
|
|
|
|
IsSuccess: false,
|
|
|
|
|
|
FailureReason: reason,
|
|
|
|
|
|
}
|
|
|
|
|
|
_ = repository.CreateLoginLog(log)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// getDefaultAvatar 获取默认头像URL
|
|
|
|
|
|
func getDefaultAvatar() string {
|
|
|
|
|
|
// 如果数据库中不存在默认头像配置,返回错误信息
|
|
|
|
|
|
const log = "数据库中不存在默认头像配置"
|
|
|
|
|
|
|
|
|
|
|
|
// 尝试从数据库读取配置
|
|
|
|
|
|
config, err := repository.GetSystemConfigByKey("default_avatar")
|
|
|
|
|
|
if err != nil || config == nil {
|
|
|
|
|
|
return log
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return config.Value
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func GetUserByEmail(email string) (*model.User, error) {
|
|
|
|
|
|
user, err := repository.FindUserByEmail(email)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, errors.New("邮箱查找失败")
|
|
|
|
|
|
}
|
|
|
|
|
|
return user, nil
|
|
|
|
|
|
}
|