using Admin.NET.Plugin.AiDOP.Entity.S8;
using Admin.NET.Plugin.AiDOP.Infrastructure;
using System.Text.RegularExpressions;
namespace Admin.NET.Plugin.AiDOP.Service.S8;
///
/// S8 数据源配置服务。
///
/// S8-LEGACY-RUNTIME-RETIREMENT-1:本服务已退化为只读。
///
/// 产品裁决:S8 = 数据中台**消费层**,不是数据接入层。
///
/// Data Platform → Governed Dataset → S8 Provider → Canonical Contract → Watch Rule
///
/// 物理数据源(连接串 / 主机 / 端口 / 凭据)的治理权归数据平台,不由租户 S8 管理员维护。
///
/// 为什么不能只隐藏 UI:ado_s8_data_source.endpoint 存的是**含凭据的真实连接串**,
/// 而 API 仍可被直接调用。写能力留着就等于「入口看不见但仍可写库、仍可让 S8 发起对外连接」。
/// 故在服务层(写入的唯一收敛点)统一拒绝。
///
/// 为什么连接测试也退役:TestAsync 虽只写 last_check_*,但它会用库里的 endpoint
/// **真实建立外部数据库连接** —— 那正是本批要终止的「S8 维护物理连接」能力,
/// 比单纯的行更新更需要关掉。随之移除 S8SqlSugarScopeFactory 依赖:
/// 本服务从此**在结构上**不具备发起外连的能力,而不只是"约定不用"。
///
/// 保留的部分:
/// · —— 存量 Legacy 规则仍绑着这些行,排障与历史核对需要能看到(endpoint 仍脱敏);
/// · 写方法签名 —— 硬约束,S8TenantIsolationContractTests 用反射断言带
/// S8TrustedScope 的写入口存在、且无 scope 的旧重载不存在;
/// · ado_s8_data_source 表 / S8DataSourceRowLoader / S8LegacySqlDataProvider ——
/// 本批只做 Runtime Retirement,物理清理留待后续独立批次。
///
public class S8DataSourceService : ITransient
{
private readonly SqlSugarRepository _rep;
public S8DataSourceService(SqlSugarRepository rep)
{
_rep = rep;
}
/// 数据源写入口退役后的统一文案。与 Rule 侧退役文案分开:二者是两条不同的产品裁决。
public const string RetiredMessage =
"S8 已切换为数据中台 Dataset 消费模式,物理数据源由数据平台治理,"
+ "S8 侧不再提供数据源的新增 / 修改 / 删除 / 连接测试。";
///
/// 只读列出本作用域的数据源。endpoint 中的 Pwd / Password 一律脱敏后返回。
///
public async Task> ListAsync(long tenantId, long factoryId)
{
var rows = await _rep.AsQueryable()
.Where(x => x.TenantId == tenantId && x.FactoryId == factoryId)
.ToListAsync();
foreach (var r in rows) r.Endpoint = MaskSecret(r.Endpoint);
return rows;
}
// ================================================================================
// 写入口:全部退役。
//
// ⚠️ 抛出发生在**任何 DB 访问之前**:不做 LoadScopedAsync、不做重复性查询。
// 这既保证零 DB 触碰,也保证任意 id(含越权 id)一律 410 而非 404
// ——「这个能力没了」优先于「这条记录不属于你」,
// 避免用越权探测反推他租户资源是否存在。
// ================================================================================
public Task CreateAsync(AdoS8DataSource body, S8TrustedScope scope) =>
throw new S8WriteRetiredException(RetiredMessage);
public Task UpdateAsync(long id, AdoS8DataSource body, S8TrustedScope scope) =>
throw new S8WriteRetiredException(RetiredMessage);
public Task DeleteAsync(long id, S8TrustedScope scope) =>
throw new S8WriteRetiredException(RetiredMessage);
public Task