qnc-server-old/service/user.go
2024-12-25 11:59:33 +08:00

400 lines
10 KiB
Go
Raw Permalink 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 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))
}