SSM整合框架的使用

SSM的整合为:springMVC控制页面跳转、spring控制bean的注入、mybatis控制数据库。通过搭建这三种框架从而形成SSM框架,框架的整合搭建可以更加方便开发人员和系统的便捷移动和高可维护性等好处。本文介绍从0搭建SSM框架并完成简单增删改查功能操作数据库的过程。

目录

一:需要配置的文件

二:增删改查操作

三:综合知识点

 

一:需要配置的文件

SSM框架的使用,是基于springMVC、spring+mybatis的基础上进行整合,说白了就是在spring+mybatis的基础上加入了springMVC,是他们两个整合之后运行在MVC架构中。相关配置文件则相应的就包含了spring、springMVC、mybatis的配置文件。现在开始从0搭建SSM的运行环境。

1. springMVC的配置:

该配置主要提供了请求和响应的配置,浏览器客户端发出请求,服务器拦截后交给spring来处理,然后交给对应的controller来完成处理,返回完成响应。首先在web.xml中设置springMVC的拦截servlet用来拦截需要过滤的请求,然后创建springMVC.xml配置文件告诉服务器拦截之后如何处理等信息,文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>SSM</display-name>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  
  <!-- 配置springMVC -->
  <servlet>
  	<servlet-name>spring</servlet-name>  
  	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  	<init-param>
  		<param-name>contextConfigLocation</param-name>
  		<param-value>classpath:springMVC.xml</param-value>   //设置springMVC配置文件的路径
  	</init-param>
  </servlet>
  
  <servlet-mapping>
  	<servlet-name>spring</servlet-name>
  	<url-pattern>*.do</url-pattern>    //拦截所有以.do结尾的请求
  </servlet-mapping>
</web-app>

src下springMVC.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
     
	<context:annotation-config/>
	
	<!-- 自动扫描controller包 -->
	<context:component-scan base-package="com.controller">
		<context:include-filter type="annotation" 
			expression="org.springframework.stereotype.Controller"/>	
	</context:component-scan>
	
	<!-- 开启注解驱动,使访问路径和注解内容匹配 -->
	 <mvc:annotation-driven >
       <mvc:message-converters register-defaults="true">
          <bean class="org.springframework.http.converter.StringHttpMessageConverter">
             <property name="supportedMediaTypes" value="text/plain;charset=UTF-8" />
          </bean>
       </mvc:message-converters>   
    </mvc:annotation-driven>
	
	<!-- 访问静态资源 -->
	<mvc:default-servlet-handler/>
        
        <!-- 视图定位 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    	<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
    	<property name="prefix" value="/jsp/hero/" />
    	<property name="suffix" value=".jsp" />
    </bean>            
</beans>

2. 配置spring,在web.xml中设置一个监听器,它会随着tomcat的启动进行扫描项目下的jar包从而加载spring配置文件。创建spring的配置文件applicationContext.xml文件,用来注入mybatis的配置信息和注册各种业务所需要的bean。这里按照SM整合的方式配置spring,配置一个映射器的整合配置方式。文件如下:

web.xml中添加监听器:

 <context-param>
  	<param-name>contextConfigLocation</param-name>
  	<param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  <listener>
  	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

src下创建applicationContext文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
     http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
     
     <context:annotation-config />        //开启自动扫描功能
     
     <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
     	<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
     	<property name="url" value="jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8" />
     	<property name="username" value="root" />
     	<property name="password" value="root" />
     </bean>
     
     <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="configLocation" value="classpath:mybatis-config.xml" />            
     </bean>
     
     <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
     	<property name="basePackage" value="com.dao" />
     </bean>     
     <import resource="spring/spring-*.xml"/>    //扫描spring下的所有spring子配置文件
     
</beans>

3. src下配置mybatis的配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
	<typeAliases>
		<package name="com.pojo"/>
	</typeAliases>

	<mappers>
		<mapper resource="mybatis/Hero.xml"/>
	</mappers>
</configuration>

4. 项目结构:

本例的项目结构为:pojo层、mapper层(dao接口层)、sql-xxx.xml(dao实现层)、service接口层、serviceImpl层、controller层(采用注解方式跳转controller)。

 

二:增删改查操作

基于SSM框架下,完成对数据库的操作。请求流程为:

客户端发起请求--web.xml拦截请求给springMVC--springMVC根据配置文件交给相关控制器处理--spring中注册service这个bean,该bean中注入heroDao这个被自动创建的bean(不懂请参考spring+mybatis整合)--控制器中自动装配service这个bean--通过该bean调用方法操作数据库--返回modelandview对象完成跳转或者返回string对象完成跳转。

1. 查询heroLiet:

㈠创建hero实体类,并在数据库中有hero表,且存在记录。

㈡创建HeroMapper接口,以及sql-hero.xml文件:


public interface HeroMapper {

	public List<Hero> getHerList();
	
	public int addHero(Hero hero);
	
	public int getTotal();
	
	public List<Hero> getHerList(Page page);
	
	public void delHero (int id);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
<mapper namespace="com.dao.HeroMapper">
	<select id="getHerList" resultType="Hero" parameterType="Page">
		select * from hero_1
		<if test="start!=null and count!=null">
			limit #{start},#{count}
		</if>
	</select>
		
	<insert id="addHero" parameterType="Hero">
		insert into hero_1
		(name) values
		(#{name})
	</insert>
	
	<insert id="delHero" parameterType="_int">
		delete from hero_1
		where id=#{id}
	</insert>
	
	<select id="getTotal" resultType="_int">
		select count(*) from hero_1
	</select>			
</mapper>

㈢创建HeroService接口和HeroServiceImpl实现类:


public interface IHeroService {
	public List<Hero> getHeroList();	
	public int addHero(Hero hero);
	public int getTotal();	
	public List<Hero> getHeroList(Page page);	
	public void delHero(int id);	
	public void addTwo();
}

public class HeroServiceImpl implements IHeroService {	
	private HeroMapper heroDao;
	public List<Hero> getHeroList() {
		return heroDao.getHerList();
	}	
	public int addHero(Hero hero) {
		return heroDao.addHero(hero);
	}
	public int getTotal() {
		return heroDao.getTotal();
	}
	public List<Hero> getHeroList(Page page) {
		return heroDao.getHerList(page);
	}	
	public void delHero(int id) {
		heroDao.delHero(id);
	}		
    //heroDao的get/set方法	
}

㈣:spring文件夹下创建spring-hero.xml文件,配置hero需要使用的bean:其中,heroDao这个bean是被spring-mybatis自动创建的。然后注入到heroService的heroDao属性中。需要在heroService接口中给出heroDao属性的get/set方法。

<beans
     <bean id="heroController" class="com.controller.HeroController">
     	<property name="iHeroService" ref="heroService" />
     </bean>
     
     <bean id="heroService" class="com.service.impl.HeroServiceImpl">
     	<property name="heroDao" ref="heroDao" />
     </bean>               
</beans>

㈤在controller包下创建HeroController类,作为控制器。此时基本架构已经创建完成,则现在可以创建一个请求地址,然后在controller控制器中完成业务逻辑,返回jsp页面,这里以请求heroList.do这个地址为例,返回heroList.jsp并遍历显示该集合为例。在controller中:

@Controller
@RequestMapping("/hero")              //上级请求地址,类似于struts的namespace
public class HeroController {

	@Resource
	private IHeroService iHeroService;    //get/set方法
	private List<Hero> heroList;          //get/set方法

	@RequestMapping("/showHero.do")     //请求地址
	public ModelAndView getHeroList() {
		ModelAndView mv = new ModelAndView("showHero");
		heroList = new ArrayList<Hero>();
		heroList = iHeroService.getHeroList();
		mv.addObject("heroList", heroList);
		return mv;
	}

}

㈥在jsp/hero下创建showHero.jsp,使用c:forEach标签完成遍历输出。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>英雄列表</title>
</head>
<body>
<table align="center" border="1">
	<tr>
		<td>id</td>
		<td>name</td>
	</tr>
	<c:forEach items="${heroList}" var="h" varStatus="vs">
	<tr>
		<td>${h.id}</td>
		<td>${h.name}</td>
	</tr>
	</c:forEach>
</table>
</body>
</html>

㈦访问localhost:8080/SSM/hero/showHero.do,即可显示heroList数据。请求数据--跳转controller--跳转到映射的方法--执行方法--跳转至JSP完成响应。

至此,一个完整的SSM框架就搭建完成了。

2. 分页实现

实现分页利用了SQL的limit查询功能,并且创建了page类,用来存储开始页,末页,显示页数,当前页数等属性信息。废话不说,直接上代码:

page实体类:

public class Page {	
	private int start;	
	private int count;	
	private int last;	
	private int total;	
	private int pages;	
	private int nowPage;	
    //此方法是:传入total值,计算最后一页的start值。
	public void changeLast(int total) {
		if(total%count > 0) {
			last = (total/count) * count;
			/*return last;*/
		}else {
			last = (total/count - 1) *count;
			/*return last;*/
		}
	}
	//此方法是显示总页数的值
	public void changePages(int total) {
		if(total%count > 0) {
			pages = (total/count)+1;
		}else {
			pages = total/count;
		}
	}
	//此方法是计算当前页数的值
	public void nowPage(int total) {
		nowPage = start/count+1;
	}
}

 HeroController中的方法如下:

	@RequestMapping("/showHeroByLimit.do")
	public ModelAndView getHeroListByLimit(Page page,HttpServletRequest req) {
		String id = req.getParameter("id"); //此操作是在添加成功后,重定向至该方法是传递一个添加成功的id值,并将这个id传递至jsp中,判断是否添加成功使用。
		ModelAndView mv = new ModelAndView();
		int total = 0;
		total = iHeroService.getTotal(); //获取总条数
		
		if(page.getCount() == 0) {   //判断如果直接访问该请求,则page对象为空的,判断为空给page属性一个初始值。即默认显示第一页。
			page = new Page();
			page.setStart(0);
			page.setCount(5);
		}
		page.changeLast(total);  //计算最后一页的start值
		page.changePages(total); //计算总页数

		if(page.getStart() < 0) {    //此判断是控制如果已经在第一页了,仍然点上一页还是会显示第一页,最后一页点击下一与同理。
			page.setStart(0);
		}else if(page.getStart() >= total) {
			page.setStart(page.getLast());
		}
		page.nowPage(total);  //计算当前页数。
		
		heroList = new ArrayList<Hero>();
		heroList = iHeroService.getHeroList(page);  //分页查询
		mv.addObject("heroList", heroList);
		mv.addObject("page", page);
		mv.addObject("id", id);
		mv.setViewName("showHeroByLimit");  //跳转
		return mv;
	}

jsp页面中: 点击上一页、下一页、首页、末页时,都会传递相关的参数到后台中,则回台根据传递的参数分页查询。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>英雄分页列表</title>
</head>
<body>
<script type="text/javascript" src="../js/jquery.min.js"></script>
<table align="center" border="1">
	<tr>
		<td>id</td>
		<td>name</td>
		<td>操作</td>
	</tr>
	<c:forEach items="${heroList}" var="h" varStatus="vs">
	<tr>
		<td>${h.id}</td>
		<td>${h.name}</td>
		<td>
			<button class="e" id="${h.id}">修改</button>
			<button class="d" id="${h.id}">删除</button>
		</td>
	</tr>
	</c:forEach>
	<tr>
		<td colspan="3">
			<a href="?start=0&count=${page.count}">首页</a>
			<a href="?start=${page.start-page.count}&count=${page.count}">上一页</a>
			<a href="?start=${page.start+page.count}&count=${page.count}">下一页</a>
			<a href="?start=${page.last}&count=${page.count}">末页</a>
			<font>总页数${page.pages}</font>
			<font>当前页数${page.nowPage}</font>
		</td>
	</tr>
	
</table>
<div style="display:none" align="center" id="showAdd">
<form>
	英雄名称<input type="text" name="name" id="name">
	<button onclick="add()">添加</button>
	<div style="display:none" align="center" id="msg"></div>
</form>
</div>
<script type="text/javascript" src="../js/hero/hero.js?t=06"></script>
</body>
</html>

3. 新增添加方法,和通过Ajax删除英雄

当点击添加英雄按钮后,显示下面的编辑框,如果再次点击添加英雄按钮,则此编辑框消失。在编辑框中设置了为空验证,如果不为空点击添加之后,提交数据至后台,添加到数据库,并返回该展示页面,提示:添加成功,显示3秒后消失。JSP页面如下:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>英雄分页列表</title>
</head>
<body>
<script type="text/javascript" src="../js/jquery.min.js"></script>
<div align="center">
	<!-- 点击按钮,触发JS中的showAdd方法,效果就是在切换隐藏和显示下面的form编辑框 -->
	<button onclick="showAdd()">添加英雄</button>
	 <!-- 用来判断是否添加成功,因为添加成功的话后台会传递一个id值过来 -->
	<input type="hidden" value="${id}" id="addSucc">
	<!-- 用来显示添加成功或者失败的提示信息 -->
	<font id="divSucc" style="display: block"></font>
</div>
<table align="center" border="1">
	<tr>
		<td>id</td>
		<td>name</td>
		<td>操作</td>
	</tr>
	<c:forEach items="${heroList}" var="h" varStatus="vs">
	<tr>
		<td>${h.id}</td>
		<td>${h.name}</td>
		<td>
			<button class="e" id="${h.id}">修改</button>
			<!-- 点击删除按钮后,JS中有class为d的触发方法,会执行ajax请求,完成删除,请看JS的触发方法 -->
			<button class="d" id="${h.id}">删除</button>
		</td>
	</tr>
	</c:forEach>
	<tr>
		<td colspan="3">
			<a href="?start=0&count=${page.count}">首页</a>
			<a href="?start=${page.start-page.count}&count=${page.count}">上一页</a>
			<a href="?start=${page.start+page.count}&count=${page.count}">下一页</a>
			<a href="?start=${page.last}&count=${page.count}">末页</a>
			<font>总页数${page.pages}</font>
			<font>当前页数${page.nowPage}</font>
		</td>
	</tr>
	
</table>
<div style="display:none" align="center" id="showAdd">
<form>
	英雄名称<input type="text" name="name" id="name">
	<!-- 点击添加之后,进行表单为空验证,验证通过后提交数据到后台,完成数据插入 -->
	<button onclick="add()">添加</button>
	<!-- 提示输入为空验证消息 -->
	<div style="display:none" align="center" id="msg"></div>
</form>
</div>
<script type="text/javascript" src="../js/hero/hero.js?t=06"></script>
</body>
</html>

JS页面:

//预加载函数
$(function () {
	if($("#addSucc").val() == null || $("#addSucc").val() == "") {
		$("#divSucc").hide();
	}else {
		$("#divSucc").attr("color","blue");
		$("#divSucc").html("英雄池添加成功!");
		$("#divSucc").fadeIn(1000); // 延迟一秒显示
		$("#divSucc").fadeOut(3000);//延迟三秒隐藏
	}
    //删除按钮的触发方法,确认框如果点击是则执行ajax删除。
	$("button.d").click(function (){
		var d = confirm("确定要删除该英雄么?");
		if(d) {
			var id = $(this).attr("id");
			var url = "delHero.do";
			$.ajax({
				url:url,
				type:"get",
				data:{
					id:id
				},
				dataType:"text",
				success:function(result) {
					if(result == 'OK') {
						alert("删除成功!");
						window.location.reload();
					}else {
						alert("删除失败!")
					}
				}
			});
		}
	});
	
	
	
});

//显示与隐藏切换
function showAdd() {
	$("#showAdd").toggle();	
	
}

//点击添加之后的表单验证和提交后台。
function add() {
	var form = window.document.forms[0];
	if($("#name").val() == null || $("#name").val() == "") {
		$("#msg").show();
		$("#msg").html("输入的英雄名为空,请检查!");
		$("form").submit(function() { 
			return false;
		});
	}else {
		form.action = "addHero.do";
		form.submit();
	}
}

4. 着重介绍下ajax删除请求

①首先点击删除按钮后,会触发这个方法,如果点击是,我们在jsp中将遍历的hero的id值存储在该按钮的id属性中,则通过attr读取id属性的值可以轻易的获取到该条数据的id值,将该id值传值后台完成删除,当然是通过ajax的方式完成的。ajax就需要传递数据到后台,后台方法返回一个结果。

$("button.d").click(function (){
        var d = confirm("确定要删除该英雄么?");
        if(d) {
            var id = $(this).attr("id");
            var url = "delHero.do";
            $.ajax({
                url:url,
                type:"get",
                data:{
                    id:id
                },
                dataType:"text",
                success:function(result) {
                    if(result == 'OK') {
                        alert("删除成功!");
                        window.location.reload();
                    }else {
                        alert("删除失败!")
                    }
                }
            });
        }
    });

②ajax请求的后台方法:一般在web服务器中,ajax请求返回值读取的话是读取写入到responseBody中的数据。我们在struts中请求ajax时,可以通过获取httpservletResponse对象,通过getWrite.write()方法,写入到response响应体中,来提供给ajax作为返回值。则再SSM框架中可以直接使用@ResponseBody注解,就可以直接通过返回字符串的方式将该字符串输出到responseBody中了。

	@ResponseBody   //使用该注解,可以直接将返回值返回至HTML.response.body中,
	@RequestMapping("/delHero.do")
	public String delHero(HttpServletResponse res,HttpServletRequest req) throws IOException {
		id = Integer.parseInt(req.getParameter("id"));
		iHeroService.delHero(id);
		System.out.println(id);
		return "OK";
	}
	

三:综合知识点

在springMVC中,controller类上面的@requestMapping("hero")与方法中的地址@requestMapping("list"),形成组合地址完成访问,也就是/hero/list。

@responseBody 与@requestBody的使用以及区别。具体可以参考这位博主的博文:https://blog.csdn.net/ff906317011/article/details/78552426

@Responsebody 注解表示该方法的返回的结果直接写入 HTTP 响应正文(ResponseBody)中,一般在ajax异步获取数据时使用,通常是在使用 @RequestMapping 后,返回值通常解析为跳转路径,加上 @Responsebody 后返回结果不会被解析为跳转路径,而是直接写入HTTP 响应正文中。 

有关JSON数据中,字符和对象之间的转换,也就是{name:"zhangsan"}与{"name":"zhangsan"}这两种方式之间的转换:

①在JS中,将json对象转换为字符即var json = {name:"zhang"}转换为{"name":"zhang"} 
var jsonStr = JSON.stringify(json)

反过来转换就是:var json = JSON.parse(jsonStr)

②在java后台中Json对象与字符串之间的转换:
从string a = {name:"zhang"}转换为Json json={"name":"zhang"}
Json json = new Json();
json.puy("json",JSONObject.toJSON(a)); //此时就有了一个json
json.toJSONString()就转换为了{"name","zhang"}了。

反过来转换就是JSON.parseObject

使用{"name":"zhang"}这种格式的JSON数据更容易匹配值对象中。

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值