u
This commit is contained in:
1 parent
6300778150
commit
e893ef2e48
27 files changed
+870
-33
No files matched your search
@@ -1,3 +1,7 @@
|
||||
app:
|
||||
name: allapp
|
||||
port: 8080
|
||||
base_url: /app
|
||||
wechat:
|
||||
app_id: wxdab3e21a1f7e392f
|
||||
app_secret: 0895443c6fedfc21cb92c9cd60de2a26
|
||||
@@ -7,6 +7,7 @@ require (
|
||||
github.com/gofiber/fiber/v3 v3.1.0
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1
|
||||
github.com/jackc/pgx/v5 v5.9.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/spf13/viper v1.21.0
|
||||
go.uber.org/zap v1.27.1
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
@@ -15,6 +16,7 @@ require (
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
@@ -31,7 +33,6 @@ require (
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/philhofer/fwd v1.2.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
@@ -41,6 +42,7 @@ require (
|
||||
github.com/tinylib/msgp v1.6.3 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.69.0 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
go.uber.org/multierr v1.10.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/crypto v0.49.0 // indirect
|
||||
|
||||
@@ -24,7 +24,7 @@ func RunServer(cfg *config.Config) error {
|
||||
app.Use(middleware.Logger())
|
||||
|
||||
// ===== router =====
|
||||
router.SetupRouter(app)
|
||||
router.SetupRouter(app, cfg)
|
||||
|
||||
addr := fmt.Sprintf(":%d", cfg.App.Port)
|
||||
|
||||
|
||||
@@ -7,11 +7,13 @@ type Config struct {
|
||||
JWT JwtConfig `mapstructure:"jwt"`
|
||||
UniqueID UniqueIDConfig `mapstructure:"unique_id"`
|
||||
Postgres PostgresConfig `mapstructure:"postgres"`
|
||||
Wechat WechatConfig `mapstructure:"wechat"`
|
||||
}
|
||||
|
||||
type AppConfig struct {
|
||||
Name string `mapstructure:"name"`
|
||||
Port int `mapstructure:"port"`
|
||||
Name string `mapstructure:"name"`
|
||||
Port int `mapstructure:"port"`
|
||||
BaseUrl string `mapstructure:"base_url"`
|
||||
}
|
||||
|
||||
type PostgresConfig struct {
|
||||
@@ -36,3 +38,8 @@ type UniqueIDConfig struct {
|
||||
DataCenterID uint16 `mapstructure:"datacenter_id"`
|
||||
WorkerID uint16 `mapstructure:"worker_id"`
|
||||
}
|
||||
|
||||
type WechatConfig struct {
|
||||
AppId string `mapstructure:"app_id"`
|
||||
AppSecret string `mapstructure:"app_secret"`
|
||||
}
|
||||
@@ -52,6 +52,7 @@ func Load() (*Config, error) {
|
||||
func setDefaults(v *viper.Viper) {
|
||||
v.SetDefault("app.name", "allapp-go")
|
||||
v.SetDefault("app.port", 8080)
|
||||
v.SetDefault("app.base_url", "app")
|
||||
|
||||
// 可以继续扩展
|
||||
v.SetDefault("unique_id.datacenter_id", 1)
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
package handle
|
||||
|
||||
import (
|
||||
"allapp-go/internal/errors"
|
||||
"allapp-go/internal/httpx"
|
||||
"allapp-go/internal/types"
|
||||
"allapp-go/pkg/db"
|
||||
"allapp-go/pkg/jwtx"
|
||||
"allapp-go/pkg/uniqueid"
|
||||
"allapp-go/pkg/wechat"
|
||||
"fmt"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/gofiber/fiber/v3/log"
|
||||
)
|
||||
|
||||
// LoginQq ======================== QQ 登录 ========================
|
||||
func LoginQq(c fiber.Ctx) error {
|
||||
var req types.LoginQqDTO
|
||||
vo := new(types.LoginVO)
|
||||
|
||||
if err := httpx.BindAndValidate(c, &req); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
return handleThirdLogin(c, req.Openid, req.Nickname, req.Avatar, 1, vo)
|
||||
}
|
||||
|
||||
// LoginWechat ======================== 微信登录 ========================
|
||||
func LoginWechat(c fiber.Ctx) error {
|
||||
var req types.LoginWechatDTO
|
||||
vo := new(types.LoginVO)
|
||||
|
||||
if err := httpx.BindAndValidate(c, &req); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
openid, token, err := wechat.GetWechatAccess(req.Code)
|
||||
if err != nil {
|
||||
log.Errorw("wechat access failed", "code", req.Code, "error", err)
|
||||
return httpx.Fail(c, "微信登录失败,请重试")
|
||||
}
|
||||
|
||||
nickname, avatar, err := wechat.GetWechatUserInfo(token, openid)
|
||||
if err != nil {
|
||||
log.Errorw("wechat userinfo failed", "openid", openid, "error", err)
|
||||
return httpx.Fail(c, "微信登录失败,请重试")
|
||||
}
|
||||
|
||||
return handleThirdLogin(c, openid, nickname, avatar, 0, vo)
|
||||
}
|
||||
|
||||
// ======================== 第三方登录主流程 ========================
|
||||
func handleThirdLogin(
|
||||
c fiber.Ctx,
|
||||
openid string,
|
||||
nickname string,
|
||||
avatar string,
|
||||
loginType int16,
|
||||
vo *types.LoginVO,
|
||||
) error {
|
||||
|
||||
dbClient := db.New()
|
||||
|
||||
user, err := getUserByOpenID(dbClient, c, openid, loginType)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
// 不存在 -> 注册
|
||||
if user == nil {
|
||||
return registerUser(dbClient, c, openid, nickname, avatar, loginType, vo)
|
||||
}
|
||||
|
||||
// ========= 校验状态 =========
|
||||
id := toInt64(user["id"])
|
||||
status := toInt64(user["status"])
|
||||
|
||||
if status == 0 {
|
||||
return httpx.Fail(c, "账号已被禁用")
|
||||
}
|
||||
|
||||
// ========= 更新登录时间 =========
|
||||
if err := dbClient.Update(
|
||||
c.Context(),
|
||||
"user",
|
||||
"id",
|
||||
map[string]any{
|
||||
"id": id,
|
||||
"last_login_time": "NOW()",
|
||||
},
|
||||
); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
// ========= token =========
|
||||
token, err := jwtx.CreateToken(c.Context(), map[string]any{
|
||||
"id": id,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
// ========= 返回 =========
|
||||
vo.Token = token
|
||||
vo.UserId = id
|
||||
vo.Nickname, _ = user["nickname"].(string)
|
||||
vo.Avatar, _ = user["avatar"].(string)
|
||||
|
||||
return httpx.OK(c, vo)
|
||||
}
|
||||
|
||||
// ======================== 注册 ========================
|
||||
func registerUser(
|
||||
dbClient *db.Client,
|
||||
c fiber.Ctx,
|
||||
openid, nickname, avatar string,
|
||||
loginType int16,
|
||||
vo *types.LoginVO,
|
||||
) error {
|
||||
userId := uniqueid.NextId()
|
||||
|
||||
if nickname == "" {
|
||||
nickname = fmt.Sprintf("用户_%d", userId)
|
||||
}
|
||||
if avatar == "" {
|
||||
avatar = "https://default-avatar-url.com/default.png"
|
||||
}
|
||||
|
||||
err := dbClient.WithTx(c.Context(), func(tx *db.Client) error {
|
||||
|
||||
if err := tx.Insert(c.Context(), "user", "id", map[string]any{
|
||||
"id": userId,
|
||||
"nickname": nickname,
|
||||
"avatar": avatar,
|
||||
"status": 1,
|
||||
"create_time": "NOW()",
|
||||
"update_time": "NOW()",
|
||||
"last_login_time": "NOW()",
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tx.Insert(c.Context(), "user_oauth", "id", map[string]any{
|
||||
"id": uniqueid.NextId(),
|
||||
"user_id": userId,
|
||||
"type": loginType,
|
||||
"openid": openid,
|
||||
"create_time": "NOW()",
|
||||
"update_time": "NOW()",
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
token, err := jwtx.CreateToken(c.Context(), map[string]any{
|
||||
"id": userId,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
vo.Token = token
|
||||
vo.UserId = userId
|
||||
vo.Nickname = nickname
|
||||
vo.Avatar = avatar
|
||||
|
||||
return httpx.OK(c, vo)
|
||||
}
|
||||
|
||||
// ======================== DB 查询封装(去重复 SQL) ========================
|
||||
func getUserByOpenID(dbClient *db.Client, c fiber.Ctx, openid string, loginType int16) (map[string]any, error) {
|
||||
|
||||
users, err := dbClient.LoadDataBySQL(
|
||||
c.Context(),
|
||||
"SELECT u.* FROM user_oauth o JOIN user u ON u.id = o.user_id WHERE o.openid = ? AND o.type = ?",
|
||||
[]any{openid, loginType},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(users) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return users[0], nil
|
||||
}
|
||||
|
||||
// ======================== 类型工具(极简版) ========================
|
||||
func toInt64(v any) int64 {
|
||||
i, _ := v.(int64)
|
||||
return i
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package handle
|
||||
|
||||
import (
|
||||
"allapp-go/internal/errors"
|
||||
"allapp-go/internal/httpx"
|
||||
"allapp-go/internal/types"
|
||||
"allapp-go/pkg/db"
|
||||
@@ -12,7 +13,7 @@ func SaveData(c fiber.Ctx) error {
|
||||
var req []types.SaveDataReq
|
||||
|
||||
if err := httpx.BindAndValidate(c, &req); err != nil {
|
||||
return err
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
if len(req) == 0 {
|
||||
@@ -49,7 +50,7 @@ func SaveData(c fiber.Ctx) error {
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
return httpx.OK(c, nil)
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"allapp-go/internal/config"
|
||||
"allapp-go/internal/handle"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
)
|
||||
|
||||
func SetupRouter(app *fiber.App) {
|
||||
func SetupRouter(app *fiber.App, cfg *config.Config) {
|
||||
api := app.Group(cfg.App.BaseUrl)
|
||||
|
||||
// ==================== data ====================
|
||||
app.Post("/data/loadData", handle.LoadData)
|
||||
app.Post("/data/loadDataBySql", handle.LoadDataBySql)
|
||||
app.Post("/data/saveData", handle.SaveData)
|
||||
api.Post("/data/loadData", handle.LoadData)
|
||||
api.Post("/data/loadDataBySql", handle.LoadDataBySql)
|
||||
api.Post("/data/saveData", handle.SaveData)
|
||||
|
||||
api.Post("/auth/login/qq", handle.LoginQq)
|
||||
api.Post("/auth/login/wechat", handle.LoginWechat)
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package types
|
||||
|
||||
type LoginQqDTO struct {
|
||||
Openid string `json:"openid" validate:"required"`
|
||||
Nickname string `json:"nickname"`
|
||||
Avatar string `json:"avatar"`
|
||||
}
|
||||
|
||||
type LoginWechatDTO struct {
|
||||
Code string `json:"code" validate:"required"`
|
||||
}
|
||||
|
||||
type LoginVO struct {
|
||||
Token string `json:"token"`
|
||||
UserId int64 `json:"userId"`
|
||||
Nickname string `json:"nickname"`
|
||||
Avatar string `json:"avatar"`
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// JSONStringToStruct 将 JSON 字符串反序列化到指定结构体类型
|
||||
func JSONStringToStruct[T any](jsonStr string) (*T, error) {
|
||||
var result T
|
||||
if err := json.Unmarshal([]byte(jsonStr), &result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func JSONStringToMap(jsonStr string) (map[string]interface{}, error) {
|
||||
var result map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(jsonStr), &result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GenerateCode 生成指定长度的随机邀请码(大写字母+数字)
|
||||
// 不会返回错误,如果随机失败,会自动重试
|
||||
func GenerateCode(length int) string {
|
||||
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
code := make([]byte, length)
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
for {
|
||||
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
|
||||
if err != nil {
|
||||
// 随机失败,重试
|
||||
continue
|
||||
}
|
||||
code[i] = charset[num.Int64()]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return string(code)
|
||||
}
|
||||
@@ -13,7 +13,7 @@ func (c *Client) LoadData(
|
||||
orderBy string,
|
||||
searchColumns []string,
|
||||
args []any,
|
||||
) ([][]any, error) {
|
||||
) ([]map[string]any, error) {
|
||||
|
||||
var where string
|
||||
|
||||
@@ -50,12 +50,14 @@ func (c *Client) LoadDataBySQL(
|
||||
ctx context.Context,
|
||||
sql string,
|
||||
args []any,
|
||||
) ([][]any, error) {
|
||||
) ([]map[string]any, error) {
|
||||
return c.query(ctx, sql, args...)
|
||||
}
|
||||
|
||||
// 统一查询方法(核心优化)
|
||||
func (c *Client) query(ctx context.Context, sql string, args ...any) ([][]any, error) {
|
||||
// ==========================
|
||||
// 核心查询方法(已升级)
|
||||
// ==========================
|
||||
func (c *Client) query(ctx context.Context, sql string, args ...any) ([]map[string]any, error) {
|
||||
sql = convertPlaceholder(sql)
|
||||
|
||||
rows, err := c.exec().Query(ctx, sql, args...)
|
||||
@@ -64,7 +66,10 @@ func (c *Client) query(ctx context.Context, sql string, args ...any) ([][]any, e
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
result := make([][]any, 0, 16)
|
||||
// 获取字段名
|
||||
fields := rows.FieldDescriptions()
|
||||
|
||||
result := make([]map[string]any, 0, 16)
|
||||
|
||||
for rows.Next() {
|
||||
values, err := rows.Values()
|
||||
@@ -72,8 +77,13 @@ func (c *Client) query(ctx context.Context, sql string, args ...any) ([][]any, e
|
||||
return nil, err
|
||||
}
|
||||
|
||||
row := make([]any, len(values))
|
||||
copy(row, values)
|
||||
row := make(map[string]any, len(values))
|
||||
|
||||
for i, f := range fields {
|
||||
if i < len(values) {
|
||||
row[string(f.Name)] = values[i]
|
||||
}
|
||||
}
|
||||
|
||||
result = append(result, row)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package requestx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v3/client"
|
||||
)
|
||||
|
||||
// 全局 Fiber Client
|
||||
var cc = client.New()
|
||||
|
||||
func init() {
|
||||
cc.SetTimeout(10 * time.Second)
|
||||
}
|
||||
|
||||
// mergeConfig 合并默认配置和用户配置
|
||||
func mergeConfig(defaultCfg, userCfg client.Config) client.Config {
|
||||
if userCfg.Ctx != nil {
|
||||
defaultCfg.Ctx = userCfg.Ctx
|
||||
}
|
||||
if userCfg.Body != nil {
|
||||
defaultCfg.Body = userCfg.Body
|
||||
}
|
||||
if userCfg.Header != nil {
|
||||
if defaultCfg.Header == nil {
|
||||
defaultCfg.Header = map[string]string{}
|
||||
}
|
||||
for k, v := range userCfg.Header {
|
||||
defaultCfg.Header[k] = v
|
||||
}
|
||||
}
|
||||
if userCfg.Param != nil {
|
||||
defaultCfg.Param = userCfg.Param
|
||||
}
|
||||
if userCfg.PathParam != nil {
|
||||
defaultCfg.PathParam = userCfg.PathParam
|
||||
}
|
||||
if userCfg.FormData != nil {
|
||||
defaultCfg.FormData = userCfg.FormData
|
||||
}
|
||||
if userCfg.Timeout != 0 {
|
||||
defaultCfg.Timeout = userCfg.Timeout
|
||||
}
|
||||
if userCfg.UserAgent != "" {
|
||||
defaultCfg.UserAgent = userCfg.UserAgent
|
||||
}
|
||||
if userCfg.Referer != "" {
|
||||
defaultCfg.Referer = userCfg.Referer
|
||||
}
|
||||
defaultCfg.DisablePathNormalizing = userCfg.DisablePathNormalizing
|
||||
defaultCfg.MaxRedirects = userCfg.MaxRedirects
|
||||
|
||||
return defaultCfg
|
||||
}
|
||||
|
||||
func Get(url string, cfg client.Config) (string, int, error) {
|
||||
defaultCfg := client.Config{
|
||||
Ctx: context.Background(),
|
||||
}
|
||||
finalCfg := mergeConfig(defaultCfg, cfg)
|
||||
|
||||
resp, err := cc.Get(url, finalCfg)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
return string(resp.Body()), resp.StatusCode(), nil
|
||||
}
|
||||
|
||||
func PostJSON(url string, data any, cfg client.Config) (string, int, error) {
|
||||
defaultCfg := client.Config{
|
||||
Ctx: context.Background(),
|
||||
Body: data,
|
||||
Header: map[string]string{"Content-Type": "application/json"},
|
||||
}
|
||||
finalCfg := mergeConfig(defaultCfg, cfg)
|
||||
|
||||
resp, err := cc.Post(url, finalCfg)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
return string(resp.Body()), resp.StatusCode(), nil
|
||||
}
|
||||
|
||||
func PostForm(url string, formData map[string]string, cfg client.Config) (string, int, error) {
|
||||
defaultCfg := client.Config{
|
||||
Ctx: context.Background(),
|
||||
FormData: formData,
|
||||
Header: map[string]string{"Content-Type": "application/x-www-form-urlencoded"},
|
||||
}
|
||||
finalCfg := mergeConfig(defaultCfg, cfg)
|
||||
|
||||
resp, err := cc.Post(url, finalCfg)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
return string(resp.Body()), resp.StatusCode(), nil
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package wechat
|
||||
|
||||
import (
|
||||
"allapp-go/pkg/common"
|
||||
"allapp-go/pkg/requestx"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/gofiber/fiber/v3/client"
|
||||
)
|
||||
|
||||
type jwtManager struct {
|
||||
appid string
|
||||
secret string
|
||||
}
|
||||
|
||||
var (
|
||||
manager *jwtManager
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
func Init(appid string, secret string) {
|
||||
once.Do(func() {
|
||||
manager = &jwtManager{
|
||||
appid: appid,
|
||||
secret: secret,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func getManager() *jwtManager {
|
||||
if manager == nil {
|
||||
panic("wechat未初始化,请先调用Init")
|
||||
}
|
||||
return manager
|
||||
}
|
||||
|
||||
func GetWechatAccess(code string) (openid string, accessToken string, err error) {
|
||||
bodyData, bodyCode, err := requestx.Get(
|
||||
"https://api.weixin.qq.com/sns/oauth2/access_token",
|
||||
client.Config{
|
||||
Param: map[string]string{
|
||||
"appid": getManager().appid,
|
||||
"secret": getManager().secret,
|
||||
"code": code,
|
||||
"grant_type": "authorization_code",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
if bodyCode != 200 {
|
||||
return "", "", fmt.Errorf("微信Http请求失败: %d", bodyCode)
|
||||
}
|
||||
|
||||
accessMap, err := common.JSONStringToMap(bodyData)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
accessToken, _ = accessMap["access_token"].(string)
|
||||
openid, _ = accessMap["openid"].(string)
|
||||
|
||||
if accessToken == "" || openid == "" {
|
||||
return "", "", fmt.Errorf("获取微信登录凭证失败: %s", bodyData)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func GetWechatUserInfo(accessToken, openid string) (nickname, avatar string, err error) {
|
||||
bodyData, bodyCode, err := requestx.Get(
|
||||
"https://api.weixin.qq.com/sns/userinfo",
|
||||
client.Config{
|
||||
Param: map[string]string{
|
||||
"access_token": accessToken,
|
||||
"openid": openid,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
if bodyCode != 200 {
|
||||
return "", "", fmt.Errorf("微信Http请求失败")
|
||||
}
|
||||
|
||||
userMap, err := common.JSONStringToMap(bodyData)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
nickname, _ = userMap["nickname"].(string)
|
||||
avatar, _ = userMap["headimgurl"].(string)
|
||||
|
||||
return
|
||||
}
|
||||
Reference in new issue
Block a user