简介:基于Jeecg-boot框架实现的物流仓储管理系统完整源代码,含数据库文件,覆盖用户管理、车辆管理、计划管理、仓库管理、库存管理、财务管理、统计报表等核心模块,适合Java开发人员、企业信息化人员学习二次开发,也可作为毕业设计或项目原型参考。资源包共1505个文件,大小12.29MB,以Java源码、Vue前端页面、JavaScript脚本、XML配置及SQL数据库脚本为主,并包含图片、文档等辅助材料,结构清晰便于定位。该资源已有696人学习下载,既有可直接导入运行的项目骨架,也有模块化代码与数据库设计,能帮助读者理解Jeecg-boot快速开发方式,掌握物流仓储业务的后台逻辑与界面交互实现,具有较高的实战参考价值。
1. 为什么用Jeecg-boot做物流仓储系统:一个能直接交付的快速开发底座
需求方给你提的清单往往长得吓人:用户权限、车辆调度、出入库计划、仓库库位、库存台账、财务对账,最后还要一张能看趋势的统计报表。如果从零用Spring Boot + Vue手写,光权限和报表就能耗尽工期。我的选择是直接用Jeecg-boot做底座,它自带用户角色权限、Online表单、代码生成器和积木报表,再叠加一套物流仓储业务表,就是标题里说的“含数据库文件”的完整交付物。这套方案适合中小型物流公司内部系统、仓库管理系统外包项目,也适合想快速落地毕业设计或比赛作品的团队。接下来我把从数据库初始化到报表调试的完整路径拆开讲,包括哪些坑必须绕开。
2. 系统骨架与数据库文件:从Jeecg-boot初始化到库表落地的第一关
拿到项目压缩包,第一件事是看数据库文件和后端配置。Jeecg-boot是前后端分离架构,后端Spring Boot,前端Vue,中间通过Redis缓存登录态和权限数据。很多人第一步就栽在“文件导进去但服务起不来”这件事上,所以这一章先把数据库文件的结构和导入流程讲透。
2.1 Jeecg-boot初始化项目:Maven工程和代码生成器怎么选
Jeecg-boot官方提供的是多模块Maven工程,一般包含jeecg-module-system、jeecg-module-demo、jeecg-boot-base-core等模块。我习惯直接用IntelliJ IDEA打开根目录pom.xml,等Maven拉完依赖,然后改配置文件启动。这里有一个关键判断:如果项目里已经带了数据库文件,说明表结构是别人设计好的,应该优先用“代码生成器”反向生成业务代码,而不是用Online表单重新建表。
代码生成器的适用场景是:你已经有一张设计好的表,希望从表结构直接生成Controller、Service、Mapper和Vue列表页面。物流仓储系统的车辆管理、计划管理、仓库管理等模块都是标准单表操作,非常适合用代码生成器。反过来,如果你还在想字段怎么设计,用Online表单先拖拽配置,再导出一张物理表也行。但在这个项目里,“含数据库文件”意味着建表工作已经完成了,不要重复造轮子。
另外要注意Maven编译版本。Jeecg-boot要求JDK 1.8或更高,我遇到过把JDK配到17导致Lombok插件不兼容的情况,报错信息非常迷惑。建议直接沿用项目pom.xml里定义的java.version,别擅自升级。
2.2 数据库文件里装的是什么:基础库、业务扩展表和初始化数据的边界
Jeecg-boot的数据库文件通常是一个大的.sql,里面至少包含三类东西,导入前要分清:
第一类是平台基础表,比如sys_user、sys_role、sys_user_role、sys_depart、sys_permission、sys_dict、sys_announcement等。这些表是权限框架和系统管理页面的数据源,字段和平台代码强绑定,不能随意改名,也不能随便删列。
第二类是业务扩展表,也就是这个物流仓储系统的核心。常见的有biz_vehicle(车辆)、biz_transport_plan(运输计划)、biz_warehouse(仓库)、biz_location(库位)、biz_stock(库存)、biz_stock_log(库存流水)、biz_finance_bill(财务费用单)等。这部分表是二开人员自己设计的,命名通常带biz_前缀,与平台表区分。
第三类是初始化数据,包括管理员账号密码、菜单权限SQL、字典项、示例数据。字典数据尤其重要,车辆状态、计划状态、费用类型、仓库类型都要靠sys_dict表驱动。
拿到数据库文件后,别急着全部导入。先打开文件看头部注释和建表语句。如果文件里同时包含CREATE DATABASE和USE语句,你可以直接导入;如果只有建表语句,需要手动先建库。我一般会检查三个点:字符集是否是utf8mb4、表名前缀是否统一、是否有物理外键。Jeecg-boot官方推荐使用MyBatis-Plus,业务表基本靠逻辑关联而不是物理外键。如果你拿到的数据库文件里大量使用FOREIGN KEY,要注意导入顺序,否则会卡在外键校验上。
2.3 导入MySQL的完整脚本流程与参数说明
下面是我在实际项目里常用的导入命令,注意先建库,再执行sql文件,避免“unknown database”报错:
# 1. 建库,使用utf8mb4字符集,避免中文乱码 mysql -uroot -p --default-character-set=utf8mb4 \ --execute "CREATE DATABASE IF NOT EXISTS jeecg_boot DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" # 2. 导入数据库文件,指定目标库名 mysql -uroot -p --default-character-set=utf8mb4 jeecg_boot < /data/backup/jeecg_boot_full.sql # 3. 验证导入结果,统计表数量 mysql -uroot -p --execute "USE jeecg_boot; SHOW TABLES; SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='jeecg_boot';"参数说明:--default-character-set=utf8mb4必须加,否则含中文的初始数据会变成乱码。第二条命令中jeecg_boot是目标数据库名,必须与后端yml配置文件里的url中的库名一致。如果sql文件比较大,超过几十MB,建议进入mysql后用source命令执行,或者用mysql命令行重定向,不要用图形化工具的“导入”按钮——它有时中断了但不报明显错误,事后表数量对不上才头疼。
导入MySQL后,还需要改后端配置。打开jeecg-module-system/src/main/resources/application-dev.yml,确认几点:数据库url中的useUnicode=true&characterEncoding=utf8mb4参数不能漏;用户名密码是否与本地一致;Redis地址和端口是否可达。Jeecg-boot的验证码、登录token和按钮权限缓存依赖Redis,Redis没起来服务能启动,但登录会失败,而且报错不是“连接失败”而是“验证码错误”,这种玄学问题最浪费排查时间。
这里还有一条血泪经验:我遇到过数据库文件本身带了默认菜单权限,导入后登录admin发现菜单显示不全。原因是sys_user_role表里缺少对应用户的角色关联,或者sys_permission表里没有把菜单挂到管理员角色上。这个问题具体怎么修,放到第5章避坑部分细讲。
3. 用户管理和车辆管理:权限模型与业务字段设计
物流仓储系统的用户不只是登录账号,它承载了调度员、仓库管理员、财务人员、司机等多种身份。车辆管理则是物流调度能跑起来的基础数据。这两个模块看似独立,实际上通过用户和车辆的状态联动。这一章讲权限模型怎么对接,以及车辆表怎么设计才够用。
3.1 用户管理:部门-角色-用户权限模型与Jeecg-boot的对接
Jeecg-boot的用户管理核心是五张表:sys_depart(部门)、sys_user(用户)、sys_role(角色)、sys_user_role(用户角色关联)、sys_user_depart(用户部门关联)。一个用户可以有多个角色和多个部门。角色决定能看到的菜单和按钮权限,部门决定数据权限范围——比如仓库管理员只能看到属于自己仓库的数据。
在这个物流仓储系统里,常见的角色设计是:系统管理员、调度员、仓库管理员、财务人员、司机。不同角色看到的菜单不同:司机登录后可能只显示车辆状态和运输计划,看不到财务对账。这些通过sys_permission表里的菜单配置和按钮权限(add/edit/delete)控制。
新增一个用户时,不能只往sys_user表插一条记录。很多人在数据库里直接insert用户,但没插sys_user_role,导致登录后跳转无权限。如果项目里用了Jeecg-boot自带的“用户管理”页面新增用户,平台会自动维护关联表。但你拿到的这个物流仓储系统可能是二开的,用了自定义注册接口,这时候就要检查代码里是否调用了userService.addUser(user, roleIds)。
这里给出一段SQL插入用户的示意,以及必须关联的权限数据:
-- 新增用户 INSERT INTO sys_user (id, username, realname, password, salt, status, org_code) VALUES ('user_001', 'driver_wang', '王师傅', '加密后的密码', '随机salt', 1, 'A01'); -- 绑定角色,角色ID从sys_role查询 INSERT INTO sys_user_role (user_id, role_id) VALUES ('user_001', 'driver_role_id'); -- 如果按部门控制数据权限,还需要关联部门 INSERT INTO sys_user_depart (user_id, dep_id) VALUES ('user_001', 'dep_warehouse_1');注意:这里的password不是明文。Jeecg-boot默认使用Shiro的Md5Hash加密,同时生成随机salt,加密公式是MD5(password + salt)循环多次。如果你直接用SQL插入,需要先调用平台工具类生成密文和salt。常见做法是先用平台页面创建一个用户,把密文和salt复制出来,再在批量初始化SQL里使用。这个细节经常有人踩坑,记住“不要手工造密文”。
3.2 车辆管理:车辆档案、调度状态和承运关系的字段设计
物流仓储系统的车辆管理不只是维护车牌号。业务上至少需要三类信息:基础档案、状态信息、承运关系。常见做法是把它们放到一张biz_vehicle表里,用状态字段区分,而不是拆成多张表增加关联复杂度。
基础档案包括:车牌号、车辆类型、核定载重、车厢体积、燃油类型、购买日期、保险到期日、运输证有效期。状态信息包括:当前状态(空闲/在途/维修/停用)、当前位置、当前司机、关联计划单号。承运关系包括:车辆归属(自有/外协)、承运商ID、司机用户ID。
参考建表语句如下:
CREATE TABLE biz_vehicle ( id varchar(32) NOT NULL COMMENT '主键', plate_no varchar(20) NOT NULL COMMENT '车牌号', vehicle_type varchar(32) DEFAULT NULL COMMENT '车辆类型(字典)', load_weight decimal(10,2) DEFAULT NULL COMMENT '核定载重(吨)', load_volume decimal(10,2) DEFAULT NULL COMMENT '车厢体积(方)', status varchar(32) DEFAULT 'IDLE' COMMENT '状态: IDLE空闲/IN_TRANSIT在途/MAINTENANCE维修/DISABLED停用', current_location varchar(255) DEFAULT NULL COMMENT '当前位置', current_plan_id varchar(32) DEFAULT NULL COMMENT '当前运输计划ID', driver_user_id varchar(32) DEFAULT NULL COMMENT '绑定司机用户ID', owner_type varchar(10) DEFAULT 'OWN' COMMENT '自有/外协', insurance_expire date DEFAULT NULL COMMENT '保险到期日', transport_cert_expire date DEFAULT NULL COMMENT '运输证到期日', PRIMARY KEY (id), UNIQUE KEY uk_plate_no (plate_no) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='车辆管理';注意plate_no要加唯一索引,不然界面会重复录入。status字段建议用字符串常量,不要用数字0/1/2,因为字典表里配置的value可能是字符串,查询时用数字容易发生隐式转换,导致索引失效。current_plan_id这个字段很重要,它把车辆和计划管理串起来,查“当前在途车辆”直接where status='IN_TRANSIT' and current_plan_id is not null即可。
3.3 用Jeecg-boot代码生成器快速生成车辆管理CRUD
建好biz_vehicle表后,在Jeecg-boot的“代码生成器”功能里选择这张表,配置好包名和模块名,点击生成,后端会生成Controller、Service、Mapper,前端生成list页面。生成后的Controller自带分页查询和增删改查接口,基于MyBatis-Plus。
生成后的Controller核心代码类似:
@RestController @RequestMapping("/biz/vehicle") public class BizVehicleController extends JeecgController<BizVehicle, IVehicleService> { @Autowired private IVehicleService vehicleService; /** * 分页查询车辆列表,支持多条件组合 */ @PostMapping("/list") public Result<?> queryPageList(@RequestBody BizVehicle vehicle, @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo, @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) { QueryWrapper<BizVehicle> queryWrapper = QueryGenerator.initQueryWrapper(vehicle); Page<BizVehicle> page = new Page<>(pageNo, pageSize); IPage<BizVehicle> pageList = vehicleService.page(page, queryWrapper); return Result.ok(pageList); } }逻辑说明:QueryGenerator是Jeecg-boot的查询条件构造器,它会把前端传的字段自动拼成查询条件。比如前端传plateNo,它自动变成plate_no = ?;如果需要模糊查询,前端在值后面加“*”即可。pageNo和pageSize是分页参数,默认第一页10条。车辆状态这类字典字段,在生成前端代码时,Jeecg-boot会根据字典配置自动渲染成下拉框。
参数说明:@RequestBody接收JSON,所以前端用axios.post('/biz/vehicle/list', params)传对象,不能直接走get。如果要用GET,接口需要改成@RequestParam接收。代码生成器默认生成的是POST分页查询,这是Jeecg-boot的约定,别想着改成restful风格,因为前端页面生成的api文件也是按POST请求来的。
生成代码后的落地要点:不要手工重写CRUD,但必须检查两个地方。一是菜单SQL是否已经挂到sys_permission表并分配给角色,否则页面上找不到入口;二是实体类字段类型是否正确,比如decimal对应BigDecimal,date对应Date。如果数据库字段有下划线,生成的实体默认驼峰映射,MyBatis-Plus会自动开启驼峰转换,不需要额外处理。
4. 计划管理、仓库管理与库存管理:核心业务链路与事务实现
这一章是整个物流仓储系统最硬的部分。计划管理负责调度指令,仓库管理维护物理位置,库存管理记录结果。三者联动如果只靠各个页面的add、edit,业务上会漏掉很多约束。落地时要把状态机和事务控制放在后端,不要依赖前端判断。
4.1 计划管理:出入库计划和运输计划的状态机设计
物流仓储系统的计划管理通常分成两类:作业计划(入库/出库)和运输计划。表结构里一般是biz_plan,包含plan_no、plan_type(INBOUND/OUTBOUND/TRANSPORT)、status、source_order_no(关联订单号)、origin_warehouse_id、dest_warehouse_id、vehicle_id、driver_id、planned_start_time、planned_end_time等字段。
状态机是计划管理的核心,我常用的设计是五个状态:
- DRAFT(草稿):计划创建后未提交,可编辑可删除。
- PENDING(待执行):提交后等待调度,此时计划已固定,不能再改数量。
- EXECUTING(执行中):车辆出发或仓库开始作业。
- COMPLETED(已完成):执行完成,关联库存流水已生成。
- CANCELLED(已取消):取消后不能恢复。
用状态机的好处是避免业务上出现“已完成计划被删除”这种事故。在代码层,每次状态流转都做一次校验,例如:
if ("COMPLETED".equals(plan.getStatus()) || "CANCELLED".equals(plan.getStatus())) { throw new BusinessException("已完成或已取消的计划不能修改"); }在数据库层,没有物理外键,但业务上要保证。比如删除一个计划时,要先确认不存在关联的出库单或运输执行记录,否则库存流水就变成了孤儿数据。常见做法是写一个PlanService.deletePlanWithCheck()方法,在事务里先查子表再删主表。
4.2 仓库管理和库存管理:库位、批次和库存流水
仓库管理包含仓库表biz_warehouse和库位表biz_location。一个仓库有多个库位,库存数据挂在库位上。库存表至少包含warehouse_id、location_id、sku_id(物料/商品)、batch_no(批次)、quantity(数量)。
为什么要挂库位和批次?因为物流仓储系统经常需要支持先进先出(FIFO)。如果只按SKU汇总库存,不区分批次,发料时无法决定先发哪一批。库存表里加了batch_no后,出库单就可以按批次优先级(一般是生产日期或入库时间)来扣减。
库存流水表biz_stock_log记录每一次变动,核心字段包括:doc_type(单据类型)、doc_no(单据号)、change_type(IN/OUT)、change_quantity、before_quantity、after_quantity、operator_id、create_time。有了流水,财务对账时就能还原每一笔业务。很多系统翻车,就是只记总数不记流水,发现账实不符时完全没法排查是哪个环节错了。
4.3 库存扣减与回滚的代码落地
库存扣减是最容易出现并发问题的地方。多辆车同时出库,如果只用select再update,不加锁,数量会超卖。下面这个方法用的是数据库悲观锁,保证同一时刻只有一个事务能扣同一批库存:
@Transactional(rollbackFor = Exception.class) public void drainStock(DrainStockRequest req) { // 1. 查询并锁住库存记录,同一时刻只有一个事务能查到 BizStock stock = stockMapper.selectForUpdate(req.getWarehouseId(), req.getLocationId(), req.getSkuId(), req.getBatchNo()); if (stock == null || stock.getQuantity() < req.getQuantity()) { throw new BusinessException("库存不足或批次不存在"); } // 2. 扣减数量 int newQuantity = stock.getQuantity() - req.getQuantity(); stock.setQuantity(newQuantity); stock.setUpdateTime(new Date()); stockMapper.updateById(stock); // 3. 记录流水,便于追溯 BizStockLog log = new BizStockLog(); log.setDocNo(req.getDocNo()); log.setChangeType("OUT"); log.setChangeQuantity(req.getQuantity()); log.setBeforeQuantity(stock.getQuantity() + req.getQuantity()); log.setAfterQuantity(newQuantity); log.setOperatorId(req.getOperatorId()); stockLogMapper.insert(log); }逻辑说明:selectForUpdate是自定义SQL里的SELECT ... FOR UPDATE,会给匹配的行加排他锁。事务提交后锁释放。这里必须加@Transactional,否则selectForUpdate的锁不生效,因为连接在方法结束时自动归还连接池,锁就没了。
如果不想用悲观锁,可以换乐观锁:在biz_stock表加version字段,扣减时用update语句一次性完成:
UPDATE biz_stock SET quantity = quantity - #{qty}, version = version + 1 WHERE id = #{id} AND quantity >= #{qty} AND version = #{oldVersion}如果影响行数为0,说明数量不够或版本已变,外层循环重试。这种方式性能更好,但库存流水需要在事务内补。参数说明:DrainStockRequest里的warehouseId、locationId、skuId、batchNo要精确匹配,不能只查skuId就扣,否则同一个SKU放在多个库位会串货。这也是仓库系统一个典型翻车点:库存按库位管理,扣减却只按sku聚合,最后每个库位的数量对不上总账。
计划管理、仓库管理和库存管理的联动逻辑,一般在Service层串起来:计划状态变为“执行中”时,生成出库任务;出库任务完成时,调用drainStock扣减库存;扣减成功后再把计划置为“已完成”。这个流程要放在同一个事务里,或者用定时任务轮询未完成的计划,二选一。我倾向于后者,因为长事务会锁库存表,影响其他前台操作。
5. 财务管理、统计报表与常见问题排查:对账逻辑、报表性能与数据库文件坑
最后一条业务链路是钱和数字。财务管理负责对账,统计报表负责展示,两者都依赖前面的库存流水和费用明细。这一章先讲费用建模和报表配置,然后集中写我从这个项目里踩出来的5个坑,都属于“数据库文件”和模块集成的实际问题。
5.1 财务管理:费用结算与应收应付的数据建模
物流仓储系统的财务模块不做复杂总账,重点是业务费用结算。表结构通常包括费用单主表biz_finance_bill、费用明细表biz_finance_bill_item,以及应收应付记录。主表记录对账周期、往来单位(客户/承运商)、总金额、状态(草稿/确认/对账中/已核销);明细表记录具体费用项。
费用类型用字典管理:运输费、仓储费、装卸费、货损赔偿、燃油附加费。每笔费用明细要关联计划单号或订单号,方便反查业务来源。一个实用建议是:费用明细表的fee_type字段必须使用字典统一值,不要出现“运输费”和“运输费用”两个值,否则统计报表里的汇总数怎么都对不上。
对账逻辑上,我一般会设计一个对账状态:草稿费用单可以改;确认后不允许改金额;对账中表示业务方已核对但还没开票;已核销表示钱已收/已付。这个状态机与计划管理的状态机思路一致,避免财务人员误改已经确认的单据。
5.2 统计报表:积木报表的SQL数据集和跨表统计
Jeecg-boot自带积木报表(JimuReport),支持在线设计报表和自定义SQL数据集。物流仓储系统的统计报表通常包括:库存台账、出入库汇总、车辆使用率、费用汇总。在积木报表里配置SQL时,参数用${param}形式接收,避免写死日期。
一个常用的出入库汇总SQL如下:
SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS biz_date, warehouse_name, SUM(CASE WHEN change_type = 'IN' THEN change_quantity ELSE 0 END) AS in_quantity, SUM(CASE WHEN change_type = 'OUT' THEN change_quantity ELSE 0 END) AS out_quantity FROM biz_stock_log WHERE create_time >= '${beginDate} 00:00:00' AND create_time <= '${endDate} 23:59:59' GROUP BY biz_date, warehouse_name ORDER BY biz_date DESC参数说明:${beginDate}和${endDate}来自报表查询表单,积木报表会把它替换成字符串。这种参数写法有类型转换风险,前端传yyyy-MM-dd格式时,拼接的00:00:00没问题;如果传了时间戳就要调整格式。另外,统计报表的效率全部压在索引上。biz_stock_log的create_time和change_type字段如果没有索引,数据量到几十万行后,报表打开要等十几秒。
报表性能的另一个坑是导出。积木报表默认会把满足条件的数据全量加载到内存再导出,如果结果集超过几万行,内存直接溢出。常见做法是给报表SQL加limit,或者用积木报表的分页导出。如果是每天跑一次的汇总数据,建议使用定时任务先把结果写入统计表,报表直接查统计表,这样就算数据量再大也不会拖垮业务库。
5.3 避坑五连:数据库文件导入、权限缓存、并发扣减和sqlite文件加密迁移
这一节专门写踩坑记录,每一条都是我在类似项目里见过或经历过的,按现象、原因、解决来写。
踩坑1:数据库文件导入后,外键约束报错现象:执行sql文件时,到中间报ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails,后面的语句停掉了。 原因:多个业务表之间存在外键关联,导入顺序不是按依赖顺序排列。或者基础数据里的主键ID(varchar)和关联表里的外键ID不一致。 解决:先用文本编辑器把文件里的FOREIGN KEY全部搜出来,按依赖关系调整顺序;或者干脆把物理外键删掉,用应用层逻辑保证。Jeecg-boot官方表设计基本不用物理外键,如果你拿到的文件里有,我建议在备份后将其移除,改成普通索引,避免以后删除数据被约束卡死。
踩坑2:新增用户后登录报“无权限”现象:用SQL直接insert sys_user后登录能成功,但跳转时提示无权访问,菜单也看不到。 原因:sys_user_role、sys_user_depart和sys_permission中的角色菜单关系没同步。Jeecg-boot的授权缓存是登录时一次性加载到Redis的,数据库改了,Redis没刷新。 解决:不要绕过平台接口去插用户。如果非要用SQL批量导入,导入后调用平台的“退出登录”接口清缓存,或者手动删除Redis里的相关key。还要检查角色ID是否存在于sys_role表。更靠谱的是使用Jeecg-boot的“用户管理”页面在线新增,或者调用userService.addUser。
踩坑3:并发出库导致库存变成负数现象:两台电脑同时出库同一SKU,各扣了100,库存却少了300。 原因:查询库存时没有加锁,两个事务都读到库存100,各自扣减后update,最后写回时互相覆盖。 解决:使用前面4.3的SELECT FOR UPDATE或者乐观锁。另外,扣减库存时也可以用SQL原子操作:
UPDATE biz_stock SET quantity = quantity - #{qty} WHERE id = #{id} AND quantity >= #{qty}如果影响行数为0就抛异常。这种方式不用先select,性能更好,但库存流水需要在事务内补。
踩坑4:统计报表导出Excel时卡死现象:点导出后浏览器一直转圈,后端日志报OutOfMemoryError。 原因:积木报表默认从数据库把全量结果集加载到内存,再生成Excel。报表数据量超过几万行后,内存扛不住。 解决:在数据集SQL里加limit限制,或者用积木报表的分页导出;给报表服务单独分配更大的JVM堆内存;如果是定时统计结果,建议提前用汇总表物化,报表直接查汇总表,不要跑明细。
踩坑5:拿到的数据库文件是sqlite格式,能否加密?现象:解压项目后发现数据库文件是xxx.db,用文本编辑器打开是二进制,尝试用MySQL导入失败。 原因:有些演示项目或备份工具会导出成SQLite单文件数据库。Jeecg-boot主库支持MySQL、PostgreSQL、Oracle,但不直接支持SQLite作为运行库。有人会问“sqlite数据库文件能否加密”——sqlite文件本身可以加密(比如用SQLCipher),但加密后的文件无法被标准工具读取,放在这个项目里没有意义。 解决:如果只有sqlite文件,先确认里面表结构是否和Jeecg-boot的MySQL脚本一致;一致就使用Navicat的数据传输功能,把表结构和数据转到MySQL;不一致就说明不是同一个项目的数据库文件,不能混用。生产环境永远不要用SQLite顶数据库,并发能力完全不同。这里也提醒一句:Access文件同理,用LabVIEW之类工具创建的Access数据库和Jeecg-boot没有任何关系,别想着直接挂载。
6. 给系统装上“后悔药”:数据库定时备份、报表缓存和上线前核对习惯
模块开发完不是终点,交付后最怕业务跑了两个月,数据对不上、报表出不来、用户权限乱了。这时候能救你的只有备份和核对习惯。最后一章分享三个我常用的技巧,都跟“数据库文件”相关。
第一个技巧是定时备份。Jeecg-boot的数据库文件是整个系统的命脉,我习惯用mysqldump做每日备份,保留最近7天:
mysqldump -uroot -p --single-transaction --routines --triggers jeecg_boot | gzip > backup_$(date +%F).sql.gz参数说明:--single-transaction保证备份过程中不锁表,业务还能继续写;--routines和--triggers把存储过程和触发器一起备份,因为Jeecg-boot某些功能可能用到。恢复时先建空库,再解压导入。这个命令配合crontab就是一道后悔药,至少出问题时能回到昨天。
第二个技巧是报表参数缓存。统计报表跑得慢,除了加索引,还可以在积木报表里打开缓存开关,或者在计划任务里凌晨预生成PDF/Excel放到服务器指定目录。业务方每天看的是前一天汇总数据,完全不需要实时计算。这样报表导出卡死的问题也能绕开。
第三个技巧是上线前的核对习惯。我每次交付前会在新机器上从头导入一遍数据库文件,然后跑三个核对SQL:表数量是否一致、关键表(sys_user、biz_stock等)的行数是否一致、admin是否能在登录后看到所有菜单。这一步能拦截90%的“在我电脑上是好的”问题。
我的习惯是把数据库连接、Redis地址、备份路径写进一个环境变量脚本,上线时只改这一个文件,绝不直接改application-dev.yml。这个习惯救过我很多次——有一回现场运维改错了一个字符,导致Redis连到测试环境,用户权限全部混乱,靠备份恢复才解决。希望这些经验能帮你在做Jeecg-boot物流仓储系统时少走弯路,一次上线顺利跑起来。
本文还有配套的精品资源,点击获取