feat(drone): 更新数据库配置并优化无人机数据管理功能
- 修改appsettings.json中的数据库连接字符串,将数据库名称从drone_enforcement_hedongnew更改为drone_enforcement_hedong2 - 在DroneSsnyApp类中添加System.IO.Compression、System.Text、Infrastructure.Helpers和NetTopologySuite.IO.Esri命名空间引用 - 重构代码格式,改善方法参数排列和查询语句的可读性 - 优化导出功能中的时间字段处理逻辑,添加对空值的检查和处理 - 实现SHP文件压缩包的解析和几何数据提取功能,支持从ZIP文件中读取SHP文件并更新数据库 - 添加临时文件清理机制,确保解压后的目录能够被正确删除 - 优化预警列表和审核列表的数据查询条件,改进JOIN操作的处理方式 - 启用SQL查询日志输出,便于调试和性能监控dev
parent
9a9476e6ce
commit
15acc78b14
|
|
@ -1,4 +1,8 @@
|
|||
using Infrastructure;
|
||||
using System.IO.Compression;
|
||||
using System.Text;
|
||||
using Infrastructure;
|
||||
using Infrastructure.Helpers;
|
||||
using NetTopologySuite.IO.Esri;
|
||||
using NPOI.HSSF.UserModel;
|
||||
using NPOI.SS.UserModel;
|
||||
using OpenAuth.App.BaseApp.Base;
|
||||
|
|
@ -14,7 +18,9 @@ namespace OpenAuth.App.ServiceApp.DroneSsnydManage
|
|||
public class DroneSsnyApp : SqlSugarBaseApp<DroneSsnyd, SugarDbContext>
|
||||
{
|
||||
private readonly ISqlSugarClient _client;
|
||||
public DroneSsnyApp(ISugarUnitOfWork<SugarDbContext> unitWork, ISimpleClient<DroneSsnyd> repository, ISqlSugarClient client, IAuth auth)
|
||||
|
||||
public DroneSsnyApp(ISugarUnitOfWork<SugarDbContext> unitWork, ISimpleClient<DroneSsnyd> repository,
|
||||
ISqlSugarClient client, IAuth auth)
|
||||
: base(unitWork, repository, auth)
|
||||
{
|
||||
_client = client;
|
||||
|
|
@ -188,8 +194,7 @@ namespace OpenAuth.App.ServiceApp.DroneSsnydManage
|
|||
.WhereIF(!string.IsNullOrEmpty(req.xiangmu_no), a => a.xiangmu_no.Contains(req.xiangmu_no))
|
||||
.WhereIF(!string.IsNullOrEmpty(req.xiangmu_name), a => a.xiangmu_name.Contains(req.xiangmu_name))
|
||||
.WhereIF(!issystem, a => (ids.Contains(a.countyid) || ids.Contains(a.streetid)))
|
||||
.Select(
|
||||
a => new DroneSsnydExport()
|
||||
.Select(a => new DroneSsnydExport()
|
||||
{
|
||||
xiangmu_no = a.xiangmu_no,
|
||||
xiangmu_name = a.xiangmu_name,
|
||||
|
|
@ -229,8 +234,7 @@ namespace OpenAuth.App.ServiceApp.DroneSsnydManage
|
|||
.WhereIF(!string.IsNullOrEmpty(req.xiangmu_no), a => a.xiangmu_no.Contains(req.xiangmu_no))
|
||||
.WhereIF(!string.IsNullOrEmpty(req.xiangmu_name), a => a.xiangmu_name.Contains(req.xiangmu_name))
|
||||
.WhereIF(!issystem, a => (ids.Contains(a.countyid) || ids.Contains(a.streetid)))
|
||||
.Select(
|
||||
a => new DroneSsnydExport()
|
||||
.Select(a => new DroneSsnydExport()
|
||||
{
|
||||
xiangmu_no = a.xiangmu_no,
|
||||
xiangmu_name = a.xiangmu_name,
|
||||
|
|
@ -326,7 +330,7 @@ namespace OpenAuth.App.ServiceApp.DroneSsnydManage
|
|||
/// <param name="pageSize"></param>
|
||||
/// <returns></returns>
|
||||
public List<DroneSsnyd> GetDronssnydExportList(string xiangmumc, string countyid, string streetid,
|
||||
string xiangmuno, DateTime? hechatimebegin, DateTime? hechatimeend, string xiangmuyt,int? status)
|
||||
string xiangmuno, DateTime? hechatimebegin, DateTime? hechatimeend, string xiangmuyt, int? status)
|
||||
{
|
||||
//获取当前登录用户
|
||||
var orgs = _auth.GetCurrentUser().Orgs;
|
||||
|
|
@ -335,6 +339,7 @@ namespace OpenAuth.App.ServiceApp.DroneSsnydManage
|
|||
{
|
||||
ids = orgs.Select(r => r.Id.ToString()).ToList();
|
||||
}
|
||||
|
||||
bool issystem = _auth.IsSystem();
|
||||
|
||||
var list = base.Repository.AsQueryable()
|
||||
|
|
@ -345,18 +350,27 @@ namespace OpenAuth.App.ServiceApp.DroneSsnydManage
|
|||
.WhereIF(!string.IsNullOrEmpty(xiangmuyt), a => a.xiangmu_yt == xiangmuyt)
|
||||
.WhereIF(!string.IsNullOrEmpty(xiangmuno), a => a.xiangmu_no.Contains(xiangmuno))
|
||||
.WhereIF(!issystem, a => (ids.Contains(a.countyid) || ids.Contains(a.streetid)))
|
||||
.WhereIF(status!=null,a=>a.handle_status_id== status)
|
||||
.WhereIF(status != null, a => a.handle_status_id == status)
|
||||
.LeftJoin<DroneSsnydRcjg>((a, d) => a.Id == d.ssnyd_id)
|
||||
.WhereIF(hechatimebegin != null && hechatimeend != null,
|
||||
(a, d) => (a.hechatime_kg != null && a.hechatime_kg < hechatimeend && a.hechatime_kg >= hechatimebegin)
|
||||
|| (a.hechatime_jz != null && a.hechatime_jz < hechatimeend && a.hechatime_jz >= hechatimebegin)
|
||||
|| (a.hechatime_wg != null && a.hechatime_wg < hechatimeend && a.hechatime_wg >= hechatimebegin)
|
||||
|| (a.hechatime_wgzg != null && a.hechatime_wgzg < hechatimeend && a.hechatime_wgzg >= hechatimebegin)
|
||||
|| (a.hechatime_jzzg != null && a.hechatime_jzzg < hechatimeend && a.hechatime_jzzg >= hechatimebegin)
|
||||
|| (a.hechatime_kgzg != null && a.hechatime_kgzg < hechatimeend && a.hechatime_kgzg >= hechatimebegin)
|
||||
|| (d.hechatime_rc != null && d.hechatime_rc < hechatimeend && d.hechatime_rc >= hechatimebegin)
|
||||
|| (d.hechatime_rczg != null && d.hechatime_rczg < hechatimeend && d.hechatime_rczg >= hechatimebegin)
|
||||
|| (d.hechatime_rcfh != null && d.hechatime_rcfh < hechatimeend && d.hechatime_rcfh >= hechatimebegin))
|
||||
(a, d) => (a.hechatime_kg != null && a.hechatime_kg < hechatimeend &&
|
||||
a.hechatime_kg >= hechatimebegin)
|
||||
|| (a.hechatime_jz != null && a.hechatime_jz < hechatimeend &&
|
||||
a.hechatime_jz >= hechatimebegin)
|
||||
|| (a.hechatime_wg != null && a.hechatime_wg < hechatimeend &&
|
||||
a.hechatime_wg >= hechatimebegin)
|
||||
|| (a.hechatime_wgzg != null && a.hechatime_wgzg < hechatimeend &&
|
||||
a.hechatime_wgzg >= hechatimebegin)
|
||||
|| (a.hechatime_jzzg != null && a.hechatime_jzzg < hechatimeend &&
|
||||
a.hechatime_jzzg >= hechatimebegin)
|
||||
|| (a.hechatime_kgzg != null && a.hechatime_kgzg < hechatimeend &&
|
||||
a.hechatime_kgzg >= hechatimebegin)
|
||||
|| (d.hechatime_rc != null && d.hechatime_rc < hechatimeend &&
|
||||
d.hechatime_rc >= hechatimebegin)
|
||||
|| (d.hechatime_rczg != null && d.hechatime_rczg < hechatimeend &&
|
||||
d.hechatime_rczg >= hechatimebegin)
|
||||
|| (d.hechatime_rcfh != null && d.hechatime_rcfh < hechatimeend &&
|
||||
d.hechatime_rcfh >= hechatimebegin))
|
||||
.OrderBy((a, d) => a.end_time, OrderByType.Desc)
|
||||
.Select((a, d) => a)
|
||||
.Distinct()
|
||||
|
|
@ -469,6 +483,7 @@ namespace OpenAuth.App.ServiceApp.DroneSsnydManage
|
|||
{
|
||||
dataRow.Cells[10].SetCellValue("");
|
||||
}
|
||||
|
||||
if (item.end_time != null)
|
||||
{
|
||||
dataRow.Cells[11].SetCellValue(Convert.ToDateTime(item.end_time).ToString("yyyy-MM-dd"));
|
||||
|
|
@ -477,6 +492,7 @@ namespace OpenAuth.App.ServiceApp.DroneSsnydManage
|
|||
{
|
||||
dataRow.Cells[11].SetCellValue("");
|
||||
}
|
||||
|
||||
dataRow.Cells[12].SetCellValue(item.xiangmu_yt);
|
||||
dataRow.Cells[13].SetCellValue(item.jianzhujiegou);
|
||||
if (item.shenqing_area != null)
|
||||
|
|
@ -538,7 +554,7 @@ namespace OpenAuth.App.ServiceApp.DroneSsnydManage
|
|||
/// <param name="pageSize"></param>
|
||||
/// <returns></returns>
|
||||
public PageInfo<List<DroneSsnyd>> GetDronssnydList(string xiangmumc, string countyid, string streetid,
|
||||
string xiangmuno, DateTime? hechatimebegin,DateTime?hechatimeend, string xiangmuyt, int? status,
|
||||
string xiangmuno, DateTime? hechatimebegin, DateTime? hechatimeend, string xiangmuyt, int? status,
|
||||
int pageIndex, int pageSize)
|
||||
{
|
||||
int totalCount = 0;
|
||||
|
|
@ -554,9 +570,8 @@ namespace OpenAuth.App.ServiceApp.DroneSsnydManage
|
|||
bool issystem = _auth.IsSystem();
|
||||
|
||||
|
||||
|
||||
var list = base.Repository.AsQueryable()
|
||||
.Where(a => a.handle_status_id != 0&&a.handle_status_id!=null)
|
||||
.Where(a => a.handle_status_id != 0 && a.handle_status_id != null)
|
||||
.WhereIF(!string.IsNullOrEmpty(xiangmumc), a => a.xiangmu_name.Contains(xiangmumc))
|
||||
.WhereIF(!string.IsNullOrEmpty(countyid), a => a.countyid == countyid)
|
||||
.WhereIF(!string.IsNullOrEmpty(streetid), a => a.streetid == streetid)
|
||||
|
|
@ -564,19 +579,26 @@ namespace OpenAuth.App.ServiceApp.DroneSsnydManage
|
|||
.WhereIF(!string.IsNullOrEmpty(xiangmuno), a => a.xiangmu_no.Contains(xiangmuno))
|
||||
.WhereIF(!issystem, a => (ids.Contains(a.countyid) || ids.Contains(a.streetid)))
|
||||
.WhereIF(status != null, a => a.handle_status_id == status)
|
||||
|
||||
.LeftJoin<DroneSsnydRcjg>((a,d)=>a.Id==d.ssnyd_id)
|
||||
.LeftJoin<DroneSsnydRcjg>((a, d) => a.Id == d.ssnyd_id)
|
||||
.WhereIF(hechatimebegin != null && hechatimeend != null,
|
||||
(a, d) => (a.hechatime_kg != null && a.hechatime_kg < hechatimeend && a.hechatime_kg >= hechatimebegin)
|
||||
|| (a.hechatime_jz != null && a.hechatime_jz < hechatimeend && a.hechatime_jz >= hechatimebegin)
|
||||
|| (a.hechatime_wg != null && a.hechatime_wg < hechatimeend && a.hechatime_wg >= hechatimebegin)
|
||||
|| (a.hechatime_wgzg != null && a.hechatime_wgzg < hechatimeend && a.hechatime_wgzg >= hechatimebegin)
|
||||
|| (a.hechatime_jzzg != null && a.hechatime_jzzg < hechatimeend && a.hechatime_jzzg >= hechatimebegin)
|
||||
|| (a.hechatime_kgzg != null && a.hechatime_kgzg < hechatimeend && a.hechatime_kgzg >= hechatimebegin)
|
||||
|| (d.hechatime_rc != null && d.hechatime_rc < hechatimeend && d.hechatime_rc >= hechatimebegin)
|
||||
|| (d.hechatime_rczg != null && d.hechatime_rczg < hechatimeend && d.hechatime_rczg >= hechatimebegin)
|
||||
|| (d.hechatime_rcfh != null && d.hechatime_rcfh < hechatimeend && d.hechatime_rcfh >= hechatimebegin))
|
||||
|
||||
(a, d) => (a.hechatime_kg != null && a.hechatime_kg < hechatimeend &&
|
||||
a.hechatime_kg >= hechatimebegin)
|
||||
|| (a.hechatime_jz != null && a.hechatime_jz < hechatimeend &&
|
||||
a.hechatime_jz >= hechatimebegin)
|
||||
|| (a.hechatime_wg != null && a.hechatime_wg < hechatimeend &&
|
||||
a.hechatime_wg >= hechatimebegin)
|
||||
|| (a.hechatime_wgzg != null && a.hechatime_wgzg < hechatimeend &&
|
||||
a.hechatime_wgzg >= hechatimebegin)
|
||||
|| (a.hechatime_jzzg != null && a.hechatime_jzzg < hechatimeend &&
|
||||
a.hechatime_jzzg >= hechatimebegin)
|
||||
|| (a.hechatime_kgzg != null && a.hechatime_kgzg < hechatimeend &&
|
||||
a.hechatime_kgzg >= hechatimebegin)
|
||||
|| (d.hechatime_rc != null && d.hechatime_rc < hechatimeend &&
|
||||
d.hechatime_rc >= hechatimebegin)
|
||||
|| (d.hechatime_rczg != null && d.hechatime_rczg < hechatimeend &&
|
||||
d.hechatime_rczg >= hechatimebegin)
|
||||
|| (d.hechatime_rcfh != null && d.hechatime_rcfh < hechatimeend &&
|
||||
d.hechatime_rcfh >= hechatimebegin))
|
||||
.OrderBy((a, d) => a.end_time, OrderByType.Desc)
|
||||
.Select((a, d) => a)
|
||||
.Distinct()
|
||||
|
|
@ -592,6 +614,7 @@ namespace OpenAuth.App.ServiceApp.DroneSsnydManage
|
|||
#endregion
|
||||
|
||||
#region 预警列表
|
||||
|
||||
/// <summary>
|
||||
/// 预警问题列表
|
||||
/// </summary>
|
||||
|
|
@ -615,8 +638,8 @@ namespace OpenAuth.App.ServiceApp.DroneSsnydManage
|
|||
bool issystem = _auth.IsSystem();
|
||||
|
||||
var list = _client.Queryable<DroneSsnydRcjg>()
|
||||
.LeftJoin<DroneSsnyd>((r,a)=>r.ssnyd_id==a.Id)
|
||||
.Where((r, a) => r.iswenti_rc=="是"&&r.is_lishi==0)
|
||||
.LeftJoin<DroneSsnyd>((r, a) => r.ssnyd_id == a.Id)
|
||||
.Where((r, a) => r.iswenti_rc == "是" && r.is_lishi == 0)
|
||||
.WhereIF(!string.IsNullOrEmpty(xiangmumc), (r, a) => a.xiangmu_name.Contains(xiangmumc))
|
||||
.WhereIF(!string.IsNullOrEmpty(countyid), (r, a) => a.countyid == countyid)
|
||||
.WhereIF(!string.IsNullOrEmpty(streetid), (r, a) => a.streetid == streetid)
|
||||
|
|
@ -665,6 +688,7 @@ namespace OpenAuth.App.ServiceApp.DroneSsnydManage
|
|||
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导出
|
||||
/// </summary>
|
||||
|
|
@ -769,6 +793,7 @@ namespace OpenAuth.App.ServiceApp.DroneSsnydManage
|
|||
{
|
||||
dataRow.Cells[10].SetCellValue("");
|
||||
}
|
||||
|
||||
if (item.end_time != null)
|
||||
{
|
||||
dataRow.Cells[11].SetCellValue(Convert.ToDateTime(item.end_time).ToString("yyyy-MM-dd"));
|
||||
|
|
@ -827,6 +852,7 @@ namespace OpenAuth.App.ServiceApp.DroneSsnydManage
|
|||
|
||||
return response;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -933,19 +959,85 @@ namespace OpenAuth.App.ServiceApp.DroneSsnydManage
|
|||
var project = req.MapTo<DroneSsnyd>();
|
||||
var oldProject = await Repository.GetByIdAsync(project.Id);
|
||||
using var uow = base.UnitWork.CreateContext();
|
||||
var a = 0;
|
||||
var a = false;
|
||||
if (!string.IsNullOrEmpty(req.shpPath))
|
||||
{
|
||||
var geomid = oldProject.geomid;
|
||||
var geometry = new DroneShpData()
|
||||
// 20260203\\2026020315453487200188.zi
|
||||
var zipFilePath = Path.Combine(AppContext.BaseDirectory, req.shpPath);
|
||||
String extractPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "temp");
|
||||
Directory.CreateDirectory(extractPath);
|
||||
// 设置字符编码
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
var gbk = Encoding.GetEncoding("utf-8");
|
||||
using (var archive = ZipFile.OpenRead(zipFilePath))
|
||||
{
|
||||
gid = int.Parse(geomid),
|
||||
geom = req.shpPath
|
||||
};
|
||||
//a = await uow.DroneShpData.AsUpdateable(geometry).IgnoreNullColumns().ExecuteCommandAsync();
|
||||
// 列举ZIP文件中的条目
|
||||
/*foreach (var entry in archive.Entries)
|
||||
{
|
||||
var xxx = gbk.GetString(Encoding.Default.GetBytes(entry.FullName));
|
||||
Console.WriteLine(xxx);
|
||||
}*/
|
||||
|
||||
// 提取ZIP文件中的所有文件到指定目录
|
||||
foreach (var entry in archive.Entries)
|
||||
{
|
||||
if (entry.Name != string.Empty)
|
||||
{
|
||||
// 确保完整路径存在 entry.FullName 是否可以编码
|
||||
var fixedEntryName = entry.FullName.Replace("/", "");
|
||||
var destinationPath = Path.GetFullPath(Path.Combine(extractPath, fixedEntryName));
|
||||
//Console.WriteLine("解压文件路径:" + destinationPath);
|
||||
if (!destinationPath.StartsWith(Path.GetFullPath(extractPath) +
|
||||
Path.DirectorySeparatorChar))
|
||||
{
|
||||
throw new UnauthorizedAccessException("试图提取的文件超出了目标文件夹的路径边界。");
|
||||
}
|
||||
|
||||
// 提取条目到目标路径
|
||||
entry.ExtractToFile(destinationPath, overwrite: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const string searchPattern = "*.shp"; // 设置你想要遍历的文件后缀名
|
||||
var fileName = Directory.GetFiles(extractPath, searchPattern, SearchOption.AllDirectories);
|
||||
if (fileName.Length == 0)
|
||||
{
|
||||
throw new Exception("压缩文件中无shp文件");
|
||||
}
|
||||
|
||||
var shpFileName = fileName[0];
|
||||
var shpFile = Path.Combine(extractPath, shpFileName);
|
||||
foreach (var geometry in Shapefile.ReadAllGeometries(shpFile))
|
||||
{
|
||||
Console.WriteLine(geometry.AsText());
|
||||
var geomid = oldProject.geomid;
|
||||
|
||||
int gid = int.Parse(geomid);
|
||||
a = await uow.DroneShpData
|
||||
.UpdateSetColumnsTrueAsync(b => new DroneShpData()
|
||||
{
|
||||
geom = geometry.AsText(),
|
||||
},
|
||||
b => b.gid == gid);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 递归删除目录和其内容
|
||||
Directory.Delete(extractPath, true);
|
||||
Console.WriteLine("解压目录删除成功");
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
Console.WriteLine("解压目录不能删除: " + ex.Message);
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
Console.WriteLine("没有权限删除解压目录: " + ex.Message);
|
||||
}
|
||||
}
|
||||
// todo 关于只更新图斑如何处理?
|
||||
var b = await uow.DroneSsnyd.AsUpdateable(project).IgnoreNullColumns().ExecuteCommandAsync();
|
||||
if (b > 0)
|
||||
{
|
||||
|
|
@ -1014,13 +1106,15 @@ namespace OpenAuth.App.ServiceApp.DroneSsnydManage
|
|||
}
|
||||
|
||||
#region 项目审核
|
||||
|
||||
/// <summary>
|
||||
/// 项目审核列表
|
||||
/// </summary>
|
||||
/// <param name="pageIndex"></param>
|
||||
/// <param name="pageSize"></param>
|
||||
/// <returns></returns>
|
||||
public PageInfo<List<DroneSsnyd>> GetDronssnydShenheList(string countyid, string streetid,string xiangmuno,int pageIndex, int pageSize)
|
||||
public PageInfo<List<DroneSsnyd>> GetDronssnydShenheList(string countyid, string streetid, string xiangmuno,
|
||||
int pageIndex, int pageSize)
|
||||
{
|
||||
int totalCount = 0;
|
||||
|
||||
|
|
@ -1031,6 +1125,7 @@ namespace OpenAuth.App.ServiceApp.DroneSsnydManage
|
|||
{
|
||||
ids = orgs.Select(r => r.Id.ToString()).ToList();
|
||||
}
|
||||
|
||||
bool issystem = _auth.IsSystem();
|
||||
|
||||
var list = base.Repository.AsQueryable()
|
||||
|
|
@ -1048,6 +1143,7 @@ namespace OpenAuth.App.ServiceApp.DroneSsnydManage
|
|||
Total = totalCount
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -270,7 +270,7 @@ namespace OpenAuth.WebApi
|
|||
//UtilMethods.GetNativeSql(sql, pars);
|
||||
|
||||
//获取无参数化SQL 影响性能只适合调试
|
||||
//Console.WriteLine(UtilMethods.GetSqlString(DbType.PostgreSQL, sql, pars));
|
||||
Console.WriteLine(UtilMethods.GetSqlString(DbType.PostgreSQL, sql, pars));
|
||||
//Console.WriteLine(sql + "\r\n" + db.Utilities.SerializeObject(pars.ToDictionary(it => it.ParameterName, it => it.Value)));
|
||||
//LogHelper.LogInformation(sql + "\r\n" +db.Utilities.SerializeObject(pars.ToDictionary(it => it.ParameterName, it => it.Value)));
|
||||
//Console.WriteLine();
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
"DataProtection": "temp-keys/",
|
||||
"ConnectionStrings": {
|
||||
//"OpenAuthDBContext": "PORT=5432;Database=hopetrycore;HOST=192.168.10.124;PASSWORD=123456;USER ID=postgres;",
|
||||
"OpenAuthDBContext": "PORT=5432;Database=drone_enforcement_hedongnew;HOST=192.168.10.163;PASSWORD=123456;USER ID=postgres;"
|
||||
"OpenAuthDBContext": "PORT=5432;Database=drone_enforcement_hedong2;HOST=192.168.10.163;PASSWORD=123456;USER ID=postgres;"
|
||||
//"OpenAuthDBContext": "PORT=5432;Database=;HOST=192.168.10.131;PASSWORD=123456;USER ID=postgres;"
|
||||
},
|
||||
"AppSetting": {
|
||||
|
|
|
|||
Loading…
Reference in New Issue