⚡ 优化数据分组逻辑.
This commit is contained in:
@@ -9,6 +9,7 @@ import javax.validation.ConstraintViolation;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
import javax.validation.Validator;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -31,6 +32,10 @@ public class Valid extends com.orion.lang.utils.Valid {
|
||||
return notBlank(s, ErrorMessage.PARAM_MISSING);
|
||||
}
|
||||
|
||||
public static <T extends Map<?, ?>> T notEmpty(T map) {
|
||||
return notEmpty(map, ErrorMessage.PARAM_MISSING);
|
||||
}
|
||||
|
||||
public static <T extends Collection<?>> T notEmpty(T object) {
|
||||
return notEmpty(object, ErrorMessage.PARAM_MISSING);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.orion.visor.module.infra.api;
|
||||
|
||||
import com.orion.visor.module.infra.entity.dto.data.DataGroupRelCreateDTO;
|
||||
import com.orion.visor.module.infra.enums.DataGroupTypeEnum;
|
||||
|
||||
import java.util.List;
|
||||
@@ -37,6 +36,7 @@ public interface DataGroupRelApi {
|
||||
/**
|
||||
* 添加关联
|
||||
*
|
||||
* @param type type
|
||||
* @param groupId groupId
|
||||
* @param relId relId
|
||||
*/
|
||||
@@ -45,9 +45,10 @@ public interface DataGroupRelApi {
|
||||
/**
|
||||
* 批量添加关联
|
||||
*
|
||||
* @param list list
|
||||
* @param type type
|
||||
* @param groupRelListMap groupRelListMap
|
||||
*/
|
||||
void addGroupRel(DataGroupTypeEnum type, List<DataGroupRelCreateDTO> list);
|
||||
void addGroupRel(DataGroupTypeEnum type, Map<Long, List<Long>> groupRelListMap);
|
||||
|
||||
/**
|
||||
* 通过 type 查询 relId 缓存
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.orion.visor.module.infra.api;
|
||||
|
||||
import com.orion.visor.module.infra.entity.dto.data.DataGroupRelCreateDTO;
|
||||
import com.orion.visor.module.infra.enums.DataGroupTypeEnum;
|
||||
|
||||
import java.util.List;
|
||||
@@ -40,11 +39,11 @@ public interface DataGroupUserRelApi {
|
||||
/**
|
||||
* 批量添加关联
|
||||
*
|
||||
* @param type type
|
||||
* @param userId userId
|
||||
* @param list list
|
||||
* @param type type
|
||||
* @param userId userId
|
||||
* @param groupRelListMap groupRelListMap
|
||||
*/
|
||||
void addGroupRel(DataGroupTypeEnum type, Long userId, List<DataGroupRelCreateDTO> list);
|
||||
void addGroupRel(DataGroupTypeEnum type, Long userId, Map<Long, List<Long>> groupRelListMap);
|
||||
|
||||
/**
|
||||
* 通过 type 查询 relId 缓存
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
package com.orion.visor.module.infra.entity.dto.data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 数据分组关联 创建请求业务对象
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023-11-7 18:44
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "DataGroupRelCreateDTO", description = "数据分组关联 创建请求业务对象")
|
||||
public class DataGroupRelCreateDTO implements Serializable {
|
||||
|
||||
@NotNull
|
||||
@Schema(description = "组id")
|
||||
private Long groupId;
|
||||
|
||||
@NotNull
|
||||
@Schema(description = "引用id")
|
||||
private Long relId;
|
||||
|
||||
}
|
||||
@@ -3,11 +3,8 @@ package com.orion.visor.module.infra.api.impl;
|
||||
import com.orion.visor.framework.common.constant.Const;
|
||||
import com.orion.visor.framework.common.utils.Valid;
|
||||
import com.orion.visor.module.infra.api.DataGroupRelApi;
|
||||
import com.orion.visor.module.infra.convert.DataGroupRelProviderConvert;
|
||||
import com.orion.visor.module.infra.entity.domain.DataGroupRelDO;
|
||||
import com.orion.visor.module.infra.entity.dto.DataGroupRelCacheDTO;
|
||||
import com.orion.visor.module.infra.entity.dto.data.DataGroupRelCreateDTO;
|
||||
import com.orion.visor.module.infra.entity.request.data.DataGroupRelCreateRequest;
|
||||
import com.orion.visor.module.infra.enums.DataGroupTypeEnum;
|
||||
import com.orion.visor.module.infra.service.DataGroupRelService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -57,10 +54,9 @@ public class DataGroupRelApiImpl implements DataGroupRelApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addGroupRel(DataGroupTypeEnum type, List<DataGroupRelCreateDTO> list) {
|
||||
Valid.valid(list);
|
||||
List<DataGroupRelCreateRequest> rows = DataGroupRelProviderConvert.MAPPER.toList(list);
|
||||
dataGroupRelService.addGroupRel(type.name(), Const.SYSTEM_USER_ID, rows);
|
||||
public void addGroupRel(DataGroupTypeEnum type, Map<Long, List<Long>> groupRelListMap) {
|
||||
Valid.notEmpty(groupRelListMap);
|
||||
dataGroupRelService.addGroupRel(type.name(), Const.SYSTEM_USER_ID, groupRelListMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,11 +2,8 @@ package com.orion.visor.module.infra.api.impl;
|
||||
|
||||
import com.orion.visor.framework.common.utils.Valid;
|
||||
import com.orion.visor.module.infra.api.DataGroupUserRelApi;
|
||||
import com.orion.visor.module.infra.convert.DataGroupRelProviderConvert;
|
||||
import com.orion.visor.module.infra.entity.domain.DataGroupRelDO;
|
||||
import com.orion.visor.module.infra.entity.dto.DataGroupRelCacheDTO;
|
||||
import com.orion.visor.module.infra.entity.dto.data.DataGroupRelCreateDTO;
|
||||
import com.orion.visor.module.infra.entity.request.data.DataGroupRelCreateRequest;
|
||||
import com.orion.visor.module.infra.enums.DataGroupTypeEnum;
|
||||
import com.orion.visor.module.infra.service.DataGroupRelService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -50,10 +47,9 @@ public class DataGroupUserRelApiImpl implements DataGroupUserRelApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addGroupRel(DataGroupTypeEnum type, Long userId, List<DataGroupRelCreateDTO> list) {
|
||||
Valid.valid(list);
|
||||
List<DataGroupRelCreateRequest> rows = DataGroupRelProviderConvert.MAPPER.toList(list);
|
||||
dataGroupRelService.addGroupRel(type.name(), userId, rows);
|
||||
public void addGroupRel(DataGroupTypeEnum type, Long userId, Map<Long, List<Long>> groupRelListMap) {
|
||||
Valid.notEmpty(groupRelListMap);
|
||||
dataGroupRelService.addGroupRel(type.name(), userId, groupRelListMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.orion.visor.module.infra.convert;
|
||||
|
||||
import com.orion.visor.module.infra.entity.domain.DataGroupRelDO;
|
||||
import com.orion.visor.module.infra.entity.dto.DataGroupRelCacheDTO;
|
||||
import com.orion.visor.module.infra.entity.request.data.DataGroupRelCreateRequest;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@@ -18,8 +17,6 @@ public interface DataGroupRelConvert {
|
||||
|
||||
DataGroupRelConvert MAPPER = Mappers.getMapper(DataGroupRelConvert.class);
|
||||
|
||||
DataGroupRelDO to(DataGroupRelCreateRequest request);
|
||||
|
||||
DataGroupRelCacheDTO toCache(DataGroupRelDO domain);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
package com.orion.visor.module.infra.convert;
|
||||
|
||||
import com.orion.visor.module.infra.entity.dto.data.DataGroupRelCreateDTO;
|
||||
import com.orion.visor.module.infra.entity.request.data.DataGroupRelCreateRequest;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据分组关联 对外服务对象转换器
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023-11-7 18:44
|
||||
*/
|
||||
@Mapper
|
||||
public interface DataGroupRelProviderConvert {
|
||||
|
||||
DataGroupRelProviderConvert MAPPER = Mappers.getMapper(DataGroupRelProviderConvert.class);
|
||||
|
||||
DataGroupRelCreateRequest toRequest(DataGroupRelCreateDTO request);
|
||||
|
||||
List<DataGroupRelCreateRequest> toList(List<DataGroupRelCreateDTO> list);
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package com.orion.visor.module.infra.entity.request.data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 数据分组关联 创建请求对象
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023-11-7 18:44
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "DataGroupRelCreateRequest", description = "数据分组关联 创建请求对象")
|
||||
public class DataGroupRelCreateRequest implements Serializable {
|
||||
|
||||
@NotNull
|
||||
@Schema(description = "组id")
|
||||
private Long groupId;
|
||||
|
||||
@NotNull
|
||||
@Schema(description = "引用id")
|
||||
private Long relId;
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package com.orion.visor.module.infra.entity.request.data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据分组关联 更新请求对象
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023-11-7 18:44
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "DataGroupRelUpdateRequest", description = "数据分组关联 更新请求对象")
|
||||
public class DataGroupRelUpdateRequest implements Serializable {
|
||||
|
||||
@NotNull
|
||||
@Schema(description = "组id")
|
||||
private Long groupId;
|
||||
|
||||
@Schema(description = "引用id")
|
||||
private List<Long> relIdList;
|
||||
|
||||
}
|
||||
@@ -2,9 +2,9 @@ package com.orion.visor.module.infra.service;
|
||||
|
||||
import com.orion.visor.module.infra.entity.domain.DataGroupRelDO;
|
||||
import com.orion.visor.module.infra.entity.dto.DataGroupRelCacheDTO;
|
||||
import com.orion.visor.module.infra.entity.request.data.DataGroupRelCreateRequest;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 数据分组关联 服务类
|
||||
@@ -34,19 +34,23 @@ public interface DataGroupRelService {
|
||||
void updateRelGroup(String type, Long userId, List<Long> groupIdList, Long relId);
|
||||
|
||||
/**
|
||||
* 添加关联
|
||||
* 添加关联 只新增
|
||||
*
|
||||
* @param type type
|
||||
* @param userId userId
|
||||
* @param groupId groupId
|
||||
* @param relId relId
|
||||
*/
|
||||
void addGroupRel(String type, Long userId, Long groupId, Long relId);
|
||||
|
||||
/**
|
||||
* 添加关联
|
||||
* 添加关联 只新增
|
||||
*
|
||||
* @param list list
|
||||
* @param type type
|
||||
* @param userId userId
|
||||
* @param groupRelListMap groupRelListMap
|
||||
*/
|
||||
void addGroupRel(String type, Long userId, List<DataGroupRelCreateRequest> list);
|
||||
void addGroupRel(String type, Long userId, Map<Long, List<Long>> groupRelListMap);
|
||||
|
||||
/**
|
||||
* 通过 type 查询 relId 缓存
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.orion.visor.module.infra.service.impl;
|
||||
|
||||
import com.orion.lang.utils.Strings;
|
||||
import com.orion.lang.utils.collect.Lists;
|
||||
import com.orion.lang.utils.collect.Maps;
|
||||
import com.orion.spring.SpringHolder;
|
||||
import com.orion.visor.framework.biz.operator.log.core.utils.OperatorLogs;
|
||||
import com.orion.visor.framework.common.constant.ErrorMessage;
|
||||
@@ -17,17 +18,13 @@ import com.orion.visor.module.infra.define.cache.DataGroupCacheKeyDefine;
|
||||
import com.orion.visor.module.infra.entity.domain.DataGroupDO;
|
||||
import com.orion.visor.module.infra.entity.domain.DataGroupRelDO;
|
||||
import com.orion.visor.module.infra.entity.dto.DataGroupRelCacheDTO;
|
||||
import com.orion.visor.module.infra.entity.request.data.DataGroupRelCreateRequest;
|
||||
import com.orion.visor.module.infra.service.DataGroupRelService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -122,64 +119,32 @@ public class DataGroupRelServiceImpl implements DataGroupRelService {
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void addGroupRel(String type, Long userId, Long groupId, Long relId) {
|
||||
DataGroupRelCreateRequest record = DataGroupRelCreateRequest.builder()
|
||||
.groupId(Valid.notNull(groupId))
|
||||
.relId(Valid.notNull(relId))
|
||||
.build();
|
||||
Map<Long, List<Long>> map = new HashMap<>();
|
||||
map.put(groupId, Lists.singleton(relId));
|
||||
// 插入
|
||||
SpringHolder.getBean(DataGroupRelService.class)
|
||||
.addGroupRel(type, userId, Lists.singleton(record));
|
||||
SpringHolder.getBean(DataGroupRelService.class).addGroupRel(type, userId, map);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void addGroupRel(String type, Long userId, List<DataGroupRelCreateRequest> list) {
|
||||
if (Lists.isEmpty(list)) {
|
||||
public void addGroupRel(String type, Long userId, Map<Long, List<Long>> groupRelListMap) {
|
||||
if (Maps.isEmpty(groupRelListMap)) {
|
||||
return;
|
||||
}
|
||||
// 通过 groupId 分组
|
||||
Map<Long, List<DataGroupRelCreateRequest>> groupMapping = list.stream()
|
||||
.collect(Collectors.groupingBy(DataGroupRelCreateRequest::getGroupId));
|
||||
// 查询分组信息
|
||||
List<DataGroupDO> groups = dataGroupDAO.selectBatchIds(groupMapping.keySet());
|
||||
Valid.eq(groups.size(), groupMapping.size(), ErrorMessage.GROUP_ABSENT);
|
||||
// 查询关联是否存在
|
||||
groupMapping.forEach((k, v) -> {
|
||||
List<Long> relIdList = v.stream()
|
||||
.map(DataGroupRelCreateRequest::getRelId)
|
||||
.collect(Collectors.toList());
|
||||
// 查询关联
|
||||
List<Long> presentRelIdList = dataGroupRelDAO.of()
|
||||
.createWrapper()
|
||||
.eq(DataGroupRelDO::getGroupId, k)
|
||||
.in(DataGroupRelDO::getRelId, relIdList)
|
||||
.then()
|
||||
.stream()
|
||||
.map(DataGroupRelDO::getRelId)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
if (!presentRelIdList.isEmpty()) {
|
||||
// 删除待插入的重复数据
|
||||
v.removeIf(s -> presentRelIdList.contains(s.getRelId()));
|
||||
}
|
||||
});
|
||||
// 构建插入数据
|
||||
List<DataGroupRelDO> records = new ArrayList<>();
|
||||
groupMapping.forEach((k, v) -> {
|
||||
groupRelListMap.forEach((k, v) -> {
|
||||
v.forEach(s -> records.add(DataGroupRelDO.builder()
|
||||
.groupId(k)
|
||||
.type(type)
|
||||
.userId(userId)
|
||||
.relId(s.getRelId())
|
||||
.relId(s)
|
||||
.build()));
|
||||
});
|
||||
// 不为空则插入
|
||||
if (!records.isEmpty()) {
|
||||
// 插入
|
||||
dataGroupRelDAO.insertBatch(records);
|
||||
// 删除缓存
|
||||
this.deleteCache(type, userId, groupMapping.keySet());
|
||||
}
|
||||
// 插入
|
||||
dataGroupRelDAO.insertBatch(records);
|
||||
// 删除缓存
|
||||
this.deleteCache(type, userId, groupRelListMap.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -101,7 +101,11 @@ export function getExecCommandLogTailToken(request: ExecLogTailRequest) {
|
||||
* 下载批量执行日志文件
|
||||
*/
|
||||
export function downloadExecCommandLogFile(id: number) {
|
||||
return axios.get('/asset/exec-command-log/download', { unwrap: true, params: { id } });
|
||||
return axios.get('/asset/exec-command-log/download', {
|
||||
unwrap: true,
|
||||
responseType: 'blob',
|
||||
params: { id },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -94,7 +94,11 @@ export function getExecJobLogTailToken(request: ExecLogTailRequest) {
|
||||
* 下载计划任务日志文件
|
||||
*/
|
||||
export function downloadExecJobLogFile(id: number) {
|
||||
return axios.get('/asset/exec-job-log/download', { unwrap: true, params: { id } });
|
||||
return axios.get('/asset/exec-job-log/download', {
|
||||
unwrap: true,
|
||||
responseType: 'blob',
|
||||
params: { id },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,52 @@ import Mock from 'mockjs';
|
||||
// import './user';
|
||||
import '@/views/dashboard/workplace/mock';
|
||||
|
||||
// Mock XHR
|
||||
interface XHR {
|
||||
responseType: string;
|
||||
timeout: number;
|
||||
withCredentials: boolean;
|
||||
onabort: () => void;
|
||||
onerror: () => void;
|
||||
onload: () => void;
|
||||
onloadend: () => void;
|
||||
onloadstart: () => void;
|
||||
onprogress: () => void;
|
||||
onreadystatechange: () => void;
|
||||
ontimeout: () => void;
|
||||
}
|
||||
|
||||
// Mock XHR Custom
|
||||
interface XHRCustom extends XHR {
|
||||
custom: XHRCustom;
|
||||
xhr: XHR;
|
||||
match: boolean;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
Mock.XHR.prototype.send = (() => {
|
||||
// @ts-ignore
|
||||
const _send = Mock.XHR.prototype.send;
|
||||
const defaultEvent = () => {
|
||||
};
|
||||
return function (this: XHRCustom) {
|
||||
if (!this.match) {
|
||||
this.custom.xhr.responseType = this.responseType || '';
|
||||
this.custom.xhr.timeout = this.timeout || 0;
|
||||
this.custom.xhr.withCredentials = this.withCredentials || false;
|
||||
this.custom.xhr.onabort = this.onabort || defaultEvent;
|
||||
this.custom.xhr.onerror = this.onerror || defaultEvent;
|
||||
this.custom.xhr.onload = this.onload || defaultEvent;
|
||||
this.custom.xhr.onloadend = this.onloadend || defaultEvent;
|
||||
this.custom.xhr.onloadstart = this.onloadstart || defaultEvent;
|
||||
this.custom.xhr.onprogress = this.onprogress || defaultEvent;
|
||||
this.custom.xhr.onreadystatechange = this.onreadystatechange || defaultEvent;
|
||||
this.custom.xhr.ontimeout = this.ontimeout || defaultEvent;
|
||||
}
|
||||
return _send.apply(this, arguments);
|
||||
};
|
||||
})();
|
||||
|
||||
Mock.setup({
|
||||
timeout: '600-1000',
|
||||
});
|
||||
|
||||
@@ -109,7 +109,7 @@ export function openDownloadFile(url: string) {
|
||||
* 下载文件
|
||||
*/
|
||||
export function downloadFile(res: any, fileName: string = '') {
|
||||
const blob = new Blob([res.data]);
|
||||
const blob = new Blob([res.data], { type: 'application/octet-stream' });
|
||||
const tempLink = document.createElement('a');
|
||||
const blobURL = window.URL.createObjectURL(blob);
|
||||
tempLink.style.display = 'none';
|
||||
|
||||
@@ -78,6 +78,9 @@
|
||||
import UserSelector from '@/components/user/user/selector/index.vue';
|
||||
import HostSelector from '@/components/asset/host/selector/index.vue';
|
||||
|
||||
const emits = defineEmits(['clear']);
|
||||
|
||||
const { toOptions } = useDictStore();
|
||||
const { visible, setVisible } = useVisible();
|
||||
const { loading, setLoading } = useLoading();
|
||||
|
||||
@@ -94,10 +97,6 @@
|
||||
|
||||
const formModel = ref<HostConnectLogQueryRequest>({});
|
||||
|
||||
const emits = defineEmits(['clear']);
|
||||
|
||||
const { toOptions } = useDictStore();
|
||||
|
||||
// 打开
|
||||
const open = (record: any) => {
|
||||
renderForm({ ...defaultForm(), ...record });
|
||||
|
||||
Reference in New Issue
Block a user