f
This commit is contained in:
@@ -47,4 +47,7 @@ type ProductApplicationService interface {
|
|||||||
CreateProductApiConfig(ctx context.Context, productID string, config *responses.ProductApiConfigResponse) error
|
CreateProductApiConfig(ctx context.Context, productID string, config *responses.ProductApiConfigResponse) error
|
||||||
UpdateProductApiConfig(ctx context.Context, configID string, config *responses.ProductApiConfigResponse) error
|
UpdateProductApiConfig(ctx context.Context, configID string, config *responses.ProductApiConfigResponse) error
|
||||||
DeleteProductApiConfig(ctx context.Context, configID string) error
|
DeleteProductApiConfig(ctx context.Context, configID string) error
|
||||||
|
|
||||||
|
// 产品字典导出
|
||||||
|
ExportProductDictionary(ctx context.Context, format string) ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
api_services "tyapi-server/internal/domains/api/services"
|
api_services "tyapi-server/internal/domains/api/services"
|
||||||
"tyapi-server/internal/domains/product/entities"
|
"tyapi-server/internal/domains/product/entities"
|
||||||
product_service "tyapi-server/internal/domains/product/services"
|
product_service "tyapi-server/internal/domains/product/services"
|
||||||
|
"tyapi-server/internal/shared/export"
|
||||||
"tyapi-server/internal/shared/interfaces"
|
"tyapi-server/internal/shared/interfaces"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ type ProductApplicationServiceImpl struct {
|
|||||||
documentationAppService DocumentationApplicationServiceInterface
|
documentationAppService DocumentationApplicationServiceInterface
|
||||||
formConfigService api_services.FormConfigService
|
formConfigService api_services.FormConfigService
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
|
exportManager *export.ExportManager
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewProductApplicationService 创建产品应用服务
|
// NewProductApplicationService 创建产品应用服务
|
||||||
@@ -38,6 +40,7 @@ func NewProductApplicationService(
|
|||||||
documentationAppService DocumentationApplicationServiceInterface,
|
documentationAppService DocumentationApplicationServiceInterface,
|
||||||
formConfigService api_services.FormConfigService,
|
formConfigService api_services.FormConfigService,
|
||||||
logger *zap.Logger,
|
logger *zap.Logger,
|
||||||
|
exportManager *export.ExportManager,
|
||||||
) ProductApplicationService {
|
) ProductApplicationService {
|
||||||
return &ProductApplicationServiceImpl{
|
return &ProductApplicationServiceImpl{
|
||||||
productManagementService: productManagementService,
|
productManagementService: productManagementService,
|
||||||
@@ -46,6 +49,7 @@ func NewProductApplicationService(
|
|||||||
documentationAppService: documentationAppService,
|
documentationAppService: documentationAppService,
|
||||||
formConfigService: formConfigService,
|
formConfigService: formConfigService,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
|
exportManager: exportManager,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,24 +496,24 @@ func (s *ProductApplicationServiceImpl) GetProductByIDForUser(ctx context.Contex
|
|||||||
// convertToProductInfoResponse 转换为产品信息响应
|
// convertToProductInfoResponse 转换为产品信息响应
|
||||||
func (s *ProductApplicationServiceImpl) convertToProductInfoResponse(product *entities.Product) *responses.ProductInfoResponse {
|
func (s *ProductApplicationServiceImpl) convertToProductInfoResponse(product *entities.Product) *responses.ProductInfoResponse {
|
||||||
response := &responses.ProductInfoResponse{
|
response := &responses.ProductInfoResponse{
|
||||||
ID: product.ID,
|
ID: product.ID,
|
||||||
OldID: product.OldID,
|
OldID: product.OldID,
|
||||||
Name: product.Name,
|
Name: product.Name,
|
||||||
Code: product.Code,
|
Code: product.Code,
|
||||||
Description: product.Description,
|
Description: product.Description,
|
||||||
Content: product.Content,
|
Content: product.Content,
|
||||||
CategoryID: product.CategoryID,
|
CategoryID: product.CategoryID,
|
||||||
SubCategoryID: product.SubCategoryID,
|
SubCategoryID: product.SubCategoryID,
|
||||||
Price: product.Price.InexactFloat64(),
|
Price: product.Price.InexactFloat64(),
|
||||||
IsEnabled: product.IsEnabled,
|
IsEnabled: product.IsEnabled,
|
||||||
IsPackage: product.IsPackage,
|
IsPackage: product.IsPackage,
|
||||||
SellUIComponent: product.SellUIComponent,
|
SellUIComponent: product.SellUIComponent,
|
||||||
UIComponentPrice: product.UIComponentPrice.InexactFloat64(),
|
UIComponentPrice: product.UIComponentPrice.InexactFloat64(),
|
||||||
SEOTitle: product.SEOTitle,
|
SEOTitle: product.SEOTitle,
|
||||||
SEODescription: product.SEODescription,
|
SEODescription: product.SEODescription,
|
||||||
SEOKeywords: product.SEOKeywords,
|
SEOKeywords: product.SEOKeywords,
|
||||||
CreatedAt: product.CreatedAt,
|
CreatedAt: product.CreatedAt,
|
||||||
UpdatedAt: product.UpdatedAt,
|
UpdatedAt: product.UpdatedAt,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加一级分类信息
|
// 添加一级分类信息
|
||||||
@@ -544,27 +548,27 @@ func (s *ProductApplicationServiceImpl) convertToProductInfoResponse(product *en
|
|||||||
// convertToProductAdminInfoResponse 转换为管理员产品信息响应
|
// convertToProductAdminInfoResponse 转换为管理员产品信息响应
|
||||||
func (s *ProductApplicationServiceImpl) convertToProductAdminInfoResponse(product *entities.Product) *responses.ProductAdminInfoResponse {
|
func (s *ProductApplicationServiceImpl) convertToProductAdminInfoResponse(product *entities.Product) *responses.ProductAdminInfoResponse {
|
||||||
response := &responses.ProductAdminInfoResponse{
|
response := &responses.ProductAdminInfoResponse{
|
||||||
ID: product.ID,
|
ID: product.ID,
|
||||||
OldID: product.OldID,
|
OldID: product.OldID,
|
||||||
Name: product.Name,
|
Name: product.Name,
|
||||||
Code: product.Code,
|
Code: product.Code,
|
||||||
Description: product.Description,
|
Description: product.Description,
|
||||||
Content: product.Content,
|
Content: product.Content,
|
||||||
CategoryID: product.CategoryID,
|
CategoryID: product.CategoryID,
|
||||||
SubCategoryID: product.SubCategoryID,
|
SubCategoryID: product.SubCategoryID,
|
||||||
Price: product.Price.InexactFloat64(),
|
Price: product.Price.InexactFloat64(),
|
||||||
CostPrice: product.CostPrice.InexactFloat64(),
|
CostPrice: product.CostPrice.InexactFloat64(),
|
||||||
Remark: product.Remark,
|
Remark: product.Remark,
|
||||||
IsEnabled: product.IsEnabled,
|
IsEnabled: product.IsEnabled,
|
||||||
IsVisible: product.IsVisible, // 管理员可以看到可见状态
|
IsVisible: product.IsVisible, // 管理员可以看到可见状态
|
||||||
IsPackage: product.IsPackage,
|
IsPackage: product.IsPackage,
|
||||||
SellUIComponent: product.SellUIComponent,
|
SellUIComponent: product.SellUIComponent,
|
||||||
UIComponentPrice: product.UIComponentPrice.InexactFloat64(),
|
UIComponentPrice: product.UIComponentPrice.InexactFloat64(),
|
||||||
SEOTitle: product.SEOTitle,
|
SEOTitle: product.SEOTitle,
|
||||||
SEODescription: product.SEODescription,
|
SEODescription: product.SEODescription,
|
||||||
SEOKeywords: product.SEOKeywords,
|
SEOKeywords: product.SEOKeywords,
|
||||||
CreatedAt: product.CreatedAt,
|
CreatedAt: product.CreatedAt,
|
||||||
UpdatedAt: product.UpdatedAt,
|
UpdatedAt: product.UpdatedAt,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加一级分类信息
|
// 添加一级分类信息
|
||||||
@@ -994,103 +998,103 @@ func (s *ProductApplicationServiceImpl) mergeRequestParamsFromDTOs(ctx context.C
|
|||||||
// getDTOMap 获取API代码到DTO结构体的映射(复用form_config_service的逻辑)
|
// getDTOMap 获取API代码到DTO结构体的映射(复用form_config_service的逻辑)
|
||||||
func (s *ProductApplicationServiceImpl) getDTOMap() map[string]interface{} {
|
func (s *ProductApplicationServiceImpl) getDTOMap() map[string]interface{} {
|
||||||
return map[string]interface{}{
|
return map[string]interface{}{
|
||||||
"IVYZ9363": &dto.IVYZ9363Req{},
|
"IVYZ9363": &dto.IVYZ9363Req{},
|
||||||
"IVYZ385E": &dto.IVYZ385EReq{},
|
"IVYZ385E": &dto.IVYZ385EReq{},
|
||||||
"IVYZ5733": &dto.IVYZ5733Req{},
|
"IVYZ5733": &dto.IVYZ5733Req{},
|
||||||
"FLXG3D56": &dto.FLXG3D56Req{},
|
"FLXG3D56": &dto.FLXG3D56Req{},
|
||||||
"FLXG75FE": &dto.FLXG75FEReq{},
|
"FLXG75FE": &dto.FLXG75FEReq{},
|
||||||
"FLXG0V3B": &dto.FLXG0V3BReq{},
|
"FLXG0V3B": &dto.FLXG0V3BReq{},
|
||||||
"FLXG0V4B": &dto.FLXG0V4BReq{},
|
"FLXG0V4B": &dto.FLXG0V4BReq{},
|
||||||
"FLXG54F5": &dto.FLXG54F5Req{},
|
"FLXG54F5": &dto.FLXG54F5Req{},
|
||||||
"FLXG162A": &dto.FLXG162AReq{},
|
"FLXG162A": &dto.FLXG162AReq{},
|
||||||
"FLXG0687": &dto.FLXG0687Req{},
|
"FLXG0687": &dto.FLXG0687Req{},
|
||||||
"FLXGBC21": &dto.FLXGBC21Req{},
|
"FLXGBC21": &dto.FLXGBC21Req{},
|
||||||
"FLXG970F": &dto.FLXG970FReq{},
|
"FLXG970F": &dto.FLXG970FReq{},
|
||||||
"FLXG5876": &dto.FLXG5876Req{},
|
"FLXG5876": &dto.FLXG5876Req{},
|
||||||
"FLXG9687": &dto.FLXG9687Req{},
|
"FLXG9687": &dto.FLXG9687Req{},
|
||||||
"FLXGC9D1": &dto.FLXGC9D1Req{},
|
"FLXGC9D1": &dto.FLXGC9D1Req{},
|
||||||
"FLXGCA3D": &dto.FLXGCA3DReq{},
|
"FLXGCA3D": &dto.FLXGCA3DReq{},
|
||||||
"FLXGDEC7": &dto.FLXGDEC7Req{},
|
"FLXGDEC7": &dto.FLXGDEC7Req{},
|
||||||
"JRZQ0A03": &dto.JRZQ0A03Req{},
|
"JRZQ0A03": &dto.JRZQ0A03Req{},
|
||||||
"JRZQ4AA8": &dto.JRZQ4AA8Req{},
|
"JRZQ4AA8": &dto.JRZQ4AA8Req{},
|
||||||
"JRZQ8203": &dto.JRZQ8203Req{},
|
"JRZQ8203": &dto.JRZQ8203Req{},
|
||||||
"JRZQDCBE": &dto.JRZQDCBEReq{},
|
"JRZQDCBE": &dto.JRZQDCBEReq{},
|
||||||
"QYGL2ACD": &dto.QYGL2ACDReq{},
|
"QYGL2ACD": &dto.QYGL2ACDReq{},
|
||||||
"QYGL6F2D": &dto.QYGL6F2DReq{},
|
"QYGL6F2D": &dto.QYGL6F2DReq{},
|
||||||
"QYGL45BD": &dto.QYGL45BDReq{},
|
"QYGL45BD": &dto.QYGL45BDReq{},
|
||||||
"QYGL8261": &dto.QYGL8261Req{},
|
"QYGL8261": &dto.QYGL8261Req{},
|
||||||
"QYGL8271": &dto.QYGL8271Req{},
|
"QYGL8271": &dto.QYGL8271Req{},
|
||||||
"QYGLB4C0": &dto.QYGLB4C0Req{},
|
"QYGLB4C0": &dto.QYGLB4C0Req{},
|
||||||
"QYGL23T7": &dto.QYGL23T7Req{},
|
"QYGL23T7": &dto.QYGL23T7Req{},
|
||||||
"QYGL5A3C": &dto.QYGL5A3CReq{},
|
"QYGL5A3C": &dto.QYGL5A3CReq{},
|
||||||
"QYGL8B4D": &dto.QYGL8B4DReq{},
|
"QYGL8B4D": &dto.QYGL8B4DReq{},
|
||||||
"QYGL9E2F": &dto.QYGL9E2FReq{},
|
"QYGL9E2F": &dto.QYGL9E2FReq{},
|
||||||
"QYGL7C1A": &dto.QYGL7C1AReq{},
|
"QYGL7C1A": &dto.QYGL7C1AReq{},
|
||||||
"QYGL3F8E": &dto.QYGL3F8EReq{},
|
"QYGL3F8E": &dto.QYGL3F8EReq{},
|
||||||
"YYSY4B37": &dto.YYSY4B37Req{},
|
"YYSY4B37": &dto.YYSY4B37Req{},
|
||||||
"YYSY4B21": &dto.YYSY4B21Req{},
|
"YYSY4B21": &dto.YYSY4B21Req{},
|
||||||
"YYSY6F2E": &dto.YYSY6F2EReq{},
|
"YYSY6F2E": &dto.YYSY6F2EReq{},
|
||||||
"YYSY09CD": &dto.YYSY09CDReq{},
|
"YYSY09CD": &dto.YYSY09CDReq{},
|
||||||
"IVYZ0B03": &dto.IVYZ0B03Req{},
|
"IVYZ0B03": &dto.IVYZ0B03Req{},
|
||||||
"YYSYBE08": &dto.YYSYBE08Req{},
|
"YYSYBE08": &dto.YYSYBE08Req{},
|
||||||
"YYSYBE08TEST": &dto.YYSYBE08Req{},
|
"YYSYBE08TEST": &dto.YYSYBE08Req{},
|
||||||
"YYSYD50F": &dto.YYSYD50FReq{},
|
"YYSYD50F": &dto.YYSYD50FReq{},
|
||||||
"YYSYF7DB": &dto.YYSYF7DBReq{},
|
"YYSYF7DB": &dto.YYSYF7DBReq{},
|
||||||
"IVYZ9A2B": &dto.IVYZ9A2BReq{},
|
"IVYZ9A2B": &dto.IVYZ9A2BReq{},
|
||||||
"IVYZ7F2A": &dto.IVYZ7F2AReq{},
|
"IVYZ7F2A": &dto.IVYZ7F2AReq{},
|
||||||
"IVYZ4E8B": &dto.IVYZ4E8BReq{},
|
"IVYZ4E8B": &dto.IVYZ4E8BReq{},
|
||||||
"IVYZ1C9D": &dto.IVYZ1C9DReq{},
|
"IVYZ1C9D": &dto.IVYZ1C9DReq{},
|
||||||
"IVYZGZ08": &dto.IVYZGZ08Req{},
|
"IVYZGZ08": &dto.IVYZGZ08Req{},
|
||||||
"FLXG8A3F": &dto.FLXG8A3FReq{},
|
"FLXG8A3F": &dto.FLXG8A3FReq{},
|
||||||
"FLXG5B2E": &dto.FLXG5B2EReq{},
|
"FLXG5B2E": &dto.FLXG5B2EReq{},
|
||||||
"COMB298Y": &dto.COMB298YReq{},
|
"COMB298Y": &dto.COMB298YReq{},
|
||||||
"COMB86PM": &dto.COMB86PMReq{},
|
"COMB86PM": &dto.COMB86PMReq{},
|
||||||
"QCXG7A2B": &dto.QCXG7A2BReq{},
|
"QCXG7A2B": &dto.QCXG7A2BReq{},
|
||||||
"COMENT01": &dto.COMENT01Req{},
|
"COMENT01": &dto.COMENT01Req{},
|
||||||
"JRZQ09J8": &dto.JRZQ09J8Req{},
|
"JRZQ09J8": &dto.JRZQ09J8Req{},
|
||||||
"FLXGDEA8": &dto.FLXGDEA8Req{},
|
"FLXGDEA8": &dto.FLXGDEA8Req{},
|
||||||
"FLXGDEA9": &dto.FLXGDEA9Req{},
|
"FLXGDEA9": &dto.FLXGDEA9Req{},
|
||||||
"JRZQ1D09": &dto.JRZQ1D09Req{},
|
"JRZQ1D09": &dto.JRZQ1D09Req{},
|
||||||
"IVYZ2A8B": &dto.IVYZ2A8BReq{},
|
"IVYZ2A8B": &dto.IVYZ2A8BReq{},
|
||||||
"IVYZ7C9D": &dto.IVYZ7C9DReq{},
|
"IVYZ7C9D": &dto.IVYZ7C9DReq{},
|
||||||
"IVYZ5E3F": &dto.IVYZ5E3FReq{},
|
"IVYZ5E3F": &dto.IVYZ5E3FReq{},
|
||||||
"YYSY4F2E": &dto.YYSY4F2EReq{},
|
"YYSY4F2E": &dto.YYSY4F2EReq{},
|
||||||
"YYSY8B1C": &dto.YYSY8B1CReq{},
|
"YYSY8B1C": &dto.YYSY8B1CReq{},
|
||||||
"YYSY6D9A": &dto.YYSY6D9AReq{},
|
"YYSY6D9A": &dto.YYSY6D9AReq{},
|
||||||
"YYSY3E7F": &dto.YYSY3E7FReq{},
|
"YYSY3E7F": &dto.YYSY3E7FReq{},
|
||||||
"FLXG5A3B": &dto.FLXG5A3BReq{},
|
"FLXG5A3B": &dto.FLXG5A3BReq{},
|
||||||
"FLXG9C1D": &dto.FLXG9C1DReq{},
|
"FLXG9C1D": &dto.FLXG9C1DReq{},
|
||||||
"FLXG2E8F": &dto.FLXG2E8FReq{},
|
"FLXG2E8F": &dto.FLXG2E8FReq{},
|
||||||
"JRZQ3C7B": &dto.JRZQ3C7BReq{},
|
"JRZQ3C7B": &dto.JRZQ3C7BReq{},
|
||||||
"JRZQ8A2D": &dto.JRZQ8A2DReq{},
|
"JRZQ8A2D": &dto.JRZQ8A2DReq{},
|
||||||
"JRZQ5E9F": &dto.JRZQ5E9FReq{},
|
"JRZQ5E9F": &dto.JRZQ5E9FReq{},
|
||||||
"JRZQ4B6C": &dto.JRZQ4B6CReq{},
|
"JRZQ4B6C": &dto.JRZQ4B6CReq{},
|
||||||
"JRZQ7F1A": &dto.JRZQ7F1AReq{},
|
"JRZQ7F1A": &dto.JRZQ7F1AReq{},
|
||||||
"DWBG6A2C": &dto.DWBG6A2CReq{},
|
"DWBG6A2C": &dto.DWBG6A2CReq{},
|
||||||
"DWBG8B4D": &dto.DWBG8B4DReq{},
|
"DWBG8B4D": &dto.DWBG8B4DReq{},
|
||||||
"FLXG8B4D": &dto.FLXG8B4DReq{},
|
"FLXG8B4D": &dto.FLXG8B4DReq{},
|
||||||
"IVYZ81NC": &dto.IVYZ81NCReq{},
|
"IVYZ81NC": &dto.IVYZ81NCReq{},
|
||||||
"IVYZ2MN6": &dto.IVYZ2MN6Req{},
|
"IVYZ2MN6": &dto.IVYZ2MN6Req{},
|
||||||
"IVYZ7F3A": &dto.IVYZ7F3AReq{},
|
"IVYZ7F3A": &dto.IVYZ7F3AReq{},
|
||||||
"IVYZ3P9M": &dto.IVYZ3P9MReq{},
|
"IVYZ3P9M": &dto.IVYZ3P9MReq{},
|
||||||
"IVYZ3A7F": &dto.IVYZ3A7FReq{},
|
"IVYZ3A7F": &dto.IVYZ3A7FReq{},
|
||||||
"IVYZ9D2E": &dto.IVYZ9D2EReq{},
|
"IVYZ9D2E": &dto.IVYZ9D2EReq{},
|
||||||
"DWBG7F3A": &dto.DWBG7F3AReq{},
|
"DWBG7F3A": &dto.DWBG7F3AReq{},
|
||||||
"YYSY8F3A": &dto.YYSY8F3AReq{},
|
"YYSY8F3A": &dto.YYSY8F3AReq{},
|
||||||
"QCXG9P1C": &dto.QCXG9P1CReq{},
|
"QCXG9P1C": &dto.QCXG9P1CReq{},
|
||||||
"JRZQ9E2A": &dto.JRZQ9E2AReq{},
|
"JRZQ9E2A": &dto.JRZQ9E2AReq{},
|
||||||
"YYSY9A1B": &dto.YYSY9A1BReq{},
|
"YYSY9A1B": &dto.YYSY9A1BReq{},
|
||||||
"YYSY8C2D": &dto.YYSY8C2DReq{},
|
"YYSY8C2D": &dto.YYSY8C2DReq{},
|
||||||
"YYSY7D3E": &dto.YYSY7D3EReq{},
|
"YYSY7D3E": &dto.YYSY7D3EReq{},
|
||||||
"YYSY9E4A": &dto.YYSY9E4AReq{},
|
"YYSY9E4A": &dto.YYSY9E4AReq{},
|
||||||
"JRZQ6F2A": &dto.JRZQ6F2AReq{},
|
"JRZQ6F2A": &dto.JRZQ6F2AReq{},
|
||||||
"JRZQ8B3C": &dto.JRZQ8B3CReq{},
|
"JRZQ8B3C": &dto.JRZQ8B3CReq{},
|
||||||
"JRZQ9D4E": &dto.JRZQ9D4EReq{},
|
"JRZQ9D4E": &dto.JRZQ9D4EReq{},
|
||||||
"FLXG7E8F": &dto.FLXG7E8FReq{},
|
"FLXG7E8F": &dto.FLXG7E8FReq{},
|
||||||
"QYGL5F6A": &dto.QYGL5F6AReq{},
|
"QYGL5F6A": &dto.QYGL5F6AReq{},
|
||||||
"IVYZ6G7H": &dto.IVYZ6G7HReq{},
|
"IVYZ6G7H": &dto.IVYZ6G7HReq{},
|
||||||
"IVYZ8I9J": &dto.IVYZ8I9JReq{},
|
"IVYZ8I9J": &dto.IVYZ8I9JReq{},
|
||||||
"JRZQ0L85": &dto.JRZQ0L85Req{},
|
"JRZQ0L85": &dto.JRZQ0L85Req{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1240,3 +1244,105 @@ func (s *ProductApplicationServiceImpl) mapFieldTypeToDocType(frontendType strin
|
|||||||
return "string"
|
return "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExportProductDictionary 导出产品字典
|
||||||
|
func (s *ProductApplicationServiceImpl) ExportProductDictionary(ctx context.Context, format string) ([]byte, error) {
|
||||||
|
// 查询所有启用且可见的产品及其分类信息
|
||||||
|
products, err := s.productManagementService.GetAllProductsForDictionary(ctx)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("获取产品字典数据失败", zap.Error(err))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(products) == 0 {
|
||||||
|
return nil, fmt.Errorf("没有找到符合条件的产品数据")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按分类分组整理数据
|
||||||
|
categoryGroups := make(map[string][]map[string]interface{})
|
||||||
|
categoryOrder := []string{} // 保持分类顺序
|
||||||
|
|
||||||
|
for _, product := range products {
|
||||||
|
// 获取分类名称
|
||||||
|
categoryName := "未分类"
|
||||||
|
if product.Category != nil {
|
||||||
|
categoryName = product.Category.Name
|
||||||
|
// 如果有二级分类,添加到分类名称中
|
||||||
|
if product.SubCategory != nil && product.SubCategory.Name != "" {
|
||||||
|
categoryName = categoryName + " / " + product.SubCategory.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果分类不存在,初始化并添加到顺序列表
|
||||||
|
if _, exists := categoryGroups[categoryName]; !exists {
|
||||||
|
categoryGroups[categoryName] = []map[string]interface{}{}
|
||||||
|
categoryOrder = append(categoryOrder, categoryName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加产品到对应分类
|
||||||
|
productInfo := map[string]interface{}{
|
||||||
|
"category": categoryName,
|
||||||
|
"product_code": product.Code,
|
||||||
|
"product_name": product.Name,
|
||||||
|
"description": product.Description,
|
||||||
|
}
|
||||||
|
categoryGroups[categoryName] = append(categoryGroups[categoryName], productInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 准备导出数据
|
||||||
|
headers := []string{"分类", "产品编码", "产品名称", "产品简介"}
|
||||||
|
columnWidths := []float64{25, 15, 20, 40}
|
||||||
|
|
||||||
|
// 构建数据行
|
||||||
|
var data [][]interface{}
|
||||||
|
for _, categoryName := range categoryOrder {
|
||||||
|
productsInCategory := categoryGroups[categoryName]
|
||||||
|
for i, product := range productsInCategory {
|
||||||
|
// 只有每个分类的第一个产品才显示分类名称
|
||||||
|
var categoryNameForRow interface{}
|
||||||
|
if i == 0 {
|
||||||
|
categoryNameForRow = product["category"]
|
||||||
|
} else {
|
||||||
|
categoryNameForRow = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
row := []interface{}{
|
||||||
|
categoryNameForRow,
|
||||||
|
product["product_code"],
|
||||||
|
product["product_name"],
|
||||||
|
product["description"],
|
||||||
|
}
|
||||||
|
data = append(data, row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算需要合并的行
|
||||||
|
mergedRegions := [][]int{}
|
||||||
|
currentRow := 1 // 从第1行开始(第0行是表头)
|
||||||
|
for _, categoryName := range categoryOrder {
|
||||||
|
productsCount := len(categoryGroups[categoryName])
|
||||||
|
if productsCount > 1 {
|
||||||
|
// 合并相同分类的单元格:从当前行开始,合并productsCount行,第0列
|
||||||
|
// Excel格式:[startRow, startCol, endRow, endCol]
|
||||||
|
mergedRegions = append(mergedRegions, []int{
|
||||||
|
currentRow, // startRow
|
||||||
|
0, // startCol (分类列)
|
||||||
|
currentRow + productsCount - 1, // endRow
|
||||||
|
0, // endCol
|
||||||
|
})
|
||||||
|
}
|
||||||
|
currentRow += productsCount
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建导出配置
|
||||||
|
config := &export.ExportConfig{
|
||||||
|
SheetName: "产品字典",
|
||||||
|
Headers: headers,
|
||||||
|
Data: data,
|
||||||
|
ColumnWidths: columnWidths,
|
||||||
|
MergedRegions: mergedRegions,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用导出管理器生成文件
|
||||||
|
return s.exportManager.Export(ctx, config, format)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1026,6 +1026,7 @@ func NewContainer() *Container {
|
|||||||
documentationAppService product.DocumentationApplicationServiceInterface,
|
documentationAppService product.DocumentationApplicationServiceInterface,
|
||||||
formConfigService api_services.FormConfigService,
|
formConfigService api_services.FormConfigService,
|
||||||
logger *zap.Logger,
|
logger *zap.Logger,
|
||||||
|
exportManager *export.ExportManager,
|
||||||
) product.ProductApplicationService {
|
) product.ProductApplicationService {
|
||||||
return product.NewProductApplicationService(
|
return product.NewProductApplicationService(
|
||||||
productManagementService,
|
productManagementService,
|
||||||
@@ -1034,6 +1035,7 @@ func NewContainer() *Container {
|
|||||||
documentationAppService,
|
documentationAppService,
|
||||||
formConfigService,
|
formConfigService,
|
||||||
logger,
|
logger,
|
||||||
|
exportManager,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
fx.As(new(product.ProductApplicationService)),
|
fx.As(new(product.ProductApplicationService)),
|
||||||
|
|||||||
@@ -488,6 +488,24 @@ type IVYZ2A8BReq struct {
|
|||||||
Authorized string `json:"authorized" validate:"required,oneof=0 1"`
|
Authorized string `json:"authorized" validate:"required,oneof=0 1"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type QYGL4YABReq struct {
|
||||||
|
EntName string `json:"ent_name" validate:"required,min=1,validEnterpriseName"`
|
||||||
|
EntCode string `json:"ent_code" validate:"required,validUSCI"`
|
||||||
|
IDCard string `json:"id_card" validate:"required,validIDCard"`
|
||||||
|
Name string `json:"name" validate:"required,min=1,validName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QYGL3YSBReq struct {
|
||||||
|
Name string `json:"name" validate:"required,min=1,validName"`
|
||||||
|
EntCode string `json:"ent_code" validate:"required,validUSCI"`
|
||||||
|
EntName string `json:"ent_name" validate:"required,min=1,validEnterpriseName"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QYGL2YSBReq struct {
|
||||||
|
EntCode string `json:"ent_code" validate:"required,validUSCI"`
|
||||||
|
Name string `json:"name" validate:"required,min=1,validName"`
|
||||||
|
}
|
||||||
|
|
||||||
type IVYZ7C9DReq struct {
|
type IVYZ7C9DReq struct {
|
||||||
IDCard string `json:"id_card" validate:"required,validIDCard"`
|
IDCard string `json:"id_card" validate:"required,validIDCard"`
|
||||||
Name string `json:"name" validate:"required,min=1,validName"`
|
Name string `json:"name" validate:"required,min=1,validName"`
|
||||||
|
|||||||
@@ -256,7 +256,9 @@ func registerAllProcessors(combService *comb.CombService) {
|
|||||||
"QYGL8848": qygl.ProcessQYGL8848Request, //企业税收违法核查
|
"QYGL8848": qygl.ProcessQYGL8848Request, //企业税收违法核查
|
||||||
"QYGLDJ33": qygl.ProcessQYGLDJ33Request, //企业年报信息核验
|
"QYGLDJ33": qygl.ProcessQYGLDJ33Request, //企业年报信息核验
|
||||||
"QYGLBH7Y": qygl.ProcessQYGLBH7YRequest, //企业涉诉案件查询汇博
|
"QYGLBH7Y": qygl.ProcessQYGLBH7YRequest, //企业涉诉案件查询汇博
|
||||||
|
"QYGL4YAB": qygl.ProcessQYGL4YABRequest, //企业四要素认证shumai
|
||||||
|
"QYGL3YSB": qygl.ProcessQYGL3YSBRequest, //企业三要素认证shumai
|
||||||
|
"QYGL2YSB": qygl.ProcessQYGL2YSBRequest, //企业二要素认证shumai
|
||||||
// YYSY系列处理器
|
// YYSY系列处理器
|
||||||
"YYSY35TA": yysy.ProcessYYSY35TARequest, //运营商归属地数卖
|
"YYSY35TA": yysy.ProcessYYSY35TARequest, //运营商归属地数卖
|
||||||
"YYSYD50F": yysy.ProcessYYSYD50FRequest,
|
"YYSYD50F": yysy.ProcessYYSYD50FRequest,
|
||||||
|
|||||||
@@ -283,6 +283,9 @@ func (s *FormConfigServiceImpl) getDTOStruct(ctx context.Context, apiCode string
|
|||||||
"IVYZ2MN7": &dto.IVYZ2MN6Req{}, //学历Bzhicha
|
"IVYZ2MN7": &dto.IVYZ2MN6Req{}, //学历Bzhicha
|
||||||
"FLXGHB4F": &dto.FLXGHB4FReq{}, //个人涉诉案件查询汇博
|
"FLXGHB4F": &dto.FLXGHB4FReq{}, //个人涉诉案件查询汇博
|
||||||
"QYGLBH7Y": &dto.QYGLBH7YReq{}, //企业涉诉案件查询汇博
|
"QYGLBH7Y": &dto.QYGLBH7YReq{}, //企业涉诉案件查询汇博
|
||||||
|
"QYGL4YAB": &dto.QYGL4YABReq{}, //企业四要素认证shumai
|
||||||
|
"QYGL3YSB": &dto.QYGL3YSBReq{}, //企业三要素认证shumai
|
||||||
|
"QYGL2YSB": &dto.QYGL2YSBReq{}, //企业二要素认证shumai
|
||||||
}
|
}
|
||||||
|
|
||||||
// 优先返回已配置的DTO
|
// 优先返回已配置的DTO
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package qygl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"tyapi-server/internal/domains/api/dto"
|
||||||
|
"tyapi-server/internal/domains/api/services/processors"
|
||||||
|
"tyapi-server/internal/infrastructure/external/shumai"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProcessQYGL2YSBRequest QYGL2YSB API处理方法 - 企业二要素认证
|
||||||
|
func ProcessQYGL2YSBRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||||
|
var paramsDto dto.QYGL2YSBReq
|
||||||
|
if err := json.Unmarshal(params, ¶msDto); err != nil {
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||||
|
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||||
|
}
|
||||||
|
reqFormData := map[string]interface{}{
|
||||||
|
"legalPerson": paramsDto.Name,
|
||||||
|
"creditNo": paramsDto.EntCode,
|
||||||
|
}
|
||||||
|
apiPath := "/v4/company/two/check" // 接口路径,根据数脉文档填写(如 v4/xxx)
|
||||||
|
respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, shumai.ErrDatasource) {
|
||||||
|
// 数据源错误
|
||||||
|
return nil, errors.Join(processors.ErrDatasource, err)
|
||||||
|
} else if errors.Is(err, shumai.ErrSystem) {
|
||||||
|
// 系统错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
} else if errors.Is(err, shumai.ErrNotFound) {
|
||||||
|
// 查无记录
|
||||||
|
return nil, errors.Join(processors.ErrNotFound, err)
|
||||||
|
} else {
|
||||||
|
// 其他未知错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return respBytes, nil
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package qygl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"tyapi-server/internal/domains/api/dto"
|
||||||
|
"tyapi-server/internal/domains/api/services/processors"
|
||||||
|
"tyapi-server/internal/infrastructure/external/shumai"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProcessQYGL3YSBRequest QYGL3YSB API处理方法 - 企业三要素认证
|
||||||
|
func ProcessQYGL3YSBRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||||
|
var paramsDto dto.QYGL3YSBReq
|
||||||
|
if err := json.Unmarshal(params, ¶msDto); err != nil {
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||||
|
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||||
|
}
|
||||||
|
reqFormData := map[string]interface{}{
|
||||||
|
"legalPerson": paramsDto.Name,
|
||||||
|
"companyName": paramsDto.EntName,
|
||||||
|
"creditNo": paramsDto.EntCode,
|
||||||
|
}
|
||||||
|
apiPath := "/v4/company-three/check" // 接口路径,根据数脉文档填写(如 v4/xxx)
|
||||||
|
respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, shumai.ErrDatasource) {
|
||||||
|
// 数据源错误
|
||||||
|
return nil, errors.Join(processors.ErrDatasource, err)
|
||||||
|
} else if errors.Is(err, shumai.ErrSystem) {
|
||||||
|
// 系统错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
} else if errors.Is(err, shumai.ErrNotFound) {
|
||||||
|
// 查无记录
|
||||||
|
return nil, errors.Join(processors.ErrNotFound, err)
|
||||||
|
} else {
|
||||||
|
// 其他未知错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return respBytes, nil
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package qygl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"tyapi-server/internal/domains/api/dto"
|
||||||
|
"tyapi-server/internal/domains/api/services/processors"
|
||||||
|
"tyapi-server/internal/infrastructure/external/shumai"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProcessQYGL4YABRequest QYGL4YAB API处理方法 - 企业四要素认证
|
||||||
|
func ProcessQYGL4YABRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||||
|
var paramsDto dto.QYGL4YABReq
|
||||||
|
if err := json.Unmarshal(params, ¶msDto); err != nil {
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||||
|
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||||
|
}
|
||||||
|
reqFormData := map[string]interface{}{
|
||||||
|
"idCard": paramsDto.IDCard,
|
||||||
|
"legalPerson": paramsDto.Name,
|
||||||
|
"companyName": paramsDto.EntName,
|
||||||
|
"creditNo": paramsDto.EntCode,
|
||||||
|
}
|
||||||
|
apiPath := "/v4/company-four/check" // 接口路径,根据数脉文档填写(如 v4/xxx)
|
||||||
|
respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, shumai.ErrDatasource) {
|
||||||
|
// 数据源错误
|
||||||
|
return nil, errors.Join(processors.ErrDatasource, err)
|
||||||
|
} else if errors.Is(err, shumai.ErrSystem) {
|
||||||
|
// 系统错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
} else if errors.Is(err, shumai.ErrNotFound) {
|
||||||
|
// 查无记录
|
||||||
|
return nil, errors.Join(processors.ErrNotFound, err)
|
||||||
|
} else {
|
||||||
|
// 其他未知错误
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return respBytes, nil
|
||||||
|
|
||||||
|
}
|
||||||
@@ -435,3 +435,46 @@ func (s *ProductManagementService) ListProductsWithSubscriptionStatus(ctx contex
|
|||||||
|
|
||||||
return products, subscriptionStatusMap, total, nil
|
return products, subscriptionStatusMap, total, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAllProductsForDictionary 获取所有启用且可见的产品(用于导出产品字典)
|
||||||
|
func (s *ProductManagementService) GetAllProductsForDictionary(ctx context.Context) ([]*entities.Product, error) {
|
||||||
|
// 构建查询条件:启用且可见
|
||||||
|
isEnabled := true
|
||||||
|
isVisible := true
|
||||||
|
|
||||||
|
filters := map[string]interface{}{
|
||||||
|
"is_enabled": isEnabled,
|
||||||
|
"is_visible": isVisible,
|
||||||
|
}
|
||||||
|
|
||||||
|
options := interfaces.ListOptions{
|
||||||
|
Page: 1,
|
||||||
|
PageSize: 1000, // 获取所有产品
|
||||||
|
Sort: "sort", // 使用 products 表的 sort 字段,排序后再按分类分组
|
||||||
|
Order: "asc",
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取产品列表
|
||||||
|
products, _, err := s.ListProducts(ctx, filters, options)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("获取产品字典数据失败", zap.Error(err))
|
||||||
|
return nil, fmt.Errorf("获取产品字典数据失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预加载分类信息
|
||||||
|
for _, product := range products {
|
||||||
|
if product.CategoryID != "" {
|
||||||
|
category, _ := s.categoryRepo.GetByID(ctx, product.CategoryID)
|
||||||
|
product.Category = &category
|
||||||
|
}
|
||||||
|
|
||||||
|
if product.SubCategoryID != nil && *product.SubCategoryID != "" {
|
||||||
|
subCategory, err := s.subCategoryRepo.GetByID(ctx, *product.SubCategoryID)
|
||||||
|
if err == nil && subCategory != nil {
|
||||||
|
product.SubCategory = subCategory
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return products, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package repositories
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"tyapi-server/internal/domains/product/entities"
|
"tyapi-server/internal/domains/product/entities"
|
||||||
"tyapi-server/internal/domains/product/repositories"
|
"tyapi-server/internal/domains/product/repositories"
|
||||||
"tyapi-server/internal/domains/product/repositories/queries"
|
"tyapi-server/internal/domains/product/repositories/queries"
|
||||||
@@ -165,13 +167,33 @@ func (r *GormProductRepository) ListProducts(ctx context.Context, query *queries
|
|||||||
|
|
||||||
// 应用排序
|
// 应用排序
|
||||||
if query.SortBy != "" {
|
if query.SortBy != "" {
|
||||||
order := query.SortBy
|
// 检查是否是关联表字段排序
|
||||||
if query.SortOrder == "desc" {
|
if strings.Contains(query.SortBy, ".") {
|
||||||
order += " DESC"
|
parts := strings.Split(query.SortBy, ".")
|
||||||
|
if len(parts) == 2 {
|
||||||
|
// 关联表字段排序,需要JOIN表
|
||||||
|
joinTable := parts[0]
|
||||||
|
sortField := parts[1]
|
||||||
|
dbQuery = dbQuery.Joins("JOIN "+joinTable+" ON products.category_id = "+joinTable+".id")
|
||||||
|
|
||||||
|
order := joinTable + "." + sortField
|
||||||
|
if query.SortOrder == "desc" {
|
||||||
|
order += " DESC"
|
||||||
|
} else {
|
||||||
|
order += " ASC"
|
||||||
|
}
|
||||||
|
dbQuery = dbQuery.Order(order)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
order += " ASC"
|
// 本表字段排序
|
||||||
|
order := query.SortBy
|
||||||
|
if query.SortOrder == "desc" {
|
||||||
|
order += " DESC"
|
||||||
|
} else {
|
||||||
|
order += " ASC"
|
||||||
|
}
|
||||||
|
dbQuery = dbQuery.Order(order)
|
||||||
}
|
}
|
||||||
dbQuery = dbQuery.Order(order)
|
|
||||||
} else {
|
} else {
|
||||||
dbQuery = dbQuery.Order("created_at DESC")
|
dbQuery = dbQuery.Order("created_at DESC")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1659,3 +1659,54 @@ func (h *ProductAdminHandler) ExportAdminApiCalls(c *gin.Context) {
|
|||||||
c.Header("Content-Disposition", "attachment; filename="+filename)
|
c.Header("Content-Disposition", "attachment; filename="+filename)
|
||||||
c.Data(200, contentType, fileData)
|
c.Data(200, contentType, fileData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExportProductDictionary 导出产品字典
|
||||||
|
// @Summary 导出产品字典
|
||||||
|
// @Description 导出所有启用且可见的产品字典,按分类分组,左侧分类列合并单元格
|
||||||
|
// @Tags 产品管理
|
||||||
|
// @Accept json
|
||||||
|
// @Produce application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,text/csv
|
||||||
|
// @Security Bearer
|
||||||
|
// @Param format query string false "导出格式" Enums(excel, csv) default(excel)
|
||||||
|
// @Success 200 {file} file "导出文件"
|
||||||
|
// @Failure 400 {object} map[string]interface{} "请求参数错误"
|
||||||
|
// @Failure 401 {object} map[string]interface{} "未认证"
|
||||||
|
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
|
||||||
|
// @Router /api/v1/admin/products/export-dictionary [get]
|
||||||
|
func (h *ProductAdminHandler) ExportProductDictionary(c *gin.Context) {
|
||||||
|
// 获取导出格式,默认为excel
|
||||||
|
format := c.DefaultQuery("format", "excel")
|
||||||
|
if format != "excel" && format != "csv" {
|
||||||
|
h.responseBuilder.BadRequest(c, "不支持的导出格式")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用应用服务导出数据
|
||||||
|
fileData, err := h.productAppService.ExportProductDictionary(c.Request.Context(), format)
|
||||||
|
if err != nil {
|
||||||
|
h.logger.Error("导出产品字典失败", zap.Error(err))
|
||||||
|
|
||||||
|
// 根据错误信息返回具体的提示
|
||||||
|
errMsg := err.Error()
|
||||||
|
if strings.Contains(errMsg, "没有找到符合条件的产品数据") || strings.Contains(errMsg, "没有数据") {
|
||||||
|
h.responseBuilder.NotFound(c, "没有找到符合条件的产品数据,请确保有启用且可见的产品")
|
||||||
|
} else if strings.Contains(errMsg, "参数") || strings.Contains(errMsg, "参数错误") {
|
||||||
|
h.responseBuilder.BadRequest(c, errMsg)
|
||||||
|
} else {
|
||||||
|
h.responseBuilder.BadRequest(c, "导出产品字典失败:"+errMsg)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置响应头
|
||||||
|
contentType := "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||||
|
filename := "产品字典.xlsx"
|
||||||
|
if format == "csv" {
|
||||||
|
contentType = "text/csv;charset=utf-8"
|
||||||
|
filename = "产品字典.csv"
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Header("Content-Type", contentType)
|
||||||
|
c.Header("Content-Disposition", "attachment; filename="+filename)
|
||||||
|
c.Data(200, contentType, fileData)
|
||||||
|
}
|
||||||
|
|||||||
@@ -60,6 +60,9 @@ func (r *ProductAdminRoutes) Register(router *sharedhttp.GinRouter) {
|
|||||||
products.GET("/:id/documentation", r.handler.GetProductDocumentation)
|
products.GET("/:id/documentation", r.handler.GetProductDocumentation)
|
||||||
products.POST("/:id/documentation", r.handler.CreateOrUpdateProductDocumentation)
|
products.POST("/:id/documentation", r.handler.CreateOrUpdateProductDocumentation)
|
||||||
products.DELETE("/:id/documentation", r.handler.DeleteProductDocumentation)
|
products.DELETE("/:id/documentation", r.handler.DeleteProductDocumentation)
|
||||||
|
|
||||||
|
// 产品字典导出
|
||||||
|
products.GET("/export-dictionary", r.handler.ExportProductDictionary)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分类管理
|
// 分类管理
|
||||||
|
|||||||
@@ -11,10 +11,11 @@ import (
|
|||||||
|
|
||||||
// ExportConfig 定义了导出所需的配置
|
// ExportConfig 定义了导出所需的配置
|
||||||
type ExportConfig struct {
|
type ExportConfig struct {
|
||||||
SheetName string // 工作表名称
|
SheetName string // 工作表名称
|
||||||
Headers []string // 表头
|
Headers []string // 表头
|
||||||
Data [][]interface{} // 导出数据
|
Data [][]interface{} // 导出数据
|
||||||
ColumnWidths []float64 // 列宽
|
ColumnWidths []float64 // 列宽
|
||||||
|
MergedRegions [][]int // 合并单元格配置 [[startRow, startCol, endRow, endCol], ...]
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExportManager 负责管理不同格式的导出
|
// ExportManager 负责管理不同格式的导出
|
||||||
@@ -95,6 +96,30 @@ func (m *ExportManager) generateExcel(ctx context.Context, config *ExportConfig)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 合并单元格
|
||||||
|
if len(config.MergedRegions) > 0 {
|
||||||
|
for _, region := range config.MergedRegions {
|
||||||
|
startRow := region[0]
|
||||||
|
startCol := region[1]
|
||||||
|
endRow := region[2]
|
||||||
|
endCol := region[3]
|
||||||
|
|
||||||
|
startCell, err := excelize.CoordinatesToCellName(startCol+1, startRow+1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("生成合并区域起始单元格坐标失败: %v", err)
|
||||||
|
}
|
||||||
|
endCell, err := excelize.CoordinatesToCellName(endCol+1, endRow+1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("生成合并区域结束单元格坐标失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = f.MergeCell(sheetName, startCell, endCell)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("合并单元格失败: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 设置列宽
|
// 设置列宽
|
||||||
for i, width := range config.ColumnWidths {
|
for i, width := range config.ColumnWidths {
|
||||||
col, err := excelize.ColumnNumberToName(i + 1)
|
col, err := excelize.ColumnNumberToName(i + 1)
|
||||||
|
|||||||
@@ -11,10 +11,11 @@ import (
|
|||||||
|
|
||||||
// ExportConfig 定义了导出所需的配置
|
// ExportConfig 定义了导出所需的配置
|
||||||
type ExportConfig struct {
|
type ExportConfig struct {
|
||||||
SheetName string // 工作表名称
|
SheetName string // 工作表名称
|
||||||
Headers []string // 表头
|
Headers []string // 表头
|
||||||
Data [][]interface{} // 导出数据
|
Data [][]interface{} // 导出数据
|
||||||
ColumnWidths []float64 // 列宽
|
ColumnWidths []float64 // 列宽
|
||||||
|
MergedRegions [][]int // 合并单元格配置 [[startRow, startCol, endRow, endCol], ...]
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExportManager 负责管理不同格式的导出
|
// ExportManager 负责管理不同格式的导出
|
||||||
|
|||||||
Reference in New Issue
Block a user