本文为实战经验分享,介绍如何使用SQLSugar和.NET Core构建一个功能完整、轻量级的微型工艺管理系统,适用于中小制造企业或车间级的数字化管理需求。
引言
在制造业数字化转型的浪潮中,工艺管理作为连接设计与生产的桥梁,其信息化水平直接影响产品质量和生产效率。对于中小型企业而言,大型ERP系统往往过于复杂且成本高昂。本文将介绍如何使用SQLSugar构建一个功能完备、易于部署的微型工艺管理系统。
一、系统概述与核心功能
1.1 系统定位
-
轻量级:单机或局域网部署,资源占用小
-
易用性:界面简洁,操作直观
-
低成本:开源技术栈,部署维护成本低
-
可扩展:模块化设计,可按需扩展功能
1.2 核心功能模块
|
模块名称 |
主要功能 |
适用场景 |
|---|---|---|
|
工艺路线管理 |
BOM管理、工艺路线制定、工序定义 |
产品工艺规划 |
|
设备管理 |
设备台账、状态监控、维护计划 |
生产设备管理 |
|
物料管理 |
物料编码、库存管理、领用记录 |
物料控制 |
|
质量控制 |
检验标准、不合格品处理、质量统计 |
质量追溯 |
|
生产监控 |
实时进度、工时统计、效率分析 |
生产过程跟踪 |
二、技术架构与数据库设计
2.1 技术选型
┌─────────────────────────────────────┐
│ 前端展示层 │
│ WinForm / Blazor │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Web API层 │
│ .NET 6 Minimal API │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ SQLSugar ORM层 │
│ 5.0.4+ │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ 数据库持久层 │
│ SQLite / PostgreSQL / SQL Server │
└─────────────────────────────────────┘
2.2 核心数据模型设计
// Models/Product.cs - 产品实体
[Table("products")]
public class Product
{
[Column("id", IsPrimaryKey = true, IsIdentity = true)]
public int Id { get; set; }
[Column("product_code", Length = 50)]
[Required]
public string ProductCode { get; set; } = string.Empty;
[Column("product_name", Length = 200)]
[Required]
public string ProductName { get; set; } = string.Empty;
[Column("specification", Length = 500)]
public string Specification { get; set; } = string.Empty;
[Column("unit", Length = 20)]
public string Unit { get; set; } = "个";
[Column("drawing_no", Length = 100)]
public string DrawingNo { get; set; } = string.Empty;
[Column("version", Length = 20)]
public string Version { get; set; } = "1.0";
[Column("is_active")]
public bool IsActive { get; set; } = true;
[Column("created_at")]
public DateTime CreatedAt { get; set; } = DateTime.Now;
[Column("updated_at")]
public DateTime UpdatedAt { get; set; } = DateTime.Now;
[Navigate(NavigateType.OneToMany, nameof(ProcessRoute.ProductId))]
public List<ProcessRoute> ProcessRoutes { get; set; } = new();
}
// Models/ProcessRoute.cs - 工艺路线实体
[Table("process_routes")]
[SugarIndex("idx_process_route_product", nameof(ProductId), OrderByType.Asc)]
[SugarIndex("idx_process_route_code", nameof(RouteCode), OrderByType.Asc)]
public class ProcessRoute
{
[Column("id", IsPrimaryKey = true, IsIdentity = true)]
public int Id { get; set; }
[Column("product_id")]
[Required]
public int ProductId { get; set; }
[Column("route_code", Length = 100)]
[Required]
public string RouteCode { get; set; } = string.Empty;
[Column("route_name", Length = 200)]
[Required]
public string RouteName { get; set; } = string.Empty;
[Column("version", Length = 20)]
public string Version { get; set; } = "1.0";
[Column("status")]
public ProcessRouteStatus Status { get; set; } = ProcessRouteStatus.Draft;
[Column("total_operations")]
public int TotalOperations { get; set; }
[Column("estimated_hours")]
[DecimalDigits(2)]
public decimal EstimatedHours { get; set; }
[Column("remarks", Length = 1000)]
public string Remarks { get; set; } = string.Empty;
[Column("is_default")]
public bool IsDefault { get; set; }
[Column("created_by", Length = 50)]
public string CreatedBy { get; set; } = string.Empty;
[Column("created_at")]
public DateTime CreatedAt { get; set; } = DateTime.Now;
[Navigate(NavigateType.OneToOne, nameof(ProductId))]
public Product Product { get; set; }
[Navigate(NavigateType.OneToMany, nameof(ProcessOperation.RouteId))]
public List<ProcessOperation> Operations { get; set; } = new();
}
// Models/ProcessOperation.cs - 工序实体
[Table("process_operations")]
[SugarIndex("idx_operation_route", nameof(RouteId), OrderByType.Asc, nameof(Sequence), OrderByType.Asc)]
public class ProcessOperation
{
[Column("id", IsPrimaryKey = true, IsIdentity = true)]
public int Id { get; set; }
[Column("route_id")]
[Required]
public int RouteId { get; set; }
[Column("operation_code", Length = 50)]
[Required]
public string OperationCode { get; set; } = string.Empty;
[Column("operation_name", Length = 200)]
[Required]
public string OperationName { get; set; } = string.Empty;
[Column("sequence")]
public int Sequence { get; set; }
[Column("work_center_id")]
public int? WorkCenterId { get; set; }
[Column("equipment_id")]
public int? EquipmentId { get; set; }
[Column("standard_time", DecimalDigits(2))]
public decimal StandardTime { get; set; } // 标准工时(分钟)
[Column("setup_time", DecimalDigits(2))]
public decimal SetupTime { get; set; } // 准备时间(分钟)
[Column("skill_level")]
public int SkillLevel { get; set; } = 1;
[Column("quality_checkpoint")]
public bool QualityCheckpoint { get; set; }
[Column("inspection_standard", Length = 1000)]
public string InspectionStandard { get; set; } = string.Empty;
[Column("input_materials", ColumnDataType = "jsonb")]
public List<OperationMaterial> InputMaterials { get; set; } = new();
[Column("output_materials", ColumnDataType = "jsonb")]
public List<OperationMaterial> OutputMaterials { get; set; } = new();
[Navigate(NavigateType.OneToOne, nameof(WorkCenterId))]
public WorkCenter WorkCenter { get; set; }
[Navigate(NavigateType.OneToOne, nameof(EquipmentId))]
public Equipment Equipment { get; set; }
}
// Models/OperationMaterial.cs - 工序物料
public class OperationMaterial
{
public int MaterialId { get; set; }
public string MaterialCode { get; set; } = string.Empty;
public string MaterialName { get; set; } = string.Empty;
public decimal Quantity { get; set; }
public string Unit { get; set; } = "个";
public bool IsMainMaterial { get; set; }
}
// Models/WorkCenter.cs - 工作中心
[Table("work_centers")]
public class WorkCenter
{
[Column("id", IsPrimaryKey = true, IsIdentity = true)]
public int Id { get; set; }
[Column("center_code", Length = 50)]
[Required]
public string CenterCode { get; set; } = string.Empty;
[Column("center_name", Length = 200)]
[Required]
public string CenterName { get; set; } = string.Empty;
[Column("type")]
public WorkCenterType Type { get; set; } = WorkCenterType.Machine;
[Column("capacity")]
public int Capacity { get; set; } // 生产能力(件/小时)
[Column("status")]
public WorkCenterStatus Status { get; set; } = WorkCenterStatus.Active;
[Column("location", Length = 200)]
public string Location { get; set; } = string.Empty;
}
// Models/Equipment.cs - 设备
[Table("equipment")]
[SugarIndex("idx_equipment_code", nameof(EquipmentCode), OrderByType.Asc)]
public class Equipment
{
[Column("id", IsPrimaryKey = true, IsIdentity = true)]
public int Id { get; set; }
[Column("equipment_code", Length = 50)]
[Required]
public string EquipmentCode { get; set; } = string.Empty;
[Column("equipment_name", Length = 200)]
[Required]
public string EquipmentName { get; set; } = string.Empty;
[Column("model", Length = 100)]
public string Model { get; set; } = string.Empty;
[Column("specification", Length = 500)]
public string Specification { get; set; } = string.Empty;
[Column("manufacturer", Length = 200)]
public string Manufacturer { get; set; } = string.Empty;
[Column("purchase_date")]
public DateTime? PurchaseDate { get; set; }
[Column("status")]
public EquipmentStatus Status { get; set; } = EquipmentStatus.Normal;
[Column("current_workcenter_id")]
public int? CurrentWorkCenterId { get; set; }
[Column("maintenance_cycle")]
public int MaintenanceCycle { get; set; } = 30; // 维护周期(天)
[Column("last_maintenance_date")]
public DateTime? LastMaintenanceDate { get; set; }
[Column("next_maintenance_date")]
public DateTime? NextMaintenanceDate { get; set; }
}
// 枚举定义
public enum ProcessRouteStatus
{
Draft = 0, // 草稿
Approved = 1, // 已批准
InUse = 2, // 使用中
Obsolete = 3 // 已作废
}
public enum WorkCenterType
{
Machine = 1, // 机加工
Assembly = 2, // 装配
Inspection = 3, // 检验
Pack
2.3 数据库初始化脚本
// Data/DatabaseInitializer.cs
public static class DatabaseInitializer
{
public static void Initialize(ISqlSugarClient db)
{
// 创建数据库(如果使用SQLite)
db.DbMaintenance.CreateDatabase();
// 创建表
db.CodeFirst.InitTables(
typeof(Product),
typeof(ProcessRoute),
typeof(ProcessOperation),
typeof(WorkCenter),
typeof(Equipment),
typeof(Material),
typeof(ProductionOrder),
typeof(ProductionProgress)
);
// 创建索引
CreateIndexes(db);
// 初始化基础数据
InitializeBaseData(db);
}
private static void CreateIndexes(ISqlSugarClient db)
{
// 为常用查询字段创建索引
db.DbMaintenance.CreateIndex("idx_products_code", "products", "product_code", false);
db.DbMaintenance.CreateIndex("idx_materials_code", "materials", "material_code", false);
db.DbMaintenance.CreateIndex("idx_orders_code", "production_orders", "order_code", false);
db.DbMaintenance.CreateIndex("idx_orders_status", "production_orders", "status", false);
db.DbMaintenance.CreateIndex("idx_orders_product", "production_orders", "product_id", false);
db.DbMaintenance.CreateIndex("idx_progress_order", "production_progress", "order_id", false);
db.DbMaintenance.CreateIndex("idx_progress_operation", "production_progress", "operation_id", false);
}
private static void InitializeBaseData(ISqlSugarClient db)
{
// 如果工作中心表为空,添加默认工作中心
if (!db.Queryable<WorkCenter>().Any())
{
var workCenters = new List<WorkCenter>
{
new() { CenterCode = "WC-001", CenterName = "加工中心", Type = WorkCenterType.Machine },
new() { CenterCode = "WC-002", CenterName = "装配车间", Type = WorkCenterType.Assembly },
new() { CenterCode = "WC-003", CenterName = "检验站", Type = WorkCenterType.Inspection },
new() { CenterCode = "WC-004", CenterName = "包装区", Type = WorkCenterType.Packing }
};
db.Insertable(workCenters).ExecuteCommand();
}
// 添加默认用户
if (!db.Queryable<User>().Any())
{
db.Insertable(new User
{
Username = "admin",
Password = HashPassword("admin123"),
FullName = "系统管理员",
Role = UserRole.Admin,
IsActive = true
}).ExecuteCommand();
}
}
private static string HashPassword(string password)
{
return BCrypt.Net.BCrypt.HashPassword(password);
}
}
三、核心业务功能实现
3.1 工艺路线管理服务
// Services/ProcessRouteService.cs
public class ProcessRouteService : IProcessRouteService
{
private readonly ISqlSugarClient _db;
private readonly ILogger<ProcessRouteService> _logger;
public ProcessRouteService(ISqlSugarClient db, ILogger<ProcessRouteService> logger)
{
_db = db;
_logger = logger;
}
/// <summary>
/// 创建工艺路线
/// </summary>
public async Task<ApiResult<ProcessRouteDto>> CreateProcessRouteAsync(CreateProcessRouteRequest request)
{
try
{
// 验证产品是否存在
var product = await _db.Queryable<Product>()
.Where(p => p.Id == request.ProductId && p.IsActive)
.FirstAsync();
if (product == null)
return ApiResult<ProcessRouteDto>.Error("产品不存在或已停用");
// 验证路线编码是否重复
var exists = await _db.Queryable<ProcessRoute>()
.Where(r => r.RouteCode == request.RouteCode)
.AnyAsync();
if (exists)
return ApiResult<ProcessRouteDto>.Error("工艺路线编码已存在");
var route = new ProcessRoute
{
ProductId = request.ProductId,
RouteCode = request.RouteCode,
RouteName = request.RouteName,
Version = request.Version,
Status = ProcessRouteStatus.Draft,
Remarks = request.Remarks,
CreatedBy = request.CreatedBy,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
};
var id = await _db.Insertable(route).ExecuteReturnIdentityAsync();
route.Id = id;
// 添加工序
if (request.Operations != null && request.Operations.Any())
{
var operations = request.Operations.Select((op, index) => new ProcessOperation
{
RouteId = id,
OperationCode = op.OperationCode,
OperationName = op.OperationName,
Sequence = op.Sequence,
WorkCenterId = op.WorkCenterId,
EquipmentId = op.EquipmentId,
StandardTime = op.StandardTime,
SetupTime = op.SetupTime,
SkillLevel = op.SkillLevel,
QualityCheckpoint = op.QualityCheckpoint,
InspectionStandard = op.InspectionStandard,
InputMaterials = op.InputMaterials,
OutputMaterials = op.OutputMaterials
}).ToList();
await _db.Insertable(operations).ExecuteCommandAsync();
// 更新路线总工序数和预计工时
route.TotalOperations = operations.Count;
route.EstimatedHours = operations.Sum(o => o.StandardTime) / 60; // 转换为小时
await _db.Updateable(route).ExecuteCommandAsync();
}
var result = MapToDto(route);
return ApiResult<ProcessRouteDto>.Success(result);
}
catch (Exception ex)
{
_logger.LogError(ex, $"创建工艺路线失败: {request.RouteCode}");
return ApiResult<ProcessRouteDto>.Error("创建工艺路线失败");
}
}
/// <summary>
/// 复制工艺路线
/// </summary>
public async Task<ApiResult<ProcessRouteDto>> CopyProcessRouteAsync(int sourceRouteId, CopyRouteRequest request)
{
using var transaction = _db.Ado.UseTran();
try
{
// 获取源路线
var sourceRoute = await _db.Queryable<ProcessRoute>()
.Includes(r => r.Operations)
.Where(r => r.Id == sourceRouteId)
.FirstAsync();
if (sourceRoute == null)
return ApiResult<ProcessRouteDto>.Error("源工艺路线不存在");
// 创建新路线
var newRoute = new ProcessRoute
{
ProductId = sourceRoute.ProductId,
RouteCode = request.NewRouteCode,
RouteName = request.NewRouteName,
Version = request.NewVersion,
Status = ProcessRouteStatus.Draft,
TotalOperations = sourceRoute.TotalOperations,
EstimatedHours = sourceRoute.EstimatedHours,
Remarks = request.Remarks ?? sourceRoute.Remarks,
CreatedBy = request.CreatedBy,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
};
var newRouteId = await _db.Insertable(newRoute).ExecuteReturnIdentityAsync();
// 复制工序
if (sourceRoute.Operations.Any())
{
var newOperations = sourceRoute.Operations.Select(op => new ProcessOperation
{
RouteId = newRouteId,
OperationCode = op.OperationCode,
OperationName = op.OperationName,
Sequence = op.Sequence,
WorkCenterId = op.WorkCenterId,
EquipmentId = op.EquipmentId,
StandardTime = op.StandardTime,
SetupTime = op.SetupTime,
SkillLevel = op.SkillLevel,
QualityCheckpoint = op.QualityCheckpoint,
InspectionStandard = op.InspectionStandard,
InputMaterials = op.InputMaterials,
OutputMaterials = op.OutputMaterials
}).ToList();
await _db.Insertable(newOperations).ExecuteCommandAsync();
}
transaction.Commit();
newRoute.Id = newRouteId;
var result = MapToDto(newRoute);
return ApiResult<ProcessRouteDto>.Success(result);
}
catch (Exception ex)
{
transaction.Rollback();
_logger.LogError(ex, $"复制工艺路线失败: {sourceRouteId}");
return ApiResult<ProcessRouteDto>.Error("复制工艺路线失败");
}
}
/// <summary>
/// 获取工艺路线树形结构
/// </summary>
public async Task<ApiResult<List<ProcessRouteTreeDto>>> GetProcessRouteTreeAsync(int? productId = null)
{
try
{
var query = _db.Queryable<ProcessRoute>()
.Includes(r => r.Product)
.Includes(r => r.Operations, op => op.WorkCenter)
.Where(r => r.Status != ProcessRouteStatus.Obsolete);
if (productId.HasValue)
query = query.Where(r => r.ProductId == productId.Value);
var routes = await query.OrderBy(r => r.ProductId)
.ThenBy(r => r.RouteCode)
.ToListAsync();
var tree = routes.GroupBy(r => r.Product)
.Select(g => new ProcessRouteTreeDto
{
ProductId = g.Key.Id,
ProductCode = g.Key.ProductCode,
ProductName = g.Key.ProductName,
Routes = g.Select(r => new ProcessRouteItemDto
{
RouteId = r.Id,
RouteCode = r.RouteCode,
RouteName = r.RouteName,
Version = r.Version,
Status = r.Status.ToString(),
TotalOperations = r.TotalOperations,
EstimatedHours = r.EstimatedHours,
Operations = r.Operations.OrderBy(op => op.Sequence)
.Select(op => new OperationItemDto
{
OperationId = op.Id,
OperationCode = op.OperationCode,
OperationName = op.OperationName,
Sequence = op.Sequence,
StandardTime = op.StandardTime,
WorkCenterName = op.WorkCenter?.CenterName
}).ToList()
}).ToList()
}).ToList();
return ApiResult<List<ProcessRouteTreeDto>>.Success(tree);
}
catch (Exception ex)
{
_logger.LogError(ex, "获取工艺路线树失败");
return ApiResult<List<ProcessRouteTreeDto>>.Error("获取数据失败");
}
}
/// <summary>
/// 导出工艺路线为Excel
/// </summary>
public async Task<byte[]> ExportProcessRouteToExcelAsync(int routeId)
{
var route = await _db.Queryable<ProcessRoute>()
.Includes(r => r.Product)
.Includes(r => r.Operations, op => op.WorkCenter)
.Where(r => r.Id == routeId)
.FirstAsync();
if (route == null)
throw new ArgumentException("工艺路线不存在");
using var package = new ExcelPackage();
var worksheet = package.Workbook.Worksheets.Add("工艺路线");
// 设置表头
worksheet.Cells[1, 1].Value = "工艺路线信息";
worksheet.Cells[1, 1].Style.Font.Bold = true;
worksheet.Cells[1, 1].Style.Font.Size = 14;
// 填写路线基本信息
worksheet.Cells[2, 1].Value = "产品编码";
worksheet.Cells[2, 2].Value = route.Product.ProductCode;
worksheet.Cells[3, 1].Value = "产品名称";
worksheet.Cells[3, 2].Value = route.Product.ProductName;
worksheet.Cells[4, 1].Value = "路线编码";
worksheet.Cells[4, 2].Value = route.RouteCode;
worksheet.Cells[5, 1].Value = "路线名称";
worksheet.Cells[5, 2].Value = route.RouteName;
worksheet.Cells[6, 1].Value = "版本";
worksheet.Cells[6, 2].Value = route.Version;
// 填写工序信息
worksheet.Cells[8, 1].Value = "工序信息";
worksheet.Cells[8, 1].Style.Font.Bold = true;
worksheet.Cells[9, 1].Value = "工序序号";
worksheet.Cells[9, 2].Value = "工序编码";
worksheet.Cells[9, 3].Value = "工序名称";
worksheet.Cells[9, 4].Value = "工作中心";
worksheet.Cells[9, 5].Value = "标准工时(分)";
var row = 10;
foreach (var operation in route.Operations.OrderBy(o => o.Sequence))
{
worksheet.Cells[row, 1].Value = operation.Sequence;
worksheet.Cells[row, 2].Value = operation.OperationCode;
worksheet.Cells[row, 3].Value = operation.OperationName;
worksheet.Cells[row, 4].Value = operation.WorkCenter?.CenterName;
worksheet.Cells[row, 5].Value = operation.StandardTime;
row++;
}
// 自动调整列宽
worksheet.Cells[worksheet.Dimension.Address].AutoFitColumns();
return package.GetAsByteArray();
}
}
3.2 生产订单管理服务
// Services/ProductionOrderService.cs
public class ProductionOrderService : IProductionOrderService
{
private readonly ISqlSugarClient _db;
private readonly ILogger<ProductionOrderService> _logger;
public ProductionOrderService(ISqlSugarClient db, ILogger<ProductionOrderService> logger)
{
_db = db;
_logger = logger;
}
/// <summary>
/// 创建生产订单
/// </summary>
public async Task<ApiResult<ProductionOrderDto>> CreateProductionOrderAsync(CreateOrderRequest request)
{
using var transaction = _db.Ado.UseTran();
try
{
// 验证产品
var product = await _db.Queryable<Product>()
.Where(p => p.Id == request.ProductId && p.IsActive)
.FirstAsync();
if (product == null)
return ApiResult<ProductionOrderDto>.Error("产品不存在或已停用");
// 验证工艺路线
var route = await _db.Queryable<ProcessRoute>()
.Where(r => r.Id == request.RouteId && r.Status == ProcessRouteStatus.Approved)
.FirstAsync();
if (route == null)
return ApiResult<ProductionOrderDto>.Error("工艺路线不存在或未批准");
// 生成订单编号
var orderCode = GenerateOrderCode();
// 创建生产订单
var order = new ProductionOrder
{
OrderCode = orderCode,
ProductId = request.ProductId,
RouteId = request.RouteId,
Quantity = request.Quantity,
Priority = request.Priority,
Status = ProductionOrderStatus.Pending,
PlanStartDate = request.PlanStartDate,
PlanEndDate = request.PlanEndDate,
CustomerName = request.CustomerName,
OrderNumber = request.OrderNumber,
Remarks = request.Remarks,
CreatedBy = request.CreatedBy,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
};
var orderId = await _db.Insertable(order).ExecuteReturnIdentityAsync();
order.Id = orderId;
// 创建生产进度记录
var progressList = new List<ProductionProgress>();
var operations = await _db.Queryable<ProcessOperation>()
.Where(o => o.RouteId == request.RouteId)
.OrderBy(o => o.Sequence)
.ToListAsync();
foreach (var operation in operations)
{
var progress = new ProductionProgress
{
OrderId = orderId,
OperationId = operation.Id,
Sequence = operation.Sequence,
Status = ProductionProgressStatus.Waiting,
StandardTime = operation.StandardTime,
ActualStartTime = null,
ActualEndTime = null,
ActualTime = 0
};
progressList.Add(progress);
}
await _db.Insertable(progressList).ExecuteCommandAsync();
transaction.Commit();
var result = MapToDto(order);
return ApiResult<ProductionOrderDto>.Success(result);
}
catch (Exception ex)
{
transaction.Rollback();
_logger.LogError(ex, "创建生产订单失败");
return ApiResult<ProductionOrderDto>.Error("创建生产订单失败");
}
}
/// <summary>
/// 更新生产订单进度
/// </summary>
public async Task<ApiResult<bool>> UpdateProductionProgressAsync(UpdateProgressRequest request)
{
try
{
// 获取当前工序进度
var progress = await _db.Queryable<ProductionProgress>()
.Where(p => p.OrderId == request.OrderId && p.OperationId == request.OperationId)
.FirstAsync();
if (progress == null)
return ApiResult<bool>.Error("生产进度记录不存在");
// 更新进度
progress.Status = request.Status;
if (request.ActualStartTime.HasValue)
progress.ActualStartTime = request.ActualStartTime.Value;
if (request.ActualEndTime.HasValue)
{
progress.ActualEndTime = request.ActualEndTime.Value;
if (progress.ActualStartTime.HasValue)
{
progress.ActualTime = (decimal)(progress.ActualEndTime.Value -
progress.ActualStartTime.Value).TotalMinutes;
}
}
progress.UpdatedAt = DateTime.Now;
// 更新生产订单状态
var order = await _db.Queryable<ProductionOrder>()
.Where(o => o.Id == request.OrderId)
.FirstAsync();
if (order != null)
{
// 如果当前工序完成,检查是否所有工序都完成
if (request.Status == ProductionProgressStatus.Completed)
{
var pendingCount = await _db.Queryable<ProductionProgress>()
.Where(p => p.OrderId == request.OrderId &&
p.Status != ProductionProgressStatus.Completed)
.CountAsync();
if (pendingCount == 0)
{
order.Status = ProductionOrderStatus.Completed;
order.ActualEndDate = DateTime.Now;
}
else
{
order.Status = ProductionOrderStatus.InProgress;
}
}
else if (request.Status == ProductionProgressStatus.InProgress)
{
order.Status = ProductionOrderStatus.InProgress;
if (order.ActualStartDate == null)
order.ActualStartDate = DateTime.Now;
}
order.UpdatedAt = DateTime.Now;
await _db.Updateable(order).ExecuteCommandAsync();
}
await _db.Updateable(progress).ExecuteCommandAsync();
return ApiResult<bool>.Success(true);
}
catch (Exception ex)
{
_logger.LogError(ex, $"更新生产进度失败: OrderId={request.OrderId}, OperationId={request.OperationId}");
return ApiResult<bool>.Error("更新生产进度失败");
}
}
/// <summary>
/// 获取生产订单统计
/// </summary>
public async Task<ApiResult<OrderStatisticsDto>> GetOrderStatisticsAsync(DateTime? startDate, DateTime? endDate)
{
try
{
var query = _db.Queryable<ProductionOrder>();
if (startDate.HasValue)
query = query.Where(o => o.CreatedAt >= startDate.Value);
if (endDate.HasValue)
query = query.Where(o => o.CreatedAt <= endDate.Value);
var orders = await query.ToListAsync();
var statistics = new OrderStatisticsDto
{
TotalOrders = orders.Count,
CompletedOrders = orders.Count(o => o.Status == ProductionOrderStatus.Completed),
InProgressOrders = orders.Count(o => o.Status == ProductionOrderStatus.InProgress),
PendingOrders = orders.Count(o => o.Status == ProductionOrderStatus.Pending),
TotalQuantity = orders.Sum(o => o.Quantity),
CompletedQuantity = orders.Where(o => o.Status == ProductionOrderStatus.Completed)
.Sum(o => o.Quantity),
AverageCompletionDays = orders.Where(o => o.Status == ProductionOrderStatus.Completed &&
o.ActualEndDate.HasValue && o.ActualStartDate.HasValue)
.Average(o => (o.ActualEndDate.Value - o.ActualStartDate.Value).TotalDays),
OnTimeDeliveryRate = CalculateOnTimeDeliveryRate(orders)
};
return ApiResult<OrderStatisticsDto>.Success(statistics);
}
catch (Exception ex)
{
_logger.LogError(ex, "获取订单统计失败");
return ApiResult<OrderStatisticsDto>.Error("获取统计失败");
}
}
private string GenerateOrderCode()
{
var datePart = DateTime.Now.ToString("yyyyMMdd");
var sequence = GetDailySequence();
return $"PO{datePart}{sequence:0000}";
}
private int GetDailySequence()
{
var today = DateTime.Today;
var tomorrow = today.AddDays(1);
var count = _db.Queryable<ProductionOrder>()
.Where(o => o.CreatedAt >= today && o.CreatedAt < tomorrow)
.Count();
return count + 1;
}
}
四、API接口设计
4.1 Minimal API实现
// Program.cs
var builder = WebApplication.CreateBuilder(args);
// 配置服务
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new() { Title = "微型工艺管理系统 API", Version = "v1" });
});
// 配置SQLSugar
builder.Services.AddScoped<ISqlSugarClient>(provider =>
{
var config = new ConnectionConfig
{
ConnectionString = builder.Configuration.GetConnectionString("DefaultConnection"),
DbType = DbType.PostgreSQL, // 可以根据需要切换
IsAutoCloseConnection = true,
InitKeyType = InitKeyType.Attribute,
MoreSettings = new ConnMoreSettings
{
PgSqlIsAutoToLower = false,
EnablePaginationOptimize = true
}
};
return new SqlSugarScope(config, db =>
{
// 配置AOP
db.Aop.OnLogExecuting = (sql, pars) =>
{
Console.WriteLine($"SQL: {sql}");
};
// 初始化数据库
DatabaseInitializer.Initialize(db);
});
});
// 注册服务
builder.Services.AddScoped<IProcessRouteService, ProcessRouteService>();
builder.Services.AddScoped<IProductionOrderService, ProductionOrderService>();
builder.Services.AddScoped<IMaterialService, MaterialService>();
builder.Services.AddScoped<IEquipmentService, EquipmentService>();
var app = builder.Build();
// 配置中间件
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
// 定义API端点
var api = app.MapGroup("/api");
// 产品管理
api.MapGet("/products", async (ISqlSugarClient db) =>
{
var products = await db.Queryable<Product>()
.Where(p => p.IsActive)
.OrderBy(p => p.ProductCode)
.ToListAsync();
return Results.Ok(products);
});
api.MapPost("/products", async (Product product, ISqlSugarClient db) =>
{
// 验证产品编码是否重复
var exists = await db.Queryable<Product>()
.Where(p => p.ProductCode == product.ProductCode)
.AnyAsync();
if (exists)
return Results.BadRequest("产品编码已存在");
product.CreatedAt = DateTime.Now;
product.UpdatedAt = DateTime.Now;
var id = await db.Insertable(product).ExecuteReturnIdentityAsync();
product.Id = id;
return Results.Created($"/api/products/{id}", product);
});
// 工艺路线管理
api.MapGet("/process-routes", async (IProcessRouteService service,
int? productId, int page = 1, int pageSize = 20) =>
{
var result = await service.GetProcessRoutesAsync(productId, page, pageSize);
return Results.Ok(result);
});
api.MapPost("/process-routes", async (CreateProcessRouteRequest request,
IProcessRouteService service) =>
{
var result = await service.CreateProcessRouteAsync(request);
return result.Success
? Results.Created($"/api/process-routes/{result.Data.Id}", result.Data)
: Results.BadRequest(result.Message);
});
api.MapGet("/process-routes/tree", async (IProcessRouteService service, int? productId) =>
{
var result = await service.GetProcessRouteTreeAsync(productId);
return Results.Ok(result);
});
// 生产订单管理
api.MapGet("/production-orders", async (IProductionOrderService service,
ProductionOrderStatus? status, DateTime? startDate, DateTime? endDate,
int page = 1, int pageSize = 20) =>
{
var result = await service.GetProductionOrdersAsync(status, startDate, endDate, page, pageSize);
return Results.Ok(result);
});
api.MapPost("/production-orders", async (CreateOrderRequest request,
IProductionOrderService service) =>
{
var result = await service.CreateProductionOrderAsync(request);
return result.Success
? Results.Created($"/api/production-orders/{result.Data.Id}", result.Data)
: Results.BadRequest(result.Message);
});
api.MapPut("/production-orders/progress", async (UpdateProgressRequest request,
IProductionOrderService service) =>
{
var result = await service.UpdateProductionProgressAsync(request);
return result.Success ? Results.Ok() : Results.BadRequest(result.Message);
});
// 设备管理
api.MapGet("/equipment", async (IEquipmentService service,
EquipmentStatus? status, int? workCenterId) =>
{
var result = await service.GetEquipmentAsync(status, workCenterId);
return Results.Ok(result);
});
api.MapPost("/equipment", async (CreateEquipmentRequest request,
IEquipmentService service) =>
{
var result = await service.CreateEquipmentAsync(request);
return result.Success
? Results.Created($"/api/equipment/{result.Data.Id}", result.Data)
: Results.BadRequest(result.Message);
});
// 物料管理
api.MapGet("/materials", async (IMaterialService service,
MaterialType? type, string? category, int page = 1, int pageSize = 20) =>
{
var result = await service.GetMaterialsAsync(type, category, page, pageSize);
return Results.Ok(result);
});
api.MapPost("/materials/inventory/check", async (InventoryCheckRequest request,
IMaterialService service) =>
{
var result = await service.CheckInventoryAsync(request);
return Results.Ok(result);
});
app.Run();
4.2 响应模型定义
// Models/Dtos/CommonDtos.cs
public class ApiResult<T>
{
public bool Success { get; set; }
public string Message { get; set; } = string.Empty;
public T? Data { get; set; }
public DateTime Timestamp { get; set; } = DateTime.Now;
public static ApiResult<T> Success(T data, string message = "")
{
return new ApiResult<T>
{
Success = true,
Data = data,
Message = message
};
}
public static ApiResult<T> Error(string message)
{
return new ApiResult<T>
{
Success = false,
Message = message
};
}
}
public class PageResult<T>
{
public int TotalCount { get; set; }
public int PageIndex { get; set; }
public int PageSize { get; set; }
public int TotalPages => (int)Math.Ceiling(TotalCount / (double)PageSize);
public List<T> Data { get; set; } = new();
}
// 工艺路线相关DTO
public class ProcessRouteDto
{
public int Id { get; set; }
public int ProductId { get; set; }
public string ProductCode { get; set; } = string.Empty;
public string ProductName { get; set; } = string.Empty;
public string RouteCode { get; set; } = string.Empty;
public string RouteName { get; set; } = string.Empty;
public string Version { get; set; } = string.Empty;
public string Status { get; set; } = string.Empty;
public int TotalOperations { get; set; }
public decimal EstimatedHours { get; set; }
public string Remarks { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; }
public List<ProcessOperationDto> Operations { get; set; } = new();
}
public class ProcessRouteTreeDto
{
public int ProductId { get; set; }
public string ProductCode { get; set; } = string.Empty;
public string ProductName { get; set; } = string.Empty;
public List<ProcessRouteItemDto> Routes { get; set; } = new();
}
// 生产订单相关DTO
public class ProductionOrderDto
{
public int Id { get; set; }
public string OrderCode { get; set; } = string.Empty;
public int ProductId { get; set; }
public string ProductCode { get; set; } = string.Empty;
public string ProductName { get; set; } = string.Empty;
public int RouteId { get; set; }
public string RouteName { get; set; } = string.Empty;
public decimal Quantity { get; set; }
public string Priority { get; set; } = string.Empty;
public string Status { get; set; } = string.Empty;
public DateTime? PlanStartDate { get; set; }
public DateTime? PlanEndDate { get; set; }
public DateTime? ActualStartDate { get; set; }
public DateTime? ActualEndDate { get; set; }
public string CustomerName { get; set; } = string.Empty;
public string OrderNumber { get; set; } = string.Empty;
public string Remarks { get; set; } = string.Empty;
public List<ProductionProgressDto> Progress { get; set; } = new();
}
五、前端界面实现
5.1 WinForm主界面
// Views/MainForm.cs
public partial class MainForm : Form
{
private readonly HttpClient _httpClient;
private readonly IConfiguration _configuration;
private TabControl _tabControl;
private StatusStrip _statusStrip;
public MainForm(IConfiguration configuration)
{
_configuration = configuration;
_httpClient = new HttpClient
{
BaseAddress = new Uri(_configuration["ApiSettings:BaseUrl"]!)
};
InitializeComponent();
InitializeUI();
LoadData();
}
private void InitializeUI()
{
this.Text = "微型工艺管理系统";
this.WindowState = FormWindowState.Maximized;
// 创建菜单
var menuStrip = new MenuStrip();
var fileMenu = new ToolStripMenuItem("文件(&F)");
fileMenu.DropDownItems.Add("退出", null, (s, e) => Application.Exit());
var viewMenu = new ToolStripMenuItem("视图(&V)");
viewMenu.DropDownItems.Add("刷新", null, (s, e) => RefreshData());
var helpMenu = new ToolStripMenuItem("帮助(&H)");
helpMenu.DropDownItems.Add("关于", null, ShowAbout);
menuStrip.Items.AddRange(new[] { fileMenu, viewMenu, helpMenu });
// 创建工具栏
var toolStrip = new ToolStrip();
toolStrip.Items.Add(new ToolStripButton("刷新", Properties.Resources.Refresh,
(s, e) => RefreshData()));
toolStrip.Items.Add(new ToolStripSeparator());
toolStrip.Items.Add(new ToolStripButton("新建订单", Properties.Resources.NewOrder,
ShowNewOrderDialog));
toolStrip.Items.Add(new ToolStripButton("工艺管理", Properties.Resources.Process,
ShowProcessManagement));
// 创建选项卡
_tabControl = new TabControl
{
Dock = DockStyle.Fill,
Appearance = TabAppearance.FlatButtons
};
_tabControl.TabPages.Add("dashboard", "仪表盘");
_tabControl.TabPages.Add("orders", "生产订单");
_tabControl.TabPages.Add("process", "工艺路线");
_tabControl.TabPages.Add("equipment", "设备管理");
_tabControl.TabPages.Add("materials", "物料管理");
// 创建状态栏
_statusStrip = new StatusStrip();
_statusStrip.Items.Add(new ToolStripStatusLabel
{
Text = "就绪",
Spring = true
});
_statusStrip.Items.Add(new ToolStripStatusLabel
{
Text = $"用户: {Environment.UserName}"
});
// 布局
this.Controls.AddRange(new Control[] { menuStrip, toolStrip, _tabControl, _statusStrip });
this.MainMenuStrip = menuStrip;
}
private async void LoadData()
{
try
{
// 加载仪表盘数据
var dashboardResponse = await _httpClient.GetAsync("/api/dashboard/statistics");
if (dashboardResponse.IsSuccessStatusCode)
{
var dashboardData = await dashboardResponse.Content.ReadFromJsonAsync<DashboardStatistics>();
UpdateDashboardTab(dashboardData);
}
// 加载生产订单
var ordersResponse = await _httpClient.GetAsync("/api/production-orders");
if (ordersResponse.IsSuccessStatusCode)
{
var orders = await ordersResponse.Content.ReadFromJsonAsync<List<ProductionOrderDto>>();
UpdateOrdersTab(orders);
}
UpdateStatus("数据加载完成");
}
catch (Exception ex)
{
MessageBox.Show($"数据加载失败: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void UpdateDashboardTab(DashboardStatistics data)
{
var tabPage = _tabControl.TabPages["dashboard"];
tabPage.Controls.Clear();
// 创建仪表盘布局
var tableLayout = new TableLayoutPanel
{
Dock = DockStyle.Fill,
ColumnCount = 2,
RowCount = 2
};
// 添加统计卡片
var orderCard = CreateStatCard("生产订单",
$"{data.TotalOrders}个",
$"进行中: {data.InProgressOrders}",
Color.FromArgb(52, 152, 219));
var completionCard = CreateStatCard("完成率",
$"{data.CompletionRate:P0}",
$"准时交付: {data.OnTimeDeliveryRate:P0}",
Color.FromArgb(46, 204, 113));
var efficiencyCard = CreateStatCard("生产效率",
$"{data.AverageEfficiency:F1}%",
$"OEE: {data.OEE:F1}%",
Color.FromArgb(241, 196, 15));
var equipmentCard = CreateStatCard("设备状态",
$"正常: {data.NormalEquipmentCount}",
$"故障: {data.FaultEquipmentCount}",
Color.FromArgb(155, 89, 182));
tableLayout.Controls.Add(orderCard, 0, 0);
tableLayout.Controls.Add(completionCard, 1, 0);
tableLayout.Controls.Add(efficiencyCard, 0, 1);
tableLayout.Controls.Add(equipmentCard, 1, 1);
tabPage.Controls.Add(tableLayout);
}
private Control CreateStatCard(string title, string value, string subValue, Color color)
{
var panel = new Panel
{
BackColor = Color.White,
BorderStyle = BorderStyle.FixedSingle,
Margin = new Padding(10),
Padding = new Padding(10)
};
var titleLabel = new Label
{
Text = title,
Font = new Font("微软雅黑", 10, FontStyle.Bold),
ForeColor = Color.Gray,
AutoSize = true,
Location = new Point(10, 10)
};
var valueLabel = new Label
{
Text = value,
Font = new Font("微软雅黑", 24, FontStyle.Bold),
ForeColor = color,
AutoSize = true,
Location = new Point(10, 35)
};
var subValueLabel = new Label
{
Text = subValue,
Font = new Font("微软雅黑", 9),
ForeColor = Color.DarkGray,
AutoSize = true,
Location = new Point(10, 75)
};
panel.Controls.AddRange(new Control[] { titleLabel, valueLabel, subValueLabel });
return panel;
}
private void ShowNewOrderDialog(object? sender, EventArgs e)
{
var dialog = new NewOrderDialog();
if (dialog.ShowDialog() == DialogResult.OK)
{
// 创建新订单
var order = dialog.GetOrder();
CreateOrderAsync(order);
}
}
private async Task CreateOrderAsync(CreateOrderRequest order)
{
try
{
var response = await _httpClient.PostAsJsonAsync("/api/production-orders", order);
if (response.IsSuccessStatusCode)
{
MessageBox.Show("生产订单创建成功", "成功",
MessageBoxButtons.OK, MessageBoxIcon.Information);
RefreshData();
}
else
{
var error = await response.Content.ReadAsStringAsync();
MessageBox.Show($"创建失败: {error}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
catch (Exception ex)
{
MessageBox.Show($"创建失败: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void UpdateStatus(string message)
{
if (_statusStrip.Items.Count > 0)
{
_statusStrip.Items[0].Text = message;
}
}
}
5.2 工艺路线管理界面
// Views/ProcessRouteForm.cs
public partial class ProcessRouteForm : Form
{
private readonly HttpClient _httpClient;
private DataGridView _dataGridView;
private TreeView _treeView;
private SplitContainer _splitContainer;
public ProcessRouteForm(HttpClient httpClient)
{
_httpClient = httpClient;
InitializeComponent();
InitializeUI();
LoadProcessTree();
}
private void InitializeUI()
{
this.Text = "工艺路线管理";
this.Size = new Size(1200, 800);
// 创建工具栏
var toolStrip = new ToolStrip();
toolStrip.Items.Add(new ToolStripButton("新建", null, ShowNewRouteDialog));
toolStrip.Items.Add(new ToolStripButton("编辑", null, EditSelectedRoute));
toolStrip.Items.Add(new ToolStripButton("删除", null, DeleteSelectedRoute));
toolStrip.Items.Add(new ToolStripSeparator());
toolStrip.Items.Add(new ToolStripButton("复制", null, CopySelectedRoute));
toolStrip.Items.Add(new ToolStripButton("导出", null, ExportSelectedRoute));
// 创建分割容器
_splitContainer = new SplitContainer
{
Dock = DockStyle.Fill,
Orientation = Orientation.Horizontal,
SplitterDistance = 300
};
// 树形视图(显示产品-工艺路线结构)
_treeView = new TreeView
{
Dock = DockStyle.Fill,
ShowLines = true,
ShowRootLines = true,
ShowPlusMinus = true,
HideSelection = false
};
_treeView.AfterSelect += OnTreeViewSelectionChanged;
// 数据网格视图(显示详细数据)
_dataGridView = new DataGridView
{
Dock = DockStyle.Fill,
AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill,
AllowUserToAddRows = false,
AllowUserToDeleteRows = false,
SelectionMode = DataGridViewSelectionMode.FullRowSelect,
MultiSelect = false,
ReadOnly = true
};
// 布局
_splitContainer.Panel1.Controls.Add(_treeView);
_splitContainer.Panel2.Controls.Add(_dataGridView);
this.Controls.AddRange(new Control[] { toolStrip, _splitContainer });
}
private async void LoadProcessTree()
{
try
{
var response = await _httpClient.GetAsync("/api/process-routes/tree");
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadFromJsonAsync<ApiResult<List<ProcessRouteTreeDto>>>();
if (result?.Success == true && result.Data != null)
{
BuildTreeView(result.Data);
}
}
}
catch (Exception ex)
{
MessageBox.Show($"加载工艺树失败: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void BuildTreeView(List<ProcessRouteTreeDto> data)
{
_treeView.Nodes.Clear();
foreach (var product in data)
{
var productNode = new TreeNode($"{product.ProductCode} - {product.ProductName}")
{
Tag = new TreeNodeTag { Type = TreeNodeType.Product, Id = product.ProductId }
};
foreach (var route in product.Routes)
{
var routeNode = new TreeNode($"{route.RouteCode} - {route.RouteName} (v{route.Version})")
{
Tag = new TreeNodeTag { Type = TreeNodeType.ProcessRoute, Id = route.RouteId }
};
foreach (var operation in route.Operations)
{
var operationNode = new TreeNode($"{operation.Sequence}. {operation.OperationName}")
{
Tag = new TreeNodeTag { Type = TreeNodeType.ProcessOperation, Id = operation.OperationId }
};
routeNode.Nodes.Add(operationNode);
}
productNode.Nodes.Add(routeNode);
}
_treeView.Nodes.Add(productNode);
}
// 展开第一级
if (_treeView.Nodes.Count > 0)
{
_treeView.Nodes[0].Expand();
}
}
private async void OnTreeViewSelectionChanged(object? sender, TreeViewEventArgs e)
{
if (e.Node?.Tag is TreeNodeTag tag)
{
switch (tag.Type)
{
case TreeNodeType.Product:
await LoadProductDetails(tag.Id);
break;
case TreeNodeType.ProcessRoute:
await LoadProcessRouteDetails(tag.Id);
break;
case TreeNodeType.ProcessOperation:
await LoadOperationDetails(tag.Id);
break;
}
}
}
private async Task LoadProcessRouteDetails(int routeId)
{
try
{
var response = await _httpClient.GetAsync($"/api/process-routes/{routeId}");
if (response.IsSuccessStatusCode)
{
var route = await response.Content.ReadFromJsonAsync<ProcessRouteDto>();
// 更新DataGridView
_dataGridView.DataSource = route?.Operations;
// 格式化列
FormatDataGridView();
}
}
catch (Exception ex)
{
MessageBox.Show($"加载工艺路线详情失败: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void FormatDataGridView()
{
if (_dataGridView.Columns.Count > 0)
{
// 设置列标题
_dataGridView.Columns["Sequence"].HeaderText = "序号";
_dataGridView.Columns["OperationCode"].HeaderText = "工序编码";
_dataGridView.Columns["OperationName"].HeaderText = "工序名称";
_dataGridView.Columns["StandardTime"].HeaderText = "标准工时(分)";
_dataGridView.Columns["WorkCenterName"].HeaderText = "工作中心";
// 设置列宽
_dataGridView.Columns["Sequence"].Width = 60;
_dataGridView.Columns["OperationCode"].Width = 120;
_dataGridView.Columns["OperationName"].Width = 200;
_dataGridView.Columns["StandardTime"].Width = 120;
_dataGridView.Columns["WorkCenterName"].Width = 150;
// 设置数字格式
_dataGridView.Columns["StandardTime"].DefaultCellStyle.Format = "F2";
}
}
}
六、部署与配置
// appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Port=5432;Database=process_management;User Id=postgres;Password=123456;",
"SQLiteConnection": "Data Source=process_management.db"
},
"ApiSettings": {
"BaseUrl": "http://localhost:5000",
"ApiKey": "your-api-key-here"
},
"JwtSettings": {
"Issuer": "ProcessManagementSystem",
"Audience": "ProcessManagementClient",
"SecretKey": "your-super-secret-key-here-minimum-32-chars",
"ExpireMinutes": 120
},
"SystemSettings": {
"CompanyName": "某某制造有限公司",
"SystemName": "微型工艺管理系统",
"Version": "1.0.0",
"DataBackupPath": "./backups",
"LogRetentionDays": 30,
"MaxFileSizeMB": 10
}
}
6.2 部署脚本
#!/bin/bash
# deploy.sh - 部署脚本
echo "开始部署微型工艺管理系统..."
# 检查.NET SDK是否安装
if ! command -v dotnet &> /dev/null; then
echo "错误: .NET SDK未安装"
exit 1
fi
# 创建发布目录
mkdir -p ./publish
rm -rf ./publish/*
# 发布后端API
echo "正在发布后端API..."
cd ./src/ProcessManagement.API
dotnet publish -c Release -o ../../publish/api
# 发布前端WinForm应用
echo "正在发布前端应用..."
cd ../ProcessManagement.WinForms
dotnet publish -c Release -o ../../publish/winforms
# 创建配置文件
echo "正在创建配置文件..."
cd ../../publish
cat > appsettings.production.json << EOF
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"DefaultConnection": "Data Source=./data/process_management.db"
}
}
EOF
# 创建启动脚本
cat > start.sh << 'EOF'
#!/bin/bash
echo "启动微型工艺管理系统..."
echo "请选择启动模式:"
echo "1) 仅启动API服务"
echo "2) 仅启动客户端"
echo "3) 同时启动API服务和客户端"
read -p "请输入选择 (1-3): " choice
case $choice in
1)
cd ./api
./ProcessManagement.API
;;
2)
cd ./winforms
./ProcessManagement.WinForms
;;
3)
# 启动API服务
cd ./api
./ProcessManagement.API &
API_PID=$!
# 等待API服务启动
sleep 3
# 启动客户端
cd ../winforms
./ProcessManagement.WinForms
# 客户端退出后关闭API服务
kill $API_PID
;;
*)
echo "无效选择"
exit 1
;;
esac
EOF
chmod +x start.sh
echo "部署完成!"
echo "使用方法:"
echo "1. 进入 publish 目录"
echo "2. 运行 ./start.sh"
echo "3. 按照提示选择启动模式"
七、系统特色与优势
7.1 核心优势
-
轻量级架构:
-
单文件数据库(SQLite)支持
-
低资源消耗,适合老旧硬件
-
无需复杂部署环境
-
-
功能完备:
-
覆盖工艺管理核心流程
-
支持BOM管理、工序规划
-
集成质量控制和设备管理
-
-
易于使用:
-
直观的界面设计
-
简化的操作流程
-
详细的帮助文档
-
-
成本效益:
-
开源技术栈,无许可费用
-
低维护成本
-
可扩展性强
-
7.2 适用场景
|
企业类型 |
应用场景 |
预期效果 |
|---|---|---|
|
小型加工厂 |
单机部署,工艺数据管理 |
提升工艺规范性,减少错误 |
|
中型制造企业 |
局域网部署,多部门协同 |
实现数据共享,提高协作效率 |
|
大型企业车间 |
作为ERP补充,精细化管理 |
填补系统空白,深化车间管理 |
|
初创企业 |
低成本信息化起步 |
快速建立管理体系,为发展奠定基础 |
八、总结
本文详细介绍了如何使用SQLSugar构建一个微型工艺管理系统,该系统具有以下特点:
-
技术选型合理:SQLSugar + .NET 6 + WinForm/Vue,平衡了性能与开发效率
-
架构清晰:分层设计,模块化开发,便于维护和扩展
-
功能实用:覆盖工艺管理核心需求,满足中小制造企业实际应用
-
部署简便:支持多种部署方式,适应不同规模企业需求
通过本系统的实施,企业可以实现:
-
工艺数据的规范化管理
-
生产过程的可视化监控
-
质量问题的快速追溯
-
生产资源的优化配置
微型工艺管理系统作为制造业数字化转型的入门级解决方案,可以帮助企业以较低的成本实现基本的信息化管理,为后续的系统升级和扩展奠定基础。
7790

被折叠的 条评论
为什么被折叠?



