前阵子一位做水产加工的朋友找到我,开口第一句是:“我朋友的公司搞了套溯源系统,手机一扫,鱼是哪天捞的、哪个塘养的都能看到,我也想整一个。”
这类需求我遇见过不少。作为一个长期做企业信息系统的开发者,我得承认:市面上大量“溯源小程序”确实做到了“扫码看介绍”,但一个真正能用的农产品溯源系统,远不是套个模板再卖点二维码标签那么简单。很多项目做到一半才发现,最麻烦的不是写接口,而是理清批次怎么流转、码怎么防重、数据怎么不可信、二维码被复制了又该怎么办。本文就基于SpringBoot,把一套农产品溯源系统的设计与实现从头到尾拆一遍,包括需求梳理、技术选型、数据库建模、核心接口、防伪防串货,以及那些容易踩的部署坑,给正要接同类项目或做毕业设计的你一份能照着落地的参考。
1. 溯源不是“扫码看简介”:先把需求链路理清
1.1 使用角色和他们的真实诉求
我第一次接触这类项目时,也以为做个后台、生成二维码、再写个H5查询页就够了。结果需求调研阶段就被上了一课:不同角色对“溯源”的理解完全不一样。
- 消费者:他们不关心你系统用了什么框架,只想知道这东西能不能放心吃。所以扫码后看到的信息必须通俗、直观,最好能讲成一个“故事”:哪个基地种的、什么时候收获的、检测报告过没过、谁发货的。
- 企业老板/管理员:他们要的是可管理性。谁录的数据、哪个批次出过问题、能不能一键召回、经销商有没有窜货,这些才是他们买单的原因。
- 一线录入人员:养殖员、采摘员、加工员、仓库管理员,他们需要的是一个非常简单的录入方式,复杂了根本没人用,数据就会断链。
- 质检/监管角色:虽然很多中小企业暂时没有真正的监管入驻,但系统设计时必须留出“抽查、导出、审计”的能力,否则后面对接会非常痛苦。
我在实际项目里建议你第一步不要急着写代码,先做一张角色-诉求表,把这四类人的核心诉求列出来,再决定模块优先级。大多数项目第一版只需要三类角色:系统管理员、业务录入人员、游客(消费者扫码)。监管端可以做最简单的只读导出,不用在前期投入太多。
1.2 一条农产品要经过哪些环节,系统就要记录哪些节点
溯源系统的本质是“时间线管理”。一件农产品从源头到消费者手里,通常经过这几个阶段:
种植/养殖 → 加工/包装 → 检测/仓储 → 物流配送 → 批发/零售
每个阶段都需要沉淀关键信息。以水产养殖为例:
- 养殖环节:苗种来源、投放时间、养殖场地、投喂/防疫记录
- 加工环节:原料批次、加工时间、负责人、生产批号
- 质检环节:检测项目、检测结果、报告编号或图片
- 仓储物流:发货时间、承运方、车辆信息、发往地点
很多新手设计数据库时,喜欢把“产地、生产日期、保质期”直接堆在商品表里,这其实是个误区。因为同一个商品可能对应多个批次的原料,同一条鱼也可能经历多次加工。正确做法是建立一个“溯源事件流”(Trace Event),让每个环节以独立记录的形式挂在批次下面,按时间排列。消费者扫码看到的就是一条完整时间线,企业只需要录好每个节点,系统自动排序展示。
1.3 把需求翻译成功能模块
通过上面的诉求和链路分析,功能模块其实就出来了:
- 系统管理:用户、角色、权限
- 基础资料:企业信息、商品分类
- 批次管理:创建批次、绑定商品、状态管理
- 溯源档案:各环节事件录入与维护
- 码管理:生成溯源码、批量打印、作废、查询记录
- 消费者查询:H5页面扫码展示
- 统计与召回:扫码数据分析、异常批次定位、导出上报
这里想多说一句:消费者端不是一个“独立系统”,它就是一个面向扫码访问的H5页面,放在同一个服务里即可。很多毕设题目会单独拆一个“小程序端”,但如果企业的诉求只是“扫码能看”,那H5就是性价比最高的方案,不需要用户下载、不用审核上架,微信扫一扫直接打开,体验也够用了。
2. 技术选型的取舍:SpringBoot是核心,但不是拼图的全部
2.1 为什么先定SpringBoot而不是别的框架
如果是纯做业务系统,SpringBoot基本是当前国内企业项目的默认选择,没有太大悬念。但我在给朋友做方案时还是把几种技术路线都摆出来对比过,因为有时候“最流行”不一定“最适合”,需要给客户一个让人信服的结论。
| 框架 | 优势 | 不足 | 适不适合做溯源 |
|---|---|---|---|
| Spring Boot | 生态全、事务和权限成熟、部署文档多、招人容易 | 相对较重,JDK版本要求高一点 | 非常适合,中小企业单体项目首选 |
| Flask/Django | 上手快、代码量少 | 生态偏Web/RPA,传统ERP这块的资料相对少 | 可以,但如果后续要接监管上报、二次开发,团队水平影响较大 |
| Node.js | 高并发I/O不错 | 事务管理、复杂权限体系支持偏弱 | 更适合做消费者端的高并发查询,不适合做管理后台主体系 |
| PHP | 老牌、部署成本低 | 现代工程化体验一般 | 除非维护老系统,否则不推荐新起盘 |
最终选SpringBoot,还有一个很现实的原因:这个项目大概率要长期迭代,而后来的开发者、甚至是接手的实习生,对SpringBoot这套体系的熟悉度是最高的,不至于人走了项目就瘫了。
版本方面我的建议是:如果是毕业设计,用 Spring Boot 2.7 + JDK 8/11 最稳,网上资料多,部署在三年的乞丐服务器上也不会太吃力;如果是新企业项目且团队能接受,可以上 Spring Boot 3.x + JDK 17。千万不要一上来追JDK 21,很多中间件和脚手架版本还没完全跟上,出了问题排查成本高。
2.2 数据库、缓存、前端与二维码组件的搭配
核心组件按这个默认清单来配,基本不会出错:
- 数据库:MySQL 8。用InnoDB引擎、utf8mb4字符集,事务和并发都够用。
- ORM:MyBatis-Plus。理由很简单:单表CRUD基本不用写SQL,分页现成,复杂查询又能手写SQL兜底。JPA虽然也很优雅,但实际团队里会写JPA的人通常没会写MyBatis的人多。
- 缓存:Redis。消费者扫码查询是典型的高频读场景,把批次详情和事件列表缓存进去能省一大半数据库压力。
- 后台管理前端:Vue3 + Element Plus。组件全、方案成熟,做批次管理、报表导出很顺手。
- 消费者H5端:如果不想上太重的工程,直接Thymeleaf渲染几个页面也完全够;想做得精致点就拆前后端。我一般建议毕设项目用后者,因为论文里能写的技术点多一些。
- 二维码生成:ZXing。Java生态最常用,几行代码就能生成图片,支持自定义大小、容错级别和白边。
2.3 区块链溯源为什么先放一放
现在很多溯源项目的需求说明书里都会写“区块链”,甚至有些毕设题目就叫“基于区块链的农产品溯源系统”。我理解这个热门词带来的加分效应,但真正落地时得泼点冷水:
区块链能解决的是“上链之后的数据不可篡改”,但农产品溯源最头疼的问题其实是“上链之前的数据真实性”——鸡是不是真的散养了、农药有没有超标、操作员有没有偷懒乱填,这些区块链完全管不了。再加上联盟链节点成本、维护门槛、性能吞吐,很多中小企业根本养不住这套基础设施。
我经手过的项目里,更务实可信的方案是:数据库 + 操作审计日志 + 权限分级 + 状态版本控制。什么意思?所有关键记录都保留操作人和时间,录入后不允许物理删除,改错了就用“红冲”方式新增一条修正记录,消费者能看到“原始记录”和“更正记录”。这个方案成本低、效果好,而且答辩时也能讲清楚“为什么没有用区块链”——这是加分项,不是减分项。
2.4 单体分层架构,够用且好维护
项目结构建议按经典分层来划分,不要一上来就上微服务:
com.example.trace ├── controller // 接口层,参数校验、返回统一结构 ├── service // 业务逻辑,事务管理 ├── mapper // 数据访问,MyBatis-Plus接口 ├── entity // 数据库实体 ├── dto/vo // 入参/出参对象 ├── config // 配置类,Redis、安全、跨域等 ├── common // 统一返回、异常处理 └── util // 工具类,如二维码生成、码生成器单体架构最大的好处是事务可控。创建批次时,要同时插入批次表、生成N个溯源码、写操作日志,这跨了好几张表,Spring的@Transactional一把梭,逻辑非常自然。你要是拆成微服务,反而要为分布式事务头疼。等哪天真的有几千个企业同时用、扫码量日均百万以上了,再把“扫码查询”这个高频模块拆出去也不迟,没必要提前过度设计。
3. 表设计与一物一码:数据底盘决定系统上限
3.1 单品码还是批次码:先想清楚码的粒度
“一物一码”这四个字经常被误解。很多人以为就是每一颗苹果、每一条鱼都要有独立二维码,这在实际生产中往往不现实。给一颗苹果贴码,光是包装成本就比苹果贵。
我把码的粒度分成三种,需要根据商品单价和包装方式选择:
- 单品码:每个最小销售单位一个码,适合高价值商品(精品水果礼盒、有机蔬菜、品牌大米)。成本高,但对消费者冲击力大。
- 批次码:一个生产批次共用一个码型或一个码段,适合散装、低价值、走大宗批发的商品。管理简单,召回也是按批次来,完全够用。
- 混合方式:一个批次一个码段,消费者扫到其中任意一箱的码都能查到同一个批次的时间线。这是目前中小企业性价比最高的方案。
我的建议:第一版先做“批次码 + 码段”模式。每个批次创建时生成M个不重复的溯源码(M就是预计包装数),但M不必等于实际果实数量,等于包装箱数就行。对消费者来说,扫出来的信息是一样的;对企业来说,成本和库存压力小了很多。
3.2 核心表结构怎么设计
数据库是溯源系统的地基,我把最重要的四张表贴出来,这些字段是我在真实项目中反复调整后留下的最小可用集合:
-- 商品表 CREATE TABLE product_info ( id BIGINT PRIMARY KEY AUTO_INCREMENT, product_name VARCHAR(100) NOT NULL, category VARCHAR(50) COMMENT '商品分类', origin VARCHAR(200) COMMENT '产地', spec VARCHAR(100) COMMENT '规格/包装方式', status TINYINT DEFAULT 1 COMMENT '1启用 0停用', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 批次表 CREATE TABLE batch_info ( id BIGINT PRIMARY KEY AUTO_INCREMENT, batch_no VARCHAR(64) NOT NULL UNIQUE COMMENT '批次号', product_id BIGINT NOT NULL, produce_date DATE COMMENT '生产/收获日期', expire_date DATE COMMENT '保质期至', origin_detail VARCHAR(500) COMMENT '详细产地:塘口/大棚/地块', status TINYINT DEFAULT 1 COMMENT '1正常 2召回 3作废', create_by VARCHAR(50), create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_product (product_id) ); -- 溯源事件表(核心!时间线) CREATE TABLE trace_event ( id BIGINT PRIMARY KEY AUTO_INCREMENT, batch_id BIGINT NOT NULL, event_type VARCHAR(20) NOT NULL COMMENT 'PLANT/GROW/PACK/CHECK/STORE/SHIP', event_name VARCHAR(100) COMMENT '环节名称,如 捕捞、加工、质检', content TEXT COMMENT '环节描述', operator_name VARCHAR(50) COMMENT '操作人姓名', event_time DATETIME NOT NULL COMMENT '实际发生时间', location_desc VARCHAR(255) COMMENT '地点描述', attachment_url VARCHAR(500) COMMENT '图片/报告附件', sort_no INT DEFAULT 0 COMMENT '同一环节下的排序', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_batch_time (batch_id, event_time) ); -- 溯源码表 CREATE TABLE trace_code ( id BIGINT PRIMARY KEY AUTO_INCREMENT, code VARCHAR(64) NOT NULL UNIQUE COMMENT '溯源码', batch_id BIGINT NOT NULL, status TINYINT DEFAULT 1 COMMENT '1正常 2作废', scan_count INT DEFAULT 0, first_scan_time DATETIME, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_batch (batch_id) );这四张表构建了最基本的数据链路:trace_code → batch_info → trace_event。消费者扫描时,拿着code查到batchId,再把该批次下所有事件按时间拼成时间线。
有一点提醒:表之间的外键我故意不建物理外键,只用索引+逻辑关联。原因是溯源系统后期做批量导入、数据清洗时,物理外键经常是灾难源。只要应用层保证batch_id存在,物理外键完全没必要。
3.3 溯源码生成规则与防重
溯源码的生成是个容易被人忽略但特别关键的细节。很多新手直接用UUID.randomUUID(),一张二维码里塞一个36位字符串,打印出来密密麻麻,用户体验很差。
推荐规则:编码前缀 + 日期 + 商品编号 + 随机流水号。
public static String generateCode(Long batchId, Long productId) { String datePart = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); String productPart = String.format("%04d", productId % 10000); String randomPart = String.format("%06d", ThreadLocalRandom.current().nextInt(1000000)); String check = genCheckBit(productPart + randomPart); return datePart + productPart + randomPart + check; }这里最后两位check是简单的校验位,用于人工输入时快速判断有没有抄错码。虽然扫码场景用得少,但后台手工补录数据时会非常有用。
真正要防的是并发重复:哪怕生成规则再复杂,也可能有巧合。最可靠的兜底是在trace_code.code上建唯一索引,插入时捕获DuplicateKeyException重新生成一次。不要用“先查一遍再插入”,查和插之间永远有并发空隙。
3.4 正向追溯与反向召回
表结构设计好了,还需要理解两个方向的数据流,这是答辩时最容易被问到的问题:
- 正向追溯:从批次出发,查出所有环节事件,按时间排列,这是消费者扫码看到的。实现就是
trace_event按batch_id和event_time排序。 - 反向召回:从某个商品二维码出发,找到批次,再找出这个批次所有发货记录,定位到经销商或门店。召回需要额外一张发货单表,核心字段包括批次号、经销商编码、发货区域、数量、发货时间。没有这张表,召回只能靠Excel,系统价值就大打折扣。
所以我的建议是:前期设计多留一张sale_order表(发货/销售记录),哪怕第一阶段不开发完整模块,也把表建好。后面客户提“怎么追回这批货”的时候,你只需要写一个联查和导出功能,而不是改库。
4. 核心链路实现:从生产建档到扫码查询
4.1 批次建档与溯源码生成
业务上,第一步是管理员创建商品,第二步是创建批次并填写产地、生产日期等基本信息,第三步是系统生成该批次对应的一批溯源码。
创建批次时必须保证事务:批次表和码表要么一起成功,要么一起回滚。
@Service @RequiredArgsConstructor public class BatchService { private final BatchInfoMapper batchInfoMapper; private final TraceCodeMapper traceCodeMapper; @Transactional(rollbackFor = Exception.class) public Long createBatch(CreateBatchRequest req) { BatchInfo batch = new BatchInfo(); batch.setBatchNo(buildBatchNo(req.getProductId())); batch.setProductId(req.getProductId()); batch.setProduceDate(req.getProduceDate()); batch.setExpireDate(req.getExpireDate()); batch.setOriginDetail(req.getOriginDetail()); batch.setStatus(1); batchInfoMapper.insert(batch); // 生成码段,假设需要500个码 for (int i = 0; i < req.getCodeCount(); i++) { TraceCode tc = new TraceCode(); tc.setCode(CodeGenerator.generateCode(batch.getId(), req.getProductId())); tc.setBatchId(batch.getId()); traceCodeMapper.insert(tc); } return batch.getId(); } }这段代码在码量小(几百个)时完全没问题。但如果码量上万,逐条插入会明显变慢,我建议改成批处理插入,每500条一组INSERT。实测下来,从逐条插入到批量插入,速度能提升一个数量级。
4.2 环节事件上报:谁来记录、怎么记录
溯源时间线的数据来自各个业务环节的录入人员。我强烈建议接口按角色分,不要让一个“万能录入接口”暴露出来。比如加工人员只能调加工事件、仓库人员只能调出库事件,这个约束在Spring Security注解里就能做:
@PostMapping("/api/trace/event") @PreAuthorize("hasAnyRole('ADMIN','OPERATOR')") public Result<Void> addEvent(@RequestBody @Valid AddEventRequest req) { traceEventService.addEvent(req); return Result.success(); }事件上报的表单字段不宜太多,否则没人愿意录。真实项目中,一屏最多四五个字段:环节名称、发生时间、地点、描述、上传1-3张照片,足够。照片上传走独立的upload接口,存MinIO或本地目录,事件表里只存URL。
这里有个细节:事件时间event_time应该允许录入人员,因为操作员很可能第二天补录昨天的出库记录。如果系统强制用数据库当前时间,生产流程就会变得很别扭。但在时间校验上要加一条:不能晚于当前时间、不能早于批次创建时间,否则数据链会乱。
4.3 消费者扫码查询接口的实现
这是被扫得最多的接口,设计核心是“快”。消费者扫码后,请求参数就是一个code,后端要做三件事:查码、查批次信息、查事件列表。
@GetMapping("/api/scan/{code}") public Result<TraceVO> scan(@PathVariable String code) { String cacheKey = "trace:scan:" + code; TraceVO vo = redisTemplate.opsForValue().get(cacheKey); if (vo == null) { vo = traceQueryService.queryByCode(code); // 缓存5分钟,避免热点码反复打数据库 redisTemplate.opsForValue().set(cacheKey, vo, 5, TimeUnit.MINUTES); } // 异步更新扫码次数,不阻塞主流程 traceQueryService.recordScanAsync(code); return Result.success(vo); }组装VO时,事件列表直接按event_time排序映射成前端友好的结构,附件URL转成完整可访问的地址。这里我踩过一个坑:附件URL存的是相对路径,发布到服务器后页面却是在HTTPS域名下访问,手机扫码打开结果图片全挂。解决方式是在配置里统一维护一个file.base-url,返回给前端时再拼上域名,不要存死路径。
另外,扫码次数和“首次扫描时间”一定要做记录,因为这是后面防伪的基础。
4.4 管理后台的召回与报表
召回功能在绝大多数教学性质的系统里是不做的,但真实项目里老板非常看重。实现上就是一个反向查询:输入溯源码或批次号,查出批次下所有发货记录,然后一键导出Excel,同时把批次状态改成“召回”。
报表模块最简单的做法是二开MyBatis-Plus的聚合查询,统计扫码量TOP10商品、扫码地域分布、近7天扫码趋势。这些数据看起来不起眼,但给客户演示时非常加分——因为“有人扫了”才是溯源系统真正产生价值的证明。建议在第三迭代再补,第一版不要贪多,贪多必乱。
5. 防伪、防串货与数据可信度:最容易翻车的地方
5.1 二维码被复制和转发的应对
坦率说,单靠软件无法100%防止二维码被复制。但我们可以做到“被复制后能被快速识破”,这是消费者信任体系的关键。
我在系统里加了三个机制:
- 首次扫描时间展示:消费者扫出来的页面上,明显位置展示“本溯源码首次被扫描于2024-06-12 10:23”。如果手里这张码不是第一次扫,消费者会自动警觉。
- 访问次数上限提醒:单个码扫描超过5次/20次(可按场景配置),页面提示“本码查询次数异常,谨防假冒”。
- 短链访问:二维码内容不要塞一串很长的参数,用短路径如
/m?c=xxxx即可,既容易扫又隐蔽。
配合线下的易碎纸、激光防伪标签一起用,才是一个完整的方案。跟客户汇报时一定要把这个预期说清楚,否则后面会变成售后纠纷。
5.2 数据“录了不能随意改”:审计日志与红冲
图纸管理里的数据可信度,核心不是技术而是机制。我给系统设计了几条铁律:
- 所有业务表都保留
create_by、create_time字段,关键表加update_by。 - 已发布到消费者端的溯源记录,不提供“修改”按钮。
- 数据录入错误走“红冲”:原记录保留,新增一条类型为“更正”的事件,写明更正原因和结果。
第二版我的系统里有过一次教训:管理员图省事,直接改了某批次的加工时间,结果消费者扫码看到的时间线前后矛盾,投诉到客服。加了红冲机制后,遇到类似情况就多一步操作,但消费者看到的是一条“原始记录+更正说明”,信任感反而更强。红冲机制实现很简单,就是trace_event表里加一个corrected_by字段,指向上一条被更正记录的ID。
5.3 防串货:出库信息与经销商绑定
串货是农产品、快消品行业的老大难问题。一个经销商把货卖到别人区域,会直接冲击价格体系。溯源系统虽然不能物理阻止运输,但可以做到“异常预警”。
实现思路:
- 发货单绑定批次、经销商、目标区域
- 消费者扫码时,后端拿到扫码IP所在城市(可以用IP归属地库,不依赖用户授权GPS)
- 后台定时任务比对“目标区域”和“实际扫码区域”,发现明显跨大区就生成预警记录
注意不要把这个机制做成对外展示,消费者看到的是正常溯源页面;预警只在管理后台出现,否则容易引发客诉。这个细节我在第一版时踩过坑——页面上一句“当前地址与发货地不符”,消费者立刻以为是假货,实际只是经销商串货,企业自己都哭笑不得。
5.4 一线录入人员作弊的流程防控
溯源系统最大的敌人不是黑客,是录入人员的懒惰和侥幸。常见的作弊包括:编造生产记录、上传网上找来的检测报告截图、照片张冠李戴。
流程上的应对方案:
- 照片上传时校验图片的Exif信息里的拍摄时间,能过滤一部分拿老图凑数的操作
- 关键环节设置“双人复核”:生产人员录入,质检或管理员审核后,该条记录才对消费者可见
- 检测报告支持上传PDF原件,比图片更可信
技术本身无法根治,但加上这些规则后,造假成本会明显提高。跟客户沟通时我会直说:系统是工具,真正的约束还得靠企业自己的内部管理制度,别再指望一套软件解决管理问题。
6. 部署与真实运营中的坑:文档里学不到的细节
6.1 二维码内容、尺寸与打印参数
二维码能不能扫出来,跟生成的代码关系不大,跟打印参数关系很大。
第一,二维码内容必须短。直接放https://www.example.com/xxx/page?code=abcdefg...这种长链接,打印出来后整张纸全是小黑点,路边手机扫码成功率会肉眼可见地下降。正确做法是短链接,比如https://trace.example.com/m/c123456,或者再短一点用二级路径映射。
第二,容错级别用H,不要为了图案好看用L。农产品的包装经常有褶皱、反光、污渍,容错率低一点就扫不出来。ZXing生成时设置:
EncodeHintType.ERROR_CORRECTION -> ErrorCorrectionLevel.H第三,打印尺寸别小于2cm×2cm,边缘留白(quiet zone)必须有。很多印刷厂会把二维码贴满整个标签,结果边缘被切掉,怎么扫都失败。
最稳妥的做法是:打样前直接生成一版A4测试图,用两三台不同档次的手机在模拟包装上各扫几遍,再批量印刷。这一步能省下后面无数的客诉。
6.2 手机端浏览器兼容
农产品溯源的扫码入口绝大多数是微信内置浏览器,这就有几个必须提前处理的技术点:
- 域名必须备案且用HTTPS,否则微信扫码直接拦截。
- 接口和页面如果部署在不同域名,CORS要配置好,不能只依赖开发环境的
@CrossOrigin。 - viewport的
width=device-width必须有,页面字体至少14px,按钮区域要够大,很多用户是中老年人,点不到是很现实的体验问题。 - 不要在页面上做整页弹窗式引导下载App,扫码用户会直接关掉页面。
另外,安卓和iOS的差异也要测:安卓下input聚焦会弹起键盘挤压布局,iOS下滑动手势和字体缩放策略不同。这些问题不是核心逻辑,但直接影响老板第一次给客户演示时的效果。
6.3 扫码峰值的缓存与限流
溯源系统的并发峰值通常出现在两类场景:产品发布会现场扫码活动、电商大促收货扫码。这两类场景下,一个码或者同一批码可能在几秒钟内被扫几千次。
应对手段很简单:缓存+限流。
Redis缓存已经覆盖了重复查询,热点码几乎不会打到数据库。限流方面,单机环境可以用Guava的RateLimiter,给扫码接口配置每秒200次;如果后续扩展成集群,再换Redis+Lua脚本做分布式限流。这里需要注意:第4.3节里的“异步更新扫码次数”一定要做成异步的,否则每扫一次就UPDATE一次trace_code表,并发高时会造成行锁等待,整个接口都会被拖慢。
6.4 时区、备份与安全收尾
部署到服务器时,有几个细节不对齐,后面会非常难排查:
- Docker容器内时区默认UTC,应用打印出来的时间会比北京时间少8小时。JVM启动参数加
-Duser.timezone=GMT+08,MySQL连接串加serverTimezone=Asia/Shanghai。 - 数据库每天做一次
mysqldump备份,保留最近15天;同时每季度做一次还原演练,不要等磁盘坏了才想起备份。 - 管理后台的账号不要用明文密码,BCrypt加密;默认管理员首次登录强制改密。
- 如果管理后台只给内部人员用,直接在网关或运维层加IP白名单,成本最低,防爆破效果最明显。
这些内容表面上跟“溯源功能”无关,但任何一个出问题,都会让客户对整个项目失去信心。我在交付时把运维文档里这几条单列出来,效果比写一百页操作手册好得多。
做了几个溯源项目之后,我最大的感受是:系统能不能落地,不取决于代码多漂亮,而取决于企业是不是真的愿意把生产流程沉淀成数据。二维码只是面子,批次管理、事件记录和召回能力才是里子。
如果你正在做毕业设计,别急着把系统做得多花哨,先把“批次-码-事件-查询”这条主链路做扎实,再看能不能把防串货预警、红冲机制讲清楚,这已经比大部分模板化项目高出一截。如果是在企业里做真实项目,建议从最小闭环开始,先让一个品类跑通,再逐步扩展到其他产品线。溯源系统最怕的不是功能少,而是一口气铺得太大,流程没理顺,最后录入数据断成一片。