尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Python工厂管理系统毕设实战:轻量MES架构与PLC通信实现
简介本资源是一套完整的Python工厂管理系统毕业设计项目面向计算机专业本科生及Python初学者解决中小型制造企业生产流程数字化管理需求。系统基于Tkinter构建图形界面集成库存管理、订单处理、生产调度、员工管理、数据库持久化SQLite/MySQL、权限控制与报表生成等核心功能覆盖GUI开发、数据库操作、面向对象编程与软件工程实践全过程。压缩包共4456个文件主体为1435个.py源码文件、760个.dat数据文件及60个.txt文档含测试与运维说明总大小36.89MB结构清晰便于按模块学习与调试。目前已有91人下载学习提供可直接运行的完整工程、详细功能注释、错误处理与日志机制实现、以及涵盖单元测试与部署流程的配套文档是GUI实战与毕设落地的高参考价值范例。1. 用 Python 搭建工厂管理系统不是写个 CRUD 就能交毕设——它得能管真实产线的物料、工单和库存变动很多计算机专业同学拿到“Python 工厂管理系统”这个毕设题目时第一反应是用 Flask 或 Django 写个带增删改查的网页连上 SQLite填几条模拟数据截图交差。但实际评审老师一眼就能看出问题——系统里没有工单状态流转逻辑库存扣减不校验可用量物料编码没按国标 GB/T 18354-2021 分层设计更别说与真实产线设备如 PLC 通信接口、扫码枪触发事件做轻量级对接。这类系统本质是离散制造场景下的轻量 MES制造执行系统子集核心不在界面多漂亮而在业务规则是否可配置、数据变更是否可追溯、异常操作是否留痕。适合本科毕设的实现路径是用 Python 构建分层清晰的后端服务非纯 Web 展示配合 SQLite 或 PostgreSQL 存储结构化业务数据并预留 OPC UA 或串口通信扩展点。本文不讲“怎么装 Python”而是聚焦如何让这个系统在答辩时经得起追问——比如“工单超期怎么预警”“BOM 展开时循环引用怎么检测”“同一物料在不同车间库存怎么隔离”。所有代码均可本地复现无需云服务或第三方 SaaS。2. 用 Flask SQLAlchemy 实现可追溯的工单与库存双模型驱动架构2.1 为什么选 Flask 而非 Django——轻量可控才是毕设落地的关键Django 自带 Admin 和 ORM 迁移工具看似省事但其强约定模式会让毕设系统快速陷入“改不动”的境地比如默认 User 模型无法直接关联车间班组权限粒度只能到 Model 级而非“某车间组长仅能审核本车间工单”。Flask 则完全由你定义路由、中间件和数据流向。更重要的是Flask 的 WSGI 应用对象app Flask(__name__)可直接被pytest加载测试方便你在答辩前跑通全部业务流程断言——例如验证“创建工单后库存预占是否生效”“工单状态从‘已派工’变‘已完成’时是否自动释放预占量”。这种可测试性是毕设答辩中证明“代码真跑得通”的硬证据。而 SQLAlchemy 不仅支持 SQLite开发调试零部署成本也兼容 PostgreSQL答辩演示时换数据库只需改一行连接字符串且其 declarative_base 定义的模型类天然支持版本控制字段如updated_at、version为后续审计日志打下基础。2.2 工单模型必须包含状态机与时间戳链不能只存 status 字段工厂现场工单绝非简单“待处理/进行中/已完成”三态。真实流程包含新建 → 技术审核 → 物料齐套检查 → 已派工 → 首件检验 → 批量生产 → 成品入库 → 关闭。其中“物料齐套检查”失败需退回“技术审核”“首件检验”不合格则进入“返工”分支。若用字符串字段存储状态极易出现非法流转如跳过审核直接派工。正确做法是定义状态枚举并绑定转移规则# models.py from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Enum, Boolean from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship from datetime import datetime import enum class WorkOrderStatus(enum.Enum): DRAFT draft # 新建 TECH_REVIEW tech_review # 技术审核 MATERIAL_CHECK material_check # 物料齐套检查 ASSIGNED assigned # 已派工 FIRST_PIECE first_piece # 首件检验 PRODUCTION production # 批量生产 IN_STOCK in_stock # 成品入库 CLOSED closed # 关闭 Base declarative_base() class WorkOrder(Base): __tablename__ work_orders id Column(Integer, primary_keyTrue) order_no Column(String(20), uniqueTrue, nullableFalse) # 工单号如 WO20240001 product_code Column(String(30), nullableFalse) # 产品编码 quantity Column(Integer, nullableFalse) # 计划数量 status Column(Enum(WorkOrderStatus), defaultWorkOrderStatus.DRAFT) created_at Column(DateTime, defaultdatetime.utcnow) updated_at Column(DateTime, defaultdatetime.utcnow, onupdatedatetime.utcnow) version Column(Integer, default1) # 乐观锁版本号 # 关联物料需求清单BOM bom_items relationship(BomItem, back_populateswork_order) # 状态变更历史关键答辩时可展示 status_logs relationship(StatusLog, back_populateswork_order) class StatusLog(Base): __tablename__ status_logs id Column(Integer, primary_keyTrue) work_order_id Column(Integer, ForeignKey(work_orders.id)) from_status Column(Enum(WorkOrderStatus)) to_status Column(Enum(WorkOrderStatus)) operator Column(String(50)) # 操作人姓名或工号 remark Column(String(200)) # 变更原因如“首件检验合格” created_at Column(DateTime, defaultdatetime.utcnow) work_order relationship(WorkOrder, back_populatesstatus_logs)提示version字段用于防止并发修改冲突。当两个用户同时编辑同一工单时SQLAlchemy 会检查version是否匹配不匹配则抛出StaleDataError避免库存扣减覆盖。此细节常被毕设忽略却是工业系统可靠性基石。2.3 库存模型必须支持多维度隔离与事务级扣减工厂库存不是“总共有多少”而是“某仓库、某库位、某批次、某状态下的可用量”。例如A 产品在 1 号仓库 A 区 1 排 2 层有 100 件状态良品另有 20 件在质检区状态待检。若工单消耗 30 件系统必须优先从“良品”状态中扣减且需记录扣减来源批次用于质量追溯。因此库存表需拆解为Inventory总账和InventoryDetail明细# models.py 续 class Inventory(Base): __tablename__ inventory id Column(Integer, primary_keyTrue) material_code Column(String(30), nullableFalse) # 物料编码 warehouse_code Column(String(20), nullableFalse) # 仓库编码 total_quantity Column(Integer, default0) # 当前总数量冗余字段加速查询 available_quantity Column(Integer, default0) # 可用数量已扣除预占、冻结等 updated_at Column(DateTime, defaultdatetime.utcnow, onupdatedatetime.utcnow) class InventoryDetail(Base): __tablename__ inventory_details id Column(Integer, primary_keyTrue) inventory_id Column(Integer, ForeignKey(inventory.id)) batch_no Column(String(50)) # 批次号如 20240501-A status Column(String(20), defaultgood) # 状态good/inspection/hold/scrap quantity Column(Integer, default0) # 该批次该状态下数量 location Column(String(50)) # 库位如 A-1-2 produced_date Column(DateTime) # 生产日期 expiry_date Column(DateTime) # 有效期对化工/食品类物料关键 inventory relationship(Inventory, backrefdetails)2.3.1 扣减库存必须用数据库事务保证原子性工单状态变为ASSIGNED时需预占物料变为IN_STOCK时需扣减原材料并增加成品库存。这些操作必须在一个数据库事务中完成否则会出现“工单已派工但库存未预占”的数据不一致。以下为预占逻辑示例# services/inventory_service.py from sqlalchemy import text from models import db, Inventory, InventoryDetail, WorkOrder def reserve_materials(work_order: WorkOrder, operator: str): 为工单预占所需物料事务内执行 try: with db.session.begin_nested(): # 使用嵌套事务避免影响外层 for bom_item in work_order.bom_items: # 查找该物料在指定仓库的可用明细 detail db.session.execute( text( SELECT id, quantity, status, location FROM inventory_details WHERE inventory_id IN ( SELECT id FROM inventory WHERE material_code :mat_code AND warehouse_code :wh_code ) AND status good ORDER BY produced_date ASC LIMIT 1 ), {mat_code: bom_item.material_code, wh_code: bom_item.warehouse_code} ).fetchone() if not detail or detail.quantity bom_item.required_quantity: raise ValueError(f物料 {bom_item.material_code} 在 {bom_item.warehouse_code} 库存不足) # 更新明细数量 db.session.execute( text(UPDATE inventory_details SET quantity quantity - :qty WHERE id :id), {qty: bom_item.required_quantity, id: detail.id} ) # 更新总账可用量 db.session.execute( text(UPDATE inventory SET available_quantity available_quantity - :qty WHERE id :inv_id), {qty: bom_item.required_quantity, inv_id: detail[0]} # detail.id 是第一个字段 ) # 记录预占日志 db.session.add(InventoryLog( actionreserve, material_codebom_item.material_code, quantitybom_item.required_quantity, work_order_idwork_order.id, operatoroperator, detail_iddetail.id )) db.session.commit() return True except Exception as e: db.session.rollback() raise e注意此处使用原生 SQL 而非 ORM 查询是因为ORDER BY produced_date ASC对 FIFO先进先出扣减至关重要而 SQLAlchemy ORM 的order_by()在子查询中易产生性能问题。毕设中宁可写稍复杂的 SQL也要确保业务逻辑正确。3. 用 PySerial 和 Modbus RTU 实现与产线 PLC 的轻量级数据交互3.1 为什么毕设必须接入真实设备信号——脱离产线的系统只是电子表格评审老师最常问“你的系统怎么知道工单真的开始生产了” 如果答案是“我点个按钮模拟”那和 Excel 没区别。真正有价值的毕设是让 Python 程序能读取 PLC 的寄存器值——例如 M100.0 为 1 表示“1 号工位启动”D200 寄存器值为 123 表示“当前工单号”。这不需要复杂网关用 USB 转 RS485 适配器 PySerial 即可实现。主流国产 PLC如汇川 H3U、信捷 XC3均支持 Modbus RTU 协议通信参数固定为9600 波特率、8 数据位、1 停止位、无校验。3.2 用 minimalmodbus 读取 PLC 线圈与寄存器5 行代码搞定握手minimalmodbus是专为工业协议设计的轻量库比pymodbus更易上手且内存占用低适合毕设环境。安装命令pip install minimalmodbus pyserial以下代码每 2 秒轮询一次 PLC 的 M100.0 线圈位地址和 D200 寄存器字地址一旦检测到变化即触发工单状态更新# plc_monitor.py import minimalmodbus import time from models import db, WorkOrder from services.work_order_service import update_work_order_status # 配置 PLC 设备COM3 为 Windows 下串口号/dev/ttyUSB0 为 Linux instrument minimalmodbus.Instrument(/dev/ttyUSB0, 1) # 从站地址 1 instrument.serial.baudrate 9600 instrument.serial.bytesize 8 instrument.serial.parity minimalmodbus.serial.PARITY_NONE instrument.serial.stopbits 1 instrument.serial.timeout 0.5 # 读取超时 500ms def monitor_plc(): last_start_flag False last_work_order_no 0 while True: try: # 读取 M100.0位地址对应线圈 0x0000 start_flag instrument.read_bit(0, functioncode1) # 功能码 1 读线圈 # 读取 D200字地址对应保持寄存器 0x00C8 work_order_no_raw instrument.read_register(200, functioncode3) # 功能码 3 读保持寄存器 # 处理信号变化 if start_flag and not last_start_flag: # 检测到启动沿查找对应工单 work_order WorkOrder.query.filter_by(order_nofWO{work_order_no_raw:06d}).first() if work_order and work_order.status WorkOrderStatus.ASSIGNED: update_work_order_status(work_order, WorkOrderStatus.PRODUCTION, PLC 启动信号触发) print(f[PLC] 工单 {work_order.order_no} 进入生产状态) last_start_flag start_flag last_work_order_no work_order_no_raw except Exception as e: print(f[PLC] 通信异常: {e}) time.sleep(2) # 每 2 秒轮询一次 if __name__ __main__: monitor_plc()3.2.1 关键参数说明与常见故障排查参数说明毕设调试要点functioncode1读线圈Coil返回布尔值若返回False但 PLC 上灯亮检查接线是否反接A/B 线functioncode3读保持寄存器Holding Register返回整数D200 对应寄存器地址为 200不是 0x00C8hex直接传入timeout0.5必须设小值否则卡死主线程若频繁超时用万用表测 RS485 A/B 间电压正常应为 ±1.5V~±6Vinstrument.serial.baudrate必须与 PLC 设置完全一致汇川 PLC 默认 9600信捷默认 19200务必查手册提示答辩演示时可准备一个 Arduino 模拟 PLC 发送 Modbus 信号用ModbusMaster库避免依赖真实设备。这样即使实验室没 PLC也能证明通信能力。4. 用 Pandas Matplotlib 实现车间级产能分析看板让数据说话4.1 毕设答辩不展示数据价值等于没做数据分析单纯展示“工单列表”“库存报表”只是信息陈列。真正的价值在于回答管理问题哪个车间月度准时交付率最低哪类物料缺料频次最高工单平均在制品WIP天数是否超标这些问题需要聚合多表数据并可视化。Pandas 是 Python 生态最成熟的结构化数据处理库Matplotlib 则提供可导出 PNG 的静态图表——无需前端框架一行命令即可生成答辩用图。4.2 从数据库提取工单周期数据计算准时交付率OTD准时交付率 按时完工工单数 / 总完工工单数× 100%。关键在于“按时”的定义合同交期 vs 计划完工日。毕设中采用计划完工日更合理因合同数据难模拟。以下代码从work_orders表提取近 30 天数据按车间分组计算 OTD# analytics/otd_analyzer.py import pandas as pd from datetime import datetime, timedelta from models import db, WorkOrder, Workshop def calculate_otd_by_workshop(days30): 计算各车间近 N 天准时交付率 cutoff_date datetime.now() - timedelta(daysdays) # 从数据库获取完工工单数据SQLAlchemy 原生查询避免 ORM 开销 sql SELECT w.name as workshop_name, COUNT(*) as total_closed, COUNT(CASE WHEN wo.actual_finish_time wo.planned_finish_time THEN 1 END) as on_time_count FROM work_orders wo JOIN workshops w ON wo.workshop_id w.id WHERE wo.status closed AND wo.actual_finish_time :cutoff GROUP BY w.name df pd.read_sql(sql, db.engine, params{cutoff: cutoff_date}) # 计算 OTD 并排序 df[otd_rate] (df[on_time_count] / df[total_closed] * 100).round(2) df df.sort_values(otd_rate, ascendingFalse) return df # 生成柱状图 def plot_otd_chart(df): import matplotlib.pyplot as plt plt.figure(figsize(10, 6)) bars plt.bar(df[workshop_name], df[otd_rate], color#2E86AB, alpha0.8) # 在柱子上方显示数值 for bar, rate in zip(bars, df[otd_rate]): plt.text(bar.get_x() bar.get_width()/2, bar.get_height() 0.5, f{rate}%, hacenter, vabottom, fontsize12) plt.title(f各车间近 {len(df)} 天准时交付率OTD, fontsize14, fontweightbold) plt.ylabel(OTD (%), fontsize12) plt.ylim(0, 105) plt.grid(axisy, linestyle--, alpha0.7) plt.xticks(rotation15) # 保存为文件供答辩插入 PPT plt.savefig(otd_chart.png, dpi300, bbox_inchestight) plt.show() # 使用示例 if __name__ __main__: df calculate_otd_by_workshop(30) print(df) plot_otd_chart(df)4.2.1 图表优化技巧让答辩老师一眼抓住重点颜色统一全图使用企业色系如深蓝 #2E86AB 浅灰 #A2A2A2避免彩虹色干扰。数值标注柱状图顶部必须显示具体百分比否则评委需自己估算。坐标轴截断plt.ylim(0, 105)防止空白区域过大突出数据差异。字体大小标题 14pt坐标轴标签 12pt确保投影后清晰可读。4.3 用 Seaborn 做工单周期分布热力图暴露瓶颈工序单纯看平均周期会掩盖问题。例如 A 车间平均 5 天但 70% 工单 2 天完工30% 却要 15 天——说明存在严重瓶颈。热力图可直观展示“不同产品类型 × 不同车间”的周期分布# analytics/cycle_time_heatmap.py import seaborn as sns import numpy as np def plot_cycle_time_heatmap(): # 模拟数据实际应从数据库查询 data { product_type: [A, B, C, D], workshop: [装配一车间, 装配二车间, 喷涂车间, 包装车间], avg_cycle_days: [[3.2, 4.1, 5.8, 2.9], [3.5, 4.3, 6.1, 3.0], [2.8, 3.9, 5.2, 2.7], [2.1, 2.5, 3.8, 1.9]] } plt.figure(figsize(10, 8)) sns.heatmap( data[avg_cycle_days], xticklabelsdata[product_type], yticklabelsdata[workshop], annotTrue, fmt.1f, cmapYlOrRd, # 黄→橙→红越红表示周期越长 cbar_kws{label: 平均周期天} ) plt.title(各产品类型在不同车间的平均生产周期天, fontsize14) plt.xlabel(产品类型) plt.ylabel(车间) plt.savefig(cycle_heatmap.png, dpi300, bbox_inchestight) plt.show()注意热力图中cmapYlOrRd比默认viridis更符合工业场景认知红色代表风险/延迟且annotTrue强制显示数值避免评委猜测。5. 毕设部署与答辩话术用 Docker 封装环境用日志证明系统健壮性5.1 用 Docker Compose 一键启动全栈服务杜绝“在我机器上能跑”争议答辩现场最尴尬的莫过于“老师您稍等我装一下 Python 环境……”。Docker 是唯一能彻底解决环境差异的方案。毕设无需复杂编排一个docker-compose.yml文件即可启动 Flask 后端、SQLite 数据库或 PostgreSQL、以及前端静态文件服务# docker-compose.yml version: 3.8 services: web: build: . ports: - 5000:5000 environment: - DATABASE_URLsqlite:////app/app.db - FLASK_ENVproduction depends_on: - db volumes: - ./data:/app/data # 持久化 SQLite 文件 db: image: postgres:15-alpine environment: - POSTGRES_DBfactory_db - POSTGRES_USERapp - POSTGRES_PASSWORDsecret volumes: - pgdata:/var/lib/postgresql/data volumes: pgdata:对应的Dockerfile极简仅安装必要依赖FROM python:3.10-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD [gunicorn, --bind, 0.0.0.0:5000, --workers, 2, app:app]提示答辩前务必在老师电脑Windows/macOS上用 Docker Desktop 测试一键启动。若学校禁用 Docker退而求其次用venvrequirements.txt但必须提供pip install -r requirements.txt后立即运行python app.py的完整终端录屏。5.2 用结构化日志替代 print让异常可追溯、可统计毕设系统上线后任何print(库存扣减成功)都是无效信息。必须用logging模块输出结构化日志包含时间、模块、行号、业务上下文# app.py 全局日志配置 import logging from logging.handlers import RotatingFileHandler def setup_logging(): handler RotatingFileHandler(factory.log, maxBytes10*1024*1024, backupCount5) formatter logging.Formatter( %(asctime)s | %(levelname)-8s | %(name)s:%(lineno)d | %(message)s, datefmt%Y-%m-%d %H:%M:%S ) handler.setFormatter(formatter) root_logger logging.getLogger() root_logger.setLevel(logging.INFO) root_logger.addHandler(handler) # 同时输出到控制台开发时用 console logging.StreamHandler() console.setFormatter(formatter) root_logger.addHandler(console) setup_logging() logger logging.getLogger(__name__) # 在库存扣减函数中 def reserve_materials(...): logger.info(f开始预占工单 {work_order.order_no} 物料操作人 {operator}) try: # ... 扣减逻辑 logger.info(f工单 {work_order.order_no} 物料预占成功消耗 {total_qty} 件) except ValueError as e: logger.error(f工单 {work_order.order_no} 预占失败{str(e)}, exc_infoTrue)5.2.1 答辩时如何用日志赢得信任展示日志文件打开factory.log指出某次工单状态变更对应的完整日志链从接收请求 → 校验 → 扣减 → 记录 → 返回。搜索关键词用grep ERROR factory.log快速证明系统异常捕获能力。统计频率grep reserve factory.log | wc -l显示预占操作总次数佐证系统已稳定运行。最终一个合格的 Python 工厂管理系统毕设不是“能跑起来”而是每个模块都有明确工业语义、每次数据变更都可回溯、每处异常都有结构化日志、每个结论都有图表支撑。把这四点做到位答辩时自然底气十足。本文还有配套的精品资源点击获取
RELATED

相关推荐

在 PostHog 中为新产品编写 ClickHouse 查询:从 HogQL 到索引与性能优化的完整工程实践

在 PostHog 中为新产品编写 ClickHouse 查询:从 HogQL 到索引与性能优化的完整工程实践

在 PostHog 中为新产品编写 ClickHouse 查询:从 HogQL 到索引与性能优化的完整工程实践 【免费下载链接】posthog :hedgehog: PostHog is the leading platform for building self-driving products. Our developer tools – AI observability, analytics, session …

📅 2026/9/12 20:43:37
Label Studio Python SDK 集成指南:将数据标注无缝接入机器学习流水线

Label Studio Python SDK 集成指南:将数据标注无缝接入机器学习流水线

Label Studio Python SDK 集成指南:将数据标注无缝接入机器学习流水线 【免费下载链接】label-studio Label Studio is a multi-type data labeling and annotation tool with standardized output format 项目地址: https://gitcode.com/GitHub_Trending/la/labe…

📅 2026/9/12 20:43:37
太原本地 APP 开发哪家的需求沟通响应效率更高?

太原本地 APP 开发哪家的需求沟通响应效率更高?

太原企业在定制企业APP、ERP管理系统、物联网管控系统时,常遇到沟通痛点:外包团队对接频繁换人、响应滞后、复杂业务理解偏差,尤其ERP流程管控、物联网设备数据对接这类定制项目,需求复杂、环节多,沟通失误极易导致返工…

📅 2026/9/12 20:43:37
MORE NEWS

更多资讯

📰

AI-Scientist 使用指南:让 AI 科研自动化平台从想法一路跑到完整论文

AI-Scientist 使用指南:让 AI 科研自动化平台从想法一路跑到完整论文 【免费下载链接】AI-Scientist The AI Scientist: Towards Fully Automated Open-Ended Scientific Discovery 🧑‍🔬 项目地址: https://gitcode.com/GitHub_Trending/…

📰

USART串口通讯

首先讲解一下通信基础知识可以把通信方式拆成三个互相独立的问题来看:一次传几位? → 串行 / 并行能不能双向、能不能同时? → 单工 / 半双工 / 全双工接收方怎么知道每一位什么时候采? → 同步 / 异步这三者不是互斥的&#xff0…

📰

扫码点餐运营版V4.7.0模块化部署与验证指南

简介:扫码点餐运营版V4.7.0原版模块打包是一款面向中小型餐饮企业及开发者的技术资源,聚焦于落地部署高可用、可扩展的扫码点餐系统,解决传统纸质菜单效率低、卫生风险高、运营数据难沉淀等痛点,适用于堂食数字化升级、连锁门店统…

📰

北斗三号规模化应用下,电力行业定位能力如何升级?

电力系统点多、线长、面广:输电线路跨越山区林区,变电站分布城乡各处,抢修队伍常常要在公网通信薄弱的区域作业。一旦发生自然灾害或突发事件,公网通信中断时,作业队伍的位置上报与调度指挥就成了现实难题。 正因如此&…

📰

虚竹哥与北京大学出版社免费送AI书活动第九期:《动手打造AI Agent:零基础开发智能体指南》

前言别再把“调 API”当 Agent 开发了:这本书讲透了如何把智能体真正做成软件 这两年,AI Agent 火得有点夸张。 从最早的 ChatGPT Prompt,到 RAG、Function Calling,再到今天的 MCP、多 Agent、任务规划、自我反思……几乎每隔一…

📰

C51单片机TCS3200颜色识别与LCD1602显示工程解析

简介:这是一套基于C51单片机的TCS3200颜色识别与LCD1602显示完整软件源码,面向正在学习单片机接口编程、传感器数据采集与显示驱动的开发者,尤其适合课程设计或电子竞赛的软硬件联调阶段。代码中main函数完成系统初始化后开启总中断&#xff…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬