统计调整

pull/64/head
kay 2023-10-05 00:06:13 +00:00 committed by smallbun
parent f35ab5abbb
commit 08dca580c3
26 changed files with 679 additions and 184 deletions

View File

@ -73,6 +73,7 @@ public class AuditEventListener implements ApplicationListener<AuditEvent> {
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);

View File

@ -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));
/**
*
*/

View File

@ -72,6 +72,11 @@ public enum EventType {
*/
MOVE_ORGANIZATION(AccountEventType.MOVE_ORGANIZATION),
/**
*
*/
CREATE_ORGANIZATION_MEMBER(AccountEventType.CREATE_ORGANIZATION_MEMBER),
/**
*
*/

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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 {

View File

@ -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

View File

@ -55,13 +55,19 @@ public class OrganizationMemberEntity extends LogicDeleteEntity<Long> {
* ID
*/
@Column(name = "org_id")
private String orgId;
private String orgId;
/**
* ID
*/
@Column(name = "user_id")
private Long userId;
private Long userId;
/**
*
*/
@Column(name = "primary_")
private Boolean primary;
public OrganizationMemberEntity() {
}

View File

@ -41,4 +41,9 @@ public class UserPO extends UserEntity {
*
*/
private String orgDisplayPath;
/**
*
*/
private String primaryOrgDisplayPath;
}

View File

@ -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 org_display_path,
group_concat( IF(organization_member.primary_ IS NULL, null, organization_.display_path ) ) AS primary_org_display_path
FROM
user_group_member ugm
INNER JOIN user u ON ugm.user_id = u.id_ AND u.is_deleted = '0'

View File

@ -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);

View File

@ -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>

View File

@ -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,45 @@ export default (props: UserListProps) => {
dataIndex: 'orgDisplayPath',
search: false,
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' }),

View File

@ -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;

View File

@ -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;

View File

@ -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.*;
@ -65,7 +67,7 @@ public interface AnalysisService {
*
*
* @param params {@link AnalysisQuery}
* @return {@link List<AuthnZoneResult>}
* @return {@link List< AuthnZoneResult >}
*/
List<AuthnZoneResult> authnZone(AnalysisQuery params);
}

View File

@ -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();
// 单点登录
String name = getAppName(key);
applicationVisitList.add(new AppVisitRankResult(name, bucket.docCount()));
}
List<AuditStatisticsResult> auditRankResults = auditRepository
.appVisitRank(EventType.APP_SSO, params.getStartTime(), params.getEndTime());
for (AuditStatisticsResult auditRankResult : auditRankResults) {
// 单点登录
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();
// 授权类型
String name = getIdentityProviderType(key).name();
authTypeList.add(new AuthnHotProviderResult(name, bucket.docCount()));
List<AuditStatisticsResult> auditRankResults = auditRepository
.authnHotProvider(EventType.LOGIN_PORTAL, params.getStartTime(), params.getEndTime());
for (AuditStatisticsResult auditRankResult : auditRankResults) {
// 授权类型
if (Objects.nonNull(auditRankResult.getKey())) {
String name = getIdentityProviderType(auditRankResult.getKey()).name();
authTypeList.add(new AuthnHotProviderResult(name, auditRankResult.getCount()));
}
}
return authTypeList;
@ -272,38 +156,16 @@ public class AnalysisServiceImpl implements AnalysisService {
*
*
* @param params {@link AnalysisQuery}
* @return {@link List<AuthnZoneResult>}
* @return {@link List< AuthnZoneResult >}
*/
@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;

View File

@ -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

View File

@ -17,6 +17,7 @@
*/
package cn.topiam.employee.openapi.converter.account;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@ -25,10 +26,15 @@ import org.mapstruct.Mapping;
import org.springframework.util.CollectionUtils;
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.OrganizationMember;
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.OrganizationMemberCreateParam;
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}
*/
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);
}

View File

@ -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;
}

View File

@ -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;
}
}