refactor(monitoring): 重构领域实体命名和枚举类型

- 将 AlertRule 重命名为 AlertRuleEntity
- 将 ExternalSystem 重命名为 ExternalSystemEntity
- 将 SyncJob 重命名为 SyncJobEntity
- 将 SyncedRecord 重命名为 SyncedRecordEntity
- 将 AlertSeverity 重命名为 AlertSeverityEnum
- 将 ExternalSystemType 重命名为 ExternalSystemTypeEnum
- 将 SyncJobStatus 重命名为 SyncJobStatusEnum
- 将相关实体的命名空间从 Entities 移至 Values
- 更新所有引用这些类型的代码以使用新名称
- 修改数据访问层配置以使用新的实体类型名称
This commit is contained in:
xiangning 2026-07-19 13:13:24 +08:00
parent a11330936a
commit 6b7116fafd
31 changed files with 159 additions and 103 deletions

View File

@ -7,7 +7,7 @@ namespace BuildFlow.Host.Services.BackgroundServices;
/// <summary>
/// 监控同步后台调度器:按 MonitoringOptions.Scheduler.PollIntervalSeconds 轮询,
/// 找出到期的启用 ExternalSystem(LastSyncedAt + PollInterval &lt;= now),
/// 找出到期的启用 ExternalSystemEntity(LastSyncedAt + PollInterval &lt;= now),
/// 调用 SyncProcessor 执行同步。
///
/// 设计要点(与 WebhookDispatcherService 一致):

View File

@ -1,4 +1,8 @@
using Monitoring.Domain.Entities;
using Monitoring.Domain.Entities.AlertRule;
using Monitoring.Domain.Entities.ExternalSystem;
using Monitoring.Domain.Entities.SyncJob;
using Monitoring.Domain.Entities.SyncedRecord;
using Monitoring.Domain.Values;
namespace Monitoring.Application.ExternalSystems.DTOs;

View File

@ -3,7 +3,11 @@ using BuildFlow.Shared.Domain.Errors;
using MediatR;
using Monitoring.Application.ExternalSystems.Commands;
using Monitoring.Application.ExternalSystems.DTOs;
using Monitoring.Domain.Entities;
using Monitoring.Domain.Entities.AlertRule;
using Monitoring.Domain.Entities.ExternalSystem;
using Monitoring.Domain.Entities.SyncJob;
using Monitoring.Domain.Entities.SyncedRecord;
using Monitoring.Domain.Values;
using Monitoring.Domain.Enums;
using Monitoring.Infrastructure.Persistence;
using System.Text.Json;
@ -16,15 +20,15 @@ public class CreateExternalSystemCommandHandler(MonitoringDbContext db)
{
public async Task<ExternalSystemDto> Handle(CreateExternalSystemCommand request, CancellationToken ct)
{
if (!Enum.IsDefined(typeof(ExternalSystemType), request.SystemType))
if (!Enum.IsDefined(typeof(ExternalSystemTypeEnum), request.SystemType))
throw new BusinessException(ErrorCode.InvalidParam, $"无效的系统类型: {request.SystemType}");
var systemType = (ExternalSystemType)request.SystemType;
var systemType = (ExternalSystemTypeEnum)request.SystemType;
var parameters = string.IsNullOrEmpty(request.ParametersJson)
? new Dictionary<string, string?>()
: JsonSerializer.Deserialize<Dictionary<string, string?>>(request.ParametersJson) ?? new();
var system = new ExternalSystem
var system = new ExternalSystemEntity
{
Data = new SystemData
{

View File

@ -26,7 +26,7 @@ public class LoginExternalSystemCommandHandler(
?? throw new NotFoundException(ErrorCode.NotFound, $"外部系统 {request.SystemId} 不存在");
// 目前仅支持 u号租自动登录
if (system.Data.SystemType != ExternalSystemType.UHaoZu)
if (system.Data.SystemType != ExternalSystemTypeEnum.UHaoZu)
{
return new LoginResult(false, $"{system.Data.SystemType} 类型暂不支持自动登录", 0);
}

View File

@ -5,7 +5,11 @@ using MediatR;
using Microsoft.EntityFrameworkCore;
using Monitoring.Application.ExternalSystems.Commands;
using Monitoring.Application.ExternalSystems.DTOs;
using Monitoring.Domain.Entities;
using Monitoring.Domain.Entities.AlertRule;
using Monitoring.Domain.Entities.ExternalSystem;
using Monitoring.Domain.Entities.SyncJob;
using Monitoring.Domain.Entities.SyncedRecord;
using Monitoring.Domain.Values;
using Monitoring.Infrastructure.Persistence;
namespace Monitoring.Application.ExternalSystems.Handlers;

View File

@ -12,7 +12,7 @@ public class MonitoringOptions
/// <summary>调度器配置节。</summary>
public class SchedulerSection
{
/// <summary>轮询间隔(秒)。调度器按此频率扫描到期的 ExternalSystem。</summary>
/// <summary>轮询间隔(秒)。调度器按此频率扫描到期的 ExternalSystemEntity。</summary>
public int PollIntervalSeconds { get; set; } = 60;
/// <summary>应用启动后延迟多少秒开始首次扫描,避免与启动初始化争抢资源。</summary>

View File

@ -23,7 +23,7 @@ public class GetSyncSummaryQueryHandler(MonitoringDbContext db)
// 最近一次成功的汇总指标(json_data 在表达式树里翻译不了,先物化再读 Data)
var latestSuccess = await db.SyncJobs
.Where(j => j.ExternalSystemId == request.ExternalSystemId && j.Status == SyncJobStatus.Succeeded)
.Where(j => j.ExternalSystemId == request.ExternalSystemId && j.Status == SyncJobStatusEnum.Succeeded)
.OrderByDescending(j => j.FinishedAt)
.FirstOrDefaultAsync(ct);
var metrics = latestSuccess?.Data.SummaryMetrics ?? new Dictionary<string, object?>();

View File

@ -3,7 +3,7 @@ using Monitoring.Application.Reports.DTOs;
namespace Monitoring.Application.Reports.Queries;
/// <summary>查询某外部系统按天的记录数趋势(基于 SyncedRecord.OccurredAt 聚合)。</summary>
/// <summary>查询某外部系统按天的记录数趋势(基于 SyncedRecordEntity.OccurredAt 聚合)。</summary>
/// <param name="ExternalSystemId">外部系统 Id。</param>
/// <param name="Days">最近 N 天,默认 14。</param>
public record GetDailyTrendQuery(Guid ExternalSystemId, int Days = 14)

View File

@ -3,6 +3,6 @@ using Monitoring.Application.Sync.DTOs;
namespace Monitoring.Application.Sync.Commands;
/// <summary>手动触发某外部系统立即同步。返回本次 SyncJob 概览。</summary>
/// <summary>手动触发某外部系统立即同步。返回本次 SyncJobEntity 概览。</summary>
/// <param name="ExternalSystemId">目标外部系统 Id。</param>
public record TriggerSyncCommand(Guid ExternalSystemId) : IRequest<SyncJobDto>;

View File

@ -2,7 +2,11 @@ using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Monitoring.Domain.Entities;
using Monitoring.Domain.Entities.AlertRule;
using Monitoring.Domain.Entities.ExternalSystem;
using Monitoring.Domain.Entities.SyncJob;
using Monitoring.Domain.Entities.SyncedRecord;
using Monitoring.Domain.Values;
using Monitoring.Domain.Enums;
using Monitoring.Domain.Services;
using Monitoring.Infrastructure.Persistence;
@ -10,12 +14,12 @@ using Monitoring.Infrastructure.Persistence;
namespace Monitoring.Application.Sync;
/// <summary>
/// 同步编排核心。从 ExternalSystem 路由到对应 IExternalConnector,执行拉取→落库→记 SyncJob,
/// 同步编排核心。从 ExternalSystemEntity 路由到对应 IExternalConnector,执行拉取→落库→记 SyncJobEntity,
/// 并在成功后触发告警评估。纯业务逻辑、无 HostedService 依赖,便于单元测试。
///
/// 设计要点:
/// 1. Connector 路由:从 IServiceProvider 取 IEnumerable&lt;IExternalConnector&gt;,按 SystemType 选实现;
/// 2. 幂等落库:SyncedRecord 按 (ExternalSystemId, ExternalId, RecordType) UPSERT,重复拉取只保留最新快照;
/// 2. 幂等落库:SyncedRecordEntity 按 (ExternalSystemId, ExternalId, RecordType) UPSERT,重复拉取只保留最新快照;
/// 3. 失败隔离:异常记录为 Failed 不抛出,单个系统失败不影响调度器后续系统。
/// json_data 模式:所有业务字段经实体 .Data 强类型视图读写,自动序列化进 JsonData 列。
/// </summary>
@ -24,8 +28,8 @@ public class SyncProcessor(
IServiceProvider services,
ILogger<SyncProcessor> logger)
{
/// <summary>对单个外部系统执行一次同步。返回本次 SyncJob 记录(已落库)。</summary>
public async Task<SyncJob> ExecuteAsync(ExternalSystem system, string triggerSource, CancellationToken ct)
/// <summary>对单个外部系统执行一次同步。返回本次 SyncJobEntity 记录(已落库)。</summary>
public async Task<SyncJobEntity> ExecuteAsync(ExternalSystemEntity system, string triggerSource, CancellationToken ct)
{
var systemType = system.Data.SystemType;
@ -38,10 +42,10 @@ public class SyncProcessor(
}
// 2. 创建并标记 Running
var job = new SyncJob
var job = new SyncJobEntity
{
ExternalSystemId = system.Id,
Status = SyncJobStatus.Running,
Status = SyncJobStatusEnum.Running,
StartedAt = DateTime.UtcNow,
Data = new JobData { TriggerSource = triggerSource },
};
@ -53,11 +57,11 @@ public class SyncProcessor(
{
var result = await connector.FetchAsync(system, ct);
// 4. 幂等落库 SyncedRecord
// 4. 幂等落库 SyncedRecordEntity
await PersistRecordsAsync(system.Id, job.Id, result.Records, ct);
// 5. 更新 SyncJob 为 Succeeded(汇总指标写入 json_data)
job.Status = SyncJobStatus.Succeeded;
// 5. 更新 SyncJobEntity 为 Succeeded(汇总指标写入 json_data)
job.Status = SyncJobStatusEnum.Succeeded;
job.FinishedAt = DateTime.UtcNow;
job.DurationMs = (long)(job.FinishedAt.Value - job.StartedAt!.Value).TotalMilliseconds;
job.RecordCount = result.RecordCount;
@ -69,7 +73,7 @@ public class SyncProcessor(
};
await db.SaveChangesAsync(ct);
// 6. 更新 ExternalSystem 的最近同步信息(写入 json_data)
// 6. 更新 ExternalSystemEntity 的最近同步信息(写入 json_data)
var sysData = system.Data;
sysData.LastSyncedAt = new DateTimeOffset(job.FinishedAt.Value).ToUnixTimeMilliseconds();
sysData.LastSyncJobId = job.Id;
@ -124,7 +128,7 @@ public class SyncProcessor(
}
else
{
db.SyncedRecords.Add(new SyncedRecord
db.SyncedRecords.Add(new SyncedRecordEntity
{
ExternalSystemId = systemId,
SyncJobId = jobId,
@ -171,16 +175,16 @@ public class SyncProcessor(
}
/// <summary>记录失败任务(job 已存在则更新,否则新建)。</summary>
private async Task<SyncJob> RecordFailureAsync(
ExternalSystem system, string triggerSource, string error, SyncJob? job, CancellationToken ct)
private async Task<SyncJobEntity> RecordFailureAsync(
ExternalSystemEntity system, string triggerSource, string error, SyncJobEntity? job, CancellationToken ct)
{
var now = DateTime.UtcNow;
if (job is null)
{
job = new SyncJob
job = new SyncJobEntity
{
ExternalSystemId = system.Id,
Status = SyncJobStatus.Failed,
Status = SyncJobStatusEnum.Failed,
StartedAt = now,
Data = new JobData { TriggerSource = triggerSource },
};
@ -188,7 +192,7 @@ public class SyncProcessor(
}
else
{
job.Status = SyncJobStatus.Failed;
job.Status = SyncJobStatusEnum.Failed;
}
job.FinishedAt = now;
job.DurationMs = job.StartedAt.HasValue ? (long)(now - job.StartedAt.Value).TotalMilliseconds : 0;

View File

@ -3,7 +3,7 @@ namespace Monitoring.Domain.Enums;
/// <summary>
/// 告警严重级别。影响通知渠道与展示排序(Critical 通常触发即时推送,Info 仅记录)。
/// </summary>
public enum AlertSeverity
public enum AlertSeverityEnum
{
/// <summary>信息级:仅记录,不打扰。</summary>
Info = 0,

View File

@ -6,7 +6,7 @@ namespace Monitoring.Domain.Enums;
/// 1) 在此枚举登记;
/// 2) 实现 IExternalConnector 并在 DI 注册。
/// </summary>
public enum ExternalSystemType
public enum ExternalSystemTypeEnum
{
/// <summary>u号租商家后台(订单/资金报表拉取)。</summary>
UHaoZu = 1,

View File

@ -6,7 +6,7 @@ namespace Monitoring.Domain.Enums;
/// ↘ Failed
/// 由 SyncProcessor 在执行前后驱动转换,SyncedRecord/SyncJob 据此统计成功率与历史。
/// </summary>
public enum SyncJobStatus
public enum SyncJobStatusEnum
{
/// <summary>已入队待执行。</summary>
Pending = 0,

View File

@ -1,4 +1,8 @@
using Monitoring.Domain.Entities;
using Monitoring.Domain.Entities.AlertRule;
using Monitoring.Domain.Entities.ExternalSystem;
using Monitoring.Domain.Entities.SyncJob;
using Monitoring.Domain.Entities.SyncedRecord;
using Monitoring.Domain.Values;
using Monitoring.Domain.Enums;
namespace Monitoring.Domain.Services;
@ -11,13 +15,13 @@ namespace Monitoring.Domain.Services;
public interface IExternalConnector
{
/// <summary>本连接器处理的系统类型。ConnectorRegistry 据此建立 SystemType → 实现的路由表。</summary>
ExternalSystemType SystemType { get; }
ExternalSystemTypeEnum SystemType { get; }
/// <summary>
/// 执行一次拉取。从 <paramref name="system"/> 读取 BaseUrl/参数与关联凭证,
/// 调用外部接口,返回汇总指标 + 明细记录。失败应抛异常,由 SyncProcessor 统一记录为 Failed。
/// </summary>
Task<FetchResult> FetchAsync(ExternalSystem system, CancellationToken ct);
Task<FetchResult> FetchAsync(ExternalSystemEntity system, CancellationToken ct);
}
/// <summary>
@ -25,7 +29,7 @@ public interface IExternalConnector
/// 明细记录放 Records(如各订单的原始 JSON)。
/// </summary>
/// <param name="RecordCount">本次拉取的明细行数。</param>
/// <param name="SummaryMetrics">汇总指标键值对(值为数值或字符串,序列化进 SyncJob.SummaryMetricsJson)。</param>
/// <param name="SummaryMetrics">汇总指标键值对(值为数值或字符串,序列化进 SyncJobEntity.SummaryMetricsJson)。</param>
/// <param name="Records">明细记录列表;若仅拉汇总可为空。</param>
public record FetchResult(
int RecordCount,
@ -33,7 +37,7 @@ public record FetchResult(
IReadOnlyList<SyncedRecordData> Records);
/// <summary>
/// 一条明细记录数据(由 Connector 产出,SyncProcessor 据此落库 SyncedRecord)。
/// 一条明细记录数据(由 Connector 产出,SyncProcessor 据此落库 SyncedRecordEntity)。
/// </summary>
/// <param name="ExternalId">外部业务 Id(订单号/流水号),幂等去重依据。</param>
/// <param name="RecordType">记录类型("order" / "finance" 等)。</param>

View File

@ -1,4 +1,8 @@
using Monitoring.Domain.Entities;
using Monitoring.Domain.Entities.AlertRule;
using Monitoring.Domain.Entities.ExternalSystem;
using Monitoring.Domain.Entities.SyncJob;
using Monitoring.Domain.Entities.SyncedRecord;
using Monitoring.Domain.Values;
namespace Monitoring.Domain.Services;
@ -15,7 +19,7 @@ public interface IAlertEvaluator
/// <param name="summaryMetrics">最新一次同步的汇总指标。</param>
/// <param name="rules">该系统下启用的告警规则。</param>
/// <returns>命中的规则列表(调用方负责后续通知与 LastTriggeredAt 更新)。</returns>
IReadOnlyList<AlertRule> Evaluate(
IReadOnlyList<AlertRuleEntity> Evaluate(
Dictionary<string, object?> summaryMetrics,
IReadOnlyList<AlertRule> rules);
IReadOnlyList<AlertRuleEntity> rules);
}

View File

@ -1,4 +1,4 @@
namespace Monitoring.Domain.Entities;
namespace Monitoring.Domain.Values;
/// <summary>凭证条目(内聚于 SystemData,无独立表)。</summary>
public class CredentialEntry

View File

@ -1,4 +1,4 @@
namespace Monitoring.Domain.Entities;
namespace Monitoring.Domain.Values;
/// <summary>同步任务 json_data 投影。</summary>
public class JobData

View File

@ -1,4 +1,4 @@
namespace Monitoring.Domain.Entities;
namespace Monitoring.Domain.Values;
/// <summary>同步记录 json_data 投影:拉取到的外部原始数据。</summary>
public class RecordData

View File

@ -1,6 +1,6 @@
using Monitoring.Domain.Enums;
namespace Monitoring.Domain.Entities;
namespace Monitoring.Domain.Values;
/// <summary>告警规则 json_data 投影。</summary>
public class RuleData
@ -18,7 +18,7 @@ public class RuleData
public string Threshold { get; set; } = default!;
/// <summary>严重级别。</summary>
public AlertSeverity Severity { get; set; } = AlertSeverity.Warning;
public AlertSeverityEnum Severity { get; set; } = AlertSeverityEnum.Warning;
/// <summary>是否启用。</summary>
public bool IsEnabled { get; set; } = true;

View File

@ -1,5 +1,9 @@
using System.Globalization;
using Monitoring.Domain.Entities;
using Monitoring.Domain.Entities.AlertRule;
using Monitoring.Domain.Entities.ExternalSystem;
using Monitoring.Domain.Entities.SyncJob;
using Monitoring.Domain.Entities.SyncedRecord;
using Monitoring.Domain.Values;
using Monitoring.Domain.Services;
namespace Monitoring.Infrastructure.Alerting;
@ -7,17 +11,17 @@ namespace Monitoring.Infrastructure.Alerting;
/// <summary>
/// 默认告警评估器:对每条规则,从汇总指标取出 MetricName 的值,
/// 按 Operator 与 Threshold 比较;命中且度过冷却期则纳入返回列表。
/// 字段读取自 AlertRule.Data(json_data 强类型视图)。
/// 字段读取自 AlertRuleEntity.Data(json_data 强类型视图)。
/// 比较失败(指标缺失/非数值)的规则静默跳过,不视为命中也不抛异常。
/// </summary>
public class DefaultAlertEvaluator : IAlertEvaluator
{
public IReadOnlyList<AlertRule> Evaluate(
public IReadOnlyList<AlertRuleEntity> Evaluate(
Dictionary<string, object?> summaryMetrics,
IReadOnlyList<AlertRule> rules)
IReadOnlyList<AlertRuleEntity> rules)
{
var nowMs = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds();
var hit = new List<AlertRule>();
var hit = new List<AlertRuleEntity>();
foreach (var rule in rules)
{

View File

@ -1,13 +1,17 @@
using System.Text.Json;
using Monitoring.Domain.Entities;
using Monitoring.Domain.Entities.AlertRule;
using Monitoring.Domain.Entities.ExternalSystem;
using Monitoring.Domain.Entities.SyncJob;
using Monitoring.Domain.Entities.SyncedRecord;
using Monitoring.Domain.Values;
namespace Monitoring.Infrastructure.Connectors;
/// <summary>
/// Connector 公共辅助:从 ExternalSystem.JsonData 解析凭证与参数,构造带凭证的 HttpClient。
/// Connector 公共辅助:从 ExternalSystemEntity.JsonData 解析凭证与参数,构造带凭证的 HttpClient。
///
/// 设计取向:把 PG 当 Mongo 用——业务字段全在 jsonb,本类只做 JSON 反序列化 + HttpClient 拼装,
/// 不再访问 DbContext(凭证内聚于 ExternalSystem,无独立表)。
/// 不再访问 DbContext(凭证内聚于 ExternalSystemEntity,无独立表)。
///
/// HttpClient 命名约定:"Monitoring." + SystemType 名(已在 MonitoringInfrastructureModule 注册工厂)。
/// </summary>
@ -16,7 +20,7 @@ public class ConnectorCredentialResolver(IHttpClientFactory httpClientFactory)
private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNameCaseInsensitive = true };
/// <summary>创建挂好 BaseUrl + 凭证的 HttpClient。</summary>
public HttpClient CreateClient(ExternalSystem system)
public HttpClient CreateClient(ExternalSystemEntity system)
{
var clientName = $"Monitoring.{system.Data.SystemType}";
var client = httpClientFactory.CreateClient(clientName);

View File

@ -1,6 +1,10 @@
using System.Net.Http.Json;
using System.Text.Json;
using Monitoring.Domain.Entities;
using Monitoring.Domain.Entities.AlertRule;
using Monitoring.Domain.Entities.ExternalSystem;
using Monitoring.Domain.Entities.SyncJob;
using Monitoring.Domain.Entities.SyncedRecord;
using Monitoring.Domain.Values;
using Monitoring.Domain.Enums;
using Monitoring.Domain.Services;
@ -8,7 +12,7 @@ namespace Monitoring.Infrastructure.Connectors;
/// <summary>
/// 通用 HTTP Connector:POST JSON + 配置化解析。
/// 通过 ExternalSystem.Data.Parameters 配置 path/body/响应字段路径,覆盖未定制对接的简单接口。
/// 通过 ExternalSystemEntity.Data.Parameters 配置 path/body/响应字段路径,覆盖未定制对接的简单接口。
///
/// 配置示例(Parameters):
/// {
@ -19,9 +23,9 @@ namespace Monitoring.Infrastructure.Connectors;
/// </summary>
public class GenericHttpConnector(ConnectorCredentialResolver resolver) : IExternalConnector
{
public ExternalSystemType SystemType => ExternalSystemType.GenericHttp;
public ExternalSystemTypeEnum SystemType => ExternalSystemTypeEnum.GenericHttp;
public async Task<FetchResult> FetchAsync(ExternalSystem system, CancellationToken ct)
public async Task<FetchResult> FetchAsync(ExternalSystemEntity system, CancellationToken ct)
{
var p = system.Data.Parameters;
var path = p.GetValueOrDefault("path") ?? "/";

View File

@ -2,7 +2,11 @@ using System.Net.Http.Json;
using System.Text;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Monitoring.Domain.Entities;
using Monitoring.Domain.Entities.AlertRule;
using Monitoring.Domain.Entities.ExternalSystem;
using Monitoring.Domain.Entities.SyncJob;
using Monitoring.Domain.Entities.SyncedRecord;
using Monitoring.Domain.Values;
using Monitoring.Domain.Enums;
using Monitoring.Domain.Services;
@ -16,8 +20,8 @@ namespace Monitoring.Infrastructure.Connectors;
/// 1) POST {BaseUrl}/merchants/orderReport/ajax/selectOrderReport → 汇总指标
/// 2) POST {BaseUrl}/merchants/orderReport/ajax/orderReportList → 订单明细(分页全量)
///
/// 凭证:Cookie(JSESSIONID/uid 等)从 ExternalSystem.Data.Credentials 读取(json_data 内聚)。
/// 参数(ExternalSystem.Data.Parameters):reportPath/lookbackDays/maxPages。
/// 凭证:Cookie(JSESSIONID/uid 等)从 ExternalSystemEntity.Data.Credentials 读取(json_data 内聚)。
/// 参数(ExternalSystemEntity.Data.Parameters):reportPath/lookbackDays/maxPages。
/// 金额单位说明:外部接口金额字段单位为"分",本 Connector 统一除以100转"元"后入库。
/// </summary>
public class UHaoZuConnector(ConnectorCredentialResolver resolver, ILogger<UHaoZuConnector> logger)
@ -29,9 +33,9 @@ public class UHaoZuConnector(ConnectorCredentialResolver resolver, ILogger<UHaoZ
private const int DefaultMaxPages = 50;
private const int PageSize = 30;
public ExternalSystemType SystemType => ExternalSystemType.UHaoZu;
public ExternalSystemTypeEnum SystemType => ExternalSystemTypeEnum.UHaoZu;
public async Task<FetchResult> FetchAsync(ExternalSystem system, CancellationToken ct)
public async Task<FetchResult> FetchAsync(ExternalSystemEntity system, CancellationToken ct)
{
var p = system.Data.Parameters;
var reportPath = p.GetValueOrDefault("reportPath") ?? DefaultReportPath;

View File

@ -2,7 +2,11 @@ using System.Net;
using System.Text;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Monitoring.Domain.Entities;
using Monitoring.Domain.Entities.AlertRule;
using Monitoring.Domain.Entities.ExternalSystem;
using Monitoring.Domain.Entities.SyncJob;
using Monitoring.Domain.Entities.SyncedRecord;
using Monitoring.Domain.Values;
namespace Monitoring.Infrastructure.Connectors;
@ -30,7 +34,7 @@ public class UHaoZuLoginService(ILogger<UHaoZuLoginService> logger)
/// 执行登录,返回提取到的凭证列表(失败抛异常)。
/// </summary>
public async Task<List<CredentialEntry>> LoginAsync(
ExternalSystem system, string account, string password,
ExternalSystemEntity system, string account, string password,
string ticket, string randstr, CancellationToken ct)
{
// 用 HttpClientHandler 捕获 Set-Cookie。

View File

@ -1,13 +1,17 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Monitoring.Domain.Entities;
using Monitoring.Domain.Entities.AlertRule;
using Monitoring.Domain.Entities.ExternalSystem;
using Monitoring.Domain.Entities.SyncJob;
using Monitoring.Domain.Entities.SyncedRecord;
using Monitoring.Domain.Values;
namespace Monitoring.Infrastructure.Persistence.Configurations;
/// <summary>告警规则表映射(id + 外键索引列 + json_data + 审计)。</summary>
public class AlertRuleConfiguration : IEntityTypeConfiguration<AlertRule>
public class AlertRuleConfiguration : IEntityTypeConfiguration<AlertRuleEntity>
{
public void Configure(EntityTypeBuilder<AlertRule> builder)
public void Configure(EntityTypeBuilder<AlertRuleEntity> builder)
{
builder.ToTable("b_alert_rules");
builder.HasKey(e => e.Id);

View File

@ -1,13 +1,17 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Monitoring.Domain.Entities;
using Monitoring.Domain.Entities.AlertRule;
using Monitoring.Domain.Entities.ExternalSystem;
using Monitoring.Domain.Entities.SyncJob;
using Monitoring.Domain.Entities.SyncedRecord;
using Monitoring.Domain.Values;
namespace Monitoring.Infrastructure.Persistence.Configurations;
/// <summary>外部系统配置表映射(极简文档模式:id + json_data + 审计)。</summary>
public class ExternalSystemConfiguration : IEntityTypeConfiguration<ExternalSystem>
public class ExternalSystemConfiguration : IEntityTypeConfiguration<ExternalSystemEntity>
{
public void Configure(EntityTypeBuilder<ExternalSystem> builder)
public void Configure(EntityTypeBuilder<ExternalSystemEntity> builder)
{
builder.ToTable("b_external_systems");
builder.HasKey(e => e.Id);

View File

@ -1,13 +1,17 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Monitoring.Domain.Entities;
using Monitoring.Domain.Entities.AlertRule;
using Monitoring.Domain.Entities.ExternalSystem;
using Monitoring.Domain.Entities.SyncJob;
using Monitoring.Domain.Entities.SyncedRecord;
using Monitoring.Domain.Values;
namespace Monitoring.Infrastructure.Persistence.Configurations;
/// <summary>同步任务记录表映射(id + 外键索引列 + json_data + 审计)。</summary>
public class SyncJobConfiguration : IEntityTypeConfiguration<SyncJob>
public class SyncJobConfiguration : IEntityTypeConfiguration<SyncJobEntity>
{
public void Configure(EntityTypeBuilder<SyncJob> builder)
public void Configure(EntityTypeBuilder<SyncJobEntity> builder)
{
builder.ToTable("b_sync_jobs");
builder.HasKey(e => e.Id);

View File

@ -1,13 +1,17 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Monitoring.Domain.Entities;
using Monitoring.Domain.Entities.AlertRule;
using Monitoring.Domain.Entities.ExternalSystem;
using Monitoring.Domain.Entities.SyncJob;
using Monitoring.Domain.Entities.SyncedRecord;
using Monitoring.Domain.Values;
namespace Monitoring.Infrastructure.Persistence.Configurations;
/// <summary>同步记录表映射(id + 去重/分类/时间索引列 + json_data + 审计)。</summary>
public class SyncedRecordConfiguration : IEntityTypeConfiguration<SyncedRecord>
public class SyncedRecordConfiguration : IEntityTypeConfiguration<SyncedRecordEntity>
{
public void Configure(EntityTypeBuilder<SyncedRecord> builder)
public void Configure(EntityTypeBuilder<SyncedRecordEntity> builder)
{
builder.ToTable("b_synced_records");
builder.HasKey(e => e.Id);

View File

@ -1,6 +1,10 @@
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using Monitoring.Domain.Entities;
using Monitoring.Domain.Entities.AlertRule;
using Monitoring.Domain.Entities.ExternalSystem;
using Monitoring.Domain.Entities.SyncJob;
using Monitoring.Domain.Entities.SyncedRecord;
using Monitoring.Domain.Values;
namespace Monitoring.Infrastructure.Persistence;
@ -13,10 +17,10 @@ public class MonitoringDbContext : DbContext
{
public MonitoringDbContext(DbContextOptions<MonitoringDbContext> options) : base(options) { }
public DbSet<ExternalSystem> ExternalSystems => Set<ExternalSystem>();
public DbSet<SyncJob> SyncJobs => Set<SyncJob>();
public DbSet<SyncedRecord> SyncedRecords => Set<SyncedRecord>();
public DbSet<AlertRule> AlertRules => Set<AlertRule>();
public DbSet<ExternalSystemEntity> ExternalSystems => Set<ExternalSystemEntity>();
public DbSet<SyncJobEntity> SyncJobs => Set<SyncJobEntity>();
public DbSet<SyncedRecordEntity> SyncedRecords => Set<SyncedRecordEntity>();
public DbSet<AlertRuleEntity> AlertRules => Set<AlertRuleEntity>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{

View File

@ -11,18 +11,14 @@ public sealed class TimestampDateTimeConverter : JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
// 兼容数字(毫秒时间戳)与字符串(ISO 8601,向后兼容旧客户端)
if (reader.TokenType == JsonTokenType.Number)
return reader.TokenType switch
{
return DateTimeOffset.FromUnixTimeMilliseconds(reader.GetInt64()).UtcDateTime;
}
if (reader.TokenType == JsonTokenType.String)
{
return DateTime.Parse(reader.GetString()!, null, System.Globalization.DateTimeStyles.RoundtripKind);
}
throw new JsonException("Expected a number (ms timestamp) or string (ISO date) for DateTime field.");
// 兼容数字(毫秒时间戳)与字符串(ISO 8601,向后兼容旧客户端)
JsonTokenType.Number => DateTimeOffset.FromUnixTimeMilliseconds(reader.GetInt64()).UtcDateTime,
JsonTokenType.String => DateTime.Parse(reader.GetString()!, null,
System.Globalization.DateTimeStyles.RoundtripKind),
_ => throw new JsonException("Expected a number (ms timestamp) or string (ISO date) for DateTime field.")
};
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)

View File

@ -11,17 +11,13 @@ public sealed class TimestampDateTimeOffsetConverter : JsonConverter<DateTimeOff
{
public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Number)
return reader.TokenType switch
{
return DateTimeOffset.FromUnixTimeMilliseconds(reader.GetInt64());
}
if (reader.TokenType == JsonTokenType.String)
{
return DateTimeOffset.Parse(reader.GetString()!);
}
throw new JsonException("Expected a number (ms timestamp) or string (ISO date) for DateTimeOffset field.");
JsonTokenType.Number => DateTimeOffset.FromUnixTimeMilliseconds(reader.GetInt64()),
JsonTokenType.String => DateTimeOffset.Parse(reader.GetString()!),
_ => throw new JsonException(
"Expected a number (ms timestamp) or string (ISO date) for DateTimeOffset field.")
};
}
public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)