SSH-1 员工增删改查案例

1. 基本需求

  基于SSH框架的员工增删改查小案例的具体需求如下图所示:
  这里写图片描述


2. 实现步骤

2.1 搭建SSH框架环境

第一步:添加Spring4框架,具体步骤如下:

  • 加入Spring4框架所依赖的jar包;
  • 配置web.xml文件使其能够在WEB应用中正常使用;
  • 添加Spring的Bean配置。

第二步:添加Hibernate4框架,具体步骤如下:

  • 加入Hibernate4所依赖的jar包;
  • 在类路径下创建hibernate.cfg.xml配置文件,用于配置hibernate的基本属性;
  • 建立所需要的持久化类及其对应的对象关系映射文件*.hbm.xml;
  • 与Spring4进行整合,即添加C3P0数据源和MySQL数据库的驱动,并在Spring的Bean配置文件中配置数据源、SessionFactory和声明式事务;
  • 配置成功后,启动当前WEB应用即可生成对应的数据表。

第三步:添加Struts2框架,具体步骤如下:

  • 加入Struts2框架所依赖的jar包,若有重复则删除版本较低的jar包;
  • 在web.xml文件中配置Struts2的过滤器;
  • 添加Struts2的配置文件;
  • 与Spring4进行整合,主要包括:
    • 加入Struts2的Spring插件,即struts2-spring-plugin-2.3.31.jar;
    • 在Spring的Bean配置文件中正常配置Action,要求其scope属性必须为prototype;
    • 在Struts2的配置文件中配置Action,其class属性需指向该Action在IoC容器中所配置的id。
2.2 显示所有员工信息

  EmployeeAction类中list()方法的核心代码如下所示:

/**
 * 功能一:查询并显示所有员工信息 
 * 注意事项: 
 *  1). 在事务作用范围之外访问员工的部门名称可能会出现懒加载异常,采用迫切左外连接查询或使用OpenSessionInViewFilter来实现
 *  2). 通过实现RequestAware接口来获取request,将获取的员工信息放入其属性中,即可在显示页面获取并显示
 * @return
 */
public String list() {
    /**
     * System.out.println(employeeService.getAll()); //会出现懒加载异常
     * 事务作用的EmployeeService的getAll()方法上,其执行完毕即提交事务,此时打印员工的部门信息即会出现异常
     */
    request.put("EMPLOYEES", employeeService.getAll());
    return "list";
}
2.3 删除当前员工信息

  执行删除后需要将返回值设置为redirect类型,并重定向到emp-list;其中,删除时的提示信息和Ajax请求实现的核心代码如下所示:

<!-- Ajax的具体使用参看struts-2.3.31/docs/docs/ajax.html -->
<script type="text/javascript" src="scripts/jquery-1.7.2.js"></script>
<script type="text/javascript">

    $(function() {
        // 1). 弹出信息提示框
        $(".delete").click(function() {
            var name = $(this).next(":input").val();
            var flag = confirm("确定要删除" + name + "的信息吗?");
            if(flag) {
                // 2). 删除员工的信息
                var $tr = $(this).parent().parent();
                // 发送删除请求
                var url = this.href;
                var args = {"times":new Date()}; //用于禁用缓存
                $.post(url, args, function(data) {
                    if(data == "1") {
                        alert("恭喜您,删除成功!");
                        if($tr.siblings().length == 0) {
                            $tr.parent().parent().remove();
                        }
                        $tr.remove();
                    } else {
                        alert("对不起,删除失败!");
                    }
                });
            }

            // 取消超链接的默认行为
            return false;
        });
    });
</script>
2.4 添加新的员工信息

  显示表单页面时,需要先查询出所有的部门信息;需要使用Struts2的ModelDriven和Preparable拦截器;也需要自定义转换器将时间字符串转为日期对象。其中,Ajax校验员工名的核心代码如下所示:

<script type="text/javascript" src="scripts/jquery-1.7.2.js"></script>
<script type="text/javascript">
    $(function() {
        $(":input[name=name]").change(function() {

            var nameval = $(this).val();
            nameval = $.trim(nameval);
            $this = $(this);
            if(nameval != "") {
                $this.nextAll("font").remove();
                var url = "emp-validateName";
                var args = {"name":nameval, "time":new Date()};
                $.post(url, args, function(data) {
                    if(data == "1") {
                        $this.after("<font color='green'>恭喜您,员工名可用!</font>");
                    } else if (data == "0") {
                        $this.after("<font color='red'>对不起,员工名不可用!</font>");
                    } else {
                        alert("服务器异常,请重试!");
                    }
                });
            } else {
                alert("对不起,员工姓名不能为空!");
                $(this).val("");
            }
        });
    });
</script>
2.5 修改指定员工信息

  需要回显当前员工的信息,并设置其员工名不可修改。


3. 具体实现

  基于SSH框架的员工增删改查小案例的实现代码下载地址:http://download.csdn.net/download/bingbeichen/9790372

代码目录结构:

    这里写图片描述

EmployeeAction.java:
package com.qiaobc.ssh.actions;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.Map;

import org.apache.struts2.interceptor.RequestAware;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.Preparable;
import com.qiaobc.ssh.entities.Employee;
import com.qiaobc.ssh.service.DepartmentService;
import com.qiaobc.ssh.service.EmployeeService;

public class EmployeeAction extends ActionSupport implements RequestAware, ModelDriven<Employee>, Preparable {

    private static final long serialVersionUID = 1L;

    private EmployeeService employeeService;
    private DepartmentService departmentService;    

    public void setEmployeeService(EmployeeService employeeService) {
        this.employeeService = employeeService;
    }

    public void setDepartmentService(DepartmentService departmentService) {
        this.departmentService = departmentService;
    }

    /**
     * 功能一:查询并显示所有员工信息 
     * 注意事项: 
     *  1). 在事务作用范围之外访问员工的部门名称可能会出现懒加载异常,采用迫切左外连接查询 
     *  2). 通过实现RequestAware接口来获取request,将获取的员工信息放入其属性中,即可在显示页面获取并显示
     * 
     * @return
     */
    public String list() {
        /**
         * System.out.println(employeeService.getAll()); //会出现懒加载异常
         * 事务作用的EmployeeService的getAll()方法上,其执行完毕即提交事务,此时打印员工的部门信息即会出现异常
         */
        request.put("EMPLOYEES", employeeService.getAll());
        return "list";
    }

    private Map<String, Object> request;

    @Override
    public void setRequest(Map<String, Object> arg0) {
        this.request = arg0;
    }

    /**
     * 功能二-1:正常删除当前员工的信息 
     * 注意事项:根据员工id删除当前员工信息后需要重定向到emp-list,以防止删除操作的重复提交
     */
    /*
     * public String delete() { employeeService.delete(id); return "delete"; }
     */
    private Integer id;

    public void setId(Integer id) {
        this.id = id;
    }

    private InputStream inputStream;

    public InputStream getInputStream() {
        return inputStream;
    }

    /**
     * 功能二-2:使用Ajax删除当前员工的信息,需要使用jQuery弹出信息提示框
     * Ajax的具体使用参见struts-2.3.31/docs/docs/ajax.html,与Struts2的文件下载类似
     */
    public String delete() {
        try {
            employeeService.delete(id);
            inputStream = new ByteArrayInputStream("1".getBytes("UTF-8"));
        } catch (Exception e) {
            e.printStackTrace();
            try {
                inputStream = new ByteArrayInputStream("0".getBytes("UTF-8"));
            } catch (UnsupportedEncodingException e1) {
                e1.printStackTrace();
            }
        }
        return "ajax-success";
    }

    /**
     * 功能三:添加员工信息
     *  1). 显示表单页面:需要先查询所有的部门信息
     *  2). 使用Struts2的ModelDriven和Preparable拦截器
     *  3). 需要将时间字符串转为日期对象
     */
    public String input() {
        // 获取所有的部门信息
        request.put("DEPARTMENTS", departmentService.getDepartments());
        return "input";
    }

    public void prepareInput() {
        if(id != null) {
            model = employeeService.get(id);
        }
    }

    @Override
    public void prepare() throws Exception {}

    private Employee model;

    @Override
    public Employee getModel() {
        return model;
    }

    public String save() {
        if(id == null) {
            model.setCreateTime(new Date());
        }
        employeeService.saveOrUpdate(model);
        return "save";
    }

    /**
     * 可以根据id来判断是从数据库获取model还是创建新的model
     * @return
     */
    public void prepareSave() {
        if(id == null) {
            model = new Employee();
        } else {
            model = employeeService.get(id);
        }
    }

    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String validateName() throws UnsupportedEncodingException {
        if(employeeService.validateName(name)) {
            inputStream = new ByteArrayInputStream("1".getBytes("UTF-8"));
        } else {
            inputStream = new ByteArrayInputStream("0".getBytes("UTF-8"));
        }
        return "ajax-success";
    }
}
SSHDateConverters.java:
package com.qiaobc.ssh.converters;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

import org.apache.struts2.util.StrutsTypeConverter;

public class SSHDateConverters extends StrutsTypeConverter {

    private DateFormat dateFormat;

    {
        dateFormat = new SimpleDateFormat("yyyy-MM-dd");
    }

    @Override
    public Object convertFromString(Map context, String[] values, Class toClass) {
        System.out.println("convertFromString");
        if(toClass == Date.class) {
            try {
                return dateFormat.parse(values[0]);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
        return values;
    }

    @Override
    public String convertToString(Map context, Object o) {
        System.out.println("convertToString");
        if(o instanceof Date) {
            return dateFormat.format((Date) o);
        }
        return null;
    }
}
BaseDao.java:
package com.qiaobc.ssh.dao;

import org.hibernate.Session;
import org.hibernate.SessionFactory;

public class BaseDao {

    private SessionFactory sessionFactory;

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public Session getSession() {
        Session session = this.sessionFactory.getCurrentSession();
        return session;
//      return this.sessionFactory.getCurrentSession();
    }

}
DepartmentDao.java:
package com.qiaobc.ssh.dao;

import java.util.List;

import com.qiaobc.ssh.entities.Department;

public class DepartmentDao extends BaseDao {

    /**
     * 查询所有的部门信息
     */
    @SuppressWarnings("unchecked")
    public List<Department> getDepartments() {
        String hql = "FROM Department";
        return getSession().createQuery(hql).list();
    }
}
EmployeeDao.java:
package com.qiaobc.ssh.dao;

import java.util.List;

import com.qiaobc.ssh.entities.Employee;

public class EmployeeDao extends BaseDao {

    @SuppressWarnings("unchecked")
    public List<Employee> getAll() {
        // 使用迫切左外连接查询防止出现懒加载异常
        String hql = "FROM Employee e LEFT JOIN FETCH e.dept";
        return getSession().createQuery(hql).list();
    }

    public void delete(int id) {
        String hql = "DELETE FROM Employee e WHERE e.id = ?";
        getSession().createQuery(hql).setInteger(0, id).executeUpdate();
    }

    public void saveOrUpdate(Employee employee) {
        getSession().saveOrUpdate(employee);
    }

    public Employee validateName(String name) {
        String hql = "FROM Employee e WHERE e.name = ?";
        return (Employee) getSession().createQuery(hql).setString(0, name).uniqueResult();
    }

    public Employee get(Integer id) {
        return (Employee) getSession().get(Employee.class, id);
    }
}
EmployeeService.java:
package com.qiaobc.ssh.service;

import java.util.List;

import com.qiaobc.ssh.dao.EmployeeDao;
import com.qiaobc.ssh.entities.Employee;

public class EmployeeService {

    private EmployeeDao employeeDao;

    public void setEmployeeDao(EmployeeDao employeeDao) {
        this.employeeDao = employeeDao;
    }

    public List<Employee> getAll() {
        List<Employee> emps = employeeDao.getAll();
        return emps;
    }

    public void delete(int id) {
        employeeDao.delete(id);
    }

    public void saveOrUpdate(Employee employee) {
        employeeDao.saveOrUpdate(employee);
    }

    public boolean validateName(String name) {
        return employeeDao.validateName(name) == null;
    }

    public Employee get(Integer id) {
        return employeeDao.get(id);
    }
}
applicationContext-beans.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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">


    <!-- 配置Struts2的Action所对应的bean:设置其scope属性为prototype -->
    <bean id="employeeAction" class="com.qiaobc.ssh.actions.EmployeeAction" scope="prototype">
        <property name="employeeService" ref="employeeService"></property>
        <property name="departmentService" ref="departmentService"></property>
    </bean>

    <bean id="employeeDao" class="com.qiaobc.ssh.dao.EmployeeDao">
        <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>

    <bean id="employeeService" class="com.qiaobc.ssh.service.EmployeeService">
        <property name="employeeDao" ref="employeeDao"></property>
    </bean>

    <bean id="departmentDao" class="com.qiaobc.ssh.dao.DepartmentDao">
        <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>

    <bean id="departmentService" class="com.qiaobc.ssh.service.DepartmentService">
        <property name="departmentDao" ref="departmentDao"></property>
    </bean>

</beans>
applicationContext.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:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <!-- 配置数据源:配置完后可测试其是否配置成功 -->
    <context:property-placeholder location="classpath:db.properties"/>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="driverClass" value="${jdbc.driverClass}"></property>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
        <property name="initialPoolSize" value="${jdbc.initialPoolSize}"></property>
        <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
    </bean>

    <!-- 配置Hibernate的SessionFactory实例:通过Spring提供的LocalSessionFactoryBean进行配置 -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">

        <!-- 配置数据源属性 -->
        <property name="dataSource" ref="dataSource"></property>

        <!-- 配置Hibernate配置文件的位置及名称 -->
        <property name="configLocation" value="classpath:hibernate.cfg.xml"></property>

        <!-- 配置Hibernate映射文件的位置及名称:可以使用通配符 -->
        <property name="mappingLocations" value="classpath:com/qiaobc/ssh/entities/*.hbm.xml"></property>
    </bean>

    <!-- 配置声明式事务 -->
    <!-- 1). 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>

    <!-- 2). 配置事务属性 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>

    <!-- 3). 配置事务切入点、关联事务属性 -->
    <aop:config>
        <aop:pointcut expression="execution(* com.qiaobc.ssh.service.*.*(..))" id="txPointcut"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
    </aop:config>

</beans>
db.properties:
jdbc.user=root
jdbc.password=root
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql:///spring

jdbc.initialPoolSize=5
jdbc.maxPoolSize=10
hibernate.cfg.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>

        <!-- 配置Hibernate的基本属性:方言、SQL显示与格式化、数据表生成策略、二级缓存相关 -->

        <property name="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>

        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.format_sql">true</property>

        <property name="hibernate.hbm2ddl.auto">update</property>

    </session-factory>
</hibernate-configuration>
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>

    <package name="ssh-emps" namespace="/" extends="struts-default">

        <!-- 定义新的拦截器栈,配置prepare拦截器栈的alwaysInvokePrepare属性为false -->
        <interceptors>
            <interceptor-stack name="sshEmpsStack">
                <interceptor-ref name="paramsPrepareParamsStack">
                    <param name="prepare.alwaysInvokePrepare">false</param>
                </interceptor-ref>
            </interceptor-stack>
        </interceptors>

        <!-- 使用新配置的拦截器栈 -->
        <default-interceptor-ref name="sshEmpsStack"></default-interceptor-ref>

        <action name="emp-*" class="employeeAction" method="{1}">
            <result name="list">/WEB-INF/views/emp-list.jsp</result>
            <result name="delete" type="redirect">/emp-list</result>
            <result name="ajax-success" type="stream">
                <param name="contentType">text/html</param>
                <param name="inputName">inputStream</param>
            </result>
            <result name="input">/WEB-INF/views/emp-input.jsp</result>
            <result name="save" type="redirect">/emp-list</result>
        </action>

    </package>

</struts>
emp-list.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>

<script type="text/javascript" src="scripts/jquery-1.7.2.js"></script>
<script type="text/javascript">

    $(function() {
        // 1). 弹出信息提示框
        $(".delete").click(function() {
            var name = $(this).next(":input").val();
            var flag = confirm("确定要删除" + name + "的信息吗?");
            if(flag) {
                // 2). 删除员工的信息
                var $tr = $(this).parent().parent();
                // 发送删除请求
                var url = this.href;
                var args = {"times":new Date()}; //用于禁用缓存
                $.post(url, args, function(data) {
                    if(data == "1") {
                        alert("恭喜您,删除成功!");
                        if($tr.siblings().length == 0) {
                            $tr.parent().parent().remove();
                        }
                        $tr.remove();
                    } else {
                        alert("对不起,删除失败!");
                    }
                });
            }

            // 取消超链接的默认行为
            return false;
        });
    });

</script>

<body>

    <s:debug></s:debug>
    <br>

    <s:if
        test="#request.EMPLOYEES == null || #request.EMPLOYEES.size() == 0">
        <h4>暂没有任何员工信息...</h4>
    </s:if>
    <s:else>
        <table border="1" cellpadding="10" cellspacing="0">
            <thead>
                <tr>
                    <td>ID</td>
                    <td>NAME</td>
                    <td>EMAIL</td>
                    <td>BIRTHDAY</td>
                    <td>CREATETIME</td>
                    <td>DEPARTMENT</td>
                    <td>DELETE</td>
                    <td>EDIT</td>
                </tr>
            </thead>
            <tbody>
                <s:iterator value="#request.EMPLOYEES">
                    <tr>
                        <td>${id }</td>
                        <td>${name }</td>
                        <td>${email }</td>
                        <td>
                            <s:date name="birth" format="yyyy-MM-dd"/>
                        </td>
                        <td>
                            <s:date name="createTime" format="yyyy-MM-dd hh-mm-ss"/>
                        </td>
                        <td>${dept.name }</td>
                        <td>
                            <a href="emp-delete?id=${id }" class="delete">Delete</a>
                            <input type="hidden" value="${name }">
                        </td>
                        <td><a href="emp-input?id=${id }">Edit</a></td>
                    </tr>
                </s:iterator>
            </tbody>
        </table>
    </s:else>
</body>
</html>
emp-input.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!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=UTF-8">
<title>Insert title here</title>
</head>

<script type="text/javascript" src="scripts/jquery-1.7.2.js"></script>
<script type="text/javascript">

    $(function() {
        $(":input[name=name]").change(function() {

            var nameval = $(this).val();
            nameval = $.trim(nameval);
            $this = $(this);
            if(nameval != "") {
                $this.nextAll("font").remove();
                var url = "emp-validateName";
                var args = {"name":nameval, "time":new Date()};
                $.post(url, args, function(data) {
                    if(data == "1") {
                        $this.after("<font color='green'>恭喜您,员工名可用!</font>");
                    } else if (data == "0") {
                        $this.after("<font color='red'>对不起,员工名不可用!</font>");
                    } else {
                        alert("服务器异常,请重试!");
                    }
                });
            } else {
                alert("对不起,员工姓名不能为空!");
                $(this).val("");
            }
        });
    });

</script>

<body>

    <h4>Add New Employee:</h4>

    <s:form action="emp-save" method="post">
        <s:if test="id != null">
            <s:textfield label="Name" name="name" disabled="true"></s:textfield>
            <s:hidden name="id"></s:hidden>
            <!-- 可以通过添加隐藏域的方式将未提交的name和createTime字段值提交到服务器
                <s:hidden name="name"></s:hidden>
                <s:hidden name="createTime"></s:hidden>
             -->
        </s:if>
        <s:else>
            <s:textfield label="Name" name="name"></s:textfield>
        </s:else>
        <s:textfield label="Email" name="email"></s:textfield>
        <s:textfield label="Birth" name="birth"></s:textfield>

        <s:select list="#request.DEPARTMENTS" 
            listKey="id" listValue="name" 
            label="Department" name="dept.id"></s:select>

        <s:submit></s:submit>
    </s:form>

</body>
</html>
  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值