| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- using System.Text.Json;
- using Yitter.IdGenerator;
- namespace Admin.NET.Plugin.AiDOP.Infrastructure;
- /// <summary>统一写入 aidop_action_run_log。</summary>
- public class AidopActionRunLogWriter : ITransient
- {
- private readonly ISqlSugarClient _db;
- public AidopActionRunLogWriter(ISqlSugarClient db)
- {
- _db = db;
- }
- /// <summary>
- /// 同租户同动作是否已有进行中的运行(用于排程单飞等互斥)。
- /// </summary>
- public async Task<bool> HasRunningAsync(string actionCode, long tenantId)
- {
- var n = await _db.Ado.GetIntAsync(
- """
- SELECT COUNT(*) FROM aidop_action_run_log
- WHERE tenant_id = @TenantId AND action_code = @ActionCode AND status = 'RUNNING'
- """,
- new SugarParameter("@TenantId", tenantId),
- new SugarParameter("@ActionCode", actionCode));
- return n > 0;
- }
- /// <summary>
- /// 取同租户同动作最近一次 SUCCESS 的 id(可作为当前有效 run_id)。
- /// </summary>
- public async Task<long?> GetLatestSuccessIdAsync(string actionCode, long tenantId)
- {
- var id = await _db.Ado.GetLongAsync(
- """
- SELECT id FROM aidop_action_run_log
- WHERE tenant_id = @TenantId AND action_code = @ActionCode AND status = 'SUCCESS'
- ORDER BY start_time DESC, id DESC
- LIMIT 1
- """,
- new List<SugarParameter>
- {
- new("@TenantId", tenantId),
- new("@ActionCode", actionCode)
- });
- return id > 0 ? id : null;
- }
- public async Task<long> StartAsync(
- string actionCode,
- long tenantId,
- string bizType,
- long? bizId,
- string? bizNo)
- {
- var id = YitIdHelper.NextId();
- var now = DateTime.Now;
- await _db.Ado.ExecuteCommandAsync(
- """
- INSERT INTO aidop_action_run_log
- (id, tenant_id, action_code, biz_type, biz_id, biz_no, status, start_time, create_time)
- VALUES
- (@Id, @TenantId, @ActionCode, @BizType, @BizId, @BizNo, 'RUNNING', @Now, @Now)
- """,
- new SugarParameter("@Id", id),
- new SugarParameter("@TenantId", tenantId),
- new SugarParameter("@ActionCode", actionCode),
- new SugarParameter("@BizType", bizType),
- new SugarParameter("@BizId", bizId ?? (object)DBNull.Value),
- new SugarParameter("@BizNo", bizNo ?? string.Empty),
- new SugarParameter("@Now", now));
- return id;
- }
- public async Task SuccessAsync(long id, string message, object? detail = null) =>
- await FinishAsync(id, "SUCCESS", message, detail);
- public async Task FailedAsync(long id, string message, object? detail = null) =>
- await FinishAsync(id, "FAILED", message, detail);
- /// <summary>运行中更新进度(不改 status,不清 end_time)。</summary>
- public async Task UpdateProgressAsync(long id, string message, object? detail = null)
- {
- await _db.Ado.ExecuteCommandAsync(
- """
- UPDATE aidop_action_run_log
- SET message = @Message, detail_json = @Detail
- WHERE id = @Id AND status = 'RUNNING'
- """,
- new SugarParameter("@Message", Truncate(message, 1000)),
- new SugarParameter("@Detail", detail is null ? (object)DBNull.Value : JsonSerializer.Serialize(detail)),
- new SugarParameter("@Id", id));
- }
- /// <summary>
- /// 将超时仍为 RUNNING 的记录标为 FAILED,避免进程崩溃后单飞永久堵死。
- /// </summary>
- public async Task FailStaleRunningAsync(string actionCode, long tenantId, TimeSpan olderThan)
- {
- var cutoff = DateTime.Now - olderThan;
- await _db.Ado.ExecuteCommandAsync(
- """
- UPDATE aidop_action_run_log
- SET status = 'FAILED',
- message = CONCAT(IFNULL(message, ''), ' [超时未完成,已自动关闭]'),
- end_time = NOW()
- WHERE tenant_id = @TenantId AND action_code = @ActionCode
- AND status = 'RUNNING' AND start_time < @Cutoff
- """,
- new SugarParameter("@TenantId", tenantId),
- new SugarParameter("@ActionCode", actionCode),
- new SugarParameter("@Cutoff", cutoff));
- }
- private async Task FinishAsync(long id, string status, string message, object? detail)
- {
- var now = DateTime.Now;
- await _db.Ado.ExecuteCommandAsync(
- """
- UPDATE aidop_action_run_log
- SET status = @Status, message = @Message, detail_json = @Detail, end_time = @Now
- WHERE id = @Id
- """,
- new SugarParameter("@Status", status),
- new SugarParameter("@Message", Truncate(message, 1000)),
- new SugarParameter("@Detail", detail is null ? (object)DBNull.Value : JsonSerializer.Serialize(detail)),
- new SugarParameter("@Now", now),
- new SugarParameter("@Id", id));
- }
- private static string Truncate(string? s, int max) =>
- string.IsNullOrEmpty(s) || s.Length <= max ? s ?? string.Empty : s[..max];
- }
|