- 新增「简记memo」一体化小程序产品原型设计文档 - 新增简记memo完整版UI视觉设计规范和界面细节 - 添加IDEA项目配置文件.gitignore - 创建404页面HTML文件,包含响应式布局和错误提示 - 添加关于页面HTML文件,展示品牌介绍和团队信息 - 实现AES加解密工具函数,支持请求体加密 - 添加用户协议页面基础框架
118 lines
3.0 KiB
Go
118 lines
3.0 KiB
Go
// 统一返回格式 + 自动加密
|
|
package utils
|
|
|
|
import (
|
|
"encoding/json"
|
|
"go.uber.org/zap"
|
|
"net/http"
|
|
"simple-memo/global"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// Response 统一返回结构体
|
|
type Response struct {
|
|
Code int `json:"code"` // 200成功
|
|
Msg string `json:"msg"` // 提示信息
|
|
Data interface{} `json:"data,omitempty"`
|
|
}
|
|
|
|
// LogContextFields 从 context 中提取链路日志字段
|
|
func LogContextFields(c *gin.Context) []zap.Field {
|
|
fields := []zap.Field{}
|
|
if c == nil {
|
|
return fields
|
|
}
|
|
|
|
ctx := c.Request.Context()
|
|
|
|
// 优先从标准 context 读取
|
|
if v := ctx.Value(RequestIdKey); v != nil {
|
|
fields = append(fields, zap.String("request_id", v.(string)))
|
|
} else if v, ok := c.Get("request_id"); ok {
|
|
fields = append(fields, zap.String("request_id", v.(string)))
|
|
}
|
|
|
|
if v := ctx.Value(RequestRouteKey); v != nil {
|
|
fields = append(fields, zap.String("route", v.(string)))
|
|
} else if v, ok := c.Get("route"); ok {
|
|
fields = append(fields, zap.String("route", v.(string)))
|
|
}
|
|
|
|
if v := ctx.Value(ClientIPKey); v != nil {
|
|
fields = append(fields, zap.String("client_ip", v.(string)))
|
|
} else if v, ok := c.Get("client_ip"); ok {
|
|
fields = append(fields, zap.String("client_ip", v.(string)))
|
|
}
|
|
|
|
if uid := c.GetUint("userID"); uid > 0 {
|
|
fields = append(fields, zap.Uint("user_id", uid))
|
|
}
|
|
|
|
return fields
|
|
}
|
|
|
|
// Result 统一返回(根据请求路径决定是否加密)
|
|
func Result(c *gin.Context, code int, msg string, data interface{}) {
|
|
// 添加缓存控制头,防止浏览器缓存
|
|
c.Header("Cache-Control", "no-cache, no-store, must-revalidate")
|
|
c.Header("Pragma", "no-cache")
|
|
c.Header("Expires", "0")
|
|
|
|
resp := Response{Code: code, Msg: msg, Data: data}
|
|
|
|
// 先序列化成JSON
|
|
jsonStr, _ := json.Marshal(resp)
|
|
|
|
// ====================== 日志:打印【要加密的明文】 ======================
|
|
baseFields := LogContextFields(c)
|
|
global.Logger.Info("【返回原始明文】",
|
|
append(baseFields,
|
|
zap.String("path", c.Request.URL.Path),
|
|
zap.String("raw_data", string(jsonStr)),
|
|
)...,
|
|
)
|
|
|
|
// 判断是否为网页端接口(/api/web 路径)
|
|
isWebAPI := len(c.Request.URL.Path) >= 8 && c.Request.URL.Path[:8] == "/api/web"
|
|
|
|
if isWebAPI {
|
|
// 网页端:直接返回JSON,不加密
|
|
c.JSON(http.StatusOK, resp)
|
|
} else {
|
|
// 小程序端:加密返回
|
|
encrypted, err := AESEncrypt(jsonStr)
|
|
if err != nil {
|
|
global.Logger.Error("加密失败",
|
|
append(baseFields, zap.Error(err))...,
|
|
)
|
|
c.JSON(http.StatusOK, Response{Code: 500, Msg: "加密错误"})
|
|
return
|
|
}
|
|
|
|
// ====================== 日志:打印【加密后返回给前端的串】 ======================
|
|
global.Logger.Info("【返回加密数据】",
|
|
append(baseFields,
|
|
zap.String("encrypt_data", encrypted),
|
|
)...,
|
|
)
|
|
|
|
c.JSON(http.StatusOK, gin.H{"data": encrypted})
|
|
}
|
|
}
|
|
|
|
// Ok 成功
|
|
func Ok(c *gin.Context, data interface{}) {
|
|
Result(c, 200, "success", data)
|
|
}
|
|
|
|
// Fail 失败
|
|
func Fail(c *gin.Context, msg string, code ...int) {
|
|
|
|
needCode := 400
|
|
if len(code) > 0 {
|
|
needCode = code[0]
|
|
}
|
|
Result(c, needCode, msg, nil)
|
|
}
|