tjx
5 天以前 edb7542fe39bc6fc2c3dea9f8f8382252f3d5478
整合钉钉和企业微信
已删除1个文件
已修改1个文件
已添加4个文件
618 ■■■■ 文件已修改
src/main/java/com/gs/dingtalk/entity/QwStaff.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/mapper/QwStaffMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/service/WorkWXService.java 372 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/QwStaffMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/java/com/gs/dingtalk/DeviceReceivingApplicationTests.java 138 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/java/com/gs/dingtalk/MesQaDingtalkServiceTest.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/entity/QwStaff.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
package com.gs.dingtalk.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
/**
 *
 * @TableName QW_STAFF
 */
@TableName(value = "QW_STAFF")
@Data
public class QwStaff implements Serializable {
    @TableField(exist = false)
    private static final long serialVersionUID = 1L;
    /**
     *
     */
    @TableId
    private Long id;
    /**
     *
     */
    private String name;
    /**
     * ä¼å¾®çš„userid
     */
    private String account;
    /**
     *
     */
    private String position;
    /**
     *
     */
    private String dept;
    /**
     *
     */
    private String sex;
    /**
     *
     */
    private String phone;
}
src/main/java/com/gs/dingtalk/mapper/QwStaffMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.gs.dingtalk.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.gs.dingtalk.entity.QwStaff;
/**
* @author Administrator
* @description é’ˆå¯¹è¡¨ã€QW_STAFF】的数据库操作Mapper
* @createDate 2025-11-26 10:51:47
* @Entity com.gs.xky.entity.QwStaff
*/
public interface QwStaffMapper extends BaseMapper<QwStaff> {
}
src/main/java/com/gs/dingtalk/service/WorkWXService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,372 @@
package com.gs.dingtalk.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gs.dingtalk.config.DataAcquisitionConfiguration;
import com.gs.dingtalk.entity.QwStaff;
import com.gs.dingtalk.mapper.QwStaffMapper;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class WorkWXService {
    private static final Logger log = LoggerFactory.getLogger(WorkWXService.class);
    private final OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(90, TimeUnit.SECONDS)
            .readTimeout(90, TimeUnit.SECONDS)
            .build();
    private final ObjectMapper objectMapper = new ObjectMapper();
    private final QwStaffMapper qwStaffMapper;
    public String getAccessToken() throws IOException {
        String url = String.format("https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s",
                DataAcquisitionConfiguration.CORPID,
                DataAcquisitionConfiguration.CORPSECRET);
        Request request = new Request.Builder()
                .url(url)
                .get()
                .build();
        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                log.error("获取企业微信access_token失败,HTTP状态码: {}", response.code());
                throw new IOException("获取access_token失败: " + response.message());
            }
            String responseBody = response.body().string();
            WorkWXTokenResponse tokenResponse = objectMapper.readValue(responseBody, WorkWXTokenResponse.class);
            if (tokenResponse.getErrcode() != 0) {
                log.error("获取企业微信access_token失败,错误码: {}, é”™è¯¯ä¿¡æ¯: {}",
                        tokenResponse.getErrcode(), tokenResponse.getErrmsg());
                throw new IOException("获取access_token失败: " + tokenResponse.getErrmsg());
            }
            log.info("成功获取企业微信access_token,有效期: {}秒", tokenResponse.getExpiresIn());
            log.info("access_token : {}", tokenResponse.getAccessToken());
            return tokenResponse.getAccessToken();
        }
    }
    public String getContactAccessToken() throws IOException {
        String url = String.format("https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s",
                DataAcquisitionConfiguration.CORPID,
                DataAcquisitionConfiguration.TXL_CORPSECRET);
        Request request = new Request.Builder()
                .url(url)
                .get()
                .build();
        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                log.error("获取企业微信通讯录access_token失败,HTTP状态码: {}", response.code());
                throw new IOException("获取通讯录access_token失败: " + response.message());
            }
            String responseBody = response.body().string();
            WorkWXTokenResponse tokenResponse = objectMapper.readValue(responseBody, WorkWXTokenResponse.class);
            if (tokenResponse.getErrcode() != 0) {
                log.error("获取企业微信通讯录access_token失败,错误码: {}, é”™è¯¯ä¿¡æ¯: {}",
                        tokenResponse.getErrcode(), tokenResponse.getErrmsg());
                throw new IOException("获取通讯录access_token失败: " + tokenResponse.getErrmsg());
            }
            log.info("成功获取企业微信通讯录access_token,有效期: {}秒", tokenResponse.getExpiresIn());
            return tokenResponse.getAccessToken();
        }
    }
    public List<WorkWXUser> getUserList() throws IOException {
        String accessToken = getContactAccessToken();
        String url = String.format(
                "https://qyapi.weixin.qq.com/cgi-bin/user/list_id?access_token=%s",
                accessToken);
        List<WorkWXUser> allUsers = new ArrayList<>();
        String cursor = null;
        do {
            Map<String, Object> requestBody = new HashMap<>();
            requestBody.put("limit", 10000);
            if (cursor != null) {
                requestBody.put("cursor", cursor);
            }
            MediaType mediaType = MediaType.parse("application/json; charset=UTF-8");
            String jsonBody = objectMapper.writeValueAsString(requestBody);
            RequestBody body = RequestBody.create(mediaType, jsonBody);
            Request request = new Request.Builder()
                    .url(url)
                    .post(body)
                    .addHeader("Content-Type", "application/json; charset=UTF-8")
                    .build();
            try (Response response = client.newCall(request).execute()) {
                if (!response.isSuccessful()) {
                    log.error("获取企业微信用户列表失败,HTTP状态码: {}", response.code());
                    throw new IOException("获取用户列表失败: " + response.message());
                }
                String responseBody = response.body().string();
                log.info("获取用户列表响应: {}", responseBody);
                WorkWXUserListIdResponse userListResponse = objectMapper.readValue(responseBody, WorkWXUserListIdResponse.class);
                if (userListResponse.getErrcode() != 0) {
                    log.error("获取企业微信用户列表失败,错误码: {}, é”™è¯¯ä¿¡æ¯: {}",
                            userListResponse.getErrcode(), userListResponse.getErrmsg());
                    throw new IOException("获取用户列表失败: " + userListResponse.getErrmsg());
                }
                if (userListResponse.getDeptUser() != null && !userListResponse.getDeptUser().isEmpty()) {
                    allUsers.addAll(userListResponse.getDeptUser());
                }
                cursor = userListResponse.getNextCursor();
            } catch (IOException e) {
                log.error("解析用户列表响应失败", e);
                throw e;
            }
        } while (cursor != null && !cursor.isEmpty());
        log.info("成功获取企业微信用户列表,用户数量: {}", allUsers.size());
        return allUsers;
    }
    public List<CheckinData> getCheckinDataByQwStaff(long startTime, long endTime) throws IOException {
        List<QwStaff> qwStaffList = qwStaffMapper.selectList(new LambdaQueryWrapper<QwStaff>());
        if (qwStaffList == null || qwStaffList.isEmpty()) {
            log.warn("QW_STAFF表中没有数据");
            return new ArrayList<>();
        }
        List<String> useridList = qwStaffList.stream()
                .map(QwStaff::getAccount)
                .filter(account -> account != null && !account.isEmpty())
                .collect(Collectors.toList());
        if (useridList.isEmpty()) {
            log.warn("QW_STAFF表中没有有效的account数据");
            return new ArrayList<>();
        }
        log.info("从QW_STAFF表获取到 {} ä¸ªç”¨æˆ·account", useridList.size());
        return getCheckinData(startTime, endTime, useridList);
    }
    public WorkWXUserDetail getUserDetail(String userid) throws IOException {
        String accessToken = getContactAccessToken();
        String url = String.format("https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token=%s&userid=%s",
                accessToken, userid);
        Request request = new Request.Builder()
                .url(url)
                .get()
                .build();
        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                log.error("获取用户详情失败,HTTP状态码: {}", response.code());
                throw new IOException("获取用户详情失败: " + response.message());
            }
            String responseBody = response.body().string();
            log.info("获取用户详情响应: {}", responseBody);
            WorkWXUserDetailResponse userDetailResponse = objectMapper.readValue(responseBody, WorkWXUserDetailResponse.class);
            if (userDetailResponse.getErrcode() != 0) {
                log.error("获取用户详情失败,错误码: {}, é”™è¯¯ä¿¡æ¯: {}",
                        userDetailResponse.getErrcode(), userDetailResponse.getErrmsg());
                throw new IOException("获取用户详情失败: " + userDetailResponse.getErrmsg());
            }
            log.info("成功获取用户 {} çš„详情", userid);
            return userDetailResponse;
        }
    }
    public List<CheckinData> getCheckinData(long startTime, long endTime, List<String> useridList) throws IOException {
        String accessToken = getAccessToken();
        String url = String.format("https://qyapi.weixin.qq.com/cgi-bin/checkin/getcheckindata?access_token=%s", accessToken);
        List<CheckinData> allCheckinData = new ArrayList<>();
        int batchSize = 100;
        int totalUsers = useridList.size();
        int batchCount = (totalUsers + batchSize - 1) / batchSize;
        log.info("开始获取打卡数据,总用户数: {}, åˆ†æ‰¹æ•°: {}, æ—¶é—´èŒƒå›´: {} - {}", totalUsers, batchCount, startTime, endTime);
        for (int i = 0; i < batchCount; i++) {
            int fromIndex = i * batchSize;
            int toIndex = Math.min((i + 1) * batchSize, totalUsers);
            List<String> batchUserList = useridList.subList(fromIndex, toIndex);
            log.info("正在获取第 {}/{} æ‰¹æ‰“卡数据,用户数: {}", i + 1, batchCount, batchUserList.size());
            Map<String, Object> requestBody = new HashMap<>();
            requestBody.put("opencheckindatatype", 3);
            requestBody.put("starttime", startTime);
            requestBody.put("endtime", endTime);
            requestBody.put("useridlist", batchUserList);
            MediaType mediaType = MediaType.parse("application/json; charset=UTF-8");
            String jsonBody = objectMapper.writeValueAsString(requestBody);
            RequestBody body = RequestBody.create(mediaType, jsonBody);
            Request request = new Request.Builder()
                    .url(url)
                    .post(body)
                    .addHeader("Content-Type", "application/json; charset=UTF-8")
                    .build();
            try (Response response = client.newCall(request).execute()) {
                if (!response.isSuccessful()) {
                    log.error("获取打卡数据失败,HTTP状态码: {}", response.code());
                    throw new IOException("获取打卡数据失败: " + response.message());
                }
                String responseBody = response.body().string();
                WorkWXCheckinResponse checkinResponse = objectMapper.readValue(responseBody, WorkWXCheckinResponse.class);
                if (checkinResponse.getErrcode() != 0) {
                    log.error("获取打卡数据失败,错误码: {}, é”™è¯¯ä¿¡æ¯: {}",
                            checkinResponse.getErrcode(), checkinResponse.getErrmsg());
                    throw new IOException("获取打卡数据失败: " + checkinResponse.getErrmsg());
                }
                if (checkinResponse.getCheckindata() != null) {
                    allCheckinData.addAll(checkinResponse.getCheckindata());
                    log.info("第 {}/{} æ‰¹èŽ·å–åˆ°æ‰“å¡è®°å½•æ•°: {}", i + 1, batchCount, checkinResponse.getCheckindata().size());
                }
            }
            if (i < batchCount - 1) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    log.warn("批次间等待被中断");
                }
            }
        }
        log.info("打卡数据获取完成,总记录数: {}", allCheckinData.size());
        return allCheckinData;
    }
    @Data
    private static class WorkWXTokenResponse {
        private Integer errcode;
        private String errmsg;
        @JsonProperty("access_token")
        private String accessToken;
        @JsonProperty("expires_in")
        private Integer expiresIn;
    }
    @Data
    private static class WorkWXUserIdResponse {
        private Integer errcode;
        private String errmsg;
        private String userid;
    }
    @Data
    private static class WorkWXUserListIdResponse {
        private Integer errcode;
        private String errmsg;
        @JsonProperty("next_cursor")
        private String nextCursor;
        @JsonProperty("dept_user")
        private List<WorkWXUser> deptUser;
    }
    @Data
    public static class WorkWXUser {
        private String userid;
        private Integer department;
        private String name;
        @JsonProperty("open_userid")
        private String openUserid;
    }
    @Data
    public static class WorkWXUserDetail {
        private Integer errcode;
        private String errmsg;
        private String userid;
        private String name;
        private String mobile;
        private String position;
    }
    @Data
    private static class WorkWXUserDetailResponse extends WorkWXUserDetail {
    }
    @Data
    private static class WorkWXCheckinResponse {
        private Integer errcode;
        private String errmsg;
        private List<CheckinData> checkindata;
    }
    @Data
    public static class CheckinData {
        private String userid;
        private String groupname;
        @JsonProperty("checkin_type")
        private String checkinType;
        @JsonProperty("exception_type")
        private String exceptionType;
        @JsonProperty("checkin_time")
        private Long checkinTime;
        @JsonProperty("location_title")
        private String locationTitle;
        @JsonProperty("location_detail")
        private String locationDetail;
        private String wifiname;
        private String notes;
        private String wifimac;
        private String mediaids;
        private Double lat;
        private Double lng;
        @JsonProperty("sch_checkin_time")
        private Long schCheckinTime;
        private Integer groupid;
        @JsonProperty("schedule_id")
        private Integer scheduleId;
        @JsonProperty("timeline_id")
        private Integer timelineId;
        private String deviceid;
    }
}
src/main/resources/mapper/QwStaffMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,7 @@
<?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.gs.dingtalk.mapper.QwStaffMapper">
</mapper>
src/test/java/com/gs/dingtalk/DeviceReceivingApplicationTests.java
@@ -3,61 +3,143 @@
import com.gs.dingtalk.config.URLEncoder;
import com.gs.dingtalk.service.SendDingtalkService;
import com.gs.dingtalk.service.SimpleExample;
import com.gs.dingtalk.service.VwCjScSjTsBbService;
import com.gs.dingtalk.service.WorkWXService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
@SpringBootTest
class DeviceReceivingApplicationTests {
    @Autowired
    private SimpleExample simpleExample;
    @Autowired
    private SendDingtalkService sendDingtalkService;
    private VwCjScSjTsBbService vwCjScSjTsBbService;
    @Autowired
    private WorkWXService workWXService;
    /**
     * æµ‹è¯•导出生产数据并发送钉钉消息
     * åŠŸèƒ½ï¼šæŸ¥è¯¢VW_CJ_SC_SJ_TS_BB表数据 -> å¯¼å‡ºExcel -> å‘送钉钉文件消息
     */
    @Test
    void contextLoads() {
        //FFFFFFFE
        long decimal = Long.parseLong("FFFFFFFE", 16);  // å°†16进制字符串转换为long类型的10进制数
        BigDecimal a = new BigDecimal(String.valueOf((int) decimal));
        BigDecimal b = new BigDecimal("1000");
        BigDecimal c = a.divide(b, 3, RoundingMode.HALF_UP);
        System.out.println(c);
    void testExportAndSendProductionData() throws Exception {
        System.out.println("=== å¼€å§‹æµ‹è¯•导出生产数据并发送钉钉 ===");
//        try {
//            boolean result = vwCjScSjTsBbService.exportAndSendToDingtalk();
//
//            if (result) {
//                System.out.println("✓ ç”Ÿäº§æ•°æ®å¯¼å‡ºå¹¶å‘送成功");
//                System.out.println("  - æ•°æ®å·²ä»Ž VW_CJ_SC_SJ_TS_BB è¡¨æŸ¥è¯¢");
//                System.out.println("  - Excel æ–‡ä»¶å·²ç”Ÿæˆå¹¶ä¿å­˜åˆ° D:\\BIFile\\");
//                System.out.println("  - é’‰é’‰æ–‡ä»¶æ¶ˆæ¯å·²å‘送");
//            } else {
//                System.out.println("✗ ç”Ÿäº§æ•°æ®å¯¼å‡ºæˆ–发送失败");
//            }
//        } catch (Exception e) {
//            System.out.println("✗ å¯¼å‡ºå¹¶å‘送时发生异常: " + e.getMessage());
//            e.printStackTrace();
//        }
        System.out.println("=== æµ‹è¯•结束 ===");
    }
    @Test
    void cont() {
//        long resultCode = Long.parseLong("00000001");
//        int result = (int) resultCode;
//        System.out.println(result);
    void testGetCheckinDataByQwStaff() {
        System.out.println("=== å¼€å§‹æµ‹è¯•通过QW_STAFF表获取打卡数据 ===");
        String encode = URLEncoder.encode("");
        System.out.println(encode);
        try {
            long currentTime = System.currentTimeMillis() / 1000;
            long oneDaySeconds = 86400;
            long endTime = (currentTime / oneDaySeconds) * oneDaySeconds - 1;
            long startTime = endTime - oneDaySeconds + 1;
            System.out.println("  - å¼€å§‹æ—¶é—´: " + new java.util.Date(startTime * 1000));
            System.out.println("  - ç»“束时间: " + new java.util.Date(endTime * 1000));
            List<WorkWXService.CheckinData> checkinDataList = workWXService.getCheckinDataByQwStaff(startTime, endTime);
            if (checkinDataList != null && !checkinDataList.isEmpty()) {
                System.out.println("✓ æˆåŠŸèŽ·å–æ‰“å¡æ•°æ®");
                System.out.println("  - æ‰“卡记录总数: " + checkinDataList.size());
                System.out.println("  - å‰5条数据:");
                checkinDataList.stream().limit(5).forEach(data -> {
                    System.out.println("    * userid: " + data.getUserid() +
                            ", æ‰“卡时间: " + new java.util.Date(data.getCheckinTime() * 1000) +
                            ", æ‰“卡类型: " + data.getCheckinType() +
                            ", å¼‚常类型: " + data.getExceptionType() +
                            ", åœ°ç‚¹: " + data.getLocationDetail());
                });
            } else {
                System.out.println("✗ èŽ·å–çš„æ‰“å¡æ•°æ®ä¸ºç©ºï¼ˆå¯èƒ½QW_STAFF表无数据或时间范围内无打卡记录)");
            }
        } catch (IOException e) {
            System.out.println("✗ èŽ·å–æ‰“å¡æ•°æ®å¤±è´¥: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println("=== æµ‹è¯•结束 ===");
    }
    @Test
    void getPhone() throws Exception {
        sendDingtalkService.getDingTalkUserId();
    void testGetWorkWXUserList() {
        System.out.println("=== å¼€å§‹æµ‹è¯•获取企业微信用户列表 ===");
        try {
            List<WorkWXService.WorkWXUser> userList = workWXService.getUserList();
            if (userList != null && !userList.isEmpty()) {
                System.out.println("✓ æˆåŠŸèŽ·å–ä¼ä¸šå¾®ä¿¡ç”¨æˆ·åˆ—è¡¨");
                System.out.println("  - ç”¨æˆ·æ€»æ•°: " + userList.size());
                System.out.println("  - å‰10条数据:");
                userList.stream().limit(10).forEach(user -> {
                    System.out.println("    * userid: " + user.getUserid() +
                            ", å§“名: " + user.getName() +
                            ", éƒ¨é—¨: " + user.getDepartment());
                });
            } else {
                System.out.println("✗ èŽ·å–çš„ç”¨æˆ·åˆ—è¡¨ä¸ºç©º");
            }
        } catch (IOException e) {
            System.out.println("✗ èŽ·å–ç”¨æˆ·åˆ—è¡¨å¤±è´¥: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println("=== æµ‹è¯•结束 ===");
    }
    @Test
    void sendDingTalk() throws Exception {
        sendDingtalkService.sendDingTalkFiveMinute();
    void testGetUserDetail() {
        System.out.println("=== å¼€å§‹æµ‹è¯•获取用户详情 ===");
        try {
            String userid = "ShenJuanYue";
            WorkWXService.WorkWXUserDetail userDetail = workWXService.getUserDetail(userid);
            if (userDetail != null && userDetail.getErrcode() == 0) {
                System.out.println("✓ æˆåŠŸèŽ·å–ç”¨æˆ·è¯¦æƒ…");
                System.out.println("  - userid: " + userDetail.getUserid());
                System.out.println("  - å§“名: " + userDetail.getName());
                System.out.println("  - æ‰‹æœºå·: " + userDetail.getMobile());
                System.out.println("  - èŒä½: " + userDetail.getPosition());
            } else {
                System.out.println("✗ èŽ·å–ç”¨æˆ·è¯¦æƒ…å¤±è´¥");
            }
        } catch (IOException e) {
            System.out.println("✗ èŽ·å–ç”¨æˆ·è¯¦æƒ…å¤±è´¥: " + e.getMessage());
            e.printStackTrace();
    }
    @Test
    void sendDingTalkthirtyMinute() throws Exception {
        sendDingtalkService.sendDingTalkthirtyMinute();
        System.out.println("=== æµ‹è¯•结束 ===");
    }
    //chatSendMessage
    @Test
    void chatSendMessage() throws Exception {
        sendDingtalkService.chatSendMessage();
    }
}
src/test/java/com/gs/dingtalk/MesQaDingtalkServiceTest.java
ÎļþÒÑɾ³ý