using Furion;
using Furion.DatabaseAccessor;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using Mapster;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using System.Linq.Dynamic.Core;
using System.Text;
using System.Web;
using Admin.NET.Core;
using Microsoft.AspNetCore.Authorization;
namespace Admin.NET.Application
{
///
/// Excel模板服务
///
[Route("api")]
[ApiDescriptionSettings(Name = "ExcelTemplate", Order = 100)]
[AllowAnonymous]
public class SysExcelTemplateService : ISysExcelTemplateService, IDynamicApiController, ITransient
{
private readonly IRepository _sysExcelTemplateRep;
private readonly IRepository _sysDictTypeRep;
private readonly IRepository _sysDictDataRep;
private readonly static object _lock = new();
///
/// Excel模板构造函数
///
///
///
///
public SysExcelTemplateService(
IRepository sysExcelTemplateRep,
IRepository sysDictTypeRep,
IRepository sysDictDataRep
)
{
_sysExcelTemplateRep = sysExcelTemplateRep;
_sysDictTypeRep = sysDictTypeRep;
_sysDictDataRep = sysDictDataRep;
}
///
/// 分页查询Excel模板
///
///
///
[HttpGet("sysExcelTemplate/page")]
public async Task> PageAsync([FromQuery] SysExcelTemplateSearch input)
{
var pName = input.Name?.Trim() ?? "";
var pVersion = input.Version?.Trim() ?? "";
var pAppName = input.AppName?.Trim() ?? "";
var pClassName = input.ClassName?.Trim() ?? "";
var pTemplateFileName = input.TemplateFileName?.Trim() ?? "";
var pUnionUniqueFields = input.UnionUniqueFields?.Trim() ?? "";
var pStatus = input.Status;
var sysExcelTemplates = await _sysExcelTemplateRep.DetachedEntities
.Where(!string.IsNullOrEmpty(pName), u => EF.Functions.Like(u.Name, $"%{pName}%"))
.Where(!string.IsNullOrEmpty(pVersion), u => EF.Functions.Like(u.Version, $"%{pVersion}%"))
.Where(!string.IsNullOrEmpty(pAppName), u => EF.Functions.Like(u.AppName, $"%{pAppName}%"))
.Where(!string.IsNullOrEmpty(pClassName), u => EF.Functions.Like(u.ClassName, $"%{pClassName}%"))
.Where(!string.IsNullOrEmpty(pTemplateFileName), u => EF.Functions.Like(u.TemplateFileName, $"%{pTemplateFileName}%"))
.Where(!string.IsNullOrEmpty(pUnionUniqueFields), u => EF.Functions.Like(u.UnionUniqueFields, $"%{pUnionUniqueFields}%"))
.Where(pStatus != null, u => u.Status == pStatus)
.OrderBy(PageInputOrder.OrderBuilder(input))
.ProjectToType()
.ToADPagedListAsync(input.PageNo, input.PageSize);
return sysExcelTemplates;
}
///
/// 不分页查询Excel模板列表
///
/// Excel模板查询参数
/// (Excel模板)实例列表
[HttpGet("sysExcelTemplate/listNonPage")]
public async Task> ListNonPageAsync([FromQuery] SysExcelTemplateSearchNonPage input)
{
var pName = input.Name?.Trim() ?? "";
var pVersion = input.Version?.Trim() ?? "";
var pAppName = input.AppName?.Trim() ?? "";
var pClassName = input.ClassName?.Trim() ?? "";
var pTemplateFileName = input.TemplateFileName?.Trim() ?? "";
var pUnionUniqueFields = input.UnionUniqueFields?.Trim() ?? "";
var pStatus = input.Status;
var sysExcelTemplates = await _sysExcelTemplateRep.DetachedEntities
.Where(!string.IsNullOrEmpty(pName), u => EF.Functions.Like(u.Name, $"%{pName}%"))
.Where(!string.IsNullOrEmpty(pVersion), u => EF.Functions.Like(u.Version, $"%{pVersion}%"))
.Where(!string.IsNullOrEmpty(pAppName), u => EF.Functions.Like(u.AppName, $"%{pAppName}%"))
.Where(!string.IsNullOrEmpty(pClassName), u => EF.Functions.Like(u.ClassName, $"%{pClassName}%"))
.Where(!string.IsNullOrEmpty(pTemplateFileName), u => EF.Functions.Like(u.TemplateFileName, $"%{pTemplateFileName}%"))
.Where(!string.IsNullOrEmpty(pUnionUniqueFields), u => EF.Functions.Like(u.UnionUniqueFields, $"%{pUnionUniqueFields}%"))
.Where(pStatus != null, u => u.Status == pStatus)
.OrderBy(PageInputOrder.OrderNonPageBuilder(input))
.ProjectToType()
.ToListAsync();
return sysExcelTemplates;
}
///
/// 增加Excel模板
///
///
///
[HttpPost("sysExcelTemplate/add")]
public async Task AddAsync(AddSysExcelTemplateInput input)
{
var sysExcelTemplate = input.Adapt();
await _sysExcelTemplateRep.InsertAsync(sysExcelTemplate);
}
///
/// 删除Excel模板
///
///
///
[HttpPost("sysExcelTemplate/delete")]
public async Task DeleteAsync(DeleteSysExcelTemplateInput input)
{
var sysExcelTemplate = await _sysExcelTemplateRep.FirstOrDefaultAsync(u => u.Id == input.Id);
await _sysExcelTemplateRep.DeleteAsync(sysExcelTemplate);
}
///
/// 更新Excel模板
///
///
///
[HttpPost("sysExcelTemplate/edit")]
public async Task UpdateAsync(UpdateSysExcelTemplateInput input)
{
var isExist = await _sysExcelTemplateRep.AnyAsync(u => u.Id == input.Id, false);
if (!isExist) throw Oops.Oh(ErrorCode.D3000);
var sysExcelTemplate = input.Adapt();
await _sysExcelTemplateRep.UpdateAsync(sysExcelTemplate, ignoreNullValues: false);
}
///
/// 修改Excel模板状态
///
///
///
[HttpPost("sysExcelTemplate/changeStatus")]
public async Task ChangeSysExcelTemplateStatusAsync(UpdateSysExcelTemplateStatusInput input)
{
var sysExcelTemplate = await _sysExcelTemplateRep.FirstOrDefaultAsync(u => u.Id == input.Id);
if (!Enum.IsDefined(typeof(CommonStatus), input.Status))
throw Oops.Oh(ErrorCode.D3005);
sysExcelTemplate.Status = input.Status;
}
///
/// 获取Excel模板
///
///
///
[HttpGet("sysExcelTemplate/detail")]
public async Task GetAsync([FromQuery] QuerySysExcelTemplateInput input)
{
return (await _sysExcelTemplateRep.DetachedEntities.FirstOrDefaultAsync(u => u.Id == input.Id))?.Adapt();
}
///
/// 获取Excel模板列表
///
///
///
[HttpGet("sysExcelTemplate/list")]
public async Task> ListAsync([FromQuery] SysExcelTemplateInput input)
{
return await _sysExcelTemplateRep.DetachedEntities.ProjectToType().ToListAsync();
}
///
/// 根据Excel模板查询参数导出Excel
///
/// Excel模板查询参数
/// 导出的Excel文件
[HttpGet("sysExcelTemplate/toExcel")]
public async Task ToExcelAsync([FromQuery] SysExcelTemplateSearchNonPage input)
{
var sysExcelTemplateList = await ListNonPageAsync(input);
MemoryStream ms = new();
DataConvertUtil.ToExcelData(sysExcelTemplateList, _sysDictTypeRep, _sysDictDataRep, out List headers,
out List> data, out string sheetName);
var excelTemplate = await GetByAppNameAndClassNameAndVersionAsync("SysExcelTemplate", "v1");
if (excelTemplate != null)
{
ExcelUtil.ToExcel((App.Configuration["Excel:Template"] ?? "") + (App.Configuration["AppName"] ?? "") + @"\" + excelTemplate.TemplateFileName, headers, data, sheetName, excelTemplate.HeadStartLine, excelTemplate.DataStartLine, ms);
}
else
{
ExcelUtil.ToExcel(headers, data, sheetName, ms);
}
ms.Position = 0;
var fileName = HttpUtility.UrlEncode($"{sheetName}[{DateTimeOffset.Now:yyyy-MM-dd}].xlsx", Encoding.GetEncoding("UTF-8"));
return new FileStreamResult(ms, "application/octet-stream") { FileDownloadName = fileName };
}
///
/// 根据Excel模板查询参数导出Excel
///
/// Excel模板查询参数
///
/// 导出的Excel文件
[HttpGet("sysExcelTemplate/toExcelByAppName")]
public async Task ToExcelAsync([FromQuery] SysExcelTemplateSearchNonPage input, [FromQuery] string appName)
{
var sysExcelTemplateList = await ListNonPageAsync(input);
MemoryStream ms = new();
DataConvertUtil.ToExcelData(sysExcelTemplateList, _sysDictTypeRep, _sysDictDataRep, out List headers,
out List> data, out string sheetName);
var excelTemplate = await GetByAppNameAndClassNameAndVersionAsync("SysExcelTemplate", "v1");
if (excelTemplate != null)
{
ExcelUtil.ToExcel((App.Configuration["Excel:Template"] ?? "") + appName + @"\" + excelTemplate.TemplateFileName, headers, data, sheetName, excelTemplate.HeadStartLine, excelTemplate.DataStartLine, ms);
}
else
{
ExcelUtil.ToExcel(headers, data, sheetName, ms);
}
ms.Position = 0;
var fileName = HttpUtility.UrlEncode($"{sheetName}[{DateTimeOffset.Now:yyyy-MM-dd}].xlsx", Encoding.GetEncoding("UTF-8"));
return new FileStreamResult(ms, "application/octet-stream") { FileDownloadName = fileName };
}
///
/// 根据类名及版本号获取单个Excel模板
///
/// 类名
/// 版本号
/// Excel模板实例
[HttpGet("sysExcelTemplate/getAppNameAndByClassNameAndVersion")]
public async Task GetByAppNameAndClassNameAndVersionAsync([FromQuery] string className, [FromQuery] string version)
{
return await _sysExcelTemplateRep.DetachedEntities.ProjectToType()
.FirstOrDefaultAsync(u => u.ClassName == className && u.Version == version);
}
///
/// 导入Excel模板文件
///
/// Excel模板文件
/// Excel导入方式
/// 导入的记录数
[HttpPost("sysExcelTemplate/fromExcel")]
public async Task FromExcelAsync(IFormFile file, [FromQuery] ImportExcelType importExcelType)
{
int size = 200;
var excelTemplate = await GetByAppNameAndClassNameAndVersionAsync("SysExcelTemplate", "v2");
if (excelTemplate == null) throw Oops.Oh(ErrorCode.Excel002);
var keys = excelTemplate.UnionUniqueFields.Split(",") ?? Array.Empty();
for (var i = 0; i < keys.Length; i++)
{
keys[i] = keys[i]?.Trim() ?? string.Empty;
}
ExcelUtil.FromExcel(file, excelTemplate.HeadStartLine, excelTemplate.DataStartLine, out List headers, out List> data, out string sheetName);
List sysExcelTemplateList = DataConvertUtil.ToObjectList(headers, data, sheetName, keys, excelTemplate?.DataStartLine ?? 2, out Dictionary dict);
List> uniqueKeyValueDictList = sysExcelTemplateList.ParseUniqueKeyValueDictList(keys.ToList(), excelTemplate?.DataStartLine ?? 2, sheetName);
var filters = DataConvertUtil.GetExpressionListByUniqueDict(keys.ToList(), uniqueKeyValueDictList, size);
var selectKeys = keys.ToList();
if (!selectKeys.Contains("Id")) selectKeys.Add("Id");
var selector = DataConvertUtil.GetSelectExpressionListByUniqueDict(selectKeys);
List updates = new();
List adds = new();
lock (_lock)
{
foreach (var filter in filters)
{
var sysExcelTemplateExistSubList = _sysExcelTemplateRep.Where(filter).Select(selector).ToList();
sysExcelTemplateExistSubList.ForEach(x =>
{
var k = DataConvertUtil.GetKey(x, keys);
if (dict.ContainsKey(k)) dict[k].Id = x.Id;
});
}
foreach (var sysExcelTemplate in sysExcelTemplateList)
{
if (sysExcelTemplate.Id > 0)
{
if (importExcelType == ImportExcelType.ADD_AND_UPDATE) updates.Add(sysExcelTemplate.Adapt());
}
else
{
adds.Add(sysExcelTemplate.Adapt());
}
}
if (importExcelType == ImportExcelType.ADD_AND_UPDATE) updates.ForEach(x => _sysExcelTemplateRep.Update(x));
var maxId = _sysExcelTemplateRep.DetachedEntities.OrderByDescending(x => x.Id).Select(x => x.Id).FirstOrDefault();
adds.ForEach(x => x.Id = ++maxId);
Db.GetDbContext().Set().AddRange(adds);
Db.GetDbContext().SaveChanges();
}
await Task.CompletedTask;
return adds.Count;
}
///
/// 根据版本下载Excel导入模板
///
/// 模板版本
/// 下载的模板
[HttpGet("sysExcelTemplate/downloadExcelTemplate")]
public async Task DownloadExcelTemplate([FromQuery] string version)
{
var excelTemplate = await GetByAppNameAndClassNameAndVersionAsync("SysExcelTemplate", version);
if (excelTemplate == null) throw Oops.Oh(ErrorCode.Excel002);
var path = Path.Combine((App.Configuration["Excel:Template"] ?? "") + (App.Configuration["AppName"] ?? "") + @"\", excelTemplate.TemplateFileName);
Stream ms = FileUtil.Download(path, excelTemplate.TemplateFileName);
var fileName = HttpUtility.UrlEncode($"{excelTemplate.Name}导入模板.xlsx", Encoding.GetEncoding("UTF-8"));
return new FileStreamResult(ms, "application/octet-stream") { FileDownloadName = fileName };
}
///
/// 获取实体名称获取属性集合
///
/// 实体名称
///
[HttpGet("sysExcelTemplate/getColumnList")]
public async Task> GetColumnListAsync([FromQuery] string className)
{
var dbContext = Db.GetDbContext();
var entityType = dbContext.GetService().Model.GetEntityTypes()
.FirstOrDefault(u => u.ClrType.Name == className);
if (entityType == null) return new List();
var type = entityType.ClrType;
if (type == null) return new List();
var columnList = new List()
{
"Id", "CreatedTime", "UpdatedTime", "CreatedUserId", "CreatedUserName", "UpdatedUserId", "UpdatedUserName", "IsDeleted"
};
List columns = type.GetProperties()
.Select(propertyInfo => entityType.FindProperty(propertyInfo.Name))
.Where(p => p != null && !columnList.Contains(p.Name)).Select(p =>
{
var propInfo = p?.PropertyInfo;
var type = propInfo?.PropertyType;
var typeName = type?.ToString() ?? string.Empty;
return new TableColumn
{
IsRequired = FieldUtil.IsRequired(p ?? null!, typeName),
ColumnName = p?.Name,
ColumnKey = ("Id".Equals(p?.Name)).ToString(),
DataType = typeName,
ColumnComment = p?.GetComment()
};
}).ToList();
columns.ForEach(x => x.Remark = x.DataType.GetColumnRemark());
await Task.CompletedTask;
return columns;
}
///
/// 据类名查询其查询标量类型
///
/// 类名
///
[HttpGet("sysExcelTemplate/queryUniqueColumns")]
public async Task> QueryUniqueColumns(string className)
{
var dbContext = Db.GetDbContext();//默认数据库
var entityType = dbContext?.GetService().Model.GetEntityTypes().FirstOrDefault(u => u.ClrType.Name == className);
await Task.CompletedTask;
return TypeUtil.GetUniqueColumns(entityType);
}
///
/// 根据类名查询其查询表信息
///
/// 类型名
///
[HttpGet("sysExcelTemplate/queryTable")]
public async Task QueryTable(string className)
{
var dbContext = Db.GetDbContext();//默认数据库
var entityType = dbContext?.GetService().Model.GetEntityTypes().FirstOrDefault(u => u.ClrType.Name == className);
await Task.CompletedTask;
return TypeUtil.GetTableInfo(entityType);
}
///
/// 根据类名生成相应的种子代码
///
/// 类名
/// 前缀
///
[HttpGet("sysExcelTemplate/parseSeedData")]
public async Task ParseSeedData([FromQuery] string className, string prefix)
{
var mId = await ParseId(prefix);
var tableInfo = await QueryTable(className);
var appName = App.Configuration["AppName"];
// var folder = App.Configuration["Desktop"];
var folder = System.IO.Path.Combine(App.WebHostEnvironment.WebRootPath, "AutoCreateFile", "ExcelTemplateTxt");
var unionUniqueColumns = await QueryUniqueColumns(className);
string unionUniqueFields = "";
unionUniqueColumns.ForEach(x => unionUniqueFields += x.ColumnName + ",");
if (unionUniqueFields.EndsWith(",")) unionUniqueFields = unionUniqueFields[..^1];
string fileName = DateTime.Now.Ticks + ".txt";
string msgExcelTemplate = $"new SysExcelTemplate {{ Id={mId++}, Name=\"{tableInfo?.TableComment ?? ""}\", Version = \"v1\" , ClassName=\"{tableInfo?.TableName ?? ""}\", TemplateFileName=\"{(tableInfo?.TableName ?? "").ToUnderLine()}_v1.xlsx\", UnionUniqueFields=\"{unionUniqueFields}\", AppName = \"{appName}\", HeadStartLine = 1, DataStartLine = 2, Status = 0}},";
FileUtil.WriteLine(folder, fileName, msgExcelTemplate);
msgExcelTemplate = $"new SysExcelTemplate {{ Id={mId++}, Name=\"{tableInfo?.TableComment ?? ""}\", Version = \"v2\" , ClassName=\"{tableInfo?.TableName ?? ""}\", TemplateFileName=\"{(tableInfo?.TableName ?? "").ToUnderLine()}_v2.xlsx\", UnionUniqueFields=\"{unionUniqueFields}\", AppName = \"{appName}\", HeadStartLine = 11, DataStartLine = 12, Status = 0}},";
FileUtil.WriteLine(folder, fileName, msgExcelTemplate);
FileUtil.WriteLine(folder, fileName, "");
return Path.Combine(folder, fileName);
}
///
/// 获取模板提示字符
///
/// 实体名称
///
[HttpGet("sysExcelTemplate/parseTemplateHint")]
public async Task ParseTemplateHintAsync([FromQuery] string className)
{
List tableColumns = await GetColumnListAsync(className);
var folder = App.Configuration["Desktop"];
string fileName = DateTime.Now.Ticks + ".txt";
FileUtil.WriteLine(folder, fileName, "");
FileUtil.WriteLine(folder, fileName, "1.支持Excel2007及以上版本文件。");
FileUtil.WriteLine(folder, fileName, "2.导入新增数据时不能超过5000行。");
FileUtil.WriteLine(folder, fileName, "3.导入更新数据时不能超过2000行。");
FileUtil.WriteLine(folder, fileName, "");
FileUtil.WriteLine(folder, fileName, "");
Dictionary typeNameDict = new()
{
{ "System.String", "文本。如: 钟孝本"},
{ "System.Int32", "数字。如: 4377"},
{ "System.Int64", "数字。如: 4377"},
{ "System.DateTimeOffset", "日期。 如: 2023/3/1"},
{ "System.DateTime", "日期。 如: 2023/3/1"}
};
tableColumns.ForEach(x =>
{
string text = "";
text += x.ColumnComment + "(" + (x.IsRequired ? "必填" : "非必填") + "): ";
text += typeNameDict.ContainsKey(x.DataType) ? typeNameDict[x.DataType] : ("文本。 可选项为: " + x.Remark);
FileUtil.WriteLine(folder, fileName, text);
});
return Path.Combine(folder, fileName);
}
///
/// 根据类名获取最大ID加1
///
/// 前缀
///
[HttpGet("sysExcelTemplate/parseId")]
public async Task ParseId([FromQuery] string prefix)
{
var lastExcelTemplate = await _sysExcelTemplateRep.DetachedEntities.OrderByDescending(x => x.Id).FirstOrDefaultAsync();
var mId = (lastExcelTemplate?.Id + 1) ?? 1;
mId = long.Parse(prefix + mId.ToString()[1..]);
return mId;
}
}
}