Files
tyapi-server/internal/domains/finance/services/invoice_domain_service.go
2025-08-02 02:54:21 +08:00

153 lines
4.9 KiB
Go

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
}