Merge remote-tracking branch 'upstream/main' into template
This commit is contained in:
commit
612e82f741
@ -26,7 +26,7 @@
|
|||||||
<PackageReference Include="Magicodes.IE.Excel" Version="2.7.5.1" />
|
<PackageReference Include="Magicodes.IE.Excel" Version="2.7.5.1" />
|
||||||
<PackageReference Include="Magicodes.IE.Pdf" Version="2.7.5.1" />
|
<PackageReference Include="Magicodes.IE.Pdf" Version="2.7.5.1" />
|
||||||
<PackageReference Include="Magicodes.IE.Word" Version="2.7.5.1" />
|
<PackageReference Include="Magicodes.IE.Word" Version="2.7.5.1" />
|
||||||
<PackageReference Include="MailKit" Version="4.7.0" />
|
<PackageReference Include="MailKit" Version="4.7.1" />
|
||||||
<PackageReference Include="NewLife.Redis" Version="5.7.2024.709" />
|
<PackageReference Include="NewLife.Redis" Version="5.7.2024.709" />
|
||||||
<PackageReference Include="Novell.Directory.Ldap.NETStandard" Version="3.6.0" />
|
<PackageReference Include="Novell.Directory.Ldap.NETStandard" Version="3.6.0" />
|
||||||
<PackageReference Include="QRCoder" Version="1.6.0" />
|
<PackageReference Include="QRCoder" Version="1.6.0" />
|
||||||
@ -37,7 +37,7 @@
|
|||||||
<PackageReference Include="SqlSugarCore" Version="5.1.4.162" />
|
<PackageReference Include="SqlSugarCore" Version="5.1.4.162" />
|
||||||
<PackageReference Include="SSH.NET" Version="2024.1.0" />
|
<PackageReference Include="SSH.NET" Version="2024.1.0" />
|
||||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.4.3" />
|
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.4.3" />
|
||||||
<PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1045" />
|
<PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1046" />
|
||||||
<PackageReference Include="UAParser" Version="3.1.47" />
|
<PackageReference Include="UAParser" Version="3.1.47" />
|
||||||
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
|
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@ -100,4 +100,9 @@ public class CacheConst
|
|||||||
/// 系统字典缓存
|
/// 系统字典缓存
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string KeyDict = "sys_dict:";
|
public const string KeyDict = "sys_dict:";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Excel临时文件缓存
|
||||||
|
/// </summary>
|
||||||
|
public const string KeyExcelTemp = "sys_excel_temp:";
|
||||||
}
|
}
|
||||||
@ -7,26 +7,32 @@
|
|||||||
namespace Admin.NET.Core;
|
namespace Admin.NET.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 性别枚举
|
/// 性别枚举(GB/T 2261.1-2003)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Description("性别枚举")]
|
[Description("性别枚举")]
|
||||||
public enum GenderEnum
|
public enum GenderEnum
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 男
|
/// 未知的性别
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Description("男")]
|
[Description("未知的性别")]
|
||||||
|
Unknown = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 男性
|
||||||
|
/// </summary>
|
||||||
|
[Description("男性")]
|
||||||
Male = 1,
|
Male = 1,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 女
|
/// 女性
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Description("女")]
|
[Description("女性")]
|
||||||
Female = 2,
|
Female = 2,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 其他
|
/// 未说明的性别
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Description("其他")]
|
[Description("未说明的性别")]
|
||||||
Other = 3
|
Unspecified = 9
|
||||||
}
|
}
|
||||||
@ -70,7 +70,7 @@ public class EnumToDictJob : IJob
|
|||||||
});
|
});
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
db.Ado.BeginTran();
|
db.BeginTran();
|
||||||
|
|
||||||
if (uSysDictType.Count > 0)
|
if (uSysDictType.Count > 0)
|
||||||
await db.Updateable(uSysDictType).ExecuteCommandAsync(stoppingToken);
|
await db.Updateable(uSysDictType).ExecuteCommandAsync(stoppingToken);
|
||||||
@ -78,11 +78,11 @@ public class EnumToDictJob : IJob
|
|||||||
if (uSysDictData.Count > 0)
|
if (uSysDictData.Count > 0)
|
||||||
await db.Updateable(uSysDictData).ExecuteCommandAsync(stoppingToken);
|
await db.Updateable(uSysDictData).ExecuteCommandAsync(stoppingToken);
|
||||||
|
|
||||||
db.Ado.CommitTran();
|
db.CommitTran();
|
||||||
}
|
}
|
||||||
catch (Exception error)
|
catch (Exception error)
|
||||||
{
|
{
|
||||||
db.Ado.RollbackTran();
|
db.RollbackTran();
|
||||||
Log.Error($"{context.Trigger.Description}更新枚举转换字典入库错误:" + _jsonSerializer.Serialize(error));
|
Log.Error($"{context.Trigger.Description}更新枚举转换字典入库错误:" + _jsonSerializer.Serialize(error));
|
||||||
throw new Exception($"{context.Trigger.Description}更新枚举转换字典入库错误");
|
throw new Exception($"{context.Trigger.Description}更新枚举转换字典入库错误");
|
||||||
}
|
}
|
||||||
@ -123,7 +123,7 @@ public class EnumToDictJob : IJob
|
|||||||
});
|
});
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
db.Ado.BeginTran();
|
db.BeginTran();
|
||||||
|
|
||||||
if (iDictType.Count > 0)
|
if (iDictType.Count > 0)
|
||||||
await db.Insertable(iDictType).ExecuteCommandAsync(stoppingToken);
|
await db.Insertable(iDictType).ExecuteCommandAsync(stoppingToken);
|
||||||
@ -131,11 +131,11 @@ public class EnumToDictJob : IJob
|
|||||||
if (iDictData.Count > 0)
|
if (iDictData.Count > 0)
|
||||||
await db.Insertable(iDictData).ExecuteCommandAsync(stoppingToken);
|
await db.Insertable(iDictData).ExecuteCommandAsync(stoppingToken);
|
||||||
|
|
||||||
db.Ado.CommitTran();
|
db.CommitTran();
|
||||||
}
|
}
|
||||||
catch (Exception error)
|
catch (Exception error)
|
||||||
{
|
{
|
||||||
db.Ado.RollbackTran();
|
db.RollbackTran();
|
||||||
Log.Error($"{context.Trigger.Description}新增枚举转换字典入库错误:" + _jsonSerializer.Serialize(error));
|
Log.Error($"{context.Trigger.Description}新增枚举转换字典入库错误:" + _jsonSerializer.Serialize(error));
|
||||||
throw new Exception($"{context.Trigger.Description}新增枚举转换字典入库错误");
|
throw new Exception($"{context.Trigger.Description}新增枚举转换字典入库错误");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -144,6 +144,10 @@ public class SysCacheService : IDynamicApiController, ISingleton
|
|||||||
[DisplayName("获取缓存值")]
|
[DisplayName("获取缓存值")]
|
||||||
public object GetValue(string key)
|
public object GetValue(string key)
|
||||||
{
|
{
|
||||||
|
// 若Key经过URL编码则进行解码
|
||||||
|
if (Regex.IsMatch(key, @"%[0-9a-fA-F]{2}"))
|
||||||
|
key = HttpUtility.UrlDecode(key);
|
||||||
|
|
||||||
return _cacheProvider.Cache == Cache.Default
|
return _cacheProvider.Cache == Cache.Default
|
||||||
? _cacheProvider.Cache.Get<object>($"{_cacheOptions.Prefix}{key}")
|
? _cacheProvider.Cache.Get<object>($"{_cacheOptions.Prefix}{key}")
|
||||||
: _cacheProvider.Cache.Get<string>($"{_cacheOptions.Prefix}{key}");
|
: _cacheProvider.Cache.Get<string>($"{_cacheOptions.Prefix}{key}");
|
||||||
|
|||||||
@ -121,4 +121,23 @@ public class SysCommonService : IDynamicApiController, ITransient
|
|||||||
}
|
}
|
||||||
return apiList;
|
return apiList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 下载标记错误的临时 Excel(全局)
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[DisplayName("下载标记错误的临时 Excel")]
|
||||||
|
public async Task<IActionResult> DownloadErrorExcelTemp([FromQuery] string fileName = null)
|
||||||
|
{
|
||||||
|
var userId = App.User?.FindFirst(ClaimConst.UserId)?.Value;
|
||||||
|
var resultStream = App.GetRequiredService<SysCacheService>().Get<MemoryStream>(CacheConst.KeyExcelTemp + userId);
|
||||||
|
|
||||||
|
if (resultStream == null)
|
||||||
|
throw Oops.Oh("错误标记文件已过期。");
|
||||||
|
|
||||||
|
return await Task.FromResult(new FileStreamResult(resultStream, "application/octet-stream")
|
||||||
|
{
|
||||||
|
FileDownloadName = $"{(string.IsNullOrEmpty(fileName) ? "错误标记_" + DateTime.Now.ToString("yyyyMMddhhmmss") : fileName)}.xlsx"
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -11,6 +11,7 @@ public class DictDataInput : BaseIdInput
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 状态
|
/// 状态
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Dict("StatusEnum")]
|
||||||
public StatusEnum Status { get; set; }
|
public StatusEnum Status { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,7 @@ public class DictTypeInput : BaseIdInput
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 状态
|
/// 状态
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Dict("StatusEnum")]
|
||||||
public StatusEnum Status { get; set; }
|
public StatusEnum Status { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
||||||
//
|
//
|
||||||
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
||||||
//
|
//
|
||||||
@ -16,8 +16,8 @@ public class SysDictDataService : IDynamicApiController, ITransient
|
|||||||
private readonly SysCacheService _sysCacheService;
|
private readonly SysCacheService _sysCacheService;
|
||||||
private readonly SqlSugarRepository<SysDictData> _sysDictDataRep;
|
private readonly SqlSugarRepository<SysDictData> _sysDictDataRep;
|
||||||
|
|
||||||
public SysDictDataService(SqlSugarRepository<SysDictData> sysDictDataRep,
|
public SysDictDataService(SqlSugarRepository<SysDictData> sysDictDataRep
|
||||||
SysCacheService sysCacheService)
|
, SysCacheService sysCacheService)
|
||||||
{
|
{
|
||||||
_sysDictDataRep = sysDictDataRep;
|
_sysDictDataRep = sysDictDataRep;
|
||||||
_sysCacheService = sysCacheService;
|
_sysCacheService = sysCacheService;
|
||||||
@ -60,8 +60,7 @@ public class SysDictDataService : IDynamicApiController, ITransient
|
|||||||
public async Task AddDictData(AddDictDataInput input)
|
public async Task AddDictData(AddDictDataInput input)
|
||||||
{
|
{
|
||||||
var isExist = await _sysDictDataRep.IsAnyAsync(u => u.Code == input.Code && u.DictTypeId == input.DictTypeId);
|
var isExist = await _sysDictDataRep.IsAnyAsync(u => u.Code == input.Code && u.DictTypeId == input.DictTypeId);
|
||||||
if (isExist)
|
if (isExist) throw Oops.Oh(ErrorCodeEnum.D3003);
|
||||||
throw Oops.Oh(ErrorCodeEnum.D3003);
|
|
||||||
|
|
||||||
await _sysDictDataRep.InsertAsync(input.Adapt<SysDictData>());
|
await _sysDictDataRep.InsertAsync(input.Adapt<SysDictData>());
|
||||||
}
|
}
|
||||||
@ -98,9 +97,7 @@ public class SysDictDataService : IDynamicApiController, ITransient
|
|||||||
[DisplayName("删除字典值")]
|
[DisplayName("删除字典值")]
|
||||||
public async Task DeleteDictData(DeleteDictDataInput input)
|
public async Task DeleteDictData(DeleteDictDataInput input)
|
||||||
{
|
{
|
||||||
var dictData = await _sysDictDataRep.GetFirstAsync(u => u.Id == input.Id);
|
var dictData = await _sysDictDataRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D3004);
|
||||||
if (dictData == null)
|
|
||||||
throw Oops.Oh(ErrorCodeEnum.D3004);
|
|
||||||
|
|
||||||
var dictTypeCode = await _sysDictDataRep.AsQueryable().Where(u => u.DictTypeId == dictData.Id).Select(u => u.DictType.Code).FirstAsync();
|
var dictTypeCode = await _sysDictDataRep.AsQueryable().Where(u => u.DictTypeId == dictData.Id).Select(u => u.DictType.Code).FirstAsync();
|
||||||
_sysCacheService.Remove($"{CacheConst.KeyDict}{dictTypeCode}");
|
_sysCacheService.Remove($"{CacheConst.KeyDict}{dictTypeCode}");
|
||||||
@ -128,18 +125,13 @@ public class SysDictDataService : IDynamicApiController, ITransient
|
|||||||
[DisplayName("修改字典值状态")]
|
[DisplayName("修改字典值状态")]
|
||||||
public async Task SetStatus(DictDataInput input)
|
public async Task SetStatus(DictDataInput input)
|
||||||
{
|
{
|
||||||
var dictData = await _sysDictDataRep.GetFirstAsync(u => u.Id == input.Id);
|
var dictData = await _sysDictDataRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D3004);
|
||||||
if (dictData == null)
|
|
||||||
throw Oops.Oh(ErrorCodeEnum.D3004);
|
|
||||||
|
|
||||||
if (!Enum.IsDefined(typeof(StatusEnum), input.Status))
|
|
||||||
throw Oops.Oh(ErrorCodeEnum.D3005);
|
|
||||||
|
|
||||||
var dictTypeCode = await _sysDictDataRep.AsQueryable().Where(u => u.DictTypeId == dictData.Id).Select(u => u.DictType.Code).FirstAsync();
|
var dictTypeCode = await _sysDictDataRep.AsQueryable().Where(u => u.DictTypeId == dictData.Id).Select(u => u.DictType.Code).FirstAsync();
|
||||||
_sysCacheService.Remove($"{CacheConst.KeyDict}{dictTypeCode}");
|
_sysCacheService.Remove($"{CacheConst.KeyDict}{dictTypeCode}");
|
||||||
|
|
||||||
dictData.Status = input.Status;
|
dictData.Status = input.Status;
|
||||||
await _sysDictDataRep.UpdateAsync(dictData);
|
await _sysDictDataRep.AsUpdateable(dictData).UpdateColumns(u => new { u.Status }, true).ExecuteCommandAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -156,13 +148,9 @@ public class SysDictDataService : IDynamicApiController, ITransient
|
|||||||
if (dictDataList == null)
|
if (dictDataList == null)
|
||||||
{
|
{
|
||||||
dictDataList = await _sysDictDataRep.AsQueryable()
|
dictDataList = await _sysDictDataRep.AsQueryable()
|
||||||
.Where(u => u.DictTypeId == dictTypeId)
|
.Where(u => u.DictTypeId == dictTypeId).OrderBy(u => new { u.OrderNo, u.Code }).ToListAsync();
|
||||||
.OrderBy(u => new { u.OrderNo, u.Code })
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
_sysCacheService.Set($"{CacheConst.KeyDict}{dictType.Code}", dictDataList);
|
_sysCacheService.Set($"{CacheConst.KeyDict}{dictType.Code}", dictDataList);
|
||||||
}
|
}
|
||||||
|
|
||||||
return dictDataList;
|
return dictDataList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -60,10 +60,7 @@ public class SysDictTypeService : IDynamicApiController, ITransient
|
|||||||
[DisplayName("获取字典类型-值列表")]
|
[DisplayName("获取字典类型-值列表")]
|
||||||
public async Task<List<SysDictData>> GetDataList([FromQuery] GetDataDictTypeInput input)
|
public async Task<List<SysDictData>> GetDataList([FromQuery] GetDataDictTypeInput input)
|
||||||
{
|
{
|
||||||
var dictType = await _sysDictTypeRep.GetFirstAsync(u => u.Code == input.Code);
|
var dictType = await _sysDictTypeRep.GetFirstAsync(u => u.Code == input.Code) ?? throw Oops.Oh(ErrorCodeEnum.D3000);
|
||||||
if (dictType == null)
|
|
||||||
throw Oops.Oh(ErrorCodeEnum.D3000);
|
|
||||||
|
|
||||||
return await _sysDictDataService.GetDictDataListByDictTypeId(dictType.Id);
|
return await _sysDictDataService.GetDictDataListByDictTypeId(dictType.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,8 +74,7 @@ public class SysDictTypeService : IDynamicApiController, ITransient
|
|||||||
public async Task AddDictType(AddDictTypeInput input)
|
public async Task AddDictType(AddDictTypeInput input)
|
||||||
{
|
{
|
||||||
var isExist = await _sysDictTypeRep.IsAnyAsync(u => u.Code == input.Code);
|
var isExist = await _sysDictTypeRep.IsAnyAsync(u => u.Code == input.Code);
|
||||||
if (isExist)
|
if (isExist) throw Oops.Oh(ErrorCodeEnum.D3001);
|
||||||
throw Oops.Oh(ErrorCodeEnum.D3001);
|
|
||||||
|
|
||||||
await _sysDictTypeRep.InsertAsync(input.Adapt<SysDictType>());
|
await _sysDictTypeRep.InsertAsync(input.Adapt<SysDictType>());
|
||||||
}
|
}
|
||||||
@ -94,12 +90,10 @@ public class SysDictTypeService : IDynamicApiController, ITransient
|
|||||||
public async Task UpdateDictType(UpdateDictTypeInput input)
|
public async Task UpdateDictType(UpdateDictTypeInput input)
|
||||||
{
|
{
|
||||||
var isExist = await _sysDictTypeRep.IsAnyAsync(u => u.Id == input.Id);
|
var isExist = await _sysDictTypeRep.IsAnyAsync(u => u.Id == input.Id);
|
||||||
if (!isExist)
|
if (!isExist) throw Oops.Oh(ErrorCodeEnum.D3000);
|
||||||
throw Oops.Oh(ErrorCodeEnum.D3000);
|
|
||||||
|
|
||||||
isExist = await _sysDictTypeRep.IsAnyAsync(u => u.Code == input.Code && u.Id != input.Id);
|
isExist = await _sysDictTypeRep.IsAnyAsync(u => u.Code == input.Code && u.Id != input.Id);
|
||||||
if (isExist)
|
if (isExist) throw Oops.Oh(ErrorCodeEnum.D3001);
|
||||||
throw Oops.Oh(ErrorCodeEnum.D3001);
|
|
||||||
|
|
||||||
_sysCacheService.Remove($"{CacheConst.KeyDict}{input.Code}");
|
_sysCacheService.Remove($"{CacheConst.KeyDict}{input.Code}");
|
||||||
await _sysDictTypeRep.UpdateAsync(input.Adapt<SysDictType>());
|
await _sysDictTypeRep.UpdateAsync(input.Adapt<SysDictType>());
|
||||||
@ -115,9 +109,7 @@ public class SysDictTypeService : IDynamicApiController, ITransient
|
|||||||
[DisplayName("删除字典类型")]
|
[DisplayName("删除字典类型")]
|
||||||
public async Task DeleteDictType(DeleteDictTypeInput input)
|
public async Task DeleteDictType(DeleteDictTypeInput input)
|
||||||
{
|
{
|
||||||
var dictType = await _sysDictTypeRep.GetFirstAsync(u => u.Id == input.Id);
|
var dictType = await _sysDictTypeRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D3000);
|
||||||
if (dictType == null)
|
|
||||||
throw Oops.Oh(ErrorCodeEnum.D3000);
|
|
||||||
|
|
||||||
// 删除字典值
|
// 删除字典值
|
||||||
await _sysDictTypeRep.DeleteAsync(dictType);
|
await _sysDictTypeRep.DeleteAsync(dictType);
|
||||||
@ -144,17 +136,12 @@ public class SysDictTypeService : IDynamicApiController, ITransient
|
|||||||
[DisplayName("修改字典类型状态")]
|
[DisplayName("修改字典类型状态")]
|
||||||
public async Task SetStatus(DictTypeInput input)
|
public async Task SetStatus(DictTypeInput input)
|
||||||
{
|
{
|
||||||
var dictType = await _sysDictTypeRep.GetFirstAsync(u => u.Id == input.Id);
|
var dictType = await _sysDictTypeRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D3000);
|
||||||
if (dictType == null)
|
|
||||||
throw Oops.Oh(ErrorCodeEnum.D3000);
|
|
||||||
|
|
||||||
if (!Enum.IsDefined(typeof(StatusEnum), input.Status))
|
|
||||||
throw Oops.Oh(ErrorCodeEnum.D3005);
|
|
||||||
|
|
||||||
_sysCacheService.Remove($"{CacheConst.KeyDict}{dictType.Code}");
|
_sysCacheService.Remove($"{CacheConst.KeyDict}{dictType.Code}");
|
||||||
|
|
||||||
dictType.Status = input.Status;
|
dictType.Status = input.Status;
|
||||||
await _sysDictTypeRep.UpdateAsync(dictType);
|
await _sysDictTypeRep.AsUpdateable(dictType).UpdateColumns(u => new { u.Status }, true).ExecuteCommandAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -49,7 +49,7 @@ public class SysEnumService : IDynamicApiController, ITransient
|
|||||||
{
|
{
|
||||||
string description = type.Name;
|
string description = type.Name;
|
||||||
var attrs = type.GetCustomAttributes(typeof(DescriptionAttribute), false);
|
var attrs = type.GetCustomAttributes(typeof(DescriptionAttribute), false);
|
||||||
if (attrs.Any())
|
if (attrs.Length != 0)
|
||||||
{
|
{
|
||||||
var att = ((DescriptionAttribute[])attrs)[0];
|
var att = ((DescriptionAttribute[])attrs)[0];
|
||||||
description = att.Description;
|
description = att.Description;
|
||||||
|
|||||||
@ -42,14 +42,14 @@ public class DbJobPersistence : IJobPersistence
|
|||||||
var jobBuilder = schedulerBuilder.GetJobBuilder();
|
var jobBuilder = schedulerBuilder.GetJobBuilder();
|
||||||
|
|
||||||
// 加载数据库数据
|
// 加载数据库数据
|
||||||
var dbDetail = await db.Queryable<SysJobDetail>().FirstAsync(u => u.JobId == jobBuilder.JobId);
|
var dbDetail = await db.Queryable<SysJobDetail>().FirstAsync(u => u.JobId == jobBuilder.JobId, stoppingToken);
|
||||||
if (dbDetail == null) continue;
|
if (dbDetail == null) continue;
|
||||||
|
|
||||||
// 同步数据库数据
|
// 同步数据库数据
|
||||||
jobBuilder.LoadFrom(dbDetail);
|
jobBuilder.LoadFrom(dbDetail);
|
||||||
|
|
||||||
// 获取作业的所有数据库的触发器
|
// 获取作业的所有数据库的触发器
|
||||||
var dbTriggers = await db.Queryable<SysJobTrigger>().Where(u => u.JobId == jobBuilder.JobId).ToListAsync();
|
var dbTriggers = await db.Queryable<SysJobTrigger>().Where(u => u.JobId == jobBuilder.JobId).ToListAsync(stoppingToken);
|
||||||
// 遍历所有作业触发器
|
// 遍历所有作业触发器
|
||||||
foreach (var (_, triggerBuilder) in schedulerBuilder.GetEnumerable())
|
foreach (var (_, triggerBuilder) in schedulerBuilder.GetEnumerable())
|
||||||
{
|
{
|
||||||
@ -73,24 +73,16 @@ public class DbJobPersistence : IJobPersistence
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取数据库所有通过脚本创建的作业
|
// 获取数据库所有通过脚本创建的作业
|
||||||
var allDbScriptJobs = await db.Queryable<SysJobDetail>().Where(u => u.CreateType != JobCreateTypeEnum.BuiltIn).ToListAsync();
|
var allDbScriptJobs = await db.Queryable<SysJobDetail>().Where(u => u.CreateType != JobCreateTypeEnum.BuiltIn).ToListAsync(stoppingToken);
|
||||||
foreach (var dbDetail in allDbScriptJobs)
|
foreach (var dbDetail in allDbScriptJobs)
|
||||||
{
|
{
|
||||||
// 动态创建作业
|
// 动态创建作业
|
||||||
Type jobType;
|
Type jobType = dbDetail.CreateType switch
|
||||||
switch (dbDetail.CreateType)
|
|
||||||
{
|
{
|
||||||
case JobCreateTypeEnum.Script:
|
JobCreateTypeEnum.Script => dynamicJobCompiler.BuildJob(dbDetail.ScriptCode),
|
||||||
jobType = dynamicJobCompiler.BuildJob(dbDetail.ScriptCode);
|
JobCreateTypeEnum.Http => typeof(HttpJob),
|
||||||
break;
|
_ => throw new NotSupportedException(),
|
||||||
|
};
|
||||||
case JobCreateTypeEnum.Http:
|
|
||||||
jobType = typeof(HttpJob);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 动态构建的 jobType 的程序集名称为随机名称,需重新设置
|
// 动态构建的 jobType 的程序集名称为随机名称,需重新设置
|
||||||
dbDetail.AssemblyName = jobType.Assembly.FullName!.Split(',')[0];
|
dbDetail.AssemblyName = jobType.Assembly.FullName!.Split(',')[0];
|
||||||
@ -131,25 +123,23 @@ public class DbJobPersistence : IJobPersistence
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task OnChangedAsync(PersistenceContext context)
|
public async Task OnChangedAsync(PersistenceContext context)
|
||||||
{
|
{
|
||||||
using (var scope = _serviceScopeFactory.CreateScope())
|
using var scope = _serviceScopeFactory.CreateScope();
|
||||||
|
var db = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>().CopyNew();
|
||||||
|
|
||||||
|
var jobDetail = context.JobDetail.Adapt<SysJobDetail>();
|
||||||
|
switch (context.Behavior)
|
||||||
{
|
{
|
||||||
var db = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>().CopyNew();
|
case PersistenceBehavior.Appended:
|
||||||
|
await db.Insertable(jobDetail).ExecuteCommandAsync();
|
||||||
|
break;
|
||||||
|
|
||||||
var jobDetail = context.JobDetail.Adapt<SysJobDetail>();
|
case PersistenceBehavior.Updated:
|
||||||
switch (context.Behavior)
|
await db.Updateable(jobDetail).WhereColumns(u => new { u.JobId }).IgnoreColumns(u => new { u.Id, u.CreateType, u.ScriptCode }).ExecuteCommandAsync();
|
||||||
{
|
break;
|
||||||
case PersistenceBehavior.Appended:
|
|
||||||
await db.Insertable(jobDetail).ExecuteCommandAsync();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PersistenceBehavior.Updated:
|
case PersistenceBehavior.Removed:
|
||||||
await db.Updateable(jobDetail).WhereColumns(u => new { u.JobId }).IgnoreColumns(u => new { u.Id, u.CreateType, u.ScriptCode }).ExecuteCommandAsync();
|
await db.Deleteable<SysJobDetail>().Where(u => u.JobId == jobDetail.JobId).ExecuteCommandAsync();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PersistenceBehavior.Removed:
|
|
||||||
await db.Deleteable<SysJobDetail>().Where(u => u.JobId == jobDetail.JobId).ExecuteCommandAsync();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,25 +150,23 @@ public class DbJobPersistence : IJobPersistence
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task OnTriggerChangedAsync(PersistenceTriggerContext context)
|
public async Task OnTriggerChangedAsync(PersistenceTriggerContext context)
|
||||||
{
|
{
|
||||||
using (var scope = _serviceScopeFactory.CreateScope())
|
using var scope = _serviceScopeFactory.CreateScope();
|
||||||
|
var db = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>().CopyNew();
|
||||||
|
|
||||||
|
var jobTrigger = context.Trigger.Adapt<SysJobTrigger>();
|
||||||
|
switch (context.Behavior)
|
||||||
{
|
{
|
||||||
var db = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>().CopyNew();
|
case PersistenceBehavior.Appended:
|
||||||
|
await db.Insertable(jobTrigger).ExecuteCommandAsync();
|
||||||
|
break;
|
||||||
|
|
||||||
var jobTrigger = context.Trigger.Adapt<SysJobTrigger>();
|
case PersistenceBehavior.Updated:
|
||||||
switch (context.Behavior)
|
await db.Updateable(jobTrigger).WhereColumns(u => new { u.TriggerId, u.JobId }).IgnoreColumns(u => new { u.Id }).ExecuteCommandAsync();
|
||||||
{
|
break;
|
||||||
case PersistenceBehavior.Appended:
|
|
||||||
await db.Insertable(jobTrigger).ExecuteCommandAsync();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PersistenceBehavior.Updated:
|
case PersistenceBehavior.Removed:
|
||||||
await db.Updateable(jobTrigger).WhereColumns(u => new { u.TriggerId, u.JobId }).IgnoreColumns(u => new { u.Id }).ExecuteCommandAsync();
|
await db.Deleteable<SysJobTrigger>().Where(u => u.TriggerId == jobTrigger.TriggerId && u.JobId == jobTrigger.JobId).ExecuteCommandAsync();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PersistenceBehavior.Removed:
|
|
||||||
await db.Deleteable<SysJobTrigger>().Where(u => u.TriggerId == jobTrigger.TriggerId && u.JobId == jobTrigger.JobId).ExecuteCommandAsync();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,12 +177,10 @@ public class DbJobPersistence : IJobPersistence
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task OnExecutionRecordAsync(PersistenceExecutionRecordContext context)
|
public async Task OnExecutionRecordAsync(PersistenceExecutionRecordContext context)
|
||||||
{
|
{
|
||||||
using (var scope = _serviceScopeFactory.CreateScope())
|
using var scope = _serviceScopeFactory.CreateScope();
|
||||||
{
|
var db = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>().CopyNew();
|
||||||
var db = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>().CopyNew();
|
|
||||||
|
|
||||||
var jobTriggerRecord = context.Timeline.Adapt<SysJobTriggerRecord>();
|
var jobTriggerRecord = context.Timeline.Adapt<SysJobTriggerRecord>();
|
||||||
await db.Insertable(jobTriggerRecord).ExecuteCommandAsync();
|
await db.Insertable(jobTriggerRecord).ExecuteCommandAsync();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
||||||
//
|
//
|
||||||
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
||||||
//
|
//
|
||||||
|
|||||||
@ -59,7 +59,7 @@ public class SysMenuService : IDynamicApiController, ITransient
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 删除登录菜单树里面的按钮
|
/// 删除登录菜单树里面的按钮
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void DeleteBtnFromMenuTree(List<SysMenu> menuList)
|
private static void DeleteBtnFromMenuTree(List<SysMenu> menuList)
|
||||||
{
|
{
|
||||||
if (menuList == null) return;
|
if (menuList == null) return;
|
||||||
for (var i = menuList.Count - 1; i >= 0; i--)
|
for (var i = menuList.Count - 1; i >= 0; i--)
|
||||||
|
|||||||
@ -27,7 +27,7 @@ public class SysRoleApiService : ITransient
|
|||||||
{
|
{
|
||||||
await _sysRoleApiRep.DeleteAsync(u => u.RoleId == input.Id);
|
await _sysRoleApiRep.DeleteAsync(u => u.RoleId == input.Id);
|
||||||
|
|
||||||
var roleApis = input.ApiList.Select(u => new SysRoleApi
|
var roleApis = input.ApiList.Where(u => !string.IsNullOrWhiteSpace(u)).Select(u => new SysRoleApi
|
||||||
{
|
{
|
||||||
RoleId = input.Id,
|
RoleId = input.Id,
|
||||||
Route = u
|
Route = u
|
||||||
|
|||||||
@ -115,4 +115,28 @@ public class AddSubscribeMessageTemplateInput
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[Required(ErrorMessage = "服务场景描述不能为空")]
|
[Required(ErrorMessage = "服务场景描述不能为空")]
|
||||||
public string SceneDescription { get; set; }
|
public string SceneDescription { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证签名
|
||||||
|
/// </summary>
|
||||||
|
public class VerifySignatureInput
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 签名
|
||||||
|
/// </summary>
|
||||||
|
public string signature { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 时间戳
|
||||||
|
/// </summary>
|
||||||
|
public string timestamp { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 随机数
|
||||||
|
/// </summary>
|
||||||
|
public string nonce { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 随机字符串
|
||||||
|
/// </summary>
|
||||||
|
public string echostr { get; set; }
|
||||||
}
|
}
|
||||||
@ -114,6 +114,25 @@ public class SysWxOpenService : IDynamicApiController, ITransient
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证签名 🔖
|
||||||
|
/// </summary>
|
||||||
|
[AllowAnonymous]
|
||||||
|
[NonUnify]
|
||||||
|
[ApiDescriptionSettings(Name = "VerifySignature"), HttpGet]
|
||||||
|
[DisplayName("验证签名")]
|
||||||
|
public string VerifySignature([FromQuery] VerifySignatureInput input)
|
||||||
|
{
|
||||||
|
|
||||||
|
bool valid = _wechatApiClient.VerifyEventSignatureForEcho(input.timestamp, input.nonce, input.signature);
|
||||||
|
if (!valid)
|
||||||
|
{
|
||||||
|
return "fail";
|
||||||
|
}
|
||||||
|
|
||||||
|
return input.echostr;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取订阅消息模板列表 🔖
|
/// 获取订阅消息模板列表 🔖
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -49,12 +49,12 @@ public static class CommonUtil
|
|||||||
// 代理模式:获取真正的本机地址
|
// 代理模式:获取真正的本机地址
|
||||||
// X-Original-Host=原始请求
|
// X-Original-Host=原始请求
|
||||||
// X-Forwarded-Server=从哪里转发过来
|
// X-Forwarded-Server=从哪里转发过来
|
||||||
if (App.HttpContext.Request.Headers.ContainsKey("Origin")) // 配置成完整的路径如(结尾不要带"/"),比如 https://www.abc.com
|
if (App.HttpContext.Request.Headers.TryGetValue("Origin", out Microsoft.Extensions.Primitives.StringValues value1)) // 配置成完整的路径如(结尾不要带"/"),比如 https://www.abc.com
|
||||||
result = $"{App.HttpContext.Request.Headers["Origin"]}";
|
result = $"{value1}";
|
||||||
else if (App.HttpContext.Request.Headers.ContainsKey("X-Original")) // 配置成完整的路径如(结尾不要带"/"),比如 https://www.abc.com
|
else if (App.HttpContext.Request.Headers.TryGetValue("X-Original", out Microsoft.Extensions.Primitives.StringValues value2)) // 配置成完整的路径如(结尾不要带"/"),比如 https://www.abc.com
|
||||||
result = $"{App.HttpContext.Request.Headers["X-Original"]}";
|
result = $"{value2}";
|
||||||
else if (App.HttpContext.Request.Headers.ContainsKey("X-Original-Host"))
|
else if (App.HttpContext.Request.Headers.TryGetValue("X-Original-Host", out Microsoft.Extensions.Primitives.StringValues value3))
|
||||||
result = $"{App.HttpContext.Request.Scheme}://{App.HttpContext.Request.Headers["X-Original-Host"]}";
|
result = $"{App.HttpContext.Request.Scheme}://{value3}";
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,7 +107,7 @@ public static class CommonUtil
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static async Task<IActionResult> ExportExcelTemplate<T>(string fileName = null) where T : class, new()
|
public static async Task<IActionResult> ExportExcelTemplate<T>(string fileName = null) where T : class, new()
|
||||||
{
|
{
|
||||||
IImporter importer = new ExcelImporter();
|
var importer = new ExcelImporter();
|
||||||
var res = await importer.GenerateTemplateBytes<T>();
|
var res = await importer.GenerateTemplateBytes<T>();
|
||||||
|
|
||||||
return new FileContentResult(res, "application/octet-stream") { FileDownloadName = $"{(string.IsNullOrEmpty(fileName) ? typeof(T).Name : fileName)}.xlsx" };
|
return new FileContentResult(res, "application/octet-stream") { FileDownloadName = $"{(string.IsNullOrEmpty(fileName) ? typeof(T).Name : fileName)}.xlsx" };
|
||||||
@ -152,9 +152,9 @@ public static class CommonUtil
|
|||||||
}
|
}
|
||||||
|
|
||||||
var map = dict.Value.Item1;
|
var map = dict.Value.Item1;
|
||||||
if (map != null && map.ContainsKey(sourceVal))
|
if (map != null && map.TryGetValue(sourceVal, out string value))
|
||||||
{
|
{
|
||||||
var newVal = map[sourceVal];
|
var newVal = value;
|
||||||
targeProp.SetValue(newData, newVal);
|
targeProp.SetValue(newData, newVal);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -190,7 +190,7 @@ public static class CommonUtil
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static async Task<ICollection<T>> ImportExcelData<T>([Required] IFormFile file) where T : class, new()
|
public static async Task<ICollection<T>> ImportExcelData<T>([Required] IFormFile file) where T : class, new()
|
||||||
{
|
{
|
||||||
IImporter importer = new ExcelImporter();
|
var importer = new ExcelImporter();
|
||||||
var res = await importer.Import<T>(file.OpenReadStream());
|
var res = await importer.Import<T>(file.OpenReadStream());
|
||||||
var message = string.Empty;
|
var message = string.Empty;
|
||||||
if (res.HasError)
|
if (res.HasError)
|
||||||
@ -203,7 +203,46 @@ public static class CommonUtil
|
|||||||
foreach (var item in drErrorInfo.FieldErrors)
|
foreach (var item in drErrorInfo.FieldErrors)
|
||||||
message += $"\r\n{item.Key}:{item.Value}(文件第{drErrorInfo.RowIndex}行)";
|
message += $"\r\n{item.Key}:{item.Value}(文件第{drErrorInfo.RowIndex}行)";
|
||||||
}
|
}
|
||||||
message += "字段缺失:" + string.Join(",", res.TemplateErrors.Select(m => m.RequireColumnName).ToList());
|
message += "\r\n字段缺失:" + string.Join(",", res.TemplateErrors.Select(m => m.RequireColumnName).ToList());
|
||||||
|
throw Oops.Oh("导入异常:" + message);
|
||||||
|
}
|
||||||
|
return res.Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 导入Excel数据并错误标记
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="file"></param>
|
||||||
|
/// <param name="importResultCallback"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static async Task<ICollection<T>> ImportExcelData<T>([Required] IFormFile file, Func<ImportResult<T>, ImportResult<T>> importResultCallback = null) where T : class, new()
|
||||||
|
{
|
||||||
|
var importer = new ExcelImporter();
|
||||||
|
var resultStream = new MemoryStream();
|
||||||
|
var res = await importer.Import<T>(file.OpenReadStream(), resultStream, importResultCallback);
|
||||||
|
resultStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
var userId = App.User?.FindFirst(ClaimConst.UserId)?.Value;
|
||||||
|
|
||||||
|
App.GetRequiredService<SysCacheService>().Remove(CacheConst.KeyExcelTemp + userId);
|
||||||
|
App.GetRequiredService<SysCacheService>().Set(CacheConst.KeyExcelTemp + userId, resultStream, TimeSpan.FromMinutes(5));
|
||||||
|
|
||||||
|
var message = string.Empty;
|
||||||
|
if (res.HasError)
|
||||||
|
{
|
||||||
|
if (res.Exception != null)
|
||||||
|
message += $"\r\n{res.Exception.Message}";
|
||||||
|
foreach (DataRowErrorInfo drErrorInfo in res.RowErrors)
|
||||||
|
{
|
||||||
|
int rowNum = drErrorInfo.RowIndex;
|
||||||
|
foreach (var item in drErrorInfo.FieldErrors)
|
||||||
|
message += $"\r\n{item.Key}:{item.Value}(文件第{drErrorInfo.RowIndex}行)";
|
||||||
|
}
|
||||||
|
if (res.TemplateErrors.Count > 0)
|
||||||
|
message += "\r\n字段缺失:" + string.Join(",", res.TemplateErrors.Select(m => m.RequireColumnName).ToList());
|
||||||
|
|
||||||
|
if (message.Length > 200)
|
||||||
|
message = string.Concat(message.AsSpan(0, 200), "...\r\n异常过多,建议下载错误标记文件查看详细错误信息并重新导入。");
|
||||||
throw Oops.Oh("导入异常:" + message);
|
throw Oops.Oh("导入异常:" + message);
|
||||||
}
|
}
|
||||||
return res.Data;
|
return res.Data;
|
||||||
@ -300,7 +339,7 @@ public static class CommonUtil
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
propMappings.Add(propertyInfo.Name, new Tuple<Dictionary<string, object>, PropertyInfo, PropertyInfo>(
|
propMappings.Add(propertyInfo.Name, new Tuple<Dictionary<string, object>, PropertyInfo, PropertyInfo>(
|
||||||
null, propertyInfo, tTargetProps.ContainsKey(propertyInfo.Name) ? tTargetProps[propertyInfo.Name] : null));
|
null, propertyInfo, tTargetProps.TryGetValue(propertyInfo.Name, out PropertyInfo value) ? value : null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,34 +381,34 @@ public static class CommonUtil
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
propMappings.Add(propertyInfo.Name, new Tuple<Dictionary<object, string>, PropertyInfo, PropertyInfo>(
|
propMappings.Add(propertyInfo.Name, new Tuple<Dictionary<object, string>, PropertyInfo, PropertyInfo>(
|
||||||
null, sourceProps.ContainsKey(propertyInfo.Name) ? sourceProps[propertyInfo.Name] : null, propertyInfo));
|
null, sourceProps.TryGetValue(propertyInfo.Name, out PropertyInfo value) ? value : null, propertyInfo));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return propMappings;
|
return propMappings;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
///// <summary>
|
||||||
/// 获取属性映射
|
///// 获取属性映射
|
||||||
/// </summary>
|
///// </summary>
|
||||||
/// <typeparam name="TTarget"></typeparam>
|
///// <typeparam name="TTarget"></typeparam>
|
||||||
/// <returns>整理导入对象的 属性名称, 字典数据,原属性信息,目标属性信息 </returns>
|
///// <returns>整理导入对象的 属性名称, 字典数据,原属性信息,目标属性信息 </returns>
|
||||||
private static Dictionary<string, Tuple<string, string>> GetExportDicttMap<TTarget>() where TTarget : new()
|
//private static Dictionary<string, Tuple<string, string>> GetExportDicttMap<TTarget>() where TTarget : new()
|
||||||
{
|
//{
|
||||||
// 整理导入对象的属性名称,目标属性名,字典Code
|
// // 整理导入对象的属性名称,目标属性名,字典Code
|
||||||
var propMappings = new Dictionary<string, Tuple<string, string>>();
|
// var propMappings = new Dictionary<string, Tuple<string, string>>();
|
||||||
var tTargetProps = typeof(TTarget).GetProperties();
|
// var tTargetProps = typeof(TTarget).GetProperties();
|
||||||
foreach (var propertyInfo in tTargetProps)
|
// foreach (var propertyInfo in tTargetProps)
|
||||||
{
|
// {
|
||||||
var attrs = propertyInfo.GetCustomAttribute<ImportDictAttribute>();
|
// var attrs = propertyInfo.GetCustomAttribute<ImportDictAttribute>();
|
||||||
if (attrs != null && !string.IsNullOrWhiteSpace(attrs.TypeCode))
|
// if (attrs != null && !string.IsNullOrWhiteSpace(attrs.TypeCode))
|
||||||
{
|
// {
|
||||||
propMappings.Add(propertyInfo.Name, new Tuple<string, string>(attrs.TargetPropName, attrs.TypeCode));
|
// propMappings.Add(propertyInfo.Name, new Tuple<string, string>(attrs.TargetPropName, attrs.TypeCode));
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
return propMappings;
|
// return propMappings;
|
||||||
}
|
//}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 解析IP地址
|
/// 解析IP地址
|
||||||
|
|||||||
@ -154,11 +154,7 @@
|
|||||||
@:<template #row_@(@column.LowerPropertyName)="{ row }">
|
@:<template #row_@(@column.LowerPropertyName)="{ row }">
|
||||||
@:<el-tag :type="dv('@(@column.DictTypeCode)', row.@(@column.LowerPropertyName))?.tagType"> {{dv('@(@column.DictTypeCode)', row.@column.LowerPropertyName)?.name}}</el-tag>
|
@:<el-tag :type="dv('@(@column.DictTypeCode)', row.@(@column.LowerPropertyName))?.tagType"> {{dv('@(@column.DictTypeCode)', row.@column.LowerPropertyName)?.name}}</el-tag>
|
||||||
@:</template>
|
@:</template>
|
||||||
} else if(@column.EffectType == "DatePicker") {
|
}
|
||||||
@:<template #row_@(@column.LowerPropertyName)="{ row }">
|
|
||||||
@:<span>{{ formatDate(new Date(row.@(@column.LowerPropertyName)), 'YYYY-mm-dd HH:MM:SS') }}</span>
|
|
||||||
@:</template>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<template #row_record="{ row }">
|
<template #row_record="{ row }">
|
||||||
@ -296,7 +292,7 @@ const options = useVxeTable<@(@Model.ClassName)>(
|
|||||||
} else if(@column.EffectType == "EnumSelector") {
|
} else if(@column.EffectType == "EnumSelector") {
|
||||||
@:{ field: '@column.LowerPropertyName', title: '@column.ColumnComment', minWidth: 100, showOverflow: 'tooltip', slots: { default: 'row_@column.LowerPropertyName' }, @whethersortable },
|
@:{ field: '@column.LowerPropertyName', title: '@column.ColumnComment', minWidth: 100, showOverflow: 'tooltip', slots: { default: 'row_@column.LowerPropertyName' }, @whethersortable },
|
||||||
} else if(@column.EffectType == "DatePicker") {
|
} else if(@column.EffectType == "DatePicker") {
|
||||||
@:{ field: '@column.LowerPropertyName', title: '@column.ColumnComment', minWidth: 100, showOverflow: 'tooltip', slots: { default: 'row_@column.LowerPropertyName' }, @whethersortable },
|
@:{ field: '@column.LowerPropertyName', title: '@column.ColumnComment', minWidth: 100, showOverflow: 'tooltip', formatter: ({ cellValue }) => formatDate(new Date(cellValue), 'YYYY-mm-dd HH:MM:SS'), @whethersortable },
|
||||||
} else {
|
} else {
|
||||||
@:{ field: '@column.LowerPropertyName', title: '@column.ColumnComment', minWidth: 100, showOverflow: 'tooltip', @whethersortable},
|
@:{ field: '@column.LowerPropertyName', title: '@column.ColumnComment', minWidth: 100, showOverflow: 'tooltip', @whethersortable},
|
||||||
}
|
}
|
||||||
@ -342,7 +338,7 @@ const handleQuery = async (reset = false) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 重置操作
|
// 重置操作
|
||||||
const resetQuery = () => {
|
const resetQuery = async () => {
|
||||||
state.queryParams.searchKey = undefined,
|
state.queryParams.searchKey = undefined,
|
||||||
@if(Model.QueryWhetherList.Count > 0) {
|
@if(Model.QueryWhetherList.Count > 0) {
|
||||||
@foreach (var column in Model.QueryWhetherList) {
|
@foreach (var column in Model.QueryWhetherList) {
|
||||||
|
|||||||
@ -36,10 +36,12 @@ public class DingTalkConst
|
|||||||
/// 主部门Id
|
/// 主部门Id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string DeptId = "sys00-mainDeptId";
|
public const string DeptId = "sys00-mainDeptId";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 主部门
|
/// 主部门
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string Dept = "sys00-mainDept";
|
public const string Dept = "sys00-mainDept";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 职位
|
/// 职位
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -73,23 +73,25 @@ public class DingTalkUser : EntityBase
|
|||||||
[SugarColumn(ColumnDescription = "工号", Length = 16)]
|
[SugarColumn(ColumnDescription = "工号", Length = 16)]
|
||||||
[MaxLength(16)]
|
[MaxLength(16)]
|
||||||
public string? JobNumber { get; set; }
|
public string? JobNumber { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 主部门Id
|
/// 主部门Id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(ColumnDescription = "主部门Id", Length = 16)]
|
[SugarColumn(ColumnDescription = "主部门Id", Length = 16)]
|
||||||
[MaxLength(16)]
|
[MaxLength(16)]
|
||||||
public string? DeptId { get; set; }
|
public string? DeptId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 主部门
|
/// 主部门
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(ColumnDescription = "主部门", Length = 16)]
|
[SugarColumn(ColumnDescription = "主部门", Length = 16)]
|
||||||
[MaxLength(16)]
|
[MaxLength(16)]
|
||||||
public string? Dept { get; set; }
|
public string? Dept { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 职位
|
/// 职位
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(ColumnDescription = "职位", Length = 16)]
|
[SugarColumn(ColumnDescription = "职位", Length = 16)]
|
||||||
[MaxLength(16)]
|
[MaxLength(16)]
|
||||||
public string? Position { get; set; }
|
public string? Position { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -43,7 +43,7 @@ public class DingTalkService : IDynamicApiController, IScoped
|
|||||||
/// <param name="access_token"></param>
|
/// <param name="access_token"></param>
|
||||||
/// <param name="input"></param>
|
/// <param name="input"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[DisplayName("获取在职员工列表")]
|
[HttpPost, DisplayName("获取在职员工列表")]
|
||||||
public async Task<DingTalkBaseResponse<GetDingTalkCurrentEmployeesListOutput>> GetDingTalkCurrentEmployeesList(string access_token, [Required] GetDingTalkCurrentEmployeesListInput input)
|
public async Task<DingTalkBaseResponse<GetDingTalkCurrentEmployeesListOutput>> GetDingTalkCurrentEmployeesList(string access_token, [Required] GetDingTalkCurrentEmployeesListInput input)
|
||||||
{
|
{
|
||||||
return await _dingTalkApi.GetDingTalkCurrentEmployeesList(access_token, input);
|
return await _dingTalkApi.GetDingTalkCurrentEmployeesList(access_token, input);
|
||||||
@ -55,7 +55,7 @@ public class DingTalkService : IDynamicApiController, IScoped
|
|||||||
/// <param name="access_token"></param>
|
/// <param name="access_token"></param>
|
||||||
/// <param name="input"></param>
|
/// <param name="input"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[DisplayName("获取员工花名册字段信息")]
|
[HttpPost, DisplayName("获取员工花名册字段信息")]
|
||||||
public async Task<DingTalkBaseResponse<List<DingTalkEmpRosterFieldVo>>> GetDingTalkCurrentEmployeesRosterList(string access_token, [Required] GetDingTalkCurrentEmployeesRosterListInput input)
|
public async Task<DingTalkBaseResponse<List<DingTalkEmpRosterFieldVo>>> GetDingTalkCurrentEmployeesRosterList(string access_token, [Required] GetDingTalkCurrentEmployeesRosterListInput input)
|
||||||
{
|
{
|
||||||
return await _dingTalkApi.GetDingTalkCurrentEmployeesRosterList(access_token, input);
|
return await _dingTalkApi.GetDingTalkCurrentEmployeesRosterList(access_token, input);
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
||||||
//
|
//
|
||||||
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
||||||
//
|
//
|
||||||
@ -10,6 +10,7 @@ namespace Admin.NET.Plugin.GoView;
|
|||||||
/// GoView 项目表
|
/// GoView 项目表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarTable(null, "GoView 项目表")]
|
[SugarTable(null, "GoView 项目表")]
|
||||||
|
[SysTable]
|
||||||
public class GoViewPro : EntityTenant
|
public class GoViewPro : EntityTenant
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -10,6 +10,7 @@ namespace Admin.NET.Plugin.GoView;
|
|||||||
/// GoView 项目数据表
|
/// GoView 项目数据表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarTable(null, "GoView 项目数据表")]
|
[SugarTable(null, "GoView 项目数据表")]
|
||||||
|
[SysTable]
|
||||||
public class GoViewProData : EntityTenant
|
public class GoViewProData : EntityTenant
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
"name": "admin.net.pro",
|
"name": "admin.net.pro",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "2.4.33",
|
"version": "2.4.33",
|
||||||
"lastBuildTime": "2024.07.12",
|
"lastBuildTime": "2024.07.14",
|
||||||
"description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架",
|
"description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架",
|
||||||
"author": "zuohuaijun",
|
"author": "zuohuaijun",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -31,7 +31,7 @@
|
|||||||
"echarts": "^5.5.1",
|
"echarts": "^5.5.1",
|
||||||
"echarts-gl": "^2.0.9",
|
"echarts-gl": "^2.0.9",
|
||||||
"echarts-wordcloud": "^2.1.0",
|
"echarts-wordcloud": "^2.1.0",
|
||||||
"element-plus": "^2.7.6",
|
"element-plus": "^2.7.7",
|
||||||
"exceljs": "^4.4.0",
|
"exceljs": "^4.4.0",
|
||||||
"ezuikit": "^1.0.0",
|
"ezuikit": "^1.0.0",
|
||||||
"ezuikit-js": "^8.0.5",
|
"ezuikit-js": "^8.0.5",
|
||||||
@ -68,8 +68,8 @@
|
|||||||
"vue-signature-pad": "^3.0.2",
|
"vue-signature-pad": "^3.0.2",
|
||||||
"vue3-tree-org": "^4.2.2",
|
"vue3-tree-org": "^4.2.2",
|
||||||
"vuedraggable": "4.0.3",
|
"vuedraggable": "4.0.3",
|
||||||
"vxe-pc-ui": "^4.0.61",
|
"vxe-pc-ui": "^4.0.67",
|
||||||
"vxe-table": "^4.7.48",
|
"vxe-table": "^4.7.50",
|
||||||
"vxe-table-plugin-element": "^4.0.4",
|
"vxe-table-plugin-element": "^4.0.4",
|
||||||
"vxe-table-plugin-export-xlsx": "^4.0.5",
|
"vxe-table-plugin-export-xlsx": "^4.0.5",
|
||||||
"xe-utils": "^3.5.28",
|
"xe-utils": "^3.5.28",
|
||||||
@ -88,12 +88,12 @@
|
|||||||
"@vitejs/plugin-vue-jsx": "^4.0.0",
|
"@vitejs/plugin-vue-jsx": "^4.0.0",
|
||||||
"@vue/compiler-sfc": "^3.4.31",
|
"@vue/compiler-sfc": "^3.4.31",
|
||||||
"code-inspector-plugin": "^0.14.2",
|
"code-inspector-plugin": "^0.14.2",
|
||||||
"eslint": "^9.6.0",
|
"eslint": "^9.7.0",
|
||||||
"eslint-plugin-vue": "^9.27.0",
|
"eslint-plugin-vue": "^9.27.0",
|
||||||
"less": "^4.2.0",
|
"less": "^4.2.0",
|
||||||
"prettier": "^3.3.2",
|
"prettier": "^3.3.3",
|
||||||
"rollup-plugin-visualizer": "^5.12.0",
|
"rollup-plugin-visualizer": "^5.12.0",
|
||||||
"sass": "^1.77.7",
|
"sass": "^1.77.8",
|
||||||
"terser": "^5.31.2",
|
"terser": "^5.31.2",
|
||||||
"typescript": "^5.5.3",
|
"typescript": "^5.5.3",
|
||||||
"vite": "^5.3.3",
|
"vite": "^5.3.3",
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
<div class="welcome">
|
<div class="welcome">
|
||||||
<div class="logo">
|
<div class="logo">
|
||||||
<!-- <img src="/@/assets/logo.png" style="height: 150px;"/> -->
|
<!-- <img src="/@/assets/logo.png" style="height: 150px;"/> -->
|
||||||
<h2>欢迎使用 Admin.NET</h2>
|
<h2>欢迎使用 {{ themeConfig.globalTitle }}</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="tips">
|
<div class="tips">
|
||||||
<div class="tips-item">
|
<div class="tips-item">
|
||||||
@ -37,6 +37,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { useThemeConfig } from '/@/stores/themeConfig';
|
||||||
|
|
||||||
|
const storesThemeConfig = useThemeConfig();
|
||||||
|
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: '欢迎',
|
title: '欢迎',
|
||||||
icon: 'ele-Present',
|
icon: 'ele-Present',
|
||||||
|
|||||||
@ -7,6 +7,10 @@
|
|||||||
<span> {{ props.title }} </span>
|
<span> {{ props.title }} </span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<div style="color: red; padding: 15px 15px; background: #faecd8; margin-bottom: 10px">
|
||||||
|
<el-icon style="transform: translateY(2px)"><ele-Bell /></el-icon>
|
||||||
|
<span> 如果是在前端生成的实体/表(在生成表选择项里面找不到),请重启后台服务后再进行代码生成。 </span>
|
||||||
|
</div>
|
||||||
<el-form :model="state.ruleForm" ref="ruleFormRef" label-width="auto">
|
<el-form :model="state.ruleForm" ref="ruleFormRef" label-width="auto">
|
||||||
<el-row :gutter="10">
|
<el-row :gutter="10">
|
||||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||||
@ -57,8 +61,8 @@
|
|||||||
<template v-slot:label>
|
<template v-slot:label>
|
||||||
<div>
|
<div>
|
||||||
生成表
|
生成表
|
||||||
<el-tooltip raw-content content="如果是刚刚在前端生成的实体,请重启后台服务后再进行代码生成。" placement="top">
|
<el-tooltip raw-content content="如果是在前端生成的实体/表(在生成表选择项里面找不到),请重启后台服务后再进行代码生成。" placement="top">
|
||||||
<SvgIcon name="fa fa-question-circle-o" :size="16" style="vertical-align: middle" />
|
<SvgIcon name="fa fa-question-circle-o" :size="16" />
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -182,6 +182,7 @@ const handleAdd = () => {
|
|||||||
pagePath: 'main',
|
pagePath: 'main',
|
||||||
nameSpace: state.applicationNamespaces[0],
|
nameSpace: state.applicationNamespaces[0],
|
||||||
generateMenu: false,
|
generateMenu: false,
|
||||||
|
isApiService: false,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -104,4 +104,9 @@ export const dataTypeList = [
|
|||||||
hasLength: false,
|
hasLength: false,
|
||||||
hasDecimalDigits: false,
|
hasDecimalDigits: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
value: 'boolean',
|
||||||
|
hasLength: false,
|
||||||
|
hasDecimalDigits: false,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -44,7 +44,7 @@
|
|||||||
<div>
|
<div>
|
||||||
扫描触发器
|
扫描触发器
|
||||||
<el-tooltip raw-content content="此参数只在新增作业时生效<br/>扫描定义在作业上的触发器" placement="top">
|
<el-tooltip raw-content content="此参数只在新增作业时生效<br/>扫描定义在作业上的触发器" placement="top">
|
||||||
<SvgIcon name="fa fa-question-circle-o" :size="16" style="vertical-align: middle" />
|
<SvgIcon name="fa fa-question-circle-o" :size="16" />
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -54,7 +54,8 @@
|
|||||||
<template #row_sex="{ row }">
|
<template #row_sex="{ row }">
|
||||||
<el-tag v-if="row.sex === 1" type="success">男</el-tag>
|
<el-tag v-if="row.sex === 1" type="success">男</el-tag>
|
||||||
<el-tag v-else-if="row.sex === 2" type="danger">女</el-tag>
|
<el-tag v-else-if="row.sex === 2" type="danger">女</el-tag>
|
||||||
<el-tag v-else type="info">其他</el-tag>
|
<el-tag v-else-if="row.sex === 0" type="info">未知的性别</el-tag>
|
||||||
|
<el-tag v-else-if="row.sex === 9" type="info">未说明的性别</el-tag>
|
||||||
</template>
|
</template>
|
||||||
<template #row_accountType="{ row }">
|
<template #row_accountType="{ row }">
|
||||||
<el-tag v-if="row.accountType === 888">系统管理员</el-tag>
|
<el-tag v-if="row.accountType === 888">系统管理员</el-tag>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user