v1.0.0
This commit is contained in:
277
internal/domains/finance/services/invoice_aggregate_service.go
Normal file
277
internal/domains/finance/services/invoice_aggregate_service.go
Normal file
@@ -0,0 +1,277 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"time"
|
||||
|
||||
"tyapi-server/internal/domains/finance/entities"
|
||||
"tyapi-server/internal/domains/finance/events"
|
||||
"tyapi-server/internal/domains/finance/repositories"
|
||||
"tyapi-server/internal/domains/finance/value_objects"
|
||||
|
||||
"tyapi-server/internal/infrastructure/external/storage"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// ApplyInvoiceRequest 申请开票请求
|
||||
type ApplyInvoiceRequest struct {
|
||||
InvoiceType value_objects.InvoiceType `json:"invoice_type" binding:"required"`
|
||||
Amount string `json:"amount" binding:"required"`
|
||||
InvoiceInfo *value_objects.InvoiceInfo `json:"invoice_info" binding:"required"`
|
||||
}
|
||||
|
||||
// ApproveInvoiceRequest 通过发票申请请求
|
||||
type ApproveInvoiceRequest struct {
|
||||
AdminNotes string `json:"admin_notes"`
|
||||
}
|
||||
|
||||
// RejectInvoiceRequest 拒绝发票申请请求
|
||||
type RejectInvoiceRequest struct {
|
||||
Reason string `json:"reason" binding:"required"`
|
||||
}
|
||||
|
||||
// InvoiceAggregateService 发票聚合服务接口
|
||||
// 职责:协调发票申请聚合根的生命周期,调用领域服务进行业务规则验证,发布领域事件
|
||||
type InvoiceAggregateService interface {
|
||||
// 申请开票
|
||||
ApplyInvoice(ctx context.Context, userID string, req ApplyInvoiceRequest) (*entities.InvoiceApplication, error)
|
||||
|
||||
// 通过发票申请(上传发票)
|
||||
ApproveInvoiceApplication(ctx context.Context, applicationID string, file multipart.File, req ApproveInvoiceRequest) error
|
||||
|
||||
// 拒绝发票申请
|
||||
RejectInvoiceApplication(ctx context.Context, applicationID string, req RejectInvoiceRequest) error
|
||||
}
|
||||
|
||||
// InvoiceAggregateServiceImpl 发票聚合服务实现
|
||||
type InvoiceAggregateServiceImpl struct {
|
||||
applicationRepo repositories.InvoiceApplicationRepository
|
||||
userInvoiceInfoRepo repositories.UserInvoiceInfoRepository
|
||||
domainService InvoiceDomainService
|
||||
qiniuStorageService *storage.QiNiuStorageService
|
||||
logger *zap.Logger
|
||||
eventPublisher EventPublisher
|
||||
}
|
||||
|
||||
// EventPublisher 事件发布器接口
|
||||
type EventPublisher interface {
|
||||
PublishInvoiceApplicationCreated(ctx context.Context, event *events.InvoiceApplicationCreatedEvent) error
|
||||
PublishInvoiceApplicationApproved(ctx context.Context, event *events.InvoiceApplicationApprovedEvent) error
|
||||
PublishInvoiceApplicationRejected(ctx context.Context, event *events.InvoiceApplicationRejectedEvent) error
|
||||
PublishInvoiceFileUploaded(ctx context.Context, event *events.InvoiceFileUploadedEvent) error
|
||||
}
|
||||
|
||||
// NewInvoiceAggregateService 创建发票聚合服务
|
||||
func NewInvoiceAggregateService(
|
||||
applicationRepo repositories.InvoiceApplicationRepository,
|
||||
userInvoiceInfoRepo repositories.UserInvoiceInfoRepository,
|
||||
domainService InvoiceDomainService,
|
||||
qiniuStorageService *storage.QiNiuStorageService,
|
||||
logger *zap.Logger,
|
||||
eventPublisher EventPublisher,
|
||||
) InvoiceAggregateService {
|
||||
return &InvoiceAggregateServiceImpl{
|
||||
applicationRepo: applicationRepo,
|
||||
userInvoiceInfoRepo: userInvoiceInfoRepo,
|
||||
domainService: domainService,
|
||||
qiniuStorageService: qiniuStorageService,
|
||||
logger: logger,
|
||||
eventPublisher: eventPublisher,
|
||||
}
|
||||
}
|
||||
|
||||
// ApplyInvoice 申请开票
|
||||
func (s *InvoiceAggregateServiceImpl) ApplyInvoice(ctx context.Context, userID string, req ApplyInvoiceRequest) (*entities.InvoiceApplication, error) {
|
||||
// 1. 解析金额
|
||||
amount, err := decimal.NewFromString(req.Amount)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("无效的金额格式: %w", err)
|
||||
}
|
||||
|
||||
// 2. 验证发票信息
|
||||
if err := s.domainService.ValidateInvoiceInfo(ctx, req.InvoiceInfo, req.InvoiceType); err != nil {
|
||||
return nil, fmt.Errorf("发票信息验证失败: %w", err)
|
||||
}
|
||||
|
||||
// 3. 获取用户开票信息
|
||||
userInvoiceInfo, err := s.userInvoiceInfoRepo.FindByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取用户开票信息失败: %w", err)
|
||||
}
|
||||
if userInvoiceInfo == nil {
|
||||
return nil, fmt.Errorf("用户开票信息不存在")
|
||||
}
|
||||
|
||||
// 4. 创建发票申请聚合根
|
||||
application := entities.NewInvoiceApplication(userID, req.InvoiceType, amount, userInvoiceInfo.ID)
|
||||
|
||||
// 5. 设置开票信息快照
|
||||
application.SetInvoiceInfoSnapshot(req.InvoiceInfo)
|
||||
|
||||
// 6. 验证聚合根业务规则
|
||||
if err := s.domainService.ValidateInvoiceApplication(ctx, application); err != nil {
|
||||
return nil, fmt.Errorf("发票申请业务规则验证失败: %w", err)
|
||||
}
|
||||
|
||||
// 7. 保存聚合根
|
||||
if err := s.applicationRepo.Create(ctx, application); err != nil {
|
||||
return nil, fmt.Errorf("保存发票申请失败: %w", err)
|
||||
}
|
||||
|
||||
// 8. 发布领域事件
|
||||
event := events.NewInvoiceApplicationCreatedEvent(
|
||||
application.ID,
|
||||
application.UserID,
|
||||
application.InvoiceType,
|
||||
application.Amount,
|
||||
application.CompanyName,
|
||||
application.ReceivingEmail,
|
||||
)
|
||||
|
||||
if err := s.eventPublisher.PublishInvoiceApplicationCreated(ctx, event); err != nil {
|
||||
// 记录错误但不影响主流程
|
||||
fmt.Printf("发布发票申请创建事件失败: %v\n", err)
|
||||
}
|
||||
|
||||
return application, nil
|
||||
}
|
||||
|
||||
// ApproveInvoiceApplication 通过发票申请(上传发票)
|
||||
func (s *InvoiceAggregateServiceImpl) ApproveInvoiceApplication(ctx context.Context, applicationID string, file multipart.File, req ApproveInvoiceRequest) error {
|
||||
// 1. 获取发票申请
|
||||
application, err := s.applicationRepo.FindByID(ctx, applicationID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取发票申请失败: %w", err)
|
||||
}
|
||||
if application == nil {
|
||||
return fmt.Errorf("发票申请不存在")
|
||||
}
|
||||
|
||||
// 2. 验证状态转换
|
||||
if err := s.domainService.ValidateStatusTransition(application.Status, entities.ApplicationStatusCompleted); err != nil {
|
||||
return fmt.Errorf("状态转换验证失败: %w", err)
|
||||
}
|
||||
|
||||
// 3. 处理文件上传
|
||||
// 读取文件内容
|
||||
fileBytes, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
s.logger.Error("读取上传文件失败", zap.Error(err))
|
||||
return fmt.Errorf("读取上传文件失败: %w", err)
|
||||
}
|
||||
|
||||
// 生成文件名(使用时间戳确保唯一性)
|
||||
fileName := fmt.Sprintf("invoice_%s_%d.pdf", applicationID, time.Now().Unix())
|
||||
|
||||
// 上传文件到七牛云
|
||||
uploadResult, err := s.qiniuStorageService.UploadFile(ctx, fileBytes, fileName)
|
||||
if err != nil {
|
||||
s.logger.Error("上传发票文件到七牛云失败", zap.String("file_name", fileName), zap.Error(err))
|
||||
return fmt.Errorf("上传发票文件到七牛云失败: %w", err)
|
||||
}
|
||||
|
||||
// 从上传结果获取文件信息
|
||||
fileID := uploadResult.Key
|
||||
fileURL := uploadResult.URL
|
||||
fileSize := uploadResult.Size
|
||||
|
||||
// 4. 更新聚合根状态
|
||||
application.MarkCompleted("admin_user_id")
|
||||
application.SetFileInfo(fileID, fileName, fileURL, fileSize)
|
||||
application.AdminNotes = &req.AdminNotes
|
||||
|
||||
// 5. 保存聚合根
|
||||
if err := s.applicationRepo.Update(ctx, application); err != nil {
|
||||
return fmt.Errorf("更新发票申请失败: %w", err)
|
||||
}
|
||||
|
||||
// 6. 发布领域事件
|
||||
approvedEvent := events.NewInvoiceApplicationApprovedEvent(
|
||||
application.ID,
|
||||
application.UserID,
|
||||
application.Amount,
|
||||
application.ReceivingEmail,
|
||||
)
|
||||
|
||||
if err := s.eventPublisher.PublishInvoiceApplicationApproved(ctx, approvedEvent); err != nil {
|
||||
s.logger.Error("发布发票申请通过事件失败",
|
||||
zap.String("application_id", applicationID),
|
||||
zap.Error(err),
|
||||
)
|
||||
// 事件发布失败不影响主流程,只记录日志
|
||||
} else {
|
||||
s.logger.Info("发票申请通过事件发布成功",
|
||||
zap.String("application_id", applicationID),
|
||||
)
|
||||
}
|
||||
|
||||
fileUploadedEvent := events.NewInvoiceFileUploadedEvent(
|
||||
application.ID,
|
||||
application.UserID,
|
||||
fileID,
|
||||
fileName,
|
||||
fileURL,
|
||||
application.ReceivingEmail,
|
||||
application.CompanyName,
|
||||
application.Amount,
|
||||
application.InvoiceType,
|
||||
)
|
||||
|
||||
if err := s.eventPublisher.PublishInvoiceFileUploaded(ctx, fileUploadedEvent); err != nil {
|
||||
s.logger.Error("发布发票文件上传事件失败",
|
||||
zap.String("application_id", applicationID),
|
||||
zap.Error(err),
|
||||
)
|
||||
// 事件发布失败不影响主流程,只记录日志
|
||||
} else {
|
||||
s.logger.Info("发票文件上传事件发布成功",
|
||||
zap.String("application_id", applicationID),
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RejectInvoiceApplication 拒绝发票申请
|
||||
func (s *InvoiceAggregateServiceImpl) RejectInvoiceApplication(ctx context.Context, applicationID string, req RejectInvoiceRequest) error {
|
||||
// 1. 获取发票申请
|
||||
application, err := s.applicationRepo.FindByID(ctx, applicationID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取发票申请失败: %w", err)
|
||||
}
|
||||
if application == nil {
|
||||
return fmt.Errorf("发票申请不存在")
|
||||
}
|
||||
|
||||
// 2. 验证状态转换
|
||||
if err := s.domainService.ValidateStatusTransition(application.Status, entities.ApplicationStatusRejected); err != nil {
|
||||
return fmt.Errorf("状态转换验证失败: %w", err)
|
||||
}
|
||||
|
||||
// 3. 更新聚合根状态
|
||||
application.MarkRejected(req.Reason, "admin_user_id")
|
||||
|
||||
// 4. 保存聚合根
|
||||
if err := s.applicationRepo.Update(ctx, application); err != nil {
|
||||
return fmt.Errorf("更新发票申请失败: %w", err)
|
||||
}
|
||||
|
||||
// 5. 发布领域事件
|
||||
event := events.NewInvoiceApplicationRejectedEvent(
|
||||
application.ID,
|
||||
application.UserID,
|
||||
req.Reason,
|
||||
application.ReceivingEmail,
|
||||
)
|
||||
|
||||
if err := s.eventPublisher.PublishInvoiceApplicationRejected(ctx, event); err != nil {
|
||||
fmt.Printf("发布发票申请拒绝事件失败: %v\n", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
152
internal/domains/finance/services/invoice_domain_service.go
Normal file
152
internal/domains/finance/services/invoice_domain_service.go
Normal file
@@ -0,0 +1,152 @@
|
||||
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
|
||||
}
|
||||
@@ -379,6 +379,15 @@ func (s *RechargeRecordServiceImpl) GetByTransferOrderID(ctx context.Context, tr
|
||||
|
||||
// GetAll 获取所有充值记录(管理员功能)
|
||||
func (s *RechargeRecordServiceImpl) GetAll(ctx context.Context, filters map[string]interface{}, options interfaces.ListOptions) ([]entities.RechargeRecord, error) {
|
||||
// 将filters添加到options中
|
||||
if filters != nil {
|
||||
if options.Filters == nil {
|
||||
options.Filters = make(map[string]interface{})
|
||||
}
|
||||
for key, value := range filters {
|
||||
options.Filters[key] = value
|
||||
}
|
||||
}
|
||||
return s.rechargeRecordRepo.List(ctx, options)
|
||||
}
|
||||
|
||||
|
||||
250
internal/domains/finance/services/user_invoice_info_service.go
Normal file
250
internal/domains/finance/services/user_invoice_info_service.go
Normal file
@@ -0,0 +1,250 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"tyapi-server/internal/domains/finance/entities"
|
||||
"tyapi-server/internal/domains/finance/repositories"
|
||||
"tyapi-server/internal/domains/finance/value_objects"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// UserInvoiceInfoService 用户开票信息服务接口
|
||||
type UserInvoiceInfoService interface {
|
||||
// GetUserInvoiceInfo 获取用户开票信息
|
||||
GetUserInvoiceInfo(ctx context.Context, userID string) (*entities.UserInvoiceInfo, error)
|
||||
|
||||
// GetUserInvoiceInfoWithEnterpriseInfo 获取用户开票信息(包含企业认证信息)
|
||||
GetUserInvoiceInfoWithEnterpriseInfo(ctx context.Context, userID string, companyName, taxpayerID string) (*entities.UserInvoiceInfo, error)
|
||||
|
||||
// CreateOrUpdateUserInvoiceInfo 创建或更新用户开票信息
|
||||
CreateOrUpdateUserInvoiceInfo(ctx context.Context, userID string, invoiceInfo *value_objects.InvoiceInfo) (*entities.UserInvoiceInfo, error)
|
||||
|
||||
// CreateOrUpdateUserInvoiceInfoWithEnterpriseInfo 创建或更新用户开票信息(包含企业认证信息)
|
||||
CreateOrUpdateUserInvoiceInfoWithEnterpriseInfo(ctx context.Context, userID string, invoiceInfo *value_objects.InvoiceInfo, companyName, taxpayerID string) (*entities.UserInvoiceInfo, error)
|
||||
|
||||
// ValidateInvoiceInfo 验证开票信息
|
||||
ValidateInvoiceInfo(ctx context.Context, invoiceInfo *value_objects.InvoiceInfo, invoiceType value_objects.InvoiceType) error
|
||||
|
||||
// DeleteUserInvoiceInfo 删除用户开票信息
|
||||
DeleteUserInvoiceInfo(ctx context.Context, userID string) error
|
||||
}
|
||||
|
||||
// UserInvoiceInfoServiceImpl 用户开票信息服务实现
|
||||
type UserInvoiceInfoServiceImpl struct {
|
||||
userInvoiceInfoRepo repositories.UserInvoiceInfoRepository
|
||||
}
|
||||
|
||||
// NewUserInvoiceInfoService 创建用户开票信息服务
|
||||
func NewUserInvoiceInfoService(userInvoiceInfoRepo repositories.UserInvoiceInfoRepository) UserInvoiceInfoService {
|
||||
return &UserInvoiceInfoServiceImpl{
|
||||
userInvoiceInfoRepo: userInvoiceInfoRepo,
|
||||
}
|
||||
}
|
||||
|
||||
// GetUserInvoiceInfo 获取用户开票信息
|
||||
func (s *UserInvoiceInfoServiceImpl) GetUserInvoiceInfo(ctx context.Context, userID string) (*entities.UserInvoiceInfo, error) {
|
||||
info, err := s.userInvoiceInfoRepo.FindByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取用户开票信息失败: %w", err)
|
||||
}
|
||||
|
||||
// 如果没有找到开票信息记录,创建新的实体
|
||||
if info == nil {
|
||||
info = &entities.UserInvoiceInfo{
|
||||
ID: uuid.New().String(),
|
||||
UserID: userID,
|
||||
CompanyName: "",
|
||||
TaxpayerID: "",
|
||||
BankName: "",
|
||||
BankAccount: "",
|
||||
CompanyAddress: "",
|
||||
CompanyPhone: "",
|
||||
ReceivingEmail: "",
|
||||
}
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// GetUserInvoiceInfoWithEnterpriseInfo 获取用户开票信息(包含企业认证信息)
|
||||
func (s *UserInvoiceInfoServiceImpl) GetUserInvoiceInfoWithEnterpriseInfo(ctx context.Context, userID string, companyName, taxpayerID string) (*entities.UserInvoiceInfo, error) {
|
||||
info, err := s.userInvoiceInfoRepo.FindByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取用户开票信息失败: %w", err)
|
||||
}
|
||||
|
||||
// 如果没有找到开票信息记录,创建新的实体
|
||||
if info == nil {
|
||||
info = &entities.UserInvoiceInfo{
|
||||
ID: uuid.New().String(),
|
||||
UserID: userID,
|
||||
CompanyName: companyName, // 使用企业认证信息填充
|
||||
TaxpayerID: taxpayerID, // 使用企业认证信息填充
|
||||
BankName: "",
|
||||
BankAccount: "",
|
||||
CompanyAddress: "",
|
||||
CompanyPhone: "",
|
||||
ReceivingEmail: "",
|
||||
}
|
||||
} else {
|
||||
// 如果已有记录,使用传入的企业认证信息覆盖公司名称和纳税人识别号
|
||||
if companyName != "" {
|
||||
info.CompanyName = companyName
|
||||
}
|
||||
if taxpayerID != "" {
|
||||
info.TaxpayerID = taxpayerID
|
||||
}
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// CreateOrUpdateUserInvoiceInfo 创建或更新用户开票信息
|
||||
func (s *UserInvoiceInfoServiceImpl) CreateOrUpdateUserInvoiceInfo(ctx context.Context, userID string, invoiceInfo *value_objects.InvoiceInfo) (*entities.UserInvoiceInfo, error) {
|
||||
// 验证开票信息
|
||||
if err := s.ValidateInvoiceInfo(ctx, invoiceInfo, value_objects.InvoiceTypeGeneral); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 检查是否已存在
|
||||
exists, err := s.userInvoiceInfoRepo.Exists(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("检查用户开票信息失败: %w", err)
|
||||
}
|
||||
|
||||
var userInvoiceInfo *entities.UserInvoiceInfo
|
||||
|
||||
if exists {
|
||||
// 更新现有记录
|
||||
userInvoiceInfo, err = s.userInvoiceInfoRepo.FindByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取用户开票信息失败: %w", err)
|
||||
}
|
||||
|
||||
// 更新字段
|
||||
userInvoiceInfo.CompanyName = invoiceInfo.CompanyName
|
||||
userInvoiceInfo.TaxpayerID = invoiceInfo.TaxpayerID
|
||||
userInvoiceInfo.BankName = invoiceInfo.BankName
|
||||
userInvoiceInfo.BankAccount = invoiceInfo.BankAccount
|
||||
userInvoiceInfo.CompanyAddress = invoiceInfo.CompanyAddress
|
||||
userInvoiceInfo.CompanyPhone = invoiceInfo.CompanyPhone
|
||||
userInvoiceInfo.ReceivingEmail = invoiceInfo.ReceivingEmail
|
||||
|
||||
err = s.userInvoiceInfoRepo.Update(ctx, userInvoiceInfo)
|
||||
} else {
|
||||
// 创建新记录
|
||||
userInvoiceInfo = &entities.UserInvoiceInfo{
|
||||
ID: uuid.New().String(),
|
||||
UserID: userID,
|
||||
CompanyName: invoiceInfo.CompanyName,
|
||||
TaxpayerID: invoiceInfo.TaxpayerID,
|
||||
BankName: invoiceInfo.BankName,
|
||||
BankAccount: invoiceInfo.BankAccount,
|
||||
CompanyAddress: invoiceInfo.CompanyAddress,
|
||||
CompanyPhone: invoiceInfo.CompanyPhone,
|
||||
ReceivingEmail: invoiceInfo.ReceivingEmail,
|
||||
}
|
||||
|
||||
err = s.userInvoiceInfoRepo.Create(ctx, userInvoiceInfo)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("保存用户开票信息失败: %w", err)
|
||||
}
|
||||
|
||||
return userInvoiceInfo, nil
|
||||
}
|
||||
|
||||
// CreateOrUpdateUserInvoiceInfoWithEnterpriseInfo 创建或更新用户开票信息(包含企业认证信息)
|
||||
func (s *UserInvoiceInfoServiceImpl) CreateOrUpdateUserInvoiceInfoWithEnterpriseInfo(ctx context.Context, userID string, invoiceInfo *value_objects.InvoiceInfo, companyName, taxpayerID string) (*entities.UserInvoiceInfo, error) {
|
||||
// 检查企业认证信息
|
||||
if companyName == "" || taxpayerID == "" {
|
||||
return nil, fmt.Errorf("用户未完成企业认证,无法创建开票信息")
|
||||
}
|
||||
|
||||
// 创建新的开票信息对象,使用传入的企业认证信息
|
||||
updatedInvoiceInfo := &value_objects.InvoiceInfo{
|
||||
CompanyName: companyName, // 从企业认证信息获取
|
||||
TaxpayerID: taxpayerID, // 从企业认证信息获取
|
||||
BankName: invoiceInfo.BankName, // 用户输入
|
||||
BankAccount: invoiceInfo.BankAccount, // 用户输入
|
||||
CompanyAddress: invoiceInfo.CompanyAddress, // 用户输入
|
||||
CompanyPhone: invoiceInfo.CompanyPhone, // 用户输入
|
||||
ReceivingEmail: invoiceInfo.ReceivingEmail, // 用户输入
|
||||
}
|
||||
|
||||
// 验证开票信息
|
||||
if err := s.ValidateInvoiceInfo(ctx, updatedInvoiceInfo, value_objects.InvoiceTypeGeneral); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 检查是否已存在
|
||||
exists, err := s.userInvoiceInfoRepo.Exists(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("检查用户开票信息失败: %w", err)
|
||||
}
|
||||
|
||||
var userInvoiceInfo *entities.UserInvoiceInfo
|
||||
|
||||
if exists {
|
||||
// 更新现有记录
|
||||
userInvoiceInfo, err = s.userInvoiceInfoRepo.FindByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取用户开票信息失败: %w", err)
|
||||
}
|
||||
|
||||
// 更新字段(公司名称和纳税人识别号从企业认证信息获取,其他字段从用户输入获取)
|
||||
userInvoiceInfo.CompanyName = companyName
|
||||
userInvoiceInfo.TaxpayerID = taxpayerID
|
||||
userInvoiceInfo.BankName = invoiceInfo.BankName
|
||||
userInvoiceInfo.BankAccount = invoiceInfo.BankAccount
|
||||
userInvoiceInfo.CompanyAddress = invoiceInfo.CompanyAddress
|
||||
userInvoiceInfo.CompanyPhone = invoiceInfo.CompanyPhone
|
||||
userInvoiceInfo.ReceivingEmail = invoiceInfo.ReceivingEmail
|
||||
|
||||
err = s.userInvoiceInfoRepo.Update(ctx, userInvoiceInfo)
|
||||
} else {
|
||||
// 创建新记录
|
||||
userInvoiceInfo = &entities.UserInvoiceInfo{
|
||||
ID: uuid.New().String(),
|
||||
UserID: userID,
|
||||
CompanyName: companyName, // 从企业认证信息获取
|
||||
TaxpayerID: taxpayerID, // 从企业认证信息获取
|
||||
BankName: invoiceInfo.BankName, // 用户输入
|
||||
BankAccount: invoiceInfo.BankAccount, // 用户输入
|
||||
CompanyAddress: invoiceInfo.CompanyAddress, // 用户输入
|
||||
CompanyPhone: invoiceInfo.CompanyPhone, // 用户输入
|
||||
ReceivingEmail: invoiceInfo.ReceivingEmail, // 用户输入
|
||||
}
|
||||
|
||||
err = s.userInvoiceInfoRepo.Create(ctx, userInvoiceInfo)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("保存用户开票信息失败: %w", err)
|
||||
}
|
||||
|
||||
return userInvoiceInfo, nil
|
||||
}
|
||||
|
||||
// ValidateInvoiceInfo 验证开票信息
|
||||
func (s *UserInvoiceInfoServiceImpl) ValidateInvoiceInfo(ctx context.Context, invoiceInfo *value_objects.InvoiceInfo, invoiceType value_objects.InvoiceType) error {
|
||||
if invoiceType == value_objects.InvoiceTypeGeneral {
|
||||
return invoiceInfo.ValidateForGeneralInvoice()
|
||||
} else if invoiceType == value_objects.InvoiceTypeSpecial {
|
||||
return invoiceInfo.ValidateForSpecialInvoice()
|
||||
}
|
||||
|
||||
return fmt.Errorf("无效的发票类型: %s", invoiceType)
|
||||
}
|
||||
|
||||
// DeleteUserInvoiceInfo 删除用户开票信息
|
||||
func (s *UserInvoiceInfoServiceImpl) DeleteUserInvoiceInfo(ctx context.Context, userID string) error {
|
||||
err := s.userInvoiceInfoRepo.Delete(ctx, userID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("删除用户开票信息失败: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user