1184 lines
40 KiB
Go
1184 lines
40 KiB
Go
package product
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"reflect"
|
||
"strings"
|
||
|
||
"github.com/shopspring/decimal"
|
||
"go.uber.org/zap"
|
||
|
||
"tyapi-server/internal/application/product/dto/commands"
|
||
appQueries "tyapi-server/internal/application/product/dto/queries"
|
||
"tyapi-server/internal/application/product/dto/responses"
|
||
"tyapi-server/internal/domains/api/dto"
|
||
api_services "tyapi-server/internal/domains/api/services"
|
||
"tyapi-server/internal/domains/product/entities"
|
||
product_service "tyapi-server/internal/domains/product/services"
|
||
"tyapi-server/internal/shared/interfaces"
|
||
)
|
||
|
||
// ProductApplicationServiceImpl 产品应用服务实现
|
||
// 负责业务流程编排、事务管理、数据转换,不直接操作仓库
|
||
type ProductApplicationServiceImpl struct {
|
||
productManagementService *product_service.ProductManagementService
|
||
productSubscriptionService *product_service.ProductSubscriptionService
|
||
productApiConfigAppService ProductApiConfigApplicationService
|
||
documentationAppService DocumentationApplicationServiceInterface
|
||
formConfigService api_services.FormConfigService
|
||
logger *zap.Logger
|
||
}
|
||
|
||
// NewProductApplicationService 创建产品应用服务
|
||
func NewProductApplicationService(
|
||
productManagementService *product_service.ProductManagementService,
|
||
productSubscriptionService *product_service.ProductSubscriptionService,
|
||
productApiConfigAppService ProductApiConfigApplicationService,
|
||
documentationAppService DocumentationApplicationServiceInterface,
|
||
formConfigService api_services.FormConfigService,
|
||
logger *zap.Logger,
|
||
) ProductApplicationService {
|
||
return &ProductApplicationServiceImpl{
|
||
productManagementService: productManagementService,
|
||
productSubscriptionService: productSubscriptionService,
|
||
productApiConfigAppService: productApiConfigAppService,
|
||
documentationAppService: documentationAppService,
|
||
formConfigService: formConfigService,
|
||
logger: logger,
|
||
}
|
||
}
|
||
|
||
// CreateProduct 创建产品
|
||
// 业务流程<E6B581>?. 构建产品实体 2. 创建产品
|
||
func (s *ProductApplicationServiceImpl) CreateProduct(ctx context.Context, cmd *commands.CreateProductCommand) error {
|
||
// 1. 构建产品实体
|
||
product := &entities.Product{
|
||
Name: cmd.Name,
|
||
Code: cmd.Code,
|
||
Description: cmd.Description,
|
||
Content: cmd.Content,
|
||
CategoryID: cmd.CategoryID,
|
||
Price: decimal.NewFromFloat(cmd.Price),
|
||
IsEnabled: cmd.IsEnabled,
|
||
IsVisible: cmd.IsVisible,
|
||
IsPackage: cmd.IsPackage,
|
||
SEOTitle: cmd.SEOTitle,
|
||
SEODescription: cmd.SEODescription,
|
||
SEOKeywords: cmd.SEOKeywords,
|
||
}
|
||
|
||
// 2. 创建产品
|
||
_, err := s.productManagementService.CreateProduct(ctx, product)
|
||
return err
|
||
}
|
||
|
||
// UpdateProduct 更新产品
|
||
// 业务流程<E6B581>?. 获取现有产品 2. 更新产品信息 3. 保存产品
|
||
func (s *ProductApplicationServiceImpl) UpdateProduct(ctx context.Context, cmd *commands.UpdateProductCommand) error {
|
||
// 1. 获取现有产品
|
||
existingProduct, err := s.productManagementService.GetProductByID(ctx, cmd.ID)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// 2. 更新产品信息
|
||
existingProduct.Name = cmd.Name
|
||
existingProduct.Code = cmd.Code
|
||
existingProduct.Description = cmd.Description
|
||
existingProduct.Content = cmd.Content
|
||
existingProduct.CategoryID = cmd.CategoryID
|
||
existingProduct.Price = decimal.NewFromFloat(cmd.Price)
|
||
existingProduct.IsEnabled = cmd.IsEnabled
|
||
existingProduct.IsVisible = cmd.IsVisible
|
||
existingProduct.IsPackage = cmd.IsPackage
|
||
existingProduct.SEOTitle = cmd.SEOTitle
|
||
existingProduct.SEODescription = cmd.SEODescription
|
||
existingProduct.SEOKeywords = cmd.SEOKeywords
|
||
|
||
// 3. 保存产品
|
||
return s.productManagementService.UpdateProduct(ctx, existingProduct)
|
||
}
|
||
|
||
// DeleteProduct 删除产品
|
||
// 业务流程<E6B581>?. 删除产品
|
||
func (s *ProductApplicationServiceImpl) DeleteProduct(ctx context.Context, cmd *commands.DeleteProductCommand) error {
|
||
return s.productManagementService.DeleteProduct(ctx, cmd.ID)
|
||
}
|
||
|
||
// ListProducts 获取产品列表
|
||
// 业务流程<E6B581>?. 获取产品列表 2. 构建响应数据
|
||
func (s *ProductApplicationServiceImpl) ListProducts(ctx context.Context, filters map[string]interface{}, options interfaces.ListOptions) (*responses.ProductListResponse, error) {
|
||
// 检查是否有用户ID,如果有则使用带订阅状态的方法
|
||
if userID, ok := filters["user_id"].(string); ok && userID != "" {
|
||
return s.ListProductsWithSubscriptionStatus(ctx, filters, options)
|
||
}
|
||
|
||
// 调用领域服务获取产品列表
|
||
products, total, err := s.productManagementService.ListProducts(ctx, filters, options)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 转换为响应对象
|
||
items := make([]responses.ProductInfoResponse, len(products))
|
||
for i := range products {
|
||
items[i] = *s.convertToProductInfoResponse(products[i])
|
||
}
|
||
|
||
return &responses.ProductListResponse{
|
||
Total: total,
|
||
Page: options.Page,
|
||
Size: options.PageSize,
|
||
Items: items,
|
||
}, nil
|
||
}
|
||
|
||
// ListProductsWithSubscriptionStatus 获取产品列表(包含订阅状态)
|
||
// 业务流程<E6B581>?. 获取产品列表和订阅状<E99885>?2. 构建响应数据
|
||
func (s *ProductApplicationServiceImpl) ListProductsWithSubscriptionStatus(ctx context.Context, filters map[string]interface{}, options interfaces.ListOptions) (*responses.ProductListResponse, error) {
|
||
// 调用领域服务获取产品列表(包含订阅状态)
|
||
products, subscriptionStatusMap, total, err := s.productManagementService.ListProductsWithSubscriptionStatus(ctx, filters, options)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 转换为响应对象
|
||
items := make([]responses.ProductInfoResponse, len(products))
|
||
for i := range products {
|
||
item := s.convertToProductInfoResponse(products[i])
|
||
|
||
// 设置订阅状态
|
||
if isSubscribed, exists := subscriptionStatusMap[products[i].ID]; exists {
|
||
item.IsSubscribed = &isSubscribed
|
||
}
|
||
|
||
items[i] = *item
|
||
}
|
||
|
||
return &responses.ProductListResponse{
|
||
Total: total,
|
||
Page: options.Page,
|
||
Size: options.PageSize,
|
||
Items: items,
|
||
}, nil
|
||
}
|
||
|
||
// GetProductsByIDs 根据ID列表获取产品
|
||
// 业务流程<E6B581>?. 获取产品列表 2. 构建响应数据
|
||
func (s *ProductApplicationServiceImpl) GetProductsByIDs(ctx context.Context, query *appQueries.GetProductsByIDsQuery) ([]*responses.ProductInfoResponse, error) {
|
||
// 这里需要扩展领域服务来支持批量获取
|
||
// 暂时返回空列表
|
||
return []*responses.ProductInfoResponse{}, nil
|
||
}
|
||
|
||
// GetSubscribableProducts 获取可订阅产品列表
|
||
// 业务流程:1. 获取启用产品 2. 过滤可订阅产品 3. 构建响应数据
|
||
func (s *ProductApplicationServiceImpl) GetSubscribableProducts(ctx context.Context, query *appQueries.GetSubscribableProductsQuery) ([]*responses.ProductInfoResponse, error) {
|
||
products, err := s.productManagementService.GetEnabledProducts(ctx)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 过滤可订阅的产品
|
||
var subscribableProducts []*entities.Product
|
||
for _, product := range products {
|
||
if product.CanBeSubscribed() {
|
||
subscribableProducts = append(subscribableProducts, product)
|
||
}
|
||
}
|
||
|
||
// 转换为响应对象
|
||
items := make([]*responses.ProductInfoResponse, len(subscribableProducts))
|
||
for i := range subscribableProducts {
|
||
items[i] = s.convertToProductInfoResponse(subscribableProducts[i])
|
||
}
|
||
|
||
return items, nil
|
||
}
|
||
|
||
// GetProductByID 根据ID获取产品
|
||
// 业务流程<E6B581>?. 获取产品信息 2. 构建响应数据
|
||
func (s *ProductApplicationServiceImpl) GetProductByID(ctx context.Context, query *appQueries.GetProductQuery) (*responses.ProductInfoResponse, error) {
|
||
product, err := s.productManagementService.GetProductWithCategory(ctx, query.ID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return s.convertToProductInfoResponse(product), nil
|
||
}
|
||
|
||
// GetProductStats 获取产品统计信息
|
||
// 业务流程<E6B581>?. 获取产品统计 2. 构建响应数据
|
||
func (s *ProductApplicationServiceImpl) GetProductStats(ctx context.Context) (*responses.ProductStatsResponse, error) {
|
||
stats, err := s.productSubscriptionService.GetProductStats(ctx)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return &responses.ProductStatsResponse{
|
||
TotalProducts: stats["total"],
|
||
EnabledProducts: stats["enabled"],
|
||
VisibleProducts: stats["visible"],
|
||
PackageProducts: 0, // 需要单独统计
|
||
}, nil
|
||
}
|
||
|
||
// AddPackageItem 添加组合包子产品
|
||
func (s *ProductApplicationServiceImpl) AddPackageItem(ctx context.Context, packageID string, cmd *commands.AddPackageItemCommand) error {
|
||
// 验证组合包是否存在
|
||
packageProduct, err := s.productManagementService.GetProductByID(ctx, packageID)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if !packageProduct.IsPackage {
|
||
return fmt.Errorf("产品不是组合包")
|
||
}
|
||
|
||
// 验证子产品是否存在且不是组合包
|
||
subProduct, err := s.productManagementService.GetProductByID(ctx, cmd.ProductID)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if subProduct.IsPackage {
|
||
return fmt.Errorf("不能将组合包作为子产品")
|
||
}
|
||
|
||
// 检查是否已经存在
|
||
existingItems, err := s.productManagementService.GetPackageItems(ctx, packageID)
|
||
if err == nil {
|
||
for _, item := range existingItems {
|
||
if item.ProductID == cmd.ProductID {
|
||
return fmt.Errorf("该产品已在组合包中")
|
||
}
|
||
}
|
||
}
|
||
|
||
// 获取当前最大排序号
|
||
maxSortOrder := 0
|
||
if existingItems != nil {
|
||
for _, item := range existingItems {
|
||
if item.SortOrder > maxSortOrder {
|
||
maxSortOrder = item.SortOrder
|
||
}
|
||
}
|
||
}
|
||
|
||
// 创建组合包项目
|
||
packageItem := &entities.ProductPackageItem{
|
||
PackageID: packageID,
|
||
ProductID: cmd.ProductID,
|
||
SortOrder: maxSortOrder + 1,
|
||
}
|
||
|
||
return s.productManagementService.CreatePackageItem(ctx, packageItem)
|
||
}
|
||
|
||
// UpdatePackageItem 更新组合包子产品
|
||
func (s *ProductApplicationServiceImpl) UpdatePackageItem(ctx context.Context, packageID, itemID string, cmd *commands.UpdatePackageItemCommand) error {
|
||
// 验证组合包项目是否存在
|
||
packageItem, err := s.productManagementService.GetPackageItemByID(ctx, itemID)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if packageItem.PackageID != packageID {
|
||
return fmt.Errorf("组合包项目不属于指定组合包")
|
||
}
|
||
|
||
// 更新项目
|
||
packageItem.SortOrder = cmd.SortOrder
|
||
|
||
return s.productManagementService.UpdatePackageItem(ctx, packageItem)
|
||
}
|
||
|
||
// RemovePackageItem 移除组合包子产品
|
||
func (s *ProductApplicationServiceImpl) RemovePackageItem(ctx context.Context, packageID, itemID string) error {
|
||
// 验证组合包项目是否存在
|
||
packageItem, err := s.productManagementService.GetPackageItemByID(ctx, itemID)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if packageItem.PackageID != packageID {
|
||
return fmt.Errorf("组合包项目不属于指定组合包")
|
||
}
|
||
|
||
return s.productManagementService.DeletePackageItem(ctx, itemID)
|
||
}
|
||
|
||
// ReorderPackageItems 重新排序组合包子产品
|
||
func (s *ProductApplicationServiceImpl) ReorderPackageItems(ctx context.Context, packageID string, cmd *commands.ReorderPackageItemsCommand) error {
|
||
// 验证所有项目是否属于该组合包
|
||
for i, itemID := range cmd.ItemIDs {
|
||
packageItem, err := s.productManagementService.GetPackageItemByID(ctx, itemID)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if packageItem.PackageID != packageID {
|
||
return fmt.Errorf("组合包项目不属于指定组合包")
|
||
}
|
||
|
||
// 更新排序
|
||
packageItem.SortOrder = i + 1
|
||
if err := s.productManagementService.UpdatePackageItem(ctx, packageItem); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// UpdatePackageItems 批量更新组合包子产品
|
||
func (s *ProductApplicationServiceImpl) UpdatePackageItems(ctx context.Context, packageID string, cmd *commands.UpdatePackageItemsCommand) error {
|
||
// 验证组合包是否存在
|
||
packageProduct, err := s.productManagementService.GetProductByID(ctx, packageID)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if !packageProduct.IsPackage {
|
||
return fmt.Errorf("产品不是组合包")
|
||
}
|
||
|
||
// 验证所有子产品是否存在且不是组合包
|
||
for _, item := range cmd.Items {
|
||
subProduct, err := s.productManagementService.GetProductByID(ctx, item.ProductID)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if subProduct.IsPackage {
|
||
return fmt.Errorf("不能将组合包作为子产品")
|
||
}
|
||
}
|
||
|
||
// 使用事务进行批量更新
|
||
return s.productManagementService.UpdatePackageItemsBatch(ctx, packageID, cmd.Items)
|
||
}
|
||
|
||
// GetAvailableProducts 获取可选子产品列表
|
||
// 业务流程:1. 获取启用产品 2. 过滤可订阅产品 3. 构建响应数据
|
||
func (s *ProductApplicationServiceImpl) GetAvailableProducts(ctx context.Context, query *appQueries.GetAvailableProductsQuery) (*responses.ProductListResponse, error) {
|
||
// 构建筛选条件
|
||
filters := make(map[string]interface{})
|
||
filters["is_package"] = false // 只获取非组合包产品
|
||
filters["is_enabled"] = true // 只获取启用产品
|
||
if query.Keyword != "" {
|
||
filters["keyword"] = query.Keyword
|
||
}
|
||
if query.CategoryID != "" {
|
||
filters["category_id"] = query.CategoryID
|
||
}
|
||
|
||
// 设置分页选项
|
||
options := interfaces.ListOptions{
|
||
Page: query.Page,
|
||
PageSize: query.PageSize,
|
||
Sort: "created_at",
|
||
Order: "desc",
|
||
}
|
||
|
||
// 获取产品列表
|
||
products, total, err := s.productManagementService.ListProducts(ctx, filters, options)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 转换为响应对象
|
||
items := make([]responses.ProductInfoResponse, len(products))
|
||
for i := range products {
|
||
items[i] = *s.convertToProductInfoResponse(products[i])
|
||
}
|
||
|
||
return &responses.ProductListResponse{
|
||
Total: total,
|
||
Page: options.Page,
|
||
Size: options.PageSize,
|
||
Items: items,
|
||
}, nil
|
||
}
|
||
|
||
// ListProductsForAdmin 获取产品列表(管理员专用)
|
||
// 业务流程:1. 获取所有产品列表(包括隐藏的) 2. 构建管理员响应数据
|
||
func (s *ProductApplicationServiceImpl) ListProductsForAdmin(ctx context.Context, filters map[string]interface{}, options interfaces.ListOptions) (*responses.ProductAdminListResponse, error) {
|
||
// 调用领域服务获取产品列表(管理员可以看到所有产品)
|
||
products, total, err := s.productManagementService.ListProducts(ctx, filters, options)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 转换为管理员响应对象
|
||
items := make([]responses.ProductAdminInfoResponse, len(products))
|
||
for i := range products {
|
||
items[i] = *s.convertToProductAdminInfoResponse(products[i])
|
||
}
|
||
|
||
return &responses.ProductAdminListResponse{
|
||
Total: total,
|
||
Page: options.Page,
|
||
Size: options.PageSize,
|
||
Items: items,
|
||
}, nil
|
||
}
|
||
|
||
// GetProductByIDForAdmin 根据ID获取产品(管理员专用)
|
||
// 业务流程:1. 获取产品信息 2. 构建管理员响应数据
|
||
func (s *ProductApplicationServiceImpl) GetProductByIDForAdmin(ctx context.Context, query *appQueries.GetProductDetailQuery) (*responses.ProductAdminInfoResponse, error) {
|
||
product, err := s.productManagementService.GetProductWithCategory(ctx, query.ID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
response := s.convertToProductAdminInfoResponse(product)
|
||
|
||
// 如果需要包含文档信息
|
||
if query.WithDocument != nil && *query.WithDocument {
|
||
doc, err := s.documentationAppService.GetDocumentationByProductID(ctx, query.ID)
|
||
if err == nil && doc != nil {
|
||
response.Documentation = doc
|
||
}
|
||
}
|
||
|
||
return response, nil
|
||
}
|
||
|
||
// GetProductByIDForUser 根据ID获取产品(用户端专用)
|
||
// 业务流程:1. 获取产品信息 2. 构建用户响应数据
|
||
// 注意:详情接口不受 is_visible 字段影响,可通过直接访问详情接口查看任何产品
|
||
func (s *ProductApplicationServiceImpl) GetProductByIDForUser(ctx context.Context, query *appQueries.GetProductDetailQuery) (*responses.ProductInfoWithDocumentResponse, error) {
|
||
// 首先尝试通过新ID查找产品
|
||
product, err := s.productManagementService.GetProductWithCategory(ctx, query.ID)
|
||
if err != nil {
|
||
// 如果通过新ID找不到,尝试通过旧ID查找
|
||
product, err = s.productManagementService.GetProductByOldIDWithCategory(ctx, query.ID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
}
|
||
|
||
response := &responses.ProductInfoWithDocumentResponse{
|
||
ProductInfoResponse: *s.convertToProductInfoResponse(product),
|
||
}
|
||
|
||
// 如果需要包含文档信息
|
||
if query.WithDocument != nil && *query.WithDocument {
|
||
doc, err := s.documentationAppService.GetDocumentationByProductID(ctx, product.ID)
|
||
if err == nil && doc != nil {
|
||
response.Documentation = doc
|
||
} else if product.IsPackage && len(response.PackageItems) > 0 {
|
||
// 如果是组合包且没有自己的文档,尝试合并子产品的文档
|
||
mergedDoc := s.mergePackageItemsDocumentation(ctx, product.Code, response.PackageItems)
|
||
if mergedDoc != nil {
|
||
response.Documentation = mergedDoc
|
||
}
|
||
}
|
||
}
|
||
|
||
return response, nil
|
||
}
|
||
|
||
// convertToProductInfoResponse 转换为产品信息响应
|
||
func (s *ProductApplicationServiceImpl) convertToProductInfoResponse(product *entities.Product) *responses.ProductInfoResponse {
|
||
response := &responses.ProductInfoResponse{
|
||
ID: product.ID,
|
||
OldID: product.OldID,
|
||
Name: product.Name,
|
||
Code: product.Code,
|
||
Description: product.Description,
|
||
Content: product.Content,
|
||
CategoryID: product.CategoryID,
|
||
Price: product.Price.InexactFloat64(),
|
||
IsEnabled: product.IsEnabled,
|
||
IsPackage: product.IsPackage,
|
||
SEOTitle: product.SEOTitle,
|
||
SEODescription: product.SEODescription,
|
||
SEOKeywords: product.SEOKeywords,
|
||
CreatedAt: product.CreatedAt,
|
||
UpdatedAt: product.UpdatedAt,
|
||
}
|
||
|
||
// 添加分类信息
|
||
if product.Category != nil {
|
||
response.Category = s.convertToCategoryInfoResponse(product.Category)
|
||
}
|
||
|
||
// 转换组合包项目信息
|
||
if product.IsPackage && len(product.PackageItems) > 0 {
|
||
response.PackageItems = make([]*responses.PackageItemResponse, len(product.PackageItems))
|
||
for i, item := range product.PackageItems {
|
||
response.PackageItems[i] = &responses.PackageItemResponse{
|
||
ID: item.ID,
|
||
ProductID: item.ProductID,
|
||
ProductCode: item.Product.Code,
|
||
ProductName: item.Product.Name,
|
||
SortOrder: item.SortOrder,
|
||
Price: item.Product.Price.InexactFloat64(),
|
||
}
|
||
}
|
||
}
|
||
|
||
return response
|
||
}
|
||
|
||
// convertToProductAdminInfoResponse 转换为管理员产品信息响应
|
||
func (s *ProductApplicationServiceImpl) convertToProductAdminInfoResponse(product *entities.Product) *responses.ProductAdminInfoResponse {
|
||
response := &responses.ProductAdminInfoResponse{
|
||
ID: product.ID,
|
||
OldID: product.OldID,
|
||
Name: product.Name,
|
||
Code: product.Code,
|
||
Description: product.Description,
|
||
Content: product.Content,
|
||
CategoryID: product.CategoryID,
|
||
Price: product.Price.InexactFloat64(),
|
||
IsEnabled: product.IsEnabled,
|
||
IsVisible: product.IsVisible, // 管理员可以看到可见状态
|
||
IsPackage: product.IsPackage,
|
||
SEOTitle: product.SEOTitle,
|
||
SEODescription: product.SEODescription,
|
||
SEOKeywords: product.SEOKeywords,
|
||
CreatedAt: product.CreatedAt,
|
||
UpdatedAt: product.UpdatedAt,
|
||
}
|
||
|
||
// 添加分类信息
|
||
if product.Category != nil {
|
||
response.Category = s.convertToCategoryInfoResponse(product.Category)
|
||
}
|
||
|
||
// 转换组合包项目信息
|
||
if product.IsPackage && len(product.PackageItems) > 0 {
|
||
response.PackageItems = make([]*responses.PackageItemResponse, len(product.PackageItems))
|
||
for i, item := range product.PackageItems {
|
||
response.PackageItems[i] = &responses.PackageItemResponse{
|
||
ID: item.ID,
|
||
ProductID: item.ProductID,
|
||
ProductCode: item.Product.Code,
|
||
ProductName: item.Product.Name,
|
||
SortOrder: item.SortOrder,
|
||
Price: item.Product.Price.InexactFloat64(),
|
||
}
|
||
}
|
||
}
|
||
|
||
return response
|
||
}
|
||
|
||
// convertToCategoryInfoResponse 转换为分类信息响应
|
||
func (s *ProductApplicationServiceImpl) convertToCategoryInfoResponse(category *entities.ProductCategory) *responses.CategoryInfoResponse {
|
||
return &responses.CategoryInfoResponse{
|
||
ID: category.ID,
|
||
Name: category.Name,
|
||
Description: category.Description,
|
||
IsEnabled: category.IsEnabled,
|
||
CreatedAt: category.CreatedAt,
|
||
UpdatedAt: category.UpdatedAt,
|
||
}
|
||
}
|
||
|
||
// GetProductApiConfig 获取产品API配置
|
||
func (s *ProductApplicationServiceImpl) GetProductApiConfig(ctx context.Context, productID string) (*responses.ProductApiConfigResponse, error) {
|
||
return s.productApiConfigAppService.GetProductApiConfig(ctx, productID)
|
||
}
|
||
|
||
// CreateProductApiConfig 创建产品API配置
|
||
func (s *ProductApplicationServiceImpl) CreateProductApiConfig(ctx context.Context, productID string, config *responses.ProductApiConfigResponse) error {
|
||
return s.productApiConfigAppService.CreateProductApiConfig(ctx, productID, config)
|
||
}
|
||
|
||
// UpdateProductApiConfig 更新产品API配置
|
||
func (s *ProductApplicationServiceImpl) UpdateProductApiConfig(ctx context.Context, configID string, config *responses.ProductApiConfigResponse) error {
|
||
return s.productApiConfigAppService.UpdateProductApiConfig(ctx, configID, config)
|
||
}
|
||
|
||
// DeleteProductApiConfig 删除产品API配置
|
||
func (s *ProductApplicationServiceImpl) DeleteProductApiConfig(ctx context.Context, configID string) error {
|
||
return s.productApiConfigAppService.DeleteProductApiConfig(ctx, configID)
|
||
}
|
||
|
||
// mergePackageItemsDocumentation 合并组合包子产品的文档
|
||
// packageCode: 组合包的产品编号,用于生成请求地址
|
||
func (s *ProductApplicationServiceImpl) mergePackageItemsDocumentation(ctx context.Context, packageCode string, packageItems []*responses.PackageItemResponse) *responses.DocumentationResponse {
|
||
if len(packageItems) == 0 {
|
||
return nil
|
||
}
|
||
|
||
// 收集所有子产品的ID
|
||
productIDs := make([]string, 0, len(packageItems))
|
||
for _, item := range packageItems {
|
||
productIDs = append(productIDs, item.ProductID)
|
||
}
|
||
|
||
// 批量获取子产品的文档
|
||
docs, err := s.documentationAppService.GetDocumentationsByProductIDs(ctx, productIDs)
|
||
if err != nil || len(docs) == 0 {
|
||
s.logger.Debug("组合包子产品文档获取失败或为空", zap.Error(err))
|
||
return nil
|
||
}
|
||
|
||
// 创建文档映射,方便按产品ID查找
|
||
docMap := make(map[string]*responses.DocumentationResponse)
|
||
for i := range docs {
|
||
docMap[docs[i].ProductID] = &docs[i]
|
||
}
|
||
|
||
// 合并文档内容
|
||
mergedDoc := &responses.DocumentationResponse{
|
||
ProductID: packageItems[0].ProductID, // 使用第一个子产品的ID作为标识
|
||
RequestMethod: "POST", // 默认方法
|
||
Version: "1.0",
|
||
}
|
||
|
||
// 收集子产品的文档数据,用于构建组合包响应结构
|
||
subProductDocs := make([]subProductDocInfo, 0)
|
||
|
||
// 按packageItems的顺序合并文档
|
||
for _, item := range packageItems {
|
||
doc, exists := docMap[item.ProductID]
|
||
if !exists || doc == nil {
|
||
continue
|
||
}
|
||
|
||
// 保存子产品文档信息,用于构建组合包响应结构
|
||
subProductDocs = append(subProductDocs, subProductDocInfo{
|
||
item: item,
|
||
doc: doc,
|
||
responseFields: doc.ResponseFields,
|
||
responseExample: doc.ResponseExample,
|
||
})
|
||
}
|
||
|
||
// 构建组合包的返回字段说明
|
||
if len(subProductDocs) > 0 {
|
||
mergedDoc.ResponseFields = s.buildCombPackageResponseFields(subProductDocs)
|
||
}
|
||
|
||
// 构建组合包的响应示例
|
||
if len(subProductDocs) > 0 {
|
||
mergedDoc.ResponseExample = s.buildCombPackageResponseExample(subProductDocs)
|
||
}
|
||
|
||
// 合并请求参数(从DTO结构体中提取所有子产品的参数,去重后生成统一文档)
|
||
if len(packageItems) > 0 {
|
||
mergedDoc.RequestParams = s.mergeRequestParamsFromDTOs(ctx, packageItems)
|
||
}
|
||
|
||
// 设置请求地址和方法(使用组合包的产品编号)
|
||
mergedDoc.RequestURL = fmt.Sprintf("https://api.tianyuanapi.com/api/v1/%s?t=13位时间戳", packageCode)
|
||
mergedDoc.RequestMethod = "POST"
|
||
|
||
// 错误代码和请求方式部分设置为空,前端会显示默认内容
|
||
mergedDoc.ErrorCodes = ""
|
||
mergedDoc.BasicInfo = ""
|
||
|
||
// 如果没有任何有意义的内容(请求参数、返回字段、响应示例都没有),返回nil
|
||
if mergedDoc.RequestParams == "" && mergedDoc.ResponseFields == "" && mergedDoc.ResponseExample == "" {
|
||
return nil
|
||
}
|
||
|
||
return mergedDoc
|
||
}
|
||
|
||
// joinParts 使用分隔符连接文档部分
|
||
func joinParts(parts []string) string {
|
||
if len(parts) == 0 {
|
||
return ""
|
||
}
|
||
if len(parts) == 1 {
|
||
return parts[0]
|
||
}
|
||
|
||
// 使用分隔线分隔不同产品的文档
|
||
return strings.Join(parts, "\n\n---\n\n")
|
||
}
|
||
|
||
// subProductDocInfo 子产品文档信息
|
||
type subProductDocInfo struct {
|
||
item *responses.PackageItemResponse
|
||
doc *responses.DocumentationResponse
|
||
responseFields string
|
||
responseExample string
|
||
}
|
||
|
||
// buildCombPackageResponseFields 构建组合包的返回字段说明
|
||
func (s *ProductApplicationServiceImpl) buildCombPackageResponseFields(subProductDocs []subProductDocInfo) string {
|
||
var builder strings.Builder
|
||
|
||
// 组合包响应结构说明
|
||
builder.WriteString("## 组合包响应结构\n\n")
|
||
builder.WriteString("组合包API返回的是一个包含所有子产品响应结果的数组。\n\n")
|
||
builder.WriteString("| 字段名 | 类型 | 说明 |\n")
|
||
builder.WriteString("|--------|------|------|\n")
|
||
builder.WriteString("| responses | array | 子产品响应列表 |\n")
|
||
builder.WriteString("| responses[].api_code | string | 子产品代码 |\n")
|
||
builder.WriteString("| responses[].success | boolean | 该子产品调用是否成功 |\n")
|
||
builder.WriteString("| responses[].data | object/null | 子产品的响应数据(成功时返回数据,失败时为null) |\n")
|
||
builder.WriteString("| responses[].error | string | 错误信息(仅在失败时返回) |\n\n")
|
||
|
||
// 各个子产品的data字段说明
|
||
builder.WriteString("## 子产品响应数据说明\n\n")
|
||
for i, spDoc := range subProductDocs {
|
||
if i > 0 {
|
||
builder.WriteString("\n---\n\n")
|
||
}
|
||
productTitle := fmt.Sprintf("### %s (%s) 的 data 字段", spDoc.item.ProductName, spDoc.item.ProductCode)
|
||
builder.WriteString(productTitle + "\n\n")
|
||
if spDoc.responseFields != "" {
|
||
// 移除子产品文档中可能存在的标题,只保留字段说明
|
||
fields := spDoc.responseFields
|
||
// 如果包含标题,尝试提取data字段相关的部分
|
||
if strings.Contains(fields, "data") || strings.Contains(fields, "返回字段") {
|
||
builder.WriteString(fields)
|
||
} else {
|
||
builder.WriteString(fields)
|
||
}
|
||
} else {
|
||
builder.WriteString("该子产品的 data 字段结构请参考该产品的单独文档。")
|
||
}
|
||
builder.WriteString("\n")
|
||
}
|
||
|
||
return builder.String()
|
||
}
|
||
|
||
// buildCombPackageResponseExample 构建组合包的响应示例
|
||
func (s *ProductApplicationServiceImpl) buildCombPackageResponseExample(subProductDocs []subProductDocInfo) string {
|
||
var builder strings.Builder
|
||
|
||
builder.WriteString("## 组合包响应示例\n\n")
|
||
builder.WriteString("### 成功响应(部分子产品成功)\n\n")
|
||
builder.WriteString("```json\n")
|
||
builder.WriteString("{\n")
|
||
builder.WriteString(" \"responses\": [\n")
|
||
|
||
// 构建示例JSON - 第一个成功,第二个可能失败
|
||
for i, spDoc := range subProductDocs {
|
||
if i > 0 {
|
||
builder.WriteString(",\n")
|
||
}
|
||
builder.WriteString(" {\n")
|
||
builder.WriteString(fmt.Sprintf(" \"api_code\": \"%s\",\n", spDoc.item.ProductCode))
|
||
|
||
// 第一个示例显示成功,其他可能显示部分失败
|
||
if i == 0 {
|
||
builder.WriteString(" \"success\": true,\n")
|
||
builder.WriteString(" \"data\": {\n")
|
||
builder.WriteString(fmt.Sprintf(" // %s 的实际响应数据\n", spDoc.item.ProductName))
|
||
builder.WriteString(" // 具体字段结构请参考下方该子产品的详细响应示例\n")
|
||
builder.WriteString(" }\n")
|
||
} else {
|
||
// 第二个可能成功也可能失败,展示成功的情况
|
||
builder.WriteString(" \"success\": true,\n")
|
||
builder.WriteString(" \"data\": {}\n")
|
||
}
|
||
builder.WriteString(" }")
|
||
}
|
||
|
||
builder.WriteString("\n ]\n")
|
||
builder.WriteString("}\n")
|
||
builder.WriteString("```\n\n")
|
||
|
||
// 添加失败示例
|
||
builder.WriteString("### 部分失败响应示例\n\n")
|
||
builder.WriteString("```json\n")
|
||
builder.WriteString("{\n")
|
||
builder.WriteString(" \"responses\": [\n")
|
||
builder.WriteString(" {\n")
|
||
if len(subProductDocs) > 0 {
|
||
builder.WriteString(fmt.Sprintf(" \"api_code\": \"%s\",\n", subProductDocs[0].item.ProductCode))
|
||
}
|
||
builder.WriteString(" \"success\": false,\n")
|
||
builder.WriteString(" \"data\": null,\n")
|
||
builder.WriteString(" \"error\": \"参数校验不正确\"\n")
|
||
builder.WriteString(" }")
|
||
if len(subProductDocs) > 1 {
|
||
builder.WriteString(",\n")
|
||
builder.WriteString(" {\n")
|
||
builder.WriteString(fmt.Sprintf(" \"api_code\": \"%s\",\n", subProductDocs[1].item.ProductCode))
|
||
builder.WriteString(" \"success\": true,\n")
|
||
builder.WriteString(" \"data\": {}\n")
|
||
builder.WriteString(" }")
|
||
}
|
||
builder.WriteString("\n ]\n")
|
||
builder.WriteString("}\n")
|
||
builder.WriteString("```\n\n")
|
||
|
||
// 添加各子产品的详细响应示例说明
|
||
if len(subProductDocs) > 0 {
|
||
builder.WriteString("## 各子产品详细响应示例\n\n")
|
||
builder.WriteString("以下为各个子产品的详细响应示例,组合包响应中的 `data` 字段即为对应子产品的完整响应数据。\n\n")
|
||
|
||
for i, spDoc := range subProductDocs {
|
||
if i > 0 {
|
||
builder.WriteString("\n---\n\n")
|
||
}
|
||
productTitle := fmt.Sprintf("### %s (%s)", spDoc.item.ProductName, spDoc.item.ProductCode)
|
||
builder.WriteString(productTitle + "\n\n")
|
||
if spDoc.responseExample != "" {
|
||
builder.WriteString(spDoc.responseExample)
|
||
} else {
|
||
builder.WriteString("该子产品的响应示例请参考该产品的单独文档。")
|
||
}
|
||
builder.WriteString("\n")
|
||
}
|
||
}
|
||
|
||
return builder.String()
|
||
}
|
||
|
||
// paramField 参数字段信息
|
||
type paramField struct {
|
||
Name string
|
||
Type string
|
||
Required string
|
||
Description string
|
||
}
|
||
|
||
// mergeRequestParamsFromDTOs 从DTO结构体中提取并合并组合包的请求参数
|
||
func (s *ProductApplicationServiceImpl) mergeRequestParamsFromDTOs(ctx context.Context, packageItems []*responses.PackageItemResponse) string {
|
||
if len(packageItems) == 0 {
|
||
return ""
|
||
}
|
||
|
||
// 用于存储所有参数字段,key为字段名(json tag)
|
||
paramMap := make(map[string]*paramField)
|
||
|
||
// 获取DTO映射(复用form_config_service的逻辑)
|
||
dtoMap := s.getDTOMap()
|
||
|
||
// 遍历每个子产品,从DTO中提取字段
|
||
for _, item := range packageItems {
|
||
// 从DTO映射中获取子产品的DTO结构体
|
||
if dtoStruct, exists := dtoMap[item.ProductCode]; exists {
|
||
// 通过反射解析DTO字段
|
||
s.extractFieldsFromDTO(dtoStruct, paramMap)
|
||
} else {
|
||
// 如果没有找到DTO,尝试通过FormConfigService获取
|
||
if s.formConfigService != nil {
|
||
formConfig, err := s.formConfigService.GetFormConfig(ctx, item.ProductCode)
|
||
if err == nil && formConfig != nil {
|
||
// 从表单配置中提取字段
|
||
for _, field := range formConfig.Fields {
|
||
if _, exists := paramMap[field.Name]; !exists {
|
||
requiredStr := "否"
|
||
if field.Required {
|
||
requiredStr = "是"
|
||
}
|
||
paramMap[field.Name] = ¶mField{
|
||
Name: field.Name,
|
||
Type: s.mapFieldTypeToDocType(field.Type),
|
||
Required: requiredStr,
|
||
Description: field.Description,
|
||
}
|
||
} else {
|
||
// 如果字段已存在,保留更详细的描述,如果新字段是必填则更新
|
||
existing := paramMap[field.Name]
|
||
if field.Description != "" && existing.Description == "" {
|
||
existing.Description = field.Description
|
||
}
|
||
if field.Required && existing.Required != "是" {
|
||
existing.Required = "是"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 如果没有提取到任何参数,返回空字符串
|
||
if len(paramMap) == 0 {
|
||
return ""
|
||
}
|
||
|
||
// 构建合并后的请求参数文档
|
||
var result strings.Builder
|
||
result.WriteString("## 请求参数\n\n")
|
||
|
||
// 构建JSON示例
|
||
result.WriteString("```json\n{\n")
|
||
first := true
|
||
for fieldName := range paramMap {
|
||
if !first {
|
||
result.WriteString(",\n")
|
||
}
|
||
result.WriteString(fmt.Sprintf(` "%s": "string"`, fieldName))
|
||
first = false
|
||
}
|
||
result.WriteString("\n}\n```\n\n")
|
||
|
||
// 构建表格
|
||
result.WriteString("| 字段名 | 类型 | 必填 | 描述 |\n")
|
||
result.WriteString("|--------|------|------|------|\n")
|
||
|
||
// 按字段名排序输出
|
||
fieldNames := make([]string, 0, len(paramMap))
|
||
for fieldName := range paramMap {
|
||
fieldNames = append(fieldNames, fieldName)
|
||
}
|
||
// 简单排序
|
||
for i := 0; i < len(fieldNames)-1; i++ {
|
||
for j := i + 1; j < len(fieldNames); j++ {
|
||
if fieldNames[i] > fieldNames[j] {
|
||
fieldNames[i], fieldNames[j] = fieldNames[j], fieldNames[i]
|
||
}
|
||
}
|
||
}
|
||
|
||
for _, fieldName := range fieldNames {
|
||
field := paramMap[fieldName]
|
||
result.WriteString(fmt.Sprintf("| %s | %s | %s | %s |\n",
|
||
field.Name, field.Type, field.Required, field.Description))
|
||
}
|
||
|
||
result.WriteString("\n通过加密后得到 Base64 字符串,将其放入到请求体中,字段名为 `data`。\n\n")
|
||
result.WriteString("```json\n{\n \"data\": \"xxxx(base64)\"\n}\n```")
|
||
|
||
return result.String()
|
||
}
|
||
|
||
// getDTOMap 获取API代码到DTO结构体的映射(复用form_config_service的逻辑)
|
||
func (s *ProductApplicationServiceImpl) getDTOMap() map[string]interface{} {
|
||
return map[string]interface{}{
|
||
"IVYZ9363": &dto.IVYZ9363Req{},
|
||
"IVYZ385E": &dto.IVYZ385EReq{},
|
||
"IVYZ5733": &dto.IVYZ5733Req{},
|
||
"FLXG3D56": &dto.FLXG3D56Req{},
|
||
"FLXG75FE": &dto.FLXG75FEReq{},
|
||
"FLXG0V3B": &dto.FLXG0V3BReq{},
|
||
"FLXG0V4B": &dto.FLXG0V4BReq{},
|
||
"FLXG54F5": &dto.FLXG54F5Req{},
|
||
"FLXG162A": &dto.FLXG162AReq{},
|
||
"FLXG0687": &dto.FLXG0687Req{},
|
||
"FLXGBC21": &dto.FLXGBC21Req{},
|
||
"FLXG970F": &dto.FLXG970FReq{},
|
||
"FLXG5876": &dto.FLXG5876Req{},
|
||
"FLXG9687": &dto.FLXG9687Req{},
|
||
"FLXGC9D1": &dto.FLXGC9D1Req{},
|
||
"FLXGCA3D": &dto.FLXGCA3DReq{},
|
||
"FLXGDEC7": &dto.FLXGDEC7Req{},
|
||
"JRZQ0A03": &dto.JRZQ0A03Req{},
|
||
"JRZQ4AA8": &dto.JRZQ4AA8Req{},
|
||
"JRZQ8203": &dto.JRZQ8203Req{},
|
||
"JRZQDBCE": &dto.JRZQDBCEReq{},
|
||
"QYGL2ACD": &dto.QYGL2ACDReq{},
|
||
"QYGL6F2D": &dto.QYGL6F2DReq{},
|
||
"QYGL45BD": &dto.QYGL45BDReq{},
|
||
"QYGL8261": &dto.QYGL8261Req{},
|
||
"QYGL8271": &dto.QYGL8271Req{},
|
||
"QYGLB4C0": &dto.QYGLB4C0Req{},
|
||
"QYGL23T7": &dto.QYGL23T7Req{},
|
||
"QYGL5A3C": &dto.QYGL5A3CReq{},
|
||
"QYGL8B4D": &dto.QYGL8B4DReq{},
|
||
"QYGL9E2F": &dto.QYGL9E2FReq{},
|
||
"QYGL7C1A": &dto.QYGL7C1AReq{},
|
||
"QYGL3F8E": &dto.QYGL3F8EReq{},
|
||
"YYSY4B37": &dto.YYSY4B37Req{},
|
||
"YYSY4B21": &dto.YYSY4B21Req{},
|
||
"YYSY6F2E": &dto.YYSY6F2EReq{},
|
||
"YYSY09CD": &dto.YYSY09CDReq{},
|
||
"IVYZ0b03": &dto.IVYZ0b03Req{},
|
||
"YYSYBE08": &dto.YYSYBE08Req{},
|
||
"YYSYD50F": &dto.YYSYD50FReq{},
|
||
"YYSYF7DB": &dto.YYSYF7DBReq{},
|
||
"IVYZ9A2B": &dto.IVYZ9A2BReq{},
|
||
"IVYZ7F2A": &dto.IVYZ7F2AReq{},
|
||
"IVYZ4E8B": &dto.IVYZ4E8BReq{},
|
||
"IVYZ1C9D": &dto.IVYZ1C9DReq{},
|
||
"IVYZGZ08": &dto.IVYZGZ08Req{},
|
||
"FLXG8A3F": &dto.FLXG8A3FReq{},
|
||
"FLXG5B2E": &dto.FLXG5B2EReq{},
|
||
"COMB298Y": &dto.COMB298YReq{},
|
||
"COMB86PM": &dto.COMB86PMReq{},
|
||
"QCXG7A2B": &dto.QCXG7A2BReq{},
|
||
"COMENT01": &dto.COMENT01Req{},
|
||
"JRZQ09J8": &dto.JRZQ09J8Req{},
|
||
"FLXGDEA8": &dto.FLXGDEA8Req{},
|
||
"FLXGDEA9": &dto.FLXGDEA9Req{},
|
||
"JRZQ1D09": &dto.JRZQ1D09Req{},
|
||
"IVYZ2A8B": &dto.IVYZ2A8BReq{},
|
||
"IVYZ7C9D": &dto.IVYZ7C9DReq{},
|
||
"IVYZ5E3F": &dto.IVYZ5E3FReq{},
|
||
"YYSY4F2E": &dto.YYSY4F2EReq{},
|
||
"YYSY8B1C": &dto.YYSY8B1CReq{},
|
||
"YYSY6D9A": &dto.YYSY6D9AReq{},
|
||
"YYSY3E7F": &dto.YYSY3E7FReq{},
|
||
"FLXG5A3B": &dto.FLXG5A3BReq{},
|
||
"FLXG9C1D": &dto.FLXG9C1DReq{},
|
||
"FLXG2E8F": &dto.FLXG2E8FReq{},
|
||
"JRZQ3C7B": &dto.JRZQ3C7BReq{},
|
||
"JRZQ8A2D": &dto.JRZQ8A2DReq{},
|
||
"JRZQ5E9F": &dto.JRZQ5E9FReq{},
|
||
"JRZQ4B6C": &dto.JRZQ4B6CReq{},
|
||
"JRZQ7F1A": &dto.JRZQ7F1AReq{},
|
||
"DWBG6A2C": &dto.DWBG6A2CReq{},
|
||
"DWBG8B4D": &dto.DWBG8B4DReq{},
|
||
"FLXG8B4D": &dto.FLXG8B4DReq{},
|
||
"IVYZ81NC": &dto.IVYZ81NCReq{},
|
||
"IVYZ7F3A": &dto.IVYZ7F3AReq{},
|
||
"IVYZ3P9M": &dto.IVYZ3P9MReq{},
|
||
"IVYZ3A7F": &dto.IVYZ3A7FReq{},
|
||
"IVYZ9D2E": &dto.IVYZ9D2EReq{},
|
||
"DWBG7F3A": &dto.DWBG7F3AReq{},
|
||
"YYSY8F3A": &dto.YYSY8F3AReq{},
|
||
"QCXG9P1C": &dto.QCXG9P1CReq{},
|
||
"JRZQ9E2A": &dto.JRZQ9E2AReq{},
|
||
"YYSY9A1B": &dto.YYSY9A1BReq{},
|
||
"YYSY8C2D": &dto.YYSY8C2DReq{},
|
||
"YYSY7D3E": &dto.YYSY7D3EReq{},
|
||
"YYSY9E4A": &dto.YYSY9E4AReq{},
|
||
"JRZQ6F2A": &dto.JRZQ6F2AReq{},
|
||
"JRZQ8B3C": &dto.JRZQ8B3CReq{},
|
||
"JRZQ9D4E": &dto.JRZQ9D4EReq{},
|
||
"FLXG7E8F": &dto.FLXG7E8FReq{},
|
||
"QYGL5F6A": &dto.QYGL5F6AReq{},
|
||
"IVYZ6G7H": &dto.IVYZ6G7HReq{},
|
||
"IVYZ8I9J": &dto.IVYZ8I9JReq{},
|
||
"JRZQ0L85": &dto.JRZQ0L85Req{},
|
||
}
|
||
}
|
||
|
||
// extractFieldsFromDTO 从DTO结构体中提取字段信息
|
||
func (s *ProductApplicationServiceImpl) extractFieldsFromDTO(dtoStruct interface{}, paramMap map[string]*paramField) {
|
||
if dtoStruct == nil {
|
||
return
|
||
}
|
||
|
||
t := reflect.TypeOf(dtoStruct).Elem()
|
||
if t == nil {
|
||
return
|
||
}
|
||
|
||
for i := 0; i < t.NumField(); i++ {
|
||
field := t.Field(i)
|
||
|
||
// 获取JSON标签
|
||
jsonTag := field.Tag.Get("json")
|
||
if jsonTag == "" || jsonTag == "-" {
|
||
continue
|
||
}
|
||
|
||
// 去除omitempty等选项
|
||
jsonTag = strings.Split(jsonTag, ",")[0]
|
||
|
||
// 跳过加密相关的字段(不应该出现在业务参数文档中)
|
||
if jsonTag == "data" || jsonTag == "encrypt" {
|
||
continue
|
||
}
|
||
|
||
// 获取验证标签
|
||
validateTag := field.Tag.Get("validate")
|
||
required := strings.Contains(validateTag, "required")
|
||
requiredStr := "否"
|
||
if required {
|
||
requiredStr = "是"
|
||
}
|
||
|
||
// 获取字段类型
|
||
fieldType := s.getGoTypeName(field.Type)
|
||
|
||
// 生成字段描述
|
||
description := s.generateFieldDescription(jsonTag, validateTag)
|
||
|
||
// 如果字段已存在,保留更详细的描述,如果新字段是必填则更新
|
||
if existing, exists := paramMap[jsonTag]; exists {
|
||
if description != "" && existing.Description == "" {
|
||
existing.Description = description
|
||
}
|
||
if required && existing.Required != "是" {
|
||
existing.Required = "是"
|
||
}
|
||
} else {
|
||
paramMap[jsonTag] = ¶mField{
|
||
Name: jsonTag,
|
||
Type: fieldType,
|
||
Required: requiredStr,
|
||
Description: description,
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// getGoTypeName 获取Go类型的名称(转换为文档中的类型描述)
|
||
func (s *ProductApplicationServiceImpl) getGoTypeName(fieldType reflect.Type) string {
|
||
switch fieldType.Kind() {
|
||
case reflect.String:
|
||
return "string"
|
||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||
return "int"
|
||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||
return "int"
|
||
case reflect.Float32, reflect.Float64:
|
||
return "float"
|
||
case reflect.Bool:
|
||
return "boolean"
|
||
case reflect.Slice, reflect.Array:
|
||
return "array"
|
||
case reflect.Map:
|
||
return "object"
|
||
default:
|
||
return "string"
|
||
}
|
||
}
|
||
|
||
// generateFieldDescription 生成字段描述
|
||
func (s *ProductApplicationServiceImpl) generateFieldDescription(jsonTag string, validateTag string) string {
|
||
// 基础字段描述映射
|
||
descMap := map[string]string{
|
||
"mobile_no": "手机号码(11位)",
|
||
"id_card": "身份证号码(18位)",
|
||
"name": "姓名",
|
||
"man_name": "男方姓名",
|
||
"woman_name": "女方姓名",
|
||
"man_id_card": "男方身份证号码",
|
||
"woman_id_card": "女方身份证号码",
|
||
"ent_name": "企业名称",
|
||
"legal_person": "法人姓名",
|
||
"ent_code": "统一社会信用代码",
|
||
"auth_date": "授权日期范围(格式:YYYYMMDD-YYYYMMDD)",
|
||
"time_range": "时间范围(格式:HH:MM-HH:MM)",
|
||
"authorized": "是否授权(0-未授权,1-已授权)",
|
||
"authorization_url": "授权书URL地址(支持格式:pdf/jpg/jpeg/png/bmp)",
|
||
"unique_id": "唯一标识",
|
||
"return_url": "返回链接",
|
||
"mobile_type": "手机类型",
|
||
"start_date": "开始日期",
|
||
"years": "查询年数(0-100)",
|
||
"bank_card": "银行卡号",
|
||
"user_type": "关系类型(1-ETC开户人;2-车辆所有人;3-ETC经办人)",
|
||
"vehicle_type": "车辆类型(0-客车;1-货车;2-全部)",
|
||
"page_num": "页码(从1开始)",
|
||
"page_size": "每页数量(1-100)",
|
||
"use_scenario": "使用场景(1-信贷审核;2-保险评估;3-招聘背景调查;4-其他业务场景;99-其他)",
|
||
"wo": "我的",
|
||
}
|
||
|
||
if desc, exists := descMap[jsonTag]; exists {
|
||
return desc
|
||
}
|
||
|
||
// 如果没有预定义,根据验证规则生成描述
|
||
if strings.Contains(validateTag, "required") {
|
||
return "必填字段"
|
||
}
|
||
|
||
return ""
|
||
}
|
||
|
||
// mapFieldTypeToDocType 将前端字段类型映射为文档类型
|
||
func (s *ProductApplicationServiceImpl) mapFieldTypeToDocType(frontendType string) string {
|
||
switch frontendType {
|
||
case "tel", "text":
|
||
return "string"
|
||
case "number":
|
||
return "int"
|
||
case "checkbox":
|
||
return "boolean"
|
||
case "select":
|
||
return "string"
|
||
case "date":
|
||
return "string"
|
||
case "url":
|
||
return "string"
|
||
default:
|
||
return "string"
|
||
}
|
||
}
|