442 lines
13 KiB
Go
442 lines
13 KiB
Go
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)
|
||
}
|