mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-29 18:51:53 +08:00
Merge branch 'feature/crm' of https://gitee.com/jiangwanwan/ruoyi-vue-pro into feature/crm
# Conflicts: # sql/mysql/crm.sql
This commit is contained in:
commit
9cf4cfca86
@ -42,25 +42,25 @@ CREATE TABLE `crm_contract`
|
|||||||
DROP TABLE IF EXISTS `crm_clue`;
|
DROP TABLE IF EXISTS `crm_clue`;
|
||||||
CREATE TABLE `crm_clue` (
|
CREATE TABLE `crm_clue` (
|
||||||
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号,主键自增',
|
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号,主键自增',
|
||||||
`transform_status` tinyint NOT NULL COMMENT '转化状态',
|
`transform_status` tinyint DEFAULT NULL COMMENT '转化状态',
|
||||||
`follow_up_status` tinyint NOT NULL COMMENT '跟进状态',
|
`follow_up_status` tinyint DEFAULT NULL COMMENT '跟进状态',
|
||||||
`name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '线索名称',
|
`name` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '线索名称',
|
||||||
`customer_id` bigint NOT NULL COMMENT '客户id',
|
`customer_id` bigint NOT NULL COMMENT '客户id',
|
||||||
`contact_next_time` datetime NULL DEFAULT NULL COMMENT '下次联系时间',
|
`contact_next_time` datetime DEFAULT NULL COMMENT '下次联系时间',
|
||||||
`telephone` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '电话',
|
`telephone` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '电话',
|
||||||
`mobile` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '手机号',
|
`mobile` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '手机号',
|
||||||
`address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '地址',
|
`address` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '地址',
|
||||||
`owner_user_id` bigint NULL DEFAULT NULL COMMENT '负责人的用户编号',
|
`owner_user_id` bigint NOT NULL COMMENT '负责人的用户编号',
|
||||||
`contact_last_time` datetime NULL DEFAULT NULL COMMENT '最后跟进时间',
|
`contact_last_time` datetime DEFAULT NULL COMMENT '最后跟进时间',
|
||||||
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',
|
`remark` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
|
||||||
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
|
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者',
|
||||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
|
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新者',
|
||||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||||
PRIMARY KEY (`id`) USING BTREE
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '线索表' ROW_FORMAT = Dynamic;
|
) ENGINE = InnoDB COMMENT = '线索表' ;
|
||||||
|
|
||||||
DROP TABLE IF EXISTS `crm_business`;
|
DROP TABLE IF EXISTS `crm_business`;
|
||||||
CREATE TABLE `crm_business` (
|
CREATE TABLE `crm_business` (
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
package cn.iocoder.yudao.framework.common.validation;
|
||||||
|
|
||||||
|
import javax.validation.Constraint;
|
||||||
|
import javax.validation.Payload;
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
@Target({
|
||||||
|
ElementType.METHOD,
|
||||||
|
ElementType.FIELD,
|
||||||
|
ElementType.ANNOTATION_TYPE,
|
||||||
|
ElementType.CONSTRUCTOR,
|
||||||
|
ElementType.PARAMETER,
|
||||||
|
ElementType.TYPE_USE
|
||||||
|
})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Constraint(
|
||||||
|
validatedBy = TelephoneValidator.class
|
||||||
|
)
|
||||||
|
public @interface Telephone {
|
||||||
|
|
||||||
|
String message() default "电话格式不正确";
|
||||||
|
|
||||||
|
Class<?>[] groups() default {};
|
||||||
|
|
||||||
|
Class<? extends Payload>[] payload() default {};
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package cn.iocoder.yudao.framework.common.validation;
|
||||||
|
|
||||||
|
import cn.hutool.core.text.CharSequenceUtil;
|
||||||
|
import cn.hutool.core.util.PhoneUtil;
|
||||||
|
|
||||||
|
import javax.validation.ConstraintValidator;
|
||||||
|
import javax.validation.ConstraintValidatorContext;
|
||||||
|
|
||||||
|
public class TelephoneValidator implements ConstraintValidator<Telephone, String> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(Telephone annotation) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid(String value, ConstraintValidatorContext context) {
|
||||||
|
// 如果手机号为空,默认不校验,即校验通过
|
||||||
|
if (CharSequenceUtil.isEmpty(value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 校验手机
|
||||||
|
return PhoneUtil.isTel(value) || PhoneUtil.isPhone(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -12,8 +12,8 @@ public interface ErrorCodeConstants {
|
|||||||
// ========== 合同管理 1-020-000-000 ==========
|
// ========== 合同管理 1-020-000-000 ==========
|
||||||
ErrorCode CONTRACT_NOT_EXISTS = new ErrorCode(1_020_000_000, "合同不存在");
|
ErrorCode CONTRACT_NOT_EXISTS = new ErrorCode(1_020_000_000, "合同不存在");
|
||||||
|
|
||||||
// TODO @wanwan:要单独一个分段噢
|
// ========== 线索管理 1-020-001-000 ==========
|
||||||
ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_000_001, "线索不存在");
|
ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在");
|
||||||
|
|
||||||
// ========== 商机管理 1-020-001-000 ==========
|
// ========== 商机管理 1-020-001-000 ==========
|
||||||
ErrorCode BUSINESS_NOT_EXISTS = new ErrorCode(1_020_001_000, "商机不存在");
|
ErrorCode BUSINESS_NOT_EXISTS = new ErrorCode(1_020_001_000, "商机不存在");
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
|
package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.validation.Mobile;
|
import cn.iocoder.yudao.framework.common.validation.Mobile;
|
||||||
|
import cn.iocoder.yudao.framework.common.validation.Telephone;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.springframework.format.annotation.DateTimeFormat;
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
@ -17,44 +19,31 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
|
|||||||
@Data
|
@Data
|
||||||
public class CrmClueBaseVO {
|
public class CrmClueBaseVO {
|
||||||
|
|
||||||
// TODO @wanwan:转化状态,新增和修改的时候,应该不传递的哈;而是在未来的时候,才会更新到
|
|
||||||
@Schema(description = "转化状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
|
||||||
@NotNull(message = "转化状态不能为空")
|
|
||||||
private Boolean transformStatus;
|
|
||||||
|
|
||||||
// TODO @wanwan:同 transformStatus
|
|
||||||
@Schema(description = "跟进状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
|
||||||
@NotNull(message = "跟进状态不能为空")
|
|
||||||
private Boolean followUpStatus;
|
|
||||||
|
|
||||||
@Schema(description = "线索名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "线索xxx")
|
@Schema(description = "线索名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "线索xxx")
|
||||||
@NotNull(message = "线索名称不能为空") // TODO @wanwan:应该是 NotEmpty 噢。空串都无法接受
|
@NotEmpty(message = "线索名称不能为空")
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
// TODO @wanwan:中英文之间,要有个空格;例如说,客户 id 不能为空
|
@Schema(description = "客户 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "520")
|
||||||
@Schema(description = "客户id", requiredMode = Schema.RequiredMode.REQUIRED, example = "520")
|
@NotNull(message = "客户不能为空")
|
||||||
@NotNull(message = "客户id不能为空")
|
|
||||||
private Long customerId;
|
private Long customerId;
|
||||||
|
|
||||||
@Schema(description = "下次联系时间", example = "2023-10-18 01:00:00")
|
@Schema(description = "下次联系时间", example = "2023-10-18 01:00:00")
|
||||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
private LocalDateTime contactNextTime;
|
private LocalDateTime contactNextTime;
|
||||||
|
|
||||||
// TODO @wanwan:@Schema 在 @Mobile 之前,要保持统一的顺序;2)可以加个 @Telephone 的校验格式;应该不是手机的格式哈
|
|
||||||
@Mobile(message = "电话格式不正确")
|
|
||||||
@Schema(description = "电话", example = "18000000000")
|
@Schema(description = "电话", example = "18000000000")
|
||||||
|
@Telephone
|
||||||
private String telephone;
|
private String telephone;
|
||||||
|
|
||||||
// TODO @wanwan:@Schema 在 @Mobile 之前,要保持统一的顺序;2)类似 @Mobile 这个提示如果是默认的,就可以不写 message
|
|
||||||
@Mobile(message = "手机号格式不正确")
|
|
||||||
@Schema(description = "手机号", example = "18000000000")
|
@Schema(description = "手机号", example = "18000000000")
|
||||||
|
@Mobile
|
||||||
private String mobile;
|
private String mobile;
|
||||||
|
|
||||||
@Schema(description = "地址", example = "北京市海淀区")
|
@Schema(description = "地址", example = "北京市海淀区")
|
||||||
private String address;
|
private String address;
|
||||||
|
|
||||||
@Schema(description = "负责人的用户编号", example = "27199")
|
@Schema(description = "负责人的用户编号", example = "27199")
|
||||||
// TODO @wanwan:这个是必填字段哈;
|
@NotNull(message = "负责人不能为空")
|
||||||
private Long ownerUserId;
|
private Long ownerUserId;
|
||||||
|
|
||||||
@Schema(description = "最后跟进时间")
|
@Schema(description = "最后跟进时间")
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
|
package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.module.infra.enums.DictTypeConstants;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -12,7 +13,6 @@ import com.alibaba.excel.annotation.ExcelProperty;
|
|||||||
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
|
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
|
||||||
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
|
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 线索 Excel VO
|
* 线索 Excel VO
|
||||||
*
|
*
|
||||||
@ -21,20 +21,21 @@ import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
|
|||||||
@Data
|
@Data
|
||||||
public class CrmClueExcelVO {
|
public class CrmClueExcelVO {
|
||||||
|
|
||||||
@ExcelProperty("编号,主键自增")
|
@ExcelProperty("编号")
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
@ExcelProperty(value = "转化状态", converter = DictConvert.class)
|
@ExcelProperty(value = "转化状态", converter = DictConvert.class)
|
||||||
@DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 XXXDictTypeConstants 枚举类中
|
@DictFormat(DictTypeConstants.BOOLEAN_STRING)
|
||||||
private Boolean transformStatus;
|
private Boolean transformStatus;
|
||||||
|
|
||||||
@ExcelProperty(value = "跟进状态", converter = DictConvert.class)
|
@ExcelProperty(value = "跟进状态", converter = DictConvert.class)
|
||||||
@DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 XXXDictTypeConstants 枚举类中
|
@DictFormat(DictTypeConstants.BOOLEAN_STRING)
|
||||||
private Boolean followUpStatus;
|
private Boolean followUpStatus;
|
||||||
|
|
||||||
@ExcelProperty("线索名称")
|
@ExcelProperty("线索名称")
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
// TODO 这里需要导出成客户名称
|
||||||
@ExcelProperty("客户id")
|
@ExcelProperty("客户id")
|
||||||
private Long customerId;
|
private Long customerId;
|
||||||
|
|
||||||
|
@ -5,54 +5,18 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
import org.springframework.format.annotation.DateTimeFormat;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
|
||||||
|
|
||||||
@Schema(description = "管理后台 - 线索分页 Request VO")
|
@Schema(description = "管理后台 - 线索分页 Request VO")
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@ToString(callSuper = true)
|
@ToString(callSuper = true)
|
||||||
public class CrmCluePageReqVO extends PageParam {
|
public class CrmCluePageReqVO extends PageParam {
|
||||||
|
|
||||||
// TODO @wanwan:目前只要支持 name、mobile、telephone 的搜索即可;其它字段应该暂时不需要哈;
|
|
||||||
|
|
||||||
@Schema(description = "转化状态", example = "true")
|
|
||||||
private Boolean transformStatus;
|
|
||||||
|
|
||||||
@Schema(description = "跟进状态", example = "true")
|
|
||||||
private Boolean followUpStatus;
|
|
||||||
|
|
||||||
@Schema(description = "线索名称", example = "线索xxx")
|
@Schema(description = "线索名称", example = "线索xxx")
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
@Schema(description = "客户id", example = "520")
|
|
||||||
private Long customerId;
|
|
||||||
|
|
||||||
@Schema(description = "下次联系时间", example = "2023-10-18 01:00:00")
|
|
||||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
|
||||||
private LocalDateTime[] contactNextTime;
|
|
||||||
|
|
||||||
@Schema(description = "电话", example = "18000000000")
|
@Schema(description = "电话", example = "18000000000")
|
||||||
private String telephone;
|
private String telephone;
|
||||||
|
|
||||||
@Schema(description = "手机号", example = "18000000000")
|
@Schema(description = "手机号", example = "18000000000")
|
||||||
private String mobile;
|
private String mobile;
|
||||||
|
|
||||||
@Schema(description = "地址", example = "北京市海淀区")
|
|
||||||
private String address;
|
|
||||||
|
|
||||||
@Schema(description = "负责人的用户编号", example = "27199")
|
|
||||||
private Long ownerUserId;
|
|
||||||
|
|
||||||
@Schema(description = "最后跟进时间")
|
|
||||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
|
||||||
private LocalDateTime[] contactLastTime;
|
|
||||||
|
|
||||||
@Schema(description = "创建时间")
|
|
||||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
|
||||||
private LocalDateTime[] createTime;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
|
|||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
@Schema(description = "管理后台 - 线索 Response VO")
|
@Schema(description = "管理后台 - 线索 Response VO")
|
||||||
@ -16,4 +18,10 @@ public class CrmClueRespVO extends CrmClueBaseVO {
|
|||||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private LocalDateTime createTime;
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
@Schema(description = "转化状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||||
|
private Boolean transformStatus;
|
||||||
|
|
||||||
|
@Schema(description = "跟进状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||||
|
private Boolean followUpStatus;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,17 +19,9 @@ public interface CrmClueMapper extends BaseMapperX<CrmClueDO> {
|
|||||||
|
|
||||||
default PageResult<CrmClueDO> selectPage(CrmCluePageReqVO reqVO) {
|
default PageResult<CrmClueDO> selectPage(CrmCluePageReqVO reqVO) {
|
||||||
return selectPage(reqVO, new LambdaQueryWrapperX<CrmClueDO>()
|
return selectPage(reqVO, new LambdaQueryWrapperX<CrmClueDO>()
|
||||||
.eqIfPresent(CrmClueDO::getTransformStatus, reqVO.getTransformStatus())
|
|
||||||
.eqIfPresent(CrmClueDO::getFollowUpStatus, reqVO.getFollowUpStatus())
|
|
||||||
.likeIfPresent(CrmClueDO::getName, reqVO.getName())
|
.likeIfPresent(CrmClueDO::getName, reqVO.getName())
|
||||||
.eqIfPresent(CrmClueDO::getCustomerId, reqVO.getCustomerId())
|
|
||||||
.betweenIfPresent(CrmClueDO::getContactNextTime, reqVO.getContactNextTime())
|
|
||||||
.likeIfPresent(CrmClueDO::getTelephone, reqVO.getTelephone())
|
.likeIfPresent(CrmClueDO::getTelephone, reqVO.getTelephone())
|
||||||
.likeIfPresent(CrmClueDO::getMobile, reqVO.getMobile())
|
.likeIfPresent(CrmClueDO::getMobile, reqVO.getMobile())
|
||||||
.likeIfPresent(CrmClueDO::getAddress, reqVO.getAddress())
|
|
||||||
.eqIfPresent(CrmClueDO::getOwnerUserId, reqVO.getOwnerUserId())
|
|
||||||
.betweenIfPresent(CrmClueDO::getContactLastTime, reqVO.getContactLastTime())
|
|
||||||
.betweenIfPresent(CrmClueDO::getCreateTime, reqVO.getCreateTime())
|
|
||||||
.orderByDesc(CrmClueDO::getId));
|
.orderByDesc(CrmClueDO::getId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user