style: 统一代码格式化 (go fmt + 配置更新)
Some checks failed
部署管理后台 / 🧪 测试和构建 (push) Failing after 1m5s
部署管理后台 / 🔒 安全扫描 (push) Has been skipped
部署后端服务 / 🧪 测试后端 (push) Failing after 3m13s
部署前端网站 / 🧪 测试和构建 (push) Failing after 2m10s
部署管理后台 / 🚀 部署到生产环境 (push) Has been skipped
部署后端服务 / 🚀 构建并部署 (push) Has been skipped
部署管理后台 / 🔄 回滚部署 (push) Has been skipped
部署前端网站 / 🚀 部署到生产环境 (push) Has been skipped
部署后端服务 / 🔄 回滚部署 (push) Has been skipped
Some checks failed
部署管理后台 / 🧪 测试和构建 (push) Failing after 1m5s
部署管理后台 / 🔒 安全扫描 (push) Has been skipped
部署后端服务 / 🧪 测试后端 (push) Failing after 3m13s
部署前端网站 / 🧪 测试和构建 (push) Failing after 2m10s
部署管理后台 / 🚀 部署到生产环境 (push) Has been skipped
部署后端服务 / 🚀 构建并部署 (push) Has been skipped
部署管理后台 / 🔄 回滚部署 (push) Has been skipped
部署前端网站 / 🚀 部署到生产环境 (push) Has been skipped
部署后端服务 / 🔄 回滚部署 (push) Has been skipped
- 后端:应用 go fmt 自动格式化,统一代码风格 - 前端:更新 API 配置,完善类型安全 - 所有代码符合项目规范,准备生产部署
This commit is contained in:
@ -7,8 +7,8 @@ import (
|
||||
|
||||
type Config struct {
|
||||
rest.RestConf
|
||||
Database database.Config `json:"database"`
|
||||
Auth AuthConfig `json:"auth"`
|
||||
Database database.Config `json:"database"`
|
||||
Auth AuthConfig `json:"auth"`
|
||||
FileUpload FileUploadConfig `json:"file_upload"`
|
||||
Middleware MiddlewareConfig `json:"middleware"`
|
||||
}
|
||||
@ -28,6 +28,6 @@ type MiddlewareConfig struct {
|
||||
EnableCORS bool `json:"enable_cors"`
|
||||
EnableLogger bool `json:"enable_logger"`
|
||||
EnableErrorHandle bool `json:"enable_error_handle"`
|
||||
CORSOrigins []string `json:"cors_origins"`
|
||||
LogLevel string `json:"log_level"`
|
||||
CORSOrigins []string `json:"cors_origins"`
|
||||
LogLevel string `json:"log_level"`
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ func UploadPhotoHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
Title: r.FormValue("title"),
|
||||
Description: r.FormValue("description"),
|
||||
}
|
||||
|
||||
|
||||
// 解析 category_id
|
||||
if categoryIdStr := r.FormValue("category_id"); categoryIdStr != "" {
|
||||
var categoryId int64
|
||||
|
||||
@ -39,24 +39,24 @@ func (l *LoginLogic) Login(req *types.LoginRequest) (resp *types.LoginResponse,
|
||||
logx.Errorf("查询用户失败: %v", err)
|
||||
return nil, errorx.NewWithCode(errorx.ServerError)
|
||||
}
|
||||
|
||||
|
||||
// 2. 验证密码
|
||||
if !hash.CheckPassword(req.Password, user.Password) {
|
||||
return nil, errorx.NewWithCode(errorx.InvalidPassword)
|
||||
}
|
||||
|
||||
|
||||
// 3. 检查用户状态
|
||||
if user.Status == 0 {
|
||||
return nil, errorx.NewWithCode(errorx.UserDisabled)
|
||||
}
|
||||
|
||||
|
||||
// 4. 生成 JWT token
|
||||
token, err := jwt.GenerateToken(user.Id, user.Username, l.svcCtx.Config.Auth.AccessSecret, time.Hour*24*7)
|
||||
if err != nil {
|
||||
logx.Errorf("生成 JWT token 失败: %v", err)
|
||||
return nil, errorx.NewWithCode(errorx.ServerError)
|
||||
}
|
||||
|
||||
|
||||
// 5. 返回登录结果
|
||||
return &types.LoginResponse{
|
||||
BaseResponse: types.BaseResponse{
|
||||
|
||||
@ -34,34 +34,34 @@ func (l *RegisterLogic) Register(req *types.RegisterRequest) (resp *types.Regist
|
||||
if err == nil && existingUser != nil {
|
||||
return nil, errors.New("用户名已存在")
|
||||
}
|
||||
|
||||
|
||||
// 2. 检查邮箱是否已存在
|
||||
existingEmail, err := l.svcCtx.UserModel.FindOneByEmail(l.ctx, req.Email)
|
||||
if err == nil && existingEmail != nil {
|
||||
return nil, errors.New("邮箱已存在")
|
||||
}
|
||||
|
||||
|
||||
// 3. 加密密码
|
||||
hashedPassword, err := hash.HashPassword(req.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
// 4. 创建用户
|
||||
user := &model.User{
|
||||
Username: req.Username,
|
||||
Email: req.Email,
|
||||
Password: hashedPassword,
|
||||
Status: 1, // 默认激活状态
|
||||
Username: req.Username,
|
||||
Email: req.Email,
|
||||
Password: hashedPassword,
|
||||
Status: 1, // 默认激活状态
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
|
||||
|
||||
_, err = l.svcCtx.UserModel.Insert(l.ctx, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
// 5. 返回注册结果
|
||||
return &types.RegisterResponse{
|
||||
BaseResponse: types.BaseResponse{
|
||||
|
||||
@ -35,12 +35,12 @@ func (l *CreateCategoryLogic) CreateCategory(req *types.CreateCategoryRequest) (
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
|
||||
|
||||
_, err = l.svcCtx.CategoryModel.Insert(l.ctx, category)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
// 2. 返回结果
|
||||
return &types.CreateCategoryResponse{
|
||||
BaseResponse: types.BaseResponse{
|
||||
|
||||
@ -30,13 +30,13 @@ func (l *GetCategoryListLogic) GetCategoryList(req *types.GetCategoryListRequest
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
// 2. 统计总数
|
||||
total, err := l.svcCtx.CategoryModel.Count(l.ctx, req.Keyword)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
// 3. 转换数据结构
|
||||
var categoryList []types.Category
|
||||
for _, category := range categories {
|
||||
@ -48,7 +48,7 @@ func (l *GetCategoryListLogic) GetCategoryList(req *types.GetCategoryListRequest
|
||||
UpdatedAt: category.UpdatedAt.Unix(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 4. 返回结果
|
||||
return &types.GetCategoryListResponse{
|
||||
BaseResponse: types.BaseResponse{
|
||||
|
||||
@ -32,14 +32,14 @@ func (l *GetPhotoListLogic) GetPhotoList(req *types.GetPhotoListRequest) (resp *
|
||||
logx.Errorf("查询照片列表失败: %v", err)
|
||||
return nil, errorx.NewWithCode(errorx.ServerError)
|
||||
}
|
||||
|
||||
|
||||
// 2. 统计总数
|
||||
total, err := l.svcCtx.PhotoModel.Count(l.ctx, req.CategoryId, req.UserId, req.Keyword)
|
||||
if err != nil {
|
||||
logx.Errorf("统计照片数量失败: %v", err)
|
||||
return nil, errorx.NewWithCode(errorx.ServerError)
|
||||
}
|
||||
|
||||
|
||||
// 3. 转换数据结构
|
||||
var photoList []types.Photo
|
||||
for _, photo := range photos {
|
||||
@ -55,7 +55,7 @@ func (l *GetPhotoListLogic) GetPhotoList(req *types.GetPhotoListRequest) (resp *
|
||||
UpdatedAt: photo.UpdatedAt.Unix(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 4. 返回结果
|
||||
return &types.GetPhotoListResponse{
|
||||
BaseResponse: types.BaseResponse{
|
||||
|
||||
@ -36,7 +36,7 @@ func (l *GetPhotoLogic) GetPhoto(req *types.GetPhotoRequest) (resp *types.GetPho
|
||||
logx.Errorf("查询照片失败: %v", err)
|
||||
return nil, errorx.NewWithCode(errorx.ServerError)
|
||||
}
|
||||
|
||||
|
||||
// 2. 返回结果
|
||||
return &types.GetPhotoResponse{
|
||||
BaseResponse: types.BaseResponse{
|
||||
|
||||
@ -40,7 +40,7 @@ func (l *UploadPhotoLogic) UploadPhoto(req *types.UploadPhotoRequest, file multi
|
||||
// 后续需要实现JWT中间件
|
||||
userId = int64(1)
|
||||
}
|
||||
|
||||
|
||||
// 2. 验证分类是否存在
|
||||
_, err = l.svcCtx.CategoryModel.FindOne(l.ctx, req.CategoryId)
|
||||
if err != nil {
|
||||
@ -50,20 +50,20 @@ func (l *UploadPhotoLogic) UploadPhoto(req *types.UploadPhotoRequest, file multi
|
||||
logx.Errorf("查询分类失败: %v", err)
|
||||
return nil, errorx.NewWithCode(errorx.ServerError)
|
||||
}
|
||||
|
||||
|
||||
// 3. 处理文件上传
|
||||
fileConfig := fileUtil.Config{
|
||||
MaxSize: l.svcCtx.Config.FileUpload.MaxSize,
|
||||
UploadDir: l.svcCtx.Config.FileUpload.UploadDir,
|
||||
AllowedTypes: l.svcCtx.Config.FileUpload.AllowedTypes,
|
||||
}
|
||||
|
||||
|
||||
uploadResult, err := fileUtil.UploadPhoto(file, header, fileConfig)
|
||||
if err != nil {
|
||||
logx.Errorf("文件上传失败: %v", err)
|
||||
return nil, errorx.NewWithCode(errorx.PhotoUploadFail)
|
||||
}
|
||||
|
||||
|
||||
// 4. 创建照片记录
|
||||
photo := &model.Photo{
|
||||
Title: req.Title,
|
||||
@ -75,7 +75,7 @@ func (l *UploadPhotoLogic) UploadPhoto(req *types.UploadPhotoRequest, file multi
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
|
||||
|
||||
_, err = l.svcCtx.PhotoModel.Insert(l.ctx, photo)
|
||||
if err != nil {
|
||||
// 如果数据库保存失败,删除已上传的文件
|
||||
@ -84,7 +84,7 @@ func (l *UploadPhotoLogic) UploadPhoto(req *types.UploadPhotoRequest, file multi
|
||||
logx.Errorf("保存照片记录失败: %v", err)
|
||||
return nil, errorx.NewWithCode(errorx.ServerError)
|
||||
}
|
||||
|
||||
|
||||
// 5. 返回上传结果
|
||||
return &types.UploadPhotoResponse{
|
||||
BaseResponse: types.BaseResponse{
|
||||
|
||||
@ -50,7 +50,7 @@ func (l *DeleteUserLogic) DeleteUser(req *types.DeleteUserRequest) (resp *types.
|
||||
// 检查用户是否有关联的照片
|
||||
// 这里可以添加业务逻辑来决定是否允许删除有照片的用户
|
||||
// 如果要严格控制,可以先检查用户是否有照片,如果有则不允许删除
|
||||
|
||||
|
||||
// 删除用户
|
||||
err = l.svcCtx.UserModel.Delete(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
|
||||
@ -30,13 +30,13 @@ func (l *GetUserListLogic) GetUserList(req *types.GetUserListRequest) (resp *typ
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
// 2. 统计总数
|
||||
total, err := l.svcCtx.UserModel.Count(l.ctx, req.Keyword)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
// 3. 转换数据结构(不返回密码)
|
||||
var userList []types.User
|
||||
for _, user := range users {
|
||||
@ -50,7 +50,7 @@ func (l *GetUserListLogic) GetUserList(req *types.GetUserListRequest) (resp *typ
|
||||
UpdatedAt: user.UpdatedAt.Unix(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 4. 返回结果
|
||||
return &types.GetUserListResponse{
|
||||
BaseResponse: types.BaseResponse{
|
||||
|
||||
@ -55,7 +55,7 @@ func (l *UploadAvatarLogic) UploadAvatar(req *types.UploadAvatarRequest, r *http
|
||||
// 4. 获取上传的文件
|
||||
uploadedFile, header, err := r.FormFile("avatar")
|
||||
if err != nil {
|
||||
return nil, errorx.New(errorx.ParamError, "获取上传文件失败: " + err.Error())
|
||||
return nil, errorx.New(errorx.ParamError, "获取上传文件失败: "+err.Error())
|
||||
}
|
||||
defer uploadedFile.Close()
|
||||
|
||||
@ -90,10 +90,10 @@ func (l *UploadAvatarLogic) UploadAvatar(req *types.UploadAvatarRequest, r *http
|
||||
|
||||
filename := fmt.Sprintf("avatar_%d_%d%s", req.Id, time.Now().Unix(), ext)
|
||||
avatarDir := "uploads/avatars"
|
||||
|
||||
|
||||
// 8. 确保头像目录存在
|
||||
if err := os.MkdirAll(avatarDir, 0755); err != nil {
|
||||
return nil, errorx.New(errorx.ServerError, "创建头像目录失败: " + err.Error())
|
||||
return nil, errorx.New(errorx.ServerError, "创建头像目录失败: "+err.Error())
|
||||
}
|
||||
|
||||
avatarPath := filepath.Join(avatarDir, filename)
|
||||
@ -101,13 +101,13 @@ func (l *UploadAvatarLogic) UploadAvatar(req *types.UploadAvatarRequest, r *http
|
||||
// 9. 保存原始头像文件
|
||||
destFile, err := os.Create(avatarPath)
|
||||
if err != nil {
|
||||
return nil, errorx.New(errorx.ServerError, "创建头像文件失败: " + err.Error())
|
||||
return nil, errorx.New(errorx.ServerError, "创建头像文件失败: "+err.Error())
|
||||
}
|
||||
defer destFile.Close()
|
||||
|
||||
_, err = io.Copy(destFile, uploadedFile)
|
||||
if err != nil {
|
||||
return nil, errorx.New(errorx.ServerError, "保存头像文件失败: " + err.Error())
|
||||
return nil, errorx.New(errorx.ServerError, "保存头像文件失败: "+err.Error())
|
||||
}
|
||||
|
||||
// 10. 生成压缩版本的头像 (150x150像素)
|
||||
@ -142,7 +142,7 @@ func (l *UploadAvatarLogic) UploadAvatar(req *types.UploadAvatarRequest, r *http
|
||||
if avatarPath != compressedPath {
|
||||
os.Remove(compressedPath)
|
||||
}
|
||||
return nil, errorx.New(errorx.ServerError, "更新用户头像失败: " + err.Error())
|
||||
return nil, errorx.New(errorx.ServerError, "更新用户头像失败: "+err.Error())
|
||||
}
|
||||
|
||||
return &types.UploadAvatarResponse{
|
||||
|
||||
@ -73,4 +73,4 @@ func (e UnauthorizedError) Error() string {
|
||||
|
||||
func NewUnauthorizedError(message string) UnauthorizedError {
|
||||
return UnauthorizedError{Message: message}
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,7 +87,7 @@ func NewCORSMiddleware(config CORSConfig) *CORSMiddleware {
|
||||
func (m *CORSMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
origin := r.Header.Get("Origin")
|
||||
|
||||
|
||||
// 检查来源是否被允许
|
||||
if origin != "" && m.isOriginAllowed(origin) {
|
||||
w.Header().Set("Access-Control-Allow-Origin", origin)
|
||||
@ -160,16 +160,16 @@ func (m *CORSMiddleware) isOriginAllowed(origin string) bool {
|
||||
func (m *CORSMiddleware) setSecurityHeaders(w http.ResponseWriter) {
|
||||
// 防止点击劫持
|
||||
w.Header().Set("X-Frame-Options", "DENY")
|
||||
|
||||
|
||||
// 防止 MIME 类型嗅探
|
||||
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||
|
||||
|
||||
// XSS 保护
|
||||
w.Header().Set("X-XSS-Protection", "1; mode=block")
|
||||
|
||||
|
||||
// 引用者策略
|
||||
w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin")
|
||||
|
||||
|
||||
// 内容安全策略 (基础版)
|
||||
w.Header().Set("Content-Security-Policy", "default-src 'self'; img-src 'self' data: https:; style-src 'self' 'unsafe-inline'; script-src 'self'")
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,8 +19,8 @@ type ErrorConfig struct {
|
||||
EnableDetailedErrors bool // 是否启用详细错误信息 (开发环境)
|
||||
EnableStackTrace bool // 是否启用堆栈跟踪
|
||||
EnableErrorMonitor bool // 是否启用错误监控
|
||||
IgnoreHTTPCodes []int // 忽略的HTTP状态码 (不记录为错误)
|
||||
SensitiveFields []string // 敏感字段列表 (日志时隐藏)
|
||||
IgnoreHTTPCodes []int // 忽略的HTTP状态码 (不记录为错误)
|
||||
SensitiveFields []string // 敏感字段列表 (日志时隐藏)
|
||||
}
|
||||
|
||||
// DefaultErrorConfig 默认错误配置
|
||||
@ -29,8 +29,8 @@ func DefaultErrorConfig() ErrorConfig {
|
||||
EnableDetailedErrors: false, // 生产环境默认关闭
|
||||
EnableStackTrace: false, // 生产环境默认关闭
|
||||
EnableErrorMonitor: true,
|
||||
IgnoreHTTPCodes: []int{http.StatusNotFound, http.StatusMethodNotAllowed},
|
||||
SensitiveFields: []string{"password", "token", "secret", "key", "authorization"},
|
||||
IgnoreHTTPCodes: []int{http.StatusNotFound, http.StatusMethodNotAllowed},
|
||||
SensitiveFields: []string{"password", "token", "secret", "key", "authorization"},
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,7 +111,7 @@ func (m *ErrorMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
|
||||
// handlePanic 处理panic
|
||||
func (m *ErrorMiddleware) handlePanic(w *errorResponseWriter, r *http.Request, err interface{}) {
|
||||
stack := string(debug.Stack())
|
||||
|
||||
|
||||
// 记录panic日志
|
||||
logFields := map[string]interface{}{
|
||||
"error": err,
|
||||
@ -206,7 +206,7 @@ func (m *ErrorMiddleware) respondWithError(w http.ResponseWriter, r *http.Reques
|
||||
|
||||
// 设置HTTP状态码
|
||||
httpStatus := errorx.GetHttpStatus(err.Code)
|
||||
|
||||
|
||||
// 设置响应头
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(httpStatus)
|
||||
@ -218,10 +218,10 @@ func (m *ErrorMiddleware) respondWithError(w http.ResponseWriter, r *http.Reques
|
||||
// sanitizeFields 隐藏敏感字段
|
||||
func (m *ErrorMiddleware) sanitizeFields(data map[string]interface{}) map[string]interface{} {
|
||||
sanitized := make(map[string]interface{})
|
||||
|
||||
|
||||
for key, value := range data {
|
||||
lowerKey := strings.ToLower(key)
|
||||
|
||||
|
||||
// 检查是否为敏感字段
|
||||
sensitive := false
|
||||
for _, sensitiveField := range m.config.SensitiveFields {
|
||||
@ -230,7 +230,7 @@ func (m *ErrorMiddleware) sanitizeFields(data map[string]interface{}) map[string
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if sensitive {
|
||||
sanitized[key] = "***REDACTED***"
|
||||
} else {
|
||||
@ -242,7 +242,7 @@ func (m *ErrorMiddleware) sanitizeFields(data map[string]interface{}) map[string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return sanitized
|
||||
}
|
||||
|
||||
@ -322,4 +322,4 @@ var CommonErrors = struct {
|
||||
Code: 429,
|
||||
Msg: "Rate Limit Exceeded",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,8 +15,8 @@ import (
|
||||
|
||||
// LoggerConfig 日志配置
|
||||
type LoggerConfig struct {
|
||||
EnableRequestBody bool // 是否记录请求体
|
||||
EnableResponseBody bool // 是否记录响应体
|
||||
EnableRequestBody bool // 是否记录请求体
|
||||
EnableResponseBody bool // 是否记录响应体
|
||||
MaxBodySize int64 // 最大记录的请求/响应体大小
|
||||
SkipPaths []string // 跳过记录的路径
|
||||
SlowRequestDuration time.Duration // 慢请求阈值
|
||||
@ -26,9 +26,9 @@ type LoggerConfig struct {
|
||||
// DefaultLoggerConfig 默认日志配置
|
||||
func DefaultLoggerConfig() LoggerConfig {
|
||||
return LoggerConfig{
|
||||
EnableRequestBody: false, // 默认不记录请求体 (可能包含敏感信息)
|
||||
EnableResponseBody: false, // 默认不记录响应体 (减少日志量)
|
||||
MaxBodySize: 1024, // 最大记录1KB
|
||||
EnableRequestBody: false, // 默认不记录请求体 (可能包含敏感信息)
|
||||
EnableResponseBody: false, // 默认不记录响应体 (减少日志量)
|
||||
MaxBodySize: 1024, // 最大记录1KB
|
||||
SkipPaths: []string{"/health", "/metrics", "/favicon.ico"},
|
||||
SlowRequestDuration: 1 * time.Second,
|
||||
EnablePanicRecover: true,
|
||||
@ -60,7 +60,7 @@ func newResponseWriter(w http.ResponseWriter) *responseWriter {
|
||||
return &responseWriter{
|
||||
ResponseWriter: w,
|
||||
status: http.StatusOK,
|
||||
body: &bytes.Buffer{},
|
||||
body: &bytes.Buffer{},
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,12 +68,12 @@ func newResponseWriter(w http.ResponseWriter) *responseWriter {
|
||||
func (rw *responseWriter) Write(b []byte) (int, error) {
|
||||
size, err := rw.ResponseWriter.Write(b)
|
||||
rw.size += int64(size)
|
||||
|
||||
|
||||
// 记录响应体 (如果启用)
|
||||
if rw.body.Len() < int(1024) { // 限制缓存大小
|
||||
rw.body.Write(b)
|
||||
}
|
||||
|
||||
|
||||
return size, err
|
||||
}
|
||||
|
||||
@ -163,7 +163,7 @@ func (m *LoggerMiddleware) generateRequestID(r *http.Request) string {
|
||||
if requestID := r.Header.Get("X-Request-ID"); requestID != "" {
|
||||
return requestID
|
||||
}
|
||||
|
||||
|
||||
// 生成新的请求ID
|
||||
return fmt.Sprintf("%d-%s", time.Now().UnixNano(), randomString(8))
|
||||
}
|
||||
@ -208,13 +208,13 @@ func (m *LoggerMiddleware) logRequestStart(r *http.Request, requestID, requestBo
|
||||
// logRequestComplete 记录请求完成
|
||||
func (m *LoggerMiddleware) logRequestComplete(r *http.Request, requestID string, status int, size int64, duration time.Duration, responseBody string) {
|
||||
fields := map[string]interface{}{
|
||||
"request_id": requestID,
|
||||
"method": r.Method,
|
||||
"path": r.URL.Path,
|
||||
"status": status,
|
||||
"response_size": size,
|
||||
"duration_ms": duration.Milliseconds(),
|
||||
"duration": duration.String(),
|
||||
"request_id": requestID,
|
||||
"method": r.Method,
|
||||
"path": r.URL.Path,
|
||||
"status": status,
|
||||
"response_size": size,
|
||||
"duration_ms": duration.Milliseconds(),
|
||||
"duration": duration.String(),
|
||||
}
|
||||
|
||||
if responseBody != "" {
|
||||
@ -267,7 +267,7 @@ func getClientIP(r *http.Request) string {
|
||||
return ip
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 使用 RemoteAddr
|
||||
if ip := r.RemoteAddr; ip != "" {
|
||||
// 移除端口号
|
||||
@ -276,7 +276,7 @@ func getClientIP(r *http.Request) string {
|
||||
}
|
||||
return ip
|
||||
}
|
||||
|
||||
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
@ -296,4 +296,4 @@ func randomString(length int) string {
|
||||
result[i] = charset[time.Now().UnixNano()%int64(len(charset))]
|
||||
}
|
||||
return string(result)
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,11 +13,11 @@ import (
|
||||
|
||||
// MiddlewareManager 中间件管理器
|
||||
type MiddlewareManager struct {
|
||||
config config.Config
|
||||
corsMiddleware *CORSMiddleware
|
||||
logMiddleware *LoggerMiddleware
|
||||
config config.Config
|
||||
corsMiddleware *CORSMiddleware
|
||||
logMiddleware *LoggerMiddleware
|
||||
errorMiddleware *ErrorMiddleware
|
||||
authMiddleware *AuthMiddleware
|
||||
authMiddleware *AuthMiddleware
|
||||
}
|
||||
|
||||
// NewMiddlewareManager 创建中间件管理器
|
||||
@ -34,12 +34,12 @@ func NewMiddlewareManager(c config.Config) *MiddlewareManager {
|
||||
// getCORSConfig 获取CORS配置
|
||||
func getCORSConfig(c config.Config) CORSConfig {
|
||||
env := getEnvironment()
|
||||
|
||||
|
||||
if env == "production" {
|
||||
// 生产环境使用严格的CORS配置
|
||||
return ProductionCORSConfig(getProductionOrigins())
|
||||
}
|
||||
|
||||
|
||||
// 开发环境使用宽松的CORS配置
|
||||
return DefaultCORSConfig()
|
||||
}
|
||||
@ -47,27 +47,27 @@ func getCORSConfig(c config.Config) CORSConfig {
|
||||
// getLoggerConfig 获取日志配置
|
||||
func getLoggerConfig(c config.Config) LoggerConfig {
|
||||
env := getEnvironment()
|
||||
|
||||
|
||||
config := DefaultLoggerConfig()
|
||||
|
||||
|
||||
if env == "development" {
|
||||
// 开发环境启用详细日志
|
||||
config.EnableRequestBody = true
|
||||
config.EnableResponseBody = true
|
||||
config.MaxBodySize = 4096
|
||||
}
|
||||
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// getErrorConfig 获取错误配置
|
||||
func getErrorConfig(c config.Config) ErrorConfig {
|
||||
env := getEnvironment()
|
||||
|
||||
|
||||
if env == "development" {
|
||||
return DevelopmentErrorConfig()
|
||||
}
|
||||
|
||||
|
||||
return DefaultErrorConfig()
|
||||
}
|
||||
|
||||
@ -108,9 +108,9 @@ func (m *MiddlewareManager) Chain(handler http.HandlerFunc, middlewares ...func(
|
||||
// GetGlobalMiddlewares 获取全局中间件
|
||||
func (m *MiddlewareManager) GetGlobalMiddlewares() []func(http.HandlerFunc) http.HandlerFunc {
|
||||
return []func(http.HandlerFunc) http.HandlerFunc{
|
||||
m.errorMiddleware.Handle, // 错误处理 (最外层)
|
||||
m.corsMiddleware.Handle, // CORS 处理
|
||||
m.logMiddleware.Handle, // 日志记录
|
||||
m.errorMiddleware.Handle, // 错误处理 (最外层)
|
||||
m.corsMiddleware.Handle, // CORS 处理
|
||||
m.logMiddleware.Handle, // 日志记录
|
||||
}
|
||||
}
|
||||
|
||||
@ -134,7 +134,7 @@ func (m *MiddlewareManager) ApplyAuthMiddlewares(handler http.HandlerFunc) http.
|
||||
func (m *MiddlewareManager) HealthCheck(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(`{"status":"ok","timestamp":"` +
|
||||
w.Write([]byte(`{"status":"ok","timestamp":"` +
|
||||
time.Now().Format("2006-01-02T15:04:05Z07:00") + `"}`))
|
||||
}
|
||||
|
||||
@ -171,7 +171,7 @@ func Recovery() MiddlewareFunc {
|
||||
"path": r.URL.Path,
|
||||
}
|
||||
logx.WithContext(r.Context()).Errorf("Panic recovered in Recovery middleware: %+v", fields)
|
||||
|
||||
|
||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||
}
|
||||
}()
|
||||
@ -188,10 +188,10 @@ func RequestID() MiddlewareFunc {
|
||||
if requestID == "" {
|
||||
requestID = generateRequestID()
|
||||
}
|
||||
|
||||
|
||||
w.Header().Set("X-Request-ID", requestID)
|
||||
r.Header.Set("X-Request-ID", requestID)
|
||||
|
||||
|
||||
next(w, r)
|
||||
})
|
||||
}
|
||||
@ -200,4 +200,4 @@ func RequestID() MiddlewareFunc {
|
||||
// generateRequestID 生成请求ID
|
||||
func generateRequestID() string {
|
||||
return randomString(16)
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,8 +3,8 @@ package model
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ CategoryModel = (*customCategoryModel)(nil)
|
||||
@ -39,20 +39,20 @@ func (m *customCategoryModel) withSession(session sqlx.Session) CategoryModel {
|
||||
func (m *customCategoryModel) FindList(ctx context.Context, page, pageSize int, keyword string) ([]*Category, error) {
|
||||
var conditions []string
|
||||
var args []interface{}
|
||||
|
||||
|
||||
if keyword != "" {
|
||||
conditions = append(conditions, "(`name` LIKE ? OR `description` LIKE ?)")
|
||||
args = append(args, "%"+keyword+"%", "%"+keyword+"%")
|
||||
}
|
||||
|
||||
|
||||
whereClause := ""
|
||||
if len(conditions) > 0 {
|
||||
whereClause = " WHERE " + strings.Join(conditions, " AND ")
|
||||
}
|
||||
|
||||
|
||||
offset := (page - 1) * pageSize
|
||||
args = append(args, pageSize, offset)
|
||||
|
||||
|
||||
query := fmt.Sprintf("select %s from %s%s ORDER BY `created_at` DESC LIMIT ? OFFSET ?", categoryRows, m.table, whereClause)
|
||||
var resp []*Category
|
||||
err := m.conn.QueryRowsCtx(ctx, &resp, query, args...)
|
||||
@ -63,17 +63,17 @@ func (m *customCategoryModel) FindList(ctx context.Context, page, pageSize int,
|
||||
func (m *customCategoryModel) Count(ctx context.Context, keyword string) (int64, error) {
|
||||
var conditions []string
|
||||
var args []interface{}
|
||||
|
||||
|
||||
if keyword != "" {
|
||||
conditions = append(conditions, "(`name` LIKE ? OR `description` LIKE ?)")
|
||||
args = append(args, "%"+keyword+"%", "%"+keyword+"%")
|
||||
}
|
||||
|
||||
|
||||
whereClause := ""
|
||||
if len(conditions) > 0 {
|
||||
whereClause = " WHERE " + strings.Join(conditions, " AND ")
|
||||
}
|
||||
|
||||
|
||||
query := fmt.Sprintf("select count(*) from %s%s", m.table, whereClause)
|
||||
var count int64
|
||||
err := m.conn.QueryRowCtx(ctx, &count, query, args...)
|
||||
|
||||
@ -3,8 +3,8 @@ package model
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ PhotoModel = (*customPhotoModel)(nil)
|
||||
@ -39,30 +39,30 @@ func (m *customPhotoModel) withSession(session sqlx.Session) PhotoModel {
|
||||
func (m *customPhotoModel) FindList(ctx context.Context, page, pageSize int, categoryId, userId int64, keyword string) ([]*Photo, error) {
|
||||
var conditions []string
|
||||
var args []interface{}
|
||||
|
||||
|
||||
if categoryId > 0 {
|
||||
conditions = append(conditions, "`category_id` = ?")
|
||||
args = append(args, categoryId)
|
||||
}
|
||||
|
||||
|
||||
if userId > 0 {
|
||||
conditions = append(conditions, "`user_id` = ?")
|
||||
args = append(args, userId)
|
||||
}
|
||||
|
||||
|
||||
if keyword != "" {
|
||||
conditions = append(conditions, "(`title` LIKE ? OR `description` LIKE ?)")
|
||||
args = append(args, "%"+keyword+"%", "%"+keyword+"%")
|
||||
}
|
||||
|
||||
|
||||
whereClause := ""
|
||||
if len(conditions) > 0 {
|
||||
whereClause = " WHERE " + strings.Join(conditions, " AND ")
|
||||
}
|
||||
|
||||
|
||||
offset := (page - 1) * pageSize
|
||||
args = append(args, pageSize, offset)
|
||||
|
||||
|
||||
query := fmt.Sprintf("select %s from %s%s ORDER BY `created_at` DESC LIMIT ? OFFSET ?", photoRows, m.table, whereClause)
|
||||
var resp []*Photo
|
||||
err := m.conn.QueryRowsCtx(ctx, &resp, query, args...)
|
||||
@ -73,27 +73,27 @@ func (m *customPhotoModel) FindList(ctx context.Context, page, pageSize int, cat
|
||||
func (m *customPhotoModel) Count(ctx context.Context, categoryId, userId int64, keyword string) (int64, error) {
|
||||
var conditions []string
|
||||
var args []interface{}
|
||||
|
||||
|
||||
if categoryId > 0 {
|
||||
conditions = append(conditions, "`category_id` = ?")
|
||||
args = append(args, categoryId)
|
||||
}
|
||||
|
||||
|
||||
if userId > 0 {
|
||||
conditions = append(conditions, "`user_id` = ?")
|
||||
args = append(args, userId)
|
||||
}
|
||||
|
||||
|
||||
if keyword != "" {
|
||||
conditions = append(conditions, "(`title` LIKE ? OR `description` LIKE ?)")
|
||||
args = append(args, "%"+keyword+"%", "%"+keyword+"%")
|
||||
}
|
||||
|
||||
|
||||
whereClause := ""
|
||||
if len(conditions) > 0 {
|
||||
whereClause = " WHERE " + strings.Join(conditions, " AND ")
|
||||
}
|
||||
|
||||
|
||||
query := fmt.Sprintf("select count(*) from %s%s", m.table, whereClause)
|
||||
var count int64
|
||||
err := m.conn.QueryRowCtx(ctx, &count, query, args...)
|
||||
|
||||
@ -3,8 +3,8 @@ package model
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ UserModel = (*customUserModel)(nil)
|
||||
@ -39,20 +39,20 @@ func (m *customUserModel) withSession(session sqlx.Session) UserModel {
|
||||
func (m *customUserModel) FindList(ctx context.Context, page, pageSize int, keyword string) ([]*User, error) {
|
||||
var conditions []string
|
||||
var args []interface{}
|
||||
|
||||
|
||||
if keyword != "" {
|
||||
conditions = append(conditions, "(`username` LIKE ? OR `email` LIKE ?)")
|
||||
args = append(args, "%"+keyword+"%", "%"+keyword+"%")
|
||||
}
|
||||
|
||||
|
||||
whereClause := ""
|
||||
if len(conditions) > 0 {
|
||||
whereClause = " WHERE " + strings.Join(conditions, " AND ")
|
||||
}
|
||||
|
||||
|
||||
offset := (page - 1) * pageSize
|
||||
args = append(args, pageSize, offset)
|
||||
|
||||
|
||||
query := fmt.Sprintf("select %s from %s%s ORDER BY `created_at` DESC LIMIT ? OFFSET ?", userRows, m.table, whereClause)
|
||||
var resp []*User
|
||||
err := m.conn.QueryRowsCtx(ctx, &resp, query, args...)
|
||||
@ -63,17 +63,17 @@ func (m *customUserModel) FindList(ctx context.Context, page, pageSize int, keyw
|
||||
func (m *customUserModel) Count(ctx context.Context, keyword string) (int64, error) {
|
||||
var conditions []string
|
||||
var args []interface{}
|
||||
|
||||
|
||||
if keyword != "" {
|
||||
conditions = append(conditions, "(`username` LIKE ? OR `email` LIKE ?)")
|
||||
args = append(args, "%"+keyword+"%", "%"+keyword+"%")
|
||||
}
|
||||
|
||||
|
||||
whereClause := ""
|
||||
if len(conditions) > 0 {
|
||||
whereClause = " WHERE " + strings.Join(conditions, " AND ")
|
||||
}
|
||||
|
||||
|
||||
query := fmt.Sprintf("select count(*) from %s%s", m.table, whereClause)
|
||||
var count int64
|
||||
err := m.conn.QueryRowCtx(ctx, &count, query, args...)
|
||||
|
||||
@ -2,17 +2,17 @@ package svc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
"gorm.io/gorm"
|
||||
"photography-backend/internal/config"
|
||||
"photography-backend/internal/middleware"
|
||||
"photography-backend/internal/model"
|
||||
"photography-backend/pkg/utils/database"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
type ServiceContext struct {
|
||||
Config config.Config
|
||||
DB *gorm.DB
|
||||
Config config.Config
|
||||
DB *gorm.DB
|
||||
UserModel model.UserModel
|
||||
PhotoModel model.PhotoModel
|
||||
CategoryModel model.CategoryModel
|
||||
@ -24,13 +24,13 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
|
||||
// Create sqlx connection for go-zero models
|
||||
sqlxConn := sqlx.NewSqlConn(getSQLDriverName(c.Database.Driver), getSQLDataSource(c.Database))
|
||||
|
||||
|
||||
return &ServiceContext{
|
||||
Config: c,
|
||||
DB: db,
|
||||
Config: c,
|
||||
DB: db,
|
||||
UserModel: model.NewUserModel(sqlxConn),
|
||||
PhotoModel: model.NewPhotoModel(sqlxConn),
|
||||
CategoryModel: model.NewCategoryModel(sqlxConn),
|
||||
|
||||
Reference in New Issue
Block a user