2024-09-14 10:48:09 +08:00
|
|
|
|
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
|
|
|
|
|
}
|
2024-12-25 11:59:33 +08:00
|
|
|
|
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
|
|
|
|
|
}
|
2024-09-14 10:48:09 +08:00
|
|
|
|
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))
|
|
|
|
|
}
|