docs(architecture): 添加互换模块独立化实施方案文档
- 设计了基于YARP反向代理的微服务架构方案 - 规划了独立互换API服务的技术实现细节 - 定义了完整的数据传输对象(DTO)结构 - 制定了前端无感知适配的路由映射规则 - 创建了详细的迁移清单和实施路线图 - 配置了监控告警和风险控制机制
This commit is contained in:
@@ -0,0 +1,594 @@
|
||||
# 互换模块独立化最终实施方案
|
||||
|
||||
## 一、项目现状分析
|
||||
|
||||
### 1.1 技术架构特征
|
||||
- **传统ASP.NET Core MVC**:基于Razor视图引擎的服务端渲染
|
||||
- **jQuery+Bootstrap前端**:经典的Web表单交互模式
|
||||
- **单体应用结构**:所有功能模块紧密耦合在同一应用中
|
||||
- **共享数据库**:统一的数据存储和访问层
|
||||
|
||||
### 1.2 互换模块现状
|
||||
```
|
||||
互换相关组件:
|
||||
├── Controllers (SwapTradeController.cs, SwapTrade2Controller.cs)
|
||||
├── Views (40+个.cshtml文件,包括复杂表单和报表)
|
||||
├── JavaScript (大量业务逻辑嵌入在页面脚本中)
|
||||
├── Business Logic (SwapModule业务服务层)
|
||||
└── Data Access (直接使用主应用DbContext)
|
||||
```
|
||||
|
||||
## 二、最终方案设计
|
||||
|
||||
### 2.1 核心架构原则
|
||||
✅ **前端零改动** - 保持所有.cshtml页面和JavaScript逻辑不变
|
||||
✅ **后端独立化** - 互换业务逻辑抽取为独立API服务
|
||||
✅ **共享数据库** - 继续使用现有数据库,避免数据迁移风险
|
||||
✅ **无感知切换** - 用户体验完全一致,业务连续性100%
|
||||
|
||||
### 2.2 最终架构图
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ 前端页面 │ │ YARP代理 │ │ 互换API服务 │
|
||||
│ (YLErpWeb) │◄──►│ (反向代理) │◄──►│ (YLSwapService) │
|
||||
│ - .cshtml页面 │ │ - 路由智能转发 │ │ - 纯REST API │
|
||||
│ - jQuery脚本 │ │ - 负载均衡 │ │ - 业务逻辑 │
|
||||
│ - Razor渲染 │ │ - SSL终止 │ │ - 数据访问 │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ 共享数据库 │ │ 缓存中间件 │ │ 监控告警 │
|
||||
│ (统一数据源) │ │ (Redis/内存) │ │ (Prometheus) │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
```
|
||||
|
||||
## 三、技术选型与实现
|
||||
|
||||
### 3.1 YARP反向代理配置(推荐)
|
||||
|
||||
#### 为什么选择YARP?
|
||||
✅ **.NET原生** - 与现有技术栈完美契合
|
||||
✅ **高性能** - 比Nginx/Ocelot更好的性能表现
|
||||
✅ **灵活路由** - 支持复杂的路由匹配和转换规则
|
||||
✅ **易于维护** - 统一的.NET生态系统管理
|
||||
|
||||
#### YARP配置示例
|
||||
```csharp
|
||||
// Program.cs - YARP代理配置
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// 添加YARP反向代理
|
||||
builder.Services.AddReverseProxy()
|
||||
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// 配置路由转发规则
|
||||
app.MapReverseProxy(proxyPipeline =>
|
||||
{
|
||||
// 互换API路由 - 转发到独立服务
|
||||
proxyPipeline.UseWhen(context =>
|
||||
context.Request.Path.StartsWithSegments("/api/swap"),
|
||||
handler =>
|
||||
{
|
||||
handler.UseMiddleware<AuthenticationMiddleware>(); // 认证前置
|
||||
handler.UseSessionAffinity(); // 会话亲和性
|
||||
});
|
||||
|
||||
// 前端页面路由 - 保持原有处理
|
||||
proxyPipeline.UseWhen(context =>
|
||||
context.Request.Path.StartsWithSegments("/swaptrade") ||
|
||||
context.Request.Path.StartsWithSegments("/swaptrade2"),
|
||||
handler =>
|
||||
{
|
||||
// 原有ASP.NET Core MVC处理逻辑
|
||||
});
|
||||
});
|
||||
|
||||
app.Run();
|
||||
```
|
||||
|
||||
#### appsettings.json配置
|
||||
```json
|
||||
{
|
||||
"ReverseProxy": {
|
||||
"Routes": {
|
||||
"swap-api-route": {
|
||||
"ClusterId": "swap-backend",
|
||||
"Match": {
|
||||
"Path": "/api/swap/{**remainder}"
|
||||
},
|
||||
"Transforms": [
|
||||
{ "PathRemovePrefix": "/api/swap" }
|
||||
]
|
||||
},
|
||||
"swap-frontend-route": {
|
||||
"ClusterId": "main-app",
|
||||
"Match": {
|
||||
"Path": "/swaptrade/{**remainder}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Clusters": {
|
||||
"swap-backend": {
|
||||
"Destinations": {
|
||||
"swap1": {
|
||||
"Address": "http://swap-service-1:8080/"
|
||||
},
|
||||
"swap2": {
|
||||
"Address": "http://swap-service-2:8080/"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main-app": {
|
||||
"Destinations": {
|
||||
"main": {
|
||||
"Address": "http://main-application:80/"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 独立互换服务实现
|
||||
|
||||
#### 项目结构
|
||||
```
|
||||
YLSwapService/
|
||||
├── Controllers/
|
||||
│ ├── SwapTradeController.cs # 交易API
|
||||
│ ├── SwapPositionController.cs # 持仓API
|
||||
│ └── SwapRiskController.cs # 风险API
|
||||
├── Services/
|
||||
│ ├── ISwapTradeService.cs # 业务接口
|
||||
│ └── SwapTradeService.cs # 业务实现
|
||||
├── DTOs/
|
||||
│ ├── Requests/ # 请求数据传输对象
|
||||
│ └── Responses/ # 响应数据传输对象
|
||||
├── Infrastructure/
|
||||
│ ├── Database/ # 数据访问配置
|
||||
│ └── Messaging/ # 消息队列集成
|
||||
└── Program.cs # 启动配置
|
||||
```
|
||||
|
||||
#### 核心服务实现
|
||||
```csharp
|
||||
// Services/SwapTradeService.cs
|
||||
public class SwapTradeService : ISwapTradeService
|
||||
{
|
||||
private readonly YLContext _context;
|
||||
private readonly ILogger<SwapTradeService> _logger;
|
||||
|
||||
public async Task<TradeResultDto> CreateTradeAsync(CreateSwapTradeRequestDto request)
|
||||
{
|
||||
using var transaction = await _context.Database.BeginTransactionAsync();
|
||||
try
|
||||
{
|
||||
// 业务逻辑处理 - 与原系统保持一致
|
||||
var tradeNumber = GenerateTradeNumber();
|
||||
var trade = new trade
|
||||
{
|
||||
TradeNumber = tradeNumber,
|
||||
ClientId = request.ClientId,
|
||||
ClientName = request.ClientName,
|
||||
UnderlyingCode = request.UnderlyingCode,
|
||||
TradeDate = request.TradeDate,
|
||||
StartDate = request.StartDate,
|
||||
ExerciseDate = request.ExerciseDate,
|
||||
Notional = request.Notional,
|
||||
SpotPrice = request.SpotPrice,
|
||||
AssetBookName = request.AssetBookName,
|
||||
TraderName = request.TraderName,
|
||||
TradeType = "收益互换",
|
||||
StructureType = "收益互换",
|
||||
TradeStatus = "待确认",
|
||||
ValidState = "Valid",
|
||||
OptId = GetCurrentUserId(),
|
||||
OptName = GetCurrentUserName(),
|
||||
OptDate = DateTime.Now
|
||||
};
|
||||
|
||||
_context.trade.Add(trade);
|
||||
await _context.SaveChangesAsync();
|
||||
await transaction.CommitAsync();
|
||||
|
||||
return new TradeResultDto
|
||||
{
|
||||
TradeId = trade.id,
|
||||
TradeNumber = tradeNumber,
|
||||
Status = "成功",
|
||||
Message = "交易创建成功"
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await transaction.RollbackAsync();
|
||||
_logger.LogError(ex, "创建互换交易失败");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 真实DTO数据结构
|
||||
|
||||
**交易相关DTO**
|
||||
```csharp
|
||||
// 创建互换交易请求DTO
|
||||
public class CreateSwapTradeRequestDto
|
||||
{
|
||||
public int ClientId { get; set; }
|
||||
public string ClientName { get; set; } = string.Empty;
|
||||
public string UnderlyingCode { get; set; } = string.Empty;
|
||||
public DateTime TradeDate { get; set; }
|
||||
public DateTime StartDate { get; set; }
|
||||
public DateTime ExerciseDate { get; set; }
|
||||
public decimal Notional { get; set; }
|
||||
public decimal SpotPrice { get; set; }
|
||||
public string AssetBookName { get; set; } = string.Empty;
|
||||
public string TraderName { get; set; } = string.Empty;
|
||||
public string PayLongShort { get; set; } = string.Empty;
|
||||
public decimal PayFixedRate { get; set; }
|
||||
public decimal GetFixedRate { get; set; }
|
||||
public string MarginRateType { get; set; } = "年化";
|
||||
public decimal MarginRate { get; set; }
|
||||
public string MetaData { get; set; } = "{}";
|
||||
}
|
||||
|
||||
// 更新互换交易请求DTO
|
||||
public class UpdateSwapTradeRequestDto
|
||||
{
|
||||
public decimal? Notional { get; set; }
|
||||
public decimal? SpotPrice { get; set; }
|
||||
public DateTime? ExerciseDate { get; set; }
|
||||
public decimal? PayFixedRate { get; set; }
|
||||
public decimal? GetFixedRate { get; set; }
|
||||
public decimal? MarginRate { get; set; }
|
||||
public string? MetaData { get; set; }
|
||||
}
|
||||
|
||||
// 交易结果DTO
|
||||
public class TradeResultDto
|
||||
{
|
||||
public int TradeId { get; set; }
|
||||
public string TradeNumber { get; set; } = string.Empty;
|
||||
public string Status { get; set; } = string.Empty;
|
||||
public string Message { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
// 互换交易DTO
|
||||
public class SwapTradeDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string TradeNumber { get; set; } = string.Empty;
|
||||
public int ClientId { get; set; }
|
||||
public string ClientName { get; set; } = string.Empty;
|
||||
public string UnderlyingCode { get; set; } = string.Empty;
|
||||
public DateTime TradeDate { get; set; }
|
||||
public DateTime StartDate { get; set; }
|
||||
public DateTime ExerciseDate { get; set; }
|
||||
public decimal Notional { get; set; }
|
||||
public decimal SpotPrice { get; set; }
|
||||
public string TradeStatus { get; set; } = string.Empty;
|
||||
public DateTime CreatedAt { get; set; }
|
||||
}
|
||||
|
||||
// 互换交易详情DTO
|
||||
public class SwapTradeDetailDto : SwapTradeDto
|
||||
{
|
||||
public string AssetBookName { get; set; } = string.Empty;
|
||||
public string TraderName { get; set; } = string.Empty;
|
||||
public string PayLongShort { get; set; } = string.Empty;
|
||||
public decimal PayFixedRate { get; set; }
|
||||
public decimal GetFixedRate { get; set; }
|
||||
public string MarginRateType { get; set; } = string.Empty;
|
||||
public decimal MarginRate { get; set; }
|
||||
public string MetaData { get; set; } = "{}";
|
||||
public List<SwapPositionDto> Positions { get; set; } = new();
|
||||
}
|
||||
```
|
||||
|
||||
**持仓相关DTO**
|
||||
```csharp
|
||||
// 互换持仓DTO
|
||||
public class SwapPositionDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int TradeId { get; set; }
|
||||
public string TradeNumber { get; set; } = string.Empty;
|
||||
public string UnderlyingCode { get; set; } = string.Empty;
|
||||
public string UnderlyingName { get; set; } = string.Empty;
|
||||
public decimal Quantity { get; set; }
|
||||
public decimal Price { get; set; }
|
||||
public decimal MarketValue { get; set; }
|
||||
public decimal Pnl { get; set; }
|
||||
public DateTime PositionDate { get; set; }
|
||||
public string Status { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
// 互换持仓详情DTO
|
||||
public class SwapPositionDetailDto : SwapPositionDto
|
||||
{
|
||||
public decimal Delta { get; set; }
|
||||
public decimal Gamma { get; set; }
|
||||
public decimal Vega { get; set; }
|
||||
public decimal Theta { get; set; }
|
||||
public decimal Rho { get; set; }
|
||||
public decimal MarginRequirement { get; set; }
|
||||
public DateTime LastUpdateTime { get; set; }
|
||||
}
|
||||
|
||||
// 持仓风险请求DTO
|
||||
public class PositionRiskRequestDto
|
||||
{
|
||||
public List<int> PositionIds { get; set; } = new();
|
||||
public string CalculationDate { get; set; } = DateTime.Today.ToString("yyyy-MM-dd");
|
||||
public string ScenarioType { get; set; } = "基准情景";
|
||||
}
|
||||
|
||||
// 持仓风险指标DTO
|
||||
public class PositionRiskMetricsDto
|
||||
{
|
||||
public decimal TotalExposure { get; set; }
|
||||
public decimal VaR { get; set; }
|
||||
public decimal MaxLoss { get; set; }
|
||||
public Dictionary<string, decimal> Greeks { get; set; } = new();
|
||||
public DateTime CalculationTime { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
**互换交易控制器 (SwapTradeController)**
|
||||
- `POST /api/SwapTrade` - 创建互换交易
|
||||
- 请求体:CreateSwapTradeRequestDto
|
||||
- 响应:TradeResultDto
|
||||
|
||||
- `GET /api/SwapTrade/{id}` - 获取交易详情
|
||||
- 参数:int id (交易ID)
|
||||
- 响应:SwapTradeDetailDto
|
||||
|
||||
- `GET /api/SwapTrade/client/{clientId}` - 查询客户互换交易列表
|
||||
- 参数:int clientId (客户ID), string status (可选)
|
||||
- 响应:List<SwapTradeDto>
|
||||
|
||||
- `PUT /api/SwapTrade/{id}` - 更新互换交易
|
||||
- 参数:int id (交易ID), UpdateSwapTradeRequestDto request
|
||||
- 响应:TradeResultDto
|
||||
|
||||
- `DELETE /api/SwapTrade/{id}` - 删除互换交易
|
||||
- 参数:int id (交易ID)
|
||||
- 响应:204 No Content
|
||||
|
||||
**互换持仓控制器 (SwapPositionController)**
|
||||
- `GET /api/SwapPosition/client/{clientId}` - 获取客户持仓列表
|
||||
- 参数:int clientId (客户ID)
|
||||
- 响应:List<SwapPositionDto>
|
||||
|
||||
- `GET /api/SwapPosition/trade/{tradeId}` - 获取交易持仓详情
|
||||
- 参数:int tradeId (交易ID)
|
||||
- 响应:List<SwapPositionDetailDto>
|
||||
|
||||
- `POST /api/SwapPosition/risk/calculate` - 计算持仓风险指标
|
||||
- 请求体:PositionRiskRequestDto
|
||||
- 响应:PositionRiskMetricsDto
|
||||
|
||||
### 3.3 前端无感知适配
|
||||
|
||||
#### JavaScript调用自动转发
|
||||
```javascript
|
||||
// 前端调用保持原有方式不变
|
||||
function createSwapTrade(formData) {
|
||||
// 原有调用方式 - 保持不变
|
||||
return $.post('/swaptrade/CreateTrade', formData);
|
||||
// 通过YARP自动转发到 /api/SwapTrade
|
||||
}
|
||||
|
||||
function querySwapTrades(clientId, status) {
|
||||
// 原有调用方式 - 保持不变
|
||||
return $.get(`/swaptrade/QueryTrades?clientId=${clientId}&status=${status || ''}`);
|
||||
// 通过YARP自动转发到 /api/SwapTrade/client/{clientId}?status={status}
|
||||
}
|
||||
|
||||
function getSwapTradeDetail(tradeId) {
|
||||
// 原有调用方式 - 保持不变
|
||||
return $.get(`/swaptrade/GetTradeDetail?id=${tradeId}`);
|
||||
// 通过YARP自动转发到 /api/SwapTrade/{id}
|
||||
}
|
||||
|
||||
function querySwapPositions(clientId) {
|
||||
// 原有调用方式 - 保持不变
|
||||
return $.get(`/swaptrade2/GetPosition?clientId=${clientId}`);
|
||||
// 通过YARP自动转发到 /api/SwapPosition/client/{clientId}
|
||||
}
|
||||
|
||||
function calculatePositionRisk(positionIds) {
|
||||
// 原有调用方式 - 保持不变
|
||||
return $.post('/swapposition/CalculateRisk', { positionIds: positionIds });
|
||||
// 通过YARP自动转发到 /api/SwapPosition/risk/calculate
|
||||
}
|
||||
```
|
||||
|
||||
#### 路由映射规则
|
||||
```csharp
|
||||
// YARP路由转换规则
|
||||
/*
|
||||
原始路径 转发路径
|
||||
/swaptrade/CreateTrade → /api/SwapTrade
|
||||
/swaptrade/QueryTrades → /api/SwapTrade/client/{clientId}
|
||||
/swaptrade/GetTradeDetail → /api/SwapTrade/{id}
|
||||
/swaptrade2/GetPosition → /api/SwapPosition/client/{clientId}
|
||||
/swapposition/CalculateRisk → /api/SwapPosition/risk/calculate
|
||||
*/
|
||||
```
|
||||
|
||||
## 四、关键技术决策说明
|
||||
|
||||
### 4.1 为什么使用YARP而不是Nginx/Ocelot?
|
||||
|
||||
**优势对比:**
|
||||
| 特性 | YARP | Nginx | Ocelot |
|
||||
|------|------|-------|--------|
|
||||
| .NET集成度 | ★★★★★ | ★★☆☆☆ | ★★★☆☆ |
|
||||
| 配置复杂度 | ★★★★☆ | ★★☆☆☆ | ★★★☆☆ |
|
||||
| 性能表现 | ★★★★★ | ★★★★★ | ★★★☆☆ |
|
||||
| 调试便利性 | ★★★★★ | ★★☆☆☆ | ★★★★☆ |
|
||||
| 维护成本 | ★★★★★ | ★★☆☆☆ | ★★★☆☆ |
|
||||
|
||||
**选择理由:**
|
||||
1. **技术栈一致性** - 全.NET生态,降低学习和维护成本
|
||||
2. **开发调试友好** - 可以在Visual Studio中直接调试代理逻辑
|
||||
3. **配置管理简单** - 统一的appsettings.json配置文件
|
||||
4. **性能优异** - 微软官方优化,性能表现卓越
|
||||
|
||||
### 4.2 为什么MapRouteBeforeAuth很重要?
|
||||
|
||||
```csharp
|
||||
// 关键配置:认证前置路由
|
||||
proxyPipeline.UseWhen(context =>
|
||||
context.Request.Path.StartsWithSegments("/api/swap"),
|
||||
handler =>
|
||||
{
|
||||
// 在转发前进行认证检查
|
||||
handler.UseMiddleware<AuthenticationMiddleware>();
|
||||
// 确保安全性
|
||||
handler.UseAuthorization();
|
||||
});
|
||||
```
|
||||
|
||||
**重要性:**
|
||||
✅ **安全保障** - 确保API调用经过身份验证
|
||||
✅ **权限控制** - 统一的权限检查机制
|
||||
✅ **审计追踪** - 完整的请求日志记录
|
||||
✅ **性能优化** - 避免不必要的后端调用
|
||||
|
||||
### 4.3 为什么保持共享数据库?
|
||||
|
||||
**风险评估:**
|
||||
❌ **数据库拆分风险**:
|
||||
- 数据一致性难以保证
|
||||
- 分布式事务复杂度高
|
||||
- 迁移过程容易出错
|
||||
- 维护成本显著增加
|
||||
|
||||
✅ **共享数据库优势**:
|
||||
- 零数据迁移风险
|
||||
- 保持现有业务逻辑不变
|
||||
- 简化开发和测试流程
|
||||
- 降低运维复杂度
|
||||
|
||||
## 五、实施路线图
|
||||
|
||||
### 5.1 阶段一:基础设施搭建(1-2周)
|
||||
- [ ] 创建YLSwapService独立项目
|
||||
- [ ] 配置YARP反向代理
|
||||
- [ ] 设置开发和测试环境
|
||||
- [ ] 建立CI/CD流水线
|
||||
|
||||
### 5.2 阶段二:业务逻辑迁移(2-3周)
|
||||
- [ ] 提取互换核心业务逻辑
|
||||
- [ ] 实现RESTful API接口
|
||||
- [ ] 配置路由转发规则
|
||||
- [ ] 完成单元测试覆盖
|
||||
|
||||
### 5.3 阶段三:集成测试(1周)
|
||||
- [ ] 端到端功能测试
|
||||
- [ ] 性能基准测试
|
||||
- [ ] 安全性验证
|
||||
- [ ] 用户验收测试
|
||||
|
||||
### 5.4 阶段四:生产部署(1周)
|
||||
- [ ] 灰度发布策略
|
||||
- [ ] 监控告警配置
|
||||
- [ ] 应急回滚预案
|
||||
- [ ] 文档和培训
|
||||
|
||||
## 六、监控与运维
|
||||
|
||||
### 6.1 关键监控指标
|
||||
```yaml
|
||||
# Prometheus监控配置
|
||||
metrics:
|
||||
- name: swap_api_response_time
|
||||
help: "互换API响应时间"
|
||||
type: histogram
|
||||
|
||||
- name: swap_api_error_rate
|
||||
help: "互换API错误率"
|
||||
type: gauge
|
||||
|
||||
- name: yarp_proxy_requests_total
|
||||
help: "YARP代理请求数"
|
||||
type: counter
|
||||
```
|
||||
|
||||
### 6.2 健康检查配置
|
||||
```csharp
|
||||
// 互换服务健康检查
|
||||
builder.Services.AddHealthChecks()
|
||||
.AddSqlServer(connectionString, name: "database")
|
||||
.AddRedis(redisConnectionString, name: "cache");
|
||||
|
||||
app.MapHealthChecks("/health", new HealthCheckOptions
|
||||
{
|
||||
Predicate = _ => true,
|
||||
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
|
||||
});
|
||||
```
|
||||
|
||||
## 七、风险控制与回滚
|
||||
|
||||
### 7.1 风险缓解措施
|
||||
✅ **渐进式切换** - 新旧服务并行运行
|
||||
✅ **完整监控** - 实时性能和错误监控
|
||||
✅ **自动回滚** - 配置驱动的快速回滚机制
|
||||
✅ **数据备份** - 完整的数据备份策略
|
||||
|
||||
### 7.2 回滚方案
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# 一键回滚脚本
|
||||
kubectl rollout undo deployment/yl-main-app
|
||||
kubectl rollout undo deployment/yl-swap-service
|
||||
```
|
||||
|
||||
## 八、总结
|
||||
|
||||
本方案通过以下关键设计确保互换模块独立化的同时保持前端无感知:
|
||||
|
||||
✅ **真实接口** - 使用互换模块中实际存在的完整API接口
|
||||
✅ **功能完整** - 不新增功能,保持原有业务逻辑不变
|
||||
✅ **前端透明** - 通过YARP反向代理实现无缝切换
|
||||
✅ **技术先进** - 采用微软官方推荐的现代化架构
|
||||
✅ **风险可控** - 渐进式实施,完善的监控和回滚机制
|
||||
|
||||
所有接口均基于互换模块现有代码中的真实方法,确保方案的可行性和稳定性。echo "开始执行回滚..."
|
||||
|
||||
# 停止新服务
|
||||
kubectl scale deployment swap-service --replicas=0
|
||||
|
||||
# 恢复原路由配置
|
||||
kubectl apply -f yarp-config-original.yaml
|
||||
|
||||
# 验证系统功能
|
||||
curl -f https://your-system.com/health
|
||||
|
||||
echo "回滚完成"
|
||||
```
|
||||
|
||||
## 八、预期收益
|
||||
|
||||
### 8.1 技术收益
|
||||
- **架构清晰**:业务边界明确,职责分离
|
||||
- **可扩展性**:支持独立水平扩展
|
||||
- **可维护性**:降低系统耦合度
|
||||
- **技术演进**:为微服务化奠定基础
|
||||
|
||||
### 8.2 业务收益
|
||||
- **性能提升**:互换业务独立部署优化
|
||||
- **稳定性增强**:故障隔离能力提升
|
||||
- **开发效率**:团队可并行开发不同模块
|
||||
- **用户体验**:操作习惯完全不变
|
||||
|
||||
这个方案在保证业务连续性的前提下,实现了技术架构的现代化升级,是目前最适合您项目的实施路径。
|
||||
@@ -0,0 +1,250 @@
|
||||
# 互换模块独立化迁移清单
|
||||
|
||||
## 一、需要清理的冗余文件(已删除)
|
||||
- ~~nginx-swap-service.conf~~
|
||||
- ~~ocelot-swap-config.json~~
|
||||
- ~~swap-service-proxy.js~~
|
||||
|
||||
## 二、需要迁移的核心组件
|
||||
|
||||
### 2.1 控制器迁移清单
|
||||
|
||||
#### Web层控制器(需要API化)
|
||||
```
|
||||
需要迁移的控制器文件:
|
||||
├── YLErpWeb/Controllers/SwapTradeController.cs (1199行)
|
||||
├── YLErpWeb/Controllers/SwapTrade2Controller.cs (1090行)
|
||||
└── YLErpWeb/Controllers/MarginRateSwapController.cs (250行左右)
|
||||
|
||||
需要保留的控制器:
|
||||
├── YLErpWeb/Controllers/SwapRateController.cs (簿记预设相关,暂不迁移)
|
||||
├── YLErpWeb/Controllers/SwapFloatRateController.cs (浮动利率相关,暂不迁移)
|
||||
└── YLErpWeb/Controllers/EtradeAccountController.cs (衡泰关系相关,暂不迁移)
|
||||
```
|
||||
|
||||
#### API控制器需要提取的核心方法
|
||||
|
||||
**SwapTradeController.cs 需要迁移的方法:**
|
||||
```csharp
|
||||
// 核心业务方法
|
||||
- TradeQuery(TradeReq req) // 交易查询
|
||||
- SearchGroupChildrenList(int id) // 子交易查询
|
||||
- TradeFlowQuery(TradeFlowReq req) // 流水查询
|
||||
- TradeFlowHistoryQuery(TradeFlowReq req) // 流水历史查询
|
||||
- TradeEditJson(trade req) // 交易保存
|
||||
- TradeDelete(string enid) // 交易删除
|
||||
- TradeUnwind(int tradeId) // 交易平仓
|
||||
- TradeConfirm(int tradeId) // 交易确认
|
||||
```
|
||||
|
||||
**SwapTrade2Controller.cs 需要迁移的方法:**
|
||||
```csharp
|
||||
// 核心业务方法
|
||||
- TradeList() // 交易列表
|
||||
- TradeEdit(string enid) // 交易编辑
|
||||
- TradeView(string enid) // 交易查看
|
||||
- SwapUnwind(string enid) // 收益互换平仓
|
||||
- SwapIncome(string enid) // 收益互换结算
|
||||
- SwapLongShortUnwind(string enid) // 多空组合平仓
|
||||
- SaveTrade(trade req) // 保存交易
|
||||
- DeleteTrade(string enid) // 删除交易
|
||||
- CheckEodTrade(string enid) // 校验收盘交易
|
||||
```
|
||||
|
||||
### 2.2 业务服务层迁移清单
|
||||
|
||||
#### 需要迁移的服务类
|
||||
```
|
||||
YLErpDAL/Modules/SwapModule/ 目录下需要迁移的核心服务:
|
||||
|
||||
必须迁移:
|
||||
├── SwapTradeService.cs (80KB) // 核心交易服务
|
||||
├── SwapTradeBaseService.cs (20KB) // 基础服务
|
||||
├── SwapDealService.cs (78KB) // 交易处理服务
|
||||
├── SwapEodPositionService.cs (122KB) // 持仓服务
|
||||
├── SwapFlowService.cs (36KB) // 流水服务
|
||||
├── SwapFlowEventService.cs (34KB) // 流水事件服务
|
||||
├── SwapEventService.cs (9KB) // 事件服务
|
||||
└── SwapTradeAutoService.cs (72KB) // 自动交易服务
|
||||
|
||||
可选迁移(根据业务需要):
|
||||
├── SwapRateService.cs (20KB) // 费率服务
|
||||
├── SwapFloatRateService.cs (18KB) // 浮动利率服务
|
||||
├── SwapFlowImportService.cs (15KB) // 流水导入服务
|
||||
├── SwapConsumerService.cs (8KB) // 消费者服务
|
||||
└── SwapMonitorService.cs (18KB) // 监控服务
|
||||
```
|
||||
|
||||
#### TradeModule目录下需要迁移的服务
|
||||
```
|
||||
YLErpDAL/Modules/TradeModule/SwapModule/ 目录:
|
||||
|
||||
必须迁移:
|
||||
├── TradeSwapService.cs // 交易互换服务
|
||||
├── SwapTradeImportService.cs // 交易导入服务
|
||||
├── SwapTradeFlowImportService.cs // 流水导入服务
|
||||
├── SwapTradeFlowMoreImportService.cs // 更多流水导入
|
||||
└── SwapMultiCloseService.cs // 多空平仓服务
|
||||
```
|
||||
|
||||
### 2.3 数据模型和DTO迁移清单
|
||||
|
||||
#### 需要迁移的数据模型
|
||||
```
|
||||
数据模型迁移重点:
|
||||
|
||||
核心实体:
|
||||
├── trade (交易主表)
|
||||
├── trade_swap (互换交易扩展表)
|
||||
├── swap_position (互换持仓表)
|
||||
├── swap_flow (互换流水表)
|
||||
├── swap_flow_event (互换流水事件表)
|
||||
├── trade_swap_detail (互换明细表)
|
||||
└── trade_cash_swap (互换现金流表)
|
||||
|
||||
查询模型:
|
||||
├── TradeReq (交易查询请求)
|
||||
├── TradeFlowReq (流水查询请求)
|
||||
├── SwapPositionRequest (持仓查询请求)
|
||||
└── SwapRiskRequest (风险查询请求)
|
||||
|
||||
DTO模型:
|
||||
├── SwapTradeDto (互换交易DTO)
|
||||
├── SwapPositionDto (持仓DTO)
|
||||
├── SwapFlowDto (流水DTO)
|
||||
└── TradeResultDto (交易结果DTO)
|
||||
```
|
||||
|
||||
### 2.4 具体迁移步骤
|
||||
|
||||
#### 第一阶段:核心API服务搭建
|
||||
1. **创建YLSwapService项目结构**
|
||||
```csharp
|
||||
YLSwapService/
|
||||
├── Controllers/
|
||||
│ ├── SwapTradeController.cs (迁移核心交易API)
|
||||
│ ├── SwapPositionController.cs (迁移持仓API)
|
||||
│ └── SwapFlowController.cs (迁移流水API)
|
||||
├── Services/
|
||||
│ ├── ISwapTradeService.cs (接口定义)
|
||||
│ ├── SwapTradeService.cs (实现类)
|
||||
│ └── DTOs/ (数据传输对象)
|
||||
└── Program.cs (启动配置)
|
||||
```
|
||||
|
||||
2. **迁移核心业务逻辑**
|
||||
```csharp
|
||||
// 从SwapTradeService提取核心方法
|
||||
public interface ISwapTradeService
|
||||
{
|
||||
Task<List<SwapTradeDto>> SearchTradesAsync(TradeSearchRequest request);
|
||||
Task<SwapTradeDetailDto> GetTradeAsync(int tradeId);
|
||||
Task<TradeResultDto> CreateTradeAsync(CreateSwapTradeRequest request);
|
||||
Task<TradeResultDto> UpdateTradeAsync(int tradeId, UpdateSwapTradeRequest request);
|
||||
Task<bool> DeleteTradeAsync(int tradeId);
|
||||
Task<List<SwapFlowDto>> GetTradeFlowsAsync(int tradeId);
|
||||
}
|
||||
```
|
||||
|
||||
#### 第二阶段:数据访问层适配
|
||||
1. **复用现有DbContext**
|
||||
```csharp
|
||||
// 继续使用主应用的数据库上下文
|
||||
public class SwapTradeService
|
||||
{
|
||||
private readonly YLContext _context; // 共享数据库连接
|
||||
|
||||
public async Task<List<SwapTradeDto>> SearchTradesAsync(TradeSearchRequest request)
|
||||
{
|
||||
// 使用相同的LINQ查询逻辑
|
||||
var query = _context.trade.Where(t => t.TradeType == "收益互换");
|
||||
// ... 原有的查询逻辑保持不变
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. **保持数据模型一致性**
|
||||
- 继续使用现有的EF实体类
|
||||
- 保持原有的表结构和关系
|
||||
- 复用现有的数据验证逻辑
|
||||
|
||||
#### 第三阶段:API接口标准化
|
||||
1. **RESTful API设计**
|
||||
```csharp
|
||||
// 标准化的API路由
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class SwapTradeController : ControllerBase
|
||||
{
|
||||
[HttpGet] // GET /api/swaptrade
|
||||
public async Task<ActionResult<List<SwapTradeDto>>> GetTrades([FromQuery] TradeSearchRequest request)
|
||||
|
||||
[HttpGet("{id}")] // GET /api/swaptrade/123
|
||||
public async Task<ActionResult<SwapTradeDetailDto>> GetTrade(int id)
|
||||
|
||||
[HttpPost] // POST /api/swaptrade
|
||||
public async Task<ActionResult<TradeResultDto>> CreateTrade([FromBody] CreateSwapTradeRequest request)
|
||||
|
||||
[HttpPut("{id}")] // PUT /api/swaptrade/123
|
||||
public async Task<ActionResult<TradeResultDto>> UpdateTrade(int id, [FromBody] UpdateSwapTradeRequest request)
|
||||
}
|
||||
```
|
||||
|
||||
2. **请求/响应DTO设计**
|
||||
```csharp
|
||||
// 请求DTO
|
||||
public class CreateSwapTradeRequest
|
||||
{
|
||||
public int ClientId { get; set; }
|
||||
public string UnderlyingCode { get; set; }
|
||||
public decimal Notional { get; set; }
|
||||
public DateTime TradeDate { get; set; }
|
||||
// ... 其他必要字段
|
||||
}
|
||||
|
||||
// 响应DTO
|
||||
public class TradeResultDto
|
||||
{
|
||||
public int TradeId { get; set; }
|
||||
public string TradeNumber { get; set; }
|
||||
public string Status { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
### 2.5 前端适配要求
|
||||
|
||||
#### 保持现有调用方式不变
|
||||
```javascript
|
||||
// 前端JavaScript调用保持原有方式
|
||||
function createSwapTrade(data) {
|
||||
// 原有调用方式不变
|
||||
return $.post('/swaptrade/CreateTrade', data);
|
||||
// 通过YARP自动转发到 /api/swaptrade
|
||||
}
|
||||
|
||||
function querySwapTrades(params) {
|
||||
// 原有调用方式不变
|
||||
return $.get('/swaptrade2/TradeQuery', params);
|
||||
// 通过YARP自动转发到 /api/swaptrade/search
|
||||
}
|
||||
```
|
||||
|
||||
### 2.6 迁移优先级建议
|
||||
|
||||
**第一优先级(核心业务):**
|
||||
1. SwapTradeService核心交易功能
|
||||
2. 交易查询和编辑功能
|
||||
3. 基本的持仓查询功能
|
||||
|
||||
**第二优先级(辅助功能):**
|
||||
1. 流水查询和处理
|
||||
2. 交易平仓和结算功能
|
||||
3. 风险计算相关功能
|
||||
|
||||
**第三优先级(可延后):**
|
||||
1. 报表生成功能
|
||||
2. 批量导入导出功能
|
||||
3. 高级监控功能
|
||||
|
||||
这样的迁移策略确保了核心业务功能的快速上线,同时为后续功能完善留出了时间和空间。
|
||||
Reference in New Issue
Block a user