- 将 wechat_official_account 重命名为 weixin_public_account,符合中文命名规范 - 新增 DOCX 文档生成技能、聊天历史、请求 ID 中间件 - 增强工作流、热点服务、文章服务等模块功能 - 前端同步重命名组件和 API - 新增架构文档 AR13/AR14、专员文档更新 - 补充测试用例(seed_specialists_test, db_migration_test) Co-Authored-AI: yes
79 lines
1.9 KiB
Go
79 lines
1.9 KiB
Go
package web
|
|
|
|
import (
|
|
"log"
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// CtxRequestID Gin Context 中存放 request_id 的键(由 middleware.RequestID 写入)。
|
|
const CtxRequestID = "req_id"
|
|
|
|
// RequestIDOf 从 Gin Context 读取本次请求的 request_id(无则返回空串)。
|
|
func RequestIDOf(c *gin.Context) string {
|
|
if v, ok := c.Get(CtxRequestID); ok {
|
|
if s, ok := v.(string); ok {
|
|
return s
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// OK 成功响应信封 {"code":0,"message":"success","data":...,"error":null}
|
|
func OK(c *gin.Context, data any) {
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"code": CodeOK,
|
|
"message": "success",
|
|
"data": data,
|
|
"error": nil,
|
|
})
|
|
}
|
|
|
|
// Fail 错误响应信封,统一输出四层模型:
|
|
//
|
|
// {"code": 稳定业务错误码, "message": 用户主提示, "data": null,
|
|
// "error": {"category":分类, "status":HTTP状态码, "hint":下一步建议, "request_id":追踪编号, "details":开发上下文}}
|
|
//
|
|
// 并回写 X-Request-ID 响应头,便于前后端与日志联查。
|
|
func Fail(c *gin.Context, e *AppError) {
|
|
requestID := RequestIDOf(c)
|
|
e.RequestID = requestID
|
|
c.Header("X-Request-ID", requestID)
|
|
|
|
errorObj := gin.H{
|
|
"category": e.Category,
|
|
"status": e.StatusCode,
|
|
"hint": e.Hint,
|
|
"request_id": requestID,
|
|
}
|
|
if e.Details != nil {
|
|
errorObj["details"] = e.Details
|
|
}
|
|
|
|
logFail(c, e)
|
|
|
|
c.JSON(e.StatusCode, gin.H{
|
|
"code": e.Code,
|
|
"message": e.Message,
|
|
"data": nil,
|
|
"error": errorObj,
|
|
})
|
|
}
|
|
|
|
// logFail 记录结构化错误日志,满足可定位要求:request_id / method / path / status / code / category。
|
|
func logFail(c *gin.Context, e *AppError) {
|
|
if e == nil {
|
|
return
|
|
}
|
|
req := c.Request
|
|
method := ""
|
|
path := ""
|
|
if req != nil {
|
|
method = req.Method
|
|
path = req.URL.Path
|
|
}
|
|
log.Printf("HTTP ERROR [%s] status=%d code=%d %s %s -> %s request_id=%s",
|
|
e.Category, e.StatusCode, e.Code, method, path, e.Message, e.RequestID)
|
|
}
|