feat(swap): 更新互换模块独立化方案文档

- 完善互换模块现状分析,明确新老表结构对比
- 更新核心架构原则,确定完全舍弃老表结构
- 优化业务逻辑实现,基于trade表和swap_position表构建
- 增强数据模型验证,添加必要的数据注解
- 完善异常处理机制,改进错误日志记录
- 重构API服务设计,实现完整的互换交易流程
- 更新实施路线图,明确各阶段任务优先级
- 删除冗余的迁移清单文档内容
This commit is contained in:
hjhan
2026-02-13 10:51:45 +08:00
parent 7f0f15ceaa
commit 497371ee97
2 changed files with 172 additions and 299 deletions
+172 -49
View File
@@ -11,16 +11,34 @@
### 1.2 互换模块现状
```
互换相关组件:
├── Controllers (SwapTradeController.cs, SwapTrade2Controller.cs)
├── Controllers (SwapTrade2Controller.cs - 现有主要控制器)
├── Views (40+个.cshtml文件,包括复杂表单和报表)
├── JavaScript (大量业务逻辑嵌入在页面脚本中)
├── Business Logic (SwapModule业务服务层)
└── Data Access (直接使用主应用DbContext)
表结构现状:
❌ 已废弃的老互换表:
├── trade_swap(互换交易主表)
├── trade_swap_flow(互换交易流水表)
└── trade_swap_flow_more(互换交易流水扩展表)
✅ 当前使用的现代化表结构:
├── trade(通用交易表,包含收益互换类型)
├── swap_flow(互换流水明细表)
├── swap_position(互换持仓表)
├── swap_flow_event(互换流水事件表)
├── swap_flow_merge(互换流水合并表)
├── swap_event(互换事件表)
├── eod_swap(互换日终表)
└── eod_swap_position(互换日终持仓归档表)
```
## 二、最终方案设计
### 2.1 核心架构原则
**完全舍弃老表结构** - 不再使用trade_swap系列废弃表
**基于新架构开发** - 围绕trade和swap_position等现代表构建
**前端零改动** - 保持所有.cshtml页面和JavaScript逻辑不变
**后端独立化** - 互换业务逻辑抽取为独立API服务
**共享数据库** - 继续使用现有数据库,避免数据迁移风险
@@ -162,13 +180,21 @@ public class SwapTradeService : ISwapTradeService
private readonly YLContext _context;
private readonly ILogger<SwapTradeService> _logger;
public SwapTradeService(YLContext context, ILogger<SwapTradeService> logger)
{
_context = context;
_logger = logger;
}
public async Task<TradeResultDto> CreateTradeAsync(CreateSwapTradeRequestDto request)
{
using var transaction = await _context.Database.BeginTransactionAsync();
try
{
// 业务逻辑处理 - 与原系统保持一致
var tradeNumber = GenerateTradeNumber();
// 基于新架构的业务逻辑 - 使用trade表和swap_position表
var tradeNumber = await GenerateTradeNumberAsync();
// 创建主交易记录
var trade = new trade
{
TradeNumber = tradeNumber,
@@ -193,6 +219,26 @@ public class SwapTradeService : ISwapTradeService
_context.trade.Add(trade);
await _context.SaveChangesAsync();
// 创建互换持仓记录
var swapPosition = new swap_position
{
SwapTradeId = trade.id,
PositionType = request.PositionType, // 1:多头, 2:空头
UnderlyingCode = request.UnderlyingCode,
PosiQuantity = request.Quantity,
PosiNotionalValue = request.Notional,
PosiNetPrice = request.SpotPrice,
PosiStartDate = request.StartDate,
PosiMatuirityDate = request.ExerciseDate,
IsInitial = true,
OptId = GetCurrentUserId(),
OptName = GetCurrentUserName(),
OptTime = DateTime.Now
};
_context.swap_position.Add(swapPosition);
await _context.SaveChangesAsync();
await transaction.CommitAsync();
return new TradeResultDto
@@ -200,16 +246,49 @@ public class SwapTradeService : ISwapTradeService
TradeId = trade.id,
TradeNumber = tradeNumber,
Status = "成功",
Message = "交易创建成功"
Message = "互换交易创建成功"
};
}
catch (Exception ex)
{
await transaction.RollbackAsync();
_logger.LogError(ex, "创建互换交易失败");
throw;
_logger.LogError(ex, "创建互换交易失败: {Message}", ex.Message);
throw new BusinessException("创建互换交易失败", ex);
}
}
public async Task<List<SwapTradeDto>> GetClientTradesAsync(int clientId, string status = "")
{
var query = _context.trade
.Where(t => t.ClientId == clientId
&& t.TradeType == "收益互换"
&& t.ValidState == "Valid");
if (!string.IsNullOrEmpty(status))
{
query = query.Where(t => t.TradeStatus == status);
}
var trades = await query
.OrderByDescending(t => t.TradeDate)
.ToListAsync();
return trades.Select(t => new SwapTradeDto
{
Id = t.id,
TradeNumber = t.TradeNumber,
ClientId = t.ClientId,
ClientName = t.ClientName,
UnderlyingCode = t.UnderlyingCode,
TradeDate = t.TradeDate.Value,
StartDate = t.StartDate.Value,
ExerciseDate = t.ExerciseDate.Value,
Notional = t.Notional ?? 0,
SpotPrice = t.SpotPrice ?? 0,
TradeStatus = t.TradeStatus,
CreatedAt = t.OptDate.Value
}).ToList();
}
}
```
@@ -220,21 +299,46 @@ public class SwapTradeService : ISwapTradeService
// 创建互换交易请求DTO
public class CreateSwapTradeRequestDto
{
[Required]
public int ClientId { get; set; }
[Required]
public string ClientName { get; set; } = string.Empty;
[Required]
public string UnderlyingCode { get; set; } = string.Empty;
[Required]
public DateTime TradeDate { get; set; }
[Required]
public DateTime StartDate { get; set; }
[Required]
public DateTime ExerciseDate { get; set; }
[Required]
[Range(0.01, double.MaxValue)]
public decimal Notional { get; set; }
[Required]
[Range(0.01, double.MaxValue)]
public decimal SpotPrice { get; set; }
[Required]
public string AssetBookName { get; set; } = string.Empty;
[Required]
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; }
[Required]
[Range(1, 2)]
public int PositionType { get; set; } // 1:多头, 2:空头
[Required]
[Range(0.01, double.MaxValue)]
public decimal Quantity { get; set; }
public string MetaData { get; set; } = "{}";
}
@@ -463,7 +567,47 @@ proxyPipeline.UseWhen(context =>
**审计追踪** - 完整的请求日志记录
**性能优化** - 避免不必要的后端调用
### 4.3 为什么保持共享数据库?
### 4.3 新架构表结构详解
**核心表关系说明:**
```
trade表 (主交易表)
├── TradeType = "收益互换" (标识互换交易)
├── id (主键,关联其他表)
└── 基础交易信息字段
swap_flow表 (互换流水明细表)
├── TradeId → trade.id (外键关联)
├── ClientId (客户ID)
├── UnderlyingCode (标的代码)
├── TradingQty (成交数量)
├── TradingAmount (成交金额)
├── TradingFee (交易费用)
└── TradeDate (交易日期)
swap_position表 (互换持仓表)
├── SwapTradeId → trade.id (外键关联)
├── PositionType (1:多头, 2:空头)
├── UnderlyingCode (标的代码)
├── PosiQuantity (持仓数量)
├── PosiNotionalValue (名义本金)
└── IsInitial (是否期初持仓)
swap_flow_event表 (流水事件表)
├── SwapTradeId → trade.id (外键关联)
├── PositionId → swap_position.id (外键关联)
├── EventType (1:开仓, 2:平仓)
├── EventDate (事件日期)
└── 数量和金额相关信息
eod_swap_position表 (日终持仓归档)
├── SwapTradeId → trade.id (外键关联)
├── PositionId → swap_position.id (外键关联)
├── ValueDate (归档日期)
└── 完整的持仓估值信息
```
### 4.4 为什么保持共享数据库?
**风险评估:**
**数据库拆分风险**
@@ -478,9 +622,10 @@ proxyPipeline.UseWhen(context =>
- 简化开发和测试流程
- 降低运维复杂度
## 、实施路线图
## 、实施路线图
### 5.1 阶段一:基础设施搭建(1-2周)
- [x] 确认完全舍弃老互换表结构(trade_swap系列)
- [ ] 创建YLSwapService独立项目
- [ ] 配置YARP反向代理
- [ ] 设置开发和测试环境
@@ -504,7 +649,7 @@ proxyPipeline.UseWhen(context =>
- [ ] 应急回滚预案
- [ ] 文档和培训
## 、监控与运维
## 、监控与运维
### 6.1 关键监控指标
```yaml
@@ -537,7 +682,7 @@ app.MapHealthChecks("/health", new HealthCheckOptions
});
```
## 、风险控制与回滚
## 、风险控制与回滚
### 7.1 风险缓解措施
**渐进式切换** - 新旧服务并行运行
@@ -553,42 +698,20 @@ kubectl rollout undo deployment/yl-main-app
kubectl rollout undo deployment/yl-swap-service
```
## 、总结
## 、总结
本方案通过以下关键设计确保互换模块独立化的同时保持前端无感知:
**真实接口** - 使用互换模块中实际存在的完整API接口
**功能完整** - 不新增功能,保持原有业务逻辑不变
**前端透明** - 通过YARP反向代理实现无缝切换
**技术先进** - 采用微软官方推荐的现代化架构
**风险可控** - 渐进式实施,完善的监控和回滚机制
**彻底现代化** - 完全基于新架构,舍弃所有老互换表结构
**零业务中断** - 前端用户无感知,操作习惯完全不变
**技术先进性** - 采用微软官方推荐的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 业务收益
- **性能提升**:互换业务独立部署优化
- **稳定性增强**:故障隔离能力提升
- **开发效率**:团队可并行开发不同模块
- **用户体验**:操作习惯完全不变
这个方案在保证业务连续性的前提下,实现了技术架构的现代化升级,是目前最适合您项目的实施路径。
**特别说明:**
- **swap_flow表**作为互换流水明细的核心表,维护着每笔交易的详细流水信息,包括成交数量、金额、费用等关键数据
- 与**swap_position表**密切配合,实现完整的持仓管理和流水追踪
- 是连接前端交易录入和后端风险计算的重要数据桥梁
- **trade表**作为通用交易表,统一管理包括收益互换在内的所有交易类型
-250
View File
@@ -1,250 +0,0 @@
# 互换模块独立化迁移清单
## 一、需要清理的冗余文件(已删除)
- ~~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. 高级监控功能
这样的迁移策略确保了核心业务功能的快速上线,同时为后续功能完善留出了时间和空间。