搭建Spring+Struts2+Ibatis工程

1、新建Web工程,并导入jar包:
spring包:
这里写图片描述
struts2包:
这里写图片描述
ibatis包:
这里写图片描述
另外需要一个数据库连接的包:
这里写图片描述
工程jar预览:
这里写图片描述
这里写图片描述

工程整体预览:
这里写图片描述
这里写图片描述
这里写图片描述


2、首先配置web.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>ssi</display-name>
        <!-- 默认主界面 -->
        <welcome-file-list>
            <welcome-file>login.jsp</welcome-file>
        </welcome-file-list>
        <!-- <welcome-file-list>
            <welcome-file>index.html</welcome-file>
            <welcome-file>index.htm</welcome-file>
            <welcome-file>login.jsp</welcome-file>
            <welcome-file>default.html</welcome-file>
            <welcome-file>default.htm</welcome-file>
            <welcome-file>default.jsp</welcome-file>
        </welcome-file-list> -->
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:base-spring.xml</param-value>
        </context-param>
        <listener>
            <listener-class>
            org.springframework.web.context.request.RequestContextListener
            </listener-class>
        </listener>
        <!-- 服务器启动时,通过监听器初始化Spring的配置环境 监听器,默认加载文件是:/WEB-INF/applicationContext.xml -->
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>

        <!-- 配置Struts2框架的核心调度器 -->
        <filter>
            <filter-name>struts2</filter-name>
            <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>struts2</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
</web-app>
---------------------------------------------------------------------

3、配置struts.xml
--------------

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
    <constant name="struts.i18n.encoding" value="utf-8" />
    <constant name="struts.enable.DynamicMethodInvocation" value="false" />
    <constant name="struts.devMode" value="false" />
    <constant name="struts.ui.theme" value="xhtml" />
    <!-- <constant name="struts.objectFactory.spring.autoWire" value="type"/> -->
    <!-- specified its actions generate by the container spring -->
    <!-- <constant name="struts.objectFactory" value="spring" /> -->
    <!-- 国际化基类 -->
    <!-- 18n -->
    <constant name="struts.custom.i18n.resources" value="message"></constant>
    <!-- 对所有的Action限制文件上传的大小 -->
    <constant name="struts.multipart.maxSize" value="5000000"></constant>
    <package name="default" namespace="/" extends="struts-default">
        <!-- 访问项目时,如果没有找到指定的action则执行这个 -->
        <default-action-ref name="index"></default-action-ref>
        <!-- 如果跳转到test.action时,没有相应的result则会跳转到这里来 -->
        <global-results>
            <result>/WEB-INF/error.jsp</result>
        </global-results>
    </package>
    <!-- 使用引入其他struts-action的配置,便于管理 -->
    <include file="struts-dept.xml"></include>
    <include file="struts-login.xml"></include>

</struts>
---------------------------------------------------------------------
4、配置ibatis.xml,本工程中的文件名为:base-ibatis.xml
----------------------------------------

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMapConfig PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN" 
"http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
    <!-- 就像hibernate的hbm.xml文件一样,一个实体对应一个xml -->
    <sqlMap resource="com/ssi/core/dept/mapper/DeptMapper.xml" />
</sqlMapConfig>
---------------------------------------------------------------------
5、配置spring.xml,本工程中的文件名为:base-spring.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:context="http://www.springframework.org/schema/context"
    xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.2.xsd
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
    <context:annotation-config />
    <context:component-scan base-package="com.ssi" />
    <!-- 表示加载属性文件 -->
    <!-- <context:property-placeholder location="classpath:jdbc.properties" 
        /> -->
    <!-- 定义数据源 -->
    <!-- 配置数据源,连接池采用的是c3p0,具体各参数代表意义参看c3p0自带的doc,非常详细。 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <property name="driverClass" value="${jdbc.driver}" />
        <property name="jdbcUrl" value="${jdbc.url}" />
        <property name="user" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="minPoolSize" value="${jdbc.minPoolSize}" />
        <property name="maxPoolSize" value="${jdbc.maxPoolSize}" />
        <property name="maxIdleTime" value="${jdbc.maxIdleTime}" />
        <property name="acquireIncrement" value="${jdbc.acquireIncrement}" />
        <property name="maxStatements" value="${jdbc.maxStatements}" />
        <property name="initialPoolSize" value="${jdbc.initialPoolSize}" />
        <property name="idleConnectionTestPeriod" value="${jdbc.idleConnectionTestPeriod}" />
        <property name="acquireRetryAttempts" value="${jdbc.acquireRetryAttempts}" />
    </bean>
    <!-- 配置iBatis的sqlMapClient,这里当然是交给了spring去处理,其中,将SqlMapConfig文件放到了WEB-INF的iBatis目录下,也是便于管理 -->
    <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
        <property name="configLocation">
            <value>classpath:base-ibatis.xml</value>
        </property>
        <!-- 这里使用的数据源就是上面配置的数据源 -->
        <property name="dataSource">
            <ref bean="dataSource" />
        </property>
    </bean>

    <!-- 上面的数据源的value值用的是表达式,原因就在这里,这将配置文件放到了iBatis目录下,也就是jdbc.properties,设置了c3p0的各项参数 -->
    <bean id="propertyConfig"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location">
            <value>classpath:jdbc.properties</value>
        </property>
    </bean>

    <!-- 这个就是spring的事务管理了,采用的DataSource事务管理,要管理的DataSource当然也是上面配置的DataSource -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource">
            <ref bean="dataSource" />
        </property>
    </bean>

    <!-- 事务管理的代理类,将其抽象化abstruct=true,以后需要进行事务管理直接继承此类就行了,非常方便 -->
    <bean id="transactionProxy"
        class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
        abstract="true">
        <!-- 这个就是刚才配置的事务管理器 -->
        <property name="transactionManager">
            <ref bean="transactionManager" />
        </property>
        <!-- 下面是spring事务管理的策略,可以看到,凡是涉及数据库插入、修改的操作都应当以add、insert、edit、update、delete开头,
            这样才能由spring进行事务管理 -->
        <property name="transactionAttributes">
            <props>
                <prop key="insert*">PROPAGATION_REQUIRED,-Exception</prop>
                <prop key="add*">PROPAGATION_REQUIRED,-Exception</prop>
                <prop key="update*">PROPAGATION_REQUIRED,-Exception</prop>
                <prop key="edit*">PROPAGATION_REQUIRED,-Exception</prop>
                <prop key="delete*">PROPAGATION_REQUIRED,-Exception</prop>
                <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
                <prop key="query*">PROPAGATION_REQUIRED,readOnly</prop>
                <prop key="*">PROPAGATION_REQUIRED</prop>
            </props>
        </property>
    </bean>
以下是通过spring注入bean的方式注入dao service action
    <!--Dept Service -->
    <!--Dept ActionBean -->
    <bean id="deptDao" class="com.ssi.core.dept.dao.impl.DeptDaoImpl">
        <property name="sqlMapClient" ref="sqlMapClient"></property>
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <bean id="deptService" class="com.ssi.core.dept.service.impl.DeptServiceImpl">
        <property name="deptDao" ref="deptDao"></property>
    </bean>
    <bean id="deptAction" class="com.ssi.core.dept.action.DeptAction">
        <property name="deptService" ref="deptService"></property>
    </bean>
    <bean id="loginAction" class="com.ssi.core.login.action.LoginAction">
        <property name="deptService" ref="deptService"></property>
    </bean>
</beans>
---------------------------------------------------------------------
6、配置具体action的xml:struts-login.xml
-----------------

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
    <package name="login" namespace="/login" extends="struts-default">
        <action name="userLogin" class="loginAction" method="loginAction">
            <result>/WEB-INF/welcome.jsp</result>
            <result name="input">/login.jsp</result>
        </action>
    </package>
</struts>
struts-dept.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts> 
    <!-- DeptAction -->
    <!-- findAllDept Action -->
    <package name="dept" namespace="/dept" extends="json-default" >
       <action name="findDept" class="deptAction" method="findDeptAction">
           <!-- <result>/oasys_manage/oasys_user_add.jsp</result> -->
           <result type="json">
               <param name="root">deptList</param>
           </result>
       </action>
    </package>
</struts>
================================
7、配置实体mapper.xml,DeptMapper.xml:
================================

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"      
"http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap>
    <typeAlias alias="Dept" type="com.ssi.core.dept.entity.Dept" />
    <resultMap id="dept" class="Dept">
        <result property="deptno" column="DEPTNO" />
        <result property="dname" column="DNAME" />
    </resultMap>
    <select id="findDept" resultClass="Dept">
        SELECT * FROM DEPT
    </select>
    <select id="findDeptById" resultClass="Dept" parameterClass="java.lang.Long">
        SELECT * FROM DEPT WHERE DEPTNO=#deptno#
    </select>
    <insert id="doAddDept" parameterClass="Dept">
        INSERT INTO DEPT(DEPTNO,DNAME)
        VALUES(#deptno#,#dname#)
    </insert>
    <delete id="doDeleteDept" parameterClass="Dept">
        DELETE FROM DEPT WHERE DEPTNO=#deptno#
    </delete>
</sqlMap>
---------------------------------------------------------------------
8、jdbc.properties文件:
--------------------

jdbc.driver=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:@localhost:1521:orcl
jdbc.username=scott
jdbc.password=1
jdbc.minPoolSize=5
jdbc.maxPoolSize=20
jdbc.maxIdleTime=1800
jdbc.acquireIncrement=5
jdbc.maxStatements=50
jdbc.initialPoolSize=10
jdbc.idleConnectionTestPeriod=1800
jdbc.acquireRetryAttempts=30
================================
9、java代码:
---------

ENTITY
------
package com.ssi.core.dept.entity;

import java.io.Serializable;

@SuppressWarnings("serial")
public class Dept implements Serializable {
    private Integer deptno;
    private String dname;

    public Dept() {
    }

    public Dept(Integer deptNo, String dname) {
        this.deptno = deptNo;
        this.dname = dname;
    }

    public Integer getDeptno() {
        return deptno;
    }

    public void setDeptno(Integer deptno) {
        this.deptno = deptno;
    }

    public String getDname() {
        return dname;
    }

    public void setDname(String dname) {
        this.dname = dname;
    }

    @Override
    public String toString() {
        return "Dept [deptno=" + deptno + ", dname=" + dname + "]";
    }

}

DAO
---
package com.ssi.core.dept.dao;

import java.io.Serializable;
import java.util.List;

import com.ssi.core.dept.entity.Dept;

public interface IDeptDao {
    public List<Dept> findDept();

    public Dept findDeptById(Serializable id);

    public boolean doAddDept(Dept dept);

    public boolean doUpdateDept(Dept dept);

    public int doDeleteDept(Dept dept);
}

DAOIMPL
-------
package com.ssi.core.dept.dao.impl;
import java.io.Serializable;
import java.util.List;
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;

import com.ssi.core.dept.dao.IDeptDao;
import com.ssi.core.dept.entity.Dept;

@SuppressWarnings("unchecked")
public class DeptDaoImpl extends SqlMapClientDaoSupport implements IDeptDao {

    public List<Dept> findDept() {
        return super.getSqlMapClientTemplate().queryForList("findDept");
    }

    @Override
    public Dept findDeptById(Serializable id) {
        return (Dept) super.getSqlMapClientTemplate().queryForObject(
                "findDeptById", id);
    }

    @Override
    public boolean doAddDept(Dept dept) {
        return (boolean) super.getSqlMapClientTemplate().insert("doAddDept",
                dept);
    }

    @Override
    public boolean doUpdateDept(Dept dept) {
        return false;
    }

    @Override
    public int doDeleteDept(Dept dept) {
        return super.getSqlMapClientTemplate().delete("doDeleteDept", dept);
    }
}

SERVICE
-------
package com.ssi.core.dept.service;

import java.io.Serializable;
import java.util.List;

import com.ssi.core.dept.entity.Dept;


public interface IDeptService {
    public List<Dept> findDept();

    public Dept findDeptById(Serializable id);

    public boolean doAddDept(Dept dept);

    public boolean doUpdateDept(Dept dept);

    public int doDeleteDept(Dept dept);
}

SERVICEIMPL--注意setter getter 必须有
--------------------------------
package com.ssi.core.dept.service.impl;
import java.io.Serializable;
import java.util.List;
import com.ssi.core.dept.dao.IDeptDao;
import com.ssi.core.dept.entity.Dept;
import com.ssi.core.dept.service.IDeptService;
public class DeptServiceImpl implements IDeptService {
    private IDeptDao deptDao;
    public List<Dept> findDept() {
        return this.deptDao.findDept();
    }

    public Dept findDeptById(Serializable id) {
        return this.deptDao.findDeptById(id);
    }

    public boolean doAddDept(Dept dept) {
        return this.deptDao.doAddDept(dept);
    }

    public boolean doUpdateDept(Dept dept) {
        return false;
    }

    public int doDeleteDept(Dept dept) {
        return this.deptDao.doDeleteDept(dept);
    }

    public IDeptDao getDeptDao() {
        return deptDao;
    }

    public void setDeptDao(IDeptDao deptDao) {
        this.deptDao = deptDao;
    }
}

ACTION--注意setter getter 必须有
由于一些需要,添加了一个抽象的AbstractAction提供相关属性与方法:
package com.ssi.common.action;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import net.sf.json.JSONObject;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
/**
 * tools class for action
 */
@SuppressWarnings("serial")
public abstract class AbstractAction extends ActionSupport {

    // 定义分页有关的处理操作,分页的参数是简写参数
    protected Integer pageNo = 1;
    protected Integer pageSize = 5;
    protected String column;
    protected String keyword;

    public Integer getPageNo() {
        return pageNo;
    }

    public void setPageNo(Integer pageNo) {
        this.pageNo = pageNo;
    }

    public Integer getPageSize() {
        return pageSize;
    }

    public void setPageSize(Integer pageSize) {
        this.pageSize = pageSize;
    }

    public String getColumn() {
        if (this.column == null || "".equals(this.column)) { // 没有设置查询列
            return this.getDefaultColumn();
        }
        return this.column;
    }

    /**
     * 用来取得默认的分页显示列
     * 
     * @return
     */
    public abstract String getDefaultColumn();

    /**
     * 设置有可能进行模糊查询字段
     * 
     * @return
     */
    public abstract String getColumnData();

    public void setColumn(String column) {
        this.column = column;
    }

    /**
     * select by keyword if keyword == null return "";
     * 
     * @return
     */
    public String getKeyword() {
        if (this.keyword == null) {
            return "";
        }
        return keyword;
    }

    public void setKeyword(String keyword) {
        this.keyword = keyword;
    }

    /**
     * get request
     * 
     * @return
     */
    public HttpServletRequest getRequest() {
        return ServletActionContext.getRequest();
    }

    /**
     * get response
     * 
     * @return
     */
    public HttpServletResponse getResponse() {
        return ServletActionContext.getResponse();
    }

    /**
     * get session
     * 
     * @return
     */
    public HttpSession getSession() {
        return this.getRequest().getSession();
    }

    /**
     * get application
     * 
     * @return
     */
    public ServletContext getApplication() {
        return ServletActionContext.getServletContext();
    }

    /**
     * output value to ajax+js
     * 
     * @param value
     */
    public void print(String value) {
        HttpServletResponse response = this.getResponse();
        response.setCharacterEncoding("utf-8");
        try {
            response.getWriter().write(value);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * output json value to ajax+js
     * 
     * @param value
     */
    public void printJSON(Object value) {
        try {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("data", value);
            this.getResponse().getWriter().print(jsonObject);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * get date by specified pattern
     * 
     * @param date
     * @return
     */
    public String formatDate(Date date) {
        if (date == null) {
            return "";
        }
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
    }
}


----------
本工程中用到的LoginAction
package com.ssi.core.login.action;

import javax.servlet.http.HttpSession;
import com.opensymphony.xwork2.Action;
import com.ssi.common.action.AbstractAction;

import com.ssi.core.dept.entity.Dept;
import com.ssi.core.dept.service.IDeptService;

public class LoginAction extends AbstractAction {
    private static final long serialVersionUID = 2712664921709561868L;
    private IDeptService deptService;

    public String loginAction() throws Exception {
        HttpSession session = super.getSession();
        System.out.println(session);
        Dept deptDel = new Dept(1, "");
        System.out.println(this.deptService.doDeleteDept(deptDel));
        return Action.SUCCESS;
    }

    @Override
    public String getDefaultColumn() {
        return null;
    }

    @Override
    public String getColumnData() {
        return null;
    }

    public IDeptService getDeptService() {
        return deptService;
    }

    public void setDeptService(IDeptService deptService) {
        this.deptService = deptService;
    }
}

下面的暂未使用到:

package com.ssi.core.dept.action;
import java.util.List;

import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionSupport;
import com.ssi.core.dept.entity.Dept;
import com.ssi.core.dept.service.IDeptService;
@SuppressWarnings("serial")
public class DeptAction extends ActionSupport {
    private IDeptService deptService;
    private List<Dept> deptList;
    public String findDeptAction(){
        this.deptList = this.deptService.findDept();
        return Action.SUCCESS;
    }

    public IDeptService getDeptService() {
        return deptService;
    }

    public void setDeptService(IDeptService deptService) {
        this.deptService = deptService;
    }
    public List<Dept> getDeptList() {
        return deptList;
    }
    public void setDeptList(List<Dept> deptList) {
        this.deptList = deptList;
    }
}

测试,页面访问:
http://localhost:8080/ssi/打开登录页面(web.xml中配置的默认页面)login.jsp
点击登录,跳转到http://localhost:8080/ssi/login/userLogin.action
页面:
login.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
    String loginUrl = basePath + "login/userLogin.action";//点击登录访问这个路径
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>ssi</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<style type="text/css">
.login {
    list-style: none;
    color: red;
    position: relative;
    left: 10px;
    top: 20px;
    width: 240px;
}
</style>
</head>
<body class="box">
    <div class="container login-box">
        <div class="login-title text-center">
            <h1>
                <small><span class="glyphicon glyphicon-user"></span>&nbsp;SSI项目登录</small>
            </h1>
        </div>
        <div class="login-content">
            <form class="form-horizontal" id="myform" action="<%=loginUrl%>"
                method="post">
                <div class="form-group" id="user.useridDiv">
                    <div class="col-md-10 col-md-push-1">
                        <input type="text" id="user.userid" name="user.uname"
                            class="form-control" placeholder="请输入登录用户名">
                    </div>
                </div>
                <div class="form-group" id="user.useridDiv">
                    <div class="col-md-10 col-md-push-1">
                        <input type="password" id="user.password" name="user.password"
                            class="form-control" placeholder="请输入登录密码">
                    </div>
                </div>
                <div class="form-group">
                    <div class="col-md-6 col-md-offset-4">
                        <button type="submit" class="btn btn-primary" id="editBtn">登录</button>
                        <button type="reset" class="btn btn-warning">重置</button>
                    </div>
                </div>
            </form>
        </div>
    </div>
</body>
</html>

这里写图片描述
点击登录,访问loginAction,如果LoginAction中return “input”;则会跳转到相应的配置页面中。


至此SSI工程搭建完毕!这是基础的没有添加过多设计的工程,会有欠缺,仅学习使用。

  • 1
    点赞
  • 1
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:编程工作室 设计师:CSDN官方博客 返回首页
评论

打赏作者

深秋的露水

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值