36 changed files with 2081 additions and 715 deletions
File diff suppressed because it is too large
@ -0,0 +1,138 @@ |
|||
using DotNetCore.CAP.Internal; |
|||
using DotNetCore.CAP.Transport; |
|||
using System; |
|||
using System.Collections.Concurrent; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
using System.Threading; |
|||
using System.Reflection; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.EventBus; |
|||
using Volo.Abp.EventBus.Distributed; |
|||
using Volo.Abp.Threading; |
|||
using System.Linq; |
|||
using DotNetCore.CAP; |
|||
using Microsoft.Extensions.Options; |
|||
|
|||
namespace LINGYUN.Abp.EventBus.CAP |
|||
{ |
|||
internal class CustomDistributedEventSubscriber : ICustomDistributedEventSubscriber, ISingletonDependency |
|||
{ |
|||
protected CapOptions CapOptions { get; } |
|||
protected IConsumerClientFactory ConsumerClientFactory { get; } |
|||
|
|||
protected ConcurrentDictionary<Type, List<IEventHandlerFactory>> HandlerFactories { get; } |
|||
protected ConcurrentDictionary<string, CancellationTokenSource> EventStopingTokens { get; } |
|||
public CustomDistributedEventSubscriber( |
|||
IOptions<CapOptions> options, |
|||
IConsumerClientFactory consumerClientFactory) |
|||
{ |
|||
CapOptions = options.Value; |
|||
ConsumerClientFactory = consumerClientFactory; |
|||
|
|||
HandlerFactories = new ConcurrentDictionary<Type, List<IEventHandlerFactory>>(); |
|||
EventStopingTokens = new ConcurrentDictionary<string, CancellationTokenSource>(); |
|||
} |
|||
|
|||
public void Subscribe(Type eventType, IEventHandlerFactory factory) |
|||
{ |
|||
GetOrCreateHandlerFactories(eventType) |
|||
.Locking(factories => |
|||
{ |
|||
if (!factories.Contains(factory)) |
|||
{ |
|||
factories.Add(factory); |
|||
// TODO 客户端订阅
|
|||
} |
|||
}); |
|||
} |
|||
|
|||
public void UnSubscribe(Type eventType, IEventHandlerFactory factory) |
|||
{ |
|||
GetOrCreateHandlerFactories(eventType) |
|||
.Locking(factories => |
|||
{ |
|||
if (factories.Contains(factory)) |
|||
{ |
|||
factories.Remove(factory); |
|||
// TODO 客户端退订
|
|||
} |
|||
}); |
|||
} |
|||
|
|||
private List<IEventHandlerFactory> GetOrCreateHandlerFactories(Type eventType) |
|||
{ |
|||
return HandlerFactories.GetOrAdd( |
|||
eventType, |
|||
type => |
|||
{ |
|||
var eventName = EventNameAttribute.GetNameOrDefault(type); |
|||
EventStopingTokens[eventName] = new CancellationTokenSource(); |
|||
return new List<IEventHandlerFactory>(); |
|||
} |
|||
); |
|||
} |
|||
|
|||
private IEnumerable<ConsumerExecutorDescriptor> GetHandlerDescription(Type eventType, Type typeInfo) |
|||
{ |
|||
var serviceTypeInfo = typeof(IDistributedEventHandler<>) |
|||
.MakeGenericType(eventType); |
|||
var method = typeInfo |
|||
.GetMethod( |
|||
nameof(IDistributedEventHandler<object>.HandleEventAsync), |
|||
new[] { eventType } |
|||
); |
|||
var eventName = EventNameAttribute.GetNameOrDefault(eventType); |
|||
var topicAttr = method.GetCustomAttributes<TopicAttribute>(true); |
|||
var topicAttributes = topicAttr.ToList(); |
|||
|
|||
topicAttributes.Add(new CapSubscribeAttribute(eventName)); |
|||
|
|||
foreach (var attr in topicAttributes) |
|||
{ |
|||
SetSubscribeAttribute(attr); |
|||
|
|||
var parameters = method.GetParameters() |
|||
.Select(parameter => new ParameterDescriptor |
|||
{ |
|||
Name = parameter.Name, |
|||
ParameterType = parameter.ParameterType, |
|||
IsFromCap = parameter.GetCustomAttributes(typeof(FromCapAttribute)).Any() |
|||
}).ToList(); |
|||
|
|||
yield return InitDescriptor(attr, method, typeInfo.GetTypeInfo(), serviceTypeInfo.GetTypeInfo(), parameters); |
|||
} |
|||
} |
|||
|
|||
private void SetSubscribeAttribute(TopicAttribute attribute) |
|||
{ |
|||
if (attribute.Group == null) |
|||
{ |
|||
attribute.Group = CapOptions.DefaultGroup + "." + CapOptions.Version; |
|||
} |
|||
else |
|||
{ |
|||
attribute.Group = attribute.Group + "." + CapOptions.Version; |
|||
} |
|||
} |
|||
|
|||
private ConsumerExecutorDescriptor InitDescriptor( |
|||
TopicAttribute attr, |
|||
MethodInfo methodInfo, |
|||
TypeInfo implType, |
|||
TypeInfo serviceTypeInfo, |
|||
IList<ParameterDescriptor> parameters) |
|||
{ |
|||
var descriptor = new ConsumerExecutorDescriptor |
|||
{ |
|||
Attribute = attr, |
|||
MethodInfo = methodInfo, |
|||
ImplTypeInfo = implType, |
|||
ServiceTypeInfo = serviceTypeInfo, |
|||
Parameters = parameters |
|||
}; |
|||
|
|||
return descriptor; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
using System; |
|||
using Volo.Abp.EventBus; |
|||
|
|||
namespace LINGYUN.Abp.EventBus.CAP |
|||
{ |
|||
/// <summary>
|
|||
/// 自定义事件订阅者
|
|||
/// </summary>
|
|||
public interface ICustomDistributedEventSubscriber |
|||
{ |
|||
/// <summary>
|
|||
/// 订阅事件
|
|||
/// </summary>
|
|||
/// <param name="eventType"></param>
|
|||
/// <param name="factory"></param>
|
|||
void Subscribe(Type eventType, IEventHandlerFactory factory); |
|||
/// <summary>
|
|||
/// 取消订阅
|
|||
/// </summary>
|
|||
/// <param name="eventType"></param>
|
|||
/// <param name="factory"></param>
|
|||
void UnSubscribe(Type eventType, IEventHandlerFactory factory); |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance> |
|||
<Version>3.0.0</Version> |
|||
<Description>腾讯位置服务</Description> |
|||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> |
|||
</PropertyGroup> |
|||
|
|||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> |
|||
<OutputPath>D:\LocalNuget</OutputPath> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<None Remove="LINGYUN\Abp\Location\Tencent\Localization\Resources\en.json" /> |
|||
<None Remove="LINGYUN\Abp\Location\Tencent\Localization\Resources\zh-Hans.json" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<EmbeddedResource Include="LINGYUN\Abp\Location\Tencent\Localization\Resources\en.json" /> |
|||
<EmbeddedResource Include="LINGYUN\Abp\Location\Tencent\Localization\Resources\zh-Hans.json" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="3.1.2" /> |
|||
<PackageReference Include="Volo.Abp.Localization" Version="3.0.0" /> |
|||
<PackageReference Include="Volo.Abp.Json" Version="3.0.0" /> |
|||
<PackageReference Include="Volo.Abp.Threading" Version="3.0.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.Location\LINGYUN.Abp.Location.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,36 @@ |
|||
using LINYUN.Abp.Location.Tencent.Localization; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Polly; |
|||
using System; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace LINGYUN.Abp.Location.Tencent |
|||
{ |
|||
[DependsOn(typeof(AbpLocationModule))] |
|||
public class AbpTencentLocationModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var configuration = context.Services.GetConfiguration(); |
|||
Configure<TencentLocationOptions>(configuration.GetSection("Location:Tencent")); |
|||
|
|||
context.Services.AddHttpClient(TencentLocationHttpConsts.HttpClientName) |
|||
.AddTransientHttpErrorPolicy(builder => |
|||
builder.WaitAndRetryAsync(3, i => TimeSpan.FromSeconds(Math.Pow(2, i)))); |
|||
|
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<AbpTencentLocationModule>(); |
|||
}); |
|||
|
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources |
|||
.Add<TencentLocationResource>("en") |
|||
.AddVirtualJson("/LINGYUN/Abp/Location/Tencent/Localization/Resources"); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
"ResolveLocationFailed": "Parse address error, error message :{0}", |
|||
"Message:RETURN_0": "OK", |
|||
"Message:RETURN_110": "The request parameter information is incorrect", |
|||
"Message:RETURN_306": "Key format error", |
|||
"Message:RETURN_310": "Check the string if the request has retention information", |
|||
"Message:RETURN_311": "The source of the request is not authorized" |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"ResolveLocationFailed": "解析地址出错,错误信息:{0}", |
|||
"Message:RETURN_0": "正常", |
|||
"Message:RETURN_110": "请求参数信息有误", |
|||
"Message:RETURN_306": "Key格式错误", |
|||
"Message:RETURN_310": "请求有护持信息请检查字符串", |
|||
"Message:RETURN_311": "请求来源未被授权" |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace LINYUN.Abp.Location.Tencent.Localization |
|||
{ |
|||
[LocalizationResourceName("TencentLocation")] |
|||
public class TencentLocationResource |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
using Newtonsoft.Json; |
|||
|
|||
namespace LINGYUN.Abp.Location.Tencent.Model |
|||
{ |
|||
/// <summary>
|
|||
/// 地址部件,address不满足需求时可自行拼接
|
|||
/// </summary>
|
|||
public class AddressComponent |
|||
{ |
|||
/// <summary>
|
|||
/// 国家
|
|||
/// </summary>
|
|||
[JsonProperty("nation")] |
|||
public string Nation { get; set; } |
|||
/// <summary>
|
|||
/// 省
|
|||
/// </summary>
|
|||
[JsonProperty("province")] |
|||
public string Province { get; set; } |
|||
/// <summary>
|
|||
/// 市
|
|||
/// </summary>
|
|||
[JsonProperty("city")] |
|||
public string City { get; set; } |
|||
/// <summary>
|
|||
/// 区,可能为空字串
|
|||
/// </summary>
|
|||
[JsonProperty("district")] |
|||
public string District { get; set; } |
|||
/// <summary>
|
|||
/// 街道,可能为空字串
|
|||
/// </summary>
|
|||
[JsonProperty("street")] |
|||
public string Street { get; set; } |
|||
/// <summary>
|
|||
/// 门牌,可能为空字串
|
|||
/// </summary>
|
|||
[JsonProperty("street_number")] |
|||
public string StreetNumber { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,53 @@ |
|||
using Newtonsoft.Json; |
|||
|
|||
namespace LINGYUN.Abp.Location.Tencent.Model |
|||
{ |
|||
/// <summary>
|
|||
/// 行政区划信息
|
|||
/// </summary>
|
|||
public class AddressInfo |
|||
{ |
|||
[JsonProperty("adcode")] |
|||
public string AdCode { get; set; } |
|||
/// <summary>
|
|||
/// 城市代码
|
|||
/// </summary>
|
|||
[JsonProperty("city_code")] |
|||
public string CityCode { get; set; } |
|||
/// <summary>
|
|||
/// 行政区划代码
|
|||
/// </summary>
|
|||
[JsonProperty("nation_code")] |
|||
public string NationCode { get; set; } |
|||
/// <summary>
|
|||
/// 行政区划名称
|
|||
/// </summary>
|
|||
[JsonProperty("name")] |
|||
public string Name { get; set; } |
|||
/// <summary>
|
|||
/// 国家
|
|||
/// </summary>
|
|||
[JsonProperty("nation")] |
|||
public string Nation { get; set; } |
|||
/// <summary>
|
|||
/// 省/直辖市
|
|||
/// </summary>
|
|||
[JsonProperty("province")] |
|||
public string Province { get; set; } |
|||
/// <summary>
|
|||
/// 市/地级区 及同级行政区划
|
|||
/// </summary>
|
|||
[JsonProperty("city")] |
|||
public string City { get; set; } |
|||
/// <summary>
|
|||
/// 区/县级市 及同级行政区划
|
|||
/// </summary>
|
|||
[JsonProperty("district")] |
|||
public string District { get; set; } |
|||
/// <summary>
|
|||
/// 行政区划中心点坐标
|
|||
/// </summary>
|
|||
[JsonProperty("location")] |
|||
public Location Location { get; set; } = new Location(); |
|||
} |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
using Newtonsoft.Json; |
|||
|
|||
namespace LINGYUN.Abp.Location.Tencent.Model |
|||
{ |
|||
/// <summary>
|
|||
/// 坐标相对位置参考
|
|||
/// </summary>
|
|||
public class AddressReference |
|||
{ |
|||
/// <summary>
|
|||
/// 商圈
|
|||
/// </summary>
|
|||
[JsonProperty("business_area")] |
|||
public Area BusinessArea { get; set; } = new Area(); |
|||
/// <summary>
|
|||
/// 知名区域,如商圈或人们普遍认为有较高知名度的区域
|
|||
/// </summary>
|
|||
[JsonProperty("famous_area")] |
|||
public Area FamousArea { get; set; } = new Area(); |
|||
/// <summary>
|
|||
/// 乡镇街道
|
|||
/// </summary>
|
|||
[JsonProperty("town")] |
|||
public Area Town { get; set; } = new Area(); |
|||
/// <summary>
|
|||
/// 一级地标,可识别性较强、规模较大的地点、小区等
|
|||
/// </summary>
|
|||
[JsonProperty("landmark_l1")] |
|||
public Area Landmark1 { get; set; } = new Area(); |
|||
/// <summary>
|
|||
/// 二级地标,较一级地标更为精确,规模更小
|
|||
/// </summary>
|
|||
[JsonProperty("landmark_l2")] |
|||
public Area Landmark2 { get; set; } = new Area(); |
|||
/// <summary>
|
|||
/// 街道
|
|||
/// </summary>
|
|||
[JsonProperty("street")] |
|||
public Area Street { get; set; } = new Area(); |
|||
/// <summary>
|
|||
/// 门牌
|
|||
/// </summary>
|
|||
[JsonProperty("street_number")] |
|||
public Area StreetNumber { get; set; } = new Area(); |
|||
/// <summary>
|
|||
/// 交叉路口
|
|||
/// </summary>
|
|||
[JsonProperty("crossroad")] |
|||
public Area CrossRoad { get; set; } = new Area(); |
|||
/// <summary>
|
|||
/// 水系
|
|||
/// </summary>
|
|||
[JsonProperty("water")] |
|||
public Area Water { get; set; } = new Area(); |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
using Newtonsoft.Json; |
|||
|
|||
namespace LINGYUN.Abp.Location.Tencent.Model |
|||
{ |
|||
/// <summary>
|
|||
/// 区域信息
|
|||
/// </summary>
|
|||
public class Area |
|||
{ |
|||
/// <summary>
|
|||
/// 地点唯一标识
|
|||
/// </summary>
|
|||
[JsonProperty("id")] |
|||
public string Id { get; set; } |
|||
/// <summary>
|
|||
/// 名称/标题
|
|||
/// </summary>
|
|||
[JsonProperty("title")] |
|||
public string Title { get; set; } |
|||
/// <summary>
|
|||
/// 坐标
|
|||
/// </summary>
|
|||
[JsonProperty("location")] |
|||
public Location Location { get; set; } = new Location(); |
|||
/// <summary>
|
|||
/// 此参考位置到输入坐标的直线距离
|
|||
/// </summary>
|
|||
[JsonProperty("_distance")] |
|||
public string Distance { get; set; } |
|||
/// <summary>
|
|||
/// 此参考位置到输入坐标的方位关系,
|
|||
/// 如:北、南、内
|
|||
/// </summary>
|
|||
[JsonProperty("_dir_desc")] |
|||
public string DirDescription { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using Newtonsoft.Json; |
|||
|
|||
namespace LINGYUN.Abp.Location.Tencent.Model |
|||
{ |
|||
/// <summary>
|
|||
/// 位置描述
|
|||
/// </summary>
|
|||
public class FormattedAddress |
|||
{ |
|||
/// <summary>
|
|||
/// 经过腾讯地图优化过的描述方式,更具人性化特点
|
|||
/// </summary>
|
|||
[JsonProperty("recommend")] |
|||
public string ReCommend { get; set; } |
|||
/// <summary>
|
|||
/// 大致位置,可用于对位置的粗略描述
|
|||
/// </summary>
|
|||
[JsonProperty("rough")] |
|||
public string Rough { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using Newtonsoft.Json; |
|||
|
|||
namespace LINGYUN.Abp.Location.Tencent.Model |
|||
{ |
|||
/// <summary>
|
|||
/// 行政区划中心点坐标
|
|||
/// </summary>
|
|||
public class Location |
|||
{ |
|||
/// <summary>
|
|||
/// 纬度
|
|||
/// </summary>
|
|||
[JsonProperty("lat")] |
|||
public double Lat { get; set; } |
|||
/// <summary>
|
|||
/// 经度
|
|||
/// </summary>
|
|||
[JsonProperty("lng")] |
|||
public double Lng { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
using Newtonsoft.Json; |
|||
|
|||
namespace LINGYUN.Abp.Location.Tencent.Model |
|||
{ |
|||
/// <summary>
|
|||
/// POI对象
|
|||
/// </summary>
|
|||
public class Poi |
|||
{ |
|||
/// <summary>
|
|||
/// 地点唯一标识
|
|||
/// </summary>
|
|||
[JsonProperty("id")] |
|||
public string Id { get; set; } |
|||
/// <summary>
|
|||
/// 名称/标题
|
|||
/// </summary>
|
|||
[JsonProperty("title")] |
|||
public string Title { get; set; } |
|||
/// <summary>
|
|||
/// 地址
|
|||
/// </summary>
|
|||
[JsonProperty("address")] |
|||
public string Address { get; set; } |
|||
/// <summary>
|
|||
/// POI分类
|
|||
/// </summary>
|
|||
[JsonProperty("category")] |
|||
public string CateGory { get; set; } |
|||
/// <summary>
|
|||
/// 坐标
|
|||
/// </summary>
|
|||
[JsonProperty("location")] |
|||
public Location Location { get; set; } = new Location(); |
|||
/// <summary>
|
|||
/// 该POI到逆地址解析传入的坐标的直线距离
|
|||
/// </summary>
|
|||
[JsonProperty("_distance")] |
|||
public string Distance { get; set; } |
|||
/// <summary>
|
|||
/// 行政区划信息
|
|||
/// </summary>
|
|||
[JsonProperty("ad_info")] |
|||
public AddressInfo AddressInfo { get; set; } = new AddressInfo(); |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
using Newtonsoft.Json; |
|||
|
|||
namespace LINGYUN.Abp.Location.Tencent.Model |
|||
{ |
|||
public class TencentGeocode |
|||
{ |
|||
/// <summary>
|
|||
/// 解析到的坐标
|
|||
/// </summary>
|
|||
[JsonProperty("location")] |
|||
public Location Location { get; set; } = new Location(); |
|||
/// <summary>
|
|||
/// 解析后的地址部件
|
|||
/// </summary>
|
|||
[JsonProperty("address_components")] |
|||
public AddressComponent AddressComponent { get; set; } = new AddressComponent(); |
|||
/// <summary>
|
|||
/// 行政区划信息
|
|||
/// </summary>
|
|||
[JsonProperty("ad_info")] |
|||
public GeocodeAddressInfo AddressInfo { get; set; } = new GeocodeAddressInfo(); |
|||
/// <summary>
|
|||
/// 可信度参考:值范围 1 <低可信> - 10 <高可信>
|
|||
/// </summary>
|
|||
[JsonProperty("reliability")] |
|||
public int Reliability { get; set; } |
|||
/// <summary>
|
|||
/// 解析精度级别,分为11个级别,一般>=9即可采用(定位到点,精度较高) 也可根据实际业务需求自行调整
|
|||
/// </summary>
|
|||
[JsonProperty("level")] |
|||
public int Level { get; set; } |
|||
} |
|||
/// <summary>
|
|||
/// 行政区划信息
|
|||
/// </summary>
|
|||
public class GeocodeAddressInfo |
|||
{ |
|||
/// <summary>
|
|||
/// 行政区划代码
|
|||
/// </summary>
|
|||
[JsonProperty("adcode")] |
|||
public string AdCode { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
using Newtonsoft.Json; |
|||
|
|||
namespace LINGYUN.Abp.Location.Tencent.Model |
|||
{ |
|||
/// <summary>
|
|||
/// IP定位结果
|
|||
/// </summary>
|
|||
public class TencentIPGeocode |
|||
{ |
|||
/// <summary>
|
|||
/// 用于定位的IP地址
|
|||
/// </summary>
|
|||
[JsonProperty("ip")] |
|||
public string IpAddress { get; set; } |
|||
/// <summary>
|
|||
/// 定位坐标
|
|||
/// </summary>
|
|||
[JsonProperty("location")] |
|||
public Location Location { get; set; } = new Location(); |
|||
/// <summary>
|
|||
/// 定位行政区划信息
|
|||
/// </summary>
|
|||
[JsonProperty("ad_info")] |
|||
public AddressInfo AddressInfo { get; set; } = new AddressInfo(); |
|||
} |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
using Newtonsoft.Json; |
|||
|
|||
namespace LINGYUN.Abp.Location.Tencent.Model |
|||
{ |
|||
/// <summary>
|
|||
/// 逆地址解析结果
|
|||
/// </summary>
|
|||
public class TencentReGeocode |
|||
{ |
|||
/// <summary>
|
|||
/// 地址描述
|
|||
/// </summary>
|
|||
[JsonProperty("address")] |
|||
public string Address { get; set; } |
|||
/// <summary>
|
|||
/// 位置描述
|
|||
/// </summary>
|
|||
[JsonProperty("formatted_addresses")] |
|||
public FormattedAddress FormattedAddress { get; set; } = new FormattedAddress(); |
|||
/// <summary>
|
|||
/// 地址部件,address不满足需求时可自行拼接
|
|||
/// </summary>
|
|||
[JsonProperty("address_component")] |
|||
public AddressComponent AddressComponent { get; set; } = new AddressComponent(); |
|||
/// <summary>
|
|||
/// 行政区划信息
|
|||
/// </summary>
|
|||
[JsonProperty("ad_info")] |
|||
public AddressInfo AddressInfo { get; set; } = new AddressInfo(); |
|||
/// <summary>
|
|||
/// 坐标相对位置参考
|
|||
/// </summary>
|
|||
[JsonProperty("address_reference")] |
|||
public AddressReference AddressReference { get; set; } = new AddressReference(); |
|||
/// <summary>
|
|||
/// 查询的周边poi的总数
|
|||
/// </summary>
|
|||
[JsonProperty("poi_count")] |
|||
public int PoiCount { get; set; } |
|||
/// <summary>
|
|||
/// POI数组,对象中每个子项为一个POI对象
|
|||
/// </summary>
|
|||
[JsonProperty("pois")] |
|||
public Poi[] Pois { get; set; } = new Poi[0]; |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using LINGYUN.Abp.Location.Tencent.Model; |
|||
using Newtonsoft.Json; |
|||
|
|||
namespace LINGYUN.Abp.Location.Tencent.Response |
|||
{ |
|||
public class TencentGeocodeResponse : TencentLocationResponse |
|||
{ |
|||
/// <summary>
|
|||
/// 地址解析结果
|
|||
/// </summary>
|
|||
[JsonProperty("result")] |
|||
public TencentGeocode Result { get; set; } = new TencentGeocode(); |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using LINGYUN.Abp.Location.Tencent.Model; |
|||
using Newtonsoft.Json; |
|||
|
|||
namespace LINGYUN.Abp.Location.Tencent.Response |
|||
{ |
|||
public class TencentIPGeocodeResponse : TencentLocationResponse |
|||
{ |
|||
/// <summary>
|
|||
/// IP定位结果
|
|||
/// </summary>
|
|||
[JsonProperty("result")] |
|||
public TencentIPGeocode Result { get; set; } = new TencentIPGeocode(); |
|||
} |
|||
} |
|||
@ -0,0 +1,52 @@ |
|||
using LINYUN.Abp.Location.Tencent.Localization; |
|||
using Newtonsoft.Json; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace LINGYUN.Abp.Location.Tencent.Response |
|||
{ |
|||
public abstract class TencentLocationResponse |
|||
{ |
|||
/// <summary>
|
|||
/// 状态码,0为正常,
|
|||
/// 310请求参数信息有误,
|
|||
/// 311Key格式错误,
|
|||
/// 306请求有护持信息请检查字符串,
|
|||
/// 110请求来源未被授权
|
|||
/// </summary>
|
|||
[JsonProperty("status")] |
|||
public int Status { get; set; } |
|||
/// <summary>
|
|||
/// 状态说明
|
|||
/// </summary>
|
|||
[JsonProperty("message")] |
|||
public string Message { get; set; } |
|||
/// <summary>
|
|||
/// 本次请求的唯一标识
|
|||
/// </summary>
|
|||
[JsonProperty("request_id")] |
|||
public string RequestId { get; set; } |
|||
/// <summary>
|
|||
/// 是否请求成功
|
|||
/// </summary>
|
|||
public bool IsSuccessed => Status.Equals(0); |
|||
|
|||
public ILocalizableString GetErrorMessage() |
|||
{ |
|||
switch (Status) |
|||
{ |
|||
case 0: |
|||
return LocalizableString.Create<TencentLocationResource>("Message:RETURN_0"); |
|||
case 110: |
|||
return LocalizableString.Create<TencentLocationResource>("Message:RETURN_110"); |
|||
case 306: |
|||
return LocalizableString.Create<TencentLocationResource>("Message:RETURN_306"); |
|||
case 310: |
|||
return LocalizableString.Create<TencentLocationResource>("Message:RETURN_310"); |
|||
case 311: |
|||
return LocalizableString.Create<TencentLocationResource>("Message:RETURN_311"); |
|||
default: throw new AbpException(Message); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using LINGYUN.Abp.Location.Tencent.Model; |
|||
using Newtonsoft.Json; |
|||
|
|||
namespace LINGYUN.Abp.Location.Tencent.Response |
|||
{ |
|||
public class TencentReGeocodeResponse : TencentLocationResponse |
|||
{ |
|||
/// <summary>
|
|||
/// 逆地址解析结果
|
|||
/// </summary>
|
|||
[JsonProperty("result")] |
|||
public TencentReGeocode Result { get; set; } = new TencentReGeocode(); |
|||
} |
|||
} |
|||
@ -0,0 +1,211 @@ |
|||
using LINGYUN.Abp.Location.Tencent.Response; |
|||
using LINGYUN.Abp.Location.Tencent.Utils; |
|||
using LINYUN.Abp.Location.Tencent.Localization; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Localization; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Net.Http; |
|||
using System.Text; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Json; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace LINGYUN.Abp.Location.Tencent |
|||
{ |
|||
public class TencentLocationHttpClient : ITransientDependency |
|||
{ |
|||
protected TencentLocationOptions Options { get; } |
|||
protected IJsonSerializer JsonSerializer { get; } |
|||
protected IServiceProvider ServiceProvider { get; } |
|||
protected IHttpClientFactory HttpClientFactory { get; } |
|||
protected ICancellationTokenProvider CancellationTokenProvider { get; } |
|||
|
|||
public TencentLocationHttpClient( |
|||
IOptions<TencentLocationOptions> options, |
|||
IJsonSerializer jsonSerializer, |
|||
IServiceProvider serviceProvider, |
|||
IHttpClientFactory httpClientFactory, |
|||
ICancellationTokenProvider cancellationTokenProvider) |
|||
{ |
|||
Options = options.Value; |
|||
JsonSerializer = jsonSerializer; |
|||
ServiceProvider = serviceProvider; |
|||
HttpClientFactory = httpClientFactory; |
|||
CancellationTokenProvider = cancellationTokenProvider; |
|||
} |
|||
|
|||
public virtual async Task<IPGecodeLocation> IPGeocodeAsync(string ipAddress) |
|||
{ |
|||
var requestParamters = new Dictionary<string, string> |
|||
{ |
|||
{ "callback", Options.Callback }, |
|||
{ "ip", ipAddress }, |
|||
{ "key", Options.AccessKey }, |
|||
{ "output", Options.Output } |
|||
}; |
|||
var tencentMapUrl = "https://apis.map.qq.com"; |
|||
var tencentMapPath = "/ws/location/v1/ip"; |
|||
if (!Options.SecretKey.IsNullOrWhiteSpace()) |
|||
{ |
|||
var sig = TencentSecretKeyCaculater.CalcSecretKey(tencentMapPath, Options.SecretKey, requestParamters); |
|||
requestParamters.Add("sig", sig); |
|||
} |
|||
var tencentLocationResponse = await GetTencentMapResponseAsync<TencentIPGeocodeResponse>(tencentMapUrl, tencentMapPath, requestParamters); |
|||
|
|||
var location = new IPGecodeLocation |
|||
{ |
|||
IpAddress = tencentLocationResponse.Result.IpAddress, |
|||
AdCode = tencentLocationResponse.Result.AddressInfo.AdCode, |
|||
City = tencentLocationResponse.Result.AddressInfo.City, |
|||
Country = tencentLocationResponse.Result.AddressInfo.Nation, |
|||
District = tencentLocationResponse.Result.AddressInfo.District, |
|||
Location = new Location |
|||
{ |
|||
Latitude = tencentLocationResponse.Result.Location.Lat, |
|||
Longitude = tencentLocationResponse.Result.Location.Lng |
|||
}, |
|||
Province = tencentLocationResponse.Result.AddressInfo.Province |
|||
}; |
|||
location.AddAdditional("TencentLocation", tencentLocationResponse.Result); |
|||
|
|||
return location; |
|||
} |
|||
|
|||
public virtual async Task<GecodeLocation> GeocodeAsync(string address, string city = null) |
|||
{ |
|||
var requestParamters = new Dictionary<string, string> |
|||
{ |
|||
{ "address", address }, |
|||
{ "callback", Options.Callback }, |
|||
{ "key", Options.AccessKey }, |
|||
{ "output", Options.Output } |
|||
}; |
|||
if (!city.IsNullOrWhiteSpace()) |
|||
{ |
|||
requestParamters.Add("region", city); |
|||
} |
|||
var tencentMapUrl = "https://apis.map.qq.com"; |
|||
var tencentMapPath = "/ws/geocoder/v1"; |
|||
if (!Options.SecretKey.IsNullOrWhiteSpace()) |
|||
{ |
|||
var sig = TencentSecretKeyCaculater.CalcSecretKey(tencentMapPath, Options.SecretKey, requestParamters); |
|||
requestParamters.Add("sig", sig); |
|||
} |
|||
var tencentLocationResponse = await GetTencentMapResponseAsync<TencentGeocodeResponse>(tencentMapUrl, tencentMapPath, requestParamters); |
|||
var location = new GecodeLocation |
|||
{ |
|||
Confidence = tencentLocationResponse.Result.Reliability, |
|||
Latitude = tencentLocationResponse.Result.Location.Lat, |
|||
Longitude = tencentLocationResponse.Result.Location.Lng, |
|||
Level = tencentLocationResponse.Result.Level.ToString() |
|||
}; |
|||
location.AddAdditional("TencentLocation", tencentLocationResponse.Result); |
|||
|
|||
return location; |
|||
} |
|||
|
|||
public virtual async Task<ReGeocodeLocation> ReGeocodeAsync(double lat, double lng, int radius = 1000) |
|||
{ |
|||
var requestParamters = new Dictionary<string, string> |
|||
{ |
|||
{ "callback", Options.Callback }, |
|||
{ "get_poi", Options.GetPoi }, |
|||
{ "key", Options.AccessKey }, |
|||
{ "location", $"{lat},{lng}" }, |
|||
{ "output", Options.Output }, |
|||
{ "poi_options", "radius=" + radius.ToString() } |
|||
}; |
|||
var tencentMapUrl = "https://apis.map.qq.com"; |
|||
var tencentMapPath = "/ws/geocoder/v1"; |
|||
if (!Options.SecretKey.IsNullOrWhiteSpace()) |
|||
{ |
|||
var sig = TencentSecretKeyCaculater.CalcSecretKey(tencentMapPath, Options.SecretKey, requestParamters); |
|||
requestParamters.Add("sig", sig); |
|||
} |
|||
var tencentLocationResponse = await GetTencentMapResponseAsync<TencentReGeocodeResponse>(tencentMapUrl, tencentMapPath, requestParamters); |
|||
var location = new ReGeocodeLocation |
|||
{ |
|||
Street = tencentLocationResponse.Result.AddressComponent.Street, |
|||
AdCode = tencentLocationResponse.Result.AddressInfo?.NationCode, |
|||
Address = tencentLocationResponse.Result.Address, |
|||
City = tencentLocationResponse.Result.AddressComponent.City, |
|||
Country = tencentLocationResponse.Result.AddressComponent.Nation, |
|||
District = tencentLocationResponse.Result.AddressComponent.District, |
|||
Number = tencentLocationResponse.Result.AddressComponent.StreetNumber, |
|||
Province = tencentLocationResponse.Result.AddressComponent.Province, |
|||
Town = tencentLocationResponse.Result.AddressReference.Town.Title, |
|||
Pois = tencentLocationResponse.Result.Pois.Select(p => new Poi |
|||
{ |
|||
Address = p.Address, |
|||
Name = p.Title, |
|||
Tag = p.Id, |
|||
Type = p.CateGory |
|||
}).ToList() |
|||
}; |
|||
location.AddAdditional("TencentLocation", tencentLocationResponse.Result); |
|||
|
|||
return location; |
|||
} |
|||
|
|||
protected virtual async Task<string> MakeRequestAndGetResultAsync(string url) |
|||
{ |
|||
var client = HttpClientFactory.CreateClient(TencentLocationHttpConsts.HttpClientName); |
|||
var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); |
|||
|
|||
var response = await client.SendAsync(requestMessage, GetCancellationToken()); |
|||
if (!response.IsSuccessStatusCode) |
|||
{ |
|||
throw new AbpException($"Tencent http request service returns error! HttpStatusCode: {response.StatusCode}, ReasonPhrase: {response.ReasonPhrase}"); |
|||
} |
|||
var resultContent = await response.Content.ReadAsStringAsync(); |
|||
|
|||
return resultContent; |
|||
} |
|||
|
|||
protected virtual CancellationToken GetCancellationToken() |
|||
{ |
|||
return CancellationTokenProvider.Token; |
|||
} |
|||
|
|||
protected virtual async Task<TResponse> GetTencentMapResponseAsync<TResponse>(string url, string path, IDictionary<string, string> paramters) |
|||
where TResponse : TencentLocationResponse |
|||
{ |
|||
var requestUrl = BuildRequestUrl(url, path, paramters); |
|||
var responseContent = await MakeRequestAndGetResultAsync(requestUrl); |
|||
var tencentLocationResponse = JsonSerializer.Deserialize<TResponse>(responseContent); |
|||
if (!tencentLocationResponse.IsSuccessed) |
|||
{ |
|||
if (Options.VisableErrorToClient) |
|||
{ |
|||
var localizerFactory = ServiceProvider.GetRequiredService<IStringLocalizerFactory>(); |
|||
var localizerErrorMessage = tencentLocationResponse.GetErrorMessage().Localize(localizerFactory); |
|||
var localizer = ServiceProvider.GetRequiredService<IStringLocalizer<TencentLocationResource>>(); |
|||
localizerErrorMessage = localizer["ResolveLocationFailed", localizerErrorMessage]; |
|||
throw new UserFriendlyException(localizerErrorMessage); |
|||
} |
|||
throw new AbpException($"Resolution address failed:{tencentLocationResponse.Message}!"); |
|||
} |
|||
return tencentLocationResponse; |
|||
} |
|||
|
|||
protected virtual string BuildRequestUrl(string uri, string path, IDictionary<string, string> paramters) |
|||
{ |
|||
var requestUrlBuilder = new StringBuilder(128); |
|||
requestUrlBuilder.Append(uri); |
|||
requestUrlBuilder.Append(path).Append("?"); |
|||
foreach (var paramter in paramters) |
|||
{ |
|||
requestUrlBuilder.AppendFormat("{0}={1}", paramter.Key, paramter.Value); |
|||
requestUrlBuilder.Append("&"); |
|||
} |
|||
requestUrlBuilder.Remove(requestUrlBuilder.Length - 1, 1); |
|||
return requestUrlBuilder.ToString(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace LINGYUN.Abp.Location.Tencent |
|||
{ |
|||
public class TencentLocationHttpConsts |
|||
{ |
|||
public const string HttpClientName = "TencentLocation"; |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
namespace LINGYUN.Abp.Location.Tencent |
|||
{ |
|||
public class TencentLocationOptions |
|||
{ |
|||
public string AccessKey { get; set; } |
|||
public string SecretKey { get; set; } |
|||
public string GetPoi { get; set; } = "1"; |
|||
public string Output { get; set; } = "JSON"; |
|||
public string Callback { get; set; } |
|||
public bool VisableErrorToClient { get; set; } = false; |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.Location.Tencent |
|||
{ |
|||
[Dependency(ServiceLifetime.Transient)] |
|||
[ExposeServices(typeof(ILocationResolveProvider))] |
|||
public class TencentLocationResolveProvider : ILocationResolveProvider |
|||
{ |
|||
protected TencentLocationHttpClient TencentLocationHttpClient { get; } |
|||
|
|||
public TencentLocationResolveProvider(TencentLocationHttpClient tencentLocationHttpClient) |
|||
{ |
|||
TencentLocationHttpClient = tencentLocationHttpClient; |
|||
} |
|||
|
|||
public virtual async Task<IPGecodeLocation> IPGeocodeAsync(string ipAddress) |
|||
{ |
|||
return await TencentLocationHttpClient.IPGeocodeAsync(ipAddress); |
|||
} |
|||
|
|||
public virtual async Task<ReGeocodeLocation> ReGeocodeAsync(double lat, double lng, int radius = 50) |
|||
{ |
|||
return await TencentLocationHttpClient.ReGeocodeAsync(lat, lng, radius); |
|||
} |
|||
|
|||
public virtual async Task<GecodeLocation> GeocodeAsync(string address, string city = null) |
|||
{ |
|||
return await TencentLocationHttpClient.GeocodeAsync(address, city); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace LINGYUN.Abp.Location.Tencent.Utils |
|||
{ |
|||
public class TencentSecretKeyCaculater |
|||
{ |
|||
private static string MD5(string password) |
|||
{ |
|||
try |
|||
{ |
|||
System.Security.Cryptography.HashAlgorithm hash = System.Security.Cryptography.MD5.Create(); |
|||
byte[] hash_out = hash.ComputeHash(Encoding.UTF8.GetBytes(password)); |
|||
|
|||
var md5_str = BitConverter.ToString(hash_out).Replace("-", ""); |
|||
return md5_str.ToLower(); |
|||
} |
|||
catch |
|||
{ |
|||
throw; |
|||
} |
|||
} |
|||
|
|||
private static string HttpBuildQuery(IDictionary<string, string> querystring_arrays) |
|||
{ |
|||
|
|||
StringBuilder sb = new StringBuilder(); |
|||
foreach (var item in querystring_arrays) |
|||
{ |
|||
sb.Append(item.Key); |
|||
sb.Append("="); |
|||
sb.Append(item.Value); |
|||
sb.Append("&"); |
|||
} |
|||
sb.Remove(sb.Length - 1, 1); |
|||
return sb.ToString(); |
|||
} |
|||
|
|||
public static string CalcSecretKey(string url, string secretKey, IDictionary<string, string> querystring_arrays) |
|||
{ |
|||
var queryString = HttpBuildQuery(querystring_arrays); |
|||
|
|||
return MD5(url + "?" + queryString + secretKey); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.Location |
|||
{ |
|||
public class IPGecodeLocation |
|||
{ |
|||
/// <summary>
|
|||
/// IP地址
|
|||
/// </summary>
|
|||
public string IpAddress { get; set; } |
|||
/// <summary>
|
|||
/// 定位坐标
|
|||
/// </summary>
|
|||
public Location Location { get; set; } = new Location(); |
|||
/// <summary>
|
|||
/// 国家
|
|||
/// </summary>
|
|||
public string Country { get; set; } |
|||
/// <summary>
|
|||
/// 城市
|
|||
/// </summary>
|
|||
public string City { get; set; } |
|||
/// <summary>
|
|||
/// 省份
|
|||
/// </summary>
|
|||
public string Province { get; set; } |
|||
/// <summary>
|
|||
/// 区县
|
|||
/// </summary>
|
|||
public string District { get; set; } |
|||
/// <summary>
|
|||
/// adcode
|
|||
/// </summary>
|
|||
public string AdCode { get; set; } |
|||
|
|||
public IDictionary<string, object> Additionals { get; } = new Dictionary<string, object>(); |
|||
|
|||
public void AddAdditional(string key, object value) |
|||
{ |
|||
Additionals.Add(key, value); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue