MES/ERP多段业务多线程并发组件

欢迎各位观众大大浏览阅读我的博客,有空麻烦加一下博客主页关注,谢谢

MES/ERP 多段独立业务多线程并发组件解决方案

你需要的是针对 MES/ERP 场景中连续多段独立不相关的繁复琐碎业务语句,设计支持多线程并行执行的高性能组件,并基于多结构并发模型构建包含可视化界面、后台服务、标准化 API 的完整信息服务中间件方案,核心目标是通过并发执行提升这类琐碎业务的整体处理效率。

本次方案重点解决:独立业务段的线程安全拆分、多结构并发调度、繁复业务的模块化封装,同时保持中间件的易用性、可监控性和可扩展性。

一、核心设计理念

1. 业务场景特征与解决方案映射

场景痛点 技术解决方案
多段业务独立无依赖,串行执行效率低 业务段原子化封装 + 多线程并行执行
业务语句繁复琐碎,代码冗余易出错 业务段模板化 + 参数化配置
多结构(产线 / 工单 / 工序)并发冲突 分级并发控制 + 资源隔离
执行状态不可见,问题定位难 实时监控 + 日志追踪 + 进度反馈
外部系统集成复杂 标准化 API + 中间件解耦

2. 整体架构

3. 核心技术选型

  • 多线程框架:.NET Task Parallel Library (TPL) + 自定义线程池
  • 并发控制:SemaphoreSlim + 分级锁 + Concurrent 集合
  • 业务封装:策略模式 + 模板方法模式 + 原子化业务段
  • 通信层ASP.NET Web API + SignalR(实时推送)
  • 界面层:WinForm(客户端) + DevExpress(增强控件)
  • 日志监控:Serilog + ELK(可选) + 实时性能计数器

二、核心组件实现

1. 原子化业务段封装

1.1 业务段基类与通用实现
复制代码
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;

#region 业务段核心枚举
/// <summary>
/// 业务段类型
/// </summary>
public enum BusinessSegmentType
{
    DataQuery,       // 数据查询
    DataUpdate,      // 数据更新
    ApiCall,         // 外部API调用
    DeviceCommand,   // 设备指令下发
    FileOperation,   // 文件操作
    Calculation      // 计算处理
}

/// <summary>
/// 业务段执行状态
/// </summary>
public enum SegmentStatus
{
    Pending,     // 待执行
    Running,     // 执行中
    Completed,   // 完成
    Failed,      // 失败
    Skipped,     // 跳过
    Cancelled    // 取消
}

/// <summary>
/// 并发执行策略
/// </summary>
public enum ConcurrentStrategy
{
    Parallel,    // 纯并行(无资源竞争)
    Sequential,  // 串行(有资源依赖)
    Batch,       // 批量并行(按批次)
    Priority     // 按优先级并行
}
#endregion

#region 业务段参数模型
/// <summary>
/// 业务段参数基类
/// </summary>
public class SegmentParams
{
    /// <summary>
    /// 业务段ID
    /// </summary>
    public string SegmentId { get; set; } = Guid.NewGuid().ToString("N");
    
    /// <summary>
    /// 业务段名称
    /// </summary>
    public string SegmentName { get; set; }
    
    /// <summary>
    /// 业务段类型
    /// </summary>
    public BusinessSegmentType SegmentType { get; set; }
    
    /// <summary>
    /// 执行优先级(1-10,1最高)
    /// </summary>
    public int Priority { get; set; } = 5;
    
    /// <summary>
    /// 是否允许跳过(执行失败时)
    /// </summary>
    public bool AllowSkip { get; set; } = false;
    
    /// <summary>
    /// 超时时间(秒)
    /// </summary>
    public int Timeout { get; set; } = 30;
    
    /// <summary>
    /// 重试次数
    /// </summary>
    public int RetryCount { get; set; } = 0;
    
    /// <summary>
    /// 重试间隔(毫秒)
    /// </summary>
    public int RetryInterval { get; set; } = 1000;
    
    /// <summary>
    /// 业务参数(JSON格式)
    /// </summary>
    public string BusinessParams { get; set; } = "{}";
    
    /// <summary>
    /// 关联结构ID(产线/工单/工序ID)
    /// </summary>
    public string StructureId { get; set; }
    
    /// <summary>
    /// 关联结构类型
    /// </summary>
    public string StructureType { get; set; }
}

/// <summary>
/// 数据查询业务段参数
/// </summary>
public class DataQuerySegmentParams : SegmentParams
{
    public DataQuerySegmentParams()
    {
        SegmentType = BusinessSegmentType.DataQuery;
    }
    
    /// <summary>
    /// SQL语句
    /// </summary>
    public string Sql { get; set; }
    
    /// <summary>
    /// 查询参数
    /// </summary>
    public Dictionary<string, object> QueryParams { get; set; } = new Dictionary<string, object>();
    
    /// <summary>
    /// 是否返回数据
    /// </summary>
    public bool ReturnData { get; set; } = true;
}

/// <summary>
/// API调用业务段参数
/// </summary>
public class ApiCallSegmentParams : SegmentParams
{
    public ApiCallSegmentParams()
    {
        SegmentType = BusinessSegmentType.ApiCall;
    }
    
    /// <summary>
    /// API地址
    /// </summary>
    public string ApiUrl { get; set; }
    
    /// <summary>
    /// 请求方法(GET/POST/PUT/DELETE)
    /// </summary>
    public string HttpMethod { get; set; } = "POST";
    
    /// <summary>
    /// 请求头
    /// </summary>
    public Dictionary<string, string> Headers { get; set; } = new Dictionary<string, string>();
    
    /// <summary>
    /// 请求体
    /// </summary>
    public string RequestBody { get; set; } = "{}";
}
#endregion

#region 业务段执行结果
/// <summary>
/// 业务段执行结果
/// </summary>
public class SegmentExecutionResult
{
    /// <summary>
    /// 业务段ID
    /// </summary>
    public string SegmentId { get; set; }
    
    /// <summary>
    /// 执行状态
    /// </summary>
    public SegmentStatus Status { get; set; }
    
    /// <summary>
    /// 执行开始时间
    /// </summary>
    public DateTime StartTime { get; set; }
    
    /// <summary>
    /// 执行结束时间
    /// </summary>
    public DateTime EndTime { get; set; }
    
    /// <summary>
    /// 执行耗时(毫秒)
    /// </summary>
    public long ExecutionTimeMs => (long)(EndTime - StartTime).TotalMilliseconds;
    
    /// <summary>
    /// 执行结果数据
    /// </summary>
    public string ResultData { get; set; }
    
    /// <summary>
    /// 错误信息
    /// </summary>
    public string ErrorMessage { get; set; }
    
    /// <summary>
    /// 实际重试次数
    /// </summary>
    public int ActualRetryCount { get; set; } = 0;
}
#endregion

#region 业务段执行器(核心)
/// <summary>
/// 业务段执行器基类
/// </summary>
public abstract class BaseSegmentExecutor
{
    /// <summary>
    /// 业务段参数
    /// </summary>
    protected readonly SegmentParams _params;
    
    /// <summary>
    /// 取消令牌
    /// </summary>
    protected readonly CancellationToken _cancellationToken;
    
    /// <summary>
    /// 进度更新委托
    /// </summary>
    protected readonly Action<string, int, SegmentStatus> _progressUpdateAction;
    
    /// <summary>
    /// 执行结果
    /// </summary>
    protected SegmentExecutionResult _result = new SegmentExecutionResult();

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="segmentParams">业务段参数</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <param name="progressUpdateAction">进度更新委托</param>
    protected BaseSegmentExecutor(SegmentParams segmentParams, 
                                 CancellationToken cancellationToken,
                                 Action<string, int, SegmentStatus> progressUpdateAction = null)
    {
        _params = segmentParams;
        _cancellationToken = cancellationToken;
        _progressUpdateAction = progressUpdateAction;
        
        _result.SegmentId = segmentParams.SegmentId;
    }

    /// <summary>
    /// 执行业务段(带重试和超时控制)
    /// </summary>
    /// <returns></returns>
    public async Task<SegmentExecutionResult> ExecuteAsync()
    {
        _result.StartTime = DateTime.Now;
        _progressUpdateAction?.Invoke(_params.SegmentId, 0, SegmentStatus.Running);

        try
        {
            // 超时控制
            var timeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(_params.Timeout));
            var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(
                _cancellationToken, timeoutTokenSource.Token);

            // 带重试执行核心逻辑
            bool executeSuccess = false;
            int retryCount = 0;

            while (retryCount <= _params.RetryCount && !executeSuccess)
            {
                try
                {
                    // 执行核心逻辑
                    await ExecuteCoreAsync(linkedTokenSource.Token);
                    executeSuccess = true;
                }
                catch (OperationCanceledException)
                {
                    _result.Status = SegmentStatus.Cancelled;
                    _result.ErrorMessage = "执行被取消或超时";
                    break;
                }
                catch (Exception ex)
                {
                    retryCount++;
                    _result.ActualRetryCount = retryCount;

                    if (retryCount > _params.RetryCount)
                    {
                        throw; // 重试次数用尽,抛出异常
                    }

                    // 重试间隔
                    await Task.Delay(_params.RetryInterval, _cancellationToken);
                    _progressUpdateAction?.Invoke(_params.SegmentId, 
                        (int)(retryCount * 100.0 / (_params.RetryCount + 1)), SegmentStatus.Running);
                }
            }

            if (executeSuccess)
            {
                _result.Status = SegmentStatus.Completed;
                _progressUpdateAction?.Invoke(_params.SegmentId, 100, SegmentStatus.Completed);
            }
        }
        catch (OperationCanceledException)
        {
            _result.Status = SegmentStatus.Cancelled;
            _result.ErrorMessage = "执行被取消";
            _progressUpdateAction?.Invoke(_params.SegmentId, 0, SegmentStatus.Cancelled);
        }
        catch (Exception ex)
        {
            _result.Status = _params.AllowSkip ? SegmentStatus.Skipped : SegmentStatus.Failed;
            _result.ErrorMessage = ex.Message;
            _progressUpdateAction?.Invoke(_params.SegmentId, 0, _result.Status);
        }
        finally
        {
            _result.EndTime = DateTime.Now;
            timeoutTokenSource?.Dispose();
        }

        return _result;
    }

    /// <summary>
    /// 核心执行逻辑(由子类实现)
    /// </summary>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns></returns>
    protected abstract Task ExecuteCoreAsync(CancellationToken cancellationToken);

    /// <summary>
    /// 更新执行进度
    /// </summary>
    /// <param name="progress">进度(0-100)</param>
    protected void UpdateProgress(int progress)
    {
        _progressUpdateAction?.Invoke(_params.SegmentId, progress, SegmentStatus.Running);
    }
}

/// <summary>
/// 数据查询业务段执行器
/// </summary>
public class DataQuerySegmentExecutor : BaseSegmentExecutor
{
    private readonly IDbConnection _dbConnection;

    public DataQuerySegmentExecutor(DataQuerySegmentParams segmentParams,
                                   IDbConnection dbConnection,
                                   CancellationToken cancellationToken,
                                   Action<string, int, SegmentStatus> progressUpdateAction = null)
        : base(segmentParams, cancellationToken, progressUpdateAction)
    {
        _dbConnection = dbConnection;
    }

    protected override async Task ExecuteCoreAsync(CancellationToken cancellationToken)
    {
        var queryParams = (DataQuerySegmentParams)_params;
        UpdateProgress(20);

        // 打开数据库连接
        if (_dbConnection.State != ConnectionState.Open)
        {
            await _dbConnection.OpenAsync(cancellationToken);
        }
        UpdateProgress(40);

        // 执行查询
        var result = await _dbConnection.QueryAsync<dynamic>(
            queryParams.Sql, 
            queryParams.QueryParams,
            cancellationToken: cancellationToken);
        UpdateProgress(80);

        // 处理结果
        if (queryParams.ReturnData)
        {
            _result.ResultData = JsonConvert.SerializeObject(result);
        }
        UpdateProgress(100);
    }
}

/// <summary>
/// API调用业务段执行器
/// </summary>
public class ApiCallSegmentExecutor : BaseSegmentExecutor
{
    private readonly HttpClient _httpClient;

    public ApiCallSegmentExecutor(ApiCallSegmentParams segmentParams,
                                 HttpClient httpClient,
                                 CancellationToken cancellationToken,
                                 Action<string, int, SegmentStatus> progressUpdateAction = null)
        : base(segmentParams, cancellationToken, progressUpdateAction)
    {
        _httpClient = httpClient;
    }

    protected override async Task ExecuteCoreAsync(CancellationToken cancellationToken)
    {
        var apiParams = (ApiCallSegmentParams)_params;
        UpdateProgress(10);

        // 创建请求消息
        var request = new HttpRequestMessage(new HttpMethod(apiParams.HttpMethod), apiParams.ApiUrl);
        
        // 添加请求头
        foreach (var header in apiParams.Headers)
        {
            request.Headers.TryAddWithoutValidation(header.Key, header.Value);
        }
        UpdateProgress(30);

        // 添加请求体
        if (!string.IsNullOrEmpty(apiParams.RequestBody) && 
            (apiParams.HttpMethod == "POST" || apiParams.HttpMethod == "PUT"))
        {
            request.Content = new StringContent(apiParams.RequestBody, Encoding.UTF8, "application/json");
        }
        UpdateProgress(50);

        // 发送请求
        var response = await _httpClient.SendAsync(request, cancellationToken);
        UpdateProgress(70);

        // 处理响应
        response.EnsureSuccessStatusCode();
        _result.ResultData = await response.Content.ReadAsStringAsync(cancellationToken);
        UpdateProgress(100);
    }
}

/// <summary>
/// 业务段执行器工厂
/// </summary>
public class SegmentExecutorFactory
{
    private readonly IDbConnection _dbConnection;
    private readonly HttpClient _httpClient;

    public SegmentExecutorFactory(IDbConnection dbConnection, HttpClient httpClient)
    {
        _dbConnection = dbConnection;
        _httpClient = httpClient;
    }

    /// <summary>
    /// 创建业务段执行器
    /// </summary>
    /// <param name="segmentParams">业务段参数</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <param name="progressUpdateAction">进度更新委托</param>
    /// <returns></returns>
    /// <exception cref="NotSupportedException"></exception>
    public BaseSegmentExecutor CreateExecutor(SegmentParams segmentParams,
                                             CancellationToken cancellationToken,
                                             Action<string, int, SegmentStatus> progressUpdateAction = null)
    {
        return segmentParams switch
        {
            DataQuerySegmentParams queryParams => new DataQuerySegmentExecutor(
                queryParams, _dbConnection, cancellationToken, progressUpdateAction),
            ApiCallSegmentParams apiParams => new ApiCallSegmentExecutor(
                apiParams, _httpClient, cancellationToken, progressUpdateAction),
            // 可扩展其他类型的业务段执行器
            _ => throw new NotSupportedException($"不支持的业务段类型:{segmentParams.SegmentType}")
        };
    }
}
#endregion

2. 多结构并发调度引擎

csharp

运行

复制代码
#region 多结构并发调度器
/// <summary>
/// 多结构业务段并发调度器
/// 功能:管理多结构下的业务段并发执行,处理资源隔离和并发控制
/// </summary>
public class MultiStructureConcurrentScheduler : IDisposable
{
    // 单例实例
    private static readonly Lazy<MultiStructureConcurrentScheduler> _instance = 
        new Lazy<MultiStructureConcurrentScheduler>(() => new MultiStructureConcurrentScheduler());
    public static MultiStructureConcurrentScheduler Instance => _instance.Value;

    // 业务段执行器工厂
    private readonly SegmentExecutorFactory _executorFactory;

    // 结构类型并发控制信号量(线程安全)
    private readonly ConcurrentDictionary<string, SemaphoreSlim> _structureSemaphores = 
        new ConcurrentDictionary<string, SemaphoreSlim>();

    // 业务段执行状态(线程安全)
    private readonly ConcurrentDictionary<string, SegmentExecutionResult> _executionResults = 
        new ConcurrentDictionary<string, SegmentExecutionResult>();

    // 取消令牌源(按批次ID)
    private readonly ConcurrentDictionary<string, CancellationTokenSource> _batchCtsDict = 
        new ConcurrentDictionary<string, CancellationTokenSource>();

    // 进度更新事件
    public event Action<string, int, SegmentStatus> ProgressUpdated;

    // 批次完成事件
    public event Action<string, Dictionary<string, SegmentExecutionResult>> BatchCompleted;

    /// <summary>
    /// 私有构造函数
    /// </summary>
    private MultiStructureConcurrentScheduler()
    {
        // 初始化数据库连接和HttpClient
        var dbConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["MESDB"].ConnectionString);
        var httpClient = new HttpClient();
        
        _executorFactory = new SegmentExecutorFactory(dbConnection, httpClient);
        
        // 初始化默认结构类型的并发控制
        var defaultStructures = new[] { "Line", "WorkOrder", "Process", "Station" };
        foreach (var structure in defaultStructures)
        {
            _structureSemaphores.TryAdd(structure, new SemaphoreSlim(5, 20)); // 默认最大并发5
        }
    }

    /// <summary>
    /// 设置结构类型的最大并发数
    /// </summary>
    /// <param name="structureType">结构类型</param>
    /// <param name="maxConcurrent">最大并发数</param>
    public void SetStructureMaxConcurrent(string structureType, int maxConcurrent)
    {
        _structureSemaphores.AddOrUpdate(structureType,
            _ => new SemaphoreSlim(maxConcurrent, maxConcurrent),
            (_, old) => new SemaphoreSlim(maxConcurrent, maxConcurrent));
    }

    /// <summary>
    /// 执行单个业务段
    /// </summary>
    /// <param name="segmentParams">业务段参数</param>
    /// <returns></returns>
    public async Task<SegmentExecutionResult> ExecuteSegmentAsync(SegmentParams segmentParams)
    {
        // 获取结构类型的并发信号量
        var semaphore = _structureSemaphores.GetOrAdd(segmentParams.StructureType,
            _ => new SemaphoreSlim(5, 20));

        // 创建取消令牌
        var cts = new CancellationTokenSource();
        
        try
        {
            // 等待信号量
            await semaphore.WaitAsync(cts.Token);
            
            // 创建执行器
            var executor = _executorFactory.CreateExecutor(
                segmentParams, 
                cts.Token,
                (id, progress, status) => OnProgressUpdated(id, progress, status));
            
            // 执行业务段
            var result = await executor.ExecuteAsync();
            
            // 保存执行结果
            _executionResults[segmentParams.SegmentId] = result;
            
            return result;
        }
        finally
        {
            // 释放信号量
            semaphore.Release();
            cts.Dispose();
        }
    }

    /// <summary>
    /// 批量执行业务段(多结构并发)
    /// </summary>
    /// <param name="batchId">批次ID</param>
    /// <param name="segmentParamsList">业务段参数列表</param>
    /// <param name="concurrentStrategy">并发策略</param>
    /// <returns></returns>
    public async Task ExecuteBatchSegmentsAsync(string batchId,
                                               List<SegmentParams> segmentParamsList,
                                               ConcurrentStrategy concurrentStrategy = ConcurrentStrategy.Parallel)
    {
        // 创建批次取消令牌
        var batchCts = new CancellationTokenSource();
        _batchCtsDict[batchId] = batchCts;

        var batchResults = new Dictionary<string, SegmentExecutionResult>();
        var segmentTasks = new List<Task>();

        try
        {
            // 根据并发策略执行
            switch (concurrentStrategy)
            {
                case ConcurrentStrategy.Parallel:
                    // 纯并行执行所有业务段
                    foreach (var segmentParams in segmentParamsList)
                    {
                        segmentTasks.Add(ExecuteSegmentInternalAsync(segmentParams, batchCts.Token, batchResults));
                    }
                    break;

                case ConcurrentStrategy.Sequential:
                    // 串行执行(按优先级)
                    var sortedSegments = segmentParamsList.OrderBy(s => s.Priority).ToList();
                    foreach (var segmentParams in sortedSegments)
                    {
                        await ExecuteSegmentInternalAsync(segmentParams, batchCts.Token, batchResults);
                    }
                    break;

                case ConcurrentStrategy.Batch:
                    // 按批次并行执行(每批5个)
                    var batches = segmentParamsList.Chunk(5);
                    foreach (var batch in batches)
                    {
                        var batchTasks = batch.Select(s => ExecuteSegmentInternalAsync(s, batchCts.Token, batchResults)).ToList();
                        await Task.WhenAll(batchTasks);
                    }
                    break;

                case ConcurrentStrategy.Priority:
                    // 按优先级分组并行
                    var priorityGroups = segmentParamsList.GroupBy(s => s.Priority).OrderBy(g => g.Key);
                    foreach (var group in priorityGroups)
                    {
                        var groupTasks = group.Select(s => ExecuteSegmentInternalAsync(s, batchCts.Token, batchResults)).ToList();
                        await Task.WhenAll(groupTasks);
                    }
                    break;
            }

            // 等待所有任务完成
            if (segmentTasks.Any())
            {
                await Task.WhenAll(segmentTasks);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"批次执行异常:{ex.Message}");
        }
        finally
        {
            // 触发批次完成事件
            BatchCompleted?.Invoke(batchId, batchResults);
            
            // 清理取消令牌
            _batchCtsDict.TryRemove(batchId, out _);
            batchCts.Dispose();
        }
    }

    /// <summary>
    /// 内部执行单个业务段的方法
    /// </summary>
    private async Task ExecuteSegmentInternalAsync(SegmentParams segmentParams,
                                                  CancellationToken cancellationToken,
                                                  Dictionary<string, SegmentExecutionResult> batchResults)
    {
        var result = await ExecuteSegmentAsync(segmentParams);
        batchResults[segmentParams.SegmentId] = result;
    }

    /// <summary>
    /// 取消批次执行
    /// </summary>
    /// <param name="batchId">批次ID</param>
    public void CancelBatchExecution(string batchId)
    {
        if (_batchCtsDict.TryGetValue(batchId, out var cts))
        {
            cts.Cancel();
            Console.WriteLine($"批次{batchId}执行已取消");
        }
    }

    /// <summary>
    /// 获取业务段执行结果
    /// </summary>
    /// <param name="segmentId">业务段ID</param>
    /// <returns></returns>
    public SegmentExecutionResult GetSegmentResult(string segmentId)
    {
        _executionResults.TryGetValue(segmentId, out var result);
        return result;
    }

    /// <summary>
    /// 清理执行结果
    /// </summary>
    /// <param name="keepHours">保留小时数</param>
    public void CleanupExecutionResults(int keepHours = 24)
    {
        var cutoffTime = DateTime.Now.AddHours(-keepHours);
        var keysToRemove = _executionResults.Where(kv => 
            kv.Value.EndTime < cutoffTime).Select(kv => kv.Key).ToList();

        foreach (var key in keysToRemove)
        {
            _executionResults.TryRemove(key, out _);
        }
    }

    /// <summary>
    /// 进度更新事件触发
    /// </summary>
    private void OnProgressUpdated(string segmentId, int progress, SegmentStatus status)
    {
        ProgressUpdated?.Invoke(segmentId, progress, status);
    }

    /// <summary>
    /// 释放资源
    /// </summary>
    public void Dispose()
    {
        // 释放所有信号量
        foreach (var semaphore in _structureSemaphores.Values)
        {
            semaphore.Dispose();
        }
        
        // 取消所有批次
        foreach (var cts in _batchCtsDict.Values)
        {
            cts.Cancel();
            cts.Dispose();
        }
        
        _structureSemaphores.Clear();
        _batchCtsDict.Clear();
        _executionResults.Clear();
    }
}
#endregion

3. 可视化界面组件(WinForm)

csharp

运行

复制代码
#region WinForm可视化界面
public partial class BusinessSegmentExecutorForm : Form
{
    // 并发调度器
    private readonly MultiStructureConcurrentScheduler _scheduler = 
        MultiStructureConcurrentScheduler.Instance;

    // 批次ID
    private string _currentBatchId = Guid.NewGuid().ToString("N");

    public BusinessSegmentExecutorForm()
    {
        InitializeComponent();
        
        // 注册事件
        _scheduler.ProgressUpdated += OnProgressUpdated;
        _scheduler.BatchCompleted += OnBatchCompleted;
        
        // 初始化UI
        InitUI();
    }

    /// <summary>
    /// 初始化UI
    /// </summary>
    private void InitUI()
    {
        // 结构类型下拉框
        cboStructureType.Items.AddRange(new[] { "Line", "WorkOrder", "Process", "Station" });
        cboStructureType.SelectedIndex = 0;

        // 业务段类型下拉框
        cboSegmentType.Items.AddRange(Enum.GetNames(typeof(BusinessSegmentType)));
        cboSegmentType.SelectedIndex = 0;

        // 并发策略下拉框
        cboConcurrentStrategy.Items.AddRange(Enum.GetNames(typeof(ConcurrentStrategy)));
        cboConcurrentStrategy.SelectedIndex = 0;

        // 初始化业务段列表DataGridView
        dgvSegments.Columns.AddRange(new DataGridViewColumn[]
        {
            new DataGridViewTextBoxColumn { Name = "SegmentId", HeaderText = "业务段ID", Width = 150 },
            new DataGridViewTextBoxColumn { Name = "SegmentName", HeaderText = "业务段名称", Width = 150 },
            new DataGridViewTextBoxColumn { Name = "SegmentType", HeaderText = "类型", Width = 100 },
            new DataGridViewTextBoxColumn { Name = "StructureType", HeaderText = "结构类型", Width = 100 },
            new DataGridViewTextBoxColumn { Name = "StructureId", HeaderText = "结构ID", Width = 100 },
            new DataGridViewTextBoxColumn { Name = "Status", HeaderText = "状态", Width = 100 },
            new DataGridViewTextBoxColumn { Name = "Progress", HeaderText = "进度", Width = 80 },
            new DataGridViewTextBoxColumn { Name = "ExecutionTime", HeaderText = "执行耗时(ms)", Width = 120 },
            new DataGridViewTextBoxColumn { Name = "ErrorMessage", HeaderText = "错误信息", Width = 200 }
        });

        // 按钮事件
        btnAddSegment.Click += BtnAddSegment_Click;
        btnExecuteBatch.Click += BtnExecuteBatch_Click;
        btnCancelBatch.Click += BtnCancelBatch_Click;
        btnClearSegments.Click += BtnClearSegments_Click;
        btnSetMaxConcurrent.Click += BtnSetMaxConcurrent_Click;
    }

    /// <summary>
    /// 添加业务段
    /// </summary>
    private void BtnAddSegment_Click(object sender, EventArgs e)
    {
        try
        {
            SegmentParams segmentParams = null;
            var segmentType = (BusinessSegmentType)Enum.Parse(typeof(BusinessSegmentType), cboSegmentType.SelectedItem.ToString());

            // 根据业务段类型创建参数对象
            switch (segmentType)
            {
                case BusinessSegmentType.DataQuery:
                    segmentParams = new DataQuerySegmentParams
                    {
                        SegmentName = txtSegmentName.Text,
                        StructureType = cboStructureType.SelectedItem.ToString(),
                        StructureId = txtStructureId.Text,
                        Priority = (int)nudPriority.Value,
                        Timeout = (int)nudTimeout.Value,
                        RetryCount = (int)nudRetryCount.Value,
                        AllowSkip = chkAllowSkip.Checked,
                        Sql = txtSql.Text,
                        QueryParams = new Dictionary<string, object>
                        {
                            { "WorkOrderId", txtWorkOrderId.Text }
                        }
                    };
                    break;

                case BusinessSegmentType.ApiCall:
                    segmentParams = new ApiCallSegmentParams
                    {
                        SegmentName = txtSegmentName.Text,
                        StructureType = cboStructureType.SelectedItem.ToString(),
                        StructureId = txtStructureId.Text,
                        Priority = (int)nudPriority.Value,
                        Timeout = (int)nudTimeout.Value,
                        RetryCount = (int)nudRetryCount.Value,
                        AllowSkip = chkAllowSkip.Checked,
                        ApiUrl = txtApiUrl.Text,
                        HttpMethod = cboHttpMethod.SelectedItem.ToString(),
                        RequestBody = txtRequestBody.Text
                    };
                    break;

                default:
                    MessageBox.Show("暂不支持该业务段类型", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    return;
            }

            // 添加到列表
            var row = dgvSegments.Rows.Add();
            row.Cells["SegmentId"].Value = segmentParams.SegmentId;
            row.Cells["SegmentName"].Value = segmentParams.SegmentName;
            row.Cells["SegmentType"].Value = segmentParams.SegmentType.ToString();
            row.Cells["StructureType"].Value = segmentParams.StructureType;
            row.Cells["StructureId"].Value = segmentParams.StructureId;
            row.Cells["Status"].Value = SegmentStatus.Pending.ToString();
            row.Cells["Progress"].Value = "0%";
            row.Cells["ExecutionTime"].Value = "";
            row.Cells["ErrorMessage"].Value = "";

            // 保存参数到行的Tag属性
            row.Tag = segmentParams;

            MessageBox.Show("业务段添加成功", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
        catch (Exception ex)
        {
            MessageBox.Show($"添加业务段失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    /// <summary>
    /// 执行批次
    /// </summary>
    private async void BtnExecuteBatch_Click(object sender, EventArgs e)
    {
        if (dgvSegments.Rows.Count == 0)
        {
            MessageBox.Show("请先添加业务段", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            return;
        }

        try
        {
            // 获取所有业务段参数
            var segmentParamsList = new List<SegmentParams>();
            foreach (DataGridViewRow row in dgvSegments.Rows)
            {
                if (row.Tag is SegmentParams param)
                {
                    segmentParamsList.Add(param);
                }
            }

            // 获取并发策略
            var concurrentStrategy = (ConcurrentStrategy)Enum.Parse(
                typeof(ConcurrentStrategy), cboConcurrentStrategy.SelectedItem.ToString());

            // 生成新的批次ID
            _currentBatchId = Guid.NewGuid().ToString("N");

            // 异步执行批次
            await Task.Run(async () =>
            {
                await _scheduler.ExecuteBatchSegmentsAsync(
                    _currentBatchId, segmentParamsList, concurrentStrategy);
            });

            MessageBox.Show("批次执行完成", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
        catch (Exception ex)
        {
            MessageBox.Show($"执行批次失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    /// <summary>
    /// 取消批次
    /// </summary>
    private void BtnCancelBatch_Click(object sender, EventArgs e)
    {
        try
        {
            _scheduler.CancelBatchExecution(_currentBatchId);
            MessageBox.Show("批次取消请求已提交", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
        catch (Exception ex)
        {
            MessageBox.Show($"取消批次失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    /// <summary>
    /// 进度更新
    /// </summary>
    private void OnProgressUpdated(string segmentId, int progress, SegmentStatus status)
    {
        if (dgvSegments.InvokeRequired)
        {
            dgvSegments.Invoke(new Action(() => UpdateSegmentProgress(segmentId, progress, status)));
        }
        else
        {
            UpdateSegmentProgress(segmentId, progress, status);
        }
    }

    /// <summary>
    /// 更新业务段进度
    /// </summary>
    private void UpdateSegmentProgress(string segmentId, int progress, SegmentStatus status)
    {
        foreach (DataGridViewRow row in dgvSegments.Rows)
        {
            if (row.Cells["SegmentId"].Value?.ToString() == segmentId)
            {
                row.Cells["Status"].Value = status.ToString();
                row.Cells["Progress"].Value = $"{progress}%";

                // 根据状态设置行颜色
                row.DefaultCellStyle.BackColor = status switch
                {
                    SegmentStatus.Running => Color.LightYellow,
                    SegmentStatus.Completed => Color.LightGreen,
                    SegmentStatus.Failed => Color.LightPink,
                    SegmentStatus.Skipped => Color.LightBlue,
                    SegmentStatus.Cancelled => Color.LightGray,
                    _ => Color.White
                };

                // 如果执行完成,更新执行耗时
                if (status == SegmentStatus.Completed || status == SegmentStatus.Failed || status == SegmentStatus.Skipped)
                {
                    var result = _scheduler.GetSegmentResult(segmentId);
                    if (result != null)
                    {
                        row.Cells["ExecutionTime"].Value = result.ExecutionTimeMs;
                        row.Cells["ErrorMessage"].Value = result.ErrorMessage;
                    }
                }

                break;
            }
        }
    }

    /// <summary>
    /// 批次完成事件处理
    /// </summary>
    private void OnBatchCompleted(string batchId, Dictionary<string, SegmentExecutionResult> results)
    {
        if (InvokeRequired)
        {
            Invoke(new Action(() =>
            {
                MessageBox.Show($"批次{batchId}执行完成,总计{results.Count}个业务段", 
                    "批次完成", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }));
        }
        else
        {
            MessageBox.Show($"批次{batchId}执行完成,总计{results.Count}个业务段", 
                "批次完成", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
    }

    /// <summary>
    /// 设置最大并发数
    /// </summary>
    private void BtnSetMaxConcurrent_Click(object sender, EventArgs e)
    {
        try
        {
            var structureType = cboStructureType.SelectedItem.ToString();
            var maxConcurrent = (int)nudMaxConcurrent.Value;
            
            _scheduler.SetStructureMaxConcurrent(structureType, maxConcurrent);
            
            MessageBox.Show($"结构类型{structureType}的最大并发数已设置为{maxConcurrent}", 
                "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
        catch (Exception ex)
        {
            MessageBox.Show($"设置最大并发数失败:{ex.Message}", 
                "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    /// <summary>
    /// 清空业务段列表
    /// </summary>
    private void BtnClearSegments_Click(object sender, EventArgs e)
    {
        dgvSegments.Rows.Clear();
    }

    /// <summary>
    /// 窗体关闭时清理资源
    /// </summary>
    private void BusinessSegmentExecutorForm_FormClosing(object sender, FormClosingEventArgs e)
    {
        _scheduler.ProgressUpdated -= OnProgressUpdated;
        _scheduler.BatchCompleted -= OnBatchCompleted;
        _scheduler.Dispose();
    }
}
#endregion

4. API 接口组件

csharp

运行

复制代码
#region API接口控制器
[Route("api/[controller]")]
[ApiController]
public class BusinessSegmentController : ControllerBase
{
    private readonly MultiStructureConcurrentScheduler _scheduler = 
        MultiStructureConcurrentScheduler.Instance;
    
    private readonly ILogger<BusinessSegmentController> _logger;

    public BusinessSegmentController(ILogger<BusinessSegmentController> logger)
    {
        _logger = logger;
    }

    /// <summary>
    /// 执行单个业务段
    /// </summary>
    /// <param name="segmentRequest">业务段请求</param>
    /// <returns></returns>
    [HttpPost("execute")]
    public async Task<IActionResult> ExecuteSegment([FromBody] SegmentRequest segmentRequest)
    {
        try
        {
            // 转换为业务段参数
            SegmentParams segmentParams = ConvertToSegmentParams(segmentRequest);
            if (segmentParams == null)
            {
                return BadRequest("不支持的业务段类型");
            }

            // 执行业务段
            var result = await _scheduler.ExecuteSegmentAsync(segmentParams);

            _logger.LogInformation($"业务段{segmentParams.SegmentId}执行完成,状态:{result.Status}");

            return Ok(new
            {
                Success = true,
                Data = result
            });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "执行业务段失败");
            return StatusCode(500, new
            {
                Success = false,
                Message = ex.Message
            });
        }
    }

    /// <summary>
    /// 批量执行业务段
    /// </summary>
    /// <param name="batchRequest">批次请求</param>
    /// <returns></returns>
    [HttpPost("execute-batch")]
    public async Task<IActionResult> ExecuteBatch([FromBody] BatchRequest batchRequest)
    {
        try
        {
            // 转换业务段参数列表
            var segmentParamsList = batchRequest.Segments
                .Select(ConvertToSegmentParams)
                .Where(p => p != null)
                .ToList();

            if (!segmentParamsList.Any())
            {
                return BadRequest("无效的业务段参数");
            }

            // 生成批次ID
            var batchId = batchRequest.BatchId ?? Guid.NewGuid().ToString("N");
            
            // 异步执行批次(不等待完成)
            _ = Task.Run(async () =>
            {
                await _scheduler.ExecuteBatchSegmentsAsync(
                    batchId, 
                    segmentParamsList, 
                    batchRequest.ConcurrentStrategy);
            });

            _logger.LogInformation($"批次{batchId}已提交执行,包含{segmentParamsList.Count}个业务段");

            return Ok(new
            {
                Success = true,
                BatchId = batchId,
                Message = "批次已提交执行"
            });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "提交批次执行失败");
            return StatusCode(500, new
            {
                Success = false,
                Message = ex.Message
            });
        }
    }

    /// <summary>
    /// 取消批次执行
    /// </summary>
    /// <param name="batchId">批次ID</param>
    /// <returns></returns>
    [HttpPost("cancel-batch/{batchId}")]
    public IActionResult CancelBatch(string batchId)
    {
        try
        {
            _scheduler.CancelBatchExecution(batchId);
            _logger.LogInformation($"批次{batchId}取消请求已提交");

            return Ok(new
            {
                Success = true,
                Message = "批次取消请求已提交"
            });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, $"取消批次{batchId}失败");
            return StatusCode(500, new
            {
                Success = false,
                Message = ex.Message
            });
        }
    }

    /// <summary>
    /// 获取业务段执行结果
    /// </summary>
    /// <param name="segmentId">业务段ID</param>
    /// <returns></returns>
    [HttpGet("result/{segmentId}")]
    public IActionResult GetSegmentResult(string segmentId)
    {
        try
        {
            var result = _scheduler.GetSegmentResult(segmentId);
            if (result == null)
            {
                return NotFound(new
                {
                    Success = false,
                    Message = "业务段执行结果不存在"
                });
            }

            return Ok(new
            {
                Success = true,
                Data = result
            });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, $"获取业务段{segmentId}执行结果失败");
            return StatusCode(500, new
            {
                Success = false,
                Message = ex.Message
            });
        }
    }

    /// <summary>
    /// 设置结构类型最大并发数
    /// </summary>
    /// <param name="structureType">结构类型</param>
    /// <param name="maxConcurrent">最大并发数</param>
    /// <returns></returns>
    [HttpPost("set-max-concurrent/{structureType}/{maxConcurrent}")]
    public IActionResult SetMaxConcurrent(string structureType, int maxConcurrent)
    {
        try
        {
            _scheduler.SetStructureMaxConcurrent(structureType, maxConcurrent);
            _logger.LogInformation($"结构类型{structureType}最大并发数已设置为{maxConcurrent}");

            return Ok(new
            {
                Success = true,
                Message = $"结构类型{structureType}最大并发数已设置为{maxConcurrent}"
            });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, $"设置结构类型{structureType}最大并发数失败");
            return StatusCode(500, new
            {
                Success = false,
                Message = ex.Message
            });
        }
    }

    /// <summary>
    /// 转换请求模型到业务段参数
    /// </summary>
    private SegmentParams ConvertToSegmentParams(SegmentRequest request)
    {
        return request.SegmentType switch
        {
            "DataQuery" => new DataQuerySegmentParams
            {
                SegmentName = request.SegmentName,
                StructureType = request.StructureType,
                StructureId = request.StructureId,
                Priority = request.Priority ?? 5,
                Timeout = request.Timeout ?? 30,
                RetryCount = request.RetryCount ?? 0,
                AllowSkip = request.AllowSkip ?? false,
                Sql = request.BusinessParams?["Sql"]?.ToString(),
                QueryParams = JsonConvert.DeserializeObject<Dictionary<string, object>>(
                    request.BusinessParams?["QueryParams"]?.ToString() ?? "{}")
            },
            "ApiCall" => new ApiCallSegmentParams
            {
                SegmentName = request.SegmentName,
                StructureType = request.StructureType,
                StructureId = request.StructureId,
                Priority = request.Priority ?? 5,
                Timeout = request.Timeout ?? 30,
                RetryCount = request.RetryCount ?? 0,
                AllowSkip = request.AllowSkip ?? false,
                ApiUrl = request.BusinessParams?["ApiUrl"]?.ToString(),
                HttpMethod = request.BusinessParams?["HttpMethod"]?.ToString() ?? "POST",
                RequestBody = request.BusinessParams?["RequestBody"]?.ToString() ?? "{}",
                Headers = JsonConvert.DeserializeObject<Dictionary<string, string>>(
                    request.BusinessParams?["Headers"]?.ToString() ?? "{}")
            },
            _ => null
        };
    }
}

/// <summary>
/// 业务段请求模型
/// </summary>
public class SegmentRequest
{
    public string SegmentName { get; set; }
    public string SegmentType { get; set; }
    public string StructureType { get; set; }
    public string StructureId { get; set; }
    public int? Priority { get; set; }
    public int? Timeout { get; set; }
    public int? RetryCount { get; set; }
    public bool? AllowSkip { get; set; }
    public Dictionary<string, object> BusinessParams { get; set; } = new Dictionary<string, object>();
}

/// <summary>
/// 批次请求模型
/// </summary>
public class BatchRequest
{
    public string BatchId { get; set; }
    public List<SegmentRequest> Segments { get; set; } = new List<SegmentRequest>();
    public ConcurrentStrategy ConcurrentStrategy { get; set; } = ConcurrentStrategy.Parallel;
}
#endregion

三、使用示例

1. 基础使用流程

csharp

运行

复制代码
// 1. 初始化调度器
var scheduler = MultiStructureConcurrentScheduler.Instance;

// 2. 设置产线结构的最大并发数
scheduler.SetStructureMaxConcurrent("Line", 10);

// 3. 创建业务段参数
var queryParams = new DataQuerySegmentParams
{
    SegmentName = "查询工单信息",
    StructureType = "Line",
    StructureId = "LINE001",
    Priority = 1,
    Timeout = 10,
    RetryCount = 2,
    Sql = "SELECT * FROM WorkOrder WHERE LineCode = @LineCode",
    QueryParams = new Dictionary<string, object> { { "LineCode", "LINE001" } }
};

var apiParams = new ApiCallSegmentParams
{
    SegmentName = "推送工单数据到ERP",
    StructureType = "Line",
    StructureId = "LINE001",
    Priority = 2,
    Timeout = 20,
    RetryCount = 3,
    ApiUrl = "http://erp-system/api/workorder/sync",
    HttpMethod = "POST",
    RequestBody = JsonConvert.SerializeObject(new { LineCode = "LINE001" })
};

// 4. 批量执行
var batchId = Guid.NewGuid().ToString("N");
await scheduler.ExecuteBatchSegmentsAsync(
    batchId,
    new List<SegmentParams> { queryParams, apiParams },
    ConcurrentStrategy.Parallel);

// 5. 获取执行结果
var queryResult = scheduler.GetSegmentResult(queryParams.SegmentId);
var apiResult = scheduler.GetSegmentResult(apiParams.SegmentId);

Console.WriteLine($"查询业务段状态:{queryResult.Status},耗时:{queryResult.ExecutionTimeMs}ms");
Console.WriteLine($"API调用业务段状态:{apiResult.Status},耗时:{apiResult.ExecutionTimeMs}ms");

2. 典型 MES/ERP 应用场景

csharp

运行

复制代码
// 场景:工单完工处理(包含多个独立业务段)
public async Task ProcessWorkOrderCompletion(string workOrderId)
{
    var scheduler = MultiStructureConcurrentScheduler.Instance;
    var batchId = $"WO_{workOrderId}_{DateTime.Now:yyyyMMddHHmmss}";

    // 业务段1:查询工单基本信息
    var queryWorkOrderParams = new DataQuerySegmentParams
    {
        SegmentName = "查询工单基本信息",
        StructureType = "WorkOrder",
        StructureId = workOrderId,
        Priority = 1,
        Sql = "SELECT * FROM WorkOrder WHERE WorkOrderId = @WorkOrderId",
        QueryParams = new Dictionary<string, object> { { "WorkOrderId", workOrderId } }
    };

    // 业务段2:查询工单生产数据
    var queryProductionParams = new DataQuerySegmentParams
    {
        SegmentName = "查询工单生产数据",
        StructureType = "WorkOrder",
        StructureId = workOrderId,
        Priority = 1,
        Sql = "SELECT * FROM ProductionData WHERE WorkOrderId = @WorkOrderId",
        QueryParams = new Dictionary<string, object> { { "WorkOrderId", workOrderId } }
    };

    // 业务段3:更新工单状态
    var updateStatusParams = new DataQuerySegmentParams
    {
        SegmentName = "更新工单状态为已完工",
        StructureType = "WorkOrder",
        StructureId = workOrderId,
        Priority = 2,
        Sql = "UPDATE WorkOrder SET Status = 'Completed', CompleteTime = GETDATE() WHERE WorkOrderId = @WorkOrderId",
        QueryParams = new Dictionary<string, object> { { "WorkOrderId", workOrderId } },
        ReturnData = false
    };

    // 业务段4:推送完工数据到ERP
    var pushToErpParams = new ApiCallSegmentParams
    {
        SegmentName = "推送工单完工数据到ERP",
        StructureType = "WorkOrder",
        StructureId = workOrderId,
        Priority = 3,
        ApiUrl = "http://erp-system/api/workorder/complete",
        RequestBody = JsonConvert.SerializeObject(new { WorkOrderId = workOrderId })
    };

    // 业务段5:生成完工报告
    var generateReportParams = new ApiCallSegmentParams
    {
        SegmentName = "生成工单完工报告",
        StructureType = "WorkOrder",
        StructureId = workOrderId,
        Priority = 3,
        ApiUrl = "http://report-system/api/report/generate",
        RequestBody = JsonConvert.SerializeObject(new { WorkOrderId = workOrderId, ReportType = "Completion" })
    };

    // 批量并行执行(前两个并行,后三个按优先级)
    var segmentList = new List<SegmentParams>
    {
        queryWorkOrderParams,
        queryProductionParams,
        updateStatusParams,
        pushToErpParams,
        generateReportParams
    };

    await scheduler.ExecuteBatchSegmentsAsync(batchId, segmentList, ConcurrentStrategy.Priority);

    // 检查执行结果
    var results = segmentList.ToDictionary(
        s => s.SegmentName, 
        s => scheduler.GetSegmentResult(s.SegmentId).Status);

    foreach (var kvp in results)
    {
        Console.WriteLine($"业务段「{kvp.Key}」执行状态:{kvp.Value}");
    }
}

四、总结

关键点回顾

  1. 原子化业务封装:将 MES/ERP 中繁复琐碎的独立业务语句封装为标准化业务段,支持数据查询、API 调用等多种类型,通过参数化配置减少代码冗余;
  2. 多线程并发执行:基于 TPL 实现业务段的并行执行,支持纯并行、串行、批量、优先级等多种并发策略,显著提升多段独立业务的处理效率;
  3. 多结构并发控制:按产线 / 工单 / 工序等结构类型进行分级并发控制,通过信号量实现资源隔离,避免并发冲突;
  4. 高可靠性设计:内置超时控制、自动重试、失败跳过机制,保证单个业务段失败不影响整体执行;
  5. 全栈组件化方案:包含可视化 WinForm 界面、高性能后台调度引擎、标准化 REST API,支持本地操作和外部系统集成;
  6. 实时监控能力:提供进度更新、状态追踪、执行耗时统计,便于问题定位和性能优化。

该方案完美适配 MES/ERP 场景中多段独立不相关业务的并发处理需求,解决了传统串行执行效率低、代码冗余易出错的问题,同时保证了系统的稳定性和可维护性,可直接应用于工单处理、数据同步、报表生成等典型 MES/ERP 业务场景。

相关推荐
十月南城2 小时前
Flink实时计算心智模型——流、窗口、水位线、状态与Checkpoint的协作
大数据·flink·wpf
谁不学习揍谁!2 小时前
基于python大数据机器学习旅游数据分析可视化推荐系统(完整系统+开发文档+部署教程+文档等资料)
大数据·python·算法·机器学习·数据分析·旅游·数据可视化
AC赳赳老秦2 小时前
DeepSeek多模态Prompt优化:贴合2026技术趋势的精准指令设计方法
大数据·人工智能·自然语言处理·架构·prompt·prometheus·deepseek
2501_944934732 小时前
高职金融大数据应用专业,怎么学习金融数据建模基础?
大数据·学习·金融
海兰2 小时前
Elasticsearch 相关性引擎(ESRE)核心能力
大数据·elasticsearch·搜索引擎
是做服装的同学11 小时前
服装软件ERP系统的基本概念是什么?主要构成有哪些?
大数据·经验分享·其他
heimeiyingwang12 小时前
企业供应链 AI 优化:需求预测与智能调度
大数据·数据库·人工智能·机器学习
Dr.AE17 小时前
AI+教育行业分析报告
大数据·人工智能·教育电商
Evaporator Core18 小时前
通信专业技术资格考试备战系列(一):通信基础知识核心要点解析
大数据·tornado