S8DataSourceService.cs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. using Admin.NET.Plugin.AiDOP.Entity.S8;
  2. using System.Text.RegularExpressions;
  3. namespace Admin.NET.Plugin.AiDOP.Service.S8;
  4. public class S8DataSourceService : ITransient
  5. {
  6. private readonly SqlSugarRepository<AdoS8DataSource> _rep;
  7. public S8DataSourceService(SqlSugarRepository<AdoS8DataSource> rep) => _rep = rep;
  8. public async Task<List<AdoS8DataSource>> ListAsync(long tenantId, long factoryId)
  9. {
  10. var rows = await _rep.AsQueryable()
  11. .Where(x => x.TenantId == tenantId && x.FactoryId == factoryId)
  12. .ToListAsync();
  13. foreach (var r in rows) r.Endpoint = MaskSecret(r.Endpoint);
  14. return rows;
  15. }
  16. public async Task<AdoS8DataSource> CreateAsync(AdoS8DataSource body)
  17. {
  18. if (string.IsNullOrWhiteSpace(body.DataSourceCode) || string.IsNullOrWhiteSpace(body.Type))
  19. throw new S8BizException("数据源编码和类型必填");
  20. var exists = await _rep.AsQueryable()
  21. .AnyAsync(x => x.TenantId == body.TenantId && x.FactoryId == body.FactoryId && x.DataSourceCode == body.DataSourceCode);
  22. if (exists) throw new S8BizException("数据源编码已存在");
  23. body.Id = 0;
  24. body.CreatedAt = DateTime.Now;
  25. await _rep.InsertAsync(body);
  26. body.Endpoint = MaskSecret(body.Endpoint);
  27. return body;
  28. }
  29. public async Task<AdoS8DataSource> UpdateAsync(long id, AdoS8DataSource body)
  30. {
  31. var e = await _rep.GetByIdAsync(id) ?? throw new S8BizException("记录不存在");
  32. if (string.IsNullOrWhiteSpace(body.DataSourceCode) || string.IsNullOrWhiteSpace(body.Type))
  33. throw new S8BizException("数据源编码和类型必填");
  34. var exists = await _rep.AsQueryable()
  35. .AnyAsync(x => x.Id != id && x.TenantId == body.TenantId && x.FactoryId == body.FactoryId && x.DataSourceCode == body.DataSourceCode);
  36. if (exists) throw new S8BizException("数据源编码已存在");
  37. // 入参 endpoint 含掩码占位符(Pwd=****** / Password=******)时保留旧值的真实密码段,避免前端
  38. // 回填脱敏值后误覆盖。Endpoint 全空时也不覆盖原密码。
  39. body.Endpoint = MergeEndpointPreservingSecret(body.Endpoint, e.Endpoint);
  40. body.Id = id;
  41. body.CreatedAt = e.CreatedAt;
  42. body.UpdatedAt = DateTime.Now;
  43. await _rep.UpdateAsync(body);
  44. body.Endpoint = MaskSecret(body.Endpoint);
  45. return body;
  46. }
  47. public async Task DeleteAsync(long id) => await _rep.DeleteByIdAsync(id);
  48. public async Task<object> TestAsync(long id)
  49. {
  50. var entity = await _rep.GetByIdAsync(id) ?? throw new S8BizException("记录不存在");
  51. var success = !string.IsNullOrWhiteSpace(entity.Endpoint);
  52. entity.LastCheckAt = DateTime.Now;
  53. entity.LastCheckStatus = success ? "SUCCESS" : "FAILED: endpoint is empty";
  54. entity.UpdatedAt = DateTime.Now;
  55. await _rep.UpdateAsync(entity);
  56. return new
  57. {
  58. id,
  59. success,
  60. message = success ? "连接信息校验通过" : "连接地址为空,未通过校验",
  61. entity.LastCheckAt,
  62. entity.LastCheckStatus
  63. };
  64. }
  65. // BUG-13:endpoint 中的 Pwd=xxx / Password=xxx(大小写不敏感)替换为 ******,保留其它字段。
  66. private static readonly Regex SecretPattern = new(
  67. @"(?i)(Pwd|Password)\s*=\s*([^;]*)",
  68. RegexOptions.Compiled);
  69. private static string? MaskSecret(string? endpoint)
  70. {
  71. if (string.IsNullOrWhiteSpace(endpoint)) return endpoint;
  72. return SecretPattern.Replace(endpoint, m => $"{m.Groups[1].Value}=******");
  73. }
  74. private static string? MergeEndpointPreservingSecret(string? incoming, string? existing)
  75. {
  76. if (string.IsNullOrWhiteSpace(incoming)) return existing;
  77. if (string.IsNullOrWhiteSpace(existing)) return incoming;
  78. // 提取旧 endpoint 中的真实密码值(首个匹配为准)
  79. var oldMatch = SecretPattern.Match(existing);
  80. if (!oldMatch.Success) return incoming;
  81. var realSecret = oldMatch.Groups[2].Value;
  82. // 把入参里 Pwd=****** 之类的占位还原为真实密码
  83. return SecretPattern.Replace(incoming, m =>
  84. {
  85. var v = m.Groups[2].Value;
  86. return IsMaskedPlaceholder(v) ? $"{m.Groups[1].Value}={realSecret}" : m.Value;
  87. });
  88. }
  89. private static bool IsMaskedPlaceholder(string? v) =>
  90. !string.IsNullOrEmpty(v) && v.All(c => c == '*');
  91. }