فهرست منبع

新增定时同步wzh用户信息至数据库

husj 1 ماه پیش
والد
کامیت
b90ac0bd3f

+ 11 - 4
pom.xml

@@ -53,14 +53,11 @@
             <artifactId>jackson-dataformat-xml</artifactId>
             <version>2.15.3</version>
         </dependency>
-        <dependency>
-            <groupId>com.fasterxml.jackson.dataformat</groupId>
-            <artifactId>jackson-dataformat-xml</artifactId>
-        </dependency>
         <!-- Apache Commons 工具类 -->
         <dependency>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-lang3</artifactId>
+            <version>3.18.0</version>
         </dependency>
 
         <dependency>
@@ -68,6 +65,16 @@
             <artifactId>commons-codec</artifactId>
             <version>1.17.1</version>
         </dependency>
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
+            <version>3.5.5</version>
+        </dependency>
+        <dependency>
+            <groupId>com.mysql</groupId>
+            <artifactId>mysql-connector-j</artifactId>
+            <version>8.3.0</version>
+        </dependency>
     </dependencies>
 
     <build>

+ 3 - 52
src/main/java/top/husj/husj_wx/config/AccessTokenConfig.java

@@ -10,6 +10,7 @@ import org.springframework.context.annotation.Configuration;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.web.client.RestTemplate;
 import top.husj.husj_wx.properties.WeChatProperties;
+import top.husj.husj_wx.service.AccessTokenService;
 import top.husj.husj_wx.utils.AccessTokenUtil;
 import top.husj.husj_wx.utils.EmailUtil;
 
@@ -17,61 +18,11 @@ import top.husj.husj_wx.utils.EmailUtil;
 @Slf4j
 public class AccessTokenConfig {
     @Autowired
-    private RestTemplate restTemplate;
-    @Autowired
-    private WeChatProperties weChatProperties;
+    private AccessTokenService accessTokenService;
 
     @PostConstruct
     public void initAccessToken() throws MessagingException {
         log.info("应用启动,正在获取初始access_token...");
-        refreshAccessToken();
-    }
-
-    /**
-     * 每小时自动刷新access_token
-     * fixedRate = 3600000 表示每3600000毫秒(1小时)执行一次
-     */
-    @Scheduled(fixedRate = 3600000)
-    public void scheduledRefreshAccessToken() {
-        log.info("定时任务触发,正在刷新access_token...");
-        try {
-            refreshAccessToken();
-        } catch (MessagingException e) {
-            log.error("定时刷新access_token失败: ", e);
-        }
-    }
-
-    /**
-     * 刷新access_token的核心逻辑
-     */
-    private void refreshAccessToken() throws MessagingException {
-        String appId = weChatProperties.getAppid();
-        String appSecret = weChatProperties.getAppsecret();
-        try {
-            String url = String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s",
-                    appId, appSecret);
-
-            String response = restTemplate.getForObject(url, String.class);
-            JSONObject jsonResponse = JSONObject.parseObject(response);
-
-            if (jsonResponse.containsKey("access_token")) {
-                String accessToken = jsonResponse.getString("access_token");
-                AccessTokenUtil.setAccessToken(accessToken);
-                log.info("成功获取access_token: {}", accessToken);
-            } else {
-                String errorMsg = jsonResponse.getString("errmsg");
-                log.error("获取access_token失败: {}", errorMsg);
-                throw new RuntimeException("获取access_token失败: " + errorMsg);
-            }
-
-        } catch (Exception e) {
-            log.error("获取access_token时发生异常: ", e);
-            String host = "smtp.030208.xyz";
-            String port = "465";
-            String username = "husj@030208.xyz";
-            String password = "15629747218hsjH";
-            EmailUtil emailUtil = new EmailUtil(host, port, username, password);
-            emailUtil.sendTextEmail("807244836@qq.com", "微信公众号token获取失败请检查", "微信公众号token获取失败请检查");
-        }
+        accessTokenService.refreshAccessToken();
     }
 }

+ 9 - 0
src/main/java/top/husj/husj_wx/dao/UserMapper.java

@@ -0,0 +1,9 @@
+package top.husj.husj_wx.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import top.husj.husj_wx.entity.model.User;
+
+@Mapper
+public interface UserMapper extends BaseMapper<User> {
+}

+ 36 - 0
src/main/java/top/husj/husj_wx/entity/dto/UserListDto.java

@@ -0,0 +1,36 @@
+package top.husj.husj_wx.entity.dto;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class UserListDto {
+    /**
+     * 关注该公众账号的总用户数
+     */
+    private Integer total;
+
+    /**
+     * 拉取的OPENID个数,最大值为10000
+     */
+    private Integer count;
+
+    /**
+     * 列表数据,OPENID的列表
+     */
+    private UserListData data;
+
+    /**
+     * 拉取列表的最后一个用户的OPENID
+     */
+    private String next_openid;
+
+    @Data
+    public static class UserListData {
+        /**
+         * OPENID列表
+         */
+        private List<String> openid;
+    }
+}

+ 21 - 0
src/main/java/top/husj/husj_wx/entity/model/User.java

@@ -0,0 +1,21 @@
+package top.husj.husj_wx.entity.model;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.Data;
+
+@Data
+public class User {
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @TableField("open_id")
+    private String openId;
+
+    @TableField("name")
+    private String name;
+
+    @TableField("remark")
+    private String remark;
+}

+ 28 - 0
src/main/java/top/husj/husj_wx/schedule/AccessTokenSchedule.java

@@ -0,0 +1,28 @@
+package top.husj.husj_wx.schedule;
+
+import jakarta.mail.MessagingException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+import top.husj.husj_wx.service.AccessTokenService;
+
+@Component
+@Slf4j
+public class AccessTokenSchedule {
+    @Autowired
+    private AccessTokenService accessTokenService;
+
+    /**
+     * 每小时自动刷新access_token
+     */
+    @Scheduled(cron = "0 0 * * * ?")
+    public void scheduledRefreshAccessToken() {
+        log.info("定时任务触发,正在刷新access_token...");
+        try {
+            accessTokenService.refreshAccessToken();
+        } catch (MessagingException e) {
+            log.error("定时刷新access_token失败: ", e);
+        }
+    }
+}

+ 49 - 0
src/main/java/top/husj/husj_wx/schedule/UserSyncSchedule.java

@@ -0,0 +1,49 @@
+package top.husj.husj_wx.schedule;
+
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
+import top.husj.husj_wx.entity.dto.UserListDto;
+import top.husj.husj_wx.properties.WeChatProperties;
+import top.husj.husj_wx.service.UserService;
+import top.husj.husj_wx.utils.AccessTokenUtil;
+import top.husj.husj_wx.utils.EmailUtil;
+
+@Component
+@Slf4j
+public class UserSyncSchedule {
+    @Autowired
+    private RestTemplate restTemplate;
+    @Autowired
+    private UserService userService;
+    @Scheduled(cron = "0 * * * * ?")
+    public void syncUser() {
+        Long count = 0L;
+        try {
+            log.info("开始同步用户信息...");
+            while (true) {
+                String url = String.format("https://api.weixin.qq.com/cgi-bin/user/get?access_token=%s",
+                        AccessTokenUtil.getAccessToken());
+
+                String response = restTemplate.getForObject(url, String.class);
+                UserListDto userListData = JSONObject.parseObject(response, UserListDto.class);
+                userService.saveByOpenIds(userListData.getData().getOpenid());
+                count += userListData.getCount();
+                if (userListData.getTotal() > count) {
+                    url = String.format("https://api.weixin.qq.com/cgi-bin/user/get?access_token=%s&next_openid=%s",
+                            AccessTokenUtil.getAccessToken(), userListData.getNext_openid());
+                    response = restTemplate.getForObject(url, String.class);
+                    userListData = JSONObject.parseObject(response, UserListDto.class);
+                    userService.saveByOpenIds(userListData.getData().getOpenid());
+                } else
+                    break;
+            }
+            log.info("同步用户信息完成,同步了{}个用户", count);
+        } catch (Exception e) {
+            log.error("同步用户信息时发生异常: ", e);
+        }
+    }
+}

+ 55 - 0
src/main/java/top/husj/husj_wx/service/AccessTokenService.java

@@ -0,0 +1,55 @@
+package top.husj.husj_wx.service;
+
+import com.alibaba.fastjson.JSONObject;
+import jakarta.mail.MessagingException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+import top.husj.husj_wx.properties.WeChatProperties;
+import top.husj.husj_wx.utils.AccessTokenUtil;
+import top.husj.husj_wx.utils.EmailUtil;
+
+@Service
+@Slf4j
+public class AccessTokenService {
+    @Autowired
+    private RestTemplate restTemplate;
+    @Autowired
+    private WeChatProperties weChatProperties;
+
+    /**
+     * 刷新access_token的核心逻辑
+     */
+    public void refreshAccessToken() throws MessagingException {
+        String appId = weChatProperties.getAppid();
+        String appSecret = weChatProperties.getAppsecret();
+        try {
+            String url = String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s",
+                    appId, appSecret);
+
+            String response = restTemplate.getForObject(url, String.class);
+            JSONObject jsonResponse = JSONObject.parseObject(response);
+
+            if (jsonResponse.containsKey("access_token")) {
+                String accessToken = jsonResponse.getString("access_token");
+                AccessTokenUtil.setAccessToken(accessToken);
+                log.info("成功获取access_token: {}", accessToken);
+            } else {
+                String errorMsg = jsonResponse.getString("errmsg");
+                log.error("获取access_token失败: {}", errorMsg);
+                throw new RuntimeException("获取access_token失败: " + errorMsg);
+            }
+
+        } catch (Exception e) {
+            log.error("获取access_token时发生异常: ", e);
+            String host = "smtp.030208.xyz";
+            String port = "465";
+            String username = "husj@030208.xyz";
+            String password = "15629747218hsjH";
+            EmailUtil emailUtil = new EmailUtil(host, port, username, password);
+            emailUtil.sendTextEmail("807244836@qq.com", "微信公众号token获取失败请检查", "微信公众号token获取失败请检查");
+        }
+    }
+
+}

+ 19 - 0
src/main/java/top/husj/husj_wx/service/UserService.java

@@ -0,0 +1,19 @@
+package top.husj.husj_wx.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import top.husj.husj_wx.entity.model.User;
+
+import java.util.List;
+
+public interface UserService extends IService<User> {
+    List<User> listByOpenIds(List<String> openIds);
+    void saveByOpenIds(List<String> openIds);
+
+    /**
+     *
+     * @param openId wx接口id
+     * @param searchDb 是否优先从数据库中查询
+     * @return
+     */
+    User getUserByOpenId(String openId, Boolean searchDb);
+}

+ 76 - 0
src/main/java/top/husj/husj_wx/service/impl/UserServiceImpl.java

@@ -0,0 +1,76 @@
+package top.husj.husj_wx.service.impl;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import jakarta.annotation.PostConstruct;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.catalina.core.ApplicationContext;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+import top.husj.husj_wx.dao.UserMapper;
+import top.husj.husj_wx.entity.model.User;
+import top.husj.husj_wx.service.UserService;
+import top.husj.husj_wx.utils.AccessTokenUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Service
+@Slf4j
+public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService{
+    @Autowired
+    private RestTemplate restTemplate;
+    @Autowired
+    @Lazy
+    private UserService proxy;
+
+    @Override
+    public List<User> listByOpenIds(List<String> openIds) {
+        return lambdaQuery().in(User::getOpenId, openIds).list();
+    }
+
+    @Override
+    public void saveByOpenIds(List<String> openIds) {
+        if (openIds.isEmpty())
+            return;
+        List<User> users = listByOpenIds(openIds);
+        Set<String> dbOpenIdSet = users.stream().map(User::getOpenId).collect(Collectors.toSet());
+        if (users.size() < openIds.size()) {
+            ArrayList<User> userList = new ArrayList<>();
+            openIds.stream().filter(e ->  !dbOpenIdSet.contains(e)).forEach(openId -> {
+                User user = getUserByOpenId(openId, false);
+                if (user != null) {
+                    userList.add(user);
+                }
+            });
+            proxy.saveBatch(userList);
+        }
+    }
+
+    @Override
+    public User getUserByOpenId(String openId, Boolean searchDb) {
+        User user = null;
+        if (searchDb) {
+            user = lambdaQuery().eq(User::getOpenId, openId).getEntity();
+        }
+        if (user != null)
+            return user;
+        log.info("用户不存在,正在从微信服务器获取用户信息...");
+        String url = String.format("https://api.weixin.qq.com/cgi-bin/user/info?access_token=%s&openid=%s&lang=zh_CN",
+                AccessTokenUtil.getAccessToken(), openId);
+        String response = restTemplate.getForObject(url, String.class);
+        if (response == null)
+            return null;
+        JSONObject jsonObject = JSONObject.parseObject(response);
+        String name = jsonObject.getString("nickname");
+        user = new User();
+        user.setOpenId(openId);
+        user.setName(name);
+        return user;
+    }
+}

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

@@ -1,6 +1,11 @@
 spring:
   application:
     name: husj-wx
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://mysql.110310.xyz:3306/husj_wx?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
+    username: root
+    password: 15629747218hsjH
 server:
   servlet:
     context-path: /wechat