钉钉考勤

This commit is contained in:
张松
2025-12-01 14:36:42 +08:00
parent fe76b4133a
commit ecdb2d9938
5 changed files with 160 additions and 11 deletions

View File

@@ -62,6 +62,20 @@ public class AttendanceController {
public CzgResult<ArrayList<DingAttendanceStatsVO>> getList(
@RequestParam(required = false) String name, @RequestParam(required = false) String startTime,
@RequestParam(required = false) String endTime) {
return CzgResult.success(attendanceService.list(StpKit.USER.getShopId(), name, startTime, endTime));
return CzgResult.success(attendanceService.list(StpKit.USER.getShopId(), name, startTime, endTime, null));
}
/**
* 用户打卡详情
* @param userId 返回的userId
* @param startTime 开始时间
* @param endTime 结束时间
*/
@GetMapping("/detail")
public CzgResult<Map<String, Object>> detail(
@RequestParam String userId, @RequestParam(required = false) String startTime,
@RequestParam(required = false) String endTime, @RequestParam(required = false, defaultValue = "0") Integer weekNum) {
return CzgResult.success(attendanceService.detail(StpKit.USER.getShopId(), userId, startTime, endTime, weekNum));
}
}

View File

@@ -3,7 +3,10 @@ package com.czg.market.service;
import com.czg.market.vo.DingAttendanceStatsVO;
import java.util.ArrayList;
import java.util.Map;
public interface AttendanceService {
ArrayList<DingAttendanceStatsVO> list(Long shopId, String name, String startTime, String endTime);
ArrayList<DingAttendanceStatsVO> list(Long shopId, String name, String startTime, String endTime, String userId);
Map<String, Object> detail(Long shopId, String userId, String startTime, String endTime, Integer weekNum);
}

View File

@@ -1,5 +1,6 @@
package com.czg.market.vo;
import com.alibaba.fastjson2.JSONArray;
import lombok.Data;
import java.math.BigDecimal;
@@ -34,6 +35,8 @@ public class DingAttendanceStatsVO {
*/
private boolean isActive;
private JSONArray report;
public void setValByName(String name, String val) {
switch (name) {
case "出勤天数":

View File

@@ -3,6 +3,8 @@ package com.czg.service.market.service.impl;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.czg.account.entity.ShopConfig;
import com.czg.account.service.ShopConfigService;
import com.czg.market.service.AttendanceService;
@@ -10,8 +12,13 @@ import com.czg.market.vo.DingAttendanceStatsVO;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.DayOfWeek;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedDeque;
@Service
public class AttendanceServiceImpl implements AttendanceService {
@@ -21,18 +28,18 @@ public class AttendanceServiceImpl implements AttendanceService {
private ShopConfigService shopConfigService;
@Override
public ArrayList<DingAttendanceStatsVO> list(Long shopId, String name, String startTime, String endTime) {
public ArrayList<DingAttendanceStatsVO> list(Long shopId, String name, String startTime, String endTime, String userId) {
DateTime start;
DateTime end;
if (StrUtil.isBlank(startTime)) {
start = DateUtil.beginOfMonth(new Date());
}else {
} else {
start = DateUtil.parse(startTime);
}
if (StrUtil.isBlank(endTime)) {
end = DateUtil.date();
}else {
} else {
end = DateUtil.parse(endTime);
}
@@ -41,6 +48,65 @@ public class AttendanceServiceImpl implements AttendanceService {
return new ArrayList<>();
}
return dingService.getUserReport(shopId, config.getDingAppKey(), config.getDingAppSecret(), start, end, name);
return dingService.getUserReport(shopId, config.getDingAppKey(), config.getDingAppSecret(), start, end, name, userId);
}
// 自定义规则1-5 → 数字, 6 → 六, 7 → 日
private static String formatWeek(String week) {
return switch (week) {
case "星期一" -> "1";
case "星期二" -> "2";
case "星期三" -> "3";
case "星期四" -> "4";
case "星期五" -> "5";
case "星期六" -> "";
case "星期日" -> "";
default -> week;
};
}
@Override
public Map<String, Object> detail(Long shopId, String userId, String startTime, String endTime, Integer weekNum) {
ArrayList<DingAttendanceStatsVO> listed = list(shopId, null, startTime, endTime, userId);
ArrayList<Map<String, Object>> weekList = new ArrayList<>();
DateTime dateTime = DateUtil.parse(endTime);
for (int i = 7 * weekNum; i < 7 + 7 * weekNum; i++) {
DateTime date = DateUtil.offsetDay(dateTime, i * -1);
String week = formatWeek(DateUtil.dayOfWeekEnum(date).toChinese());
if (date.compareTo(DateUtil.parseDate(startTime)) < 0) {
break;
}
weekList.add(new HashMap<>(){{
put("week", week);
put("date", date);
}});
}
if (listed.isEmpty()) {
return new HashMap<String, Object>() {{
put("attendanceSummary", null);
put("weekList", weekList);
put("statusList", null);
}};
}
ShopConfig config = shopConfigService.getById(shopId);
if (config == null || StrUtil.isBlank(config.getDingAppSecret())) {
return new HashMap<String, Object>() {{
put("attendanceSummary", null);
put("weekList", weekList);
put("statusList", null);
}};
}
ConcurrentLinkedDeque<String> statusList = new ConcurrentLinkedDeque<>();
weekList.parallelStream().forEach(week -> {
statusList.add(dingService.getAttendanceStatus(config.getDingAppKey(), config.getDingAppSecret(), shopId, (Date) week.get("date"), userId));
});
return Map.of(
"attendanceSummary", listed.getFirst(),
"weekList", weekList,
"statusList", statusList
);
}
}

View File

@@ -5,6 +5,7 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.czg.market.vo.DingAttendanceStatsVO;
import com.czg.market.vo.DingUserVO;
@@ -15,6 +16,7 @@ import com.dingtalk.api.request.*;
import com.dingtalk.api.response.*;
import com.taobao.api.ApiException;
import com.taobao.api.TaobaoResponse;
import com.taobao.api.internal.util.StringUtils;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
@@ -24,10 +26,20 @@ import java.util.stream.Collectors;
@Service
public class DingService {
public static final Map<String, String> STATUS_MAP = new HashMap<>(){{
put("Normal", "正常");
put("Early", "早退");
put("Late", "迟到");
put("SeriousLate", "严重迟到");
put("Absenteeism", "旷工迟到");
put("NotSigned", "未打卡");
}};
@Resource
private RedisService redisService;
public String getToken(String key, String secret, Long shopId) {
Object accessToken = redisService.get("ding_access_token:" + shopId);
if (accessToken instanceof String && StrUtil.isNotBlank((String) accessToken)) {
@@ -53,6 +65,20 @@ public class DingService {
return data.getString("access_token");
}
public String getColumIdByName(String key, String secret, Long shopId, String name) {
Map<String, String> columns = getColumns(key, secret, shopId);
String columId = null;
for (Map.Entry<String, String> entry : columns.entrySet()) {
String key1 = entry.getKey();
String value = entry.getValue();
if (value.equals(name)) {
columId = key1;
break;
}
}
return columId;
}
public Map<String, String> getColumns(String key, String secret, Long shopId) {
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/attendance/getattcolumns");
OapiAttendanceGetattcolumnsRequest req = new OapiAttendanceGetattcolumnsRequest();
@@ -72,7 +98,7 @@ public class DingService {
Map<String, String> columMap = jsonObject.getJSONObject("result").getJSONArray("columns").stream()
.filter(item -> {
JSONObject item1 = (JSONObject) item;
return item1.containsKey("id") && CollUtil.newArrayList("出勤天数", "休息天数", "工作时长", "迟到次数", "迟到时长", "应出勤天数").contains(item1.getString("name"));
return item1.containsKey("id") && CollUtil.newArrayList("出勤天数", "休息天数", "工作时长", "迟到次数", "迟到时长", "应出勤天数", "旷工迟到次数").contains(item1.getString("name"));
})
.collect(Collectors.toMap(item -> ((JSONObject) item).getLong("id").toString(), item -> ((JSONObject) item).getString("name")));
@@ -125,7 +151,33 @@ public class DingService {
return data == null ? null : data.getJSONObject("result");
}
public ArrayList<DingUserVO> getUserList(String name,String key, String secret, Long shopId) {
public String getAttendanceStatus(String key, String secret, Long shopId, Date date, String userId) {
String status = "";
try {
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/attendance/getupdatedata");
OapiAttendanceGetupdatedataRequest req = new OapiAttendanceGetupdatedataRequest();
req.setUserid(userId);
req.setWorkDate(date);
OapiAttendanceGetupdatedataResponse rsp = client.execute(req, getToken(key, secret, shopId));
JSONArray resultList = JSONObject.parseObject(rsp.getBody()).getJSONObject("result").getJSONArray("attendance_result_list");
if (resultList.isEmpty()) {
status = "休息日";
}
for (Object item : resultList) {
JSONObject jsonObject = (JSONObject) item;
String timeResult = jsonObject.getString("time_result");
if (!"正常".equals(status)) {
status = STATUS_MAP.get(timeResult);
}
}
return status;
} catch (ApiException e) {
throw new RuntimeException(e);
}
}
public ArrayList<DingUserVO> getUserList(String name,String key, String secret, Long shopId, String userId) {
try {
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/department/listsub");
OapiV2DepartmentListsubRequest req = new OapiV2DepartmentListsubRequest();
@@ -140,7 +192,17 @@ public class DingService {
dingUserVOS.addAll(getUserByDeptId(dptId, key, secret, shopId));
}
if (StrUtil.isNotBlank(name)) {
return dingUserVOS.stream().filter(item -> item.getName().contains(name)).collect(Collectors.toCollection(ArrayList::new));
return dingUserVOS.stream().filter(item -> {
boolean flag = true;
if (StrUtil.isNotBlank(userId)) {
flag = item.getUserid().equals(userId);
}
if (StrUtil.isNotBlank(name)) {
flag = item.getName().contains(name);
}
return flag;
}).collect(Collectors.toCollection(ArrayList::new));
}
return dingUserVOS;
}catch (Exception e) {
@@ -148,9 +210,9 @@ public class DingService {
}
}
public ArrayList<DingAttendanceStatsVO> getUserReport(Long shopId, String key, String secret, DateTime startTime, DateTime endTime, String name) {
public ArrayList<DingAttendanceStatsVO> getUserReport(Long shopId, String key, String secret, DateTime startTime, DateTime endTime, String name, String userId) {
Map<String, String> columns = getColumns(key, secret, shopId);
ArrayList<DingUserVO> userList = getUserList(name, key, secret, shopId);
ArrayList<DingUserVO> userList = getUserList(name, key, secret, shopId, userId);
ArrayList<DingAttendanceStatsVO> statsVOS = new ArrayList<>();
for (DingUserVO item : userList) {
JSONObject repost = null;
@@ -166,6 +228,7 @@ public class DingService {
statsVO.setName(item.getName());
if (repost != null) {
statsVO.setActive(true);
statsVO.setReport(repost.getJSONArray("column_vals"));
repost.getJSONArray("column_vals").forEach(info -> {
JSONObject infoObj = (JSONObject) info;
Long id = infoObj.getJSONObject("column_vo").getLong("id");