111
tjx
14 小时以前 95e16a3a6877d104d61cb0f99321a31784fcceda
111
已修改1个文件
已添加5个文件
411 ■■■■■ 文件已修改
src/main/java/com/gs/dingtalk/entity/QwCheckinData.java 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/mapper/QwCheckinDataMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/service/QwCheckinDataService.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/service/impl/QwCheckinDataServiceImpl.java 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/QwCheckinDataMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/java/com/gs/dingtalk/DeviceReceivingApplicationTests.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/gs/dingtalk/entity/QwCheckinData.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,130 @@
package com.gs.dingtalk.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
 * ä¼ä¸šå¾®ä¿¡æ‰“卡数据
 * @TableName QW_CHECKIN_DATA
 */
@TableName(value = "QW_CHECKIN_DATA")
@Data
@KeySequence(value = "SEQ_QW_CHECKIN_DATA", dbType = DbType.ORACLE)
public class QwCheckinData implements Serializable {
    @TableField(exist = false)
    private static final long serialVersionUID = 1L;
    /**
     * ä¸»é”®ID
     */
    @TableId
    private Long id;
    /**
     * ç”¨æˆ·id
     */
    private String userid;
    /**
     * æ‰“卡规则名称
     */
    private String groupname;
    /**
     * æ‰“卡类型:上班打卡,下班打卡,外出打卡,仅记录打卡时间和位置
     */
    private String checkinType;
    /**
     * å¼‚常类型:时间异常,地点异常,未打卡,wifi异常,非常用设备(多个异常以分号间隔)
     */
    private String exceptionType;
    /**
     * æ‰“卡时间(Unix时间戳)
     */
    private Long checkinTime;
    /**
     * æ‰“卡时间(转换后的日期时间)
     */
    private Date checkinDate;
    /**
     * æ‰“卡地点title
     */
    private String locationTitle;
    /**
     * æ‰“卡地点详情
     */
    private String locationDetail;
    /**
     * æ‰“卡wifi名称
     */
    private String wifiname;
    /**
     * æ‰“卡备注
     */
    private String notes;
    /**
     * æ‰“卡的MAC地址/bssid
     */
    private String wifimac;
    /**
     * æ‰“卡的附件media_id(多个以逗号分隔)
     */
    private String mediaids;
    /**
     * ä½ç½®æ‰“卡地点纬度(实际纬度的1000000倍,GCJ-02坐标系)
     */
    private Long lat;
    /**
     * ä½ç½®æ‰“卡地点经度(实际经度的1000000倍,GCJ-02坐标系)
     */
    private Long lng;
    /**
     * æ‰“卡设备id
     */
    private String deviceid;
    /**
     * æ ‡å‡†æ‰“卡时间(Unix时间戳)
     */
    private Long schCheckinTime;
    /**
     * æ ‡å‡†æ‰“卡时间(转换后的日期时间)
     */
    private Date schCheckinDate;
    /**
     * è§„则id
     */
    private Long groupid;
    /**
     * ç­æ¬¡id
     */
    private Long scheduleId;
    /**
     * æ—¶æ®µid
     */
    private Long timelineId;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    private Date createTime;
}
src/main/java/com/gs/dingtalk/mapper/QwCheckinDataMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,12 @@
package com.gs.dingtalk.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.gs.dingtalk.entity.QwCheckinData;
/**
 * @description é’ˆå¯¹è¡¨ã€QW_CHECKIN_DATA】企业微信打卡数据的数据库操作Mapper
 * @Entity com.gs.dingtalk.entity.QwCheckinData
 */
public interface QwCheckinDataMapper extends BaseMapper<QwCheckinData> {
}
src/main/java/com/gs/dingtalk/service/QwCheckinDataService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,47 @@
package com.gs.dingtalk.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.gs.dingtalk.entity.QwCheckinData;
import java.io.IOException;
import java.util.List;
/**
 * @description é’ˆå¯¹è¡¨ã€QW_CHECKIN_DATA】企业微信打卡数据的数据库操作Service
 */
public interface QwCheckinDataService extends IService<QwCheckinData> {
    /**
     * å°†CheckinData转换为QwCheckinData实体
     *
     * @param checkinData ä¼ä¸šå¾®ä¿¡æ‰“卡数据
     * @return QwCheckinData实体
     */
    QwCheckinData convertToEntity(WorkWXService.CheckinData checkinData);
    /**
     * æ‰¹é‡ä¿å­˜æ‰“卡数据(自动去重)
     *
     * @param checkinDataList æ‰“卡数据列表
     * @return æ–°å¢žè®°å½•æ•°
     */
    int saveCheckinDataBatch(List<WorkWXService.CheckinData> checkinDataList);
    /**
     * åŒæ­¥æŒ‡å®šæ—¶é—´èŒƒå›´å†…的打卡数据到数据库
     *
     * @param startTime å¼€å§‹æ—¶é—´ï¼ˆUnix时间戳)
     * @param endTime   ç»“束时间(Unix时间戳)
     * @return æ–°å¢žè®°å½•æ•°
     * @throws IOException èŽ·å–æ‰“å¡æ•°æ®å¼‚å¸¸
     */
    int syncCheckinData(long startTime, long endTime) throws IOException;
    /**
     * åŒæ­¥æ˜¨å¤©çš„æ‰“卡数据到数据库
     *
     * @return æ–°å¢žè®°å½•æ•°
     * @throws IOException èŽ·å–æ‰“å¡æ•°æ®å¼‚å¸¸
     */
    int syncYesterdayCheckinData() throws IOException;
}
src/main/java/com/gs/dingtalk/service/impl/QwCheckinDataServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,144 @@
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.QwCheckinData;
import com.gs.dingtalk.mapper.QwCheckinDataMapper;
import com.gs.dingtalk.service.QwCheckinDataService;
import com.gs.dingtalk.service.WorkWXService;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
import java.util.Date;
import java.util.List;
/**
 * @description é’ˆå¯¹è¡¨ã€QW_CHECKIN_DATA】企业微信打卡数据的数据库操作Service实现
 */
@Service
@RequiredArgsConstructor
public class QwCheckinDataServiceImpl extends ServiceImpl<QwCheckinDataMapper, QwCheckinData>
        implements QwCheckinDataService {
    private static final Logger log = LoggerFactory.getLogger(QwCheckinDataServiceImpl.class);
    private final WorkWXService workWXService;
    @Override
    public QwCheckinData convertToEntity(WorkWXService.CheckinData checkinData) {
        if (checkinData == null) {
            return null;
        }
        QwCheckinData entity = new QwCheckinData();
        entity.setUserid(checkinData.getUserid());
        entity.setGroupname(checkinData.getGroupname());
        entity.setCheckinType(checkinData.getCheckinType());
        entity.setExceptionType(checkinData.getExceptionType());
        entity.setCheckinTime(checkinData.getCheckinTime());
        entity.setLocationTitle(checkinData.getLocationTitle());
        entity.setLocationDetail(checkinData.getLocationDetail());
        entity.setWifiname(checkinData.getWifiname());
        entity.setNotes(checkinData.getNotes());
        entity.setWifimac(checkinData.getWifimac());
        entity.setDeviceid(checkinData.getDeviceid());
        // è½¬æ¢Unix时间戳为Date
        if (checkinData.getCheckinTime() != null) {
            entity.setCheckinDate(new Date(checkinData.getCheckinTime() * 1000));
        }
        if (checkinData.getSchCheckinTime() != null) {
            entity.setSchCheckinTime(checkinData.getSchCheckinTime());
            entity.setSchCheckinDate(new Date(checkinData.getSchCheckinTime() * 1000));
        }
        // è½¬æ¢ç»çº¬åº¦ï¼ˆæŽ¥å£è¿”回的是Double,需要转为Long存储)
        if (checkinData.getLat() != null) {
            entity.setLat(checkinData.getLat().longValue());
        }
        if (checkinData.getLng() != null) {
            entity.setLng(checkinData.getLng().longValue());
        }
        // è½¬æ¢mediaids列表为逗号分隔的字符串
        if (checkinData.getMediaids() != null && !checkinData.getMediaids().isEmpty()) {
            entity.setMediaids(String.join(",", checkinData.getMediaids()));
        }
        // è½¬æ¢groupid、scheduleId、timelineId
        if (checkinData.getGroupid() != null) {
            entity.setGroupid(checkinData.getGroupid().longValue());
        }
        if (checkinData.getScheduleId() != null) {
            entity.setScheduleId(checkinData.getScheduleId().longValue());
        }
        if (checkinData.getTimelineId() != null) {
            entity.setTimelineId(checkinData.getTimelineId().longValue());
        }
        entity.setCreateTime(new Date());
        return entity;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int saveCheckinDataBatch(List<WorkWXService.CheckinData> checkinDataList) {
        if (checkinDataList == null || checkinDataList.isEmpty()) {
            return 0;
        }
        int insertCount = 0;
        for (WorkWXService.CheckinData checkinData : checkinDataList) {
            // æ£€æŸ¥æ˜¯å¦å·²å­˜åœ¨ï¼ˆæ ¹æ®userid、checkin_time、checkin_type判断)
            LambdaQueryWrapper<QwCheckinData> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(QwCheckinData::getUserid, checkinData.getUserid())
                    .eq(QwCheckinData::getCheckinTime, checkinData.getCheckinTime())
                    .eq(QwCheckinData::getCheckinType, checkinData.getCheckinType());
            if (this.count(queryWrapper) == 0) {
                QwCheckinData entity = convertToEntity(checkinData);
                this.save(entity);
                insertCount++;
            }
        }
        log.info("批量保存打卡数据完成,总数: {}, æ–°å¢ž: {}, è·³è¿‡(已存在): {}",
                checkinDataList.size(), insertCount, checkinDataList.size() - insertCount);
        return insertCount;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int syncCheckinData(long startTime, long endTime) throws IOException {
        log.info("开始同步打卡数据,时间范围: {} - {}", new Date(startTime * 1000), new Date(endTime * 1000));
        List<WorkWXService.CheckinData> checkinDataList = workWXService.getCheckinDataByQwStaff(startTime, endTime);
        if (checkinDataList == null || checkinDataList.isEmpty()) {
            log.info("未获取到打卡数据");
            return 0;
        }
        log.info("获取到打卡数据 {} æ¡ï¼Œå¼€å§‹ä¿å­˜åˆ°æ•°æ®åº“", checkinDataList.size());
        return saveCheckinDataBatch(checkinDataList);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int syncYesterdayCheckinData() throws IOException {
        long currentTime = System.currentTimeMillis() / 1000;
        long oneDaySeconds = 86400;
        // æ˜¨å¤©å¼€å§‹æ—¶é—´ï¼ˆ00:00:00)
        long startTime = ((currentTime / oneDaySeconds) - 1) * oneDaySeconds;
        // æ˜¨å¤©ç»“束时间(23:59:59)
        long endTime = startTime + oneDaySeconds - 1;
        log.info("同步昨天打卡数据,时间范围: {} - {}", new Date(startTime * 1000), new Date(endTime * 1000));
        return syncCheckinData(startTime, endTime);
    }
}
src/main/resources/mapper/QwCheckinDataMapper.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.QwCheckinDataMapper">
</mapper>
src/test/java/com/gs/dingtalk/DeviceReceivingApplicationTests.java
@@ -4,6 +4,7 @@
import com.gs.dingtalk.config.URLEncoder;
import com.gs.dingtalk.entity.QwStaff;
import com.gs.dingtalk.mapper.QwStaffMapper;
import com.gs.dingtalk.service.QwCheckinDataService;
import com.gs.dingtalk.service.SendDingtalkService;
import com.gs.dingtalk.service.SimpleExample;
import com.gs.dingtalk.service.VwCjScSjTsBbService;
@@ -29,6 +30,9 @@
    @Autowired
    private QwStaffMapper qwStaffMapper;
    @Autowired
    private QwCheckinDataService qwCheckinDataService;
    /**
     * æµ‹è¯•导出生产数据并发送钉钉消息
@@ -67,6 +71,68 @@
        // 3. æŽ¥å£è¿”回最多3000条打卡数据
        // 4. æ ‡å‡†æ‰“卡时间只对于固定排班和自定义排班两种类型有效
        // 5. æŽ¥å£è°ƒç”¨é¢‘率限制为600次/分钟(已在Service层实现批次间延迟)
        //接口返回的原始数据
        //{
        //   "errcode":0,
        //   "errmsg":"ok",
        //   "checkindata": [{
        //        "userid" : "james",
        //        "groupname" : "打卡一组",
        //        "checkin_type" : "上班打卡",
        //        "exception_type" : "地点异常",
        //        "checkin_time" : 1492617610,
        //        "location_title" : "依澜府",
        //        "location_detail" : "四川省成都市武侯区益州大道中段784号附近",
        //        "wifiname" : "办公一区",
        //        "notes" : "路上堵车,迟到了5分钟",
        //        "wifimac" : "3c:46:d8:0c:7a:70",
        //        "mediaids":["WWCISP_G8PYgRaOVHjXWUWFqchpBqqqUpGj0OyR9z6WTwhnMZGCPHxyviVstiv_2fTG8YOJq8L8zJT2T2OvTebANV-2MQ"],
        //        "sch_checkin_time" : 1492617610,
        //        "groupid" : 1,
        //        "schedule_id" : 0,
        //        "timeline_id" : 2
        //    },{
        //        "userid" : "paul",
        //        "groupname" : "打卡二组",
        //        "checkin_type" : "外出打卡",
        //        "exception_type" : "时间异常",
        //        "checkin_time" : 1492617620,
        //        "location_title" : "重庆出口加工区",
        //        "location_detail" : "重庆市渝北区金渝大道101号金渝大道",
        //        "wifiname" : "办公室二区",
        //        "notes" : "",
        //        "wifimac" : "3c:46:d8:0c:7a:71",
        //        "mediaids":["WWCISP_G8PYgRaOVHjXWUWFqchpBqqqUpGj0OyR9z6WTwhnMZGCPHxyviVstiv_2fTG8YOJq8L8zJT2T2OvTebANV-2MQ"],
        //        "lat": 30547645,
        //        "lng": 104063236,
        //        "deviceid":"E5FA89F6-3926-4972-BE4F-4A7ACF4701E2",
        //        "sch_checkin_time" : 1492617610,
        //        "groupid" : 2,
        //        "schedule_id" : 3,
        //        "timeline_id" : 1
        //    }]
        //}
        //checkindata的字段说明
        //userid    ç”¨æˆ·id
        //groupname    æ‰“卡规则名称
        //checkin_type    æ‰“卡类型。字符串,目前有:上班打卡,下班打卡,外出打卡,仅记录打卡时间和位置
        //exception_type    å¼‚常类型,字符串,包括:时间异常,地点异常,未打卡,wifi异常,非常用设备。如果有多个异常,以分号间隔
        //checkin_time    æ‰“卡时间。Unix时间戳
        //location_title    æ‰“卡地点title
        //location_detail    æ‰“卡地点详情
        //wifiname    æ‰“卡wifi名称
        //notes    æ‰“卡备注
        //wifimac    æ‰“卡的MAC地址/bssid
        //mediaids    æ‰“卡的附件media_id,可使用media/get获取附件
        //lat    ä½ç½®æ‰“卡地点纬度,是实际纬度的1000000倍,与腾讯地图一致采用GCJ-02坐标系统标准
        //lng    ä½ç½®æ‰“卡地点经度,是实际经度的1000000倍,与腾讯地图一致采用GCJ-02坐标系统标准
        //deviceid    æ‰“卡设备id
        //sch_checkin_time    æ ‡å‡†æ‰“卡时间,指此次打卡时间对应的标准上班时间或标准下班时间
        //groupid    è§„则id,表示打卡记录所属规则的id
        //schedule_id    ç­æ¬¡id,表示打卡记录所属规则中,所属班次的id
        //timeline_id    æ—¶æ®µid,表示打卡记录所属规则中,某一班次中的某一时段的id,如上下班时间为9:00-12:00、13:00-18:00的班次中,9:00-12:00为其中一组时段
        try {
            long currentTime = System.currentTimeMillis() / 1000;
@@ -110,6 +176,11 @@
                            ", å¼‚常类型: " + data.getExceptionType() +
                            ", åœ°ç‚¹: " + data.getLocationDetail());
                });
                // ä¿å­˜åˆ°æ•°æ®åº“
                System.out.println("  - å¼€å§‹ä¿å­˜åˆ°æ•°æ®åº“...");
                int insertCount = qwCheckinDataService.saveCheckinDataBatch(checkinDataList);
                System.out.println("✓ ä¿å­˜å®Œæˆï¼Œæ–°å¢žè®°å½•æ•°: " + insertCount + ", è·³è¿‡(已存在): " + (checkinDataList.size() - insertCount));
            } else {
                System.out.println("✗ èŽ·å–çš„æ‰“å¡æ•°æ®ä¸ºç©ºï¼ˆå¯èƒ½QW_STAFF表无数据或时间范围内无打卡记录)");
            }