Browse Source

add(backend) [粉丝同步]

wangxiao 4 years ago
parent
commit
d41667f0a7

+ 0 - 8
operation-backend/pom.xml

@@ -51,14 +51,6 @@
         <!-- swagger2  start -->
 
 
-        <!--kaptcha start-->
-        <dependency>
-            <groupId>com.baomidou</groupId>
-            <artifactId>kaptcha-spring-boot-starter</artifactId>
-            <version>1.1.0</version>
-        </dependency>
-        <!--kaptcha end-->
-
         <!--JWT start-->
         <dependency>
             <groupId>com.auth0</groupId>

+ 36 - 0
operation-backend/src/main/java/com/idiot/operationbackend/config/AsyncPoolConfig.java

@@ -0,0 +1,36 @@
+package com.idiot.operationbackend.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadPoolExecutor;
+
+
+/**
+ * 异步线程 但是丢弃任务会使得用户信息同步失败
+ * @author wang xiao
+ * @date Created in 18:23 2020/9/14
+ */
+@EnableAsync
+@Configuration
+public class AsyncPoolConfig {
+
+
+    @Bean(name = "asyncExecutor")
+    public Executor  asyncExecutor () {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setCorePoolSize(5);
+        executor.setMaxPoolSize(10);
+        executor.setQueueCapacity(500);
+        executor.setKeepAliveSeconds(60);
+        executor.setThreadNamePrefix("asyncExecutor-");
+        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+        executor.setWaitForTasksToCompleteOnShutdown(true);
+        executor.setAwaitTerminationSeconds(60);
+        executor.initialize();
+        return executor;
+    }
+}

+ 5 - 8
operation-backend/src/main/java/com/idiot/operationbackend/controller/AccountController.java

@@ -2,8 +2,10 @@ package com.idiot.operationbackend.controller;
 
 import com.idiot.operationbackend.entity.Account;
 import com.idiot.operationbackend.entity.AuthUser;
+import com.idiot.operationbackend.handler.SyncUserTask;
 import com.idiot.operationbackend.service.facade.AccountService;
 import com.idiot.operationbackend.service.facade.AuthUserService;
+import com.idiot.operationbackend.service.facade.WeChatService;
 import com.idiot.operationbackend.support.JsonResult;
 import com.idiot.operationbackend.util.JwtTokenUtil;
 import io.swagger.annotations.Api;
@@ -36,6 +38,9 @@ public class AccountController {
     @Autowired
     private AuthUserService userService;
 
+    @Autowired
+    private WeChatService weChatService;
+
 
 
     @GetMapping
@@ -57,14 +62,6 @@ public class AccountController {
     }
 
 
-    @GetMapping("confirmAccount")
-    @ApiOperation(value = "确认授权的微信公众号")
-    public ResponseEntity<JsonResult<Boolean>> confirmAccount (@RequestHeader String token,
-                                                               @RequestParam String accountId) {
-        String userId = JwtTokenUtil.getUserId(token);
-        boolean ifUpdate = accountService.updateUserIdByAccount(accountId,userId);
-        return ResponseEntity.ok(JsonResult.success(ifUpdate));
-    }
 
     @GetMapping("/{id}")
     @ApiOperation(value = "根据id查询微信公众号")

+ 0 - 2
operation-backend/src/main/java/com/idiot/operationbackend/controller/AuthController.java

@@ -44,8 +44,6 @@ public class AuthController  {
     @ApiOperation(value = "账号登录")
     public ResponseEntity<JsonResult<AuthUser>> login(@RequestBody @Valid AuthUser authUser) {
         logger.info("用户:{}账号密码登录",authUser.getUserCode());
-
-
         authUser =userService.queryByUserCodeAndPassword(authUser.getPassword(),authUser.getUserCode());
         if (Objects.isNull(authUser)) {
             throw new CustomException(500,"账号或者密码错误!请检查大小写");

+ 21 - 1
operation-backend/src/main/java/com/idiot/operationbackend/controller/WeChatController.java

@@ -7,6 +7,7 @@ import com.idiot.operationbackend.service.facade.WeChatService;
 import com.idiot.operationbackend.support.AccountState;
 import com.idiot.operationbackend.support.Constants;
 import com.idiot.operationbackend.support.JsonResult;
+import com.idiot.operationbackend.util.JwtTokenUtil;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import org.slf4j.Logger;
@@ -118,14 +119,33 @@ public class WeChatController {
             boolean ifResult = weChatService.saveOrUpdateWechatAcc(account);
             weChatService.cacheAuthorizerAccessToken(account.getId(),authorizerAccessToken);
             // 重定向到 用户确认中介页面
-
             response.sendRedirect(confirmDomain+"?accountId="+account.getId());
         }catch (Exception e) {
             response.sendRedirect(confirmDomain+"?accountId=");
             logger.error("微信授权回调 ------->失败,authCode is :{},expiresIn:{},error message is :{}",authCode,expiresIn,e.getMessage());
         }
+    }
+
 
+    @GetMapping("confirmAccount")
+    @ApiOperation(value = "确认授权的微信公众号")
+    public ResponseEntity<JsonResult<Boolean>> confirmAccount (@RequestHeader String token,
+                                                               @RequestParam String accountId) {
+
+        String userId = JwtTokenUtil.getUserId(token);
+        logger.error("微信公众号:{}授权,用户:{}确认 ,",accountId,userId);
+        boolean ifUpdate = weChatService.confirmAccount(accountId,userId);
+        if (ifUpdate){
+            logger.error("微信公众号:{}授权,用户:{} 成功确认 ,开始同步用户标签数据",accountId,userId);
+            weChatService.syncAccountUser(accountId);
+            weChatService.syncTag(accountId);
+        }
+
+        return ResponseEntity.ok(JsonResult.success(ifUpdate));
     }
 
 
+
+
+
 }

+ 41 - 0
operation-backend/src/main/java/com/idiot/operationbackend/entity/AccountFans.java

@@ -24,6 +24,8 @@ public class AccountFans {
 
     private String city;
 
+    private String province;
+
     private String country;
 
     private String headImgUrl;
@@ -38,6 +40,12 @@ public class AccountFans {
 
     private String tagIdList;
 
+    private String remark;
+
+    private String unionId;
+
+    private Integer groupId;
+
     private Long lastInactiveTime;
 
     private String updateTime;
@@ -91,6 +99,14 @@ public class AccountFans {
         this.city = city;
     }
 
+    public String getProvince() {
+        return province;
+    }
+
+    public void setProvince(String province) {
+        this.province = province;
+    }
+
     public String getCountry() {
         return country;
     }
@@ -162,4 +178,29 @@ public class AccountFans {
     public void setUpdateTime(String updateTime) {
         this.updateTime = updateTime;
     }
+
+
+    public String getRemark() {
+        return remark;
+    }
+
+    public void setRemark(String remark) {
+        this.remark = remark;
+    }
+
+    public String getUnionId() {
+        return unionId;
+    }
+
+    public void setUnionId(String unionId) {
+        this.unionId = unionId;
+    }
+
+    public Integer getGroupId() {
+        return groupId;
+    }
+
+    public void setGroupId(Integer groupId) {
+        this.groupId = groupId;
+    }
 }

+ 33 - 0
operation-backend/src/main/java/com/idiot/operationbackend/handler/SyncUserTask.java

@@ -0,0 +1,33 @@
+package com.idiot.operationbackend.handler;
+
+import com.idiot.operationbackend.service.facade.WeChatService;
+
+import java.util.List;
+
+/**
+ * @author wang xiao
+ * @date Created in 18:41 2020/9/14
+ */
+public class SyncUserTask {
+
+    private String accountId;
+
+    private  List<String> openIds;
+
+    private WeChatService weChatService;
+
+
+
+    /**
+     * 卸载这里原因是 jdk 代理 异步方法不生效
+     */
+    public void  doSyncUserTask () {
+        weChatService.syncUserTask(accountId,openIds);
+    }
+
+    public SyncUserTask(String accountId, List<String> openIds, WeChatService weChatService) {
+        this.accountId = accountId;
+        this.openIds = openIds;
+        this.weChatService = weChatService;
+    }
+}

+ 11 - 0
operation-backend/src/main/java/com/idiot/operationbackend/service/facade/AccountFansService.java

@@ -8,4 +8,15 @@ import com.idiot.operationbackend.entity.AccountFans;
  * @date Created in 19:24 2020/9/10
  */
 public interface AccountFansService extends IService<AccountFans> {
+
+
+    /**
+     *  openId和公众号id  查询用户
+     * @author wangxiao
+     * @date 18:59 2020/9/14
+     * @param accountId
+     * @param openId
+     * @return com.idiot.operationbackend.entity.AccountFans
+     */
+    AccountFans queryByAccountIdAndOpenId (String accountId,String openId);
 }

+ 46 - 0
operation-backend/src/main/java/com/idiot/operationbackend/service/facade/WeChatService.java

@@ -151,6 +151,18 @@ public interface WeChatService {
   void cacheAuthorizerAccessToken(String accountId,String authorizerAccessToken);
 
 
+  /**
+   *  同步用户数据
+   * @author wangxiao
+   * @date 18:51 2020/9/14
+   * @param accountId
+   * @param openIds
+   * @return void
+   */
+  void syncUserTask(String accountId,List<String> openIds);
+
+
+
   /**
    *  同步用户
    * @author wangxiao
@@ -160,6 +172,40 @@ public interface WeChatService {
    */
   int syncAccountUser(String accountId);
 
+  /**
+   *  同步标签信息
+   * @author wangxiao
+   * @date 20:16 2020/9/14
+   * @param accountId
+   * @return void
+   */
+  void syncTag(String accountId);
+
+
+  /**
+   * 获取粉丝信息
+   * @author wangxiao
+   * @date 19:09 2020/9/14
+   * @param accountId
+   * @param openId
+   * @return java.lang.String
+   */
+  String getFansInfo(String accountId,String openId);
+
+
+  /**
+   *  微信授权后确认公众号
+   * @author wangxiao
+   * @date 20:12 2020/9/14
+   * @param accountId
+   * @param userId
+   * @return boolean
+   */
+  boolean confirmAccount(String accountId,String userId);
+
+
+
+
 
   /**
    *  微信消息转换成map(非加密)

+ 8 - 0
operation-backend/src/main/java/com/idiot/operationbackend/service/impl/AccountFansServiceImpl.java

@@ -1,5 +1,6 @@
 package com.idiot.operationbackend.service.impl;
 
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.idiot.operationbackend.entity.AccountFans;
 import com.idiot.operationbackend.mappers.AccountFansMapper;
@@ -13,4 +14,11 @@ import org.springframework.stereotype.Service;
 @Service
 public class AccountFansServiceImpl extends ServiceImpl<AccountFansMapper, AccountFans>
         implements AccountFansService {
+
+
+    @Override
+    public AccountFans queryByAccountIdAndOpenId(String accountId, String openId) {
+        return getOne(Wrappers.<AccountFans>lambdaQuery().eq(AccountFans::getAccountId,accountId)
+                .eq(AccountFans::getOpenId,openId),false);
+    }
 }

+ 84 - 1
operation-backend/src/main/java/com/idiot/operationbackend/service/impl/WeChatServiceImpl.java

@@ -1,9 +1,15 @@
 package com.idiot.operationbackend.service.impl;
 
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.google.common.cache.*;
 import com.idiot.operationbackend.config.PlatformProperties;
 import com.idiot.operationbackend.entity.Account;
+import com.idiot.operationbackend.entity.AccountFans;
+import com.idiot.operationbackend.entity.AccountTag;
+import com.idiot.operationbackend.handler.SyncUserTask;
+import com.idiot.operationbackend.service.facade.AccountFansService;
 import com.idiot.operationbackend.service.facade.AccountService;
 import com.idiot.operationbackend.service.facade.WeChatService;
 import com.idiot.operationbackend.support.Constants;
@@ -15,6 +21,7 @@ import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.*;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 import org.springframework.util.LinkedMultiValueMap;
 import org.springframework.util.MultiValueMap;
@@ -23,6 +30,8 @@ import org.springframework.web.client.RestTemplate;
 
 import javax.annotation.Resource;
 import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.TimeUnit;
@@ -59,6 +68,9 @@ public class WeChatServiceImpl implements WeChatService, InitializingBean {
     @Autowired
     private AccountService accountService;
 
+    @Autowired
+    private AccountFansService fansService;
+
 
     @Override
     public String notice(Map<String, String> param) {
@@ -260,7 +272,7 @@ public class WeChatServiceImpl implements WeChatService, InitializingBean {
             throw new CustomException(500,"当前公众号正在后台同步粉丝数据,请您稍等一会!");
         }
         cache.put(lockKey,lockValue);
-        logger.info("公众号:{}同步粉丝数据,时间:{}",accountId, LocalDateTime.now().toString());
+        logger.info("公众号:{}同步粉丝数据---- start,时间:{}",accountId, LocalDateTime.now().toString());
         String requestUrl = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=%s&next_openid=%s";
         String nextOpenId = "";
         String accessToken = getAuthorizerAccessToken(accountId);
@@ -282,10 +294,81 @@ public class WeChatServiceImpl implements WeChatService, InitializingBean {
             total = respJsonBody.getInteger("total");
             count += respJsonBody.getInteger("count");
             nextOpenId = respJsonBody.getString("next_openid");
+            JSONArray openIdArray = respJsonBody.getJSONObject("data").getJSONArray("openid");
+            List<String> openIds = JSONArray.parseArray(openIdArray.toJSONString(),String.class);
+            new SyncUserTask(accountId,openIds,this);
         }while (count < total);
         cache.invalidate(lockKey);
+        logger.info("公众号:{}同步粉丝数据----end,时间:{}",accountId, LocalDateTime.now().toString());
         return count;
     }
 
 
+    @Override
+    @Async("asyncExecutor")
+    public void syncUserTask(String accountId, List<String> openIds) {
+
+        List<AccountFans> accountFans =  new ArrayList<>(openIds.size());
+
+        for (String openId : openIds) {
+            AccountFans fans = fansService.queryByAccountIdAndOpenId(accountId,openId);
+            if (fans == null) {
+                fans = new AccountFans();
+            }
+            JSONObject fansObject = JSON.parseObject(getFansInfo(accountId,openId));
+            fans.setAccountId(accountId);
+            fans.setOpenId(openId);
+            fans.setNickName(fansObject.getString("nickname"));
+            fans.setHeadImgUrl(fansObject.getString("headimgurl"));
+            fans.setSex(fansObject.getInteger("sex"));
+            fans.setSubscribe(fansObject.getInteger("subscribe"));
+            fans.setCity(fansObject.getString("city"));
+            fans.setProvince(fansObject.getString("province"));
+            fans.setSubscribeTime(fansObject.getLong("subscribe_time"));
+            fans.setSubscribeScene(fansObject.getString("subscribe_scene"));
+            fans.setUnionId(fansObject.getString("unionid"));
+            fans.setRemark(fansObject.getString("remark"));
+            fans.setGroupId(fansObject.getInteger("groupid"));
+            fans.setTagIdList(JSONObject.toJSONString(fansObject.getJSONObject("tagid_list")));
+            accountFans.add(fans);
+        }
+        //  保存用户
+        fansService.saveOrUpdateBatch(accountFans,1000);
+    }
+
+
+    @Override
+    @Async("asyncExecutor")
+    public void syncTag(String accountId) {
+        String requestUrl = "https://api.weixin.qq.com/cgi-bin/tags/get?access_token=%s";
+        String accessToken =  getAuthorizerAccessToken(accountId);
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_JSON);
+        requestUrl = String.format(requestUrl,accessToken);
+        HttpEntity<MultiValueMap<String, String>>  entity = new HttpEntity<> (headers);
+        ResponseEntity<String> respStr = restTemplate.exchange(requestUrl,HttpMethod.GET,entity,String.class);
+        List<AccountTag> accountTags = JSONObject.parseArray(respStr.getBody(), AccountTag.class);
+
+    }
+
+    @Override
+    public String getFansInfo(String accountId, String openId) {
+        logger.info("查询粉丝信息,opendId:{}----start,时间:{}",accountId, LocalDateTime.now().toString());
+        String requestUrl = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=%s&openid=OPENID&lang=zh_CN";
+        String accessToken =  getAuthorizerAccessToken(accountId);
+        requestUrl = String.format(requestUrl,accessToken);
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_JSON);
+        HttpEntity<MultiValueMap<String, String>>  entity = new HttpEntity<> (headers);
+        ResponseEntity<String> respStr = restTemplate.exchange(requestUrl,HttpMethod.GET,entity,String.class);
+        String jsonStr = respStr.getBody();
+        logger.info("查询粉丝信息,openId:{}----end,时间:{},微信返回:{}",accountId, LocalDateTime.now().toString(),jsonStr);
+        return jsonStr;
+    }
+
+
+    @Override
+    public boolean confirmAccount(String accountId, String userId) {
+        return accountService.updateUserIdByAccount(accountId,userId);
+    }
 }

+ 4 - 2
operation-frontend/package.json

@@ -14,8 +14,9 @@
     "@ant-design-vue/pro-layout": "^0.3.12",
     "@antv/data-set": "^0.10.2",
     "ant-design-vue": "^1.6.2",
-    "axios": "^0.19.0",
+    "axios": "^0.19.2",
     "core-js": "^3.1.2",
+    "cors": "^2.8.5",
     "enquire.js": "^2.1.6",
     "lodash.clonedeep": "^4.5.0",
     "lodash.get": "^4.4.2",
@@ -35,7 +36,8 @@
     "vue-router": "^3.1.2",
     "vue-svg-component-runtime": "^1.0.1",
     "vuex": "^3.1.1",
-    "wangeditor": "^3.1.1"
+    "wangeditor": "^3.1.1",
+    "yarn": "^1.22.5"
   },
   "devDependencies": {
     "@ant-design/colors": "^3.2.1",

+ 0 - 2
operation-frontend/src/main.js

@@ -22,8 +22,6 @@ import './utils/filter' // global filter
 import './global.less'
 Vue.use(SlideVerify)
 
-Vue.config.productionTip = false
-
 // mount axios to `Vue.$http` and `this.$http`
 Vue.use(VueAxios)
 Vue.component('pro-layout', ProLayout)

+ 5 - 1
sql/dataBase.sql

@@ -55,14 +55,18 @@ CREATE TABLE `t_account_fans`  (
   `nick_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT 'nickname',
   `sex` tinyint(1) NULL DEFAULT NULL COMMENT '用户的性别,值为1时是男性,值为2时是女性,值为0时是未知',
   `city` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '城市',
+  `province` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '省份',
   `country` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '国家',
   `head_img_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT 'headimgurl',
+  `union_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
   `subscribe` tinyint(1) NULL DEFAULT NULL COMMENT '1是关注',
   `subscribe_time` bigint(0) NULL DEFAULT NULL COMMENT '关注时间',
   `subscribe_scene` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '关注的渠道来源',
-  `subscribe_scene_label` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL  COMMENT '关注的渠道来源zh',
+  `subscribe_scene_label` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '关注的渠道来源zh',
   `tag_id_list` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '标签id集合tagid_list',
   `last_inactive_time` bigint(0) NULL DEFAULT NULL COMMENT '最后互动时间',
+  `group_id` int(0) NULL DEFAULT NULL COMMENT '分组',
+  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '备注',
   `update_time` datetime(0) NULL DEFAULT NULL,
   PRIMARY KEY (`id`) USING BTREE
 ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '公众号粉丝' ROW_FORMAT = Dynamic;