新增后台发放道具全功能
This commit is contained in:
parent
931836f2c5
commit
c9e791160f
@ -0,0 +1,16 @@
|
|||||||
|
package com.red.circle.other.inner.endpoint.material.props;
|
||||||
|
|
||||||
|
import com.red.circle.other.inner.endpoint.material.props.api.PropCouponClientApi;
|
||||||
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 道具券 Client
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
* @date 2025-11-07
|
||||||
|
*/
|
||||||
|
@FeignClient(name = "propCouponClient", url = "${feign.other.url}" +
|
||||||
|
PropCouponClientApi.API_PREFIX)
|
||||||
|
public interface PropCouponClient extends PropCouponClientApi {
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package com.red.circle.other.inner.endpoint.material.props.api;
|
||||||
|
|
||||||
|
import com.red.circle.framework.dto.ResultResponse;
|
||||||
|
import com.red.circle.other.inner.model.cmd.material.PropCouponGrantCmd;
|
||||||
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 道具券 Client API
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
* @date 2025-11-071
|
||||||
|
*/
|
||||||
|
public interface PropCouponClientApi {
|
||||||
|
|
||||||
|
String API_PREFIX = "/props/coupon/client";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 后台赠送道具券
|
||||||
|
*/
|
||||||
|
@PostMapping("/grant")
|
||||||
|
ResultResponse<Void> grantCoupons(@RequestBody PropCouponGrantCmd cmd);
|
||||||
|
}
|
||||||
@ -1,7 +1,6 @@
|
|||||||
package com.red.circle.other.app.dto.cmd;
|
package com.red.circle.other.inner.model.cmd.material;
|
||||||
|
|
||||||
import com.red.circle.common.business.dto.cmd.AppExtCommand;
|
import com.red.circle.framework.dto.Command;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
@ -16,7 +15,7 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@Data
|
@Data
|
||||||
public class PropCouponGrantCmd extends AppExtCommand {
|
public class PropCouponGrantCmd extends Command {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 券类型(1头像框 2座驾 3VIP 4聊天气泡 5主题 6资料卡 7飘屏 8徽章 9靓号)
|
* 券类型(1头像框 2座驾 3VIP 4聊天气泡 5主题 6资料卡 7飘屏 8徽章 9靓号)
|
||||||
@ -27,7 +26,7 @@ public class PropCouponGrantCmd extends AppExtCommand {
|
|||||||
* 道具ID
|
* 道具ID
|
||||||
*/
|
*/
|
||||||
@NotNull(message = "道具ID不能为空")
|
@NotNull(message = "道具ID不能为空")
|
||||||
private Long propId;
|
private String propId;
|
||||||
|
|
||||||
private String propIcon;
|
private String propIcon;
|
||||||
|
|
||||||
@ -46,7 +45,7 @@ public class PropCouponGrantCmd extends AppExtCommand {
|
|||||||
* 接收用户ID列表
|
* 接收用户ID列表
|
||||||
*/
|
*/
|
||||||
@NotNull(message = "接收用户ID列表不能为空")
|
@NotNull(message = "接收用户ID列表不能为空")
|
||||||
private List<Long> userIds;
|
private List<String> userIds;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 备注
|
* 备注
|
||||||
@ -4,6 +4,7 @@ import com.red.circle.console.app.dto.cmd.app.props.PropsGiveUserCmd;
|
|||||||
import com.red.circle.console.app.service.app.props.PropsGiveService;
|
import com.red.circle.console.app.service.app.props.PropsGiveService;
|
||||||
import com.red.circle.console.infra.annotations.OpsOperationReqLog;
|
import com.red.circle.console.infra.annotations.OpsOperationReqLog;
|
||||||
import com.red.circle.framework.web.controller.BaseController;
|
import com.red.circle.framework.web.controller.BaseController;
|
||||||
|
import com.red.circle.other.inner.model.cmd.material.PropCouponGrantCmd;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@ -31,4 +32,10 @@ public class PropsGiveRestController extends BaseController {
|
|||||||
return propsGiveService.give(cmd);
|
return propsGiveService.give(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OpsOperationReqLog(value = "道具赠送-后台赠送道具券")
|
||||||
|
@PostMapping("/grant-coupon")
|
||||||
|
public void grantCoupon(@RequestBody @Validated PropCouponGrantCmd cmd) {
|
||||||
|
propsGiveService.givePropCoupon(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,12 +10,7 @@ import com.red.circle.console.app.service.admin.UserAccountService;
|
|||||||
import com.red.circle.console.inner.error.ConsoleErrorCode;
|
import com.red.circle.console.inner.error.ConsoleErrorCode;
|
||||||
import com.red.circle.framework.core.asserts.ResponseAssert;
|
import com.red.circle.framework.core.asserts.ResponseAssert;
|
||||||
import com.red.circle.other.inner.endpoint.live.RoomManagerClient;
|
import com.red.circle.other.inner.endpoint.live.RoomManagerClient;
|
||||||
import com.red.circle.other.inner.endpoint.material.props.BadgeBackpackClient;
|
import com.red.circle.other.inner.endpoint.material.props.*;
|
||||||
import com.red.circle.other.inner.endpoint.material.props.PropsBackpackClient;
|
|
||||||
import com.red.circle.other.inner.endpoint.material.props.PropsCommodityStoreClient;
|
|
||||||
import com.red.circle.other.inner.endpoint.material.props.PropsNobleVipClient;
|
|
||||||
import com.red.circle.other.inner.endpoint.material.props.PropsSourceClient;
|
|
||||||
import com.red.circle.other.inner.endpoint.material.props.RoomThemeBackpackClient;
|
|
||||||
import com.red.circle.other.inner.endpoint.user.user.SysUserProfileClient;
|
import com.red.circle.other.inner.endpoint.user.user.SysUserProfileClient;
|
||||||
import com.red.circle.other.inner.enums.material.BadgeBackpackExpireTypeEnum;
|
import com.red.circle.other.inner.enums.material.BadgeBackpackExpireTypeEnum;
|
||||||
import com.red.circle.other.inner.enums.material.ConsolePropsTypeEnum;
|
import com.red.circle.other.inner.enums.material.ConsolePropsTypeEnum;
|
||||||
@ -24,6 +19,7 @@ import com.red.circle.other.inner.enums.sys.SysBadgeConfigTypeEnum;
|
|||||||
import com.red.circle.other.inner.enums.sys.SysCurrencySendReasonEnum;
|
import com.red.circle.other.inner.enums.sys.SysCurrencySendReasonEnum;
|
||||||
import com.red.circle.other.inner.model.cmd.material.GiveBadgeCmd;
|
import com.red.circle.other.inner.model.cmd.material.GiveBadgeCmd;
|
||||||
import com.red.circle.other.inner.model.cmd.material.GivePropsBackpackCmd;
|
import com.red.circle.other.inner.model.cmd.material.GivePropsBackpackCmd;
|
||||||
|
import com.red.circle.other.inner.model.cmd.material.PropCouponGrantCmd;
|
||||||
import com.red.circle.other.inner.model.dto.material.PropsCommodityStoreDTO;
|
import com.red.circle.other.inner.model.dto.material.PropsCommodityStoreDTO;
|
||||||
import com.red.circle.other.inner.model.dto.material.PropsResourcesDTO;
|
import com.red.circle.other.inner.model.dto.material.PropsResourcesDTO;
|
||||||
import com.red.circle.other.inner.model.dto.material.props.PropsNobleVipAbilityDTO;
|
import com.red.circle.other.inner.model.dto.material.props.PropsNobleVipAbilityDTO;
|
||||||
@ -74,6 +70,7 @@ public class PropsGiveServiceImpl implements PropsGiveService {
|
|||||||
private final RoomThemeBackpackClient roomThemeBackpackClient;
|
private final RoomThemeBackpackClient roomThemeBackpackClient;
|
||||||
private final PropsCommodityStoreClient propsCommodityStoreClient;
|
private final PropsCommodityStoreClient propsCommodityStoreClient;
|
||||||
private final UserAccountService userAccountService;
|
private final UserAccountService userAccountService;
|
||||||
|
private final PropCouponClient propCouponClient;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String give(PropsGiveUserCmd cmd) {
|
public String give(PropsGiveUserCmd cmd) {
|
||||||
@ -120,6 +117,11 @@ public class PropsGiveServiceImpl implements PropsGiveService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void givePropCoupon(PropCouponGrantCmd cmd) {
|
||||||
|
propCouponClient.grantCoupons(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
private void send(PropsGiveUserCmd cmd) {
|
private void send(PropsGiveUserCmd cmd) {
|
||||||
String sysOrigin = getUserSysOrigin(cmd.getReceiverId());
|
String sysOrigin = getUserSysOrigin(cmd.getReceiverId());
|
||||||
ResponseAssert.isFalse(UserErrorCode.USER_INFO_NOT_FOUND, StringUtils.isBlank(sysOrigin));
|
ResponseAssert.isFalse(UserErrorCode.USER_INFO_NOT_FOUND, StringUtils.isBlank(sysOrigin));
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package com.red.circle.console.app.service.app.props;
|
|||||||
|
|
||||||
|
|
||||||
import com.red.circle.console.app.dto.cmd.app.props.PropsGiveUserCmd;
|
import com.red.circle.console.app.dto.cmd.app.props.PropsGiveUserCmd;
|
||||||
|
import com.red.circle.other.inner.model.cmd.material.PropCouponGrantCmd;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@ -18,4 +19,5 @@ public interface PropsGiveService {
|
|||||||
*/
|
*/
|
||||||
String give(PropsGiveUserCmd cmd);
|
String give(PropsGiveUserCmd cmd);
|
||||||
|
|
||||||
|
void givePropCoupon(PropCouponGrantCmd cmd);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ package com.red.circle.other.adapter.app.props;
|
|||||||
import com.red.circle.framework.dto.PageResult;
|
import com.red.circle.framework.dto.PageResult;
|
||||||
import com.red.circle.other.app.dto.clientobject.PropCouponCO;
|
import com.red.circle.other.app.dto.clientobject.PropCouponCO;
|
||||||
import com.red.circle.other.app.dto.clientobject.PropCouponRecordCO;
|
import com.red.circle.other.app.dto.clientobject.PropCouponRecordCO;
|
||||||
import com.red.circle.other.app.dto.cmd.PropCouponGrantCmd;
|
import com.red.circle.other.inner.model.cmd.material.PropCouponGrantCmd;
|
||||||
import com.red.circle.other.app.dto.cmd.PropCouponQueryCmd;
|
import com.red.circle.other.app.dto.cmd.PropCouponQueryCmd;
|
||||||
import com.red.circle.other.app.dto.cmd.PropCouponSendCmd;
|
import com.red.circle.other.app.dto.cmd.PropCouponSendCmd;
|
||||||
import com.red.circle.other.app.dto.cmd.PropCouponUseCmd;
|
import com.red.circle.other.app.dto.cmd.PropCouponUseCmd;
|
||||||
|
|||||||
@ -4,19 +4,15 @@ import com.red.circle.external.inner.endpoint.message.OfficialNoticeClient;
|
|||||||
import com.red.circle.external.inner.model.cmd.message.notice.official.NoticeExtTemplateCustomizeCmd;
|
import com.red.circle.external.inner.model.cmd.message.notice.official.NoticeExtTemplateCustomizeCmd;
|
||||||
import com.red.circle.external.inner.model.enums.message.OfficialNoticeTypeEnum;
|
import com.red.circle.external.inner.model.enums.message.OfficialNoticeTypeEnum;
|
||||||
import com.red.circle.framework.core.asserts.ResponseAssert;
|
import com.red.circle.framework.core.asserts.ResponseAssert;
|
||||||
import com.red.circle.other.app.dto.cmd.PropCouponGrantCmd;
|
import com.red.circle.other.inner.model.cmd.material.PropCouponGrantCmd;
|
||||||
import com.red.circle.other.domain.gateway.PropCouponGateway;
|
import com.red.circle.other.domain.gateway.PropCouponGateway;
|
||||||
import com.red.circle.other.domain.propcoupon.PropCoupon;
|
import com.red.circle.other.domain.propcoupon.PropCoupon;
|
||||||
import com.red.circle.other.domain.propcoupon.PropCouponSource;
|
import com.red.circle.other.domain.propcoupon.PropCouponSource;
|
||||||
import com.red.circle.other.domain.propcoupon.PropCouponStatus;
|
import com.red.circle.other.domain.propcoupon.PropCouponStatus;
|
||||||
import com.red.circle.other.domain.propcoupon.PropCouponType;
|
import com.red.circle.other.domain.propcoupon.PropCouponType;
|
||||||
import com.red.circle.other.infra.database.rds.entity.props.PropsCommodityStore;
|
|
||||||
import com.red.circle.other.infra.database.rds.service.props.PropsCommodityStoreService;
|
|
||||||
import com.red.circle.other.inner.asserts.OtherErrorCode;
|
import com.red.circle.other.inner.asserts.OtherErrorCode;
|
||||||
import com.red.circle.other.inner.endpoint.material.props.PropsCommodityStoreClient;
|
|
||||||
import com.red.circle.other.inner.endpoint.material.props.PropsSourceClient;
|
import com.red.circle.other.inner.endpoint.material.props.PropsSourceClient;
|
||||||
import com.red.circle.other.inner.endpoint.user.user.UserProfileClient;
|
import com.red.circle.other.inner.endpoint.user.user.UserProfileClient;
|
||||||
import com.red.circle.other.inner.model.dto.material.PropsCommodityStoreDTO;
|
|
||||||
import com.red.circle.other.inner.model.dto.material.PropsResourcesDTO;
|
import com.red.circle.other.inner.model.dto.material.PropsResourcesDTO;
|
||||||
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
|
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
|
||||||
import com.red.circle.tool.core.sequence.IdWorkerUtils;
|
import com.red.circle.tool.core.sequence.IdWorkerUtils;
|
||||||
@ -26,10 +22,7 @@ import org.springframework.stereotype.Component;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,7 +40,6 @@ public class PropCouponGrantCmdExe {
|
|||||||
private final UserProfileClient userProfileClient;
|
private final UserProfileClient userProfileClient;
|
||||||
private final OfficialNoticeClient officialNoticeClient;
|
private final OfficialNoticeClient officialNoticeClient;
|
||||||
private final PropsSourceClient propsSourceClient;
|
private final PropsSourceClient propsSourceClient;
|
||||||
private final PropsCommodityStoreService propsCommodityStoreService;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行后台赠送道具券
|
* 执行后台赠送道具券
|
||||||
@ -58,11 +50,12 @@ public class PropCouponGrantCmdExe {
|
|||||||
validateParams(cmd);
|
validateParams(cmd);
|
||||||
|
|
||||||
// 2. 校验用户是否存在
|
// 2. 校验用户是否存在
|
||||||
Set<Long> userIds = Set.copyOf(cmd.getUserIds());
|
|
||||||
|
Set<Long> userIds = cmd.getUserIds().stream().map(Long::parseLong).collect(Collectors.toSet());
|
||||||
Map<Long, UserProfileDTO> userMap = validateUsers(userIds);
|
Map<Long, UserProfileDTO> userMap = validateUsers(userIds);
|
||||||
|
|
||||||
PropsResourcesDTO propsResourcesDTO = ResponseAssert.requiredSuccess(
|
PropsResourcesDTO propsResourcesDTO = ResponseAssert.requiredSuccess(
|
||||||
propsSourceClient.getById(cmd.getPropId()));
|
propsSourceClient.getById(Long.parseLong(cmd.getPropId())));
|
||||||
ResponseAssert.notNull(OtherErrorCode.PROP_COUPON_PROP_NOT_FOUND, propsResourcesDTO);
|
ResponseAssert.notNull(OtherErrorCode.PROP_COUPON_PROP_NOT_FOUND, propsResourcesDTO);
|
||||||
|
|
||||||
PropCouponType couponType = PropCouponType.getByName(propsResourcesDTO.getType());
|
PropCouponType couponType = PropCouponType.getByName(propsResourcesDTO.getType());
|
||||||
@ -127,7 +120,7 @@ public class PropCouponGrantCmdExe {
|
|||||||
propCoupon.setCouponNo(generateCouponNo());
|
propCoupon.setCouponNo(generateCouponNo());
|
||||||
propCoupon.setUserId(userId);
|
propCoupon.setUserId(userId);
|
||||||
propCoupon.setCouponType(couponType);
|
propCoupon.setCouponType(couponType);
|
||||||
propCoupon.setPropId(cmd.getPropId());
|
propCoupon.setPropId(Long.parseLong(cmd.getPropId()));
|
||||||
propCoupon.setPropIcon(cmd.getPropIcon());
|
propCoupon.setPropIcon(cmd.getPropIcon());
|
||||||
propCoupon.setPropName(displayPropName);
|
propCoupon.setPropName(displayPropName);
|
||||||
propCoupon.setValidDays(cmd.getValidDays());
|
propCoupon.setValidDays(cmd.getValidDays());
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import com.red.circle.other.app.command.propcoupon.query.PropCouponListQryExe;
|
|||||||
import com.red.circle.other.app.command.propcoupon.query.PropCouponRecordQryExe;
|
import com.red.circle.other.app.command.propcoupon.query.PropCouponRecordQryExe;
|
||||||
import com.red.circle.other.app.dto.clientobject.PropCouponCO;
|
import com.red.circle.other.app.dto.clientobject.PropCouponCO;
|
||||||
import com.red.circle.other.app.dto.clientobject.PropCouponRecordCO;
|
import com.red.circle.other.app.dto.clientobject.PropCouponRecordCO;
|
||||||
import com.red.circle.other.app.dto.cmd.PropCouponGrantCmd;
|
import com.red.circle.other.inner.model.cmd.material.PropCouponGrantCmd;
|
||||||
import com.red.circle.other.app.dto.cmd.PropCouponQueryCmd;
|
import com.red.circle.other.app.dto.cmd.PropCouponQueryCmd;
|
||||||
import com.red.circle.other.app.dto.cmd.PropCouponSendCmd;
|
import com.red.circle.other.app.dto.cmd.PropCouponSendCmd;
|
||||||
import com.red.circle.other.app.dto.cmd.PropCouponUseCmd;
|
import com.red.circle.other.app.dto.cmd.PropCouponUseCmd;
|
||||||
|
|||||||
@ -3,7 +3,7 @@ package com.red.circle.other.app.service;
|
|||||||
import com.red.circle.framework.dto.PageResult;
|
import com.red.circle.framework.dto.PageResult;
|
||||||
import com.red.circle.other.app.dto.clientobject.PropCouponCO;
|
import com.red.circle.other.app.dto.clientobject.PropCouponCO;
|
||||||
import com.red.circle.other.app.dto.clientobject.PropCouponRecordCO;
|
import com.red.circle.other.app.dto.clientobject.PropCouponRecordCO;
|
||||||
import com.red.circle.other.app.dto.cmd.PropCouponGrantCmd;
|
import com.red.circle.other.inner.model.cmd.material.PropCouponGrantCmd;
|
||||||
import com.red.circle.other.app.dto.cmd.PropCouponQueryCmd;
|
import com.red.circle.other.app.dto.cmd.PropCouponQueryCmd;
|
||||||
import com.red.circle.other.app.dto.cmd.PropCouponSendCmd;
|
import com.red.circle.other.app.dto.cmd.PropCouponSendCmd;
|
||||||
import com.red.circle.other.app.dto.cmd.PropCouponUseCmd;
|
import com.red.circle.other.app.dto.cmd.PropCouponUseCmd;
|
||||||
|
|||||||
@ -0,0 +1,35 @@
|
|||||||
|
package com.red.circle.other.app.inner.endpoint.material.props;
|
||||||
|
|
||||||
|
import com.red.circle.framework.dto.ResultResponse;
|
||||||
|
import com.red.circle.other.inner.model.cmd.material.PropCouponGrantCmd;
|
||||||
|
import com.red.circle.other.app.service.PropCouponService;
|
||||||
|
import com.red.circle.other.inner.endpoint.material.props.api.PropCouponClientApi;
|
||||||
|
import com.red.circle.other.inner.endpoint.material.props.api.PropsRedPacketSkinClientApi;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 道具券内部端点
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
* @date 2025-11-07
|
||||||
|
*/
|
||||||
|
@Validated
|
||||||
|
@RestController
|
||||||
|
@RequestMapping(value = PropCouponClientApi.API_PREFIX, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class PropCouponClientEndpoint implements PropCouponClientApi {
|
||||||
|
|
||||||
|
private final PropCouponService propCouponService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResultResponse<Void> grantCoupons(PropCouponGrantCmd cmd) {
|
||||||
|
propCouponService.grantCoupons(cmd);
|
||||||
|
return ResultResponse.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user