|
|
@@ -0,0 +1,244 @@
|
|
|
+using Admin.NET.Plugin.AiDOP.Dto.S8;
|
|
|
+using Admin.NET.Plugin.AiDOP.Entity.S8;
|
|
|
+
|
|
|
+namespace Admin.NET.Plugin.AiDOP.Service.S8;
|
|
|
+
|
|
|
+internal static class S8ConfigDraftStatus
|
|
|
+{
|
|
|
+ public const string InProgress = "IN_PROGRESS";
|
|
|
+ public const string Generated = "GENERATED";
|
|
|
+}
|
|
|
+
|
|
|
+public class S8ConfigDraftService : ITransient
|
|
|
+{
|
|
|
+ private readonly SqlSugarRepository<AdoS8ConfigDraft> _rep;
|
|
|
+ private readonly S8WatchRuleService _watchRuleService;
|
|
|
+
|
|
|
+ public S8ConfigDraftService(
|
|
|
+ SqlSugarRepository<AdoS8ConfigDraft> rep,
|
|
|
+ S8WatchRuleService watchRuleService)
|
|
|
+ {
|
|
|
+ _rep = rep;
|
|
|
+ _watchRuleService = watchRuleService;
|
|
|
+ }
|
|
|
+
|
|
|
+ public async Task<AdoS8ConfigDraftListResultDto> ListAsync(AdoS8ConfigDraftListQueryDto query)
|
|
|
+ {
|
|
|
+ var page = query.Page < 1 ? 1 : query.Page;
|
|
|
+ var pageSize = query.PageSize < 1 ? 20 : Math.Min(query.PageSize, 200);
|
|
|
+ var status = string.IsNullOrWhiteSpace(query.Status) ? null : query.Status.Trim();
|
|
|
+
|
|
|
+ var q = _rep.AsQueryable()
|
|
|
+ .Where(x => x.TenantId == query.TenantId && x.FactoryId == query.FactoryId);
|
|
|
+ if (status != null)
|
|
|
+ q = q.Where(x => x.Status == status);
|
|
|
+
|
|
|
+ RefAsync<int> total = 0;
|
|
|
+ var rows = await q
|
|
|
+ .OrderBy(x => x.Id, OrderByType.Desc)
|
|
|
+ .Select(x => new AdoS8ConfigDraftListItemDto
|
|
|
+ {
|
|
|
+ Id = x.Id,
|
|
|
+ TenantId = x.TenantId,
|
|
|
+ FactoryId = x.FactoryId,
|
|
|
+ DraftCode = x.DraftCode,
|
|
|
+ DraftName = x.DraftName,
|
|
|
+ Status = x.Status,
|
|
|
+ CurrentStep = x.CurrentStep,
|
|
|
+ Mechanism = x.Mechanism,
|
|
|
+ StageCode = x.StageCode,
|
|
|
+ OrderFlowCode = x.OrderFlowCode,
|
|
|
+ ExceptionTypeCode = x.ExceptionTypeCode,
|
|
|
+ GeneratedRuleId = x.GeneratedRuleId,
|
|
|
+ Remark = x.Remark,
|
|
|
+ CreatedAt = x.CreatedAt,
|
|
|
+ UpdatedAt = x.UpdatedAt
|
|
|
+ })
|
|
|
+ .ToPageListAsync(page, pageSize, total);
|
|
|
+
|
|
|
+ return new AdoS8ConfigDraftListResultDto
|
|
|
+ {
|
|
|
+ Items = rows,
|
|
|
+ Total = total.Value,
|
|
|
+ Page = page,
|
|
|
+ PageSize = pageSize
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ public async Task<AdoS8ConfigDraftDetailDto> GetAsync(long id)
|
|
|
+ {
|
|
|
+ var e = await _rep.GetByIdAsync(id) ?? throw new S8BizException("草稿不存在");
|
|
|
+ return ToDetail(e);
|
|
|
+ }
|
|
|
+
|
|
|
+ public async Task<AdoS8ConfigDraftDetailDto> CreateAsync(AdoS8ConfigDraftCreateDto dto)
|
|
|
+ {
|
|
|
+ if (string.IsNullOrWhiteSpace(dto.DraftCode))
|
|
|
+ throw new S8BizException("草稿编码必填");
|
|
|
+ if (string.IsNullOrWhiteSpace(dto.WizardJson))
|
|
|
+ throw new S8BizException("草稿 wizard_json 必填");
|
|
|
+
|
|
|
+ var draftCode = dto.DraftCode.Trim();
|
|
|
+ var exists = await _rep.AsQueryable()
|
|
|
+ .AnyAsync(x => x.TenantId == dto.TenantId && x.FactoryId == dto.FactoryId && x.DraftCode == draftCode);
|
|
|
+ if (exists) throw new S8BizException("草稿编码已存在");
|
|
|
+
|
|
|
+ var entity = new AdoS8ConfigDraft
|
|
|
+ {
|
|
|
+ TenantId = dto.TenantId,
|
|
|
+ FactoryId = dto.FactoryId,
|
|
|
+ DraftCode = draftCode,
|
|
|
+ DraftName = NormalizeOrNull(dto.DraftName),
|
|
|
+ WizardJson = dto.WizardJson,
|
|
|
+ CurrentStep = dto.CurrentStep ?? 0,
|
|
|
+ Mechanism = NormalizeOrNull(dto.Mechanism),
|
|
|
+ StageCode = NormalizeOrNull(dto.StageCode),
|
|
|
+ OrderFlowCode = NormalizeOrNull(dto.OrderFlowCode),
|
|
|
+ ExceptionTypeCode = NormalizeOrNull(dto.ExceptionTypeCode),
|
|
|
+ Remark = NormalizeOrNull(dto.Remark),
|
|
|
+ Status = S8ConfigDraftStatus.InProgress,
|
|
|
+ CreatedAt = DateTime.Now
|
|
|
+ };
|
|
|
+ entity.Id = await _rep.AsInsertable(entity).ExecuteReturnBigIdentityAsync();
|
|
|
+ return ToDetail(entity);
|
|
|
+ }
|
|
|
+
|
|
|
+ public async Task<AdoS8ConfigDraftDetailDto> UpdateAsync(long id, AdoS8ConfigDraftUpdateDto dto)
|
|
|
+ {
|
|
|
+ var e = await _rep.GetByIdAsync(id) ?? throw new S8BizException("草稿不存在");
|
|
|
+ if (e.Status == S8ConfigDraftStatus.Generated)
|
|
|
+ throw new S8BizException("草稿已生成正式规则,禁止再编辑");
|
|
|
+
|
|
|
+ if (dto.DraftName != null) e.DraftName = NormalizeOrNull(dto.DraftName);
|
|
|
+ if (dto.WizardJson != null)
|
|
|
+ {
|
|
|
+ if (string.IsNullOrWhiteSpace(dto.WizardJson))
|
|
|
+ throw new S8BizException("wizard_json 不能为空字符串");
|
|
|
+ e.WizardJson = dto.WizardJson;
|
|
|
+ }
|
|
|
+ if (dto.CurrentStep.HasValue) e.CurrentStep = dto.CurrentStep.Value;
|
|
|
+ if (dto.Mechanism != null) e.Mechanism = NormalizeOrNull(dto.Mechanism);
|
|
|
+ if (dto.StageCode != null) e.StageCode = NormalizeOrNull(dto.StageCode);
|
|
|
+ if (dto.OrderFlowCode != null) e.OrderFlowCode = NormalizeOrNull(dto.OrderFlowCode);
|
|
|
+ if (dto.ExceptionTypeCode != null) e.ExceptionTypeCode = NormalizeOrNull(dto.ExceptionTypeCode);
|
|
|
+ if (dto.Remark != null) e.Remark = NormalizeOrNull(dto.Remark);
|
|
|
+
|
|
|
+ e.UpdatedAt = DateTime.Now;
|
|
|
+ await _rep.UpdateAsync(e);
|
|
|
+ return ToDetail(e);
|
|
|
+ }
|
|
|
+
|
|
|
+ public async Task DeleteAsync(long id)
|
|
|
+ {
|
|
|
+ var e = await _rep.GetByIdAsync(id) ?? throw new S8BizException("草稿不存在");
|
|
|
+ await _rep.DeleteByIdAsync(e.Id);
|
|
|
+ }
|
|
|
+
|
|
|
+ public async Task<AdoS8ConfigDraftGenerateRuleResultDto> GenerateRuleAsync(long id, AdoS8ConfigDraftGenerateRuleDto dto)
|
|
|
+ {
|
|
|
+ var draft = await _rep.GetByIdAsync(id) ?? throw new S8BizException("草稿不存在");
|
|
|
+
|
|
|
+ if (draft.GeneratedRuleId.HasValue)
|
|
|
+ {
|
|
|
+ return new AdoS8ConfigDraftGenerateRuleResultDto
|
|
|
+ {
|
|
|
+ RuleId = draft.GeneratedRuleId.Value,
|
|
|
+ RuleCode = await ResolveExistingRuleCodeAsync(draft.GeneratedRuleId.Value),
|
|
|
+ DraftStatus = draft.Status
|
|
|
+ };
|
|
|
+ }
|
|
|
+ if (draft.Status == S8ConfigDraftStatus.Generated)
|
|
|
+ throw new S8BizException("草稿状态异常:已标记 GENERATED 但未回写规则 ID");
|
|
|
+
|
|
|
+ var payload = dto.RulePayload ?? throw new S8BizException("rulePayload 必填");
|
|
|
+ if (string.IsNullOrWhiteSpace(payload.RuleCode))
|
|
|
+ throw new S8BizException("rulePayload.ruleCode 必填");
|
|
|
+
|
|
|
+ // 安全收口:强制对齐草稿租户/工厂、强制 enabled=false。
|
|
|
+ payload.Id = 0;
|
|
|
+ payload.TenantId = draft.TenantId;
|
|
|
+ payload.FactoryId = draft.FactoryId;
|
|
|
+ payload.Enabled = false;
|
|
|
+
|
|
|
+ var db = _rep.Context;
|
|
|
+ await db.Ado.BeginTranAsync();
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var created = await _watchRuleService.CreateAsync(payload);
|
|
|
+
|
|
|
+ // S8WatchRuleService.CreateAsync 内部用 InsertAsync 不回填 Id;按 RuleCode 反查真实 id。
|
|
|
+ var newRuleId = created.Id;
|
|
|
+ if (newRuleId <= 0)
|
|
|
+ {
|
|
|
+ newRuleId = await db.Queryable<AdoS8WatchRule>()
|
|
|
+ .Where(x => x.TenantId == draft.TenantId
|
|
|
+ && x.FactoryId == draft.FactoryId
|
|
|
+ && x.RuleCode == created.RuleCode)
|
|
|
+ .Select(x => x.Id)
|
|
|
+ .FirstAsync();
|
|
|
+ }
|
|
|
+ if (newRuleId <= 0)
|
|
|
+ throw new S8BizException("生成规则失败:无法定位新规则 id");
|
|
|
+
|
|
|
+ await db.Updateable<AdoS8ConfigDraft>()
|
|
|
+ .SetColumns(x => new AdoS8ConfigDraft
|
|
|
+ {
|
|
|
+ GeneratedRuleId = newRuleId,
|
|
|
+ Status = S8ConfigDraftStatus.Generated,
|
|
|
+ UpdatedAt = DateTime.Now
|
|
|
+ })
|
|
|
+ .Where(x => x.Id == draft.Id)
|
|
|
+ .ExecuteCommandAsync();
|
|
|
+
|
|
|
+ await db.Ado.CommitTranAsync();
|
|
|
+
|
|
|
+ return new AdoS8ConfigDraftGenerateRuleResultDto
|
|
|
+ {
|
|
|
+ RuleId = newRuleId,
|
|
|
+ RuleCode = created.RuleCode,
|
|
|
+ DraftStatus = S8ConfigDraftStatus.Generated
|
|
|
+ };
|
|
|
+ }
|
|
|
+ catch
|
|
|
+ {
|
|
|
+ await db.Ado.RollbackTranAsync();
|
|
|
+ throw;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task<string> ResolveExistingRuleCodeAsync(long ruleId)
|
|
|
+ {
|
|
|
+ var rule = await _rep.Context.Queryable<AdoS8WatchRule>()
|
|
|
+ .Where(x => x.Id == ruleId)
|
|
|
+ .Select(x => new { x.RuleCode })
|
|
|
+ .FirstAsync();
|
|
|
+ return rule?.RuleCode ?? string.Empty;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static AdoS8ConfigDraftDetailDto ToDetail(AdoS8ConfigDraft e) => new()
|
|
|
+ {
|
|
|
+ Id = e.Id,
|
|
|
+ TenantId = e.TenantId,
|
|
|
+ FactoryId = e.FactoryId,
|
|
|
+ DraftCode = e.DraftCode,
|
|
|
+ DraftName = e.DraftName,
|
|
|
+ Status = e.Status,
|
|
|
+ CurrentStep = e.CurrentStep,
|
|
|
+ Mechanism = e.Mechanism,
|
|
|
+ StageCode = e.StageCode,
|
|
|
+ OrderFlowCode = e.OrderFlowCode,
|
|
|
+ ExceptionTypeCode = e.ExceptionTypeCode,
|
|
|
+ GeneratedRuleId = e.GeneratedRuleId,
|
|
|
+ Remark = e.Remark,
|
|
|
+ CreatedAt = e.CreatedAt,
|
|
|
+ UpdatedAt = e.UpdatedAt,
|
|
|
+ WizardJson = e.WizardJson
|
|
|
+ };
|
|
|
+
|
|
|
+ private static string? NormalizeOrNull(string? value)
|
|
|
+ {
|
|
|
+ if (string.IsNullOrWhiteSpace(value)) return null;
|
|
|
+ var trimmed = value.Trim();
|
|
|
+ return trimmed.Length == 0 ? null : trimmed;
|
|
|
+ }
|
|
|
+}
|