AdoSmartOpsKpiDimensionConfigController.cs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. using System.Security.Claims;
  2. using Admin.NET.Core;
  3. using Admin.NET.Plugin.AiDOP.Dto.SmartOps;
  4. using Admin.NET.Plugin.AiDOP.Entity;
  5. using Admin.NET.Plugin.AiDOP.SmartOps;
  6. using Microsoft.AspNetCore.Authorization;
  7. using Microsoft.AspNetCore.Mvc;
  8. using SqlSugar;
  9. namespace Admin.NET.Plugin.AiDOP.Controllers;
  10. /// <summary>
  11. /// KPI 维度计算配置(DIMENSION_SQL 配置化)接口。**要求登录**——SQL 编辑/发布是高危面。
  12. /// 租户由服务端按 moduleCode 解析;操作人取自 JWT。
  13. /// </summary>
  14. [ApiController]
  15. [Route("api/[controller]")]
  16. [Authorize]
  17. public class AdoSmartOpsKpiDimensionConfigController : ControllerBase
  18. {
  19. private readonly AdoSmartOpsKpiDimensionConfigService _service;
  20. private readonly KpiDimensionRunService _run;
  21. private readonly ISqlSugarClient _db;
  22. public AdoSmartOpsKpiDimensionConfigController(
  23. AdoSmartOpsKpiDimensionConfigService service, KpiDimensionRunService run, ISqlSugarClient db)
  24. {
  25. _service = service;
  26. _run = run;
  27. _db = db;
  28. }
  29. private string Operator() =>
  30. User?.FindFirstValue("RealName") ?? User?.FindFirstValue("Account") ?? User?.Identity?.Name ?? "unknown";
  31. /// <summary>某 KPI 的全部维度配置版本。</summary>
  32. [HttpGet("by-metric/{metricCode}")]
  33. public async Task<IActionResult> GetByMetric(string metricCode, [FromQuery] string moduleCode)
  34. {
  35. if (string.IsNullOrWhiteSpace(moduleCode))
  36. return BadRequest(new { message = "moduleCode 必填" });
  37. return Ok(await _service.GetByMetricAsync(metricCode, moduleCode));
  38. }
  39. /// <summary>已登记数据源列表。</summary>
  40. [HttpGet("data-sources")]
  41. public IActionResult DataSources() => Ok(_service.ListDataSources());
  42. /// <summary>KPI 维度能力(驱动前端筛选/下钻可用性)。</summary>
  43. [HttpGet("capability/{metricCode}")]
  44. public async Task<IActionResult> Capability(string metricCode, [FromQuery] string moduleCode)
  45. {
  46. if (string.IsNullOrWhiteSpace(moduleCode))
  47. return BadRequest(new { message = "moduleCode 必填" });
  48. return Ok(await _service.GetCapabilityAsync(metricCode, moduleCode));
  49. }
  50. /// <summary>新增草稿。</summary>
  51. [HttpPost]
  52. public async Task<IActionResult> Create([FromBody] KpiDimensionConfigUpsertDto dto)
  53. {
  54. if (string.IsNullOrWhiteSpace(dto.MetricCode) || string.IsNullOrWhiteSpace(dto.ModuleCode))
  55. return BadRequest(new { message = "metricCode / moduleCode 必填" });
  56. return Ok(await _service.CreateDraftAsync(dto, Operator()));
  57. }
  58. /// <summary>编辑草稿。</summary>
  59. [HttpPut("{id:long}")]
  60. public async Task<IActionResult> Update(long id, [FromBody] KpiDimensionConfigUpsertDto dto)
  61. {
  62. await _service.UpdateDraftAsync(id, dto, Operator());
  63. return Ok(new { ok = true });
  64. }
  65. /// <summary>删除草稿。</summary>
  66. [HttpDelete("{id:long}")]
  67. public async Task<IActionResult> Delete(long id)
  68. {
  69. await _service.DeleteAsync(id);
  70. return Ok(new { ok = true });
  71. }
  72. /// <summary>SQL 安全校验。</summary>
  73. [HttpPost("validate")]
  74. public IActionResult Validate([FromBody] KpiSqlValidateDto dto) => Ok(_service.Validate(dto.SqlScript));
  75. /// <summary>试算(不写维度结果;服务端重校验 + 只读多行执行)。</summary>
  76. [HttpPost("preview")]
  77. public async Task<IActionResult> Preview([FromBody] KpiDimensionPreviewDto dto, CancellationToken ct)
  78. {
  79. if (string.IsNullOrWhiteSpace(dto.MetricCode) || string.IsNullOrWhiteSpace(dto.ModuleCode))
  80. return BadRequest(new { message = "metricCode / moduleCode 必填" });
  81. return Ok(await _service.PreviewAsync(dto, ct));
  82. }
  83. /// <summary>发布(事务:单生效版本 + 绑定汇总版本一致性)。</summary>
  84. [HttpPost("{id:long}/publish")]
  85. public async Task<IActionResult> Publish(long id)
  86. {
  87. await _service.PublishAsync(id, Operator());
  88. return Ok(new { ok = true });
  89. }
  90. /// <summary>激活/回滚。</summary>
  91. [HttpPost("{id:long}/activate")]
  92. public async Task<IActionResult> Activate(long id)
  93. {
  94. await _service.ActivateAsync(id, Operator());
  95. return Ok(new { ok = true });
  96. }
  97. /// <summary>停用。</summary>
  98. [HttpPost("{id:long}/retire")]
  99. public async Task<IActionResult> Retire(long id)
  100. {
  101. await _service.RetireAsync(id, Operator());
  102. return Ok(new { ok = true });
  103. }
  104. /// <summary>手动触发一次维度执行(通用入口 RunDimensionAsync,TriggerType=MANUAL)。</summary>
  105. [HttpPost("run/{metricCode}")]
  106. public async Task<IActionResult> Run(
  107. string metricCode, [FromQuery] string moduleCode, [FromQuery] DateTime? valueDate, CancellationToken ct)
  108. {
  109. if (string.IsNullOrWhiteSpace(metricCode) || string.IsNullOrWhiteSpace(moduleCode))
  110. return BadRequest(new { message = "metricCode / moduleCode 必填" });
  111. var tenantId = AdoSmartOpsKpiCalcConfigService.ResolveKpiTenantId(moduleCode);
  112. var vd = (valueDate ?? DateTime.Today.AddDays(-1)).Date;
  113. var batchId = $"DIM_MANUAL_{metricCode}_{vd:yyyyMMdd}_{DateTime.Now:HHmmssfff}";
  114. var res = await _run.RunDimensionAsync(metricCode, moduleCode, tenantId, vd, batchId, "MANUAL", ct);
  115. return Ok(res);
  116. }
  117. /// <summary>维度明细只读查询(当前生效维度版本;含按 AggregationType 复算的聚合值 + 分页明细)。</summary>
  118. [HttpGet("detail/{metricCode}")]
  119. public async Task<IActionResult> Detail(
  120. string metricCode, [FromQuery] string moduleCode,
  121. [FromQuery] DateTime? startDate, [FromQuery] DateTime? endDate,
  122. [FromQuery] string? dimensionType, [FromQuery] string? dimensionCode, [FromQuery] string? orderNo,
  123. [FromQuery] string? sortField, [FromQuery] string? sortOrder,
  124. [FromQuery] int page = 1, [FromQuery] int pageSize = 20)
  125. {
  126. if (string.IsNullOrWhiteSpace(metricCode) || string.IsNullOrWhiteSpace(moduleCode))
  127. return BadRequest(new { message = "metricCode / moduleCode 必填" });
  128. if (pageSize > 200) pageSize = 200; // 分页上限,防大结果
  129. return Ok(await _service.QueryDetailAsync(metricCode, moduleCode, startDate, endDate,
  130. dimensionType, dimensionCode, page, pageSize, orderNo, sortField, sortOrder));
  131. }
  132. /// <summary>某 KPI 的维度运行日志(分页,脱敏,租户后端解析)。</summary>
  133. [HttpGet("run-log/{metricCode}")]
  134. public async Task<IActionResult> RunLog(
  135. string metricCode, [FromQuery] string moduleCode,
  136. [FromQuery] int page = 1, [FromQuery] int pageSize = 20, [FromQuery] string? status = null)
  137. {
  138. if (string.IsNullOrWhiteSpace(metricCode) || string.IsNullOrWhiteSpace(moduleCode))
  139. return BadRequest(new { message = "metricCode / moduleCode 必填" });
  140. var tenantId = AdoSmartOpsKpiCalcConfigService.ResolveKpiTenantId(moduleCode);
  141. RefAsync<int> total = 0;
  142. var list = await _db.Queryable<AdoSmartOpsKpiDimensionRunLog>().ClearFilter<ITenantIdFilter>()
  143. .Where(x => x.MetricCode == metricCode && x.TenantId == tenantId)
  144. .WhereIF(!string.IsNullOrWhiteSpace(status), x => x.Status == status)
  145. .OrderBy(x => x.StartedAt, OrderByType.Desc)
  146. .ToPageListAsync(page <= 0 ? 1 : page, pageSize <= 0 ? 20 : pageSize, total);
  147. return Ok(new { list, total = total.Value });
  148. }
  149. }