NumberRuleService.cs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. namespace Admin.NET.Plugin.AiDOP.Supply;
  2. /// <summary>
  3. /// 编号规则服务。第一阶段兼容现有 NbrControl 规则,替代业务代码直接调用编号存储过程。
  4. /// </summary>
  5. public class NumberRuleService : ITransient
  6. {
  7. private readonly ISqlSugarClient _db;
  8. public NumberRuleService(ISqlSugarClient db)
  9. {
  10. _db = db;
  11. }
  12. public async Task<IReadOnlyList<string>> NextBatchAsync(string ruleCode, string domain, int count, string operatorNo)
  13. {
  14. try
  15. {
  16. _db.Ado.BeginTran();
  17. var numbers = await NextBatchInCurrentTransactionAsync(ruleCode, domain, count, operatorNo);
  18. _db.Ado.CommitTran();
  19. return numbers;
  20. }
  21. catch
  22. {
  23. _db.Ado.RollbackTran();
  24. throw;
  25. }
  26. }
  27. public async Task<IReadOnlyList<string>> NextBatchInCurrentTransactionAsync(string ruleCode, string domain, int count, string operatorNo)
  28. {
  29. if (string.IsNullOrWhiteSpace(ruleCode)) throw Oops.Oh("编号规则不能为空。");
  30. if (count <= 0) throw Oops.Oh("编号数量必须大于 0。");
  31. var normalizedRuleCode = ruleCode.Trim();
  32. var normalizedDomain = domain?.Trim() ?? string.Empty;
  33. var now = DateTime.Now;
  34. var rule = (await _db.Ado.SqlQueryAsync<NbrControlRuleRow>(
  35. """
  36. SELECT
  37. RecID AS Id,
  38. NbrType,
  39. NbrPre1,
  40. NbrPre2,
  41. NbrPre3,
  42. DateType,
  43. IsDateType,
  44. Description,
  45. IniValue,
  46. MinValue,
  47. `MaxValue` AS `MaxValue`,
  48. ResetValue,
  49. AllowReset,
  50. AllowSkip,
  51. AllowManual,
  52. IsActive,
  53. IsConfirm,
  54. Domain,
  55. tenant_id AS TenantId
  56. FROM NbrControl
  57. WHERE LOWER(TRIM(IFNULL(NbrType,''))) = LOWER(@RuleCode)
  58. AND (
  59. IFNULL(TRIM(domain_code),'') = @Domain
  60. OR IFNULL(TRIM(Domain),'') = @Domain
  61. OR IFNULL(TRIM(domain_code),'') = ''
  62. OR IFNULL(TRIM(Domain),'') = ''
  63. )
  64. AND IFNULL(IsActive, 1) = 1
  65. ORDER BY
  66. CASE
  67. WHEN IFNULL(TRIM(domain_code),'') = @Domain THEN 0
  68. WHEN IFNULL(TRIM(Domain),'') = @Domain THEN 1
  69. ELSE 2
  70. END,
  71. RecID
  72. LIMIT 1
  73. FOR UPDATE
  74. """,
  75. new SugarParameter("@RuleCode", normalizedRuleCode),
  76. new SugarParameter("@Domain", normalizedDomain))).FirstOrDefault();
  77. if (rule == null)
  78. throw Oops.Oh($"未找到编号规则:{normalizedRuleCode}");
  79. var sequenceDate = NumberRuleFormatter.ResolveSequenceDate(rule.DateType, rule.IsDateType, now);
  80. var dayInfo = await GetDayInfoForUpdateAsync(rule, normalizedRuleCode, normalizedDomain, sequenceDate);
  81. var firstSerial = ResolveFirstSerial(rule, dayInfo);
  82. var lastSerial = firstSerial + count - 1;
  83. if (rule.MaxValue.HasValue && lastSerial > rule.MaxValue.Value)
  84. {
  85. if (rule.AllowReset == true)
  86. {
  87. firstSerial = (rule.ResetValue ?? 0) + 1;
  88. lastSerial = firstSerial + count - 1;
  89. }
  90. if (lastSerial > rule.MaxValue.Value)
  91. throw Oops.Oh($"编号规则 {normalizedRuleCode} 已超过最大值 {rule.MaxValue.Value}。");
  92. }
  93. var numbers = Enumerable.Range(firstSerial, count)
  94. .Select(serial => NumberRuleFormatter.BuildNumber(
  95. rule.NbrPre1,
  96. rule.NbrPre2,
  97. rule.NbrPre3,
  98. rule.DateType,
  99. rule.IsDateType,
  100. serial,
  101. rule.MaxValue,
  102. now))
  103. .ToList();
  104. if (dayInfo == null)
  105. await InsertDayInfoAsync(rule, sequenceDate, lastSerial, operatorNo, now);
  106. else
  107. await UpdateDayInfoAsync(dayInfo.Id, lastSerial, operatorNo, now);
  108. return numbers;
  109. }
  110. private async Task<NbrDayInfoRow?> GetDayInfoForUpdateAsync(NbrControlRuleRow rule, string ruleCode, string domain, DateTime? sequenceDate)
  111. {
  112. var dateFilter = sequenceDate.HasValue ? "AND DATE(Today) = DATE(@Today)" : string.Empty;
  113. return (await _db.Ado.SqlQueryAsync<NbrDayInfoRow>(
  114. $"""
  115. SELECT
  116. RecID AS Id,
  117. NextValue
  118. FROM NbrDayInfo
  119. WHERE LOWER(TRIM(IFNULL(NbrType,''))) = LOWER(@RuleCode)
  120. AND IFNULL(TRIM(Domain),'') = @Domain
  121. {dateFilter}
  122. ORDER BY RecID
  123. LIMIT 1
  124. FOR UPDATE
  125. """,
  126. new SugarParameter("@RuleCode", rule.NbrType ?? ruleCode),
  127. new SugarParameter("@Domain", rule.Domain ?? domain),
  128. new SugarParameter("@Today", sequenceDate ?? DateTime.Today))).FirstOrDefault();
  129. }
  130. private async Task InsertDayInfoAsync(NbrControlRuleRow rule, DateTime? sequenceDate, int nextValue, string operatorNo, DateTime now)
  131. {
  132. await _db.Ado.ExecuteCommandAsync(
  133. """
  134. INSERT INTO NbrDayInfo
  135. (NbrType, Description, NbrPre1, NbrPre2, NbrPre3, Today,
  136. NextValue, IniValue, MinValue, `MaxValue`, ResetValue,
  137. AllowReset, AllowSkip, AllowManual, DateType, IsDateType,
  138. IsActive, IsConfirm, Domain, CreateUser, CreateTime, UpdateUser, UpdateTime, tenant_id)
  139. VALUES
  140. (@NbrType, @Description, @NbrPre1, @NbrPre2, @NbrPre3, @Today,
  141. @NextValue, @IniValue, @MinValue, @MaxValue, @ResetValue,
  142. @AllowReset, @AllowSkip, @AllowManual, @DateType, @IsDateType,
  143. @IsActive, @IsConfirm, @Domain, @UserNo, @Now, @UserNo, @Now, @TenantId)
  144. """,
  145. new SugarParameter("@NbrType", rule.NbrType ?? string.Empty),
  146. new SugarParameter("@Description", rule.Description ?? string.Empty),
  147. new SugarParameter("@NbrPre1", rule.NbrPre1 ?? string.Empty),
  148. new SugarParameter("@NbrPre2", rule.NbrPre2 ?? string.Empty),
  149. new SugarParameter("@NbrPre3", rule.NbrPre3 ?? string.Empty),
  150. new SugarParameter("@Today", sequenceDate ?? now.Date),
  151. new SugarParameter("@NextValue", nextValue),
  152. new SugarParameter("@IniValue", rule.IniValue),
  153. new SugarParameter("@MinValue", rule.MinValue),
  154. new SugarParameter("@MaxValue", rule.MaxValue),
  155. new SugarParameter("@ResetValue", rule.ResetValue),
  156. new SugarParameter("@AllowReset", rule.AllowReset),
  157. new SugarParameter("@AllowSkip", rule.AllowSkip),
  158. new SugarParameter("@AllowManual", rule.AllowManual),
  159. new SugarParameter("@DateType", rule.DateType ?? string.Empty),
  160. new SugarParameter("@IsDateType", rule.IsDateType),
  161. new SugarParameter("@IsActive", rule.IsActive ?? true),
  162. new SugarParameter("@IsConfirm", rule.IsConfirm ?? true),
  163. new SugarParameter("@Domain", rule.Domain ?? string.Empty),
  164. new SugarParameter("@UserNo", operatorNo ?? string.Empty),
  165. new SugarParameter("@Now", now),
  166. new SugarParameter("@TenantId", rule.TenantId));
  167. }
  168. private async Task UpdateDayInfoAsync(int dayInfoId, int nextValue, string operatorNo, DateTime now)
  169. {
  170. await _db.Ado.ExecuteCommandAsync(
  171. """
  172. UPDATE NbrDayInfo
  173. SET NextValue=@NextValue,
  174. UpdateUser=@UpdateUser,
  175. UpdateTime=@UpdateTime
  176. WHERE RecID=@Id
  177. """,
  178. new SugarParameter("@NextValue", nextValue),
  179. new SugarParameter("@UpdateUser", operatorNo ?? string.Empty),
  180. new SugarParameter("@UpdateTime", now),
  181. new SugarParameter("@Id", dayInfoId));
  182. }
  183. private static int ResolveFirstSerial(NbrControlRuleRow rule, NbrDayInfoRow? dayInfo)
  184. {
  185. if (dayInfo?.NextValue != null) return dayInfo.NextValue.Value + 1;
  186. if (rule.IniValue.HasValue) return rule.IniValue.Value;
  187. if (rule.MinValue.HasValue) return rule.MinValue.Value;
  188. return 1;
  189. }
  190. private sealed class NbrControlRuleRow
  191. {
  192. public int Id { get; set; }
  193. public string? NbrType { get; set; }
  194. public string? NbrPre1 { get; set; }
  195. public string? NbrPre2 { get; set; }
  196. public string? NbrPre3 { get; set; }
  197. public string? DateType { get; set; }
  198. public bool? IsDateType { get; set; }
  199. public string? Description { get; set; }
  200. public int? IniValue { get; set; }
  201. public int? MinValue { get; set; }
  202. public int? MaxValue { get; set; }
  203. public int? ResetValue { get; set; }
  204. public bool? AllowReset { get; set; }
  205. public bool? AllowSkip { get; set; }
  206. public bool? AllowManual { get; set; }
  207. public bool? IsActive { get; set; }
  208. public bool? IsConfirm { get; set; }
  209. public string? Domain { get; set; }
  210. public long? TenantId { get; set; }
  211. }
  212. private sealed class NbrDayInfoRow
  213. {
  214. public int Id { get; set; }
  215. public int? NextValue { get; set; }
  216. }
  217. }