package services import ( "context" "errors" "fmt" "tyapi-server/internal/domains/finance/entities" "tyapi-server/internal/domains/finance/value_objects" "github.com/shopspring/decimal" ) // InvoiceDomainService 发票领域服务接口 // 职责:处理发票领域的业务规则和计算逻辑,不涉及外部依赖 type InvoiceDomainService interface { // 验证发票信息完整性 ValidateInvoiceInfo(ctx context.Context, info *value_objects.InvoiceInfo, invoiceType value_objects.InvoiceType) error // 验证开票金额是否合法(基于业务规则) ValidateInvoiceAmount(ctx context.Context, amount decimal.Decimal, availableAmount decimal.Decimal) error // 计算可开票金额(纯计算逻辑) CalculateAvailableAmount(totalRecharged decimal.Decimal, totalGifted decimal.Decimal, totalInvoiced decimal.Decimal) decimal.Decimal // 验证发票申请状态转换 ValidateStatusTransition(currentStatus entities.ApplicationStatus, targetStatus entities.ApplicationStatus) error // 验证发票申请业务规则 ValidateInvoiceApplication(ctx context.Context, application *entities.InvoiceApplication) error } // InvoiceDomainServiceImpl 发票领域服务实现 type InvoiceDomainServiceImpl struct { // 领域服务不依赖仓储,只处理业务规则 } // NewInvoiceDomainService 创建发票领域服务 func NewInvoiceDomainService() InvoiceDomainService { return &InvoiceDomainServiceImpl{} } // ValidateInvoiceInfo 验证发票信息完整性 func (s *InvoiceDomainServiceImpl) ValidateInvoiceInfo(ctx context.Context, info *value_objects.InvoiceInfo, invoiceType value_objects.InvoiceType) error { if info == nil { return errors.New("发票信息不能为空") } switch invoiceType { case value_objects.InvoiceTypeGeneral: return info.ValidateForGeneralInvoice() case value_objects.InvoiceTypeSpecial: return info.ValidateForSpecialInvoice() default: return errors.New("无效的发票类型") } } // ValidateInvoiceAmount 验证开票金额是否合法(基于业务规则) func (s *InvoiceDomainServiceImpl) ValidateInvoiceAmount(ctx context.Context, amount decimal.Decimal, availableAmount decimal.Decimal) error { if amount.LessThanOrEqual(decimal.Zero) { return errors.New("开票金额必须大于0") } if amount.GreaterThan(availableAmount) { return fmt.Errorf("开票金额不能超过可开票金额,可开票金额:%s", availableAmount.String()) } // 最小开票金额限制 minAmount := decimal.NewFromFloat(0.01) // 最小0.01元 if amount.LessThan(minAmount) { return fmt.Errorf("开票金额不能少于%s元", minAmount.String()) } return nil } // CalculateAvailableAmount 计算可开票金额(纯计算逻辑) func (s *InvoiceDomainServiceImpl) CalculateAvailableAmount(totalRecharged decimal.Decimal, totalGifted decimal.Decimal, totalInvoiced decimal.Decimal) decimal.Decimal { // 可开票金额 = 充值金额 - 已开票金额(不包含赠送金额) availableAmount := totalRecharged.Sub(totalInvoiced) if availableAmount.LessThan(decimal.Zero) { availableAmount = decimal.Zero } return availableAmount } // ValidateStatusTransition 验证发票申请状态转换 func (s *InvoiceDomainServiceImpl) ValidateStatusTransition(currentStatus entities.ApplicationStatus, targetStatus entities.ApplicationStatus) error { // 定义允许的状态转换 allowedTransitions := map[entities.ApplicationStatus][]entities.ApplicationStatus{ entities.ApplicationStatusPending: { entities.ApplicationStatusCompleted, entities.ApplicationStatusRejected, }, entities.ApplicationStatusCompleted: { // 已完成状态不能再转换 }, entities.ApplicationStatusRejected: { // 已拒绝状态不能再转换 }, } allowedTargets, exists := allowedTransitions[currentStatus] if !exists { return fmt.Errorf("无效的当前状态:%s", currentStatus) } for _, allowed := range allowedTargets { if allowed == targetStatus { return nil } } return fmt.Errorf("不允许从状态 %s 转换到状态 %s", currentStatus, targetStatus) } // ValidateInvoiceApplication 验证发票申请业务规则 func (s *InvoiceDomainServiceImpl) ValidateInvoiceApplication(ctx context.Context, application *entities.InvoiceApplication) error { if application == nil { return errors.New("发票申请不能为空") } // 验证基础字段 if application.UserID == "" { return errors.New("用户ID不能为空") } if application.Amount.LessThanOrEqual(decimal.Zero) { return errors.New("申请金额必须大于0") } // 验证发票类型 if !application.InvoiceType.IsValid() { return errors.New("无效的发票类型") } // 验证开票信息 if application.CompanyName == "" { return errors.New("公司名称不能为空") } if application.TaxpayerID == "" { return errors.New("纳税人识别号不能为空") } if application.ReceivingEmail == "" { return errors.New("发票接收邮箱不能为空") } return nil }