feat: 添加部分修改

This commit is contained in:
g17681086218 2025-10-13 10:15:36 +08:00
parent 54c60ab607
commit 3916aa0bab
10 changed files with 1275 additions and 45 deletions

View File

@ -16,7 +16,7 @@ services.AddSwaggerGen();
services.AddHangfire(configuration =>
{
configuration.UseConsole();
configuration.UseRedisStorage("172.16.127.100:34188,defaultDatabase=15,connectTimeout=100000,syncTimeout=100000,connectRetry=50", new RedisStorageOptions { Db = 10 });
configuration.UseRedisStorage("192.168.1.148:6379,defaultDatabase=15,connectTimeout=100000,syncTimeout=100000,connectRetry=50", new RedisStorageOptions { Db = 15 });
});
services.AddHangfireServer(option =>
{

View File

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace RedisDemo.Controller
{
[Route("api/[controller]")]
[ApiController]
public class TestController : ControllerBase
{
[HttpGet]
public async Task<string> Test(string str) {
Console.WriteLine($"这是参数:{str}");
return "这是回参";
}
}
}

View File

@ -8,7 +8,7 @@
"applicationUrl": "http://localhost:5030;http://0.0.0.0:5030;",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"Redis:Connection": "172.16.127.100:34188,defaultDatabase=14,connectTimeout=100000,syncTimeout=100000,connectRetry=50"
"Redis:Connection": "192.168.1.148:6379,Password=abc-123,defaultDatabase=14,connectTimeout=100000,syncTimeout=100000,connectRetry=50"
}
}
}

485
SelfUseUtil/AMap.cs Normal file
View File

@ -0,0 +1,485 @@
using NewLife.Data;
using NewLife.Map.Models;
using NewLife.Map;
using NewLife;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using NewLife.Serialization;
using SelfUseUtil.Model;
namespace SelfUseUtil
{
public class AMap : Map, IMap
{
private readonly string[] _KeyWords = new string[4] { "TOO_FREQUENT", "LIMIT", "NOMATCH", "RECYCLED" };
//
// 摘要:
// 高德地图
public AMap()
{
base.Server = "http://restapi.amap.com";
base.KeyName = "key";
}
//
// 摘要:
// 远程调用
//
// 参数:
// url:
// 目标Url
//
// result:
// 结果字段
protected override async Task<T> InvokeAsync<T>(string url, string? result)
{
IDictionary<string, object> dictionary = await base.InvokeAsync<IDictionary<string, object>>(url, result);
if (dictionary == null || dictionary.Count == 0)
{
return null;
}
if (dictionary["status"].ToInt() != 1)
{
string text = dictionary["info"]?.ToString() ?? "";
if (!base.LastKey.IsNullOrEmpty() && IsValidKey(text))
{
RemoveKey(base.LastKey, DateTime.Now.AddHours(1.0));
}
if (base.ThrowException)
{
throw new Exception(text);
}
return null;
}
if (result.IsNullOrEmpty())
{
return (T)dictionary;
}
// 👉 把 dictionary[result] 转为 JSON 再反序列化
var json = Newtonsoft.Json.JsonConvert.SerializeObject(dictionary[result]);
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(json);
}
//
// 摘要:
// 查询地址的经纬度坐标
//
// 参数:
// address:
//
// city:
//
// coordtype:
protected async Task<IDictionary<string, object>?> GetGeocoderAsync(string address, string? city = null, string? coordtype = null)
{
if (address.IsNullOrEmpty())
{
throw new ArgumentNullException("address");
}
address = HttpUtility.UrlEncode(address);
city = HttpUtility.UrlEncode(city);
string url = "http://restapi.amap.com/v3/geocode/geo?address=" + address + "&city=" + city + "&output=json";
return (await InvokeAsync<IList<object>>(url, "geocodes"))?.FirstOrDefault() as IDictionary<string, object>;
}
//
// 摘要:
// 查询地址获取坐标
//
// 参数:
// address:
// 地址
//
// city:
// 城市
//
// coordtype:
//
// formatAddress:
// 是否格式化地址。高德地图默认已经格式化地址
public async Task<GeoAddress?> GetGeoAsync(string address, string? city = null, string? coordtype = null, bool formatAddress = false)
{
IDictionary<string, object> rs = await GetGeocoderAsync(address, city);
if (rs == null || rs.Count == 0)
{
return null;
}
GeoAddress geo = new GeoAddress
{
Location = new GeoPoint(rs["location"] as string)
};
rs.Remove("location");
new JsonReader().ToObject(rs, null, geo);
geo.Code = rs["adcode"].ToInt();
if (rs["township"] is IList<object> list && list.Count > 0)
{
geo.Township = list[0]?.ToString() ?? "";
}
if (rs["number"] is IList<object> list2 && list2.Count > 0)
{
geo.StreetNumber = list2[0]?.ToString() ?? "";
}
if (formatAddress)
{
GeoAddress geoAddress = await GetReverseGeoAsync(geo.Location, "gcj02");
if (geoAddress != null)
{
geo = geoAddress;
if (geo.Level.IsNullOrEmpty())
{
geo.Level = rs["level"]?.ToString() ?? "";
}
}
}
string text = rs["formatted_address"]?.ToString() ?? "";
if (!text.IsNullOrEmpty() && (geo.Address.IsNullOrEmpty() || geo.Address.Length < text.Length))
{
geo.Address = text;
}
TrimAddress(geo);
return geo;
}
private static void TrimAddress(GeoAddress geo)
{
if (!geo.Address.IsNullOrEmpty())
{
geo.Address = geo.Address.Replace("|", null);
}
}
//
// 摘要:
// 根据坐标获取地址
//
// 参数:
// point:
//
// coordtype:
//
// 言论:
// http://lbs.amap.com/api/webservice/guide/api/georegeo/#regeo
protected async Task<IDictionary<string, object>> GetReverseGeocoderAsync(GeoPoint point, string? coordtype)
{
if (point.Longitude < 0.1 || point.Latitude < 0.1)
{
throw new ArgumentNullException("point");
}
string url = $"http://restapi.amap.com/v3/geocode/regeo?location={point.Longitude},{point.Latitude}&extensions=base&output=json";
return await InvokeAsync<IDictionary<string, object>>(url, "regeocode");
}
//
// 摘要:
// 根据坐标获取地址
//
// 参数:
// point:
//
// coordtype:
// 坐标系
public async Task<GeoAddress?> GetReverseGeoAsync(GeoPoint point, string? coordtype)
{
IDictionary<string, object> dictionary = await GetReverseGeocoderAsync(point, coordtype);
if (dictionary == null || dictionary.Count == 0)
{
return null;
}
GeoAddress geoAddress = new GeoAddress
{
Address = (dictionary["formatted_address"]?.ToString() ?? ""),
Location = point
};
if (dictionary["addressComponent"] is IDictionary<string, object> dictionary2)
{
new JsonReader().ToObject(dictionary2, null, geoAddress);
if (dictionary2.TryGetValue("city", out var value) && !(value is string))
{
geoAddress.City = null;
}
if (dictionary2.TryGetValue("streetNumber", out value) && !(value is string))
{
geoAddress.StreetNumber = null;
}
geoAddress.Code = dictionary2["adcode"].ToInt();
geoAddress.Township = null;
geoAddress.Towncode = 0;
string text = "";
if (dictionary2["township"] is string township)
{
geoAddress.Township = township;
}
if (dictionary2["towncode"] is string text2)
{
text = text2;
}
if (!text.IsNullOrEmpty() && text.Length > 9)
{
text = text.TrimEnd("000");
}
geoAddress.Towncode = text.ToInt();
if (dictionary2["streetNumber"] is IDictionary<string, object> dictionary3 && dictionary3.Count > 0)
{
if (dictionary3["street"] is IList<object> list && list.Count > 0)
{
geoAddress.Street = list[0]?.ToString() ?? "";
}
else
{
geoAddress.Street = dictionary3["street"]?.ToString() ?? "";
}
if (dictionary3["number"] is IList<object> list2 && list2.Count > 0)
{
geoAddress.StreetNumber = list2[0]?.ToString() ?? "";
}
else
{
geoAddress.StreetNumber = dictionary3["number"]?.ToString() ?? "";
}
if (geoAddress.Title.IsNullOrEmpty())
{
if (!dictionary3.TryGetValue("direction", out var value2))
{
value2 = "";
}
if (dictionary3.TryGetValue("distance", out var value3))
{
value3 = Math.Round(value3.ToDouble(), 0) + "米";
}
geoAddress.Title = $"{geoAddress.Province}{geoAddress.City}{geoAddress.District}{geoAddress.Township}{geoAddress.Street}{geoAddress.StreetNumber}{value2}{value3}";
}
}
}
geoAddress.Location = point;
TrimAddress(geoAddress);
return geoAddress;
}
//
// 摘要:
// 计算距离和驾车时间
//
// 参数:
// origin:
//
// destination:
//
// coordtype:
//
// type:
// 路径计算的方式和方法
//
// 言论:
// http://lbs.amap.com/api/webservice/guide/api/direction type: 0直线距离 1驾车导航距离仅支持国内坐标
// 必须指出当为1时会考虑路况故在不同时间请求返回结果可能不同。 此策略和driving接口的 strategy = 4策略一致 2公交规划距离仅支持同城坐标
// 3步行规划距离仅支持5km之间的距离 distance 路径距离,单位:米 duration 预计行驶时间,单位:秒
public async Task<Driving?> GetDistanceAsync(GeoPoint origin, GeoPoint destination, string? coordtype, int type = 1)
{
if (origin == null || (origin.Longitude < 1.0 && origin.Latitude < 1.0))
{
throw new ArgumentNullException("origin");
}
if (destination == null || (destination.Longitude < 1.0 && destination.Latitude < 1.0))
{
throw new ArgumentNullException("destination");
}
if (type <= 0)
{
type = 1;
}
string url = $"http://restapi.amap.com/v3/distance?origins={origin.Longitude},{origin.Latitude}&destination={destination.Longitude},{destination.Latitude}&type={type}&output=json";
IList<object> list = await InvokeAsync<IList<object>>(url, "results");
if (list == null || list.Count == 0)
{
return null;
}
if (!(list.FirstOrDefault() is IDictionary<string, object> dictionary))
{
return null;
}
return new Driving
{
Distance = dictionary["distance"].ToInt(),
Duration = dictionary["duration"].ToInt()
};
}
//
// 摘要:
// 计算距离和驾车时间
//
// 参数:
// origin:
//
// destination:
//
// coordtype:
//
// type:
// 路径计算的方式和方法
//
// 言论:
// http://lbs.amap.com/api/webservice/guide/api/direction type: 0直线距离 1驾车导航距离仅支持国内坐标
// 必须指出当为1时会考虑路况故在不同时间请求返回结果可能不同。 此策略和driving接口的 strategy = 4策略一致 2公交规划距离仅支持同城坐标
// 3步行规划距离仅支持5km之间的距离 distance 路径距离,单位:米 duration 预计行驶时间,单位:秒
public async Task<DrivingEx?> GetDistanceExAsync(GeoPoint origin, GeoPoint destination, string? coordtype, int type = 1)
{
if (origin == null || (origin.Longitude < 1.0 && origin.Latitude < 1.0))
{
throw new ArgumentNullException("origin");
}
if (destination == null || (destination.Longitude < 1.0 && destination.Latitude < 1.0))
{
throw new ArgumentNullException("destination");
}
if (type <= 0)
{
type = 1;
}
string url = $"https://restapi.amap.com/v5/direction/driving?origin={origin.Longitude},{origin.Latitude}&destination={destination.Longitude},{destination.Latitude}&key=c7591c54f7443323b41ec4c0f315acf5&output=json&show_fields=cost,polyline";
var route = await InvokeAsync<Route>(url, "route");
var list = route.paths;
if (route.paths == null || route.paths.Count == 0)
{
return null;
}
return new DrivingEx
{
Distance = route.paths[0].distance.ToInt(),
Duration = route.paths[0].cost.duration.ToInt(),
Steps = route.paths[0].steps
};
}
//
// 摘要:
// 行政区划
//
// 参数:
// keywords:
// 查询关键字
//
// subdistrict:
// 设置显示下级行政区级数
//
// code:
// 按照指定行政区划进行过滤,填入后则只返回该省/直辖市信息
//
// 言论:
// http://lbs.amap.com/api/webservice/guide/api/district
public async Task<IList<GeoArea>> GetAreaAsync(string keywords, int subdistrict = 1, int code = 0)
{
if (keywords.IsNullOrEmpty())
{
throw new ArgumentNullException("keywords");
}
keywords = HttpUtility.UrlEncode(keywords);
string url = $"http://restapi.amap.com/v3/config/district?keywords={keywords}&subdistrict={subdistrict}&filter={code}&extensions=base&output=json";
IList<object> list = await InvokeAsync<IList<object>>(url, "districts");
if (list == null || list.Count == 0)
{
return new List<GeoArea>();
}
if (!(list.FirstOrDefault() is IDictionary<string, object> geo))
{
return new List<GeoArea>();
}
return GetArea(geo, 0);
}
private IList<GeoArea> GetArea(IDictionary<string, object> geo, int parentCode)
{
if (geo == null || geo.Count == 0)
{
return new List<GeoArea>();
}
List<GeoArea> list = new List<GeoArea>();
GeoArea geoArea = new GeoArea();
new JsonReader().ToObject(geo, null, geoArea);
geoArea.Code = geo["adcode"].ToInt();
if (parentCode > 0)
{
geoArea.ParentCode = parentCode;
}
list.Add(geoArea);
if (geo["districts"] is IList<object> list2 && list2.Count > 0)
{
foreach (object item in list2)
{
if (item is IDictionary<string, object> geo2)
{
IList<GeoArea> area = GetArea(geo2, geoArea.Code);
if (area != null && area.Count > 0)
{
list.AddRange(area);
}
}
}
}
return list;
}
//
// 摘要:
// 是否无效Key。可能禁用或超出限制
//
// 参数:
// result:
protected override bool IsValidKey(string result)
{
if (result.IsNullOrEmpty())
{
return false;
}
if (_KeyWords.Any(result.Contains))
{
return true;
}
return base.IsValidKey(result);
}
}
}

542
SelfUseUtil/BaiduMap.cs Normal file
View File

@ -0,0 +1,542 @@
using NewLife.Data;
using NewLife.Map.Models;
using NewLife.Map;
using NewLife;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using NewLife.Serialization;
namespace SelfUseUtil
{
//
// 摘要:
// 百度地图
//
// 言论:
// 参考手册 https://lbsyun.baidu.com/index.php?title=webapi
[DisplayName("百度地图")]
public class BaiduMap : Map, IMap
{
private static readonly List<string> _coordTypes = new List<string> { "", "wgs84ll", "sougou", "gcj02ll", "gcj02mc", "bd09ll", "bd09mc", "amap", "gps" };
//
// 摘要:
// 高德地图
public BaiduMap()
{
base.Server = "https://api.map.baidu.com";
base.KeyName = "ak";
}
//
// 摘要:
// 远程调用
//
// 参数:
// url:
// 目标Url
//
// result:
// 结果字段
protected override async Task<T> InvokeAsync<T>(string url, string? result)
{
IDictionary<string, object> dictionary = await base.InvokeAsync<IDictionary<string, object>>(url, result);
if (dictionary == null || dictionary.Count == 0)
{
return null;
}
int num = dictionary["status"].ToInt();
if (num != 0)
{
string text = (dictionary["msg"] ?? dictionary["message"])?.ToString() ?? "";
if (num >= 200 || IsValidKey(text))
{
RemoveKey(base.LastKey, DateTime.Now.AddHours(1.0));
}
if (base.ThrowException)
{
throw new Exception(text);
}
return null;
}
if (result.IsNullOrEmpty())
{
return (T)dictionary;
}
return (T)dictionary[result];
}
//
// 摘要:
// 查询地址的经纬度坐标
//
// 参数:
// address:
//
// city:
//
// coordtype:
//
// 言论:
// 参考手册 https://lbsyun.baidu.com/index.php?title=webapi/guide/webservice-geocoding
protected async Task<IDictionary<string, object>> GetGeocoderAsync(string address, string? city = null, string? coordtype = null)
{
if (address.IsNullOrEmpty())
{
throw new ArgumentNullException("address");
}
address = HttpUtility.UrlEncode(address);
city = HttpUtility.UrlEncode(city);
string url = "/geocoding/v3/?address=" + address + "&city=" + city + "&ret_coordtype=" + coordtype + "&extension_analys_level=1&output=json";
return await InvokeAsync<IDictionary<string, object>>(url, "result");
}
//
// 摘要:
// 查询地址获取坐标
//
// 参数:
// address:
// 地址
//
// city:
// 城市
//
// coordtype:
//
// formatAddress:
// 是否格式化地址
public async Task<GeoAddress?> GetGeoAsync(string address, string? city = null, string? coordtype = null, bool formatAddress = false)
{
IDictionary<string, object> rs = await GetGeocoderAsync(address, city, coordtype);
if (rs == null || rs.Count == 0)
{
return null;
}
if (!(rs["location"] is IDictionary<string, object> dictionary) || dictionary.Count < 2)
{
return null;
}
GeoAddress geo = new GeoAddress
{
Location = new GeoPoint(dictionary["lng"], dictionary["lat"])
};
if (formatAddress)
{
GeoAddress geoAddress = await GetReverseGeoAsync(geo.Location, coordtype);
if (geoAddress != null)
{
geo = geoAddress;
}
}
geo.Precise = rs["precise"].ToBoolean();
geo.Confidence = rs["confidence"].ToInt();
geo.Comprehension = rs["comprehension"].ToInt();
geo.Level = rs["level"]?.ToString() ?? "";
return geo;
}
//
// 摘要:
// 根据坐标获取地址
//
// 参数:
// point:
//
// coordtype:
// 坐标系
//
// 言论:
// 参考手册 https://lbsyun.baidu.com/index.php?title=webapi/guide/webservice-geocoding-abroad
protected async Task<IDictionary<string, object>> GetReverseGeocoderAsync(GeoPoint point, string? coordtype)
{
if (point == null || point.Longitude == 0.0 || point.Latitude == 0.0)
{
throw new ArgumentNullException("point");
}
string url = $"/reverse_geocoding/v3/?location={point.Latitude},{point.Longitude}&extensions_poi=1&extensions_town=true&coordtype={coordtype}&output=json";
return await InvokeAsync<IDictionary<string, object>>(url, "result");
}
//
// 摘要:
// 根据坐标获取地址
//
// 参数:
// point:
//
// coordtype:
public async Task<GeoAddress?> GetReverseGeoAsync(GeoPoint point, string? coordtype)
{
IDictionary<string, object> dictionary = await GetReverseGeocoderAsync(point, coordtype);
if (dictionary == null || dictionary.Count == 0)
{
return null;
}
GeoAddress geoAddress = new GeoAddress
{
Address = (dictionary["formatted_address"]?.ToString() ?? ""),
Confidence = dictionary["confidence"].ToInt()
};
if (dictionary["location"] is IDictionary<string, object> dictionary2 && dictionary2.Count >= 2)
{
geoAddress.Location = new GeoPoint(dictionary2["lng"], dictionary2["lat"]);
}
if (dictionary["addressComponent"] is IDictionary<string, object> dictionary3)
{
new JsonReader().ToObject(dictionary3, null, geoAddress);
geoAddress.Code = dictionary3["adcode"].ToInt();
geoAddress.Township = dictionary3["town"]?.ToString() ?? "";
geoAddress.Towncode = dictionary3["town_code"].ToInt();
geoAddress.StreetNumber = dictionary3["street_number"]?.ToString() ?? "";
}
if (dictionary.TryGetValue("sematic_description", out var value) && value is string text && !text.IsNullOrEmpty())
{
geoAddress.Title = text;
}
return geoAddress;
}
//
// 摘要:
// 计算距离和驾车时间
//
// 参数:
// origin:
//
// destination:
//
// coordtype:
//
// type:
// 路径计算的方式和方法
//
// 言论:
// https://lbsyun.baidu.com/index.php?title=webapi/route-matrix-api-v2
public async Task<Driving?> GetDistanceAsync(GeoPoint origin, GeoPoint destination, string? coordtype, int type = 13)
{
if (origin == null || (origin.Longitude < 1.0 && origin.Latitude < 1.0))
{
throw new ArgumentNullException("origin");
}
if (destination == null || (destination.Longitude < 1.0 && destination.Latitude < 1.0))
{
throw new ArgumentNullException("destination");
}
if (type <= 0)
{
type = 13;
}
string text = coordtype;
if (!text.IsNullOrEmpty() && text.Length > 6)
{
text = text.TrimEnd("ll");
}
string url = $"/routematrix/v2/driving?origins={origin.Latitude},{origin.Longitude}&destinations={destination.Latitude},{destination.Longitude}&tactics={type}&coord_type={text}&output=json";
IList<object> list = await InvokeAsync<IList<object>>(url, "result");
if (list == null || list.Count == 0)
{
return null;
}
if (!(list.FirstOrDefault() is IDictionary<string, object> dictionary))
{
return null;
}
IDictionary<string, object> dictionary2 = dictionary["distance"] as IDictionary<string, object>;
IDictionary<string, object> dictionary3 = dictionary["duration"] as IDictionary<string, object>;
return new Driving
{
Distance = (dictionary2?["value"].ToInt() ?? 0),
Duration = (dictionary3?["value"].ToInt() ?? 0)
};
}
//
// 摘要:
// 计算距离和驾车时间
//
// 参数:
// origin:
//
// destination:
//
// coordtype:
//
// type:
// 路径计算的方式和方法
//
// 言论:
// https://lbsyun.baidu.com/index.php?title=webapi/route-matrix-api-v2
public async Task<Driving?> GetDistanceExAsync(GeoPoint origin, GeoPoint destination, string stratCity, string endCity, string? coordtype, int type = 13)
{
if (origin == null || (origin.Longitude < 1.0 && origin.Latitude < 1.0))
{
throw new ArgumentNullException("origin");
}
if (destination == null || (destination.Longitude < 1.0 && destination.Latitude < 1.0))
{
throw new ArgumentNullException("destination");
}
if (type <= 0)
{
type = 13;
}
string text = coordtype;
if (!text.IsNullOrEmpty() && text.Length > 6)
{
text = text.TrimEnd("ll");
}
string url = $"/direction/v2/driving?origin={origin}&destination={destination}&ak={base.AppKey}&origin_region={stratCity}&destination_region={endCity}&coord_type={text}&output=json";
IList<object> list = await InvokeAsync<IList<object>>(url, "result");
if (list == null || list.Count == 0)
{
return null;
}
if (!(list.FirstOrDefault() is IDictionary<string, object> dictionary))
{
return null;
}
IDictionary<string, object> dictionary2 = dictionary["distance"] as IDictionary<string, object>;
IDictionary<string, object> dictionary3 = dictionary["duration"] as IDictionary<string, object>;
return new Driving
{
Distance = (dictionary2?["value"].ToInt() ?? 0),
Duration = (dictionary3?["value"].ToInt() ?? 0)
};
}
//
// 摘要:
// 行政区划区域检索
//
// 参数:
// query:
//
// tag:
//
// region:
//
// coordtype:
//
// formatAddress:
//
// 言论:
// https://lbsyun.baidu.com/index.php?title=webapi/guide/webservice-placeapi
public async Task<GeoAddress?> PlaceSearchAsync(string query, string tag, string region, string? coordtype = null, bool formatAddress = true)
{
query = HttpUtility.UrlEncode(query);
tag = HttpUtility.UrlEncode(tag);
region = HttpUtility.UrlEncode(region);
string url = "/place/v2/search?output=json&query=" + query + "&tag=" + tag + "&region=" + region + "&city_limit=true&ret_coordtype=" + coordtype;
IList<object> list = await InvokeAsync<IList<object>>(url, "results");
if (list == null || list.Count == 0)
{
return null;
}
object obj = list.FirstOrDefault();
IDictionary<string, object> rs = obj as IDictionary<string, object>;
if (rs == null)
{
return null;
}
GeoAddress geo = new GeoAddress();
if (rs["location"] is IDictionary<string, object> dictionary && dictionary.Count >= 2)
{
geo.Location = new GeoPoint(dictionary["lng"], dictionary["lat"]);
if (formatAddress && geo.Location != null)
{
GeoAddress geoAddress = await GetReverseGeoAsync(geo.Location, coordtype);
if (geoAddress != null)
{
geo = geoAddress;
}
}
geo.Name = rs["name"]?.ToString() ?? "";
string text = rs["address"]?.ToString() ?? "";
if (!text.IsNullOrEmpty())
{
geo.Address = text;
}
return geo;
}
return null;
}
//
// 摘要:
// IP定位
//
// 参数:
// ip:
//
// coordtype:
//
// 言论:
// https://lbsyun.baidu.com/index.php?title=webapi/ip-api
public async Task<IDictionary<string, object?>?> IpLocationAsync(string ip, string coordtype)
{
string url = "/location/ip?ip=" + ip + "&coor=" + coordtype;
IDictionary<string, object> dictionary = await InvokeAsync<IDictionary<string, object>>(url, null);
if (dictionary == null || dictionary.Count == 0)
{
return null;
}
if (!(dictionary["content"] is IDictionary<string, object> dictionary2))
{
return null;
}
if (dictionary.TryGetValue("address", out var value))
{
dictionary2["full_address"] = value;
}
if (dictionary2.TryGetValue("address_detail", out var value2))
{
if (value2 != null)
{
dictionary2.Merge(value2);
}
dictionary2.Remove("address_detail");
}
if (dictionary2.TryGetValue("point", out var value3))
{
if (value3 != null)
{
dictionary2.Merge(value3);
}
dictionary2.Remove("point");
}
return dictionary2;
}
//
// 摘要:
// 坐标转换
//
// 参数:
// points:
// 需转换的源坐标
//
// from:
// 源坐标类型。wgs84ll/gcj02/bd09ll
//
// to:
// 目标坐标类型。gcj02/bd09ll
//
// 言论:
// https://lbsyun.baidu.com/index.php?title=webapi/guide/changeposition
public override async Task<IList<GeoPoint>> ConvertAsync(IList<GeoPoint> points, string from, string to)
{
if (points == null || points.Count == 0)
{
throw new ArgumentNullException("points");
}
if (from.IsNullOrEmpty())
{
throw new ArgumentNullException("from");
}
if (to.IsNullOrEmpty())
{
throw new ArgumentNullException("to");
}
//if (!from.EndsWithIgnoreCase("ll", "mc"))
//{
// from += "ll";
//}
//if (!to.EndsWithIgnoreCase("ll", "mc"))
//{
// to += "ll";
//}
if (from.EqualIgnoreCase(to))
{
return points;
}
int num = 0;
int num2 = 0;
for (int i = 0; i < _coordTypes.Count; i++)
{
if (_coordTypes[i].EqualIgnoreCase(from))
{
num = i;
}
if (_coordTypes[i].EqualIgnoreCase(to))
{
num2 = i;
}
}
if (num == 0)
{
throw new ArgumentOutOfRangeException("from");
}
if (num2 == 0)
{
throw new ArgumentOutOfRangeException("to");
}
string url = string.Format("/geoconv/v2/?coords={0}&from={1}&to={2}&output=json", points.Join(";", (GeoPoint e) => $"{e.Longitude},{e.Latitude}"), num, num2);
List<GeoPoint> list = new List<GeoPoint>();
IList<object> list2 = await InvokeAsync<IList<object>>(url, "result");
if (list2 == null || list2.Count == 0)
{
return list;
}
foreach (IDictionary<string, object> item in list2.Cast<IDictionary<string, object>>())
{
list.Add(new GeoPoint(item["x"], item["y"]));
}
return list;
}
}
}

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace SelfUseUtil.Helper
@ -11,27 +12,10 @@ namespace SelfUseUtil.Helper
/// <summary>
/// 取小数,向下取整不四舍五入
/// </summary>
/// <param name="d"></param>
/// <param name="n"></param>
/// <returns></returns>
public static decimal CutDecimalWithN(decimal d, int n = 0)
{
string strDecimal = d.ToString();
int index = strDecimal.IndexOf(".");
if (index == -1 || strDecimal.Length < index + n + 1)
{
strDecimal = string.Format("{0:F" + n + "}", d);
}
else
{
int length = index;
if (n != 0)
{
length = index + n + 1;
}
strDecimal = strDecimal.Substring(0, length);
}
return decimal.Parse(strDecimal);
decimal factor = (decimal)Math.Pow(10, n);
return Math.Floor(d * factor) / factor;
}
}
}

View File

@ -3,6 +3,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace SelfUseUtil.Helper
@ -74,6 +75,85 @@ namespace SelfUseUtil.Helper
{
return (long)(value.ToUniversalTime().Subtract(new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc))).TotalSeconds;
}
/// <summary>
/// 将输入字符串所有非数字字符替换为 -
/// </summary>
public static string NormalizeDate(string input)
{
if (string.IsNullOrWhiteSpace(input))
return string.Empty;
input = Regex.Replace(input.Trim(), @"\D+", "-");
return input.Trim('-');
}
/// <summary>
/// 判断日期精度
/// </summary>
public static DatePrecision GetDatePrecision(string input)
{
input = NormalizeDate(input);
return input.Length switch
{
4 => DatePrecision.Year,
6 => DatePrecision.Month,
8 => DatePrecision.Day,
_ => input.Count(c => c == '-') switch
{
1 => DatePrecision.Month,
2 => DatePrecision.Day,
_ => DatePrecision.Invalid
}
};
}
/// <summary>
/// 将字符串转换为 DateTime支持选择起始时间或结束时间精确到毫秒
/// </summary>
public static DateTime? ParseToDateTime(string input, bool endTime = false)
{
input = NormalizeDate(input);
var precision = GetDatePrecision(input);
if (precision == DatePrecision.Invalid)
return null;
try
{
// 统一处理紧凑格式(无 -
if (precision == DatePrecision.Month && Regex.IsMatch(input, @"^\d{6}$"))
input = $"{input.Substring(0, 4)}-{input.Substring(4, 2)}";
if (precision == DatePrecision.Day && Regex.IsMatch(input, @"^\d{8}$"))
input = $"{input.Substring(0, 4)}-{input.Substring(4, 2)}-{input.Substring(6, 2)}";
// 拆分年月日
string[] parts = input.Split('-');
int year = int.Parse(parts[0]);
int month = parts.Length > 1 ? int.Parse(parts[1]) : 1;
int day = parts.Length > 2 ? int.Parse(parts[2]) : 1;
switch (precision)
{
case DatePrecision.Year:
return endTime
? new DateTime(year, 12, 31, 23, 59, 59, 999)
: new DateTime(year, 1, 1, 0, 0, 0, 0);
case DatePrecision.Month:
if (endTime)
day = DateTime.DaysInMonth(year, month);
return new DateTime(year, month, day, endTime ? 23 : 0, endTime ? 59 : 0, endTime ? 59 : 0, endTime ? 999 : 0);
case DatePrecision.Day:
return new DateTime(year, month, day, endTime ? 23 : 0, endTime ? 59 : 0, endTime ? 59 : 0, endTime ? 999 : 0);
default:
return null;
}
}
catch
{
return null;
}
}
}
[Serializable]
@ -97,4 +177,12 @@ namespace SelfUseUtil.Helper
return obj.ToJson().ToObject<DateDto>();
}
}
public enum DatePrecision
{
Year,
Month,
Day,
Invalid
}
}

View File

@ -0,0 +1,55 @@
using NewLife.Map.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SelfUseUtil.Model
{
public class DrivingEx : Driving
{
/// <summary>路线步骤</summary>
public IList<Step> Steps { get; set; } = new List<Step>();
}
public class Step
{
public string instruction { get; set; }
public string orientation { get; set; }
public string road_name { get; set; }
public string step_distance { get; set; }
public Cost cost { get; set; }
public string polyline { get; set; }
}
public class Path
{
public string distance { get; set; }
public string restriction { get; set; }
public List<Step> steps { get; set; }
public Cost cost { get; set; }
}
public class Route
{
public string origin { get; set; }
public string destination { get; set; }
public string taxi_cost { get; set; }
public List<Path> paths { get; set; }
}
public class Root
{
public Route route { get; set; }
}
public class Cost
{
public string duration { get; set; }
public string tolls { get; set; }
public string toll_distance { get; set; }
public string toll_road { get; set; }
public string traffic_lights { get; set; }
}
}

View File

@ -1,46 +1,102 @@
using ScottPlot;
//using ScottPlot;
using DocumentFormat.OpenXml.Spreadsheet;
using DocumentFormat.OpenXml.Wordprocessing;
using hyjiacan.py4n;
using NewLife;
using NewLife.Data;
using NewLife.Map;
using Newtonsoft.Json;
using SelfUseUtil;
using SelfUseUtil.Demo;
using SelfUseUtil.Helper;
using System.Drawing;
using System.IO.Packaging;
using System.Management;
using System.Net.NetworkInformation;
using System.Text.RegularExpressions;
using AMap = SelfUseUtil.AMap;
using BaiduMap = SelfUseUtil.BaiduMap;
// 创建 ScottPlot 图表对象
var plt = new Plot();
//// 创建 ScottPlot 图表对象
//var plt = new Plot();
// 添加一些示例数据
double[] x = { 1, 2, 3, 4, 5 };
double[] y1 = { 10, 20, 15, 30, 25 };
double[] y2 = { 15, 25, 20, 35, 30 };
double[] y3 = { 30, 30, 30, 30, 30 };
//// 添加一些示例数据
//double[] x = { 1, 2, 3, 4, 5 };
//double[] y1 = { 10, 20, 15, 30, 25 };
//double[] y2 = { 15, 25, 20, 35, 30 };
//double[] y3 = { 30, 30, 30, 30, 30 };
var bar1 = plt.PlotFill(x, y3, baseline: 20, label: "国家线", fillColor: Color.FromArgb(20, 255, 0, 0));
//var bar1 = plt.PlotFill(x, y3, baseline: 20, label: "国家线", fillColor: Color.FromArgb(20, 255, 0, 0));
// 添加折线图
var line1 = plt.PlotScatter(x, y1, markerSize: 10, label: "线条1", lineStyle: LineStyle.Solid, color: Color.Blue);
var line2 = plt.PlotScatter(x, y2, markerSize: 10, label: "线条2", lineStyle: LineStyle.Solid, color: Color.Orange);
//// 添加折线图
//var line1 = plt.PlotScatter(x, y1, markerSize: 10, label: "线条1", lineStyle: LineStyle.Solid, color: Color.Blue);
//var line2 = plt.PlotScatter(x, y2, markerSize: 10, label: "线条2", lineStyle: LineStyle.Solid, color: Color.Orange);
// 设置图表标题
plt.Title("折线图示例");
//// 设置图表标题
//plt.Title("折线图示例");
// 设置中文横轴标签
plt.XTicks(x, new[] { "一", "二", "三", "四", "五" });
//// 设置中文横轴标签
//plt.XTicks(x, new[] { "一", "二", "三", "四", "五" });
// 显示图例
plt.Legend();
// 保存为图片文件
string imagePath = $"{ConstData.DesktopPath}/LinePlot.png";
plt.SaveFig(imagePath);
Console.WriteLine($"折线图已保存到 {imagePath}");
//// 显示图例
//plt.Legend();
//// 保存为图片文件
//string imagePath = $"{ConstData.DesktopPath}/LinePlot.png";
//plt.SaveFig(imagePath);
//Console.WriteLine($"折线图已保存到 {imagePath}");
var start = new GeoPoint(115.636961, 33.140093);
var end = new GeoPoint(115.885212, 32.911371);
var codeType = "bd09ll";
//Console.WriteLine("----- 高德地图 -----");
//var gaode = new AMap { AppKey = "c7591c54f7443323b41ec4c0f315acf5" };
////请求地址https://restapi.amap.com/v5/direction/driving?origin=115.636961,33.140093&destination=115.885212,32.911371&key=c7591c54f7443323b41ec4c0f315acf5&output=json&show_fields=cost,polyline
//var gaodeData = await gaode.GetDistanceExAsync(start, end, codeType);
//Console.WriteLine($"公里: {gaodeData?.Distance / 1000d}km 时间:{gaodeData?.Duration / 60d}m");
//Console.WriteLine("----- 百度地图 -----");
//var baidu = new BaiduMap { AppKey = "VkKeY0zh7hxCkA788etU4Fxv6q25EuEw" };
//var data = await baidu.ConvertAsync(new[] { start, end }, "amap", codeType);
//start = data[0];
//end = data[1];
//var startAddr = await baidu.GetReverseGeoAsync(start, codeType);
//Console.WriteLine(startAddr?.City);
//var endAddr = await baidu.GetReverseGeoAsync(end, codeType);
//Console.WriteLine(endAddr?.City);
////请求地址https://api.map.baidu.com/direction/v2/driving?origin=33.146014,115.643602&destination=32.917289,115.891866&ak=VkKeY0zh7hxCkA788etU4Fxv6q25EuEw
//var baiduData = await baidu.GetDistanceExAsync(start, end, startAddr?.City, endAddr?.City, codeType);
//Console.WriteLine($"公里: {baiduData?.Distance / 1000d}km 时间:{baiduData?.Duration / 60d}m");
//Console.WriteLine("----- 腾讯地图 -----");
//var tencent = new TencentMap { AppKey = "TS5BZ-4RSCW-NVGRZ-YOTG4-UR645-K6FKD" };
////请求地址https://api.map.baidu.com/direction/v2/driving?origin=115.643602,33.146014&destination=115.891866,32.917289&ak=VkKeY0zh7hxCkA788etU4Fxv6q25EuEw&origin_region=阜阳&destination_region=阜阳
//var tencentData = await tencent.GetDistanceExAsync(start, end, codeType);
//Console.WriteLine($"公里: {tencentData?.Distance / 1000d}km 时间:{tencentData?.Duration / 60d}m");
//Console.WriteLine("----- 天地图 -----");
//var tiandi = new TianDiMap { AppKey = "TS5BZ-4RSCW-NVGRZ-YOTG4-UR645-K6FKD" };
////请求地址https://api.map.baidu.com/direction/v2/driving?origin=115.643602,33.146014&destination=115.891866,32.917289&ak=VkKeY0zh7hxCkA788etU4Fxv6q25EuEw&origin_region=阜阳&destination_region=阜阳
//var tiandiData = await tiandi.GetDistanceExAsync(start, end, codeType);
//Console.WriteLine($"公里: {tiandiData?.Distance / 1000d}km 时间:{tiandiData?.Duration / 60d}m");
string[] tests = { "2025", "202510", "20251013", "2025-10", "2025-10-13", "2025.10", "2025.10.13", "2025/10", "2025/10/13", "2025 10", "2025 10 13" };
foreach (var input in tests)
{
Console.WriteLine($"{input} → {DateHelper.ParseToDateTime(input, true)}");
}

View File

@ -19,17 +19,21 @@
<PackageReference Include="DocX" Version="2.5.0" />
<PackageReference Include="FluentFTP" Version="47.1.0" />
<PackageReference Include="FreeSql.Provider.ClickHouse" Version="3.2.809" />
<PackageReference Include="hyjiacan.pinyin4net" Version="4.1.1" />
<PackageReference Include="Irony" Version="1.2.0" />
<PackageReference Include="Microsoft.SqlServer.Management.SqlParser" Version="170.9.0" />
<PackageReference Include="Microsoft.SqlServer.TransactSql.ScriptDom" Version="161.8901.0" />
<PackageReference Include="MiniSqlParser" Version="1.0.8" />
<PackageReference Include="NewLife.Map" Version="2.6.2025.601" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NPinyinPro" Version="0.3.3" />
<PackageReference Include="NPOI" Version="2.6.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="ScottPlot" Version="4.1.67" />
<PackageReference Include="System.Data.Odbc" Version="8.0.0" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.5" />
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
<PackageReference Include="System.Management" Version="9.0.8" />
</ItemGroup>
</Project>