687 lines
21 KiB
Go
687 lines
21 KiB
Go
|
|
package product
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"context"
|
|||
|
|
"fmt"
|
|||
|
|
"io"
|
|||
|
|
"mime/multipart"
|
|||
|
|
"path/filepath"
|
|||
|
|
"strings"
|
|||
|
|
|
|||
|
|
"tyapi-server/internal/domains/product/entities"
|
|||
|
|
"tyapi-server/internal/domains/product/repositories"
|
|||
|
|
|
|||
|
|
"github.com/shopspring/decimal"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// UIComponentApplicationService UI组件应用服务接口
|
|||
|
|
type UIComponentApplicationService interface {
|
|||
|
|
// 基本CRUD操作
|
|||
|
|
CreateUIComponent(ctx context.Context, req CreateUIComponentRequest) (entities.UIComponent, error)
|
|||
|
|
CreateUIComponentWithFile(ctx context.Context, req CreateUIComponentRequest, file *multipart.FileHeader) (entities.UIComponent, error)
|
|||
|
|
CreateUIComponentWithFiles(ctx context.Context, req CreateUIComponentRequest, files []*multipart.FileHeader) (entities.UIComponent, error)
|
|||
|
|
CreateUIComponentWithFilesAndPaths(ctx context.Context, req CreateUIComponentRequest, files []*multipart.FileHeader, paths []string) (entities.UIComponent, error)
|
|||
|
|
GetUIComponentByID(ctx context.Context, id string) (*entities.UIComponent, error)
|
|||
|
|
GetUIComponentByCode(ctx context.Context, code string) (*entities.UIComponent, error)
|
|||
|
|
UpdateUIComponent(ctx context.Context, req UpdateUIComponentRequest) error
|
|||
|
|
DeleteUIComponent(ctx context.Context, id string) error
|
|||
|
|
ListUIComponents(ctx context.Context, req ListUIComponentsRequest) (ListUIComponentsResponse, error)
|
|||
|
|
|
|||
|
|
// 文件操作
|
|||
|
|
UploadUIComponentFile(ctx context.Context, id string, file *multipart.FileHeader) (string, error)
|
|||
|
|
UploadAndExtractUIComponentFile(ctx context.Context, id string, file *multipart.FileHeader) error
|
|||
|
|
DownloadUIComponentFile(ctx context.Context, id string) (string, error)
|
|||
|
|
GetUIComponentFolderContent(ctx context.Context, id string) ([]FileInfo, error)
|
|||
|
|
DeleteUIComponentFolder(ctx context.Context, id string) error
|
|||
|
|
|
|||
|
|
// 产品关联操作
|
|||
|
|
AssociateUIComponentToProduct(ctx context.Context, req AssociateUIComponentRequest) error
|
|||
|
|
GetProductUIComponents(ctx context.Context, productID string) ([]entities.ProductUIComponent, error)
|
|||
|
|
RemoveUIComponentFromProduct(ctx context.Context, productID, componentID string) error
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// CreateUIComponentRequest 创建UI组件请求
|
|||
|
|
type CreateUIComponentRequest struct {
|
|||
|
|
ComponentCode string `json:"component_code" binding:"required"`
|
|||
|
|
ComponentName string `json:"component_name" binding:"required"`
|
|||
|
|
Description string `json:"description"`
|
|||
|
|
Version string `json:"version"`
|
|||
|
|
IsActive bool `json:"is_active"`
|
|||
|
|
SortOrder int `json:"sort_order"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// UpdateUIComponentRequest 更新UI组件请求
|
|||
|
|
type UpdateUIComponentRequest struct {
|
|||
|
|
ID string `json:"id" binding:"required"`
|
|||
|
|
ComponentCode string `json:"component_code"`
|
|||
|
|
ComponentName string `json:"component_name"`
|
|||
|
|
Description string `json:"description"`
|
|||
|
|
Version string `json:"version"`
|
|||
|
|
IsActive *bool `json:"is_active"`
|
|||
|
|
SortOrder *int `json:"sort_order"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ListUIComponentsRequest 获取UI组件列表请求
|
|||
|
|
type ListUIComponentsRequest struct {
|
|||
|
|
Page int `form:"page,default=1"`
|
|||
|
|
PageSize int `form:"page_size,default=10"`
|
|||
|
|
Keyword string `form:"keyword"`
|
|||
|
|
IsActive *bool `form:"is_active"`
|
|||
|
|
SortBy string `form:"sort_by,default=sort_order"`
|
|||
|
|
SortOrder string `form:"sort_order,default=asc"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ListUIComponentsResponse 获取UI组件列表响应
|
|||
|
|
type ListUIComponentsResponse struct {
|
|||
|
|
Components []entities.UIComponent `json:"components"`
|
|||
|
|
Total int64 `json:"total"`
|
|||
|
|
Page int `json:"page"`
|
|||
|
|
PageSize int `json:"page_size"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// AssociateUIComponentRequest 关联UI组件到产品请求
|
|||
|
|
type AssociateUIComponentRequest struct {
|
|||
|
|
ProductID string `json:"product_id" binding:"required"`
|
|||
|
|
UIComponentID string `json:"ui_component_id" binding:"required"`
|
|||
|
|
Price float64 `json:"price" binding:"required,min=0"`
|
|||
|
|
IsEnabled bool `json:"is_enabled"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// UIComponentApplicationServiceImpl UI组件应用服务实现
|
|||
|
|
type UIComponentApplicationServiceImpl struct {
|
|||
|
|
uiComponentRepo repositories.UIComponentRepository
|
|||
|
|
productUIComponentRepo repositories.ProductUIComponentRepository
|
|||
|
|
fileStorageService FileStorageService
|
|||
|
|
fileService UIComponentFileService
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// FileStorageService 文件存储服务接口
|
|||
|
|
type FileStorageService interface {
|
|||
|
|
StoreFile(ctx context.Context, file io.Reader, filename string) (string, error)
|
|||
|
|
GetFileURL(ctx context.Context, filePath string) (string, error)
|
|||
|
|
DeleteFile(ctx context.Context, filePath string) error
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// NewUIComponentApplicationService 创建UI组件应用服务
|
|||
|
|
func NewUIComponentApplicationService(
|
|||
|
|
uiComponentRepo repositories.UIComponentRepository,
|
|||
|
|
productUIComponentRepo repositories.ProductUIComponentRepository,
|
|||
|
|
fileStorageService FileStorageService,
|
|||
|
|
fileService UIComponentFileService,
|
|||
|
|
) UIComponentApplicationService {
|
|||
|
|
return &UIComponentApplicationServiceImpl{
|
|||
|
|
uiComponentRepo: uiComponentRepo,
|
|||
|
|
productUIComponentRepo: productUIComponentRepo,
|
|||
|
|
fileStorageService: fileStorageService,
|
|||
|
|
fileService: fileService,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// CreateUIComponent 创建UI组件
|
|||
|
|
func (s *UIComponentApplicationServiceImpl) CreateUIComponent(ctx context.Context, req CreateUIComponentRequest) (entities.UIComponent, error) {
|
|||
|
|
// 检查编码是否已存在
|
|||
|
|
existing, _ := s.uiComponentRepo.GetByCode(ctx, req.ComponentCode)
|
|||
|
|
if existing != nil {
|
|||
|
|
return entities.UIComponent{}, ErrComponentCodeAlreadyExists
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
component := entities.UIComponent{
|
|||
|
|
ComponentCode: req.ComponentCode,
|
|||
|
|
ComponentName: req.ComponentName,
|
|||
|
|
Description: req.Description,
|
|||
|
|
Version: req.Version,
|
|||
|
|
IsActive: req.IsActive,
|
|||
|
|
SortOrder: req.SortOrder,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return s.uiComponentRepo.Create(ctx, component)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// CreateUIComponentWithFile 创建UI组件并上传文件
|
|||
|
|
func (s *UIComponentApplicationServiceImpl) CreateUIComponentWithFile(ctx context.Context, req CreateUIComponentRequest, file *multipart.FileHeader) (entities.UIComponent, error) {
|
|||
|
|
// 检查编码是否已存在
|
|||
|
|
existing, _ := s.uiComponentRepo.GetByCode(ctx, req.ComponentCode)
|
|||
|
|
if existing != nil {
|
|||
|
|
return entities.UIComponent{}, ErrComponentCodeAlreadyExists
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建组件
|
|||
|
|
component := entities.UIComponent{
|
|||
|
|
ComponentCode: req.ComponentCode,
|
|||
|
|
ComponentName: req.ComponentName,
|
|||
|
|
Description: req.Description,
|
|||
|
|
Version: req.Version,
|
|||
|
|
IsActive: req.IsActive,
|
|||
|
|
SortOrder: req.SortOrder,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
createdComponent, err := s.uiComponentRepo.Create(ctx, component)
|
|||
|
|
if err != nil {
|
|||
|
|
return entities.UIComponent{}, err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果有文件,则上传并处理文件
|
|||
|
|
if file != nil {
|
|||
|
|
// 打开上传的文件
|
|||
|
|
src, err := file.Open()
|
|||
|
|
if err != nil {
|
|||
|
|
// 删除已创建的组件记录
|
|||
|
|
_ = s.uiComponentRepo.Delete(ctx, createdComponent.ID)
|
|||
|
|
return entities.UIComponent{}, fmt.Errorf("打开上传文件失败: %w", err)
|
|||
|
|
}
|
|||
|
|
defer src.Close()
|
|||
|
|
|
|||
|
|
// 上传并解压文件
|
|||
|
|
if err := s.fileService.UploadAndExtract(ctx, createdComponent.ID, createdComponent.ComponentCode, src, file.Filename); err != nil {
|
|||
|
|
// 删除已创建的组件记录
|
|||
|
|
_ = s.uiComponentRepo.Delete(ctx, createdComponent.ID)
|
|||
|
|
return entities.UIComponent{}, err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取文件类型
|
|||
|
|
fileType := strings.ToLower(filepath.Ext(file.Filename))
|
|||
|
|
|
|||
|
|
// 更新组件信息
|
|||
|
|
folderPath := "resources/Pure Component/src/ui"
|
|||
|
|
createdComponent.FolderPath = &folderPath
|
|||
|
|
createdComponent.FileType = &fileType
|
|||
|
|
|
|||
|
|
// 仅对ZIP文件设置已解压标记
|
|||
|
|
if fileType == ".zip" {
|
|||
|
|
createdComponent.IsExtracted = true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新组件信息
|
|||
|
|
err = s.uiComponentRepo.Update(ctx, createdComponent)
|
|||
|
|
if err != nil {
|
|||
|
|
// 尝试删除已创建的组件记录
|
|||
|
|
_ = s.uiComponentRepo.Delete(ctx, createdComponent.ID)
|
|||
|
|
return entities.UIComponent{}, err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return createdComponent, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return createdComponent, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// CreateUIComponentWithFiles 创建UI组件并上传多个文件
|
|||
|
|
func (s *UIComponentApplicationServiceImpl) CreateUIComponentWithFiles(ctx context.Context, req CreateUIComponentRequest, files []*multipart.FileHeader) (entities.UIComponent, error) {
|
|||
|
|
// 检查编码是否已存在
|
|||
|
|
existing, _ := s.uiComponentRepo.GetByCode(ctx, req.ComponentCode)
|
|||
|
|
if existing != nil {
|
|||
|
|
return entities.UIComponent{}, ErrComponentCodeAlreadyExists
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建组件
|
|||
|
|
component := entities.UIComponent{
|
|||
|
|
ComponentCode: req.ComponentCode,
|
|||
|
|
ComponentName: req.ComponentName,
|
|||
|
|
Description: req.Description,
|
|||
|
|
Version: req.Version,
|
|||
|
|
IsActive: req.IsActive,
|
|||
|
|
SortOrder: req.SortOrder,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
createdComponent, err := s.uiComponentRepo.Create(ctx, component)
|
|||
|
|
if err != nil {
|
|||
|
|
return entities.UIComponent{}, err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果有文件,则上传并处理文件
|
|||
|
|
if len(files) > 0 {
|
|||
|
|
// 处理每个文件
|
|||
|
|
var extractedFiles []string
|
|||
|
|
for _, fileHeader := range files {
|
|||
|
|
// 打开上传的文件
|
|||
|
|
src, err := fileHeader.Open()
|
|||
|
|
if err != nil {
|
|||
|
|
// 删除已创建的组件记录
|
|||
|
|
_ = s.uiComponentRepo.Delete(ctx, createdComponent.ID)
|
|||
|
|
return entities.UIComponent{}, fmt.Errorf("打开上传文件失败: %w", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 上传并解压文件
|
|||
|
|
if err := s.fileService.UploadAndExtract(ctx, createdComponent.ID, createdComponent.ComponentCode, src, fileHeader.Filename); err != nil {
|
|||
|
|
src.Close()
|
|||
|
|
// 删除已创建的组件记录
|
|||
|
|
_ = s.uiComponentRepo.Delete(ctx, createdComponent.ID)
|
|||
|
|
return entities.UIComponent{}, err
|
|||
|
|
}
|
|||
|
|
src.Close()
|
|||
|
|
|
|||
|
|
// 记录已处理的文件,用于日志
|
|||
|
|
extractedFiles = append(extractedFiles, fileHeader.Filename)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新组件信息
|
|||
|
|
folderPath := "resources/Pure Component/src/ui"
|
|||
|
|
createdComponent.FolderPath = &folderPath
|
|||
|
|
|
|||
|
|
// 检查是否有ZIP文件
|
|||
|
|
hasZipFile := false
|
|||
|
|
for _, fileHeader := range files {
|
|||
|
|
if strings.HasSuffix(strings.ToLower(fileHeader.Filename), ".zip") {
|
|||
|
|
hasZipFile = true
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果有ZIP文件,则标记为已解压
|
|||
|
|
if hasZipFile {
|
|||
|
|
createdComponent.IsExtracted = true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新组件信息
|
|||
|
|
err = s.uiComponentRepo.Update(ctx, createdComponent)
|
|||
|
|
if err != nil {
|
|||
|
|
// 尝试删除已创建的组件记录
|
|||
|
|
_ = s.uiComponentRepo.Delete(ctx, createdComponent.ID)
|
|||
|
|
return entities.UIComponent{}, err
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return createdComponent, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// CreateUIComponentWithFilesAndPaths 创建UI组件并上传带路径的文件
|
|||
|
|
func (s *UIComponentApplicationServiceImpl) CreateUIComponentWithFilesAndPaths(ctx context.Context, req CreateUIComponentRequest, files []*multipart.FileHeader, paths []string) (entities.UIComponent, error) {
|
|||
|
|
// 检查编码是否已存在
|
|||
|
|
existing, _ := s.uiComponentRepo.GetByCode(ctx, req.ComponentCode)
|
|||
|
|
if existing != nil {
|
|||
|
|
return entities.UIComponent{}, ErrComponentCodeAlreadyExists
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建组件
|
|||
|
|
component := entities.UIComponent{
|
|||
|
|
ComponentCode: req.ComponentCode,
|
|||
|
|
ComponentName: req.ComponentName,
|
|||
|
|
Description: req.Description,
|
|||
|
|
Version: req.Version,
|
|||
|
|
IsActive: req.IsActive,
|
|||
|
|
SortOrder: req.SortOrder,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
createdComponent, err := s.uiComponentRepo.Create(ctx, component)
|
|||
|
|
if err != nil {
|
|||
|
|
return entities.UIComponent{}, err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果有文件,则上传并处理文件
|
|||
|
|
if len(files) > 0 {
|
|||
|
|
// 打开所有文件
|
|||
|
|
var readers []io.Reader
|
|||
|
|
var filenames []string
|
|||
|
|
var filePaths []string
|
|||
|
|
|
|||
|
|
for i, fileHeader := range files {
|
|||
|
|
// 打开上传的文件
|
|||
|
|
src, err := fileHeader.Open()
|
|||
|
|
if err != nil {
|
|||
|
|
// 关闭已打开的文件
|
|||
|
|
for _, r := range readers {
|
|||
|
|
if closer, ok := r.(io.Closer); ok {
|
|||
|
|
closer.Close()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// 删除已创建的组件记录
|
|||
|
|
_ = s.uiComponentRepo.Delete(ctx, createdComponent.ID)
|
|||
|
|
return entities.UIComponent{}, fmt.Errorf("打开上传文件失败: %w", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
readers = append(readers, src)
|
|||
|
|
filenames = append(filenames, fileHeader.Filename)
|
|||
|
|
|
|||
|
|
// 确定文件路径
|
|||
|
|
var path string
|
|||
|
|
if i < len(paths) && paths[i] != "" {
|
|||
|
|
path = paths[i]
|
|||
|
|
} else {
|
|||
|
|
path = fileHeader.Filename
|
|||
|
|
}
|
|||
|
|
filePaths = append(filePaths, path)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 使用新的批量上传方法
|
|||
|
|
if err := s.fileService.UploadMultipleFiles(ctx, createdComponent.ID, createdComponent.ComponentCode, readers, filenames, filePaths); err != nil {
|
|||
|
|
// 关闭已打开的文件
|
|||
|
|
for _, r := range readers {
|
|||
|
|
if closer, ok := r.(io.Closer); ok {
|
|||
|
|
closer.Close()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// 删除已创建的组件记录
|
|||
|
|
_ = s.uiComponentRepo.Delete(ctx, createdComponent.ID)
|
|||
|
|
return entities.UIComponent{}, err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 关闭所有文件
|
|||
|
|
for _, r := range readers {
|
|||
|
|
if closer, ok := r.(io.Closer); ok {
|
|||
|
|
closer.Close()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新组件信息
|
|||
|
|
folderPath := "resources/Pure Component/src/ui"
|
|||
|
|
createdComponent.FolderPath = &folderPath
|
|||
|
|
|
|||
|
|
// 检查是否有ZIP文件
|
|||
|
|
hasZipFile := false
|
|||
|
|
for _, fileHeader := range files {
|
|||
|
|
if strings.HasSuffix(strings.ToLower(fileHeader.Filename), ".zip") {
|
|||
|
|
hasZipFile = true
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果有ZIP文件,则标记为已解压
|
|||
|
|
if hasZipFile {
|
|||
|
|
createdComponent.IsExtracted = true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新组件信息
|
|||
|
|
err = s.uiComponentRepo.Update(ctx, createdComponent)
|
|||
|
|
if err != nil {
|
|||
|
|
// 尝试删除已创建的组件记录
|
|||
|
|
_ = s.uiComponentRepo.Delete(ctx, createdComponent.ID)
|
|||
|
|
return entities.UIComponent{}, err
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return createdComponent, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetUIComponentByID 根据ID获取UI组件
|
|||
|
|
func (s *UIComponentApplicationServiceImpl) GetUIComponentByID(ctx context.Context, id string) (*entities.UIComponent, error) {
|
|||
|
|
return s.uiComponentRepo.GetByID(ctx, id)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetUIComponentByCode 根据编码获取UI组件
|
|||
|
|
func (s *UIComponentApplicationServiceImpl) GetUIComponentByCode(ctx context.Context, code string) (*entities.UIComponent, error) {
|
|||
|
|
return s.uiComponentRepo.GetByCode(ctx, code)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// UpdateUIComponent 更新UI组件
|
|||
|
|
func (s *UIComponentApplicationServiceImpl) UpdateUIComponent(ctx context.Context, req UpdateUIComponentRequest) error {
|
|||
|
|
component, err := s.uiComponentRepo.GetByID(ctx, req.ID)
|
|||
|
|
if err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
if component == nil {
|
|||
|
|
return ErrComponentNotFound
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果更新编码,检查是否与其他组件冲突
|
|||
|
|
if req.ComponentCode != "" && req.ComponentCode != component.ComponentCode {
|
|||
|
|
existing, _ := s.uiComponentRepo.GetByCode(ctx, req.ComponentCode)
|
|||
|
|
if existing != nil && existing.ID != req.ID {
|
|||
|
|
return ErrComponentCodeAlreadyExists
|
|||
|
|
}
|
|||
|
|
component.ComponentCode = req.ComponentCode
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if req.ComponentName != "" {
|
|||
|
|
component.ComponentName = req.ComponentName
|
|||
|
|
}
|
|||
|
|
if req.Description != "" {
|
|||
|
|
component.Description = req.Description
|
|||
|
|
}
|
|||
|
|
if req.Version != "" {
|
|||
|
|
component.Version = req.Version
|
|||
|
|
}
|
|||
|
|
if req.IsActive != nil {
|
|||
|
|
component.IsActive = *req.IsActive
|
|||
|
|
}
|
|||
|
|
if req.SortOrder != nil {
|
|||
|
|
component.SortOrder = *req.SortOrder
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return s.uiComponentRepo.Update(ctx, *component)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// DeleteUIComponent 删除UI组件
|
|||
|
|
func (s *UIComponentApplicationServiceImpl) DeleteUIComponent(ctx context.Context, id string) error {
|
|||
|
|
component, err := s.uiComponentRepo.GetByID(ctx, id)
|
|||
|
|
if err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
if component == nil {
|
|||
|
|
return ErrComponentNotFound
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 删除关联的文件
|
|||
|
|
if component.FilePath != nil {
|
|||
|
|
_ = s.fileStorageService.DeleteFile(ctx, *component.FilePath)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return s.uiComponentRepo.Delete(ctx, id)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ListUIComponents 获取UI组件列表
|
|||
|
|
func (s *UIComponentApplicationServiceImpl) ListUIComponents(ctx context.Context, req ListUIComponentsRequest) (ListUIComponentsResponse, error) {
|
|||
|
|
filters := make(map[string]interface{})
|
|||
|
|
|
|||
|
|
if req.Keyword != "" {
|
|||
|
|
filters["keyword"] = req.Keyword
|
|||
|
|
}
|
|||
|
|
if req.IsActive != nil {
|
|||
|
|
filters["is_active"] = *req.IsActive
|
|||
|
|
}
|
|||
|
|
filters["page"] = req.Page
|
|||
|
|
filters["page_size"] = req.PageSize
|
|||
|
|
filters["sort_by"] = req.SortBy
|
|||
|
|
filters["sort_order"] = req.SortOrder
|
|||
|
|
|
|||
|
|
components, total, err := s.uiComponentRepo.List(ctx, filters)
|
|||
|
|
if err != nil {
|
|||
|
|
return ListUIComponentsResponse{}, err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return ListUIComponentsResponse{
|
|||
|
|
Components: components,
|
|||
|
|
Total: total,
|
|||
|
|
Page: req.Page,
|
|||
|
|
PageSize: req.PageSize,
|
|||
|
|
}, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// UploadUIComponentFile 上传UI组件文件
|
|||
|
|
func (s *UIComponentApplicationServiceImpl) UploadUIComponentFile(ctx context.Context, id string, file *multipart.FileHeader) (string, error) {
|
|||
|
|
component, err := s.uiComponentRepo.GetByID(ctx, id)
|
|||
|
|
if err != nil {
|
|||
|
|
return "", err
|
|||
|
|
}
|
|||
|
|
if component == nil {
|
|||
|
|
return "", ErrComponentNotFound
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查文件大小(100MB)
|
|||
|
|
if file.Size > 100*1024*1024 {
|
|||
|
|
return "", ErrInvalidFileType // 复用此错误表示文件太大
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 打开上传的文件
|
|||
|
|
src, err := file.Open()
|
|||
|
|
if err != nil {
|
|||
|
|
return "", err
|
|||
|
|
}
|
|||
|
|
defer src.Close()
|
|||
|
|
|
|||
|
|
// 生成文件路径
|
|||
|
|
filePath := filepath.Join("ui-components", id+"_"+file.Filename)
|
|||
|
|
|
|||
|
|
// 存储文件
|
|||
|
|
storedPath, err := s.fileStorageService.StoreFile(ctx, src, filePath)
|
|||
|
|
if err != nil {
|
|||
|
|
return "", err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 删除旧文件
|
|||
|
|
if component.FilePath != nil {
|
|||
|
|
_ = s.fileStorageService.DeleteFile(ctx, *component.FilePath)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取文件类型
|
|||
|
|
fileType := strings.ToLower(filepath.Ext(file.Filename))
|
|||
|
|
|
|||
|
|
// 更新组件信息
|
|||
|
|
component.FilePath = &storedPath
|
|||
|
|
component.FileSize = &file.Size
|
|||
|
|
component.FileType = &fileType
|
|||
|
|
if err := s.uiComponentRepo.Update(ctx, *component); err != nil {
|
|||
|
|
// 如果更新失败,尝试删除已上传的文件
|
|||
|
|
_ = s.fileStorageService.DeleteFile(ctx, storedPath)
|
|||
|
|
return "", err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return storedPath, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// DownloadUIComponentFile 下载UI组件文件
|
|||
|
|
func (s *UIComponentApplicationServiceImpl) DownloadUIComponentFile(ctx context.Context, id string) (string, error) {
|
|||
|
|
component, err := s.uiComponentRepo.GetByID(ctx, id)
|
|||
|
|
if err != nil {
|
|||
|
|
return "", err
|
|||
|
|
}
|
|||
|
|
if component == nil {
|
|||
|
|
return "", ErrComponentNotFound
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if component.FilePath == nil {
|
|||
|
|
return "", ErrComponentFileNotFound
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return s.fileStorageService.GetFileURL(ctx, *component.FilePath)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// AssociateUIComponentToProduct 关联UI组件到产品
|
|||
|
|
func (s *UIComponentApplicationServiceImpl) AssociateUIComponentToProduct(ctx context.Context, req AssociateUIComponentRequest) error {
|
|||
|
|
// 检查组件是否存在
|
|||
|
|
component, err := s.uiComponentRepo.GetByID(ctx, req.UIComponentID)
|
|||
|
|
if err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
if component == nil {
|
|||
|
|
return ErrComponentNotFound
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建关联
|
|||
|
|
relation := entities.ProductUIComponent{
|
|||
|
|
ProductID: req.ProductID,
|
|||
|
|
UIComponentID: req.UIComponentID,
|
|||
|
|
Price: decimal.NewFromFloat(req.Price),
|
|||
|
|
IsEnabled: req.IsEnabled,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_, err = s.productUIComponentRepo.Create(ctx, relation)
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetProductUIComponents 获取产品的UI组件列表
|
|||
|
|
func (s *UIComponentApplicationServiceImpl) GetProductUIComponents(ctx context.Context, productID string) ([]entities.ProductUIComponent, error) {
|
|||
|
|
return s.productUIComponentRepo.GetByProductID(ctx, productID)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// RemoveUIComponentFromProduct 从产品中移除UI组件
|
|||
|
|
func (s *UIComponentApplicationServiceImpl) RemoveUIComponentFromProduct(ctx context.Context, productID, componentID string) error {
|
|||
|
|
// 查找关联记录
|
|||
|
|
relations, err := s.productUIComponentRepo.GetByProductID(ctx, productID)
|
|||
|
|
if err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 找到要删除的关联记录
|
|||
|
|
var relationID string
|
|||
|
|
for _, relation := range relations {
|
|||
|
|
if relation.UIComponentID == componentID {
|
|||
|
|
relationID = relation.ID
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if relationID == "" {
|
|||
|
|
return ErrProductComponentRelationNotFound
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return s.productUIComponentRepo.Delete(ctx, relationID)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// UploadAndExtractUIComponentFile 上传并解压UI组件文件
|
|||
|
|
func (s *UIComponentApplicationServiceImpl) UploadAndExtractUIComponentFile(ctx context.Context, id string, file *multipart.FileHeader) error {
|
|||
|
|
// 获取组件信息
|
|||
|
|
component, err := s.uiComponentRepo.GetByID(ctx, id)
|
|||
|
|
if err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
if component == nil {
|
|||
|
|
return ErrComponentNotFound
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 打开上传的文件
|
|||
|
|
src, err := file.Open()
|
|||
|
|
if err != nil {
|
|||
|
|
return fmt.Errorf("打开上传文件失败: %w", err)
|
|||
|
|
}
|
|||
|
|
defer src.Close()
|
|||
|
|
|
|||
|
|
// 上传并解压文件
|
|||
|
|
if err := s.fileService.UploadAndExtract(ctx, id, component.ComponentCode, src, file.Filename); err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取文件类型
|
|||
|
|
fileType := strings.ToLower(filepath.Ext(file.Filename))
|
|||
|
|
|
|||
|
|
// 更新组件信息
|
|||
|
|
folderPath := "resources/Pure Component/src/ui"
|
|||
|
|
component.FolderPath = &folderPath
|
|||
|
|
component.FileType = &fileType
|
|||
|
|
|
|||
|
|
// 仅对ZIP文件设置已解压标记
|
|||
|
|
if fileType == ".zip" {
|
|||
|
|
component.IsExtracted = true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return s.uiComponentRepo.Update(ctx, *component)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetUIComponentFolderContent 获取UI组件文件夹内容
|
|||
|
|
func (s *UIComponentApplicationServiceImpl) GetUIComponentFolderContent(ctx context.Context, id string) ([]FileInfo, error) {
|
|||
|
|
// 获取组件信息
|
|||
|
|
component, err := s.uiComponentRepo.GetByID(ctx, id)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, err
|
|||
|
|
}
|
|||
|
|
if component == nil {
|
|||
|
|
return nil, ErrComponentNotFound
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果没有文件夹路径,返回空
|
|||
|
|
if component.FolderPath == nil {
|
|||
|
|
return []FileInfo{}, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取文件夹内容
|
|||
|
|
return s.fileService.GetFolderContent(*component.FolderPath)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// DeleteUIComponentFolder 删除UI组件文件夹
|
|||
|
|
func (s *UIComponentApplicationServiceImpl) DeleteUIComponentFolder(ctx context.Context, id string) error {
|
|||
|
|
// 获取组件信息
|
|||
|
|
component, err := s.uiComponentRepo.GetByID(ctx, id)
|
|||
|
|
if err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
if component == nil {
|
|||
|
|
return ErrComponentNotFound
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 注意:我们不再删除整个UI目录,因为所有组件共享同一个目录
|
|||
|
|
// 这里只更新组件信息,标记为未上传状态
|
|||
|
|
// 更新组件信息
|
|||
|
|
component.FolderPath = nil
|
|||
|
|
component.IsExtracted = false
|
|||
|
|
return s.uiComponentRepo.Update(ctx, *component)
|
|||
|
|
}
|