addui
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"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 (
|
||||
ComponentReportDownloadsTable = "component_report_downloads"
|
||||
)
|
||||
|
||||
type GormComponentReportRepository struct {
|
||||
*database.CachedBaseRepositoryImpl
|
||||
}
|
||||
|
||||
var _ repositories.ComponentReportRepository = (*GormComponentReportRepository)(nil)
|
||||
|
||||
func NewGormComponentReportRepository(db *gorm.DB, logger *zap.Logger) repositories.ComponentReportRepository {
|
||||
return &GormComponentReportRepository{
|
||||
CachedBaseRepositoryImpl: database.NewCachedBaseRepositoryImpl(db, logger, ComponentReportDownloadsTable),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *GormComponentReportRepository) CreateDownload(ctx context.Context, download *entities.ComponentReportDownload) (*entities.ComponentReportDownload, error) {
|
||||
err := r.CreateEntity(ctx, download)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return download, nil
|
||||
}
|
||||
|
||||
func (r *GormComponentReportRepository) UpdateDownload(ctx context.Context, download *entities.ComponentReportDownload) error {
|
||||
return r.UpdateEntity(ctx, download)
|
||||
}
|
||||
|
||||
func (r *GormComponentReportRepository) GetDownloadByID(ctx context.Context, id string) (*entities.ComponentReportDownload, error) {
|
||||
var download entities.ComponentReportDownload
|
||||
err := r.SmartGetByID(ctx, id, &download)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, gorm.ErrRecordNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &download, nil
|
||||
}
|
||||
|
||||
func (r *GormComponentReportRepository) GetUserDownloads(ctx context.Context, userID string, productID *string) ([]*entities.ComponentReportDownload, error) {
|
||||
var downloads []entities.ComponentReportDownload
|
||||
query := r.GetDB(ctx).Where("user_id = ? AND payment_status = ?", userID, "success")
|
||||
|
||||
if productID != nil && *productID != "" {
|
||||
query = query.Where("product_id = ?", *productID)
|
||||
}
|
||||
|
||||
err := query.Order("created_at DESC").Find(&downloads).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make([]*entities.ComponentReportDownload, len(downloads))
|
||||
for i := range downloads {
|
||||
result[i] = &downloads[i]
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *GormComponentReportRepository) HasUserDownloaded(ctx context.Context, userID string, productCode string) (bool, error) {
|
||||
var count int64
|
||||
err := r.GetDB(ctx).Model(&entities.ComponentReportDownload{}).
|
||||
Where("user_id = ? AND product_code = ? AND payment_status = ?", userID, productCode, "success").
|
||||
Count(&count).Error
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
func (r *GormComponentReportRepository) GetUserDownloadedProductCodes(ctx context.Context, userID string) ([]string, error) {
|
||||
var downloads []entities.ComponentReportDownload
|
||||
err := r.GetDB(ctx).
|
||||
Select("DISTINCT sub_product_codes").
|
||||
Where("user_id = ? AND payment_status = ?", userID, "success").
|
||||
Find(&downloads).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
codesMap := make(map[string]bool)
|
||||
for _, download := range downloads {
|
||||
if download.SubProductCodes != "" {
|
||||
var codes []string
|
||||
if err := json.Unmarshal([]byte(download.SubProductCodes), &codes); err == nil {
|
||||
for _, code := range codes {
|
||||
codesMap[code] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
// 也添加主产品编号
|
||||
if download.ProductCode != "" {
|
||||
codesMap[download.ProductCode] = true
|
||||
}
|
||||
}
|
||||
|
||||
codes := make([]string, 0, len(codesMap))
|
||||
for code := range codesMap {
|
||||
codes = append(codes, code)
|
||||
}
|
||||
return codes, nil
|
||||
}
|
||||
|
||||
func (r *GormComponentReportRepository) GetDownloadByPaymentOrderID(ctx context.Context, orderID string) (*entities.ComponentReportDownload, error) {
|
||||
var download entities.ComponentReportDownload
|
||||
err := r.GetDB(ctx).Where("payment_order_id = ?", orderID).First(&download).Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, gorm.ErrRecordNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &download, nil
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"tyapi-server/internal/domains/product/entities"
|
||||
"tyapi-server/internal/domains/product/repositories"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// GormProductUIComponentRepository 产品UI组件关联仓储实现
|
||||
type GormProductUIComponentRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewGormProductUIComponentRepository 创建产品UI组件关联仓储实例
|
||||
func NewGormProductUIComponentRepository(db *gorm.DB) repositories.ProductUIComponentRepository {
|
||||
return &GormProductUIComponentRepository{db: db}
|
||||
}
|
||||
|
||||
// Create 创建产品UI组件关联
|
||||
func (r *GormProductUIComponentRepository) Create(ctx context.Context, relation entities.ProductUIComponent) (entities.ProductUIComponent, error) {
|
||||
if err := r.db.WithContext(ctx).Create(&relation).Error; err != nil {
|
||||
return entities.ProductUIComponent{}, fmt.Errorf("创建产品UI组件关联失败: %w", err)
|
||||
}
|
||||
return relation, nil
|
||||
}
|
||||
|
||||
// GetByProductID 根据产品ID获取UI组件关联列表
|
||||
func (r *GormProductUIComponentRepository) GetByProductID(ctx context.Context, productID string) ([]entities.ProductUIComponent, error) {
|
||||
var relations []entities.ProductUIComponent
|
||||
if err := r.db.WithContext(ctx).
|
||||
Preload("UIComponent").
|
||||
Where("product_id = ?", productID).
|
||||
Find(&relations).Error; err != nil {
|
||||
return nil, fmt.Errorf("获取产品UI组件关联列表失败: %w", err)
|
||||
}
|
||||
return relations, nil
|
||||
}
|
||||
|
||||
// GetByUIComponentID 根据UI组件ID获取产品关联列表
|
||||
func (r *GormProductUIComponentRepository) GetByUIComponentID(ctx context.Context, componentID string) ([]entities.ProductUIComponent, error) {
|
||||
var relations []entities.ProductUIComponent
|
||||
if err := r.db.WithContext(ctx).
|
||||
Preload("Product").
|
||||
Where("ui_component_id = ?", componentID).
|
||||
Find(&relations).Error; err != nil {
|
||||
return nil, fmt.Errorf("获取UI组件产品关联列表失败: %w", err)
|
||||
}
|
||||
return relations, nil
|
||||
}
|
||||
|
||||
// Delete 删除产品UI组件关联
|
||||
func (r *GormProductUIComponentRepository) Delete(ctx context.Context, id string) error {
|
||||
if err := r.db.WithContext(ctx).Delete(&entities.ProductUIComponent{}, id).Error; err != nil {
|
||||
return fmt.Errorf("删除产品UI组件关联失败: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteByProductID 根据产品ID删除所有关联
|
||||
func (r *GormProductUIComponentRepository) DeleteByProductID(ctx context.Context, productID string) error {
|
||||
if err := r.db.WithContext(ctx).Where("product_id = ?", productID).Delete(&entities.ProductUIComponent{}).Error; err != nil {
|
||||
return fmt.Errorf("根据产品ID删除UI组件关联失败: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BatchCreate 批量创建产品UI组件关联
|
||||
func (r *GormProductUIComponentRepository) BatchCreate(ctx context.Context, relations []entities.ProductUIComponent) error {
|
||||
if len(relations) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := r.db.WithContext(ctx).CreateInBatches(relations, 100).Error; err != nil {
|
||||
return fmt.Errorf("批量创建产品UI组件关联失败: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"tyapi-server/internal/domains/product/entities"
|
||||
"tyapi-server/internal/domains/product/repositories"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// GormUIComponentRepository UI组件仓储实现
|
||||
type GormUIComponentRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewGormUIComponentRepository 创建UI组件仓储实例
|
||||
func NewGormUIComponentRepository(db *gorm.DB) repositories.UIComponentRepository {
|
||||
return &GormUIComponentRepository{db: db}
|
||||
}
|
||||
|
||||
// Create 创建UI组件
|
||||
func (r *GormUIComponentRepository) Create(ctx context.Context, component entities.UIComponent) (entities.UIComponent, error) {
|
||||
if err := r.db.WithContext(ctx).Create(&component).Error; err != nil {
|
||||
return entities.UIComponent{}, fmt.Errorf("创建UI组件失败: %w", err)
|
||||
}
|
||||
return component, nil
|
||||
}
|
||||
|
||||
// GetByID 根据ID获取UI组件
|
||||
func (r *GormUIComponentRepository) GetByID(ctx context.Context, id string) (*entities.UIComponent, error) {
|
||||
var component entities.UIComponent
|
||||
if err := r.db.WithContext(ctx).Where("id = ?", id).First(&component).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("获取UI组件失败: %w", err)
|
||||
}
|
||||
return &component, nil
|
||||
}
|
||||
|
||||
// GetByCode 根据编码获取UI组件
|
||||
func (r *GormUIComponentRepository) GetByCode(ctx context.Context, code string) (*entities.UIComponent, error) {
|
||||
var component entities.UIComponent
|
||||
if err := r.db.WithContext(ctx).Where("component_code = ?", code).First(&component).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("获取UI组件失败: %w", err)
|
||||
}
|
||||
return &component, nil
|
||||
}
|
||||
|
||||
// List 获取UI组件列表
|
||||
func (r *GormUIComponentRepository) List(ctx context.Context, filters map[string]interface{}) ([]entities.UIComponent, int64, error) {
|
||||
var components []entities.UIComponent
|
||||
var total int64
|
||||
|
||||
query := r.db.WithContext(ctx).Model(&entities.UIComponent{})
|
||||
|
||||
// 应用过滤条件
|
||||
if isActive, ok := filters["is_active"]; ok {
|
||||
query = query.Where("is_active = ?", isActive)
|
||||
}
|
||||
|
||||
if keyword, ok := filters["keyword"]; ok && keyword != "" {
|
||||
query = query.Where("component_name LIKE ? OR component_code LIKE ? OR description LIKE ?",
|
||||
"%"+keyword.(string)+"%", "%"+keyword.(string)+"%", "%"+keyword.(string)+"%")
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
return nil, 0, fmt.Errorf("获取UI组件总数失败: %w", err)
|
||||
}
|
||||
|
||||
// 分页
|
||||
if page, ok := filters["page"]; ok {
|
||||
if pageSize, ok := filters["page_size"]; ok {
|
||||
offset := (page.(int) - 1) * pageSize.(int)
|
||||
query = query.Offset(offset).Limit(pageSize.(int))
|
||||
}
|
||||
}
|
||||
|
||||
// 排序
|
||||
if sortBy, ok := filters["sort_by"]; ok {
|
||||
if sortOrder, ok := filters["sort_order"]; ok {
|
||||
query = query.Order(fmt.Sprintf("%s %s", sortBy, sortOrder))
|
||||
}
|
||||
} else {
|
||||
query = query.Order("sort_order ASC, created_at DESC")
|
||||
}
|
||||
|
||||
// 获取数据
|
||||
if err := query.Find(&components).Error; err != nil {
|
||||
return nil, 0, fmt.Errorf("获取UI组件列表失败: %w", err)
|
||||
}
|
||||
|
||||
return components, total, nil
|
||||
}
|
||||
|
||||
// Update 更新UI组件
|
||||
func (r *GormUIComponentRepository) Update(ctx context.Context, component entities.UIComponent) error {
|
||||
if err := r.db.WithContext(ctx).Save(&component).Error; err != nil {
|
||||
return fmt.Errorf("更新UI组件失败: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete 删除UI组件
|
||||
func (r *GormUIComponentRepository) Delete(ctx context.Context, id string) error {
|
||||
if err := r.db.WithContext(ctx).Delete(&entities.UIComponent{}, id).Error; err != nil {
|
||||
return fmt.Errorf("删除UI组件失败: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetByCodes 根据编码列表获取UI组件
|
||||
func (r *GormUIComponentRepository) GetByCodes(ctx context.Context, codes []string) ([]entities.UIComponent, error) {
|
||||
var components []entities.UIComponent
|
||||
if len(codes) == 0 {
|
||||
return components, nil
|
||||
}
|
||||
|
||||
if err := r.db.WithContext(ctx).Where("component_code IN ?", codes).Find(&components).Error; err != nil {
|
||||
return nil, fmt.Errorf("根据编码列表获取UI组件失败: %w", err)
|
||||
}
|
||||
|
||||
return components, nil
|
||||
}
|
||||
115
internal/infrastructure/external/storage/local_file_storage_service.go
vendored
Normal file
115
internal/infrastructure/external/storage/local_file_storage_service.go
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// LocalFileStorageService 本地文件存储服务
|
||||
type LocalFileStorageService struct {
|
||||
basePath string
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// LocalFileStorageConfig 本地文件存储配置
|
||||
type LocalFileStorageConfig struct {
|
||||
BasePath string `yaml:"base_path"`
|
||||
}
|
||||
|
||||
// NewLocalFileStorageService 创建本地文件存储服务
|
||||
func NewLocalFileStorageService(basePath string, logger *zap.Logger) *LocalFileStorageService {
|
||||
// 确保基础路径存在
|
||||
if err := os.MkdirAll(basePath, 0755); err != nil {
|
||||
logger.Error("创建基础存储目录失败", zap.Error(err), zap.String("path", basePath))
|
||||
}
|
||||
|
||||
return &LocalFileStorageService{
|
||||
basePath: basePath,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// StoreFile 存储文件
|
||||
func (s *LocalFileStorageService) StoreFile(ctx context.Context, file io.Reader, filename string) (string, error) {
|
||||
// 构建完整文件路径
|
||||
fullPath := filepath.Join(s.basePath, filename)
|
||||
|
||||
// 确保目录存在
|
||||
dir := filepath.Dir(fullPath)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
s.logger.Error("创建目录失败", zap.Error(err), zap.String("dir", dir))
|
||||
return "", fmt.Errorf("创建目录失败: %w", err)
|
||||
}
|
||||
|
||||
// 创建文件
|
||||
dst, err := os.Create(fullPath)
|
||||
if err != nil {
|
||||
s.logger.Error("创建文件失败", zap.Error(err), zap.String("path", fullPath))
|
||||
return "", fmt.Errorf("创建文件失败: %w", err)
|
||||
}
|
||||
defer dst.Close()
|
||||
|
||||
// 复制文件内容
|
||||
if _, err := io.Copy(dst, file); err != nil {
|
||||
s.logger.Error("写入文件失败", zap.Error(err), zap.String("path", fullPath))
|
||||
// 删除部分写入的文件
|
||||
_ = os.Remove(fullPath)
|
||||
return "", fmt.Errorf("写入文件失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("文件存储成功", zap.String("path", fullPath))
|
||||
return fullPath, nil
|
||||
}
|
||||
|
||||
// StoreMultipartFile 存储multipart文件
|
||||
func (s *LocalFileStorageService) StoreMultipartFile(ctx context.Context, file *multipart.FileHeader, filename string) (string, error) {
|
||||
src, err := file.Open()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("打开上传文件失败: %w", err)
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
return s.StoreFile(ctx, src, filename)
|
||||
}
|
||||
|
||||
// GetFileURL 获取文件URL
|
||||
func (s *LocalFileStorageService) GetFileURL(ctx context.Context, filePath string) (string, error) {
|
||||
// 检查文件是否存在
|
||||
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||
return "", fmt.Errorf("文件不存在: %s", filePath)
|
||||
}
|
||||
|
||||
// 返回文件路径(在实际应用中,这里应该返回可访问的URL)
|
||||
return filePath, nil
|
||||
}
|
||||
|
||||
// DeleteFile 删除文件
|
||||
func (s *LocalFileStorageService) DeleteFile(ctx context.Context, filePath string) error {
|
||||
if err := os.Remove(filePath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// 文件不存在,不视为错误
|
||||
return nil
|
||||
}
|
||||
s.logger.Error("删除文件失败", zap.Error(err), zap.String("path", filePath))
|
||||
return fmt.Errorf("删除文件失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("文件删除成功", zap.String("path", filePath))
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFileReader 获取文件读取器
|
||||
func (s *LocalFileStorageService) GetFileReader(ctx context.Context, filePath string) (io.ReadCloser, error) {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("打开文件失败: %w", err)
|
||||
}
|
||||
|
||||
return file, nil
|
||||
}
|
||||
110
internal/infrastructure/external/storage/local_file_storage_service_impl.go
vendored
Normal file
110
internal/infrastructure/external/storage/local_file_storage_service_impl.go
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// LocalFileStorageServiceImpl 本地文件存储服务实现
|
||||
type LocalFileStorageServiceImpl struct {
|
||||
basePath string
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewLocalFileStorageServiceImpl 创建本地文件存储服务实现
|
||||
func NewLocalFileStorageServiceImpl(basePath string, logger *zap.Logger) *LocalFileStorageServiceImpl {
|
||||
// 确保基础路径存在
|
||||
if err := os.MkdirAll(basePath, 0755); err != nil {
|
||||
logger.Error("创建基础存储目录失败", zap.Error(err), zap.String("path", basePath))
|
||||
}
|
||||
|
||||
return &LocalFileStorageServiceImpl{
|
||||
basePath: basePath,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// StoreFile 存储文件
|
||||
func (s *LocalFileStorageServiceImpl) StoreFile(ctx context.Context, file io.Reader, filename string) (string, error) {
|
||||
// 构建完整文件路径
|
||||
fullPath := filepath.Join(s.basePath, filename)
|
||||
|
||||
// 确保目录存在
|
||||
dir := filepath.Dir(fullPath)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
s.logger.Error("创建目录失败", zap.Error(err), zap.String("dir", dir))
|
||||
return "", fmt.Errorf("创建目录失败: %w", err)
|
||||
}
|
||||
|
||||
// 创建文件
|
||||
dst, err := os.Create(fullPath)
|
||||
if err != nil {
|
||||
s.logger.Error("创建文件失败", zap.Error(err), zap.String("path", fullPath))
|
||||
return "", fmt.Errorf("创建文件失败: %w", err)
|
||||
}
|
||||
defer dst.Close()
|
||||
|
||||
// 复制文件内容
|
||||
if _, err := io.Copy(dst, file); err != nil {
|
||||
s.logger.Error("写入文件失败", zap.Error(err), zap.String("path", fullPath))
|
||||
// 删除部分写入的文件
|
||||
_ = os.Remove(fullPath)
|
||||
return "", fmt.Errorf("写入文件失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("文件存储成功", zap.String("path", fullPath))
|
||||
return fullPath, nil
|
||||
}
|
||||
|
||||
// StoreMultipartFile 存储multipart文件
|
||||
func (s *LocalFileStorageServiceImpl) StoreMultipartFile(ctx context.Context, file *multipart.FileHeader, filename string) (string, error) {
|
||||
src, err := file.Open()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("打开上传文件失败: %w", err)
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
return s.StoreFile(ctx, src, filename)
|
||||
}
|
||||
|
||||
// GetFileURL 获取文件URL
|
||||
func (s *LocalFileStorageServiceImpl) GetFileURL(ctx context.Context, filePath string) (string, error) {
|
||||
// 检查文件是否存在
|
||||
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||
return "", fmt.Errorf("文件不存在: %s", filePath)
|
||||
}
|
||||
|
||||
// 返回文件路径(在实际应用中,这里应该返回可访问的URL)
|
||||
return filePath, nil
|
||||
}
|
||||
|
||||
// DeleteFile 删除文件
|
||||
func (s *LocalFileStorageServiceImpl) DeleteFile(ctx context.Context, filePath string) error {
|
||||
if err := os.Remove(filePath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// 文件不存在,不视为错误
|
||||
return nil
|
||||
}
|
||||
s.logger.Error("删除文件失败", zap.Error(err), zap.String("path", filePath))
|
||||
return fmt.Errorf("删除文件失败: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("文件删除成功", zap.String("path", filePath))
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFileReader 获取文件读取器
|
||||
func (s *LocalFileStorageServiceImpl) GetFileReader(ctx context.Context, filePath string) (io.ReadCloser, error) {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("打开文件失败: %w", err)
|
||||
}
|
||||
|
||||
return file, nil
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"tyapi-server/internal/application/product"
|
||||
"tyapi-server/internal/shared/interfaces"
|
||||
)
|
||||
|
||||
// FileDownloadHandler 文件下载处理器
|
||||
type FileDownloadHandler struct {
|
||||
uiComponentAppService product.UIComponentApplicationService
|
||||
responseBuilder interfaces.ResponseBuilder
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewFileDownloadHandler 创建文件下载处理器
|
||||
func NewFileDownloadHandler(
|
||||
uiComponentAppService product.UIComponentApplicationService,
|
||||
responseBuilder interfaces.ResponseBuilder,
|
||||
logger *zap.Logger,
|
||||
) *FileDownloadHandler {
|
||||
return &FileDownloadHandler{
|
||||
uiComponentAppService: uiComponentAppService,
|
||||
responseBuilder: responseBuilder,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// DownloadUIComponentFile 下载UI组件文件
|
||||
// @Summary 下载UI组件文件
|
||||
// @Description 下载UI组件文件
|
||||
// @Tags 文件下载
|
||||
// @Accept json
|
||||
// @Produce application/octet-stream
|
||||
// @Param id path string true "UI组件ID"
|
||||
// @Success 200 {file} file "文件内容"
|
||||
// @Failure 400 {object} interfaces.Response "请求参数错误"
|
||||
// @Failure 404 {object} interfaces.Response "UI组件不存在或文件不存在"
|
||||
// @Failure 500 {object} interfaces.Response "服务器内部错误"
|
||||
// @Router /api/v1/ui-components/{id}/download [get]
|
||||
func (h *FileDownloadHandler) DownloadUIComponentFile(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
h.responseBuilder.BadRequest(c, "UI组件ID不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取UI组件信息
|
||||
component, err := h.uiComponentAppService.GetUIComponentByID(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
h.logger.Error("获取UI组件失败", zap.Error(err), zap.String("id", id))
|
||||
h.responseBuilder.InternalError(c, "获取UI组件失败")
|
||||
return
|
||||
}
|
||||
|
||||
if component == nil {
|
||||
h.responseBuilder.NotFound(c, "UI组件不存在")
|
||||
return
|
||||
}
|
||||
|
||||
if component.FilePath == nil {
|
||||
h.responseBuilder.NotFound(c, "UI组件文件不存在")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取文件路径
|
||||
filePath, err := h.uiComponentAppService.DownloadUIComponentFile(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
h.logger.Error("获取UI组件文件路径失败", zap.Error(err), zap.String("id", id))
|
||||
h.responseBuilder.InternalError(c, "获取UI组件文件路径失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 设置下载文件名
|
||||
fileName := component.ComponentName
|
||||
if !strings.HasSuffix(strings.ToLower(fileName), ".zip") {
|
||||
fileName += ".zip"
|
||||
}
|
||||
|
||||
// 设置响应头
|
||||
c.Header("Content-Description", "File Transfer")
|
||||
c.Header("Content-Transfer-Encoding", "binary")
|
||||
c.Header("Content-Disposition", "attachment; filename="+fileName)
|
||||
c.Header("Content-Type", "application/octet-stream")
|
||||
|
||||
// 发送文件
|
||||
c.File(filePath)
|
||||
}
|
||||
551
internal/infrastructure/http/handlers/ui_component_handler.go
Normal file
551
internal/infrastructure/http/handlers/ui_component_handler.go
Normal file
@@ -0,0 +1,551 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"tyapi-server/internal/application/product"
|
||||
"tyapi-server/internal/shared/interfaces"
|
||||
)
|
||||
|
||||
// UIComponentHandler UI组件HTTP处理器
|
||||
type UIComponentHandler struct {
|
||||
uiComponentAppService product.UIComponentApplicationService
|
||||
responseBuilder interfaces.ResponseBuilder
|
||||
validator interfaces.RequestValidator
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewUIComponentHandler 创建UI组件HTTP处理器
|
||||
func NewUIComponentHandler(
|
||||
uiComponentAppService product.UIComponentApplicationService,
|
||||
responseBuilder interfaces.ResponseBuilder,
|
||||
validator interfaces.RequestValidator,
|
||||
logger *zap.Logger,
|
||||
) *UIComponentHandler {
|
||||
return &UIComponentHandler{
|
||||
uiComponentAppService: uiComponentAppService,
|
||||
responseBuilder: responseBuilder,
|
||||
validator: validator,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateUIComponent 创建UI组件
|
||||
// @Summary 创建UI组件
|
||||
// @Description 管理员创建新的UI组件
|
||||
// @Tags UI组件管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body product.CreateUIComponentRequest true "创建UI组件请求"
|
||||
// @Success 200 {object} interfaces.Response{data=entities.UIComponent} "创建成功"
|
||||
// @Failure 400 {object} interfaces.Response "请求参数错误"
|
||||
// @Failure 500 {object} interfaces.Response "服务器内部错误"
|
||||
// @Router /api/v1/admin/ui-components [post]
|
||||
func (h *UIComponentHandler) CreateUIComponent(c *gin.Context) {
|
||||
var req product.CreateUIComponentRequest
|
||||
|
||||
// 一次性读取请求体并绑定到结构体
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.logger.Error("验证创建UI组件请求失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, fmt.Sprintf("请求参数错误: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// 使用结构体数据记录日志
|
||||
h.logger.Info("创建UI组件请求数据",
|
||||
zap.String("component_code", req.ComponentCode),
|
||||
zap.String("component_name", req.ComponentName),
|
||||
zap.String("description", req.Description),
|
||||
zap.String("version", req.Version),
|
||||
zap.Bool("is_active", req.IsActive),
|
||||
zap.Int("sort_order", req.SortOrder))
|
||||
|
||||
component, err := h.uiComponentAppService.CreateUIComponent(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
h.logger.Error("创建UI组件失败", zap.Error(err), zap.String("component_code", req.ComponentCode))
|
||||
if err == product.ErrComponentCodeAlreadyExists {
|
||||
h.responseBuilder.BadRequest(c, "UI组件编码已存在")
|
||||
return
|
||||
}
|
||||
h.responseBuilder.InternalError(c, fmt.Sprintf("创建UI组件失败: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
h.responseBuilder.Success(c, component)
|
||||
}
|
||||
|
||||
// CreateUIComponentWithFile 创建UI组件并上传文件
|
||||
// @Summary 创建UI组件并上传文件
|
||||
// @Description 管理员创建新的UI组件并同时上传文件
|
||||
// @Tags UI组件管理
|
||||
// @Accept multipart/form-data
|
||||
// @Produce json
|
||||
// @Param component_code formData string true "组件编码"
|
||||
// @Param component_name formData string true "组件名称"
|
||||
// @Param description formData string false "组件描述"
|
||||
// @Param version formData string false "组件版本"
|
||||
// @Param is_active formData bool false "是否启用"
|
||||
// @Param sort_order formData int false "排序"
|
||||
// @Param file formData file true "组件文件"
|
||||
// @Success 200 {object} interfaces.Response{data=entities.UIComponent} "创建成功"
|
||||
// @Failure 400 {object} interfaces.Response "请求参数错误"
|
||||
// @Failure 500 {object} interfaces.Response "服务器内部错误"
|
||||
// @Router /api/v1/admin/ui-components/create-with-file [post]
|
||||
func (h *UIComponentHandler) CreateUIComponentWithFile(c *gin.Context) {
|
||||
// 创建请求结构体
|
||||
var req product.CreateUIComponentRequest
|
||||
|
||||
// 从表单数据中获取组件信息
|
||||
req.ComponentCode = c.PostForm("component_code")
|
||||
req.ComponentName = c.PostForm("component_name")
|
||||
req.Description = c.PostForm("description")
|
||||
req.Version = c.PostForm("version")
|
||||
req.IsActive = c.PostForm("is_active") == "true"
|
||||
|
||||
if sortOrderStr := c.PostForm("sort_order"); sortOrderStr != "" {
|
||||
if sortOrder, err := strconv.Atoi(sortOrderStr); err == nil {
|
||||
req.SortOrder = sortOrder
|
||||
}
|
||||
}
|
||||
|
||||
// 验证必需字段
|
||||
if req.ComponentCode == "" {
|
||||
h.responseBuilder.BadRequest(c, "组件编码不能为空")
|
||||
return
|
||||
}
|
||||
if req.ComponentName == "" {
|
||||
h.responseBuilder.BadRequest(c, "组件名称不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取上传的文件
|
||||
form, err := c.MultipartForm()
|
||||
if err != nil {
|
||||
h.logger.Error("获取表单数据失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, "获取表单数据失败")
|
||||
return
|
||||
}
|
||||
|
||||
files := form.File["files"]
|
||||
if len(files) == 0 {
|
||||
h.responseBuilder.BadRequest(c, "请上传组件文件")
|
||||
return
|
||||
}
|
||||
|
||||
// 检查文件大小(100MB)
|
||||
for _, fileHeader := range files {
|
||||
if fileHeader.Size > 100*1024*1024 {
|
||||
h.responseBuilder.BadRequest(c, fmt.Sprintf("文件 %s 大小不能超过100MB", fileHeader.Filename))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 获取路径信息
|
||||
paths := c.PostFormArray("paths")
|
||||
|
||||
// 记录请求日志
|
||||
h.logger.Info("创建UI组件并上传文件请求",
|
||||
zap.String("component_code", req.ComponentCode),
|
||||
zap.String("component_name", req.ComponentName),
|
||||
zap.String("description", req.Description),
|
||||
zap.String("version", req.Version),
|
||||
zap.Bool("is_active", req.IsActive),
|
||||
zap.Int("sort_order", req.SortOrder),
|
||||
zap.Int("files_count", len(files)),
|
||||
zap.Strings("paths", paths))
|
||||
|
||||
// 调用应用服务创建组件并上传文件
|
||||
component, err := h.uiComponentAppService.CreateUIComponentWithFilesAndPaths(c.Request.Context(), req, files, paths)
|
||||
if err != nil {
|
||||
h.logger.Error("创建UI组件并上传文件失败", zap.Error(err), zap.String("component_code", req.ComponentCode))
|
||||
if err == product.ErrComponentCodeAlreadyExists {
|
||||
h.responseBuilder.BadRequest(c, "UI组件编码已存在")
|
||||
return
|
||||
}
|
||||
h.responseBuilder.InternalError(c, fmt.Sprintf("创建UI组件并上传文件失败: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
h.responseBuilder.Success(c, component)
|
||||
}
|
||||
|
||||
// GetUIComponent 获取UI组件详情
|
||||
// @Summary 获取UI组件详情
|
||||
// @Description 根据ID获取UI组件详情
|
||||
// @Tags UI组件管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "UI组件ID"
|
||||
// @Success 200 {object} interfaces.Response{data=entities.UIComponent} "获取成功"
|
||||
// @Failure 400 {object} interfaces.Response "请求参数错误"
|
||||
// @Failure 404 {object} interfaces.Response "UI组件不存在"
|
||||
// @Failure 500 {object} interfaces.Response "服务器内部错误"
|
||||
// @Router /api/v1/admin/ui-components/{id} [get]
|
||||
func (h *UIComponentHandler) GetUIComponent(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
h.responseBuilder.BadRequest(c, "UI组件ID不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
component, err := h.uiComponentAppService.GetUIComponentByID(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
h.logger.Error("获取UI组件失败", zap.Error(err), zap.String("id", id))
|
||||
h.responseBuilder.InternalError(c, "获取UI组件失败")
|
||||
return
|
||||
}
|
||||
|
||||
if component == nil {
|
||||
h.responseBuilder.NotFound(c, "UI组件不存在")
|
||||
return
|
||||
}
|
||||
|
||||
h.responseBuilder.Success(c, component)
|
||||
}
|
||||
|
||||
// UpdateUIComponent 更新UI组件
|
||||
// @Summary 更新UI组件
|
||||
// @Description 更新UI组件信息
|
||||
// @Tags UI组件管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "UI组件ID"
|
||||
// @Param request body product.UpdateUIComponentRequest true "更新UI组件请求"
|
||||
// @Success 200 {object} interfaces.Response "更新成功"
|
||||
// @Failure 400 {object} interfaces.Response "请求参数错误"
|
||||
// @Failure 404 {object} interfaces.Response "UI组件不存在"
|
||||
// @Failure 500 {object} interfaces.Response "服务器内部错误"
|
||||
// @Router /api/v1/admin/ui-components/{id} [put]
|
||||
func (h *UIComponentHandler) UpdateUIComponent(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
h.responseBuilder.BadRequest(c, "UI组件ID不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
var req product.UpdateUIComponentRequest
|
||||
|
||||
// 设置ID
|
||||
req.ID = id
|
||||
|
||||
// 验证请求
|
||||
if err := h.validator.Validate(c, &req); err != nil {
|
||||
h.logger.Error("验证更新UI组件请求失败", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
err := h.uiComponentAppService.UpdateUIComponent(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
h.logger.Error("更新UI组件失败", zap.Error(err), zap.String("id", id))
|
||||
if err == product.ErrComponentNotFound {
|
||||
h.responseBuilder.NotFound(c, "UI组件不存在")
|
||||
return
|
||||
}
|
||||
if err == product.ErrComponentCodeAlreadyExists {
|
||||
h.responseBuilder.BadRequest(c, "UI组件编码已存在")
|
||||
return
|
||||
}
|
||||
h.responseBuilder.InternalError(c, "更新UI组件失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.responseBuilder.Success(c, nil)
|
||||
}
|
||||
|
||||
// DeleteUIComponent 删除UI组件
|
||||
// @Summary 删除UI组件
|
||||
// @Description 删除UI组件
|
||||
// @Tags UI组件管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "UI组件ID"
|
||||
// @Success 200 {object} interfaces.Response "删除成功"
|
||||
// @Failure 400 {object} interfaces.Response "请求参数错误"
|
||||
// @Failure 404 {object} interfaces.Response "UI组件不存在"
|
||||
// @Failure 500 {object} interfaces.Response "服务器内部错误"
|
||||
// @Router /api/v1/admin/ui-components/{id} [delete]
|
||||
func (h *UIComponentHandler) DeleteUIComponent(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
h.responseBuilder.BadRequest(c, "UI组件ID不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
err := h.uiComponentAppService.DeleteUIComponent(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
h.logger.Error("删除UI组件失败", zap.Error(err), zap.String("id", id))
|
||||
if err == product.ErrComponentNotFound {
|
||||
h.responseBuilder.NotFound(c, "UI组件不存在")
|
||||
return
|
||||
}
|
||||
h.responseBuilder.InternalError(c, "删除UI组件失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.responseBuilder.Success(c, nil)
|
||||
}
|
||||
|
||||
// ListUIComponents 获取UI组件列表
|
||||
// @Summary 获取UI组件列表
|
||||
// @Description 获取UI组件列表,支持分页和筛选
|
||||
// @Tags UI组件管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param page query int false "页码" default(1)
|
||||
// @Param page_size query int false "每页数量" default(10)
|
||||
// @Param keyword query string false "关键词搜索"
|
||||
// @Param is_active query bool false "是否启用"
|
||||
// @Param sort_by query string false "排序字段" default(sort_order)
|
||||
// @Param sort_order query string false "排序方向" default(asc)
|
||||
// @Success 200 {object} interfaces.Response{data=product.ListUIComponentsResponse} "获取成功"
|
||||
// @Failure 500 {object} interfaces.Response "服务器内部错误"
|
||||
// @Router /api/v1/admin/ui-components [get]
|
||||
func (h *UIComponentHandler) ListUIComponents(c *gin.Context) {
|
||||
// 解析查询参数
|
||||
req := product.ListUIComponentsRequest{}
|
||||
|
||||
if pageStr := c.Query("page"); pageStr != "" {
|
||||
if page, err := strconv.Atoi(pageStr); err == nil {
|
||||
req.Page = page
|
||||
}
|
||||
}
|
||||
|
||||
if pageSizeStr := c.Query("page_size"); pageSizeStr != "" {
|
||||
if pageSize, err := strconv.Atoi(pageSizeStr); err == nil {
|
||||
req.PageSize = pageSize
|
||||
}
|
||||
}
|
||||
|
||||
req.Keyword = c.Query("keyword")
|
||||
|
||||
if isActiveStr := c.Query("is_active"); isActiveStr != "" {
|
||||
if isActive, err := strconv.ParseBool(isActiveStr); err == nil {
|
||||
req.IsActive = &isActive
|
||||
}
|
||||
}
|
||||
|
||||
req.SortBy = c.DefaultQuery("sort_by", "sort_order")
|
||||
req.SortOrder = c.DefaultQuery("sort_order", "asc")
|
||||
|
||||
response, err := h.uiComponentAppService.ListUIComponents(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
h.logger.Error("获取UI组件列表失败", zap.Error(err))
|
||||
h.responseBuilder.InternalError(c, "获取UI组件列表失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.responseBuilder.Success(c, response)
|
||||
}
|
||||
|
||||
// UploadUIComponentFile 上传UI组件文件
|
||||
// @Summary 上传UI组件文件
|
||||
// @Description 上传UI组件文件
|
||||
// @Tags UI组件管理
|
||||
// @Accept multipart/form-data
|
||||
// @Produce json
|
||||
// @Param id path string true "UI组件ID"
|
||||
// @Param file formData file true "UI组件文件(ZIP格式)"
|
||||
// @Success 200 {object} interfaces.Response{data=string} "上传成功,返回文件路径"
|
||||
// @Failure 400 {object} interfaces.Response "请求参数错误"
|
||||
// @Failure 404 {object} interfaces.Response "UI组件不存在"
|
||||
// @Failure 500 {object} interfaces.Response "服务器内部错误"
|
||||
// @Router /api/v1/admin/ui-components/{id}/upload [post]
|
||||
func (h *UIComponentHandler) UploadUIComponentFile(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
h.responseBuilder.BadRequest(c, "UI组件ID不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取上传的文件
|
||||
file, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
h.logger.Error("获取上传文件失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, "获取上传文件失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 检查文件大小(100MB)
|
||||
if file.Size > 100*1024*1024 {
|
||||
h.responseBuilder.BadRequest(c, "文件大小不能超过100MB")
|
||||
return
|
||||
}
|
||||
|
||||
filePath, err := h.uiComponentAppService.UploadUIComponentFile(c.Request.Context(), id, file)
|
||||
if err != nil {
|
||||
h.logger.Error("上传UI组件文件失败", zap.Error(err), zap.String("id", id))
|
||||
if err == product.ErrComponentNotFound {
|
||||
h.responseBuilder.NotFound(c, "UI组件不存在")
|
||||
return
|
||||
}
|
||||
if err == product.ErrInvalidFileType {
|
||||
h.responseBuilder.BadRequest(c, "文件类型错误")
|
||||
return
|
||||
}
|
||||
h.responseBuilder.InternalError(c, "上传UI组件文件失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.responseBuilder.Success(c, filePath)
|
||||
}
|
||||
|
||||
// UploadAndExtractUIComponentFile 上传并解压UI组件文件
|
||||
// @Summary 上传并解压UI组件文件
|
||||
// @Description 上传文件并自动解压到组件文件夹(仅ZIP文件支持解压)
|
||||
// @Tags UI组件管理
|
||||
// @Accept multipart/form-data
|
||||
// @Produce json
|
||||
// @Param id path string true "UI组件ID"
|
||||
// @Param file formData file true "UI组件文件(任意格式,ZIP格式支持自动解压)"
|
||||
// @Success 200 {object} interfaces.Response "上传成功"
|
||||
// @Failure 400 {object} interfaces.Response "请求参数错误"
|
||||
// @Failure 404 {object} interfaces.Response "UI组件不存在"
|
||||
// @Failure 500 {object} interfaces.Response "服务器内部错误"
|
||||
// @Router /api/v1/admin/ui-components/{id}/upload-extract [post]
|
||||
func (h *UIComponentHandler) UploadAndExtractUIComponentFile(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
h.responseBuilder.BadRequest(c, "UI组件ID不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取上传的文件
|
||||
file, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
h.logger.Error("获取上传文件失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, "获取上传文件失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 检查文件大小(100MB)
|
||||
if file.Size > 100*1024*1024 {
|
||||
h.responseBuilder.BadRequest(c, "文件大小不能超过100MB")
|
||||
return
|
||||
}
|
||||
|
||||
err = h.uiComponentAppService.UploadAndExtractUIComponentFile(c.Request.Context(), id, file)
|
||||
if err != nil {
|
||||
h.logger.Error("上传并解压UI组件文件失败", zap.Error(err), zap.String("id", id))
|
||||
if err == product.ErrComponentNotFound {
|
||||
h.responseBuilder.NotFound(c, "UI组件不存在")
|
||||
return
|
||||
}
|
||||
if err == product.ErrInvalidFileType {
|
||||
h.responseBuilder.BadRequest(c, "文件类型错误")
|
||||
return
|
||||
}
|
||||
h.responseBuilder.InternalError(c, "上传并解压UI组件文件失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.responseBuilder.Success(c, nil)
|
||||
}
|
||||
|
||||
// GetUIComponentFolderContent 获取UI组件文件夹内容
|
||||
// @Summary 获取UI组件文件夹内容
|
||||
// @Description 获取UI组件文件夹内容
|
||||
// @Tags UI组件管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "UI组件ID"
|
||||
// @Success 200 {object} interfaces.Response{data=[]FileInfo} "获取成功"
|
||||
// @Failure 400 {object} interfaces.Response "请求参数错误"
|
||||
// @Failure 404 {object} interfaces.Response "UI组件不存在"
|
||||
// @Failure 500 {object} interfaces.Response "服务器内部错误"
|
||||
// @Router /api/v1/admin/ui-components/{id}/folder-content [get]
|
||||
func (h *UIComponentHandler) GetUIComponentFolderContent(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
h.responseBuilder.BadRequest(c, "UI组件ID不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
files, err := h.uiComponentAppService.GetUIComponentFolderContent(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
h.logger.Error("获取UI组件文件夹内容失败", zap.Error(err), zap.String("id", id))
|
||||
if err == product.ErrComponentNotFound {
|
||||
h.responseBuilder.NotFound(c, "UI组件不存在")
|
||||
return
|
||||
}
|
||||
h.responseBuilder.InternalError(c, "获取UI组件文件夹内容失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.responseBuilder.Success(c, files)
|
||||
}
|
||||
|
||||
// DeleteUIComponentFolder 删除UI组件文件夹
|
||||
// @Summary 删除UI组件文件夹
|
||||
// @Description 删除UI组件文件夹
|
||||
// @Tags UI组件管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "UI组件ID"
|
||||
// @Success 200 {object} interfaces.Response "删除成功"
|
||||
// @Failure 400 {object} interfaces.Response "请求参数错误"
|
||||
// @Failure 404 {object} interfaces.Response "UI组件不存在"
|
||||
// @Failure 500 {object} interfaces.Response "服务器内部错误"
|
||||
// @Router /api/v1/admin/ui-components/{id}/folder [delete]
|
||||
func (h *UIComponentHandler) DeleteUIComponentFolder(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
h.responseBuilder.BadRequest(c, "UI组件ID不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
err := h.uiComponentAppService.DeleteUIComponentFolder(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
h.logger.Error("删除UI组件文件夹失败", zap.Error(err), zap.String("id", id))
|
||||
if err == product.ErrComponentNotFound {
|
||||
h.responseBuilder.NotFound(c, "UI组件不存在")
|
||||
return
|
||||
}
|
||||
h.responseBuilder.InternalError(c, "删除UI组件文件夹失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.responseBuilder.Success(c, nil)
|
||||
}
|
||||
|
||||
// DownloadUIComponentFile 下载UI组件文件
|
||||
// @Summary 下载UI组件文件
|
||||
// @Description 下载UI组件文件
|
||||
// @Tags UI组件管理
|
||||
// @Accept json
|
||||
// @Produce application/octet-stream
|
||||
// @Param id path string true "UI组件ID"
|
||||
// @Success 200 {file} file "文件内容"
|
||||
// @Failure 400 {object} interfaces.Response "请求参数错误"
|
||||
// @Failure 404 {object} interfaces.Response "UI组件不存在或文件不存在"
|
||||
// @Failure 500 {object} interfaces.Response "服务器内部错误"
|
||||
// @Router /api/v1/admin/ui-components/{id}/download [get]
|
||||
func (h *UIComponentHandler) DownloadUIComponentFile(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
h.responseBuilder.BadRequest(c, "UI组件ID不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
filePath, err := h.uiComponentAppService.DownloadUIComponentFile(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
h.logger.Error("下载UI组件文件失败", zap.Error(err), zap.String("id", id))
|
||||
if err == product.ErrComponentNotFound {
|
||||
h.responseBuilder.NotFound(c, "UI组件不存在")
|
||||
return
|
||||
}
|
||||
if err == product.ErrComponentFileNotFound {
|
||||
h.responseBuilder.NotFound(c, "UI组件文件不存在")
|
||||
return
|
||||
}
|
||||
h.responseBuilder.InternalError(c, "下载UI组件文件失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 这里应该实现文件下载逻辑,返回文件内容
|
||||
// 由于我们使用的是本地文件存储,可以直接返回文件
|
||||
c.File(filePath)
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package routes
|
||||
|
||||
import (
|
||||
"tyapi-server/internal/infrastructure/http/handlers"
|
||||
component_report "tyapi-server/internal/shared/component_report"
|
||||
sharedhttp "tyapi-server/internal/shared/http"
|
||||
"tyapi-server/internal/shared/middleware"
|
||||
|
||||
@@ -11,7 +12,7 @@ import (
|
||||
// ProductRoutes 产品路由
|
||||
type ProductRoutes struct {
|
||||
productHandler *handlers.ProductHandler
|
||||
componentReportHandler *handlers.ComponentReportHandler
|
||||
componentReportHandler *component_report.ComponentReportHandler
|
||||
auth *middleware.JWTAuthMiddleware
|
||||
optionalAuth *middleware.OptionalAuthMiddleware
|
||||
logger *zap.Logger
|
||||
@@ -20,7 +21,7 @@ type ProductRoutes struct {
|
||||
// NewProductRoutes 创建产品路由
|
||||
func NewProductRoutes(
|
||||
productHandler *handlers.ProductHandler,
|
||||
componentReportHandler *handlers.ComponentReportHandler,
|
||||
componentReportHandler *component_report.ComponentReportHandler,
|
||||
auth *middleware.JWTAuthMiddleware,
|
||||
optionalAuth *middleware.OptionalAuthMiddleware,
|
||||
logger *zap.Logger,
|
||||
@@ -58,19 +59,24 @@ func (r *ProductRoutes) Register(router *sharedhttp.GinRouter) {
|
||||
|
||||
// 订阅产品(需要认证)
|
||||
products.POST("/:id/subscribe", r.auth.Handle(), r.productHandler.SubscribeProduct)
|
||||
}
|
||||
|
||||
// 组件报告相关路由(需要认证)
|
||||
componentReport := products.Group("/:id/component-report", r.auth.Handle())
|
||||
{
|
||||
// 获取报告下载信息
|
||||
componentReport.GET("/info", r.componentReportHandler.GetReportDownloadInfo)
|
||||
|
||||
// 创建支付订单(暂时注释,后续实现)
|
||||
// componentReport.POST("/create-order", r.componentReportHandler.CreateReportPaymentOrder)
|
||||
|
||||
// 下载报告文件
|
||||
componentReport.GET("/download/:downloadId", r.componentReportHandler.DownloadReport)
|
||||
}
|
||||
// 组件报告 - 需要认证
|
||||
componentReport := engine.Group("/api/v1/component-report", r.auth.Handle())
|
||||
{
|
||||
// 生成并下载 example.json 文件
|
||||
componentReport.POST("/download-example-json", r.componentReportHandler.DownloadExampleJSON)
|
||||
// 生成并下载示例报告ZIP文件
|
||||
componentReport.POST("/generate-and-download", r.componentReportHandler.GenerateAndDownloadZip)
|
||||
}
|
||||
|
||||
// 产品组件报告相关接口 - 需要认证
|
||||
componentReportGroup := products.Group("/:id/component-report", r.auth.Handle())
|
||||
{
|
||||
componentReportGroup.GET("/check", r.componentReportHandler.CheckDownloadAvailability)
|
||||
componentReportGroup.GET("/info", r.componentReportHandler.GetDownloadInfo)
|
||||
componentReportGroup.POST("/create-order", r.componentReportHandler.CreatePaymentOrder)
|
||||
componentReportGroup.GET("/check-payment/:orderId", r.componentReportHandler.CheckPaymentStatus)
|
||||
}
|
||||
|
||||
// 分类 - 公开接口
|
||||
@@ -103,9 +109,6 @@ func (r *ProductRoutes) Register(router *sharedhttp.GinRouter) {
|
||||
// 取消订阅
|
||||
subscriptions.POST("/:id/cancel", r.productHandler.CancelMySubscription)
|
||||
}
|
||||
|
||||
// 我的组件报告下载历史
|
||||
my.GET("/component-reports", r.componentReportHandler.GetUserDownloadHistory)
|
||||
}
|
||||
|
||||
r.logger.Info("产品路由注册完成")
|
||||
|
||||
48
internal/infrastructure/http/routes/ui_component_routes.go
Normal file
48
internal/infrastructure/http/routes/ui_component_routes.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"tyapi-server/internal/infrastructure/http/handlers"
|
||||
"tyapi-server/internal/shared/interfaces"
|
||||
)
|
||||
|
||||
// UIComponentRoutes UI组件路由
|
||||
type UIComponentRoutes struct {
|
||||
uiComponentHandler *handlers.UIComponentHandler
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewUIComponentRoutes 创建UI组件路由
|
||||
func NewUIComponentRoutes(
|
||||
uiComponentHandler *handlers.UIComponentHandler,
|
||||
logger *zap.Logger,
|
||||
) *UIComponentRoutes {
|
||||
return &UIComponentRoutes{
|
||||
uiComponentHandler: uiComponentHandler,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterRoutes 注册UI组件路由
|
||||
func (r *UIComponentRoutes) RegisterRoutes(router *gin.RouterGroup, authMiddleware interfaces.Middleware) {
|
||||
uiComponentGroup := router.Group("/ui-components")
|
||||
uiComponentGroup.Use(authMiddleware.Handle())
|
||||
{
|
||||
// UI组件管理
|
||||
uiComponentGroup.POST("", r.uiComponentHandler.CreateUIComponent) // 创建UI组件
|
||||
uiComponentGroup.POST("/create-with-file", r.uiComponentHandler.CreateUIComponentWithFile) // 创建UI组件并上传文件
|
||||
uiComponentGroup.GET("", r.uiComponentHandler.ListUIComponents) // 获取UI组件列表
|
||||
uiComponentGroup.GET("/:id", r.uiComponentHandler.GetUIComponent) // 获取UI组件详情
|
||||
uiComponentGroup.PUT("/:id", r.uiComponentHandler.UpdateUIComponent) // 更新UI组件
|
||||
uiComponentGroup.DELETE("/:id", r.uiComponentHandler.DeleteUIComponent) // 删除UI组件
|
||||
|
||||
// 文件操作
|
||||
uiComponentGroup.POST("/:id/upload", r.uiComponentHandler.UploadUIComponentFile) // 上传UI组件文件
|
||||
uiComponentGroup.POST("/:id/upload-extract", r.uiComponentHandler.UploadAndExtractUIComponentFile) // 上传并解压UI组件文件
|
||||
uiComponentGroup.GET("/:id/folder-content", r.uiComponentHandler.GetUIComponentFolderContent) // 获取UI组件文件夹内容
|
||||
uiComponentGroup.DELETE("/:id/folder", r.uiComponentHandler.DeleteUIComponentFolder) // 删除UI组件文件夹
|
||||
uiComponentGroup.GET("/:id/download", r.uiComponentHandler.DownloadUIComponentFile) // 下载UI组件文件
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user