5.27

一天时间,20+ 模块,从后端到前端,从业务规则引擎到地图可视化——记录一次高强度全栈开发之旅。


背景

工厂设备管理涉及工位、网格、巡检、维护、检测、备件、故障报修、人员调度等诸多环节。传统 Excel 管理方式效率低下且容易出错,一个统一的全生命周期管理系统是刚需。

我选择 FastAPI + 原生 JavaScript SPA 的架构,不引入前端框架,用最小依赖实现一个功能完整的工业管理系统。


技术栈

层级 技术
后端框架 FastAPI (Python)
ORM SQLAlchemy (async)
数据库 MySQL
前端 原生 JS SPA (零框架)
地图 Leaflet.js + 高德瓦片
AI DeepSeek Agent
消息 飞书 Webhook

一、后端基础设施

1.1 通用 CRUD 基类

所有实体共享同一个 CRUD 基类,自动处理分页、排序、搜索和关联查询:

class BaseCRUD:
    model: type[Base]
    
    async def list(self, page, page_size, order_by, filters) -> PaginatedResponse
    async def get(self, id) -> model
    async def create(self, data) -> model
    async def update(self, id, data) -> model
    async def delete(self, id) -> None

这样一来,新增一个模块只需要定义 Model、Schema、Router 三部分,CRUD 逻辑完全复用。

1.2 依赖解析器

实体之间存在复杂的引用关系(例如工位属于网格,网格属于位置),删除时不能简单地级联,而需要精确判断依赖。我实现了 DependencyResolver,维护一张 9 实体映射表:

  • 查询某个实体被哪些其他实体引用
  • 删除前自动检查是否有子依赖
  • 返回清晰的依赖树供前端展示

1.3 业务规则引擎

规则引擎采用可配置架构,每条规则定义为一个独立的 BusinessRule

class BusinessRule:
    code: str        # 规则编码, 如 "HAS_CHILDREN"
    message: str     # 用户可读的错误信息
    check: Callable  # 校验函数
    detail: dict     # 附加详情

已实现的规则包括:

  • DEPENDENCY_NOT_FOUND — 引用的外键实体不存在
  • HAS_CHILDREN — 实体仍有子项,拒绝删除
  • CIRCULAR_REFERENCE — 工艺路线中出现循环引用
  • INVALID_STATUS_TRANSITION — 工单状态变更不合法
  • STOCK_RANGE_INVALID — 备件库存超出安全范围

每个模块的创建、更新、删除操作都串联了对应的业务规则检查,在数据库层面之前拦截非法操作。


二、后端模块全景

系统共实现了 28 个 API 模块,涵盖工厂管理的方方面面:

基础数据模块

  • 工位管理 (Station) — 工位基本信息、经纬度坐标、状态
  • 网格管理 (Grid) — 厂区网格划分
  • 位置管理 (Location) — 车间/厂房层级结构

设备与备件模块

  • 设备分类 / 用途 / 制造商 — 设备主数据
  • 故障字典 — 标准化故障编码与描述
  • 备件库位 / 分类 / 型号 — 备件库存主数据

业务路线模块

  • 巡检路线 & 项目 — 巡检计划与检查项
  • 维护路线 & 项目 — 维护计划与作业项
  • 检测路线 & 项目 — 检测计划与测试项

工艺模块

  • 工艺定义 — 标准工艺流程
  • 工艺路线 — 工艺步骤与流转规则(含循环引用检测)

任务与工单模块

  • 巡检 / 维护 / 检测任务 — 任务生成、分配、跟踪
  • 故障报修单 — 报修、诊断、备件推荐、维修闭环
  • 问题报告 & 调度 — 问题上报与派工

人员与审批

  • 人员档案 & 分配 — 员工信息与任务分配
  • 审批中心 — 多类型审批流
  • 库存工作台 — 备件出入库、流水账

集成模块

  • AI Agent — 接入 DeepSeek,提供智能问答与故障诊断
  • 飞书通知 — 异步 Webhook,关键事件实时推送
  • 统计面板 — 多维度数据汇总
  • 导入导出 — Excel 批量导入(含模板下载与预览)

三、前端 SPA 架构

3.1 设计决策:零框架

没有使用 React/Vue。理由很简单:这是一个管理后台,交互模式高度统一(列表 → 表单 → 提交),不需要虚拟 DOM 的响应式能力。原生 JS 够用、够快、零构建步骤。

3.2 CSS 变量驱动的设计系统

采用暖色工业风主题:

:root {
  --primary: #e67e22;        /* 橙色主色调 */
  --bg: #f5f0eb;             /* 暖灰背景 */
  --card-bg: #ffffff;
  --text: #2c3e50;
  --border: #e0d5c7;
  --shadow: 0 2px 12px rgba(0,0,0,0.08);
}

3.3 SPA 路由

手写了一个轻量 Hash Router:

  • #/dashboard → 仪表盘
  • #/stations → 工位列表
  • #/stations/map → 工位地图
  • #/grids, #/locations, #/inspection-routes ... → 各模块列表

路由切换时动态加载对应的页面模块,保持单页体验。

3.4 可复用组件

抽象了 6 个通用组件,覆盖管理后台 90% 的交互场景:

组件 功能
DataTable 分页、搜索、排序、行操作
FormBuilder JSON Schema 驱动的表单生成
CascadeSelect 级联下拉(如:位置 → 网格 → 工位)
DependentsPanel 展示实体被哪些记录引用
Wizard 多步骤向导(如创建工单流程)
ImportComponent 文件上传、预览、确认导入

四、地图可视化 — 工位地图

这是整个系统视觉上最亮眼的功能。

4.1 瓦片选择

国内访问 OpenStreetMap 速度不稳定,因此配置了三级降级策略:

高德瓦片 (GCJ02) → ESRI 卫星图 → CartoDB 备用

4.2 坐标转换

高德使用 GCJ02 坐标系,GPS 使用 WGS84。我在前端实现了双向转换

function wgs84ToGcj02(lng, lat) { ... }
function gcj02ToWgs84(lng, lat) { ... }

编辑工位时存储 WGS84 坐标,地图展示时自动转为 GCJ02,保证国内地图精度。

4.3 交互功能

  • 点击标记 → 弹出详情卡片(名称、状态、所属网格、设备数)
  • 侧边栏 CRUD → 不离开地图页面完成增删改查
  • 飞入动画 → 列表点击工位,地图自动 flyTo 并高亮
  • 在线搜索 → 输入地址,调用地理编码 API 定位

五、AI Agent 集成

接入了 DeepSeek 大模型,提供两个核心能力:

  1. 故障诊断助手 — 输入故障现象,推荐可能的原因和替换备件
  2. 操作问答 — 回答系统使用问题,降低培训成本

Agent 服务层使用单例模式管理 HTTP 客户端,应用关闭时自动回收资源。


六、开发心得

批量 commit 的节奏感

这次开发共提交了 30+ 个 commit,每个 commit 粒度很小且语义明确:

  • feat: 新功能(如 "add business rules service layer")
  • fix: Bug 修复(如 "guard loadData against undefined module")
  • debug: 临时调试日志
  • chore: 工程配置

小步提交的好处是:出错时可以精确回滚到任意一个安全点,而不用面对一个巨大的 diff 无从下手。

业务规则在前

在写 CRUD 之前先写好规则引擎是正确的——这保证了所有模块的删除/更新行为一致,不会出现 A 模块做了依赖检查而 B 模块漏掉的情况。

原生 JS 并不痛苦

只要把组件抽象好,原生 JS 写管理后台的体验和框架没有本质区别。反而省去了构建配置、热更新、状态管理库的心智负担。


下一步计划


这是一次"全栈密度"极高的开发体验。从数据库设计到前端地图,从业务规则到 AI 集成,一天之内构建了一个可运行、可演示的完整系统。

posted @ 2026-05-27 22:01  douzishuo  阅读(28)  评论(0)    收藏  举报