From 034a190e7f5e632b7b8c75bc3fba7afb54c49cff Mon Sep 17 00:00:00 2001 From: chencaixia Date: Wed, 13 Aug 2025 15:29:00 +0800 Subject: [PATCH] =?UTF-8?q?=E5=81=9C=E8=BD=A6=E5=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WeiCloud.Fusion/ParkLotInfoService/Class1.cs | 7 + .../IParkingLotDataService.cs | 16 + .../ParkLotInfoService.csproj | 21 ++ .../ParkingLotDataService.cs | 195 +++++++++++ WeiCloud.Fusion/ParkingLotEntity/Class1.cs | 7 + .../ParkingLotEntity/EntityCreate.txt | 1 + .../ParkingLotEntity/ParkingLotEntity.csproj | 24 ++ .../BaseModels/ApiResult.cs | 56 ++++ .../ParkingAccessRecordDto.cs | 199 ++++++++++++ .../ParkingLotModels/ParkingAccessRecord.cs | 64 ++++ .../ParkingLotModels/WeiCloudFusionContext.cs | 91 ++++++ .../Controllers/ParkLotInfoController.cs | 44 +++ .../ParkingLotService.API.csproj | 14 +- .../ParkingLotService.API/Program.cs | 56 +++- .../ParkingLotService.API/Startup.cs | 25 ++ .../ParkingLotService.API/appsettings.json | 8 +- WeiCloud.Fusion/WeiCloud.Fusion.sln | 45 ++- .../WeiCloud.Utils/EncodeTools/AESHelper.cs | 82 +++++ .../WeiCloud.Utils/EncodeTools/AESUtils.cs | 306 ++++++++++++++++++ .../EncodeTools/AesEncryptor.cs | 243 ++++++++++++++ 20 files changed, 1488 insertions(+), 16 deletions(-) create mode 100644 WeiCloud.Fusion/ParkLotInfoService/Class1.cs create mode 100644 WeiCloud.Fusion/ParkLotInfoService/IParkingLotDataService.cs create mode 100644 WeiCloud.Fusion/ParkLotInfoService/ParkLotInfoService.csproj create mode 100644 WeiCloud.Fusion/ParkLotInfoService/ParkingLotDataService.cs create mode 100644 WeiCloud.Fusion/ParkingLotEntity/Class1.cs create mode 100644 WeiCloud.Fusion/ParkingLotEntity/EntityCreate.txt create mode 100644 WeiCloud.Fusion/ParkingLotEntity/ParkingLotEntity.csproj create mode 100644 WeiCloud.Fusion/ParkingLotEntity/ParkingLotModelDto/BaseModels/ApiResult.cs create mode 100644 WeiCloud.Fusion/ParkingLotEntity/ParkingLotModelDto/ParkingAccessRecordDto.cs create mode 100644 WeiCloud.Fusion/ParkingLotEntity/ParkingLotModels/ParkingAccessRecord.cs create mode 100644 WeiCloud.Fusion/ParkingLotEntity/ParkingLotModels/WeiCloudFusionContext.cs create mode 100644 WeiCloud.Fusion/ParkingLotService/ParkingLotService.API/Controllers/ParkLotInfoController.cs create mode 100644 WeiCloud.Fusion/ParkingLotService/ParkingLotService.API/Startup.cs create mode 100644 WeiCloud.Fusion/WeiCloud.Utils/EncodeTools/AESUtils.cs create mode 100644 WeiCloud.Fusion/WeiCloud.Utils/EncodeTools/AesEncryptor.cs diff --git a/WeiCloud.Fusion/ParkLotInfoService/Class1.cs b/WeiCloud.Fusion/ParkLotInfoService/Class1.cs new file mode 100644 index 0000000..62785ac --- /dev/null +++ b/WeiCloud.Fusion/ParkLotInfoService/Class1.cs @@ -0,0 +1,7 @@ +namespace ParkLotInfoService +{ + public class Class1 + { + + } +} diff --git a/WeiCloud.Fusion/ParkLotInfoService/IParkingLotDataService.cs b/WeiCloud.Fusion/ParkLotInfoService/IParkingLotDataService.cs new file mode 100644 index 0000000..e255b69 --- /dev/null +++ b/WeiCloud.Fusion/ParkLotInfoService/IParkingLotDataService.cs @@ -0,0 +1,16 @@ +using ParkingLotEntity.ParkingLotModelDto.BaseModels; +using ParkingLotEntity.ParkingLotModelDto; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ParkLotInfoService +{ + public interface IParkingLotDataService + { + Task> PostParkingLots(ParkingAccessRecordDto dto); + Task> GetParkingLotInfo(string parkId); + } +} diff --git a/WeiCloud.Fusion/ParkLotInfoService/ParkLotInfoService.csproj b/WeiCloud.Fusion/ParkLotInfoService/ParkLotInfoService.csproj new file mode 100644 index 0000000..1a17624 --- /dev/null +++ b/WeiCloud.Fusion/ParkLotInfoService/ParkLotInfoService.csproj @@ -0,0 +1,21 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + + diff --git a/WeiCloud.Fusion/ParkLotInfoService/ParkingLotDataService.cs b/WeiCloud.Fusion/ParkLotInfoService/ParkingLotDataService.cs new file mode 100644 index 0000000..36687ca --- /dev/null +++ b/WeiCloud.Fusion/ParkLotInfoService/ParkingLotDataService.cs @@ -0,0 +1,195 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.Extensions.Logging; +using MongoDB.Bson; +using Newtonsoft.Json.Serialization; +using Newtonsoft.Json; +using ParkingLotEntity.ParkingLotModelDto; +using ParkingLotEntity.ParkingLotModelDto.BaseModels; +using ParkingLotEntity.ParkingLotModels; +using System.Text.Json; +using WeiCloud.Core; +using WeiCloud.Utils.EncodeTools; +using WeiCloudAPP.Core; +using NewLife.Serialization; +using WeiCloud.Utils.Common; +using Microsoft.Extensions.Configuration; +using System.Net.Http; +using System.Text.Encodings.Web; +using System.Text; +using System.Net.NetworkInformation; + +namespace ParkLotInfoService +{ + public class ParkingLotDataService:IParkingLotDataService + { + private readonly ILogger _logger; + private readonly WeiCloudFusionContext _context; + private readonly IConfiguration _configuration; + private readonly string liFangkey = "cmVmb3JtZXJyZWZvcm1lcg=="; + private JsonSerializerSettings settings = new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver() + }; + private readonly IHttpClientFactory _httpClientFactory; + public ParkingLotDataService(ILogger logger, WeiCloudFusionContext context, IConfiguration configuration, IHttpClientFactory httpClientFactory) + { + _logger = logger; + _context = context; + _configuration = configuration; + _httpClientFactory = httpClientFactory; + } + /// + /// 上传停车场信息 + /// + /// + /// + public async Task> PostParkingLots(ParkingAccessRecordDto dto) + { + ApiResult result = new ApiResult() { Code= RequestBackStatuEnum.success.Value,Msg="接口请求成功" }; + //ParkingAccessRecordDto dto=new ParkingAccessRecordDto(); + try + { + + //ParkingAccessRecord record = new ParkingAccessRecord() { Id=1,CarCode="001"}; + //_context.ParkingAccessRecords.Add(record); + //var res = await _context.SaveChangesAsync(); + var str = "8B2B9C1AA8B9E01713980D1275CA2DF7F3A7B1DA11145C2471B6CEBEF8A90A76F01B4C82FA3FBE291EC64650AB1206EBE6B32BD9F76DC5A231ED6C649058518B75F8D3D93C1D2864F4FA74051D3FCFBC038BDF8AE12D88E7CB1F972C194E7DD36FF179E42DD565DED895ECF58A3FB2EDA52E8188B835B405BF86BEC18500985D2D152CCFFEDD5F408F3C467352205780EBDDA6BA6247CA712A8496B82040181A9FD07D1BD7A7FFED450F68E9192A61067BA445C0C53309DFF4A040EF2FCB82CBA399AA351E276223F01130CC588295A6B46C831ECB182D77234FDCA2BF7E105D242BC2C372895CF776EE49469AAA3FBC2C37AA2B6FA37FFA2DEA3A1B7D0A04DE7624B116E4244083E0CC90C641223A08A2EBDAA29C22087BE037C3FB4231D0A0669BCEF0D31011E63F11188EB0918330"; + + var data= AESUtils.DecryptString(str, liFangkey); + var model= JsonConvert.DeserializeObject(data, settings); + if (model == null) + { + result.Msg = "上传数据为空"; + return result; + } + ParkingAccessRecord record=new ParkingAccessRecord() { Id= UidGenerator.Uid(), CarCode=model.CarCode, ChannelId=model.ChannelId,ChannelName=model.ChannelName,Guid=model.GUID,ImagePath=model.ImagePath,InOrOut=model.InOrOut,InTime=model.InTime,ParkId=model.ParkId,PassTime=model.PassTime,ProjectId=model.ProjectId,CreateTime=DateTime.Now }; + _context.ParkingAccessRecords.Add(record); + var res = await _context.SaveChangesAsync(); + _logger.LogInformation("Doing some work..."); + } + catch (Exception ex) + { + _logger.LogError(ex, $"PostParkingLots 上传停车场信息失败:{ex.Message}"); + } + return result; + + } + /// + /// 获取停车场信息 + /// + /// + /// + public async Task> GetParkingLotInfo(string parkId) + { + ApiResult result = new ApiResult() { Code = RequestBackStatuEnum.success.Value, Msg = "接口请求成功" }; + var str = "A28ADF261302B0779371FDF714C286904F19848D4D44A63C5D6C247D98D45A5B23747C490FAD5FF6D8B0ECC046B01FFF431A292A058C82BB0ABFB0FA0A2FCB30DE227AC64EB691E4C10640D3264883986116758B5C984B77FED4F0E5066F2DA232741B99B327A147CA0E3B56821E6FDD4F8626A11855E0610B85D43CF216CDD5F4C0DFEEB508379FBBE9BAAC35559606C30BA045B3816AC7C43FBCF18DADB4ED876562EC3BA87D9ED03A0B0F56EAC6CCFB529724E7C2BF25DA6102A12ED962A4472EDE67D60B059372DB25BF9A377A5C6E7E26312E11D58B796260218F862F74FC276A5B82E259A08BC2B04EAB6345EAE346A73AB9CBFF32E81F2D600C691CA0D9695561AC1693C8F12B8FB10932896D5E1939E46EFBD8AD283DEA7C02C810F12683390F99952D5AD2A4729EFFECDF835C92848F79EAB106A3131A201E7FBF8DF6B4C872F366FBFFD5F9D00DCF9061D9A48A3B56B362ABCA9376A4B4F46353679E91CAFD58635BE966AFD23B985FB31AF01B0B51BFFA13E6113DB0A95931AF0FCC2256E6F5F660A248A74CAA0A7AD26FE0CF76F0FF5CFAA0A62B9FC2181A07202A149F87844A41CB32D28119580741FBAF981AE0B4658038512ACEA68B7ABA148606721AE6213F61D0187DD7AA5727B2D71D112DC91E74B2875E26BD6A90EFE4587B907EC95270867C31201AFDADE3CDEE9823277FCFFE65B51F18D839B9E62A8E5DE3C173A6AC19C710ED9043F19EE59EF0B45BAF21B9A92246082ACE5533A7604C6BAFEA57E89BE457B98FACD75012E400C18D8EC186025728FEE917E3AD7967E0AF361DBF3C36C3A1B852259BBD427AB317B7E4EC53B408D91523A35E2133DFB7A68141BA27A221BA2D4FC81D7629"; + try + { + //测试ip + var res=await GetIpPing("223.70.243.165"); + ParkingLotInfoParmDto parm= new ParkingLotInfoParmDto() { StationNo = parkId }; + //调用立方接口 + var restr=await GetLifangData("GetParkingLotInfo", parm); + if (string.IsNullOrEmpty(restr)) + { + //result.Code = RequestBackStatuEnum.diy.Value; + result.Msg = "没有查到停车场信息"; + return result; + } + var data = AESUtils.DecryptString(str, liFangkey); + var model = JsonConvert.DeserializeObject(data, settings); + if (model==null||model.Code != 0) + { + result.Code = RequestBackStatuEnum.diy.Value; + result.Msg = model.ResMsg; + return result; + } + result.Data = model; + + } + catch (Exception ex) + { + _logger.LogError(ex, $"GetParkingLotInfo 取停车场信息失败:{ex.Message}"); + } + return result; + } + /// + /// 获取立方接口数据 + /// + /// + /// + /// + private async Task GetLifangData(string apiName, object parmJson) + { + + string resStr = string.Empty; + //立方,目前只能本地测试 + var url = _configuration["ThirdApi:LifangParkLotApiAddress"]+ apiName; + var client = _httpClientFactory.CreateClient(); + client.Timeout = TimeSpan.FromMinutes(1);//设置超时时间 + + var options = new JsonSerializerOptions + { + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping // 可选,防止特殊字符转义 + }; + + string json = System.Text.Json.JsonSerializer.Serialize(parmJson, options); + var content = new StringContent(json, Encoding.UTF8, "application/json"); + _ = Task.Run(async () => + { + try + { + var httpResponse = client.PostAsync(url, content); + var response = await httpResponse; + if (response.IsSuccessStatusCode) + { + resStr = await response.Content.ReadAsStringAsync(); + } + // 处理响应... + } + catch (TaskCanceledException) + { + _logger.LogError(this.GetType().FullName + " PostSearchResult", string.Format("抛出了异常信息: 请求超时,但主进程不受影响;")); + //Console.WriteLine("请求超时,但主进程不受影响"); + } + catch (Exception ex) + { + _logger.LogError(this.GetType().FullName + " PostSearchResult", string.Format("抛出了异常信息: 请求超时,请求失败;{0}", ex.Message)); + } + }); + return resStr; + } + private async Task GetIpPing(string ip) + { + string host = ip; + int timeout = 1000; // 超时时间(毫秒) + string result = string.Empty; + try + { + using (var ping = new Ping()) + { + PingReply reply = await ping.SendPingAsync(host, timeout); + + if (reply.Status == IPStatus.Success) + { + result = "1"; + Console.WriteLine($"来自 {reply.Address} 的回复: 字节={reply.Buffer.Length} 时间={reply.RoundtripTime}ms TTL={reply.Options?.Ttl ?? 0}"); + } + else + { + Console.WriteLine($"Ping 失败: {reply.Status}"); + } + } + } + catch (PingException ex) + { + Console.WriteLine($"Ping 异常: {ex.InnerException?.Message ?? ex.Message}"); + } + catch (Exception ex) + { + Console.WriteLine($"错误: {ex.Message}"); + } + return result; + } + } +} diff --git a/WeiCloud.Fusion/ParkingLotEntity/Class1.cs b/WeiCloud.Fusion/ParkingLotEntity/Class1.cs new file mode 100644 index 0000000..381e1d3 --- /dev/null +++ b/WeiCloud.Fusion/ParkingLotEntity/Class1.cs @@ -0,0 +1,7 @@ +namespace ParkingLotEntity +{ + public class Class1 + { + + } +} diff --git a/WeiCloud.Fusion/ParkingLotEntity/EntityCreate.txt b/WeiCloud.Fusion/ParkingLotEntity/EntityCreate.txt new file mode 100644 index 0000000..0588d38 --- /dev/null +++ b/WeiCloud.Fusion/ParkingLotEntity/EntityCreate.txt @@ -0,0 +1 @@ +Scaffold-DbContext "Server=v4.weienergy.cn;Database=WeiCloud.Fusion;User=root;Password=Zrhdb#2019;Port=3307;" Pomelo.EntityFrameworkCore.MySql -OutputDir ParkingLotModels -Force \ No newline at end of file diff --git a/WeiCloud.Fusion/ParkingLotEntity/ParkingLotEntity.csproj b/WeiCloud.Fusion/ParkingLotEntity/ParkingLotEntity.csproj new file mode 100644 index 0000000..f5edd34 --- /dev/null +++ b/WeiCloud.Fusion/ParkingLotEntity/ParkingLotEntity.csproj @@ -0,0 +1,24 @@ + + + + net8.0 + enable + enable + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + diff --git a/WeiCloud.Fusion/ParkingLotEntity/ParkingLotModelDto/BaseModels/ApiResult.cs b/WeiCloud.Fusion/ParkingLotEntity/ParkingLotModelDto/BaseModels/ApiResult.cs new file mode 100644 index 0000000..0c93ac5 --- /dev/null +++ b/WeiCloud.Fusion/ParkingLotEntity/ParkingLotModelDto/BaseModels/ApiResult.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Text; +namespace ParkingLotEntity.ParkingLotModelDto.BaseModels +{ + public class ApiResult + { + public int Code { get; set; } + public string Msg { get; set; } + public string InnerMsg { get; set; } + public T Data { get; set; } + + + public static ApiResult IsSuccess(T data, string msg = "请求成功") + { + return new ApiResult() { Code = 200, Data = data, Msg = msg }; + } + + /// + /// 服务端处理错误 + /// + /// + /// + public static ApiResult IsFail(string msg) + { + return new ApiResult { Code = 2005, Msg = msg }; + } + + + /// + /// 服务端处理错误 + /// + /// + /// + public static ApiResult IsNoFound(string msg = null) + { + return new ApiResult { Code = 2005, Msg = msg ?? "数据不存在" }; + } + + /// + /// 客户端错误 + /// + /// + /// + public static ApiResult IsBadReq(string msg) + { + return new ApiResult { Code = 2006, Msg = msg }; + } + } + + public class ApiHubResult : ApiResult + { + public long? Projectid { get; set; } + public long? Constid { get; set; } + } +} diff --git a/WeiCloud.Fusion/ParkingLotEntity/ParkingLotModelDto/ParkingAccessRecordDto.cs b/WeiCloud.Fusion/ParkingLotEntity/ParkingLotModelDto/ParkingAccessRecordDto.cs new file mode 100644 index 0000000..df6ef79 --- /dev/null +++ b/WeiCloud.Fusion/ParkingLotEntity/ParkingLotModelDto/ParkingAccessRecordDto.cs @@ -0,0 +1,199 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ParkingLotEntity.ParkingLotModelDto +{ + public class ParkingAccessRecordDto + { + /// + /// 项目id + /// + public long ProjectId { get; set; } + + /// + /// 车牌号码 + /// + public string? CarCode { get; set; } + + /// + /// 进场时间(出场时若找不到场内车辆信息,则为空) + /// + public DateTime? InTime { get; set; } + + /// + /// 过场时间(进场时等于进场时间,出场时为出场时间) + /// + public DateTime? PassTime { get; set; } + + /// + /// 车场 ID + /// + public string? ParkId { get; set; } + + /// + /// 0:进场,1:出场 + /// + public string? InOrOut { get; set; } + + /// + /// 车辆本次进场出场标识 + /// + public string? GUID { get; set; } + + /// + /// 通道 ID + /// + public string? ChannelId { get; set; } + + /// + /// 通道名称 + /// + public string? ChannelName { get; set; } + + /// + /// 图片路径地址 + /// + public string? ImagePath { get; set; } + + } + public class ParkingAccessRecord2Dto + { + /// + /// id + /// + public long Id { get; set; } + + /// + /// 项目id + /// + public long ProjectId { get; set; } + + /// + /// 车牌号码 + /// + public string? CarCode { get; set; } + + /// + /// 进场时间(出场时若找不到场内车辆信息,则为空) + /// + public DateTime? InTime { get; set; } + + /// + /// 过场时间(进场时等于进场时间,出场时为出场时间) + /// + public DateTime? PassTime { get; set; } + + /// + /// 车场 ID + /// + public string? ParkId { get; set; } + + /// + /// 0:进场,1:出场 + /// + public string? InOrOut { get; set; } + + /// + /// 车辆本次进场出场标识 + /// + public string? GUID { get; set; } + + /// + /// 通道 ID + /// + public string? ChannelId { get; set; } + + /// + /// 通道名称 + /// + public string? ChannelName { get; set; } + + /// + /// 图片路径地址 + /// + public string? ImagePath { get; set; } + + } + /// + /// 车场信息 + /// + public class ParkingLotInfoDto + { + /// + /// 0成功,-1失败 + /// + public int Code { get; set; } + /// + /// 返回说明 + /// + public string? ResMsg { get; set; } + /// + /// 总车位数 + /// + public int TotalNum { get; set; } + /// + /// 总已停车位数 + /// + public int TotalStopNum { get; set; } + + /// + /// 总剩余车位 + /// + public int TotalRemainNum { get; set; } + /// + /// 车场ID + /// + public string? ParkID { get; set; } + /// + /// 车场名称 + /// + public string? ParkName { get; set; } + /// + /// 收费标准 + /// + public string? ChargeRuleDesc { get; set; } + /// + /// 区域余位信息 + /// + public List? ParkingLotInfo { get; set; } + } + /// + /// 区域信息 + /// + public class ParkingLotInfo + { + /// + /// 区域ID + /// + public int ParkingLotId { get; set; } + /// + /// 区域名称 + /// + public string? ParkingLotName { get; set; } + /// + /// 区域总车位数 + /// + public int TotalNum { get; set; } + /// + /// 区域已停车位数 + /// + public int TotalStopNum { get; set; } + /// + /// 区域剩余车位 + /// + public int TotalRemainNum { get; set; } + } + /// + /// 车场信息参数 + /// + public class ParkingLotInfoParmDto + { + /// + /// 终端号 + /// + public string? StationNo { get; set; } + } +} diff --git a/WeiCloud.Fusion/ParkingLotEntity/ParkingLotModels/ParkingAccessRecord.cs b/WeiCloud.Fusion/ParkingLotEntity/ParkingLotModels/ParkingAccessRecord.cs new file mode 100644 index 0000000..1f09b0b --- /dev/null +++ b/WeiCloud.Fusion/ParkingLotEntity/ParkingLotModels/ParkingAccessRecord.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; + +namespace ParkingLotEntity.ParkingLotModels; + +public partial class ParkingAccessRecord +{ + public long Id { get; set; } + + /// + /// 项目id + /// + public long ProjectId { get; set; } + + /// + /// 车牌号码 + /// + public string? CarCode { get; set; } + + /// + /// 进场时间(出场时若找不到场内车辆信息,则为空) + /// + public DateTime? InTime { get; set; } + + /// + /// 过场时间(进场时等于进场时间,出场时为出场时间) + /// + public DateTime? PassTime { get; set; } + + /// + /// 车场 ID + /// + public string? ParkId { get; set; } + + /// + /// 0:进场,1:出场 + /// + public string? InOrOut { get; set; } + + /// + /// 车辆本次进场出场标识 + /// + public string? Guid { get; set; } + + /// + /// 通道 ID + /// + public string? ChannelId { get; set; } + + /// + /// 通道名称 + /// + public string? ChannelName { get; set; } + + /// + /// 图片路径地址 + /// + public string? ImagePath { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } +} diff --git a/WeiCloud.Fusion/ParkingLotEntity/ParkingLotModels/WeiCloudFusionContext.cs b/WeiCloud.Fusion/ParkingLotEntity/ParkingLotModels/WeiCloudFusionContext.cs new file mode 100644 index 0000000..9bd341d --- /dev/null +++ b/WeiCloud.Fusion/ParkingLotEntity/ParkingLotModels/WeiCloudFusionContext.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using Pomelo.EntityFrameworkCore.MySql.Scaffolding.Internal; + +namespace ParkingLotEntity.ParkingLotModels; + +public partial class WeiCloudFusionContext : DbContext +{ + public WeiCloudFusionContext() + { + } + + public WeiCloudFusionContext(DbContextOptions options) + : base(options) + { + } + + public virtual DbSet ParkingAccessRecords { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) +#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see https://go.microsoft.com/fwlink/?LinkId=723263. + => optionsBuilder.UseMySql("server=v4.weienergy.cn;database=WeiCloud.Fusion;user=root;password=Zrhdb#2019;port=3307", Microsoft.EntityFrameworkCore.ServerVersion.Parse("8.0.18-mysql")); + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder + .UseCollation("utf8mb4_0900_ai_ci") + .HasCharSet("utf8mb4"); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PRIMARY"); + + entity.ToTable("parking_access_records"); + + entity.Property(e => e.Id) + .ValueGeneratedNever() + .HasColumnType("bigint(20)") + .HasColumnName("id"); + entity.Property(e => e.CarCode) + .HasMaxLength(50) + .HasComment("车牌号码") + .HasColumnName("carCode"); + entity.Property(e => e.ChannelId) + .HasMaxLength(50) + .HasComment("通道 ID") + .HasColumnName("channelID"); + entity.Property(e => e.ChannelName) + .HasMaxLength(50) + .HasComment("通道名称") + .HasColumnName("channelName"); + entity.Property(e => e.CreateTime) + .HasDefaultValueSql("CURRENT_TIMESTAMP") + .HasComment("创建时间") + .HasColumnType("datetime"); + entity.Property(e => e.Guid) + .HasMaxLength(50) + .HasComment("车辆本次进场出场标识") + .HasColumnName("GUID"); + entity.Property(e => e.ImagePath) + .HasMaxLength(100) + .HasComment("图片路径地址") + .HasColumnName("imagePath"); + entity.Property(e => e.InOrOut) + .HasMaxLength(10) + .HasComment("0:进场,1:出场") + .HasColumnName("inOrOut"); + entity.Property(e => e.InTime) + .HasComment("进场时间(出场时若找不到场内车辆信息,则为空)") + .HasColumnType("datetime") + .HasColumnName("inTime"); + entity.Property(e => e.ParkId) + .HasMaxLength(50) + .HasComment("车场 ID") + .HasColumnName("parkID"); + entity.Property(e => e.PassTime) + .HasComment("过场时间(进场时等于进场时间,出场时为出场时间)") + .HasColumnType("datetime") + .HasColumnName("passTime"); + entity.Property(e => e.ProjectId) + .HasComment("项目id") + .HasColumnType("bigint(20)") + .HasColumnName("project_id"); + }); + + OnModelCreatingPartial(modelBuilder); + } + + partial void OnModelCreatingPartial(ModelBuilder modelBuilder); +} diff --git a/WeiCloud.Fusion/ParkingLotService/ParkingLotService.API/Controllers/ParkLotInfoController.cs b/WeiCloud.Fusion/ParkingLotService/ParkingLotService.API/Controllers/ParkLotInfoController.cs new file mode 100644 index 0000000..731b999 --- /dev/null +++ b/WeiCloud.Fusion/ParkingLotService/ParkingLotService.API/Controllers/ParkLotInfoController.cs @@ -0,0 +1,44 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore.Infrastructure.Internal; +using ParkingLotEntity.ParkingLotModelDto.BaseModels; +using ParkingLotEntity.ParkingLotModelDto; +using ParkLotInfoService; + +namespace ParkingLotService.API.Controllers +{ + [Route("api/[controller]/[Action]")] + [ApiController] + public class ParkLotInfoController : ControllerBase + { + private IParkingLotDataService _parkingLotDataService; + private readonly ILogger _logger; + //private readonly CurrentContext CurrentContext; + public ParkLotInfoController(IParkingLotDataService parkingLotDataService, ILogger logger) + { + _logger = logger; + _parkingLotDataService = parkingLotDataService; + } + /// + /// 提交停车信息 + /// + /// + /// + [HttpPost] + public async Task> PostParkingLots(ParkingAccessRecordDto dto) + { + return await _parkingLotDataService.PostParkingLots(dto); + } + /// + /// 获取停车场信息 + /// + /// + /// + [HttpGet] + public async Task> GetParkingLotInfo(string parkId) + { + return await _parkingLotDataService.GetParkingLotInfo(parkId); + } + + } +} diff --git a/WeiCloud.Fusion/ParkingLotService/ParkingLotService.API/ParkingLotService.API.csproj b/WeiCloud.Fusion/ParkingLotService/ParkingLotService.API/ParkingLotService.API.csproj index 0c56541..c51e801 100644 --- a/WeiCloud.Fusion/ParkingLotService/ParkingLotService.API/ParkingLotService.API.csproj +++ b/WeiCloud.Fusion/ParkingLotService/ParkingLotService.API/ParkingLotService.API.csproj @@ -7,11 +7,23 @@ + + + + + + + + + + - + + + diff --git a/WeiCloud.Fusion/ParkingLotService/ParkingLotService.API/Program.cs b/WeiCloud.Fusion/ParkingLotService/ParkingLotService.API/Program.cs index 06dd2a4..e94ae16 100644 --- a/WeiCloud.Fusion/ParkingLotService/ParkingLotService.API/Program.cs +++ b/WeiCloud.Fusion/ParkingLotService/ParkingLotService.API/Program.cs @@ -1,4 +1,9 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.OpenApi.Models; +using ParkingLotEntity.ParkingLotModels; +using ParkLotInfoService; +using Serilog; namespace ParkingLotService.API { public class Program @@ -14,15 +19,62 @@ namespace ParkingLotService.API builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); + //---- + // Swagger + builder.Services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo { Title = "ParkingLotAPI", Version = "v1" }); + }); + //// ע DbContext Entity Framework Core + builder.Services.AddDbContext(Options => + { + Options.UseMySql(builder.Configuration.GetConnectionString("WeiCloudFusionDBConn"), new MySqlServerVersion(new Version(8, 0, 19))); + Options.EnableDetailedErrors(); + }, ServiceLifetime.Scoped); + builder.Services.AddScoped(); + + + //--- + #region ־ + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Information() // ־ + .WriteTo.File( + path: "logs/log-.txt", // ־ļ· + rollingInterval: RollingInterval.Day, // ָ־ + fileSizeLimitBytes: 10 * 1024 * 1024, // ־ļ 10MB + retainedFileCountLimit: 5, // ౣ 5 ־ļ + rollOnFileSizeLimit: true // ļСʱļ + ) + .CreateLogger(); + builder.Services.AddHttpClient(); + //ʹ Serilog 滻Ĭ־ + builder.Host.UseSerilog(); + #endregion + + var app = builder.Build(); - // Configure the HTTP request pipeline. + // Swagger м if (app.Environment.IsDevelopment()) { app.UseSwagger(); - app.UseSwaggerUI(); + app.UseSwaggerUI(c => + { + c.SwaggerEndpoint("/swagger/v1/swagger.json", "ParkingLotAPI V1"); + }); } + + + + //var app = builder.Build(); + //// Configure the HTTP request pipeline. + //if (app.Environment.IsDevelopment()) + //{ + // app.UseSwagger(); + // app.UseSwaggerUI(); + //} + app.UseRouting(); app.UseHttpsRedirection(); app.UseAuthorization(); diff --git a/WeiCloud.Fusion/ParkingLotService/ParkingLotService.API/Startup.cs b/WeiCloud.Fusion/ParkingLotService/ParkingLotService.API/Startup.cs new file mode 100644 index 0000000..cb3bfc6 --- /dev/null +++ b/WeiCloud.Fusion/ParkingLotService/ParkingLotService.API/Startup.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore; +using ParkingLotEntity.ParkingLotModels; +using ParkLotInfoService; + +namespace ParkingLotService.API +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IConfiguration configuration, IServiceCollection services) + { + + #region 数据库连接 + var _weicloudWeiCloudFusionDBConndbconn = Configuration.GetConnectionString("WeiCloudFusionDBConn"); + #endregion + services.AddDbContext(Options => Options.UseMySql(_weicloudWeiCloudFusionDBConndbconn, new MySqlServerVersion(new Version(8, 0, 19))), ServiceLifetime.Scoped); + } + + } +} diff --git a/WeiCloud.Fusion/ParkingLotService/ParkingLotService.API/appsettings.json b/WeiCloud.Fusion/ParkingLotService/ParkingLotService.API/appsettings.json index 10f68b8..91ec38d 100644 --- a/WeiCloud.Fusion/ParkingLotService/ParkingLotService.API/appsettings.json +++ b/WeiCloud.Fusion/ParkingLotService/ParkingLotService.API/appsettings.json @@ -5,5 +5,11 @@ "Microsoft.AspNetCore": "Warning" } }, - "AllowedHosts": "*" + "WeiCloudFusionDBConn": "server=v4.weienergy.cn;uid=root;pwd=Zrhdb#2019;port=3307;database=WeiCloudDB.Fusion;default command timeout=100;CharSet=utf8;SslMode=None;allowPublicKeyRetrieval=true", + "AllowedHosts": "*", + "ThirdApi": { + "LifangParkLotApiAddress": "192.168.21.22:9988/Parking/Handheld/" + + }, + "PorjectId": "" } diff --git a/WeiCloud.Fusion/WeiCloud.Fusion.sln b/WeiCloud.Fusion/WeiCloud.Fusion.sln index 1eec34f..8fa89cf 100644 --- a/WeiCloud.Fusion/WeiCloud.Fusion.sln +++ b/WeiCloud.Fusion/WeiCloud.Fusion.sln @@ -15,29 +15,35 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WeiCloud.Core", "WeiCloud.C EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ParkingLotService", "ParkingLotService", "{0A3134C8-219C-4674-B152-1FA6561E4217}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WeiCloud.Utils", "WeiCloud.Utils\WeiCloud.Utils.csproj", "{AFC27971-48AA-23BE-1DC6-0924C6F69B9E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WeiCloud.Utils", "WeiCloud.Utils\WeiCloud.Utils.csproj", "{AFC27971-48AA-23BE-1DC6-0924C6F69B9E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ocelot.Gateway", "Gateway\Ocelot.Gateway\Ocelot.Gateway.csproj", "{B296A02A-6021-489D-9D6B-3B88BFDE5404}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Gateway", "Gateway\Ocelot.Gateway\Ocelot.Gateway.csproj", "{B296A02A-6021-489D-9D6B-3B88BFDE5404}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Manage.AppHost.AppHost", "AspireApp\Manage.AppHost.AppHost\Manage.AppHost.AppHost.csproj", "{BA1F33B5-5C46-4C7A-93E0-C1D9E31C401C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Manage.AppHost.AppHost", "AspireApp\Manage.AppHost.AppHost\Manage.AppHost.AppHost.csproj", "{BA1F33B5-5C46-4C7A-93E0-C1D9E31C401C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Manage.AppHost.ServiceDefaults", "AspireApp\Manage.AppHost.ServiceDefaults\Manage.AppHost.ServiceDefaults.csproj", "{684DBB92-1616-6165-2785-D103BB50F037}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Manage.AppHost.ServiceDefaults", "AspireApp\Manage.AppHost.ServiceDefaults\Manage.AppHost.ServiceDefaults.csproj", "{684DBB92-1616-6165-2785-D103BB50F037}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Manage.AppHost.ApiService", "AspireApp\Manage.AppHost.ApiService\Manage.AppHost.ApiService.csproj", "{8E214058-5AD4-9321-785D-132BFE9E8916}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Manage.AppHost.ApiService", "AspireApp\Manage.AppHost.ApiService\Manage.AppHost.ApiService.csproj", "{8E214058-5AD4-9321-785D-132BFE9E8916}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Manage.AppHost.Web", "AspireApp\Manage.AppHost.Web\Manage.AppHost.Web.csproj", "{8AF6C52A-0D3D-93EF-0631-0AC98A0C2789}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Manage.AppHost.Web", "AspireApp\Manage.AppHost.Web\Manage.AppHost.Web.csproj", "{8AF6C52A-0D3D-93EF-0631-0AC98A0C2789}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Video.Entities", "VideoService\Video.Entities\Video.Entities.csproj", "{B80620DD-8790-404C-B399-4FACB5DAB65A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Video.Entities", "VideoService\Video.Entities\Video.Entities.csproj", "{B80620DD-8790-404C-B399-4FACB5DAB65A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Video.API", "VideoService\Video.API\Video.API.csproj", "{70C7BF69-734A-4C57-B4B8-0A7D7DA21897}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Video.API", "VideoService\Video.API\Video.API.csproj", "{70C7BF69-734A-4C57-B4B8-0A7D7DA21897}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Video.DomainService", "VideoService\Video.DomainService\Video.DomainService.csproj", "{44E0B645-482B-4841-87E7-1E9166700185}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Video.DomainService", "VideoService\Video.DomainService\Video.DomainService.csproj", "{44E0B645-482B-4841-87E7-1E9166700185}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Video.Domain", "VideoService\Video.Domain\Video.Domain.csproj", "{6CBD9E97-4FEF-4DA2-ADFB-21B4D9DB366F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Video.Domain", "VideoService\Video.Domain\Video.Domain.csproj", "{6CBD9E97-4FEF-4DA2-ADFB-21B4D9DB366F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Video.Store", "VideoService\Video.Store\Video.Store.csproj", "{058C0CAB-5956-4811-8340-86919DDB2845}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Video.Store", "VideoService\Video.Store\Video.Store.csproj", "{058C0CAB-5956-4811-8340-86919DDB2845}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ParkingLotService.API", "ParkingLotService\ParkingLotService.API\ParkingLotService.API.csproj", "{D97C471C-3190-4F8E-A916-7A056A65EDCE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ParkingLotService.API", "ParkingLotService\ParkingLotService.API\ParkingLotService.API.csproj", "{D97C471C-3190-4F8E-A916-7A056A65EDCE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ParkingLotEntity", "ParkingLotEntity\ParkingLotEntity.csproj", "{F5AA3CCF-ED0C-40E5-AE10-CDFDC89443F0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ParkLotInfoService", "ParkLotInfoService\ParkLotInfoService.csproj", "{19E28D5D-C144-4FF5-B71D-D81DA3494AD9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WeiCloud.Core", "WeiCloud.Core\WeiCloud.Core.csproj", "{B5C2EFBB-8991-48CB-95B6-77C0770D245D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -93,6 +99,18 @@ Global {D97C471C-3190-4F8E-A916-7A056A65EDCE}.Debug|Any CPU.Build.0 = Debug|Any CPU {D97C471C-3190-4F8E-A916-7A056A65EDCE}.Release|Any CPU.ActiveCfg = Release|Any CPU {D97C471C-3190-4F8E-A916-7A056A65EDCE}.Release|Any CPU.Build.0 = Release|Any CPU + {F5AA3CCF-ED0C-40E5-AE10-CDFDC89443F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F5AA3CCF-ED0C-40E5-AE10-CDFDC89443F0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F5AA3CCF-ED0C-40E5-AE10-CDFDC89443F0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F5AA3CCF-ED0C-40E5-AE10-CDFDC89443F0}.Release|Any CPU.Build.0 = Release|Any CPU + {19E28D5D-C144-4FF5-B71D-D81DA3494AD9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {19E28D5D-C144-4FF5-B71D-D81DA3494AD9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {19E28D5D-C144-4FF5-B71D-D81DA3494AD9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {19E28D5D-C144-4FF5-B71D-D81DA3494AD9}.Release|Any CPU.Build.0 = Release|Any CPU + {B5C2EFBB-8991-48CB-95B6-77C0770D245D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B5C2EFBB-8991-48CB-95B6-77C0770D245D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B5C2EFBB-8991-48CB-95B6-77C0770D245D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B5C2EFBB-8991-48CB-95B6-77C0770D245D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -110,6 +128,9 @@ Global {6CBD9E97-4FEF-4DA2-ADFB-21B4D9DB366F} = {19A25984-FFA8-49BE-A710-6F269A406C61} {058C0CAB-5956-4811-8340-86919DDB2845} = {19A25984-FFA8-49BE-A710-6F269A406C61} {D97C471C-3190-4F8E-A916-7A056A65EDCE} = {0A3134C8-219C-4674-B152-1FA6561E4217} + {F5AA3CCF-ED0C-40E5-AE10-CDFDC89443F0} = {0A3134C8-219C-4674-B152-1FA6561E4217} + {19E28D5D-C144-4FF5-B71D-D81DA3494AD9} = {0A3134C8-219C-4674-B152-1FA6561E4217} + {B5C2EFBB-8991-48CB-95B6-77C0770D245D} = {44DAA396-C724-480A-A2BC-9A33D29E8FEA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {379A56DA-D3F0-4E7E-8FF7-DA8E20015BF3} diff --git a/WeiCloud.Fusion/WeiCloud.Utils/EncodeTools/AESHelper.cs b/WeiCloud.Fusion/WeiCloud.Utils/EncodeTools/AESHelper.cs index c49089b..b12e699 100644 --- a/WeiCloud.Fusion/WeiCloud.Utils/EncodeTools/AESHelper.cs +++ b/WeiCloud.Fusion/WeiCloud.Utils/EncodeTools/AESHelper.cs @@ -223,6 +223,88 @@ namespace WeiCloudAPP.Core } return sb.ToString().ToUpper(); } + /// + /// AES解密 + /// + /// + /// + /// + /// + /// + /// + /// + public static string Decrypt(string cipherText, string key, string iv) + { + if (string.IsNullOrEmpty(cipherText)) + throw new ArgumentNullException(nameof(cipherText)); + if (string.IsNullOrEmpty(key)) + throw new ArgumentNullException(nameof(key)); + if (string.IsNullOrEmpty(iv)) + throw new ArgumentNullException(nameof(iv)); + + // AES 标准密钥大小为 128, 192 或 256 位 (16, 24 或 32 字节) + if (key.Length != 16 && key.Length != 24 && key.Length != 32) + throw new ArgumentException("密钥长度必须为 16, 24 或 32 字符 (128, 192 或 256 位)"); + + // IV 必须为 16 字节 (128 位) + if (iv.Length != 16) + throw new ArgumentException("IV 长度必须为 16 字符 (128 位)"); + + try + { + byte[] keyBytes = Encoding.UTF8.GetBytes(key); + byte[] ivBytes = Encoding.UTF8.GetBytes(iv); + byte[] cipherTextBytes = Convert.FromBase64String(cipherText); + + using (Aes aesAlg = Aes.Create()) + { + aesAlg.Key = keyBytes; + aesAlg.IV = ivBytes; + aesAlg.Mode = CipherMode.CBC; // 默认就是 CBC,但明确指定是个好习惯 + aesAlg.Padding = PaddingMode.PKCS7; // 默认是 PKCS7 + + ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); + + using (MemoryStream msDecrypt = new MemoryStream(cipherTextBytes)) + { + using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) + { + using (StreamReader srDecrypt = new StreamReader(csDecrypt)) + { + return srDecrypt.ReadToEnd(); + } + } + } + } + } + catch (Exception ex) when (ex is CryptographicException || ex is FormatException) + { + // 记录日志或处理特定异常 + throw new Exception("解密失败", ex); + } + } + /// + /// AES decrypt string + /// + public static string AesDecrypt(string str, string key) + { + if (str == null || key == null) + return null; + + using (Aes aes = Aes.Create()) + { + aes.Key = Encoding.UTF8.GetBytes(key); + aes.Mode = CipherMode.ECB; + aes.Padding = PaddingMode.PKCS7; + + using (var decryptor = aes.CreateDecryptor()) + { + byte[] bytes = Convert.FromBase64String(str); + byte[] decrypted = decryptor.TransformFinalBlock(bytes, 0, bytes.Length); + return Encoding.UTF8.GetString(decrypted); + } + } + } } } diff --git a/WeiCloud.Fusion/WeiCloud.Utils/EncodeTools/AESUtils.cs b/WeiCloud.Fusion/WeiCloud.Utils/EncodeTools/AESUtils.cs new file mode 100644 index 0000000..fe7cd5f --- /dev/null +++ b/WeiCloud.Fusion/WeiCloud.Utils/EncodeTools/AESUtils.cs @@ -0,0 +1,306 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace WeiCloud.Utils.EncodeTools +{ + /// + /// AES加密类 + /// + public class AESUtils + { + // AES块大小固定为16字节(128位) + private const int AES_BLOCK_SIZE = 16; + private const string KEY_ALGORITHM = "AES"; + private const string DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS7Padding"; // PKCS7与Java的PKCS5兼容 + + /// + /// 初始化密钥(128位) + /// + public static byte[] InitSecretKey() + { + using (var aes = Aes.Create()) + { + aes.KeySize = 128; + aes.GenerateKey(); + return aes.Key; + } + } + + /// + /// 加密数据 + /// + public static byte[] Encrypt(byte[] data, byte[] key, string cipherAlgorithm = DEFAULT_CIPHER_ALGORITHM) + { + // 验证输入参数 + ValidateEncryptionParameters(data, key, cipherAlgorithm); + + var (mode, padding) = ParseAlgorithmParameters(cipherAlgorithm); + + using (var aes = Aes.Create()) + { + aes.Key = key; + aes.Mode = mode; + aes.Padding = padding; + + // ECB模式不需要IV,其他模式需要生成IV + if (mode != CipherMode.ECB) + { + aes.GenerateIV(); + } + + using (var encryptor = aes.CreateEncryptor()) + { + // 对于需要IV的模式,将IV前缀到加密结果前 + using (var ms = new MemoryStream()) + { + // 写入IV(如果不是ECB模式) + if (mode != CipherMode.ECB) + { + ms.Write(aes.IV, 0, aes.IV.Length); + } + + using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write)) + { + cs.Write(data, 0, data.Length); + cs.FlushFinalBlock(); + } + + return ms.ToArray(); + } + } + } + } + + /// + /// 解密数据 + /// + public static byte[] Decrypt(byte[] data, byte[] key, string cipherAlgorithm = DEFAULT_CIPHER_ALGORITHM) + { + // 验证输入参数 + ValidateDecryptionParameters(data, key, cipherAlgorithm); + + var (mode, padding) = ParseAlgorithmParameters(cipherAlgorithm); + byte[] iv = null; + byte[] actualData = data; + + // 对于需要IV的模式,从数据开头提取IV + if (mode != CipherMode.ECB) + { + if (data.Length < AES_BLOCK_SIZE) + { + throw new ArgumentException("数据长度不足,无法提取IV"); + } + + // 提取IV(前16字节) + iv = new byte[AES_BLOCK_SIZE]; + Array.Copy(data, 0, iv, 0, iv.Length); + + // 剩余部分为实际加密数据 + actualData = new byte[data.Length - AES_BLOCK_SIZE]; + Array.Copy(data, AES_BLOCK_SIZE, actualData, 0, actualData.Length); + } + + // 验证实际数据是否为完整块 + if (actualData.Length % AES_BLOCK_SIZE != 0) + { + throw new CryptographicException("输入数据不是完整块,请检查数据是否损坏或编码错误"); + } + + using (var aes = Aes.Create()) + { + aes.Key = key; + aes.Mode = mode; + aes.Padding = padding; + if (iv != null) + { + aes.IV = iv; + } + + try + { + using (var decryptor = aes.CreateDecryptor()) + { + using (var ms = new MemoryStream()) + { + using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Write)) + { + cs.Write(actualData, 0, actualData.Length); + cs.FlushFinalBlock(); + } + return ms.ToArray(); + } + } + } + catch (CryptographicException ex) + { + // 处理填充错误,通常是密钥错误或数据被篡改 + if (ex.Message.Contains("Padding is invalid")) + { + throw new CryptographicException("解密失败:填充无效。可能原因:密钥错误、数据被篡改或算法参数不匹配", ex); + } + throw; + } + } + } + + /// + /// 字符串加密(Base64输出) + /// + public static string EncryptString(string plainText, string keyBase64, string cipherAlgorithm = DEFAULT_CIPHER_ALGORITHM) + { + try + { + byte[] key = Convert.FromBase64String(keyBase64); + byte[] data = Encoding.UTF8.GetBytes(plainText); + byte[] encrypted = Encrypt(data, key, cipherAlgorithm); + return Convert.ToBase64String(encrypted); + } + catch (Exception ex) + { + throw new Exception("字符串加密失败:" + ex.Message, ex); + } + } + + /// + /// 字符串解密(Base64输入) + /// + public static string DecryptString(string encryptedText, string keyBase64, string cipherAlgorithm = DEFAULT_CIPHER_ALGORITHM) + { + try + { + byte[] data = ParseHexStr2Byte(encryptedText); + byte[] key = Convert.FromBase64String(keyBase64); + //byte[] data = Convert.FromBase64String(encryptedText); + byte[] decrypted = Decrypt(data, key, cipherAlgorithm); + return Encoding.UTF8.GetString(decrypted); + } + catch (FormatException) + { + throw new Exception("解密失败:输入不是有效的Base64字符串"); + } + catch (CryptographicException ex) + { + throw new Exception("解密失败:" + ex.Message, ex); + } + catch (Exception ex) + { + throw new Exception("字符串解密失败:" + ex.Message, ex); + } + } + + /// + /// 验证加密参数 + /// + private static void ValidateEncryptionParameters(byte[] data, byte[] key, string algorithm) + { + if (data == null || data.Length == 0) + throw new ArgumentNullException(nameof(data), "待加密数据不能为空"); + + if (key == null || key.Length == 0) + throw new ArgumentNullException(nameof(key), "密钥不能为空"); + + // 验证密钥长度(128/192/256位) + if (key.Length != 16 && key.Length != 24 && key.Length != 32) + throw new ArgumentException("密钥长度必须为16字节(128位)、24字节(192位)或32字节(256位)", nameof(key)); + + try + { + ParseAlgorithmParameters(algorithm); + } + catch (Exception ex) + { + throw new ArgumentException("无效的加密算法参数:" + ex.Message, nameof(algorithm), ex); + } + } + + /// + /// 验证解密参数 + /// + private static void ValidateDecryptionParameters(byte[] data, byte[] key, string algorithm) + { + if (data == null || data.Length == 0) + throw new ArgumentNullException(nameof(data), "待解密数据不能为空"); + + if (key == null || key.Length == 0) + throw new ArgumentNullException(nameof(key), "密钥不能为空"); + + // 验证密钥长度 + if (key.Length != 16 && key.Length != 24 && key.Length != 32) + throw new ArgumentException("密钥长度必须为16字节(128位)、24字节(192位)或32字节(256位)", nameof(key)); + } + + /// + /// 解析算法参数(模式和填充方式) + /// + private static (CipherMode mode, PaddingMode padding) ParseAlgorithmParameters(string algorithm) + { + var parts = algorithm.Split('/'); + if (parts.Length != 3) + throw new ArgumentException("算法参数格式应为:Algorithm/Mode/Padding,例如AES/ECB/PKCS7Padding"); + + CipherMode mode = parts[1].ToUpper() switch + { + "ECB" => CipherMode.ECB, + "CBC" => CipherMode.CBC, + "CFB" => CipherMode.CFB, + "OFB" => CipherMode.OFB, + "CTS" => CipherMode.CTS, + _ => throw new ArgumentException($"不支持的加密模式:{parts[1]}") + }; + + PaddingMode padding = parts[2].ToUpper() switch + { + "PKCS5PADDING" => PaddingMode.PKCS7, + "PKCS7PADDING" => PaddingMode.PKCS7, + "ZEROPADDING" => PaddingMode.Zeros, + "ANSIX923PADDING" => PaddingMode.ANSIX923, + "ISO10126PADDING" => PaddingMode.ISO10126, + "NOPADDING" => PaddingMode.None, + _ => throw new ArgumentException($"不支持的填充方式:{parts[2]}") + }; + + return (mode, padding); + } + + // 辅助方法:十六进制与字节数组转换 + public static string BytesToHex(byte[] bytes) + { + if (bytes == null) return null; + StringBuilder sb = new StringBuilder(); + foreach (byte b in bytes) + sb.Append(b.ToString("X2")); + return sb.ToString(); + } + + public static byte[] HexToBytes(string hex) + { + if (string.IsNullOrEmpty(hex) || hex.Length % 2 != 0) + return null; + + byte[] bytes = new byte[hex.Length / 2]; + for (int i = 0; i < hex.Length; i += 2) + { + if (!byte.TryParse(hex.Substring(i, 2), System.Globalization.NumberStyles.HexNumber, null, out bytes[i / 2])) + return null; + } + return bytes; + } + public static byte[] ParseHexStr2Byte(string hexStr) + { + if (string.IsNullOrEmpty(hexStr) || hexStr.Length % 2 != 0) + return null; + + byte[] result = new byte[hexStr.Length / 2]; + for (int i = 0; i < result.Length; i++) + { + int high = Convert.ToInt32(hexStr.Substring(i * 2, 1), 16); + int low = Convert.ToInt32(hexStr.Substring(i * 2 + 1, 1), 16); + result[i] = (byte)((high << 4) | low); + } + return result; + } + } +} \ No newline at end of file diff --git a/WeiCloud.Fusion/WeiCloud.Utils/EncodeTools/AesEncryptor.cs b/WeiCloud.Fusion/WeiCloud.Utils/EncodeTools/AesEncryptor.cs new file mode 100644 index 0000000..e919f46 --- /dev/null +++ b/WeiCloud.Fusion/WeiCloud.Utils/EncodeTools/AesEncryptor.cs @@ -0,0 +1,243 @@ +using System; +using System.Collections.Generic; +using System.Security.Cryptography; +using System.Text; + +namespace WeiCloud.Utils.EncodeTools +{ + public class AesEncryptor + { + // 默认加密算法: AES/ECB/PKCS7Padding + public const string DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS7Padding"; + + /// + /// 解密 + /// + /// 待解密数据 + /// 二进制密钥 + /// 解密后的数据 + public static byte[] Decrypt(byte[] data, byte[] key) + { + return Decrypt(data, key, DEFAULT_CIPHER_ALGORITHM); + } + + /// + /// 解密 + /// + /// 待解密数据 + /// 密钥 + /// 解密后的数据 + public static byte[] Decrypt(byte[] data, SymmetricAlgorithm key) + { + return Decrypt(data, key, DEFAULT_CIPHER_ALGORITHM); + } + + /// + /// 解密 + /// + /// 待解密数据 + /// 二进制密钥 + /// 加密算法/工作模式/填充方式 + /// 解密后的数据 + public static byte[] Decrypt(byte[] data, byte[] key, string cipherAlgorithm) + { + // 还原密钥 + SymmetricAlgorithm symmetricKey = ToKey(key); + return Decrypt(data, symmetricKey, cipherAlgorithm); + } + + /// + /// 解密 + /// + /// 待解密数据 + /// 密钥 + /// 加密算法/工作模式/填充方式 + /// 解密后的数据 + public static byte[] Decrypt(byte[] data, SymmetricAlgorithm key, string cipherAlgorithm) + { + // 解析算法参数 + var algorithmParts = cipherAlgorithm.Split('/'); + if (algorithmParts.Length != 3) + { + throw new ArgumentException("无效的加密算法格式,正确格式: 算法/工作模式/填充方式"); + } + + // 设置算法模式和填充方式 + key.Mode = ParseCipherMode(algorithmParts[1]); + key.Padding = ParsePaddingMode(algorithmParts[2]); + + // 创建解密器并执行解密 + using (ICryptoTransform decryptor = key.CreateDecryptor(key.Key, key.IV)) + { + return decryptor.TransformFinalBlock(data, 0, data.Length); + } + } + + /// + /// 将字节数组转换为可视化字符串 + /// + public static string ShowByteArray(byte[] data) + { + if (data == null) return null; + + StringBuilder sb = new StringBuilder("{"); + foreach (byte b in data) + { + sb.Append(b).Append(","); + } + sb.Remove(sb.Length - 1, 1); + sb.Append("}"); + return sb.ToString(); + } + + /// + /// 将16进制字符串转换为二进制数组 + /// + public static byte[] ParseHexStr2Byte(string hexStr) + { + if (string.IsNullOrEmpty(hexStr) || hexStr.Length % 2 != 0) + return null; + + byte[] result = new byte[hexStr.Length / 2]; + for (int i = 0; i < hexStr.Length / 2; i++) + { + int high = Convert.ToInt32(hexStr.Substring(i * 2, 1), 16); + int low = Convert.ToInt32(hexStr.Substring(i * 2 + 1, 1), 16); + result[i] = (byte)(high * 16 + low); + } + return result; + } + + /// + /// 将二进制数组转换为16进制字符串 + /// + public static string ParseByte2HexStr(byte[] buf) + { + if (buf == null) return null; + + StringBuilder sb = new StringBuilder(); + foreach (byte b in buf) + { + string hex = b.ToString("X2"); // 格式化为两位十六进制,自动补0 + sb.Append(hex); + } + return sb.ToString(); + } + + /// + /// AES加密(ECB模式) + /// + public static string AesEncrypt(string str, string key) + { + if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(key)) + return null; + + using (Aes aes = Aes.Create()) + { + aes.Mode = CipherMode.ECB; + aes.Padding = PaddingMode.PKCS7; + aes.Key = Encoding.UTF8.GetBytes(key); + + using (ICryptoTransform encryptor = aes.CreateEncryptor()) + { + byte[] inputBytes = Encoding.UTF8.GetBytes(str); + byte[] encryptedBytes = encryptor.TransformFinalBlock(inputBytes, 0, inputBytes.Length); + return Convert.ToBase64String(encryptedBytes); + } + } + } + + /// + /// AES解密(ECB模式) + /// + public static string AesDecrypt(string str, string key) + { + if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(key)) + return null; + + using (Aes aes = Aes.Create()) + { + aes.Mode = CipherMode.ECB; + aes.Padding = PaddingMode.PKCS7; + aes.Key = Encoding.UTF8.GetBytes(key); + + using (ICryptoTransform decryptor = aes.CreateDecryptor()) + { + byte[] inputBytes = Convert.FromBase64String(str); + byte[] decryptedBytes = decryptor.TransformFinalBlock(inputBytes, 0, inputBytes.Length); + return Encoding.UTF8.GetString(decryptedBytes); + } + } + } + + /// + /// 将字节数组转换为SymmetricAlgorithm(AES密钥) + /// + private static SymmetricAlgorithm ToKey(byte[] key) + { + Aes aes = Aes.Create(); + aes.Key = key; + aes.IV = new byte[aes.BlockSize / 8]; // ECB模式不需要IV,但需要初始化 + return aes; + } + + /// + /// 解析工作模式字符串为CipherMode枚举 + /// + private static CipherMode ParseCipherMode(string mode) + { + return mode.ToUpper() switch + { + "ECB" => CipherMode.ECB, + "CBC" => CipherMode.CBC, + "CFB" => CipherMode.CFB, + "OFB" => CipherMode.OFB, + _ => throw new ArgumentException($"不支持的工作模式: {mode}") + }; + } + + /// + /// 解析填充方式字符串为PaddingMode枚举 + /// + private static PaddingMode ParsePaddingMode(string padding) + { + return padding.ToUpper() switch + { + "PKCS5PADDING" => PaddingMode.PKCS7, // .NET中PKCS7兼容PKCS5 + "PKCS7PADDING" => PaddingMode.PKCS7, + "NOPADDING" => PaddingMode.None, + "ZEROPADDING" => PaddingMode.Zeros, + _ => throw new ArgumentException($"不支持的填充方式: {padding}") + }; + } + + // 测试方法 + public static void Main(string[] args) + { + try + { + // 测试密钥 (Base64编码) + string keyBase64 = "cmVmb3JtZXJyZWZvcm1lcg=="; + byte[] keyBytes = Convert.FromBase64String(keyBase64); + Console.WriteLine("密钥 (Base64): " + keyBase64); + Console.WriteLine("密钥 (字节数组): " + ShowByteArray(keyBytes)); + + // 待解密的16进制字符串 + string hexStr = "C6376BAB4DDBAF1CB22392B44E5FCAB7E954F1A99812AF85A538D91CBD1BC24B7C6BCE8E2C479FD50E477748EA2FAABECA6D16911B00671F00EE54B7B8449EA133750FF8E62107C302BCE241B62353448A02483EB6D65E23449498EC873A1D09C808652E39E38890EA0248370A57C6C3AF5B4C0AE9315F4B15BEEE667A68F24221CD1CD33F99B318487F8AAA1B9B69F6A7FAA5508651D5782907DF60423771E66E0C2AED967B589471C429D9BBF23831A3DA56F709B8449AC3DFF791D1E3996AF0E5C3E046740568432120C99C736B737F0967DF4928DC118B03F96AE286D3D55EAE5CA9D9F4F9D1EC2C8781D413F081A0F666C7ABF994AD993A0270544A72F91EFC871E11BC911512E0FF32BC06D90C"; + + // 解密过程 + byte[] encryptedData = ParseHexStr2Byte(hexStr); + byte[] decryptedData = Decrypt(encryptedData, keyBytes); + string decryptedStr = Encoding.UTF8.GetString(decryptedData); + + // 输出结果 + Console.WriteLine("\n解密后数据: " + decryptedStr); + } + catch (Exception ex) + { + Console.WriteLine("解密出错: " + ex.Message); + } + } + } + +}