This commit is contained in:
2025-07-31 15:41:00 +08:00
parent f3a3bc84c7
commit 934dce2776
36 changed files with 1614 additions and 264 deletions

View File

@@ -0,0 +1,116 @@
package services
import (
"context"
"errors"
"fmt"
"tyapi-server/internal/domains/product/entities"
"tyapi-server/internal/domains/product/repositories"
)
// ProductDocumentationService 产品文档服务
type ProductDocumentationService struct {
docRepo repositories.ProductDocumentationRepository
productRepo repositories.ProductRepository
}
// NewProductDocumentationService 创建文档服务实例
func NewProductDocumentationService(
docRepo repositories.ProductDocumentationRepository,
productRepo repositories.ProductRepository,
) *ProductDocumentationService {
return &ProductDocumentationService{
docRepo: docRepo,
productRepo: productRepo,
}
}
// CreateDocumentation 创建文档
func (s *ProductDocumentationService) CreateDocumentation(ctx context.Context, productID string, doc *entities.ProductDocumentation) error {
// 验证产品是否存在
product, err := s.productRepo.GetByID(ctx, productID)
if err != nil {
return fmt.Errorf("产品不存在: %w", err)
}
if !product.IsValid() {
return errors.New("产品已禁用或删除")
}
// 检查是否已存在文档
existingDoc, err := s.docRepo.FindByProductID(ctx, productID)
if err == nil && existingDoc != nil {
return errors.New("该产品已存在文档")
}
// 设置产品ID
doc.ProductID = productID
// 验证文档完整性
if err := doc.Validate(); err != nil {
return fmt.Errorf("文档验证失败: %w", err)
}
// 创建文档
return s.docRepo.Create(ctx, doc)
}
// UpdateDocumentation 更新文档
func (s *ProductDocumentationService) UpdateDocumentation(ctx context.Context, id string, requestURL, requestMethod, basicInfo, requestParams, responseFields, responseExample, errorCodes string) error {
// 查找现有文档
doc, err := s.docRepo.FindByID(ctx, id)
if err != nil {
return fmt.Errorf("文档不存在: %w", err)
}
// 使用实体的更新方法
err = doc.UpdateDocumentation(requestURL, requestMethod, basicInfo, requestParams, responseFields, responseExample, errorCodes)
if err != nil {
return fmt.Errorf("文档更新失败: %w", err)
}
// 保存更新
return s.docRepo.Update(ctx, doc)
}
// GetDocumentation 获取文档
func (s *ProductDocumentationService) GetDocumentation(ctx context.Context, id string) (*entities.ProductDocumentation, error) {
return s.docRepo.FindByID(ctx, id)
}
// GetDocumentationByProductID 通过产品ID获取文档
func (s *ProductDocumentationService) GetDocumentationByProductID(ctx context.Context, productID string) (*entities.ProductDocumentation, error) {
return s.docRepo.FindByProductID(ctx, productID)
}
// DeleteDocumentation 删除文档
func (s *ProductDocumentationService) DeleteDocumentation(ctx context.Context, id string) error {
_, err := s.docRepo.FindByID(ctx, id)
if err != nil {
return fmt.Errorf("文档不存在: %w", err)
}
return s.docRepo.Delete(ctx, id)
}
// GetDocumentationWithProduct 获取文档及其关联的产品信息
func (s *ProductDocumentationService) GetDocumentationWithProduct(ctx context.Context, id string) (*entities.ProductDocumentation, error) {
doc, err := s.docRepo.FindByID(ctx, id)
if err != nil {
return nil, err
}
// 加载产品信息
product, err := s.productRepo.GetByID(ctx, doc.ProductID)
if err != nil {
return nil, fmt.Errorf("获取产品信息失败: %w", err)
}
doc.Product = &product
return doc, nil
}
// GetDocumentationsByProductIDs 批量获取文档
func (s *ProductDocumentationService) GetDocumentationsByProductIDs(ctx context.Context, productIDs []string) ([]*entities.ProductDocumentation, error) {
return s.docRepo.FindByProductIDs(ctx, productIDs)
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"time"
"gorm.io/gorm"
@@ -190,4 +191,58 @@ func (s *ProductSubscriptionService) SaveSubscription(ctx context.Context, subsc
}
return nil
}
}
}
// IncrementSubscriptionAPIUsage 增加订阅API使用次数使用乐观锁带重试机制
func (s *ProductSubscriptionService) IncrementSubscriptionAPIUsage(ctx context.Context, subscriptionID string, increment int64) error {
const maxRetries = 3
const baseDelay = 10 * time.Millisecond
for attempt := 0; attempt < maxRetries; attempt++ {
// 使用乐观锁直接更新数据库
err := s.subscriptionRepo.IncrementAPIUsageWithOptimisticLock(ctx, subscriptionID, increment)
if err == nil {
// 更新成功
if attempt > 0 {
s.logger.Info("订阅API使用次数更新成功重试后",
zap.String("subscription_id", subscriptionID),
zap.Int64("increment", increment),
zap.Int("retry_count", attempt))
} else {
s.logger.Info("订阅API使用次数更新成功",
zap.String("subscription_id", subscriptionID),
zap.Int64("increment", increment))
}
return nil
}
// 检查是否是版本冲突错误
if errors.Is(err, gorm.ErrRecordNotFound) {
// 版本冲突,等待后重试
if attempt < maxRetries-1 {
delay := time.Duration(attempt+1) * baseDelay
s.logger.Debug("订阅版本冲突,准备重试",
zap.String("subscription_id", subscriptionID),
zap.Int("attempt", attempt+1),
zap.Duration("delay", delay))
time.Sleep(delay)
continue
}
// 最后一次重试失败
s.logger.Error("订阅不存在或版本冲突,重试次数已用完",
zap.String("subscription_id", subscriptionID),
zap.Int("max_retries", maxRetries),
zap.Error(err))
return fmt.Errorf("订阅不存在或已被其他操作修改(重试%d次后失败: %w", maxRetries, err)
}
// 其他错误直接返回,不重试
s.logger.Error("更新订阅API使用次数失败",
zap.String("subscription_id", subscriptionID),
zap.Int64("increment", increment),
zap.Error(err))
return fmt.Errorf("更新订阅API使用次数失败: %w", err)
}
return fmt.Errorf("更新失败,已重试%d次", maxRetries)
}