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 _rep; private readonly S8WatchRuleService _watchRuleService; public S8ConfigDraftService( SqlSugarRepository rep, S8WatchRuleService watchRuleService) { _rep = rep; _watchRuleService = watchRuleService; } public async Task 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 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 GetAsync(long id) { var e = await _rep.GetByIdAsync(id) ?? throw new S8BizException("草稿不存在"); return ToDetail(e); } public async Task 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 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 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() .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() .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 ResolveExistingRuleCodeAsync(long ruleId) { var rule = await _rep.Context.Queryable() .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; } }