diff --git a/API/TaskManager.Entity/Entity.cs b/API/TaskManager.Entity/Entity.cs index 239cbc5..186038f 100644 --- a/API/TaskManager.Entity/Entity.cs +++ b/API/TaskManager.Entity/Entity.cs @@ -39,6 +39,13 @@ namespace TaskManager.Entity [ExporterHeader(IsIgnore = true)] public string? Module { get; set; } + /// + /// 模块 + /// + [ExporterHeader(IsIgnore = true)] + public string? Client { get; set; } + + /// /// 备注 /// @@ -64,6 +71,8 @@ namespace TaskManager.Entity /// [ExporterHeader(DisplayName = "是否自动执行")] public bool IsAuto { get; set; } + public TaskConifgure() + { } } diff --git a/API/Wood.Admin.WebApi/Startup.cs b/API/Wood.Admin.WebApi/Startup.cs index 03a0a5d..bc3f64e 100644 --- a/API/Wood.Admin.WebApi/Startup.cs +++ b/API/Wood.Admin.WebApi/Startup.cs @@ -28,6 +28,7 @@ using TaskManager.EntityFramework.Repository; using Wood.Admin.WebApi.Filter; using Wood.Admin.WebApi.Middleware; using Wood.Data.Repository; +using Wood.Service.Controllers.RegistService; using Wood.Util; namespace Wood.Admin.WebApi @@ -66,8 +67,31 @@ namespace Wood.Admin.WebApi GlobalContext.JwtConfig = Configuration.GetSection("JwtConfig").Get()!; GlobalContext.Services = services; GlobalContext.Configuration = Configuration; - //初始化 eventbus - services.AddEventBus(); + + + //// 扫描当前程序集 + //var assembly = Assembly.GetExecutingAssembly(); + + //// 方法1:按约定注册服务 + //services.AddServicesByConvention(assembly); + + //// 方法2:注册所有实现了IRepository接口的类 + //services.AddImplementationsOf(assembly); + + //// 方法3:注册特定命名空间下的所有服务 + //services.Scan(scan => scan + // .FromAssemblyOf() + // .AddClasses(classes => classes.InNamespace("YourProject.Services")) + // .AsImplementedInterfaces() + // .WithScopedLifetime()); + + + + + + + //初始化 eventbus + services.AddEventBus(); //初始化数据库 services.AddSqlSugar(Configuration); @@ -119,92 +143,6 @@ namespace Wood.Admin.WebApi services.AddScoped(); - //services.AddTransient(implementationFactory => - //{ - // Func accesor = key => - // { - // if (key.Equals(typeof(JisBBACSeEdiCompareExportService).FullName)) - // { - // return implementationFactory.GetService(); - // } - // if (key.Equals(typeof(JisHBPOSeEdiCompareExportService).FullName)) - // { - // return implementationFactory.GetService(); - // } - // if (key.Equals(typeof(JisBBACSaSeEdiCompareExportService).FullName)) - // { - // return implementationFactory.GetService(); - // } - // if (key.Equals(typeof(JisHBPOSaSeEdiCompareExportService).FullName)) - // { - // return implementationFactory.GetService(); - // } - // if (key.Equals(typeof(MaiDanBBACSaSeCompareExportService).FullName)) - // { - // return implementationFactory.GetService(); - // } - // if (key.Equals(typeof(MaiDanHBPOSaSeCompareExportService).FullName)) - // { - // return implementationFactory.GetService(); - // } - // if (key.Equals(typeof(PubSaSeCompareExportService).FullName)) - // { - // return implementationFactory.GetService(); - // } - // if (key.Equals(typeof(PendingDeductionService).FullName)) - // { - // return implementationFactory.GetService(); - // } - // if (key.Equals(typeof(PendingDeductionDelService).FullName)) - // { - // return implementationFactory.GetService(); - // } - // if (key.Equals(typeof(GenerateJisInvoiceService).FullName)) - // { - // return implementationFactory.GetService(); - // } - - // if (key.Equals(typeof(BalanceSumService).FullName)) - // { - // return implementationFactory.GetService(); - // } - - // if (key.Equals(typeof(EdiWmsDiffService).FullName)) - // { - // return implementationFactory.GetService(); - // } - // if (key.Equals(typeof(HBPOEdiWmsDiffService).FullName)) - // { - // return implementationFactory.GetService(); - // } - - // if (key.Equals(typeof(PDMakeService).FullName)) - // { - // return implementationFactory.GetService(); - // } - // if (key.Equals(typeof(MakeCanSettlementService).FullName)) - // { - // return implementationFactory.GetService(); - // } - // if (key.Equals(typeof(ReturnCanSettlement).FullName)) - // { - // return implementationFactory.GetService(); - // } - - - // else - // { - // throw new ArgumentException($"Not Support key:{key}"); - // } - // }; return accesor; - //}); - - - - - - - // 14. 风险确认相关服务(如果类型名正确) // 配置 DbContext 使用 SQL Server 连接字符串 diff --git a/API/Wood.Service/Controllers/NormalBaseController.cs b/API/Wood.Service/Controllers/NormalBaseController.cs index 45ad0c0..c844a9d 100644 --- a/API/Wood.Service/Controllers/NormalBaseController.cs +++ b/API/Wood.Service/Controllers/NormalBaseController.cs @@ -6,24 +6,29 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; +using System.Data; using System.Linq; using System.Linq.Expressions; +using System.Reflection; using System.Text; using System.Threading.Tasks; using TaskManager.Entity; using TaskManager.EntityFramework; using TaskManager.EntityFramework.Repository; +using Wood.Util; namespace Wood.Service.Controllers { [AllowAnonymous] - public class NormalBaseController:ControllerBase where T:BaseEntity + public class NormalBaseController:ControllerBase where T:BaseEntity { protected readonly JobDbContext _context; protected readonly IServiceProvider _builder; protected readonly IConfiguration _configuration; protected readonly IRepository _repository; + + public NormalBaseController(JobDbContext context, IServiceProvider builder, IConfiguration configuration, IRepository repository) { _builder = builder; @@ -90,7 +95,7 @@ namespace Wood.Service.Controllers [HttpGet] - public async Task>> GetPaged( + public async Task>> GetPaged( [FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 10, [FromQuery] string sortBy = "", @@ -113,12 +118,12 @@ namespace Wood.Service.Controllers return Ok(pagedResult); } - - public async Task Export([FromQuery] int pageNumber = 1, + [HttpGet] + public async Task Export([FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 10, [FromQuery] string sortBy = "", [FromQuery] bool isAscending = true, - [FromQuery] Dictionary filters = null) where T : class, new() + [FromQuery] Dictionary filters = null) { var pagingParams = new PagingParams { @@ -133,53 +138,211 @@ namespace Wood.Service.Controllers Expression> filter = null; var pagedResult = await _repository.GetPagedAsync(null, pagingParams); - return await ExportFile(pagedResult.Data as ICollection, Guid.NewGuid().ToString() + ".xlsx"); + + var dataTable=pagedResult.Data.ToDataTable(); + + + + return await ExportFile(dataTable, Guid.NewGuid().ToString() + ".xlsx"); } - protected async Task ExportFile(ICollection dtos, string fileName) where T: class, new() + protected async Task ExportFile(DataTable dtos, string fileName) { var excelExporter = HttpContext.RequestServices.GetRequiredService(); - var res = await excelExporter.ExportAsByteArray(dtos); + 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() where T: class, new() + //[HttpGet] + + //public async Task GetImportTemplate() + //{ + // try + // { + // // 创建导入模板生成器 + // var importer = new ExcelImporter(); + + // // 生成导入模板流(这里假设使用 YourModel 作为导入模型) + // byte[] by = await importer.GenerateTemplate; + // using var stream = new MemoryStream(by); + // stream.Seek(0, SeekOrigin.Begin); + + // // 设置友好的文件名,例如:"导入模板_20250530.xlsx" + // var fileName = $"导入模板_{DateTime.Now:yyyyMMdd}.xlsx"; + + // // 返回文件流结果 + // return File( + // fileStream: stream, + // contentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + // fileDownloadName: fileName); + // } + // catch (Exception ex) + // { + // // 记录异常日志 + // Console.WriteLine($"生成导入模板时出错: {ex.Message}"); + + // // 返回错误响应 + // return StatusCode(500, "生成导入模板时发生错误"); + // } + //} + + + + + + } + + public static class DataTableHelper + { + /// + /// 将泛型列表转换为DataTable + /// + /// 实体类型 + /// 泛型列表 + /// 转换配置(可选) + /// DataTable + public static DataTable ToDataTable(this List list, Action> config = null) { - try + if (list == null || list.Count == 0) + return new DataTable(); + + var tableConfig = new DataTableConfig(); + config?.Invoke(tableConfig); + + var dataTable = new DataTable(); + var properties = GetProperties(typeof(T), tableConfig); + + // 创建列 + foreach (var property in properties) { - // 创建导入模板生成器 - var importer = new ExcelImporter(); - - // 生成导入模板流(这里假设使用 YourModel 作为导入模型) - byte[] by = await importer.GenerateTemplateBytes(); - using var stream = new MemoryStream(by); - stream.Seek(0, SeekOrigin.Begin); - - // 设置友好的文件名,例如:"导入模板_20250530.xlsx" - var fileName = $"导入模板_{DateTime.Now:yyyyMMdd}.xlsx"; - - // 返回文件流结果 - return File( - fileStream: stream, - contentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - fileDownloadName: fileName); + var columnType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType; + var columnName = tableConfig.ColumnMappings.TryGetValue(property.Name, out var mappedName) + ? mappedName + : property.Name; + + var column = new DataColumn(columnName, columnType); + dataTable.Columns.Add(column); } - catch (Exception ex) + + // 填充数据 + foreach (var item in list) { - // 记录异常日志 - Console.WriteLine($"生成导入模板时出错: {ex.Message}"); + var row = dataTable.NewRow(); + foreach (var property in properties) + { + var columnName = tableConfig.ColumnMappings.TryGetValue(property.Name, out var mappedName) + ? mappedName + : property.Name; - // 返回错误响应 - return StatusCode(500, "生成导入模板时发生错误"); + var value = property.GetValue(item); + row[columnName] = value ?? DBNull.Value; + } + dataTable.Rows.Add(row); } + + return dataTable; } + /// + /// 获取需要转换的属性列表 + /// + private static PropertyInfo[] GetProperties(Type type, DataTableConfig config) + { + var bindingFlags = BindingFlags.Public | BindingFlags.Instance; + if (config.IgnoreNonPublicProperties) + bindingFlags &= ~BindingFlags.NonPublic; + var properties = type.GetProperties(bindingFlags); + if (config.IgnoreProperties?.Count > 0) + { + properties = Array.FindAll(properties, p => !config.IgnoreProperties.Contains(p.Name)); + } + if (config.OnlyIncludeProperties?.Count > 0) + { + properties = Array.FindAll(properties, p => config.OnlyIncludeProperties.Contains(p.Name)); + } + + return properties; + } + /// + /// 数据转换配置类 + /// + public class DataTableConfig : DataTableConfig + { + public new Dictionary ColumnMappings { get; } = new Dictionary(); + + /// + /// 设置列映射(属性名 -> 列名) + /// + public DataTableConfig MapColumn(string propertyName, string columnName) + { + ColumnMappings[propertyName] = columnName; + return this; + } + + /// + /// 设置忽略的属性 + /// + public new DataTableConfig IgnoreProperty(params string[] propertyNames) + { + base.IgnoreProperty(propertyNames); + return this; + } + + /// + /// 设置只包含的属性 + /// + public new DataTableConfig OnlyIncludeProperty(params string[] propertyNames) + { + base.OnlyIncludeProperty(propertyNames); + return this; + } + + /// + /// 设置是否忽略非公共属性(默认忽略) + /// + public new DataTableConfig SetIgnoreNonPublicProperties(bool ignore = true) + { + base.SetIgnoreNonPublicProperties(ignore); + return this; + } + } + + /// + /// 数据转换配置基类 + /// + public class DataTableConfig + { + public HashSet IgnoreProperties { get; } = new HashSet(); + public HashSet OnlyIncludeProperties { get; } = new HashSet(); + public Dictionary ColumnMappings { get; } = new Dictionary(); + public bool IgnoreNonPublicProperties { get; private set; } = true; + + public DataTableConfig IgnoreProperty(params string[] propertyNames) + { + foreach (var name in propertyNames) + IgnoreProperties.Add(name); + return this; + } + + public DataTableConfig OnlyIncludeProperty(params string[] propertyNames) + { + OnlyIncludeProperties.Clear(); + foreach (var name in propertyNames) + OnlyIncludeProperties.Add(name); + return this; + } + + public DataTableConfig SetIgnoreNonPublicProperties(bool ignore = true) + { + IgnoreNonPublicProperties = ignore; + return this; + } + } } } diff --git a/API/Wood.Service/Controllers/RecurringJobBaseController.cs b/API/Wood.Service/Controllers/RecurringJobBaseController.cs index 9a36dc7..e79084e 100644 --- a/API/Wood.Service/Controllers/RecurringJobBaseController.cs +++ b/API/Wood.Service/Controllers/RecurringJobBaseController.cs @@ -33,6 +33,7 @@ namespace TaskManager.Controllers protected string Path { set; get; } = "/v2/get/supplierProPlaning"; protected string Url { set; get; } = "/v2/get/supplierProPlaning"; protected virtual string TaskName { set; get; } = "SupplierProPlaning"; + protected readonly LogController _logger; diff --git a/API/Wood.Service/Controllers/RecurringJobInputPageController.cs b/API/Wood.Service/Controllers/RecurringJobInputPageController.cs index 9453803..373a7b9 100644 --- a/API/Wood.Service/Controllers/RecurringJobInputPageController.cs +++ b/API/Wood.Service/Controllers/RecurringJobInputPageController.cs @@ -19,9 +19,13 @@ namespace TaskManager.Controllers } - public async Task ProcessUnsyncedTasks() + protected override async Task DoExecutingAsync(string url, string path, string takName, string client) { - // 查询主表中未完成且子表存在的任务 + Url = url; + Path = path; + TaskName = takName; + Client = client; + await SyncTaskSubTable(TaskName,Client); } private async Task PostPageAsync(PagedRequest t) @@ -94,9 +98,9 @@ namespace TaskManager.Controllers } - private async Task SyncTaskSubTable(TaskSub master) + private async Task SyncTaskSubTable(string taskName, string client) { - var sublist = _jobDbContext.TaskSub.Where(p => p.TaskName == master.TaskName && p.WriteState == false).ToList(); + var sublist = _jobDbContext.TaskSub.Where(p => p.TaskName == taskName && p.WriteState == false && p.Subscriber == client).ToList(); int pageSize = 1000; if (!sublist.Any()) { @@ -132,20 +136,6 @@ namespace TaskManager.Controllers } - //if (!records.Any()) - //{ - - - - - - // // 所有子表记录已同步,标记主表完成 - // master.WriteState = true; - // _dbContext.Update(master); - // await _dbContext.SaveChangesAsync(); - // return; - //} - } } diff --git a/API/Wood.Service/Controllers/RecurringJobOutPageController.cs b/API/Wood.Service/Controllers/RecurringJobOutPageController.cs index fece58f..248a43a 100644 --- a/API/Wood.Service/Controllers/RecurringJobOutPageController.cs +++ b/API/Wood.Service/Controllers/RecurringJobOutPageController.cs @@ -378,7 +378,7 @@ namespace TaskManager.Controllers var pagedResult = await _repository.GetPagedAsync(filter, pagingParams); return Ok(pagedResult); } - //GET /api/products?pageNumber=1&pageSize=10&filters[name]=Phone&filters[price]=999 + diff --git a/API/Wood.Service/Controllers/TOKEN_CONTROLLER.cs b/API/Wood.Service/Controllers/TOKEN_CONTROLLER.cs deleted file mode 100644 index e8f35c8..0000000 --- a/API/Wood.Service/Controllers/TOKEN_CONTROLLER.cs +++ /dev/null @@ -1,185 +0,0 @@ -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Text; - -using System.Threading.Tasks; - - -namespace TaskManager.Controllers -{ - public class TOKEN_CONTROLLER - { - - private readonly HttpClient _httpClient; - private readonly string _appKey = "8EG566b9bedd2bf46d"; - private readonly string _appSecret = "48edc4425647425d87f806a1ba492580"; // 若有密钥需传入 - - public TOKEN_CONTROLLER() - { - _httpClient = new HttpClient(); - - - - } - public async Task ExecuteAsync() - { - try - { - var retult = await GetTokenAsync("https://ediuat.mychery.com/prod-api/auth/public/login/appKey"); - } - catch (Exception ex) - { - Console.WriteLine(ex.Message); - } - - } - - public async Task GetTokenAsync(string tokenUrl) - { - var parameters = new - { - appKey = _appKey, - appSecret = _appSecret, // 按需传参 - // 其他参数如 grant_type、scope 等根据接口要求调整 - }; - - var content = new StringContent( - JsonConvert.SerializeObject(parameters), - Encoding.UTF8, - "application/json" - ); - var response = await _httpClient.PostAsync(tokenUrl, content); - response.EnsureSuccessStatusCode(); // 抛异常处理错误 - var responseBody = await response.Content.ReadAsStringAsync(); - var result = JsonConvert.DeserializeObject(responseBody); - return result.data.access_token; // 假设返回字段为 access_token - } - - - - } - - //private readonly string _appKey = "8EG566b9bedd2bf46d"; - //private readonly string _appSecret = "48edc4425647425d87f806a1ba492580"; - //private readonly string _tokenEndpoint = "https://ediuat.mychery.com/prod-api/auth/public/login/appKey"; - - //private readonly HttpClient _httpClient; - //private readonly SemaphoreSlim _refreshLock = new SemaphoreSlim(1, 1); - //private string _currentToken; - //private DateTime _tokenExpiry; - //private bool _disposed; - - //public TokenServiceController() - //{ - // _httpClient = new HttpClient(); - // _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - //} - - //public async Task GetTokenAsync(CancellationToken cancellationToken = default) - //{ - // // 检查令牌是否存在且未过期(提前60秒刷新以防止边缘情况) - // if (!string.IsNullOrEmpty(_currentToken) && _tokenExpiry > DateTime.UtcNow.AddSeconds(60)) - // { - // return _currentToken; - // } - - // // 等待获取锁,确保只有一个线程刷新令牌 - // await _refreshLock.WaitAsync(cancellationToken); - // try - // { - // // 再次检查,避免其他线程已经刷新了令牌 - // if (!string.IsNullOrEmpty(_currentToken) && _tokenExpiry > DateTime.UtcNow.AddSeconds(60)) - // { - // return _currentToken; - // } - - // // 调用认证API获取新令牌 - // var tokenResponse = await FetchNewTokenAsync(cancellationToken); - - // // 更新令牌和过期时间 - // _currentToken = tokenResponse.AccessToken; - // _tokenExpiry = DateTime.UtcNow.AddSeconds(tokenResponse.ExpiresIn); - - // return _currentToken; - // } - // finally - // { - // _refreshLock.Release(); - // } - //} - - //private async Task FetchNewTokenAsync(CancellationToken cancellationToken) - //{ - // try - // { - // // 创建请求内容 - // var requestBody = new - // { - // appKey = _appKey, - // appSecret = _appSecret - // }; - - // var content = new StringContent( - // JsonSerializer.Serialize(requestBody), - // Encoding.UTF8, - // "application/json"); - - // // 发送请求 - // var response = await _httpClient.PostAsync(_tokenEndpoint, content, cancellationToken); - // response.EnsureSuccessStatusCode(); - - // // 解析响应 - // var jsonResponse = await response.Content.ReadAsStringAsync(cancellationToken); - // var tokenResponse = JsonSerializer.Deserialize( - // jsonResponse, - // new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); - - // if (tokenResponse == null || string.IsNullOrEmpty(tokenResponse.AccessToken)) - // { - // throw new InvalidOperationException("Failed to retrieve access token."); - // } - - // return tokenResponse; - // } - // catch (Exception ex) - // { - // Console.WriteLine($"Token acquisition failed: {ex.Message}"); - // throw; - // } - //} - - //// 令牌响应模型 - //private class TokenResponse - //{ - // public string AccessToken { get; set; } - // public int ExpiresIn { get; set; } = 3600; // 默认1小时 - //} - - //public void Dispose() - //{ - // Dispose(true); - // GC.SuppressFinalize(this); - //} - - //protected virtual void Dispose(bool disposing) - //{ - // if (!_disposed) - // { - // if (disposing) - // { - // _httpClient?.Dispose(); - // _refreshLock?.Dispose(); - // } - // _disposed = true; - // } - //} - - //public Task ExecuteAsync() - //{ - // throw new NotImplementedException(); - //} -} diff --git a/API/Wood.Service/Controllers/TaskConifgureController.cs b/API/Wood.Service/Controllers/TaskConifgureController.cs index 43d2ab1..e7d3de9 100644 --- a/API/Wood.Service/Controllers/TaskConifgureController.cs +++ b/API/Wood.Service/Controllers/TaskConifgureController.cs @@ -34,14 +34,6 @@ namespace TaskManager.Controllers } - protected async Task ExportFile(ICollection dtos, string fileName) where T : class, new() - { - 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 }; - } - - /// /// 请除所有任务 /// @@ -68,7 +60,7 @@ namespace TaskManager.Controllers var first = await _context.TaskConifgure.FirstOrDefaultAsync(p => p.TaskName == taskName); var url = first.Url; var path = first.Api; - var client = first.Module; + var client = first.Client; var controller = _builder.GetRequiredService(); await controller.ExecuteAsync(url, path, taskName,client); @@ -276,7 +268,7 @@ namespace TaskManager.Controllers { var url = task.Url; var path = task.Api; - var client = task.Module; + var client = task.Client; @@ -732,39 +724,8 @@ namespace TaskManager.Controllers } - /// 导出 - /// - /// 第几页 - /// 每页条数 - /// 排序列 - /// 是否升序 - /// 查询条件 - /// - - 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"); - - - } - + + // 使用 Hangfire 注册定时任务