mirror of https://gitee.com/topiam/eiam
统计调整
parent
f35ab5abbb
commit
08dca580c3
|
@ -73,6 +73,7 @@ public class AuditEventListener implements ApplicationListener<AuditEvent> {
|
||||||
entity.setUserAgent(userAgent);
|
entity.setUserAgent(userAgent);
|
||||||
entity.setActorId(actor.getId());
|
entity.setActorId(actor.getId());
|
||||||
entity.setActorType(actor.getType());
|
entity.setActorType(actor.getType());
|
||||||
|
entity.setActorAuthType(actor.getAuthType());
|
||||||
auditRepository.save(entity);
|
auditRepository.save(entity);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Audit record saving failed: {}", JSONObject.toJSONString(entity), e);
|
logger.error("Audit record saving failed: {}", JSONObject.toJSONString(entity), e);
|
||||||
|
|
|
@ -78,6 +78,13 @@ public class AccountEventType {
|
||||||
public static Type MOVE_ORGANIZATION = new Type(
|
public static Type MOVE_ORGANIZATION = new Type(
|
||||||
"eiam:event:account:move_organization", "移动组织", ORG_ACCOUNT_RESOURCE, List.of(ADMIN));
|
"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),
|
MOVE_ORGANIZATION(AccountEventType.MOVE_ORGANIZATION),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加组织用户
|
||||||
|
*/
|
||||||
|
CREATE_ORGANIZATION_MEMBER(AccountEventType.CREATE_ORGANIZATION_MEMBER),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建用户
|
* 创建用户
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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
|
@Repository
|
||||||
public interface AuditRepository extends LogicDeleteRepository<AuditEntity, Long>,
|
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}
|
* @return {@link AuditEntity}
|
||||||
*/
|
*/
|
||||||
Optional<AuditEntity> findByRequestId(String requestId);
|
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;
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* 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.result;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审计统计
|
||||||
|
*
|
||||||
|
* @author TopIAM
|
||||||
|
* Created by support@topiam.cn on 2023/10/04 23:16
|
||||||
|
*/
|
||||||
|
@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)
|
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* 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/>.
|
* 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 java.io.Serializable;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
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
|
* Created by support@topiam.cn on 2020/11/22 23:16
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Schema(description = "认证量统计响应")
|
@Schema(description = "认证量统计响应")
|
||||||
public class AuthnQuantityResult implements Serializable {
|
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)
|
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* 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/>.
|
* 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.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
@ -33,7 +35,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Schema(description = "登录区域响应")
|
@Schema(description = "登录区域响应")
|
||||||
public class AuthnZoneResult {
|
public class AuthnZoneResult implements Serializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 省份code
|
* 省份code
|
|
@ -55,13 +55,19 @@ public class OrganizationMemberEntity extends LogicDeleteEntity<Long> {
|
||||||
* 组织机构ID
|
* 组织机构ID
|
||||||
*/
|
*/
|
||||||
@Column(name = "org_id")
|
@Column(name = "org_id")
|
||||||
private String orgId;
|
private String orgId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户ID
|
* 用户ID
|
||||||
*/
|
*/
|
||||||
@Column(name = "user_id")
|
@Column(name = "user_id")
|
||||||
private Long userId;
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主组织
|
||||||
|
*/
|
||||||
|
@Column(name = "primary_")
|
||||||
|
private Boolean primary;
|
||||||
|
|
||||||
public OrganizationMemberEntity() {
|
public OrganizationMemberEntity() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,4 +41,9 @@ public class UserPO extends UserEntity {
|
||||||
* 组织机构显示目录
|
* 组织机构显示目录
|
||||||
*/
|
*/
|
||||||
private String orgDisplayPath;
|
private String orgDisplayPath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主组织机构显示目录
|
||||||
|
*/
|
||||||
|
private String primaryOrgDisplayPath;
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,8 @@ public class UserGroupMemberRepositoryCustomizedImpl implements
|
||||||
`u`.update_by,
|
`u`.update_by,
|
||||||
`u`.update_time,
|
`u`.update_time,
|
||||||
`u`.remark_,
|
`u`.remark_,
|
||||||
group_concat( organization_.display_path ) AS org_display_path
|
group_concat( IF(organization_member.primary_ = 1, null, organization_.display_path ) ) AS org_display_path,
|
||||||
|
group_concat( IF(organization_member.primary_ IS NULL, null, organization_.display_path ) ) AS primary_org_display_path
|
||||||
FROM
|
FROM
|
||||||
user_group_member ugm
|
user_group_member ugm
|
||||||
INNER JOIN user u ON ugm.user_id = u.id_ AND u.is_deleted = '0'
|
INNER JOIN user u ON ugm.user_id = u.id_ AND u.is_deleted = '0'
|
||||||
|
|
|
@ -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.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.setLastAuthTime(ObjectUtils.isNotEmpty(rs.getTimestamp("last_auth_time")) ? rs.getTimestamp("last_auth_time").toLocalDateTime() : null);
|
||||||
user.setOrgDisplayPath(rs.getString("org_display_path"));
|
user.setOrgDisplayPath(rs.getString("org_display_path"));
|
||||||
|
user.setPrimaryOrgDisplayPath(rs.getString("primary_org_display_path"));
|
||||||
//额外数据
|
//额外数据
|
||||||
user.setCreateBy(rs.getString("create_by"));
|
user.setCreateBy(rs.getString("create_by"));
|
||||||
user.setCreateTime(ObjectUtils.isNotEmpty(rs.getTimestamp("create_time")) ? rs.getTimestamp("create_time").toLocalDateTime() : null);
|
user.setCreateTime(ObjectUtils.isNotEmpty(rs.getTimestamp("create_time")) ? rs.getTimestamp("create_time").toLocalDateTime() : null);
|
||||||
|
|
|
@ -82,11 +82,22 @@
|
||||||
<constraints nullable="true"/>
|
<constraints nullable="true"/>
|
||||||
</column>
|
</column>
|
||||||
</createTable>
|
</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">
|
<createIndex tableName="app_group_association" indexName="uk_app_group_association" unique="true">
|
||||||
<column name="group_id"/>
|
<column name="group_id"/>
|
||||||
<column name="app_id"/>
|
<column name="app_id"/>
|
||||||
<column name="is_deleted"/>
|
<column name="is_deleted"/>
|
||||||
</createIndex>
|
</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>
|
</changeSet>
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
|
@ -39,6 +39,8 @@ import {
|
||||||
Table,
|
Table,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Typography,
|
Typography,
|
||||||
|
Tag,
|
||||||
|
Popover
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import React, { useRef, useState } from 'react';
|
import React, { useRef, useState } from 'react';
|
||||||
import CreateUser from '../CreateUser';
|
import CreateUser from '../CreateUser';
|
||||||
|
@ -182,6 +184,45 @@ export default (props: UserListProps) => {
|
||||||
dataIndex: 'orgDisplayPath',
|
dataIndex: 'orgDisplayPath',
|
||||||
search: false,
|
search: false,
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
|
render: (_, record) => [
|
||||||
|
<Popover
|
||||||
|
key="pop"
|
||||||
|
title={
|
||||||
|
<Tag color={'geekblue'} key={record.primaryOrgDisplayPath}>
|
||||||
|
{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' }),
|
title: intl.formatMessage({ id: 'pages.account.user_list.user.columns.status' }),
|
||||||
|
|
|
@ -25,6 +25,8 @@ import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
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.query.analysis.AnalysisQuery;
|
||||||
import cn.topiam.employee.console.pojo.result.analysis.*;
|
import cn.topiam.employee.console.pojo.result.analysis.*;
|
||||||
import cn.topiam.employee.console.service.analysis.AnalysisService;
|
import cn.topiam.employee.console.service.analysis.AnalysisService;
|
||||||
|
|
|
@ -69,15 +69,15 @@ public class AnalysisQuery implements Serializable {
|
||||||
/**
|
/**
|
||||||
* HOUR
|
* HOUR
|
||||||
*/
|
*/
|
||||||
HOUR(CalendarInterval.Hour, "HH时"),
|
HOUR(CalendarInterval.Hour, "%h时"),
|
||||||
/**
|
/**
|
||||||
* DAY
|
* DAY
|
||||||
*/
|
*/
|
||||||
DAY(CalendarInterval.Day, "dd日"),
|
DAY(CalendarInterval.Day, "%d日"),
|
||||||
/**
|
/**
|
||||||
* MONTH
|
* MONTH
|
||||||
*/
|
*/
|
||||||
MONTH(CalendarInterval.Month, "MM月");
|
MONTH(CalendarInterval.Month, "%m月");
|
||||||
|
|
||||||
private final CalendarInterval type;
|
private final CalendarInterval type;
|
||||||
private final String format;
|
private final String format;
|
||||||
|
|
|
@ -19,6 +19,8 @@ package cn.topiam.employee.console.service.analysis;
|
||||||
|
|
||||||
import java.util.List;
|
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.query.analysis.AnalysisQuery;
|
||||||
import cn.topiam.employee.console.pojo.result.analysis.*;
|
import cn.topiam.employee.console.pojo.result.analysis.*;
|
||||||
|
|
||||||
|
@ -65,7 +67,7 @@ public interface AnalysisService {
|
||||||
* 登录区域统计
|
* 登录区域统计
|
||||||
*
|
*
|
||||||
* @param params {@link AnalysisQuery}
|
* @param params {@link AnalysisQuery}
|
||||||
* @return {@link List<AuthnZoneResult>}
|
* @return {@link List< AuthnZoneResult >}
|
||||||
*/
|
*/
|
||||||
List<AuthnZoneResult> authnZone(AnalysisQuery params);
|
List<AuthnZoneResult> authnZone(AnalysisQuery params);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,14 +24,15 @@ import java.util.*;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.springframework.data.elasticsearch.client.elc.*;
|
import org.springframework.data.elasticsearch.client.elc.*;
|
||||||
import org.springframework.data.elasticsearch.core.SearchHits;
|
import org.springframework.data.elasticsearch.core.SearchHits;
|
||||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import cn.topiam.employee.audit.entity.AuditElasticSearchEntity;
|
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.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.entity.app.AppEntity;
|
||||||
import cn.topiam.employee.common.repository.account.UserRepository;
|
import cn.topiam.employee.common.repository.account.UserRepository;
|
||||||
import cn.topiam.employee.common.repository.app.AppRepository;
|
import cn.topiam.employee.common.repository.app.AppRepository;
|
||||||
|
@ -44,17 +45,12 @@ import cn.topiam.employee.support.autoconfiguration.SupportProperties;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
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.aggregations.Aggregation;
|
||||||
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
|
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.Query;
|
||||||
import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders;
|
import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders;
|
||||||
import co.elastic.clients.json.JsonData;
|
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.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.console.converter.authn.IdentityProviderConverter.getIdentityProviderType;
|
||||||
import static cn.topiam.employee.support.constant.EiamConstants.DEFAULT_DATE_TIME_FORMATTER_PATTERN;
|
import static cn.topiam.employee.support.constant.EiamConstants.DEFAULT_DATE_TIME_FORMATTER_PATTERN;
|
||||||
|
|
||||||
|
@ -76,27 +72,13 @@ public class AnalysisServiceImpl implements AnalysisService {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public OverviewResult overview() {
|
public OverviewResult overview() {
|
||||||
IndexCoordinates indexCoordinates = IndexCoordinates
|
|
||||||
.of(getAuditIndexPrefix(supportProperties.getAudit().getIndexPrefix()) + "*");
|
|
||||||
// 不存在索引
|
|
||||||
if (!elasticsearchTemplate.indexOps(indexCoordinates).exists()) {
|
|
||||||
return new OverviewResult();
|
|
||||||
}
|
|
||||||
OverviewResult result = new OverviewResult();
|
OverviewResult result = new OverviewResult();
|
||||||
result.setAppCount(appRepository.count());
|
result.setAppCount(appRepository.count());
|
||||||
result.setUserCount(userRepository.count());
|
result.setUserCount(userRepository.count());
|
||||||
result.setIdpCount(identityProviderRepository.count());
|
result.setIdpCount(identityProviderRepository.count());
|
||||||
// 查询今日认证量条件
|
// 查询今日认证量条件
|
||||||
Query rangeQuery = QueryBuilders.range(range -> range.field(EVENT_TIME)
|
result.setTodayAuthnCount(auditRepository.countByTypeAndTime(
|
||||||
.gte(JsonData.of(LocalDateTime.of(LocalDate.now(), LocalTime.MIN)
|
EventType.LOGIN_PORTAL.getCode(), LocalDateTime.MIN, LocalDateTime.MAX));
|
||||||
.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));
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,71 +90,11 @@ public class AnalysisServiceImpl implements AnalysisService {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<AuthnQuantityResult> authnQuantity(AnalysisQuery params) {
|
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 min = params.getStartTime();
|
||||||
LocalDateTime max = params.getEndTime();
|
LocalDateTime max = params.getEndTime();
|
||||||
AnalysisQuery.Interval timeInterval = params.getTimeInterval();
|
AnalysisQuery.Interval timeInterval = params.getTimeInterval();
|
||||||
// 根据事件月份分组统计认证数量 count
|
return auditRepository.authnQuantity(EventType.LOGIN_PORTAL, min, max,
|
||||||
Aggregation dateGroup = AggregationBuilders
|
timeInterval.getFormat());
|
||||||
.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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -183,32 +105,13 @@ public class AnalysisServiceImpl implements AnalysisService {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<AppVisitRankResult> appVisitRank(AnalysisQuery params) {
|
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<>();
|
List<AppVisitRankResult> applicationVisitList = new ArrayList<>();
|
||||||
if (countAggregation != null) {
|
List<AuditStatisticsResult> auditRankResults = auditRepository
|
||||||
List<StringTermsBucket> array = countAggregation.aggregation().getAggregate().sterms()
|
.appVisitRank(EventType.APP_SSO, params.getStartTime(), params.getEndTime());
|
||||||
.buckets().array();
|
for (AuditStatisticsResult auditRankResult : auditRankResults) {
|
||||||
for (StringTermsBucket bucket : array) {
|
// 单点登录
|
||||||
String key = bucket.key().stringValue();
|
String name = getAppName(auditRankResult.getKey());
|
||||||
// 单点登录
|
applicationVisitList.add(new AppVisitRankResult(name, auditRankResult.getCount()));
|
||||||
String name = getAppName(key);
|
|
||||||
applicationVisitList.add(new AppVisitRankResult(name, bucket.docCount()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return applicationVisitList;
|
return applicationVisitList;
|
||||||
}
|
}
|
||||||
|
@ -236,33 +139,14 @@ public class AnalysisServiceImpl implements AnalysisService {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<AuthnHotProviderResult> authnHotProvider(AnalysisQuery params) {
|
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<>();
|
List<AuthnHotProviderResult> authTypeList = new ArrayList<>();
|
||||||
if (authTypeStringTerms != null) {
|
List<AuditStatisticsResult> auditRankResults = auditRepository
|
||||||
List<StringTermsBucket> array = authTypeStringTerms.aggregation().getAggregate()
|
.authnHotProvider(EventType.LOGIN_PORTAL, params.getStartTime(), params.getEndTime());
|
||||||
.sterms().buckets().array();
|
for (AuditStatisticsResult auditRankResult : auditRankResults) {
|
||||||
for (StringTermsBucket bucket : array) {
|
// 授权类型
|
||||||
String key = bucket.key().stringValue();
|
if (Objects.nonNull(auditRankResult.getKey())) {
|
||||||
// 授权类型
|
String name = getIdentityProviderType(auditRankResult.getKey()).name();
|
||||||
String name = getIdentityProviderType(key).name();
|
authTypeList.add(new AuthnHotProviderResult(name, auditRankResult.getCount()));
|
||||||
authTypeList.add(new AuthnHotProviderResult(name, bucket.docCount()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return authTypeList;
|
return authTypeList;
|
||||||
|
@ -272,38 +156,16 @@ public class AnalysisServiceImpl implements AnalysisService {
|
||||||
* 登录区域统计
|
* 登录区域统计
|
||||||
*
|
*
|
||||||
* @param params {@link AnalysisQuery}
|
* @param params {@link AnalysisQuery}
|
||||||
* @return {@link List<AuthnZoneResult>}
|
* @return {@link List< AuthnZoneResult >}
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<AuthnZoneResult> authnZone(AnalysisQuery params) {
|
public List<AuthnZoneResult> authnZone(AnalysisQuery params) {
|
||||||
IndexCoordinates indexCoordinates = IndexCoordinates
|
List<AuditStatisticsResult> auditStatisticsResults = auditRepository
|
||||||
.of(getAuditIndexPrefix(supportProperties.getAudit().getIndexPrefix()) + "*");
|
.authnZone(EventType.LOGIN_PORTAL, params.getStartTime(), params.getEndTime());
|
||||||
// 不存在索引
|
return auditStatisticsResults.stream()
|
||||||
if (!elasticsearchTemplate.indexOps(indexCoordinates).exists()) {
|
.map(auditStatisticsResult -> new AuthnZoneResult(auditStatisticsResult.getKey(),
|
||||||
return new ArrayList<>();
|
auditStatisticsResult.getCount()))
|
||||||
}
|
.toList();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -372,7 +234,9 @@ public class AnalysisServiceImpl implements AnalysisService {
|
||||||
|
|
||||||
private final SupportProperties supportProperties;
|
private final SupportProperties supportProperties;
|
||||||
|
|
||||||
private final ElasticsearchTemplate elasticsearchTemplate;
|
// private final ElasticsearchTemplate elasticsearchTemplate;
|
||||||
|
|
||||||
|
private final AuditRepository auditRepository;
|
||||||
|
|
||||||
private final AppRepository appRepository;
|
private final AppRepository appRepository;
|
||||||
|
|
||||||
|
|
|
@ -132,10 +132,6 @@ spring:
|
||||||
overwrite-existing-jobs: true
|
overwrite-existing-jobs: true
|
||||||
jdbc:
|
jdbc:
|
||||||
initialize-schema: never
|
initialize-schema: never
|
||||||
#rabbitmq
|
|
||||||
rabbitmq:
|
|
||||||
template:
|
|
||||||
reply-timeout: 60000
|
|
||||||
#日志配置
|
#日志配置
|
||||||
logging:
|
logging:
|
||||||
config: classpath:config/logback-spring.xml
|
config: classpath:config/logback-spring.xml
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
package cn.topiam.employee.openapi.converter.account;
|
package cn.topiam.employee.openapi.converter.account;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -25,10 +26,15 @@ import org.mapstruct.Mapping;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
import cn.topiam.employee.common.entity.account.OrganizationEntity;
|
import cn.topiam.employee.common.entity.account.OrganizationEntity;
|
||||||
|
import cn.topiam.employee.common.entity.account.OrganizationMemberEntity;
|
||||||
import cn.topiam.employee.openapi.pojo.result.account.OrganizationChildResult;
|
import cn.topiam.employee.openapi.pojo.result.account.OrganizationChildResult;
|
||||||
|
import cn.topiam.employee.openapi.pojo.result.account.OrganizationMember;
|
||||||
import cn.topiam.employee.openapi.pojo.result.account.OrganizationResult;
|
import cn.topiam.employee.openapi.pojo.result.account.OrganizationResult;
|
||||||
import cn.topiam.employee.openapi.pojo.save.account.OrganizationCreateParam;
|
import cn.topiam.employee.openapi.pojo.save.account.OrganizationCreateParam;
|
||||||
|
import cn.topiam.employee.openapi.pojo.save.account.OrganizationMemberCreateParam;
|
||||||
import cn.topiam.employee.openapi.pojo.update.account.OrganizationUpdateParam;
|
import cn.topiam.employee.openapi.pojo.update.account.OrganizationUpdateParam;
|
||||||
|
import cn.topiam.employee.support.exception.BadParamsException;
|
||||||
|
import cn.topiam.employee.support.security.util.SecurityUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 组织架构数据映射
|
* 组织架构数据映射
|
||||||
|
@ -119,4 +125,42 @@ public interface OrganizationConverter {
|
||||||
* @return {@link OrganizationResult}
|
* @return {@link OrganizationResult}
|
||||||
*/
|
*/
|
||||||
OrganizationResult entityConvertToOrgDetailResult(OrganizationEntity organization);
|
OrganizationResult entityConvertToOrgDetailResult(OrganizationEntity organization);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组织用户关系入参转entity
|
||||||
|
*
|
||||||
|
* @param createParam {@link OrganizationMemberCreateParam}
|
||||||
|
* @return {@link List}
|
||||||
|
*/
|
||||||
|
default List<OrganizationMemberEntity> orgMemberConvertToEntity(OrganizationMemberCreateParam createParam) {
|
||||||
|
List<OrganizationMemberCreateParam.Organization> list = createParam.getOrganizationList();
|
||||||
|
if (list == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!(list.stream().filter(OrganizationMemberCreateParam.Organization::getPrimary)
|
||||||
|
.count() == 1)) {
|
||||||
|
throw new BadParamsException("主组织有且只能存在一个");
|
||||||
|
}
|
||||||
|
List<OrganizationMemberEntity> entities = new ArrayList<>(list.size());
|
||||||
|
for (OrganizationMemberCreateParam.Organization organization : list) {
|
||||||
|
OrganizationMemberEntity organizationMemberEntity = new OrganizationMemberEntity();
|
||||||
|
organizationMemberEntity.setOrgId(organization.getOrgId());
|
||||||
|
organizationMemberEntity.setUserId(createParam.getUserId());
|
||||||
|
organizationMemberEntity.setPrimary(organization.getPrimary());
|
||||||
|
organizationMemberEntity.setCreateBy(SecurityUtils.getCurrentUserId());
|
||||||
|
organizationMemberEntity.setCreateTime(LocalDateTime.now());
|
||||||
|
organizationMemberEntity.setUpdateBy(SecurityUtils.getCurrentUserId());
|
||||||
|
organizationMemberEntity.setUpdateTime(LocalDateTime.now());
|
||||||
|
entities.add(organizationMemberEntity);
|
||||||
|
}
|
||||||
|
return entities;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* entity转组织用户关系
|
||||||
|
*
|
||||||
|
* @param list {@link List}
|
||||||
|
* @return {@link List}
|
||||||
|
*/
|
||||||
|
List<OrganizationMember> entityConvertToOrgMember(List<OrganizationMemberEntity> list);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* eiam-openapi - 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.openapi.pojo.result.account;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组织用户关系
|
||||||
|
*
|
||||||
|
* @author TopIAM
|
||||||
|
* Created by support@topiam.cn on 2020/8/11 21:27
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "组织用户关系")
|
||||||
|
public class OrganizationMember implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 5599721546299698344L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
@Schema(description = "ID")
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户ID
|
||||||
|
*/
|
||||||
|
@Schema(description = "用户ID")
|
||||||
|
private String userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组织ID
|
||||||
|
*/
|
||||||
|
@Schema(description = "组织ID")
|
||||||
|
private String orgId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否主组织
|
||||||
|
*/
|
||||||
|
@Schema(description = "是否主组织")
|
||||||
|
private Boolean primary;
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* eiam-openapi - 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.openapi.pojo.save.account;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存组织用户关系
|
||||||
|
*
|
||||||
|
* @author TopIAM
|
||||||
|
* Created by support@topiam.cn on 2023/9/23 21:27
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "保存组织用户关系")
|
||||||
|
public class OrganizationMemberCreateParam implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 5599713546299698344L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户ID
|
||||||
|
*/
|
||||||
|
@Schema(description = "用户ID")
|
||||||
|
@NotNull(message = "用户ID不能为空")
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组织信息
|
||||||
|
*/
|
||||||
|
@Schema(description = "组织信息")
|
||||||
|
@Valid
|
||||||
|
@Size(min = 1, message = "至少选择一个组织")
|
||||||
|
private List<Organization> organizationList;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "组织信息")
|
||||||
|
public static class Organization {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组织ID
|
||||||
|
*/
|
||||||
|
@Schema(description = "组织ID")
|
||||||
|
@NotNull(message = "组织ID不能为空")
|
||||||
|
private String orgId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否主组织
|
||||||
|
*/
|
||||||
|
@Schema(description = "是否主组织")
|
||||||
|
private Boolean primary;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue