docs(website): 添加简记memo产品原型和UI设计规范文档

- 新增「简记memo」一体化小程序产品原型设计文档
- 新增简记memo完整版UI视觉设计规范和界面细节
- 添加IDEA项目配置文件.gitignore
- 创建404页面HTML文件,包含响应式布局和错误提示
- 添加关于页面HTML文件,展示品牌介绍和团队信息
- 实现AES加解密工具函数,支持请求体加密
- 添加用户协议页面基础框架
This commit is contained in:
sunct
2026-07-31 14:12:32 +08:00
commit 3c3bf53ae4
115 changed files with 21304 additions and 0 deletions
+117
View File
@@ -0,0 +1,117 @@
// 统一返回格式 + 自动加密
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)
}