package user import ( "crypto/aes" "crypto/cipher" "encoding/base64" "encoding/json" "fmt" "github.com/gin-gonic/gin" "math/rand" "net/http" "net/url" "strconv" "time" "toutoukan/config" "toutoukan/init/databaseInit" "toutoukan/model/usermodel" "toutoukan/utill" ) 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{ // http.StatusBadRequest 对应 400 "error": fmt.Sprintf("微信登录失败: %s (错误码: %d)", wxResp.ErrCode, wxResp.ErrMsg), "code": "10027", }) return } //可选)使用session_key、encryptedData和iv解密用户信息 // 步骤2:解密手机号(复用AES解密函数) phoneData, err := decryptWxData(wxResp.SessionKey, req.EncryptedData, req.Iv) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "解密失败: " + err.Error()}) return } // 步骤3:解析为手机号结构体 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 ctx := c.Request.Context() // 获取请求上下文,用于控制数据库操作超时 var username string // 1. 查询用户是否存在 var exists bool query := "SELECT EXISTS(SELECT 1 FROM user_info WHERE uid = ? LIMIT 1)" err = databaseInit.UserDB.QueryRowContext(ctx, query, openid).Scan(&exists) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "error": "查询用户存在性失败: " + err.Error(), "code": "10029", }) return } // 2. 如果用户不存在,插入新用户 if !exists { username = generateUsername() now := time.Now() insertSQL := ` INSERT INTO user_info ( uid, gender,createdtime, updatedtime,username,telephone ) VALUES (?, ?, ?,?,?,?) ` _, err := databaseInit.UserDB.ExecContext( ctx, insertSQL, openid, // uid(使用微信 openid) 2, now, // createdtime now, // updatedtime username, phoneInfo.PhoneNumber, ) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "error": "插入新用户失败: " + err.Error(), "code": "10029", }) return } } else { queryUser := "SELECT username FROM user_info WHERE uid = ? LIMIT 1" err = databaseInit.UserDB.QueryRowContext(ctx, queryUser, openid).Scan(&username) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "error": "查询用户信息失败: " + err.Error(), "code": "10030", }) return } } token, err := utill.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()) // 生成8位随机数字(范围:10000000-99999999) randomNum := 10000000 + rand.Intn(90000000) // 8位随机数 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 // 返回去除填充后的原始数据 } // 去除PKCS#7填充(微信加密数据使用PKCS#7填充) 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] }