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/BatchDeleteTrackInput.cs b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/BatchDeleteTrackInput.cs index 07db4f25..45ccc51c 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/BatchDeleteTrackInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/BatchDeleteTrackInput.cs @@ -5,9 +5,9 @@ namespace SJZY.Track.Application.Contract.Dtos.ProvideServices; public class BatchDeleteTrackInput : OrderNosInfo { /// - /// 轨迹code,必填 + /// 轨迹标识符集合,必填 /// [Required] [MaxLength(500)] - public List TrackCodes { get; set; } = new(); + public List TrackIdentifiers { 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..b5a73ed3 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/BatchUpsertTrackInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/BatchUpsertTrackInput.cs @@ -29,10 +29,94 @@ public class BatchUpsertTrackInput : OrderNosInfo [Required] public DateTime TrackTime { get; set; } + /// + /// 更改后的轨迹发生时间,无需更改保持为空 + /// + public DateTime? NewTrackTime { get; set; } + /// /// 轨迹发生时区 /// - [Required] - [Range(-12, 12)] + // [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/DeleteOrderInput.cs b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/DeleteOrderInput.cs index c70c9b42..de305c57 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/DeleteOrderInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/DeleteOrderInput.cs @@ -9,8 +9,8 @@ public class DeleteOrderInput : OrderNoInfo public class DeleteTrackInput : OrderNoInfo { /// - /// 轨迹code + /// 轨迹标识符集合,必填 /// [Required] - public List TrackCodes { get; set; } = new(); + public List TrackIdentifiers { get; set; } = new(); } 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/GetTrackOutput.cs b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/GetTrackOutput.cs index 72df17e3..84c1ed15 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/GetTrackOutput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/GetTrackOutput.cs @@ -7,6 +7,9 @@ public class GetTrackOutput /// public string OrderNo { get; set; } = null!; + /// + /// 轨迹明细列表 + /// public List TrackDetails { get; set; } = new(); } @@ -32,4 +35,70 @@ public class TrackDetail /// 是否对外 /// public bool IsDisplay { 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; } + + /// + /// 目的地时区 + /// + public float? DestinationTimeZone { get; set; } } diff --git a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/TrackIdentifierDto.cs b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/TrackIdentifierDto.cs new file mode 100644 index 00000000..993de61c --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/TrackIdentifierDto.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; + +namespace SJZY.Track.Application.Contract.Dtos.ProvideServices; + +/// +/// 轨迹标识符,用于唯一标识一个轨迹 +/// +public sealed class TrackIdentifierDto +{ + /// + /// 轨迹code + /// + [Required] + public string TrackCode { get; set; } + + /// + /// 轨迹发生时间 + /// + [Required] + public DateTime TrackTime { 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/UpsertOrderInput.cs b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/UpsertOrderInput.cs index 4bd7b0b7..1214f51d 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/UpsertOrderInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/UpsertOrderInput.cs @@ -7,9 +7,17 @@ public class UpsertOrderInput : UpdateOrderInput /// /// 客户id /// - [Required] - [Range(1, int.MaxValue)] public int CustomId { get; set; } + + /// + /// 模板code + /// + public List TemplateCodes { get; set; } + + /// + /// 当前操作人 + /// + public int OptionUserId { get; set; } } public class OrderFlightInfo diff --git a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/UpsertTrackInput.cs b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/UpsertTrackInput.cs index 945a973b..2b34f832 100644 --- a/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/UpsertTrackInput.cs +++ b/src/SJZY.Track.Application.Contract/Dtos/ProvideServices/UpsertTrackInput.cs @@ -29,10 +29,98 @@ public class UpsertTrackInput : OrderNoInfo [Required] public DateTime? TrackTime { get; set; } + /// + /// 更改后的轨迹发生时间,无需更改保持为空 + /// + public DateTime? NewTrackTime { get; set; } + /// /// 轨迹发生时区 /// - [Required] - [Range(-12, 12)] + // [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; } + + /// + /// 当前操作人 + /// + public int OptionUserId { 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..47425b3c --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/TrackContents/GetAvailableContentInput.cs @@ -0,0 +1,26 @@ +namespace SJZY.Track.Application.Contract.Dtos.TrackContents; + +public class GetAvailableContentInput +{ + /// + /// 子系统id + /// + public int SystemId { get; set; } + + /// + /// 订单号 + /// + public string? OrderNo { get; set; } + + /// + /// 操作类型 1=创建 2=修改 3=删除 + /// + public TrackOptionType TrackOptionType { get; set; } +} + +public enum TrackOptionType +{ + 创建 = 1, + 修改 = 2, + 删除 = 3 +} 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..7af80b7c --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/TrackContents/GetAvailableContentOutput.cs @@ -0,0 +1,104 @@ +using SJZY.Track.Application.Contract.Dtos.ProvideServices; + +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; } + + /// + /// 轨迹来源 + /// + public int? Source { get; set; } + + /// + /// 轨迹发生地 + /// + public string? TrackPlace { get; set; } + + /// + /// 轨迹发生时间 + /// + public DateTime? TrackTime { get; set; } + + /// + /// 轨迹发生时区 + /// + public float? TrackTimeZone { get; set; } + + /// + /// 时区的值 + /// + public float? TrackTimeZoneValue { get; set; } + + /// + /// 备注 + /// + public string? Remarks { get; set; } + + /// + /// 轨迹发生地(城市) + /// + public string? TrackPlaceCity { get; set; } + public int? TrackPlaceCityId { get; set; } + + /// + /// 轨迹发生地(省/州) + /// + public string? TrackPlaceProvince { get; set; } + public int? TrackPlaceProvinceId { get; set; } + + /// + /// 轨迹发生地(国家/地区) + /// + public string? TrackPlaceNation { get; set; } + public int? TrackPlaceNationId { get; set; } + + /// + /// 轨迹发生地邮编 + /// + public string? TrackPlacePostcode { get; set; } + + /// + /// 预计到达时间 + /// + public DateTime? EstimatedDeliveryDate { get; set; } + + /// + /// 目的地时区 + /// + public float? DestinationTimeZone { get; set; } + + /// + /// 附件文件集合 + /// + public List? FileItems { get; set; } + + /// + /// 客户是否可见 + /// + public bool IsVisibility { get; set; } +} diff --git a/src/SJZY.Track.Application.Contract/Dtos/TrackContents/GetContentDetailInput.cs b/src/SJZY.Track.Application.Contract/Dtos/TrackContents/GetContentDetailInput.cs new file mode 100644 index 00000000..d61d19bb --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/TrackContents/GetContentDetailInput.cs @@ -0,0 +1,29 @@ +namespace SJZY.Track.Application.Contract.Dtos.TrackContents; + +public class GetContentDetailInput +{ + /// + /// 子系统编号 + /// + public int SystemId { get; set; } + + /// + /// 订单号 + /// + public string OrderNumber { get; set; } + + /// + /// 订单类型 + /// + public int OrderType { get; set; } + + /// + /// 轨迹code + /// + public string TrackCode { get; set; } + + /// + /// 轨迹发生时间 + /// + public DateTime TrackTime { get; set; } +} diff --git a/src/SJZY.Track.Application.Contract/Dtos/TrackContents/GetContentDetailOutput.cs b/src/SJZY.Track.Application.Contract/Dtos/TrackContents/GetContentDetailOutput.cs new file mode 100644 index 00000000..ea58971c --- /dev/null +++ b/src/SJZY.Track.Application.Contract/Dtos/TrackContents/GetContentDetailOutput.cs @@ -0,0 +1,192 @@ +namespace SJZY.Track.Application.Contract.Dtos.TrackContents; + +public class GetContentDetailOutput +{ + /// + /// 子系统 + /// + public int SubSystemId { get; set; } + + /// + /// 订单Id + /// + public int OrderId { get; set; } + + /// + /// 订单号 + /// + public string OrderNo { get; set; } = null!; + + /// + /// 索引名称 + /// + public string IndexName { get; set; } = null!; + + /// + /// 订单类型(主数据枚举TrackOrderType) + /// + public int OrderType { get; set; } + + /// + /// 轨迹状态 + /// + public string TrackStatus { get; set; } + + /// + /// 轨迹状态code + /// + public string TrackStatusCode { get; set; } + + /// + /// 轨迹内容代码 + /// + public string TrackCode { get; set; } + + /// + /// 轨迹内容 + /// + public string TrackContent { get; set; } + + /// + /// 轨迹内容英文 + /// + public string TrackEnContent { get; set; } + + /// + /// 服务商code + /// + public string ServiceProviderCode { get; set; } + + /// + /// 原轨迹内容 + /// + public string? OriginTrackContent { get; set; } + + /// + /// 原轨迹报文hash(刷新轨迹重复性校验使用) + /// + public string OriginTrackDataHash { get; set; } = null!; + + /// + /// 轨迹发生地 + /// + public string TrackPlace { get; set; } + + /// + /// 轨迹发生时间 + /// + public DateTime TrackTime { get; set; } + + /// + /// 轨迹发生时区 + /// + public float TrackTimeZone { get; set; } + + /// + /// 转化后的轨迹发生时间 + /// + public DateTime ZeroTrackTime { get; set; } + + /// + /// 创建人 + /// + public string CreateUser { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 修改人 + /// + public string UpdateUser { get; set; } + + /// + /// 修改时间 + /// + public DateTime? UpdateTime { 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; } + + /// + /// 签收地时区 + /// + public float? DestinationTimeZone { get; set; } +} + +public class FileItem +{ + /// + /// 文件osskey + /// + public string OssKey { get; set; } + + /// + /// 文件名称 + /// + public string FileName { get; set; } + + /// + /// 路径 + /// + public string Url { 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..0859de6b 100644 --- a/src/SJZY.Track.Application.Contract/Events/TrackDeleteEvent.cs +++ b/src/SJZY.Track.Application.Contract/Events/TrackDeleteEvent.cs @@ -1,3 +1,5 @@ +using SJZY.Track.Application.Contract.Dtos.ProvideServices; + namespace SJZY.Track.Application.Contract.Events; public class TrackDeleteEvent @@ -7,21 +9,31 @@ public class TrackDeleteEvent /// public int SubSystemId { get; set; } + /// + /// es中的id + /// + public string[] EsIds { get; set; } + /// /// 订单ids /// public int[] OrderIds { get; set; } = null!; /// - /// 轨迹代码 + /// 轨迹标识符 /// - public List TrackCodes { get; set; } = null!; + public List TrackIdentifiers { get; set; } = null!; /// /// 操作人 /// public string? OperationMan { get; set; } + /// + /// 操作人姓名 + /// + public int OperationManId { get; set; } + /// /// 已经处理过的订单 /// @@ -30,6 +42,6 @@ public class TrackDeleteEvent public override string ToString() { return $"[TrackDelete by {OperationMan}] SubSystemId: {SubSystemId}, OrderIds: {string.Join(",", - OrderIds)}, TrackCodes: {string.Join(",", TrackCodes)}"; + OrderIds)}, TrackCodes: {string.Join(",", TrackIdentifiers.Select(x=>x.TrackCode))}"; } } diff --git a/src/SJZY.Track.Application.Contract/Events/TrackUpsertEvent.cs b/src/SJZY.Track.Application.Contract/Events/TrackUpsertEvent.cs index 1bd4b033..4102df32 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更新 /// @@ -42,6 +49,11 @@ public class TrackUpsertEvent /// public DateTime TrackTime { get; set; } + /// + /// 更改后的轨迹发生时间,无需更改保持为空 + /// + public DateTime? NewTrackTime { get; set; } + /// /// 轨迹发生时区 /// @@ -54,6 +66,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/AutoMapper/TrackContentAutoMapperProfile.cs b/src/SJZY.Track.Application/AutoMapper/TrackContentAutoMapperProfile.cs index 862447be..a161314e 100644 --- a/src/SJZY.Track.Application/AutoMapper/TrackContentAutoMapperProfile.cs +++ b/src/SJZY.Track.Application/AutoMapper/TrackContentAutoMapperProfile.cs @@ -1,6 +1,7 @@ using AutoMapper; using SJZY.Track.Application.Contract.Dtos.TrackContents; using SJZY.Track.Domain.Aggregates.TrackContentAggregate; +using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; using SJZY.Track.Domain.Dtos.TrackContents; namespace SJZY.Track.Application.AutoMapper; @@ -16,5 +17,9 @@ public class TrackContentAutoMapperProfile : Profile CreateMap, UnifyPageResultDto>( MemberList.None); CreateMap(MemberList.None); + + CreateMap(MemberList.None).ReverseMap(); + CreateMap< SJZY.Track.Domain.Aggregates.TrackDetailsAggregate.FileItem, SJZY.Track.Application.Contract.Dtos.TrackContents.FileItem>(MemberList.None).ReverseMap(); + } } 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..08e079d6 100644 --- a/src/SJZY.Track.Application/CAP/EventHandlers/TrackDeleteEventHandler.cs +++ b/src/SJZY.Track.Application/CAP/EventHandlers/TrackDeleteEventHandler.cs @@ -1,9 +1,15 @@ +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.Dtos.ProvideServices; 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 +19,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 +49,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); @@ -47,22 +68,29 @@ public class TrackDeleteEventHandler : ITransientDependency, ICapSubscribe { var nextEvent = @event.DeepCopy(); nextEvent.Visited.AddRange(@event.OrderIds); - nextEvent.TrackCodes = @event.TrackCodes; + nextEvent.TrackIdentifiers = @event.TrackIdentifiers; nextEvent.SubSystemId = group.Key; // 不同子系统需要做内容映射 if (@event.SubSystemId != nextEvent.SubSystemId) { - var mapCodes = await _provideQueries - .MapTrackCodesAsync(@event.SubSystemId, nextEvent.SubSystemId, @event.TrackCodes.ToArray()); + var codeMaps = await _provideQueries + .GetTrackCodeMapsAsync(@event.SubSystemId, nextEvent.SubSystemId, + @event.TrackIdentifiers.Select(x => x.TrackCode).ToArray()); - if (mapCodes.Count == 0) + if (codeMaps.Count == 0) { LogHelper.Warn($"订单跨子系统,未配置内容映射,请联系管理员:{@event}", true); continue; } - nextEvent.TrackCodes = mapCodes; + //映射轨迹code + foreach (var trackIdentity in nextEvent.TrackIdentifiers) + { + var match = codeMaps.FirstOrDefault(x => x.MapTrackContentCode == trackIdentity.TrackCode); + if (match == null) continue; + trackIdentity.TrackCode = match.TrackContentCode; + } } var identifiers = group.Select(x => x.OrderId).ToList(); @@ -84,12 +112,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 +126,102 @@ 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(); + @event.TrackIdentifiers = tracks.Select(x => new TrackIdentifierDto + { + TrackCode = x.TrackCode, + TrackTime = x.TrackTime + }).ToList(); + + // 外部api获取的轨迹不允许删除 + if (tracks.Any(m => m.Source == TrackSourceEnum.ExternalServiceProvide.GetHashCode())) + return tracks; + + // 删除轨迹 + foreach (var item in tracks) + { + await _trackDetailsEsRepository.DeleteTrackByIdAsync(item.Id, item.IndexName); + } + + //清理已经删除过的esid + @event.EsIds = Array.Empty(); + } + else //原有逻辑,根据轨迹code和订单进行删除 + { + //根据订单查询出所有的轨迹,查所有是因为主单上需要进行回退更新 + tracks = await _trackDetailsEsRepository.GetListAsync(@event.OrderIds); + + //构建标识符集合 + var ids = @event.TrackIdentifiers.Select(x => $"{x.TrackCode}{x.TrackTime}").ToList(); + //根据标识符选出要删除的轨迹明细 + var deleteTracks = tracks.Where(x => ids.Contains($"{x.TrackCode}{x.TrackTime}")).ToList(); + + if (deleteTracks.Count == 0) return deleteTracks; + + foreach (var track in deleteTracks) + { + await _trackDetailsEsRepository.DeleteTrackByIdAsync(track.Id, track.IndexName); + } + + //从被删除轨迹寻找订单id,稍后会更新订单主表 + orderIds = deleteTracks.Select(m => m.OrderId).Distinct().ToList(); + //排除掉已经删除的轨迹 + tracks = tracks.Except(deleteTracks).ToList(); + } - if (orderIds.Count == 0) return; var orders = await _orderRepository.GetListAsync(x => orderIds.Contains(x.Id), x => new OrderEntity @@ -121,12 +233,13 @@ 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) { - var track = tracks.FirstOrDefault(x => x.OrderId == order.Id); + //找到最新的轨迹数据 + var track = tracks?.OrderByDescending(x=>x.TrackTime) + .FirstOrDefault(x => x.OrderId == order.Id); // 删除全部轨迹导致的重置行为 if (track == default) @@ -146,6 +259,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 +283,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..c26118a3 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,21 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe { // 找不到轨迹内容,直接结束 LogHelper.Warn($"找不到轨迹内容:{@event}", true); - return; + return default; } + //#7001:未来潜在需求,当轨迹内容匹配预设,记录一个标记,保证下刷过程都是使用预设内衣 + //虽然在引发事件之前做了判断,引发之后并不一定立即处理,在此过程中轨迹内容仍然可能被修改,所以要二次进行判断。 + //通过来源指示是否为手动操作 + 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 +243,8 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe TrackPlace = @event.TrackPlace, TrackTime = @event.TrackTime, TrackTimeZone = @event.TrackTimeZone, + DestinationTimeZone = @event.DestinationTimeZone, + EstimatedDeliveryDate = @event.EstimatedDeliveryDate }); // 触发轨迹推送事件(异步) @@ -192,6 +266,8 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe } }; await _capPublisher.PublishAsync(GlobalConst.TrackPushSubscribe, trackPushEvent); + + return trackContent; } private async Task> UpsertTrackAsync(TrackUpsertEvent @event, List canNextOrderIds, @@ -200,7 +276,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe var orderIds = @event.OrderIdentifiers.Select(x => x.OrderId).ToArray(); // important:轨迹code相同,时间、发生地不同的,可能存在多条!! - var tracks = await _trackDetailsEsRepository.GetListAsync(orderIds, @event.TrackCode, true); + var tracks = await _trackDetailsEsRepository.GetListAsync(orderIds, @event.TrackCode, false); var insertTracks = new List(); var updateTracks = new List(); @@ -210,7 +286,9 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe TrackDetailsIndex? track; if (!@event.IsAdd) { - var existTracks = tracks.Where(x => x.OrderId == order.OrderId).ToArray(); + //同一订单同轨迹code同一轨迹时间如果有多条轨迹明细则不允许修改 + var existTracks = tracks.Where(x => x.OrderId == order.OrderId + && x.TrackTime == @event.TrackTime).ToArray(); if (existTracks.Length > 1) { // 修改轨迹时,订单存在多条轨迹不允许修改 @@ -223,10 +301,38 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe } 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) @@ -239,35 +345,107 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe OrderType = order.OrderType, TrackCode = @event.TrackCode, TrackPlace = @event.TrackPlace, - TrackTime = @event.TrackTime, + TrackTime = @event.NewTrackTime ?? @event.TrackTime, TrackTimeZone = @event.TrackTimeZone, ServiceProviderCode = @event.TrackRefreshData.ServiceProviderCode ?? GlobalConst.EmptyString, OriginTrackContent = @event.TrackRefreshData.OriginTrackContent ?? GlobalConst.EmptyString, OriginTrackDataHash = @event.TrackRefreshData.OriginTrackDataHash ?? GlobalConst.EmptyString, CreateUser = @event.OperationMan ?? GlobalConst.SystemUser, CreateTime = DateTime.Now, + //来源轨迹内容,第一次新增的时候使用轨迹内容表提供的 TrackStatus = trackContent.TrackStatus, - TrackContent = trackContent.TrackContent, - TrackEnContent = trackContent.TrackEnContent + TrackStatusCode = trackContent.TrackStatusCode, + //跨子系统? + TrackContent = @event.Content ?? trackContent.TrackContent, + TrackEnContent = @event.EnStatus ?? 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 { - track.TrackPlace = @event.TrackPlace; - track.TrackTime = @event.TrackTime; - track.TrackTimeZone = @event.TrackTimeZone; + //外部轨迹,轨迹发生地,轨迹发生时间,轨迹发生时区,不允许更改 + if (track.Source != (int) TrackSourceEnum.ExternalServiceProvide) + { + track.TrackPlace = @event.TrackPlace; + track.TrackTime = @event.NewTrackTime ?? @event.TrackTime; + track.TrackTimeZone = @event.TrackTimeZone; + } + track.ZeroTrackTime = Math.Abs(track.TrackTimeZone - GlobalConst.NoneTrackTimeZone) < 1 ? track.TrackTime : track.TrackTime.Subtract(TimeSpan.FromHours(track.TrackTimeZone)); 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); @@ -275,7 +453,8 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe if (updateTracks.Any()) { - await _trackDetailsEsRepository.UpdateManyAsync(updateTracks); + //批量差异化更新轨迹 + await _trackDetailsEsRepository.BulkUpdateAsync(updateTracks); } return insertTracks; diff --git a/src/SJZY.Track.Application/Rabbit/TrackPushCompletedEventHandler.cs b/src/SJZY.Track.Application/Rabbit/TrackPushCompletedEventHandler.cs index 348d7a4b..e55485a9 100644 --- a/src/SJZY.Track.Application/Rabbit/TrackPushCompletedEventHandler.cs +++ b/src/SJZY.Track.Application/Rabbit/TrackPushCompletedEventHandler.cs @@ -26,9 +26,12 @@ public class TrackPushCompletedEventHandler : ITransientDependency, IRabbitHandl _trackPushRecordEsRepository = trackPushRecordEsRepository; } - public bool Enable(ConsumeOptions options) + public ConsumeOptions Options() { - return options.Queue == GlobalConst.TrackPushCompletedSubscribe; + return new ConsumeOptions() + { + Queue = GlobalConst.TrackPushCompletedSubscribe + }; } public async Task Handle(IServiceProvider sp, BasicDeliverEventArgs args, ConsumeOptions options) diff --git a/src/SJZY.Track.Application/Rabbit/TrackRefreshEventHandler.cs b/src/SJZY.Track.Application/Rabbit/TrackRefreshEventHandler.cs index ff8c6d1d..36baaec7 100644 --- a/src/SJZY.Track.Application/Rabbit/TrackRefreshEventHandler.cs +++ b/src/SJZY.Track.Application/Rabbit/TrackRefreshEventHandler.cs @@ -15,6 +15,7 @@ using SJZY.Track.Application.Contract.Services; using SJZY.Track.Application.Helpers; using SJZY.Track.Domain.Aggregates.OrderAggregate; using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; +using SJZY.Track.Domain.Enums; using Volo.Abp.DependencyInjection; namespace SJZY.Track.Application.Rabbit; @@ -39,10 +40,12 @@ public class TrackRefreshEventHandler : ITransientDependency, IRabbitHandler _logger = logger; } - - public bool Enable(ConsumeOptions options) + public ConsumeOptions Options() { - return options.Queue == GlobalConst.TrackRefreshSubscribe; + return new ConsumeOptions() + { + Queue = GlobalConst.TrackRefreshSubscribe, + }; } public async Task Handle(IServiceProvider sp, BasicDeliverEventArgs args, ConsumeOptions options) @@ -108,18 +111,18 @@ public class TrackRefreshEventHandler : ITransientDependency, IRabbitHandler var trackToken = orderToken.SelectToken(paramConfig.TrackDetailsPath); if (trackToken is null) return; - var order = await _orderRepository.GetFirstAsync(x => - x.SubSystemId == attach.SubSystemId && x.OrderType == attach.OrderType && x.OrderNo == orderNo, - x => new - { - x.Id, - x.CustomId - }); - if (order == default) return; + //回传单号可能是主单也可能是外部单号 + //通过平台code和(外部单号/主单号)查询主单 + var order = await _orderRepository.GetTrackOrderByProviderAndOrderNo(attach.ServiceProviderCode, orderNo!); + if (order == default) + { + _logger.LogError($"请求id: {attach.ToJson()},轨迹消费失败,找不到主单,平台code:{attach.ServiceProviderCode},单号:{orderNo}"); + return; + } var context = new TrackRefreshContext { - OrderId = order.Id, + OrderId = order.MainOrderId, OrderType = attach.OrderType, OrderNo = orderNo!, ServiceProviderCode = attach.ServiceProviderCode, @@ -256,6 +259,22 @@ public class TrackRefreshEventHandler : ITransientDependency, IRabbitHandler TrackTime = trackTime ?? DateTime.Now, TrackTimeZone = trackTimeZone ?? GlobalConst.NoneTrackTimeZone, IsAdd = true, + //S06新增 + IsVisibility = false, + Source = (int)TrackSourceEnum.ExternalServiceProvide, + Remarks = null, + Files = null, + Content = null, + EnStatus = null, + TrackPlaceCity = null, + TrackPlaceCityId = null, + TrackPlaceProvince = null, + TrackPlaceProvinceId = null, + TrackPlaceNation = null, + TrackPlaceNationId = null, + TrackPlacePostcode = null, + DestinationTimeZone = null, + EstimatedDeliveryDate = null, }; await _capPublisher.PublishAsync(GlobalConst.TrackUpsertForRefreshSubscribe, trackUpsertEvent); @@ -279,7 +298,26 @@ public class TrackRefreshEventHandler : ITransientDependency, IRabbitHandler OriginTrackDataHash = StringHelper.GetHash(JsonConvert.SerializeObject(trackToken)), OriginTrackContent = trackContent ?? GlobalConst.EmptyString, CreateUser = GlobalConst.SystemUser, - CreateTime = DateTime.Now + CreateTime = DateTime.Now, + //来源轨迹内容,第一次新增的时候使用轨迹内容表提供的 + TrackStatus = GlobalConst.EmptyString, + TrackStatusCode = GlobalConst.EmptyString, + TrackContent = GlobalConst.EmptyString, + TrackEnContent = GlobalConst.EmptyString, + //S06新增轨迹明细字段 + IsVisibility = false, + Source = (int) TrackSourceEnum.ExternalServiceProvide, + Remarks = null, + Files = null, + TrackPlaceCity = null, + TrackPlaceCityId = null, + TrackPlaceProvince = null, + TrackPlaceProvinceId = null, + TrackPlaceNation =null, + TrackPlaceNationId = null, + TrackPlacePostcode = null, + DestinationTimeZone = null, + EstimatedDeliveryDate = null, }; track.ZeroTrackTime = Math.Abs(track.TrackTimeZone - GlobalConst.NoneTrackTimeZone) < 1 diff --git a/src/SJZY.Track.Application/SJZY.Track.Application.csproj b/src/SJZY.Track.Application/SJZY.Track.Application.csproj index 926a7d69..0beb6ea8 100644 --- a/src/SJZY.Track.Application/SJZY.Track.Application.csproj +++ b/src/SJZY.Track.Application/SJZY.Track.Application.csproj @@ -8,8 +8,9 @@ - + + diff --git a/src/SJZY.Track.Application/Services/ProvideServices/OrderService.cs b/src/SJZY.Track.Application/Services/ProvideServices/OrderService.cs index fd7f723e..8bf60542 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,8 @@ 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); + order.TemplateCodes = string.Join(',', input.TemplateCodes); var orderId = await _orderRepository.CreateOrderAsync(order); @@ -72,9 +85,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 +129,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 +144,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 +164,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.Query.cs b/src/SJZY.Track.Application/Services/ProvideServices/TrackService.Query.cs index 30b0e0d2..ab0bd27d 100644 --- a/src/SJZY.Track.Application/Services/ProvideServices/TrackService.Query.cs +++ b/src/SJZY.Track.Application/Services/ProvideServices/TrackService.Query.cs @@ -62,7 +62,24 @@ public partial class TrackService TimeZone = Math.Abs(x.TrackTimeZone - GlobalConst.NoneTrackTimeZone) < 1 ? null : input.TimeZone ?? 8, - IsDisplay = trackContentDic.TryGetValue(x.TrackCode, out var isExternal) && isExternal + IsDisplay = trackContentDic.TryGetValue(x.TrackCode, out var isExternal) && isExternal, + IsVisibility = x.IsVisibility, + Source = x.Source, + Remarks = x.Remarks, + Files = x.Files?.Select(y => new FileItemDto + { + FileName = y.FileName, + OssKey = y.OssKey + }).ToList(), + TrackPlaceCity = x.TrackPlaceCity, + TrackPlaceCityId = x.TrackPlaceCityId, + TrackPlaceProvince = x.TrackPlaceProvince, + TrackPlaceProvinceId = x.TrackPlaceProvinceId, + TrackPlaceNation = x.TrackPlaceNation, + TrackPlaceNationId = x.TrackPlaceNationId, + TrackPlacePostcode = x.TrackPlacePostcode, + DestinationTimeZone = x.DestinationTimeZone, + EstimatedDeliveryDate = x.EstimatedDeliveryDate }).ToList(); res.Add(item); diff --git a/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs b/src/SJZY.Track.Application/Services/ProvideServices/TrackService.cs index 63fdd064..4e0322a3 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; @@ -54,12 +55,60 @@ public partial class TrackService : ApplicationService, ITrackService throw new BusinessException(message: $"订单:{input.OrderNo}不存在"); } + if (!GlobalConst.SupportTrackSources.Contains(input.Source)) + { + throw new BusinessException(message: "轨迹来源是必须的"); + } + if (input.TrackTime < DateTime.Now.AddYears(-100) || input.TrackTime > DateTime.Now.AddYears(100)) { throw new BusinessException(message: "轨迹时间不合法"); } - var userInfo = await _requestCurrentUserService.Service.GetCurrentUserBasicInfoAsync(); + 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)) + .Where(x => x.TrackTime == input.TrackTime) + .ToList(); + + //更新轨迹,同一时间存在多条轨迹记录,理由:不清晰到底要修改哪一条或是全部修改 + if (!input.IsAdd && tracks.Count > 1) + { + throw new BusinessException(message: $"订单:{input.OrderNo},轨迹代码:{input.TrackCode}存在多条记录,不允许修改"); + } + + var userInfo = await _requestCurrentUserService.Service.GetCurrentUserBasicInfoAsync(input.OptionUserId); if (userInfo.Code != GlobalConst.SuccessCode || userInfo.Data is null) { //TODO: log || ex @@ -112,11 +161,25 @@ public partial class TrackService : ApplicationService, ITrackService var name = userInfo.Data.UserName; + //删除轨迹约束,只要其中有一条来源是外部的就立即返回错误 + var tracks = await _trackDetailsEsRepository + .GetListAsync(new[] { orderId }, + input.TrackIdentifiers.Select(x => x.TrackCode).ToList(), + TrackSourceEnum.ExternalServiceProvide); + + //当要删除的轨迹标识出现交叉则不允许删除 + var intersect = tracks.Select(x => $"{x.TrackCode}{x.TrackTime}") + .Intersect(input.TrackIdentifiers.Select(y => $"{y.TrackCode}{y.TrackTime}")).ToList(); + if (intersect.Count > 0) + { + throw new BusinessException(message: "存在来源于外部服务商的轨迹,不允许删除"); + } + // 删除轨迹 var @event = new TrackDeleteEvent { OrderIds = new[] { orderId }, - TrackCodes = input.TrackCodes, + TrackIdentifiers = input.TrackIdentifiers, SubSystemId = input.SubSystemId, OperationMan = name }; @@ -125,6 +188,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 +258,27 @@ public partial class TrackService : ApplicationService, ITrackService throw new BusinessException(message: $"调用用户中心,获取用户信息失败:{userInfo.Msg}"); } + //删除轨迹约束,只要其中有一条来源是外部的就立即返回错误 + var tracks = await _trackDetailsEsRepository + .GetListAsync(orderIds.ToArray(), + input.TrackIdentifiers.Select(x => x.TrackCode).ToList(), + TrackSourceEnum.ExternalServiceProvide); + + //当要删除的轨迹标识出现交叉则不允许删除 + var intersect = tracks.Select(x => $"{x.TrackCode}{x.TrackTime}") + .Intersect(input.TrackIdentifiers.Select(y => $"{y.TrackCode}{y.TrackTime}")).ToList(); + if (intersect.Count > 0) + { + throw new BusinessException(message: "存在来源于外部服务商的轨迹,不允许删除"); + } + // 删除轨迹 var @event = new TrackDeleteEvent { OrderIds = orderIds.ToArray(), - TrackCodes = input.TrackCodes, + TrackIdentifiers = input.TrackIdentifiers, SubSystemId = input.SubSystemId, - OperationMan = userInfo.Data.UserName + OperationMan = userInfo.Data.UserName, }; await _capPublisher.PublishAsync(GlobalConst.TrackDeleteSubscribe, @event); @@ -191,6 +310,64 @@ public partial class TrackService : ApplicationService, ITrackService throw new BusinessException(message: "订单一个都不存在"); } + if (!GlobalConst.SupportTrackSources.Contains(input.Source)) + { + 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)) + .Where(x => x.TrackTime == input.TrackTime) + .ToList(); + + //更新轨迹,同一时间存在多条轨迹记录,理由:不清晰到底要修改哪一条或是全部修改 + 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) @@ -215,8 +392,25 @@ public partial class TrackService : ApplicationService, ITrackService TrackCode = input.TrackCode, TrackPlace = input.TrackPlace, TrackTime = input.TrackTime, + NewTrackTime = input.NewTrackTime, 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/TimerTaskss/TrackPullTask.cs b/src/SJZY.Track.Application/Services/TimerTaskss/TrackPullTask.cs index 460bd655..6bd0edb1 100644 --- a/src/SJZY.Track.Application/Services/TimerTaskss/TrackPullTask.cs +++ b/src/SJZY.Track.Application/Services/TimerTaskss/TrackPullTask.cs @@ -22,6 +22,13 @@ namespace SJZY.Track.Application.Services.TimerTaskss; [ApiExplorerSettings(GroupName = "定时任务")] public class TrackPullTask : ApplicationService, ISingletonDependency { + private readonly IServiceScopeFactory _serviceScopeFactory; + + public TrackPullTask(IServiceScopeFactory serviceScopeFactory) + { + _serviceScopeFactory = serviceScopeFactory; + } + /// /// 定时任务刷新轨迹 /// @@ -30,7 +37,7 @@ public class TrackPullTask : ApplicationService, ISingletonDependency [HttpPost("/TimerTask/Refresh")] public async Task RefreshForTaskAsync() { - using var serviceScope = LazyServiceProvider.CreateScope(); + using var serviceScope = _serviceScopeFactory.CreateScope(); var provideQueries = serviceScope.ServiceProvider.GetRequiredService(); var configs = await provideQueries.GetAllPullConfigsAsync(); diff --git a/src/SJZY.Track.Application/Services/TimerTaskss/TrackPushTask.cs b/src/SJZY.Track.Application/Services/TimerTaskss/TrackPushTask.cs index 928b54e9..47f150de 100644 --- a/src/SJZY.Track.Application/Services/TimerTaskss/TrackPushTask.cs +++ b/src/SJZY.Track.Application/Services/TimerTaskss/TrackPushTask.cs @@ -13,6 +13,13 @@ namespace SJZY.Track.Application.Services.TimerTaskss; [ApiExplorerSettings(GroupName = "定时任务")] public class TrackPushTask : ApplicationService { + private readonly IServiceScopeFactory _serviceScopeFactory; + + public TrackPushTask(IServiceScopeFactory serviceScopeFactory) + { + _serviceScopeFactory = serviceScopeFactory; + } + /// /// 定时任务推送预警(》12小时没有推送的) /// @@ -21,7 +28,7 @@ public class TrackPushTask : ApplicationService [HttpPost("/TimerTask/PushWarn")] public async Task PushWarnForTaskAsync() { - using var serviceScope = LazyServiceProvider.CreateScope(); + using var serviceScope = _serviceScopeFactory.CreateScope(); var configs = serviceScope.ServiceProvider.GetRequiredService>() .Value; diff --git a/src/SJZY.Track.Application/Services/TimerTaskss/TrackWarningTask.cs b/src/SJZY.Track.Application/Services/TimerTaskss/TrackWarningTask.cs index 655f2995..3b9a93d3 100644 --- a/src/SJZY.Track.Application/Services/TimerTaskss/TrackWarningTask.cs +++ b/src/SJZY.Track.Application/Services/TimerTaskss/TrackWarningTask.cs @@ -5,6 +5,7 @@ using SJZY.Expand.ABP.Core.Logger; using SJZY.Track.Application.Contract.Services; using SJZY.Track.Application.Track2InterfaceRelay; using SJZY.Track.Domain.Aggregates.OrderAggregate; +using Volo.Abp.DependencyInjection; namespace SJZY.Track.Application.Services.TimerTaskss; @@ -12,15 +13,18 @@ namespace SJZY.Track.Application.Services.TimerTaskss; /// 轨迹运输时效预警定时任务 /// [ApiExplorerSettings(GroupName = "定时任务")] -public class TrackWarningTask : ApplicationService +public class TrackWarningTask : ApplicationService, ISingletonDependency { private readonly IOptionsSnapshot _options; private readonly IOrderRepository _orderRepository; + private readonly IServiceScopeFactory _serviceScopeFactory; - public TrackWarningTask(IOrderRepository orderRepository, IOptionsSnapshot options) + public TrackWarningTask(IOrderRepository orderRepository, IOptionsSnapshot options, + IServiceScopeFactory serviceScopeFactory) { _orderRepository = orderRepository; _options = options; + _serviceScopeFactory = serviceScopeFactory; } /// @@ -63,7 +67,7 @@ public class TrackWarningTask : ApplicationService { try { - using var serviceScope = LazyServiceProvider.CreateScope(); + using var serviceScope = _serviceScopeFactory.CreateScope(); var forewarningNotifyService = serviceScope.ServiceProvider.GetRequiredService(); diff --git a/src/SJZY.Track.Application/Services/TrackContents/TrackContentService.cs b/src/SJZY.Track.Application/Services/TrackContents/TrackContentService.cs index faf3f428..c5e23f8d 100644 --- a/src/SJZY.Track.Application/Services/TrackContents/TrackContentService.cs +++ b/src/SJZY.Track.Application/Services/TrackContents/TrackContentService.cs @@ -2,12 +2,18 @@ using System.Linq.Expressions; using Microsoft.Extensions.Localization; using SJZY.Expand.ABP.Core.Localization; using SJZY.Expand.ABP.Core.PublicMethords; +using SJZY.Expand.AliyunOSS; +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.Aggregates.TrackTemplateAggregate; using SJZY.Track.Domain.Dtos.TrackContents; +using SJZY.Track.Domain.Dtos.TrackTemplate; using SJZY.Track.Domain.Queries; namespace SJZY.Track.Application.Services.TrackContents; @@ -21,19 +27,28 @@ public class TrackContentService : ApplicationService { private readonly IStringLocalizer _stringLocalizer; private readonly ITrackContentRepository _trackContentRepository; + private readonly ITrackTemplateRepository _trackTemplateRepository; 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, + ITrackTemplateRepository trackTemplateRepository) { _stringLocalizer = stringLocalizer; _trackContentRepository = trackContentRepository; _trackStatusBasicQuery = trackStatusBasicQuery; _trackFlagBasicQuery = trackFlagBasicQuery; + _orderRepository = orderRepository; + _trackDetailsEsRepository = trackDetailsEsRepository; + _trackTemplateRepository = trackTemplateRepository; } /// @@ -96,8 +111,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 +195,205 @@ 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; + } + + /// + /// 根据子系统id、单号、订单类型、轨迹发生时间、轨迹code获取轨迹内容 + /// + /// + /// + [HttpPost("GetContentDetail")] + public async Task> GetContentDetail([FromBody] GetContentDetailInput input) + { + if (input.SystemId <= 0) + return new UnifyResultDto(false, "子系统Id必须大于等于0"); + + if (string.IsNullOrEmpty(input.TrackCode)) + return new UnifyResultDto(false, "轨迹内容code不能为空"); + + if (string.IsNullOrEmpty(input.OrderNumber)) + return new UnifyResultDto(false, "订单code不能为空"); + + if (input.OrderType <= 0) + return new UnifyResultDto(false, "订单类型不能为空"); + + if (input.TrackTime == default) + return new UnifyResultDto(false, "轨迹发生时间不能为空"); + + var content = await _trackDetailsEsRepository.GetDetailAsync(input.OrderNumber, input.TrackCode, input.OrderType, input.SystemId, input.TrackTime); + if (content == default) + return new UnifyResultDto(false, "在当前子系统中未找到指定code的轨迹内容"); + + var res = new UnifyResultDto(true) + { + Data = ObjectMapper.Map(content) + }; + + + if (res.Data != default && res.Data.Files.Any()) + { + foreach (var item in res.Data.Files) + { + item.Url = OSSHelper.GetUrlByKey(item.OssKey); + } + } + + 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 order = await _orderRepository.GetFirstAsync(x => x.OrderNo == input.OrderNo && x.SubSystemId == input.SystemId); + if (order == default) + throw new BusinessException(message: "订单不存在!"); + + //获取轨迹详情 + var trackDetails = await _trackDetailsEsRepository.GetListAsync(order.Id); + + //获取当前子系统下所有的轨迹内容 + var contentList = await _trackContentRepository.GetListBySystemIdAsync(input.SystemId); + if (contentList == null) + throw new BusinessException(message: "当前子系统下未找到任何可用的轨迹内容!"); + + //如果订单给了获取模板code,则进一步根据模板code进行过滤 + if (!string.IsNullOrEmpty(order.TemplateCodes)) + { + var currentCodes = order.TemplateCodes.Split(","); + var template = await _trackTemplateRepository.GetContentByTemplateCodes(currentCodes, input.SystemId); + contentList = contentList.Where(m => template.Contains(m.Code)).ToList(); + } + + 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 + }); + } + + if (input.TrackOptionType == TrackOptionType.创建 + || trackDetails == null + || trackDetails.Count == 0) + { + return res; + } + + + //修改操作,查轨迹内容列表反映明细表数据,重新构造响应数据 + var matchContent = new List(); + var contents = res.Data.Select(x => new + { + x.Id, + x.IsExternal, + x.Code + }); + + foreach (var content in contents) + { + var match = trackDetails + .Where(x => x.TrackCode == content.Code) + .Select(matchItem => new GetAvailableContentOutput + { + IsExternal = content.IsExternal, + Id = content.Id, + EnContent = matchItem.TrackEnContent, + Content = matchItem.TrackContent, + Code = matchItem.TrackCode, + TrackPlace = matchItem.TrackPlace, + TrackTime = matchItem.TrackTime, + TrackTimeZone = matchItem.TrackTimeZone, + Source = matchItem.Source, + + DestinationTimeZone = matchItem.DestinationTimeZone, + EstimatedDeliveryDate = matchItem.EstimatedDeliveryDate, + TrackPlacePostcode = matchItem.TrackPlacePostcode, + TrackPlaceNation = matchItem.TrackPlaceNation, + TrackPlaceNationId = matchItem.TrackPlaceNationId, + TrackPlaceProvince = matchItem.TrackPlaceProvince, + TrackPlaceProvinceId = matchItem.TrackPlaceProvinceId, + TrackPlaceCity = matchItem.TrackPlaceCity, + TrackPlaceCityId = matchItem.TrackPlaceCityId, + Remarks = matchItem.Remarks, + TrackTimeZoneValue = matchItem.DestinationTimeZone, + IsVisibility = matchItem.IsVisibility, + FileItems = matchItem.Files?.Select(x => new FileItemDto + { + FileName = x.FileName, + OssKey = x.OssKey + }).ToList() + }); + matchContent.AddRange(match); + } + + res.Data = matchContent; + 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..75806f9e 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,118 @@ 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 trackDetails = await _trackDetailsEsRepository.GetListAsync(order.Id); + //时间轴不显示轨迹内容为空的轨迹,进行过滤 + trackDetails = trackDetails.Where(x => !x.TrackContent.IsNullOrWhiteSpace() && x.TrackContent != GlobalConst.EmptyString).ToList(); + res.Data = new List(); + + foreach (var item in trackDetails) + { + //如果是外部系统,且轨迹内容不对外则跳过这条轨迹内容 + if (system.SystemProperty == 2 && !item.IsVisibility) + continue; + + //时间和时区任意一个为空则使用创建时间做为排序参照值 + var timeTick = ToUnixTimestamp((item.TrackTime == null + || Math.Abs(item.TrackTimeZone - GlobalConst.NoneTrackTimeZone) < 1) + ? item.CreateTime + : item.TrackTime); + 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 = timeTick, + 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, + }); + } + + // 按轨迹发生时间正序排序 + res.Data = res.Data.OrderBy(m => m.TrackTime).ToList(); + + if (!order.IsCompleted) //如果没完结则显示签收节点 + { + var eddUnixTimestamp = order.EstimatedDeliveryDate.HasValue ? ToUnixTimestamp(DateTime.Parse(order.EstimatedDeliveryDate.ToString()!)) : 0; + + var isShowTime = eddUnixTimestamp > 0 && res.Data != null && res.Data.All(m => m.TrackTime < eddUnixTimestamp); + res.Data.Add(new GetDetailTimelineOutput() + { + Content = "签收", + IsHighlight = false, + TrackTimeZone = isShowTime ? $"GMT{(order.DestinationTimeZone >= 0 ? "+" : "-")}{order.DestinationTimeZone}" : "", + TrackTime = isShowTime ? eddUnixTimestamp : 0, + TrackTimePrefix = isShowTime ? "预计签收时间:" : "", + }); + } + + 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/Services/TrackPush/TrackPushService.cs b/src/SJZY.Track.Application/Services/TrackPush/TrackPushService.cs index 93a5e4c7..66e95cf2 100644 --- a/src/SJZY.Track.Application/Services/TrackPush/TrackPushService.cs +++ b/src/SJZY.Track.Application/Services/TrackPush/TrackPushService.cs @@ -8,6 +8,7 @@ using SJZY.Track.Application.Localization; using SJZY.Track.Domain.Aggregates.TrackPushAggregate; using SJZY.Track.Domain.Dtos.Common; using SJZY.Track.Domain.Dtos.TrackPush; +using Volo.Abp.DependencyInjection; namespace SJZY.Track.Application.Services.TrackPush; @@ -16,7 +17,7 @@ namespace SJZY.Track.Application.Services.TrackPush; /// [ApiExplorerSettings(GroupName = "轨迹推送")] [Route("/Track/PushConfig")] -public class TrackPushService : ApplicationService +public class TrackPushService : ApplicationService, ISingletonDependency { private readonly IStringLocalizer _stringLocalizer; private readonly ITrackPushCacheService _trackPushCacheService; diff --git a/src/SJZY.Track.Application/SjzyTrackApplicationModule.cs b/src/SJZY.Track.Application/SjzyTrackApplicationModule.cs index 9a8cc79b..9318e6ca 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 => @@ -127,6 +140,10 @@ public class SjzyTrackApplicationModule : AbpModule options.ConnectionString = $"amqp://{configuration["RabbitMQ:UserName"]}:{configuration["RabbitMQ:Password"]}@{configuration["RabbitMQ:HostName"]}:{configuration["RabbitMQ:Port"]}"; options.PoolSize = configuration.GetValue("RabbitMQ:PoolSize"); + options.Declarations = new List>() + { + o=>o.ExchangeDeclare(GlobalConst.TrackChangeExchange, ExchangeType.Topic, true), + }; options.Consumes = new List { new() @@ -148,6 +165,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..96be07a4 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; @@ -11,6 +12,14 @@ public interface IOrderRepository : IBasicRepository /// Task> GetTrackOrderPagedList(GetPageTrackOrderInputVO input); + /// + /// 通过三方平台和三方单号查询订单 + /// + /// 三方平台 + /// 三方平台单号 + /// (主单号id,客户id) + Task<(int MainOrderId, int CustomId)> GetTrackOrderByProviderAndOrderNo(string serviceProvidrCode, string orderNo); + /// /// 创建订单 /// @@ -131,4 +140,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..762c1324 100644 --- a/src/SJZY.Track.Domain/Aggregates/OrderAggregate/OrderEntity.cs +++ b/src/SJZY.Track.Domain/Aggregates/OrderAggregate/OrderEntity.cs @@ -168,10 +168,31 @@ 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; } + + /// + /// 模板code + /// + [SugarColumn(ColumnName = "TemplateCodes")] + public string? TemplateCodes { 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..6f182653 100644 --- a/src/SJZY.Track.Domain/Aggregates/TrackDetailsAggregate/ITrackDetailsEsRepository.cs +++ b/src/SJZY.Track.Domain/Aggregates/TrackDetailsAggregate/ITrackDetailsEsRepository.cs @@ -1,4 +1,6 @@ using SJZY.Expand.ElasticSearch.Services; +using SJZY.Track.Domain.Dtos.TrackContents; +using SJZY.Track.Domain.Enums; namespace SJZY.Track.Domain.Aggregates.TrackDetailsAggregate; @@ -16,13 +18,19 @@ public interface ITrackDetailsEsRepository : IElasticsearchClientService indexes, int maxRetries = 3); + Task BulkUpdateAsync(List indexes, int maxRetries = 3); + Task AddManyAsync(List index); Task AddAsync(TrackDetailsIndex track); Task> GetListAsync(int orderId); + Task> GetListAsync(int[] orderIds, List? trackCodes = null, TrackSourceEnum? source = null); + Task> DeleteTrackAsync(int[] orderIds, List trackCodes); Task DeleteTrackByIdAsync(string id, string indexName); + Task> GetDataByIds(string[] ids); + Task GetDetailAsync(string orderNo, string trackCode, int orderType, int subSystemId, DateTime trackTime); } 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/Aggregates/TrackTemplateAggregate/ITrackTemplateRepository.cs b/src/SJZY.Track.Domain/Aggregates/TrackTemplateAggregate/ITrackTemplateRepository.cs index 8a104c3c..db202083 100644 --- a/src/SJZY.Track.Domain/Aggregates/TrackTemplateAggregate/ITrackTemplateRepository.cs +++ b/src/SJZY.Track.Domain/Aggregates/TrackTemplateAggregate/ITrackTemplateRepository.cs @@ -45,4 +45,5 @@ public interface ITrackTemplateRepository : IBasicRepository> whereExpression); Task> GetTrackTemplateListAsync(GetTrackTemplateListInputVo input); + Task> GetContentByTemplateCodes(string[] codes, int systemId); } 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.Track.cs b/src/SJZY.Track.Domain/Consts/GlobalConst.Track.cs new file mode 100644 index 00000000..eed1301d --- /dev/null +++ b/src/SJZY.Track.Domain/Consts/GlobalConst.Track.cs @@ -0,0 +1,11 @@ +using System.Collections.Immutable; + +namespace SJZY.Track.Domain.Consts; + +public partial class GlobalConst +{ + /// + /// 轨迹数据来源:1外部服务商创建,2内部系统创建,3人工创建 + /// + public static readonly ImmutableHashSet SupportTrackSources = ImmutableHashSet.Create(1, 2, 3); +} 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/TrackCodeMapVO.cs b/src/SJZY.Track.Domain/Dtos/TrackContents/TrackCodeMapVO.cs new file mode 100644 index 00000000..c06fc740 --- /dev/null +++ b/src/SJZY.Track.Domain/Dtos/TrackContents/TrackCodeMapVO.cs @@ -0,0 +1,27 @@ +namespace SJZY.Track.Domain.Dtos.TrackContents; + +/// +/// 轨迹代码映射 +/// +public sealed class TrackCodeMapVO +{ + /// + /// 源子系统id + /// + public int SubSystemId { get; set; } + + /// + /// 源轨迹内容code + /// + public string TrackContentCode { get; set; } = null!; + + /// + /// 映射子系统id + /// + public int MapSubSystemId { get; set; } + + /// + /// 映射轨迹内容code + /// + public string MapTrackContentCode { get; set; } = null!; +} 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.Domain/Queries/IProvideQueries.cs b/src/SJZY.Track.Domain/Queries/IProvideQueries.cs index 60c0b330..351224c5 100644 --- a/src/SJZY.Track.Domain/Queries/IProvideQueries.cs +++ b/src/SJZY.Track.Domain/Queries/IProvideQueries.cs @@ -18,6 +18,14 @@ public interface IProvideQueries /// Task> MapTrackCodesAsync(int upSubSystemId, int subSystemId, params string[] trackCodes); + /// + /// 获取子系统内容Code映射 + /// + /// + /// + /// + /// + Task> GetTrackCodeMapsAsync(int upSubSystemId, int subSystemId, params string[] trackCodes); /// /// 根据订单模板找对应服务商 /// diff --git a/src/SJZY.Track.Repository/Queries/ProvideQueries.cs b/src/SJZY.Track.Repository/Queries/ProvideQueries.cs index 91550425..b59a308d 100644 --- a/src/SJZY.Track.Repository/Queries/ProvideQueries.cs +++ b/src/SJZY.Track.Repository/Queries/ProvideQueries.cs @@ -33,6 +33,22 @@ public class ProvideQueries : ISugarRepository, IProvideQueries, ITransientDepen .ToListAsync(); } + public async Task> GetTrackCodeMapsAsync(int upSubSystemId, int subSystemId, params string[] trackCodes) + { + // 根据上游code得到下游code + return await Context.Queryable() + .Where(x => x.SubSystemId == subSystemId && x.MapSubSystemId == upSubSystemId && + trackCodes.Contains(x.MapTrackContentCode)) + .Select(x => new TrackCodeMapVO() + { + SubSystemId = x.SubSystemId, + TrackContentCode = x.TrackContentCode, + MapSubSystemId = x.MapSubSystemId, + MapTrackContentCode = x.MapTrackContentCode + }) + .ToListAsync(); + } + /// public async Task> FindOrderWithServiceProviderAsync(int subSystemId, List orderNos) @@ -45,7 +61,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 +82,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 +105,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..ec5f18fe 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,13 +48,44 @@ 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); return pageList; } + public async Task<(int MainOrderId, int CustomId)> GetTrackOrderByProviderAndOrderNo(string serviceProvidrCode, string orderNo) + { + var orderList = await Context.Queryable() + .LeftJoin((a, b) => a.Id == b.OrderId && b.IsDeleted == false) + .Where((a, b) => a.OrderNo == orderNo + || (b.Platform == serviceProvidrCode && b.OrderNumber == orderNo)) + .Select((a, b) => new + { + a.Id, + a.CustomId, + b.Platform, + b.OrderNumber + }) + .ToListAsync(); + + if (orderList.Count == 0) + { + return default; + } + + var order = orderList.First(); + //当出现了多条记录匹配,优先使用外部单匹配提升精确度 + var matchOrder = orderList.FirstOrDefault(x => x.Platform == serviceProvidrCode && x.OrderNumber == orderNo); + return matchOrder != null + ? (matchOrder.Id, matchOrder.CustomId) + : (order.Id, order.CustomId); + } + public async Task CreateOrderAsync(OrderEntity order) { using var tran = TransactionHelper.GetReadCommitted(); @@ -60,6 +95,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 +127,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 +207,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 +228,21 @@ public class OrderRepository : BasicRepository, IOrderRepository, I }); } + /// + /// 拼接多个三方单号,默认使用分隔符默认(、) + /// 第三方单号展示: 0000000(服务商)、0000000(服务商) + /// + /// + /// + private static string BuildThirdPartyOrderNumber(List orderPlatformNumberEntities) + { + const string separator = "、"; + var thirdPartyOrderNumbers = orderPlatformNumberEntities + .Where(x => !string.IsNullOrWhiteSpace(x.OrderNumber)) + .Select(x => $"{x.OrderNumber}({x.Platform})").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..246cfa7e 100644 --- a/src/SJZY.Track.Repository/Repositories/TrackDetailsEsRepository.cs +++ b/src/SJZY.Track.Repository/Repositories/TrackDetailsEsRepository.cs @@ -3,6 +3,8 @@ 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.Dtos.TrackContents; +using SJZY.Track.Domain.Enums; namespace SJZY.Track.Repository.Repositories; @@ -74,6 +76,8 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract f.TrackTimeZone) .Field(f => f.OrderId) .Field(f => f.TrackCode) + .Field(f => f.Id) + .Field(f => f.IndexName) )) .Query(q => q .Bool(b => b @@ -98,6 +102,17 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract so .Field(f => f.ZeroTrackTime, SortOrder.Descending) ) + .Source(s2 => s2.Includes(fd2 => fd2 + .Field(f => f.TrackEnContent) + .Field(f => f.TrackContent) + .Field(f => f.TrackPlace) + .Field(f => f.TrackTime) + .Field(f => f.TrackTimeZone) + .Field(f => f.OrderId) + .Field(f => f.TrackCode) + .Field(f => f.Id) + .Field(f => f.IndexName) + )) ) ) ) @@ -145,6 +160,20 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract indexes, int maxRetries = 3) { var retryCount = 0; @@ -174,6 +204,21 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract p .Add("trackPlace", track.TrackPlace) @@ -181,6 +226,21 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract indexes, int maxRetries = 3) + { + if (indexes.Count == 0) return; + + //构建批量更新操作描述符 + var desc = new BulkDescriptor().UpdateMany(indexes, + (u, track) => + u.Id(track.Id) + .Index(track.IndexName) + .Script(s => s.Source(@" + ctx._source.trackPlace = params.trackPlace; + ctx._source.trackTime = params.trackTime; + ctx._source.trackTimeZone = params.trackTimeZone; + ctx._source.updateUser = params.updateUser; + ctx._source.updateTime = params.updateTime; + ctx._source.trackContent = params.trackContent; + ctx._source.trackEnContent = params.trackEnContent; + ctx._source.isVisibility = params.isVisibility; + ctx._source.remarks = params.remarks; + ctx._source.files = params.files; + ctx._source.trackPlaceCity = params.trackPlaceCity; + ctx._source.trackPlaceCityId = params.trackPlaceCityId; + ctx._source.trackPlaceProvince = params.trackPlaceProvince; + ctx._source.trackPlaceProvinceId = params.trackPlaceProvinceId; + ctx._source.trackPlaceNation = params.trackPlaceNation; + ctx._source.trackPlaceNationId = params.trackPlaceNationId; + ctx._source.trackPlacePostcode = params.trackPlacePostcode; + ctx._source.destinationTimeZone = params.destinationTimeZone; + ctx._source.estimatedDeliveryDate = params.estimatedDeliveryDate; + ").Params(p => p + .Add("trackPlace", track.TrackPlace) + .Add("trackTime", track.TrackTime) + .Add("trackTimeZone", track.TrackTimeZone) + .Add("updateUser", track.UpdateUser) + .Add("updateTime", DateTime.Now) + //S06新增字段更新 + .Add("trackContent", track.TrackContent) + .Add("trackEnContent", track.TrackEnContent) + .Add("isVisibility", track.IsVisibility) + .Add("remarks", track.Remarks) + .Add("files", track.Files) + .Add("trackPlaceCity", track.TrackPlaceCity) + .Add("trackPlaceCityId", track.TrackPlaceCityId) + .Add("trackPlaceProvince", track.TrackPlaceProvince) + .Add("trackPlaceProvinceId", track.TrackPlaceProvinceId) + .Add("trackPlaceNation", track.TrackPlaceNation) + .Add("trackPlaceNationId", track.TrackPlaceNationId) + .Add("trackPlacePostcode", track.TrackPlacePostcode) + .Add("destinationTimeZone", track.DestinationTimeZone) + .Add("estimatedDeliveryDate", track.EstimatedDeliveryDate)) + )); + + var retryCount = 0; + var updateSuccessful = false; + while (retryCount < maxRetries && !updateSuccessful) + { + var response = await Context.BulkAsync(desc); + if (response.IsValid) + { + updateSuccessful = true; + } + else if (response.ServerError?.Status == 409) // 409 表示版本冲突 + { + retryCount++; + if (retryCount >= maxRetries) + { + throw new InvalidOperationException( + $"轨迹更新冲突达到最大次数,track:{indexes.Count}条,原因:{response.DebugInformation}。"); + } + } + else + { + throw new InvalidOperationException($"轨迹更新失败,track:{indexes.Count}条,原因:{response.DebugInformation}。"); + } + } + } + public async Task AddManyAsync(List tracks) { // 按天写入es @@ -298,6 +435,39 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract> GetListAsync(int[] orderIds, List? trackCodes = null, TrackSourceEnum? source = null) + { + var query = new QueryContainer(); + + if (orderIds.Length > 0) + { + query &= Query.Terms(t => t.Field(f => f.OrderId).Terms(orderIds)); + } + + if (trackCodes != null && trackCodes.Count > 0) + { + query &= Query.Terms(t => t.Field(f => f.TrackCode.Suffix("keyword")).Terms(trackCodes)); + } + + if (source.HasValue) + { + query &= Query.Term(t => t.Field(f => f.Source).Value(source.Value)); + } + + var r = await Context.SearchAsync(x => x + .Index(GlobalConst.TrackDetailsIndex) + .Query(q => query) + .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 +504,7 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract new { doc.OrderId, @@ -351,6 +522,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 +534,49 @@ 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(); + } + + public async Task GetDetailAsync(string orderNo, string trackCode, int orderType, int subSystemId, DateTime trackTime) + { + var res = await Context.SearchAsync(s => s + .Index("sjzy_track_details") + .Query(q => q + .Bool(b => b + .Must(mu => mu + .Terms(t => t + .Field(f => f.OrderNo.Suffix("keyword")) + .Terms(orderNo)), + mu => mu + .Terms(t => t + .Field(f => f.TrackCode.Suffix("keyword")) + .Terms(trackCode)), + mu => mu + .Terms(t => t + .Field(f => f.OrderType) + .Terms(orderType)), + mu => mu + .Terms(t => t + .Field(f => f.SubSystemId) + .Terms(subSystemId)), + mu => mu + .Terms(t => t + .Field(f => f.TrackTime) + .Terms(trackTime)) // 注意:这里你重复了OrderType的查询,你可能需要移除重复的查询 + ) + ) + ) + ); + + return res.Documents.FirstOrDefault(); + } } diff --git a/src/SJZY.Track.Repository/Repositories/TrackTemplateRepository.cs b/src/SJZY.Track.Repository/Repositories/TrackTemplateRepository.cs index be08e964..9f28d8fc 100644 --- a/src/SJZY.Track.Repository/Repositories/TrackTemplateRepository.cs +++ b/src/SJZY.Track.Repository/Repositories/TrackTemplateRepository.cs @@ -8,6 +8,7 @@ using SJZY.Track.Domain.Aggregates.TrackTemplateAggregate; using SJZY.Track.Domain.Dtos.TrackTemplate; using SJZY.Track.Domain.Enums; using SJZY.Track.Domain.VEntities; +using SJZY.Expand.ABP.Core.SqlSugar.Tools; namespace SJZY.Track.Repository.Repositories; @@ -225,4 +226,26 @@ public class TrackTemplateRepository : BasicRepository, ITr return templates; } + + public async Task> GetContentByTemplateCodes(string[] codes, int systemId) + { + + var sql = @$" + select + distinct + c.Code + from t_track_template a + left join t_track_template_content b on a.id = b.TrackTemplateId and b.IsDeleted = 0 + left join t_track_content c on b.TrackContentId = c.Id and c.IsDeleted = 0 + where + a.SubSystemId = {systemId} + and a.Code in (@codes) + and a.IsDeleted = 0 + "; + + + var list = Context.Ado.SqlQuery(sql, new {codes = codes}).ToList(); + + return list; + } }