pull/12/head
chencaixia 4 months ago
parent 1a8d1e602e
commit 034a190e7f
  1. 7
      WeiCloud.Fusion/ParkLotInfoService/Class1.cs
  2. 16
      WeiCloud.Fusion/ParkLotInfoService/IParkingLotDataService.cs
  3. 21
      WeiCloud.Fusion/ParkLotInfoService/ParkLotInfoService.csproj
  4. 195
      WeiCloud.Fusion/ParkLotInfoService/ParkingLotDataService.cs
  5. 7
      WeiCloud.Fusion/ParkingLotEntity/Class1.cs
  6. 1
      WeiCloud.Fusion/ParkingLotEntity/EntityCreate.txt
  7. 24
      WeiCloud.Fusion/ParkingLotEntity/ParkingLotEntity.csproj
  8. 56
      WeiCloud.Fusion/ParkingLotEntity/ParkingLotModelDto/BaseModels/ApiResult.cs
  9. 199
      WeiCloud.Fusion/ParkingLotEntity/ParkingLotModelDto/ParkingAccessRecordDto.cs
  10. 64
      WeiCloud.Fusion/ParkingLotEntity/ParkingLotModels/ParkingAccessRecord.cs
  11. 91
      WeiCloud.Fusion/ParkingLotEntity/ParkingLotModels/WeiCloudFusionContext.cs
  12. 44
      WeiCloud.Fusion/ParkingLotService/ParkingLotService.API/Controllers/ParkLotInfoController.cs
  13. 14
      WeiCloud.Fusion/ParkingLotService/ParkingLotService.API/ParkingLotService.API.csproj
  14. 56
      WeiCloud.Fusion/ParkingLotService/ParkingLotService.API/Program.cs
  15. 25
      WeiCloud.Fusion/ParkingLotService/ParkingLotService.API/Startup.cs
  16. 8
      WeiCloud.Fusion/ParkingLotService/ParkingLotService.API/appsettings.json
  17. 45
      WeiCloud.Fusion/WeiCloud.Fusion.sln
  18. 82
      WeiCloud.Fusion/WeiCloud.Utils/EncodeTools/AESHelper.cs
  19. 306
      WeiCloud.Fusion/WeiCloud.Utils/EncodeTools/AESUtils.cs
  20. 243
      WeiCloud.Fusion/WeiCloud.Utils/EncodeTools/AesEncryptor.cs

@ -0,0 +1,7 @@
namespace ParkLotInfoService
{
public class Class1
{
}
}

@ -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<ApiResult<int>> PostParkingLots(ParkingAccessRecordDto dto);
Task<ApiResult<ParkingLotInfoDto>> GetParkingLotInfo(string parkId);
}
}

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.8" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ParkingLotEntity\ParkingLotEntity.csproj" />
<ProjectReference Include="..\WeiCloud.Core\WeiCloud.Core.csproj" />
<ProjectReference Include="..\WeiCloud.Utils\WeiCloud.Utils.csproj" />
</ItemGroup>
</Project>

@ -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<ParkingLotDataService> _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<ParkingLotDataService> logger, WeiCloudFusionContext context, IConfiguration configuration, IHttpClientFactory httpClientFactory)
{
_logger = logger;
_context = context;
_configuration = configuration;
_httpClientFactory = httpClientFactory;
}
/// <summary>
/// 上传停车场信息
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public async Task<ApiResult<int>> PostParkingLots(ParkingAccessRecordDto dto)
{
ApiResult<int> result = new ApiResult<int>() { 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<ParkingAccessRecordDto>(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;
}
/// <summary>
/// 获取停车场信息
/// </summary>
/// <param name="parkId"></param>
/// <returns></returns>
public async Task<ApiResult<ParkingLotInfoDto>> GetParkingLotInfo(string parkId)
{
ApiResult<ParkingLotInfoDto> result = new ApiResult<ParkingLotInfoDto>() { 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<ParkingLotInfoDto>(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;
}
/// <summary>
/// 获取立方接口数据
/// </summary>
/// <param name="apiName"></param>
/// <param name="parmJson"></param>
/// <returns></returns>
private async Task<string> 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<string> 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;
}
}
}

@ -0,0 +1,7 @@
namespace ParkingLotEntity
{
public class Class1
{
}
}

@ -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

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="9.0.0-rc.1.efcore.9.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
</ItemGroup>
</Project>

@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace ParkingLotEntity.ParkingLotModelDto.BaseModels
{
public class ApiResult<T>
{
public int Code { get; set; }
public string Msg { get; set; }
public string InnerMsg { get; set; }
public T Data { get; set; }
public static ApiResult<T> IsSuccess(T data, string msg = "请求成功")
{
return new ApiResult<T>() { Code = 200, Data = data, Msg = msg };
}
/// <summary>
/// 服务端处理错误
/// </summary>
/// <param name="msg"></param>
/// <returns></returns>
public static ApiResult<T> IsFail(string msg)
{
return new ApiResult<T> { Code = 2005, Msg = msg };
}
/// <summary>
/// 服务端处理错误
/// </summary>
/// <param name="msg"></param>
/// <returns></returns>
public static ApiResult<T> IsNoFound(string msg = null)
{
return new ApiResult<T> { Code = 2005, Msg = msg ?? "数据不存在" };
}
/// <summary>
/// 客户端错误
/// </summary>
/// <param name="msg"></param>
/// <returns></returns>
public static ApiResult<T> IsBadReq(string msg)
{
return new ApiResult<T> { Code = 2006, Msg = msg };
}
}
public class ApiHubResult<T> : ApiResult<T>
{
public long? Projectid { get; set; }
public long? Constid { get; set; }
}
}

@ -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
{
/// <summary>
/// 项目id
/// </summary>
public long ProjectId { get; set; }
/// <summary>
/// 车牌号码
/// </summary>
public string? CarCode { get; set; }
/// <summary>
/// 进场时间(出场时若找不到场内车辆信息,则为空)
/// </summary>
public DateTime? InTime { get; set; }
/// <summary>
/// 过场时间(进场时等于进场时间,出场时为出场时间)
/// </summary>
public DateTime? PassTime { get; set; }
/// <summary>
/// 车场 ID
/// </summary>
public string? ParkId { get; set; }
/// <summary>
/// 0:进场,1:出场
/// </summary>
public string? InOrOut { get; set; }
/// <summary>
/// 车辆本次进场出场标识
/// </summary>
public string? GUID { get; set; }
/// <summary>
/// 通道 ID
/// </summary>
public string? ChannelId { get; set; }
/// <summary>
/// 通道名称
/// </summary>
public string? ChannelName { get; set; }
/// <summary>
/// 图片路径地址
/// </summary>
public string? ImagePath { get; set; }
}
public class ParkingAccessRecord2Dto
{
/// <summary>
/// id
/// </summary>
public long Id { get; set; }
/// <summary>
/// 项目id
/// </summary>
public long ProjectId { get; set; }
/// <summary>
/// 车牌号码
/// </summary>
public string? CarCode { get; set; }
/// <summary>
/// 进场时间(出场时若找不到场内车辆信息,则为空)
/// </summary>
public DateTime? InTime { get; set; }
/// <summary>
/// 过场时间(进场时等于进场时间,出场时为出场时间)
/// </summary>
public DateTime? PassTime { get; set; }
/// <summary>
/// 车场 ID
/// </summary>
public string? ParkId { get; set; }
/// <summary>
/// 0:进场,1:出场
/// </summary>
public string? InOrOut { get; set; }
/// <summary>
/// 车辆本次进场出场标识
/// </summary>
public string? GUID { get; set; }
/// <summary>
/// 通道 ID
/// </summary>
public string? ChannelId { get; set; }
/// <summary>
/// 通道名称
/// </summary>
public string? ChannelName { get; set; }
/// <summary>
/// 图片路径地址
/// </summary>
public string? ImagePath { get; set; }
}
/// <summary>
/// 车场信息
/// </summary>
public class ParkingLotInfoDto
{
/// <summary>
/// 0成功,-1失败
/// </summary>
public int Code { get; set; }
/// <summary>
/// 返回说明
/// </summary>
public string? ResMsg { get; set; }
/// <summary>
/// 总车位数
/// </summary>
public int TotalNum { get; set; }
/// <summary>
/// 总已停车位数
/// </summary>
public int TotalStopNum { get; set; }
/// <summary>
/// 总剩余车位
/// </summary>
public int TotalRemainNum { get; set; }
/// <summary>
/// 车场ID
/// </summary>
public string? ParkID { get; set; }
/// <summary>
/// 车场名称
/// </summary>
public string? ParkName { get; set; }
/// <summary>
/// 收费标准
/// </summary>
public string? ChargeRuleDesc { get; set; }
/// <summary>
/// 区域余位信息
/// </summary>
public List<ParkingLotInfo>? ParkingLotInfo { get; set; }
}
/// <summary>
/// 区域信息
/// </summary>
public class ParkingLotInfo
{
/// <summary>
/// 区域ID
/// </summary>
public int ParkingLotId { get; set; }
/// <summary>
/// 区域名称
/// </summary>
public string? ParkingLotName { get; set; }
/// <summary>
/// 区域总车位数
/// </summary>
public int TotalNum { get; set; }
/// <summary>
/// 区域已停车位数
/// </summary>
public int TotalStopNum { get; set; }
/// <summary>
/// 区域剩余车位
/// </summary>
public int TotalRemainNum { get; set; }
}
/// <summary>
/// 车场信息参数
/// </summary>
public class ParkingLotInfoParmDto
{
/// <summary>
/// 终端号
/// </summary>
public string? StationNo { get; set; }
}
}

@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
namespace ParkingLotEntity.ParkingLotModels;
public partial class ParkingAccessRecord
{
public long Id { get; set; }
/// <summary>
/// 项目id
/// </summary>
public long ProjectId { get; set; }
/// <summary>
/// 车牌号码
/// </summary>
public string? CarCode { get; set; }
/// <summary>
/// 进场时间(出场时若找不到场内车辆信息,则为空)
/// </summary>
public DateTime? InTime { get; set; }
/// <summary>
/// 过场时间(进场时等于进场时间,出场时为出场时间)
/// </summary>
public DateTime? PassTime { get; set; }
/// <summary>
/// 车场 ID
/// </summary>
public string? ParkId { get; set; }
/// <summary>
/// 0:进场,1:出场
/// </summary>
public string? InOrOut { get; set; }
/// <summary>
/// 车辆本次进场出场标识
/// </summary>
public string? Guid { get; set; }
/// <summary>
/// 通道 ID
/// </summary>
public string? ChannelId { get; set; }
/// <summary>
/// 通道名称
/// </summary>
public string? ChannelName { get; set; }
/// <summary>
/// 图片路径地址
/// </summary>
public string? ImagePath { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreateTime { get; set; }
}

@ -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<WeiCloudFusionContext> options)
: base(options)
{
}
public virtual DbSet<ParkingAccessRecord> 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<ParkingAccessRecord>(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);
}

@ -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<ParkLotInfoController> _logger;
//private readonly CurrentContext CurrentContext;
public ParkLotInfoController(IParkingLotDataService parkingLotDataService, ILogger<ParkLotInfoController> logger)
{
_logger = logger;
_parkingLotDataService = parkingLotDataService;
}
/// <summary>
/// 提交停车信息
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost]
public async Task<ApiResult<int>> PostParkingLots(ParkingAccessRecordDto dto)
{
return await _parkingLotDataService.PostParkingLots(dto);
}
/// <summary>
/// 获取停车场信息
/// </summary>
/// <param name="parkId"></param>
/// <returns></returns>
[HttpGet]
public async Task<ApiResult<ParkingLotInfoDto>> GetParkingLotInfo(string parkId)
{
return await _parkingLotDataService.GetParkingLotInfo(parkId);
}
}
}

@ -7,11 +7,23 @@
</PropertyGroup>
<ItemGroup>
<Compile Remove="logs\**" />
<Content Remove="logs\**" />
<EmbeddedResource Remove="logs\**" />
<None Remove="logs\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.8" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
</ItemGroup>
<ItemGroup>
<Folder Include="Controllers\" />
<ProjectReference Include="..\..\ParkingLotEntity\ParkingLotEntity.csproj" />
<ProjectReference Include="..\..\ParkLotInfoService\ParkLotInfoService.csproj" />
<ProjectReference Include="..\..\WeiCloud.Utils\WeiCloud.Utils.csproj" />
</ItemGroup>
</Project>

@ -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<WeiCloudFusionContext>(Options =>
{
Options.UseMySql(builder.Configuration.GetConnectionString("WeiCloudFusionDBConn"), new MySqlServerVersion(new Version(8, 0, 19)));
Options.EnableDetailedErrors();
}, ServiceLifetime.Scoped);
builder.Services.AddScoped<IParkingLotDataService, ParkingLotDataService>();
//---
#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();

@ -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<WeiCloudFusionContext>(Options => Options.UseMySql(_weicloudWeiCloudFusionDBConndbconn, new MySqlServerVersion(new Version(8, 0, 19))), ServiceLifetime.Scoped);
}
}
}

@ -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": ""
}

@ -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}

@ -223,6 +223,88 @@ namespace WeiCloudAPP.Core
}
return sb.ToString().ToUpper();
}
/// <summary>
/// AES解密
/// </summary>
/// <param name="cipherText"></param>
/// <param name="key"></param>
/// <param name="iv"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="Exception"></exception>
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);
}
}
/// <summary>
/// AES decrypt string
/// </summary>
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);
}
}
}
}
}

@ -0,0 +1,306 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace WeiCloud.Utils.EncodeTools
{
/// <summary>
/// AES加密类
/// </summary>
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兼容
/// <summary>
/// 初始化密钥(128位)
/// </summary>
public static byte[] InitSecretKey()
{
using (var aes = Aes.Create())
{
aes.KeySize = 128;
aes.GenerateKey();
return aes.Key;
}
}
/// <summary>
/// 加密数据
/// </summary>
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();
}
}
}
}
/// <summary>
/// 解密数据
/// </summary>
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;
}
}
}
/// <summary>
/// 字符串加密(Base64输出)
/// </summary>
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);
}
}
/// <summary>
/// 字符串解密(Base64输入)
/// </summary>
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);
}
}
/// <summary>
/// 验证加密参数
/// </summary>
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);
}
}
/// <summary>
/// 验证解密参数
/// </summary>
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));
}
/// <summary>
/// 解析算法参数(模式和填充方式)
/// </summary>
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;
}
}
}

@ -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";
/// <summary>
/// 解密
/// </summary>
/// <param name="data">待解密数据</param>
/// <param name="key">二进制密钥</param>
/// <returns>解密后的数据</returns>
public static byte[] Decrypt(byte[] data, byte[] key)
{
return Decrypt(data, key, DEFAULT_CIPHER_ALGORITHM);
}
/// <summary>
/// 解密
/// </summary>
/// <param name="data">待解密数据</param>
/// <param name="key">密钥</param>
/// <returns>解密后的数据</returns>
public static byte[] Decrypt(byte[] data, SymmetricAlgorithm key)
{
return Decrypt(data, key, DEFAULT_CIPHER_ALGORITHM);
}
/// <summary>
/// 解密
/// </summary>
/// <param name="data">待解密数据</param>
/// <param name="key">二进制密钥</param>
/// <param name="cipherAlgorithm">加密算法/工作模式/填充方式</param>
/// <returns>解密后的数据</returns>
public static byte[] Decrypt(byte[] data, byte[] key, string cipherAlgorithm)
{
// 还原密钥
SymmetricAlgorithm symmetricKey = ToKey(key);
return Decrypt(data, symmetricKey, cipherAlgorithm);
}
/// <summary>
/// 解密
/// </summary>
/// <param name="data">待解密数据</param>
/// <param name="key">密钥</param>
/// <param name="cipherAlgorithm">加密算法/工作模式/填充方式</param>
/// <returns>解密后的数据</returns>
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);
}
}
/// <summary>
/// 将字节数组转换为可视化字符串
/// </summary>
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();
}
/// <summary>
/// 将16进制字符串转换为二进制数组
/// </summary>
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;
}
/// <summary>
/// 将二进制数组转换为16进制字符串
/// </summary>
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();
}
/// <summary>
/// AES加密(ECB模式)
/// </summary>
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);
}
}
}
/// <summary>
/// AES解密(ECB模式)
/// </summary>
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);
}
}
}
/// <summary>
/// 将字节数组转换为SymmetricAlgorithm(AES密钥)
/// </summary>
private static SymmetricAlgorithm ToKey(byte[] key)
{
Aes aes = Aes.Create();
aes.Key = key;
aes.IV = new byte[aes.BlockSize / 8]; // ECB模式不需要IV,但需要初始化
return aes;
}
/// <summary>
/// 解析工作模式字符串为CipherMode枚举
/// </summary>
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}")
};
}
/// <summary>
/// 解析填充方式字符串为PaddingMode枚举
/// </summary>
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);
}
}
}
}
Loading…
Cancel
Save