update:修改项目名称从Dilon到Ewide

This commit is contained in:
2021-04-25 14:12:19 +08:00
parent 2fff6b58f9
commit b145f2035d
255 changed files with 21828 additions and 21828 deletions

View File

@@ -0,0 +1,100 @@
using System.ComponentModel.DataAnnotations;
namespace Ewide.Core.Service
{
/// <summary>
/// 系统应用参数
/// </summary>
public class AppInput : PageInputBase
{
/// <summary>
/// 名称
/// </summary>
public virtual string Name { get; set; }
/// <summary>
/// 编码
/// </summary>
public virtual string Code { get; set; }
/// <summary>
/// 图标
/// </summary>
public virtual string Icon { get; set; }
/// <summary>
/// 图标颜色
/// </summary>
public virtual string Color { get; set; }
/// <summary>
/// 是否默认激活Y-是N-否),只能有一个系统默认激活
/// 用户登录后默认展示此系统菜单
/// </summary>
public bool Active { get; set; }
/// <summary>
/// 状态(字典 0正常 1停用 2删除
/// </summary>
public CommonStatus Status { get; set; }
/// <summary>
/// 排序
/// </summary>
public int Sort { get; set; }
}
public class AddAppInput : AppInput
{
/// <summary>
/// 名称
/// </summary>
[Required(ErrorMessage = "应用名称不能为空")]
public override string Name { get; set; }
/// <summary>
/// 编码
/// </summary>
[Required(ErrorMessage = "应用编码不能为空")]
public override string Code { get; set; }
/// <summary>
/// 图标
/// </summary>
public override string Icon { get; set; }
/// <summary>
/// 图标颜色
/// </summary>
[RegularExpression("^#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3}$", ErrorMessage = "")]
public override string Color { get; set; }
}
public class DeleteAppInput
{
/// <summary>
/// 应用Id
/// </summary>
[Required(ErrorMessage = "应用Id不能为空")]
public string Id { get; set; }
}
public class UpdateAppInput : AppInput
{
/// <summary>
/// 应用Id
/// </summary>
[Required(ErrorMessage = "应用Id不能为空")]
public string Id { get; set; }
}
public class QueryAppInput : DeleteAppInput
{
}
public class SetDefaultAppInput : DeleteAppInput
{
}
}

View File

@@ -0,0 +1,33 @@
namespace Ewide.Core.Service
{
/// <summary>
/// 系统应用参数
/// </summary>
public class AppOutput
{
/// <summary>
/// 应用Id
/// </summary>
public string Id { get; set; }
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 编码
/// </summary>
public string Code { get; set; }
/// <summary>
/// 是否默认
/// </summary>
public bool Active { get; set; }
/// <summary>
/// 排序
/// </summary>
public int Sort { get; set; }
}
}

View File

@@ -0,0 +1,18 @@
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
public interface ISysAppService
{
Task AddApp(AddAppInput input);
Task DeleteApp(DeleteAppInput input);
Task<SysApp> GetApp([FromQuery] QueryAppInput input);
Task<dynamic> GetAppList([FromQuery] AppInput input);
Task<dynamic> GetLoginApps(string userId);
Task<dynamic> QueryAppPageList([FromQuery] AppInput input);
Task SetAsDefault(SetDefaultAppInput input);
Task UpdateApp(UpdateAppInput input);
Task ChangeUserAppStatus(UpdateAppInput input);
}
}

View File

@@ -0,0 +1,198 @@
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using Mapster;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
/// <summary>
/// 系统应用服务
/// </summary>
[ApiDescriptionSettings(Name = "App", Order = 100)]
public class SysAppService : ISysAppService, IDynamicApiController, ITransient
{
private readonly IRepository<SysApp> _sysAppRep; // 应用表仓储
private readonly IUserManager _userManager;
private readonly ISysMenuService _sysMenuService;
public SysAppService(IRepository<SysApp> sysAppRep,
IUserManager userManager,
ISysMenuService sysMenuService)
{
_sysAppRep = sysAppRep;
_userManager = userManager;
_sysMenuService = sysMenuService;
}
/// <summary>
/// 获取用户应用相关信息
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
[NonAction]
public async Task<dynamic> GetLoginApps(string userId)
{
var apps = _sysAppRep.DetachedEntities.Where(u => u.Status == (int)CommonStatus.ENABLE);
if (!_userManager.SuperAdmin)
{
var appCodeList = await _sysMenuService.GetUserMenuAppCodeList(userId);
apps = apps.Where(u => appCodeList.Contains(u.Code));
}
var appList = await apps.OrderBy(u => u.Sort).Select(u => new AppOutput
{
Code = u.Code,
Name = u.Name,
Active = u.Active
}).ToListAsync(); // .OrderByDescending(u => u.Active) // 将激活的放到第一个
//// 默认激活第一个应用
//if (appList != null && appList.Count > 0 && appList[0].Active != YesOrNot.Y.ToString())
// appList[0].Active = YesOrNot.Y.ToString();
return appList;
}
/// <summary>
/// 分页查询系统应用
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysApp/page")]
public async Task<dynamic> QueryAppPageList([FromQuery] AppInput input)
{
var name = !string.IsNullOrEmpty(input.Name?.Trim());
var code = !string.IsNullOrEmpty(input.Code?.Trim());
var apps = await _sysAppRep.DetachedEntities
.Where((name, u => EF.Functions.Like(u.Name, $"%{input.Name.Trim()}%")),
(code, u => EF.Functions.Like(u.Code, $"%{input.Code.Trim()}%")))
//.Where(u => u.Status == (int)CommonStatus.ENABLE)
.ToPagedListAsync(input.PageNo, input.PageSize);
return XnPageResult<SysApp>.PageResult(apps);
}
/// <summary>
/// 增加系统应用
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysApp/add")]
public async Task AddApp(AddAppInput input)
{
var isExist = await _sysAppRep.DetachedEntities.AnyAsync(u => u.Name == input.Name || u.Code == input.Code);
if (isExist)
throw Oops.Oh(ErrorCode.D5000);
if (input.Active)
{
isExist = await _sysAppRep.DetachedEntities.AnyAsync(u => u.Active == input.Active);
if (isExist)
throw Oops.Oh(ErrorCode.D5001);
}
var app = input.Adapt<SysApp>();
await app.InsertAsync();
}
/// <summary>
/// 删除系统应用
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysApp/delete")]
public async Task DeleteApp(DeleteAppInput input)
{
var app = await _sysAppRep.FirstOrDefaultAsync(u => u.Id == input.Id);
// 该应用下是否有状态为正常的菜单
var hasMenu = await _sysMenuService.HasMenu(app.Code);
if (hasMenu)
throw Oops.Oh(ErrorCode.D5002);
await app.DeleteAsync();
}
/// <summary>
/// 更新系统应用
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysApp/edit")]
public async Task UpdateApp(UpdateAppInput input)
{
var isExist = await _sysAppRep.DetachedEntities.AnyAsync(u => (u.Name == input.Name || u.Code == input.Code) && u.Id != input.Id);
if (isExist)
throw Oops.Oh(ErrorCode.D5000);
if (input.Active)
{
isExist = await _sysAppRep.DetachedEntities.AnyAsync(u => u.Active == input.Active);
if (isExist)
throw Oops.Oh(ErrorCode.D5001);
}
var app = input.Adapt<SysApp>();
await app.UpdateExcludeAsync(new[] { nameof(SysApp.Status) }, true);
}
/// <summary>
/// 获取系统应用
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysApp/detail")]
public async Task<SysApp> GetApp([FromQuery] QueryAppInput input)
{
return await _sysAppRep.DetachedEntities.FirstOrDefaultAsync(u => u.Id == input.Id);
}
/// <summary>
/// 获取系统应用列表
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysApp/list")]
public async Task<dynamic> GetAppList([FromQuery] AppInput input)
{
return await _sysAppRep.DetachedEntities.Where(u => u.Status == (int)CommonStatus.ENABLE).ToListAsync();
}
/// <summary>
/// 设为默认应用
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysApp/setAsDefault")]
public async Task SetAsDefault(SetDefaultAppInput input)
{
var apps = await _sysAppRep.Where(u => u.Status == (int)CommonStatus.ENABLE).ToListAsync();
apps.ForEach(u =>
{
u.Active = false;
});
var app = await _sysAppRep.FirstOrDefaultAsync(u => u.Id == input.Id);
app.Active = true;
}
/// <summary>
/// 修改用户状态
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysApp/changeStatus")]
public async Task ChangeUserAppStatus(UpdateAppInput input)
{
if (!Enum.IsDefined(typeof(CommonStatus), input.Status))
throw Oops.Oh(ErrorCode.D3005);
var app = await _sysAppRep.FirstOrDefaultAsync(u => u.Id == input.Id);
app.Status = input.Status;
}
}
}

View File

@@ -0,0 +1,227 @@
using Furion;
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DataEncryption;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using Mapster;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using UAParser;
namespace Ewide.Core.Service
{
/// <summary>
/// 登录授权相关服务
/// </summary>
[ApiDescriptionSettings(Name = "Auth", Order = 160)]
public class AuthService : IAuthService, IDynamicApiController, ITransient
{
private readonly IRepository<SysUser> _sysUserRep; // 用户表仓储
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IUserManager _userManager; // 用户管理
private readonly ISysUserService _sysUserService; // 系统用户服务
private readonly ISysEmpService _sysEmpService; // 系统员工服务
private readonly ISysRoleService _sysRoleService; // 系统角色服务
private readonly ISysMenuService _sysMenuService; // 系统菜单服务
private readonly ISysAppService _sysAppService; // 系统应用服务
private readonly IClickWordCaptcha _captchaHandle;// 验证码服务
private readonly ISysConfigService _sysConfigService; // 验证码服务
public AuthService(IRepository<SysUser> sysUserRep,
IHttpContextAccessor httpContextAccessor,
IUserManager userManager,
ISysUserService sysUserService,
ISysEmpService sysEmpService,
ISysRoleService sysRoleService,
ISysMenuService sysMenuService,
ISysAppService sysAppService,
IClickWordCaptcha captchaHandle,
ISysConfigService sysConfigService)
{
_sysUserRep = sysUserRep;
_httpContextAccessor = httpContextAccessor;
_userManager = userManager;
_sysUserService = sysUserService;
_sysEmpService = sysEmpService;
_sysRoleService = sysRoleService;
_sysMenuService = sysMenuService;
_sysAppService = sysAppService;
_captchaHandle = captchaHandle;
_sysConfigService = sysConfigService;
}
/// <summary>
/// 用户登录
/// </summary>
/// <param name="input"></param>
/// <remarks>默认用户名/密码admin/admin</remarks>
/// <returns></returns>
[HttpPost("/login")]
[AllowAnonymous]
public async Task<string> LoginAsync([Required] LoginInput input)
{
// 获取加密后的密码
var encryptPasswod = MD5Encryption.Encrypt(input.Password);
// 判断用户名和密码是否正确
var user = await _sysUserRep.FirstOrDefaultAsync(u => u.Account.Equals(input.Account) && u.Password.Equals(encryptPasswod));
_ = user ?? throw Oops.Oh(ErrorCode.D1000);
// 验证账号是否被冻结
if (user.Status == CommonStatus.DISABLE)
throw Oops.Oh(ErrorCode.D1017);
// 生成Token令牌
//var accessToken = await _jwtBearerManager.CreateTokenAdmin(user);
var accessToken = JWTEncryption.Encrypt(new Dictionary<string, object>
{
{ ClaimConst.CLAINM_USERID, user.Id },
{ ClaimConst.CLAINM_ACCOUNT, user.Account },
{ ClaimConst.CLAINM_NAME, user.Name },
{ ClaimConst.CLAINM_SUPERADMIN, user.AdminType },
});
// 设置Swagger自动登录
_httpContextAccessor.SigninToSwagger(accessToken);
// 生成刷新Token令牌
var refreshToken = JWTEncryption.GenerateRefreshToken(accessToken, 30);
// 设置刷新Token令牌
_httpContextAccessor.HttpContext.Response.Headers["x-access-token"] = refreshToken;
return accessToken;
}
/// <summary>
/// 获取当前登录用户信息
/// </summary>
/// <returns></returns>
[HttpGet("/getLoginUser")]
public async Task<LoginOutput> GetLoginUserAsync()
{
var user = _userManager.User;
var userId = user.Id;
var httpContext = App.GetService<IHttpContextAccessor>().HttpContext;
var loginOutput = user.Adapt<LoginOutput>();
loginOutput.LastLoginTime = user.LastLoginTime = DateTimeOffset.Now;
var ip = httpContext.Request.Headers["X-Real-IP"].FirstOrDefault();
loginOutput.LastLoginIp = user.LastLoginIp = string.IsNullOrEmpty(user.LastLoginIp) ? httpContext.GetRemoteIpAddressToIPv4() : ip;
//var ipInfo = IpTool.Search(loginOutput.LastLoginIp);
//loginOutput.LastLoginAddress = ipInfo.Country + ipInfo.Province + ipInfo.City + "[" + ipInfo.NetworkOperator + "][" + ipInfo.Latitude + ipInfo.Longitude + "]";
var clent = Parser.GetDefault().Parse(httpContext.Request.Headers["User-Agent"]);
loginOutput.LastLoginBrowser = clent.UA.Family + clent.UA.Major;
loginOutput.LastLoginOs = clent.OS.Family + clent.OS.Major;
// 员工信息
loginOutput.LoginEmpInfo = await _sysEmpService.GetEmpInfo(userId);
// 角色信息
loginOutput.Roles = await _sysRoleService.GetUserRoleList(userId);
// 权限信息
loginOutput.Permissions = await _sysMenuService.GetLoginPermissionList(userId);
// 数据范围信息(机构Id集合)
loginOutput.DataScopes = await _sysUserService.GetUserDataScopeIdList(userId);
// 具备应用信息(多系统,默认激活一个,可根据系统切换菜单),返回的结果中第一个为激活的系统
loginOutput.Apps = await _sysAppService.GetLoginApps(userId);
// 菜单信息
if (loginOutput.Apps.Count > 0)
{
var defaultActiveAppCode = loginOutput.Apps.FirstOrDefault(u => u.Active == true).Code; // loginOutput.Apps[0].Code;
loginOutput.Menus = await _sysMenuService.GetLoginMenusAntDesign(userId, defaultActiveAppCode);
}
// 增加登录日志
await new SysLogVis
{
Name = "登录",
Success = YesOrNot.Y.ToString(),
Message = "登录成功",
Ip = loginOutput.LastLoginIp,
Browser = loginOutput.LastLoginBrowser,
Os = loginOutput.LastLoginOs,
VisType = 1,
VisTime = loginOutput.LastLoginTime,
Account = loginOutput.Account
}.InsertAsync();
return loginOutput;
}
/// <summary>
/// 退出
/// </summary>
/// <returns></returns>
[HttpGet("/logout")]
public async Task LogoutAsync()
{
_httpContextAccessor.SignoutToSwagger();
//_httpContextAccessor.HttpContext.Response.Headers["access-token"] = "invalid token";
// 增加退出日志
await new SysLogVis
{
Name = "退出",
Success = YesOrNot.Y.ToString(),
Message = "退出成功",
VisType = 2
}.InsertAsync();
await Task.CompletedTask;
}
/// <summary>
/// 获取验证码开关
/// </summary>
/// <returns></returns>
[HttpGet("/getCaptchaOpen")]
[AllowAnonymous]
public async Task<bool> GetCaptchaOpen()
{
return await _sysConfigService.GetCaptchaOpenFlag();
}
/// <summary>
/// 获取验证码(默认点选模式)
/// </summary>
/// <returns></returns>
[HttpPost("/captcha/get")]
[AllowAnonymous]
[NonUnify]
public async Task<dynamic> GetCaptcha()
{
// 图片大小要与前端保持一致(坐标范围)
return await Task.FromResult(_captchaHandle.CreateCaptchaImage(_captchaHandle.RandomCode(6), 310, 155));
}
/// <summary>
/// 校验验证码
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/captcha/check")]
[AllowAnonymous]
[NonUnify]
public async Task<dynamic> VerificationCode(ClickWordCaptchaInput input)
{
return await Task.FromResult(_captchaHandle.CheckCode(input));
}
}
}

View File

@@ -0,0 +1,26 @@
using Furion.DependencyInjection;
using System.ComponentModel.DataAnnotations;
namespace Ewide.Core.Service
{
/// <summary>
/// 登录输入参数
/// </summary>
[SkipScan]
public class LoginInput
{
/// <summary>
/// 用户名
/// </summary>
/// <example>superAdmin</example>
[Required(ErrorMessage = "用户名不能为空"), MinLength(3, ErrorMessage = "用户名不能少于3位字符")]
public string Account { get; set; }
/// <summary>
/// 密码
/// </summary>
/// <example>123456</example>
[Required(ErrorMessage = "密码不能为空"), MinLength(5, ErrorMessage = "密码不能少于5位字符")]
public string Password { get; set; }
}
}

View File

@@ -0,0 +1,163 @@
using Furion.DependencyInjection;
using System;
using System.Collections.Generic;
namespace Ewide.Core.Service
{
/// <summary>
/// 用户登录输出参数
/// </summary>
[SkipScan]
public class LoginOutput
{
/// <summary>
/// 主键
/// </summary>
public string Id { get; set; }
/// <summary>
/// 账号
/// </summary>
public string Account { get; set; }
/// <summary>
/// 昵称
/// </summary>
public string NickName { get; set; }
/// <summary>
/// 姓名
/// </summary>
public string Name { get; set; }
/// <summary>
/// 头像
/// </summary>
public string Avatar { get; set; }
/// <summary>
/// 生日
/// </summary>
public DateTimeOffset Birthday { get; set; }
/// <summary>
/// 性别(字典 1男 2女)
/// </summary>
public int Sex { get; set; }
/// <summary>
/// 邮箱
/// </summary>
public String Email { get; set; }
/// <summary>
/// 手机
/// </summary>
public String Phone { get; set; }
/// <summary>
/// 电话
/// </summary>
public String Tel { get; set; }
/// <summary>
/// 管理员类型0超级管理员 1非管理员
/// </summary>
public int AdminType { get; set; }
/// <summary>
/// 最后登陆IP
/// </summary>
public string LastLoginIp { get; set; }
/// <summary>
/// 最后登陆时间
/// </summary>
public DateTimeOffset LastLoginTime { get; set; }
/// <summary>
/// 最后登陆地址
/// </summary>
public string LastLoginAddress { get; set; }
/// <summary>
/// 最后登陆所用浏览器
/// </summary>
public string LastLoginBrowser { get; set; }
/// <summary>
/// 最后登陆所用系统
/// </summary>
public string LastLoginOs { get; set; }
/// <summary>
/// 员工信息
/// </summary>
public EmpOutput LoginEmpInfo { get; set; } = new EmpOutput();
/// <summary>
/// 具备应用信息
/// </summary>
public List<AppOutput> Apps { get; set; } = new List<AppOutput>();
/// <summary>
/// 角色信息
/// </summary>
public List<RoleOutput> Roles { get; set; } = new List<RoleOutput>();
/// <summary>
/// 权限信息
/// </summary>
public List<string> Permissions { get; set; } = new List<string>();
/// <summary>
/// 登录菜单信息---AntDesign版本菜单
/// </summary>
public List<AntDesignTreeNode> Menus { get; set; } = new List<AntDesignTreeNode>();
/// <summary>
/// 数据范围(机构)信息
/// </summary>
public List<string> DataScopes { get; set; } = new List<string>();
///// <summary>
///// 租户信息
///// </summary>
//public List<long> Tenants { get; set; }
///// <summary>
///// 密码
///// </summary>
//public string Password { get; set; }
///// <summary>
///// 账户过期
///// </summary>
//public string AccountNonExpired { get; set; }
///// <summary>
///// 凭证过期
///// </summary>
//public string CredentialsNonExpired { get; set; }
///// <summary>
///// 账户锁定
///// </summary>
//public bool AccountNonLocked { get; set; }
///// <summary>
///// 用户名称
///// </summary>
//public string UserName { get; set; }
///// <summary>
///// 权限
///// </summary>
//public List<long> Authorities { get; set; } = new List<long>();
///// <summary>
///// 是否启动
///// </summary>
//public bool Enabled { get; set; }
}
}

View File

@@ -0,0 +1,15 @@
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
public interface IAuthService
{
Task<dynamic> GetCaptcha();
Task<bool> GetCaptchaOpen();
Task<LoginOutput> GetLoginUserAsync();
Task<string> LoginAsync([Required] LoginInput input);
Task LogoutAsync();
Task<dynamic> VerificationCode(ClickWordCaptchaInput input);
}
}

View File

@@ -0,0 +1,21 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
public interface ISysCacheService
{
Task<bool> DelAsync(string key);
Task<bool> DelByPatternAsync(string key);
List<string> GetAllCacheKeys();
Task<List<string>> GetDataScope(string userId);
Task<List<AntDesignTreeNode>> GetMenu(string userId, string appCode);
Task<List<string>> GetPermission(string userId);
Task SetDataScope(string userId, List<string> dataScopes);
Task SetMenu(string userId, string appCode, List<AntDesignTreeNode> menus);
Task SetPermission(string userId, List<string> permissions);
Task<bool> SetAsync(string key, object value);
Task<string> GetAsync(string key);
Task<T> GetAsync<T>(string key);
}
}

View File

@@ -0,0 +1,169 @@
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
/// <summary>
/// 系统缓存服务
/// </summary>
[ApiDescriptionSettings(Name = "Cache", Order = 100)]
public class SysCacheService : ISysCacheService, IDynamicApiController, ISingleton
{
private readonly ICache _cache;
private readonly CacheOptions _cacheOptions;
public SysCacheService(IOptions<CacheOptions> cacheOptions, Func<string, ISingleton, object> resolveNamed)
{
_cacheOptions = cacheOptions.Value;
_cache = resolveNamed(_cacheOptions.CacheType.ToString(), default) as ICache;
}
/// <summary>
/// 获取数据范围缓存机构Id集合
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
public async Task<List<string>> GetDataScope(string userId)
{
var cacheKey = CommonConst.CACHE_KEY_DATASCOPE + $"{userId}";
return await _cache.GetAsync<List<string>>(cacheKey);
}
/// <summary>
/// 缓存数据范围机构Id集合
/// </summary>
/// <param name="userId"></param>
/// <param name="dataScopes"></param>
/// <returns></returns>
[NonAction]
public async Task SetDataScope(string userId, List<string> dataScopes)
{
var cacheKey = CommonConst.CACHE_KEY_DATASCOPE + $"{userId}";
await _cache.SetAsync(cacheKey, dataScopes);
}
/// <summary>
/// 获取菜单缓存
/// </summary>
/// <param name="userId"></param>
/// <param name="appCode"></param>
/// <returns></returns>
public async Task<List<AntDesignTreeNode>> GetMenu(string userId, string appCode)
{
var cacheKey = CommonConst.CACHE_KEY_MENU + $"{userId}-{appCode}";
return await _cache.GetAsync<List<AntDesignTreeNode>>(cacheKey);
}
/// <summary>
/// 缓存菜单
/// </summary>
/// <param name="userId"></param>
/// <param name="appCode"></param>
/// <param name="menus"></param>
/// <returns></returns>
[NonAction]
public async Task SetMenu(string userId, string appCode, List<AntDesignTreeNode> menus)
{
var cacheKey = CommonConst.CACHE_KEY_MENU + $"{userId}-{appCode}";
await _cache.SetAsync(cacheKey, menus);
}
/// <summary>
/// 获取权限缓存(按钮)
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
public async Task<List<string>> GetPermission(string userId)
{
var cacheKey = CommonConst.CACHE_KEY_PERMISSION + $"{userId}";
return await _cache.GetAsync<List<string>>(cacheKey);
}
/// <summary>
/// 缓存权限
/// </summary>
/// <param name="userId"></param>
/// <param name="permissions"></param>
/// <returns></returns>
[NonAction]
public async Task SetPermission(string userId, List<string> permissions)
{
var cacheKey = CommonConst.CACHE_KEY_PERMISSION + $"{userId}";
await _cache.SetAsync(cacheKey, permissions);
}
/// <summary>
/// 获取所有缓存关键字
/// </summary>
/// <returns></returns>
public List<string> GetAllCacheKeys()
{
const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
var entries = _cache.GetType().GetField("_entries", flags).GetValue(_cache);
if (entries.GetType().GetProperty("Keys").GetValue(entries) is not ICollection<object> cacheItems) return new List<string>();
return cacheItems.Where(u => !u.ToString().StartsWith("mini-profiler"))
.Select(u => u.ToString()).ToList();
}
/// <summary>
/// 删除指定关键字缓存
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public Task<bool> DelAsync(string key)
{
_cache.DelAsync(key);
return Task.FromResult(true);
}
/// <summary>
/// 删除某特征关键字缓存
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public Task<bool> DelByPatternAsync(string key)
{
_cache.DelByPatternAsync(key);
return Task.FromResult(true);
}
/// <summary>
/// 设置缓存
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public async Task<bool> SetAsync(string key, object value)
{
return await _cache.SetAsync(key, value);
}
/// <summary>
/// 获取缓存
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public async Task<string> GetAsync(string key)
{
return await _cache.GetAsync(key);
}
/// <summary>
/// 获取缓存
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key"></param>
/// <returns></returns>
public Task<T> GetAsync<T>(string key)
{
return _cache.GetAsync<T>(key);
}
}
}

View File

@@ -0,0 +1,207 @@
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Mapster;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
/// <summary>
/// 代码生成详细配置服务
/// </summary>
[ApiDescriptionSettings(Name = "CodeGenConfig", Order = 100)]
public class CodeGenConfigService : ICodeGenConfigService, IDynamicApiController, ITransient
{
private readonly IRepository<SysCodeGenConfig> _sysCodeGenConfigRep; // 代码生成详细配置仓储
public CodeGenConfigService(IRepository<SysCodeGenConfig> sysCodeGenConfigRep)
{
_sysCodeGenConfigRep = sysCodeGenConfigRep;
}
/// <summary>
/// 代码生成详细配置列表
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysCodeGenerateConfig/list")]
public async Task<List<CodeGenConfig>> List([FromQuery] CodeGenConfig input)
{
return await _sysCodeGenConfigRep.DetachedEntities.Where(u => u.CodeGenId == input.CodeGenId && u.WhetherCommon != YesOrNot.Y.ToString())
.Select(u => u.Adapt<CodeGenConfig>()).ToListAsync();
}
/// <summary>
/// 增加
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[NonAction]
public async Task Add(CodeGenConfig input)
{
var codeGenConfig = input.Adapt<SysCodeGenConfig>();
await codeGenConfig.InsertAsync();
}
/// <summary>
/// 删除
/// </summary>
/// <param name="codeGenId"></param>
/// <returns></returns>
[NonAction]
public async Task Delete(string codeGenId)
{
var codeGenConfigList = await _sysCodeGenConfigRep.Where(u => u.CodeGenId == codeGenId).ToListAsync();
codeGenConfigList.ForEach(u =>
{
u.Delete();
});
}
/// <summary>
/// 更新
/// </summary>
/// <param name="inputList"></param>
/// <returns></returns>
[HttpPost("/sysCodeGenerateConfig/edit")]
public async Task Update(List<CodeGenConfig> inputList)
{
if (inputList == null || inputList.Count < 1) return;
inputList.ForEach(u =>
{
var codeGenConfig = u.Adapt<SysCodeGenConfig>();
codeGenConfig.Update(true);
});
await Task.CompletedTask;
}
/// <summary>
/// 详情
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysCodeGenerateConfig/detail")]
public async Task<SysCodeGenConfig> Detail(CodeGenConfig input)
{
return await _sysCodeGenConfigRep.FirstOrDefaultAsync(u => u.Id == input.Id);
}
/// <summary>
/// 批量增加
/// </summary>
/// <param name="tableColumnOuputList"></param>
/// <param name="codeGenerate"></param>
[NonAction]
public void AddList(List<TableColumnOuput> tableColumnOuputList, SysCodeGen codeGenerate)
{
if (tableColumnOuputList == null) return;
foreach (var tableColumn in tableColumnOuputList)
{
var codeGenConfig = new SysCodeGenConfig();
var YesOrNo = YesOrNot.Y.ToString();
if (Convert.ToBoolean(tableColumn.ColumnKey))
{
YesOrNo = YesOrNot.N.ToString();
}
if (IsCommonColumn(tableColumn.ColumnName))
{
codeGenConfig.WhetherCommon = YesOrNot.Y.ToString();
YesOrNo = YesOrNot.N.ToString();
}
else
{
codeGenConfig.WhetherCommon = YesOrNot.N.ToString();
}
codeGenConfig.CodeGenId = codeGenerate.Id;
codeGenConfig.ColumnName = tableColumn.ColumnName;
codeGenConfig.ColumnComment = tableColumn.ColumnComment;
codeGenConfig.NetType = ConvertDataType(tableColumn.DataType);
codeGenConfig.WhetherRetract = YesOrNot.N.ToString();
codeGenConfig.WhetherRequired = YesOrNot.N.ToString();
codeGenConfig.QueryWhether = YesOrNo;
codeGenConfig.WhetherAddUpdate = YesOrNo;
codeGenConfig.WhetherTable = YesOrNo;
codeGenConfig.ColumnKey = tableColumn.ColumnKey;
codeGenConfig.DataType = tableColumn.DataType;
codeGenConfig.EffectType = DataTypeToEff(codeGenConfig.NetType);
codeGenConfig.QueryType = "=="; // QueryTypeEnum.eq.ToString();
codeGenConfig.InsertAsync();
}
}
/// <summary>
/// 数据类型转显示类型
/// </summary>
/// <param name="dataType"></param>
/// <returns></returns>
private static string DataTypeToEff(string dataType)
{
if (string.IsNullOrEmpty(dataType)) return "";
return dataType switch
{
"string" => "input",
"int" => "inputnumber",
"long" => "input",
"float" => "input",
"double" => "input",
"decimal" => "input",
"bool" => "switch",
"Guid" => "input",
"DateTime" => "datepicker",
"DateTimeOffset" => "datepicker",
_ => "input",
};
}
// 转换.NET数据类型
[NonAction]
public string ConvertDataType(string dataType)
{
if (string.IsNullOrEmpty(dataType)) return "";
if (dataType.StartsWith("System.Nullable"))
dataType = new Regex(@"(?i)(?<=\[)(.*)(?=\])").Match(dataType).Value; // 中括号[]里面值
switch (dataType)
{
case "System.Guid": return "Guid";
case "System.String": return "string";
case "System.Int32": return "int";
case "System.Int64": return "long";
case "System.Single": return "float";
case "System.Double": return "double";
case "System.Decimal": return "decimal";
case "System.Boolean": return "bool";
case "System.DateTime": return "DateTime";
case "System.DateTimeOffset": return "DateTimeOffset";
case "System.Byte": return "byte";
case "System.Byte[]": return "byte[]";
default:
break;
}
return dataType;
}
// 是否通用字段
private static bool IsCommonColumn(string columnName)
{
var columnList = new List<string>() { "CreatedTime", "UpdatedTime", "CreatedUserId", "CreatedUserName", "UpdatedUserId", "UpdatedUserName", "IsDeleted" };
return columnList.Contains(columnName);
}
}
}

View File

@@ -0,0 +1,348 @@
using Furion;
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using Furion.ViewEngine;
using Mapster;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Ewide.Core.Service.CodeGen
{
/// <summary>
/// 代码生成器服务
/// </summary>
[ApiDescriptionSettings(Name = "CodeGen", Order = 100)]
public class CodeGenService : ICodeGenService, IDynamicApiController, ITransient
{
private readonly IRepository<SysCodeGen> _sysCodeGenRep; // 代码生成器仓储
private readonly ICodeGenConfigService _codeGenConfigService;
private readonly IViewEngine _viewEngine;
private readonly IRepository<SysMenu> _sysMenuRep; // 菜单表仓储
public CodeGenService(IRepository<SysCodeGen> sysCodeGenRep,
ICodeGenConfigService codeGenConfigService,
IViewEngine viewEngine,
IRepository<SysMenu> sysMenuRep)
{
_sysCodeGenRep = sysCodeGenRep;
_codeGenConfigService = codeGenConfigService;
_viewEngine = viewEngine;
_sysMenuRep = sysMenuRep;
}
/// <summary>
/// 分页查询
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/codeGenerate/page")]
public async Task<dynamic> QueryCodeGenPageList([FromQuery] CodeGenInput input)
{
var tableName = !string.IsNullOrEmpty(input.TableName?.Trim());
var codeGens = await _sysCodeGenRep.DetachedEntities
.Where((tableName, u => EF.Functions.Like(u.TableName, $"%{input.TableName.Trim()}%")))
.ToPagedListAsync(input.PageNo, input.PageSize);
return XnPageResult<SysCodeGen>.PageResult(codeGens);
}
/// <summary>
/// 增加
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/codeGenerate/add")]
public async Task AddCodeGen(AddCodeGenInput input)
{
var isExist = await _sysCodeGenRep.DetachedEntities.AnyAsync(u => u.TableName == input.TableName);
if (isExist)
throw Oops.Oh(ErrorCode.D1400);
var codeGen = input.Adapt<SysCodeGen>();
var newCodeGen = await codeGen.InsertNowAsync();
// 加入配置表中
_codeGenConfigService.AddList(GetColumnList(input), newCodeGen.Entity);
}
/// <summary>
/// 删除
/// </summary>
/// <param name="inputs"></param>
/// <returns></returns>
[HttpPost("/codeGenerate/delete")]
public async Task DeleteCodeGen(List<DeleteCodeGenInput> inputs)
{
if (inputs == null || inputs.Count < 1) return;
var codeGenConfigTaskList = new List<Task>();
inputs.ForEach(u =>
{
_sysCodeGenRep.Delete(u.Id);
// 删除配置表中
codeGenConfigTaskList.Add(_codeGenConfigService.Delete(u.Id));
});
await Task.WhenAll(codeGenConfigTaskList);
}
/// <summary>
/// 更新
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/codeGenerate/edit")]
public async Task UpdateCodeGen(UpdateCodeGenInput input)
{
var isExist = await _sysCodeGenRep.DetachedEntities.AnyAsync(u => u.TableName == input.TableName && u.Id != input.Id);
if (isExist)
throw Oops.Oh(ErrorCode.D1400);
var codeGen = input.Adapt<SysCodeGen>();
await codeGen.UpdateAsync();
}
/// <summary>
/// 详情
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/codeGenerate/detail")]
public async Task<SysCodeGen> GetCodeGen([FromQuery] QueryCodeGenInput input)
{
return await _sysCodeGenRep.DetachedEntities.FirstOrDefaultAsync(u => u.Id == input.Id);
}
/// <summary>
/// 获取数据库表(实体)集合
/// </summary>
/// <returns></returns>
[HttpGet("/codeGenerate/InformationList")]
public List<TableOutput> GetTableList()
{
return Db.GetDbContext().Model.GetEntityTypes().Select(u => new TableOutput
{
TableName = u.GetDefaultTableName(),
TableComment = u.GetComment()
}).ToList();
}
/// <summary>
/// 获取数据表列(实体属性)集合
/// </summary>
/// <returns></returns>
[NonAction]
public List<TableColumnOuput> GetColumnList(AddCodeGenInput input)
{
var entityType = Db.GetDbContext().Model.GetEntityTypes().FirstOrDefault(u => u.ClrType.Name == input.TableName);
if (entityType == null) return null;
return entityType.GetProperties().Select(u => new TableColumnOuput
{
ColumnName = u.Name,
ColumnKey = u.IsKey().ToString(),
DataType = u.PropertyInfo.PropertyType.ToString(),
ColumnComment = u.GetComment()
}).ToList();
}
/// <summary>
/// 代码生成_本地项目
/// </summary>
/// <returns></returns>
[HttpPost("/codeGenerate/runLocal")]
public async void RunLocal(SysCodeGen input)
{
var templatePathList = GetTemplatePathList();
var targetPathList = GetTargetPathList(input);
for (var i = 0; i < templatePathList.Count; i++)
{
var tContent = File.ReadAllText(templatePathList[i]);
var tableFieldList = await _codeGenConfigService.List(new CodeGenConfig() { CodeGenId = input.Id }); // 字段集合
if (i >= 4) // 适应前端首字母小写
{
tableFieldList.ForEach(u =>
{
u.ColumnName = u.ColumnName.Substring(0, 1).ToLower() + u.ColumnName.Substring(1);
});
}
var queryWhetherList = tableFieldList.Where(u => u.QueryWhether == YesOrNot.Y.ToString()).ToList(); // 前端查询集合
var tResult = _viewEngine.RunCompileFromCached(tContent, new
{
input.AuthorName,
input.BusName,
input.NameSpace,
ClassName = input.TableName,
QueryWhetherList = queryWhetherList,
TableField = tableFieldList
});
var dirPath = new DirectoryInfo(targetPathList[i]).Parent.FullName;
if (!Directory.Exists(dirPath))
Directory.CreateDirectory(dirPath);
File.WriteAllText(targetPathList[i], tResult, Encoding.UTF8);
}
await AddMenu(input.TableName, input.BusName);
}
private async Task AddMenu(string className, string busName)
{
// 先删除该表已生成的菜单列表
var menus = await _sysMenuRep.DetachedEntities.Where(u => u.Code.StartsWith("dilon_" + className.ToLower())).ToListAsync();
menus.ForEach(u =>
{
u.Delete();
});
var emptyGuid = System.Guid.Empty.ToString();
// 目录
var menuType0 = new SysMenu
{
Pid = emptyGuid,
Pids = "["+ emptyGuid + "],",
Name = busName + "管理",
Code = "dilon_" + className.ToLower(),
Type = 1,
Icon = "robot",
Router = "/" + className.ToLower(),
Component = "PageView",
Application = "busapp"
};
var pid0 = _sysMenuRep.InsertNowAsync(menuType0).GetAwaiter().GetResult().Entity.Id;
// 菜单
var menuType1 = new SysMenu
{
Pid = pid0,
Pids = "[0],[" + pid0 + "],",
Name = busName + "管理",
Code = "dilon_" + className.ToLower() + "_mgr",
Type = 1,
Router = "/" + className.ToLower(),
Component = "main/" + className + "/index",
Application = "busapp",
OpenType = 1
};
var pid1 = _sysMenuRep.InsertNowAsync(menuType1).GetAwaiter().GetResult().Entity.Id;
// 按钮-page
var menuType2 = new SysMenu
{
Pid = pid1,
Pids = "[0],[" + pid0 + "],[" + pid1 + "],",
Name = busName + "查询",
Code = "dilon_" + className.ToLower() + "_mgr_page",
Type = 2,
Permission = className + ":page",
Application = "busapp",
}.InsertAsync();
// 按钮-detail
var menuType2_1 = new SysMenu
{
Pid = pid1,
Pids = "[0],[" + pid0 + "],[" + pid1 + "],",
Name = busName + "详情",
Code = "dilon_" + className.ToLower() + "_mgr_detail",
Type = 2,
Permission = className + ":detail",
Application = "busapp",
}.InsertAsync();
// 按钮-add
var menuType2_2 = new SysMenu
{
Pid = pid1,
Pids = "[0],[" + pid0 + "],[" + pid1 + "],",
Name = busName + "增加",
Code = "dilon_" + className.ToLower() + "_mgr_add",
Type = 2,
Permission = className + ":add",
Application = "busapp",
}.InsertAsync();
// 按钮-delete
var menuType2_3 = new SysMenu
{
Pid = pid1,
Pids = "[0],[" + pid0 + "],[" + pid1 + "],",
Name = busName + "删除",
Code = "dilon_" + className.ToLower() + "_mgr_delete",
Type = 2,
Permission = className + ":delete",
Application = "busapp",
}.InsertAsync();
// 按钮-edit
var menuType2_4 = new SysMenu
{
Pid = pid1,
Pids = "[0],[" + pid0 + "],[" + pid1 + "],",
Name = busName + "编辑",
Code = "dilon_" + className.ToLower() + "_mgr_edit",
Type = 2,
Permission = className + ":edit",
Application = "busapp",
}.InsertAsync();
}
/// <summary>
/// 获取模板文件路径集合
/// </summary>
/// <returns></returns>
private List<string> GetTemplatePathList()
{
var templatePath = App.WebHostEnvironment.WebRootPath + @"\Template\";
return new List<string>() {
templatePath + "Service.cs.vm",
templatePath + "IService.cs.vm",
templatePath + "Input.cs.vm",
templatePath + "Output.cs.vm",
templatePath + "index.vue.vm",
templatePath + "addForm.vue.vm",
templatePath + "editForm.vue.vm",
templatePath + "manage.js.vm",
};
}
/// <summary>
/// 设置生成文件路径
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
private List<string> GetTargetPathList(SysCodeGen input)
{
var backendPath = new DirectoryInfo(App.WebHostEnvironment.ContentRootPath).Parent.FullName + @"\Dilon.Application\Service\" + input.TableName + @"\";
var servicePath = backendPath + input.TableName + "Service.cs";
var iservicePath = backendPath + "I" + input.TableName + "Service.cs";
var inputPath = backendPath + @"Dto\" + input.TableName + "Input.cs";
var outputPath = backendPath + @"Dto\" + input.TableName + "Output.cs";
var frontendPath = new DirectoryInfo(App.WebHostEnvironment.ContentRootPath).Parent.Parent.FullName + @"\frontend\src\views\main\";
var indexPath = frontendPath + input.TableName + @"\index.vue";
var addFormPath = frontendPath + input.TableName + @"\addForm.vue";
var editFormPath = frontendPath + input.TableName + @"\editForm.vue";
var apiJsPath = new DirectoryInfo(App.WebHostEnvironment.ContentRootPath).Parent.Parent.FullName + @"\frontend\src\api\modular\main\" + input.TableName + "Manage.js";
return new List<string>() {
servicePath,
iservicePath,
inputPath,
outputPath,
indexPath,
addFormPath,
editFormPath,
apiJsPath
};
}
}
}

View File

@@ -0,0 +1,88 @@
namespace Ewide.Core.Service
{
/// <summary>
/// 代码生成详细配置参数
/// </summary>
public class CodeGenConfig
{
/// <summary>
/// 主键
/// </summary>
public string Id { get; set; }
/// <summary>
/// 代码生成主表ID
/// </summary>
public string CodeGenId { get; set; }
/// <summary>
/// 数据库字段名
/// </summary>
public string ColumnName { get; set; }
/// <summary>
/// 字段描述
/// </summary>
public string ColumnComment { get; set; }
/// <summary>
/// .NET类型
/// </summary>
public string NetType { get; set; }
/// <summary>
/// 作用类型(字典)
/// </summary>
public string EffectType { get; set; }
/// <summary>
/// 字典code
/// </summary>
public string DictTypeCode { get; set; }
/// <summary>
/// 列表是否缩进(字典)
/// </summary>
public string WhetherRetract { get; set; }
/// <summary>
/// 是否必填(字典)
/// </summary>
public string WhetherRequired { get; set; }
/// <summary>
/// 是否是查询条件
/// </summary>
public string QueryWhether { get; set; }
/// <summary>
/// 查询方式
/// </summary>
public string QueryType { get; set; }
/// <summary>
/// 列表显示
/// </summary>
public string WhetherTable { get; set; }
/// <summary>
/// 增改
/// </summary>
public string WhetherAddUpdate { get; set; }
/// <summary>
/// 主外键
/// </summary>
public string ColumnKey { get; set; }
/// <summary>
/// 数据库中类型(物理类型)
/// </summary>
public string DataType { get; set; }
/// <summary>
/// 是否是通用字段
/// </summary>
public string WhetherCommon { get; set; }
}
}

View File

@@ -0,0 +1,124 @@
using System.ComponentModel.DataAnnotations;
namespace Ewide.Core.Service
{
/// <summary>
/// 代码生成参数类
/// </summary>
public class CodeGenInput : XnInputBase
{
/// <summary>
/// 作者姓名
/// </summary>
public virtual string AuthorName { get; set; }
/// <summary>
/// 类名
/// </summary>
public virtual string ClassName { get; set; }
/// <summary>
/// 是否移除表前缀
/// </summary>
public virtual string TablePrefix { get; set; }
/// <summary>
/// 生成方式
/// </summary>
public virtual string GenerateType { get; set; }
/// <summary>
/// 数据库表名
/// </summary>
public virtual string TableName { get; set; }
/// <summary>
/// 命名空间
/// </summary>
public virtual string NameSpace { get; set; }
/// <summary>
/// 业务名(业务代码包名称)
/// </summary>
public virtual string BusName { get; set; }
/// <summary>
/// 功能名(数据库表名称)
/// </summary>
public virtual string TableComment { get; set; }
}
public class AddCodeGenInput : CodeGenInput
{
/// <summary>
/// 数据库表名
/// </summary>
[Required(ErrorMessage = "数据库表名不能为空")]
public override string TableName { get; set; }
/// <summary>
/// 业务名(业务代码包名称)
/// </summary>
[Required(ErrorMessage = "业务名不能为空")]
public override string BusName { get; set; }
/// <summary>
/// 命名空间
/// </summary>
[Required(ErrorMessage = "命名空间不能为空")]
public override string NameSpace { get; set; }
/// <summary>
/// 作者姓名
/// </summary>
[Required(ErrorMessage = "作者姓名不能为空")]
public override string AuthorName { get; set; }
///// <summary>
///// 类名
///// </summary>
//[Required(ErrorMessage = "类名不能为空")]
//public override string ClassName { get; set; }
///// <summary>
///// 是否移除表前缀
///// </summary>
//[Required(ErrorMessage = "是否移除表前缀不能为空")]
//public override string TablePrefix { get; set; }
/// <summary>
/// 生成方式
/// </summary>
[Required(ErrorMessage = "生成方式不能为空")]
public override string GenerateType { get; set; }
///// <summary>
///// 功能名(数据库表名称)
///// </summary>
//[Required(ErrorMessage = "数据库表名不能为空")]
//public override string TableComment { get; set; }
}
public class DeleteCodeGenInput
{
/// <summary>
/// 代码生成器Id
/// </summary>
[Required(ErrorMessage = "代码生成器Id不能为空")]
public string Id { get; set; }
}
public class UpdateCodeGenInput : CodeGenInput
{
/// <summary>
/// 代码生成器Id
/// </summary>
[Required(ErrorMessage = "代码生成器Id不能为空")]
public string Id { get; set; }
}
public class QueryCodeGenInput : DeleteCodeGenInput
{
}
}

View File

@@ -0,0 +1,53 @@
namespace Ewide.Core.Service
{
/// <summary>
/// 代码生成参数类
/// </summary>
public class CodeGenOutput
{
/// <summary>
/// 代码生成器Id
/// </summary>
public long Id { get; set; }
/// <summary>
/// 作者姓名
/// </summary>
public string AuthorName { get; set; }
/// <summary>
/// 类名
/// </summary>
public string ClassName { get; set; }
/// <summary>
/// 是否移除表前缀
/// </summary>
public string TablePrefix { get; set; }
/// <summary>
/// 生成方式
/// </summary>
public string GenerateType { get; set; }
/// <summary>
/// 数据库表名
/// </summary>
public string TableName { get; set; }
/// <summary>
/// 包名
/// </summary>
public string PackageName { get; set; }
/// <summary>
/// 业务名(业务代码包名称)
/// </summary>
public string BusName { get; set; }
/// <summary>
/// 功能名(数据库表名称)
/// </summary>
public string TableComment { get; set; }
}
}

View File

@@ -0,0 +1,28 @@
namespace Ewide.Core.Service
{
/// <summary>
/// 数据库表列
/// </summary>
public class TableColumnOuput
{
/// <summary>
/// 字段名
/// </summary>
public string ColumnName { get; set; }
/// <summary>
/// 数据库中类型
/// </summary>
public string DataType { get; set; }
/// <summary>
/// 字段描述
/// </summary>
public string ColumnComment { get; set; }
/// <summary>
/// 主外键
/// </summary>
public string ColumnKey { get; set; }
}
}

View File

@@ -0,0 +1,28 @@
namespace Ewide.Core.Service
{
/// <summary>
/// 数据库表列表参数
/// </summary>
public class TableOutput
{
/// <summary>
/// 表名(字母形式的)
/// </summary>
public string TableName { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public string CreateTime { get; set; }
/// <summary>
/// 更新时间
/// </summary>
public string UpdateTime { get; set; }
/// <summary>
/// 表名称描述(注释)(功能名)
/// </summary>
public string TableComment { get; set; }
}
}

View File

@@ -0,0 +1,52 @@
using System.Collections.Generic;
namespace Ewide.Core.Service
{
public class XnCodeGenOutput
{
/// <summary>
/// 作者姓名
/// </summary>
public string AuthorName { get; set; }
/// <summary>
/// 是否移除表前缀
/// </summary>
public string TablePrefix { get; set; }
/// <summary>
/// 生成方式
/// </summary>
public string GenerateType { get; set; }
/// <summary>
/// 数据库表名
/// </summary>
public string TableName { get; set; }
/// <summary>
/// 数据库表名(经过组装的)
/// </summary>
public string TableNameAss { get; set; }
/// <summary>
/// 代码包名
/// </summary>
public string PackageName { get; set; }
/// <summary>
/// 生成时间string类型的
/// </summary>
public string CreateTimestring { get; set; }
/// <summary>
/// 数据库表中字段集合
/// </summary>
public List<SysCodeGenConfig> ConfigList { get; set; }
/// <summary>
/// 业务名
/// </summary>
public string BusName { get; set; }
}
}

View File

@@ -0,0 +1,17 @@
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
public interface ICodeGenConfigService
{
Task Add(CodeGenConfig input);
void AddList(List<TableColumnOuput> tableColumnOuputList, SysCodeGen codeGenerate);
string ConvertDataType(string dataType);
Task Delete(string codeGenId);
Task<SysCodeGenConfig> Detail(CodeGenConfig input);
Task<List<CodeGenConfig>> List([FromQuery] CodeGenConfig input);
Task Update(List<CodeGenConfig> inputList);
}
}

View File

@@ -0,0 +1,18 @@
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Ewide.Core.Service.CodeGen
{
public interface ICodeGenService
{
Task AddCodeGen(AddCodeGenInput input);
Task DeleteCodeGen(List<DeleteCodeGenInput> inputs);
Task<SysCodeGen> GetCodeGen([FromQuery] QueryCodeGenInput input);
List<TableColumnOuput> GetColumnList(AddCodeGenInput input);
List<TableOutput> GetTableList();
Task<dynamic> QueryCodeGenPageList([FromQuery] CodeGenInput input);
void RunLocal(SysCodeGen input);
Task UpdateCodeGen(UpdateCodeGenInput input);
}
}

View File

@@ -0,0 +1,83 @@
using System.ComponentModel.DataAnnotations;
namespace Ewide.Core.Service
{
/// <summary>
/// 参数配置
/// </summary>
public class ConfigInput : PageInputBase
{
/// <summary>
/// 名称
/// </summary>
public virtual string Name { get; set; }
/// <summary>
/// 编码
/// </summary>
public virtual string Code { get; set; }
/// <summary>
/// 属性值
/// </summary>
public virtual string Value { get; set; }
/// <summary>
/// 是否是系统参数Y-是N-否)
/// </summary>
public virtual string SysFlag { get; set; }
/// <summary>
/// 备注
/// </summary>
public virtual string Remark { get; set; }
/// <summary>
/// 状态(字典 0正常 1停用 2删除
/// </summary>
public virtual int Status { get; set; }
/// <summary>
/// 常量所属分类的编码,来自于“常量的分类”字典
/// </summary>
public virtual string GroupCode { get; set; }
}
public class AddConfigInput : ConfigInput
{
/// <summary>
/// 名称
/// </summary>
[Required(ErrorMessage = "参数名称不能为空")]
public override string Name { get; set; }
/// <summary>
/// 编码
/// </summary>
[Required(ErrorMessage = "参数编码不能为空")]
public override string Code { get; set; }
}
public class DeleteConfigInput
{
/// <summary>
/// 应用Id
/// </summary>
[Required(ErrorMessage = "参数Id不能为空")]
public string Id { get; set; }
}
public class UpdateConfigInput : AddConfigInput
{
/// <summary>
/// 应用Id
/// </summary>
[Required(ErrorMessage = "应用Id不能为空")]
public string Id { get; set; }
}
public class QueryConfigInput : DeleteConfigInput
{
}
}

View File

@@ -0,0 +1,18 @@
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
public interface ISysConfigService
{
Task AddConfig(AddConfigInput input);
Task DeleteConfig(DeleteConfigInput input);
Task<SysConfig> GetConfig([FromQuery] QueryConfigInput input);
Task<dynamic> GetConfigList([FromQuery] ConfigInput input);
Task<dynamic> QueryConfigPageList([FromQuery] ConfigInput input);
Task UpdateConfig(UpdateConfigInput input);
Task<bool> GetDemoEnvFlag();
Task<bool> GetCaptchaOpenFlag();
Task UpdateConfigCache(string code, object value);
}
}

View File

@@ -0,0 +1,168 @@
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using Mapster;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
/// <summary>
/// 系统参数配置服务
/// </summary>
[ApiDescriptionSettings(Name = "Config", Order = 100)]
public class SysConfigService : ISysConfigService, IDynamicApiController, ITransient
{
private readonly IRepository<SysConfig> _sysConfigRep; // 参数配置表仓储
private readonly ISysCacheService _sysCacheService;
public SysConfigService(IRepository<SysConfig> sysConfigRep, ISysCacheService sysCacheService)
{
_sysConfigRep = sysConfigRep;
_sysCacheService = sysCacheService;
}
/// <summary>
/// 分页获取系统参数配置
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysConfig/page")]
public async Task<dynamic> QueryConfigPageList([FromQuery] ConfigInput input)
{
var name = !string.IsNullOrEmpty(input.Name?.Trim());
var code = !string.IsNullOrEmpty(input.Code?.Trim());
var groupCode = !string.IsNullOrEmpty(input.GroupCode?.Trim());
var configs = await _sysConfigRep.DetachedEntities
.Where((name, u => EF.Functions.Like(u.Name, $"%{input.Name.Trim()}%")),
(code, u => EF.Functions.Like(u.Code, $"%{input.Code.Trim()}%")),
(groupCode, u => EF.Functions.Like(u.Code, $"%{input.GroupCode.Trim()}%")))
.Where(u => u.Status != CommonStatus.DELETED).OrderBy(u => u.GroupCode)
.ToPagedListAsync(input.PageNo, input.PageSize);
return XnPageResult<SysConfig>.PageResult(configs);
}
/// <summary>
/// 获取系统参数配置列表
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysConfig/list")]
public async Task<dynamic> GetConfigList([FromQuery] ConfigInput input)
{
return await _sysConfigRep.DetachedEntities.Where(u => u.Status != CommonStatus.DELETED).ToListAsync();
}
/// <summary>
/// 增加系统参数配置
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysConfig/add")]
public async Task AddConfig(AddConfigInput input)
{
var isExist = await _sysConfigRep.DetachedEntities.AnyAsync(u => u.Name == input.Name || u.Code == input.Code);
if (isExist)
throw Oops.Oh(ErrorCode.D9000);
var config = input.Adapt<SysConfig>();
await config.InsertAsync();
}
/// <summary>
/// 删除系统参数配置
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysConfig/delete")]
public async Task DeleteConfig(DeleteConfigInput input)
{
var config = await _sysConfigRep.FirstOrDefaultAsync(u => u.Id == input.Id);
// 禁止删除系统参数
if (config.SysFlag == YesOrNot.Y.ToString())
throw Oops.Oh(ErrorCode.D9001);
await config.DeleteAsync();
}
/// <summary>
/// 更新系统参数配置
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysConfig/edit")]
public async Task UpdateConfig(UpdateConfigInput input)
{
var isExist = await _sysConfigRep.DetachedEntities.AnyAsync(u => (u.Name == input.Name || u.Code == input.Code) && u.Id != input.Id);
if (isExist)
throw Oops.Oh(ErrorCode.D9000);
var config = input.Adapt<SysConfig>();
await config.UpdateAsync(ignoreNullValues: true);
}
/// <summary>
/// 获取系统参数配置
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysConfig/detail")]
public async Task<SysConfig> GetConfig([FromQuery] QueryConfigInput input)
{
return await _sysConfigRep.DetachedEntities.FirstOrDefaultAsync(u => u.Id == input.Id);
}
/// <summary>
/// 获取配置信息
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
private async Task<dynamic> GetConfigCache(string code)
{
var value = await _sysCacheService.GetAsync(code);
if (string.IsNullOrEmpty(value))
{
var config = await _sysConfigRep.DetachedEntities.FirstOrDefaultAsync(u => u.Code == code);
value = config != null ? config.Value : "";
await _sysCacheService.SetAsync(code, value);
}
return value;
}
/// <summary>
/// 更新配置缓存
/// </summary>
/// <param name="code"></param>
/// <param name="value"></param>
/// <returns></returns>
public async Task UpdateConfigCache(string code, object value)
{
await _sysCacheService.SetAsync(code, value);
}
/// <summary>
/// 获取演示环境开关是否开启默认为false
/// </summary>
/// <returns></returns>
[NonAction]
public async Task<bool> GetDemoEnvFlag()
{
var value = await GetConfigCache("DILON_DEMO_ENV_FLAG");
return bool.Parse(value);
}
/// <summary>
/// 获取验证码开关标识
/// </summary>
/// <returns></returns>
public async Task<bool> GetCaptchaOpenFlag()
{
var value = await GetConfigCache("DILON_CAPTCHA_OPEN");
return bool.Parse(value);
}
}
}

View File

@@ -0,0 +1,94 @@
using Furion.DataValidation;
using System.ComponentModel.DataAnnotations;
namespace Ewide.Core.Service
{
/// <summary>
/// 字典值参数
/// </summary>
public class DictDataInput : PageInputBase
{
/// <summary>
/// 字典类型Id
/// </summary>
public virtual string TypeId { get; set; }
/// <summary>
/// 值
/// </summary>
public virtual string Value { get; set; }
/// <summary>
/// 编码
/// </summary>
public virtual string Code { get; set; }
/// <summary>
/// 排序
/// </summary>
public virtual int Sort { get; set; }
/// <summary>
/// 备注
/// </summary>
public virtual string Remark { get; set; }
/// <summary>
/// 状态(字典 0正常 1停用 2删除
/// </summary>
public virtual CommonStatus Status { get; set; }
}
public class QueryDictDataListInput
{
/// <summary>
/// 字典类型Id
/// </summary>
[Required(ErrorMessage = "字典类型Id不能为空"), DataValidation(ValidationTypes.Numeric)]
public string TypeId { get; set; }
}
public class AddDictDataInput : DictDataInput
{
/// <summary>
/// 字典类型Id
/// </summary>
[Required(ErrorMessage = "字典类型Id不能为空"), DataValidation(ValidationTypes.Numeric)]
public override string TypeId { get; set; }
/// <summary>
/// 值
/// </summary>
[Required(ErrorMessage = "字典值不能为空")]
public override string Value { get; set; }
/// <summary>
/// 编码
/// </summary>
[Required(ErrorMessage = "字典值编码不能为空")]
public override string Code { get; set; }
}
public class DeleteDictDataInput
{
/// <summary>
/// 字典值Id
/// </summary>
[Required(ErrorMessage = "字典值Id不能为空"), DataValidation(ValidationTypes.Numeric)]
public string Id { get; set; }
}
public class UpdateDictDataInput : AddDictDataInput
{
/// <summary>
/// 字典值Id
/// </summary>
[Required(ErrorMessage = "字典值Id不能为空"), DataValidation(ValidationTypes.Numeric)]
public string Id { get; set; }
}
public class QueryDictDataInput : DeleteDictDataInput
{
}
}

View File

@@ -0,0 +1,13 @@
namespace Ewide.Core.Service
{
/// <summary>
/// 字典值参数
/// </summary>
public class DictDataOutput : DictDataInput
{
/// <summary>
/// 字典Id
/// </summary>
public virtual long Id { get; set; }
}
}

View File

@@ -0,0 +1,35 @@
using System.Collections.Generic;
namespace Ewide.Core.Service
{
/// <summary>
/// 字典类型与字典值构造的树
/// </summary>
public class DictTreeOutput
{
/// <summary>
/// Id
/// </summary>
public string Id { get; set; }
/// <summary>
/// 父Id
/// </summary>
public string Pid { get; set; }
/// <summary>
/// 编码-对应字典值的编码
/// </summary>
public string Code { get; set; }
/// <summary>
/// 名称-对应字典值的value
/// </summary>
public string Name { get; set; }
/// <summary>
/// 子节点集合
/// </summary>
public List<DictTreeOutput> Children { get; set; } = new List<DictTreeOutput>();
}
}

View File

@@ -0,0 +1,82 @@
using System.ComponentModel.DataAnnotations;
namespace Ewide.Core.Service
{
/// <summary>
/// 字典类型参数
/// </summary>
public class DictTypeInput : PageInputBase
{
/// <summary>
/// 名称
/// </summary>
public virtual string Name { get; set; }
/// <summary>
/// 编码
/// </summary>
public virtual string Code { get; set; }
/// <summary>
/// 排序
/// </summary>
public virtual int Sort { get; set; }
/// <summary>
/// 备注
/// </summary>
public virtual string Remark { get; set; }
/// <summary>
/// 状态(字典 0正常 1停用 2删除
/// </summary>
public virtual CommonStatus Status { get; set; }
}
public class AddDictTypeInput : DictTypeInput
{
/// <summary>
/// 名称
/// </summary>
[Required(ErrorMessage = "字典类型名称不能为空")]
public override string Name { get; set; }
/// <summary>
/// 编码
/// </summary>
[Required(ErrorMessage = "字典类型编码不能为空")]
public override string Code { get; set; }
}
public class DeleteDictTypeInput
{
/// <summary>
/// 编号Id
/// </summary>
[Required(ErrorMessage = "字典类型Id不能为空")]
public string Id { get; set; }
}
public class UpdateDictTypeInput : AddDictTypeInput
{
/// <summary>
/// Id
/// </summary>
[Required(ErrorMessage = "字典类型Id不能为空")]
public string Id { get; set; }
}
public class DropDownDictTypeInput
{
/// <summary>
/// 编码
/// </summary>
[Required(ErrorMessage = "字典类型编码不能为空")]
public string Code { get; set; }
}
public class QueryDictTypeInfoInput : DeleteDictTypeInput
{
}
}

View File

@@ -0,0 +1,18 @@
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
public interface ISysDictDataService
{
Task AddDictData(AddDictDataInput input);
Task ChangeDictDataStatus(UpdateDictDataInput input);
Task DeleteByTypeId(string dictTypeId);
Task DeleteDictData(DeleteDictDataInput input);
Task<dynamic> GetDictData([FromQuery] QueryDictDataInput input);
Task<dynamic> GetDictDataList([FromQuery] QueryDictDataListInput input);
Task<dynamic> GetDictDataListByDictTypeId(string dictTypeId);
Task<dynamic> QueryDictDataPageList([FromQuery] DictDataInput input);
Task UpdateDictData(UpdateDictDataInput input);
}
}

View File

@@ -0,0 +1,19 @@
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
public interface ISysDictTypeService
{
Task AddDictType(AddDictTypeInput input);
Task ChangeDictTypeStatus(UpdateDictTypeInput input);
Task DeleteDictType(DeleteDictTypeInput input);
Task<List<DictTreeOutput>> GetDictTree();
Task<dynamic> GetDictType([FromQuery] QueryDictTypeInfoInput input);
Task<dynamic> GetDictTypeDropDown([FromQuery] DropDownDictTypeInput input);
Task<dynamic> GetDictTypeList();
Task<dynamic> QueryDictTypePageList([FromQuery] DictTypeInput input);
Task UpdateDictType(UpdateDictTypeInput input);
}
}

View File

@@ -0,0 +1,164 @@
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using Mapster;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
/// <summary>
/// 字典值服务
/// </summary>
[ApiDescriptionSettings(Name = "DictData", Order = 100)]
public class SysDictDataService : ISysDictDataService, IDynamicApiController, ITransient
{
private readonly IRepository<SysDictData> _sysDictDataRep; // 字典类型表仓储
public SysDictDataService(IRepository<SysDictData> sysDictDataRep)
{
_sysDictDataRep = sysDictDataRep;
}
/// <summary>
/// 分页查询字典值
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysDictData/page")]
public async Task<dynamic> QueryDictDataPageList([FromQuery] DictDataInput input)
{
var code = !string.IsNullOrEmpty(input.Code?.Trim());
var value = !string.IsNullOrEmpty(input.Value?.Trim());
var dictDatas = await _sysDictDataRep.DetachedEntities
.Where(u => u.TypeId == input.TypeId)
.Where((code, u => EF.Functions.Like(u.Code, $"%{input.Code.Trim()}%")),
(value, u => EF.Functions.Like(u.Value, $"%{input.Value.Trim()}%")))
.Where(u => u.Status != CommonStatus.DELETED).OrderBy(u => u.Sort)
.Select(u => u.Adapt<DictDataOutput>())
.ToPagedListAsync(input.PageNo, input.PageSize);
return XnPageResult<DictDataOutput>.PageResult(dictDatas);
}
/// <summary>
/// 获取某个字典类型下字典值列表
/// </summary>
/// <returns></returns>
[HttpGet("/sysDictData/list")]
public async Task<dynamic> GetDictDataList([FromQuery] QueryDictDataListInput input)
{
return await _sysDictDataRep.DetachedEntities.Where(u => u.TypeId == input.TypeId).Where(u => u.Status != CommonStatus.DELETED).OrderBy(u => u.Sort).ToListAsync();
}
/// <summary>
/// 增加字典值
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysDictData/add")]
public async Task AddDictData(AddDictDataInput input)
{
var isExist = await _sysDictDataRep.AnyAsync(u => (u.Code == input.Code || u.Value == input.Value) && u.TypeId == input.TypeId, false);
if (isExist) throw Oops.Oh(ErrorCode.D3003);
var dictData = input.Adapt<SysDictData>();
await _sysDictDataRep.InsertAsync(dictData);
}
/// <summary>
/// 删除字典值
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysDictData/delete")]
public async Task DeleteDictData(DeleteDictDataInput input)
{
var dictData = await _sysDictDataRep.FirstOrDefaultAsync(u => u.Id == input.Id);
if (dictData == null) throw Oops.Oh(ErrorCode.D3004);
await dictData.DeleteAsync();
}
/// <summary>
/// 更新字典值
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysDictData/edit")]
public async Task UpdateDictData(UpdateDictDataInput input)
{
var isExist = await _sysDictDataRep.AnyAsync(u => u.Id == input.Id, false);
if (!isExist) throw Oops.Oh(ErrorCode.D3004);
// 排除自己并且判断与其他是否相同
isExist = await _sysDictDataRep.AnyAsync(u => (u.Value == input.Value || u.Code == input.Code) && u.TypeId == input.TypeId && u.Id != input.Id, false);
if (isExist) throw Oops.Oh(ErrorCode.D3003);
var dictData = input.Adapt<SysDictData>();
await _sysDictDataRep.UpdateAsync(dictData, ignoreNullValues: true);
}
/// <summary>
/// 字典值详情
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysDictData/detail")]
public async Task<dynamic> GetDictData([FromQuery] QueryDictDataInput input)
{
return await _sysDictDataRep.FirstOrDefaultAsync(u => u.Id == input.Id, false);
}
/// <summary>
/// 修改字典值状态
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysDictData/changeStatus")]
public async Task ChangeDictDataStatus(UpdateDictDataInput input)
{
var dictData = await _sysDictDataRep.FirstOrDefaultAsync(u => u.Id == input.Id);
if (dictData == null) throw Oops.Oh(ErrorCode.D3004);
if (!Enum.IsDefined(typeof(CommonStatus), input.Status))
throw Oops.Oh(ErrorCode.D3005);
dictData.Status = input.Status;
}
/// <summary>
/// 根据字典类型Id获取字典值集合
/// </summary>
/// <param name="dictTypeId"></param>
/// <returns></returns>
[NonAction]
public async Task<dynamic> GetDictDataListByDictTypeId(string dictTypeId)
{
return await _sysDictDataRep.DetachedEntities.Where(u => u.SysDictType.Id == dictTypeId)
.Where(u => u.Status != CommonStatus.DELETED).OrderBy(u => u.Sort)
.Select(u => new
{
u.Code,
u.Value
}).ToListAsync();
}
/// <summary>
/// 删除字典下所有值
/// </summary>
/// <param name="dictTypeId"></param>
[NonAction]
public async Task DeleteByTypeId(string dictTypeId)
{
var dictDatas = await _sysDictDataRep.Where(u => u.TypeId == dictTypeId).ToListAsync();
dictDatas.ForEach(u =>
{
u.Delete();
});
}
}
}

View File

@@ -0,0 +1,175 @@
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using Mapster;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
/// <summary>
/// 字典类型服务
/// </summary>
[ApiDescriptionSettings(Name = "DictType", Order = 100)]
public class SysDictTypeService : ISysDictTypeService, IDynamicApiController, ITransient
{
private readonly IRepository<SysDictType> _sysDictTypeRep; // 字典类型表仓储
private readonly ISysDictDataService _sysDictDataService;
public SysDictTypeService(ISysDictDataService sysDictDataService,
IRepository<SysDictType> sysDictTypeRep)
{
_sysDictDataService = sysDictDataService;
_sysDictTypeRep = sysDictTypeRep;
}
/// <summary>
/// 分页查询字典类型
/// </summary>
/// <returns></returns>
[HttpGet("/sysDictType/page")]
public async Task<dynamic> QueryDictTypePageList([FromQuery] DictTypeInput input)
{
var code = !string.IsNullOrEmpty(input.Code?.Trim());
var name = !string.IsNullOrEmpty(input.Name?.Trim());
var dictTypes = await _sysDictTypeRep.DetachedEntities
.Where((code, u => EF.Functions.Like(u.Code, $"%{input.Code.Trim()}%")),
(name, u => EF.Functions.Like(u.Name, $"%{input.Name.Trim()}%")))
.Where(u => u.Status != CommonStatus.DELETED).OrderBy(u => u.Sort)
.ToPagedListAsync(input.PageNo, input.PageSize);
return XnPageResult<SysDictType>.PageResult(dictTypes);
}
/// <summary>
/// 获取字典类型列表
/// </summary>
/// <returns></returns>
[HttpGet("/sysDictType/list")]
public async Task<dynamic> GetDictTypeList()
{
return await _sysDictTypeRep.DetachedEntities.Where(u => u.Status != CommonStatus.DELETED).ToListAsync();
}
/// <summary>
/// 获取字典类型下所有字典值
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[AllowAnonymous]
[HttpGet("/sysDictType/dropDown")]
public async Task<dynamic> GetDictTypeDropDown([FromQuery] DropDownDictTypeInput input)
{
var dictType = await _sysDictTypeRep.FirstOrDefaultAsync(u => u.Code == input.Code, false);
if (dictType == null) throw Oops.Oh(ErrorCode.D3000);
return await _sysDictDataService.GetDictDataListByDictTypeId(dictType.Id);
}
/// <summary>
/// 添加字典类型
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysDictType/add")]
public async Task AddDictType(AddDictTypeInput input)
{
var isExist = await _sysDictTypeRep.AnyAsync(u => u.Name == input.Name || u.Code == input.Code, false);
if (isExist) throw Oops.Oh(ErrorCode.D3001);
var dictType = input.Adapt<SysDictType>();
await _sysDictTypeRep.InsertAsync(dictType);
}
/// <summary>
/// 删除字典类型
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysDictType/delete")]
public async Task DeleteDictType(DeleteDictTypeInput input)
{
var dictType = await _sysDictTypeRep.FirstOrDefaultAsync(u => u.Id == input.Id);
if (dictType == null) throw Oops.Oh(ErrorCode.D3000);
var dictDatas = await _sysDictDataService.GetDictDataListByDictTypeId(input.Id); //_sysDictDataService.DeleteByTypeId(input.Id);
if (dictDatas != null && dictDatas.Count > 0) throw Oops.Oh(ErrorCode.D3002);
await dictType.DeleteAsync();
}
/// <summary>
/// 更新字典类型
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysDictType/edit"),]
public async Task UpdateDictType(UpdateDictTypeInput input)
{
var isExist = await _sysDictTypeRep.AnyAsync(u => u.Id == input.Id, false);
if (!isExist) throw Oops.Oh(ErrorCode.D3000);
// 排除自己并且判断与其他是否相同
isExist = await _sysDictTypeRep.AnyAsync(u => (u.Name == input.Name || u.Code == input.Code) && u.Id != input.Id, false);
if (isExist) throw Oops.Oh(ErrorCode.D3001);
var dictType = input.Adapt<SysDictType>();
await _sysDictTypeRep.UpdateAsync(dictType, ignoreNullValues: true);
}
/// <summary>
/// 字典类型详情
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysDictType/detail")]
public async Task<dynamic> GetDictType([FromQuery] QueryDictTypeInfoInput input)
{
return await _sysDictTypeRep.FirstOrDefaultAsync(u => u.Id == input.Id, false);
}
/// <summary>
/// 更新字典类型状态
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysDictType/changeStatus")]
public async Task ChangeDictTypeStatus(UpdateDictTypeInput input)
{
var dictType = await _sysDictTypeRep.FirstOrDefaultAsync(u => u.Id == input.Id);
if (dictType == null) throw Oops.Oh(ErrorCode.D3000);
if (!Enum.IsDefined(typeof(CommonStatus), input.Status))
throw Oops.Oh(ErrorCode.D3005);
dictType.Status = input.Status;
}
/// <summary>
/// 字典类型与字典值构造的字典树
/// </summary>
/// <returns></returns>
[AllowAnonymous]
[HttpGet("/sysDictType/tree")]
public async Task<List<DictTreeOutput>> GetDictTree()
{
return await _sysDictTypeRep.DetachedEntities.Select(u => new DictTreeOutput
{
Id = u.Id,
Code = u.Code,
Name = u.Name,
Children = u.SysDictDatas.Select(c => new DictTreeOutput
{
Id = c.Id,
Pid = c.TypeId,
Code = c.Code,
Name = c.Value
}).ToList()
}).ToListAsync();
}
}
}

View File

@@ -0,0 +1,38 @@
namespace Ewide.Core.Service
{
/// <summary>
/// 附属机构和职位参数
/// </summary>
public class EmpExtOrgPosOutput
{
/// <summary>
/// 附属机构id
/// </summary>
public string OrgId { get; set; }
/// <summary>
/// 附属机构编码
/// </summary>
public string OrgCode { get; set; }
/// <summary>
/// 附属机构名称
/// </summary>
public string OrgName { get; set; }
/// <summary>
/// 附属职位id
/// </summary>
public string PosId { get; set; }
/// <summary>
/// 附属职位编码
/// </summary>
public string PosCode { get; set; }
/// <summary>
/// 附属职位名称
/// </summary>
public string PosName { get; set; }
}
}

View File

@@ -0,0 +1,35 @@
using System.Collections.Generic;
namespace Ewide.Core.Service
{
/// <summary>
/// 员工信息参数
/// </summary>
public class EmpOutput
{
/// <summary>
/// 工号
/// </summary>
public string JobNum { get; set; }
/// <summary>
/// 机构id
/// </summary>
public string OrgId { get; set; }
/// <summary>
/// 机构名称
/// </summary>
public string OrgName { get; set; }
/// <summary>
/// 机构与职位信息
/// </summary>
public List<EmpExtOrgPosOutput> ExtOrgPos { get; set; } = new List<EmpExtOrgPosOutput>();
/// <summary>
/// 职位信息
/// </summary>
public List<EmpPosOutput> Positions { get; set; } = new List<EmpPosOutput>();
}
}

View File

@@ -0,0 +1,40 @@
using System.Collections.Generic;
namespace Ewide.Core.Service
{
/// <summary>
/// 员工信息参数2
/// </summary>
public class EmpOutput2
{
/// <summary>
/// 员工Id
/// </summary>
public string Id { get; set; }
/// <summary>
/// 工号
/// </summary>
public string JobNum { get; set; }
/// <summary>
/// 机构Id
/// </summary>
public string OrgId { get; set; }
/// <summary>
/// 机构名称
/// </summary>
public string OrgName { get; set; }
/// <summary>
/// 附属机构
/// </summary>
public List<EmpExtOrgPosOutput> ExtIds { get; set; } = new List<EmpExtOrgPosOutput>();
/// <summary>
/// 职位集合
/// </summary>
public List<string> PosIdList { get; set; } = new List<string>();
}
}

View File

@@ -0,0 +1,23 @@
namespace Ewide.Core.Service
{
/// <summary>
/// 员工职位参数
/// </summary>
public class EmpPosOutput
{
/// <summary>
/// 职位Id
/// </summary>
public string PosId { get; set; }
/// <summary>
/// 职位编码
/// </summary>
public string PosCode { get; set; }
/// <summary>
/// 职位名称
/// </summary>
public string PosName { get; set; }
}
}

View File

@@ -0,0 +1,14 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
public interface ISysEmpExtOrgPosService
{
Task AddOrUpdate(string empId, List<EmpExtOrgPosOutput> extIdList);
Task DeleteEmpExtInfoByUserId(string empId);
Task<List<EmpExtOrgPosOutput>> GetEmpExtOrgPosList(string empId);
Task<bool> HasExtOrgEmp(string orgId);
Task<bool> HasExtPosEmp(string posId);
}
}

View File

@@ -0,0 +1,13 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
public interface ISysEmpPosService
{
Task AddOrUpdate(string empId, List<string> posIdList);
Task DeleteEmpPosInfoByUserId(string empId);
Task<List<EmpPosOutput>> GetEmpPosList(string empId);
Task<bool> HasPosEmp(string posId);
}
}

View File

@@ -0,0 +1,14 @@
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
public interface ISysEmpService
{
Task AddOrUpdate(EmpOutput2 sysEmpParam);
Task DeleteEmpInfoByUserId(string empId);
Task<EmpOutput> GetEmpInfo(string empId);
Task<string> GetEmpOrgId(string empId);
Task<bool> HasOrgEmp(string orgId);
Task UpdateEmpOrgInfo(string orgId, string orgName);
}
}

View File

@@ -0,0 +1,100 @@
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
/// <summary>
/// 员工附属机构和职位服务
/// </summary>
public class SysEmpExtOrgPosService : ISysEmpExtOrgPosService, ITransient
{
private readonly IRepository<SysEmpExtOrgPos> _sysEmpExtOrgPosRep; // 附属机构表仓储
public SysEmpExtOrgPosService(IRepository<SysEmpExtOrgPos> sysEmpExtOrgPosRep)
{
_sysEmpExtOrgPosRep = sysEmpExtOrgPosRep;
}
/// <summary>
/// 保存或编辑附属机构相关信息
/// </summary>
/// <returns></returns>
[UnitOfWork]
public async Task AddOrUpdate(string empId, List<EmpExtOrgPosOutput> extIdList)
{
// 先删除
await DeleteEmpExtInfoByUserId(empId);
var tasks = new List<Task>();
extIdList.ForEach(u =>
{
tasks.Add(new SysEmpExtOrgPos
{
SysEmpId = empId,
SysOrgId = u.OrgId,
SysPosId = u.PosId
}.InsertAsync());
});
await Task.WhenAll(tasks);
}
/// <summary>
/// 获取附属机构和职位信息
/// </summary>
/// <param name="empId"></param>
/// <returns></returns>
public async Task<List<EmpExtOrgPosOutput>> GetEmpExtOrgPosList(string empId)
{
return await _sysEmpExtOrgPosRep.DetachedEntities
.Where(u => u.SysEmpId == empId)
.Select(u => new EmpExtOrgPosOutput
{
OrgId = u.SysOrg.Id,
OrgCode = u.SysOrg.Code,
OrgName = u.SysOrg.Name,
PosId = u.SysPos.Id,
PosCode = u.SysPos.Code,
PosName = u.SysPos.Name
}).ToListAsync();
}
/// <summary>
/// 根据机构Id判断该附属机构下是否有员工
/// </summary>
/// <param name="orgId"></param>
/// <returns></returns>
public async Task<bool> HasExtOrgEmp(string orgId)
{
return await _sysEmpExtOrgPosRep.DetachedEntities.AnyAsync(u => u.SysOrgId == orgId);
}
/// <summary>
/// 根据职位Id判断该附属职位下是否有员工
/// </summary>
/// <param name="posId"></param>
/// <returns></returns>
public async Task<bool> HasExtPosEmp(string posId)
{
return await _sysEmpExtOrgPosRep.DetachedEntities.AnyAsync(u => u.SysPosId == posId);
}
/// <summary>
/// 根据员工Id删除对应的员工-附属信息
/// </summary>
/// <param name="empId"></param>
/// <returns></returns>
public async Task DeleteEmpExtInfoByUserId(string empId)
{
var empExtOrgPos = await _sysEmpExtOrgPosRep.Where(u => u.SysEmpId == empId).ToListAsync();
empExtOrgPos.ForEach(u =>
{
u.Delete();
});
}
}
}

View File

@@ -0,0 +1,85 @@
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
/// <summary>
/// 员工职位服务
/// </summary>
public class SysEmpPosService : ISysEmpPosService, ITransient
{
private readonly IRepository<SysEmpPos> _sysEmpPosRep; // 员工职位表仓储
public SysEmpPosService(IRepository<SysEmpPos> sysEmpPosRep)
{
_sysEmpPosRep = sysEmpPosRep;
}
/// <summary>
/// 增加或编辑员工职位相关信息
/// </summary>
/// <param name="empId">员工Id用户Id</param>
/// <param name="posIdList">职位id集合</param>
/// <returns></returns>
[UnitOfWork]
public async Task AddOrUpdate(string empId, List<string> posIdList)
{
// 先删除
await DeleteEmpPosInfoByUserId(empId);
posIdList.ForEach(u =>
{
new SysEmpPos
{
SysEmpId = empId,
SysPosId = u
}.Insert();
});
}
/// <summary>
/// 获取所属职位信息
/// </summary>
/// <param name="empId">员工Id用户Id</param>
public async Task<List<EmpPosOutput>> GetEmpPosList(string empId)
{
return await _sysEmpPosRep.DetachedEntities
.Where(u => u.SysEmpId == empId)
.Select(u => new EmpPosOutput
{
PosId = u.SysPos.Id,
PosCode = u.SysPos.Code,
PosName = u.SysPos.Name
}).ToListAsync();
}
/// <summary>
/// 根据职位Id判断该职位下是否有员工
/// </summary>
/// <param name="posId"></param>
/// <returns></returns>
public async Task<bool> HasPosEmp(string posId)
{
return await _sysEmpPosRep.DetachedEntities.AnyAsync(u => u.SysPosId == posId);
}
/// <summary>
/// 根据员工Id删除对用的员工-职位信息
/// </summary>
/// <param name="empId"></param>
/// <returns></returns>
public async Task DeleteEmpPosInfoByUserId(string empId)
{
var empPos = await _sysEmpPosRep.Where(u => u.SysEmpId == empId).ToListAsync();
empPos.ForEach(u =>
{
u.Delete();
});
}
}
}

View File

@@ -0,0 +1,125 @@
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection;
using Mapster;
using Microsoft.EntityFrameworkCore;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
/// <summary>
/// 员工服务
/// </summary>
public class SysEmpService : ISysEmpService, ITransient
{
private readonly IRepository<SysEmp> _sysEmpRep; // 员工表仓储
private readonly ISysEmpExtOrgPosService _sysEmpExtOrgPosService;
private readonly ISysEmpPosService _sysEmpPosService;
public SysEmpService(IRepository<SysEmp> sysEmpRep,
ISysEmpExtOrgPosService sysEmpExtOrgPosService,
ISysEmpPosService sysEmpPosService)
{
_sysEmpRep = sysEmpRep;
_sysEmpExtOrgPosService = sysEmpExtOrgPosService;
_sysEmpPosService = sysEmpPosService;
}
/// <summary>
/// 获取用户员工相关信息(包括登录)
/// </summary>
/// <param name="empId"></param>
/// <returns></returns>
public async Task<EmpOutput> GetEmpInfo(string empId)
{
var empInfoOutput = new EmpOutput();
var sysEmp = await _sysEmpRep.FirstOrDefaultAsync(u => u.Id == empId, false);
if (sysEmp == null) return empInfoOutput;
empInfoOutput = sysEmp.Adapt<EmpOutput>();
empInfoOutput.ExtOrgPos = await _sysEmpExtOrgPosService.GetEmpExtOrgPosList(empId);
empInfoOutput.Positions = await _sysEmpPosService.GetEmpPosList(empId);
return empInfoOutput;
}
/// <summary>
/// 增加或编辑员工相关信息
/// </summary>
/// <returns></returns>
[UnitOfWork]
public async Task AddOrUpdate(EmpOutput2 sysEmpParam)
{
// 先删除员工信息
var emps = await _sysEmpRep.Where(u => u.Id == sysEmpParam.Id).ToListAsync();
emps.ForEach(u =>
{
u.DeleteNow();
});
// 再新增新员工信息
var emp = sysEmpParam.Adapt<SysEmp>();
await _sysEmpRep.InsertAsync(emp);
// 更新附属机构职位信息
await _sysEmpExtOrgPosService.AddOrUpdate(emp.Id, sysEmpParam.ExtIds);
// 更新职位信息
await _sysEmpPosService.AddOrUpdate(emp.Id, sysEmpParam.PosIdList);
}
/// <summary>
/// 修改员工相关机构信息
/// </summary>
/// <param name="orgId"></param>
/// <param name="orgName"></param>
/// <returns></returns>
public async Task UpdateEmpOrgInfo(string orgId, string orgName)
{
var emps = await _sysEmpRep.Where(u => u.OrgId == orgId).ToListAsync();
emps.ForEach(u =>
{
u.OrgName = orgName;
});
}
/// <summary>
/// 根据机构Id判断该机构下是否有员工
/// </summary>
/// <param name="orgId"></param>
/// <returns></returns>
public async Task<bool> HasOrgEmp(string orgId)
{
return await _sysEmpRep.DetachedEntities.AnyAsync(u => u.OrgId == orgId);
}
/// <summary>
/// 根据员工Id删除对应的员工表信息
/// </summary>
/// <param name="empId"></param>
/// <returns></returns>
[UnitOfWork]
public async Task DeleteEmpInfoByUserId(string empId)
{
// 删除员工信息
var emp = await _sysEmpRep.FirstOrDefaultAsync(u => u.Id == empId);
await emp.DeleteAsync();
// 级联删除对应的员工-附属信息
await _sysEmpExtOrgPosService.DeleteEmpExtInfoByUserId(empId);
// 级联删除对用的员工-职位信息
await _sysEmpPosService.DeleteEmpPosInfoByUserId(empId);
}
/// <summary>
/// 获取员工机构Id
/// </summary>
/// <param name="empId"></param>
/// <returns></returns>
public async Task<string> GetEmpOrgId(string empId)
{
return (await _sysEmpRep.FirstOrDefaultAsync(u => u.Id == empId, false)).OrgId;
}
}
}

View File

@@ -0,0 +1,64 @@
using System.ComponentModel.DataAnnotations;
namespace Ewide.Core.Service
{
/// <summary>
/// 上传文件参数
/// </summary>
public class FileInput : PageInputBase
{
/// <summary>
/// 文件存储位置1:阿里云2:腾讯云3:minio4:本地)
/// </summary>
public int FileLocation { get; set; }
/// <summary>
/// 文件仓库
/// </summary>
public string FileBucket { get; set; }
/// <summary>
/// 文件名称(上传时候的文件名)
/// </summary>
public string FileOriginName { get; set; }
/// <summary>
/// 文件后缀
/// </summary>
public string FileSuffix { get; set; }
/// <summary>
/// 文件大小kb
/// </summary>
public long FileSizeKb { get; set; }
/// <summary>
/// 文件大小信息,计算后的
/// </summary>
public string FileSizeInfo { get; set; }
/// <summary>
/// 存储到bucket的名称文件唯一标识id
/// </summary>
public string FileObjectName { get; set; }
/// <summary>
/// 存储路径
/// </summary>
public string FilePath { get; set; }
}
public class DeleteFileInfoInput
{
/// <summary>
/// 文件Id
/// </summary>
[Required(ErrorMessage = "文件Id不能为空")]
public string Id { get; set; }
}
public class QueryFileInoInput : DeleteFileInfoInput
{
}
}

View File

@@ -0,0 +1,13 @@
namespace Ewide.Core.Service
{
/// <summary>
/// 上传文件参数
/// </summary>
public class FileOutput : FileInput
{
/// <summary>
/// 文件Id
/// </summary>
public string Id { get; set; }
}
}

View File

@@ -0,0 +1,21 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
public interface ISysFileService
{
Task DeleteFileInfo(DeleteFileInfoInput input);
Task<IActionResult> DownloadFileInfo([FromQuery] QueryFileInoInput input);
Task<SysFile> GetFileInfo([FromQuery] QueryFileInoInput input);
Task<List<SysFile>> GetFileInfoList([FromQuery] FileOutput input);
Task<IActionResult> PreviewFileInfo([FromQuery] QueryFileInoInput input);
Task<dynamic> QueryFileInfoPageList([FromQuery] FileInput input);
Task UploadFileAvatar(IFormFile file);
Task UploadFileDefault(IFormFile file);
Task UploadFileDocument(IFormFile file);
Task UploadFileShop(IFormFile file);
}
}

View File

@@ -0,0 +1,205 @@
using Furion;
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using Furion.Snowflake;
using Mapster;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
namespace Ewide.Core.Service
{
/// <summary>
/// 文件服务
/// </summary>
[ApiDescriptionSettings(Name = "File", Order = 100)]
public class SysFileService : ISysFileService, IDynamicApiController, ITransient
{
private readonly IRepository<SysFile> _sysFileInfoRep; // 文件信息表仓储
private readonly IConfiguration _configuration;
public SysFileService(IRepository<SysFile> sysFileInfoRep, IConfiguration configuration)
{
_sysFileInfoRep = sysFileInfoRep;
_configuration = configuration;
}
/// <summary>
/// 分页获取文件列表
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysFileInfo/page")]
public async Task<dynamic> QueryFileInfoPageList([FromQuery] FileInput input)
{
var fileBucket = !string.IsNullOrEmpty(input.FileBucket?.Trim());
var fileOriginName = !string.IsNullOrEmpty(input.FileOriginName?.Trim());
var files = await _sysFileInfoRep.DetachedEntities
.Where(input.FileLocation > 0, u => u.FileLocation == input.FileLocation)
.Where(fileBucket, u => EF.Functions.Like(u.FileBucket, $"%{input.FileBucket.Trim()}%"))
.Where(fileOriginName, u => EF.Functions.Like(u.FileOriginName, $"%{input.FileOriginName.Trim()}%"))
.Select(u => u.Adapt<FileOutput>())
.ToPagedListAsync(input.PageNo, input.PageSize);
return XnPageResult<FileOutput>.PageResult(files);
}
/// <summary>
/// 获取文件列表
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysFileInfo/list")]
public async Task<List<SysFile>> GetFileInfoList([FromQuery] FileOutput input)
{
return await _sysFileInfoRep.DetachedEntities.ToListAsync();
}
/// <summary>
/// 删除文件
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysFileInfo/delete")]
public async Task DeleteFileInfo(DeleteFileInfoInput input)
{
var file = await _sysFileInfoRep.FirstOrDefaultAsync(u => u.Id == input.Id);
if (file != null)
{
await file.DeleteAsync();
var filePath = Path.Combine(App.WebHostEnvironment.WebRootPath, file.FileBucket, file.FileObjectName);
if (File.Exists(filePath))
File.Delete(filePath);
}
}
/// <summary>
/// 获取文件详情
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysFileInfo/detail")]
public async Task<SysFile> GetFileInfo([FromQuery] QueryFileInoInput input)
{
var file = await _sysFileInfoRep.FirstOrDefaultAsync(u => u.Id == input.Id);
if (file == null)
throw Oops.Oh(ErrorCode.D8000);
return file;
}
/// <summary>
/// 预览文件
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysFileInfo/preview")]
public async Task<IActionResult> PreviewFileInfo([FromQuery] QueryFileInoInput input)
{
return await DownloadFileInfo(input);
}
/// <summary>
/// 上传文件
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
[HttpPost("/sysFileInfo/upload")]
public async Task UploadFileDefault(IFormFile file)
{
await UploadFile(file, _configuration["UploadFile:Default:path"]);
}
/// <summary>
/// 下载文件
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysFileInfo/download")]
public async Task<IActionResult> DownloadFileInfo([FromQuery] QueryFileInoInput input)
{
var file = await GetFileInfo(input);
var filePath = Path.Combine(App.WebHostEnvironment.WebRootPath, file.FileBucket, file.FileObjectName);
var fileName = HttpUtility.UrlEncode(file.FileOriginName, Encoding.GetEncoding("UTF-8"));
return new FileStreamResult(new FileStream(filePath, FileMode.Open), "application/octet-stream") { FileDownloadName = fileName };
}
/// <summary>
/// 上传头像
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public async Task UploadFileAvatar(IFormFile file)
{
await UploadFile(file, _configuration["UploadFile:Avatar:path"]);
}
/// <summary>
/// 上传文档
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public async Task UploadFileDocument(IFormFile file)
{
await UploadFile(file, _configuration["UploadFile:Document:path"]);
}
/// <summary>
/// 上传商店图片
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public async Task UploadFileShop(IFormFile file)
{
await UploadFile(file, _configuration["UploadFile:Shop:path"]);
}
/// <summary>
/// 上传文件
/// </summary>
/// <param name="file"></param>
/// <param name="pathType"></param>
/// <returns></returns>
private static async Task UploadFile(IFormFile file, string pathType)
{
var fileId = Guid.NewGuid().ToString();
var fileSizeKb = (long)(file.Length / 1024.0); // 文件大小KB
var originalFilename = file.FileName; // 文件原始名称
var fileSuffix = Path.GetExtension(file.FileName).ToLower(); // 文件后缀
var finalName = fileId + fileSuffix; // 生成文件的最终名称
var filePath = Path.Combine(App.WebHostEnvironment.WebRootPath, pathType);
if (!Directory.Exists(filePath))
Directory.CreateDirectory(filePath);
using (var stream = File.Create(Path.Combine(filePath, finalName)))
{
await file.CopyToAsync(stream);
}
var sysFileInfo = new SysFile
{
Id = fileId,
FileLocation = (int)FileLocation.LOCAL,
FileBucket = pathType,
FileObjectName = finalName,
FileOriginName = originalFilename,
FileSuffix = fileSuffix.TrimStart('.'),
FileSizeKb = fileSizeKb
};
await sysFileInfo.InsertAsync();
}
}
}

View File

@@ -0,0 +1,95 @@
using System;
namespace Ewide.Core.Service
{
/// <summary>
/// 请求日志参数
/// </summary>
public class OpLogInput : PageInputBase
{
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 操作类型0其他 1增加 2删除 3编辑见LogAnnotionOpTypeEnum
/// </summary>
public int? OpType { get; set; }
/// <summary>
/// 是否执行成功Y-是N-否)
/// </summary>
public string Success { get; set; }
/// <summary>
/// 具体消息
/// </summary>
public string Message { get; set; }
/// <summary>
/// ip
/// </summary>
public string Ip { get; set; }
/// <summary>
/// 地址
/// </summary>
public string Location { get; set; }
/// <summary>
/// 浏览器
/// </summary>
public string Browser { get; set; }
/// <summary>
/// 操作系统
/// </summary>
public string Os { get; set; }
/// <summary>
/// 请求地址
/// </summary>
public string Url { get; set; }
/// <summary>
/// 类名称
/// </summary>
public string ClassName { get; set; }
/// <summary>
/// 方法名称
/// </summary>
public string MethodName { get; set; }
/// <summary>
/// 请求方式GET POST PUT DELETE)
/// </summary>
public string ReqMethod { get; set; }
/// <summary>
/// 请求参数
/// </summary>
public string Param { get; set; }
/// <summary>
/// 返回结果
/// </summary>
public string Result { get; set; }
/// <summary>
/// 耗时(毫秒)
/// </summary>
public long ElapsedTime { get; set; }
/// <summary>
/// 操作时间
/// </summary>
public DateTimeOffset OpTime { get; set; }
/// <summary>
/// 操作人
/// </summary>
public string Account { get; set; }
}
}

View File

@@ -0,0 +1,10 @@
namespace Ewide.Core.Service
{
/// <summary>
/// 请求日志参数
/// </summary>
public class OpLogOutput : OpLogInput
{
}
}

View File

@@ -0,0 +1,60 @@
using System;
namespace Ewide.Core.Service
{
/// <summary>
/// 访问日志参数
/// </summary>
public class VisLogInput : PageInputBase
{
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 是否执行成功Y-是N-否)
/// </summary>
public string Success { get; set; }
/// <summary>
/// 具体消息
/// </summary>
public string Message { get; set; }
/// <summary>
/// IP
/// </summary>
public string Ip { get; set; }
/// <summary>
/// 地址
/// </summary>
public string Location { get; set; }
/// <summary>
/// 浏览器
/// </summary>
public string Browser { get; set; }
/// <summary>
/// 操作系统
/// </summary>
public string Os { get; set; }
/// <summary>
/// 访问类型(字典 1登入 2登出
/// </summary>
public int? VisType { get; set; }
/// <summary>
/// 访问时间
/// </summary>
public DateTimeOffset VisTime { get; set; }
/// <summary>
/// 访问人
/// </summary>
public string Account { get; set; }
}
}

View File

@@ -0,0 +1,10 @@
namespace Ewide.Core.Service
{
/// <summary>
/// 访问日志参数
/// </summary>
public class VisLogOutput : VisLogInput
{
}
}

View File

@@ -0,0 +1,11 @@
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
public interface ISysOpLogService
{
Task ClearOpLog();
Task<dynamic> QueryOpLogPageList([FromQuery] OpLogInput input);
}
}

View File

@@ -0,0 +1,11 @@
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
public interface ISysVisLogService
{
Task ClearVisLog();
Task<dynamic> QueryVisLogPageList([FromQuery] VisLogInput input);
}
}

View File

@@ -0,0 +1,64 @@
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Mapster;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
/// <summary>
/// 操作日志服务
/// </summary>
[ApiDescriptionSettings(Name = "OpLog", Order = 100)]
public class SysOpLogService : ISysOpLogService, IDynamicApiController, ITransient
{
private readonly IRepository<SysLogOp> _sysOpLogRep; // 操作日志表仓储
public SysOpLogService(IRepository<SysLogOp> sysOpLogRep)
{
_sysOpLogRep = sysOpLogRep;
}
/// <summary>
/// 分页查询操作日志
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysOpLog/page")]
public async Task<dynamic> QueryOpLogPageList([FromQuery] OpLogInput input)
{
var name = !string.IsNullOrEmpty(input.Name?.Trim());
var success = !string.IsNullOrEmpty(input.Success?.Trim());
var searchBeginTime = !string.IsNullOrEmpty(input.SearchBeginTime?.Trim());
var opLogs = await _sysOpLogRep.DetachedEntities
.Where((name, u => EF.Functions.Like(u.Name, $"%{input.Name.Trim()}%")))
.Where(input.OpType > 0, u => u.OpType == input.OpType)
.Where(success, u => u.Success == input.Success.Trim())
.Where(searchBeginTime, u => u.OpTime >= DateTime.Parse(input.SearchBeginTime.Trim()) &&
u.OpTime <= DateTime.Parse(input.SearchEndTime.Trim()))
.OrderByDescending(u => u.Id)
.Select(u => u.Adapt<OpLogOutput>())
.ToPagedListAsync(input.PageNo, input.PageSize);
return XnPageResult<OpLogOutput>.PageResult(opLogs);
}
/// <summary>
/// 清空操作日志
/// </summary>
/// <returns></returns>
[HttpPost("/sysOpLog/delete")]
public async Task ClearOpLog()
{
var opLogs = await _sysOpLogRep.Entities.ToListAsync();
opLogs.ForEach(u =>
{
u.Delete();
});
}
}
}

View File

@@ -0,0 +1,64 @@
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Mapster;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
/// <summary>
/// 访问日志服务
/// </summary>
[ApiDescriptionSettings(Name = "VisLog", Order = 100)]
public class SysVisLogService : ISysVisLogService, IDynamicApiController, ITransient
{
private readonly IRepository<SysLogVis> _sysVisLogRep; // 访问日志表仓储
public SysVisLogService(IRepository<SysLogVis> sysVisLogRep)
{
_sysVisLogRep = sysVisLogRep;
}
/// <summary>
/// 分页查询访问日志
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysVisLog/page")]
public async Task<dynamic> QueryVisLogPageList([FromQuery] VisLogInput input)
{
var name = !string.IsNullOrEmpty(input.Name?.Trim());
var success = !string.IsNullOrEmpty(input.Success?.Trim());
var searchBeginTime = !string.IsNullOrEmpty(input.SearchBeginTime?.Trim());
var visLogs = await _sysVisLogRep.DetachedEntities
.Where((name, u => EF.Functions.Like(u.Name, $"%{input.Name.Trim()}%")))
.Where(input.VisType > 0, u => u.VisType == input.VisType)
.Where(success, u => u.Success == input.Success.Trim())
.Where(searchBeginTime, u => u.VisTime >= DateTime.Parse(input.SearchBeginTime.Trim()) &&
u.VisTime <= DateTime.Parse(input.SearchEndTime.Trim()))
.OrderByDescending(u => u.Id)
.Select(u => u.Adapt<VisLogOutput>())
.ToPagedListAsync(input.PageNo, input.PageSize);
return XnPageResult<VisLogOutput>.PageResult(visLogs);
}
/// <summary>
/// 清空访问日志
/// </summary>
/// <returns></returns>
[HttpPost("/sysVisLog/delete")]
public async Task ClearVisLog()
{
var visLogs = await _sysVisLogRep.Entities.ToListAsync();
visLogs.ForEach(u =>
{
u.Delete();
});
}
}
}

View File

@@ -0,0 +1,79 @@
namespace Ewide.Core.Service
{
/// <summary>
/// 登录菜单-AntDesign菜单类型
/// </summary>
public class AntDesignTreeNode
{
/// <summary>
/// id
/// </summary>
public string Id { get; set; }
/// <summary>
/// 父id
/// </summary>
public string Pid { get; set; }
/// <summary>
/// 路由名称, 必须设置,且不能重名
/// </summary>
public string Name { get; set; }
/// <summary>
/// 组件
/// </summary>
public string Component { get; set; }
/// <summary>
/// 重定向地址, 访问这个路由时, 自定进行重定向
/// </summary>
public string Redirect { get; set; }
/// <summary>
/// 路由元信息(路由附带扩展信息)
/// </summary>
public Meta Meta { get; set; }
/// <summary>
/// 路径
/// </summary>
public string Path { get; set; }
/// <summary>
/// 控制路由和子路由是否显示在 sidebar
/// </summary>
public bool Hidden { get; set; }
}
/// <summary>
/// 路由元信息内部类
/// </summary>
public class Meta
{
/// <summary>
/// 路由标题, 用于显示面包屑, 页面标题 *推荐设置
/// </summary>
public string Title { get; set; }
/// <summary>
/// 图标
/// </summary>
public string Icon { get; set; }
/// <summary>
/// 是否可见
/// </summary>
public bool Show { get; set; }
/// <summary>
/// 如需外部打开增加_blank
/// </summary>
public string Target { get; set; }
/// <summary>
/// 内链打开http链接
/// </summary>
public string Link { get; set; }
}
}

View File

@@ -0,0 +1,137 @@
using System.ComponentModel.DataAnnotations;
namespace Ewide.Core.Service
{
/// <summary>
/// 菜单参数
/// </summary>
public class MenuInput
{
/// <summary>
/// 父Id
/// </summary>
public virtual string Pid { get; set; }
/// <summary>
/// 名称
/// </summary>
public virtual string Name { get; set; }
/// <summary>
/// 编码
/// </summary>
public virtual string Code { get; set; }
/// <summary>
/// 菜单类型(字典 0目录 1菜单 2按钮
/// </summary>
public virtual string Type { get; set; }
/// <summary>
/// 图标
/// </summary>
public string Icon { get; set; }
/// <summary>
/// 路由地址
/// </summary>
public virtual string Router { get; set; }
/// <summary>
/// 组件地址
/// </summary>
public virtual string Component { get; set; }
/// <summary>
/// 权限标识
/// </summary>
public virtual string Permission { get; set; }
/// <summary>
/// 应用分类(应用编码)
/// </summary>
public virtual string Application { get; set; }
/// <summary>
/// 打开方式(字典 0无 1组件 2内链 3外链
/// </summary>
public virtual string OpenType { get; set; }
/// <summary>
/// 是否可见Y-是N-否)
/// </summary>
public string Visible { get; set; }
/// <summary>
/// 内链地址
/// </summary>
public string Link { get; set; }
/// <summary>
/// 重定向地址
/// </summary>
public string Redirect { get; set; }
/// <summary>
/// 权重(字典 1系统权重 2业务权重
/// </summary>
public string Weight { get; set; }
/// <summary>
/// 排序
/// </summary>
public int Sort { get; set; }
/// <summary>
/// 备注
/// </summary>
public string Remark { get; set; }
}
public class AddMenuInput : MenuInput
{
/// <summary>
/// 菜单类型(字典 0目录 1菜单 2按钮
/// </summary>
[Required(ErrorMessage = "菜单类型不能为空")]
public override string Type { get; set; }
}
public class DeleteMenuInput
{
/// <summary>
/// 菜单Id
/// </summary>
[Required(ErrorMessage = "菜单Id不能为空")]
public string Id { get; set; }
}
public class UpdateMenuInput : AddMenuInput
{
/// <summary>
/// 菜单Id
/// </summary>
[Required(ErrorMessage = "菜单Id不能为空")]
public string Id { get; set; }
/// <summary>
/// 父Id
/// </summary>DeleteMenuInput
[Required(ErrorMessage = "父级菜单Id不能为空")]
public override string Pid { get; set; }
}
public class QueryMenuInput : DeleteMenuInput
{
}
public class ChangeAppMenuInput : MenuInput
{
/// <summary>
/// 应用编码
/// </summary>DeleteMenuInput
[Required(ErrorMessage = "应用编码不能为空")]
public override string Application { get; set; }
}
}

View File

@@ -0,0 +1,36 @@
using System.Collections;
using System.Collections.Generic;
namespace Ewide.Core.Service
{
/// <summary>
/// 菜单树(列表形式)
/// </summary>
public class MenuOutput : MenuInput, ITreeNode
{
/// <summary>
/// 菜单Id
/// </summary>
public string Id { get; set; }
/// <summary>
/// 子节点
/// </summary>
public List<MenuOutput> Children { get; set; } = new List<MenuOutput>();
public string GetId()
{
return Id;
}
public string GetPid()
{
return Pid;
}
public void SetChildren(IList children)
{
Children = (List<MenuOutput>)children;
}
}
}

View File

@@ -0,0 +1,56 @@
using System.Collections;
using System.Collections.Generic;
namespace Ewide.Core.Service
{
/// <summary>
/// 菜单树---授权、新增编辑时选择
/// </summary>
public class MenuTreeOutput : ITreeNode
{
/// <summary>
/// 主键
/// </summary>
public string Id { get; set; }
/// <summary>
/// 父Id
/// </summary>
public string ParentId { get; set; }
/// <summary>
/// 名称
/// </summary>
public string Title { get; set; }
/// <summary>
/// 值
/// </summary>
public string Value { get; set; }
/// <summary>
/// 排序,越小优先级越高
/// </summary>
public int Weight { get; set; }
/// <summary>
/// 子节点
/// </summary>
public List<MenuTreeOutput> Children { get; set; } = new List<MenuTreeOutput>();
public string GetId()
{
return Id;
}
public string GetPid()
{
return ParentId;
}
public void SetChildren(IList children)
{
Children = (List<MenuTreeOutput>)children;
}
}
}

View File

@@ -0,0 +1,22 @@
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
public interface ISysMenuService
{
Task AddMenu(AddMenuInput input);
Task<List<AntDesignTreeNode>> ChangeAppMenu(ChangeAppMenuInput input);
Task DeleteMenu(DeleteMenuInput input);
Task<List<AntDesignTreeNode>> GetLoginMenusAntDesign(string userId, string appCode);
Task<List<string>> GetLoginPermissionList(string userId);
Task<dynamic> GetMenu(QueryMenuInput input);
Task<dynamic> GetMenuList([FromQuery] MenuInput input);
Task<dynamic> GetMenuTree([FromQuery] MenuInput input);
Task<List<string>> GetUserMenuAppCodeList(string userId);
Task<bool> HasMenu(string appCode);
Task<dynamic> TreeForGrant([FromQuery] MenuInput input);
Task UpdateMenu(UpdateMenuInput input);
}
}

View File

@@ -0,0 +1,438 @@
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using Mapster;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
/// <summary>
/// 系统菜单服务
/// </summary>
[ApiDescriptionSettings(Name = "Menu", Order = 146)]
public class SysMenuService : ISysMenuService, IDynamicApiController, ITransient
{
private readonly IRepository<SysMenu> _sysMenuRep; // 菜单表仓储
private readonly IUserManager _userManager;
private readonly ISysCacheService _sysCacheService;
private readonly ISysUserRoleService _sysUserRoleService;
private readonly ISysRoleMenuService _sysRoleMenuService;
public SysMenuService(IRepository<SysMenu> sysMenuRep,
IUserManager userManager,
ISysCacheService sysCacheService,
ISysUserRoleService sysUserRoleService,
ISysRoleMenuService sysRoleMenuService)
{
_sysMenuRep = sysMenuRep;
_userManager = userManager;
_sysCacheService = sysCacheService;
_sysUserRoleService = sysUserRoleService;
_sysRoleMenuService = sysRoleMenuService;
}
/// <summary>
/// 获取用户权限(按钮权限标识集合)
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
[NonAction]
public async Task<List<string>> GetLoginPermissionList(string userId)
{
var permissions = await _sysCacheService.GetPermission(userId); // 先从缓存里面读取
if (permissions == null || permissions.Count < 1)
{
var roleIdList = await _sysUserRoleService.GetUserRoleIdList(userId);
var menuIdList = await _sysRoleMenuService.GetRoleMenuIdList(roleIdList);
permissions = await _sysMenuRep.DetachedEntities.Where(u => menuIdList.Contains(u.Id))
.Where(u => u.Type == (int)MenuType.BTN)
.Where(u => u.Status == (int)CommonStatus.ENABLE)
.Select(u => u.Permission).ToListAsync();
await _sysCacheService.SetPermission(userId, permissions); // 缓存结果
}
return permissions;
}
/// <summary>
/// 获取用户AntDesign菜单集合
/// </summary>
/// <param name="userId"></param>
/// <param name="appCode"></param>
/// <returns></returns>
[NonAction]
public async Task<List<AntDesignTreeNode>> GetLoginMenusAntDesign(string userId, string appCode)
{
var antDesignTreeNodes = await _sysCacheService.GetMenu(userId, appCode); // 先从缓存里面读取
if (antDesignTreeNodes == null || antDesignTreeNodes.Count < 1)
{
var sysMenuList = new List<SysMenu>();
// 管理员则展示所有系统权重菜单,不能展示业务权重菜单
if (_userManager.SuperAdmin)
{
sysMenuList = await _sysMenuRep.DetachedEntities
.Where(u => u.Status == (int)CommonStatus.ENABLE)
.Where(u => u.Application == appCode)
.Where(u => u.Type != (int)MenuType.BTN)
//.Where(u => u.Weight != (int)MenuWeight.DEFAULT_WEIGHT)
.OrderBy(u => u.Sort).ToListAsync();
}
else
{
// 非管理员则获取自己角色所拥有的菜单集合
var roleIdList = await _sysUserRoleService.GetUserRoleIdList(userId);
var menuIdList = await _sysRoleMenuService.GetRoleMenuIdList(roleIdList);
sysMenuList = await _sysMenuRep.DetachedEntities
.Where(u => menuIdList.Contains(u.Id))
.Where(u => u.Status == (int)CommonStatus.ENABLE)
.Where(u => u.Application == appCode)
.Where(u => u.Type != (int)MenuType.BTN)
.OrderBy(u => u.Sort).ToListAsync();
}
// 转换成登录菜单
antDesignTreeNodes = sysMenuList.Select(u => new AntDesignTreeNode
{
Id = u.Id,
Pid = u.Pid,
Name = u.Code,
Component = u.Component,
Redirect = u.OpenType == (int)MenuOpenType.OUTER ? u.Link : u.Redirect,
Path = u.OpenType == (int)MenuOpenType.OUTER ? u.Link : u.Router,
Meta = new Meta
{
Title = u.Name,
Icon = u.Icon,
Show = u.Visible,
Link = u.Link,
Target = u.OpenType == (int)MenuOpenType.OUTER ? "_blank" : ""
}
}).ToList();
await _sysCacheService.SetMenu(userId, appCode, antDesignTreeNodes); // 缓存结果
}
return antDesignTreeNodes;
}
/// <summary>
/// 获取用户菜单所属的应用编码集合
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
[NonAction]
public async Task<List<string>> GetUserMenuAppCodeList(string userId)
{
var roleIdList = await _sysUserRoleService.GetUserRoleIdList(userId);
var menuIdList = await _sysRoleMenuService.GetRoleMenuIdList(roleIdList);
return await _sysMenuRep.DetachedEntities.Where(u => menuIdList.Contains(u.Id))
.Where(u => u.Status == (int)CommonStatus.ENABLE)
.Select(u => u.Application).ToListAsync();
}
/// <summary>
/// 系统菜单列表(树表)
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysMenu/list")]
public async Task<dynamic> GetMenuList([FromQuery] MenuInput input)
{
var application = !string.IsNullOrEmpty(input.Application?.Trim());
var name = !string.IsNullOrEmpty(input.Name?.Trim());
var menus = await _sysMenuRep.DetachedEntities.Where((application, u => u.Application == input.Application.Trim()),
(name, u => EF.Functions.Like(u.Name, $"%{input.Name.Trim()}%")))
.Where(u => u.Status == (int)CommonStatus.ENABLE).OrderBy(u => u.Sort)
.Select(u => u.Adapt<MenuOutput>())
.ToListAsync();
return new TreeBuildUtil<MenuOutput>().DoTreeBuild(menus);
}
/// <summary>
/// 创建Pids格式
/// 如果pid是0顶级节点pids就是 [0];
/// 如果pid不是顶级节点pids就是 pid菜单的 pids + [pid] + ,
/// </summary>
/// <param name="pid"></param>
/// <returns></returns>
private async Task<string> CreateNewPids(string pid)
{
if (pid.Equals(System.Guid.Empty.ToString()))
{
return "[" + System.Guid.Empty + "],";
}
else
{
var pmenu = await _sysMenuRep.DetachedEntities.FirstOrDefaultAsync(u => u.Id == pid);
return pmenu.Pids + "[" + pid + "],";
}
}
/// <summary>
/// 增加和编辑时检查参数
/// </summary>
/// <param name="input"></param>
private static void CheckMenuParam(MenuInput input)
{
var type = input.Type;
var router = input.Router;
var permission = input.Permission;
var openType = input.OpenType;
if (type.Equals((int)MenuType.DIR))
{
if (string.IsNullOrEmpty(router))
throw Oops.Oh(ErrorCode.D4001);
}
else if (type.Equals((int)MenuType.MENU))
{
if (string.IsNullOrEmpty(router))
throw Oops.Oh(ErrorCode.D4001);
if (string.IsNullOrEmpty(openType))
throw Oops.Oh(ErrorCode.D4002);
}
else if (type.Equals((int)MenuType.BTN))
{
if (string.IsNullOrEmpty(permission))
throw Oops.Oh(ErrorCode.D4003);
if (!permission.Contains(":"))
throw Oops.Oh(ErrorCode.D4004);
// 判断该资源是否存在
//permission = ":" + permission;
//var urlSet = resourceCache.getAllResources();
//if (!urlSet.Contains(permission.Replace(":","/")))
// throw Oops.Oh(ErrorCode.meu1005);
}
}
/// <summary>
/// 增加系统菜单
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysMenu/add")]
public async Task AddMenu(AddMenuInput input)
{
var isExist = await _sysMenuRep.DetachedEntities.AnyAsync(u => u.Code == input.Code); // u.Name == input.Name
if (isExist)
throw Oops.Oh(ErrorCode.D4000);
// 校验参数
CheckMenuParam(input);
var menu = input.Adapt<SysMenu>();
menu.Pids = await CreateNewPids(input.Pid);
menu.Status = (int)CommonStatus.ENABLE;
await menu.InsertAsync();
// 清除缓存
await _sysCacheService.DelByPatternAsync(CommonConst.CACHE_KEY_MENU);
}
/// <summary>
/// 删除系统菜单
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysMenu/delete")]
[UnitOfWork]
public async Task DeleteMenu(DeleteMenuInput input)
{
var childIdList = await _sysMenuRep.DetachedEntities.Where(u => u.Pids.Contains(input.Id.ToString()))
.Select(u => u.Id).ToListAsync();
childIdList.Add(input.Id);
var menus = await _sysMenuRep.Where(u => childIdList.Contains(u.Id)).ToListAsync();
menus.ForEach(u =>
{
u.Delete();
});
// 级联删除该菜单及子菜单对应的角色-菜单表信息
await _sysRoleMenuService.DeleteRoleMenuListByMenuIdList(childIdList);
// 清除缓存
await _sysCacheService.DelByPatternAsync(CommonConst.CACHE_KEY_MENU);
}
/// <summary>
/// 更新系统菜单
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysMenu/edit"),]
public async Task UpdateMenu(UpdateMenuInput input)
{
// Pid和Id不能一致一致会导致无限递归
if (input.Id == input.Pid)
throw Oops.Oh(ErrorCode.D4006);
var isExist = await _sysMenuRep.DetachedEntities.AnyAsync(u => u.Code == input.Code && u.Id != input.Id); // u.Name == input.Name
if (isExist)
throw Oops.Oh(ErrorCode.D4000);
// 校验参数
CheckMenuParam(input);
// 如果是编辑父id不能为自己的子节点
var childIdList = await _sysMenuRep.DetachedEntities.Where(u => u.Pids.Contains(input.Id.ToString()))
.Select(u => u.Id).ToListAsync();
if (childIdList.Contains(input.Pid))
throw Oops.Oh(ErrorCode.D4006);
var oldMenu = await _sysMenuRep.DetachedEntities.FirstOrDefaultAsync(u => u.Id == input.Id);
// 生成新的pids
var newPids = await CreateNewPids(input.Pid);
// 是否更新子应用的标识
var updateSubAppsFlag = false;
// 是否更新子节点的pids的标识
var updateSubPidsFlag = false;
// 如果应用有变化
if (input.Application != oldMenu.Application)
{
// 父节点不是根节点不能移动应用
if (!oldMenu.Pid.Equals(System.Guid.Empty.ToString()))
throw Oops.Oh(ErrorCode.D4007);
updateSubAppsFlag = true;
}
// 父节点有变化
if (input.Pid != oldMenu.Pid)
updateSubPidsFlag = true;
// 开始更新所有子节点的配置
if (updateSubAppsFlag || updateSubPidsFlag)
{
// 查找所有叶子节点,包含子节点的子节点
var menuList = await _sysMenuRep.Where(u => EF.Functions.Like(u.Pids, $"%{oldMenu.Id}%")).ToListAsync();
// 更新所有子节点的应用为当前菜单的应用
if (menuList.Count > 0)
{
// 更新所有子节点的application
if (updateSubAppsFlag)
{
menuList.ForEach(u =>
{
u.Application = input.Application;
});
}
// 更新所有子节点的pids
if (updateSubPidsFlag)
{
menuList.ForEach(u =>
{
// 子节点pids组成 = 当前菜单新pids + 当前菜单id + 子节点自己的pids后缀
var oldParentCodesPrefix = oldMenu.Pids + "[" + oldMenu.Id + "],";
var oldParentCodesSuffix = u.Pids.Substring(oldParentCodesPrefix.Length);
var menuParentCodes = newPids + "[" + oldMenu.Id + "]," + oldParentCodesSuffix;
u.Pids = menuParentCodes;
});
}
}
}
// 更新当前菜单
oldMenu = input.Adapt<SysMenu>();
oldMenu.Pids = newPids;
await oldMenu.UpdateAsync(ignoreNullValues: true);
// 清除缓存
await _sysCacheService.DelByPatternAsync(CommonConst.CACHE_KEY_MENU);
}
/// <summary>
/// 获取系统菜单
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysMenu/detail")]
public async Task<dynamic> GetMenu(QueryMenuInput input)
{
return await _sysMenuRep.DetachedEntities.FirstOrDefaultAsync(u => u.Id == input.Id);
}
/// <summary>
/// 获取系统菜单树,用于新增、编辑时选择上级节点
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysMenu/tree")]
public async Task<dynamic> GetMenuTree([FromQuery] MenuInput input)
{
var application = !string.IsNullOrEmpty(input.Application?.Trim());
var menus = await _sysMenuRep.DetachedEntities
.Where(application, u => u.Application == input.Application.Trim())
.Where(u => u.Status == (int)CommonStatus.ENABLE)
.Where(u => u.Type == (int)MenuType.DIR || u.Type == (int)MenuType.MENU)
.OrderBy(u => u.Sort)
.Select(u => new MenuTreeOutput
{
Id = u.Id,
ParentId = u.Pid,
Value = u.Id.ToString(),
Title = u.Name,
Weight = u.Sort
}).ToListAsync();
return new TreeBuildUtil<MenuTreeOutput>().DoTreeBuild(menus);
}
/// <summary>
/// 获取系统菜单树,用于给角色授权时选择
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysMenu/treeForGrant")]
public async Task<dynamic> TreeForGrant([FromQuery] MenuInput input)
{
var menuIdList = new List<string>();
if (_userManager.SuperAdmin)
{
var roleIdList = await _sysUserRoleService.GetUserRoleIdList(_userManager.UserId);
menuIdList = await _sysRoleMenuService.GetRoleMenuIdList(roleIdList);
}
var application = !string.IsNullOrEmpty(input.Application?.Trim());
var menus = await _sysMenuRep.DetachedEntities
.Where(application, u => u.Application == input.Application.Trim())
.Where(u => u.Status == (int)CommonStatus.ENABLE)
.Where(menuIdList.Count > 0, u => menuIdList.Contains(u.Id))
.OrderBy(u => u.Sort).Select(u => new MenuTreeOutput
{
Id = u.Id,
ParentId = u.Pid,
Value = u.Id.ToString(),
Title = u.Name,
Weight = u.Sort
}).ToListAsync();
return new TreeBuildUtil<MenuTreeOutput>().DoTreeBuild(menus);
}
/// <summary>
/// 根据应用编码判断该机构下是否有状态为正常的菜单
/// </summary>
/// <param name="appCode"></param>
/// <returns></returns>
[NonAction]
public async Task<bool> HasMenu(string appCode)
{
return await _sysMenuRep.DetachedEntities.AnyAsync(u => u.Application == appCode && u.Status != CommonStatus.DELETED);
}
/// <summary>
/// 根据系统应用切换菜单
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[AllowAnonymous]
[HttpPost("/sysMenu/change")]
public async Task<List<AntDesignTreeNode>> ChangeAppMenu(ChangeAppMenuInput input)
{
return await GetLoginMenusAntDesign(_userManager.UserId, input.Application);
}
}
}

View File

@@ -0,0 +1,11 @@
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
public interface IMachineService
{
Task<dynamic> GetMachineBaseInfo();
Task<dynamic> GetMachineNetWorkInfo();
Task<dynamic> GetMachineUseInfo();
}
}

View File

@@ -0,0 +1,53 @@
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
/// <summary>
/// 服务器信息服务
/// </summary>
[AllowAnonymous]
[ApiDescriptionSettings(Name = "Machine", Order = 100)]
public class MachineService : IMachineService, IDynamicApiController, ITransient
{
public MachineService()
{
}
/// <summary>
/// 获取服务器资源信息
/// </summary>
/// <returns></returns>
[HttpGet("/sysMachine/use")]
public async Task<dynamic> GetMachineUseInfo()
{
var useInfo = MachineUtil.GetMachineUseInfo();
return await Task.FromResult(useInfo);
}
/// <summary>
/// 获取服务器基本参数
/// </summary>
/// <returns></returns>
[HttpGet("/sysMachine/base")]
public async Task<dynamic> GetMachineBaseInfo()
{
return await MachineUtil.GetMachineBaseInfo();
}
/// <summary>
/// 动态获取网络信息
/// </summary>
/// <returns></returns>
[HttpGet("/sysMachine/network")]
public async Task<dynamic> GetMachineNetWorkInfo()
{
var baseInfo = MachineUtil.GetMachineNetWorkInfo();
return await Task.FromResult(baseInfo);
}
}
}

View File

@@ -0,0 +1,60 @@
using System;
namespace Ewide.Core.Service
{
/// <summary>
/// 通知公告参数
/// </summary>
public class NoticeBase
{
/// <summary>
/// 标题
/// </summary>
public string Title { get; set; }
/// <summary>
/// 内容
/// </summary>
public string Content { get; set; }
/// <summary>
/// 类型(字典 1通知 2公告
/// </summary>
public int Type { get; set; }
/// <summary>
/// 发布人Id
/// </summary>
public long PublicUserId { get; set; }
/// <summary>
/// 发布人姓名
/// </summary>
public string PublicUserName { get; set; }
/// <summary>
/// 发布机构Id
/// </summary>
public long PublicOrgId { get; set; }
/// <summary>
/// 发布机构名称
/// </summary>
public string PublicOrgName { get; set; }
/// <summary>
/// 发布时间
/// </summary>
public DateTimeOffset PublicTime { get; set; }
/// <summary>
/// 撤回时间
/// </summary>
public DateTimeOffset CancelTime { get; set; }
/// <summary>
/// 状态(字典 0草稿 1发布 2撤回 3删除
/// </summary>
public int Status { get; set; }
}
}

View File

@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
namespace Ewide.Core.Service
{
/// <summary>
/// 系统通知公告详情参数
/// </summary>
public class NoticeDetailOutput : NoticeBase
{
/// <summary>
/// 通知到的用户Id集合
/// </summary>
public List<string> NoticeUserIdList { get; set; }
/// <summary>
/// 通知到的用户阅读信息集合
/// </summary>
public List<NoticeUserRead> NoticeUserReadInfoList { get; set; }
}
public class NoticeUserRead
{
/// <summary>
/// 用户Id
/// </summary>
public string UserId { get; set; }
/// <summary>
/// 用户名称
/// </summary>
public string UserName { get; set; }
/// <summary>
/// 状态(字典 0未读 1已读
/// </summary>
public int ReadStatus { get; set; }
/// <summary>
/// 阅读时间
/// </summary>
public DateTimeOffset ReadTime { get; set; }
}
}

View File

@@ -0,0 +1,101 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Ewide.Core.Service
{
/// <summary>
/// 通知公告参数
/// </summary>
public class NoticeInput : PageInputBase
{
/// <summary>
/// 标题
/// </summary>
public virtual string Title { get; set; }
/// <summary>
/// 内容
/// </summary>
public virtual string Content { get; set; }
/// <summary>
/// 类型(字典 1通知 2公告
/// </summary>
public virtual int Type { get; set; }
/// <summary>
/// 状态(字典 0草稿 1发布 2撤回 3删除
/// </summary>
public virtual int Status { get; set; }
/// <summary>
/// 通知到的人
/// </summary>
public virtual List<string> NoticeUserIdList { get; set; }
}
public class AddNoticeInput : NoticeInput
{
/// <summary>
/// 标题
/// </summary>
[Required(ErrorMessage = "标题不能为空")]
public override string Title { get; set; }
/// <summary>
/// 内容
/// </summary>
[Required(ErrorMessage = "内容不能为空")]
public override string Content { get; set; }
/// <summary>
/// 类型(字典 1通知 2公告
/// </summary>
[Required(ErrorMessage = "类型不能为空")]
public override int Type { get; set; }
/// <summary>
/// 状态(字典 0草稿 1发布 2撤回 3删除
/// </summary>
[Required(ErrorMessage = "状态不能为空")]
public override int Status { get; set; }
/// <summary>
/// 通知到的人
/// </summary>
[Required(ErrorMessage = "通知到的人不能为空")]
public override List<string> NoticeUserIdList { get; set; }
}
public class DeleteNoticeInput
{
/// <summary>
/// Id
/// </summary>
[Required(ErrorMessage = "通知公告Id不能为空")]
public string Id { get; set; }
}
public class UpdateNoticeInput : AddNoticeInput
{
/// <summary>
/// Id
/// </summary>
[Required(ErrorMessage = "通知公告Id不能为空")]
public string Id { get; set; }
}
public class QueryNoticeInput : DeleteNoticeInput
{
}
public class ChangeStatusNoticeInput : DeleteNoticeInput
{
/// <summary>
/// 状态(字典 0草稿 1发布 2撤回 3删除
/// </summary>
[Required(ErrorMessage = "状态不能为空")]
public int Status { get; set; }
}
}

View File

@@ -0,0 +1,25 @@
using System;
namespace Ewide.Core.Service
{
/// <summary>
/// 通知公告接收参数
/// </summary>
public class NoticeReceiveOutput : NoticeBase
{
/// <summary>
/// Id
/// </summary>
public long Id { get; set; }
/// <summary>
/// 阅读状态(字典 0未读 1已读
/// </summary>
public int ReadStatus { get; set; }
/// <summary>
/// 阅读时间
/// </summary>
public DateTimeOffset ReadTime { get; set; }
}
}

View File

@@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace Ewide.Core.Service.Notice
{
public interface ISysNoticeService
{
Task AddNotice(AddNoticeInput input);
Task ChangeStatus(ChangeStatusNoticeInput input);
Task DeleteNotice(DeleteNoticeInput input);
Task<NoticeDetailOutput> GetNotice([FromQuery] QueryNoticeInput input);
Task<dynamic> QueryNoticePageList([FromQuery] NoticeInput input);
Task<dynamic> ReceivedNoticePageList([FromQuery] NoticeInput input);
Task UpdateNotice(UpdateNoticeInput input);
}
}

View File

@@ -0,0 +1,13 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Ewide.Core.Service.Notice
{
public interface ISysNoticeUserService
{
Task Add(string noticeId, List<string> noticeUserIdList, int noticeUserStatus);
Task<List<SysNoticeUser>> GetNoticeUserListByNoticeId(string noticeId);
Task Read(string noticeId, string userId, int status);
Task Update(string noticeId, List<string> noticeUserIdList, int noticeUserStatus);
}
}

View File

@@ -0,0 +1,224 @@
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using Mapster;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Ewide.Core.Service.Notice
{
/// <summary>
/// 通知公告服务
/// </summary>
[ApiDescriptionSettings(Name = "Notice", Order = 100)]
public class SysNoticeService : ISysNoticeService, IDynamicApiController, ITransient
{
private readonly IRepository<SysNotice> _sysNoticeRep; // 通知公告表仓储
private readonly IRepository<SysNoticeUser> _sysNoticeUserRep; // 通知公告用户表仓储
private readonly IUserManager _userManager;
private readonly ISysNoticeUserService _sysNoticeUserService;
public SysNoticeService(IRepository<SysNotice> sysNoticeRep,
IRepository<SysNoticeUser> sysNoticeUserRep,
IUserManager userManager,
ISysNoticeUserService sysNoticeUserService)
{
_sysNoticeRep = sysNoticeRep;
_sysNoticeUserRep = sysNoticeUserRep;
_userManager = userManager;
_sysNoticeUserService = sysNoticeUserService;
}
/// <summary>
/// 分页查询通知公告
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysNotice/page")]
public async Task<dynamic> QueryNoticePageList([FromQuery] NoticeInput input)
{
var searchValue = !string.IsNullOrEmpty(input.SearchValue?.Trim());
var notices = await _sysNoticeRep.DetachedEntities
.Where(searchValue, u => EF.Functions.Like(u.Title, $"%{input.SearchValue.Trim()}%") ||
EF.Functions.Like(u.Content, $"%{input.SearchValue.Trim()}%"))
.Where(input.Type > 0, u => u.Type == input.Type)
.Where(u => u.Status != (int)NoticeStatus.DELETED)
.ToPagedListAsync(input.PageNo, input.PageSize);
return XnPageResult<SysNotice>.PageResult(notices);
}
/// <summary>
/// 增加通知公告
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysNotice/add")]
public async Task AddNotice(AddNoticeInput input)
{
if (input.Status != (int)NoticeStatus.DRAFT && input.Status != (int)NoticeStatus.PUBLIC)
throw Oops.Oh(ErrorCode.D7000);
var notice = input.Adapt<SysNotice>();
await UpdatePublicInfo(notice);
// 如果是发布,则设置发布时间
if (input.Status == (int)NoticeStatus.PUBLIC)
notice.PublicTime = DateTimeOffset.Now;
var newItem = await notice.InsertNowAsync();
// 通知到的人
var noticeUserIdList = input.NoticeUserIdList;
var noticeUserStatus = (int)NoticeUserStatus.UNREAD;
await _sysNoticeUserService.Add(newItem.Entity.Id, noticeUserIdList, noticeUserStatus);
}
/// <summary>
/// 删除通知公告
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysNotice/delete")]
public async Task DeleteNotice(DeleteNoticeInput input)
{
var notice = await _sysNoticeRep.FirstOrDefaultAsync(u => u.Id == input.Id);
if (notice.Status != (int)NoticeStatus.DRAFT && notice.Status != (int)NoticeStatus.CANCEL) // 只能删除草稿和撤回的
throw Oops.Oh(ErrorCode.D7001);
//notice.Status = (int)NoticeStatus.DELETED;
await notice.DeleteAsync();
}
/// <summary>
/// 更新通知公告
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysNotice/edit")]
public async Task UpdateNotice(UpdateNoticeInput input)
{
if (input.Status != (int)NoticeStatus.DRAFT && input.Status != (int)NoticeStatus.PUBLIC)
throw Oops.Oh(ErrorCode.D7000);
// 非草稿状态
if (input.Status != (int)NoticeStatus.DRAFT)
throw Oops.Oh(ErrorCode.D7002);
var notice = input.Adapt<SysNotice>();
if (input.Status == (int)NoticeStatus.PUBLIC)
{
notice.PublicTime = DateTimeOffset.Now;
await UpdatePublicInfo(notice);
}
await notice.UpdateAsync();
// 通知到的人
var noticeUserIdList = input.NoticeUserIdList;
var noticeUserStatus = (int)NoticeUserStatus.UNREAD;
await _sysNoticeUserService.Update(input.Id, noticeUserIdList, noticeUserStatus);
}
/// <summary>
/// 获取通知公告详情
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysNotice/detail")]
public async Task<NoticeDetailOutput> GetNotice([FromQuery] QueryNoticeInput input)
{
var notice = await _sysNoticeRep.FirstOrDefaultAsync(u => u.Id == input.Id);
// 获取通知到的用户
var noticeUserList = await _sysNoticeUserService.GetNoticeUserListByNoticeId(input.Id);
var noticeUserIdList = new List<string>();
var noticeUserReadInfoList = new List<NoticeUserRead>();
if (noticeUserList != null)
{
noticeUserList.ForEach(u =>
{
noticeUserIdList.Add(u.UserId.ToString());
var noticeUserRead = new NoticeUserRead
{
UserId = u.UserId,
UserName = _userManager.Name,
ReadStatus = u.ReadStatus,
ReadTime = u.ReadTime
};
noticeUserReadInfoList.Add(noticeUserRead);
});
}
var noticeResult = notice.Adapt<NoticeDetailOutput>();
noticeResult.NoticeUserIdList = noticeUserIdList;
noticeResult.NoticeUserReadInfoList = noticeUserReadInfoList;
// 如果该条通知公告为已发布,则将当前用户的该条通知公告设置为已读
if (notice.Status == (int)NoticeStatus.PUBLIC)
await _sysNoticeUserService.Read(notice.Id, _userManager.UserId, (int)NoticeUserStatus.READ);
return noticeResult;
}
/// <summary>
/// 修改通知公告状态
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysNotice/changeStatus")]
public async Task ChangeStatus(ChangeStatusNoticeInput input)
{
// 状态应为撤回或删除或发布
if (input.Status != (int)NoticeStatus.CANCEL && input.Status != (int)NoticeStatus.DELETED && input.Status != (int)NoticeStatus.PUBLIC)
throw Oops.Oh(ErrorCode.D7000);
var notice = await _sysNoticeRep.FirstOrDefaultAsync(u => u.Id == input.Id);
notice.Status = input.Status;
if (input.Status == (int)NoticeStatus.CANCEL)
{
notice.CancelTime = DateTimeOffset.Now;
}
else if (input.Status == (int)NoticeStatus.PUBLIC)
{
notice.PublicTime = DateTimeOffset.Now;
}
await notice.UpdateAsync();
}
/// <summary>
/// 获取接收的通知公告
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysNotice/received")]
public async Task<dynamic> ReceivedNoticePageList([FromQuery] NoticeInput input)
{
var searchValue = !string.IsNullOrEmpty(input.SearchValue?.Trim());
var notices = await _sysNoticeRep.DetachedEntities.Join(_sysNoticeUserRep.DetachedEntities, u => u.Id, e => e.NoticeId, (u, e) => new { u, e })
.Where(u => u.e.UserId == _userManager.UserId)
.Where(searchValue, u => EF.Functions.Like(u.u.Title, $"%{input.SearchValue.Trim()}%") || EF.Functions.Like(u.u.Content, $"%{input.SearchValue.Trim()}%"))
.Where(input.Type > 0, u => u.u.Type == input.Type)
.Where(u => u.u.Status != (int)NoticeStatus.DELETED)
.Select(u => u.u.Adapt<NoticeReceiveOutput>())
.ToPagedListAsync(input.PageNo, input.PageSize);
return XnPageResult<NoticeReceiveOutput>.PageResult(notices);
}
/// <summary>
/// 更新发布信息
/// </summary>
/// <param name="notice"></param>
[NonAction]
private async Task UpdatePublicInfo(SysNotice notice)
{
var emp = await _userManager.GetUserEmpInfo(_userManager.UserId);
notice.PublicUserId = _userManager.UserId;
notice.PublicUserName = _userManager.Name;
notice.PublicOrgId = emp.OrgId;
notice.PublicOrgName = emp.OrgName;
}
}
}

View File

@@ -0,0 +1,90 @@
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Ewide.Core.Service.Notice
{
/// <summary>
/// 通知公告用户
/// </summary>
public class SysNoticeUserService : ISysNoticeUserService, ITransient
{
private readonly IRepository<SysNoticeUser> _sysNoticeUserRep; // 通知公告用户表仓储
public SysNoticeUserService(IRepository<SysNoticeUser> sysNoticeUserRep)
{
_sysNoticeUserRep = sysNoticeUserRep;
}
/// <summary>
/// 增加
/// </summary>
/// <param name="noticeId"></param>
/// <param name="noticeUserIdList"></param>
/// <param name="noticeUserStatus"></param>
/// <returns></returns>
public Task Add(string noticeId, List<string> noticeUserIdList, int noticeUserStatus)
{
noticeUserIdList.ForEach(u =>
{
new SysNoticeUser
{
NoticeId = noticeId,
UserId = u,
ReadStatus = noticeUserStatus
}.InsertAsync();
});
return Task.CompletedTask;
}
/// <summary>
/// 更新
/// </summary>
/// <param name="noticeId"></param>
/// <param name="noticeUserIdList"></param>
/// <param name="noticeUserStatus"></param>
/// <returns></returns>
public async Task Update(string noticeId, List<string> noticeUserIdList, int noticeUserStatus)
{
var noticeUsers = await _sysNoticeUserRep.Where(u => u.NoticeId == noticeId).ToListAsync();
noticeUsers.ForEach(u =>
{
u.Delete();
});
await Add(noticeId, noticeUserIdList, noticeUserStatus);
}
/// <summary>
/// 获取通知公告用户列表
/// </summary>
/// <param name="noticeId"></param>
/// <returns></returns>
public async Task<List<SysNoticeUser>> GetNoticeUserListByNoticeId(string noticeId)
{
return await _sysNoticeUserRep.Where(u => u.NoticeId == noticeId).ToListAsync();
}
/// <summary>
/// 设置通知公告读取状态
/// </summary>
/// <param name="noticeId"></param>
/// <param name="userId"></param>
/// <param name="status"></param>
/// <returns></returns>
public async Task Read(string noticeId, string userId, int status)
{
var noticeUser = await _sysNoticeUserRep.FirstOrDefaultAsync(u => u.NoticeId == noticeId && u.UserId == userId);
if (noticeUser != null)
{
noticeUser.ReadStatus = status;
noticeUser.ReadTime = DateTimeOffset.Now;
await noticeUser.UpdateAsync();
}
}
}
}

View File

@@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace Ewide.Core.Service.OAuth
{
public interface ISysOauthService
{
Task<dynamic> GetWechatUserInfo([FromQuery] string token, [FromQuery] string openId);
Task WechatLogin();
Task WechatLoginCallback([FromQuery] string code, [FromQuery] string state, [FromQuery] string error_description = "");
}
}

View File

@@ -0,0 +1,68 @@
using Dilon.Core.OAuth;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace Ewide.Core.Service.OAuth
{
/// <summary>
/// OAuth服务
/// </summary>
[ApiDescriptionSettings(Name = "OAuth", Order = 159)]
[AllowAnonymous]
public class SysOauthService : ISysOauthService, IDynamicApiController, ITransient
{
private readonly HttpContext _httpContext;
private readonly WechatOAuth _wechatOAuth;
public SysOauthService(IHttpContextAccessor httpContextAccessor, WechatOAuth wechatOAuth)
{
_httpContext = httpContextAccessor.HttpContext;
_wechatOAuth = wechatOAuth;
}
/// <summary>
/// 微信登录授权
/// </summary>
[HttpGet("oauth/wechat")]
public Task WechatLogin()
{
_httpContext.Response.Redirect(_wechatOAuth.GetAuthorizeUrl("Dilon"));
return Task.CompletedTask;
}
/// <summary>
/// 微信登录授权回调
/// </summary>
/// <param name="code"></param>
/// <param name="state"></param>
/// <param name="error_description"></param>
/// <returns></returns>
[HttpGet("oauth/wechatcallback")]
public async Task WechatLoginCallback([FromQuery] string code, [FromQuery] string state, [FromQuery] string error_description = "")
{
if (!string.IsNullOrEmpty(error_description))
throw Oops.Oh(error_description);
var accessTokenModel = await _wechatOAuth.GetAccessTokenAsync(code, state);
//var userInfoModel = await _wechatOAuth.GetUserInfoAsync(accessTokenModel.AccessToken, accessTokenModel.OpenId);
await _httpContext.Response.WriteAsJsonAsync(accessTokenModel);
}
/// <summary>
/// 获取微信用户基本信息
/// </summary>
/// <param name="token"></param>
/// <param name="openId"></param>
/// <returns></returns>
[HttpGet("oauth/wechat/user")]
public async Task<dynamic> GetWechatUserInfo([FromQuery] string token, [FromQuery] string openId)
{
return await _wechatOAuth.GetUserInfoAsync(token, openId);
}
}
}

View File

@@ -0,0 +1,93 @@
using System.ComponentModel.DataAnnotations;
namespace Ewide.Core.Service
{
/// <summary>
/// 组织机构参数
/// </summary>
public class OrgInput : XnInputBase
{
/// <summary>
/// 父Id
/// </summary>
public string Pid { get; set; }
/// <summary>
/// 父Ids
/// </summary>
public string Pids { get; set; }
/// <summary>
/// 名称
/// </summary>
public virtual string Name { get; set; }
/// <summary>
/// 编码
/// </summary>
public virtual string Code { get; set; }
/// <summary>
/// 电话
/// </summary>
public virtual string Tel { get; set; }
/// <summary>
/// 排序
/// </summary>
public int Sort { get; set; }
/// <summary>
/// 备注
/// </summary>
public string Remark { get; set; }
/// <summary>
/// 状态(字典 0正常 1停用 2删除
/// </summary>
public int Status { get; set; }
}
public class AddOrgInput : OrgInput
{
/// <summary>
/// 名称
/// </summary>
[Required(ErrorMessage = "机构名称不能为空")]
public override string Name { get; set; }
/// <summary>
/// 编码
/// </summary>
[Required(ErrorMessage = "机构编码不能为空")]
public override string Code { get; set; }
}
public class DeleteOrgInput
{
/// <summary>
/// 机构Id
/// </summary>
[Required(ErrorMessage = "机构Id不能为空")]
public string Id { get; set; }
}
public class UpdateOrgInput : AddOrgInput
{
/// <summary>
/// 机构Id
/// </summary>
[Required(ErrorMessage = "机构Id不能为空")]
public string Id { get; set; }
}
public class QueryOrgInput : DeleteOrgInput
{
}
public class PageOrgInput : OrgInput
{
public string Id { get; set; }
}
}

View File

@@ -0,0 +1,13 @@
namespace Ewide.Core.Service
{
/// <summary>
/// 组织机构参数
/// </summary>
public class OrgOutput : OrgInput
{
/// <summary>
/// 机构Id
/// </summary>
public string Id { get; set; }
}
}

View File

@@ -0,0 +1,61 @@
using System.Collections;
using System.Collections.Generic;
namespace Ewide.Core.Service
{
/// <summary>
/// 组织机构树
/// </summary>
public class OrgTreeNode : ITreeNode
{
/// <summary>
/// Id
/// </summary>
public string Id { get; set; }
/// <summary>
/// 父Id
/// </summary>
public string ParentId { get; set; }
/// <summary>
/// 名称
/// </summary>
public string Title { get; set; }
/// <summary>
/// 值
/// </summary>
public string Value { get; set; }
/// <summary>
/// 排序,越小优先级越高
/// </summary>
public int Weight { get; set; }
/// <summary>
/// 子节点
/// </summary>
public List<OrgTreeNode> Children { get; set; } = new List<OrgTreeNode>();
/// <summary>
/// 上一级Id
/// </summary>
public long Pid { get; set; }
public string GetId()
{
return Id;
}
public string GetPid()
{
return ParentId;
}
public void SetChildren(IList children)
{
Children = (List<OrgTreeNode>)children;
}
}
}

View File

@@ -0,0 +1,19 @@
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
public interface ISysOrgService
{
Task AddOrg(AddOrgInput input);
Task DeleteOrg(DeleteOrgInput input);
Task<List<string>> GetDataScopeListByDataScopeType(int dataScopeType, string orgId);
Task<SysOrg> GetOrg([FromQuery] QueryOrgInput input);
Task<List<OrgOutput>> GetOrgList([FromQuery] OrgInput input);
Task<dynamic> GetOrgTree([FromQuery] OrgInput input);
Task<dynamic> QueryOrgPageList([FromQuery] PageOrgInput input);
Task UpdateOrg(UpdateOrgInput input);
Task<List<string>> GetUserDataScopeIdList();
}
}

View File

@@ -0,0 +1,348 @@
using Furion;
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using Mapster;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
/// <summary>
/// 组织机构服务
/// </summary>
[ApiDescriptionSettings(Name = "Org", Order = 148)]
public class SysOrgService : ISysOrgService, IDynamicApiController, ITransient
{
private readonly IRepository<SysOrg> _sysOrgRep; // 组织机构表仓储
private readonly IUserManager _userManager;
private readonly ISysEmpService _sysEmpService;
private readonly ISysEmpExtOrgPosService _sysEmpExtOrgPosService;
private readonly ISysRoleDataScopeService _sysRoleDataScopeService;
private readonly ISysUserDataScopeService _sysUserDataScopeService;
public SysOrgService(IRepository<SysOrg> sysOrgRep,
IUserManager userManager,
ISysEmpService sysEmpService,
ISysEmpExtOrgPosService sysEmpExtOrgPosService,
ISysRoleDataScopeService sysRoleDataScopeService,
ISysUserDataScopeService sysUserDataScopeService)
{
_sysOrgRep = sysOrgRep;
_userManager = userManager;
_sysEmpService = sysEmpService;
_sysEmpExtOrgPosService = sysEmpExtOrgPosService;
_sysRoleDataScopeService = sysRoleDataScopeService;
_sysUserDataScopeService = sysUserDataScopeService;
}
/// <summary>
/// 分页查询组织机构
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysOrg/page")]
public async Task<dynamic> QueryOrgPageList([FromQuery] PageOrgInput input)
{
var dataScopeList = GetDataScopeList(await GetUserDataScopeIdList());
var name = !string.IsNullOrEmpty(input.Name?.Trim());
var id = !string.IsNullOrEmpty(input.Id?.Trim());
var pId = !string.IsNullOrEmpty(input.Pid?.Trim());
var orgs = await _sysOrgRep.DetachedEntities
.Where((name, u => EF.Functions.Like(u.Name, $"%{input.Name.Trim()}%")), // 根据机构名称模糊查询
(id, u => u.Id == input.Id.Trim()), // 根据机构id查询
(pId, u => EF.Functions.Like(u.Pids, $"%[{input.Pid.Trim()}]%")
|| u.Id == input.Pid.Trim())) // 根据父机构id查询
.Where(dataScopeList.Count > 0, u => dataScopeList.Contains(u.Id)) // 非管理员范围限制
.Where(u => u.Status != CommonStatus.DELETED).OrderBy(u => u.Sort)
.Select(u => u.Adapt<OrgOutput>())
.ToPagedListAsync(input.PageNo, input.PageSize);
return XnPageResult<OrgOutput>.PageResult(orgs);
}
/// <summary>
/// (非管理员)获取当前用户数据范围机构Id
/// </summary>
/// <param name="dataScopes"></param>
/// <returns></returns>
private List<string> GetDataScopeList(List<string> dataScopes)
{
var dataScopeList = new List<string>();
// 如果是超级管理员则获取所有组织机构,否则只获取其数据范围的机构数据
if (!_userManager.SuperAdmin)
{
if (dataScopes.Count < 1)
return dataScopeList;
// 此处获取所有的上级节点,用于构造完整树
dataScopes.ForEach(u =>
{
var sysOrg = _sysOrgRep.DetachedEntities.FirstOrDefault(c => c.Id == u);
var parentAndChildIdListWithSelf = sysOrg.Pids.TrimEnd(',').Replace("[", "").Replace("]", "")
.Split(",").ToList();
dataScopeList.AddRange(parentAndChildIdListWithSelf);
});
}
return dataScopeList;
}
/// <summary>
/// 获取组织机构列表
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysOrg/list")]
public async Task<List<OrgOutput>> GetOrgList([FromQuery] OrgInput input)
{
var dataScopeList = GetDataScopeList(await GetUserDataScopeIdList());
var pId = !string.IsNullOrEmpty(input.Pid?.Trim());
var orgs = await _sysOrgRep.DetachedEntities
.Where(pId, u => u.Pid == input.Pid)
.Where(dataScopeList.Count > 0, u => dataScopeList.Contains(u.Id))
.Where(u => u.Status != CommonStatus.DELETED).OrderBy(u => u.Sort).ToListAsync();
return orgs.Adapt<List<OrgOutput>>();
}
/// <summary>
/// 增加组织机构
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysOrg/add")]
public async Task AddOrg(AddOrgInput input)
{
var isExist = await _sysOrgRep.DetachedEntities.AnyAsync(u => u.Name == input.Name || u.Code == input.Code);
if (isExist)
throw Oops.Oh(ErrorCode.D2002);
if (!_userManager.SuperAdmin)
{
// 如果新增的机构父Id不是0则进行数据权限校验
if (input.Pid != "0" && !string.IsNullOrEmpty(input.Pid))
{
// 新增组织机构的父机构不在自己的数据范围内
var dataScopes = await GetUserDataScopeIdList();
if (dataScopes.Count < 1 || !dataScopes.Contains(input.Pid))
throw Oops.Oh(ErrorCode.D2003);
}
else
throw Oops.Oh(ErrorCode.D2003);
}
var sysOrg = input.Adapt<SysOrg>();
await FillPids(sysOrg);
await sysOrg.InsertAsync();
}
/// <summary>
/// 填充父Ids字段
/// </summary>
/// <param name="sysOrg"></param>
/// <returns></returns>
private async Task FillPids(SysOrg sysOrg)
{
if (sysOrg.Pid.Equals(System.Guid.Empty.ToString()))
{
sysOrg.Pids = "[" + System.Guid.Empty + "],";
}
else
{
var t = await _sysOrgRep.DetachedEntities.FirstOrDefaultAsync(u => u.Id == sysOrg.Pid);
sysOrg.Pids = t.Pids + "[" + t.Id + "],";
}
}
/// <summary>
/// 删除组织机构
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysOrg/delete")]
[UnitOfWork]
public async Task DeleteOrg(DeleteOrgInput input)
{
var sysOrg = await _sysOrgRep.DetachedEntities.FirstOrDefaultAsync(u => u.Id == input.Id);
// 检测数据范围能不能操作这个机构
var dataScopes = await GetUserDataScopeIdList();
if (!_userManager.SuperAdmin && (dataScopes.Count < 1 || !dataScopes.Contains(sysOrg.Id)))
throw Oops.Oh(ErrorCode.D2003);
// 该机构下有员工,则不能删
var hasOrgEmp = await _sysEmpService.HasOrgEmp(sysOrg.Id);
if (hasOrgEmp)
throw Oops.Oh(ErrorCode.D2004);
// 该附属机构下若有员工,则不能删
var hasExtOrgEmp = await _sysEmpExtOrgPosService.HasExtOrgEmp(sysOrg.Id);
if (hasExtOrgEmp)
throw Oops.Oh(ErrorCode.D2005);
// 级联删除子节点
var childIdList = await GetChildIdListWithSelfById(sysOrg.Id);
var orgs = await _sysOrgRep.Where(u => childIdList.Contains(u.Id)).ToListAsync();
orgs.ForEach(u =>
{
u.Delete();
});
// 级联删除该机构及子机构对应的角色-数据范围关联信息
await _sysRoleDataScopeService.DeleteRoleDataScopeListByOrgIdList(childIdList);
// 级联删除该机构子机构对应的用户-数据范围关联信息
await _sysUserDataScopeService.DeleteUserDataScopeListByOrgIdList(childIdList);
}
/// <summary>
/// 更新组织机构
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysOrg/edit")]
[UnitOfWork]
public async Task UpdateOrg(UpdateOrgInput input)
{
if (input.Pid != "0" && !string.IsNullOrEmpty(input.Pid))
{
var org = await _sysOrgRep.DetachedEntities.FirstOrDefaultAsync(u => u.Id == input.Pid);
_ = org ?? throw Oops.Oh(ErrorCode.D2000);
}
if (input.Id == input.Pid)
throw Oops.Oh(ErrorCode.D2001);
// 如果是编辑父id不能为自己的子节点
var childIdListById = await GetChildIdListWithSelfById(input.Id);
if (childIdListById.Contains(input.Pid))
throw Oops.Oh(ErrorCode.D2001);
var sysOrg = await _sysOrgRep.DetachedEntities.FirstOrDefaultAsync(u => u.Id == input.Id);
// 检测数据范围能不能操作这个机构
var dataScopes = await GetUserDataScopeIdList();
if (!_userManager.SuperAdmin && (dataScopes.Count < 1 || !dataScopes.Contains(sysOrg.Id)))
throw Oops.Oh(ErrorCode.D2003);
var isExist = await _sysOrgRep.DetachedEntities.AnyAsync(u => (u.Name == input.Name || u.Code == input.Code) && u.Id != sysOrg.Id);
if (isExist)
throw Oops.Oh(ErrorCode.D2002);
// 如果名称有变化,则修改对应员工的机构相关信息
if (!sysOrg.Name.Equals(input.Name))
await _sysEmpService.UpdateEmpOrgInfo(sysOrg.Id, sysOrg.Name);
sysOrg = input.Adapt<SysOrg>();
await FillPids(sysOrg);
await sysOrg.UpdateAsync(ignoreNullValues: true);
//// 将所有子的父id进行更新
//childIdListById.ForEach(u=> {
// var child = _sysOrgRep.DetachedEntities.FirstOrDefaultAsync(u => u.Id == u.Id);
// var newInput = child.Adapt<UpdateOrgInput>();
// UpdateOrg(newInput).GetAwaiter();
//});
}
/// <summary>
/// 获取组织机构信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysOrg/detail")]
public async Task<SysOrg> GetOrg([FromQuery] QueryOrgInput input)
{
return await _sysOrgRep.DetachedEntities.FirstOrDefaultAsync(u => u.Id == input.Id);
}
/// <summary>
/// 根据节点Id获取所有子节点Id集合包含自己
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
private async Task<List<string>> GetChildIdListWithSelfById(string id)
{
var childIdList = await _sysOrgRep.DetachedEntities
.Where(u => EF.Functions.Like(u.Pids, $"%{id}%"))
.Select(u => u.Id).ToListAsync();
childIdList.Add(id);
return childIdList;
}
/// <summary>
/// 获取组织机构树
/// </summary>
/// <returns></returns>
[HttpGet("/sysOrg/tree")]
public async Task<dynamic> GetOrgTree([FromQuery] OrgInput input)
{
var dataScopeList = new List<string>();
if (!_userManager.SuperAdmin)
{
var dataScopes = await GetUserDataScopeIdList();
if (dataScopes.Count < 1)
return dataScopeList;
dataScopeList = GetDataScopeList(dataScopes);
}
var orgs = await _sysOrgRep.DetachedEntities.Where(dataScopeList.Count > 0, u => dataScopeList.Contains(u.Id))
.Where(u => u.Status == (int)CommonStatus.ENABLE).OrderBy(u => u.Sort)
.Select(u => new OrgTreeNode
{
Id = u.Id,
ParentId = u.Pid,
Title = u.Name,
Value = u.Id.ToString(),
Weight = u.Sort
}).ToListAsync();
return new TreeBuildUtil<OrgTreeNode>().DoTreeBuild(orgs);
}
/// <summary>
/// 根据数据范围类型获取当前用户的数据范围机构Id集合
/// </summary>
/// <param name="dataScopeType"></param>
/// <param name="orgId"></param>
/// <returns></returns>
[NonAction]
public async Task<List<string>> GetDataScopeListByDataScopeType(int dataScopeType, string orgId)
{
var orgIdList = new List<string>();
if (string.IsNullOrEmpty(orgId))
return orgIdList;
// 如果是范围类型是全部数据则获取当前所有的组织架构Id
if (dataScopeType == (int)DataScopeType.ALL)
{
orgIdList = await _sysOrgRep.DetachedEntities.Where(u => u.Status != (int)CommonStatus.ENABLE).Select(u => u.Id).ToListAsync();
}
// 如果范围类型是本部门及以下部门,则查询本节点和子节点集合,包含本节点
else if (dataScopeType == (int)DataScopeType.DEPT_WITH_CHILD)
{
orgIdList = await GetChildIdListWithSelfById(orgId);
}
// 如果数据范围是本部门,不含子节点,则直接返回本部门
else if (dataScopeType == (int)DataScopeType.DEPT)
{
orgIdList.Add(orgId);
}
return orgIdList;
}
/// <summary>
/// 获取用户数据范围机构Id集合
/// </summary>
/// <returns></returns>
[NonAction]
public async Task<List<string>> GetUserDataScopeIdList()
{
return await App.GetService<ISysUserService>().GetUserDataScopeIdList();
}
}
}

View File

@@ -0,0 +1,83 @@
using System.ComponentModel.DataAnnotations;
namespace Ewide.Core.Service
{
/// <summary>
/// 职位参数
/// </summary>
public class PosInput
{
/// <summary>
/// 名称
/// </summary>
public virtual string Name { get; set; }
/// <summary>
/// 编码
/// </summary>
public virtual string Code { get; set; }
/// <summary>
/// 排序
/// </summary>
public int Sort { get; set; }
/// <summary>
/// 备注
/// </summary>
public string Remark { get; set; }
/// <summary>
/// 状态(字典 0正常 1停用 2删除
/// </summary>
public int Status { get; set; }
/// <summary>
/// 当前页码
/// </summary>
public int PageNo { get; set; } = 1;
/// <summary>
/// 页码容量
/// </summary>
public int PageSize { get; set; } = 20;
}
public class AddPosInput : PosInput
{
/// <summary>
/// 名称
/// </summary>
[Required(ErrorMessage ="职位名称不能为空")]
public override string Name { get; set; }
/// <summary>
/// 编码
/// </summary>
[Required(ErrorMessage = "职位编码不能为空")]
public override string Code { get; set; }
}
public class DeletePosInput
{
/// <summary>
/// 职位Id
/// </summary>
[Required(ErrorMessage = "职位Id不能为空")]
public string Id { get; set; }
}
public class UpdatePosInput : AddPosInput
{
/// <summary>
/// 职位Id
/// </summary>
[Required(ErrorMessage = "职位Id不能为空")]
public string Id { get; set; }
}
public class QueryPosInput : DeletePosInput
{
}
}

View File

@@ -0,0 +1,15 @@
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
public interface ISysPosService
{
Task AddPos(AddPosInput input);
Task DeletePos(DeletePosInput input);
Task<SysPos> GetPos([FromQuery] QueryPosInput input);
Task<dynamic> GetPosList([FromQuery] PosInput input);
Task<dynamic> QueryPosPageList([FromQuery] PosInput input);
Task UpdatePos(UpdatePosInput input);
}
}

View File

@@ -0,0 +1,130 @@
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using Mapster;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
/// <summary>
/// 职位服务
/// </summary>
[ApiDescriptionSettings(Name = "Pos", Order = 147)]
public class SysPosService : ISysPosService, IDynamicApiController, ITransient
{
private readonly IRepository<SysPos> _sysPosRep; // 职位表仓储
private readonly ISysEmpPosService _sysEmpPosService;
private readonly ISysEmpExtOrgPosService _sysEmpExtOrgPosService;
public SysPosService(IRepository<SysPos> sysPosRep,
ISysEmpPosService sysEmpPosService,
ISysEmpExtOrgPosService sysEmpExtOrgPosService)
{
_sysPosRep = sysPosRep;
_sysEmpPosService = sysEmpPosService;
_sysEmpExtOrgPosService = sysEmpExtOrgPosService;
}
/// <summary>
/// 分页获取职位
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysPos/page")]
public async Task<dynamic> QueryPosPageList([FromQuery] PosInput input)
{
var name = !string.IsNullOrEmpty(input.Name?.Trim());
var code = !string.IsNullOrEmpty(input.Code?.Trim());
var pos = await _sysPosRep.DetachedEntities
.Where((name, u => EF.Functions.Like(u.Name, $"%{input.Name.Trim()}%")),
(code, u => EF.Functions.Like(u.Code, $"%{input.Code.Trim()}%")))
.Where(u => u.Status == CommonStatus.ENABLE).OrderBy(u => u.Sort)
.ToPagedListAsync(input.PageNo, input.PageSize);
return XnPageResult<SysPos>.PageResult(pos);
}
/// <summary>
/// 获取职位列表
/// </summary>
/// <returns></returns>
[HttpGet("/sysPos/list")]
public async Task<dynamic> GetPosList([FromQuery] PosInput input)
{
var code = !string.IsNullOrEmpty(input.Code?.Trim());
return await _sysPosRep.DetachedEntities.Where(code, u => EF.Functions.Like(u.Code, $"%{input.Code.Trim()}%"))
.Where(u => u.Status != CommonStatus.DELETED)
.OrderBy(u => u.Sort).ToListAsync();
}
/// <summary>
/// 增加职位
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysPos/add")]
public async Task AddPos(AddPosInput input)
{
var isExist = await _sysPosRep.DetachedEntities.AnyAsync(u => u.Name == input.Name || u.Code == input.Code);
if (isExist)
throw Oops.Oh(ErrorCode.D6000);
var pos = input.Adapt<SysPos>();
await pos.InsertAsync();
}
/// <summary>
/// 删除职位
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysPos/delete")]
public async Task DeletePos(DeletePosInput input)
{
// 该职位下是否有员工
var hasPosEmp = await _sysEmpPosService.HasPosEmp(input.Id);
if (hasPosEmp)
throw Oops.Oh(ErrorCode.D6001);
// 该附属职位下是否有员工
var hasExtPosEmp = await _sysEmpExtOrgPosService.HasExtPosEmp(input.Id);
if (hasExtPosEmp)
throw Oops.Oh(ErrorCode.D6001);
var pos = await _sysPosRep.FirstOrDefaultAsync(u => u.Id == input.Id);
await pos.DeleteAsync();
}
/// <summary>
/// 更新职位
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysPos/edit")]
public async Task UpdatePos(UpdatePosInput input)
{
var isExist = await _sysPosRep.DetachedEntities.AnyAsync(u => (u.Name == input.Name || u.Code == input.Code) && u.Id != input.Id);
if (isExist)
throw Oops.Oh(ErrorCode.D6000);
var pos = input.Adapt<SysPos>();
await pos.UpdateAsync(ignoreNullValues: true);
}
/// <summary>
/// 获取职位
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysPos/detail")]
public async Task<SysPos> GetPos([FromQuery] QueryPosInput input)
{
return await _sysPosRep.DetachedEntities.FirstOrDefaultAsync(u => u.Id == input.Id);
}
}
}

View File

@@ -0,0 +1,87 @@
using System.ComponentModel.DataAnnotations;
namespace Ewide.Core.Service
{
/// <summary>
/// 角色参数
/// </summary>
public class RoleInput : XnInputBase
{
/// <summary>
/// 名称
/// </summary>
public virtual string Name { get; set; }
/// <summary>
/// 编码
/// </summary>
public virtual string Code { get; set; }
/// <summary>
/// 排序
/// </summary>
public int Sort { get; set; }
/// <summary>
/// 数据范围类型(字典 1全部数据 2本部门及以下数据 3本部门数据 4仅本人数据 5自定义数据
/// </summary>
public int DataScopeType { get; set; }
/// <summary>
/// 备注
/// </summary>
public string Remark { get; set; }
}
public class AddRoleInput : RoleInput
{
/// <summary>
/// 名称
/// </summary>
[Required(ErrorMessageResourceName = "角色名称不能为空")]
public override string Name { get; set; }
/// <summary>
/// 编码
/// </summary>
[Required(ErrorMessageResourceName = "角色编码不能为空")]
public override string Code { get; set; }
}
public class DeleteRoleInput
{
/// <summary>
/// 角色Id
/// </summary>
[Required(ErrorMessageResourceName = "角色Id不能为空")]
public string Id { get; set; }
}
public class UpdateRoleInput : AddRoleInput
{
/// <summary>
/// 角色Id
/// </summary>
[Required(ErrorMessageResourceName = "角色Id不能为空")]
public string Id { get; set; }
}
public class QueryRoleInput : DeleteRoleInput
{
}
public class GrantRoleMenuInput : RoleInput
{
/// <summary>
/// 角色Id
/// </summary>
[Required(ErrorMessageResourceName = "角色Id不能为空")]
public string Id { get; set; }
}
public class GrantRoleDataInput : GrantRoleMenuInput
{
}
}

View File

@@ -0,0 +1,23 @@
namespace Ewide.Core.Service
{
/// <summary>
/// 登录用户角色参数
/// </summary>
public class RoleOutput
{
/// <summary>
/// Id
/// </summary>
public long Id { get; set; }
/// <summary>
/// 编码
/// </summary>
public string Code { get; set; }
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; }
}
}

View File

@@ -0,0 +1,13 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
public interface ISysRoleDataScopeService
{
Task DeleteRoleDataScopeListByOrgIdList(List<string> orgIdList);
Task DeleteRoleDataScopeListByRoleId(string roleId);
Task<List<string>> GetRoleDataScopeIdList(List<string> roleIdList);
Task GrantDataScope(GrantRoleDataInput input);
}
}

View File

@@ -0,0 +1,13 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
public interface ISysRoleMenuService
{
Task DeleteRoleMenuListByMenuIdList(List<string> menuIdList);
Task DeleteRoleMenuListByRoleId(string roleId);
Task<List<string>> GetRoleMenuIdList(List<string> roleIdList);
Task GrantMenu(GrantRoleMenuInput input);
}
}

View File

@@ -0,0 +1,24 @@
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
public interface ISysRoleService
{
Task AddRole(AddRoleInput input);
Task DeleteRole(DeleteRoleInput input);
Task<string> GetNameByRoleId(string roleId);
Task<dynamic> GetRoleDropDown();
Task<SysRole> GetRoleInfo([FromQuery] QueryRoleInput input);
Task<dynamic> GetRoleList([FromQuery] RoleInput input);
Task<List<string>> GetUserDataScopeIdList(List<string> roleIdList, string orgId);
Task<List<RoleOutput>> GetUserRoleList(string userId);
Task GrantData(GrantRoleDataInput input);
Task GrantMenu(GrantRoleMenuInput input);
Task<List<string>> OwnData([FromQuery] QueryRoleInput input);
Task<List<string>> OwnMenu([FromQuery] QueryRoleInput input);
Task<dynamic> QueryRolePageList([FromQuery] RoleInput input);
Task UpdateRole(UpdateRoleInput input);
}
}

View File

@@ -0,0 +1,87 @@
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
/// <summary>
/// 角色数据范围服务
/// </summary>
public class SysRoleDataScopeService : ISysRoleDataScopeService, ITransient
{
private readonly IRepository<SysRoleDataScope> _sysRoleDataScopeRep; // 角色数据范围表仓储
public SysRoleDataScopeService(IRepository<SysRoleDataScope> sysRoleDataScopeRep)
{
_sysRoleDataScopeRep = sysRoleDataScopeRep;
}
/// <summary>
/// 授权角色数据范围
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[UnitOfWork]
public async Task GrantDataScope(GrantRoleDataInput input)
{
var dataScopes = await _sysRoleDataScopeRep.DetachedEntities.Where(u => u.SysRoleId == input.Id).ToListAsync();
dataScopes.ForEach(u =>
{
u.Delete();
});
input.GrantOrgIdList.ForEach(u =>
{
new SysRoleDataScope
{
SysRoleId = input.Id,
SysOrgId = u
}.Insert();
});
}
/// <summary>
/// 根据角色Id集合获取角色数据范围集合
/// </summary>
/// <param name="roleIdList"></param>
/// <returns></returns>
public async Task<List<string>> GetRoleDataScopeIdList(List<string> roleIdList)
{
return await _sysRoleDataScopeRep.DetachedEntities
.Where(u => roleIdList.Contains(u.SysRoleId))
.Select(u => u.SysOrgId).ToListAsync();
}
/// <summary>
/// 根据机构Id集合删除对应的角色-数据范围关联信息
/// </summary>
/// <param name="orgIdList"></param>
/// <returns></returns>
public async Task DeleteRoleDataScopeListByOrgIdList(List<string> orgIdList)
{
var dataScopes = await _sysRoleDataScopeRep.DetachedEntities.Where(u => orgIdList.Contains(u.SysOrgId)).ToListAsync();
dataScopes.ForEach(u =>
{
u.Delete();
});
}
/// <summary>
/// 根据角色Id删除对应的角色-数据范围关联信息
/// </summary>
/// <param name="roleId"></param>
/// <returns></returns>
public async Task DeleteRoleDataScopeListByRoleId(string roleId)
{
var dataScopes = await _sysRoleDataScopeRep.DetachedEntities.Where(u => u.SysRoleId == roleId).ToListAsync();
dataScopes.ForEach(u =>
{
u.Delete();
});
}
}
}

View File

@@ -0,0 +1,93 @@
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
/// <summary>
/// 角色菜单
/// </summary>
public class SysRoleMenuService : ISysRoleMenuService, ITransient
{
private readonly IRepository<SysRoleMenu> _sysRoleMenuRep; // 角色菜单表仓储
private readonly ISysCacheService _sysCacheService;
public SysRoleMenuService(IRepository<SysRoleMenu> sysRoleMenuRep, ISysCacheService sysCacheService)
{
_sysRoleMenuRep = sysRoleMenuRep;
_sysCacheService = sysCacheService;
}
/// <summary>
/// 获取角色的菜单Id集合
/// </summary>
/// <param name="roleIdList"></param>
/// <returns></returns>
public async Task<List<string>> GetRoleMenuIdList(List<string> roleIdList)
{
return await _sysRoleMenuRep.DetachedEntities
.Where(u => roleIdList.Contains(u.SysRoleId))
.Select(u => u.SysMenuId).ToListAsync();
}
/// <summary>
/// 授权角色菜单
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[UnitOfWork]
public async Task GrantMenu(GrantRoleMenuInput input)
{
var roleMenus = await _sysRoleMenuRep.DetachedEntities.Where(u => u.SysRoleId == input.Id).ToListAsync();
roleMenus.ForEach(u =>
{
u.Delete();
});
input.GrantMenuIdList.ForEach(u =>
{
new SysRoleMenu
{
SysRoleId = input.Id,
SysMenuId = u
}.Insert();
});
// 清除缓存
await _sysCacheService.DelByPatternAsync(CommonConst.CACHE_KEY_MENU);
await _sysCacheService.DelByPatternAsync(CommonConst.CACHE_KEY_PERMISSION);
}
/// <summary>
/// 根据菜单Id集合删除对应的角色-菜单表信息
/// </summary>
/// <param name="menuIdList"></param>
/// <returns></returns>
public async Task DeleteRoleMenuListByMenuIdList(List<string> menuIdList)
{
var roleMenus = await _sysRoleMenuRep.DetachedEntities.Where(u => menuIdList.Contains(u.SysMenuId)).ToListAsync();
roleMenus.ForEach(u =>
{
u.Delete();
});
}
/// <summary>
/// 根据角色Id删除对应的角色-菜单表关联信息
/// </summary>
/// <param name="roleId"></param>
/// <returns></returns>
public async Task DeleteRoleMenuListByRoleId(string roleId)
{
var roleMenus = await _sysRoleMenuRep.DetachedEntities.Where(u => u.SysRoleId == roleId).ToListAsync();
roleMenus.ForEach(u =>
{
u.Delete();
});
}
}
}

View File

@@ -0,0 +1,305 @@
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using Mapster;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
/// <summary>
/// 角色服务
/// </summary>
[ApiDescriptionSettings(Name = "Role", Order = 149)]
public class SysRoleService : ISysRoleService, IDynamicApiController, ITransient
{
private readonly IRepository<SysRole> _sysRoleRep; // 角色表仓储
private readonly IRepository<SysUserRole> _sysUserRoleRep; // 用户角色表仓储
private readonly IUserManager _userManager;
private readonly ISysRoleDataScopeService _sysRoleDataScopeService;
private readonly ISysOrgService _sysOrgService;
private readonly ISysRoleMenuService _sysRoleMenuService;
public SysRoleService(IRepository<SysRole> sysRoleRep,
IRepository<SysUserRole> sysUserRoleRep,
IUserManager userManager,
ISysRoleDataScopeService sysRoleDataScopeService,
ISysOrgService sysOrgService,
ISysRoleMenuService sysRoleMenuService)
{
_sysRoleRep = sysRoleRep;
_sysUserRoleRep = sysUserRoleRep;
_userManager = userManager;
_sysRoleDataScopeService = sysRoleDataScopeService;
_sysOrgService = sysOrgService;
_sysRoleMenuService = sysRoleMenuService;
}
/// <summary>
/// 获取用户角色相关信息(登录)
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
[NonAction]
public async Task<List<RoleOutput>> GetUserRoleList(string userId)
{
return await _sysRoleRep.DetachedEntities.Join(_sysUserRoleRep.DetachedEntities, u => u.Id, e => e.SysRoleId, (u, e) => new { u, e })
.Where(x => x.e.SysUserId == userId)
.Select(x => x.u.Adapt<RoleOutput>()).ToListAsync();
}
/// <summary>
/// 分页获取角色列表
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysRole/page")]
public async Task<dynamic> QueryRolePageList([FromQuery] RoleInput input)
{
var name = !string.IsNullOrEmpty(input.Name?.Trim());
var code = !string.IsNullOrEmpty(input.Code?.Trim());
var roles = await _sysRoleRep.DetachedEntities
.Where((name, u => EF.Functions.Like(u.Name, $"%{input.Name.Trim()}%")),
(code, u => EF.Functions.Like(u.Code, $"%{input.Code.Trim()}%")))
.Where(u => u.Status == (int)CommonStatus.ENABLE).OrderBy(u => u.Sort)
.ToPagedListAsync(input.PageNo, input.PageSize);
return XnPageResult<SysRole>.PageResult(roles);
}
/// <summary>
/// 获取角色列表
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[NonAction]
public async Task<dynamic> GetRoleList([FromQuery] RoleInput input)
{
var name = !string.IsNullOrEmpty(input.Name?.Trim());
var code = !string.IsNullOrEmpty(input.Code?.Trim());
return await _sysRoleRep.DetachedEntities
.Where((name, u => EF.Functions.Like(u.Name, $"%{input.Name.Trim()}%")),
(code, u => EF.Functions.Like(u.Code, $"%{input.Code.Trim()}%")))
.Where(u => u.Status == (int)CommonStatus.ENABLE).OrderBy(u => u.Sort)
.Select(u => new
{
u.Id,
Name = u.Name + "[" + u.Code + "]"
}).ToListAsync();
}
/// <summary>
/// 角色下拉(用于授权角色时选择)
/// </summary>
/// <returns></returns>
[HttpGet("/sysRole/dropDown")]
public async Task<dynamic> GetRoleDropDown()
{
// 如果不是超级管理员,则查询自己拥有的角色集合
var roles = _userManager.SuperAdmin
? await _sysUserRoleRep.Where(u => u.SysUserId == _userManager.UserId).Select(u => u.SysRoleId).ToListAsync()
: new List<string>();
return await _sysRoleRep.DetachedEntities
.Where(roles.Count > 0, u => roles.Contains(u.Id))
.Where(u => u.Status == (int)CommonStatus.ENABLE)
.Select(u => new
{
u.Id,
u.Code,
u.Name
}).ToListAsync();
}
/// <summary>
/// 增加角色
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysRole/add")]
public async Task AddRole(AddRoleInput input)
{
var isExist = await _sysRoleRep.DetachedEntities.AnyAsync(u => u.Code == input.Code || u.Name == input.Name);
if (isExist)
throw Oops.Oh(ErrorCode.D1006);
var role = input.Adapt<SysRole>();
role.DataScopeType = 1; // 新角色默认全部数据范围
await role.InsertAsync();
}
/// <summary>
/// 删除角色
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysRole/delete")]
[UnitOfWork]
public async Task DeleteRole(DeleteRoleInput input)
{
var sysRole = await _sysRoleRep.FirstOrDefaultAsync(u => u.Id == input.Id);
await sysRole.DeleteAsync();
//级联删除该角色对应的角色-数据范围关联信息
await _sysRoleDataScopeService.DeleteRoleDataScopeListByRoleId(sysRole.Id);
////级联删除该角色对应的用户-角色表关联信息
//await _sysUserRoleService.DeleteUserRoleListByRoleId(sysRole.Id); // 避免循环引用,故用下面逻辑
var userRoles = await _sysUserRoleRep.Where(u => u.SysRoleId == sysRole.Id).ToListAsync();
userRoles.ForEach(u =>
{
u.Delete();
});
//级联删除该角色对应的角色-菜单表关联信息
await _sysRoleMenuService.DeleteRoleMenuListByRoleId(sysRole.Id);
}
/// <summary>
/// 更新角色
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysRole/edit")]
public async Task UpdateRole(UpdateRoleInput input)
{
var isExist = await _sysRoleRep.DetachedEntities.AnyAsync(u => (u.Name == input.Name || u.Code == input.Code) && u.Id != input.Id);
if (isExist)
throw Oops.Oh(ErrorCode.D1006);
var sysRole = input.Adapt<SysRole>();
await sysRole.UpdateAsync();
}
/// <summary>
/// 获取角色
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysRole/detail")]
public async Task<SysRole> GetRoleInfo([FromQuery] QueryRoleInput input)
{
return await _sysRoleRep.DetachedEntities.FirstOrDefaultAsync(u => u.Id == input.Id);
}
/// <summary>
/// 授权角色菜单
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysRole/grantMenu")]
public async Task GrantMenu(GrantRoleMenuInput input)
{
await _sysRoleMenuService.GrantMenu(input);
}
/// <summary>
/// 授权角色数据
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysRole/grantData")]
public async Task GrantData(GrantRoleDataInput input)
{
var role = await _sysRoleRep.FirstOrDefaultAsync(u => u.Id == input.Id);
var dataScopeType = input.DataScopeType;
if (!_userManager.SuperAdmin)
{
//如果授权的角色的数据范围类型为全部,则没权限,只有超级管理员有
if ((int)DataScopeType.ALL == dataScopeType)
throw Oops.Oh(ErrorCode.D1016);
//如果授权的角色数据范围类型为自定义,则要判断授权的数据范围是否在自己的数据范围内
if ((int)DataScopeType.DEFINE == dataScopeType)
{
var dataScopes = await _sysOrgService.GetUserDataScopeIdList();
var grantOrgIdList = input.GrantOrgIdList; //要授权的数据范围列表
if (grantOrgIdList.Count > 0)
{
if (dataScopes.Count < 1)
throw Oops.Oh(ErrorCode.D1016);
else if (!dataScopes.All(u => grantOrgIdList.Any(c => c == u)))
throw Oops.Oh(ErrorCode.D1016);
}
}
}
role.DataScopeType = dataScopeType;
await _sysRoleDataScopeService.GrantDataScope(input);
}
/// <summary>
/// 根据角色Id集合获取数据范围Id集合
/// </summary>
/// <param name="roleIdList"></param>
/// <param name="orgId"></param>
/// <returns></returns>
[NonAction]
public async Task<List<string>> GetUserDataScopeIdList(List<string> roleIdList, string orgId)
{
// 定义角色中最大数据范围的类型目前按最大范围策略来如果你同时拥有ALL和SELF的权限最后按ALL返回
int strongerDataScopeType = (int)DataScopeType.SELF;
var customDataScopeRoleIdList = new List<string>();
if (roleIdList != null && roleIdList.Count > 0)
{
var roles = await _sysRoleRep.DetachedEntities.Where(u => roleIdList.Contains(u.Id)).ToListAsync();
roles.ForEach(u =>
{
if (u.DataScopeType == (int)DataScopeType.DEFINE)
customDataScopeRoleIdList.Add(u.Id);
else if (u.DataScopeType <= strongerDataScopeType)
strongerDataScopeType = u.DataScopeType;
});
}
// 自定义数据范围的角色对应的数据范围
var roleDataScopeIdList = await _sysRoleDataScopeService.GetRoleDataScopeIdList(customDataScopeRoleIdList);
// 角色中拥有最大数据范围类型的数据范围
var dataScopeIdList = await _sysOrgService.GetDataScopeListByDataScopeType(strongerDataScopeType, orgId);
return roleDataScopeIdList.Concat(dataScopeIdList).Distinct().ToList(); //并集
}
/// <summary>
/// 根据角色Id获取角色名称
/// </summary>
/// <param name="roleId"></param>
/// <returns></returns>
[NonAction]
public async Task<string> GetNameByRoleId(string roleId)
{
var role = await _sysRoleRep.DetachedEntities.FirstOrDefaultAsync(u => u.Id == roleId);
if (role == null)
throw Oops.Oh(ErrorCode.D1002);
return role.Name;
}
/// <summary>
/// 获取角色拥有菜单Id集合
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysRole/ownMenu")]
public async Task<List<string>> OwnMenu([FromQuery] QueryRoleInput input)
{
return await _sysRoleMenuService.GetRoleMenuIdList(new List<string> { input.Id });
}
/// <summary>
/// 获取角色拥有数据Id集合
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysRole/ownData")]
public async Task<List<string>> OwnData([FromQuery] QueryRoleInput input)
{
return await _sysRoleDataScopeService.GetRoleDataScopeIdList(new List<string> { input.Id });
}
}
}

View File

@@ -0,0 +1,94 @@
using System.ComponentModel.DataAnnotations;
namespace Ewide.Core.Service
{
/// <summary>
/// 租户参数
/// </summary>
public class TenantInput : PageInputBase
{
/// <summary>
/// 名称
/// </summary>
public virtual string Name { get; set; }
/// <summary>
/// 主机
/// </summary>
public virtual string Host { get; set; }
/// <summary>
/// 电子邮箱
/// </summary>
public string Email { get; set; }
/// <summary>
/// 电话号码
/// </summary>
public string Phone { get; set; }
/// <summary>
/// 模式
/// </summary>
public string Schema { get; set; }
/// <summary>
/// 数据库连接
/// </summary>
public virtual string Connection { get; set; }
/// <summary>
/// 备注
/// </summary>
public string Remark { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public string CreatedTime { get; set; }
}
public class AddTenantInput : TenantInput
{
/// <summary>
/// 租户名称
/// </summary>
[Required(ErrorMessage = "租户名称不能为空")]
public override string Name { get; set; }
/// <summary>
/// 主机名称
/// </summary>
[Required(ErrorMessage = "主机名称不能为空")]
public override string Host { get; set; }
/// <summary>
/// 数据库连接
/// </summary>
[Required(ErrorMessage = "数据库连接不能为空")]
public override string Connection { get; set; }
}
public class DeleteTenantInput
{
/// <summary>
/// 租户Id
/// </summary>
[Required(ErrorMessage = "租户Id不能为空")]
public string Id { get; set; }
}
public class UpdateTenantInput : TenantInput
{
/// <summary>
/// 租户Id
/// </summary>
[Required(ErrorMessage = "租户Id不能为空")]
public string Id { get; set; }
}
public class QueryTenantInput : DeleteTenantInput
{
}
}

View File

@@ -0,0 +1,53 @@
namespace Ewide.Core.Service
{
/// <summary>
/// 租户参数
/// </summary>
public class TenantOutput
{
/// <summary>
/// 租户Id
/// </summary>
public long Id { get; set; }
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 主机
/// </summary>
public string Host { get; set; }
/// <summary>
/// 电子邮箱
/// </summary>
public string Email { get; set; }
/// <summary>
/// 电话号码
/// </summary>
public string Phone { get; set; }
/// <summary>
/// 模式
/// </summary>
public string Schema { get; set; }
/// <summary>
/// 数据库连接
/// </summary>
public string Connection { get; set; }
/// <summary>
/// 备注
/// </summary>
public string Remark { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public string CreatedTime { get; set; }
}
}

View File

@@ -0,0 +1,14 @@
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
public interface ISysTenantService
{
Task AddTenant(AddTenantInput input);
Task DeleteTenant(DeleteTenantInput input);
Task<SysTenant> GetTenant([FromQuery] QueryTenantInput input);
Task<dynamic> QueryTenantPageList([FromQuery] TenantInput input);
Task UpdateTenant(UpdateTenantInput input);
}
}

View File

@@ -0,0 +1,100 @@
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using Mapster;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
/// <summary>
/// 租户服务
/// </summary>
[ApiDescriptionSettings(Name = "Tenant", Order = 100)]
public class SysTenantService : ISysTenantService, IDynamicApiController, ITransient
{
private readonly IRepository<SysTenant, MultiTenantDbContextLocator> _sysTenantRep; // 租户表仓储
public SysTenantService(IRepository<SysTenant, MultiTenantDbContextLocator> sysTenantRep)
{
_sysTenantRep = sysTenantRep;
}
/// <summary>
/// 分页查询租户
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysTenant/page")]
public async Task<dynamic> QueryTenantPageList([FromQuery] TenantInput input)
{
var name = !string.IsNullOrEmpty(input.Name?.Trim());
var host = !string.IsNullOrEmpty(input.Host?.Trim());
var tenants = await _sysTenantRep.DetachedEntities
.Where((name, u => EF.Functions.Like(u.Name, $"%{input.Name.Trim()}%")),
(host, u => EF.Functions.Like(u.Host, $"%{input.Host.Trim()}%")))
.Select(u => u.Adapt<TenantOutput>())
.ToPagedListAsync(input.PageNo, input.PageSize);
return XnPageResult<TenantOutput>.PageResult(tenants);
}
/// <summary>
/// 增加租户
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysTenant/add")]
public async Task AddTenant(AddTenantInput input)
{
var isExist = await _sysTenantRep.DetachedEntities.AnyAsync(u => u.Name == input.Name || u.Host == input.Host);
if (isExist)
throw Oops.Oh(ErrorCode.D1300);
var tenant = input.Adapt<SysTenant>();
await _sysTenantRep.InsertAsync(tenant);
}
/// <summary>
/// 删除租户
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysTenant/delete")]
public async Task DeleteTenant(DeleteTenantInput input)
{
var tenant = await _sysTenantRep.FirstOrDefaultAsync(u => u.Id == input.Id);
await _sysTenantRep.DeleteAsync(tenant);
}
/// <summary>
/// 更新租户
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysTenant/edit")]
public async Task UpdateTenant(UpdateTenantInput input)
{
var isExist = await _sysTenantRep.DetachedEntities.AnyAsync(u => (u.Name == input.Name || u.Host == input.Host) && u.Id != input.Id);
if (isExist)
throw Oops.Oh(ErrorCode.D1300);
var tenant = input.Adapt<SysTenant>();
await _sysTenantRep.UpdateAsync(tenant, true);
}
/// <summary>
/// 获取租户
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysTenant/detail")]
public async Task<SysTenant> GetTenant([FromQuery] QueryTenantInput input)
{
return await _sysTenantRep.DetachedEntities.FirstOrDefaultAsync(u => u.Id == input.Id);
}
}
}

View File

@@ -0,0 +1,109 @@
using Furion.DataValidation;
using System;
using System.ComponentModel.DataAnnotations;
namespace Ewide.Core.Service
{
/// <summary>
/// 任务调度参数
/// </summary>
public class JobInput : PageInputBase
{
/// <summary>
/// 任务名称
/// </summary>
/// <example>dilon</example>
public string JobName { get; set; }
/// <summary>
/// 任务分组
/// </summary>
/// <example>dilon</example>
public string JobGroup { get; set; }
/// <summary>
/// 开始时间
/// </summary>
public DateTimeOffset BeginTime { get; set; } = DateTimeOffset.Now;
/// <summary>
/// 结束时间
/// </summary>
/// <example>null</example>
public DateTimeOffset? EndTime { get; set; }
/// <summary>
/// Cron表达式
/// </summary>
/// <example></example>
public string Cron { get; set; }
/// <summary>
/// 执行次数(默认无限循环)
/// </summary>
/// <example>10</example>
public int? RunNumber { get; set; }
/// <summary>
/// 执行间隔时间单位秒如果有Cron则IntervalSecond失效
/// </summary>
/// <example>5</example>
public int? Interval { get; set; }
/// <summary>
/// 触发器类型
/// </summary>
public TriggerTypeEnum TriggerType { get; set; } = TriggerTypeEnum.Simple;
/// <summary>
/// 请求url
/// </summary>
public string RequestUrl { get; set; }
/// <summary>
/// 请求参数PostPut请求用
/// </summary>
public string RequestParameters { get; set; }
/// <summary>
/// Headers(可以包含如Authorization授权认证)
/// 格式:{"Authorization":"userpassword.."}
/// </summary>
public string Headers { get; set; }
/// <summary>
/// 请求类型
/// </summary>
/// <example>2</example>
public RequestTypeEnum RequestType { get; set; } = RequestTypeEnum.Post;
/// <summary>
/// 描述
/// </summary>
public string Remark { get; set; }
/// <summary>
/// 任务状态
/// </summary>
public string DisplayState { get; set; }
}
public class DeleteJobInput : JobInput
{
/// <summary>
/// 任务Id
/// </summary>
[Required(ErrorMessage = "任务Id不能为空"), DataValidation(ValidationTypes.Numeric)]
public string Id { get; set; }
}
public class UpdateJobInput : DeleteJobInput
{
}
public class QueryJobInput : DeleteJobInput
{
}
}

View File

@@ -0,0 +1,96 @@
using Quartz;
using System;
namespace Ewide.Core.Service
{
/// <summary>
/// 任务信息---任务详情
/// </summary>
public class JobOutput
{
/// <summary>
/// 任务名称
/// </summary>
public string JobName { get; set; }
/// <summary>
/// 任务组名
/// </summary>
public string JobGroup { get; set; }
/// <summary>
/// 下次执行时间
/// </summary>
public DateTime? NextFireTime { get; set; }
/// <summary>
/// 上次执行时间
/// </summary>
public DateTime? PreviousFireTime { get; set; }
/// <summary>
/// 开始时间
/// </summary>
public DateTime BeginTime { get; set; }
/// <summary>
/// 结束时间
/// </summary>
public DateTime? EndTime { get; set; }
/// <summary>
/// 上次执行的异常信息
/// </summary>
public string LastErrMsg { get; set; }
/// <summary>
/// 任务状态
/// </summary>
public TriggerState TriggerState { get; set; }
/// <summary>
/// 描述
/// </summary>
public string Remark { get; set; }
/// <summary>
/// 显示状态
/// </summary>
public string DisplayState
{
get
{
return TriggerState switch
{
TriggerState.Normal => "正常",
TriggerState.Paused => "暂停",
TriggerState.Complete => "完成",
TriggerState.Error => "异常",
TriggerState.Blocked => "阻塞",
TriggerState.None => "不存在",
_ => "未知",
};
}
}
/// <summary>
/// 时间间隔
/// </summary>
public string Interval { get; set; }
/// <summary>
/// 请求地址
/// </summary>
public string RequestUrl { get; set; }
/// <summary>
/// 请求类型
/// </summary>
public string RequestType { get; set; }
/// <summary>
/// 已经执行的次数
/// </summary>
public string RunNumber { get; set; }
}
}

View File

@@ -0,0 +1,68 @@
using Furion.JsonSerialization;
using Furion.RemoteRequest.Extensions;
using Quartz;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
[DisallowConcurrentExecution]
public class HttpJob : IJob
{
//protected readonly int _maxLogCount = 20; //最多保存日志数量
//protected Stopwatch _stopwatch = new();
public async Task Execute(IJobExecutionContext context)
{
// 获取相关参数
var requestUrl = context.JobDetail.JobDataMap.GetString(SchedulerDef.REQUESTURL)?.Trim();
requestUrl = requestUrl?.IndexOf("http") == 0 ? requestUrl : "http://" + requestUrl;
var requestParameters = context.JobDetail.JobDataMap.GetString(SchedulerDef.REQUESTPARAMETERS);
var headersString = context.JobDetail.JobDataMap.GetString(SchedulerDef.HEADERS);
var headers = !string.IsNullOrWhiteSpace(headersString) ? JSON.GetJsonSerializer().Deserialize<Dictionary<string, string>>(headersString.Trim()) : null;
var requestType = (RequestTypeEnum)int.Parse(context.JobDetail.JobDataMap.GetString(SchedulerDef.REQUESTTYPE));
// var response = new HttpResponseMessage();
switch (requestType)
{
case RequestTypeEnum.Get:
await requestUrl.SetHeaders(headers).GetAsync();
break;
case RequestTypeEnum.Post:
await requestUrl.SetHeaders(headers).SetQueries(requestParameters).PostAsync();
break;
case RequestTypeEnum.Put:
await requestUrl.SetHeaders(headers).SetQueries(requestParameters).PutAsync();
break;
case RequestTypeEnum.Delete:
await requestUrl.SetHeaders(headers).DeleteAsync();
break;
}
//_stopwatch.Restart(); // 开始监视代码运行时间
//// var beginTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
//Debug.WriteLine(DateTimeOffset.Now.ToString());
//// var endTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
//_stopwatch.Stop(); // 停止监视
////// 执行次数
////var runNumber = context.JobDetail.JobDataMap.GetString(SchedulerDef.RUNNUMBER);
////context.JobDetail.JobDataMap[SchedulerDef.RUNNUMBER] = runNumber;
//// 耗时
//var seconds = _stopwatch.Elapsed.TotalSeconds; // 总秒数
//var executeTime = seconds >= 1 ? seconds + "秒" : _stopwatch.Elapsed.TotalMilliseconds + "毫秒";
////// 只保留20条记录
////var logs = context.JobDetail.JobDataMap[SchedulerDef.LOGLIST] as List<string> ?? new List<string>();
////if (logs.Count >= _maxLogCount)
//// logs.RemoveRange(0, logs.Count - _maxLogCount);
////logs.Add($"<p class='msgList'><span class='time'>{beginTime} 至 {endTime} 【耗时】{executeTime}</span></p>");
////context.JobDetail.JobDataMap[SchedulerDef.LOGLIST] = logs;
}
}
}

View File

@@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
public interface ISysTimerService
{
Task AddJob(JobInput input);
Task DeleteJob(DeleteJobInput input);
Task<dynamic> GetJobPageList([FromQuery] JobInput input);
Task<dynamic> GetTimer([FromQuery] QueryJobInput input);
Task StopScheduleJobAsync(JobInput input);
Task TriggerJobAsync(JobInput input);
Task UpdateJob(UpdateJobInput input);
}
}

View File

@@ -0,0 +1,336 @@
using Furion.DatabaseAccessor;
using Furion.DependencyInjection;
using Furion.FriendlyException;
using Mapster;
using Microsoft.AspNetCore.Mvc;
using Quartz;
using Quartz.Impl;
using Quartz.Impl.Matchers;
using Quartz.Impl.Triggers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
/// <summary>
/// 任务调度中心
/// </summary>
public class SchedulerCenter : ISingleton
{
private IScheduler _scheduler = null;
public SchedulerCenter()
{
_ = StartScheduleAsync();
InitAllJob().GetAwaiter();
}
/// <summary>
/// 开启调度器
/// </summary>
/// <returns></returns>
private async Task<bool> StartScheduleAsync()
{
if (_scheduler == null)
{
// 初始化Scheduler
var schedulerFactory = new StdSchedulerFactory();
_scheduler = await schedulerFactory.GetScheduler();
// 开启调度器
if (_scheduler.InStandbyMode)
await _scheduler.Start();
}
return _scheduler.InStandbyMode;
}
/// <summary>
/// 停止调度器
/// </summary>
private async Task<bool> StopScheduleAsync()
{
//判断调度是否已经关闭
if (!_scheduler.InStandbyMode)
{
//等待任务运行完成
await _scheduler.Standby(); // 注意Shutdown后Start会报错所以这里使用暂停。
}
return !_scheduler.InStandbyMode;
}
/// <summary>
/// 添加一个工作任务
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<dynamic> AddScheduleJobAsync(JobInput input)
{
// 检查任务是否已存在
var jobKey = new JobKey(input.JobName, input.JobGroup);
if (await _scheduler.CheckExists(jobKey))
throw Oops.Oh("任务已存在");
// http请求配置
var httpDir = new Dictionary<string, string>()
{
{ SchedulerDef.ENDAT, input.EndTime.ToString()},
{ SchedulerDef.REQUESTURL, input.RequestUrl},
{ SchedulerDef.HEADERS, input.Headers },
{ SchedulerDef.REQUESTPARAMETERS, input.RequestParameters},
{ SchedulerDef.REQUESTTYPE, ((int)input.RequestType).ToString()},
{ SchedulerDef.RUNNUMBER, input.RunNumber.ToString()}
};
// 定义这个工作并将其绑定到我们的IJob实现类
IJobDetail job = JobBuilder.Create<HttpJob>()
.SetJobData(new JobDataMap(httpDir))
.WithDescription(input.Remark)
.WithIdentity(input.JobName, input.JobGroup)
.Build();
// 创建触发器
ITrigger trigger = input.TriggerType == TriggerTypeEnum.Cron // && CronExpression.IsValidExpression(entity.Cron)
? CreateCronTrigger(input)
: CreateSimpleTrigger(input);
return await _scheduler.ScheduleJob(job, trigger);
}
/// <summary>
/// 暂停任务
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task StopScheduleJobAsync(JobInput input)
{
var jobKey = new JobKey(input.JobName, input.JobGroup);
await _scheduler.PauseJob(jobKey);
}
/// <summary>
/// 删除任务
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task DeleteScheduleJobAsync(DeleteJobInput input)
{
var jobKey = new JobKey(input.JobName, input.JobGroup);
await _scheduler.PauseJob(jobKey);
await _scheduler.DeleteJob(jobKey);
}
/// <summary>
/// 恢复运行暂停的任务
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysTimer/resumeJob")]
public async Task<dynamic> ResumeJobAsync(JobInput input)
{
//检查任务是否存在
var jobKey = new JobKey(input.JobName, input.JobGroup);
if (await _scheduler.CheckExists(jobKey))
{
var jobDetail = await _scheduler.GetJobDetail(jobKey);
var endTime = jobDetail.JobDataMap.GetString(SchedulerDef.ENDAT);
if (!string.IsNullOrWhiteSpace(endTime) && DateTime.Parse(endTime) <= DateTime.Now)
{
throw Oops.Oh("Job的结束时间已过期");
}
else
{
await _scheduler.ResumeJob(jobKey); // 任务已经存在则暂停任务
}
}
return Task.CompletedTask;
}
/// <summary>
/// 查询任务
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<JobInput> QueryJobAsync(JobInput input)
{
var jobKey = new JobKey(input.JobName, input.JobGroup);
var jobDetail = await _scheduler.GetJobDetail(jobKey);
var triggersList = await _scheduler.GetTriggersOfJob(jobKey);
var triggers = triggersList.AsEnumerable().FirstOrDefault();
var intervalSeconds = (triggers as SimpleTriggerImpl)?.RepeatInterval.TotalSeconds;
var endTime = jobDetail.JobDataMap.GetString(SchedulerDef.ENDAT);
return new JobInput
{
BeginTime = triggers.StartTimeUtc.LocalDateTime,
EndTime = !string.IsNullOrWhiteSpace(endTime) ? DateTime.Parse(endTime) : null,
Interval = intervalSeconds.HasValue ? Convert.ToInt32(intervalSeconds.Value) : null,
JobGroup = input.JobGroup,
JobName = input.JobName,
Cron = (triggers as CronTriggerImpl)?.CronExpressionString,
RunNumber = (triggers as SimpleTriggerImpl)?.RepeatCount,
TriggerType = triggers is SimpleTriggerImpl ? TriggerTypeEnum.Simple : TriggerTypeEnum.Cron,
Remark = jobDetail.Description,
RequestUrl = jobDetail.JobDataMap.GetString(SchedulerDef.REQUESTURL),
RequestType = (RequestTypeEnum)int.Parse(jobDetail.JobDataMap.GetString(SchedulerDef.REQUESTTYPE)),
RequestParameters = jobDetail.JobDataMap.GetString(SchedulerDef.REQUESTPARAMETERS),
Headers = jobDetail.JobDataMap.GetString(SchedulerDef.HEADERS)
};
}
/// <summary>
/// 立即执行
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task TriggerJobAsync(JobInput input)
{
var jobKey = new JobKey(input.JobName, input.JobGroup);
await _scheduler.ResumeJob(jobKey);
}
/// <summary>
/// 获取任务日志
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<List<string>> GetJobLogsAsync(JobInput input)
{
var jobKey = new JobKey(input.JobName, input.JobGroup);
var jobDetail = await _scheduler.GetJobDetail(jobKey);
return jobDetail.JobDataMap[SchedulerDef.LOGLIST] as List<string>;
}
/// <summary>
/// 获取任务运行次数
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<long> GetRunNumberAsync(JobInput input)
{
var jobKey = new JobKey(input.JobName, input.JobGroup);
var jobDetail = await _scheduler.GetJobDetail(jobKey);
return jobDetail.JobDataMap.GetLong(SchedulerDef.RUNNUMBER);
}
/// <summary>
/// 获取所有任务详情
/// </summary>
/// <returns></returns>
public async Task<List<JobOutput>> GetJobList()
{
var jobInfoList = new List<JobOutput>();
var groupNames = await _scheduler.GetJobGroupNames();
var jboKeyList = new List<JobKey>();
foreach (var groupName in groupNames.OrderBy(t => t))
{
jboKeyList.AddRange(await _scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName)));
//jobInfoList.Add(new JobOutput() { JobGroup = groupName });
}
foreach (var jobKey in jboKeyList.OrderBy(t => t.Name))
{
var jobDetail = await _scheduler.GetJobDetail(jobKey);
var triggersList = await _scheduler.GetTriggersOfJob(jobKey);
var triggers = triggersList.AsEnumerable().FirstOrDefault();
var interval = triggers is SimpleTriggerImpl
? ((triggers as SimpleTriggerImpl)?.RepeatInterval.ToString())
: ((triggers as CronTriggerImpl)?.CronExpressionString);
jobInfoList.Add(new JobOutput
{
JobName = jobKey.Name,
JobGroup = jobKey.Group,
LastErrMsg = jobDetail.JobDataMap.GetString(SchedulerDef.EXCEPTION),
RequestUrl = jobDetail.JobDataMap.GetString(SchedulerDef.REQUESTURL),
TriggerState = await _scheduler.GetTriggerState(triggers.Key),
PreviousFireTime = triggers.GetPreviousFireTimeUtc()?.LocalDateTime,
NextFireTime = triggers.GetNextFireTimeUtc()?.LocalDateTime,
BeginTime = triggers.StartTimeUtc.LocalDateTime,
Interval = interval,
EndTime = triggers.EndTimeUtc?.LocalDateTime,
Remark = jobDetail.Description,
RequestType = jobDetail.JobDataMap.GetString(SchedulerDef.REQUESTTYPE),
RunNumber = jobDetail.JobDataMap.GetString(SchedulerDef.RUNNUMBER)
});
}
return jobInfoList;
}
/// <summary>
/// 从数据库里面获取所有任务并初始化
/// </summary>
private async Task InitAllJob()
{
var jobList = Db.GetRepository<SysTimer>().DetachedEntities.Select(u => u.Adapt<JobInput>()).ToList();
var jobTasks = new List<Task<dynamic>>();
jobList.ForEach(u =>
{
jobTasks.Add(AddScheduleJobAsync(u));
});
await Task.WhenAll(jobTasks);
}
/// <summary>
/// 创建类型Simple的触发器
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
private static ITrigger CreateSimpleTrigger(JobInput input)
{
//作业触发器
if (input.RunNumber.HasValue && input.RunNumber > 0)
{
return TriggerBuilder.Create()
.WithIdentity(input.JobName, input.JobGroup)
.StartAt(input.BeginTime)//开始时间
//.EndAt(entity.EndTime)//结束数据
.WithSimpleSchedule(x =>
{
x.WithIntervalInSeconds(input.Interval.Value)//执行时间间隔,单位秒
.WithRepeatCount(input.RunNumber.Value)//执行次数、默认从0开始
.WithMisfireHandlingInstructionFireNow();
})
.ForJob(input.JobName, input.JobGroup)//作业名称
.Build();
}
else
{
return TriggerBuilder.Create()
.WithIdentity(input.JobName, input.JobGroup)
.StartAt(input.BeginTime)//开始时间
//.EndAt(entity.EndTime)//结束数据
.WithSimpleSchedule(x =>
{
x.WithIntervalInSeconds(input.Interval.Value)//执行时间间隔,单位秒
.RepeatForever()//无限循环
.WithMisfireHandlingInstructionFireNow();
})
.ForJob(input.JobName, input.JobGroup)//作业名称
.Build();
}
}
/// <summary>
/// 创建类型Cron的触发器
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
private static ITrigger CreateCronTrigger(JobInput input)
{
if (!CronExpression.IsValidExpression(input.Cron))
throw Oops.Oh("Cron表达式错误");
// 作业触发器
return TriggerBuilder.Create()
.WithIdentity(input.JobName, input.JobGroup)
.StartAt(input.BeginTime) //开始时间
//.EndAt(entity.EndTime) //结束时间
.WithCronSchedule(input.Cron, cronScheduleBuilder => cronScheduleBuilder.WithMisfireHandlingInstructionFireAndProceed())//指定cron表达式
.ForJob(input.JobName, input.JobGroup)//作业名称
.Build();
}
}
}

View File

@@ -0,0 +1,63 @@
namespace Ewide.Core.Service
{
/// <summary>
/// 任务调度相关常量
/// </summary>
public class SchedulerDef
{
/// <summary>
/// 请求url RequestUrl
/// </summary>
public const string REQUESTURL = "RequestUrl";
/// <summary>
/// 请求参数 RequestParameters
/// </summary>
public const string REQUESTPARAMETERS = "RequestParameters";
/// <summary>
/// Headers可以包含Authorization授权认证
/// </summary>
public const string HEADERS = "Headers";
/// <summary>
/// 请求类型 RequestType
/// </summary>
public const string REQUESTTYPE = "RequestType";
/// <summary>
/// 日志 LogList
/// </summary>
public const string LOGLIST = "LogList";
/// <summary>
/// 异常 Exception
/// </summary>
public const string EXCEPTION = "Exception";
/// <summary>
/// 执行次数
/// </summary>
public const string RUNNUMBER = "RunNumber";
/// <summary>
/// 任务结束时间
/// </summary>
public const string ENDAT = "EndAt";
}
/// <summary>
/// http请求类型
/// </summary>
public enum RequestTypeEnum
{
None = 0,
Get = 1,
Post = 2,
Put = 3,
Delete = 4
}
/// <summary>
/// 触发器类型
/// </summary>
public enum TriggerTypeEnum
{
None = 0,
Simple = 1,
Cron = 2
}
}

View File

@@ -0,0 +1,145 @@
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using Mapster;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Threading.Tasks;
namespace Ewide.Core.Service
{
/// <summary>
/// 任务调度服务
/// </summary>
[ApiDescriptionSettings(Name = "Timer", Order = 100)]
public class SysTimerService : ISysTimerService, IDynamicApiController, IScoped
{
private readonly IRepository<SysTimer> _sysTimerRep; // 任务表仓储
private readonly SchedulerCenter _schedulerCenter;
public SysTimerService(IRepository<SysTimer> sysTimerRep, SchedulerCenter schedulerCenter)
{
_sysTimerRep = sysTimerRep;
_schedulerCenter = schedulerCenter;
}
/// <summary>
/// 分页获取任务列表
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysTimers/page")]
public async Task<dynamic> GetJobPageList([FromQuery] JobInput input)
{
var jobList = await _schedulerCenter.GetJobList();
var jobName = !string.IsNullOrEmpty(input.JobName?.Trim());
var timers = await _sysTimerRep.DetachedEntities
.Where((jobName, u => EF.Functions.Like(u.JobName, $"%{input.JobName.Trim()}%")))
.Select(u => u.Adapt<JobInput>())
.ToPagedListAsync(input.PageNo, input.PageSize);
timers.Items.ToList().ForEach(u =>
{
u.DisplayState = jobList.Find(m => m.JobName == u.JobName)?.DisplayState;
});
return XnPageResult<JobInput>.PageResult(timers);
}
/// <summary>
/// 增加任务
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysTimers/add")]
public async Task AddJob(JobInput input)
{
var isExist = await _sysTimerRep.AnyAsync(u => u.JobName == input.JobName, false);
if (isExist)
throw Oops.Oh(ErrorCode.D1100);
var timer = input.Adapt<SysTimer>();
await _sysTimerRep.InsertAsync(timer);
// 添加到调度
await _schedulerCenter.AddScheduleJobAsync(input);
}
/// <summary>
/// 删除任务
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysTimers/delete")]
public async Task DeleteJob(DeleteJobInput input)
{
var timer = await _sysTimerRep.FirstOrDefaultAsync(u => u.Id == input.Id);
if (timer == null)
throw Oops.Oh(ErrorCode.D1101);
await timer.DeleteAsync();
// 从调度器里删除
await _schedulerCenter.DeleteScheduleJobAsync(input);
}
/// <summary>
/// 修改任务
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysTimers/edit")]
public async Task UpdateJob(UpdateJobInput input)
{
// 排除自己并且判断与其他是否相同
var isExist = await _sysTimerRep.AnyAsync(u => u.JobName == input.JobName && u.Id != input.Id, false);
if (isExist) throw Oops.Oh(ErrorCode.D1100);
var timer = input.Adapt<SysTimer>();
await timer.UpdateAsync(ignoreNullValues: true);
// 先从调度器里删除
var oldTimer = await _sysTimerRep.FirstOrDefaultAsync(u => u.Id == input.Id, false);
await _schedulerCenter.DeleteScheduleJobAsync(oldTimer.Adapt<DeleteJobInput>());
// 再加到调度里
await _schedulerCenter.AddScheduleJobAsync(timer.Adapt<JobInput>());
}
/// <summary>
/// 查看任务
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpGet("/sysTimers/detail")]
public async Task<dynamic> GetTimer([FromQuery] QueryJobInput input)
{
return await _sysTimerRep.DetachedEntities.FirstOrDefaultAsync(u => u.Id == input.Id);
}
/// <summary>
/// 停止任务
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysTimers/stop")]
public async Task StopScheduleJobAsync(JobInput input)
{
await _schedulerCenter.StopScheduleJobAsync(input);
}
/// <summary>
/// 启动任务
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("/sysTimers/start")]
public async Task TriggerJobAsync(JobInput input)
{
await _schedulerCenter.TriggerJobAsync(input);
}
}
}

View File

@@ -0,0 +1,27 @@
namespace Ewide.Core.Service
{
/// <summary>
/// AuthToken参数
/// </summary>
public class AuthToken
{
public string AccessToken { get; set; }
public int ExpireIn { get; set; }
public string RefreshToken { get; set; }
public string Uid { get; set; }
public string OpenId { get; set; }
public string AccessCode { get; set; }
public string UnionId { get; set; }
public string Scope { get; set; }
public string TokenType { get; set; }
public string IdToken { get; set; }
public string MacAlgorithm { get; set; }
public string MacKey { get; set; }
public string Code { get; set; }
public string OauthToken { get; set; }
public string OauthTokenSecret { get; set; }
public string UserId { get; set; }
public string ScreenName { get; set; }
public bool OauthCallbackConfirmed { get; set; }
}
}

View File

@@ -0,0 +1,22 @@
namespace Ewide.Core.Service
{
/// <summary>
/// OAuth用户参数
/// </summary>
public class AuthUserInput
{
public string Uuid { get; set; }
public string Username { get; set; }
public string Nickname { get; set; }
public string Avatar { get; set; }
public string Blog { get; set; }
public string Company { get; set; }
public string Location { get; set; }
public string Email { get; set; }
public string Eemark { get; set; }
public Gender Gender { get; set; }
public string Source { get; set; }
public AuthToken Token { get; set; }
public string RawUserInfo { get; set; }
}
}

Some files were not shown because too many files have changed in this diff Show More