feat: simplify transport warning to product+country with assessment SLA
AppKey-level scheme; scene matches one product and one country; stores assessment total hours (days×24) with migration SQL. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
dabb9a4edb
commit
aeae900b5e
31
scripts/sql/20260801_transport_warning_simplify.sql
Normal file
31
scripts/sql/20260801_transport_warning_simplify.sql
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
-- 运输时效:一产品一国国家 + 考核总时效;规则主体按 AppKey 方案级
|
||||||
|
-- 执行前请备份 t_warning / t_warning_transport_scene
|
||||||
|
|
||||||
|
-- 1) 国家列(若已存在可跳过)
|
||||||
|
ALTER TABLE t_warning_transport_scene
|
||||||
|
ADD COLUMN CountryCode VARCHAR(16) NULL COMMENT '国家二字码(单一)' AFTER ProductCode;
|
||||||
|
|
||||||
|
-- 2) 考核总时效(小时)
|
||||||
|
ALTER TABLE t_warning_transport_scene
|
||||||
|
ADD COLUMN AssessmentTotalTimeSpan FLOAT NULL COMMENT '考核总时效(小时)' AFTER CountryCode;
|
||||||
|
|
||||||
|
-- 3) 运输时效规则主体改为方案级:客户清空
|
||||||
|
UPDATE t_warning
|
||||||
|
SET Customer = ''
|
||||||
|
WHERE WarningType = 1
|
||||||
|
AND (Customer IS NULL OR Customer <> '');
|
||||||
|
|
||||||
|
-- 4) 同一 AppKey 下多个已启用运输时效方案,仅保留 Id 最小的一条
|
||||||
|
UPDATE t_warning w
|
||||||
|
INNER JOIN (
|
||||||
|
SELECT AppKey, MIN(Id) AS KeepId
|
||||||
|
FROM t_warning
|
||||||
|
WHERE WarningType = 1
|
||||||
|
AND IsEnabled = 1
|
||||||
|
GROUP BY AppKey
|
||||||
|
HAVING COUNT(1) > 1
|
||||||
|
) d ON w.AppKey = d.AppKey
|
||||||
|
SET w.IsEnabled = 0
|
||||||
|
WHERE w.WarningType = 1
|
||||||
|
AND w.IsEnabled = 1
|
||||||
|
AND w.Id <> d.KeepId;
|
||||||
@ -36,22 +36,32 @@ public class TransportWarningSceneDto
|
|||||||
public string? LeavePort { get; set; }
|
public string? LeavePort { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 产品编码(多个,分割)
|
/// 产品编码(单一)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? ProductCode { get; set; }
|
public string? ProductCode { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 月份(多个/分割)
|
/// 国家二字码(单一)
|
||||||
|
/// </summary>
|
||||||
|
public string? CountryCode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 考核总时效(小时)
|
||||||
|
/// </summary>
|
||||||
|
public float? AssessmentTotalTimeSpan { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 月份(多个/分割,历史字段)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? Months { get; set; }
|
public string? Months { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 区域Id(单一)
|
/// 区域Id(单一,历史字段)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int? AreaId { get; set; }
|
public int? AreaId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 区域id对应的名称
|
/// 区域id对应的名称(历史字段)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? AreaName { get; set; }
|
public string? AreaName { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
using TrackCenter.Application.Dtos.Warning.DataAnnotations;
|
using TrackCenter.Application.Dtos.Warning.DataAnnotations;
|
||||||
|
|
||||||
namespace TrackCenter.Application.Dtos.Warning.Transport;
|
namespace TrackCenter.Application.Dtos.Warning.Transport;
|
||||||
@ -15,33 +16,31 @@ public sealed class UpsertTransportWarningSceneInput
|
|||||||
[IdRequired]
|
[IdRequired]
|
||||||
public int WarningId { get; set; }
|
public int WarningId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>历史字段,忽略</summary>
|
||||||
/// 是否中转(单一)
|
|
||||||
/// </summary>
|
|
||||||
public bool? IsTransit { get; set; }
|
public bool? IsTransit { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>历史字段,忽略</summary>
|
||||||
/// 货物类型(多个,分割)
|
|
||||||
/// </summary>
|
|
||||||
public string? CargoType { get; set; }
|
public string? CargoType { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>历史字段,忽略</summary>
|
||||||
/// 起运港口(多个,分割)
|
|
||||||
/// </summary>
|
|
||||||
public string? LeavePort { get; set; }
|
public string? LeavePort { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>产品编码(单一销售产品)</summary>
|
||||||
/// 产品编码(多个,分割)
|
[Required(ErrorMessage = "请配置销售产品")]
|
||||||
/// </summary>
|
|
||||||
public string? ProductCode { get; set; }
|
public string? ProductCode { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>国家二字码(单一)</summary>
|
||||||
/// 月份(多个/分割)
|
[Required(ErrorMessage = "请配置国家")]
|
||||||
/// </summary>
|
public string? CountryCode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>考核总时效(小时)</summary>
|
||||||
|
[Required(ErrorMessage = "请配置考核总时效")]
|
||||||
|
[Range(0.1, 99999, ErrorMessage = "考核总时效须大于0")]
|
||||||
|
public float? AssessmentTotalTimeSpan { get; set; }
|
||||||
|
|
||||||
|
/// <summary>历史字段,忽略</summary>
|
||||||
public string? Months { get; set; }
|
public string? Months { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>历史字段,忽略</summary>
|
||||||
/// 区域Id(单一)
|
|
||||||
/// </summary>
|
|
||||||
public int? AreaId { get; set; }
|
public int? AreaId { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,9 +19,9 @@ public class UpsertWarningRuleInput
|
|||||||
public string AppKey { get; set; } //应用
|
public string AppKey { get; set; } //应用
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 客户
|
/// 客户(运输时效可空,后端会置空;回传/完整预警必填)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Customer { get; set; } //客户
|
public string? Customer { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 预警规则名称
|
/// 预警规则名称
|
||||||
|
|||||||
@ -223,9 +223,9 @@ public class TransportWarningHandler : ITransportWarningHandler, ITransientDepen
|
|||||||
orders = orders.Where(x => earliestTrackStatusTrackDict.ContainsKey(x.Id)).ToList();
|
orders = orders.Where(x => earliestTrackStatusTrackDict.ContainsKey(x.Id)).ToList();
|
||||||
if (orders.Count == 0) return;
|
if (orders.Count == 0) return;
|
||||||
|
|
||||||
// 3.查规则主体
|
// 3.查规则主体(运输时效:AppKey 方案级,不按客户)
|
||||||
var conditions = orders.Select(x => (AppKey: x.AppKey, x.Customer)).ToHashSet();
|
var appKeys = orders.Select(x => x.AppKey).ToHashSet();
|
||||||
var ruleDict = await _warningRuleRepository.GetWarningRuleForEnabledAsync(conditions, WarningRuleType.运输时效预警);
|
var ruleDict = await _warningRuleRepository.GetWarningRuleForEnabledByAppKeysAsync(appKeys, WarningRuleType.运输时效预警);
|
||||||
if (ruleDict.Count == 0) return;
|
if (ruleDict.Count == 0) return;
|
||||||
|
|
||||||
// 4.查规则场景
|
// 4.查规则场景
|
||||||
@ -233,11 +233,7 @@ public class TransportWarningHandler : ITransportWarningHandler, ITransientDepen
|
|||||||
var scenes = await _warningRuleRepository.GetTransportSceneAsync(x => ruleIds.Contains(x.WarningId));
|
var scenes = await _warningRuleRepository.GetTransportSceneAsync(x => ruleIds.Contains(x.WarningId));
|
||||||
if (scenes.Count == 0) return;
|
if (scenes.Count == 0) return;
|
||||||
|
|
||||||
// 5.查区域信息
|
// 选出订单命中的场景(仅产品 + 国家)
|
||||||
var areaIds = scenes.Where(x => x.AreaId > 0).Select(x => x.AreaId!.Value).ToHashSet();
|
|
||||||
var ruleAreaInfosDict = await _kpiAreaRepository.GetAreaInfosDictByAreaId(areaIds);
|
|
||||||
|
|
||||||
// 选出订单命中的场景
|
|
||||||
var hitSceneIds = new HashSet<int>();
|
var hitSceneIds = new HashSet<int>();
|
||||||
var orderIdHitSceneIdsDict = new Dictionary<int, HashSet<int>>();
|
var orderIdHitSceneIdsDict = new Dictionary<int, HashSet<int>>();
|
||||||
var sceneBos = TransportWarningSceneBO.From(scenes);
|
var sceneBos = TransportWarningSceneBO.From(scenes);
|
||||||
@ -246,7 +242,10 @@ public class TransportWarningHandler : ITransportWarningHandler, ITransientDepen
|
|||||||
var sceneSet = new HashSet<int>();
|
var sceneSet = new HashSet<int>();
|
||||||
foreach (var scene in sceneBos)
|
foreach (var scene in sceneBos)
|
||||||
{
|
{
|
||||||
if (!scene.CanHitScene(order, ruleAreaInfosDict.GetValueOrDefault(scene.AreaId ?? 0))) continue;
|
// 仅匹配同 AppKey 下方案的场景
|
||||||
|
if (!ruleDict.TryGetValue(scene.WarningId, out var rule) || rule.AppKey != order.AppKey)
|
||||||
|
continue;
|
||||||
|
if (!scene.CanHitScene(order)) continue;
|
||||||
sceneSet.Add(scene.Id);
|
sceneSet.Add(scene.Id);
|
||||||
hitSceneIds.Add(scene.Id);
|
hitSceneIds.Add(scene.Id);
|
||||||
}
|
}
|
||||||
@ -576,14 +575,11 @@ public class TransportWarningHandler : ITransportWarningHandler, ITransientDepen
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// 3.重新验证订单是否命中新场景
|
// 3.重新验证订单是否命中新场景
|
||||||
var areas = postScene.AreaId.HasValue
|
|
||||||
? _kpiAreaRepository.GetAreaInfosByAreaId(postScene.AreaId.Value)
|
|
||||||
: new List<KpiAreaInfo>();
|
|
||||||
var noHitSceneOrderIds = new HashSet<int>();
|
var noHitSceneOrderIds = new HashSet<int>();
|
||||||
var reHitSceneOrderIds = new HashSet<int>();
|
var reHitSceneOrderIds = new HashSet<int>();
|
||||||
foreach (var order in orders)
|
foreach (var order in orders)
|
||||||
{
|
{
|
||||||
if (postScene.CanHitScene(order, areas))
|
if (postScene.CanHitScene(order))
|
||||||
reHitSceneOrderIds.Add(order.Id);
|
reHitSceneOrderIds.Add(order.Id);
|
||||||
else
|
else
|
||||||
noHitSceneOrderIds.Add(order.Id);
|
noHitSceneOrderIds.Add(order.Id);
|
||||||
@ -599,15 +595,11 @@ public class TransportWarningHandler : ITransportWarningHandler, ITransientDepen
|
|||||||
var updateRuleItems = await _warningRuleRepository.GetTransportSceneRuleItemsAsync(x => updateRuleItemIds.Contains(x.Id));
|
var updateRuleItems = await _warningRuleRepository.GetTransportSceneRuleItemsAsync(x => updateRuleItemIds.Contains(x.Id));
|
||||||
var ruleItemDict = updateRuleItems.ToDictionary(x => x.Id);
|
var ruleItemDict = updateRuleItems.ToDictionary(x => x.Id);
|
||||||
|
|
||||||
// 场景->区域
|
|
||||||
var ruleArea = postScene.AreaId.HasValue
|
|
||||||
? await _kpiAreaRepository.GetFirstAsync(x => x.Id == postScene.AreaId.Value)
|
|
||||||
: null;
|
|
||||||
foreach (var detail in updateDetails)
|
foreach (var detail in updateDetails)
|
||||||
{
|
{
|
||||||
var ruleItem = ruleItemDict[detail.WarningSceneRuleItemId];
|
var ruleItem = ruleItemDict[detail.WarningSceneRuleItemId];
|
||||||
detail.WarningName = rule.Name;
|
detail.WarningName = rule.Name;
|
||||||
detail.WarningRuleDescription = detail.GetWarningRuleDescription(postScene.ToEntity(), ruleItem, ruleArea);
|
detail.WarningRuleDescription = detail.GetWarningRuleDescription(postScene.ToEntity(), ruleItem, null);
|
||||||
needUpdateDetails.Add(detail);
|
needUpdateDetails.Add(detail);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -679,12 +671,8 @@ public class TransportWarningHandler : ITransportWarningHandler, ITransientDepen
|
|||||||
|
|
||||||
private static bool CanSceneChange(TransportWarningSceneBO oldScene, TransportWarningSceneBO postScene)
|
private static bool CanSceneChange(TransportWarningSceneBO oldScene, TransportWarningSceneBO postScene)
|
||||||
{
|
{
|
||||||
return oldScene.CargoType != postScene.CargoType || //多个 普货,危险品
|
return !string.Equals(oldScene.ProductCode, postScene.ProductCode, StringComparison.OrdinalIgnoreCase)
|
||||||
oldScene.Months != postScene.Months || //多个 约束提交的月份组从小到大 1/3/4,不能出现 4/1/3这种 (如果出现则SequenceEqual)
|
|| !string.Equals(oldScene.CountryCode, postScene.CountryCode, StringComparison.OrdinalIgnoreCase);
|
||||||
oldScene.AreaId != postScene.AreaId || // 单一,area1
|
|
||||||
oldScene.IsTransit != postScene.IsTransit || //单一,true/false
|
|
||||||
oldScene.LeavePort != postScene.LeavePort || //多个
|
|
||||||
oldScene.ProductCode != postScene.ProductCode; //多个
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion todo: 规则场景变更
|
#endregion todo: 规则场景变更
|
||||||
|
|||||||
@ -7,122 +7,64 @@ namespace TrackCenter.Application.Services.Warning.Model;
|
|||||||
|
|
||||||
public sealed class TransportWarningSceneBO
|
public sealed class TransportWarningSceneBO
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// 场景唯一id (传0视为新增)
|
|
||||||
/// </summary>
|
|
||||||
public int Id { get; }
|
public int Id { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 预警规则主体id
|
|
||||||
/// </summary>
|
|
||||||
public int WarningId { get; }
|
public int WarningId { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 货物类型(多个,分割)
|
|
||||||
/// </summary>
|
|
||||||
public string? CargoType { get; }
|
public string? CargoType { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 月份(多个/分割)
|
|
||||||
/// </summary>
|
|
||||||
public string? Months { get; }
|
public string? Months { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 区域Id(单一)
|
|
||||||
/// </summary>
|
|
||||||
public int? AreaId { get; }
|
public int? AreaId { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 是否中转(单一)
|
|
||||||
/// </summary>
|
|
||||||
public bool? IsTransit { get; }
|
public bool? IsTransit { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 起运港口(多个,分割)
|
|
||||||
/// </summary>
|
|
||||||
public string? LeavePort { get; }
|
public string? LeavePort { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>产品编码(单一)</summary>
|
||||||
/// 产品编码(多个,分割)
|
|
||||||
/// </summary>
|
|
||||||
public string? ProductCode { get; }
|
public string? ProductCode { get; }
|
||||||
|
|
||||||
public TransportWarningSceneBO(int id, int warningId, string? cargoType, string? months, int? areaId, bool? isTransit, string? leavePort, string? productCode)
|
/// <summary>国家二字码(单一)</summary>
|
||||||
|
public string? CountryCode { get; }
|
||||||
|
|
||||||
|
/// <summary>考核总时效(小时)</summary>
|
||||||
|
public float? AssessmentTotalTimeSpan { get; }
|
||||||
|
|
||||||
|
public TransportWarningSceneBO(
|
||||||
|
int id,
|
||||||
|
int warningId,
|
||||||
|
string? cargoType,
|
||||||
|
string? months,
|
||||||
|
int? areaId,
|
||||||
|
bool? isTransit,
|
||||||
|
string? leavePort,
|
||||||
|
string? productCode,
|
||||||
|
string? countryCode = null,
|
||||||
|
float? assessmentTotalTimeSpan = null)
|
||||||
{
|
{
|
||||||
Id = id;
|
Id = id;
|
||||||
WarningId = warningId;
|
WarningId = warningId;
|
||||||
CargoType = cargoType;
|
CargoType = cargoType;
|
||||||
|
Months = months;
|
||||||
// 如果有月份则排序之后在写入
|
|
||||||
if (!string.IsNullOrWhiteSpace(months))
|
|
||||||
{
|
|
||||||
var sorted = months.Split('/').Select(int.Parse).OrderBy(x => x).ToList();
|
|
||||||
Months = string.Join('/', sorted);
|
|
||||||
}
|
|
||||||
|
|
||||||
AreaId = areaId;
|
AreaId = areaId;
|
||||||
IsTransit = isTransit;
|
IsTransit = isTransit;
|
||||||
LeavePort = leavePort;
|
LeavePort = leavePort;
|
||||||
ProductCode = productCode;
|
ProductCode = string.IsNullOrWhiteSpace(productCode) ? null : productCode.Trim();
|
||||||
|
CountryCode = NormalizeSingleCountryCode(countryCode);
|
||||||
|
AssessmentTotalTimeSpan = assessmentTotalTimeSpan;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CanHitScene(OrderEntity order, List<KpiAreaInfo>? ruleAreas)
|
/// <summary>
|
||||||
|
/// 命中:订单产品 = 配置产品;到达国家 = 配置国家(一对一)。
|
||||||
|
/// </summary>
|
||||||
|
public bool CanHitScene(OrderEntity order, List<KpiAreaInfo>? ruleAreas = null)
|
||||||
{
|
{
|
||||||
// 是否中转
|
_ = ruleAreas;
|
||||||
if (IsTransit != null && IsTransit != order.IsTransit)
|
|
||||||
|
if (string.IsNullOrWhiteSpace(ProductCode) || string.IsNullOrWhiteSpace(order.ProductCode))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// 货物类型
|
if (!string.Equals(order.ProductCode.Trim(), ProductCode, StringComparison.OrdinalIgnoreCase))
|
||||||
if (!string.IsNullOrWhiteSpace(CargoType) && order.CargoType != CargoType)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// 启运港口
|
if (string.IsNullOrWhiteSpace(CountryCode) || string.IsNullOrWhiteSpace(order.ArrivalCountry))
|
||||||
if (!string.IsNullOrWhiteSpace(LeavePort) && order.LeavePort != LeavePort)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// 产品编码
|
return string.Equals(order.ArrivalCountry.Trim(), CountryCode, StringComparison.OrdinalIgnoreCase);
|
||||||
if (!string.IsNullOrWhiteSpace(ProductCode) && order.ProductCode != ProductCode)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// 月份
|
|
||||||
if (!string.IsNullOrWhiteSpace(Months))
|
|
||||||
{
|
|
||||||
var months = string.IsNullOrEmpty(Months)
|
|
||||||
? new HashSet<int>()
|
|
||||||
: Months.Split('/').Select(int.Parse).ToHashSet();
|
|
||||||
// 订单创建时间月份
|
|
||||||
var orderCreateTimeMonth = order.CreateTime.Month;
|
|
||||||
if (!months.Contains(orderCreateTimeMonth))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 区域
|
|
||||||
if (ruleAreas != null && ruleAreas.Count > 0)
|
|
||||||
{
|
|
||||||
// 使用订单到达国家的二字码匹配区域
|
|
||||||
var areas = ruleAreas.Where(area => order.ArrivalCountry == area.CountryCode).ToList();
|
|
||||||
foreach (var area in areas)
|
|
||||||
{
|
|
||||||
// 邮编是否在关注的范围内?
|
|
||||||
if (!string.IsNullOrWhiteSpace(area.LeftPostcode) && !string.IsNullOrWhiteSpace(area.RightPostcode))
|
|
||||||
{
|
|
||||||
// tips: 参考小包的邮编匹配,字典序
|
|
||||||
if (string.CompareOrdinal(order.ArrivalPostCode, area.LeftPostcode) < 0 ||
|
|
||||||
string.CompareOrdinal(order.ArrivalPostCode, area.RightPostcode) > 0)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 城市是否匹配?
|
|
||||||
if (!string.IsNullOrWhiteSpace(area.City) && order.ArrivalCity != area.City)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// 省份是否匹配?
|
|
||||||
if (!string.IsNullOrWhiteSpace(area.Province) && order.ArrivalProvince != area.Province)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TransportWarningSceneEntity ToEntity(TransportWarningSceneBO bo)
|
public static TransportWarningSceneEntity ToEntity(TransportWarningSceneBO bo)
|
||||||
@ -135,23 +77,13 @@ public sealed class TransportWarningSceneBO
|
|||||||
bo.AreaId,
|
bo.AreaId,
|
||||||
bo.IsTransit,
|
bo.IsTransit,
|
||||||
bo.LeavePort,
|
bo.LeavePort,
|
||||||
bo.ProductCode
|
bo.ProductCode,
|
||||||
|
bo.CountryCode,
|
||||||
|
bo.AssessmentTotalTimeSpan
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TransportWarningSceneEntity ToEntity()
|
public TransportWarningSceneEntity ToEntity() => ToEntity(this);
|
||||||
{
|
|
||||||
return new TransportWarningSceneEntity(
|
|
||||||
Id,
|
|
||||||
WarningId,
|
|
||||||
CargoType,
|
|
||||||
Months,
|
|
||||||
AreaId,
|
|
||||||
IsTransit,
|
|
||||||
LeavePort,
|
|
||||||
ProductCode
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TransportWarningSceneBO From(TransportWarningSceneEntity entity)
|
public static TransportWarningSceneBO From(TransportWarningSceneEntity entity)
|
||||||
{
|
{
|
||||||
@ -163,47 +95,54 @@ public sealed class TransportWarningSceneBO
|
|||||||
entity.AreaId,
|
entity.AreaId,
|
||||||
entity.IsTransit,
|
entity.IsTransit,
|
||||||
entity.LeavePort,
|
entity.LeavePort,
|
||||||
entity.ProductCode
|
entity.ProductCode,
|
||||||
|
entity.CountryCode,
|
||||||
|
entity.AssessmentTotalTimeSpan
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<TransportWarningSceneBO> From(List<TransportWarningSceneEntity> entities)
|
public static List<TransportWarningSceneBO> From(List<TransportWarningSceneEntity> entities)
|
||||||
{
|
=> entities.Select(From).ToList();
|
||||||
return entities.Select(From).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TransportWarningSceneBO From(UpsertTransportWarningSceneInput input)
|
public static TransportWarningSceneBO From(UpsertTransportWarningSceneInput input)
|
||||||
{
|
{
|
||||||
return new TransportWarningSceneBO(
|
return new TransportWarningSceneBO(
|
||||||
input.Id,
|
input.Id,
|
||||||
input.WarningId,
|
input.WarningId,
|
||||||
input.CargoType,
|
null,
|
||||||
input.Months,
|
null,
|
||||||
input.AreaId,
|
null,
|
||||||
input.IsTransit,
|
null,
|
||||||
input.LeavePort,
|
null,
|
||||||
input.ProductCode
|
input.ProductCode,
|
||||||
|
input.CountryCode,
|
||||||
|
input.AssessmentTotalTimeSpan
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool Equals(TransportWarningSceneBO other)
|
private bool Equals(TransportWarningSceneBO other)
|
||||||
{
|
{
|
||||||
return WarningId == other.WarningId
|
return WarningId == other.WarningId
|
||||||
&& IsTransit == other.IsTransit
|
&& string.Equals(ProductCode, other.ProductCode, StringComparison.OrdinalIgnoreCase)
|
||||||
&& CargoType == other.CargoType
|
&& string.Equals(CountryCode, other.CountryCode, StringComparison.OrdinalIgnoreCase);
|
||||||
&& LeavePort == other.LeavePort
|
|
||||||
&& ProductCode == other.ProductCode
|
|
||||||
&& Months == other.Months
|
|
||||||
&& AreaId == other.AreaId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Equals(object? obj)
|
public override bool Equals(object? obj)
|
||||||
{
|
=> ReferenceEquals(this, obj) || obj is TransportWarningSceneBO other && Equals(other);
|
||||||
return ReferenceEquals(this, obj) || obj is TransportWarningSceneBO other && Equals(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
|
=> HashCode.Combine(WarningId, ProductCode?.ToUpperInvariant(), CountryCode?.ToUpperInvariant());
|
||||||
|
|
||||||
|
private static string? NormalizeSingleCountryCode(string? countryCode)
|
||||||
{
|
{
|
||||||
return HashCode.Combine(WarningId, IsTransit, CargoType, LeavePort, ProductCode, Months, AreaId);
|
if (string.IsNullOrWhiteSpace(countryCode))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var code = countryCode
|
||||||
|
.Split([',', '/', ';', '|', ' '], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
||||||
|
.Select(x => x.ToUpperInvariant())
|
||||||
|
.FirstOrDefault(x => x.Length > 0);
|
||||||
|
|
||||||
|
return string.IsNullOrWhiteSpace(code) ? null : code;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -89,6 +89,8 @@ public class TransportWarningService : ApplicationServiceBase
|
|||||||
CargoType = x.CargoType,
|
CargoType = x.CargoType,
|
||||||
LeavePort = x.LeavePort,
|
LeavePort = x.LeavePort,
|
||||||
ProductCode = x.ProductCode,
|
ProductCode = x.ProductCode,
|
||||||
|
CountryCode = x.CountryCode,
|
||||||
|
AssessmentTotalTimeSpan = x.AssessmentTotalTimeSpan,
|
||||||
Months = x.Months,
|
Months = x.Months,
|
||||||
AreaId = x.AreaId,
|
AreaId = x.AreaId,
|
||||||
AreaName = x.AreaId.HasValue ? areaNameDict?.GetValueOrDefault(x.AreaId.Value) : null
|
AreaName = x.AreaId.HasValue ? areaNameDict?.GetValueOrDefault(x.AreaId.Value) : null
|
||||||
@ -96,37 +98,17 @@ public class TransportWarningService : ApplicationServiceBase
|
|||||||
|
|
||||||
return map.ToList();
|
return map.ToList();
|
||||||
|
|
||||||
// 构建场景摘要
|
// 构建场景摘要:产品 / 国家 / 总时效Xh
|
||||||
string BuildSummary(TransportWarningSceneEntity entity)
|
string BuildSummary(TransportWarningSceneEntity entity)
|
||||||
{
|
{
|
||||||
const string separator = "/";
|
var list = new List<string>(3);
|
||||||
// var sb = new StringBuilder();
|
|
||||||
var list = new List<string>(6);
|
|
||||||
// 货物类型
|
|
||||||
if (!string.IsNullOrWhiteSpace(entity.CargoType))
|
|
||||||
list.Add($"{entity.CargoType}");
|
|
||||||
// 月份
|
|
||||||
if (!string.IsNullOrWhiteSpace(entity.Months))
|
|
||||||
list.Add($"{entity.Months.Replace("/", ",")}月份");
|
|
||||||
// 区域名称
|
|
||||||
if (entity.AreaId != null && areaNameDict != null)
|
|
||||||
{
|
|
||||||
var areaName = areaNameDict.GetValueOrDefault(entity.AreaId.Value);
|
|
||||||
if (!string.IsNullOrWhiteSpace(areaName))
|
|
||||||
list.Add($"{areaName}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 中转
|
|
||||||
if (entity.IsTransit != null)
|
|
||||||
list.Add($"中转为{(entity.IsTransit.Value ? "是" : "否")}");
|
|
||||||
// 起运港口
|
|
||||||
if (!string.IsNullOrWhiteSpace(entity.LeavePort))
|
|
||||||
list.Add($"{entity.LeavePort}");
|
|
||||||
// 产品编码
|
|
||||||
if (!string.IsNullOrWhiteSpace(entity.ProductCode))
|
if (!string.IsNullOrWhiteSpace(entity.ProductCode))
|
||||||
list.Add($"{entity.ProductCode}");
|
list.Add(entity.ProductCode);
|
||||||
|
if (!string.IsNullOrWhiteSpace(entity.CountryCode))
|
||||||
return string.Join(separator, list);
|
list.Add(entity.CountryCode);
|
||||||
|
if (entity.AssessmentTotalTimeSpan is > 0)
|
||||||
|
list.Add($"{entity.AssessmentTotalTimeSpan.Value / 24f:0.##}天");
|
||||||
|
return list.Count == 0 ? "-" : string.Join(" / ", list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,14 +264,31 @@ public class TransportWarningService : ApplicationServiceBase
|
|||||||
|
|
||||||
var postBo = TransportWarningSceneBO.From(input);
|
var postBo = TransportWarningSceneBO.From(input);
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(postBo.ProductCode))
|
||||||
|
throw new BusinessException(message: "请配置销售产品");
|
||||||
|
if (postBo.ProductCode.Contains(',') || postBo.ProductCode.Contains('/'))
|
||||||
|
throw new BusinessException(message: "运输时效仅支持单一销售产品");
|
||||||
|
if (string.IsNullOrWhiteSpace(postBo.CountryCode))
|
||||||
|
throw new BusinessException(message: "请配置国家");
|
||||||
|
if (input.CountryCode != null &&
|
||||||
|
input.CountryCode.IndexOfAny([',', '/', ';', '|', ' ']) >= 0)
|
||||||
|
throw new BusinessException(message: "运输时效仅支持单一国家");
|
||||||
|
if (postBo.AssessmentTotalTimeSpan is null or <= 0)
|
||||||
|
throw new BusinessException(message: "请配置有效的考核总时效");
|
||||||
|
// 前端按整数天录入后转小时(天×24)
|
||||||
|
var totalHours = postBo.AssessmentTotalTimeSpan.Value;
|
||||||
|
if (Math.Abs(totalHours - MathF.Round(totalHours)) > 0.001f ||
|
||||||
|
Math.Abs(totalHours % 24f) > 0.001f)
|
||||||
|
throw new BusinessException(message: "考核总时效须为整天对应的小时数(天×24)");
|
||||||
|
|
||||||
// 查出这个规则主体下的所有场景
|
// 查出这个规则主体下的所有场景
|
||||||
var oldScenes = await _warningRuleRepository.GetTransportSceneAsync(input.WarningId);
|
var oldScenes = await _warningRuleRepository.GetTransportSceneAsync(input.WarningId);
|
||||||
var oldBos = oldScenes.Count > 0 ? TransportWarningSceneBO.From(oldScenes) : Empty<TransportWarningSceneBO>.List;
|
var oldBos = oldScenes.Count > 0 ? TransportWarningSceneBO.From(oldScenes) : Empty<TransportWarningSceneBO>.List;
|
||||||
|
|
||||||
// 验证规则场景是否重复
|
// 验证规则场景是否重复(同一方案下:产品+国家唯一)
|
||||||
if (oldBos.Any(old => old.Equals(postBo) && old.Id != postBo.Id))
|
if (oldBos.Any(old => old.Equals(postBo) && old.Id != postBo.Id))
|
||||||
{
|
{
|
||||||
throw new BusinessException(message: "同一规则主体下,不允许出现因素完全相同的场景!");
|
throw new BusinessException(message: "同一方案下,不允许出现产品与国家完全相同的配置!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新或者新增
|
// 更新或者新增
|
||||||
|
|||||||
@ -84,10 +84,13 @@ public class WarningService : ApplicationServiceBase
|
|||||||
throw new BusinessException(message: $"预警规则主体启用状态未发生变化,不允许重复操作");
|
throw new BusinessException(message: $"预警规则主体启用状态未发生变化,不允许重复操作");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果设置为启用则检查同一个应用同一客户下有多个启用的规则
|
// 启用查重:运输时效按 AppKey+类型;其它类型按 AppKey+客户+类型
|
||||||
if (input.IsEnabled && await _warningRuleRepository.CheckEnableDuplicateAsync(rule.Id, rule.AppKey, rule.Customer, (WarningRuleType) rule.WarningType))
|
if (input.IsEnabled && await _warningRuleRepository.CheckEnableDuplicateAsync(rule.Id, rule.AppKey, rule.Customer, (WarningRuleType) rule.WarningType))
|
||||||
{
|
{
|
||||||
throw new BusinessException(message: $"同一【应用+客户+类型】下不能有多个启用的预警规则主体,AppKey=[{rule.AppKey},Customer=[{rule.Customer}]]");
|
var scope = (WarningRuleType) rule.WarningType == WarningRuleType.运输时效预警
|
||||||
|
? $"同一【应用+类型】下不能有多个启用的预警规则主体,AppKey=[{rule.AppKey}]"
|
||||||
|
: $"同一【应用+客户+类型】下不能有多个启用的预警规则主体,AppKey=[{rule.AppKey},Customer=[{rule.Customer}]]";
|
||||||
|
throw new BusinessException(message: scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新规则主体状态
|
// 更新规则主体状态
|
||||||
@ -120,17 +123,28 @@ public class WarningService : ApplicationServiceBase
|
|||||||
throw new BusinessException(message: $"WarningId=[{input.Id}],WarningType=[{input.WarningType}]预警规则主体不存在,不允许修改");
|
throw new BusinessException(message: $"WarningId=[{input.Id}],WarningType=[{input.WarningType}]预警规则主体不存在,不允许修改");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 要求 AppKey+Customer+Name+WarningType 联合不能重复
|
// 运输时效方案级:客户固定为空,不参与匹配
|
||||||
if (await _warningRuleRepository.CheckNameDuplicateAsync(input.Id, input.AppKey, input.Customer, input.Name, (WarningRuleType) input.WarningType))
|
var customer = (WarningRuleType) input.WarningType == WarningRuleType.运输时效预警
|
||||||
|
? string.Empty
|
||||||
|
: (input.Customer ?? string.Empty);
|
||||||
|
|
||||||
|
if ((WarningRuleType) input.WarningType != WarningRuleType.运输时效预警 && string.IsNullOrWhiteSpace(customer))
|
||||||
|
throw new BusinessException(message: "客户不能为空");
|
||||||
|
|
||||||
|
// 名称查重:运输时效按 AppKey+Name+Type;其它按 AppKey+Customer+Name+Type
|
||||||
|
if (await _warningRuleRepository.CheckNameDuplicateAsync(input.Id, input.AppKey, customer, input.Name, (WarningRuleType) input.WarningType))
|
||||||
{
|
{
|
||||||
throw new BusinessException(message: $"AppKey:[{input.AppKey}],Customer:[{input.Customer}]," +
|
throw new BusinessException(message: $"AppKey:[{input.AppKey}],Customer:[{customer}]," +
|
||||||
$"WarningType:[{input.WarningType.ToString()}]已存在名称为[{input.Name}]规则,不允许重复创建/修改");
|
$"WarningType:[{input.WarningType.ToString()}]已存在名称为[{input.Name}]规则,不允许重复创建/修改");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果创建/修改的主体为启用状态,则要求通过检查
|
// 如果创建/修改的主体为启用状态,则要求通过检查
|
||||||
if (input.IsEnabled && await _warningRuleRepository.CheckEnableDuplicateAsync(input.Id, input.AppKey, input.Customer, (WarningRuleType) input.WarningType))
|
if (input.IsEnabled && await _warningRuleRepository.CheckEnableDuplicateAsync(input.Id, input.AppKey, customer, (WarningRuleType) input.WarningType))
|
||||||
{
|
{
|
||||||
throw new BusinessException(message: $"同一【应用+客户+预警类型】下不能有多个启用的预警规则主体,不允许创建/修改");
|
var scope = (WarningRuleType) input.WarningType == WarningRuleType.运输时效预警
|
||||||
|
? "同一【应用+预警类型】下不能有多个启用的预警规则主体,不允许创建/修改"
|
||||||
|
: "同一【应用+客户+预警类型】下不能有多个启用的预警规则主体,不允许创建/修改";
|
||||||
|
throw new BusinessException(message: scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
var enabledChanged = false;
|
var enabledChanged = false;
|
||||||
@ -140,7 +154,7 @@ public class WarningService : ApplicationServiceBase
|
|||||||
input.Id,
|
input.Id,
|
||||||
input.Name,
|
input.Name,
|
||||||
input.AppKey,
|
input.AppKey,
|
||||||
input.Customer,
|
customer,
|
||||||
input.IsEnabled,
|
input.IsEnabled,
|
||||||
input.WarningType
|
input.WarningType
|
||||||
);
|
);
|
||||||
@ -150,7 +164,7 @@ public class WarningService : ApplicationServiceBase
|
|||||||
enabledChanged = ruleEntity.IsEnabled != input.IsEnabled;
|
enabledChanged = ruleEntity.IsEnabled != input.IsEnabled;
|
||||||
ruleEntity.Name = input.Name;
|
ruleEntity.Name = input.Name;
|
||||||
ruleEntity.AppKey = input.AppKey;
|
ruleEntity.AppKey = input.AppKey;
|
||||||
ruleEntity.Customer = input.Customer;
|
ruleEntity.Customer = customer;
|
||||||
ruleEntity.IsEnabled = input.IsEnabled;
|
ruleEntity.IsEnabled = input.IsEnabled;
|
||||||
ruleEntity.WarningType = input.WarningType;
|
ruleEntity.WarningType = input.WarningType;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,6 +32,11 @@ public interface IWarningRuleRepository : IBasicRepository<WarningRuleEntity>
|
|||||||
// 获取启用的预警规则主体
|
// 获取启用的预警规则主体
|
||||||
Task<IReadOnlyDictionary<int, WarningRuleEntity>> GetWarningRuleForEnabledAsync(HashSet<(string AppKey, string Customer)> conditions, WarningRuleType warningType);
|
Task<IReadOnlyDictionary<int, WarningRuleEntity>> GetWarningRuleForEnabledAsync(HashSet<(string AppKey, string Customer)> conditions, WarningRuleType warningType);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 按 AppKey 获取启用的预警规则主体(运输时效:方案级,不按客户)
|
||||||
|
/// </summary>
|
||||||
|
Task<IReadOnlyDictionary<int, WarningRuleEntity>> GetWarningRuleForEnabledByAppKeysAsync(HashSet<string> appKeys, WarningRuleType warningType);
|
||||||
|
|
||||||
// 获取预警规则主体根据id
|
// 获取预警规则主体根据id
|
||||||
Task<WarningRuleEntity?> GetWarningRuleByIdAsync(int id, WarningRuleType? warningType = null);
|
Task<WarningRuleEntity?> GetWarningRuleByIdAsync(int id, WarningRuleType? warningType = null);
|
||||||
|
|
||||||
|
|||||||
@ -155,6 +155,12 @@ public sealed class TransportWarningDetailsIndex : IdBaseIndex
|
|||||||
// 产品代码(xxx)
|
// 产品代码(xxx)
|
||||||
if (!string.IsNullOrWhiteSpace(scene.ProductCode))
|
if (!string.IsNullOrWhiteSpace(scene.ProductCode))
|
||||||
res.Append($",{scene.ProductCode}");
|
res.Append($",{scene.ProductCode}");
|
||||||
|
// 国家(US)
|
||||||
|
if (!string.IsNullOrWhiteSpace(scene.CountryCode))
|
||||||
|
res.Append($",{scene.CountryCode}");
|
||||||
|
// 考核总时效
|
||||||
|
if (scene.AssessmentTotalTimeSpan is > 0)
|
||||||
|
res.Append($",考核总时效{scene.AssessmentTotalTimeSpan.Value / 24f:0.##}天");
|
||||||
|
|
||||||
// 规则配置信息
|
// 规则配置信息
|
||||||
res.Append($",生成预警时间{ruleItem.StartTrackWarningTimeSpan}小时" +
|
res.Append($",生成预警时间{ruleItem.StartTrackWarningTimeSpan}小时" +
|
||||||
|
|||||||
@ -13,29 +13,45 @@ public sealed class TransportWarningSceneEntity : FullAuditedEntity<int, int>
|
|||||||
[SugarColumn(ColumnName = "WarningId", ColumnDescription = "预警规则id")]
|
[SugarColumn(ColumnName = "WarningId", ColumnDescription = "预警规则id")]
|
||||||
public int WarningId { get; set; }
|
public int WarningId { get; set; }
|
||||||
|
|
||||||
[SugarColumn(ColumnName = "IsTransit", ColumnDescription = "是否中转")]
|
[SugarColumn(ColumnName = "IsTransit", ColumnDescription = "是否中转", IsNullable = true)]
|
||||||
public bool? IsTransit { get; set; } //是否中转
|
public bool? IsTransit { get; set; } //历史字段
|
||||||
|
|
||||||
[SugarColumn(ColumnName = "CargoType", ColumnDescription = "货物类型")]
|
[SugarColumn(ColumnName = "CargoType", ColumnDescription = "货物类型", IsNullable = true)]
|
||||||
public string? CargoType { get; set; } //货物类型,
|
public string? CargoType { get; set; } //历史字段
|
||||||
|
|
||||||
[SugarColumn(ColumnName = "LeavePort", ColumnDescription = "起运港口")]
|
[SugarColumn(ColumnName = "LeavePort", ColumnDescription = "起运港口", IsNullable = true)]
|
||||||
public string? LeavePort { get; set; } //起运港口
|
public string? LeavePort { get; set; } //历史字段
|
||||||
|
|
||||||
[SugarColumn(ColumnName = "ProductCode", ColumnDescription = "产品编码")]
|
[SugarColumn(ColumnName = "ProductCode", ColumnDescription = "产品编码", IsNullable = true)]
|
||||||
public string? ProductCode { get; set; } //产品编码
|
public string? ProductCode { get; set; } //单一销售产品
|
||||||
|
|
||||||
[SugarColumn(ColumnName = "Months", ColumnDescription = "月份")]
|
[SugarColumn(ColumnName = "CountryCode", ColumnDescription = "国家二字码(单一)", IsNullable = true, Length = 16)]
|
||||||
public string? Months { get; set; } //月份, 1/2/3
|
public string? CountryCode { get; set; } //单一国家二字码
|
||||||
|
|
||||||
[SugarColumn(ColumnName = "AreaId", ColumnDescription = "区域id")]
|
[SugarColumn(ColumnName = "AssessmentTotalTimeSpan", ColumnDescription = "考核总时效(小时)", IsNullable = true)]
|
||||||
public int? AreaId { get; set; } //区域Id,单分区
|
public float? AssessmentTotalTimeSpan { get; set; } //考核总时效
|
||||||
|
|
||||||
|
[SugarColumn(ColumnName = "Months", ColumnDescription = "月份", IsNullable = true)]
|
||||||
|
public string? Months { get; set; } //历史字段
|
||||||
|
|
||||||
|
[SugarColumn(ColumnName = "AreaId", ColumnDescription = "区域id", IsNullable = true)]
|
||||||
|
public int? AreaId { get; set; } //历史字段
|
||||||
|
|
||||||
public TransportWarningSceneEntity()
|
public TransportWarningSceneEntity()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public TransportWarningSceneEntity(int id, int warningId, string? cargoType, string? months, int? areaId, bool? isTransit, string? leavePort, string? productCode)
|
public TransportWarningSceneEntity(
|
||||||
|
int id,
|
||||||
|
int warningId,
|
||||||
|
string? cargoType,
|
||||||
|
string? months,
|
||||||
|
int? areaId,
|
||||||
|
bool? isTransit,
|
||||||
|
string? leavePort,
|
||||||
|
string? productCode,
|
||||||
|
string? countryCode = null,
|
||||||
|
float? assessmentTotalTimeSpan = null)
|
||||||
{
|
{
|
||||||
Id = id;
|
Id = id;
|
||||||
WarningId = warningId;
|
WarningId = warningId;
|
||||||
@ -45,5 +61,7 @@ public sealed class TransportWarningSceneEntity : FullAuditedEntity<int, int>
|
|||||||
IsTransit = isTransit;
|
IsTransit = isTransit;
|
||||||
LeavePort = leavePort;
|
LeavePort = leavePort;
|
||||||
ProductCode = productCode;
|
ProductCode = productCode;
|
||||||
|
CountryCode = countryCode;
|
||||||
|
AssessmentTotalTimeSpan = assessmentTotalTimeSpan;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,12 +15,16 @@ public class WarningRuleRepository : BasicRepository<WarningRuleEntity>, IWarnin
|
|||||||
|
|
||||||
public async Task<bool> CheckNameDuplicateAsync(int id, string appKey, string customer, string name, WarningRuleType warningType)
|
public async Task<bool> CheckNameDuplicateAsync(int id, string appKey, string customer, string name, WarningRuleType warningType)
|
||||||
{
|
{
|
||||||
var entity = await Context.Queryable<WarningRuleEntity>()
|
// 运输时效:AppKey + Name + Type(方案级,不按客户)
|
||||||
|
var query = Context.Queryable<WarningRuleEntity>()
|
||||||
.Where(x => x.AppKey == appKey
|
.Where(x => x.AppKey == appKey
|
||||||
&& x.Customer == customer
|
|
||||||
&& x.WarningType == (int) warningType
|
&& x.WarningType == (int) warningType
|
||||||
&& string.Equals(x.Name, name))
|
&& string.Equals(x.Name, name));
|
||||||
.FirstAsync();
|
|
||||||
|
if (warningType != WarningRuleType.运输时效预警)
|
||||||
|
query = query.Where(x => x.Customer == customer);
|
||||||
|
|
||||||
|
var entity = await query.FirstAsync();
|
||||||
|
|
||||||
// 不重复
|
// 不重复
|
||||||
if (entity == null)
|
if (entity == null)
|
||||||
@ -32,14 +36,18 @@ public class WarningRuleRepository : BasicRepository<WarningRuleEntity>, IWarnin
|
|||||||
|
|
||||||
public async Task<bool> CheckEnableDuplicateAsync(int warningId, string appKey, string customer, WarningRuleType warningType)
|
public async Task<bool> CheckEnableDuplicateAsync(int warningId, string appKey, string customer, WarningRuleType warningType)
|
||||||
{
|
{
|
||||||
var entity = await Context.Queryable<WarningRuleEntity>()
|
// 运输时效:同一 AppKey + 类型仅允许一个启用方案
|
||||||
|
var query = Context.Queryable<WarningRuleEntity>()
|
||||||
.Where(x => x.AppKey == appKey
|
.Where(x => x.AppKey == appKey
|
||||||
&& x.Customer == customer
|
|
||||||
&& x.WarningType == (int) warningType
|
&& x.WarningType == (int) warningType
|
||||||
&& x.IsEnabled)
|
&& x.IsEnabled);
|
||||||
.FirstAsync();
|
|
||||||
|
|
||||||
// 没有【应用+客户+类型】启用的规则
|
if (warningType != WarningRuleType.运输时效预警)
|
||||||
|
query = query.Where(x => x.Customer == customer);
|
||||||
|
|
||||||
|
var entity = await query.FirstAsync();
|
||||||
|
|
||||||
|
// 没有启用的规则
|
||||||
if (entity == null) return false;
|
if (entity == null) return false;
|
||||||
|
|
||||||
// 已经启用和当前规则不同,视为启用重复,不允许操作
|
// 已经启用和当前规则不同,视为启用重复,不允许操作
|
||||||
@ -117,6 +125,22 @@ public class WarningRuleRepository : BasicRepository<WarningRuleEntity>, IWarnin
|
|||||||
: list.ToDictionary(x => x.Id);
|
: list.ToDictionary(x => x.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IReadOnlyDictionary<int, WarningRuleEntity>> GetWarningRuleForEnabledByAppKeysAsync(HashSet<string> appKeys, WarningRuleType warningType)
|
||||||
|
{
|
||||||
|
if (appKeys.Count == 0)
|
||||||
|
return Empty<int, WarningRuleEntity>.Dict;
|
||||||
|
|
||||||
|
var list = await Context.Queryable<WarningRuleEntity>()
|
||||||
|
.Where(x => x.WarningType == (int) warningType
|
||||||
|
&& x.IsEnabled
|
||||||
|
&& appKeys.Contains(x.AppKey))
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return list.Count == 0
|
||||||
|
? Empty<int, WarningRuleEntity>.Dict
|
||||||
|
: list.ToDictionary(x => x.Id);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<WarningRuleEntity?> GetWarningRuleByIdAsync(int id, WarningRuleType? warningType = null)
|
public async Task<WarningRuleEntity?> GetWarningRuleByIdAsync(int id, WarningRuleType? warningType = null)
|
||||||
{
|
{
|
||||||
var result = await Context.Queryable<WarningRuleEntity>()
|
var result = await Context.Queryable<WarningRuleEntity>()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user