Merge pull request 'S06' (#73) from S06 into uat

Reviewed-on: https://git.shijizhongyun.com/Dotnet/SJZY.Track.Api/pulls/73
This commit is contained in:
xumengen_eli 2024-11-27 16:09:03 +08:00
commit 730c01e759
71 changed files with 3048 additions and 143 deletions

View File

@ -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"

View File

@ -0,0 +1,13 @@
using System.ComponentModel.DataAnnotations;
namespace SJZY.Track.Application.Contract.Dtos.ProvideServices;
public class BatchDeleteTrackByEsIdInput
{
/// <summary>
/// 轨迹内容es id,必填
/// </summary>
[MaxLength(500)]
public List<string> TrackContentEsIds { get; set; } = new();
}

View File

@ -5,9 +5,9 @@ namespace SJZY.Track.Application.Contract.Dtos.ProvideServices;
public class BatchDeleteTrackInput : OrderNosInfo
{
/// <summary>
/// 轨迹code,必填
/// 轨迹标识符集合,必填
/// </summary>
[Required]
[MaxLength(500)]
public List<string> TrackCodes { get; set; } = new();
public List<TrackIdentifierDto> TrackIdentifiers { get; set; } = new();
}

View File

@ -29,10 +29,94 @@ public class BatchUpsertTrackInput : OrderNosInfo
[Required]
public DateTime TrackTime { get; set; }
/// <summary>
/// 更改后的轨迹发生时间,无需更改保持为空
/// </summary>
public DateTime? NewTrackTime { get; set; }
/// <summary>
/// 轨迹发生时区
/// </summary>
[Required]
[Range(-12, 12)]
// [Required]
// [Range(-12, 12)]
public float TrackTimeZone { get; set; }
/// <summary>
/// 客户是否可见
/// </summary>
[Required]
public bool IsVisibility { get; set; }
/// <summary>
/// 数据来源:1外部服务商创建,2内部系统创建,3人工创建
/// 批量新增时使用指定轨迹来源,更新时无需指定
/// </summary>
[Required]
public int Source { get; set; }
/// <summary>
/// 轨迹备注
/// </summary>
public string? Remarks { get; set; }
/// <summary>
/// 附件集合
/// </summary>
public List<FileItemDto>? Files { get; set; }
/// <summary>
/// 轨迹内容,若为空则取轨迹内容表中该轨迹Code对应的轨迹内容
/// </summary>
public string? Content { get; set; }
/// <summary>
/// 轨迹内容英文,若为空则取轨迹内容表中该轨迹Code对应的轨迹英文内容
/// </summary>
public string? EnStatus { get; set; }
/// <summary>
/// 轨迹发生地(城市)
/// </summary>
public string? TrackPlaceCity { get; set; }
/// <summary>
/// 轨迹发生地(城市)Id
/// </summary>
public int? TrackPlaceCityId { get; set; }
/// <summary>
/// 轨迹发生地(省/州)
/// </summary>
public string? TrackPlaceProvince { get; set; }
/// <summary>
///轨迹发生地(省/州)Id
/// </summary>
public int? TrackPlaceProvinceId { get; set; }
/// <summary>
/// 轨迹发生地(国家/地区)
/// </summary>
public string? TrackPlaceNation { get; set; }
/// <summary>
/// 轨迹发生地(国家/地区)Id
/// </summary>
public int? TrackPlaceNationId { get; set; }
/// <summary>
/// 轨迹发生地邮编
/// </summary>
public string? TrackPlacePostcode { get; set; }
/// <summary>
/// 预计到达时间
/// </summary>
public DateTime? EstimatedDeliveryDate { get; set; }
/// <summary>
/// 目的地时区
/// </summary>
[Range(-12, 12)]
public float? DestinationTimeZone { get; set; }
}

View File

@ -9,8 +9,8 @@ public class DeleteOrderInput : OrderNoInfo
public class DeleteTrackInput : OrderNoInfo
{
/// <summary>
/// 轨迹code
/// 轨迹标识符集合,必填
/// </summary>
[Required]
public List<string> TrackCodes { get; set; } = new();
public List<TrackIdentifierDto> TrackIdentifiers { get; set; } = new();
}

View File

@ -0,0 +1,14 @@
namespace SJZY.Track.Application.Contract.Dtos.ProvideServices;
public class FileItemDto
{
/// <summary>
/// 文件osskey
/// </summary>
public string OssKey { get; set; }
/// <summary>
/// 文件名称
/// </summary>
public string FileName { get; set; }
}

View File

@ -0,0 +1,19 @@
namespace SJZY.Track.Application.Contract.Dtos.ProvideServices;
public class GetDetailTimelineInput
{
/// <summary>
/// 订单编号
/// </summary>
public string OrderNumber { get; set; }
/// <summary>
/// 订单所属子系统id
/// </summary>
public int SystemId { get; set; }
/// <summary>
/// 当前用户登陆子系统id
/// </summary>
public int CurrenUserSystemId { get; set; }
}

View File

@ -0,0 +1,92 @@
namespace SJZY.Track.Application.Contract.Dtos.ProvideServices;
public class GetDetailTimelineOutput
{
/// <summary>
/// 轨迹编码
/// </summary>
public string TrackCode { get; set; }
/// <summary>
/// 轨迹内容
/// </summary>
public string Content { get; set; }
/// <summary>
/// 轨迹英文内容
/// </summary>
public string EnContent { get; set; }
/// <summary>
/// 轨迹发生时间
/// </summary>
public long TrackTime { get; set; }
/// <summary>
/// 轨迹时间前缀
/// </summary>
public string TrackTimePrefix { get; set; }
/// <summary>
/// 轨迹发生时区
/// </summary>
public string TrackTimeZone { get; set; }
/// <summary>
/// 备注
/// </summary>
public string Remark { get; set; }
/// <summary>
/// 文件 osskey 集合
/// </summary>
public List<TrackFileItem> Files { get; set; }
/// <summary>
/// 是否高亮
/// </summary>
public bool IsHighlight { get; set; }
/// <summary>
/// 是否显示悬停效果
/// </summary>
public bool IsShowHover { get; set; }
/// <summary>
/// 轨迹发生地
/// </summary>
public string TrackPlace { get; set; }
/// <summary>
/// 轨迹发生地(城市)
/// </summary>
public string? TrackPlaceCity { get; set; }
/// <summary>
/// 轨迹发生地(省/州)
/// </summary>
public string? TrackPlaceProvince { get; set; }
/// <summary>
/// 轨迹发生地(国家/地区)
/// </summary>
public string? TrackPlaceNation { get; set; }
}
public class TrackFileItem
{
/// <summary>
/// 文件osskey
/// </summary>
public string OssKey { get; set; }
/// <summary>
/// 文件名称
/// </summary>
public string FileName { get; set; }
/// <summary>
/// 文件url
/// </summary>
public string Url { get; set; }
}

View File

@ -86,4 +86,54 @@ public class TrackOrderDetailDto
/// 创建人
/// </summary>
public string? CreateUser { get; set; }
/// <summary>
/// 客户是否可见
/// </summary>
public bool IsVisibility { get; set; }
/// <summary>
/// 数据来源:1外部服务商创建,2内部系统创建,3人工创建
/// </summary>
public int Source { get; set; }
/// <summary>
/// 备注
/// </summary>
public string? Remarks { get; set; }
/// <summary>
/// 附件集合
/// </summary>
public List<FileItemDto>? Files { get; set; }
/// <summary>
/// 轨迹发生地(城市)
/// </summary>
public string? TrackPlaceCity { get; set; }
/// <summary>
/// 轨迹发生地(省/州)
/// </summary>
public string? TrackPlaceProvince { get; set; }
/// <summary>
/// 轨迹发生地(国家/地区)
/// </summary>
public string? TrackPlaceNation { get; set; }
/// <summary>
/// 轨迹发生地邮编
/// </summary>
public string? TrackPlacePostcode { get; set; }
/// <summary>
/// 预计到达时间
/// </summary>
public DateTime? EstimatedDeliveryDate { get; set; }
/// <summary>
/// 目的地时区
/// </summary>
public float? DestinationTimeZone { get; set; }
}

View File

@ -152,4 +152,19 @@ public class TrackOrderDto
///// 问题描述名称
///// </summary>
//public string? ProblemDescriptionName { get; set; }
/// <summary>
/// 预计到达时间
/// </summary>
public DateTime? EstimatedDeliveryDate { get; set; }
/// <summary>
/// 目的地时区
/// </summary>
public float? DestinationTimeZone { get; set; }
/// <summary>
/// 第三方单号,服务商-轨迹0000000、轨迹服务商-0000000
/// </summary>
public string? ThirdPartyOrderNumber { get; set; }
}

View File

@ -7,6 +7,9 @@ public class GetTrackOutput
/// </summary>
public string OrderNo { get; set; } = null!;
/// <summary>
/// 轨迹明细列表
/// </summary>
public List<TrackDetail> TrackDetails { get; set; } = new();
}
@ -32,4 +35,70 @@ public class TrackDetail
/// 是否对外
/// </summary>
public bool IsDisplay { get; set; }
/// <summary>
/// 客户是否可见
/// </summary>
public bool IsVisibility { get; set; }
/// <summary>
/// 数据来源:1外部服务商创建,2内部系统创建,3人工创建
/// </summary>
public int Source { get; set; }
/// <summary>
/// 备注
/// </summary>
public string? Remarks { get; set; }
/// <summary>
/// 附件集合
/// </summary>
public List<FileItemDto>? Files { get; set; }
/// <summary>
/// 轨迹发生地(城市)
/// </summary>
public string? TrackPlaceCity { get; set; }
/// <summary>
/// 轨迹发生地(城市)Id
/// </summary>
public int? TrackPlaceCityId { get; set; }
/// <summary>
/// 轨迹发生地(省/州)
/// </summary>
public string? TrackPlaceProvince { get; set; }
/// <summary>
/// 轨迹发生地(省/州)Id
/// </summary>
public int? TrackPlaceProvinceId { get; set; }
/// <summary>
/// 轨迹发生地(国家/地区)
/// </summary>
public string? TrackPlaceNation { get; set; }
/// <summary>
/// 轨迹发生地(国家/地区)Id
/// </summary>
public int? TrackPlaceNationId { get; set; }
/// <summary>
/// 轨迹发生地邮编
/// </summary>
public string? TrackPlacePostcode { get; set; }
/// <summary>
/// 预计到达时间
/// </summary>
public DateTime? EstimatedDeliveryDate { get; set; }
/// <summary>
/// 目的地时区
/// </summary>
public float? DestinationTimeZone { get; set; }
}

View File

@ -0,0 +1,21 @@
using System.ComponentModel.DataAnnotations;
namespace SJZY.Track.Application.Contract.Dtos.ProvideServices;
/// <summary>
/// 轨迹标识符,用于唯一标识一个轨迹
/// </summary>
public sealed class TrackIdentifierDto
{
/// <summary>
/// 轨迹code
/// </summary>
[Required]
public string TrackCode { get; set; }
/// <summary>
/// 轨迹发生时间
/// </summary>
[Required]
public DateTime TrackTime { get; set; }
}

View File

@ -57,6 +57,16 @@ public class UpdateOrderInput : OrderNoInfo
/// </summary>
public string? ProductCode { get; set; }
/// <summary>
/// 预计到达时间
/// </summary>
public DateTime? EstimatedDeliveryDate { get; set; }
/// <summary>
/// 目的地时区
/// </summary>
public float? DestinationTimeZone { get; set; }
/// <summary>
/// 拉取配置信息
/// </summary>
@ -76,6 +86,11 @@ public class PullConfigInfo
[Required]
public string ServiceCode { get; set; }
/// <summary>
/// api服务商订单号(第三方单号)
/// </summary>
public string? OrderNo { get; set; }
/// <summary>
/// 开始轨迹code
/// </summary>

View File

@ -7,9 +7,17 @@ public class UpsertOrderInput : UpdateOrderInput
/// <summary>
/// 客户id
/// </summary>
[Required]
[Range(1, int.MaxValue)]
public int CustomId { get; set; }
/// <summary>
/// 模板code
/// </summary>
public List<string> TemplateCodes { get; set; }
/// <summary>
/// 当前操作人
/// </summary>
public int OptionUserId { get; set; }
}
public class OrderFlightInfo

View File

@ -29,10 +29,98 @@ public class UpsertTrackInput : OrderNoInfo
[Required]
public DateTime? TrackTime { get; set; }
/// <summary>
/// 更改后的轨迹发生时间,无需更改保持为空
/// </summary>
public DateTime? NewTrackTime { get; set; }
/// <summary>
/// 轨迹发生时区
/// </summary>
[Required]
[Range(-12, 12)]
// [Required]
// [Range(-12, 12)]
public float? TrackTimeZone { get; set; }
/// <summary>
/// 客户是否可见
/// </summary>
[Required]
public bool IsVisibility { get; set; }
/// <summary>
/// 数据来源:1外部服务商创建,2内部系统创建,3人工创建
/// </summary>
[Required]
public int Source { get; set; }
/// <summary>
/// 轨迹备注
/// </summary>
public string? Remarks { get; set; }
/// <summary>
/// 附件集合
/// </summary>
public List<FileItemDto>? Files { get; set; }
/// <summary>
/// 轨迹内容,若为空则取轨迹内容表中该轨迹Code对应的轨迹内容
/// </summary>
public string? Content { get; set; }
/// <summary>
/// 轨迹内容英文,若为空则取轨迹内容表中该轨迹Code对应的轨迹英文内容
/// </summary>
public string? EnStatus { get; set; }
/// <summary>
/// 轨迹发生地(城市)
/// </summary>
public string? TrackPlaceCity { get; set; }
/// <summary>
/// 轨迹发生地(城市)Id
/// </summary>
public int? TrackPlaceCityId { get; set; }
/// <summary>
/// 轨迹发生地(省/州)
/// </summary>
public string? TrackPlaceProvince { get; set; }
/// <summary>
/// 轨迹发生地(省/州)Id
/// </summary>
public int? TrackPlaceProvinceId { get; set; }
/// <summary>
/// 轨迹发生地(国家/地区)
/// </summary>
public string? TrackPlaceNation { get; set; }
/// <summary>
/// 轨迹发生地(国家/地区)Id
/// </summary>
public int? TrackPlaceNationId { get; set; }
/// <summary>
/// 轨迹发生地邮编
/// </summary>
public string? TrackPlacePostcode { get; set; }
/// <summary>
/// 预计到达时间
/// </summary>
public DateTime? EstimatedDeliveryDate { get; set; }
/// <summary>
/// 目的地时区
/// </summary>
[Range(-12, 12)]
public float? DestinationTimeZone { get; set; }
/// <summary>
/// 当前操作人
/// </summary>
public int OptionUserId { get; set; }
}

View File

@ -46,6 +46,21 @@ public class EditTrackContentInput
/// 是否对外
/// </summary>
public bool IsExternal { get; set; }
/// <summary>
/// 是否可重复
/// </summary>
public bool IsRepeatable { get; set; }
/// <summary>
/// 是否允许手动修改
/// </summary>
public bool IsAllowManuallyUpdate { get; set; }
/// <summary>
/// 是否允许手动创建
/// </summary>
public bool IsAllowManuallyCreate { get; set; }
}
public class GetTrackContentDetailDto
@ -104,4 +119,19 @@ public class GetTrackContentDetailDto
/// 是否对外
/// </summary>
public bool IsExternal { get; set; }
/// <summary>
/// 是否可重复
/// </summary>
public bool IsRepeatable { get; set; }
/// <summary>
/// 是否允许手动修改
/// </summary>
public bool IsAllowManuallyUpdate { get; set; }
/// <summary>
/// 是否允许手动创建
/// </summary>
public bool IsAllowManuallyCreate { get; set; }
}

View File

@ -0,0 +1,26 @@
namespace SJZY.Track.Application.Contract.Dtos.TrackContents;
public class GetAvailableContentInput
{
/// <summary>
/// 子系统id
/// </summary>
public int SystemId { get; set; }
/// <summary>
/// 订单号
/// </summary>
public string? OrderNo { get; set; }
/// <summary>
/// 操作类型 1=创建 2=修改 3=删除
/// </summary>
public TrackOptionType TrackOptionType { get; set; }
}
public enum TrackOptionType
{
创建 = 1,
修改 = 2,
删除 = 3
}

View File

@ -0,0 +1,104 @@
using SJZY.Track.Application.Contract.Dtos.ProvideServices;
namespace SJZY.Track.Application.Contract.Dtos.TrackContents;
public class GetAvailableContentOutput
{
/// <summary>
/// Id
/// </summary>
public int Id { get; set; }
/// <summary>
/// 轨迹内容代码
/// </summary>
public string Code { get; set; }
/// <summary>
/// 轨迹内容
/// </summary>
public string Content { get; set; }
/// <summary>
/// 轨迹内容英文
/// </summary>
public string EnContent { get; set; }
/// <summary>
/// 是否对外
/// </summary>
public bool IsExternal { get; set; }
/// <summary>
/// 轨迹来源
/// </summary>
public int? Source { get; set; }
/// <summary>
/// 轨迹发生地
/// </summary>
public string? TrackPlace { get; set; }
/// <summary>
/// 轨迹发生时间
/// </summary>
public DateTime? TrackTime { get; set; }
/// <summary>
/// 轨迹发生时区
/// </summary>
public float? TrackTimeZone { get; set; }
/// <summary>
/// 时区的值
/// </summary>
public float? TrackTimeZoneValue { get; set; }
/// <summary>
/// 备注
/// </summary>
public string? Remarks { get; set; }
/// <summary>
/// 轨迹发生地(城市)
/// </summary>
public string? TrackPlaceCity { get; set; }
public int? TrackPlaceCityId { get; set; }
/// <summary>
/// 轨迹发生地(省/州)
/// </summary>
public string? TrackPlaceProvince { get; set; }
public int? TrackPlaceProvinceId { get; set; }
/// <summary>
/// 轨迹发生地(国家/地区)
/// </summary>
public string? TrackPlaceNation { get; set; }
public int? TrackPlaceNationId { get; set; }
/// <summary>
/// 轨迹发生地邮编
/// </summary>
public string? TrackPlacePostcode { get; set; }
/// <summary>
/// 预计到达时间
/// </summary>
public DateTime? EstimatedDeliveryDate { get; set; }
/// <summary>
/// 目的地时区
/// </summary>
public float? DestinationTimeZone { get; set; }
/// <summary>
/// 附件文件集合
/// </summary>
public List<FileItemDto>? FileItems { get; set; }
/// <summary>
/// 客户是否可见
/// </summary>
public bool IsVisibility { get; set; }
}

View File

@ -0,0 +1,29 @@
namespace SJZY.Track.Application.Contract.Dtos.TrackContents;
public class GetContentDetailInput
{
/// <summary>
/// 子系统编号
/// </summary>
public int SystemId { get; set; }
/// <summary>
/// 订单号
/// </summary>
public string OrderNumber { get; set; }
/// <summary>
/// 订单类型
/// </summary>
public int OrderType { get; set; }
/// <summary>
/// 轨迹code
/// </summary>
public string TrackCode { get; set; }
/// <summary>
/// 轨迹发生时间
/// </summary>
public DateTime TrackTime { get; set; }
}

View File

@ -0,0 +1,192 @@
namespace SJZY.Track.Application.Contract.Dtos.TrackContents;
public class GetContentDetailOutput
{
/// <summary>
/// 子系统
/// </summary>
public int SubSystemId { get; set; }
/// <summary>
/// 订单Id
/// </summary>
public int OrderId { get; set; }
/// <summary>
/// 订单号
/// </summary>
public string OrderNo { get; set; } = null!;
/// <summary>
/// 索引名称
/// </summary>
public string IndexName { get; set; } = null!;
/// <summary>
/// 订单类型(主数据枚举TrackOrderType)
/// </summary>
public int OrderType { get; set; }
/// <summary>
/// 轨迹状态
/// </summary>
public string TrackStatus { get; set; }
/// <summary>
/// 轨迹状态code
/// </summary>
public string TrackStatusCode { get; set; }
/// <summary>
/// 轨迹内容代码
/// </summary>
public string TrackCode { get; set; }
/// <summary>
/// 轨迹内容
/// </summary>
public string TrackContent { get; set; }
/// <summary>
/// 轨迹内容英文
/// </summary>
public string TrackEnContent { get; set; }
/// <summary>
/// 服务商code
/// </summary>
public string ServiceProviderCode { get; set; }
/// <summary>
/// 原轨迹内容
/// </summary>
public string? OriginTrackContent { get; set; }
/// <summary>
/// 原轨迹报文hash(刷新轨迹重复性校验使用)
/// </summary>
public string OriginTrackDataHash { get; set; } = null!;
/// <summary>
/// 轨迹发生地
/// </summary>
public string TrackPlace { get; set; }
/// <summary>
/// 轨迹发生时间
/// </summary>
public DateTime TrackTime { get; set; }
/// <summary>
/// 轨迹发生时区
/// </summary>
public float TrackTimeZone { get; set; }
/// <summary>
/// 转化后的轨迹发生时间
/// </summary>
public DateTime ZeroTrackTime { get; set; }
/// <summary>
/// 创建人
/// </summary>
public string CreateUser { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreateTime { get; set; }
/// <summary>
/// 修改人
/// </summary>
public string UpdateUser { get; set; }
/// <summary>
/// 修改时间
/// </summary>
public DateTime? UpdateTime { get; set; }
/// <summary>
/// 客户是否可见
/// </summary>
public bool IsVisibility { get; set; }
/// <summary>
/// 数据来源:1外部服务商创建,2内部系统创建,3人工创建
/// </summary>
public int Source { get; set; }
/// <summary>
/// 备注
/// </summary>
public string? Remarks { get; set; }
/// <summary>
/// 附件集合
/// </summary>
public List<FileItem>? Files { get; set; }
/// <summary>
/// 轨迹发生地(城市)
/// </summary>
public string? TrackPlaceCity { get; set; }
/// <summary>
/// 轨迹发生地(城市)Id
/// </summary>
public int? TrackPlaceCityId { get; set; }
/// <summary>
/// 轨迹发生地(省/州)
/// </summary>
public string? TrackPlaceProvince { get; set; }
/// <summary>
///轨迹发生地(省/州)Id
/// </summary>
public int? TrackPlaceProvinceId { get; set; }
/// <summary>
/// 轨迹发生地(国家/地区)
/// </summary>
public string? TrackPlaceNation { get; set; }
/// <summary>
/// 轨迹发生地(国家/地区)Id
/// </summary>
public int? TrackPlaceNationId { get; set; }
/// <summary>
/// 轨迹发生地邮编
/// </summary>
public string? TrackPlacePostcode { get; set; }
/// <summary>
/// 预计签收时间
/// </summary>
public DateTime? EstimatedDeliveryDate { get; set; }
/// <summary>
/// 签收地时区
/// </summary>
public float? DestinationTimeZone { get; set; }
}
public class FileItem
{
/// <summary>
/// 文件osskey
/// </summary>
public string OssKey { get; set; }
/// <summary>
/// 文件名称
/// </summary>
public string FileName { get; set; }
/// <summary>
/// 路径
/// </summary>
public string Url { get; set; }
}

View File

@ -89,4 +89,19 @@ public class GetTrackContentListDto
/// 修改人
/// </summary>
public string UpdateUser { get; set; }
/// <summary>
/// 是否可重复
/// </summary>
public bool IsRepeatable { get; set; }
/// <summary>
/// 是否允许手动修改
/// </summary>
public bool IsAllowManuallyUpdate { get; set; }
/// <summary>
/// 是否允许手动创建
/// </summary>
public bool IsAllowManuallyCreate { get; set; }
}

View File

@ -0,0 +1,14 @@
namespace SJZY.Track.Application.Contract.Dtos.TrackContents;
public class GetTrackContentByCodeInput
{
/// <summary>
/// 子系统id
/// </summary>
public int SystemId { get; set; }
/// <summary>
/// 轨迹内容code
/// </summary>
public string Code { get; set; }
}

View File

@ -0,0 +1,19 @@
namespace SJZY.Track.Application.Contract.Dtos.TrackContents;
public class GetTrackContentByCodeOutput
{
/// <summary>
/// 中文内容
/// </summary>
public string Content { get; set; }
/// <summary>
/// 英文内容
/// </summary>
public string EnContent { get; set; }
/// <summary>
/// 是否对外
/// </summary>
public bool IsExternal { get; set; }
}

View File

@ -41,4 +41,14 @@ public class TrackChangeAfterUpdateOrderEvent
/// 时区缺失和发生地缺失标记,1表示时区,2表示发生地,3表示两种
/// </summary>
public int DeficiencyTagValue { get; set; }
/// <summary>
/// 预计到达时间
/// </summary>
public DateTime? EstimatedDeliveryDate { get; set; }
/// <summary>
/// 签收地时区
/// </summary>
public float? DestinationTimeZone { get; set; }
}

View File

@ -0,0 +1,131 @@
using SJZY.Track.Application.Contract.Dtos.ProvideServices;
namespace SJZY.Track.Application.Contract.Events;
public class TrackChangeEto
{
/// <summary>
/// 操作类型:1新增,2删除,3修改
/// </summary>
public int Option { get; set; }
/// <summary>
/// 子系统id
/// </summary>
public int SubSystemId { get; set; }
/// <summary>
/// 单号
/// </summary>
public string OrderNo { get; set; }
/// <summary>
/// 客户id
/// </summary>
public int CustomId { get; set; }
/// <summary>
/// 订单id
/// </summary>
public OrderIdentifier[] OrderIdentifiers { get; set; } = null!;
/// <summary>
/// 操作人
/// </summary>
public string? OperationMan { get; set; }
/// <summary>
/// 轨迹内容代码
/// </summary>
public string TrackCode { get; set; } = null!;
/// <summary>
/// 轨迹内容
/// </summary>
public string? Content { get; set; }
/// <summary>
/// 轨迹内容英文
/// </summary>
public string? EnStatus { get; set; }
/// <summary>
/// 轨迹状态代码
/// </summary>
public string TrackStatusCode { get; set; }
/// <summary>
/// 轨迹发生地
/// </summary>
public string TrackPlace { get; set; } = null!;
/// <summary>
/// 轨迹发生时间
/// </summary>
public DateTime TrackTime { get; set; }
/// <summary>
/// 轨迹发生地时区
/// </summary>
public float TrackTimeZone { get; set; }
/// <summary>
/// 客户是否可见
/// </summary>
public bool IsVisibility { get; set; }
/// <summary>
/// 数据来源:1外部服务商创建,2内部系统创建,3人工创建
/// </summary>
public int Source { get; set; }
/// <summary>
/// 轨迹备注
/// </summary>
public string? Remarks { get; set; }
/// <summary>
/// 附件集合
/// </summary>
public List<FileItemDto>? Files { get; set; }
/// <summary>
/// 轨迹发生地(城市)
/// </summary>
public string? TrackPlaceCity { get; set; }
/// <summary>
/// 轨迹发生地(城市)Id
/// </summary>
public int? TrackPlaceCityId { get; set; }
/// <summary>
/// 轨迹发生地(省/州)
/// </summary>
public string? TrackPlaceProvince { get; set; }
/// <summary>
///轨迹发生地(省/州)Id
/// </summary>
public int? TrackPlaceProvinceId { get; set; }
/// <summary>
/// 轨迹发生地(国家/地区)
/// </summary>
public string? TrackPlaceNation { get; set; }
/// <summary>
/// 轨迹发生地(国家/地区)Id
/// </summary>
public int? TrackPlaceNationId { get; set; }
/// <summary>
/// 轨迹发生地邮编
/// </summary>
public string? TrackPlacePostcode { get; set; }
/// <summary>
/// 预计到达时间
/// </summary>
public DateTime? EstimatedDeliveryDate { get; set; }
}

View File

@ -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
/// </summary>
public int SubSystemId { get; set; }
/// <summary>
/// es中的id
/// </summary>
public string[] EsIds { get; set; }
/// <summary>
/// 订单ids
/// </summary>
public int[] OrderIds { get; set; } = null!;
/// <summary>
/// 轨迹代码
/// 轨迹标识符
/// </summary>
public List<string> TrackCodes { get; set; } = null!;
public List<TrackIdentifierDto> TrackIdentifiers { get; set; } = null!;
/// <summary>
/// 操作人
/// </summary>
public string? OperationMan { get; set; }
/// <summary>
/// 操作人姓名
/// </summary>
public int OperationManId { get; set; }
/// <summary>
/// 已经处理过的订单
/// </summary>
@ -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))}";
}
}

View File

@ -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
/// </summary>
public string? OperationMan { get; set; }
/// <summary>
/// 操作人id
/// </summary>
public int OperationManId { get; set; }
/// <summary>
/// true新增,false更新
/// </summary>
@ -42,6 +49,11 @@ public class TrackUpsertEvent
/// </summary>
public DateTime TrackTime { get; set; }
/// <summary>
/// 更改后的轨迹发生时间,无需更改保持为空
/// </summary>
public DateTime? NewTrackTime { get; set; }
/// <summary>
/// 轨迹发生时区
/// </summary>
@ -54,6 +66,81 @@ public class TrackUpsertEvent
public TrackRefreshStruct TrackRefreshData { get; set; }
/// <summary>
/// 客户是否可见
/// </summary>
public bool IsVisibility { get; set; }
/// <summary>
/// 数据来源:1外部服务商创建,2内部系统创建,3人工创建
/// </summary>
public int Source { get; set; }
/// <summary>
/// 轨迹备注
/// </summary>
public string? Remarks { get; set; }
/// <summary>
/// 附件集合
/// </summary>
public List<FileItemDto>? Files { get; set; }
/// <summary>
/// 轨迹内容,若为空则取轨迹内容表中该轨迹Code对应的轨迹内容
/// </summary>
public string? Content { get; set; }
/// <summary>
/// 轨迹内容英文,若为空则取轨迹内容表中该轨迹Code对应的轨迹英文内容
/// </summary>
public string? EnStatus { get; set; }
/// <summary>
/// 轨迹发生地(城市)
/// </summary>
public string? TrackPlaceCity { get; set; }
/// <summary>
/// 轨迹发生地(城市)Id
/// </summary>
public int? TrackPlaceCityId { get; set; }
/// <summary>
/// 轨迹发生地(省/州)
/// </summary>
public string? TrackPlaceProvince { get; set; }
/// <summary>
///轨迹发生地(省/州)Id
/// </summary>
public int? TrackPlaceProvinceId { get; set; }
/// <summary>
/// 轨迹发生地(国家/地区)
/// </summary>
public string? TrackPlaceNation { get; set; }
/// <summary>
/// 轨迹发生地(国家/地区)Id
/// </summary>
public int? TrackPlaceNationId { get; set; }
/// <summary>
/// 轨迹发生地邮编
/// </summary>
public string? TrackPlacePostcode { get; set; }
/// <summary>
/// 预计到达时间
/// </summary>
public DateTime? EstimatedDeliveryDate { get; set; }
/// <summary>
/// 目的地时区
/// </summary>
public float? DestinationTimeZone { get; set; }
public override string ToString()
{
return

View File

@ -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<FileItem, FileItemDto>(MemberList.None);
CreateMap<TrackPushRecordIndex, TrackPushRecordOutput>(MemberList.None);
}

View File

@ -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<GetTrackContentListVO>, UnifyPageResultDto<GetTrackContentListDto>>(
MemberList.None);
CreateMap<EditTrackContentMapInput, TrackContentMapEntity>(MemberList.None);
CreateMap<TrackDetailsIndex, GetContentDetailOutput>(MemberList.None).ReverseMap();
CreateMap< SJZY.Track.Domain.Aggregates.TrackDetailsAggregate.FileItem, SJZY.Track.Application.Contract.Dtos.TrackContents.FileItem>(MemberList.None).ReverseMap();
}
}

View File

@ -26,6 +26,8 @@ public class TrackChangeAfterUpdateOrderEventHandler : ITransientDependency, ICa
return;
}
//指示是否需要更新【预计签收时间】【签收地时区】
var isNeedUpdate = false;
var updateOrders = new List<OrderEntity>();
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<List<OrderEntity>> GetOrdersAsync(int[] orderIds)

View File

@ -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<TrackDetailsIndex> 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<List<TrackDetailsIndex>> DeleteTrackAsync(TrackDeleteEvent @event)
{
List<int> orderIds;
List<TrackDetailsIndex> 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<string>();
}
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;
}
}

View File

@ -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<int>();
await HandleTrackAsync(@event, canNextOrderIds);
var content = await HandleTrackAsync(@event, canNextOrderIds);
// 处理广播事件
PublicTrackTopic(@event, content);
if (canNextOrderIds.Count == 0) return;
// 根据订单关联关系异步向下刷(异步)
await DownByOrderLink(@event, canNextOrderIds);
}
/// <summary>
/// 发送轨迹广播
/// </summary>
/// <param name="eto"></param>
/// <param name="content"></param>
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<int> canNextOrderIds)
{
// 根据订单关联关系异步向下刷(异步)
@ -125,7 +188,7 @@ public class TrackUpsertEventHandler : ITransientDependency, ICapSubscribe
return true;
}
private async Task HandleTrackAsync(TrackUpsertEvent @event, List<int> canNextOrderIds)
private async Task<TrackContentVO?> HandleTrackAsync(TrackUpsertEvent @event, List<int> 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<List<TrackDetailsIndex>> UpsertTrackAsync(TrackUpsertEvent @event, List<int> 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<TrackDetailsIndex>();
var updateTracks = new List<TrackDetailsIndex>();
@ -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;

View File

@ -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)

View File

@ -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

View File

@ -8,8 +8,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BSI.Stack.Rabbit" Version="2.0.9" />
<PackageReference Include="BSI.Stack.Rabbit" Version="2.1.3" />
<PackageReference Include="Savorboard.CAP.InMemoryMessageQueue" Version="8.2.1"/>
<PackageReference Include="SJZY.Expand.AliyunOSS" Version="7.0.17" />
<PackageReference Include="SJZY.InterfaceRelay.Application.Contracts" Version="7.2.3"/>
<PackageReference Include="Volo.Abp.Http.Client" Version="7.0.2" />
<PackageReference Include="Volo.Abp.FluentValidation" Version="7.0.2" />

View File

@ -36,6 +36,15 @@ public partial class OrderService : ApplicationService, IOrderService
[HttpPost("Order/Upsert")]
public async Task<UnifyResultDto<IdBaseDto>> 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<OrderFlightInfo>, List<OrderFlightEntity>>(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<OrderFlightInfo>, List<OrderFlightEntity>>(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<OrderPlatformNumberEntity> BuiltOrderPlatformNumbers(List<PullConfigInfo>? pullConfigInfos)
{
if (pullConfigInfos == null) return new List<OrderPlatformNumberEntity>();
return pullConfigInfos.Select(x =>
{
var status = new OrderPlatformNumberEntity
{
//三方单号
OrderNumber = x.OrderNo,
//三方平台名称,目前认为是服务商code
Platform = x.ServiceCode,
};
return status;
}).ToList();
}
}

View File

@ -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);

View File

@ -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);
}
/// <summary>
/// 根据esid删除轨迹内容
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("Track/BatchDeleteByEsId")]
public async Task<UnifyResultDto> 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);
}
/// <summary>
/// 批量删除轨迹
@ -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);
}

View File

@ -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;
}
/// <summary>
/// 定时任务刷新轨迹
/// </summary>
@ -30,7 +37,7 @@ public class TrackPullTask : ApplicationService, ISingletonDependency
[HttpPost("/TimerTask/Refresh")]
public async Task<bool> RefreshForTaskAsync()
{
using var serviceScope = LazyServiceProvider.CreateScope();
using var serviceScope = _serviceScopeFactory.CreateScope();
var provideQueries = serviceScope.ServiceProvider.GetRequiredService<IProvideQueries>();
var configs = await provideQueries.GetAllPullConfigsAsync();

View File

@ -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;
}
/// <summary>
/// 定时任务推送预警(》12小时没有推送的)
/// </summary>
@ -21,7 +28,7 @@ public class TrackPushTask : ApplicationService
[HttpPost("/TimerTask/PushWarn")]
public async Task<string> PushWarnForTaskAsync()
{
using var serviceScope = LazyServiceProvider.CreateScope();
using var serviceScope = _serviceScopeFactory.CreateScope();
var configs = serviceScope.ServiceProvider.GetRequiredService<IOptionsSnapshot<Track2InterfaceRelayOptions>>()
.Value;

View File

@ -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;
/// 轨迹运输时效预警定时任务
/// </summary>
[ApiExplorerSettings(GroupName = "定时任务")]
public class TrackWarningTask : ApplicationService
public class TrackWarningTask : ApplicationService, ISingletonDependency
{
private readonly IOptionsSnapshot<Track2InterfaceRelayOptions> _options;
private readonly IOrderRepository _orderRepository;
private readonly IServiceScopeFactory _serviceScopeFactory;
public TrackWarningTask(IOrderRepository orderRepository, IOptionsSnapshot<Track2InterfaceRelayOptions> options)
public TrackWarningTask(IOrderRepository orderRepository, IOptionsSnapshot<Track2InterfaceRelayOptions> options,
IServiceScopeFactory serviceScopeFactory)
{
_orderRepository = orderRepository;
_options = options;
_serviceScopeFactory = serviceScopeFactory;
}
/// <summary>
@ -63,7 +67,7 @@ public class TrackWarningTask : ApplicationService
{
try
{
using var serviceScope = LazyServiceProvider.CreateScope();
using var serviceScope = _serviceScopeFactory.CreateScope();
var forewarningNotifyService =
serviceScope.ServiceProvider.GetRequiredService<IForewarningNotifyService>();

View File

@ -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<TrackLocalizationResource> _stringLocalizer;
private readonly ITrackContentRepository _trackContentRepository;
private readonly ITrackTemplateRepository _trackTemplateRepository;
private readonly IBasicQuery<TrackFlagEntity> _trackFlagBasicQuery;
private readonly IBasicQuery<TrackStatusEntity> _trackStatusBasicQuery;
private readonly IOrderRepository _orderRepository;
private readonly ITrackDetailsEsRepository _trackDetailsEsRepository;
public TrackContentService(
IStringLocalizer<TrackLocalizationResource> stringLocalizer,
ITrackContentRepository trackContentRepository,
IBasicQuery<TrackStatusEntity> trackStatusBasicQuery,
IBasicQuery<TrackFlagEntity> trackFlagBasicQuery)
IBasicQuery<TrackFlagEntity> trackFlagBasicQuery,
IOrderRepository orderRepository,
ITrackDetailsEsRepository trackDetailsEsRepository,
ITrackTemplateRepository trackTemplateRepository)
{
_stringLocalizer = stringLocalizer;
_trackContentRepository = trackContentRepository;
_trackStatusBasicQuery = trackStatusBasicQuery;
_trackFlagBasicQuery = trackFlagBasicQuery;
_orderRepository = orderRepository;
_trackDetailsEsRepository = trackDetailsEsRepository;
_trackTemplateRepository = trackTemplateRepository;
}
/// <summary>
@ -96,8 +111,7 @@ public class TrackContentService : ApplicationService
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("GetList")]
public async Task<UnifyPageResultDto<GetTrackContentListDto>> GetPageTrackContent(
GetPageTrackContentInput input)
public async Task<UnifyPageResultDto<GetTrackContentListDto>> GetPageTrackContent(GetPageTrackContentInput input)
{
var vo = ObjectMapper.Map<GetPageTrackContentInput, GetPageTrackContentVO>(input);
var resultDto = await _trackContentRepository.GetPageTrackContent(vo);
@ -181,6 +195,205 @@ public class TrackContentService : ApplicationService
return true;
}
/// <summary>
/// 根据轨迹内容code获取轨迹内容
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("GetTrackContentByCode")]
public async Task<UnifyResultDto<GetTrackContentByCodeOutput>> GetTrackContentByCodeAsync([FromBody] GetTrackContentByCodeInput input)
{
if (input.SystemId <= 0)
return new UnifyResultDto<GetTrackContentByCodeOutput>(false, "子系统Id必须大于等于0");
if (string.IsNullOrEmpty(input.Code))
return new UnifyResultDto<GetTrackContentByCodeOutput>(false, "轨迹内容code不能为空");
var content = await _trackContentRepository.GetByCodeAsync(input.SystemId, input.Code);
if (content == null)
return new UnifyResultDto<GetTrackContentByCodeOutput>(false, "在当前子系统中未找到指定code的轨迹内容");
var res = new UnifyResultDto<GetTrackContentByCodeOutput>(true)
{
Data = new GetTrackContentByCodeOutput
{
Content = content.Content,
EnContent = content.EnContent,
IsExternal = content.IsExternal
}
};
return res;
}
/// <summary>
/// 根据子系统id、单号、订单类型、轨迹发生时间、轨迹code获取轨迹内容
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("GetContentDetail")]
public async Task<UnifyResultDto<GetContentDetailOutput>> GetContentDetail([FromBody] GetContentDetailInput input)
{
if (input.SystemId <= 0)
return new UnifyResultDto<GetContentDetailOutput>(false, "子系统Id必须大于等于0");
if (string.IsNullOrEmpty(input.TrackCode))
return new UnifyResultDto<GetContentDetailOutput>(false, "轨迹内容code不能为空");
if (string.IsNullOrEmpty(input.OrderNumber))
return new UnifyResultDto<GetContentDetailOutput>(false, "订单code不能为空");
if (input.OrderType <= 0)
return new UnifyResultDto<GetContentDetailOutput>(false, "订单类型不能为空");
if (input.TrackTime == default)
return new UnifyResultDto<GetContentDetailOutput>(false, "轨迹发生时间不能为空");
var content = await _trackDetailsEsRepository.GetDetailAsync(input.OrderNumber, input.TrackCode, input.OrderType, input.SystemId, input.TrackTime);
if (content == default)
return new UnifyResultDto<GetContentDetailOutput>(false, "在当前子系统中未找到指定code的轨迹内容");
var res = new UnifyResultDto<GetContentDetailOutput>(true)
{
Data = ObjectMapper.Map<TrackDetailsIndex, GetContentDetailOutput>(content)
};
if (res.Data != default && res.Data.Files.Any())
{
foreach (var item in res.Data.Files)
{
item.Url = OSSHelper.GetUrlByKey(item.OssKey);
}
}
return res;
}
/// <summary>
/// 创建轨迹时获取可用的轨迹内容
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("GetAvailableContent")]
public async Task<UnifyResultDto<List<GetAvailableContentOutput>>> GetAvailableContent(GetAvailableContentInput input)
{
if (input.SystemId <= 0)
return new UnifyResultDto<List<GetAvailableContentOutput>>(false, "系统id需大于0!");
if (string.IsNullOrEmpty(input.OrderNo))
return new UnifyResultDto<List<GetAvailableContentOutput>>(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<List<GetAvailableContentOutput>>(true)
{
Data = new List<GetAvailableContentOutput>()
};
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<GetAvailableContentOutput>();
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<UnifyResultDto> AddAsync(EditTrackContentInput input)

View File

@ -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;
}
/// <summary>
/// 按时间轴获取轨迹内容
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("GetDetailTimeline")]
public async Task<UnifyResultDto<List<GetDetailTimelineOutput>>> GetDetailTimeline(GetDetailTimelineInput input)
{
var res = new UnifyResultDto<List<GetDetailTimelineOutput>>(true);
var order = await _orderRepository.GetFirstAsync(m => m.OrderNo == input.OrderNumber);
if (order == null)
return new UnifyResultDto<List<GetDetailTimelineOutput>>(false, "订单不存在");
if (input.SystemId <= 0)
return new UnifyResultDto<List<GetDetailTimelineOutput>>(false, "子系统id不合法");
if (input.CurrenUserSystemId <= 0)
return new UnifyResultDto<List<GetDetailTimelineOutput>>(false, "当前登陆用户子系统id不合法");
var system = await _orderRepository.GetSystemInfoById(input.CurrenUserSystemId);
if (system == null)
return new UnifyResultDto<List<GetDetailTimelineOutput>>(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<GetDetailTimelineOutput>();
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<TrackFileItem>()
: 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";
}
}
/// <summary>
/// 根据轨迹id删除单条轨迹
/// </summary>

View File

@ -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;
/// </summary>
[ApiExplorerSettings(GroupName = "轨迹推送")]
[Route("/Track/PushConfig")]
public class TrackPushService : ApplicationService
public class TrackPushService : ApplicationService, ISingletonDependency
{
private readonly IStringLocalizer<TrackLocalizationResource> _stringLocalizer;
private readonly ITrackPushCacheService _trackPushCacheService;

View File

@ -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<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<SjzyTrackApplicationModule>("SJZY.Track.Application");
});
Configure<AbpVirtualFileSystemOptions>(options => { options.FileSets.AddEmbedded<SjzyTrackApplicationModule>("SJZY.Track.Application"); });
//添加多语言
var languageConfig = configuration.GetSection("Language");
@ -81,45 +80,59 @@ public class SjzyTrackApplicationModule : AbpModule
//CAP
context.Services.AddSingleton<ISubscribeExecutor, MySubscribeExecutor>();
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<int>("RabbitMQ:Port");
configure.BasicQosOptions =
new RabbitMQOptions.BasicQos(configuration.GetValue<ushort>("CAP:PrefetchCount"));
});
options.FailedRetryInterval = configuration.GetValue<int>("CAP:FailedRetryInterval");
options.FailedRetryCount = configuration.GetValue<int>("CAP:FailedRetryCount");
options.SucceedMessageExpiredAfter = configuration.GetValue<int>("CAP:SucceedMessageExpiredAfter");
options.FallbackWindowLookbackSeconds =
configuration.GetValue<int>("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<int>("RabbitMQ:Port");
configure.BasicQosOptions =
new RabbitMQOptions.BasicQos(configuration.GetValue<ushort>("CAP:PrefetchCount"));
});
options.FailedRetryInterval = configuration.GetValue<int>("CAP:FailedRetryInterval");
options.FailedRetryCount = configuration.GetValue<int>("CAP:FailedRetryCount");
options.SucceedMessageExpiredAfter = configuration.GetValue<int>("CAP:SucceedMessageExpiredAfter");
options.FallbackWindowLookbackSeconds =
configuration.GetValue<int>("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<CapFilter>();
}
catch (Exception ex)
{
LogHelper.Error("【严重】CAP失败回调事件发生未处理异常" + ex);
}
};
}).AddSubscribeFilter<CapFilter>();
}
else
{
//是本地调试环境,用内存存储和内存队列
context.Services.AddCap(capOptions =>
{
capOptions.UseInMemoryStorage();
capOptions.UseInMemoryMessageQueue();
capOptions.UseDashboard();
}).AddSubscribeFilter<CapFilter>();
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<int>("RabbitMQ:PoolSize");
options.Declarations = new List<Action<IDeclaration>>()
{
o=>o.ExchangeDeclare(GlobalConst.TrackChangeExchange, ExchangeType.Topic, true),
};
options.Consumes = new List<ConsumeOptions>
{
new()
@ -148,6 +165,16 @@ public class SjzyTrackApplicationModule : AbpModule
context.Services.Configure<TrackAppOptions>(configuration.GetSection("TrackApp"));
context.Services.Configure<InterfaceRelayParamsConfigOptions>(
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)

View File

@ -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<OrderEntity>
/// <returns></returns>
Task<UnifyPageResultDto<TrackOrderVO>> GetTrackOrderPagedList(GetPageTrackOrderInputVO input);
/// <summary>
/// 通过三方平台和三方单号查询订单
/// </summary>
/// <param name="serviceProvidrCode">三方平台</param>
/// <param name="orderNo">三方平台单号</param>
/// <returns>(主单号id,客户id)</returns>
Task<(int MainOrderId, int CustomId)> GetTrackOrderByProviderAndOrderNo(string serviceProvidrCode, string orderNo);
/// <summary>
/// 创建订单
/// </summary>
@ -131,4 +140,18 @@ public interface IOrderRepository : IBasicRepository<OrderEntity>
Task<int> UpdateOrderPullConfigForServiceProviderAsync(UpdateOrderPullConfigsInputVo inputVo);
#endregion
/// <summary>
/// 根据id获取子系统信息
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
Task<VSystemEntity> GetSystemInfoById(int id);
/// <summary>
/// 根据id获取员工姓名
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
Task<VUserEntity> GetUserById(int id);
}

View File

@ -168,10 +168,31 @@ public class OrderEntity : IFullAuditedBasicEntitys, IAggregateRoot
[SugarColumn(ColumnName = "ArrivalProvince")]
public string? ArrivalProvince { get; set; }
/// <summary>
/// 预计到达时间
/// </summary>
[SugarColumn(ColumnName = "EstimatedDeliveryDate")]
public DateTime? EstimatedDeliveryDate { get; set; }
/// <summary>
/// 目的地时区
/// </summary>
[SugarColumn(ColumnName = "DestinationTimeZone")]
public float? DestinationTimeZone { get; set; }
/// <summary>
/// 模板code
/// </summary>
[SugarColumn(ColumnName = "TemplateCodes")]
public string? TemplateCodes { get; set; }
[SugarColumn(IsIgnore = true)] public List<OrderFlightEntity> OrderFlights { get; set; }
[SugarColumn(IsIgnore = true)] public List<OrderPullStatusEntity> OrderPullStatus { get; set; }
[Navigate(NavigateType.OneToMany, nameof(OrderPlatformNumberEntity.OrderId))]
[SugarColumn(IsIgnore = true)] public List<OrderPlatformNumberEntity> OrderPlatformNumbers { get; set; }
public void ResetTrackInfo()
{
IsCompleted = false;

View File

@ -40,8 +40,22 @@ public class OrderOperationRecordIndex : IdBaseIndex
/// </summary>
public int CreateUserId { get; set; }
/// <summary>
/// 创建人姓名
/// </summary>
public string CreateUserName { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreateTime { get; set; }
}
public enum OrderOperationType
{
创建订单 = 1,
修改订单 = 2,
创建轨迹 = 3,
修改轨迹 = 4,
删除轨迹 = 5
}

View File

@ -0,0 +1,32 @@
namespace SJZY.Track.Domain.Aggregates.OrderAggregate;
/// <summary>
/// 外部单号关联表
/// </summary>
[SugarTable("t_order_platform_number")]
public class OrderPlatformNumberEntity : IFullAuditedBasicEntitys, IEntity
{
/// <summary>
/// 自增主键
/// </summary>
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)]
public int Id { get; set; }
/// <summary>
/// 订单id
/// </summary>
[SugarColumn(ColumnName = "OrderId")]
public int OrderId { get; set; }
/// <summary>
/// 外部单号
/// </summary>
[SugarColumn(ColumnName = "OrderNumber")]
public string? OrderNumber { get; set; }
/// <summary>
/// 平台名称
/// </summary>
[SugarColumn(ColumnName = "Platform")]
public string? Platform { get; set; }
}

View File

@ -18,6 +18,12 @@ public class OrderPullStatusEntity : ICreateBaicEntity, IUpdateBasicEntity, IEnt
[SugarColumn(ColumnName = "OrderId")]
public int OrderId { get; set; }
/// <summary>
/// api服务商订单号(第三方单号)
/// </summary>
[SugarColumn(ColumnName = "OrderNo")]
public string? OrderNo { get; set; }
/// <summary>
/// 服务商code
/// </summary>

View File

@ -26,4 +26,7 @@ public interface ITrackContentRepository : IBasicRepository<TrackContentEntity>
Expression<Func<TrackContentMapEntity, bool>> whereExpression);
Task<bool> GetTemplateIsExist(int trackContentId);
Task<TrackContentEntity> GetByCodeAsync(int systemId, string code);
Task<List<TrackContentEntity>> GetListBySystemIdAsync(int systemId);
Task<List<TrackContentVO>> GetTrackContentBySyetemIdAsync(int subSystemId);
}

View File

@ -36,6 +36,12 @@ public class TrackContentEntity : IFullAuditedBasicEntitys, IAggregateRoot
[SugarColumn(ColumnName = "SubSystemId")]
public int SubSystemId { get; set; }
/// <summary>
/// 子系统名称
/// </summary>
[SugarColumn(IsIgnore = true)]
public string SubSystem { get; set; }
/// <summary>
/// 轨迹标记id,多个逗号分隔
/// </summary>
@ -53,4 +59,22 @@ public class TrackContentEntity : IFullAuditedBasicEntitys, IAggregateRoot
/// </summary>
[SugarColumn(ColumnName = "IsExternal")]
public bool IsExternal { get; set; }
/// <summary>
/// 是否可重复
/// </summary>
[SugarColumn(ColumnName = "IsRepeatable")]
public bool IsRepeatable { get; set; }
/// <summary>
/// 是否允许手动修改
/// </summary>
[SugarColumn(ColumnName = "IsAllowManuallyUpdate")]
public bool IsAllowManuallyUpdate { get; set; }
/// <summary>
/// 是否允许手动创建
/// </summary>
[SugarColumn(ColumnName = "IsAllowManuallyCreate")]
public bool IsAllowManuallyCreate { get; set; }
}

View File

@ -0,0 +1,14 @@
namespace SJZY.Track.Domain.Aggregates.TrackDetailsAggregate;
public class FileItem
{
/// <summary>
/// 文件osskey
/// </summary>
public string OssKey { get; set; }
/// <summary>
/// 文件名称
/// </summary>
public string FileName { get; set; }
}

View File

@ -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<TrackDe
Task UpdateManyAsync(List<TrackDetailsIndex> indexes, int maxRetries = 3);
Task BulkUpdateAsync(List<TrackDetailsIndex> indexes, int maxRetries = 3);
Task AddManyAsync(List<TrackDetailsIndex> index);
Task AddAsync(TrackDetailsIndex track);
Task<List<TrackDetailsIndex>> GetListAsync(int orderId);
Task<List<TrackDetailsIndex>> GetListAsync(int[] orderIds, List<string>? trackCodes = null, TrackSourceEnum? source = null);
Task<List<int>> DeleteTrackAsync(int[] orderIds, List<string> trackCodes);
Task DeleteTrackByIdAsync(string id, string indexName);
Task<List<TrackDetailsIndex>> GetDataByIds(string[] ids);
Task<TrackDetailsIndex> GetDetailAsync(string orderNo, string trackCode, int orderType, int subSystemId, DateTime trackTime);
}

View File

@ -37,6 +37,11 @@ public class TrackDetailsIndex : IdBaseIndex
/// </summary>
public string TrackStatus { get; set; } = GlobalConst.EmptyString;
/// <summary>
/// 轨迹状态code
/// </summary>
public string TrackStatusCode { get; set; } = GlobalConst.EmptyString;
/// <summary>
/// 轨迹内容代码
/// </summary>
@ -106,4 +111,69 @@ public class TrackDetailsIndex : IdBaseIndex
/// 修改时间
/// </summary>
public DateTime? UpdateTime { get; set; }
/// <summary>
/// 客户是否可见
/// </summary>
public bool IsVisibility { get; set; }
/// <summary>
/// 数据来源:1外部服务商创建,2内部系统创建,3人工创建
/// </summary>
public int Source { get; set; }
/// <summary>
/// 备注
/// </summary>
public string? Remarks { get; set; } = GlobalConst.EmptyString;
/// <summary>
/// 附件集合
/// </summary>
public List<FileItem>? Files { get; set; }
/// <summary>
/// 轨迹发生地(城市)
/// </summary>
public string? TrackPlaceCity { get; set; } = GlobalConst.EmptyString;
/// <summary>
/// 轨迹发生地(城市)Id
/// </summary>
public int? TrackPlaceCityId { get; set; } = GlobalConst.NotExistId;
/// <summary>
/// 轨迹发生地(省/州)
/// </summary>
public string? TrackPlaceProvince { get; set; } = GlobalConst.EmptyString;
/// <summary>
///轨迹发生地(省/州)Id
/// </summary>
public int? TrackPlaceProvinceId { get; set; } = GlobalConst.NotExistId;
/// <summary>
/// 轨迹发生地(国家/地区)
/// </summary>
public string? TrackPlaceNation { get; set; } = GlobalConst.EmptyString;
/// <summary>
/// 轨迹发生地(国家/地区)Id
/// </summary>
public int? TrackPlaceNationId { get; set; } = GlobalConst.NotExistId;
/// <summary>
/// 轨迹发生地邮编
/// </summary>
public string? TrackPlacePostcode { get; set; } = GlobalConst.EmptyString;
/// <summary>
/// 预计签收时间
/// </summary>
public DateTime? EstimatedDeliveryDate { get; set; }
/// <summary>
/// 签收地时区
/// </summary>
public float? DestinationTimeZone { get; set; }
}

View File

@ -45,4 +45,5 @@ public interface ITrackTemplateRepository : IBasicRepository<TrackTemplateEntity
Expression<Func<TrackTemplateContentEntity, bool>> whereExpression);
Task<List<TrackTemplateOutputVo>> GetTrackTemplateListAsync(GetTrackTemplateListInputVo input);
Task<List<string>> GetContentByTemplateCodes(string[] codes, int systemId);
}

View File

@ -51,4 +51,11 @@ public partial class GlobalConst
/// 预警计算任务
/// </summary>
public const string TrackWarningCalcSubscribe = "SJZY.Track.TrackWarningCalc";
/// <summary>
/// 轨迹变更通知交换机
/// </summary>
public const string TrackChangeExchange = "sjzy.track.on_track_change_ex";
}

View File

@ -0,0 +1,11 @@
using System.Collections.Immutable;
namespace SJZY.Track.Domain.Consts;
public partial class GlobalConst
{
/// <summary>
/// 轨迹数据来源:1外部服务商创建,2内部系统创建,3人工创建
/// </summary>
public static readonly ImmutableHashSet<int> SupportTrackSources = ImmutableHashSet.Create(1, 2, 3);
}

View File

@ -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;

View File

@ -68,6 +68,7 @@ public class TrackOrderVO
/// </summary>
public string? OrderNumber { get; set; }
/// <summary>
/// 客户简称
/// </summary>
@ -137,4 +138,19 @@ public class TrackOrderVO
/// 问题类型名称
/// </summary>
public string? QuestionTypeName { get; set; }
/// <summary>
/// 预计到达时间
/// </summary>
public DateTime? EstimatedDeliveryDate { get; set; }
/// <summary>
/// 目的地时区
/// </summary>
public float? DestinationTimeZone { get; set; }
/// <summary>
/// 第三方单号,服务商-轨迹0000000、轨迹服务商-0000000
/// </summary>
public string? ThirdPartyOrderNumber { get; set; }
}

View File

@ -89,4 +89,19 @@ public class GetTrackContentListVO
/// 修改人
/// </summary>
public string UpdateUser { get; set; }
/// <summary>
/// 是否可重复
/// </summary>
public bool IsRepeatable { get; set; }
/// <summary>
/// 是否允许手动修改
/// </summary>
public bool IsAllowManuallyUpdate { get; set; }
/// <summary>
/// 是否允许手动创建
/// </summary>
public bool IsAllowManuallyCreate { get; set; }
}

View File

@ -56,4 +56,18 @@ public class GetTrackContentDetailVO
/// 是否对外
/// </summary>
public bool IsExternal { get; set; }
/// <summary>
/// 是否可重复
/// </summary>
public bool IsRepeatable { get; set; }
/// <summary>
/// 是否允许手动修改
/// </summary>
public bool IsAllowManuallyUpdate { get; set; }
/// <summary>
/// 是否允许手动创建
/// </summary>
public bool IsAllowManuallyCreate { get; set; }
}

View File

@ -0,0 +1,27 @@
namespace SJZY.Track.Domain.Dtos.TrackContents;
/// <summary>
/// 轨迹代码映射
/// </summary>
public sealed class TrackCodeMapVO
{
/// <summary>
/// 源子系统id
/// </summary>
public int SubSystemId { get; set; }
/// <summary>
/// 源轨迹内容code
/// </summary>
public string TrackContentCode { get; set; } = null!;
/// <summary>
/// 映射子系统id
/// </summary>
public int MapSubSystemId { get; set; }
/// <summary>
/// 映射轨迹内容code
/// </summary>
public string MapTrackContentCode { get; set; } = null!;
}

View File

@ -31,4 +31,34 @@ public class TrackContentVO
/// 是否完成态
/// </summary>
public bool IsCompleted { get; set; }
/// <summary>
/// 轨迹状态代码
/// </summary>
public string TrackStatusCode { get; set; }
/// <summary>
/// 是否可重复
/// </summary>
public bool IsRepeatable { get; set; }
/// <summary>
/// 是否允许手动修改
/// </summary>
public bool IsAllowManuallyUpdate { get; set; }
/// <summary>
/// 是否允许手动创建
/// </summary>
public bool IsAllowManuallyCreate { get; set; }
/// <summary>
/// 是否广播
/// </summary>
public bool IsToMq { get; set; }
/// <summary>
/// 轨迹内容code
/// </summary>
public string TrackContentCode { get; set; }
}

View File

@ -0,0 +1,24 @@
using System.ComponentModel;
namespace SJZY.Track.Domain.Enums;
/// <summary>
/// 轨迹来源
/// </summary>
public enum TrackSourceEnum
{
/// <summary>
/// 外部服务商创建
/// </summary>
[Description("外部服务商创建")] ExternalServiceProvide = 1,
/// <summary>
/// 内部系统创建
/// </summary>
[Description("内部系统创建")] InternalService = 2,
/// <summary>
/// 人工创建
/// </summary>
[Description("人工创建")] Artificial = 3,
}

View File

@ -18,6 +18,14 @@ public interface IProvideQueries
/// <returns></returns>
Task<List<string>> MapTrackCodesAsync(int upSubSystemId, int subSystemId, params string[] trackCodes);
/// <summary>
/// 获取子系统内容Code映射
/// </summary>
/// <param name="upSubSystemId"></param>
/// <param name="subSystemId"></param>
/// <param name="trackCodes"></param>
/// <returns></returns>
Task<List<TrackCodeMapVO>> GetTrackCodeMapsAsync(int upSubSystemId, int subSystemId, params string[] trackCodes);
/// <summary>
/// 根据订单模板找对应服务商
/// </summary>

View File

@ -33,6 +33,22 @@ public class ProvideQueries : ISugarRepository, IProvideQueries, ITransientDepen
.ToListAsync();
}
public async Task<List<TrackCodeMapVO>> GetTrackCodeMapsAsync(int upSubSystemId, int subSystemId, params string[] trackCodes)
{
// 根据上游code得到下游code
return await Context.Queryable<TrackContentMapEntity>()
.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();
}
/// <inheritdoc />
public async Task<List<OrderWithServiceProviderVO>> FindOrderWithServiceProviderAsync(int subSystemId,
List<string> 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();
}

View File

@ -12,11 +12,14 @@ public class OrderRepository : BasicRepository<OrderEntity>, IOrderRepository, I
{
public async Task<UnifyPageResultDto<TrackOrderVO>> GetTrackOrderPagedList(GetPageTrackOrderInputVO input)
{
//查询完去重
var pageList = await Context.Queryable<OrderEntity>()
.Includes(a => a.OrderPlatformNumbers)
.LeftJoin<VExternalSubjectEntity>((a, b) => a.CustomId == b.Id && b.IsDeleted == false)
.LeftJoin<VSystemEntity>((a, b, c) => a.SubSystemId == c.Id && c.IsDeleted == false)
.LeftJoin<OrderPlatformNumberEntity>((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<OrderEntity>, 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<OrderEntity>, 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<OrderEntity>()
.LeftJoin<OrderPlatformNumberEntity>((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<int> CreateOrderAsync(OrderEntity order)
{
using var tran = TransactionHelper.GetReadCommitted();
@ -60,6 +95,8 @@ public class OrderRepository : BasicRepository<OrderEntity>, 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<OrderEntity>, IOrderRepository, I
// 更新订单模板
await Context.Deleteable<OrderPullStatusEntity>().Where(x => x.OrderId == order.Id).ExecuteCommandAsync();
order.OrderPullStatus.ForEach(x => x.OrderId = order.Id);
await Context.Insertable(order.OrderPullStatus).ExecuteCommandAsync();
// 更新订单三方关联
await Context.Deleteable<OrderPlatformNumberEntity>().Where(x => x.OrderId == order.Id).ExecuteCommandAsync();
order.OrderPlatformNumbers.ForEach(x => x.OrderId = order.Id);
await Context.Insertable(order.OrderPlatformNumbers).ExecuteCommandAsync();
// 更新订单航班
await Context.Deleteable<OrderFlightEntity>().Where(x => x.OrderId == order.Id).ExecuteCommandAsync();
@ -163,6 +207,16 @@ public class OrderRepository : BasicRepository<OrderEntity>, IOrderRepository, I
return res.Select(x => (x.Key, x.Id)).ToList();
}
public Task<VSystemEntity> GetSystemInfoById(int id)
{
return Context.Queryable<VSystemEntity>().FirstAsync(m => m.IsDeleted == false && m.Id == id);
}
public Task<VUserEntity> GetUserById(int id)
{
return Context.Queryable<VUserEntity>().FirstAsync(m => m.IsDeleted == false && m.Id == id);
}
private int UpdateOrderQuestionType(int questionType, IEnumerable<int> ids)
{
var idsString = string.Join(",", ids);
@ -174,6 +228,21 @@ public class OrderRepository : BasicRepository<OrderEntity>, IOrderRepository, I
});
}
/// <summary>
/// 拼接多个三方单号,默认使用分隔符默认(、)
/// 第三方单号展示: 0000000(服务商)、0000000(服务商)
/// </summary>
/// <param name="orderPlatformNumberEntities"></param>
/// <returns></returns>
private static string BuildThirdPartyOrderNumber(List<OrderPlatformNumberEntity> 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<List<LinkedOrderVO>> GetLinkedOrdersAsync(int[] orderIds)

View File

@ -33,6 +33,9 @@ public class TrackContentRepository : BasicRepository<TrackContentEntity>, 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<TrackContentEntity>, 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<TrackContentEntity>, ITrac
return false;
}
public async Task<UnifyPageResultDto<GetTrackContentMapPageOutputVO>> GetMapPageAsync(
GetTrackContentMapPageInputVO input)
{
@ -220,4 +225,50 @@ public class TrackContentRepository : BasicRepository<TrackContentEntity>, ITrac
return await Context.Updateable<TrackContentMapEntity>().SetColumns(columns).Where(whereExpression)
.ExecuteCommandAsync() > 0;
}
public Task<TrackContentEntity> GetByCodeAsync(int systemId, string code)
{
return Context.Queryable<TrackContentEntity>().FirstAsync(m => m.SubSystemId == systemId && m.Code == code);
}
public Task<List<TrackContentEntity>> GetListBySystemIdAsync(int systemId)
{
return Context.Queryable<TrackContentEntity>()
.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<List<TrackContentVO>> GetTrackContentBySyetemIdAsync(int subSystemId)
{
return Context.Queryable<TrackContentEntity>()
.LeftJoin<TrackStatusEntity>((x, y) => x.TrackStatusId == y.Id)
.LeftJoin<VSystemEntity>((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();
}
}

View File

@ -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<TrackDetail
.Field(f => 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<TrackDetail
.Sort(so => 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<TrackDetail
track.TrackTimeZone,
track.UpdateUser,
UpdateTime = DateTime.Now,
track.TrackContent,
track.TrackEnContent,
track.IsVisibility,
track.Remarks,
track.Files,
track.TrackPlaceCity,
track.TrackPlaceCityId,
track.TrackPlaceProvince,
track.TrackPlaceProvinceId,
track.TrackPlaceNation,
track.TrackPlaceNationId,
track.TrackPlacePostcode,
track.DestinationTimeZone,
track.EstimatedDeliveryDate
})
);
@ -154,6 +183,7 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract<TrackDetail
}
}
//#4001批量更新时只会将所有id匹配的记录更新为同一内容,后续需要支持差异化更新
public async Task UpdateManyAsync(List<TrackDetailsIndex> indexes, int maxRetries = 3)
{
var retryCount = 0;
@ -174,6 +204,21 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract<TrackDetail
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)
@ -181,6 +226,21 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract<TrackDetail
.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)
)
)
);
@ -205,6 +265,83 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract<TrackDetail
}
}
public async Task BulkUpdateAsync(List<TrackDetailsIndex> 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<TrackDetailsIndex> tracks)
{
// 按天写入es
@ -298,6 +435,39 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract<TrackDetail
return r.ToIdList();
}
public async Task<List<TrackDetailsIndex>> GetListAsync(int[] orderIds, List<string>? trackCodes = null, TrackSourceEnum? source = null)
{
var query = new QueryContainer();
if (orderIds.Length > 0)
{
query &= Query<TrackDetailsIndex>.Terms(t => t.Field(f => f.OrderId).Terms(orderIds));
}
if (trackCodes != null && trackCodes.Count > 0)
{
query &= Query<TrackDetailsIndex>.Terms(t => t.Field(f => f.TrackCode.Suffix("keyword")).Terms(trackCodes));
}
if (source.HasValue)
{
query &= Query<TrackDetailsIndex>.Term(t => t.Field(f => f.Source).Value(source.Value));
}
var r = await Context.SearchAsync<TrackDetailsIndex>(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<List<int>> DeleteTrackAsync(int[] orderIds, List<string> trackCodes)
{
// var deleteResponse = await Context.DeleteByQueryAsync<TrackDetailsIndex>(d => d
@ -334,6 +504,7 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract<TrackDetail
$"轨迹查询失败,orderIds:[{orderIds.JoinAsString(", ")}],trackCodes:[{trackCodes.JoinAsString(", ")}],原因:{search.DebugInformation}。");
}
//按订单id和code分组,只删除组元素数量为1的轨迹
var tracks = search.ToIdList().GroupBy(doc => new
{
doc.OrderId,
@ -351,6 +522,7 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract<TrackDetail
return tracks.Select(x => x.OrderId).ToList();
}
public async Task DeleteTrackByIdAsync(string id, string indexName)
{
var r = await Context.DeleteAsync<TrackDetailsIndex>(id, u => u
@ -362,4 +534,49 @@ public class TrackDetailsEsRepository : ElasticSearchContextAbstract<TrackDetail
throw new InvalidOperationException($"轨迹删除失败,id:{indexName},原因:{r.DebugInformation}。");
}
}
public async Task<List<TrackDetailsIndex>> GetDataByIds(string[] ids)
{
var res = await Context.SearchAsync<TrackDetailsIndex>(m => m
.Index(GlobalConst.TrackDetailsIndex)
.Query(
q => q.Ids(i => i.Values(ids))
));
return res.ToIdList();
}
public async Task<TrackDetailsIndex> GetDetailAsync(string orderNo, string trackCode, int orderType, int subSystemId, DateTime trackTime)
{
var res = await Context.SearchAsync<TrackDetailsIndex>(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();
}
}

View File

@ -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<TrackTemplateEntity>, ITr
return templates;
}
public async Task<List<string>> 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<string>(sql, new {codes = codes}).ToList();
return list;
}
}