qnc-server-old/api/user.go

368 lines
11 KiB
Go
Raw Normal View History

2024-09-14 10:48:09 +08:00
package api
import (
"fmt"
"github.com/gin-gonic/gin"
"log"
"math/rand"
"qnc-server/config"
common "qnc-server/model/common"
model "qnc-server/model/model"
"qnc-server/model/request"
"qnc-server/model/response"
"qnc-server/service"
"qnc-server/utils"
"strconv"
"time"
)
type User struct {
}
var userService service.UserService
var verifyService service.VerifyCode
// 注册路由
func InitUser(group *gin.RouterGroup) {
var u User
userPublicGroup := group.Group("user")
userPrivateGroup := group.Group("user")
{
userPublicGroup.GET("test", u.Test)
userPublicGroup.POST("log", u.Log)
userPublicGroup.POST("login", u.Login)
userPublicGroup.POST("h5_login", u.H5Login)
userPublicGroup.POST("phone_login", u.PhoneLogin)
userPublicGroup.POST("verify", u.GetVerify)
userPublicGroup.GET("get_config", u.GetSDKConfig)
}
{
userPrivateGroup.Use(JWTAuth())
}
}
func (u *User) Test(c *gin.Context) {
authIdentifier := model.AuthIdentifier{
OpenID: "oR_hJ6kv2bmH7YWPzuZdwQvN4B5g",
UnionID: "ogzHA6W4T7vHGl-99nlHkSJDczBc",
}
authentications, err := userService.MatchingAuthentications(authIdentifier)
if err != nil {
return
}
response.OkWithData(authentications, c)
}
func (u *User) Log(c *gin.Context) {
postForm := c.Request.PostForm
log.Printf("前端请求打印:%s", postForm)
response.Ok(c)
}
func (u *User) Login(c *gin.Context) {
var reqBody request.UserLoginReq
// 尝试将请求的 JSON body 绑定到 reqBody 结构体
if err := c.ShouldBindJSON(&reqBody); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
// 检查请求体中是否包含 code 字段
if reqBody.Code == "" {
response.FailWithMessage("code is required", c)
return
}
// Code获取用户信息
wxResponse, err := userService.RequestWx(reqBody.Code)
if err != nil {
response.FailWithMessage(fmt.Errorf("request weixin err%w", err).Error(), c)
return
}
authIdentifier := model.AuthIdentifier{
OpenID: wxResponse.OpenId,
UnionID: wxResponse.Unionid,
}
// 是否有匹配的校验信息
authentications, err := userService.MatchingAuthentications(authIdentifier)
if err != nil {
log.Printf("MatchingAuthentications Error%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
var authentication model.Authentication
if len(authentications) == 0 {
authentication, err = userService.Register(model.User{}, model.AUTHTYPE_WECHAT_MP, authIdentifier)
if err != nil {
log.Printf("注册用户错误:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
//成功创建用户
} else {
// 校验信息是否有该平台
hasWechatMP := false
for _, auth := range authentications {
if auth.AuthType == model.AUTHTYPE_WECHAT_MP {
hasWechatMP = true
authentication = auth
}
}
// 没有则创建该平台校验并关联用户
if !hasWechatMP {
userid := authentication.UserID
authentication, err = userService.CreateAuthentications(userid, authIdentifier, model.AUTHTYPE_WECHAT_MP)
if err != nil {
log.Printf("创建平台校验错误:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
//成功校验该平台成功
}
}
user, err := userService.GetUser(authentication.UserID)
if err != nil {
log.Printf("authentication获取关联用户失败%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
u.TokenNext(c, user, authIdentifier, model.AUTHTYPE_WECHAT_MP)
}
// 公众号h5登录
func (u *User) H5Login(c *gin.Context) {
var reqBody request.H5UserLoginReq
// 尝试将请求的 JSON body 绑定到 reqBody 结构体
if err := c.ShouldBindJSON(&reqBody); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if reqBody.UserinfoAuth == nil {
response.FailWithDetailed(reqBody, "根本没有Auth", c)
return
}
// 检查请求体中是否包含 code 字段
if reqBody.Code == "" {
response.FailWithMessage("code is required", c)
return
}
// 获取到h5的openid token
wxH5Response, err := userService.RequestWxH5(reqBody.Code)
if err != nil {
log.Printf("request weixin err%v", err)
response.FailWithMessage(fmt.Sprintf("登录失败,请稍后再试:%v", err), c)
return
}
authIdentifier := model.AuthIdentifier{
OpenID: wxH5Response.Openid,
}
// 是否有匹配的校验信息
authentications, err := userService.MatchingAuthentications(authIdentifier)
if err != nil {
log.Printf("GetUserByH5 err%v", err)
response.FailWithMessage(fmt.Sprintf("系统错误登录失败,请稍后再试:%v", err), c)
return
}
var authentication model.Authentication
if len(authentications) == 0 {
// 是否手动授权登录
if *reqBody.UserinfoAuth {
userinfo, err := userService.GetSnsUserInfo(wxH5Response.AccessToken, wxH5Response.Openid)
if err != nil {
log.Printf("request weixin getuserinfo err%v", err)
response.FailWithMessage(fmt.Sprintf("登录失败,请稍后再试:%v", err), c)
return
}
log.Printf("UserInfo%v", userinfo)
authIdentifier.UnionID = userinfo.UnionID
log.Printf("authIdentifier%v", authIdentifier)
//查找匹配UnionID
authentications, err = userService.MatchingAuthentications(authIdentifier)
log.Printf("authentications%v", authentications)
if err != nil {
log.Printf("get user err%v", err)
response.FailWithMessage(fmt.Sprintf("登录失败,请稍后再试:%v", err), c)
return
}
if len(authentications) == 0 {
//没有则创建用户
authentication, err = userService.Register(model.User{}, model.AUTHTYPE_WECHAT_H5, authIdentifier)
if err != nil {
log.Printf("注册用户错误:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
} else {
// 有则创建平台校验
authentication, err = userService.CreateAuthentications(authentications[0].UserID, authIdentifier, model.AUTHTYPE_WECHAT_H5)
if err != nil {
log.Printf("创建平台校验错误:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
}
} else {
// 静默登录并且库中无openid让前端重新进行手动授权
response.ManualAuth(c)
return
}
} else {
for _, auth := range authentications {
if auth.AuthType == model.AUTHTYPE_WECHAT_H5 {
authentication = auth
}
}
}
user, err := userService.GetUser(authentication.UserID)
if err != nil {
log.Printf("authentication获取关联用户失败%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
//有则直接登录
u.TokenNext(c, user, authIdentifier, model.AUTHTYPE_WECHAT_H5)
}
// 手机号登录
func (u *User) PhoneLogin(c *gin.Context) {
var reqBody request.PhoneLoginReq
// 尝试将请求的 JSON body 绑定到 reqBody 结构体
if err := c.ShouldBindJSON(&reqBody); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
// 校验验证码
isRight, err := utils.VerifyCode(reqBody.PhoneNumber, reqBody.VerifyCode)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
} else if !isRight {
response.FailWithMessage("验证码错误", c)
return
}
authIdentifier := model.AuthIdentifier{
Phone: reqBody.PhoneNumber,
}
authentications, err := userService.MatchingAuthentications(authIdentifier)
var authentication model.Authentication
if len(authentications) == 0 {
authentication, err = userService.Register(model.User{Phone: reqBody.PhoneNumber}, model.AUTHTYPE_PHONE, authIdentifier)
if err != nil {
log.Printf("注册用户错误:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
//成功创建用户
} else {
authentication = authentications[0]
}
user, err := userService.GetUser(authentication.UserID)
if err != nil {
log.Printf("authentication获取关联用户失败%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
u.TokenNext(c, user, authIdentifier, model.AUTHTYPE_PHONE)
}
// TokenNext 登录以后签发jwt
func (u *User) TokenNext(c *gin.Context, user *model.User, authIdentifiers model.AuthIdentifier, platform model.AuthType) {
j := &utils.JWT{SigningKey: []byte(config.ConfigData.JWT.SigningKey)} // 唯一签名
claims := j.CreateClaims(common.BaseClaims{
Userid: user.Userid,
Disable: user.Disable != nil && *user.Disable,
AuthIdentifiers: authIdentifiers,
Platform: platform,
})
token, err := j.CreateToken(claims)
if err != nil {
response.FailWithMessage("获取token失败", c)
return
}
utils.SetToken(c, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix()))
response.OkWithDetailed(response.LoginResponse{
User: *user,
//AuthIdentifiers: authIdentifiers,
Token: token,
ExpiresAt: claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
}, "登录成功", c)
return
}
// 获取验证码
func (u *User) GetVerify(c *gin.Context) {
var req request.VerifyCodeReq
// 绑定JSON数据到结构体
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage("PhoneNumber is required", c)
return
}
// 验证手机号码格式
if !utils.IsValidPhoneNumber(req.PhoneNumber) {
response.FailWithMessage("Invalid phone number format", c)
return
}
existsCode, err := utils.CanRequestCode(req.PhoneNumber)
if err != nil {
return
}
if existsCode {
response.FailWithMessage("请求过于频繁,请稍后再试", c)
return
}
var code = utils.GenerateVerificationCode()
defaultTemplateCode := config.ConfigData.VerifyCode.TemplateCode
if req.Template == "marriage" {
defaultTemplateCode = config.ConfigData.VerifyCode.MarriageTemplateCode
}
err = verifyService.Send(code, req.PhoneNumber, defaultTemplateCode)
if err != nil {
log.Printf("【获取验证码】请求验证码发送失败:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
err = utils.StoreVerificationCode(req.PhoneNumber, code)
if err != nil {
log.Printf("【获取验证码】验证码缓存失败:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
response.OkWithMessage("获取成功", c)
}
// h5 sdk config
func (u *User) GetSDKConfig(c *gin.Context) {
pageURL := c.Query("url")
if pageURL == "" {
log.Printf("GetSDKConfig url 没传")
response.FailWithMessage("系统错误", c)
return
}
ticket, err := userService.GetJsapiTicket()
if err != nil || ticket == "" {
log.Printf("get jsapi ticket err:%v", err)
response.FailWithMessage("获取配置失败", c)
return
}
nonceStr := strconv.Itoa(rand.Int())
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
signature := userService.CreateSignature(ticket, nonceStr, timestamp, pageURL)
response.OkWithData(gin.H{
"ticket": ticket,
"appId": config.ConfigData.System.WxH5AppId,
"timestamp": timestamp,
"nonceStr": nonceStr,
"signature": signature,
}, c)
}