diff --git a/src/SJZY.Track.Application.Contract/Dtos/TrackPull/EditTrackPullContentInput.cs b/src/SJZY.Track.Application.Contract/Dtos/TrackPull/EditTrackPullContentInput.cs
index 1342b38b..24882e20 100644
--- a/src/SJZY.Track.Application.Contract/Dtos/TrackPull/EditTrackPullContentInput.cs
+++ b/src/SJZY.Track.Application.Contract/Dtos/TrackPull/EditTrackPullContentInput.cs
@@ -52,8 +52,14 @@ public class EditTrackPullContentInput
///
/// 轨迹时区GMT
///
+ [Obsolete]
public double? TrackTimeZone { get; set; }
+ ///
+ /// 兜底轨迹时区发生地
+ ///
+ public int? TrackTimePlaceValue { get; set; }
+
///
/// 是否完结
///
diff --git a/src/SJZY.Track.Application.Contract/Dtos/TrackPull/TrackPullContentConfigDto.cs b/src/SJZY.Track.Application.Contract/Dtos/TrackPull/TrackPullContentConfigDto.cs
index 771b328c..a7f2c500 100644
--- a/src/SJZY.Track.Application.Contract/Dtos/TrackPull/TrackPullContentConfigDto.cs
+++ b/src/SJZY.Track.Application.Contract/Dtos/TrackPull/TrackPullContentConfigDto.cs
@@ -37,13 +37,13 @@ public class TrackPullContentConfigDto
///
public int? TrackPlaceValue { get; set; }
- ///
- /// 轨迹时区GMT值
- ///
- public float? TrackTimeZoneValue { get; set; }
-
///
/// 订单是否完成(由轨迹刷新配置决定)
///
public bool IsCompleted { get; set; }
+
+ ///
+ /// 轨迹时区发生地参数值
+ ///
+ public int? TrackZonePlaceValue { get; set; }
}
\ No newline at end of file
diff --git a/src/SJZY.Track.Application.Contract/Events/PullChangeBottomLineUpdateOrderEvent.cs b/src/SJZY.Track.Application.Contract/Events/PullChangeBottomLineUpdateOrderEvent.cs
new file mode 100644
index 00000000..ed59fce6
--- /dev/null
+++ b/src/SJZY.Track.Application.Contract/Events/PullChangeBottomLineUpdateOrderEvent.cs
@@ -0,0 +1,11 @@
+namespace SJZY.Track.Application.Contract.Events;
+
+public class PullChangeBottomLineUpdateOrderEvent
+{
+ public List OrderIds { get; set; } = null!;
+
+ ///
+ /// 时区缺失和发生地缺失标记,1表示时区,2表示发生地,3表示两种
+ ///
+ public int DeficiencyTagValue { get; set; }
+}
\ No newline at end of file
diff --git a/src/SJZY.Track.Application/CAP/EventHandlers/PullChangeBottomLineUpdateOrderEventHandler.cs b/src/SJZY.Track.Application/CAP/EventHandlers/PullChangeBottomLineUpdateOrderEventHandler.cs
new file mode 100644
index 00000000..0365727d
--- /dev/null
+++ b/src/SJZY.Track.Application/CAP/EventHandlers/PullChangeBottomLineUpdateOrderEventHandler.cs
@@ -0,0 +1,23 @@
+using DotNetCore.CAP;
+using SJZY.Track.Application.Contract.Events;
+using SJZY.Track.Domain.Aggregates.OrderAggregate;
+using SJZY.Track.Domain.Consts;
+using Volo.Abp.DependencyInjection;
+
+namespace SJZY.Track.Application.CAP.EventHandlers;
+
+public class PullChangeBottomLineUpdateOrderEventHandler :ITransientDependency, ICapSubscribe
+{
+ private readonly IOrderRepository _orderRepository;
+
+ public PullChangeBottomLineUpdateOrderEventHandler(IOrderRepository orderRepository)
+ {
+ _orderRepository = orderRepository;
+ }
+
+ [CapSubscribe(GlobalConst.PullChangeBottomLineUpdateOrderSubscribe)]
+ public async Task ConsumerAsync(PullChangeBottomLineUpdateOrderEvent @event)
+ {
+ await _orderRepository.UpdateBatchIdsAsync(@event.OrderIds, @event.DeficiencyTagValue);
+ }
+}
\ No newline at end of file
diff --git a/src/SJZY.Track.Application/CAP/EventHandlers/TrackRefreshEventHandler.cs b/src/SJZY.Track.Application/CAP/EventHandlers/TrackRefreshEventHandler.cs
index f982343e..26380b66 100644
--- a/src/SJZY.Track.Application/CAP/EventHandlers/TrackRefreshEventHandler.cs
+++ b/src/SJZY.Track.Application/CAP/EventHandlers/TrackRefreshEventHandler.cs
@@ -12,6 +12,7 @@ using SJZY.Track.Domain.Aggregates.OrderAggregate;
using SJZY.Track.Domain.Aggregates.TrackDetailsAggregate;
using SJZY.Track.Domain.Consts;
using SJZY.Track.Domain.Enums;
+using SJZY.Track.Domain.Queries;
using Volo.Abp.DependencyInjection;
namespace SJZY.Track.Application.CAP.EventHandlers;
@@ -22,15 +23,17 @@ public class TrackRefreshEventHandler : ITransientDependency, ICapSubscribe
private readonly ICapPublisher _capPublisher;
private readonly ITrackPullCacheService _trackPullCacheService;
private readonly ITrackDetailsRepository _trackDetailsRepository;
+ private readonly ITimezoneQueries _timezoneQueries;
public TrackRefreshEventHandler(IOrderRepository orderRepository,
ICapPublisher capPublisher, ITrackPullCacheService trackPullCacheService,
- ITrackDetailsRepository trackDetailsRepository)
+ ITrackDetailsRepository trackDetailsRepository, ITimezoneQueries timezoneQueries)
{
_orderRepository = orderRepository;
_capPublisher = capPublisher;
_trackPullCacheService = trackPullCacheService;
_trackDetailsRepository = trackDetailsRepository;
+ _timezoneQueries = timezoneQueries;
}
[CapSubscribe(GlobalConst.TrackRefreshSubscribe, Group = GlobalConst.TrackRefreshSubscribe)]
@@ -172,21 +175,12 @@ public class TrackRefreshEventHandler : ITransientDependency, ICapSubscribe
var trackPlace = paramConfig.TrackPlaceName == null
? null
: trackToken[paramConfig.TrackPlaceName]?.Value();
- if (trackPlace.IsNullOrWhiteSpace())
+ var hasPlaceBottomLine = false; //执行了兜底但是没有兜底住,需要记录
+ if (trackPlace.IsNullOrWhiteSpace() && matchedContentConfig?.TrackPlaceValue != null && matchedContentConfig.TrackPlaceValue.HasValue)
{
- Expression> resultExpression =
- (TrackLocationValue?)matchedContentConfig?.TrackPlaceValue switch
- {
- TrackLocationValue.ArrivalPort => x => x.ArrivalPort,
- TrackLocationValue.ArriveCity => x => x.ArrivalCity,
- TrackLocationValue.ArrivalCountry => x => x.ArrivalCountry,
- TrackLocationValue.LeavePort => x => x.LeavePort,
- TrackLocationValue.LeaveCity => x => x.LeaveCity,
- TrackLocationValue.LeaveCountry => x => x.LeaveCountry,
- _ => x => x.LeaveCountry
- };
- trackPlace = await _orderRepository.GetFirstAsync(x =>
- x.Id == context.OrderId, resultExpression);
+ //兜底没配置的就不需要兜底,拉取的发生地没就没
+ trackPlace = await GetPlaceByOrder(matchedContentConfig!.TrackPlaceValue.Value,context.OrderId);
+ hasPlaceBottomLine = string.IsNullOrWhiteSpace(trackPlace);
}
// 轨迹发生时间
@@ -198,6 +192,17 @@ public class TrackRefreshEventHandler : ITransientDependency, ICapSubscribe
? null
: trackToken[paramConfig.TrackTimeZoneName]?.Value();
+ //兜底时区
+ var hasTimezoneBottomLine = false; //执行了兜底但是没有兜底住,需要记录
+ if (!trackTimeZone.HasValue && matchedContentConfig?.TrackZonePlaceValue != null && matchedContentConfig.TrackZonePlaceValue.HasValue)
+ {
+ //兜底没配置的就不需要兜底,拉取的发生地没就没
+ var timezone= await GetTimezone(matchedContentConfig,context.OrderId);
+
+ trackTimeZone = timezone;
+ hasTimezoneBottomLine = !timezone.HasValue;
+ }
+
if (matchedContentConfig is null)
{
var notMappedEvent = new TrackInsertForNotMappedEvent
@@ -209,7 +214,7 @@ public class TrackRefreshEventHandler : ITransientDependency, ICapSubscribe
OriginTrackData = JsonConvert.SerializeObject(trackToken),
TrackPlace = trackPlace ?? GlobalConst.EmptyString,
TrackTime = trackTime ?? DateTime.Now,
- TrackTimeZone = trackTimeZone ?? matchedContentConfig?.TrackTimeZoneValue ?? 8
+ TrackTimeZone = trackTimeZone ?? 8
};
if (paramConfig.TrackContentName != null)
{
@@ -246,11 +251,74 @@ public class TrackRefreshEventHandler : ITransientDependency, ICapSubscribe
TrackCode = matchedContentConfig.TrackContentCode,
TrackPlace = trackPlace ?? GlobalConst.EmptyString,
TrackTime = trackTime ?? DateTime.Now,
- TrackTimeZone = trackTimeZone ?? matchedContentConfig.TrackTimeZoneValue ?? 8
+ TrackTimeZone = trackTimeZone ?? 8
};
trackUpsertEvent.IsAdd =
!await _trackDetailsRepository.AnyByCodeAsync(context.OrderId, matchedContentConfig.TrackContentCode);
await _capPublisher.PublishAsync(GlobalConst.TrackUpsertForRefreshSubscribe, trackUpsertEvent);
+
+ // 触发了兜底,但是没有兜底住,对订单进行标记
+ if (hasPlaceBottomLine || hasTimezoneBottomLine)
+ {
+ //1表示时区,2表示发生地,3表示两种
+ var placeTagValue = hasPlaceBottomLine ? 2 : 0;
+ var timezoneTagValue = hasTimezoneBottomLine ? 1 : 0;
+ PullChangeBottomLineUpdateOrderEvent deficiencyTagEvent = new PullChangeBottomLineUpdateOrderEvent
+ {
+ OrderIds = new List(){context.OrderId},
+ DeficiencyTagValue = placeTagValue+timezoneTagValue
+ };
+ await _capPublisher.PublishAsync(GlobalConst.PullChangeBottomLineUpdateOrderSubscribe, deficiencyTagEvent);
+ }
+ }
+
+ ///
+ /// 根据订单id获取兜底位置
+ ///
+ ///
+ ///
+ ///
+ private Task GetPlaceByOrder(int trackValue,int orderId)
+ {
+ Expression> resultExpression =
+ (TrackLocationValue)trackValue switch
+ {
+ TrackLocationValue.ArrivalPort => x => x.ArrivalPort,
+ TrackLocationValue.ArriveCity => x => x.ArrivalCity,
+ TrackLocationValue.ArrivalCountry => x => x.ArrivalCountry,
+ TrackLocationValue.LeavePort => x => x.LeavePort,
+ TrackLocationValue.LeaveCity => x => x.LeaveCity,
+ TrackLocationValue.LeaveCountry => x => x.LeaveCountry,
+ _ => x => x.LeaveCountry
+ };
+ return _orderRepository.GetFirstAsync(x => x.Id == orderId, resultExpression);
+ }
+
+ ///
+ /// 获取时区
+ ///
+ ///
+ ///
+ ///
+ private async Task GetTimezone(TrackPullContentConfigDto matchedContentConfig,int orderId )
+ {
+ var placeOrCode = await GetPlaceByOrder(matchedContentConfig!.TrackZonePlaceValue.Value,orderId);
+ string timezone = null;
+ if (!string.IsNullOrEmpty(placeOrCode))
+ {
+
+ if (matchedContentConfig!.TrackZonePlaceValue.Value == (int)TrackLocationValue.ArrivalPort
+ || matchedContentConfig!.TrackZonePlaceValue.Value == (int)TrackLocationValue.LeavePort)
+ {
+ timezone = await _timezoneQueries.GetPortTimezone(placeOrCode);
+ return DateTimeHelper.ConvertTimeZoneToFloat(timezone);
+ }
+
+ timezone = await _timezoneQueries.GetCityTimezone(placeOrCode);
+ return DateTimeHelper.ConvertTimeZoneToFloat(timezone);
+ }
+
+ return null;
}
}
\ No newline at end of file
diff --git a/src/SJZY.Track.Application/Helpers/DateTimeHelper.cs b/src/SJZY.Track.Application/Helpers/DateTimeHelper.cs
index 907cab70..afc41d27 100644
--- a/src/SJZY.Track.Application/Helpers/DateTimeHelper.cs
+++ b/src/SJZY.Track.Application/Helpers/DateTimeHelper.cs
@@ -36,4 +36,25 @@ public static class DateTimeHelper
//时区是整数丢掉浮点数部分
return format.Replace("hh", ((int)timezone).ToString());
}
+
+ ///
+ /// 转换主数据中心类似GMT-09:00这种的时区为浮点数
+ ///
+ /// 分区
+ ///
+ ///
+ public static float? ConvertTimeZoneToFloat(string timeZone)
+ {
+ if (string.IsNullOrEmpty(timeZone)|| !timeZone.StartsWith("GMT"))
+ {
+ return null;
+ }
+
+ var sign = timeZone[3] == '+' ? 1 : -1;
+ var parts = timeZone.Substring(4).Split(':');
+ var hours = int.Parse(parts[0]);
+ var minutes = int.Parse(parts[1]);
+
+ return sign * (hours + minutes / 60f);
+ }
}
\ No newline at end of file
diff --git a/src/SJZY.Track.Domain/Aggregates/OrderAggregate/IOrderRepository.cs b/src/SJZY.Track.Domain/Aggregates/OrderAggregate/IOrderRepository.cs
index 78cdd5df..ff7ab6f7 100644
--- a/src/SJZY.Track.Domain/Aggregates/OrderAggregate/IOrderRepository.cs
+++ b/src/SJZY.Track.Domain/Aggregates/OrderAggregate/IOrderRepository.cs
@@ -31,6 +31,14 @@ public interface IOrderRepository : IBasicRepository
///
Task PushWarnOrdersAsync();
+ ///
+ /// 更新缺失标记,时区缺失和发生地缺失标记,1表示时区,2表示发生地,3表示两种
+ ///
+ /// 订单id
+ /// 值
+ ///
+ Task UpdateBatchIdsAsync(List ids, int newValue);
+
#region 订单关联
///
diff --git a/src/SJZY.Track.Domain/Aggregates/OrderAggregate/OrderEntity.cs b/src/SJZY.Track.Domain/Aggregates/OrderAggregate/OrderEntity.cs
index 9a7c4597..b04aa76a 100644
--- a/src/SJZY.Track.Domain/Aggregates/OrderAggregate/OrderEntity.cs
+++ b/src/SJZY.Track.Domain/Aggregates/OrderAggregate/OrderEntity.cs
@@ -149,6 +149,12 @@ public class OrderEntity : IFullAuditedBasicEntitys, IAggregateRoot
///
[SugarColumn(ColumnName = "ProductCode")]
public string? ProductCode { get; set; }
+
+ ///
+ /// 时区缺失和发生地缺失标记,1表示时区,2表示发生地,3表示两种
+ ///
+ [SugarColumn(ColumnName = "DeficiencyTag")]
+ public int? DeficiencyTag { get; set; }
[SugarColumn(IsIgnore = true)] public List OrderFlights { get; set; }
[SugarColumn(IsIgnore = true)] public List OrderTemplates { get; set; }
diff --git a/src/SJZY.Track.Domain/Aggregates/TrackPullAggregate/TrackPullContentConfigEntity.cs b/src/SJZY.Track.Domain/Aggregates/TrackPullAggregate/TrackPullContentConfigEntity.cs
index 5afef0fb..6e717032 100644
--- a/src/SJZY.Track.Domain/Aggregates/TrackPullAggregate/TrackPullContentConfigEntity.cs
+++ b/src/SJZY.Track.Domain/Aggregates/TrackPullAggregate/TrackPullContentConfigEntity.cs
@@ -71,4 +71,10 @@ public class TrackPullContentConfigEntity : IFullAuditedBasicEntitys, IEntity
///
[SugarColumn(ColumnName = "IsCompleted")]
public bool IsCompleted { get; set; }
+
+ ///
+ /// 轨迹时区发生地参数值
+ ///
+ [SugarColumn(ColumnName = "TrackZonePlaceValue")]
+ public int? TrackZonePlaceValue { get; set; }
}
\ No newline at end of file
diff --git a/src/SJZY.Track.Domain/Consts/GlobalConst.MQ.cs b/src/SJZY.Track.Domain/Consts/GlobalConst.MQ.cs
index 378f0b89..1b853ffe 100644
--- a/src/SJZY.Track.Domain/Consts/GlobalConst.MQ.cs
+++ b/src/SJZY.Track.Domain/Consts/GlobalConst.MQ.cs
@@ -56,4 +56,9 @@ public partial class GlobalConst
/// 关联订单后补轨迹
///
public const string OrderLinkAfterFillTrack = "SJZY.Track.OrderLinkAfterFillTrack";
+
+ ///
+ /// 拉取轨迹触发兜底后更新订单
+ ///
+ public const string PullChangeBottomLineUpdateOrderSubscribe = "SJZY.Track.PullChangeBottomLineUpdateOrder";
}
\ No newline at end of file
diff --git a/src/SJZY.Track.Domain/Consts/GlobalConst.Redis.cs b/src/SJZY.Track.Domain/Consts/GlobalConst.Redis.cs
index dcaf7a35..6f49c80d 100644
--- a/src/SJZY.Track.Domain/Consts/GlobalConst.Redis.cs
+++ b/src/SJZY.Track.Domain/Consts/GlobalConst.Redis.cs
@@ -36,4 +36,14 @@ public partial class GlobalConst
/// kpi区域redis key前缀
///
public const string KpiAreaKeyPrefix = "KpiArea";
+
+ ///
+ /// 城市时区redis key前缀
+ ///
+ public const string CityTimezoneKeyPrefix = "CityTimezone";
+
+ ///
+ /// 港口时区redis key前缀
+ ///
+ public const string PortTimezoneKeyPrefix = "PortTimezone";
}
\ No newline at end of file
diff --git a/src/SJZY.Track.Domain/Dtos/Common/CityTimezoneDto.cs b/src/SJZY.Track.Domain/Dtos/Common/CityTimezoneDto.cs
new file mode 100644
index 00000000..63cd5dad
--- /dev/null
+++ b/src/SJZY.Track.Domain/Dtos/Common/CityTimezoneDto.cs
@@ -0,0 +1,14 @@
+namespace SJZY.Track.Domain.Dtos.Common;
+
+public class CityTimezoneDto
+{
+ ///
+ /// 城市英文名称
+ ///
+ public string? CityEnName { get; set; }
+
+ ///
+ /// 时区
+ ///
+ public string Timezone { get; set; }
+}
diff --git a/src/SJZY.Track.Domain/Dtos/Common/PortTimezoneDto.cs b/src/SJZY.Track.Domain/Dtos/Common/PortTimezoneDto.cs
new file mode 100644
index 00000000..896981cb
--- /dev/null
+++ b/src/SJZY.Track.Domain/Dtos/Common/PortTimezoneDto.cs
@@ -0,0 +1,14 @@
+namespace SJZY.Track.Domain.Dtos.Common;
+
+public class PortTimezoneDto
+{
+ ///
+ /// 城市英文名称
+ ///
+ public string? PortCode { get; set; }
+
+ ///
+ /// 时区
+ ///
+ public string Timezone { get; set; }
+}
\ No newline at end of file
diff --git a/src/SJZY.Track.Domain/Dtos/TrackPull/GetTrackPullContentListCacheVO.cs b/src/SJZY.Track.Domain/Dtos/TrackPull/GetTrackPullContentListCacheVO.cs
index 047431d9..8fad0074 100644
--- a/src/SJZY.Track.Domain/Dtos/TrackPull/GetTrackPullContentListCacheVO.cs
+++ b/src/SJZY.Track.Domain/Dtos/TrackPull/GetTrackPullContentListCacheVO.cs
@@ -36,6 +36,11 @@ public class GetTrackPullContentListCacheVO
/// 轨迹发生地参数值(主数据枚举)
///
public int? TrackPlaceValue { get; set; }
+
+ ///
+ /// 兜底时区轨迹发生地参数值(TrackLocationValue)
+ ///
+ public int? TrackTimePlaceValue { get; set; }
///
/// 轨迹时区GMT
@@ -46,4 +51,9 @@ public class GetTrackPullContentListCacheVO
/// 订单是否完成(由轨迹刷新配置决定)
///
public bool IsCompleted { get; set; }
+
+ ///
+ /// 轨迹时区发生地参数值
+ ///
+ public int? TrackZonePlaceValue { get; set; }
}
\ No newline at end of file
diff --git a/src/SJZY.Track.Domain/Queries/ITimezoneQueries.cs b/src/SJZY.Track.Domain/Queries/ITimezoneQueries.cs
new file mode 100644
index 00000000..14e33748
--- /dev/null
+++ b/src/SJZY.Track.Domain/Queries/ITimezoneQueries.cs
@@ -0,0 +1,20 @@
+using SJZY.Track.Domain.Dtos.Common;
+
+namespace SJZY.Track.Domain.Queries;
+
+public interface ITimezoneQueries
+{
+ ///
+ /// 通过城市英文名称获取时区
+ ///
+ ///
+ ///
+ Task GetCityTimezone(string cityEnName);
+
+ ///
+ /// 通过港口三字码获取时区
+ ///
+ ///
+ ///
+ Task GetPortTimezone(string airCode);
+}
\ No newline at end of file
diff --git a/src/SJZY.Track.Domain/VEntities/VAirportEntity.cs b/src/SJZY.Track.Domain/VEntities/VAirportEntity.cs
new file mode 100644
index 00000000..ba69e854
--- /dev/null
+++ b/src/SJZY.Track.Domain/VEntities/VAirportEntity.cs
@@ -0,0 +1,121 @@
+using SqlSugar;
+
+namespace SJZY.Track.Domain.VEntities;
+
+///
+/// 空港
+///
+[SugarTable("t_airport", "空港")]
+public class VAirportEntity : IFullAuditedBasicEntitys
+{
+ [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)]
+ public int Id { get; set; }
+
+ ///
+ /// 空运航线id
+ ///
+ [SugarColumn(ColumnName = "AirRouteId")]
+ public int AirRouteId { get; set; }
+
+ ///
+ /// 城市id
+ ///
+ [SugarColumn(ColumnName = "CityId")]
+ public int CityId { get; set; }
+
+ ///
+ /// 主港口,空港id
+ ///
+ [SugarColumn(ColumnName = "MId", IsNullable = true)]
+ public int? MId { get; set; }
+
+ ///
+ /// 中文名
+ ///
+ [SugarColumn(ColumnName = "CnName")]
+ public string CnName { get; set; }
+
+ ///
+ /// 英文名
+ ///
+ [SugarColumn(ColumnName = "EnName")]
+ public string EnName { get; set; }
+
+ ///
+ /// 代码
+ ///
+ [SugarColumn(ColumnName = "Code", IsNullable = true)]
+ public string Code { get; set; }
+
+ ///
+ /// 海关代码
+ ///
+ [SugarColumn(ColumnName = "CustomsCode", IsNullable = true)]
+ public string CustomsCode { get; set; }
+
+ ///
+ /// 海关名称
+ ///
+ [SugarColumn(ColumnName = "CustomsName", IsNullable = true)]
+ public string CustomsName { get; set; }
+
+ ///
+ /// 离境口岸代码
+ ///
+ [SugarColumn(ColumnName = "DeparturePortCode", IsNullable = true)]
+ public string DeparturePortCode { get; set; }
+
+ ///
+ /// 离境口岸名称
+ ///
+ [SugarColumn(ColumnName = "DeparturePortName", IsNullable = true)]
+ public string DeparturePortName { get; set; }
+
+ ///
+ /// 国内排序
+ ///
+ [SugarColumn(ColumnName = "DomcSort")]
+ public int DomcSort { get; set; }
+
+ ///
+ /// 国外排序
+ ///
+ [SugarColumn(ColumnName = "OverSort")]
+ public int OverSort { get; set; }
+
+ ///
+ /// 备注
+ ///
+ [SugarColumn(ColumnName = "Remark", IsNullable = true)]
+ public string Remark { get; set; }
+
+ ///
+ /// 创建人
+ ///
+ [SugarColumn(ColumnName = "CreateUserId", IsNullable = true)]
+ public int? CreateUserId { get; set; }
+
+ ///
+ /// 创建时间
+ ///
+ [SugarColumn(ColumnName = "CreateTime", IsNullable = false, DefaultValue = "CURRENT_TIMESTAMP")]
+ public DateTime CreateTime { get; set; }
+
+ ///
+ /// 更新人
+ ///
+ [SugarColumn(ColumnName = "UpdateUserId", IsNullable = true)]
+ public int? UpdateUserId { get; set; }
+
+ ///
+ /// 更新时间
+ ///
+ [SugarColumn(ColumnName = "UpdateTime", IsNullable = true)]
+ public DateTime? UpdateTime { get; set; }
+
+ ///
+ /// 是否删除
+ ///
+ [SugarColumn(ColumnName = "IsDeleted")]
+ public bool IsDeleted { get; set; }
+}
\ No newline at end of file
diff --git a/src/SJZY.Track.Domain/VEntities/VCityEntity.cs b/src/SJZY.Track.Domain/VEntities/VCityEntity.cs
new file mode 100644
index 00000000..ad914504
--- /dev/null
+++ b/src/SJZY.Track.Domain/VEntities/VCityEntity.cs
@@ -0,0 +1,140 @@
+using SqlSugar;
+using System;
+
+namespace SJZY.Track.Domain.VEntities;
+
+[SugarTable("t_city")]
+public class VCityEntity : IFullAuditedBasicEntitys
+{
+ ///
+ /// 主键
+ ///
+ [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)]
+ public int Id { get; set; }
+
+ ///
+ /// 国家id
+ ///
+ [SugarColumn(ColumnName = "CountryId")]
+ public int CountryId { get; set; }
+
+ ///
+ /// 省份id
+ ///
+ [SugarColumn(ColumnName = "ProvinceId", IsNullable = true)]
+ public int? ProvinceId { get; set; }
+
+ ///
+ /// 中文名
+ ///
+ [SugarColumn(ColumnName = "CnName")]
+ public string CnName { get; set; }
+
+ ///
+ /// 英文名
+ ///
+ [SugarColumn(ColumnName = "EnName")]
+ public string EnName { get; set; }
+
+ ///
+ /// 联合国code
+ ///
+ [SugarColumn(ColumnName = "UnCode", IsNullable = true)]
+ public string UnCode { get; set; }
+
+ ///
+ /// 所区分组(1华南,2华东,3华北,4 西南,5华中)
+ ///
+ [SugarColumn(ColumnName = "CityType", IsNullable = true)]
+ public int? CityType { get; set; }
+
+ ///
+ /// 时区
+ ///
+ [SugarColumn(ColumnName = "TimeZone", IsNullable = true)]
+ public string TimeZone { get; set; }
+
+ ///
+ /// 排序
+ ///
+ [SugarColumn(ColumnName = "Sort")]
+ public int Sort { get; set; }
+
+ ///
+ /// 备注
+ ///
+ [SugarColumn(ColumnName = "Remark", IsNullable = true)]
+ public string Remark { get; set; }
+
+ ///
+ /// 国家统计局统计用区划代码
+ ///
+ [SugarColumn(ColumnName = "StatisticalZoningCode", IsNullable = true)]
+ public string StatisticalZoningCode { get; set; }
+
+ ///
+ /// 创建人
+ ///
+ [SugarColumn(ColumnName = "CreateUserId", IsNullable = true)]
+ public int? CreateUserId { get; set; }
+
+ ///
+ /// 创建时间
+ ///
+ [SugarColumn(ColumnName = "CreateTime", IsNullable = false, DefaultValue = "CURRENT_TIMESTAMP")]
+ public DateTime CreateTime { get; set; }
+
+ ///
+ /// 更新人
+ ///
+ [SugarColumn(ColumnName = "UpdateUserId", IsNullable = true)]
+ public int? UpdateUserId { get; set; }
+
+ ///
+ /// 更新时间
+ ///
+ [SugarColumn(ColumnName = "UpdateTime", IsNullable = true)]
+ public DateTime? UpdateTime { get; set; }
+
+ ///
+ /// 是否删
+ ///
+ [SugarColumn(ColumnName = "IsDeleted")]
+ public bool IsDeleted { get; set; }
+
+ ///
+ /// 维护时区
+ ///
+ [SugarColumn(ColumnName = "MaintainTimeZone", IsNullable = true)]
+ public decimal? MaintainTimeZone { get; set; }
+
+ ///
+ /// 经度
+ ///
+ [SugarColumn(ColumnName = "Longitude", IsNullable = true)]
+ public string Longitude { get; set; }
+
+ ///
+ /// 纬度
+ ///
+ [SugarColumn(ColumnName = "Latitude", IsNullable = true)]
+ public string Latitude { get; set; }
+
+ ///
+ /// 海拔
+ ///
+ [SugarColumn(ColumnName = "Elevation", IsNullable = true)]
+ public string Elevation { get; set; }
+
+ ///
+ /// 语言
+ ///
+ [SugarColumn(ColumnName = "Language", IsNullable = true)]
+ public string Language { get; set; }
+
+ ///
+ /// timezoneURL
+ ///
+ [SugarColumn(ColumnName = "TimezoneURL", IsNullable = true)]
+ public string TimezoneURL { get; set; }
+}
\ No newline at end of file
diff --git a/src/SJZY.Track.Repository/Queries/TimezoneQueries.cs b/src/SJZY.Track.Repository/Queries/TimezoneQueries.cs
new file mode 100644
index 00000000..cd2a0569
--- /dev/null
+++ b/src/SJZY.Track.Repository/Queries/TimezoneQueries.cs
@@ -0,0 +1,101 @@
+using System.Reflection.Metadata;
+using SJZY.Track.Domain.Consts;
+using SJZY.Track.Domain.Dtos.Common;
+using SJZY.Track.Domain.Queries;
+using SJZY.Track.Domain.VEntities;
+
+namespace SJZY.Track.Repository.Queries;
+
+public class TimezoneQueries:ITimezoneQueries, ISugarRepository, ITransientDependency
+{
+ public ISqlSugarClient Context { get; set; }
+
+ public TimezoneQueries()
+ {
+ Context = SqlsugarScopeBasic.GetContextSafely("default");
+ }
+
+ private string GetCityKey(string cityEnName)
+ {
+ return $"{GlobalConst.CityTimezoneKeyPrefix}:{cityEnName}";
+ }
+
+ private string GetPortKey(string airCode)
+ {
+ return $"{GlobalConst.PortTimezoneKeyPrefix}:{airCode}";
+ }
+
+ ///
+ /// 获取城市时区
+ ///
+ /// 城市英文名称
+ /// 时区
+ public async Task GetCityTimezone(string cityEnName)
+ {
+ var key =GetCityKey(cityEnName);
+ var timezone = RedisHelper.Client.Get(key);
+ if (!string.IsNullOrEmpty(timezone))
+ {
+ return timezone;
+ }
+
+ var cityTimezone = await GetTimezoneByCityName(cityEnName);
+ if (cityTimezone == null!)
+ {
+ await RedisHelper.Client.SetAsync(key, string.Empty, TimeSpan.FromSeconds(20));
+ return string.Empty;
+ }
+
+ await RedisHelper.Client.SetAsync(key, cityTimezone.Timezone, TimeSpan.FromHours(6));
+ return cityTimezone.Timezone;
+ }
+
+ ///
+ /// 获取港口时区
+ ///
+ /// 港口三字码
+ /// 时区
+ public async Task GetPortTimezone(string airCode)
+ {
+ var key = GetPortKey(airCode);
+ var timezone = RedisHelper.Client.Get(key);
+ if (!string.IsNullOrEmpty(timezone))
+ {
+ return timezone;
+ }
+
+ var portTimezone = await GetTimezoneByPortCode(airCode);
+ if (portTimezone == null!)
+ {
+ await RedisHelper.Client.SetAsync(key, string.Empty, TimeSpan.FromSeconds(20));
+ return string.Empty;
+ }
+
+ await RedisHelper.Client.SetAsync(key, portTimezone.Timezone, TimeSpan.FromHours(6));
+ return portTimezone.Timezone;
+ }
+
+ private Task GetTimezoneByCityName(string cityEnName)
+ {
+ return Context.Queryable().Where(val => !val.IsDeleted && val.EnName == cityEnName)
+ .Select(val=>new CityTimezoneDto()
+ {
+ CityEnName =val.EnName,
+ Timezone = val.TimeZone
+ })
+ .FirstAsync();
+ }
+
+ private Task GetTimezoneByPortCode(string airCode)
+ {
+ return Context.Queryable()
+ .InnerJoin((city, airport) => city.Id == airport.CityId)
+ .Where((city,airport) => !airport.IsDeleted && !city.IsDeleted && airport.Code == airCode)
+ .Select((city,airport)=>new PortTimezoneDto()
+ {
+ PortCode = airport.Code,
+ Timezone = city.TimeZone
+ })
+ .FirstAsync();
+ }
+}
\ No newline at end of file
diff --git a/src/SJZY.Track.Repository/Repositories/OrderRepository.cs b/src/SJZY.Track.Repository/Repositories/OrderRepository.cs
index 85faff5d..85df94ed 100644
--- a/src/SJZY.Track.Repository/Repositories/OrderRepository.cs
+++ b/src/SJZY.Track.Repository/Repositories/OrderRepository.cs
@@ -19,6 +19,11 @@ public class OrderRepository : BasicRepository, IOrderRepository, I
tran.Complete();
}
+ public Task UpdateBatchIdsAsync(List ids, int newValue)
+ {
+ return Context.Ado.ExecuteCommandAsync("UPDATE `t_order` SET `DeficiencyTag` = @newValue WHERE `Id` IN @ids", new { ids, newValue });
+ }
+
public async Task UpdateOrderAsync(OrderEntity order)
{
using var tran = TransactionHelper.GetReadCommitted();