浏览代码

Merge branch 'master' of http://git.ius.plus/Project-Wing/WingCloudServer

Jeremy 2 年之前
父节点
当前提交
a0360e912b

+ 17 - 0
src/AutoMapperProfile.cs

@@ -0,0 +1,17 @@
+using AutoMapper;
+using WingInterfaceLibrary.DTO.Device;
+using WingInterfaceLibrary.DTO.DistributedServerInfo;
+using WingInterfaceLibrary.DTO.User;
+using WingServerCommon.Interfaces.Cache;
+
+namespace WingCloudServer
+{
+    public class AutoMapperProfile : Profile
+    {   
+         public AutoMapperProfile()
+         {
+            CreateMap<CacheIPAddressInfoDTO, IPAddressInfoDTO>().ReverseMap();
+             
+         }
+    }
+}

+ 9 - 22
src/InteractionCenter/VinnoServerService.cs

@@ -14,6 +14,7 @@ using WingServerCommon.Config;
 using WingServerCommon.Config.Parameters;
 using System.Net;
 using Newtonsoft.Json;
+using WingServerCommon.Mapper;
 
 namespace WingCloudServer.InteractionCenter
 {
@@ -31,7 +32,7 @@ namespace WingCloudServer.InteractionCenter
         private ITokenDBService _tokenDBService;
 
         /// <summary>
-        /// 默认构造
+        /// 默认构é€
         /// </summary>
         public VinnoServerService()
         {
@@ -96,6 +97,7 @@ namespace WingCloudServer.InteractionCenter
                             Enable = d.Enable,
                             ServerType = (int)d.ServerType,
                             ServerUrl = d.ServerUrl,
+                            AssignClientIPList = d.AssignClientIPList.MappingTo<List<CacheIPAddressInfoDTO>>()
                         }).ToList();
                     }
                 }
@@ -118,6 +120,7 @@ namespace WingCloudServer.InteractionCenter
                             Enable = d.Enable,
                             ServerType = (int)d.ServerType,
                             ServerUrl = d.ServerUrl,
+                            AssignClientIPList = d.AssignClientIPList.MappingTo<List<CacheIPAddressInfoDTO>>()
                         }).ToList();
                     }
                 }
@@ -130,7 +133,7 @@ namespace WingCloudServer.InteractionCenter
         }
 
         /// <summary>
-        /// åˆ�始åŒ
+        /// åˆ�始åŒ
         /// </summary>
         public override void Load(JsonRpcClientPool jsonRpcClientPool)
         {
@@ -142,7 +145,7 @@ namespace WingCloudServer.InteractionCenter
         }
 
         /// <summary>
-        /// 加载ServerInfo到内存
+        /// 加载ServerInfo到内å­
         /// </summary>
         /// <returns></returns>
         private void LoadDBServerInfo()
@@ -215,24 +218,8 @@ namespace WingCloudServer.InteractionCenter
         {
             //这里应该是批�查询,请求�数�改
             var list = new List<ServerInfoDTO>();
-            var serverInformationList = new List<CacheDistributedServerInfosDTO>();
-            if (request.Codes != null && request.Codes.Any())
-            {
-                foreach (var item in request.Codes)
-                {
-                    var entity = CacheMaintenance.Instance.Get<IDistributedServerInfosManager>().Get(item);
-                    if (entity != null && !string.IsNullOrEmpty(entity.Code))
-                    {
-                        serverInformationList.Add(entity);
-                    }
-                }
-
-            }
-            else
-            {
-                serverInformationList = CacheMaintenance.Instance.Get<IDistributedServerInfosManager>()
-                .Where(x => !string.IsNullOrEmpty(x.Name) && !string.IsNullOrEmpty(x.ServerUrl))?.ToList();
-            }
+            var serverInformationList = CacheMaintenance.Instance.Get<IDistributedServerInfosManager>()
+            .Where(x => !string.IsNullOrEmpty(x.Name) && !string.IsNullOrEmpty(x.ServerUrl))?.ToList();
             if (serverInformationList != null && serverInformationList.Any())
             {
                 list = serverInformationList.Select(c => new ServerInfoDTO()
@@ -245,7 +232,7 @@ namespace WingCloudServer.InteractionCenter
         }
 
         /// <summary>
-        /// 测速接� 
+        /// 测速接å�
         /// </summary>
         /// <returns></returns>
         public async Task<EchoResult> EchoAsync()

+ 387 - 0
src/Plugin/ServerListPlugin.cs

@@ -0,0 +1,387 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using JsonRpcLite.Network;
+using JsonRpcLite.Services;
+using JsonRpcLite.Utilities;
+using Newtonsoft.Json;
+using WingInterfaceLibrary.DTO.ServerInfo;
+using WingInterfaceLibrary.DTO.User;
+using WingInterfaceLibrary.Enum;
+using WingInterfaceLibrary.Interface;
+using WingInterfaceLibrary.Request.Authentication;
+using WingInterfaceLibrary.Result.Device;
+using WingServerCommon.Interfaces.Cache;
+using WingServerCommon.Log;
+using WingServerCommon.Service;
+
+namespace WingCloudServer.Plugin
+{
+    /// <summary>
+    /// 最快服务器列表Plugin接口
+    /// </summary>
+    public interface IServerListPlugin : IJsonRpcHttpServerEnginePlugin
+    {
+
+    }
+
+    /// <summary>
+    /// 最快服务器列表Plugin
+    /// </summary>
+    public class ServerListPlugin : JsonRpcService, IServerListPlugin
+    {
+        //地球半径,单位米
+        private const double EARTH_RADIUS = 6378137;
+        protected IAuthenticationService _authenticationService;
+        private string _apiName = string.Empty;
+
+        /// <summary>
+        /// 初始化
+        /// </summary>
+        public override void Load(JsonRpcClientPool jsonRpcClientPool)
+        {
+            base.Load(jsonRpcClientPool);
+            _authenticationService = GetProxy<IAuthenticationService>();
+        }
+
+        /// <summary>
+        /// 前处理
+        /// </summary>
+        /// <param name="context">上下文</param>
+        /// <param name="requestData">请求参数</param>
+        /// <returns></returns>
+        public PluginProcessResult PreProcess(IJsonRpcHttpContext context, byte[] requestData)
+        {
+            var dataLength = (int)context.GetRequestContentLength();
+            var requests = JsonRpcCodec.DecodeRequestsAsync(requestData, new System.Threading.CancellationToken(), dataLength).Result;
+            _apiName = $"{context.GetRequestPath().Trim('/')}/{requests[0].Method.Trim('/')}";
+            return new PluginProcessResult(requestData, false);
+        }
+
+        /// <summary>
+        /// 后处理
+        /// </summary>
+        /// <param name="context">上下文</param>
+        /// <param name="responseData">响应参数</param>
+        /// <returns></returns>
+        public PluginProcessResult PostProcess(IJsonRpcHttpContext context, byte[] responseData)
+        {
+            try
+            {
+                var ipAddress = context.RemoteEndPoint?.Address.ToString();
+                var ipLongAddress = IpToLong(ipAddress);
+                var serverInformationList = CacheMaintenance.Instance.Get<IDistributedServerInfosManager>()
+                .Where(x => !string.IsNullOrEmpty(x.Name) && !string.IsNullOrEmpty(x.ServerUrl))?.ToList();
+                var fasterServerInfo = new ServerInfoDTO();
+                var parallelOption = new ParallelOptions() { MaxDegreeOfParallelism = 4 };
+                //本地IP段检索
+                Parallel.ForEach(serverInformationList, parallelOption, (item, parallelLoopState) => {
+                    if (item.AssignClientIPList != null && item.AssignClientIPList.Any()) {
+                        var ipInfo = item.AssignClientIPList.Find(c => c.LongStartIP >= ipLongAddress && c.LongEndIP <= ipLongAddress);
+                        if (ipInfo != null && !string.IsNullOrEmpty(ipInfo.StartIp)) {
+                            //本地库找到此IP
+                            fasterServerInfo = new ServerInfoDTO() {
+                                Name = item.Name,
+                                Host = item.ServerUrl
+                            };
+                            parallelLoopState.Break();
+                            return;
+                        }
+                    }
+                });
+                List<ServerInfoDTO> list = new List<ServerInfoDTO>();
+                if (fasterServerInfo != null && !string.IsNullOrEmpty(fasterServerInfo.Name)) {
+                    //找到,直接返回
+                    list.Add(fasterServerInfo);
+                    //添加其他服务器
+                    var otherServerList = serverInformationList.FindAll(c => c.Name != fasterServerInfo.Name).Select(c => new ServerInfoDTO() {
+                        Name = c.Name,
+                        Host = c.ServerUrl,
+                    }).ToList();
+                    list.AddRange(otherServerList);
+                }
+                else {
+                    //没找到,定位,计算,更新,返回
+                    var locationInfo = GetIpLocationInfo(ipAddress);
+                    if (locationInfo.ad_info != null && !string.IsNullOrEmpty(locationInfo.ad_info.nation)) {
+                        if (locationInfo.ad_info.nation == "中国") {
+                            //最快指向北京,
+                            var fastServerInfo = serverInformationList.FindAll(c => c.Name.Contains("北京") || c.Name.Contains("BJ"))?.Select(c => new ServerInfoDTO() {
+                                Name = c.Name,
+                                Host = c.ServerUrl,
+                            })?.FirstOrDefault() ?? new ServerInfoDTO();
+                            list.Add(fastServerInfo);
+                            //添加其他服务器
+                            var otherServerList = serverInformationList.FindAll(c => c.Name != fastServerInfo.Name).Select(c => new ServerInfoDTO() {
+                                Name = c.Name,
+                                Host = c.ServerUrl,
+                            }).ToList();
+                            list.AddRange(otherServerList);
+                        }
+                        else 
+                        {
+                            var extendList = new List<ServerInfoExtend>();
+                            foreach(var item in serverInformationList) {
+                                //计算最短距离
+                                // double beelineDistanceTemp = GetDistance(locationInfo.location.lat, locationInfo.location.lng, item.lat, item.lng);
+                                // var extendInfo = new ServerInfoExtend() {
+                                //     Name = item.Name,
+                                //     Host = item.ServerUrl,
+                                //     BeelineDistance = beelineDistanceTemp
+                                // };
+                                // extendList.Add(extendInfo);
+                            }
+                            list = extendList.OrderBy(c => c.BeelineDistance).Select(e => new ServerInfoDTO() {
+                                Name = e.Name,
+                                Host = e.Host,
+                            }).ToList();
+                        }
+                    }
+                    else {
+                        //啥也没找到,默认优先香港服务器[主服务器]
+                        list = serverInformationList.OrderBy(c => c.IsMaster).Select(e => new ServerInfoDTO() {
+                            Name = e.Name,
+                            Host = e.ServerUrl,
+                        }).ToList();
+                    }
+                }
+                
+                //todo 处理输出数据
+            }
+            catch (Exception ex)
+            {
+                Logger.WriteLineInfo($"IPAddressPlugin failed, api name:{_apiName}, ex:{ex}");
+            }
+            return new PluginProcessResult(responseData, false);
+        }
+    
+        /// <summary>
+        /// ip地址转long
+        /// </summary>
+        /// <param name="ipAddress"></param>
+        /// <returns></returns>
+        private long IpToLong(string ipAddress)
+        {
+            byte[] byts = IPAddress.Parse(ipAddress).GetAddressBytes();
+            Array.Reverse(byts); // 需要倒置一次字节序
+            long ipLong = BitConverter.ToUInt32(byts, 0);
+            return ipLong;
+        }
+
+        /// <summary>
+        /// 计算两点位置的距离,返回两点的距离,单位 米
+        /// 该公式为GOOGLE提供,误差小于0.2米
+        /// </summary>
+        /// <param name="lat1">第一点纬度</param>
+        /// <param name="lng1">第一点经度</param>
+        /// <param name="lat2">第二点纬度</param>
+        /// <param name="lng2">第二点经度</param>
+        /// <returns></returns>
+        public double GetDistance(double lat1, double lng1, double lat2, double lng2)
+        {
+            double radLat1 = Rad(lat1);
+            double radLng1 = Rad(lng1);
+            double radLat2 = Rad(lat2);
+            double radLng2 = Rad(lng2);
+            double a = radLat1 - radLat2;
+            double b = radLng1 - radLng2;
+            double result = 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin(a / 2), 2) + Math.Cos(radLat1) * Math.Cos(radLat2) * Math.Pow(Math.Sin(b / 2), 2))) * EARTH_RADIUS;
+            return result;
+        }
+ 
+        /// <summary>
+        /// 经纬度转化成弧度
+        /// </summary>
+        /// <param name="d"></param>
+        /// <returns></returns>
+        private double Rad(double d)
+        {
+            return (double)d * Math.PI / 180d;
+        }
+
+        /// <summary>
+        /// 获取ip的定位信息
+        /// </summary>
+        /// <param name="d"></param>
+        /// <returns></returns>
+        private IPServiceResult GetIpLocationInfo(string ip) {
+            IPServiceResult info = new IPServiceResult();
+            string key = "4Z3BZ-L5RK6-MJ5SC-EXNFA-3V7PH-RSB3U";//此处要改成配置
+            var url = $"https://apis.map.qq.com/ws/location/v1/ip?ip={ip}&key={key}";
+            try
+            {
+                var json = HttpGet(url);
+                if (!string.IsNullOrEmpty(json)) {
+                    var serviceGuideInfo = JsonConvert.DeserializeObject<IPServiceGuide>(json);
+                    if (serviceGuideInfo != null && serviceGuideInfo.status == 0 && serviceGuideInfo.result != null) 
+                    {
+                        info = serviceGuideInfo.result;
+                    }
+                }
+            }
+            catch(Exception ex) {
+                //异常处理
+            }
+            return info;
+        }
+
+        /// <summary>
+        /// 处理http GET请求,返回数据
+        /// </summary>
+        /// <param name="url">请求的url地址</param>
+        /// <returns>http GET成功后返回的数据,失败抛WebException异常</returns>
+        public static string HttpGet(string url)
+        {
+            System.GC.Collect();
+            string result = "";
+
+            HttpWebRequest request = null;
+            HttpWebResponse response = null;
+
+            //请求url以获取数据
+            try
+            {
+                //设置最大连接数
+                ServicePointManager.DefaultConnectionLimit = 40;
+
+                /***************************************************************
+                * 下面设置HttpWebRequest的相关属性
+                * ************************************************************/
+                request = (HttpWebRequest)WebRequest.Create(url);
+
+                request.Method = "GET";
+                
+                //获取服务器返回
+                response = (HttpWebResponse)request.GetResponse();
+
+                //获取HTTP返回数据
+                StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
+                result = sr.ReadToEnd().Trim();
+                sr.Close();
+            }
+            catch (Exception e)
+            {
+                throw e;
+            }
+            finally
+            {
+                //关闭连接和流
+                if (response != null)
+                {
+                    response.Close();
+                }
+                if (request != null)
+                {
+                    request.Abort();
+                }
+            }
+            return result;
+        }
+    }
+
+    /// <summary>
+    /// 腾讯云定位响应实体
+    /// </summary>
+    class IPServiceGuide
+    {
+        /// <summary>
+        /// 状态码;正常为0
+        /// </summary>
+        public int status { get; set;}
+
+        /// <summary>
+        /// 响应信息
+        /// </summary>
+        public string message { get; set;}
+
+        /// <summary>
+        /// 响应结果
+        /// </summary>
+        public IPServiceResult result { get; set;}
+    }
+
+    /// <summary>
+    /// 腾讯云定位响应结果
+    /// </summary>
+    class IPServiceResult
+    {
+        /// <summary>
+        /// ip地址
+        /// </summary>
+        public string ip { get; set;}
+
+        /// <summary>
+        /// 经纬度信息
+        /// </summary>
+        public IPServiceLocationResult location { get; set;}
+
+        /// <summary>
+        /// 地区信息
+        /// </summary>
+        public IPServiceNationResult ad_info { get; set;}
+    }
+
+    /// <summary>
+    /// 腾讯云定位经纬度信息
+    /// </summary>
+    class IPServiceLocationResult
+    {
+        /// <summary>
+        /// 纬度
+        /// </summary>
+        public string lat { get; set;}
+
+        /// <summary>
+        /// 经度
+        /// </summary>
+        public string lng { get; set;}
+    }
+
+    /// <summary>
+    /// 腾讯云定位地区信息
+    /// </summary>
+    class IPServiceNationResult
+    {
+        /// <summary>
+        /// 国家
+        /// </summary>
+        public string nation { get; set;}
+
+        /// <summary>
+        /// 省份
+        /// </summary>
+        public string province { get; set;}
+
+        /// <summary>
+        /// 城市
+        /// </summary>
+        public string city { get; set;}
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public string district { get; set;}
+
+        /// <summary>
+        /// 编码
+        /// </summary>
+        public string adcode { get; set;}
+    }
+
+    /// <summary>
+    /// 最快服务器扩展
+    /// </summary>
+    class ServerInfoExtend : ServerInfoDTO
+    {
+        /// <summary>
+        /// 最短距离
+        /// </summary>
+        public double BeelineDistance { get; set;}
+    }
+}

+ 10 - 0
src/Resource/Languge/en-US/emailTemplate.json

@@ -0,0 +1,10 @@
+{
+    "Register": {
+        "Subject": "杏聆荟|验证码",
+        "Body": "【杏聆荟】您的验证码为:{1},5分钟内有效,请勿将验证码告知他人,以防止导致账号被盗,谢谢!"
+    },
+    "ShareReport": {
+        "Subject": "杏聆荟|超声报告",
+        "Body": "【杏聆荟】您的报告已经生成,您可以访问{1},并通过报告码{2}查看。"
+    }
+}

+ 10 - 0
src/Resource/Languge/zh-CN/emailTemplate.json

@@ -0,0 +1,10 @@
+{
+    "Register": {
+        "Subject": "杏聆荟|验证码",
+        "Body": "【杏聆荟】您的验证码为:{1},5分钟内有效,请勿将验证码告知他人,以防止导致账号被盗,谢谢!"
+    },
+    "ShareReport": {
+        "Subject": "杏聆荟|超声报告",
+        "Body": "【杏聆荟】您的报告已经生成,您可以访问{1},并通过报告码{2}查看。"
+    }
+}