111
tjx
16 小时以前 8e3309ef57424194ce9683175b59d3c9e8cb0b27
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
package com.gs.dingtalk;
 
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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;
import com.gs.dingtalk.service.WorkWXService;
import com.gs.dingtalk.service.QwCheckinDayDataService;
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 VwCjScSjTsBbService vwCjScSjTsBbService;
 
    @Autowired
    private WorkWXService workWXService;
 
    @Autowired
    private QwStaffMapper qwStaffMapper;
 
    @Autowired
    private QwCheckinDataService qwCheckinDataService;
 
    @Autowired
    private QwCheckinDayDataService qwCheckinDayDataService;
 
    /**
     * 测试导出生产数据并发送钉钉消息
     * 功能:查询VW_CJ_SC_SJ_TS_BB表数据 -> 导出Excel -> 发送钉钉文件消息
     */
    @Test
    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 testGetCheckinDataByQwStaff() {
        System.out.println("=== 开始测试通过QW_STAFF表获取打卡数据 ===");
 
        // 企业微信打卡接口限制:
        // 1. 获取记录时间跨度不超过30天
        // 2. 用户列表不超过100个。若用户超过100个,请分批获取(已在Service层实现自动分批)
        // 3. 接口返回最多3000条打卡数据
        // 4. 标准打卡时间只对于固定排班和自定义排班两种类型有效
        // 5. 接口调用频率限制为600次/分钟(已在Service层实现批次间延迟)
 
        try {
            long currentTime = System.currentTimeMillis() / 1000;
            long oneDaySeconds = 86400;
            long thirtyDaysSeconds = 30 * oneDaySeconds;
 
            long endTime = (currentTime / oneDaySeconds) * oneDaySeconds - 1;
            long startTime = endTime - oneDaySeconds + 1;
 
            // 验证时间跨度不超过30天
            long timeSpan = endTime - startTime;
            if (timeSpan > thirtyDaysSeconds) {
                System.out.println("✗ 时间跨度超过30天限制: " + (timeSpan / oneDaySeconds) + "天");
                return;
            }
 
            System.out.println("  - 开始时间: " + new java.util.Date(startTime * 1000));
            System.out.println("  - 结束时间: " + new java.util.Date(endTime * 1000));
            System.out.println("  - 时间跨度: " + (timeSpan / oneDaySeconds) + "天 (限制: ≤30天)");
 
            // 获取用户总数
            long totalUsers = qwStaffMapper.selectCount(new LambdaQueryWrapper<QwStaff>()
                    .isNotNull(QwStaff::getAccount)
                    .ne(QwStaff::getAccount, ""));
            System.out.println("  - QW_STAFF表用户总数: " + totalUsers);
            if (totalUsers > 100) {
                int batchCount = (int) ((totalUsers + 99) / 100);
                System.out.println("  - 将自动分批处理: " + batchCount + "批 (每批≤100用户)");
            }
 
            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());
                });
 
                // 保存到数据库
                System.out.println("  - 开始保存到数据库...");
                int insertCount = qwCheckinDataService.saveCheckinDataBatch(checkinDataList);
                System.out.println("✓ 保存完成,新增记录数: " + insertCount + ", 跳过(已存在): " + (checkinDataList.size() - insertCount));
            } else {
                System.out.println("✗ 获取的打卡数据为空(可能QW_STAFF表无数据或时间范围内无打卡记录)");
            }
        } catch (IOException e) {
            System.out.println("✗ 获取打卡数据失败: " + e.getMessage());
            e.printStackTrace();
        }
 
        System.out.println("=== 测试结束 ===");
    }
 
    @Test
    void testGetCheckinDataById() {
        System.out.println("=== 开始测试通过QW_STAFF表获取打卡数据 ===");
        QwStaff qwStaff = qwStaffMapper.selectById(3);
 
        if (qwStaff == null || qwStaff.getAccount() == null || qwStaff.getAccount().isEmpty()) {
            System.out.println("✗ 未找到ID为3的员工或员工account为空");
            return;
        }
 
        System.out.println("  - 员工姓名: " + qwStaff.getName());
        System.out.println("  - 员工账号: " + qwStaff.getAccount());
 
        try {
            long currentTime = System.currentTimeMillis() / 1000;
            long oneDaySeconds = 86400;
            long thirtyDaysSeconds = 30 * oneDaySeconds;
 
            // 昨天结束时间(23:59:59)
            long yesterdayEnd = ((currentTime / oneDaySeconds) - 1) * oneDaySeconds + oneDaySeconds - 1;
            // 20天前开始时间(00:00:00)
            long twentyDaysAgoStart = yesterdayEnd - 20 * oneDaySeconds + 1;
 
            long endTime = yesterdayEnd;
            long startTime = twentyDaysAgoStart;
 
            // 验证时间跨度不超过30天
            long timeSpan = endTime - startTime;
            if (timeSpan > thirtyDaysSeconds) {
                System.out.println("✗ 时间跨度超过30天限制: " + (timeSpan / oneDaySeconds) + "天");
                return;
            }
 
            System.out.println("  - 开始时间: " + new java.util.Date(startTime * 1000) + " (20天前)");
            System.out.println("  - 结束时间: " + new java.util.Date(endTime * 1000) + " (昨天)");
            System.out.println("  - 时间跨度: " + (timeSpan / oneDaySeconds) + "天");
 
            List<String> useridList = new java.util.ArrayList<>();
            useridList.add(qwStaff.getAccount());
 
            List<WorkWXService.CheckinData> checkinDataList = workWXService.getCheckinData(startTime, endTime, useridList);
 
            if (checkinDataList != null && !checkinDataList.isEmpty()) {
                System.out.println("✓ 成功获取打卡数据");
                System.out.println("  - 打卡记录总数: " + checkinDataList.size());
                checkinDataList.forEach(data -> {
                    System.out.println("    * 打卡时间: " + new java.util.Date(data.getCheckinTime() * 1000) +
                            ", 打卡类型: " + data.getCheckinType() +
                            ", 异常类型: " + data.getExceptionType() +
                            ", 地点: " + data.getLocationDetail());
                });
            } else {
                System.out.println("✗ 该员工在时间范围内无打卡记录");
            }
        } catch (IOException e) {
            System.out.println("✗ 获取打卡数据失败: " + e.getMessage());
            e.printStackTrace();
        }
 
        System.out.println("=== 测试结束 ===");
    }
 
    @Test
    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 testSyncUsersToQwStaff() {
        System.out.println("=== 开始测试同步企业微信用户到QW_STAFF表 ===");
 
        try {
            int insertCount = workWXService.syncUsersToQwStaff();
 
            System.out.println("✓ 同步完成");
            System.out.println("  - 新增用户数: " + insertCount);
        } catch (IOException e) {
            System.out.println("✗ 同步用户失败: " + e.getMessage());
            e.printStackTrace();
        }
 
        System.out.println("=== 测试结束 ===");
    }
 
    /**
     * 测试获取打卡日报数据
     * 接口限制:100次/分钟,时间跨度不超过30天
     */
    @Test
    void testGetCheckinDayData() {
        System.out.println("=== 开始测试获取打卡日报数据 ===");
 
        try {
            long currentTime = System.currentTimeMillis() / 1000;
            long oneDaySeconds = 86400;
 
            // 获取昨天的日报数据(starttime和endtime需要是同一天)
            long yesterdayStart = ((currentTime / oneDaySeconds) - 1) * oneDaySeconds;
            long startTime = yesterdayStart;
            long endTime = yesterdayStart;  // 日报接口starttime和endtime需相同
 
            System.out.println("  - 查询日期: " + new java.util.Date(startTime * 1000));
 
            // 获取用户总数
            long totalUsers = qwStaffMapper.selectCount(new LambdaQueryWrapper<QwStaff>()
                    .isNotNull(QwStaff::getAccount)
                    .ne(QwStaff::getAccount, ""));
            System.out.println("  - QW_STAFF表用户总数: " + totalUsers);
 
            List<WorkWXService.CheckinDayData> dayDataList = workWXService.getCheckinDayDataByQwStaff(startTime, endTime);
 
            if (dayDataList != null && !dayDataList.isEmpty()) {
                System.out.println("✓ 成功获取打卡日报数据");
                System.out.println("  - 日报记录总数: " + dayDataList.size());
                System.out.println("  - 前5条数据:");
                dayDataList.stream().limit(5).forEach(data -> {
                    WorkWXService.BaseInfo baseInfo = data.getBaseInfo();
                    WorkWXService.SummaryInfo summaryInfo = data.getSummaryInfo();
                    System.out.println("    * 姓名: " + (baseInfo != null ? baseInfo.getName() : "N/A") +
                            ", 账号: " + (baseInfo != null ? baseInfo.getAcctid() : "N/A") +
                            ", 部门: " + (baseInfo != null ? baseInfo.getDepartsName() : "N/A") +
                            ", 打卡次数: " + (summaryInfo != null ? summaryInfo.getCheckinCount() : 0) +
                            ", 实际工时(秒): " + (summaryInfo != null ? summaryInfo.getRegularWorkSec() : 0) +
                            ", 标准工时(秒): " + (summaryInfo != null ? summaryInfo.getStandardWorkSec() : 0));
 
                    // 打印异常信息
                    if (data.getExceptionInfos() != null && !data.getExceptionInfos().isEmpty()) {
                        data.getExceptionInfos().forEach(ex -> {
                            String exType = getExceptionTypeName(ex.getException());
                            System.out.println("      异常: " + exType + ", 次数: " + ex.getCount() + ", 时长(秒): " + ex.getDuration());
                        });
                    }
                });
 
                // 保存到数据库
                System.out.println("  - 开始保存打卡日报数据到数据库...");
                int insertCount = qwCheckinDayDataService.saveDayDataBatch(dayDataList);
                System.out.println("✓ 保存完成,新增记录数: " + insertCount + ", 更新(已存在): " + (dayDataList.size() - insertCount));
            } else {
                System.out.println("✗ 获取的打卡日报数据为空");
            }
        } catch (IOException e) {
            System.out.println("✗ 获取打卡日报数据失败: " + e.getMessage());
            e.printStackTrace();
        }
 
        System.out.println("=== 测试结束 ===");
    }
 
    /**
     * 测试获取指定员工的打卡日报数据
     */
    @Test
    void testGetCheckinDayDataById() {
        System.out.println("=== 开始测试获取指定员工打卡日报数据 ===");
 
        QwStaff qwStaff = qwStaffMapper.selectById(3);
 
        if (qwStaff == null || qwStaff.getAccount() == null || qwStaff.getAccount().isEmpty()) {
            System.out.println("✗ 未找到ID为3的员工或员工account为空");
            return;
        }
 
        System.out.println("  - 员工姓名: " + qwStaff.getName());
        System.out.println("  - 员工账号: " + qwStaff.getAccount());
 
        try {
            long currentTime = System.currentTimeMillis() / 1000;
            long oneDaySeconds = 86400;
 
            // 获取昨天的日报
            long yesterdayStart = ((currentTime / oneDaySeconds) - 1) * oneDaySeconds;
            long startTime = yesterdayStart;
            long endTime = yesterdayStart;
 
            System.out.println("  - 查询日期: " + new java.util.Date(startTime * 1000));
 
            List<String> useridList = new java.util.ArrayList<>();
            useridList.add(qwStaff.getAccount());
 
            List<WorkWXService.CheckinDayData> dayDataList = workWXService.getCheckinDayData(startTime, endTime, useridList);
 
            if (dayDataList != null && !dayDataList.isEmpty()) {
                System.out.println("✓ 成功获取打卡日报数据");
                dayDataList.forEach(data -> {
                    WorkWXService.BaseInfo baseInfo = data.getBaseInfo();
                    WorkWXService.SummaryInfo summaryInfo = data.getSummaryInfo();
 
                    System.out.println("  基础信息:");
                    System.out.println("    - 姓名: " + (baseInfo != null ? baseInfo.getName() : "N/A"));
                    System.out.println("    - 部门: " + (baseInfo != null ? baseInfo.getDepartsName() : "N/A"));
                    System.out.println("    - 规则: " + (baseInfo != null && baseInfo.getRuleInfo() != null ? baseInfo.getRuleInfo().getGroupname() : "N/A"));
 
                    System.out.println("  汇总信息:");
                    System.out.println("    - 打卡次数: " + (summaryInfo != null ? summaryInfo.getCheckinCount() : 0));
                    System.out.println("    - 实际工时: " + (summaryInfo != null ? formatSeconds(summaryInfo.getRegularWorkSec()) : "0"));
                    System.out.println("    - 标准工时: " + (summaryInfo != null ? formatSeconds(summaryInfo.getStandardWorkSec()) : "0"));
 
                    if (data.getExceptionInfos() != null && !data.getExceptionInfos().isEmpty()) {
                        System.out.println("  异常信息:");
                        data.getExceptionInfos().forEach(ex -> {
                            String exType = getExceptionTypeName(ex.getException());
                            System.out.println("    - " + exType + ": " + ex.getCount() + "次, 时长: " + formatSeconds(ex.getDuration()));
                        });
                    }
 
                    if (data.getOtInfo() != null && data.getOtInfo().getOtStatus() != null && data.getOtInfo().getOtStatus() > 0) {
                        System.out.println("  加班信息:");
                        System.out.println("    - 加班时长: " + formatSeconds(data.getOtInfo().getOtDuration()));
                    }
                });
            } else {
                System.out.println("✗ 该员工在指定日期无打卡日报数据");
            }
        } catch (IOException e) {
            System.out.println("✗ 获取打卡日报数据失败: " + e.getMessage());
            e.printStackTrace();
        }
 
        System.out.println("=== 测试结束 ===");
    }
 
    /**
     * 格式化秒数为时分秒
     */
    private String formatSeconds(Integer seconds) {
        if (seconds == null || seconds == 0) {
            return "0秒";
        }
        int hours = seconds / 3600;
        int minutes = (seconds % 3600) / 60;
        int secs = seconds % 60;
 
        StringBuilder sb = new StringBuilder();
        if (hours > 0) sb.append(hours).append("小时");
        if (minutes > 0) sb.append(minutes).append("分钟");
        if (secs > 0) sb.append(secs).append("秒");
        return sb.toString();
    }
 
    /**
     * 获取异常类型名称
     * @param exceptionType 异常类型代码:1-迟到;2-早退;3-缺卡;4-旷工;5-地点异常;6-设备异常
     */
    private String getExceptionTypeName(Integer exceptionType) {
        if (exceptionType == null) {
            return "未知";
        }
        switch (exceptionType) {
            case 1:
                return "迟到";
            case 2:
                return "早退";
            case 3:
                return "缺卡";
            case 4:
                return "旷工";
            case 5:
                return "地点异常";
            case 6:
                return "设备异常";
            default:
                return "未知";
        }
    }
 
}