From b68a6c471fa0c9907146cf49764a06b27aa3e06f Mon Sep 17 00:00:00 2001 From: liguoyang Date: Fri, 18 Apr 2025 16:06:16 +0800 Subject: [PATCH] =?UTF-8?q?feat=20=E5=AF=BC=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dlhis.Jinko.Face.sln | 6 + Src/Dlhis.Canteen/Dlhis.Canteen.csproj | 1 + Src/Dlhis.Canteen/Dockerfile | 2 + Src/Dlhis.Canteen/ExcelResultAttribute.cs | 43 ++++ .../Order/Controllers/BalancesController.cs | 1 + .../Order/Response/BalanceRecordResponse.cs | 37 +++- .../Dlhis.ExcelHelper.csproj | 13 ++ Src/Dlhis.ExcelHelper/ExcelAttribute.cs | 19 ++ Src/Dlhis.ExcelHelper/ExcelExtensions.cs | 51 +++++ Src/Dlhis.ExcelHelper/IExcelExportable.cs | 5 + .../Request/DataExportRequest.cs | 13 ++ .../Request/DataImportRequest.cs | 9 + .../Services/IDataExportService.cs | 13 ++ .../Services/Implements/DataExportService.cs | 188 ++++++++++++++++++ 14 files changed, 391 insertions(+), 10 deletions(-) create mode 100644 Src/Dlhis.Canteen/ExcelResultAttribute.cs create mode 100644 Src/Dlhis.ExcelHelper/Dlhis.ExcelHelper.csproj create mode 100644 Src/Dlhis.ExcelHelper/ExcelAttribute.cs create mode 100644 Src/Dlhis.ExcelHelper/ExcelExtensions.cs create mode 100644 Src/Dlhis.ExcelHelper/IExcelExportable.cs create mode 100644 Src/Dlhis.ExcelHelper/Request/DataExportRequest.cs create mode 100644 Src/Dlhis.ExcelHelper/Request/DataImportRequest.cs create mode 100644 Src/Dlhis.ExcelHelper/Services/IDataExportService.cs create mode 100644 Src/Dlhis.ExcelHelper/Services/Implements/DataExportService.cs diff --git a/Dlhis.Jinko.Face.sln b/Dlhis.Jinko.Face.sln index 0cf2b39a..ed3b6625 100644 --- a/Dlhis.Jinko.Face.sln +++ b/Dlhis.Jinko.Face.sln @@ -51,6 +51,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dlhis.Constants", "Src\Dlhi EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dlhis.Canteen.Api", "Src\Dlhis.Canteen.Api\Dlhis.Canteen.Api.csproj", "{7934BE46-9409-4D28-8B35-82A0F900DD01}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dlhis.ExcelHelper", "Src\Dlhis.ExcelHelper\Dlhis.ExcelHelper.csproj", "{F8287775-E641-479D-952B-FFE7D826E52E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -129,6 +131,10 @@ Global {7934BE46-9409-4D28-8B35-82A0F900DD01}.Debug|Any CPU.Build.0 = Debug|Any CPU {7934BE46-9409-4D28-8B35-82A0F900DD01}.Release|Any CPU.ActiveCfg = Release|Any CPU {7934BE46-9409-4D28-8B35-82A0F900DD01}.Release|Any CPU.Build.0 = Release|Any CPU + {F8287775-E641-479D-952B-FFE7D826E52E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F8287775-E641-479D-952B-FFE7D826E52E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F8287775-E641-479D-952B-FFE7D826E52E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F8287775-E641-479D-952B-FFE7D826E52E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Src/Dlhis.Canteen/Dlhis.Canteen.csproj b/Src/Dlhis.Canteen/Dlhis.Canteen.csproj index b3030c28..42d85d59 100644 --- a/Src/Dlhis.Canteen/Dlhis.Canteen.csproj +++ b/Src/Dlhis.Canteen/Dlhis.Canteen.csproj @@ -24,6 +24,7 @@ + diff --git a/Src/Dlhis.Canteen/Dockerfile b/Src/Dlhis.Canteen/Dockerfile index dcac8e9d..7b29619f 100644 --- a/Src/Dlhis.Canteen/Dockerfile +++ b/Src/Dlhis.Canteen/Dockerfile @@ -12,8 +12,10 @@ ARG BUILD_CONFIGURATION=Release WORKDIR /src COPY ["Src/Dlhis.Canteen/Dlhis.Canteen.csproj", "Src/Dlhis.Canteen/"] COPY ["Src/Dlhis.Basic.Api/Dlhis.Basic.Api.csproj", "Src/Dlhis.Basic.Api/"] +COPY ["Src/Dlhis.Constants/Dlhis.Constants.csproj", "Src/Dlhis.Constants/"] COPY ["Src/Dlhis.Entity/Dlhis.Entity.csproj", "Src/Dlhis.Entity/"] COPY ["Src/Dlhis.DataPermission.Model/Dlhis.DataPermission.Model.csproj", "Src/Dlhis.DataPermission.Model/"] +COPY ["Src/Dlhis.ExcelHelper/Dlhis.ExcelHelper.csproj", "Src/Dlhis.ExcelHelper/"] COPY ["Src/Dlhis.Sys.Api/Dlhis.Sys.Api.csproj", "Src/Dlhis.Sys.Api/"] RUN dotnet restore "./Src/Dlhis.Canteen/Dlhis.Canteen.csproj" COPY . . diff --git a/Src/Dlhis.Canteen/ExcelResultAttribute.cs b/Src/Dlhis.Canteen/ExcelResultAttribute.cs new file mode 100644 index 00000000..3f0dde87 --- /dev/null +++ b/Src/Dlhis.Canteen/ExcelResultAttribute.cs @@ -0,0 +1,43 @@ +using Dlhis.ExcelHelper; +using Kurisu.AspNetCore.DataAccess.Dto; +using Kurisu.AspNetCore.UnifyResultAndValidation.Abstractions; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; + +namespace Dlhis.Canteen; + +/// +/// 忽略响应.Data的自动camelCase +/// +[AttributeUsage(AttributeTargets.Method)] +public class ExcelResultAttribute : Attribute, IAsyncActionFilter +{ + private readonly string _fileName; + + public ExcelResultAttribute(string fileName) + { + _fileName = fileName; + } + + /// + public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + var r = await next(); + var or = r.Result as ObjectResult; + var v = or.Value; + var type = or.DeclaredType.GenericTypeArguments[0]; + if (or.DeclaredType.IsGenericType && or.DeclaredType.GetGenericTypeDefinition() == typeof(Pagination<>)) + { + v = v.GetType().GetProperty("Data").GetValue(v, null); + } + + var method = typeof(ExcelExtensions).GetMethod(nameof(ExcelExtensions.ToExcelBytesAsync)); + var task = (Task)method.MakeGenericMethod(type).Invoke(null, new[] { v }); + var data = await task; + + r.Result = new FileContentResult(data, "application/vnd.ms-excel") + { + FileDownloadName = _fileName + }; + } +} \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Order/Controllers/BalancesController.cs b/Src/Dlhis.Canteen/Order/Controllers/BalancesController.cs index ee707e15..62c05f76 100644 --- a/Src/Dlhis.Canteen/Order/Controllers/BalancesController.cs +++ b/Src/Dlhis.Canteen/Order/Controllers/BalancesController.cs @@ -42,6 +42,7 @@ public class BalancesController : ControllerBase /// /// [HttpGet("export")] + [ExcelResult("员工账务明细")] [IgnoreTenant] public async Task> GetPageToExportAsync([FromQuery] BalanceRecordPageRequest request) { diff --git a/Src/Dlhis.Canteen/Order/Response/BalanceRecordResponse.cs b/Src/Dlhis.Canteen/Order/Response/BalanceRecordResponse.cs index ac19cb85..12bb8a1d 100644 --- a/Src/Dlhis.Canteen/Order/Response/BalanceRecordResponse.cs +++ b/Src/Dlhis.Canteen/Order/Response/BalanceRecordResponse.cs @@ -1,11 +1,13 @@ using Dlhis.Entity.Canteen.Enums; +using Dlhis.ExcelHelper; using Kurisu.AspNetCore.Utils.Converters; using Kurisu.AspNetCore.Utils.Extensions; using Newtonsoft.Json; namespace Dlhis.Canteen.Order.Response; -public class BalanceRecordResponse +[Excel("BalanceRecords")] +public class BalanceRecordResponse : IExcelExportable { public long Id { get; set; } @@ -27,34 +29,40 @@ public class BalanceRecordResponse /// /// 姓名 /// + [Excel("姓名")] public string Name { get; set; } /// /// 学工号 /// + [Excel("工号")] public string UserCode { get; set; } /// /// 部门号 /// + [Excel("部门号")] public string DepartmentNo { get; set; } /// /// 部门 /// + [Excel("部门名称")] public string DepartmentName { get; set; } /// /// 所属成本日期 /// + [Excel("营业日期", "yyyy-MM-dd")] [JsonConverter(typeof(ToDateJsonConverter))] public DateTime CostDate { get; set; } /// - /// 备注 + /// 记录产生时间 /// - public string Remark { get; set; } + [Excel("消费时间", "yyyy-MM-dd HH:mm:ss")] + public DateTime CostDateTime { get; set; } /// /// 来源 @@ -64,6 +72,7 @@ public class BalanceRecordResponse /// /// 消费识别来源 /// + [Excel("消费来源")] public string SourceDesc => Source.GetDescription(); /// @@ -76,11 +85,13 @@ public class BalanceRecordResponse /// /// 金额 /// + [Excel("消费金额", "0.00")] public decimal Amount { get; set; } /// /// 营业点 /// + [Excel("营业点")] public string BusinessPlaceName { get; set; } /// @@ -91,6 +102,7 @@ public class BalanceRecordResponse /// /// 餐次 /// + [Excel("场次")] public string ShiftTypeDesc => ShiftType?.GetDescription(); /// @@ -103,11 +115,6 @@ public class BalanceRecordResponse /// public string CostId { get; set; } - /// - /// 消费者所在基地 - /// - public string OriginTenantId { get; set; } - /// /// 数据状态 /// @@ -116,20 +123,30 @@ public class BalanceRecordResponse /// /// 数据状态描述 /// + [Excel("数据状态")] public string StatusDesc => Status.GetDescription(); /// - /// 记录产生时间 + /// 消费者所在基地 /// - public DateTime CostDateTime { get; set; } + [Excel("在职厂区")] + public string OriginTenantId { get; set; } /// /// 厂区 /// + [Excel("消费厂区")] public string Factory { get; set; } /// /// 人事范围 /// + [Excel("人事范围")] public string Werks { get; set; } + + /// + /// 备注 + /// + [Excel("备注")] + public string Remark { get; set; } } \ No newline at end of file diff --git a/Src/Dlhis.ExcelHelper/Dlhis.ExcelHelper.csproj b/Src/Dlhis.ExcelHelper/Dlhis.ExcelHelper.csproj new file mode 100644 index 00000000..ef6578a3 --- /dev/null +++ b/Src/Dlhis.ExcelHelper/Dlhis.ExcelHelper.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + enable + enable + + + + + + + diff --git a/Src/Dlhis.ExcelHelper/ExcelAttribute.cs b/Src/Dlhis.ExcelHelper/ExcelAttribute.cs new file mode 100644 index 00000000..04a358c3 --- /dev/null +++ b/Src/Dlhis.ExcelHelper/ExcelAttribute.cs @@ -0,0 +1,19 @@ +namespace Dlhis.ExcelHelper; + +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property)] +public class ExcelAttribute : Attribute +{ + public ExcelAttribute(string description) : this(description, string.Empty) + { + } + + public ExcelAttribute(string description, string format) + { + Description = description; + Format = format; + } + + + public string Description { get; set; } + public string Format { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.ExcelHelper/ExcelExtensions.cs b/Src/Dlhis.ExcelHelper/ExcelExtensions.cs new file mode 100644 index 00000000..8878ca80 --- /dev/null +++ b/Src/Dlhis.ExcelHelper/ExcelExtensions.cs @@ -0,0 +1,51 @@ +using System.Reflection; +using OfficeOpenXml; + +namespace Dlhis.ExcelHelper; + +public static class ExcelExtensions +{ + public static async Task ToExcelBytesAsync(this List data) where T : IExcelExportable + { + ExcelPackage.LicenseContext = LicenseContext.NonCommercial; + + var type = typeof(T); + var sheetName = type.GetCustomAttribute()!.Description; + var excelHeaderNames = type.GetProperties().Where(x => x.IsDefined(typeof(ExcelAttribute))).ToList(); + + using var package = new ExcelPackage(); + ExcelWorksheet worksheet = package.Workbook.Worksheets.Add(sheetName); + + // 写入表头 + for (int i = 0; i < excelHeaderNames.Count; i++) + { + var cell = worksheet.Cells[1, i + 1]; + worksheet.Column(i + 1).BestFit = true; + cell.Style.Font.Bold = true; + cell.Value = excelHeaderNames[i].GetCustomAttribute()!.Description; + } + + int row = 2; + foreach (var item in data) + { + for (int col = 0; col < excelHeaderNames.Count; col++) + { + var column = excelHeaderNames[col]; + var value = column.GetValue(item); + + var cell = worksheet.Cells[row, col + 1]; + var format = column.GetCustomAttribute()!.Format; + cell.Value = value; + if (!string.IsNullOrEmpty(format)) + { + cell.Style.Numberformat.Format = format; + } + } + + row++; + } + + worksheet.Cells.AutoFitColumns(); + return await package.GetAsByteArrayAsync(); + } +} \ No newline at end of file diff --git a/Src/Dlhis.ExcelHelper/IExcelExportable.cs b/Src/Dlhis.ExcelHelper/IExcelExportable.cs new file mode 100644 index 00000000..b4070791 --- /dev/null +++ b/Src/Dlhis.ExcelHelper/IExcelExportable.cs @@ -0,0 +1,5 @@ +namespace Dlhis.ExcelHelper; + +public interface IExcelExportable +{ +} diff --git a/Src/Dlhis.ExcelHelper/Request/DataExportRequest.cs b/Src/Dlhis.ExcelHelper/Request/DataExportRequest.cs new file mode 100644 index 00000000..40f0f536 --- /dev/null +++ b/Src/Dlhis.ExcelHelper/Request/DataExportRequest.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations; + +namespace Dlhis.Basic.Common.DataExport.Request +{ + public class DataExportRequest + { + [Required] + public string TableName { get; set; } + + [MinLength(1)] + public List ColumnNames { get; set; } + } +} \ No newline at end of file diff --git a/Src/Dlhis.ExcelHelper/Request/DataImportRequest.cs b/Src/Dlhis.ExcelHelper/Request/DataImportRequest.cs new file mode 100644 index 00000000..cbf1b8ff --- /dev/null +++ b/Src/Dlhis.ExcelHelper/Request/DataImportRequest.cs @@ -0,0 +1,9 @@ +namespace Dlhis.Basic.Common.DataExport.Request +{ + public class DataImportRequest + { + public string TableName { get; set; } + + public string FilePath { get; set; } + } +} diff --git a/Src/Dlhis.ExcelHelper/Services/IDataExportService.cs b/Src/Dlhis.ExcelHelper/Services/IDataExportService.cs new file mode 100644 index 00000000..972905bd --- /dev/null +++ b/Src/Dlhis.ExcelHelper/Services/IDataExportService.cs @@ -0,0 +1,13 @@ +//using Dlhis.Basic.Common.DataExport.Request; +//using Microsoft.AspNetCore.Mvc; + +//namespace Dlhis.Basic.Common.DataExport.Services +//{ +// public interface IDataExportService : IScopeDependency +// { +// Task GetDataExportAsync(DataExportRequest request); + +// Task DataImportAsync(IFormFile file); + +// } +//} \ No newline at end of file diff --git a/Src/Dlhis.ExcelHelper/Services/Implements/DataExportService.cs b/Src/Dlhis.ExcelHelper/Services/Implements/DataExportService.cs new file mode 100644 index 00000000..174afcc7 --- /dev/null +++ b/Src/Dlhis.ExcelHelper/Services/Implements/DataExportService.cs @@ -0,0 +1,188 @@ +//using System.Collections; +//using System.Reflection; +//using Dlhis.Basic.Common.DataExport.Request; +//using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; +//using Kurisu.AspNetCore.Utils.Extensions; +//using Mapster; +//using Microsoft.AspNetCore.Mvc; +//using Newtonsoft.Json.Linq; +//using OfficeOpenXml; +//using SqlSugar; + +//namespace Dlhis.Basic.Common.DataExport.Services.Implements +//{ +// public class DataExportService : IDataExportService +// { +// protected IDbContext DbContext { get; } + +// public DataExportService(IDbContext dbContext) +// { +// DbContext = dbContext; +// ExcelPackage.LicenseContext = LicenseContext.NonCommercial; +// } + +// private readonly static MethodInfo _queryMethod = typeof(IAdo).GetMethod(nameof(IAdo.SqlQueryAsync), 1, new[] { typeof(string), typeof(object) }, null); +// private readonly static MethodInfo _handlerTaskMethod = typeof(DataExportService).GetMethod(nameof(HandlerResult), BindingFlags.NonPublic | BindingFlags.Instance); + +// public async Task GetDataExportAsync(DataExportRequest request) +// { +// var types = Assembly.Load("Dlhis.Entity") +// .GetTypes() +// .Where(x => x.IsClass && x.GetCustomAttribute() != null); + +// var type = types.FirstOrDefault(x => x.GetCustomAttribute()?.TableName == request.TableName)!; +// type.ThrowIfNull("该表不存在"); + +// var columns = type.GetProperties().Where(x => request.ColumnNames.Contains(x.Name)).ToList(); +// var excelHeaderNames = columns.Select(x => x.GetCustomAttribute()?.ColumnDescription ?? x.Name) +// .ToList(); + + +// using (var package = new ExcelPackage()) +// { +// ExcelWorksheet worksheet = package.Workbook.Worksheets.Add(request.TableName); + +// // 写入表头 +// for (int i = 0; i < excelHeaderNames.Count; i++) +// { +// worksheet.Cells[1, i + 1].Value = excelHeaderNames[i]; +// } + +// var tableName = type.GetCustomAttribute().TableName; +// var fields = columns.Select(x => x.Name).Join(","); + +// var task = _queryMethod.MakeGenericMethod(type).Invoke(DbContext.Client.Ado, new object[] { @$"SELECT {fields} FROM {tableName}", null }); +// var data = _handlerTaskMethod.MakeGenericMethod(type).Invoke(this, new object[] { task }); + +// int row = 2; +// foreach (var item in data.ToJson().ToObject>()) +// { +// for (int col = 0; col < columns.Count; col++) +// { +// var column = columns[col]; +// var value = item[column.Name]!.Value(); + +// if (column.PropertyType.IsEnum) +// { +// value = column.PropertyType.GetFields(BindingFlags.Public | BindingFlags.Static).Where(p => (int)p.GetValue(null) == item[column.Name]!.Value()).Select(p => +// { +// var e = (Enum)Enum.Parse(p.FieldType, p.GetValue(null).ToString()); +// var fd = e.GetDescription(); + +// return fd; +// }).First(); +// } + +// worksheet.Cells[row, col + 1].Value = value; +// } +// row++; +// } + +// var ms = new MemoryStream(); + +// await package.SaveAsAsync(ms); +// var contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; + +// return new FileContentResult(ms.ToArray(), contentType); + +// } +// } +// private List HandlerResult(object o) +// { +// var originalValue = (Task>)o; +// if (!originalValue.IsCompleted) +// { +// return Task.Run(() => originalValue).GetAwaiter().GetResult(); +// } + +// return originalValue.Result; +// } + +// public async Task DataImportAsync(IFormFile file) +// { +// //获取数据 +// ExcelPackage.LicenseContext = OfficeOpenXml.LicenseContext.NonCommercial; +// using (var package = new ExcelPackage(file.OpenReadStream())) +// { +// var worksheet = package.Workbook.Worksheets[0]; + +// //通过表名,获取实体表类型 +// var type = GetEntityTypeByTableName(worksheet.Name); +// var tableType = typeof(List<>).MakeGenericType(type); +// var listGenericAddMethod = tableType.GetMethod("Add"); +// var tableTypeInstance = Activator.CreateInstance(tableType); + +// var headers = new List(); +// for (int col = 1; col <= worksheet.Dimension.Columns; col++) +// { +// headers.Add(worksheet.Cells[1, col].Value?.ToString() ?? string.Empty); +// } + +// var properties = type.GetProperties(); +// for (int row = 2; row <= worksheet.Dimension.Rows; row++) +// { +// var item = Activator.CreateInstance(type); + +// for (int col = 1; col <= worksheet.Dimension.Columns; col++) +// { +// var header = headers[col - 1]; +// var property = properties.FirstOrDefault(p => (p.GetCustomAttribute()?.ColumnDescription ?? p.Name).Equals(header, StringComparison.OrdinalIgnoreCase)); + +// var value = worksheet.Cells[row, col].Value.Adapt(typeof(object), property.PropertyType); +// if (property != null) +// { +// if (property.PropertyType.IsEnum) +// { +// try +// { +// var enumValue = Enum.Parse(property.PropertyType, value.ToString(), true); +// property.SetValue(item, enumValue); +// } +// catch (Exception ex) +// { +// Console.WriteLine($"无法将值 '{value}' 转换为枚举类型 '{property.PropertyType.Name}':{ex.Message}"); +// } +// } +// else +// { +// property.SetValue(item, value); +// } +// } +// } + +// listGenericAddMethod.Invoke(tableTypeInstance, new[] { item }); +// } + +// //foreach (var item in list) +// // await DbContext.Client.InsertableByObject(item).ExecuteCommandAsync(); + +// var insertMethod = typeof(IDbContext).GetMethods(BindingFlags.Public | BindingFlags.Instance) +// .Where(x => x.Name == nameof(DbContext.InsertAsync)) +// .Where(x => +// { +// var t = x.GetParameters().First().ParameterType; + +// var rr = x.GetParameters().First().ParameterType.IsAssignableTo(typeof(IList)) && !t.IsArray; + +// return rr; +// }) +// .First(); + +// var task = (Task)insertMethod.MakeGenericMethod(type).Invoke(DbContext, new object[] { tableTypeInstance }); +// await task.ConfigureAwait(false); +// } +// } + + + +// public Type GetEntityTypeByTableName(string tableName) +// { +// var type = Assembly.Load("Dlhis.Entity") +// .GetTypes() +// .Where(x => x.IsClass && x.GetCustomAttribute() != null) +// .FirstOrDefault(x => x.GetCustomAttribute().TableName == tableName); + +// return type; +// } +// } +//} \ No newline at end of file