815 lines
30 KiB
C#
Raw Normal View History

2025-06-06 16:00:39 +08:00
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using OfficeOpenXml;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using VOL.Core.Extensions;
using VOL.Core.Services;
2025-07-11 09:37:06 +08:00
using VOL.Entity.DomainModels;
2025-06-06 16:00:39 +08:00
using VOL.Entity.Enum;
2025-07-11 09:37:06 +08:00
using VOL.Model.Norm.Response;
2025-06-06 16:00:39 +08:00
namespace VOL.Core.Utilities
{
public static class Tool
{
public static string Upload(IFormFile file, string filePath)
{
string fullPath = filePath.MapPath(true);
//string fileName = $"{DateTime.Now.ToString("yyyyMMddHHmmssfff")}.{file.FileName.Split(".")[1]}";
//string fileName = file.FileName.Split(".")[0];
try
{
if (!Directory.Exists(fullPath)) Directory.CreateDirectory(fullPath);
if (File.Exists(file.FileName))
File.Delete(file.FileName);
using (var stream = new FileStream(fullPath + file.FileName, FileMode.Create))
{
file.CopyTo(stream);
}
}
catch (Exception ex)
{
Logger.Error($"上传文件失败Tool.Upload,路径:{filePath},失败文件:{file},{ex.Message + ex.StackTrace}");
}
return $"{filePath}{file.FileName}";
}
//public static void BatchUpload(List<IFormFile> files, string filePath)
//{
// string fullPath = filePath.MapPath(true);
// //string fileName = $"{DateTime.Now.ToString("yyyyMMddHHmmssfff")}.{file.FileName.Split(".")[1]}";
// try
// {
// if (!Directory.Exists(fullPath)) Directory.CreateDirectory(fullPath);
// foreach (var file in files)
// {
// string fileName = file.FileName.Split(".")[0];
// if (File.Exists(fileName))
// File.Delete(fileName);
// using (var stream = new FileStream(fullPath + fileName, FileMode.Create))
// {
// file.CopyTo(stream);
// }
// }
// }
// catch (Exception ex)
// {
// Logger.Error($"上传文件失败Tool.Upload,路径:{filePath},失败文件:{files},{ex.Message + ex.StackTrace}");
// }
// //return $"{filePath}{fileName}{file.FileName.Split(".")[1]}";
//}
public static byte[] UploadFile(IFormFile file)
{
if (file != null && file.Length > 0)
{
using (var memoryStream = new MemoryStream())
{
file.CopyTo(memoryStream);
var fileData = memoryStream.ToArray();
return fileData;
}
}
else
{
throw new Exception("未选择文件或文件大小为零。");
}
}
public static byte[] ExportToExcel<T>(Dictionary<string, List<T>> sheetDataList, List<string> fields)
{
using (var package = new ExcelPackage(new FileInfo("filename.xlsx")))
{
var sheetCount = sheetDataList.Count;
foreach (var sheet in sheetDataList)
{
var sheetData = sheetDataList[sheet.Key];
var sheetName = sheet.Key;
var worksheet = package.Workbook.Worksheets.Add(sheetName);
ConfigureWorksheet<T>(worksheet, sheetData, fields);
}
return package.GetAsByteArray();
}
}
/// <summary>
/// 通用导入Excel
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fileStream"></param>
/// <returns></returns>
public static List<T> ConvertExcelToList<T>(Stream fileStream) where T : new()
{
var result = new List<T>();
using (var package = new ExcelPackage(fileStream))
{
var worksheet = package.Workbook.Worksheets[1]; // 假设数据在第一个工作表中
// 确保工作表中有数据
if (worksheet.Dimension == null)
{
throw new Exception("工作表中没有任何数据。");
}
// 获取 Excel 文件的表头(列名),假设第一行是表头
var headerRow = worksheet.Cells[1, 1, 1, worksheet.Dimension.End.Column];
var headers = headerRow.Select(cell => cell.Text.Trim()).ToList();
// 获取实体类的属性信息和 Display 属性的中文名
var properties = typeof(T).GetProperties();
var displayNames = GetDisplayNames<T>();
// 为 Display 名称构建映射表(避免每次都做遍历)
var displayNameToPropertyMap = properties
.Where(p => displayNames.ContainsKey(p.Name))
.ToDictionary(p => displayNames[p.Name], p => p);
// 从第二行开始解析数据(假设第一行是表头)
for (int row = 2; row <= worksheet.Dimension.End.Row; row++)
{
var dataObject = new T();
// 遍历每一列,将 Excel 中的数据映射到实体类的属性上
for (int col = 1; col <= worksheet.Dimension.End.Column; col++)
{
var header = headers[col - 1];
if (displayNameToPropertyMap.TryGetValue(header, out var property))
{
try
{
var cellValue = worksheet.Cells[row, col].GetValue<object>();
var propertyType = property.PropertyType;
// 处理 Excel 日期类型的特殊情况
if (propertyType == typeof(DateTime?))
{
if (cellValue is double numericValue)
{
// 如果 Excel 中的值是数字(日期),将其转换为 DateTime 类型
cellValue = DateTime.FromOADate(numericValue);
}
else if (cellValue is string stringValue)
{
// 如果 Excel 单元格是字符串类型的日期(如 '2000-08-01'
if (DateTime.TryParseExact(stringValue, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime parsedDate))
{
cellValue = parsedDate;
}
else if (DateTime.TryParseExact(stringValue, "M/d/yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out parsedDate))
{
cellValue = parsedDate;
}
else
{
// 无法解析为日期格式
throw new InvalidCastException($"无法将字符串 '{stringValue}' 转换为有效的日期。");
}
}
}
// 确保转换后的类型与目标属性匹配
if (cellValue != null)
{
// 如果属性是 DateTime 类型并且值已经是 DateTime无需再调用 Convert.ChangeType
if (propertyType == typeof(DateTime?) && cellValue is DateTime?)
{
// 如果是日期类型,直接赋值
property.SetValue(dataObject, (DateTime?)cellValue);
}
else
{
// 尝试转换数据类型
cellValue = Convert.ChangeType(cellValue, propertyType);
property.SetValue(dataObject, cellValue);
}
}
}
catch (Exception ex)
{
throw new InvalidCastException(
$"行 {row} 列 {col}:转换 Excel 单元格值 '{worksheet.Cells[row, col].Text}' 到属性 '{property.Name}' 失败。",
ex);
}
}
}
result.Add(dataObject);
}
}
return result;
}
private static Dictionary<string, string> GetDisplayNames<T>()
{
var displayNames = new Dictionary<string, string>();
var properties = typeof(T).GetProperties();
foreach (var property in properties)
{
var displayNameAttribute = property.GetCustomAttribute<DisplayAttribute>();
if (displayNameAttribute != null)
{
var propertyName = property.Name;
var displayName = displayNameAttribute.Name;
displayNames[propertyName] = displayName;
}
}
return displayNames;
}
/// <summary>
/// 导出Excel
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="worksheet"></param>
/// <param name="sheetData"></param>
/// <param name="fields"></param>
private static void ConfigureWorksheet<T>(ExcelWorksheet worksheet, IEnumerable<T> sheetData, List<string> fields)
{
// 添加表头
for (int i = 0; i < fields.Count; i++)
{
var fieldName = fields[i];
var displayName = GetDisplayName<T>(fieldName);
worksheet.Cells[1, i + 1].Value = displayName;
}
// 添加数据行
var dataList = sheetData.ToList();
for (int rowIndex = 0; rowIndex < dataList.Count; rowIndex++)
{
var rowData = dataList[rowIndex];
for (int colIndex = 0; colIndex < fields.Count; colIndex++)
{
var fieldName = fields[colIndex];
var value = GetPropertyValue(rowData, fieldName);
// 设置值时处理日期时间
if (value is DateTime dateTimeValue)
{
worksheet.Cells[rowIndex + 2, colIndex + 1].Value = dateTimeValue;
worksheet.Cells[rowIndex + 2, colIndex + 1].Style.Numberformat.Format = "yyyy-mm-dd hh:mm:ss";
}
else
{
worksheet.Cells[rowIndex + 2, colIndex + 1].Value = value;
}
}
}
// 自动调整列宽
worksheet.Cells.AutoFitColumns();
}
private static string GetDisplayName<T>(string propertyName)
{
var property = typeof(T).GetProperty(propertyName);
if (property != null)
{
var displayNameAttribute = property.GetCustomAttributes(typeof(DisplayAttribute), false).FirstOrDefault() as DisplayAttribute;
if (displayNameAttribute != null)
{
return displayNameAttribute.Name;
}
}
return propertyName;
}
private static object GetPropertyValue<T>(T obj, string propertyName)
{
var property = typeof(T).GetProperty(propertyName);
return property?.GetValue(obj);
}
/// <summary>
/// 获取类中所有得列名
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static List<string> GetPropertyNames<T>()
{
var propertyNames = new List<string>();
var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var property in properties)
{
// 获取属性上的 DisplayAttribute 特性
var displayAttribute = property.GetCustomAttribute<DisplayAttribute>();
// 检查 DisplayAttribute 特性的名称是否为 "IgnoreColumnAttribute",如果是,则忽略该属性
if (displayAttribute != null && displayAttribute.Name.ToLower() == "ignorecolumnattribute")
{
continue;
}
propertyNames.Add(property.Name);
}
return propertyNames;
}
public static string NumberToChinese(this int number)
{
// 定义中文数字数组
string[] chineseNumbers = { "零", "一", "二", "三", "四", "五", "六", "七", "八", "九" };
// 如果数字在 0 到 9 之间,则直接返回中文数字
if (number >= 0 && number <= 9)
{
return chineseNumbers[number];
}
else
{
// 如果数字不在 0 到 9 之间,则抛出异常
throw new ArgumentOutOfRangeException(nameof(number), "Number must be between 0 and 9.");
}
}
2025-06-06 16:55:14 +08:00
public static string IncrementId(string maxId)
{
// 获取当前日期的前缀(年年年年+月月+日日)
string datePrefix = DateTime.Now.ToString("yyyyMMdd");
// 提取后4位编号部分
string numberPart = maxId.Substring(maxId.Length - 4);
// 将编号部分转为整数并加1
if (!int.TryParse(numberPart, out int number))
{
throw new InvalidOperationException("编号部分格式不正确,应为数字");
}
// 检查编号是否超出范围
if (number >= 9999)
{
throw new InvalidOperationException("编号已达到最大值");
}
// 返回新的编号,保持当前日期前缀和递增后的编号
return $"{datePrefix}{(number + 1):D4}";
}
2025-06-06 16:00:39 +08:00
/// <summary>
/// 获取集合中位数
/// </summary>
/// <param name="values"></param>
/// <returns></returns>
public static double Median(this List<float> values)
{
var sortedValues = values.OrderBy(x => x).ToList();
int count = sortedValues.Count;
if (count % 2 == 0)
{
return (sortedValues[count / 2 - 1] + sortedValues[count / 2]) / 2.0;
}
else
{
return sortedValues[count / 2];
}
}
/// <summary>
/// 获取集合中位数
/// </summary>
/// <param name="values"></param>
/// <returns></returns>
public static double? Median(this List<double?> values)
{
// 过滤掉 null 值
var sortedValues = values.Where(x => x.HasValue)
.Select(x => x.Value)
.OrderBy(x => x)
.ToList();
// 如果列表为空,返回 null
if (sortedValues.Count == 0)
return null;
int count = sortedValues.Count;
// 计算中位数
if (count % 2 == 0)
{
return (sortedValues[count / 2 - 1] + sortedValues[count / 2]) / 2.0;
}
else
{
return sortedValues[count / 2];
}
}
/// <summary>
/// 获取取样时间
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
public static DateTime GetDateTimeFromFixedAsh(this string input)
{
string timePart;
// 判断字符串中是否包含 '-',以确定截取的位置
int dashIndex = input.IndexOf('-');
if (dashIndex != -1)
{
// 截取 '-' 之前的部分
timePart = input.Substring(6, dashIndex - 6);
}
else
{
// 如果没有 '-'则直接截取后面的8位数字
timePart = input.Substring(6, 8);
}
// 将截取的时间字符串转换为 DateTime 类型
DateTime result;
if (DateTime.TryParseExact(timePart, "yyyyMMdd", null, System.Globalization.DateTimeStyles.None, out result))
{
return result;
}
else
{
throw new ArgumentException("时间转换失败");
}
}
/// <summary>
/// 获取枚举所有描述
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TEnum"></typeparam>
/// <param name="createItemFunc"></param>
/// <returns></returns>
public static List<T> GetEnumDescriptions<T, TEnum>(Func<int, string, T> createItemFunc) where TEnum : Enum
{
var enumDescriptions = new List<T>();
foreach (TEnum value in Enum.GetValues(typeof(TEnum)))
{
int intValue = Convert.ToInt32(value);
if (intValue >= 1)
{
var descriptionAttribute = value.GetType()
.GetField(value.ToString())
.GetCustomAttributes(typeof(DescriptionAttribute), false)
.FirstOrDefault() as DescriptionAttribute;
if (descriptionAttribute != null)
{
var item = createItemFunc(intValue, descriptionAttribute.Description);
enumDescriptions.Add(item);
}
}
}
return enumDescriptions;
}
/// <summary>
/// 获取枚举描述
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static string GetDescription(this Enum value)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value), "Enum value cannot be null");
}
// 获取字段信息
FieldInfo field = value.GetType().GetField(value.ToString());
if (field == null)
{
// 如果字段信息为空,返回默认的描述
return "";
}
// 获取 DescriptionAttribute 特性
var attributes = field.GetCustomAttributes(typeof(DescriptionAttribute), false);
var attribute = attributes.FirstOrDefault() as DescriptionAttribute;
// 如果特性为空,返回空字符串
return attribute?.Description ?? "";
}
/// <summary>
/// 测试类型枚举转换
/// </summary>
/// <param name="itemCode"></param>
/// <returns></returns>
//public static CategoryEnumType? GetCategoryEnumTypeByItemCode(string itemCode)
//{
// if (Enum.TryParse(typeof(Ai_CategoryEnumType), itemCode, true, out var aiCategoryEnum))
// {
// var aiCategoryName = Enum.GetName(typeof(Ai_CategoryEnumType), aiCategoryEnum);
// if (Enum.TryParse(typeof(CategoryEnumType), aiCategoryName, true, out var categoryEnum))
// {
// return (CategoryEnumType)categoryEnum;
// }
// }
// return null; // 如果找不到对应的映射值,返回 null 或者抛出异常
//}
public static byte[] DicExportToExcel(Dictionary<string, List<Dictionary<string, string>>> sheetDataList)
{
using (var package = new ExcelPackage())
{
foreach (var sheet in sheetDataList)
{
var sheetName = sheet.Key;
var sheetData = sheet.Value;
var worksheet = package.Workbook.Worksheets.Add(sheetName);
DicConfigureWorksheet(worksheet, sheetData);
}
return package.GetAsByteArray();
}
}
private static void DicConfigureWorksheet(ExcelWorksheet worksheet, List<Dictionary<string, string>> sheetData)
{
// 假设所有字典在List中都有相同的Key集合我们可以从第一个元素获取表头
if (sheetData.Count > 0)
{
int rowIndex = 1; // 从第一行开始
var firstRow = sheetData[0];
// 添加表头
int columnIndex = 1; // 从第一列开始
foreach (var header in firstRow)
{
worksheet.Cells[1, columnIndex++].Value = header.Key;
}
// 添加数据行
foreach (var rowDict in sheetData)
{
rowIndex++;
columnIndex = 1; // 重置列索引
foreach (var cell in rowDict)
{
worksheet.Cells[rowIndex, columnIndex].Value = cell.Value;
// 检查是否需要设置日期时间格式
if (DateTime.TryParse(cell.Value, out DateTime dateTimeValue))
{
worksheet.Cells[rowIndex, columnIndex].Style.Numberformat.Format = "yyyy-mm-dd hh:mm:ss";
}
columnIndex++;
}
}
// 自动调整列宽
worksheet.Cells[worksheet.Dimension.Address].AutoFitColumns();
}
}
public static DateTime GetQuarterStartDate(int quarter, int year)
{
return quarter switch
{
1 => new DateTime(year, 1, 1),
2 => new DateTime(year, 4, 1),
3 => new DateTime(year, 7, 1),
4 => new DateTime(year, 10, 1),
_ => throw new ArgumentOutOfRangeException()
};
}
public static DateTime GetQuarterEndDate(int quarter, int year)
{
return quarter switch
{
1 => new DateTime(year, 3, 31),
2 => new DateTime(year, 6, 30),
3 => new DateTime(year, 9, 30),
4 => new DateTime(year, 12, 31),
_ => throw new ArgumentOutOfRangeException()
};
}
public static IQueryable<T> WhereIF<T>(this IQueryable<T> query, bool condition, Expression<Func<T, bool>> predicate)
{
return condition ? query.Where(predicate) : query;
}
/// <summary>
/// DateTime转时间戳
/// </summary>
/// <param name="dateTime"></param>
/// <returns></returns>
public static long ToUnixTimeMilliseconds(this DateTime dateTime)
{
return new DateTimeOffset(dateTime).ToUnixTimeMilliseconds();
}
public static string GetEnumNameByValue(int value)
{
if (Enum.IsDefined(typeof(SportsTestItemType), value))
{
return ((SportsTestItemType)value).ToString();
}
return ""; // Return an empty string if the value is not defined in the enum
}
/// <summary>
/// 获取分数等级
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="score"></param>
/// <returns></returns>
public static string GetRank<T>(this T score) where T : struct, IComparable
{
// 将 score 转换为 double 进行比较
double value = Convert.ToDouble(score);
return value switch
{
>= 90 => "优秀",
>= 80 => "良好",
>= 60 => "及格",
_ => "不及格"
};
}
/// <summary>
/// 根据等级名称返回枚举
/// </summary>
/// <param name="rankName"></param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
public static AchievementRank GetEnumByRankName(string rankName)
{
return rankName switch
{
"优秀" => AchievementRank.Excellent,
"良好" => AchievementRank.Fine,
"及格" => AchievementRank.Pass,
"不及格" => AchievementRank.Fail,
_ => throw new ArgumentException("Invalid rank name", nameof(rankName)) // Throw exception for invalid rank name
};
}
public static string ToChineseNumber(this int number)
{
string[] chineseNumbers = { "零", "一", "二", "三", "四", "五", "六", "七", "八", "九" };
if (number <= 0)
throw new ArgumentException("Number must be greater than zero.");
if (number < 10)
return chineseNumbers[number];
if (number < 20)
return "十" + (number % 10 == 0 ? "" : chineseNumbers[number % 10]);
int tens = number / 10;
int units = number % 10;
return chineseNumbers[tens] + "十" + (units == 0 ? "" : chineseNumbers[units]);
}
2025-07-11 09:37:06 +08:00
/// <summary>
/// 心率个数
/// </summary>
/// <param name="heartRateDataList"></param>
/// <returns></returns>
public static List<SportsProportionData> GetHeartRateNumber(List<Ai_HeartRateData> heartRateDataList)
{
var result = new List<SportsProportionData>();
var maleData = heartRateDataList.Where(x => x.Sex == SexType.Male).ToList();
var femaleData = heartRateDataList.Where(x => x.Sex == SexType.Female).ToList();
var m_avg = maleData.Any() ? (int)maleData.Average(x => x.Value) : 0;
var f_avg = femaleData.Any() ? (int)femaleData.Average(x => x.Value) : 0;
var avgData = new SportsProportionData
{
Name = "平均",
Datas = new List<StudentSportsProportionData>
{
new StudentSportsProportionData { Title = "男", Value = m_avg },
new StudentSportsProportionData { Title = "女", Value = f_avg }
}
};
var m_max = maleData.Any() ? maleData.Max(x => x.Value) : 0;
var f_max = femaleData.Any() ? femaleData.Max(x => x.Value) : 0;
var maxData = new SportsProportionData
{
Name = "最高",
Datas = new List<StudentSportsProportionData>
{
new StudentSportsProportionData { Title = "男", Value = m_max },
new StudentSportsProportionData { Title = "女", Value = f_max }
}
};
var m_min = maleData.Any() ? maleData.Min(x => x.Value) : 0;
var f_min = femaleData.Any() ? femaleData.Min(x => x.Value) : 0;
var minData = new SportsProportionData
{
Name = "最低",
Datas = new List<StudentSportsProportionData>
{
new StudentSportsProportionData { Title = "男", Value = m_min },
new StudentSportsProportionData { Title = "女", Value = f_min }
}
};
result.Add(avgData);
result.Add(minData);
result.Add(maxData);
return result;
}
/// <summary>
/// 心率变化趋势
/// </summary>
/// <param name="heartRateDataList"></param>
/// <returns></returns>
public static List<SportsProportionData> GetHeartRateTrend(List<Ai_HeartRateData> heartRateDataList)
{
var result = new List<SportsProportionData>();
if (heartRateDataList == null || !heartRateDataList.Any())
return result;
var baseTime = heartRateDataList.Min(x => x.ScoreTime);
var heartRateWithMinutes = heartRateDataList
.Select(data => new
{
Data = data,
MinuteBucket = (int)(data.ScoreTime - baseTime).TotalMinutes
})
.ToList();
var maxMinute = heartRateWithMinutes.Max(x => x.MinuteBucket);
for (int minute = 0; minute <= maxMinute; minute++)
{
var minuteData = heartRateWithMinutes
.Where(x => x.MinuteBucket == minute)
.Select(x => x.Data)
.ToList();
if (minuteData.Any())
{
result.Add(new SportsProportionData()
{
Name = $"{minute + 1} 分钟",
Datas = new List<StudentSportsProportionData>
{
new StudentSportsProportionData {
Title = "心率",
Value = (int)minuteData.Average(x => x.Value)
}
}
});
}
}
return result;
}
/// <summary>
/// 通用百分比计算方法
/// </summary>
/// <param name="count"></param>
/// <param name="totalCount"></param>
/// <returns></returns>
public static double CalculatePercentage(int count, int totalCount)
{
if (totalCount == 0) return 0;
return Math.Round((double)count / totalCount * 100, 0);
}
2025-06-06 16:00:39 +08:00
}
}