Browse Source

抓取数据

jinxia.mo 3 năm trước cách đây
commit
118fe8a61a

+ 64 - 0
pom.xml

@@ -0,0 +1,64 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.springframework.boot</groupId>
+    <artifactId>spring-boot-starter-parent</artifactId>
+    <version>2.6.4</version>
+  </parent>
+  <groupId>com.mingxue</groupId>
+  <artifactId>spider</artifactId>
+  <version>1</version>
+
+  <properties>
+    <java.version>1.8</java.version>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    <maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
+    <skipTests>true</skipTests>
+    <maven.test.skip>true</maven.test.skip>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpclient</artifactId>
+    </dependency>
+    <!-- SpringBoot 核心包 -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>cn.hutool</groupId>
+      <artifactId>hutool-db</artifactId>
+      <version>5.7.21</version>
+    </dependency>
+    <dependency>
+      <groupId>com.squareup.okhttp3</groupId>
+      <artifactId>okhttp</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.alibaba</groupId>
+      <artifactId>fastjson</artifactId>
+      <version>1.2.79</version>
+    </dependency>
+    <dependency>
+      <groupId>mysql</groupId>
+      <artifactId>mysql-connector-java</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>

+ 19 - 0
src/main/java/com/mingxue/spider/Application.java

@@ -0,0 +1,19 @@
+package com.mingxue.spider;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+
+/**
+ * 启动程序
+ * 
+ * @author ruoyi
+ */
+@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
+public class Application {
+    public static void main(String[] args) {
+        // System.setProperty("spring.devtools.restart.enabled", "false");
+        SpringApplication.run(Application.class, args);
+        System.out.println("(♥◠‿◠)ノ゙  项目启动成功   ლ(´ڡ`ლ)゙  ");
+    }
+}

+ 84 - 0
src/main/java/com/mingxue/spider/dto/User.java

@@ -0,0 +1,84 @@
+package com.mingxue.spider.dto;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+public class User {
+
+    private String code;
+    private String password;
+    private String token;
+    private Integer provinceId;
+    private String url;
+    private String host;
+    private Boolean isHn;
+
+    public String getCode() {
+        return code;
+    }
+
+    public User setCode(String code) {
+        this.code = code;
+        return this;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public User setPassword(String password) {
+        this.password = password;
+        return this;
+    }
+
+    public User setUrl(String url) {
+        this.url = url;
+        return this;
+    }
+
+    public User setHost(String host) {
+        this.host = host;
+        return this;
+    }
+
+    public User setIsHn(Boolean isHn) {
+        this.isHn = isHn;
+        return this;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public String getHost() {
+        return host;
+    }
+
+    public Boolean getIsHn() {
+        return isHn;
+    }
+
+    public Integer getProvinceId() {
+        return provinceId;
+    }
+
+    public User setProvinceId(Integer provinceId) {
+        this.provinceId = provinceId;
+        return this;
+    }
+
+    public String getToken() {
+        return token;
+    }
+
+    public User setToken(String token) {
+        this.token = token;
+        return this;
+    }
+
+    @Override
+    public String toString() {
+        return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
+    }
+
+}

+ 161 - 0
src/main/java/com/mingxue/spider/helper/AjaxResult.java

@@ -0,0 +1,161 @@
+package com.mingxue.spider.helper;
+
+
+import java.util.HashMap;
+
+/**
+ * 操作消息提醒
+ * 
+ * @author ruoyi
+ */
+public class AjaxResult extends HashMap<String, Object>
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 状态码 */
+    public static final String CODE_TAG = "code";
+
+    /** 返回内容 */
+    public static final String MSG_TAG = "msg";
+
+    /** 数据对象 */
+    public static final String DATA_TAG = "data";
+
+    /**
+     * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。
+     */
+    public AjaxResult()
+    {
+    }
+
+    /**
+     * 初始化一个新创建的 AjaxResult 对象
+     * 
+     * @param code 状态码
+     * @param msg 返回内容
+     */
+    public AjaxResult(int code, String msg)
+    {
+        super.put(CODE_TAG, code);
+        super.put(MSG_TAG, msg);
+    }
+
+    /**
+     * 初始化一个新创建的 AjaxResult 对象
+     * 
+     * @param code 状态码
+     * @param msg 返回内容
+     * @param data 数据对象
+     */
+    public AjaxResult(int code, String msg, Object data)
+    {
+        super.put(CODE_TAG, code);
+        super.put(MSG_TAG, msg);
+        if (StringUtils.isNotNull(data))
+        {
+            super.put(DATA_TAG, data);
+        }
+    }
+
+    /**
+     * 返回成功消息
+     * 
+     * @return 成功消息
+     */
+    public static AjaxResult success()
+    {
+        return AjaxResult.success("操作成功");
+    }
+
+    /**
+     * 返回成功数据
+     * 
+     * @return 成功消息
+     */
+    public static AjaxResult success(Object data)
+    {
+        return AjaxResult.success("操作成功", data);
+    }
+
+    /**
+     * 返回成功消息
+     * 
+     * @param msg 返回内容
+     * @return 成功消息
+     */
+    public static AjaxResult success(String msg)
+    {
+        return AjaxResult.success(msg, null);
+    }
+
+    /**
+     * 返回成功消息
+     * 
+     * @param msg 返回内容
+     * @param data 数据对象
+     * @return 成功消息
+     */
+    public static AjaxResult success(String msg, Object data)
+    {
+        return new AjaxResult(HttpStatus.SUCCESS, msg, data);
+    }
+
+    /**
+     * 返回错误消息
+     * 
+     * @return
+     */
+    public static AjaxResult error()
+    {
+        return AjaxResult.error("操作失败");
+    }
+
+    /**
+     * 返回错误消息
+     * 
+     * @param msg 返回内容
+     * @return 警告消息
+     */
+    public static AjaxResult error(String msg)
+    {
+        return AjaxResult.error(msg, null);
+    }
+
+    /**
+     * 返回错误消息
+     * 
+     * @param msg 返回内容
+     * @param data 数据对象
+     * @return 警告消息
+     */
+    public static AjaxResult error(String msg, Object data)
+    {
+        return new AjaxResult(HttpStatus.ERROR, msg, data);
+    }
+
+    /**
+     * 返回错误消息
+     * 
+     * @param code 状态码
+     * @param msg 返回内容
+     * @return 警告消息
+     */
+    public static AjaxResult error(int code, String msg)
+    {
+        return new AjaxResult(code, msg, null);
+    }
+
+    /**
+     * 方便链式调用
+     *
+     * @param key 键
+     * @param value 值
+     * @return 数据对象
+     */
+    @Override
+    public AjaxResult put(String key, Object value)
+    {
+        super.put(key, value);
+        return this;
+    }
+}

+ 85 - 0
src/main/java/com/mingxue/spider/helper/CharsetKit.java

@@ -0,0 +1,85 @@
+package com.mingxue.spider.helper;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 字符集工具类
+ * 
+ * @author ruoyi
+ */
+public class CharsetKit
+{
+    /** ISO-8859-1 */
+    public static final String ISO_8859_1 = "ISO-8859-1";
+    /** UTF-8 */
+    public static final String UTF_8 = "UTF-8";
+    /** GBK */
+    public static final String GBK = "GBK";
+
+    /** ISO-8859-1 */
+    public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1);
+    /** UTF-8 */
+    public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8);
+    /** GBK */
+    public static final Charset CHARSET_GBK = Charset.forName(GBK);
+
+    /**
+     * 转换为Charset对象
+     * 
+     * @param charset 字符集,为空则返回默认字符集
+     * @return Charset
+     */
+    public static Charset charset(String charset)
+    {
+        return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset);
+    }
+
+    /**
+     * 转换字符串的字符集编码
+     * 
+     * @param source 字符串
+     * @param srcCharset 源字符集,默认ISO-8859-1
+     * @param destCharset 目标字符集,默认UTF-8
+     * @return 转换后的字符集
+     */
+    public static String convert(String source, String srcCharset, String destCharset)
+    {
+        return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset));
+    }
+
+    /**
+     * 转换字符串的字符集编码
+     * 
+     * @param source 字符串
+     * @param srcCharset 源字符集,默认ISO-8859-1
+     * @param destCharset 目标字符集,默认UTF-8
+     * @return 转换后的字符集
+     */
+    public static String convert(String source, Charset srcCharset, Charset destCharset)
+    {
+        if (null == srcCharset)
+        {
+            srcCharset = StandardCharsets.ISO_8859_1;
+        }
+
+        if (null == destCharset)
+        {
+            destCharset = StandardCharsets.UTF_8;
+        }
+
+        if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset))
+        {
+            return source;
+        }
+        return new String(source.getBytes(srcCharset), destCharset);
+    }
+
+    /**
+     * @return 系统字符集编码
+     */
+    public static String systemCharset()
+    {
+        return Charset.defaultCharset().name();
+    }
+}

+ 154 - 0
src/main/java/com/mingxue/spider/helper/Constants.java

@@ -0,0 +1,154 @@
+package com.mingxue.spider.helper;
+
+
+/**
+ * 通用常量信息
+ * 
+ * @author ruoyi
+ */
+public class Constants {
+    /**
+     * UTF-8 字符集
+     */
+    public static final String UTF8 = "UTF-8";
+
+    /**
+     * GBK 字符集
+     */
+    public static final String GBK = "GBK";
+
+    /**
+     * http请求
+     */
+    public static final String HTTP = "http://";
+
+    /**
+     * https请求
+     */
+    public static final String HTTPS = "https://";
+
+    /**
+     * 通用成功标识
+     */
+    public static final String SUCCESS = "0";
+
+    /**
+     * 通用失败标识
+     */
+    public static final String FAIL = "1";
+
+    /**
+     * 登录成功
+     */
+    public static final String LOGIN_SUCCESS = "Success";
+
+    /**
+     * 注销
+     */
+    public static final String LOGOUT = "Logout";
+
+    /**
+     * 注册
+     */
+    public static final String REGISTER = "Register";
+
+    /**
+     * 登录失败
+     */
+    public static final String LOGIN_FAIL = "Error";
+
+    /**
+     * 验证码 redis key
+     */
+    public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
+
+    /**
+     * 登录用户 redis key
+     */
+    public static final String LOGIN_TOKEN_KEY = "login_tokens:";
+
+    /**
+     * 防重提交 redis key
+     */
+    public static final String REPEAT_SUBMIT_KEY = "repeat_submit:";
+
+    /**
+     * 限流 redis key
+     */
+    public static final String RATE_LIMIT_KEY = "rate_limit:";
+
+    /**
+     * 验证码有效期(分钟)
+     */
+    public static final Integer CAPTCHA_EXPIRATION = 2;
+
+    /**
+     * 令牌
+     */
+    public static final String TOKEN = "token";
+
+    /**
+     * 令牌前缀
+     */
+    public static final String TOKEN_PREFIX = "Bearer ";
+
+    /**
+     * 令牌前缀
+     */
+    public static final String LOGIN_USER_KEY = "login_user_key";
+
+    /**
+     * 用户ID
+     */
+    public static final String JWT_USERID = "userid";
+
+    /**
+     * 用户头像
+     */
+    public static final String JWT_AVATAR = "avatar";
+
+    /**
+     * 创建时间
+     */
+    public static final String JWT_CREATED = "created";
+
+    /**
+     * 用户权限
+     */
+    public static final String JWT_AUTHORITIES = "authorities";
+
+    /**
+     * 参数管理 cache key
+     */
+    public static final String SYS_CONFIG_KEY = "sys_config:";
+
+    /**
+     * 字典管理 cache key
+     */
+    public static final String SYS_DICT_KEY = "sys_dict:";
+    /**
+     * 实验室机构缓存key
+     */
+    public static final String SYNC_INST = "sync_inst:";
+
+    /**
+     * 接受数据统计key
+     */
+    public static final String SYNC_FILE_VOLUME = "sync_file_volume:";
+    /**
+     * 资源映射路径 前缀
+     */
+    public static final String RESOURCE_PREFIX = "/profile";
+
+    /**
+     * RMI 远程方法调用
+     */
+    public static final String LOOKUP_RMI = "rmi://";
+
+    /**
+     * LDAP 远程方法调用
+     */
+    public static final String LOOKUP_LDAP = "ldap://";
+
+    public static final String CREATE_BY_NODE = "sqlcreatebynode";
+}

+ 1005 - 0
src/main/java/com/mingxue/spider/helper/Convert.java

@@ -0,0 +1,1005 @@
+package com.mingxue.spider.helper;
+
+import org.apache.commons.lang3.ArrayUtils;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.text.NumberFormat;
+import java.util.Set;
+
+/**
+ * 类型转换器
+ *
+ * @author ruoyi
+ */
+public class Convert
+{
+    /**
+     * 转换为字符串<br>
+     * 如果给定的值为null,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static String toStr(Object value, String defaultValue)
+    {
+        if (null == value)
+        {
+            return defaultValue;
+        }
+        if (value instanceof String)
+        {
+            return (String) value;
+        }
+        return value.toString();
+    }
+
+    /**
+     * 转换为字符串<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static String toStr(Object value)
+    {
+        return toStr(value, null);
+    }
+
+    /**
+     * 转换为字符<br>
+     * 如果给定的值为null,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Character toChar(Object value, Character defaultValue)
+    {
+        if (null == value)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Character)
+        {
+            return (Character) value;
+        }
+
+        final String valueStr = toStr(value, null);
+        return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0);
+    }
+
+    /**
+     * 转换为字符<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Character toChar(Object value)
+    {
+        return toChar(value, null);
+    }
+
+    /**
+     * 转换为byte<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Byte toByte(Object value, Byte defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Byte)
+        {
+            return (Byte) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).byteValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return Byte.parseByte(valueStr);
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为byte<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Byte toByte(Object value)
+    {
+        return toByte(value, null);
+    }
+
+    /**
+     * 转换为Short<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Short toShort(Object value, Short defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Short)
+        {
+            return (Short) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).shortValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return Short.parseShort(valueStr.trim());
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为Short<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Short toShort(Object value)
+    {
+        return toShort(value, null);
+    }
+
+    /**
+     * 转换为Number<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Number toNumber(Object value, Number defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Number)
+        {
+            return (Number) value;
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return NumberFormat.getInstance().parse(valueStr);
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为Number<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Number toNumber(Object value)
+    {
+        return toNumber(value, null);
+    }
+
+    /**
+     * 转换为int<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Integer toInt(Object value, Integer defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Integer)
+        {
+            return (Integer) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).intValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return Integer.parseInt(valueStr.trim());
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为int<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Integer toInt(Object value)
+    {
+        return toInt(value, null);
+    }
+
+    /**
+     * 转换为Integer数组<br>
+     *
+     * @param str 被转换的值
+     * @return 结果
+     */
+    public static Integer[] toIntArray(String str)
+    {
+        return toIntArray(",", str);
+    }
+
+    /**
+     * 转换为Long数组<br>
+     *
+     * @param str 被转换的值
+     * @return 结果
+     */
+    public static Long[] toLongArray(String str)
+    {
+        return toLongArray(",", str);
+    }
+
+    /**
+     * 转换为Integer数组<br>
+     *
+     * @param split 分隔符
+     * @param split 被转换的值
+     * @return 结果
+     */
+    public static Integer[] toIntArray(String split, String str)
+    {
+        if (StringUtils.isEmpty(str))
+        {
+            return new Integer[] {};
+        }
+        String[] arr = str.split(split);
+        final Integer[] ints = new Integer[arr.length];
+        for (int i = 0; i < arr.length; i++)
+        {
+            final Integer v = toInt(arr[i], 0);
+            ints[i] = v;
+        }
+        return ints;
+    }
+
+    /**
+     * 转换为Long数组<br>
+     *
+     * @param split 分隔符
+     * @param str 被转换的值
+     * @return 结果
+     */
+    public static Long[] toLongArray(String split, String str)
+    {
+        if (StringUtils.isEmpty(str))
+        {
+            return new Long[] {};
+        }
+        String[] arr = str.split(split);
+        final Long[] longs = new Long[arr.length];
+        for (int i = 0; i < arr.length; i++)
+        {
+            final Long v = toLong(arr[i], null);
+            longs[i] = v;
+        }
+        return longs;
+    }
+
+    /**
+     * 转换为String数组<br>
+     *
+     * @param str 被转换的值
+     * @return 结果
+     */
+    public static String[] toStrArray(String str)
+    {
+        return toStrArray(",", str);
+    }
+
+    /**
+     * 转换为String数组<br>
+     *
+     * @param split 分隔符
+     * @param split 被转换的值
+     * @return 结果
+     */
+    public static String[] toStrArray(String split, String str)
+    {
+        return str.split(split);
+    }
+
+    /**
+     * 转换为long<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Long toLong(Object value, Long defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Long)
+        {
+            return (Long) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).longValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            // 支持科学计数法
+            return new BigDecimal(valueStr.trim()).longValue();
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为long<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Long toLong(Object value)
+    {
+        return toLong(value, null);
+    }
+
+    /**
+     * 转换为double<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Double toDouble(Object value, Double defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Double)
+        {
+            return (Double) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).doubleValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            // 支持科学计数法
+            return new BigDecimal(valueStr.trim()).doubleValue();
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为double<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Double toDouble(Object value)
+    {
+        return toDouble(value, null);
+    }
+
+    /**
+     * 转换为Float<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Float toFloat(Object value, Float defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Float)
+        {
+            return (Float) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).floatValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return Float.parseFloat(valueStr.trim());
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为Float<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Float toFloat(Object value)
+    {
+        return toFloat(value, null);
+    }
+
+    /**
+     * 转换为boolean<br>
+     * String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Boolean toBool(Object value, Boolean defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Boolean)
+        {
+            return (Boolean) value;
+        }
+        String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        valueStr = valueStr.trim().toLowerCase();
+        switch (valueStr)
+        {
+            case "true":
+                return true;
+            case "false":
+                return false;
+            case "yes":
+                return true;
+            case "ok":
+                return true;
+            case "no":
+                return false;
+            case "1":
+                return true;
+            case "0":
+                return false;
+            default:
+                return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为boolean<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Boolean toBool(Object value)
+    {
+        return toBool(value, null);
+    }
+
+    /**
+     * 转换为Enum对象<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     *
+     * @param clazz Enum的Class
+     * @param value 值
+     * @param defaultValue 默认值
+     * @return Enum
+     */
+    public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value, E defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (clazz.isAssignableFrom(value.getClass()))
+        {
+            @SuppressWarnings("unchecked")
+            E myE = (E) value;
+            return myE;
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return Enum.valueOf(clazz, valueStr);
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为Enum对象<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     *
+     * @param clazz Enum的Class
+     * @param value 值
+     * @return Enum
+     */
+    public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value)
+    {
+        return toEnum(clazz, value, null);
+    }
+
+    /**
+     * 转换为BigInteger<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static BigInteger toBigInteger(Object value, BigInteger defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof BigInteger)
+        {
+            return (BigInteger) value;
+        }
+        if (value instanceof Long)
+        {
+            return BigInteger.valueOf((Long) value);
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return new BigInteger(valueStr);
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为BigInteger<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static BigInteger toBigInteger(Object value)
+    {
+        return toBigInteger(value, null);
+    }
+
+    /**
+     * 转换为BigDecimal<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof BigDecimal)
+        {
+            return (BigDecimal) value;
+        }
+        if (value instanceof Long)
+        {
+            return new BigDecimal((Long) value);
+        }
+        if (value instanceof Double)
+        {
+            return new BigDecimal((Double) value);
+        }
+        if (value instanceof Integer)
+        {
+            return new BigDecimal((Integer) value);
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return new BigDecimal(valueStr);
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为BigDecimal<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     *
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static BigDecimal toBigDecimal(Object value)
+    {
+        return toBigDecimal(value, null);
+    }
+
+    /**
+     * 将对象转为字符串<br>
+     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
+     *
+     * @param obj 对象
+     * @return 字符串
+     */
+    public static String utf8Str(Object obj)
+    {
+        return str(obj, CharsetKit.CHARSET_UTF_8);
+    }
+
+    /**
+     * 将对象转为字符串<br>
+     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
+     *
+     * @param obj 对象
+     * @param charsetName 字符集
+     * @return 字符串
+     */
+    public static String str(Object obj, String charsetName)
+    {
+        return str(obj, Charset.forName(charsetName));
+    }
+
+    /**
+     * 将对象转为字符串<br>
+     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
+     *
+     * @param obj 对象
+     * @param charset 字符集
+     * @return 字符串
+     */
+    public static String str(Object obj, Charset charset)
+    {
+        if (null == obj)
+        {
+            return null;
+        }
+
+        if (obj instanceof String)
+        {
+            return (String) obj;
+        }
+        else if (obj instanceof byte[])
+        {
+            return str((byte[]) obj, charset);
+        }
+        else if (obj instanceof Byte[])
+        {
+            byte[] bytes = ArrayUtils.toPrimitive((Byte[]) obj);
+            return str(bytes, charset);
+        }
+        else if (obj instanceof ByteBuffer)
+        {
+            return str((ByteBuffer) obj, charset);
+        }
+        return obj.toString();
+    }
+
+    /**
+     * 将byte数组转为字符串
+     *
+     * @param bytes byte数组
+     * @param charset 字符集
+     * @return 字符串
+     */
+    public static String str(byte[] bytes, String charset)
+    {
+        return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset));
+    }
+
+    /**
+     * 解码字节码
+     *
+     * @param data 字符串
+     * @param charset 字符集,如果此字段为空,则解码的结果取决于平台
+     * @return 解码后的字符串
+     */
+    public static String str(byte[] data, Charset charset)
+    {
+        if (data == null)
+        {
+            return null;
+        }
+
+        if (null == charset)
+        {
+            return new String(data);
+        }
+        return new String(data, charset);
+    }
+
+    /**
+     * 将编码的byteBuffer数据转换为字符串
+     *
+     * @param data 数据
+     * @param charset 字符集,如果为空使用当前系统字符集
+     * @return 字符串
+     */
+    public static String str(ByteBuffer data, String charset)
+    {
+        if (data == null)
+        {
+            return null;
+        }
+
+        return str(data, Charset.forName(charset));
+    }
+
+    /**
+     * 将编码的byteBuffer数据转换为字符串
+     *
+     * @param data 数据
+     * @param charset 字符集,如果为空使用当前系统字符集
+     * @return 字符串
+     */
+    public static String str(ByteBuffer data, Charset charset)
+    {
+        if (null == charset)
+        {
+            charset = Charset.defaultCharset();
+        }
+        return charset.decode(data).toString();
+    }
+
+    // ----------------------------------------------------------------------- 全角半角转换
+    /**
+     * 半角转全角
+     *
+     * @param input String.
+     * @return 全角字符串.
+     */
+    public static String toSBC(String input)
+    {
+        return toSBC(input, null);
+    }
+
+    /**
+     * 半角转全角
+     *
+     * @param input String
+     * @param notConvertSet 不替换的字符集合
+     * @return 全角字符串.
+     */
+    public static String toSBC(String input, Set<Character> notConvertSet)
+    {
+        char c[] = input.toCharArray();
+        for (int i = 0; i < c.length; i++)
+        {
+            if (null != notConvertSet && notConvertSet.contains(c[i]))
+            {
+                // 跳过不替换的字符
+                continue;
+            }
+
+            if (c[i] == ' ')
+            {
+                c[i] = '\u3000';
+            }
+            else if (c[i] < '\177')
+            {
+                c[i] = (char) (c[i] + 65248);
+
+            }
+        }
+        return new String(c);
+    }
+
+    /**
+     * 全角转半角
+     *
+     * @param input String.
+     * @return 半角字符串
+     */
+    public static String toDBC(String input)
+    {
+        return toDBC(input, null);
+    }
+
+    /**
+     * 替换全角为半角
+     *
+     * @param text 文本
+     * @param notConvertSet 不替换的字符集合
+     * @return 替换后的字符
+     */
+    public static String toDBC(String text, Set<Character> notConvertSet)
+    {
+        char c[] = text.toCharArray();
+        for (int i = 0; i < c.length; i++)
+        {
+            if (null != notConvertSet && notConvertSet.contains(c[i]))
+            {
+                // 跳过不替换的字符
+                continue;
+            }
+
+            if (c[i] == '\u3000')
+            {
+                c[i] = ' ';
+            }
+            else if (c[i] > '\uFF00' && c[i] < '\uFF5F')
+            {
+                c[i] = (char) (c[i] - 65248);
+            }
+        }
+        String returnString = new String(c);
+
+        return returnString;
+    }
+
+    /**
+     * 数字金额大写转换 先写个完整的然后将如零拾替换成零
+     *
+     * @param n 数字
+     * @return 中文大写数字
+     */
+    public static String digitUppercase(double n)
+    {
+        String[] fraction = { "角", "分" };
+        String[] digit = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" };
+        String[][] unit = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } };
+
+        String head = n < 0 ? "负" : "";
+        n = Math.abs(n);
+
+        String s = "";
+        for (int i = 0; i < fraction.length; i++)
+        {
+            s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", "");
+        }
+        if (s.length() < 1)
+        {
+            s = "整";
+        }
+        int integerPart = (int) Math.floor(n);
+
+        for (int i = 0; i < unit[0].length && integerPart > 0; i++)
+        {
+            String p = "";
+            for (int j = 0; j < unit[1].length && n > 0; j++)
+            {
+                p = digit[integerPart % 10] + unit[1][j] + p;
+                integerPart = integerPart / 10;
+            }
+            s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s;
+        }
+        return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整");
+    }
+}

+ 89 - 0
src/main/java/com/mingxue/spider/helper/HttpStatus.java

@@ -0,0 +1,89 @@
+package com.mingxue.spider.helper;
+
+/**
+ * 返回状态码
+ * 
+ * @author ruoyi
+ */
+public class HttpStatus
+{
+    /**
+     * 操作成功
+     */
+    public static final int SUCCESS = 200;
+
+    /**
+     * 对象创建成功
+     */
+    public static final int CREATED = 201;
+
+    /**
+     * 请求已经被接受
+     */
+    public static final int ACCEPTED = 202;
+
+    /**
+     * 操作已经执行成功,但是没有返回数据
+     */
+    public static final int NO_CONTENT = 204;
+
+    /**
+     * 资源已被移除
+     */
+    public static final int MOVED_PERM = 301;
+
+    /**
+     * 重定向
+     */
+    public static final int SEE_OTHER = 303;
+
+    /**
+     * 资源没有被修改
+     */
+    public static final int NOT_MODIFIED = 304;
+
+    /**
+     * 参数列表错误(缺少,格式不匹配)
+     */
+    public static final int BAD_REQUEST = 400;
+
+    /**
+     * 未授权
+     */
+    public static final int UNAUTHORIZED = 401;
+
+    /**
+     * 访问受限,授权过期
+     */
+    public static final int FORBIDDEN = 403;
+
+    /**
+     * 资源,服务未找到
+     */
+    public static final int NOT_FOUND = 404;
+
+    /**
+     * 不允许的http方法
+     */
+    public static final int BAD_METHOD = 405;
+
+    /**
+     * 资源冲突,或者资源被锁
+     */
+    public static final int CONFLICT = 409;
+
+    /**
+     * 不支持的数据,媒体类型
+     */
+    public static final int UNSUPPORTED_TYPE = 415;
+
+    /**
+     * 系统内部错误
+     */
+    public static final int ERROR = 500;
+
+    /**
+     * 接口未实现
+     */
+    public static final int NOT_IMPLEMENTED = 501;
+}

+ 90 - 0
src/main/java/com/mingxue/spider/helper/StrFormatter.java

@@ -0,0 +1,90 @@
+package com.mingxue.spider.helper;
+
+/**
+ * 字符串格式化
+ * 
+ * @author ruoyi
+ */
+public class StrFormatter
+{
+    public static final String EMPTY_JSON = "{}";
+    public static final char C_BACKSLASH = '\\';
+    public static final char C_DELIM_START = '{';
+    public static final char C_DELIM_END = '}';
+
+    /**
+     * 格式化字符串<br>
+     * 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
+     * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
+     * 例:<br>
+     * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
+     * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
+     * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
+     * 
+     * @param strPattern 字符串模板
+     * @param argArray 参数列表
+     * @return 结果
+     */
+    public static String format(final String strPattern, final Object... argArray)
+    {
+        if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray))
+        {
+            return strPattern;
+        }
+        final int strPatternLength = strPattern.length();
+
+        // 初始化定义好的长度以获得更好的性能
+        StringBuilder sbuf = new StringBuilder(strPatternLength + 50);
+
+        int handledPosition = 0;
+        int delimIndex;// 占位符所在位置
+        for (int argIndex = 0; argIndex < argArray.length; argIndex++)
+        {
+            delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition);
+            if (delimIndex == -1)
+            {
+                if (handledPosition == 0)
+                {
+                    return strPattern;
+                }
+                else
+                { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果
+                    sbuf.append(strPattern, handledPosition, strPatternLength);
+                    return sbuf.toString();
+                }
+            }
+            else
+            {
+                if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH)
+                {
+                    if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH)
+                    {
+                        // 转义符之前还有一个转义符,占位符依旧有效
+                        sbuf.append(strPattern, handledPosition, delimIndex - 1);
+                        sbuf.append(Convert.utf8Str(argArray[argIndex]));
+                        handledPosition = delimIndex + 2;
+                    }
+                    else
+                    {
+                        // 占位符被转义
+                        argIndex--;
+                        sbuf.append(strPattern, handledPosition, delimIndex - 1);
+                        sbuf.append(C_DELIM_START);
+                        handledPosition = delimIndex + 1;
+                    }
+                }
+                else
+                {
+                    // 正常占位符
+                    sbuf.append(strPattern, handledPosition, delimIndex);
+                    sbuf.append(Convert.utf8Str(argArray[argIndex]));
+                    handledPosition = delimIndex + 2;
+                }
+            }
+        }
+        // 加入最后一个占位符后所有的字符
+        sbuf.append(strPattern, handledPosition, strPattern.length());
+
+        return sbuf.toString();
+    }
+}

+ 573 - 0
src/main/java/com/mingxue/spider/helper/StringUtils.java

@@ -0,0 +1,573 @@
+package com.mingxue.spider.helper;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.springframework.util.AntPathMatcher;
+
+/**
+ * 字符串工具类
+ * 
+ * @author ruoyi
+ */
+public class StringUtils extends org.apache.commons.lang3.StringUtils
+{
+    /** 空字符串 */
+    private static final String NULLSTR = "";
+
+    /** 下划线 */
+    private static final char SEPARATOR = '_';
+
+    /**
+     * 获取参数不为空值
+     * 
+     * @param value defaultValue 要判断的value
+     * @return value 返回值
+     */
+    public static <T> T nvl(T value, T defaultValue)
+    {
+        return value != null ? value : defaultValue;
+    }
+
+    /**
+     * * 判断一个Collection是否为空, 包含List,Set,Queue
+     * 
+     * @param coll 要判断的Collection
+     * @return true:为空 false:非空
+     */
+    public static boolean isEmpty(Collection<?> coll)
+    {
+        return isNull(coll) || coll.isEmpty();
+    }
+
+    /**
+     * * 判断一个Collection是否非空,包含List,Set,Queue
+     * 
+     * @param coll 要判断的Collection
+     * @return true:非空 false:空
+     */
+    public static boolean isNotEmpty(Collection<?> coll)
+    {
+        return !isEmpty(coll);
+    }
+
+    /**
+     * * 判断一个对象数组是否为空
+     * 
+     * @param objects 要判断的对象数组
+     ** @return true:为空 false:非空
+     */
+    public static boolean isEmpty(Object[] objects)
+    {
+        return isNull(objects) || (objects.length == 0);
+    }
+
+    /**
+     * * 判断一个对象数组是否非空
+     * 
+     * @param objects 要判断的对象数组
+     * @return true:非空 false:空
+     */
+    public static boolean isNotEmpty(Object[] objects)
+    {
+        return !isEmpty(objects);
+    }
+
+    /**
+     * * 判断一个Map是否为空
+     * 
+     * @param map 要判断的Map
+     * @return true:为空 false:非空
+     */
+    public static boolean isEmpty(Map<?, ?> map)
+    {
+        return isNull(map) || map.isEmpty();
+    }
+
+    /**
+     * * 判断一个Map是否为空
+     * 
+     * @param map 要判断的Map
+     * @return true:非空 false:空
+     */
+    public static boolean isNotEmpty(Map<?, ?> map)
+    {
+        return !isEmpty(map);
+    }
+
+    /**
+     * * 判断一个字符串是否为空串
+     * 
+     * @param str String
+     * @return true:为空 false:非空
+     */
+    public static boolean isEmpty(String str)
+    {
+        return isNull(str) || NULLSTR.equals(str.trim());
+    }
+
+    /**
+     * * 判断一个字符串是否为非空串
+     * 
+     * @param str String
+     * @return true:非空串 false:空串
+     */
+    public static boolean isNotEmpty(String str)
+    {
+        return !isEmpty(str);
+    }
+
+    /**
+     * * 判断一个对象是否为空
+     * 
+     * @param object Object
+     * @return true:为空 false:非空
+     */
+    public static boolean isNull(Object object)
+    {
+        return object == null;
+    }
+
+    /**
+     * * 判断一个对象是否非空
+     * 
+     * @param object Object
+     * @return true:非空 false:空
+     */
+    public static boolean isNotNull(Object object)
+    {
+        return !isNull(object);
+    }
+
+    /**
+     * * 判断一个对象是否是数组类型(Java基本型别的数组)
+     * 
+     * @param object 对象
+     * @return true:是数组 false:不是数组
+     */
+    public static boolean isArray(Object object)
+    {
+        return isNotNull(object) && object.getClass().isArray();
+    }
+
+    /**
+     * 去空格
+     */
+    public static String trim(String str)
+    {
+        return (str == null ? "" : str.trim());
+    }
+
+    /**
+     * 截取字符串
+     * 
+     * @param str 字符串
+     * @param start 开始
+     * @return 结果
+     */
+    public static String substring(final String str, int start)
+    {
+        if (str == null)
+        {
+            return NULLSTR;
+        }
+
+        if (start < 0)
+        {
+            start = str.length() + start;
+        }
+
+        if (start < 0)
+        {
+            start = 0;
+        }
+        if (start > str.length())
+        {
+            return NULLSTR;
+        }
+
+        return str.substring(start);
+    }
+
+    /**
+     * 截取字符串
+     * 
+     * @param str 字符串
+     * @param start 开始
+     * @param end 结束
+     * @return 结果
+     */
+    public static String substring(final String str, int start, int end)
+    {
+        if (str == null)
+        {
+            return NULLSTR;
+        }
+
+        if (end < 0)
+        {
+            end = str.length() + end;
+        }
+        if (start < 0)
+        {
+            start = str.length() + start;
+        }
+
+        if (end > str.length())
+        {
+            end = str.length();
+        }
+
+        if (start > end)
+        {
+            return NULLSTR;
+        }
+
+        if (start < 0)
+        {
+            start = 0;
+        }
+        if (end < 0)
+        {
+            end = 0;
+        }
+
+        return str.substring(start, end);
+    }
+
+    /**
+     * 格式化文本, {} 表示占位符<br>
+     * 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
+     * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
+     * 例:<br>
+     * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
+     * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
+     * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
+     * 
+     * @param template 文本模板,被替换的部分用 {} 表示
+     * @param params 参数值
+     * @return 格式化后的文本
+     */
+    public static String format(String template, Object... params)
+    {
+        if (isEmpty(params) || isEmpty(template))
+        {
+            return template;
+        }
+        return StrFormatter.format(template, params);
+    }
+
+    /**
+     * 是否为http(s)://开头
+     * 
+     * @param link 链接
+     * @return 结果
+     */
+    public static boolean ishttp(String link)
+    {
+        return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS);
+    }
+
+    /**
+     * 字符串转set
+     * 
+     * @param str 字符串
+     * @param sep 分隔符
+     * @return set集合
+     */
+    public static final Set<String> str2Set(String str, String sep)
+    {
+        return new HashSet<String>(str2List(str, sep, true, false));
+    }
+
+    /**
+     * 字符串转list
+     * 
+     * @param str 字符串
+     * @param sep 分隔符
+     * @param filterBlank 过滤纯空白
+     * @param trim 去掉首尾空白
+     * @return list集合
+     */
+    public static final List<String> str2List(String str, String sep, boolean filterBlank, boolean trim)
+    {
+        List<String> list = new ArrayList<String>();
+        if (StringUtils.isEmpty(str))
+        {
+            return list;
+        }
+
+        // 过滤空白字符串
+        if (filterBlank && StringUtils.isBlank(str))
+        {
+            return list;
+        }
+        String[] split = str.split(sep);
+        for (String string : split)
+        {
+            if (filterBlank && StringUtils.isBlank(string))
+            {
+                continue;
+            }
+            if (trim)
+            {
+                string = string.trim();
+            }
+            list.add(string);
+        }
+
+        return list;
+    }
+
+    /**
+     * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
+     *
+     * @param cs 指定字符串
+     * @param searchCharSequences 需要检查的字符串数组
+     * @return 是否包含任意一个字符串
+     */
+    public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences)
+    {
+        if (isEmpty(cs) || isEmpty(searchCharSequences))
+        {
+            return false;
+        }
+        for (CharSequence testStr : searchCharSequences)
+        {
+            if (containsIgnoreCase(cs, testStr))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 驼峰转下划线命名
+     */
+    public static String toUnderScoreCase(String str)
+    {
+        if (str == null)
+        {
+            return null;
+        }
+        StringBuilder sb = new StringBuilder();
+        // 前置字符是否大写
+        boolean preCharIsUpperCase = true;
+        // 当前字符是否大写
+        boolean curreCharIsUpperCase = true;
+        // 下一字符是否大写
+        boolean nexteCharIsUpperCase = true;
+        for (int i = 0; i < str.length(); i++)
+        {
+            char c = str.charAt(i);
+            if (i > 0)
+            {
+                preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
+            }
+            else
+            {
+                preCharIsUpperCase = false;
+            }
+
+            curreCharIsUpperCase = Character.isUpperCase(c);
+
+            if (i < (str.length() - 1))
+            {
+                nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
+            }
+
+            if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase)
+            {
+                sb.append(SEPARATOR);
+            }
+            else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase)
+            {
+                sb.append(SEPARATOR);
+            }
+            sb.append(Character.toLowerCase(c));
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * 是否包含字符串
+     * 
+     * @param str 验证字符串
+     * @param strs 字符串组
+     * @return 包含返回true
+     */
+    public static boolean inStringIgnoreCase(String str, String... strs)
+    {
+        if (str != null && strs != null)
+        {
+            for (String s : strs)
+            {
+                if (str.equalsIgnoreCase(trim(s)))
+                {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld
+     * 
+     * @param name 转换前的下划线大写方式命名的字符串
+     * @return 转换后的驼峰式命名的字符串
+     */
+    public static String convertToCamelCase(String name)
+    {
+        StringBuilder result = new StringBuilder();
+        // 快速检查
+        if (name == null || name.isEmpty())
+        {
+            // 没必要转换
+            return "";
+        }
+        else if (!name.contains("_"))
+        {
+            // 不含下划线,仅将首字母大写
+            return name.substring(0, 1).toUpperCase() + name.substring(1);
+        }
+        // 用下划线将原始字符串分割
+        String[] camels = name.split("_");
+        for (String camel : camels)
+        {
+            // 跳过原始字符串中开头、结尾的下换线或双重下划线
+            if (camel.isEmpty())
+            {
+                continue;
+            }
+            // 首字母大写
+            result.append(camel.substring(0, 1).toUpperCase());
+            result.append(camel.substring(1).toLowerCase());
+        }
+        return result.toString();
+    }
+
+    /**
+     * 驼峰式命名法 例如:user_name->userName
+     */
+    public static String toCamelCase(String s)
+    {
+        if (s == null)
+        {
+            return null;
+        }
+        s = s.toLowerCase();
+        StringBuilder sb = new StringBuilder(s.length());
+        boolean upperCase = false;
+        for (int i = 0; i < s.length(); i++)
+        {
+            char c = s.charAt(i);
+
+            if (c == SEPARATOR)
+            {
+                upperCase = true;
+            }
+            else if (upperCase)
+            {
+                sb.append(Character.toUpperCase(c));
+                upperCase = false;
+            }
+            else
+            {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
+     * 
+     * @param str 指定字符串
+     * @param strs 需要检查的字符串数组
+     * @return 是否匹配
+     */
+    public static boolean matches(String str, List<String> strs)
+    {
+        if (isEmpty(str) || isEmpty(strs))
+        {
+            return false;
+        }
+        for (String pattern : strs)
+        {
+            if (isMatch(pattern, str))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 判断url是否与规则配置: 
+     * ? 表示单个字符; 
+     * * 表示一层路径内的任意字符串,不可跨层级; 
+     * ** 表示任意层路径;
+     * 
+     * @param pattern 匹配规则
+     * @param url 需要匹配的url
+     * @return
+     */
+    public static boolean isMatch(String pattern, String url)
+    {
+        AntPathMatcher matcher = new AntPathMatcher();
+        return matcher.match(pattern, url);
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T> T cast(Object obj)
+    {
+        return (T) obj;
+    }
+
+    public static int computeDisplayLen(String s) {
+        int len = 0;
+        if ( s == null ) {
+            return len;
+        }
+
+        for(int i = 0; i < s.length(); ++i)
+        {
+            char c = s.charAt(i);
+            if(c >= '\u0000' && c <= '\u00FF') {
+                len = len + 1;
+            } else {
+                len = len + 2;
+            }
+        }
+        return len;
+    }
+    
+    /**
+     * 字节转换
+     * 
+     * @param size 字节大小
+     * @return 转换后值
+     */
+    public static String convertFileSize(long size) {
+        long kb = 1024;
+        long mb = kb * 1024;
+        long gb = mb * 1024;
+        if (size >= gb) {
+            return String.format("%.1f GB", (float)size / gb);
+        } else if (size >= mb) {
+            float f = (float)size / mb;
+            return String.format(f > 100 ? "%.0f MB" : "%.1f MB", f);
+        } else if (size >= kb) {
+            float f = (float)size / kb;
+            return String.format(f > 100 ? "%.0f KB" : "%.1f KB", f);
+        } else {
+            return String.format("%d B", size);
+        }
+    }
+}

+ 184 - 0
src/main/java/com/mingxue/spider/utils/ApiUtil.java

@@ -0,0 +1,184 @@
+package com.mingxue.spider.utils;
+
+import java.io.IOException;
+
+import com.mingxue.spider.helper.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.mingxue.spider.dto.User;
+
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.FormBody;
+import okhttp3.Interceptor;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+
+public class ApiUtil {
+    private static Logger log = LoggerFactory.getLogger(ApiUtil.class);
+    private static ApiUtil instance;
+    private OkHttpClient.Builder builder;
+    private Request.Builder requestBuilder;
+    private OkHttpClient okHttpClient;
+    private User user;
+
+    private ApiUtil() {
+        builder = new OkHttpClient.Builder();
+        okHttpClient = builder.addInterceptor(new RequestLoggerInterceptor()).addInterceptor(new ResponseLoggerInterceptor()).build();
+        requestBuilder = new Request.Builder();// 省的每次都new request操作,直接builder出来,随后需要什么往里加,build出来即可
+    }
+
+    public static ApiUtil getInstance() {
+        if (null == instance) {
+            synchronized (ApiUtil.class) {
+                if (null == instance) {
+                    instance = new ApiUtil();
+                }
+            }
+        }
+        return instance;
+    }
+
+    public User getUser() {
+        return user;
+    }
+
+    public void httpGet(String url, final Callback callback) {
+        Request request = requestBuilder.url(url).build();
+        okHttpClient.newCall(request).enqueue(callback);
+    }
+
+    private void httpGetHtml(String url) throws IOException {
+        Request request = requestBuilder.url(url).build();
+        okHttpClient.newCall(request).execute();
+    }
+
+    public JSONObject httpSyncGet(String url) throws IOException {
+        Request request = requestBuilder.url(url).build();
+        final Response res = okHttpClient.newCall(request).execute();
+        JSONObject obj = JSON.parseObject(res.body().string());
+        if (400 == obj.getIntValue("error")) {
+            this.login();
+            return httpSyncGet(url);
+        } else {
+            return obj;
+        }
+    }
+
+    public void httpPost(String urlString, FormBody formBody, Callback callback) {
+        Request request = requestBuilder.url(urlString).method("POST", formBody).build();
+        okHttpClient.newCall(request).enqueue(callback);
+    }
+
+    /**
+     * 第一高考网 http://www.diyigaokao.com/
+     * @param user
+     */
+    public void login(User user) {
+        try {
+            String url= StringUtils.EMPTY;
+            String host= StringUtils.EMPTY;
+            if(user.getIsHn()){
+                url = "http://jlzy.zljy33.com/";
+                host="jlzy.zljy33.com";
+            }else{
+                url = "http://www.diyigaokao.com/";
+                host="www.diyigaokao.com";
+            }
+            httpGetHtml(url);
+            this.user = user;
+            JSONObject res = httpSyncGet("https://apiv4.diyigaokao.com/index/dailiConfig?host="+host);
+            Integer dailiId = res.getJSONObject("data").getInteger("id");
+            StringBuilder sb = new StringBuilder();
+            sb.append("https://apiv4.diyigaokao.com/login?uuid=agentpc&platform=pc");
+            sb.append("&dailiId=").append(dailiId);
+            sb.append("&userName=").append(user.getCode()).append("&password=").append(user.getPassword());
+            res = httpSyncGet(sb.toString());
+            if (0 == res.getIntValue("error")) {
+                JSONObject data = res.getJSONObject("data");
+                Integer provinceId = data.getInteger("provinceId");
+                String token = data.getString("accessToken");
+                requestBuilder.addHeader("accessToken", token);
+                this.user.setProvinceId(provinceId).setToken(token);
+            } else {
+                System.out.println(res);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 登录系统:湖南 http://jlzy.zljy33.com/
+     * @return
+     */
+    public void login() {
+        login(UserUtil.next());
+    }
+
+    public void login(String code, String password,Boolean isHn) {
+        login(new User().setCode(code).setPassword(password).setIsHn(isHn));
+    }
+
+    /**
+     * 请求拦截器
+     */
+    static class RequestLoggerInterceptor implements Interceptor {
+
+        @Override
+        public Response intercept(Chain chain) throws IOException {
+            Request request = chain.request();
+            log.debug(this.getClass().getSimpleName(), "url    =  : " + request.url());
+            log.debug(this.getClass().getSimpleName(), "method =  : " + request.method());
+            log.debug(this.getClass().getSimpleName(), "headers=  : " + request.headers());
+            log.debug(this.getClass().getSimpleName(), "body   =  : " + request.body());
+            return chain.proceed(request);
+        }
+    }
+
+    /**
+     * 响应拦截器
+     */
+    static class ResponseLoggerInterceptor implements Interceptor {
+        @Override
+        public Response intercept(Chain chain) throws IOException {
+            Response response = chain.proceed(chain.request());
+            log.debug(this.getClass().getSimpleName(), "code    =  : " + response.code());
+            log.debug(this.getClass().getSimpleName(), "message =  : " + response.message());
+            log.debug(this.getClass().getSimpleName(), "protocol=  : " + response.protocol());
+            if (response.body() != null && response.body().contentType() != null) {
+                MediaType mediaType = response.body().contentType();
+                String string = response.body().string();
+                log.debug(this.getClass().getSimpleName(), "mediaType=  :  " + mediaType.toString());
+                log.debug(this.getClass().getSimpleName(), "string   =  : " + string);
+                ResponseBody responseBody = ResponseBody.create(mediaType, string);
+                return response.newBuilder().body(responseBody).build();
+            } else {
+                return response;
+            }
+        }
+    }
+
+    public static class CallbackAdapter implements Callback {
+
+        @Override
+        public void onFailure(Call call, IOException e) {
+
+        }
+
+        @Override
+        public void onResponse(Call call, Response res) throws IOException {
+            success(res);
+        }
+
+        public void success(Response res) {}
+
+    }
+
+}

+ 289 - 0
src/main/java/com/mingxue/spider/utils/HttpUtils.java

@@ -0,0 +1,289 @@
+package com.mingxue.spider.utils;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.ConnectException;
+import java.net.SocketTimeoutException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import com.mingxue.spider.helper.AjaxResult;
+import com.mingxue.spider.helper.Constants;
+import com.mingxue.spider.helper.StringUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.alibaba.fastjson.JSON;
+
+/**
+ * 通用http发送方法
+ * 
+ * @author ruoyi
+ */
+public class HttpUtils {
+    private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);
+
+    public static String postBody(String postUrl, String body,String uSign) {
+        try {
+            CloseableHttpClient httpClient = HttpClients.createDefault();
+            HttpPost httpPost = new HttpPost(postUrl);
+            httpPost.setEntity(new StringEntity(body, ContentType.APPLICATION_JSON));
+
+            if(StringUtils.isNotEmpty(uSign)){
+                httpPost.setHeader("u-sign", uSign);
+            }
+            httpPost.setHeader("Content-Type", "application/json");
+            CloseableHttpResponse response = httpClient.execute(httpPost);
+            String result = "";
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode == 200 && response != null) {
+                HttpEntity httpEntity = response.getEntity();
+
+                // 如果返回的内容不为空
+                if (httpEntity != null) {
+                    result = EntityUtils.toString(httpEntity);
+                }
+            }
+
+            return result;
+        } catch (Exception e) {
+            log.error("postUrl is {}",postUrl);
+            log.error(e.getMessage(), e);
+        }
+
+        return "";
+    }
+    public static String postBody(String postUrl, String body) {
+        return postBody(postUrl,body,null);
+    }
+
+    /**
+     * 
+     * @param url
+     * @param param
+     * @return
+     */
+    public static AjaxResult ajaxGet(String url, String param, Integer timeout) {
+        String json = sendGet(url, param, timeout);
+        return JSON.parseObject(json, AjaxResult.class);
+    }
+
+    public static AjaxResult ajaxGet(String url, String param) {
+        return ajaxGet(url, param, null);
+    }
+
+    /**
+     * 向指定 URL 发送GET方法的请求
+     *
+     * @param url 发送请求的 URL
+     * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
+     * @return 所代表远程资源的响应结果
+     */
+    public static String sendGet(String url, String param, Integer timeout) {
+        return sendGet(url, param, Constants.UTF8, timeout);
+    }
+
+    public static String sendGet(String url, String param) {
+        return sendGet(url, param, Constants.UTF8);
+    }
+
+    public static String sendGet(String url, String param, String contentType) {
+        return sendGet(url, param, contentType, null);
+    }
+
+    /**
+     * 向指定 URL 发送GET方法的请求
+     *
+     * @param url 发送请求的 URL
+     * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
+     * @param contentType 编码类型
+     * @return 所代表远程资源的响应结果
+     */
+    public static String sendGet(String url, String param, String contentType, Integer timeout) {
+        StringBuilder result = new StringBuilder();
+        BufferedReader in = null;
+        try {
+            String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url;
+            log.debug("sendGet - {}", urlNameString);
+            URL realUrl = new URL(urlNameString);
+            URLConnection connection = realUrl.openConnection();
+            if (null != timeout) {
+                connection.setConnectTimeout(timeout);
+                connection.setReadTimeout(timeout);
+            }
+            connection.setRequestProperty("accept", "*/*");
+            connection.setRequestProperty("connection", "Keep-Alive");
+            connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
+            connection.connect();
+            in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
+            String line;
+            while ((line = in.readLine()) != null) {
+                result.append(line);
+            }
+            log.debug("recv - {}", result);
+        } catch (ConnectException e) {
+            log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
+        } catch (SocketTimeoutException e) {
+            log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
+        } catch (IOException e) {
+            log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
+        } catch (Exception e) {
+            log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
+        } finally {
+            try {
+                if (in != null) {
+                    in.close();
+                }
+            } catch (Exception ex) {
+                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
+            }
+        }
+        return result.toString();
+    }
+
+    /**
+     * 向指定 URL 发送POST方法的请求
+     *
+     * @param url 发送请求的 URL
+     * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
+     * @return 所代表远程资源的响应结果
+     */
+    public static String sendPost(String url, String param) {
+        PrintWriter out = null;
+        BufferedReader in = null;
+        StringBuilder result = new StringBuilder();
+        try {
+            String urlNameString = url;
+            log.debug("sendPost - {}", urlNameString);
+            URL realUrl = new URL(urlNameString);
+            URLConnection conn = realUrl.openConnection();
+            conn.setRequestProperty("accept", "*/*");
+            conn.setRequestProperty("connection", "Keep-Alive");
+            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
+            conn.setRequestProperty("Accept-Charset", "utf-8");
+            conn.setRequestProperty("contentType", "utf-8");
+            conn.setDoOutput(true);
+            conn.setDoInput(true);
+            out = new PrintWriter(conn.getOutputStream());
+            out.print(param);
+            out.flush();
+            in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
+            String line;
+            while ((line = in.readLine()) != null) {
+                result.append(line);
+            }
+            log.debug("recv - {}", result);
+        } catch (ConnectException e) {
+            log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
+        } catch (SocketTimeoutException e) {
+            log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
+        } catch (IOException e) {
+            log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
+        } catch (Exception e) {
+            log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
+        } finally {
+            try {
+                if (out != null) {
+                    out.close();
+                }
+                if (in != null) {
+                    in.close();
+                }
+            } catch (IOException ex) {
+                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
+            }
+        }
+        return result.toString();
+    }
+
+    public static String sendSSLPost(String url, String param) {
+        StringBuilder result = new StringBuilder();
+        String urlNameString = url + "?" + param;
+        try {
+            log.debug("sendSSLPost - {}", urlNameString);
+            SSLContext sc = SSLContext.getInstance("SSL");
+            sc.init(null, new TrustManager[] {new TrustAnyTrustManager()}, new java.security.SecureRandom());
+            URL console = new URL(urlNameString);
+            HttpsURLConnection conn = (HttpsURLConnection)console.openConnection();
+            conn.setRequestProperty("accept", "*/*");
+            conn.setRequestProperty("connection", "Keep-Alive");
+            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
+            conn.setRequestProperty("Accept-Charset", "utf-8");
+            conn.setRequestProperty("contentType", "utf-8");
+            conn.setDoOutput(true);
+            conn.setDoInput(true);
+
+            conn.setSSLSocketFactory(sc.getSocketFactory());
+            conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
+            conn.connect();
+            InputStream is = conn.getInputStream();
+            BufferedReader br = new BufferedReader(new InputStreamReader(is));
+            String ret = "";
+            while ((ret = br.readLine()) != null) {
+                if (ret != null && !"".equals(ret.trim())) {
+                    result.append(new String(ret.getBytes("ISO-8859-1"), "utf-8"));
+                }
+            }
+            log.debug("recv - {}", result);
+            conn.disconnect();
+            br.close();
+        } catch (ConnectException e) {
+            log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e);
+        } catch (SocketTimeoutException e) {
+            log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e);
+        } catch (IOException e) {
+            log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e);
+        } catch (Exception e) {
+            log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e);
+        }
+        return result.toString();
+    }
+
+    private static class TrustAnyTrustManager implements X509TrustManager {
+        @Override
+        public void checkClientTrusted(X509Certificate[] chain, String authType) {}
+
+        @Override
+        public void checkServerTrusted(X509Certificate[] chain, String authType) {}
+
+        @Override
+        public X509Certificate[] getAcceptedIssuers() {
+            return new X509Certificate[] {};
+        }
+    }
+
+    private static class TrustAnyHostnameVerifier implements HostnameVerifier {
+        @Override
+        public boolean verify(String hostname, SSLSession session) {
+            return true;
+        }
+    }
+
+    public static Object ajaxGetData(String url, String param, Object def) {
+        try {
+            Object data = ajaxGet(url, param, null).get("data");
+            return null == data ? def : data;
+        } catch (Exception e) {
+        }
+        return def;
+    }
+}

+ 45 - 0
src/main/java/com/mingxue/spider/utils/UserUtil.java

@@ -0,0 +1,45 @@
+package com.mingxue.spider.utils;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.mingxue.spider.dto.User;
+
+import cn.hutool.core.text.csv.CsvData;
+import cn.hutool.core.text.csv.CsvUtil;
+
+public class UserUtil {
+
+    private static List<User> list = new ArrayList<>(12);
+    private static User current;
+    static {
+        File file = new File("./users.txt");
+        if (!file.exists()) {
+            file = new File(UserUtil.class.getResource("/users.txt").getFile());
+        }
+        if (file.exists()) {
+            CsvData data = CsvUtil.getReader().read(file);
+            data.forEach(row -> {
+                list.add(new User().setCode(row.get(0)).setPassword(row.get(1)));
+            });
+        }
+
+    }
+
+    /**
+     * isHn:供湖南使用
+     * @return
+     */
+    public static User next() {
+        User user = list.remove(0);
+        user.setIsHn(true);
+        current = user;
+        list.add(user);
+        return user;
+    }
+
+    public static User current() {
+        return current;
+    }
+}

+ 110 - 0
src/main/java/com/mingxue/spider/utils/UsignUtils.java

@@ -0,0 +1,110 @@
+package com.mingxue.spider.utils;
+
+import com.alibaba.fastjson.JSONObject;
+import com.mingxue.spider.helper.StringUtils;
+import jdk.nashorn.api.scripting.NashornScriptEngine;
+import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
+
+import java.io.FileReader;
+import java.util.HashMap;
+import java.util.Map;
+
+public class UsignUtils {
+    public static String getUsign(String jsParam,String jsonParam){
+        String[] options = new String[] {"--language=es6"};
+        NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
+
+        try {
+            NashornScriptEngine engine = getEngine();
+            JSONObject jsonObject = JSONObject.parseObject(jsonParam);
+
+            String aaaa= "u_sign('"+jsParam+"',"+jsonObject+")";
+
+            Object o = engine.eval("u_sign('"+jsParam+"',"+jsonParam+")");
+//            System.out.println("value : "+o);
+            return o.toString();
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    public static String getUsign(String jsParam){
+        String[] options = new String[] {"--language=es6"};
+        NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
+
+        try {
+            NashornScriptEngine engine = getEngine();
+
+            Object o = engine.eval("u_sign('"+jsParam+"')");
+//            System.out.println("value : "+o);
+            return o.toString();
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    public static NashornScriptEngine getEngine(){
+        /* 1.首先引入js引擎 */
+//		ScriptEngineManager manager = new ScriptEngineManager();
+        //引擎名称传入JavaScript、js、javascript、nashorn、Nashorn 均可等价
+//		ScriptEngine engine = manager.getEngineByName("nashorn");
+
+        String[] options = new String[] {"--language=es6"};
+        NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
+        NashornScriptEngine engine = (NashornScriptEngine) factory.getScriptEngine(options);
+
+        try {
+			/* 2.然后读取js文件 */
+            String path = Thread.currentThread().getContextClassLoader().getResource("").getPath(); // 获取targe路径
+
+//            String requestPath = "/youzy.dms.basiclib.api.career.job.hits.inc";
+//            String jobCode="02030502";
+//            params =requestPath+"?jobCode="+jobCode;
+
+            engine.eval(new FileReader(path + "js/usign.js"));
+
+            return engine;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    public static String getRequestPathAndParam(String requestPath,Map<String,Object> paramMap){
+        StringBuilder parameter= new StringBuilder();
+        int i=0;
+        for(Map.Entry<String,Object> entry:paramMap.entrySet()){
+            if(i==0){
+                parameter.append("?");
+            }else {
+                parameter.append("&");
+            }
+            parameter.append(entry.getKey()).append("=").append(entry.getValue());
+            i++;
+        }
+        String jsParam = requestPath+parameter.toString();
+        return jsParam;
+    }
+
+
+    public static void main(String[] args) {
+        String requestPath = "/youzy.dms.basiclib.api.career.job.hits.inc";
+        String jobCode = "02030502";
+        Map<String,Object> paramMap =new HashMap<>();
+        paramMap.put("jobCode",jobCode);
+        System.out.println(getUsign(getRequestPathAndParam(requestPath,paramMap)));
+
+        requestPath = "/youzy.dms.basiclib.api.major.tree.query";
+        String eduLevel = "ben";
+        paramMap =new HashMap<>();
+        paramMap.put("eduLevel",eduLevel);
+//        System.out.println(getUsign(getRequestPathAndParam(requestPath,paramMap)));
+
+        requestPath = "/youzy.dms.basiclib.api.major.tree.query";
+        String jsonParam="{\"count\":5,\"rankType\":1,\"eduLevel\":\"ben\"}";
+        paramMap =new HashMap<>();
+        System.out.println(getUsign(getRequestPathAndParam(requestPath,paramMap),jsonParam));
+    }
+}

+ 15 - 0
src/main/resources/db.setting

@@ -0,0 +1,15 @@
+## db.setting文件
+
+url = jdbc:mysql://121.4.203.192:3306/xuanke
+user = root
+pass = Root123.
+
+## 可选配置
+# 是否在日志中显示执行的SQL
+showSql = false
+# 是否格式化显示的SQL
+formatSql = false
+# 是否显示SQL参数
+showParams = false
+# 打印SQL的日志等级,默认debug,可以是info、warn、error
+sqlLevel = info

+ 307 - 0
src/main/resources/js/usign.js

@@ -0,0 +1,307 @@
+function Rc(n) {
+  return null == n ? "" : pu(n)
+}
+function pu(n) {
+  if ("string" == typeof n)
+    return n;
+  if (yh(n))
+    return c(n, pu) + "";
+  if (yc(n))
+    return ps ? ps.call(n) : "";
+  var t = n + "";
+  return "0" == t && 1 / n == -Rn ? "-0" : t
+}
+function V(n) {
+  return B(n) ? H(n) : p(n)
+}
+function B(n) {
+  return undefined;
+}
+function p(n) {
+  return n.split("")
+}
+function W(n, t) {
+  for (var r = n.length; r-- && y(t, n[r], 0) > -1;)
+    ;
+  return r
+}
+function y(n, t, r) {
+  return t === t ? q(n, t, r) : g(n, b, r)
+}
+function q(n, t, r) {
+  for (var e = r - 1, u = n.length; ++e < u;)
+    if (n[e] === t)
+      return e;
+  return -1
+}
+function Au(n, t, r) {
+  var e = n.length;
+  return r = r === undefined ? e : r,
+    !t && r >= e ? n : fu(n, t, r)
+}
+function fu(n, t, r) {
+  var e = -1
+    , u = n.length;
+  t < 0 && (t = -t > u ? 0 : u + t),
+    r = r > u ? u : r,
+    r < 0 && (r += u),
+    u = t > r ? 0 : r - t >>> 0,
+    t >>>= 0;
+  for (var i = Array(u); ++e < u;)
+    i[e] = n[e + t];
+  return i
+}
+function xa(n, t, r) {
+  if (n = Rc(n),
+    n && (r || t === undefined))
+    return n.replace(Wt, "");
+  if (!n || !(t = pu(t)))
+    return n;
+  var e = V(n);
+  return Au(e, 0, W(e, V(t)) + 1).join("")
+}
+
+
+
+
+
+
+const rr = {
+  '00d8': function (e, t) {
+    var t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+      , r = {
+        rotl: function (e, t) {
+          return e << t | e >>> 32 - t
+        },
+        rotr: function (e, t) {
+          return e << 32 - t | e >>> t
+        },
+        endian: function (e) {
+          if (e.constructor == Number)
+            return 16711935 & r.rotl(e, 8) | 4278255360 & r.rotl(e, 24);
+          for (var t = 0; t < e.length; t++)
+            e[t] = r.endian(e[t]);
+          return e
+        },
+        randomBytes: function (e) {
+          for (var t = []; e > 0; e--)
+            t.push(Math.floor(256 * Math.random()));
+          return t
+        },
+        bytesToWords: function (e) {
+          for (var t = [], r = 0, n = 0; r < e.length; r++,
+            n += 8)
+            t[n >>> 5] |= e[r] << 24 - n % 32;
+          return t
+        },
+        wordsToBytes: function (e) {
+          for (var t = [], r = 0; r < 32 * e.length; r += 8)
+            t.push(e[r >>> 5] >>> 24 - r % 32 & 255);
+          return t
+        },
+        bytesToHex: function (e) {
+          for (var t = [], r = 0; r < e.length; r++)
+            t.push((e[r] >>> 4).toString(16)),
+              t.push((15 & e[r]).toString(16));
+          return t.join("")
+        },
+        hexToBytes: function (e) {
+          for (var t = [], r = 0; r < e.length; r += 2)
+            t.push(parseInt(e.substr(r, 2), 16));
+          return t
+        },
+        bytesToBase64: function (e) {
+          for (var r = [], n = 0; n < e.length; n += 3)
+            for (var i = e[n] << 16 | e[n + 1] << 8 | e[n + 2], o = 0; o < 4; o++)
+              8 * n + 6 * o <= 8 * e.length ? r.push(t.charAt(i >>> 6 * (3 - o) & 63)) : r.push("=");
+          return r.join("")
+        },
+        base64ToBytes: function (e) {
+          e = e.replace(/[^A-Z0-9+\/]/gi, "");
+          for (var r = [], n = 0, i = 0; n < e.length; i = ++n % 4)
+            0 != i && r.push((t.indexOf(e.charAt(n - 1)) & Math.pow(2, -2 * i + 8) - 1) << 2 * i | t.indexOf(e.charAt(n)) >>> 6 - 2 * i);
+          return r
+        }
+      };
+    return r;
+  },
+  "9a63": function (e, t) {
+    var r = {
+      utf8: {
+        stringToBytes: function (e) {
+          return r.bin.stringToBytes(unescape(encodeURIComponent(e)))
+        },
+        bytesToString: function (e) {
+          return decodeURIComponent(escape(r.bin.bytesToString(e)))
+        }
+      },
+      bin: {
+        stringToBytes: function (e) {
+          for (var t = [], r = 0; r < e.length; r++)
+            t.push(255 & e.charCodeAt(r));
+          return t
+        },
+        bytesToString: function (e) {
+          for (var t = [], r = 0; r < e.length; r++)
+            t.push(String.fromCharCode(e[r]));
+          return t.join("")
+        }
+      }
+    };
+    return r
+  },
+  "8349": function (e, t) {
+    function r(e) {
+      return !!e.constructor && "function" == typeof e.constructor.isBuffer && e.constructor.isBuffer(e)
+    }
+    return function (e) {
+      return null != e && (r(e) || function (e) {
+        return "function" == typeof e.readFloatLE && "function" == typeof e.slice && r(e.slice(0, 0))
+      }(e) || !!e._isBuffer)
+    }
+  },
+}
+
+
+function a(e, r) {
+  var t = rr["00d8"]()
+    , n = rr["9a63"]().utf8
+    , i = rr["8349"]
+    , o = rr["9a63"]().bin
+  e.constructor == String ? e = r && "binary" === r.encoding ? o.stringToBytes(e) : n.stringToBytes(e) : i(e) ? e = Array.prototype.slice.call(e, 0) : Array.isArray(e) || e.constructor === Uint8Array || (e = e.toString());
+  for (var s = t.bytesToWords(e), u = 8 * e.length, c = 1732584193, f = -271733879, d = -1732584194, l = 271733878, h = 0; h < s.length; h++)
+    s[h] = 16711935 & (s[h] << 8 | s[h] >>> 24) | 4278255360 & (s[h] << 24 | s[h] >>> 8);
+  s[u >>> 5] |= 128 << u % 32,
+    s[14 + (u + 64 >>> 9 << 4)] = u;
+  var p = a._ff
+    , b = a._gg
+    , y = a._hh
+    , m = a._ii;
+  for (h = 0; h < s.length; h += 16) {
+    var g = c
+      , v = f
+      , _ = d
+      , w = l;
+    c = p(c, f, d, l, s[h + 0], 7, -680876936),
+      l = p(l, c, f, d, s[h + 1], 12, -389564586),
+      d = p(d, l, c, f, s[h + 2], 17, 606105819),
+      f = p(f, d, l, c, s[h + 3], 22, -1044525330),
+      c = p(c, f, d, l, s[h + 4], 7, -176418897),
+      l = p(l, c, f, d, s[h + 5], 12, 1200080426),
+      d = p(d, l, c, f, s[h + 6], 17, -1473231341),
+      f = p(f, d, l, c, s[h + 7], 22, -45705983),
+      c = p(c, f, d, l, s[h + 8], 7, 1770035416),
+      l = p(l, c, f, d, s[h + 9], 12, -1958414417),
+      d = p(d, l, c, f, s[h + 10], 17, -42063),
+      f = p(f, d, l, c, s[h + 11], 22, -1990404162),
+      c = p(c, f, d, l, s[h + 12], 7, 1804603682),
+      l = p(l, c, f, d, s[h + 13], 12, -40341101),
+      d = p(d, l, c, f, s[h + 14], 17, -1502002290),
+      c = b(c, f = p(f, d, l, c, s[h + 15], 22, 1236535329), d, l, s[h + 1], 5, -165796510),
+      l = b(l, c, f, d, s[h + 6], 9, -1069501632),
+      d = b(d, l, c, f, s[h + 11], 14, 643717713),
+      f = b(f, d, l, c, s[h + 0], 20, -373897302),
+      c = b(c, f, d, l, s[h + 5], 5, -701558691),
+      l = b(l, c, f, d, s[h + 10], 9, 38016083),
+      d = b(d, l, c, f, s[h + 15], 14, -660478335),
+      f = b(f, d, l, c, s[h + 4], 20, -405537848),
+      c = b(c, f, d, l, s[h + 9], 5, 568446438),
+      l = b(l, c, f, d, s[h + 14], 9, -1019803690),
+      d = b(d, l, c, f, s[h + 3], 14, -187363961),
+      f = b(f, d, l, c, s[h + 8], 20, 1163531501),
+      c = b(c, f, d, l, s[h + 13], 5, -1444681467),
+      l = b(l, c, f, d, s[h + 2], 9, -51403784),
+      d = b(d, l, c, f, s[h + 7], 14, 1735328473),
+      c = y(c, f = b(f, d, l, c, s[h + 12], 20, -1926607734), d, l, s[h + 5], 4, -378558),
+      l = y(l, c, f, d, s[h + 8], 11, -2022574463),
+      d = y(d, l, c, f, s[h + 11], 16, 1839030562),
+      f = y(f, d, l, c, s[h + 14], 23, -35309556),
+      c = y(c, f, d, l, s[h + 1], 4, -1530992060),
+      l = y(l, c, f, d, s[h + 4], 11, 1272893353),
+      d = y(d, l, c, f, s[h + 7], 16, -155497632),
+      f = y(f, d, l, c, s[h + 10], 23, -1094730640),
+      c = y(c, f, d, l, s[h + 13], 4, 681279174),
+      l = y(l, c, f, d, s[h + 0], 11, -358537222),
+      d = y(d, l, c, f, s[h + 3], 16, -722521979),
+      f = y(f, d, l, c, s[h + 6], 23, 76029189),
+      c = y(c, f, d, l, s[h + 9], 4, -640364487),
+      l = y(l, c, f, d, s[h + 12], 11, -421815835),
+      d = y(d, l, c, f, s[h + 15], 16, 530742520),
+      c = m(c, f = y(f, d, l, c, s[h + 2], 23, -995338651), d, l, s[h + 0], 6, -198630844),
+      l = m(l, c, f, d, s[h + 7], 10, 1126891415),
+      d = m(d, l, c, f, s[h + 14], 15, -1416354905),
+      f = m(f, d, l, c, s[h + 5], 21, -57434055),
+      c = m(c, f, d, l, s[h + 12], 6, 1700485571),
+      l = m(l, c, f, d, s[h + 3], 10, -1894986606),
+      d = m(d, l, c, f, s[h + 10], 15, -1051523),
+      f = m(f, d, l, c, s[h + 1], 21, -2054922799),
+      c = m(c, f, d, l, s[h + 8], 6, 1873313359),
+      l = m(l, c, f, d, s[h + 15], 10, -30611744),
+      d = m(d, l, c, f, s[h + 6], 15, -1560198380),
+      f = m(f, d, l, c, s[h + 13], 21, 1309151649),
+      c = m(c, f, d, l, s[h + 4], 6, -145523070),
+      l = m(l, c, f, d, s[h + 11], 10, -1120210379),
+      d = m(d, l, c, f, s[h + 2], 15, 718787259),
+      f = m(f, d, l, c, s[h + 9], 21, -343485551),
+      c = c + g >>> 0,
+      f = f + v >>> 0,
+      d = d + _ >>> 0,
+      l = l + w >>> 0
+  }
+  return t.endian([c, f, d, l])
+};
+
+
+a._ff = function (e, t, r, n, i, o, a) {
+  var s = e + (t & r | ~t & n) + (i >>> 0) + a;
+  return (s << o | s >>> 32 - o) + t
+}
+  ,
+  a._gg = function (e, t, r, n, i, o, a) {
+    var s = e + (t & n | r & ~n) + (i >>> 0) + a;
+    return (s << o | s >>> 32 - o) + t
+  }
+  ,
+  a._hh = function (e, t, r, n, i, o, a) {
+    var s = e + (t ^ r ^ n) + (i >>> 0) + a;
+    return (s << o | s >>> 32 - o) + t
+  }
+  ,
+  a._ii = function (e, t, r, n, i, o, a) {
+    var s = e + (r ^ (t | ~n)) + (i >>> 0) + a;
+    return (s << o | s >>> 32 - o) + t
+  }
+
+function n(e) {
+  var t = rr["00d8"]()
+    , n = rr["9a63"]().utf8
+    , i = rr["8349"]()
+    , o = rr["9a63"]().bin
+  if (null == e)
+    throw new Error("Illegal argument " + e);
+  var n = t.wordsToBytes(a(e));
+  return t.bytesToHex(n)
+}
+
+
+// 第一个参数是请求地址及参数,第二个参数暂时不用传
+function u_sign(e, t ) {
+  var r, o = "9SASji5OWnG41iRKiSvTJHlXHmRySRp1", a = "", s = t || {}, u = (e = e || "").split("?");
+  if (u.length > 0 && (r = u[1]),
+    r) {
+    var c = r.split("&")
+      , f = "";
+    c.forEach((function (e) {
+      var t = e.split("=");
+      f += "".concat(t[0], "=").concat(encodeURI(t[1]), "&")
+    }
+    )),
+      a = "".concat(xa(f, "&"), "&").concat(o)
+  } else
+    a = Object.keys(s).length > 0 ? "".concat(JSON.stringify(s), "&").concat(o) : "&".concat(o);
+  return a = a.toLowerCase(),
+    n(a)
+}
+
+// 示例:
+// u_sign('/youzy.dms.basiclib.api.career.job.hits.inc?jobCode=02030502')

+ 83 - 0
src/main/resources/logback.xml

@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <!-- 日志存放路径 -->
+	<property name="log.path" value="./logs" />
+    <!-- 日志输出格式 -->
+	<property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
+
+	<!-- 控制台输出 -->
+	<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+	</appender>
+	
+	<!-- 系统日志输出 -->
+	<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/sys-info.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+			<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
+			<!-- 日志最大的历史 60天 -->
+			<maxHistory>60</maxHistory>
+		</rollingPolicy>
+		<encoder>
+			<pattern>${log.pattern}</pattern>
+		</encoder>
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>INFO</level>
+            <!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+            <!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+	</appender>
+	
+	<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
+	    <file>${log.path}/sys-error.log</file>
+        <!-- 循环政策:基于时间创建日志文件 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 日志文件名格式 -->
+            <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
+			<!-- 日志最大的历史 60天 -->
+			<maxHistory>60</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!-- 过滤的级别 -->
+            <level>ERROR</level>
+			<!-- 匹配时的操作:接收(记录) -->
+            <onMatch>ACCEPT</onMatch>
+			<!-- 不匹配时的操作:拒绝(不记录) -->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+	
+	<!-- 用户访问日志输出  -->
+    <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
+		<file>${log.path}/sys-user.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 按天回滚 daily -->
+            <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 日志最大的历史 60天 -->
+            <maxHistory>60</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+    </appender>
+	
+	<!-- 系统模块日志级别控制  -->
+	<logger name="com.mingxue" level="info" />
+	<!-- Spring日志级别控制  -->
+	<logger name="org.springframework" level="warn" />
+
+	<root level="info">
+		<appender-ref ref="console" />
+	</root>
+	 
+</configuration> 

+ 305 - 0
src/main/resources/templates/js/index.js

@@ -0,0 +1,305 @@
+function Rc(n) {
+  return null == n ? "" : pu(n)
+}
+function pu(n) {
+  if ("string" == typeof n)
+    return n;
+  if (yh(n))
+    return c(n, pu) + "";
+  if (yc(n))
+    return ps ? ps.call(n) : "";
+  var t = n + "";
+  return "0" == t && 1 / n == -Rn ? "-0" : t
+}
+function V(n) {
+  return B(n) ? H(n) : p(n)
+}
+function B(n) {
+  return undefined;
+}
+function p(n) {
+  return n.split("")
+}
+function W(n, t) {
+  for (var r = n.length; r-- && y(t, n[r], 0) > -1; )
+      ;
+  return r
+}
+function y(n, t, r) {
+  return t === t ? q(n, t, r) : g(n, b, r)
+}
+function q(n, t, r) {
+  for (var e = r - 1, u = n.length; ++e < u; )
+      if (n[e] === t)
+          return e;
+  return -1
+}
+function Au(n, t, r) {
+  var e = n.length;
+  return r = r === undefined ? e : r,
+  !t && r >= e ? n : fu(n, t, r)
+}
+function fu(n, t, r) {
+  var e = -1
+    , u = n.length;
+  t < 0 && (t = -t > u ? 0 : u + t),
+  r = r > u ? u : r,
+  r < 0 && (r += u),
+  u = t > r ? 0 : r - t >>> 0,
+  t >>>= 0;
+  for (var i = Array(u); ++e < u; )
+      i[e] = n[e + t];
+  return i
+}
+function xa(n, t, r) {
+  if (n = Rc(n),
+    n && (r || t === undefined))
+    return n.replace(Wt, "");
+  if (!n || !(t = pu(t)))
+    return n;
+  var e = V(n);
+  return Au(e, 0, W(e, V(t)) + 1).join("")
+}
+
+
+
+
+
+
+const rr = {
+  '00d8': function (e, t) {
+    var t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+      , r = {
+        rotl: function (e, t) {
+          return e << t | e >>> 32 - t
+        },
+        rotr: function (e, t) {
+          return e << 32 - t | e >>> t
+        },
+        endian: function (e) {
+          if (e.constructor == Number)
+            return 16711935 & r.rotl(e, 8) | 4278255360 & r.rotl(e, 24);
+          for (var t = 0; t < e.length; t++)
+            e[t] = r.endian(e[t]);
+          return e
+        },
+        randomBytes: function (e) {
+          for (var t = []; e > 0; e--)
+            t.push(Math.floor(256 * Math.random()));
+          return t
+        },
+        bytesToWords: function (e) {
+          for (var t = [], r = 0, n = 0; r < e.length; r++,
+            n += 8)
+            t[n >>> 5] |= e[r] << 24 - n % 32;
+          return t
+        },
+        wordsToBytes: function (e) {
+          for (var t = [], r = 0; r < 32 * e.length; r += 8)
+            t.push(e[r >>> 5] >>> 24 - r % 32 & 255);
+          return t
+        },
+        bytesToHex: function (e) {
+          for (var t = [], r = 0; r < e.length; r++)
+            t.push((e[r] >>> 4).toString(16)),
+              t.push((15 & e[r]).toString(16));
+          return t.join("")
+        },
+        hexToBytes: function (e) {
+          for (var t = [], r = 0; r < e.length; r += 2)
+            t.push(parseInt(e.substr(r, 2), 16));
+          return t
+        },
+        bytesToBase64: function (e) {
+          for (var r = [], n = 0; n < e.length; n += 3)
+            for (var i = e[n] << 16 | e[n + 1] << 8 | e[n + 2], o = 0; o < 4; o++)
+              8 * n + 6 * o <= 8 * e.length ? r.push(t.charAt(i >>> 6 * (3 - o) & 63)) : r.push("=");
+          return r.join("")
+        },
+        base64ToBytes: function (e) {
+          e = e.replace(/[^A-Z0-9+\/]/gi, "");
+          for (var r = [], n = 0, i = 0; n < e.length; i = ++n % 4)
+            0 != i && r.push((t.indexOf(e.charAt(n - 1)) & Math.pow(2, -2 * i + 8) - 1) << 2 * i | t.indexOf(e.charAt(n)) >>> 6 - 2 * i);
+          return r
+        }
+      };
+    return r;
+  },
+  "9a63": function (e, t) {
+    var r = {
+      utf8: {
+        stringToBytes: function (e) {
+          return r.bin.stringToBytes(unescape(encodeURIComponent(e)))
+        },
+        bytesToString: function (e) {
+          return decodeURIComponent(escape(r.bin.bytesToString(e)))
+        }
+      },
+      bin: {
+        stringToBytes: function (e) {
+          for (var t = [], r = 0; r < e.length; r++)
+            t.push(255 & e.charCodeAt(r));
+          return t
+        },
+        bytesToString: function (e) {
+          for (var t = [], r = 0; r < e.length; r++)
+            t.push(String.fromCharCode(e[r]));
+          return t.join("")
+        }
+      }
+    };
+    return r
+  },
+  "8349": function (e, t) {
+    function r(e) {
+      return !!e.constructor && "function" == typeof e.constructor.isBuffer && e.constructor.isBuffer(e)
+    }
+    return function (e) {
+      return null != e && (r(e) || function (e) {
+        return "function" == typeof e.readFloatLE && "function" == typeof e.slice && r(e.slice(0, 0))
+      }(e) || !!e._isBuffer)
+    }
+  },
+}
+
+
+function a(e, r) {
+  var t = rr["00d8"]()
+    , n = rr["9a63"]().utf8
+    , i = rr["8349"]
+    , o = rr["9a63"]().bin
+  e.constructor == String ? e = r && "binary" === r.encoding ? o.stringToBytes(e) : n.stringToBytes(e) : i(e) ? e = Array.prototype.slice.call(e, 0) : Array.isArray(e) || e.constructor === Uint8Array || (e = e.toString());
+  for (var s = t.bytesToWords(e), u = 8 * e.length, c = 1732584193, f = -271733879, d = -1732584194, l = 271733878, h = 0; h < s.length; h++)
+    s[h] = 16711935 & (s[h] << 8 | s[h] >>> 24) | 4278255360 & (s[h] << 24 | s[h] >>> 8);
+  s[u >>> 5] |= 128 << u % 32,
+    s[14 + (u + 64 >>> 9 << 4)] = u;
+  var p = a._ff
+    , b = a._gg
+    , y = a._hh
+    , m = a._ii;
+  for (h = 0; h < s.length; h += 16) {
+    var g = c
+      , v = f
+      , _ = d
+      , w = l;
+    c = p(c, f, d, l, s[h + 0], 7, -680876936),
+      l = p(l, c, f, d, s[h + 1], 12, -389564586),
+      d = p(d, l, c, f, s[h + 2], 17, 606105819),
+      f = p(f, d, l, c, s[h + 3], 22, -1044525330),
+      c = p(c, f, d, l, s[h + 4], 7, -176418897),
+      l = p(l, c, f, d, s[h + 5], 12, 1200080426),
+      d = p(d, l, c, f, s[h + 6], 17, -1473231341),
+      f = p(f, d, l, c, s[h + 7], 22, -45705983),
+      c = p(c, f, d, l, s[h + 8], 7, 1770035416),
+      l = p(l, c, f, d, s[h + 9], 12, -1958414417),
+      d = p(d, l, c, f, s[h + 10], 17, -42063),
+      f = p(f, d, l, c, s[h + 11], 22, -1990404162),
+      c = p(c, f, d, l, s[h + 12], 7, 1804603682),
+      l = p(l, c, f, d, s[h + 13], 12, -40341101),
+      d = p(d, l, c, f, s[h + 14], 17, -1502002290),
+      c = b(c, f = p(f, d, l, c, s[h + 15], 22, 1236535329), d, l, s[h + 1], 5, -165796510),
+      l = b(l, c, f, d, s[h + 6], 9, -1069501632),
+      d = b(d, l, c, f, s[h + 11], 14, 643717713),
+      f = b(f, d, l, c, s[h + 0], 20, -373897302),
+      c = b(c, f, d, l, s[h + 5], 5, -701558691),
+      l = b(l, c, f, d, s[h + 10], 9, 38016083),
+      d = b(d, l, c, f, s[h + 15], 14, -660478335),
+      f = b(f, d, l, c, s[h + 4], 20, -405537848),
+      c = b(c, f, d, l, s[h + 9], 5, 568446438),
+      l = b(l, c, f, d, s[h + 14], 9, -1019803690),
+      d = b(d, l, c, f, s[h + 3], 14, -187363961),
+      f = b(f, d, l, c, s[h + 8], 20, 1163531501),
+      c = b(c, f, d, l, s[h + 13], 5, -1444681467),
+      l = b(l, c, f, d, s[h + 2], 9, -51403784),
+      d = b(d, l, c, f, s[h + 7], 14, 1735328473),
+      c = y(c, f = b(f, d, l, c, s[h + 12], 20, -1926607734), d, l, s[h + 5], 4, -378558),
+      l = y(l, c, f, d, s[h + 8], 11, -2022574463),
+      d = y(d, l, c, f, s[h + 11], 16, 1839030562),
+      f = y(f, d, l, c, s[h + 14], 23, -35309556),
+      c = y(c, f, d, l, s[h + 1], 4, -1530992060),
+      l = y(l, c, f, d, s[h + 4], 11, 1272893353),
+      d = y(d, l, c, f, s[h + 7], 16, -155497632),
+      f = y(f, d, l, c, s[h + 10], 23, -1094730640),
+      c = y(c, f, d, l, s[h + 13], 4, 681279174),
+      l = y(l, c, f, d, s[h + 0], 11, -358537222),
+      d = y(d, l, c, f, s[h + 3], 16, -722521979),
+      f = y(f, d, l, c, s[h + 6], 23, 76029189),
+      c = y(c, f, d, l, s[h + 9], 4, -640364487),
+      l = y(l, c, f, d, s[h + 12], 11, -421815835),
+      d = y(d, l, c, f, s[h + 15], 16, 530742520),
+      c = m(c, f = y(f, d, l, c, s[h + 2], 23, -995338651), d, l, s[h + 0], 6, -198630844),
+      l = m(l, c, f, d, s[h + 7], 10, 1126891415),
+      d = m(d, l, c, f, s[h + 14], 15, -1416354905),
+      f = m(f, d, l, c, s[h + 5], 21, -57434055),
+      c = m(c, f, d, l, s[h + 12], 6, 1700485571),
+      l = m(l, c, f, d, s[h + 3], 10, -1894986606),
+      d = m(d, l, c, f, s[h + 10], 15, -1051523),
+      f = m(f, d, l, c, s[h + 1], 21, -2054922799),
+      c = m(c, f, d, l, s[h + 8], 6, 1873313359),
+      l = m(l, c, f, d, s[h + 15], 10, -30611744),
+      d = m(d, l, c, f, s[h + 6], 15, -1560198380),
+      f = m(f, d, l, c, s[h + 13], 21, 1309151649),
+      c = m(c, f, d, l, s[h + 4], 6, -145523070),
+      l = m(l, c, f, d, s[h + 11], 10, -1120210379),
+      d = m(d, l, c, f, s[h + 2], 15, 718787259),
+      f = m(f, d, l, c, s[h + 9], 21, -343485551),
+      c = c + g >>> 0,
+      f = f + v >>> 0,
+      d = d + _ >>> 0,
+      l = l + w >>> 0
+  }
+  return t.endian([c, f, d, l])
+};
+
+
+a._ff = function (e, t, r, n, i, o, a) {
+  var s = e + (t & r | ~t & n) + (i >>> 0) + a;
+  return (s << o | s >>> 32 - o) + t
+}
+  ,
+  a._gg = function (e, t, r, n, i, o, a) {
+    var s = e + (t & n | r & ~n) + (i >>> 0) + a;
+    return (s << o | s >>> 32 - o) + t
+  }
+  ,
+  a._hh = function (e, t, r, n, i, o, a) {
+    var s = e + (t ^ r ^ n) + (i >>> 0) + a;
+    return (s << o | s >>> 32 - o) + t
+  }
+  ,
+  a._ii = function (e, t, r, n, i, o, a) {
+    var s = e + (r ^ (t | ~n)) + (i >>> 0) + a;
+    return (s << o | s >>> 32 - o) + t
+  }
+
+function n(e) {
+  var t = rr["00d8"]()
+    , n = rr["9a63"]().utf8
+    , i = rr["8349"]()
+    , o = rr["9a63"]().bin
+  if (null == e)
+    throw new Error("Illegal argument " + e);
+  var n = t.wordsToBytes(a(e));
+  return t.bytesToHex(n)
+}
+
+
+function u_sign(e, t) {
+  var r, o = "9SASji5OWnG41iRKiSvTJHlXHmRySRp1", a = "", s = t || {}, u = (e = e || "").split("?");
+  if (u.length > 0 && (r = u[1]),
+    r) {
+    var c = r.split("&")
+      , f = "";
+    c.forEach((function (e) {
+      var t = e.split("=");
+      f += "".concat(t[0], "=").concat(encodeURI(t[1]), "&")
+    }
+    )),
+      a = "".concat(xa(f, "&"), "&").concat(o)
+  } else
+    a = Object.keys(s).length > 0 ? "".concat(JSON.stringify(s), "&").concat(o) : "&".concat(o);
+  return a = a.toLowerCase(),
+    n(a)
+}
+
+console.log(u_sign('/youzy.dms.basiclib.api.career.job.hits.inc?jobCode=02030502', undefined))

+ 5 - 0
src/main/resources/users.txt

@@ -0,0 +1,5 @@
+002180054495,46805392
+002180054494,17988675
+002180054496,85603400
+002180054499,41507645
+002180054497,86080741

+ 293 - 0
src/test/java/spider/CollegeData.java

@@ -0,0 +1,293 @@
+package spider;
+
+import java.io.File;
+import java.io.IOException;
+import java.sql.SQLException;
+import java.util.*;
+
+import org.junit.Test;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.mingxue.spider.utils.ApiUtil;
+
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.thread.ThreadUtil;
+import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.db.DbUtil;
+import cn.hutool.db.Entity;
+
+public class CollegeData {
+    private static String host = "https://apiv4.diyigaokao.com";
+
+    private List<String> errors = new ArrayList<>();
+
+    @Test
+    public void test() throws Exception {
+        // 湖南
+//        ApiUtil.getInstance().login();
+//        collegeList();
+//        getCategoryMajor();
+//        generateNineDate();
+//         广东账号:18774924158,密码:123456
+//         ApiUtil.getInstance().login("18774924158", "123456",false);
+        // 湖北账号:18674898114, 密码:123456
+        // ApiUtil.getInstance().login("18674898114", "123456",false);
+        // 江西账号:13203226079,密码:123456
+        ApiUtil.getInstance().login("13203226079", "123456",false);
+//        collegeList();
+        // getCategoryMajor();
+    }
+
+    public void collegeList() throws SQLException {
+        int count = 0, total = 5000, page = 1;
+        do {
+            try {
+                StringBuilder sb = new StringBuilder(host);
+                sb.append("/college/list/byMultiple");
+                sb.append("?provinceIds=&yxjbz=&levels=&collegeType=&hotTags=");
+                sb.append("&pageIndex=").append(page).append("&pageSize=15");
+                JSONObject res = ApiUtil.getInstance().httpSyncGet(sb.toString());
+                total = res.getInteger("total");
+                JSONArray data = res.getJSONArray("data");
+                for (int i = 0; i < data.size(); i++) {
+                    long start = System.currentTimeMillis();
+                    JSONObject row = data.getJSONObject(i);
+                    final Integer collegeId = row.getInteger("id");
+                    Entity entity = DbUtil.use().get("college", "id", collegeId);
+                    if (null == entity) {
+//                        detail(collegeId, 0);
+                        ThreadUtil.safeSleep(1 * 1000);
+                    }
+                    xuanke(collegeId);
+                    ThreadUtil.safeSleep(1 * 1000);
+                    System.out.println(DateUtil.formatBetween(System.currentTimeMillis() - start));
+                }
+                System.out.println("已完成:" + page);
+                count = page * 15;
+                page += 1;
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        } while (count <= total);
+        File dir = new File("E:/projects/wangmin/evaluation/code/spider/");
+        FileUtil.writeLines(errors, new File(dir, ApiUtil.getInstance().getUser().getCode() + ".txt"), CharsetUtil.CHARSET_UTF_8);
+        System.out.println("执行完成!");
+    }
+
+    private void detail(Integer id, Integer retry) {
+        int maxRetry = 2;
+        JSONObject res = null;
+        try {
+            String url = host + "/college/detail/" + id;
+            res = ApiUtil.getInstance().httpSyncGet(url);
+            if (Integer.valueOf(-1).equals(res.getInteger("error"))) {
+                System.out.println(url);
+                if (retry >= maxRetry) {
+                    System.out.println("重试" + maxRetry + "次还不够:" + id);
+                    errors.add(url + "\t" + res.getString("data"));
+                    return;
+                }
+                ThreadUtil.safeSleep(1 * 1000);
+                detail(id, retry + 1);
+            } else {
+                JSONObject data = res.getJSONObject("data");
+                data.remove("rankMap");
+                data.remove("collegeDetailHotMajor");
+                data.remove("yuanxiList");
+                Entity record = Entity.create("college");
+                record.putAll(data);
+                DbUtil.use().insertOrUpdate(record, "id");
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        } catch (SQLException e) {
+            e.printStackTrace();
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw e;
+        }
+    }
+
+    private void xuanke(Integer collegeId) {
+        List<Integer> years = new ArrayList<Integer>(2);
+        Integer provinceId = ApiUtil.getInstance().getUser().getProvinceId();
+        try {
+            JSONObject res = ApiUtil.getInstance().httpSyncGet(host + "/years?provinceId=" + provinceId + "&queryType=4");
+            JSONArray rows = res.getJSONArray("data");
+            for (int i = 0; i < rows.size(); i++) {
+                years.add(rows.getJSONObject(i).getInteger("year"));
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        List<Entity> records = new ArrayList<>(12);
+        for (Integer year : years) {
+            int count = 0, total = 100, page = 1;
+            do {
+                try {
+                    StringBuilder sb = new StringBuilder();
+                    sb.append(host).append("/xuanke/list/byCollegeId");
+                    sb.append("?provinceId=").append(provinceId);
+                    sb.append("&collegeId=").append(collegeId);
+                    sb.append("&year=").append(year);
+                    sb.append("&page=").append(page).append("&pageSize=30");
+                    JSONObject res = ApiUtil.getInstance().httpSyncGet(sb.toString());
+                    JSONArray rows = res.getJSONArray("data");
+                    for (int i = 0; i < rows.size(); i++) {
+                        JSONObject row = rows.getJSONObject(i);
+                        row.remove("Row");
+                        Entity record = Entity.create("major");
+                        record.putAll(row);
+                        record.set("_provinceId", provinceId).set("_collegeId", collegeId).set("_year", year);
+                        records.add(record);
+                    }
+                    total = res.getInteger("total");
+                    count = page * 30;
+                    page += 1;
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+
+            } while (count <= total);
+        }
+        try {
+            DbUtil.use().del(Entity.create("major").set("CollegeID", collegeId));
+            DbUtil.use().insert(records);
+            // DbUtil.use()
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 获取专业列表
+     */
+    public void getCategoryMajor() {
+        Set<Entity> MajorSubjectList = new HashSet<>();// 一级专业
+        Set<Entity> MajorCategoryList = new HashSet<Entity>();// 二级专业
+        Set<Entity> MajorList = new HashSet<Entity>();// 三级专业
+        try {
+            JSONObject res = ApiUtil.getInstance().httpSyncGet(host + "/major/allCategoryMajor");
+            JSONObject data = res.getJSONObject("data");
+            // JSONArray bk = data.getJSONArray("BK");
+            JSONArray zk = data.getJSONArray("ZK");
+            // getData(bk,"本科",MajorSubjectList,MajorCategoryList,MajorList);
+            MajorSubjectList.clear();// 一级专业
+            MajorCategoryList.clear();// 二级专业
+            MajorList.clear();// 三级专业
+            getData(zk, "专科", MajorSubjectList, MajorCategoryList, MajorList);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void getData(JSONArray bk, String Type, Set<Entity> MajorSubjectList, Set<Entity> MajorCategoryList, Set<Entity> MajorList) {
+        for (int i = 0; i < bk.size(); i++) {
+            JSONObject Subject = bk.getJSONObject(i);
+            if ("专科".equalsIgnoreCase(Type)) {
+                // 专科的ID在本科的基础上增加12
+                Subject.put("ID", (Subject.getInteger("ID") + 12));
+            }
+            Subject.put("Type", Type);
+            JSONArray CategoryArray = Subject.getJSONArray("Category");
+            Subject.remove("Category");
+            Entity subjectTable = Entity.create("sy_major_subject");
+            subjectTable.putAll(Subject);
+
+            MajorSubjectList.add(subjectTable);
+            System.out.println(Type + ":MajorSubjectList:" + JSONObject.toJSONString(MajorSubjectList));
+
+            for (int j = 0; j < CategoryArray.size(); j++) {
+                JSONObject Category = CategoryArray.getJSONObject(j);
+                Category.put("MajorSubjectCode", Subject.getString("SubjectCode"));// 上级ID填充下级
+                Category.remove("MajorSubjectID");// ID转换为code作为关联
+
+                Category.put("Type", Type);
+                if ("专科".equalsIgnoreCase(Type)) {
+                    Category.remove("ID");
+                }
+
+                // System.out.println("before Category:"+Category);
+                JSONArray MajorArray = Category.getJSONArray("Major");
+
+                Category.remove("Major");
+                // System.out.println("after Category:"+Category);
+
+                Entity categoryTable = Entity.create("sy_major_category");
+                categoryTable.putAll(Category);
+                MajorCategoryList.add(categoryTable);
+                for (int k = 0; k < MajorArray.size(); k++) {
+                    JSONObject Major = MajorArray.getJSONObject(k);
+                    Major.put("MajorCategoryCode", Category.getString("MajorCategoryCode"));// 上级ID填充下级
+                    Major.remove("MajorCategoryID");// ID转换为code关联
+                    Major.put("Type", Type);
+                    if ("专科".equalsIgnoreCase(Type)) {
+                        Major.remove("ID");
+                    }
+
+                    Entity majorTable = Entity.create("sy_major_major");
+                    majorTable.putAll(Major);
+                    MajorList.add(majorTable);
+                }
+            }
+
+            try {
+                // DbUtil.use().del(Entity.create("sy_major_subject").set("Type", Type));
+                DbUtil.use().insert(MajorSubjectList);
+                MajorSubjectList.clear();
+
+                // DbUtil.use().del(Entity.create("sy_major_category").set("Type", Type));
+                DbUtil.use().insert(MajorCategoryList);
+                MajorCategoryList.clear();
+
+                // DbUtil.use().del(Entity.create("sy_major_major").set("Type", Type));
+                DbUtil.use().insert(MajorList);
+                MajorList.clear();
+
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+
+        }
+    }
+
+    /**
+     * 随机生成9科数据
+     */
+    private void generateNineDate(){
+        int count = 6000;
+//        JSONArray majorArray = new JSONArray();
+        Set<Entity> MajorList = new HashSet<Entity>();
+        for (int i =0 ;i<count;i++){
+            Map major=new TreeMap();
+            major.put("chinese",(int)(100-Math.random()*50));
+            major.put("math",(int)(100-Math.random()*50));
+            major.put("english",(int)(100-Math.random()*50));
+            major.put("physics",(int)(100-Math.random()*50));
+            major.put("chemistry",(int)(100-Math.random()*50));
+            major.put("biology",(int)(100-Math.random()*50));
+            major.put("history",(int)(100-Math.random()*50));
+            major.put("politics",(int)(100-Math.random()*50));
+            major.put("geography",(int)(100-Math.random()*50));
+
+            Entity majorTable = Entity.create("major_nine");
+            majorTable.putAll(major);
+            MajorList.add(majorTable);
+
+//            System.out.println(major);
+        }
+
+
+        try {
+            DbUtil.use().insert(MajorList);
+        } catch (SQLException e) {
+            e.printStackTrace();
+        }
+
+        System.out.println("finished");
+
+    }
+}

+ 50 - 0
src/test/java/spider/TestUsign.java

@@ -0,0 +1,50 @@
+package spider;
+
+import com.mingxue.spider.utils.UsignUtils;
+import jdk.nashorn.api.scripting.NashornScriptEngine;
+import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
+import java.io.FileReader;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Hello world!
+ *
+ */
+public class TestUsign {
+	public static void main(String[] args) {
+		/* 1.首先引入js引擎 */
+//		ScriptEngineManager manager = new ScriptEngineManager();
+		//引擎名称传入JavaScript、js、javascript、nashorn、Nashorn 均可等价
+//		ScriptEngine engine = manager.getEngineByName("nashorn");
+
+		String[] options = new String[] {"--language=es6"};
+		NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
+		NashornScriptEngine engine = (NashornScriptEngine) factory.getScriptEngine(options);
+
+		try {
+			/* 2.然后读取js文件 */
+			String path = Thread.currentThread().getContextClassLoader().getResource("").getPath(); // 获取targe路径
+
+			String requestPath = "/youzy.dms.basiclib.api.career.job.hits.inc";
+			String jobCode="02030502";
+			String params =requestPath+"?jobCode="+jobCode;
+			engine.eval(new FileReader(path + "js/usign.js"));
+//			Object o = engine.eval("u_sign('/youzy.dms.basiclib.api.career.job.hits.inc?jobCode=02030502')");
+//			System.out.println("value : "+o);
+//
+//			o = engine.eval("u_sign('"+params+"')");
+//			System.out.println("value : "+o);
+
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+
+		String requestPath = "/youzy.dms.basiclib.api.career.job.hits.inc";
+		String jobCode = "02030502";
+		Map<String,Object> paramMap =new HashMap<>();
+		paramMap.put("jobCode",jobCode);
+		UsignUtils.getUsign(UsignUtils.getRequestPathAndParam(requestPath,paramMap));
+	}
+
+}

+ 628 - 0
src/test/java/spider/YouZy.java

@@ -0,0 +1,628 @@
+package spider;
+
+import cn.hutool.db.DbUtil;
+import cn.hutool.db.Entity;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.mingxue.spider.helper.StringUtils;
+import com.mingxue.spider.utils.HttpUtils;
+import com.mingxue.spider.utils.UsignUtils;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.sql.SQLException;
+import java.util.*;
+
+/**
+ * 优志愿
+ * 1、获取职业列表数据;
+ * https://www.youzy.cn/tzy/search/vocationalLibrary/homepage
+ * 2、详情数据(职业概况、就业岗位);
+ */
+public class YouZy {
+    private static Logger log = LoggerFactory.getLogger(YouZy.class);
+
+    private static final String host = "https://uwf7de983aad7a717eb.youzy.cn";
+
+    @Test
+    public void test() throws Exception {
+//        processVocationalHotAndLack();
+//        getVocationalList();
+//        getCareerProspects("010101","c29e43498afdfe626ea17818b86fec4b");
+
+
+        //eduLevel:"ben","zhuan"
+//        List<String> majorList = getMajor(3, "ben,zhuan");
+//        System.out.println(JSONObject.toJSONString(majorList));
+        List<String> majorList = getMajor(3, "ben,zhuan");
+        System.out.println(majorList.size());
+
+//        majorList = getMajor(3, "zhuan");
+//        System.out.println(majorList.size());
+
+        for (String majorCode : majorList) {
+//            if(majorCode.compareTo("130503")<=-1){
+//                continue;
+//            }
+
+            getCareerProspects(majorCode);
+        }
+    }
+
+    /**
+     * @param majorLevel =1 2 3级专业
+     * @param eduLevels(教育水平):"ben","zhuan"
+     * @return
+     */
+    public List<String> getMajor(Integer majorLevel, String eduLevels) {
+        List<String> majorList = new LinkedList<>();
+        for (String eduLevel : eduLevels.split(",")) {
+            String path = "/youzy.dms.basiclib.api.major.tree.query";
+            Map<String, Object> map = new HashMap<>();
+            map.put("eduLevel", eduLevel);
+
+            String result = getResult(path, map);
+            if (StringUtils.isEmpty(result)) {
+                return majorList;
+            }
+
+            JSONArray resultArray = JSONObject.parseArray(result);
+            resultArray.stream().forEach(major1 -> {
+                JSONObject major1Obj = (JSONObject) major1;
+                String code1 = major1Obj.getString("code");
+                String name1 = major1Obj.getString("name");
+                if (majorLevel == 1) {
+                    majorList.add(code1);
+                } else {
+                    JSONArray major2Array = major1Obj.getJSONArray("majors");
+                    major2Array.stream().forEach(major2 -> {
+                        JSONObject major2Obj = (JSONObject) major2;
+                        String code2 = major2Obj.getString("code");
+                        String name2 = major2Obj.getString("name");
+                        if (majorLevel == 2) {
+                            majorList.add(code2);
+                        } else if (majorLevel == 3) {
+                            JSONArray major3Array = major2Obj.getJSONArray("majors");
+                            major3Array.stream().forEach(major3 -> {
+                                JSONObject major3Obj = (JSONObject) major3;
+                                String code3 = major3Obj.getString("code");
+                                String name3 = major3Obj.getString("name");
+                                majorList.add(code3);
+                            });
+                        }
+                    });
+                }
+            });
+        }
+        return majorList;
+    }
+
+    /**
+     * 就业前景 sy_major_career_prospects
+     * https://www.youzy.cn/majors/small?code=010101
+     *
+     * @param code
+     */
+    public void getCareerProspects(String code) {
+        log.info("start code {}",code);
+        //就业方向
+        String path = "/youzy.dms.basiclib.api.major.bycode.get";
+        Map<String, Object> map = new HashMap<>();
+        map.put("code", code);
+
+        String res = getRes(path, map);
+
+        String jobDirection = StringUtils.EMPTY;
+        String major=StringUtils.EMPTY;
+        String majorName = StringUtils.EMPTY;
+        if (StringUtils.isEmpty(res)) {
+            log.error("res is empty");
+            return;
+        } else {
+            JSONObject resultJsonobject = JSONObject.parseObject(res);
+            if (resultJsonobject.getBoolean("isSuccess")) {
+                JSONObject resultJsonObject = resultJsonobject.getJSONObject("result");
+                String bigName = resultJsonObject.getString("bigName");
+                jobDirection = resultJsonObject.getJSONObject("major").getString("jobDirection");
+
+                majorName = resultJsonObject.getJSONObject("major").getString("name");
+                major = bigName+"-"+majorName;
+            }
+        }
+        if (StringUtils.isEmpty(jobDirection)) {
+            log.error("jobDirection is empty,code is {},major is {},resultJsonobject is {}",code,major
+                    ,JSONObject.parseObject(res));
+
+//            return;
+        }
+
+
+        path = "/youzy.dms.basiclib.api.major.jobfuture.bycode.query";
+        String result = getResult(path, map);
+        if (StringUtils.isEmpty(result)) {
+            log.error("result is empty");
+            return;
+        }
+
+        JSONArray resultJsonArray = JSONObject.parseArray(result);
+
+        JSONObject insertObj4 = new JSONObject();
+        insertObj4.put("job_direction", jobDirection);
+        insertObj4.put("code", code);
+        insertObj4.put("name", majorName);
+
+        List<Entity> dbInsertList = new LinkedList<>();
+
+        resultJsonArray.stream().forEach(jsonobejct00 -> {
+            JSONObject jsonobejct0 = (JSONObject) jsonobejct00;
+            String type = jsonobejct0.getString("type");
+            switch (type) {
+                case "ZhiYeFX"://主要职业分布
+                    String vocational_distribution = jsonobejct0.getString("futures");
+                    insertObj4.put("vocational_distribution", vocational_distribution);
+                    String description = jsonobejct0.getString("description");
+                    if (StringUtils.isNotEmpty(description)) {
+                        insertObj4.put("description", description);
+                    }
+                    break;
+                case "HangYeFB"://主要行业分布
+                    String industry_distribution = jsonobejct0.getString("futures");
+                    insertObj4.put("industry_distribution", industry_distribution);
+                    description = jsonobejct0.getString("description");
+                    if (StringUtils.isNotEmpty(description)) {
+                        insertObj4.put("description", description);
+                    }
+                    break;
+                case "ChengShiFB"://主要就业地区分布
+                    String job_region_distribution = jsonobejct0.getString("futures");
+                    insertObj4.put("job_region_distribution", job_region_distribution);
+                    description = jsonobejct0.getString("description");
+                    if (StringUtils.isNotEmpty(description)) {
+                        insertObj4.put("description", description);
+                    }
+                    break;
+                case "XinZiPJ"://近10年平均薪资
+                    String average_salary = jsonobejct0.getString("futures");
+                    insertObj4.put("average_salary", average_salary);
+                    description = jsonobejct0.getString("description");
+                    if (StringUtils.isNotEmpty(description)) {
+                        insertObj4.put("description", description);
+                    }
+                    break;
+                default:
+                    break;
+            }
+
+        });
+        //插入表sy_vocational_post
+        Entity subjectTable4 = Entity.create("sy_major_career_prospects");
+        subjectTable4.putAll(insertObj4);
+//                dbInsertList.add(subjectTable4);
+
+        try {
+            DbUtil.use().insertOrUpdate(subjectTable4, "code");
+            log.info("finished code {}",code);
+//                    DbUtil.use().insert(dbInsertList);
+        } catch (SQLException e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    public void processVocationalHotAndLack() throws SQLException {
+        List<Entity> dbInsertList = new LinkedList<>();
+        JSONArray hitsArray = getVocationalHits();
+        hitsArray.stream().forEach(hitsObj -> {
+            JSONObject insertObj5 = (JSONObject) hitsObj;
+            insertObj5.put("code", insertObj5.getString("code"));
+            insertObj5.put("hits", insertObj5.getString("hits"));
+            insertObj5.put("level", insertObj5.getInteger("level"));
+            insertObj5.put("levels", insertObj5.getString("levels"));
+            insertObj5.put("name", insertObj5.getString("name"));
+            insertObj5.put("summary", insertObj5.getString("summary"));
+            insertObj5.put("type", 1);
+
+            Entity subjectTable5 = Entity.create("sy_vocational_hits_lack");
+            subjectTable5.putAll(insertObj5);
+            dbInsertList.add(subjectTable5);
+        });
+
+        JSONArray lackArray = getVocationalLack();
+        lackArray.stream().forEach(hitsObj -> {
+            JSONObject insertObj5 = (JSONObject) hitsObj;
+            insertObj5.put("code", insertObj5.getString("code"));
+            insertObj5.put("hits", insertObj5.getString("hits"));
+            insertObj5.put("level", insertObj5.getInteger("level"));
+            insertObj5.put("levels", insertObj5.getString("levels"));
+            insertObj5.put("name", insertObj5.getString("name"));
+            insertObj5.put("summary", insertObj5.getString("summary"));
+            insertObj5.put("type", 2);
+
+            Entity subjectTable5 = Entity.create("sy_vocational_hits_lack");
+            subjectTable5.putAll(insertObj5);
+            dbInsertList.add(subjectTable5);
+        });
+
+        DbUtil.use().insert(dbInsertList);
+    }
+
+    /**
+     * 热门职业
+     */
+    public JSONArray getVocationalHits() {
+        String url = host + "/youzy.dms.basiclib.api.career.jobs.hot.get?count=" + 10;
+        String uSign = "d4ed2f64f1fb31fb92b9ea621f43d1d8";
+        String res = HttpUtils.postBody(url, "", uSign);
+        JSONArray resultJsonArray = new JSONArray();
+        if (StringUtils.isEmpty(res)) {
+            log.error("res is empty");
+        } else {
+            JSONObject resultJsonobject = JSONObject.parseObject(res);
+            if (resultJsonobject.getBoolean("isSuccess")) {
+                resultJsonArray = resultJsonobject.getJSONArray("result");
+            }
+        }
+        return resultJsonArray;
+    }
+
+    /**
+     * 稀缺职业
+     */
+    public JSONArray getVocationalLack() {
+        String url = host + "/youzy.dms.basiclib.api.career.jobs.lack.get?count=" + 20;
+        String uSign = "9ae912cae8aa4513d3fe5b25620018c3";
+        String res = HttpUtils.postBody(url, "", uSign);
+        JSONArray resultJsonArray = new JSONArray();
+        if (StringUtils.isEmpty(res)) {
+            log.error("res is empty");
+        } else {
+            JSONObject resultJsonobject = JSONObject.parseObject(res);
+            if (resultJsonobject.getBoolean("isSuccess")) {
+                resultJsonArray = resultJsonobject.getJSONArray("result");
+            }
+        }
+        return resultJsonArray;
+    }
+
+    public void getVocationalList() throws Exception {
+        String url = host + "/youzy.dms.basiclib.api.career.jobs.tree.get";
+        String uSign = "c08ac01cdae3d836849cc3ee3721332b";
+        String res = HttpUtils.postBody(url, "", uSign);
+        if (StringUtils.isEmpty(res)) {
+            log.error("res is empty");
+            return;
+        }
+        JSONObject resultJsonobject = JSONObject.parseObject(res);
+
+        List<Entity> dbInsertList = new LinkedList<>();
+        List<JSONObject> dataSet = new LinkedList<>();
+        final int[] dataSize = {0};
+
+        if (resultJsonobject.getBoolean("isSuccess")) {
+            JSONArray resultJsonArray = resultJsonobject.getJSONArray("result");
+//            log.info("resultJsonArray size is {}",resultJsonArray.size());
+            dataSize[0] += resultJsonArray.size();
+            resultJsonArray.stream().forEach(jsonobejct00 -> {
+                JSONObject jsonobejct0 = (JSONObject) jsonobejct00;
+                String code1 = jsonobejct0.getString("code");
+                String name1 = jsonobejct0.getString("name");
+                Integer level1 = 1;
+                String parentCode1 = "0";
+
+                JSONObject insertObj1 = new JSONObject();
+                insertObj1.put("code", code1);
+                insertObj1.put("name", name1);
+                insertObj1.put("level", level1);
+                insertObj1.put("parent_code", parentCode1);
+                insertObj1.put("ancestors", parentCode1);
+
+                Entity subjectTable = Entity.create("sy_vocational");
+                subjectTable.putAll(insertObj1);
+
+//                try {
+//                    DbUtil.use().insertOrUpdate(subjectTable,"code");
+//                } catch (SQLException e) {
+//                    e.printStackTrace();
+//                }
+
+                dbInsertList.add(subjectTable);
+                dataSet.add(insertObj1);
+
+                JSONArray childrenArray1 = jsonobejct0.getJSONArray("children");
+//                log.info("childrenArray1 size is {}",childrenArray1.size());
+                dataSize[0] += childrenArray1.size();
+
+                childrenArray1.stream().forEach(jsonobejct1 -> {
+                    JSONObject ca1 = (JSONObject) jsonobejct1;
+
+                    String code2 = ca1.getString("code");
+                    String name2 = ca1.getString("name");
+                    Integer level2 = 2;
+                    String parentCode2 = code1;
+                    JSONObject insertObj2 = new JSONObject();
+                    insertObj2.put("code", code2);
+                    insertObj2.put("name", name2);
+                    insertObj2.put("level", level2);
+                    insertObj2.put("parent_code", parentCode2);
+                    insertObj2.put("ancestors", parentCode1 + "," + parentCode2);
+                    dataSet.add(insertObj2);
+
+                    Entity subjectTable2 = Entity.create("sy_vocational");
+                    subjectTable2.putAll(insertObj2);
+                    dbInsertList.add(subjectTable2);
+
+//                    try {
+//                        DbUtil.use().insertOrUpdate(subjectTable2,"code");
+//                    } catch (SQLException e) {
+//                        e.printStackTrace();
+//                    }
+
+                    JSONArray jobsArray2 = ca1.getJSONArray("jobs");
+//                    log.info("jobsArray2 size is {}",jobsArray2.size());
+                    dataSize[0] += jobsArray2.size();
+
+                    jobsArray2.stream().forEach(jsonobejct2 -> {
+                        JSONObject ca2 = (JSONObject) jsonobejct2;
+                        String code3 = ca2.getString("code");
+                        String name3 = ca2.getString("name");
+                        Integer level3 = 3;
+                        String parentCode3 = code2;
+
+                        JSONObject insertObj3 = new JSONObject();
+                        insertObj3.put("code", code3);
+                        insertObj3.put("name", name3);
+                        insertObj3.put("level", level3);
+                        insertObj3.put("parent_code", parentCode3);
+                        insertObj3.put("ancestors", parentCode1 + "," + parentCode2 + "," + parentCode3);
+
+
+                        Entity subjectTable3 = Entity.create("sy_vocational");
+                        subjectTable3.putAll(insertObj3);
+                        dbInsertList.add(subjectTable3);
+                        dataSet.add(insertObj3);
+
+//                        try {
+//                            DbUtil.use().insertOrUpdate(subjectTable3,"code");
+//                        } catch (SQLException e) {
+//                            e.printStackTrace();
+//                        }
+
+                        //code3概述与就业岗位
+                        try {
+                            processVocationalOverviewAndPost(code3);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        }
+                        log.info("code3 is {}, name3 is {}", code3, name3);
+
+                    });
+                });
+            });
+        }
+
+        DbUtil.use().insert(dbInsertList);
+        dbInsertList.clear();
+
+        log.info("dataSize[0] is {}", dataSize[0]);
+        log.info(res);
+        log.info(JSONObject.toJSONString(res));
+    }
+
+    private void processVocationalOverviewAndPost(String vocationalCode) throws Exception {
+        //1-1概述
+        String url = host + "/youzy.dms.basiclib.api.career.job.bycode.get?code=" + vocationalCode;
+        String body = "code=" + vocationalCode;
+        String uSign = "28b6defebdf24aab9d6b6f0b68d58c4f";
+        String res = HttpUtils.postBody(url, "", uSign);
+        if (StringUtils.isEmpty(res)) {
+            log.error("1-1概述:res is empty,the code is {}", vocationalCode);
+            return;
+        }
+
+        JSONObject resObject = JSONObject.parseObject(res);
+
+        JSONObject insertObj3 = new JSONObject();
+        if (resObject.getBoolean("isSuccess")) {
+            JSONObject resultObj = resObject.getJSONObject("result");
+            String code = resultObj.getString("code");
+            String description = resultObj.getString("description");
+            Integer hits = resultObj.getInteger("hits");
+//            JSONArray jobs = resultObj.getJSONArray("jobs");
+            String jobs = resultObj.getString("jobs");
+
+            Integer level = resultObj.getInteger("level");
+            String levelName = resultObj.getString("levelName");
+//            JSONArray levels=resultObj.getJSONArray("levels");
+            String levels = resultObj.getString("levels");
+
+            String name = resultObj.getString("name");
+//            JSONArray postJobs=resultObj.getJSONArray("postJobs");
+            String postJobs = resultObj.getString("postJobs");
+
+            Integer status = resultObj.getInteger("status");
+            String summary = resultObj.getString("summary");
+//            JSONArray tags=resultObj.getJSONArray("tags");
+            String tags = resultObj.getString("tags");
+
+            insertObj3.put("code", code);
+            insertObj3.put("description", description);
+            insertObj3.put("hits", hits);
+            insertObj3.put("jobs", jobs);
+
+            insertObj3.put("level", level);
+            insertObj3.put("level_name", levelName);
+            insertObj3.put("levels", levels);
+            insertObj3.put("name", name);
+            insertObj3.put("post_jobs", postJobs);
+            insertObj3.put("status", status);
+            insertObj3.put("status", status);
+            insertObj3.put("summary", summary);
+            insertObj3.put("tags", tags);
+        }
+
+
+        //1-2、概述-相关专业
+        url = host + "/youzy.dms.basiclib.api.jobmajors.relevant.query?jobCode=" + vocationalCode;
+        ;
+//        body = "jobCode="+vocationalCode;
+        uSign = "6907cd5128cd30d61afb3774e90771a8";
+        res = HttpUtils.postBody(url, "", uSign);
+        if (StringUtils.isEmpty(res)) {
+            log.error("概述-相关专业:res is emptythe code is {}", vocationalCode);
+            return;
+        }
+        JSONObject postMajorsJsonobject = JSONObject.parseObject(res);
+
+        if (postMajorsJsonobject.getBoolean("isSuccess")) {
+            String postMajors = postMajorsJsonobject.getString("result");
+            insertObj3.put("post_majors", postMajors);
+        }
+
+        //插入表 sy_vocational_overview
+//        Entity subjectTable3 = Entity.create("sy_vocational_overview");
+//        subjectTable3.putAll(insertObj3);
+//        DbUtil.use().insertOrUpdate(subjectTable3,"code");
+
+
+        //2、就业岗位
+        url = host + "/youzy.dms.basiclib.api.career.post.tabs.get?code=" + vocationalCode;
+        ;
+//        body = "jobCode="+vocationalCode;
+        uSign = "28b6defebdf24aab9d6b6f0b68d58c4f";
+        res = HttpUtils.postBody(url, "", uSign);
+        if (StringUtils.isEmpty(res)) {
+            log.error("2-1、就业岗位:res is emptythe code is {}", vocationalCode);
+            return;
+        }
+        JSONObject postJsonobject = JSONObject.parseObject(res);
+
+        JSONObject insertObj4 = new JSONObject();
+        if (postJsonobject.getBoolean("isSuccess")) {
+            JSONArray postArray = postJsonobject.getJSONArray("result");
+            postArray.stream().forEach(jsonobejct -> {
+                JSONObject ca2 = (JSONObject) jsonobejct;
+                String name = ca2.getString("name");
+                Integer salaryMax = ca2.getInteger("salaryMax");
+                Integer salaryMin = ca2.getInteger("salaryMin");
+
+                insertObj4.put("code", vocationalCode);
+                insertObj4.put("name", name);
+                insertObj4.put("salary_max", salaryMax);
+                insertObj4.put("salary_min", salaryMin);
+
+                //插入表sy_vocational_post
+                Entity subjectTable4 = Entity.create("sy_vocational_post");
+                subjectTable4.putAll(insertObj4);
+                try {
+                    DbUtil.use().insertOrUpdate(subjectTable4, "name");
+                } catch (SQLException e) {
+                    e.printStackTrace();
+                }
+
+                //岗位详情
+                try {
+                    processPostDetails(name);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            });
+
+        }
+
+    }
+
+    private void processPostDetails(String postName) throws Exception {
+        //2-2、岗位详情
+        String url = host + "/youzy.dms.basiclib.api.career.post.byname.get?name=" + postName;
+        ;
+//        body = "jobCode="+vocationalCode;
+        String uSign = "e4b60f1264f61582957a67c1f23cede3";
+        String res = HttpUtils.postBody(url, "", uSign);
+        if (StringUtils.isEmpty(res)) {
+            log.error("2-2、岗位详情:res is emptythe name is {}", postName);
+            return;
+        }
+
+        JSONObject postDetailJsonobject = JSONObject.parseObject(res);
+
+        JSONObject insertObj5 = new JSONObject();
+
+        if (postDetailJsonobject.getBoolean("isSuccess")) {
+            JSONObject postDetailObj = postDetailJsonobject.getJSONObject("result");
+
+            String citySalary = postDetailObj.getString("citySalary");
+            String demand = postDetailObj.getString("demand");
+            String edu = postDetailObj.getString("edu");
+            String exp = postDetailObj.getString("exp");
+            String experience = postDetailObj.getString("experience");
+            String industrySalary = postDetailObj.getString("industrySalary");
+            String name = postDetailObj.getString("name");
+            String salary = postDetailObj.getString("salary");
+            Integer salaryAvg = postDetailObj.getInteger("salaryAvg");
+            String sampleDesc = postDetailObj.getString("sampleDesc");
+            //职业数据来源
+            String vocationalSource = "职业数据来自《中华人民共和国国家职业分类大典》";
+            //薪资数据来源
+            String salarySource = "薪资数据来自于职友集" + postDetailObj.getString("sampleDesc");
+
+            insertObj5.put("city_salary", citySalary);
+            insertObj5.put("demand", demand);
+            insertObj5.put("edu", edu);
+            insertObj5.put("exp", exp);
+            insertObj5.put("experience", experience);
+            insertObj5.put("industry_salary", industrySalary);
+            insertObj5.put("name", name);
+            insertObj5.put("salary", salary);
+            insertObj5.put("salary_avg", salaryAvg);
+            insertObj5.put("sample_desc", sampleDesc);
+            insertObj5.put("vocational_source", vocationalSource);
+            insertObj5.put("salary_source", salarySource);
+
+            //插入表sy_vocational_post_detail
+            Entity subjectTable5 = Entity.create("sy_vocational_post_detail");
+            subjectTable5.putAll(insertObj5);
+            DbUtil.use().insertOrUpdate(subjectTable5, "name");
+        }
+    }
+
+
+    /**
+     * 获取请求的result
+     *
+     * @param path /youzy.dms.basiclib.api.major.tree.query
+     * @param map  参数
+     * @return
+     */
+    public String getResult(String path, Map<String, Object> map) {
+        String res = getRes(path, map);
+        String resultString = StringUtils.EMPTY;
+        if (StringUtils.isEmpty(res)) {
+            log.error("res is empty");
+        } else {
+            JSONObject resultJsonobject = JSONObject.parseObject(res);
+            if (resultJsonobject.getBoolean("isSuccess")) {
+                resultString = resultJsonobject.getString("result");
+            }
+        }
+        return resultString;
+    }
+
+    public String getRes(String path, Map<String, Object> map) {
+        String requestPath = UsignUtils.getRequestPathAndParam(path, map);
+        String usign = UsignUtils.getUsign(requestPath);
+
+        String url = host + requestPath;
+        String res = HttpUtils.postBody(url, "", usign);
+
+        try {
+            Thread.sleep(500);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+
+        return res;
+    }
+
+
+}

+ 307 - 0
src/test/resources/js/usign.js

@@ -0,0 +1,307 @@
+function Rc(n) {
+  return null == n ? "" : pu(n)
+}
+function pu(n) {
+  if ("string" == typeof n)
+    return n;
+  if (yh(n))
+    return c(n, pu) + "";
+  if (yc(n))
+    return ps ? ps.call(n) : "";
+  var t = n + "";
+  return "0" == t && 1 / n == -Rn ? "-0" : t
+}
+function V(n) {
+  return B(n) ? H(n) : p(n)
+}
+function B(n) {
+  return undefined;
+}
+function p(n) {
+  return n.split("")
+}
+function W(n, t) {
+  for (var r = n.length; r-- && y(t, n[r], 0) > -1;)
+    ;
+  return r
+}
+function y(n, t, r) {
+  return t === t ? q(n, t, r) : g(n, b, r)
+}
+function q(n, t, r) {
+  for (var e = r - 1, u = n.length; ++e < u;)
+    if (n[e] === t)
+      return e;
+  return -1
+}
+function Au(n, t, r) {
+  var e = n.length;
+  return r = r === undefined ? e : r,
+    !t && r >= e ? n : fu(n, t, r)
+}
+function fu(n, t, r) {
+  var e = -1
+    , u = n.length;
+  t < 0 && (t = -t > u ? 0 : u + t),
+    r = r > u ? u : r,
+    r < 0 && (r += u),
+    u = t > r ? 0 : r - t >>> 0,
+    t >>>= 0;
+  for (var i = Array(u); ++e < u;)
+    i[e] = n[e + t];
+  return i
+}
+function xa(n, t, r) {
+  if (n = Rc(n),
+    n && (r || t === undefined))
+    return n.replace(Wt, "");
+  if (!n || !(t = pu(t)))
+    return n;
+  var e = V(n);
+  return Au(e, 0, W(e, V(t)) + 1).join("")
+}
+
+
+
+
+
+
+const rr = {
+  '00d8': function (e, t) {
+    var t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+      , r = {
+        rotl: function (e, t) {
+          return e << t | e >>> 32 - t
+        },
+        rotr: function (e, t) {
+          return e << 32 - t | e >>> t
+        },
+        endian: function (e) {
+          if (e.constructor == Number)
+            return 16711935 & r.rotl(e, 8) | 4278255360 & r.rotl(e, 24);
+          for (var t = 0; t < e.length; t++)
+            e[t] = r.endian(e[t]);
+          return e
+        },
+        randomBytes: function (e) {
+          for (var t = []; e > 0; e--)
+            t.push(Math.floor(256 * Math.random()));
+          return t
+        },
+        bytesToWords: function (e) {
+          for (var t = [], r = 0, n = 0; r < e.length; r++,
+            n += 8)
+            t[n >>> 5] |= e[r] << 24 - n % 32;
+          return t
+        },
+        wordsToBytes: function (e) {
+          for (var t = [], r = 0; r < 32 * e.length; r += 8)
+            t.push(e[r >>> 5] >>> 24 - r % 32 & 255);
+          return t
+        },
+        bytesToHex: function (e) {
+          for (var t = [], r = 0; r < e.length; r++)
+            t.push((e[r] >>> 4).toString(16)),
+              t.push((15 & e[r]).toString(16));
+          return t.join("")
+        },
+        hexToBytes: function (e) {
+          for (var t = [], r = 0; r < e.length; r += 2)
+            t.push(parseInt(e.substr(r, 2), 16));
+          return t
+        },
+        bytesToBase64: function (e) {
+          for (var r = [], n = 0; n < e.length; n += 3)
+            for (var i = e[n] << 16 | e[n + 1] << 8 | e[n + 2], o = 0; o < 4; o++)
+              8 * n + 6 * o <= 8 * e.length ? r.push(t.charAt(i >>> 6 * (3 - o) & 63)) : r.push("=");
+          return r.join("")
+        },
+        base64ToBytes: function (e) {
+          e = e.replace(/[^A-Z0-9+\/]/gi, "");
+          for (var r = [], n = 0, i = 0; n < e.length; i = ++n % 4)
+            0 != i && r.push((t.indexOf(e.charAt(n - 1)) & Math.pow(2, -2 * i + 8) - 1) << 2 * i | t.indexOf(e.charAt(n)) >>> 6 - 2 * i);
+          return r
+        }
+      };
+    return r;
+  },
+  "9a63": function (e, t) {
+    var r = {
+      utf8: {
+        stringToBytes: function (e) {
+          return r.bin.stringToBytes(unescape(encodeURIComponent(e)))
+        },
+        bytesToString: function (e) {
+          return decodeURIComponent(escape(r.bin.bytesToString(e)))
+        }
+      },
+      bin: {
+        stringToBytes: function (e) {
+          for (var t = [], r = 0; r < e.length; r++)
+            t.push(255 & e.charCodeAt(r));
+          return t
+        },
+        bytesToString: function (e) {
+          for (var t = [], r = 0; r < e.length; r++)
+            t.push(String.fromCharCode(e[r]));
+          return t.join("")
+        }
+      }
+    };
+    return r
+  },
+  "8349": function (e, t) {
+    function r(e) {
+      return !!e.constructor && "function" == typeof e.constructor.isBuffer && e.constructor.isBuffer(e)
+    }
+    return function (e) {
+      return null != e && (r(e) || function (e) {
+        return "function" == typeof e.readFloatLE && "function" == typeof e.slice && r(e.slice(0, 0))
+      }(e) || !!e._isBuffer)
+    }
+  },
+}
+
+
+function a(e, r) {
+  var t = rr["00d8"]()
+    , n = rr["9a63"]().utf8
+    , i = rr["8349"]
+    , o = rr["9a63"]().bin
+  e.constructor == String ? e = r && "binary" === r.encoding ? o.stringToBytes(e) : n.stringToBytes(e) : i(e) ? e = Array.prototype.slice.call(e, 0) : Array.isArray(e) || e.constructor === Uint8Array || (e = e.toString());
+  for (var s = t.bytesToWords(e), u = 8 * e.length, c = 1732584193, f = -271733879, d = -1732584194, l = 271733878, h = 0; h < s.length; h++)
+    s[h] = 16711935 & (s[h] << 8 | s[h] >>> 24) | 4278255360 & (s[h] << 24 | s[h] >>> 8);
+  s[u >>> 5] |= 128 << u % 32,
+    s[14 + (u + 64 >>> 9 << 4)] = u;
+  var p = a._ff
+    , b = a._gg
+    , y = a._hh
+    , m = a._ii;
+  for (h = 0; h < s.length; h += 16) {
+    var g = c
+      , v = f
+      , _ = d
+      , w = l;
+    c = p(c, f, d, l, s[h + 0], 7, -680876936),
+      l = p(l, c, f, d, s[h + 1], 12, -389564586),
+      d = p(d, l, c, f, s[h + 2], 17, 606105819),
+      f = p(f, d, l, c, s[h + 3], 22, -1044525330),
+      c = p(c, f, d, l, s[h + 4], 7, -176418897),
+      l = p(l, c, f, d, s[h + 5], 12, 1200080426),
+      d = p(d, l, c, f, s[h + 6], 17, -1473231341),
+      f = p(f, d, l, c, s[h + 7], 22, -45705983),
+      c = p(c, f, d, l, s[h + 8], 7, 1770035416),
+      l = p(l, c, f, d, s[h + 9], 12, -1958414417),
+      d = p(d, l, c, f, s[h + 10], 17, -42063),
+      f = p(f, d, l, c, s[h + 11], 22, -1990404162),
+      c = p(c, f, d, l, s[h + 12], 7, 1804603682),
+      l = p(l, c, f, d, s[h + 13], 12, -40341101),
+      d = p(d, l, c, f, s[h + 14], 17, -1502002290),
+      c = b(c, f = p(f, d, l, c, s[h + 15], 22, 1236535329), d, l, s[h + 1], 5, -165796510),
+      l = b(l, c, f, d, s[h + 6], 9, -1069501632),
+      d = b(d, l, c, f, s[h + 11], 14, 643717713),
+      f = b(f, d, l, c, s[h + 0], 20, -373897302),
+      c = b(c, f, d, l, s[h + 5], 5, -701558691),
+      l = b(l, c, f, d, s[h + 10], 9, 38016083),
+      d = b(d, l, c, f, s[h + 15], 14, -660478335),
+      f = b(f, d, l, c, s[h + 4], 20, -405537848),
+      c = b(c, f, d, l, s[h + 9], 5, 568446438),
+      l = b(l, c, f, d, s[h + 14], 9, -1019803690),
+      d = b(d, l, c, f, s[h + 3], 14, -187363961),
+      f = b(f, d, l, c, s[h + 8], 20, 1163531501),
+      c = b(c, f, d, l, s[h + 13], 5, -1444681467),
+      l = b(l, c, f, d, s[h + 2], 9, -51403784),
+      d = b(d, l, c, f, s[h + 7], 14, 1735328473),
+      c = y(c, f = b(f, d, l, c, s[h + 12], 20, -1926607734), d, l, s[h + 5], 4, -378558),
+      l = y(l, c, f, d, s[h + 8], 11, -2022574463),
+      d = y(d, l, c, f, s[h + 11], 16, 1839030562),
+      f = y(f, d, l, c, s[h + 14], 23, -35309556),
+      c = y(c, f, d, l, s[h + 1], 4, -1530992060),
+      l = y(l, c, f, d, s[h + 4], 11, 1272893353),
+      d = y(d, l, c, f, s[h + 7], 16, -155497632),
+      f = y(f, d, l, c, s[h + 10], 23, -1094730640),
+      c = y(c, f, d, l, s[h + 13], 4, 681279174),
+      l = y(l, c, f, d, s[h + 0], 11, -358537222),
+      d = y(d, l, c, f, s[h + 3], 16, -722521979),
+      f = y(f, d, l, c, s[h + 6], 23, 76029189),
+      c = y(c, f, d, l, s[h + 9], 4, -640364487),
+      l = y(l, c, f, d, s[h + 12], 11, -421815835),
+      d = y(d, l, c, f, s[h + 15], 16, 530742520),
+      c = m(c, f = y(f, d, l, c, s[h + 2], 23, -995338651), d, l, s[h + 0], 6, -198630844),
+      l = m(l, c, f, d, s[h + 7], 10, 1126891415),
+      d = m(d, l, c, f, s[h + 14], 15, -1416354905),
+      f = m(f, d, l, c, s[h + 5], 21, -57434055),
+      c = m(c, f, d, l, s[h + 12], 6, 1700485571),
+      l = m(l, c, f, d, s[h + 3], 10, -1894986606),
+      d = m(d, l, c, f, s[h + 10], 15, -1051523),
+      f = m(f, d, l, c, s[h + 1], 21, -2054922799),
+      c = m(c, f, d, l, s[h + 8], 6, 1873313359),
+      l = m(l, c, f, d, s[h + 15], 10, -30611744),
+      d = m(d, l, c, f, s[h + 6], 15, -1560198380),
+      f = m(f, d, l, c, s[h + 13], 21, 1309151649),
+      c = m(c, f, d, l, s[h + 4], 6, -145523070),
+      l = m(l, c, f, d, s[h + 11], 10, -1120210379),
+      d = m(d, l, c, f, s[h + 2], 15, 718787259),
+      f = m(f, d, l, c, s[h + 9], 21, -343485551),
+      c = c + g >>> 0,
+      f = f + v >>> 0,
+      d = d + _ >>> 0,
+      l = l + w >>> 0
+  }
+  return t.endian([c, f, d, l])
+};
+
+
+a._ff = function (e, t, r, n, i, o, a) {
+  var s = e + (t & r | ~t & n) + (i >>> 0) + a;
+  return (s << o | s >>> 32 - o) + t
+}
+  ,
+  a._gg = function (e, t, r, n, i, o, a) {
+    var s = e + (t & n | r & ~n) + (i >>> 0) + a;
+    return (s << o | s >>> 32 - o) + t
+  }
+  ,
+  a._hh = function (e, t, r, n, i, o, a) {
+    var s = e + (t ^ r ^ n) + (i >>> 0) + a;
+    return (s << o | s >>> 32 - o) + t
+  }
+  ,
+  a._ii = function (e, t, r, n, i, o, a) {
+    var s = e + (r ^ (t | ~n)) + (i >>> 0) + a;
+    return (s << o | s >>> 32 - o) + t
+  }
+
+function n(e) {
+  var t = rr["00d8"]()
+    , n = rr["9a63"]().utf8
+    , i = rr["8349"]()
+    , o = rr["9a63"]().bin
+  if (null == e)
+    throw new Error("Illegal argument " + e);
+  var n = t.wordsToBytes(a(e));
+  return t.bytesToHex(n)
+}
+
+
+// 第一个参数是请求地址及参数,第二个参数暂时不用传
+function u_sign(e, t ) {
+  var r, o = "9SASji5OWnG41iRKiSvTJHlXHmRySRp1", a = "", s = t || {}, u = (e = e || "").split("?");
+  if (u.length > 0 && (r = u[1]),
+    r) {
+    var c = r.split("&")
+      , f = "";
+    c.forEach((function (e) {
+      var t = e.split("=");
+      f += "".concat(t[0], "=").concat(encodeURI(t[1]), "&")
+    }
+    )),
+      a = "".concat(xa(f, "&"), "&").concat(o)
+  } else
+    a = Object.keys(s).length > 0 ? "".concat(JSON.stringify(s), "&").concat(o) : "&".concat(o);
+  return a = a.toLowerCase(),
+    n(a)
+}
+
+// 示例:
+// u_sign('/youzy.dms.basiclib.api.career.job.hits.inc?jobCode=02030502')