上传源代码版本

This commit is contained in:
Im-Jenisson
2026-06-01 16:30:29 +08:00
commit b2a9b7d3c2
462 changed files with 104365 additions and 0 deletions

653
OrderLogManagementDesign.md Normal file
View File

@@ -0,0 +1,653 @@
# 订单日志管理系统设计方案
## 1. 需求分析
### 1.1 业务背景
label_replace_requests 是一个换单请求的订单表,用于存储标签替换请求的相关信息。为了更好地追踪和管理订单的操作历史,需要建立一个订单日志管理表,用于记录订单所有的操作和操作结果。
### 1.2 功能需求
- 记录订单的所有操作,包括换单、集包、数据更新、下单、取消订单等
- 记录操作结果,如成功、失败
- 记录详细的操作说明,如每次集包的关联袋牌信息、数据更新的具体内容等
- 支持按订单号、尾程单号、操作类型、操作结果等条件查询日志
- 支持日志的分页查询和管理
## 2. 技术方案
### 2.1 数据库设计
#### 2.1.1 表结构设计
**表名order_logs**
| 字段名 | 数据类型 | 约束 | 描述 |
|--------|----------|------|------|
| Id | BIGINT UNSIGNED | PRIMARY KEY, AUTO_INCREMENT | 主键ID自增 |
| NeutralWaybillNumber | VARCHAR(100) | NOT NULL | 中性面单单号(必填) |
| FinalMileTrackingNumber | VARCHAR(100) | NULL | 尾程跟踪单号 |
| OperationType | VARCHAR(50) | NOT NULL | 操作类型(换单、集包、数据更新、下单、取消订单等) |
| OperationResult | VARCHAR(20) | NOT NULL | 操作结果(成功、失败) |
| OperationDescription | TEXT | NOT NULL | 操作说明(详细描述操作内容) |
| Operator | VARCHAR(100) | NULL | 操作人 |
| CreatedAt | DATETIME | NOT NULL | 操作时间 |
#### 2.1.2 索引设计
- `idx_neutral_waybill_number`:中性面单单号索引,用于快速查询特定订单的所有操作日志
- `idx_final_mile_tracking_number`:尾程跟踪单号索引,用于通过尾程单号查询日志
- `idx_operation_type`:操作类型索引,用于按操作类型查询日志
- `idx_operation_result`:操作结果索引,用于按操作结果查询日志
- `idx_created_at`:操作时间索引,用于按时间范围查询日志和排序
#### 2.1.3 关联关系
- 移除了与 `label_replace_requests` 表的外键关联
- 通过 `NeutralWaybillNumber``FinalMileTrackingNumber` 与订单表建立逻辑关联
### 2.2 代码实现
#### 2.2.1 枚举定义
```csharp
// MDL/Enums/OrderLogEnums.cs
using System;
namespace MDL.Enums
{
/// <summary>
/// 订单日志操作类型枚举
/// </summary>
public static class OrderLogOperationType
{
/// <summary>
/// 换单
/// </summary>
public const string REPLACE = "换单";
/// <summary>
/// 集包
/// </summary>
public const string PACK = "集包";
/// <summary>
/// 数据更新
/// </summary>
public const string UPDATE = "数据更新";
/// <summary>
/// 下单
/// </summary>
public const string CREATE_ORDER = "下单";
/// <summary>
/// 取消订单
/// </summary>
public const string CANCEL_ORDER = "取消订单";
}
/// <summary>
/// 订单日志操作结果枚举
/// </summary>
public static class OrderLogOperationResult
{
/// <summary>
/// 成功
/// </summary>
public const string SUCCESS = "成功";
/// <summary>
/// 失败
/// </summary>
public const string FAILED = "失败";
}
}
```
#### 2.2.2 实体类
```csharp
// MDL/Models/OrderLogEntity.cs
using System;
namespace MDL.Models
{
/// <summary>
/// 订单日志实体类
/// </summary>
public class OrderLogEntity
{
/// <summary>
/// 主键ID自增
/// </summary>
public long Id { get; set; }
/// <summary>
/// 中性面单单号
/// </summary>
public string NeutralWaybillNumber { get; set; }
/// <summary>
/// 尾程跟踪单号
/// </summary>
public string FinalMileTrackingNumber { get; set; }
/// <summary>
/// 操作类型(换单、集包、数据更新、下单、取消订单等)
/// </summary>
public string OperationType { get; set; }
/// <summary>
/// 操作结果(成功、失败)
/// </summary>
public string OperationResult { get; set; }
/// <summary>
/// 操作说明(详细描述操作内容)
/// </summary>
public string OperationDescription { get; set; }
/// <summary>
/// 操作人
/// </summary>
public string Operator { get; set; }
/// <summary>
/// 操作时间
/// </summary>
public DateTime CreatedAt { get; set; }
}
}
```
#### 2.2.3 仓储接口
```csharp
// DAL/Interfaces/IOrderLogRepository.cs
using System.Collections.Generic;
using System.Threading.Tasks;
using MDL.Models;
namespace DAL.Interfaces
{
/// <summary>
/// 订单日志仓储接口
/// </summary>
public interface IOrderLogRepository
{
/// <summary>
/// 插入订单日志
/// </summary>
/// <param name="log">订单日志实体</param>
/// <returns>插入是否成功</returns>
Task<bool> InsertOrderLogAsync(OrderLogEntity log);
/// <summary>
/// 根据中性面单单号获取订单日志列表
/// </summary>
/// <param name="neutralWaybillNumber">中性面单单号</param>
/// <returns>订单日志实体列表</returns>
Task<List<OrderLogEntity>> GetOrderLogsByWaybillNumberAsync(string neutralWaybillNumber);
/// <summary>
/// 根据尾程跟踪单号获取订单日志列表
/// </summary>
/// <param name="finalMileTrackingNumber">尾程跟踪单号</param>
/// <returns>订单日志实体列表</returns>
Task<List<OrderLogEntity>> GetOrderLogsByTrackingNumberAsync(string finalMileTrackingNumber);
/// <summary>
/// 获取所有订单日志列表(分页)
/// </summary>
/// <param name="pageIndex">页码</param>
/// <param name="pageSize">每页大小</param>
/// <returns>订单日志实体列表</returns>
Task<List<OrderLogEntity>> GetAllOrderLogsAsync(int pageIndex, int pageSize);
/// <summary>
/// 根据操作类型和操作结果获取订单日志列表
/// </summary>
/// <param name="operationType">操作类型</param>
/// <param name="operationResult">操作结果</param>
/// <param name="pageIndex">页码</param>
/// <param name="pageSize">每页大小</param>
/// <returns>订单日志实体列表</returns>
Task<List<OrderLogEntity>> GetOrderLogsByOperationAsync(string operationType, string operationResult, int pageIndex, int pageSize);
}
}
```
#### 2.2.4 仓储实现
```csharp
// DAL/Repositories/OrderLogRepository.cs
using System.Collections.Generic;
using System.Threading.Tasks;
using MDL.Models;
using DAL.Interfaces;
using SqlSugar;
namespace DAL.Repositories
{
/// <summary>
/// 订单日志仓储实现类
/// </summary>
public class OrderLogRepository : IOrderLogRepository
{
private readonly ISqlSugarClient _db;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="db">SqlSugar客户端</param>
public OrderLogRepository(ISqlSugarClient db)
{
_db = db;
}
/// <summary>
/// 插入订单日志
/// </summary>
/// <param name="log">订单日志实体</param>
/// <returns>插入是否成功</returns>
public async Task<bool> InsertOrderLogAsync(OrderLogEntity log)
{
var result = await _db.Insertable(log).ExecuteCommandAsync();
return result > 0;
}
/// <summary>
/// 根据中性面单单号获取订单日志列表
/// </summary>
/// <param name="neutralWaybillNumber">中性面单单号</param>
/// <returns>订单日志实体列表</returns>
public async Task<List<OrderLogEntity>> GetOrderLogsByWaybillNumberAsync(string neutralWaybillNumber)
{
return await _db.Queryable<OrderLogEntity>()
.Where(x => x.NeutralWaybillNumber == neutralWaybillNumber)
.OrderBy(x => x.CreatedAt)
.ToListAsync();
}
/// <summary>
/// 根据尾程跟踪单号获取订单日志列表
/// </summary>
/// <param name="finalMileTrackingNumber">尾程跟踪单号</param>
/// <returns>订单日志实体列表</returns>
public async Task<List<OrderLogEntity>> GetOrderLogsByTrackingNumberAsync(string finalMileTrackingNumber)
{
return await _db.Queryable<OrderLogEntity>()
.Where(x => x.FinalMileTrackingNumber == finalMileTrackingNumber)
.OrderBy(x => x.CreatedAt)
.ToListAsync();
}
/// <summary>
/// 获取所有订单日志列表(分页)
/// </summary>
/// <param name="pageIndex">页码</param>
/// <param name="pageSize">每页大小</param>
/// <returns>订单日志实体列表</returns>
public async Task<List<OrderLogEntity>> GetAllOrderLogsAsync(int pageIndex, int pageSize)
{
return await _db.Queryable<OrderLogEntity>()
.OrderBy(x => x.CreatedAt, OrderByType.Desc)
.ToPageListAsync(pageIndex, pageSize);
}
/// <summary>
/// 根据操作类型和操作结果获取订单日志列表
/// </summary>
/// <param name="operationType">操作类型</param>
/// <param name="operationResult">操作结果</param>
/// <param name="pageIndex">页码</param>
/// <param name="pageSize">每页大小</param>
/// <returns>订单日志实体列表</returns>
public async Task<List<OrderLogEntity>> GetOrderLogsByOperationAsync(string operationType, string operationResult, int pageIndex, int pageSize)
{
var query = _db.Queryable<OrderLogEntity>();
if (!string.IsNullOrEmpty(operationType))
{
query = query.Where(x => x.OperationType == operationType);
}
if (!string.IsNullOrEmpty(operationResult))
{
query = query.Where(x => x.OperationResult == operationResult);
}
return await query
.OrderBy(x => x.CreatedAt, OrderByType.Desc)
.ToPageListAsync(pageIndex, pageSize);
}
}
}
```
#### 2.2.5 服务接口
```csharp
// BLL/Interfaces/IOrderLogService.cs
using System.Collections.Generic;
using System.Threading.Tasks;
using MDL.Models;
namespace BLL.Interfaces
{
/// <summary>
/// 订单日志服务接口
/// </summary>
public interface IOrderLogService
{
/// <summary>
/// 记录订单操作日志
/// </summary>
/// <param name="neutralWaybillNumber">中性面单单号</param>
/// <param name="finalMileTrackingNumber">尾程跟踪单号</param>
/// <param name="operationType">操作类型</param>
/// <param name="operationResult">操作结果</param>
/// <param name="operationDescription">操作说明</param>
/// <param name="operator">操作人</param>
/// <returns>记录是否成功</returns>
Task<bool> RecordOrderLogAsync(string neutralWaybillNumber, string finalMileTrackingNumber, string operationType, string operationResult, string operationDescription, string @operator = null);
/// <summary>
/// 根据中性面单单号获取订单日志列表
/// </summary>
/// <param name="neutralWaybillNumber">中性面单单号</param>
/// <returns>订单日志实体列表</returns>
Task<List<OrderLogEntity>> GetOrderLogsByWaybillNumberAsync(string neutralWaybillNumber);
/// <summary>
/// 根据尾程跟踪单号获取订单日志列表
/// </summary>
/// <param name="finalMileTrackingNumber">尾程跟踪单号</param>
/// <returns>订单日志实体列表</returns>
Task<List<OrderLogEntity>> GetOrderLogsByTrackingNumberAsync(string finalMileTrackingNumber);
/// <summary>
/// 获取所有订单日志列表(分页)
/// </summary>
/// <param name="pageIndex">页码</param>
/// <param name="pageSize">每页大小</param>
/// <returns>订单日志实体列表</returns>
Task<List<OrderLogEntity>> GetAllOrderLogsAsync(int pageIndex, int pageSize);
/// <summary>
/// 根据操作类型和操作结果获取订单日志列表
/// </summary>
/// <param name="operationType">操作类型</param>
/// <param name="operationResult">操作结果</param>
/// <param name="pageIndex">页码</param>
/// <param name="pageSize">每页大小</param>
/// <returns>订单日志实体列表</returns>
Task<List<OrderLogEntity>> GetOrderLogsByOperationAsync(string operationType, string operationResult, int pageIndex, int pageSize);
}
}
```
#### 2.2.6 服务实现
```csharp
// BLL/Services/OrderLogService.cs
using System.Collections.Generic;
using System.Threading.Tasks;
using MDL.Models;
using BLL.Interfaces;
using DAL.Interfaces;
namespace BLL.Services
{
/// <summary>
/// 订单日志服务实现类
/// </summary>
public class OrderLogService : IOrderLogService
{
private readonly IOrderLogRepository _orderLogRepository;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="orderLogRepository">订单日志仓储</param>
public OrderLogService(IOrderLogRepository orderLogRepository)
{
_orderLogRepository = orderLogRepository;
}
/// <summary>
/// 记录订单操作日志
/// </summary>
/// <param name="neutralWaybillNumber">中性面单单号</param>
/// <param name="finalMileTrackingNumber">尾程跟踪单号</param>
/// <param name="operationType">操作类型</param>
/// <param name="operationResult">操作结果</param>
/// <param name="operationDescription">操作说明</param>
/// <param name="operator">操作人</param>
/// <returns>记录是否成功</returns>
public async Task<bool> RecordOrderLogAsync(string neutralWaybillNumber, string finalMileTrackingNumber, string operationType, string operationResult, string operationDescription, string @operator = null)
{
var log = new OrderLogEntity
{
NeutralWaybillNumber = neutralWaybillNumber,
FinalMileTrackingNumber = finalMileTrackingNumber,
OperationType = operationType,
OperationResult = operationResult,
OperationDescription = operationDescription,
Operator = @operator,
CreatedAt = System.DateTime.Now
};
return await _orderLogRepository.InsertOrderLogAsync(log);
}
/// <summary>
/// 根据中性面单单号获取订单日志列表
/// </summary>
/// <param name="neutralWaybillNumber">中性面单单号</param>
/// <returns>订单日志实体列表</returns>
public async Task<List<OrderLogEntity>> GetOrderLogsByWaybillNumberAsync(string neutralWaybillNumber)
{
return await _orderLogRepository.GetOrderLogsByWaybillNumberAsync(neutralWaybillNumber);
}
/// <summary>
/// 根据尾程跟踪单号获取订单日志列表
/// </summary>
/// <param name="finalMileTrackingNumber">尾程跟踪单号</param>
/// <returns>订单日志实体列表</returns>
public async Task<List<OrderLogEntity>> GetOrderLogsByTrackingNumberAsync(string finalMileTrackingNumber)
{
return await _orderLogRepository.GetOrderLogsByTrackingNumberAsync(finalMileTrackingNumber);
}
/// <summary>
/// 获取所有订单日志列表(分页)
/// </summary>
/// <param name="pageIndex">页码</param>
/// <param name="pageSize">每页大小</param>
/// <returns>订单日志实体列表</returns>
public async Task<List<OrderLogEntity>> GetAllOrderLogsAsync(int pageIndex, int pageSize)
{
return await _orderLogRepository.GetAllOrderLogsAsync(pageIndex, pageSize);
}
/// <summary>
/// 根据操作类型和操作结果获取订单日志列表
/// </summary>
/// <param name="operationType">操作类型</param>
/// <param name="operationResult">操作结果</param>
/// <param name="pageIndex">页码</param>
/// <param name="pageSize">每页大小</param>
/// <returns>订单日志实体列表</returns>
public async Task<List<OrderLogEntity>> GetOrderLogsByOperationAsync(string operationType, string operationResult, int pageIndex, int pageSize)
{
return await _orderLogRepository.GetOrderLogsByOperationAsync(operationType, operationResult, pageIndex, pageSize);
}
}
}
```
### 2.3 依赖注入配置
需要在依赖注入容器中注册订单日志相关的服务和仓储:
```csharp
// 注册订单日志仓储
services.AddScoped<IOrderLogRepository, OrderLogRepository>();
// 注册订单日志服务
services.AddScoped<IOrderLogService, OrderLogService>();
```
## 3. 使用示例
### 3.1 记录下单操作日志
```csharp
// 注入订单日志服务
private readonly IOrderLogService _orderLogService;
public async Task CreateOrderAsync(string neutralWaybillNumber, string finalMileTrackingNumber)
{
// 处理下单逻辑
bool success = await ProcessCreateOrder(neutralWaybillNumber, finalMileTrackingNumber);
// 记录下单操作日志
await _orderLogService.RecordOrderLogAsync(
neutralWaybillNumber: neutralWaybillNumber,
finalMileTrackingNumber: finalMileTrackingNumber,
operationType: OrderLogOperationType.CREATE_ORDER,
operationResult: success ? OrderLogOperationResult.SUCCESS : OrderLogOperationResult.FAILED,
operationDescription: success ? $"客户在 {DateTime.Now} 下单成功" : $"客户在 {DateTime.Now} 下单失败",
@operator: "Customer"
);
}
```
### 3.2 记录取消订单操作日志
```csharp
public async Task CancelOrderAsync(string neutralWaybillNumber, string finalMileTrackingNumber)
{
// 处理取消订单逻辑
bool success = await ProcessCancelOrder(neutralWaybillNumber, finalMileTrackingNumber);
// 记录取消订单操作日志
await _orderLogService.RecordOrderLogAsync(
neutralWaybillNumber: neutralWaybillNumber,
finalMileTrackingNumber: finalMileTrackingNumber,
operationType: OrderLogOperationType.CANCEL_ORDER,
operationResult: success ? OrderLogOperationResult.SUCCESS : OrderLogOperationResult.FAILED,
operationDescription: success ? $"客户在 {DateTime.Now} 取消订单成功" : $"客户在 {DateTime.Now} 取消订单失败",
@operator: "Customer"
);
}
```
### 3.3 记录换单操作日志
```csharp
public async Task ProcessLabelReplaceAsync(string neutralWaybillNumber, string finalMileTrackingNumber)
{
// 处理换单逻辑
bool success = await ProcessReplace(neutralWaybillNumber, finalMileTrackingNumber);
// 记录换单操作日志
await _orderLogService.RecordOrderLogAsync(
neutralWaybillNumber: neutralWaybillNumber,
finalMileTrackingNumber: finalMileTrackingNumber,
operationType: OrderLogOperationType.REPLACE,
operationResult: success ? OrderLogOperationResult.SUCCESS : OrderLogOperationResult.FAILED,
operationDescription: success ? "换单成功" : "换单失败",
@operator: "System"
);
}
```
### 3.4 记录集包操作日志
```csharp
public async Task ProcessPackAsync(string neutralWaybillNumber, string bagTag)
{
// 处理集包逻辑
bool success = await ProcessPacking(neutralWaybillNumber, bagTag);
// 记录集包操作日志
await _orderLogService.RecordOrderLogAsync(
neutralWaybillNumber: neutralWaybillNumber,
finalMileTrackingNumber: null, // 可能没有尾程单号
operationType: OrderLogOperationType.PACK,
operationResult: success ? OrderLogOperationResult.SUCCESS : OrderLogOperationResult.FAILED,
operationDescription: success ? $"关联袋牌 {bagTag} 成功" : $"关联袋牌 {bagTag} 失败",
@operator: "System"
);
}
```
### 3.5 记录数据更新操作日志
```csharp
public async Task UpdateBillOfLadingAsync(string neutralWaybillNumber, string billOfLadingNumber)
{
// 处理数据更新逻辑
bool success = await UpdateBillOfLading(neutralWaybillNumber, billOfLadingNumber);
// 记录数据更新操作日志
await _orderLogService.RecordOrderLogAsync(
neutralWaybillNumber: neutralWaybillNumber,
finalMileTrackingNumber: null, // 可能没有尾程单号
operationType: OrderLogOperationType.UPDATE,
operationResult: success ? OrderLogOperationResult.SUCCESS : OrderLogOperationResult.FAILED,
operationDescription: success ? "更新提单号成功" : "更新提单号失败",
@operator: "System"
);
}
```
### 3.6 查询订单日志
```csharp
// 根据中性面单单号查询订单日志
var logs = await _orderLogService.GetOrderLogsByWaybillNumberAsync("NW1234567890");
// 根据尾程跟踪单号查询订单日志
var logs = await _orderLogService.GetOrderLogsByTrackingNumberAsync("FM1234567890");
// 分页查询所有订单日志
var logs = await _orderLogService.GetAllOrderLogsAsync(pageIndex: 1, pageSize: 20);
// 根据操作类型和操作结果查询订单日志
var logs = await _orderLogService.GetOrderLogsByOperationAsync(
operationType: OrderLogOperationType.CREATE_ORDER,
operationResult: OrderLogOperationResult.SUCCESS,
pageIndex: 1,
pageSize: 20
);
```
## 4. 性能优化
### 4.1 索引优化
- 已在表结构中定义了必要的索引包括关联ID、中性面单单号、操作类型、操作结果和操作时间的索引
- 这些索引将提高查询性能,特别是在按中性面单单号、操作类型和操作结果查询时
### 4.2 分页查询
- 实现了分页查询功能,避免一次性加载大量日志数据
- 建议在前端展示时使用分页,提高用户体验
### 4.3 日志清理策略
- 考虑到日志数据会随着时间增长而变得庞大,建议制定日志清理策略
- 可以定期清理 older 日志数据,或者将 older 日志数据归档到历史表中
## 5. 总结
本设计方案实现了订单日志管理系统,用于记录订单的所有操作和操作结果。通过建立独立的订单日志表,并提供完整的日志记录和查询功能,可以更好地追踪和管理订单的操作历史,提高系统的可追溯性和可维护性。
系统支持记录换单、集包、数据更新等操作类型以及成功、失败等操作结果并提供详细的操作说明。同时系统提供了多种查询方式包括按中性面单单号、标签替换请求ID、操作类型和操作结果等条件查询以及分页查询功能。
通过合理的索引设计和性能优化,系统可以高效地处理大量的日志数据,为业务运营提供有力的支持。