| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- namespace Admin.NET.Plugin.AiDOP.Supply;
- /// <summary>
- /// 编号规则服务。第一阶段兼容现有 NbrControl 规则,替代业务代码直接调用编号存储过程。
- /// </summary>
- public class NumberRuleService : ITransient
- {
- private readonly ISqlSugarClient _db;
- public NumberRuleService(ISqlSugarClient db)
- {
- _db = db;
- }
- public async Task<IReadOnlyList<string>> NextBatchAsync(string ruleCode, string domain, int count, string operatorNo)
- {
- try
- {
- _db.Ado.BeginTran();
- var numbers = await NextBatchInCurrentTransactionAsync(ruleCode, domain, count, operatorNo);
- _db.Ado.CommitTran();
- return numbers;
- }
- catch
- {
- _db.Ado.RollbackTran();
- throw;
- }
- }
- public async Task<IReadOnlyList<string>> NextBatchInCurrentTransactionAsync(string ruleCode, string domain, int count, string operatorNo)
- {
- if (string.IsNullOrWhiteSpace(ruleCode)) throw Oops.Oh("编号规则不能为空。");
- if (count <= 0) throw Oops.Oh("编号数量必须大于 0。");
- var normalizedRuleCode = ruleCode.Trim();
- var normalizedDomain = domain?.Trim() ?? string.Empty;
- var now = DateTime.Now;
- var rule = (await _db.Ado.SqlQueryAsync<NbrControlRuleRow>(
- """
- SELECT
- RecID AS Id,
- NbrType,
- NbrPre1,
- NbrPre2,
- NbrPre3,
- DateType,
- IsDateType,
- Description,
- IniValue,
- MinValue,
- `MaxValue` AS `MaxValue`,
- ResetValue,
- AllowReset,
- AllowSkip,
- AllowManual,
- IsActive,
- IsConfirm,
- Domain,
- tenant_id AS TenantId
- FROM NbrControl
- WHERE LOWER(TRIM(IFNULL(NbrType,''))) = LOWER(@RuleCode)
- AND (
- IFNULL(TRIM(domain_code),'') = @Domain
- OR IFNULL(TRIM(Domain),'') = @Domain
- OR IFNULL(TRIM(domain_code),'') = ''
- OR IFNULL(TRIM(Domain),'') = ''
- )
- AND IFNULL(IsActive, 1) = 1
- ORDER BY
- CASE
- WHEN IFNULL(TRIM(domain_code),'') = @Domain THEN 0
- WHEN IFNULL(TRIM(Domain),'') = @Domain THEN 1
- ELSE 2
- END,
- RecID
- LIMIT 1
- FOR UPDATE
- """,
- new SugarParameter("@RuleCode", normalizedRuleCode),
- new SugarParameter("@Domain", normalizedDomain))).FirstOrDefault();
- if (rule == null)
- throw Oops.Oh($"未找到编号规则:{normalizedRuleCode}");
- var sequenceDate = NumberRuleFormatter.ResolveSequenceDate(rule.DateType, rule.IsDateType, now);
- var dayInfo = await GetDayInfoForUpdateAsync(rule, normalizedRuleCode, normalizedDomain, sequenceDate);
- var firstSerial = ResolveFirstSerial(rule, dayInfo);
- var lastSerial = firstSerial + count - 1;
- if (rule.MaxValue.HasValue && lastSerial > rule.MaxValue.Value)
- {
- if (rule.AllowReset == true)
- {
- firstSerial = (rule.ResetValue ?? 0) + 1;
- lastSerial = firstSerial + count - 1;
- }
- if (lastSerial > rule.MaxValue.Value)
- throw Oops.Oh($"编号规则 {normalizedRuleCode} 已超过最大值 {rule.MaxValue.Value}。");
- }
- var numbers = Enumerable.Range(firstSerial, count)
- .Select(serial => NumberRuleFormatter.BuildNumber(
- rule.NbrPre1,
- rule.NbrPre2,
- rule.NbrPre3,
- rule.DateType,
- rule.IsDateType,
- serial,
- rule.MaxValue,
- now))
- .ToList();
- if (dayInfo == null)
- await InsertDayInfoAsync(rule, sequenceDate, lastSerial, operatorNo, now);
- else
- await UpdateDayInfoAsync(dayInfo.Id, lastSerial, operatorNo, now);
- return numbers;
- }
- private async Task<NbrDayInfoRow?> GetDayInfoForUpdateAsync(NbrControlRuleRow rule, string ruleCode, string domain, DateTime? sequenceDate)
- {
- var dateFilter = sequenceDate.HasValue ? "AND DATE(Today) = DATE(@Today)" : string.Empty;
- return (await _db.Ado.SqlQueryAsync<NbrDayInfoRow>(
- $"""
- SELECT
- RecID AS Id,
- NextValue
- FROM NbrDayInfo
- WHERE LOWER(TRIM(IFNULL(NbrType,''))) = LOWER(@RuleCode)
- AND IFNULL(TRIM(Domain),'') = @Domain
- {dateFilter}
- ORDER BY RecID
- LIMIT 1
- FOR UPDATE
- """,
- new SugarParameter("@RuleCode", rule.NbrType ?? ruleCode),
- new SugarParameter("@Domain", rule.Domain ?? domain),
- new SugarParameter("@Today", sequenceDate ?? DateTime.Today))).FirstOrDefault();
- }
- private async Task InsertDayInfoAsync(NbrControlRuleRow rule, DateTime? sequenceDate, int nextValue, string operatorNo, DateTime now)
- {
- await _db.Ado.ExecuteCommandAsync(
- """
- INSERT INTO NbrDayInfo
- (NbrType, Description, NbrPre1, NbrPre2, NbrPre3, Today,
- NextValue, IniValue, MinValue, `MaxValue`, ResetValue,
- AllowReset, AllowSkip, AllowManual, DateType, IsDateType,
- IsActive, IsConfirm, Domain, CreateUser, CreateTime, UpdateUser, UpdateTime, tenant_id)
- VALUES
- (@NbrType, @Description, @NbrPre1, @NbrPre2, @NbrPre3, @Today,
- @NextValue, @IniValue, @MinValue, @MaxValue, @ResetValue,
- @AllowReset, @AllowSkip, @AllowManual, @DateType, @IsDateType,
- @IsActive, @IsConfirm, @Domain, @UserNo, @Now, @UserNo, @Now, @TenantId)
- """,
- new SugarParameter("@NbrType", rule.NbrType ?? string.Empty),
- new SugarParameter("@Description", rule.Description ?? string.Empty),
- new SugarParameter("@NbrPre1", rule.NbrPre1 ?? string.Empty),
- new SugarParameter("@NbrPre2", rule.NbrPre2 ?? string.Empty),
- new SugarParameter("@NbrPre3", rule.NbrPre3 ?? string.Empty),
- new SugarParameter("@Today", sequenceDate ?? now.Date),
- new SugarParameter("@NextValue", nextValue),
- new SugarParameter("@IniValue", rule.IniValue),
- new SugarParameter("@MinValue", rule.MinValue),
- new SugarParameter("@MaxValue", rule.MaxValue),
- new SugarParameter("@ResetValue", rule.ResetValue),
- new SugarParameter("@AllowReset", rule.AllowReset),
- new SugarParameter("@AllowSkip", rule.AllowSkip),
- new SugarParameter("@AllowManual", rule.AllowManual),
- new SugarParameter("@DateType", rule.DateType ?? string.Empty),
- new SugarParameter("@IsDateType", rule.IsDateType),
- new SugarParameter("@IsActive", rule.IsActive ?? true),
- new SugarParameter("@IsConfirm", rule.IsConfirm ?? true),
- new SugarParameter("@Domain", rule.Domain ?? string.Empty),
- new SugarParameter("@UserNo", operatorNo ?? string.Empty),
- new SugarParameter("@Now", now),
- new SugarParameter("@TenantId", rule.TenantId));
- }
- private async Task UpdateDayInfoAsync(int dayInfoId, int nextValue, string operatorNo, DateTime now)
- {
- await _db.Ado.ExecuteCommandAsync(
- """
- UPDATE NbrDayInfo
- SET NextValue=@NextValue,
- UpdateUser=@UpdateUser,
- UpdateTime=@UpdateTime
- WHERE RecID=@Id
- """,
- new SugarParameter("@NextValue", nextValue),
- new SugarParameter("@UpdateUser", operatorNo ?? string.Empty),
- new SugarParameter("@UpdateTime", now),
- new SugarParameter("@Id", dayInfoId));
- }
- private static int ResolveFirstSerial(NbrControlRuleRow rule, NbrDayInfoRow? dayInfo)
- {
- if (dayInfo?.NextValue != null) return dayInfo.NextValue.Value + 1;
- if (rule.IniValue.HasValue) return rule.IniValue.Value;
- if (rule.MinValue.HasValue) return rule.MinValue.Value;
- return 1;
- }
- private sealed class NbrControlRuleRow
- {
- public int Id { get; set; }
- public string? NbrType { get; set; }
- public string? NbrPre1 { get; set; }
- public string? NbrPre2 { get; set; }
- public string? NbrPre3 { get; set; }
- public string? DateType { get; set; }
- public bool? IsDateType { get; set; }
- public string? Description { get; set; }
- public int? IniValue { get; set; }
- public int? MinValue { get; set; }
- public int? MaxValue { get; set; }
- public int? ResetValue { get; set; }
- public bool? AllowReset { get; set; }
- public bool? AllowSkip { get; set; }
- public bool? AllowManual { get; set; }
- public bool? IsActive { get; set; }
- public bool? IsConfirm { get; set; }
- public string? Domain { get; set; }
- public long? TenantId { get; set; }
- }
- private sealed class NbrDayInfoRow
- {
- public int Id { get; set; }
- public int? NextValue { get; set; }
- }
- }
|