Merge branch 'S07' into test

This commit is contained in:
hardstylewyl 2025-03-27 15:21:21 +08:00
commit 25c76944fc
4 changed files with 92 additions and 54 deletions

View File

@ -7,7 +7,7 @@ public class NotifyConfig
{ {
private const string ContentKey = "content"; //内容参数 private const string ContentKey = "content"; //内容参数
public bool IsNeedToHandle { get; set; } = true; //是否需要处理(默认为未处理,传false为无需处理,针对站内信是否处理功能) public bool IsNeedToHandle { get; set; } = false; //是否需要处理(默认为未处理,传false为无需处理,针对站内信是否处理功能)
public int MsgSource { get; set; } // 消息来源:1.消息 2.任务 3.流程 4.消息提醒 public int MsgSource { get; set; } // 消息来源:1.消息 2.任务 3.流程 4.消息提醒
public int SystemId { get; set; } = 9498; //子系统id 业务中台 public int SystemId { get; set; } = 9498; //子系统id 业务中台
public string TemplateCode { get; set; } = "TrackWarningNotify"; //模板code public string TemplateCode { get; set; } = "TrackWarningNotify"; //模板code

View File

@ -269,8 +269,9 @@ public sealed class CompleteWarningHandler : ICompleteWarningHandler, ITransient
return; return;
} }
// 关心规则修改/删除 // 修改/关注/删除
var updateRuleItems = new List<CompleteWarningRuleItemBO>(); var updateRuleItems = new List<CompleteWarningRuleItemBO>();
var followRuleItems = new List<CompleteWarningRuleItemBO>();
var deleteRuleItems = new List<CompleteWarningRuleItemBO>(); var deleteRuleItems = new List<CompleteWarningRuleItemBO>();
// 构建id配置项字典 // 构建id配置项字典
@ -288,20 +289,24 @@ public sealed class CompleteWarningHandler : ICompleteWarningHandler, ITransient
var postRule = postDict.GetValueOrDefault(ruleItemId); var postRule = postDict.GetValueOrDefault(ruleItemId);
if (oldRule != null && postRule != null) if (oldRule != null && postRule != null)
{ {
//规则属性变更,(状态减少,时效变更) // 规则属性变更关注(状态减少,时效变更)
if (CanNeedFollow(oldRule, postRule)) var (follow, change) = CanNeedFollowAndChange(oldRule, postRule);
updateRuleItems.Add(postRule); //更新 关注新配置 if (follow) followRuleItems.Add(postRule); //需要关注的
if (change) updateRuleItems.Add(postRule); //更新
continue; continue;
} }
//规则条目被删除 // 规则条目被删除
if (oldRule != null && postRule == null) if (oldRule != null && postRule == null)
deleteRuleItems.Add(oldRule); //删除 关注旧配置 {
followRuleItems.Add(oldRule); //删除一定需要关注
deleteRuleItems.Add(oldRule); //删除
}
} }
// 3.处理变更 // 3.处理变更
await HandleRuleItemsChangeAsync(rule, addRuleItems, updateRuleItems, deleteRuleItems); await HandleRuleItemsChangeAsync(rule, addRuleItems, updateRuleItems, followRuleItems, deleteRuleItems);
} }
private async Task OnlyDeleteHandleAsync(WarningRuleEntity rule, List<CompleteWarningRuleItemBO> bos) private async Task OnlyDeleteHandleAsync(WarningRuleEntity rule, List<CompleteWarningRuleItemBO> bos)
@ -329,11 +334,11 @@ public sealed class CompleteWarningHandler : ICompleteWarningHandler, ITransient
WarningRuleEntity rule, WarningRuleEntity rule,
List<CompleteWarningRuleItemBO> addRuleItems, List<CompleteWarningRuleItemBO> addRuleItems,
List<CompleteWarningRuleItemBO> updateRuleItems, List<CompleteWarningRuleItemBO> updateRuleItems,
List<CompleteWarningRuleItemBO> followRuleItems,
List<CompleteWarningRuleItemBO> deleteRuleItems List<CompleteWarningRuleItemBO> deleteRuleItems
) )
{ {
var ruleItemIds = new HashSet<int>(updateRuleItems.Count + deleteRuleItems.Count); // 新增/修改/关注/删除 实体,稍后做处理
// 新增/修改/删除 实体,稍后做处理
if (addRuleItems.Count > 0) if (addRuleItems.Count > 0)
{ {
var addEntities = CompleteWarningRuleItemBO.ToEntities(addRuleItems); var addEntities = CompleteWarningRuleItemBO.ToEntities(addRuleItems);
@ -344,39 +349,44 @@ public sealed class CompleteWarningHandler : ICompleteWarningHandler, ITransient
{ {
var updateEntities = CompleteWarningRuleItemBO.ToEntities(updateRuleItems); var updateEntities = CompleteWarningRuleItemBO.ToEntities(updateRuleItems);
await _warningRuleRepository.BulkUpdateCompleteRuleItemsAsync(updateEntities); await _warningRuleRepository.BulkUpdateCompleteRuleItemsAsync(updateEntities);
ruleItemIds.UnionWith(updateEntities.Select(x => x.Id));
} }
// 删除规则条目是必须要关注
if (deleteRuleItems.Count > 0) if (deleteRuleItems.Count > 0)
{ {
var deleteRuleItemIds = deleteRuleItems.Select(x => x.Id).ToHashSet(); var deleteRuleItemIds = deleteRuleItems.Select(x => x.Id).ToHashSet();
await _warningRuleRepository.DeleteCompleteRuleItemsAsync(deleteRuleItemIds); await _warningRuleRepository.DeleteCompleteRuleItemsAsync(deleteRuleItemIds);
ruleItemIds.UnionWith(deleteRuleItemIds);
} }
// 更新规则主体版本号 // 更新规则主体版本号
await _warningRuleRepository.UpdateVersionAsync(rule); await _warningRuleRepository.UpdateVersionAsync(rule);
if (ruleItemIds.Count == 0) return; if (followRuleItems.Count == 0) return;
// 关闭预警规则关联的所有信息(包含明细,通知任务) // 关闭预警规则关联的所有信息(包含明细,通知任务)
var ruleItemIds = followRuleItems.Select(x => x.Id).ToHashSet();
await CloseWarningRuleLinkAsync(ruleItemIds); await CloseWarningRuleLinkAsync(ruleItemIds);
} }
private static bool CanNeedFollow(CompleteWarningRuleItemBO oldRule, CompleteWarningRuleItemBO postRule) private static (bool follow, bool change) CanNeedFollowAndChange(CompleteWarningRuleItemBO oldRule, CompleteWarningRuleItemBO postRule)
{ {
var defaultResult = (true, true);
// 关注的订单类型减少 // 关注的订单类型减少
var oldTypes = oldRule.OrderTypes; var oldTypes = oldRule.OrderTypes;
var postTypes = postRule.OrderTypes; var postTypes = postRule.OrderTypes;
if (oldTypes.Count > postTypes.Count) //数量减少,一定需要关注
return defaultResult;
foreach (var type in oldTypes) foreach (var type in oldTypes)
{ {
if (!postTypes.Contains(type)) if (!postTypes.Contains(type)) //类型减少,需要关注
return true; return defaultResult;
} }
// 检查点发生变化 // 检查点发生变化
if (oldRule.CheckPointTrackStatusId != postRule.CheckPointTrackStatusId) if (oldRule.CheckPointTrackStatusId != postRule.CheckPointTrackStatusId)
return true; return defaultResult;
// 要求的轨迹状态, // 要求的轨迹状态,
// 条目内或关系状态变更,比如 案例1:S1/S2,S4 => S1/S3,案例2: S1/S2,S4 => S1,S4 // 条目内或关系状态变更,比如 案例1:S1/S2,S4 => S1/S3,案例2: S1/S2,S4 => S1,S4
@ -385,15 +395,19 @@ public sealed class CompleteWarningHandler : ICompleteWarningHandler, ITransient
var postStatusIds = postRule.RequireTrackStatus.SelectMany(x => x).ToHashSet(); var postStatusIds = postRule.RequireTrackStatus.SelectMany(x => x).ToHashSet();
if (oldStatusIds.Count > postStatusIds.Count) //数量减少,一定需要关注 if (oldStatusIds.Count > postStatusIds.Count) //数量减少,一定需要关注
return true; return defaultResult;
foreach (var statusId in oldStatusIds) foreach (var statusId in oldStatusIds)
{ {
if (!postStatusIds.Contains(statusId)) //状态减少,需要关注 if (!postStatusIds.Contains(statusId)) //状态减少,需要关注
return true; return defaultResult;
} }
return false; // 通知对象
var oldNotifyUserIds = oldRule.NotifyUsers.Select(x => x.UserId).ToHashSet();
var postNotifyUserIds = oldRule.NotifyUsers.Select(x => x.UserId).ToHashSet();
var hasChange = !oldTypes.SetEquals(postTypes) || !oldStatusIds.SetEquals(postStatusIds) || !oldNotifyUserIds.SetEquals(postNotifyUserIds);
return (false, hasChange);
} }
#endregion todo: 规则项目变更 #endregion todo: 规则项目变更

View File

@ -504,8 +504,9 @@ public sealed class ReturnWarningHandler : IReturnWarningHandler, ITransientDepe
return; return;
} }
// 关心规则修改/删除 // 修改/关注/删除
var updateRuleItems = new List<ReturnWarningRuleItemBO>(); var updateRuleItems = new List<ReturnWarningRuleItemBO>();
var followRuleItems = new List<ReturnWarningRuleItemBO>();
var deleteRuleItems = new List<ReturnWarningRuleItemBO>(); var deleteRuleItems = new List<ReturnWarningRuleItemBO>();
// 2.差异化比较 // 2.差异化比较
@ -522,18 +523,22 @@ public sealed class ReturnWarningHandler : IReturnWarningHandler, ITransientDepe
if (oldRule != null && postRule != null) if (oldRule != null && postRule != null)
{ {
//规则属性变更,(状态减少,时效变更) //规则属性变更,(状态减少,时效变更)
if (CanNeedFollow(oldRule, postRule)) var (follow, change) = CanNeedFollowAndChange(oldRule, postRule);
updateRuleItems.Add(postRule); if (follow) followRuleItems.Add(postRule); //更新需要关注的
if (change) updateRuleItems.Add(postRule); //更新
continue; continue;
} }
//规则条目被删除 //规则条目被删除
if (oldRule != null && postRule == null) if (oldRule != null && postRule == null)
deleteRuleItems.Add(oldRule); {
followRuleItems.Add(oldRule); //删除一定需要关注
deleteRuleItems.Add(oldRule); //删除
}
} }
// 3.处理变更 // 3.处理变更
await HandleRuleItemsChangeAsync(rule, addRuleItems, updateRuleItems, deleteRuleItems); await HandleRuleItemsChangeAsync(rule, addRuleItems, updateRuleItems, followRuleItems, deleteRuleItems);
} }
private async Task OnlyAddHandleAsync(WarningRuleEntity rule, List<ReturnWarningRuleItemBO> bos) private async Task OnlyAddHandleAsync(WarningRuleEntity rule, List<ReturnWarningRuleItemBO> bos)
@ -560,9 +565,9 @@ public sealed class ReturnWarningHandler : IReturnWarningHandler, ITransientDepe
private async Task HandleRuleItemsChangeAsync(WarningRuleEntity rule, private async Task HandleRuleItemsChangeAsync(WarningRuleEntity rule,
List<ReturnWarningRuleItemBO> addRuleItems, List<ReturnWarningRuleItemBO> addRuleItems,
List<ReturnWarningRuleItemBO> updateRuleItems, List<ReturnWarningRuleItemBO> updateRuleItems,
List<ReturnWarningRuleItemBO> followRuleItems,
List<ReturnWarningRuleItemBO> deleteRuleItems) List<ReturnWarningRuleItemBO> deleteRuleItems)
{ {
var ruleItemIds = new HashSet<int>(updateRuleItems.Count + deleteRuleItems.Count);
// 1.新增/修改/删除规则,稍后做处理 // 1.新增/修改/删除规则,稍后做处理
if (addRuleItems.Count > 0) if (addRuleItems.Count > 0)
{ {
@ -574,39 +579,46 @@ public sealed class ReturnWarningHandler : IReturnWarningHandler, ITransientDepe
{ {
var updateEntities = ReturnWarningRuleItemBO.ToEntities(updateRuleItems); var updateEntities = ReturnWarningRuleItemBO.ToEntities(updateRuleItems);
await _warningRuleRepository.BulkUpdateReturnRuleItemsAsync(updateEntities); await _warningRuleRepository.BulkUpdateReturnRuleItemsAsync(updateEntities);
ruleItemIds.UnionWith(updateEntities.Select(x => x.Id));
} }
if (deleteRuleItems.Count > 0) if (deleteRuleItems.Count > 0)
{ {
var deleteRuleItemIds = deleteRuleItems.Select(x => x.Id).ToHashSet(); var deleteRuleItemIds = deleteRuleItems.Select(x => x.Id).ToHashSet();
await _warningRuleRepository.DeleteReturnRuleItemsAsync(deleteRuleItemIds); await _warningRuleRepository.DeleteReturnRuleItemsAsync(deleteRuleItemIds);
ruleItemIds.UnionWith(deleteRuleItemIds);
} }
// 更新规则主体版本号 // 更新规则主体版本号
await _warningRuleRepository.UpdateVersionAsync(rule); await _warningRuleRepository.UpdateVersionAsync(rule);
if (ruleItemIds.Count == 0) return; if (followRuleItems.Count == 0) return;
// 2.关闭预警规则关联的所有信息(包含明细,重试任务,通知任务) // 2.关闭预警规则关联的所有信息(包含明细,重试任务,通知任务)
var ruleItemIds = followRuleItems.Select(x => x.Id).ToHashSet();
await CloseWarningRuleLinkAsync(ruleItemIds); await CloseWarningRuleLinkAsync(ruleItemIds);
} }
private static bool CanNeedFollow(ReturnWarningRuleItemBO oldRule, ReturnWarningRuleItemBO postRule) private static (bool follow, bool change) CanNeedFollowAndChange(ReturnWarningRuleItemBO oldRule, ReturnWarningRuleItemBO postRule)
{ {
var defaultFollow = (true, true);
// 时效发生更改 // 时效发生更改
if (Math.Abs(oldRule.ReturnTimeSpan - postRule.ReturnTimeSpan) > 0.01f) if (Math.Abs(oldRule.ReturnTimeSpan - postRule.ReturnTimeSpan) > 0.01f)
return true; return defaultFollow;
// 关注的轨迹状态减少 // 关注的轨迹状态减少
var oldStatusIds = oldRule.TrackStatusIds; var oldStatusIds = oldRule.TrackStatusIds;
var postStatusIds = postRule.TrackStatusIds; var postStatusIds = postRule.TrackStatusIds;
foreach (var statusId in oldStatusIds) foreach (var statusId in oldStatusIds)
if (!postStatusIds.Contains(statusId)) {
return true; if (!postStatusIds.Contains(statusId)) // 状态减少,需要关注
return defaultFollow;
}
return false; // 通知对象
var oldNotifyUserIds = oldRule.NotifyUsers.Select(x => x.UserId).ToHashSet();
var postNotifyUserIds = oldRule.NotifyUsers.Select(x => x.UserId).ToHashSet();
var hasChange = !oldStatusIds.SetEquals(postStatusIds) || !oldNotifyUserIds.SetEquals(postNotifyUserIds);
return (false, hasChange);
} }
#endregion #endregion

View File

@ -702,8 +702,9 @@ public class TransportWarningHandler : ITransportWarningHandler, ITransientDepen
return; return;
} }
// 可能同时存在 新增/修改/删除 // 可能同时存在 新增/修改/修改中需要关注的/删除
var updateRuleItems = new List<TransportWarningSceneRuleItemBO>(); var updateRuleItems = new List<TransportWarningSceneRuleItemBO>();
var updateFollowRuleItems = new List<TransportWarningSceneRuleItemBO>();
var deleteRuleItems = new List<TransportWarningSceneRuleItemBO>(); var deleteRuleItems = new List<TransportWarningSceneRuleItemBO>();
// 2.差异化比较 // 2.差异化比较
@ -721,19 +722,19 @@ public class TransportWarningHandler : ITransportWarningHandler, ITransientDepen
if (oldRule != null && postRule != null) if (oldRule != null && postRule != null)
{ {
//规则属性变更,(起/止状态,时效变更) //规则属性变更,(起/止状态,时效变更)
if (CanNeedFollow(oldRule, postRule)) var (follow, change) = CanNeedFollowAndChange(oldRule, postRule);
updateRuleItems.Add(postRule); //更新 关注新的配置 if (follow) updateFollowRuleItems.Add(postRule); //需要关注的配置
if (change) updateRuleItems.Add(postRule); //更新
continue; continue;
} }
//规则条目被删除 //规则条目被删除
if (oldRule != null && postRule == null) if (oldRule != null && postRule == null)
deleteRuleItems.Add(oldRule); //删除 关注旧配置 deleteRuleItems.Add(oldRule); //删除
} }
// 3.处理变更 // 3.处理变更
await HandleRuleItemsChangeAsync(rule, scene, addRuleItems, updateRuleItems, deleteRuleItems); await HandleRuleItemsChangeAsync(rule, scene, addRuleItems, updateRuleItems, updateFollowRuleItems, deleteRuleItems);
} }
private async Task OnlyAddHandleAsync(WarningRuleEntity rule, TransportWarningSceneBO scene, List<TransportWarningSceneRuleItemBO> bos) private async Task OnlyAddHandleAsync(WarningRuleEntity rule, TransportWarningSceneBO scene, List<TransportWarningSceneRuleItemBO> bos)
@ -763,6 +764,7 @@ public class TransportWarningHandler : ITransportWarningHandler, ITransientDepen
TransportWarningSceneBO scene, TransportWarningSceneBO scene,
List<TransportWarningSceneRuleItemBO> addRuleItems, List<TransportWarningSceneRuleItemBO> addRuleItems,
List<TransportWarningSceneRuleItemBO> updateRuleItems, List<TransportWarningSceneRuleItemBO> updateRuleItems,
List<TransportWarningSceneRuleItemBO> updateFollowRuleItems,
List<TransportWarningSceneRuleItemBO> deleteRuleItems List<TransportWarningSceneRuleItemBO> deleteRuleItems
) )
{ {
@ -783,14 +785,15 @@ public class TransportWarningHandler : ITransportWarningHandler, ITransientDepen
if (deleteRuleItemIds.Count > 0) if (deleteRuleItemIds.Count > 0)
await _warningRuleRepository.DeleteTransportSceneRuleItemsAsync(deleteRuleItemIds); await _warningRuleRepository.DeleteTransportSceneRuleItemsAsync(deleteRuleItemIds);
if (updateRuleItems.Count == 0) return; // 不存在更新需要关注的,也不存在删除的
if (updateFollowRuleItems.Count == 0 && deleteRuleItemIds.Count == 0) return;
// 1.查询变更配置相关的预警生成任务(未关闭的) // 1.查询变更配置相关的预警生成任务(未关闭的)
var updateRuleItemIds = updateRuleItems.Select(x => x.Id).ToHashSet(); var updateFollowRuleItemIds = updateFollowRuleItems.Select(x => x.Id).ToHashSet();
var generateTasks = await _warningTaskRepository.GetTransportWarningDetailsGenerateTaskAsync(x => var generateTasks = await _warningTaskRepository.GetTransportWarningDetailsGenerateTaskAsync(x =>
!x.IsClosed !x.IsClosed
&& x.WarningId == scene.WarningId && x.WarningId == scene.WarningId
&& updateRuleItemIds.Contains(x.WarningSceneRuleItemId) && updateFollowRuleItemIds.Contains(x.WarningSceneRuleItemId)
); );
// 2.查询变更配置相关的预警通知任务(未关闭的) // 2.查询变更配置相关的预警通知任务(未关闭的)
@ -798,13 +801,13 @@ public class TransportWarningHandler : ITransportWarningHandler, ITransientDepen
!x.IsClosed !x.IsClosed
&& x.WarningId == rule.Id && x.WarningId == rule.Id
&& x.WarningType == WarningRuleType.运输时效预警.GetHashCode() && x.WarningType == WarningRuleType.运输时效预警.GetHashCode()
&& updateRuleItemIds.Contains(x.WarningRuleItemId) && updateFollowRuleItemIds.Contains(x.WarningRuleItemId)
); );
// 3.查询变更配置相关的预警明细(即将超时/已超时) // 3.查询变更配置相关的预警明细(即将超时/已超时)
var warningDetails = await _warningDetailsEsRepository.GetTransportWarningDetailsAsync(new GetTransportWarningDetailsVO var warningDetails = await _warningDetailsEsRepository.GetTransportWarningDetailsAsync(new GetTransportWarningDetailsVO
{ {
RuleItemIds = updateRuleItemIds, RuleItemIds = updateFollowRuleItemIds,
}); });
// 构建处理快 // 构建处理快
@ -830,11 +833,11 @@ public class TransportWarningHandler : ITransportWarningHandler, ITransientDepen
var ruleArea = scene.AreaId.HasValue var ruleArea = scene.AreaId.HasValue
? await _kpiAreaRepository.GetFirstAsync(x => x.Id == scene.AreaId.Value) ? await _kpiAreaRepository.GetFirstAsync(x => x.Id == scene.AreaId.Value)
: null; : null;
var updateRuleItemIdDict = updateRuleItems.ToDictionary(x => x.Id); var updateFollowRuleItemIdDict = updateFollowRuleItems.ToDictionary(x => x.Id);
foreach (var group in handleBlocks.GroupBy(x => (x.OrderId, x.RuleItemId))) foreach (var group in handleBlocks.GroupBy(x => (x.OrderId, x.RuleItemId)))
{ {
var key = group.Key; var key = group.Key;
var ruleItem = updateRuleItemIdDict[key.RuleItemId]; var ruleItem = updateFollowRuleItemIdDict[key.RuleItemId];
var orderTracks = orderTrackStatusIdsDict.GetValueOrDefault(key.OrderId); var orderTracks = orderTrackStatusIdsDict.GetValueOrDefault(key.OrderId);
if (orderTracks == null) continue; // tips:该订单不存在任何轨迹 if (orderTracks == null) continue; // tips:该订单不存在任何轨迹
var orderStatusIds = orderTracks var orderStatusIds = orderTracks
@ -844,7 +847,7 @@ public class TransportWarningHandler : ITransportWarningHandler, ITransientDepen
//存在起始,不存在截止,视为命中 //存在起始,不存在截止,视为命中
var reHit = ruleItem.CanHitRule(orderStatusIds); var reHit = ruleItem.CanHitRule(orderStatusIds);
// 命中规则的一定存在起始轨迹 // 命中规则的一定存在起始轨迹,选出最早轨迹
var earliestTrack = orderTracks var earliestTrack = orderTracks
.Where(x => x.TrackStatusId == ruleItem.StartTrackStatusId) .Where(x => x.TrackStatusId == ruleItem.StartTrackStatusId)
.OrderBy(x => x.TrackTime) .OrderBy(x => x.TrackTime)
@ -972,21 +975,30 @@ public class TransportWarningHandler : ITransportWarningHandler, ITransientDepen
); );
} }
private static bool CanNeedFollow(TransportWarningSceneRuleItemBO oldRule, TransportWarningSceneRuleItemBO postRule) private static (bool follow, bool change) CanNeedFollowAndChange(TransportWarningSceneRuleItemBO oldRule, TransportWarningSceneRuleItemBO postRule)
{ {
var defaultResult = (true, true);
// 起始轨迹状态id
if (oldRule.StartTrackStatusId != postRule.StartTrackStatusId) if (oldRule.StartTrackStatusId != postRule.StartTrackStatusId)
return true; return defaultResult;
// 截止轨迹状态id
if (oldRule.EndTrackStatusId != postRule.EndTrackStatusId) if (oldRule.EndTrackStatusId != postRule.EndTrackStatusId)
return true; return defaultResult;
// 运输预警时效
if (Math.Abs(oldRule.TransportTimeSpan - postRule.TransportTimeSpan) > 0.01f) if (Math.Abs(oldRule.TransportTimeSpan - postRule.TransportTimeSpan) > 0.01f)
return true; return defaultResult;
// 起始轨迹生成预警时间
if (Math.Abs(oldRule.StartTrackWarningTimeSpan - postRule.StartTrackWarningTimeSpan) > 0.01f) if (Math.Abs(oldRule.StartTrackWarningTimeSpan - postRule.StartTrackWarningTimeSpan) > 0.01f)
return true; return defaultResult;
return false; // 通知对象
var oldNotifyUserIds = oldRule.NotifyUsers.Select(x => x.UserId).ToHashSet();
var postNotifyUserIds = oldRule.NotifyUsers.Select(x => x.UserId).ToHashSet();
var hasChange = !oldNotifyUserIds.SetEquals(postNotifyUserIds);
return (false, hasChange);
} }
private enum HandleType private enum HandleType