Commit 8a89a17f by tangzhaoqian Committed by chenzg

validation校验优化、审批搜索优化(审批汇总表加索引查询)

parent 80a28f85
......@@ -7,11 +7,7 @@ import java.util.List;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.ObjectError;
......@@ -25,12 +21,12 @@ import cn.timer.api.aspect.lang.bean.ValidationError;
import cn.timer.api.utils.ResultUtil;
/**
* 校验信息返回
* 校验信息返回-(闲置)
*
* @author TZQ
*/
@Aspect
@Component
//@Aspect
//@Component
public class BindingResultAspect {
// @annotation配置织入点
......@@ -40,7 +36,7 @@ public class BindingResultAspect {
// }
// execution 配置织入点 -匹配 cn.timer.api.controller 包下的所有子包的类的方法
@Pointcut("execution(* cn.timer.api.controller.spmk.*.*(..))")
// @Pointcut("execution(* cn.timer.api.controller.spmk.*.*(..))")
public void clazzPointCut(){
}
......@@ -53,45 +49,41 @@ public class BindingResultAspect {
// }
// @Around("bindingResultPointCut() && args(..,bindingResult)")
@Around("clazzPointCut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
Long startTime = System.currentTimeMillis();
Object retVal;
Object[] objs = joinPoint.getArgs();
List<Object> listObj = CollectionUtil.toList(objs);
BeanPropertyBindingResult optional = (BeanPropertyBindingResult)listObj.stream()
.filter(p -> "BeanPropertyBindingResult".equals(ClassUtil.getClassName(p, true)))
.findFirst()
.orElse(null);
if(optional != null && optional.hasErrors()){
List<ObjectError> ls = optional.getAllErrors();
List<ValidationError> listVe = new ArrayList<ValidationError>();
ValidationError ve;
for (ObjectError one : ls) {
String fieldString = one.getCodes().length >= 1 ? one.getCodes()[0] : "";
if (fieldString != null) {
fieldString = fieldString.substring(fieldString.lastIndexOf(".") + 1);
}
// for (String str : one.getCodes()) {
// System.err.println(str);
// @Around("clazzPointCut()")
// public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
// Long startTime = System.currentTimeMillis();
// Object retVal;
// Object[] objs = joinPoint.getArgs();
// List<Object> listObj = CollectionUtil.toList(objs);
// BeanPropertyBindingResult optional = (BeanPropertyBindingResult)listObj.stream()
// .filter(p -> "BeanPropertyBindingResult".equals(ClassUtil.getClassName(p, true)))
// .findFirst()
// .orElse(null);
// if(optional != null && optional.hasErrors()){
// List<ObjectError> ls = optional.getAllErrors();
// List<ValidationError> listVe = new ArrayList<ValidationError>();
// ValidationError ve;
// for (ObjectError one : ls) {
//
// String fieldString = one.getCodes().length >= 1 ? one.getCodes()[0] : "";
// if (fieldString != null) {
// fieldString = fieldString.substring(fieldString.lastIndexOf(".") + 1);
// }
//
// ve = ValidationError.builder().field(fieldString).msg(one.getDefaultMessage()).build();
// listVe.add(ve);
//
// }
// retVal = ResultUtil.error(listVe);
// }else {
// retVal = joinPoint.proceed(joinPoint.getArgs());
// }
// Console.log("返回内容 {}: " ,JSONObject.toJSONString(retVal));
// Long endtime = System.currentTimeMillis();
// Console.log("执行耗时为{}:" ,endtime-startTime + "ms");
//
// return retVal;
// }
ve = ValidationError.builder().field(fieldString).msg(one.getDefaultMessage()).build();
listVe.add(ve);
}
retVal = ResultUtil.error(listVe);
}else {
retVal = joinPoint.proceed(joinPoint.getArgs());
}
Console.log("返回内容 {}: " ,JSONObject.toJSONString(retVal));
Long endtime = System.currentTimeMillis();
Console.log("执行耗时为{}:" ,endtime-startTime + "ms");
return retVal;
}
protected void handleDataScope(final JoinPoint joinPoint)
{
......
package cn.timer.api.config.exception;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.timer.api.aspect.lang.bean.ValidationError;
import cn.timer.api.utils.Result;
import cn.timer.api.utils.ResultUtil;
......@@ -57,4 +65,31 @@ public class GlobalExceptionHandler {
}
return ResultUtil.error(e.getCode(), e.getMessage());
}
/**
* 自定义验证异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Object validExceptionHandler(MethodArgumentNotValidException e)
{
// log.error("1--------"+e.getMessage());
// log.error("2--------"+JSONUtil.parse(e.getBindingResult().getAllErrors()));
// String message = e.getBindingResult().getFieldError().getDefaultMessage();
// String field = e.getBindingResult().getFieldError().getField();
List<ObjectError> ls = e.getBindingResult().getAllErrors();
List<ValidationError> listVe = new ArrayList<ValidationError>();
ValidationError ve;
for (ObjectError one : ls) {
String fieldString = one.getCodes().length >= 1 ? one.getCodes()[0] : "";
if (fieldString != null) {
fieldString = fieldString.substring(fieldString.lastIndexOf(".") + 1);
}
ve = ValidationError.builder().field(fieldString).msg(one.getDefaultMessage()).build();
listVe.add(ve);
}
return ResultUtil.error(listVe);
}
}
package cn.timer.api.config.exception;
public class ValidationMsg {
public static final String NULL = "参数必须为空";
public static final String NOTNULL = "参数值不能为空";
public static final String NOTEMPTY = "参数值不能为空";
public static final String NOTBLANK = "参数键值不能为空";
public static final String ASSERTTRUE = "参数值必须为true";
public static final String ASSERTFALSE = "参数值必须为false";
public static final String PATTERN = "参数值格式不符";
public static final String SIZE = "参数长度不符";
public static final String MIN = "参数值太小";
public static final String MAX = "参数值太大";
public static final String DECIMALMIN = "参数值太小";
public static final String DECIMALMAX = "参数值太大";
public static final String DIGITS = "参数值大小不在有效范围内";
public static final String PAST = "参数值必须为过去日期";
public static final String FUTURE = "参数值必须为未来日期";
public static final String EMAIL = "参数值格式必须为邮箱地址";
public static final String LENGTH = "参数值长度不在有效范围内";
public static final String RANGE = "参数值不在有效范围内";
}
......@@ -22,9 +22,10 @@ public class MyConfiguration {
// 创建配置类
FastJsonConfig config = new FastJsonConfig();
config.setSerializerFeatures(
SerializerFeature.WriteNullListAsEmpty,
SerializerFeature.WriteMapNullValue,
SerializerFeature.WriteNullStringAsEmpty
SerializerFeature.PrettyFormat
// SerializerFeature.WriteNullListAsEmpty,
// SerializerFeature.WriteMapNullValue,
// SerializerFeature.WriteNullStringAsEmpty
);
//此处是全局处理方式
......
......@@ -75,7 +75,7 @@ public class WebSecurityConfig implements WebMvcConfigurer {
// 2.添加fastJson的配置信息,比如,是否需要格式化返回的json数据
FastJsonConfig fastJsonConfig = new FastJsonConfig();
// 时间格式化
// fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
// fastJsonConfig.setDateFormat("yyyy-MM-dd");
// 空值特别处理
// WriteNullListAsEmpty 将Collection类型字段的字段空值输出为[]
......
......@@ -9,6 +9,8 @@ import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
......@@ -167,7 +169,7 @@ public class SpmkController {
@PostMapping(value = "/save_approval_template")
@ApiOperation(value = "5.新增或编辑-审批模板", httpMethod = "POST", notes = "新增或编辑-审批模板")
@ApiOperationSupport(order = 5)
public Result<Object> saveAt(@Valid @RequestBody SpmkApprovalTemplateDto spmkApprovalTemplateDto,BindingResult bindingResult){
public Result<Object> saveAt(@Validated @RequestBody SpmkApprovalTemplateDto spmkApprovalTemplateDto){
Integer approvalTemplateGId = spmkApprovalTemplateDto.getApprovalTemplateGId();
if (ObjectUtil.isNull(approvalTemplateGId))
return ResultUtil.error("操作失败!-1");
......@@ -415,8 +417,7 @@ public class SpmkController {
@ApiOperation(value = "17.发起审批", httpMethod = "POST", notes = "发起审批")
@ApiOperationSupport(order = 17)
@Transactional(rollbackFor = Exception.class)
// @BindingResultCtrol(title = "发起审批")
public Result<Object> saveCa(@CurrentUser UserBean userBean,@Valid @RequestBody SpmkApproveSummaryDto spmkApproveSummaryDto,BindingResult bindingResult) throws Exception{
public Result<Object> saveCa(@CurrentUser UserBean userBean,@Validated @RequestBody SpmkApproveSummaryDto spmkApproveSummaryDto) throws Exception{
YgglMainEmp ygglMainEmp = YgglMainEmp.builder().build().selectOne(new QueryWrapper<YgglMainEmp>()
.lambda()
.select(YgglMainEmp::getHeadUrl,YgglMainEmp::getName)
......@@ -545,7 +546,7 @@ public class SpmkController {
@Transactional(rollbackFor = Exception.class)
@ApiOperationSupport(order = 20)
// @BindingResultCtrol(title = "审批人审批")
public Result<Object> approving(@Valid @RequestBody ApprovingDto approvingDto, BindingResult bindingResult) throws Exception {
public Result<Object> approving(@Validated @RequestBody ApprovingDto approvingDto) throws Exception {
SpmkApproveDetail ad = SpmkApproveDetail.builder().build().selectOne(new QueryWrapper<SpmkApproveDetail>()
.lambda()
.select(SpmkApproveDetail::getId,
......@@ -615,7 +616,7 @@ public class SpmkController {
@PostMapping(value = "/select_my_approve")
@ApiOperation(value = "21.查询列表-我审批的/抄送我的-分页", httpMethod = "POST", notes = "查询列表-我审批的-关键字、审批状态、发起时间-分页")
@ApiOperationSupport(order = 21)
public Result<Object> selectMyAs(@CurrentUser UserBean userBean, @RequestBody MySummaryQueryDto mySummaryQueryDto) {
public Result<Object> selectMyAs(@CurrentUser UserBean userBean, @Validated @RequestBody MySummaryQueryDto mySummaryQueryDto) throws MethodArgumentNotValidException{
IPage<SpmkApproveSummary> page = new Page<SpmkApproveSummary>(
mySummaryQueryDto.getCurrentPage() == null ? 1 : mySummaryQueryDto.getCurrentPage(),
mySummaryQueryDto.getTotalPage() == null ? 10 : mySummaryQueryDto.getTotalPage());
......@@ -625,7 +626,6 @@ public class SpmkController {
IPage<SpmkApproveSummary> pageAs = spmkApproveSummaryMapper.selectPageByQueryForEmpNum(page, mySummaryQueryDto);
List<SpmkApproveSummary> listAs = pageAs.getRecords();
return ResultUtil.data(pageAs, listAs, "操作成功!");
}
......
......@@ -1548,7 +1548,7 @@ public class YgglController {
updateWrapper.eq("emp_num", empNum);
UpdateWrapper<YgglMainLzb> updateWrapper1 = new UpdateWrapper<YgglMainLzb>();
updateWrapper1.eq("emp_num", empNum);
YgglMainEmp.builder().empNum(lzygQueryDto.getEmpNum()).jobStatus(3).build().update(updateWrapper);
YgglMainEmp.builder().empNum(lzygQueryDto.getEmpNum()).jobStatus(YgEnumInterface.jobStatus.LIZHIZHONG.getType()).build().update(updateWrapper);
YgglMainLzb.builder().jobStatus(YgEnumInterface.jobStatus.LIZHIZHONG.getType()).lzTime(lzygQueryDto.getLzTime())
.lzyy(lzygQueryDto.getLzyy()).lzbz(lzygQueryDto.getLzbz()).build().update(updateWrapper1);
......@@ -1816,7 +1816,7 @@ public class YgglController {
@ApiOperation(value = "查询员工列表", httpMethod = "GET", notes = "接口发布说明")
@ApiOperationSupport(order = 62)
public Result<List<YgQueryDto>> queryEmpMessage(@CurrentUser UserBean userBean) {
List<YgQueryDto> ygQueryDto = ygglMainEmpMapper.queryEmpMessage(userBean);
List<YgQueryDto> ygQueryDto = ygglMainEmpMapper.queryEmpMessage(userBean.getOrgCode());
for (YgQueryDto yg : ygQueryDto) {
if (StringUtil.isEmpty(yg.getHeadUrl())) {
yg.setHeadUrl("");
......
......@@ -2,6 +2,7 @@ package cn.timer.api.dao.yggl;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import org.springframework.web.bind.annotation.PathVariable;
......@@ -46,7 +47,7 @@ public interface YgglMainEmpMapper extends BaseMapper<YgglMainEmp> {
* @param userBean
* @return
*/
List<YgQueryDto> queryEmpMessage(UserBean userBean);
List<YgQueryDto> queryEmpMessage(@Param("orgCode") Integer orgCode);
/**
......
......@@ -2,6 +2,7 @@ package cn.timer.api.dto.spmk;
import javax.validation.constraints.NotNull;
import cn.timer.api.config.exception.ValidationMsg;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
......@@ -14,22 +15,22 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
public class ApprovingDto {
@NotNull(message = "asId为空")
@NotNull(message = ValidationMsg.NOTNULL)
@ApiModelProperty(value = "审批汇总id", example = "2", required = true)
private Integer asId;
@NotNull(message = "executeRecordId为空")
@NotNull(message = ValidationMsg.NOTNULL)
@ApiModelProperty(value = "审批执行记录id", example = "10", required = true)
private Integer executeRecordId;
@NotNull(message = "executorId为空")
@NotNull(message = ValidationMsg.NOTNULL)
@ApiModelProperty(value = "执行人记录id", example = "10", required = true)
private Integer executorId;
@ApiModelProperty(value = "意见", example = "同意、拒绝")
private String opinion;
@NotNull(message = "sts为空")
@NotNull(message = ValidationMsg.NOTNULL)
@ApiModelProperty(value = "状态 2同意 3拒绝 4转派", example = "2", required = true)
private Integer sts;
......
......@@ -2,9 +2,11 @@ package cn.timer.api.dto.spmk;
import javax.validation.constraints.DecimalMax;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import cn.timer.api.config.exception.ValidationMsg;
import cn.timer.api.utils.Page;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
......@@ -34,9 +36,9 @@ public class MySummaryQueryDto extends Page{
@ApiModelProperty(value = "结束时间 ", example = "2020-10-10 10:10:10")
private String endTime;
@NotNull(message = "type为空")
@DecimalMax(value = "2",message = "type 只能为 0我发起的 1抄送我的 2我审批的")
@DecimalMin(value = "0",message = "type 只能为 0我发起的 1抄送我的 2我审批的")
@NotNull(message = ValidationMsg.NOTNULL)
@DecimalMax(value = "2",message = ValidationMsg.DECIMALMAX+" 只能为 0我发起的 1抄送我的 2我审批的")
@DecimalMin(value = "0",message = ValidationMsg.DECIMALMIN+" 只能为 0我发起的 1抄送我的 2我审批的")
@ApiModelProperty(value = "0我发起的 1抄送我的 2我审批的", example = "0")
private Integer type;
......
......@@ -11,6 +11,7 @@ import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import cn.hutool.json.JSONObject;
import cn.timer.api.config.exception.ValidationMsg;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
......@@ -26,15 +27,15 @@ public class SpmkApprovalTemplateDto {
@ApiModelProperty(value = "编号 编号", example = "101")
private Integer id;
@NotNull(message = "approvalTemplateGId为空")
@NotNull(message = ValidationMsg.NOTNULL)
@ApiModelProperty(value = "审批模板组id 当前用户ID", example = "101")
private Integer approvalTemplateGId;
@NotBlank(message = "iconUrl为空")
@NotBlank(message = ValidationMsg.NOTBLANK)
@ApiModelProperty(value = "审批图标地址 ", example = "审批图标地址")
private String iconUrl;
@NotBlank(message = "name为空")
@NotBlank(message = ValidationMsg.NOTBLANK)
@ApiModelProperty(value = "审批名称 ", example = "审批名称")
private String name;
......@@ -47,7 +48,7 @@ public class SpmkApprovalTemplateDto {
@ApiModelProperty(value = "排序 由于区分关键字,命名后缀加s", example = "101")
private Integer ranks;
@NotBlank(message = "isOpinion为空 是否必填 意见 0是 1否")
@NotBlank(message = ValidationMsg.NOTBLANK)
@ApiModelProperty(value = "审批意见 是否必填 意见 0是 1否", example = "101")
private Integer isOpinion;
......@@ -57,17 +58,17 @@ public class SpmkApprovalTemplateDto {
@ApiModelProperty(value = "创建时间 ", example = "创建时间")
private Date createTime;
@NotNull(message = "assoType为空")
@DecimalMax(value = "9",message = "assoType 只能为 0无 1转正 2离职 3调岗 4招聘 5加班 6请假 7出差 8外出 9补卡 ")
@DecimalMin(value = "0",message = "assoType 只能为 0无 1转正 2离职 3调岗 4招聘 5加班 6请假 7出差 8外出 9补卡 ")
@NotNull(message = ValidationMsg.NOTNULL)
@DecimalMax(value = "9",message = ValidationMsg.DECIMALMAX +" 只能为 0无 1转正 2离职 3调岗 4招聘 5加班 6请假 7出差 8外出 9补卡 ")
@DecimalMin(value = "0",message = ValidationMsg.DECIMALMIN +" 只能为 0无 1转正 2离职 3调岗 4招聘 5加班 6请假 7出差 8外出 9补卡 ")
@ApiModelProperty(value = "关联类型 0无 1转正 2离职 3调岗 4加班 5请假 6出差 7外出 8补卡 9调薪", example = "1")
private Integer assoType;
@NotEmpty(message = "froms为空")
@NotEmpty(message = ValidationMsg.NOTEMPTY)
@ApiModelProperty(value = "审批表单 ", example = "审批表单")
private List<JSONObject> froms;
@NotNull(message = "router为空")
@NotNull(message = ValidationMsg.NOTNULL)
@ApiModelProperty(value = "审批流程 ", example = "审批流程")
private Router router;
......
......@@ -8,6 +8,7 @@ import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import cn.hutool.json.JSONObject;
import cn.timer.api.config.exception.ValidationMsg;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
......@@ -23,36 +24,36 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
public class SpmkApproveSummaryDto{
@NotBlank(message = "title为空")
@NotBlank(message = ValidationMsg.NOTBLANK)
@ApiModelProperty(value = "标题 ", example = "标题", required = true)
private String title;
@NotBlank(message = "approveName为空")
@NotBlank(message = ValidationMsg.NOTBLANK)
@ApiModelProperty(value = "审批名称 ", example = "审批名称", required = true)
private String approveName;
@ApiModelProperty(value = "摘要", example = "摘要", required = true)
private String digest;
@NotBlank(message = "initiator为空")
@NotBlank(message = ValidationMsg.NOTBLANK)
@ApiModelProperty(value = "发起人名称 ", example = "发起人名称", required = true)
private String initiator;
@NotNull(message = "assoType为空")
@DecimalMax(value = "9",message = "assoType 只能为 0无 1转正 2离职 3调岗 4招聘 5加班 6请假 7出差 8外出 9补卡 ")
@DecimalMin(value = "0",message = "assoType 只能为 0无 1转正 2离职 3调岗 4招聘 5加班 6请假 7出差 8外出 9补卡 ")
@NotNull(message = ValidationMsg.NOTNULL)
@DecimalMax(value = "9",message = ValidationMsg.DECIMALMAX +" 只能为 0无 1转正 2离职 3调岗 4招聘 5加班 6请假 7出差 8外出 9补卡 ")
@DecimalMin(value = "0",message = ValidationMsg.DECIMALMIN +" 只能为 0无 1转正 2离职 3调岗 4招聘 5加班 6请假 7出差 8外出 9补卡 ")
@ApiModelProperty(value = "关联类型 0无 1转正 2离职 3调岗 4招聘 5加班 6请假 7出差 8外出 9补卡", example = "1")
private Integer assoType;
@NotNull(message = "requestData为空")
@NotNull(message = ValidationMsg.NOTNULL)
@ApiModelProperty(value = "申请数据 ", example = "申请数据", required = true)
private JSONObject requestData;
@NotNull(message = "froms为空")
@NotNull(message = ValidationMsg.NOTNULL)
@ApiModelProperty(value = "审批表单 ", example = "数组", required = true)
private List<JSONObject> froms;
@NotNull(message = "router为空")
@NotNull(message = ValidationMsg.NOTNULL)
@ApiModelProperty(value = "审批流程", example = "审批流程", required = true)
private Router router;
......
......@@ -107,9 +107,9 @@
WHERE a.org_code = #{param.orgCode}
<if test="param.query != null and param.query != ''">
and (
a.title like CONCAT(#{param.query},'%') or
a.initiator like CONCAT(#{param.query},'%') or
a.id like CONCAT(#{param.query},'%')
a.title like CONCAT('%',#{param.query},'%') or
a.initiator like CONCAT('%',#{param.query},'%') or
a.id like CONCAT('%',#{param.query},'%')
)
</if>
<if test="param.sts != null">
......@@ -159,9 +159,9 @@
</if>
<if test="param.query != null and param.query != ''">
and (
a.title like CONCAT(#{param.query},'%') or
a.initiator like CONCAT(#{param.query},'%') or
a.id like CONCAT(#{param.query},'%')
a.title like CONCAT('%',#{param.query},'%') or
a.initiator like CONCAT('%',#{param.query},'%') or
a.id like CONCAT('%',#{param.query},'%')
)
</if>
<if test="param.startTime != null and param.startTime != ''">
......
......@@ -193,9 +193,9 @@
<!-- 查询员工信息 搜索 分页 -->
<select id="queryEmpMessage" resultType="cn.timer.api.dto.yggl.YgQueryDto">
SELECT
a. NAME empName,
a.name empName,
a.emp_num empNum,
b. NAME deptName,
b.name deptName,
a.rz_time rzTime,
a.job_type jobType,
a.phone phone,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment