ERP/MES 系统在物料计划、排程、调拨协同场景下,开发面向 “多端指令 / 业务主题汇总” 的工作台(端台)WinForm 中间件界面组件,核心解决物料领域多业务主题(计划、排程、调拨)、多指令端(ERP 计划端、MES 执行端、仓储端、产线端、采购端)的事项集中展示、指令协同、状态追踪问题,实现物料全流程的一站式管控。以下是完整的设计与实现方案,包含业务主题建模、多端指令协同、WinForm 工作台组件及可直接落地的代码示例。
一、 组件核心定位与架构设计
1. 核心定位(物料计划排程调拨协同)
| 业务主题 | 物料计划(需求 / 供给)、物料排程(产能 / 齐套)、物料调拨(跨厂 / 跨线 / 跨仓) | ERP 物料需求计划 (MRP)、MES 物料齐套检查、仓储调拨执行 |
| 多指令端 | ERP 计划端、MES 执行端、仓储端、产线端、采购端、质控端 | 指令按业务主题分类、多端状态实时同步 |
| 工作台(端台) | 事项汇总(待办 / 在办 / 已办)、指令追踪、异常预警、数据看板、快速操作 | 适配物料协同全流程,支持多维度筛选、一键处理 |
| 核心目标 | 打破物料计划 – 排程 – 调拨的信息孤岛,实现多端指令协同、事项集中管控 | 物料齐套率提升、调拨效率提升、异常响应时效提升 |
2. 整体架构(WinForm 中间件适配)
| 界面层 | WinForm 工作台组件(事项汇总面板、指令追踪列表、异常预警看板、快速操作区) | .NET Framework 4.8 + DevExpress(推荐) |
| 业务逻辑层 | 物料主题聚合服务、多端指令路由服务、事项状态同步服务、异常预警服务 | 观察者模式 + 事件总线 + 规则引擎 |
| 数据访问层 | 物料计划 DAO、物料排程 DAO、物料调拨 DAO、多端指令日志 DAO | SQL Server + Dapper + 读写分离 |
| 基础支撑层 | 事项分类引擎、指令解析器、跨线程工具、缓存组件 | Log4net + MemoryCache + Quartz |
3. 核心数据模型(物料协同 + 多端指令)
csharp
运行
// 物料计划主模型(ERP层面)
public class MaterialPlan
{
/// <summary>
/// 计划编号
/// </summary>
public string PlanNo { get; set; }
/// <summary>
/// 物料编码
/// </summary>
public string MaterialCode { get; set; }
/// <summary>
/// 物料名称
/// </summary>
public string MaterialName { get; set; }
/// <summary>
/// 需求数量
/// </summary>
public decimal ReqQty { get; set; }
/// <summary>
/// 供给数量
/// </summary>
public decimal SupplyQty { get; set; }
/// <summary>
/// 需求日期
/// </summary>
public DateTime ReqDate { get; set; }
/// <summary>
/// 计划状态:待确认/已确认/执行中/已完成/暂停/取消
/// </summary>
public string PlanStatus { get; set; }
/// <summary>
/// 所属工厂/车间
/// </summary>
public string WorkShop { get; set; }
/// <summary>
/// 关联工单/生产订单
/// </summary>
public string RelateOrderNo { get; set; }
/// <summary>
/// 计划类型:MRP计划/手工计划/补料计划
/// </summary>
public string PlanType { get; set; }
}
// 物料排程模型(MES层面)
public class MaterialSchedule
{
/// <summary>
/// 排程ID
/// </summary>
public int ScheduleId { get; set; }
/// <summary>
/// 关联物料计划编号
/// </summary>
public string PlanNo { get; set; }
/// <summary>
/// 物料编码
/// </summary>
public string MaterialCode { get; set; }
/// <summary>
/// 排程数量
/// </summary>
public decimal ScheduleQty { get; set; }
/// <summary>
/// 排程开始时间
/// </summary>
public DateTime ScheduleStartTime { get; set; }
/// <summary>
/// 排程结束时间
/// </summary>
public DateTime ScheduleEndTime { get; set; }
/// <summary>
/// 齐套数量
/// </summary>
public decimal KitQty { get; set; }
/// <summary>
/// 齐套率(%)
/// </summary>
public decimal KitRate { get; set; }
/// <summary>
/// 排程状态:待排程/已排程/齐套检查中/已齐套/缺料/异常
/// </summary>
public string ScheduleStatus { get; set; }
/// <summary>
/// 所属产线
/// </summary>
public string LineCode { get; set; }
}
// 物料调拨模型(仓储/跨组织层面)
public class MaterialTransfer
{
/// <summary>
/// 调拨单号
/// </summary>
public string TransferNo { get; set; }
/// <summary>
/// 物料编码
/// </summary>
public string MaterialCode { get; set; }
/// <summary>
/// 调拨数量
/// </summary>
public decimal TransferQty { get; set; }
/// <summary>
/// 调出仓库/车间
/// </summary>
public string OutWhCode { get; set; }
/// <summary>
/// 调入仓库/车间
/// </summary>
public string InWhCode { get; set; }
/// <summary>
/// 调拨状态:待调拨/调拨中/已完成/已取消/异常
/// </summary>
public string TransferStatus { get; set; }
/// <summary>
/// 调拨类型:跨厂/跨线/跨仓/补料
/// </summary>
public string TransferType { get; set; }
/// <summary>
/// 计划调拨时间
/// </summary>
public DateTime PlanTransferTime { get; set; }
/// <summary>
/// 实际调拨时间
/// </summary>
public DateTime? ActualTransferTime { get; set; }
/// <summary>
/// 关联排程ID
/// </summary>
public int? RelateScheduleId { get; set; }
}
// 多端指令模型(物料协同专用)
public class MaterialCommand
{
/// <summary>
/// 指令ID
/// </summary>
public string CommandId { get; set; }
/// <summary>
/// 指令来源端:ERP_PLAN/MES_EXEC/WH_STORAGE/PRO_LINE/PURCHASE/QUALITY
/// </summary>
public string SourceTerminal { get; set; }
/// <summary>
/// 指令目标端
/// </summary>
public string TargetTerminal { get; set; }
/// <summary>
/// 业务主题:物料计划/物料排程/物料调拨/齐套检查/缺料预警
/// </summary>
public string BusinessTopic { get; set; }
/// <summary>
/// 指令类型:计划确认/排程调整/调拨执行/齐套检查/缺料上报/异常处理
/// </summary>
public string CommandType { get; set; }
/// <summary>
/// 关联单号(计划/排程/调拨单号)
/// </summary>
public string RelateNo { get; set; }
/// <summary>
/// 物料编码
/// </summary>
public string MaterialCode { get; set; }
/// <summary>
/// 指令内容(JSON)
/// </summary>
public string CommandContent { get; set; }
/// <summary>
/// 指令状态:待处理/已接收/已执行/执行失败/已回滚
/// </summary>
public string CommandStatus { get; set; }
/// <summary>
/// 指令优先级:高/中/低
/// </summary>
public string Priority { get; set; }
/// <summary>
/// 指令创建时间
/// </summary>
public DateTime CreateTime { get; set; }
/// <summary>
/// 指令执行时间
/// </summary>
public DateTime? ExecuteTime { get; set; }
/// <summary>
/// 处理人
/// </summary>
public string Handler { get; set; }
}
// 工作台事项模型(聚合多业务主题)
public class WorkbenchItem
{
/// <summary>
/// 事项ID
/// </summary>
public string ItemId { get; set; }
/// <summary>
/// 事项标题
/// </summary>
public string ItemTitle { get; set; }
/// <summary>
/// 业务主题:物料计划/物料排程/物料调拨
/// </summary>
public string BusinessTopic { get; set; }
/// <summary>
/// 事项类型:待办/在办/已办/预警/异常
/// </summary>
public string ItemType { get; set; }
/// <summary>
/// 关联单号
/// </summary>
public string RelateNo { get; set; }
/// <summary>
/// 物料编码
/// </summary>
public string MaterialCode { get; set; }
/// <summary>
/// 物料名称
/// </summary>
public string MaterialName { get; set; }
/// <summary>
/// 数量
/// </summary>
public decimal Qty { get; set; }
/// <summary>
/// 状态
/// </summary>
public string Status { get; set; }
/// <summary>
/// 优先级:高/中/低
/// </summary>
public string Priority { get; set; }
/// <summary>
/// 截止时间
/// </summary>
public DateTime Deadline { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreateTime { get; set; }
/// <summary>
/// 处理状态:未处理/处理中/已处理/无需处理
/// </summary>
public string HandleStatus { get; set; }
/// <summary>
/// 所属指令端
/// </summary>
public string Terminal { get; set; }
}
二、 核心业务组件实现(物料协同 + 工作台聚合)
1. 物料工作台事项聚合服务(核心)
csharp
运行
/// <summary>
/// 物料工作台事项聚合服务(整合计划/排程/调拨多业务主题)
/// </summary>
public class MaterialWorkbenchAggregationService
{
private readonly IDbConnection _dbConn;
private readonly LogHelper _logHelper;
public MaterialWorkbenchAggregationService(string connStr)
{
_dbConn = new SqlConnection(connStr);
_logHelper = new LogHelper();
}
/// <summary>
/// 聚合工作台事项(多业务主题)
/// </summary>
public List<WorkbenchItem> AggregateWorkbenchItems(
string itemType = "",
string businessTopic = "",
string terminal = "",
DateTime? startTime = null,
string materialCode = "")
{
try
{
var items = new List<WorkbenchItem>();
// 1. 聚合物料计划事项
items.AddRange(AggregateMaterialPlanItems(itemType, businessTopic, startTime, materialCode));
// 2. 聚合物料排程事项
items.AddRange(AggregateMaterialScheduleItems(itemType, businessTopic, startTime, materialCode));
// 3. 聚合物料调拨事项
items.AddRange(AggregateMaterialTransferItems(itemType, businessTopic, startTime, materialCode));
// 4. 按终端筛选
if (!string.IsNullOrEmpty(terminal))
{
items = items.Where(i => i.Terminal == terminal).ToList();
}
// 5. 按优先级排序(高→中→低)+ 截止时间
items = items.OrderByDescending(i => i.Priority switch
{
"高" => 3,
"中" => 2,
"低" => 1,
_ => 0
}).ThenBy(i => i.Deadline).ToList();
_logHelper.Info($"工作台事项聚合完成,共聚合{items.Count}条事项");
return items;
}
catch (Exception ex)
{
_logHelper.Error($"工作台事项聚合失败:{ex.Message}", ex);
return new List<WorkbenchItem>();
}
}
/// <summary>
/// 聚合物料计划事项
/// </summary>
private List<WorkbenchItem> AggregateMaterialPlanItems(
string itemType, string businessTopic, DateTime? startTime, string materialCode)
{
var planItems = new List<WorkbenchItem>();
if (string.IsNullOrEmpty(businessTopic) || businessTopic == "物料计划")
{
string sql = "SELECT * FROM MaterialPlan WHERE 1=1 ";
var param = new DynamicParameters();
// 时间筛选
if (startTime.HasValue)
{
sql += "AND ReqDate >= @StartTime ";
param.Add("StartTime", startTime.Value);
}
// 物料筛选
if (!string.IsNullOrEmpty(materialCode))
{
sql += "AND MaterialCode = @MaterialCode ";
param.Add("MaterialCode", materialCode);
}
// 状态筛选(对应事项类型)
if (!string.IsNullOrEmpty(itemType))
{
switch (itemType)
{
case "待办":
sql += "AND PlanStatus IN ('待确认') ";
break;
case "在办":
sql += "AND PlanStatus IN ('已确认', '执行中') ";
break;
case "已办":
sql += "AND PlanStatus IN ('已完成', '取消') ";
break;
case "预警":
sql += "AND PlanStatus IN ('暂停') AND ReqDate < GETDATE() ";
break;
case "异常":
sql += "AND PlanStatus IN ('异常') ";
break;
}
}
var plans = _dbConn.Query<MaterialPlan>(sql, param);
foreach (var plan in plans)
{
planItems.Add(new WorkbenchItem
{
ItemId = $"PLAN_{plan.PlanNo}",
ItemTitle = $"物料计划确认 – {plan.MaterialName}({plan.MaterialCode})",
BusinessTopic = "物料计划",
ItemType = GetPlanItemType(plan.PlanStatus, plan.ReqDate),
RelateNo = plan.PlanNo,
MaterialCode = plan.MaterialCode,
MaterialName = plan.MaterialName,
Qty = plan.ReqQty – plan.SupplyQty,
Status = plan.PlanStatus,
Priority = plan.ReqDate < DateTime.Now ? "高" : "中",
Deadline = plan.ReqDate,
CreateTime = DateTime.Now, // 实际应取计划创建时间
HandleStatus = plan.PlanStatus == "待确认" ? "未处理" : "已处理",
Terminal = "ERP_PLAN"
});
}
}
return planItems;
}
/// <summary>
/// 聚合物料排程事项
/// </summary>
private List<WorkbenchItem> AggregateMaterialScheduleItems(
string itemType, string businessTopic, DateTime? startTime, string materialCode)
{
var scheduleItems = new List<WorkbenchItem>();
if (string.IsNullOrEmpty(businessTopic) || businessTopic == "物料排程")
{
string sql = "SELECT s.*, m.MaterialName FROM MaterialSchedule s " +
"LEFT JOIN MaterialPlan m ON s.PlanNo = m.PlanNo WHERE 1=1 ";
var param = new DynamicParameters();
if (startTime.HasValue)
{
sql += "AND s.ScheduleStartTime >= @StartTime ";
param.Add("StartTime", startTime.Value);
}
if (!string.IsNullOrEmpty(materialCode))
{
sql += "AND s.MaterialCode = @MaterialCode ";
param.Add("MaterialCode", materialCode);
}
if (!string.IsNullOrEmpty(itemType))
{
switch (itemType)
{
case "待办":
sql += "AND s.ScheduleStatus IN ('待排程', '齐套检查中') ";
break;
case "在办":
sql += "AND s.ScheduleStatus IN ('已排程') ";
break;
case "已办":
sql += "AND s.ScheduleStatus IN ('已齐套') ";
break;
case "预警":
sql += "AND s.KitRate < 100 AND s.ScheduleEndTime < GETDATE() ";
break;
case "异常":
sql += "AND s.ScheduleStatus IN ('缺料', '异常') ";
break;
}
}
var schedules = _dbConn.Query(sql, param);
foreach (var schedule in schedules)
{
scheduleItems.Add(new WorkbenchItem
{
ItemId = $"SCHEDULE_{schedule.ScheduleId}",
ItemTitle = $"物料排程齐套检查 – {schedule.MaterialName}({schedule.MaterialCode})",
BusinessTopic = "物料排程",
ItemType = GetScheduleItemType(schedule.ScheduleStatus, schedule.KitRate, schedule.ScheduleEndTime),
RelateNo = schedule.ScheduleId.ToString(),
MaterialCode = schedule.MaterialCode,
MaterialName = schedule.MaterialName,
Qty = schedule.ScheduleQty – schedule.KitQty,
Status = schedule.ScheduleStatus,
Priority = schedule.KitRate < 80 ? "高" : (schedule.KitRate < 100 ? "中" : "低"),
Deadline = schedule.ScheduleEndTime,
CreateTime = DateTime.Now,
HandleStatus = schedule.ScheduleStatus == "待排程" ? "未处理" : "已处理",
Terminal = "MES_EXEC"
});
}
}
return scheduleItems;
}
/// <summary>
/// 聚合物料调拨事项
/// </summary>
private List<WorkbenchItem> AggregateMaterialTransferItems(
string itemType, string businessTopic, DateTime? startTime, string materialCode)
{
var transferItems = new List<WorkbenchItem>();
if (string.IsNullOrEmpty(businessTopic) || businessTopic == "物料调拨")
{
string sql = "SELECT t.*, m.MaterialName FROM MaterialTransfer t " +
"LEFT JOIN MaterialPlan m ON t.MaterialCode = m.MaterialCode WHERE 1=1 ";
var param = new DynamicParameters();
if (startTime.HasValue)
{
sql += "AND t.PlanTransferTime >= @StartTime ";
param.Add("StartTime", startTime.Value);
}
if (!string.IsNullOrEmpty(materialCode))
{
sql += "AND t.MaterialCode = @MaterialCode ";
param.Add("MaterialCode", materialCode);
}
if (!string.IsNullOrEmpty(itemType))
{
switch (itemType)
{
case "待办":
sql += "AND t.TransferStatus IN ('待调拨') ";
break;
case "在办":
sql += "AND t.TransferStatus IN ('调拨中') ";
break;
case "已办":
sql += "AND t.TransferStatus IN ('已完成') ";
break;
case "预警":
sql += "AND t.TransferStatus IN ('待调拨') AND t.PlanTransferTime < GETDATE() ";
break;
case "异常":
sql += "AND t.TransferStatus IN ('异常', '已取消') ";
break;
}
}
var transfers = _dbConn.Query(sql, param);
foreach (var transfer in transfers)
{
transferItems.Add(new WorkbenchItem
{
ItemId = $"TRANSFER_{transfer.TransferNo}",
ItemTitle = $"物料调拨执行 – {transfer.MaterialName}({transfer.MaterialCode})",
BusinessTopic = "物料调拨",
ItemType = GetTransferItemType(transfer.TransferStatus, transfer.PlanTransferTime),
RelateNo = transfer.TransferNo,
MaterialCode = transfer.MaterialCode,
MaterialName = transfer.MaterialName,
Qty = transfer.TransferQty,
Status = transfer.TransferStatus,
Priority = transfer.PlanTransferTime < DateTime.Now ? "高" : "中",
Deadline = transfer.PlanTransferTime,
CreateTime = DateTime.Now,
HandleStatus = transfer.TransferStatus == "待调拨" ? "未处理" : "已处理",
Terminal = "WH_STORAGE"
});
}
}
return transferItems;
}
/// <summary>
/// 获取计划事项类型
/// </summary>
private string GetPlanItemType(string planStatus, DateTime reqDate)
{
return planStatus switch
{
"待确认" => "待办",
"已确认" or "执行中" => "在办",
"已完成" or "取消" => "已办",
"暂停" when reqDate < DateTime.Now => "预警",
"异常" => "异常",
_ => "其他"
};
}
/// <summary>
/// 获取排程事项类型
/// </summary>
private string GetScheduleItemType(string scheduleStatus, decimal kitRate, DateTime scheduleEndTime)
{
if (scheduleStatus == "缺料" || scheduleStatus == "异常") return "异常";
if (kitRate < 100 && scheduleEndTime < DateTime.Now) return "预警";
return scheduleStatus switch
{
"待排程" or "齐套检查中" => "待办",
"已排程" => "在办",
"已齐套" => "已办",
_ => "其他"
};
}
/// <summary>
/// 获取调拨事项类型
/// </summary>
private string GetTransferItemType(string transferStatus, DateTime planTransferTime)
{
if (transferStatus == "异常" || transferStatus == "已取消") return "异常";
if (transferStatus == "待调拨" && planTransferTime < DateTime.Now) return "预警";
return transferStatus switch
{
"待调拨" => "待办",
"调拨中" => "在办",
"已完成" => "已办",
_ => "其他"
};
}
/// <summary>
/// 获取工作台统计数据(适配看板)
/// </summary>
public Dictionary<string, int> GetWorkbenchStatistics()
{
var stats = new Dictionary<string, int>();
// 按事项类型统计
var allItems = AggregateWorkbenchItems();
stats["待办事项"] = allItems.Count(i => i.ItemType == "待办");
stats["在办事项"] = allItems.Count(i => i.ItemType == "在办");
stats["已办事项"] = allItems.Count(i => i.ItemType == "已办");
stats["预警事项"] = allItems.Count(i => i.ItemType == "预警");
stats["异常事项"] = allItems.Count(i => i.ItemType == "异常");
// 按业务主题统计
stats["物料计划"] = allItems.Count(i => i.BusinessTopic == "物料计划");
stats["物料排程"] = allItems.Count(i => i.BusinessTopic == "物料排程");
stats["物料调拨"] = allItems.Count(i => i.BusinessTopic == "物料调拨");
// 按优先级统计
stats["高优先级"] = allItems.Count(i => i.Priority == "高");
stats["中优先级"] = allItems.Count(i => i.Priority == "中");
stats["低优先级"] = allItems.Count(i => i.Priority == "低");
return stats;
}
/// <summary>
/// 更新事项处理状态
/// </summary>
public bool UpdateItemHandleStatus(string itemId, string handleStatus, string handler)
{
try
{
// 解析事项类型和关联单号
var parts = itemId.Split('_');
if (parts.Length < 2) return false;
var itemType = parts[0];
var relateNo = parts[1];
// 根据事项类型更新对应业务表状态
switch (itemType)
{
case "PLAN":
// 更新物料计划状态
_dbConn.Execute(
"UPDATE MaterialPlan SET PlanStatus = CASE WHEN @HandleStatus='已处理' THEN '已确认' ELSE PlanStatus END " +
"WHERE PlanNo = @RelateNo",
new { HandleStatus = handleStatus, RelateNo = relateNo });
break;
case "SCHEDULE":
// 更新物料排程状态
_dbConn.Execute(
"UPDATE MaterialSchedule SET ScheduleStatus = CASE WHEN @HandleStatus='已处理' THEN '齐套检查中' ELSE ScheduleStatus END " +
"WHERE ScheduleId = @RelateNo",
new { HandleStatus = handleStatus, RelateNo = int.Parse(relateNo) });
break;
case "TRANSFER":
// 更新物料调拨状态
_dbConn.Execute(
"UPDATE MaterialTransfer SET TransferStatus = CASE WHEN @HandleStatus='已处理' THEN '调拨中' ELSE TransferStatus END " +
"WHERE TransferNo = @RelateNo",
new { HandleStatus = handleStatus, RelateNo = relateNo });
break;
}
_logHelper.Info($"工作台事项{itemId}处理状态已更新为{handleStatus},处理人:{handler}");
return true;
}
catch (Exception ex)
{
_logHelper.Error($"更新事项处理状态失败:{ex.Message}", ex);
return false;
}
}
}
// 通用日志辅助类
public static class LogHelper
{
private static readonly ILog _log = LogManager.GetLogger(typeof(LogHelper));
public static void Info(string message) => _log.Info(message);
public static void Warn(string message) => _log.Warn(message);
public static void Error(string message) => _log.Error(message);
public static void Error(string message, Exception ex) => _log.Error(message, ex);
}
2. 多端指令调度服务(物料协同专用)
csharp
运行
/// <summary>
/// 物料协同多端指令调度服务(事件总线+队列模式)
/// </summary>
public class MaterialMultiTerminalDispatcher
{
private readonly IDbConnection _dbConn;
private readonly LogHelper _logHelper;
private readonly Queue<MaterialCommand> _commandQueue = new Queue<MaterialCommand>();
private readonly Thread _commandProcessThread;
private readonly object _queueLock = new object();
// 指令事件(WinForm工作台订阅)
public event EventHandler<MaterialCommandEventArgs> CommandProcessed;
// 注册的指令端
private readonly List<string> _registeredTerminals = new List<string>
{
"ERP_PLAN", // ERP计划端
"MES_EXEC", // MES执行端
"WH_STORAGE", // 仓储端
"PRO_LINE", // 产线端
"PURCHASE", // 采购端
"QUALITY" // 质控端
};
public MaterialMultiTerminalDispatcher(string connStr)
{
_dbConn = new SqlConnection(connStr);
_logHelper = new LogHelper();
// 启动指令处理线程
_commandProcessThread = new Thread(ProcessCommandQueue) { IsBackground = true };
_commandProcessThread.Start();
}
/// <summary>
/// 提交物料协同指令
/// </summary>
public bool SubmitCommand(MaterialCommand command)
{
try
{
// 校验指令端
if (!_registeredTerminals.Contains(command.SourceTerminal) ||
!_registeredTerminals.Contains(command.TargetTerminal))
{
_logHelper.Warn($"指令端未注册:来源={command.SourceTerminal},目标={command.TargetTerminal}");
return false;
}
// 初始化指令信息
command.CommandId = Guid.NewGuid().ToString("N");
command.CreateTime = DateTime.Now;
command.CommandStatus = "待处理";
command.Handler = Environment.UserName;
// 加入队列
lock (_queueLock)
{
_commandQueue.Enqueue(command);
}
// 保存指令日志
SaveCommandLog(command);
_logHelper.Info($"物料协同指令{command.CommandId}已提交,业务主题:{command.BusinessTopic},物料:{command.MaterialCode}");
return true;
}
catch (Exception ex)
{
_logHelper.Error($"提交指令失败:{ex.Message}", ex);
return false;
}
}
/// <summary>
/// 处理指令队列
/// </summary>
private void ProcessCommandQueue()
{
while (true)
{
MaterialCommand command = null;
// 从队列取出指令
lock (_queueLock)
{
if (_commandQueue.Count > 0)
{
command = _commandQueue.Dequeue();
}
}
if (command != null)
{
try
{
// 执行指令
ExecuteCommand(command);
command.CommandStatus = "已执行";
command.ExecuteTime = DateTime.Now;
_logHelper.Info($"指令{command.CommandId}执行成功");
}
catch (Exception ex)
{
command.CommandStatus = "执行失败";
command.ExecuteTime = DateTime.Now;
command.CommandContent += $"||执行失败原因:{ex.Message}";
_logHelper.Error($"执行指令{command.CommandId}失败:{ex.Message}", ex);
}
finally
{
// 更新指令日志
UpdateCommandLog(command);
// 触发界面更新事件
OnCommandProcessed(new MaterialCommandEventArgs(command));
}
}
else
{
Thread.Sleep(1000); // 无指令时休眠
}
}
}
/// <summary>
/// 执行具体指令
/// </summary>
private void ExecuteCommand(MaterialCommand command)
{
switch (command.CommandType)
{
case "计划确认":
ExecutePlanConfirmCommand(command);
break;
case "排程调整":
ExecuteScheduleAdjustCommand(command);
break;
case "调拨执行":
ExecuteTransferExecuteCommand(command);
break;
case "齐套检查":
ExecuteKitCheckCommand(command);
break;
case "缺料上报":
ExecuteShortageReportCommand(command);
break;
case "异常处理":
ExecuteExceptionHandleCommand(command);
break;
default:
throw new NotSupportedException($"不支持的指令类型:{command.CommandType}");
}
}
#region 指令执行具体实现
/// <summary>
/// 执行计划确认指令
/// </summary>
private void ExecutePlanConfirmCommand(MaterialCommand command)
{
var cmdContent = JsonConvert.DeserializeObject<PlanConfirmContent>(command.CommandContent);
// 更新物料计划状态
_dbConn.Execute(
"UPDATE MaterialPlan SET PlanStatus='已确认', SupplyQty=@SupplyQty WHERE PlanNo=@PlanNo",
new { cmdContent.SupplyQty, cmdContent.PlanNo });
// 自动创建排程任务
var plan = _dbConn.QuerySingle<MaterialPlan>(
"SELECT * FROM MaterialPlan WHERE PlanNo = @PlanNo",
new { cmdContent.PlanNo });
_dbConn.Execute(
"INSERT INTO MaterialSchedule (PlanNo, MaterialCode, ScheduleQty, ScheduleStartTime, ScheduleEndTime, " +
"KitQty, KitRate, ScheduleStatus, LineCode) " +
"VALUES (@PlanNo, @MaterialCode, @ScheduleQty, @ScheduleStartTime, @ScheduleEndTime, 0, 0, '待排程', @LineCode)",
new
{
plan.PlanNo,
plan.MaterialCode,
ScheduleQty = plan.ReqQty,
ScheduleStartTime = DateTime.Now,
ScheduleEndTime = plan.ReqDate.AddHours(-2),
LineCode = cmdContent.LineCode
});
}
/// <summary>
/// 执行排程调整指令
/// </summary>
private void ExecuteScheduleAdjustCommand(MaterialCommand command)
{
var cmdContent = JsonConvert.DeserializeObject<ScheduleAdjustContent>(command.CommandContent);
_dbConn.Execute(
"UPDATE MaterialSchedule SET ScheduleQty=@NewQty, ScheduleStartTime=@NewStartTime, " +
"ScheduleEndTime=@NewEndTime WHERE ScheduleId=@ScheduleId",
new { cmdContent.NewQty, cmdContent.NewStartTime, cmdContent.NewEndTime, cmdContent.ScheduleId });
}
/// <summary>
/// 执行调拨执行指令
/// </summary>
private void ExecuteTransferExecuteCommand(MaterialCommand command)
{
var cmdContent = JsonConvert.DeserializeObject<TransferExecuteContent>(command.CommandContent);
// 更新调拨状态
_dbConn.Execute(
"UPDATE MaterialTransfer SET TransferStatus='调拨中', ActualTransferTime=GETDATE() WHERE TransferNo=@TransferNo",
new { cmdContent.TransferNo });
// 执行库存扣减/增加(简化版)
// 实际场景需对接WMS系统
_logHelper.Info($"执行库存调拨:{cmdContent.OutWhCode}→{cmdContent.InWhCode},物料{command.MaterialCode},数量{cmdContent.TransferQty}");
}
/// <summary>
/// 执行齐套检查指令
/// </summary>
private void ExecuteKitCheckCommand(MaterialCommand command)
{
var cmdContent = JsonConvert.DeserializeObject<KitCheckContent>(command.CommandContent);
// 查询当前库存
var stockQty = _dbConn.QuerySingle<decimal>(
"SELECT ISNULL(SUM(StockQty),0) FROM MaterialStock WHERE MaterialCode = @MaterialCode AND WhCode = @WhCode",
new { command.MaterialCode, cmdContent.WhCode });
// 更新齐套信息
var schedule = _dbConn.QuerySingle<MaterialSchedule>(
"SELECT * FROM MaterialSchedule WHERE ScheduleId = @ScheduleId",
new { cmdContent.ScheduleId });
decimal kitRate = stockQty >= schedule.ScheduleQty ? 100 : (stockQty / schedule.ScheduleQty) * 100;
_dbConn.Execute(
"UPDATE MaterialSchedule SET KitQty=@KitQty, KitRate=@KitRate, " +
"ScheduleStatus=CASE WHEN @KitRate=100 THEN '已齐套' ELSE '缺料' END " +
"WHERE ScheduleId=@ScheduleId",
new { KitQty = stockQty, KitRate = kitRate, cmdContent.ScheduleId });
}
/// <summary>
/// 执行缺料上报指令
/// </summary>
private void ExecuteShortageReportCommand(MaterialCommand command)
{
var cmdContent = JsonConvert.DeserializeObject<ShortageReportContent>(command.CommandContent);
// 记录缺料信息
_dbConn.Execute(
"INSERT INTO MaterialShortageLog (MaterialCode, ShortageQty, ReportTime, Reporter, RelateScheduleId, Status) " +
"VALUES (@MaterialCode, @ShortageQty, GETDATE(), @Reporter, @RelateScheduleId, '待处理')",
new { command.MaterialCode, cmdContent.ShortageQty, cmdContent.Reporter, cmdContent.RelateScheduleId });
// 更新排程状态
_dbConn.Execute(
"UPDATE MaterialSchedule SET ScheduleStatus='缺料' WHERE ScheduleId=@ScheduleId",
new { cmdContent.RelateScheduleId });
}
/// <summary>
/// 执行异常处理指令
/// </summary>
private void ExecuteExceptionHandleCommand(MaterialCommand command)
{
var cmdContent = JsonConvert.DeserializeObject<ExceptionHandleContent>(command.CommandContent);
// 更新对应业务状态
switch (cmdContent.BusinessType)
{
case "物料计划":
_dbConn.Execute(
"UPDATE MaterialPlan SET PlanStatus='已完成' WHERE PlanNo=@RelateNo",
new { cmdContent.RelateNo });
break;
case "物料排程":
_dbConn.Execute(
"UPDATE MaterialSchedule SET ScheduleStatus='已齐套' WHERE ScheduleId=@RelateNo",
new { RelateNo = int.Parse(cmdContent.RelateNo) });
break;
case "物料调拨":
_dbConn.Execute(
"UPDATE MaterialTransfer SET TransferStatus='已完成' WHERE TransferNo=@RelateNo",
new { cmdContent.RelateNo });
break;
}
// 记录异常处理日志
_dbConn.Execute(
"INSERT INTO MaterialExceptionLog (RelateNo, BusinessType, ExceptionDesc, HandleDesc, HandleTime, Handler) " +
"VALUES (@RelateNo, @BusinessType, @ExceptionDesc, @HandleDesc, GETDATE(), @Handler)",
new { cmdContent.RelateNo, cmdContent.BusinessType, cmdContent.ExceptionDesc, cmdContent.HandleDesc, command.Handler });
}
#endregion
/// <summary>
/// 保存指令日志
/// </summary>
private void SaveCommandLog(MaterialCommand command)
{
_dbConn.Execute(
"INSERT INTO MaterialCommandLog (CommandId, SourceTerminal, TargetTerminal, BusinessTopic, " +
"CommandType, RelateNo, MaterialCode, CommandContent, CommandStatus, Priority, " +
"CreateTime, ExecuteTime, Handler) " +
"VALUES (@CommandId, @SourceTerminal, @TargetTerminal, @BusinessTopic, " +
"@CommandType, @RelateNo, @MaterialCode, @CommandContent, @CommandStatus, @Priority, " +
"@CreateTime, @ExecuteTime, @Handler)",
command);
}
/// <summary>
/// 更新指令日志
/// </summary>
private void UpdateCommandLog(MaterialCommand command)
{
_dbConn.Execute(
"UPDATE MaterialCommandLog SET CommandStatus = @CommandStatus, ExecuteTime = @ExecuteTime, " +
"CommandContent = @CommandContent, Handler = @Handler WHERE CommandId = @CommandId",
new { command.CommandStatus, command.ExecuteTime, command.CommandContent, command.Handler, command.CommandId });
}
/// <summary>
/// 获取指令汇总(适配WinForm展示)
/// </summary>
public DataTable GetCommandSummary(
DateTime startTime,
string businessTopic = "",
string commandType = "",
string materialCode = "")
{
var dt = new DataTable();
dt.Columns.Add("指令ID", typeof(string));
dt.Columns.Add("业务主题", typeof(string));
dt.Columns.Add("指令类型", typeof(string));
dt.Columns.Add("来源端", typeof(string));
dt.Columns.Add("目标端", typeof(string));
dt.Columns.Add("物料编码", typeof(string));
dt.Columns.Add("关联单号", typeof(string));
dt.Columns.Add("指令状态", typeof(string));
dt.Columns.Add("优先级", typeof(string));
dt.Columns.Add("创建时间", typeof(DateTime));
dt.Columns.Add("处理人", typeof(string));
string sql = "SELECT CommandId, BusinessTopic, CommandType, SourceTerminal, TargetTerminal, " +
"MaterialCode, RelateNo, CommandStatus, Priority, CreateTime, Handler " +
"FROM MaterialCommandLog WHERE CreateTime >= @StartTime ";
var param = new DynamicParameters();
param.Add("StartTime", startTime);
if (!string.IsNullOrEmpty(businessTopic))
{
sql += "AND BusinessTopic = @BusinessTopic ";
param.Add("BusinessTopic", businessTopic);
}
if (!string.IsNullOrEmpty(commandType))
{
sql += "AND CommandType = @CommandType ";
param.Add("CommandType", commandType);
}
if (!string.IsNullOrEmpty(materialCode))
{
sql += "AND MaterialCode = @MaterialCode ";
param.Add("MaterialCode", materialCode);
}
sql += "ORDER BY CreateTime DESC";
var logs = _dbConn.Query(sql, param);
foreach (var log in logs)
{
dt.Rows.Add(
log.CommandId,
log.BusinessTopic,
log.CommandType,
log.SourceTerminal,
log.TargetTerminal,
log.MaterialCode,
log.RelateNo,
log.CommandStatus,
log.Priority,
log.CreateTime,
log.Handler
);
}
return dt;
}
/// <summary>
/// 触发指令处理事件
/// </summary>
protected virtual void OnCommandProcessed(MaterialCommandEventArgs e)
{
CommandProcessed?.Invoke(this, e);
}
}
// 指令事件参数
public class MaterialCommandEventArgs : EventArgs
{
public MaterialCommand Command { get; set; }
public MaterialCommandEventArgs(MaterialCommand command)
{
Command = command;
}
}
// 指令内容DTO
public class PlanConfirmContent { public string PlanNo { get; set; } public decimal SupplyQty { get; set; } public string LineCode { get; set; } }
public class ScheduleAdjustContent { public int ScheduleId { get; set; } public decimal NewQty { get; set; } public DateTime NewStartTime { get; set; } public DateTime NewEndTime { get; set; } }
public class TransferExecuteContent { public string TransferNo { get; set; } public string OutWhCode { get; set; } public string InWhCode { get; set; } public decimal TransferQty { get; set; } }
public class KitCheckContent { public int ScheduleId { get; set; } public string WhCode { get; set; } }
public class ShortageReportContent { public int RelateScheduleId { get; set; } public decimal ShortageQty { get; set; } public string Reporter { get; set; } }
public class ExceptionHandleContent { public string RelateNo { get; set; } public string BusinessType { get; set; } public string ExceptionDesc { get; set; } public string HandleDesc { get; set; } }
三、 WinForm 工作台界面组件实现(核心界面)
1. 物料协同工作台主界面(核心)
csharp
运行
public partial class MaterialWorkbenchMainForm : Form
{
private readonly MaterialWorkbenchAggregationService _aggregationService;
private readonly MaterialMultiTerminalDispatcher _dispatcher;
private readonly string _connStr;
public MaterialWorkbenchMainForm()
{
InitializeComponent();
_connStr = ConfigHelper.GetConnStr();
_aggregationService = new MaterialWorkbenchAggregationService(_connStr);
_dispatcher = new MaterialMultiTerminalDispatcher(_connStr);
_dispatcher.CommandProcessed += OnCommandProcessed;
// 初始化筛选条件
InitFilterConditions();
// 加载工作台数据
LoadWorkbenchData();
// 加载数据看板
LoadWorkbenchDashboard();
}
/// <summary>
/// 初始化筛选条件
/// </summary>
private void InitFilterConditions()
{
// 事项类型
cboItemType.Items.AddRange(new string[] { "全部", "待办", "在办", "已办", "预警", "异常" });
cboItemType.SelectedIndex = 0;
// 业务主题
cboBusinessTopic.Items.AddRange(new string[] { "全部", "物料计划", "物料排程", "物料调拨" });
cboBusinessTopic.SelectedIndex = 0;
// 指令端
cboTerminal.Items.AddRange(new string[] { "全部", "ERP_PLAN", "MES_EXEC", "WH_STORAGE", "PRO_LINE", "PURCHASE", "QUALITY" });
cboTerminal.SelectedIndex = 0;
// 时间范围
dtpStartTime.Value = DateTime.Today.AddDays(-7);
dtpEndTime.Value = DateTime.Today;
// 优先级
cboPriority.Items.AddRange(new string[] { "全部", "高", "中", "低" });
cboPriority.SelectedIndex = 0;
}
/// <summary>
/// 加载工作台数据
/// </summary>
private void LoadWorkbenchData()
{
// 获取筛选条件
var itemType = cboItemType.Text == "全部" ? "" : cboItemType.Text;
var businessTopic = cboBusinessTopic.Text == "全部" ? "" : cboBusinessTopic.Text;
var terminal = cboTerminal.Text == "全部" ? "" : cboTerminal.Text;
var materialCode = txtMaterialCode.Text.Trim();
// 聚合事项数据
var items = _aggregationService.AggregateWorkbenchItems(
itemType, businessTopic, terminal, dtpStartTime.Value, materialCode);
// 按优先级二次筛选
if (cboPriority.Text != "全部")
{
items = items.Where(i => i.Priority == cboPriority.Text).ToList();
}
// 绑定到DataGridView
dgvWorkbenchItems.DataSource = items;
// 设置行样式
SetWorkbenchItemRowStyle();
// 更新统计信息
lblTotalCount.Text = $"总计:{items.Count} 条";
lblTodoCount.Text = $"待办:{items.Count(i => i.ItemType == "待办")} 条";
lblWarningCount.Text = $"预警:{items.Count(i => i.ItemType == "预警")} 条";
lblExceptionCount.Text = $"异常:{items.Count(i => i.ItemType == "异常")} 条";
}
/// <summary>
/// 设置工作台事项行样式
/// </summary>
private void SetWorkbenchItemRowStyle()
{
foreach (DataGridViewRow row in dgvWorkbenchItems.Rows)
{
if (row.IsNewRow) continue;
var itemType = row.Cells["ItemType"].Value.ToString();
var priority = row.Cells["Priority"].Value.ToString();
// 按事项类型设置背景色
switch (itemType)
{
case "待办":
row.DefaultCellStyle.BackColor = Color.LightYellow;
break;
case "预警":
row.DefaultCellStyle.BackColor = Color.Orange;
row.DefaultCellStyle.ForeColor = Color.White;
break;
case "异常":
row.DefaultCellStyle.BackColor = Color.LightCoral;
row.DefaultCellStyle.ForeColor = Color.White;
break;
case "已办":
row.DefaultCellStyle.BackColor = Color.LightGray;
break;
}
// 高优先级加红色边框
if (priority == "高")
{
row.DefaultCellStyle.BorderWidth = 2;
row.DefaultCellStyle.BorderStyle = DataGridViewBorderStyle.Single;
}
}
}
/// <summary>
/// 加载工作台数据看板
/// </summary>
private void LoadWorkbenchDashboard()
{
var stats = _aggregationService.GetWorkbenchStatistics();
// 1. 事项类型分布饼图
chartItemType.Series.Clear();
var itemTypeSeries = new Series("事项类型分布", SeriesChartType.Pie);
itemTypeSeries.Points.Add(new DataPoint("待办", stats["待办事项"]));
itemTypeSeries.Points.Add(new DataPoint("在办", stats["在办事项"]));
itemTypeSeries.Points.Add(new DataPoint("已办", stats["已办事项"]));
itemTypeSeries.Points.Add(new DataPoint("预警", stats["预警事项"]));
itemTypeSeries.Points.Add(new DataPoint("异常", stats["异常事项"]));
chartItemType.Series.Add(itemTypeSeries);
// 2. 业务主题分布柱状图
chartBusinessTopic.Series.Clear();
var topicSeries = new Series("业务主题分布", SeriesChartType.Column);
topicSeries.Points.Add(new DataPoint("物料计划", stats["物料计划"]));
topicSeries.Points.Add(new DataPoint("物料排程", stats["物料排程"]));
topicSeries.Points.Add(new DataPoint("物料调拨", stats["物料调拨"]));
chartBusinessTopic.Series.Add(topicSeries);
// 3. 优先级分布
chartPriority.Series.Clear();
var prioritySeries = new Series("优先级分布", SeriesChartType.Bar);
prioritySeries.Points.Add(new DataPoint("高", stats["高优先级"]));
prioritySeries.Points.Add(new DataPoint("中", stats["中优先级"]));
prioritySeries.Points.Add(new DataPoint("低", stats["低优先级"]));
chartPriority.Series.Add(prioritySeries);
}
/// <summary>
/// 筛选条件变更
/// </summary>
private void cboItemType_SelectedIndexChanged(object sender, EventArgs e)
{
LoadWorkbenchData();
}
private void cboBusinessTopic_SelectedIndexChanged(object sender, EventArgs e)
{
LoadWorkbenchData();
}
private void cboTerminal_SelectedIndexChanged(object sender, EventArgs e)
{
LoadWorkbenchData();
}
private void cboPriority_SelectedIndexChanged(object sender, EventArgs e)
{
LoadWorkbenchData();
}
private void dtpStartTime_ValueChanged(object sender, EventArgs e)
{
LoadWorkbenchData();
}
private void btnSearch_Click(object sender, EventArgs e)
{
LoadWorkbenchData();
LoadWorkbenchDashboard();
}
/// <summary>
/// 处理选中事项
/// </summary>
private void btnHandleItem_Click(object sender, EventArgs e)
{
if (dgvWorkbenchItems.SelectedRows.Count == 0)
{
MessageBox.Show("请选择要处理的事项");
return;
}
var selectedRow = dgvWorkbenchItems.SelectedRows[0];
var itemId = selectedRow.Cells["ItemId"].Value.ToString();
var businessTopic = selectedRow.Cells["BusinessTopic"].Value.ToString();
var relateNo = selectedRow.Cells["RelateNo"].Value.ToString();
var materialCode = selectedRow.Cells["MaterialCode"].Value.ToString();
// 根据业务主题打开处理窗体
switch (businessTopic)
{
case "物料计划":
var planForm = new MaterialPlanHandleForm(relateNo, _dispatcher);
planForm.ShowDialog();
break;
case "物料排程":
var scheduleForm = new MaterialScheduleHandleForm(int.Parse(relateNo), _dispatcher);
scheduleForm.ShowDialog();
break;
case "物料调拨":
var transferForm = new MaterialTransferHandleForm(relateNo, _dispatcher);
transferForm.ShowDialog();
break;
}
// 更新事项处理状态
_aggregationService.UpdateItemHandleStatus(itemId, "已处理", Environment.UserName);
// 刷新数据
LoadWorkbenchData();
LoadWorkbenchDashboard();
}
/// <summary>
/// 指令处理事件(刷新界面)
/// </summary>
private void OnCommandProcessed(object sender, MaterialCommandEventArgs e)
{
Invoke(new Action(() =>
{
LoadWorkbenchData();
LoadWorkbenchDashboard();
// 刷新指令追踪标签页
if (tabControl1.SelectedTab == tabPageCommandTrack)
{
LoadCommandTrackingData();
}
}));
}
/// <summary>
/// 加载指令追踪数据
/// </summary>
private void LoadCommandTrackingData()
{
var businessTopic = cboTrackBusinessTopic.Text == "全部" ? "" : cboTrackBusinessTopic.Text;
var commandType = cboTrackCommandType.Text == "全部" ? "" : cboTrackCommandType.Text;
var materialCode = txtTrackMaterialCode.Text.Trim();
dgvCommandTrack.DataSource = _dispatcher.GetCommandSummary(
dtpTrackStartTime.Value, businessTopic, commandType, materialCode);
// 失败指令标红
foreach (DataGridViewRow row in dgvCommandTrack.Rows)
{
if (row.Cells["指令状态"].Value.ToString() == "执行失败")
{
row.DefaultCellStyle.BackColor = Color.LightPink;
row.DefaultCellStyle.ForeColor = Color.Red;
}
}
}
/// <summary>
/// 指令追踪筛选
/// </summary>
private void btnTrackSearch_Click(object sender, EventArgs e)
{
LoadCommandTrackingData();
}
/// <summary>
/// 切换标签页
/// </summary>
private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
{
if (tabControl1.SelectedTab == tabPageCommandTrack)
{
LoadCommandTrackingData();
}
}
}
// 物料计划处理窗体
public partial class MaterialPlanHandleForm : Form
{
private readonly string _planNo;
private readonly MaterialMultiTerminalDispatcher _dispatcher;
public MaterialPlanHandleForm(string planNo, MaterialMultiTerminalDispatcher dispatcher)
{
InitializeComponent();
_planNo = planNo;
_dispatcher = dispatcher;
// 加载计划信息
LoadPlanInfo();
}
private void LoadPlanInfo()
{
using (var conn = new SqlConnection(ConfigHelper.GetConnStr()))
{
var plan = conn.QuerySingle<MaterialPlan>(
"SELECT * FROM MaterialPlan WHERE PlanNo = @PlanNo",
new { _planNo });
lblPlanNo.Text = plan.PlanNo;
lblMaterialCode.Text = plan.MaterialCode;
lblMaterialName.Text = plan.MaterialName;
lblReqQty.Text = plan.ReqQty.ToString();
lblSupplyQty.Text = plan.SupplyQty.ToString();
lblReqDate.Text = plan.ReqDate.ToString("yyyy-MM-dd");
lblPlanStatus.Text = plan.PlanStatus;
txtSupplyQty.Text = plan.SupplyQty.ToString();
cboLineCode.SelectedValue = plan.RelateOrderNo.Split('_')[0]; // 简化处理
}
}
private void btnConfirmPlan_Click(object sender, EventArgs e)
{
// 提交计划确认指令
var result = _dispatcher.SubmitCommand(new MaterialCommand
{
SourceTerminal = "ERP_PLAN",
TargetTerminal = "MES_EXEC",
BusinessTopic = "物料计划",
CommandType = "计划确认",
RelateNo = _planNo,
MaterialCode = lblMaterialCode.Text,
CommandContent = JsonConvert.SerializeObject(new PlanConfirmContent
{
PlanNo = _planNo,
SupplyQty = decimal.Parse(txtSupplyQty.Text),
LineCode = cboLineCode.SelectedValue.ToString()
}),
Priority = "高"
});
if (result)
{
MessageBox.Show("物料计划已确认,自动创建排程任务");
Close();
}
else
{
MessageBox.Show("计划确认失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
// 物料排程处理窗体
public partial class MaterialScheduleHandleForm : Form
{
private readonly int _scheduleId;
private readonly MaterialMultiTerminalDispatcher _dispatcher;
public MaterialScheduleHandleForm(int scheduleId, MaterialMultiTerminalDispatcher dispatcher)
{
InitializeComponent();
_scheduleId = scheduleId;
_dispatcher = dispatcher;
LoadScheduleInfo();
}
private void LoadScheduleInfo()
{
using (var conn = new SqlConnection(ConfigHelper.GetConnStr()))
{
var schedule = conn.QuerySingle<MaterialSchedule>(
"SELECT * FROM MaterialSchedule WHERE ScheduleId = @ScheduleId",
new { _scheduleId });
lblScheduleId.Text = schedule.ScheduleId.ToString();
lblPlanNo.Text = schedule.PlanNo;
lblMaterialCode.Text = schedule.MaterialCode;
lblScheduleQty.Text = schedule.ScheduleQty.ToString();
lblKitQty.Text = schedule.KitQty.ToString();
lblKitRate.Text = $"{schedule.KitRate}%";
lblScheduleStatus.Text = schedule.ScheduleStatus;
txtNewQty.Text = schedule.ScheduleQty.ToString();
dtpNewStartTime.Value = schedule.ScheduleStartTime;
dtpNewEndTime.Value = schedule.ScheduleEndTime;
}
}
private void btnAdjustSchedule_Click(object sender, EventArgs e)
{
// 提交排程调整指令
var result = _dispatcher.SubmitCommand(new MaterialCommand
{
SourceTerminal = "MES_EXEC",
TargetTerminal = "MES_EXEC",
BusinessTopic = "物料排程",
CommandType = "排程调整",
RelateNo = _scheduleId.ToString(),
MaterialCode = lblMaterialCode.Text,
CommandContent = JsonConvert.SerializeObject(new ScheduleAdjustContent
{
ScheduleId = _scheduleId,
NewQty = decimal.Parse(txtNewQty.Text),
NewStartTime = dtpNewStartTime.Value,
NewEndTime = dtpNewEndTime.Value
}),
Priority = "中"
});
if (result)
{
MessageBox.Show("排程调整指令已提交");
Close();
}
else
{
MessageBox.Show("排程调整失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void btnKitCheck_Click(object sender, EventArgs e)
{
// 提交齐套检查指令
var result = _dispatcher.SubmitCommand(new MaterialCommand
{
SourceTerminal = "MES_EXEC",
TargetTerminal = "WH_STORAGE",
BusinessTopic = "物料排程",
CommandType = "齐套检查",
RelateNo = _scheduleId.ToString(),
MaterialCode = lblMaterialCode.Text,
CommandContent = JsonConvert.SerializeObject(new KitCheckContent
{
ScheduleId = _scheduleId,
WhCode = cboWhCode.SelectedValue.ToString()
}),
Priority = "高"
});
if (result)
{
MessageBox.Show("齐套检查指令已提交,结果将同步更新");
LoadScheduleInfo();
}
else
{
MessageBox.Show("齐套检查失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void btnReportShortage_Click(object sender, EventArgs e)
{
// 提交缺料上报指令
var result = _dispatcher.SubmitCommand(new MaterialCommand
{
SourceTerminal = "MES_EXEC",
TargetTerminal = "PURCHASE",
BusinessTopic = "物料排程",
CommandType = "缺料上报",
RelateNo = _scheduleId.ToString(),
MaterialCode = lblMaterialCode.Text,
CommandContent = JsonConvert.SerializeObject(new ShortageReportContent
{
RelateScheduleId = _scheduleId,
ShortageQty = decimal.Parse(txtShortageQty.Text),
Reporter = Environment.UserName
}),
Priority = "高"
});
if (result)
{
MessageBox.Show("缺料上报指令已提交,采购端将处理");
Close();
}
else
{
MessageBox.Show("缺料上报失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
// 物料调拨处理窗体
public partial class MaterialTransferHandleForm : Form
{
private readonly string _transferNo;
private readonly MaterialMultiTerminalDispatcher _dispatcher;
public MaterialTransferHandleForm(string transferNo, MaterialMultiTerminalDispatcher dispatcher)
{
InitializeComponent();
_transferNo = transferNo;
_dispatcher = dispatcher;
LoadTransferInfo();
}
private void LoadTransferInfo()
{
using (var conn = new SqlConnection(ConfigHelper.GetConnStr()))
{
var transfer = conn.QuerySingle<MaterialTransfer>(
"SELECT * FROM MaterialTransfer WHERE TransferNo = @TransferNo",
new { _transferNo });
lblTransferNo.Text = transfer.TransferNo;
lblMaterialCode.Text = transfer.MaterialCode;
lblTransferQty.Text = transfer.TransferQty.ToString();
lblOutWhCode.Text = transfer.OutWhCode;
lblInWhCode.Text = transfer.InWhCode;
lblTransferStatus.Text = transfer.TransferStatus;
lblPlanTime.Text = transfer.PlanTransferTime.ToString("yyyy-MM-dd HH:mm");
}
}
private void btnExecuteTransfer_Click(object sender, EventArgs e)
{
// 提交调拨执行指令
var result = _dispatcher.SubmitCommand(new MaterialCommand
{
SourceTerminal = "WH_STORAGE",
TargetTerminal = "WH_STORAGE",
BusinessTopic = "物料调拨",
CommandType = "调拨执行",
RelateNo = _transferNo,
MaterialCode = lblMaterialCode.Text,
CommandContent = JsonConvert.SerializeObject(new TransferExecuteContent
{
TransferNo = _transferNo,
OutWhCode = lblOutWhCode.Text,
InWhCode = lblInWhCode.Text,
TransferQty = decimal.Parse(lblTransferQty.Text)
}),
Priority = "高"
});
if (result)
{
MessageBox.Show("调拨执行指令已提交,库存将同步更新");
LoadTransferInfo();
}
else
{
MessageBox.Show("调拨执行失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void btnHandleException_Click(object sender, EventArgs e)
{
// 提交异常处理指令
var result = _dispatcher.SubmitCommand(new MaterialCommand
{
SourceTerminal = "WH_STORAGE",
TargetTerminal = "ERP_PLAN",
BusinessTopic = "物料调拨",
CommandType = "异常处理",
RelateNo = _transferNo,
MaterialCode = lblMaterialCode.Text,
CommandContent = JsonConvert.SerializeObject(new ExceptionHandleContent
{
RelateNo = _transferNo,
BusinessType = "物料调拨",
ExceptionDesc = txtExceptionDesc.Text.Trim(),
HandleDesc = txtHandleDesc.Text.Trim()
}),
Priority = "高"
});
if (result)
{
MessageBox.Show("异常处理指令已提交");
Close();
}
else
{
MessageBox.Show("异常处理失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
四、 物料协同适配要点与扩展
1. 核心适配要点
- 多业务主题聚合:将物料计划、排程、调拨三大核心业务主题聚合到统一工作台,打破信息孤岛,适配 ERP/MES 物料协同全流程;
- 多端指令协同:指令按业务主题 + 指令端路由,如缺料指令自动推送到采购端,调拨指令自动推送到仓储端,保障多端协同效率;
- 事项分类管控:按待办 / 在办 / 已办 / 预警 / 异常分类展示物料事项,高优先级 / 超期事项醒目展示,适配物料协同的紧急度管控;
- 可视化看板:通过图表直观展示物料事项分布、业务主题占比、优先级分布,支持决策层快速掌握物料协同整体状态;
- 一键式处理:针对不同业务主题提供标准化处理界面,支持计划确认、排程调整、调拨执行、齐套检查等快速操作,提升处理效率。
2. 扩展方向
- 智能预警规则:集成规则引擎,支持自定义预警规则(如齐套率 < 80%、调拨超期 2 小时等),自动触发预警并推送;
- 移动端推送:对接企业微信 / 钉钉,将高优先级 / 预警 / 异常事项推送到相关人员移动端,支持移动端快速处理;
- WMS/ERP 集成:深度对接 WMS 库存系统、ERP 计划系统,实现库存数据自动同步、计划状态实时更新;
- 智能排程算法:集成遗传算法 / 蚁群算法,根据物料齐套率、产线产能、订单优先级自动优化物料排程;
- 物料追溯:记录物料从计划→排程→调拨→使用的全流程轨迹,支持物料批次 / 供应商 / 库位的精准追溯;
- 数据大屏:扩展至车间 / 仓储大屏,实时展示物料齐套率、调拨进度、缺料预警等核心指标。
总结
核心关键点
该组件可直接作为 ERP/MES 物料计划排程调拨协同的核心 WinForm 中间件,适配多指令端 / 业务主题汇总的工作台模式,支撑物料协同全流程的一站式管控。
网硕互联帮助中心




评论前必须登录!
注册