一公共模块功能
1.1邮件发送
这里有两个地方可以介绍一下,都是通过过滤器特性实现的。 一是发送邮件的权限[ActionPermissionFilter(Permission = "tool:email:send")]
二是记录发送日志 [Log(Title = "发送邮件", IsSaveRequestData = false)]//日志记录
/// <summary>
/// 发送邮件
/// </summary>
/// <param name="sendEmailVo">请求参数接收实体</param>
/// <returns></returns>
[ActionPermissionFilter(Permission = "tool:email:send")]
[Log(Title = "发送邮件", IsSaveRequestData = false)]//日志记录
[HttpPost]
public IActionResult SendEmail([FromBody] SendEmailDto sendEmailVo)
{
if (sendEmailVo == null || string.IsNullOrEmpty(sendEmailVo.Subject) || string.IsNullOrEmpty(sendEmailVo.ToUser))
{
return ToResponse(ApiResult.Error($"请求参数不完整"));
}
if (string.IsNullOrEmpty(OptionsSetting.MailOptions.From) || string.IsNullOrEmpty(OptionsSetting.MailOptions.Password))
{
return ToResponse(ApiResult.Error($"请配置邮箱信息"));
}
MailHelper mailHelper = new();
string[] toUsers = sendEmailVo.ToUser.Split(",", StringSplitOptions.RemoveEmptyEntries);
if (sendEmailVo.SendMe)
{
toUsers.Append(mailHelper.FromEmail);
}
mailHelper.SendMail(toUsers, sendEmailVo.Subject, sendEmailVo.Content, sendEmailVo.FileUrl, sendEmailVo.HtmlContent);
logger.Info($"发送邮件{JsonConvert.SerializeObject(sendEmailVo)}");
return SUCCESS(true);
}
1.2 上传功能
也是有两个地方需要注意。 一是权限问题 [ActionPermissionFilter(Permission = "common")]
二是授权问题 [Verify] 代码
/// <summary>
/// 存储文件
/// </summary>
/// <param name="formFile"></param>
/// <param name="fileDir">存储目录</param>
/// <param name="fileName">自定义文件名</param>
/// <param name="storeType">上传类型1、保存到本地 2、保存到阿里云</param>
/// <returns></returns>
[HttpPost()]
[Verify]
[ActionPermissionFilter(Permission = "common")]
public async Task<IActionResult> UploadFile([FromForm(Name = "file")] IFormFile formFile, string fileName = "", string fileDir = "uploads", StoreType storeType = StoreType.LOCAL)
{
if (formFile == null) throw new CustomException(ResultCode.PARAM_ERROR, "上传文件不能为空");
SysFile file = new();
string fileExt = Path.GetExtension(formFile.FileName);//文件后缀
double fileSize = Math.Round(formFile.Length / 1024.0, 2);//文件大小KB
string[] NotAllowedFileExtensions = new string[] { ".bat", ".exe", ".jar", ".js" };
int MaxContentLength = 15;
if (NotAllowedFileExtensions.Contains(fileExt))
{
return ToResponse(ResultCode.CUSTOM_ERROR, "上传失败,未经允许上传类型");
}
switch (storeType)
{
case StoreType.LOCAL:
file = await SysFileService.SaveFileToLocal(WebHostEnvironment.WebRootPath, fileName, fileDir, HttpContext.GetName(), formFile);
break;
case StoreType.ALIYUN:
if ((fileSize / 1024) > MaxContentLength)
{
return ToResponse(ResultCode.CUSTOM_ERROR, "上传文件过大,不能超过 " + MaxContentLength + " MB");
}
file = new(formFile.FileName, fileName, fileExt, fileSize + "kb", fileDir, HttpContext.GetName())
{
StoreType = (int)StoreType.ALIYUN,
FileType = formFile.ContentType
};
file = await SysFileService.SaveFileToAliyun(file, formFile);
if (file.Id <= 0) { return ToResponse(ApiResult.Error("阿里云连接失败")); }
break;
case StoreType.TENCENT:
break;
case StoreType.QINIU:
break;
default:
break;
}
return SUCCESS(new
{
url = file.AccessUrl,
fileName = file.FileName,
fileId = file.Id.ToString()
});
}
1.3 获取服务信息
代码
/// <summary>
/// 获取服务器信息
/// </summary>
/// <returns></returns>
[HttpGet("monitor/server")]
//[AllowAnonymous]
public IActionResult Server()
{
//核心数
int cpuNum = Environment.ProcessorCount;
string computerName = Environment.MachineName;
string osName = RuntimeInformation.OSDescription;
string osArch = RuntimeInformation.OSArchitecture.ToString();
string version = RuntimeInformation.FrameworkDescription;
string appRAM = ((double)Process.GetCurrentProcess().WorkingSet64 / 1048576).ToString("N2") + " MB";
string startTime = Process.GetCurrentProcess().StartTime.ToString("yyyy-MM-dd HH:mm:ss");
string sysRunTime = ComputerHelper.GetRunTime();
string serverIP = Request.HttpContext.Connection.LocalIpAddress.MapToIPv4().ToString() + ":" + Request.HttpContext.Connection.LocalPort;//获取服务器IP
var programStartTime = Process.GetCurrentProcess().StartTime;
string programRunTime = DateTimeHelper.FormatTime((DateTime.Now - programStartTime).TotalMilliseconds.ToString().Split('.')[0].ParseToLong());
var data = new
{
cpu = ComputerHelper.GetComputerInfo(),
disk = ComputerHelper.GetDiskInfos(),
sys = new { cpuNum, computerName, osName, osArch, serverIP, runTime = sysRunTime },
app = new
{
name = HostEnvironment.EnvironmentName,
rootPath = HostEnvironment.ContentRootPath,
webRootPath = HostEnvironment.WebRootPath,
version,
appRAM,
startTime,
runTime = programRunTime,
host = serverIP
},
};
return SUCCESS(data);
}
电脑帮助类
public class ComputerHelper
{
/// <summary>
/// 内存使用情况
/// </summary>
/// <returns></returns>
public static MemoryMetrics GetComputerInfo()
{
try
{
MemoryMetricsClient client = new();
MemoryMetrics memoryMetrics = IsUnix() ? client.GetUnixMetrics() : client.GetWindowsMetrics();
memoryMetrics.FreeRam = Math.Round(memoryMetrics.Free / 1024, 2) + "GB";
memoryMetrics.UsedRam = Math.Round(memoryMetrics.Used / 1024, 2) + "GB";
memoryMetrics.TotalRAM = Math.Round(memoryMetrics.Total / 1024, 2) + "GB";
memoryMetrics.RAMRate = Math.Ceiling(100 * memoryMetrics.Used / memoryMetrics.Total).ToString() + "%";
memoryMetrics.CPURate = Math.Ceiling(GetCPURate().ParseToDouble()) + "%";
return memoryMetrics;
}
catch (Exception ex)
{
Console.WriteLine("获取内存使用出错,msg=" + ex.Message + "," + ex.StackTrace);
}
return new MemoryMetrics();
}
/// <summary>
/// 获取内存大小
/// </summary>
/// <returns></returns>
public static List<DiskInfo> GetDiskInfos()
{
List<DiskInfo> diskInfos = new();
if (IsUnix())
{
try
{
string output = ShellHelper.Bash("df -m / | awk '{print $2,$3,$4,$5,$6}'");
var arr = output.Split('\n', StringSplitOptions.RemoveEmptyEntries);
if (arr.Length == 0) return diskInfos;
var rootDisk = arr[1].Split(' ', (char)StringSplitOptions.RemoveEmptyEntries);
if (rootDisk == null || rootDisk.Length == 0)
{
return diskInfos;
}
DiskInfo diskInfo = new()
{
DiskName = "/",
TotalSize = long.Parse(rootDisk[0]) / 1024,
Used = long.Parse(rootDisk[1]) / 1024,
AvailableFreeSpace = long.Parse(rootDisk[2]) / 1024,
AvailablePercent = decimal.Parse(rootDisk[3].Replace("%", ""))
};
diskInfos.Add(diskInfo);
}
catch (Exception ex)
{
Console.WriteLine("获取磁盘信息出错了" + ex.Message);
}
}
else
{
var driv = DriveInfo.GetDrives();
foreach (var item in driv)
{
var obj = new DiskInfo()
{
DiskName = item.Name,
TypeName = item.DriveType.ToString(),
TotalSize = item.TotalSize / 1024 / 1024 / 1024,
AvailableFreeSpace = item.AvailableFreeSpace / 1024 / 1024 / 1024,
};
obj.Used = obj.TotalSize - obj.AvailableFreeSpace;
obj.AvailablePercent = decimal.Ceiling(obj.Used / (decimal)obj.TotalSize * 100);
diskInfos.Add(obj);
}
}
return diskInfos;
}
public static bool IsUnix()
{
var isUnix = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
return isUnix;
}
public static string GetCPURate()
{
string cpuRate;
if (IsUnix())
{
string output = ShellHelper.Bash("top -b -n1 | grep \"Cpu(s)\" | awk '{print $2 + $4}'");
cpuRate = output.Trim();
}
else
{
string output = ShellHelper.Cmd("wmic", "cpu get LoadPercentage");
cpuRate = output.Replace("LoadPercentage", string.Empty).Trim();
}
return cpuRate;
}
/// <summary>
/// 获取系统运行时间
/// </summary>
/// <returns></returns>
public static string GetRunTime()
{
string runTime = string.Empty;
try
{
if (IsUnix())
{
string output = ShellHelper.Bash("uptime -s").Trim();
runTime = DateTimeHelper.FormatTime((DateTime.Now - output.ParseToDateTime()).TotalMilliseconds.ToString().Split('.')[0].ParseToLong());
}
else
{
string output = ShellHelper.Cmd("wmic", "OS get LastBootUpTime/Value");
string[] outputArr = output.Split('=', (char)StringSplitOptions.RemoveEmptyEntries);
if (outputArr.Length == 2)
{
runTime = DateTimeHelper.FormatTime((DateTime.Now - outputArr[1].Split('.')[0].ParseToDateTime()).TotalMilliseconds.ToString().Split('.')[0].ParseToLong());
}
}
}
catch (Exception ex)
{
Console.WriteLine("获取runTime出错" + ex.Message);
}
return runTime;
}
}
/// <summary>
/// 内存信息
/// </summary>
public class MemoryMetrics
{
[JsonIgnore]
public double Total { get; set; }
[JsonIgnore]
public double Used { get; set; }
[JsonIgnore]
public double Free { get; set; }
public string UsedRam { get; set; }
/// <summary>
/// CPU使用率%
/// </summary>
public string CPURate { get; set; }
/// <summary>
/// 总内存 GB
/// </summary>
public string TotalRAM { get; set; }
/// <summary>
/// 内存使用率 %
/// </summary>
public string RAMRate { get; set; }
/// <summary>
/// 空闲内存
/// </summary>
public string FreeRam { get; set; }
}
public class DiskInfo
{
/// <summary>
/// 磁盘名
/// </summary>
public string DiskName { get; set; }
public string TypeName { get; set; }
public long TotalFree { get; set; }
public long TotalSize { get; set; }
/// <summary>
/// 已使用
/// </summary>
public long Used { get; set; }
/// <summary>
/// 可使用
/// </summary>
public long AvailableFreeSpace { get; set; }
public decimal AvailablePercent { get; set; }
}
public class MemoryMetricsClient
{
#region 获取内存信息
/// <summary>
/// windows系统获取内存信息
/// </summary>
/// <returns></returns>
public MemoryMetrics GetWindowsMetrics()
{
string output = ShellHelper.Cmd("wmic", "OS get FreePhysicalMemory,TotalVisibleMemorySize /Value");
var metrics = new MemoryMetrics();
var lines = output.Trim().Split('\n', (char)StringSplitOptions.RemoveEmptyEntries);
if (lines.Length <= 0) return metrics;
var freeMemoryParts = lines[0].Split('=', (char)StringSplitOptions.RemoveEmptyEntries);
var totalMemoryParts = lines[1].Split('=', (char)StringSplitOptions.RemoveEmptyEntries);
metrics.Total = Math.Round(double.Parse(totalMemoryParts[1]) / 1024, 0);
metrics.Free = Math.Round(double.Parse(freeMemoryParts[1]) / 1024, 0);//m
metrics.Used = metrics.Total - metrics.Free;
return metrics;
}
/// <summary>
/// Unix系统获取
/// </summary>
/// <returns></returns>
public MemoryMetrics GetUnixMetrics()
{
string output = ShellHelper.Bash("free -m | awk '{print $2,$3,$4,$5,$6}'");
var metrics = new MemoryMetrics();
var lines = output.Split('\n', (char)StringSplitOptions.RemoveEmptyEntries);
if (lines.Length <= 0) return metrics;
if (lines != null && lines.Length > 0)
{
var memory = lines[1].Split(' ', (char)StringSplitOptions.RemoveEmptyEntries);
if (memory.Length >= 3)
{
metrics.Total = double.Parse(memory[0]);
metrics.Used = double.Parse(memory[1]);
metrics.Free = double.Parse(memory[2]);//m
}
}
return metrics;
}
#endregion
}
系统命令帮助类
public class ShellHelper
{
/// <summary>
/// linux 系统命令
/// </summary>
/// <param name="command"></param>
/// <returns></returns>
public static string Bash(string command)
{
var escapedArgs = command.Replace("\"", "\\\"");
var process = new Process()
{
StartInfo = new ProcessStartInfo
{
FileName = "/bin/bash",
Arguments = $"-c \"{escapedArgs}\"",
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true,
}
};
process.Start();
string result = process.StandardOutput.ReadToEnd();
process.WaitForExit();
process.Dispose();
return result;
}
/// <summary>
/// windows系统命令
/// </summary>
/// <param name="fileName"></param>
/// <param name="args"></param>
/// <returns></returns>
public static string Cmd(string fileName, string args)
{
string output = string.Empty;
var info = new ProcessStartInfo();
info.FileName = fileName;
info.Arguments = args;
info.RedirectStandardOutput = true;
using (var process = Process.Start(info))
{
output = process.StandardOutput.ReadToEnd();
}
return output;
}
}
1.4 sqlsugar 框架条件搜索
接口请求action别名处理[HttpGet("list")] ,本来是QuerySysConfig,自定义就可以使用list访问。
/// <summary>
/// 查询参数配置列表
/// </summary>
/// <returns></returns>
[HttpGet("list")]
[ActionPermissionFilter(Permission = "system:config:list")]
public IActionResult QuerySysConfig([FromQuery] SysConfigQueryDto parm)
{
//开始拼装查询条件
var predicate = Expressionable.Create<SysConfig>();
//TODO 搜索条件
predicate = predicate.AndIF(!parm.ConfigType.IsEmpty(),m => m.ConfigType == parm.ConfigType);
predicate = predicate.AndIF(!parm.ConfigName.IsEmpty(),m => m.ConfigName.Contains(parm.ConfigType));
predicate = predicate.AndIF(!parm.ConfigKey.IsEmpty(),m => m.ConfigKey.Contains(parm.ConfigKey));
predicate = predicate.AndIF(!parm.BeginTime.IsEmpty(),m => m.Create_time >= parm.BeginTime );
predicate = predicate.AndIF(!parm.BeginTime.IsEmpty(),m => m.Create_time <= parm.EndTime);
var response = _SysConfigService.GetPages(predicate.ToExpression(), parm);
return SUCCESS(response);
}
1.5 登陆功能
/// <summary>
/// 登录
/// </summary>
/// <param name="loginBody">登录对象</param>
/// <returns></returns>
[Route("login")]
[HttpPost]
//[Log(Title = "登录")]
public IActionResult Login([FromBody] LoginBodyDto loginBody)
{
if (loginBody == null) { throw new CustomException("请求参数错误"); }
loginBody.LoginIP = HttpContextExtension.GetClientUserIp(HttpContext);
SysConfig sysConfig = sysConfigService.GetSysConfigByKey("sys.account.captchaOnOff");
if (sysConfig?.ConfigValue != "off" && CacheHelper.Get(loginBody.Uuid) is string str && !str.ToLower().Equals(loginBody.Code.ToLower()))
{
return ToResponse(ResultCode.CAPTCHA_ERROR, "验证码错误");
}
var user = sysLoginService.Login(loginBody, RecordLogInfo(httpContextAccessor.HttpContext));
List<SysRole> roles = roleService.SelectUserRoleListByUserId(user.UserId);
//权限集合 eg *:*:*,system:user:list
List<string> permissions = permissionService.GetMenuPermission(user);
LoginUser loginUser = new(user, roles, permissions);//权限还传给前端,用于控制操作按钮显示
CacheService.SetUserPerms(GlobalConstant.UserPermKEY + user.UserId, permissions);//权限缓存起来
return SUCCESS(JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(loginUser), jwtSettings.JwtSettings));
}
1.6 注册和信息获取功能
/// <summary>
/// 注销
/// </summary>
/// <returns></returns>
[Log(Title = "注销")]
[HttpPost("logout")]
public IActionResult LogOut()
{
//Task.Run(async () =>
//{
// //注销登录的用户,相当于ASP.NET中的FormsAuthentication.SignOut
// await HttpContext.SignOutAsync();
//}).Wait();
var userid = HttpContext.GetUId();
var name = HttpContext.GetName();
CacheService.RemoveUserPerms(GlobalConstant.UserPermKEY + userid);
return SUCCESS(new { name, id = userid });
}
/// <summary>
/// 获取用户信息
/// </summary>
/// <returns></returns>
[Verify]
[HttpGet("getInfo")]
public IActionResult GetUserInfo()
{
long userid = HttpContext.GetUId();
var user = sysUserService.SelectUserById(userid);
//前端校验按钮权限使用
//角色集合 eg: admin,yunying,common
List<string> roles = permissionService.GetRolePermission(user);
//权限集合 eg *:*:*,system:user:list
List<string> permissions = permissionService.GetMenuPermission(user);
user.WelcomeContent = GlobalConstant.WelcomeMessages[new Random().Next(0, GlobalConstant.WelcomeMessages.Length)];
return SUCCESS(new { user, roles, permissions });
}
1.7验证码功能
这里是引用了第三方模块 【Hei.Captcha.dll】 ,减少了工作量
/// <summary>
/// 生成图片验证码
/// </summary>
/// <returns></returns>
[HttpGet("captchaImage")]
public ApiResult CaptchaImage()
{
string uuid = Guid.NewGuid().ToString().Replace("-", "");
SysConfig sysConfig = sysConfigService.GetSysConfigByKey("sys.account.captchaOnOff");
var captchaOff = sysConfig?.ConfigValue ?? "0";
var code = SecurityCodeHelper.GetRandomEnDigitalText(4);
byte[] imgByte;
if (captchaOff == "1")
{
imgByte = SecurityCodeHelper.GetGifEnDigitalCodeByte(code);//动态gif数字字母
}
else if (captchaOff == "2")
{
imgByte = SecurityCodeHelper.GetGifBubbleCodeByte(code);//动态gif泡泡
}
else if (captchaOff == "3")
{
imgByte = SecurityCodeHelper.GetBubbleCodeByte(code);//泡泡
}
else
{
imgByte = SecurityCodeHelper.GetEnDigitalCodeByte(code);//英文字母加数字
}
string base64Str = Convert.ToBase64String(imgByte);
CacheHelper.SetCache(uuid, code);
var obj = new { uuid, img = base64Str };// File(stream, "image/png")
return ToJson(1, obj);
}