f
This commit is contained in:
74
internal/domains/api/services/processors/comb/README.md
Normal file
74
internal/domains/api/services/processors/comb/README.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# 组合包处理器说明
|
||||
|
||||
## 🚀 动态组合包机制
|
||||
|
||||
从现在开始,组合包支持**动态处理机制**,大大简化了组合包的开发和维护工作。
|
||||
|
||||
## 📋 工作原理
|
||||
|
||||
### 1. 自动识别
|
||||
- 所有以 `COMB` 开头的API编码会被自动识别为组合包
|
||||
- 系统会自动调用通用组合包处理器处理请求
|
||||
|
||||
### 2. 处理流程
|
||||
1. **优先级检查**:首先检查是否有注册的自定义处理器
|
||||
2. **通用处理**:如果没有自定义处理器,且API编码以COMB开头,使用通用处理器
|
||||
3. **数据库驱动**:根据数据库中的组合包配置自动调用相应的子产品处理器
|
||||
|
||||
## 🛠️ 使用方式
|
||||
|
||||
### 普通组合包(无自定义逻辑)
|
||||
**只需要在数据库配置,无需编写任何代码!**
|
||||
|
||||
1. 在 `products` 表中创建组合包产品:
|
||||
```sql
|
||||
INSERT INTO products (code, name, is_package, ...)
|
||||
VALUES ('COMB1234', '新组合包', true, ...);
|
||||
```
|
||||
|
||||
2. 在 `product_package_items` 表中配置子产品:
|
||||
```sql
|
||||
INSERT INTO product_package_items (package_id, product_id, sort_order)
|
||||
VALUES
|
||||
('组合包产品ID', '子产品1ID', 1),
|
||||
('组合包产品ID', '子产品2ID', 2);
|
||||
```
|
||||
|
||||
3. **直接调用**:无需任何额外编码,API立即可用!
|
||||
|
||||
### 自定义组合包(有特殊逻辑)
|
||||
如果需要对组合包结果进行后处理,才需要编写代码:
|
||||
|
||||
1. **创建处理器文件**:`combXXXX_processor.go`
|
||||
2. **注册处理器**:在 `api_request_service.go` 中注册
|
||||
3. **实现自定义逻辑**:在处理器中实现特殊业务逻辑
|
||||
|
||||
## 📁 现有组合包示例
|
||||
|
||||
### COMB86PM(自定义处理器)
|
||||
```go
|
||||
// 有自定义逻辑:重命名子产品ApiCode
|
||||
for _, resp := range combinedResult.Responses {
|
||||
if resp.ApiCode == "FLXGBC21" {
|
||||
resp.ApiCode = "FLXG54F5"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### COMB298Y(通用处理器)
|
||||
- **无需编码**:已删除专门的处理器文件
|
||||
- **自动处理**:通过数据库配置自动工作
|
||||
|
||||
## ✅ 优势
|
||||
|
||||
1. **零配置**:普通组合包只需数据库配置,无需编码
|
||||
2. **灵活性**:特殊需求仍可通过自定义处理器实现
|
||||
3. **维护性**:减少重复代码,统一处理逻辑
|
||||
4. **扩展性**:新增组合包极其简单,配置即用
|
||||
|
||||
## 🔧 开发建议
|
||||
|
||||
1. **优先使用通用处理器**:除非有特殊业务逻辑,否则不要编写自定义处理器
|
||||
2. **命名规范**:组合包编码必须以 `COMB` 开头
|
||||
3. **数据库配置**:确保组合包在数据库中正确配置了 `is_package=true` 和子产品关系
|
||||
4. **排序控制**:通过 `sort_order` 字段控制子产品在响应中的顺序
|
||||
@@ -0,0 +1,36 @@
|
||||
package comb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"hyapi-server/internal/domains/api/dto"
|
||||
"hyapi-server/internal/domains/api/services/processors"
|
||||
)
|
||||
|
||||
// ProcessCOMB86PMRequest COMB86PM API处理方法
|
||||
func ProcessCOMB86PMRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||
var paramsDto dto.COMB86PMReq
|
||||
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)
|
||||
}
|
||||
|
||||
// 调用组合包服务处理请求
|
||||
// Options会自动传递给所有子处理器
|
||||
combinedResult, err := deps.CombService.ProcessCombRequest(ctx, params, deps, "COMB86PM")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 如果有ApiCode为FLXG54F5的子产品,改名为FLXG54F6
|
||||
for _, resp := range combinedResult.Responses {
|
||||
if resp.ApiCode == "FLXGBC21" {
|
||||
resp.ApiCode = "FLXG54F5"
|
||||
}
|
||||
}
|
||||
return json.Marshal(combinedResult)
|
||||
}
|
||||
178
internal/domains/api/services/processors/comb/comb_service.go
Normal file
178
internal/domains/api/services/processors/comb/comb_service.go
Normal file
@@ -0,0 +1,178 @@
|
||||
package comb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"hyapi-server/internal/domains/api/services/processors"
|
||||
"hyapi-server/internal/domains/product/entities"
|
||||
"hyapi-server/internal/domains/product/services"
|
||||
)
|
||||
|
||||
// CombService 组合包服务
|
||||
type CombService struct {
|
||||
productManagementService *services.ProductManagementService
|
||||
processorRegistry map[string]processors.ProcessorFunc
|
||||
}
|
||||
|
||||
// NewCombService 创建组合包服务
|
||||
func NewCombService(productManagementService *services.ProductManagementService) *CombService {
|
||||
return &CombService{
|
||||
productManagementService: productManagementService,
|
||||
processorRegistry: make(map[string]processors.ProcessorFunc),
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterProcessor 注册处理器
|
||||
func (cs *CombService) RegisterProcessor(apiCode string, processor processors.ProcessorFunc) {
|
||||
cs.processorRegistry[apiCode] = processor
|
||||
}
|
||||
|
||||
// GetProcessor 获取处理器(用于内部调用)
|
||||
func (cs *CombService) GetProcessor(apiCode string) (processors.ProcessorFunc, bool) {
|
||||
processor, exists := cs.processorRegistry[apiCode]
|
||||
return processor, exists
|
||||
}
|
||||
|
||||
// ProcessCombRequest 处理组合包请求 - 实现 CombServiceInterface
|
||||
func (cs *CombService) ProcessCombRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies, packageCode string) (*processors.CombinedResult, error) {
|
||||
// 1. 根据组合包code获取产品信息
|
||||
packageProduct, err := cs.productManagementService.GetProductByCode(ctx, packageCode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取组合包信息失败: %s", err.Error())
|
||||
}
|
||||
|
||||
if !packageProduct.IsPackage {
|
||||
return nil, fmt.Errorf("产品 %s 不是组合包", packageCode)
|
||||
}
|
||||
|
||||
// 2. 获取组合包的所有子产品
|
||||
packageItems, err := cs.productManagementService.GetPackageItems(ctx, packageProduct.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取组合包子产品失败: %s", err.Error())
|
||||
}
|
||||
|
||||
if len(packageItems) == 0 {
|
||||
return nil, fmt.Errorf("组合包 %s 没有配置子产品", packageCode)
|
||||
}
|
||||
|
||||
// 3. 并发调用所有子产品的处理器
|
||||
results := cs.processSubProducts(ctx, params, deps, packageItems)
|
||||
|
||||
// 4. 组合结果
|
||||
return cs.combineResults(results)
|
||||
}
|
||||
|
||||
// processSubProducts 并发处理子产品
|
||||
func (cs *CombService) processSubProducts(
|
||||
ctx context.Context,
|
||||
params []byte,
|
||||
deps *processors.ProcessorDependencies,
|
||||
packageItems []*entities.ProductPackageItem,
|
||||
) []*processors.SubProductResult {
|
||||
results := make([]*processors.SubProductResult, 0, len(packageItems))
|
||||
var wg sync.WaitGroup
|
||||
var mu sync.Mutex
|
||||
|
||||
// 并发处理每个子产品
|
||||
for _, item := range packageItems {
|
||||
wg.Add(1)
|
||||
go func(item *entities.ProductPackageItem) {
|
||||
defer wg.Done()
|
||||
|
||||
result := cs.processSingleSubProduct(ctx, params, deps, item)
|
||||
|
||||
mu.Lock()
|
||||
results = append(results, result)
|
||||
mu.Unlock()
|
||||
}(item)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
// 按SortOrder排序
|
||||
sort.Slice(results, func(i, j int) bool {
|
||||
return results[i].SortOrder < results[j].SortOrder
|
||||
})
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
// processSingleSubProduct 处理单个子产品
|
||||
func (cs *CombService) processSingleSubProduct(
|
||||
ctx context.Context,
|
||||
params []byte,
|
||||
deps *processors.ProcessorDependencies,
|
||||
item *entities.ProductPackageItem,
|
||||
) *processors.SubProductResult {
|
||||
result := &processors.SubProductResult{
|
||||
ApiCode: item.Product.Code,
|
||||
SortOrder: item.SortOrder,
|
||||
Success: false,
|
||||
}
|
||||
|
||||
// 查找对应的处理器
|
||||
processor, exists := cs.processorRegistry[item.Product.Code]
|
||||
if !exists {
|
||||
result.Error = fmt.Sprintf("未找到处理器: %s", item.Product.Code)
|
||||
return result
|
||||
}
|
||||
|
||||
// 调用处理器
|
||||
respBytes, err := processor(ctx, params, deps)
|
||||
if err != nil {
|
||||
result.Error = err.Error()
|
||||
return result
|
||||
}
|
||||
|
||||
// 解析响应
|
||||
var responseData interface{}
|
||||
if err := json.Unmarshal(respBytes, &responseData); err != nil {
|
||||
result.Error = fmt.Sprintf("解析响应失败: %s", err.Error())
|
||||
return result
|
||||
}
|
||||
|
||||
result.Success = true
|
||||
result.Data = responseData
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// combineResults 组合所有子产品的结果
|
||||
// 只要至少有一个子产品成功,就返回成功结果(部分成功也算成功)
|
||||
// 只有当所有子产品都失败时,才返回错误
|
||||
func (cs *CombService) combineResults(results []*processors.SubProductResult) (*processors.CombinedResult, error) {
|
||||
// 检查是否至少有一个成功的子产品
|
||||
hasSuccess := false
|
||||
for _, result := range results {
|
||||
if result.Success {
|
||||
hasSuccess = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 构建组合结果
|
||||
combinedResult := &processors.CombinedResult{
|
||||
Responses: results,
|
||||
}
|
||||
|
||||
// 如果所有子产品都失败,返回错误
|
||||
if !hasSuccess && len(results) > 0 {
|
||||
// 构建错误信息,包含所有失败的原因
|
||||
errorMessages := make([]string, 0, len(results))
|
||||
for _, result := range results {
|
||||
if result.Error != "" {
|
||||
errorMessages = append(errorMessages, fmt.Sprintf("%s: %s", result.ApiCode, result.Error))
|
||||
}
|
||||
}
|
||||
errorMsg := fmt.Sprintf("组合包所有子产品调用失败: %s", strings.Join(errorMessages, "; "))
|
||||
return nil, fmt.Errorf(errorMsg)
|
||||
}
|
||||
|
||||
// 至少有一个成功,返回成功结果
|
||||
return combinedResult, nil
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
package comb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"hyapi-server/internal/domains/api/dto"
|
||||
"hyapi-server/internal/domains/api/services/processors"
|
||||
"hyapi-server/internal/shared/logger"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// ProcessCOMBHZY2Request 处理 COMBHZY2 组合包请求
|
||||
func ProcessCOMBHZY2Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||
log := logger.GetGlobalLogger()
|
||||
|
||||
var req dto.COMBHZY2Req
|
||||
if err := json.Unmarshal(params, &req); err != nil {
|
||||
log.Error("COMBHZY2请求参数反序列化失败",
|
||||
zap.Error(err),
|
||||
zap.String("params", string(params)),
|
||||
zap.String("api_code", "COMBHZY2"),
|
||||
)
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
if err := deps.Validator.ValidateStruct(req); err != nil {
|
||||
log.Error("COMBHZY2请求参数验证失败",
|
||||
zap.Error(err),
|
||||
zap.String("api_code", "COMBHZY2"),
|
||||
)
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
|
||||
combinedResult, err := deps.CombService.ProcessCombRequest(ctx, params, deps, "COMBHZY2")
|
||||
if err != nil {
|
||||
log.Error("COMBHZY2组合包服务调用失败",
|
||||
zap.Error(err),
|
||||
zap.String("api_code", "COMBHZY2"),
|
||||
)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if combinedResult == nil {
|
||||
log.Error("COMBHZY2组合包响应为空",
|
||||
zap.String("api_code", "COMBHZY2"),
|
||||
)
|
||||
return nil, errors.New("组合包响应为空")
|
||||
}
|
||||
|
||||
log.Info("COMBHZY2组合包服务调用成功",
|
||||
zap.Int("子产品数量", len(combinedResult.Responses)),
|
||||
zap.String("api_code", "COMBHZY2"),
|
||||
)
|
||||
|
||||
sourceCtx, err := buildSourceContextFromCombined(ctx, combinedResult)
|
||||
if err != nil {
|
||||
log.Error("COMBHZY2构建源数据上下文失败",
|
||||
zap.Error(err),
|
||||
zap.String("api_code", "COMBHZY2"),
|
||||
)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
report := buildTargetReport(ctx, sourceCtx)
|
||||
|
||||
reportBytes, err := json.Marshal(report)
|
||||
if err != nil {
|
||||
log.Error("COMBHZY2报告序列化失败",
|
||||
zap.Error(err),
|
||||
zap.String("api_code", "COMBHZY2"),
|
||||
)
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
return reportBytes, nil
|
||||
}
|
||||
|
||||
func buildSourceContextFromCombined(ctx context.Context, result *processors.CombinedResult) (*sourceContext, error) {
|
||||
log := logger.GetGlobalLogger()
|
||||
|
||||
if result == nil {
|
||||
log.Error("组合包响应为空", zap.String("api_code", "COMBHZY2"))
|
||||
return nil, errors.New("组合包响应为空")
|
||||
}
|
||||
|
||||
src := sourceFile{Responses: make([]sourceResponse, 0, len(result.Responses))}
|
||||
successCount := 0
|
||||
failedCount := 0
|
||||
|
||||
for _, resp := range result.Responses {
|
||||
if !resp.Success {
|
||||
log.Warn("子产品调用失败,跳过",
|
||||
zap.String("api_code", resp.ApiCode),
|
||||
zap.String("error", resp.Error),
|
||||
zap.String("parent_api_code", "COMBHZY2"),
|
||||
)
|
||||
failedCount++
|
||||
continue
|
||||
}
|
||||
|
||||
if resp.Data == nil {
|
||||
log.Warn("子产品数据为空,跳过",
|
||||
zap.String("api_code", resp.ApiCode),
|
||||
zap.String("parent_api_code", "COMBHZY2"),
|
||||
)
|
||||
failedCount++
|
||||
continue
|
||||
}
|
||||
|
||||
raw, err := json.Marshal(resp.Data)
|
||||
if err != nil {
|
||||
log.Error("序列化子产品数据失败",
|
||||
zap.Error(err),
|
||||
zap.String("api_code", resp.ApiCode),
|
||||
zap.String("parent_api_code", "COMBHZY2"),
|
||||
)
|
||||
failedCount++
|
||||
continue
|
||||
}
|
||||
|
||||
src.Responses = append(src.Responses, sourceResponse{
|
||||
ApiCode: resp.ApiCode,
|
||||
Data: raw,
|
||||
Success: resp.Success,
|
||||
})
|
||||
successCount++
|
||||
}
|
||||
|
||||
log.Info("组合包子产品处理完成",
|
||||
zap.Int("成功数量", successCount),
|
||||
zap.Int("失败数量", failedCount),
|
||||
zap.Int("总数量", len(result.Responses)),
|
||||
zap.String("api_code", "COMBHZY2"),
|
||||
)
|
||||
|
||||
if len(src.Responses) == 0 {
|
||||
log.Error("组合包子产品全部调用失败",
|
||||
zap.Int("总数量", len(result.Responses)),
|
||||
zap.String("api_code", "COMBHZY2"),
|
||||
)
|
||||
return nil, errors.New("组合包子产品全部调用失败")
|
||||
}
|
||||
|
||||
return buildSourceContext(ctx, src)
|
||||
}
|
||||
2053
internal/domains/api/services/processors/comb/combhzy2_transform.go
Normal file
2053
internal/domains/api/services/processors/comb/combhzy2_transform.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,91 @@
|
||||
package comb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"hyapi-server/internal/domains/api/services/processors"
|
||||
"hyapi-server/internal/shared/logger"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// ProcessCOMBWD01Request 处理 COMBWD01 组合包请求
|
||||
// 将返回结构从数组改为以 api_code 为 key 的对象结构
|
||||
func ProcessCOMBWD01Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||
log := logger.GetGlobalLogger()
|
||||
|
||||
// 调用组合包服务处理请求
|
||||
combinedResult, err := deps.CombService.ProcessCombRequest(ctx, params, deps, "COMBWD01")
|
||||
if err != nil {
|
||||
log.Error("COMBWD01组合包服务调用失败",
|
||||
zap.Error(err),
|
||||
zap.String("api_code", "COMBWD01"),
|
||||
)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if combinedResult == nil {
|
||||
log.Error("COMBWD01组合包响应为空",
|
||||
zap.String("api_code", "COMBWD01"),
|
||||
)
|
||||
return nil, errors.New("组合包响应为空")
|
||||
}
|
||||
|
||||
log.Info("COMBWD01组合包服务调用成功",
|
||||
zap.Int("子产品数量", len(combinedResult.Responses)),
|
||||
zap.String("api_code", "COMBWD01"),
|
||||
)
|
||||
|
||||
// 将数组结构转换为对象结构
|
||||
responsesMap := make(map[string]*ResponseItem)
|
||||
for _, resp := range combinedResult.Responses {
|
||||
item := &ResponseItem{
|
||||
ApiCode: resp.ApiCode,
|
||||
Success: resp.Success,
|
||||
}
|
||||
|
||||
// 根据成功/失败状态设置 data 和 error 字段
|
||||
if resp.Success {
|
||||
// 成功时:data 有值(可能为 nil),error 为 null
|
||||
item.Data = resp.Data
|
||||
item.Error = nil
|
||||
} else {
|
||||
// 失败时:data 为 null,error 有值
|
||||
item.Data = nil
|
||||
if resp.Error != "" {
|
||||
item.Error = resp.Error
|
||||
} else {
|
||||
item.Error = "未知错误"
|
||||
}
|
||||
}
|
||||
|
||||
responsesMap[resp.ApiCode] = item
|
||||
}
|
||||
|
||||
// 构建新的响应结构
|
||||
result := map[string]interface{}{
|
||||
"responses": responsesMap,
|
||||
}
|
||||
|
||||
// 序列化并返回
|
||||
resultBytes, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
log.Error("COMBWD01响应序列化失败",
|
||||
zap.Error(err),
|
||||
zap.String("api_code", "COMBWD01"),
|
||||
)
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
return resultBytes, nil
|
||||
}
|
||||
|
||||
// ResponseItem 响应项结构
|
||||
type ResponseItem struct {
|
||||
ApiCode string `json:"api_code"`
|
||||
Success bool `json:"success"`
|
||||
Data interface{} `json:"data"`
|
||||
Error interface{} `json:"error"`
|
||||
}
|
||||
Reference in New Issue
Block a user