From db6910b712ad2382c533eb871fa23a08297a5125 Mon Sep 17 00:00:00 2001 From: liuhaodong Date: Thu, 15 Jan 2026 18:12:42 +0800 Subject: [PATCH] =?UTF-8?q?init:=E6=B7=BB=E5=8A=A0=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E5=B0=8F=E7=A8=8B=E5=BA=8F=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 13 ++ api/client/mini_program_client.go | 203 ++++++++++++++++++ api/constant/constant.go | 5 + api/entity/third_party.go | 1 + api/handler/handler.go | 40 ++++ api/main.go | 65 ++++++ api/publish-prod.sh | 226 ++++++++++++++++++++ api/route/route.go | 25 +++ api/version.go | 3 + config/third-party-gateway-dev.consul.yaml | 1 + config/third-party-gateway-prod.conf | 9 + config/third-party-gateway-prod.consul.yaml | 1 + go.mod | 75 +++++++ utils/error.go | 79 +++++++ utils/gin.go | 47 ++++ utils/logger.go | 30 +++ 16 files changed, 823 insertions(+) create mode 100644 .gitignore create mode 100644 api/client/mini_program_client.go create mode 100644 api/constant/constant.go create mode 100644 api/entity/third_party.go create mode 100644 api/handler/handler.go create mode 100644 api/main.go create mode 100644 api/publish-prod.sh create mode 100644 api/route/route.go create mode 100644 api/version.go create mode 100644 config/third-party-gateway-dev.consul.yaml create mode 100644 config/third-party-gateway-prod.conf create mode 100644 config/third-party-gateway-prod.consul.yaml create mode 100644 go.mod create mode 100644 utils/error.go create mode 100644 utils/gin.go create mode 100644 utils/logger.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..13e5c41 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +*.pb.go +*.log +go.sum +*.bin +*.pem +*.srv +*.signature +*.zip +*.pdf +.idea +.vscode + + diff --git a/api/client/mini_program_client.go b/api/client/mini_program_client.go new file mode 100644 index 0000000..957b787 --- /dev/null +++ b/api/client/mini_program_client.go @@ -0,0 +1,203 @@ +package client + +import ( + "encoding/json" + "errors" + "fmt" + "jhttp" + "jredis" + "log" + "strconv" + "sync" + "third-party-gateway/api/constant" + "time" +) + +type MiniProgramAuthReq struct { + Code string `json:"code" binding:"required"` + SceneId string `json:"sceneId"` +} + +type MiniProgramLoginResponse struct { + OpenID string `json:"openid"` + SessionKey string `json:"session_key"` + UnionID string `json:"unionid,omitempty"` + ErrCode int `json:"errcode"` + ErrMsg string `json:"errmsg"` +} + +type MiniProgramTokenResponse struct { + AccessToken string `json:"access_token"` + ExpiresIn int `json:"expires_in"` + ErrCode int `json:"errcode"` + ErrMsg string `json:"errmsg"` +} + +var ( + _app_id = "" + _app_secret = "" +) + +// InitMiniProgramClient 初始化小程序客户端配置 +func InitMiniProgramClient(appid, appsecret string) { + _app_id = appid + _app_secret = appsecret +} + +// GetMiniProgramQrCode 获取小程序码 +func GetMiniProgramQrCode(envVersion string) ([]byte, int64, error) { + if envVersion == "" { + envVersion = "release" + } + // 生成唯一的场景值 + sceneId := generateUniqueID() + // 从缓存获取access_token + key := fmt.Sprintf(constant.MiniProgramAccessToken, _app_id) + accessToken, err := jredis.GetString(key) + if err != nil || accessToken == "" { + // 重新获取access_token + accessToken, err = refreshAccessToken() + if err != nil { + return nil, 0, fmt.Errorf("获取access_token失败: %w", err) + } + } + // 调用微信接口获取小程序码 + url := fmt.Sprintf("https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s", accessToken) + params := map[string]any{ + "scene": sceneId, + "width": 280, + "env_version": envVersion, + } + qrcode, err := jhttp.Post(url, nil, params) + if err != nil { + return nil, 0, err + } + + // 尝试解析错误响应 + var errResp MiniProgramTokenResponse + if err := json.Unmarshal(qrcode, &errResp); err == nil && errResp.ErrCode != 0 { + return nil, 0, fmt.Errorf("获取小程序码失败:%s", errResp.ErrMsg) + } + + // 将sceneId存入Redis,设置过期时间 + //err = common.SetValueWithExpired(strconv.FormatInt(sceneId, 10), "waiting", 300) // 5分钟过期 + //if err != nil { + // log.ERROR("存储sceneId状态失败", err) + // return nil, 0, err + //} + return qrcode, sceneId, nil +} + +var ( + mu sync.Mutex + seq int64 + lastTs int64 +) + +// GenerateUniqueID 生成小于14位的唯一ID(最多13位) +func generateUniqueID() int64 { + mu.Lock() + defer mu.Unlock() + // 当前时间戳:秒级(10位)或毫秒级(13位)可调 + ts := time.Now().Unix() // 秒级时间戳(10位) + // 如果在同一秒内生成,则递增序列 + if ts == lastTs { + seq++ + } else { + lastTs = ts + seq = 0 + } + // 拼接时间戳和序列(限制在13位内) + idStr := fmt.Sprintf("%d%02d", ts%1e10, seq%100) // 10位时间戳 + 2位序列(最多12位) + id, _ := strconv.ParseInt(idStr, 10, 64) + return id +} + +// GetMiniProgramQrCode 获取小程序码 +func GetPrintMiniProgramQrCode(scene_Id, envVersion string) ([]byte, error) { + if envVersion == "" { + envVersion = "release" + } + // 生成唯一的场景值 + sceneId := fmt.Sprintf("scene_Id:%v", scene_Id) + // 从缓存获取access_token + key := fmt.Sprintf(constant.MiniProgramAccessToken, _app_id) + accessToken, err := jredis.GetString(key) + if err != nil || accessToken == "" { + // 重新获取access_token + accessToken, err = refreshAccessToken() + if err != nil { + return nil, fmt.Errorf("获取access_token失败: %w", err) + } + } + // 调用微信接口获取小程序码 + url := fmt.Sprintf("https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s", accessToken) + params := map[string]any{ + "scene": sceneId, + "page": "pages/index/index", + "width": 280, + "env_version": envVersion, + } + qrcode, err := jhttp.Post(url, nil, params) + if err != nil { + return nil, err + } + + // 尝试解析错误响应 + var errResp MiniProgramTokenResponse + if err := json.Unmarshal(qrcode, &errResp); err == nil && errResp.ErrCode != 0 { + log.Printf("获取小程序码失败:%s", errResp.ErrMsg) + return nil, fmt.Errorf("获取小程序码失败:%s", errResp.ErrMsg) + } + + // 将sceneId存入Redis,设置过期时间 + //err = common.SetValueWithExpired(strconv.FormatInt(sceneId, 10), "waiting", 300) // 5分钟过期 + //if err != nil { + // log.ERROR("存储sceneId状态失败", err) + // return nil, 0, err + //} + return qrcode, nil +} + +// GetMiniProgramOpenId 获取小程序用户openId +func GetMiniProgramOpenId(code string) (string, error) { + if code == "" { + return "", errors.New("code不能为空") + } + // 构建请求URL + url := fmt.Sprintf("https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code", + _app_id, _app_secret, code) + + // 发送请求并解析响应 + if result, err := jhttp.GetJson[MiniProgramLoginResponse](url, nil); err != nil { + log.Println("获取openId失败", err) + return "", err + } else if result.ErrCode != 0 { + log.Println(fmt.Errorf("获取openId失败:%s", result.ErrMsg)) + return "", fmt.Errorf("获取openId失败:%s", result.ErrMsg) + } else { + return result.OpenID, nil + } +} + +// refreshAccessToken 刷新access_token +func refreshAccessToken() (string, error) { + // 获取新的access_token + url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", + _app_id, _app_secret) + + // 发送请求并解析响应 + if result, err := jhttp.GetJson[MiniProgramTokenResponse](url, nil); err != nil { + return "", err + } else if result.ErrCode != 0 { + return "", fmt.Errorf("获取access_token失败:%s", result.ErrMsg) + } else { + // 将access_token存入Redis,设置过期时间为7000秒(微信默认7200秒) + key := fmt.Sprintf(constant.MiniProgramAccessToken, _app_id) + err := jredis.SetValueWithExpired(key, result.AccessToken, 7000) + if err != nil { + return "", err + } + return result.AccessToken, nil + } +} diff --git a/api/constant/constant.go b/api/constant/constant.go new file mode 100644 index 0000000..263381e --- /dev/null +++ b/api/constant/constant.go @@ -0,0 +1,5 @@ +package constant + +const ( + MiniProgramAccessToken = "third-party-gateway:mini_program:access_token:%s" //二维码扫描信息,存储唯一时间戳 +) diff --git a/api/entity/third_party.go b/api/entity/third_party.go new file mode 100644 index 0000000..9356433 --- /dev/null +++ b/api/entity/third_party.go @@ -0,0 +1 @@ +package entity diff --git a/api/handler/handler.go b/api/handler/handler.go new file mode 100644 index 0000000..ca35f2c --- /dev/null +++ b/api/handler/handler.go @@ -0,0 +1,40 @@ +package handler + +import ( + "github.com/gin-gonic/gin" + "jrest" + "net/http" + "third-party-gateway/api/client" + "third-party-gateway/utils" +) + +func GetPrintMiniProgramQrCode(c *gin.Context) { + var sceneId string + var envVersion string + err := jrest.QueryScanOptional(c, "sceneId:envVersion", + &sceneId, &envVersion) + if err != nil { + utils.ParamError(c) + return + } + resp, err := client.GetPrintMiniProgramQrCode(sceneId, envVersion) + if err != nil { + utils.Error(c, http.StatusInternalServerError, "获取微信小程序二维码失败") + return + } + c.Data(http.StatusOK, "application/octet-stream", resp) +} + +func GetMiniProgramOpenId(c *gin.Context) { + var code string + if err := jrest.QueryScan(c, "code", &code); err != nil { + utils.ParamError(c) + return + } + openId, err := client.GetMiniProgramOpenId(code) + if err != nil { + utils.Error(c, http.StatusInternalServerError, "权限检验失败") + return + } + utils.OKWithData(c, openId) +} diff --git a/api/main.go b/api/main.go new file mode 100644 index 0000000..e53614b --- /dev/null +++ b/api/main.go @@ -0,0 +1,65 @@ +package main + +import ( + "flag" + "fmt" + "jconfig" + "jconsul" + "jlog" + "jredis" + "jrest" + "jversion" + "log" + "third-party-gateway/api/client" + "third-party-gateway/api/route" + "third-party-gateway/utils" +) + +const ServiceName = "third-party-gateway" + +type ConsulConf struct { + Url string `yaml:"url"` +} + +type MPConfig struct { + AppId string `yaml:"appid"` + AppSecret string `yaml:"appsecret"` +} + +type Config struct { + HttpPort int32 `yaml:"api-port"` + Redis string `yaml:"redis"` + MP MPConfig `yaml:"mini-program"` + Debug bool `yaml:"debug"` +} + +func main() { + jversion.Init(VERSION) + jversion.LogVersion() + env := flag.String("env", "dev", "environment") + flag.Parse() + serviceNameEnv := ServiceName + "-" + *env + consulConf := ConsulConf{} + err := jconfig.ParseConfig("config/"+serviceNameEnv+".consul.yaml", &consulConf) + if err != nil { + return + } + + jconsul.InitConsul(consulConf.Url) + config := &Config{} + if err := jconsul.LoadConfig(serviceNameEnv, config); err != nil { + log.Println("load config error", err) + return + } + jlog.Init(config.Debug) + log.Println("config:", config) + jconsul.RegisterServices(fmt.Sprintf("%s:%d", serviceNameEnv, config.HttpPort)) + log.Println("conf:", config) + + jredis.Init(config.Redis) + defer jredis.Close() + client.InitMiniProgramClient(config.MP.AppId, config.MP.AppSecret) + jrest.ServerWithHandlers(config.Debug, utils.TracingHandler, utils.ErrorHandler) + route.InitRouter() + jrest.ServerRun(config.HttpPort) +} diff --git a/api/publish-prod.sh b/api/publish-prod.sh new file mode 100644 index 0000000..3ffb26e --- /dev/null +++ b/api/publish-prod.sh @@ -0,0 +1,226 @@ +#!/bin/bash + +#部署的服务器上要安装supervisor进程管理工具 + +TARGET="third-party-gateway-prod.srv" # 编译后的文件名 +REMOTE="root@jg-serv1" # 远程服务器 +REMOTE_DIR="/usr/local/third-party-gateway-prod" # 部署目录 +SERVICE_NAME="third-party-gateway-prod" # Supervisor 配置里的服务名 +WAIT_TIME=2 # 等待服务启动时间(秒) +BACKUP_DIR="$REMOTE_DIR/backup" # 旧版本备份目录 + +# Supervisor 配置目录 +#cat /etc/os-release 查看操作系统版本信息 +#Debian 安装supervisor +#sudo apt update +#sudo apt install supervisor -y +#systemctl status supervisor + + +SUPERVISOR_CONF_DIR="/etc/supervisor/conf.d" #Ubuntu/Debian 默认路径 +#SUPERVISOR_CONF_DIR="/etc/supervisord.d/" #CentOS 7(EPEL)默认路径 + +trap 'ssh -O exit $REMOTE 2>/dev/null || true' EXIT + +function remote_cmd() { + ssh "$REMOTE" "$@" /dev/null | head -n1") + if [ -z "$LATEST_BACKUP" ]; then + echo "⚠️ 没有找到备份,无法回滚!" + return + fi + + echo "♻️ 回滚到 $LATEST_BACKUP ..." + remote_cmd "cp '$LATEST_BACKUP' '$REMOTE_DIR/$TARGET' && chmod +x '$REMOTE_DIR/$TARGET'" + echo "🔄 重启服务..." + remote_cmd "supervisorctl restart '$SERVICE_NAME'" + + STATUS=$(remote_cmd "supervisorctl status '$SERVICE_NAME'") + echo "$STATUS" + + if echo "$STATUS" | grep -q "RUNNING"; then + echo "✅ 回滚成功,服务已恢复" + else + echo "❌ 回滚失败,请手动处理" + fi +} + +# 8️⃣ 清理本地产物 +function clear() { + echo "🧹 清理本地编译产物..." + rm -f "$TARGET" +} + +# 🔹 主流程 +build_local +ensure_remote_dir +backup_old +upload_binary +upload_consul_conf +#upload_locale_conf +upload_supervisor_conf +restart_service +clear + +echo "✅ 部署完成!" diff --git a/api/route/route.go b/api/route/route.go new file mode 100644 index 0000000..d0494e0 --- /dev/null +++ b/api/route/route.go @@ -0,0 +1,25 @@ +package route + +import ( + "jrest" + "third-party-gateway/api/handler" +) + +func InitRouter() { + root := jrest.RouteGroup("thirdParty") + + //base := root.Group("base") + //{ + // //上传文件 + // base.POST("upload", handler.FileUpload) + //} + + wechat := root.Group("wechat") + { + //获取授权二维码 + wechat.GET("getPrintMiniProgramQrCode", handler.GetPrintMiniProgramQrCode) + //扫码注册登录 + wechat.GET("getMiniProgramOpenId", handler.GetMiniProgramOpenId) + } + +} diff --git a/api/version.go b/api/version.go new file mode 100644 index 0000000..0bd2cad --- /dev/null +++ b/api/version.go @@ -0,0 +1,3 @@ +package main + +var VERSION = "0.0.1" diff --git a/config/third-party-gateway-dev.consul.yaml b/config/third-party-gateway-dev.consul.yaml new file mode 100644 index 0000000..b60a450 --- /dev/null +++ b/config/third-party-gateway-dev.consul.yaml @@ -0,0 +1 @@ +url: http://192.168.10.111:8500 \ No newline at end of file diff --git a/config/third-party-gateway-prod.conf b/config/third-party-gateway-prod.conf new file mode 100644 index 0000000..ab8d378 --- /dev/null +++ b/config/third-party-gateway-prod.conf @@ -0,0 +1,9 @@ +[program:third-party-gateway-prod] +command=/usr/local/third-party-gateway-prod/third-party-gateway-prod.srv --env=prod +directory=/usr/local/third-party-gateway-prod/ +autostart=true +autorestart=true +startsecs=3 +stderr_logfile=/usr/local/third-party-gateway-prod/log/err.log +stdout_logfile=/usr/local/third-party-gateway-prod/log/info.log +user=root diff --git a/config/third-party-gateway-prod.consul.yaml b/config/third-party-gateway-prod.consul.yaml new file mode 100644 index 0000000..1584556 --- /dev/null +++ b/config/third-party-gateway-prod.consul.yaml @@ -0,0 +1 @@ +url: http://192.168.1.13:8500 diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..c0f540a --- /dev/null +++ b/go.mod @@ -0,0 +1,75 @@ +module third-party-gateway + +go 1.25.3 + +replace ( + jconfig => ../golib/config + jconsul => ../golib/consul + jhttp => ../golib/http + jlog => ../golib/log + jobs => ../golib/obs + jredis => ../golib/redis + jrest => ../golib/restful + jversion => ../golib/version +) + +require ( + github.com/gin-gonic/gin v1.10.1 + jconfig v0.0.0-00010101000000-000000000000 + jconsul v0.0.0-00010101000000-000000000000 + jhttp v0.0.0-00010101000000-000000000000 + jlog v0.0.0-00010101000000-000000000000 + jobs v0.0.0-00010101000000-000000000000 + jredis v0.0.0-00010101000000-000000000000 + jrest v0.0.0-00010101000000-000000000000 + jversion v0.0.0-00010101000000-000000000000 +) + +require ( + github.com/armon/go-metrics v0.4.1 // indirect + github.com/bytedance/sonic v1.14.0 // indirect + github.com/bytedance/sonic/loader v0.3.0 // indirect + github.com/cloudwego/base64x v0.1.6 // indirect + github.com/fatih/color v1.16.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.4 // indirect + github.com/gin-contrib/sse v1.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.20.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect + github.com/gomodule/redigo v1.8.9 // indirect + github.com/hashicorp/consul/api v1.31.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-hclog v1.5.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/serf v0.10.1 // indirect + github.com/huaweicloud/huaweicloud-sdk-go-obs v3.21.12+incompatible // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/rs/xid v1.6.0 // indirect + github.com/rs/zerolog v1.34.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.3.0 // indirect + golang.org/x/arch v0.20.0 // indirect + golang.org/x/crypto v0.43.0 // indirect + golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect + golang.org/x/net v0.46.0 // indirect + golang.org/x/sys v0.37.0 // indirect + golang.org/x/text v0.30.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/utils/error.go b/utils/error.go new file mode 100644 index 0000000..7bb0080 --- /dev/null +++ b/utils/error.go @@ -0,0 +1,79 @@ +package utils + +import ( + "log" + "net/http" + + "github.com/gin-gonic/gin" +) + +type BusinessError struct { + Code int + Msg string +} + +type ErrorResult struct { + Msg string `json:"msg,omitempty"` +} + +func (e *BusinessError) Error() string { + return e.Msg +} + +func NewBusinessError(code int, msg string) *BusinessError { + return &BusinessError{code, msg} +} + +func ErrorHandler(c *gin.Context) { + c.Next() + + for _, e := range c.Errors { + switch err := e.Err.(type) { + case *BusinessError: + GinLogger(c).ERROR(err.Msg, "err", err.Error()) + c.JSON(err.Code, &ErrorResult{err.Msg}) + default: + GinLogger(c).ERROR("generic error", "err", err.Error()) + c.JSON(http.StatusInternalServerError, "ERROR") + } + return + } +} + +func ThrowAgError(c *gin.Context, err *BusinessError) { + c.AbortWithError(err.Code, err) +} + +func Error(c *gin.Context, code int, msg string) { + c.AbortWithError(code, &BusinessError{code, msg}) +} + +func AuthError(c *gin.Context) { + Error(c, http.StatusUnauthorized, "INVALID_TOKEN") +} + +func CodeError(c *gin.Context) { + Error(c, http.StatusBadRequest, "WRONG_CODE") +} + +func ParamError(c *gin.Context) { + Error(c, http.StatusBadRequest, "INVALID_PARAM") +} + +func PermissionError(c *gin.Context) { + Error(c, http.StatusForbidden, "NO_PERMISSION") +} + +func DbError(c *gin.Context) { + Error(c, http.StatusInternalServerError, "DB_ERROR") +} + +// 错误日志就近输出,后续不需要重复输出 +// 无错误返回true +func ErrorCheckAndLog(err error) bool { + if err != nil { + log.Println("db:", err) + return false + } + return true +} diff --git a/utils/gin.go b/utils/gin.go new file mode 100644 index 0000000..45cb40d --- /dev/null +++ b/utils/gin.go @@ -0,0 +1,47 @@ +package utils + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +type R struct { + Code int `json:"code"` + Data any `json:"data"` +} + +func OK(c *gin.Context) { + c.JSON(http.StatusOK, R{ + Code: http.StatusOK, + }) +} +func OKNoData(c *gin.Context) { + c.JSON(http.StatusNoContent, R{ + Code: http.StatusOK, + }) +} + +func OKWithData(c *gin.Context, obj interface{}) { + c.JSON(http.StatusOK, R{ + Code: http.StatusOK, + Data: obj, + }) +} + +func FailWithData(c *gin.Context, obj interface{}) { + c.JSON(http.StatusOK, R{ + Code: http.StatusInternalServerError, + Data: obj, + }) +} + +func OKWithList(c *gin.Context, total int, obj any) { + c.JSON(http.StatusOK, gin.H{ + "code": http.StatusOK, + "data": gin.H{ + "total": total, + "list": obj, + }, + }) +} diff --git a/utils/logger.go b/utils/logger.go new file mode 100644 index 0000000..3ba15d3 --- /dev/null +++ b/utils/logger.go @@ -0,0 +1,30 @@ +package utils + +import ( + "context" + "jlog" + + "github.com/gin-gonic/gin" +) + +type ctxKey string + +const _trace_key ctxKey = "trace_id" + +func TracingHandler(c *gin.Context) { + traceId := jlog.NewTraceId() + ctx := context.WithValue(c.Request.Context(), _trace_key, traceId) + c.Request = c.Request.WithContext(ctx) + c.Next() +} + +func TraceLogger(ctx context.Context) *jlog.Logger { + if traceID, ok := ctx.Value(_trace_key).(string); ok { + return jlog.NewLoggerWithTraceId(traceID) + } + return jlog.NewLoggerWithTraceId("mqttResp111222333") +} + +func GinLogger(c *gin.Context) *jlog.Logger { + return TraceLogger(c.Request.Context()) +}