diff --git a/src/SJZY.Track.Api/appsettings.json b/src/SJZY.Track.Api/appsettings.json index 41fc37c6..ac8a5882 100644 --- a/src/SJZY.Track.Api/appsettings.json +++ b/src/SJZY.Track.Api/appsettings.json @@ -7,7 +7,7 @@ "Apollo": { "AppId": "SJZY.Track.Configuration", "Env": "DEV", - "Namespace": "test", + "Namespace": "dev", "MetaServer": "http://apollo-service-test-apollo-configservice.apollo.svc.cluster.local:8080", "ConfigServer": [ "http://apollo-service-test-apollo-configservice.apollo.svc.cluster.local:8080" diff --git a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/BatchDeleteTrackByEsIdInput.cs b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/BatchDeleteTrackByEsIdInput.cs new file mode 100644 index 00000000..681b3b75 --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/BatchDeleteTrackByEsIdInput.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations; + +namespace SJZY.Track.Application.Contract.Dtos.ProvideServices; + +public class BatchDeleteTrackByEsIdInput +{ + + /// + /// 轨迹内容es id,必填 + /// + [MaxLength(500)] + public List TrackContentEsIds { get; set; } = new(); +} diff --git a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/BatchUpsertTrackInput.cs b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/BatchUpsertTrackInput.cs index d8cab392..3992554f 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/BatchUpsertTrackInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/BatchUpsertTrackInput.cs @@ -35,4 +35,82 @@ public class BatchUpsertTrackInput : OrderNosInfo [Required] [Range(-12, 12)] public float TrackTimeZone { get; set; } + + /// + /// 客户是否可见 + /// + [Required] + public bool IsVisibility { get; set; } + + /// + /// 数据来源:1外部服务商创建,2内部系统创建,3人工创建 + /// + [Required] + public int Source { get; set; } + + /// + /// 轨迹备注 + /// + public string? Remarks { get; set; } + + /// + /// 附件集合 + /// + public List? Files { get; set; } + + /// + /// 轨迹内容,若为空则取轨迹内容表中该轨迹Code对应的轨迹内容 + /// + public string? Content { get; set; } + + /// + /// 轨迹内容英文,若为空则取轨迹内容表中该轨迹Code对应的轨迹英文内容 + /// + public string? EnStatus { get; set; } + + /// + /// 轨迹发生地(城市) + /// + public string? TrackPlaceCity { get; set; } + + /// + /// 轨迹发生地(城市)Id + /// + public int? TrackPlaceCityId { get; set; } + + /// + /// 轨迹发生地(省/州) + /// + public string? TrackPlaceProvince { get; set; } + + /// + ///轨迹发生地(省/州)Id + /// + public int? TrackPlaceProvinceId { get; set; } + + /// + /// 轨迹发生地(国家/地区) + /// + public string? TrackPlaceNation { get; set; } + + /// + /// 轨迹发生地(国家/地区)Id + /// + public int? TrackPlaceNationId { get; set; } + + /// + /// 轨迹发生地邮编 + /// + public string? TrackPlacePostcode { get; set; } + + /// + /// 预计到达时间 + /// + public DateTime? EstimatedDeliveryDate { get; set; } + + /// + /// 目的地时区 + /// + [Range(-12, 12)] + public float? DestinationTimeZone { get; set; } } diff --git a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/FileItemDto.cs b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/FileItemDto.cs new file mode 100644 index 00000000..f483a7ab --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/FileItemDto.cs @@ -0,0 +1,14 @@ +namespace SJZY.Track.Application.Contract.Dtos.ProvideServices; + +public class FileItemDto +{ + /// + /// 文件osskey + /// + public string OssKey { get; set; } + + /// + /// 文件名称 + /// + public string FileName { get; set; } +} diff --git a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/GetDetailTimelineInput.cs b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/GetDetailTimelineInput.cs new file mode 100644 index 00000000..21ec867c --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/GetDetailTimelineInput.cs @@ -0,0 +1,19 @@ +namespace SJZY.Track.Application.Contract.Dtos.ProvideServices; + +public class GetDetailTimelineInput +{ + /// + /// 订单编号 + /// + public string OrderNumber { get; set; } + + /// + /// 订单所属子系统id + /// + public int SystemId { get; set; } + + /// + /// 当前用户登陆子系统id + /// + public int CurrenUserSystemId { get; set; } +} diff --git a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/GetDetailTimelineOutput.cs b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/GetDetailTimelineOutput.cs new file mode 100644 index 00000000..4441323c --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/GetDetailTimelineOutput.cs @@ -0,0 +1,92 @@ +namespace SJZY.Track.Application.Contract.Dtos.ProvideServices; + +public class GetDetailTimelineOutput +{ + /// + /// 轨迹编码 + /// + public string TrackCode { get; set; } + + /// + /// 轨迹内容 + /// + public string Content { get; set; } + + /// + /// 轨迹英文内容 + /// + public string EnContent { get; set; } + + /// + /// 轨迹发生时间 + /// + public long TrackTime { get; set; } + + /// + /// 轨迹时间前缀 + /// + public string TrackTimePrefix { get; set; } + + /// + /// 轨迹发生时区 + /// + public string TrackTimeZone { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } + + /// + /// 文件 osskey 集合 + /// + public List Files { get; set; } + + /// + /// 是否高亮 + /// + public bool IsHighlight { get; set; } + + /// + /// 是否显示悬停效果 + /// + public bool IsShowHover { get; set; } + + /// + /// 轨迹发生地 + /// + public string TrackPlace { get; set; } + + /// + /// 轨迹发生地(城市) + /// + public string? TrackPlaceCity { get; set; } + + /// + /// 轨迹发生地(省/州) + /// + public string? TrackPlaceProvince { get; set; } + + /// + /// 轨迹发生地(国家/地区) + /// + public string? TrackPlaceNation { get; set; } +} + +public class TrackFileItem +{ + /// + /// 文件osskey + /// + public string OssKey { get; set; } + + /// + /// 文件名称 + /// + public string FileName { get; set; } + + /// + /// 文件url + /// + public string Url { get; set; } +} diff --git a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/GetOrderDetailsInput.cs b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/GetOrderDetailsInput.cs index 265723e8..23cdaec4 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/GetOrderDetailsInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/GetOrderDetailsInput.cs @@ -86,4 +86,54 @@ public class TrackOrderDetailDto /// 创建人 /// public string? CreateUser { get; set; } + + /// + /// 客户是否可见 + /// + public bool IsVisibility { get; set; } + + /// + /// 数据来源:1外部服务商创建,2内部系统创建,3人工创建 + /// + public int Source { get; set; } + + /// + /// 备注 + /// + public string? Remarks { get; set; } + + /// + /// 附件集合 + /// + public List? Files { get; set; } + + /// + /// 轨迹发生地(城市) + /// + public string? TrackPlaceCity { get; set; } + + /// + /// 轨迹发生地(省/州) + /// + public string? TrackPlaceProvince { get; set; } + + /// + /// 轨迹发生地(国家/地区) + /// + public string? TrackPlaceNation { get; set; } + + /// + /// 轨迹发生地邮编 + /// + public string? TrackPlacePostcode { get; set; } + + /// + /// 预计到达时间 + /// + public DateTime? EstimatedDeliveryDate { get; set; } + + /// + /// 目的地时区 + /// + public float? DestinationTimeZone { get; set; } } diff --git a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/GetPageTrackOrderInput.cs b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/GetPageTrackOrderInput.cs index 61d9fb82..324e9c48 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/GetPageTrackOrderInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/GetPageTrackOrderInput.cs @@ -152,4 +152,19 @@ public class TrackOrderDto ///// 问题描述名称 ///// //public string? ProblemDescriptionName { get; set; } + + /// + /// 预计到达时间 + /// + public DateTime? EstimatedDeliveryDate { get; set; } + + /// + /// 目的地时区 + /// + public float? DestinationTimeZone { get; set; } + + /// + /// 第三方单号,服务商-轨迹0000000、轨迹服务商-0000000 + /// + public string? ThirdPartyOrderNumber { get; set; } } diff --git a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/UpdateOrderInput.cs b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/UpdateOrderInput.cs index 02f025e8..b46ff5be 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/UpdateOrderInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/UpdateOrderInput.cs @@ -57,6 +57,16 @@ public class UpdateOrderInput : OrderNoInfo /// public string? ProductCode { get; set; } + /// + /// 预计到达时间 + /// + public DateTime? EstimatedDeliveryDate { get; set; } + + /// + /// 目的地时区 + /// + public float? DestinationTimeZone { get; set; } + /// /// 拉取配置信息 /// @@ -76,6 +86,11 @@ public class PullConfigInfo [Required] public string ServiceCode { get; set; } + /// + /// api服务商订单号(第三方单号) + /// + public string? OrderNo { get; set; } + /// /// 开始轨迹code /// diff --git a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/UpsertTrackInput.cs b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/UpsertTrackInput.cs index 945a973b..309d652e 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/UpsertTrackInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/UpsertTrackInput.cs @@ -35,4 +35,82 @@ public class UpsertTrackInput : OrderNoInfo [Required] [Range(-12, 12)] public float? TrackTimeZone { get; set; } + + /// + /// 客户是否可见 + /// + [Required] + public bool IsVisibility { get; set; } + + /// + /// 数据来源:1外部服务商创建,2内部系统创建,3人工创建 + /// + [Required] + public int Source { get; set; } + + /// + /// 轨迹备注 + /// + public string? Remarks { get; set; } + + /// + /// 附件集合 + /// + public List? Files { get; set; } + + /// + /// 轨迹内容,若为空则取轨迹内容表中该轨迹Code对应的轨迹内容 + /// + public string? Content { get; set; } + + /// + /// 轨迹内容英文,若为空则取轨迹内容表中该轨迹Code对应的轨迹英文内容 + /// + public string? EnStatus { get; set; } + + /// + /// 轨迹发生地(城市) + /// + public string? TrackPlaceCity { get; set; } + + /// + /// 轨迹发生地(城市)Id + /// + public int? TrackPlaceCityId { get; set; } + + /// + /// 轨迹发生地(省/州) + /// + public string? TrackPlaceProvince { get; set; } + + /// + /// 轨迹发生地(省/州)Id + /// + public int? TrackPlaceProvinceId { get; set; } + + /// + /// 轨迹发生地(国家/地区) + /// + public string? TrackPlaceNation { get; set; } + + /// + /// 轨迹发生地(国家/地区)Id + /// + public int? TrackPlaceNationId { get; set; } + + /// + /// 轨迹发生地邮编 + /// + public string? TrackPlacePostcode { get; set; } + + /// + /// 预计到达时间 + /// + public DateTime? EstimatedDeliveryDate { get; set; } + + /// + /// 目的地时区 + /// + [Range(-12, 12)] + public float? DestinationTimeZone { get; set; } } diff --git a/src/SJZY.Track.Application.Contract/Dtos/TrackContents/EditTrackContentInput.cs b/src/SJZY.Track.Application.Contract/Dtos/TrackContents/EditTrackContentInput.cs index 6b7c663f..8b1aca31 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/TrackContents/EditTrackContentInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/TrackContents/EditTrackContentInput.cs @@ -46,6 +46,21 @@ public class EditTrackContentInput /// 是否对外 /// public bool IsExternal { get; set; } + + /// + /// 是否可重复 + /// + public bool IsRepeatable { get; set; } + + /// + /// 是否允许手动修改 + /// + public bool IsAllowManuallyUpdate { get; set; } + + /// + /// 是否允许手动创建 + /// + public bool IsAllowManuallyCreate { get; set; } } public class GetTrackContentDetailDto @@ -104,4 +119,19 @@ public class GetTrackContentDetailDto /// 是否对外 /// public bool IsExternal { get; set; } + + /// + /// 是否可重复 + /// + public bool IsRepeatable { get; set; } + + /// + /// 是否允许手动修改 + /// + public bool IsAllowManuallyUpdate { get; set; } + + /// + /// 是否允许手动创建 + /// + public bool IsAllowManuallyCreate { get; set; } } diff --git a/src/SJZY.Track.Application.Contract/Dtos/TrackContents/GetAvailableContentInput.cs b/src/SJZY.Track.Application.Contract/Dtos/TrackContents/GetAvailableContentInput.cs new file mode 100644 index 00000000..fb6aa163 --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/TrackContents/GetAvailableContentInput.cs @@ -0,0 +1,25 @@ +namespace SJZY.Track.Application.Contract.Dtos.TrackContents; + +public class GetAvailableContentInput +{ + /// + /// 子系统id + /// + public int SystemId { get; set; } + + /// + /// 订单号 + /// + public string? OrderNo { get; set; } + + /// + /// 操作类型 1=创建 2=修改 + /// + public TrackOptionType TrackOptionType { get; set; } +} + +public enum TrackOptionType +{ + 创建 = 1, + 修改 = 2 +} diff --git a/src/SJZY.Track.Application.Contract/Dtos/TrackContents/GetAvailableContentOutput.cs b/src/SJZY.Track.Application.Contract/Dtos/TrackContents/GetAvailableContentOutput.cs new file mode 100644 index 00000000..69e77773 --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/TrackContents/GetAvailableContentOutput.cs @@ -0,0 +1,29 @@ +namespace SJZY.Track.Application.Contract.Dtos.TrackContents; + +public class GetAvailableContentOutput +{ + /// + /// Id + /// + public int Id { get; set; } + + /// + /// 轨迹内容代码 + /// + public string Code { get; set; } + + /// + /// 轨迹内容 + /// + public string Content { get; set; } + + /// + /// 轨迹内容英文 + /// + public string EnContent { get; set; } + + /// + /// 是否对外 + /// + public bool IsExternal { get; set; } +} diff --git a/src/SJZY.Track.Application.Contract/Dtos/TrackContents/GetPageTrackContentInput.cs b/src/SJZY.Track.Application.Contract/Dtos/TrackContents/GetPageTrackContentInput.cs index 9a5693a3..1d5cceac 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/TrackContents/GetPageTrackContentInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/TrackContents/GetPageTrackContentInput.cs @@ -89,4 +89,19 @@ public class GetTrackContentListDto /// 修改人 /// public string UpdateUser { get; set; } + + /// + /// 是否可重复 + /// + public bool IsRepeatable { get; set; } + + /// + /// 是否允许手动修改 + /// + public bool IsAllowManuallyUpdate { get; set; } + + /// + /// 是否允许手动创建 + /// + public bool IsAllowManuallyCreate { get; set; } } diff --git a/src/SJZY.Track.Application.Contract/Dtos/TrackContents/GetTrackContentByCodeInput.cs b/src/SJZY.Track.Application.Contract/Dtos/TrackContents/GetTrackContentByCodeInput.cs new file mode 100644 index 00000000..8f7b1c85 --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/TrackContents/GetTrackContentByCodeInput.cs @@ -0,0 +1,14 @@ +namespace SJZY.Track.Application.Contract.Dtos.TrackContents; + +public class GetTrackContentByCodeInput +{ + /// + /// 子系统id + /// + public int SystemId { get; set; } + + /// + /// 轨迹内容code + /// + public string Code { get; set; } +} diff --git a/src/SJZY.Track.Application.Contract/Dtos/TrackContents/GetTrackContentByCodeOutput.cs b/src/SJZY.Track.Application.Contract/Dtos/TrackContents/GetTrackContentByCodeOutput.cs new file mode 100644 index 00000000..e584522d --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/TrackContents/GetTrackContentByCodeOutput.cs @@ -0,0 +1,19 @@ +namespace SJZY.Track.Application.Contract.Dtos.TrackContents; + +public class GetTrackContentByCodeOutput +{ + /// + /// 中文内容 + /// + public string Content { get; set; } + + /// + /// 英文内容 + /// + public string EnContent { get; set; } + + /// + /// 是否对外 + /// + public bool IsExternal { get; set; } +} diff --git a/src/SJZY.Track.Application.Contract/Events/TrackChangeAfterUpdateOrderEvent.cs b/src/SJZY.Track.Application.Contract/Events/TrackChangeAfterUpdateOrderEvent.cs index 037c5a8c..90c807d0 100644 --- a/src/SJZY.Track.Application.Contract/Events/TrackChangeAfterUpdateOrderEvent.cs +++ b/src/SJZY.Track.Application.Contract/Events/TrackChangeAfterUpdateOrderEvent.cs @@ -41,4 +41,14 @@ public class TrackChangeAfterUpdateOrderEvent /// 时区缺失和发生地缺失标记,1表示时区,2表示发生地,3表示两种 /// public int DeficiencyTagValue { get; set; } + + /// + /// 预计到达时间 + /// + public DateTime? EstimatedDeliveryDate { get; set; } + + /// + /// 签收地时区 + /// + public float? DestinationTimeZone { get; set; } } diff --git a/src/SJZY.Track.Application.Contract/Events/TrackChangeEto.cs b/src/SJZY.Track.Application.Contract/Events/TrackChangeEto.cs new file mode 100644 index 00000000..f2eb4fe9 --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Events/TrackChangeEto.cs @@ -0,0 +1,131 @@ +using SJZY.Track.Application.Contract.Dtos.ProvideServices; + +namespace SJZY.Track.Application.Contract.Events; + +public class TrackChangeEto +{ + /// + /// 操作类型:1新增,2删除,3修改 + /// + public int Option { get; set; } + + /// + /// 子系统id + /// + public int SubSystemId { get; set; } + + /// + /// 单号 + /// + public string OrderNo { get; set; } + + /// + /// 客户id + /// + public int CustomId { get; set; } + + /// + /// 订单id + /// + public OrderIdentifier[] OrderIdentifiers { get; set; } = null!; + + /// + /// 操作人 + /// + public string? OperationMan { get; set; } + + /// + /// 轨迹内容代码 + /// + public string TrackCode { get; set; } = null!; + + /// + /// 轨迹内容 + /// + public string? Content { get; set; } + + /// + /// 轨迹内容英文 + /// + public string? EnStatus { get; set; } + + /// + /// 轨迹状态代码 + /// + public string TrackStatusCode { get; set; } + + /// + /// 轨迹发生地 + /// + public string TrackPlace { get; set; } = null!; + + /// + /// 轨迹发生时间 + /// + public DateTime TrackTime { get; set; } + + /// + /// 轨迹发生地时区 + /// + public float TrackTimeZone { get; set; } + + /// + /// 客户是否可见 + /// + public bool IsVisibility { get; set; } + + /// + /// 数据来源:1外部服务商创建,2内部系统创建,3人工创建 + /// + public int Source { get; set; } + + /// + /// 轨迹备注 + /// + public string? Remarks { get; set; } + + /// + /// 附件集合 + /// + public List? Files { get; set; } + + /// + /// 轨迹发生地(城市) + /// + public string? TrackPlaceCity { get; set; } + + /// + /// 轨迹发生地(城市)Id + /// + public int? TrackPlaceCityId { get; set; } + + /// + /// 轨迹发生地(省/州) + /// + public string? TrackPlaceProvince { get; set; } + + /// + ///轨迹发生地(省/州)Id + /// + public int? TrackPlaceProvinceId { get; set; } + + /// + /// 轨迹发生地(国家/地区) + /// + public string? TrackPlaceNation { get; set; } + + /// + /// 轨迹发生地(国家/地区)Id + /// + public int? TrackPlaceNationId { get; set; } + + /// + /// 轨迹发生地邮编 + /// + public string? TrackPlacePostcode { get; set; } + + /// + /// 预计到达时间 + /// + public DateTime? EstimatedDeliveryDate { get; set; } +} diff --git a/src/SJZY.Track.Application.Contract/Events/TrackDeleteEvent.cs b/src/SJZY.Track.Application.Contract/Events/TrackDeleteEvent.cs index f395e972..e8072e27 100644 --- a/src/SJZY.Track.Application.Contract/Events/TrackDeleteEvent.cs +++ b/src/SJZY.Track.Application.Contract/Events/TrackDeleteEvent.cs @@ -7,6 +7,11 @@ public class TrackDeleteEvent /// public int SubSystemId { get; set; } + /// + /// es中的id + /// + public string[] EsIds { get; set; } + /// /// 订单ids /// @@ -22,6 +27,11 @@ public class TrackDeleteEvent /// public string? OperationMan { get; set; } + /// + /// 操作人姓名 + /// + public int OperationManId { get; set; } + /// /// 已经处理过的订单 /// diff --git a/src/SJZY.Track.Application.Contract/Events/TrackUpsertEvent.cs b/src/SJZY.Track.Application.Contract/Events/TrackUpsertEvent.cs index 1bd4b033..483c929b 100644 --- a/src/SJZY.Track.Application.Contract/Events/TrackUpsertEvent.cs +++ b/src/SJZY.Track.Application.Contract/Events/TrackUpsertEvent.cs @@ -1,3 +1,5 @@ +using SJZY.Track.Application.Contract.Dtos.ProvideServices; + namespace SJZY.Track.Application.Contract.Events; public class TrackUpsertEvent @@ -22,6 +24,11 @@ public class TrackUpsertEvent /// public string? OperationMan { get; set; } + /// + /// 操作人id + /// + public int OperationManId { get; set; } + /// /// true新增,false更新 /// @@ -54,6 +61,81 @@ public class TrackUpsertEvent public TrackRefreshStruct TrackRefreshData { get; set; } + /// + /// 客户是否可见 + /// + public bool IsVisibility { get; set; } + + /// + /// 数据来源:1外部服务商创建,2内部系统创建,3人工创建 + /// + public int Source { get; set; } + + /// + /// 轨迹备注 + /// + public string? Remarks { get; set; } + + /// + /// 附件集合 + /// + public List? Files { get; set; } + + /// + /// 轨迹内容,若为空则取轨迹内容表中该轨迹Code对应的轨迹内容 + /// + public string? Content { get; set; } + + /// + /// 轨迹内容英文,若为空则取轨迹内容表中该轨迹Code对应的轨迹英文内容 + /// + public string? EnStatus { get; set; } + + /// + /// 轨迹发生地(城市) + /// + public string? TrackPlaceCity { get; set; } + + /// + /// 轨迹发生地(城市)Id + /// + public int? TrackPlaceCityId { get; set; } + + /// + /// 轨迹发生地(省/州) + /// + public string? TrackPlaceProvince { get; set; } + + /// + ///轨迹发生地(省/州)Id + /// + public int? TrackPlaceProvinceId { get; set; } + + /// + /// 轨迹发生地(国家/地区) + /// + public string? TrackPlaceNation { get; set; } + + /// + /// 轨迹发生地(国家/地区)Id + /// + public int? TrackPlaceNationId { get; set; } + + /// + /// 轨迹发生地邮编 + /// + public string? TrackPlacePostcode { get; set; } + + /// + /// 预计到达时间 + /// + public DateTime? EstimatedDeliveryDate { get; set; } + + /// + /// 目的地时区 + /// + public float? DestinationTimeZone { get; set; } + public override string ToString() { return diff --git a/src/SJZY.Track.Application/AutoMapper/OrderAutoMapperProfile.cs b/src/SJZY.Track.Application/AutoMapper/OrderAutoMapperProfile.cs index 8fc8a9d4..f1a32d4d 100644 --- a/src/SJZY.Track.Application/AutoMapper/OrderAutoMapperProfile.cs +++ b/src/SJZY.Track.Application/AutoMapper/OrderAutoMapperProfile.cs @@ -33,6 +33,7 @@ public class OrderAutoMapperProfile : Profile .ForMember(dest => dest.Content, opt => opt.MapFrom(m => m.TrackContent)) .ForMember(dest => dest.EnContent, opt => opt.MapFrom(m => m.TrackEnContent)) .ForMember(dest => dest.TrackTimeZoneValue, opt => opt.MapFrom(m => m.TrackTimeZone)); + CreateMap(MemberList.None); CreateMap(MemberList.None); } diff --git a/src/SJZY.Track.Application/CAP/EventHandlers/TrackChangeAfterUpdateOrderEventHandler.cs b/src/SJZY.Track.Application/CAP/EventHandlers/TrackChangeAfterUpdateOrderEventHandler.cs index 153e9700..1f32a4e3 100644 --- a/src/SJZY.Track.Application/CAP/EventHandlers/TrackChangeAfterUpdateOrderEventHandler.cs +++ b/src/SJZY.Track.Application/CAP/EventHandlers/TrackChangeAfterUpdateOrderEventHandler.cs @@ -26,6 +26,8 @@ public class TrackChangeAfterUpdateOrderEventHandler : ITransientDependency, ICa return; } + //指示是否需要更新【预计签收时间】【签收地时区】 + var isNeedUpdate = false; var updateOrders = new List(); foreach (var order in orders) { @@ -55,20 +57,43 @@ public class TrackChangeAfterUpdateOrderEventHandler : ITransientDependency, ICa order.LatestTrackPlace = @event.TrackPlace; order.LatestTrackTime = @event.TrackTime; order.LatestTrackTimeZone = @event.TrackTimeZone; + //当【预计签收时间】【签收地时区】同时存在时则更新主表 + if (@event.EstimatedDeliveryDate.HasValue && @event.DestinationTimeZone.HasValue) + { + order.EstimatedDeliveryDate = @event.EstimatedDeliveryDate.Value; + order.DestinationTimeZone = @event.DestinationTimeZone.Value; + isNeedUpdate = true; + } + updateOrders.Add(order); } await _orderRepository.UpdateRangeAsync(updateOrders, - x => new - { - x.IsCompleted, - x.LatestTrackContent, - x.LatestTrackEnContent, - x.LatestTrackPlace, - x.LatestTrackTime, - x.LatestTrackTimeZone, - x.DeficiencyTag - }); + isNeedUpdate + ? x => new + { + x.IsCompleted, + x.LatestTrackContent, + x.LatestTrackEnContent, + x.LatestTrackPlace, + x.LatestTrackTime, + x.LatestTrackTimeZone, + x.DeficiencyTag, + //当标识指示需要更新时添加更新列 + x.EstimatedDeliveryDate, + x.DestinationTimeZone + } + : x => new + { + x.IsCompleted, + x.LatestTrackContent, + x.LatestTrackEnContent, + x.LatestTrackPlace, + x.LatestTrackTime, + x.LatestTrackTimeZone, + x.DeficiencyTag, + } + ); } private async Task> GetOrdersAsync(int[] orderIds) diff --git a/src/SJZY.Track.Application/CAP/EventHandlers/TrackDeleteEventHandler.cs b/src/SJZY.Track.Application/CAP/EventHandlers/TrackDeleteEventHandler.cs index 127305ac..436e55dd 100644 --- a/src/SJZY.Track.Application/CAP/EventHandlers/TrackDeleteEventHandler.cs +++ b/src/SJZY.Track.Application/CAP/EventHandlers/TrackDeleteEventHandler.cs @@ -1,9 +1,14 @@ +using System.Text; +using BSI.Stack.Rabbit; using DotNetCore.CAP; using SJZY.Expand.ABP.Core.Logger; using SJZY.Expand.ABP.Core.PublicMethords; using SJZY.Track.Application.Contract.Events; using SJZY.Track.Domain.Aggregates.OrderAggregate; +using SJZY.Track.Domain.Aggregates.TrackContentAggregate; using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; +using SJZY.Track.Domain.Aggregates.TrackStatusAggregate; +using SJZY.Track.Domain.Enums; using SJZY.Track.Domain.Queries; using Volo.Abp.DependencyInjection; @@ -13,17 +18,29 @@ public class TrackDeleteEventHandler : ITransientDependency, ICapSubscribe { private readonly ICapPublisher _capPublisher; private readonly IOrderRepository _orderRepository; + private readonly ITrackStatusRepository _trackStatusRepository; + private readonly ITrackContentRepository _trackContentRepository; private readonly IProvideQueries _provideQueries; private readonly ITrackDetailsEsRepository _trackDetailsEsRepository; + private readonly RabbitClientBus _rabbitClientBus; + private readonly IOrderOperationRecordEsRepository _orderOperationRecordEsRepository; public TrackDeleteEventHandler(IProvideQueries provideQueries, IOrderRepository orderRepository, ICapPublisher capPublisher, - ITrackDetailsEsRepository trackDetailsEsRepository) + ITrackDetailsEsRepository trackDetailsEsRepository, + RabbitClientBus rabbitClientBus, + ITrackStatusRepository trackStatusRepository, + ITrackContentRepository trackContentRepository, + IOrderOperationRecordEsRepository orderOperationRecordEsRepository) { _provideQueries = provideQueries; _orderRepository = orderRepository; _capPublisher = capPublisher; _trackDetailsEsRepository = trackDetailsEsRepository; + _rabbitClientBus = rabbitClientBus; + _trackStatusRepository = trackStatusRepository; + _trackContentRepository = trackContentRepository; + _orderOperationRecordEsRepository = orderOperationRecordEsRepository; } [CapSubscribe(GlobalConst.TrackDeleteSubscribe, Group = GlobalConst.TrackDeleteSubscribe)] @@ -31,7 +48,10 @@ public class TrackDeleteEventHandler : ITransientDependency, ICapSubscribe { if (!CanDelete(@event)) return; - await DeleteTrackAsync(@event); + var trackList = await DeleteTrackAsync(@event); + + //发布事件 + await PublicTrackTopicAsync(@event, trackList); // 根据订单关联关系异步向下刷(异步) await DownByOrderLink(@event); @@ -84,12 +104,8 @@ public class TrackDeleteEventHandler : ITransientDependency, ICapSubscribe private bool CanDelete(TrackDeleteEvent @event) { - // 系统创建的轨迹不允许删除 - if (@event.Visited.IsNullOrEmpty() && @event.OperationMan == GlobalConst.SystemUser) - { - LogHelper.Warn($"系统创建的轨迹不允许被删除:{@event}", true); - return false; - } + if (@event.OrderIds == null) + return true; // 订单循环关联检测 var dup = @event.Visited.Intersect(@event.OrderIds).ToArray(); @@ -102,14 +118,80 @@ public class TrackDeleteEventHandler : ITransientDependency, ICapSubscribe return true; } - private async Task DeleteTrackAsync(TrackDeleteEvent @event) + private async Task PublicTrackTopicAsync(TrackDeleteEvent eto, List trackList) { - // 删除轨迹(删的是同子系统下的单,所以code是统一的) - var tracks = await _trackDetailsEsRepository.GetLatestListAsync(@event.OrderIds, @event.TrackCodes); + if (trackList == null) + return; - var orderIds = await _trackDetailsEsRepository.DeleteTrackAsync(@event.OrderIds, @event.TrackCodes); + var statusList = await _trackStatusRepository.GetListAsync(m => m.SubSystemId == eto.SubSystemId); + if (statusList == null) + return; + + var contentList = await _trackContentRepository.GetTrackContentBySyetemIdAsync(eto.SubSystemId); + if (contentList == null) + return; + + foreach (var dataItem in trackList.GroupBy(m => new + { + m.TrackCode, + m.OrderNo + })) + { + var item = dataItem.First(); + var status = statusList.FirstOrDefault(m => m.Code == item.TrackStatusCode); + if (status == null || !status.IsToMq) + continue; + + var content = contentList.FirstOrDefault(m => m.TrackContentCode == item.TrackCode); + + var data = new TrackChangeEto() + { + OrderIdentifiers = dataItem.Select(m => new OrderIdentifier + { + OrderId = m.OrderId, + OrderNo = m.OrderNo, + OrderType = m.OrderType + }).ToArray(), + TrackStatusCode = item.TrackStatusCode, + TrackCode = item.TrackCode, + OperationMan = eto.OperationMan, + SubSystemId = eto.SubSystemId, + Option = 2 + }; + + _rabbitClientBus.Publish(GlobalConst.TrackChangeExchange, $"TrackChange.{content.SubSystemCode}", Encoding.UTF8.GetBytes(data.ToJson())); + } + } + + private async Task> DeleteTrackAsync(TrackDeleteEvent @event) + { + List orderIds; + List tracks; + if (@event.EsIds != null && @event.EsIds.Any()) //根据明确的esid进行删除 + { + tracks = await _trackDetailsEsRepository.GetDataByIds(@event.EsIds); + orderIds = tracks.Select(m => m.OrderId).Distinct().ToList(); + @event.OrderIds = orderIds.ToArray(); + + // 外部api获取的轨迹不允许删除 + if (tracks.Any(m => m.Source == TrackSourceEnum.ExternalServiceProvide.GetHashCode())) + return tracks; + + // 删除轨迹 + foreach (var item in tracks) + { + await _trackDetailsEsRepository.DeleteTrackByIdAsync(item.Id, item.IndexName); + } + } + else //原有逻辑,根据轨迹code和订单进行删除 + { + // 删除轨迹(删的是同子系统下的单,所以code是统一的) + tracks = await _trackDetailsEsRepository.GetLatestListAsync(@event.OrderIds, @event.TrackCodes); + + orderIds = await _trackDetailsEsRepository.DeleteTrackAsync(@event.OrderIds, @event.TrackCodes); + if (orderIds.Count == 0) return tracks; + } - if (orderIds.Count == 0) return; var orders = await _orderRepository.GetListAsync(x => orderIds.Contains(x.Id), x => new OrderEntity @@ -121,8 +203,7 @@ public class TrackDeleteEventHandler : ITransientDependency, ICapSubscribe LatestTrackTime = x.LatestTrackTime, LatestTrackTimeZone = x.LatestTrackTimeZone, }); - - if (orders.Count == 0) return; + if (orders.Count == 0) return tracks; foreach (var order in orders) { @@ -146,6 +227,19 @@ public class TrackDeleteEventHandler : ITransientDependency, ICapSubscribe order.LatestTrackTime = track.TrackTime; order.LatestTrackTimeZone = track.TrackTimeZone; } + + + // 写入操作日志 + _ = _orderOperationRecordEsRepository.AddAsync(new OrderOperationRecordIndex + { + OrderId = order.Id, + OrderNo = order.OrderNo, + OptionType = OrderOperationType.删除轨迹.ToString(), + Content = tracks?.Where(m=>m.OrderId == order.Id)?.ToList().ToJson(), + CreateUserId = @event.OperationManId, + CreateUserName = @event.OperationMan ?? "", + CreateTime = DateTime.Now + }); } await _orderRepository.UpdateRangeAsync(orders, @@ -157,5 +251,7 @@ public class TrackDeleteEventHandler : ITransientDependency, ICapSubscribe x.LatestTrackTime, x.LatestTrackTimeZone }); + + return tracks; } } diff --git a/src/SJZY.Track.Application/CAP/EventHandlers/TrackUpsertEventHandler.cs b/src/SJZY.Track.Application/CAP/EventHandlers/TrackUpsertEventHandler.cs index f30f51fc..c807dab6 100644 --- a/src/SJZY.Track.Application/CAP/EventHandlers/TrackUpsertEventHandler.cs +++ b/src/SJZY.Track.Application/CAP/EventHandlers/TrackUpsertEventHandler.cs @@ -1,10 +1,14 @@ +using System.Text; +using BSI.Stack.Rabbit; using DotNetCore.CAP; using SJZY.Expand.ABP.Core.Logger; using SJZY.Expand.ABP.Core.PublicMethords; using SJZY.Track.Application.Contract.Events; using SJZY.Track.Domain.Aggregates.OrderAggregate; using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; +using SJZY.Track.Domain.Aggregates.TrackStatusAggregate; using SJZY.Track.Domain.Dtos.TrackContents; +using SJZY.Track.Domain.Enums; using SJZY.Track.Domain.Queries; using Volo.Abp.DependencyInjection; using OrderIdentifier = SJZY.Track.Application.Contract.Events.OrderIdentifier; @@ -17,14 +21,23 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe private readonly IOrderRepository _orderRepository; private readonly IProvideQueries _provideQueries; private readonly ITrackDetailsEsRepository _trackDetailsEsRepository; + private readonly RabbitClientBus _rabbitClientBus; + private readonly IOrderOperationRecordEsRepository _orderOperationRecordEsRepository; - public TrackUpsertEventHandler(IOrderRepository orderRepository, ICapPublisher capPublisher, - IProvideQueries provideQueries, ITrackDetailsEsRepository trackDetailsEsRepository) + public TrackUpsertEventHandler( + IOrderRepository orderRepository, + ICapPublisher capPublisher, + IProvideQueries provideQueries, + ITrackDetailsEsRepository trackDetailsEsRepository, + RabbitClientBus rabbitClientBus, + IOrderOperationRecordEsRepository orderOperationRecordEsRepository) { _orderRepository = orderRepository; _capPublisher = capPublisher; _provideQueries = provideQueries; _trackDetailsEsRepository = trackDetailsEsRepository; + _rabbitClientBus = rabbitClientBus; + _orderOperationRecordEsRepository = orderOperationRecordEsRepository; } [CapSubscribe(GlobalConst.TrackUpsertSubscribe, Group = GlobalConst.TrackUpsertSubscribe)] @@ -34,13 +47,63 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe if (!CanDelete(@event)) return; var canNextOrderIds = new List(); - await HandleTrackAsync(@event, canNextOrderIds); + + var content = await HandleTrackAsync(@event, canNextOrderIds); + + // 处理广播事件 + PublicTrackTopic(@event, content); if (canNextOrderIds.Count == 0) return; // 根据订单关联关系异步向下刷(异步) await DownByOrderLink(@event, canNextOrderIds); } + /// + /// 发送轨迹广播 + /// + /// + /// + private void PublicTrackTopic(TrackUpsertEvent eto, TrackContentVO? content) + { + // var trackStatus = await _trackStatusRepository.GetFirstAsync(m => m.Code == content.TrackStatusCode); + if (content == null || !content.IsToMq) + return; + + var data = new TrackChangeEto() + { + EstimatedDeliveryDate = eto.EstimatedDeliveryDate, + TrackPlacePostcode = eto.TrackPlacePostcode, + TrackPlaceNation = eto.TrackPlaceNation, + TrackPlaceNationId = eto.TrackPlaceNationId, + TrackPlaceProvince = eto.TrackPlaceProvince, + TrackPlaceProvinceId = eto.TrackPlaceProvinceId, + TrackPlaceCity = eto.TrackPlaceCity, + TrackPlaceCityId = eto.TrackPlaceCityId, + Files = eto.Files, + Remarks = eto.Remarks, + Source = eto.Source, + IsVisibility = eto.IsVisibility, + TrackTimeZone = eto.TrackTimeZone, + TrackTime = eto.TrackTime, + TrackPlace = eto.TrackPlace, + TrackStatusCode = content.TrackStatusCode, + EnStatus = eto.EnStatus, + Content = eto.Content, + TrackCode = eto.TrackCode, + OperationMan = eto.OperationMan, + OrderIdentifiers = eto.OrderIdentifiers, + CustomId = eto.CustomId, + SubSystemId = eto.SubSystemId + }; + + if (eto.IsAdd) + data.Option = 1; + else + data.Option = 3; + + _rabbitClientBus.Publish(GlobalConst.TrackChangeExchange, $"TrackChange.{content.SubSystemCode}", Encoding.UTF8.GetBytes(data.ToJson())); + } + private async Task DownByOrderLink(TrackUpsertEvent @event, List canNextOrderIds) { // 根据订单关联关系异步向下刷(异步) @@ -125,7 +188,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe return true; } - private async Task HandleTrackAsync(TrackUpsertEvent @event, List canNextOrderIds) + private async Task HandleTrackAsync(TrackUpsertEvent @event, List canNextOrderIds) { var trackContent = await _provideQueries.GetTrackContentAsync(@event.SubSystemId, @event.TrackCode); @@ -133,12 +196,20 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe { // 找不到轨迹内容,直接结束 LogHelper.Warn($"找不到轨迹内容:{@event}", true); - return; + return default; } + //虽然在引发事件之前做了判断,引发之后并不一定立即处理,在此过程中轨迹内容仍然可能被修改,所以要二次进行判断。 + //通过来源指示是否为手动操作 + var manualOperation = @event.Source == (int) TrackSourceEnum.Artificial; + //轨迹内容标识不允许手动创建的 + if (manualOperation && @event.IsAdd && trackContent.IsAllowManuallyCreate == false) return trackContent; + //轨迹内容标识不允许手动更新的 + if (manualOperation && !@event.IsAdd && trackContent.IsAllowManuallyUpdate == false) return trackContent; + var insertTracks = await UpsertTrackAsync(@event, canNextOrderIds, trackContent); - if (canNextOrderIds.Count == 0) return; + if (canNextOrderIds.Count == 0) return trackContent; if (insertTracks.Any()) { @@ -171,6 +242,8 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe TrackPlace = @event.TrackPlace, TrackTime = @event.TrackTime, TrackTimeZone = @event.TrackTimeZone, + DestinationTimeZone = @event.DestinationTimeZone, + EstimatedDeliveryDate = @event.EstimatedDeliveryDate }); // 触发轨迹推送事件(异步) @@ -192,6 +265,8 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe } }; await _capPublisher.PublishAsync(GlobalConst.TrackPushSubscribe, trackPushEvent); + + return trackContent; } private async Task> UpsertTrackAsync(TrackUpsertEvent @event, List canNextOrderIds, @@ -210,6 +285,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe TrackDetailsIndex? track; if (!@event.IsAdd) { + //同一订单同轨迹code如果有多条轨迹明细则不允许修改 var existTracks = tracks.Where(x => x.OrderId == order.OrderId).ToArray(); if (existTracks.Length > 1) { @@ -220,13 +296,44 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe track = existTracks.FirstOrDefault(); if (track == null) continue; // 不存在不让改 + + //来源与外部服务商不允许修改 + if (track.Source == (int) TrackSourceEnum.ExternalServiceProvide) continue; } else { - track = tracks.FirstOrDefault(x => - x.OrderId == order.OrderId && x.TrackTime == @event.TrackTime && x.TrackPlace == @event.TrackPlace); + // 【批量新增】所有可能遇到的场景 + // 标识:允许重复,不允许重复,零条,一条,多条; + // 场景A:允许重复,零条 => 新增 + // 场景B:允许重复,一条 => 新增(约束受限新增,否则跳过新增) + // 场景C:允许重复,多条 => 新增(约束受限新增,否则跳过新增) + // 场景D:不允许重复,零条 => 新增 + // 场景E:不允许重复,一条 => 跳过 + // 场景F:不允许重复,(理想不会出现多条) => 跳过 + var existTracks = tracks.Where(x => x.OrderId == order.OrderId).ToArray(); + track = existTracks.FirstOrDefault(); - if (track != null) continue; // 存在不让创建 + //覆盖场景:B,C + if (trackContent.IsRepeatable && existTracks.Length >= 1) + { + if (existTracks.Any(x => x.TrackPlace == @event.TrackPlace && x.TrackTime == @event.TrackTime)) + { + //允许重复,出现【轨迹发生地】,【轨迹发生时间】的都重复,则不允许新增 + continue; + } + + // 设置null后面执行新增逻辑 + track = null; + } + + //不允许重复,并且记录大于等于一条,跳过 + //覆盖场景:E,F + if (!trackContent.IsRepeatable && existTracks.Length >= 1) + { + continue; + } + + //其余情况:A,D都为新增操作,无需检查数量,track为null执行新增操作 } if (track == default) @@ -246,15 +353,47 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe OriginTrackDataHash = @event.TrackRefreshData.OriginTrackDataHash ?? GlobalConst.EmptyString, CreateUser = @event.OperationMan ?? GlobalConst.SystemUser, CreateTime = DateTime.Now, + //来源轨迹内容,第一次新增的时候使用轨迹内容表提供的 TrackStatus = trackContent.TrackStatus, + TrackStatusCode = trackContent.TrackStatusCode, TrackContent = trackContent.TrackContent, - TrackEnContent = trackContent.TrackEnContent + TrackEnContent = trackContent.TrackEnContent, + //S06新增轨迹明细字段 + IsVisibility = @event.IsVisibility, + Source = @event.Source, + Remarks = @event.Remarks ?? GlobalConst.EmptyString, + Files = @event.Files?.Select(x => new FileItem + { + OssKey = x.OssKey, + FileName = x.FileName + }).ToList(), + TrackPlaceCity = @event.TrackPlaceCity ?? GlobalConst.EmptyString, + TrackPlaceCityId = @event.TrackPlaceCityId ?? GlobalConst.NotExistId, + TrackPlaceProvince = @event.TrackPlaceProvince ?? GlobalConst.EmptyString, + TrackPlaceProvinceId = @event.TrackPlaceProvinceId ?? GlobalConst.NotExistId, + TrackPlaceNation = @event.TrackPlaceNation ?? GlobalConst.EmptyString, + TrackPlaceNationId = @event.TrackPlaceNationId ?? GlobalConst.NotExistId, + TrackPlacePostcode = @event.TrackPlacePostcode ?? GlobalConst.EmptyString, + DestinationTimeZone = @event.DestinationTimeZone, + EstimatedDeliveryDate = @event.EstimatedDeliveryDate, }; track.ZeroTrackTime = Math.Abs(track.TrackTimeZone - GlobalConst.NoneTrackTimeZone) < 1 ? track.TrackTime : track.TrackTime.Subtract(TimeSpan.FromHours(track.TrackTimeZone)); insertTracks.Add(track); + + // 写入操作日志 + _ = _orderOperationRecordEsRepository.AddAsync(new OrderOperationRecordIndex + { + OrderId = order.OrderId, + OrderNo = order.OrderNo, + OptionType = OrderOperationType.创建轨迹.ToString(), + Content = track.ToJson(), + CreateUserId = @event.OperationManId, + CreateUserName = @event.OperationMan ?? "", + CreateTime = DateTime.Now + }); } else { @@ -267,7 +406,40 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe track.UpdateUser = @event.OperationMan ?? GlobalConst.SystemUser; track.UpdateTime = DateTime.Now; + //允许二次修改,在修改了轨迹内容后进行修改轨迹操作,如果输入的轨迹内容则以输入为准 + track.TrackContent = @event.Content ?? trackContent.TrackContent; + track.TrackEnContent = @event.EnStatus ?? trackContent.TrackEnContent; + //S06新增 + track.IsVisibility = @event.IsVisibility; + track.Remarks = @event.Remarks ?? GlobalConst.EmptyString; + track.Files = @event.Files?.Select(x => new FileItem + { + OssKey = x.OssKey, + FileName = x.FileName + }).ToList(); + track.TrackPlaceCity = @event.TrackPlaceCity ?? GlobalConst.EmptyString; + track.TrackPlaceCityId = @event.TrackPlaceCityId ?? GlobalConst.NotExistId; + track.TrackPlaceProvince = @event.TrackPlaceProvince ?? GlobalConst.EmptyString; + track.TrackPlaceProvinceId = @event.TrackPlaceProvinceId ?? GlobalConst.NotExistId; + track.TrackPlaceNation = @event.TrackPlaceNation ?? GlobalConst.EmptyString; + track.TrackPlaceNationId = @event.TrackPlaceNationId ?? GlobalConst.NotExistId; + track.TrackPlacePostcode = @event.TrackPlacePostcode ?? GlobalConst.EmptyString; + track.DestinationTimeZone = @event.DestinationTimeZone; + track.EstimatedDeliveryDate = @event.EstimatedDeliveryDate; + updateTracks.Add(track); + + // 写入操作日志 + _ = _orderOperationRecordEsRepository.AddAsync(new OrderOperationRecordIndex + { + OrderId = order.OrderId, + OrderNo = order.OrderNo, + OptionType = OrderOperationType.修改轨迹.ToString(), + Content = track.ToJson(), + CreateUserId = @event.OperationManId, + CreateUserName = @event.OperationMan ?? "", + CreateTime = DateTime.Now + }); } canNextOrderIds.Add(order.OrderId); diff --git a/src/SJZY.Track.Application/SJZY.Track.Application.csproj b/src/SJZY.Track.Application/SJZY.Track.Application.csproj index 926a7d69..0bcf26d3 100644 --- a/src/SJZY.Track.Application/SJZY.Track.Application.csproj +++ b/src/SJZY.Track.Application/SJZY.Track.Application.csproj @@ -10,6 +10,7 @@ + diff --git a/src/SJZY.Track.Application/Services/ProvideServices/OrderService.cs b/src/SJZY.Track.Application/Services/ProvideServices/OrderService.cs index fd7f723e..dea72486 100644 --- a/src/SJZY.Track.Application/Services/ProvideServices/OrderService.cs +++ b/src/SJZY.Track.Application/Services/ProvideServices/OrderService.cs @@ -36,6 +36,15 @@ public partial class OrderService : ApplicationService, IOrderService [HttpPost("Order/Upsert")] public async Task> UpsertAsync(UpsertOrderInput input) { + // 校验预计到达时间是否不是同时存在或同时不存在 + if ((input.EstimatedDeliveryDate.HasValue && input.DestinationTimeZone.HasValue == false) || + (input.EstimatedDeliveryDate.HasValue == false && input.DestinationTimeZone.HasValue)) + { + throw new BusinessException(message: "【目的地时区】和【预计到达时间】必须同时输入"); + } + + var currentUser = CurrentUserRequest.CurrentUser; + // 校验订单是否已存在,存在则更新 var order = await _orderRepository.GetFirstAsync(x => x.SubSystemId == input.SubSystemId && x.OrderType == input.OrderType && x.OrderNo == input.OrderNo); @@ -48,8 +57,10 @@ public partial class OrderService : ApplicationService, IOrderService { OrderId = order.Id, OrderNo = input.OrderNo, - OptionType = OptionType.修改.ToString(), + OptionType = OrderOperationType.修改订单.ToString(), Content = input.ToJson(), + CreateUserId = currentUser.UserId, + CreateUserName = currentUser.Name, }; _ = _orderOperationRecordEsRepository.AddAsync(index); @@ -64,6 +75,7 @@ public partial class OrderService : ApplicationService, IOrderService order.SubSystemId = input.SubSystemId; order.OrderFlights = ObjectMapper.Map, List>(input.FlightInfos); order.OrderPullStatus = BuiltOrderPullStatus(input.PullConfigInfos); + order.OrderPlatformNumbers = BuiltOrderPlatformNumbers(input.PullConfigInfos); var orderId = await _orderRepository.CreateOrderAsync(order); @@ -72,9 +84,10 @@ public partial class OrderService : ApplicationService, IOrderService { OrderId = orderId, OrderNo = input.OrderNo, - OptionType = OptionType.新增.ToString(), + OptionType = OrderOperationType.创建订单.ToString(), Content = input.ToJson(), - CreateUserId = CurrentUserRequest.CurrentUser.UserId, + CreateUserId = currentUser.UserId, + CreateUserName = currentUser.Name, CreateTime = DateTime.Now }; _ = _orderOperationRecordEsRepository.AddAsync(indexForAdd); @@ -115,6 +128,8 @@ public partial class OrderService : ApplicationService, IOrderService { order = ObjectMapper.Map(input, order); order.OrderFlights = ObjectMapper.Map, List>(input.FlightInfos); + order.OrderPullStatus = BuiltOrderPullStatus(input.PullConfigInfos); + order.OrderPlatformNumbers = BuiltOrderPlatformNumbers(input.PullConfigInfos); await _orderRepository.UpdateOrderAsync(order); } @@ -128,6 +143,7 @@ public partial class OrderService : ApplicationService, IOrderService var status = new OrderPullStatusEntity { ServiceProviderCode = x.ServiceCode, + OrderNo = x.OrderNo, PullStatus = TrackPullStatus.Init, StartTrackCode = x.StartCode ?? GlobalConst.EmptyString, EndTrackCode = x.EndCode ?? GlobalConst.EmptyString, @@ -147,4 +163,22 @@ public partial class OrderService : ApplicationService, IOrderService return status; }).ToList(); } + + private List BuiltOrderPlatformNumbers(List? pullConfigInfos) + { + if (pullConfigInfos == null) return new List(); + + return pullConfigInfos.Select(x => + { + var status = new OrderPlatformNumberEntity + { + //三方单号 + OrderNumber = x.OrderNo, + //三方平台名称,目前认为是服务商code + Platform = x.ServiceCode, + }; + + return status; + }).ToList(); + } } diff --git a/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs b/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs index 63fdd064..c9844221 100644 --- a/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs +++ b/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs @@ -5,6 +5,7 @@ using SJZY.Track.Application.Contract.Services.ProvideServices; using SJZY.Track.Domain.Aggregates.OrderAggregate; using SJZY.Track.Domain.Aggregates.TrackContentAggregate; using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; +using SJZY.Track.Domain.Enums; using SJZY.UserCenter.HttpApi.Services; using Volo.Abp.Http.Client.DynamicProxying; @@ -59,6 +60,47 @@ public partial class TrackService : ApplicationService, ITrackService throw new BusinessException(message: "轨迹时间不合法"); } + if ((input.EstimatedDeliveryDate.HasValue == false && input.DestinationTimeZone.HasValue) || + (input.EstimatedDeliveryDate.HasValue && input.DestinationTimeZone.HasValue == false)) + { + throw new BusinessException(message: "【目的地时区】和【预计到达时间】必须同时输入"); + } + + //当修改时,如果为来源为外部提供商则不允许修改 + if (!input.IsAdd && input.Source == (int) TrackSourceEnum.ExternalServiceProvide) + { + throw new BusinessException(message: "来源为外部服务商轨迹不允许修改"); + } + + var trackContent = await _trackContentRepository.GetByCodeAsync(input.SubSystemId, input.TrackCode); + if (trackContent == default) + { + throw new BusinessException(message: $"轨迹代码:{input.TrackCode}轨迹内容不存在"); + } + + //指示是否为手动操作 + var manualOperation = input.Source == (int) TrackSourceEnum.Artificial; + + //不允许手动创建 + if (manualOperation && input.IsAdd && !trackContent.IsAllowManuallyCreate) + { + throw new BusinessException(message: $"轨迹代码:{input.TrackCode}不允许手动创建"); + } + + //不允许手动更新 + if (manualOperation && !input.IsAdd && !trackContent.IsAllowManuallyUpdate) + { + throw new BusinessException(message: $"轨迹代码:{input.TrackCode}不允许手动更新"); + } + + var tracks = await _trackDetailsEsRepository.GetListAsync(new[] { order.Id }, input.TrackCode, true); + + //更新轨迹,但存在多条轨迹记录,理由:不清晰到底要修改哪一条或是全部修改 + if (!input.IsAdd && tracks.Count > 1) + { + throw new BusinessException(message: $"订单:{input.OrderNo},轨迹代码:{input.TrackCode}存在多条记录,不允许修改"); + } + var userInfo = await _requestCurrentUserService.Service.GetCurrentUserBasicInfoAsync(); if (userInfo.Code != GlobalConst.SuccessCode || userInfo.Data is null) { @@ -112,6 +154,14 @@ public partial class TrackService : ApplicationService, ITrackService var name = userInfo.Data.UserName; + //删除轨迹约束,只要其中有一条来源是外部的就立即返回错误 + var tracks = await _trackDetailsEsRepository + .GetListAsync(new[] { orderId }, input.TrackCodes, TrackSourceEnum.ExternalServiceProvide); + if (tracks.Count > 0) + { + throw new BusinessException(message: "存在来源于外部服务商的轨迹,不允许删除"); + } + // 删除轨迹 var @event = new TrackDeleteEvent { @@ -125,6 +175,48 @@ public partial class TrackService : ApplicationService, ITrackService return new UnifyResultDto(true, true); } + /// + /// 根据esid删除轨迹内容 + /// + /// + /// + [HttpPost("Track/BatchDeleteByEsId")] + public async Task BatchDeleteTrackByEsIdAsync(BatchDeleteTrackByEsIdInput input) + { + var tracks = await _trackDetailsEsRepository.GetDataByIds(input.TrackContentEsIds.ToArray()); + if (tracks == null || tracks.Count == 0) + return new UnifyResultDto(false, "未找到指定id下的轨迹内容"); + + if (tracks.Any(m => m.Source == TrackSourceEnum.ExternalServiceProvide.GetHashCode())) + return new UnifyResultDto(false, "存在来源于外部服务商的轨迹,不允许删除"); + + if (tracks.GroupBy(m => m.SubSystemId).Count() > 1) + return new UnifyResultDto(false, "暂不支持跨子系统批量删除"); + + // 获取用户name + var userInfo = await _requestCurrentUserService.Service.GetCurrentUserBasicInfoAsync(); + if (userInfo.Code != GlobalConst.SuccessCode || userInfo.Data is null) + { + throw new BusinessException(message: $"调用用户中心,获取用户信息失败:{userInfo.Msg}"); + } + + // 删除轨迹 + var currentUser = CurrentUserRequest.CurrentUser; + var userName = userInfo.Data.UserName; + if (string.IsNullOrEmpty(userName)) + userName = (await _orderRepository.GetUserById(currentUser.UserId))?.Name; + + var @event = new TrackDeleteEvent + { + EsIds = input.TrackContentEsIds.ToArray(), + SubSystemId = tracks.First().SubSystemId, + OperationMan = userName, + OperationManId = currentUser.UserId + }; + await _capPublisher.PublishAsync(GlobalConst.TrackDeleteSubscribe, @event); + + return new UnifyResultDto(true); + } /// /// 批量删除轨迹 @@ -153,13 +245,21 @@ public partial class TrackService : ApplicationService, ITrackService throw new BusinessException(message: $"调用用户中心,获取用户信息失败:{userInfo.Msg}"); } + //删除轨迹约束,只要其中有一条来源是外部的就立即返回错误 + var tracks = await _trackDetailsEsRepository + .GetListAsync(orderIds.ToArray(), input.TrackCodes, TrackSourceEnum.ExternalServiceProvide); + if (tracks.Count > 0) + { + throw new BusinessException(message: "存在来源于外部服务商的轨迹,不允许删除"); + } + // 删除轨迹 var @event = new TrackDeleteEvent { OrderIds = orderIds.ToArray(), TrackCodes = input.TrackCodes, SubSystemId = input.SubSystemId, - OperationMan = userInfo.Data.UserName + OperationMan = userInfo.Data.UserName, }; await _capPublisher.PublishAsync(GlobalConst.TrackDeleteSubscribe, @event); @@ -191,6 +291,57 @@ public partial class TrackService : ApplicationService, ITrackService throw new BusinessException(message: "订单一个都不存在"); } + //当修改时,如果为来源为外部提供商则不允许修改 + if (!input.IsAdd && input.Source == (int) TrackSourceEnum.ExternalServiceProvide) + { + throw new BusinessException(message: "来源为外部服务商轨迹不允许修改"); + } + + // 校验主体:批量新增只选取第一条订单进行校验 + //【新增】存在指定轨迹的内容,进行更新覆盖(只处理一条的情况,并且决定权交给用户),不存在直接插入即可 + //【更新】存在指定轨迹的内容(匹配到多条则不处理) + var order = orders.First(); + + if (input.TrackTime < DateTime.Now.AddYears(-100) || input.TrackTime > DateTime.Now.AddYears(100)) + { + throw new BusinessException(message: "轨迹时间不合法"); + } + + if ((input.EstimatedDeliveryDate.HasValue == false && input.DestinationTimeZone.HasValue) || + (input.EstimatedDeliveryDate.HasValue && input.DestinationTimeZone.HasValue == false)) + { + throw new BusinessException(message: "【目的地时区】和【预计到达时间】必须同时输入"); + } + + var trackContent = await _trackContentRepository.GetByCodeAsync(input.SubSystemId, input.TrackCode); + if (trackContent == default) + { + throw new BusinessException(message: $"轨迹代码:{input.TrackCode}轨迹内容不存在"); + } + + //指示是否为手动操作 + var manualOperation = input.Source == (int) TrackSourceEnum.Artificial; + + //不允许手动创建 + if (manualOperation && input.IsAdd && !trackContent.IsAllowManuallyCreate) + { + throw new BusinessException(message: $"轨迹代码:{input.TrackCode}不允许手动创建"); + } + + //不允许手动更新 + if (manualOperation && !input.IsAdd && !trackContent.IsAllowManuallyUpdate) + { + throw new BusinessException(message: $"轨迹代码:{input.TrackCode}不允许手动更新"); + } + + var tracks = await _trackDetailsEsRepository.GetListAsync(new[] { order.Id }, input.TrackCode, true); + + //更新轨迹,存在多条轨迹记录不允许更改,理由:不清晰到底要修改哪一条或是全部修改 + if (!input.IsAdd && tracks.Count > 1) + { + throw new BusinessException(message: $"订单:{order.OrderNo},轨迹代码:{input.TrackCode}存在多条记录,不允许修改"); + } + // 获取用户name var userInfo = await _requestCurrentUserService.Service.GetCurrentUserBasicInfoAsync(); if (userInfo.Code != GlobalConst.SuccessCode || userInfo.Data is null) @@ -216,7 +367,23 @@ public partial class TrackService : ApplicationService, ITrackService TrackPlace = input.TrackPlace, TrackTime = input.TrackTime, TrackTimeZone = input.TrackTimeZone, - OperationMan = userInfo.Data.UserName + OperationMan = userInfo.Data.UserName, + OperationManId = userInfo.Data.UserId, + IsVisibility = input.IsVisibility, + Source = input.Source, + Remarks = input.Remarks, + Files = input.Files, + Content = input.Content, + EnStatus = input.EnStatus, + TrackPlaceCity = input.TrackPlaceCity, + TrackPlaceCityId = input.TrackPlaceCityId, + TrackPlaceProvince = input.TrackPlaceProvince, + TrackPlaceProvinceId = input.TrackPlaceProvinceId, + TrackPlaceNation = input.TrackPlaceNation, + TrackPlaceNationId = input.TrackPlaceNationId, + TrackPlacePostcode = input.TrackPlacePostcode, + EstimatedDeliveryDate = input.EstimatedDeliveryDate, + DestinationTimeZone = input.DestinationTimeZone, }; await _capPublisher.PublishAsync(GlobalConst.TrackUpsertSubscribe, @event); } diff --git a/src/SJZY.Track.Application/Services/TrackContents/TrackContentService.cs b/src/SJZY.Track.Application/Services/TrackContents/TrackContentService.cs index faf3f428..8cb91e48 100644 --- a/src/SJZY.Track.Application/Services/TrackContents/TrackContentService.cs +++ b/src/SJZY.Track.Application/Services/TrackContents/TrackContentService.cs @@ -2,9 +2,12 @@ using System.Linq.Expressions; using Microsoft.Extensions.Localization; using SJZY.Expand.ABP.Core.Localization; using SJZY.Expand.ABP.Core.PublicMethords; +using SJZY.Track.Application.Contract.Dtos.ProvideServices; using SJZY.Track.Application.Contract.Dtos.TrackContents; using SJZY.Track.Application.Localization; +using SJZY.Track.Domain.Aggregates.OrderAggregate; using SJZY.Track.Domain.Aggregates.TrackContentAggregate; +using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; using SJZY.Track.Domain.Aggregates.TrackFlagAggregate; using SJZY.Track.Domain.Aggregates.TrackStatusAggregate; using SJZY.Track.Domain.Dtos.TrackContents; @@ -23,17 +26,23 @@ public class TrackContentService : ApplicationService private readonly ITrackContentRepository _trackContentRepository; private readonly IBasicQuery _trackFlagBasicQuery; private readonly IBasicQuery _trackStatusBasicQuery; + private readonly IOrderRepository _orderRepository; + private readonly ITrackDetailsEsRepository _trackDetailsEsRepository; public TrackContentService( IStringLocalizer stringLocalizer, ITrackContentRepository trackContentRepository, IBasicQuery trackStatusBasicQuery, - IBasicQuery trackFlagBasicQuery) + IBasicQuery trackFlagBasicQuery, + IOrderRepository orderRepository, + ITrackDetailsEsRepository trackDetailsEsRepository) { _stringLocalizer = stringLocalizer; _trackContentRepository = trackContentRepository; _trackStatusBasicQuery = trackStatusBasicQuery; _trackFlagBasicQuery = trackFlagBasicQuery; + _orderRepository = orderRepository; + _trackDetailsEsRepository = trackDetailsEsRepository; } /// @@ -96,8 +105,7 @@ public class TrackContentService : ApplicationService /// /// [HttpPost("GetList")] - public async Task> GetPageTrackContent( - GetPageTrackContentInput input) + public async Task> GetPageTrackContent(GetPageTrackContentInput input) { var vo = ObjectMapper.Map(input); var resultDto = await _trackContentRepository.GetPageTrackContent(vo); @@ -181,6 +189,99 @@ public class TrackContentService : ApplicationService return true; } + /// + /// 根据轨迹内容code获取轨迹内容 + /// + /// + /// + [HttpPost("GetTrackContentByCode")] + public async Task> GetTrackContentByCodeAsync([FromBody] GetTrackContentByCodeInput input) + { + if (input.SystemId <= 0) + return new UnifyResultDto(false, "子系统Id必须大于等于0"); + + if (string.IsNullOrEmpty(input.Code)) + return new UnifyResultDto(false, "轨迹内容code不能为空"); + + var content = await _trackContentRepository.GetByCodeAsync(input.SystemId, input.Code); + if (content == null) + return new UnifyResultDto(false, "在当前子系统中未找到指定code的轨迹内容"); + + var res = new UnifyResultDto(true) + { + Data = new GetTrackContentByCodeOutput + { + Content = content.Content, + EnContent = content.EnContent, + IsExternal = content.IsExternal + } + }; + + return res; + } + + /// + /// 创建轨迹时获取可用的轨迹内容 + /// + /// + /// + [HttpPost("GetAvailableContent")] + public async Task>> GetAvailableContent(GetAvailableContentInput input) + { + if (input.SystemId <= 0) + return new UnifyResultDto>(false, "系统id需大于0!"); + + if (string.IsNullOrEmpty(input.OrderNo)) + return new UnifyResultDto>(false, "单号不能为空!"); + + //获取当前订单下已有的轨迹内容 + var orderId = await _orderRepository.GetFirstAsync(x => x.OrderNo == input.OrderNo && x.SubSystemId == input.SystemId, x => x.Id); + if (orderId == default) + throw new BusinessException(message: "订单不存在!"); + + //获取轨迹详情 + var trackDetails = await _trackDetailsEsRepository.GetListAsync(orderId); + + //获取当前子系统下所有的轨迹内容 + var contentList = await _trackContentRepository.GetListBySystemIdAsync(input.SystemId); + if (contentList == null) + throw new BusinessException(message: "当前子系统下未找到任何可用的轨迹内容!"); + + + var res = new UnifyResultDto>(true) + { + Data = new List() + }; + + foreach (var item in contentList) + { + //新增时跳过已存在轨迹内容 + if (input.TrackOptionType == TrackOptionType.创建 && !item.IsRepeatable && trackDetails?.Any(m => m.TrackCode == item.Code) == true) + continue; + + // 修改时跳过不存在轨迹内容 + if (input.TrackOptionType == TrackOptionType.修改 && trackDetails != null && trackDetails.All(m => m.TrackCode != item.Code)) + continue; + + if (!item.IsAllowManuallyCreate && input.TrackOptionType == TrackOptionType.创建) //跳过不允许手动创建的轨迹内容 + continue; + + if (!item.IsAllowManuallyUpdate && input.TrackOptionType == TrackOptionType.修改) //跳过不允许手动创建的轨迹内容 + continue; + + res.Data.Add(new GetAvailableContentOutput() + { + IsExternal = item.IsExternal, + EnContent = item.EnContent, + Content = item.Content, + Code = item.Code, + Id = item.Id + }); + } + + return res; + } + #region 私有方法 private async Task AddAsync(EditTrackContentInput input) diff --git a/src/SJZY.Track.Application/Services/TrackOrders/TrackOrderService.cs b/src/SJZY.Track.Application/Services/TrackOrders/TrackOrderService.cs index 7c7950dd..af769700 100644 --- a/src/SJZY.Track.Application/Services/TrackOrders/TrackOrderService.cs +++ b/src/SJZY.Track.Application/Services/TrackOrders/TrackOrderService.cs @@ -1,9 +1,11 @@ +using SJZY.Expand.AliyunOSS; using SJZY.Track.Application.Contract.Dtos.ProvideServices; using SJZY.Track.Application.Contract.Dtos.TrackOrder; using SJZY.Track.Domain.Aggregates.OrderAggregate; using SJZY.Track.Domain.Aggregates.TrackContentAggregate; using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; using SJZY.Track.Domain.Aggregates.TrackPushAggregate; +using SJZY.Track.Domain.Aggregates.TrackStatusAggregate; using SJZY.Track.Domain.Dtos.Order; namespace SJZY.Track.Application.Services.TrackOrders; @@ -18,6 +20,7 @@ public class TrackOrderService : ApplicationService private readonly IOrderOperationRecordEsRepository _orderOperationRecordEsRepository; private readonly IOrderRepository _orderRepository; private readonly ITrackContentRepository _trackContentRepository; + private readonly ITrackStatusRepository _trackStatusRepository; private readonly ITrackDetailsEsRepository _trackDetailsEsRepository; private readonly ITrackPushRecordEsRepository _trackPushRecordEsRepository; @@ -26,13 +29,15 @@ public class TrackOrderService : ApplicationService ITrackDetailsEsRepository trackDetailsEsRepository, ITrackContentRepository trackContentRepository, ITrackPushRecordEsRepository trackPushRecordEsRepository, - IOrderOperationRecordEsRepository orderOperationRecordEsRepository) + IOrderOperationRecordEsRepository orderOperationRecordEsRepository, + ITrackStatusRepository trackStatusRepository) { _orderRepository = orderRepository; _trackDetailsEsRepository = trackDetailsEsRepository; _trackContentRepository = trackContentRepository; _trackPushRecordEsRepository = trackPushRecordEsRepository; _orderOperationRecordEsRepository = orderOperationRecordEsRepository; + _trackStatusRepository = trackStatusRepository; } #region 轨迹订单列表 @@ -93,7 +98,7 @@ public class TrackOrderService : ApplicationService int hours = (int) x.TrackTimeZone; // 提取整数部分作为小时 int minutes = (int) ((x.TrackTimeZone - hours) * 60); // 将小数部分乘以 60 转换为分钟 x.TrackGTMTimeZone = - $"GTM {(x.TrackTimeZone >= 0 ? "+" : "-")}{new TimeSpan(hours, minutes, 0).ToString().RemovePostFix(":00")}"; + $"GMT {(x.TrackTimeZone >= 0 ? "+" : "-")}{new TimeSpan(hours, minutes, 0).ToString().RemovePostFix(":00")}"; }); return res; @@ -144,7 +149,7 @@ public class TrackOrderService : ApplicationService { if (a.TrackTimeZoneValue == 0f) { - a.TrackTimeZone = "GTM +00:00"; + a.TrackTimeZone = "GMT +00:00"; } else if (Math.Abs(a.TrackTimeZoneValue - GlobalConst.NoneTrackTimeZone) < 1) { @@ -158,12 +163,121 @@ public class TrackOrderService : ApplicationService var offset = new TimeSpan(hours, minutes, 0); var timeZoneStr = offset.ToString(); var flag = a.TrackTimeZoneValue > 0f ? "+" : ""; - a.TrackTimeZone = $"GTM {flag}{timeZoneStr.RemovePostFix(":00")}"; + a.TrackTimeZone = $"GMT {flag}{timeZoneStr.RemovePostFix(":00")}"; } }); return list; } + + /// + /// 按时间轴获取轨迹内容 + /// + /// + /// + [HttpPost("GetDetailTimeline")] + public async Task>> GetDetailTimeline(GetDetailTimelineInput input) + { + var res = new UnifyResultDto>(true); + + var order = await _orderRepository.GetFirstAsync(m => m.OrderNo == input.OrderNumber); + if (order == null) + return new UnifyResultDto>(false, "订单不存在"); + + if (input.SystemId <= 0) + return new UnifyResultDto>(false, "子系统id不合法"); + + if (input.CurrenUserSystemId <= 0) + return new UnifyResultDto>(false, "当前登陆用户子系统id不合法"); + + var system = await _orderRepository.GetSystemInfoById(input.CurrenUserSystemId); + if (system == null) + return new UnifyResultDto>(false, "未找到指定id的子系统"); + + var trackStatus = await _trackStatusRepository.GetListAsync(m => m.SubSystemId == input.SystemId); + if (trackStatus == null) + return new UnifyResultDto>(false, "未找到子系统下的任何轨迹状态"); + + var trackContent = await _trackContentRepository.GetListAsync(m => m.SubSystemId == input.SystemId); + if (trackContent == null) + return new UnifyResultDto>(false, "未找到子系统下的任何轨迹内容"); + + var trackDetails = await _trackDetailsEsRepository.GetListAsync(order.Id); + res.Data = new List(); + var isCompleted = false; + foreach (var item in trackDetails) + { + var currentTrackStatus = trackStatus.FirstOrDefault(m => m.Code == item.TrackStatusCode); + var currentTrackContet = trackContent.FirstOrDefault(m => m.Code == item.TrackCode); + + //是否完结 + if (!isCompleted) + isCompleted = currentTrackStatus is { IsCompleted: true }; + + //如果是外部系统,且轨迹内容不对外则跳过这条轨迹内容 + if (system.SystemProperty == 2 && currentTrackContet != null && !currentTrackContet.IsExternal) + continue; + + res.Data.Add(new GetDetailTimelineOutput() + { + IsHighlight = true, + IsShowHover = true, + Remark = item.Remarks == null || item.Remarks == "--" ? "" : item.Remarks, + TrackTimeZone = $"GMT{(item.TrackTimeZone >= 0 ? "+" : "-")}{GetTimeZoneStr(item.TrackTimeZone)}", + TrackTime = ToUnixTimestamp(item.TrackTime), + TrackPlace = item.TrackPlace, + EnContent = item.TrackEnContent, + Files = item.Files == null + ? new List() + : item.Files!.Select(m => new TrackFileItem() + { + OssKey = m.OssKey, + FileName = m.FileName, + Url = m.OssKey.GetUrlByKey(), + }).ToList(), + Content = item.TrackContent, + TrackCode = item.TrackCode, + TrackPlaceNation = item.TrackPlaceNation, + TrackPlaceProvince = item.TrackPlaceProvince, + TrackPlaceCity = item.TrackPlaceCity, + }); + } + + //如果轨迹状态没完结则追加最后一个默认节点 + if (!isCompleted) + { + res.Data.Add(new GetDetailTimelineOutput() + { + Content = "签收", + IsHighlight = false, + TrackTimeZone = order.EstimatedDeliveryDate.HasValue ? $"GMT{(order.DestinationTimeZone >= 0 ? "+" : "-")}{order.DestinationTimeZone}" : "", + TrackTime = order.EstimatedDeliveryDate.HasValue ? ToUnixTimestamp(DateTime.Parse(order.EstimatedDeliveryDate.ToString()!)) : 0, + TrackTimePrefix = order.EstimatedDeliveryDate.HasValue ? "预计签收时间:" : "", + }); + } + + return res; + } + + public static long ToUnixTimestamp(DateTime time) + { + var sTime = TimeZoneInfo.ConvertTime(new DateTime(1970, 1, 1), TimeZoneInfo.Utc, TimeZoneInfo.Local); + return (long) (time - sTime).TotalMilliseconds; + } + + private string GetTimeZoneStr(float timeZone) + { + var time = timeZone.ToString().Split('.'); + if (time.Length > 1 && time[1] != "0") //如果时区有小数点 + { + return time[0] + ":30"; + } + else + { + return time[0] + ":00"; + } + } + /// /// 根据轨迹id删除单条轨迹 /// diff --git a/src/SJZY.Track.Application/SjzyTrackApplicationModule.cs b/src/SJZY.Track.Application/SjzyTrackApplicationModule.cs index 9a8cc79b..e36b86ce 100644 --- a/src/SJZY.Track.Application/SjzyTrackApplicationModule.cs +++ b/src/SJZY.Track.Application/SjzyTrackApplicationModule.cs @@ -3,9 +3,11 @@ using DotNetCore.CAP; using DotNetCore.CAP.Internal; using Microsoft.Extensions.Configuration; using RabbitMQ.Client; +using Savorboard.CAP.InMemoryMessageQueue; using SJZY.Expand.ABP.Core.Localization; using SJZY.Expand.ABP.Core.Logger; using SJZY.Expand.ABP.Core.PublicMethords; +using SJZY.Expand.AliyunOSS; using SJZY.InterfaceRelay.Application.Contracts; using SJZY.Track.Application.CAP; using SJZY.Track.Application.Contract; @@ -49,10 +51,7 @@ public class SjzyTrackApplicationModule : AbpModule }); //添加虚拟文件系统 - Configure(options => - { - options.FileSets.AddEmbedded("SJZY.Track.Application"); - }); + Configure(options => { options.FileSets.AddEmbedded("SJZY.Track.Application"); }); //添加多语言 var languageConfig = configuration.GetSection("Language"); @@ -81,45 +80,59 @@ public class SjzyTrackApplicationModule : AbpModule //CAP context.Services.AddSingleton(); context.Services.AddCapTableNameService(); - context.Services.AddCap(options => + if (!System.Diagnostics.Debugger.IsAttached) { - options.UseMySql(configuration["ConnectionStrings:DefaultConnection"]!); - options.UseRabbitMQ(configure => + context.Services.AddCap(options => { - configure.HostName = configuration["RabbitMQ:HostName"]!; - configure.UserName = configuration["RabbitMQ:UserName"]!; - configure.Password = configuration["RabbitMQ:Password"]!; - configure.Port = configuration.GetValue("RabbitMQ:Port"); - configure.BasicQosOptions = - new RabbitMQOptions.BasicQos(configuration.GetValue("CAP:PrefetchCount")); - }); - options.FailedRetryInterval = configuration.GetValue("CAP:FailedRetryInterval"); - options.FailedRetryCount = configuration.GetValue("CAP:FailedRetryCount"); - options.SucceedMessageExpiredAfter = configuration.GetValue("CAP:SucceedMessageExpiredAfter"); - options.FallbackWindowLookbackSeconds = - configuration.GetValue("CAP:FallbackWindowLookbackSeconds"); - - options.UseDashboard(dashboardOptions => - { - dashboardOptions.PathBase = System.Diagnostics.Debugger.IsAttached - ? "" - : configuration["Swagger:JsonUrlPrefix"]!; - }); - options.FailedThresholdCallback = faileCalback => - { - try + options.UseMySql(configuration["ConnectionStrings:DefaultConnection"]!); + options.UseRabbitMQ(configure => { - LogHelper.Error($@" + configure.HostName = configuration["RabbitMQ:HostName"]!; + configure.UserName = configuration["RabbitMQ:UserName"]!; + configure.Password = configuration["RabbitMQ:Password"]!; + configure.Port = configuration.GetValue("RabbitMQ:Port"); + configure.BasicQosOptions = + new RabbitMQOptions.BasicQos(configuration.GetValue("CAP:PrefetchCount")); + }); + options.FailedRetryInterval = configuration.GetValue("CAP:FailedRetryInterval"); + options.FailedRetryCount = configuration.GetValue("CAP:FailedRetryCount"); + options.SucceedMessageExpiredAfter = configuration.GetValue("CAP:SucceedMessageExpiredAfter"); + options.FallbackWindowLookbackSeconds = + configuration.GetValue("CAP:FallbackWindowLookbackSeconds"); + + options.UseDashboard(dashboardOptions => + { + dashboardOptions.PathBase = System.Diagnostics.Debugger.IsAttached + ? "" + : configuration["Swagger:JsonUrlPrefix"]!; + }); + options.FailedThresholdCallback = faileCalback => + { + try + { + LogHelper.Error($@" |-CAP失败回调事件: |-处理类型:{(faileCalback.MessageType == DotNetCore.CAP.Messages.MessageType.Subscribe ? "消费消息达到最大重试次数,需要开发人员介入" : "生产消息推送失败")} |-头部信息:{faileCalback.Message.Headers.ToJson()}"); - } - catch (Exception ex) - { - LogHelper.Error("【严重】CAP失败回调事件发生未处理异常" + ex); - } - }; - }).AddSubscribeFilter(); + } + catch (Exception ex) + { + LogHelper.Error("【严重】CAP失败回调事件发生未处理异常" + ex); + } + }; + }).AddSubscribeFilter(); + } + else + { + //是本地调试环境,用内存存储和内存队列 + context.Services.AddCap(capOptions => + { + capOptions.UseInMemoryStorage(); + capOptions.UseInMemoryMessageQueue(); + capOptions.UseDashboard(); + }).AddSubscribeFilter(); + Console.WriteLine("-- 当前为VS本地环境,用内存存储和内存队列"); + } context.Services.AddRabbitBoot(options => @@ -141,6 +154,17 @@ public class SjzyTrackApplicationModule : AbpModule o.QueueBind(GlobalConst.TrackRefreshSubscribe, GlobalConst.InterfaceRelayExchange, GlobalConst.TrackRefreshSubscribe); } + }, + new() + { + //广播轨迹 + Queue = GlobalConst.TrackRefreshSubscribe, + FetchCount = configuration.GetValue("TrackApp:TrackRefreshFetchCount"), + FailedRequeue = false, + Declaration = o => + { + o.ExchangeDeclare(GlobalConst.TrackChangeExchange, ExchangeType.Topic, true); + } } }; }, typeof(SjzyTrackApplicationModule).Assembly); @@ -148,6 +172,16 @@ public class SjzyTrackApplicationModule : AbpModule context.Services.Configure(configuration.GetSection("TrackApp")); context.Services.Configure( configuration.GetSection("InterfaceRelayParamsConfig")); + + //注册阿里云OSS服务 + context.Services.AddAliOSS(option => + { + option.AccessKeyId = configuration["OSS:AccessKeyId"]; + option.AccessKeySecret = configuration["OSS:AccessKeySecret"]; + option.Endpoint = configuration["OSS:Endpoint"]; + option.BucketName = configuration["OSS:BucketName"]; + option.ReturnHttpsUrl = true; + }); } public override void PreConfigureServices(ServiceConfigurationContext context) diff --git a/src/SJZY.Track.Domain/Aggregates/OrderAggregate/IOrderRepository.cs b/src/SJZY.Track.Domain/Aggregates/OrderAggregate/IOrderRepository.cs index be928230..ae172fc0 100644 --- a/src/SJZY.Track.Domain/Aggregates/OrderAggregate/IOrderRepository.cs +++ b/src/SJZY.Track.Domain/Aggregates/OrderAggregate/IOrderRepository.cs @@ -1,4 +1,5 @@ using SJZY.Track.Domain.Dtos.Order; +using SJZY.Track.Domain.VEntities; namespace SJZY.Track.Domain.Aggregates.OrderAggregate; @@ -131,4 +132,18 @@ public interface IOrderRepository : IBasicRepository Task UpdateOrderPullConfigForServiceProviderAsync(UpdateOrderPullConfigsInputVo inputVo); #endregion + + /// + /// 根据id获取子系统信息 + /// + /// + /// + Task GetSystemInfoById(int id); + + /// + /// 根据id获取员工姓名 + /// + /// + /// + Task GetUserById(int id); } diff --git a/src/SJZY.Track.Domain/Aggregates/OrderAggregate/OrderEntity.cs b/src/SJZY.Track.Domain/Aggregates/OrderAggregate/OrderEntity.cs index 9a24699e..b2e3a5ba 100644 --- a/src/SJZY.Track.Domain/Aggregates/OrderAggregate/OrderEntity.cs +++ b/src/SJZY.Track.Domain/Aggregates/OrderAggregate/OrderEntity.cs @@ -168,10 +168,25 @@ public class OrderEntity : IFullAuditedBasicEntitys, IAggregateRoot [SugarColumn(ColumnName = "ArrivalProvince")] public string? ArrivalProvince { get; set; } + /// + /// 预计到达时间 + /// + [SugarColumn(ColumnName = "EstimatedDeliveryDate")] + public DateTime? EstimatedDeliveryDate { get; set; } + + /// + /// 目的地时区 + /// + [SugarColumn(ColumnName = "DestinationTimeZone")] + public float? DestinationTimeZone { get; set; } + [SugarColumn(IsIgnore = true)] public List OrderFlights { get; set; } [SugarColumn(IsIgnore = true)] public List OrderPullStatus { get; set; } + [Navigate(NavigateType.OneToMany, nameof(OrderPlatformNumberEntity.OrderId))] + [SugarColumn(IsIgnore = true)] public List OrderPlatformNumbers { get; set; } + public void ResetTrackInfo() { IsCompleted = false; diff --git a/src/SJZY.Track.Domain/Aggregates/OrderAggregate/OrderOperationRecordIndex.cs b/src/SJZY.Track.Domain/Aggregates/OrderAggregate/OrderOperationRecordIndex.cs index cedba47d..c53ed8c5 100644 --- a/src/SJZY.Track.Domain/Aggregates/OrderAggregate/OrderOperationRecordIndex.cs +++ b/src/SJZY.Track.Domain/Aggregates/OrderAggregate/OrderOperationRecordIndex.cs @@ -40,8 +40,22 @@ public class OrderOperationRecordIndex : IdBaseIndex /// public int CreateUserId { get; set; } + /// + /// 创建人姓名 + /// + public string CreateUserName { get; set; } + /// /// 创建时间 /// public DateTime CreateTime { get; set; } } + +public enum OrderOperationType +{ + 创建订单 = 1, + 修改订单 = 2, + 创建轨迹 = 3, + 修改轨迹 = 4, + 删除轨迹 = 5 +} diff --git a/src/SJZY.Track.Domain/Aggregates/OrderAggregate/OrderPlatformNumberEntity.cs b/src/SJZY.Track.Domain/Aggregates/OrderAggregate/OrderPlatformNumberEntity.cs new file mode 100644 index 00000000..b4ae68bf --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/OrderAggregate/OrderPlatformNumberEntity.cs @@ -0,0 +1,32 @@ +namespace SJZY.Track.Domain.Aggregates.OrderAggregate; + +/// +/// 外部单号关联表 +/// +[SugarTable("t_order_platform_number")] +public class OrderPlatformNumberEntity : IFullAuditedBasicEntitys, IEntity +{ + /// + /// 自增主键 + /// + [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)] + public int Id { get; set; } + + /// + /// 订单id + /// + [SugarColumn(ColumnName = "OrderId")] + public int OrderId { get; set; } + + /// + /// 外部单号 + /// + [SugarColumn(ColumnName = "OrderNumber")] + public string? OrderNumber { get; set; } + + /// + /// 平台名称 + /// + [SugarColumn(ColumnName = "Platform")] + public string? Platform { get; set; } +} diff --git a/src/SJZY.Track.Domain/Aggregates/OrderAggregate/OrderTrackPullStatusEntity.cs b/src/SJZY.Track.Domain/Aggregates/OrderAggregate/OrderTrackPullStatusEntity.cs index 29215b28..b4db417c 100644 --- a/src/SJZY.Track.Domain/Aggregates/OrderAggregate/OrderTrackPullStatusEntity.cs +++ b/src/SJZY.Track.Domain/Aggregates/OrderAggregate/OrderTrackPullStatusEntity.cs @@ -18,6 +18,12 @@ public class OrderPullStatusEntity : ICreateBaicEntity, IUpdateBasicEntity, IEnt [SugarColumn(ColumnName = "OrderId")] public int OrderId { get; set; } + /// + /// api服务商订单号(第三方单号) + /// + [SugarColumn(ColumnName = "OrderNo")] + public string? OrderNo { get; set; } + /// /// 服务商code /// diff --git a/src/SJZY.Track.Domain/Aggregates/TrackContentAggregate/ITrackContentRepository.cs b/src/SJZY.Track.Domain/Aggregates/TrackContentAggregate/ITrackContentRepository.cs index 000dbe3b..fe3a81d7 100644 --- a/src/SJZY.Track.Domain/Aggregates/TrackContentAggregate/ITrackContentRepository.cs +++ b/src/SJZY.Track.Domain/Aggregates/TrackContentAggregate/ITrackContentRepository.cs @@ -26,4 +26,7 @@ public interface ITrackContentRepository : IBasicRepository Expression> whereExpression); Task GetTemplateIsExist(int trackContentId); + Task GetByCodeAsync(int systemId, string code); + Task> GetListBySystemIdAsync(int systemId); + Task> GetTrackContentBySyetemIdAsync(int subSystemId); } diff --git a/src/SJZY.Track.Domain/Aggregates/TrackContentAggregate/TrackContentEntity.cs b/src/SJZY.Track.Domain/Aggregates/TrackContentAggregate/TrackContentEntity.cs index ee797ce1..b6809891 100644 --- a/src/SJZY.Track.Domain/Aggregates/TrackContentAggregate/TrackContentEntity.cs +++ b/src/SJZY.Track.Domain/Aggregates/TrackContentAggregate/TrackContentEntity.cs @@ -36,6 +36,12 @@ public class TrackContentEntity : IFullAuditedBasicEntitys, IAggregateRoot [SugarColumn(ColumnName = "SubSystemId")] public int SubSystemId { get; set; } + /// + /// 子系统名称 + /// + [SugarColumn(IsIgnore = true)] + public string SubSystem { get; set; } + /// /// 轨迹标记id,多个逗号分隔 /// @@ -53,4 +59,22 @@ public class TrackContentEntity : IFullAuditedBasicEntitys, IAggregateRoot /// [SugarColumn(ColumnName = "IsExternal")] public bool IsExternal { get; set; } + + /// + /// 是否可重复 + /// + [SugarColumn(ColumnName = "IsRepeatable")] + public bool IsRepeatable { get; set; } + + /// + /// 是否允许手动修改 + /// + [SugarColumn(ColumnName = "IsAllowManuallyUpdate")] + public bool IsAllowManuallyUpdate { get; set; } + + /// + /// 是否允许手动创建 + /// + [SugarColumn(ColumnName = "IsAllowManuallyCreate")] + public bool IsAllowManuallyCreate { get; set; } } diff --git a/src/SJZY.Track.Domain/Aggregates/TrackDetailsAggregate/FileItem.cs b/src/SJZY.Track.Domain/Aggregates/TrackDetailsAggregate/FileItem.cs new file mode 100644 index 00000000..2d2dc4a3 --- /dev/null +++ b/src/SJZY.Track.Domain/Aggregates/TrackDetailsAggregate/FileItem.cs @@ -0,0 +1,14 @@ +namespace SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; + +public class FileItem +{ + /// + /// 文件osskey + /// + public string OssKey { get; set; } + + /// + /// 文件名称 + /// + public string FileName { get; set; } +} diff --git a/src/SJZY.Track.Domain/Aggregates/TrackDetailsAggregate/ITrackDetailsEsRepository.cs b/src/SJZY.Track.Domain/Aggregates/TrackDetailsAggregate/ITrackDetailsEsRepository.cs index b52f5f20..a8da5185 100644 --- a/src/SJZY.Track.Domain/Aggregates/TrackDetailsAggregate/ITrackDetailsEsRepository.cs +++ b/src/SJZY.Track.Domain/Aggregates/TrackDetailsAggregate/ITrackDetailsEsRepository.cs @@ -1,4 +1,5 @@ using SJZY.Expand.ElasticSearch.Services; +using SJZY.Track.Domain.Enums; namespace SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; @@ -22,7 +23,10 @@ public interface ITrackDetailsEsRepository : IElasticsearchClientService> GetListAsync(int orderId); + Task> GetListAsync(int[] orderIds, List trackCodes, TrackSourceEnum source); + Task> DeleteTrackAsync(int[] orderIds, List trackCodes); Task DeleteTrackByIdAsync(string id, string indexName); + Task> GetDataByIds(string[] ids); } diff --git a/src/SJZY.Track.Domain/Aggregates/TrackDetailsAggregate/TrackDetailsIndex.cs b/src/SJZY.Track.Domain/Aggregates/TrackDetailsAggregate/TrackDetailsIndex.cs index a89fb67e..c842786b 100644 --- a/src/SJZY.Track.Domain/Aggregates/TrackDetailsAggregate/TrackDetailsIndex.cs +++ b/src/SJZY.Track.Domain/Aggregates/TrackDetailsAggregate/TrackDetailsIndex.cs @@ -37,6 +37,11 @@ public class TrackDetailsIndex : IdBaseIndex /// public string TrackStatus { get; set; } = GlobalConst.EmptyString; + /// + /// 轨迹状态code + /// + public string TrackStatusCode { get; set; } = GlobalConst.EmptyString; + /// /// 轨迹内容代码 /// @@ -106,4 +111,69 @@ public class TrackDetailsIndex : IdBaseIndex /// 修改时间 /// public DateTime? UpdateTime { get; set; } + + /// + /// 客户是否可见 + /// + public bool IsVisibility { get; set; } + + /// + /// 数据来源:1外部服务商创建,2内部系统创建,3人工创建 + /// + public int Source { get; set; } + + /// + /// 备注 + /// + public string? Remarks { get; set; } = GlobalConst.EmptyString; + + /// + /// 附件集合 + /// + public List? Files { get; set; } + + /// + /// 轨迹发生地(城市) + /// + public string? TrackPlaceCity { get; set; } = GlobalConst.EmptyString; + + /// + /// 轨迹发生地(城市)Id + /// + public int? TrackPlaceCityId { get; set; } = GlobalConst.NotExistId; + + /// + /// 轨迹发生地(省/州) + /// + public string? TrackPlaceProvince { get; set; } = GlobalConst.EmptyString; + + /// + ///轨迹发生地(省/州)Id + /// + public int? TrackPlaceProvinceId { get; set; } = GlobalConst.NotExistId; + + /// + /// 轨迹发生地(国家/地区) + /// + public string? TrackPlaceNation { get; set; } = GlobalConst.EmptyString; + + /// + /// 轨迹发生地(国家/地区)Id + /// + public int? TrackPlaceNationId { get; set; } = GlobalConst.NotExistId; + + /// + /// 轨迹发生地邮编 + /// + public string? TrackPlacePostcode { get; set; } = GlobalConst.EmptyString; + + /// + /// 预计签收时间 + /// + public DateTime? EstimatedDeliveryDate { get; set; } + + /// + /// 签收地时区 + /// + public float? DestinationTimeZone { get; set; } } diff --git a/src/SJZY.Track.Domain/Consts/GlobalConst.MQ.cs b/src/SJZY.Track.Domain/Consts/GlobalConst.MQ.cs index d517bfc8..e7901efa 100644 --- a/src/SJZY.Track.Domain/Consts/GlobalConst.MQ.cs +++ b/src/SJZY.Track.Domain/Consts/GlobalConst.MQ.cs @@ -51,4 +51,11 @@ public partial class GlobalConst /// 预警计算任务 /// public const string TrackWarningCalcSubscribe = "SJZY.Track.TrackWarningCalc"; + + /// + /// 轨迹变更通知交换机 + /// + public const string TrackChangeExchange = "sjzy.track.on_track_change_ex"; + + } diff --git a/src/SJZY.Track.Domain/Consts/GlobalConst.cs b/src/SJZY.Track.Domain/Consts/GlobalConst.cs index 183d921e..d8064f12 100644 --- a/src/SJZY.Track.Domain/Consts/GlobalConst.cs +++ b/src/SJZY.Track.Domain/Consts/GlobalConst.cs @@ -7,6 +7,7 @@ public partial class GlobalConst public const int ChunkSize = 100; public const int SafelyLimit = 10000; public const int NormalLimit = 10000; + public const int NotExistId = -9999; public const float NoneTrackTimeZone = 99999; diff --git a/src/SJZY.Track.Domain/Dtos/Order/GetPageTrackOrderInputVO.cs b/src/SJZY.Track.Domain/Dtos/Order/GetPageTrackOrderInputVO.cs index 7d473d1f..b57037a0 100644 --- a/src/SJZY.Track.Domain/Dtos/Order/GetPageTrackOrderInputVO.cs +++ b/src/SJZY.Track.Domain/Dtos/Order/GetPageTrackOrderInputVO.cs @@ -68,6 +68,7 @@ public class TrackOrderVO /// public string? OrderNumber { get; set; } + /// /// 客户简称 /// @@ -137,4 +138,19 @@ public class TrackOrderVO /// 问题类型名称 /// public string? QuestionTypeName { get; set; } + + /// + /// 预计到达时间 + /// + public DateTime? EstimatedDeliveryDate { get; set; } + + /// + /// 目的地时区 + /// + public float? DestinationTimeZone { get; set; } + + /// + /// 第三方单号,服务商-轨迹0000000、轨迹服务商-0000000 + /// + public string? ThirdPartyOrderNumber { get; set; } } diff --git a/src/SJZY.Track.Domain/Dtos/TrackContents/GetPageTrackContentVO.cs b/src/SJZY.Track.Domain/Dtos/TrackContents/GetPageTrackContentVO.cs index 5dc044c7..2c9ef883 100644 --- a/src/SJZY.Track.Domain/Dtos/TrackContents/GetPageTrackContentVO.cs +++ b/src/SJZY.Track.Domain/Dtos/TrackContents/GetPageTrackContentVO.cs @@ -89,4 +89,19 @@ public class GetTrackContentListVO /// 修改人 /// public string UpdateUser { get; set; } + + /// + /// 是否可重复 + /// + public bool IsRepeatable { get; set; } + + /// + /// 是否允许手动修改 + /// + public bool IsAllowManuallyUpdate { get; set; } + + /// + /// 是否允许手动创建 + /// + public bool IsAllowManuallyCreate { get; set; } } diff --git a/src/SJZY.Track.Domain/Dtos/TrackContents/GetTrackContentDetailVO.cs b/src/SJZY.Track.Domain/Dtos/TrackContents/GetTrackContentDetailVO.cs index 2d1d8426..fe5b29d2 100644 --- a/src/SJZY.Track.Domain/Dtos/TrackContents/GetTrackContentDetailVO.cs +++ b/src/SJZY.Track.Domain/Dtos/TrackContents/GetTrackContentDetailVO.cs @@ -56,4 +56,18 @@ public class GetTrackContentDetailVO /// 是否对外 /// public bool IsExternal { get; set; } + /// + /// 是否可重复 + /// + public bool IsRepeatable { get; set; } + + /// + /// 是否允许手动修改 + /// + public bool IsAllowManuallyUpdate { get; set; } + + /// + /// 是否允许手动创建 + /// + public bool IsAllowManuallyCreate { get; set; } } diff --git a/src/SJZY.Track.Domain/Dtos/TrackContents/TrackContentVO.cs b/src/SJZY.Track.Domain/Dtos/TrackContents/TrackContentVO.cs index 374fa387..8b7cdf3f 100644 --- a/src/SJZY.Track.Domain/Dtos/TrackContents/TrackContentVO.cs +++ b/src/SJZY.Track.Domain/Dtos/TrackContents/TrackContentVO.cs @@ -31,4 +31,34 @@ public class TrackContentVO /// 是否完成态 /// public bool IsCompleted { get; set; } + + /// + /// 轨迹状态代码 + /// + public string TrackStatusCode { get; set; } + + /// + /// 是否可重复 + /// + public bool IsRepeatable { get; set; } + + /// + /// 是否允许手动修改 + /// + public bool IsAllowManuallyUpdate { get; set; } + + /// + /// 是否允许手动创建 + /// + public bool IsAllowManuallyCreate { get; set; } + + /// + /// 是否广播 + /// + public bool IsToMq { get; set; } + + /// + /// 轨迹内容code + /// + public string TrackContentCode { get; set; } } diff --git a/src/SJZY.Track.Domain/Enums/TrackSourceEnum.cs b/src/SJZY.Track.Domain/Enums/TrackSourceEnum.cs new file mode 100644 index 00000000..021616af --- /dev/null +++ b/src/SJZY.Track.Domain/Enums/TrackSourceEnum.cs @@ -0,0 +1,24 @@ +using System.ComponentModel; + +namespace SJZY.Track.Domain.Enums; + +/// +/// 轨迹来源 +/// +public enum TrackSourceEnum +{ + /// + /// 外部服务商创建 + /// + [Description("外部服务商创建")] ExternalServiceProvide = 1, + + /// + /// 内部系统创建 + /// + [Description("内部系统创建")] InternalService = 2, + + /// + /// 人工创建 + /// + [Description("人工创建")] Artificial = 3, +} diff --git a/src/SJZY.Track.Repository/Queries/ProvideQueries.cs b/src/SJZY.Track.Repository/Queries/ProvideQueries.cs index 91550425..31855027 100644 --- a/src/SJZY.Track.Repository/Queries/ProvideQueries.cs +++ b/src/SJZY.Track.Repository/Queries/ProvideQueries.cs @@ -45,7 +45,8 @@ public class ProvideQueries : ISugarRepository, IProvideQueries, ITransientDepen && b.StartTime <= DateTime.Now && b.EndTime >= DateTime.Now) .Select((a, b, c) => new OrderWithServiceProviderVO { - OrderNo = a.OrderNo, + //优先使用第三方的订单号,为空时才使用主单号 + OrderNo = b.OrderNo ?? a.OrderNo, OrderType = a.OrderType, SubSystemId = a.SubSystemId, ServiceProviderCode = b.ServiceProviderCode, @@ -65,7 +66,8 @@ public class ProvideQueries : ISugarRepository, IProvideQueries, ITransientDepen b.EndTime >= DateTime.Now) .Select((a, b) => new OrderWithServiceProviderVO { - OrderNo = a.OrderNo, + //优先使用第三方的订单号,为空时才使用主单号 + OrderNo = b.OrderNo ?? a.OrderNo, OrderType = a.OrderType, SubSystemId = a.SubSystemId, ServiceProviderCode = b.ServiceProviderCode @@ -87,9 +89,14 @@ public class ProvideQueries : ISugarRepository, IProvideQueries, ITransientDepen SubSystemCode = z.Key, TrackStatusId = x.TrackStatusId, TrackStatus = y.Status, + TrackStatusCode = y.Code, TrackContent = x.Content, TrackEnContent = x.EnContent, IsCompleted = y.IsCompleted, + IsRepeatable = x.IsRepeatable, + IsAllowManuallyUpdate = x.IsAllowManuallyUpdate, + IsAllowManuallyCreate = x.IsAllowManuallyCreate, + IsToMq = y.IsToMq, }) .FirstAsync(); } diff --git a/src/SJZY.Track.Repository/Repositories/OrderRepository.cs b/src/SJZY.Track.Repository/Repositories/OrderRepository.cs index eb62e350..727f17d6 100644 --- a/src/SJZY.Track.Repository/Repositories/OrderRepository.cs +++ b/src/SJZY.Track.Repository/Repositories/OrderRepository.cs @@ -12,11 +12,14 @@ public class OrderRepository : BasicRepository, IOrderRepository, I { public async Task> GetTrackOrderPagedList(GetPageTrackOrderInputVO input) { + //查询完去重 var pageList = await Context.Queryable() + .Includes(a => a.OrderPlatformNumbers) .LeftJoin((a, b) => a.CustomId == b.Id && b.IsDeleted == false) .LeftJoin((a, b, c) => a.SubSystemId == c.Id && c.IsDeleted == false) + .LeftJoin((a, b, c, d) => a.Id == d.OrderId && d.IsDeleted == false) .WhereIF(input.SubSystemId > 0, (a, b, c) => a.SubSystemId == input.SubSystemId) - .WhereIF(!input.OrderNos.IsNullOrEmpty(), (a, b, c) => input.OrderNos!.Contains(a.OrderNo)) + .WhereIF(!input.OrderNos.IsNullOrEmpty(), (a, b, c, d) => input.OrderNos!.Contains(a.OrderNo) || input.OrderNos!.Contains(d.OrderNumber)) .WhereIF(input.CreateTimeStart.HasValue && input.CreateTimeEnd.HasValue, (a, b, c) => a.CreateTime >= input.CreateTimeStart && a.CreateTime <= input.CreateTimeEnd) .WhereIF(input.CreateTimeStart.HasValue && (!input.CreateTimeEnd.HasValue), @@ -28,6 +31,7 @@ public class OrderRepository : BasicRepository, IOrderRepository, I .WhereIF(input.TabType > 0, (a, b, c) => a.QuestionType == input.TabType) .WhereIF(input.TabType == (int) TrackProblemType.Warn, (a, b, c) => !a.IsCompleted) .OrderByDescending((a, b, c) => a.CreateTime) + .Distinct() .Select((a, b, c) => new TrackOrderVO { Id = a.Id, @@ -44,7 +48,10 @@ public class OrderRepository : BasicRepository, IOrderRepository, I IsFinish = a.IsCompleted, CreateTime = a.CreateTime, SubSystem = c.CnName, - SubSystemId = c.Id + SubSystemId = c.Id, + DestinationTimeZone = a.DestinationTimeZone, + EstimatedDeliveryDate = a.EstimatedDeliveryDate, + ThirdPartyOrderNumber = BuildThirdPartyOrderNumber(a.OrderPlatformNumbers) }) .ToPagedListAsync(input.PageIndex, input.PageSize); @@ -60,6 +67,8 @@ public class OrderRepository : BasicRepository, IOrderRepository, I await Context.Insertable(order.OrderFlights).ExecuteCommandAsync(); order.OrderPullStatus.ForEach(x => x.OrderId = orderId); await Context.Insertable(order.OrderPullStatus).ExecuteCommandAsync(); + order.OrderPlatformNumbers.ForEach(x => x.OrderId = orderId); + await Context.Insertable(order.OrderPlatformNumbers).ExecuteCommandAsync(); tran.Complete(); @@ -90,6 +99,13 @@ public class OrderRepository : BasicRepository, IOrderRepository, I // 更新订单模板 await Context.Deleteable().Where(x => x.OrderId == order.Id).ExecuteCommandAsync(); + order.OrderPullStatus.ForEach(x => x.OrderId = order.Id); + await Context.Insertable(order.OrderPullStatus).ExecuteCommandAsync(); + + // 更新订单三方关联 + await Context.Deleteable().Where(x => x.OrderId == order.Id).ExecuteCommandAsync(); + order.OrderPlatformNumbers.ForEach(x => x.OrderId = order.Id); + await Context.Insertable(order.OrderPlatformNumbers).ExecuteCommandAsync(); // 更新订单航班 await Context.Deleteable().Where(x => x.OrderId == order.Id).ExecuteCommandAsync(); @@ -163,6 +179,16 @@ public class OrderRepository : BasicRepository, IOrderRepository, I return res.Select(x => (x.Key, x.Id)).ToList(); } + public Task GetSystemInfoById(int id) + { + return Context.Queryable().FirstAsync(m => m.IsDeleted == false && m.Id == id); + } + + public Task GetUserById(int id) + { + return Context.Queryable().FirstAsync(m => m.IsDeleted == false && m.Id == id); + } + private int UpdateOrderQuestionType(int questionType, IEnumerable ids) { var idsString = string.Join(",", ids); @@ -174,6 +200,20 @@ public class OrderRepository : BasicRepository, IOrderRepository, I }); } + /// + /// 拼接多个三方单号,默认使用分隔符默认(、) + /// 第三方单号,服务商-轨迹0000000、轨迹服务商-0000000 + /// + /// + /// + private static string BuildThirdPartyOrderNumber(List orderPlatformNumberEntities) + { + const string separator = "、"; + var thirdPartyOrderNumbers = orderPlatformNumberEntities + .Select(x => $"{x.Platform}-{x.OrderNumber}").ToList(); + return string.Join(separator, thirdPartyOrderNumbers); + } + #region 订单关联 public async Task> GetLinkedOrdersAsync(int[] orderIds) diff --git a/src/SJZY.Track.Repository/Repositories/TrackContentRepository.cs b/src/SJZY.Track.Repository/Repositories/TrackContentRepository.cs index c1399c6a..d619613b 100644 --- a/src/SJZY.Track.Repository/Repositories/TrackContentRepository.cs +++ b/src/SJZY.Track.Repository/Repositories/TrackContentRepository.cs @@ -33,6 +33,9 @@ public class TrackContentRepository : BasicRepository, ITrac EnContent = a.EnContent, TrackFlagId = a.TrackFlagId, IsExternal = a.IsExternal, + IsRepeatable = a.IsRepeatable, + IsAllowManuallyUpdate = a.IsAllowManuallyUpdate, + IsAllowManuallyCreate = a.IsAllowManuallyCreate }) .Mapper(x => { x.TrackFlagIds = x.TrackFlagId?.Split(',').Select(int.Parse).ToList(); }) .FirstAsync(); @@ -80,6 +83,9 @@ public class TrackContentRepository : BasicRepository, ITrac CreateUser = e.Name, UpdateTime = a.UpdateTime, UpdateUser = f.Name, + IsRepeatable = a.IsRepeatable, + IsAllowManuallyUpdate = a.IsAllowManuallyUpdate, + IsAllowManuallyCreate = a.IsAllowManuallyCreate }) .ToPagedListAsync(input.PageIndex, input.PageSize); @@ -113,7 +119,6 @@ public class TrackContentRepository : BasicRepository, ITrac return false; } - public async Task> GetMapPageAsync( GetTrackContentMapPageInputVO input) { @@ -220,4 +225,50 @@ public class TrackContentRepository : BasicRepository, ITrac return await Context.Updateable().SetColumns(columns).Where(whereExpression) .ExecuteCommandAsync() > 0; } + + public Task GetByCodeAsync(int systemId, string code) + { + return Context.Queryable().FirstAsync(m => m.SubSystemId == systemId && m.Code == code); + } + + public Task> GetListBySystemIdAsync(int systemId) + { + return Context.Queryable() + .Where(content => content.SubSystemId == systemId && !content.IsDeleted) + .Select((content) => new TrackContentEntity + { + Id = content.Id, + Code = content.Code, + IsExternal = content.IsExternal, + EnContent = content.EnContent, + Content = content.Content, + IsRepeatable = content.IsRepeatable, + IsAllowManuallyCreate = content.IsAllowManuallyCreate, + IsAllowManuallyUpdate = content.IsAllowManuallyUpdate, + }) + .ToListAsync(); + } + + public Task> GetTrackContentBySyetemIdAsync(int subSystemId) + { + return Context.Queryable() + .LeftJoin((x, y) => x.TrackStatusId == y.Id) + .LeftJoin((x, y, z) => x.SubSystemId == z.Id) + .Where(x => x.SubSystemId == subSystemId) + .Select((x, y, z) => new TrackContentVO + { + SubSystemCode = z.Key, + TrackStatusId = x.TrackStatusId, + TrackStatus = y.Status, + TrackStatusCode = y.Code, + TrackContent = x.Content, + TrackContentCode = x.Code, + TrackEnContent = x.EnContent, + IsCompleted = y.IsCompleted, + IsRepeatable = x.IsRepeatable, + IsAllowManuallyUpdate = x.IsAllowManuallyUpdate, + IsAllowManuallyCreate = x.IsAllowManuallyCreate, + IsToMq = y.IsToMq, + }).ToListAsync(); + } } diff --git a/src/SJZY.Track.Repository/Repositories/TrackDetailsEsRepository.cs b/src/SJZY.Track.Repository/Repositories/TrackDetailsEsRepository.cs index 44715ae6..0fee4fc6 100644 --- a/src/SJZY.Track.Repository/Repositories/TrackDetailsEsRepository.cs +++ b/src/SJZY.Track.Repository/Repositories/TrackDetailsEsRepository.cs @@ -3,6 +3,7 @@ using SJZY.Expand.ABP.Core.PublicMethords; using SJZY.Expand.ElasticSearch.Services; using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; using SJZY.Track.Domain.Consts; +using SJZY.Track.Domain.Enums; namespace SJZY.Track.Repository.Repositories; @@ -145,6 +146,20 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract p .Add("trackPlace", track.TrackPlace) @@ -181,6 +211,21 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract> GetListAsync(int[] orderIds, List trackCodes, TrackSourceEnum source) + { + var r = await Context.SearchAsync(x => x + .Index(GlobalConst.TrackDetailsIndex) + .Query(q => + q.Terms(t => t.Field(f => f.OrderId).Terms(orderIds)) + && q.Terms(t => t.Field(f => f.TrackCode.Suffix("keyword")).Terms(trackCodes)) + && q.Term(t => t.Source, (int) source) + ) + .Size(SafelyLimit) + .Sort(s => s.Field(f => f.ZeroTrackTime, SortOrder.Descending))); + + if (!r.IsValid) + { + throw new InvalidOperationException($"轨迹查询失败,orderIds:[{string.Join(',', orderIds)}],原因:{r.DebugInformation}。"); + } + + return r.ToIdList(); + } + public async Task> DeleteTrackAsync(int[] orderIds, List trackCodes) { // var deleteResponse = await Context.DeleteByQueryAsync(d => d @@ -334,6 +399,7 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract new { doc.OrderId, @@ -351,6 +417,7 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract x.OrderId).ToList(); } + public async Task DeleteTrackByIdAsync(string id, string indexName) { var r = await Context.DeleteAsync(id, u => u @@ -362,4 +429,15 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract> GetDataByIds(string[] ids) + { + var res = await Context.SearchAsync(m => m + .Index(GlobalConst.TrackDetailsIndex) + .Query( + q => q.Ids(i => i.Values(ids)) + )); + + return res.ToIdList(); + } }