简介:面向Java毕业设计或课程设计场景,这是一套基于SSM框架与Vue前后端分离的广告管理系统项目。系统按业务角色划分为管理员、广告客户、广告商、广告制作商四大端,覆盖广告投放渠道管理、广告需求与提案流转、设计预览与调整、签署管理及结算等完整业务流程,适合作为计算机类毕业设计选题参考或项目学习模板。资源包共540个文件,压缩后约83.42MB,包含Java后端源码、Vue前端页面、SQL数据库脚本、XML与yml配置,以及svg、jpg、png等UI素材,并附带环境安装与启动的bat脚本和项目工程配置文件,便于本地还原与二次开发。另含演示视频,可快速理解系统运行效果。资源已有150人浏览学习,对需要快速搭建广告管理类项目或借鉴SSM+Vue整合思路的开发者具有一定参考价值。
1. 基于SSM框架的广告管理系统:一个能直接答辩的毕业设计到底长什么样
不少人解压这类资源包后的第一反应是盯着文件列表发呆:十几个文件夹、一个 .sql 脚本,不知道从哪看起。基于SSM框架的广告管理系统,是典型的完整毕业设计项目——广告主、广告位、广告订单、审核、权限、曝光与点击统计,功能齐全到能答辩,代码量又适合逐行读懂。
它适合两类人:一类是要交毕业设计或课程设计、想找可靠骨架做二次开发的学生;另一类是刚学完JavaWeb、想搞明白Spring、SpringMVC、MyBatis三层到底怎么串起来的新手。下面按从解压到跑通、再到答辩改造的路径,把架构、配置、核心流程和坑一次讲透。
2. 为什么毕业设计选 SSM 而不是 Spring Boot:技术栈拆解与选型逻辑
2.1 Spring、SpringMVC、MyBatis 在广告系统里各自管什么
先解决最基础的问题:SSM 到底是哪三样,它们在广告系统里各干各的活。很多人把这三个框架当成三座孤岛去学,其实在项目里它们是一条流水线上的三个岗位,边界非常清楚。
Spring 管对象。广告服务类、用户服务类、数据源、事务管理器,这些对象的创建和依赖注入都由 Spring 的 IoC 容器接管。你在源码里基本找不到 new AdService() 这种写法,常见的是在 Controller 里写一个 @Autowired 字段,把 Service 依赖拿进来。好处是业务代码不关心对象怎么创建,以后换实现类只改配置或注解,不动调用方,这就是控制反转带来的直接效果。
SpringMVC 管请求。浏览器发出的 HTTP 请求先到 DispatcherServlet,它根据 URL 匹配到某个 Controller 方法,Controller 调 Service,Service 调 Mapper,最后通过视图解析器把结果渲染成 JSP 页面。这个系统里你在浏览器看到的登录页、广告位列表、新增表单、审核按钮的每一次响应,都是这一层在分发和处理。
MyBatis 管数据库。它负责把 Mapper 接口和 XML 里的 SQL 绑定起来。比如“查询某个广告位下面所有状态正常的广告”这种 SQL,写在 XML 里,MyBatis 把结果集自动映射成 Java 对象;反过来,Java 对象的属性也能通过 #{...} 写到 SQL 里。复杂查询都集中在这里,这也是后面做统计报表时最能发挥的地方。
把三者串成一条链路看:JSP 表单提交 → SpringMVC 的 Controller 接收 → Spring 管理的 Service 做业务判断 → MyBatis 的 Mapper 执行 SQL → MySQL 落库并返回结果。三个框架各管一段,谁也不越界。答辩时被问到“SSM 怎么协作”,把这条链路讲清楚,比背十遍概念都管用。
2.2 三层结构与典型包划分:一眼看穿这套代码的骨架
导入项目之后,第一件事不是读代码,而是看 src/main 下的包结构。常见划分是 controller、service、mapper、entity 四个包,加上 resources 下的 mapper XML 和 Spring 配置文件。这套划分就是标准的三层架构:表现层、业务层、持久层。
| 包/目录 | 职责 | 在广告系统里的典型类 | | controller | 接收请求、做参数校验、决定返回哪个视图 | AdController、UserController | | service | 业务规则、事务控制、状态流转 | AdService、OrderService | | mapper/dao | 定义数据库操作方法 | AdMapper、OrderMapper | | entity/pojo | 对应数据库表的 Java 对象 | Advertisement、AdPosition、User | | resources/mapper | 存实际的 SQL 语句 | AdMapper.xml、UserMapper.xml | | resources/*.xml | Spring、MyBatis 的配置 | applicationContext.xml、spring-mvc.xml |
这个表看着简单,但它是整个项目的索引。一旦你能把任意一个类快速放进表格对应的格子里,读代码的速度会明显提升。比如看到 AdController,第一反应就应该是“表现层,只负责接收和转发,不写业务逻辑”;看到 XML 里的 SQL,就明白持久层的活在这个文件里。
至于为什么这类毕设选 SSM 而不是更时髦的 Spring Boot,我说句实话:不是 SSM 比 Boot 好,而是教程和资源包里沉淀下来的旧项目大量是 SSM。SSM 的配置是显式的,每个 Bean 注册、每个映射、每个拦截器都写在 XML 里明明白白,特别适合答辩现场讲“原理”;Spring Boot 把配置自动化了,反而容易让新手面对“DispatcherServlet 从哪来的”这类问题答不上来。如果你的目标是快速出活,Boot 更省事;如果目标是弄懂 JavaWeb 的底层协作方式,这套项目的学习密度更高。
提示:判断一个项目是 SSM 还是 SSH(Struts2 + Hibernate),不用看标题,直接翻 pom.xml 里有没有 mybatis 相关依赖、resources 下有没有大量 *Mapper.xml 就够了。
2.3 拿到手先读哪几个文件:理解一套陌生代码的阅读顺序
陌生项目最怕从头到尾读,两小时过去还停在第一个类。我一直在用的做法是按“入口到链路”的顺序读,具体五步:pom.xml 看依赖版本和项目年代;web.xml 看总入口,DispatcherServlet 映射路径、字符编码过滤器、Spring 配置文件加载位置都在这;spring-mvc.xml 看组件扫描范围、视图解析器前缀后缀、静态资源放行规则;jdbc.properties 看数据库连接,这是跑通前必改的文件;最后选一条业务主链读代码,比如广告审核,从 Controller 进 Service 再进 Mapper 的 XML,把一条链路读通。
这个顺序比按文件夹从上往下刷快得多。尤其是答辩前临时拿到一个陌生项目,“先看入口再顺链路”基本是标准动作。读完一条链路后,你会发现其他模块只是换了业务名词,骨架完全一致,剩下的都是体力活。
3. 从 .rar 到第一个页面:环境配置与导入运行全流程
3.1 JDK、Tomcat、MySQL 版本怎么定
这类项目绝大多数按 JDK 1.8 编写,运行时环境也按 1.8 配最稳。有人图新鲜装了 JDK 17 甚至 21,老项目里如果用了 CGLIB 代理或旧版 Tomcat,可能蹦出一堆看似莫名其妙、实则版本不兼容的报错。所以我的习惯是用一条经验值表格直接锁死:
| 组件 | 常见搭配 | 关键注意点 | | JDK | 1.8 | 兼容性最好,答辩环境普遍可用 | | Tomcat | 8.5 或 9.0 | 不要上 10 以上,包名已改成 jakarta,老代码会编译不过 | | MySQL | 5.7 或 8.0 | 8.0 的连接串必须带时区和公钥参数 | | IDE | 近年稳定版即可 | 安装时勾选 Maven 支持 |
这个组合能覆盖市面上九成 SSM 毕业设计。如果打开 pom.xml 发现 Spring 版本还在 3.x 时代,连 JDK 1.7 写法都有,不用慌,把 spring 相关依赖统一升到 4.3 或 5.x 通常不用改代码,主要是命名空间版本号不同。
参数说明放在连接串这里:MySQL 8.0 最典型的问题是驱动加载和时区。连接串写 jdbc:mysql://localhost:3306/ad_system?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true。useSSL=false 是关闭安全套接字减少握手报错,serverTimezone 是 8.0 必须指定的时区,allowPublicKeyRetrieval=true 专门解决 8.0 的 RSA 公钥获取失败问题。这三项缺哪个,启动阶段都会给出让人摸不着头脑的报错。
3.2 导入 IDE 与数据库脚本执行
第一步是解压。把 .rar 解压到一个不含中文和空格的路径,比如 D:\workspace\ad-system。这一步看着没技术含量,但确实是很多人的第一个坑:路径带中文时,Tomcat 有时候能启动,页面上的静态资源和文件上传功能却会诡异报错,排查半天才发现是路径编码问题。
解压后确认是不是 Maven 项目。最常见的做法是 IDE 里 File -> Open 直接选 pom.xml,让 IDE 以 Maven 项目方式导入,然后等依赖下载。如果你习惯用命令行验证依赖,可以先跑两个命令:
# 在项目根目录执行,依次验证 Maven 环境和依赖解析 mvn -v mvn clean compile -DskipTests=truemvn -v 确认本机 Maven 版本和 Java 版本;mvn clean compile 清理 target 目录后做一次编译验证。这一步通过,说明依赖基本完整;如果报红或失败,优先检查 Maven 仓库镜像是否配置,以及网络是否把某些 jar 卡在半路。
数据库脚本一般叫 ad_system.sql 或 ad_manage.sql 之类,包含建库、建表和初始数据。在终端里用一行命令导入:
# 把脚本文件导入 MySQL,root 密码按实际修改 mysql -uroot -p < ad_system.sql导入前先看一眼脚本头部有没有 CREATE DATABASE 语句。如果有,要确认本机不存在同名库;如果没有,就先手动建库再导入。我更常用的方式是进入 MySQL 后执行 SOURCE 命令,因为 SOURCE 的编码行为比部分图形客户端更可控,不容易出现中文乱码:
-- 手动建库并导入脚本 CREATE DATABASE IF NOT EXISTS ad_system DEFAULT CHARACTER SET utf8mb4; USE ad_system; SOURCE D:/workspace/ad-system/ad_system.sql;注意这里的库名要和后面 jdbc.properties 里 jdbc.url 的路径保持一致。这一步的失误会在启动时以“Unknown database”报错的形式告诉你,属于能定位但很浪费时间的低级错误。
3.3 改 jdbc.properties 并启动 Tomcat:第一次跑起来的完整动作
数据库恢复完成后,找到 src/main/resources 下的 jdbc.properties,这是系统连数据库的钥匙。典型内容如下:
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/ad_system?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true jdbc.username=root jdbc.password=123456实际要改的只有两处:jdbc.url 里的库名 ad_system 和 jdbc.password。如果你用的 MySQL 是 8.x,驱动类建议换成 com.mysql.cj.jdbc.Driver,这是 MySQL 8 的新驱动全名,老驱动虽然能用但控制台会一直打 deprecated 警告。jdbc.username 不要硬套 root,以你本机 MySQL 的实际账号为准。
改完配置就可以启动 Tomcat。IDE 里新增 Tomcat Server,Deployment 中把项目以 war exploded 方式部署,Application context 填 /ad,这样访问路径就是 http://localhost:8080/ad。需要注意两点:一是 Server 页的 JRE 下拉框要选 1.8,否则用默认高版本 JDK 编译会有兼容风险;二是本机 8080 端口若被其他服务占用,就把 HTTP port 改成 8081,别浪费时间找幽灵进程。
注意:启动过程如果报 “Error during artifact deployment”,通常是 Tomcat 版本和项目编译级别不匹配,或部署配置里没带上 Maven 依赖。这类问题九成出在配置而不是代码。
3.4 启动成功后的验证清单:别急着高兴
看到 Tomcat 日志输出“Server startup in xxx ms”只说明进程起来了,不代表系统可用。建议按下面这张清单逐项走一遍:
| 验证项 | 操作 | 预期结果 | | 登录页 | 访问 http://localhost:8080/ad/login | 正常渲染,验证码可显示 | | 登录 | 输入初始账号 | 跳转主页,无 500 报错 | | 广告位列表 | 打开对应菜单 | 表格出现初始数据 | | 新增一条广告位 | 提交表单 | 刷新后出现新记录且中文不乱码 | | 越权访问 | 不登录直接访问管理页 | 被拦截器重定向回登录页 |
初始账号在哪看?一般在数据库脚本的插入语句里,或者项目文档里。如果你拿到手没有文档,就去 SQL 脚本里搜 INSERT INTO t_user 或 sys_user,密码通常以 MD5 形式存储,md5("123456") 这类值是常见默认。这一套走通,系统才算真正落到你手里。
4. 广告管理系统的核心流程是怎么串起来的
4.1 三张主表怎么设计:广告位置、广告、点击日志的关系
先看数据库核心表。广告系统最核心的数据模型是“广告位-广告-点击日志”三条线。广告位是售卖的资源,比如首页轮播图、侧边栏横幅,定义好尺寸和价格;广告是广告主投放到某个位上的具体内容;点击日志是每次曝光点击的行为记录,用来做统计。
-- 广告位表:定义可投放的资源 CREATE TABLE ad_position ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL COMMENT '广告位名称', width INT COMMENT '宽度px', height INT COMMENT '高度px', price DECIMAL(10,2) COMMENT '每日投放价格', status TINYINT DEFAULT 1 COMMENT '状态:1启用 0停用' ); -- 广告表:广告主提交的投放内容 CREATE TABLE ad ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(200) NOT NULL COMMENT '广告标题', position_id INT COMMENT '投放的广告位', advertiser_id INT COMMENT '归属广告主', start_date DATE, end_date DATE, status TINYINT DEFAULT 0 COMMENT '0待审核 1通过 2驳回 3下线' );这段 DDL 里有两个值得注意的设计细节:所有关联都用 xxx_id 的整型外键持有,而不是直接存名称字符串,目的是保持数据一致性和后续联表查询的能力;状态字段全用 TINYINT 数字,不用字符串,因为字符串状态下“待审核”和“已下线”很容易因拼写不一致产生脏数据。
对应的实体类通常长这样:
public class Ad { private Integer id; private String title; private Integer positionId; private Integer advertiserId; private Date startDate; private Date endDate; private Integer status; // 用于联表后直接承载关联对象,避免二次查询 private AdPosition position; }这个实体类里的 AdPosition 字段是联表查询的关键。当 Mapper 的 XML 用了 join 查询时,resultMap 可以把结果映射进这个嵌套对象,前端展示时直接 ad.position.name 就能拿到广告位名称,不用再单独查一次。
4.2 广告审核的状态流转:一个能被写进论文的细节
广告主提交广告后不能直接上线,要经过管理员审核。这个流程看起来只是改一个状态值,但实现时有个容易翻车的细节:并发重复审核。
两个管理员同时打开同一条待审核广告,一个点了通过,另一个点了驳回。如果没有约束,后提交的人会覆盖先提交的结果,最终状态和操作日志对不上。解决方式是在 UPDATE 语句里带上当前状态作为条件:
// 审核广告:只有状态等于 fromStatus 时才允许流转到 toStatus public boolean audit(Integer adId, Integer fromStatus, Integer toStatus) { int rows = adMapper.updateStatus(adId, fromStatus, toStatus); return rows == 1; }<update id="updateStatus"> UPDATE ad SET status = #{toStatus} WHERE id = #{adId} AND status = #{fromStatus} </update>这里的原理是“乐观锁”:先查出来看到的状态是待审核,执行更新时再确认一遍还是待审核才动手。数据库层面 update 会锁行,第二个事务进来时匹配不到原来的状态,影响行数为 0,于是 audit 返回 false,业务层就可以提示“该广告状态已变化,请刷新后重试”。这是很小的一个细节,但在答辩现场很容易成为加分点,因为很多学生写的审核就是直接 set status = 2,压根不管并发。
业务层还要区分角色:广告主只能提交和查看自己的广告,管理员才能审批。这在 SSM 项目里通常通过拦截器或过滤器判断 session 里的角色字段,后面第 6 章会落到具体代码。
4.3 统计曝光与点击:MyBatis 聚合查询的两种写法
广告系统的交付指标是数据:某个广告位曝光多少、被点击多少次、点击率是多少。统计功能在 SSM 里一般用 MyBatis 的聚合查询实现,写法上有两个典型场景。
场景一是按广告位统计在投广告数量,用 GROUP BY + COUNT:
<select id="countByPosition" resultType="map"> SELECT p.name AS positionName, COUNT(a.id) AS adCount FROM ad_position p LEFT JOIN ad a ON a.position_id = p.id AND a.status = 1 GROUP BY p.id, p.name </select>这里有两个容易踩的点:一是 JOIN 条件里那个 a.status = 1,如果把它写到 WHERE 里,LEFT JOIN 会退化成 INNER JOIN,统计出的无广告位数量会不准;二是 resultType 用 map,让 MyBatis 直接把查询结果转成 List<Map<String, Object>>,适合报表这种字段不固定的场景。
场景二是按天统计点击趋势,输出折线图数据:
SELECT DATE(click_time) AS day, COUNT(*) AS clicks FROM ad_click_log WHERE ad_id = #{adId} AND click_time >= #{start} GROUP BY DATE(click_time) ORDER BY day这段 SQL 的逻辑是:从点击日志里按天分组,统计某条广告在给定时间范围内的每日点击量。使用 DATE() 函数把 datetime 截断到天,再 GROUP BY 这个函数结果。性能上的注意点是 click_time 列必须建索引,否则广告点击量大时,这个查询会在日志表上全表扫描,页面报表会明显卡顿。
统计结果从 Service 层返回后,一般封装成 List
5. 避坑:SSM 项目导入后最常见的 5 个翻车现场
这一章要把跑这套项目时遇过、也帮人排查过的几类高频问题列出来,每条按现象、原因、解决的顺序写。这些问题集中在导入和启动阶段,占掉了毕设求助里的大部分提问,提前记住能省下好几个通宵。
5.1 一启动就 ClassNotFoundException / NoClassDefFoundError
现象:Tomcat 启动日志抛 ClassNotFoundException,或者页面跑到一半抛 NoClassDefFoundError,关键类看起来明明在项目里,编译也不报错。
原因:绝大多数是 Maven 依赖没有完整下载,或 IDE 的 Artifacts 配置里没有把模块依赖打包进部署目录。后者更隐蔽,因为 IDE 编码时能找到类,但运行环境里没有这个 jar,属于“编译通过、运行失败”的典型代表。
解决:先用 mvn clean compile 确认依赖本身有没有问题。如果编译通过但运行还报,检查 Project Structure -> Artifacts,在输出目录的 lib 里看有没有项目自己的依赖 jar;没有的话,在 Artifacts 配置里把 module 依赖手动添加进去。再不行就把整个 target 删除后 Rebuild Project,让构建工具重新拷贝 lib 目录。
5.2 登录页能打开,提交后却 404
现象:首页和登录页渲染正常,点击登录按钮后地址栏跳转,但页面 404。
原因:两种常见情况。一是 web.xml 里 DispatcherServlet 的 url-pattern 配置成 /*,把本该交给容器处理的 JSP 请求也拦截了,导致视图解析不到;二是表单的 action 路径没写项目上下文路径,比如写成 /login,而实际部署上下文是 /ad,路径一错自然 404。
解决:打开浏览器开发者工具看 Network 里的实际请求 URL,判断是 404 还是 405,这一步比瞎猜快得多。然后回头查 web.xml 的 url-pattern,SSM 项目最常见的两种写法是.do 或 /,极少用 /;再核对表单 action 是否包含上下文路径。改完后重启 Tomcat,清浏览器缓存再看结果。
5.3 MySQL 8 连不上,报 Public Key Retrieval is not allowed
现象:项目启动时数据库连接池报错,提示 Public Key Retrieval is not allowed,日志末尾常跟着 caching_sha2_password 的字样,看起来像是密码错误,但密码确实是对的。
原因:MySQL 8 默认的认证插件是 caching_sha2_password,首次连接需要从服务端获取 RSA 公钥做加密握手,而驱动默认关闭了这个获取动作,于是连接被拒。
解决:在 jdbc.url 连接串里追加 allowPublicKeyRetrieval=true&useSSL=false,问题立刻消失。根本上,也可以把 MySQL 用户认证插件改回 mysql_native_password,但不推荐改库,改连接串更通用——换一台电脑部署时不用动数据库。改完记得重启 Tomcat,连接池只在启动时初始化一次,改了配置不重启等于白改。
5.4 新增数据后,页面上中文全变成问号
现象:从初始脚本带进去的中文正常,但自己新增的中文数据刷新后显示成 ??,或者反过来初始数据乱码、新增正常。
原因:乱码是编码链路断了一环。常见断点有三个:MySQL 数据库或表是 latin1 字符集;jdbc.url 没带 characterEncoding=utf8;JSP 页面头部缺少 charset=UTF-8 的声明。只要有一环不对,中文就保不住。
解决:按从易到难的顺序查。先看 JSP 头部有没有 <%@ page contentType="text/html;charset=UTF-8" %>,再看 jdbc.url 是否带 characterEncoding=utf8,最后进 MySQL 执行 SHOW CREATE TABLE 确认表字符集是 utf8mb4。已经产生 ?? 的旧数据建议直接清掉重新导脚本,别指望能转换回来。三层一起保证,前后端中文就不会再出幺蛾子。
5.5 点击登录没反应,控制台报 Invalid bound statement (not found)
现象:启动不报错,登录按钮点了没反应或直接白屏,后台日志出现类似 Invalid bound statement (not found): xxxMapper.login 的提示。
原因:MyBatis 把 Mapper 接口和 XML 绑定时,要求接口的全限定名与 XML 的 namespace 完全一致,方法名与 XML 里 statement 的 id 一致,任何一个对不上,运行时就找不到绑定。另一条常见原因是 XML 文件没有被构建工具复制到 classes 目录,因为默认构建配置只会拷贝 resources 下已声明的文件类型。
解决:打开报错里提示的 Mapper XML,检查 namespace 是否等于接口全限定名,再检查 resources 目录下 XML 的位置是否与接口包路径对应。文件都在但还报错,就在 pom.xml 的 build 节点里加一段 resource 配置,把 src/main/java 下的 **/*.xml 一起打进输出目录,然后 Rebuild Project。这个配置在 SSM 项目里属于“不加必翻车”的经典项。
6. 进阶:毕业设计想加分,给这套系统加两个改造点
6.1 统计页接上 ECharts:从聚合查询到可视化图表
论文里展示度最高的功能通常是图表。常见做法是后端加一个返回 JSON 的接口,前端用 ECharts 渲染。改造量不大,收益直观:Controller 方法上加 @ResponseBody,返回 List
@ResponseBody @RequestMapping("/stat/trend") public List<Map<String, Object>> trend(Integer adId, String start) { return adMapper.selectClickTrend(adId, start); }前端引入 echarts.min.js,用 ajax 拿数据填充图表:
$.get("/ad/stat/trend", {adId: 1}, function (rows) { var chart = echarts.init(document.getElementById("trendChart")); chart.setOption({ xAxis: {type: "category", data: rows.map(r => r.day)}, yAxis: {type: "value"}, series: [{type: "line", data: rows.map(r => r.clicks)}] }); });这个改造的完整链路是 Controller 返回 JSON、前端异步渲染,论文里能顺势写一节“前后端数据交互设计”。注意 ajax 路径要和部署上下文一致,改完用开发者工具看 Network 里 JSON 是否真的返回。
6.2 加一个操作日志拦截器:小改动体现系统意识
答辩老师常问“系统有没有权限控制、有没有操作留痕”。与其口头解释,不如加一个 HandlerInterceptor,在请求进 Controller 前记一笔:
public class OperLogInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) { User user = (User) req.getSession().getAttribute("loginUser"); String log = (user != null ? user.getUserName() : "anonymous") + " " + req.getMethod() + " " + req.getRequestURI(); logService.write(log); return true; } }在 spring-mvc.xml 里注册,只拦截管理路径。这个点能引出两类答辩话术:操作留痕便于追溯,以及拦截器与过滤器的区别,后者几乎是必考题。
我刚接触这类改造时栽过的坑是 ajax 请求被拦截器挡掉拿不到 JSON,最后把拦截范围缩小到 /admin/** 就解决了。我也养成了一个习惯:答辩前把数据库脚本删掉重跑一遍,模拟评审现场从零部署,顺带验证文档步骤没漏。这套系统顺着前面的路径走通后,离这一步就不远了,希望帮到你。
本文还有配套的精品资源,点击获取