390 lines
11 KiB
Go
390 lines
11 KiB
Go
|
|
package user
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"context"
|
|||
|
|
"errors"
|
|||
|
|
"fmt"
|
|||
|
|
"time"
|
|||
|
|
|
|||
|
|
"github.com/gogf/gf/v2/frame/g"
|
|||
|
|
"github.com/gogf/gf/v2/os/gtime"
|
|||
|
|
"nl-video-api/internal/dao"
|
|||
|
|
"nl-video-api/internal/model/entity"
|
|||
|
|
"nl-video-api/utility/crypto"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// UserService 用户服务
|
|||
|
|
type UserService struct{}
|
|||
|
|
|
|||
|
|
var User = &UserService{}
|
|||
|
|
|
|||
|
|
// UserCreateReq 创建用户请求
|
|||
|
|
type UserCreateReq struct {
|
|||
|
|
Username string `json:"username" v:"required|length:3,20#用户名不能为空|用户名长度为3-20位"`
|
|||
|
|
Phone string `json:"phone" v:"required|phone#手机号不能为空|手机号格式错误"`
|
|||
|
|
Email string `json:"email" v:"email#邮箱格式错误"`
|
|||
|
|
Password string `json:"password" v:"required|length:6,20#密码不能为空|密码长度为6-20位"`
|
|||
|
|
Nickname string `json:"nickname" v:"length:1,20#昵称长度为1-20位"`
|
|||
|
|
Avatar string `json:"avatar"`
|
|||
|
|
Gender int `json:"gender" v:"in:0,1,2#性别值错误"`
|
|||
|
|
Birthday string `json:"birthday"`
|
|||
|
|
VipLevel int `json:"vip_level" v:"min:1,max:10#VIP等级范围1-10"`
|
|||
|
|
Status int `json:"status" v:"in:0,1#状态只能为0或1"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// UserUpdateReq 更新用户请求
|
|||
|
|
type UserUpdateReq struct {
|
|||
|
|
Id int `json:"id" v:"required|min:1#用户ID不能为空"`
|
|||
|
|
Username string `json:"username" v:"length:3,20#用户名长度为3-20位"`
|
|||
|
|
Phone string `json:"phone" v:"phone#手机号格式错误"`
|
|||
|
|
Email string `json:"email" v:"email#邮箱格式错误"`
|
|||
|
|
Nickname string `json:"nickname" v:"length:1,20#昵称长度为1-20位"`
|
|||
|
|
Avatar string `json:"avatar"`
|
|||
|
|
Gender int `json:"gender" v:"in:0,1,2#性别值错误"`
|
|||
|
|
Birthday string `json:"birthday"`
|
|||
|
|
VipLevel int `json:"vip_level" v:"min:1,max:10#VIP等级范围1-10"`
|
|||
|
|
Status int `json:"status" v:"in:0,1#状态只能为0或1"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// UserPasswordReq 修改密码请求
|
|||
|
|
type UserPasswordReq struct {
|
|||
|
|
Id int `json:"id" v:"required|min:1#用户ID不能为空"`
|
|||
|
|
OldPassword string `json:"old_password" v:"required#原密码不能为空"`
|
|||
|
|
NewPassword string `json:"new_password" v:"required|length:6,20#新密码不能为空|新密码长度为6-20位"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// VipUpgradeReq VIP升级请求
|
|||
|
|
type VipUpgradeReq struct {
|
|||
|
|
UserId int `json:"user_id" v:"required|min:1#用户ID不能为空"`
|
|||
|
|
VipLevel int `json:"vip_level" v:"required|min:1,max:10#VIP等级不能为空|VIP等级范围1-10"`
|
|||
|
|
Days int `json:"days" v:"required|min:1#天数不能为空"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Create 创建用户
|
|||
|
|
func (s *UserService) Create(ctx context.Context, req *UserCreateReq) (int64, error) {
|
|||
|
|
// 检查用户名是否存在
|
|||
|
|
existUser, err := dao.User.GetByUsername(ctx, req.Username)
|
|||
|
|
if err != nil {
|
|||
|
|
return 0, fmt.Errorf("检查用户名失败: %v", err)
|
|||
|
|
}
|
|||
|
|
if existUser != nil {
|
|||
|
|
return 0, errors.New("用户名已存在")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查手机号是否存在
|
|||
|
|
existPhone, err := dao.User.GetByPhone(ctx, req.Phone)
|
|||
|
|
if err != nil {
|
|||
|
|
return 0, fmt.Errorf("检查手机号失败: %v", err)
|
|||
|
|
}
|
|||
|
|
if existPhone != nil {
|
|||
|
|
return 0, errors.New("手机号已存在")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查邮箱是否存在
|
|||
|
|
if req.Email != "" {
|
|||
|
|
existEmail, err := dao.User.GetByEmail(ctx, req.Email)
|
|||
|
|
if err != nil {
|
|||
|
|
return 0, fmt.Errorf("检查邮箱失败: %v", err)
|
|||
|
|
}
|
|||
|
|
if existEmail != nil {
|
|||
|
|
return 0, errors.New("邮箱已存在")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 加密密码
|
|||
|
|
hashedPassword, err := crypto.HashPassword(req.Password)
|
|||
|
|
if err != nil {
|
|||
|
|
return 0, fmt.Errorf("密码加密失败: %v", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 设置默认值
|
|||
|
|
if req.Nickname == "" {
|
|||
|
|
req.Nickname = req.Username
|
|||
|
|
}
|
|||
|
|
if req.VipLevel == 0 {
|
|||
|
|
req.VipLevel = 1 // 默认普通用户
|
|||
|
|
}
|
|||
|
|
if req.Status == 0 {
|
|||
|
|
req.Status = 1 // 默认启用
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 解析生日
|
|||
|
|
var birthday *time.Time
|
|||
|
|
if req.Birthday != "" {
|
|||
|
|
if parsedTime, err := time.Parse("2006-01-02", req.Birthday); err == nil {
|
|||
|
|
birthday = &parsedTime
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建用户
|
|||
|
|
user := &entity.NlUser{
|
|||
|
|
Username: req.Username,
|
|||
|
|
Phone: req.Phone,
|
|||
|
|
Email: req.Email,
|
|||
|
|
Password: hashedPassword,
|
|||
|
|
NickName: req.Nickname,
|
|||
|
|
Avatar: req.Avatar,
|
|||
|
|
Gender: req.Gender,
|
|||
|
|
Birthday: birthday,
|
|||
|
|
VipLevel: req.VipLevel,
|
|||
|
|
Status: req.Status,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return dao.User.Create(ctx, user)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Update 更新用户
|
|||
|
|
func (s *UserService) Update(ctx context.Context, req *UserUpdateReq) error {
|
|||
|
|
// 检查用户是否存在
|
|||
|
|
existUser, err := dao.User.GetById(ctx, req.Id)
|
|||
|
|
if err != nil {
|
|||
|
|
return fmt.Errorf("查询用户失败: %v", err)
|
|||
|
|
}
|
|||
|
|
if existUser == nil {
|
|||
|
|
return errors.New("用户不存在")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
updateData := g.Map{}
|
|||
|
|
|
|||
|
|
// 检查用户名是否重复
|
|||
|
|
if req.Username != "" && req.Username != existUser.Username {
|
|||
|
|
checkUser, err := dao.User.GetByUsername(ctx, req.Username)
|
|||
|
|
if err != nil {
|
|||
|
|
return fmt.Errorf("检查用户名失败: %v", err)
|
|||
|
|
}
|
|||
|
|
if checkUser != nil && int(checkUser.Id) != req.Id {
|
|||
|
|
return errors.New("用户名已存在")
|
|||
|
|
}
|
|||
|
|
updateData["username"] = req.Username
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查手机号是否重复
|
|||
|
|
if req.Phone != "" && req.Phone != existUser.Phone {
|
|||
|
|
checkPhone, err := dao.User.GetByPhone(ctx, req.Phone)
|
|||
|
|
if err != nil {
|
|||
|
|
return fmt.Errorf("检查手机号失败: %v", err)
|
|||
|
|
}
|
|||
|
|
if checkPhone != nil && int(checkPhone.Id) != req.Id {
|
|||
|
|
return errors.New("手机号已存在")
|
|||
|
|
}
|
|||
|
|
updateData["phone"] = req.Phone
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查邮箱是否重复
|
|||
|
|
if req.Email != "" && req.Email != existUser.Email {
|
|||
|
|
checkEmail, err := dao.User.GetByEmail(ctx, req.Email)
|
|||
|
|
if err != nil {
|
|||
|
|
return fmt.Errorf("检查邮箱失败: %v", err)
|
|||
|
|
}
|
|||
|
|
if checkEmail != nil && int(checkEmail.Id) != req.Id {
|
|||
|
|
return errors.New("邮箱已存在")
|
|||
|
|
}
|
|||
|
|
updateData["email"] = req.Email
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新其他字段
|
|||
|
|
if req.Nickname != "" {
|
|||
|
|
updateData["nick_name"] = req.Nickname
|
|||
|
|
}
|
|||
|
|
if req.Avatar != "" {
|
|||
|
|
updateData["avatar"] = req.Avatar
|
|||
|
|
}
|
|||
|
|
if req.Gender >= 0 {
|
|||
|
|
updateData["gender"] = req.Gender
|
|||
|
|
}
|
|||
|
|
if req.Birthday != "" {
|
|||
|
|
// 解析生日
|
|||
|
|
if parsedTime, err := time.Parse("2006-01-02", req.Birthday); err == nil {
|
|||
|
|
updateData["birthday"] = &parsedTime
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if req.VipLevel > 0 {
|
|||
|
|
updateData["vip_level"] = req.VipLevel
|
|||
|
|
}
|
|||
|
|
if req.Status >= 0 {
|
|||
|
|
updateData["status"] = req.Status
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if len(updateData) == 0 {
|
|||
|
|
return errors.New("没有需要更新的数据")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return dao.User.Update(ctx, req.Id, updateData)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetById 根据ID获取用户
|
|||
|
|
func (s *UserService) GetById(ctx context.Context, id int) (*entity.NlUser, error) {
|
|||
|
|
user, err := dao.User.GetById(ctx, id)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, fmt.Errorf("查询用户失败: %v", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 清除敏感信息
|
|||
|
|
if user != nil {
|
|||
|
|
user.Password = ""
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return user, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetList 获取用户列表
|
|||
|
|
func (s *UserService) GetList(ctx context.Context, req *dao.UserListReq) ([]*entity.NlUser, int, error) {
|
|||
|
|
users, total, err := dao.User.GetList(ctx, req)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, 0, fmt.Errorf("查询用户列表失败: %v", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 清除敏感信息
|
|||
|
|
for _, user := range users {
|
|||
|
|
user.Password = ""
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return users, total, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Delete 删除用户
|
|||
|
|
func (s *UserService) Delete(ctx context.Context, id int) error {
|
|||
|
|
// 检查用户是否存在
|
|||
|
|
user, err := dao.User.GetById(ctx, id)
|
|||
|
|
if err != nil {
|
|||
|
|
return fmt.Errorf("查询用户失败: %v", err)
|
|||
|
|
}
|
|||
|
|
if user == nil {
|
|||
|
|
return errors.New("用户不存在")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return dao.User.Delete(ctx, id)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ChangePassword 修改密码
|
|||
|
|
func (s *UserService) ChangePassword(ctx context.Context, req *UserPasswordReq) error {
|
|||
|
|
// 获取用户信息
|
|||
|
|
user, err := dao.User.GetById(ctx, req.Id)
|
|||
|
|
if err != nil {
|
|||
|
|
return fmt.Errorf("查询用户失败: %v", err)
|
|||
|
|
}
|
|||
|
|
if user == nil {
|
|||
|
|
return errors.New("用户不存在")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 验证原密码
|
|||
|
|
if !crypto.CheckPassword(req.OldPassword, user.Password) {
|
|||
|
|
return errors.New("原密码错误")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 加密新密码
|
|||
|
|
hashedPassword, err := crypto.HashPassword(req.NewPassword)
|
|||
|
|
if err != nil {
|
|||
|
|
return fmt.Errorf("密码加密失败: %v", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新密码
|
|||
|
|
return dao.User.Update(ctx, req.Id, g.Map{
|
|||
|
|
"password": hashedPassword,
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// BatchUpdateStatus 批量更新用户状态
|
|||
|
|
func (s *UserService) BatchUpdateStatus(ctx context.Context, ids []int, status int) error {
|
|||
|
|
if len(ids) == 0 {
|
|||
|
|
return errors.New("请选择要操作的用户")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return dao.User.BatchUpdateStatus(ctx, ids, status)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// BatchDelete 批量删除用户
|
|||
|
|
func (s *UserService) BatchDelete(ctx context.Context, ids []int) error {
|
|||
|
|
if len(ids) == 0 {
|
|||
|
|
return errors.New("请选择要删除的用户")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return dao.User.BatchDelete(ctx, ids)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetUserStats 获取用户统计信息
|
|||
|
|
func (s *UserService) GetUserStats(ctx context.Context) (g.Map, error) {
|
|||
|
|
return dao.User.GetUserStats(ctx)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// UpgradeVip 升级VIP
|
|||
|
|
func (s *UserService) UpgradeVip(ctx context.Context, req *VipUpgradeReq) error {
|
|||
|
|
// 检查用户是否存在
|
|||
|
|
user, err := dao.User.GetById(ctx, req.UserId)
|
|||
|
|
if err != nil {
|
|||
|
|
return fmt.Errorf("查询用户失败: %v", err)
|
|||
|
|
}
|
|||
|
|
if user == nil {
|
|||
|
|
return errors.New("用户不存在")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 计算VIP到期时间
|
|||
|
|
var expireAt int64
|
|||
|
|
if int64(user.VipExpireTime) > gtime.Now().Unix() {
|
|||
|
|
// 如果当前VIP未过期,在原基础上延长
|
|||
|
|
expireAt = int64(user.VipExpireTime) + int64(req.Days*24*3600)
|
|||
|
|
} else {
|
|||
|
|
// 如果已过期或首次开通,从现在开始计算
|
|||
|
|
expireAt = gtime.Now().Unix() + int64(req.Days*24*3600)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新用户VIP信息
|
|||
|
|
return dao.User.Update(ctx, req.UserId, g.Map{
|
|||
|
|
"vip_level": req.VipLevel,
|
|||
|
|
"vip_expire_time": int(expireAt),
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// SearchUsers 搜索用户
|
|||
|
|
func (s *UserService) SearchUsers(ctx context.Context, keyword string, page, pageSize int) ([]*entity.NlUser, int, error) {
|
|||
|
|
if keyword == "" {
|
|||
|
|
return nil, 0, errors.New("搜索关键词不能为空")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
users, total, err := dao.User.SearchUsers(ctx, keyword, page, pageSize)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, 0, fmt.Errorf("搜索用户失败: %v", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 清除敏感信息
|
|||
|
|
for _, user := range users {
|
|||
|
|
user.Password = ""
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return users, total, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetVipUsers 获取VIP用户列表
|
|||
|
|
func (s *UserService) GetVipUsers(ctx context.Context, page, pageSize int) ([]*entity.NlUser, int, error) {
|
|||
|
|
users, total, err := dao.User.GetVipUsers(ctx, page, pageSize)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, 0, fmt.Errorf("查询VIP用户失败: %v", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 清除敏感信息
|
|||
|
|
for _, user := range users {
|
|||
|
|
user.Password = ""
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return users, total, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetExpiredVipUsers 获取VIP即将过期的用户
|
|||
|
|
func (s *UserService) GetExpiredVipUsers(ctx context.Context, days int) ([]*entity.NlUser, error) {
|
|||
|
|
users, err := dao.User.GetExpiredVipUsers(ctx, days)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, fmt.Errorf("查询即将过期VIP用户失败: %v", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 清除敏感信息
|
|||
|
|
for _, user := range users {
|
|||
|
|
user.Password = ""
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return users, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// UpdateLoginInfo 更新登录信息
|
|||
|
|
func (s *UserService) UpdateLoginInfo(ctx context.Context, userId int, ip string) error {
|
|||
|
|
return dao.User.UpdateLoginInfo(ctx, userId, ip)
|
|||
|
|
}
|