Java导入Excel到数据库工具——EXCEL-UTIL4J使用示例

2017.9.3 更新v2.1.0: 进度监控调整为整个导入过程的进度监控,并且更加精确

前些时日开发了一款用Java将Excel导入数据库的工具:EXCEL-UTIL4J源码和文档在这里),觉得还是写一个使用示例的好,主要想用来记录使用方法。

因为时间有限,工具只实现了一对一关系数据导入,以及只能使用hibernate作为ORM框架进行导入,所以本示例将只做一对一关系数据导入,并且最大可能的利用到工具的所有功能,使用hibernate进行导入。

示例中只会简单的提示代码作用,建议先大概浏览一下工具文档,这样有利于理解示例的代码,本示例代码源码见 excel-util4j-sample


需求

1. Excel表如下:
Excel表头
2. 数据表结构如下:
表结构
3. 分析
  1. 字段对应
    Excel中的账号密码在数据表excel_util4j_user(下简称user)中,分别对应usernamepassword
    Excel中的姓名性别生日电话地址excel_util4j_user_info(下简称info)中,分别对应namegenderbirthdayphoneaddress

  2. 字段约束(下面有建表语句,展示更清晰)

    1. 唯一性
      user表中username以及info表中phone字段为唯一的
    2. 非空
      usernamepasswordname
    3. 其他
      username最大20字、password6~20字、name最大20字、phone最大20,并且需要验证是否为电话号码、address最大50字
  3. 常量值字段
    user表中除了excel中的字段外,还有enable字段,该字段表示是否启用账号,值为01,分别表示禁用启用,本例中所有账户设置为1

  4. 其他字段

  • 每个用户还有角色,对应的表为excel_util4j_user_role(下简称user_role)表,由于excel-util4j只能导入一对一关系的数据,所以这里仅仅设置角色为一个,假设role_id3

  • 创建时间字段create_time,该字段不由excel导入,也不是常量字段,而是系统当前时间

  1. 密码处理
    密码解析后,需要验证是否为6~20个字符,通过验证后需要转换为MD5+Base64加密的格式再存表

下面是建表语句,表结构和约束更清晰的展现:

  • user表:
CREATE TABLE excel_util4j_user (
  
  id bigint(20) NOT NULL AUTO_INCREMENT,
  username varchar(20) NOT NULL,
  password varchar(20) NOT NULL,
  create_time datetime DEFAULT NULL,
  enable int(1) NOT NULL,
  
  PRIMARY KEY (id),
  UNIQUE KEY username_unique (username) USING BTREE
);
  • info表:
CREATE TABLE excel_util4j_user_info (

  id bigint(20) NOT NULL AUTO_INCREMENT,
  user_id bigint(20) NOT NULL,
  name varchar(20) NOT NULL,
  gender int(1) DEFAULT NULL,
  birthday date DEFAULT NULL,
  phone varchar(20) DEFAULT NULL,
  address varchar(50) DEFAULT NULL,

  PRIMARY KEY (id),
  UNIQUE KEY phone_unique (phone) USING BTREE
);
  • user_role表:
CREATE TABLE excel_util4j_user_role (

  id bigint(20) NOT NULL AUTO_INCREMENT,
  user_id bigint(20) NOT NULL,
  role_id int(11) NOT NULL,

  PRIMARY KEY (id)
);

编码

  1. 源码下载下来,然后mvn install -Dmaven.test.skip=true安装相应jar包,共4个

  2. 配置POM文件,将包添加到依赖:

    <!-- 读取excel的包 -->
    <dependency>
        <groupId>online.dinghuiye</groupId>
        <artifactId>poi-kit</artifactId>
        <version>1.0.1</version>
    </dependency>
    
    <!-- excel-util4j-api包 -->
    <dependency>
        <groupId>online.dinghuiye</groupId>
        <artifactId>excelutil-api</artifactId>
        <version>2.1.0</version>
    </dependency>
    
    <!-- excel-util4核心包 -->
    <dependency>
        <groupId>online.dinghuiye</groupId>
        <artifactId>excelutil</artifactId>
        <version>2.1.0</version>
    </dependency>
    
    <!-- ORM实现包 -->
    <dependency>
        <groupId>online.dinghuiye</groupId>
        <artifactId>persistence-hibernate-impl</artifactId>
        <version>2.1.0</version>
    </dependency>
    
    <!-- 数据库相关的包,使用Mysql数据库 -->
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.42</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.0.31</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>4.3.11.Final</version>
    </dependency>
    
    <!-- 转换,验证等包 -->
    <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.6</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>1.1.0.Final</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator -->
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>5.4.1.Final</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish</groupId>
        <artifactId>javax.el</artifactId>
        <version>3.0.1-b08</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.33</version>
    </dependency>
    
  3. 创建实体类,用Intellij Idea自动生成,下面列出3个pojo代码,关键部分都有注释。属性注解参看文档

    值得注意的是:

    • UniqueValidator.class(v2.0.0版本的示例)或UsernameUniqueValidator.classPhoneUniqueValidator.class(v2.1.0版本示例)是自定义的判重验证器,并不是使用的hibernate validator的自定义验证,是excel-util4j中实现的验证器,使用前需要编写类并实现online.dinghuiye.api.validation.Validator,代码见下文UniqueValidator验证器,另外两个验证器相似

    • password字段从excel导入后,需要先验证长度在6 ~ 20个字符,然后通过PasswordRepairer进行修正,即将POJO对象的password属性值设置为MD5+Base64加密的字符串。加密后的字符串长度不一定再满足6 ~ 20个字符了,hibernate存表时还会再次按照POJO注解进行验证,此时就可能无法验证通过而报错,所以需要将因为属性修正而可能影响到的验证的注解加上groups = {Validator.class}参数,Validator.classonline.dinghuiye.api.validation.Validator.class

    • createTime字段是系统时间字段,使用自定义转换器设值,使用谦虚编写类并实现online.dinghuiye.api.resolution.Convertor接口,重写方法返回需要的特定值即可,代码见下文CurrentTimeConvertor转换器(v2.1.0版本示例)

  • user pojo
package online.dinghuiye.example.entity;

import online.dinghuiye.api.annotation.validate.Validate;
import online.dinghuiye.api.validation.Validator;
import online.dinghuiye.core.annotation.convert.ConstValue;
import online.dinghuiye.core.annotation.convert.ValueConvert;
import online.dinghuiye.core.annotation.excel.SheetTitleName;
import online.dinghuiye.example.convertor.CurrentTimeConvertor;
import online.dinghuiye.example.validator.UsernameUniqueValidator;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.validator.constraints.NotBlank;

import javax.persistence.*;
import javax.validation.Valid;
import javax.validation.constraints.Size;
import java.util.Date;

/**
 * @author Strangeen on 2017/08/27
 *
 * @author Strangeen on 2017/9/3
 * @version 2.1.0
 */
@Entity
@DynamicInsert(true)
@Table(name = "excel_util4j_user")
public class ExcelUtil4JUserEntity {

    @Transient // 不需要执行转换和验证,但并不影响hibernate存表的操作
    private Long id;

    @SheetTitleName("账号") // excel表字段对应
    @NotBlank
    @Size(max = 20, message = "输入最大{max}个字")
    @Validate(validator = UsernameUniqueValidator.class, message = "已被注册") // 自定义检验器,判断重复
    private String username;

    @SheetTitleName("密码")
    // 如果后续需要repaire的属性,需要将repaire可能影响的验证加上groups={Validator.class}
    // 否则可能会导致比如字符串长度改变而无法再存表时通过hibernate的验证
    @NotBlank
    @Size(max = 20, min = 6, message = "输入{min}~{max}个字", groups = {Validator.class})
    private String password;

    @ValueConvert(CurrentTimeConvertor.class) // 自定义转换器,存入当前时间
    private Date createTime;

    @ConstValue("1") // 常量值转换器,导入时会被设置为1
    private Integer enable;

    @Valid // 执行hibernate validator支持的对象属性检测,不注释@Valid则不会对info对象的属性进行检测
    private ExcelUtil4JUserInfoEntity info;

    private ExcelUtil4JUserRoleEntity userRole;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    public Long getId() {
        return id;
    }

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

    @Basic
    @Column(name = "username")
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Basic
    @Column(name = "password")
    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Basic
    @Column(name = "create_time")
    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    @Basic
    @Column(name = "enable")
    public Integer getEnable() {
        return enable;
    }

    public void setEnable(Integer enable) {
        this.enable = enable;
    }

    @OneToOne(mappedBy = "user", cascade = {CascadeType.ALL})
    public ExcelUtil4JUserInfoEntity getInfo() {
        return info;
    }

    public void setInfo(ExcelUtil4JUserInfoEntity info) {
        this.info = info;
    }

    // 这里定义为OneToOne并不太合适,只是为了演示
    // 常规应该使用OneToMany,现阶段无法实现OneToMany的导入,就只能使用RowRecordPerPersistentRepairer在存表前进行修正了
    @OneToOne(mappedBy = "user", cascade = {CascadeType.ALL})
    public ExcelUtil4JUserRoleEntity getUserRole() {
        return userRole;
    }

    public void setUserRole(ExcelUtil4JUserRoleEntity userRole) {
        this.userRole = userRole;
    }
}
  • info pojo
package online.dinghuiye.example.entity;

import online.dinghuiye.api.annotation.validate.Validate;
import online.dinghuiye.core.annotation.convert.BlankToNull;
import online.dinghuiye.core.annotation.convert.DateFormat;
import online.dinghuiye.core.annotation.convert.ValueMap;
import online.dinghuiye.core.annotation.excel.SheetTitleName;
import online.dinghuiye.example.validator.PhoneUniqueValidator;
import org.hibernate.validator.constraints.NotBlank;

import javax.persistence.*;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.util.Date;

/**
 * @author Strangeen on 2017/08/27
 *
 * @author Strangeen on 2017/9/3
 * @version 2.1.0
 */
@Entity
@Table(name = "excel_util4j_user_info")
public class ExcelUtil4JUserInfoEntity {

    @Transient
    private Long id;

    @OneToOne // 必须使用hibernate的双向绑定,否则无法生成hibernate的POJO对象
    @JoinColumn(name = "user_id")
    private ExcelUtil4JUserEntity user;

    @SheetTitleName("姓名")
    @NotBlank
    @Size(max = 20, message = "输入最大{max}个字")
    private String name;

    @SheetTitleName("性别")
    @ValueMap("{'男':1,'女':0}") // Map值转换器,将excel的只按照Map映射进行转换
    private Integer gender;

    @SheetTitleName("生日")
    @BlankToNull // 空串转NULL转换器,防止生日字段为空串转换为Date时报错
    @DateFormat("yyyy-MM-dd") // 时间格式转换器,将时间转换为指定格式,如果单元格为“文本”就会使用
    private Date birthday;

    @SheetTitleName("电话")
    // hibernate validator的正则验证,这里大概写一个电话的验证正则
    @Pattern(regexp = "(^(\\+|0)[0-9]{2}[0-9]{11}$)|(^[0-9]{11}$)", message = "填写不正确")
    @Validate(validator = PhoneUniqueValidator.class, message = "已被注册")
    private String phone;

    @SheetTitleName("地址")
    @Size(max = 50, message = "输入最大{max}个字")
    private String address;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    public Long getId() {
        return id;
    }

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

    @Basic
    @Column(name = "name")
    public String getName() {
        return name;
    }

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

    @Basic
    @Column(name = "gender")
    public Integer getGender() {
        return gender;
    }

    public void setGender(Integer gender) {
        this.gender = gender;
    }

    @Basic
    @Column(name = "birthday")
    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Basic
    @Column(name = "phone")
    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    @Basic
    @Column(name = "address")
    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @OneToOne
    @JoinColumn(name = "user_id")
    public ExcelUtil4JUserEntity getUser() {
        return user;
    }

    public void setUser(ExcelUtil4JUserEntity user) {
        this.user = user;
    }
}
  • user_role pojo
package online.dinghuiye.example.entity;

import online.dinghuiye.core.annotation.convert.ConstValue;
import online.dinghuiye.core.annotation.excel.Transient;

import javax.persistence.*;

/**
 * @author Strangeen on 2017/08/27
 */
@Entity
@Table(name = "excel_util4j_user_role")
public class ExcelUtil4JUserRoleEntity {

    @Transient
    private Long id;

    private ExcelUtil4JUserEntity user;

    @ConstValue("3") // 导入的用户角色均为3
    private Integer roleId;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    public Long getId() {
        return id;
    }

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

    @OneToOne
    @JoinColumn(name = "user_id")
    public ExcelUtil4JUserEntity getUser() {
        return user;
    }

    public void setUser(ExcelUtil4JUserEntity user) {
        this.user = user;
    }

    @Basic
    @Column(name = "role_id")
    public Integer getRoleId() {
        return roleId;
    }

    public void setRoleId(Integer roleId) {
        this.roleId = roleId;
    }
}
  1. 编写CurrentTimeConvertor转换器代码:
    自定义转换器需要实现online.dinghuiye.api.resolution.Convertor
package online.dinghuiye.example.convertor;

import online.dinghuiye.api.resolution.Convertor;

import java.lang.reflect.Field;
import java.util.Date;
import java.util.Map;

/**
 * 当前时间转换器,该转换器为自定义转换器,用于适应字段为当前时间的情况
 * 自定义转换器需要实现online.dinghuiye.api.resolution.Convertor
 *
 * @author Strangeen on 2017/09/04
 * @version 2.1.0
 */
public class CurrentTimeConvertor implements Convertor {

    // convet方法参数会传入所有可能用到的值
    // obj 需要转换的值
    // field pojo属性字段
    // excelRecordMap excel数据map<表头名称, 单元格值>
    @Override
    public Object convert(Object obj, Field field, Map<String, Object> excelRecordMap) {

        // 返回当前时间即可,自定义转换器也可以用于其他特定值得转换
        return new Date();
    }
}
  1. 编写UniqueValidator验证器代码(UsernameUniqueValidatorPhoneUniqueValidator验证器代码略,请查看示例代码v2.1.0版本):
    自定义判重验证器需要实现online.dinghuiye.api.validation.Validator
package online.dinghuiye.example.validator;

import online.dinghuiye.api.validation.Validator;
import online.dinghuiye.example.util.SessionFactoryUtil;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;

import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 判重Validator
 *
 * @author Strangeen on 2017/08/16
 */
public class UniqueValidator implements Validator {

    /*
    实现原理是将数据库中唯一值全部读取出来缓存到cache中,
    然后将导入的只和cache比对,如果重复则返回false,如果不重复则加入到cache,返回true

    这样做可以提高检验效率,但是必须要考虑并发问题
     */

    private static Set<Object> usernameCache = new HashSet<>();
    private static Set<Object> phoneCache = new HashSet<>();

    public UniqueValidator() {
        // 设置username的cache
        setCache(usernameCache, "excel_util4j_user", "username");
        // 设置phone的cache
        setCache(phoneCache, "excel_util4j_user_info", "phone");
    }

    public static void setCache(Set<Object> cache, String tableName, String columnName) {
        SessionFactory factory = SessionFactoryUtil.getSessionFactory();
        Session session = factory.openSession();
        SQLQuery query = session.createSQLQuery("select " + columnName + " from " + tableName);
        List<Object> list = query.list();
        for (Object obj : list) {
            cache.add(obj);
        }
        session.close();
    }

    @Override
    public <User> boolean validate(Object fieldValue, Field field, User obj) {

        // 判断是username还是phone,这里只是演示,所以将cache写在一起,常规思路应该是分开的2个类
        if ("username".equals(field.getName())) {
            if (usernameCache.contains(fieldValue)) return false;
            usernameCache.add(fieldValue);
            return true;
        } else if ("phone".equals(field.getName())) {
            if (phoneCache.contains(fieldValue)) return false;
            phoneCache.add(fieldValue);
            return true;
        }

        // 其他字段不用检测,直接返回true
        return true;
    }
}
  1. 编写PasswordRepairer修正器代码:
    在存表前将密码设置为密码明文的加密字符串,MD5Util的代码略
package online.dinghuiye.example.repairer;

import online.dinghuiye.api.entity.Process;
import online.dinghuiye.api.entity.ResultStatus;
import online.dinghuiye.api.entity.RowRecord;
import online.dinghuiye.api.persistence.RowRecordPerPersistentRepairer;
import online.dinghuiye.example.entity.ExcelUtil4JUserEntity;
import online.dinghuiye.example.util.MD5Util;

import java.util.List;

/**
 * 对密码进行MD5加密处理
 * 由于密码需要验证长度,所以不能在验证前就MD5加密,否则验证是不正确的
 * 所以需要在存表前进行修正
 *
 * 通过实现RowRecordPerPersistentRepairer可以获得hibernate的POJO对象,从而进行修正
 *
 * v2.1.0 进度监控更佳精确,接口提供了进度对象
 *        如果遍历了List<RowRecord> list,可以对每一次循环执行process.updateProcess(1)
          需要注意的是,使用前必须判断`process`是否为`null`,如果入口方法出传入的`ProcessObserver`为`null`,那么`process`就会为`null`
 *        如果没有遍历或者不执行上述方法,当repairer执行完毕,程序会自动修正进度,
 *           进度展示效果会立即变更到repairer方法执行完毕的进度状态
 *
 * @author Strangeen on 2017/9/3
 * @version 2.1.0
 */
public class PasswordRepairer implements RowRecordPerPersistentRepairer {

    @Override
    public void repaire(List<RowRecord> list, Process process) {
        for (RowRecord rr : list) {
            if (rr.getResult().getResult() != ResultStatus.SUCCESS) continue;
            ExcelUtil4JUserEntity obj =
                    (ExcelUtil4JUserEntity) rr.getPojoRecordMap().get(ExcelUtil4JUserEntity.class);
            obj.setPassword(MD5Util.encode(obj.getPassword()));

            // 精确的进度展示,可以操作process对象
            if (process != null)
                process.updateProcess(1);
        }
    }
}
  1. 编写入口代码:
    配置SessionFactory的代码略,可以使用多种方式配置,如Spring等
package online.dinghuiye.example;

import online.dinghuiye.api.entity.Process;
import online.dinghuiye.api.entity.ResultStatus;
import online.dinghuiye.api.entity.RowRecord;
import online.dinghuiye.api.entity.TransactionMode;
import online.dinghuiye.core.ImportHandler;
import online.dinghuiye.core.persistence.RowRecordPersistencorHibernateImpl;
import online.dinghuiye.core.resolution.torowrecord.RowRecordHandlerImpl;
import online.dinghuiye.core.validation.RowRecordValidatorImpl;
import online.dinghuiye.example.entity.ExcelUtil4JUserEntity;
import online.dinghuiye.example.repairer.PasswordRepairer;
import online.dinghuiye.example.util.SessionFactoryUtil;
import online.dinghuiye.excel.ExcelFactory;
import org.hibernate.SessionFactory;

import java.io.File;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

/**
 * @author Strangeen on 2017/08/30
 *
 * @author Strangeen on 2017/9/3
 * @version 2.1.0
 */
public class ExcelImportor {

    public static void main(String[] args) {

        SessionFactory factory = null;
        try {

            // 获取SessionFactory
            factory = SessionFactoryUtil.getSessionFactory();
            // 设置mode:SINGLETON为单条存储事务,MULTIPLE为整体事务,详见文档
            TransactionMode mode = TransactionMode.SINGLETON;

            // 创建导入器handler
            ImportHandler handler = new ImportHandler();
            handler.setHandler(new RowRecordHandlerImpl()); // 一对一关系解析器
            handler.setValidator(new RowRecordValidatorImpl()); // 验证器
            handler.setPersistencor(new RowRecordPersistencorHibernateImpl(factory)); // 持久化器hibernate实现
            handler.setRepairer(new PasswordRepairer()); // 密码存储修正器
            handler.setMode(mode);

            // 执行excel导入
            List<RowRecord> resultList = handler.importExcel(
                    ExcelFactory.newExcel(new File("D:/test_template.xlsx")), // 创建AbstractExcel对象读取excle
                    0, // 读取sheet序号为0的sheet
                    new Observer() {
                        @Override
                        public void update(Observable o, Object arg) {
                            // 创建导入进度观察者,arg为导入进度百分数(没有%)
                            Process process = (Process) arg;
                            System.out.println("进度:" + process.getProcess() + ",当前阶段:" + process.getNode());
                        }
                    },
                    ExcelUtil4JUserEntity.class); // 传入POJO

            // 打印结果,如果有错误可以在resultList中得到
            int successCount = 0;
            int errorCount = 0;
            for (RowRecord rr : resultList) {
                if (rr.getResult().getResult() != ResultStatus.SUCCESS) { // 导入不成功
                    System.out.println(rr.getRowNo() + "行 - " + rr.getResult().getMsg()); // 打印行号和错误信息
                    errorCount ++; // 记录错误数
                } else
                    successCount ++; // 记录成功数
            }
            // 注意:MULTIPLE为整体事务,successCount依然可能不为0,仅作为标识,实际上没有任何数据存入数据库的
            System.out.println("success " + successCount + ", error " + errorCount);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            SessionFactoryUtil.closeSessionFactory(factory);
        }
    }
}

至此,代码全部编写完毕,执行导入后,控制台打印出来的内容类似如下(我导入了10条数据,有6条存在问题,使用SINGLETON事务形式):

进度:2.5,当前阶段:RESOLUTION
进度:5.0,当前阶段:RESOLUTION
进度:7.5,当前阶段:RESOLUTION
进度:10.0,当前阶段:RESOLUTION
进度:12.5,当前阶段:RESOLUTION
进度:15.0,当前阶段:RESOLUTION
进度:17.5,当前阶段:RESOLUTION
进度:20.0,当前阶段:RESOLUTION
进度:22.5,当前阶段:RESOLUTION
进度:25.0,当前阶段:RESOLUTION
进度:27.500000000000004,当前阶段:VALIDATION
进度:30.0,当前阶段:VALIDATION
进度:32.5,当前阶段:VALIDATION
进度:35.0,当前阶段:VALIDATION
进度:37.5,当前阶段:VALIDATION
进度:40.0,当前阶段:VALIDATION
进度:42.5,当前阶段:VALIDATION
进度:45.0,当前阶段:VALIDATION
进度:47.5,当前阶段:VALIDATION
进度:50.0,当前阶段:VALIDATION
进度:52.5,当前阶段:REPAIRATION
进度:55.00000000000001,当前阶段:REPAIRATION
进度:57.49999999999999,当前阶段:REPAIRATION
进度:60.0,当前阶段:REPAIRATION
进度:77.5,当前阶段:PERSISTENCE
进度:80.0,当前阶段:PERSISTENCE
进度:82.5,当前阶段:PERSISTENCE
进度:85.0,当前阶段:PERSISTENCE
进度:87.5,当前阶段:PERSISTENCE
进度:90.0,当前阶段:PERSISTENCE
进度:92.5,当前阶段:PERSISTENCE
进度:95.0,当前阶段:PERSISTENCE
进度:97.5,当前阶段:PERSISTENCE
进度:100.0,当前阶段:PERSISTENCE
4行 - 账号不能为空;密码输入6~20个字;姓名不能为空;电话填写不正确;地址输入最大50个字;
5行 - 电话填写不正确;
7行 - 电话已被注册;账号已被注册;
8行 - 姓名不能为空;账号不能为空;
10行 - 密码不能为空;
11行 - 姓名不能为空;
success 4, error 6
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容