电力设备管理系统设计参考:从台账、扫码巡检到缺陷闭环的落地实践
简介这份资源面向电力行业信息化开发者与电气工程专业学习者提供一套电力设备管理系统的参考实现帮助理解设备全生命周期管理的软件架构与功能落地。压缩包共65个文件约1.47MB以C源码为主体含19个h头文件与16个cpp实现文件另有bmp、ico等界面位图与图标资源、rc/rc2资源脚本、dsw/dsp工程文件及txt说明文档构成一个可直接用VC打开编译的完整工程。内容覆盖设备资产管理、状态监测、维护检修计划、工单派发、备件库存、数据报表、安全权限、合同与供应商管理以及与SCADA、ERP、GIS等系统的集成思路并涉及移动端巡检报修场景。已有225人学习下载适合作为课程设计、毕业设计或企业原型开发的参考蓝本读者可从中获取模块划分方式、数据库结构设计与界面交互实现等可复用经验快速搭建自己的设备管理应用框架。1. 电力设备管理系统参考从台账混乱到扫码巡检的落地路径如果你在变电站、配电房或者工业厂区做过设备管理大概率经历过这样的场景台账靠 Excel 维护巡检靠纸质表格设备到了检修周期没人提醒出了故障翻半天找不到上次的试验报告。这套「电力设备管理系统参考」就是冲着这些具体问题来的——它是一份可落地的系统设计参考覆盖设备台账、巡检任务、缺陷管理、检修计划、备品备件几个核心模块适合电力运维班组、工业企业的设备科以及正在做类似管理系统的开发人员。我拿到这份参考后重点拆了它的数据模型和巡检流程设计下面把能直接抄的部分和容易翻车的地方都摊开讲。2. 设备台账与巡检任务的数据模型先定表结构再写代码2.1 设备台账的核心字段与层级设计设备台账是整个系统的地基。很多团队一上来就急着画页面结果表结构改了三版数据迁移做了两轮。这份参考里给了一个比较务实的做法用「位置层级 设备类型 设备实例」三层结构来组织。位置层级解决的是「设备在哪」——变电站、配电室、开关柜、具体间隔这是一棵树。设备类型解决的是「这是什么」——变压器、断路器、避雷器、互感器这是一张类型表。设备实例才是真正的台账记录它同时挂到位置节点和类型上。核心表结构大致是这样-- 位置层级表支持无限级树形结构 CREATE TABLE t_location ( id BIGINT PRIMARY KEY AUTO_INCREMENT, parent_id BIGINT DEFAULT 0 COMMENT 父节点ID0为根, loc_code VARCHAR(64) NOT NULL COMMENT 位置编码如 BDZ-01, loc_name VARCHAR(128) NOT NULL COMMENT 位置名称, loc_level TINYINT NOT NULL COMMENT 层级1变电站 2配电室 3开关柜, sort_order INT DEFAULT 0, UNIQUE KEY uk_loc_code (loc_code) ) COMMENT 位置层级表; -- 设备类型表定义设备分类和属性模板 CREATE TABLE t_device_type ( id BIGINT PRIMARY KEY AUTO_INCREMENT, type_code VARCHAR(64) NOT NULL COMMENT 类型编码如 TRANSFORMER, type_name VARCHAR(128) NOT NULL COMMENT 类型名称, attr_template JSON COMMENT 属性模板定义该类型设备的扩展字段, UNIQUE KEY uk_type_code (type_code) ) COMMENT 设备类型表; -- 设备台账主表 CREATE TABLE t_device ( id BIGINT PRIMARY KEY AUTO_INCREMENT, device_code VARCHAR(64) NOT NULL COMMENT 设备唯一编码, device_name VARCHAR(128) NOT NULL COMMENT 设备名称, type_id BIGINT NOT NULL COMMENT 设备类型ID, location_id BIGINT NOT NULL COMMENT 位置ID, model_spec VARCHAR(256) COMMENT 型号规格, manufacturer VARCHAR(128) COMMENT 生产厂家, commission_date DATE COMMENT 投运日期, status TINYINT DEFAULT 1 COMMENT 状态1运行 2检修 3停用 4报废, qr_code VARCHAR(256) COMMENT 二维码内容, ext_attrs JSON COMMENT 扩展属性按类型模板填充, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_device_code (device_code), KEY idx_location (location_id), KEY idx_type (type_id) ) COMMENT 设备台账主表;这里有几个设计取舍值得说清楚。ext_attrs用 JSON 字段而不是给每种设备类型建一张扩展表好处是加新设备类型不用改表结构坏处是没法对扩展属性建索引做精确查询。我一般会建议如果扩展属性里有关键的查询字段比如变压器的容量等级单独抽一列出来纯展示用的参数放 JSON 里。qr_code字段存的是二维码的内容通常是设备编码加一个校验串。生成二维码的时候不要只放设备 ID否则别人扫出来猜一下就能遍历你的设备数据。常见做法是device_code - md5(device_code salt)扫码后服务端校验。2.2 巡检任务与巡检记录的关联设计巡检模块的难点不在任务本身而在「计划任务」和「实际执行」的分离。很多系统把这两个揉在一张表里结果就是计划改了历史记录跟着变或者一条计划对应多次执行查询的时候要写一堆 UNION。这份参考的做法是拆成三张表巡检计划、巡检任务、巡检记录。-- 巡检计划定义什么时候、对哪些设备、按什么路线巡检 CREATE TABLE t_inspect_plan ( id BIGINT PRIMARY KEY AUTO_INCREMENT, plan_name VARCHAR(128) NOT NULL COMMENT 计划名称, plan_type TINYINT NOT NULL COMMENT 1日常巡检 2专业巡检 3特殊巡检, cron_expr VARCHAR(64) COMMENT 调度表达式如 0 0 8 * * ?, location_ids JSON COMMENT 覆盖的位置节点ID列表, device_ids JSON COMMENT 覆盖的设备ID列表为空则按位置下所有设备, items JSON COMMENT 巡检项模板如 [{name:油温,type:number,min:-20,max:80}], status TINYINT DEFAULT 1 COMMENT 1启用 0停用, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) COMMENT 巡检计划表; -- 巡检任务由计划生成的一次具体执行 CREATE TABLE t_inspect_task ( id BIGINT PRIMARY KEY AUTO_INCREMENT, plan_id BIGINT NOT NULL COMMENT 关联的计划ID, task_code VARCHAR(64) NOT NULL COMMENT 任务编号, executor_id BIGINT COMMENT 执行人ID, plan_start DATETIME NOT NULL COMMENT 计划开始时间, plan_end DATETIME NOT NULL COMMENT 计划结束时间, actual_start DATETIME COMMENT 实际开始时间, actual_end DATETIME COMMENT 实际结束时间, task_status TINYINT DEFAULT 0 COMMENT 0待执行 1执行中 2已完成 3已超期 4已取消, UNIQUE KEY uk_task_code (task_code), KEY idx_plan (plan_id), KEY idx_executor (executor_id) ) COMMENT 巡检任务表; -- 巡检记录每个设备每个巡检项的实测值 CREATE TABLE t_inspect_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, task_id BIGINT NOT NULL COMMENT 关联任务ID, device_id BIGINT NOT NULL COMMENT 设备ID, item_name VARCHAR(64) NOT NULL COMMENT 巡检项名称, item_value VARCHAR(256) COMMENT 实测值, item_result TINYINT COMMENT 1正常 2异常 3未检, remark VARCHAR(512) COMMENT 备注, inspect_time DATETIME COMMENT 巡检时间, KEY idx_task (task_id), KEY idx_device (device_id) ) COMMENT 巡检记录表;这个拆法的好处是计划可以随时调整不影响已经生成的任务任务可以追溯执行人和时间记录可以按设备维度做历史趋势分析。items字段用 JSON 存巡检项模板每个巡检项定义名称、类型数值/枚举/文本、正常范围。生成任务的时候把模板展开成具体的记录行。有个细节要注意t_inspect_record里没有存巡检项的定义只存了名称和值。如果巡检项模板改了历史记录里的名称不会变这是对的——历史就是历史。但如果你想做「同一设备同一巡检项的历史趋势」就得靠item_name来关联所以命名要规范不能今天叫「油温」明天叫「变压器油温」。3. 扫码巡检与缺陷闭环从扫码到工单的完整链路3.1 二维码生成与扫码解析的实现扫码巡检是这套系统里使用频率最高的功能。设备上贴二维码巡检人员到现场用手机扫一下自动带出设备信息和巡检项填完提交。听起来简单但实际落地时有几个坑。先说二维码生成。后端生成二维码内容前端渲染成图片打印。常见做法是用 Python 的qrcode库或者 Java 的zxing。内容格式建议用 JSON 的简化版不要用纯文本拼接import qrcode import json import hashlib def generate_device_qr(device_code: str, salt: str your_salt_here) - str: 生成设备二维码内容 :param device_code: 设备唯一编码 :param salt: 校验盐值服务端保存 :return: 二维码内容字符串 # 校验串防止设备编码被遍历猜测 check hashlib.md5(f{device_code}{salt}.encode()).hexdigest()[:8] payload { c: device_code, # 设备编码 k: check, # 校验串 v: 1 # 版本号方便后续升级 } # 内容尽量短二维码密度低更容易扫 content json.dumps(payload, separators(,, :)) qr qrcode.QRCode( versionNone, # 自动选择版本 error_correctionqrcode.constants.ERROR_CORRECT_M, # 15%容错 box_size10, border2 ) qr.add_data(content) qr.make(fitTrue) img qr.make_image(fill_colorblack, back_colorwhite) return img参数说明error_correction选 M 级别15% 容错就够了H 级别30%会让二维码更密贴在配电柜上反而不容易扫。box_size是每个小方块的像素数打印的时候根据实际尺寸调整一般贴在设备上的二维码边长 3-5 厘米比较合适。扫码解析端要做三件事校验k字段是否匹配、检查v版本号是否支持、根据c查询设备信息。校验不通过的直接拒绝不要给任何提示信息防止被用来探测设备编码是否存在。注意二维码贴上去之前一定要实测。我见过用热敏打印机打的二维码贴在户外配电箱上三个月就褪色扫不出来了。户外场景建议用金属铭牌激光打标或者 PET 材质的防水标签。3.2 缺陷登记与工单流转的状态机巡检发现异常下一步就是登记缺陷、派工单、处理、验收、闭环。这条链路如果没有状态机约束很容易出现「工单已关闭但缺陷还在」「处理人改了但记录没留痕」这类问题。这份参考里给的状态流转是这样的当前状态允许操作目标状态操作角色待确认确认缺陷待派工运维班长待确认驳回已关闭运维班长待派工指派处理人处理中运维班长处理中提交处理结果待验收处理人处理中申请延期处理中处理人待验收验收通过已闭环运维班长待验收验收驳回处理中运维班长状态流转的代码实现关键是在每次变更时记录操作日志from datetime import datetime from enum import IntEnum class DefectStatus(IntEnum): PENDING_CONFIRM 0 # 待确认 PENDING_ASSIGN 1 # 待派工 PROCESSING 2 # 处理中 PENDING_ACCEPT 3 # 待验收 CLOSED 4 # 已闭环 REJECTED 5 # 已驳回 # 允许的状态迁移映射 TRANSITIONS { DefectStatus.PENDING_CONFIRM: [DefectStatus.PENDING_ASSIGN, DefectStatus.REJECTED], DefectStatus.PENDING_ASSIGN: [DefectStatus.PROCESSING], DefectStatus.PROCESSING: [DefectStatus.PENDING_ACCEPT, DefectStatus.PROCESSING], DefectStatus.PENDING_ACCEPT: [DefectStatus.CLOSED, DefectStatus.PROCESSING], } def transition_defect(defect_id: int, target_status: DefectStatus, operator_id: int, remark: str ): 缺陷状态流转 :param defect_id: 缺陷ID :param target_status: 目标状态 :param operator_id: 操作人ID :param remark: 操作备注 defect get_defect(defect_id) current DefectStatus(defect.status) # 校验迁移是否合法 if target_status not in TRANSITIONS.get(current, []): raise ValueError(f不允许从 {current.name} 迁移到 {target_status.name}) # 更新状态 defect.status target_status defect.updated_at datetime.now() save_defect(defect) # 记录操作日志这是追溯的关键 save_defect_log( defect_iddefect_id, from_statuscurrent.value, to_statustarget_status.value, operator_idoperator_id, remarkremark, created_atdatetime.now() )这段代码的核心是TRANSITIONS字典它把业务规则固化在代码里而不是散落在各个接口里。新增状态或者调整流转规则只改这一个地方。save_defect_log记录每次变更后面查「这个缺陷为什么被驳回了」的时候直接看日志。实际落地时还有一个容易忽略的点并发操作。两个人同时打开同一个缺陷一个点「验收通过」一个点「验收驳回」如果不加锁后提交的会覆盖先提交的。常见做法是在更新时加乐观锁用updated_at或者版本号做条件更新。4. 检修计划与备品备件周期计算和库存联动4.1 检修周期的计算逻辑与提醒机制电力设备的检修周期通常按「运行时间」或者「日历时间」来算。比如变压器一般投运后 5 年做第一次大修之后每 10 年一次断路器按操作次数达到一定次数就要检修。这份参考里把检修周期分成了三种类型固定周期按投运日期加固定间隔比如「每 3 年一次」运行时长按累计运行小时数比如「每 20000 小时」动作次数按累计动作次数比如「每 2000 次」计算下次检修日期的时候固定周期最简单直接加就行。运行时长和动作次数需要从设备上采集数据或者人工录入。如果数据采集不完整系统算出来的日期就是错的所以这类周期一定要允许人工修正。提醒机制建议做两级提前 30 天生成待办提前 7 天升级提醒。不要一上来就天天弹窗运维人员会直接忽略。-- 检修计划表 CREATE TABLE t_maintain_plan ( id BIGINT PRIMARY KEY AUTO_INCREMENT, device_id BIGINT NOT NULL COMMENT 设备ID, maintain_type TINYINT NOT NULL COMMENT 1大修 2小修 3预防性试验, cycle_type TINYINT NOT NULL COMMENT 1固定周期 2运行时长 3动作次数, cycle_value INT NOT NULL COMMENT 周期值如36月或20000小时, last_date DATE COMMENT 上次检修日期, next_date DATE COMMENT 下次检修日期, last_reading DECIMAL(12,2) COMMENT 上次检修时的累计读数, current_reading DECIMAL(12,2) COMMENT 当前累计读数, status TINYINT DEFAULT 1 COMMENT 1启用 0停用, KEY idx_device (device_id), KEY idx_next_date (next_date) ) COMMENT 检修计划表;next_date这个字段是冗余的但值得冗余。因为查询「未来 30 天内需要检修的设备」时直接WHERE next_date BETWEEN ...就能走索引不用每次去算。代价是每次current_reading更新时要同步更新next_date。4.2 备品备件的库存扣减与预警备品备件模块的核心是库存扣减的时机和方式。很多系统在工单创建时就扣库存结果工单取消了还要手动加回来。更稳妥的做法是工单创建时只做「预占」实际领用时才扣减。-- 备件库存表 CREATE TABLE t_spare_part ( id BIGINT PRIMARY KEY AUTO_INCREMENT, part_code VARCHAR(64) NOT NULL COMMENT 备件编码, part_name VARCHAR(128) NOT NULL COMMENT 备件名称, spec VARCHAR(256) COMMENT 规格型号, unit VARCHAR(16) COMMENT 单位, total_qty INT DEFAULT 0 COMMENT 库存总量, locked_qty INT DEFAULT 0 COMMENT 预占数量, available_qty INT GENERATED ALWAYS AS (total_qty - locked_qty) STORED COMMENT 可用数量, min_qty INT DEFAULT 0 COMMENT 最低库存预警值, UNIQUE KEY uk_part_code (part_code) ) COMMENT 备件库存表; -- 备件出入库记录 CREATE TABLE t_spare_part_log ( id BIGINT PRIMARY KEY AUTO_INCREMENT, part_id BIGINT NOT NULL COMMENT 备件ID, change_type TINYINT NOT NULL COMMENT 1入库 2出库 3预占 4释放预占, change_qty INT NOT NULL COMMENT 变动数量正数入库负数出库, ref_type VARCHAR(32) COMMENT 关联单据类型如 WORK_ORDER, ref_id BIGINT COMMENT 关联单据ID, operator_id BIGINT COMMENT 操作人, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_part (part_id), KEY idx_ref (ref_type, ref_id) ) COMMENT 备件出入库记录表;available_qty用生成列自动计算查询的时候直接查这个字段不用每次写total_qty - locked_qty。预占和释放预占都记日志这样任何一笔库存变动都能追溯到来源。预警逻辑很简单定时任务扫描available_qty min_qty的备件生成预警记录。但要注意预警不要重复生成同一个备件在库存恢复到阈值以上之前只提醒一次。5. 避坑与排查部署和对接时最容易翻车的五个点5.1 数据库时区不一致导致巡检时间错乱现象巡检记录里的inspect_time比实际时间差了 8 小时或者任务超期判断不准。原因应用服务器、数据库服务器、容器环境的时区设置不一致。MySQL 默认用 UTCJava 应用用Asia/Shanghai写入的时候没做转换。解决统一时区。MySQL 连接串加serverTimezoneAsia/Shanghai应用启动参数加-Duser.timezoneAsia/Shanghai容器环境变量设TZAsia/Shanghai。写入时间统一用DATETIME而不是TIMESTAMPTIMESTAMP会随时区转换容易出玄学问题。5.2 二维码内容过长导致扫码识别率低现象贴在设备上的二维码手机要凑很近才能扫出来或者干脆扫不出来。原因二维码内容太长或者纠错级别设太高导致二维码密度过大。有些系统把整个设备 JSON 都塞进二维码内容几百个字符。解决二维码内容控制在 100 字符以内只放设备编码和校验串。设备详细信息扫码后从服务端查。纠错级别用 M 不用 H。打印尺寸不要小于 3cm×3cm。5.3 巡检任务生成时设备列表为空现象定时任务执行了但生成的巡检任务里一个设备都没有。原因t_inspect_plan里的location_ids和device_ids都是 JSON 数组查询的时候用JSON_CONTAINS或者FIND_IN_SET如果 JSON 格式不对或者位置节点下没有设备就会返回空。解决生成任务前先校验。如果device_ids为空按location_ids递归查子节点下的所有设备如果还是空记录日志并跳过不要生成空任务。JSON 字段写入前用JSON_VALID()校验。5.4 缺陷工单并发更新导致状态覆盖现象两个人同时操作同一个缺陷一个验收通过一个验收驳回最后状态是驳回但验收通过的操作日志也记了。原因更新时没有加锁后提交的覆盖了先提交的。解决用乐观锁。在t_defect表加version字段更新时UPDATE ... SET status ?, version version 1 WHERE id ? AND version ?影响行数为 0 就说明被改过了提示用户刷新重试。5.5 备件库存扣减后对不上账现象系统里的库存数量和实际盘点对不上差了几个。原因出库和预占释放的顺序不对或者并发扣减时没有加锁。比如两个工单同时领同一个备件都查到available_qty够都扣了结果超领。解决扣减库存用UPDATE t_spare_part SET locked_qty locked_qty ? WHERE id ? AND total_qty - locked_qty ?用数据库的行锁保证原子性。影响行数为 0 就说明库存不足直接返回失败。不要先查再更新。6. 从参考到落地我的验证习惯和一条实用技巧拿到任何一份系统参考我的习惯是先跑通一条最小链路再往两边扩。这套电力设备管理系统的最小链路就是建一个位置节点 → 建一个设备类型 → 建一台设备 → 生成二维码 → 建一个巡检计划 → 生成任务 → 扫码填记录 → 发现异常登记缺陷 → 派工 → 处理 → 验收闭环。这条链路走通了说明数据模型和状态流转没有大问题剩下的就是补功能和调体验。验证的时候有几个具体的检查点。第一设备编码和二维码校验串是否唯一我一般会写个脚本批量生成 1000 个设备看有没有重复。第二巡检任务生成后plan_start和plan_end的时间跨度是否合理有些 cron 表达式写错了会导致任务时间窗口是负的。第三缺陷状态流转的每一条边都要手动走一遍特别是驳回和重新处理这两条反向边很多系统在这里出问题。一个实用技巧在t_inspect_record表上加一个client_time字段记录手机端提交时的时间和inspect_time服务端接收时间对比。如果两个时间差超过 5 分钟说明网络延迟大或者手机时间不对这种记录要标记出来人工复核。这个字段在排查「巡检人员说提交了但系统没记录」这类问题时特别有用。还有一点巡检项的item_value用VARCHAR存不要用DECIMAL。因为有些巡检项是枚举值比如「正常/异常/轻微渗油」有些是数值有些是文本描述。统一用字符串存在应用层做类型转换和校验。如果一开始就按数值存后面加枚举项的时候就要改表结构。从那以后我每次拿到类似的系统参考都会先花半天时间把数据模型和状态机画出来确认没有逻辑死循环和状态孤岛再开始写代码。这个习惯帮我省了很多返工的时间。希望帮到你。本文还有配套的精品资源点击获取