using System.Security.Claims; using Admin.NET.Core; using Admin.NET.Plugin.AiDOP.Dto.SmartOps; using Admin.NET.Plugin.AiDOP.Entity; using Admin.NET.Plugin.AiDOP.SmartOps; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using SqlSugar; namespace Admin.NET.Plugin.AiDOP.Controllers; /// /// KPI 维度计算配置(DIMENSION_SQL 配置化)接口。**要求登录**——SQL 编辑/发布是高危面。 /// 租户由服务端按 moduleCode 解析;操作人取自 JWT。 /// [ApiController] [Route("api/[controller]")] [Authorize] public class AdoSmartOpsKpiDimensionConfigController : ControllerBase { private readonly AdoSmartOpsKpiDimensionConfigService _service; private readonly KpiDimensionRunService _run; private readonly ISqlSugarClient _db; public AdoSmartOpsKpiDimensionConfigController( AdoSmartOpsKpiDimensionConfigService service, KpiDimensionRunService run, ISqlSugarClient db) { _service = service; _run = run; _db = db; } private string Operator() => User?.FindFirstValue("RealName") ?? User?.FindFirstValue("Account") ?? User?.Identity?.Name ?? "unknown"; /// 某 KPI 的全部维度配置版本。 [HttpGet("by-metric/{metricCode}")] public async Task GetByMetric(string metricCode, [FromQuery] string moduleCode) { if (string.IsNullOrWhiteSpace(moduleCode)) return BadRequest(new { message = "moduleCode 必填" }); return Ok(await _service.GetByMetricAsync(metricCode, moduleCode)); } /// 已登记数据源列表。 [HttpGet("data-sources")] public IActionResult DataSources() => Ok(_service.ListDataSources()); /// KPI 维度能力(驱动前端筛选/下钻可用性)。 [HttpGet("capability/{metricCode}")] public async Task Capability(string metricCode, [FromQuery] string moduleCode) { if (string.IsNullOrWhiteSpace(moduleCode)) return BadRequest(new { message = "moduleCode 必填" }); return Ok(await _service.GetCapabilityAsync(metricCode, moduleCode)); } /// 新增草稿。 [HttpPost] public async Task Create([FromBody] KpiDimensionConfigUpsertDto dto) { if (string.IsNullOrWhiteSpace(dto.MetricCode) || string.IsNullOrWhiteSpace(dto.ModuleCode)) return BadRequest(new { message = "metricCode / moduleCode 必填" }); return Ok(await _service.CreateDraftAsync(dto, Operator())); } /// 编辑草稿。 [HttpPut("{id:long}")] public async Task Update(long id, [FromBody] KpiDimensionConfigUpsertDto dto) { await _service.UpdateDraftAsync(id, dto, Operator()); return Ok(new { ok = true }); } /// 删除草稿。 [HttpDelete("{id:long}")] public async Task Delete(long id) { await _service.DeleteAsync(id); return Ok(new { ok = true }); } /// SQL 安全校验。 [HttpPost("validate")] public IActionResult Validate([FromBody] KpiSqlValidateDto dto) => Ok(_service.Validate(dto.SqlScript)); /// 试算(不写维度结果;服务端重校验 + 只读多行执行)。 [HttpPost("preview")] public async Task Preview([FromBody] KpiDimensionPreviewDto dto, CancellationToken ct) { if (string.IsNullOrWhiteSpace(dto.MetricCode) || string.IsNullOrWhiteSpace(dto.ModuleCode)) return BadRequest(new { message = "metricCode / moduleCode 必填" }); return Ok(await _service.PreviewAsync(dto, ct)); } /// 发布(事务:单生效版本 + 绑定汇总版本一致性)。 [HttpPost("{id:long}/publish")] public async Task Publish(long id) { await _service.PublishAsync(id, Operator()); return Ok(new { ok = true }); } /// 激活/回滚。 [HttpPost("{id:long}/activate")] public async Task Activate(long id) { await _service.ActivateAsync(id, Operator()); return Ok(new { ok = true }); } /// 停用。 [HttpPost("{id:long}/retire")] public async Task Retire(long id) { await _service.RetireAsync(id, Operator()); return Ok(new { ok = true }); } /// 手动触发一次维度执行(通用入口 RunDimensionAsync,TriggerType=MANUAL)。 [HttpPost("run/{metricCode}")] public async Task Run( string metricCode, [FromQuery] string moduleCode, [FromQuery] DateTime? valueDate, CancellationToken ct) { if (string.IsNullOrWhiteSpace(metricCode) || string.IsNullOrWhiteSpace(moduleCode)) return BadRequest(new { message = "metricCode / moduleCode 必填" }); var tenantId = AdoSmartOpsKpiCalcConfigService.ResolveKpiTenantId(moduleCode); var vd = (valueDate ?? DateTime.Today.AddDays(-1)).Date; var batchId = $"DIM_MANUAL_{metricCode}_{vd:yyyyMMdd}_{DateTime.Now:HHmmssfff}"; var res = await _run.RunDimensionAsync(metricCode, moduleCode, tenantId, vd, batchId, "MANUAL", ct); return Ok(res); } /// 维度明细只读查询(当前生效维度版本;含按 AggregationType 复算的聚合值 + 分页明细)。 [HttpGet("detail/{metricCode}")] public async Task Detail( string metricCode, [FromQuery] string moduleCode, [FromQuery] DateTime? startDate, [FromQuery] DateTime? endDate, [FromQuery] string? dimensionType, [FromQuery] string? dimensionCode, [FromQuery] string? orderNo, [FromQuery] string? sortField, [FromQuery] string? sortOrder, [FromQuery] int page = 1, [FromQuery] int pageSize = 20) { if (string.IsNullOrWhiteSpace(metricCode) || string.IsNullOrWhiteSpace(moduleCode)) return BadRequest(new { message = "metricCode / moduleCode 必填" }); if (pageSize > 200) pageSize = 200; // 分页上限,防大结果 return Ok(await _service.QueryDetailAsync(metricCode, moduleCode, startDate, endDate, dimensionType, dimensionCode, page, pageSize, orderNo, sortField, sortOrder)); } /// 某 KPI 的维度运行日志(分页,脱敏,租户后端解析)。 [HttpGet("run-log/{metricCode}")] public async Task RunLog( string metricCode, [FromQuery] string moduleCode, [FromQuery] int page = 1, [FromQuery] int pageSize = 20, [FromQuery] string? status = null) { if (string.IsNullOrWhiteSpace(metricCode) || string.IsNullOrWhiteSpace(moduleCode)) return BadRequest(new { message = "metricCode / moduleCode 必填" }); var tenantId = AdoSmartOpsKpiCalcConfigService.ResolveKpiTenantId(moduleCode); RefAsync total = 0; var list = await _db.Queryable().ClearFilter() .Where(x => x.MetricCode == metricCode && x.TenantId == tenantId) .WhereIF(!string.IsNullOrWhiteSpace(status), x => x.Status == status) .OrderBy(x => x.StartedAt, OrderByType.Desc) .ToPageListAsync(page <= 0 ? 1 : page, pageSize <= 0 ? 20 : pageSize, total); return Ok(new { list, total = total.Value }); } }