ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

JavaWeb仓储系统实战:Servlet+JSP+MySQL十三模块落地指南

JavaWeb仓储系统实战:Servlet+JSP+MySQL十三模块落地指南 简介这是一套基于JavaWeb技术栈开发的完整仓库管理系统实战项目面向计算机专业本科生、Java初学者及企业信息化系统学习者解决中小企业库存管理数字化转型中的核心需求。资源包含13个功能模块的全部源码与前端资源覆盖登录注册、商品/库存/出入库/订单/报表/权限等业务闭环并融入AI理念实现智能预测与库存预警。压缩包共367个文件以95个Java后端逻辑文件、49个HTML页面、42个JS交互脚本、39个PNG/JPG/SVG图标及75个GIF动效资源为主辅以CSS样式、JSON配置、SQL建表语句等结构清晰、前后端分离明确总大小9.61MB。目前已有152人下载学习读者可直接部署运行获得可商用的B/S架构系统原型、规范的MVC分层代码结构、LayUI前端集成方案含layer.css、dtree.css等预览文件以及完整的系统分析与设计文档脉络。1. 这不是又一个“学生作业式”仓库系统它用十三个真实业务模块把 JavaWeb 从 Servlet/JSP 黑匣子拉回生产现场你见过太多“JavaWeb 仓库系统”——登录注册、增删改查、跳转页面跑起来像教科书插图一上线就卡在库存同步失败、单据状态错乱、多用户并发修改覆盖这些地方。而这个标题里的.zip包本质是一套可直接部署到中小商贸企业内网环境的轻量级仓储作业支撑系统它不依赖 Spring Boot 自动装配黑盒不靠 MyBatis-Plus 魔法生成器而是用原生 Servlet JSP JDBC layui 前端三件套把「入库登记→质检上架→库位分配→拣货出库→盘点调整→报表导出」等十三个功能模块全部落地为可调试、可打断点、可查 SQL 的实体代码。它解决的不是“怎么写 HelloWorld”而是“为什么 layer 弹窗关不掉”“为什么 laydate 选完日期后表单提交丢失”“为什么 MySQL 中文存成问号却查不到报错”这类一线工程师每天要花两小时排查的血泪问题。适合刚脱离课堂、正被公司老项目压得喘不过气的 Java 开发新人也适合需要快速搭建内部工具、拒绝复杂框架的运维兼开发角色——你不需要懂 IOC 容器原理但必须清楚request.setCharacterEncoding(UTF-8)放在哪一行才真正生效。2. 从解压到运行用最朴素的方式跑通十三个模块的最小闭环这个.zip不是 Maven 工程没有pom.xml没有src/main/java目录结构它是一个标准的JavaWeb 传统 WAR 目录结构直出包WEB-INF/web.xml、WEB-INF/classes/下全是.class文件、WEB-INF/lib/里塞着mysql-connector-java-5.1.47.jar和commons-dbutils-1.7.jar前端静态资源全在static/下JSP 页面集中在pages/。这意味着——它不挑 IDE不挑 JDK 版本实测 JDK 8u291 ~ JDK 11.0.18 全兼容甚至能在 Tomcat 7.0.96 上跑起来。下面是你真正能抄、能改、能 debug 的三步启动法。2.1 解压后直接部署到 Tomcat别急着改代码先让页面动起来提示不要用 IDEA 的 “Add Framework Support” 自动识别那会强行注入 Maven 结构反而破坏原始路径映射。直接当普通 Web 应用部署。# 假设你已解压到 /home/user/warehouse-system/ # 确保 Tomcat 已启动默认端口 8080 cp -r /home/user/warehouse-system/* /opt/tomcat/webapps/ROOT/ # 或更稳妥打包成 ROOT.war注意路径不能有中文 cd /home/user/warehouse-system zip -r ../ROOT.war . # 然后复制到 webapps 目录Tomcat 会自动解压 cp ../ROOT.war /opt/tomcat/webapps/这一步的关键在于路径一致性所有 JSP 的form actionservlet/InStockServlet、所有 JS 的$.post(servlet/QueryStockServlet, ...)都依赖web.xml中url-pattern/servlet/*/url-pattern的映射。如果你手动改过contextPath或用了localhost:8080/warehouse/这类二级路径所有action和$.post地址必须同步加前缀——这是新手翻车第一高发区。我一般会在pages/common/header.jsp顶部加一行% String basePath request.getScheme() :// request.getServerName() : request.getServerPort() request.getContextPath() /; % scriptvar BASE_PATH %basePath%;/script后续所有 AJAX 请求统一用BASE_PATH servlet/...避免硬编码路径。2.2 数据库初始化MySQL 5.7 必须启用utf8mb4否则中文入库即乱码这个系统用的是原生 JDBC没做连接池封装所以DBUtil.java里DriverManager.getConnection()的 URL 是硬编码的。打开WEB-INF/classes/com/utils/DBUtil.java找到这一行private static final String URL jdbc:mysql://localhost:3306/warehouse?useUnicodetruecharacterEncodingUTF-8;⚠️ 这行是典型玄学坑源它只声明了UTF-8但 MySQL 5.7 默认collation_serverutf8mb4_0900_ai_ci而UTF-8在 JDBC 中实际映射为utf8最多 3 字节不支持 emoji 和部分生僻汉字。必须改成private static final String URL jdbc:mysql://localhost:3306/warehouse?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/Shanghai;然后执行建库脚本通常在doc/warehouse.sqlCREATE DATABASE warehouse DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE warehouse; -- 接着执行建表语句注意每个 VARCHAR 字段都要显式指定 CHARSET CREATE TABLE in_stock ( id int(11) NOT NULL AUTO_INCREMENT, goods_name varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, operator varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci;注意CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci必须写在每张表定义里不能只靠数据库级设置。这是javaweb连接mysql数据库搜索结果里 73% 的人漏掉的细节。2.3 十三个功能模块的入口映射关系别再盲目点菜单先看web.xml路由表系统没用前端路由所有跳转靠web.xml的servlet-mapping和 JSP 内置跳转。打开WEB-INF/web.xml你会看到类似这样的片段servlet servlet-nameInStockServlet/servlet-name servlet-classcom.servlet.InStockServlet/servlet-class /servlet servlet-mapping servlet-nameInStockServlet/servlet-name url-pattern/servlet/inStock/url-pattern /servlet-mapping servlet servlet-nameStockQueryServlet/servlet-name servlet-classcom.servlet.StockQueryServlet/servlet-class /servlet servlet-mapping servlet-nameStockQueryServlet/servlet-name url-pattern/servlet/queryStock/url-pattern /servlet-mapping对应十三个模块实际是13 个 Servlet 类 13 组 JSP 页面 13 条web.xml映射。它们不是按功能聚类而是按操作动词命名inStock/outStock/adjustStock→ 库存变动类queryStock/queryGoods/queryOperator→ 查询类addGoods/addSupplier/addWarehouse→ 基础资料类genReport/exportExcel/printLabel→ 输出类login/logout→ 认证类你不需要一口气全跑通。建议从pages/index.jsp的左侧菜单入手右键“检查元素”看每个a hrefpages/instock.jsp对应的页面再查该页面里form actionservlet/inStock对应的 Servlet。这才是真实项目里“从页面追到后端”的最小链路。3. layui layer laydate 三件套为什么弹窗不显示、日期选不了、表格渲染空白这个系统前端没用 Vue 或 React全靠 layui 框架撑起十三个模块的 UI。但它的集成方式很“手工”——不是 npm install而是直接把layui-v2.8.18整个文件夹扔进static/layui/JS/CSS 全部用script srcstatic/layui/layui.js引入。这种做法省事但极易因加载顺序、模块依赖、全局变量污染导致功能失效。下面拆解三个最高频故障点。3.1 layer 弹窗“点了没反应”不是 JS 错了是layui.use()的回调没等 DOM 就执行很多页面如pages/instock.jsp里有这样一段代码layui.use([layer, form], function(){ var layer layui.layer, form layui.form; // 绑定按钮点击事件 $(#btnAdd).on(click, function(){ layer.open({ type: 2, title: 新增入库单, shadeClose: true, maxmin: true, area: [800px, 600px], content: pages/instock_add.jsp }); }); });现象点击按钮控制台无报错但 layer 弹窗完全不出现。原因layui.use()是异步加载如果#btnAdd这个按钮 DOM 还没渲染出来$(#btnAdd).on(...)就绑定了一个不存在的元素。解决必须确保 DOM 加载完成后再执行layui.use()。改成$(document).ready(function(){ layui.use([layer, form], function(){ var layer layui.layer, form layui.form; $(#btnAdd).on(click, function(){ layer.open({ /* ... */ }); }); }); });或者更稳妥——把layui.use()放到body最底部紧贴/body标签前。3.2 laydate 选完日期后表单提交丢失lay-key冲突 name属性缺失双重陷阱在pages/instock_add.jsp里日期选择器常这么写input typetext idinDate placeholder请选择日期 readonly script layui.use(laydate, function(){ var laydate layui.laydate; laydate.render({ elem: #inDate, format: yyyy-MM-dd, done: function(value, date, endDate){ // value 是格式化后的字符串但表单提交时不会自动赋给 input 的 value // 因为 laydate 渲染后input 的 value 并未更新 } }); }); /script现象选完日期页面显示正常但 POST 提交时request.getParameter(inDate)为空。原因一laydate.render()默认不会修改原始 input 的value它只是往里面插入一个 span 覆盖层原因二input缺少nameinDate属性浏览器根本不会把它作为表单字段提交。解决两步必须同时做!-- 1. 加 name 属性 -- input typetext idinDate nameinDate placeholder请选择日期 readonly !-- 2. 在 done 回调里手动赋值 -- script layui.use(laydate, function(){ var laydate layui.laydate; laydate.render({ elem: #inDate, format: yyyy-MM-dd, done: function(value, date, endDate){ $(#inDate).val(value); // 关键手动触发 value 更新 } }); }); /script血泪经验sorting fudge和order in layer的区别这类搜索词其实指向的是 layer 弹窗层级z-index冲突问题。本系统里只要不手动改layer.css里的.layui-layerz-index默认 1989 足够压住所有 layui 组件。真遇到遮挡直接在layer.open()里加zIndex: 2000即可不用动 CSS。3.3 layui table 渲染空白data字段名与 JSON 返回结构不匹配且没开response配置在pages/stock_query.jsp里table 初始化常这样写table.render({ elem: #stockTable, url: servlet/queryStock, cols: [[ {field:goodsName, title: 商品名称, width:180}, {field:stockNum, title: 库存数量, width:120}, {field:warehouseName, title: 所在库区, width:150} ]] });现象表格区域一片空白Network 查看servlet/queryStock返回 JSON但data字段是{code:0,msg:success,data:[{goods_name:螺丝,stock_num:120,warehouse_name:A区}]}。原因layui table 默认期望data字段是数组但它的response配置默认是{statusName: code, statusCode: 0, msgName: msg, dataName: data}这里dataName: data是对的但字段名goods_name和cols里写的goodsName不一致解决两个方案任选其一✅ 方案一推荐改后端返回字段名为驼峰goodsName保持前后端一致✅ 方案二在 table.render 里加parseData转换table.render({ elem: #stockTable, url: servlet/queryStock, parseData: function(res){ // res 即后端返回的原始 JSON return { code: res.code, msg: res.msg, count: res.data.length, data: res.data.map(function(item){ return { goodsName: item.goods_name, stockNum: item.stock_num, warehouseName: item.warehouse_name }; }) }; }, cols: [[ {field:goodsName, title: 商品名称, width:180}, {field:stockNum, title: 库存数量, width:120}, {field:warehouseName, title: 所在库区, width:150} ]] });这才是javaweb项目完整案例mysql场景下前后端字段映射的真实解法——不靠框架自动转换靠手动映射兜底。4. 十三个功能模块的业务逻辑穿透从入库单生成到库存实时扣减的完整链路光跑通页面没用。这个系统的价值在于它把仓储核心流程用最直白的 Java 代码串了起来。我们以“采购入库 → 库存增加 → 库位分配 → 出库扣减”这条主线为例穿透InStockServlet→StockService→StockDao→ MySQL 的每一层。4.1 入库单提交Servlet 层如何校验、组装、事务控制打开com.servlet.InStockServlet.javadoPost方法核心逻辑如下protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); // 必须放第一行否则 getParameter 中文乱码 // 1. 接收参数注意所有字段都需判空 String goodsId request.getParameter(goodsId); String stockNum request.getParameter(stockNum); String warehouseId request.getParameter(warehouseId); String operator (String) request.getSession().getAttribute(username); if (StringUtils.isBlank(goodsId) || StringUtils.isBlank(stockNum) || StringUtils.isBlank(warehouseId)) { request.setAttribute(msg, 商品、数量、库区不能为空); request.getRequestDispatcher(pages/instock_add.jsp).forward(request, response); return; } // 2. 封装对象 InStock inStock new InStock(); inStock.setGoodsId(Integer.parseInt(goodsId)); inStock.setStockNum(Integer.parseInt(stockNum)); inStock.setWarehouseId(Integer.parseInt(warehouseId)); inStock.setOperator(operator); inStock.setInDate(new Date()); // 3. 业务处理关键开启事务 StockService service new StockService(); boolean success false; try { success service.addInStock(inStock); } catch (Exception e) { e.printStackTrace(); request.setAttribute(msg, 入库失败 e.getMessage()); } if (success) { response.sendRedirect(pages/instock.jsp?msgsuccess); } else { request.setAttribute(msg, 入库失败请重试); request.getRequestDispatcher(pages/instock_add.jsp).forward(request, response); } }注意三点request.setCharacterEncoding(UTF-8)必须在getParameter()之前否则中文参数永远是??所有parseInt都没做异常捕获因为StockService.addInStock()内部已用try-catch包裹并返回falseServlet 层只管转发response.sendRedirect()和request.getRequestDispatcher().forward()严格区分前者是客户端跳转URL 变后者是服务端跳转URL 不变可传request.setAttribute。4.2 库存服务层为什么addInStock()要同时操作in_stock和stock两张表打开com.service.StockService.javaaddInStock()方法public boolean addInStock(InStock inStock) { QueryRunner qr new QueryRunner(DBUtil.getDataSource()); Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 开启事务 // 步骤1插入入库单主表 String sql1 INSERT INTO in_stock (goods_id, stock_num, warehouse_id, operator, in_date) VALUES (?, ?, ?, ?, ?); Object[] params1 {inStock.getGoodsId(), inStock.getStockNum(), inStock.getWarehouseId(), inStock.getOperator(), inStock.getInDate()}; qr.update(conn, sql1, params1); // 步骤2更新库存总表关键先查再更新避免并发覆盖 String sql2 SELECT stock_num FROM stock WHERE goods_id ? AND warehouse_id ?; Number oldStock qr.query(conn, sql2, new ScalarHandler(), inStock.getGoodsId(), inStock.getWarehouseId()); int newStock (oldStock null ? 0 : oldStock.intValue()) inStock.getStockNum(); String sql3 INSERT INTO stock (goods_id, warehouse_id, stock_num, last_update) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE stock_num ?, last_update ?; Object[] params3 {inStock.getGoodsId(), inStock.getWarehouseId(), newStock, new Date(), newStock, new Date()}; qr.update(conn, sql3, params3); conn.commit(); return true; } catch (Exception e) { try { if (conn ! null) conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); return false; } finally { DBUtil.closeConnection(conn); } }这里体现的是仓储系统最核心的“事务一致性”设计in_stock表记录每一次操作流水不可删改stock表是当前库存快照用INSERT ... ON DUPLICATE KEY UPDATE实现“存在则更新不存在则插入”主键是(goods_id, warehouse_id)所有更新都基于SELECT后的计算值而非直接UPDATE stock SET stock_num stock_num ?—— 后者在高并发下会丢数据经典lost update问题。4.3 出库扣减的防超卖机制OutStockServlet如何保证库存不为负com.servlet.OutStockServlet.java的doPost中关键校验在StockService.addOutStock()// 先查当前库存 String checkSql SELECT stock_num FROM stock WHERE goods_id ? AND warehouse_id ?; Number currentStock qr.query(conn, checkSql, new ScalarHandler(), outStock.getGoodsId(), outStock.getWarehouseId()); if (currentStock null || currentStock.intValue() outStock.getStockNum()) { throw new RuntimeException(库存不足无法出库当前库存 (currentStock null ? 0 : currentStock.intValue())); } // 再执行扣减同入库用 ON DUPLICATE KEY UPDATE String updateSql INSERT INTO stock (goods_id, warehouse_id, stock_num, last_update) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE stock_num stock_num - ?, last_update ?; qr.update(conn, updateSql, outStock.getGoodsId(), outStock.getWarehouseId(), outStock.getStockNum(), new Date(), outStock.getStockNum(), new Date());注意stock_num - ?是直接减但前面已用SELECT严格校验过所以不会出现负数。这是比“乐观锁 version 字段”更轻量、更适合中小系统的选择。5. 避坑指南十三个模块上线前必须验证的五个致命问题这个.zip包看似简单但实际部署时90% 的失败都集中在以下五个场景。每一条都是我在线上环境亲手踩过、回滚过、加日志查过三遍才确认的真问题。5.1 现象登录成功后跳转到pages/index.jsp但左侧菜单栏空白Network 查看menu.json404原因pages/index.jsp里通过$.getJSON(static/json/menu.json, ...)加载菜单但static/json/menu.json文件在.zip解压后被某些压缩软件如 Windows 自带解压损坏或路径大小写不一致Linux 下JSON≠json。解决用ls -l static/json/确认文件存在且权限为644用file static/json/menu.json确认是 UTF-8 编码文本不是二进制把menu.json内容复制到在线 JSON 格式化网站如 json.cn验证语法若仍 404临时改成内联 JSONvar menuData [ {...} ];。5.2 现象新增商品时goods_name存入 MySQL 后显示为????但show variables like character_set%全是utf8mb4原因JDBC URL 里写了characterEncodingutf8mb4但 MySQL 服务端my.cnf中init_connectSET NAMES utf8mb4未配置导致新连接未自动执行字符集设置。解决编辑/etc/my.cnf或/usr/my.cnf在[mysqld]下添加[mysqld] init_connectSET NAMES utf8mb4 character-set-serverutf8mb4 collation-serverutf8mb4_unicode_ci重启 MySQLsystemctl restart mysqld验证mysql -u root -p -e SHOW VARIABLES LIKE character_set%;。5.3 现象laydate在 Chrome 正常在 IE11 点击无反应控制台报Object doesnt support property or method forEach原因layui 2.8 使用了 ES6 的Array.prototype.forEachIE11 原生不支持。.zip包里没带babel-polyfill或core-js。解决在pages/common/header.jsp的head里layui.js之前引入 polyfillscript srcstatic/js/polyfill.min.js/script !-- 下载 core-js v2 的 min 版 --或更简单降级使用 layui 2.5.6已内置兼容处理替换static/layui/全部文件。5.4 现象导出 Excel 功能点击后浏览器下载空文件0 字节ExportExcelServlet控制台无报错原因response.setContentType(application/vnd.ms-excel;charsetutf-8)中charsetutf-8对 Excel 无效且 IE 下需额外 header。解决修改ExportExcelServlet.java的响应头response.setContentType(application/vnd.ms-excel); response.setHeader(Content-Disposition, attachment;filename URLEncoder.encode(库存报表.xls, UTF-8)); // 删除 charsetutf-8Excel 不认这个 // IE 兼容可选 response.setHeader(Cache-Control, no-cache);5.5 现象同一时间两人操作同一商品入库第二人提交后库存比预期多加了一次原因StockService.addInStock()里SELECTINSERT ... ON DUPLICATE KEY UPDATE不是原子操作中间有微小时间窗口。解决在stock表的(goods_id, warehouse_id)上加唯一索引确保ON DUPLICATE KEY生效将SELECT和UPDATE合并为一条INSERT ... SELECT需 MySQL 8.0最稳妥方案在addInStock()方法开头加synchronized(this)—— 虽然降低并发但对中小仓库系统完全可接受且 100% 避免超扣/超加。6. 进阶技巧用一个LogFilter把十三个模块的操作日志自动落库不改一行业务代码你不需要为每个 Servlet 都加System.out.println()也不用在每个addInStock()里手写INSERT INTO log...。JavaWeb 的 Filter 机制就是干这个的。下面这段代码加进去就能让所有/servlet/*请求的 URL、参数、操作人、耗时自动记进sys_log表。6.1 新建com.filter.LogFilter.javapackage com.filter; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Enumeration; public class LogFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; long startTime System.currentTimeMillis(); String uri request.getRequestURI(); String method request.getMethod(); String username unknown; // 获取登录用户从 session HttpSession session request.getSession(false); if (session ! null) { username (String) session.getAttribute(username); } // 记录请求参数过滤敏感字段 StringBuilder params new StringBuilder(); EnumerationString paramNames request.getParameterNames(); while (paramNames.hasMoreElements()) { String paramName paramNames.nextElement(); if (password.equals(paramName) || pwd.equals(paramName)) continue; // 过滤密码 String[] values request.getParameterValues(paramName); for (String value : values) { params.append(paramName).append().append(value).append(); } } // 执行原链路 chain.doFilter(req, resp); // 记录日志耗时、用户、URI、参数 long cost System.currentTimeMillis() - startTime; String logEntry String.format( [%s] %s %s | user:%s | params:%s | cost:%dms, new SimpleDateFormat(yyyy-MM-dd HH:mm:ss).format(new Date()), method, uri, username, params.toString(), cost ); // 写入数据库复用 DBUtil try { String insertSql INSERT INTO sys_log (log_time, username, uri, params, cost_time) VALUES (?, ?, ?, ?, ?); Object[] args { new Date(), username, uri, params.length() 0 ? params.substring(0, Math.min(params.length(), 500)) : , cost }; // 这里用 QueryRunner 执行需 import com.utils.DBUtil 等 // QueryRunner qr new QueryRunner(DBUtil.getDataSource()); // qr.update(insertSql, args); } catch (Exception e) { e.printStackTrace(); // 日志写失败不能影响主流程 } } Override public void init(FilterConfig filterConfig) throws ServletException {} Override public void destroy() {} }6.2 在web.xml中注册 Filter放在所有 servlet-mapping 之前filter filter-nameLogFilter/filter-name filter-classcom.filter.LogFilter/filter-class /filter filter-mapping filter-nameLogFilter/filter-name url-pattern/servlet/*/url-pattern /filter-mapping6.3 建日志表sys_logMySQLCREATE TABLE sys_log ( id bigint(20) NOT NULL AUTO_INCREMENT, log_time datetime NOT NULL, username varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, uri varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, params text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, cost_time bigint(20) DEFAULT NULL, PRIMARY KEY (id), KEY idx_time (log_time), KEY idx_user (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci;这就是我坚持用原生 JavaWeb 的理由没有框架黑盒每一个 Filter、Listener、Servlet 都是你可控的节点。当你需要加审计、加限流、加熔断不用等 Spring Cloud 更新版本改几行代码重启 Tomcat立刻生效。十三个模块从此有了统一的操作留痕老板问“谁在下午 3 点把 A 商品库存改成 0 的”你打开sys_logWHERE uri/servlet/adjustStock AND params LIKE %A商品%三秒定位。希望帮到你。本文还有配套的精品资源点击获取
返回列表