package daytask import ( "app/apis/middleware" "app/commons/core/redisclient" "app/commons/model/entity" "crypto/rand" "fmt" "github.com/gin-gonic/gin" "golang.org/x/crypto/bcrypt" "math/big" "regexp" "time" ) // generateInviteCode 生成邀请码 func generateInviteCode() string { const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" code := make([]byte, 8) for i := range code { n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(charset)))) code[i] = charset[n.Int64()] } return string(code) } // generateUid 生成用户UID func generateUid() string { return fmt.Sprintf("DT%d", time.Now().UnixNano()/1000000) } // SendSmsCode 发送短信验证码 func (s *Server) SendSmsCode(c *gin.Context) { ctx := s.FromContext(c) type Req struct { Phone string `json:"phone" binding:"required"` Type string `json:"type" binding:"required"` // register/login/reset } var req Req if err := c.ShouldBindJSON(&req); err != nil { ctx.Fail("invalid_params") return } // 验证手机号格式 phoneRegex := regexp.MustCompile(`^\d{9,15}$`) if !phoneRegex.MatchString(req.Phone) { ctx.Fail("invalid_phone") return } // 防止重复发送 cacheKey := fmt.Sprintf("sms:%s:%s", req.Type, req.Phone) if !ctx.RepeatFilter(cacheKey, 60*time.Second) { ctx.Fail("sms_send_too_fast") return } // 生成验证码 n, _ := rand.Int(rand.Reader, big.NewInt(900000)) code := fmt.Sprintf("%06d", n.Int64()+100000) // TODO: 调用短信服务发送验证码 // smsService.Send(req.Phone, code) // 存储验证码到Redis codeKey := fmt.Sprintf("smscode:%s:%s", req.Type, req.Phone) redisclient.DefaultClient().Set(c, codeKey, code, 5*time.Minute) ctx.OK(gin.H{ "message": "sms_sent", }) } // Register 用户注册 func (s *Server) Register(c *gin.Context) { ctx := s.FromContext(c) db := s.DB() type Req struct { Phone string `json:"phone" binding:"required"` Code string `json:"code" binding:"required"` Password string `json:"password" binding:"required,min=6"` InviteCode string `json:"inviteCode"` } var req Req if err := c.ShouldBindJSON(&req); err != nil { ctx.Fail("invalid_params") return } // 验证短信验证码 codeKey := fmt.Sprintf("smscode:register:%s", req.Phone) storedCode, err := redisclient.DefaultClient().Get(c, codeKey).Result() if err != nil || storedCode != req.Code { ctx.Fail("invalid_code") return } // 检查手机号是否已注册 var existUser entity.DtUser if err := db.Where("phone = ?", req.Phone).First(&existUser).Error; err == nil { ctx.Fail("phone_registered") return } // 查找邀请人 var parentId int64 = 0 if req.InviteCode != "" { var inviter entity.DtUser if err := db.Where("invite_code = ?", req.InviteCode).First(&inviter).Error; err == nil { parentId = inviter.Id } } // 加密密码 hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) if err != nil { ctx.Fail("register_failed") return } // 获取默认等级 var defaultLevel entity.DtUserLevel db.Where("is_default = ?", 1).First(&defaultLevel) // 创建用户 user := &entity.DtUser{ Uid: generateUid(), Phone: req.Phone, Password: string(hashedPassword), Nickname: "用户" + req.Phone[len(req.Phone)-4:], ParentId: parentId, LevelId: defaultLevel.Id, InviteCode: generateInviteCode(), Status: 1, } tx := db.Begin() if err := tx.Create(user).Error; err != nil { tx.Rollback() ctx.Fail("register_failed") return } // 更新邀请人的直推人数和团队人数 if parentId > 0 { tx.Model(&entity.DtUser{}).Where("id = ?", parentId). Updates(map[string]interface{}{ "direct_invite_count": db.Raw("direct_invite_count + 1"), "team_count": db.Raw("team_count + 1"), }) // 更新上级的团队人数(多级) var parent entity.DtUser if err := tx.Where("id = ?", parentId).First(&parent).Error; err == nil && parent.ParentId > 0 { tx.Model(&entity.DtUser{}).Where("id = ?", parent.ParentId). Update("team_count", db.Raw("team_count + 1")) } } tx.Commit() // 删除验证码 redisclient.DefaultClient().Del(c, codeKey) // 生成Token token, err := middleware.GenerateJWT(middleware.Member{ID: user.Id, Uid: user.Uid}) if err != nil { ctx.Fail("register_failed") return } ctx.OK(gin.H{ "token": token, "user": gin.H{ "id": user.Id, "uid": user.Uid, "nickname": user.Nickname, "avatar": user.Avatar, "phone": user.Phone, }, }) } // LoginByPassword 密码登录 func (s *Server) LoginByPassword(c *gin.Context) { ctx := s.FromContext(c) db := s.DB() type Req struct { Phone string `json:"phone" binding:"required"` Password string `json:"password" binding:"required"` } var req Req if err := c.ShouldBindJSON(&req); err != nil { ctx.Fail("invalid_params") return } // 查找用户 var user entity.DtUser if err := db.Where("phone = ?", req.Phone).First(&user).Error; err != nil { ctx.Fail("user_not_found") return } // 检查用户状态 if user.Status != 1 { ctx.Fail("user_disabled") return } // 验证密码 if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password)); err != nil { ctx.Fail("invalid_password") return } // 更新登录时间 db.Model(&entity.DtUser{}).Where("id = ?", user.Id). Update("last_login_at", time.Now().Unix()) // 生成Token token, err := middleware.GenerateJWT(middleware.Member{ID: user.Id, Uid: user.Uid}) if err != nil { ctx.Fail("login_failed") return } ctx.OK(gin.H{ "token": token, "user": gin.H{ "id": user.Id, "uid": user.Uid, "nickname": user.Nickname, "avatar": user.Avatar, "phone": user.Phone, }, }) } // LoginBySms 短信验证码登录 func (s *Server) LoginBySms(c *gin.Context) { ctx := s.FromContext(c) db := s.DB() type Req struct { Phone string `json:"phone" binding:"required"` Code string `json:"code" binding:"required"` } var req Req if err := c.ShouldBindJSON(&req); err != nil { ctx.Fail("invalid_params") return } // 验证短信验证码 codeKey := fmt.Sprintf("smscode:login:%s", req.Phone) storedCode, err := redisclient.DefaultClient().Get(c, codeKey).Result() if err != nil || storedCode != req.Code { ctx.Fail("invalid_code") return } // 查找用户 var user entity.DtUser if err := db.Where("phone = ?", req.Phone).First(&user).Error; err != nil { ctx.Fail("user_not_found") return } // 检查用户状态 if user.Status != 1 { ctx.Fail("user_disabled") return } // 更新登录时间 db.Model(&entity.DtUser{}).Where("id = ?", user.Id). Update("last_login_at", time.Now().Unix()) // 删除验证码 redisclient.DefaultClient().Del(c, codeKey) // 生成Token token, err := middleware.GenerateJWT(middleware.Member{ID: user.Id, Uid: user.Uid}) if err != nil { ctx.Fail("login_failed") return } ctx.OK(gin.H{ "token": token, "user": gin.H{ "id": user.Id, "uid": user.Uid, "nickname": user.Nickname, "avatar": user.Avatar, "phone": user.Phone, }, }) } // ResetPassword 重置密码 func (s *Server) ResetPassword(c *gin.Context) { ctx := s.FromContext(c) db := s.DB() type Req struct { Phone string `json:"phone" binding:"required"` Code string `json:"code" binding:"required"` NewPassword string `json:"newPassword" binding:"required,min=6"` } var req Req if err := c.ShouldBindJSON(&req); err != nil { ctx.Fail("invalid_params") return } // 验证短信验证码 codeKey := fmt.Sprintf("smscode:reset:%s", req.Phone) storedCode, err := redisclient.DefaultClient().Get(c, codeKey).Result() if err != nil || storedCode != req.Code { ctx.Fail("invalid_code") return } // 查找用户 var user entity.DtUser if err := db.Where("phone = ?", req.Phone).First(&user).Error; err != nil { ctx.Fail("user_not_found") return } // 加密新密码 hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.NewPassword), bcrypt.DefaultCost) if err != nil { ctx.Fail("reset_failed") return } // 更新密码 db.Model(&entity.DtUser{}).Where("id = ?", user.Id). Update("password", string(hashedPassword)) // 删除验证码 redisclient.DefaultClient().Del(c, codeKey) ctx.OK(gin.H{ "message": "password_reset_success", }) } // OAuthLogin OAuth登录(Google/Zalo等) func (s *Server) OAuthLogin(c *gin.Context) { ctx := s.FromContext(c) db := s.DB() type Req struct { Provider string `json:"provider" binding:"required"` // google/zalo/telegram OpenId string `json:"openId" binding:"required"` Nickname string `json:"nickname"` Avatar string `json:"avatar"` InviteCode string `json:"inviteCode"` } var req Req if err := c.ShouldBindJSON(&req); err != nil { ctx.Fail("invalid_params") return } // 查找是否已绑定 var social entity.DtUserSocial if err := db.Where("platform = ? AND open_id = ?", req.Provider, req.OpenId).First(&social).Error; err == nil { // 已绑定,直接登录 var user entity.DtUser if err := db.Where("id = ?", social.UserId).First(&user).Error; err != nil { ctx.Fail("user_not_found") return } if user.Status != 1 { ctx.Fail("user_disabled") return } // 更新登录时间 db.Model(&entity.DtUser{}).Where("id = ?", user.Id). Update("last_login_at", time.Now().Unix()) token, err := middleware.GenerateJWT(middleware.Member{ID: user.Id, Uid: user.Uid}) if err != nil { ctx.Fail("login_failed") return } ctx.OK(gin.H{ "token": token, "user": gin.H{ "id": user.Id, "uid": user.Uid, "nickname": user.Nickname, "avatar": user.Avatar, "phone": user.Phone, }, }) return } // 未绑定,创建新用户 var parentId int64 = 0 if req.InviteCode != "" { var inviter entity.DtUser if err := db.Where("invite_code = ?", req.InviteCode).First(&inviter).Error; err == nil { parentId = inviter.Id } } // 获取默认等级 var defaultLevel entity.DtUserLevel db.Where("is_default = ?", 1).First(&defaultLevel) nickname := req.Nickname if nickname == "" { nickname = req.Provider + "用户" } user := &entity.DtUser{ Uid: generateUid(), Nickname: nickname, Avatar: req.Avatar, ParentId: parentId, LevelId: defaultLevel.Id, InviteCode: generateInviteCode(), Status: 1, } tx := db.Begin() if err := tx.Create(user).Error; err != nil { tx.Rollback() ctx.Fail("login_failed") return } // 创建社交账号绑定 social = entity.DtUserSocial{ UserId: user.Id, Platform: req.Provider, Account: req.OpenId, Nickname: req.Nickname, Avatar: req.Avatar, } if err := tx.Create(&social).Error; err != nil { tx.Rollback() ctx.Fail("login_failed") return } // 更新邀请人统计 if parentId > 0 { tx.Model(&entity.DtUser{}).Where("id = ?", parentId). Updates(map[string]interface{}{ "direct_invite_count": db.Raw("direct_invite_count + 1"), "team_count": db.Raw("team_count + 1"), }) } tx.Commit() token, err := middleware.GenerateJWT(middleware.Member{ID: user.Id, Uid: user.Uid}) if err != nil { ctx.Fail("login_failed") return } ctx.OK(gin.H{ "token": token, "user": gin.H{ "id": user.Id, "uid": user.Uid, "nickname": user.Nickname, "avatar": user.Avatar, "phone": user.Phone, }, "isNew": true, }) }