feat: add FlexibleStringJsonConverter and update NotifyUserDto to use string UserId

Added a new JSON converter to handle flexible string conversions. Updated NotifyUserDto to change UserId from int to string and removed the [IdRequired] attribute. This aligns with the new converter's functionality to handle different ID formats.
I need to condense this. The core
This commit is contained in:
xiaolipro 2026-03-17 18:01:04 +08:00
parent 3e06c83cd6
commit f519405c63
12 changed files with 73 additions and 23 deletions

View File

@ -42,7 +42,7 @@ public class GetPageCompleteWarningDetailsInput : PageInfoInput
/// <summary> /// <summary>
/// 通知对象id /// 通知对象id
/// </summary> /// </summary>
public int? NotifyUserId { get; set; } public string? NotifyUserId { get; set; }
/// <summary> /// <summary>
/// 预警创建时间开始 /// 预警创建时间开始

View File

@ -1,5 +1,6 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using TrackCenter.Application.Dtos.Warning.DataAnnotations; using TrackCenter.Application.Dtos.Warning.Serialization;
using System.Text.Json.Serialization;
namespace TrackCenter.Application.Dtos.Warning; namespace TrackCenter.Application.Dtos.Warning;
@ -8,8 +9,9 @@ public sealed class NotifyUserDto
/// <summary> /// <summary>
/// 用户id /// 用户id
/// </summary> /// </summary>
[IdRequired] [Required]
public int UserId { get; set; } [JsonConverter(typeof(FlexibleStringJsonConverter))]
public string UserId { get; set; } = string.Empty;
/// <summary> /// <summary>
/// 用户名 /// 用户名

View File

@ -52,7 +52,7 @@ public class GetPageReturnWarningDetailsInput : PageInfoInput
/// <summary> /// <summary>
/// 通知对象用户id /// 通知对象用户id
/// </summary> /// </summary>
public int? NotifyUserId { get; set; } public string? NotifyUserId { get; set; }
/// <summary> /// <summary>
/// 轨迹发生时间开始 /// 轨迹发生时间开始

View File

@ -0,0 +1,44 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Globalization;
namespace TrackCenter.Application.Dtos.Warning.Serialization;
/// <summary>
/// 兼容 string/number 两种 token 读取为 string,避免历史数据反序列化失败。
/// </summary>
public sealed class FlexibleStringJsonConverter : JsonConverter<string>
{
public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.TokenType switch
{
JsonTokenType.String => reader.GetString() ?? string.Empty,
JsonTokenType.Number => ReadNumberAsString(ref reader),
JsonTokenType.True => bool.TrueString,
JsonTokenType.False => bool.FalseString,
JsonTokenType.Null => string.Empty,
_ => throw new JsonException("Only string/number/bool/null can be converted to string.")
};
}
public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
{
writer.WriteStringValue(value);
}
private static string ReadNumberAsString(ref Utf8JsonReader reader)
{
if (reader.TryGetInt64(out var longVal))
{
return longVal.ToString(CultureInfo.InvariantCulture);
}
if (reader.TryGetDecimal(out var decimalVal))
{
return decimalVal.ToString(CultureInfo.InvariantCulture);
}
return reader.GetDouble().ToString(CultureInfo.InvariantCulture);
}
}

View File

@ -41,7 +41,7 @@ public class GetPageTransportWarningDetailsInput : PageInfoInput
/// <summary> /// <summary>
/// 通知对象用户Id /// 通知对象用户Id
/// </summary> /// </summary>
public int? NotifyUserId { get; set; } public string? NotifyUserId { get; set; }
/// <summary> /// <summary>
/// 创建时间开始 /// 创建时间开始

View File

@ -264,11 +264,14 @@ public sealed class WarningNotifyAggregator : ITransientDependency
// 1.以人的维度对不同类别的预警通知分组聚合 // 1.以人的维度对不同类别的预警通知分组聚合
// key: userId value: {key:type,value:notify } // key: userId value: {key:type,value:notify }
var userIdNotifyTaskDict = new Dictionary<int, Dictionary<int, List<WarningNotifyTaskEntity>>>(); var userIdNotifyTaskDict = new Dictionary<string, Dictionary<int, List<WarningNotifyTaskEntity>>>();
foreach (var task in needNotifyTasks) foreach (var task in needNotifyTasks)
{ {
var notifyUsers = TextJsonHelper.Deserialize<List<NotifyUserDto>>(task.NotifyUsers)!; var notifyUsers = TextJsonHelper.Deserialize<List<NotifyUserDto>>(task.NotifyUsers)!;
var notifyUserIds = notifyUsers.Select(x => x.UserId).ToHashSet(); var notifyUserIds = notifyUsers
.Select(x => x.UserId.Trim())
.Where(x => !string.IsNullOrWhiteSpace(x))
.ToHashSet();
foreach (var userId in notifyUserIds) foreach (var userId in notifyUserIds)
{ {
@ -303,7 +306,7 @@ public sealed class WarningNotifyAggregator : ITransientDependency
return result; return result;
} }
private WarningNotifyParam BuildNotifyParam(int userId, Dictionary<int, List<WarningNotifyTaskEntity>> typeNotifyTaskDict) private WarningNotifyParam BuildNotifyParam(string userId, Dictionary<int, List<WarningNotifyTaskEntity>> typeNotifyTaskDict)
{ {
var options = _optionsMonitor.CurrentValue; var options = _optionsMonitor.CurrentValue;
var hours = options.ScanWarningNotifyTaskHourInterval; var hours = options.ScanWarningNotifyTaskHourInterval;

View File

@ -77,13 +77,14 @@ public static class WarningNotifyBuilder
public const string ReturnPath = "/trajectoryCenter/trajectoryWarningOrder/ReturnWarning"; public const string ReturnPath = "/trajectoryCenter/trajectoryWarningOrder/ReturnWarning";
public const string CompletePath = "/trajectoryCenter/trajectoryWarningOrder/CompleteWarning"; public const string CompletePath = "/trajectoryCenter/trajectoryWarningOrder/CompleteWarning";
public static string BuildUrl(string baseUrl, WarningRuleType warningRuleType, int userId) public static string BuildUrl(string baseUrl, WarningRuleType warningRuleType, string userId)
{ {
var encodedUserId = Uri.EscapeDataString(userId);
return warningRuleType switch return warningRuleType switch
{ {
WarningRuleType.运输时效预警 => $"{baseUrl}{TransportPath}?userid={userId}", WarningRuleType.运输时效预警 => $"{baseUrl}{TransportPath}?userid={encodedUserId}",
WarningRuleType.回传时效预警 => $"{baseUrl}{ReturnPath}?userid={userId}", WarningRuleType.回传时效预警 => $"{baseUrl}{ReturnPath}?userid={encodedUserId}",
WarningRuleType.完整预警 => $"{baseUrl}{CompletePath}?userid={userId}", WarningRuleType.完整预警 => $"{baseUrl}{CompletePath}?userid={encodedUserId}",
_ => throw new ArgumentOutOfRangeException() _ => throw new ArgumentOutOfRangeException()
}; };
} }

View File

@ -5,7 +5,7 @@ namespace TrackCenter.Application.Hangfire;
/// </summary> /// </summary>
public class WarningNotifyParam public class WarningNotifyParam
{ {
public int UserId { get; set; } public string UserId { get; set; } = string.Empty;
public int Hours { get; set; } public int Hours { get; set; }

View File

@ -36,7 +36,7 @@ public class GetPageCompleteWarningDetailsInputVO : PageInfoInput
/// <summary> /// <summary>
/// 通知对象id /// 通知对象id
/// </summary> /// </summary>
public int? NotifyUserId { get; set; } public string? NotifyUserId { get; set; }
/// <summary> /// <summary>
/// 预警创建时间开始 /// 预警创建时间开始

View File

@ -49,7 +49,7 @@ public class GetPageReturnWarningDetailsInputVO : PageInfoInput
/// <summary> /// <summary>
/// 通知对象用户id /// 通知对象用户id
/// </summary> /// </summary>
public int? NotifyUserId { get; set; } public string? NotifyUserId { get; set; }
/// <summary> /// <summary>
/// 轨迹发生时间开始 /// 轨迹发生时间开始

View File

@ -38,7 +38,7 @@ public class GetPageTransportWarningDetailsInputVO : PageInfoInput
/// <summary> /// <summary>
/// 通知对象用户Id /// 通知对象用户Id
/// </summary> /// </summary>
public int? NotifyUserId { get; set; } public string? NotifyUserId { get; set; }
/// <summary> /// <summary>
/// 创建时间开始 /// 创建时间开始

View File

@ -46,8 +46,8 @@ public class WarningDetailsEsRepository : IWarningDetailsEsRepository, ITransien
c => c.DateRange(r => r.Field(f => f.CreateTime).LessThanOrEquals(input.CreateTimeEnd))); c => c.DateRange(r => r.Field(f => f.CreateTime).LessThanOrEquals(input.CreateTimeEnd)));
// 模糊匹配userId // 模糊匹配userId
musts.AddIf(x => x.NotifyUserId.HasValue, musts.AddIf(x => !string.IsNullOrWhiteSpace(x.NotifyUserId),
c => c.Match(f => f.Field(d => d.NotifyUsers).Query(input.NotifyUserId.ToString()))); c => c.Match(f => f.Field(d => d.NotifyUsers).Query(input.NotifyUserId)));
var r = await _client.SearchAsync<TransportWarningDetailsIndex>(x => x var r = await _client.SearchAsync<TransportWarningDetailsIndex>(x => x
.Index(GlobalConst.TransportWarningDetailsIndex) .Index(GlobalConst.TransportWarningDetailsIndex)
@ -381,8 +381,8 @@ public class WarningDetailsEsRepository : IWarningDetailsEsRepository, ITransien
c => c.DateRange(r => r.Field(f => f.CreateTime).LessThanOrEquals(input.WarningCreateTimeEnd))); c => c.DateRange(r => r.Field(f => f.CreateTime).LessThanOrEquals(input.WarningCreateTimeEnd)));
// 模糊匹配userId(json字符串包含userId) // 模糊匹配userId(json字符串包含userId)
musts.AddIf(x => x.NotifyUserId.HasValue, musts.AddIf(x => !string.IsNullOrWhiteSpace(x.NotifyUserId),
c => c.Match(f => f.Field(d => d.NotifyUsers).Query(input.NotifyUserId.ToString()))); c => c.Match(f => f.Field(d => d.NotifyUsers).Query(input.NotifyUserId)));
musts.AddIf(x => !string.IsNullOrWhiteSpace(x.Customer), musts.AddIf(x => !string.IsNullOrWhiteSpace(x.Customer),
c => c.Match(f => f.Field(d => d.Customer).Query(input.Customer))); c => c.Match(f => f.Field(d => d.Customer).Query(input.Customer)));
@ -753,8 +753,8 @@ public class WarningDetailsEsRepository : IWarningDetailsEsRepository, ITransien
c => c.DateRange(r => r.Field(f => f.ProcessedTime).LessThanOrEquals(input.WarningHandleTimeEnd))); c => c.DateRange(r => r.Field(f => f.ProcessedTime).LessThanOrEquals(input.WarningHandleTimeEnd)));
// 模糊匹配userId // 模糊匹配userId
musts.AddIf(x => x.NotifyUserId.HasValue, musts.AddIf(x => !string.IsNullOrWhiteSpace(x.NotifyUserId),
c => c.Match(f => f.Field(d => d.NotifyUsers).Query(input.NotifyUserId.ToString()))); c => c.Match(f => f.Field(d => d.NotifyUsers).Query(input.NotifyUserId)));
musts.AddIf(x => !string.IsNullOrWhiteSpace(x.Customer), musts.AddIf(x => !string.IsNullOrWhiteSpace(x.Customer),
c => c.Match(f => f.Field(d => d.Customer).Query(input.Customer))); c => c.Match(f => f.Field(d => d.Customer).Query(input.Customer)));