v1.0.0
This commit is contained in:
288
DDD规范企业认证信息自动填充实现总结.md
Normal file
288
DDD规范企业认证信息自动填充实现总结.md
Normal file
@@ -0,0 +1,288 @@
|
||||
# DDD规范企业认证信息自动填充实现总结
|
||||
|
||||
## 概述
|
||||
|
||||
根据DDD(领域驱动设计)架构规范,重新设计了企业认证信息自动填充功能。在DDD中,跨域操作应该通过应用服务层来协调,而不是在领域服务层直接操作其他领域的仓储。
|
||||
|
||||
## DDD架构规范
|
||||
|
||||
### 1. 领域边界原则
|
||||
- **领域服务层**:只能操作本领域的仓储和实体
|
||||
- **应用服务层**:负责跨域协调,调用不同领域的服务
|
||||
- **聚合根**:每个领域有自己的聚合根,不能直接访问其他领域的聚合根
|
||||
|
||||
### 2. 依赖方向
|
||||
```
|
||||
应用服务层 → 领域服务层 → 仓储层
|
||||
↓
|
||||
跨域协调
|
||||
```
|
||||
|
||||
## 重新设计架构
|
||||
|
||||
### 1. 领域服务层(Finance Domain)
|
||||
|
||||
#### `UserInvoiceInfoService`接口更新
|
||||
```go
|
||||
type UserInvoiceInfoService interface {
|
||||
// 基础方法
|
||||
GetUserInvoiceInfo(ctx context.Context, userID string) (*entities.UserInvoiceInfo, error)
|
||||
CreateOrUpdateUserInvoiceInfo(ctx context.Context, userID string, invoiceInfo *value_objects.InvoiceInfo) (*entities.UserInvoiceInfo, error)
|
||||
|
||||
// 新增:包含企业认证信息的方法
|
||||
GetUserInvoiceInfoWithEnterpriseInfo(ctx context.Context, userID string, companyName, taxpayerID string) (*entities.UserInvoiceInfo, error)
|
||||
CreateOrUpdateUserInvoiceInfoWithEnterpriseInfo(ctx context.Context, userID string, invoiceInfo *value_objects.InvoiceInfo, companyName, taxpayerID string) (*entities.UserInvoiceInfo, error)
|
||||
|
||||
ValidateInvoiceInfo(ctx context.Context, invoiceInfo *value_objects.InvoiceInfo, invoiceType value_objects.InvoiceType) error
|
||||
DeleteUserInvoiceInfo(ctx context.Context, userID string) error
|
||||
}
|
||||
```
|
||||
|
||||
#### 实现特点
|
||||
- **移除跨域依赖**:不再直接依赖`user_repo.UserRepository`
|
||||
- **参数化设计**:通过方法参数接收企业认证信息
|
||||
- **保持纯净性**:领域服务只处理本领域的业务逻辑
|
||||
|
||||
### 2. 应用服务层(Application Layer)
|
||||
|
||||
#### `InvoiceApplicationService`更新
|
||||
```go
|
||||
type InvoiceApplicationServiceImpl struct {
|
||||
invoiceRepo finance_repo.InvoiceApplicationRepository
|
||||
userInvoiceInfoRepo finance_repo.UserInvoiceInfoRepository
|
||||
userRepo user_repo.UserRepository
|
||||
userAggregateService user_service.UserAggregateService // 新增:用户聚合服务
|
||||
rechargeRecordRepo finance_repo.RechargeRecordRepository
|
||||
walletRepo finance_repo.WalletRepository
|
||||
invoiceDomainService services.InvoiceDomainService
|
||||
invoiceAggregateService services.InvoiceAggregateService
|
||||
userInvoiceInfoService services.UserInvoiceInfoService
|
||||
storageService *storage.QiNiuStorageService
|
||||
logger *zap.Logger
|
||||
}
|
||||
```
|
||||
|
||||
#### 跨域协调逻辑
|
||||
```go
|
||||
func (s *InvoiceApplicationServiceImpl) GetUserInvoiceInfo(ctx context.Context, userID string) (*dto.InvoiceInfoResponse, error) {
|
||||
// 1. 通过用户聚合服务获取企业认证信息
|
||||
user, err := s.userAggregateService.GetUserWithEnterpriseInfo(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取用户企业认证信息失败: %w", err)
|
||||
}
|
||||
|
||||
// 2. 提取企业认证信息
|
||||
var companyName, taxpayerID string
|
||||
var companyNameReadOnly, taxpayerIDReadOnly bool
|
||||
|
||||
if user.EnterpriseInfo != nil {
|
||||
companyName = user.EnterpriseInfo.CompanyName
|
||||
taxpayerID = user.EnterpriseInfo.UnifiedSocialCode
|
||||
companyNameReadOnly = true
|
||||
taxpayerIDReadOnly = true
|
||||
}
|
||||
|
||||
// 3. 调用领域服务,传入企业认证信息
|
||||
userInvoiceInfo, err := s.userInvoiceInfoService.GetUserInvoiceInfoWithEnterpriseInfo(ctx, userID, companyName, taxpayerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 4. 构建响应DTO
|
||||
return &dto.InvoiceInfoResponse{
|
||||
CompanyName: userInvoiceInfo.CompanyName,
|
||||
TaxpayerID: userInvoiceInfo.TaxpayerID,
|
||||
// ... 其他字段
|
||||
CompanyNameReadOnly: companyNameReadOnly,
|
||||
TaxpayerIDReadOnly: taxpayerIDReadOnly,
|
||||
}, nil
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 依赖注入更新
|
||||
|
||||
#### 容器配置
|
||||
```go
|
||||
// 用户聚合服务
|
||||
fx.Annotate(
|
||||
user_service.NewUserAggregateService,
|
||||
fx.ResultTags(`name:"userAggregateService"`),
|
||||
),
|
||||
|
||||
// 用户开票信息服务(移除userRepo依赖)
|
||||
fx.Annotate(
|
||||
finance_service.NewUserInvoiceInfoService,
|
||||
fx.ParamTags(
|
||||
`name:"userInvoiceInfoRepo"`,
|
||||
),
|
||||
fx.ResultTags(`name:"userInvoiceInfoService"`),
|
||||
),
|
||||
|
||||
// 发票应用服务(添加userAggregateService依赖)
|
||||
fx.Annotate(
|
||||
finance.NewInvoiceApplicationService,
|
||||
fx.As(new(finance.InvoiceApplicationService)),
|
||||
fx.ParamTags(
|
||||
`name:"invoiceRepo"`,
|
||||
`name:"userInvoiceInfoRepo"`,
|
||||
`name:"userRepo"`,
|
||||
`name:"userAggregateService"`, // 新增
|
||||
`name:"rechargeRecordRepo"`,
|
||||
`name:"walletRepo"`,
|
||||
`name:"domainService"`,
|
||||
`name:"aggregateService"`,
|
||||
`name:"userInvoiceInfoService"`,
|
||||
`name:"storageService"`,
|
||||
`name:"logger"`,
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
## 架构优势
|
||||
|
||||
### 1. 符合DDD规范
|
||||
- **领域边界清晰**:每个领域只处理自己的业务逻辑
|
||||
- **依赖方向正确**:应用服务层负责跨域协调
|
||||
- **聚合根隔离**:不同领域的聚合根不直接交互
|
||||
|
||||
### 2. 可维护性
|
||||
- **职责分离**:领域服务专注于本领域逻辑
|
||||
- **易于测试**:可以独立测试每个领域服务
|
||||
- **扩展性好**:新增跨域功能时只需修改应用服务层
|
||||
|
||||
### 3. 业务逻辑清晰
|
||||
- **数据流向明确**:企业认证信息 → 应用服务 → 开票信息
|
||||
- **错误处理统一**:在应用服务层统一处理跨域错误
|
||||
- **权限控制集中**:在应用服务层统一控制访问权限
|
||||
|
||||
## 工作流程
|
||||
|
||||
### 1. 获取开票信息流程
|
||||
```
|
||||
用户请求 → 应用服务层
|
||||
↓
|
||||
调用UserAggregateService.GetUserWithEnterpriseInfo()
|
||||
↓
|
||||
获取企业认证信息
|
||||
↓
|
||||
调用UserInvoiceInfoService.GetUserInvoiceInfoWithEnterpriseInfo()
|
||||
↓
|
||||
返回开票信息(包含企业认证信息)
|
||||
```
|
||||
|
||||
### 2. 更新开票信息流程
|
||||
```
|
||||
用户请求 → 应用服务层
|
||||
↓
|
||||
调用UserAggregateService.GetUserWithEnterpriseInfo()
|
||||
↓
|
||||
验证企业认证状态
|
||||
↓
|
||||
调用UserInvoiceInfoService.CreateOrUpdateUserInvoiceInfoWithEnterpriseInfo()
|
||||
↓
|
||||
保存开票信息(企业认证信息自动填充)
|
||||
```
|
||||
|
||||
## 技术实现要点
|
||||
|
||||
### 1. 接口设计
|
||||
- **向后兼容**:保留原有的基础方法
|
||||
- **功能扩展**:新增包含企业认证信息的方法
|
||||
- **参数传递**:通过方法参数传递跨域数据
|
||||
|
||||
### 2. 错误处理
|
||||
- **分层处理**:在应用服务层处理跨域错误
|
||||
- **错误传播**:领域服务层错误向上传播
|
||||
- **用户友好**:提供清晰的错误信息
|
||||
|
||||
### 3. 性能优化
|
||||
- **减少查询**:应用服务层缓存企业认证信息
|
||||
- **批量操作**:支持批量获取和更新
|
||||
- **异步处理**:非关键路径支持异步处理
|
||||
|
||||
## 代码示例
|
||||
|
||||
### 1. 领域服务实现
|
||||
```go
|
||||
// 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: "",
|
||||
TaxpayerID: "",
|
||||
BankName: "",
|
||||
BankAccount: "",
|
||||
CompanyAddress: "",
|
||||
CompanyPhone: "",
|
||||
ReceivingEmail: "",
|
||||
}
|
||||
}
|
||||
|
||||
// 使用传入的企业认证信息填充公司名称和纳税人识别号
|
||||
if companyName != "" {
|
||||
info.CompanyName = companyName
|
||||
}
|
||||
if taxpayerID != "" {
|
||||
info.TaxpayerID = taxpayerID
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 应用服务实现
|
||||
```go
|
||||
func (s *InvoiceApplicationServiceImpl) UpdateUserInvoiceInfo(ctx context.Context, userID string, req UpdateInvoiceInfoRequest) error {
|
||||
// 获取用户企业认证信息
|
||||
user, err := s.userAggregateService.GetUserWithEnterpriseInfo(ctx, userID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取用户企业认证信息失败: %w", err)
|
||||
}
|
||||
|
||||
// 检查用户是否有企业认证信息
|
||||
if user.EnterpriseInfo == nil {
|
||||
return fmt.Errorf("用户未完成企业认证,无法创建开票信息")
|
||||
}
|
||||
|
||||
// 创建开票信息对象
|
||||
invoiceInfo := value_objects.NewInvoiceInfo(
|
||||
"", // 公司名称将由服务层从企业认证信息中获取
|
||||
"", // 纳税人识别号将由服务层从企业认证信息中获取
|
||||
req.BankName,
|
||||
req.BankAccount,
|
||||
req.CompanyAddress,
|
||||
req.CompanyPhone,
|
||||
req.ReceivingEmail,
|
||||
)
|
||||
|
||||
// 使用包含企业认证信息的方法
|
||||
_, err = s.userInvoiceInfoService.CreateOrUpdateUserInvoiceInfoWithEnterpriseInfo(
|
||||
ctx,
|
||||
userID,
|
||||
invoiceInfo,
|
||||
user.EnterpriseInfo.CompanyName,
|
||||
user.EnterpriseInfo.UnifiedSocialCode,
|
||||
)
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
## 总结
|
||||
|
||||
通过按照DDD规范重新设计,我们实现了:
|
||||
|
||||
1. ✅ **架构规范**:严格遵循DDD的领域边界和依赖方向
|
||||
2. ✅ **职责分离**:领域服务专注于本领域逻辑,应用服务负责跨域协调
|
||||
3. ✅ **可维护性**:代码结构清晰,易于理解和维护
|
||||
4. ✅ **可扩展性**:新增跨域功能时只需修改应用服务层
|
||||
5. ✅ **业务逻辑**:企业认证信息自动填充功能完整实现
|
||||
|
||||
这种设计既满足了业务需求,又符合DDD架构规范,是一个优秀的架构实现。
|
||||
Reference in New Issue
Block a user