Jelajahi Sumber

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	book-server/src/main/java/com/book/server/service/impl/BookServiceImpl.java
tianyunperfect 3 tahun lalu
induk
melakukan
cafce81bd4
48 mengubah file dengan 3468 tambahan dan 60 penghapusan
  1. 35 2
      book-push/pom.xml
  2. 6 0
      book-push/src/main/java/com/book/push/Application.java
  3. 17 0
      book-push/src/main/java/com/book/push/builder/AbstractBuilder.java
  4. 24 0
      book-push/src/main/java/com/book/push/builder/ImageBuilder.java
  5. 22 0
      book-push/src/main/java/com/book/push/builder/TextBuilder.java
  6. 128 0
      book-push/src/main/java/com/book/push/config/WxMpConfiguration.java
  7. 77 0
      book-push/src/main/java/com/book/push/config/WxMpProperties.java
  8. 52 0
      book-push/src/main/java/com/book/push/controller/MenuIndexController.java
  9. 188 0
      book-push/src/main/java/com/book/push/controller/WxMenuController.java
  10. 131 0
      book-push/src/main/java/com/book/push/controller/WxPortalController.java
  11. 68 0
      book-push/src/main/java/com/book/push/controller/WxRedirectController.java
  12. 24 0
      book-push/src/main/java/com/book/push/dao/mapper/CustomMapper.java
  13. 21 0
      book-push/src/main/java/com/book/push/dao/mapper/UserMapper.java
  14. 105 0
      book-push/src/main/java/com/book/push/dao/pojo/Custom.java
  15. 145 0
      book-push/src/main/java/com/book/push/dao/pojo/User.java
  16. 12 0
      book-push/src/main/java/com/book/push/handler/AbstractHandler.java
  17. 25 0
      book-push/src/main/java/com/book/push/handler/KfSessionHandler.java
  18. 44 0
      book-push/src/main/java/com/book/push/handler/LocationHandler.java
  19. 27 0
      book-push/src/main/java/com/book/push/handler/LogHandler.java
  20. 35 0
      book-push/src/main/java/com/book/push/handler/MenuHandler.java
  21. 53 0
      book-push/src/main/java/com/book/push/handler/MsgHandler.java
  22. 24 0
      book-push/src/main/java/com/book/push/handler/NullHandler.java
  23. 24 0
      book-push/src/main/java/com/book/push/handler/ScanHandler.java
  24. 27 0
      book-push/src/main/java/com/book/push/handler/StoreCheckNotifyHandler.java
  25. 70 0
      book-push/src/main/java/com/book/push/handler/SubscribeHandler.java
  26. 27 0
      book-push/src/main/java/com/book/push/handler/UnsubscribeHandler.java
  27. 18 0
      book-push/src/main/java/com/book/push/service/dao/CustomService.java
  28. 14 0
      book-push/src/main/java/com/book/push/service/dao/UserService.java
  29. 26 0
      book-push/src/main/java/com/book/push/service/dao/impl/CustomServiceImpl.java
  30. 23 0
      book-push/src/main/java/com/book/push/service/dao/impl/UserServiceImpl.java
  31. 12 0
      book-push/src/main/java/com/book/push/task/KefuTask.java
  32. 52 0
      book-push/src/main/resources/application-dev.yml
  33. 52 0
      book-push/src/main/resources/application-test.yml
  34. 5 0
      book-push/src/main/resources/application.yml
  35. 261 0
      book-push/src/main/resources/mapper/CustomMapper.xml
  36. 366 0
      book-push/src/main/resources/mapper/UserMapper.xml
  37. 1 0
      book-server/pom.xml
  38. 1 0
      book-server/src/main/java/com/book/server/common/util/JsonUtils.java
  39. 38 1
      book-server/src/main/java/com/book/server/controller/BookController.java
  40. 23 0
      book-server/src/main/java/com/book/server/model/VO/Chapter.java
  41. 5 0
      book-server/src/main/java/com/book/server/service/BookService.java
  42. 158 9
      book-server/src/main/java/com/book/server/service/impl/BookServiceImpl.java
  43. 397 0
      book-server/src/main/java/com/book/server/utils/HttpTool.java
  44. 98 0
      book-server/src/main/java/com/book/server/utils/JsonUtils.java
  45. 52 0
      book-server/src/main/resources/application-dev.yml
  46. 52 0
      book-server/src/main/resources/application-test.yml
  47. 3 48
      book-server/src/main/resources/application.yml
  48. 400 0
      book-server/src/test/java/utils/HttpTool.java

+ 35 - 2
book-push/pom.xml

@@ -64,6 +64,10 @@
                     <groupId>org.springframework.boot</groupId>
                     <artifactId>spring-boot-starter-logging</artifactId>
                 </exclusion>
+                <exclusion>
+                    <groupId>com.fasterxml.jackson.core</groupId>
+                    <artifactId>jackson-databind</artifactId>
+                </exclusion>
             </exclusions>
         </dependency>
         <dependency>
@@ -92,14 +96,43 @@
             <artifactId>disruptor</artifactId>
             <version>3.3.7</version>
         </dependency>
-
-
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>redis.clients</groupId>
+            <artifactId>jedis</artifactId>
+            <version>3.3.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.github.jedis-lock</groupId>
+            <artifactId>jedis-lock</artifactId>
+            <version>1.0.0</version>
+        </dependency>
         <!-- lombok -->
         <dependency>
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>
             <version>1.18.4</version>
         </dependency>
+        <dependency>
+            <groupId>com.github.binarywang</groupId>
+            <artifactId>wx-java-mp-spring-boot-starter</artifactId>
+            <version>4.1.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>1.2.75</version>
+        </dependency>
+        <!--mybatis-->
+        <dependency>
+            <groupId>org.mybatis.spring.boot</groupId>
+            <artifactId>mybatis-spring-boot-starter</artifactId>
+            <version>1.3.2</version>
+        </dependency>
 
     </dependencies>
 </project>

+ 6 - 0
book-push/src/main/java/com/book/push/Application.java

@@ -1,9 +1,15 @@
 package com.book.push;
 
+import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 
+
+/**
+ * @author win7
+ */
 @SpringBootApplication
+@MapperScan("com.book.push.dao.mapper")
 public class Application {
     public static void main(String[] args) {
         SpringApplication.run(Application.class);

+ 17 - 0
book-push/src/main/java/com/book/push/builder/AbstractBuilder.java

@@ -0,0 +1,17 @@
+package com.book.push.builder;
+
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author
+ */
+public abstract class AbstractBuilder {
+    protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+    public abstract WxMpXmlOutMessage build(String content,
+                                            WxMpXmlMessage wxMessage, WxMpService service);
+}

+ 24 - 0
book-push/src/main/java/com/book/push/builder/ImageBuilder.java

@@ -0,0 +1,24 @@
+package com.book.push.builder;
+
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutImageMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
+
+/**
+ * @author
+ */
+public class ImageBuilder extends AbstractBuilder {
+
+    @Override
+    public WxMpXmlOutMessage build(String content, WxMpXmlMessage wxMessage,
+                                   WxMpService service) {
+
+        WxMpXmlOutImageMessage m = WxMpXmlOutMessage.IMAGE().mediaId(content)
+            .fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser())
+            .build();
+
+        return m;
+    }
+
+}

+ 22 - 0
book-push/src/main/java/com/book/push/builder/TextBuilder.java

@@ -0,0 +1,22 @@
+package com.book.push.builder;
+
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutTextMessage;
+
+/**
+ * @author
+ */
+public class TextBuilder extends AbstractBuilder {
+
+    @Override
+    public WxMpXmlOutMessage build(String content, WxMpXmlMessage wxMessage,
+                                   WxMpService service) {
+        WxMpXmlOutTextMessage m = WxMpXmlOutMessage.TEXT().content(content)
+            .fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser())
+            .build();
+        return m;
+    }
+
+}

+ 128 - 0
book-push/src/main/java/com/book/push/config/WxMpConfiguration.java

@@ -0,0 +1,128 @@
+package com.book.push.config;
+
+import com.book.push.handler.*;
+import lombok.AllArgsConstructor;
+import me.chanjar.weixin.common.redis.JedisWxRedisOps;
+import me.chanjar.weixin.mp.api.WxMpMessageRouter;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
+import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
+import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisPoolConfig;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static me.chanjar.weixin.common.api.WxConsts.EventType;
+import static me.chanjar.weixin.common.api.WxConsts.EventType.SUBSCRIBE;
+import static me.chanjar.weixin.common.api.WxConsts.EventType.UNSUBSCRIBE;
+import static me.chanjar.weixin.common.api.WxConsts.XmlMsgType;
+import static me.chanjar.weixin.common.api.WxConsts.XmlMsgType.EVENT;
+import static me.chanjar.weixin.mp.constant.WxMpEventConstants.CustomerService.*;
+import static me.chanjar.weixin.mp.constant.WxMpEventConstants.POI_CHECK_NOTIFY;
+
+/**
+ * wechat mp configuration
+ *
+ * @author win7
+ */
+@AllArgsConstructor
+@Configuration
+@EnableConfigurationProperties(WxMpProperties.class)
+public class WxMpConfiguration {
+    private final LogHandler logHandler;
+    private final NullHandler nullHandler;
+    private final KfSessionHandler kfSessionHandler;
+    private final StoreCheckNotifyHandler storeCheckNotifyHandler;
+    private final LocationHandler locationHandler;
+    private final MenuHandler menuHandler;
+    private final MsgHandler msgHandler;
+    private final UnsubscribeHandler unsubscribeHandler;
+    private final SubscribeHandler subscribeHandler;
+    private final ScanHandler scanHandler;
+    private final WxMpProperties properties;
+
+    @Bean
+    public WxMpService wxMpService() {
+        // 代码里 getConfigs()处报错的同学,请注意仔细阅读项目说明,你的IDE需要引入lombok插件!!!!
+        final List<WxMpProperties.MpConfig> configs = this.properties.getConfigs();
+        if (configs == null) {
+            throw new RuntimeException("请配置公众号相关参数!");
+        }
+
+        WxMpService service = new WxMpServiceImpl();
+        service.setMultiConfigStorages(configs
+            .stream().map(a -> {
+                WxMpDefaultConfigImpl configStorage;
+                if (this.properties.isUseRedis()) {
+                    final WxMpProperties.RedisConfig redisConfig = this.properties.getRedisConfig();
+                    JedisPoolConfig poolConfig = new JedisPoolConfig();
+                    JedisPool jedisPool = new JedisPool(poolConfig, redisConfig.getHost(), redisConfig.getPort(),
+                       -1, redisConfig.getPassword());
+                    configStorage = new WxMpRedisConfigImpl(new JedisWxRedisOps(jedisPool), a.getAppId());
+                } else {
+                    configStorage = new WxMpDefaultConfigImpl();
+                }
+
+                configStorage.setAppId(a.getAppId());
+                configStorage.setSecret(a.getSecret());
+                configStorage.setToken(a.getToken());
+                configStorage.setAesKey(a.getAesKey());
+                return configStorage;
+            }).collect(Collectors.toMap(WxMpDefaultConfigImpl::getAppId, a -> a, (o, n) -> o)));
+
+
+
+        return service;
+    }
+
+    @Bean
+    public WxMpMessageRouter messageRouter(WxMpService wxMpService) {
+        final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService);
+
+        // 记录所有事件的日志 (异步执行)
+        newRouter.rule().handler(this.logHandler).next();
+
+        // 接收客服会话管理事件
+        newRouter.rule().async(false).msgType(EVENT).event(KF_CREATE_SESSION)
+            .handler(this.kfSessionHandler).end();
+        newRouter.rule().async(false).msgType(EVENT).event(KF_CLOSE_SESSION)
+            .handler(this.kfSessionHandler).end();
+        newRouter.rule().async(false).msgType(EVENT).event(KF_SWITCH_SESSION)
+            .handler(this.kfSessionHandler).end();
+
+        // 门店审核事件
+        newRouter.rule().async(false).msgType(EVENT).event(POI_CHECK_NOTIFY).handler(this.storeCheckNotifyHandler).end();
+
+        // 自定义菜单事件
+        newRouter.rule().async(false).msgType(EVENT).event(EventType.CLICK).handler(this.menuHandler).end();
+
+        // 点击菜单连接事件
+        newRouter.rule().async(false).msgType(EVENT).event(EventType.VIEW).handler(this.nullHandler).end();
+
+        // 关注事件
+        newRouter.rule().async(false).msgType(EVENT).event(SUBSCRIBE).handler(this.subscribeHandler).end();
+
+        // 取消关注事件
+        newRouter.rule().async(false).msgType(EVENT).event(UNSUBSCRIBE).handler(this.unsubscribeHandler).end();
+
+        // 上报地理位置事件
+        newRouter.rule().async(false).msgType(EVENT).event(EventType.LOCATION).handler(this.locationHandler).end();
+
+        // 接收地理位置消息
+        newRouter.rule().async(false).msgType(XmlMsgType.LOCATION).handler(this.locationHandler).end();
+
+        // 扫码事件
+        newRouter.rule().async(false).msgType(EVENT).event(EventType.SCAN).handler(this.scanHandler).end();
+
+        // 默认
+        newRouter.rule().async(false).handler(this.msgHandler).end();
+
+        return newRouter;
+    }
+
+}

+ 77 - 0
book-push/src/main/java/com/book/push/config/WxMpProperties.java

@@ -0,0 +1,77 @@
+package com.book.push.config;
+
+import lombok.Data;
+import org.apache.logging.log4j.core.util.JsonUtils;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.List;
+
+/**
+ * wechat mp properties
+ *
+ * @author Binary Wang(https://github.com/binarywang)
+ */
+@Data
+@ConfigurationProperties(prefix = "wx.mp")
+public class WxMpProperties {
+    /**
+     * 是否使用redis存储access token
+     */
+    private boolean useRedis;
+
+    /**
+     * redis 配置
+     */
+    private RedisConfig redisConfig;
+
+    @Data
+    public static class RedisConfig {
+        /**
+         * redis服务器 主机地址
+         */
+        private String host;
+
+        /**
+         * redis服务器 端口号
+         */
+        private Integer port;
+
+        /**
+         * redis服务器 密码
+         */
+        private String password;
+    }
+
+    /**
+     * 多个公众号配置信息
+     */
+    private List<MpConfig> configs;
+
+    @Data
+    public static class MpConfig {
+        /**
+         * 设置微信公众号的appid
+         */
+        private String appId;
+
+        /**
+         * 设置微信公众号的app secret
+         */
+        private String secret;
+
+        /**
+         * 设置微信公众号的token
+         */
+        private String token;
+
+        /**
+         * 设置微信公众号的EncodingAESKey
+         */
+        private String aesKey;
+    }
+
+    @Override
+    public String toString() {
+        return JsonUtils.toJson(this);
+    }
+}

+ 52 - 0
book-push/src/main/java/com/book/push/controller/MenuIndexController.java

@@ -0,0 +1,52 @@
+package com.book.push.controller;
+
+/**
+ * created in 2021/8/17
+ * Project: book-store
+ *
+ * @author win7
+ */
+
+import com.book.push.dao.pojo.User;
+import com.book.push.service.dao.UserService;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * @author
+ */
+@AllArgsConstructor
+@RestController
+@RequestMapping("/menu/index")
+public class MenuIndexController {
+
+    /**
+     * //FIXME
+     *
+     * 重定向urL,需要改
+     */
+    public static final String REDIRECT_URL = "/wx/redirect/{appid}/greet";
+
+    @GetMapping("/{state}")
+    public String BookMain(HttpServletRequest request, @PathVariable String state) {
+
+        StringBuffer url = request.getRequestURL();
+        String contextUrl = url.delete(url.length() - request.getRequestURI().length(), url.length()).append("/").toString();
+        String[] split = contextUrl.split("\\.");
+        String appid = split[0];
+
+        String auto2Url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=SCOPE&state=%s#wechat_redirect ";
+        String redirect_url = REDIRECT_URL.replace("{appid}",appid);
+        String format = String.format(auto2Url, appid, redirect_url, state);
+        return "redirect:" + format;
+
+
+    }
+
+
+}

+ 188 - 0
book-push/src/main/java/com/book/push/controller/WxMenuController.java

@@ -0,0 +1,188 @@
+package com.book.push.controller;
+
+import lombok.AllArgsConstructor;
+import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.common.bean.menu.WxMenu;
+import me.chanjar.weixin.common.bean.menu.WxMenuButton;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.menu.WxMpGetSelfMenuInfoResult;
+import me.chanjar.weixin.mp.bean.menu.WxMpMenu;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import static me.chanjar.weixin.common.api.WxConsts.MenuButtonType;
+
+/**
+ * @author
+ */
+@AllArgsConstructor
+@RestController
+@RequestMapping("/wx/menu/{appid}")
+public class WxMenuController {
+    private final WxMpService wxService;
+
+    /**
+     * <pre>
+     * 自定义菜单创建接口
+     * 详情请见:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141013&token=&lang=zh_CN
+     * 如果要创建个性化菜单,请设置matchrule属性
+     * 详情请见:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1455782296&token=&lang=zh_CN
+     * </pre>
+     *
+     * @return 如果是个性化菜单,则返回menuid,否则返回null
+     */
+    @PostMapping("/create")
+    public String menuCreate(@PathVariable String appid, @RequestBody WxMenu menu) throws WxErrorException {
+        return this.wxService.switchoverTo(appid).getMenuService().menuCreate(menu);
+    }
+
+    @GetMapping("/create")
+    public String menuCreateSample(@PathVariable String appid) throws WxErrorException, MalformedURLException {
+        WxMenu menu = new WxMenu();
+        WxMenuButton button1 = new WxMenuButton();
+        button1.setType(MenuButtonType.CLICK);
+        button1.setName("今日歌曲");
+        button1.setKey("V1001_TODAY_MUSIC");
+
+//        WxMenuButton button2 = new WxMenuButton();
+//        button2.setType(WxConsts.BUTTON_MINIPROGRAM);
+//        button2.setName("小程序");
+//        button2.setAppId("wx286b93c14bbf93aa");
+//        button2.setPagePath("pages/lunar/index.html");
+//        button2.setUrl("http://mp.weixin.qq.com");
+
+        WxMenuButton button3 = new WxMenuButton();
+        button3.setName("菜单");
+
+        menu.getButtons().add(button1);
+//        menu.getButtons().add(button2);
+        menu.getButtons().add(button3);
+
+        WxMenuButton button31 = new WxMenuButton();
+        button31.setType(MenuButtonType.VIEW);
+        button31.setName("搜索");
+        button31.setUrl("http://www.soso.com/");
+
+        WxMenuButton button32 = new WxMenuButton();
+        button32.setType(MenuButtonType.VIEW);
+        button32.setName("视频");
+        button32.setUrl("http://v.qq.com/");
+
+        WxMenuButton button33 = new WxMenuButton();
+        button33.setType(MenuButtonType.CLICK);
+        button33.setName("赞一下我们");
+        button33.setKey("V1001_GOOD");
+
+        WxMenuButton button34 = new WxMenuButton();
+        button34.setType(MenuButtonType.VIEW);
+        button34.setName("获取用户信息");
+
+        ServletRequestAttributes servletRequestAttributes =
+            (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+        if (servletRequestAttributes != null) {
+            HttpServletRequest request = servletRequestAttributes.getRequest();
+            URL requestURL = new URL(request.getRequestURL().toString());
+            String url = this.wxService.switchoverTo(appid).getOAuth2Service().buildAuthorizationUrl(
+                String.format("%s://%s/wx/redirect/%s/greet", requestURL.getProtocol(), requestURL.getHost(), appid),
+                WxConsts.OAuth2Scope.SNSAPI_USERINFO, null);
+            button34.setUrl(url);
+        }
+
+        button3.getSubButtons().add(button31);
+        button3.getSubButtons().add(button32);
+        button3.getSubButtons().add(button33);
+        button3.getSubButtons().add(button34);
+
+        this.wxService.switchover(appid);
+        return this.wxService.getMenuService().menuCreate(menu);
+    }
+
+    /**
+     * <pre>
+     * 自定义菜单创建接口
+     * 详情请见: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141013&token=&lang=zh_CN
+     * 如果要创建个性化菜单,请设置matchrule属性
+     * 详情请见:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1455782296&token=&lang=zh_CN
+     * </pre>
+     *
+     * @return 如果是个性化菜单,则返回menuid,否则返回null
+     */
+    @PostMapping("/createByJson")
+    public String menuCreate(@PathVariable String appid, @RequestBody String json) throws WxErrorException {
+        return this.wxService.switchoverTo(appid).getMenuService().menuCreate(json);
+    }
+
+    /**
+     * <pre>
+     * 自定义菜单删除接口
+     * 详情请见: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141015&token=&lang=zh_CN
+     * </pre>
+     */
+    @GetMapping("/delete")
+    public void menuDelete(@PathVariable String appid) throws WxErrorException {
+        this.wxService.switchoverTo(appid).getMenuService().menuDelete();
+    }
+
+    /**
+     * <pre>
+     * 删除个性化菜单接口
+     * 详情请见: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1455782296&token=&lang=zh_CN
+     * </pre>
+     *
+     * @param menuId 个性化菜单的menuid
+     */
+    @GetMapping("/delete/{menuId}")
+    public void menuDelete(@PathVariable String appid, @PathVariable String menuId) throws WxErrorException {
+        this.wxService.switchoverTo(appid).getMenuService().menuDelete(menuId);
+    }
+
+    /**
+     * <pre>
+     * 自定义菜单查询接口
+     * 详情请见: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141014&token=&lang=zh_CN
+     * </pre>
+     */
+    @GetMapping("/get")
+    public WxMpMenu menuGet(@PathVariable String appid) throws WxErrorException {
+        return this.wxService.switchoverTo(appid).getMenuService().menuGet();
+    }
+
+    /**
+     * <pre>
+     * 测试个性化菜单匹配结果
+     * 详情请见: http://mp.weixin.qq.com/wiki/0/c48ccd12b69ae023159b4bfaa7c39c20.html
+     * </pre>
+     *
+     * @param userid 可以是粉丝的OpenID,也可以是粉丝的微信号。
+     */
+    @GetMapping("/menuTryMatch/{userid}")
+    public WxMenu menuTryMatch(@PathVariable String appid, @PathVariable String userid) throws WxErrorException {
+        return this.wxService.switchoverTo(appid).getMenuService().menuTryMatch(userid);
+    }
+
+    /**
+     * <pre>
+     * 获取自定义菜单配置接口
+     * 本接口将会提供公众号当前使用的自定义菜单的配置,如果公众号是通过API调用设置的菜单,则返回菜单的开发配置,而如果公众号是在公众平台官网通过网站功能发布菜单,则本接口返回运营者设置的菜单配置。
+     * 请注意:
+     * 1、第三方平台开发者可以通过本接口,在旗下公众号将业务授权给你后,立即通过本接口检测公众号的自定义菜单配置,并通过接口再次给公众号设置好自动回复规则,以提升公众号运营者的业务体验。
+     * 2、本接口与自定义菜单查询接口的不同之处在于,本接口无论公众号的接口是如何设置的,都能查询到接口,而自定义菜单查询接口则仅能查询到使用API设置的菜单配置。
+     * 3、认证/未认证的服务号/订阅号,以及接口测试号,均拥有该接口权限。
+     * 4、从第三方平台的公众号登录授权机制上来说,该接口从属于消息与菜单权限集。
+     * 5、本接口中返回的图片/语音/视频为临时素材(临时素材每次获取都不同,3天内有效,通过素材管理-获取临时素材接口来获取这些素材),本接口返回的图文消息为永久素材素材(通过素材管理-获取永久素材接口来获取这些素材)。
+     *  接口调用请求说明:
+     * http请求方式: GET(请使用https协议)
+     * https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info?access_token=ACCESS_TOKEN
+     * </pre>
+     */
+    @GetMapping("/getSelfMenuInfo")
+    public WxMpGetSelfMenuInfoResult getSelfMenuInfo(@PathVariable String appid) throws WxErrorException {
+        return this.wxService.switchoverTo(appid).getMenuService().getSelfMenuInfo();
+    }
+}

+ 131 - 0
book-push/src/main/java/com/book/push/controller/WxPortalController.java

@@ -0,0 +1,131 @@
+package com.book.push.controller;
+
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.mp.api.WxMpMessageRouter;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 微信认证
+ *
+ * @author
+ */
+@Slf4j
+@AllArgsConstructor
+@RestController
+@RequestMapping("/wx/portal/{appid}")
+public class WxPortalController {
+    private final WxMpService wxService;
+    private final WxMpMessageRouter messageRouter;
+
+    /**
+     * GET
+     *
+     * @param appid
+     * @param signature
+     * @param timestamp
+     * @param nonce
+     * @param echostr
+     * @return
+     */
+    @GetMapping(produces = "text/plain;charset=utf-8")
+    public String authGet(@PathVariable String appid,
+                          @RequestParam(name = "signature", required = false) String signature,
+                          @RequestParam(name = "timestamp", required = false) String timestamp,
+                          @RequestParam(name = "nonce", required = false) String nonce,
+                          @RequestParam(name = "echostr", required = false) String echostr) {
+
+        log.info("\n接收到来自微信服务器的认证消息:[{}, {}, {}, {}]", signature,
+                timestamp, nonce, echostr);
+        if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) {
+            throw new IllegalArgumentException("请求参数非法,请核实!");
+        }
+
+        if (!this.wxService.switchover(appid)) {
+            throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));
+        }
+
+        if (wxService.checkSignature(timestamp, nonce, signature)) {
+            return echostr;
+        }
+
+        return "非法请求";
+    }
+
+
+    /**
+     * POST
+     *
+     * @param appid
+     * @param requestBody
+     * @param signature
+     * @param timestamp
+     * @param nonce
+     * @param openid
+     * @param encType
+     * @param msgSignature
+     * @return
+     */
+    @PostMapping(produces = "application/xml; charset=UTF-8")
+    public String post(@PathVariable String appid,
+                       @RequestBody String requestBody,
+                       @RequestParam("signature") String signature,
+                       @RequestParam("timestamp") String timestamp,
+                       @RequestParam("nonce") String nonce,
+                       @RequestParam("openid") String openid,
+                       @RequestParam(name = "encrypt_type", required = false) String encType,
+                       @RequestParam(name = "msg_signature", required = false) String msgSignature) {
+        log.info("\n接收微信请求:[openid=[{}], [signature=[{}], encType=[{}], msgSignature=[{}],"
+                        + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
+                openid, signature, encType, msgSignature, timestamp, nonce, requestBody);
+
+        if (!this.wxService.switchover(appid)) {
+            throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));
+        }
+
+        if (!wxService.checkSignature(timestamp, nonce, signature)) {
+            throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
+        }
+
+        String out = null;
+        if (encType == null) {
+            // 明文传输的消息
+            WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody);
+            WxMpXmlOutMessage outMessage = this.route(inMessage);
+            if (outMessage == null) {
+                return "";
+            }
+
+            out = outMessage.toXml();
+        } else if ("aes".equalsIgnoreCase(encType)) {
+            // aes加密的消息
+            WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody, wxService.getWxMpConfigStorage(),
+                    timestamp, nonce, msgSignature);
+            log.debug("\n消息解密后内容为:\n{} ", inMessage.toString());
+            WxMpXmlOutMessage outMessage = this.route(inMessage);
+            if (outMessage == null) {
+                return "";
+            }
+
+            out = outMessage.toEncryptedXml(wxService.getWxMpConfigStorage());
+        }
+
+        log.debug("\n组装回复信息:{}", out);
+        return out;
+    }
+
+    private WxMpXmlOutMessage route(WxMpXmlMessage message) {
+        try {
+            return this.messageRouter.route(message);
+        } catch (Exception e) {
+            log.error("路由消息时出现异常!", e);
+        }
+
+        return null;
+    }
+
+}

+ 68 - 0
book-push/src/main/java/com/book/push/controller/WxRedirectController.java

@@ -0,0 +1,68 @@
+package com.book.push.controller;
+
+import com.book.push.dao.pojo.User;
+import com.book.push.service.dao.UserService;
+import lombok.AllArgsConstructor;
+import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
+import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.mp.api.WxMpService;
+import org.checkerframework.checker.units.qual.A;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+/**
+ * 接收微信重定向
+ * @author win7
+ */
+@AllArgsConstructor
+@Controller
+@RequestMapping("/wx/redirect/{appid}")
+public class WxRedirectController {
+    private final WxMpService wxService;
+    @Autowired
+    private UserService userService;
+    /**
+     * //FIXME
+     * 前端页面url
+     */
+    private final static String FRONT_URL = "/%s/%s/%d";
+
+    /**
+     * 接收微信静默授权,获取用户信息,跳转前台页面
+     *
+     * @param appid
+     * @param code
+     * @param map
+     * @return
+     */
+    @RequestMapping("/greet")
+    public String greetUser(@PathVariable String appid, @RequestParam String code, @RequestParam String state, ModelMap map) {
+        if (!this.wxService.switchover(appid)) {
+            throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));
+        }
+
+        try {
+            WxOAuth2AccessToken accessToken = wxService.getOAuth2Service().getAccessToken(code);
+            WxOAuth2UserInfo wxOAuth2UserInfo = wxService.getOAuth2Service().getUserInfo(accessToken, null);
+            map.put("user", wxOAuth2UserInfo);
+            User user = new User();
+            user.setOpenid(wxOAuth2UserInfo.getOpenid());
+            user = userService.selectUserByUser(user);
+            if (user==null){
+                //todo
+            }
+            String openid = user.getOpenid();
+            String frontUrl = String.format(FRONT_URL, state, appid, user.getId());
+            return "redirect:" + frontUrl;
+        } catch (WxErrorException e) {
+            e.printStackTrace();
+        }
+
+        return "greet_user";
+    }
+}

+ 24 - 0
book-push/src/main/java/com/book/push/dao/mapper/CustomMapper.java

@@ -0,0 +1,24 @@
+package com.book.push.dao.mapper;
+
+
+import com.book.push.dao.pojo.Custom;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+@Repository
+public interface CustomMapper {
+    int deleteByPrimaryKey(Integer id);
+
+    int insert(Custom record);
+
+    int insertSelective(Custom record);
+
+    Custom selectByPrimaryKey(Integer id);
+
+    int updateByPrimaryKeySelective(Custom record);
+
+    int updateByPrimaryKey(Custom record);
+
+
+    List<Custom> selectListBeforeSendTime();
+}

+ 21 - 0
book-push/src/main/java/com/book/push/dao/mapper/UserMapper.java

@@ -0,0 +1,21 @@
+package com.book.push.dao.mapper;
+
+import com.book.push.dao.pojo.User;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface UserMapper {
+    int deleteByPrimaryKey(Long id);
+
+    int insert(User record);
+
+    int insertSelective(User record);
+
+    User selectByPrimaryKey(Long id);
+
+    int updateByPrimaryKeySelective(User record);
+
+    int updateByPrimaryKey(User record);
+
+    User selectByUser(User user);
+}

+ 105 - 0
book-push/src/main/java/com/book/push/dao/pojo/Custom.java

@@ -0,0 +1,105 @@
+package com.book.push.dao.pojo;
+
+import java.io.Serializable;
+import lombok.Data;
+
+/**
+ * custom
+ * @author 
+ */
+@Data
+public class Custom implements Serializable {
+    /**
+     * ID
+     */
+    private Integer id;
+
+    /**
+     * 任务名称
+     */
+    private String title;
+
+    private Integer adminId;
+
+    /**
+     * 消息内容
+     */
+    private Object messageJson;
+
+    /**
+     * 发送时间
+     */
+    private Integer sendtime;
+
+    /**
+     * 接收用户
+     */
+    private Object userJson;
+
+    /**
+     * 发送总人数
+     */
+    private Integer sendNum;
+
+    /**
+     * 发送成功人数
+     */
+    private Integer successNum;
+
+    /**
+     * 发送失败人数
+     */
+    private Integer failNum;
+
+    /**
+     * 状态
+     */
+    private Object statue;
+
+    /**
+     * 创建时间
+     */
+    private Integer createtime;
+
+    /**
+     * 更新时间
+     */
+    private Integer updatetime;
+
+    /**
+     * 创建者admin_id
+     */
+    private Integer createdAdminId;
+
+    /**
+     * 客服消息素材推送记录表id(可空,默认0无关连)
+     */
+    private Integer customMediaPushId;
+
+    /**
+     * 文字链
+     */
+    private String messageText;
+
+    /**
+     * 类型:0=图文,1=文字链
+     */
+    private Object messageType;
+
+    /**
+     * 公众号id。official_account_type为0时,为admin表(渠道商)的id;official_account_type为1时,此字段为subscription表id。
+     */
+    private Integer officialAccountId;
+
+    /**
+     * 公众号类型:0=服务号,1=订阅号
+     */
+    private Object officialAccountType;
+
+    /**
+     * 创建来源 1:客服消息添加 2:群发
+     */
+    private Object createdFrom;
+
+    private static final long serialVersionUID = 1L;
+}

+ 145 - 0
book-push/src/main/java/com/book/push/dao/pojo/User.java

@@ -0,0 +1,145 @@
+package com.book.push.dao.pojo;
+
+import java.io.Serializable;
+import lombok.Data;
+
+/**
+ * user
+ * @author 
+ */
+@Data
+public class User implements Serializable {
+    private Long id;
+
+    /**
+     * 微信openID
+     */
+    private String openid;
+
+    /**
+     * 微信unionid
+     */
+    private String unionid;
+
+    /**
+     * 访客id
+     */
+    private String visitor;
+
+    /**
+     * 昵称
+     */
+    private String nickname;
+
+    /**
+     * 状态值:0=未知,1=男性,2=女性
+     */
+    private Object sex;
+
+    /**
+     * 手机号
+     */
+    private String mobile;
+
+    /**
+     * 头像
+     */
+    private String avatar;
+
+    /**
+     * 关注公众号状态:1=已关注,0=未关注
+     */
+    private Object isSubscribe;
+
+    /**
+     * 关注引导公众号状态:1=已关注,0=未关注
+     */
+    private Object subscriptionExtend;
+
+    /**
+     * 关注时间
+     */
+    private Integer subscribeTime;
+
+    /**
+     * 喜好书籍分类
+     */
+    private String bookCategoryIds;
+
+    /**
+     * 微信交互时间戳
+     */
+    private Integer operateTime;
+
+    /**
+     * 首充状态:1=已充值,0=未充值
+     */
+    private Object isPay;
+
+    /**
+     * 充值看点数
+     */
+    private Integer kandian;
+
+    /**
+     * 赠送看点数
+     */
+    private Integer freeKandian;
+
+    /**
+     * VIP到期时间
+     */
+    private Integer vipEndtime;
+
+    /**
+     * 注册IP
+     */
+    private String registerIp;
+
+    /**
+     * 国家
+     */
+    private String country;
+
+    /**
+     * 区域
+     */
+    private String area;
+
+    /**
+     * 省份
+     */
+    private String province;
+
+    /**
+     * 城市
+     */
+    private String city;
+
+    /**
+     * isp服务商
+     */
+    private String isp;
+
+    /**
+     * 关联渠道商ID
+     */
+    private Integer channelId;
+
+    /**
+     * 状态值:0=禁用,1=正常
+     */
+    private Object state;
+
+    /**
+     * 创建时间
+     */
+    private Integer createtime;
+
+    /**
+     * 更新时间
+     */
+    private Integer updatetime;
+
+    private static final long serialVersionUID = 1L;
+}

+ 12 - 0
book-push/src/main/java/com/book/push/handler/AbstractHandler.java

@@ -0,0 +1,12 @@
+package com.book.push.handler;
+
+import me.chanjar.weixin.mp.api.WxMpMessageHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author
+ */
+public abstract class AbstractHandler implements WxMpMessageHandler {
+    protected Logger logger = LoggerFactory.getLogger(getClass());
+}

+ 25 - 0
book-push/src/main/java/com/book/push/handler/KfSessionHandler.java

@@ -0,0 +1,25 @@
+package com.book.push.handler;
+
+import me.chanjar.weixin.common.session.WxSessionManager;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * @author
+ */
+@Component
+public class KfSessionHandler extends AbstractHandler {
+
+    @Override
+    public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
+                                    Map<String, Object> context, WxMpService wxMpService,
+                                    WxSessionManager sessionManager) {
+        //TODO 对会话做处理
+        return null;
+    }
+
+}

+ 44 - 0
book-push/src/main/java/com/book/push/handler/LocationHandler.java

@@ -0,0 +1,44 @@
+package com.book.push.handler;
+
+import com.book.push.builder.TextBuilder;
+import me.chanjar.weixin.common.session.WxSessionManager;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+import static me.chanjar.weixin.common.api.WxConsts.XmlMsgType;
+
+/**
+ * @author
+ */
+@Component
+public class LocationHandler extends AbstractHandler {
+
+    @Override
+    public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
+                                    Map<String, Object> context, WxMpService wxMpService,
+                                    WxSessionManager sessionManager) {
+        if (wxMessage.getMsgType().equals(XmlMsgType.LOCATION)) {
+            //TODO 接收处理用户发送的地理位置消息
+            try {
+                String content = "感谢反馈,您的的地理位置已收到!";
+                return new TextBuilder().build(content, wxMessage, null);
+            } catch (Exception e) {
+                this.logger.error("位置消息接收处理失败", e);
+                return null;
+            }
+        }
+
+        //上报地理位置事件
+        this.logger.info("上报地理位置,纬度 : {},经度 : {},精度 : {}",
+            wxMessage.getLatitude(), wxMessage.getLongitude(), String.valueOf(wxMessage.getPrecision()));
+
+        //TODO  可以将用户地理位置信息保存到本地数据库,以便以后使用
+
+        return null;
+    }
+
+}

+ 27 - 0
book-push/src/main/java/com/book/push/handler/LogHandler.java

@@ -0,0 +1,27 @@
+package com.book.push.handler;
+
+import com.alibaba.fastjson.JSON;
+import me.chanjar.weixin.common.session.WxSessionManager;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+
+
+/**
+ * @author
+ */
+@Component
+public class LogHandler extends AbstractHandler {
+    @Override
+    public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
+                                    Map<String, Object> context, WxMpService wxMpService,
+                                    WxSessionManager sessionManager) {
+        this.logger.info("\n接收到请求消息,内容:{}", JSON.toJSONString(wxMessage));
+        return null;
+    }
+
+}

+ 35 - 0
book-push/src/main/java/com/book/push/handler/MenuHandler.java

@@ -0,0 +1,35 @@
+package com.book.push.handler;
+
+import me.chanjar.weixin.common.session.WxSessionManager;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+import static me.chanjar.weixin.common.api.WxConsts.EventType;
+
+/**
+ * @author
+ */
+@Component
+public class MenuHandler extends AbstractHandler {
+
+    @Override
+    public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
+                                    Map<String, Object> context, WxMpService weixinService,
+                                    WxSessionManager sessionManager) {
+        String msg = String.format("type:%s, event:%s, key:%s",
+            wxMessage.getMsgType(), wxMessage.getEvent(),
+            wxMessage.getEventKey());
+        if (EventType.VIEW.equals(wxMessage.getEvent())) {
+            return null;
+        }
+
+        return WxMpXmlOutMessage.TEXT().content(msg)
+            .fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser())
+            .build();
+    }
+
+}

+ 53 - 0
book-push/src/main/java/com/book/push/handler/MsgHandler.java

@@ -0,0 +1,53 @@
+package com.book.push.handler;
+
+import com.alibaba.fastjson.JSON;
+import com.book.push.builder.TextBuilder;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.session.WxSessionManager;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.core.util.JsonUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+import static me.chanjar.weixin.common.api.WxConsts.XmlMsgType;
+
+/**
+ * @author
+ */
+@Component
+public class MsgHandler extends AbstractHandler {
+
+    @Override
+    public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
+                                    Map<String, Object> context, WxMpService weixinService,
+                                    WxSessionManager sessionManager) {
+
+        if (!wxMessage.getMsgType().equals(XmlMsgType.EVENT)) {
+            //TODO 可以选择将消息保存到本地
+        }
+
+        //当用户输入关键词如“你好”,“客服”等,并且有客服在线时,把消息转发给在线客服
+        try {
+            if (StringUtils.startsWithAny(wxMessage.getContent(), "你好", "客服")
+                && weixinService.getKefuService().kfOnlineList()
+                .getKfOnlineList().size() > 0) {
+                return WxMpXmlOutMessage.TRANSFER_CUSTOMER_SERVICE()
+                    .fromUser(wxMessage.getToUser())
+                    .toUser(wxMessage.getFromUser()).build();
+            }
+        } catch (WxErrorException e) {
+            e.printStackTrace();
+        }
+
+        //TODO 组装回复消息
+        String content = "收到信息内容:" + JSON.toJSONString(wxMessage);
+
+        return new TextBuilder().build(content, wxMessage, weixinService);
+
+    }
+
+}

+ 24 - 0
book-push/src/main/java/com/book/push/handler/NullHandler.java

@@ -0,0 +1,24 @@
+package com.book.push.handler;
+
+import me.chanjar.weixin.common.session.WxSessionManager;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * @author
+ */
+@Component
+public class NullHandler extends AbstractHandler {
+
+    @Override
+    public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
+                                    Map<String, Object> context, WxMpService wxMpService,
+                                    WxSessionManager sessionManager) {
+        return null;
+    }
+
+}

+ 24 - 0
book-push/src/main/java/com/book/push/handler/ScanHandler.java

@@ -0,0 +1,24 @@
+package com.book.push.handler;
+
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.session.WxSessionManager;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * @author
+ */
+@Component
+public class ScanHandler extends AbstractHandler {
+
+    @Override
+    public WxMpXmlOutMessage handle(WxMpXmlMessage wxMpXmlMessage, Map<String, Object> map,
+                                    WxMpService wxMpService, WxSessionManager wxSessionManager) throws WxErrorException {
+        // 扫码事件处理
+        return null;
+    }
+}

+ 27 - 0
book-push/src/main/java/com/book/push/handler/StoreCheckNotifyHandler.java

@@ -0,0 +1,27 @@
+package com.book.push.handler;
+
+import me.chanjar.weixin.common.session.WxSessionManager;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * 门店审核事件处理
+ *
+ * @author
+ */
+@Component
+public class StoreCheckNotifyHandler extends AbstractHandler {
+
+    @Override
+    public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
+                                    Map<String, Object> context, WxMpService wxMpService,
+                                    WxSessionManager sessionManager) {
+        // TODO 处理门店审核事件
+        return null;
+    }
+
+}

+ 70 - 0
book-push/src/main/java/com/book/push/handler/SubscribeHandler.java

@@ -0,0 +1,70 @@
+package com.book.push.handler;
+
+import com.book.push.builder.TextBuilder;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.session.WxSessionManager;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
+import me.chanjar.weixin.mp.bean.result.WxMpUser;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * @author
+ */
+@Component
+public class SubscribeHandler extends AbstractHandler {
+
+    @Override
+    public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
+                                    Map<String, Object> context, WxMpService weixinService,
+                                    WxSessionManager sessionManager) throws WxErrorException {
+
+        this.logger.info("新关注用户 OPENID: " + wxMessage.getFromUser());
+
+        // 获取微信用户基本信息
+        try {
+            WxMpUser userWxInfo = weixinService.getUserService()
+                .userInfo(wxMessage.getFromUser(), null);
+            if (userWxInfo != null) {
+                // TODO 可以添加关注用户到本地数据库
+            }
+        } catch (WxErrorException e) {
+            if (e.getError().getErrorCode() == 48001) {
+                this.logger.info("该公众号没有获取用户信息权限!");
+            }
+        }
+
+
+        WxMpXmlOutMessage responseResult = null;
+        try {
+            responseResult = this.handleSpecial(wxMessage);
+        } catch (Exception e) {
+            this.logger.error(e.getMessage(), e);
+        }
+
+        if (responseResult != null) {
+            return responseResult;
+        }
+
+        try {
+            return new TextBuilder().build("感谢关注", wxMessage, weixinService);
+        } catch (Exception e) {
+            this.logger.error(e.getMessage(), e);
+        }
+
+        return null;
+    }
+
+    /**
+     * 处理特殊请求,比如如果是扫码进来的,可以做相应处理
+     */
+    private WxMpXmlOutMessage handleSpecial(WxMpXmlMessage wxMessage)
+        throws Exception {
+        //TODO
+        return null;
+    }
+
+}

+ 27 - 0
book-push/src/main/java/com/book/push/handler/UnsubscribeHandler.java

@@ -0,0 +1,27 @@
+package com.book.push.handler;
+
+import me.chanjar.weixin.common.session.WxSessionManager;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
+import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * @author
+ */
+@Component
+public class UnsubscribeHandler extends AbstractHandler {
+
+    @Override
+    public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
+                                    Map<String, Object> context, WxMpService wxMpService,
+                                    WxSessionManager sessionManager) {
+        String openId = wxMessage.getFromUser();
+        this.logger.info("取消关注用户 OPENID: " + openId);
+        // TODO 可以更新本地数据库为取消关注状态
+        return null;
+    }
+
+}

+ 18 - 0
book-push/src/main/java/com/book/push/service/dao/CustomService.java

@@ -0,0 +1,18 @@
+package com.book.push.service.dao;
+
+import com.book.push.dao.pojo.Custom;
+
+import java.util.List;
+
+/**
+ * created in 2021/8/17
+ * Project: book-store
+ *
+ * @author win7
+ */
+
+public interface CustomService {
+
+     List<Custom> selectListBeforeSendTime();
+
+}

+ 14 - 0
book-push/src/main/java/com/book/push/service/dao/UserService.java

@@ -0,0 +1,14 @@
+package com.book.push.service.dao;
+
+import com.book.push.dao.pojo.User;
+
+/**
+ * created in 2021/8/17
+ * Project: book-store
+ *
+ * @author win7
+ */
+
+public interface UserService {
+     User selectUserByUser(User user);
+}

+ 26 - 0
book-push/src/main/java/com/book/push/service/dao/impl/CustomServiceImpl.java

@@ -0,0 +1,26 @@
+package com.book.push.service.dao.impl;
+
+import com.book.push.dao.mapper.CustomMapper;
+import com.book.push.dao.pojo.Custom;
+import com.book.push.service.dao.CustomService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * created in 2021/8/17
+ * Project: book-store
+ *
+ * @author win7
+ */
+@Service("customService")
+public class CustomServiceImpl implements CustomService {
+    @Autowired
+    private CustomMapper customMapper;
+
+    @Override
+    public List<Custom> selectListBeforeSendTime() {
+        return customMapper.selectListBeforeSendTime();
+    }
+}

+ 23 - 0
book-push/src/main/java/com/book/push/service/dao/impl/UserServiceImpl.java

@@ -0,0 +1,23 @@
+package com.book.push.service.dao.impl;
+
+import com.book.push.dao.mapper.UserMapper;
+import com.book.push.dao.pojo.User;
+import com.book.push.service.dao.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * created in 2021/8/17
+ * Project: book-store
+ *
+ * @author win7
+ */
+@Service("userService")
+public class UserServiceImpl implements UserService {
+    @Autowired
+    private UserMapper userMapper;
+    @Override
+    public User selectUserByUser(User user) {
+        return userMapper.selectByUser(user);
+    }
+}

+ 12 - 0
book-push/src/main/java/com/book/push/task/KefuTask.java

@@ -0,0 +1,12 @@
+package com.book.push.task;
+
+/**
+ * created in 2021/8/17
+ * Project: book-store
+ * 客服消息
+ *
+ * @author win7
+ */
+
+public class KefuTask {
+}

+ 52 - 0
book-push/src/main/resources/application-dev.yml

@@ -0,0 +1,52 @@
+server:
+  port: 8080
+
+spring:
+  datasource:
+    type: com.alibaba.druid.pool.DruidDataSource
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://121.41.100.198:3306/test_cps?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
+    username: root
+    password: root
+    #下面为连接池补充设置
+    druid:
+      initial-size: 5 # 初始化
+      max-active: 5 # 最大
+      min-idle: 5 # 最小
+      max-wait: 6000 # 超时时间
+      time-between-eviction-runs-millis: 60000 # 每分钟检查一次空闲链接
+      min-evictable-idle-time-millis: 300000 # 空闲链接可以保持多久而不被驱逐
+      # 检测链接是否有效的query
+      validation-query: SELECT 1 FROM DUAL
+      test-while-idle: true # 检测到链接空闲时,验证是否有效
+      test-on-borrow: false # 申请链接时,不检测
+      test-on-return: false # 返回链接时,不检测
+      pool-prepared-statements: false # 是否缓存preparedStatement,oracle打开,mysql关闭
+      # 如果上面开启了游标,这里要设置一下大小,例如 50
+      max-pool-prepared-statement-per-connection-size: -1
+      # 统计、监控配置
+      filters: stat,wall # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
+      # 合并执行的相同sql,避免因为参数不同而统计多条sql语句;开启慢sql记录
+      connect-properties: config.stat.mergeSql=true;config.stat.slowSqlMillis=500
+      use-global-data-source-stat: true # 合并多个DruidDataSource的监控数据
+      stat-view-servlet:
+        enabled: true
+        login-username: tianyun
+        login-password: tianyunperfect
+        allow: # 默认运行所有
+        deny: # 默认即可
+        reset-enable: true
+
+
+mybatis:
+  type-aliases-package: com.book.server.entity
+  mapper-locations: classpath:mapper/*Mapper.xml
+
+
+# 设置debug模式下打印mysql
+logging:
+  level:
+    com:
+      book:
+        server:
+          mapper: debug

+ 52 - 0
book-push/src/main/resources/application-test.yml

@@ -0,0 +1,52 @@
+server:
+  port: 9999
+
+spring:
+  datasource:
+    type: com.alibaba.druid.pool.DruidDataSource
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://www.tianyunperfect.cn:3306/test_cps?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
+    username: test_cps
+    password: xKysDECnJLXkMYdJ
+    #下面为连接池补充设置
+    druid:
+      initial-size: 5 # 初始化
+      max-active: 5 # 最大
+      min-idle: 5 # 最小
+      max-wait: 6000 # 超时时间
+      time-between-eviction-runs-millis: 60000 # 每分钟检查一次空闲链接
+      min-evictable-idle-time-millis: 300000 # 空闲链接可以保持多久而不被驱逐
+      # 检测链接是否有效的query
+      validation-query: SELECT 1 FROM DUAL
+      test-while-idle: true # 检测到链接空闲时,验证是否有效
+      test-on-borrow: false # 申请链接时,不检测
+      test-on-return: false # 返回链接时,不检测
+      pool-prepared-statements: false # 是否缓存preparedStatement,oracle打开,mysql关闭
+      # 如果上面开启了游标,这里要设置一下大小,例如 50
+      max-pool-prepared-statement-per-connection-size: -1
+      # 统计、监控配置
+      filters: stat,wall # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
+      # 合并执行的相同sql,避免因为参数不同而统计多条sql语句;开启慢sql记录
+      connect-properties: config.stat.mergeSql=true;config.stat.slowSqlMillis=500
+      use-global-data-source-stat: true # 合并多个DruidDataSource的监控数据
+      stat-view-servlet:
+        enabled: true
+        login-username: tianyun
+        login-password: tianyunperfect
+        allow: # 默认运行所有
+        deny: # 默认即可
+        reset-enable: true
+
+
+mybatis:
+  type-aliases-package: com.book.server.entity
+  mapper-locations: classpath:mapper/*Mapper.xml
+
+
+# 设置debug模式下打印mysql
+logging:
+  level:
+    com:
+      book:
+        server:
+          mapper: debug

+ 5 - 0
book-push/src/main/resources/application.yml

@@ -0,0 +1,5 @@
+spring:
+  spring:
+    # 环境 dev:开发环境|test:测试环境|prod:生产环境
+    profiles:
+      active: dev #激活的配置文件

+ 261 - 0
book-push/src/main/resources/mapper/CustomMapper.xml

@@ -0,0 +1,261 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.book.push.dao.mapper.CustomMapper">
+    <resultMap id="BaseResultMap" type="com.book.push.dao.pojo.Custom">
+        <id column="id" jdbcType="INTEGER" property="id"/>
+        <result column="title" jdbcType="VARCHAR" property="title"/>
+        <result column="admin_id" jdbcType="INTEGER" property="adminId"/>
+        <result column="message_json" jdbcType="OTHER" property="messageJson"/>
+        <result column="sendtime" jdbcType="INTEGER" property="sendtime"/>
+        <result column="user_json" jdbcType="OTHER" property="userJson"/>
+        <result column="send_num" jdbcType="INTEGER" property="sendNum"/>
+        <result column="success_num" jdbcType="INTEGER" property="successNum"/>
+        <result column="fail_num" jdbcType="INTEGER" property="failNum"/>
+        <result column="statue" jdbcType="OTHER" property="statue"/>
+        <result column="createtime" jdbcType="INTEGER" property="createtime"/>
+        <result column="updatetime" jdbcType="INTEGER" property="updatetime"/>
+        <result column="created_admin_id" jdbcType="INTEGER" property="createdAdminId"/>
+        <result column="custom_media_push_id" jdbcType="INTEGER" property="customMediaPushId"/>
+        <result column="message_text" jdbcType="VARCHAR" property="messageText"/>
+        <result column="message_type" jdbcType="OTHER" property="messageType"/>
+        <result column="official_account_id" jdbcType="INTEGER" property="officialAccountId"/>
+        <result column="official_account_type" jdbcType="OTHER" property="officialAccountType"/>
+        <result column="created_from" jdbcType="OTHER" property="createdFrom"/>
+    </resultMap>
+    <sql id="Base_Column_List">
+        id, title, admin_id, message_json, sendtime, user_json, send_num, success_num, fail_num,
+    statue, createtime, updatetime, created_admin_id, custom_media_push_id, `message_text`, 
+    message_type, official_account_id, official_account_type, created_from
+    </sql>
+    <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
+        select
+        <include refid="Base_Column_List"/>
+        from custom
+        where id = #{id,jdbcType=INTEGER}
+    </select>
+    <select id="selectListBeforeSendTime" resultType="com.book.push.dao.pojo.Custom">
+        select *
+        from custom
+        where sendtime > UNIX_TIMESTAMP(NOW());
+
+    </select>
+    <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
+        delete
+        from custom
+        where id = #{id,jdbcType=INTEGER}
+    </delete>
+    <insert id="insert" keyColumn="id" keyProperty="id" parameterType="com.book.push.dao.pojo.Custom"
+            useGeneratedKeys="true">
+        insert into custom (title, admin_id, message_json,
+                            sendtime, user_json, send_num,
+                            success_num, fail_num, statue,
+                            createtime, updatetime, created_admin_id,
+                            custom_media_push_id, `message_text`, message_type,
+                            official_account_id, official_account_type, created_from)
+        values (#{title,jdbcType=VARCHAR}, #{adminId,jdbcType=INTEGER}, #{messageJson,jdbcType=OTHER},
+                #{sendtime,jdbcType=INTEGER}, #{userJson,jdbcType=OTHER}, #{sendNum,jdbcType=INTEGER},
+                #{successNum,jdbcType=INTEGER}, #{failNum,jdbcType=INTEGER}, #{statue,jdbcType=OTHER},
+                #{createtime,jdbcType=INTEGER}, #{updatetime,jdbcType=INTEGER}, #{createdAdminId,jdbcType=INTEGER},
+                #{customMediaPushId,jdbcType=INTEGER}, #{messageText,jdbcType=VARCHAR}, #{messageType,jdbcType=OTHER},
+                #{officialAccountId,jdbcType=INTEGER}, #{officialAccountType,jdbcType=OTHER},
+                #{createdFrom,jdbcType=OTHER})
+    </insert>
+    <insert id="insertSelective" keyColumn="id" keyProperty="id" parameterType="com.book.push.dao.pojo.Custom"
+            useGeneratedKeys="true">
+        insert into custom
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="title != null">
+                title,
+            </if>
+            <if test="adminId != null">
+                admin_id,
+            </if>
+            <if test="messageJson != null">
+                message_json,
+            </if>
+            <if test="sendtime != null">
+                sendtime,
+            </if>
+            <if test="userJson != null">
+                user_json,
+            </if>
+            <if test="sendNum != null">
+                send_num,
+            </if>
+            <if test="successNum != null">
+                success_num,
+            </if>
+            <if test="failNum != null">
+                fail_num,
+            </if>
+            <if test="statue != null">
+                statue,
+            </if>
+            <if test="createtime != null">
+                createtime,
+            </if>
+            <if test="updatetime != null">
+                updatetime,
+            </if>
+            <if test="createdAdminId != null">
+                created_admin_id,
+            </if>
+            <if test="customMediaPushId != null">
+                custom_media_push_id,
+            </if>
+            <if test="messageText != null">
+                `message_text`,
+            </if>
+            <if test="messageType != null">
+                message_type,
+            </if>
+            <if test="officialAccountId != null">
+                official_account_id,
+            </if>
+            <if test="officialAccountType != null">
+                official_account_type,
+            </if>
+            <if test="createdFrom != null">
+                created_from,
+            </if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="title != null">
+                #{title,jdbcType=VARCHAR},
+            </if>
+            <if test="adminId != null">
+                #{adminId,jdbcType=INTEGER},
+            </if>
+            <if test="messageJson != null">
+                #{messageJson,jdbcType=OTHER},
+            </if>
+            <if test="sendtime != null">
+                #{sendtime,jdbcType=INTEGER},
+            </if>
+            <if test="userJson != null">
+                #{userJson,jdbcType=OTHER},
+            </if>
+            <if test="sendNum != null">
+                #{sendNum,jdbcType=INTEGER},
+            </if>
+            <if test="successNum != null">
+                #{successNum,jdbcType=INTEGER},
+            </if>
+            <if test="failNum != null">
+                #{failNum,jdbcType=INTEGER},
+            </if>
+            <if test="statue != null">
+                #{statue,jdbcType=OTHER},
+            </if>
+            <if test="createtime != null">
+                #{createtime,jdbcType=INTEGER},
+            </if>
+            <if test="updatetime != null">
+                #{updatetime,jdbcType=INTEGER},
+            </if>
+            <if test="createdAdminId != null">
+                #{createdAdminId,jdbcType=INTEGER},
+            </if>
+            <if test="customMediaPushId != null">
+                #{customMediaPushId,jdbcType=INTEGER},
+            </if>
+            <if test="messageText != null">
+                #{messageText,jdbcType=VARCHAR},
+            </if>
+            <if test="messageType != null">
+                #{messageType,jdbcType=OTHER},
+            </if>
+            <if test="officialAccountId != null">
+                #{officialAccountId,jdbcType=INTEGER},
+            </if>
+            <if test="officialAccountType != null">
+                #{officialAccountType,jdbcType=OTHER},
+            </if>
+            <if test="createdFrom != null">
+                #{createdFrom,jdbcType=OTHER},
+            </if>
+        </trim>
+    </insert>
+    <update id="updateByPrimaryKeySelective" parameterType="com.book.push.dao.pojo.Custom">
+        update custom
+        <set>
+            <if test="title != null">
+                title = #{title,jdbcType=VARCHAR},
+            </if>
+            <if test="adminId != null">
+                admin_id = #{adminId,jdbcType=INTEGER},
+            </if>
+            <if test="messageJson != null">
+                message_json = #{messageJson,jdbcType=OTHER},
+            </if>
+            <if test="sendtime != null">
+                sendtime = #{sendtime,jdbcType=INTEGER},
+            </if>
+            <if test="userJson != null">
+                user_json = #{userJson,jdbcType=OTHER},
+            </if>
+            <if test="sendNum != null">
+                send_num = #{sendNum,jdbcType=INTEGER},
+            </if>
+            <if test="successNum != null">
+                success_num = #{successNum,jdbcType=INTEGER},
+            </if>
+            <if test="failNum != null">
+                fail_num = #{failNum,jdbcType=INTEGER},
+            </if>
+            <if test="statue != null">
+                statue = #{statue,jdbcType=OTHER},
+            </if>
+            <if test="createtime != null">
+                createtime = #{createtime,jdbcType=INTEGER},
+            </if>
+            <if test="updatetime != null">
+                updatetime = #{updatetime,jdbcType=INTEGER},
+            </if>
+            <if test="createdAdminId != null">
+                created_admin_id = #{createdAdminId,jdbcType=INTEGER},
+            </if>
+            <if test="customMediaPushId != null">
+                custom_media_push_id = #{customMediaPushId,jdbcType=INTEGER},
+            </if>
+            <if test="messageText != null">
+                `message_text` = #{messageText,jdbcType=VARCHAR},
+            </if>
+            <if test="messageType != null">
+                message_type = #{messageType,jdbcType=OTHER},
+            </if>
+            <if test="officialAccountId != null">
+                official_account_id = #{officialAccountId,jdbcType=INTEGER},
+            </if>
+            <if test="officialAccountType != null">
+                official_account_type = #{officialAccountType,jdbcType=OTHER},
+            </if>
+            <if test="createdFrom != null">
+                created_from = #{createdFrom,jdbcType=OTHER},
+            </if>
+        </set>
+        where id = #{id,jdbcType=INTEGER}
+    </update>
+    <update id="updateByPrimaryKey" parameterType="com.book.push.dao.pojo.Custom">
+        update custom
+        set title                 = #{title,jdbcType=VARCHAR},
+            admin_id              = #{adminId,jdbcType=INTEGER},
+            message_json          = #{messageJson,jdbcType=OTHER},
+            sendtime              = #{sendtime,jdbcType=INTEGER},
+            user_json             = #{userJson,jdbcType=OTHER},
+            send_num              = #{sendNum,jdbcType=INTEGER},
+            success_num           = #{successNum,jdbcType=INTEGER},
+            fail_num              = #{failNum,jdbcType=INTEGER},
+            statue                = #{statue,jdbcType=OTHER},
+            createtime            = #{createtime,jdbcType=INTEGER},
+            updatetime            = #{updatetime,jdbcType=INTEGER},
+            created_admin_id      = #{createdAdminId,jdbcType=INTEGER},
+            custom_media_push_id  = #{customMediaPushId,jdbcType=INTEGER},
+            `message_text`        = #{messageText,jdbcType=VARCHAR},
+            message_type          = #{messageType,jdbcType=OTHER},
+            official_account_id   = #{officialAccountId,jdbcType=INTEGER},
+            official_account_type = #{officialAccountType,jdbcType=OTHER},
+            created_from          = #{createdFrom,jdbcType=OTHER}
+        where id = #{id,jdbcType=INTEGER}
+    </update>
+</mapper>

+ 366 - 0
book-push/src/main/resources/mapper/UserMapper.xml

@@ -0,0 +1,366 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.book.push.dao.mapper.UserMapper">
+  <resultMap id="BaseResultMap" type="com.book.push.dao.pojo.User">
+    <id column="id" jdbcType="BIGINT" property="id" />
+    <result column="openid" jdbcType="VARCHAR" property="openid" />
+    <result column="unionid" jdbcType="VARCHAR" property="unionid" />
+    <result column="visitor" jdbcType="VARCHAR" property="visitor" />
+    <result column="nickname" jdbcType="VARCHAR" property="nickname" />
+    <result column="sex" jdbcType="OTHER" property="sex" />
+    <result column="mobile" jdbcType="CHAR" property="mobile" />
+    <result column="avatar" jdbcType="VARCHAR" property="avatar" />
+    <result column="is_subscribe" jdbcType="OTHER" property="isSubscribe" />
+    <result column="subscription_extend" jdbcType="OTHER" property="subscriptionExtend" />
+    <result column="subscribe_time" jdbcType="INTEGER" property="subscribeTime" />
+    <result column="book_category_ids" jdbcType="VARCHAR" property="bookCategoryIds" />
+    <result column="operate_time" jdbcType="INTEGER" property="operateTime" />
+    <result column="is_pay" jdbcType="OTHER" property="isPay" />
+    <result column="kandian" jdbcType="INTEGER" property="kandian" />
+    <result column="free_kandian" jdbcType="INTEGER" property="freeKandian" />
+    <result column="vip_endtime" jdbcType="INTEGER" property="vipEndtime" />
+    <result column="register_ip" jdbcType="VARCHAR" property="registerIp" />
+    <result column="country" jdbcType="VARCHAR" property="country" />
+    <result column="area" jdbcType="VARCHAR" property="area" />
+    <result column="province" jdbcType="VARCHAR" property="province" />
+    <result column="city" jdbcType="VARCHAR" property="city" />
+    <result column="isp" jdbcType="VARCHAR" property="isp" />
+    <result column="channel_id" jdbcType="INTEGER" property="channelId" />
+    <result column="state" jdbcType="OTHER" property="state" />
+    <result column="createtime" jdbcType="INTEGER" property="createtime" />
+    <result column="updatetime" jdbcType="INTEGER" property="updatetime" />
+  </resultMap>
+  <sql id="Base_Column_List">
+    id, openid, unionid, visitor, nickname, sex, mobile, avatar, is_subscribe, subscription_extend, 
+    subscribe_time, book_category_ids, operate_time, is_pay, kandian, free_kandian, vip_endtime, 
+    register_ip, country, area, province, city, isp, channel_id, `state`, createtime, 
+    updatetime
+  </sql>
+  <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
+    select 
+    <include refid="Base_Column_List" />
+    from user
+    where id = #{id,jdbcType=BIGINT}
+  </select>
+    <select id="selectByUser" resultType="com.book.push.dao.pojo.User" parameterType="com.book.push.dao.pojo.User">
+
+        select id from user
+      <trim prefix="WHERE" prefixOverrides="AND | OR">
+        <if test="openid!=null and openid!=''">
+          and openid != #{openid}
+        </if>
+        <if test="mobile!=null and mobile!=''">
+          and mobile != #{mobile}
+        </if>
+        <if test="channel_id!=null and channel_id!=0">
+          and channel_id != #{channel_id}
+        </if>
+
+
+      </trim>
+
+
+
+    </select>
+    <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
+    delete from user
+    where id = #{id,jdbcType=BIGINT}
+  </delete>
+  <insert id="insert" keyColumn="id" keyProperty="id" parameterType="com.book.push.dao.pojo.User" useGeneratedKeys="true">
+    insert into user (openid, unionid, visitor, 
+      nickname, sex, mobile, avatar, 
+      is_subscribe, subscription_extend, subscribe_time, 
+      book_category_ids, operate_time, is_pay, 
+      kandian, free_kandian, vip_endtime, 
+      register_ip, country, area, 
+      province, city, isp, 
+      channel_id, `state`, createtime, 
+      updatetime)
+    values (#{openid,jdbcType=VARCHAR}, #{unionid,jdbcType=VARCHAR}, #{visitor,jdbcType=VARCHAR}, 
+      #{nickname,jdbcType=VARCHAR}, #{sex,jdbcType=OTHER}, #{mobile,jdbcType=CHAR}, #{avatar,jdbcType=VARCHAR}, 
+      #{isSubscribe,jdbcType=OTHER}, #{subscriptionExtend,jdbcType=OTHER}, #{subscribeTime,jdbcType=INTEGER}, 
+      #{bookCategoryIds,jdbcType=VARCHAR}, #{operateTime,jdbcType=INTEGER}, #{isPay,jdbcType=OTHER}, 
+      #{kandian,jdbcType=INTEGER}, #{freeKandian,jdbcType=INTEGER}, #{vipEndtime,jdbcType=INTEGER}, 
+      #{registerIp,jdbcType=VARCHAR}, #{country,jdbcType=VARCHAR}, #{area,jdbcType=VARCHAR}, 
+      #{province,jdbcType=VARCHAR}, #{city,jdbcType=VARCHAR}, #{isp,jdbcType=VARCHAR}, 
+      #{channelId,jdbcType=INTEGER}, #{state,jdbcType=OTHER}, #{createtime,jdbcType=INTEGER}, 
+      #{updatetime,jdbcType=INTEGER})
+  </insert>
+  <insert id="insertSelective" keyColumn="id" keyProperty="id" parameterType="com.book.push.dao.pojo.User" useGeneratedKeys="true">
+    insert into user
+    <trim prefix="(" suffix=")" suffixOverrides=",">
+      <if test="openid != null">
+        openid,
+      </if>
+      <if test="unionid != null">
+        unionid,
+      </if>
+      <if test="visitor != null">
+        visitor,
+      </if>
+      <if test="nickname != null">
+        nickname,
+      </if>
+      <if test="sex != null">
+        sex,
+      </if>
+      <if test="mobile != null">
+        mobile,
+      </if>
+      <if test="avatar != null">
+        avatar,
+      </if>
+      <if test="isSubscribe != null">
+        is_subscribe,
+      </if>
+      <if test="subscriptionExtend != null">
+        subscription_extend,
+      </if>
+      <if test="subscribeTime != null">
+        subscribe_time,
+      </if>
+      <if test="bookCategoryIds != null">
+        book_category_ids,
+      </if>
+      <if test="operateTime != null">
+        operate_time,
+      </if>
+      <if test="isPay != null">
+        is_pay,
+      </if>
+      <if test="kandian != null">
+        kandian,
+      </if>
+      <if test="freeKandian != null">
+        free_kandian,
+      </if>
+      <if test="vipEndtime != null">
+        vip_endtime,
+      </if>
+      <if test="registerIp != null">
+        register_ip,
+      </if>
+      <if test="country != null">
+        country,
+      </if>
+      <if test="area != null">
+        area,
+      </if>
+      <if test="province != null">
+        province,
+      </if>
+      <if test="city != null">
+        city,
+      </if>
+      <if test="isp != null">
+        isp,
+      </if>
+      <if test="channelId != null">
+        channel_id,
+      </if>
+      <if test="state != null">
+        `state`,
+      </if>
+      <if test="createtime != null">
+        createtime,
+      </if>
+      <if test="updatetime != null">
+        updatetime,
+      </if>
+    </trim>
+    <trim prefix="values (" suffix=")" suffixOverrides=",">
+      <if test="openid != null">
+        #{openid,jdbcType=VARCHAR},
+      </if>
+      <if test="unionid != null">
+        #{unionid,jdbcType=VARCHAR},
+      </if>
+      <if test="visitor != null">
+        #{visitor,jdbcType=VARCHAR},
+      </if>
+      <if test="nickname != null">
+        #{nickname,jdbcType=VARCHAR},
+      </if>
+      <if test="sex != null">
+        #{sex,jdbcType=OTHER},
+      </if>
+      <if test="mobile != null">
+        #{mobile,jdbcType=CHAR},
+      </if>
+      <if test="avatar != null">
+        #{avatar,jdbcType=VARCHAR},
+      </if>
+      <if test="isSubscribe != null">
+        #{isSubscribe,jdbcType=OTHER},
+      </if>
+      <if test="subscriptionExtend != null">
+        #{subscriptionExtend,jdbcType=OTHER},
+      </if>
+      <if test="subscribeTime != null">
+        #{subscribeTime,jdbcType=INTEGER},
+      </if>
+      <if test="bookCategoryIds != null">
+        #{bookCategoryIds,jdbcType=VARCHAR},
+      </if>
+      <if test="operateTime != null">
+        #{operateTime,jdbcType=INTEGER},
+      </if>
+      <if test="isPay != null">
+        #{isPay,jdbcType=OTHER},
+      </if>
+      <if test="kandian != null">
+        #{kandian,jdbcType=INTEGER},
+      </if>
+      <if test="freeKandian != null">
+        #{freeKandian,jdbcType=INTEGER},
+      </if>
+      <if test="vipEndtime != null">
+        #{vipEndtime,jdbcType=INTEGER},
+      </if>
+      <if test="registerIp != null">
+        #{registerIp,jdbcType=VARCHAR},
+      </if>
+      <if test="country != null">
+        #{country,jdbcType=VARCHAR},
+      </if>
+      <if test="area != null">
+        #{area,jdbcType=VARCHAR},
+      </if>
+      <if test="province != null">
+        #{province,jdbcType=VARCHAR},
+      </if>
+      <if test="city != null">
+        #{city,jdbcType=VARCHAR},
+      </if>
+      <if test="isp != null">
+        #{isp,jdbcType=VARCHAR},
+      </if>
+      <if test="channelId != null">
+        #{channelId,jdbcType=INTEGER},
+      </if>
+      <if test="state != null">
+        #{state,jdbcType=OTHER},
+      </if>
+      <if test="createtime != null">
+        #{createtime,jdbcType=INTEGER},
+      </if>
+      <if test="updatetime != null">
+        #{updatetime,jdbcType=INTEGER},
+      </if>
+    </trim>
+  </insert>
+  <update id="updateByPrimaryKeySelective" parameterType="com.book.push.dao.pojo.User">
+    update user
+    <set>
+      <if test="openid != null">
+        openid = #{openid,jdbcType=VARCHAR},
+      </if>
+      <if test="unionid != null">
+        unionid = #{unionid,jdbcType=VARCHAR},
+      </if>
+      <if test="visitor != null">
+        visitor = #{visitor,jdbcType=VARCHAR},
+      </if>
+      <if test="nickname != null">
+        nickname = #{nickname,jdbcType=VARCHAR},
+      </if>
+      <if test="sex != null">
+        sex = #{sex,jdbcType=OTHER},
+      </if>
+      <if test="mobile != null">
+        mobile = #{mobile,jdbcType=CHAR},
+      </if>
+      <if test="avatar != null">
+        avatar = #{avatar,jdbcType=VARCHAR},
+      </if>
+      <if test="isSubscribe != null">
+        is_subscribe = #{isSubscribe,jdbcType=OTHER},
+      </if>
+      <if test="subscriptionExtend != null">
+        subscription_extend = #{subscriptionExtend,jdbcType=OTHER},
+      </if>
+      <if test="subscribeTime != null">
+        subscribe_time = #{subscribeTime,jdbcType=INTEGER},
+      </if>
+      <if test="bookCategoryIds != null">
+        book_category_ids = #{bookCategoryIds,jdbcType=VARCHAR},
+      </if>
+      <if test="operateTime != null">
+        operate_time = #{operateTime,jdbcType=INTEGER},
+      </if>
+      <if test="isPay != null">
+        is_pay = #{isPay,jdbcType=OTHER},
+      </if>
+      <if test="kandian != null">
+        kandian = #{kandian,jdbcType=INTEGER},
+      </if>
+      <if test="freeKandian != null">
+        free_kandian = #{freeKandian,jdbcType=INTEGER},
+      </if>
+      <if test="vipEndtime != null">
+        vip_endtime = #{vipEndtime,jdbcType=INTEGER},
+      </if>
+      <if test="registerIp != null">
+        register_ip = #{registerIp,jdbcType=VARCHAR},
+      </if>
+      <if test="country != null">
+        country = #{country,jdbcType=VARCHAR},
+      </if>
+      <if test="area != null">
+        area = #{area,jdbcType=VARCHAR},
+      </if>
+      <if test="province != null">
+        province = #{province,jdbcType=VARCHAR},
+      </if>
+      <if test="city != null">
+        city = #{city,jdbcType=VARCHAR},
+      </if>
+      <if test="isp != null">
+        isp = #{isp,jdbcType=VARCHAR},
+      </if>
+      <if test="channelId != null">
+        channel_id = #{channelId,jdbcType=INTEGER},
+      </if>
+      <if test="state != null">
+        `state` = #{state,jdbcType=OTHER},
+      </if>
+      <if test="createtime != null">
+        createtime = #{createtime,jdbcType=INTEGER},
+      </if>
+      <if test="updatetime != null">
+        updatetime = #{updatetime,jdbcType=INTEGER},
+      </if>
+    </set>
+    where id = #{id,jdbcType=BIGINT}
+  </update>
+  <update id="updateByPrimaryKey" parameterType="com.book.push.dao.pojo.User">
+    update user
+    set openid = #{openid,jdbcType=VARCHAR},
+      unionid = #{unionid,jdbcType=VARCHAR},
+      visitor = #{visitor,jdbcType=VARCHAR},
+      nickname = #{nickname,jdbcType=VARCHAR},
+      sex = #{sex,jdbcType=OTHER},
+      mobile = #{mobile,jdbcType=CHAR},
+      avatar = #{avatar,jdbcType=VARCHAR},
+      is_subscribe = #{isSubscribe,jdbcType=OTHER},
+      subscription_extend = #{subscriptionExtend,jdbcType=OTHER},
+      subscribe_time = #{subscribeTime,jdbcType=INTEGER},
+      book_category_ids = #{bookCategoryIds,jdbcType=VARCHAR},
+      operate_time = #{operateTime,jdbcType=INTEGER},
+      is_pay = #{isPay,jdbcType=OTHER},
+      kandian = #{kandian,jdbcType=INTEGER},
+      free_kandian = #{freeKandian,jdbcType=INTEGER},
+      vip_endtime = #{vipEndtime,jdbcType=INTEGER},
+      register_ip = #{registerIp,jdbcType=VARCHAR},
+      country = #{country,jdbcType=VARCHAR},
+      area = #{area,jdbcType=VARCHAR},
+      province = #{province,jdbcType=VARCHAR},
+      city = #{city,jdbcType=VARCHAR},
+      isp = #{isp,jdbcType=VARCHAR},
+      channel_id = #{channelId,jdbcType=INTEGER},
+      `state` = #{state,jdbcType=OTHER},
+      createtime = #{createtime,jdbcType=INTEGER},
+      updatetime = #{updatetime,jdbcType=INTEGER}
+    where id = #{id,jdbcType=BIGINT}
+  </update>
+</mapper>

+ 1 - 0
book-server/pom.xml

@@ -160,5 +160,6 @@
             <artifactId>mysql-connector-java</artifactId>
             <version>8.0.16</version>
         </dependency>
+
     </dependencies>
 </project>

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

@@ -2,6 +2,7 @@ package com.book.server.common.util;
 
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
+import com.google.gson.JsonObject;
 import com.google.gson.reflect.TypeToken;
 
 import java.util.List;

+ 38 - 1
book-server/src/main/java/com/book/server/controller/BookController.java

@@ -2,19 +2,22 @@ package com.book.server.controller;
 
 import com.book.server.common.entity.PageResult;
 import com.book.server.common.entity.Result;
+import com.book.server.common.entity.ResultCode;
 import com.book.server.dao.entity.Book;
 import com.book.server.model.VO.BlockRes;
 import com.book.server.model.VO.BookRes;
+import com.book.server.model.VO.Chapter;
 import com.book.server.model.VO.QueryVO;
 import com.book.server.service.BookService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.cache.annotation.CacheEvict;
 import org.springframework.cache.annotation.Cacheable;
-import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.util.CollectionUtils;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * 书籍
@@ -73,4 +76,38 @@ public class BookController extends BaseController {
         return Result.byObject("OK");
     }
 
+
+    /**
+     * 导入书库
+     * 注意:此代码仅限拉取书籍使用,只能运行一次。
+     *
+     * @return
+     */
+
+    @GetMapping("/insertBooks")
+    public Result insertBooks() {
+        boolean open = false;
+        if (open) {
+            bookService.insertBooks();
+        }
+        return Result.byObject("OK");
+    }
+
+    /**
+     * 获取章节列表
+     * @param book
+     * @return
+     */
+    @PostMapping("/chapterList")
+    public Result<List<Chapter>> getChaptersByPageAndRow(@RequestBody Map<String,String> book) {
+
+      List<Chapter>  chapterList =  bookService.getChaptersByBookId(book.get("bookId"));
+      if ( CollectionUtils.isEmpty(chapterList)){
+          return Result.failure(ResultCode.FAIL.getCode(),"哎呀~没有数据啦~");
+      }else {
+          return Result.byObject(chapterList);
+      }
+    }
+
+
 }

+ 23 - 0
book-server/src/main/java/com/book/server/model/VO/Chapter.java

@@ -0,0 +1,23 @@
+package com.book.server.model.VO;
+
+import lombok.Data;
+
+/**
+ * created in 2021/8/16
+ * Project: book-store
+ *
+ * @author win7
+ */
+@Data
+public class Chapter {
+    private String bookId;
+    private String contentId;
+    private String isPay;
+    private String name;
+    private String num;
+    private String type;
+    private String words;
+    private String volumeId;
+
+
+}

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

@@ -4,6 +4,7 @@ import com.book.server.common.entity.PageResult;
 import com.book.server.dao.entity.Book;
 import com.book.server.model.VO.BlockRes;
 import com.book.server.model.VO.BookRes;
+import com.book.server.model.VO.Chapter;
 import com.book.server.model.VO.QueryVO;
 
 import java.util.List;
@@ -14,4 +15,8 @@ public interface BookService {
 
     List<BlockRes> getBlockByPageId(Integer pageId);
 
+    void insertBooks();
+
+    List<Chapter> getChaptersByBookId(String bookId);
+
 }

+ 158 - 9
book-server/src/main/java/com/book/server/service/impl/BookServiceImpl.java

@@ -1,7 +1,8 @@
 package com.book.server.service.impl;
 
+
 import com.book.server.common.entity.PageResult;
-import com.book.server.common.entity.Result;
+import com.book.server.common.util.JsonUtils;
 import com.book.server.dao.entity.Book;
 import com.book.server.dao.entity.BookCategory;
 import com.book.server.dao.entity.ManageBlock;
@@ -16,19 +17,24 @@ import com.book.server.model.VO.BookRes;
 import com.book.server.model.VO.ManageBlockResourceRes;
 import com.book.server.model.VO.QueryVO;
 import com.book.server.service.BookService;
+import com.book.server.utils.HttpTool;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.cache.annotation.CacheEvict;
-import org.springframework.cache.annotation.Cacheable;
 import org.springframework.stereotype.Service;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
+import java.util.*;
 import java.util.stream.Collectors;
 
 @Service
 public class BookServiceImpl implements BookService {
+    public static final Logger logger = LoggerFactory.getLogger(BookServiceImpl.class);
     @Autowired
     private BookMapper bookMapper;
     @Autowired
@@ -97,12 +103,12 @@ public class BookServiceImpl implements BookService {
                                 .example()
                 );
                 HashMap<Long, Book> bookHashMap = new HashMap<>();
-                books.forEach(x->{
+                books.forEach(x -> {
                     bookHashMap.put(x.getId(), x);
                 });
-                manageBlockResources.forEach(x->{
+                manageBlockResources.forEach(x -> {
                     ManageBlockResourceRes manageBlockResourceRes = new ManageBlockResourceRes();
-                    BeanUtils.copyProperties(x,manageBlockResourceRes);
+                    BeanUtils.copyProperties(x, manageBlockResourceRes);
                     manageBlockResourceResList.add(manageBlockResourceRes);
 
                     Book book = bookHashMap.get(x.getBookId());
@@ -124,5 +130,148 @@ public class BookServiceImpl implements BookService {
         return list;
     }
 
+    @Override
+    public void insertBooks() {
+
+        //插入书籍基本信息
+        String bookInfoUrl = "http://new.emeixs.com/index.php/Home/Interface/listing";
+
+        String jsonStr = HttpTool.sendGet(bookInfoUrl, "");
+        JsonObject jsonObject = JsonUtils.getObject(jsonStr, JsonObject.class);
+        JsonArray list = jsonObject.getAsJsonArray("list");
+
+        Map<String, String> typeMap = new HashMap<String, String>();
+        typeMap.put("15", "豪门总裁");
+        typeMap.put("16", "婚恋家庭");
+        typeMap.put("17", "豪门虐恋");
+        typeMap.put("18", "甜宠萌宝");
+        typeMap.put("19", "重生逆袭");
+        typeMap.put("20", "穿越重生");
+        typeMap.put("21", "权谋宫斗");
+        typeMap.put("22", "经商种田");
+        typeMap.put("23", "出版");
+        typeMap.put("24", "其他");
+        List<Book> bookList = new ArrayList<>();
+        for (int i = 0; i < list.size(); i++) {
+            JsonElement jsonElement = list.get(i);
+            JsonObject bookObject = jsonElement.getAsJsonObject();
+            String author_name = bookObject.get("author_name").getAsString();
+            String book_brief = bookObject.get("book_brief").getAsString();
+            String book_id = bookObject.get("book_id").getAsString();
+            String book_name = bookObject.get("book_name").getAsString();
+            String chapter = bookObject.get("chapter").getAsString();
+            String charge = bookObject.get("charge").getAsString();
+            String cover = bookObject.get("cover").getAsString();
+            String ctype_id = bookObject.get("ctype_id").getAsString();
+            String gender = bookObject.get("gender").getAsString();
+            String is_show = bookObject.get("is_show").getAsString();
+            String state = bookObject.get("state").getAsString();
+            String type_id = bookObject.get("type_id").getAsString();
+            String words = bookObject.get("words").getAsString();
+
+            //章节列表
+            String chaptersStr = HttpTool.sendGet("http://new.emeixs.com/index.php/Home/Interface/chapters/bid/" + book_id, "");
+            JsonParser parser = new JsonParser();
+            JsonArray jsonArray = parser.parse(chaptersStr).getAsJsonArray();
+            JsonObject firstChapters = jsonArray.get(1).getAsJsonObject();
+            String first_chapter_id = firstChapters.get("content_id").getAsString();
+            String first_chapter_name = firstChapters.get("name").getAsString();
+            JsonObject lastChapters = jsonArray.get(jsonArray.size() - 1).getAsJsonObject();
+            String last_chapter_id = lastChapters.get("content_id").getAsString();
+            String last_chapter_name = lastChapters.get("name").getAsString();
+
+
+            Book book = new Book();
+            book.setId(Long.parseLong(book_id));
+            book.setBookCategoryId(Integer.parseInt(type_id));
+            book.setName(book_name);
+            book.setRealname(book_name);
+            book.setAuthor(author_name);
+            book.setImage(cover);
+            book.setDescription(book_brief);
+            book.setSex(gender);
+            book.setBillingType(String.valueOf(Integer.parseInt(charge) + 1));
+            book.setWordCount(words);
+            book.setChapterNum(Integer.valueOf(chapter));
+//            book.setLastChapterUtime(Integer.parseInt(System.currentTimeMillis()+""));
+            book.setCreatetime(Integer.parseInt(System.currentTimeMillis() / 1000 + ""));
+            book.setCpId(Integer.parseInt(book_id));
+            book.setCpName(book_name);
+            book.setTags(typeMap.get(ctype_id));
+            book.setState("1");
+            book.setPrice(45);
+            book.setArticleChapterOrder(4);
+            book.setAttentChapterOrder(4);
+            book.setCornerMark("hot");
+            book.setIsFinish(String.valueOf(Integer.parseInt(state) - 1));
+            book.setReferralNum(new Byte((byte) 5));
+            book.setPutAdSet(new Byte((byte) 1));
+            book.setClassifyWhiteList(new Byte((byte) 0));
+            book.setCheckRank(new Byte((byte) 0));
+            book.setRank(new Byte((byte) 6));
+            book.setExpireTime(Integer.MAX_VALUE);
+            book.setLastChapterUtime(Integer.parseInt(System.currentTimeMillis() / 1000 + ""));
+            book.setAppPrice(45);
+            book.setFirstChapterId(Long.parseLong(first_chapter_id));
+            book.setFirstChapterName(first_chapter_name);
+            book.setLastChapterId(Long.parseLong(last_chapter_id));
+            book.setLastChapterName(last_chapter_name);
+            book.setCansee("1");
+            book.setRank(new Byte((byte) 6));
+
+            bookList.add(book);
+
+
+        }
+
+        for (Book book : bookList) {
+            int i = new Random().nextInt(bookList.size());
+            book.setRecommandBookId(bookList.get(i).getId());
+            System.out.println(JsonUtils.toJsonStr(book));
+               bookMapper.insert(book);
+
+
+        }
+
+    }
+
+    @Override
+    public List<Chapter> getChaptersByBookId(String bookId) {
+        List<Chapter> list = new ArrayList<>();
+        try {
+            if (StringUtils.isEmpty(bookId)) {
+                throw new IllegalArgumentException("bookId cannot be null or empty");
+            }
+            final String chaptersUrl = "http://new.emeixs.com/index.php/Home/Interface/chapters/bid/" + bookId;
+            String result = HttpTool.sendGet(chaptersUrl, "");
+            if (StringUtils.isEmpty(result)) {
+                return list;
+            }
+            JsonParser parser = new JsonParser();
+            JsonArray jsonArray = parser.parse(result).getAsJsonArray();
+
+            for (int i = 0; i < jsonArray.size(); i++) {
+                JsonObject jsonObject = jsonArray.get(i).getAsJsonObject();
+                Chapter chapter = new Chapter();
+                chapter.setBookId(jsonObject.get("book_id").getAsString());
+                chapter.setContentId(jsonObject.get("content_id").getAsString());
+                chapter.setIsPay(jsonObject.get("ispay").getAsString());
+                chapter.setName(jsonObject.get("name").getAsString());
+                chapter.setNum(jsonObject.get("num").getAsString());
+                chapter.setType(jsonObject.get("type").getAsString());
+                chapter.setVolumeId(jsonObject.get("volume_id").getAsString());
+                chapter.setWords(jsonObject.get("words").getAsString());
+                list.add(chapter);
+            }
+        } catch (Exception e) {
+            logger.error(e.getMessage());
+            e.printStackTrace();
+
+            return list;
+        }
+
+        return list;
+    }
+
 
 }

+ 397 - 0
book-server/src/main/java/com/book/server/utils/HttpTool.java

@@ -0,0 +1,397 @@
+package com.book.server.utils;
+
+
+import javax.net.ssl.HttpsURLConnection;
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.*;
+
+public class HttpTool {
+    public static final String GET = "GET";
+    public static final String POST = "POST";
+    // 签名key
+    public final static String SIGNATURE = "sign";
+    // 签名方法key
+    public final static String SIGN_METHOD = "sign_type";
+    // =
+    public static final String QSTRING_EQUAL = "=";
+    // &
+    public static final String QSTRING_SPLIT = "&";
+
+    /**
+     * 向指定URL发送GET方法的请求
+     *
+     * @param address 发送请求的URL
+     * @param form    请求参数,请求参数应该是name1=value1&name2=value2的形式。
+     * @return URL所代表远程资源的响应
+     */
+
+    public static String sendGet(final String address, String form) {
+        String req = address + "?" + form;
+        System.out.println("请求:" + req);
+        StringBuilder response = new StringBuilder();
+        HttpURLConnection connection = null;
+        InputStream in = null;
+        try {
+            // 创建一个URL对象并传入地址
+            URL url = new URL(req);
+            // 用地址打开连接通道
+            connection = (HttpURLConnection) url.openConnection();
+            // 设置请求方式为get
+            connection.setRequestMethod("GET");
+            // 设置连接超时为8秒
+            connection.setConnectTimeout(8000);
+            // 设置读取超时为8秒
+            connection.setReadTimeout(8000);
+            connection.setRequestProperty("Charset", "utf-8");
+            // 设置可取
+//			connection.setDoInput(true);
+//			// 设置可读
+//			connection.setDoOutput(true);
+//			// 得到输入流
+            in = connection.getInputStream();
+
+            // 创建高效流对象
+            BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
+            // 创建StringBuilder对象存储数据
+
+            String line;// 一次读取一行
+            while ((line = reader.readLine()) != null) {
+                response.append(line);// 得到的数据存入StringBuilder
+            }
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (connection != null) {// 通道不为null
+                connection.disconnect();// 关闭通道
+            }
+            try {
+                if (in != null) {
+                    in.close();
+                }
+
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        return response.toString();
+    }
+
+    /**
+     * 向指定URL发送GET方法的请求
+     *
+     * @param url   发送请求的URL
+     * @param param 请求参数,请求参数应该是name1=value1&name2=value2的形式。
+     * @return URL所代表远程资源的响应
+     */
+
+    public static String sendGetCookie(String url, String param, String cookie) {
+        String result = "";
+        BufferedReader in = null;
+        try {
+            String urlName = url + "?" + param;
+            System.out.println(urlName);
+            URL realUrl = new URL(urlName);
+            // 打开和URL之间的连接
+            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("Cookie", cookie);
+            // 建立实际的连接
+            conn.connect();
+            // 获取所有响应头字段
+            Map<String, List<String>> map = conn.getHeaderFields();
+            // 遍历所有的响应头字段
+            // for (String key : map.keySet()) {
+            // System.out.println(key + "--->" + map.get(key));
+            // }
+            // 定义BufferedReader输入流来读取URL的响应
+            in = new BufferedReader(
+                    new InputStreamReader(conn.getInputStream()));
+            String line;
+            while ((line = in.readLine()) != null) {
+                result += line;
+                // result += "/n" + line;
+            }
+        } catch (Exception e) {
+            System.out.println("发送GET请求出现异常!" + e);
+            e.printStackTrace();
+        }
+        // 使用finally块来关闭输入流
+        finally {
+            try {
+                if (in != null) {
+                    in.close();
+                }
+            } catch (IOException ex) {
+                ex.printStackTrace();
+            }
+        }
+        return result;
+    }
+
+    /**
+     * 向指定URL发送POST方法的请求
+     *
+     * @param url   发送请求的URL
+     * @param param 请求参数,请求参数应该是name1=value1&name2=value2的形式。
+     * @return URL所代表远程资源的响应
+     */
+    public static String sendPost(String url, String param) {
+        PrintWriter out = null;
+        BufferedReader in = null;
+        String result = "";
+        try {
+            URL realUrl = new URL(url);
+            // 打开和URL之间的连接
+            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)");
+            // 发送POST请求必须设置如下两行
+            conn.setDoOutput(true);
+            conn.setDoInput(true);
+            // 获取URLConnection对象对应的输出流
+            out = new PrintWriter(conn.getOutputStream());
+            // 发送请求参数
+            out.print(param);
+            // flush输出流的缓冲
+            out.flush();
+            // 定义BufferedReader输入流来读取URL的响应
+            in = new BufferedReader(
+                    new InputStreamReader(conn.getInputStream()));
+            String line;
+            while ((line = in.readLine()) != null) {
+                result += line;
+                // result += "/n" + line;
+            }
+        } catch (Exception e) {
+            System.out.println("发送POST请求出现异常!" + e);
+            e.printStackTrace();
+        }
+        // 使用finally块来关闭输出流、输入流
+        finally {
+            try {
+                if (out != null) {
+                    out.close();
+                }
+                if (in != null) {
+                    in.close();
+                }
+            } catch (IOException ex) {
+                ex.printStackTrace();
+            }
+        }
+        return result;
+    }
+
+    /**
+     * 向指定URL发送POST方法的请求
+     *
+     * @param url   发送请求的URL
+     * @param param 请求参数,请求参数应该是name1=value1&name2=value2的形式。
+     * @return URL所代表远程资源的响应
+     */
+    public static String sendPostCookie(String url, String param, String cookie) {
+        PrintWriter out = null;
+        BufferedReader in = null;
+        String result = "";
+        try {
+            URL realUrl = new URL(url);
+            // 打开和URL之间的连接
+            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("Cookie", cookie);
+            // 发送POST请求必须设置如下两行
+            conn.setDoOutput(true);
+            conn.setDoInput(true);
+            // 获取URLConnection对象对应的输出流
+            out = new PrintWriter(conn.getOutputStream());
+            // 发送请求参数
+            out.print(param);
+            // flush输出流的缓冲
+            out.flush();
+            // 定义BufferedReader输入流来读取URL的响应
+            in = new BufferedReader(
+                    new InputStreamReader(conn.getInputStream()));
+            String line;
+            while ((line = in.readLine()) != null) {
+                result += line;
+                // result += "/n" + line;
+            }
+        } catch (Exception e) {
+            System.out.println("发送POST请求出现异常!" + e);
+            e.printStackTrace();
+        }
+        // 使用finally块来关闭输出流、输入流
+        finally {
+            try {
+                if (out != null) {
+                    out.close();
+                }
+                if (in != null) {
+                    in.close();
+                }
+            } catch (IOException ex) {
+                ex.printStackTrace();
+            }
+        }
+        return result;
+    }
+
+    /**
+     * https访问
+     */
+    public static String doHttps(String httpurl, String method, String param) {
+        try {
+            URL reqURL = new URL(httpurl + param); // 创建URL对象
+            HttpsURLConnection httpsConn = (HttpsURLConnection) reqURL
+                    .openConnection();
+
+            /*
+             * 下面这段代码实现向Web页面发送数据,实现与网页的交互访问 httpsConn.setDoOutput(true);
+             * OutputStreamWriter out = new
+             * OutputStreamWriter(huc.getOutputStream(), "8859_1"); out.write(
+             * "……" ); out.flush(); out.close();
+             */
+
+            // 取得该连接的输入流,以读取响应内容
+            InputStreamReader insr = new InputStreamReader(
+                    httpsConn.getInputStream());
+
+            // 读取服务器的响应内容并显示
+            int respInt = insr.read();
+            while (respInt != -1) {
+                System.out.print((char) respInt);
+                respInt = insr.read();
+            }
+        } catch (Exception e) {
+            // TODO: handle exception
+        }
+
+        return "";
+    }
+
+
+    /**
+     * 除去请求要素中的空值和签名参数
+     *
+     * @param para 请求要素
+     * @return 去掉空值与签名参数后的请求要素
+     */
+    public static Map<String, String> paraFilter(Map<String, String> para) {
+
+        Map<String, String> result = new HashMap<String, String>();
+
+        if (para == null || para.size() <= 0) {
+            return result;
+        }
+
+        for (String key : para.keySet()) {
+            String value = para.get(key);
+            if (value == null || value.equals("")
+                    || key.equalsIgnoreCase(SIGNATURE)
+                    || key.equalsIgnoreCase(SIGN_METHOD)) {
+                continue;
+            }
+            result.put(key, value);
+        }
+
+        return result;
+    }
+
+    /**
+     * 把请求要素按照“参数=参数值”的模式用“&”字符拼接成字符串
+     *
+     * @param para   请求要素
+     * @param sort   是否需要根据key值作升序排列
+     * @param encode 是否需要URL编码
+     * @return 拼接成的字符串
+     */
+    public static String createLinkString(Map<String, String> para,
+                                          boolean sort, boolean encode) {
+
+        List<String> keys = new ArrayList<String>(para.keySet());
+
+        if (sort)
+            Collections.sort(keys);
+
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < keys.size(); i++) {
+            String key = keys.get(i);
+            String value = para.get(key);
+
+            if (encode) {
+                try {
+                    value = URLEncoder.encode(value, "utf-8");
+                } catch (UnsupportedEncodingException e) {
+                }
+            }
+
+            if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符
+                sb.append(key).append(QSTRING_EQUAL).append(value);
+            } else {
+                sb.append(key).append(QSTRING_EQUAL).append(value)
+                        .append(QSTRING_SPLIT);
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * 对传入的参数进行MD5摘要
+     *
+     * @param str 需进行MD5摘要的数据
+     * @return MD5摘要值
+     */
+    public static String md5Summary(String str) {
+
+        if (str == null) {
+            return null;
+        }
+
+        MessageDigest messageDigest = null;
+
+        try {
+            messageDigest = MessageDigest.getInstance("MD5");
+            messageDigest.reset();
+            messageDigest.update(str.getBytes(StandardCharsets.UTF_8));
+        } catch (NoSuchAlgorithmException e) {
+
+            return str;
+        }
+
+        byte[] byteArray = messageDigest.digest();
+
+        StringBuffer md5StrBuff = new StringBuffer();
+
+        for (int i = 0; i < byteArray.length; i++) {
+            if (Integer.toHexString(0xFF & byteArray[i]).length() == 1)
+                md5StrBuff.append("0").append(
+                        Integer.toHexString(0xFF & byteArray[i]));
+            else
+                md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i]));
+        }
+
+        return md5StrBuff.toString();
+    }
+
+
+
+}

+ 98 - 0
book-server/src/main/java/com/book/server/utils/JsonUtils.java

@@ -0,0 +1,98 @@
+package com.book.server.utils;
+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工具类
+ * created in 2021/8/17
+ * Project: book-store
+ *
+ * @author win7
+ */
+
+
+
+
+
+
+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());
+    }
+}

+ 52 - 0
book-server/src/main/resources/application-dev.yml

@@ -0,0 +1,52 @@
+server:
+  port: 8080
+
+spring:
+  datasource:
+    type: com.alibaba.druid.pool.DruidDataSource
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://121.41.100.198:3306/test_cps?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
+    username: root
+    password: root
+    #下面为连接池补充设置
+    druid:
+      initial-size: 5 # 初始化
+      max-active: 5 # 最大
+      min-idle: 5 # 最小
+      max-wait: 6000 # 超时时间
+      time-between-eviction-runs-millis: 60000 # 每分钟检查一次空闲链接
+      min-evictable-idle-time-millis: 300000 # 空闲链接可以保持多久而不被驱逐
+      # 检测链接是否有效的query
+      validation-query: SELECT 1 FROM DUAL
+      test-while-idle: true # 检测到链接空闲时,验证是否有效
+      test-on-borrow: false # 申请链接时,不检测
+      test-on-return: false # 返回链接时,不检测
+      pool-prepared-statements: false # 是否缓存preparedStatement,oracle打开,mysql关闭
+      # 如果上面开启了游标,这里要设置一下大小,例如 50
+      max-pool-prepared-statement-per-connection-size: -1
+      # 统计、监控配置
+      filters: stat,wall # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
+      # 合并执行的相同sql,避免因为参数不同而统计多条sql语句;开启慢sql记录
+      connect-properties: config.stat.mergeSql=true;config.stat.slowSqlMillis=500
+      use-global-data-source-stat: true # 合并多个DruidDataSource的监控数据
+      stat-view-servlet:
+        enabled: true
+        login-username: tianyun
+        login-password: tianyunperfect
+        allow: # 默认运行所有
+        deny: # 默认即可
+        reset-enable: true
+
+
+mybatis:
+  type-aliases-package: com.book.server.entity
+  mapper-locations: classpath:mapper/*Mapper.xml
+
+
+# 设置debug模式下打印mysql
+logging:
+  level:
+    com:
+      book:
+        server:
+          mapper: debug

+ 52 - 0
book-server/src/main/resources/application-test.yml

@@ -0,0 +1,52 @@
+server:
+  port: 9999
+
+spring:
+  datasource:
+    type: com.alibaba.druid.pool.DruidDataSource
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://www.tianyunperfect.cn:3306/test_cps?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
+    username: test_cps
+    password: xKysDECnJLXkMYdJ
+    #下面为连接池补充设置
+    druid:
+      initial-size: 5 # 初始化
+      max-active: 5 # 最大
+      min-idle: 5 # 最小
+      max-wait: 6000 # 超时时间
+      time-between-eviction-runs-millis: 60000 # 每分钟检查一次空闲链接
+      min-evictable-idle-time-millis: 300000 # 空闲链接可以保持多久而不被驱逐
+      # 检测链接是否有效的query
+      validation-query: SELECT 1 FROM DUAL
+      test-while-idle: true # 检测到链接空闲时,验证是否有效
+      test-on-borrow: false # 申请链接时,不检测
+      test-on-return: false # 返回链接时,不检测
+      pool-prepared-statements: false # 是否缓存preparedStatement,oracle打开,mysql关闭
+      # 如果上面开启了游标,这里要设置一下大小,例如 50
+      max-pool-prepared-statement-per-connection-size: -1
+      # 统计、监控配置
+      filters: stat,wall # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
+      # 合并执行的相同sql,避免因为参数不同而统计多条sql语句;开启慢sql记录
+      connect-properties: config.stat.mergeSql=true;config.stat.slowSqlMillis=500
+      use-global-data-source-stat: true # 合并多个DruidDataSource的监控数据
+      stat-view-servlet:
+        enabled: true
+        login-username: tianyun
+        login-password: tianyunperfect
+        allow: # 默认运行所有
+        deny: # 默认即可
+        reset-enable: true
+
+
+mybatis:
+  type-aliases-package: com.book.server.entity
+  mapper-locations: classpath:mapper/*Mapper.xml
+
+
+# 设置debug模式下打印mysql
+logging:
+  level:
+    com:
+      book:
+        server:
+          mapper: debug

+ 3 - 48
book-server/src/main/resources/application.yml

@@ -2,51 +2,6 @@ server:
   port: 9999
 
 spring:
-  datasource:
-    type: com.alibaba.druid.pool.DruidDataSource
-    driver-class-name: com.mysql.cj.jdbc.Driver
-    url: jdbc:mysql://www.tianyunperfect.cn:3306/test_cps?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
-    username: test_cps
-    password: xKysDECnJLXkMYdJ
-    #下面为连接池补充设置
-    druid:
-      initial-size: 5 # 初始化
-      max-active: 5 # 最大
-      min-idle: 5 # 最小
-      max-wait: 6000 # 超时时间
-      time-between-eviction-runs-millis: 60000 # 每分钟检查一次空闲链接
-      min-evictable-idle-time-millis: 300000 # 空闲链接可以保持多久而不被驱逐
-      # 检测链接是否有效的query
-      validation-query: SELECT 1 FROM DUAL
-      test-while-idle: true # 检测到链接空闲时,验证是否有效
-      test-on-borrow: false # 申请链接时,不检测
-      test-on-return: false # 返回链接时,不检测
-      pool-prepared-statements: false # 是否缓存preparedStatement,oracle打开,mysql关闭
-      # 如果上面开启了游标,这里要设置一下大小,例如 50
-      max-pool-prepared-statement-per-connection-size: -1
-      # 统计、监控配置
-      filters: stat,wall # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
-      # 合并执行的相同sql,避免因为参数不同而统计多条sql语句;开启慢sql记录
-      connect-properties: config.stat.mergeSql=true;config.stat.slowSqlMillis=500
-      use-global-data-source-stat: true # 合并多个DruidDataSource的监控数据
-      stat-view-servlet:
-        enabled: true
-        login-username: tianyun
-        login-password: tianyunperfect
-        allow: # 默认运行所有
-        deny: # 默认即可
-        reset-enable: true
-
-
-mybatis:
-  type-aliases-package: com.book.server.entity
-  mapper-locations: classpath:mapper/*Mapper.xml
-
-
-# 设置debug模式下打印mysql
-logging:
-  level:
-    com:
-      book:
-        server:
-          mapper: debug
+  # 环境 dev:开发环境|test:测试环境|prod:生产环境
+  profiles:
+    active: dev #激活的配置文件

+ 400 - 0
book-server/src/test/java/utils/HttpTool.java

@@ -0,0 +1,400 @@
+package utils;
+
+
+import javax.net.ssl.HttpsURLConnection;
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.*;
+
+public class HttpTool {
+    public static final String GET = "GET";
+    public static final String POST = "POST";
+    // 签名key
+    public final static String SIGNATURE = "sign";
+    // 签名方法key
+    public final static String SIGN_METHOD = "sign_type";
+    // =
+    public static final String QSTRING_EQUAL = "=";
+    // &
+    public static final String QSTRING_SPLIT = "&";
+
+    /**
+     * 向指定URL发送GET方法的请求
+     *
+     * @param address 发送请求的URL
+     * @param form    请求参数,请求参数应该是name1=value1&name2=value2的形式。
+     * @return URL所代表远程资源的响应
+     */
+
+    public static String sendGet(final String address, String form) {
+        String req = address + "?" + form;
+        System.out.println("请求:" + req);
+        StringBuilder response = new StringBuilder();
+        HttpURLConnection connection = null;
+        InputStream in = null;
+        try {
+            // 创建一个URL对象并传入地址
+            URL url = new URL(req);
+            // 用地址打开连接通道
+            connection = (HttpURLConnection) url.openConnection();
+            // 设置请求方式为get
+            connection.setRequestMethod("GET");
+            // 设置连接超时为8秒
+            connection.setConnectTimeout(8000);
+            // 设置读取超时为8秒
+            connection.setReadTimeout(8000);
+            connection.setRequestProperty("Charset", "utf-8");
+            // 设置可取
+//			connection.setDoInput(true);
+//			// 设置可读
+//			connection.setDoOutput(true);
+//			// 得到输入流
+            in = connection.getInputStream();
+
+            // 创建高效流对象
+            BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
+            // 创建StringBuilder对象存储数据
+
+            String line;// 一次读取一行
+            while ((line = reader.readLine()) != null) {
+                response.append(line);// 得到的数据存入StringBuilder
+            }
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (connection != null) {// 通道不为null
+                connection.disconnect();// 关闭通道
+            }
+            try {
+                if (in != null) {
+                    in.close();
+                }
+
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        return response.toString();
+    }
+
+    /**
+     * 向指定URL发送GET方法的请求
+     *
+     * @param url   发送请求的URL
+     * @param param 请求参数,请求参数应该是name1=value1&name2=value2的形式。
+     * @return URL所代表远程资源的响应
+     */
+
+    public static String sendGetCookie(String url, String param, String cookie) {
+        String result = "";
+        BufferedReader in = null;
+        try {
+            String urlName = url + "?" + param;
+            System.out.println(urlName);
+            URL realUrl = new URL(urlName);
+            // 打开和URL之间的连接
+            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("Cookie", cookie);
+            // 建立实际的连接
+            conn.connect();
+            // 获取所有响应头字段
+            Map<String, List<String>> map = conn.getHeaderFields();
+            // 遍历所有的响应头字段
+            // for (String key : map.keySet()) {
+            // System.out.println(key + "--->" + map.get(key));
+            // }
+            // 定义BufferedReader输入流来读取URL的响应
+            in = new BufferedReader(
+                    new InputStreamReader(conn.getInputStream()));
+            String line;
+            while ((line = in.readLine()) != null) {
+                result += line;
+                // result += "/n" + line;
+            }
+        } catch (Exception e) {
+            System.out.println("发送GET请求出现异常!" + e);
+            e.printStackTrace();
+        }
+        // 使用finally块来关闭输入流
+        finally {
+            try {
+                if (in != null) {
+                    in.close();
+                }
+            } catch (IOException ex) {
+                ex.printStackTrace();
+            }
+        }
+        return result;
+    }
+
+    /**
+     * 向指定URL发送POST方法的请求
+     *
+     * @param url   发送请求的URL
+     * @param param 请求参数,请求参数应该是name1=value1&name2=value2的形式。
+     * @return URL所代表远程资源的响应
+     */
+    public static String sendPost(String url, String param) {
+        PrintWriter out = null;
+        BufferedReader in = null;
+        String result = "";
+        try {
+            URL realUrl = new URL(url);
+            // 打开和URL之间的连接
+            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)");
+            // 发送POST请求必须设置如下两行
+            conn.setDoOutput(true);
+            conn.setDoInput(true);
+            // 获取URLConnection对象对应的输出流
+            out = new PrintWriter(conn.getOutputStream());
+            // 发送请求参数
+            out.print(param);
+            // flush输出流的缓冲
+            out.flush();
+            // 定义BufferedReader输入流来读取URL的响应
+            in = new BufferedReader(
+                    new InputStreamReader(conn.getInputStream()));
+            String line;
+            while ((line = in.readLine()) != null) {
+                result += line;
+                // result += "/n" + line;
+            }
+        } catch (Exception e) {
+            System.out.println("发送POST请求出现异常!" + e);
+            e.printStackTrace();
+        }
+        // 使用finally块来关闭输出流、输入流
+        finally {
+            try {
+                if (out != null) {
+                    out.close();
+                }
+                if (in != null) {
+                    in.close();
+                }
+            } catch (IOException ex) {
+                ex.printStackTrace();
+            }
+        }
+        return result;
+    }
+
+    /**
+     * 向指定URL发送POST方法的请求
+     *
+     * @param url   发送请求的URL
+     * @param param 请求参数,请求参数应该是name1=value1&name2=value2的形式。
+     * @return URL所代表远程资源的响应
+     */
+    public static String sendPostCookie(String url, String param, String cookie) {
+        PrintWriter out = null;
+        BufferedReader in = null;
+        String result = "";
+        try {
+            URL realUrl = new URL(url);
+            // 打开和URL之间的连接
+            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("Cookie", cookie);
+            // 发送POST请求必须设置如下两行
+            conn.setDoOutput(true);
+            conn.setDoInput(true);
+            // 获取URLConnection对象对应的输出流
+            out = new PrintWriter(conn.getOutputStream());
+            // 发送请求参数
+            out.print(param);
+            // flush输出流的缓冲
+            out.flush();
+            // 定义BufferedReader输入流来读取URL的响应
+            in = new BufferedReader(
+                    new InputStreamReader(conn.getInputStream()));
+            String line;
+            while ((line = in.readLine()) != null) {
+                result += line;
+                // result += "/n" + line;
+            }
+        } catch (Exception e) {
+            System.out.println("发送POST请求出现异常!" + e);
+            e.printStackTrace();
+        }
+        // 使用finally块来关闭输出流、输入流
+        finally {
+            try {
+                if (out != null) {
+                    out.close();
+                }
+                if (in != null) {
+                    in.close();
+                }
+            } catch (IOException ex) {
+                ex.printStackTrace();
+            }
+        }
+        return result;
+    }
+
+    /**
+     * https访问
+     */
+    public static String doHttps(String httpurl, String method, String param) {
+        try {
+            URL reqURL = new URL(httpurl + param); // 创建URL对象
+            HttpsURLConnection httpsConn = (HttpsURLConnection) reqURL
+                    .openConnection();
+
+            /*
+             * 下面这段代码实现向Web页面发送数据,实现与网页的交互访问 httpsConn.setDoOutput(true);
+             * OutputStreamWriter out = new
+             * OutputStreamWriter(huc.getOutputStream(), "8859_1"); out.write(
+             * "……" ); out.flush(); out.close();
+             */
+
+            // 取得该连接的输入流,以读取响应内容
+            InputStreamReader insr = new InputStreamReader(
+                    httpsConn.getInputStream());
+
+            // 读取服务器的响应内容并显示
+            int respInt = insr.read();
+            while (respInt != -1) {
+                System.out.print((char) respInt);
+                respInt = insr.read();
+            }
+        } catch (Exception e) {
+            // TODO: handle exception
+        }
+
+        return "";
+    }
+
+
+    /**
+     * 除去请求要素中的空值和签名参数
+     *
+     * @param para 请求要素
+     * @return 去掉空值与签名参数后的请求要素
+     */
+    public static Map<String, String> paraFilter(Map<String, String> para) {
+
+        Map<String, String> result = new HashMap<String, String>();
+
+        if (para == null || para.size() <= 0) {
+            return result;
+        }
+
+        for (String key : para.keySet()) {
+            String value = para.get(key);
+            if (value == null || value.equals("")
+                    || key.equalsIgnoreCase(SIGNATURE)
+                    || key.equalsIgnoreCase(SIGN_METHOD)) {
+                continue;
+            }
+            result.put(key, value);
+        }
+
+        return result;
+    }
+
+    /**
+     * 把请求要素按照“参数=参数值”的模式用“&”字符拼接成字符串
+     *
+     * @param para   请求要素
+     * @param sort   是否需要根据key值作升序排列
+     * @param encode 是否需要URL编码
+     * @return 拼接成的字符串
+     */
+    public static String createLinkString(Map<String, String> para,
+                                          boolean sort, boolean encode) {
+
+        List<String> keys = new ArrayList<String>(para.keySet());
+
+        if (sort)
+            Collections.sort(keys);
+
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < keys.size(); i++) {
+            String key = keys.get(i);
+            String value = para.get(key);
+
+            if (encode) {
+                try {
+                    value = URLEncoder.encode(value, "utf-8");
+                } catch (UnsupportedEncodingException e) {
+                }
+            }
+
+            if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符
+                sb.append(key).append(QSTRING_EQUAL).append(value);
+            } else {
+                sb.append(key).append(QSTRING_EQUAL).append(value)
+                        .append(QSTRING_SPLIT);
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * 对传入的参数进行MD5摘要
+     *
+     * @param str 需进行MD5摘要的数据
+     * @return MD5摘要值
+     */
+    public static String md5Summary(String str) {
+
+        if (str == null) {
+            return null;
+        }
+
+        MessageDigest messageDigest = null;
+
+        try {
+            messageDigest = MessageDigest.getInstance("MD5");
+            messageDigest.reset();
+            messageDigest.update(str.getBytes(StandardCharsets.UTF_8));
+        } catch (NoSuchAlgorithmException e) {
+
+            return str;
+        }
+
+        byte[] byteArray = messageDigest.digest();
+
+        StringBuffer md5StrBuff = new StringBuffer();
+
+        for (int i = 0; i < byteArray.length; i++) {
+            if (Integer.toHexString(0xFF & byteArray[i]).length() == 1)
+                md5StrBuff.append("0").append(
+                        Integer.toHexString(0xFF & byteArray[i]));
+            else
+                md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i]));
+        }
+
+        return md5StrBuff.toString();
+    }
+
+
+    public static void main(String[] args) {
+        String result = sendGet("http://localhost:8080/agentPay", "account_type=01&amount=ds&appid=4110&banck_name=dsf&card_number=fsds&customer_name=sdfsdf&customer_type=01&mchorder_id=1505280293071&pay_password=123&sign=cf587510835e4f2d2fbdf43b094a13e4");
+        System.out.println(result);
+    }
+}