qnc-server-old/api/user.go
2024-12-25 11:59:33 +08:00

442 lines
13 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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("tydata_login", u.TyDataLogin)
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)
}
// 天远数据登录
func (u *User) TyDataLogin(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.RequestWxTyData(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_TYDATA, authIdentifier)
if err != nil {
log.Printf("注册用户错误:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
//成功创建用户
} else {
// 校验信息是否有该平台
hasWechatMP := false
for _, auth := range authentications {
if auth.AuthType == model.AUTHTYPE_TYDATA {
hasWechatMP = true
authentication = auth
}
}
// 没有则创建该平台校验并关联用户
if !hasWechatMP {
userid := authentication.UserID
authentication, err = userService.CreateAuthentications(userid, authIdentifier, model.AUTHTYPE_TYDATA)
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_TYDATA)
}
// 公众号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)
}