优化开放接口日志分类

This commit is contained in:
yml2213
2026-07-30 21:07:10 +08:00
parent 9c3414607a
commit a16e518b6c
6 changed files with 210 additions and 9 deletions
+2
View File
@@ -23,6 +23,7 @@ func NewOpenHandler(fulfillmentSvc *service.FulfillmentService) *OpenHandler {
// QueryOrder GET /api/open/v1/orders/:order_no // QueryOrder GET /api/open/v1/orders/:order_no
// 上游输入店铺订单号,查询商品与是否可发货 // 上游输入店铺订单号,查询商品与是否可发货
func (h *OpenHandler) QueryOrder(c *gin.Context) { func (h *OpenHandler) QueryOrder(c *gin.Context) {
openlog.SetAction(c, openlog.ActionQuery)
orderNo := c.Param("order_no") orderNo := c.Param("order_no")
if orderNo == "" { if orderNo == "" {
orderNo = c.Query("order_no") orderNo = c.Query("order_no")
@@ -67,6 +68,7 @@ type shipNotifyReq struct {
// ShipNotify POST /api/open/v1/orders/ship-notify // ShipNotify POST /api/open/v1/orders/ship-notify
// 上游发货后推送结果,同步订单状态 // 上游发货后推送结果,同步订单状态
func (h *OpenHandler) ShipNotify(c *gin.Context) { func (h *OpenHandler) ShipNotify(c *gin.Context) {
openlog.SetAction(c, openlog.ActionPush)
var req shipNotifyReq var req shipNotifyReq
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
openlog.Warn(c, "ship_notify bind_fail err=%v", err) openlog.Warn(c, "ship_notify bind_fail err=%v", err)
+5
View File
@@ -25,6 +25,7 @@ func NewOpenV1Handler(merchantSvc *service.MerchantService, fulfillmentSvc *serv
} }
func (h *OpenV1Handler) ListProducts(c *gin.Context) { func (h *OpenV1Handler) ListProducts(c *gin.Context) {
openlog.SetAction(c, openlog.ActionList)
page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
size, _ := strconv.Atoi(c.DefaultQuery("size", "20")) size, _ := strconv.Atoi(c.DefaultQuery("size", "20"))
openlog.Info(c, "list_products start page=%d size=%d", page, size) openlog.Info(c, "list_products start page=%d size=%d", page, size)
@@ -47,6 +48,7 @@ type openCreateOrderReq struct {
} }
func (h *OpenV1Handler) CreateOrder(c *gin.Context) { func (h *OpenV1Handler) CreateOrder(c *gin.Context) {
openlog.SetAction(c, openlog.ActionCreate)
var req openCreateOrderReq var req openCreateOrderReq
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
openlog.Warn(c, "create_order bind_fail err=%v", err) openlog.Warn(c, "create_order bind_fail err=%v", err)
@@ -103,6 +105,7 @@ func (h *OpenV1Handler) CreateOrder(c *gin.Context) {
} }
func (h *OpenV1Handler) QueryOrder(c *gin.Context) { func (h *OpenV1Handler) QueryOrder(c *gin.Context) {
openlog.SetAction(c, openlog.ActionQuery)
orderNo := c.Param("order_no") orderNo := c.Param("order_no")
openlog.Info(c, "query_order start order_no=%s", orderNo) openlog.Info(c, "query_order start order_no=%s", orderNo)
order, err := h.fulfillmentSvc.GetOrder(middleware.GetMerchantID(c), orderNo) order, err := h.fulfillmentSvc.GetOrder(middleware.GetMerchantID(c), orderNo)
@@ -125,6 +128,7 @@ type openCancelOrderReq struct {
} }
func (h *OpenV1Handler) CancelOrder(c *gin.Context) { func (h *OpenV1Handler) CancelOrder(c *gin.Context) {
openlog.SetAction(c, openlog.ActionCancel)
var req openCancelOrderReq var req openCancelOrderReq
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
openlog.Warn(c, "cancel_order bind_fail err=%v", err) openlog.Warn(c, "cancel_order bind_fail err=%v", err)
@@ -145,6 +149,7 @@ func (h *OpenV1Handler) CancelOrder(c *gin.Context) {
} }
func (h *OpenV1Handler) GetWallet(c *gin.Context) { func (h *OpenV1Handler) GetWallet(c *gin.Context) {
openlog.SetAction(c, openlog.ActionWallet)
openlog.Info(c, "get_wallet start") openlog.Info(c, "get_wallet start")
wallet, err := h.fulfillmentSvc.GetWallet(middleware.GetMerchantID(c)) wallet, err := h.fulfillmentSvc.GetWallet(middleware.GetMerchantID(c))
if err != nil { if err != nil {
+2
View File
@@ -43,6 +43,8 @@ func OpenAuth(cfg OpenAuthConfig) gin.HandlerFunc {
} }
return func(c *gin.Context) { return func(c *gin.Context) {
reqID := openlog.EnsureReqID(c) reqID := openlog.EnsureReqID(c)
side, action := openlog.ScopeFromPath(openlog.SideClient, c.Request.Method, c.Request.URL.Path)
openlog.SetScope(c, side, action)
c.Set(openlog.CtxDebug, cfg.Debug) c.Set(openlog.CtxDebug, cfg.Debug)
c.Set(openlog.CtxStart, time.Now()) c.Set(openlog.CtxStart, time.Now())
c.Header("X-Request-Id", reqID) c.Header("X-Request-Id", reqID)
@@ -55,6 +55,8 @@ func SourceOpenAuth(cfg SourceOpenAuthConfig) gin.HandlerFunc {
store := newSourceNonceStore() store := newSourceNonceStore()
return func(c *gin.Context) { return func(c *gin.Context) {
reqID := openlog.EnsureReqID(c) reqID := openlog.EnsureReqID(c)
side, action := openlog.ScopeFromPath(openlog.SideSource, c.Request.Method, c.Request.URL.Path)
openlog.SetScope(c, side, action)
c.Set(openlog.CtxDebug, cfg.Debug) c.Set(openlog.CtxDebug, cfg.Debug)
c.Set(openlog.CtxStart, time.Now()) c.Set(openlog.CtxStart, time.Now())
c.Header("X-Request-Id", reqID) c.Header("X-Request-Id", reqID)
+111 -4
View File
@@ -16,6 +16,27 @@ const (
CtxBody = "open_body" CtxBody = "open_body"
CtxStart = "open_start" CtxStart = "open_start"
CtxAPIKey = "open_api_key" CtxAPIKey = "open_api_key"
CtxSide = "open_side"
CtxAPIType = "open_api_type"
CtxAction = "open_action"
)
const (
SideClient = "client"
SideSource = "source"
APITypeClientV1 = "client_v1"
APITypeSourceOpenV1 = "source_open_v1"
APITypeUnknown = "unknown"
ActionAuth = "auth"
ActionList = "list"
ActionCreate = "create"
ActionQuery = "query"
ActionCancel = "cancel"
ActionWallet = "wallet"
ActionPush = "push"
ActionUnknown = "unknown"
) )
// Enabled 是否开启开放接口详细日志 // Enabled 是否开启开放接口详细日志
@@ -50,6 +71,38 @@ func EnsureReqID(c *gin.Context) string {
return id return id
} }
func SetScope(c *gin.Context, side, action string) {
c.Set(CtxSide, side)
c.Set(CtxAPIType, APITypeFromSide(side))
c.Set(CtxAction, action)
}
func SetAction(c *gin.Context, action string) {
c.Set(CtxAction, action)
}
func ScopeFromPath(side, method, path string) (string, string) {
action := ActionUnknown
switch side {
case SideClient:
action = clientAction(method, path)
case SideSource:
action = sourceAction(method, path)
}
return side, action
}
func APITypeFromSide(side string) string {
switch side {
case SideClient:
return APITypeClientV1
case SideSource:
return APITypeSourceOpenV1
default:
return APITypeUnknown
}
}
func IsDebug(c *gin.Context) bool { func IsDebug(c *gin.Context) bool {
if !Enabled { if !Enabled {
return false return false
@@ -92,12 +145,37 @@ func Truncate(s string, max int) string {
return s[:max] + fmt.Sprintf("...(%d bytes)", len(s)) return s[:max] + fmt.Sprintf("...(%d bytes)", len(s))
} }
func Prefix(c *gin.Context) string {
side, _ := c.Get(CtxSide)
apiType, _ := c.Get(CtxAPIType)
action, _ := c.Get(CtxAction)
sideText, _ := side.(string)
apiTypeText, _ := apiType.(string)
actionText, _ := action.(string)
tag := "open"
if apiTypeText != "" && apiTypeText != APITypeUnknown {
tag += "." + apiTypeText
} else if sideText != "" {
tag += "." + sideText
}
if actionText != "" {
tag += "." + actionText
}
fields := ""
if apiTypeText != "" {
fields += fmt.Sprintf(" api_type=%s", apiTypeText)
}
if actionText != "" {
fields += fmt.Sprintf(" action=%s", actionText)
}
return fmt.Sprintf("[%s] req_id=%s%s ", tag, GetReqID(c), fields)
}
func Info(c *gin.Context, format string, args ...interface{}) { func Info(c *gin.Context, format string, args ...interface{}) {
if !IsDebug(c) { if !IsDebug(c) {
return return
} }
prefix := fmt.Sprintf("[open] req_id=%s ", GetReqID(c)) log.Printf(Prefix(c)+format, args...)
log.Printf(prefix+format, args...)
} }
func Warn(c *gin.Context, format string, args ...interface{}) { func Warn(c *gin.Context, format string, args ...interface{}) {
@@ -105,8 +183,7 @@ func Warn(c *gin.Context, format string, args ...interface{}) {
if !Enabled { if !Enabled {
return return
} }
prefix := fmt.Sprintf("[open] req_id=%s ", GetReqID(c)) log.Printf(Prefix(c)+format, args...)
log.Printf(prefix+format, args...)
} }
func Elapsed(c *gin.Context) time.Duration { func Elapsed(c *gin.Context) time.Duration {
@@ -117,3 +194,33 @@ func Elapsed(c *gin.Context) time.Duration {
} }
return 0 return 0
} }
func clientAction(method, path string) string {
method = strings.ToUpper(method)
switch {
case method == "GET" && path == "/api/client/v1/products":
return ActionList
case method == "POST" && path == "/api/client/v1/orders":
return ActionCreate
case method == "GET" && strings.HasPrefix(path, "/api/client/v1/orders/"):
return ActionQuery
case method == "POST" && strings.HasPrefix(path, "/api/client/v1/orders/") && strings.HasSuffix(path, "/cancel"):
return ActionCancel
case method == "GET" && path == "/api/client/v1/wallet":
return ActionWallet
default:
return ActionUnknown
}
}
func sourceAction(method, path string) string {
method = strings.ToUpper(method)
switch {
case method == "GET" && strings.HasPrefix(path, "/api/open/v1/orders/"):
return ActionQuery
case method == "POST" && path == "/api/open/v1/orders/ship-notify":
return ActionPush
default:
return ActionUnknown
}
}
@@ -0,0 +1,83 @@
package openlog
import (
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
)
func TestPrefixIncludesSideAndAction(t *testing.T) {
gin.SetMode(gin.TestMode)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Set(CtxReqID, "req123")
SetScope(c, SideSource, ActionPush)
got := Prefix(c)
want := "[open.source_open_v1.push] req_id=req123 api_type=source_open_v1 action=push "
if got != want {
t.Fatalf("prefix mismatch: got %q want %q", got, want)
}
}
func TestScopeFromPath(t *testing.T) {
cases := []struct {
name string
side string
method string
path string
wantSide string
wantAction string
wantType string
}{
{
name: "客户侧查询订单",
side: SideClient,
method: "GET",
path: "/api/client/v1/orders/FO123",
wantSide: SideClient,
wantAction: ActionQuery,
wantType: APITypeClientV1,
},
{
name: "客户侧创建订单",
side: SideClient,
method: "POST",
path: "/api/client/v1/orders",
wantSide: SideClient,
wantAction: ActionCreate,
wantType: APITypeClientV1,
},
{
name: "源头侧发货推送",
side: SideSource,
method: "POST",
path: "/api/open/v1/orders/ship-notify",
wantSide: SideSource,
wantAction: ActionPush,
wantType: APITypeSourceOpenV1,
},
{
name: "源头侧查询订单",
side: SideSource,
method: "GET",
path: "/api/open/v1/orders/FO123",
wantSide: SideSource,
wantAction: ActionQuery,
wantType: APITypeSourceOpenV1,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
gotSide, gotAction := ScopeFromPath(tc.side, tc.method, tc.path)
if gotSide != tc.wantSide || gotAction != tc.wantAction {
t.Fatalf("scope mismatch: got %s/%s want %s/%s", gotSide, gotAction, tc.wantSide, tc.wantAction)
}
if gotType := APITypeFromSide(gotSide); gotType != tc.wantType {
t.Fatalf("api type mismatch: got %s want %s", gotType, tc.wantType)
}
})
}
}