feat(REQ-3714) 工人退场流程优化 - 考勤记录事件MQ处理
This commit is contained in:
parent
83658d7edf
commit
28f6b7736c
@ -1,6 +1,11 @@
|
||||
package cn.axzo.orgmanax.infra.event.config;
|
||||
|
||||
import cn.axzo.foundation.event.support.consumer.*;
|
||||
import cn.axzo.foundation.event.support.consumer.DefaultEventConsumer;
|
||||
import cn.axzo.foundation.event.support.consumer.DefaultRocketMQListener;
|
||||
import cn.axzo.foundation.event.support.consumer.EventConsumer;
|
||||
import cn.axzo.foundation.event.support.consumer.EventHandlerRepository;
|
||||
import cn.axzo.foundation.event.support.consumer.RetryableEventConsumer;
|
||||
import cn.axzo.foundation.event.support.consumer.RocketRetryableEventConsumer;
|
||||
import cn.axzo.foundation.event.support.producer.EventProducer;
|
||||
import cn.axzo.foundation.event.support.producer.RocketMQEventProducer;
|
||||
import cn.axzo.foundation.web.support.AppRuntime;
|
||||
@ -72,6 +77,17 @@ public class RocketMQEventConfig {
|
||||
}
|
||||
}
|
||||
|
||||
@NonLocalCondition.Conditional
|
||||
@Component
|
||||
@RocketMQMessageListener(topic = "topic_attendance_common_${spring.profiles.active}",
|
||||
consumerGroup = "GID_${spring.application.name}_attendance_common_${spring.profiles.active}",
|
||||
consumeMode = ConsumeMode.ORDERLY, maxReconsumeTimes = 20)
|
||||
public static class AttendanceListener extends DefaultRocketMQListener {
|
||||
public AttendanceListener(EventConsumer eventConsumer) {
|
||||
super(eventConsumer);
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
EventHandlerRepository eventHandlerRepository() {
|
||||
return EventHandlerRepository.builder()
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
package cn.axzo.orgmanax.server.orguser.event.outer;
|
||||
|
||||
import cn.axzo.foundation.event.support.Event;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author luofu
|
||||
* @version 1.0
|
||||
* @description 考勤事件枚举
|
||||
* @date 2025/3/5
|
||||
*/
|
||||
@Getter
|
||||
public enum AttendanceEventEnum {
|
||||
|
||||
ATTENDANCE_RECORD("ATTENDANCE", "attendance_record", "发送新增考勤记录");
|
||||
|
||||
private final Event.EventCode eventCode;
|
||||
private final String desc;
|
||||
|
||||
AttendanceEventEnum(String module, String tag, String desc) {
|
||||
this.eventCode = Event.EventCode.builder().module(module).name(tag).build();
|
||||
this.desc = desc;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,103 @@
|
||||
package cn.axzo.orgmanax.server.orguser.event.outer.handler;
|
||||
|
||||
import cn.axzo.foundation.event.support.Event;
|
||||
import cn.axzo.foundation.event.support.EventHandler;
|
||||
import cn.axzo.foundation.event.support.consumer.EventConsumer;
|
||||
import cn.axzo.orgmanax.dto.common.util.NumberUtil;
|
||||
import cn.axzo.orgmanax.server.orguser.event.outer.AttendanceEventEnum;
|
||||
import cn.axzo.orgmanax.server.orguser.event.outer.payload.AttendanceRecordPayload;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author luofu
|
||||
* @version 1.0
|
||||
* @description 考勤新增记录事件处理器
|
||||
* @date 2025/3/5
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class AttendanceRecordEventHandler implements EventHandler, InitializingBean {
|
||||
|
||||
private static final String HANDLER_ALIAS = "ATTENDANCE_RECORD_EVENT_HANDLER";
|
||||
private static final String LOG_PREFIX = "[" + HANDLER_ALIAS + "] ";
|
||||
|
||||
private static final ImmutableList<Integer> EXPECTED_CLOCK_TYPES = ImmutableList.of(
|
||||
AttendanceRecordPayload.CLOCK_TYPE_ENTRY,
|
||||
AttendanceRecordPayload.CLOCK_TYPE_EXIT
|
||||
);
|
||||
|
||||
private final EventConsumer eventConsumer;
|
||||
|
||||
@Override
|
||||
public void onEvent(Event event, EventConsumer.Context context) {
|
||||
info("received the event. msgId:{}", context.getMsgId());
|
||||
AttendanceRecordPayload payload = parsePayload(event);
|
||||
if (Objects.isNull(payload)) {
|
||||
info("the event data is invalid. msgId:{}", context.getMsgId());
|
||||
return;
|
||||
}
|
||||
if (invalidPayload(payload)) {
|
||||
error("invalid payload. msgId:{}, data:{}", context.getMsgId(), payload);
|
||||
return;
|
||||
}
|
||||
if (unexpectedPayload(payload)) {
|
||||
info("unexpected payload. msgId:{}, data:{}", context.getMsgId(), payload);
|
||||
return;
|
||||
}
|
||||
info("started to handle the event. msgId:{}", context.getMsgId());
|
||||
handle(payload, context.getMsgId());
|
||||
info("completed to handle the event. msgId:{}", context.getMsgId());
|
||||
}
|
||||
|
||||
private AttendanceRecordPayload parsePayload(Event event) {
|
||||
try {
|
||||
return event.normalizedData(AttendanceRecordPayload.class);
|
||||
} catch (Exception e) {
|
||||
error("broke out some exception while parsing payload.", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean invalidPayload(AttendanceRecordPayload payload) {
|
||||
return NumberUtil.isNotPositiveNumber(payload.getPersonId())
|
||||
|| NumberUtil.isNotPositiveNumber(payload.getOuId())
|
||||
|| NumberUtil.isNotPositiveNumber(payload.getWorkspaceId())
|
||||
|| Objects.isNull(payload.getUserType())
|
||||
|| Objects.isNull(payload.getClockType())
|
||||
|| Objects.isNull(payload.getClockAt())
|
||||
// 非从业人员,需要通过teamId去获取其真实的单位id
|
||||
|| (payload.isNotPractitioner() && NumberUtil.isNotPositiveNumber(payload.getTeamId()));
|
||||
}
|
||||
|
||||
private boolean unexpectedPayload(AttendanceRecordPayload payload) {
|
||||
return EXPECTED_CLOCK_TYPES.stream().noneMatch(e -> e.equals(payload.getClockType()));
|
||||
}
|
||||
|
||||
private void handle(AttendanceRecordPayload payload, String msgId) {
|
||||
// FIXME: cold_blade 暂时废弃通过接MQ来实现标签取消的方案,改用XXL-JOB方式实现
|
||||
// 缘由:考勤MQ的消息集中在上下班的高峰,且有暂离标签的用户是少数,因此大多数的MQ消息都是空转
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
eventConsumer.registerHandler(AttendanceEventEnum.ATTENDANCE_RECORD.getEventCode(), this, HANDLER_ALIAS);
|
||||
}
|
||||
|
||||
private static void info(String msgFormat, Object... args) {
|
||||
msgFormat = LOG_PREFIX + msgFormat;
|
||||
log.info(msgFormat, args);
|
||||
}
|
||||
|
||||
private static void error(String msgFormat, Object... args) {
|
||||
msgFormat = LOG_PREFIX + msgFormat;
|
||||
// FIXME: cold_blade 基于MQ方案暂时不考虑的前提,此处就不用告警出来了
|
||||
log.warn(msgFormat, args);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,206 @@
|
||||
package cn.axzo.orgmanax.server.orguser.event.outer.payload;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author luofu
|
||||
* @version 1.0
|
||||
* @description 新增考勤记录事件数据
|
||||
* @date 2025/3/5
|
||||
*/
|
||||
@Data
|
||||
public class AttendanceRecordPayload implements Serializable {
|
||||
|
||||
public static final int CLOCK_TYPE_ENTRY = 1;
|
||||
public static final int CLOCK_TYPE_EXIT = 2;
|
||||
|
||||
private static final int USER_TYPE_PRACTITIONER = 2;
|
||||
|
||||
/**
|
||||
* 考勤记录主键
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 分包机构Id
|
||||
*/
|
||||
private Long ouId;
|
||||
|
||||
/**
|
||||
* 分包机构名称
|
||||
*/
|
||||
private String ouName;
|
||||
/**
|
||||
* 项目部id
|
||||
*/
|
||||
private Long workspaceId;
|
||||
/**
|
||||
* 项目部名称
|
||||
*/
|
||||
private String workspaceName;
|
||||
/**
|
||||
* 考勤归属项目json数组,可以为null
|
||||
*/
|
||||
private Set<Long> projectIds;
|
||||
/**
|
||||
* 项目内班组id
|
||||
*/
|
||||
private Long teamId;
|
||||
/**
|
||||
* 小组Id
|
||||
*/
|
||||
private Long groupId;
|
||||
/**
|
||||
* 用户类型:用户类型:
|
||||
* 1工人,
|
||||
* 2从业人员,
|
||||
* 3突击工
|
||||
* 4班组长
|
||||
*/
|
||||
private Integer userType;
|
||||
/**
|
||||
* 用户identityId
|
||||
*/
|
||||
private Long userId;
|
||||
/**
|
||||
* 用户名称
|
||||
*/
|
||||
private String userName;
|
||||
/**
|
||||
* 用户手机号
|
||||
*/
|
||||
private String userPhone;
|
||||
/**
|
||||
* 用户身份证号
|
||||
*/
|
||||
private String userIdCard;
|
||||
/**
|
||||
* 性别:0-未知 1-男 2-女
|
||||
*/
|
||||
private Integer userSex;
|
||||
/**
|
||||
* 班组名称
|
||||
*/
|
||||
private String teamName;
|
||||
/**
|
||||
* 工种id
|
||||
*/
|
||||
private String professionId;
|
||||
/**
|
||||
* 工种名称
|
||||
*/
|
||||
private String professionName;
|
||||
/**
|
||||
* 岗位名称
|
||||
*/
|
||||
private String jobName;
|
||||
/**
|
||||
* 岗位标签码
|
||||
*/
|
||||
private String jobCode;
|
||||
/**
|
||||
* personId
|
||||
*/
|
||||
private Long personId;
|
||||
/**
|
||||
* 本次打卡归属日期(为满足跨夜考勤)
|
||||
*/
|
||||
private Date dataDate;
|
||||
/**
|
||||
* 打卡时间
|
||||
*/
|
||||
private Date clockAt;
|
||||
/**
|
||||
* 设备名称
|
||||
*/
|
||||
private String deviceSn;
|
||||
|
||||
/**
|
||||
* 设备序列号
|
||||
*/
|
||||
private String deviceName;
|
||||
/**
|
||||
* 打卡类型:1进场,2出场,3抓拍
|
||||
*/
|
||||
private Integer clockType;
|
||||
/**
|
||||
* 打卡方式:0-闸机 1-电子围栏 2-考勤补卡
|
||||
*/
|
||||
private Integer clockMethod;
|
||||
/**
|
||||
* 考勤时的抓拍图片 可能为空未获取到
|
||||
*/
|
||||
private String capturePhoto;
|
||||
|
||||
/**
|
||||
* 考勤时的抓拍图片key 可能为空未获取到
|
||||
*/
|
||||
private String capturePhotoKey;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createAt;
|
||||
/**
|
||||
* 修改时间
|
||||
*/
|
||||
private Date updateAt;
|
||||
/**
|
||||
* 打卡地址
|
||||
*/
|
||||
private String address;
|
||||
/**
|
||||
* 打卡经度
|
||||
*/
|
||||
private BigDecimal longitude;
|
||||
/**
|
||||
* 打卡纬度
|
||||
*/
|
||||
private BigDecimal latitude;
|
||||
|
||||
/**
|
||||
* 企业id
|
||||
*/
|
||||
private Long entId;
|
||||
|
||||
/**
|
||||
* 企业名称
|
||||
*/
|
||||
private String entName;
|
||||
|
||||
/**
|
||||
* 小组名称
|
||||
*/
|
||||
private String groupName;
|
||||
|
||||
/**
|
||||
* 小组类型 0-直属小组 1-合作小组
|
||||
*/
|
||||
private Integer groupType;
|
||||
|
||||
/**
|
||||
* 0:内部部门 1:总包部门 2:建设单位 3:监理单位 4:劳务分包 5:专业分包
|
||||
*/
|
||||
private Integer ouType;
|
||||
|
||||
/**
|
||||
* 设备位置信息
|
||||
*/
|
||||
private String deviceAddress;
|
||||
|
||||
private String dataSource;
|
||||
|
||||
public boolean isNotPractitioner() {
|
||||
return !Objects.equals(USER_TYPE_PRACTITIONER, userType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user