Bläddra i källkod

添加书籍查询接口

lijinjin 3 år sedan
förälder
incheckning
1cde9512c7
20 ändrade filer med 1325 tillägg och 3 borttagningar
  1. 2 1
      .gitignore
  2. 32 0
      book-server/src/main/java/com/book/server/advice/ExceptionAdvice.java
  3. 55 0
      book-server/src/main/java/com/book/server/aspect/LogAspect.java
  4. 57 0
      book-server/src/main/java/com/book/server/aspect/TxAspect.java
  5. 36 0
      book-server/src/main/java/com/book/server/common/entity/PageResult.java
  6. 83 0
      book-server/src/main/java/com/book/server/common/entity/Result.java
  7. 35 0
      book-server/src/main/java/com/book/server/common/entity/ResultCode.java
  8. 96 0
      book-server/src/main/java/com/book/server/common/util/DateUtils.java
  9. 4 0
      book-server/src/main/java/com/book/server/common/util/EmailUtils.java
  10. 38 0
      book-server/src/main/java/com/book/server/common/util/FileUtils.java
  11. 369 0
      book-server/src/main/java/com/book/server/common/util/HttpUtils.java
  12. 97 0
      book-server/src/main/java/com/book/server/common/util/JsonUtils.java
  13. 232 0
      book-server/src/main/java/com/book/server/common/util/SnowflakeIdWorker.java
  14. 80 0
      book-server/src/main/java/com/book/server/common/util/ThreadPoolUtils.java
  15. 17 0
      book-server/src/main/java/com/book/server/controller/BaseController.java
  16. 30 0
      book-server/src/main/java/com/book/server/controller/BookController.java
  17. 2 2
      book-server/src/main/java/com/book/server/dao/entity/Book.java
  18. 19 0
      book-server/src/main/java/com/book/server/model/VO/QueryVO.java
  19. 9 0
      book-server/src/main/java/com/book/server/service/BookService.java
  20. 32 0
      book-server/src/main/java/com/book/server/service/impl/BookServiceImpl.java

+ 2 - 1
.gitignore

@@ -3,4 +3,5 @@ target
 .settings
 *.class
 .history
-*.iml
+*.iml
+*.log

+ 32 - 0
book-server/src/main/java/com/book/server/advice/ExceptionAdvice.java

@@ -0,0 +1,32 @@
+package com.book.server.advice;
+
+import com.book.server.common.entity.Result;
+import com.book.server.common.entity.ResultCode;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+/**
+ * Class ExceptionHandle ...
+ *s
+ * @author tianyunperfect
+ * Created on 2019/9/19
+ */
+@ControllerAdvice
+@Slf4j
+public class ExceptionAdvice {
+
+    /**
+     * 统一异常处理
+     * @param e 异常
+     * @return Result
+     */
+    @ResponseBody
+    @ExceptionHandler
+    public Result exceptionHandle(Exception e) {
+        e.printStackTrace();
+        log.error("系统异常:{}", e.toString());
+        return Result.failure(ResultCode.UNKNOWN.ordinal(), e.getMessage());
+    }
+}

+ 55 - 0
book-server/src/main/java/com/book/server/aspect/LogAspect.java

@@ -0,0 +1,55 @@
+package com.book.server.aspect;
+
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * 日志切片
+ *
+ * @author tianyunperfect
+ * Created on 2019/9/19
+ */
+@Component
+@Aspect
+@Slf4j
+public class LogAspect {
+
+    @Pointcut("execution(public * com.book.server.controller..*.*(..))")
+    public void webLog() {
+    }
+
+    @Around("webLog()")
+    public Object webAop(ProceedingJoinPoint joinPoint) throws Throwable {
+        /*
+         * 执行方法之前
+         */
+        long start = System.currentTimeMillis();
+        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
+        /*
+         * 执行方法
+         */
+        Object result = joinPoint.proceed();
+        /*
+         * 执行方法之后
+         */
+        String split = "; ";
+        String sb = "url=" + request.getRequestURL() + split +
+                "method=" + request.getMethod() + split +
+                "class_method=" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName() + split +
+                "args=" + Arrays.toString(joinPoint.getArgs()) + split +
+                "consumer time(ms) = " + (System.currentTimeMillis() - start);
+        log.info(sb);
+
+        return result;
+    }
+}

+ 57 - 0
book-server/src/main/java/com/book/server/aspect/TxAspect.java

@@ -0,0 +1,57 @@
+package com.book.server.aspect;
+
+import org.aspectj.lang.annotation.Aspect;
+import org.springframework.aop.Advisor;
+import org.springframework.aop.aspectj.AspectJExpressionPointcut;
+import org.springframework.aop.support.DefaultPointcutAdvisor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.TransactionDefinition;
+import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
+import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
+import org.springframework.transaction.interceptor.TransactionInterceptor;
+
+@Aspect
+@Configuration
+public class TxAspect {
+    private static final String AOP_POINTCUT_EXPRESSION = "execution(* com.book.server.service.*.*(..))";
+
+    @Autowired
+    private PlatformTransactionManager transactionManager;
+
+    @Bean
+    public TransactionInterceptor txAdvice() {
+
+        DefaultTransactionAttribute txAttr_REQUIRED = new DefaultTransactionAttribute();
+        txAttr_REQUIRED.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
+
+        DefaultTransactionAttribute txAttr_REQUIRED_READONLY = new DefaultTransactionAttribute();
+        txAttr_REQUIRED_READONLY.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
+        txAttr_REQUIRED_READONLY.setReadOnly(true);
+
+        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
+
+        source.addTransactionalMethod("save*", txAttr_REQUIRED);
+        source.addTransactionalMethod("delete*", txAttr_REQUIRED);
+        source.addTransactionalMethod("update*", txAttr_REQUIRED);
+        source.addTransactionalMethod("exec*", txAttr_REQUIRED);
+        source.addTransactionalMethod("set*", txAttr_REQUIRED);
+        source.addTransactionalMethod("get*", txAttr_REQUIRED_READONLY);
+        source.addTransactionalMethod("query*", txAttr_REQUIRED_READONLY);
+        source.addTransactionalMethod("find*", txAttr_REQUIRED_READONLY);
+        source.addTransactionalMethod("list*", txAttr_REQUIRED_READONLY);
+        source.addTransactionalMethod("count*", txAttr_REQUIRED_READONLY);
+        source.addTransactionalMethod("is*", txAttr_REQUIRED_READONLY);
+
+        return new TransactionInterceptor(transactionManager, source);
+    }
+
+    @Bean
+    public Advisor txAdviceAdvisor() {
+        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
+        pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
+        return new DefaultPointcutAdvisor(pointcut, txAdvice());
+    }
+}

+ 36 - 0
book-server/src/main/java/com/book/server/common/entity/PageResult.java

@@ -0,0 +1,36 @@
+package com.book.server.common.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 分页模板
+ *
+ * @author tianyunperfect
+ * @date 2020/05/20
+ */
+@Data
+@AllArgsConstructor
+public class PageResult<T> {
+    /**
+     * 第几页
+     *
+     * @mock 1
+     */
+    private Integer currentPage;
+    /**
+     * 每页数量
+     *
+     * @mock 10
+     */
+    private Integer pageSize;
+    /**
+     * 总数
+     *
+     * @mock @integer(9,99)
+     */
+    private Long total;
+    private List<T> list;
+}

+ 83 - 0
book-server/src/main/java/com/book/server/common/entity/Result.java

@@ -0,0 +1,83 @@
+package com.book.server.common.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * @Description
+ * @Author 田云
+ * @Date 2020/5/1 20:54
+ * @Version 1.0
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class Result<T> implements Serializable {
+    private boolean success;
+    private Integer code;
+    private String message;
+    private T data;
+
+    public static <T> Result<T> result(Boolean flag, ResultCode resultCode, T data) {
+        return new Result<>(flag, resultCode.getCode(), resultCode.getMessage(), data);
+    }
+
+    //region success封装
+    public static <T> Result<T> success(ResultCode resultCode, T data) {
+        return result(true, resultCode, data);
+    }
+
+    public static <T> Result<T> success(T data) {
+        return success(ResultCode.SUCCESS, data);
+    }
+
+    public static <T> Result<T> success() {
+        return success(null);
+    }
+    //endregion
+
+
+    //region failure封装
+    public static <T> Result<T> failure(Integer code, String message) {
+        return new Result<>(false, code, message, null);
+    }
+
+    public static <T> Result<T> failure(ResultCode resultCode) {
+        return new Result<>(false, resultCode.getCode(), resultCode.getMessage(), null);
+    }
+
+    public static <T> Result<T> failure() {
+        return failure(ResultCode.FAIL);
+    }
+    //endregion
+
+    public static <T> Result<T> byObject(T t) {
+        return byObject(t,ResultCode.UNKNOWN);
+    }
+    /**
+     * 通过对象输出结果
+     * 当对象为 true 或者不为 null 的时候返回正常
+     *
+     * @param t          t
+     * @param resultCode 结果代码
+     * @return {@link Result<T>}
+     */
+    public static <T> Result<T> byObject(T t, ResultCode resultCode) {
+        if (t instanceof Boolean) {
+            if ((Boolean) t) {
+                return Result.success();
+            }
+        } else if (t != null) {
+            return Result.success(t);
+        }
+        return Result.failure(resultCode);
+    }
+
+    public static void main(String[] args) {
+        System.out.println(Result.byObject(null, ResultCode.UNKNOWN));
+        System.out.println(Result.byObject(false, ResultCode.UNKNOWN));
+    }
+}

+ 35 - 0
book-server/src/main/java/com/book/server/common/entity/ResultCode.java

@@ -0,0 +1,35 @@
+package com.book.server.common.entity;
+
+public enum ResultCode {
+    // 基本
+    SUCCESS(0, "success"),
+    FAIL(-1, "fail"),
+    UNKNOWN(-2, "unknown"),
+
+    ;
+
+    private Integer code;
+    private String message;
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public void setCode(Integer code) {
+        this.code = code;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    ResultCode(Integer code, String message) {
+        this.code = code;
+        this.message = message;
+    }
+
+}

+ 96 - 0
book-server/src/main/java/com/book/server/common/util/DateUtils.java

@@ -0,0 +1,96 @@
+package com.book.server.common.util;
+
+import org.springframework.util.Assert;
+
+import java.time.*;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.TemporalAdjusters;
+
+/**
+ * 日期工具类
+ * 日期大小比较
+ * 日期加减
+ * 时间戳转换
+ *
+ * @author tianyunperfect
+ * @date 2020/05/28
+ */
+public class DateUtils {
+
+    /**
+     * 当前秒
+     *
+     * @return {@link Long}
+     */
+    public static Long getEpochMilli() {
+        return Instant.now().toEpochMilli();
+    }
+
+    /**
+     * 当前秒
+     *
+     * @return {@link Long}
+     */
+    public static Long getEpochSecond() {
+        return Instant.now().getEpochSecond();
+    }
+
+    /**
+     * 将Long类型的时间戳转换成String 类型的时间格式,时间格式为:yyyy-MM-dd HH:mm:ss
+     */
+    public static String convertTimeToString(Long time) {
+        Assert.notNull(time, "time is null");
+        DateTimeFormatter ftf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+        return ftf.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(time), ZoneId.systemDefault()));
+    }
+
+    /**
+     * 将字符串转日期成Long类型的时间戳,格式为:yyyy-MM-dd HH:mm:ss
+     */
+    public static Long convertTimeToLong(String time) {
+        Assert.notNull(time, "time is null");
+        DateTimeFormatter ftf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+        LocalDateTime parse = LocalDateTime.parse("2018-05-29 13:52:50", ftf);
+        return LocalDateTime.from(parse).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
+    }
+
+    /**
+     * 取本月第一天
+     */
+    public static LocalDate firstDayOfThisMonth() {
+        LocalDate today = LocalDate.now();
+        return today.with(TemporalAdjusters.firstDayOfMonth());
+    }
+
+    /**
+     * 取本月第N天
+     */
+    public static LocalDate dayOfThisMonth(int n) {
+        LocalDate today = LocalDate.now();
+        return today.withDayOfMonth(n);
+    }
+
+    /**
+     * 取本月最后一天
+     */
+    public static LocalDate lastDayOfThisMonth() {
+        LocalDate today = LocalDate.now();
+        return today.with(TemporalAdjusters.lastDayOfMonth());
+    }
+
+    /**
+     * 取本月第一天的开始时间
+     */
+    public static LocalDateTime startOfThisMonth() {
+        return LocalDateTime.of(firstDayOfThisMonth(), LocalTime.MIN);
+    }
+
+
+    /**
+     * 取本月最后一天的结束时间
+     */
+    public static LocalDateTime endOfThisMonth() {
+        return LocalDateTime.of(lastDayOfThisMonth(), LocalTime.MAX);
+    }
+
+}

+ 4 - 0
book-server/src/main/java/com/book/server/common/util/EmailUtils.java

@@ -0,0 +1,4 @@
+package com.book.server.common.util;
+
+public class EmailUtils {
+}

+ 38 - 0
book-server/src/main/java/com/book/server/common/util/FileUtils.java

@@ -0,0 +1,38 @@
+package com.book.server.common.util;
+
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 常用文件工具类
+ * 1、读取所有行请使用common-io工具类
+ * 2、读取单行数据可以使用
+ *
+ * @author tianyunperfect
+ * @date 2021/01/15
+ */
+public class FileUtils {
+    /**
+     * 获取到 BF,一定要手动关闭
+     *
+     * @param filePath 文件路径
+     * @param encode   编码
+     * @return {@link BufferedReader}* @throws FileNotFoundException 文件未发现异常
+     */
+    public static BufferedReader getBufferedReader(String filePath, String encode) throws FileNotFoundException, UnsupportedEncodingException {
+        return new BufferedReader(
+                new InputStreamReader(
+                        new FileInputStream(filePath), encode));
+    }
+
+    /**
+     * 获取到 BF,一定要手动关闭,默认UTF-8
+     *
+     * @param filePath 文件路径
+     * @return {@link BufferedReader}* @throws FileNotFoundException 文件未发现异常
+     * @throws UnsupportedEncodingException 不支持的编码异常
+     */
+    public static BufferedReader getBufferedReader(String filePath) throws FileNotFoundException, UnsupportedEncodingException {
+        return getBufferedReader(filePath, StandardCharsets.UTF_8.name());
+    }
+}

+ 369 - 0
book-server/src/main/java/com/book/server/common/util/HttpUtils.java

@@ -0,0 +1,369 @@
+package com.book.server.common.util;
+
+import com.google.gson.Gson;
+import lombok.Data;
+import lombok.experimental.Accessors;
+import org.apache.http.*;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.BasicCookieStore;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.cookie.BasicClientCookie;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.util.EntityUtils;
+
+import java.io.BufferedOutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 我的utils封装
+ * 读取设置:url、cookies、params
+ * 读取:response
+ * 设置 proxy、header、超时时间
+ *
+ * @author Mark
+ */
+
+@Data
+@Accessors(chain = true)
+public class HttpUtils {
+
+
+    /** 代理  */
+    private HttpHost proxy;
+
+    /** 超时时间 5秒  */
+    private Integer connectTimeout = 5000;
+
+    /** header  */
+    private HashMap<String, String> headerMap;
+
+    /** urlStr  */
+    private String urlStr;
+
+    private BasicCookieStore cookieStore = new BasicCookieStore();
+
+    private RequestConfig config;
+
+    private HttpResponse httpResponse;
+    /**
+     * params参数
+     */
+    private List<NameValuePair> nameValuePairs = new ArrayList<>();
+
+    public HttpUtils(String urlStr) {
+        // 5秒
+        this.urlStr = urlStr;
+    }
+
+    /**
+     * 给url设置参数
+     *
+     * @param paramsMap
+     * @throws URISyntaxException
+     */
+    public HttpUtils setParams(HashMap<String, String> paramsMap){
+        for (Map.Entry<String, String> entry : paramsMap.entrySet()) {
+            nameValuePairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
+        }
+        return this;
+    }
+
+    /**
+     * 获取get请求结果
+     *
+     * @return
+     * @throws IOException
+     */
+    public String get() throws Exception {
+        return getExecuteResult(getHttpGet());
+    }
+
+    /**
+     * 获取post请求结果
+     *
+     * @param dataMap
+     * @return
+     * @throws IOException
+     */
+    public String post(HashMap<String, String> dataMap) throws Exception {
+        HttpPost httpPost = getHttpPost();
+
+        //解决中文乱码
+        httpPost.setHeader("Content-Type", "text/html; charset=UTF-8");
+        // 添加data
+        if (dataMap != null) {
+            List<NameValuePair> nameValuePairs = new ArrayList<>();
+            for (Map.Entry<String, String> entry : dataMap.entrySet()) {
+                nameValuePairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
+            }
+            UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(nameValuePairs, "utf-8");
+            httpPost.setEntity(urlEncodedFormEntity);
+        }
+        return getExecuteResult(httpPost);
+    }
+
+    /**
+     * 发送json请求
+     * @param object
+     * @return
+     * @throws Exception
+     */
+    public String postJson(Object object) throws Exception {
+        HttpPost httpPost = getHttpPost();
+        //解决中文乱码
+        httpPost.setHeader("Content-Type", "application/json;charset=UTF-8");
+        if (object != null) {
+            StringEntity entity = new StringEntity(new Gson().toJson(object), "utf-8");
+            httpPost.setEntity(entity);
+        }
+        return getExecuteResult(httpPost);
+    }
+
+    /**
+     * 处理get和post请求,返回html
+     *
+     * @param httpUriRequest
+     * @return
+     * @throws IOException
+     */
+    private String getExecuteResult(HttpUriRequest httpUriRequest) throws IOException {
+        String returnStr;
+        //获取一个链接
+        CloseableHttpClient httpClient = HttpClients.custom()
+                //.setConnectionManager(connectionManager)
+                .setDefaultCookieStore(cookieStore).build();
+
+        //设置header
+        httpUriRequest.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36");
+        if (headerMap != null) {
+            for (Map.Entry<String, String> entry : headerMap.entrySet()) {
+                httpUriRequest.setHeader(entry.getKey(), entry.getValue());
+            }
+        }
+
+        httpResponse = httpClient.execute(httpUriRequest);
+        //获取返回结果中的实体
+        HttpEntity entity = httpResponse.getEntity();
+        //判断是否失败
+        if (httpResponse.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
+            returnStr = null;
+        } else {
+            //查看页面内容结果
+            String rawHTMLContent = EntityUtils.toString(entity, "utf-8");
+            returnStr = decodeUnicode(rawHTMLContent);
+        }
+        //关闭HttpEntity流
+        EntityUtils.consume(entity);
+        return returnStr;
+    }
+
+    /**
+     * 设置cookies
+     *
+     * @param cookieMap
+     */
+    public void setCookies(HashMap<String, String> cookieMap) {
+        for (Map.Entry<String, String> entry : cookieMap.entrySet()) {
+            BasicClientCookie clientCookie = new BasicClientCookie(entry.getKey(), entry.getValue());
+            cookieStore.addCookie(clientCookie);
+        }
+    }
+
+    /**
+     * 获取cookies
+     *
+     * @return
+     */
+    public List<Cookie> getCookies() {
+        return cookieStore.getCookies();
+    }
+
+    private HttpGet getHttpGet() throws Exception {
+        URIBuilder uriBuilder = new URIBuilder(urlStr);
+        if (nameValuePairs.size() > 0) {
+            uriBuilder.addParameters(nameValuePairs);
+        }
+        URI uri = uriBuilder.build();
+        HttpGet httpGet = new HttpGet(uri);
+
+        httpGet.setConfig(getConfig());
+        return httpGet;
+    }
+
+    private HttpPost getHttpPost() throws Exception {
+        URIBuilder uriBuilder = new URIBuilder(urlStr);
+        if (nameValuePairs.size() > 0) {
+            uriBuilder.addParameters(nameValuePairs);
+        }
+        URI uri = uriBuilder.build();
+        HttpPost httpPost = new HttpPost(uri);
+        httpPost.setConfig(getConfig());
+        return httpPost;
+    }
+
+    public boolean getDownLoad(String filePath) throws Exception {
+        return downLoadFile(getHttpGet(), filePath);
+    }
+    public boolean PostDownLoad(String filePath) throws Exception {
+        return downLoadFile(getHttpPost(), filePath);
+    }
+    /**
+     * 下载文件到指定路径
+     *
+     * @param httpUriRequest
+     * @param filePath
+     * @return
+     */
+    private boolean downLoadFile(HttpUriRequest httpUriRequest, String filePath) throws IOException {
+        CloseableHttpClient httpClient = HttpClients.custom()
+                //.setConnectionManager(connectionManager)
+                .setDefaultCookieStore(cookieStore).build();
+
+        HttpResponse httpResponse = httpClient.execute(httpUriRequest);
+        //判断是否失败
+        if (httpResponse.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
+            return false;
+        }
+        //获取返回结果中的实体
+        HttpEntity entity = httpResponse.getEntity();
+
+        //存储
+        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath));
+        entity.writeTo(bos);
+
+        bos.close();
+        //关闭HttpEntity流
+        EntityUtils.consume(entity);
+        return true;
+    }
+
+
+
+    /**
+     * 设置代理服务器
+     *
+     * @param ip
+     * @param port
+     * @param httpType
+     */
+    public HttpUtils setProxy(String ip, int port, String httpType) {
+        this.proxy = new HttpHost(ip, port, httpType);
+        return this;
+    }
+
+    private RequestConfig getConfig() {
+        RequestConfig.Builder custom = RequestConfig.custom();
+        if (proxy != null) {
+            custom.setProxy(proxy);
+        }
+        /**
+         * 设置超时时间
+         */
+        custom.setConnectTimeout(connectTimeout)
+                .setSocketTimeout(connectTimeout)
+                .setConnectionRequestTimeout(connectTimeout);
+
+        return custom.build();
+    }
+
+    /**
+     * 解析string里面的字符串,因为只有部分需要解析,所有需要判断
+     *
+     * @param s 字符串
+     * @return
+     */
+    public String decodeUnicode(String s) {
+        char aChar;
+        int len = s.length();
+        StringBuffer outBuffer = new StringBuffer(len);
+        for (int x = 0; x < len; ) {
+            aChar = s.charAt(x++);
+            if (aChar == '\\') {
+                aChar = s.charAt(x++);
+                if (aChar == 'u') {
+                    // Read the xxxx
+                    int value = 0;
+                    for (int i = 0; i < 4; i++) {
+                        aChar = s.charAt(x++);
+                        switch (aChar) {
+                            case '0':
+                            case '1':
+                            case '2':
+                            case '3':
+                            case '4':
+                            case '5':
+                            case '6':
+                            case '7':
+                            case '8':
+                            case '9':
+                                value = (value << 4) + aChar - '0';
+                                break;
+                            case 'a':
+                            case 'b':
+                            case 'c':
+                            case 'd':
+                            case 'e':
+                            case 'f':
+                                value = (value << 4) + 10 + aChar - 'a';
+                                break;
+                            case 'A':
+                            case 'B':
+                            case 'C':
+                            case 'D':
+                            case 'E':
+                            case 'F':
+                                value = (value << 4) + 10 + aChar - 'A';
+                                break;
+                            default:
+                                throw new IllegalArgumentException(
+                                        "Malformed   \\uxxxx   encoding.");
+                        }
+
+                    }
+                    outBuffer.append((char) value);
+                } else {
+                    switch (aChar) {
+                        case 't':
+                            aChar = '\t';
+                            break;
+                        case 'r':
+                            aChar = '\r';
+                            break;
+                        case 'n':
+                            aChar = '\n';
+                            break;
+                        case 'f':
+                            aChar = '\f';
+                            break;
+                        default:
+                            break;
+                    }
+                    outBuffer.append(aChar);
+                }
+            } else {
+                outBuffer.append(aChar);
+            }
+        }
+        return outBuffer.toString();
+    }
+
+    public static void main(String[] args) throws Exception {
+        String s = new HttpUtils("https://www.baidu.com").get();
+        System.out.println(s);
+    }
+}

+ 97 - 0
book-server/src/main/java/com/book/server/common/util/JsonUtils.java

@@ -0,0 +1,97 @@
+package com.book.server.common.util;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.reflect.TypeToken;
+
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * json工具类
+ * https://www.yuque.com/tianyunperfect/ygzsw4/bv1mlg
+ * <p>
+ * JsonUtil.getObject(str,JsonHello.class)
+ *
+ * @author tianyunperfect
+ * @date 2020/05/20
+ */
+public class JsonUtils {
+    private static final Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
+
+    public JsonUtils() {
+    }
+
+    /**
+     * 转换为json字符串
+     *
+     * @param object 对象
+     * @return {@link String}
+     */
+    public static String toJsonStr(Object object) {
+        return gson.toJson(object);
+    }
+
+
+    /**
+     * 将json字符串 转换为 普通类
+     *
+     * @param jsonStr json str
+     * @param clazz   clazz
+     * @return {@link T}
+     */
+    public static <T> T getObject(String jsonStr, Class<T> clazz) {
+        return gson.fromJson(jsonStr, clazz);
+    }
+
+    /**
+     * 支持泛型等复杂类型
+     * new TypeToken<Result<List<Integer>>>(){}
+     * new TypeToken<List<Map<String, T>>>() {}
+     *
+     * @param jsonString json字符串
+     * @param typeToken  令牌类型
+     * @return {@link T}
+     */
+    public static <T> T getObject(String jsonString, TypeToken typeToken) {
+
+        return gson.fromJson(jsonString, typeToken.getType());
+    }
+
+    /**
+     * 转为数组
+     *
+     * @param jsonString
+     * @param tClass
+     * @param <T>
+     * @return
+     */
+    public static <T> T[] getArray(String jsonString, Class<T> tClass) {
+        return gson.fromJson(jsonString, TypeToken.getArray(tClass).getType());
+    }
+
+    /**
+     * 转为list
+     *
+     * @param jsonString
+     * @param tClass
+     * @param <T>
+     * @return
+     */
+    public static <T> List<T> getList(String jsonString, Class<T> tClass) {
+        return gson.fromJson(jsonString, new TypeToken<List<T>>() {
+        }.getType());
+    }
+
+    /**
+     * json字符串转成map的
+     *
+     * @param gsonString
+     * @return
+     */
+    public static <T> Map<String, T> getMap(String gsonString) {
+        return gson.fromJson(gsonString, new TypeToken<Map<String, T>>() {
+        }.getType());
+    }
+}

+ 232 - 0
book-server/src/main/java/com/book/server/common/util/SnowflakeIdWorker.java

@@ -0,0 +1,232 @@
+package com.book.server.common.util;
+
+import org.apache.commons.lang3.RandomUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.SystemUtils;
+
+import java.net.Inet4Address;
+import java.net.UnknownHostException;
+
+/**
+ * !!!根据hostName和IP自动生成workerID和centerId,但是当这两个值的修改同时相差32时,就会出现错误,那个时候建议从redis或者数据库中获取。
+ *
+ * Twitter_Snowflake<br>
+ * SnowFlake的结构如下(每部分用-分开):<br>
+ * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>
+ * 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br>
+ * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
+ * 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>
+ * 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId<br>
+ * 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号<br>
+ * 加起来刚好64位,为一个Long型。<br>
+ * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
+ */
+public class SnowflakeIdWorker {
+
+    // ==============================Fields===========================================
+    /**
+     * 开始时间截 (2015-01-01)
+     */
+    private final long twepoch = 1489111610226L;
+
+    /**
+     * 机器id所占的位数
+     */
+    private final long workerIdBits = 5L;
+
+    /**
+     * 数据标识id所占的位数
+     */
+    private final long dataCenterIdBits = 5L;
+
+    /**
+     * 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
+     */
+    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
+
+    /**
+     * 支持的最大数据标识id,结果是31
+     */
+    private final long maxDataCenterId = -1L ^ (-1L << dataCenterIdBits);
+
+    /**
+     * 序列在id中占的位数
+     */
+    private final long sequenceBits = 12L;
+
+    /**
+     * 机器ID向左移12位
+     */
+    private final long workerIdShift = sequenceBits;
+
+    /**
+     * 数据标识id向左移17位(12+5)
+     */
+    private final long dataCenterIdShift = sequenceBits + workerIdBits;
+
+    /**
+     * 时间截向左移22位(5+5+12)
+     */
+    private final long timestampLeftShift = sequenceBits + workerIdBits + dataCenterIdBits;
+
+    /**
+     * 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
+     */
+    private final long sequenceMask = -1L ^ (-1L << sequenceBits);
+
+    /**
+     * 工作机器ID(0~31)
+     */
+    private long workerId;
+
+    /**
+     * 数据中心ID(0~31)
+     */
+    private long dataCenterId;
+
+    /**
+     * 毫秒内序列(0~4095)
+     */
+    private long sequence = 0L;
+
+    /**
+     * 上次生成ID的时间截
+     */
+    private long lastTimestamp = -1L;
+
+    private static final SnowflakeIdWorker idWorker;
+
+    static {
+        idWorker = new SnowflakeIdWorker(getWorkId(), getDataCenterId());
+    }
+
+    //==============================Constructors=====================================
+
+    /**
+     * 构造函数
+     *
+     * @param workerId     工作ID (0~31)
+     * @param dataCenterId 数据中心ID (0~31)
+     */
+    public SnowflakeIdWorker(long workerId, long dataCenterId) {
+        if (workerId > maxWorkerId || workerId < 0) {
+            throw new IllegalArgumentException(String.format("workerId can't be greater than %d or less than 0", maxWorkerId));
+        }
+        if (dataCenterId > maxDataCenterId || dataCenterId < 0) {
+            throw new IllegalArgumentException(String.format("dataCenterId can't be greater than %d or less than 0", maxDataCenterId));
+        }
+        this.workerId = workerId;
+        this.dataCenterId = dataCenterId;
+    }
+
+    // ==============================Methods==========================================
+
+    /**
+     * 获得下一个ID (该方法是线程安全的)
+     *
+     * @return SnowflakeId
+     */
+    public synchronized long nextId() {
+        long timestamp = timeGen();
+
+        //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
+        if (timestamp < lastTimestamp) {
+            throw new RuntimeException(
+                    String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
+        }
+
+        //如果是同一时间生成的,则进行毫秒内序列
+        if (lastTimestamp == timestamp) {
+            sequence = (sequence + 1) & sequenceMask;
+            //毫秒内序列溢出
+            if (sequence == 0) {
+                //阻塞到下一个毫秒,获得新的时间戳
+                timestamp = tilNextMillis(lastTimestamp);
+            }
+        }
+        //时间戳改变,毫秒内序列重置
+        else {
+            sequence = 0L;
+        }
+
+        //上次生成ID的时间截
+        lastTimestamp = timestamp;
+
+        //移位并通过或运算拼到一起组成64位的ID
+        return ((timestamp - twepoch) << timestampLeftShift)
+                | (dataCenterId << dataCenterIdShift)
+                | (workerId << workerIdShift)
+                | sequence;
+    }
+
+    /**
+     * 阻塞到下一个毫秒,直到获得新的时间戳
+     *
+     * @param lastTimestamp 上次生成ID的时间截
+     * @return 当前时间戳
+     */
+    protected long tilNextMillis(long lastTimestamp) {
+        long timestamp = timeGen();
+        while (timestamp <= lastTimestamp) {
+            timestamp = timeGen();
+        }
+        return timestamp;
+    }
+
+    /**
+     * 返回以毫秒为单位的当前时间
+     *
+     * @return 当前时间(毫秒)
+     */
+    protected long timeGen() {
+        return System.currentTimeMillis();
+    }
+
+    private static Long getWorkId() {
+        try {
+            String hostAddress = Inet4Address.getLocalHost().getHostAddress();
+            int[] ints = StringUtils.toCodePoints(hostAddress);
+            int sums = 0;
+            for (int b : ints) {
+                sums += b;
+            }
+            return (long) (sums % 32);
+        } catch (UnknownHostException e) {
+            // 如果获取失败,则使用随机数备用
+            return RandomUtils.nextLong(0, 32);
+        }
+    }
+
+    private static Long getDataCenterId() {
+        int[] ints = StringUtils.toCodePoints(SystemUtils.getHostName());
+        int sums = 0;
+        for (int i : ints) {
+            sums += i;
+        }
+        return (long) (sums % 32);
+    }
+
+
+    /**
+     * 静态工具类
+     *
+     * @return
+     */
+    public static Long generateId() {
+        return idWorker.nextId();
+    }
+
+    //==============================Test=============================================
+
+    /**
+     * 测试
+     */
+    public static void main(String[] args) throws UnknownHostException {
+        long startTime = System.nanoTime();
+        for (int i = 0; i < 500000; i++) {
+            long id = SnowflakeIdWorker.generateId();
+            //System.out.println(id);
+        }
+        System.out.println((System.nanoTime() - startTime) / 1000000 + "ms");
+    }
+}

+ 80 - 0
book-server/src/main/java/com/book/server/common/util/ThreadPoolUtils.java

@@ -0,0 +1,80 @@
+package com.book.server.common.util;
+
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * 线程池工具类
+ *
+ * @author tianyunperfect
+ * @date 2021/02/03
+ */
+public class ThreadPoolUtils {
+
+    /**
+     * 获取线程池
+     *
+     * @param corePoolSize    核心池大小
+     * @param maximumPoolSize 最大池大小
+     * @param keepAliveTime   维持时间
+     * @param unit            单位
+     * @param queueSize       队列的大小
+     * @param poolName        池名称
+     * @param handler         拒绝策略
+     * @return {@link ThreadPoolExecutor}
+     */
+    public static ThreadPoolExecutor getThreadPool(
+            int corePoolSize,
+            int maximumPoolSize,
+            long keepAliveTime,
+            TimeUnit unit,
+            int queueSize,
+            String poolName,
+            RejectedExecutionHandler handler) {
+        return new ThreadPoolExecutor(
+                corePoolSize, maximumPoolSize,
+                keepAliveTime, unit,
+                new LinkedBlockingDeque<>(queueSize),
+                new MyThreadFactory(poolName),
+                handler
+        );
+    }
+
+
+    /**
+     * 修改于默认线程池工程类,添加了自定义线程名
+     *
+     * @author tianyunperfect
+     * @date 2021/02/03
+     */
+    public static class MyThreadFactory implements ThreadFactory {
+        private static final AtomicInteger poolNumber = new AtomicInteger(1);
+        private final ThreadGroup group;
+        private final AtomicInteger threadNumber = new AtomicInteger(1);
+        private final String namePrefix;
+
+        MyThreadFactory(String name) {
+            SecurityManager s = System.getSecurityManager();
+            group = (s != null) ? s.getThreadGroup() :
+                    Thread.currentThread().getThreadGroup();
+            //自定义名称
+            if (name == null || name.isEmpty()) {
+                name = "pool";
+            }
+            namePrefix = name + "-" +
+                    poolNumber.getAndIncrement() +
+                    "-thread-";
+        }
+
+        public Thread newThread(Runnable r) {
+            Thread t = new Thread(group, r,
+                    namePrefix + threadNumber.getAndIncrement(),
+                    0);
+            if (t.isDaemon())
+                t.setDaemon(false);
+            if (t.getPriority() != Thread.NORM_PRIORITY)
+                t.setPriority(Thread.NORM_PRIORITY);
+            return t;
+        }
+    }
+}

+ 17 - 0
book-server/src/main/java/com/book/server/controller/BaseController.java

@@ -0,0 +1,17 @@
+package com.book.server.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.CrossOrigin;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@Controller
+@CrossOrigin
+public class BaseController {
+    @Autowired
+    protected HttpServletRequest request;
+    @Autowired
+    protected HttpServletResponse response;
+}

+ 30 - 0
book-server/src/main/java/com/book/server/controller/BookController.java

@@ -0,0 +1,30 @@
+package com.book.server.controller;
+
+import com.book.server.common.entity.PageResult;
+import com.book.server.common.entity.Result;
+import com.book.server.dao.entity.Book;
+import com.book.server.model.VO.QueryVO;
+import com.book.server.service.BookService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 书籍
+ */
+@RestController
+@CrossOrigin
+@RequestMapping("/book")
+public class BookController extends BaseController {
+    @Autowired
+    private BookService bookService;
+
+    /**
+     * 书库查询
+     * @param queryVO
+     * @return
+     */
+    @PostMapping("/query")
+    public PageResult<Book> query(@RequestBody QueryVO queryVO) {
+        return bookService.query(queryVO);
+    }
+}

+ 2 - 2
book-server/src/main/java/com/book/server/dao/entity/Book.java

@@ -284,7 +284,7 @@ public class Book implements Serializable {
      *
      * @mbg.generated Thu Aug 12 00:28:56 CST 2021
      */
-    private Integer readNum;
+    private Long readNum;
 
     /**
      *
@@ -920,7 +920,7 @@ public class Book implements Serializable {
          *
          * @mbg.generated Thu Aug 12 00:28:56 CST 2021
          */
-        public Builder readNum(Integer readNum) {
+        public Builder readNum(Long readNum) {
             obj.setReadNum(readNum);
             return this;
         }

+ 19 - 0
book-server/src/main/java/com/book/server/model/VO/QueryVO.java

@@ -0,0 +1,19 @@
+package com.book.server.model.VO;
+
+import lombok.Data;
+
+@Data
+public class QueryVO {
+    /*
+    查询字段
+     */
+    String query;
+    /*
+    第几页
+     */
+    int page;
+    /*
+    size
+     */
+    int size;
+}

+ 9 - 0
book-server/src/main/java/com/book/server/service/BookService.java

@@ -0,0 +1,9 @@
+package com.book.server.service;
+
+import com.book.server.common.entity.PageResult;
+import com.book.server.dao.entity.Book;
+import com.book.server.model.VO.QueryVO;
+
+public interface BookService {
+    PageResult<Book> query(QueryVO queryVO);
+}

+ 32 - 0
book-server/src/main/java/com/book/server/service/impl/BookServiceImpl.java

@@ -0,0 +1,32 @@
+package com.book.server.service.impl;
+
+import com.book.server.common.entity.PageResult;
+import com.book.server.dao.entity.Book;
+import com.book.server.dao.entity.example.BookExample;
+import com.book.server.dao.mapper.BookMapper;
+import com.book.server.model.VO.QueryVO;
+import com.book.server.service.BookService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class BookServiceImpl implements BookService {
+    @Autowired
+    private BookMapper bookMapper;
+
+    @Override
+    public PageResult<Book> query(QueryVO queryVO) {
+        BookExample example = BookExample.newAndCreateCriteria().example();
+        example.or().andNameLike("%" + queryVO.getQuery() + "%");
+        example.or().andAuthorLike("%" + queryVO.getQuery() + "%");
+        long count = bookMapper.countByExample(example);
+        if (count <= 0) {
+            return new PageResult<>(queryVO.getPage(), queryVO.getSize(), 0L, null);
+        }
+        example.page(queryVO.getPage(), queryVO.getSize());
+        List<Book> books = bookMapper.selectByExample(example);
+        return new PageResult<>(queryVO.getPage(), queryVO.getSize(), count, books);
+    }
+}