😎1、优化枚举转字典 2、新增小程序用户头像昵称服务接口 3、优化修改密码显示 4、新增金蝶云星空ERP插件 5、其他优化

This commit is contained in:
zuohuaijun 2024-09-01 01:48:03 +08:00
parent cdf356fc16
commit ac813f491f
18 changed files with 602 additions and 115 deletions

View File

@ -312,4 +312,29 @@ public partial class SysUser : EntityTenant
[SugarColumn(ColumnDescription = "电子签名", Length = 512)]
[MaxLength(512)]
public string? Signature { get; set; }
/// <summary>
/// 验证超级管理员类型,若账号类型为超级管理员则报错
/// </summary>
/// <param name="errorMsg">自定义错误消息</param>
public void ValidateIsSuperAdminAccountType(ErrorCodeEnum? errorMsg = ErrorCodeEnum.D1014)
{
if (AccountType == AccountTypeEnum.SuperAdmin)
{
throw Oops.Oh(errorMsg);
}
}
/// <summary>
/// 验证用户Id是否相同若用户Id相同则报错
/// </summary>
/// <param name="userId">用户Id</param>
/// <param name="errorMsg">自定义错误消息</param>
public void ValidateIsUserId(long userId, ErrorCodeEnum? errorMsg = ErrorCodeEnum.D1001)
{
if (Id == userId)
{
throw Oops.Oh(errorMsg);
}
}
}

View File

@ -4,6 +4,8 @@
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
using AngleSharp.Common;
namespace Admin.NET.Core;
/// <summary>
@ -14,87 +16,89 @@ namespace Admin.NET.Core;
public class EnumToDictJob : IJob
{
private readonly IServiceScopeFactory _scopeFactory;
private readonly IJsonSerializerProvider _jsonSerializer;
private const int OrderOffset = 10;
private const string DefaultTagType = "info";
public EnumToDictJob(IServiceScopeFactory scopeFactory, IJsonSerializerProvider jsonSerializer)
public EnumToDictJob(IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
_jsonSerializer = jsonSerializer;
}
public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
{
using var serviceScope = _scopeFactory.CreateScope();
var sysEnumService = serviceScope.ServiceProvider.GetRequiredService<SysEnumService>();
// 获取数据库连接
var db = serviceScope.ServiceProvider.GetRequiredService<ISqlSugarClient>().CopyNew();
// 获取枚举类型列表
var enumTypeList = sysEnumService.GetEnumTypeList();
var enumCodeList = enumTypeList.Select(u => u.TypeName);
var sysDictTypeCodeList = await db.Queryable<SysDictType>().Where(u => enumCodeList.Contains(u.Code)).Select(u => u.Code).ToListAsync(stoppingToken);
// 查询数据库中已存在的枚举类型代码
var sysDictTypeList = await db.Queryable<SysDictType>()
.Includes(d => d.Children)
.Where(d => enumCodeList.Contains(d.Code))
.ToListAsync(stoppingToken);
// 更新的枚举转换字典
var uEnumType = enumTypeList.Where(u => sysDictTypeCodeList.Contains(u.TypeName)).ToList();
var waitUpdateSysDictType = await db.Queryable<SysDictType>().Where(u => uEnumType.Any(a => a.TypeName == u.Code)).ToListAsync(stoppingToken);
var waitUpdateSysDictTypeDict = waitUpdateSysDictType.ToDictionary(u => u.Code, u => u);
var waitUpdateSysDictData = await db.Queryable<SysDictData>().Where(u => uEnumType.Any(a => a.TypeName == u.DictType.Code)).ToListAsync(stoppingToken);
var uSysDictType = new List<SysDictType>();
var uSysDictData = new List<SysDictData>();
if (uEnumType.Count > 0)
{
uEnumType.ForEach(e =>
{
if (waitUpdateSysDictTypeDict.TryGetValue(e.TypeName, out SysDictType value))
{
var uDictType = value;
uDictType.Name = e.TypeDescribe;
uDictType.Remark = e.TypeRemark;
var uDictData = waitUpdateSysDictData.Where(u => u.DictTypeId == uDictType.Id).ToList();
if (uDictData.Count > 0)
{
uDictData.ForEach(dictData =>
{
var enumData = e.EnumEntities.Where(u => dictData.Code == u.Name).FirstOrDefault();
if (enumData != null)
{
dictData.Value = enumData.Value.ToString();
dictData.Code = enumData.Name;
dictData.OrderNo = enumData.Value + 10;
dictData.Name = enumData.Describe;
dictData.TagType = enumData.Theme != "" ? enumData.Theme : dictData.TagType != "" ? dictData.TagType : "info";
uSysDictData.Add(dictData);
}
});
}
if (!uSysDictType.Any(u => u.Id == uDictType.Id))
uSysDictType.Add(uDictType);
}
});
try
{
db.BeginTran();
if (uSysDictType.Count > 0)
await db.Updateable(uSysDictType).ExecuteCommandAsync(stoppingToken);
if (uSysDictData.Count > 0)
await db.Updateable(uSysDictData).ExecuteCommandAsync(stoppingToken);
db.CommitTran();
}
catch (Exception error)
{
db.RollbackTran();
Log.Error($"{context.Trigger.Description}更新枚举转换字典入库错误:" + _jsonSerializer.Serialize(error));
throw new Exception($"{context.Trigger.Description}更新枚举转换字典入库错误");
}
}
var updatedEnumCodes = sysDictTypeList.Select(u => u.Code);
var updatedEnumType = enumTypeList.Where(u => updatedEnumCodes.Contains(u.TypeName)).ToList();
var sysDictTypeDict = sysDictTypeList.ToDictionary(u => u.Code, u => u);
var (updatedDictTypes, updatedDictDatas) = GetUpdatedDicts(updatedEnumType, sysDictTypeDict);
// 新增的枚举转换字典
var iEnumType = enumTypeList.Where(u => !sysDictTypeCodeList.Contains(u.TypeName)).ToList();
if (iEnumType.Count > 0)
var newEnumType = enumTypeList.Where(u => !updatedEnumCodes.Contains(u.TypeName)).ToList();
var (newDictTypes, newDictDatas) = GetNewSysDicts(newEnumType);
// 执行数据库操作
try
{
db.BeginTran();
if (updatedDictTypes.Count > 0)
await db.Updateable(updatedDictTypes).ExecuteCommandAsync(stoppingToken);
if (updatedDictDatas.Count > 0)
await db.Updateable(updatedDictDatas).ExecuteCommandAsync(stoppingToken);
if (newDictTypes.Count > 0)
await db.Insertable(newDictTypes).ExecuteCommandAsync(stoppingToken);
if (newDictDatas.Count > 0)
await db.Insertable(newDictDatas).ExecuteCommandAsync(stoppingToken);
db.CommitTran();
}
catch (Exception error)
{
db.RollbackTran();
Log.Error($"系统枚举转换字典操作错误:{error.Message}\n堆栈跟踪{error.StackTrace}", error);
throw;
}
var originColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($"【{DateTime.Now}】系统枚举转换字典");
Console.ForegroundColor = originColor;
}
/// <summary>
/// 获取需要新增的字典列表
/// </summary>
/// <param name="addEnumType"></param>
/// <returns>
/// 一个元组,包含以下元素:
/// <list type="table">
/// <item><term>SysDictTypes</term><description>字典类型列表</description></item>
/// <item><term>SysDictDatas</term><description>字典数据列表</description></item>
/// </list>
/// </returns>
private (List<SysDictType>, List<SysDictData>) GetNewSysDicts(List<EnumTypeOutput> addEnumType)
{
var newDictType = new List<SysDictType>();
var newDictData = new List<SysDictData>();
if (addEnumType.Count > 0)
{
// 新增字典类型
var iDictType = iEnumType.Select(u => new SysDictType
newDictType = addEnumType.Select(u => new SysDictType
{
Id = YitIdHelper.NextId(),
Code = u.TypeName,
@ -102,49 +106,70 @@ public class EnumToDictJob : IJob
Remark = u.TypeRemark,
Status = StatusEnum.Enable
}).ToList();
// 新增字典数据
var dictData = iEnumType.Join(iDictType, t1 => t1.TypeName, t2 => t2.Code, (t1, t2) => new
newDictData = addEnumType.Join(newDictType, t1 => t1.TypeName, t2 => t2.Code, (t1, t2) => new
{
data = t1.EnumEntities.Select(u => new SysDictData
Data = t1.EnumEntities.Select(u => new SysDictData
{
Id = YitIdHelper.NextId(), // 性能优化使用BulkCopyAsync必须手动获取Id
Id = YitIdHelper.NextId(),
DictTypeId = t2.Id,
Name = u.Describe,
Value = u.Value.ToString(),
Code = u.Name,
Remark = t2.Remark,
OrderNo = u.Value + 10,
TagType = u.Theme != "" ? u.Theme : "info"
OrderNo = u.Value + OrderOffset,
TagType = u.Theme != "" ? u.Theme : DefaultTagType,
}).ToList()
}).ToList();
var iDictData = new List<SysDictData>();
dictData.ForEach(item =>
{
iDictData.AddRange(item.data);
});
try
{
db.BeginTran();
}).SelectMany(x => x.Data).ToList();
}
return (newDictType, newDictData);
}
if (iDictType.Count > 0)
await db.Insertable(iDictType).ExecuteCommandAsync(stoppingToken);
if (iDictData.Count > 0)
await db.Insertable(iDictData).ExecuteCommandAsync(stoppingToken);
db.CommitTran();
}
catch (Exception error)
/// <summary>
/// 获取需要更新的字典列表
/// </summary>
/// <param name="updatedEnumType"></param>
/// <param name="sysDictTypeDict"></param>
/// <returns>
/// 一个元组,包含以下元素:
/// <list type="table">
/// <item><term>SysDictTypes</term><description>字典类型列表</description>
/// </item>
/// <item><term>SysDictDatas</term><description>字典数据列表</description>
/// </item>
/// </list>
/// </returns>
private (List<SysDictType>, List<SysDictData>) GetUpdatedDicts(List<EnumTypeOutput> updatedEnumType, Dictionary<string, SysDictType> sysDictTypeDict)
{
var updatedSysDictTypes = new List<SysDictType>();
var updatedSysDictData = new List<SysDictData>();
foreach (var e in updatedEnumType)
{
if (sysDictTypeDict.TryGetValue(e.TypeName, out var value))
{
db.RollbackTran();
Log.Error($"{context.Trigger.Description}新增枚举转换字典入库错误:" + _jsonSerializer.Serialize(error));
throw new Exception($"{context.Trigger.Description}新增枚举转换字典入库错误");
var updatedDictType = value;
updatedDictType.Name = e.TypeDescribe;
updatedDictType.Remark = e.TypeRemark;
updatedSysDictTypes.Add(updatedDictType);
var updatedDictData = updatedDictType.Children.Where(u => u.DictTypeId == updatedDictType.Id).ToList();
// 遍历需要更新的字典数据
foreach (var dictData in updatedDictData)
{
var enumData = e.EnumEntities.Where(u => dictData.Code == u.Name).FirstOrDefault();
if (enumData != null)
{
dictData.Value = enumData.Value.ToString();
dictData.OrderNo = enumData.Value + OrderOffset;
dictData.Name = enumData.Describe;
dictData.TagType = enumData.Theme != "" ? enumData.Theme : dictData.TagType != "" ? dictData.TagType : DefaultTagType;
updatedSysDictData.Add(dictData);
}
}
}
}
var originColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($"【{DateTime.Now}】系统枚举转换字典");
Console.ForegroundColor = originColor;
return (updatedSysDictTypes, updatedSysDictData);
}
}

View File

@ -165,14 +165,9 @@ public class SysUserService : IDynamicApiController, ITransient
[DisplayName("删除用户")]
public virtual async Task DeleteUser(DeleteUserInput input)
{
var user = await _sysUserRep.GetFirstAsync(u => u.Id == input.Id);
if (user == null)
return;
if (user.AccountType == AccountTypeEnum.SuperAdmin)
throw Oops.Oh(ErrorCodeEnum.D1014);
if (user.Id == _userManager.UserId)
throw Oops.Oh(ErrorCodeEnum.D1001);
var user = await _sysUserRep.GetByIdAsync(input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D0009);
user.ValidateIsSuperAdminAccountType();
user.ValidateIsUserId(_userManager.UserId);
// 若账号为租户默认账号则禁止删除
var isTenantUser = await _sysUserRep.ChangeRepository<SqlSugarRepository<SysTenant>>().IsAnyAsync(u => u.UserId == input.Id);
@ -206,7 +201,7 @@ public class SysUserService : IDynamicApiController, ITransient
[DisplayName("查看用户基本信息")]
public virtual async Task<SysUser> GetBaseInfo()
{
return await _sysUserRep.GetFirstAsync(u => u.Id == _userManager.UserId);
return await _sysUserRep.GetByIdAsync(_userManager.UserId);
}
/// <summary>
@ -232,9 +227,8 @@ public class SysUserService : IDynamicApiController, ITransient
if (_userManager.UserId == input.Id)
throw Oops.Oh(ErrorCodeEnum.D1026);
var user = await _sysUserRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D0009);
if (user.AccountType == AccountTypeEnum.SuperAdmin)
throw Oops.Oh(ErrorCodeEnum.D1015);
var user = await _sysUserRep.GetByIdAsync(input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D0009);
user.ValidateIsSuperAdminAccountType(ErrorCodeEnum.D1015);
if (!Enum.IsDefined(typeof(StatusEnum), input.Status))
throw Oops.Oh(ErrorCodeEnum.D3005);
@ -285,7 +279,7 @@ public class SysUserService : IDynamicApiController, ITransient
input.PasswordOld = CryptogramUtil.SM2Decrypt(input.PasswordOld);
input.PasswordNew = CryptogramUtil.SM2Decrypt(input.PasswordNew);
var user = await _sysUserRep.GetFirstAsync(u => u.Id == _userManager.UserId) ?? throw Oops.Oh(ErrorCodeEnum.D0009);
var user = await _sysUserRep.GetByIdAsync(_userManager.UserId) ?? throw Oops.Oh(ErrorCodeEnum.D0009);
if (CryptogramUtil.CryptoType == CryptogramEnum.MD5.ToString())
{
if (user.Password != MD5Encryption.Encrypt(input.PasswordOld))
@ -323,7 +317,7 @@ public class SysUserService : IDynamicApiController, ITransient
[DisplayName("重置用户密码")]
public virtual async Task<string> ResetPwd(ResetPwdUserInput input)
{
var user = await _sysUserRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D0009);
var user = await _sysUserRep.GetByIdAsync(input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D0009);
var password = await _sysConfigService.GetConfigValue<string>(ConfigConst.SysPassword);
user.Password = CryptogramUtil.Encrypt(password);
await _sysUserRep.AsUpdateable(user).UpdateColumns(u => u.Password).ExecuteCommandAsync();
@ -343,7 +337,7 @@ public class SysUserService : IDynamicApiController, ITransient
[DisplayName("解除登录锁定")]
public virtual async Task UnlockLogin(UnlockLoginInput input)
{
var user = await _sysUserRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D0009);
var user = await _sysUserRep.GetByIdAsync(input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D0009);
// 清空密码错误次数
var keyPasswordErrorTimes = $"{CacheConst.KeyPasswordErrorTimes}{user.Account}";

View File

@ -106,4 +106,44 @@ public class DeleteMessageTemplateInput
/// </summary>
[Required(ErrorMessage = "订阅模板Id不能为空")]
public string TemplateId { get; set; }
}
public class UploadAvatarInput
{
/// <summary>
/// 小程序用户身份标识
/// </summary>
[Required(ErrorMessage = "OpenId不能为空")]
public string OpenId { get; set; }
/// <summary>
/// 文件
/// </summary>
[Required]
public IFormFile File { get; set; }
/// <summary>
/// 文件类型
/// </summary>
public string FileType { get; set; }
/// <summary>
/// 文件路径
/// </summary>
public string Path { get; set; }
}
public class SetNickNameInput
{
/// <summary>
/// 小程序用户身份标识
/// </summary>
[Required(ErrorMessage = "OpenId不能为空")]
public string OpenId { get; set; }
/// <summary>
/// 昵称
/// </summary>
[Required(ErrorMessage = "昵称不能为空")]
public string NickName { get; set; }
}

View File

@ -15,14 +15,17 @@ public class SysWxOpenService : IDynamicApiController, ITransient
private readonly SqlSugarRepository<SysOAuthUser> _sysOAuthUserRep;
private readonly SysConfigService _sysConfigService;
private readonly WechatApiClient _wechatApiClient;
private readonly SysFileService _sysFileService;
public SysWxOpenService(SqlSugarRepository<SysOAuthUser> sysOAuthUserRep,
SysConfigService sysConfigService,
WechatApiClientFactory wechatApiClientFactory)
WechatApiClientFactory wechatApiClientFactory,
SysFileService sysFileService)
{
_sysOAuthUserRep = sysOAuthUserRep;
_sysConfigService = sysConfigService;
_wechatApiClient = wechatApiClientFactory.CreateWxOpenClient();
_sysFileService = sysFileService;
}
/// <summary>
@ -131,6 +134,57 @@ public class SysWxOpenService : IDynamicApiController, ITransient
};
}
/// <summary>
/// 上传小程序头像
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[AllowAnonymous]
[DisplayName("上传小程序头像")]
public async Task<SysFile> UploadAvatar([FromForm] UploadAvatarInput input)
{
var wxUser = await _sysOAuthUserRep.GetFirstAsync(u => u.OpenId == input.OpenId);
if (wxUser == null)
throw Oops.Oh("未找到用户上传失败");
var res = await _sysFileService.UploadFile(new FileUploadInput { File = input.File, FileType = input.FileType, Path = input.Path });
wxUser.Avatar = res.Url;
await _sysOAuthUserRep.AsUpdateable(wxUser).IgnoreColumns(true).ExecuteCommandAsync();
return res;
}
/// <summary>
/// 设置小程序用户昵称
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[AllowAnonymous]
[HttpPost]
public async Task SetNickName(SetNickNameInput input)
{
var wxUser = await _sysOAuthUserRep.GetFirstAsync(u => u.OpenId == input.OpenId);
if (wxUser == null)
throw Oops.Oh("未找到用户信息设置失败");
wxUser.NickName = input.NickName;
await _sysOAuthUserRep.AsUpdateable(wxUser).IgnoreColumns(true).ExecuteCommandAsync();
return;
}
/// <summary>
/// 获取小程序用户信息
/// </summary>
/// <param name="openid"></param>
/// <returns></returns>
[AllowAnonymous]
public async Task<dynamic> GetUserInfo(string openid)
{
var wxUser = await _sysOAuthUserRep.GetFirstAsync(u => u.OpenId == openid);
if (wxUser == null)
throw Oops.Oh("未找到用户信息获取失败");
return new { nickName = wxUser.NickName, avator = wxUser.Avatar };
}
/// <summary>
/// 验证签名 🔖
/// </summary>

View File

@ -26,6 +26,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Admin.NET.Plugin.ReZero", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Admin.NET.Plugin.ApprovalFlow", "Plugins\Admin.NET.Plugin.ApprovalFlow\Admin.NET.Plugin.ApprovalFlow.csproj", "{4124E31B-EA94-4EE3-9EC6-A565F1420AEA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Admin.NET.Plugin.K3Cloud", "Plugins\Admin.NET.Plugin.K3Cloud\Admin.NET.Plugin.K3Cloud.csproj", "{9EB9C39E-E14F-443E-9AA3-EE417ABCBC1D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -64,6 +66,10 @@ Global
{4124E31B-EA94-4EE3-9EC6-A565F1420AEA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4124E31B-EA94-4EE3-9EC6-A565F1420AEA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4124E31B-EA94-4EE3-9EC6-A565F1420AEA}.Release|Any CPU.Build.0 = Release|Any CPU
{9EB9C39E-E14F-443E-9AA3-EE417ABCBC1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9EB9C39E-E14F-443E-9AA3-EE417ABCBC1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9EB9C39E-E14F-443E-9AA3-EE417ABCBC1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9EB9C39E-E14F-443E-9AA3-EE417ABCBC1D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -73,6 +79,7 @@ Global
{F6A002AD-CF7F-4771-8597-F12A50A93DAA} = {76F70D22-8D53-468E-A3B6-1704666A1D71}
{04AB2E76-DE8B-4EFD-9F48-F8D4C0993106} = {76F70D22-8D53-468E-A3B6-1704666A1D71}
{4124E31B-EA94-4EE3-9EC6-A565F1420AEA} = {76F70D22-8D53-468E-A3B6-1704666A1D71}
{9EB9C39E-E14F-443E-9AA3-EE417ABCBC1D} = {76F70D22-8D53-468E-A3B6-1704666A1D71}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5CD801D7-984A-4F5C-8FA2-211B7A5EA9F3}

View File

@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<NoWarn>1701;1702;1591;8632</NoWarn>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<Copyright>Admin.NET</Copyright>
<Description>Admin.NET 通用权限开发平台</Description>
</PropertyGroup>
<ItemGroup>
<None Update="Configuration\**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Admin.NET.Core\Admin.NET.Core.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,20 @@
{
"$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
"K3Cloud": {
// ERP
"Url": "http://127.0.0.1/k3cloud/",
// Id(ID)
"AcctID": "XXXXXXXXX",
// Id
"AppId": "XXXXXXXX",
//
"AppKey": "XXX",
//
"UserName": "XXX",
//
"UserPassword": "XXXX@2024",
//
"LanguageCode": "2052"
}
}

View File

@ -0,0 +1,8 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
global using Furion;
global using Furion.ConfigurableOptions;

View File

@ -0,0 +1,45 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Plugin.K3Cloud;
public sealed class K3CloudOptions : IConfigurableOptions
{
/// <summary>
/// ERP业务站点地址
/// </summary>
public string Url { get; set; }
/// <summary>
/// 帐套Id(数据中心ID)
/// </summary>
public string AcctID { get; set; }
/// <summary>
/// 应用Id
/// </summary>
public string AppId { get; set; }
/// <summary>
/// 应用密钥
/// </summary>
public string AppKey { get; set; }
/// <summary>
/// 用户名称
/// </summary>
public string UserName { get; set; }
/// <summary>
/// 用户密码
/// </summary>
public string UserPassword { get; set; }
/// <summary>
/// 语言代码
/// </summary>
public string LanguageCode { get; set; }
}

View File

@ -0,0 +1,23 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Plugin.K3Cloud.Service;
/// <summary>
/// ERP基础入参
/// </summary>
public class K3CloudBaeInput<T>
{
/// <summary>
/// 表单Id
/// </summary>
public string formid { get; set; }
/// <summary>
/// 数据包
/// </summary>
public T data { get; set; }
}

View File

@ -0,0 +1,12 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Plugin.K3Cloud.Service;
public class K3CloudLoginInput
{
public List<string> parameters { get; set; }
}

View File

@ -0,0 +1,62 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Plugin.K3Cloud.Service;
public class K3CloudLoginOutput
{
public string Message { get; set; }
public string MessageCode { get; set; }
public ErpLoginResultType LoginResultType { get; set; }
}
public enum ErpLoginResultType
{
/// <summary>
/// 激活
/// </summary>
Activation = -7,
/// <summary>
/// 云通行证未绑定Cloud账号
/// </summary>
EntryCloudUnBind = -6,
/// <summary>
/// 需要表单处理
/// </summary>
DealWithForm = -5,
/// <summary>
/// 登录警告
/// </summary>
Wanning = -4,
/// <summary>
/// 密码验证不通过(强制的)
/// </summary>
PWInvalid_Required = -3,
/// <summary>
/// 密码验证不通过(可选的)
/// </summary>
PWInvalid_Optional = -2,
/// <summary>
/// 登录失败
/// </summary>
Failure = -1,
/// <summary>
/// 用户或密码错误
/// </summary>
PWError = 0,
/// <summary>
/// 登录成功
/// </summary>
Success = 1
}

View File

@ -0,0 +1,66 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Plugin.K3Cloud.Service;
public class K3CloudPushResultOutput
{
public ErpPushResultInfo Result { get; set; }
}
public class ErpPushResultInfo
{
/// <summary>
/// Id
/// </summary>
public object? Id { get; set; }
/// <summary>
/// 编码
/// </summary>
public string? Number { get; set; }
public ErpPushResultInfo_ResponseStatus ResponseStatus { get; set; }
}
public class ErpPushResultInfo_ResponseStatus
{
public bool IsSuccess { get; set; }
public int? ErrorCode { get; set; }
/// <summary>
/// 错误代码MsgCode说明
///0默认
///1上下文丢失 会话过期
///2没有权限
///3操作标识为空
///4异常
///5单据标识为空
///6数据库操作失败
///7许可错误
///8参数错误
///9指定字段/值不存在
///10未找到对应数据
///11验证失败
///12不可操作
///13网控冲突
///14调用限制
///15禁止管理员登录
/// </summary>
public int? MsgCode { get; set; }
/// <summary>
/// 如果失败,具体失败原因
/// </summary>
public List<ErpPushResultInfo_Errors> Errors { get; set; }
}
public class ErpPushResultInfo_Errors
{
public string FieldName { get; set; }
public string Message { get; set; }
public int DIndex { get; set; }
}

View File

@ -0,0 +1,52 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
using Furion.RemoteRequest;
namespace Admin.NET.Plugin.K3Cloud.Service;
/// <summary>
/// 金蝶云星空ERP接口
/// </summary>
[Client("K3Cloud")]
public interface IK3CloudApi : IHttpDispatchProxy
{
/// <summary>
/// 验证用户
/// </summary>
/// <param name="input"></param>
/// <param name="action"></param>
/// <returns></returns>
[Post("Kingdee.BOS.WebApi.ServicesStub.AuthService.ValidateUser.common.kdsvc")]
Task<K3CloudLoginOutput> ValidateUser([Body] K3CloudLoginInput input, [Interceptor(InterceptorTypes.Response)] Action<HttpClient, HttpResponseMessage> action = default);
/// <summary>
/// 保存表单
/// </summary>
/// <param name="input"></param>
/// <param name="action"></param>
/// <returns></returns>
[Post("Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.Save.common.kdsvc")]
Task<K3CloudPushResultOutput> Save<T>([Body] K3CloudBaeInput<T> input, [Interceptor(InterceptorTypes.Request)] Action<HttpClient, HttpRequestMessage> action = default);
/// <summary>
/// 提交表单
/// </summary>
/// <param name="input"></param>
/// <param name="action"></param>
/// <returns></returns>
[Post("Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.Submit.common.kdsvc")]
Task<K3CloudPushResultOutput> Submit<T>([Body] K3CloudBaeInput<T> input, [Interceptor(InterceptorTypes.Request)] Action<HttpClient, HttpRequestMessage> action = default);
/// <summary>
/// 审核表单
/// </summary>
/// <param name="input"></param>
/// <param name="action"></param>
/// <returns></returns>
[Post("Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.Audit.common.kdsvc")]
Task<K3CloudPushResultOutput> Audit<T>([Body] K3CloudBaeInput<T> input, [Interceptor(InterceptorTypes.Request)] Action<HttpClient, HttpRequestMessage> action = default);
}

View File

@ -0,0 +1,31 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
namespace Admin.NET.Plugin.K3Cloud;
[AppStartup(100)]
public class Startup : AppStartup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddConfigurableOptions<K3CloudOptions>();
services.AddRemoteRequest(options =>
{
options.AddHttpClient("K3Cloud", u =>
{
u.BaseAddress = new Uri(App.GetConfig<K3CloudOptions>("K3Cloud", true).Url);
});
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
}
}

View File

@ -2,7 +2,7 @@
"name": "admin.net.pro",
"type": "module",
"version": "2.4.33",
"lastBuildTime": "2024.08.30",
"lastBuildTime": "2024.09.01",
"description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架",
"author": "zuohuaijun",
"license": "MIT",
@ -26,7 +26,7 @@
"@wangeditor/editor-for-vue": "^5.1.12",
"animate.css": "^4.1.1",
"async-validator": "^4.2.5",
"axios": "^1.7.5",
"axios": "^1.7.6",
"countup.js": "^2.8.0",
"cropperjs": "^1.6.2",
"echarts": "^5.5.1",
@ -70,7 +70,7 @@
"vue-router": "^4.4.3",
"vue-signature-pad": "^3.0.2",
"vue3-tree-org": "^4.2.2",
"vxe-pc-ui": "^4.1.12",
"vxe-pc-ui": "^4.1.15",
"vxe-table": "^4.7.59",
"vxe-table-plugin-element": "^4.0.4",
"vxe-table-plugin-export-xlsx": "^4.0.5",

View File

@ -113,13 +113,13 @@
<el-tab-pane label="修改密码">
<el-form ref="ruleFormPasswordRef" :model="state.ruleFormPassword" label-width="auto">
<el-form-item label="当前密码" prop="passwordOld" :rules="[{ required: true, message: '当前密码不能为空', trigger: 'blur' }]">
<el-input v-model="state.ruleFormPassword.passwordOld" type="password" autocomplete="off" />
<el-input v-model="state.ruleFormPassword.passwordOld" type="password" autocomplete="off" show-password />
</el-form-item>
<el-form-item label="新密码" prop="passwordNew" :rules="[{ required: true, message: '新密码不能为空', trigger: 'blur' }]">
<el-input v-model="state.ruleFormPassword.passwordNew" type="password" autocomplete="off" />
<el-input v-model="state.ruleFormPassword.passwordNew" type="password" autocomplete="off" show-password />
</el-form-item>
<el-form-item label="确认密码" prop="passwordNew2" :rules="[{ validator: validatePassword, required: true, trigger: 'blur' }]">
<el-input v-model="state.passwordNew2" type="password" autocomplete="off" />
<el-input v-model="state.passwordNew2" type="password" autocomplete="off" show-password />
</el-form-item>
<el-form-item>
<el-button icon="ele-Refresh" @click="resetPassword"> </el-button>