package service import ( "context" "crypto/sha1" "encoding/hex" "encoding/json" "errors" "fmt" "github.com/redis/go-redis/v9" "io" "log" "net/http" "net/url" "qnc-server/config" "qnc-server/db" "qnc-server/model/model" "qnc-server/model/response" "sort" "sync" "time" ) type UserService struct { } // 全能查小程序Code获取openid Unionid func (UserService *UserService) RequestWx(code string) (wxResponse response.WxLoginResp, err error) { // 向微信发出登录请求 baseURL := config.ConfigData.System.WxLoginUrl // 创建查询参数 params := url.Values{} params.Add("appid", config.ConfigData.System.WxAppId) params.Add("secret", config.ConfigData.System.WxSecret) params.Add("js_code", code) params.Add("grant_type", "authorization_code") // 构建完整的请求 URL requestURL := fmt.Sprintf("%s?%s", baseURL, params.Encode()) // 发送 GET 请求 resp, err := http.Get(requestURL) if err != nil { return } defer resp.Body.Close() // 读取响应体 body, err := io.ReadAll(resp.Body) if err != nil { return } // 将响应体解析为结构体 err = json.Unmarshal(body, &wxResponse) if err != nil { return } if wxResponse.ErrCode != 0 { err = errors.New(wxResponse.ErrMsg) return } return } func (UserService *UserService) RequestWxTyData(code string) (wxResponse response.WxLoginResp, err error) { // 向微信发出登录请求 baseURL := config.ConfigData.System.WxTyDataLoginUrl // 创建查询参数 params := url.Values{} params.Add("appid", config.ConfigData.System.WxTyDataAppId) params.Add("secret", config.ConfigData.System.WxTyDataSecret) params.Add("js_code", code) params.Add("grant_type", "authorization_code") // 构建完整的请求 URL requestURL := fmt.Sprintf("%s?%s", baseURL, params.Encode()) // 发送 GET 请求 resp, err := http.Get(requestURL) if err != nil { return } defer resp.Body.Close() // 读取响应体 body, err := io.ReadAll(resp.Body) if err != nil { return } // 将响应体解析为结构体 err = json.Unmarshal(body, &wxResponse) if err != nil { return } if wxResponse.ErrCode != 0 { err = errors.New(wxResponse.ErrMsg) return } return } // Login 全能查 公众号 获取access_token func (UserService *UserService) RequestWxH5(code string) (wxH5Response response.WxH5LoginResp, err error) { // 向微信发出登录请求 baseURL := config.ConfigData.System.WxH5LoginUrl // 创建查询参数 params := url.Values{} params.Add("appid", config.ConfigData.System.WxH5AppId) params.Add("secret", config.ConfigData.System.WxH5Secret) params.Add("code", code) params.Add("grant_type", "authorization_code") // 构建完整的请求 URL requestURL := fmt.Sprintf("%s?%s", baseURL, params.Encode()) // 发送 GET 请求 resp, err := http.Get(requestURL) if err != nil { return } defer resp.Body.Close() // 读取响应体 body, err := io.ReadAll(resp.Body) if err != nil { return } err = json.Unmarshal(body, &wxH5Response) if err != nil { return } log.Printf("H5-以Code获取access_token响应:%v", wxH5Response) if wxH5Response.Errcode != 0 { err = errors.New(wxH5Response.Errmsg) return } return } // 刷新token // access_token 2小时过期 // refresh_token 30天过期 //func (UserService *UserService) RefreshToken(openid string) (err error) { // //} // snsapi_userinfo授权拉取用户信息 func (UserService *UserService) GetSnsUserInfo(accessToken string, openid string) (userinfoResp response.WeChatUserInfoResp, err error) { //https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN // 向微信发出登录请求 baseURL := config.ConfigData.System.WxH5UserinfoUrl // 创建查询参数 params := url.Values{} params.Add("access_token", accessToken) params.Add("openid", openid) params.Add("lang", "zh_CN") // 构建完整的请求 URL requestURL := fmt.Sprintf("%s?%s", baseURL, params.Encode()) log.Printf("请求url: %s", requestURL) // 发送 GET 请求 resp, err := http.Get(requestURL) if err != nil { return } defer resp.Body.Close() // 读取响应体 body, err := io.ReadAll(resp.Body) if err != nil { return } err = json.Unmarshal(body, &userinfoResp) if err != nil { return } log.Printf("H5-snsapi_userinfo授权拉取用户信息响应:%v", userinfoResp) if userinfoResp.Errcode != 0 { err = errors.New(userinfoResp.Errmsg) return } return } // 刷新token // access_token 2小时过期 // refresh_token 30天过期 //func (UserService *UserService) RefreshToken(openid string) (err error) { // //} // 注册用户,创建 func (UserService *UserService) Register(user model.User, authType model.AuthType, authIdentifier model.AuthIdentifier) (auth model.Authentication, err error) { err = db.DB.Create(&user).Error if err != nil { return } auth = model.Authentication{ UserID: user.ID, AuthType: authType, OpenID: authIdentifier.OpenID, UnionID: authIdentifier.UnionID, Phone: authIdentifier.Phone, } err = db.DB.Create(&auth).Error if err != nil { return } return } // MatchingAuthentications配对是否有相交的校验 func (UserService *UserService) MatchingAuthentications(authIdentifier model.AuthIdentifier) (authentications []model.Authentication, err error) { query := db.DB if authIdentifier.OpenID != "" { query = query.Or("open_id = ?", authIdentifier.OpenID) } if authIdentifier.UnionID != "" { query = query.Or("union_id = ?", authIdentifier.UnionID) } if authIdentifier.Phone != "" { query = query.Or("phone = ?", authIdentifier.Phone) } err = query.Find(&authentications).Error return } // CreateAuthentications 创建校验 func (UserService *UserService) CreateAuthentications(userid uint, authIdentifier model.AuthIdentifier, AuthType model.AuthType) (authentications model.Authentication, err error) { authentications = model.Authentication{ UserID: userid, AuthType: AuthType, OpenID: authIdentifier.OpenID, UnionID: authIdentifier.UnionID, Phone: authIdentifier.Phone, } err = db.DB.Create(&authentications).Error if err != nil { return } return } // GetUser 根据openid获取全能查用户记录 func (UserService *UserService) GetUser(id uint) (user *model.User, err error) { err = db.DB.Where("id = ?", id).First(&user).Error return } // GetUser 根据h5_openid获取全能查用户记录 func (UserService *UserService) GetUserByH5(h5Openid string) (user *model.User, err error) { err = db.DB.Where("h5_openid = ?", h5Openid).First(&user).Error return } // GetUser 根据unionid获取全能查用户记录 func (UserService *UserService) GetUserByUnionid(unionid string) (user *model.User, err error) { err = db.DB.Where("unionid = ?", unionid).First(&user).Error return } // GetUser 根据userid获取全能查用户记录 func (UserService *UserService) GetUserByUserid(userid uint) (user *model.User, err error) { err = db.DB.Where("userid = ?", userid).First(&user).Error return } // CreateUser 创建全能查用户 func (UserService *UserService) CreateUser(user *model.User) (err error) { err = db.DB.Create(&user).Error return err } // update 按unionid更新用户信息 func (UserService *UserService) UpdateUser(user *model.User) (err error) { err = db.DB.Save(user).Error return err } // h5 SDK 获取config部分 var ( mutex sync.Mutex ) var ctx = context.Background() // GetJsapiTicket 获取JSAPI Ticket func (UserService *UserService) GetJsapiTicket() (string, error) { jsapiTicket, err := db.RedisClient.Get(ctx, "JsapiTicket").Result() if err != nil && !errors.Is(err, redis.Nil) { return "", err } if jsapiTicket != "" { return jsapiTicket, nil } mutex.Lock() defer mutex.Unlock() accessToken, err := UserService.getAccessToken() if err != nil { return "", err } ticket, err := UserService.fetchJsapiTicket(accessToken) if err != nil { return "", err } log.Printf("获取到新的JsapiTicket:%s", ticket) jsapiTicket = ticket err = db.RedisClient.Set(ctx, "JsapiTicket", jsapiTicket, 7200*time.Second).Err() if err != nil { return "", err } return jsapiTicket, nil } func (UserService *UserService) getAccessToken() (string, error) { reqUrl := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", config.ConfigData.System.WxH5AppId, config.ConfigData.System.WxH5Secret) resp, err := http.Get(reqUrl) if err != nil { return "", err } defer resp.Body.Close() var result struct { AccessToken string `json:"access_token"` ExpiresIn int `json:"expires_in"` Errcode int `json:"errcode"` Errmsg string `json:"errmsg"` } err = json.NewDecoder(resp.Body).Decode(&result) if err != nil { return "", err } if result.Errcode != 0 { log.Printf("获取AccessToken错误:%s", result.Errmsg) return "", errors.New(result.Errmsg) } return result.AccessToken, nil } func (UserService *UserService) fetchJsapiTicket(accessToken string) (string, error) { reqUrl := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi", accessToken) resp, err := http.Get(reqUrl) if err != nil { return "", err } defer resp.Body.Close() var result struct { Ticket string `json:"ticket"` ExpiresIn int `json:"expires_in"` Errcode int `json:"errcode"` Errmsg string `json:"errmsg"` } err = json.NewDecoder(resp.Body).Decode(&result) if err != nil { return "", err } if result.Errcode != 0 { log.Printf("获取JsapiTicket错误:%s", result.Errmsg) return "", errors.New(result.Errmsg) } return result.Ticket, nil } // CreateSignature 创建签名 func (UserService *UserService) CreateSignature(ticket, nonceStr, timestamp, url string) string { // 将参数按 key=value 格式拼接 params := map[string]string{ "jsapi_ticket": ticket, "noncestr": nonceStr, "timestamp": timestamp, "url": url, } // 按照 key 的字典序排序 keys := make([]string, 0, len(params)) for key := range params { keys = append(keys, key) } sort.Strings(keys) // 拼接成一个字符串 str := "" for _, key := range keys { if str != "" { str += "&" } str += fmt.Sprintf("%s=%s", key, params[key]) } // 对字符串进行 SHA1 加密 h := sha1.New() h.Write([]byte(str)) return hex.EncodeToString(h.Sum(nil)) }