This commit is contained in:
2026-01-09 15:58:09 +08:00
parent bd76520d22
commit ead5f17b7c
18 changed files with 1175 additions and 121 deletions

View File

@@ -116,7 +116,7 @@ func (r *GormComponentReportRepository) GetUserDownloadedProductCodes(ctx contex
func (r *GormComponentReportRepository) GetDownloadByPaymentOrderID(ctx context.Context, orderID string) (*entities.ComponentReportDownload, error) {
var download entities.ComponentReportDownload
err := r.GetDB(ctx).Where("order_number = ?", orderID).First(&download).Error
err := r.GetDB(ctx).Where("order_id = ?", orderID).First(&download).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, gorm.ErrRecordNotFound

View File

@@ -0,0 +1,137 @@
package repositories
import (
"context"
"errors"
"tyapi-server/internal/domains/product/entities"
"tyapi-server/internal/domains/product/repositories"
"tyapi-server/internal/shared/database"
"go.uber.org/zap"
"gorm.io/gorm"
)
const (
ProductSubCategoriesTable = "product_sub_categories"
)
type GormProductSubCategoryRepository struct {
*database.CachedBaseRepositoryImpl
}
var _ repositories.ProductSubCategoryRepository = (*GormProductSubCategoryRepository)(nil)
func NewGormProductSubCategoryRepository(db *gorm.DB, logger *zap.Logger) repositories.ProductSubCategoryRepository {
return &GormProductSubCategoryRepository{
CachedBaseRepositoryImpl: database.NewCachedBaseRepositoryImpl(db, logger, ProductSubCategoriesTable),
}
}
// Create 创建二级分类
func (r *GormProductSubCategoryRepository) Create(ctx context.Context, category entities.ProductSubCategory) (*entities.ProductSubCategory, error) {
err := r.CreateEntity(ctx, &category)
if err != nil {
return nil, err
}
return &category, nil
}
// GetByID 根据ID获取二级分类
func (r *GormProductSubCategoryRepository) GetByID(ctx context.Context, id string) (*entities.ProductSubCategory, error) {
var entity entities.ProductSubCategory
err := r.SmartGetByID(ctx, id, &entity)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, gorm.ErrRecordNotFound
}
return nil, err
}
return &entity, nil
}
// Update 更新二级分类
func (r *GormProductSubCategoryRepository) Update(ctx context.Context, category entities.ProductSubCategory) error {
return r.UpdateEntity(ctx, &category)
}
// Delete 删除二级分类
func (r *GormProductSubCategoryRepository) Delete(ctx context.Context, id string) error {
return r.DeleteEntity(ctx, id, &entities.ProductSubCategory{})
}
// List 获取所有二级分类
func (r *GormProductSubCategoryRepository) List(ctx context.Context) ([]*entities.ProductSubCategory, error) {
var categories []entities.ProductSubCategory
err := r.GetDB(ctx).Order("sort ASC, created_at DESC").Find(&categories).Error
if err != nil {
return nil, err
}
// 转换为指针切片
result := make([]*entities.ProductSubCategory, len(categories))
for i := range categories {
result[i] = &categories[i]
}
return result, nil
}
// FindByCode 根据编号查找二级分类
func (r *GormProductSubCategoryRepository) FindByCode(ctx context.Context, code string) (*entities.ProductSubCategory, error) {
var entity entities.ProductSubCategory
err := r.GetDB(ctx).Where("code = ?", code).First(&entity).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, gorm.ErrRecordNotFound
}
return nil, err
}
return &entity, nil
}
// FindByCategoryID 根据一级分类ID查找二级分类
func (r *GormProductSubCategoryRepository) FindByCategoryID(ctx context.Context, categoryID string) ([]*entities.ProductSubCategory, error) {
var categories []entities.ProductSubCategory
err := r.GetDB(ctx).Where("category_id = ?", categoryID).Order("sort ASC, created_at DESC").Find(&categories).Error
if err != nil {
return nil, err
}
// 转换为指针切片
result := make([]*entities.ProductSubCategory, len(categories))
for i := range categories {
result[i] = &categories[i]
}
return result, nil
}
// FindVisible 查找可见的二级分类
func (r *GormProductSubCategoryRepository) FindVisible(ctx context.Context) ([]*entities.ProductSubCategory, error) {
var categories []entities.ProductSubCategory
err := r.GetDB(ctx).Where("is_visible = ? AND is_enabled = ?", true, true).Order("sort ASC, created_at DESC").Find(&categories).Error
if err != nil {
return nil, err
}
// 转换为指针切片
result := make([]*entities.ProductSubCategory, len(categories))
for i := range categories {
result[i] = &categories[i]
}
return result, nil
}
// FindEnabled 查找启用的二级分类
func (r *GormProductSubCategoryRepository) FindEnabled(ctx context.Context) ([]*entities.ProductSubCategory, error) {
var categories []entities.ProductSubCategory
err := r.GetDB(ctx).Where("is_enabled = ?", true).Order("sort ASC, created_at DESC").Find(&categories).Error
if err != nil {
return nil, err
}
// 转换为指针切片
result := make([]*entities.ProductSubCategory, len(categories))
for i := range categories {
result[i] = &categories[i]
}
return result, nil
}

View File

@@ -0,0 +1,229 @@
package handlers
import (
"tyapi-server/internal/application/product"
"tyapi-server/internal/application/product/dto/commands"
"tyapi-server/internal/application/product/dto/queries"
"tyapi-server/internal/shared/interfaces"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// SubCategoryHandler 二级分类HTTP处理器
type SubCategoryHandler struct {
subCategoryAppService product.SubCategoryApplicationService
responseBuilder interfaces.ResponseBuilder
validator interfaces.RequestValidator
logger *zap.Logger
}
// NewSubCategoryHandler 创建二级分类HTTP处理器
func NewSubCategoryHandler(
subCategoryAppService product.SubCategoryApplicationService,
responseBuilder interfaces.ResponseBuilder,
validator interfaces.RequestValidator,
logger *zap.Logger,
) *SubCategoryHandler {
return &SubCategoryHandler{
subCategoryAppService: subCategoryAppService,
responseBuilder: responseBuilder,
validator: validator,
logger: logger,
}
}
// CreateSubCategory 创建二级分类
// @Summary 创建二级分类
// @Description 管理员创建新二级分类
// @Tags 二级分类管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param request body commands.CreateSubCategoryCommand true "创建二级分类请求"
// @Success 201 {object} map[string]interface{} "二级分类创建成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/admin/sub-categories [post]
func (h *SubCategoryHandler) CreateSubCategory(c *gin.Context) {
var cmd commands.CreateSubCategoryCommand
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
return
}
if err := h.subCategoryAppService.CreateSubCategory(c.Request.Context(), &cmd); err != nil {
h.logger.Error("创建二级分类失败", zap.Error(err))
h.responseBuilder.BadRequest(c, err.Error())
return
}
h.responseBuilder.Created(c, nil, "二级分类创建成功")
}
// UpdateSubCategory 更新二级分类
// @Summary 更新二级分类
// @Description 管理员更新二级分类信息
// @Tags 二级分类管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param id path string true "二级分类ID"
// @Param request body commands.UpdateSubCategoryCommand true "更新二级分类请求"
// @Success 200 {object} map[string]interface{} "二级分类更新成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 404 {object} map[string]interface{} "二级分类不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/admin/sub-categories/{id} [put]
func (h *SubCategoryHandler) UpdateSubCategory(c *gin.Context) {
var cmd commands.UpdateSubCategoryCommand
cmd.ID = c.Param("id")
if cmd.ID == "" {
h.responseBuilder.BadRequest(c, "二级分类ID不能为空")
return
}
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
return
}
if err := h.subCategoryAppService.UpdateSubCategory(c.Request.Context(), &cmd); err != nil {
h.logger.Error("更新二级分类失败", zap.Error(err))
h.responseBuilder.BadRequest(c, err.Error())
return
}
h.responseBuilder.Success(c, nil, "二级分类更新成功")
}
// DeleteSubCategory 删除二级分类
// @Summary 删除二级分类
// @Description 管理员删除二级分类
// @Tags 二级分类管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param id path string true "二级分类ID"
// @Success 200 {object} map[string]interface{} "二级分类删除成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 404 {object} map[string]interface{} "二级分类不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/admin/sub-categories/{id} [delete]
func (h *SubCategoryHandler) DeleteSubCategory(c *gin.Context) {
cmd := commands.DeleteSubCategoryCommand{ID: c.Param("id")}
if err := h.validator.ValidateParam(c, &cmd); err != nil {
return
}
if err := h.subCategoryAppService.DeleteSubCategory(c.Request.Context(), &cmd); err != nil {
h.logger.Error("删除二级分类失败", zap.Error(err))
h.responseBuilder.BadRequest(c, err.Error())
return
}
h.responseBuilder.Success(c, nil, "二级分类删除成功")
}
// GetSubCategory 获取二级分类详情
// @Summary 获取二级分类详情
// @Description 获取指定二级分类的详细信息
// @Tags 二级分类管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param id path string true "二级分类ID"
// @Success 200 {object} responses.SubCategoryInfoResponse "二级分类信息"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 404 {object} map[string]interface{} "二级分类不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/admin/sub-categories/{id} [get]
func (h *SubCategoryHandler) GetSubCategory(c *gin.Context) {
query := &queries.GetSubCategoryQuery{ID: c.Param("id")}
if err := h.validator.ValidateParam(c, query); err != nil {
return
}
result, err := h.subCategoryAppService.GetSubCategoryByID(c.Request.Context(), query)
if err != nil {
h.logger.Error("获取二级分类失败", zap.Error(err))
h.responseBuilder.NotFound(c, err.Error())
return
}
h.responseBuilder.Success(c, result, "获取二级分类成功")
}
// ListSubCategories 获取二级分类列表
// @Summary 获取二级分类列表
// @Description 获取二级分类列表,支持筛选和分页
// @Tags 二级分类管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param category_id query string false "一级分类ID"
// @Param page query int false "页码"
// @Param page_size query int false "每页数量"
// @Param is_enabled query bool false "是否启用"
// @Param is_visible query bool false "是否展示"
// @Param sort_by query string false "排序字段"
// @Param sort_order query string false "排序方向"
// @Success 200 {object} responses.SubCategoryListResponse "二级分类列表"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/admin/sub-categories [get]
func (h *SubCategoryHandler) ListSubCategories(c *gin.Context) {
query := &queries.ListSubCategoriesQuery{}
if err := h.validator.ValidateQuery(c, query); err != nil {
return
}
// 设置默认分页参数
if query.Page == 0 {
query.Page = 1
}
if query.PageSize == 0 {
query.PageSize = 20
}
result, err := h.subCategoryAppService.ListSubCategories(c.Request.Context(), query)
if err != nil {
h.logger.Error("获取二级分类列表失败", zap.Error(err))
h.responseBuilder.BadRequest(c, err.Error())
return
}
h.responseBuilder.Success(c, result, "获取二级分类列表成功")
}
// ListSubCategoriesByCategory 根据一级分类ID获取二级分类列表级联选择用
// @Summary 根据一级分类获取二级分类列表
// @Description 根据一级分类ID获取二级分类简单列表用于级联选择
// @Tags 二级分类管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param id path string true "一级分类ID"
// @Success 200 {object} []responses.SubCategorySimpleResponse "二级分类简单列表"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/admin/categories/{id}/sub-categories [get]
func (h *SubCategoryHandler) ListSubCategoriesByCategory(c *gin.Context) {
categoryID := c.Param("id")
if categoryID == "" {
h.responseBuilder.BadRequest(c, "一级分类ID不能为空")
return
}
result, err := h.subCategoryAppService.ListSubCategoriesByCategoryID(c.Request.Context(), categoryID)
if err != nil {
h.logger.Error("获取二级分类列表失败", zap.Error(err))
h.responseBuilder.BadRequest(c, err.Error())
return
}
h.responseBuilder.Success(c, result, "获取二级分类列表成功")
}

View File

@@ -0,0 +1,52 @@
package routes
import (
"tyapi-server/internal/infrastructure/http/handlers"
sharedhttp "tyapi-server/internal/shared/http"
"tyapi-server/internal/shared/middleware"
)
// SubCategoryRoutes 二级分类路由
type SubCategoryRoutes struct {
handler *handlers.SubCategoryHandler
auth *middleware.JWTAuthMiddleware
admin *middleware.AdminAuthMiddleware
}
// NewSubCategoryRoutes 创建二级分类路由
func NewSubCategoryRoutes(
handler *handlers.SubCategoryHandler,
auth *middleware.JWTAuthMiddleware,
admin *middleware.AdminAuthMiddleware,
) *SubCategoryRoutes {
return &SubCategoryRoutes{
handler: handler,
auth: auth,
admin: admin,
}
}
// Register 注册路由
func (r *SubCategoryRoutes) Register(router *sharedhttp.GinRouter) {
engine := router.GetEngine()
adminGroup := engine.Group("/api/v1/admin")
adminGroup.Use(r.auth.Handle())
adminGroup.Use(r.admin.Handle())
{
// 二级分类管理
subCategories := adminGroup.Group("/sub-categories")
{
subCategories.POST("", r.handler.CreateSubCategory) // 创建二级分类
subCategories.PUT("/:id", r.handler.UpdateSubCategory) // 更新二级分类
subCategories.DELETE("/:id", r.handler.DeleteSubCategory) // 删除二级分类
subCategories.GET("/:id", r.handler.GetSubCategory) // 获取二级分类详情
subCategories.GET("", r.handler.ListSubCategories) // 获取二级分类列表
}
// 一级分类下的二级分类路由(级联选择)
categoryAdmin := adminGroup.Group("/product-categories")
{
categoryAdmin.GET("/:id/sub-categories", r.handler.ListSubCategoriesByCategory) // 根据一级分类获取二级分类列表
}
}
}