人脸对接
This commit is contained in:
@ -0,0 +1,89 @@
|
|||||||
|
package org.dromara.common.utils.attendance;
|
||||||
|
|
||||||
|
|
||||||
|
import cn.hutool.http.HttpUtil;
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.alibaba.fastjson2.JSONArray;
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class FaceUtil {
|
||||||
|
|
||||||
|
private static final String FACE_URL = "http://192.168.110.5:1224";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人脸记录 post
|
||||||
|
* @param name 姓名
|
||||||
|
* @param card 身份证
|
||||||
|
* @param path 人脸图片HTTP地址(需可公开访问)
|
||||||
|
*/
|
||||||
|
public static void createFaceRecord(String name, String card, String path) {
|
||||||
|
String url = FACE_URL+"/api/faces";
|
||||||
|
|
||||||
|
HashMap<String, Object> param = new HashMap<>() {{
|
||||||
|
put("name", name);
|
||||||
|
put("card", card);
|
||||||
|
put("path", path);
|
||||||
|
}};
|
||||||
|
HttpUtil.post(url, param);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 人脸检测
|
||||||
|
* @param path 图片HTTP地址(需可公开访问)
|
||||||
|
*/
|
||||||
|
public static Map<String, String> faceDetect(String path) {
|
||||||
|
String url = FACE_URL+"/api/faces/detect";
|
||||||
|
|
||||||
|
HashMap<String, Object> param = new HashMap<>() {{
|
||||||
|
put("path", path);
|
||||||
|
put("similarity_threshold", 0.8);
|
||||||
|
}};
|
||||||
|
//转成json
|
||||||
|
|
||||||
|
String post = HttpUtil.post(url, param);
|
||||||
|
Map<String, String> map = new HashMap<>();
|
||||||
|
// 遍历检测到的人脸数据
|
||||||
|
try {
|
||||||
|
// 解析返回的JSON数据
|
||||||
|
JSONObject response = JSON.parseObject(post);
|
||||||
|
JSONObject data = response.getJSONObject("data");
|
||||||
|
JSONArray detectedFaces = data.getJSONArray("detected_faces");
|
||||||
|
|
||||||
|
for (int i = 0; i < detectedFaces.size(); i++) {
|
||||||
|
JSONObject face = detectedFaces.getJSONObject(i);
|
||||||
|
JSONObject matchInfo = face.getJSONObject("match_info");
|
||||||
|
|
||||||
|
// 检查相似度是否大于等于阈值
|
||||||
|
double similarity = matchInfo.getDoubleValue("similarity");
|
||||||
|
double threshold = matchInfo.getDoubleValue("threshold");
|
||||||
|
|
||||||
|
if (similarity >= threshold) {
|
||||||
|
// 提取用户信息
|
||||||
|
JSONObject userInfo = face.getJSONObject("user_info");
|
||||||
|
String name = userInfo.getString("name");
|
||||||
|
String idCard = userInfo.getString("id_card");
|
||||||
|
map.put(idCard, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch (Exception e){
|
||||||
|
log.error("人脸检测失败",e);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// Map<String, String> map = faceDetect("http://xny.yj-3d.com:9000/xinnengyuan-dev/2025/10/12/9688ce2474ad47e7bf59c641848cdf8f.jpg");
|
||||||
|
// System.out.println(map);
|
||||||
|
// createFaceRecord("石志强","513022111145632652","http://xny.yj-3d.com:9000/xinnengyuan-dev/2025/10/12/9688ce2474ad47e7bf59c641848cdf8f.jpg");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -69,6 +69,7 @@ public class BusAttendanceDeviceController extends BaseController {
|
|||||||
req.setProjectId(one.getProjectId());
|
req.setProjectId(one.getProjectId());
|
||||||
req.setUserId(userId);
|
req.setUserId(userId);
|
||||||
req.setPunchTime(localDateTime);
|
req.setPunchTime(localDateTime);
|
||||||
|
req.setSource("1");
|
||||||
//打印req
|
//打印req
|
||||||
log.info("请求参数:{}", req);
|
log.info("请求参数:{}", req);
|
||||||
//base64转MultipartFile
|
//base64转MultipartFile
|
||||||
|
|||||||
@ -113,4 +113,9 @@ public class BusAttendance extends BaseEntity {
|
|||||||
* 代打卡人员Id
|
* 代打卡人员Id
|
||||||
*/
|
*/
|
||||||
private Long replaceId;
|
private Long replaceId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 来源
|
||||||
|
*/
|
||||||
|
private String source;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -47,4 +47,10 @@ public class BusAttendancePunchCardByFaceReq implements Serializable {
|
|||||||
*/
|
*/
|
||||||
private LocalDateTime punchTime;
|
private LocalDateTime punchTime;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 来源 (0-app,1-考勤机 )
|
||||||
|
*/
|
||||||
|
private String source;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -391,63 +391,105 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
|
|||||||
List<BusAttendance> outAttendances = attendances.stream().filter(attendance ->
|
List<BusAttendance> outAttendances = attendances.stream().filter(attendance ->
|
||||||
BusAttendanceCommuterEnum.CLOCKOUT.getValue().equals(attendance.getClockType())).toList();
|
BusAttendanceCommuterEnum.CLOCKOUT.getValue().equals(attendance.getClockType())).toList();
|
||||||
|
|
||||||
if (clockTypeByTime == 1 && CollectionUtils.isEmpty(inAttendances)) {
|
if (clockTypeByTime == 1) {
|
||||||
BusAttendance attendance = new BusAttendance();
|
if(CollectionUtils.isEmpty(inAttendances)){
|
||||||
// 上班打卡
|
BusAttendance attendance = new BusAttendance();
|
||||||
attendance.setClockType(BusAttendanceCommuterEnum.CLOCKIN.getValue());
|
// 上班打卡
|
||||||
//打卡时间
|
attendance.setClockType(BusAttendanceCommuterEnum.CLOCKIN.getValue());
|
||||||
attendance.setRuleTime(busAttendanceRuleVo.getClockInTime());
|
//打卡时间
|
||||||
// 判断是否为迟到
|
attendance.setRuleTime(busAttendanceRuleVo.getClockInTime());
|
||||||
if (isLate(now, busAttendanceRuleVo)) {
|
// 判断是否为迟到
|
||||||
attendance.setClockStatus(BusAttendanceClockStatusEnum.LATE.getValue());
|
if (isLate(now, busAttendanceRuleVo)) {
|
||||||
attendance.setMinuteCount(getMinutesDifference(now, busAttendanceRuleVo.getClockInTime()));
|
attendance.setClockStatus(BusAttendanceClockStatusEnum.LATE.getValue());
|
||||||
} else {
|
attendance.setMinuteCount(getMinutesDifference(now, busAttendanceRuleVo.getClockInTime()));
|
||||||
attendance.setClockStatus(BusAttendanceClockStatusEnum.NORMAL.getValue());
|
} else {
|
||||||
}
|
attendance.setClockStatus(BusAttendanceClockStatusEnum.NORMAL.getValue());
|
||||||
//只要请假,直接归为请假
|
|
||||||
LocalDateTime localDateTime = localDate.atTime(busAttendanceRuleVo.getClockInTime());
|
|
||||||
if (leaveService.isLeave(localDateTime, userId)) {
|
|
||||||
attendance.setClockStatus(BusAttendanceClockStatusEnum.LEAVE.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 填充信息
|
|
||||||
attendance.setUserId(userId);
|
|
||||||
attendance.setProjectId(req.getProjectId());
|
|
||||||
attendance.setClockDate(localDate);
|
|
||||||
attendance.setClockTime(now);
|
|
||||||
attendance.setUserName(constructionUser.getUserName());
|
|
||||||
attendance.setReplaceId(replaceId);
|
|
||||||
// 记录打卡坐标
|
|
||||||
attendance.setLat(req.getLat());
|
|
||||||
attendance.setLng(req.getLng());
|
|
||||||
try {
|
|
||||||
attendance.setClockLocation(JSTUtil.getLocationName(req.getLat(), req.getLng()));
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("获取打卡位置失败", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 上传人脸照
|
|
||||||
SysOssVo upload = ossService.upload(file);
|
|
||||||
attendance.setFacePic(upload.getOssId().toString());
|
|
||||||
Long finalUserId = userId;
|
|
||||||
CompletableFuture.runAsync(() -> {
|
|
||||||
try {
|
|
||||||
chatServerHandler.sendSystemMessageToUser(finalUserId, "打卡成功", "1");
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("异步发送系统消息失败,用户ID: {}, 消息: {}", finalUserId, "打卡成功", e);
|
|
||||||
}
|
}
|
||||||
});
|
//只要请假,直接归为请假
|
||||||
//计算工资
|
LocalDateTime localDateTime = localDate.atTime(busAttendanceRuleVo.getClockInTime());
|
||||||
attendance.setSalary(computeSalary(constructionUser, inAttendances));
|
if (leaveService.isLeave(localDateTime, userId)) {
|
||||||
return this.save(attendance);
|
attendance.setClockStatus(BusAttendanceClockStatusEnum.LEAVE.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 填充信息
|
||||||
|
attendance.setUserId(userId);
|
||||||
|
attendance.setProjectId(req.getProjectId());
|
||||||
|
attendance.setClockDate(localDate);
|
||||||
|
attendance.setClockTime(now);
|
||||||
|
attendance.setUserName(constructionUser.getUserName());
|
||||||
|
attendance.setReplaceId(replaceId);
|
||||||
|
if(req.getSource() != null){
|
||||||
|
attendance.setSource(req.getSource());
|
||||||
|
}
|
||||||
|
// 记录打卡坐标
|
||||||
|
attendance.setLat(req.getLat());
|
||||||
|
attendance.setLng(req.getLng());
|
||||||
|
try {
|
||||||
|
attendance.setClockLocation(JSTUtil.getLocationName(req.getLat(), req.getLng()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取打卡位置失败", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传人脸照
|
||||||
|
SysOssVo upload = ossService.upload(file);
|
||||||
|
attendance.setFacePic(upload.getOssId().toString());
|
||||||
|
Long finalUserId = userId;
|
||||||
|
CompletableFuture.runAsync(() -> {
|
||||||
|
try {
|
||||||
|
chatServerHandler.sendSystemMessageToUser(finalUserId, "打卡成功", "1");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("异步发送系统消息失败,用户ID: {}, 消息: {}", finalUserId, "打卡成功", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//计算工资
|
||||||
|
attendance.setSalary(computeSalary(constructionUser, inAttendances));
|
||||||
|
return this.save(attendance);
|
||||||
|
}
|
||||||
|
//考勤机打卡会有历史记录,需要更新状态
|
||||||
|
if(CollectionUtil.isNotEmpty(outAttendances) && "1".equals(req.getSource())){
|
||||||
|
BusAttendance busAttendance = outAttendances.getFirst();
|
||||||
|
String oldStatus = busAttendance.getClockStatus();
|
||||||
|
//更新打卡时间
|
||||||
|
busAttendance.setClockTime(now);
|
||||||
|
// 判断是否为迟到
|
||||||
|
if (isLate(now, busAttendanceRuleVo)) {
|
||||||
|
busAttendance.setClockStatus(BusAttendanceClockStatusEnum.LATE.getValue());
|
||||||
|
busAttendance.setMinuteCount(getMinutesDifference(now, busAttendanceRuleVo.getClockInTime()));
|
||||||
|
} else {
|
||||||
|
busAttendance.setClockStatus(BusAttendanceClockStatusEnum.NORMAL.getValue());
|
||||||
|
}
|
||||||
|
//只要请假,直接归为请假
|
||||||
|
LocalDateTime localDateTime = localDate.atTime(busAttendanceRuleVo.getClockInTime());
|
||||||
|
if (leaveService.isLeave(localDateTime, userId)) {
|
||||||
|
busAttendance.setClockStatus(BusAttendanceClockStatusEnum.LEAVE.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
busAttendance.setSource(req.getSource());
|
||||||
|
//如果是缺卡需要上传人脸
|
||||||
|
if(oldStatus.equals(BusAttendanceClockStatusEnum.UNCLOCK.getValue())){
|
||||||
|
SysOssVo upload = ossService.upload(file);
|
||||||
|
busAttendance.setFacePic(upload.getOssId().toString());
|
||||||
|
}
|
||||||
|
updateById(busAttendance);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} else if (clockTypeByTime == 2 || CollectionUtils.isNotEmpty(inAttendances)) {
|
} else if (clockTypeByTime == 2 || CollectionUtils.isNotEmpty(inAttendances)) {
|
||||||
|
|
||||||
if (CollectionUtil.isNotEmpty(outAttendances)) {
|
if (CollectionUtil.isNotEmpty(outAttendances)) {
|
||||||
BusAttendance busAttendance = outAttendances.getFirst();
|
BusAttendance busAttendance = outAttendances.getFirst();
|
||||||
if (busAttendance.getClockStatus().equals(BusAttendanceClockStatusEnum.UNCLOCK.getValue())) {
|
if("1".equals(req.getSource())){
|
||||||
throw new ServiceException("下班缺卡记录已生成,不能更新");
|
busAttendance.setSource(req.getSource());
|
||||||
|
if(busAttendance.getClockStatus().equals(BusAttendanceClockStatusEnum.UNCLOCK.getValue())){
|
||||||
|
SysOssVo upload = ossService.upload(file);
|
||||||
|
busAttendance.setFacePic(upload.getOssId().toString());
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
if (busAttendance.getClockStatus().equals(BusAttendanceClockStatusEnum.UNCLOCK.getValue())) {
|
||||||
|
throw new ServiceException("下班缺卡记录已生成,不能更新");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//更新打卡时间
|
//更新打卡时间
|
||||||
busAttendance.setClockTime(now);
|
busAttendance.setClockTime(now);
|
||||||
// 判断是否为早退
|
// 判断是否为早退
|
||||||
@ -488,6 +530,9 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
|
|||||||
attendance.setClockTime(now);
|
attendance.setClockTime(now);
|
||||||
attendance.setUserName(constructionUser.getUserName());
|
attendance.setUserName(constructionUser.getUserName());
|
||||||
attendance.setReplaceId(replaceId);
|
attendance.setReplaceId(replaceId);
|
||||||
|
if(req.getSource() != null){
|
||||||
|
attendance.setSource(req.getSource());
|
||||||
|
}
|
||||||
// 记录打卡坐标
|
// 记录打卡坐标
|
||||||
attendance.setLat(req.getLat());
|
attendance.setLat(req.getLat());
|
||||||
attendance.setLng(req.getLng());
|
attendance.setLng(req.getLng());
|
||||||
@ -1109,7 +1154,9 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
|
|||||||
// 过滤有效考勤记录并按日期分组
|
// 过滤有效考勤记录并按日期分组
|
||||||
Map<LocalDate, List<BusAttendanceVo>> dateAttendanceMap = attendanceList.stream()
|
Map<LocalDate, List<BusAttendanceVo>> dateAttendanceMap = attendanceList.stream()
|
||||||
.filter(a -> validStatusList.contains(a.getClockStatus()))
|
.filter(a -> validStatusList.contains(a.getClockStatus()))
|
||||||
.collect(Collectors.groupingBy(BusAttendanceVo::getClockDate));
|
.collect(Collectors.groupingBy(BusAttendanceVo::getClockDate,
|
||||||
|
LinkedHashMap::new,
|
||||||
|
Collectors.toList()));
|
||||||
List<AttendanceUserDataDetailVo> workList = new ArrayList<>();
|
List<AttendanceUserDataDetailVo> workList = new ArrayList<>();
|
||||||
for (Map.Entry<LocalDate, List<BusAttendanceVo>> entry : dateAttendanceMap.entrySet()) {
|
for (Map.Entry<LocalDate, List<BusAttendanceVo>> entry : dateAttendanceMap.entrySet()) {
|
||||||
LocalDate key = entry.getKey();
|
LocalDate key = entry.getKey();
|
||||||
|
|||||||
Reference in New Issue
Block a user