refactor(monitoring): 重构领域实体命名和枚举类型
- 将 AlertRule 重命名为 AlertRuleEntity - 将 ExternalSystem 重命名为 ExternalSystemEntity - 将 SyncJob 重命名为 SyncJobEntity - 将 SyncedRecord 重命名为 SyncedRecordEntity - 将 AlertSeverity 重命名为 AlertSeverityEnum - 将 ExternalSystemType 重命名为 ExternalSystemTypeEnum - 将 SyncJobStatus 重命名为 SyncJobStatusEnum - 将相关实体的命名空间从 Entities 移至 Values - 更新所有引用这些类型的代码以使用新名称 - 修改数据访问层配置以使用新的实体类型名称
This commit is contained in:
parent
a11330936a
commit
6b7116fafd
@ -7,7 +7,7 @@ namespace BuildFlow.Host.Services.BackgroundServices;
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 监控同步后台调度器:按 MonitoringOptions.Scheduler.PollIntervalSeconds 轮询,
|
/// 监控同步后台调度器:按 MonitoringOptions.Scheduler.PollIntervalSeconds 轮询,
|
||||||
/// 找出到期的启用 ExternalSystem(LastSyncedAt + PollInterval <= now),
|
/// 找出到期的启用 ExternalSystemEntity(LastSyncedAt + PollInterval <= now),
|
||||||
/// 调用 SyncProcessor 执行同步。
|
/// 调用 SyncProcessor 执行同步。
|
||||||
///
|
///
|
||||||
/// 设计要点(与 WebhookDispatcherService 一致):
|
/// 设计要点(与 WebhookDispatcherService 一致):
|
||||||
|
|||||||
@ -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;
|
namespace Monitoring.Application.ExternalSystems.DTOs;
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,11 @@ using BuildFlow.Shared.Domain.Errors;
|
|||||||
using MediatR;
|
using MediatR;
|
||||||
using Monitoring.Application.ExternalSystems.Commands;
|
using Monitoring.Application.ExternalSystems.Commands;
|
||||||
using Monitoring.Application.ExternalSystems.DTOs;
|
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.Domain.Enums;
|
||||||
using Monitoring.Infrastructure.Persistence;
|
using Monitoring.Infrastructure.Persistence;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
@ -16,15 +20,15 @@ public class CreateExternalSystemCommandHandler(MonitoringDbContext db)
|
|||||||
{
|
{
|
||||||
public async Task<ExternalSystemDto> Handle(CreateExternalSystemCommand request, CancellationToken ct)
|
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}");
|
throw new BusinessException(ErrorCode.InvalidParam, $"无效的系统类型: {request.SystemType}");
|
||||||
|
|
||||||
var systemType = (ExternalSystemType)request.SystemType;
|
var systemType = (ExternalSystemTypeEnum)request.SystemType;
|
||||||
var parameters = string.IsNullOrEmpty(request.ParametersJson)
|
var parameters = string.IsNullOrEmpty(request.ParametersJson)
|
||||||
? new Dictionary<string, string?>()
|
? new Dictionary<string, string?>()
|
||||||
: JsonSerializer.Deserialize<Dictionary<string, string?>>(request.ParametersJson) ?? new();
|
: JsonSerializer.Deserialize<Dictionary<string, string?>>(request.ParametersJson) ?? new();
|
||||||
|
|
||||||
var system = new ExternalSystem
|
var system = new ExternalSystemEntity
|
||||||
{
|
{
|
||||||
Data = new SystemData
|
Data = new SystemData
|
||||||
{
|
{
|
||||||
|
|||||||
@ -26,7 +26,7 @@ public class LoginExternalSystemCommandHandler(
|
|||||||
?? throw new NotFoundException(ErrorCode.NotFound, $"外部系统 {request.SystemId} 不存在");
|
?? throw new NotFoundException(ErrorCode.NotFound, $"外部系统 {request.SystemId} 不存在");
|
||||||
|
|
||||||
// 目前仅支持 u号租自动登录
|
// 目前仅支持 u号租自动登录
|
||||||
if (system.Data.SystemType != ExternalSystemType.UHaoZu)
|
if (system.Data.SystemType != ExternalSystemTypeEnum.UHaoZu)
|
||||||
{
|
{
|
||||||
return new LoginResult(false, $"{system.Data.SystemType} 类型暂不支持自动登录", 0);
|
return new LoginResult(false, $"{system.Data.SystemType} 类型暂不支持自动登录", 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,11 @@ using MediatR;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Monitoring.Application.ExternalSystems.Commands;
|
using Monitoring.Application.ExternalSystems.Commands;
|
||||||
using Monitoring.Application.ExternalSystems.DTOs;
|
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;
|
using Monitoring.Infrastructure.Persistence;
|
||||||
|
|
||||||
namespace Monitoring.Application.ExternalSystems.Handlers;
|
namespace Monitoring.Application.ExternalSystems.Handlers;
|
||||||
|
|||||||
@ -12,7 +12,7 @@ public class MonitoringOptions
|
|||||||
/// <summary>调度器配置节。</summary>
|
/// <summary>调度器配置节。</summary>
|
||||||
public class SchedulerSection
|
public class SchedulerSection
|
||||||
{
|
{
|
||||||
/// <summary>轮询间隔(秒)。调度器按此频率扫描到期的 ExternalSystem。</summary>
|
/// <summary>轮询间隔(秒)。调度器按此频率扫描到期的 ExternalSystemEntity。</summary>
|
||||||
public int PollIntervalSeconds { get; set; } = 60;
|
public int PollIntervalSeconds { get; set; } = 60;
|
||||||
|
|
||||||
/// <summary>应用启动后延迟多少秒开始首次扫描,避免与启动初始化争抢资源。</summary>
|
/// <summary>应用启动后延迟多少秒开始首次扫描,避免与启动初始化争抢资源。</summary>
|
||||||
|
|||||||
@ -23,7 +23,7 @@ public class GetSyncSummaryQueryHandler(MonitoringDbContext db)
|
|||||||
|
|
||||||
// 最近一次成功的汇总指标(json_data 在表达式树里翻译不了,先物化再读 Data)
|
// 最近一次成功的汇总指标(json_data 在表达式树里翻译不了,先物化再读 Data)
|
||||||
var latestSuccess = await db.SyncJobs
|
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)
|
.OrderByDescending(j => j.FinishedAt)
|
||||||
.FirstOrDefaultAsync(ct);
|
.FirstOrDefaultAsync(ct);
|
||||||
var metrics = latestSuccess?.Data.SummaryMetrics ?? new Dictionary<string, object?>();
|
var metrics = latestSuccess?.Data.SummaryMetrics ?? new Dictionary<string, object?>();
|
||||||
|
|||||||
@ -3,7 +3,7 @@ using Monitoring.Application.Reports.DTOs;
|
|||||||
|
|
||||||
namespace Monitoring.Application.Reports.Queries;
|
namespace Monitoring.Application.Reports.Queries;
|
||||||
|
|
||||||
/// <summary>查询某外部系统按天的记录数趋势(基于 SyncedRecord.OccurredAt 聚合)。</summary>
|
/// <summary>查询某外部系统按天的记录数趋势(基于 SyncedRecordEntity.OccurredAt 聚合)。</summary>
|
||||||
/// <param name="ExternalSystemId">外部系统 Id。</param>
|
/// <param name="ExternalSystemId">外部系统 Id。</param>
|
||||||
/// <param name="Days">最近 N 天,默认 14。</param>
|
/// <param name="Days">最近 N 天,默认 14。</param>
|
||||||
public record GetDailyTrendQuery(Guid ExternalSystemId, int Days = 14)
|
public record GetDailyTrendQuery(Guid ExternalSystemId, int Days = 14)
|
||||||
|
|||||||
@ -3,6 +3,6 @@ using Monitoring.Application.Sync.DTOs;
|
|||||||
|
|
||||||
namespace Monitoring.Application.Sync.Commands;
|
namespace Monitoring.Application.Sync.Commands;
|
||||||
|
|
||||||
/// <summary>手动触发某外部系统立即同步。返回本次 SyncJob 概览。</summary>
|
/// <summary>手动触发某外部系统立即同步。返回本次 SyncJobEntity 概览。</summary>
|
||||||
/// <param name="ExternalSystemId">目标外部系统 Id。</param>
|
/// <param name="ExternalSystemId">目标外部系统 Id。</param>
|
||||||
public record TriggerSyncCommand(Guid ExternalSystemId) : IRequest<SyncJobDto>;
|
public record TriggerSyncCommand(Guid ExternalSystemId) : IRequest<SyncJobDto>;
|
||||||
|
|||||||
@ -2,7 +2,11 @@ using System.Text.Json;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
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.Enums;
|
||||||
using Monitoring.Domain.Services;
|
using Monitoring.Domain.Services;
|
||||||
using Monitoring.Infrastructure.Persistence;
|
using Monitoring.Infrastructure.Persistence;
|
||||||
@ -10,12 +14,12 @@ using Monitoring.Infrastructure.Persistence;
|
|||||||
namespace Monitoring.Application.Sync;
|
namespace Monitoring.Application.Sync;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 同步编排核心。从 ExternalSystem 路由到对应 IExternalConnector,执行拉取→落库→记 SyncJob,
|
/// 同步编排核心。从 ExternalSystemEntity 路由到对应 IExternalConnector,执行拉取→落库→记 SyncJobEntity,
|
||||||
/// 并在成功后触发告警评估。纯业务逻辑、无 HostedService 依赖,便于单元测试。
|
/// 并在成功后触发告警评估。纯业务逻辑、无 HostedService 依赖,便于单元测试。
|
||||||
///
|
///
|
||||||
/// 设计要点:
|
/// 设计要点:
|
||||||
/// 1. Connector 路由:从 IServiceProvider 取 IEnumerable<IExternalConnector>,按 SystemType 选实现;
|
/// 1. Connector 路由:从 IServiceProvider 取 IEnumerable<IExternalConnector>,按 SystemType 选实现;
|
||||||
/// 2. 幂等落库:SyncedRecord 按 (ExternalSystemId, ExternalId, RecordType) UPSERT,重复拉取只保留最新快照;
|
/// 2. 幂等落库:SyncedRecordEntity 按 (ExternalSystemId, ExternalId, RecordType) UPSERT,重复拉取只保留最新快照;
|
||||||
/// 3. 失败隔离:异常记录为 Failed 不抛出,单个系统失败不影响调度器后续系统。
|
/// 3. 失败隔离:异常记录为 Failed 不抛出,单个系统失败不影响调度器后续系统。
|
||||||
/// json_data 模式:所有业务字段经实体 .Data 强类型视图读写,自动序列化进 JsonData 列。
|
/// json_data 模式:所有业务字段经实体 .Data 强类型视图读写,自动序列化进 JsonData 列。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -24,8 +28,8 @@ public class SyncProcessor(
|
|||||||
IServiceProvider services,
|
IServiceProvider services,
|
||||||
ILogger<SyncProcessor> logger)
|
ILogger<SyncProcessor> logger)
|
||||||
{
|
{
|
||||||
/// <summary>对单个外部系统执行一次同步。返回本次 SyncJob 记录(已落库)。</summary>
|
/// <summary>对单个外部系统执行一次同步。返回本次 SyncJobEntity 记录(已落库)。</summary>
|
||||||
public async Task<SyncJob> ExecuteAsync(ExternalSystem system, string triggerSource, CancellationToken ct)
|
public async Task<SyncJobEntity> ExecuteAsync(ExternalSystemEntity system, string triggerSource, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var systemType = system.Data.SystemType;
|
var systemType = system.Data.SystemType;
|
||||||
|
|
||||||
@ -38,10 +42,10 @@ public class SyncProcessor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. 创建并标记 Running
|
// 2. 创建并标记 Running
|
||||||
var job = new SyncJob
|
var job = new SyncJobEntity
|
||||||
{
|
{
|
||||||
ExternalSystemId = system.Id,
|
ExternalSystemId = system.Id,
|
||||||
Status = SyncJobStatus.Running,
|
Status = SyncJobStatusEnum.Running,
|
||||||
StartedAt = DateTime.UtcNow,
|
StartedAt = DateTime.UtcNow,
|
||||||
Data = new JobData { TriggerSource = triggerSource },
|
Data = new JobData { TriggerSource = triggerSource },
|
||||||
};
|
};
|
||||||
@ -53,11 +57,11 @@ public class SyncProcessor(
|
|||||||
{
|
{
|
||||||
var result = await connector.FetchAsync(system, ct);
|
var result = await connector.FetchAsync(system, ct);
|
||||||
|
|
||||||
// 4. 幂等落库 SyncedRecord
|
// 4. 幂等落库 SyncedRecordEntity
|
||||||
await PersistRecordsAsync(system.Id, job.Id, result.Records, ct);
|
await PersistRecordsAsync(system.Id, job.Id, result.Records, ct);
|
||||||
|
|
||||||
// 5. 更新 SyncJob 为 Succeeded(汇总指标写入 json_data)
|
// 5. 更新 SyncJobEntity 为 Succeeded(汇总指标写入 json_data)
|
||||||
job.Status = SyncJobStatus.Succeeded;
|
job.Status = SyncJobStatusEnum.Succeeded;
|
||||||
job.FinishedAt = DateTime.UtcNow;
|
job.FinishedAt = DateTime.UtcNow;
|
||||||
job.DurationMs = (long)(job.FinishedAt.Value - job.StartedAt!.Value).TotalMilliseconds;
|
job.DurationMs = (long)(job.FinishedAt.Value - job.StartedAt!.Value).TotalMilliseconds;
|
||||||
job.RecordCount = result.RecordCount;
|
job.RecordCount = result.RecordCount;
|
||||||
@ -69,7 +73,7 @@ public class SyncProcessor(
|
|||||||
};
|
};
|
||||||
await db.SaveChangesAsync(ct);
|
await db.SaveChangesAsync(ct);
|
||||||
|
|
||||||
// 6. 更新 ExternalSystem 的最近同步信息(写入 json_data)
|
// 6. 更新 ExternalSystemEntity 的最近同步信息(写入 json_data)
|
||||||
var sysData = system.Data;
|
var sysData = system.Data;
|
||||||
sysData.LastSyncedAt = new DateTimeOffset(job.FinishedAt.Value).ToUnixTimeMilliseconds();
|
sysData.LastSyncedAt = new DateTimeOffset(job.FinishedAt.Value).ToUnixTimeMilliseconds();
|
||||||
sysData.LastSyncJobId = job.Id;
|
sysData.LastSyncJobId = job.Id;
|
||||||
@ -124,7 +128,7 @@ public class SyncProcessor(
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
db.SyncedRecords.Add(new SyncedRecord
|
db.SyncedRecords.Add(new SyncedRecordEntity
|
||||||
{
|
{
|
||||||
ExternalSystemId = systemId,
|
ExternalSystemId = systemId,
|
||||||
SyncJobId = jobId,
|
SyncJobId = jobId,
|
||||||
@ -171,16 +175,16 @@ public class SyncProcessor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>记录失败任务(job 已存在则更新,否则新建)。</summary>
|
/// <summary>记录失败任务(job 已存在则更新,否则新建)。</summary>
|
||||||
private async Task<SyncJob> RecordFailureAsync(
|
private async Task<SyncJobEntity> RecordFailureAsync(
|
||||||
ExternalSystem system, string triggerSource, string error, SyncJob? job, CancellationToken ct)
|
ExternalSystemEntity system, string triggerSource, string error, SyncJobEntity? job, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var now = DateTime.UtcNow;
|
var now = DateTime.UtcNow;
|
||||||
if (job is null)
|
if (job is null)
|
||||||
{
|
{
|
||||||
job = new SyncJob
|
job = new SyncJobEntity
|
||||||
{
|
{
|
||||||
ExternalSystemId = system.Id,
|
ExternalSystemId = system.Id,
|
||||||
Status = SyncJobStatus.Failed,
|
Status = SyncJobStatusEnum.Failed,
|
||||||
StartedAt = now,
|
StartedAt = now,
|
||||||
Data = new JobData { TriggerSource = triggerSource },
|
Data = new JobData { TriggerSource = triggerSource },
|
||||||
};
|
};
|
||||||
@ -188,7 +192,7 @@ public class SyncProcessor(
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
job.Status = SyncJobStatus.Failed;
|
job.Status = SyncJobStatusEnum.Failed;
|
||||||
}
|
}
|
||||||
job.FinishedAt = now;
|
job.FinishedAt = now;
|
||||||
job.DurationMs = job.StartedAt.HasValue ? (long)(now - job.StartedAt.Value).TotalMilliseconds : 0;
|
job.DurationMs = job.StartedAt.HasValue ? (long)(now - job.StartedAt.Value).TotalMilliseconds : 0;
|
||||||
|
|||||||
@ -3,7 +3,7 @@ namespace Monitoring.Domain.Enums;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 告警严重级别。影响通知渠道与展示排序(Critical 通常触发即时推送,Info 仅记录)。
|
/// 告警严重级别。影响通知渠道与展示排序(Critical 通常触发即时推送,Info 仅记录)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum AlertSeverity
|
public enum AlertSeverityEnum
|
||||||
{
|
{
|
||||||
/// <summary>信息级:仅记录,不打扰。</summary>
|
/// <summary>信息级:仅记录,不打扰。</summary>
|
||||||
Info = 0,
|
Info = 0,
|
||||||
@ -6,7 +6,7 @@ namespace Monitoring.Domain.Enums;
|
|||||||
/// 1) 在此枚举登记;
|
/// 1) 在此枚举登记;
|
||||||
/// 2) 实现 IExternalConnector 并在 DI 注册。
|
/// 2) 实现 IExternalConnector 并在 DI 注册。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum ExternalSystemType
|
public enum ExternalSystemTypeEnum
|
||||||
{
|
{
|
||||||
/// <summary>u号租商家后台(订单/资金报表拉取)。</summary>
|
/// <summary>u号租商家后台(订单/资金报表拉取)。</summary>
|
||||||
UHaoZu = 1,
|
UHaoZu = 1,
|
||||||
@ -6,7 +6,7 @@ namespace Monitoring.Domain.Enums;
|
|||||||
/// ↘ Failed
|
/// ↘ Failed
|
||||||
/// 由 SyncProcessor 在执行前后驱动转换,SyncedRecord/SyncJob 据此统计成功率与历史。
|
/// 由 SyncProcessor 在执行前后驱动转换,SyncedRecord/SyncJob 据此统计成功率与历史。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum SyncJobStatus
|
public enum SyncJobStatusEnum
|
||||||
{
|
{
|
||||||
/// <summary>已入队待执行。</summary>
|
/// <summary>已入队待执行。</summary>
|
||||||
Pending = 0,
|
Pending = 0,
|
||||||
@ -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;
|
using Monitoring.Domain.Enums;
|
||||||
|
|
||||||
namespace Monitoring.Domain.Services;
|
namespace Monitoring.Domain.Services;
|
||||||
@ -11,13 +15,13 @@ namespace Monitoring.Domain.Services;
|
|||||||
public interface IExternalConnector
|
public interface IExternalConnector
|
||||||
{
|
{
|
||||||
/// <summary>本连接器处理的系统类型。ConnectorRegistry 据此建立 SystemType → 实现的路由表。</summary>
|
/// <summary>本连接器处理的系统类型。ConnectorRegistry 据此建立 SystemType → 实现的路由表。</summary>
|
||||||
ExternalSystemType SystemType { get; }
|
ExternalSystemTypeEnum SystemType { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 执行一次拉取。从 <paramref name="system"/> 读取 BaseUrl/参数与关联凭证,
|
/// 执行一次拉取。从 <paramref name="system"/> 读取 BaseUrl/参数与关联凭证,
|
||||||
/// 调用外部接口,返回汇总指标 + 明细记录。失败应抛异常,由 SyncProcessor 统一记录为 Failed。
|
/// 调用外部接口,返回汇总指标 + 明细记录。失败应抛异常,由 SyncProcessor 统一记录为 Failed。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task<FetchResult> FetchAsync(ExternalSystem system, CancellationToken ct);
|
Task<FetchResult> FetchAsync(ExternalSystemEntity system, CancellationToken ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -25,7 +29,7 @@ public interface IExternalConnector
|
|||||||
/// 明细记录放 Records(如各订单的原始 JSON)。
|
/// 明细记录放 Records(如各订单的原始 JSON)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="RecordCount">本次拉取的明细行数。</param>
|
/// <param name="RecordCount">本次拉取的明细行数。</param>
|
||||||
/// <param name="SummaryMetrics">汇总指标键值对(值为数值或字符串,序列化进 SyncJob.SummaryMetricsJson)。</param>
|
/// <param name="SummaryMetrics">汇总指标键值对(值为数值或字符串,序列化进 SyncJobEntity.SummaryMetricsJson)。</param>
|
||||||
/// <param name="Records">明细记录列表;若仅拉汇总可为空。</param>
|
/// <param name="Records">明细记录列表;若仅拉汇总可为空。</param>
|
||||||
public record FetchResult(
|
public record FetchResult(
|
||||||
int RecordCount,
|
int RecordCount,
|
||||||
@ -33,7 +37,7 @@ public record FetchResult(
|
|||||||
IReadOnlyList<SyncedRecordData> Records);
|
IReadOnlyList<SyncedRecordData> Records);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 一条明细记录数据(由 Connector 产出,SyncProcessor 据此落库 SyncedRecord)。
|
/// 一条明细记录数据(由 Connector 产出,SyncProcessor 据此落库 SyncedRecordEntity)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="ExternalId">外部业务 Id(订单号/流水号),幂等去重依据。</param>
|
/// <param name="ExternalId">外部业务 Id(订单号/流水号),幂等去重依据。</param>
|
||||||
/// <param name="RecordType">记录类型("order" / "finance" 等)。</param>
|
/// <param name="RecordType">记录类型("order" / "finance" 等)。</param>
|
||||||
|
|||||||
@ -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;
|
namespace Monitoring.Domain.Services;
|
||||||
|
|
||||||
@ -15,7 +19,7 @@ public interface IAlertEvaluator
|
|||||||
/// <param name="summaryMetrics">最新一次同步的汇总指标。</param>
|
/// <param name="summaryMetrics">最新一次同步的汇总指标。</param>
|
||||||
/// <param name="rules">该系统下启用的告警规则。</param>
|
/// <param name="rules">该系统下启用的告警规则。</param>
|
||||||
/// <returns>命中的规则列表(调用方负责后续通知与 LastTriggeredAt 更新)。</returns>
|
/// <returns>命中的规则列表(调用方负责后续通知与 LastTriggeredAt 更新)。</returns>
|
||||||
IReadOnlyList<AlertRule> Evaluate(
|
IReadOnlyList<AlertRuleEntity> Evaluate(
|
||||||
Dictionary<string, object?> summaryMetrics,
|
Dictionary<string, object?> summaryMetrics,
|
||||||
IReadOnlyList<AlertRule> rules);
|
IReadOnlyList<AlertRuleEntity> rules);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
namespace Monitoring.Domain.Entities;
|
namespace Monitoring.Domain.Values;
|
||||||
|
|
||||||
/// <summary>凭证条目(内聚于 SystemData,无独立表)。</summary>
|
/// <summary>凭证条目(内聚于 SystemData,无独立表)。</summary>
|
||||||
public class CredentialEntry
|
public class CredentialEntry
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
namespace Monitoring.Domain.Entities;
|
namespace Monitoring.Domain.Values;
|
||||||
|
|
||||||
/// <summary>同步任务 json_data 投影。</summary>
|
/// <summary>同步任务 json_data 投影。</summary>
|
||||||
public class JobData
|
public class JobData
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
namespace Monitoring.Domain.Entities;
|
namespace Monitoring.Domain.Values;
|
||||||
|
|
||||||
/// <summary>同步记录 json_data 投影:拉取到的外部原始数据。</summary>
|
/// <summary>同步记录 json_data 投影:拉取到的外部原始数据。</summary>
|
||||||
public class RecordData
|
public class RecordData
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
using Monitoring.Domain.Enums;
|
using Monitoring.Domain.Enums;
|
||||||
|
|
||||||
namespace Monitoring.Domain.Entities;
|
namespace Monitoring.Domain.Values;
|
||||||
|
|
||||||
/// <summary>告警规则 json_data 投影。</summary>
|
/// <summary>告警规则 json_data 投影。</summary>
|
||||||
public class RuleData
|
public class RuleData
|
||||||
@ -18,7 +18,7 @@ public class RuleData
|
|||||||
public string Threshold { get; set; } = default!;
|
public string Threshold { get; set; } = default!;
|
||||||
|
|
||||||
/// <summary>严重级别。</summary>
|
/// <summary>严重级别。</summary>
|
||||||
public AlertSeverity Severity { get; set; } = AlertSeverity.Warning;
|
public AlertSeverityEnum Severity { get; set; } = AlertSeverityEnum.Warning;
|
||||||
|
|
||||||
/// <summary>是否启用。</summary>
|
/// <summary>是否启用。</summary>
|
||||||
public bool IsEnabled { get; set; } = true;
|
public bool IsEnabled { get; set; } = true;
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
using System.Globalization;
|
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;
|
using Monitoring.Domain.Services;
|
||||||
|
|
||||||
namespace Monitoring.Infrastructure.Alerting;
|
namespace Monitoring.Infrastructure.Alerting;
|
||||||
@ -7,17 +11,17 @@ namespace Monitoring.Infrastructure.Alerting;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 默认告警评估器:对每条规则,从汇总指标取出 MetricName 的值,
|
/// 默认告警评估器:对每条规则,从汇总指标取出 MetricName 的值,
|
||||||
/// 按 Operator 与 Threshold 比较;命中且度过冷却期则纳入返回列表。
|
/// 按 Operator 与 Threshold 比较;命中且度过冷却期则纳入返回列表。
|
||||||
/// 字段读取自 AlertRule.Data(json_data 强类型视图)。
|
/// 字段读取自 AlertRuleEntity.Data(json_data 强类型视图)。
|
||||||
/// 比较失败(指标缺失/非数值)的规则静默跳过,不视为命中也不抛异常。
|
/// 比较失败(指标缺失/非数值)的规则静默跳过,不视为命中也不抛异常。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DefaultAlertEvaluator : IAlertEvaluator
|
public class DefaultAlertEvaluator : IAlertEvaluator
|
||||||
{
|
{
|
||||||
public IReadOnlyList<AlertRule> Evaluate(
|
public IReadOnlyList<AlertRuleEntity> Evaluate(
|
||||||
Dictionary<string, object?> summaryMetrics,
|
Dictionary<string, object?> summaryMetrics,
|
||||||
IReadOnlyList<AlertRule> rules)
|
IReadOnlyList<AlertRuleEntity> rules)
|
||||||
{
|
{
|
||||||
var nowMs = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds();
|
var nowMs = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds();
|
||||||
var hit = new List<AlertRule>();
|
var hit = new List<AlertRuleEntity>();
|
||||||
|
|
||||||
foreach (var rule in rules)
|
foreach (var rule in rules)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,13 +1,17 @@
|
|||||||
using System.Text.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;
|
||||||
|
|
||||||
namespace Monitoring.Infrastructure.Connectors;
|
namespace Monitoring.Infrastructure.Connectors;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Connector 公共辅助:从 ExternalSystem.JsonData 解析凭证与参数,构造带凭证的 HttpClient。
|
/// Connector 公共辅助:从 ExternalSystemEntity.JsonData 解析凭证与参数,构造带凭证的 HttpClient。
|
||||||
///
|
///
|
||||||
/// 设计取向:把 PG 当 Mongo 用——业务字段全在 jsonb,本类只做 JSON 反序列化 + HttpClient 拼装,
|
/// 设计取向:把 PG 当 Mongo 用——业务字段全在 jsonb,本类只做 JSON 反序列化 + HttpClient 拼装,
|
||||||
/// 不再访问 DbContext(凭证内聚于 ExternalSystem,无独立表)。
|
/// 不再访问 DbContext(凭证内聚于 ExternalSystemEntity,无独立表)。
|
||||||
///
|
///
|
||||||
/// HttpClient 命名约定:"Monitoring." + SystemType 名(已在 MonitoringInfrastructureModule 注册工厂)。
|
/// HttpClient 命名约定:"Monitoring." + SystemType 名(已在 MonitoringInfrastructureModule 注册工厂)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -16,7 +20,7 @@ public class ConnectorCredentialResolver(IHttpClientFactory httpClientFactory)
|
|||||||
private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNameCaseInsensitive = true };
|
private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNameCaseInsensitive = true };
|
||||||
|
|
||||||
/// <summary>创建挂好 BaseUrl + 凭证的 HttpClient。</summary>
|
/// <summary>创建挂好 BaseUrl + 凭证的 HttpClient。</summary>
|
||||||
public HttpClient CreateClient(ExternalSystem system)
|
public HttpClient CreateClient(ExternalSystemEntity system)
|
||||||
{
|
{
|
||||||
var clientName = $"Monitoring.{system.Data.SystemType}";
|
var clientName = $"Monitoring.{system.Data.SystemType}";
|
||||||
var client = httpClientFactory.CreateClient(clientName);
|
var client = httpClientFactory.CreateClient(clientName);
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
using System.Text.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.Enums;
|
||||||
using Monitoring.Domain.Services;
|
using Monitoring.Domain.Services;
|
||||||
|
|
||||||
@ -8,7 +12,7 @@ namespace Monitoring.Infrastructure.Connectors;
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 通用 HTTP Connector:POST JSON + 配置化解析。
|
/// 通用 HTTP Connector:POST JSON + 配置化解析。
|
||||||
/// 通过 ExternalSystem.Data.Parameters 配置 path/body/响应字段路径,覆盖未定制对接的简单接口。
|
/// 通过 ExternalSystemEntity.Data.Parameters 配置 path/body/响应字段路径,覆盖未定制对接的简单接口。
|
||||||
///
|
///
|
||||||
/// 配置示例(Parameters):
|
/// 配置示例(Parameters):
|
||||||
/// {
|
/// {
|
||||||
@ -19,9 +23,9 @@ namespace Monitoring.Infrastructure.Connectors;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class GenericHttpConnector(ConnectorCredentialResolver resolver) : IExternalConnector
|
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 p = system.Data.Parameters;
|
||||||
var path = p.GetValueOrDefault("path") ?? "/";
|
var path = p.GetValueOrDefault("path") ?? "/";
|
||||||
|
|||||||
@ -2,7 +2,11 @@ using System.Net.Http.Json;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Microsoft.Extensions.Logging;
|
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.Enums;
|
||||||
using Monitoring.Domain.Services;
|
using Monitoring.Domain.Services;
|
||||||
|
|
||||||
@ -16,8 +20,8 @@ namespace Monitoring.Infrastructure.Connectors;
|
|||||||
/// 1) POST {BaseUrl}/merchants/orderReport/ajax/selectOrderReport → 汇总指标
|
/// 1) POST {BaseUrl}/merchants/orderReport/ajax/selectOrderReport → 汇总指标
|
||||||
/// 2) POST {BaseUrl}/merchants/orderReport/ajax/orderReportList → 订单明细(分页全量)
|
/// 2) POST {BaseUrl}/merchants/orderReport/ajax/orderReportList → 订单明细(分页全量)
|
||||||
///
|
///
|
||||||
/// 凭证:Cookie(JSESSIONID/uid 等)从 ExternalSystem.Data.Credentials 读取(json_data 内聚)。
|
/// 凭证:Cookie(JSESSIONID/uid 等)从 ExternalSystemEntity.Data.Credentials 读取(json_data 内聚)。
|
||||||
/// 参数(ExternalSystem.Data.Parameters):reportPath/lookbackDays/maxPages。
|
/// 参数(ExternalSystemEntity.Data.Parameters):reportPath/lookbackDays/maxPages。
|
||||||
/// 金额单位说明:外部接口金额字段单位为"分",本 Connector 统一除以100转"元"后入库。
|
/// 金额单位说明:外部接口金额字段单位为"分",本 Connector 统一除以100转"元"后入库。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class UHaoZuConnector(ConnectorCredentialResolver resolver, ILogger<UHaoZuConnector> logger)
|
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 DefaultMaxPages = 50;
|
||||||
private const int PageSize = 30;
|
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 p = system.Data.Parameters;
|
||||||
var reportPath = p.GetValueOrDefault("reportPath") ?? DefaultReportPath;
|
var reportPath = p.GetValueOrDefault("reportPath") ?? DefaultReportPath;
|
||||||
|
|||||||
@ -2,7 +2,11 @@ using System.Net;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Microsoft.Extensions.Logging;
|
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;
|
namespace Monitoring.Infrastructure.Connectors;
|
||||||
|
|
||||||
@ -30,7 +34,7 @@ public class UHaoZuLoginService(ILogger<UHaoZuLoginService> logger)
|
|||||||
/// 执行登录,返回提取到的凭证列表(失败抛异常)。
|
/// 执行登录,返回提取到的凭证列表(失败抛异常)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<List<CredentialEntry>> LoginAsync(
|
public async Task<List<CredentialEntry>> LoginAsync(
|
||||||
ExternalSystem system, string account, string password,
|
ExternalSystemEntity system, string account, string password,
|
||||||
string ticket, string randstr, CancellationToken ct)
|
string ticket, string randstr, CancellationToken ct)
|
||||||
{
|
{
|
||||||
// 用 HttpClientHandler 捕获 Set-Cookie。
|
// 用 HttpClientHandler 捕获 Set-Cookie。
|
||||||
|
|||||||
@ -1,13 +1,17 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
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;
|
namespace Monitoring.Infrastructure.Persistence.Configurations;
|
||||||
|
|
||||||
/// <summary>告警规则表映射(id + 外键索引列 + json_data + 审计)。</summary>
|
/// <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.ToTable("b_alert_rules");
|
||||||
builder.HasKey(e => e.Id);
|
builder.HasKey(e => e.Id);
|
||||||
|
|||||||
@ -1,13 +1,17 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
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;
|
namespace Monitoring.Infrastructure.Persistence.Configurations;
|
||||||
|
|
||||||
/// <summary>外部系统配置表映射(极简文档模式:id + json_data + 审计)。</summary>
|
/// <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.ToTable("b_external_systems");
|
||||||
builder.HasKey(e => e.Id);
|
builder.HasKey(e => e.Id);
|
||||||
|
|||||||
@ -1,13 +1,17 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
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;
|
namespace Monitoring.Infrastructure.Persistence.Configurations;
|
||||||
|
|
||||||
/// <summary>同步任务记录表映射(id + 外键索引列 + json_data + 审计)。</summary>
|
/// <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.ToTable("b_sync_jobs");
|
||||||
builder.HasKey(e => e.Id);
|
builder.HasKey(e => e.Id);
|
||||||
|
|||||||
@ -1,13 +1,17 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
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;
|
namespace Monitoring.Infrastructure.Persistence.Configurations;
|
||||||
|
|
||||||
/// <summary>同步记录表映射(id + 去重/分类/时间索引列 + json_data + 审计)。</summary>
|
/// <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.ToTable("b_synced_records");
|
||||||
builder.HasKey(e => e.Id);
|
builder.HasKey(e => e.Id);
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using Microsoft.EntityFrameworkCore;
|
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;
|
namespace Monitoring.Infrastructure.Persistence;
|
||||||
|
|
||||||
@ -13,10 +17,10 @@ public class MonitoringDbContext : DbContext
|
|||||||
{
|
{
|
||||||
public MonitoringDbContext(DbContextOptions<MonitoringDbContext> options) : base(options) { }
|
public MonitoringDbContext(DbContextOptions<MonitoringDbContext> options) : base(options) { }
|
||||||
|
|
||||||
public DbSet<ExternalSystem> ExternalSystems => Set<ExternalSystem>();
|
public DbSet<ExternalSystemEntity> ExternalSystems => Set<ExternalSystemEntity>();
|
||||||
public DbSet<SyncJob> SyncJobs => Set<SyncJob>();
|
public DbSet<SyncJobEntity> SyncJobs => Set<SyncJobEntity>();
|
||||||
public DbSet<SyncedRecord> SyncedRecords => Set<SyncedRecord>();
|
public DbSet<SyncedRecordEntity> SyncedRecords => Set<SyncedRecordEntity>();
|
||||||
public DbSet<AlertRule> AlertRules => Set<AlertRule>();
|
public DbSet<AlertRuleEntity> AlertRules => Set<AlertRuleEntity>();
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -10,19 +10,15 @@ namespace BuildFlow.Shared.Infrastructure.Serialization;
|
|||||||
public sealed class TimestampDateTimeConverter : JsonConverter<DateTime>
|
public sealed class TimestampDateTimeConverter : JsonConverter<DateTime>
|
||||||
{
|
{
|
||||||
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
return reader.TokenType switch
|
||||||
{
|
{
|
||||||
// 兼容数字(毫秒时间戳)与字符串(ISO 8601,向后兼容旧客户端)
|
// 兼容数字(毫秒时间戳)与字符串(ISO 8601,向后兼容旧客户端)
|
||||||
if (reader.TokenType == JsonTokenType.Number)
|
JsonTokenType.Number => DateTimeOffset.FromUnixTimeMilliseconds(reader.GetInt64()).UtcDateTime,
|
||||||
{
|
JsonTokenType.String => DateTime.Parse(reader.GetString()!, null,
|
||||||
return DateTimeOffset.FromUnixTimeMilliseconds(reader.GetInt64()).UtcDateTime;
|
System.Globalization.DateTimeStyles.RoundtripKind),
|
||||||
}
|
_ => throw new JsonException("Expected a number (ms timestamp) or string (ISO date) for DateTime field.")
|
||||||
|
};
|
||||||
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.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
|
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
|
||||||
|
|||||||
@ -11,17 +11,13 @@ public sealed class TimestampDateTimeOffsetConverter : JsonConverter<DateTimeOff
|
|||||||
{
|
{
|
||||||
public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
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());
|
JsonTokenType.Number => DateTimeOffset.FromUnixTimeMilliseconds(reader.GetInt64()),
|
||||||
}
|
JsonTokenType.String => DateTimeOffset.Parse(reader.GetString()!),
|
||||||
|
_ => throw new JsonException(
|
||||||
if (reader.TokenType == JsonTokenType.String)
|
"Expected a number (ms timestamp) or string (ISO date) for DateTimeOffset field.")
|
||||||
{
|
};
|
||||||
return 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)
|
public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user