添加项目文件。

This commit is contained in:
yanghongwei 2025-03-22 20:16:22 +08:00
parent 30b0ce1ac8
commit 1972c0d453
54 changed files with 4913 additions and 0 deletions

25
.dockerignore Normal file
View File

@ -0,0 +1,25 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md

57
Common/CoordConvert.cs Normal file
View File

@ -0,0 +1,57 @@
using NetTopologySuite.Geometries;
namespace LY.App.Common
{
public static class CoordConvert
{
/// <summary>
/// 经纬度转Web墨卡托单位
/// </summary>
/// <param name="longitude">经度</param>
/// <param name="latitude">纬度</param>
/// <returns>转换后的位置</returns>
public static CoordPoint WGS84ToMercator(double lon, double lat)
{
var x = lon * Math.PI / 180 * Parameters.LongRadius;
var param = lat * Math.PI / 180;
var y = Parameters.LongRadius / 2 * Math.Log((1.0 + Math.Sin(param)) / (1.0 - Math.Sin(param)));
return new CoordPoint(x, y);
}
/// <summary>
/// 经纬度转Web墨卡托单位
/// </summary>
/// <param name="longitude">经度</param>
/// <param name="latitude">纬度</param>
/// <returns>转换后的位置</returns>
public static CoordPoint WGS84ToMercator(Coordinate coord)
{
return WGS84ToMercator(coord.Lon, coord.Lat);
}
/// <summary>
/// Web墨卡托转经纬度
/// </summary>
/// <param name="x">X坐标值单位</param>
/// <param name="y">Y坐标值单位</param>
/// <returns>转换后的位置</returns>
public static Coordinate MercatorToWGS84(double x, double y)
{
var lon = x / Parameters.MercatorLength * 180;
var lat = y / Parameters.MercatorLength * 180;
lat = 180 / Math.PI * (2 * Math.Atan(Math.Exp(lat * Math.PI / 180)) - Math.PI / 2);
return new Coordinate(lon, lat);
}
/// <summary>
/// Web墨卡托转经纬度
/// </summary>
/// <param name="x">X坐标值单位</param>
/// <param name="y">Y坐标值单位</param>
/// <returns>转换后的位置</returns>
public static Coordinate MercatorToWGS84(CoordPoint point)
{
return MercatorToWGS84(point.X, point.Y);
}
}
}

34
Common/CoordPoint.cs Normal file
View File

@ -0,0 +1,34 @@
namespace LY.App.Common
{
public struct CoordPoint
{
public CoordPoint(double x, double y)
{
X = x;
Y = y;
Z = 0;
}
public CoordPoint(double x, double y, double z)
{
X = x;
Y = y;
Z = z;
}
/// <summary>
/// X轴
/// </summary>
public double X { get; set; }
/// <summary>
/// Y轴
/// </summary>
public double Y { get; set; }
/// <summary>
/// Z轴
/// </summary>
public double Z { get; set; }
}
}

34
Common/Coordinate.cs Normal file
View File

@ -0,0 +1,34 @@
namespace LY.App.Common
{
public struct Coordinate
{
public Coordinate(double lon, double lat)
{
Lon = lon;
Lat = lat;
Alt = 0;
}
public Coordinate(double lon, double lat, double alt)
{
Lon = lon;
Lat = lat;
Alt = alt;
}
/// <summary>
/// 经度
/// </summary>
public double Lon { get; set; }
/// <summary>
/// 纬度
/// </summary>
public double Lat { get; set; }
/// <summary>
/// 高程
/// </summary>
public double Alt { get; set; }
}
}

View File

@ -0,0 +1,58 @@
using System.Security.Cryptography;
using System.Text;
namespace LY.App.Common.Cypher
{
public class AESCypherUtil
{
/// <summary>
/// AES 加密
/// </summary>
/// <param name="str">明文</param>
/// <param name="key">密钥对</param>
/// <returns>密文</returns>
public static string Encrypt(string str, string key, string iv)
{
using (var aes = Aes.Create())
{
aes.Key = Encoding.UTF8.GetBytes(key);
aes.IV = Encoding.UTF8.GetBytes(iv);
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
byte[] strbuffer = Encoding.UTF8.GetBytes(str);
using (var crytTransform = aes.CreateEncryptor(aes.Key, aes.IV))
{
byte[] buffer = crytTransform.TransformFinalBlock(strbuffer, 0, strbuffer.Length);
return Convert.ToBase64String(buffer, 0, buffer.Length);
}
}
}
/// <summary>
/// AES 解密
/// </summary>
/// <param name="str">密文</param>
/// <param name="key">密钥对</param>
/// <returns>明文</returns>
public static string Decrypt(string str, string key, string iv)
{
using (var aes = Aes.Create())
{
aes.Key = Encoding.UTF8.GetBytes(key);
aes.IV = Encoding.UTF8.GetBytes(iv);
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
byte[] strbuffer = System.Convert.FromBase64String(str);
using (var crytTransform = aes.CreateDecryptor(aes.Key, aes.IV))
{
byte[] buffer = crytTransform.TransformFinalBlock(strbuffer, 0, strbuffer.Length);
return Encoding.UTF8.GetString(buffer);
}
}
}
}
}

View File

@ -0,0 +1,51 @@
using System.Security.Cryptography;
using System.Text;
namespace LY.App.Common.Cypher
{
public class MD5CypherUtil
{
/// <summary>
/// MD5加密
/// </summary>
/// <param name="str">内容</param>
/// <returns></returns>
public static string Hash(string str)
{
return Hash(Encoding.UTF8.GetBytes(str));
}
/// <summary>
/// MD5加密
/// </summary>
/// <param name="buffer"></param>
/// <returns></returns>
public static string Hash(byte[] buffer)
{
return BitConverter.ToString(BinaryHash(buffer)).Replace("-", "");
}
/// <summary>
/// MD5加密
/// </summary>
/// <param name="buffer"></param>
/// <returns></returns>
public static byte[] BinaryHash(string str)
{
return BinaryHash(Encoding.UTF8.GetBytes(str));
}
/// <summary>
/// MD5加密
/// </summary>
/// <param name="buffer"></param>
/// <returns></returns>
public static byte[] BinaryHash(byte[] buffer)
{
using (var md5 = MD5.Create())
{
return md5.ComputeHash(buffer);
}
}
}
}

62
Common/FreqConvert.cs Normal file
View File

@ -0,0 +1,62 @@
namespace LY.App.Common
{
/// <summary>
/// 频段转换
/// </summary>
public static class FreqConvert
{
/// <summary>
///返回频段转换结果
/// </summary>
/// <param name="frequency"></param>
/// <returns></returns>
public static double CoverFreq(double frequency)
{
double result = 0;
if (frequency > 0)
{
if (370 <= frequency && frequency <= 500)
{
return 433;
}
else if (700 <= frequency && frequency <= 890)
{
return 840;
}
else if (890 < frequency && frequency <= 1000)
{
return 915;
}
else if (1100 <= frequency && frequency <= 1300)
{
return 1.2;
}
else if (1300 < frequency && frequency <= 1500)
{
return 1.4;
}
else if (1500 < frequency && frequency <= 1700)
{
return 1.6;
}
else if (2300 <= frequency && frequency <= 2500)
{
return 2.4;
}
else if (5100 <= frequency && frequency <= 5300)
{
return 5.2;
}
else if (5700 <= frequency && frequency <= 5900)
{
return 5.8;
}
else
{
return result; // 表示输入频率不在已定义范围内
}
}
return result;
}
}
}

622
Common/GeoJsonHelper.cs Normal file
View File

@ -0,0 +1,622 @@
using LY.App.Model;
using NetTopologySuite.Features;
using NetTopologySuite.Geometries;
using NetTopologySuite.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace LY.App.Common
{
public class GeoJsonHelper
{
/// <summary>
/// 格式为json字符串
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public static string ConvertJsonString(string str)
{
try
{
//格式化json字符串
JsonSerializer serializer = new JsonSerializer();
TextReader tr = new StringReader(str);
JsonTextReader jtr = new JsonTextReader(tr);
object obj = serializer.Deserialize(jtr);
if (obj != null)
{
StringWriter textWriter = new StringWriter();
JsonTextWriter jsonWriter = new JsonTextWriter(textWriter)
{
Formatting = Formatting.Indented,
Indentation = 4,
IndentChar = ' '
};
serializer.Serialize(jsonWriter, obj);
return textWriter.ToString();
}
else
{
return str;
}
}
catch (Exception)
{
}
return str;
}
/// <summary>
/// 转换为geojson格式字符串
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static string GetGeoJson(object? value)
{
if (value == null)
return "";
var serializer = GeoJsonSerializer.Create();
using (var sw = new System.IO.StringWriter())
{
serializer.Serialize(sw, value);
return sw.ToString();
}
}
/// <summary>
/// 将坐标转换为点
/// </summary>
/// <param name="x">经度</param>
/// <param name="y">纬度</param>
/// <returns></returns>
public static NetTopologySuite.Geometries.Point ConvertToPoint(double x, double y)
{
return new NetTopologySuite.Geometries.Point(x, y);
}
public static object? GetDynamicFeature(Geometry geometry, IDictionary<string, object> attributes)
{
var f = GetGeoFeature(geometry, attributes);
var geoJson = GetGeoJson(f);
return geoJson;
// return JsonConvert.DeserializeObject(geoJson);
}
/// <summary>
/// 获取集合中能够组成多面的几何数据
/// </summary>
/// <param name="features"></param>
/// <returns></returns>
public static List<MultiPolygon> GetGeoMultiPolygon(FeatureCollection features)
{
var result = new List<MultiPolygon>();
if (features == null)
return result;
foreach (var feature in features)
{
if (feature.Geometry is MultiPolygon)
{
result.Add(feature.Geometry as MultiPolygon);
}
else if (feature.Geometry is Polygon)
{
result.Add(new MultiPolygon(new Polygon[] { feature.Geometry as Polygon }));
}
else if (feature.Geometry is LineString)
{
var line = (LineString)feature.Geometry;
if (line.IsRing)
{
result.Add(new MultiPolygon(new Polygon[] { new Polygon(new LinearRing(line.Coordinates)) }));
}
}
}
return result;
}
/// <summary>
/// 获取集合中能够组成多线的几何数据
/// </summary>
/// <param name="features"></param>
/// <returns></returns>
public static List<MultiLineString> GetGeoMultiLineString(FeatureCollection features)
{
var result = new List<MultiLineString>();
if (features == null)
return result;
foreach (var feature in features)
{
if (feature.Geometry is MultiLineString)
{
result.Add(feature.Geometry as MultiLineString);
}
else if (feature.Geometry is LineString)
{
result.Add(new MultiLineString(new LineString[] { feature.Geometry as LineString }));
}
else if (feature.Geometry is MultiPoint)
{
var mp = (MultiPoint)feature.Geometry;
if (mp.Count() > 1)
{
result.Add(new MultiLineString(new LineString[] { new LineString(mp.Coordinates) }));
}
}
}
return result;
}
/// <summary>
/// 将geojson转为FeatureCollection,
/// 原json支持点、线面等
/// </summary>
/// <param name="geoJson"></param>
/// <returns></returns>
public static FeatureCollection? GetGeoFeatureCollectionBuild(string geoJson)
{
if (string.IsNullOrWhiteSpace(geoJson)) return null;
try
{
var jObj = JObject.Parse(geoJson);
if (jObj != null)
{
if (jObj.TryGetValue("type", out var jToken))
{
if (jToken != null)
{
if (string.Compare(jToken.ToString(), "FeatureCollection") == 0)
{
return GetGeoFeatureCollection(geoJson);
}
else
{
var f = GetGeoFeatureBuild(geoJson);
if (f != null)
{
var result = new FeatureCollection();
result.Add(f);
return result;
}
}
}
}
}
}
catch (Exception)
{
throw new Exception("geojson无效");
}
return null;
}
public static Feature? GetGeoFeatureBuild(string geoJson)
{
var jObj = JObject.Parse(geoJson);
if (jObj != null)
{
//先判断type
if (jObj.TryGetValue("type", out var jToken))
{
if (jToken != null)
{
if (string.Compare(jToken.ToString(), "Feature") == 0)
{
return GetGeoFeature(geoJson);
}
Geometry? geometry = null;
if (string.Compare(jToken.ToString(), "Point") == 0)
{
geometry = GetGeoPoint(geoJson);
}
if (string.Compare(jToken.ToString(), "MultiPoint") == 0)
{
geometry = GetGeoMultiPoint(geoJson);
}
if (string.Compare(jToken.ToString(), "LineString") == 0)
{
geometry = GetGeoLineString(geoJson);
}
if (string.Compare(jToken.ToString(), "MultiLineString") == 0)
{
geometry = GetGeoMultiLineString(geoJson);
}
if (string.Compare(jToken.ToString(), "Polygon") == 0)
{
geometry = GetGeoPolygon(geoJson);
}
if (string.Compare(jToken.ToString(), "MultiPolygon") == 0)
{
geometry = GetGeoMultiPolygon(geoJson);
}
if (geometry != null)
return new Feature(geometry, new AttributesTable());
}
}
}
return null;
}
public static Feature? GetGeoFeature(Geometry geometry, IDictionary<string, object> attributes)
{
AttributesTable attributesTable = new AttributesTable();
foreach (var item in attributes)
{
attributesTable.Add(item.Key, item.Value);
}
var result = new Feature(geometry, attributesTable);
return result;
}
public static Feature? GetGeoFeature(string geoJson)
{
var serializer = GeoJsonSerializer.Create();
using (var stringReader = new StringReader(geoJson))
using (var jsonReader = new JsonTextReader(stringReader))
{
return serializer.Deserialize<NetTopologySuite.Features.Feature>(jsonReader);
}
}
public static FeatureCollection? GetGeoFeatureCollection(string geoJson)
{
var serializer = GeoJsonSerializer.Create();
using (var stringReader = new StringReader(geoJson))
using (var jsonReader = new JsonTextReader(stringReader))
{
return serializer.Deserialize<NetTopologySuite.Features.FeatureCollection>(jsonReader);
}
}
public static NetTopologySuite.Geometries.Point? GetGeoPoint(string geoJson)
{
var jObj = JObject.Parse(geoJson);
if (jObj != null)
{
//先判断type
if (jObj.TryGetValue("type", out var jToken))
{
if (jToken != null)
{
if (string.Compare(jToken.ToString(), "Feature") == 0)
{
var f = GetGeoFeature(geoJson);
if (f.Geometry is Point)
return f.Geometry as NetTopologySuite.Geometries.Point;
}
else
{
var serializer = GeoJsonSerializer.Create();
using (var stringReader = new StringReader(geoJson))
using (var jsonReader = new JsonTextReader(stringReader))
{
return serializer.Deserialize<NetTopologySuite.Geometries.Point>(jsonReader);
}
}
}
}
}
return null;
}
public static NetTopologySuite.Geometries.MultiPoint? GetGeoMultiPoint(string geoJson)
{
var serializer = GeoJsonSerializer.Create();
using (var stringReader = new StringReader(geoJson))
using (var jsonReader = new JsonTextReader(stringReader))
{
return serializer.Deserialize<NetTopologySuite.Geometries.MultiPoint>(jsonReader);
}
}
public static NetTopologySuite.Geometries.LineString? GetGeoLineString(string geoJson)
{
var serializer = GeoJsonSerializer.Create();
using (var stringReader = new StringReader(geoJson))
using (var jsonReader = new JsonTextReader(stringReader))
{
return serializer.Deserialize<NetTopologySuite.Geometries.LineString>(jsonReader);
}
}
public static NetTopologySuite.Geometries.MultiLineString? GetGeoMultiLineString(string geoJson)
{
var serializer = GeoJsonSerializer.Create();
using (var stringReader = new StringReader(geoJson))
using (var jsonReader = new JsonTextReader(stringReader))
{
return serializer.Deserialize<NetTopologySuite.Geometries.MultiLineString>(jsonReader);
}
}
public static NetTopologySuite.Geometries.Polygon? GetGeoPolygon(string geoJson)
{
var serializer = GeoJsonSerializer.Create();
using (var stringReader = new StringReader(geoJson))
using (var jsonReader = new JsonTextReader(stringReader))
{
return serializer.Deserialize<NetTopologySuite.Geometries.Polygon>(jsonReader);
}
}
public static NetTopologySuite.Geometries.MultiPolygon? GetGeoMultiPolygon(string geoJson)
{
var serializer = GeoJsonSerializer.Create();
using (var stringReader = new StringReader(geoJson))
using (var jsonReader = new JsonTextReader(stringReader))
{
return serializer.Deserialize<NetTopologySuite.Geometries.MultiPolygon>(jsonReader);
}
}
public static bool TryGetGeomWKTFromGeoJson(string? geoJson, out MultiPolygon? geometry)
{
geometry = null;
try
{
if (!string.IsNullOrWhiteSpace(geoJson))
{
var features = GeoJsonHelper.GetGeoFeatureCollectionBuild(geoJson);
var multPolygons = GeoJsonHelper.GetGeoMultiPolygon(features);
if (multPolygons?.Count > 0)
{
geometry = multPolygons[0];
geometry.SRID = 4326;
return true;
}
}
}
catch
{
}
return false;
}
public static Geometry FromWKT(string wktData)
{
WKTReader reader = new WKTReader();
return reader.Read(wktData);
}
public static Geometry FromWKB(byte[] wkbData)
{
WKBReader reader = new WKBReader();
return reader.Read(wkbData);
}
public static Polygon Transfer2CoordinateByCoords(List<Point> Coords)
{
if (Coords.Any())
{
var list = Coords.Select(s => s.X + "" + s.Y).ToList();
var content = $"POLYGON(({string.Join(",", list)}))";
WKTReader wktReader = new WKTReader();
Polygon polygon = wktReader.Read(content) as Polygon;
return polygon;
}
return null;
}
#region
private static GeometryFactory _gf = NetTopologySuite.NtsGeometryServices.Instance.CreateGeometryFactory(4326);
public static bool IsWithin(BasePoint pnt, BasePoint[] polygon)
{
if (IsValidPolygon(polygon))
{
var point = CreatPoint(pnt);
var region = CreatePolygon(polygon);
return point.Within(region);
}
return false;
}
/// <summary>
/// 判断点是否在多边形内
/// </summary>
/// <param name="pnt"></param>
/// <param name="multiPolygon"></param>
/// <returns></returns>
public static bool isWithin(BasePoint pnt, MultiPolygon multiPolygon)
{
var point = CreatPoint(pnt);
return point.Within(multiPolygon);
}
/// <summary>
/// 基本判断点数据是否有效转为面
/// </summary>
/// <param name="polygon"></param>
/// <returns></returns>
public static bool IsValidPolygon(BasePoint[] polygon)
{
if (polygon?.Count() >= 4)
{
//polygon.Distinct();//重复点?
return polygon[0].Equal(polygon[polygon.Count() - 1]);
}
return false;
}
/// <summary>
/// 基本判断点数据是否有效转为线
/// </summary>
/// <param name="polygon"></param>
/// <returns></returns>
public static bool IsValidLineString(BasePoint[] polygon)
{
if (polygon?.Count() >= 2)
{
//polygon.Distinct();//重复点?
return true;
}
return false;
}
/// <summary>
/// 创建点
/// </summary>
/// <param name="pnt"></param>
/// <returns></returns>
public static Point CreatPoint(BasePoint pnt)
{
return _gf.CreatePoint(GetCoordinate(pnt));
}
/// <summary>
/// 创建点
/// </summary>
/// <param name="pnt"></param>
/// <returns></returns>
public static Point CreatPoint(double x, double y)
{
return _gf.CreatePoint(GetCoordinate(x, y));
}
/// <summary>
/// 创建线
/// </summary>
/// <param name="polygon"></param>
/// <returns></returns>
public static LineString CreateLineString(BasePoint[] points)
{
return _gf.CreateLineString(GetCoordinate(points));
}
/// <summary>
/// 创建面
/// </summary>
/// <param name="polygon"></param>
/// <returns></returns>
public static Polygon CreatePolygon(BasePoint[] polygon)
{
return _gf.CreatePolygon(GetCoordinate(polygon));
}
/// <summary>
/// 点转换到坐标
/// </summary>
/// <param name="pnt"></param>
/// <returns></returns>
public static NetTopologySuite.Geometries.Coordinate GetCoordinate(BasePoint pnt)
{
return new NetTopologySuite.Geometries.Coordinate(pnt.X, pnt.Y);
}
/// <summary>
/// 点转换到坐标
/// </summary>
/// <param name="pnt"></param>
/// <returns></returns>
public static NetTopologySuite.Geometries.Coordinate GetCoordinate(double x, double y)
{
return new NetTopologySuite.Geometries.Coordinate(x, y);
}
/// <summary>
/// 点组转换坐标组
/// </summary>
/// <param name="polygon"></param>
/// <returns></returns>
public static NetTopologySuite.Geometries.Coordinate[] GetCoordinate(BasePoint[] polygon)
{
List<NetTopologySuite.Geometries.Coordinate> coordinates = new List<NetTopologySuite.Geometries.Coordinate>();
foreach (var item in polygon)
{
coordinates.Add(GetCoordinate(item));
}
return coordinates.ToArray();
}
public static Geometry CreateCircleMercator(double x, double y, double radius)
{
var geometryFactory = new GeometryFactory();
var wbp = CoordConvert.WGS84ToMercator(x, y);
var point1 = geometryFactory.CreatePoint(new NetTopologySuite.Geometries.Coordinate(wbp.X, wbp.Y));
var geo = point1.Buffer(radius, 80);
//var geo1 = point1.Buffer(radius);
return geo;
}
/// <summary>
/// 创建圆形
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="radius"></param>
/// <returns></returns>
public static Geometry CreateCircle(double x, double y, double radius, int pnts = 100)
{
var geo1 = CreateCircleMercator(x, y, radius);
var coordinates = geo1.Coordinates;
List<BasePoint> points = new List<BasePoint>();
foreach (var item in coordinates)
{
var wgs84 = CoordConvert.MercatorToWGS84(item.X, item.Y);
points.Add(new BasePoint(wgs84.Lon, wgs84.Lat, 0));
}
return CreatePolygon(points.ToArray());
}
public static double CalculateCircleAreaKmUnit(double x, double y, double radius)
{
if (x > 0 && y > 0 && radius > 0)
{
var area11 = CalculateCircleArea(x, y, radius);
return Math.Round(area11 / 1000000, 5);
}
return 0;
}
public static double CalculateCircleArea(double x, double y, double radius)
{
if (x > 0 && y > 0 && radius > 0)
{
var geo1 = CreateCircleMercator(x, y, radius);
return geo1.Area;
}
return 0;
}
/// <summary>
/// 计算合并后的总面积
/// </summary>
/// <param name="geometries"></param>
/// <returns></returns>
public static double CalculateAreaMercator(Geometry[] geometries)
{
//将多个 Geometry 合并成一个 Geometry避免交集重复计算
var unionedGeometry = _gf.BuildGeometry(geometries).Union();
string geojson = GetGeoJson(unionedGeometry);
var area = unionedGeometry.Area;
// 计算合并后的总面积
return area;
}
///// <summary>
///// 计算合并后的总面积
///// </summary>
///// <param name="geometries"></param>
///// <returns></returns>
//public static double CalculateArea(Geometry[] geometries)
//{
// //将多个 Geometry 合并成一个 Geometry避免交集重复计算
// var unionedGeometry = _gf.BuildGeometry(geometries).Union();
// string geojson = GetGeoJson(unionedGeometry);
// var area = Math.Round(unionedGeometry.ProjectTo(2855).Area / 1000000.0, 5);
// // 计算合并后的总面积
// return area;
//}
#endregion
}
}

View File

@ -0,0 +1,420 @@
using Newtonsoft.Json;
using System.Net.Http.Headers;
using System.Net;
namespace LY.App.Common.HttpUtil
{
public static class RequestUtil
{
/// <summary>
/// 是否已经初始化加密协议
/// </summary>
private static bool _securitinit = false;
/// <summary>
/// 初始化加密协议
/// </summary>
public static void InitialSecurity()
{
if (_securitinit) return;
try
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
| SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13;
}
catch (Exception)
{
try
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
| SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
}
catch (Exception)
{
try
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11;
}
catch (Exception)
{
try
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
}
catch (Exception) { }
}
}
}
ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => true;
_securitinit = true;
}
/// <summary>
/// 获取HTTP连接
/// </summary>
/// <param name="haveSSL">是否ssl连接</param>
/// <returns></returns>
public static HttpClient GetClient(bool haveSSL)
{
if (haveSSL)
{
InitialSecurity();
var handle = new HttpClientHandler();
handle.ServerCertificateCustomValidationCallback
= HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
return new HttpClient(handle);
}
else return new HttpClient();
}
/// <summary>
/// 异步请求GET
/// </summary>
/// <param name="url">网址</param>
/// <param name="headers">头部信息</param>
/// <param name="auth">验证信息</param>
/// <param name="timeout">过期时间</param>
/// <returns></returns>
public static async Task<string> GetAsync(string url,
Dictionary<string, string> headers = null,
AuthenticationHeaderValue auth = null, int timeout = 5000)
{
using (var client = GetClient(url.StartsWith("https")))
{
client.Timeout = TimeSpan.FromMilliseconds(timeout);
if (headers != null && headers.Any())
{
foreach (var item in headers)
{
client.DefaultRequestHeaders.Add(item.Key, item.Value);
}
}
if (auth != null)
client.DefaultRequestHeaders.Authorization = auth;
var result = await client.GetAsync(url);
if (result.IsSuccessStatusCode)
return await result.Content.ReadAsStringAsync();
}
return null;
}
/// <summary>
/// 异步请求GET
/// </summary>
/// <param name="url">网址</param>
/// <param name="headers">头部信息</param>
/// <param name="auth">验证信息</param>
/// <param name="timeout">过期时间</param>
/// <returns></returns>
public static async Task<Stream> GetStreamAsync(string url,
Dictionary<string, string> headers = null,
AuthenticationHeaderValue auth = null, int timeout = 5000)
{
using (var client = GetClient(url.StartsWith("https")))
{
client.Timeout = TimeSpan.FromMilliseconds(timeout);
if (headers != null && headers.Any())
{
foreach (var item in headers)
{
client.DefaultRequestHeaders.Add(item.Key, item.Value);
}
}
if (auth != null)
client.DefaultRequestHeaders.Authorization = auth;
var result = await client.GetAsync(url);
if (result.IsSuccessStatusCode)
return await result.Content.ReadAsStreamAsync();
}
return null;
}
/// <summary>
/// 删除
/// </summary>
/// <param name="url"></param>
/// <param name="headers"></param>
/// <param name="auth"></param>
/// <param name="timeout"></param>
/// <returns></returns>
public static async Task<string> DeleteAsync(string url,
Dictionary<string, string> headers = null,
AuthenticationHeaderValue auth = null, int timeout = 5000)
{
using (var client = GetClient(url.StartsWith("https")))
{
client.Timeout = TimeSpan.FromMilliseconds(timeout);
if (headers != null && headers.Any())
{
foreach (var item in headers)
{
client.DefaultRequestHeaders.Add(item.Key, item.Value);
}
}
if (auth != null)
client.DefaultRequestHeaders.Authorization = auth;
var result = await client.DeleteAsync(url);
if (result.IsSuccessStatusCode)
return await result.Content.ReadAsStringAsync();
}
return null;
}
/// <summary>
/// 异步请求并解析
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="url">网址</param>
/// <param name="headers">头部信息</param>
/// <param name="auth">验证信息</param>
/// <param name="timeout">过期时间</param>
/// <returns></returns>
public static async Task<T> GetAsync<T>(string url,
Dictionary<string, string> headers = null,
AuthenticationHeaderValue auth = null,
int timeout = 5000)
{
var result = await GetAsync(url, headers, auth, timeout);
if (result != null)
return JsonConvert.DeserializeObject<T>(result);
return default;
}
/// <summary>
/// 返回btye[]
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public static async Task<byte[]> GetAsync(string url)
{
using (var client = GetClient(url.StartsWith("https")))
{
client.Timeout = TimeSpan.FromMilliseconds(2000);
var result = await client.GetAsync(url);
if (result.IsSuccessStatusCode)
return await result.Content.ReadAsByteArrayAsync();
}
return null;
}
public static AuthenticationHeaderValue GetTokenHeader(string token)
{
return new AuthenticationHeaderValue("Bearer", token);
}
/// <summary>
/// 异步请求POST
/// </summary>
/// <param name="url">网址</param>
/// <param name="dataJson">信息</param>
/// <param name="headers">头部信息</param>
/// <param name="auth">验证信息</param>
/// <param name="timeout">过期时间</param>
/// <returns></returns>
public static async Task<string> PostAsync(string url,
string dataJson = null,
Dictionary<string, string> headers = null,
AuthenticationHeaderValue auth = null, int timeout = 5000)
{
using (var client = GetClient(url.StartsWith("https")))
{
client.Timeout = TimeSpan.FromMilliseconds(timeout);
if (headers != null && headers.Any())
{
foreach (var item in headers)
{
client.DefaultRequestHeaders.Add(item.Key, item.Value);
}
}
if (auth != null)
client.DefaultRequestHeaders.Authorization = auth;
StringContent content = null;
if (dataJson != null)
{
content = new StringContent(dataJson);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
}
var result = await client.PostAsync(url, content);
if (result.IsSuccessStatusCode)
return await result.Content.ReadAsStringAsync();
}
return null;
}
/// <summary>
/// 异步请求POST
/// </summary>
/// <param name="url">网址</param>
/// <param name="data">消息</param>
/// <param name="headers">头部信息</param>
/// <param name="auth">验证信息</param>
/// <param name="timeout">过期时间</param>
/// <returns></returns>
public static async Task<string> PostAsync(string url,
Dictionary<string, string> data,
Dictionary<string, string> headers = null,
AuthenticationHeaderValue auth = null, int timeout = 5000)
{
return await PostAsync(url, JsonConvert.SerializeObject(data), headers, auth, timeout);
}
/// <summary>
/// 异步请求POST
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="url">网址</param>
/// <param name="dataJson">信息</param>
/// <param name="headers">头部信息</param>
/// <param name="auth">验证信息</param>
/// <param name="timeout">过期时间</param>
/// <returns></returns>
public static async Task<T> PostAsync<T>(string url,
string dataJson = null,
Dictionary<string, string> headers = null,
AuthenticationHeaderValue auth = null, int timeout = 5000)
{
var result = await PostAsync(url, dataJson, headers, auth, timeout);
return JsonConvert.DeserializeObject<T>(result);
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="url">网址</param>
/// <param name="data">消息</param>
/// <param name="headers">头部信息</param>
/// <param name="auth">验证信息</param>
/// <param name="timeout">过期时间</param>
/// <returns></returns>
public static async Task<T> PostAsync<T>(string url,
Dictionary<string, string> data = null,
Dictionary<string, string> headers = null,
AuthenticationHeaderValue auth = null, int timeout = 5000)
{
return await PostAsync<T>(url, JsonConvert.SerializeObject(data), headers, auth, timeout);
}
/// <summary>
/// 异步推送数据流
/// </summary>
/// <param name="url">网址</param>
/// <param name="stream">数据流</param>
/// <param name="headers">头部信息</param>
/// <param name="auth">验证信息</param>
/// <param name="timeout">过期时间</param>
/// <returns></returns>
public static async Task<string> PutStreamAsync(
string url, Stream stream,
Dictionary<string, string> headers = null,
AuthenticationHeaderValue auth = null, int timeout = 10000)
{
using (var client = GetClient(url.StartsWith("https")))
{
client.Timeout = TimeSpan.FromMilliseconds(timeout);
if (headers != null && headers.Any())
{
foreach (var item in headers)
{
client.DefaultRequestHeaders.Add(item.Key, item.Value);
}
}
if (auth != null)
client.DefaultRequestHeaders.Authorization = auth;
StreamContent content = null;
if (stream != null)
{
content = new StreamContent(stream);
content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
}
var result = await client.PutAsync(url, content);
if (result.IsSuccessStatusCode)
return await result.Content.ReadAsStringAsync();
}
return null;
}
/// <summary>
/// 异步推送数据流
/// </summary>
/// <param name="url">网址</param>
/// <param name="stream">数据流</param>
/// <param name="headers">头部信息</param>
/// <param name="auth">验证信息</param>
/// <param name="timeout">过期时间</param>
/// <returns></returns>
public static async Task<T> PutStreamAsync<T>(
string url, Stream stream,
Dictionary<string, string> headers = null,
AuthenticationHeaderValue auth = null, int timeout = 10000)
{
var result = await PutStreamAsync(url, stream, headers, auth, timeout);
return JsonConvert.DeserializeObject<T>(result);
}
/// <summary>
/// PUT请求
/// </summary>
/// <param name="url"></param>
/// <param name="dataJson"></param>
/// <param name="headers"></param>
/// <param name="auth"></param>
/// <param name="timeout"></param>
/// <returns></returns>
public static async Task<string> PutAsync(string url,
string dataJson = null,
Dictionary<string, string> headers = null,
AuthenticationHeaderValue auth = null, int timeout = 5000)
{
using (var client = GetClient(url.StartsWith("https")))
{
client.Timeout = TimeSpan.FromMilliseconds(timeout);
if (headers != null && headers.Any())
{
foreach (var item in headers)
{
client.DefaultRequestHeaders.Add(item.Key, item.Value);
}
}
if (auth != null)
client.DefaultRequestHeaders.Authorization = auth;
StringContent content = null;
if (dataJson != null)
{
content = new StringContent(dataJson);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
}
var result = await client.PutAsync(url, content);
if (result.IsSuccessStatusCode)
return await result.Content.ReadAsStringAsync();
}
return null;
}
}
}

30
Common/Parameters.cs Normal file
View File

@ -0,0 +1,30 @@
namespace LY.App.Common
{
public static class Parameters
{
/// <summary>
/// 地球平均半径
/// </summary>
public const double EarthRadius = 6371393;
/// <summary>
/// 地球长轴半径
/// </summary>
public const double LongRadius = 6378137;
/// <summary>
/// 地球短轴半径
/// </summary>
public const double ShortRadius = 6356752.3142;
/// <summary>
/// 地球扁率
/// </summary>
public const double Oblateness = 0.00335281066433;
/// <summary>
///
/// </summary>
public const double MercatorLength = 20037508.34;
}
}

View File

@ -0,0 +1,43 @@
namespace LY.App.Common.Redis
{
public class RedisKeyList
{
public static string BatchIdBysn(string sn)
{
return $"batchid_{sn}";
}
public static string LogNum(string userName)
{
return $"login_fail_{userName}";
}
public static string TokenUser(string userName)
{
return $"token_{userName}";
}
public static string DeviceInfo(string code)
{
return $"deviceInfo_{code}";
}
/// <summary>
/// 缓存首页数据
/// </summary>
/// <returns></returns>
public static string index_data()
{
return $"index_cache";
}
public static string DeviceStatus(string sn)
{
return $"device_status_{sn}";
}
/// <summary>
/// 缓存设备token
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public static string DeviceTokenById(long id)
{
return $"device_token_{id}";
}
}
}

View File

@ -0,0 +1,58 @@
using LY.App.Extensions.DI;
using StackExchange.Redis;
using System.Text.Json;
namespace LY.App.Common.Redis
{
/// <summary>
/// redis 连接服务
/// </summary>
public class RedisService
{
private readonly IDatabase _db;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="connectionString"></param>
public RedisService(string connectionString)
{
var redis = ConnectionMultiplexer.Connect(connectionString);
_db = redis.GetDatabase();
}
/// <summary>
/// 泛型存储数据到 Redis
/// </summary>
public async Task<bool> SetAsync<T>(string key, T value, TimeSpan? expiry = null)
{
string jsonData = JsonSerializer.Serialize(value);
return await _db.StringSetAsync(key, jsonData, expiry);
}
/// <summary>
/// 泛型获取数据
/// </summary>
public async Task<T?> GetAsync<T>(string key)
{
string jsonData = await _db.StringGetAsync(key);
return jsonData is not null ? JsonSerializer.Deserialize<T>(jsonData) : default;
}
/// <summary>
/// 删除 Key
/// </summary>
public async Task<bool> DeleteAsync(string key)
{
return await _db.KeyDeleteAsync(key);
}
/// <summary>
/// 检查 Key 是否存在
/// </summary>
public async Task<bool> ExistsAsync(string key)
{
return await _db.KeyExistsAsync(key);
}
}
}

View File

@ -0,0 +1,42 @@
using LY.App.Extensions.DI;
using Microsoft.AspNetCore.SignalR;
using Newtonsoft.Json;
namespace LY.App.Common.WebSocket
{
/// <summary>
/// 客户端push消息
/// </summary>
[ServiceInjection(InjectionType.Singleton)]
public class PushService
{
private readonly IHubContext<SocketHub> _hubContext;
public PushService(IHubContext<SocketHub> hubContext)
{
_hubContext = hubContext;
}
/// <summary>
/// 1对1消息
/// </summary>
/// <param name="connectionId"></param>
/// <param name="message"></param>
/// <returns></returns>
public async Task SendMessageToClient(string connectionId, object message)
{
await _hubContext.Clients.Client(connectionId).SendAsync("ReceiveMessage", message);
}
/// <summary>
/// 全部消息
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public async Task SendMessageToAll(object message)
{
var data = JsonConvert.SerializeObject(message);
Console.WriteLine($"推送前端消息:{data}");
await _hubContext.Clients.All.SendAsync("ReceiveMessage", data);
}
}
}

View File

@ -0,0 +1,46 @@
using LY.App.Extensions.DI;
using Microsoft.AspNetCore.SignalR;
using Newtonsoft.Json;
namespace LY.App.Common.WebSocket
{
/// <summary>
/// socket
/// </summary>
[ServiceInjection(InjectionType.Singleton)]
public class SocketHub : Hub
{
// 客户端连接事件
public override async Task OnConnectedAsync()
{
var msg = JsonConvert.SerializeObject(new { msgType = "info", data = "hello" });
// 发送消息给连接的客户端
await Clients.Caller.SendAsync("ReceiveMessage", msg);
await base.OnConnectedAsync();
}
/// <summary>
/// // 自定义方法,用于向客户端推送消息
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public async Task SendMessageToClient(object message)
{
// 发送消息给所有客户端
var connId = this.Context.ConnectionId;
var msg = JsonConvert.SerializeObject(message);
await this.Clients.All.SendAsync("ReceiveMessage", msg);
}
/// <summary>
/// 向指的用户发送
/// </summary>
/// <param name="user"></param>
/// <param name="message"></param>
/// <returns></returns>
public async Task SendMessageToUser(string user, object message)
{
var msg = JsonConvert.SerializeObject(message);
await Clients.All.SendAsync("ReceiveMessage", user, msg);
}
}
}

View File

@ -0,0 +1,68 @@
using LY.App.Model;
using LY.App.Service;
using Mapster;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace LY.App.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class AlarmController : ControllerBase
{
private readonly AlarmService _alarmService;
public AlarmController(AlarmService alarmService)
{
_alarmService = alarmService;
}
/// <summary>
///列表
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("list")]
public async Task<IActionResult> List([FromQuery] AlarmReq input)
{
var result = await _alarmService.GetPage(input);
return Ok(result);
}
/// <summary>
/// 新增告警
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("add")]
public async Task<IActionResult> AddAlarm(RevData input)
{
var result = await _alarmService.AddAlarm(input);
return Ok(result);
}
/// <summary>
/// 获取指定批次的告警详情
/// </summary>
/// <param name="batchid"></param>
/// <returns></returns>
[HttpPost("detail")]
public async Task<IActionResult> detail(long batchid)
{
var result = await _alarmService.GetByBatchId(batchid);
return Ok(result);
}
/// <summary>
/// 统计报表
/// </summary>
/// <param name="start"></param>
/// <param name="end"></param>
/// <returns></returns>
[HttpGet("report")]
public async Task<IActionResult> report(DateTime? start, DateTime? end)
{
var result=await _alarmService.GetReport(start, end);
return Ok(result);
}
}
}

View File

@ -0,0 +1,62 @@
using LY.App.Model;
using LY.App.Service;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Drawing.Printing;
using System.Security.Permissions;
namespace LY.App.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class DeviceController : ControllerBase
{
private readonly DeviceService _deviceService;
public DeviceController(DeviceService deviceService)
{
_deviceService = deviceService;
}
/// <summary>
/// 新增设备
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("add")]
public async Task<IActionResult> Add(AddDevice input)
{
var result =await _deviceService.Add(input);
return Ok(result);
}
/// <summary>
/// 列表
/// </summary>
/// <param name="pageNum"></param>
/// <param name="pageSize"></param>
/// <param name="key"></param>
/// <returns></returns>
[HttpGet("list")]
public async Task<IActionResult> List(int pageNum, int pageSize, string key)
{
var result = await _deviceService.List(pageNum, pageSize, key);
return Ok(result);
}
/// <summary>
///更新
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("updata")]
public async Task<IActionResult> Updata(UpdateDevice input)
{
var result = _deviceService.updata(input);
return Ok(result);
}
[HttpDelete("delete")]
public async Task<IActionResult> Delete(long id)
{
var result = await _deviceService.delete(id);
return Ok(result);
}
}
}

View File

@ -0,0 +1,40 @@
using LY.App.Common.Redis;
using LY.App.Device;
using LY.App.Model;
using LY.App.Service;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace LY.App.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class HomeController : ControllerBase
{
private readonly DeviceService _deviceService;
private readonly DeviceManager deviceManager = DeviceManager.Instance;
private readonly PositionService _positionService;
private readonly RedisService _redisService;
public HomeController(DeviceService deviceService, PositionService positionService, RedisService redisService)
{
_deviceService = deviceService;
_positionService = positionService;
_redisService = redisService;
}
/// <summary>
/// 首页
/// </summary>
/// <returns></returns>
[HttpGet("view")]
public async Task<IActionResult> view()
{
ApiResult result = new ApiResult();
var positions = await _positionService.Index();
result.data = new
{
positions
};
return Ok(result);
}
}
}

View File

@ -0,0 +1,63 @@
using LY.App.Model;
using LY.App.Service;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace LY.App.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class PositionController : ControllerBase
{
private readonly PositionService _positionService;
public PositionController(PositionService positionService)
{
_positionService = positionService;
}
/// <summary>
/// 获取列表
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("list")]
public async Task<IActionResult> Get(PositionQueryInput input)
{
var positions = _positionService.GetList(input);
return Ok(positions);
}
/// <summary>
/// 删除
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpDelete("delete")]
public async Task<IActionResult> Delete(long id)
{
var position = await _positionService.Delete(id);
return Ok(position);
}
/// <summary>
/// 新增
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("add")]
public async Task<IActionResult> Add(AddPosition input)
{
var result = await _positionService.MainAdd(input);
return Ok(result);
}
/// <summary>
/// 更新
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("update")]
public async Task<IActionResult> update(UpdatePosition input)
{
var result = await _positionService.Update(input);
return Ok(result);
}
}
}

View File

@ -0,0 +1,113 @@
using LY.App.Model;
using LY.App.Service;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace LY.App.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class UserController : ControllerBase
{
private readonly UserService _userService;
public UserController(UserService userService)
{
_userService = userService;
}
#region
[HttpPost("add")]
public async Task<IActionResult> Add(AddUser input)
{
var result = await _userService.Add(input);
return Ok(result);
}
/// <summary>
/// 列表
/// </summary>
/// <returns></returns>
[HttpGet("list")]
public async Task<IActionResult> GetList(int pageSize = 10, int pageNum = 1, string key = "")
{
var result = await _userService.GetPage(pageSize, pageNum, key);
return Ok(new ApiResult() { data = result });
}
/// <summary>
/// 删除
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
[HttpDelete("remove")]
public async Task<IActionResult> Delete(IEnumerable<long> ids)
{
var result = await _userService.SoftDelete(ids);
return Ok(new ApiResult()
{
code = string.IsNullOrEmpty(result) ? 0 : 1,
msg = result
});
}
/// <summary>
/// 更新用户对象
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("update")]
public async Task<IActionResult> Update(UpdateUser input)
{
var result = await _userService.Update(input);
return Ok(new ApiResult()
{
code = string.IsNullOrEmpty(result) ? 0 : 1,
msg = result,
});
}
/// <summary>
/// 更新密码
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("updatepwd")]
public async Task<IActionResult> updatepwd(UpdatePwdDto input)
{
var result = await _userService.UpdatePwd(input);
return Ok(new ApiResult()
{
code = string.IsNullOrEmpty(result) ? 0 : 1,
msg = result
});
}
[HttpGet("detail")]
public async Task<IActionResult> Detail(long id)
{
var result = await _userService.GetUserById(id);
if (result == null)
{
return Ok(new ApiResult()
{
code = 0,
data = { },
msg = ""
});
}
else
{
return Ok(new ApiResult()
{
code = 0,
data = result,
msg = ""
});
}
}
#endregion
#region
[HttpPost("login")]
public async Task<IActionResult> Login(LoginModel input)
{
var result = await _userService.Login(input);
return Ok(result);
}
#endregion
}
}

View File

@ -0,0 +1,240 @@
namespace LY.App.Device.Command
{
public static class GraphCommand
{
/// <summary>
/// 获取打击状态
/// </summary>
/// <returns></returns>
public static string AttackStatus()
{
return @"{
widebandJammer {
band15
band24
band58
}
}";
}
/// <summary>
/// 获取无人机
/// </summary>
/// <returns></returns>
public static string GetFindTarget()
{
return @"{drone {
attack_bands,
attack_type,
attacking,
can_attack,
can_takeover,
created_time,
deleted_time,
description,
direction,
distance,
has_duplicate,
id,
initial_location {
lat
lng
},
jamming_conflicts,
lastseen,
latitude,
link_id,
localization {
lat
lng
},
longitude,
name,
height,
distance,
seen_sensor {
detected_freq_khz
noise_dbm
sensor_id
signal_dbm
snr_dB
},
state,
whitelisted
}}";
}
/// <summary>
/// 获取设备状态信息
/// </summary>
public static string GetDeveceStatusInfo()
{
return @"{
devices{
id
config
gps_fixed
class
}
}";
}
public static string GetAdsb()
{
return @"{
droneAdsB {
support
drone {
id
speed
height
latitude
longitude
yaw_angle
confirmed
registration
typecode
manufacturer
model
owner
}
}
}";
}
/// <summary>
/// 获取传感器状态
/// </summary>
/// <returns></returns>
public static string GetSensorStatus()
{
return @"{sensor {
config,
faults,
id,
mac,
name,
sensor_status {
built_time
component_type
first_seen
git_hash
ip_address
last_seen
temperature
version
},
state,
ttl
}}";
}
/// <summary>
/// 白名单
/// </summary>
/// <returns></returns>
public static string GetWhitelist()
{
return @"{whitelist{dronetype,id}}";
}
/// <summary>
/// 系统能力查询
/// </summary>
/// <returns></returns>
public static string GetSystemCapability()
{
return @"{sysCapability}";
}
/// <summary>
/// 获取宽带打击的频段
/// </summary>
/// <returns></returns>
public static string widebanJammer()
{
return @"{widebandJammer{
band12
band14
band15
band18
band24
band4
band58
band9}}";
}
/// <summary>
/// 添加白名单
/// </summary>
/// <param name="droneId"></param>
/// <param name="droneType"></param>
/// <returns></returns>
public static string AddWhitelist(string droneId, string droneType)
{
return "mutation{addWhitelist(id:\"" + droneId + "\"\ndronetype:\"" + droneType + "\"\ntimerange:\"permanent,permanent\")}";
}
/// <summary>
/// 删除白名单
/// </summary>
/// <param name="droneId"></param>
/// <param name="droneType"></param>
/// <returns></returns>
public static string DeleteWhitelist(string droneId)
{
return "mutation{deleteWhitelist(id:\"" + droneId + "\")}";
}
/// <summary>
/// 精确打击flse开始true停止
/// </summary>
/// <param name="isAttack"></param>
/// <param name="droneId"></param>
/// <returns></returns>
public static string AccurateAttack(bool isAttack, string droneId)
{
string cmdstr = "mutation{ attack( cancel:true \n takeover:false \n id: \"" + droneId + "\"\n )}";
if (isAttack)
{
cmdstr = "mutation{ attack( cancel:false \n takeover:false \n id: \"" + droneId + "\"\n )}";
}
else
{
cmdstr = "mutation{ attack( cancel:true \n takeover:false \n id: \"" + droneId + "\"\n )}";
}
return cmdstr;
}
/// <summary>
/// 自动打击
/// </summary>
/// <param name="isAuto"></param>
/// <returns></returns>
public static string AutoAttack(bool isAuto)
{
string cmdstr = "mutation{ autoAttack( on: " + isAuto + " \n wbEnabled: false ) }";
if (isAuto)
{
cmdstr = "mutation{ autoAttack( on:true \n wbEnabled: false ) }";
}
else
{
cmdstr = "mutation{ autoAttack( on:false \n wbEnabled: false ) }";
}
return cmdstr.ToLower();
}
/// <summary>
/// 宽带打击
/// </summary>
/// <param name="band">15,24,58</param>
/// <param name="status">true,开启false关闭</param>
/// <returns></returns>
public static string WidebandAttack(string band, bool status)
{
string cmdstr = "mutation{ wideband_attack( band: \"" + band + "\"\n wb_status:" + status + ")\n" +
"{band15\nband24\nband58\nband9}}";
return cmdstr.ToLower();
}
}
}

429
Device/DeviceManager.cs Normal file
View File

@ -0,0 +1,429 @@
using System;
using System.Collections.Concurrent;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using GraphQL.Client.Http;
using GraphQL.Transport;
using GraphQL.Client.Serializer.Newtonsoft;
using GraphQL.Client.Abstractions;
using MQTTnet;
using MQTTnet.Client;
using System.Net.Http.Headers;
using LY.App.Common.Redis;
using LY.App.Model;
using Newtonsoft.Json;
using StackExchange.Redis;
using LY.App.Common.HttpUtil;
using LY.App.Device.Command;
namespace LY.App.Device
{
public enum ProtocolType
{
TCP,
UDP,
GraphQL,
MQTT
}
public class Device
{
public long Id { get; set; }
public ProtocolType Protocol { get; set; }
public string IpAddress { get; set; }
public int Port { get; set; }
public string GraphQlEndpoint { get; set; }
public bool IsConnected { get; set; }
public string BrokerAddress { get; set; } // MQTT Broker 地址
public string Topic { get; set; } // MQTT 主题
public string username { get; set; } // MQTT 用户名
public string password { get; set; } // MQTT 密码
/// <summary>
/// 数据接收事件
/// </summary>
public Action<string> DataReceived { get; set; }
}
public class DeviceManager
{
private readonly ConcurrentDictionary<long, Device> _devices = new();
private readonly ConcurrentDictionary<long, IMqttClient> _mqttClients = new(); // 维护 MQTT 客户端
private static DeviceManager _instance;
private readonly RedisService _redis = ServiceLocator.Instance.GetService<RedisService>();
private static readonly object _lock = new object();
private DeviceManager() { }
public static DeviceManager Instance
{
get
{
lock (_lock)
{
if (_instance == null)
{
_instance = new DeviceManager();
}
return _instance;
}
}
}
public void AddDevice(Device device)
{
if (_devices.TryAdd(device.Id, device))
{
switch (device.Protocol)
{
case ProtocolType.TCP:
Task.Run(() => HandleTcpDevice(device));
break;
case ProtocolType.UDP:
Task.Run(() => HandleUdpDevice(device));
break;
case ProtocolType.GraphQL:
Task.Run(() => HandleGraphQLDevice(device));
break;
case ProtocolType.MQTT:
Task.Run(() => HandleMqttDevice(device));
break;
}
}
}
public async void RemoveDevice(long deviceId)
{
if (_devices.TryRemove(deviceId, out var device))
{
device.IsConnected = false; // 终止接收数据的循环
// 对于 MQTT 协议,断开客户端连接并清理
if (device.Protocol == ProtocolType.MQTT && _mqttClients.TryRemove(deviceId, out var mqttClient))
{
try
{
if (mqttClient.IsConnected)
{
await mqttClient.DisconnectAsync();
}
mqttClient.Dispose();
}
catch (Exception ex)
{
Console.WriteLine($"设备 {deviceId} 断开 MQTT 连接时出错: {ex.Message}");
}
}
}
}
public ConcurrentDictionary<long, Device> GetAllDevices()
{
return _devices;
}
public async Task SendCommand(long deviceId, string command)
{
if (_devices.TryGetValue(deviceId, out var device))
{
var data = Encoding.UTF8.GetBytes(command);
switch (device.Protocol)
{
case ProtocolType.TCP:
using (var client = new TcpClient())
{
await client.ConnectAsync(device.IpAddress, device.Port);
var stream = client.GetStream();
await stream.WriteAsync(data, 0, data.Length);
}
break;
case ProtocolType.UDP:
using (var udpClient = new UdpClient())
{
await udpClient.SendAsync(data, data.Length, device.IpAddress, device.Port);
}
break;
case ProtocolType.GraphQL:
using (var graphQLClient = new GraphQLHttpClient(device.GraphQlEndpoint, new NewtonsoftJsonSerializer()))
{
var token = await _redis.GetAsync<string>(RedisKeyList.DeviceTokenById(deviceId));
var response = await Send<dynamic>(device.GraphQlEndpoint, token, command);
}
break;
case ProtocolType.MQTT:
if (_mqttClients.TryGetValue(deviceId, out var mqttClient) && mqttClient.IsConnected)
{
var message = new MqttApplicationMessageBuilder()
.WithTopic(device.Topic)
.WithPayload(data)
.Build();
await mqttClient.PublishAsync(message);
Console.WriteLine($"设备 {deviceId} (MQTT) 发送命令 {command} 成功");
}
else
{
throw new Exception("MQTT 客户端未连接");
}
break;
}
}
}
private async Task HandleTcpDevice(Device device)
{
try
{
using var client = new TcpClient();
await client.ConnectAsync(device.IpAddress, device.Port);
device.IsConnected = true;
var buffer = new byte[1024];
var stream = client.GetStream();
while (device.IsConnected)
{
var bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
if (bytesRead > 0)
{
var data = Encoding.UTF8.GetString(buffer, 0, bytesRead);
device.DataReceived?.Invoke(data);
}
}
}
catch (Exception)
{
device.IsConnected = false;
}
}
private async Task HandleUdpDevice(Device device)
{
using var udpClient = new UdpClient(device.Port);
device.IsConnected = true;
while (device.IsConnected)
{
var result = await udpClient.ReceiveAsync();
var data = Encoding.UTF8.GetString(result.Buffer);
device.DataReceived?.Invoke(data);
}
}
private async Task HandleGraphQLDevice(Device device)
{
try
{
var user = new
{
device.username,
device.password
};
var jsonstr = JsonConvert.SerializeObject(user);
//登陆 历正设备获取token
var loginUrl = $"https://{device.IpAddress}/login";
var req = await RequestUtil.PostAsync(loginUrl, jsonstr);
if (req != null)
{
var userinfo = JsonConvert.DeserializeObject<Dto.LoginModel>(req);
if (userinfo != null)
{
Console.WriteLine($"设备 {device.Id} 连接成功");
string token = userinfo.token;
device.IsConnected = true;
await _redis.SetAsync<string>(RedisKeyList.DeviceTokenById(device.Id), token);
device.GraphQlEndpoint = $"https://{device.IpAddress}/rf/graphql";//更新graphql地址
using var graphQLClient = new GraphQLHttpClient(device.GraphQlEndpoint, new NewtonsoftJsonSerializer());
while (device.IsConnected)
{
var response = await Send<dynamic>(device.GraphQlEndpoint, token, GraphCommand.GetFindTarget());
device.DataReceived?.Invoke(response.ToString());
await Task.Delay(1000);
}
}
}
Console.WriteLine("登录失败");
}
catch (Exception)
{
device.IsConnected = false;
}
}
private async Task<T> Send<T>(string url, string token, string cmd)
{
try
{
var httpClientHandle = new HttpClientHandler();
httpClientHandle.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
var client = new HttpClient(httpClientHandle);
var opt = new GraphQLHttpClientOptions() { EndPoint = new Uri(url) };
var graphQLClient = new GraphQLHttpClient(opt, new NewtonsoftJsonSerializer(), client);
var request = new GraphQL.GraphQLRequest
{
Query = cmd
};
graphQLClient.HttpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("Safari", "537.36"));
graphQLClient.HttpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
graphQLClient.HttpClient.DefaultRequestHeaders.Add("ContentType", "application/json");
var graphQLResponse = await graphQLClient.SendQueryAsync<T>(request);
return graphQLResponse.Data;
}
catch { }
return default;
}
private async Task HandleMqttDevice(Device device)
{
try
{
var factory = new MqttFactory();
var mqttClient = factory.CreateMqttClient();
var options = new MqttClientOptionsBuilder()
.WithTcpServer(device.IpAddress, device.Port)
.WithCredentials(device.username, device.password)
.WithClientId(Guid.NewGuid().ToString())
.Build();
mqttClient.ConnectedAsync += async e =>
{
Console.WriteLine($"设备 {device.Id} 连接成功");
await mqttClient.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic(device.Topic).Build());
};
mqttClient.DisconnectedAsync += async e =>
{
Console.WriteLine($"设备 {device.Id} 掉线,尝试重连...");
device.IsConnected = false;
await HandleDeviceConnection(device);
};
mqttClient.ApplicationMessageReceivedAsync += e =>
{
// 接收到 MQTT 消息后,处理数据
var message = Encoding.UTF8.GetString(e.ApplicationMessage.PayloadSegment.Array);
device.DataReceived?.Invoke(message);
return Task.CompletedTask;
};
await mqttClient.ConnectAsync(options);
if (mqttClient.IsConnected)
{
_mqttClients[device.Id] = mqttClient; // 保存 MQTT 客户端实例
device.IsConnected = true;
}
//while (device.isconnected)
//{
// await task.delay(1000);
//}
// 循环结束后断开连接
//if (!mqttClient.IsConnected)
//{
// await mqttClient.DisconnectAsync();
// mqttClient.Dispose();
//}
}
catch (Exception ex)
{
Console.WriteLine($"设备 {device.Id} 连接失败: {ex.Message}");
device.IsConnected = false;
}
}
private async Task HandleDeviceConnection(Device device)
{
int retryDelay = 1000; // 初始重连间隔(毫秒)
int maxDelay = 30000; // 最大重连间隔30秒
while (!device.IsConnected)
{
try
{
switch (device.Protocol)
{
case ProtocolType.TCP:
await HandleTcpDevice(device);
break;
case ProtocolType.UDP:
await HandleUdpDevice(device);
break;
case ProtocolType.GraphQL:
await HandleGraphQLDevice(device);
break;
case ProtocolType.MQTT:
await HandleMqttDevice(device);
break;
}
if (device.IsConnected)
{
// 连接成功,重置重连间隔
retryDelay = 1000;
}
}
catch (Exception ex)
{
Console.WriteLine($"设备 {device.Id} 连接失败,{ex.Message}{retryDelay}ms 后重试...");
}
Console.WriteLine($"设备 {device.Id} 连接失败,{retryDelay}ms 后重试...");
await Task.Delay(retryDelay);
// 指数退避算法,避免频繁重试
retryDelay = Math.Min(retryDelay * 2, maxDelay);
}
}
public async Task<bool> TestConnection(long deviceId)
{
if (_devices.TryGetValue(deviceId, out var device))
{
try
{
switch (device.Protocol)
{
case ProtocolType.TCP:
using (TcpClient client = new TcpClient())
{
await client.ConnectAsync(device.IpAddress, device.Port);
return true;
}
case ProtocolType.UDP:
using (var udpClient = new UdpClient())
{
await udpClient.SendAsync(new byte[] { 0 }, 1, device.IpAddress, device.Port);
return true;
}
case ProtocolType.GraphQL:
using (var graphQLClient = new GraphQLHttpClient(device.GraphQlEndpoint, new NewtonsoftJsonSerializer()))
{
var request = new GraphQL.GraphQLRequest { Query = "{ deviceStatus { id status } }" };
var response = await graphQLClient.SendQueryAsync<dynamic>(request);
return response.Data != null;
}
case ProtocolType.MQTT:
var factory = new MqttFactory();
var mqttClient = factory.CreateMqttClient();
var options = new MqttClientOptionsBuilder()
.WithTcpServer(device.BrokerAddress)
.WithClientId(Guid.NewGuid().ToString())
.Build();
await mqttClient.ConnectAsync(options);
await mqttClient.DisconnectAsync();
return true;
default:
return false;
}
}
catch (Exception)
{
return false;
}
}
return false;
}
public Device GetDevice(long deviceId)
{
_devices.TryGetValue(deviceId, out var device);
return device;
}
}
}

34
Device/Dto/LoginModel.cs Normal file
View File

@ -0,0 +1,34 @@
namespace LY.App.Device.Dto
{
public class LoginModel
{
/// <summary>
/// 秘钥
/// </summary>
public string token { get; set; }
/// <summary>
/// 用户信息
/// </summary>
public User user { get; set; }
}
public class User
{
/// <summary>
///
/// </summary>
public string id { get; set; }
/// <summary>
///
/// </summary>
public string displayName { get; set; }
/// <summary>
///
/// </summary>
public string imageUrl { get; set; }
/// <summary>
///
/// </summary>
public string role { get; set; }
}
}

21
Dockerfile Normal file
View File

@ -0,0 +1,21 @@
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["LY.App.csproj", "."]
RUN dotnet restore "./LY.App.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "LY.App.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "LY.App.csproj" -c Release -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "LY.App.dll"]

View File

@ -0,0 +1,23 @@
namespace LY.App.Extensions.DI
{
/// <summary>
/// Injection type
/// </summary>
public enum InjectionType
{
/// <summary>
/// Transient
/// </summary>
Transient,
/// <summary>
/// Scoped
/// </summary>
Scoped,
/// <summary>
/// Singleton
/// </summary>
Singleton
}
}

View File

@ -0,0 +1,43 @@
namespace LY.App.Extensions.DI
{
[AttributeUsage(AttributeTargets.Class)]
public class ServiceInjectionAttribute : Attribute
{
/// <summary>
///
/// </summary>
public Type InterfaceType { get; set; }
/// <summary>
/// 注入类型
/// </summary>
public InjectionType InjectionType { get; }
/// <summary>
/// 服务注入
/// </summary>
public ServiceInjectionAttribute()
{
this.InjectionType = InjectionType.Scoped;
}
/// <summary>
/// 服务注入
/// </summary>
/// <param name="injectionType">注入类型</param>
public ServiceInjectionAttribute(InjectionType injectionType)
{
this.InjectionType = injectionType;
}
/// <summary>
/// 服务注入
/// </summary>
/// <param name="interfaceType">服务的接口类型</param>
/// <param name="injectionType">注入的类型</param>
public ServiceInjectionAttribute(Type interfaceType, InjectionType injectionType)
{
this.InterfaceType = interfaceType;
this.InjectionType = injectionType;
}
}
}

View File

@ -0,0 +1,69 @@
using LY.App.Extensions.DI;
using System.Reflection;
namespace LY.App.Extensions
{
public static class AutoDIExtensions
{
/// <summary>
///
/// </summary>
/// <param name="serviceCollection"></param>
/// <returns></returns>
public static IServiceCollection ServicesAutoInjectionExtension(this IServiceCollection serviceCollection)
{
var directory = AppDomain.CurrentDomain.BaseDirectory;
var types = Directory.GetFiles(directory, "*.dll", SearchOption.TopDirectoryOnly)
.Where(s => !s.ToLower().Contains("sql"))
.Select(Assembly.LoadFrom)
.SelectMany(a => a.GetTypes());
Injection(serviceCollection, types);
return serviceCollection;
}
/// <summary>
/// 服务自动注入
/// </summary>
/// <param name="serviceCollection">需要自动注入服务的服务集合</param>
/// <param name="selector">应用于每个Assembly的筛选函数</param>
/// <exception cref="ArgumentOutOfRangeException">指定的注入类型不在可注入的范围内</exception>
/// <exception cref="NoImplementationInterfaceException">指定注入的类型未实现任何服务</exception>
/// <exception cref="ArgumentException">输入的参数错误1、注入的类型未实现指定的服务。2、指定的服务不是Interface类型</exception>
/// <returns>自动注入服务后的服务集合</returns>
public static IServiceCollection ServicesAutoInjection(this IServiceCollection serviceCollection, Func<Assembly, bool> selector)
{
var directory = AppDomain.CurrentDomain.BaseDirectory;
var types = Directory.GetFiles(directory, "*.dll", SearchOption.TopDirectoryOnly)
.Select(Assembly.LoadFrom)
.Where(selector)
.SelectMany(a => a.GetTypes());
Injection(serviceCollection, types);
return serviceCollection;
}
// 注入逻辑
private static void Injection(IServiceCollection serviceCollection, IEnumerable<Type> types)
{
foreach (var type in types)
{
var attribute = type.GetCustomAttribute<ServiceInjectionAttribute>();
if (attribute == null) continue;
switch (attribute.InjectionType)
{
case InjectionType.Transient:
serviceCollection.AddTransient(type);
break;
case InjectionType.Scoped:
serviceCollection.AddScoped(type);
break;
case InjectionType.Singleton:
serviceCollection.AddSingleton(type);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
}
}

24
LY - Backup.App.csproj Normal file
View File

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>disable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerfileContext>.</DockerfileContext>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="GraphQL" Version="8.4.1" />
<PackageReference Include="GraphQL.Client" Version="6.1.0" />
<PackageReference Include="GraphQL.Client.Serializer.Newtonsoft" Version="6.1.0" />
<PackageReference Include="GraphQL.NewtonsoftJson" Version="8.4.1" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.4" />
<PackageReference Include="MQTTnet" Version="4.3.7.1207" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.182" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Mapster" Version="7.4.0" />
</ItemGroup>
</Project>

33
LY.App.csproj Normal file
View File

@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>disable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerfileContext>.</DockerfileContext>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<DocumentationFile>obj\Debug\net6.0\ly.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="GraphQL" Version="8.4.1" />
<PackageReference Include="GraphQL.Client" Version="6.1.0" />
<PackageReference Include="GraphQL.Client.Serializer.Newtonsoft" Version="6.1.0" />
<PackageReference Include="GraphQL.NewtonsoftJson" Version="8.4.1" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.4" />
<PackageReference Include="MQTTnet" Version="4.3.7.1207" />
<PackageReference Include="NetTopologySuite" Version="2.6.0" />
<PackageReference Include="NetTopologySuite.IO.GeoJSON" Version="4.0.0" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.182" />
<PackageReference Include="StackExchange.Redis" Version="2.8.31" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Mapster" Version="7.4.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.21" />
</ItemGroup>
<ItemGroup>
<Folder Include="Common\SSE\" />
</ItemGroup>
</Project>

25
LY.App.sln Normal file
View File

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.7.34221.43
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LY.App", "LY.App.csproj", "{0962C31E-CDCF-438A-B1EA-9C0F1E240F93}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0962C31E-CDCF-438A-B1EA-9C0F1E240F93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0962C31E-CDCF-438A-B1EA-9C0F1E240F93}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0962C31E-CDCF-438A-B1EA-9C0F1E240F93}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0962C31E-CDCF-438A-B1EA-9C0F1E240F93}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9AE26013-632A-4ACF-9F3E-307C788F4266}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,104 @@
using LY.App.Service;
using System.Text;
namespace LY.App.MiddleWare
{
public class CustomErrorMiddleware
{
private readonly RequestDelegate _next;
private string body = string.Empty;
private readonly LogService _logger;
public CustomErrorMiddleware(RequestDelegate requestDelegate, LogService logger)
{
this._next = requestDelegate;
_logger = logger;
}
public async Task Invoke(HttpContext context, IConfiguration configuration)
{
try
{
// Check if the request is a form post with a file
if (context.Request.HasFormContentType && context.Request.Form.Files.Count > 0)
{
foreach (var file in context.Request.Form.Files)
{
// Log file information
//_logger.LogInformation($"File uploaded - Name: {file.Name}, " +
// $"FileName: {file.FileName}, " +
// $"ContentType: {file.ContentType}, " +
// $"Size: {file.Length} bytes");
}
}
else
{
context.Request.EnableBuffering();
using (var reader = new StreamReader(context.Request.Body,
encoding: Encoding.UTF8,
detectEncodingFromByteOrderMarks: false,
leaveOpen: true))
{
body = await reader.ReadToEndAsync();
// Do some processing with body…
// Reset the request body stream position so the next middleware can read it
context.Request.Body.Position = 0;
}
}
await _next.Invoke(context);
}
catch (Exception ex)
{
await HandleError(context, ex, configuration);
}
}
/// <summary>
/// 错误信息处理方法
/// </summary>
/// <param name="context"></param>
/// <param name="ex"></param>
/// <returns></returns>
private async Task HandleError(HttpContext context, Exception ex, IConfiguration configuration)
{
string pars = string.Empty;
var method = context.Request.Method.ToUpper();
var enable = configuration.GetSection("log2db").Value.ToLower();
if (enable == "true")
{
switch (method)
{
case "GET":
pars = context.Request.QueryString.ToString();
break;
case "POST":
// var requestReader = new StreamReader(context.Request.Body);
// pars = await requestReader.ReadToEndAsync();
pars = body;
break;
default:
break;
}
await _logger.AddLog(new Model.AddLog()
{
StackTrace = ex.StackTrace.Length > 1000 ? ex.StackTrace.Substring(0, 999) : ex.StackTrace,
Message = ex.Message,
Parameters = pars,
url = context.Request.Path,
});
}
Console.WriteLine($"错误消息:{ex.Message}错误追踪{ex.StackTrace}");
context.Response.StatusCode = 500;
context.Response.ContentType = "text/json;charset=utf-8;";
await context.Response.WriteAsJsonAsync(new { code = 1, msg = $"抱歉,服务端出错了,错误消息:{ex.Message}", data = "" });
}
}
public static class RequestCultureMiddlewareExtensions
{
public static IApplicationBuilder UseCustomErrorMiddleware(
this IApplicationBuilder builder)
{
return builder.UseMiddleware<CustomErrorMiddleware>();
}
}
}

156
Model/Alarm.cs Normal file
View File

@ -0,0 +1,156 @@
using Newtonsoft.Json;
using SqlSugar;
using System.ComponentModel;
namespace LY.App.Model
{
[SplitTable(SplitType.Month)]//按年分表 (自带分表支持 年、季、月、周、日)
[SugarTable("ly_alarm_{year}{month}{day}")]
public class Alarm : BaseEntity
{
/// <summary>
/// 无人机批次id
/// </summary>
[JsonConverter(typeof(ValueToStringConverter))]
[SugarColumn(ColumnName = "batch_id", ColumnDescription = "批次id")]
public long BatchId { get; set; }
/// <summary>
/// #无人机的序列号
/// </summary>
public string serial_number { get; set; }
/// <summary>
/// #无人机型号
/// </summary>
public string device_type { get; set; }
/// <summary>
///
/// </summary>
public int device_type_8 { get; set; }
/// <summary>
/// #遥控器的纬
/// </summary>
public double app_lat { get; set; }
public double app_lon { get; set; }
public double drone_lat { get; set; }
public double drone_lon { get; set; }
public double height { get; set; }
public double altitude { get; set; }
public double home_lat { get; set; }
public double home_lon { get; set; }
/// <summary>
/// ,#东向速度
/// </summary>
public double speed_E { get; set; }
/// <summary>
/// #北向速度
/// </summary>
public double speed_N { get; set; }
public double speed_U { get; set; }
/// <summary>
/// #信号增益
/// </summary>
[SugarColumn(ColumnName = "rssi", ColumnDescription = "信号增益")]
public double RSSI { get; set; }
/// <summary>
/// 无人机型号
/// </summary>
/// <summary>
/// 位置id
/// </summary>
[JsonConverter(typeof(ValueToStringConverter))]
[SugarColumn(ColumnName = "position_id", ColumnDescription = "阵地id")]
public long positionId { get; set; }
/// <summary>
/// 位置id
/// </summary>
[SugarColumn(ColumnName = "position_name", ColumnDescription = "阵地名字")]
public string PostionName { get; set; }
[JsonConverter(typeof(ValueToStringConverter))]
[SugarColumn(ColumnName = "device_Id", ColumnDescription = "设备id")]
public long DeviceId { get; set; }
/// <summary>
/// 来源设备名称
/// </summary>
[SugarColumn(ColumnName = "device_name", ColumnDescription = "DeviceName")]
public string DeviceName { get; set; }
/// <summary>
/// 频率
/// </summary>
[SugarColumn(ColumnName = "freq", ColumnDescription = "频率")]
public double freq { get; set; }
public int alarmLevel { get; set; } = 1;
[SugarColumn(ColumnName = "time", ColumnDescription = "上传时间")]
public long Time { get; set; }
/// <summary>
/// 创建时间
/// </summary>
[SplitField]
[SugarColumn(IsNullable = true, ColumnDescription = "创建时间")]
public override DateTime CreateTime { get; set; } = DateTime.Now;
}
public class RevData
{
/// <summary>
/// 设备序列号
/// </summary>
public string product_ad_id { get; set; }
public IEnumerable<AddAlarm> data { get; set; }
/// <summary>
/// time
/// </summary>
public long time { get; set; }
public double product_lat { get; set; }
public double product_lon { get; set; }
public string product_id { get; set; }
}
/// <summary>
/// 添加报警
/// </summary>
public class AddAlarm
{
/// <summary>
/// #无人机的序列号
/// </summary>
public string serial_number { get; set; }
/// <summary>
/// #无人机型号
/// </summary>
public string device_type { get; set; }
/// <summary>
///
/// </summary>
public int device_type_8 { get; set; }
/// <summary>
/// #遥控器的纬
/// </summary>
public double app_lat { get; set; }
public double app_lon { get; set; }
public double drone_lat { get; set; }
public double drone_lon { get; set; }
public double height { get; set; }
public double altitude { get; set; }
public double home_lat { get; set; }
public double home_lon { get; set; }
public double freq { get; set; }
/// <summary>
/// ,#东向速度
/// </summary>
public double speed_E { get; set; }
/// <summary>
/// #北向速度
/// </summary>
public double speed_N { get; set; }
public double speed_U { get; set; }
/// <summary>
/// #信号增益
/// </summary>
public double RSSI { get; set; }
}
}

27
Model/AlarmRepDto.cs Normal file
View File

@ -0,0 +1,27 @@
using Newtonsoft.Json;
using SqlSugar;
namespace LY.App.Model
{
public class AlarmRepDto
{
public string batchId { get; set; }
//非数据库字段
public DateTime startTime { get; set; }
//非数据库字段
public DateTime endTime { get; set; }
public double duration { get; set; }
public string model { get; set; }
public string sn { get; set; }
public double Frequency { get; set; }
//非数据库字段
public bool IsWhitelist { get; set; }
[SqlSugar.SugarColumn(IsIgnore = true)]
public string Whitelist { get; set; }
[JsonConverter(typeof(ValueToStringConverter))]
public long positionId { get; set; }
public string positionName { get; set; }
//public long position_id { get; set; }
}
}

42
Model/AlarmReq.cs Normal file
View File

@ -0,0 +1,42 @@
namespace LY.App.Model
{
public class AlarmReq
{
/// <summary>
/// 开始日期
/// </summary>
public DateTime? strartDate { get; set; }
public DateTime? endDate { get; set; }
public string sn { get; set; }
/// <summary>
/// 时长
/// </summary>
public int? startduration { get; set; }
/// <summary>
/// 结束时长
/// </summary>
public int? endduration { get; set; }
/// <summary>
/// 机型
/// </summary>
public string model { get; set; }
/// <summary>
/// 白名单
/// </summary>
public bool? IsWhitelist { get; set; }
/// <summary>
/// 频率
/// </summary>
public double? Frequency { get; set; }
/// <summary>
/// 页码
/// </summary>
public int pageNum { get; set; } = 1;
/// <summary>
/// 每页条数
/// </summary>
public int pageSize { get; set; } = 10;
}
}

30
Model/ApiResult.cs Normal file
View File

@ -0,0 +1,30 @@
namespace LY.App.Model
{
public class ApiResult
{
/// <summary>
/// 状态码
/// </summary>
public int code { get; set; } = 0;
/// <summary>
/// 返回信息
/// </summary>
public string msg { get; set; } = "";
/// <summary>
/// 返回数据
/// </summary>
public object data { get; set; }
public ApiResult() { }
public ApiResult(int codeValue, string msgValue)
{
code = codeValue;
msg = msgValue;
}
public ApiResult(bool sucecss, string msgValue)
{
code = sucecss == true ? 0 : 1;
msg = msgValue;
}
}
}

29
Model/BaseEntity.cs Normal file
View File

@ -0,0 +1,29 @@
using Newtonsoft.Json;
using SqlSugar;
namespace LY.App.Model
{
public class BaseEntity
{
/// <summary>
/// ID
/// 精度原因,返回前端时要转成字符串
/// </summary>
[SugarColumn(IsNullable = false, IsPrimaryKey = true)]
[JsonConverter(typeof(ValueToStringConverter))]
public long Id { get; set; }
/// <summary>
/// 创建时间
/// </summary>
[SugarColumn(IsNullable = true, ColumnDescription = "创建时间")]
public virtual DateTime CreateTime { get; set; } = DateTime.Now;
/// <summary>
/// 是否删除
/// </summary>
[SugarColumn(IsNullable = true, ColumnDescription = "是否删除", DefaultValue = "0")]
[JsonIgnore]
public bool IsDeleted { get; set; }
}
}

34
Model/BasePoint.cs Normal file
View File

@ -0,0 +1,34 @@
namespace LY.App.Model
{
public struct BasePoint
{
public BasePoint(double x, double y, double z, string time = null)
{
this.X = x;
this.Y = y;
this.Z = z;
this.Time = time;
}
public double X { get; set; }
public double Y { get; set; }
/// <summary>
/// 高度
/// </summary>
public double Z { get; set; }
public string Time { get; set; }
/// <summary>
/// 两点值是否相等
/// </summary>
/// <param name="basePoint"></param>
/// <returns></returns>
public bool Equal(BasePoint basePoint)
{
return basePoint.X == this.X && basePoint.Y == this.Y;
}
}
}

104
Model/DeviceEntity.cs Normal file
View File

@ -0,0 +1,104 @@
using LY.App.Device;
using Newtonsoft.Json;
using SqlSugar;
namespace LY.App.Model
{
[SugarTable("ly_device_info")]
public class DeviceEntity : BaseEntity
{
/// <summary>
///
/// </summary>
[SugarColumn(ColumnName = "device_sn", ColumnDescription = "DeviceSN")]
public string DeviceSN { get; set; }
[SugarColumn(Length = 63, IsNullable = true, ColumnDescription = "Name")]
public string Name { get; set; }
[SugarColumn(Length = 63, IsNullable = true, ColumnDescription = "account")]
public string account { get; set; }
[SugarColumn(Length = 63, IsNullable = true, ColumnDescription = "password")]
public string password { get; set; }
[JsonConverter(typeof(ValueToStringConverter))]
[SugarColumn(ColumnName = "position_id", ColumnDescription = "阵地id")]
public long PositionId { get; set; }
/// <summary>
/// 阵地名称
/// </summary>
[SugarColumn(ColumnName = "position_name", ColumnDescription = "阵地名称")]
public string PositionName { get; set; }
public string ip { get; set; }
public ProtocolType Protocol { get; set; }
public int Port { get; set; }
[SugarColumn(IsNullable = true)]
public string GraphQlEndpoint { get; set; }
[SugarColumn(IsNullable = true)]
public string BrokerAddress { get; set; } // MQTT Broker 地址
[SugarColumn(IsNullable = true)]
public string Topic { get; set; } // MQTT 主题
/// <summary>
/// 机型
/// </summary>
[SugarColumn(Length = 63, IsNullable = true, ColumnDescription = "机型")]
public string Model { get; set; }
/// <summary>
/// 位置
/// </summary>
[SugarColumn(ColumnName = "address", ColumnDescription = "位置", IsNullable = true)]
public string Address { get; set; }
/// <summary>
/// 经度
/// </summary>
[SugarColumn(ColumnName = "lon", ColumnDescription = "经度", IsNullable = true)]
public double Lon { get; set; }
/// <summary>
/// 纬度
/// </summary>
[SugarColumn(ColumnName = "lat", ColumnDescription = "纬度", IsNullable = true)]
public double Lat { get; set; }
/// <summary>
/// 高度
/// </summary>
[SugarColumn(ColumnName = "alt", ColumnDescription = "高度", IsNullable = true)]
public double Alt { get; set; }
}
public class AddDevice
{
public long PositionId { get; set; }
/// <summary>
/// 阵地名称
/// </summary>
public string PositionName { get; set; }
public string account { get; set; }
public string password { get; set; }
public string Name { get; set; }
public string DeviceSN { get; set; }
public string ip { get; set; }
public ProtocolType Protocol { get; set; }
public int Port { get; set; }
public string GraphQlEndpoint { get; set; }
public string BrokerAddress { get; set; } // MQTT Broker 地址
public string Topic { get; set; } // MQTT 主题
/// <summary>
/// 机型
/// </summary>
public string Model { get; set; }
/// <summary>
/// 位置
/// </summary>
public string Address { get; set; }
/// <summary>
/// 经度
/// </summary>
public double Lon { get; set; }
/// <summary>
/// 纬度
/// </summary>
public double Lat { get; set; }
}
public class UpdateDevice : AddDevice
{
public long Id { get; set; }
}
}

21
Model/LogEntity.cs Normal file
View File

@ -0,0 +1,21 @@
using SqlSugar;
namespace LY.App.Model
{
[SugarTable("ly_logInfo")]
public class LogEntity : BaseEntity
{
public string Parameters { get; set; }
public string Message { get; set; }
public string StackTrace { get; set; }
public string url { get; set; }
}
public class AddLog
{
public string Message { get; set; }
public string Parameters { get; set; }
public string StackTrace { get; set; }
public string url { get; set; }
}
}

10
Model/LoginModel.cs Normal file
View File

@ -0,0 +1,10 @@
namespace LY.App.Model
{
public class LoginModel
{
public string username { get; set; }
public string password { get; set; }
public string type { get; set; }
public string vertifyCode { get; set; }
}
}

107
Model/MultPolygonEntity.cs Normal file
View File

@ -0,0 +1,107 @@
using LY.App.Common;
using NetTopologySuite.Algorithm;
using NetTopologySuite.Geometries;
using Newtonsoft.Json;
using Pipelines.Sockets.Unofficial.Arenas;
using SqlSugar;
namespace LY.App.Model
{
/// <summary>
/// 多边形实体
/// </summary>
public class MultPolygonEntity : BaseEntity
{
/// <summary>
/// 空间位置点数据
/// </summary>
[SugarColumn(QuerySql = "st_astext(location)", ColumnDataType = "point", ColumnName = "location",
UpdateSql = "ST_GeomFromText(@location)",
InsertSql = "ST_GeomFromText(@location)", IsNullable = true, ColumnDescription = "空间位置点数据")]
[JsonIgnore]
public string? Location { get; set; } // Geometry
/// <summary>
/// 空间面数据
/// </summary>
[SugarColumn(QuerySql = "st_astext(region)", ColumnDataType = "multipolygon", ColumnName = "region",
UpdateSql = "ST_GeomFromText(@region)",
InsertSql = "ST_GeomFromText(@region)", IsNullable = true, ColumnDescription = "空间面数据")]
[JsonIgnore]
public string? Region { get; set; } //MultiPolygon Geometry
[SugarColumn(IsIgnore = true)]
public string RegionJson { get; set; }
/// <summary>
/// 空间位置点经度
/// </summary>
public double Lon { get; set; }
/// <summary>
/// 空间位置点纬度
/// </summary>
public double Lat { get; set; }
/// <summary>
/// 空间位置点Json数据
/// </summary>
public void SetRegionJson()
{
RegionJson = "";
if (!string.IsNullOrWhiteSpace(Region))
{
var geo = GeoJsonHelper.FromWKT(Region);
if (geo != null)
{
RegionJson = GeoJsonHelper.GetGeoJson(geo);
}
}
}
/// <summary>
/// 设置空间位置点
/// </summary>
/// <param name="region"></param>
public void SetRegion(MultiPolygon region)
{
if (region != null)
{
Region = region.AsText();
}
}
/// <summary>
/// 设置空间位置点
/// </summary>
/// <param name="lon"></param>
/// <param name="lat"></param>
public void SetLocation(double lon, double lat)
{
Lon = lon;
Lat = lat;
SetLocation();
}
/// <summary>
/// 设置空间位置点
/// </summary>
/// <param name="region"></param>
public void SetRegionJson(string region)
{
RegionJson = "";
if (!string.IsNullOrWhiteSpace(region))
{
var geo = GeoJsonHelper.FromWKT(region);
if (geo != null)
{
RegionJson = GeoJsonHelper.GetGeoJson(geo);
}
}
}
/// <summary>
/// 设置空间位置点
/// </summary>
public void SetLocation()
{
Location = $"POINT({Lon} {Lat})";
}
}
}

View File

@ -0,0 +1,22 @@
using Newtonsoft.Json;
using SqlSugar;
namespace LY.App.Model
{
public class PositionDeviceDto
{
public PositionInfo position { get; set; }
public List<DeviceItem> Devices { get; set; }
}
public class DeviceItem
{
[JsonConverter(typeof(ValueToStringConverter))]
public long Id { get; set; }
public string Name { get; set; }
public string DeviceSN { get; set; }
public double Lat { get; set; }
public double Lon { get; set; }
public string Model { get; set; }
}
}

153
Model/PositionInfo.cs Normal file
View File

@ -0,0 +1,153 @@
using SqlSugar;
namespace LY.App.Model
{
/// <summary>
/// 位置信息
/// </summary>
[SugarTable("ly_position")]
public class PositionInfo: MultPolygonEntity
{
/// <summary>
/// 名称
/// </summary>
[SugarColumn(Length = 63, IsNullable = true, ColumnDescription = "名称")]
public string Name { get; set; }
/// <summary>
/// 地址
/// </summary>
[SugarColumn(IsNullable = true, ColumnDescription = "地址")]
public string Address { get; set; }
/// <summary>
/// 联系人
/// </summary>
[SugarColumn(Length = 63, IsNullable = true, ColumnDescription = "联系人")]
public string ContactName { get; set; }
/// <summary>
/// 联系人电话
/// </summary>
[SugarColumn(Length = 31, IsNullable = true, ColumnDescription = "联系人电话")]
public string ContactTel { get; set; }
/// <summary>
/// 图片文件名
/// </summary>
[SugarColumn(IsNullable = true, ColumnDescription = "图片地址")]
public string ImageName { get; set; }
/// <summary>
/// 图片地址
/// </summary>
[SugarColumn(IsIgnore = true)]
public string ImageUrl { get; set; }
/// <summary>
/// 图片缩略图地址
/// </summary>
[SugarColumn(IsIgnore = true)]
public string ImageBriefUrl { get; set; }
/// <summary>
/// 启用时间
/// </summary>
[SugarColumn(IsNullable = true, ColumnDescription = "启用时间")]
public DateTime? EnableTime { get; set; }
/// <summary>
/// 是否启用
/// </summary>
[SugarColumn(ColumnDescription = "是否启用")]
public bool Enabled { get; set; }
/// <summary>
/// 状态
/// </summary>
[SugarColumn(IsNullable = true, ColumnDescription = "状态")]
public string Status { get; set; } = "离线";
/// <summary>
/// 备注
/// </summary>
[SugarColumn(IsNullable = true, ColumnDescription = "备注")]
public string Remarks { get; set; }
}
/// <summary>
/// 添加
/// </summary>
public class AddPosition
{
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 空间数据geojson
/// </summary>
public string RegionJson { get; set; }
/// <summary>
/// 经度
/// </summary>
public double Lon { get; set; }
/// <summary>
/// 纬度
/// </summary>
public double Lat { get; set; }
/// <summary>
/// 地址
/// </summary>
public string Address { get; set; }
/// <summary>
/// 联系人
/// </summary>
public string ContactName { get; set; }
/// <summary>
/// 联系人电话
/// </summary>
public string ContactTel { get; set; }
/// <summary>
/// 图片文件名
/// </summary>
public string ImageName { get; set; }
/// <summary>
/// 启用时间
/// </summary>
public DateTime? EnableTime { get; set; }
/// <summary>
/// 是否启用
/// </summary>
public bool Enabled { get; set; }
/// <summary>
/// 状态
/// </summary>
public string Status { get; set; } = "离线";
/// <summary>
/// 备注
/// </summary>
public string Remarks { get; set; }
}
/// <summary>
/// 更新
/// </summary>
public class UpdatePosition : AddPosition
{
/// <summary>
/// 主键id
/// </summary>
public long Id { get; set; }
}
}

View File

@ -0,0 +1,15 @@
namespace LY.App.Model
{
public class PositionQueryInput
{
public string Name { get; set; }
/// <summary>
/// 页码
/// </summary>
public int pageNum { get; set; } = 1;
/// <summary>
/// 每页条数
/// </summary>
public int pageSize { get; set; } = 10;
}
}

70
Model/UserEntity.cs Normal file
View File

@ -0,0 +1,70 @@
using SqlSugar;
namespace LY.App.Model
{
[SugarTable("ly_user")]
public class UserEntity
{
[Newtonsoft.Json.JsonConverter(typeof(ValueToStringConverter))]
[SugarColumn(IsPrimaryKey = true)]//long类型的主键会自动赋值
public long Id { get; set; }
/// <summary>
/// 用户名
/// </summary>
public string Name { get; set; }
/// <summary>
/// 密码
/// </summary>
public string Password { get; set; }
/// <summary>
/// 登录时间
/// </summary>
public DateTime LoginTime { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreateTime { get; set; }
/// <summary>
/// 上次更新密码时间
/// </summary>
public DateTime? UpdatePwdTime { get; set; }
/// <summary>
/// 删除状态
/// </summary>
public bool Disable { get; set; }
/// <summary>
/// 邮箱
/// </summary>
public string Email { get; set; }
/// <summary>
/// 是否管理员,如果不是管理员,不可操作
/// </summary>
public bool IsAdmin { get; set; }
}
public class AddUser
{
/// <summary>
/// 用户名
/// </summary>
public string Name { get; set; }
/// <summary>
/// 密码
/// </summary>
public string Password { get; set; }
/// <summary>
/// 是否管理员,如果不是管理员,不可操作
/// </summary>
public bool IsAdmin { get; set; }
}
public class UpdateUser:AddUser
{
public long Id { get; set; }
}
public class UpdatePwdDto
{
public long Id { get; set; }
public string oldPwd { get; set; }
public string newPwd { get; set; }
}
}

142
Program.cs Normal file
View File

@ -0,0 +1,142 @@
using LY.App.Extensions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json;
using SqlSugar;
using Microsoft.AspNetCore.Cors.Infrastructure;
using LY.App.Common.Redis;
using LY.App.Service;
using LY.App.Model;
using LY.App.MiddleWare;
using LY.App.Common.WebSocket;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers(options =>
{
// 禁用隐式 [Required] 属性标记对非可空引用类型的属性
options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true;
}).AddNewtonsoftJson()
.AddNewtonsoftJson(NewtonsoftInitialize); ;
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(
options =>
{
options.SwaggerDoc("v1", new OpenApiInfo()
{
Title = "LY.App-Api",
Version = "v1",
Description = "Api接口文档",
});
var path = Path.Combine(AppContext.BaseDirectory, "ly.xml");
options.IncludeXmlComments(path, true);
options.OrderActionsBy(_ => _.RelativePath);
});
builder.Services.AddCors(CorsOptionsEvnet);
//redis
string redisConnection = builder.Configuration.GetValue<string>("Redis:ConnectionString") ?? "localhost:6379";
// 注册 RedisService
builder.Services.AddSingleton(new RedisService(redisConnection));
////注册SignalR
builder.Services.AddSignalR();
//注册依赖注入
builder.Services.ServicesAutoInjectionExtension();
//数据库
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
//sqlsugar
builder.Services.AddTransient<SqlSugarClient>(sp =>
{
return new SqlSugarClient(new ConnectionConfig()
{
ConnectionString = connectionString,
DbType = DbType.MySql,
IsAutoCloseConnection = true,
ConfigureExternalServices = new ConfigureExternalServices()
{
EntityService = (x, p) => //处理列名
{
//最好排除DTO类
p.DbColumnName = UtilMethods.ToUnderLine(p.DbColumnName);//ToUnderLine驼峰转下划线方法
}
},
InitKeyType = InitKeyType.Attribute // 使用属性名作为列名
}, db =>
{
#if DEBUG
db.Aop.OnLogExecuting = (sql, pars) =>
{
Console.WriteLine(sql + "参数值:" + db.Utilities.SerializeObject(pars.ToDictionary(it => it.ParameterName, it => it.Value)));
};
//创建数据库和表的语句仅执行一次
//db.DbMaintenance.CreateDatabase();
db.CodeFirst.SetStringDefaultLength(2000).InitTables(typeof(LogEntity));
#endif
//过滤器写在这儿就行了
// db.QueryFilter.AddTableFilter<IDeleted>(it => it.IsDeleted == false);
});
});
SnowFlakeSingle.WorkId = Convert.ToInt32(builder.Configuration.GetSection("SnowFlakeWordId")?.Value ?? "1");
var app = builder.Build();
ServiceLocator.Instance = app.Services;
var device = app.Services.GetService<DeviceService>();
device?.Init();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
//路由匹配
app.UseRouting();
app.UseAuthorization();
app.UseCors("CorsPolicy");
//异常中间件
app.UseMiddleware<CustomErrorMiddleware>();
//执行匹配的端点
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<SocketHub>("/notification");
endpoints.MapControllers();
});
app.MapControllers();
app.Run();
static void NewtonsoftInitialize(MvcNewtonsoftJsonOptions options)
{
//忽略循环引用
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
//期类型默认格式化处理
options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
//解决命名不一致问题
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
//空值处理
//options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
/// <summary>
/// 跨域配置事件
/// </summary>
static void CorsOptionsEvnet(CorsOptions options)
{
options.AddPolicy("CorsPolicy", cors =>
{
cors.AllowAnyOrigin();
cors.AllowAnyHeader();
cors.AllowAnyMethod();
});
}
public static class ServiceLocator
{
public static IServiceProvider Instance { get; set; }
}

View File

@ -0,0 +1,40 @@
{
"profiles": {
"LY.App": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": true,
"applicationUrl": "http://localhost:5233"
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Docker": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
"environmentVariables": {
"ASPNETCORE_URLS": "http://+:80"
},
"publishAllPorts": true
}
},
"$schema": "https://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:53161",
"sslPort": 0
}
}
}

277
Service/AlarmService.cs Normal file
View File

@ -0,0 +1,277 @@
using LY.App.Common;
using LY.App.Common.Redis;
using LY.App.Common.WebSocket;
using LY.App.Extensions.DI;
using LY.App.Model;
using Mapster;
using Microsoft.AspNetCore.SignalR;
using SqlSugar;
using StackExchange.Redis;
namespace LY.App.Service
{
/// <summary>
/// 报警服务
/// </summary>
[ServiceInjection(InjectionType.Transient)]
public class AlarmService
{
private readonly SqlSugarClient _db;
private readonly IConfiguration _config;
private readonly RedisService _redisService;
private readonly PushService _pushService;
public AlarmService(SqlSugarClient db, IConfiguration config, RedisService redisService, PushService pushService)
{
_db = db;
_config = config;
_redisService = redisService;
_pushService = pushService;
}
/// <summary>
/// 新增报警信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<ApiResult> AddAlarm(RevData input)
{
await SetDeviceStataus(input);
if (input.data.Any())
{
var key = RedisKeyList.DeviceInfo(input.product_ad_id);
var deviceinfo = await _redisService.GetAsync<DeviceEntity>(key);
if (deviceinfo == null)
{
deviceinfo = await _db.CopyNew().Queryable<DeviceEntity>().Where(s => s.DeviceSN == input.product_ad_id).FirstAsync();
if (deviceinfo == null)
{
return new ApiResult() { code = 1, msg = "设备不存在" };
}
await _redisService.SetAsync(key, deviceinfo, TimeSpan.FromDays(1));
}
var entity = input.data.Adapt<List<Alarm>>();
foreach (var item in entity)
{
item.BatchId = await GetBatId(item.serial_number);
item.DeviceId = deviceinfo.Id;
item.DeviceName = deviceinfo.Name;
item.positionId = deviceinfo.PositionId;
item.PostionName = deviceinfo.PositionName;
item.Time = input.time;
}
await _db.CopyNew().Insertable(entity).SplitTable().ExecuteReturnSnowflakeIdListAsync();
//推送报警信息
await _pushService.SendMessageToAll(new { msgType = "event", data = entity });
}
return new ApiResult();
}
/// <summary>
/// 推送消息
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public async Task PushMessage(object message)
{
//推送前端无人机数据
await _pushService.SendMessageToAll(message);
}
/// <summary>
/// 设置设备在线状态,并更新数据
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
private async Task SetDeviceStataus(RevData input)
{
await _redisService.SetAsync(RedisKeyList.DeviceStatus(input.product_ad_id), true, TimeSpan.FromSeconds(5));
//更新 设备缓存
var key = RedisKeyList.DeviceInfo(input.product_ad_id);
var deviceinfo = await _redisService.GetAsync<DeviceEntity>(key);
if (deviceinfo == null)
{
deviceinfo.Lat = input.product_lat;
deviceinfo.Lon = input.product_lon;
await _redisService.SetAsync(key, deviceinfo);
}
}
private async Task<long> GetBatId(string droneId)
{
var timeSpan = Convert.ToDouble(_config["BatchId"]);
var key = RedisKeyList.BatchIdBysn(droneId);
//从redis取出batchid如果没有就新加一个每次访问都重置一下过期时间来模拟滑动过期
var batchId = await _redisService.GetAsync<long>(key);
if (batchId == 0)
{
batchId = SnowFlakeSingle.Instance.NextId();
}
await _redisService.SetAsync(key, batchId, TimeSpan.FromSeconds(timeSpan));
return batchId;
}
/// <summary>
/// //根据batchId获取报警信息
/// </summary>
/// <param name="batchId"></param>
/// <returns></returns>
public async Task<ApiResult> GetByBatchId(long batchId)
{
var items = await _db.Queryable<Alarm>().SplitTable()
.Where(s => s.BatchId == batchId)
.OrderBy(s => s.Id).Select(s => new
{
Lon = s.drone_lon,
Lat = s.drone_lat,
Alt = s.height,
s.CreateTime,
AlarmLevel = s.alarmLevel,
}).ToListAsync();
return new ApiResult() { data = items };
}
/// <summary>
/// 分页
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<ApiResult> GetPage(AlarmReq input)
{
var result = await CreatePage(input);
return new ApiResult()
{
code = 0,
data = new
{
total = result.Item1,
items = result.Item2
}
};
}
/// <summary>
/// 热力图
/// </summary>
/// <returns></returns>
public async Task<ApiResult> hotmap()
{
var query = await _db.Queryable<Alarm>().SplitTable()
.GroupBy(x => new { Lon = SqlFunc.Round(x.drone_lon, 4), Lat = SqlFunc.Round(x.drone_lat, 4) }) // 按经纬度分组
.Select(x => new
{
Lon = SqlFunc.Round(x.drone_lon, 4),
Lat = SqlFunc.Round(x.drone_lat, 4),
Count = SqlFunc.AggregateCount(x.BatchId), // 统计去重后的BatchId
})
.ToListAsync();
return new ApiResult() { data = query };
}
/// <summary>
/// 列表
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<Tuple<int, List<AlarmRepDto>>> CreatePage(AlarmReq input)
{
RefAsync<int> total = 0;
var items = await _db.Queryable<Alarm>().SplitTable()
.WhereIF(input.Frequency.HasValue, st => st.freq == input.Frequency.Value)
.WhereIF(!string.IsNullOrEmpty(input.sn), s => s.serial_number.Contains(input.sn))
.WhereIF(!string.IsNullOrEmpty(input.model), st => st.device_type == input.model)
.WhereIF(input.strartDate.HasValue, st => st.CreateTime >= input.strartDate.Value)
.WhereIF(input.endDate.HasValue, st => st.CreateTime <= input.endDate.Value.AddDays(1))
.OrderBy(s => s.BatchId, OrderByType.Desc)
.GroupBy(s => new { s.BatchId, s.serial_number, s.device_type, s.positionId, s.PostionName })
.Select(st => new AlarmRepDto
{
batchId = st.BatchId.ToString(),
startTime = SqlFunc.AggregateMin(st.CreateTime),
endTime = SqlFunc.AggregateMax(st.CreateTime),
sn = st.serial_number,
Frequency = SqlFunc.AggregateMax(st.freq),
duration = (SqlFunc.AggregateMax(st.CreateTime) - SqlFunc.AggregateMin(st.CreateTime)).TotalSeconds,
model = st.device_type,
positionId = st.positionId,
positionName = st.PostionName
}).MergeTable()//合并查询
.ToPageListAsync(input.pageNum, input.pageSize, total);
return Tuple.Create(total.Value, items);
}
/// <summary>
/// 报表统计
/// </summary>
/// <returns></returns>
public async Task<ApiResult> GetReport(DateTime? start, DateTime? end)
{
start = start.HasValue ? start.Value : DateTime.Now.AddMonths(-1);
end = end.HasValue ? end.Value.AddDays(1) : DateTime.Now.Date.AddDays(1);
var query = await _db.Queryable<Alarm>().SplitTable()
.WhereIF(start.HasValue, st => st.CreateTime >= start.Value)
.WhereIF(end.HasValue, st => st.CreateTime <= end.Value.AddDays(1))
.GroupBy(s => new { s.BatchId, s.serial_number, s.device_type, s.positionId, s.PostionName, s.DeviceId, s.DeviceName })
.Select(st => new
{
batchId = st.BatchId,
startTime = SqlFunc.AggregateMin(st.CreateTime),
endTime = SqlFunc.AggregateMax(st.CreateTime),
sn = st.serial_number,
Frequency = SqlFunc.AggregateMax(st.freq),
duration = (SqlFunc.AggregateMax(st.CreateTime) - SqlFunc.AggregateMin(st.CreateTime)).TotalSeconds,
model = st.device_type,
positionName = st.PostionName,
deviceId = st.DeviceId,
deviceName = st.DeviceName
}).MergeTable()//合并查询
.ToListAsync();
return new ApiResult()
{
data = new
{
freq = query.GroupBy(s => new { freq = FreqConvert.CoverFreq(s.Frequency) })
.Select(b => new
{
b.Key.freq,
count = b.Count()
}),
model = query.GroupBy(s => new { s.model })
.Select(b => new
{
b.Key.model,
count = b.Count()
}),
position = query.GroupBy(s => new { s.positionName })
.Select(s => new
{
s.Key.positionName,
count = s.Count()
}),
hotmap = await GenerateHotMap(query.Select(s => s.batchId).ToList()),
device = query.GroupBy(s => new { s.deviceName })
.Select(s => new
{
s.Key.deviceName,
count = s.Count()
}),
date = query.GroupBy(s => s.startTime.ToString("yyyy-MM-dd"))
.Select(it => new { it.Key, count = it.Count() })
}
};
}
/// <summary>
/// 热力图取经纬度4位数
/// </summary>
/// <param name="batchIds"></param>
/// <returns></returns>
private async Task<object> GenerateHotMap(List<long> batchIds)
{
return await _db.Queryable<Alarm>().SplitTable()
.Where(s => s.drone_lat > 0 && s.drone_lon > 0 && batchIds.Contains(s.BatchId))
.Select(s => new
{
drone_lon = SqlFunc.Round(s.drone_lon, 4),
drone_lat = SqlFunc.Round(s.drone_lat, 4),
})
.GroupBy(s => new { s.drone_lat, s.drone_lon })
.Select(s => new
{
Key = new { Lon = SqlFunc.Round(s.drone_lat, 4), Lat = SqlFunc.Round(s.drone_lon, 4) },
val = SqlFunc.AggregateCount(s)
})
.ToListAsync();
}
}
}

191
Service/DeviceService.cs Normal file
View File

@ -0,0 +1,191 @@
using GraphQL;
using LY.App.Common.Redis;
using LY.App.Device;
using LY.App.Extensions.DI;
using LY.App.Model;
using Mapster;
using Newtonsoft.Json;
using SqlSugar;
namespace LY.App.Service
{
[ServiceInjection(InjectionType.Transient)]
public class DeviceService
{
private readonly SqlSugarClient _db;
private readonly IConfiguration _config;
private readonly RedisService _redisService;
private readonly AlarmService _alarmService;
private readonly DeviceManager deviceManager = DeviceManager.Instance;
public DeviceService(SqlSugarClient db, IConfiguration config, RedisService redisService, AlarmService alarmService)
{
_db = db;
_config = config;
_redisService = redisService;
_alarmService = alarmService;
}
/// <summary>
/// 分页查询
/// </summary>
/// <param name="pageNum"></param>
/// <param name="pageSize"></param>
/// <param name="key"></param>
/// <returns></returns>
public async Task<object> List(int pageNum, int pageSize, string key)
{
RefAsync<int> total = 0;
var items = await _db.Queryable<DeviceEntity>()
.Where(s => s.IsDeleted == false)
.WhereIF(!string.IsNullOrEmpty(key), s => s.Name.Contains(key) || s.DeviceSN.Contains(key))
.OrderByDescending(s => s.Id)
.ToPageListAsync(pageNum, pageSize, total);
return new
{
total = total.Value,
items
};
}
/// <summary>
/// 新增设备
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<ApiResult> Add(AddDevice input)
{
var entity = input.Adapt<DeviceEntity>();
var id = await _db.Insertable(entity).ExecuteReturnSnowflakeIdAsync();
entity.Id = id;
await AddDevice2Manager(entity);
var key = RedisKeyList.DeviceInfo(entity.DeviceSN);
await _redisService.SetAsync(key, entity, TimeSpan.FromDays(10));
return new ApiResult()
{
code = 0,
msg = "success"
};
}
/// <summary>
/// 更新设备信息
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
public async Task<ApiResult> updata(UpdateDevice input)
{
var entity = input.Adapt<DeviceEntity>();
await _db.Updateable(entity).ExecuteCommandAsync();
return new ApiResult()
{
code = 0,
msg = "success"
};
}
/// <summary>
/// 删除设备
/// </summary>
/// <param name="Id"></param>
/// <returns></returns>
public async Task<ApiResult> delete(long Id)
{
var entity = await _db.Queryable<DeviceEntity>().FirstAsync(s => s.Id == Id);
entity.IsDeleted = true;
await _db.Updateable(entity).ExecuteCommandAsync();
deviceManager.RemoveDevice(entity.Id);
return new ApiResult()
{
code = 0,
msg = "success"
};
}
/// <summary>
/// 取单个设备详情
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public async Task<DeviceEntity> detail(long Id)
{
return await _db.Queryable<DeviceEntity>()
.FirstAsync(s => s.Id == Id);
}
/// <summary>
/// 获取首页数据
/// </summary>
/// <returns></returns>
public async Task<List<DeviceEntity>> All()
{
var result = await _db.Queryable<DeviceEntity>()
.Where(s => s.IsDeleted == false)
.ToListAsync();
return result;
}
private Task AddDevice2Manager(DeviceEntity entity)
{
var device = new Device.Device()
{
Id = entity.Id,
Port = entity.Port,
GraphQlEndpoint = entity.GraphQlEndpoint,
Topic = entity.Topic,
IpAddress = entity.ip,
Protocol = entity.Protocol,
username = entity.account,
password = entity.password,
DataReceived = async (data) =>
{
try
{
var input = JsonConvert.DeserializeObject<RevData>(data);
Console.WriteLine($"rev data:{data}");
await _alarmService.AddAlarm(input);
}
catch (Exception ex)
{
Console.WriteLine($"error:{ex.Message},data:{data}");
}
}
};
deviceManager.AddDevice(device);
return Task.CompletedTask;
}
/// <summary>
/// 初始化,加载所有设备
/// </summary>
/// <returns></returns>
public async Task Init()
{
var devices = await _db.Queryable<DeviceEntity>().Where(s => s.IsDeleted == false).ToListAsync();
foreach (var item in devices)
{
var key = RedisKeyList.DeviceInfo(item.DeviceSN);
await _redisService.SetAsync(key, item, TimeSpan.FromDays(10));
await AddDevice2Manager(item);
}
//deviceManager.AddDevice(new Device.Device()
//{
// Id = 1111,
// Port = 1883,
// GraphQlEndpoint = "http://localhost:5000/graphql",
// Topic = "F010/uav_dynamic_data/beijing",
// IpAddress = "110.42.53.32",
// Protocol = ProtocolType.MQTT,
// username = "yangzi",
// password = "64B7qvztS38l",
// DataReceived = async (data) =>
// {
// try
// {
// var input = JsonConvert.DeserializeObject<RevData>(data);
// await _alarmService.AddAlarm(input);
// }
// catch (Exception ex)
// {
// Console.WriteLine($"error:{ex.Message},data:{data}");
// }
// }
//});
}
}
}

24
Service/LogService.cs Normal file
View File

@ -0,0 +1,24 @@
using Azure.Core;
using LY.App.Extensions.DI;
using LY.App.Model;
using Mapster;
using SqlSugar;
using System.Security.AccessControl;
namespace LY.App.Service
{
[ServiceInjection(InjectionType.Transient)]
public class LogService
{
private readonly SqlSugarClient _db;
public LogService(SqlSugarClient sqlSugarClient)
{
_db = sqlSugarClient;
}
public async Task AddLog(AddLog input)
{
var entity = input.Adapt<LogEntity>();
await _db.Insertable(entity).ExecuteReturnSnowflakeIdAsync();
}
}
}

156
Service/PositionService.cs Normal file
View File

@ -0,0 +1,156 @@
using LY.App.Common;
using LY.App.Extensions.DI;
using LY.App.Model;
using Mapster;
using Microsoft.AspNetCore.Http;
using NetTopologySuite.Geometries;
using NetTopologySuite.GeometriesGraph;
using SqlSugar;
using System.Xml.Schema;
namespace LY.App.Service
{
/// <summary>
/// 阵地服务
/// </summary>
[ServiceInjection(InjectionType.Transient)]
public class PositionService
{
private readonly SqlSugarClient _db;
public PositionService(SqlSugarClient db)
{
_db = db;
}
/// <summary>
/// 添加
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<ApiResult> MainAdd(AddPosition input)
{
var entity = input.Adapt<PositionInfo>();
entity.CreateTime = DateTime.Now;
//名称重复判断
var isNameExist = await _db.Queryable<PositionInfo>().CountAsync(a => a.Name == input.Name && a.IsDeleted != true);
if (isNameExist > 0)
{
return new ApiResult(false, "名称已存在");
}
if (!string.IsNullOrWhiteSpace(entity.RegionJson))
{
if (GeoJsonHelper.TryGetGeomWKTFromGeoJson(entity.RegionJson, out MultiPolygon geo))
{
entity.SetRegion(geo);
}
else
{
return new ApiResult(false, "空间数据无效");
}
}
// 位置
entity.SetLocation();
await _db.Insertable(entity).ExecuteReturnSnowflakeIdAsync();
return new ApiResult(true, "添加成功");
}
public async Task<ApiResult> Update(UpdatePosition input)
{
var entity = input.Adapt<PositionInfo>();
await _db.Queryable<PositionInfo>().FirstAsync(a => a.Id == input.Id && a.IsDeleted != true);
if (entity != null)
{
if (!string.IsNullOrWhiteSpace(entity.RegionJson))
{
if (GeoJsonHelper.TryGetGeomWKTFromGeoJson(entity.RegionJson, out MultiPolygon geo))
{
entity.SetRegion(geo);
}
else
{
return new ApiResult(false, "空间数据无效");
}
}
// 位置
entity.SetLocation();
await _db.Updateable(entity).ExecuteCommandAsync();
return new ApiResult(true, "添加成功");
}
return new ApiResult(false, "未找到要更新的对象");
}
/// <summary>
///
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public async Task<ApiResult> Delete(long id)
{
var entity = await _db.Queryable<PositionInfo>().FirstAsync(a => a.Id == id && a.IsDeleted != true);
if (entity != null)
{
entity.IsDeleted = true;
await _db.Updateable(entity).ExecuteCommandAsync();
return new ApiResult(true, "删除成功");
}
return new ApiResult(false, "未找到要删除的对象");
}
/// <summary>
/// 获取
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public async Task<ApiResult> Get(int id)
{
var entity = await _db.Queryable<PositionInfo>().FirstAsync(a => a.Id == id && a.IsDeleted != true);
if (entity != null)
{
return new ApiResult() { data = entity };
}
return new ApiResult(false, "未找到要获取的对象");
}
/// <summary>
/// 获取列表
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<ApiResult> GetList(PositionQueryInput input)
{
var query = _db.Queryable<PositionInfo>()
.Where(a => a.IsDeleted != true)
.WhereIF(!string.IsNullOrWhiteSpace(input.Name), a => a.Name.Contains(input.Name))
.OrderBy(a => a.Id, OrderByType.Desc);
var result = await query.ToPageListAsync(input.pageNum, input.pageSize);
return new ApiResult()
{
data = new
{
items = result,
totalCount = query.Count()
}
};
}
/// <summary>
/// 首页数据
/// </summary>
/// <returns></returns>
public async Task<List<PositionDeviceDto>> Index()
{
var query = await _db.Queryable<PositionInfo>()
.Where(a => a.IsDeleted != true).ToListAsync();
var positionIds = query.Select(s => s.Id).ToList();
var deviceList = await _db.Queryable<DeviceEntity>()
.Where(s => positionIds.Contains(s.PositionId))
.Where(s => s.IsDeleted == false)
.ToListAsync();
List<PositionDeviceDto> result = new List<PositionDeviceDto>();
foreach (var item in query)
{
result.Add(new PositionDeviceDto()
{
position = item,
Devices = deviceList.Where(s => s.PositionId == item.Id).ToList().Adapt<List<DeviceItem>>()
});
}
return result;
}
}
}

237
Service/UserService.cs Normal file
View File

@ -0,0 +1,237 @@
using GraphQL;
using LY.App.Common.Cypher;
using LY.App.Common.Redis;
using LY.App.Model;
using Mapster;
using Microsoft.IdentityModel.Tokens;
using SqlSugar;
using StackExchange.Redis;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace LY.App.Service
{
public class UserService
{
private readonly SqlSugarClient _db;
private readonly IConfiguration _config;
private readonly RedisService _redisService;
public UserService(SqlSugarClient db, IConfiguration config, RedisService redisService)
{
_db = db;
_config = config;
_redisService = redisService;
}
#region
public async Task<ApiResult> Add(AddUser input)
{
var exists = await _db.Queryable<UserEntity>().AnyAsync(s => s.Name == input.Name && s.Disable == false);
if (!exists)
{
var entity = input.Adapt<UserEntity>();
// var entity = _mapper.Map<UserEntity>(input);
entity.CreateTime = DateTime.Now;
entity.UpdatePwdTime = DateTime.Now;
entity.Password = MD5CypherUtil.Hash("ly_" + input.Password);
var Id = await _db.Insertable(entity).ExecuteReturnSnowflakeIdAsync();
return new ApiResult() { code = 0, data = Id.ToString() };
}
else
{
return new ApiResult() { code = 1, msg = "已存在该用户名" };
}
}
/// <summary>
/// 删除用户,标记删除,不是物理删除
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
public async Task<string> SoftDelete(IEnumerable<long> ids)
{
if (ids.Any())
{
foreach (var item in ids)
{
var entity = await _db.Queryable<UserEntity>().FirstAsync(s => s.Id == item);
if (entity?.Name != "admin")
{
entity.Disable = entity.Disable == true ? false : true;
await _db.Updateable<UserEntity>(entity).ExecuteCommandAsync();
}
else
return "admin 不可删除!";
}
return "";
}
return "ids不能为空";
}
public async Task<ApiResult> GetUserById(long userId)
{
var entity = await _db.Queryable<UserEntity>().FirstAsync(s => s.Id == userId);
if (entity != null)
{
entity.Password = "";
return new ApiResult() { code = 0, data = entity };
}
return null;
}
/// <summary>
/// 监测登录次数
/// </summary>
/// <param name="username"></param>
/// <returns></returns>
private async Task<bool> CheckLoginNum(string username)
{
int num = 0;
// var node = SettingHelper.Instance.GetNode("Vertify", "LoginFailed");
var node = _config["Vertify"];
var key = RedisKeyList.LogNum(username);
var val = await _redisService.GetAsync<int>(key);
if (num >= int.Parse(node))
{
return true;
}
return false;
}
public async Task<ApiResult> Login(LoginModel input)
{
if (await CheckLoginNum(input.username))
{
return new ApiResult()
{
code = 1,
msg = "连续登录失败5次锁定30分钟"
};
}
var password = MD5CypherUtil.Hash("ly_" + input.password);
var users = await _db.Queryable<UserEntity>().ToListAsync();
var entity = await _db.Queryable<UserEntity>()
.Where(s => s.Disable == false &&
s.Name == input.username && s.Password == password).FirstAsync();
if (entity == null)
{
await SetLoginNum(input.username);
return new ApiResult()
{
code = 1,
msg = "请检查用户名跟密码!"
};
}
entity.LoginTime = DateTime.Now;
await _db.Updateable(entity).ExecuteCommandAsync();
await RemoveLoginNum(input.username);
string token = CreateToken(entity);
//加入redis
await _redisService.SetAsync<string>(RedisKeyList.TokenUser(input.username),
token, TimeSpan.FromSeconds(60 * 60 * 24 * 7));
return new ApiResult()
{
code = 1,
data = new
{
token,
expires = DateTime.UtcNow.AddSeconds(60*60*24*7),
isAdmin = entity.IsAdmin,
userid = entity.Id.ToString()
}
};
}
private async Task RemoveLoginNum(string username)
{
var key = $"login_num_{username}";
await _redisService.DeleteAsync(key);
}
private async Task SetLoginNum(string username)
{
var key = RedisKeyList.LogNum(username);
var num = await _redisService.GetAsync<int>(key);
await _redisService.SetAsync(key, num + 1, TimeSpan.FromMinutes(30));
}
private string CreateToken(UserEntity user)
{
// 创建JWT的密钥
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Token:SecretKey"]));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
// 创建Claims
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub,user.Name),
new Claim(JwtRegisteredClaimNames.Sid, user.Id.ToString())
// 还可以添加其他需要的Claims
};
// 创建Token
var token = new JwtSecurityToken(
issuer: _config["Token:Issuer"],
audience: _config["Token:Audience"],
claims: claims,
expires: DateTime.UtcNow.AddSeconds(60 * 60 * 24 * 7), // 设置Token过期时间
signingCredentials: credentials
);
// 生成Token字符串
var tokenString = new JwtSecurityTokenHandler().WriteToken(token);
return tokenString;
}
/// <summary>
///
/// </summary>
/// <param name="pageSize"></param>
/// <param name="pageNum"></param>
/// <param name="key"></param>
/// <returns></returns>
public async Task<object> GetPage(int pageSize, int pageNum, string key)
{
RefAsync<int> total = 0;
var query = await _db.Queryable<UserEntity>()
.Where(s => s.Disable == false).ToPageListAsync(pageNum, pageSize, total);
return new
{
total = total.Value,
items = query
};
}
/// <summary>
/// 更新密码
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<string> UpdatePwd(UpdatePwdDto input)
{
var md5 = MD5CypherUtil.Hash("ly_" + input.oldPwd);
var entity = await _db.Queryable<UserEntity>().FirstAsync(s => s.Id == input.Id);
if (entity.Password == md5)
{
entity.Password = MD5CypherUtil.Hash("ly_" + input.newPwd);
entity.UpdatePwdTime = DateTime.Now;
await _db.Updateable(entity).UpdateColumns(s => new
{
s.Password,
s.UpdatePwdTime
}).ExecuteCommandAsync();
return "";
}
return "原密码不正确";
}
public async Task<string> Update(UpdateUser input)
{
if (!string.IsNullOrEmpty(input.Name))
{
var entity = input.Adapt<UserEntity>();
await _db.Updateable(entity).UpdateColumns(it => new
{
it.UpdatePwdTime,
it.Password,
it.Email,
it.IsAdmin
}).ExecuteCommandAsync();
return null;
}
return "用户名不能为空";
}
#endregion
}
}

13
WeatherForecast.cs Normal file
View File

@ -0,0 +1,13 @@
namespace LY.App
{
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string? Summary { get; set; }
}
}

View File

@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

32
appsettings.json Normal file
View File

@ -0,0 +1,32 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"log2db": true, //
"AllowedHosts": "*",
"ConnectionStrings": {
"DefaultConnection": "server=101.43.201.20;port=3307;database=lyapp;user=root;password=Aa123;Pooling=true;"
},
"Token": {
"SecretKey": "HWLSNPM+OhlFe4wwEV/teSWsxGjrWbxKnHonxW5Z+mFlQq3zonv5",
"Issuer": "ly_server_issuer",
"Audience": "ly_server_audience",
"AccessExpires": 86400,
"RefreshExpires": 43200,
"Admin": "GYJlKgVaHPThTGvw8iB9n/SQiApYPiVaisHHzs9z47eBI3TxseU7WYihNRLGTMcekVfgtcaD05r+hdOTpA0obMlMpb0I6lVFtaw2iUGXqpwleqtBOy5ajlE/cG3THTDUhkYJTwpAYt3mwPnvFc2gVskVUpzA3g9n33jHKhQQyIM="
},
"Swagger": {
"Title": "LY.App-Api",
"Description": "接口文档",
"Version": "v1.0"
},
"Redis": {
"ConnectionString": "101.43.201.20:6379,password=Aa123,abortConnect =false"
},
"Vertify": 5, //
"BatchId": 60, //
"SnowFlakeWordId": 1 //wordIdwordId32
}