Files
toutoukan/controllers/user/userLogin.go
2025-09-23 18:39:01 +08:00

199 lines
5.5 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 user
import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"math/rand"
"net/http"
"net/url"
"strconv"
"time"
"toutoukan/init/config"
"toutoukan/init/databaseInit"
"toutoukan/model/usermodel"
"toutoukan/utill/jwt"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
const wxLoginURL = "https://api.weixin.qq.com/sns/jscode2session"
func UserLogin(c *gin.Context) {
fmt.Println("Request Body:", c.Request.Body)
var req usermodel.WxLoginM
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
fmt.Println("req:123")
fmt.Println(req.EncryptedData)
fmt.Println(req.Code)
fmt.Println(req.Iv)
fmt.Println("使用的appid:", config.Conf.WxID)
fmt.Println("使用的secret:", config.Conf.Wxsecret)
//----------------发送验证请求
params := url.Values{}
params.Add("appid", config.Conf.WxID)
params.Add("secret", config.Conf.Wxsecret)
params.Add("js_code", req.Code)
params.Add("grant_type", "authorization_code")
requestURL := fmt.Sprintf("%s?%s", wxLoginURL, params.Encode())
fmt.Println("微信接口请求URL:", requestURL)
resp, err := http.Get(fmt.Sprintf("%s?%s", wxLoginURL, params.Encode()))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "调用微信登录接口失败: " + err.Error()})
return
}
fmt.Println("微信登录返回结果:", resp.Body)
defer resp.Body.Close()
var wxResp usermodel.WxLoginResponse
if err := json.NewDecoder(resp.Body).Decode(&wxResp); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "解析微信登录响应失败: " + err.Error(), "code": "10026"})
return
}
if wxResp.ErrCode != 0 {
c.JSON(http.StatusBadRequest, gin.H{
"error": fmt.Sprintf("微信登录失败: %s (错误码: %d)", wxResp.ErrCode, wxResp.ErrMsg),
"code": "10027",
})
return
}
phoneData, err := decryptWxData(wxResp.SessionKey, req.EncryptedData, req.Iv)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "解密失败: " + err.Error()})
return
}
var phoneInfo usermodel.WxPhoneInfo
if err := json.Unmarshal(phoneData, &phoneInfo); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "解析手机号失败"})
return
}
if phoneInfo.Watermark.AppID != config.Conf.WxID {
c.JSON(http.StatusForbidden, gin.H{"error": "数据水印验证失败"})
return
}
fmt.Println("用户手机号为:", phoneInfo.PhoneNumber)
openid := wxResp.OpenID
// 业务逻辑实现
var user UserInfo
var username string
// 查找用户是否存在
result := databaseInit.UserDB.Where("uid = ?", openid).First(&user)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
// 用户不存在,创建新用户
username := generateUsername()
now := time.Now()
newUser := UserInfo{
Uid: openid,
Gender: 2, // 这里假设 gender 传 2可根据实际需求修改
CreatedTime: now,
UpdatedTime: now,
Username: username,
Telephone: phoneInfo.PhoneNumber,
// 若还有 password、avatar_url、birthdate、bio 等字段需要赋值,可在此补充
// 比如 Password: "默认密码"实际中密码应加密存储AvatarUrl: "默认头像地址" 等
}
if err := databaseInit.UserDB.Create(&newUser).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": "插入新用户失败: " + err.Error(),
"code": "10029",
})
return
}
username = newUser.Username
} else {
// 数据库查询错误
c.JSON(http.StatusInternalServerError, gin.H{
"error": "查询用户存在性失败: " + result.Error.Error(),
"code": "10029",
})
return
}
} else {
// 用户已存在,获取用户名
username = user.Username
}
token, err := jwt.GenerateJWTAndStore(openid)
if err != nil {
c.JSON(500, gin.H{"error": "生成令牌失败", "code": "10036"})
return
}
c.JSON(http.StatusOK, gin.H{"result": "success", "error": nil, "code": "20001", "token": token,
"userinfo": map[string]string{
"username": username,
"uid": openid,
"telephone": phoneInfo.PhoneNumber,
},
})
}
func generateUsername() string {
rand.Seed(time.Now().UnixNano())
randomNum := 10000000 + rand.Intn(90000000)
return "用户" + strconv.Itoa(randomNum)
}
func decryptWxData(sessionKey, encryptedData, iv string) ([]byte, error) {
key, _ := base64.StdEncoding.DecodeString(sessionKey)
data, _ := base64.StdEncoding.DecodeString(encryptedData)
ivBytes, _ := base64.StdEncoding.DecodeString(iv)
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
mode := cipher.NewCBCDecrypter(block, ivBytes)
mode.CryptBlocks(data, data)
return pkcs7Unpad(data), nil
}
func pkcs7Unpad(data []byte) []byte {
if len(data) == 0 {
return nil
}
padding := int(data[len(data)-1])
if padding > len(data) {
return nil
}
return data[:len(data)-padding]
}
// 定义与表结构对应的用户模型
type UserInfo struct {
Uid string `gorm:"column:uid;primaryKey"`
Telephone string `gorm:"column:telephone"`
Password string `gorm:"column:password"`
AvatarUrl string `gorm:"column:avatar_url"`
Gender int `gorm:"column:gender"`
Birthdate time.Time `gorm:"column:birthdate;type:datetime"`
CreatedTime time.Time `gorm:"column:createdtime;type:datetime"`
UpdatedTime time.Time `gorm:"column:updatedtime;type:datetime"`
Bio string `gorm:"column:bio"`
Username string `gorm:"column:username"`
}
// 自定义表名
func (UserInfo) TableName() string {
return "user_info"
}