180 lines
6.7 KiB
C#
180 lines
6.7 KiB
C#
using YLErp.BLL;
|
||
using YLErp.DBModels;
|
||
using YLErp.DBModels.Consts;
|
||
using YLErp.DBModels.Enums;
|
||
|
||
namespace YLErp.Modules.TradeModule.DocGenerateModule
|
||
{
|
||
/// <summary>
|
||
/// 国贸交易确认书编号生成器
|
||
/// 规则:
|
||
/// - 对客交易(OpponentRole为"乙方"):GLMS-{对手方代码}-{成交日期(yyyyMMdd)}-{当日第N笔对客交易(数字递增)}-{标的代码},从01开始
|
||
/// 例:GLMS-IS-20260424-01-250210IB
|
||
/// - 非对客交易(OpponentRole为"甲方"):GLMS-{对手方代码}-{成交日期(yyyyMMdd)}-{当日第N笔非对客交易使用字母递增}-{标的代码},从A开始
|
||
/// 例:GLMS-IS-20260424-A-250210IB
|
||
/// </summary>
|
||
public class GuolianContractNoGenerator
|
||
{
|
||
private static readonly object _syncLock = new object();
|
||
|
||
/// <summary>
|
||
/// 生成国贸交易确认书编号
|
||
/// </summary>
|
||
/// <param name="dbContext">数据库上下文</param>
|
||
/// <param name="trade">交易记录</param>
|
||
/// <param name="clientCode">客户代码</param>
|
||
/// <returns>确认书编号</returns>
|
||
public static string Generate(
|
||
YLContext dbContext,
|
||
trade trade,
|
||
string clientCode)
|
||
{
|
||
// 根据trade的OpponentRole判断是否为对客交易(乙方=对客)
|
||
var isClientTrade = trade.OpponentRole == "乙方";
|
||
var underlyingCode = (trade.UnderlyingCode ?? "").Replace(".", ""); // 去掉标的代码中的点号
|
||
var tradeDate = trade.TradeDate ?? DateTime.MinValue;
|
||
var tradeDateStr = tradeDate.ToString("yyyyMMdd");
|
||
var prefix = $"GLMS-{clientCode}-{tradeDateStr}-";
|
||
|
||
lock (_syncLock)
|
||
{
|
||
var existingForTrade = dbContext.trade_contract_r
|
||
.Where(r => r.TradeId == trade.id && r.Type == ContractTypeEnum.Trade && r.IsValid)
|
||
.Select(r => r.ContractCode)
|
||
.FirstOrDefault();
|
||
|
||
if (!string.IsNullOrWhiteSpace(existingForTrade))
|
||
{
|
||
return existingForTrade;
|
||
}
|
||
|
||
var existingCodes = dbContext.trade_contract_r
|
||
.Where(d => d.ContractCode.StartsWith(prefix) && d.Type == ContractTypeEnum.Trade && d.IsValid)
|
||
.Select(d => d.ContractCode)
|
||
.ToList();
|
||
|
||
var existingCodeSet = new HashSet<string>();
|
||
for (var i = 0; i < existingCodes.Count; i++)
|
||
{
|
||
var code = existingCodes[i];
|
||
if (!string.IsNullOrWhiteSpace(code))
|
||
{
|
||
existingCodeSet.Add(code);
|
||
}
|
||
}
|
||
|
||
if (isClientTrade)
|
||
{
|
||
// 对客交易:GLMS-{clientCode}-{date}-{counter:D2}-{underlyingCode}
|
||
var maxNo = 0;
|
||
var prefixLen = prefix.Length;
|
||
for (var i = 0; i < existingCodes.Count; i++)
|
||
{
|
||
var code = existingCodes[i];
|
||
if (string.IsNullOrEmpty(code) || code.Length <= prefixLen + 2)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
var counterStr = code.Substring(prefixLen, 2);
|
||
if (int.TryParse(counterStr, out var n) && n > maxNo)
|
||
{
|
||
maxNo = n;
|
||
}
|
||
}
|
||
|
||
var nextNo = maxNo + 1;
|
||
var contractNo = $"{prefix}{nextNo:D2}-{underlyingCode}";
|
||
while (existingCodeSet.Contains(contractNo))
|
||
{
|
||
nextNo++;
|
||
contractNo = $"{prefix}{nextNo:D2}-{underlyingCode}";
|
||
}
|
||
return contractNo;
|
||
}
|
||
else
|
||
{
|
||
// 非对客交易:GLMS-{clientCode}-{date}-{letter}-{underlyingCode}
|
||
// 找当日已有的非对客交易最大字母序号
|
||
var maxLetter = 0;
|
||
var prefixLen = prefix.Length;
|
||
for (var i = 0; i < existingCodes.Count; i++)
|
||
{
|
||
var code = existingCodes[i];
|
||
if (string.IsNullOrEmpty(code) || code.Length <= prefixLen + 2)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
var rest = code.Substring(prefixLen);
|
||
var dashIdx = rest.IndexOf('-');
|
||
if (dashIdx > 0)
|
||
{
|
||
var letterPart = rest.Substring(0, dashIdx);
|
||
if (TryParseLettersToNumber(letterPart, out var ln) && ln > maxLetter)
|
||
{
|
||
maxLetter = ln;
|
||
}
|
||
}
|
||
}
|
||
|
||
var nextLetter = maxLetter + 1;
|
||
var contractNo = $"{prefix}{NumberToLetter(nextLetter)}-{underlyingCode}";
|
||
while (existingCodeSet.Contains(contractNo))
|
||
{
|
||
nextLetter++;
|
||
contractNo = $"{prefix}{NumberToLetter(nextLetter)}-{underlyingCode}";
|
||
}
|
||
return contractNo;
|
||
}
|
||
}
|
||
}
|
||
|
||
private static bool TryParseLettersToNumber(string letters, out int number)
|
||
{
|
||
number = 0;
|
||
if (string.IsNullOrEmpty(letters))
|
||
{
|
||
return false;
|
||
}
|
||
|
||
for (var i = 0; i < letters.Length; i++)
|
||
{
|
||
var c = letters[i];
|
||
if (c >= 'a' && c <= 'z')
|
||
{
|
||
c = (char)(c - 32);
|
||
}
|
||
else if (c < 'A' || c > 'Z')
|
||
{
|
||
number = 0;
|
||
return false;
|
||
}
|
||
|
||
number = (number * 26) + (c - 'A' + 1);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将数字转换为字母序列(1=A, 2=B, 26=Z, 27=AA, 28=AB...)
|
||
/// </summary>
|
||
private static string NumberToLetter(int number)
|
||
{
|
||
if (number <= 0)
|
||
{
|
||
return "A";
|
||
}
|
||
|
||
string result = "";
|
||
while (number > 0)
|
||
{
|
||
number--; // 调整为从0开始
|
||
result = (char)('A' + (number % 26)) + result;
|
||
number /= 26;
|
||
}
|
||
return result;
|
||
}
|
||
}
|
||
}
|