主要变更: - 采用 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 最佳实践
148 lines
6.1 KiB
Go
148 lines
6.1 KiB
Go
package dto
|
|
|
|
import (
|
|
"time"
|
|
|
|
"photography-backend/internal/model/entity"
|
|
)
|
|
|
|
// CreateUserRequest 创建用户请求
|
|
type CreateUserRequest struct {
|
|
Username string `json:"username" binding:"required,min=3,max=50" validate:"required,min=3,max=50"`
|
|
Email string `json:"email" binding:"required,email" validate:"required,email"`
|
|
Password string `json:"password" binding:"required,min=6" validate:"required,min=6"`
|
|
Name string `json:"name" binding:"max=100" validate:"max=100"`
|
|
Role entity.UserRole `json:"role" binding:"omitempty,oneof=user admin photographer" validate:"omitempty,oneof=user admin photographer"`
|
|
}
|
|
|
|
// UpdateUserRequest 更新用户请求
|
|
type UpdateUserRequest struct {
|
|
Username *string `json:"username" binding:"omitempty,min=3,max=50" validate:"omitempty,min=3,max=50"`
|
|
Email *string `json:"email" binding:"omitempty,email" validate:"omitempty,email"`
|
|
Name *string `json:"name" binding:"omitempty,max=100" validate:"omitempty,max=100"`
|
|
Avatar *string `json:"avatar" binding:"omitempty,url" validate:"omitempty,url"`
|
|
Bio *string `json:"bio" binding:"omitempty,max=1000" validate:"omitempty,max=1000"`
|
|
Website *string `json:"website" binding:"omitempty,url" validate:"omitempty,url"`
|
|
Location *string `json:"location" binding:"omitempty,max=100" validate:"omitempty,max=100"`
|
|
IsActive *bool `json:"is_active" binding:"omitempty"`
|
|
IsPublic *bool `json:"is_public" binding:"omitempty"`
|
|
}
|
|
|
|
// ChangePasswordRequest 修改密码请求
|
|
type ChangePasswordRequest struct {
|
|
OldPassword string `json:"old_password" binding:"required" validate:"required"`
|
|
NewPassword string `json:"new_password" binding:"required,min=6" validate:"required,min=6"`
|
|
}
|
|
|
|
// UserResponse 用户响应
|
|
type UserResponse struct {
|
|
ID uint `json:"id"`
|
|
Username string `json:"username"`
|
|
Email string `json:"email"`
|
|
Name string `json:"name"`
|
|
Avatar string `json:"avatar"`
|
|
Bio string `json:"bio"`
|
|
Website string `json:"website"`
|
|
Location string `json:"location"`
|
|
Role entity.UserRole `json:"role"`
|
|
IsActive bool `json:"is_active"`
|
|
IsPublic bool `json:"is_public"`
|
|
EmailVerified bool `json:"email_verified"`
|
|
LastLogin *time.Time `json:"last_login"`
|
|
LoginCount int `json:"login_count"`
|
|
PhotoCount int64 `json:"photo_count"`
|
|
AlbumCount int64 `json:"album_count"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
// UserProfileResponse 用户档案响应(公开信息)
|
|
type UserProfileResponse struct {
|
|
ID uint `json:"id"`
|
|
Username string `json:"username"`
|
|
Name string `json:"name"`
|
|
Avatar string `json:"avatar"`
|
|
Bio string `json:"bio"`
|
|
Website string `json:"website"`
|
|
Location string `json:"location"`
|
|
Role entity.UserRole `json:"role"`
|
|
PhotoCount int64 `json:"photo_count"`
|
|
AlbumCount int64 `json:"album_count"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
}
|
|
|
|
// ListUsersOptions 用户列表查询选项
|
|
type ListUsersOptions struct {
|
|
Page int `json:"page" form:"page" binding:"omitempty,min=1" validate:"omitempty,min=1"`
|
|
Limit int `json:"limit" form:"limit" binding:"omitempty,min=1,max=100" validate:"omitempty,min=1,max=100"`
|
|
Sort string `json:"sort" form:"sort" binding:"omitempty,oneof=id username email created_at updated_at" validate:"omitempty,oneof=id username email created_at updated_at"`
|
|
Order string `json:"order" form:"order" binding:"omitempty,oneof=asc desc" validate:"omitempty,oneof=asc desc"`
|
|
Role entity.UserRole `json:"role" form:"role" binding:"omitempty,oneof=user admin photographer" validate:"omitempty,oneof=user admin photographer"`
|
|
IsActive *bool `json:"is_active" form:"is_active" binding:"omitempty"`
|
|
IsPublic *bool `json:"is_public" form:"is_public" binding:"omitempty"`
|
|
Search string `json:"search" form:"search" binding:"omitempty,max=100" validate:"omitempty,max=100"`
|
|
}
|
|
|
|
// UserListResponse 用户列表响应
|
|
type UserListResponse struct {
|
|
Users []UserResponse `json:"users"`
|
|
Total int64 `json:"total"`
|
|
Page int `json:"page"`
|
|
Limit int `json:"limit"`
|
|
}
|
|
|
|
// UserStatsResponse 用户统计响应
|
|
type UserStatsResponse struct {
|
|
Total int64 `json:"total"`
|
|
Active int64 `json:"active"`
|
|
Inactive int64 `json:"inactive"`
|
|
Verified int64 `json:"verified"`
|
|
Unverified int64 `json:"unverified"`
|
|
RoleCounts map[entity.UserRole]int64 `json:"role_counts"`
|
|
RecentLogins []UserResponse `json:"recent_logins"`
|
|
}
|
|
|
|
// ConvertToUserResponse 将用户实体转换为响应DTO
|
|
func ConvertToUserResponse(user *entity.User) *UserResponse {
|
|
if user == nil {
|
|
return nil
|
|
}
|
|
|
|
return &UserResponse{
|
|
ID: user.ID,
|
|
Username: user.Username,
|
|
Email: user.Email,
|
|
Name: user.Name,
|
|
Avatar: user.Avatar,
|
|
Bio: user.Bio,
|
|
Website: user.Website,
|
|
Location: user.Location,
|
|
Role: user.Role,
|
|
IsActive: user.IsActive,
|
|
IsPublic: user.IsPublic,
|
|
EmailVerified: user.EmailVerified,
|
|
LastLogin: user.LastLogin,
|
|
LoginCount: user.LoginCount,
|
|
CreatedAt: user.CreatedAt,
|
|
UpdatedAt: user.UpdatedAt,
|
|
}
|
|
}
|
|
|
|
// ConvertToUserProfile 将用户实体转换为公开档案DTO
|
|
func ConvertToUserProfile(user *entity.User) *UserProfileResponse {
|
|
if user == nil {
|
|
return nil
|
|
}
|
|
|
|
return &UserProfileResponse{
|
|
ID: user.ID,
|
|
Username: user.Username,
|
|
Name: user.Name,
|
|
Avatar: user.Avatar,
|
|
Bio: user.Bio,
|
|
Website: user.Website,
|
|
Location: user.Location,
|
|
Role: user.Role,
|
|
CreatedAt: user.CreatedAt,
|
|
}
|
|
} |