news 2026/10/8 7:42:54

linjiashop源码深度拆解:用Spring Boot+MyBatis实现商品、订单与支付闭环

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
linjiashop源码深度拆解:用Spring Boot+MyBatis实现商品、订单与支付闭环

简介:这是一套基于 Java 的轻量级单商户商城系统源码,主项目 microapp-linjiashop-master 面向中小型商家、个人卖家和 Java 学习者,可用来搭建微信小程序商城,也能作为毕业设计或电商实战项目的参考。尤其适合有 Java 基础、想转入电商方向的开发者。代码采用 Spring Boot、MyBatis、Redis、Thymeleaf 等主流技术,覆盖商品管理、订单处理、用户中心、支付模块等核心业务;同时体现微服务拆分、小程序接口对接、OAuth2/HTTPS 安全认证、前端 Vue.js 交互以及 Docker 部署与自动化测试思路,适合想了解电商全栈架构的开发者细读。资源压缩包为 ZIP 格式,约 13.2MB;上游暂未解析出文件总数与类型明细,预计包含 Java 后端工程、配置文件和页面相关源码。截至目前该资源已有 166 人学习浏览,读者可以借此梳理从数据库表设计、接口开发到前后端联调、部署上线的完整体验。

1. 一套能跑通商品到成交的 Java 商城源码:linjiashop 的定位与打开方式

如果你是第一次打开 microapp-linjiashop-master 这套 Java 商城源码,大概率会被目录里的 admin、api、weixin 几个模块绕晕,甚至因为名字里的 microapp 前缀就以为它是微服务架构——这恰恰是很多人卡在第一步的原因。实际上这是一套单商户商城系统,后端用 Spring Boot + MyBatis 处理商品、订单、用户与支付回调,管理后台用 Thymeleaf 渲染页面,缓存依赖 Redis,接口层同时服务管理端和小程序端。它能解决的是从零搭建电商后端的样板问题:你不需要从空项目开始搭框架,直接把这套代码跑起来,就能看到商品上架、下单扣库存、订单状态机、微信登录的完整实现路径。适合刚接触 Java 电商开发、想找一个能运行的项目来改的人,也适合面试前快速回顾订单状态和库存扣减这些高频考点。

2. 技术栈与模块边界:从 pom.xml 看懂这套 Java 商城

2.1 从 pom.xml 确认版本搭配

拿到源码第一步,打开根目录的 pom.xml,确认 Spring Boot 版本和 MyBatis starter 版本是否兼容。这套项目最核心的依赖组合是 Spring Boot + MyBatis + Redis + Thymeleaf。常见的坑是 Spring Boot 2.3.x 配了最新版的 mybatis-spring-boot-starter,启动时报 Mapper 代理创建失败,查半天发现是版本不匹配。我一般先看 parent 里锁定的 spring-boot-dependencies,再决定要不要手动覆盖 starter 版本。

<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.4.RELEASE</version> <relativePath/> </parent> <dependencies> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.4</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> </dependencies>

mybatis-spring-boot-starter 负责数据访问层的自动装配,starter-data-redis 提供 RedisTemplate 和 Spring Cache 抽象实现,thymeleaf 渲染管理后台页面。这个组合决定了 linjiashop 的运行方式:一个 Spring Boot 进程同时承载 HTML 后台和 JSON 接口,而不是多个独立服务。

版本上有两个容易踩的点。第一,mybatis starter 2.1.x 默认扫描 @Mapper 注解的包路径,如果你的 mapper 被放在 common 这类子模块里,需要额外配置 @MapperScan 指定基础包,否则启动时会出现 mapper bean 找不到的报错。第二,Spring Boot 2.3 之后 Redis 默认客户端是 Lettuce,如果你原来的代码里用的是 Jedis 连接池配置,会直接不生效。判断办法是看启动日志里有没有 “Using Lettuce” 字样。这类问题属于“配置看着没问题但服务起不来”的典型,报错往往藏在嵌套堆栈里,需要耐心点开。

另外,如果依赖冲突导致 Lettuce 和 Jedis 同时出现在 classpath 里,Redis 连接池初始化的行为会变得很怪。我一般用 mvn dependency:tree -Dincludes=redis.clients:jedis 检查是否有意外的 Jedis 传递依赖,有就直接在 pom 里排除。这个检查花不了两分钟,能省掉后面反复重启试错的时间。

2.2 模块目录不是微服务,而是单体内部分层

microapp 前缀容易让人误判。这套代码并不是把订单、用户、商品拆成独立部署的微服务,而是同一个 Spring Boot 工程里的多模块分层,各模块只是职责边界不同。我一般把这类项目叫“单体分层商城”,对学习来说它比微服务更好入手,因为没有服务发现和分布式事务的额外负担,一次只用看一条调用链。

模块职责通常会这样划分:core 或 common 放实体、Mapper 和公共工具类;admin 是后台管理模块,提供商家管理页面和接口;api 是面向小程序和 App 的接口模块,商品列表、购物车、下单、支付都走这里;weixin 单独处理微信相关能力,比如登录 code2session、支付回调的报文解析。这样一个请求从 controller 到 service 到 mapper 再到 MySQL 的链路在每个模块里是完整的,调试时只需要打断点,不需要跨服务看链路。

模块主要职责常见路径前缀
core/common实体、Mapper、工具类被其他模块依赖
admin后台商品管理、订单管理/admin/**
api小程序端接口/api/**
weixin微信登录与支付回调/wx/**

这种结构对 Java 基础阶段的人来说非常友好。你不需要理解注册中心、网关、配置中心那一套,直接把 Maven 依赖关系理清楚,就知道代码该往哪里放。比如我要加一个优惠券模块,实体和 mapper 放 core,后台管理页面放 admin,小程序领券接口放 api,这个边界在动手之前就明确了。

2.3 配置文件的读取顺序与 Redis 依赖

Spring Boot 的配置文件优先级里,application.yml 是底,真正部署时会用环境变量或外部 config 目录覆盖。源码里一般有两个必改项:数据源连接串和 Redis 连接地址。下面这段是典型的 application.yml 局部内容。

server: port: 8080 spring: datasource: url: jdbc:mysql://127.0.0.1:3306/linjiashop?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: '123456' redis: host: 127.0.0.1 port: 6379 database: 0 mybatis: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: true

url 里的 characterEncoding 和 serverTimezone 一定要保留。MySQL 8 要求 serverTimezone,不然驱动初始化直接报时区错误;MyBatis 的 map-underscore-to-camel-case 打开之后,数据库的 create_time 才能自动转成实体里的 createTime 字段,否则所有带下划线的字段映射出来全是 null,这种问题很容易被误判成 SQL 写错。

Redis 的 database 默认 0,如果本机 Redis 被其他项目占用,可以改成 1 或 2,避免 key 冲突。需要注意的是,这套代码里的验证码、Token、热点数据几乎都依赖 Redis,所以数据库和 Redis 任何一个没准备好,启动阶段就会失败。建议先把 Redis 服务跑起来,再动 Maven 启动命令,否则看到的报错里一半以上都是从 Redis 连接引发的连锁异常。

3. 本地跑通全流程:建库、导数据、启动与登录

3.1 找到 SQL 脚本并导入

源码里一般会有 sql 或 doc 目录放数据库脚本,常见是一个 schema.sql 负责建库建表,另一个 data.sql 插入初始商家、分类和商品数据。拿到后先确认本机 MySQL 版本,5.7 和 8.0 对 utf8mb4 的默认配置不一样,但这类商城脚本通常都兼容。我一般用命令行导入而不是在图形化工具里手动跑,因为命令行能保留脚本执行顺序,报错位置也明确。

mysql -uroot -p123456 -e "CREATE DATABASE IF NOT EXISTS linjiashop DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" mysql -uroot -p123456 linjiashop < sql/schema.sql mysql -uroot -p123456 linjiashop < sql/data.sql

第一句创建库,指定 utf8mb4 字符集,避免后续插入中文乱码。第二句和第三句按顺序导入表结构和初始数据。如果 data.sql 里有外键依赖,导入顺序不能反,先表结构后数据是底线。执行完后可以用 mysql linjiashop -e "show tables;" 验证一下,商品表、订单表、用户表、支付记录表都应该在里面。

这里有一个“后悔药”性质的建议:建表语句里如果没写 DEFAULT CHARSET=utf8mb4,建议手工补上,改库字符集比改代码容易得多。乱码问题一旦发生,清理数据重新导入的成本不低。

3.2 修改配置并启动后端

导入完数据后,改 application.yml 里的数据库密码和 Redis 地址,确认本机 6379 端口能连上,然后开始启动。第一次跑优先用 Maven 直接启动 admin 模块下的启动类,因为热加载和日志输出都在前台,报错一目了然。命令行方式则用 mvn spring-boot:run 指定模块。

mvn spring-boot:run -pl admin -am

如果工程聚合了多个模块,-pl admin -am 的意思是只构建 admin 模块及其依赖模块,避免把无关子模块全部构建一遍。admin 是管理后台入口,启动端口以配置为准,通常是 8080。看到 Started AdminApplication 这样的日志后,说明 Web 容器起来了,但这不代表所有初始化都完成。

启动成功的标志不只是 “Started”,还要看有没有跟 Redis 建立起实际连接。如果 Redis 里没有出现预期的缓存 key,说明登录模块的初始化可能没走完,最直接的表现是验证码接口第一次调用超时或报错。先不要急着看页面,先用 curl 调一个不需要登录的接口验证链路,具体方法看下一节。

3.3 验证管理后台登录

浏览器访问 http://127.0.0.1:8080/admin,看到登录页说明 Thymeleaf 模板渲染成功。初始账号密码一般写在 data.sql 或 README 里,常见是 admin 加一串简单密码。登录时如果验证码不显示,优先检查 Redis 连接,因为验证码是先存进 Redis 再返回图片的。

不用浏览器也可以验证接口层。api 模块通常会有不需要登录就能访问的商品列表接口,比如 GET /api/goods,用 curl 加 JSON 请求头调用:

curl -H "Content-Type: application/json" http://127.0.0.1:8080/api/goods

如果返回 200 和商品 JSON,说明 Spring 容器、MyBatis、MySQL 链路是通的。如果这一层都不通,问题大概率在数据源配置而不是页面模板。先确定接口通,再进后台登录,排查范围会小很多。

登录成功后进入后台首页,左侧菜单一般包含商品管理、分类管理、订单管理和会员管理。此时整个系统的调用链已经跑通:浏览器请求、Spring Boot 控制器、Service 业务层、MyBatis Mapper、MySQL 数据库、Redis 缓存。到这一步,本地“能跑”的目标就完成了,接下来才有资格谈改造。

3.4 小程序接口的启动差异

管理后台跑起来后,小程序端的启动其实复用同一个进程,没有单独的端口。api 模块的接口统一挂在 /api 前缀下,微信开发者工具里配置的合法域名要先填 http://127.0.0.1:8080。开发期有个常用做法:在微信开发者工具里勾选“不校验合法域名”,这样可以直接用本地 IP,不用等域名备案。

但要注意,小程序端和后台管理的会话机制不一样。后台走登录态加 Cookie,小程序端走 token 请求头。如果你改动代码新增了一个 controller,记得在鉴权拦截器里加上对应的放行规则,否则请求会直接返回 401。很多人第一次联调时花半小时排查跨域,其实拦住的不是跨域,是拦截器白名单没配好。

4. 核心业务拆解:商品表、下单流程与支付回调

4.1 商品与 SKU 的表结构

电商代码里最核心的表结构组合是 goods 主表和 goods_sku 子表。单商户商城场景下,商品是 SPU,规格如颜色、尺码、版本是 SKU,价格和库存挂在 SKU 上而不是商品上。下面是一个简化后的建表参考。

CREATE TABLE `goods` ( `id` bigint NOT NULL AUTO_INCREMENT, `name` varchar(128) NOT NULL, `main_image` varchar(255) DEFAULT NULL, `category_id` bigint DEFAULT NULL, `status` tinyint DEFAULT '1', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `goods_sku` ( `id` bigint NOT NULL AUTO_INCREMENT, `goods_id` bigint NOT NULL, `spec` varchar(64) DEFAULT NULL, `price` decimal(10,2) NOT NULL, `stock` int NOT NULL DEFAULT '0', `image` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_goods_id` (`goods_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

数据访问层用 MyBatis 实现时,一次商品详情查询通常要分两步:先查 goods 主表,再查 goods_sku 列表。常见做法是在 mapper 里写一个关联查询,用 resultMap 的 collection 标签把 SKU 列表嵌套进去,这样前端只需要调用一次接口就能拿到完整商品信息,不用前端自己拼多个请求。

status 字段控制上下架,列表查询时记得过滤 status=1,否则会出现“商品列表里看到了已下架商品”这种低级 bug。price 字段用 decimal(10,2) 而不是 float,商城金额用浮点数保存会导致结算出现累积误差,这是一条 Java 电商开发里的老规矩。

4.2 创建订单:先锁库存还是先写订单

下单接口是最能体现业务功底的部分。常见做法是开启事务,先查 SKU 并加行锁,再校验库存,扣减成功后才插入订单记录。下面是一段贴近这类商城写法的代码参考。

@Transactional(rollbackFor = Exception.class) public Order createOrder(Long userId, Long skuId, Integer count) { GoodsSku sku = skuMapper.selectByIdForUpdate(skuId); if (sku == null || sku.getStock() < count) { throw new BizException("库存不足"); } skuMapper.decreaseStock(skuId, count); Order order = new Order(); order.setUserId(userId); order.setSkuId(skuId); order.setCount(count); order.setAmount(sku.getPrice().multiply(new BigDecimal(count))); order.setState(0); // 0=待支付 1=已支付 2=已发货 3=已完成 4=已关闭 order.setCreateTime(new Date()); orderMapper.insert(order); return order; }

逻辑说明:selectByIdForUpdate 使用数据库的行级排他锁,保证同一时刻只有一个线程能读到同一 SKU 的库存;decreaseStock 是 update 语句,把 stock 减掉 count,并在 SQL 里加 stock >= count 的条件,双保险防超卖。订单金额计算用 BigDecimal,避免 double 的浮点误差。

对应的 MyBatis XML 里,锁查询的写法如下:

<select id="selectByIdForUpdate" resultType="com.linjiashop.entity.GoodsSku"> select id, goods_id, spec, price, stock from goods_sku where id = #{id} for update </select>

这条 SQL 里的 for update 在事务提交前会一直持有行锁。如果一个请求已经把某个 SKU 锁住了,另一个请求的 selectByIdForUpdate 会等锁,这在并发下单时是正常的,压测时看到等待时间变长不要误判成死锁。

还有一层要注意:事务注解加在 public 方法上,且必须在这个类内部被调用才生效,如果从其他类绕过 service 直接调 mapper,锁和扣库存就散落了。有一种典型的翻车场景是新建订单后用户没支付,库存已经被扣了,这时候需要另外一个定时关单任务把库存加回来,常见做法是自己加一个定时任务去超时关单,再配合支付结果做补偿。

4.3 支付回调与状态机更新

支付回调是商城系统里最容易踩坑的接口。微信支付成功后,服务器会回调我们配置的 notify_url,回调报文是 XML,里面包含 out_trade_no、transaction_id、total_fee 和验签信息。常见做法是在回调接口里做三件事:验签、核对订单金额、幂等更新订单状态。

@PostMapping("/wx/pay/notify") public String payNotify(@RequestBody String xml) { Map<String, String> result = WxPayUtil.parseXml(xml); String orderNo = result.get("out_trade_no"); String fee = result.get("total_fee"); Order order = orderMapper.selectByOrderNo(orderNo); if (order == null || order.getState() != 0) { return WxPayUtil.responseXml("SUCCESS"); } if (!String.valueOf(order.getAmount().multiply(new BigDecimal(100)).intValue()).equals(fee)) { return WxPayUtil.responseXml("FAIL"); } order.setState(1); order.setPayTime(new Date()); orderMapper.updateState(order); return WxPayUtil.responseXml("SUCCESS"); }

逻辑说明:第一步判断订单为空或状态已经不是待支付,直接返回 SUCCESS,因为重复回调不能报错;第二步把订单金额换算成分,与微信回调金额比对,防止“支付了 1 元但订单是 100 元”的篡改场景;第三步更新订单状态并返回 SUCCESS 给微信服务器。

这里有个新手常犯的错:回调成功时返回的报文必须严格是字符串 SUCCESS,否则微信会按失败继续重试,造成回调日志刷屏。另一个细节是回调更新订单状态最好在数据库层面也加条件,比如 update order set state=1 where state=0 and order_no=xxx,避免两个线程同时处理同一个订单。这类源码读下来,基本就能理解整个商城的状态机是怎么转的:待支付、已支付、已发货、已完成、已关闭,每个状态变更都有一个明确的触发入口。

4.4 用户登录与小程序的 token 鉴权

后台管理端登录用 Session 配合 Thymeleaf 模板,Session 存 Redis,实现跨实例共享。小程序端登录流程则是 wx.login 拿到 code,后端拿 code 去微信服务器换 openid 和 session_key,再为本端生成一个自定义 token 返回给小程序,之后所有请求头带 token。这段流程在 Java 面试题里也是常客。用参考代码表示:

@PostMapping("/wx/login") public Map<String, Object> wxLogin(@RequestBody WxLoginReq req) { String openid = wxService.code2Session(req.getCode()); // 调用微信接口换取 openid String token = UUID.randomUUID().toString().replace("-", ""); redisTemplate.opsForValue().set("wx:token:" + token, openid, 7, TimeUnit.DAYS); Map<String, Object> result = new HashMap<>(); result.put("token", token); return result; }

逻辑说明:code2Session 是后端用 code 换 openid 的微信接口,token 是本地自己生成的,存 Redis 并设置过期时间。后续接口拦截器用 token 从 Redis 查 openid,就知道当前用户是谁。注意如果项目没配 appid 和 secret,这个接口会调不通,联调时优先检查这两项。

5. 常见问题排查:启动失败、验证码不显示、接口 404

5.1 Redis 连不上:服务一直重启或登录接口报错

现象:Spring Boot 启动过程中抛 RedisConnectionFailureException,或者管理后台登录时验证码接口直接 500,日志里出现 Connection refused: 127.0.0.1:6379。

原因:本机没有安装 Redis,或者 Redis 配置了密码而 application.yml 里没填 password,也可能是 Redis 端口被改过。

解决:先跑 redis-cli ping,确认是否返回 PONG。返回异常就检查 Redis 进程和端口;有密码就在配置里加 spring.redis.password。启动失败之后不要急着重试,先把 Error creating bean with name 'redisTemplate' 整段日志看完,确认连不上再动手。这也是一种“黑匣子”式报错,不点开堆栈很容易觉得是代码写错,其实是环境问题。

5.2 数据库中文乱码:商品名称全是问号

现象:导入 data.sql 后,数据库里中文正常,但后台页面显示乱码;或者反过来,页面正常但数据库查询出来是 ?,集中在第一次建库时出现。

原因:建库语句没有指定 utf8mb4,数据库默认字符集是 latin1;连接串也没带 characterEncoding=utf8。

解决:在 MySQL 里执行 ALTER DATABASE linjiashop CHARACTER SET utf8mb4;,并把 application.yml 的 jdbc 连接串补上 useUnicode=true&characterEncoding=utf8。已经乱码的表数据需要删掉重新导入,修改字符集不会自动修复存量数据。

5.3 验证码不显示:图片裂了或永远校验失败

现象:后台登录页能打开,但验证码区域显示红叉,或者验证码明明输对了,登录还是提示验证码错误。

原因:验证码问题通常有两类。一类是生成验证码的字体缺失,Linux 服务器上没有对应字体,图片渲染失败;另一类是 Redis 里验证码 key 过期时间太短,输入验证码时已经失效。

解决:先看后台日志有没有 AWTError 字样,有就是字体库问题,安装 fontconfig 依赖;没有的话把验证码过期时间从 60 秒调大到 180 秒,同时确认 Redis 的 key 前缀和校验逻辑一致。本地 Windows 上第一类问题很少见,部署到服务器时才高发。

5.4 接口 404 或 401:模块扫描和拦截器放行

现象:管理后台正常,但小程序端请求 /api/goods 返回 404;或者接口明明存在却返回 401。

原因:404 大概率是 controller 的 @RequestMapping 路径写错,或者包扫描起点没覆盖 api 模块;401 则是鉴权拦截器拦住了未放行的路径,常见于新增接口时忘了加白名单。

解决:404 先翻源码确认 mapping 路径,看控制器类上的 @RequestMapping("/api/goods") 是否真的存在;401 找拦截器注册配置,把新接口添加到排除路径里。这里有一条经验:新增 controller 时先加白名单再写业务,避免联调时反复被拦。

5.5 登录态失效:token 明明在 Redis 里却鉴权失败

现象:小程序端登录成功后,过一段时间再请求接口返回 401,但查 Redis 发现 token 还存在。

原因:多数情况是 Redis key 的前缀不一致。登录时用的前缀是 wx:token:,拦截器校验时取的前缀是 wx:login:,两处字符差一点就匹配不上。这种问题日志里可能没有任何报错,属于最磨人的一种。

解决:全局搜索 token 前缀字符串,把设置和读取统一到一个常量类里。另一个原因是 Jackson 反序列化 Redis 里的对象时类型不兼容,通常发生在把用户对象序列化成 JSON、反序列化时又要求转成指定对象,解决方法是给 RedisTemplate 显式配置 GenericJackson2JsonRedisSerializer。

6. 进阶用法:缓存预热与上线前的最小改造

6.1 缓存预热:让第一批请求不再慢

这套代码跑通之后,第一个可以动手的改造是商品详情的缓存预热。第一次访问某个商品详情时,Redis 里没有缓存,请求会穿透到 MySQL,虽然单次查询不慢,但首页同时加载几十个商品会造成明显的响应毛刺。常见做法是在应用启动完成后,用 ApplicationRunner 把热门前 N 个商品的详情写入 Redis,key 设计为 goods:detail:{id},过期时间设 30 分钟,接口读取时先查缓存。

@Component public class GoodsCacheWarmer implements ApplicationRunner { @Autowired private GoodsService goodsService; @Autowired private StringRedisTemplate redisTemplate; @Override public void run(ApplicationArguments args) { List<Long> hotIds = goodsService.getHotGoodsIds(20); for (Long id : hotIds) { String detail = goodsService.getDetailJson(id); redisTemplate.opsForValue().set("goods:detail:" + id, detail, 30, TimeUnit.MINUTES); } } }

这个类的执行时机是 Spring 容器初始化完成之后,不会影响启动流程,即使预热失败也只是缓存为空,接口照样能走数据库兜底。预热逻辑放在代码里,而不是手动执行 SQL 或脚本,好处是部署新版本时不需要人工介入,服务起来自动完成。

6.2 用 JMeter 验证下单接口的并发表现

改造完缓存,用 JMeter 做一次基础压测,能直观看到这套代码的边界。我一般会建一个线程组,模拟 50 个并发用户同时调用商品列表接口和下单接口。商品列表接口关注的是 TPS 和响应时间,下单接口更关键的是有没有超卖或锁等待超时。

压测前把日志级别调成 WARN,否则并发请求产生的 INFO 日志会把磁盘写满,压测结果也会被日志写入拖慢。下单接口压测时观察数据库连接池和行锁等待时间,如果错误率集中在 deadlock 或 Lock wait timeout exceeded,就要检查是否正确使用了 for update,以及是否有慢 SQL 把事务拉长。

6.3 上线前改掉这些默认值

本地能跑和真正上线之间,还有一段距离。数据库密码不能留在 application.yml 里,常见做法是用环境变量注入,比如 spring.datasource.password=${MYSQL_PASSWORD};Redis 要开密码验证,不能裸奔在局域网;管理后台的初始账号密码必须强制第一次登录修改。另外,如果项目里有 Swagger 或调试接口,生产环境要关掉,避免接口文档暴露给公网。

从那以后我每拆一套商城源码,都会先确认 Redis 依赖、数据库脚本、模块扫描范围这三件事再动手,顺序一旦颠倒,排查成本会翻倍。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/8 7:42:54

微信小程序云开发菜谱源码:免服务器搭建完整流程与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/8 7:42:03

QT/C++开发俄罗斯方块:从核心算法到课设答辩完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/8 7:41:27

TPS259483AYWPR+PIC32MX360F512L工业级主动电源保护方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/8 7:41:08

外资企业为何率先采用自动化循环水系统?全成本与落地逻辑解析

站在冷却塔底下往上望&#xff0c;水雾扑面而来&#xff0c;塔底的集水盘里&#xff0c;pH探头、电导率探头、余氯探头一字排开&#xff0c;旁边的小控制柜里PLC指示灯有节奏地闪着。这是我前几年去一家外资化工厂参观时看到的场景。对方负责公用工程的老工程师跟我说了一句话&…

作者头像 李华
网站建设 2026/10/8 7:41:05

OpenCV烟丝图像分割实战:HSV阈值+形态学+轮廓过滤

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/8 7:40:31

TPS259483AYWPR与MKV46F128VLH16工业电源路径协同设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华