| src/main/java/com/gs/dingtalk/entity/QwHardwareCheckinData.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/gs/dingtalk/mapper/QwHardwareCheckinDataMapper.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/gs/dingtalk/service/QwHardwareCheckinDataService.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/gs/dingtalk/service/WorkWXService.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/gs/dingtalk/service/impl/QwHardwareCheckinDataServiceImpl.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/resources/mapper/QwHardwareCheckinDataMapper.xml | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/main/java/com/gs/dingtalk/entity/QwHardwareCheckinData.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,55 @@ package com.gs.dingtalk.entity; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import java.io.Serializable; import java.util.Date; /** * ä¼ä¸å¾®ä¿¡è®¾å¤æå¡æ°æ® * @TableName QW_HARDWARE_CHECKIN_DATA */ @TableName(value = "QW_HARDWARE_CHECKIN_DATA") @Data @KeySequence(value = "SEQ_QW_HARDWARE_CHECKIN", dbType = DbType.ORACLE) public class QwHardwareCheckinData implements Serializable { @TableField(exist = false) private static final long serialVersionUID = 1L; /** * 主é®ID */ @TableId private Long id; /** * ç¨æ·id */ private String userid; /** * æå¡æ¶é´ï¼Unixæ¶é´æ³ï¼ */ private Long checkinTime; /** * æå¡æ¶é´ï¼è½¬æ¢åçæ¥ææ¶é´ï¼ */ private Date checkinDatetime; /** * æå¡è®¾å¤åºåå· */ private String deviceSn; /** * æå¡è®¾å¤åç§° */ private String deviceName; /** * å建æ¶é´ */ private Date createTime; } src/main/java/com/gs/dingtalk/mapper/QwHardwareCheckinDataMapper.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,12 @@ package com.gs.dingtalk.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.gs.dingtalk.entity.QwHardwareCheckinData; import org.apache.ibatis.annotations.Mapper; /** * ä¼ä¸å¾®ä¿¡è®¾å¤æå¡æ°æ®Mapper */ @Mapper public interface QwHardwareCheckinDataMapper extends BaseMapper<QwHardwareCheckinData> { } src/main/java/com/gs/dingtalk/service/QwHardwareCheckinDataService.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,47 @@ package com.gs.dingtalk.service; import com.baomidou.mybatisplus.extension.service.IService; import com.gs.dingtalk.entity.QwHardwareCheckinData; import java.io.IOException; import java.util.List; /** * ä¼ä¸å¾®ä¿¡è®¾å¤æå¡æ°æ®Service */ public interface QwHardwareCheckinDataService extends IService<QwHardwareCheckinData> { /** * å°HardwareCheckinData转æ¢ä¸ºQwHardwareCheckinDataå®ä½ * * @param data ä¼ä¸å¾®ä¿¡è®¾å¤æå¡æ°æ® * @return QwHardwareCheckinDataå®ä½ */ QwHardwareCheckinData convertToEntity(WorkWXService.HardwareCheckinData data); /** * æ¹éä¿åè®¾å¤æå¡æ°æ®ï¼èªå¨å»éï¼æuserid+checkin_time+device_snï¼ * * @param dataList è®¾å¤æå¡æ°æ®å表 * @return æ°å¢è®°å½æ° */ int saveHardwareDataBatch(List<WorkWXService.HardwareCheckinData> dataList); /** * 忥æå®æ¶é´èå´çè®¾å¤æå¡æ°æ®å°æ°æ®åº * * @param startTime å¼å§æ¶é´ï¼Unixæ¶é´æ³ï¼ç§ï¼ * @param endTime ç»ææ¶é´ï¼Unixæ¶é´æ³ï¼ç§ï¼ * @return æ°å¢è®°å½æ° * @throws IOException è·åè®¾å¤æå¡æ°æ®å¼å¸¸ */ int syncHardwareData(long startTime, long endTime) throws IOException; /** * 忥æ¨å¤©çè®¾å¤æå¡æ°æ®å°æ°æ®åº * * @return æ°å¢è®°å½æ° * @throws IOException è·åè®¾å¤æå¡æ°æ®å¼å¸¸ */ int syncYesterdayHardwareData() throws IOException; } src/main/java/com/gs/dingtalk/service/WorkWXService.java
@@ -825,4 +825,136 @@ private Integer timelineId; private String deviceid; } // ==================== è®¾å¤æå¡æ°æ®ç»æ ==================== @Data private static class WorkWXHardwareCheckinResponse { private Integer errcode; private String errmsg; private List<HardwareCheckinData> checkindata; } @Data public static class HardwareCheckinData { private String userid; @JsonProperty("checkin_time") private Long checkinTime; @JsonProperty("device_sn") private String deviceSn; @JsonProperty("device_name") private String deviceName; } /** * è·åè®¾å¤æå¡æ°æ® * æ¥å£ææ¡£: https://developer.work.weixin.qq.com/document/path/94126 * 1. è·åè®°å½æ¶é´è·¨åº¦ä¸è¶ è¿ä¸ä¸ªæ * 2. ç¨æ·å表ä¸è¶ è¿100个ï¼è¥ç¨æ·è¶ è¿100个ï¼è¯·åæ¹è·å * * @param startTime å¼å§æ¶é´æ³ï¼ç§ï¼ * @param endTime ç»ææ¶é´æ³ï¼ç§ï¼ * @param useridList ç¨æ·IDå表 * @return è®¾å¤æå¡æ°æ®å表 */ public List<HardwareCheckinData> getHardwareCheckinData(long startTime, long endTime, List<String> useridList) throws IOException { String accessToken = getAccessToken(); String url = String.format("https://qyapi.weixin.qq.com/cgi-bin/hardware/get_hardware_checkin_data?access_token=%s", accessToken); List<HardwareCheckinData> 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("filter_type", 1); // 1表示æuseridè¿æ»¤ 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(); WorkWXHardwareCheckinResponse checkinResponse = objectMapper.readValue(responseBody, WorkWXHardwareCheckinResponse.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()); } } // æ¹æ¬¡é´çå¾ 500msï¼é¿å 请æ±è¿äºé¢ç¹ if (i < batchCount - 1) { try { Thread.sleep(500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); log.warn("æ¹æ¬¡é´çå¾ è¢«ä¸æ"); } } } log.info("è®¾å¤æå¡æ°æ®è·åå®æï¼æ»è®°å½æ°: {}", allCheckinData.size()); return allCheckinData; } /** * æ ¹æ®QW_STAFF表è·åè®¾å¤æå¡æ°æ® * * @param startTime å¼å§æ¶é´æ³ï¼ç§ï¼ * @param endTime ç»ææ¶é´æ³ï¼ç§ï¼ * @return è®¾å¤æå¡æ°æ®å表 */ public List<HardwareCheckinData> getHardwareCheckinDataByQwStaff(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 getHardwareCheckinData(startTime, endTime, useridList); } } src/main/java/com/gs/dingtalk/service/impl/QwHardwareCheckinDataServiceImpl.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,107 @@ package com.gs.dingtalk.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.gs.dingtalk.entity.QwHardwareCheckinData; import com.gs.dingtalk.mapper.QwHardwareCheckinDataMapper; import com.gs.dingtalk.service.QwHardwareCheckinDataService; import com.gs.dingtalk.service.WorkWXService; import lombok.RequiredArgsConstructor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.io.IOException; import java.util.Date; import java.util.List; /** * ä¼ä¸å¾®ä¿¡è®¾å¤æå¡æ°æ®Serviceå®ç° */ @Service @RequiredArgsConstructor public class QwHardwareCheckinDataServiceImpl extends ServiceImpl<QwHardwareCheckinDataMapper, QwHardwareCheckinData> implements QwHardwareCheckinDataService { private static final Logger log = LoggerFactory.getLogger(QwHardwareCheckinDataServiceImpl.class); private final WorkWXService workWXService; @Override public QwHardwareCheckinData convertToEntity(WorkWXService.HardwareCheckinData data) { if (data == null) { return null; } QwHardwareCheckinData entity = new QwHardwareCheckinData(); entity.setUserid(data.getUserid()); entity.setCheckinTime(data.getCheckinTime()); if (data.getCheckinTime() != null) { entity.setCheckinDatetime(new Date(data.getCheckinTime() * 1000)); } entity.setDeviceSn(data.getDeviceSn()); entity.setDeviceName(data.getDeviceName()); entity.setCreateTime(new Date()); return entity; } @Override public int saveHardwareDataBatch(List<WorkWXService.HardwareCheckinData> dataList) { if (dataList == null || dataList.isEmpty()) { return 0; } int insertCount = 0; for (WorkWXService.HardwareCheckinData data : dataList) { QwHardwareCheckinData entity = convertToEntity(data); if (entity == null || entity.getUserid() == null || entity.getCheckinTime() == null) { continue; } // æ£æ¥æ¯å¦å·²åå¨ï¼æuserid + checkin_time + device_snå»éï¼ LambdaQueryWrapper<QwHardwareCheckinData> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(QwHardwareCheckinData::getUserid, entity.getUserid()) .eq(QwHardwareCheckinData::getCheckinTime, entity.getCheckinTime()) .eq(QwHardwareCheckinData::getDeviceSn, entity.getDeviceSn()); QwHardwareCheckinData existing = this.getOne(wrapper); if (existing != null) { // å·²åå¨åæ´æ° entity.setId(existing.getId()); this.updateById(entity); log.debug("æ´æ°è®¾å¤æå¡æ°æ®: userid={}, time={}, device={}", entity.getUserid(), entity.getCheckinDatetime(), entity.getDeviceSn()); } else { // ä¸åå¨åæå ¥ this.save(entity); insertCount++; log.debug("æ°å¢è®¾å¤æå¡æ°æ®: userid={}, time={}, device={}", entity.getUserid(), entity.getCheckinDatetime(), entity.getDeviceSn()); } } log.info("è®¾å¤æå¡æ°æ®ä¿åå®æï¼æ°å¢: {}, æ»å¤ç: {}", insertCount, dataList.size()); return insertCount; } @Override public int syncHardwareData(long startTime, long endTime) throws IOException { List<WorkWXService.HardwareCheckinData> dataList = workWXService.getHardwareCheckinDataByQwStaff(startTime, endTime); return saveHardwareDataBatch(dataList); } @Override public int syncYesterdayHardwareData() throws IOException { long currentTime = System.currentTimeMillis() / 1000; long oneDaySeconds = 86400; // æ¨å¤©0ç¹ long yesterdayStart = ((currentTime / oneDaySeconds) - 1) * oneDaySeconds; // æ¨å¤©23:59:59 long yesterdayEnd = yesterdayStart + oneDaySeconds - 1; log.info("å¼å§åæ¥æ¨å¤©({} ~ {})çè®¾å¤æå¡æ°æ®", new Date(yesterdayStart * 1000), new Date(yesterdayEnd * 1000)); return syncHardwareData(yesterdayStart, yesterdayEnd); } } src/main/resources/mapper/QwHardwareCheckinDataMapper.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.QwHardwareCheckinDataMapper"> </mapper>