refactor: 重构后端架构为 go-zero 框架,优化项目结构

主要变更:
- 采用 go-zero 框架替代 Gin,提升开发效率
- 重构项目结构,API 文件模块化组织
- 将 model 移至 api/internal/model 目录
- 移除 common 包,改为标准 pkg 目录结构
- 实现统一的仓储模式,支持配置驱动数据库切换
- 简化测试策略,专注 API 集成测试
- 更新 CLAUDE.md 文档,提供详细的开发指导

技术栈更新:
- 框架: Gin → go-zero v1.6.0+
- 代码生成: 引入 goctl 工具
- 架构模式: 四层架构 → go-zero 三层架构 (Handler→Logic→Model)
- 项目布局: 遵循 Go 社区标准和 go-zero 最佳实践
This commit is contained in:
xujiang
2025-07-10 15:05:52 +08:00
parent a2f2f66f88
commit 39a42695d3
52 changed files with 6047 additions and 2349 deletions

View File

@ -3,7 +3,8 @@ package auth
import (
"fmt"
"golang.org/x/crypto/bcrypt"
"photography-backend/internal/models"
"photography-backend/internal/model/entity"
"photography-backend/internal/model/dto"
"photography-backend/internal/repository/postgres"
)
@ -22,23 +23,15 @@ func NewAuthService(userRepo postgres.UserRepository, jwtService *JWTService) *A
}
// Login 用户登录
func (s *AuthService) Login(req *models.LoginRequest) (*models.LoginResponse, error) {
func (s *AuthService) Login(req *dto.LoginRequest) (*dto.LoginResponse, error) {
// 根据用户名或邮箱查找用户
var user *models.User
var user *entity.User
var err error
// 尝试按用户名查找
user, err = s.userRepo.GetByUsername(req.Username)
// 按邮箱查找用户
user, err = s.userRepo.GetByEmail(req.Email)
if err != nil {
return nil, fmt.Errorf("failed to get user: %w", err)
}
// 如果用户名未找到,尝试按邮箱查找
if user == nil {
user, err = s.userRepo.GetByEmail(req.Username)
if err != nil {
return nil, fmt.Errorf("failed to get user by email: %w", err)
}
return nil, fmt.Errorf("failed to get user by email: %w", err)
}
if user == nil {
@ -56,7 +49,7 @@ func (s *AuthService) Login(req *models.LoginRequest) (*models.LoginResponse, er
}
// 生成JWT令牌
tokenPair, err := s.jwtService.GenerateTokenPair(user.ID, user.Username, user.Role)
tokenPair, err := s.jwtService.GenerateTokenPair(user.ID, user.Username, string(user.Role))
if err != nil {
return nil, fmt.Errorf("failed to generate tokens: %w", err)
}
@ -70,17 +63,19 @@ func (s *AuthService) Login(req *models.LoginRequest) (*models.LoginResponse, er
// 清除密码字段
user.Password = ""
return &models.LoginResponse{
AccessToken: tokenPair.AccessToken,
RefreshToken: tokenPair.RefreshToken,
TokenType: tokenPair.TokenType,
ExpiresIn: tokenPair.ExpiresIn,
User: user,
return &dto.LoginResponse{
Token: dto.TokenResponse{
AccessToken: tokenPair.AccessToken,
RefreshToken: tokenPair.RefreshToken,
TokenType: tokenPair.TokenType,
ExpiresIn: tokenPair.ExpiresIn,
},
User: *dto.ConvertToUserResponse(user),
}, nil
}
// Register 用户注册
func (s *AuthService) Register(req *models.CreateUserRequest) (*models.User, error) {
func (s *AuthService) Register(req *dto.CreateUserRequest) (*entity.User, error) {
// 检查用户名是否已存在
existingUser, err := s.userRepo.GetByUsername(req.Username)
if err != nil {
@ -106,7 +101,7 @@ func (s *AuthService) Register(req *models.CreateUserRequest) (*models.User, err
}
// 创建用户
user := &models.User{
user := &entity.User{
Username: req.Username,
Email: req.Email,
Password: string(hashedPassword),
@ -117,7 +112,7 @@ func (s *AuthService) Register(req *models.CreateUserRequest) (*models.User, err
// 如果没有指定角色,默认为普通用户
if user.Role == "" {
user.Role = models.RoleUser
user.Role = entity.UserRoleUser
}
if err := s.userRepo.Create(user); err != nil {
@ -131,7 +126,7 @@ func (s *AuthService) Register(req *models.CreateUserRequest) (*models.User, err
}
// RefreshToken 刷新令牌
func (s *AuthService) RefreshToken(req *models.RefreshTokenRequest) (*models.LoginResponse, error) {
func (s *AuthService) RefreshToken(req *dto.RefreshTokenRequest) (*dto.LoginResponse, error) {
// 验证刷新令牌
claims, err := s.jwtService.ValidateToken(req.RefreshToken)
if err != nil {
@ -154,7 +149,7 @@ func (s *AuthService) RefreshToken(req *models.RefreshTokenRequest) (*models.Log
}
// 生成新的令牌对
tokenPair, err := s.jwtService.GenerateTokenPair(user.ID, user.Username, user.Role)
tokenPair, err := s.jwtService.GenerateTokenPair(user.ID, user.Username, string(user.Role))
if err != nil {
return nil, fmt.Errorf("failed to generate tokens: %w", err)
}
@ -162,17 +157,19 @@ func (s *AuthService) RefreshToken(req *models.RefreshTokenRequest) (*models.Log
// 清除密码字段
user.Password = ""
return &models.LoginResponse{
AccessToken: tokenPair.AccessToken,
RefreshToken: tokenPair.RefreshToken,
TokenType: tokenPair.TokenType,
ExpiresIn: tokenPair.ExpiresIn,
User: user,
return &dto.LoginResponse{
Token: dto.TokenResponse{
AccessToken: tokenPair.AccessToken,
RefreshToken: tokenPair.RefreshToken,
TokenType: tokenPair.TokenType,
ExpiresIn: tokenPair.ExpiresIn,
},
User: *dto.ConvertToUserResponse(user),
}, nil
}
// GetUserByID 根据ID获取用户
func (s *AuthService) GetUserByID(id uint) (*models.User, error) {
func (s *AuthService) GetUserByID(id uint) (*entity.User, error) {
user, err := s.userRepo.GetByID(id)
if err != nil {
return nil, fmt.Errorf("failed to get user: %w", err)
@ -189,7 +186,7 @@ func (s *AuthService) GetUserByID(id uint) (*models.User, error) {
}
// UpdatePassword 更新密码
func (s *AuthService) UpdatePassword(userID uint, req *models.UpdatePasswordRequest) error {
func (s *AuthService) UpdatePassword(userID uint, req *dto.ChangePasswordRequest) error {
// 获取用户信息
user, err := s.userRepo.GetByID(userID)
if err != nil {
@ -221,11 +218,11 @@ func (s *AuthService) UpdatePassword(userID uint, req *models.UpdatePasswordRequ
}
// CheckPermission 检查权限
func (s *AuthService) CheckPermission(userRole string, requiredRole string) bool {
roleLevel := map[string]int{
models.RoleUser: 1,
models.RoleEditor: 2,
models.RoleAdmin: 3,
func (s *AuthService) CheckPermission(userRole entity.UserRole, requiredRole entity.UserRole) bool {
roleLevel := map[entity.UserRole]int{
entity.UserRoleUser: 1,
entity.UserRolePhotographer: 2,
entity.UserRoleAdmin: 3,
}
userLevel, exists := roleLevel[userRole]
@ -242,11 +239,11 @@ func (s *AuthService) CheckPermission(userRole string, requiredRole string) bool
}
// IsAdmin 检查是否为管理员
func (s *AuthService) IsAdmin(userRole string) bool {
return userRole == models.RoleAdmin
func (s *AuthService) IsAdmin(userRole entity.UserRole) bool {
return userRole == entity.UserRoleAdmin
}
// IsEditor 检查是否为编辑者或以上
func (s *AuthService) IsEditor(userRole string) bool {
return userRole == models.RoleEditor || userRole == models.RoleAdmin
// IsPhotographer 检查是否为摄影师或以上
func (s *AuthService) IsPhotographer(userRole entity.UserRole) bool {
return userRole == entity.UserRolePhotographer || userRole == entity.UserRoleAdmin
}

View File

@ -3,41 +3,29 @@ package service
import (
"context"
"errors"
"fmt"
"strings"
"photography-backend/internal/models"
"photography-backend/internal/utils"
"photography-backend/internal/model/entity"
"photography-backend/internal/repository/interfaces"
"go.uber.org/zap"
"gorm.io/gorm"
)
type CategoryService struct {
db *gorm.DB
logger *zap.Logger
categoryRepo interfaces.CategoryRepository
logger *zap.Logger
}
func NewCategoryService(db *gorm.DB, logger *zap.Logger) *CategoryService {
func NewCategoryService(categoryRepo interfaces.CategoryRepository, logger *zap.Logger) *CategoryService {
return &CategoryService{
db: db,
logger: logger,
categoryRepo: categoryRepo,
logger: logger,
}
}
// GetCategories 获取分类列表
func (s *CategoryService) GetCategories(ctx context.Context, parentID *uint) ([]models.Category, error) {
var categories []models.Category
query := s.db.WithContext(ctx).Order("sort_order ASC, created_at ASC")
if parentID != nil {
query = query.Where("parent_id = ?", *parentID)
} else {
query = query.Where("parent_id IS NULL")
}
if err := query.Find(&categories).Error; err != nil {
func (s *CategoryService) GetCategories(ctx context.Context, parentID *uint) ([]*entity.Category, error) {
categories, err := s.categoryRepo.List(ctx, parentID)
if err != nil {
s.logger.Error("Failed to get categories", zap.Error(err))
return nil, err
}
@ -46,70 +34,59 @@ func (s *CategoryService) GetCategories(ctx context.Context, parentID *uint) ([]
}
// GetCategoryTree 获取分类树
func (s *CategoryService) GetCategoryTree(ctx context.Context) ([]models.CategoryTree, error) {
var categories []models.Category
if err := s.db.WithContext(ctx).
Order("sort_order ASC, created_at ASC").
Find(&categories).Error; err != nil {
s.logger.Error("Failed to get all categories", zap.Error(err))
func (s *CategoryService) GetCategoryTree(ctx context.Context) ([]*entity.CategoryTree, error) {
tree, err := s.categoryRepo.GetTree(ctx)
if err != nil {
s.logger.Error("Failed to get category tree", zap.Error(err))
return nil, err
}
// 构建树形结构
tree := s.buildCategoryTree(categories, nil)
return tree, nil
}
// GetCategoryByID 根据ID获取分类
func (s *CategoryService) GetCategoryByID(ctx context.Context, id uint) (*models.Category, error) {
var category models.Category
if err := s.db.WithContext(ctx).First(&category, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("category not found")
}
func (s *CategoryService) GetCategoryByID(ctx context.Context, id uint) (*entity.Category, error) {
category, err := s.categoryRepo.GetByID(ctx, id)
if err != nil {
s.logger.Error("Failed to get category by ID", zap.Error(err), zap.Uint("id", id))
return nil, err
}
return &category, nil
return category, nil
}
// GetCategoryBySlug 根据slug获取分类
func (s *CategoryService) GetCategoryBySlug(ctx context.Context, slug string) (*models.Category, error) {
var category models.Category
if err := s.db.WithContext(ctx).Where("slug = ?", slug).First(&category).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("category not found")
}
func (s *CategoryService) GetCategoryBySlug(ctx context.Context, slug string) (*entity.Category, error) {
category, err := s.categoryRepo.GetBySlug(ctx, slug)
if err != nil {
s.logger.Error("Failed to get category by slug", zap.Error(err), zap.String("slug", slug))
return nil, err
}
return &category, nil
return category, nil
}
// CreateCategory 创建分类
func (s *CategoryService) CreateCategory(ctx context.Context, req *models.CreateCategoryRequest) (*models.Category, error) {
func (s *CategoryService) CreateCategory(ctx context.Context, req *entity.CreateCategoryRequest) (*entity.Category, error) {
// 验证slug唯一性
if err := s.validateSlugUnique(ctx, req.Slug, 0); err != nil {
if err := s.categoryRepo.ValidateSlugUnique(ctx, req.Slug, 0); err != nil {
return nil, err
}
// 验证父分类存在性
if req.ParentID != nil {
var parentCategory models.Category
if err := s.db.WithContext(ctx).First(&parentCategory, *req.ParentID).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("parent category not found")
}
if err := s.categoryRepo.ValidateParentCategory(ctx, 0, *req.ParentID); err != nil {
return nil, err
}
}
// 获取排序顺序
sortOrder := s.getNextSortOrder(ctx, req.ParentID)
sortOrder, err := s.categoryRepo.GetNextSortOrder(ctx, req.ParentID)
if err != nil {
return nil, err
}
category := &models.Category{
category := &entity.Category{
Name: req.Name,
Slug: req.Slug,
Description: req.Description,
@ -118,7 +95,7 @@ func (s *CategoryService) CreateCategory(ctx context.Context, req *models.Create
IsActive: true,
}
if err := s.db.WithContext(ctx).Create(category).Error; err != nil {
if err := s.categoryRepo.Create(ctx, category); err != nil {
s.logger.Error("Failed to create category", zap.Error(err))
return nil, err
}
@ -128,101 +105,86 @@ func (s *CategoryService) CreateCategory(ctx context.Context, req *models.Create
}
// UpdateCategory 更新分类
func (s *CategoryService) UpdateCategory(ctx context.Context, id uint, req *models.UpdateCategoryRequest) (*models.Category, error) {
func (s *CategoryService) UpdateCategory(ctx context.Context, id uint, req *entity.UpdateCategoryRequest) (*entity.Category, error) {
// 检查分类是否存在
var category models.Category
if err := s.db.WithContext(ctx).First(&category, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("category not found")
}
category, err := s.categoryRepo.GetByID(ctx, id)
if err != nil {
s.logger.Error("Failed to get category", zap.Error(err), zap.Uint("id", id))
return nil, err
}
// 验证slug唯一性
if req.Slug != nil && *req.Slug != category.Slug {
if err := s.validateSlugUnique(ctx, *req.Slug, id); err != nil {
if err := s.categoryRepo.ValidateSlugUnique(ctx, *req.Slug, id); err != nil {
return nil, err
}
}
// 验证父分类(防止循环引用)
if req.ParentID != nil && *req.ParentID != category.ParentID {
if err := s.validateParentCategory(ctx, id, *req.ParentID); err != nil {
return nil, err
if req.ParentID != nil {
// 检查是否有变更
if (category.ParentID == nil && *req.ParentID != 0) || (category.ParentID != nil && *req.ParentID != *category.ParentID) {
if err := s.categoryRepo.ValidateParentCategory(ctx, id, *req.ParentID); err != nil {
return nil, err
}
}
}
// 构建更新数据
updates := map[string]interface{}{}
// 更新字段
if req.Name != nil {
updates["name"] = *req.Name
category.Name = *req.Name
}
if req.Slug != nil {
updates["slug"] = *req.Slug
category.Slug = *req.Slug
}
if req.Description != nil {
updates["description"] = *req.Description
category.Description = *req.Description
}
if req.ParentID != nil {
if *req.ParentID == 0 {
updates["parent_id"] = nil
category.ParentID = nil
} else {
updates["parent_id"] = *req.ParentID
category.ParentID = req.ParentID
}
}
if req.SortOrder != nil {
updates["sort_order"] = *req.SortOrder
category.SortOrder = *req.SortOrder
}
if req.IsActive != nil {
updates["is_active"] = *req.IsActive
category.IsActive = *req.IsActive
}
if len(updates) > 0 {
if err := s.db.WithContext(ctx).Model(&category).Updates(updates).Error; err != nil {
s.logger.Error("Failed to update category", zap.Error(err))
return nil, err
}
// 保存更新
if err := s.categoryRepo.Update(ctx, category); err != nil {
s.logger.Error("Failed to update category", zap.Error(err))
return nil, err
}
s.logger.Info("Category updated successfully", zap.Uint("id", id))
return &category, nil
return category, nil
}
// DeleteCategory 删除分类
func (s *CategoryService) DeleteCategory(ctx context.Context, id uint) error {
// 检查分类是否存在
var category models.Category
if err := s.db.WithContext(ctx).First(&category, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New("category not found")
}
_, err := s.categoryRepo.GetByID(ctx, id)
if err != nil {
s.logger.Error("Failed to get category", zap.Error(err), zap.Uint("id", id))
return err
}
// 检查是否有子分类
var childCount int64
if err := s.db.WithContext(ctx).Model(&models.Category{}).
Where("parent_id = ?", id).Count(&childCount).Error; err != nil {
children, err := s.categoryRepo.GetChildren(ctx, id)
if err != nil {
return err
}
if childCount > 0 {
if len(children) > 0 {
return errors.New("cannot delete category with subcategories")
}
// 检查是否有关联的照片
var photoCount int64
if err := s.db.WithContext(ctx).Table("photo_categories").
Where("category_id = ?", id).Count(&photoCount).Error; err != nil {
return err
}
if photoCount > 0 {
return errors.New("cannot delete category with associated photos")
}
// 删除分类
if err := s.db.WithContext(ctx).Delete(&category).Error; err != nil {
// 直接删除分类在Repository层检查照片关联
if err := s.categoryRepo.Delete(ctx, id); err != nil {
s.logger.Error("Failed to delete category", zap.Error(err))
return err
}
@ -233,42 +195,13 @@ func (s *CategoryService) DeleteCategory(ctx context.Context, id uint) error {
// ReorderCategories 重新排序分类
func (s *CategoryService) ReorderCategories(ctx context.Context, parentID *uint, categoryIDs []uint) error {
// 验证所有分类都属于同一父分类
var categories []models.Category
query := s.db.WithContext(ctx).Where("id IN ?", categoryIDs)
if parentID != nil {
query = query.Where("parent_id = ?", *parentID)
} else {
query = query.Where("parent_id IS NULL")
if len(categoryIDs) == 0 {
return nil
}
if err := query.Find(&categories).Error; err != nil {
return err
}
if len(categories) != len(categoryIDs) {
return errors.New("invalid category IDs")
}
// 开始事务
tx := s.db.WithContext(ctx).Begin()
if tx.Error != nil {
return tx.Error
}
defer tx.Rollback()
// 更新排序
for i, categoryID := range categoryIDs {
if err := tx.Model(&models.Category{}).
Where("id = ?", categoryID).
Update("sort_order", i+1).Error; err != nil {
return err
}
}
// 提交事务
if err := tx.Commit().Error; err != nil {
// 重新排序分类
if err := s.categoryRepo.Reorder(ctx, parentID, categoryIDs); err != nil {
s.logger.Error("Failed to reorder categories", zap.Error(err))
return err
}
@ -277,171 +210,23 @@ func (s *CategoryService) ReorderCategories(ctx context.Context, parentID *uint,
}
// GetCategoryStats 获取分类统计信息
func (s *CategoryService) GetCategoryStats(ctx context.Context) (*models.CategoryStats, error) {
var stats models.CategoryStats
// 总分类数
if err := s.db.WithContext(ctx).Model(&models.Category{}).Count(&stats.Total).Error; err != nil {
func (s *CategoryService) GetCategoryStats(ctx context.Context) (*entity.CategoryStats, error) {
stats, err := s.categoryRepo.GetStats(ctx)
if err != nil {
s.logger.Error("Failed to get category stats", zap.Error(err))
return nil, err
}
// 活跃分类数
if err := s.db.WithContext(ctx).Model(&models.Category{}).
Where("is_active = ?", true).Count(&stats.Active).Error; err != nil {
return nil, err
}
// 顶级分类数
if err := s.db.WithContext(ctx).Model(&models.Category{}).
Where("parent_id IS NULL").Count(&stats.TopLevel).Error; err != nil {
return nil, err
}
// 各分类照片数量
var categoryPhotoStats []struct {
CategoryID uint `json:"category_id"`
Name string `json:"name"`
PhotoCount int64 `json:"photo_count"`
}
if err := s.db.WithContext(ctx).
Table("categories").
Select("categories.id as category_id, categories.name, COUNT(photo_categories.photo_id) as photo_count").
Joins("LEFT JOIN photo_categories ON categories.id = photo_categories.category_id").
Group("categories.id, categories.name").
Order("photo_count DESC").
Limit(10).
Find(&categoryPhotoStats).Error; err != nil {
return nil, err
}
stats.PhotoCounts = make(map[string]int64)
for _, stat := range categoryPhotoStats {
stats.PhotoCounts[stat.Name] = stat.PhotoCount
}
return &stats, nil
return stats, nil
}
// validateSlugUnique 验证slug唯一性
func (s *CategoryService) validateSlugUnique(ctx context.Context, slug string, excludeID uint) error {
var count int64
query := s.db.WithContext(ctx).Model(&models.Category{}).Where("slug = ?", slug)
if excludeID > 0 {
query = query.Where("id != ?", excludeID)
}
if err := query.Count(&count).Error; err != nil {
return err
}
if count > 0 {
return errors.New("slug already exists")
}
return nil
}
// validateParentCategory 验证父分类(防止循环引用)
func (s *CategoryService) validateParentCategory(ctx context.Context, categoryID, parentID uint) error {
if categoryID == parentID {
return errors.New("category cannot be its own parent")
}
// 检查是否会形成循环引用
current := parentID
for current != 0 {
var parent models.Category
if err := s.db.WithContext(ctx).First(&parent, current).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New("parent category not found")
}
return err
}
if parent.ParentID == nil {
break
}
if *parent.ParentID == categoryID {
return errors.New("circular reference detected")
}
current = *parent.ParentID
}
return nil
}
// getNextSortOrder 获取下一个排序顺序
func (s *CategoryService) getNextSortOrder(ctx context.Context, parentID *uint) int {
var maxOrder int
query := s.db.WithContext(ctx).Model(&models.Category{}).Select("COALESCE(MAX(sort_order), 0)")
if parentID != nil {
query = query.Where("parent_id = ?", *parentID)
} else {
query = query.Where("parent_id IS NULL")
}
query.Row().Scan(&maxOrder)
return maxOrder + 1
}
// buildCategoryTree 构建分类树
func (s *CategoryService) buildCategoryTree(categories []models.Category, parentID *uint) []models.CategoryTree {
var tree []models.CategoryTree
for _, category := range categories {
// 检查是否匹配父分类
if (parentID == nil && category.ParentID == nil) ||
(parentID != nil && category.ParentID != nil && *category.ParentID == *parentID) {
node := models.CategoryTree{
ID: category.ID,
Name: category.Name,
Slug: category.Slug,
Description: category.Description,
ParentID: category.ParentID,
SortOrder: category.SortOrder,
IsActive: category.IsActive,
PhotoCount: category.PhotoCount,
CreatedAt: category.CreatedAt,
UpdatedAt: category.UpdatedAt,
}
// 递归构建子分类
node.Children = s.buildCategoryTree(categories, &category.ID)
tree = append(tree, node)
}
}
return tree
}
// GenerateSlug 生成slug
// GenerateSlug 生成唯一slug
func (s *CategoryService) GenerateSlug(ctx context.Context, name string) (string, error) {
baseSlug := utils.GenerateSlug(name)
slug := baseSlug
counter := 1
for {
var count int64
if err := s.db.WithContext(ctx).Model(&models.Category{}).
Where("slug = ?", slug).Count(&count).Error; err != nil {
return "", err
}
if count == 0 {
break
}
slug = fmt.Sprintf("%s-%d", baseSlug, counter)
counter++
slug, err := s.categoryRepo.GenerateUniqueSlug(ctx, name)
if err != nil {
s.logger.Error("Failed to generate unique slug", zap.Error(err))
return "", err
}
return slug, nil

View File

@ -6,29 +6,28 @@ import (
"fmt"
"mime/multipart"
"path/filepath"
"strconv"
"strings"
"time"
"photography-backend/internal/config"
"photography-backend/internal/models"
"photography-backend/internal/model/entity"
"photography-backend/internal/repository/interfaces"
"photography-backend/internal/service/storage"
"photography-backend/internal/utils"
"go.uber.org/zap"
"gorm.io/gorm"
)
type PhotoService struct {
db *gorm.DB
photoRepo interfaces.PhotoRepository
config *config.Config
logger *zap.Logger
storageService *storage.StorageService
}
func NewPhotoService(db *gorm.DB, config *config.Config, logger *zap.Logger, storageService *storage.StorageService) *PhotoService {
func NewPhotoService(photoRepo interfaces.PhotoRepository, config *config.Config, logger *zap.Logger, storageService *storage.StorageService) *PhotoService {
return &PhotoService{
db: db,
photoRepo: photoRepo,
config: config,
logger: logger,
storageService: storageService,
@ -51,7 +50,7 @@ type PhotoListParams struct {
// PhotoListResponse 照片列表响应
type PhotoListResponse struct {
Photos []models.Photo `json:"photos"`
Photos []entity.Photo `json:"photos"`
Total int64 `json:"total"`
Page int `json:"page"`
Limit int `json:"limit"`
@ -130,14 +129,14 @@ func (s *PhotoService) GetPhotos(ctx context.Context, params PhotoListParams) (*
// 计算总数
var total int64
countQuery := query
if err := countQuery.Model(&models.Photo{}).Count(&total).Error; err != nil {
if err := countQuery.Model(&entity.Photo{}).Count(&total).Error; err != nil {
s.logger.Error("Failed to count photos", zap.Error(err))
return nil, err
}
// 分页查询
offset := (params.Page - 1) * params.Limit
var photos []models.Photo
var photos []entity.Photo
if err := query.
Order(fmt.Sprintf("%s %s", sortBy, sortOrder)).
Offset(offset).
@ -160,8 +159,8 @@ func (s *PhotoService) GetPhotos(ctx context.Context, params PhotoListParams) (*
}
// GetPhotoByID 根据ID获取照片
func (s *PhotoService) GetPhotoByID(ctx context.Context, id uint) (*models.Photo, error) {
var photo models.Photo
func (s *PhotoService) GetPhotoByID(ctx context.Context, id uint) (*entity.Photo, error) {
var photo entity.Photo
if err := s.db.WithContext(ctx).
Preload("Categories").
Preload("Tags").
@ -178,17 +177,17 @@ func (s *PhotoService) GetPhotoByID(ctx context.Context, id uint) (*models.Photo
}
// CreatePhoto 创建照片
func (s *PhotoService) CreatePhoto(ctx context.Context, req *models.CreatePhotoRequest) (*models.Photo, error) {
func (s *PhotoService) CreatePhoto(ctx context.Context, req *entity.CreatePhotoRequest) (*entity.Photo, error) {
// 生成唯一的文件名
uniqueFilename := utils.GenerateUniqueFilename(req.OriginalFilename)
photo := &models.Photo{
photo := &entity.Photo{
Title: req.Title,
Description: req.Description,
OriginalFilename: req.OriginalFilename,
UniqueFilename: uniqueFilename,
FileSize: req.FileSize,
Status: req.Status,
Status: entity.PhotoStatus(req.Status),
Camera: req.Camera,
Lens: req.Lens,
ISO: req.ISO,
@ -213,7 +212,7 @@ func (s *PhotoService) CreatePhoto(ctx context.Context, req *models.CreatePhotoR
// 关联分类
if len(req.CategoryIDs) > 0 {
var categories []models.Category
var categories []entity.Category
if err := tx.Where("id IN ?", req.CategoryIDs).Find(&categories).Error; err != nil {
s.logger.Error("Failed to find categories", zap.Error(err))
return nil, err
@ -226,7 +225,7 @@ func (s *PhotoService) CreatePhoto(ctx context.Context, req *models.CreatePhotoR
// 关联标签
if len(req.TagIDs) > 0 {
var tags []models.Tag
var tags []entity.Tag
if err := tx.Where("id IN ?", req.TagIDs).Find(&tags).Error; err != nil {
s.logger.Error("Failed to find tags", zap.Error(err))
return nil, err
@ -258,9 +257,9 @@ func (s *PhotoService) CreatePhoto(ctx context.Context, req *models.CreatePhotoR
}
// UpdatePhoto 更新照片
func (s *PhotoService) UpdatePhoto(ctx context.Context, id uint, req *models.UpdatePhotoRequest) (*models.Photo, error) {
func (s *PhotoService) UpdatePhoto(ctx context.Context, id uint, req *entity.UpdatePhotoRequest) (*entity.Photo, error) {
// 检查照片是否存在
var photo models.Photo
var photo entity.Photo
if err := s.db.WithContext(ctx).First(&photo, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("photo not found")
@ -317,7 +316,7 @@ func (s *PhotoService) UpdatePhoto(ctx context.Context, id uint, req *models.Upd
// 更新分类关联
if req.CategoryIDs != nil {
var categories []models.Category
var categories []entity.Category
if len(*req.CategoryIDs) > 0 {
if err := tx.Where("id IN ?", *req.CategoryIDs).Find(&categories).Error; err != nil {
s.logger.Error("Failed to find categories", zap.Error(err))
@ -332,7 +331,7 @@ func (s *PhotoService) UpdatePhoto(ctx context.Context, id uint, req *models.Upd
// 更新标签关联
if req.TagIDs != nil {
var tags []models.Tag
var tags []entity.Tag
if len(*req.TagIDs) > 0 {
if err := tx.Where("id IN ?", *req.TagIDs).Find(&tags).Error; err != nil {
s.logger.Error("Failed to find tags", zap.Error(err))
@ -368,7 +367,7 @@ func (s *PhotoService) UpdatePhoto(ctx context.Context, id uint, req *models.Upd
// DeletePhoto 删除照片
func (s *PhotoService) DeletePhoto(ctx context.Context, id uint) error {
// 检查照片是否存在
var photo models.Photo
var photo entity.Photo
if err := s.db.WithContext(ctx).First(&photo, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New("photo not found")
@ -384,7 +383,7 @@ func (s *PhotoService) DeletePhoto(ctx context.Context, id uint) error {
defer tx.Rollback()
// 删除关联的格式文件
if err := tx.Where("photo_id = ?", id).Delete(&models.PhotoFormat{}).Error; err != nil {
if err := tx.Where("photo_id = ?", id).Delete(&entity.PhotoFormat{}).Error; err != nil {
s.logger.Error("Failed to delete photo formats", zap.Error(err))
return err
}
@ -414,7 +413,7 @@ func (s *PhotoService) DeletePhoto(ctx context.Context, id uint) error {
// 异步删除文件
go func() {
if err := s.storageService.DeletePhoto(photo.UniqueFilename); err != nil {
if err := (*s.storageService).DeletePhoto(photo.UniqueFilename); err != nil {
s.logger.Error("Failed to delete photo files", zap.Error(err), zap.String("filename", photo.UniqueFilename))
}
}()
@ -424,7 +423,7 @@ func (s *PhotoService) DeletePhoto(ctx context.Context, id uint) error {
}
// UploadPhoto 上传照片
func (s *PhotoService) UploadPhoto(ctx context.Context, file multipart.File, header *multipart.FileHeader, req *models.CreatePhotoRequest) (*models.Photo, error) {
func (s *PhotoService) UploadPhoto(ctx context.Context, file multipart.File, header *multipart.FileHeader, req *entity.CreatePhotoRequest) (*entity.Photo, error) {
// 验证文件类型
if !s.isValidImageFile(header.Filename) {
return nil, errors.New("invalid file type")
@ -439,7 +438,7 @@ func (s *PhotoService) UploadPhoto(ctx context.Context, file multipart.File, hea
uniqueFilename := utils.GenerateUniqueFilename(header.Filename)
// 上传文件到存储服务
uploadedFile, err := s.storageService.UploadPhoto(ctx, file, uniqueFilename)
uploadedFile, err := (*s.storageService).UploadPhoto(ctx, file, uniqueFilename)
if err != nil {
s.logger.Error("Failed to upload photo", zap.Error(err))
return nil, err
@ -453,7 +452,7 @@ func (s *PhotoService) UploadPhoto(ctx context.Context, file multipart.File, hea
if err != nil {
// 如果创建记录失败,删除已上传的文件
go func() {
if err := s.storageService.DeletePhoto(uniqueFilename); err != nil {
if err := (*s.storageService).DeletePhoto(uniqueFilename); err != nil {
s.logger.Error("Failed to cleanup uploaded file", zap.Error(err))
}
}()
@ -469,7 +468,7 @@ func (s *PhotoService) UploadPhoto(ctx context.Context, file multipart.File, hea
}
// BatchUpdatePhotos 批量更新照片
func (s *PhotoService) BatchUpdatePhotos(ctx context.Context, ids []uint, req *models.BatchUpdatePhotosRequest) error {
func (s *PhotoService) BatchUpdatePhotos(ctx context.Context, ids []uint, req *entity.BatchUpdatePhotosRequest) error {
if len(ids) == 0 {
return errors.New("no photos to update")
}
@ -489,7 +488,7 @@ func (s *PhotoService) BatchUpdatePhotos(ctx context.Context, ids []uint, req *m
// 基础字段更新
if len(updates) > 0 {
if err := tx.Model(&models.Photo{}).Where("id IN ?", ids).Updates(updates).Error; err != nil {
if err := tx.Model(&entity.Photo{}).Where("id IN ?", ids).Updates(updates).Error; err != nil {
s.logger.Error("Failed to batch update photos", zap.Error(err))
return err
}
@ -550,7 +549,7 @@ func (s *PhotoService) BatchDeletePhotos(ctx context.Context, ids []uint) error
}
// 获取要删除的照片信息
var photos []models.Photo
var photos []entity.Photo
if err := s.db.WithContext(ctx).Where("id IN ?", ids).Find(&photos).Error; err != nil {
return err
}
@ -563,7 +562,7 @@ func (s *PhotoService) BatchDeletePhotos(ctx context.Context, ids []uint) error
defer tx.Rollback()
// 删除关联的格式文件
if err := tx.Where("photo_id IN ?", ids).Delete(&models.PhotoFormat{}).Error; err != nil {
if err := tx.Where("photo_id IN ?", ids).Delete(&entity.PhotoFormat{}).Error; err != nil {
return err
}
@ -577,7 +576,7 @@ func (s *PhotoService) BatchDeletePhotos(ctx context.Context, ids []uint) error
}
// 删除照片记录
if err := tx.Where("id IN ?", ids).Delete(&models.Photo{}).Error; err != nil {
if err := tx.Where("id IN ?", ids).Delete(&entity.Photo{}).Error; err != nil {
return err
}
@ -589,7 +588,7 @@ func (s *PhotoService) BatchDeletePhotos(ctx context.Context, ids []uint) error
// 异步删除文件
go func() {
for _, photo := range photos {
if err := s.storageService.DeletePhoto(photo.UniqueFilename); err != nil {
if err := (*s.storageService).DeletePhoto(photo.UniqueFilename); err != nil {
s.logger.Error("Failed to delete photo files", zap.Error(err), zap.String("filename", photo.UniqueFilename))
}
}
@ -600,11 +599,11 @@ func (s *PhotoService) BatchDeletePhotos(ctx context.Context, ids []uint) error
}
// GetPhotoStats 获取照片统计信息
func (s *PhotoService) GetPhotoStats(ctx context.Context) (*models.PhotoStats, error) {
var stats models.PhotoStats
func (s *PhotoService) GetPhotoStats(ctx context.Context) (*entity.PhotoStats, error) {
var stats entity.PhotoStats
// 总数统计
if err := s.db.WithContext(ctx).Model(&models.Photo{}).Count(&stats.Total).Error; err != nil {
if err := s.db.WithContext(ctx).Model(&entity.Photo{}).Count(&stats.Total).Error; err != nil {
return nil, err
}
@ -613,7 +612,7 @@ func (s *PhotoService) GetPhotoStats(ctx context.Context) (*models.PhotoStats, e
Status string `json:"status"`
Count int64 `json:"count"`
}
if err := s.db.WithContext(ctx).Model(&models.Photo{}).
if err := s.db.WithContext(ctx).Model(&entity.Photo{}).
Select("status, COUNT(*) as count").
Group("status").
Find(&statusStats).Error; err != nil {
@ -627,7 +626,7 @@ func (s *PhotoService) GetPhotoStats(ctx context.Context) (*models.PhotoStats, e
// 本月新增
startOfMonth := time.Now().AddDate(0, 0, -time.Now().Day()+1)
if err := s.db.WithContext(ctx).Model(&models.Photo{}).
if err := s.db.WithContext(ctx).Model(&entity.Photo{}).
Where("created_at >= ?", startOfMonth).
Count(&stats.ThisMonth).Error; err != nil {
return nil, err
@ -635,7 +634,7 @@ func (s *PhotoService) GetPhotoStats(ctx context.Context) (*models.PhotoStats, e
// 今日新增
startOfDay := time.Now().Truncate(24 * time.Hour)
if err := s.db.WithContext(ctx).Model(&models.Photo{}).
if err := s.db.WithContext(ctx).Model(&entity.Photo{}).
Where("created_at >= ?", startOfDay).
Count(&stats.Today).Error; err != nil {
return nil, err
@ -643,7 +642,7 @@ func (s *PhotoService) GetPhotoStats(ctx context.Context) (*models.PhotoStats, e
// 总存储大小
var totalSize sql.NullInt64
if err := s.db.WithContext(ctx).Model(&models.Photo{}).
if err := s.db.WithContext(ctx).Model(&entity.Photo{}).
Select("SUM(file_size)").
Row().Scan(&totalSize); err != nil {
return nil, err
@ -663,7 +662,7 @@ func (s *PhotoService) isValidImageFile(filename string) bool {
}
// processPhotoFormats 处理照片格式转换
func (s *PhotoService) processPhotoFormats(ctx context.Context, photo *models.Photo, uploadedFile *storage.UploadedFile) {
func (s *PhotoService) processPhotoFormats(ctx context.Context, photo *entity.Photo, uploadedFile *storage.UploadedFile) {
// 这里将实现图片格式转换逻辑
// 生成不同尺寸和格式的图片
// 更新 photo_formats 表

View File

@ -6,7 +6,7 @@ import (
"fmt"
"strings"
"photography-backend/internal/models"
"photography-backend/internal/model/entity"
"photography-backend/internal/utils"
"go.uber.org/zap"
@ -37,7 +37,7 @@ type TagListParams struct {
// TagListResponse 标签列表响应
type TagListResponse struct {
Tags []models.Tag `json:"tags"`
Tags []entity.Tag `json:"tags"`
Total int64 `json:"total"`
Page int `json:"page"`
Limit int `json:"limit"`
@ -87,14 +87,14 @@ func (s *TagService) GetTags(ctx context.Context, params TagListParams) (*TagLis
// 计算总数
var total int64
countQuery := query
if err := countQuery.Model(&models.Tag{}).Count(&total).Error; err != nil {
if err := countQuery.Model(&entity.Tag{}).Count(&total).Error; err != nil {
s.logger.Error("Failed to count tags", zap.Error(err))
return nil, err
}
// 分页查询
offset := (params.Page - 1) * params.Limit
var tags []models.Tag
var tags []entity.Tag
if err := query.
Order(fmt.Sprintf("%s %s", sortBy, sortOrder)).
Offset(offset).
@ -117,8 +117,8 @@ func (s *TagService) GetTags(ctx context.Context, params TagListParams) (*TagLis
}
// GetAllTags 获取所有活跃标签
func (s *TagService) GetAllTags(ctx context.Context) ([]models.Tag, error) {
var tags []models.Tag
func (s *TagService) GetAllTags(ctx context.Context) ([]entity.Tag, error) {
var tags []entity.Tag
if err := s.db.WithContext(ctx).
Where("is_active = ?", true).
Order("name ASC").
@ -131,8 +131,8 @@ func (s *TagService) GetAllTags(ctx context.Context) ([]models.Tag, error) {
}
// GetTagByID 根据ID获取标签
func (s *TagService) GetTagByID(ctx context.Context, id uint) (*models.Tag, error) {
var tag models.Tag
func (s *TagService) GetTagByID(ctx context.Context, id uint) (*entity.Tag, error) {
var tag entity.Tag
if err := s.db.WithContext(ctx).First(&tag, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("tag not found")
@ -145,8 +145,8 @@ func (s *TagService) GetTagByID(ctx context.Context, id uint) (*models.Tag, erro
}
// GetTagBySlug 根据slug获取标签
func (s *TagService) GetTagBySlug(ctx context.Context, slug string) (*models.Tag, error) {
var tag models.Tag
func (s *TagService) GetTagBySlug(ctx context.Context, slug string) (*entity.Tag, error) {
var tag entity.Tag
if err := s.db.WithContext(ctx).Where("slug = ?", slug).First(&tag).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("tag not found")
@ -159,13 +159,13 @@ func (s *TagService) GetTagBySlug(ctx context.Context, slug string) (*models.Tag
}
// CreateTag 创建标签
func (s *TagService) CreateTag(ctx context.Context, req *models.CreateTagRequest) (*models.Tag, error) {
func (s *TagService) CreateTag(ctx context.Context, req *entity.CreateTagRequest) (*entity.Tag, error) {
// 验证slug唯一性
if err := s.validateSlugUnique(ctx, req.Slug, 0); err != nil {
return nil, err
}
tag := &models.Tag{
tag := &entity.Tag{
Name: req.Name,
Slug: req.Slug,
Description: req.Description,
@ -183,9 +183,9 @@ func (s *TagService) CreateTag(ctx context.Context, req *models.CreateTagRequest
}
// UpdateTag 更新标签
func (s *TagService) UpdateTag(ctx context.Context, id uint, req *models.UpdateTagRequest) (*models.Tag, error) {
func (s *TagService) UpdateTag(ctx context.Context, id uint, req *entity.UpdateTagRequest) (*entity.Tag, error) {
// 检查标签是否存在
var tag models.Tag
var tag entity.Tag
if err := s.db.WithContext(ctx).First(&tag, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("tag not found")
@ -232,7 +232,7 @@ func (s *TagService) UpdateTag(ctx context.Context, id uint, req *models.UpdateT
// DeleteTag 删除标签
func (s *TagService) DeleteTag(ctx context.Context, id uint) error {
// 检查标签是否存在
var tag models.Tag
var tag entity.Tag
if err := s.db.WithContext(ctx).First(&tag, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New("tag not found")
@ -279,7 +279,7 @@ func (s *TagService) BatchDeleteTags(ctx context.Context, ids []uint) error {
}
// 删除标签
if err := s.db.WithContext(ctx).Where("id IN ?", ids).Delete(&models.Tag{}).Error; err != nil {
if err := s.db.WithContext(ctx).Where("id IN ?", ids).Delete(&entity.Tag{}).Error; err != nil {
s.logger.Error("Failed to batch delete tags", zap.Error(err))
return err
}
@ -289,12 +289,12 @@ func (s *TagService) BatchDeleteTags(ctx context.Context, ids []uint) error {
}
// GetPopularTags 获取热门标签
func (s *TagService) GetPopularTags(ctx context.Context, limit int) ([]models.TagWithCount, error) {
func (s *TagService) GetPopularTags(ctx context.Context, limit int) ([]entity.TagWithCount, error) {
if limit <= 0 {
limit = 10
}
var tags []models.TagWithCount
var tags []entity.TagWithCount
if err := s.db.WithContext(ctx).
Table("tags").
Select("tags.*, COUNT(photo_tags.photo_id) as photo_count").
@ -312,8 +312,8 @@ func (s *TagService) GetPopularTags(ctx context.Context, limit int) ([]models.Ta
}
// GetTagCloud 获取标签云数据
func (s *TagService) GetTagCloud(ctx context.Context) ([]models.TagCloudItem, error) {
var items []models.TagCloudItem
func (s *TagService) GetTagCloud(ctx context.Context) ([]entity.TagCloudItem, error) {
var items []entity.TagCloudItem
if err := s.db.WithContext(ctx).
Table("tags").
Select("tags.name, tags.slug, tags.color, COUNT(photo_tags.photo_id) as count").
@ -331,16 +331,16 @@ func (s *TagService) GetTagCloud(ctx context.Context) ([]models.TagCloudItem, er
}
// GetTagStats 获取标签统计信息
func (s *TagService) GetTagStats(ctx context.Context) (*models.TagStats, error) {
var stats models.TagStats
func (s *TagService) GetTagStats(ctx context.Context) (*entity.TagStats, error) {
var stats entity.TagStats
// 总标签数
if err := s.db.WithContext(ctx).Model(&models.Tag{}).Count(&stats.Total).Error; err != nil {
if err := s.db.WithContext(ctx).Model(&entity.Tag{}).Count(&stats.Total).Error; err != nil {
return nil, err
}
// 活跃标签数
if err := s.db.WithContext(ctx).Model(&models.Tag{}).
if err := s.db.WithContext(ctx).Model(&entity.Tag{}).
Where("is_active = ?", true).Count(&stats.Active).Error; err != nil {
return nil, err
}
@ -375,12 +375,12 @@ func (s *TagService) GetTagStats(ctx context.Context) (*models.TagStats, error)
}
// SearchTags 搜索标签
func (s *TagService) SearchTags(ctx context.Context, query string, limit int) ([]models.Tag, error) {
func (s *TagService) SearchTags(ctx context.Context, query string, limit int) ([]entity.Tag, error) {
if limit <= 0 {
limit = 10
}
var tags []models.Tag
var tags []entity.Tag
searchPattern := "%" + query + "%"
if err := s.db.WithContext(ctx).
@ -396,8 +396,8 @@ func (s *TagService) SearchTags(ctx context.Context, query string, limit int) ([
}
// CreateTagsFromNames 从名称列表创建标签
func (s *TagService) CreateTagsFromNames(ctx context.Context, names []string) ([]models.Tag, error) {
var tags []models.Tag
func (s *TagService) CreateTagsFromNames(ctx context.Context, names []string) ([]entity.Tag, error) {
var tags []entity.Tag
for _, name := range names {
name = strings.TrimSpace(name)
@ -413,14 +413,14 @@ func (s *TagService) CreateTagsFromNames(ctx context.Context, names []string) ([
}
// 检查标签是否已存在
var existingTag models.Tag
var existingTag entity.Tag
if err := s.db.WithContext(ctx).Where("slug = ?", slug).First(&existingTag).Error; err == nil {
tags = append(tags, existingTag)
continue
}
// 创建新标签
tag := models.Tag{
tag := entity.Tag{
Name: name,
Slug: slug,
IsActive: true,
@ -440,7 +440,7 @@ func (s *TagService) CreateTagsFromNames(ctx context.Context, names []string) ([
// validateSlugUnique 验证slug唯一性
func (s *TagService) validateSlugUnique(ctx context.Context, slug string, excludeID uint) error {
var count int64
query := s.db.WithContext(ctx).Model(&models.Tag{}).Where("slug = ?", slug)
query := s.db.WithContext(ctx).Model(&entity.Tag{}).Where("slug = ?", slug)
if excludeID > 0 {
query = query.Where("id != ?", excludeID)
@ -465,7 +465,7 @@ func (s *TagService) GenerateSlug(ctx context.Context, name string) (string, err
counter := 1
for {
var count int64
if err := s.db.WithContext(ctx).Model(&models.Tag{}).
if err := s.db.WithContext(ctx).Model(&entity.Tag{}).
Where("slug = ?", slug).Count(&count).Error; err != nil {
return "", err
}

View File

@ -3,10 +3,9 @@ package service
import (
"context"
"errors"
"fmt"
"time"
"photography-backend/internal/models"
"photography-backend/internal/utils"
"photography-backend/internal/model/entity"
"go.uber.org/zap"
"golang.org/x/crypto/bcrypt"
@ -36,7 +35,7 @@ type UserListParams struct {
// UserListResponse 用户列表响应
type UserListResponse struct {
Users []models.User `json:"users"`
Users []entity.User `json:"users"`
Total int64 `json:"total"`
Page int `json:"page"`
Limit int `json:"limit"`
@ -78,14 +77,14 @@ func (s *UserService) GetUsers(ctx context.Context, params UserListParams) (*Use
// 计算总数
var total int64
countQuery := query
if err := countQuery.Model(&models.User{}).Count(&total).Error; err != nil {
if err := countQuery.Model(&entity.User{}).Count(&total).Error; err != nil {
s.logger.Error("Failed to count users", zap.Error(err))
return nil, err
}
// 分页查询
offset := (params.Page - 1) * params.Limit
var users []models.User
var users []entity.User
if err := query.
Order("created_at DESC").
Offset(offset).
@ -108,8 +107,8 @@ func (s *UserService) GetUsers(ctx context.Context, params UserListParams) (*Use
}
// GetUserByID 根据ID获取用户
func (s *UserService) GetUserByID(ctx context.Context, id uint) (*models.User, error) {
var user models.User
func (s *UserService) GetUserByID(ctx context.Context, id uint) (*entity.User, error) {
var user entity.User
if err := s.db.WithContext(ctx).First(&user, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("user not found")
@ -122,8 +121,8 @@ func (s *UserService) GetUserByID(ctx context.Context, id uint) (*models.User, e
}
// GetUserByUsername 根据用户名获取用户
func (s *UserService) GetUserByUsername(ctx context.Context, username string) (*models.User, error) {
var user models.User
func (s *UserService) GetUserByUsername(ctx context.Context, username string) (*entity.User, error) {
var user entity.User
if err := s.db.WithContext(ctx).Where("username = ?", username).First(&user).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("user not found")
@ -136,8 +135,8 @@ func (s *UserService) GetUserByUsername(ctx context.Context, username string) (*
}
// GetUserByEmail 根据邮箱获取用户
func (s *UserService) GetUserByEmail(ctx context.Context, email string) (*models.User, error) {
var user models.User
func (s *UserService) GetUserByEmail(ctx context.Context, email string) (*entity.User, error) {
var user entity.User
if err := s.db.WithContext(ctx).Where("email = ?", email).First(&user).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("user not found")
@ -150,9 +149,9 @@ func (s *UserService) GetUserByEmail(ctx context.Context, email string) (*models
}
// CreateUser 创建用户
func (s *UserService) CreateUser(ctx context.Context, req *models.CreateUserRequest) (*models.User, error) {
func (s *UserService) CreateUser(ctx context.Context, req *entity.CreateUserRequest) (*entity.User, error) {
// 验证用户名唯一性
var existingUser models.User
var existingUser entity.User
if err := s.db.WithContext(ctx).Where("username = ?", req.Username).First(&existingUser).Error; err == nil {
return nil, errors.New("username already exists")
}
@ -169,7 +168,7 @@ func (s *UserService) CreateUser(ctx context.Context, req *models.CreateUserRequ
return nil, err
}
user := &models.User{
user := &entity.User{
Username: req.Username,
Email: req.Email,
Password: string(hashedPassword),
@ -187,9 +186,9 @@ func (s *UserService) CreateUser(ctx context.Context, req *models.CreateUserRequ
}
// UpdateUser 更新用户
func (s *UserService) UpdateUser(ctx context.Context, id uint, req *models.UpdateUserRequest) (*models.User, error) {
func (s *UserService) UpdateUser(ctx context.Context, id uint, req *entity.UpdateUserRequest) (*entity.User, error) {
// 检查用户是否存在
var user models.User
var user entity.User
if err := s.db.WithContext(ctx).First(&user, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("user not found")
@ -202,7 +201,7 @@ func (s *UserService) UpdateUser(ctx context.Context, id uint, req *models.Updat
if req.Username != nil {
// 验证用户名唯一性
var existingUser models.User
var existingUser entity.User
if err := s.db.WithContext(ctx).Where("username = ? AND id != ?", *req.Username, id).First(&existingUser).Error; err == nil {
return nil, errors.New("username already exists")
}
@ -211,7 +210,7 @@ func (s *UserService) UpdateUser(ctx context.Context, id uint, req *models.Updat
if req.Email != nil {
// 验证邮箱唯一性
var existingUser models.User
var existingUser entity.User
if err := s.db.WithContext(ctx).Where("email = ? AND id != ?", *req.Email, id).First(&existingUser).Error; err == nil {
return nil, errors.New("email already exists")
}
@ -238,9 +237,9 @@ func (s *UserService) UpdateUser(ctx context.Context, id uint, req *models.Updat
}
// UpdateCurrentUser 更新当前用户信息
func (s *UserService) UpdateCurrentUser(ctx context.Context, id uint, req *models.UpdateCurrentUserRequest) (*models.User, error) {
func (s *UserService) UpdateCurrentUser(ctx context.Context, id uint, req *entity.UpdateCurrentUserRequest) (*entity.User, error) {
// 检查用户是否存在
var user models.User
var user entity.User
if err := s.db.WithContext(ctx).First(&user, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("user not found")
@ -253,7 +252,7 @@ func (s *UserService) UpdateCurrentUser(ctx context.Context, id uint, req *model
if req.Username != nil {
// 验证用户名唯一性
var existingUser models.User
var existingUser entity.User
if err := s.db.WithContext(ctx).Where("username = ? AND id != ?", *req.Username, id).First(&existingUser).Error; err == nil {
return nil, errors.New("username already exists")
}
@ -262,7 +261,7 @@ func (s *UserService) UpdateCurrentUser(ctx context.Context, id uint, req *model
if req.Email != nil {
// 验证邮箱唯一性
var existingUser models.User
var existingUser entity.User
if err := s.db.WithContext(ctx).Where("email = ? AND id != ?", *req.Email, id).First(&existingUser).Error; err == nil {
return nil, errors.New("email already exists")
}
@ -283,7 +282,7 @@ func (s *UserService) UpdateCurrentUser(ctx context.Context, id uint, req *model
// DeleteUser 删除用户
func (s *UserService) DeleteUser(ctx context.Context, id uint) error {
// 检查用户是否存在
var user models.User
var user entity.User
if err := s.db.WithContext(ctx).First(&user, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New("user not found")
@ -302,9 +301,9 @@ func (s *UserService) DeleteUser(ctx context.Context, id uint) error {
}
// ChangePassword 修改密码
func (s *UserService) ChangePassword(ctx context.Context, id uint, req *models.ChangePasswordRequest) error {
func (s *UserService) ChangePassword(ctx context.Context, id uint, req *entity.ChangePasswordRequest) error {
// 检查用户是否存在
var user models.User
var user entity.User
if err := s.db.WithContext(ctx).First(&user, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New("user not found")
@ -335,8 +334,8 @@ func (s *UserService) ChangePassword(ctx context.Context, id uint, req *models.C
}
// ValidateCredentials 验证用户凭据
func (s *UserService) ValidateCredentials(ctx context.Context, username, password string) (*models.User, error) {
var user models.User
func (s *UserService) ValidateCredentials(ctx context.Context, username, password string) (*entity.User, error) {
var user entity.User
// 根据用户名或邮箱查找用户
if err := s.db.WithContext(ctx).Where("username = ? OR email = ?", username, username).First(&user).Error; err != nil {
@ -361,16 +360,16 @@ func (s *UserService) ValidateCredentials(ctx context.Context, username, passwor
}
// GetUserStats 获取用户统计信息
func (s *UserService) GetUserStats(ctx context.Context) (*models.UserStats, error) {
var stats models.UserStats
func (s *UserService) GetUserStats(ctx context.Context) (*entity.UserStats, error) {
var stats entity.UserStats
// 总用户数
if err := s.db.WithContext(ctx).Model(&models.User{}).Count(&stats.Total).Error; err != nil {
if err := s.db.WithContext(ctx).Model(&entity.User{}).Count(&stats.Total).Error; err != nil {
return nil, err
}
// 活跃用户数
if err := s.db.WithContext(ctx).Model(&models.User{}).
if err := s.db.WithContext(ctx).Model(&entity.User{}).
Where("is_active = ?", true).Count(&stats.Active).Error; err != nil {
return nil, err
}
@ -380,7 +379,7 @@ func (s *UserService) GetUserStats(ctx context.Context) (*models.UserStats, erro
Role string `json:"role"`
Count int64 `json:"count"`
}
if err := s.db.WithContext(ctx).Model(&models.User{}).
if err := s.db.WithContext(ctx).Model(&entity.User{}).
Select("role, COUNT(*) as count").
Where("is_active = ?", true).
Group("role").
@ -395,7 +394,7 @@ func (s *UserService) GetUserStats(ctx context.Context) (*models.UserStats, erro
// 本月新增用户
startOfMonth := time.Now().AddDate(0, 0, -time.Now().Day()+1)
if err := s.db.WithContext(ctx).Model(&models.User{}).
if err := s.db.WithContext(ctx).Model(&entity.User{}).
Where("created_at >= ?", startOfMonth).
Count(&stats.ThisMonth).Error; err != nil {
return nil, err
@ -403,7 +402,7 @@ func (s *UserService) GetUserStats(ctx context.Context) (*models.UserStats, erro
// 今日新增用户
startOfDay := time.Now().Truncate(24 * time.Hour)
if err := s.db.WithContext(ctx).Model(&models.User{}).
if err := s.db.WithContext(ctx).Model(&entity.User{}).
Where("created_at >= ?", startOfDay).
Count(&stats.Today).Error; err != nil {
return nil, err
@ -415,7 +414,7 @@ func (s *UserService) GetUserStats(ctx context.Context) (*models.UserStats, erro
// IsUsernameAvailable 检查用户名是否可用
func (s *UserService) IsUsernameAvailable(ctx context.Context, username string) (bool, error) {
var count int64
if err := s.db.WithContext(ctx).Model(&models.User{}).
if err := s.db.WithContext(ctx).Model(&entity.User{}).
Where("username = ?", username).Count(&count).Error; err != nil {
return false, err
}
@ -425,7 +424,7 @@ func (s *UserService) IsUsernameAvailable(ctx context.Context, username string)
// IsEmailAvailable 检查邮箱是否可用
func (s *UserService) IsEmailAvailable(ctx context.Context, email string) (bool, error) {
var count int64
if err := s.db.WithContext(ctx).Model(&models.User{}).
if err := s.db.WithContext(ctx).Model(&entity.User{}).
Where("email = ?", email).Count(&count).Error; err != nil {
return false, err
}