拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Java课程设计实战:车辆管理系统核心设计与SSM实现

Java课程设计实战:车辆管理系统核心设计与SSM实现 简介面向计算机相关专业毕业设计及Java Web实战学习者提供基于JSPServletMavenMySQL实现的车辆管理系统课程设计源码与项目说明。环境采用IDEA与MySQL数据库围绕车辆信息登记、状态跟踪、进出时间记录等核心模块展开适合理解传统Java Web分层开发流程。压缩包共70个文件包含12个JSP页面、4个Java类、24个XML配置及6个JAR依赖等类型包体大小5.67MB可按项目说明快速搭库并运行。已有381人学习下载。通过该项目可直接获得完整源码、建表SQL与数据样例、项目目录结构说明有助于梳理车辆管理业务逻辑、JSP/Servlet交互与Maven工程组织方式为毕业设计或课设答辩提供可扩展的参考基础。1. 用Java手写车辆管理系统课程设计为什么要选它很多人在选课程设计题目时第一反应是做个商城或者博客系统结果全班一半人交了相似的页面数据库表结构都大同小异。车辆管理系统这个题目的价值在于它表结构清晰、业务闭环完整从用户登录到车辆增删改查再到权限控制能把Java Web开发的核心知识点全部串起来又不至于像商城那样牵扯订单、支付、优惠券这些复杂状态。以我接触过的课程设计源码来看这套项目在Java课程设计案例源码里属于性价比很高的一类代码量在3000到5000行之间既能体现工作量又不会把自己写崩。这个题目适合的人群也很明确刚学完Servlet、JSP和MyBatis需要一份能完整讲清楚“请求怎么进来、数据怎么存、页面怎么渲染”的作品或者是数据库课设和Java课设一起交想用一份系统同时覆盖两个课设要求。车辆管理系统能解决的痛点很具体——它包含了几乎所有Web系统都要面对的用户认证、数据校验、列表分页、条件查询和权限区分做好这一套后续做其他管理系统基本是套模板的事。源码包里的项目说明文档通常还附带ER图和数据字典这对接下来的答辩准备帮助极大。2. 系统设计与三个核心难点技术栈选型、分层结构和数据表设计2.1 技术栈选型SSH还是SSM为什么建议用SSM传统Java Web课程设计里常见的技术栈有三套JSPServletJDBC的纯基础版、SSHStruts2SpringHibernate版、SSMSpringSpringMVCMyBatis版。从我看到的源码和学生的反馈来看课程设计阶段SSM是更稳妥的选择原因不在于SSH过时而是SSH的配置文件太多Struts2的拦截器机制和Hibernate的ORM映射对新手来说容易变成黑匣子一旦报错很难定位是配置问题还是代码问题。SSM组合里Spring负责对象管理和事务SpringMVC负责请求路由MyBatis负责SQL映射。三层各司其职报错时能通过堆栈信息快速缩小范围。比如请求到不了Controller先查SpringMVC的组件扫描配置SQL执行报错直接看Mapper XML里的SQL语句排查路径非常直观。如果你的课程设计题目要求必须用Servlet原生API那另说如果没有强制要求我建议直接用SSM搭一个Maven工程。以一个典型的车辆管理系统为例pom.xml中的核心依赖大致是这样的dependencies dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version5.3.30/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version5.3.30/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version3.5.13/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis-spring/artifactId version2.0.7/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version scoperuntime/scope /dependency /dependencies这段配置里需要注意两个版本对齐Spring 5.x对应JDK 8MyBatis 3.5.x和mybatis-spring 2.0.x的搭配是稳定的组合。很多同学在这里翻车是因为mybatis-spring用了1.x老版本和Spring 5的兼容性有坑导致启动时Bean创建异常。后端接口返回数据用的fastjson或Jackson建议用Jackson虽然多写几行配置但和Spring MVC的集成更自然。2.2 分层结构从Controller到Mapper每一层只做一件事车辆管理系统这种课设项目包结构的分层直接决定答辩时能不能讲清楚。标准的做法是四层结构controller、service、mapper、entity再加一个common包放统一返回结果和异常处理。以车辆管理为例controller层只接收参数、调用service、返回结果不做任何业务判断service层处理业务逻辑比如新增车辆时校验车牌号是否重复、删除车辆时检查是否存在未结清的罚款记录mapper层只负责和数据库交互一个方法对应一条SQL。RestController RequestMapping(/api/vehicle) public class VehicleController { Autowired private VehicleService vehicleService; GetMapping(/list) public Result list(RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 10) Integer pageSize, String keyword) { PageInfoVehicle page vehicleService.queryVehiclePage(pageNum, pageSize, keyword); return Result.success(page); } }这段代码是典型的Controller写法逻辑很薄。RequestParam里设置了默认值保证了前端不传分页参数时后端不会报错。这里有个容易忽略的点PageInfo是PageHelper分页插件返回的对象如果你没有引入PageHelper就得自己写LIMIT语句拼接工作量会大不少。我一般会在项目里引入PageHelper配置一个拦截器插件这样分页时的count查询和limit查询自动完成代码看起来干净很多。注意PageHelper的版本和MyBatis版本要匹配3.5.x的MyBatis配5.3.x的PageHelper是常见组合。Service层接口和实现分离也是课设加分项虽然项目不大但能体现你对接口编程的理解。实现类上标注Service事务注解Transactional加在需要保证原子性的方法上比如车辆入库时要同时更新车辆表和车位状态表两步操作必须在一个事务里。2.3 数据表设计车辆信息、驾驶员、用车记录三张核心表车辆管理系统的数据表设计是答辩时最容易被老师追问的点。很多初稿上来就是一张大表把所有字段塞进去看起来省事实际上违反了基本的范式设计。常见的做法是拆成用户表、车辆信息表、驾驶员表、用车记录表四张核心表。车辆信息表和驾驶员表是一对一关系一辆车对应一个固定驾驶员用车记录表是流水表记录谁在什么时间申请用了哪辆车、用途是什么、审批状态如何。CREATE TABLE vehicle_info ( id INT PRIMARY KEY AUTO_INCREMENT, plate_number VARCHAR(20) NOT NULL COMMENT 车牌号, brand VARCHAR(50) NOT NULL COMMENT 品牌型号, vehicle_type TINYINT NOT NULL COMMENT 车辆类型1-轿车 2-SUV 3-货车, purchase_date DATE COMMENT 购置日期, status TINYINT NOT NULL DEFAULT 1 COMMENT 状态1-可用 0-维修中, driver_id INT NOT NULL COMMENT 驾驶员ID, UNIQUE KEY uk_plate (plate_number) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT车辆信息表;车牌号字段加唯一索引非常关键这是车辆系统的业务核心约束。在Java课程设计的数据库评分环节主键、外键、唯一约束、非空约束都是送分点缺一个都会在答辩时被抓。vehicle_type用TINYINT存储而不是字符串是为了后续扩展方便加一种车型不需要改表结构只需要在代码里维护枚举映射。status字段默认值设为1这样新增车辆时如果不刻意指定状态默认就是可用状态减少数据不一致的概率。用车记录表里外键关联车辆ID和用户ID加上申请时间、开始时间、结束时间、用途、审批状态这几个字段。审批状态用一个枚举值表示0-待审批、1-已通过、2-已驳回。这里我在项目中看到不少学生把审批状态设计成字符串然后四处散落着“已通过”“通过”“审批通过”这类不一致的写法导致统计时数据对不上。用数字枚举配合application.yml里的映射说明是更稳的做法。MySQL存储引擎要用InnoDB字符集用utf8mb4前者支持事务后者避免中文乱码和生僻字存储问题。3. 从零跑通项目环境准备、数据库初始化与五个必调参数3.1 环境搭建的版本组合JDK、Maven、Tomcat怎么配对不翻车拿到课程设计源码包后第一个卡住人的地方往往是环境版本不匹配。车辆管理系统这类SSM项目最常见的推荐组合是JDK 1.8、Maven 3.6.x、Tomcat 8.5和MySQL 5.7——这几个版本是经过大量项目验证的稳定组合互相兼容没有坑。如果你用JDK 11或者17跑Spring 5.3的旧项目出现IllegalArgumentException之类的问题时先别急着改代码检查一下JDK版本更实际。Tomcat版本的选择也要留意Tomcat 9和Tomcat 10的Servlet API坐标不一样Tomcat 10把javax.servlet迁移成了jakarta.servlet如果项目代码用的是javax包名部署到Tomcat 10会直接启动失败。课程设计源码里绝大多数还是javax.servlet所以Tomcat 8.5是最省心的选择。这是一个不值得踩的坑却几乎每年都有人踩一遍。3.2 数据库初始化SQL脚本里的字符集和时区陷阱数据库初始化看似简单实际上有三处容易翻车。第一处是SQL脚本的字符集如果脚本文件本身是GBK编码而你用utf8mb4的库去执行中文注释和字段默认值会乱码。建议用IntelliJ IDEA直接打开SQL文件在右下角把文件编码切换成UTF-8后再执行。第二处是时区问题MySQL 8.0的默认时区是UTC如果连接串里不指定serverTimezoneJava后端查出来的时间会比本地时间早8小时。CREATE DATABASE vehicle_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE vehicle_system; SOURCE /path/to/vehicle_system.sql;在MySQL命令行执行时SOURCE后面的路径要写绝对路径相对路径有时会因为当前目录不对而报ERROR 1049 (42000): Unknown database。如果你用的是Navicat或DataGrip直接运行SQL文件即可但执行前一定要确认连接的库选对了否则脚本里的CREATE TABLE会跑到别的库里。第三处是初始化数据的完整性。课程设计源码里的SQL脚本通常带有几条测试数据方便程序启动后能看到列表效果。不要手贱把这些数据删掉答辩演示时需要这些数据来展示分页和查询功能。如果脚本里没有测试数据手动插入几条车辆信息和用户记录密码用MD5或BCrypt加密后的值不然登录不进来。3.3 配置文件参数四个必调项和两个选调项SSM项目的核心配置集中在jdbc.properties或application.yml里。车辆管理系统跑不起来八成是这里没配对。jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/vehicle_system?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.passwordroot驱动类这一项最常见的问题是com.mysql.jdbc.Driver和com.mysql.cj.jdbc.Driver混用。MySQL 5.7及之前版本用前者MySQL 8.0必须用后者。如果你的MySQL是5.7但驱动jar是8.x版本前者也能跑但会有一条弃用警告建议直接统一成com.mysql.cj.jdbc.Driver。连接串里useSSLfalse一定要加否则启动时会有SSL握手警告在某些Windows环境下还会拖慢连接速度。serverTimezoneAsia/Shanghai解决上面说的时间差问题。第二个必调项是MyBatis的Mapper XML扫描路径property namemapperLocations valueclasspath:mapper/*.xml/这个路径要和实际的文件目录对应。Maven项目的标准结构里XML放在src/main/resources/mapper下如果你的项目里XML在src/main/java下的某个包中要么把XML移到resources目录要么修改build里的resources配置否则启动日志会出现Invalid bound statement (not found)。第三个必调项是SpringMVC的静态资源放行规则第四个是Tomcat的端口号改端口是为了避免8080被占用时启动失败。选调项里一个是分页插件PageHelper的reasonable参数设为true后越界页码自动归位另一个是日志级别开发阶段建议设为DEBUG能看到SQL执行日志排查时省力很多。3.4 部署到Tomcatwar包还是IDEA直接跑启动项目的常用方式有两种用IDEA配置Tomcat运行或者直接打war包丢到Tomcat的webapps目录。课程设计阶段我建议直接在IDEA里配置Tomcat运行因为这样能断点调试打war包的方式适合最后做演示备份时用。配置时注意Deployment里Application context要写成/vehicle不要留空根路径否则访问URL没有前缀多个项目部署时容易混淆。mvn clean package -DskipTests打war包的命令很简单但要注意项目里pom.xml的packaging标签必须是war而不是jar。有些同学用Spring Boot的套路建了项目打成jar后在Tomcat里怎么也部署不成功就是这个原因。如果打包后war包只有几KB说明某个依赖没下载完整删除本地Maven仓库里的相关目录重新构建即可这是新手最常见的java课程设计案例源码打包失败场景之一。4. 三条核心链路改造从登录鉴权到车辆增删改查4.1 登录链路Session还是JWT课程设计怎么选课程设计场景下登录功能的技术选型有两种主流做法SessionCookie的经典方式或者JWT Token的方式。我推荐在车辆管理系统里用Session方式理由有两个一是代码简单使用HttpSession保存登录状态不需要额外引入拦截器处理JWT解析二是答辩时老师问起来容易解释会话机制是课程基础内容能讲明白比追求新潮更重要。PostMapping(/login) public Result login(String username, String password, HttpSession session) { User user userService.login(username, password); if (user null) { return Result.error(用户名或密码错误); } session.setAttribute(loginUser, user); return Result.success(user); }这段代码里的userService.login返回值设计需要注意登录失败返回null登录成功返回完整用户对象。很多课设项目在这里返回布尔值会导致页面拿不到用户信息还得再查一遍数据库。把用户对象直接放进Session后续所有需要当前用户信息的接口从Session里取就可以了。密码校验的逻辑通常在Service层做从数据库查出来用户后把用户输入的密码用MD5加密再比对。MD5虽然不算特别安全的算法但课程设计阶段完全够用回答老师“为什么不用BCrypt”时可以答“生产环境建议用BCrypt课程设计为了演示原理选择MD5更直观”。退出登录时调用session.invalidate()清空会话再重定向到登录页。这里有个细节如果项目里有多个角色管理员、普通用户登录成功后要根据角色跳转不同首页这个判断可以放在前端JS里完成也可以在Controller里返回不同的视图名。4.2 车辆增删改查校验逻辑放在哪里最不容易漏车辆信息的增删改查是系统的核心功能也是最容易在答辩时被追问业务细节的地方。增删改查不是简单地写四条SQL真正的难点在于业务校验。新增车辆时车牌号不能为空、不能重复、格式要符合规则删除车辆前要检查这辆车是否有未归还的用车记录修改车辆信息时车牌号如果变了要处理历史记录里的关联数据。Service public class VehicleServiceImpl implements VehicleService { Override public void addVehicle(Vehicle vehicle) { if (StringUtils.isEmpty(vehicle.getPlateNumber())) { throw new BusinessException(车牌号不能为空); } if (!vehicle.getPlateNumber().matches(^[\\u4e00-\\u9fa5][A-Z][A-Z0-9]{5}$)) { throw new BusinessException(车牌号格式不正确); } int count vehicleMapper.countByPlateNumber(vehicle.getPlateNumber()); if (count 0) { throw new BusinessException(该车牌号已存在); } vehicleMapper.insert(vehicle); } }这段代码体现了三个原则非空校验提前返回、格式校验用正则、唯一性校验查数据库。BusinessException是自定义运行时异常配合全局异常处理器统一返回给前端避免把500错误暴露给用户。写正则匹配车牌号时这个表达式覆盖了常见的“京A12345”格式但纯电车的“京AD12345”是6位字符这个正则就不适用了可以根据自己项目需求调整。课程设计阶段只要讲清楚格式校验的意义和正则的基本语法就行。删除车辆时的业务校验一般在Service层做用Transactional保证事务。如果直接删除车辆但保留用车记录会导致关联数据的逻辑错乱常见的做法是逻辑删除给vehicle_info表加一个is_deleted字段删除操作变成UPDATE更新该字段。这比物理删除好在两点一是数据可恢复答辩演示时翻车了有后悔药二是保留历史记录用车统计报表的准确性不受影响。4.3 权限控制拦截器实现管理员和普通用户的路由区分车辆管理系统的权限模型相对简单通常只有管理员和普通用户两种角色所以不需要引入Spring Security这类重量级框架自己写一个拦截器就够了。拦截器在SpringMVC里的配置分两步第一步是实现HandlerInterceptor接口第二步在Spring配置里注册并设置拦截规则。public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session request.getSession(); User loginUser (User) session.getAttribute(loginUser); if (loginUser null) { response.sendRedirect(request.getContextPath() /login); return false; } return true; } }注意/login这个请求本身不能被拦截否则会变成死循环。在SpringMVC配置里要设置excludePathPatterns把登录接口、静态资源和验证码接口放行。这里还涉及一个小坑放行的路径的写法是/login还是/login/**。如果登录请求是POST /api/user/login而你的拦截路径写的是/api/**那要把/api/user/login也加进放行列表否则永远登录不进去。管理员角色的接口比如用户管理、车辆配置管理需要单独校验角色。可以在拦截器里取Session中的用户对象判断角色类型也可以做一个AdminInterceptor专门拦截/admin/**路径。后者更清晰毕竟用两个拦截器各干各的比一个拦截器里处理所有逻辑要容易理解。答辩时讲到权限这块能画出拦截器的流程图基本上就能证明你理解了这个设计。4.4 使用MyBatis的XML映射复杂查询怎么避免扫描全表车辆管理系统里最常用的复杂查询是车辆列表的条件筛选加关键字搜索加分页。用MyBatis的XML映射来写动态SQL可以避免在Java代码里拼接SQL字符串的糟糕体验。这里直接使用实体类Vehicle为例来演示。select idqueryByCondition resultTypecom.example.entity.Vehicle SELECT id, plate_number, brand, vehicle_type, status, purchase_date FROM vehicle_info where if testkeyword ! null and keyword ! AND (plate_number LIKE CONCAT(%, #{keyword}, %) OR brand LIKE CONCAT(%, #{keyword}, %)) /if if teststatus ! null AND status #{status} /if AND is_deleted 0 /where ORDER BY create_time DESC LIMIT #{offset}, #{pageSize} /selectwhere标签自动处理WHERE关键字和多余的AND这是MyBatis动态SQL里最实用的一个标签。LIKE CONCAT(%, #{keyword}, %)避免SQL注入风险不要直接拼字符串。手写分页时Java代码里计算offset是必须的(pageNum - 1) * pageSize如果忘记这个计算会出现数据错位和重复显示。这个查询里把is_deleted 0放在where里保证所有列表查询自动过滤已删除数据防止遗漏。写XML映射文件时一共要写几个基础SQLinsert、selectByPrimaryKey、updateByPrimaryKeySelective、deleteByPrimaryKey逻辑删除是update、countByCondition。最后一个countByCondition是给分页统计用的MyBatis的select会返回Integer类型注意resultType不要写成int尽量用包装类型避免空指针。5. 车辆管理系统避坑指南踩过的五个坑和解决办法5.1 中文乱码问题接口返回JSON和页面渲染两套解法现象后端返回的中文在页面上显示为???或者一堆乱码。原因第一层是HTTP响应没有设置UTF-8编码第二层是数据库连接串没加characterEncodingutf8第三层是页面本身的charset没设对。解决按顺序排查——先看响应头Content-Type是否包含charsetUTF-8SpringMVC里配置StringHttpMessageConverter时设置UTF-8再改jdbc.properties的URL加上characterEncodingutf8最后确认JSP或HTML头部有meta charsetutf-8。三个位置都改对后乱码基本消失。还有一个容易漏的是Tomcat的server.xml里Connector配置URIEncodingUTF-8GET请求的参数编码由这一项决定。5.2 登录后刷新页面就退出Session过期还是Cookie没存现象登录成功后跳转到首页按F5刷新一下马上跳回登录页。原因在前后端分离或跨域场景下前端没有保存SessionID对应的Cookie或者Cookie的过期时间太短浏览器关闭就清理了。解决检查前端Ajax请求是否设置了withCredentials: true如果用的是JWT方式后端要配置CORS的allowCredentials(true)如果是在同一个Tomcat下的JSP页面检查web.xml里的session-config超时时间默认是30分钟开发调试时可以设置在60分钟。最直接的一招在Chrome的Application标签页里查看Cookie确认JSESSIONID是否存在。5.3 MyBatis报Invalid bound statementMapper接口和XML对不上现象启动项目时报Invalid bound statement (not found)但Mapper接口里的方法明明存在。原因XML文件没有被扫描到或者XML里的namespace写的不是Mapper接口的完整类名再或者方法ID和接口方法名不一致。解决先看namespace必须和Mapper接口全限定名一致再看mapperLocations的路径配置确认XML文件确实在对应目录下最后看方法IDselect标签的id属性要和接口方法名完全一致这个中差一个字母都找不到。还有一个特殊场景项目里用了MyBatis的注解方式写了SQL又在XML里配了同名方法会冲突日常维护时不要混用。5.4 MySQL 8.0连接失败Public Key Retrieval错误现象数据库配置一切正常但启动时报Public Key Retrieval is not allowed。原因MySQL 8.0默认使用sha256_password或caching_sha2_password认证插件客户端第一次连接时需要获取服务器的公钥来加密密码这个行为默认被禁止。解决在JDBC连接串后追加allowPublicKeyRetrievaltrue。前面提到连接串里的useSSLfalse这两个参数最好同时存在不要删一个留一个否则要么报SSL警告要么报公钥错误。这是SSM项目使用MySQL 8.0时的一个高频经典问题记录下来以后直接用。5.5 IDEA运行时报404项目结构不对还是访问路径不对现象Tomcat启动了控制台没报错但访问首页时404。原因多半是Artifacts配置有问题比如缺失lib目录里的jar包或者Application context和实际URL不匹配。解决在IDEA里右键项目Open Module Settings看Artifacts的Output Layout里有没有WEB-INF/lib下的依赖如果没有把library的jar包拖进去然后确认URL前缀和Application context一致。如果更新了新依赖后404频繁发生选择Build菜单里的Rebuild Project重新构建一次清掉旧的编译产物和部署文件再试。6. 答辩前必做的三件事数据验证、环境瘦身和现场演示备份第一件事是数据验证。把系统里的核心流程完整走一遍管理员登录、新增一辆车、修改车辆信息、条件查询、删除车辆、普通用户登录后看不到管理菜单。每一步都记录下预期结果和实际结果。特别要验证分页功能的边界场景——第1页的数据、最后一页数据少于每页条数时的展示、搜索关键词为空时的回退逻辑。这些是百试百灵的答辩追问点也是自己最容易忽略的位置。mysql -u root -p -e USE vehicle_system; SELECT COUNT(*) FROM vehicle_info; SELECT COUNT(*) FROM vehicle_usage_record;这条命令能快速确认数据库里测试数据是否齐全避免答辩时打开页面发现列表是空的然后现场陷入尴尬。发票过后如果老师问“系统如何保证数据的一致性”可以结合事务和唯一索引来回答这些都是代码里已有的实现直接引用即可。第二件事是环境瘦身。课程设计演示通常用的是一台实验室电脑或自己的笔记本提前把数据库服务手动启动起来确认服务端口没有被防火墙拦截。演示前可以手动清理掉后台上一次运行留下的Tomcat进程避免端口冲突导致IDEA里的项目运行不起来。准备一张纸质的“运行清单”写清楚启动MySQL、IDEA、Tomcat的几步操作万一现场环境出问题靠这份清单能快速复位。第三件事是准备扩展方向。给车辆管理系统加一个简单的报表功能统计每周的车辆使用频率按车型统计用车占比。实现上可以利用上一章里提到countByCondition的思路写一个selectUsageReport查询按日期分组计数前端用表格展示结果。答辩时主动说“这个系统还可以扩展出车辆养护预警模块通过行驶里程和保养里程的差值自动生成提醒”会比被动等老师提问显得更有想法。这门课设的价值不在于代码量有多大而在于把Java Web开发的主干线完整走了一遍。如果你在实现了车辆管理系统的增删改查之外还能主动加了权限控制、逻辑删除这些设计已经比大多数同期的课设作品扎实了。我自己的经验是这类系统最核心的竞争力在数据库设计和业务闭环的完整性不需要炫技把细节做好就是把课设做到了位。希望这篇笔记帮到你少走一些老路。本文还有配套的精品资源点击获取
返回列表