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 });
}
}