mirror of https://gitee.com/topiam/eiam
Merge remote-tracking branch 'origin/master'
commit
d2aa510913
|
@ -1,34 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
eiam-alert - Employee Identity and Access Management
|
||||
Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>cn.topiam</groupId>
|
||||
<artifactId>eiam</artifactId>
|
||||
<version>1.0.2-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>eiam-alert</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
</project>
|
|
@ -23,12 +23,15 @@ import java.io.Serializable;
|
|||
import org.springframework.data.elasticsearch.annotations.Field;
|
||||
import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||
|
||||
import cn.topiam.employee.support.security.userdetails.UserType;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
|
||||
/**
|
||||
* Actor
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2022/11/5 23:30
|
||||
*/
|
||||
|
@ -56,7 +59,7 @@ public class Actor implements Serializable {
|
|||
*/
|
||||
@NonNull
|
||||
@Field(type = FieldType.Keyword, name = "type")
|
||||
private cn.topiam.employee.support.security.userdetails.UserType type;
|
||||
private UserType type;
|
||||
|
||||
/**
|
||||
* 身份验证类型
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
/*
|
||||
* eiam-audit - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.audit.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.Field;
|
||||
import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||
import org.springframework.data.elasticsearch.annotations.Setting;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 审计elasticsearch实体
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2022/10/13 23:22
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@Document(indexName = "#{@auditDynamicIndexName.getIndexName()}")
|
||||
@Setting(replicas = 0)
|
||||
public class AuditElasticSearchEntity implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 6589338521638519634L;
|
||||
|
||||
@Id
|
||||
@Field(type = FieldType.Keyword, name = "id")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* Request Id
|
||||
*/
|
||||
@Field(type = FieldType.Keyword, name = "request_id")
|
||||
private String requestId;
|
||||
|
||||
/**
|
||||
* Session Id
|
||||
*/
|
||||
@Field(type = FieldType.Keyword, name = "session_id")
|
||||
private String sessionId;
|
||||
|
||||
/**
|
||||
* 操作者
|
||||
*/
|
||||
@Field(type = FieldType.Object, name = "actor")
|
||||
private Actor actor;
|
||||
|
||||
/**
|
||||
* 事件
|
||||
*/
|
||||
@Field(type = FieldType.Object, name = "event")
|
||||
private Event event;
|
||||
|
||||
/**
|
||||
* 操作目标
|
||||
*/
|
||||
@Field(type = FieldType.Object, name = "target")
|
||||
private List<Target> targets;
|
||||
|
||||
/**
|
||||
* UserAgent
|
||||
*/
|
||||
@Field(type = FieldType.Object, name = "user_agent")
|
||||
private UserAgent userAgent;
|
||||
|
||||
/**
|
||||
* 地理位置
|
||||
*/
|
||||
@Field(type = FieldType.Object, name = "geo_location")
|
||||
private GeoLocation geoLocation;
|
||||
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* eiam-audit - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.audit.event;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
|
||||
import cn.topiam.employee.audit.entity.*;
|
||||
import cn.topiam.employee.audit.repository.AuditRepository;
|
||||
|
||||
/**
|
||||
* 事件监听
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2021/9/12 22:49
|
||||
*/
|
||||
@Component
|
||||
@Async
|
||||
public class AuditEventListener implements ApplicationListener<AuditEvent> {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(AuditEventListener.class);
|
||||
|
||||
/**
|
||||
* onApplicationEvent
|
||||
*
|
||||
* @param auditEvent {@link AuditEvent}
|
||||
*/
|
||||
@Override
|
||||
public void onApplicationEvent(@NonNull AuditEvent auditEvent) {
|
||||
Event event = auditEvent.getEvent();
|
||||
Actor actor = auditEvent.getActor();
|
||||
List<Target> target = auditEvent.getTargets();
|
||||
GeoLocation geoLocation = auditEvent.getGeoLocation();
|
||||
UserAgent userAgent = auditEvent.getUserAgent();
|
||||
//保存数据库
|
||||
AuditEntity entity = new AuditEntity();
|
||||
try {
|
||||
entity.setRequestId(auditEvent.getRequestId());
|
||||
entity.setSessionId(auditEvent.getSessionId());
|
||||
//事件
|
||||
entity.setEventType(event.getType());
|
||||
entity.setEventContent(event.getContent());
|
||||
entity.setEventParam(event.getParam());
|
||||
entity.setEventStatus(event.getStatus());
|
||||
entity.setEventResult(event.getResult());
|
||||
entity.setEventTime(event.getTime());
|
||||
//操作目标
|
||||
entity.setTargets(target);
|
||||
entity.setGeoLocation(geoLocation);
|
||||
entity.setUserAgent(userAgent);
|
||||
entity.setActorId(actor.getId());
|
||||
entity.setActorType(actor.getType());
|
||||
entity.setActorAuthType(actor.getAuthType());
|
||||
auditRepository.save(entity);
|
||||
} catch (Exception e) {
|
||||
logger.error("Audit record saving failed: {}", JSONObject.toJSONString(entity), e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* AuditRepository
|
||||
*/
|
||||
private final AuditRepository auditRepository;
|
||||
|
||||
public AuditEventListener(AuditRepository auditRepository) {
|
||||
this.auditRepository = auditRepository;
|
||||
}
|
||||
|
||||
}
|
|
@ -23,8 +23,7 @@ import java.util.Map;
|
|||
import java.util.Objects;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
|
@ -37,7 +36,6 @@ import com.google.common.collect.Maps;
|
|||
import cn.topiam.employee.audit.entity.*;
|
||||
import cn.topiam.employee.audit.enums.EventStatus;
|
||||
import cn.topiam.employee.audit.event.type.EventType;
|
||||
import cn.topiam.employee.audit.mq.AuditMessagePublisher;
|
||||
import cn.topiam.employee.support.context.ServletContextHelp;
|
||||
import cn.topiam.employee.support.geo.GeoLocationService;
|
||||
import cn.topiam.employee.support.security.authentication.WebAuthenticationDetails;
|
||||
|
@ -47,8 +45,6 @@ import cn.topiam.employee.support.trace.TraceUtils;
|
|||
import cn.topiam.employee.support.util.IpUtils;
|
||||
import cn.topiam.employee.support.web.useragent.UserAgentParser;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import static cn.topiam.employee.support.util.StringUtils.replaceBlank;
|
||||
|
||||
|
@ -59,11 +55,8 @@ import static cn.topiam.employee.support.util.StringUtils.replaceBlank;
|
|||
* Created by support@topiam.cn on 2021/8/1 21:04
|
||||
*/
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class AuditEventPublish {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(AuditEventPublish.class);
|
||||
|
||||
/**
|
||||
* 发布 审计事件
|
||||
*
|
||||
|
@ -84,7 +77,7 @@ public class AuditEventPublish {
|
|||
//封装操作人
|
||||
Actor actor = getActor();
|
||||
//Publish AuditEvent
|
||||
auditMessagePublisher.sendAuditChangeMessage(new AuditEvent(TraceUtils.get(), ServletContextHelp.getSession().getId(), actor, event, userAgent, geoLocationModal, null));
|
||||
applicationEventPublisher.publishEvent(new AuditEvent(TraceUtils.get(), ServletContextHelp.getSession().getId(), actor, event, userAgent, geoLocationModal, null));
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
|
@ -115,7 +108,7 @@ public class AuditEventPublish {
|
|||
//封装操作人
|
||||
Actor actor = getActor(authentication);
|
||||
//Publish AuditEvent
|
||||
auditMessagePublisher.sendAuditChangeMessage(new AuditEvent(TraceUtils.get(), ServletContextHelp.getSession().getId(), actor, event, userAgent, geoLocationModal, targets));
|
||||
applicationEventPublisher.publishEvent(new AuditEvent(TraceUtils.get(), ServletContextHelp.getSession().getId(), actor, event, userAgent, geoLocationModal, targets));
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
|
@ -137,7 +130,7 @@ public class AuditEventPublish {
|
|||
//封装用户代理
|
||||
UserAgent userAgent = getUserAgent();
|
||||
//Publish AuditEvent
|
||||
auditMessagePublisher.sendAuditChangeMessage(new AuditEvent(TraceUtils.get(), ServletContextHelp.getSession().getId(), actor, event, userAgent, geoLocationModal, null));
|
||||
applicationEventPublisher.publishEvent(new AuditEvent(TraceUtils.get(), ServletContextHelp.getSession().getId(), actor, event, userAgent, geoLocationModal, null));
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
|
@ -178,7 +171,7 @@ public class AuditEventPublish {
|
|||
actor = getActor();
|
||||
}
|
||||
//Publish AuditEvent
|
||||
auditMessagePublisher.sendAuditChangeMessage(new AuditEvent(TraceUtils.get(), ServletContextHelp.getSession().getId(), actor, event, userAgent, geoLocationModal, target));
|
||||
applicationEventPublisher.publishEvent(new AuditEvent(TraceUtils.get(), ServletContextHelp.getSession().getId(), actor, event, userAgent, geoLocationModal, target));
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
|
@ -204,7 +197,7 @@ public class AuditEventPublish {
|
|||
//封装操作人
|
||||
Actor actor = getActor();
|
||||
//Publish AuditEvent
|
||||
auditMessagePublisher.sendAuditChangeMessage(new AuditEvent(TraceUtils.get(), ServletContextHelp.getSession().getId(), actor, event, userAgent, geoLocationModal, target));
|
||||
applicationEventPublisher.publishEvent(new AuditEvent(TraceUtils.get(), ServletContextHelp.getSession().getId(), actor, event, userAgent, geoLocationModal, target));
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
|
@ -249,6 +242,9 @@ public class AuditEventPublish {
|
|||
if (principal instanceof UserDetails) {
|
||||
return ((UserDetails) principal).getId();
|
||||
}
|
||||
if (principal instanceof String) {
|
||||
return (String) principal;
|
||||
}
|
||||
return null;
|
||||
//@formatter:on
|
||||
}
|
||||
|
@ -384,13 +380,18 @@ public class AuditEventPublish {
|
|||
}
|
||||
|
||||
/**
|
||||
* AuditMessagePublisher
|
||||
* ApplicationEventPublisher
|
||||
*/
|
||||
private final AuditMessagePublisher auditMessagePublisher;
|
||||
private final ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
/**
|
||||
* 地理位置
|
||||
*/
|
||||
private final GeoLocationService geoLocationService;
|
||||
|
||||
public AuditEventPublish(ApplicationEventPublisher applicationEventPublisher,
|
||||
GeoLocationService geoLocationService) {
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
this.geoLocationService = geoLocationService;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,6 +78,13 @@ public class AccountEventType {
|
|||
public static Type MOVE_ORGANIZATION = new Type(
|
||||
"eiam:event:account:move_organization", "移动组织", ORG_ACCOUNT_RESOURCE, List.of(ADMIN));
|
||||
|
||||
/**
|
||||
* 添加组织用户
|
||||
*/
|
||||
public static Type CREATE_ORGANIZATION_MEMBER = new Type(
|
||||
"eiam:event:account:create_organization_member", "添加组织用户", ORG_ACCOUNT_RESOURCE,
|
||||
List.of(ADMIN));
|
||||
|
||||
/**
|
||||
* 用户离职
|
||||
*/
|
||||
|
|
|
@ -72,6 +72,11 @@ public enum EventType {
|
|||
*/
|
||||
MOVE_ORGANIZATION(AccountEventType.MOVE_ORGANIZATION),
|
||||
|
||||
/**
|
||||
* 添加组织用户
|
||||
*/
|
||||
CREATE_ORGANIZATION_MEMBER(AccountEventType.CREATE_ORGANIZATION_MEMBER),
|
||||
|
||||
/**
|
||||
* 创建用户
|
||||
*/
|
||||
|
|
|
@ -1,137 +0,0 @@
|
|||
/*
|
||||
* eiam-audit - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.audit.mq;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.messaging.handler.annotation.Headers;
|
||||
import org.springframework.messaging.handler.annotation.Payload;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.rabbitmq.client.Channel;
|
||||
|
||||
import cn.topiam.employee.audit.entity.*;
|
||||
import cn.topiam.employee.audit.event.AuditEvent;
|
||||
import cn.topiam.employee.audit.repository.AuditElasticSearchRepository;
|
||||
import cn.topiam.employee.audit.repository.AuditRepository;
|
||||
import cn.topiam.employee.support.exception.TopIamException;
|
||||
import cn.topiam.employee.support.trace.TraceUtils;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import static cn.topiam.employee.audit.mq.AuditMessagePublisher.AUDIT_TOPIC;
|
||||
import static cn.topiam.employee.support.trace.TraceAspect.TRACE_ID;
|
||||
|
||||
/**
|
||||
* 审计消息监听器
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2023/5/30 23:12
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class AuditMessageListener {
|
||||
|
||||
/**
|
||||
* 接收用户消息
|
||||
*
|
||||
* @param message {@link Message}
|
||||
* @param channel {@link Channel}
|
||||
*/
|
||||
@SneakyThrows
|
||||
@RabbitListener(queues = AUDIT_TOPIC, ackMode = "MANUAL")
|
||||
@RabbitHandler()
|
||||
public String onMessage(Message message, Channel channel, @Payload AuditEvent auditEvent,
|
||||
@Headers Map<String, Object> headers) throws TopIamException {
|
||||
try {
|
||||
//设置TraceId
|
||||
TraceUtils.put(String.valueOf(headers.get(TRACE_ID)));
|
||||
log.info("接收审计事件入参: [{}]", message);
|
||||
Event event = auditEvent.getEvent();
|
||||
Actor actor = auditEvent.getActor();
|
||||
List<Target> target = auditEvent.getTargets();
|
||||
GeoLocation geoLocation = auditEvent.getGeoLocation();
|
||||
UserAgent userAgent = auditEvent.getUserAgent();
|
||||
//保存数据库
|
||||
AuditEntity entity = new AuditEntity();
|
||||
Optional<AuditEntity> optional = auditRepository
|
||||
.findByRequestId(auditEvent.getRequestId());
|
||||
if (optional.isEmpty()) {
|
||||
entity.setRequestId(auditEvent.getRequestId());
|
||||
entity.setSessionId(auditEvent.getSessionId());
|
||||
//事件
|
||||
entity.setEventType(event.getType());
|
||||
entity.setEventContent(event.getContent());
|
||||
entity.setEventParam(event.getParam());
|
||||
entity.setEventStatus(event.getStatus());
|
||||
entity.setEventResult(event.getResult());
|
||||
entity.setEventTime(event.getTime());
|
||||
//操作目标
|
||||
entity.setTargets(target);
|
||||
entity.setGeoLocation(geoLocation);
|
||||
entity.setUserAgent(userAgent);
|
||||
entity.setActorId(actor.getId());
|
||||
entity.setActorType(actor.getType());
|
||||
auditRepository.save(entity);
|
||||
} else {
|
||||
entity = optional.get();
|
||||
}
|
||||
|
||||
if (!Objects.isNull(entity.getId())) {
|
||||
//保存 Elasticsearch
|
||||
AuditElasticSearchEntity audit = AuditElasticSearchEntity.builder().build();
|
||||
audit.setRequestId(auditEvent.getRequestId());
|
||||
audit.setSessionId(auditEvent.getSessionId());
|
||||
audit.setId(entity.getId().toString());
|
||||
audit.setEvent(event);
|
||||
audit.setTargets(target);
|
||||
audit.setGeoLocation(geoLocation);
|
||||
audit.setUserAgent(userAgent);
|
||||
audit.setActor(actor);
|
||||
auditElasticSearchRepository.save(audit);
|
||||
}
|
||||
log.info("处理审计事件成功:[{}]", message.getMessageProperties().getDeliveryTag());
|
||||
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
|
||||
return "处理审计事件成功";
|
||||
} catch (Exception e) {
|
||||
log.error("处理审计事件出现异常: MessageProperties: [{}], 审计内容:[{}]",
|
||||
message.getMessageProperties(), JSONObject.toJSONString(auditEvent), e);
|
||||
channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
|
||||
return "处理审计事件失败";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AuditRepository
|
||||
*/
|
||||
private final AuditRepository auditRepository;
|
||||
|
||||
/**
|
||||
* AuditElasticSearchRepository
|
||||
*/
|
||||
private final AuditElasticSearchRepository auditElasticSearchRepository;
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* eiam-audit - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.audit.mq;
|
||||
|
||||
import org.springframework.amqp.core.AmqpAdmin;
|
||||
import org.springframework.amqp.core.BindingBuilder;
|
||||
import org.springframework.amqp.core.Queue;
|
||||
import org.springframework.amqp.core.TopicExchange;
|
||||
import org.springframework.amqp.rabbit.AsyncRabbitTemplate;
|
||||
import org.springframework.amqp.rabbit.RabbitConverterFuture;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
|
||||
import cn.topiam.employee.audit.event.AuditEvent;
|
||||
import cn.topiam.employee.support.trace.TraceUtils;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import static cn.topiam.employee.support.trace.TraceAspect.TRACE_ID;
|
||||
|
||||
/**
|
||||
* MQ消息发送
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2023/5/30 23:12
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class AuditMessagePublisher {
|
||||
|
||||
private final AsyncRabbitTemplate asyncRabbitTemplate;
|
||||
|
||||
private final AmqpAdmin amqpAdmin;
|
||||
|
||||
public final static String AUDIT_TOPIC = "audit";
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
TopicExchange topicExchange = new TopicExchange(AUDIT_TOPIC);
|
||||
Queue queue = new Queue(AUDIT_TOPIC, true);
|
||||
amqpAdmin.declareExchange(topicExchange);
|
||||
amqpAdmin.declareQueue(queue);
|
||||
amqpAdmin.declareBinding(BindingBuilder.bind(queue).to(topicExchange).with(AUDIT_TOPIC));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送异步审计消息
|
||||
*
|
||||
* @param data {@link String} 审计内容
|
||||
*/
|
||||
public void sendAuditChangeMessage(AuditEvent data) {
|
||||
String traceId = TraceUtils.get();
|
||||
log.info("发送审计消息, 审计内容:[{}]", JSONObject.toJSONString(data));
|
||||
RabbitConverterFuture<Object> future = asyncRabbitTemplate.convertSendAndReceive(
|
||||
AUDIT_TOPIC, AUDIT_TOPIC,
|
||||
MessageBuilder.withPayload(data).setHeader(TRACE_ID, traceId).build());
|
||||
future.whenComplete((result, ex) -> {
|
||||
if (ex == null) {
|
||||
log.info("发送审计消息成功,处理结果为:[{}]", result);
|
||||
} else {
|
||||
log.error("发送审计消息异常,审计内容:[{}]", JSONObject.toJSONString(data), ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* eiam-audit - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.audit.repository;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import cn.topiam.employee.audit.event.type.EventType;
|
||||
import cn.topiam.employee.audit.repository.result.AuditStatisticsResult;
|
||||
import cn.topiam.employee.audit.repository.result.AuthnQuantityResult;
|
||||
|
||||
/**
|
||||
* 组织成员
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2022/10/2 02:53
|
||||
*/
|
||||
public interface AuditCustomizedRepository {
|
||||
|
||||
List<AuditStatisticsResult> authnHotProvider(EventType type, LocalDateTime startTime,
|
||||
LocalDateTime endTime);
|
||||
|
||||
List<AuthnQuantityResult> authnQuantity(EventType type, LocalDateTime startTime,
|
||||
LocalDateTime endTime, String dateFormat);
|
||||
|
||||
List<AuditStatisticsResult> appVisitRank(EventType type, LocalDateTime startTime,
|
||||
LocalDateTime endTime);
|
||||
|
||||
List<AuditStatisticsResult> authnZone(EventType type, LocalDateTime startTime,
|
||||
LocalDateTime endTime);
|
||||
}
|
|
@ -36,7 +36,7 @@ import cn.topiam.employee.support.repository.LogicDeleteRepository;
|
|||
*/
|
||||
@Repository
|
||||
public interface AuditRepository extends LogicDeleteRepository<AuditEntity, Long>,
|
||||
QuerydslPredicateExecutor<AuditEntity> {
|
||||
QuerydslPredicateExecutor<AuditEntity>, AuditCustomizedRepository {
|
||||
|
||||
/**
|
||||
* 统计指定时间范围内用户登录失败次数
|
||||
|
@ -58,4 +58,8 @@ public interface AuditRepository extends LogicDeleteRepository<AuditEntity, Long
|
|||
* @return {@link AuditEntity}
|
||||
*/
|
||||
Optional<AuditEntity> findByRequestId(String requestId);
|
||||
|
||||
@Query(value = "SELECT COUNT(*) FROM audit WHERE event_type = :type AND event_time BETWEEN :startTime AND :endTime", nativeQuery = true)
|
||||
Long countByTypeAndTime(@Param("type") String type, @Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* eiam-audit - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.audit.repository.impl;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import cn.topiam.employee.audit.event.type.EventType;
|
||||
import cn.topiam.employee.audit.repository.AuditCustomizedRepository;
|
||||
import cn.topiam.employee.audit.repository.impl.mapper.AuditStatisticsResultMapper;
|
||||
import cn.topiam.employee.audit.repository.impl.mapper.AuthnQuantityResultMapper;
|
||||
import cn.topiam.employee.audit.repository.result.AuditStatisticsResult;
|
||||
import cn.topiam.employee.audit.repository.result.AuthnQuantityResult;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2022/10/2 02:54
|
||||
*/
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
public class AuditCustomizedRepositoryImpl implements AuditCustomizedRepository {
|
||||
|
||||
/**
|
||||
* JdbcTemplate
|
||||
*/
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Override
|
||||
public List<AuditStatisticsResult> authnHotProvider(EventType type, LocalDateTime startTime,
|
||||
LocalDateTime endTime) {
|
||||
String sql = """
|
||||
SELECT
|
||||
actor_auth_type AS key_,
|
||||
COUNT(*) AS count_
|
||||
FROM
|
||||
audit
|
||||
WHERE
|
||||
event_type = ?
|
||||
AND event_time BETWEEN ?
|
||||
AND ?
|
||||
GROUP BY
|
||||
actor_auth_type
|
||||
""";
|
||||
return jdbcTemplate.query(sql, new AuditStatisticsResultMapper(), type.getCode(), startTime,
|
||||
endTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AuthnQuantityResult> authnQuantity(EventType type, LocalDateTime startTime,
|
||||
LocalDateTime endTime, String dateFormat) {
|
||||
String sql = """
|
||||
SELECT
|
||||
DATE_FORMAT( event_time, ? ) AS name_,
|
||||
COUNT(*) AS count_,
|
||||
event_status AS status_
|
||||
FROM
|
||||
audit
|
||||
WHERE
|
||||
event_type = ?
|
||||
AND event_time BETWEEN ?
|
||||
AND ?
|
||||
GROUP BY
|
||||
DATE_FORMAT( event_time, ? ),
|
||||
event_status
|
||||
""";
|
||||
|
||||
return jdbcTemplate.query(sql, new AuthnQuantityResultMapper(), dateFormat, type.getCode(),
|
||||
startTime, endTime, dateFormat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AuditStatisticsResult> appVisitRank(EventType type, LocalDateTime startTime,
|
||||
LocalDateTime endTime) {
|
||||
String sql = """
|
||||
SELECT
|
||||
JSON_EXTRACT( target_, '$[0].id' ) AS key_,
|
||||
COUNT(*) AS count_
|
||||
FROM
|
||||
audit
|
||||
WHERE
|
||||
event_type = ?
|
||||
AND event_time BETWEEN ?
|
||||
AND ?
|
||||
GROUP BY
|
||||
JSON_EXTRACT(
|
||||
target_,
|
||||
'$[0].id'
|
||||
)
|
||||
""";
|
||||
return jdbcTemplate.query(sql, new AuditStatisticsResultMapper(), type.getCode(), startTime,
|
||||
endTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AuditStatisticsResult> authnZone(EventType type, LocalDateTime startTime,
|
||||
LocalDateTime endTime) {
|
||||
String sql = """
|
||||
SELECT
|
||||
JSON_EXTRACT( target_, '$.provinceCode' ) AS key_,
|
||||
COUNT(*) AS count_
|
||||
FROM
|
||||
audit
|
||||
WHERE
|
||||
event_type = ?
|
||||
AND event_time BETWEEN ?
|
||||
AND ?
|
||||
GROUP BY
|
||||
JSON_EXTRACT(
|
||||
target_,
|
||||
'$.provinceCode'
|
||||
)
|
||||
""";
|
||||
return jdbcTemplate.query(sql, new AuditStatisticsResultMapper(), type.getCode(), startTime,
|
||||
endTime);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* eiam-audit - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.audit.repository.impl.mapper;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
import cn.topiam.employee.audit.repository.result.AuditStatisticsResult;
|
||||
|
||||
/**
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2023/10/04 22:25
|
||||
*/
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
public class AuditStatisticsResultMapper implements RowMapper<AuditStatisticsResult> {
|
||||
/**
|
||||
* Implementations must implement this method to map each row of data
|
||||
* in the ResultSet. This method should not call {@code next()} on
|
||||
* the ResultSet; it is only supposed to map values of the current row.
|
||||
*
|
||||
* @param rs the ResultSet to map (pre-initialized for the current row)
|
||||
* @param rowNum the number of the current row
|
||||
* @return the result object for the current row (may be {@code null})
|
||||
* @throws SQLException if an SQLException is encountered getting
|
||||
* column values (that is, there's no need to catch SQLException)
|
||||
*/
|
||||
@Override
|
||||
public AuditStatisticsResult mapRow(@NonNull ResultSet rs, int rowNum) throws SQLException {
|
||||
//@formatter:off
|
||||
AuditStatisticsResult user = new AuditStatisticsResult();
|
||||
if (StringUtils.isNoneBlank(rs.getString("key_"))) {
|
||||
user.setKey(rs.getString("key_").replace("\"", ""));
|
||||
}
|
||||
user.setCount(rs.getLong("count_"));
|
||||
//@formatter:on
|
||||
return user;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* eiam-audit - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.audit.repository.impl.mapper;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
import cn.topiam.employee.audit.repository.result.AuthnQuantityResult;
|
||||
|
||||
/**
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2023/10/04 22:25
|
||||
*/
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
public class AuthnQuantityResultMapper implements RowMapper<AuthnQuantityResult> {
|
||||
/**
|
||||
* Implementations must implement this method to map each row of data
|
||||
* in the ResultSet. This method should not call {@code next()} on
|
||||
* the ResultSet; it is only supposed to map values of the current row.
|
||||
*
|
||||
* @param rs the ResultSet to map (pre-initialized for the current row)
|
||||
* @param rowNum the number of the current row
|
||||
* @return the result object for the current row (may be {@code null})
|
||||
* @throws SQLException if an SQLException is encountered getting
|
||||
* column values (that is, there's no need to catch SQLException)
|
||||
*/
|
||||
@Override
|
||||
public AuthnQuantityResult mapRow(@NonNull ResultSet rs, int rowNum) throws SQLException {
|
||||
//@formatter:off
|
||||
AuthnQuantityResult user = new AuthnQuantityResult();
|
||||
user.setName(rs.getString("name_"));
|
||||
user.setCount(rs.getLong("count_"));
|
||||
user.setStatus(rs.getString("status_"));
|
||||
//@formatter:on
|
||||
return user;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* eiam-audit - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.audit.repository.impl;
|
|
@ -15,21 +15,35 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.audit.repository;
|
||||
package cn.topiam.employee.audit.repository.result;
|
||||
|
||||
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import java.io.Serializable;
|
||||
|
||||
import cn.topiam.employee.audit.entity.AuditElasticSearchEntity;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
/**
|
||||
* 行为审计repository
|
||||
* 审计统计
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2021/9/11 22:32
|
||||
* Created by support@topiam.cn on 2023/10/04 23:16
|
||||
*/
|
||||
@Repository
|
||||
public interface AuditElasticSearchRepository extends
|
||||
ElasticsearchRepository<AuditElasticSearchEntity, String> {
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "审计统计")
|
||||
public class AuditStatisticsResult implements Serializable {
|
||||
|
||||
/**
|
||||
* key
|
||||
*/
|
||||
private String key;
|
||||
|
||||
/**
|
||||
* count
|
||||
*/
|
||||
private Long count;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* eiam-audit - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
|
@ -15,12 +15,13 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.console.pojo.result.analysis;
|
||||
package cn.topiam.employee.audit.repository.result;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
|
@ -31,6 +32,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
|||
* Created by support@topiam.cn on 2020/11/22 23:16
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "认证量统计响应")
|
||||
public class AuthnQuantityResult implements Serializable {
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* eiam-audit - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
|
@ -15,7 +15,9 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.console.pojo.result.analysis;
|
||||
package cn.topiam.employee.audit.repository.result;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
@ -33,7 +35,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
|||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "登录区域响应")
|
||||
public class AuthnZoneResult {
|
||||
public class AuthnZoneResult implements Serializable {
|
||||
|
||||
/**
|
||||
* 省份code
|
|
@ -38,9 +38,7 @@ import com.google.common.collect.Lists;
|
|||
|
||||
import cn.topiam.employee.audit.controller.pojo.AuditListQuery;
|
||||
import cn.topiam.employee.audit.controller.pojo.AuditListResult;
|
||||
import cn.topiam.employee.audit.entity.Actor;
|
||||
import cn.topiam.employee.audit.entity.AuditElasticSearchEntity;
|
||||
import cn.topiam.employee.audit.entity.Event;
|
||||
import cn.topiam.employee.audit.entity.AuditEntity;
|
||||
import cn.topiam.employee.audit.entity.Target;
|
||||
import cn.topiam.employee.audit.enums.TargetType;
|
||||
import cn.topiam.employee.common.entity.account.OrganizationEntity;
|
||||
|
@ -93,27 +91,25 @@ public interface AuditDataConverter {
|
|||
* @param page {@link PageModel}
|
||||
* @return {@link Page}
|
||||
*/
|
||||
default Page<AuditListResult> searchHitsConvertToAuditListResult(SearchHits<AuditElasticSearchEntity> search,
|
||||
default Page<AuditListResult> searchHitsConvertToAuditListResult(SearchHits<AuditEntity> search,
|
||||
PageModel page) {
|
||||
List<AuditListResult> list = new ArrayList<>();
|
||||
//总记录数
|
||||
search.forEach(hit -> {
|
||||
AuditElasticSearchEntity content = hit.getContent();
|
||||
Event event = content.getEvent();
|
||||
AuditEntity content = hit.getContent();
|
||||
AuditListResult result = new AuditListResult();
|
||||
result.setId(content.getId());
|
||||
result.setEventStatus(event.getStatus());
|
||||
result.setEventType(event.getType().getDesc());
|
||||
result.setEventTime(event.getTime());
|
||||
result.setId(content.getId().toString());
|
||||
result.setEventStatus(content.getEventStatus());
|
||||
result.setEventType(content.getEventType().getDesc());
|
||||
result.setEventTime(content.getEventTime());
|
||||
//用户代理
|
||||
result.setUserAgent(content.getUserAgent());
|
||||
result.setGeoLocation(content.getGeoLocation());
|
||||
Actor actor = content.getActor();
|
||||
//用户ID
|
||||
result.setUserId(actor.getId());
|
||||
result.setUsername(getUsername(actor.getType(), actor.getId()));
|
||||
result.setUserId(content.getActorId());
|
||||
result.setUsername(getUsername(content.getActorType(), content.getActorId()));
|
||||
//用户类型
|
||||
result.setUserType(actor.getType().getType());
|
||||
result.setUserType(content.getActorType().getType());
|
||||
//操作对象
|
||||
if (Objects.nonNull(content.getTargets())) {
|
||||
for (Target target : content.getTargets()) {
|
||||
|
|
|
@ -33,7 +33,7 @@ import org.springframework.util.CollectionUtils;
|
|||
import cn.topiam.employee.audit.controller.pojo.AuditListQuery;
|
||||
import cn.topiam.employee.audit.controller.pojo.AuditListResult;
|
||||
import cn.topiam.employee.audit.controller.pojo.DictResult;
|
||||
import cn.topiam.employee.audit.entity.AuditElasticSearchEntity;
|
||||
import cn.topiam.employee.audit.entity.AuditEntity;
|
||||
import cn.topiam.employee.audit.event.type.EventType;
|
||||
import cn.topiam.employee.audit.service.AuditService;
|
||||
import cn.topiam.employee.audit.service.converter.AuditDataConverter;
|
||||
|
@ -71,8 +71,8 @@ public class AuditServiceImpl implements AuditService {
|
|||
//查询入参转查询条件
|
||||
NativeQuery nsq = auditDataConverter.auditListRequestConvertToNativeQuery(query, page);
|
||||
//查询列表
|
||||
SearchHits<AuditElasticSearchEntity> search = elasticsearchTemplate.search(nsq,
|
||||
AuditElasticSearchEntity.class, IndexCoordinates
|
||||
SearchHits<AuditEntity> search = elasticsearchTemplate.search(nsq, AuditEntity.class,
|
||||
IndexCoordinates
|
||||
.of(getAuditIndexPrefix(supportProperties.getAudit().getIndexPrefix()) + "*"));
|
||||
//结果转返回结果
|
||||
return auditDataConverter.searchHitsConvertToAuditListResult(search, page);
|
||||
|
|
|
@ -24,10 +24,8 @@ package cn.topiam.employee.common.constant;
|
|||
* Created by support@topiam.cn on 2020/7/26 21:07
|
||||
*/
|
||||
public final class SecurityConstants {
|
||||
|
||||
/**
|
||||
* CODE
|
||||
* 默认管理员用户名
|
||||
*/
|
||||
public static final String PASSWORD_POLICY_CODE = "default";
|
||||
|
||||
public static String DEFAULT_ADMIN_USERNAME = "admin";
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* eiam-synchronizer - Employee Identity and Access Management
|
||||
* eiam-common - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.synchronizer.constants;
|
||||
package cn.topiam.employee.common.constant;
|
||||
|
||||
import cn.topiam.employee.support.constant.EiamConstants;
|
||||
|
|
@ -63,6 +63,12 @@ public class OrganizationMemberEntity extends LogicDeleteEntity<Long> {
|
|||
@Column(name = "user_id")
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 主组织
|
||||
*/
|
||||
@Column(name = "primary_")
|
||||
private Boolean primary;
|
||||
|
||||
public OrganizationMemberEntity() {
|
||||
}
|
||||
|
||||
|
|
|
@ -1,227 +0,0 @@
|
|||
/*
|
||||
* eiam-common - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.common.entity.account;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.Field;
|
||||
import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||
import org.springframework.data.elasticsearch.annotations.Setting;
|
||||
|
||||
import cn.topiam.employee.common.entity.account.po.OrganizationPO;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import static org.springframework.data.elasticsearch.annotations.DateFormat.date;
|
||||
import static org.springframework.data.elasticsearch.annotations.DateFormat.date_hour_minute_second_millis;
|
||||
|
||||
/**
|
||||
* 用户elasticsearch实体
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2023/4/12 23:22
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@EqualsAndHashCode
|
||||
@Document(indexName = "#{@userIndexName.getIndexName()}")
|
||||
@Setting(replicas = 0)
|
||||
public class UserElasticSearchEntity implements Serializable {
|
||||
|
||||
/**
|
||||
* 用户userId
|
||||
*/
|
||||
@Id
|
||||
@Field(type = FieldType.Keyword, name = "id")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
@Field(type = FieldType.Keyword, name = "username")
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
@Field(type = FieldType.Keyword, name = "email")
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
@Field(type = FieldType.Keyword, name = "phone")
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
@Field(type = FieldType.Keyword, name = "phoneAreaCode")
|
||||
private String phoneAreaCode;
|
||||
|
||||
/**
|
||||
* 姓名
|
||||
*/
|
||||
@Field(type = FieldType.Keyword, name = "fullName")
|
||||
private String fullName;
|
||||
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
@Field(type = FieldType.Keyword, name = "nickName")
|
||||
private String nickName;
|
||||
|
||||
/**
|
||||
* 头像URL
|
||||
*/
|
||||
@Field(type = FieldType.Keyword, name = "avatar")
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 状态 ENABLE:启用 DISABLE:禁用 LOCKING:锁定
|
||||
*/
|
||||
@Field(type = FieldType.Keyword, name = "status")
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 数据来源
|
||||
*/
|
||||
@Field(type = FieldType.Keyword, name = "dataOrigin")
|
||||
private String dataOrigin;
|
||||
|
||||
/**
|
||||
* 身份源ID
|
||||
*/
|
||||
@Field(type = FieldType.Keyword, name = "identitySourceId")
|
||||
private Long identitySourceId;
|
||||
|
||||
/**
|
||||
* 邮箱验证有效
|
||||
*/
|
||||
@Field(type = FieldType.Keyword, name = "emailVerified")
|
||||
private Boolean emailVerified;
|
||||
|
||||
/**
|
||||
* 手机有效
|
||||
*/
|
||||
@Field(type = FieldType.Keyword, name = "phoneVerified")
|
||||
private Boolean phoneVerified;
|
||||
|
||||
/**
|
||||
* 认证次数
|
||||
*/
|
||||
@Field(type = FieldType.Keyword, name = "authTotal")
|
||||
private Long authTotal;
|
||||
/**
|
||||
* 上次认证IP
|
||||
*/
|
||||
@Field(type = FieldType.Keyword, name = "lastAuthIp")
|
||||
private String lastAuthIp;
|
||||
/**
|
||||
* 上次认证时间
|
||||
*/
|
||||
@Field(type = FieldType.Date, name = "lastAuthTime", format = date_hour_minute_second_millis)
|
||||
private LocalDateTime lastAuthTime;
|
||||
|
||||
/**
|
||||
* 扩展参数
|
||||
*/
|
||||
@Column(name = "expand_")
|
||||
private String expand;
|
||||
|
||||
/**
|
||||
* 外部ID
|
||||
*/
|
||||
@Column(name = "external_id")
|
||||
private String externalId;
|
||||
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
@Field(type = FieldType.Date, name = "expireDate", format = date)
|
||||
private LocalDate expireDate;
|
||||
|
||||
/**
|
||||
* 最后修改密码时间
|
||||
*/
|
||||
@Field(type = FieldType.Date, name = "lastUpdatePasswordTime", format = date_hour_minute_second_millis)
|
||||
private LocalDateTime lastUpdatePasswordTime;
|
||||
|
||||
// ----------------------------------用户详情----------------------------------
|
||||
/**
|
||||
* 证件类型
|
||||
*/
|
||||
@Field(type = FieldType.Keyword, name = "idType")
|
||||
private String idType;
|
||||
|
||||
/**
|
||||
* 身份证号
|
||||
*/
|
||||
@Field(type = FieldType.Keyword, name = "idCard")
|
||||
private String idCard;
|
||||
|
||||
/**
|
||||
* 个人主页
|
||||
*/
|
||||
@Field(type = FieldType.Text, name = "website")
|
||||
private String website;
|
||||
|
||||
/**u
|
||||
* 地址
|
||||
*/
|
||||
@Field(type = FieldType.Keyword, name = "address")
|
||||
private String address;
|
||||
|
||||
/**
|
||||
* 组织列表
|
||||
*/
|
||||
@Field(type = FieldType.Object, name = "organizations")
|
||||
private List<OrganizationPO> organizations;
|
||||
|
||||
/**
|
||||
* 用户组
|
||||
*/
|
||||
@Field(type = FieldType.Object, name = "userGroups")
|
||||
private List<UserGroup> userGroups;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public static class UserGroup {
|
||||
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
@Field(type = FieldType.Keyword, name = "id")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 用户组名称
|
||||
*/
|
||||
@Field(type = FieldType.Keyword, name = "name")
|
||||
private String name;
|
||||
}
|
||||
}
|
|
@ -30,7 +30,7 @@ import lombok.EqualsAndHashCode;
|
|||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class UserIdpBindPo extends UserIdpBindEntity {
|
||||
public class UserIdpBindPO extends UserIdpBindEntity {
|
||||
|
||||
/**
|
||||
* 用户名称
|
|
@ -41,4 +41,9 @@ public class UserPO extends UserEntity {
|
|||
* 组织机构显示目录
|
||||
*/
|
||||
private String orgDisplayPath;
|
||||
|
||||
/**
|
||||
* 主组织机构显示目录
|
||||
*/
|
||||
private String primaryOrgDisplayPath;
|
||||
}
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* eiam-common - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.common.repository.account;
|
||||
|
||||
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import cn.topiam.employee.common.entity.account.UserElasticSearchEntity;
|
||||
|
||||
/**
|
||||
* 用户信息repository
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2021/9/11 22:32
|
||||
*/
|
||||
@Repository
|
||||
public interface UserElasticSearchRepository extends
|
||||
ElasticsearchRepository<UserElasticSearchEntity, String> {
|
||||
|
||||
}
|
|
@ -19,7 +19,7 @@ package cn.topiam.employee.common.repository.account;
|
|||
|
||||
import java.util.Optional;
|
||||
|
||||
import cn.topiam.employee.common.entity.account.po.UserIdpBindPo;
|
||||
import cn.topiam.employee.common.entity.account.po.UserIdpBindPO;
|
||||
import cn.topiam.employee.support.repository.page.domain.Page;
|
||||
|
||||
/**
|
||||
|
@ -37,7 +37,7 @@ public interface UserIdpRepositoryCustomized {
|
|||
* @param openId {@link String}
|
||||
* @return {@link Optional}
|
||||
*/
|
||||
Optional<UserIdpBindPo> findByIdpIdAndOpenId(String idpId, String openId);
|
||||
Optional<UserIdpBindPO> findByIdpIdAndOpenId(String idpId, String openId);
|
||||
|
||||
/**
|
||||
* 根据身份源ID和userId查询
|
||||
|
@ -46,7 +46,7 @@ public interface UserIdpRepositoryCustomized {
|
|||
* @param userId {@link String}
|
||||
* @return {@link Optional}
|
||||
*/
|
||||
Optional<UserIdpBindPo> findByIdpIdAndUserId(String idpId, Long userId);
|
||||
Optional<UserIdpBindPO> findByIdpIdAndUserId(String idpId, Long userId);
|
||||
|
||||
/**
|
||||
* 查询用户身份提供商绑定
|
||||
|
@ -54,5 +54,5 @@ public interface UserIdpRepositoryCustomized {
|
|||
* @param userId {@link Long}
|
||||
* @return {@link Page}
|
||||
*/
|
||||
Iterable<UserIdpBindPo> getUserIdpBindList(Long userId);
|
||||
Iterable<UserIdpBindPO> getUserIdpBindList(Long userId);
|
||||
}
|
||||
|
|
|
@ -21,17 +21,12 @@ import java.util.List;
|
|||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
|
||||
import cn.topiam.employee.common.entity.account.UserElasticSearchEntity;
|
||||
import cn.topiam.employee.common.entity.account.UserEntity;
|
||||
import cn.topiam.employee.common.entity.account.po.UserEsPO;
|
||||
import cn.topiam.employee.common.entity.account.po.UserPO;
|
||||
import cn.topiam.employee.common.entity.account.query.UserListNotInGroupQuery;
|
||||
import cn.topiam.employee.common.entity.account.query.UserListQuery;
|
||||
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
|
||||
|
||||
/**
|
||||
* User Repository Customized
|
||||
*
|
||||
|
@ -107,29 +102,4 @@ public interface UserRepositoryCustomized {
|
|||
*/
|
||||
void batchUpdate(List<UserEntity> list);
|
||||
|
||||
/**
|
||||
* 获取用户列表
|
||||
*
|
||||
* @param idList {@link List}
|
||||
* @return {@link List}
|
||||
*/
|
||||
List<UserEsPO> getUserList(List<String> idList);
|
||||
|
||||
/**
|
||||
* 查询es用户数据
|
||||
*
|
||||
* @param userIndex {@link IndexCoordinates}
|
||||
* @return {@link List}
|
||||
*/
|
||||
List<UserElasticSearchEntity> getAllUserElasticSearchEntity(IndexCoordinates userIndex);
|
||||
|
||||
/**
|
||||
* 查询es用户数据
|
||||
*
|
||||
* @param userIndex {@link IndexCoordinates}
|
||||
* @param queryBuilder {@link Query}
|
||||
* @return {@link List}
|
||||
*/
|
||||
List<UserElasticSearchEntity> getAllUserElasticSearchEntity(IndexCoordinates userIndex,
|
||||
Query queryBuilder);
|
||||
}
|
||||
|
|
|
@ -79,7 +79,8 @@ public class UserGroupMemberRepositoryCustomizedImpl implements
|
|||
`u`.update_by,
|
||||
`u`.update_time,
|
||||
`u`.remark_,
|
||||
group_concat( organization_.display_path ) AS org_display_path
|
||||
group_concat( IF(organization_member.primary_ = 1, null, organization_.display_path ) ) AS primary_org_display_path,
|
||||
group_concat( IF(organization_member.primary_ IS NULL, null, organization_.display_path ) ) AS org_display_path
|
||||
FROM
|
||||
user_group_member ugm
|
||||
INNER JOIN user u ON ugm.user_id = u.id_ AND u.is_deleted = '0'
|
||||
|
|
|
@ -26,7 +26,7 @@ import org.springframework.dao.EmptyResultDataAccessException;
|
|||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import cn.topiam.employee.common.entity.account.po.UserIdpBindPo;
|
||||
import cn.topiam.employee.common.entity.account.po.UserIdpBindPO;
|
||||
import cn.topiam.employee.common.repository.account.UserIdpRepositoryCustomized;
|
||||
import cn.topiam.employee.common.repository.account.impl.mapper.UserIdpBindPoMapper;
|
||||
import cn.topiam.employee.support.repository.page.domain.Page;
|
||||
|
@ -53,7 +53,7 @@ public class UserIdpRepositoryCustomizedImpl implements UserIdpRepositoryCustomi
|
|||
* @return {@link Optional}
|
||||
*/
|
||||
@Override
|
||||
public Optional<UserIdpBindPo> findByIdpIdAndOpenId(String idpId, String openId) {
|
||||
public Optional<UserIdpBindPO> findByIdpIdAndOpenId(String idpId, String openId) {
|
||||
//@formatter:off
|
||||
StringBuilder builder = new StringBuilder("SELECT uidp.*,`user`.username_,idp.name_ as idp_name FROM user_idp_bind uidp LEFT JOIN `user` ON uidp.user_id = `user`.id_ AND `user`.is_deleted = '0' LEFT JOIN identity_provider idp ON uidp.idp_id = idp.id_ AND idp.is_deleted = '0' WHERE uidp.is_deleted = '0' ");
|
||||
//身份提供商ID
|
||||
|
@ -67,7 +67,7 @@ public class UserIdpRepositoryCustomizedImpl implements UserIdpRepositoryCustomi
|
|||
//@formatter:on
|
||||
String sql = builder.toString();
|
||||
try {
|
||||
UserIdpBindPo userIdpBindPo = jdbcTemplate.queryForObject(sql,
|
||||
UserIdpBindPO userIdpBindPo = jdbcTemplate.queryForObject(sql,
|
||||
new UserIdpBindPoMapper());
|
||||
return Optional.ofNullable(userIdpBindPo);
|
||||
} catch (EmptyResultDataAccessException e) {
|
||||
|
@ -83,7 +83,7 @@ public class UserIdpRepositoryCustomizedImpl implements UserIdpRepositoryCustomi
|
|||
* @return {@link Optional}
|
||||
*/
|
||||
@Override
|
||||
public Optional<UserIdpBindPo> findByIdpIdAndUserId(String idpId, Long userId) {
|
||||
public Optional<UserIdpBindPO> findByIdpIdAndUserId(String idpId, Long userId) {
|
||||
//@formatter:off
|
||||
StringBuilder builder = new StringBuilder("SELECT uidp.*,`user`.username_,idp.name_ as idp_name FROM user_idp_bind uidp LEFT JOIN `user` ON uidp.user_id = `user`.id_ AND `user`.is_deleted = '0' LEFT JOIN identity_provider idp ON uidp.idp_id = idp.id_ AND idp.is_deleted = '0' WHERE uidp.is_deleted = '0' ");
|
||||
//身份提供商ID
|
||||
|
@ -97,7 +97,7 @@ public class UserIdpRepositoryCustomizedImpl implements UserIdpRepositoryCustomi
|
|||
//@formatter:on
|
||||
String sql = builder.toString();
|
||||
try {
|
||||
UserIdpBindPo userIdpBindPo = jdbcTemplate.queryForObject(sql,
|
||||
UserIdpBindPO userIdpBindPo = jdbcTemplate.queryForObject(sql,
|
||||
new UserIdpBindPoMapper());
|
||||
return Optional.ofNullable(userIdpBindPo);
|
||||
} catch (EmptyResultDataAccessException e) {
|
||||
|
@ -112,7 +112,7 @@ public class UserIdpRepositoryCustomizedImpl implements UserIdpRepositoryCustomi
|
|||
* @return {@link Page}
|
||||
*/
|
||||
@Override
|
||||
public Iterable<UserIdpBindPo> getUserIdpBindList(Long userId) {
|
||||
public Iterable<UserIdpBindPO> getUserIdpBindList(Long userId) {
|
||||
//@formatter:off
|
||||
StringBuilder builder = new StringBuilder("SELECT uidp.*,idp.name_ as idp_name FROM user_idp_bind uidp LEFT JOIN identity_provider idp ON uidp.idp_id = idp.id_ AND idp.is_deleted = '0' WHERE uidp.is_deleted = '0' ");
|
||||
//用户ID
|
||||
|
|
|
@ -21,7 +21,6 @@ import java.sql.Date;
|
|||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
|
@ -31,34 +30,21 @@ import org.springframework.cache.annotation.CacheConfig;
|
|||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
|
||||
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
|
||||
import org.springframework.data.elasticsearch.client.elc.NativeQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.core.SearchHit;
|
||||
import org.springframework.data.elasticsearch.core.SearchHitsIterator;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import cn.topiam.employee.common.entity.account.UserElasticSearchEntity;
|
||||
import cn.topiam.employee.common.entity.account.UserEntity;
|
||||
import cn.topiam.employee.common.entity.account.po.UserEsPO;
|
||||
import cn.topiam.employee.common.entity.account.po.UserPO;
|
||||
import cn.topiam.employee.common.entity.account.query.UserListNotInGroupQuery;
|
||||
import cn.topiam.employee.common.entity.account.query.UserListQuery;
|
||||
import cn.topiam.employee.common.repository.account.UserRepositoryCustomized;
|
||||
import cn.topiam.employee.common.repository.account.impl.mapper.UserEntityMapper;
|
||||
import cn.topiam.employee.common.repository.account.impl.mapper.UserEsMapper;
|
||||
import cn.topiam.employee.common.repository.account.impl.mapper.UserPoMapper;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders;
|
||||
import static cn.topiam.employee.common.constant.AccountConstants.USER_CACHE_NAME;
|
||||
|
||||
/**
|
||||
|
@ -83,7 +69,7 @@ public class UserRepositoryCustomizedImpl implements UserRepositoryCustomized {
|
|||
@Override
|
||||
public Page<UserPO> getUserList(UserListQuery query, Pageable pageable) {
|
||||
//@formatter:off
|
||||
StringBuilder builder = new StringBuilder("SELECT `user`.id_, `user`.username_,`user`.password_, `user`.email_, `user`.phone_,`user`.phone_area_code, `user`.full_name ,`user`.nick_name, `user`.avatar_ , `user`.status_, `user`.data_origin, `user`.email_verified, `user`.phone_verified, `user`.auth_total, `user`.last_auth_ip, `user`.last_auth_time, `user`.expand_, `user`.external_id , `user`.expire_date,`user`.create_by, `user`.create_time, `user`.update_by , `user`.update_time, `user`.remark_, group_concat(organization_.display_path) AS org_display_path FROM `user` INNER JOIN `organization_member` ON (`user`.id_ = organization_member.user_id) INNER JOIN `organization` organization_ ON (organization_.id_ = organization_member.org_id) WHERE `user`.is_deleted = 0");
|
||||
StringBuilder builder = new StringBuilder("SELECT `user`.id_, `user`.username_,`user`.password_, `user`.email_, `user`.phone_,`user`.phone_area_code, `user`.full_name ,`user`.nick_name, `user`.avatar_ , `user`.status_, `user`.data_origin, `user`.email_verified, `user`.phone_verified, `user`.auth_total, `user`.last_auth_ip, `user`.last_auth_time, `user`.expand_, `user`.external_id , `user`.expire_date,`user`.create_by, `user`.create_time, `user`.update_by , `user`.update_time, `user`.remark_, group_concat( IF(organization_member.primary_ = 1, null, organization_.display_path ) ) AS primary_org_display_path, group_concat( IF(organization_member.primary_ IS NULL, null, organization_.display_path ) ) AS org_display_path FROM `user` INNER JOIN `organization_member` ON (`user`.id_ = organization_member.user_id) INNER JOIN `organization` organization_ ON (organization_.id_ = organization_member.org_id) WHERE `user`.is_deleted = 0");
|
||||
//组织条件
|
||||
if (StringUtils.isNotBlank(query.getOrganizationId())) {
|
||||
if (Boolean.TRUE.equals(query.getInclSubOrganization())) {
|
||||
|
@ -169,7 +155,8 @@ public class UserRepositoryCustomizedImpl implements UserRepositoryCustomized {
|
|||
`user`.update_by,
|
||||
`user`.update_time,
|
||||
`user`.remark_,
|
||||
group_concat( organization_.display_path ) AS org_display_path
|
||||
group_concat( IF(organization_member.primary_ = 1, null, organization_.display_path ) ) AS primary_org_display_path,
|
||||
group_concat( IF(organization_member.primary_ IS NULL, null, organization_.display_path ) ) AS org_display_path
|
||||
FROM `user`
|
||||
LEFT JOIN `organization_member` ON ( `user`.id_ = organization_member.user_id AND organization_member.is_deleted = '0' )
|
||||
LEFT JOIN `organization` organization_ ON ( organization_.id_ = organization_member.org_id AND organization_.is_deleted = '0' )
|
||||
|
@ -362,120 +349,6 @@ public class UserRepositoryCustomizedImpl implements UserRepositoryCustomized {
|
|||
//@formatter:on
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
public List<UserEsPO> getUserList(List<String> idList) {
|
||||
//@formatter:off
|
||||
String whereUserId = "";
|
||||
if (!CollectionUtils.isEmpty(idList)) {
|
||||
if (idList.size() > 1) {
|
||||
whereUserId = "AND `user`.id_ IN ('%s')".formatted(String.join("','", idList));
|
||||
}
|
||||
else {
|
||||
whereUserId = "AND `user`.id_ = '%s'".formatted(idList.get(0));
|
||||
}
|
||||
}
|
||||
String sql = """
|
||||
SELECT
|
||||
`user`.id_,
|
||||
`user`.username_,
|
||||
`user`.email_,
|
||||
`user`.phone_,
|
||||
`user`.phone_area_code,
|
||||
`user`.full_name,
|
||||
`user`.nick_name,
|
||||
`user`.avatar_,
|
||||
`user`.status_,
|
||||
`user`.data_origin,
|
||||
`user`.email_verified,
|
||||
`user`.phone_verified,
|
||||
`user`.auth_total,
|
||||
`user`.last_auth_ip,
|
||||
`user`.last_auth_time,
|
||||
`user`.expand_,
|
||||
`user`.external_id,
|
||||
`user`.expire_date,
|
||||
`user`.create_by,
|
||||
`user`.create_time,
|
||||
`user`.update_by,
|
||||
`user`.update_time,
|
||||
`user`.remark_,
|
||||
`user`.identity_source_id,
|
||||
`user`.last_update_password_time,
|
||||
`user_detail`.id_type,
|
||||
`user_detail`.id_card,
|
||||
`user_detail`.website_,
|
||||
`user_detail`.address_,
|
||||
GROUP_CONCAT( DISTINCT `organization_member`.org_id SEPARATOR ',' ) AS organization_ids,
|
||||
CONCAT( '{', GROUP_CONCAT( DISTINCT CONCAT( '"', user_group.id_, '": "', user_group.name_, '"' ) SEPARATOR ',' ), '}' ) AS user_groups
|
||||
FROM
|
||||
`user`
|
||||
LEFT JOIN `user_detail` ON `user`.id_ = user_detail.user_id
|
||||
AND user_detail.is_deleted = '0'
|
||||
LEFT JOIN `organization_member` ON `user`.id_ = organization_member.user_id
|
||||
AND organization_member.is_deleted = '0'
|
||||
LEFT JOIN `user_group_member` ON `user`.id_ = user_group_member.user_id
|
||||
AND user_group_member.is_deleted = '0'
|
||||
LEFT JOIN `user_group` ON `user_group`.id_ = user_group_member.group_id
|
||||
AND user_group.is_deleted = '0'
|
||||
WHERE
|
||||
`user`.is_deleted = 0 %s
|
||||
GROUP BY
|
||||
`user`.id_,
|
||||
`user`.username_,
|
||||
`user`.email_,
|
||||
`user`.phone_,
|
||||
`user`.phone_area_code,
|
||||
`user`.full_name,
|
||||
`user`.nick_name,
|
||||
`user`.avatar_,
|
||||
`user`.status_,
|
||||
`user`.data_origin,
|
||||
`user`.email_verified,
|
||||
`user`.phone_verified,
|
||||
`user`.auth_total,
|
||||
`user`.last_auth_ip,
|
||||
`user`.last_auth_time,
|
||||
`user`.expand_,
|
||||
`user`.external_id,
|
||||
`user`.expire_date,
|
||||
`user`.create_by,
|
||||
`user`.create_time,
|
||||
`user`.update_by,
|
||||
`user`.update_time,
|
||||
`user`.remark_,
|
||||
`user`.identity_source_id,
|
||||
`user`.last_update_password_time
|
||||
""".formatted(whereUserId);
|
||||
//@formatter:on
|
||||
return jdbcTemplate.query(sql, new UserEsMapper());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserElasticSearchEntity> getAllUserElasticSearchEntity(IndexCoordinates userIndex) {
|
||||
return getAllUserElasticSearchEntity(userIndex,
|
||||
QueryBuilders.matchAll().build()._toQuery());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserElasticSearchEntity> getAllUserElasticSearchEntity(IndexCoordinates userIndex,
|
||||
Query query) {
|
||||
if (!elasticsearchTemplate.indexOps(userIndex).exists()) {
|
||||
return null;
|
||||
}
|
||||
NativeQuery searchQuery = new NativeQueryBuilder().withQuery(query)
|
||||
// 设置每页数据量
|
||||
.withPageable(PageRequest.of(0, 2000)).build();
|
||||
List<UserElasticSearchEntity> userElasticSearchEntityList = new ArrayList<>();
|
||||
SearchHitsIterator<UserElasticSearchEntity> searchScrollHits = elasticsearchTemplate
|
||||
.searchForStream(searchQuery, UserElasticSearchEntity.class, userIndex);
|
||||
while (searchScrollHits.hasNext()) {
|
||||
SearchHit<UserElasticSearchEntity> next = searchScrollHits.next();
|
||||
userElasticSearchEntityList.add(next.getContent());
|
||||
}
|
||||
return userElasticSearchEntityList;
|
||||
}
|
||||
|
||||
/**
|
||||
* JdbcTemplate
|
||||
*/
|
||||
|
|
|
@ -1,111 +0,0 @@
|
|||
/*
|
||||
* eiam-common - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.common.repository.account.impl.mapper;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import cn.topiam.employee.common.entity.account.po.UserEsPO;
|
||||
import cn.topiam.employee.common.enums.DataOrigin;
|
||||
import cn.topiam.employee.common.enums.UserStatus;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2023/5/30 22:25
|
||||
*/
|
||||
@Slf4j
|
||||
public class UserEsMapper implements RowMapper<UserEsPO> {
|
||||
/**
|
||||
* Implementations must implement this method to map each row of data
|
||||
* in the ResultSet. This method should not call {@code next()} on
|
||||
* the ResultSet; it is only supposed to map values of the current row.
|
||||
*
|
||||
* @param rs the ResultSet to map (pre-initialized for the current row)
|
||||
* @param rowNum the number of the current row
|
||||
* @return the result object for the current row (may be {@code null})
|
||||
* @throws SQLException if an SQLException is encountered getting
|
||||
* column values (that is, there's no need to catch SQLException)
|
||||
*/
|
||||
@Override
|
||||
public UserEsPO mapRow(@NonNull ResultSet rs, int rowNum) throws SQLException {
|
||||
//@formatter:off
|
||||
UserEsPO user = new UserEsPO();
|
||||
user.setId(rs.getLong("id_"));
|
||||
user.setUsername(rs.getString("username_"));
|
||||
user.setEmail(rs.getString("email_"));
|
||||
user.setPhone(rs.getString("phone_"));
|
||||
user.setPhoneAreaCode(rs.getString("phone_area_code"));
|
||||
user.setFullName(rs.getString("full_name"));
|
||||
user.setNickName(rs.getString("nick_name"));
|
||||
user.setAvatar(rs.getString("avatar_"));
|
||||
user.setStatus(UserStatus.getStatus(rs.getString("status_")));
|
||||
user.setDataOrigin(DataOrigin.getType(rs.getString("data_origin")));
|
||||
user.setEmailVerified(rs.getBoolean("email_verified"));
|
||||
user.setAuthTotal(rs.getLong("auth_total"));
|
||||
user.setLastAuthIp(rs.getString("last_auth_ip"));
|
||||
user.setLastAuthTime(ObjectUtils.isNotEmpty(rs.getTimestamp("last_auth_time")) ? rs.getTimestamp("last_auth_time").toLocalDateTime() : null);
|
||||
user.setExpand(rs.getString("expand_"));
|
||||
user.setExternalId(rs.getString("external_id"));
|
||||
user.setIdentitySourceId(rs.getLong("identity_source_id"));
|
||||
user.setExpireDate(ObjectUtils.isNotEmpty(rs.getTimestamp("expire_date")) ? rs.getDate("expire_date").toLocalDate() : null);
|
||||
user.setLastAuthTime(ObjectUtils.isNotEmpty(rs.getTimestamp("last_auth_time")) ? rs.getTimestamp("last_auth_time").toLocalDateTime() : null);
|
||||
user.setCreateBy(rs.getString("create_by"));
|
||||
user.setCreateTime(ObjectUtils.isNotEmpty(rs.getTimestamp("create_time")) ? rs.getTimestamp("create_time").toLocalDateTime() : null);
|
||||
user.setUpdateBy(rs.getString("update_by"));
|
||||
user.setUpdateTime(ObjectUtils.isNotEmpty(rs.getTimestamp("update_time")) ? rs.getTimestamp("update_time").toLocalDateTime() : null);
|
||||
user.setRemark(rs.getString("remark_"));
|
||||
user.setLastUpdatePasswordTime(ObjectUtils.isNotEmpty(rs.getTimestamp("last_update_password_time")) ? rs.getTimestamp("last_update_password_time").toLocalDateTime() : null);
|
||||
// 额外数据
|
||||
user.setIdType(rs.getString("id_type"));
|
||||
user.setIdCard(rs.getString("id_card"));
|
||||
user.setWebsite(rs.getString("website_"));
|
||||
user.setAddress(rs.getString("address_"));
|
||||
String organizationIds = rs.getString("organization_ids");
|
||||
if (StringUtils.isNotBlank(organizationIds)) {
|
||||
user.setOrganizationIds(Set.of((organizationIds.split(","))));
|
||||
}
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
String userGroups = rs.getString("user_groups");
|
||||
if (StringUtils.isNotBlank(userGroups)) {
|
||||
Map<String, String> userGroupMap;
|
||||
try {
|
||||
userGroupMap = objectMapper.readValue(userGroups, new TypeReference<>() {
|
||||
});
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("解析用户组字段json异常:[{}]", e.getMessage());
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
user.setUserGroups(userGroupMap);
|
||||
}
|
||||
return user;
|
||||
//@formatter:on
|
||||
}
|
||||
}
|
|
@ -24,14 +24,14 @@ import org.apache.commons.lang3.ObjectUtils;
|
|||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
import cn.topiam.employee.common.entity.account.po.UserIdpBindPo;
|
||||
import cn.topiam.employee.common.entity.account.po.UserIdpBindPO;
|
||||
|
||||
/**
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2022/2/13 22:25
|
||||
*/
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
public class UserIdpBindPoMapper implements RowMapper<UserIdpBindPo> {
|
||||
public class UserIdpBindPoMapper implements RowMapper<UserIdpBindPO> {
|
||||
/**
|
||||
* Implementations must implement this method to map each row of data
|
||||
* in the ResultSet. This method should not call {@code next()} on
|
||||
|
@ -44,9 +44,9 @@ public class UserIdpBindPoMapper implements RowMapper<UserIdpBindPo> {
|
|||
* column values (that is, there's no need to catch SQLException)
|
||||
*/
|
||||
@Override
|
||||
public UserIdpBindPo mapRow(@NonNull ResultSet rs, int rowNum) throws SQLException {
|
||||
public UserIdpBindPO mapRow(@NonNull ResultSet rs, int rowNum) throws SQLException {
|
||||
//@formatter:off
|
||||
UserIdpBindPo userIdpBindPo = new UserIdpBindPo();
|
||||
UserIdpBindPO userIdpBindPo = new UserIdpBindPO();
|
||||
userIdpBindPo.setId(rs.getLong("id_"));
|
||||
userIdpBindPo.setUserId(rs.getLong("user_id"));
|
||||
userIdpBindPo.setOpenId(rs.getString("open_id"));
|
||||
|
|
|
@ -70,6 +70,7 @@ public class UserPoMapper implements RowMapper<UserPO> {
|
|||
user.setExpireDate(ObjectUtils.isNotEmpty(rs.getTimestamp("expire_date")) ? rs.getDate("expire_date").toLocalDate() : null);
|
||||
user.setLastAuthTime(ObjectUtils.isNotEmpty(rs.getTimestamp("last_auth_time")) ? rs.getTimestamp("last_auth_time").toLocalDateTime() : null);
|
||||
user.setOrgDisplayPath(rs.getString("org_display_path"));
|
||||
user.setPrimaryOrgDisplayPath(rs.getString("primary_org_display_path"));
|
||||
//额外数据
|
||||
user.setCreateBy(rs.getString("create_by"));
|
||||
user.setCreateTime(ObjectUtils.isNotEmpty(rs.getTimestamp("create_time")) ? rs.getTimestamp("create_time").toLocalDateTime() : null);
|
||||
|
|
|
@ -63,7 +63,7 @@ public interface AdministratorRepository extends LogicDeleteRepository<Administr
|
|||
*/
|
||||
@NotNull
|
||||
@Cacheable
|
||||
@Query(value = "SELECT * FROM administrator WHERE id_ = :id", nativeQuery = true)
|
||||
@Query(value = "SELECT AdministratorEntity FROM AdministratorEntity WHERE id = :id")
|
||||
Optional<AdministratorEntity> findByIdContainsDeleted(@NotNull @Param(value = "id") Long id);
|
||||
|
||||
/**
|
||||
|
@ -89,7 +89,6 @@ public interface AdministratorRepository extends LogicDeleteRepository<Administr
|
|||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @return {@link AdministratorEntity}
|
||||
* @param <S>
|
||||
*/
|
||||
@Override
|
||||
@CacheEvict(allEntries = true)
|
||||
|
@ -128,7 +127,7 @@ public interface AdministratorRepository extends LogicDeleteRepository<Administr
|
|||
@Transactional(rollbackFor = Exception.class)
|
||||
@Modifying
|
||||
@CacheEvict(allEntries = true)
|
||||
@Query(value = "update administrator set status_ = ?2 where id_ = ?1", nativeQuery = true)
|
||||
@Query(value = "update AdministratorEntity set status = :status where id = :id")
|
||||
void updateStatus(@Param(value = "id") String id, @Param(value = "status") String status);
|
||||
|
||||
/**
|
||||
|
@ -140,7 +139,7 @@ public interface AdministratorRepository extends LogicDeleteRepository<Administr
|
|||
@Transactional(rollbackFor = Exception.class)
|
||||
@Modifying
|
||||
@CacheEvict(allEntries = true)
|
||||
@Query(value = "update administrator set password_ = ?2 where id_ = ?1", nativeQuery = true)
|
||||
@Query(value = "update AdministratorEntity set password = :password where id = :id")
|
||||
void updatePassword(@Param(value = "id") String id, @Param(value = "password") String password);
|
||||
|
||||
/**
|
||||
|
|
|
@ -82,11 +82,22 @@
|
|||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<!-- 添加字段 -->
|
||||
<addColumn tableName="organization_member">
|
||||
<column name="primary_" remarks="主组织" type="TINYINT(1)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
<!--创建索引-->
|
||||
<createIndex tableName="app_group_association" indexName="uk_app_group_association" unique="true">
|
||||
<column name="group_id"/>
|
||||
<column name="app_id"/>
|
||||
<column name="is_deleted"/>
|
||||
</createIndex>
|
||||
<createIndex tableName="organization_member" indexName="uk_organization_member_primary" unique="true">
|
||||
<column name="user_id"/>
|
||||
<column name="primary_"/>
|
||||
<column name="is_deleted"/>
|
||||
</createIndex>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
|
@ -148,7 +148,7 @@
|
|||
<phase>generate-resources</phase>
|
||||
<configuration>
|
||||
<nodeVersion>v18.17.1</nodeVersion>
|
||||
<pnpmVersion>v8.7.0</pnpmVersion>
|
||||
<pnpmVersion>v8.8.0</pnpmVersion>
|
||||
<nodeDownloadRoot>https://npm.taobao.org/mirrors/node/</nodeDownloadRoot>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
@ -201,7 +201,7 @@
|
|||
<phase>generate-resources</phase>
|
||||
<configuration>
|
||||
<nodeVersion>v18.17.1</nodeVersion>
|
||||
<pnpmVersion>v8.7.0</pnpmVersion>
|
||||
<pnpmVersion>v8.8.0</pnpmVersion>
|
||||
<nodeDownloadRoot>https://npm.taobao.org/mirrors/node/</nodeDownloadRoot>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
|
|
@ -75,7 +75,7 @@
|
|||
"rc-select": "^14.9.0",
|
||||
"rc-tree": "^5.7.12",
|
||||
"react": "^18.2.0",
|
||||
"react-codemirror2": "^7.2.1",
|
||||
"react-codemirror2": "^7.3.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-fast-marquee": "^1.6.1",
|
||||
"react-helmet-async": "^1.3.0",
|
||||
|
@ -92,8 +92,8 @@
|
|||
"@types/lodash": "^4.14.199",
|
||||
"@types/numeral": "^2.0.3",
|
||||
"@types/qs": "^6.9.8",
|
||||
"@types/react": "^18.2.23",
|
||||
"@types/react-dom": "^18.2.8",
|
||||
"@types/react": "^18.2.25",
|
||||
"@types/react-dom": "^18.2.10",
|
||||
"@types/react-helmet": "^6.1.7",
|
||||
"@umijs/lint": "^4.0.83",
|
||||
"@umijs/max": "^4.0.83",
|
||||
|
|
|
@ -39,6 +39,8 @@ import {
|
|||
Table,
|
||||
Tooltip,
|
||||
Typography,
|
||||
Tag,
|
||||
Popover,
|
||||
} from 'antd';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import CreateUser from '../CreateUser';
|
||||
|
@ -182,6 +184,44 @@ export default (props: UserListProps) => {
|
|||
dataIndex: 'orgDisplayPath',
|
||||
search: false,
|
||||
ellipsis: true,
|
||||
render: (_, record) => [
|
||||
<Popover
|
||||
key="pop"
|
||||
title={
|
||||
<Tag color={'geekblue'} key={record.orgDisplayPath}>
|
||||
{record.primaryOrgDisplayPath}
|
||||
</Tag>
|
||||
}
|
||||
content={
|
||||
<Space direction="vertical" size="small" style={{ display: 'flex' }}>
|
||||
{record.orgDisplayPath.split(',')?.map((p: string) => {
|
||||
return (
|
||||
<Tag color={'green'} key={p}>
|
||||
{p}
|
||||
</Tag>
|
||||
);
|
||||
})}
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Space key="primary_path">
|
||||
{
|
||||
<Tag color={'geekblue'} key={record.primaryOrgDisplayPath}>
|
||||
{record.primaryOrgDisplayPath}
|
||||
</Tag>
|
||||
}
|
||||
</Space>
|
||||
<Space key="path" direction="vertical" size="small" style={{ display: 'flex' }}>
|
||||
{record.orgDisplayPath.split(',')?.map((p: string) => {
|
||||
return (
|
||||
<Tag color={'green'} key={p}>
|
||||
{p}
|
||||
</Tag>
|
||||
);
|
||||
})}
|
||||
</Space>
|
||||
</Popover>,
|
||||
],
|
||||
},
|
||||
{
|
||||
title: intl.formatMessage({ id: 'pages.account.user_list.user.columns.status' }),
|
||||
|
|
|
@ -273,7 +273,9 @@ export async function removeUser(id: string): Promise<API.ApiResult<boolean>> {
|
|||
/**
|
||||
* Remove Batch User
|
||||
*/
|
||||
export async function removeBatchUser(ids: (number | string)[]): Promise<API.ApiResult<boolean>> {
|
||||
export async function removeBatchUser(
|
||||
ids: (number | string | Key)[],
|
||||
): Promise<API.ApiResult<boolean>> {
|
||||
return request<API.ApiResult<boolean>>(`/api/v1/user/batch_delete`, {
|
||||
method: 'DELETE',
|
||||
params: { ids: ids },
|
||||
|
|
|
@ -28,7 +28,7 @@ import org.springframework.boot.web.servlet.ServletComponentScan;
|
|||
* Created by support@topiam.cn on 2020/7/9
|
||||
*/
|
||||
@ServletComponentScan
|
||||
@SpringBootApplication(scanBasePackages = { "cn.topiam.employee" })
|
||||
@SpringBootApplication
|
||||
public class EiamConsoleApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(EiamConsoleApplication.class, args);
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.console.access;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2023/5/24 23:16
|
||||
*/
|
||||
public interface DefaultAdministratorConstants {
|
||||
|
||||
/**
|
||||
* 默认管理员用户名
|
||||
*/
|
||||
String DEFAULT_ADMIN_USERNAME = "admin";
|
||||
}
|
|
@ -54,19 +54,14 @@ import cn.topiam.employee.audit.event.AuditEventPublish;
|
|||
import cn.topiam.employee.authentication.common.jackjson.AuthenticationJacksonModule;
|
||||
import cn.topiam.employee.common.constant.AuthorizeConstants;
|
||||
import cn.topiam.employee.common.entity.setting.SettingEntity;
|
||||
import cn.topiam.employee.common.repository.account.OrganizationRepository;
|
||||
import cn.topiam.employee.common.repository.account.UserElasticSearchRepository;
|
||||
import cn.topiam.employee.common.repository.account.UserRepository;
|
||||
import cn.topiam.employee.common.repository.setting.AdministratorRepository;
|
||||
import cn.topiam.employee.common.repository.setting.SettingRepository;
|
||||
import cn.topiam.employee.console.handler.*;
|
||||
import cn.topiam.employee.console.listener.ConsoleAuthenticationFailureEventListener;
|
||||
import cn.topiam.employee.console.listener.ConsoleAuthenticationSuccessEventListener;
|
||||
import cn.topiam.employee.console.listener.ConsoleLogoutSuccessEventListener;
|
||||
import cn.topiam.employee.console.listener.ConsoleSessionInformationExpiredStrategy;
|
||||
import cn.topiam.employee.core.dynamic.UserSyncTask;
|
||||
import cn.topiam.employee.console.security.handler.*;
|
||||
import cn.topiam.employee.console.security.listener.ConsoleAuthenticationFailureEventListener;
|
||||
import cn.topiam.employee.console.security.listener.ConsoleAuthenticationSuccessEventListener;
|
||||
import cn.topiam.employee.console.security.listener.ConsoleLogoutSuccessEventListener;
|
||||
import cn.topiam.employee.console.security.listener.ConsoleSessionInformationExpiredStrategy;
|
||||
import cn.topiam.employee.core.security.form.FormLoginSecretFilter;
|
||||
import cn.topiam.employee.support.autoconfiguration.SupportProperties;
|
||||
import cn.topiam.employee.support.geo.GeoLocationService;
|
||||
import cn.topiam.employee.support.jackjson.SupportJackson2Module;
|
||||
import cn.topiam.employee.support.security.authentication.WebAuthenticationDetailsSource;
|
||||
|
@ -357,24 +352,6 @@ public class ConsoleSecurityConfiguration implements BeanClassLoaderAware {
|
|||
return new GenericJackson2JsonRedisSerializer(mapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步es用户数据定时任务
|
||||
*
|
||||
* @param supportProperties {@link SupportProperties}
|
||||
* @param userElasticSearchRepository {@link UserElasticSearchRepository}
|
||||
* @param userRepository {@link UserRepository}
|
||||
* @param organizationRepository {@link OrganizationRepository}
|
||||
* @return {@link UserSyncTask}
|
||||
*/
|
||||
@Bean
|
||||
public UserSyncTask userSyncTask(SupportProperties supportProperties,
|
||||
UserElasticSearchRepository userElasticSearchRepository,
|
||||
UserRepository userRepository,
|
||||
OrganizationRepository organizationRepository) {
|
||||
return new UserSyncTask(supportProperties, userElasticSearchRepository, userRepository,
|
||||
organizationRepository);
|
||||
}
|
||||
|
||||
/**
|
||||
* WebAuthenticationDetailsSource
|
||||
*
|
||||
|
|
|
@ -25,6 +25,8 @@ import org.springframework.web.bind.annotation.GetMapping;
|
|||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import cn.topiam.employee.audit.repository.result.AuthnQuantityResult;
|
||||
import cn.topiam.employee.audit.repository.result.AuthnZoneResult;
|
||||
import cn.topiam.employee.console.pojo.query.analysis.AnalysisQuery;
|
||||
import cn.topiam.employee.console.pojo.result.analysis.*;
|
||||
import cn.topiam.employee.console.service.analysis.AnalysisService;
|
||||
|
|
|
@ -22,7 +22,7 @@ import java.util.List;
|
|||
|
||||
import org.mapstruct.Mapper;
|
||||
|
||||
import cn.topiam.employee.common.entity.account.po.UserIdpBindPo;
|
||||
import cn.topiam.employee.common.entity.account.po.UserIdpBindPO;
|
||||
import cn.topiam.employee.console.pojo.result.app.UserIdpBindListResult;
|
||||
import cn.topiam.employee.support.repository.page.domain.Page;
|
||||
|
||||
|
@ -41,9 +41,9 @@ public interface UserIdpBindConverter {
|
|||
* @param page {@link Page}
|
||||
* @return {@link Page}
|
||||
*/
|
||||
default List<UserIdpBindListResult> userIdpBindEntityConvertToUserIdpBindListResult(Iterable<UserIdpBindPo> page) {
|
||||
default List<UserIdpBindListResult> userIdpBindEntityConvertToUserIdpBindListResult(Iterable<UserIdpBindPO> page) {
|
||||
List<UserIdpBindListResult> list = new ArrayList<>();
|
||||
for (UserIdpBindPo entity : page) {
|
||||
for (UserIdpBindPO entity : page) {
|
||||
list.add(entityConvertToAppAccountResult(entity));
|
||||
}
|
||||
return list;
|
||||
|
@ -52,8 +52,8 @@ public interface UserIdpBindConverter {
|
|||
/**
|
||||
* 用户身份提供商绑定关系转换结果
|
||||
*
|
||||
* @param userIdpBindPo {@link UserIdpBindPo}
|
||||
* @param userIdpBindPo {@link UserIdpBindPO}
|
||||
* @return {@link UserIdpBindListResult}
|
||||
*/
|
||||
UserIdpBindListResult entityConvertToAppAccountResult(UserIdpBindPo userIdpBindPo);
|
||||
UserIdpBindListResult entityConvertToAppAccountResult(UserIdpBindPO userIdpBindPo);
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ public interface AdministratorConverter {
|
|||
* @param page {@link AdministratorEntity}
|
||||
* @return {@link AdministratorListResult}
|
||||
*/
|
||||
@Mapping(target = "initialized", expression = "java(page.getUsername().equals(cn.topiam.employee.console.access.DefaultAdministratorConstants.DEFAULT_ADMIN_USERNAME))")
|
||||
@Mapping(target = "initialized", expression = "java(page.getUsername().equals(cn.topiam.employee.common.constant.SecurityConstants.DEFAULT_ADMIN_USERNAME))")
|
||||
@Mapping(target = "status", source = "status.code")
|
||||
@Mapping(target = "emailVerified", source = "emailVerified", defaultValue = "false")
|
||||
@Mapping(target = "authTotal", source = "authTotal", defaultValue = "0L")
|
||||
|
@ -195,7 +195,7 @@ public interface AdministratorConverter {
|
|||
* @return {@link AdministratorResult} 管理员详情
|
||||
*/
|
||||
@Mapping(target = "status", source = "status.code")
|
||||
@Mapping(target = "initialized", expression = "java(user.getUsername().equals(cn.topiam.employee.console.access.DefaultAdministratorConstants.DEFAULT_ADMIN_USERNAME))")
|
||||
@Mapping(target = "initialized", expression = "java(user.getUsername().equals(cn.topiam.employee.common.constant.SecurityConstants.DEFAULT_ADMIN_USERNAME))")
|
||||
AdministratorResult entityConvertToAdministratorDetailsResult(AdministratorEntity user);
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.console.initialize;
|
||||
package cn.topiam.employee.console.initializer;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
|
@ -42,7 +42,7 @@ import cn.topiam.employee.common.entity.setting.AdministratorEntity;
|
|||
import cn.topiam.employee.common.enums.UserStatus;
|
||||
import cn.topiam.employee.common.repository.setting.AdministratorRepository;
|
||||
import cn.topiam.employee.support.trace.TraceUtils;
|
||||
import static cn.topiam.employee.console.access.DefaultAdministratorConstants.DEFAULT_ADMIN_USERNAME;
|
||||
import static cn.topiam.employee.common.constant.SecurityConstants.DEFAULT_ADMIN_USERNAME;
|
||||
import static cn.topiam.employee.support.lock.LockAspect.getTopiamLockKeyPrefix;
|
||||
import static cn.topiam.employee.support.util.CreateFileUtil.createFile;
|
||||
|
||||
|
@ -54,10 +54,10 @@ import static cn.topiam.employee.support.util.CreateFileUtil.createFile;
|
|||
*/
|
||||
@Order(2)
|
||||
@Component
|
||||
public class DefaultAdministratorInitialize implements ApplicationListener<ContextRefreshedEvent> {
|
||||
public class DefaultAdministratorInitializer implements ApplicationListener<ContextRefreshedEvent> {
|
||||
|
||||
private final Logger logger = LoggerFactory
|
||||
.getLogger(DefaultAdministratorInitialize.class);
|
||||
.getLogger(DefaultAdministratorInitializer.class);
|
||||
private static final String DIR_NAME = ".topiam";
|
||||
private static final String USER_HOME = "user.home";
|
||||
|
||||
|
@ -158,7 +158,7 @@ public class DefaultAdministratorInitialize implements ApplicationListener<Conte
|
|||
|
||||
private final RedissonClient redissonClient;
|
||||
|
||||
public DefaultAdministratorInitialize(AdministratorRepository administratorRepository,
|
||||
public DefaultAdministratorInitializer(AdministratorRepository administratorRepository,
|
||||
PasswordEncoder passwordEncoder,
|
||||
RedissonClient redissonClient) {
|
||||
this.administratorRepository = administratorRepository;
|
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.console.initialize;
|
||||
package cn.topiam.employee.console.initializer;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
@ -47,7 +47,7 @@ import static cn.topiam.employee.support.lock.LockAspect.getTopiamLockKeyPrefix;
|
|||
*/
|
||||
@Order(2)
|
||||
@Component
|
||||
public class DefaultAppGroupInitialize implements ApplicationListener<ContextRefreshedEvent> {
|
||||
public class DefaultAppGroupInitializer implements ApplicationListener<ContextRefreshedEvent> {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
|
@ -94,7 +94,7 @@ public class DefaultAppGroupInitialize implements ApplicationListener<ContextRef
|
|||
|
||||
private final RedissonClient redissonClient;
|
||||
|
||||
public DefaultAppGroupInitialize(AppGroupRepository appGroupRepository,
|
||||
public DefaultAppGroupInitializer(AppGroupRepository appGroupRepository,
|
||||
RedissonClient redissonClient) {
|
||||
this.appGroupRepository = appGroupRepository;
|
||||
this.redissonClient = redissonClient;
|
|
@ -69,15 +69,15 @@ public class AnalysisQuery implements Serializable {
|
|||
/**
|
||||
* HOUR
|
||||
*/
|
||||
HOUR(CalendarInterval.Hour, "HH时"),
|
||||
HOUR(CalendarInterval.Hour, "%h时"),
|
||||
/**
|
||||
* DAY
|
||||
*/
|
||||
DAY(CalendarInterval.Day, "dd日"),
|
||||
DAY(CalendarInterval.Day, "%d日"),
|
||||
/**
|
||||
* MONTH
|
||||
*/
|
||||
MONTH(CalendarInterval.Month, "MM月");
|
||||
MONTH(CalendarInterval.Month, "%m月");
|
||||
|
||||
private final CalendarInterval type;
|
||||
private final String format;
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.console.handler;
|
||||
package cn.topiam.employee.console.security.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.console.handler;
|
||||
package cn.topiam.employee.console.security.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.console.handler;
|
||||
package cn.topiam.employee.console.security.handler;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.HttpStatus;
|
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.console.handler;
|
||||
package cn.topiam.employee.console.security.handler;
|
||||
|
||||
import java.util.List;
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.console.handler;
|
||||
package cn.topiam.employee.console.security.handler;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
|
@ -21,4 +21,4 @@
|
|||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2020/10/29 23:12
|
||||
*/
|
||||
package cn.topiam.employee.console.handler;
|
||||
package cn.topiam.employee.console.security.handler;
|
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.console.listener;
|
||||
package cn.topiam.employee.console.security.listener;
|
||||
|
||||
import java.util.Optional;
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.console.listener;
|
||||
package cn.topiam.employee.console.security.listener;
|
||||
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.lang.NonNull;
|
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.console.listener;
|
||||
package cn.topiam.employee.console.security.listener;
|
||||
|
||||
import java.util.List;
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.console.listener;
|
||||
package cn.topiam.employee.console.security.listener;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.web.session.SessionInformationExpiredEvent;
|
|
@ -15,4 +15,4 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee;
|
||||
package cn.topiam.employee.console.security.listener;
|
|
@ -15,4 +15,4 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.console;
|
||||
package cn.topiam.employee.console.security;
|
|
@ -21,7 +21,6 @@ import java.util.LinkedHashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
@ -34,18 +33,14 @@ import cn.topiam.employee.audit.context.AuditContext;
|
|||
import cn.topiam.employee.audit.entity.Target;
|
||||
import cn.topiam.employee.audit.enums.TargetType;
|
||||
import cn.topiam.employee.common.entity.account.OrganizationEntity;
|
||||
import cn.topiam.employee.common.entity.account.OrganizationMemberEntity;
|
||||
import cn.topiam.employee.common.entity.account.QUserEntity;
|
||||
import cn.topiam.employee.common.enums.DataOrigin;
|
||||
import cn.topiam.employee.common.repository.account.OrganizationMemberRepository;
|
||||
import cn.topiam.employee.common.repository.account.OrganizationRepository;
|
||||
import cn.topiam.employee.console.converter.account.OrganizationConverter;
|
||||
import cn.topiam.employee.console.pojo.result.account.*;
|
||||
import cn.topiam.employee.console.pojo.save.account.OrganizationCreateParam;
|
||||
import cn.topiam.employee.console.pojo.update.account.OrganizationUpdateParam;
|
||||
import cn.topiam.employee.console.service.account.OrganizationService;
|
||||
import cn.topiam.employee.core.mq.UserMessagePublisher;
|
||||
import cn.topiam.employee.core.mq.UserMessageTag;
|
||||
import cn.topiam.employee.support.repository.id.SnowflakeIdGenerator;
|
||||
import cn.topiam.employee.support.util.BeanUtils;
|
||||
|
||||
|
@ -116,7 +111,6 @@ public class OrganizationServiceImpl implements OrganizationService {
|
|||
Optional<OrganizationEntity> optional = this.organizationRepository.findById(param.getId());
|
||||
if (optional.isPresent()) {
|
||||
OrganizationEntity entity = optional.get();
|
||||
String userIds;
|
||||
//如果修改了名字,递归修改和该组织有关所有节点信息的展示路径
|
||||
if (!optional.get().getName().equals(param.getName())) {
|
||||
//修改名称
|
||||
|
@ -126,22 +120,10 @@ public class OrganizationServiceImpl implements OrganizationService {
|
|||
if (!entity.getLeaf()) {
|
||||
recursiveUpdateDisplayPath(entity.getId(), entity.getId(), param.getName());
|
||||
}
|
||||
userIds = organizationRepository
|
||||
.getOrgMemberList(organization.getId(), QUserEntity.userEntity.id).stream()
|
||||
.map(String::valueOf).collect(Collectors.joining(","));
|
||||
} else {
|
||||
List<OrganizationMemberEntity> orgMemberList = organizationMemberRepository
|
||||
.findAllByOrgId(entity.getId());
|
||||
userIds = orgMemberList.stream().map(item -> String.valueOf(item.getUserId()))
|
||||
.collect(Collectors.joining(","));
|
||||
}
|
||||
//修改
|
||||
BeanUtils.merge(organization, entity, LAST_MODIFIED_BY, LAST_MODIFIED_TIME);
|
||||
organizationRepository.save(entity);
|
||||
// 更新用户es信息
|
||||
if (StringUtils.isNotBlank(userIds)) {
|
||||
userMessagePublisher.sendUserChangeMessage(UserMessageTag.SAVE, userIds);
|
||||
}
|
||||
AuditContext.setTarget(
|
||||
Target.builder().id(entity.getId()).type(TargetType.ORGANIZATION).build());
|
||||
return true;
|
||||
|
@ -259,9 +241,7 @@ public class OrganizationServiceImpl implements OrganizationService {
|
|||
@Override
|
||||
public OrganizationResult getOrganization(String id) {
|
||||
Optional<OrganizationEntity> entity = organizationRepository.findById(id);
|
||||
OrganizationResult organizationResult = entity
|
||||
.map(organizationConverter::entityConvertToGetOrganizationResult).orElse(null);
|
||||
return organizationResult;
|
||||
return entity.map(organizationConverter::entityConvertToGetOrganizationResult).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -315,13 +295,6 @@ public class OrganizationServiceImpl implements OrganizationService {
|
|||
.name(organization.get().getName()).build());
|
||||
//存在子组织,递归更改子组织 path 和 displayPath
|
||||
recursiveUpdateChildNodePathAndDisplayPath(entity.getId());
|
||||
// 更新用户es信息
|
||||
String userIds = organizationRepository
|
||||
.getOrgMemberList(entity.getId(), QUserEntity.userEntity.id).stream()
|
||||
.map(String::valueOf).collect(Collectors.joining(","));
|
||||
if (StringUtils.isNotBlank(userIds)) {
|
||||
userMessagePublisher.sendUserChangeMessage(UserMessageTag.SAVE, userIds);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -465,13 +438,4 @@ public class OrganizationServiceImpl implements OrganizationService {
|
|||
*/
|
||||
private final OrganizationRepository organizationRepository;
|
||||
|
||||
/**
|
||||
* MessagePublisher
|
||||
*/
|
||||
private final UserMessagePublisher userMessagePublisher;
|
||||
|
||||
/**
|
||||
* OrganizationMemberRepository
|
||||
*/
|
||||
private final OrganizationMemberRepository organizationMemberRepository;
|
||||
}
|
||||
|
|
|
@ -21,12 +21,10 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.querydsl.QPageRequest;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.querydsl.core.types.ExpressionUtils;
|
||||
|
@ -49,8 +47,6 @@ import cn.topiam.employee.console.pojo.result.account.UserGroupMemberListResult;
|
|||
import cn.topiam.employee.console.pojo.save.account.UserGroupCreateParam;
|
||||
import cn.topiam.employee.console.pojo.update.account.UserGroupUpdateParam;
|
||||
import cn.topiam.employee.console.service.account.UserGroupService;
|
||||
import cn.topiam.employee.core.mq.UserMessagePublisher;
|
||||
import cn.topiam.employee.core.mq.UserMessageTag;
|
||||
import cn.topiam.employee.support.exception.TopIamException;
|
||||
import cn.topiam.employee.support.repository.page.domain.Page;
|
||||
import cn.topiam.employee.support.repository.page.domain.PageModel;
|
||||
|
@ -118,14 +114,6 @@ public class UserGroupServiceImpl implements UserGroupService {
|
|||
UserGroupEntity details = getUserGroup(Long.valueOf(param.getId()));
|
||||
BeanUtils.merge(entity, details, LAST_MODIFIED_TIME, LAST_MODIFIED_BY);
|
||||
userGroupRepository.save(details);
|
||||
// 更新用户es信息
|
||||
List<UserGroupMemberEntity> userGroupMemberList = userGroupMemberRepository
|
||||
.findByGroupId(details.getId());
|
||||
if (!CollectionUtils.isEmpty(userGroupMemberList)) {
|
||||
userMessagePublisher.sendUserChangeMessage(UserMessageTag.SAVE,
|
||||
userGroupMemberList.stream().map(item -> String.valueOf(item.getUserId()))
|
||||
.collect(Collectors.joining(",")));
|
||||
}
|
||||
AuditContext.setTarget(
|
||||
Target.builder().id(details.getId().toString()).type(TargetType.USER_GROUP).build());
|
||||
return true;
|
||||
|
@ -183,8 +171,6 @@ public class UserGroupServiceImpl implements UserGroupService {
|
|||
public Boolean removeMember(String id, String userId) {
|
||||
//查询关联关系
|
||||
userGroupMemberRepository.deleteByGroupIdAndUserId(Long.valueOf(id), Long.valueOf(userId));
|
||||
// 更新用户es用户组信息
|
||||
userMessagePublisher.sendUserChangeMessage(UserMessageTag.SAVE, userId);
|
||||
AuditContext.setTarget(Target.builder().id(userId).type(TargetType.USER).build(),
|
||||
Target.builder().id(id).type(TargetType.USER_GROUP).build());
|
||||
return true;
|
||||
|
@ -215,8 +201,6 @@ public class UserGroupServiceImpl implements UserGroupService {
|
|||
});
|
||||
//添加
|
||||
userGroupMemberRepository.saveAll(list);
|
||||
// 更新用户es用户组信息
|
||||
userMessagePublisher.sendUserChangeMessage(UserMessageTag.SAVE, String.join(",", userIds));
|
||||
List<Target> targets = new ArrayList<>(Arrays.stream(userIds)
|
||||
.map(i -> Target.builder().id(i).type(TargetType.USER).build()).toList());
|
||||
|
||||
|
@ -254,8 +238,6 @@ public class UserGroupServiceImpl implements UserGroupService {
|
|||
}
|
||||
userIds.forEach(userId -> userGroupMemberRepository
|
||||
.deleteByGroupIdAndUserId(Long.valueOf(id), Long.valueOf(userId)));
|
||||
// 更新用户es用户组信息
|
||||
userMessagePublisher.sendUserChangeMessage(UserMessageTag.SAVE, String.join(",", userIds));
|
||||
List<Target> targets = new ArrayList<>(userIds.stream()
|
||||
.map(i -> Target.builder().id(i).type(TargetType.USER).build()).toList());
|
||||
|
||||
|
@ -301,9 +283,4 @@ public class UserGroupServiceImpl implements UserGroupService {
|
|||
* UserGroupMemberRepository
|
||||
*/
|
||||
private final UserGroupMemberRepository userGroupMemberRepository;
|
||||
|
||||
/**
|
||||
* MessagePublisher
|
||||
*/
|
||||
private final UserMessagePublisher userMessagePublisher;
|
||||
}
|
||||
|
|
|
@ -61,8 +61,6 @@ import cn.topiam.employee.console.service.account.UserService;
|
|||
import cn.topiam.employee.core.message.MsgVariable;
|
||||
import cn.topiam.employee.core.message.mail.MailMsgEventPublish;
|
||||
import cn.topiam.employee.core.message.sms.SmsMsgEventPublish;
|
||||
import cn.topiam.employee.core.mq.UserMessagePublisher;
|
||||
import cn.topiam.employee.core.mq.UserMessageTag;
|
||||
import cn.topiam.employee.support.autoconfiguration.SupportProperties;
|
||||
import cn.topiam.employee.support.exception.BadParamsException;
|
||||
import cn.topiam.employee.support.exception.InfoValidityFailException;
|
||||
|
@ -200,12 +198,7 @@ public class UserServiceImpl implements UserService {
|
|||
throw new TopIamException(AuditContext.getContent());
|
||||
}
|
||||
AuditContext.setTarget(Target.builder().id(id.toString()).type(TargetType.USER).build());
|
||||
boolean update = userRepository.updateUserStatus(id, status) > 0;
|
||||
if (update) {
|
||||
// 更新索引数据
|
||||
userMessagePublisher.sendUserChangeMessage(UserMessageTag.SAVE, String.valueOf(id));
|
||||
}
|
||||
return update;
|
||||
return userRepository.updateUserStatus(id, status) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -258,9 +251,6 @@ public class UserServiceImpl implements UserService {
|
|||
organizationMemberRepository.save(member);
|
||||
AuditContext.setTarget(Target.builder().type(USER).id(user.getId().toString()).build(),
|
||||
Target.builder().type(USER_DETAIL).id(detail.getId().toString()).build());
|
||||
// 保存ES用户信息
|
||||
userMessagePublisher.sendUserChangeMessage(UserMessageTag.SAVE,
|
||||
String.valueOf(user.getId()));
|
||||
// 发送短信和邮件的欢迎信息(密码通知)
|
||||
UserCreateParam.PasswordInitializeConfig passwordInitializeConfig = param
|
||||
.getPasswordInitializeConfig();
|
||||
|
@ -369,9 +359,6 @@ public class UserServiceImpl implements UserService {
|
|||
userDetailsRepository.save(detail);
|
||||
AuditContext.setTarget(Target.builder().type(USER).id(user.getId().toString()).build(),
|
||||
Target.builder().type(USER_DETAIL).id(detail.getId().toString()).build());
|
||||
// 更新ES用户信息
|
||||
userMessagePublisher.sendUserChangeMessage(UserMessageTag.SAVE,
|
||||
String.valueOf(user.getId()));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -400,8 +387,6 @@ public class UserServiceImpl implements UserService {
|
|||
//删除用户组用户详情
|
||||
userGroupMemberRepository.deleteByUserId(Long.valueOf(id));
|
||||
AuditContext.setTarget(Target.builder().id(id).type(TargetType.USER).build());
|
||||
// 删除ES用户信息
|
||||
userMessagePublisher.sendUserChangeMessage(UserMessageTag.DELETE, id);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -445,8 +430,6 @@ public class UserServiceImpl implements UserService {
|
|||
organizationMemberRepository.deleteAllByUserId(idList);
|
||||
//删除用户组关系
|
||||
userGroupMemberRepository.deleteAllByUserId(idList);
|
||||
// 批量删除ES用户信息
|
||||
userMessagePublisher.sendUserChangeMessage(UserMessageTag.DELETE, String.join(",", ids));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -614,8 +597,4 @@ public class UserServiceImpl implements UserService {
|
|||
*/
|
||||
private final PasswordPolicyManager<UserEntity> passwordPolicyManager;
|
||||
|
||||
/**
|
||||
* MessagePublisher
|
||||
*/
|
||||
private final UserMessagePublisher userMessagePublisher;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ package cn.topiam.employee.console.service.analysis;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import cn.topiam.employee.audit.repository.result.AuthnQuantityResult;
|
||||
import cn.topiam.employee.audit.repository.result.AuthnZoneResult;
|
||||
import cn.topiam.employee.console.pojo.query.analysis.AnalysisQuery;
|
||||
import cn.topiam.employee.console.pojo.result.analysis.*;
|
||||
|
||||
|
|
|
@ -24,14 +24,15 @@ import java.util.*;
|
|||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.data.elasticsearch.client.elc.*;
|
||||
import org.springframework.data.elasticsearch.core.SearchHits;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import cn.topiam.employee.audit.entity.AuditElasticSearchEntity;
|
||||
import cn.topiam.employee.audit.enums.EventStatus;
|
||||
import cn.topiam.employee.audit.event.type.EventType;
|
||||
import cn.topiam.employee.authentication.common.IdentityProviderType;
|
||||
import cn.topiam.employee.audit.repository.AuditRepository;
|
||||
import cn.topiam.employee.audit.repository.result.AuditStatisticsResult;
|
||||
import cn.topiam.employee.audit.repository.result.AuthnQuantityResult;
|
||||
import cn.topiam.employee.audit.repository.result.AuthnZoneResult;
|
||||
import cn.topiam.employee.common.entity.app.AppEntity;
|
||||
import cn.topiam.employee.common.repository.account.UserRepository;
|
||||
import cn.topiam.employee.common.repository.app.AppRepository;
|
||||
|
@ -44,17 +45,12 @@ import cn.topiam.employee.support.autoconfiguration.SupportProperties;
|
|||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import co.elastic.clients.elasticsearch._types.aggregations.*;
|
||||
import co.elastic.clients.elasticsearch._types.aggregations.Aggregation;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders;
|
||||
import co.elastic.clients.json.JsonData;
|
||||
import static cn.topiam.employee.audit.entity.Actor.ACTOR_AUTH_TYPE;
|
||||
import static cn.topiam.employee.audit.entity.Event.*;
|
||||
import static cn.topiam.employee.audit.entity.GeoLocation.GEO_LOCATION_PROVINCE_CODE;
|
||||
import static cn.topiam.employee.audit.entity.Target.TARGET_ID_KEYWORD;
|
||||
import static cn.topiam.employee.common.constant.AuditConstants.getAuditIndexPrefix;
|
||||
import static cn.topiam.employee.console.converter.authn.IdentityProviderConverter.getIdentityProviderType;
|
||||
import static cn.topiam.employee.support.constant.EiamConstants.DEFAULT_DATE_TIME_FORMATTER_PATTERN;
|
||||
|
||||
|
@ -76,27 +72,13 @@ public class AnalysisServiceImpl implements AnalysisService {
|
|||
*/
|
||||
@Override
|
||||
public OverviewResult overview() {
|
||||
IndexCoordinates indexCoordinates = IndexCoordinates
|
||||
.of(getAuditIndexPrefix(supportProperties.getAudit().getIndexPrefix()) + "*");
|
||||
// 不存在索引
|
||||
if (!elasticsearchTemplate.indexOps(indexCoordinates).exists()) {
|
||||
return new OverviewResult();
|
||||
}
|
||||
OverviewResult result = new OverviewResult();
|
||||
result.setAppCount(appRepository.count());
|
||||
result.setUserCount(userRepository.count());
|
||||
result.setIdpCount(identityProviderRepository.count());
|
||||
// 查询今日认证量条件
|
||||
Query rangeQuery = QueryBuilders.range(range -> range.field(EVENT_TIME)
|
||||
.gte(JsonData.of(LocalDateTime.of(LocalDate.now(), LocalTime.MIN)
|
||||
.format(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMATTER_PATTERN))))
|
||||
.lt(JsonData.of(LocalDateTime.of(LocalDate.now(), LocalTime.MAX)
|
||||
.format(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMATTER_PATTERN))))
|
||||
.timeZone(ZONE_ID).format(DEFAULT_DATE_TIME_FORMATTER_PATTERN));
|
||||
Query query = getQuery(rangeQuery, EventType.LOGIN_PORTAL);
|
||||
result.setTodayAuthnCount(
|
||||
elasticsearchTemplate.count(new NativeQueryBuilder().withQuery(query).build(),
|
||||
AuditElasticSearchEntity.class, indexCoordinates));
|
||||
result.setTodayAuthnCount(auditRepository.countByTypeAndTime(
|
||||
EventType.LOGIN_PORTAL.getCode(), LocalDateTime.MIN, LocalDateTime.MAX));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -108,71 +90,11 @@ public class AnalysisServiceImpl implements AnalysisService {
|
|||
*/
|
||||
@Override
|
||||
public List<AuthnQuantityResult> authnQuantity(AnalysisQuery params) {
|
||||
IndexCoordinates indexCoordinates = IndexCoordinates
|
||||
.of(getAuditIndexPrefix(supportProperties.getAudit().getIndexPrefix()) + "*");
|
||||
// 不存在索引
|
||||
if (!elasticsearchTemplate.indexOps(indexCoordinates).exists()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
LocalDateTime min = params.getStartTime();
|
||||
LocalDateTime max = params.getEndTime();
|
||||
AnalysisQuery.Interval timeInterval = params.getTimeInterval();
|
||||
// 根据事件月份分组统计认证数量 count
|
||||
Aggregation dateGroup = AggregationBuilders
|
||||
.dateHistogram(count -> count.calendarInterval(timeInterval.getType()).extendedBounds(
|
||||
fieldDateMathBuilder -> fieldDateMathBuilder.min(FieldDateMath.of(math -> {
|
||||
math.value(
|
||||
(double) min.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
|
||||
return math;
|
||||
})).max(FieldDateMath.of(math -> {
|
||||
math.value(
|
||||
(double) max.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
|
||||
return math;
|
||||
}))).field(EVENT_TIME).timeZone(ZONE_ID).format(timeInterval.getFormat()));
|
||||
// 事件状态group
|
||||
TermsAggregation statusGroup = AggregationBuilders.terms(t -> t.field(EVENT_STATUS))
|
||||
.terms();
|
||||
Aggregation group = new Aggregation.Builder().aggregations("dateGroup", dateGroup)
|
||||
.terms(statusGroup).build();
|
||||
|
||||
return getAuthnQuantityResults(indexCoordinates, min, max, group);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<AuthnQuantityResult> getAuthnQuantityResults(IndexCoordinates indexCoordinates,
|
||||
LocalDateTime min, LocalDateTime max,
|
||||
Aggregation aggregation) {
|
||||
// 查询条件
|
||||
Query rangeBuilder = QueryBuilders.range(range -> range.field(EVENT_TIME).timeZone(ZONE_ID)
|
||||
.format(DEFAULT_DATE_TIME_FORMATTER_PATTERN)
|
||||
.gt(JsonData
|
||||
.of(min.format(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMATTER_PATTERN))))
|
||||
.lt(JsonData
|
||||
.of(max.format(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMATTER_PATTERN)))));
|
||||
Query query = getQuery(rangeBuilder, EventType.LOGIN_PORTAL);
|
||||
NativeQuery authCountBuild = new NativeQueryBuilder().withQuery(query)
|
||||
.withAggregation(COUNT, aggregation).withMaxResults(0).build();
|
||||
// 统计认证量
|
||||
SearchHits<AuditElasticSearchEntity> authCountResult = elasticsearchTemplate
|
||||
.search(authCountBuild, AuditElasticSearchEntity.class, indexCoordinates);
|
||||
ElasticsearchAggregation countGroupAggregation = getCountAggregation(authCountResult);
|
||||
List<AuthnQuantityResult> authCountList = new ArrayList<>();
|
||||
if (countGroupAggregation != null) {
|
||||
List<StringTermsBucket> buckets = countGroupAggregation.aggregation().getAggregate()
|
||||
.sterms().buckets().array();
|
||||
for (StringTermsBucket bucket : buckets) {
|
||||
// success/fail
|
||||
String statusGroupKey = bucket.key().stringValue();
|
||||
List<DateHistogramBucket> dateGroupList = bucket.aggregations().get("dateGroup")
|
||||
.dateHistogram().buckets().array();
|
||||
for (DateHistogramBucket dateGroup : dateGroupList) {
|
||||
String dateGroupKey = dateGroup.keyAsString();
|
||||
authCountList.add(new AuthnQuantityResult(dateGroupKey, dateGroup.docCount(),
|
||||
Objects.requireNonNull(EventStatus.getType(statusGroupKey)).getDesc()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return authCountList;
|
||||
return auditRepository.authnQuantity(EventType.LOGIN_PORTAL, min, max,
|
||||
timeInterval.getFormat());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -183,32 +105,13 @@ public class AnalysisServiceImpl implements AnalysisService {
|
|||
*/
|
||||
@Override
|
||||
public List<AppVisitRankResult> appVisitRank(AnalysisQuery params) {
|
||||
IndexCoordinates indexCoordinates = IndexCoordinates
|
||||
.of(getAuditIndexPrefix(supportProperties.getAudit().getIndexPrefix()) + "*");
|
||||
// 不存在索引
|
||||
if (!elasticsearchTemplate.indexOps(indexCoordinates).exists()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
Query rangeQuery = getRangeQueryBuilder(params);
|
||||
Query query = getQuery(rangeQuery, EventType.APP_SSO);
|
||||
// 应用访问频次前10条
|
||||
Aggregation groupAppVisit = AggregationBuilders
|
||||
.terms(terms -> terms.field(TARGET_ID_KEYWORD).size(10));
|
||||
NativeQuery appVisitBuild = new NativeQueryBuilder().withQuery(query)
|
||||
.withAggregation(COUNT, groupAppVisit).build();
|
||||
SearchHits<AuditElasticSearchEntity> appVisitResult = elasticsearchTemplate
|
||||
.search(appVisitBuild, AuditElasticSearchEntity.class, indexCoordinates);
|
||||
ElasticsearchAggregation countAggregation = getCountAggregation(appVisitResult);
|
||||
List<AppVisitRankResult> applicationVisitList = new ArrayList<>();
|
||||
if (countAggregation != null) {
|
||||
List<StringTermsBucket> array = countAggregation.aggregation().getAggregate().sterms()
|
||||
.buckets().array();
|
||||
for (StringTermsBucket bucket : array) {
|
||||
String key = bucket.key().stringValue();
|
||||
List<AuditStatisticsResult> auditRankResults = auditRepository
|
||||
.appVisitRank(EventType.APP_SSO, params.getStartTime(), params.getEndTime());
|
||||
for (AuditStatisticsResult auditRankResult : auditRankResults) {
|
||||
// 单点登录
|
||||
String name = getAppName(key);
|
||||
applicationVisitList.add(new AppVisitRankResult(name, bucket.docCount()));
|
||||
}
|
||||
String name = getAppName(auditRankResult.getKey());
|
||||
applicationVisitList.add(new AppVisitRankResult(name, auditRankResult.getCount()));
|
||||
}
|
||||
return applicationVisitList;
|
||||
}
|
||||
|
@ -236,33 +139,14 @@ public class AnalysisServiceImpl implements AnalysisService {
|
|||
*/
|
||||
@Override
|
||||
public List<AuthnHotProviderResult> authnHotProvider(AnalysisQuery params) {
|
||||
IndexCoordinates indexCoordinates = IndexCoordinates
|
||||
.of(getAuditIndexPrefix(supportProperties.getAudit().getIndexPrefix()) + "*");
|
||||
// 不存在索引
|
||||
if (!elasticsearchTemplate.indexOps(indexCoordinates).exists()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
Query builder = getRangeQueryBuilder(params);
|
||||
BoolQuery.Builder queryBuilder = getQueryBuilder(builder, EventType.LOGIN_PORTAL);
|
||||
queryBuilder.must(QueryBuilders.exists(e -> e.field(ACTOR_AUTH_TYPE)));
|
||||
// 授权类型频次
|
||||
Aggregation groupAuthType = AggregationBuilders
|
||||
.terms(terms -> terms.field(ACTOR_AUTH_TYPE).size(IdentityProviderType.size()));
|
||||
NativeQuery appVisitBuild = new NativeQueryBuilder()
|
||||
.withQuery(queryBuilder.build()._toQuery()).withAggregation(COUNT, groupAuthType)
|
||||
.build();
|
||||
SearchHits<AuditElasticSearchEntity> authTypeResult = elasticsearchTemplate
|
||||
.search(appVisitBuild, AuditElasticSearchEntity.class, indexCoordinates);
|
||||
ElasticsearchAggregation authTypeStringTerms = getCountAggregation(authTypeResult);
|
||||
List<AuthnHotProviderResult> authTypeList = new ArrayList<>();
|
||||
if (authTypeStringTerms != null) {
|
||||
List<StringTermsBucket> array = authTypeStringTerms.aggregation().getAggregate()
|
||||
.sterms().buckets().array();
|
||||
for (StringTermsBucket bucket : array) {
|
||||
String key = bucket.key().stringValue();
|
||||
List<AuditStatisticsResult> auditRankResults = auditRepository
|
||||
.authnHotProvider(EventType.LOGIN_PORTAL, params.getStartTime(), params.getEndTime());
|
||||
for (AuditStatisticsResult auditRankResult : auditRankResults) {
|
||||
// 授权类型
|
||||
String name = getIdentityProviderType(key).name();
|
||||
authTypeList.add(new AuthnHotProviderResult(name, bucket.docCount()));
|
||||
if (Objects.nonNull(auditRankResult.getKey())) {
|
||||
String name = getIdentityProviderType(auditRankResult.getKey()).name();
|
||||
authTypeList.add(new AuthnHotProviderResult(name, auditRankResult.getCount()));
|
||||
}
|
||||
}
|
||||
return authTypeList;
|
||||
|
@ -276,34 +160,12 @@ public class AnalysisServiceImpl implements AnalysisService {
|
|||
*/
|
||||
@Override
|
||||
public List<AuthnZoneResult> authnZone(AnalysisQuery params) {
|
||||
IndexCoordinates indexCoordinates = IndexCoordinates
|
||||
.of(getAuditIndexPrefix(supportProperties.getAudit().getIndexPrefix()) + "*");
|
||||
// 不存在索引
|
||||
if (!elasticsearchTemplate.indexOps(indexCoordinates).exists()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
Query builder = getRangeQueryBuilder(params);
|
||||
BoolQuery.Builder queryBuilder = getQueryBuilder(builder, EventType.LOGIN_PORTAL);
|
||||
queryBuilder.must(QueryBuilders.exists(exists -> exists.field(GEO_LOCATION_PROVINCE_CODE)));
|
||||
// 登录城市分组统计
|
||||
Aggregation groupAuthZone = AggregationBuilders
|
||||
.terms(terms -> terms.field(GEO_LOCATION_PROVINCE_CODE).size(36));
|
||||
NativeQuery appVisitBuild = new NativeQueryBuilder()
|
||||
.withQuery(queryBuilder.build()._toQuery()).withAggregation(COUNT, groupAuthZone)
|
||||
.build();
|
||||
SearchHits<AuditElasticSearchEntity> authZoneResult = elasticsearchTemplate
|
||||
.search(appVisitBuild, AuditElasticSearchEntity.class, indexCoordinates);
|
||||
ElasticsearchAggregation authZoneStringTerms = getCountAggregation(authZoneResult);
|
||||
List<AuthnZoneResult> authnZoneResults = new ArrayList<>();
|
||||
if (authZoneStringTerms != null) {
|
||||
List<StringTermsBucket> array = authZoneStringTerms.aggregation().getAggregate()
|
||||
.sterms().buckets().array();
|
||||
for (StringTermsBucket bucket : array) {
|
||||
String key = bucket.key().stringValue();
|
||||
authnZoneResults.add(new AuthnZoneResult(key, bucket.docCount()));
|
||||
}
|
||||
}
|
||||
return authnZoneResults;
|
||||
List<AuditStatisticsResult> auditStatisticsResults = auditRepository
|
||||
.authnZone(EventType.LOGIN_PORTAL, params.getStartTime(), params.getEndTime());
|
||||
return auditStatisticsResults.stream()
|
||||
.map(auditStatisticsResult -> new AuthnZoneResult(auditStatisticsResult.getKey(),
|
||||
auditStatisticsResult.getCount()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -372,7 +234,9 @@ public class AnalysisServiceImpl implements AnalysisService {
|
|||
|
||||
private final SupportProperties supportProperties;
|
||||
|
||||
private final ElasticsearchTemplate elasticsearchTemplate;
|
||||
// private final ElasticsearchTemplate elasticsearchTemplate;
|
||||
|
||||
private final AuditRepository auditRepository;
|
||||
|
||||
private final AppRepository appRepository;
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ import cn.topiam.employee.support.util.PhoneNumberUtils;
|
|||
import cn.topiam.employee.support.validation.annotation.ValidationPhone;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import static cn.topiam.employee.console.access.DefaultAdministratorConstants.DEFAULT_ADMIN_USERNAME;
|
||||
import static cn.topiam.employee.common.constant.SecurityConstants.DEFAULT_ADMIN_USERNAME;
|
||||
import static cn.topiam.employee.support.util.PhoneNumberUtils.getPhoneNumber;
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* eiam-synchronizer - Employee Identity and Access Management
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.synchronizer.configuration;
|
||||
package cn.topiam.employee.console.synchronizer.configuration;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -47,6 +47,7 @@ import cn.topiam.employee.common.entity.identitysource.IdentitySourceEntity;
|
|||
import cn.topiam.employee.common.enums.TriggerType;
|
||||
import cn.topiam.employee.common.enums.identitysource.IdentitySourceProvider;
|
||||
import cn.topiam.employee.common.repository.identitysource.IdentitySourceRepository;
|
||||
import cn.topiam.employee.console.synchronizer.task.IdentitySourceSyncTask;
|
||||
import cn.topiam.employee.identitysource.core.IdentitySource;
|
||||
import cn.topiam.employee.identitysource.core.IdentitySourceConfig;
|
||||
import cn.topiam.employee.identitysource.core.client.IdentitySourceClient;
|
||||
|
@ -64,14 +65,12 @@ import cn.topiam.employee.identitysource.feishu.client.FeiShuClient;
|
|||
import cn.topiam.employee.identitysource.wechatwork.WeChatWorkConfig;
|
||||
import cn.topiam.employee.identitysource.wechatwork.WeChatWorkIdentitySource;
|
||||
import cn.topiam.employee.identitysource.wechatwork.client.WeChatWorkClient;
|
||||
import cn.topiam.employee.support.scheduler.SpringSchedulerRegistrar;
|
||||
import cn.topiam.employee.support.scheduler.SpringSchedulerRegister;
|
||||
import cn.topiam.employee.support.trace.TraceUtils;
|
||||
import cn.topiam.employee.synchronizer.task.IdentitySourceSyncTask;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import static cn.topiam.employee.common.enums.identitysource.IdentitySourceProvider.DINGTALK;
|
||||
import static cn.topiam.employee.support.lock.LockAspect.getTopiamLockKeyPrefix;
|
||||
import static cn.topiam.employee.synchronizer.configuration.IdentitySourceBeanUtils.getSourceBeanName;
|
||||
|
||||
/**
|
||||
* 身份源Bean 注册
|
||||
|
@ -124,7 +123,8 @@ public class IdentitySourceBeanRegistry implements IdentitySourceEventListener {
|
|||
.getBeanFactory();
|
||||
//如果已经存在,销毁
|
||||
try {
|
||||
if (ObjectUtils.isNotEmpty(beanFactory.getBean(getSourceBeanName(id)))) {
|
||||
if (ObjectUtils
|
||||
.isNotEmpty(beanFactory.getBean(IdentitySourceBeanUtils.getSourceBeanName(id)))) {
|
||||
destroyIdentitySourceBean(id, applicationContext);
|
||||
}
|
||||
} catch (NoSuchBeanDefinitionException ignored) {
|
||||
|
@ -200,7 +200,7 @@ public class IdentitySourceBeanRegistry implements IdentitySourceEventListener {
|
|||
//设置为 RefreshScope
|
||||
definitionBuilder.setScope("refresh");
|
||||
return new BeanDefinitionHolder(definitionBuilder.getBeanDefinition(),
|
||||
getSourceBeanName(entity.getId().toString()));
|
||||
IdentitySourceBeanUtils.getSourceBeanName(entity.getId().toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -213,7 +213,7 @@ public class IdentitySourceBeanRegistry implements IdentitySourceEventListener {
|
|||
ApplicationContext applicationContext) {
|
||||
BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry) ((ConfigurableApplicationContext) applicationContext)
|
||||
.getBeanFactory();
|
||||
String beanName = getSourceBeanName(id);
|
||||
String beanName = IdentitySourceBeanUtils.getSourceBeanName(id);
|
||||
try {
|
||||
beanDefinitionRegistry.removeBeanDefinition(beanName);
|
||||
} catch (NoSuchBeanDefinitionException ignored) {
|
||||
|
@ -229,11 +229,11 @@ public class IdentitySourceBeanRegistry implements IdentitySourceEventListener {
|
|||
public static void registerIdentitySourceSyncTask(IdentitySourceEntity entity,
|
||||
ApplicationContext applicationContext) {
|
||||
String id = entity.getId().toString();
|
||||
String beanName = getSourceBeanName(id);
|
||||
String beanName = IdentitySourceBeanUtils.getSourceBeanName(id);
|
||||
IdentitySource<? extends IdentitySourceConfig> identitySource = (IdentitySource<? extends IdentitySourceConfig>) applicationContext
|
||||
.getBean(beanName);
|
||||
SpringSchedulerRegistrar schedulerRegistrarHelp = applicationContext
|
||||
.getBean(SpringSchedulerRegistrar.class);
|
||||
SpringSchedulerRegister schedulerRegistrarHelp = applicationContext
|
||||
.getBean(SpringSchedulerRegister.class);
|
||||
RedissonClient redissonClient = applicationContext.getBean(RedissonClient.class);
|
||||
//注册定时任务
|
||||
String cronExpression = entity.getJobConfig().getCronExpression(CronType.SPRING);
|
||||
|
@ -252,8 +252,8 @@ public class IdentitySourceBeanRegistry implements IdentitySourceEventListener {
|
|||
*/
|
||||
public static void destroyIdentitySourceSyncTask(String id,
|
||||
ApplicationContext applicationContext) {
|
||||
SpringSchedulerRegistrar schedulerRegistrarHelp = applicationContext
|
||||
.getBean(SpringSchedulerRegistrar.class);
|
||||
SpringSchedulerRegister schedulerRegistrarHelp = applicationContext
|
||||
.getBean(SpringSchedulerRegister.class);
|
||||
schedulerRegistrarHelp.removeCronTask(id);
|
||||
}
|
||||
|
||||
|
@ -304,7 +304,7 @@ public class IdentitySourceBeanRegistry implements IdentitySourceEventListener {
|
|||
@Override
|
||||
public void sync(String id) {
|
||||
IdentitySource<? extends IdentitySourceConfig> identitySource = (IdentitySource<? extends IdentitySourceConfig>) applicationContext
|
||||
.getBean(getSourceBeanName(id));
|
||||
.getBean(IdentitySourceBeanUtils.getSourceBeanName(id));
|
||||
RedissonClient redissonClient = applicationContext.getBean(RedissonClient.class);
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
stopWatch.start();
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* eiam-synchronizer - Employee Identity and Access Management
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.synchronizer.configuration;
|
||||
package cn.topiam.employee.console.synchronizer.configuration;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.console.synchronizer.configuration;
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* eiam-synchronizer - Employee Identity and Access Management
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.synchronizer.endpoint;
|
||||
package cn.topiam.employee.console.synchronizer.endpoint;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
|
@ -28,15 +28,15 @@ import org.springframework.web.bind.annotation.RestController;
|
|||
|
||||
import cn.topiam.employee.common.entity.identitysource.IdentitySourceEntity;
|
||||
import cn.topiam.employee.common.repository.identitysource.IdentitySourceRepository;
|
||||
import cn.topiam.employee.console.synchronizer.configuration.IdentitySourceBeanUtils;
|
||||
import cn.topiam.employee.identitysource.core.IdentitySource;
|
||||
import cn.topiam.employee.identitysource.core.IdentitySourceConfig;
|
||||
import cn.topiam.employee.support.trace.Trace;
|
||||
import cn.topiam.employee.synchronizer.configuration.IdentitySourceBeanUtils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import static cn.topiam.employee.synchronizer.constants.SynchronizerConstants.EVENT_RECEIVE_PATH;
|
||||
import static cn.topiam.employee.common.constant.SynchronizerConstants.EVENT_RECEIVE_PATH;
|
||||
|
||||
/**
|
||||
* 身份源回调事件端点
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.console.synchronizer.endpoint;
|
|
@ -15,4 +15,4 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.console.listener;
|
||||
package cn.topiam.employee.console.synchronizer;
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* eiam-synchronizer - Employee Identity and Access Management
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.synchronizer.processor;
|
||||
package cn.topiam.employee.console.synchronizer.processor;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* eiam-synchronizer - Employee Identity and Access Management
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.synchronizer.processor;
|
||||
package cn.topiam.employee.console.synchronizer.processor;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* eiam-synchronizer - Employee Identity and Access Management
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.synchronizer.processor;
|
||||
package cn.topiam.employee.console.synchronizer.processor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
@ -55,8 +55,6 @@ import cn.topiam.employee.common.repository.identitysource.IdentitySourceSyncRec
|
|||
import cn.topiam.employee.common.storage.Storage;
|
||||
import cn.topiam.employee.core.message.mail.MailMsgEventPublish;
|
||||
import cn.topiam.employee.core.message.sms.SmsMsgEventPublish;
|
||||
import cn.topiam.employee.core.mq.UserMessagePublisher;
|
||||
import cn.topiam.employee.core.mq.UserMessageTag;
|
||||
import cn.topiam.employee.identitysource.core.domain.Dept;
|
||||
import cn.topiam.employee.identitysource.core.domain.User;
|
||||
import cn.topiam.employee.identitysource.core.enums.IdentitySourceEventReceiveType;
|
||||
|
@ -133,7 +131,6 @@ public class DefaultIdentitySourceEventPostProcessor extends AbstractIdentitySou
|
|||
List<IdentitySourceEventRecordEntity> records = new ArrayList<>();
|
||||
List<UserEntity> createUsers = new ArrayList<>();
|
||||
Set<OrganizationMemberEntity> createOrganizationMembers = Sets.newHashSet();
|
||||
List<String> batchEsUserIdList = new ArrayList<>();
|
||||
users.forEach(thirdPartyUser -> {
|
||||
log.info("处理上游用户新增事件:[{}]开始", thirdPartyUser.getUserId());
|
||||
UserEntity userEntity = thirdPartyUserToUserEntity(thirdPartyUser, identitySource);
|
||||
|
@ -149,7 +146,6 @@ public class DefaultIdentitySourceEventPostProcessor extends AbstractIdentitySou
|
|||
userEntity.getId());
|
||||
createOrganizationMember(createOrganizationMembers, userEntity, organization);
|
||||
});
|
||||
batchEsUserIdList.add(String.valueOf(userEntity.getId()));
|
||||
//记录日志
|
||||
IdentitySourceEventRecordEntity record = new IdentitySourceEventRecordEntity();
|
||||
record.setObjectId(userEntity.getId().toString());
|
||||
|
@ -174,9 +170,6 @@ public class DefaultIdentitySourceEventPostProcessor extends AbstractIdentitySou
|
|||
organizationMemberRepository.batchSave(Lists.newArrayList(createOrganizationMembers));
|
||||
//保存事件记录
|
||||
identitySourceEventRecordRepository.batchSave(records);
|
||||
// 异步创建ES用户数据
|
||||
userMessagePublisher.sendUserChangeMessage(UserMessageTag.SAVE,
|
||||
String.join(",", batchEsUserIdList));
|
||||
// 批量发送短信邮件欢迎信息(密码通知)
|
||||
publishMessage(createUsers);
|
||||
}
|
||||
|
@ -192,7 +185,6 @@ public class DefaultIdentitySourceEventPostProcessor extends AbstractIdentitySou
|
|||
List<IdentitySourceEventRecordEntity> records = new ArrayList<>();
|
||||
List<UserEntity> updateUsers = new ArrayList<>();
|
||||
List<OrganizationMemberEntity> createOrganizationMembers = new ArrayList<>();
|
||||
List<String> batchEsUserIdList = new ArrayList<>();
|
||||
//需要删除组织成员关系集合 KEY:用户ID,value:组织ID
|
||||
Map<Long, Set<String>> deleteOrganizationMembers = Maps.newHashMap();
|
||||
Map<UserEntity, Set<OrganizationEntity>> currentUsers = Maps.newHashMap();
|
||||
|
@ -252,9 +244,6 @@ public class DefaultIdentitySourceEventPostProcessor extends AbstractIdentitySou
|
|||
record.setUpdateBy(SYSTEM_DEFAULT_USER_NAME);
|
||||
record.setUpdateTime(LocalDateTime.now());
|
||||
records.add(record);
|
||||
|
||||
// 构建ES用户Id信息
|
||||
batchEsUserIdList.add(String.valueOf(currentUser.getId()));
|
||||
log.info("处理上游用户修改事件:[{}]结束", thirdPartyUser.getUserId());
|
||||
}
|
||||
}));
|
||||
|
@ -267,8 +256,6 @@ public class DefaultIdentitySourceEventPostProcessor extends AbstractIdentitySou
|
|||
.forEach(user -> deleteOrganizationMembers.get(user)
|
||||
.forEach(organization -> organizationMemberRepository
|
||||
.deleteByOrgIdAndUserId(organization, user)));
|
||||
// 异步更新ES用户数据
|
||||
userMessagePublisher.sendUserChangeMessage(UserMessageTag.SAVE, String.join(",", batchEsUserIdList));
|
||||
//保存事件记录
|
||||
identitySourceEventRecordRepository.batchSave(records);
|
||||
//@formatter:on
|
||||
|
@ -398,7 +385,6 @@ public class DefaultIdentitySourceEventPostProcessor extends AbstractIdentitySou
|
|||
//根据外部ID查询
|
||||
List<OrganizationEntity> list = organizationRepository
|
||||
.findByExternalIdIn(organizations.stream().map(Dept::getDeptId).toList());
|
||||
List<String> userIds = new ArrayList<>();
|
||||
list.forEach(current -> organizations.forEach(threeParty -> {
|
||||
//当前三方ID和上游科室对应
|
||||
if (current.getExternalId().equals(threeParty.getDeptId())) {
|
||||
|
@ -434,20 +420,10 @@ public class DefaultIdentitySourceEventPostProcessor extends AbstractIdentitySou
|
|||
if (subOrganizations.stream().map(i -> i.getId().equals(current.getId()))
|
||||
.findAny().isEmpty()) {
|
||||
organizationRepository.updateIsLeaf(parentOrganization.getId(), true);
|
||||
// 查询关联用户
|
||||
List<OrganizationMemberEntity> orgMemberList = organizationMemberRepository
|
||||
.findAllByOrgId(parentOrganization.getId());
|
||||
userIds.addAll(orgMemberList.stream()
|
||||
.map(item -> String.valueOf(item.getUserId())).toList());
|
||||
}
|
||||
}
|
||||
if (parentOrganization.getLeaf()) {
|
||||
organizationRepository.updateIsLeaf(parentOrganization.getId(), false);
|
||||
// 查询关联用户
|
||||
List<OrganizationMemberEntity> orgMemberList = organizationMemberRepository
|
||||
.findAllByOrgId(parentOrganization.getId());
|
||||
userIds.addAll(orgMemberList.stream()
|
||||
.map(item -> String.valueOf(item.getUserId())).toList());
|
||||
}
|
||||
//修改基本信息
|
||||
current.setName(threeParty.getName());
|
||||
|
@ -469,17 +445,10 @@ public class DefaultIdentitySourceEventPostProcessor extends AbstractIdentitySou
|
|||
record.setUpdateBy(SYSTEM_DEFAULT_USER_NAME);
|
||||
record.setUpdateTime(LocalDateTime.now());
|
||||
records.add(record);
|
||||
// 查询关联用户
|
||||
List<OrganizationMemberEntity> orgMemberList = organizationMemberRepository
|
||||
.findAllByOrgId(current.getId());
|
||||
userIds.addAll(
|
||||
orgMemberList.stream().map(item -> String.valueOf(item.getUserId())).toList());
|
||||
}
|
||||
}));
|
||||
//批量保存部门信息
|
||||
organizationRepository.batchUpdate(updateOrganizations);
|
||||
// 更新es用户信息
|
||||
userMessagePublisher.sendUserChangeMessage(UserMessageTag.SAVE, String.join(",", userIds));
|
||||
//保存事件记录
|
||||
identitySourceEventRecordRepository.batchSave(records);
|
||||
}
|
||||
|
@ -554,11 +523,6 @@ public class DefaultIdentitySourceEventPostProcessor extends AbstractIdentitySou
|
|||
*/
|
||||
private final IdentitySourceEventRecordRepository identitySourceEventRecordRepository;
|
||||
|
||||
/**
|
||||
* MessagePublisher
|
||||
*/
|
||||
private final UserMessagePublisher userMessagePublisher;
|
||||
|
||||
public DefaultIdentitySourceEventPostProcessor(SmsMsgEventPublish smsMsgEventPublish,
|
||||
MailMsgEventPublish mailMsgEventPublish,
|
||||
PasswordEncoder passwordEncoder,
|
||||
|
@ -573,8 +537,7 @@ public class DefaultIdentitySourceEventPostProcessor extends AbstractIdentitySou
|
|||
OrganizationRepository organizationRepository,
|
||||
OrganizationMemberRepository organizationMemberRepository,
|
||||
IdentitySourceEventRecordRepository identitySourceEventRecordRepository,
|
||||
Storage storage,
|
||||
UserMessagePublisher userMessagePublisher) {
|
||||
Storage storage) {
|
||||
super(smsMsgEventPublish, mailMsgEventPublish, passwordEncoder, passwordGenerator,
|
||||
transactionDefinition, platformTransactionManager, entityManager,
|
||||
identitySourceRepository, identitySourceSyncHistoryRepository,
|
||||
|
@ -583,6 +546,5 @@ public class DefaultIdentitySourceEventPostProcessor extends AbstractIdentitySou
|
|||
this.organizationRepository = organizationRepository;
|
||||
this.organizationMemberRepository = organizationMemberRepository;
|
||||
this.identitySourceEventRecordRepository = identitySourceEventRecordRepository;
|
||||
this.userMessagePublisher = userMessagePublisher;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* eiam-synchronizer - Employee Identity and Access Management
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
|
@ -15,13 +15,12 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.synchronizer.processor;
|
||||
package cn.topiam.employee.console.synchronizer.processor;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
@ -54,8 +53,6 @@ import cn.topiam.employee.common.repository.identitysource.IdentitySourceSyncRec
|
|||
import cn.topiam.employee.common.storage.Storage;
|
||||
import cn.topiam.employee.core.message.mail.MailMsgEventPublish;
|
||||
import cn.topiam.employee.core.message.sms.SmsMsgEventPublish;
|
||||
import cn.topiam.employee.core.mq.UserMessagePublisher;
|
||||
import cn.topiam.employee.core.mq.UserMessageTag;
|
||||
import cn.topiam.employee.identitysource.core.domain.User;
|
||||
import cn.topiam.employee.identitysource.core.processor.IdentitySourceSyncUserPostProcessor;
|
||||
import cn.topiam.employee.support.repository.domain.IdEntity;
|
||||
|
@ -100,19 +97,14 @@ public class DefaultIdentitySourceUserPostProcessor extends AbstractIdentitySour
|
|||
processData = processData(identitySource, initData);
|
||||
//校验数据
|
||||
validateData(processData);
|
||||
String batchEsUserId = "";
|
||||
//批量保存用户
|
||||
if (!CollectionUtils.isEmpty(processData.getCreateUsers())) {
|
||||
List<UserEntity> createUserList = Lists.newArrayList(processData.getCreateUsers());
|
||||
batchEsUserId += createUserList.stream().map(user -> String.valueOf(user.getId()))
|
||||
.collect(Collectors.joining(","));
|
||||
userRepository.batchSave(createUserList);
|
||||
}
|
||||
//批量更新用户
|
||||
if (!CollectionUtils.isEmpty(processData.getUpdateUsers())) {
|
||||
List<UserEntity> updateUserList = Lists.newArrayList(processData.getUpdateUsers());
|
||||
batchEsUserId += updateUserList.stream().map(user -> String.valueOf(user.getId()))
|
||||
.collect(Collectors.joining(","));
|
||||
userRepository.batchUpdate(updateUserList);
|
||||
}
|
||||
//保存组织成员关系
|
||||
|
@ -148,13 +140,6 @@ public class DefaultIdentitySourceUserPostProcessor extends AbstractIdentitySour
|
|||
saveSyncHistoryRecord(history.getId(), processData);
|
||||
//提交事务
|
||||
platformTransactionManager.commit(transactionStatus);
|
||||
// 异步更新ES用户数据
|
||||
userMessagePublisher.sendUserChangeMessage(UserMessageTag.SAVE, batchEsUserId);
|
||||
// 异步删除用户ES数据
|
||||
if (!CollectionUtils.isEmpty(processData.getDeleteUsers())) {
|
||||
userMessagePublisher.sendUserChangeMessage(UserMessageTag.DELETE,
|
||||
deleteUserIds.stream().map(String::valueOf).collect(Collectors.joining(",")));
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(processData.getCreateUsers())) {
|
||||
// 发送密码通知
|
||||
publishMessage(Lists.newArrayList(processData.getCreateUsers()));
|
||||
|
@ -734,11 +719,6 @@ public class DefaultIdentitySourceUserPostProcessor extends AbstractIdentitySour
|
|||
*/
|
||||
private final UserGroupMemberRepository userGroupMemberRepository;
|
||||
|
||||
/**
|
||||
* MessagePublisher
|
||||
*/
|
||||
private final UserMessagePublisher userMessagePublisher;
|
||||
|
||||
public DefaultIdentitySourceUserPostProcessor(SmsMsgEventPublish smsMsgEventPublish,
|
||||
MailMsgEventPublish mailMsgEventPublish,
|
||||
TransactionDefinition transactionDefinition,
|
||||
|
@ -754,8 +734,7 @@ public class DefaultIdentitySourceUserPostProcessor extends AbstractIdentitySour
|
|||
OrganizationMemberRepository organizationMemberRepository,
|
||||
UserGroupMemberRepository userGroupMemberRepository,
|
||||
OrganizationRepository organizationRepository,
|
||||
Storage storage,
|
||||
UserMessagePublisher userMessagePublisher) {
|
||||
Storage storage) {
|
||||
super(smsMsgEventPublish, mailMsgEventPublish, passwordEncoder, passwordGenerator,
|
||||
transactionDefinition, platformTransactionManager, entityManager,
|
||||
identitySourceRepository, identitySourceSyncHistoryRepository,
|
||||
|
@ -765,6 +744,5 @@ public class DefaultIdentitySourceUserPostProcessor extends AbstractIdentitySour
|
|||
this.organizationMemberRepository = organizationMemberRepository;
|
||||
this.userGroupMemberRepository = userGroupMemberRepository;
|
||||
this.organizationRepository = organizationRepository;
|
||||
this.userMessagePublisher = userMessagePublisher;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* eiam-synchronizer - Employee Identity and Access Management
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
|
@ -15,7 +15,7 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.synchronizer.task;
|
||||
package cn.topiam.employee.console.synchronizer.task;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
|
@ -15,4 +15,4 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.console.constant;
|
||||
package cn.topiam.employee.console.synchronizer.task;
|
|
@ -132,10 +132,6 @@ spring:
|
|||
overwrite-existing-jobs: true
|
||||
jdbc:
|
||||
initialize-schema: never
|
||||
#rabbitmq
|
||||
rabbitmq:
|
||||
template:
|
||||
reply-timeout: 60000
|
||||
#日志配置
|
||||
logging:
|
||||
config: classpath:config/logback-spring.xml
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* eiam-console - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee;
|
||||
|
||||
import cn.topiam.employee.support.geo.GeoLocation;
|
||||
import cn.topiam.employee.support.geo.GeoLocationService;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@SpringBootTest
|
||||
public class GeoLocationServiceTest {
|
||||
|
||||
private final Logger logger= LoggerFactory.getLogger(GeoLocationServiceTest.class);
|
||||
|
||||
@Test
|
||||
public void getGeoLocation() throws JsonProcessingException {
|
||||
GeoLocation geoLocation = geoLocationService.getGeoLocation("119.163.76.166");
|
||||
logger.info("Test 119.163.76.166 GeoLocation : {}", objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(geoLocation));
|
||||
}
|
||||
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Autowired
|
||||
private GeoLocationService geoLocationService;
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
/*
|
||||
* eiam-core - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.core.dynamic;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import cn.topiam.employee.common.entity.account.UserElasticSearchEntity;
|
||||
import cn.topiam.employee.common.entity.account.po.UserEsPO;
|
||||
import cn.topiam.employee.common.repository.account.OrganizationRepository;
|
||||
import cn.topiam.employee.common.repository.account.UserElasticSearchRepository;
|
||||
import cn.topiam.employee.common.repository.account.UserRepository;
|
||||
import cn.topiam.employee.support.autoconfiguration.SupportProperties;
|
||||
import cn.topiam.employee.support.lock.Lock;
|
||||
import cn.topiam.employee.support.trace.Trace;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import static cn.topiam.employee.core.mq.UserMessageListener.getUserElasticSearchEntity;
|
||||
|
||||
/**
|
||||
* UserSyncTask
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2023/6/18 21:42
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class UserSyncTask {
|
||||
|
||||
@Lock(throwException = false)
|
||||
@Trace
|
||||
@Scheduled(fixedRate = 60000)
|
||||
public void syncUser() {
|
||||
try {
|
||||
long start = System.currentTimeMillis();
|
||||
log.info("同步用户数据到ES开始");
|
||||
IndexCoordinates userIndex = IndexCoordinates
|
||||
.of(supportProperties.getUser().getIndexPrefix());
|
||||
// 查询库中全部用户信息
|
||||
List<UserEsPO> userList = userRepository.getUserList(null);
|
||||
List<UserElasticSearchEntity> newUserElasticSearch = getUserElasticSearchEntity(
|
||||
userList, organizationRepository);
|
||||
List<UserElasticSearchEntity> saveUserList;
|
||||
int removeNumber = 0;
|
||||
// 存在索引,查询es用户数据
|
||||
List<UserElasticSearchEntity> oldUserElasticSearchList = userRepository
|
||||
.getAllUserElasticSearchEntity(userIndex);
|
||||
if (!CollectionUtils.isEmpty(oldUserElasticSearchList)) {
|
||||
// 要删除的数据
|
||||
List<String> removeUserIdList = oldUserElasticSearchList.stream()
|
||||
.map(UserElasticSearchEntity::getId).filter(id -> newUserElasticSearch.stream()
|
||||
.noneMatch(newUser -> newUser.getId().equals(id)))
|
||||
.toList();
|
||||
removeNumber = removeUserIdList.size();
|
||||
// 删除
|
||||
if (!CollectionUtils.isEmpty(removeUserIdList)) {
|
||||
userElasticSearchRepository.deleteAllById(removeUserIdList);
|
||||
}
|
||||
// 要更新的数据
|
||||
saveUserList = newUserElasticSearch.stream()
|
||||
.filter(newUser -> oldUserElasticSearchList.stream()
|
||||
.noneMatch(oldUser -> oldUser.equals(newUser)))
|
||||
.toList();
|
||||
} else {
|
||||
saveUserList = newUserElasticSearch;
|
||||
}
|
||||
// 更新
|
||||
userElasticSearchRepository.saveAll(saveUserList);
|
||||
log.info("同步用户数据到ES成功, 更新:[{}], 删除:[{}], 耗时:[{}]s", saveUserList.size(), removeNumber,
|
||||
(System.currentTimeMillis() - start) / 1000);
|
||||
} catch (Exception e) {
|
||||
log.error("同步用户数据到ES异常:[{}]", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SupportProperties
|
||||
*/
|
||||
private final SupportProperties supportProperties;
|
||||
|
||||
/**
|
||||
* UserElasticSearchRepository
|
||||
*/
|
||||
private final UserElasticSearchRepository userElasticSearchRepository;
|
||||
|
||||
/**
|
||||
* UserRepository
|
||||
*/
|
||||
private final UserRepository userRepository;
|
||||
|
||||
/**
|
||||
* OrganizationRepository
|
||||
*/
|
||||
private final OrganizationRepository organizationRepository;
|
||||
}
|
|
@ -15,23 +15,41 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.core.dynamic;
|
||||
package cn.topiam.employee.core.message.mail;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import java.util.Map;
|
||||
|
||||
import cn.topiam.employee.support.autoconfiguration.SupportProperties;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
import cn.topiam.employee.common.enums.MailType;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* ElasticsearchConfiguration
|
||||
* 消息事件
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2022/11/3 23:31
|
||||
* Created by support@topiam.cn on 2021/9/25 21:07
|
||||
*/
|
||||
@Configuration
|
||||
public class UserElasticsearchConfiguration {
|
||||
@Bean
|
||||
public UserIndexName userIndexName(SupportProperties supportProperties) {
|
||||
return new UserIndexName(supportProperties);
|
||||
@Getter
|
||||
public class MailMsgEvent extends ApplicationEvent {
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
private final MailType type;
|
||||
/**
|
||||
* 接收人
|
||||
*/
|
||||
private final String receiver;
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
private final Map<String, Object> parameter;
|
||||
|
||||
public MailMsgEvent(MailType type, String receiver, Map<String, Object> parameter) {
|
||||
super(type);
|
||||
this.type = type;
|
||||
this.receiver = receiver;
|
||||
this.parameter = parameter;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* eiam-core - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.core.message.mail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.topiam.employee.common.entity.message.MailSendRecordEntity;
|
||||
import cn.topiam.employee.common.entity.setting.MailTemplateEntity;
|
||||
import cn.topiam.employee.common.enums.MailType;
|
||||
import cn.topiam.employee.common.exception.MailMessageSendException;
|
||||
import cn.topiam.employee.common.message.mail.MailProviderSend;
|
||||
import cn.topiam.employee.common.message.mail.SendMailRequest;
|
||||
import cn.topiam.employee.common.repository.message.MailSendRecordRepository;
|
||||
import cn.topiam.employee.core.message.MsgVariable;
|
||||
import cn.topiam.employee.core.setting.constant.MessageSettingConstants;
|
||||
|
||||
import freemarker.cache.StringTemplateLoader;
|
||||
import freemarker.cache.TemplateLoader;
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.Template;
|
||||
import static org.apache.commons.codec.CharEncoding.UTF_8;
|
||||
import static org.springframework.web.util.HtmlUtils.htmlUnescape;
|
||||
|
||||
import static cn.topiam.employee.support.constant.EiamConstants.COLON;
|
||||
|
||||
/**
|
||||
* 消息通知事件
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2021/9/25 21:07
|
||||
*/
|
||||
@Async
|
||||
@Component
|
||||
public class MailMsgEventListener implements ApplicationListener<MailMsgEvent> {
|
||||
private final Logger logger = LoggerFactory.getLogger(MailMsgEventListener.class);
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(@NonNull MailMsgEvent event) {
|
||||
// 邮件通知类型
|
||||
MailType type = event.getType();
|
||||
String content = htmlUnescape(MailUtils.readEmailContent(type.getContent()));
|
||||
String subject = type.getSubject();
|
||||
String sender = type.getSender();
|
||||
try {
|
||||
Map<String, Object> parameter = event.getParameter();
|
||||
// 判断是否存在自定义
|
||||
MailTemplateEntity templateEntity = (MailTemplateEntity) redisTemplate.opsForValue()
|
||||
.get(MessageSettingConstants.SETTING_EMAIL_TEMPLATE_CACHE_NAME + COLON
|
||||
+ type.getCode());
|
||||
if (!Objects.isNull(templateEntity)) {
|
||||
content = htmlUnescape(templateEntity.getContent());
|
||||
subject = templateEntity.getSubject();
|
||||
sender = templateEntity.getSender();
|
||||
}
|
||||
// 主题
|
||||
StringWriter themeStringWriter = new StringWriter();
|
||||
Template themeTpl = createTemplate(type.getCode() + "-theme", subject);
|
||||
themeTpl.process(parameter, themeStringWriter);
|
||||
// 测试邮件
|
||||
if (parameter.containsKey(MsgVariable.TEST)) {
|
||||
String test = parameter.get(MsgVariable.TEST).toString();
|
||||
themeStringWriter.append(test);
|
||||
}
|
||||
// 内容
|
||||
StringWriter contentStringWriter = new StringWriter();
|
||||
Template contentTpl = createTemplate(type.getCode() + "-content", content);
|
||||
contentTpl.process(parameter, contentStringWriter);
|
||||
// 发送邮件
|
||||
//@formatter:off
|
||||
SendMailRequest params = new SendMailRequest()
|
||||
// 发送人
|
||||
.setSender(sender)
|
||||
// 接收人
|
||||
.setReceiver(event.getReceiver())
|
||||
// 主题
|
||||
.setSubject(themeStringWriter.toString())
|
||||
// 内容
|
||||
.setBody(contentStringWriter.toString());
|
||||
//@formatter:on
|
||||
// 发送邮件
|
||||
mailProviderSend.sendMailHtml(params);
|
||||
// 保存发送记录
|
||||
MailSendRecordEntity record = new MailSendRecordEntity();
|
||||
//@formatter:off
|
||||
record.setSender(event.getReceiver())
|
||||
.setSubject(params.getSubject())
|
||||
.setContent(params.getBody())
|
||||
.setProvider(mailProviderSend.getProvider())
|
||||
.setType(event.getType())
|
||||
.setReceiver(event.getReceiver())
|
||||
.setSendTime(LocalDateTime.now())
|
||||
.setSuccess(true);
|
||||
//@formatter:on
|
||||
mailSendRecordRepository.save(record);
|
||||
} catch (Exception e) {
|
||||
logger.error("邮件信息发送失败: {}", e.getMessage());
|
||||
throw new MailMessageSendException("邮件信息发送失败!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建模板
|
||||
*
|
||||
* @param name {@link String} 模板名称
|
||||
* @param templateContent {@link String} 模板内容
|
||||
* @return {@link Template}
|
||||
* @throws IOException IOException
|
||||
*/
|
||||
private Template createTemplate(String name, String templateContent) throws IOException {
|
||||
try {
|
||||
Template template = freeMarkerConfiguration.getTemplate(name, UTF_8);
|
||||
if (template != null) {
|
||||
if (template.toString().trim().equals(templateContent.trim())) {
|
||||
return template;
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
// 以下操作不是线程安全,要加上同步
|
||||
synchronized (this) {
|
||||
// 获取模板加载器
|
||||
TemplateLoader templateLoader = freeMarkerConfiguration.getTemplateLoader();
|
||||
if (templateLoader instanceof StringTemplateLoader) {
|
||||
// 如果加载器已经是字符串加载器,则在原来的加载器上put一个新的模板
|
||||
((StringTemplateLoader) templateLoader).putTemplate(name, templateContent);
|
||||
freeMarkerConfiguration.setTemplateLoader(templateLoader);
|
||||
} else {
|
||||
// 如果原来的模板加载器不是字符串的(默认是文件加载器),则新建
|
||||
StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
|
||||
stringTemplateLoader.putTemplate(name, templateContent);
|
||||
freeMarkerConfiguration.setTemplateLoader(stringTemplateLoader);
|
||||
}
|
||||
// 这里要清一下缓存,不然下面可能会获取不到模板
|
||||
freeMarkerConfiguration.clearTemplateCache();
|
||||
|
||||
return freeMarkerConfiguration.getTemplate(name, UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MailSend
|
||||
*/
|
||||
private final MailProviderSend mailProviderSend;
|
||||
|
||||
/**
|
||||
* MailSendRecordRepository
|
||||
*/
|
||||
private final MailSendRecordRepository mailSendRecordRepository;
|
||||
/**
|
||||
* redis
|
||||
*/
|
||||
private final RedisTemplate<Object, Object> redisTemplate;
|
||||
|
||||
private final Configuration freeMarkerConfiguration;
|
||||
|
||||
public MailMsgEventListener(MailProviderSend mailProviderSend,
|
||||
MailSendRecordRepository mailSendRecordRepository,
|
||||
RedisTemplate<Object, Object> redisTemplate) {
|
||||
this.mailProviderSend = mailProviderSend;
|
||||
this.mailSendRecordRepository = mailSendRecordRepository;
|
||||
this.redisTemplate = redisTemplate;
|
||||
freeMarkerConfiguration = new Configuration(Configuration.VERSION_2_3_31);
|
||||
//设置模板加载文件夹
|
||||
freeMarkerConfiguration.setClassForTemplateLoading(this.getClass(), "/mail/content");
|
||||
}
|
||||
|
||||
}
|
|
@ -23,15 +23,14 @@ import java.util.Map;
|
|||
import java.util.Objects;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import cn.topiam.employee.common.enums.MailType;
|
||||
import cn.topiam.employee.common.enums.MessageCategory;
|
||||
import cn.topiam.employee.common.message.enums.MessageType;
|
||||
import cn.topiam.employee.common.message.mail.MailProviderConfig;
|
||||
import cn.topiam.employee.core.mq.NoticeMessagePublisher;
|
||||
import cn.topiam.employee.support.context.ApplicationContextHelp;
|
||||
import cn.topiam.employee.support.exception.TopIamException;
|
||||
|
||||
|
@ -53,10 +52,11 @@ import static cn.topiam.employee.support.constant.EiamConstants.DEFAULT_DATE_TIM
|
|||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public class MailMsgEventPublish {
|
||||
|
||||
/**
|
||||
* NoticeMessagePublisher
|
||||
* ApplicationEventPublisher
|
||||
*/
|
||||
private final NoticeMessagePublisher noticeMessageProducer;
|
||||
private final ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
/**
|
||||
* 发布验证代码
|
||||
|
@ -101,7 +101,7 @@ public class MailMsgEventPublish {
|
|||
parameter.put(USER_EMAIL, receiver);
|
||||
// publish event
|
||||
ObjectMapper objectMapper = ApplicationContextHelp.getBean(ObjectMapper.class);
|
||||
noticeMessageProducer.sendNotice(MessageType.MAIL,
|
||||
objectMapper.writeValueAsString(new MailMessage(type, receiver, parameter)));
|
||||
applicationEventPublisher.publishEvent(new MailMsgEvent(type, receiver, parameter));
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,30 +15,36 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.core.dynamic;
|
||||
package cn.topiam.employee.core.message.sms;
|
||||
|
||||
import cn.topiam.employee.support.autoconfiguration.SupportProperties;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
import cn.topiam.employee.common.enums.SmsType;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* UserIndexName
|
||||
* 短信消息事件
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2022/11/3 23:31
|
||||
* Created by support@topiam.cn on 2021/9/25 21:07
|
||||
*/
|
||||
public class UserIndexName {
|
||||
|
||||
private final SupportProperties supportProperties;
|
||||
|
||||
public UserIndexName(SupportProperties supportProperties) {
|
||||
this.supportProperties = supportProperties;
|
||||
}
|
||||
|
||||
@Getter
|
||||
public class SmsMsgEvent extends ApplicationEvent {
|
||||
/**
|
||||
* 获取索引
|
||||
*
|
||||
* @return {@link String}
|
||||
* 消息类型
|
||||
*/
|
||||
public String getIndexName() {
|
||||
return supportProperties.getUser().getIndexPrefix();
|
||||
private final SmsType type;
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
private final Map<String, String> parameter;
|
||||
|
||||
public SmsMsgEvent(SmsType type, Map<String, String> parameter) {
|
||||
super(parameter);
|
||||
this.type = type;
|
||||
this.parameter = parameter;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* eiam-core - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.core.message.sms;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
|
||||
import cn.topiam.employee.common.entity.message.SmsSendRecordEntity;
|
||||
import cn.topiam.employee.common.enums.MessageCategory;
|
||||
import cn.topiam.employee.common.exception.SmsMessageSendException;
|
||||
import cn.topiam.employee.common.message.sms.SendSmsRequest;
|
||||
import cn.topiam.employee.common.message.sms.SmsProviderSend;
|
||||
import cn.topiam.employee.common.message.sms.SmsResponse;
|
||||
import cn.topiam.employee.common.repository.message.SmsSendRecordRepository;
|
||||
|
||||
/**
|
||||
* 短信消息通知事件
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2021/9/25 21:07
|
||||
*/
|
||||
@Async
|
||||
@Component
|
||||
public class SmsMsgEventListener implements ApplicationListener<SmsMsgEvent> {
|
||||
/**
|
||||
* Logger
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(SmsMsgEventListener.class);
|
||||
|
||||
/**
|
||||
* 发送事件通知
|
||||
*
|
||||
* @param event {@link SmsMsgEvent}
|
||||
*/
|
||||
@Override
|
||||
public void onApplicationEvent(@NonNull SmsMsgEvent event) {
|
||||
SendSmsRequest smsParam = new SendSmsRequest();
|
||||
try {
|
||||
//@formatter:off
|
||||
// 手机号
|
||||
smsParam.setPhone(event.getParameter().get(SmsMsgEventPublish.PHONE));
|
||||
// 模版编码
|
||||
smsParam.setTemplate(event.getParameter().get(SmsMsgEventPublish.TEMPLATE_CODE));
|
||||
// Content 记录参数值
|
||||
String content = event.getParameter().get(SmsMsgEventPublish.CONTENT);
|
||||
// 移除手机号,模版编码和Content
|
||||
event.getParameter().remove(SmsMsgEventPublish.PHONE);
|
||||
event.getParameter().remove(SmsMsgEventPublish.TEMPLATE_CODE);
|
||||
event.getParameter().remove(SmsMsgEventPublish.CONTENT);
|
||||
// 短信模版参数
|
||||
smsParam.setParameters(event.getParameter());
|
||||
|
||||
//@formatter:on
|
||||
SmsResponse send = smsProviderSend.send(smsParam);
|
||||
// 保存发送记录
|
||||
if (!Objects.isNull(send)) {
|
||||
//@formatter:off
|
||||
SmsSendRecordEntity record = new SmsSendRecordEntity()
|
||||
.setContent(content)
|
||||
.setResult(send.getMessage())
|
||||
.setSuccess(send.getSuccess())
|
||||
.setSendTime(LocalDateTime.now())
|
||||
.setProvider(send.getProvider())
|
||||
.setCategory(MessageCategory.CODE)
|
||||
.setType(event.getType())
|
||||
.setPhone(smsParam.getPhone());
|
||||
record.setRemark(JSON.toJSONString(send));
|
||||
if (!send.getSuccess()) {
|
||||
logger.error("发送短信失败: params: {}, response: {}", smsParam, send);
|
||||
}
|
||||
//@formatter:on
|
||||
smsSendLogRepository.save(record);
|
||||
} else {
|
||||
logger.error("发送短信失败,返回值为空: params: {}, ", smsParam);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("发送短信消息异常 params:{}, error: {}", smsParam, e.getMessage());
|
||||
throw new SmsMessageSendException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SmsSend
|
||||
*/
|
||||
private final SmsProviderSend smsProviderSend;
|
||||
|
||||
private final SmsSendRecordRepository smsSendLogRepository;
|
||||
|
||||
public SmsMsgEventListener(SmsProviderSend smsProviderSend,
|
||||
SmsSendRecordRepository smsSendLogRepository) {
|
||||
this.smsProviderSend = smsProviderSend;
|
||||
this.smsSendLogRepository = smsSendLogRepository;
|
||||
}
|
||||
}
|
|
@ -23,18 +23,15 @@ import java.util.Map;
|
|||
import java.util.Optional;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import cn.topiam.employee.common.entity.setting.config.SmsConfig;
|
||||
import cn.topiam.employee.common.enums.MessageCategory;
|
||||
import cn.topiam.employee.common.enums.SmsType;
|
||||
import cn.topiam.employee.common.message.enums.MessageType;
|
||||
import cn.topiam.employee.core.mq.NoticeMessagePublisher;
|
||||
import cn.topiam.employee.support.context.ApplicationContextHelp;
|
||||
import cn.topiam.employee.support.exception.TopIamException;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
@ -59,11 +56,6 @@ public class SmsMsgEventPublish {
|
|||
|
||||
public static final String CONTENT = "content";
|
||||
|
||||
/**
|
||||
* NoticeMessagePublisher
|
||||
*/
|
||||
private final NoticeMessagePublisher noticeMessageProducer;
|
||||
|
||||
public static final String PHONE = "phone";
|
||||
|
||||
public static final String USERNAME = "username";
|
||||
|
@ -110,9 +102,11 @@ public class SmsMsgEventPublish {
|
|||
parameter.put(TEMPLATE_CODE, templateConfig.getCode());
|
||||
parameter.put(CONTENT, JSON.toJSONString(parameter));
|
||||
// publish event
|
||||
ObjectMapper objectMapper = ApplicationContextHelp.getBean(ObjectMapper.class);
|
||||
noticeMessageProducer.sendNotice(MessageType.SMS,
|
||||
objectMapper.writeValueAsString(new SmsMessage(type, parameter)));
|
||||
applicationEventPublisher.publishEvent(new SmsMsgEvent(type, parameter));
|
||||
}
|
||||
|
||||
/**
|
||||
* ApplicationEventPublisher
|
||||
*/
|
||||
private final ApplicationEventPublisher applicationEventPublisher;
|
||||
}
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
* eiam-core - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.core.mq;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.messaging.handler.annotation.Headers;
|
||||
import org.springframework.messaging.handler.annotation.Payload;
|
||||
|
||||
import com.rabbitmq.client.Channel;
|
||||
|
||||
import cn.topiam.employee.support.trace.TraceUtils;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import static cn.topiam.employee.support.trace.TraceAspect.TRACE_ID;
|
||||
|
||||
/**
|
||||
* 用户消息监听器
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2023/5/30 23:12
|
||||
*/
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public abstract class AbstractMessageListener {
|
||||
|
||||
/**
|
||||
* 接收用户消息
|
||||
*
|
||||
* @param message {@link Message}
|
||||
* @param channel {@link Channel}
|
||||
* @param body {@link String}
|
||||
* @param headers {@link Map}
|
||||
*/
|
||||
public void onMessage(Message message, Channel channel, @Payload String body,
|
||||
@Headers Map<String, Object> headers) throws IOException {
|
||||
// 设置TraceId
|
||||
TraceUtils.put(String.valueOf(headers.get(TRACE_ID)));
|
||||
}
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
/*
|
||||
* eiam-core - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.core.mq;
|
||||
|
||||
import org.springframework.amqp.core.AmqpAdmin;
|
||||
import org.springframework.amqp.rabbit.AsyncRabbitTemplate;
|
||||
import org.springframework.amqp.rabbit.RabbitConverterFuture;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
|
||||
import cn.topiam.employee.support.constant.EiamConstants;
|
||||
import cn.topiam.employee.support.trace.TraceUtils;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import static cn.topiam.employee.support.trace.TraceAspect.TRACE_ID;
|
||||
|
||||
/**
|
||||
* 消息发送
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2023/6/15 23:12
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public abstract class AbstractMessagePublisher {
|
||||
|
||||
/**
|
||||
* 用户消息
|
||||
*/
|
||||
public final static String USER = "user";
|
||||
|
||||
/**
|
||||
* 用户保存
|
||||
*/
|
||||
public final static String USER_SAVE = USER + EiamConstants.POINT + "save";
|
||||
|
||||
/**
|
||||
* 用户删除
|
||||
*/
|
||||
public final static String USER_DELETE = USER + EiamConstants.POINT + "delete";
|
||||
|
||||
/**
|
||||
* 短信/邮件消息
|
||||
*/
|
||||
public final static String NOTICE = "notice";
|
||||
|
||||
/**
|
||||
* 短信
|
||||
*/
|
||||
public final static String NOTICE_SMS = NOTICE + EiamConstants.POINT + "sms";
|
||||
|
||||
/**
|
||||
* 邮件
|
||||
*/
|
||||
public final static String NOTICE_MAIL = NOTICE + EiamConstants.POINT + "mail";
|
||||
|
||||
/**
|
||||
* AsyncRabbitTemplate
|
||||
*/
|
||||
private final AsyncRabbitTemplate asyncRabbitTemplate;
|
||||
|
||||
/**
|
||||
* AmqpAdmin
|
||||
*/
|
||||
protected final AmqpAdmin amqpAdmin;
|
||||
|
||||
/**
|
||||
* 发送异步RocketMQ消息
|
||||
*
|
||||
* @param exchange {@link String}
|
||||
* @param routingKey {@link String}
|
||||
* @param message {@link String}
|
||||
*/
|
||||
public RabbitConverterFuture<Object> sendMessage(String exchange, String routingKey,
|
||||
String message) {
|
||||
// 获取traceId,放入消息keys属性中
|
||||
String traceId = TraceUtils.get();
|
||||
RabbitConverterFuture<Object> future = asyncRabbitTemplate.convertSendAndReceive(exchange,
|
||||
routingKey, MessageBuilder.withPayload(message).setHeader(TRACE_ID, traceId).build());
|
||||
return future;
|
||||
}
|
||||
}
|
|
@ -1,338 +0,0 @@
|
|||
/*
|
||||
* eiam-core - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.core.mq;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.messaging.handler.annotation.Headers;
|
||||
import org.springframework.messaging.handler.annotation.Payload;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.rabbitmq.client.Channel;
|
||||
|
||||
import cn.topiam.employee.common.entity.message.MailSendRecordEntity;
|
||||
import cn.topiam.employee.common.entity.message.SmsSendRecordEntity;
|
||||
import cn.topiam.employee.common.entity.setting.MailTemplateEntity;
|
||||
import cn.topiam.employee.common.enums.MailType;
|
||||
import cn.topiam.employee.common.enums.MessageCategory;
|
||||
import cn.topiam.employee.common.exception.MailMessageSendException;
|
||||
import cn.topiam.employee.common.exception.SmsMessageSendException;
|
||||
import cn.topiam.employee.common.message.mail.MailNoneProviderSend;
|
||||
import cn.topiam.employee.common.message.mail.MailProviderSend;
|
||||
import cn.topiam.employee.common.message.mail.SendMailRequest;
|
||||
import cn.topiam.employee.common.message.sms.SendSmsRequest;
|
||||
import cn.topiam.employee.common.message.sms.SmsNoneProviderSend;
|
||||
import cn.topiam.employee.common.message.sms.SmsProviderSend;
|
||||
import cn.topiam.employee.common.message.sms.SmsResponse;
|
||||
import cn.topiam.employee.common.repository.message.MailSendRecordRepository;
|
||||
import cn.topiam.employee.common.repository.message.SmsSendRecordRepository;
|
||||
import cn.topiam.employee.core.message.MsgVariable;
|
||||
import cn.topiam.employee.core.message.mail.MailMessage;
|
||||
import cn.topiam.employee.core.message.mail.MailUtils;
|
||||
import cn.topiam.employee.core.message.sms.SmsMessage;
|
||||
import cn.topiam.employee.core.message.sms.SmsMsgEventPublish;
|
||||
import cn.topiam.employee.core.setting.constant.MessageSettingConstants;
|
||||
import cn.topiam.employee.support.context.ApplicationContextHelp;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import freemarker.cache.StringTemplateLoader;
|
||||
import freemarker.cache.TemplateLoader;
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.Template;
|
||||
import static org.apache.commons.codec.CharEncoding.UTF_8;
|
||||
import static org.springframework.web.util.HtmlUtils.htmlUnescape;
|
||||
|
||||
import static cn.topiam.employee.core.mq.AbstractMessagePublisher.NOTICE_MAIL;
|
||||
import static cn.topiam.employee.core.mq.AbstractMessagePublisher.NOTICE_SMS;
|
||||
import static cn.topiam.employee.support.constant.EiamConstants.COLON;
|
||||
|
||||
/**
|
||||
* 短信/邮件消息监听器
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2023/5/30 23:12
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class NoticeMessageListener extends AbstractMessageListener {
|
||||
|
||||
/**
|
||||
* 接收消息
|
||||
*
|
||||
* @param message {@link Message}
|
||||
* @param channel {@link Channel}
|
||||
* @param body {@link String}
|
||||
* @param headers {@link Map}
|
||||
*/
|
||||
@Override
|
||||
@RabbitListener(queues = { NOTICE_SMS, NOTICE_MAIL }, ackMode = "MANUAL")
|
||||
@RabbitHandler()
|
||||
public void onMessage(Message message, Channel channel, @Payload String body,
|
||||
@Headers Map<String, Object> headers) throws IOException {
|
||||
super.onMessage(message, channel, body, headers);
|
||||
log.info("异步接收ES用户信息入参: [{}]", message);
|
||||
sendNotice(message, channel, body);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送短信邮件通知消息数据
|
||||
*
|
||||
* @param message {@link Message}
|
||||
* @param channel {@link Channel}
|
||||
* @param body {@link String}
|
||||
*/
|
||||
private String sendNotice(Message message, Channel channel, String body) throws IOException {
|
||||
try {
|
||||
String queueName = message.getMessageProperties().getConsumerQueue();
|
||||
if (Objects.isNull(body)) {
|
||||
log.warn("接收短信/邮件通知消息内容为空:[{}]", message.getMessageProperties().getDeliveryTag());
|
||||
return "接收短信/邮件通知消息内容为空";
|
||||
}
|
||||
log.info("接收通知消息:[{}]", body);
|
||||
ObjectMapper objectMapper = ApplicationContextHelp.getBean(ObjectMapper.class);
|
||||
if (queueName.equals(NOTICE_SMS)) {
|
||||
sendSms(objectMapper.readValue(body, SmsMessage.class));
|
||||
} else if (queueName.equals(NOTICE_MAIL)) {
|
||||
sendMail(objectMapper.readValue(body, MailMessage.class));
|
||||
}
|
||||
log.info("处理发送通知完成:[{}]", message.getMessageProperties().getDeliveryTag());
|
||||
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
|
||||
return "处理发送通知成功";
|
||||
} catch (Exception e) {
|
||||
log.error("处理发送通知消息出现异常: MessageProperties: [{}], message:[{}]",
|
||||
message.getMessageProperties(), body, e);
|
||||
channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
|
||||
return "处理发送通知失败";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送短信通知
|
||||
*
|
||||
* @param smsMessage {@link SmsMessage}
|
||||
*/
|
||||
private void sendSms(@NonNull SmsMessage smsMessage) {
|
||||
if (smsProviderSend instanceof SmsNoneProviderSend) {
|
||||
throw new SmsMessageSendException("暂未配置短信服务");
|
||||
}
|
||||
SendSmsRequest smsParam = new SendSmsRequest();
|
||||
try {
|
||||
//@formatter:off
|
||||
// 手机号
|
||||
smsParam.setPhone(smsMessage.getParameter().get(SmsMsgEventPublish.PHONE));
|
||||
// 模版编码
|
||||
smsParam.setTemplate(smsMessage.getParameter().get(SmsMsgEventPublish.TEMPLATE_CODE));
|
||||
// Content 记录参数值
|
||||
String content = smsMessage.getParameter().get(SmsMsgEventPublish.CONTENT);
|
||||
// 移除手机号,模版编码和Content
|
||||
smsMessage.getParameter().remove(SmsMsgEventPublish.PHONE);
|
||||
smsMessage.getParameter().remove(SmsMsgEventPublish.TEMPLATE_CODE);
|
||||
smsMessage.getParameter().remove(SmsMsgEventPublish.CONTENT);
|
||||
// 短信模版参数
|
||||
smsParam.setParameters(smsMessage.getParameter());
|
||||
//@formatter:on
|
||||
SmsResponse send = smsProviderSend.send(smsParam);
|
||||
// 保存发送记录
|
||||
if (!Objects.isNull(send)) {
|
||||
//@formatter:off
|
||||
SmsSendRecordEntity record = new SmsSendRecordEntity()
|
||||
.setContent(content)
|
||||
.setResult(send.getMessage())
|
||||
.setSuccess(send.getSuccess())
|
||||
.setSendTime(LocalDateTime.now())
|
||||
.setProvider(send.getProvider())
|
||||
.setCategory(MessageCategory.CODE)
|
||||
.setType(smsMessage.getType())
|
||||
.setPhone(smsParam.getPhone());
|
||||
record.setRemark(JSON.toJSONString(send));
|
||||
if (!send.getSuccess()) {
|
||||
log.error("发送短信失败: params: {}, response: {}", smsParam, send);
|
||||
}
|
||||
//@formatter:on
|
||||
smsSendLogRepository.save(record);
|
||||
} else {
|
||||
log.error("发送短信失败,返回值为空: params: {}, ", smsParam);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("发送短信消息异常 params:{}, error: {}", smsParam, e.getMessage());
|
||||
throw new SmsMessageSendException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送邮件通知
|
||||
*
|
||||
* @param mailMessage {@link MailMessage}
|
||||
*/
|
||||
private void sendMail(@NonNull MailMessage mailMessage) {
|
||||
if (mailProviderSend instanceof MailNoneProviderSend) {
|
||||
throw new MailMessageSendException("暂未配置邮件服务");
|
||||
}
|
||||
// 邮件通知类型
|
||||
MailType type = mailMessage.getType();
|
||||
String content = htmlUnescape(MailUtils.readEmailContent(type.getContent()));
|
||||
String subject = type.getSubject();
|
||||
String sender = type.getSender();
|
||||
try {
|
||||
Map<String, Object> parameter = mailMessage.getParameter();
|
||||
// 判断是否存在自定义
|
||||
MailTemplateEntity templateEntity = (MailTemplateEntity) redisTemplate.opsForValue()
|
||||
.get(MessageSettingConstants.SETTING_EMAIL_TEMPLATE_CACHE_NAME + COLON
|
||||
+ type.getCode());
|
||||
if (!Objects.isNull(templateEntity)) {
|
||||
content = htmlUnescape(templateEntity.getContent());
|
||||
subject = templateEntity.getSubject();
|
||||
sender = templateEntity.getSender();
|
||||
}
|
||||
// 主题
|
||||
StringWriter themeStringWriter = new StringWriter();
|
||||
Template themeTpl = createTemplate(type.getCode() + "-theme", subject);
|
||||
themeTpl.process(parameter, themeStringWriter);
|
||||
// 测试邮件
|
||||
if (parameter.containsKey(MsgVariable.TEST)) {
|
||||
String test = parameter.get(MsgVariable.TEST).toString();
|
||||
themeStringWriter.append(test);
|
||||
}
|
||||
// 内容
|
||||
StringWriter contentStringWriter = new StringWriter();
|
||||
Template contentTpl = createTemplate(type.getCode() + "-content", content);
|
||||
contentTpl.process(parameter, contentStringWriter);
|
||||
// 发送邮件
|
||||
//@formatter:off
|
||||
SendMailRequest params = new SendMailRequest()
|
||||
// 发送人
|
||||
.setSender(sender)
|
||||
// 接收人
|
||||
.setReceiver(mailMessage.getReceiver())
|
||||
// 主题
|
||||
.setSubject(themeStringWriter.toString())
|
||||
// 内容
|
||||
.setBody(contentStringWriter.toString());
|
||||
//@formatter:on
|
||||
// 发送邮件
|
||||
mailProviderSend.sendMailHtml(params);
|
||||
// 保存发送记录
|
||||
MailSendRecordEntity record = new MailSendRecordEntity();
|
||||
//@formatter:off
|
||||
record.setSender(mailMessage.getReceiver())
|
||||
.setSubject(params.getSubject())
|
||||
.setContent(params.getBody())
|
||||
.setProvider(mailProviderSend.getProvider())
|
||||
.setType(mailMessage.getType())
|
||||
.setReceiver(mailMessage.getReceiver())
|
||||
.setSendTime(LocalDateTime.now())
|
||||
.setSuccess(true);
|
||||
//@formatter:on
|
||||
mailSendRecordRepository.save(record);
|
||||
} catch (Exception e) {
|
||||
log.error("邮件信息发送失败: {}", e.getMessage());
|
||||
throw new MailMessageSendException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建模板
|
||||
*
|
||||
* @param name {@link String} 模板名称
|
||||
* @param templateContent {@link String} 模板内容
|
||||
* @return {@link Template}
|
||||
* @throws IOException IOException
|
||||
*/
|
||||
private Template createTemplate(String name, String templateContent) throws IOException {
|
||||
Template template = freeMarkerConfiguration.getTemplate(name, null, null, UTF_8, true,
|
||||
true);
|
||||
if (template != null) {
|
||||
return template;
|
||||
}
|
||||
// 以下操作不是线程安全,要加上同步
|
||||
synchronized (this) {
|
||||
// 获取模板加载器
|
||||
TemplateLoader templateLoader = freeMarkerConfiguration.getTemplateLoader();
|
||||
if (templateLoader instanceof StringTemplateLoader) {
|
||||
// 如果加载器已经是字符串加载器,则在原来的加载器上put一个新的模板
|
||||
((StringTemplateLoader) templateLoader).putTemplate(name, templateContent);
|
||||
freeMarkerConfiguration.setTemplateLoader(templateLoader);
|
||||
} else {
|
||||
// 如果原来的模板加载器不是字符串的(默认是文件加载器),则新建
|
||||
StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
|
||||
stringTemplateLoader.putTemplate(name, templateContent);
|
||||
freeMarkerConfiguration.setTemplateLoader(stringTemplateLoader);
|
||||
}
|
||||
// 这里要清一下缓存,不然下面可能会获取不到模板
|
||||
freeMarkerConfiguration.clearTemplateCache();
|
||||
return freeMarkerConfiguration.getTemplate(name, UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MailSend
|
||||
*/
|
||||
private final MailProviderSend mailProviderSend;
|
||||
|
||||
/**
|
||||
* MailSendRecordRepository
|
||||
*/
|
||||
private final MailSendRecordRepository mailSendRecordRepository;
|
||||
|
||||
/**
|
||||
* redis
|
||||
*/
|
||||
private final RedisTemplate<Object, Object> redisTemplate;
|
||||
|
||||
/**
|
||||
* Configuration
|
||||
*/
|
||||
private final Configuration freeMarkerConfiguration;
|
||||
|
||||
/**
|
||||
* SmsSend
|
||||
*/
|
||||
private final SmsProviderSend smsProviderSend;
|
||||
|
||||
/**
|
||||
* SmsSendRecordRepository
|
||||
*/
|
||||
private final SmsSendRecordRepository smsSendLogRepository;
|
||||
|
||||
public NoticeMessageListener(MailProviderSend mailProviderSend,
|
||||
MailSendRecordRepository mailSendRecordRepository,
|
||||
RedisTemplate<Object, Object> redisTemplate,
|
||||
SmsProviderSend smsProviderSend,
|
||||
SmsSendRecordRepository smsSendLogRepository) {
|
||||
this.mailProviderSend = mailProviderSend;
|
||||
this.mailSendRecordRepository = mailSendRecordRepository;
|
||||
this.redisTemplate = redisTemplate;
|
||||
this.smsProviderSend = smsProviderSend;
|
||||
this.smsSendLogRepository = smsSendLogRepository;
|
||||
this.freeMarkerConfiguration = new Configuration(Configuration.VERSION_2_3_31);
|
||||
//设置模板加载文件夹
|
||||
this.freeMarkerConfiguration.setClassForTemplateLoading(this.getClass(), "/mail/content");
|
||||
}
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
/*
|
||||
* eiam-core - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.core.mq;
|
||||
|
||||
import org.springframework.amqp.core.AmqpAdmin;
|
||||
import org.springframework.amqp.core.BindingBuilder;
|
||||
import org.springframework.amqp.core.Queue;
|
||||
import org.springframework.amqp.core.TopicExchange;
|
||||
import org.springframework.amqp.rabbit.AsyncRabbitTemplate;
|
||||
import org.springframework.amqp.rabbit.RabbitConverterFuture;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.topiam.employee.common.message.enums.MessageType;
|
||||
import cn.topiam.employee.support.constant.EiamConstants;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
|
||||
/**
|
||||
* MQ消息发送
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2023/6/15 23:12
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class NoticeMessagePublisher extends AbstractMessagePublisher {
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
TopicExchange topicExchange = new TopicExchange(NOTICE);
|
||||
Queue smsQueue = new Queue(NOTICE_SMS, true);
|
||||
Queue mailQueue = new Queue(NOTICE_MAIL, true);
|
||||
amqpAdmin.declareExchange(topicExchange);
|
||||
amqpAdmin.declareQueue(smsQueue);
|
||||
amqpAdmin.declareQueue(mailQueue);
|
||||
amqpAdmin.declareBinding(BindingBuilder.bind(smsQueue).to(topicExchange).with(NOTICE_SMS));
|
||||
amqpAdmin
|
||||
.declareBinding(BindingBuilder.bind(mailQueue).to(topicExchange).with(NOTICE_MAIL));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param asyncRabbitTemplate {@link AsyncRabbitTemplate}
|
||||
* @param amqpAdmin {@link AmqpAdmin}
|
||||
*/
|
||||
public NoticeMessagePublisher(AsyncRabbitTemplate asyncRabbitTemplate, AmqpAdmin amqpAdmin) {
|
||||
super(asyncRabbitTemplate, amqpAdmin);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送异步短信邮件通知消息
|
||||
*
|
||||
* @param type {@link MessageType}
|
||||
* @param message {@link String}
|
||||
*/
|
||||
public void sendNotice(MessageType type, String message) {
|
||||
log.info("发送[{}]通知消息, message:[{}]", type.getDesc(), message);
|
||||
String routingKey = NOTICE + EiamConstants.POINT + type.getCode().toLowerCase();
|
||||
RabbitConverterFuture<Object> future = sendMessage(NOTICE, routingKey, message);
|
||||
future.whenComplete((result, ex) -> {
|
||||
if (ex == null) {
|
||||
log.info("发送[{}]通知消息成功, message:[{}],处理结果为:[{}]", type.getDesc(), message, result);
|
||||
} else {
|
||||
log.info("发送[{}]通知消息异常,message:[{}],失败原因:[{}], ", type.getDesc(), message,
|
||||
ex.getMessage(), ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,202 +0,0 @@
|
|||
/*
|
||||
* eiam-core - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.core.mq;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.messaging.handler.annotation.Headers;
|
||||
import org.springframework.messaging.handler.annotation.Payload;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.rabbitmq.client.Channel;
|
||||
|
||||
import cn.topiam.employee.common.entity.account.UserElasticSearchEntity;
|
||||
import cn.topiam.employee.common.entity.account.po.UserEsPO;
|
||||
import cn.topiam.employee.common.repository.account.OrganizationRepository;
|
||||
import cn.topiam.employee.common.repository.account.UserElasticSearchRepository;
|
||||
import cn.topiam.employee.common.repository.account.UserRepository;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import static cn.topiam.employee.core.mq.AbstractMessagePublisher.USER_DELETE;
|
||||
import static cn.topiam.employee.core.mq.AbstractMessagePublisher.USER_SAVE;
|
||||
|
||||
/**
|
||||
* 用户消息监听器
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2023/5/30 23:12
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class UserMessageListener extends AbstractMessageListener {
|
||||
|
||||
/**
|
||||
* 接收用户消息
|
||||
*
|
||||
* @param message {@link Message}
|
||||
* @param channel {@link Channel}
|
||||
* @param body {@link String}
|
||||
* @param headers {@link Map}
|
||||
*/
|
||||
@Override
|
||||
@RabbitListener(queues = { USER_SAVE, USER_DELETE }, ackMode = "MANUAL")
|
||||
@RabbitHandler()
|
||||
public void onMessage(Message message, Channel channel, @Payload String body,
|
||||
@Headers Map<String, Object> headers) throws IOException {
|
||||
super.onMessage(message, channel, body, headers);
|
||||
log.info("异步接收ES用户信息入参: [{}]", message);
|
||||
syncUser(message, channel, body);
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步用户数据
|
||||
*
|
||||
* @param message {@link Message}
|
||||
* @param channel {@link Channel}
|
||||
* @param body {@link String}
|
||||
*/
|
||||
private void syncUser(Message message, Channel channel, String body) {
|
||||
try {
|
||||
// 处理消息逻辑
|
||||
String queueName = message.getMessageProperties().getConsumerQueue();
|
||||
if (!StringUtils.hasText(body)) {
|
||||
log.warn("接收用户消息内容为空:[{}]", message.getMessageProperties().getDeliveryTag());
|
||||
return;
|
||||
}
|
||||
log.info("接收用户消息:[{}]", body);
|
||||
List<String> idList = Arrays.asList(body.split(","));
|
||||
if (queueName.equals(USER_SAVE)) {
|
||||
List<UserEsPO> userList = userRepository.getUserList(idList);
|
||||
List<UserElasticSearchEntity> userElasticSearchEntity = getUserElasticSearchEntity(
|
||||
userList, organizationRepository);
|
||||
userElasticSearchRepository.saveAll(userElasticSearchEntity);
|
||||
} else if (queueName.equals(USER_DELETE)) {
|
||||
userElasticSearchRepository.deleteAllById(idList);
|
||||
}
|
||||
log.info("同步用户数据成功:[{}]", message.getMessageProperties().getDeliveryTag());
|
||||
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
|
||||
} catch (Exception e) {
|
||||
log.error("处理用户数据失败出现异常: MessageProperties: [{}], 用户ID:[{}], Error:[{}]",
|
||||
message.getMessageProperties(), body, e.getMessage(), e);
|
||||
try {
|
||||
channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
|
||||
} catch (IOException exception) {
|
||||
log.error("接收用户消息退回出现异常: MessageProperties: [{}], 用户ID:[{}], Error:[{}]",
|
||||
message.getMessageProperties(), body, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建用户es对象
|
||||
*
|
||||
* @param userList {@link List}
|
||||
* @param organizationRepository {@link OrganizationRepository}
|
||||
* @return {@link List}
|
||||
*/
|
||||
@NotNull
|
||||
public static List<UserElasticSearchEntity> getUserElasticSearchEntity(List<UserEsPO> userList,
|
||||
OrganizationRepository organizationRepository) {
|
||||
List<UserElasticSearchEntity> userElasticSearchEntityList = new ArrayList<>();
|
||||
userList.forEach(user -> {
|
||||
UserElasticSearchEntity entity = UserElasticSearchEntity.builder().build();
|
||||
entity.setId(user.getId().toString());
|
||||
entity.setUsername(user.getUsername());
|
||||
entity.setEmail(user.getEmail());
|
||||
entity.setPhone(user.getPhone());
|
||||
entity.setPhoneAreaCode(user.getPhoneAreaCode());
|
||||
entity.setFullName(user.getFullName());
|
||||
entity.setNickName(user.getNickName());
|
||||
entity.setAvatar(user.getAvatar());
|
||||
if (Objects.nonNull(user.getStatus())) {
|
||||
entity.setStatus(user.getStatus().getCode());
|
||||
}
|
||||
if (Objects.nonNull(user.getDataOrigin())) {
|
||||
entity.setDataOrigin(user.getDataOrigin().getCode());
|
||||
}
|
||||
entity.setIdentitySourceId(user.getIdentitySourceId());
|
||||
entity.setEmailVerified(user.getEmailVerified());
|
||||
entity.setPhoneVerified(user.getPhoneVerified());
|
||||
entity.setAuthTotal(user.getAuthTotal());
|
||||
entity.setLastAuthIp(user.getLastAuthIp());
|
||||
if (Objects.nonNull(user.getLastUpdatePasswordTime())) {
|
||||
entity.setLastUpdatePasswordTime(user.getLastUpdatePasswordTime());
|
||||
}
|
||||
if (Objects.nonNull(user.getLastAuthTime())) {
|
||||
entity.setLastAuthTime(user.getLastAuthTime());
|
||||
}
|
||||
entity.setExpand(user.getExpand());
|
||||
entity.setExternalId(user.getExternalId());
|
||||
if (Objects.nonNull(user.getExpireDate())) {
|
||||
entity.setExpireDate(user.getExpireDate());
|
||||
}
|
||||
// 用户详情
|
||||
entity.setIdType(user.getIdType());
|
||||
entity.setIdCard(user.getIdCard());
|
||||
entity.setWebsite(user.getWebsite());
|
||||
entity.setAddress(user.getAddress());
|
||||
// 组织列表
|
||||
if (!CollectionUtils.isEmpty(user.getOrganizationIds())) {
|
||||
entity.setOrganizations(organizationRepository
|
||||
.getOrganizationList(new ArrayList<>(user.getOrganizationIds())));
|
||||
}
|
||||
// 用户组
|
||||
List<UserElasticSearchEntity.UserGroup> userGroups = new ArrayList<>();
|
||||
if (!CollectionUtils.isEmpty(user.getUserGroups())) {
|
||||
userGroups.addAll(user.getUserGroups().entrySet().stream()
|
||||
.map(group -> new UserElasticSearchEntity.UserGroup(group.getKey(),
|
||||
group.getValue()))
|
||||
.toList());
|
||||
}
|
||||
entity.setUserGroups(userGroups);
|
||||
userElasticSearchEntityList.add(entity);
|
||||
});
|
||||
|
||||
return userElasticSearchEntityList;
|
||||
}
|
||||
|
||||
/**
|
||||
* UserElasticSearchRepository
|
||||
*/
|
||||
private final UserElasticSearchRepository userElasticSearchRepository;
|
||||
|
||||
/**
|
||||
* UserRepository
|
||||
*/
|
||||
private final UserRepository userRepository;
|
||||
|
||||
/**
|
||||
* OrganizationRepository
|
||||
*/
|
||||
private final OrganizationRepository organizationRepository;
|
||||
|
||||
public UserMessageListener(UserElasticSearchRepository userElasticSearchRepository,
|
||||
UserRepository userRepository,
|
||||
OrganizationRepository organizationRepository) {
|
||||
this.userElasticSearchRepository = userElasticSearchRepository;
|
||||
this.userRepository = userRepository;
|
||||
this.organizationRepository = organizationRepository;
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* eiam-core - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.core.mq;
|
||||
|
||||
import org.springframework.amqp.core.AmqpAdmin;
|
||||
import org.springframework.amqp.core.BindingBuilder;
|
||||
import org.springframework.amqp.core.Queue;
|
||||
import org.springframework.amqp.core.TopicExchange;
|
||||
import org.springframework.amqp.rabbit.AsyncRabbitTemplate;
|
||||
import org.springframework.amqp.rabbit.RabbitConverterFuture;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.topiam.employee.support.constant.EiamConstants;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
|
||||
/**
|
||||
* 用户MQ消息发送
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2023/5/30 23:12
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class UserMessagePublisher extends AbstractMessagePublisher {
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
TopicExchange topicExchange = new TopicExchange(USER);
|
||||
Queue saveQueue = new Queue(USER_SAVE, true);
|
||||
Queue deleteQueue = new Queue(USER_DELETE, true);
|
||||
amqpAdmin.declareExchange(topicExchange);
|
||||
amqpAdmin.declareQueue(saveQueue);
|
||||
amqpAdmin.declareQueue(deleteQueue);
|
||||
amqpAdmin.declareBinding(BindingBuilder.bind(saveQueue).to(topicExchange).with(USER_SAVE));
|
||||
amqpAdmin
|
||||
.declareBinding(BindingBuilder.bind(deleteQueue).to(topicExchange).with(USER_DELETE));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param asyncRabbitTemplate {@link AsyncRabbitTemplate}
|
||||
* @param amqpAdmin {@link AmqpAdmin}
|
||||
*/
|
||||
public UserMessagePublisher(AsyncRabbitTemplate asyncRabbitTemplate, AmqpAdmin amqpAdmin) {
|
||||
super(asyncRabbitTemplate, amqpAdmin);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送异步用户消息
|
||||
*
|
||||
* @param tag {@link UserMessageTag} SAVE/DELETE
|
||||
* @param userIds {@link String} 逗号分隔的用户id
|
||||
*/
|
||||
public void sendUserChangeMessage(UserMessageTag tag, String userIds) {
|
||||
log.info("发送[{}]用户消息, 用户ID:[{}]", tag.name(), userIds);
|
||||
String routingKey = USER + EiamConstants.POINT + tag.name().toLowerCase();
|
||||
RabbitConverterFuture<Object> future = sendMessage(USER, routingKey, userIds);
|
||||
future.whenComplete((result, ex) -> {
|
||||
if (ex == null) {
|
||||
log.info("发送[{}]用户消息成功, 用户ID:[{}],处理结果为:[{}]", tag.name(), userIds, result);
|
||||
} else {
|
||||
log.info("发送[{}]用户消息异常,用户ID:[{}],失败原因:[{}] ", tag.name(), userIds, ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* eiam-core - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.core.mq;
|
||||
|
||||
/**
|
||||
* 用户消息标签
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2023/5/30 23:12
|
||||
*/
|
||||
public enum UserMessageTag {
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
DELETE,
|
||||
/**
|
||||
* 保存
|
||||
*/
|
||||
SAVE
|
||||
}
|
|
@ -19,36 +19,23 @@ package cn.topiam.employee.core.security.password.task.impl;
|
|||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import cn.topiam.employee.common.entity.account.UserElasticSearchEntity;
|
||||
import cn.topiam.employee.common.entity.account.UserEntity;
|
||||
import cn.topiam.employee.common.entity.setting.SettingEntity;
|
||||
import cn.topiam.employee.common.enums.UserStatus;
|
||||
import cn.topiam.employee.common.repository.account.UserRepository;
|
||||
import cn.topiam.employee.common.repository.setting.SettingRepository;
|
||||
import cn.topiam.employee.core.mq.UserMessagePublisher;
|
||||
import cn.topiam.employee.core.mq.UserMessageTag;
|
||||
import cn.topiam.employee.core.security.password.task.PasswordExpireTask;
|
||||
import cn.topiam.employee.support.autoconfiguration.SupportProperties;
|
||||
import cn.topiam.employee.support.lock.Lock;
|
||||
import cn.topiam.employee.support.trace.Trace;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import co.elastic.clients.elasticsearch._types.FieldValue;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.TermsQueryField;
|
||||
import static cn.topiam.employee.core.setting.constant.PasswordPolicySettingConstants.PASSWORD_POLICY_DEFAULT_SETTINGS;
|
||||
import static cn.topiam.employee.core.setting.constant.PasswordPolicySettingConstants.PASSWORD_POLICY_VALID_DAYS;
|
||||
|
||||
|
@ -66,48 +53,30 @@ public class PasswordExpireLockTask implements PasswordExpireTask {
|
|||
/**
|
||||
* 锁定密码过期用户
|
||||
*/
|
||||
@Lock(throwException = false)
|
||||
@Trace
|
||||
@Scheduled(cron = "0 0 0 * * ?")
|
||||
@Override
|
||||
public void execute() {
|
||||
long start = System.currentTimeMillis();
|
||||
logger.info("密码过期锁定用户任务开始");
|
||||
int expireDays = getExpireDays();
|
||||
// 查询非密码过期锁定和过期锁定用户信息
|
||||
Query query = QueryBuilders.terms(builder -> {
|
||||
builder.terms(new TermsQueryField.Builder().value(
|
||||
Lists.newArrayList(FieldValue.of(UserStatus.PASSWORD_EXPIRED_LOCKED.getCode()),
|
||||
FieldValue.of(UserStatus.EXPIRED_LOCKED.getCode())))
|
||||
.build());
|
||||
builder.field("status");
|
||||
return builder;
|
||||
});
|
||||
List<UserElasticSearchEntity> userElasticSearchList = userRepository
|
||||
.getAllUserElasticSearchEntity(
|
||||
IndexCoordinates.of(supportProperties.getUser().getIndexPrefix()), query);
|
||||
List<String> lockedUserIdList = new ArrayList<>();
|
||||
Iterator<UserElasticSearchEntity> iterator = userElasticSearchList.iterator();
|
||||
logger.info("密码过期待锁定用户数量为:{}个", userElasticSearchList.size());
|
||||
while (!userElasticSearchList.isEmpty()) {
|
||||
UserElasticSearchEntity entity = iterator.next();
|
||||
//1、根据提醒时间,分页查询即将要过期的密码
|
||||
List<UserEntity> list = userRepository.findPasswordExpireUser(expireDays);
|
||||
Iterator<UserEntity> iterator = list.iterator();
|
||||
logger.info("密码过期待锁定用户数量为:{}个", list.size());
|
||||
while (!list.isEmpty()) {
|
||||
UserEntity entity = iterator.next();
|
||||
//获取到期日期
|
||||
LocalDateTime expiredDate = entity.getLastUpdatePasswordTime().atOffset(ZoneOffset.MAX)
|
||||
.plusDays(expireDays).toLocalDateTime();
|
||||
if (LocalDateTime.now().isBefore(expiredDate)) {
|
||||
lockedUserIdList.add(entity.getId());
|
||||
userRepository.updateUserStatus(Long.valueOf(entity.getId()),
|
||||
UserStatus.PASSWORD_EXPIRED_LOCKED);
|
||||
logger.info("锁定密码过期用户:{}", entity.getUsername());
|
||||
entity.setStatus(UserStatus.PASSWORD_EXPIRED_LOCKED);
|
||||
userRepository.save(entity);
|
||||
logger.info("锁定密码过期用户:{}", entity.getUsername());
|
||||
iterator.remove();
|
||||
}
|
||||
iterator = userElasticSearchList.iterator();
|
||||
iterator = list.iterator();
|
||||
}
|
||||
// 推送es用户消息
|
||||
userMessagePublisher.sendUserChangeMessage(UserMessageTag.SAVE,
|
||||
String.join(",", lockedUserIdList));
|
||||
logger.info("密码过期锁定用户任务结束: 冻结用户数量[{}], 耗时:[{}]s", lockedUserIdList.size(),
|
||||
(System.currentTimeMillis() - start) / 1000);
|
||||
logger.info("密码过期锁定用户任务结束");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -133,14 +102,4 @@ public class PasswordExpireLockTask implements PasswordExpireTask {
|
|||
*/
|
||||
private final UserRepository userRepository;
|
||||
|
||||
/**
|
||||
* SupportProperties
|
||||
*/
|
||||
private final SupportProperties supportProperties;
|
||||
|
||||
/**
|
||||
* UserMessagePublisher
|
||||
*/
|
||||
private final UserMessagePublisher userMessagePublisher;
|
||||
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ package cn.topiam.employee.core.security.password.task.impl;
|
|||
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
|
@ -29,29 +28,22 @@ import java.util.Objects;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.elasticsearch.client.elc.Queries;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import cn.topiam.employee.common.entity.account.UserElasticSearchEntity;
|
||||
import cn.topiam.employee.common.entity.account.UserEntity;
|
||||
import cn.topiam.employee.common.entity.setting.SettingEntity;
|
||||
import cn.topiam.employee.common.enums.MailType;
|
||||
import cn.topiam.employee.common.enums.SmsType;
|
||||
import cn.topiam.employee.common.enums.UserStatus;
|
||||
import cn.topiam.employee.common.repository.account.UserRepository;
|
||||
import cn.topiam.employee.common.repository.setting.SettingRepository;
|
||||
import cn.topiam.employee.core.message.mail.MailMsgEventPublish;
|
||||
import cn.topiam.employee.core.message.sms.SmsMsgEventPublish;
|
||||
import cn.topiam.employee.core.security.password.task.PasswordExpireTask;
|
||||
import cn.topiam.employee.support.autoconfiguration.SupportProperties;
|
||||
import cn.topiam.employee.support.lock.Lock;
|
||||
import cn.topiam.employee.support.trace.Trace;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
|
||||
import static cn.topiam.employee.core.message.MsgVariable.EXPIRE_DAYS;
|
||||
import static cn.topiam.employee.core.setting.constant.PasswordPolicySettingConstants.*;
|
||||
|
||||
|
@ -68,41 +60,28 @@ public class PasswordExpireWarnTask implements PasswordExpireTask {
|
|||
/**
|
||||
* 提醒 每天凌晨扫描,发送邮件及短信提醒
|
||||
*/
|
||||
@Lock(throwException = false)
|
||||
@Trace
|
||||
@Scheduled(cron = "0 0 0 * * ?")
|
||||
@Override
|
||||
public void execute() {
|
||||
long start = System.currentTimeMillis();
|
||||
logger.info("密码过期前提醒任务开始");
|
||||
//密码有效期天数
|
||||
int expireDays = getExpireDays();
|
||||
//密码过期前提醒天数
|
||||
int warnExpireDays = getWarnExpireDays();
|
||||
//1、根据提醒时间,分页查询即将要过期的密码
|
||||
// 查询已启用用户信息
|
||||
Query query = Queries.termQueryAsQuery("status", UserStatus.ENABLE.getCode());
|
||||
List<UserElasticSearchEntity> list = userRepository.getAllUserElasticSearchEntity(
|
||||
IndexCoordinates.of(supportProperties.getUser().getIndexPrefix()), query);
|
||||
List<UserEntity> list = userRepository
|
||||
.findPasswordExpireWarnUser(expireDays - warnExpireDays);
|
||||
logger.info("待提醒用户为:{}个", list.size());
|
||||
int reminderNumber = 0;
|
||||
//2、发送通知提醒
|
||||
for (UserElasticSearchEntity entity : list) {
|
||||
// 获取到密码期日期
|
||||
LocalDate expiredDate = entity.getLastUpdatePasswordTime().atOffset(ZoneOffset.MAX)
|
||||
.plusDays(expireDays).toLocalDate();
|
||||
LocalDate warnDate = expiredDate.minusDays(warnExpireDays);
|
||||
//如果当前日期等于密码到期日期前几天的日期
|
||||
if (LocalDate.now().equals(warnDate)) {
|
||||
reminderNumber++;
|
||||
long expiredDays = Duration.between(LocalDate.now().atTime(LocalTime.MIN),
|
||||
expiredDate.atTime(LocalTime.MIN)).toDays();
|
||||
for (UserEntity entity : list) {
|
||||
//如果邮箱不为空,发送邮件通知
|
||||
if (StringUtils.isNotBlank(entity.getEmail())) {
|
||||
logger.info("开始提醒用户:{}", entity.getUsername());
|
||||
//获取到期日期
|
||||
LocalDate expiredDate = entity.getLastUpdatePasswordTime().atOffset(ZoneOffset.MAX)
|
||||
.plusDays(expireDays).toLocalDate();
|
||||
HashMap<String, Object> map = Maps.newHashMap();
|
||||
//查询两个时间的间距
|
||||
map.put(EXPIRE_DAYS, expiredDays);
|
||||
map.put(EXPIRE_DAYS, Duration.between(LocalDate.now(), expiredDate).toDays());
|
||||
mailMsgEventPublish.publish(MailType.PASSWORD_SOON_EXPIRED_REMIND,
|
||||
entity.getEmail(), map);
|
||||
logger.info("结束提醒用户:{}", entity.getUsername());
|
||||
|
@ -110,17 +89,19 @@ public class PasswordExpireWarnTask implements PasswordExpireTask {
|
|||
// 短信通知
|
||||
if (StringUtils.isNotBlank(entity.getPhone())) {
|
||||
logger.info("开始提醒用户:{}", entity.getUsername());
|
||||
// 获取到期日期
|
||||
LocalDate expiredDate = entity.getLastUpdatePasswordTime().atOffset(ZoneOffset.MAX)
|
||||
.plusDays(expireDays).toLocalDate();
|
||||
LinkedHashMap<String, String> map = Maps.newLinkedHashMap();
|
||||
// 查询两个时间的间距
|
||||
map.put(EXPIRE_DAYS, String.valueOf(expiredDays));
|
||||
smsMsgEventPublish.publish(SmsType.PASSWORD_SOON_EXPIRED_REMIND,
|
||||
entity.getPhone(), map);
|
||||
map.put(EXPIRE_DAYS,
|
||||
String.valueOf(Duration.between(LocalDate.now(), expiredDate).toDays()));
|
||||
smsMsgEventPublish.publish(SmsType.PASSWORD_SOON_EXPIRED_REMIND, entity.getPhone(),
|
||||
map);
|
||||
logger.info("结束提醒用户:{}", entity.getUsername());
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.info("密码过期前提醒任务结束, 提醒用户数量[{}], 耗时:[{}]s", reminderNumber,
|
||||
(System.currentTimeMillis() - start) / 1000);
|
||||
logger.info("密码过期前提醒任务结束");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -169,9 +150,4 @@ public class PasswordExpireWarnTask implements PasswordExpireTask {
|
|||
* 短信消息发送
|
||||
*/
|
||||
private final SmsMsgEventPublish smsMsgEventPublish;
|
||||
|
||||
/**
|
||||
* SupportProperties
|
||||
*/
|
||||
private final SupportProperties supportProperties;
|
||||
}
|
||||
|
|
|
@ -1,143 +0,0 @@
|
|||
/*
|
||||
* eiam-core - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package schema;
|
||||
|
||||
import cn.topiam.employee.common.schema.FormSchema;
|
||||
import cn.topiam.employee.common.schema.field.*;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* FormSchemaTests
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2023/4/12 21:17
|
||||
*/
|
||||
public class FormSchemaTests {
|
||||
private final Logger logger= LoggerFactory.getLogger(FormSchemaTests.class);
|
||||
|
||||
|
||||
@Test
|
||||
public void test() throws JsonProcessingException {
|
||||
//@formatter:off
|
||||
// Input
|
||||
Input input = (Input) new Input("输入框")
|
||||
.addRule(new Field.Rule().required(true).min(2).max(20).type("string"))
|
||||
.setPlaceholder(Lists.newArrayList("请输入")).setDisabled(false);
|
||||
//数字输入
|
||||
NumberInput numberInput = new NumberInput("数字");
|
||||
//文本区域
|
||||
TextArea textArea = new TextArea("文本区域");
|
||||
// Select
|
||||
ConcurrentMap<String, Object> selectProps = Maps.newConcurrentMap();
|
||||
selectProps.put("allowClear", "true");
|
||||
Select select = (Select) new Select("下拉单选",
|
||||
Lists.newArrayList(
|
||||
new Option("选项A", "A"),
|
||||
new Option("选项B", "B"),
|
||||
new Option("选项C", "C")))
|
||||
.addRule(new Field.Rule().required(true).message("请选择一个选项"))
|
||||
.setPlaceholder(Lists.newArrayList("请选择一个选项"))
|
||||
.addProps(selectProps);
|
||||
|
||||
//多选Select
|
||||
MultiSelect multiSelect = (MultiSelect) new MultiSelect("下拉多选",
|
||||
Lists.newArrayList(
|
||||
new Option("选项A", "A"),
|
||||
new Option("选项B", "B"),
|
||||
new Option("选项C", "C")))
|
||||
.addRule(new Field.Rule().required(true).message("请选择一个选项"))
|
||||
.setPlaceholder(Lists.newArrayList("请选择一个选项"))
|
||||
.addProps(selectProps);
|
||||
|
||||
//Switch
|
||||
Switch aswitch = new Switch("开关");
|
||||
//Radio
|
||||
Radio radio = (Radio) new Radio("单选框",
|
||||
Lists.newArrayList(
|
||||
new Option("选项A", "A"),
|
||||
new Option("选项B", "B"),
|
||||
new Option("选项C", "C")))
|
||||
.setRules(Lists.newArrayList(new Field.Rule().required(true).message("请选择一个选项")));
|
||||
|
||||
//多选框
|
||||
Checkboxes checkBoxes = new Checkboxes("多选框", Lists.newArrayList(
|
||||
new Option("选项A", "A"),
|
||||
new Option("选项B", "B"),
|
||||
new Option("选项C", "C")));
|
||||
|
||||
//单选框
|
||||
Checkbox checkBox = new Checkbox("单选框");
|
||||
|
||||
//年
|
||||
Year year = new Year("年");
|
||||
|
||||
//季度
|
||||
Quarter quarter = new Quarter("季度");
|
||||
|
||||
//月份
|
||||
Month month = new Month("月份");
|
||||
|
||||
//周选择
|
||||
Week week = new Week("周");
|
||||
|
||||
//日期
|
||||
Date date = new Date("日期");
|
||||
|
||||
//日期时间
|
||||
DateTime dateTime = new DateTime("日期时间");
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||
|
||||
//FormSchema
|
||||
FormSchema formSchema = new FormSchema();
|
||||
formSchema.setType("object");
|
||||
HashMap<String, Object> map = new HashMap<>(16);
|
||||
map.put("input", input);
|
||||
map.put("number", numberInput);
|
||||
map.put("textarea", textArea);
|
||||
map.put("select", select);
|
||||
map.put("multiselect", multiSelect);
|
||||
map.put("radio", radio);
|
||||
map.put("switch", aswitch);
|
||||
map.put("checkbox", checkBox);
|
||||
map.put("checkboxes", checkBoxes);
|
||||
map.put("year", year);
|
||||
map.put("month", month);
|
||||
map.put("quarter", quarter);
|
||||
map.put("week", week);
|
||||
map.put("date", date);
|
||||
map.put("date_time", dateTime);
|
||||
formSchema.setProperties(map);
|
||||
|
||||
String schema = objectMapper.writerWithDefaultPrettyPrinter()
|
||||
.writeValueAsString(formSchema);
|
||||
|
||||
logger.info("JSON Schema: \n{}",schema);
|
||||
}
|
||||
}
|
|
@ -20,13 +20,6 @@ package cn.topiam.employee;
|
|||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.web.servlet.ServletComponentScan;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.FilterType;
|
||||
|
||||
import cn.topiam.employee.audit.controller.AuditController;
|
||||
import cn.topiam.employee.common.exception.handler.GlobalExceptionHandler;
|
||||
import cn.topiam.employee.common.storage.controller.StorageFileResource;
|
||||
import cn.topiam.employee.core.configuration.EiamApiConfiguration;
|
||||
|
||||
/**
|
||||
* 应用程序启动入口
|
||||
|
@ -35,11 +28,7 @@ import cn.topiam.employee.core.configuration.EiamApiConfiguration;
|
|||
* Created by support@topiam.cn on 2020/7/9
|
||||
*/
|
||||
@ServletComponentScan
|
||||
@SpringBootApplication(scanBasePackages = { "cn.topiam.employee" })
|
||||
@ComponentScan(excludeFilters = { @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = { EiamApiConfiguration.class,
|
||||
StorageFileResource.class,
|
||||
AuditController.class,
|
||||
GlobalExceptionHandler.class }) })
|
||||
@SpringBootApplication
|
||||
public class EiamOpenApiApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(EiamOpenApiApplication.class, args);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue