SQLSugar实现微型工艺管理系统

2025博客之星年度评选已开启 10w+人浏览 1.6k人参与

本文为实战经验分享,介绍如何使用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 核心优势

  1. 轻量级架构

    • 单文件数据库(SQLite)支持

    • 低资源消耗,适合老旧硬件

    • 无需复杂部署环境

  2. 功能完备

    • 覆盖工艺管理核心流程

    • 支持BOM管理、工序规划

    • 集成质量控制和设备管理

  3. 易于使用

    • 直观的界面设计

    • 简化的操作流程

    • 详细的帮助文档

  4. 成本效益

    • 开源技术栈,无许可费用

    • 低维护成本

    • 可扩展性强

7.2 适用场景

企业类型

应用场景

预期效果

小型加工厂

单机部署,工艺数据管理

提升工艺规范性,减少错误

中型制造企业

局域网部署,多部门协同

实现数据共享,提高协作效率

大型企业车间

作为ERP补充,精细化管理

填补系统空白,深化车间管理

初创企业

低成本信息化起步

快速建立管理体系,为发展奠定基础

八、总结

本文详细介绍了如何使用SQLSugar构建一个微型工艺管理系统,该系统具有以下特点:

  1. 技术选型合理:SQLSugar + .NET 6 + WinForm/Vue,平衡了性能与开发效率

  2. 架构清晰:分层设计,模块化开发,便于维护和扩展

  3. 功能实用:覆盖工艺管理核心需求,满足中小制造企业实际应用

  4. 部署简便:支持多种部署方式,适应不同规模企业需求

通过本系统的实施,企业可以实现:

  • 工艺数据的规范化管理

  • 生产过程的可视化监控

  • 质量问题的快速追溯

  • 生产资源的优化配置

微型工艺管理系统作为制造业数字化转型的入门级解决方案,可以帮助企业以较低的成本实现基本的信息化管理,为后续的系统升级和扩展奠定基础。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

code_shenbing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值