钉钉考勤
This commit is contained in:
@@ -62,6 +62,20 @@ public class AttendanceController {
|
|||||||
public CzgResult<ArrayList<DingAttendanceStatsVO>> getList(
|
public CzgResult<ArrayList<DingAttendanceStatsVO>> getList(
|
||||||
@RequestParam(required = false) String name, @RequestParam(required = false) String startTime,
|
@RequestParam(required = false) String name, @RequestParam(required = false) String startTime,
|
||||||
@RequestParam(required = false) String endTime) {
|
@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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ package com.czg.market.service;
|
|||||||
import com.czg.market.vo.DingAttendanceStatsVO;
|
import com.czg.market.vo.DingAttendanceStatsVO;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public interface AttendanceService {
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.czg.market.vo;
|
package com.czg.market.vo;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSONArray;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
@@ -34,6 +35,8 @@ public class DingAttendanceStatsVO {
|
|||||||
*/
|
*/
|
||||||
private boolean isActive;
|
private boolean isActive;
|
||||||
|
|
||||||
|
private JSONArray report;
|
||||||
|
|
||||||
public void setValByName(String name, String val) {
|
public void setValByName(String name, String val) {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "出勤天数":
|
case "出勤天数":
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package com.czg.service.market.service.impl;
|
|||||||
import cn.hutool.core.date.DateTime;
|
import cn.hutool.core.date.DateTime;
|
||||||
import cn.hutool.core.date.DateUtil;
|
import cn.hutool.core.date.DateUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
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.entity.ShopConfig;
|
||||||
import com.czg.account.service.ShopConfigService;
|
import com.czg.account.service.ShopConfigService;
|
||||||
import com.czg.market.service.AttendanceService;
|
import com.czg.market.service.AttendanceService;
|
||||||
@@ -10,8 +12,13 @@ import com.czg.market.vo.DingAttendanceStatsVO;
|
|||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.DayOfWeek;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class AttendanceServiceImpl implements AttendanceService {
|
public class AttendanceServiceImpl implements AttendanceService {
|
||||||
@@ -21,18 +28,18 @@ public class AttendanceServiceImpl implements AttendanceService {
|
|||||||
private ShopConfigService shopConfigService;
|
private ShopConfigService shopConfigService;
|
||||||
|
|
||||||
@Override
|
@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 start;
|
||||||
DateTime end;
|
DateTime end;
|
||||||
if (StrUtil.isBlank(startTime)) {
|
if (StrUtil.isBlank(startTime)) {
|
||||||
start = DateUtil.beginOfMonth(new Date());
|
start = DateUtil.beginOfMonth(new Date());
|
||||||
}else {
|
} else {
|
||||||
start = DateUtil.parse(startTime);
|
start = DateUtil.parse(startTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StrUtil.isBlank(endTime)) {
|
if (StrUtil.isBlank(endTime)) {
|
||||||
end = DateUtil.date();
|
end = DateUtil.date();
|
||||||
}else {
|
} else {
|
||||||
end = DateUtil.parse(endTime);
|
end = DateUtil.parse(endTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,6 +48,65 @@ public class AttendanceServiceImpl implements AttendanceService {
|
|||||||
return new ArrayList<>();
|
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
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import cn.hutool.core.collection.CollUtil;
|
|||||||
import cn.hutool.core.date.DateTime;
|
import cn.hutool.core.date.DateTime;
|
||||||
import cn.hutool.core.date.DateUtil;
|
import cn.hutool.core.date.DateUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.alibaba.fastjson2.JSONArray;
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import com.czg.market.vo.DingAttendanceStatsVO;
|
import com.czg.market.vo.DingAttendanceStatsVO;
|
||||||
import com.czg.market.vo.DingUserVO;
|
import com.czg.market.vo.DingUserVO;
|
||||||
@@ -15,6 +16,7 @@ import com.dingtalk.api.request.*;
|
|||||||
import com.dingtalk.api.response.*;
|
import com.dingtalk.api.response.*;
|
||||||
import com.taobao.api.ApiException;
|
import com.taobao.api.ApiException;
|
||||||
import com.taobao.api.TaobaoResponse;
|
import com.taobao.api.TaobaoResponse;
|
||||||
|
import com.taobao.api.internal.util.StringUtils;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@@ -24,10 +26,20 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class DingService {
|
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
|
@Resource
|
||||||
private RedisService redisService;
|
private RedisService redisService;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public String getToken(String key, String secret, Long shopId) {
|
public String getToken(String key, String secret, Long shopId) {
|
||||||
Object accessToken = redisService.get("ding_access_token:" + shopId);
|
Object accessToken = redisService.get("ding_access_token:" + shopId);
|
||||||
if (accessToken instanceof String && StrUtil.isNotBlank((String) accessToken)) {
|
if (accessToken instanceof String && StrUtil.isNotBlank((String) accessToken)) {
|
||||||
@@ -53,6 +65,20 @@ public class DingService {
|
|||||||
return data.getString("access_token");
|
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) {
|
public Map<String, String> getColumns(String key, String secret, Long shopId) {
|
||||||
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/attendance/getattcolumns");
|
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/attendance/getattcolumns");
|
||||||
OapiAttendanceGetattcolumnsRequest req = new OapiAttendanceGetattcolumnsRequest();
|
OapiAttendanceGetattcolumnsRequest req = new OapiAttendanceGetattcolumnsRequest();
|
||||||
@@ -72,7 +98,7 @@ public class DingService {
|
|||||||
Map<String, String> columMap = jsonObject.getJSONObject("result").getJSONArray("columns").stream()
|
Map<String, String> columMap = jsonObject.getJSONObject("result").getJSONArray("columns").stream()
|
||||||
.filter(item -> {
|
.filter(item -> {
|
||||||
JSONObject item1 = (JSONObject) 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")));
|
.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");
|
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 {
|
try {
|
||||||
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/department/listsub");
|
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/department/listsub");
|
||||||
OapiV2DepartmentListsubRequest req = new OapiV2DepartmentListsubRequest();
|
OapiV2DepartmentListsubRequest req = new OapiV2DepartmentListsubRequest();
|
||||||
@@ -140,7 +192,17 @@ public class DingService {
|
|||||||
dingUserVOS.addAll(getUserByDeptId(dptId, key, secret, shopId));
|
dingUserVOS.addAll(getUserByDeptId(dptId, key, secret, shopId));
|
||||||
}
|
}
|
||||||
if (StrUtil.isNotBlank(name)) {
|
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;
|
return dingUserVOS;
|
||||||
}catch (Exception e) {
|
}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);
|
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<>();
|
ArrayList<DingAttendanceStatsVO> statsVOS = new ArrayList<>();
|
||||||
for (DingUserVO item : userList) {
|
for (DingUserVO item : userList) {
|
||||||
JSONObject repost = null;
|
JSONObject repost = null;
|
||||||
@@ -166,6 +228,7 @@ public class DingService {
|
|||||||
statsVO.setName(item.getName());
|
statsVO.setName(item.getName());
|
||||||
if (repost != null) {
|
if (repost != null) {
|
||||||
statsVO.setActive(true);
|
statsVO.setActive(true);
|
||||||
|
statsVO.setReport(repost.getJSONArray("column_vals"));
|
||||||
repost.getJSONArray("column_vals").forEach(info -> {
|
repost.getJSONArray("column_vals").forEach(info -> {
|
||||||
JSONObject infoObj = (JSONObject) info;
|
JSONObject infoObj = (JSONObject) info;
|
||||||
Long id = infoObj.getJSONObject("column_vo").getLong("id");
|
Long id = infoObj.getJSONObject("column_vo").getLong("id");
|
||||||
|
|||||||
Reference in New Issue
Block a user