SSM超市管理系统源码解析:从分层架构到本地部署与业务修改

发布时间:2026/11/5 13:20:10

SSM超市管理系统源码解析:从分层架构到本地部署与业务修改 简介这份资源是面向高校计算机专业学生与Java初学者的一套SSM框架课程设计完整项目围绕超市管理场景帮助读者完成从框架整合到业务模块落地的实战练习适合作为毕业设计或课程作业的参考方案。压缩包共792个文件约1.78MB以452个html页面、48个java源文件、48个class编译文件、41个js脚本、28个xml配置及12个jsp视图为主另含css样式、图片素材与sql建库脚本覆盖前端页面、后端控制器与数据库脚本的完整链路。已有86人学习下载。项目按商品管理、库存管理、销售管理、会员管理与统计报表等模块组织后端基于Spring依赖注入与事务管理、Spring MVC处理请求、MyBatis完成SQL映射前端采用JSP与HTML、CSS、JavaScript构建界面读者可据此理解分层架构、掌握控制器与实体类设计并借鉴事务一致性与数据库交互的排错思路。1. 课程设计选 SSM 做超市管理系统这套源码到底能帮你省下多少时间每年到了课程设计季后台被问得最多的一类问题就是有没有一套能跑起来、结构清楚、还能改得动的 SSM 项目超市管理系统算是被选得最多的题目之一原因很实在——业务模块够多商品、库存、订单、供应商、会员但每个模块的逻辑又不算复杂正好能把 Spring MVC 的请求分发、MyBatis 的映射配置、事务控制这几块串起来练一遍。这套基于 SSM 的超市管理系统源码走的就是这条路线后端用 Spring Spring MVC MyBatis 三层结构前端是常规的 JSP/HTML 页面配合少量 JavaScript数据库用 MySQL整体是一个标准的 Maven Web 工程。它解决的不是“高并发秒杀”那种炫技需求而是让你在有限时间里拿到一个分层清晰、能本地跑通、能对着改的骨架。适合谁正在做课程设计、需要一套可复现 SSM 范例的在校开发者以及想回头补一补传统三层架构基本功的初级后端。下面我按“先看结构、再跑起来、然后改一处业务、最后排坑”的顺序拆。2. 拆开工程看分层Spring MVC 请求链路与 MyBatis 映射是怎么接上的拿到一套 SSM 源码最忌讳上来就点运行。先花二十分钟把目录和配置读一遍后面能少踩一半的坑。这套工程的结构是典型的 Maven 标准布局src/main/java下按controller / service / service.impl / mapper / entity分包src/main/resources放 MyBatis 映射文件和数据库配置src/main/webapp放 JSP 页面和静态资源。理解它的关键是把“一个浏览器请求进来之后到底经过了哪些层”这条链路在脑子里跑通。2.1 从 web.xml 到 DispatcherServlet请求入口在哪Spring MVC 的核心是前端控制器DispatcherServlet它由web.xml里的 Servlet 配置接管。打开web.xml你会看到两段关键配置一段是ContextLoaderListener负责加载 Spring 的根容器Service、Mapper、数据源这些另一段是DispatcherServlet负责加载 Spring MVC 的子容器Controller、视图解析器这些。两个容器是父子关系子容器能拿到父容器的 Bean反过来不行——这一点决定了你后面把 Service 注入 Controller 没问题但别指望在 Service 里注入 Controller。!-- web.xml 关键片段两个容器各管一摊 -- context-param param-namecontextConfigLocation/param-name param-valueclasspath:applicationContext.xml/param-value /context-param listener listener-classorg.springframework.web.context.ContextLoaderListener/listener-class /listener servlet servlet-namedispatcher/servlet-name servlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class init-param param-namecontextConfigLocation/param-name param-valueclasspath:spring-mvc.xml/param-value /init-param load-on-startup1/load-on-startup /servlet servlet-mapping servlet-namedispatcher/servlet-name url-pattern//url-pattern /servlet-mapping这段配置的含义要拆开看contextConfigLocation指向applicationContext.xml里面通常配数据源、SqlSessionFactory、事务管理器和组件扫描扫描service包DispatcherServlet的init-param指向spring-mvc.xml里面配注解驱动、视图解析器和 Controller 扫描。url-pattern写成/而不是/*是为了让静态资源css、js、图片不被 DispatcherServlet 全部拦截这是新手最容易忽略的一个细节。load-on-startup设为 1 表示容器启动时就初始化 Servlet而不是等第一个请求来了才初始化调试阶段建议保留能更早暴露配置错误。2.2 Controller → Service → Mapper一条商品查询的完整链路以“商品列表查询”为例链路是这样的浏览器请求/goods/listDispatcherServlet根据RequestMapping找到对应的 Controller 方法Controller 调用注入的 ServiceService 调用 Mapper 接口Mapper 接口通过 MyBatis 的映射文件执行 SQL结果逐层返回最后 Controller 把数据放进Model视图解析器拼出 JSP 路径渲染页面。Controller RequestMapping(/goods) public class GoodsController { Autowired private GoodsService goodsService; RequestMapping(/list) public String list(Model model) { // Service 返回商品集合塞进 Model 供 JSP 遍历 ListGoods goodsList goodsService.findAll(); model.addAttribute(goodsList, goodsList); return goods/list; // 视图解析器会拼成 /WEB-INF/jsp/goods/list.jsp } }这里有两个参数点值得注意。第一Autowired注入的是接口GoodsService实际实现类在service.impl包里靠 Spring 的组件扫描和接口多态完成装配所以实现类上必须有Service注解否则启动就报找不到 Bean。第二返回值goods/list是逻辑视图名真正的物理路径由spring-mvc.xml里的InternalResourceViewResolver决定常见配置是前缀/WEB-INF/jsp/、后缀.jsp。如果你改了 JSP 的存放位置记得同步改这里不然就是 404。2.3 MyBatis 映射文件namespace 与 resultMap 的对应关系MyBatis 这一层新手翻车最多的是namespace和接口对不上。映射文件里的namespace必须写 Mapper 接口的全限定名select的id必须和方法名一致否则启动时报Invalid bound statement。这套工程的映射文件放在resources/mapper下applicationContext.xml里通过mapperLocations通配扫描。!-- GoodsMapper.xml 片段 -- mapper namespacecom.demo.mapper.GoodsMapper resultMap idgoodsMap typecom.demo.entity.Goods id columngoods_id propertygoodsId/ result columngoods_name propertygoodsName/ result columnstock propertystock/ /resultMap select idfindAll resultMapgoodsMap SELECT goods_id, goods_name, stock FROM goods ORDER BY goods_id DESC /select /mapperresultMap的作用是解决数据库列名下划线风格和 Java 属性名驼峰风格不一致的问题。如果你图省事直接写resultType那要么在applicationContext.xml里开启mapUnderscoreToCamelCase要么就得保证列名和属性名完全一致否则查出来的字段全是 null——这个现象很迷惑SQL 明明有数据页面上就是不显示血泪经验是先在控制台打印一下返回对象。另外ORDER BY这类排序条件如果来自前端参数别直接字符串拼接用#{}占位或者做白名单校验这是后面避坑章节要展开的点。3. 本地跑通这套系统环境、建库与启动的完整步骤结构看明白了接下来是把它跑起来。这一步的目标很明确浏览器能打开登录页能用默认账号进到商品列表。整个过程分环境准备、建库导数据、改配置、启动验证四步任何一步跳过都会在启动时报错所以按顺序来。3.1 环境版本对齐JDK、Tomcat、MySQL 怎么选这套工程是传统 SSM不是 Spring Boot所以没有内嵌容器需要外置 Tomcat。版本上我一般这样配JDK 8工程里的pom.xml如果写了source和target为 1.8就别用 JDK 17 硬上编译能过但运行期反射和 JSP 编译容易出问题、Tomcat 8.5 或 9.0Tomcat 10 把javax.*换成了jakarta.*老工程直接部署会大面积报 ClassNotFound、MySQL 5.7 或 8.08.0 要注意驱动类名和时区参数。Maven 用 3.6 以上即可。组件推荐版本说明JDK1.8与 pom 编译级别保持一致Tomcat8.5 / 9.0避免 jakarta 命名空间问题MySQL5.7 / 8.08.0 需改驱动与 URL 参数Maven3.6用于依赖拉取和打包版本对齐这件事看着琐碎但它决定了你后面是“调业务”还是“调环境”。我见过太多人卡在启动阶段最后发现是 Tomcat 10 部署了javax工程白折腾一下午。3.2 建库与导入 SQL字符集和自增主键的两个细节工程一般会附带一个sql目录或者根目录下的.sql文件。导入之前先建库字符集用utf8mb4排序规则utf8mb4_general_ci这样中文商品名不会乱码。CREATE DATABASE supermarket DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE supermarket; -- 再执行工程自带的建表与初始化数据脚本 SOURCE /path/to/supermarket.sql;导入后做两件事验证一是SHOW TABLES;看表数量是否和预期一致商品、分类、库存、订单、用户这几张核心表要在二是随便SELECT * FROM goods LIMIT 5;看有没有初始数据。如果表建好了但数据是空的登录页大概率进不去因为账号密码存在用户表里。另一个细节是自增主键的起始值如果脚本里写了AUTO_INCREMENT1000之类新增商品时 ID 会从 1000 开始这不影响功能但如果你在代码里硬编码了 ID 判断就会出问题——所以别硬编码。3.3 改数据源与连接参数MySQL 8 的驱动和时区坑数据源配置在applicationContext.xml或独立的jdbc.properties里。MySQL 5.7 和 8.0 的配置差异集中在驱动类名和 URL 参数上这是必须改对的地方。# MySQL 5.7 写法 jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/supermarket?useUnicodetruecharacterEncodingutf8 jdbc.usernameroot jdbc.password你的密码 # MySQL 8.0 写法驱动类名变了URL 要加时区和 SSL 参数 jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/supermarket?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue参数逐个说清楚serverTimezone不配MySQL 8 驱动会报时区无法识别表现为启动时连接池初始化失败useSSLfalse是本地开发关掉 SSL 握手避免证书告警allowPublicKeyRetrievaltrue是 8.0 默认认证插件caching_sha2_password在非 SSL 连接下需要的开关不加上会报Public Key Retrieval is not allowed。密码别用明文提交到版本库本地开发图省事可以但养成放jdbc.properties并加进.gitignore的习惯。3.4 部署到 Tomcat 并验证从登录页到商品列表配置改完用 Maven 打包成 war丢进 Tomcat 的webapps目录启动bin/startup.batWindows或startup.shLinux。访问http://localhost:8080/工程名/应该能看到登录页。用 SQL 脚本里的初始账号登录进到后台首页点商品管理列表能出来说明 Controller、Service、Mapper、数据库这条链路全通了。# 打包并部署的典型命令 mvn clean package -DskipTests cp target/*.war $TOMCAT_HOME/webapps/ $TOMCAT_HOME/bin/startup.sh # 看日志确认无异常 tail -f $TOMCAT_HOME/logs/catalina.out验证顺序建议是先看 Tomcat 启动日志有没有SEVERE再看应用日志有没有 Spring 容器初始化异常最后才是页面。如果页面 404先确认访问路径里的工程名和 war 包名一致如果页面 500看catalina.out里的堆栈八成是某个 Bean 没注入或者 SQL 映射找不到。这一步跑通后面改业务才有意义。4. 改一处真实业务给商品模块加一个按名称模糊查询跑通之后最有价值的练习不是继续点页面而是动手改一个功能验证自己真的理解了分层。选“商品按名称模糊查询”最合适它同时涉及前端传参、Controller 接收、Service 组装条件、Mapper 写动态 SQL 四个环节改完这一处整套架构你就摸透了。4.1 前端传参与 Controller 接收RequestParam 的取舍在商品列表页加一个搜索框表单提交到/goods/search参数名keyword。Controller 里用RequestParam接收注意设required false否则不传参数会直接 400。RequestMapping(/search) public String search(RequestParam(value keyword, required false) String keyword, Model model) { // 关键字为空时退化为全量查询避免拼出 LIKE %% 之外的空条件 ListGoods goodsList goodsService.searchByName(keyword); model.addAttribute(goodsList, goodsList); model.addAttribute(keyword, keyword); // 回填搜索框 return goods/list; }required false是关键参数它允许搜索框为空时也能进方法方法内部再做空值判断。model.addAttribute(keyword, keyword)是为了页面回显用户体验上的小细节但课程设计答辩时这种细节很加分。别用RequestBody接表单参数那是给 JSON 请求体用的普通表单提交用RequestParam或直接方法参数绑定。4.2 Service 与 Mapper 动态 SQL用if和CONCAT防注入Service 层做空值兜底Mapper 层用 MyBatis 的动态 SQL 标签拼条件。这里必须用#{}而不是${}前者是预编译占位符能防 SQL 注入后者是字符串直接拼接是安全漏洞。select idsearchByName resultMapgoodsMap SELECT goods_id, goods_name, stock FROM goods where if testkeyword ! null and keyword ! goods_name LIKE CONCAT(%, #{keyword}, %) /if /where ORDER BY goods_id DESC /selectwhere标签会自动处理AND前缀问题条件为空时不会拼出多余的WHERE。CONCAT(%, #{keyword}, %)是标准写法#{}会被编译成?占位符参数由 JDBC 安全绑定。如果你写成LIKE %${keyword}%用户输入 OR 11就能把整张表拖出来这是课程设计里最容易被答辩老师追问的安全点。改完这一处重新打包部署输入“可乐”能筛出对应商品功能就闭环了。5. 这套源码最容易翻车的地方五条排查记录跑通和改通之后剩下的时间基本都花在排错上。下面五条是我拆这类 SSM 工程时反复遇到的按“现象 → 原因 → 解决”记下来你遇到时能少走弯路。5.1 启动报 Invalid bound statementnamespace 或 id 对不上现象是应用启动或首次查询时抛org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)。原因通常是映射文件的namespace没写 Mapper 接口全限定名或者select的id和方法名不一致也可能是mapperLocations没扫描到该文件。解决先核对namespace与接口包路径逐字符一致再核对方法名最后确认applicationContext.xml里mapperLocations的通配路径常见classpath:mapper/*.xml能覆盖到文件所在目录。改完 clean 一次再打包避免旧 class 残留。5.2 页面字段全是 null列名与属性名没映射上现象是 SQL 在数据库客户端能查出数据但页面上商品名、库存全是空白。原因是resultType直接映射时数据库下划线列名和 Java 驼峰属性名对不上。解决要么改用resultMap显式写column与property的对应要么在 MyBatis 配置里开启mapUnderscoreToCamelCasetrue。排查技巧是在 Service 返回后打一行日志把对象打印出来确认是查询层问题还是 JSP 取值问题别一上来就怀疑前端。5.3 中文商品名乱码三处字符集要一起改现象是新增商品后列表显示问号或乱码。原因可能出在数据库、连接 URL、JSP 页面三处任意一处。解决数据库和表用utf8mb4JDBC URL 带useUnicodetruecharacterEncodingutf8JSP 页面顶部声明% page contentTypetext/html;charsetUTF-8 %。三处缺一不可只改一处往往还是乱码这是典型的“改对了但没全对”。5.4 事务不生效注解加错位置或没开事务管理现象是下单扣库存时扣减成功但订单插入失败库存却没回滚。原因是Transactional加在了 Controller 上或者applicationContext.xml没配DataSourceTransactionManager和tx:annotation-driven/。解决事务注解加在 Service 实现类的public方法上确认事务管理器 Bean 存在且注解驱动已开启。另外注意同类内部方法自调用不会触发代理事务同样失效这是 Spring AOP 的经典边界。5.5 静态资源 404DispatcherServlet 拦截了 css 和 js现象是页面能打开但样式全丢控制台一堆 404。原因是web.xml里url-pattern写成了/*把所有请求都交给了 DispatcherServlet。解决改成/并在spring-mvc.xml里配mvc:default-servlet-handler/或mvc:resources放行静态资源目录。这个坑在课程设计里极常见因为很多人是从网上抄的web.xml片段没注意这一个字符的差别。6. 从能跑到能讲用日志和断点把 SSM 链路验证成自己的东西课程设计的终点不是“页面能点”而是答辩时被问到“这个请求怎么走到数据库的”你能张口就答。我的习惯是挑一条链路用日志和断点把它完整走一遍把每一层的输入输出都看清楚。具体做法在 Controller 方法入口打一行log.info打印请求参数在 Service 实现类打一行打印业务处理前后的对象在 Mapper 对应的 SQL 上开 MyBatis 的 SQL 日志applicationContext.xml里配logImpl为STDOUT_LOGGING或接 logback这样一次请求下来参数、SQL、返回结果全在控制台排成一条线。!-- 开启 MyBatis 控制台 SQL 日志调试期用上线关掉 -- bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property namedataSource refdataSource/ property namemapperLocations valueclasspath:mapper/*.xml/ property nameconfiguration bean classorg.apache.ibatis.session.Configuration property namelogImpl valueorg.apache.ibatis.logging.stdout.StdOutImpl/ /bean /property /bean配上之后控制台会打印Preparing: SELECT ...和Parameters: 可乐(String)你能直观看到#{}被替换成?再绑定参数的过程这比看十篇原理文章都管用。断点则建议打在 Service 实现类的第一行用 IDE 的 Debug 模式启动 Tomcat单步跟到 Mapper 调用观察代理对象是怎么把接口方法翻译成 SQL 的。这套验证方法的价值在于它把“黑匣子”变成了“透明管道”以后换任何一套 SSM 工程你都能用同样的方式快速定位问题在哪一层。再补一个进阶技巧把logImpl换成 logback 并配置独立的 MyBatis 日志级别可以按包名过滤只打自己项目的 SQL避免被框架日志淹没。另外调试完记得把STDOUT_LOGGING关掉生产环境打全量 SQL 既影响性能又可能泄露敏感字段。从那以后我每次接手一套陌生的 SSM 工程都强制先开一次 SQL 日志、走一遍登录到列表的链路确认分层和映射都对得上再动业务代码——这个习惯帮我省掉了大量“改了不知道哪层出问题”的时间。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/11/5 13:20:10

SSM超市管理系统实战:从课设跑通到二次开发全解析

简介:这份资源是面向高校计算机相关专业学生与Java入门开发者的SSM课程设计参考项目,围绕超市管理场景,解决商品、库存、销售等业务模块从设计到落地的实践需求,适合作为课程作业、毕业设计原型或SSM框架练手案例。压缩包共792个文…

2026/11/5 13:15:07

PIPE4.3.0安装排错:JDK版本、JAVA_HOME与Petri验证

简介:PIPE 4.3.0 是一款基于 Petri 网的图形化建模与仿真软件,面向软件工程、系统分析与教学实验场景,帮助读者完成并发、同步与竞争等系统行为的建模、动态仿真及性质分析。压缩包共 2391 个文件,约 28.43MB,包含大量…

2026/11/5 13:15:07

用栈实现队列:双栈结构与工程实践

1. 栈与队列的相爱相杀第一次看到这个题目时,我正坐在星巴克啃着牛角包。用栈实现队列?这就像用筷子喝汤——不是不行,但总觉得哪里怪怪的。栈和队列这对数据结构里的欢喜冤家,一个后进先出(LIFO)&#xff…

2026/11/5 13:20:10

Dubbo服务下线实时告警:基于ZooKeeper Watch的轻量级监控方案

简介:这是一套面向Java分布式系统运维与开发人员的Dubbo服务高可用监控解决方案,聚焦于Provider异常下线的实时感知与告警响应。资源通过Zookeeper监听机制实现服务状态变更捕获,结合邮件通知模块,在不侵入原有Dubbo代码的前提下&…

2026/11/5 13:20:10

SSM超市管理系统源码解析:从分层架构到本地部署与业务修改

简介:这份资源是面向高校计算机专业学生与Java初学者的一套SSM框架课程设计完整项目,围绕超市管理场景,帮助读者完成从框架整合到业务模块落地的实战练习,适合作为毕业设计或课程作业的参考方案。压缩包共792个文件,约…

2026/11/5 13:20:10

SSM超市管理系统实战:从课设跑通到二次开发全解析

简介:这份资源是面向高校计算机相关专业学生与Java入门开发者的SSM课程设计参考项目,围绕超市管理场景,解决商品、库存、销售等业务模块从设计到落地的实践需求,适合作为课程作业、毕业设计原型或SSM框架练手案例。压缩包共792个文…

2026/11/5 13:15:07

PIPE4.3.0安装排错:JDK版本、JAVA_HOME与Petri验证

简介:PIPE 4.3.0 是一款基于 Petri 网的图形化建模与仿真软件,面向软件工程、系统分析与教学实验场景,帮助读者完成并发、同步与竞争等系统行为的建模、动态仿真及性质分析。压缩包共 2391 个文件,约 28.43MB,包含大量…

2026/11/5 13:15:07

用栈实现队列:双栈结构与工程实践

1. 栈与队列的相爱相杀第一次看到这个题目时,我正坐在星巴克啃着牛角包。用栈实现队列?这就像用筷子喝汤——不是不行,但总觉得哪里怪怪的。栈和队列这对数据结构里的欢喜冤家,一个后进先出(LIFO)&#xff…

2026/11/5 13:10:06

锂电池PACK生产数字孪生平台架构与实现

1. 锂电池PACK生产数字孪生平台建设背景 在新能源行业快速发展的当下,锂电池PACK生产面临着前所未有的质量管控挑战。传统生产模式下,工艺参数分散在各个设备中,质量数据追溯困难,设备之间缺乏有效协同,这些问题直接影…

2026/11/4 12:38:23

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

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

2026/11/5 0:02:07

多智能体协作系统架构设计与实践优化

1. 多智能体协作系统的时代背景2017年Transformer架构的诞生彻底改变了人工智能的发展轨迹。当行业还沉浸在单一大模型带来的性能突破时,Anthropic的研究团队已经将目光投向了更前沿的领域——如何让多个智能体通过协作产生超越单体能力的系统智能。这就像交响乐团中…

2026/11/5 0:02:07

大模型系统性入门实战指南:从原理到部署微调的全链路路线

提到大模型的系统性入门,我先说个结论:这事真不难,难的是资料太散、路线太多,今天这个框架明天那个工具,新人很容易在“收藏夹吃灰”和“教程看到一半放弃”之间反复横跳。市面上关于大模型的内容少说有几十万篇&#…

2026/11/5 0:02:07

鸿蒙文件系统沙箱机制解析与开发实践

1. 鸿蒙文件系统沙箱机制深度解析作为一名从Android转向鸿蒙开发的工程师,我深刻体会到文件系统访问是迁移过程中最先遇到的"拦路虎"。在Android上运行良好的文件读写代码,在鸿蒙真机环境却频频出现文件写入后神秘消失或直接抛出权限拒绝错误的…

2026/11/4 12:38:23

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

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

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑