1. 加油站管理系统概述
加油站管理系统是一套基于Java后端与Vue前端技术栈开发的综合性业务管理平台。这个系统我前后参与过三个不同规模的实施项目,从单体加油站到跨区域连锁油站都有涉及。现代加油站早已不是简单的"加油-收钱"模式,而是融合了油品进销存、会员营销、支付对账、设备监控等多维度的复杂业务场景。
传统Excel表格+纸质记录的管理方式在日均500车次以上的油站就会出现明显瓶颈:油罐库存统计滞后、交接班对账困难、促销活动执行不到位等问题频发。而一套定制开发的数字化管理系统能够将油枪销售数据实时同步到后台,自动生成日结报表,并通过移动端随时查看经营数据。
2. 技术架构设计
2.1 前后端分离架构
我们采用SpringBoot+Vue的经典前后端分离方案。后端使用SpringBoot 2.7.x构建RESTful API,前端通过Vue 3组合式API开发管理界面。这种架构的优势在加油站场景特别明显:
- 前端轻量化:加油站办公室的电脑配置普遍不高,Vue编译后的静态文件对硬件要求低
- 接口标准化:油枪控制器、地磅等硬件设备通过统一API接入
- 部署灵活:后端可部署在油站本地服务器或集团云平台
2.2 数据库选型
MySQL 8.0作为主数据库,考虑到:
- 油品交易记录日均增长约300-500条
- 需要支持复杂的统计报表查询
- 事务一致性要求高(如加油扣款+库存减少必须原子性完成)
典型表结构示例:
CREATE TABLE `gas_transaction` ( `id` bigint NOT NULL AUTO_INCREMENT, `gun_no` varchar(5) COMMENT '油枪编号', `oil_type` enum('92#','95#','98#','0#') NOT NULL, `volume` decimal(10,2) NOT NULL COMMENT '升数', `amount` decimal(10,2) NOT NULL COMMENT '金额', `start_time` datetime NOT NULL, `end_time` datetime DEFAULT NULL, `status` tinyint DEFAULT '0' COMMENT '0-进行中 1-已完成', PRIMARY KEY (`id`), KEY `idx_gun_time` (`gun_no`,`start_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;3. 核心功能实现
3.1 实时油枪监控
通过串口通信协议与加油机控制器对接,关键实现步骤:
- 使用RXTX库进行串口通信配置
// 初始化串口 CommPortIdentifier portId = CommPortIdentifier.getPortIdentifier("COM3"); SerialPort serialPort = (SerialPort) portId.open("OilGunMonitor", 2000); serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);- 定义数据帧解析规则(以正星加油机为例):
帧头(0xAA) | 长度(1字节) | 命令码(1字节) | 数据区(N字节) | 校验和(1字节)- 前端通过WebSocket实时展示油枪状态:
<template> <div v-for="gun in gunStatus" :key="gun.no"> <OilGunIndicator :volume="gun.currentVolume" :status="gun.statusCode" @emergencyStop="handleEmergencyStop(gun.no)"/> </div> </template> <script setup> import { ref, onMounted } from 'vue' const gunStatus = ref([]) const ws = new WebSocket('ws://your-backend/oilgun/ws') ws.onmessage = (event) => { gunStatus.value = JSON.parse(event.data) } </script>3.2 油罐库存管理
采用"期初库存+入库-出库=当前库存"的基础模型,但实际开发中要注意:
- 温度补偿计算(油品体积随温度变化):
public BigDecimal temperatureCompensation(BigDecimal volume, BigDecimal temp) { // 根据GB/T 1885-2016标准计算 BigDecimal vcf = BigDecimal.ONE.subtract( temp.subtract(standardTemp) .multiply(expansionCoefficient)); return volume.multiply(vcf).setScale(2, RoundingMode.HALF_UP); }- 库存预警采用双阈值策略:
- 软阈值(预警线):达到总容量80%触发补货提醒
- 硬阈值(停售线):达到总容量95%自动停用关联油枪
4. 特殊业务场景处理
4.1 交接班对账
加油站每天至少3次交接班,系统需要实现:
- 当班销售汇总
- 现金/电子支付核对
- 油枪起止码记录
我们采用"虚拟钱包"方案解决现金差异问题:
public class ShiftHandoverService { @Transactional public void handover(Long staffId, BigDecimal cashAmount) { // 1. 生成当班交易汇总 ShiftSummary summary = generateSummary(staffId); // 2. 计算理论现金金额 BigDecimal expectedCash = summary.getTotalAmount() .subtract(summary.getElectronicPay()); // 3. 记录差异 CashDifference diff = new CashDifference( expectedCash.subtract(cashAmount)); diffRepository.save(diff); } }4.2 会员价格体系
支持多级会员折扣和时段促销,数据库设计采用策略模式:
CREATE TABLE `price_policy` ( `id` int NOT NULL, `policy_type` enum('MEMBER_LEVEL','TIME_PERIOD','VOLUME') NOT NULL, `oil_type` varchar(10) NOT NULL, `condition_value` varchar(50) NOT NULL COMMENT '如会员等级值/时间段', `discount_type` enum('FIXED','PERCENT') NOT NULL, `discount_value` decimal(10,2) NOT NULL, PRIMARY KEY (`id`) );价格计算服务实现:
public BigDecimal calculateFinalPrice(String oilType, User user) { List<PricePolicy> policies = policyRepository.findByOilType(oilType); return policies.stream() .filter(p -> matchesPolicy(p, user)) .map(p -> applyDiscount(basePrice, p)) .min(Comparator.naturalOrder()) .orElse(basePrice); }5. 系统部署实践
5.1 本地化部署方案
中小型加油站推荐配置:
- 硬件:Intel NUC迷你主机(i5/16GB/512GB SSD)
- 软件环境:
- Ubuntu Server 22.04 LTS
- Docker 24.0+
- MySQL 8.0容器化部署
- Redis缓存服务
使用docker-compose编排:
version: '3' services: db: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: yourstrongpassword MYSQL_DATABASE: gas_station volumes: - mysql_data:/var/lib/mysql backend: image: your-java-backend ports: - "8080:8080" depends_on: - db5.2 安全防护要点
- 加油交易数据加密:
// 使用国密SM4算法加密交易记录 public String encryptTransaction(Transaction tx) { SM4Engine engine = new SM4Engine(); engine.init(true, new KeyParameter(sm4Key.getBytes())); byte[] input = objectMapper.writeValueAsBytes(tx); byte[] encrypted = new byte[input.length]; engine.processBlock(input, 0, encrypted, 0); return Base64.getEncoder().encodeToString(encrypted); }- 前端敏感操作二次验证:
<script setup> const handleDelete = async () => { try { await ElMessageBox.prompt('请输入管理员密码', '安全验证', { inputType: 'password' }) // 调用删除API } catch { ElMessage.warning('操作已取消') } } </script>6. 典型问题排查
6.1 油枪数据断连
常见原因及解决方案:
- 串口接触不良 → 检查RS485转USB转换器
- 数据帧校验失败 → 确认加油机型号对应的协议文档
- 缓冲区溢出 → 调整Java串口读取的buffer大小
日志分析技巧:
// 在串口监听线程中加入调试日志 logger.debug("Received raw bytes: {}", Hex.encodeHexString(buffer));6.2 日结报表差异
排查流程:
- 核对交易记录数与油枪日志数
- 检查是否有未完成的挂账交易
- 验证库存计算是否包含温度补偿
- 确认支付渠道对账文件是否完整
7. 性能优化实践
7.1 交易记录批量插入
使用MyBatis批量插入优化:
@Transactional public void batchInsertTransactions(List<Transaction> list) { SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH); try { TransactionMapper mapper = session.getMapper(TransactionMapper.class); for (Transaction tx : list) { mapper.insert(tx); } session.commit(); } finally { session.close(); } }7.2 前端表格渲染优化
针对万级交易记录展示:
<template> <el-table :data="tableData" :row-key="row => row.id" :virtual-scroll="true" :item-size="56" height="500px"> <!-- 列定义 --> </el-table> </template>8. 扩展功能方向
移动端应用:通过uni-app开发跨平台APP,实现:
- 移动支付扫码
- 电子发票开具
- 会员自助查询
物联网集成:
- 油罐液位传感器实时监测
- 消防设备状态监控
- 车牌识别自动关联会员
大数据分析:
- 基于历史数据的销量预测
- 客户消费行为分析
- 最优促销策略推荐
这套系统在实际部署后,中型加油站的平均日结时间从原来的45分钟缩短到8分钟,库存差异率从3%降至0.5%以内。特别提醒注意硬件接口的兼容性问题,不同品牌的加油机协议差异很大,建议在需求阶段就明确设备型号并获取官方通信协议文档。