| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051 |
- namespace Admin.NET.Plugin.AiDOP.MaterialWarehouse.InventoryPosting.Numbering;
- /// <summary>
- /// 业务单号服务:基于原子 provider 取号 + 「DB UNIQUE 兜底 + duplicate-key 有限次数重试」消费。
- /// 硬约束:禁 SELECT MAX()+1 / 禁 read-max-then-write / 禁仅靠进程锁 / 禁随机数假唯一。
- /// 最终唯一性 = provider 原子性 + 目标表 DB UNIQUE 兜底;本服务负责有界重试,绝不无限循环。
- /// </summary>
- public sealed class BusinessNumberService
- {
- public const int DefaultMaxRetry = 5;
- private readonly IBusinessNumberProvider _provider;
- public BusinessNumberService(IBusinessNumberProvider provider)
- {
- _provider = provider ?? throw new ArgumentNullException(nameof(provider));
- }
- /// <summary>取单个业务单号(原子)。</summary>
- public async Task<string> NextAsync(string ruleCode, string domain, string operatorNo)
- {
- var batch = await _provider.NextBatchAsync(ruleCode, domain, 1, operatorNo);
- return batch[0];
- }
- /// <summary>取一批业务单号(原子、连续)。</summary>
- public Task<IReadOnlyList<string>> NextBatchAsync(string ruleCode, string domain, int count, string operatorNo)
- => _provider.NextBatchAsync(ruleCode, domain, count, operatorNo);
- /// <summary>
- /// 取号 → 消费(tryConsume 尝试落库;返回 true=成功占用/唯一,false=撞 DB UNIQUE 需换号重试)。
- /// 有界重试 maxRetry 次;耗尽仍失败则返回 Success=false(不无限循环)。
- /// tryConsume 应捕获 duplicate-key(1062) 返回 false,其它异常向上抛。
- /// </summary>
- public async Task<BusinessNumberConsumeResult> NextWithUniqueRetryAsync(
- string ruleCode, string domain, string operatorNo,
- Func<string, Task<bool>> tryConsume, int maxRetry = DefaultMaxRetry)
- {
- if (tryConsume == null) throw new ArgumentNullException(nameof(tryConsume));
- if (maxRetry < 1) maxRetry = 1;
- string last = null;
- for (var attempt = 1; attempt <= maxRetry; attempt++)
- {
- last = await NextAsync(ruleCode, domain, operatorNo);
- if (await tryConsume(last))
- return new BusinessNumberConsumeResult { Success = true, Number = last, Attempts = attempt };
- }
- return new BusinessNumberConsumeResult { Success = false, Number = last, Attempts = maxRetry };
- }
- }
|