Files
tyapi-server/docs/认证服务重构说明.md
2025-07-20 20:53:26 +08:00

295 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 认证服务重构说明
## 重构目标
根据DDD领域驱动设计原则重新划分认证服务的应用层和领域层职责提高代码的解耦性和可维护性。
## 重构前后对比
### 重构前的问题
1. **职责混乱**:应用服务层直接操作仓库,违反了分层架构原则
2. **业务逻辑分散**:认证相关的业务逻辑分散在应用服务中,难以维护
3. **耦合度高**:应用服务与具体的数据访问技术耦合
4. **可测试性差**:业务逻辑与基础设施代码混合,难以进行单元测试
### 重构后的改进
1. **职责清晰**:应用层专注业务流程编排,领域层专注业务逻辑
2. **高内聚低耦合**:按业务功能模块划分领域服务
3. **易于测试**:业务逻辑独立,可以独立进行单元测试
4. **易于扩展**:新增业务功能时,只需要在相应的领域服务中添加方法
## 新的架构设计
### 1. 应用服务层 (Application Layer)
**职责**
- 业务流程编排和协调
- 事务管理
- 数据转换DTO ↔ 领域对象)
- 调用领域服务
- 不直接操作仓库
**主要方法**
```go
type CertificationApplicationService interface {
// 认证状态查询
GetCertificationStatus(ctx context.Context, query *queries.GetCertificationStatusQuery) (*responses.CertificationResponse, error)
GetCertificationDetails(ctx context.Context, query *queries.GetCertificationDetailsQuery) (*responses.CertificationResponse, error)
GetCertificationProgress(ctx context.Context, userID string) (map[string]interface{}, error)
// 企业信息管理
SubmitEnterpriseInfo(ctx context.Context, cmd *commands.SubmitEnterpriseInfoCommand) (*responses.EnterpriseInfoResponse, error)
// 企业认证
EnterpriseVerify(ctx context.Context, userID string) (*responses.CertificationResponse, error)
// 合同管理
ApplyContract(ctx context.Context, userID string) (*responses.CertificationResponse, error)
CompleteContractSign(ctx context.Context, certificationID, contractURL string) error
}
```
### 2. 领域服务层 (Domain Layer)
#### 2.1 认证管理服务 (CertificationManagementService)
**职责**
- 认证申请的生命周期管理
- 认证申请的创建、查询
- 认证进度信息获取
**主要方法**
```go
type CertificationManagementService struct {
certRepo repositories.CertificationRepository
stateMachine *CertificationStateMachine
logger *zap.Logger
}
// 主要方法
- CreateCertification(ctx context.Context, userID string) (*entities.Certification, error)
- GetCertificationByUserID(ctx context.Context, userID string) (*entities.Certification, error)
- GetCertificationByID(ctx context.Context, certificationID string) (*entities.Certification, error)
- GetCertificationProgress(ctx context.Context, certificationID string) (map[string]interface{}, error)
```
#### 2.2 认证工作流服务 (CertificationWorkflowService)
**职责**
- 认证流程的状态转换
- 业务规则验证
- 状态机操作
**主要方法**
```go
type CertificationWorkflowService struct {
certRepo repositories.CertificationRepository
stateMachine *CertificationStateMachine
logger *zap.Logger
}
// 主要方法
- SubmitEnterpriseInfo(ctx context.Context, certificationID string) error
- CompleteEnterpriseVerification(ctx context.Context, certificationID string) error
- ApplyContract(ctx context.Context, certificationID string) error
- CompleteContractSign(ctx context.Context, certificationID, contractURL string) error
- CompleteCertification(ctx context.Context, certificationID string) error
```
#### 2.3 企业信息服务 (EnterpriseService)
**职责**
- 企业信息的创建、更新、查询
- 企业信息验证状态管理
- 企业认证流程
**主要方法**
```go
type EnterpriseService struct {
userRepo repositories.UserRepository
enterpriseInfoRepo repositories.EnterpriseInfoRepository
logger *zap.Logger
}
// 主要方法
- CreateEnterpriseInfo(ctx context.Context, userID, companyName, unifiedSocialCode, legalPersonName, legalPersonID string) (*entities.EnterpriseInfo, error)
- GetEnterpriseInfo(ctx context.Context, userID string) (*entities.EnterpriseInfo, error)
- UpdateOCRVerification(ctx context.Context, userID string, isVerified bool, rawData string, confidence float64) error
- UpdateFaceVerification(ctx context.Context, userID string, isVerified bool) error
- CheckUnifiedSocialCodeExists(ctx context.Context, unifiedSocialCode, excludeUserID string) (bool, error)
```
## 业务流程示例
### 提交企业信息流程
```go
// 应用服务层 - 业务流程编排
func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(ctx context.Context, cmd *commands.SubmitEnterpriseInfoCommand) (*responses.EnterpriseInfoResponse, error) {
// 1. 验证企业信息(检查统一社会信用代码是否已存在)
exists, err := s.enterpriseService.CheckUnifiedSocialCodeExists(ctx, cmd.UnifiedSocialCode, "")
if err != nil {
return nil, fmt.Errorf("检查企业信息失败: %w", err)
}
if exists {
return nil, fmt.Errorf("统一社会信用代码已存在")
}
// 2. 获取或创建认证申请
certification, err := s.certManagementService.GetCertificationByUserID(ctx, cmd.UserID)
if err != nil {
// 如果认证申请不存在,自动创建
if err.Error() == "认证申请不存在" || err.Error() == "record not found" {
certification, err = s.certManagementService.CreateCertification(ctx, cmd.UserID)
if err != nil {
return nil, fmt.Errorf("创建认证申请失败: %w", err)
}
} else {
return nil, fmt.Errorf("获取认证申请失败: %w", err)
}
}
// 3. 提交企业信息(状态转换)
if err := s.certWorkflowService.SubmitEnterpriseInfo(ctx, certification.ID); err != nil {
return nil, err
}
// 4. 创建企业信息
enterpriseInfo, err := s.enterpriseService.CreateEnterpriseInfo(ctx, cmd.UserID, cmd.CompanyName, cmd.UnifiedSocialCode, cmd.LegalPersonName, cmd.LegalPersonID)
if err != nil {
return nil, err
}
return s.buildEnterpriseInfoResponse(enterpriseInfo), nil
}
```
### 企业认证流程
```go
// 应用服务层 - 业务流程编排
func (s *CertificationApplicationServiceImpl) EnterpriseVerify(ctx context.Context, userID string) (*responses.CertificationResponse, error) {
// 1. 获取认证申请
certification, err := s.certManagementService.GetCertificationByUserID(ctx, userID)
if err != nil {
return nil, fmt.Errorf("用户尚未创建认证申请: %w", err)
}
// 2. 完成企业认证(状态转换)
if err := s.certWorkflowService.CompleteEnterpriseVerification(ctx, certification.ID); err != nil {
return nil, err
}
// 3. 更新企业信息验证状态模拟OCR和人脸识别验证
if err := s.enterpriseService.UpdateOCRVerification(ctx, userID, true, "OCR验证通过", 0.95); err != nil {
s.logger.Warn("更新OCR验证状态失败", zap.Error(err))
}
if err := s.enterpriseService.UpdateFaceVerification(ctx, userID, true); err != nil {
s.logger.Warn("更新人脸识别验证状态失败", zap.Error(err))
}
// 4. 重新获取更新后的认证申请
updatedCertification, err := s.certManagementService.GetCertificationByID(ctx, certification.ID)
if err != nil {
return nil, err
}
return s.buildCertificationResponse(updatedCertification), nil
}
```
## 依赖注入配置
### 领域服务注册
```go
// 领域服务
fx.Provide(
user_service.NewUserService,
user_service.NewSMSCodeService,
user_service.NewEnterpriseService,
certification_service.NewCertificationManagementService, // 新增
certification_service.NewCertificationWorkflowService, // 新增
certification_service.NewCertificationStateMachine,
finance_service.NewFinanceService,
product_service.NewProductService,
),
```
### 应用服务注册
```go
// 应用服务
fx.Annotate(
certification.NewCertificationApplicationService,
fx.As(new(certification.CertificationApplicationService)),
),
```
## 重构收益
### 1. 代码质量提升
- **单一职责原则**:每个服务都有明确的职责范围
- **开闭原则**:新增功能时不需要修改现有代码
- **依赖倒置原则**:高层模块不依赖低层模块,都依赖抽象
### 2. 可维护性提升
- **业务逻辑集中**:相关的业务逻辑集中在对应的领域服务中
- **易于理解**:代码结构清晰,新人容易理解
- **易于调试**:问题定位更容易,调试更简单
### 3. 可测试性提升
- **单元测试**:每个领域服务可以独立进行单元测试
- **集成测试**:应用服务层可以独立进行集成测试
- **模拟测试**:可以轻松模拟依赖的服务
### 4. 可扩展性提升
- **新增功能**:只需要在相应的领域服务中添加方法
- **修改功能**:修改影响范围小,不会影响其他模块
- **替换实现**:可以轻松替换某个服务的实现
## 最佳实践
### 1. 应用服务层设计原则
- **业务流程编排**:专注于业务流程的编排和协调
- **事务管理**:负责事务的边界和一致性
- **数据转换**负责DTO和领域对象之间的转换
- **错误处理**:统一处理业务异常和系统异常
### 2. 领域服务层设计原则
- **业务功能模块化**:按业务功能划分服务
- **单一职责**:每个服务只负责一个业务领域
- **高内聚**:相关的业务逻辑集中在一起
- **低耦合**:服务之间通过接口进行交互
### 3. 命名规范
- **应用服务**`XxxApplicationService`
- **领域服务**`XxxManagementService``XxxWorkflowService``XxxService`
- **方法命名**:使用动词+名词的形式,如`CreateCertification``SubmitEnterpriseInfo`
### 4. 错误处理
- **业务异常**:在领域服务中抛出业务异常
- **系统异常**:在应用服务中处理系统异常
- **错误信息**:使用中文错误信息,提高用户体验
## 总结
通过这次重构,我们实现了:
1. **清晰的职责划分**:应用层专注业务流程编排,领域层专注业务逻辑
2. **高内聚低耦合**:按业务功能模块划分服务,降低模块间耦合
3. **易于测试**:业务逻辑独立,可以独立进行单元测试
4. **易于维护**:代码结构清晰,新人容易理解和维护
5. **易于扩展**:新增功能时影响范围小,不会影响其他模块
这种架构设计符合DDD原则为后续的功能扩展和维护奠定了良好的基础。