ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

基于SSM的母婴商城毕业设计:从框架搭建到订单事务的完整实现

基于SSM的母婴商城毕业设计:从框架搭建到订单事务的完整实现

简介:这是一份基于SSM框架开发的母婴用品网站Java毕业设计全套资料,面向计算机专业毕业生与Java学习者,可作为毕业设计、课程设计或项目实战的完整参考。项目覆盖管理员后台(用户管理、商品分类管理、商品信息管理、订单管理、留言管理等)、用户前台(购物车、个人中心、收藏管理)以及首页、论坛信息、新闻资讯、留言反馈等模块,业务逻辑清晰,角色权限分明。同时附有毕业论文与答辩PPT,方便快速整理成自己的设计文档。压缩包共1372个文件,以Java源码、JSP页面、JavaScript脚本、CSS样式和SQL数据库脚本为主,另有配置文件与前端图片资源,整体大小63.7MB,采用JDK1.8、Tomcat7、MySQL5.7即可部署运行。目前已有84人学习下载,适合需要获取完整可运行电商网站源码并完成毕业设计的同学参考,也可在此基础上做二次开发定制。

1. 别把 SSM 母婴商城当“又一个增删改查”:先看清这套毕设的真实分量

第一次看到“基于 ssm 框架开发的母婴用品网站”这个选题,我的第一反应是:又一个电商 CRUD,换了个品类名而已。但真正坐下来拆需求才发现,这个题目比图书商城、二手交易平台要重得多——它要求你处理多级商品分类、适用月龄与安全标准这类母婴专属属性、预售与库存锁定、订单状态流转,还要让论文里有一张能讲清楚的数据模型图。对做毕业设计的人来说,这反而是好事:论文有东西可写,答辩有业务场景可讲,源码里也有真正值得展示的代码。下面按我带学生做项目的习惯,从框架选型、数据库设计、核心链路实现,一路写到避坑清单和答辩前怎么准备,新手能照着跑通,已经动手的也能补上几个容易拖进度的盲区。

2. 为什么是 SSM 而不是 Spring Boot:选型逻辑与骨架搭建

2.1 SSM 三件套的职责边界

很多人在开题时纠结:老师指定 SSM,但网上教程清一色 Spring Boot,代码抄都抄不顺。先别急着换。SSM 不是过气框架,它只是比 Spring Boot 更“显式”——每一个 Bean 怎么注册、拦截器怎么配、SQL 写在哪,你都得自己搭一遍。恰恰是这种“显式”,让答辩时你能把三层架构讲得很具体,而不是只说“Spring Boot 自动配置了”。SSM 三件套的职责可以这样划:

组件职责对应层
Spring管理 Service、Dao 对象的创建与依赖注入,处理事务业务层
SpringMVC接收 HTTP 请求,做参数绑定,返回视图或 JSON表现层
MyBatis把接口方法映射成 SQL,处理结果集到对象的转换持久层

这个分工直接决定了后面代码的目录结构。前端请求先到 SpringMVC 的 Controller,Controller 只做参数接收和视图转发,真正的业务逻辑在 Service 里;Service 调 MyBatis 的 Mapper 接口,Mapper 的 SQL 写在 XML 文件里。这样一个请求从 Controller 到 Service 再到 Mapper,层次是单方向的,谁依赖谁都能说清。

这里有个常见的选型问题:既然 Spring Boot + MyBatis 更省事,为什么还要用 SSM?我一般这样建议——如果开题已经定了 SSM,就按 SSM 做完,不要中途换 Boot。因为毕业设计的评分标准里,“框架原理掌握程度”占很大比重,SSM 手动配置的过程本身就是你讲原理的素材。如果还没定框架,且导师允许自由选择,那用 Spring Boot 确实开发效率更高。本文后面所有内容都按 SSM 讲,你换 Boot 时只需要把配置部分替换成自动配置。

2.2 Maven 工程搭建与目录结构

SSM 项目基本都用 Maven 管理依赖,而不是手动往 WEB-INF/lib 丢 jar 包。用 Maven 的好处是依赖版本清晰、打包方便,而且论文里可以写“基于 Maven 的依赖管理”,也算一个可说的点。先看最基本的 pom.xml:

<properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <spring.version>5.2.3.RELEASE</spring.version> <mybatis.version>3.5.4</mybatis.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>${mybatis.version}</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.4</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.49</version> </dependency> </dependencies>

这里版本号是写死的主要疑点。Spring 5.2 搭配 MyBatis 3.5 是稳定组合,mybatis-spring 2.0.4 是桥接包,负责把 MyBatis 的 SqlSessionFactory 交给 Spring 管理。JDK 用 1.8 最稳,用更高版本时 Spring 5.2 不一定兼容。新版配置时,把 mysql-connector-java 的版本换成 8.0.x 也能跑,但需要留意驱动类名从 com.mysql.jdbc.Driver 变成 com.mysql.cj.jdbc.Driver,这部分在配置里要同步改。

工程目录按三层架构分包,我常用这种做法:

src/main/java ├── controller // 页面跳转与接口入口 ├── service // 业务接口 ├── service.impl // 业务实现 ├── mapper // MyBatis Mapper 接口 ├── entity // 实体类 └── util // 公共工具(分页、日期等) src/main/resources ├── mapper // Mapper XML 文件 ├── spring // Spring 与 SpringMVC 配置 └── mybatis // MyBatis 全局配置

实体类按表结构写,字段名与表字段一致或开启驼峰映射。比如product表里有sale_num,实体类写成saleNum,在 MyBatis 配置里开启 mapUnderscoreToCamelCase 后会自动映射。这个分包结构可以画成架构图放进论文,比贴代码更直观。

2.3 配置文件的三个关键文件

SSM 的配置是整个项目里最容易翻车的地方。三个文件各管一摊:web.xml管容器启动和拦截规则,spring-mvc.xml管 Controller 扫描与视图解析,applicationContext.xml管 Service、数据源和事务。先看 web.xml 的核心部分:

<servlet> <servlet-name>springMvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springMvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>

DispatcherServlet 是 SpringMVC 的入口,load-on-startup为 1 表示容器启动时就初始化。url-pattern写成/会拦截所有请求,静态资源(图片、CSS、JS)也需要放行,一般另外配一个<mvc:resources>标签指向/static/目录,否则页面会光秃秃的。

spring-mvc.xml 里除了扫描 controller,还要配视图解析器:

<context:component-scan base-package="com.muying.controller"/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/"/> <property name="suffix" value=".jsp"/> </bean> <mvc:resources mapping="/static/**" location="/static/"/>

视图解析器把 Controller 返回的字符串作为逻辑视图名,拼上前缀后缀,找到 JSP。注意 JSP 放在 WEB-INF 下,用户不能直接通过 URL 访问,只能经 Controller 转发,这是很多项目必须遵守的安全习惯。

最后的 mybatis-config.xml 把数据库连接和 Mapper XML 位置写清楚:

<configuration> <properties resource="jdbc.properties"/> <settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <typeAliases> <package name="com.muying.entity"/> </typeAliases> </configuration>

typeAliases 让 XML 里写实体类型时不用带包名,直接写product即可。jdbc.properties 里是数据库 URL、用户名密码、驱动类名,数据源用 Spring 管理而不是写在 MyBatis 里,这样才能让事务注解生效。配置到这里,骨架基本跑得起来。

3. 母婴商品的数据模型:分类、SKU 与安全字段才是论文亮点

3.1 两级分类与前台商品位的设计

母婴网站的商品分类跟普通百货不太一样。普通电商往往是“服装 > 男装 > 上衣”这样按属性分;母婴商品更依赖使用场景——孕妇、0-6 月、6-12 月、1-3 岁、3-6 岁,加上喂养、洗护、出行、玩具等品类维度。如果只做单级分类,前台筛选会很难受;做多级分类,查询又容易写成深递归。我通常建议只用两级分类,再加一个“前台推荐位”字段来控制首页展示。

CREATE TABLE category ( id INT PRIMARY KEY AUTO_INCREMENT, parent_id INT DEFAULT 0 COMMENT '父分类ID,0为顶级', name VARCHAR(50) NOT NULL COMMENT '分类名称', sort_order INT DEFAULT 0 COMMENT '排序权重', is_display TINYINT DEFAULT 1 COMMENT '是否前台展示' );

parent_id 自关联实现两级分类。查询顶级分类时WHERE parent_id = 0,查子分类时WHERE parent_id = ?。这样做牺牲了无限层级,但换来的是查询简单,而且给论文留了一个可以展开说的点:为什么不做无限分级?因为母婴网站目前用两级足够,无限分级会带来递归查询和缓存失效问题。答辩时老师很吃这一套——你不是没考虑,而是基于业务做了取舍。

sort_order 控制首页分类的展示顺序,is_display 可以临时隐藏某个分类而不删数据。这样在后台管理里加一个“下架分类”的功能,也不难实现,还能增加一个后台模块作为论文的功能亮点。

3.2 商品表与 SKU 表:适用月龄、安全标注

母婴商品和普通商品最核心的区别,在于“适用月龄”和“安全信息”是购买决策的关键。论文里可以用一张业务表说明:商品基本信息 + 适用属性 + 印象标签。SQL 里商品主表加几个专门的字段:

CREATE TABLE product ( id INT PRIMARY KEY AUTO_INCREMENT, category_id INT NOT NULL COMMENT '所属分类', product_name VARCHAR(100) NOT NULL, subtitle VARCHAR(200) COMMENT '卖点副标题', main_image VARCHAR(255) COMMENT '主图URL', detail TEXT COMMENT '富文本详情', price DECIMAL(10,2) NOT NULL COMMENT '销售价', stock INT NOT NULL DEFAULT 0 COMMENT '总库存', sale_num INT DEFAULT 0 COMMENT '销量', fit_age VARCHAR(50) COMMENT '适用月龄,如0-12月', safety_level TINYINT DEFAULT 1 COMMENT '安全标注:1普通 2婴幼儿级', is_hot TINYINT DEFAULT 0 COMMENT '是否推荐位', status TINYINT DEFAULT 1 COMMENT '1上架 0下架', create_time DATETIME );

fit_age 用字符串存“0-12月”“1-3岁”,做筛选时用 LIKE 或区间解析。虽然这种设计不够范式化,但前台搜索更高效——数据库表少了不少关联。safety_level 是母婴网站特有的字段,可以在商品详情页渲染出“食品级”“婴幼儿级”等标识,后台也按等级做筛选,让论文里的“安全属性”不是空话。

商品如果做多规格(比如奶粉分 400g 和 800g),需要再拆一张 sku 表:商品 ID、规格名、价格、库存、规格图。主表的 stock 是冗余汇总字段,每次下单扣减 sku 库存后同步更新主表。没有多规格需求的话,sku 表可以先不加,避免过度设计。但论文里把“支持单规格/多规格”这个扩展点写一句,能体现你对表结构的考虑。

3.3 订单表和库存扣减的字段设计

母婴商城订单表和普通订单差别不大,但两个细节值得注意:价格快照和状态字段。

CREATE TABLE order_item ( id INT PRIMARY KEY AUTO_INCREMENT, order_id INT NOT NULL, product_id INT NOT NULL, product_name VARCHAR(100) COMMENT '商品名快照', price DECIMAL(10,2) COMMENT '下单时价格快照', quantity INT NOT NULL );

订单明细里必须冗余商品名和价格。否则后台改了商品标题或价格,历史订单里显示的内容也会跟着变,这在论文测试阶段很容易被发现。订单主表的状态我建议用status TINYINT而不是字符串:0 待付款、1 待发货、2 待收货、3 已完成、4 已取消。数据库存数字,页面展示时用字典翻译成中文文字。这样写的好处是索引效率高,论文里也可以画一张订单状态机图,把每个状态允许的流转写清楚。

库存扣减是另一个关键字段设计。如果直接在商品表里stock = stock - 1,高并发下会丢更新。常见做法是在扣减 SQL 里加条件:

UPDATE product SET stock = stock - 1 WHERE id = ? AND stock > 0;

影响行数为 0 表示库存不足,业务层据此返回“库存不够”。这个写法在论文里可以写成“基于乐观锁思想的库存扣减”,比先把库存查出来再判断要严谨得多。预售场景还可以单独加一个 pre_sale 字段区分现货和预售,但这就属于加分项了,核心表先把上面这些字段做扎实。

4. 落到代码:商品查询、购物车与下单扣库存的完整链路

4.1 商品分页查询:MyBatis 动态 SQL 两种写法

商品列表是母婴网站流量最大的页面,需要按分类筛选、按价格排序、按月龄过滤。MyBatis 里最直观的是在 Mapper XML 写动态 SQL:

<select id="selectProductPage" resultType="product"> SELECT * FROM product <where> <if test="categoryId != null"> AND category_id = #{categoryId} </if> <if test="fitAge != null and fitAge != ''"> AND fit_age LIKE CONCAT('%', #{fitAge}, '%') </if> <if test="status != null"> AND status = #{status} </if> </where> ORDER BY ${orderBy} LIMIT #{offset}, #{pageSize} </select>

<where>标签会自动去掉第一个多余的 AND,这是我最常用的写法,避免手动拼接 SQL 出错。#{categoryId}是预编译参数,防注入;${orderBy}是字符串替换,只允许传入写死的白名单值,比如price DESC、sale_num DESC,不能直接把用户输入拼进去。分页用的是 MySQL 的 LIMIT,offset 在 Service 层计算成(pageNum - 1) * pageSize。

另一个常见做法是引入 PageHelper 插件,在 Service 里写一行:

PageHelper.startPage(pageNum, pageSize); List<Product> list = productMapper.selectProductPage(condition); PageInfo<Product> pageInfo = new PageInfo<>(list);

PageHelper 的原理是拦截你的查询 SQL,自动在后面拼 LIMIT,并额外生成一条 count 查询。它适合简化代码,但要注意一个坑:startPage 只对接下来的一条查询生效,如果你在中间插了其他查询,分页就会串到别人身上。所以用 PageHelper 时,startPage 和 Mapper 调用之间不要穿插任何数据库操作。我在带学生时发现,不少人把这两行拆到不同方法里,导致分页时灵时不灵——这就是典型的“分页玄学”。

4.2 购物车:Session 方案与数据库方案的取舍

购物车有两种实现方式。Session 购物车把数据存在服务端,结构简单,但用户换设备或清缓存后购物车就没了;数据库购物车表与用户绑定,体验更好,但每次加购、改数量都要写库。毕业设计我建议做 Session 购物车,理由有三:省一张表、不用处理登录态过期、演示时只要浏览器不断开就能连续操作。

Session 购物车的实体可以是一个 HashMap:

public class CartItem { private Integer productId; private String productName; private BigDecimal price; private Integer quantity; private Integer stock; }

加购时从 Session 取出 Map,如果 productId 已存在就改数量,不存在就 new 一个放进去。数量还要校验不能超过 stock,否则下单时库存不够会尴尬。把购物车存 Session 的一个记忆点细节是:对象要实现 Serializable,否则 Tomcat 重启后 Session 序列化会报错。

数据库购物车方案也不是不能做,只是要多考虑合并逻辑——用户登录前在 Session 加了几个商品,登录后要不要迁移到数据库?这段代码放在哪?如果论文章节紧张,我建议别把复杂度耗在这个地方。先用 Session 把主流程打通,答辩被问到“为什么不用数据库”,你就说“Session 能减轻数据库压力,适合未登录用户先浏览后结算的业务场景”,再把数据库方案的优缺点作为扩展性分析写一段,这比硬做出一个不稳定的数据库购物车更稳妥。

4.3 下单事务:扣库存、生成订单、清购物车必须绑在一起

下单是最能体现事务能力的环节。扣库存成功后生成订单主表和明细表,然后清空购物车——这三步任何一个失败,都不能留一半数据。Spring 声明式事务用@Transactional就可以:

@Service public class OrderServiceImpl implements OrderService { @Autowired private ProductMapper productMapper; @Autowired private OrderMapper orderMapper; @Autowired private OrderItemMapper orderItemMapper; @Transactional(rollbackFor = Exception.class) public Order createOrder(Integer userId, Integer productId, Integer quantity) { // 1. 扣减库存,返回影响行数 int rows = productMapper.deductStock(productId, quantity); if (rows == 0) { throw new RuntimeException("库存不足"); } // 2. 查商品价格,生成订单与明细 Product product = productMapper.selectById(productId); Order order = new Order(); order.setUserId(userId); order.setStatus(0); order.setTotalPrice(product.getPrice().multiply(new BigDecimal(quantity))); orderMapper.insert(order); OrderItem item = new OrderItem(); item.setOrderId(order.getId()); item.setProductId(productId); item.setProductName(product.getProductName()); item.setPrice(product.getPrice()); item.setQuantity(quantity); orderItemMapper.insert(item); return order; } }

rollbackFor = Exception.class是必须写的一句。因为 Spring 默认只在运行时异常时回滚,如果你抛的是受检异常,事务不会自动回滚,库存扣了但订单没生成,数据就全乱了。deductStock对应上一章那条UPDATE ... SET stock = stock - 1 WHERE id = ? AND stock >= #{quantity},返回值是影响行数,0 就说明库存不足,直接抛异常让事务回滚。

事务配置有个常见翻车点:@Transactional 放在 Controller 层,或者放在 Service 接口实现类的同类方法互相调用。放在 Controller 的问题是事务范围太宽、拦截可能失效;同类方法互相调用问题在于 Spring 代理时,内部 this 调用不走代理,事务注解被忽略。正确做法是事务注解加在 ServiceImpl 的公开方法上,Controller 只调 Service 接口。这个方法值得在答辩时专门讲,因为很多人的项目事务注解形同虚设,你能讲清楚这个细节,会明显拔高印象分。

5. SSM 项目避坑实录:5 个让新手翻车的经典问题

5.1 现象:@Autowired 注入为 null,一启动就报 NullPointerException

原因:Spring 容器没有扫描到 Service 实现类。绝大多数情况是<context:component-scan>的 base-package 写错了,或者 ServiceImpl 上忘了加 @Service。还有一种情况是 Service 接口和实现类不在同一个被扫描的包路径下。

解决:统一把 base-package 写成项目的根包,比如com.muying,让 controller、service、mapper 全在扫描范围内。检查顺序是:先看类上有没有注解,再看扫描路径对不对,最后看 XML 里是否写了<context:annotation-config/>。@Autowired 报 null 几乎都是这三类原因,按顺序排查十分钟内能定位。

5.2 现象:启动时提示 Invalid bound statement (not found),Mapper 接口方法找不到

原因:Mapper 接口扫描到了,但 Mapper XML 文件没有加载,或者 XML 里的 namespace 写错。没有加载 XML 往往是 resources 下的 mapper 目录没被 Maven 打进 classpath,尤其当 XML 放在src/main/java里面时,Maven 默认只打包 .java 文件。

解决:XML 统一放src/main/resources/mapper,然后在 spring 配置里注册 MapperLocations:

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.muying.mapper"/> </bean>

再检查 XML 的 namespace 必须和 Mapper 接口全限定名一致,<select id="selectProductPage">的 id 必须和接口方法名一致。这个错误报错信息很明确,但新手容易在命名空间上纠结半天,记住一条规则:接口、XML namespace、XML id 三者对不上就报这个错。

5.3 现象:页面取不到后端数据,JSP 里 ${} 全部原样输出

原因:页面里没有引入 JSTL 标签库,或者 Servlet 3.0 环境下 JSP 默认禁用了 EL 表达式。还有一种情况是 Controller 返回的视图名对应 JSP 路径不准,数据存在 request 里,但页面被直接当成静态文件访问了。

解决:在 JSP 顶部加<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>,并把 jstl 依赖加进 pom。如果加了还是原样输出,检查 web.xml 的 web-app 版本声明——用 3.0 以上时要确认 isELIgnored 被设为 false。最后确认用户访问的路径确实经过 Controller,而不是直接访问了 WEB-INF 下的文件(WEB-INF 本就不该被直连)。

5.4 现象:Tomcat 启动半天后第一次请求特别慢,或者运行几小时后数据库连接报错

原因:MySQL 默认的 wait_timeout 是 8 小时,连接池里的连接长时间空闲会被数据库服务端断开,但连接池不知道,拿出来用时才发现连接已失效。Tomcat 启动慢通常是连接池初始化时没设置合理的初始连接数。

解决:给 Druid 或 C3P0 数据源配三个参数——initialSize 设 5,minIdle 设 5,maxActive 设 20,再加 testWhileIdle 和 validationQuery。如果用的 Druid,配validationQuery: SELECT 1即可,连接池会定期检测并剔除失效连接。这个坑不解决,答辩演示时翻车的概率极高,一定要提前压测一下长时间挂着再操作。

5.5 现象:上传的商品图片刷新后变成裂图,数据库里存的是相对路径却打不开

原因:图片被存到了本地磁盘目录,但浏览器以网站路径访问时找不到。比如上传文件写到D:/upload,页面却请求/upload/xxx.jpg,Web 服务器默认不会把磁盘目录映射成 URL。

解决:在 spring-mvc.xml 里加一个资源映射,把 URL 路径指到磁盘路径:

<mvc:resources mapping="/upload/**" location="file:D:/upload/"/>

同时注意 Windows 和 Linux 的路径分隔符区别,部署到服务器时建议用相对路径加绝对路径配置项。这个坑主要是开发环境和部署环境不一致导致的,把它写进论文“系统测试与部署”一节,也是一个很好的实战素材。

6. 答辩前夜:论文结构、PPT 演示与一次完整的查错演练

先讲论文怎么组织。不要照网上的模板一章一章抄,而是围绕“我是怎么把一个母婴业务问题用 SSM 解决的”来展开。我建议按五章写:绪论和研究现状、需求分析与技术选型、系统设计(架构图和数据库模型)、系统实现(带核心代码片段和截图)、系统测试。数据库设计那章放分类表、商品表、订单表的关系图,把 fit_age、safety_level 这两个字段标出来,解释为什么母婴商品需要这两类字段,这一下就有了业务味道。

PPT 控制在 10 页以内,演示顺序比美观重要。第一页放题目和架构图;第二页讲流程图,从用户注册、浏览商品、加购物车、下单到支付,用一张时序图讲完;第三页开始进入系统演示,按“前台浏览 → 搜索筛选 → 加购下单 → 后台管理”走一遍。演示时先做一遍完整的正常流程,再做一个小异常流程——比如故意把库存改小再下单,展示“库存不足”的提示,这会让评委觉得你考虑过边界情况。

答辩前必做的三个自查动作:第一,检查所有 JSP 页面的静态资源路径;第二,重新建一个数据库,用你的初始化 SQL 脚本把表全部建起来,验证脚本完整;第三,把项目从 GitHub 重新 clone 下来,按 README 从零启动一遍。我见过太多项目能跑是因为开发环境里什么都是现成的,换台电脑就起不来。这三步都能通过,项目才真正算“可交付”。

至于最后想叮嘱的,是把这次踩坑的细节记下来。我最初带项目时就吃过亏——写得快,但遇到 @Autowired 报 null 这种“玄学问题”后绕了很久,后来才发现是扫描包路径写错了。所以我把这些坑整理成了固定的检查清单,每个新项目第一周就按清单过一遍。这次母婴商城里你踩过的每一个坑,都不要急着删掉报错记录,留在论文附录里写一条“问题与解决”,比干巴巴的代码列表有用得多。希望帮到你。

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

返回列表