feat: 微信公众号技能包重命名(weixin_public_account)并增强功能
- 将 wechat_official_account 重命名为 weixin_public_account,符合中文命名规范 - 新增 DOCX 文档生成技能、聊天历史、请求 ID 中间件 - 增强工作流、热点服务、文章服务等模块功能 - 前端同步重命名组件和 API - 新增架构文档 AR13/AR14、专员文档更新 - 补充测试用例(seed_specialists_test, db_migration_test) Co-Authored-AI: yes
This commit is contained in:
@@ -1,17 +1,78 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// OK 成功响应信封 {"data": ..., "error": null, "message": "success"}
|
||||
func OK(c *gin.Context, data any) {
|
||||
c.JSON(http.StatusOK, gin.H{"data": data, "error": nil, "message": "success"})
|
||||
// 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 ""
|
||||
}
|
||||
|
||||
// Fail 错误响应信封 {"data": null, "error": ..., "message": ...}
|
||||
// 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) {
|
||||
c.JSON(e.StatusCode, gin.H{"data": nil, "error": e.Code, "message": e.Message})
|
||||
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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user