using Azure.Core; using Dapper; using Magicodes.ExporterAndImporter.Core.Extension; using Magicodes.ExporterAndImporter.Excel; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Omu.ValueInjecter; using SkiaSharp; using System.Data; using System.Drawing.Printing; using System.Globalization; using System.Linq.Expressions; using System.Security.Policy; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using TaskManager.Contracts.Dtos; using TaskManager.Entity; using TaskManager.EntityFramework; using TaskManager.EntityFramework.Repository; using Wood.Service; using Wood.Util; using Wood.Util.Filters; using static Dapper.SqlMapper; namespace TaskManager.Controllers { public class CheryRecurringJobInputPageController : RecurringJobBaseController where T : BaseEntity ,new() where TDTO:class,new() { protected readonly IRepository _repository; public CheryRecurringJobInputPageController(HttpClient httpClient, JobDbContext jobDbContext, LogController log, IRepository repository) : base(httpClient, jobDbContext, log) { _repository = repository; } /// /// 手工发送调用接口 /// /// 任务名称 /// 请求日期 /// 客户(Chery) /// [HttpGet] public virtual async Task CustomInvokeAsync(string taskName, string client) { var first = _jobDbContext.TaskConifgure.FirstOrDefault(p => p.TaskName == taskName && p.Client == client); if (first == null) { return; } Url = first.Url; Path = first.Api; TaskName = first.TaskName; Client = client; CPageSize = first.PageSize; await SyncTaskSubTable(TaskName, Client); } protected override async Task DoExecutingAsync(string url, string path, string takName, string client, int pagesize) { Url = url; Path = path; TaskName = takName; Client = client; CPageSize = pagesize; await SyncTaskSubTable(TaskName,Client); } private async Task PostPageAsync(PagedRequest t,Guid taskId,string version) { try { var inputjson = JsonSerializer.Serialize(t, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, Converters = { new DecimalTrimConverter() }, WriteIndented = false // 可选,用于格式化输出 } ); //inputjson = RemoveWhitespace(inputjson); var content = await Post(Url, Path, inputjson,taskId,version); if (!string.IsNullOrEmpty(content)) { var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, Converters = { new JsonStringEnumConverter(), // 枚举转换 new CustomDateTimeConverter("yyyy-MM-dd HH:mm:ss") // 日期转换 } }; return JsonSerializer.Deserialize(content, options); } else { await _logger.AddError($"调用接口无返回值{Url}", TaskName,taskId,version); return null; } } catch (Exception ex) { await _logger.AddError($"调用接口无返回值错误{ex.Message}", TaskName,taskId,version); return null; } } public static string GenerateRandomStringWith8EG() { const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; var random = new Random(); var sb = new StringBuilder(32); // 随机生成 "8EG" 的插入位置 (0-29) int position = random.Next(0, 30); // 生成前半部分随机字符串 for (int i = 0; i < position; i++) { sb.Append(chars[random.Next(chars.Length)]); } // 插入 "8EG" sb.Append("8EG"); // 生成后半部分随机字符串,补足32位 int remainingLength = 32 - sb.Length; for (int i = 0; i < remainingLength; i++) { sb.Append(chars[random.Next(chars.Length)]); } return sb.ToString(); } private async Task SyncTaskSubTable(string taskName, string client) { if (string.IsNullOrEmpty(taskName) || string.IsNullOrEmpty(client)) { await _logger.AddError("任务名称或客户端不能为空",taskName,Guid.NewGuid(),DateTime.Now.ToString("yyyyMMdd")); return; } var sublist = _jobDbContext.TaskSub.Where(p => taskName.Contains(p.TaskName) && p.WriteState == false && p.Subscriber == client ).ToList(); int pageSize = CPageSize; if (sublist.Any()) { foreach (var sub in sublist) { string querystr = $"select * from {sub.TableName} where TaskId='{sub.TaskId}' and WriteState=0 and ReadState=1 order by uid";//任务表 var entites = _jobDbContext.Database.GetDbConnection().Query(querystr);//明细表 if (entites.Any()) { var total = entites.Count(); sub.DataCount = total; int totalPages = (int)Math.Ceiling((double)total / pageSize); for (int i = sub.SyncedPageCount; i <= totalPages; i++) { var records = entites.Skip((i - 1) * pageSize) .Take(pageSize).ToList(); #region dto转换作为参数发给客户API 因为实体比DTO字段多 List dtos = new List(); foreach (var itm in records) { TDTO dto = new TDTO(); dto.InjectFrom(itm); dtos.Add(dto); } PagedRequest pagedRequest = new PagedRequest() { batchNo = GenerateRandomStringWith8EG(), total = entites.Count(), pageSize = pageSize, list = dtos, pageNum = i }; #endregion foreach (var itm in records) { itm.WriteState = true; } var result = await PostPageAsync(pagedRequest,sub.TaskId, DateTime.Now.ToString("yyyyMMdd")); if(result.code == 200) { _jobDbContext.BulkUpdate(records); sub.SyncedPageCount = i; if (i == totalPages) { sub.WriteState = true; _jobDbContext.Set().Update(sub); _jobDbContext.SaveChanges(); } } else { sub.FailedCount = i*pageSize; await _logger.AddError($"第 {i} 页奇瑞数据保存失败 {result.message}。", TaskName,sub.TaskId, DateTime.Now.ToString("yyyyMMdd")); } } } } } } [HttpGet] public async Task>> GetAll() { return await _repository.GetAllAsync() as List; } [HttpGet("{id}")] public async Task> GetById(int id) { var entity = await _repository.GetByIdAsync(id); if (entity == null) return NotFound(); return entity; } [HttpPost] public async Task> Create(T entity) { entity.CreationTime = DateTime.Now; var createdEntity = await _repository.AddAsync(entity); return new JsonResult(new { Code = 200, Message = "创建成功!" }); ; } [HttpPut("{id}")] public async Task Update(T entity) { if (entity.UId == 0) { throw new Exception("更新时,实体主键UId不能为空或0"); } var target = await _repository.GetByIdAsync(entity.UId); if (target == null) { throw new Exception($"CheryRecurringJobInputPageController.Update报错:根据UId{entity.UId}没有找到记录"); return new JsonResult(new { Code = 400, Message = "修改失败!" }); } EntityMapper.Trans(entity, target, "UId"); await _repository.UpdateAsync(target); return new JsonResult(new { Code = 200, Message = "修改成功!" }); } [HttpDelete("{id}")] public async Task Delete(int id) { await _repository.DeleteAsync(id); return new JsonResult(new { Code = 200, Message = "删除成功!" }); ; } //[HttpGet] //public async Task GetPaged( //[FromQuery] int pageNumber = 1, //[FromQuery] int pageSize = 10, //[FromQuery] string sortBy = "", //[FromQuery] bool isAscending = true, //[FromQuery] Dictionary filters = null) //{ // var pagingParams = new PagingParams // { // PageNumber = pageNumber, // PageSize = pageSize, // SortBy = sortBy, // IsAscending = isAscending, // Filters = filters // }; // // 可以在这里构建表达式树过滤条件 // Expression> filter = null; // var pagedResult = await _repository.GetPagedAsync(filter, pagingParams); // return Ok(pagedResult); //} /// /// 分页New /// /// /// [HttpPost] public async Task>> GetDataPaged(RequestInputBase input) { var pagingParams = new PagingParams { PageNumber = input.pageNumber, PageSize = input.pageSize, SortBy = input.sortBy, IsAscending = input.isAscending, }; var json= JsonSerializer.Serialize(input); // 可以在这里构建表达式树过滤条件 Expression> filter = null; var pagedResult = await _repository.GetDataPagedAsync(filter, pagingParams, input.Condition); return Ok(pagedResult); } public async Task> GetTaskDataPaged(RequestInputBase input) { var pagingParams = new PagingParams { PageNumber = input.pageNumber, PageSize = input.pageSize, SortBy = input.sortBy, IsAscending = input.isAscending, }; var json = JsonSerializer.Serialize(input); // 可以在这里构建表达式树过滤条件 Expression> filter = null; var pagedResult = await _repository.GetDataPagedAsync(filter, pagingParams, input.Condition); return pagedResult; } /// /// 导出New /// /// /// [HttpPost] public async Task ExportData(RequestInputBase input) { var pagingParams = new PagingParams { PageNumber = input.pageNumber, PageSize = input.pageSize, SortBy = input.sortBy, IsAscending = input.isAscending, }; // 可以在这里构建表达式树过滤条件 //Expression> filter = null; var pagedResult = await _repository.GetDataPagedAsync(null, pagingParams, input.Condition); return await ExportFile(pagedResult.Data, Guid.NewGuid().ToString() + ".xlsx"); } // [HttpGet] // public async Task Export([FromQuery] int pageNumber = 1, //[FromQuery] int pageSize = 10, //[FromQuery] string sortBy = "", //[FromQuery] bool isAscending = true, //[FromQuery] Dictionary filters = null) // { // var pagingParams = new PagingParams // { // PageNumber = pageNumber, // PageSize = pageSize, // SortBy = sortBy, // IsAscending = isAscending, // Filters = filters // }; // // 可以在这里构建表达式树过滤条件 // //Expression> filter = null; // var pagedResult = await _repository.GetPagedAsync(null, pagingParams); // return await ExportFile(pagedResult.Data, Guid.NewGuid().ToString() + ".xlsx"); // } protected async Task ExportFile(ICollection dtos, string fileName) { var excelExporter = HttpContext.RequestServices.GetRequiredService(); var res = await excelExporter.ExportAsByteArray(dtos); return new FileStreamResult(new MemoryStream(res), "application/octet-stream") { FileDownloadName = DateTime.Now.ToString("yyyyMMddHHmm") + "_" + fileName }; } [HttpGet] public async Task GetImportTemplate() { try { // 创建导入模板生成器 var importer = new ExcelImporter(); // 生成导入模板流(这里假设使用 YourModel 作为导入模型) var bytes = await importer.GenerateTemplateBytes(); //using var stream = new MemoryStream(bytes); //stream.Seek(0, SeekOrigin.Begin); // 设置友好的文件名,例如:"导入模板_20250530.xlsx" var fileName = $"导入模板_{DateTime.Now:yyyyMMdd}.xlsx"; // 返回文件流结果 return new FileStreamResult(new MemoryStream(bytes), "application/octet-stream") { FileDownloadName = DateTime.Now.ToString("yyyyMMddHHmm") + "_" + fileName }; //return File( // fileStream: stream, // contentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", // fileDownloadName: fileName); } catch (Exception ex) { // 记录异常日志 Console.WriteLine($"生成导入模板时出错: {ex.Message}"); // 返回错误响应 return StatusCode(500, "生成导入模板时发生错误"); } } [HttpPost("Import")] public async virtual Task Import(IFormFile file) { if (file == null || file.Length <= 0) { return NotFound("No file uploaded."); } try { var excelImporter = HttpContext.RequestServices.GetRequiredService(); var importResult = await excelImporter.Import(file.OpenReadStream()); if (importResult.HasError) { StringBuilder sb = new StringBuilder(); foreach (var rowErr in importResult.RowErrors) { string temp = string.Join(";", rowErr.FieldErrors.Select(itm => $"第{rowErr.RowIndex}行:{itm.Key}-{itm.Value}")); sb.AppendLine(temp); } foreach (var templateErr in importResult.TemplateErrors) { string temp = $"列名:{templateErr.RequireColumnName},错误信息:{templateErr.Message}"; sb.AppendLine(temp); } throw new Exception(sb.ToString()); } // 处理导入的数据 List list = importResult.Data.ToList(); await ImportBefore(list); // 校验数据长度 var validationErrors = ValidationHelper.ValidateDataLength(list, _jobDbContext); if (validationErrors.Any()) { throw new Exception("数据校验失败:" + string.Join(", ", validationErrors)); } foreach (var item in list) { item.ReadState = true; item.CreationTime = CommonHelper.CurrentTime; } await _jobDbContext.BulkInsertAsync(list); await ImportAfter(list); return new JsonResult(new { Code = 200, Message = "修改成功!" }); } catch (Exception ex) { //await _logger.AddError(ex.Message, TaskName); return new JsonResult(new { Code = 400, Message = "导入失败:" + ex.Message }); } } /// /// 重写插入前进行操作,如校验等 /// /// /// protected virtual async Task ImportBefore(List p_list) { } /// /// 重写插入后进行操作如 /// /// /// protected virtual async Task ImportAfter(List p_list) { } public class DecimalTrimConverter : JsonConverter { public override decimal Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return reader.GetDecimal(); } public override void Write(Utf8JsonWriter writer, decimal value, JsonSerializerOptions options) { // 关键步骤:使用 TrimTrailingZeros() 去掉末尾的 0 writer.WriteStringValue(value.ToString("0.########################", CultureInfo.InvariantCulture)); } } } }