新增用户和房间违规审批功能

This commit is contained in:
tianfeng 2025-11-20 15:20:49 +08:00
parent 2ba8198c2e
commit 278acc870c
26 changed files with 1832 additions and 0 deletions

View File

@ -212,6 +212,16 @@ public enum OfficialNoticeTypeEnum {
*/
RECEIVE_PROP_COUPON,
/**
* 违规警告
*/
VIOLATION_WARNING,
/**
* 违规调整
*/
VIOLATION_ADJUST,
;

View File

@ -0,0 +1,39 @@
package com.red.circle.other.adapter.app.violation;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.framework.web.controller.BaseController;
import com.red.circle.other.app.dto.cmd.violation.RoomViolationHandleCmd;
import com.red.circle.other.app.dto.cmd.violation.ViolationRecordPageQueryCmd;
import com.red.circle.other.app.dto.clientobject.violation.ViolationHandleResultCO;
import com.red.circle.other.app.dto.clientobject.violation.ViolationRecordCO;
import com.red.circle.other.app.service.violation.RoomViolationService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
/**
* 房间违规处理控制器
*/
@RestController
@RequestMapping("/room-violation")
@RequiredArgsConstructor
public class RoomViolationRestController extends BaseController {
private final RoomViolationService roomViolationService;
/**
* 处理房间违规
*/
@PostMapping("/handle")
public ViolationHandleResultCO handle(@Valid @RequestBody RoomViolationHandleCmd cmd) {
return roomViolationService.handle(cmd);
}
/**
* 分页查询违规记录
*/
@PostMapping("/page-query")
public PageResult<ViolationRecordCO> pageQuery(@Valid @RequestBody ViolationRecordPageQueryCmd cmd) {
return roomViolationService.pageQuery(cmd);
}
}

View File

@ -0,0 +1,40 @@
package com.red.circle.other.adapter.app.violation;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.framework.web.controller.BaseController;
import com.red.circle.other.app.dto.cmd.violation.UserViolationHandleCmd;
import com.red.circle.other.app.dto.cmd.violation.ViolationRecordPageQueryCmd;
import com.red.circle.other.app.dto.clientobject.violation.ViolationHandleResultCO;
import com.red.circle.other.app.dto.clientobject.violation.ViolationRecordCO;
import com.red.circle.other.app.service.violation.UserViolationService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
/**
* 用户违规处理控制器
*/
@RestController
@RequestMapping("/user-violation")
@RequiredArgsConstructor
public class UserViolationRestController extends BaseController {
private final UserViolationService userViolationService;
/**
* 处理用户违规
*/
@PostMapping("/handle")
public ViolationHandleResultCO handle(@Valid @RequestBody UserViolationHandleCmd cmd) {
return userViolationService.handle(cmd);
}
/**
* 分页查询违规记录
*/
@PostMapping("/page-query")
public PageResult<ViolationRecordCO> pageQuery(@Valid @RequestBody ViolationRecordPageQueryCmd cmd) {
return userViolationService.pageQuery(cmd);
}
}

View File

@ -0,0 +1,55 @@
package com.red.circle.other.app.command.violation.query;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.other.app.convertor.violation.ViolationRecordConvertor;
import com.red.circle.other.app.dto.cmd.violation.ViolationRecordPageQueryCmd;
import com.red.circle.other.app.dto.clientobject.violation.ViolationRecordCO;
import com.red.circle.other.domain.gateway.violation.ViolationRecordGateway;
import com.red.circle.other.domain.violation.ViolationRecord;
import com.red.circle.other.infra.database.rds.service.sys.AdministratorAuthService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 违规记录分页查询执行器
*/
@Component
@RequiredArgsConstructor
public class ViolationRecordPageQryExe {
private final ViolationRecordGateway violationRecordGateway;
private final AdministratorAuthService administratorAuthService;
private final ViolationRecordConvertor violationRecordConvertor;
public PageResult<ViolationRecordCO> execute(ViolationRecordPageQueryCmd cmd) {
// 验证管理员权限
ResponseAssert.isTrue(CommonErrorCode.INSUFFICIENT_PERMISSION,
administratorAuthService.existsAuth(cmd.requiredReqUserId(), "VIOLATION_QUERY"));
// 分页查询
PageResult<ViolationRecord> pageResult = violationRecordGateway.pageQuery(
cmd.getTargetType(),
cmd.getTargetId(),
cmd.getViolationType(),
cmd.getOperationType(),
cmd.getOperatorId(),
cmd.getCurrent().intValue(),
cmd.getSize().intValue()
);
// 转换为 CO
List<ViolationRecordCO> records = violationRecordConvertor.toCOList(pageResult.getRecords().stream().toList());
PageResult<ViolationRecordCO> coPageResult = new PageResult<>();
coPageResult.setRecords(records);
coPageResult.setTotal(pageResult.getTotal());
coPageResult.setCurrent(pageResult.getCurrent());
coPageResult.setSize(pageResult.getSize());
return coPageResult;
}
}

View File

@ -0,0 +1,177 @@
package com.red.circle.other.app.command.violation.room;
import com.alibaba.fastjson.JSON;
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.enums.message.OfficialNoticeTypeEnum;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.other.app.dto.cmd.violation.RoomViolationHandleCmd;
import com.red.circle.other.app.dto.clientobject.violation.ViolationHandleResultCO;
import com.red.circle.other.app.enums.violation.OperationTypeEnum;
import com.red.circle.other.app.enums.violation.TargetTypeEnum;
import com.red.circle.other.app.enums.violation.ViolationTypeEnum;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.gateway.violation.ViolationRecordGateway;
import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.domain.violation.ViolationRecord;
import com.red.circle.other.infra.database.cache.service.other.EnumConfigCacheService;
import com.red.circle.other.infra.database.mongo.entity.live.RoomProfile;
import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService;
import com.red.circle.other.infra.database.rds.service.sys.AdministratorAuthService;
import com.red.circle.other.inner.asserts.RoomErrorCode;
import com.red.circle.other.inner.enums.config.EnumConfigKey;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* 房间违规调整命令执行器
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class RoomViolationAdjustCmdExe {
private final ViolationRecordGateway violationRecordGateway;
private final RoomProfileManagerService roomProfileManagerService;
private final AdministratorAuthService administratorAuthService;
private final OfficialNoticeClient officialNoticeClient;
private final UserProfileGateway userProfileGateway;
private final EnumConfigCacheService enumConfigCacheService;
// 默认内容配置
private static final String DEFAULT_ROOM_NOTICE = "Welcome to my room";
private static final String DEFAULT_THEME = "default";
public ViolationHandleResultCO execute(RoomViolationHandleCmd cmd) {
ViolationTypeEnum violationTypeEnum = ViolationTypeEnum.getByCode(cmd.getViolationType());
ResponseAssert.notNull(CommonErrorCode.DATA_ERROR, violationTypeEnum);
// 1. 验证管理员权限
ResponseAssert.isTrue(CommonErrorCode.INSUFFICIENT_PERMISSION,
administratorAuthService.existsAuth(cmd.requiredReqUserId(), violationTypeEnum.name()));
// 2. 查询房间信息
RoomProfile roomProfile = roomProfileManagerService.getProfileById(Long.valueOf(cmd.getRoomId()));
ResponseAssert.notNull(RoomErrorCode.ROOM_NOT_EXISTS, roomProfile);
// 3. 获取原始内容
String originalContent = getOriginalContent(roomProfile, violationTypeEnum);
// 4. 调整违规内容
String adjustedContent = adjustViolationContent(roomProfile, violationTypeEnum);
UserProfile userProfile = userProfileGateway.getByUserId(cmd.getReqUserId());
// 5. 保存违规记录
ViolationRecord record = ViolationRecord.builder()
.targetType(TargetTypeEnum.ROOM.getCode())
.targetId(cmd.getRoomId())
.violationType(cmd.getViolationType())
.operationType(OperationTypeEnum.ADJUST.getCode())
.description(cmd.getDescription())
.screenshotUrls(JSON.toJSONString(cmd.getScreenshotUrls()))
.originalContent(originalContent)
.adjustedContent(adjustedContent)
.operatorId(userProfile.getId())
.operatorName(userProfile.getUserNickname())
.build();
Long recordId = violationRecordGateway.save(record);
// 6. 发送调整通知
sendAdjustNotification(roomProfile.getUserId(), violationTypeEnum, adjustedContent);
return ViolationHandleResultCO.builder()
.recordId(recordId)
.success(true)
.message("调整成功")
.originalContent(originalContent)
.adjustedContent(adjustedContent)
.build();
}
/**
* 获取原始内容
*/
private String getOriginalContent(RoomProfile roomProfile, ViolationTypeEnum violationType) {
return switch (violationType) {
case ROOM_NICKNAME -> roomProfile.getRoomName();
case ROOM_COVER -> roomProfile.getRoomCover();
case ROOM_NOTICE -> roomProfile.getRoomDesc();
case ROOM_THEME -> "";
default -> null;
};
}
/**
* 调整违规内容
*/
private String adjustViolationContent(RoomProfile roomProfile, ViolationTypeEnum violationType) {
String adjustedContent;
switch (violationType) {
case ROOM_NICKNAME -> {
adjustedContent = roomProfile.getRoomAccount() + "&" + roomProfile.getId();
RoomProfile updateProfile = new RoomProfile();
updateProfile.setId(roomProfile.getId());
updateProfile.setRoomName(adjustedContent);
roomProfileManagerService.updateSelectiveProfile(updateProfile);
}
case ROOM_COVER -> {
adjustedContent = enumConfigCacheService.getValue(EnumConfigKey.DEFAULT_ROOM_COVER, roomProfile.getSysOrigin());
roomProfileManagerService.updateRoomCoverByUserId(roomProfile.getUserId(), adjustedContent);
}
case ROOM_NOTICE -> {
adjustedContent = DEFAULT_ROOM_NOTICE;
roomProfileManagerService.updateRoomDescByIds(java.util.Collections.singletonList(roomProfile.getId()), adjustedContent);
}
case ROOM_THEME -> {
adjustedContent = DEFAULT_THEME;
RoomProfile updateProfile = new RoomProfile();
updateProfile.setId(roomProfile.getId());
// updateProfile.setTheme(adjustedContent);
roomProfileManagerService.updateSelectiveProfile(updateProfile);
}
default -> adjustedContent = null;
}
return adjustedContent;
}
/**
* 发送调整通知
*/
private void sendAdjustNotification(Long userId, ViolationTypeEnum violationType, String adjustedContent) {
String content = getAdjustNotificationContent(violationType, adjustedContent);
Map<String, Object> titleMap = new HashMap<>();
titleMap.put("type", "violation_adjust");
titleMap.put("violationType", violationType.getCode());
officialNoticeClient.send(
NoticeExtTemplateCustomizeCmd.builder()
.toAccount(userId)
.noticeType(OfficialNoticeTypeEnum.VIOLATION_ADJUST)
.content(content)
.title(com.alibaba.fastjson.JSON.toJSONString(titleMap))
.build());
}
/**
* 获取调整通知内容
*/
private String getAdjustNotificationContent(ViolationTypeEnum violationType, String adjustedContent) {
return switch (violationType) {
case ROOM_NICKNAME -> String.format("Your room name has been changed to '%s' due to violation of rules.", adjustedContent);
case ROOM_COVER -> "Your room profile picture has been changed to the default image due to violation of rules.";
case ROOM_NOTICE -> String.format("Your room notice has been changed to '%s' due to violation of regulations.", adjustedContent);
case ROOM_THEME -> "Your room theme has been changed to the default theme due to violation of rules.";
default -> "Your room information has been adjusted due to violation of rules.";
};
}
}

View File

@ -0,0 +1,129 @@
package com.red.circle.other.app.command.violation.room;
import com.alibaba.fastjson.JSON;
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.enums.message.OfficialNoticeTypeEnum;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.other.app.dto.cmd.violation.RoomViolationHandleCmd;
import com.red.circle.other.app.dto.clientobject.violation.ViolationHandleResultCO;
import com.red.circle.other.app.enums.violation.OperationTypeEnum;
import com.red.circle.other.app.enums.violation.TargetTypeEnum;
import com.red.circle.other.app.enums.violation.ViolationTypeEnum;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.gateway.violation.ViolationRecordGateway;
import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.domain.violation.ViolationRecord;
import com.red.circle.other.infra.database.mongo.entity.live.RoomProfile;
import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService;
import com.red.circle.other.infra.database.rds.service.sys.AdministratorAuthService;
import com.red.circle.other.inner.asserts.RoomErrorCode;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* 房间违规警告命令执行器
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class RoomViolationWarningCmdExe {
private final ViolationRecordGateway violationRecordGateway;
private final RoomProfileManagerService roomProfileManagerService;
private final AdministratorAuthService administratorAuthService;
private final OfficialNoticeClient officialNoticeClient;
private final UserProfileGateway userProfileGateway;
public ViolationHandleResultCO execute(RoomViolationHandleCmd cmd) {
ViolationTypeEnum violationTypeEnum = ViolationTypeEnum.getByCode(cmd.getViolationType());
ResponseAssert.notNull(CommonErrorCode.DATA_ERROR, violationTypeEnum);
// 1. 验证管理员权限
ResponseAssert.isTrue(CommonErrorCode.INSUFFICIENT_PERMISSION,
administratorAuthService.existsAuth(cmd.requiredReqUserId(), violationTypeEnum.name()));
// 2. 查询房间信息
RoomProfile roomProfile = roomProfileManagerService.getProfileById(Long.valueOf(cmd.getRoomId()));
ResponseAssert.notNull(RoomErrorCode.ROOM_NOT_EXISTS, roomProfile);
// 3. 获取原始内容
String originalContent = getOriginalContent(roomProfile, violationTypeEnum);
UserProfile userProfile = userProfileGateway.getByUserId(cmd.getReqUserId());
// 4. 保存违规记录
ViolationRecord record = ViolationRecord.builder()
.targetType(TargetTypeEnum.ROOM.getCode())
.targetId(cmd.getRoomId())
.violationType(cmd.getViolationType())
.operationType(OperationTypeEnum.WARNING.getCode())
.description(cmd.getDescription())
.screenshotUrls(JSON.toJSONString(cmd.getScreenshotUrls()))
.originalContent(originalContent)
.operatorId(userProfile.getId())
.operatorName(userProfile.getUserNickname())
.build();
Long recordId = violationRecordGateway.save(record);
// 5. 发送警告消息
sendWarningMessage(roomProfile.getUserId(), violationTypeEnum);
return ViolationHandleResultCO.builder()
.recordId(recordId)
.success(true)
.message("警告发送成功")
.build();
}
/**
* 获取原始内容
*/
private String getOriginalContent(RoomProfile roomProfile, ViolationTypeEnum violationType) {
return switch (violationType) {
case ROOM_NICKNAME -> roomProfile.getRoomName();
case ROOM_COVER -> roomProfile.getRoomCover();
case ROOM_NOTICE -> roomProfile.getRoomDesc();
case ROOM_THEME -> "";
default -> null;
};
}
/**
* 发送警告消息
*/
private void sendWarningMessage(Long userId, ViolationTypeEnum violationType) {
String content = getWarningContent(violationType);
Map<String, Object> titleMap = new HashMap<>();
titleMap.put("type", "violation_warning");
titleMap.put("violationType", violationType.getCode());
officialNoticeClient.send(
NoticeExtTemplateCustomizeCmd.builder()
.toAccount(userId)
.noticeType(OfficialNoticeTypeEnum.VIOLATION_WARNING)
.content(content)
.title(JSON.toJSONString(titleMap))
.build());
}
/**
* 获取警告内容
*/
private String getWarningContent(ViolationTypeEnum violationType) {
return switch (violationType) {
case ROOM_NICKNAME -> "Your room name is in violation of the rules. Please correct the current room name immediately.";
case ROOM_COVER -> "Your room profile picture violates the rules. Please change your current room profile picture immediately.";
case ROOM_NOTICE -> "Your room notice is in violation of regulations. Please rectify it immediately.";
case ROOM_THEME -> "Your room theme violates the rules. Please rectify it immediately.";
default -> "Your room information is in violation of the rules. Please correct it immediately.";
};
}
}

View File

@ -0,0 +1,169 @@
package com.red.circle.other.app.command.violation.user;
import com.alibaba.fastjson.JSON;
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.enums.message.OfficialNoticeTypeEnum;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.other.app.dto.clientobject.violation.ViolationHandleResultCO;
import com.red.circle.other.app.dto.cmd.violation.UserViolationHandleCmd;
import com.red.circle.other.app.enums.violation.OperationTypeEnum;
import com.red.circle.other.app.enums.violation.TargetTypeEnum;
import com.red.circle.other.app.enums.violation.ViolationTypeEnum;
import com.red.circle.other.domain.gateway.violation.ViolationRecordGateway;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.domain.violation.ViolationRecord;
import com.red.circle.other.infra.database.cache.service.other.EnumConfigCacheService;
import com.red.circle.other.infra.database.rds.service.sys.AdministratorAuthService;
import com.red.circle.other.inner.asserts.user.UserErrorCode;
import com.red.circle.other.inner.enums.config.EnumConfigKey;
import com.red.circle.other.inner.enums.sys.appmanager.ManagerApprovalType;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
/**
* 用户违规调整命令执行器
*
* @author Claude
* @date 2025-01-15
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class UserViolationAdjustCmdExe {
private final UserProfileGateway userProfileGateway;
private final ViolationRecordGateway violationRecordGateway;
private final AdministratorAuthService administratorAuthService;
private final OfficialNoticeClient officialNoticeClient;
private final EnumConfigCacheService enumConfigCacheService;
@Transactional(rollbackFor = Exception.class)
public ViolationHandleResultCO execute(UserViolationHandleCmd cmd) {
// 1. 权限验证
ResponseAssert.isTrue(CommonErrorCode.INSUFFICIENT_PERMISSION,
administratorAuthService.existsAuth(cmd.requiredReqUserId(), ManagerApprovalType.USER_AVATAR.name()));
// 2. 查询用户信息
UserProfile userProfile = userProfileGateway.getByUserId(cmd.getUserId());
ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, userProfile);
// 3. 验证违规类型
ViolationTypeEnum violationType = ViolationTypeEnum.getByCode(cmd.getViolationType());
ResponseAssert.notNull(CommonErrorCode.DATA_ERROR, violationType);
ResponseAssert.isTrue(CommonErrorCode.DATA_ERROR, violationType.matchTargetType(TargetTypeEnum.USER));
// 4. 获取原始内容
String originalContent = getOriginalContent(userProfile, violationType);
// 5. 调整内容
String adjustedContent = adjustContent(userProfile, violationType, cmd.getUserId());
// 6. 更新用户信息
updateUserProfile(userProfile, violationType, adjustedContent);
// 7. 保存违规记录
String bizNo = "violation_user_" + cmd.getUserId() + "_" + System.currentTimeMillis();
UserProfile reqUser = userProfileGateway.getByUserId(cmd.getUserId());
ViolationRecord record = buildViolationRecord(cmd, reqUser, bizNo, originalContent, adjustedContent);
Long recordId = violationRecordGateway.save(record);
// 8. 发送通知消息
sendAdjustMessage(cmd.getUserId(), violationType);
log.info("用户违规调整完成, userId={}, violationType={}, original={}, adjusted={}",
cmd.getUserId(), violationType.getDesc(), originalContent, adjustedContent);
return ViolationHandleResultCO.builder()
.recordId(recordId)
.bizNo(bizNo)
.success(true)
.originalContent(originalContent)
.adjustedContent(adjustedContent)
.message("调整已完成")
.build();
}
private String getOriginalContent(UserProfile userProfile, ViolationTypeEnum violationType) {
switch (violationType) {
case USER_NICKNAME:
return userProfile.getUserNickname();
case USER_AVATAR:
return userProfile.getUserAvatar();
default:
return null;
}
}
private String adjustContent(UserProfile userProfile, ViolationTypeEnum violationType, Long userId) {
switch (violationType) {
case USER_NICKNAME:
return "用户" + userProfile.getAccount();
case USER_AVATAR:
return enumConfigCacheService.getValue(EnumConfigKey.DEFAULT_USER_AVATAR, userProfile.getOriginSys());
default:
return null;
}
}
private void updateUserProfile(UserProfile userProfile, ViolationTypeEnum violationType, String adjustedContent) {
switch (violationType) {
case USER_NICKNAME:
userProfile.setUserNickname(adjustedContent);
break;
case USER_AVATAR:
userProfile.setUserAvatar(adjustedContent);
break;
}
userProfileGateway.updateSelectiveById(userProfile);
}
private void sendAdjustMessage(Long userId, ViolationTypeEnum violationType) {
String content = buildAdjustContent(violationType);
officialNoticeClient.send(
NoticeExtTemplateCustomizeCmd.builder()
.toAccount(userId)
.noticeType(OfficialNoticeTypeEnum.VIOLATION_ADJUST)
.content(content)
.title("System")
.build());
}
private String buildAdjustContent(ViolationTypeEnum violationType) {
switch (violationType) {
case USER_NICKNAME:
return "Your username has been adjusted due to rule violations. Please update your username as soon as possible.";
case USER_AVATAR:
return "Your profile picture has been adjusted due to rule violations. Please update your profile picture as soon as possible.";
default:
return "Your information has been adjusted due to rule violations. Please update it as soon as possible.";
}
}
private ViolationRecord buildViolationRecord(UserViolationHandleCmd cmd, UserProfile reqUser,
String bizNo, String originalContent, String adjustedContent) {
ViolationRecord record = new ViolationRecord();
record.setBizNo(bizNo);
record.setTargetType(TargetTypeEnum.USER.getCode());
record.setTargetId(String.valueOf(cmd.getUserId()));
record.setViolationType(cmd.getViolationType());
record.setOperationType(OperationTypeEnum.ADJUST.getCode());
record.setDescription(cmd.getDescription());
record.setScreenshotUrls(cmd.getScreenshotUrls() != null ? JSON.toJSONString(cmd.getScreenshotUrls()) : null);
record.setOriginalContent(originalContent);
record.setAdjustedContent(adjustedContent);
record.setOperatorId(reqUser.getId());
record.setOperatorName(reqUser.getUserNickname());
record.setStatus(1);
record.setCreateTime(LocalDateTime.now());
record.setUpdateTime(LocalDateTime.now());
return record;
}
}

View File

@ -0,0 +1,132 @@
package com.red.circle.other.app.command.violation.user;
import com.alibaba.fastjson.JSON;
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.enums.message.OfficialNoticeTypeEnum;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.other.app.dto.clientobject.violation.ViolationHandleResultCO;
import com.red.circle.other.app.dto.cmd.violation.UserViolationHandleCmd;
import com.red.circle.other.app.enums.violation.OperationTypeEnum;
import com.red.circle.other.app.enums.violation.TargetTypeEnum;
import com.red.circle.other.app.enums.violation.ViolationTypeEnum;
import com.red.circle.other.domain.gateway.violation.ViolationRecordGateway;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.domain.violation.ViolationRecord;
import com.red.circle.other.infra.database.rds.service.sys.AdministratorAuthService;
import com.red.circle.other.inner.asserts.user.UserErrorCode;
import com.red.circle.other.inner.enums.sys.appmanager.ManagerApprovalType;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
/**
* 用户违规警告命令执行器
*
* @author Claude
* @date 2025-01-15
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class UserViolationWarningCmdExe {
private final UserProfileGateway userProfileGateway;
private final ViolationRecordGateway violationRecordGateway;
private final AdministratorAuthService administratorAuthService;
private final OfficialNoticeClient officialNoticeClient;
@Transactional(rollbackFor = Exception.class)
public ViolationHandleResultCO execute(UserViolationHandleCmd cmd) {
// 1. 权限验证
ResponseAssert.isTrue(CommonErrorCode.INSUFFICIENT_PERMISSION,
administratorAuthService.existsAuth(cmd.requiredReqUserId(), ManagerApprovalType.USER_NICKNAME.name()));
// 2. 查询用户信息
UserProfile userProfile = userProfileGateway.getByUserId(cmd.getUserId());
ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, userProfile);
// 3. 验证违规类型
ViolationTypeEnum violationType = ViolationTypeEnum.getByCode(cmd.getViolationType());
ResponseAssert.notNull(CommonErrorCode.DATA_ERROR, violationType);
ResponseAssert.isTrue(CommonErrorCode.DATA_ERROR, violationType.matchTargetType(TargetTypeEnum.USER));
// 4. 获取原始内容
String originalContent = getOriginalContent(userProfile, violationType);
// 5. 保存违规记录
String bizNo = "violation_user_" + cmd.getUserId() + "_" + System.currentTimeMillis();
UserProfile reqUser = userProfileGateway.getByUserId(cmd.getReqUserId());
ViolationRecord record = buildViolationRecord(cmd, reqUser, bizNo, originalContent, null);
Long recordId = violationRecordGateway.save(record);
// 6. 发送警告消息
sendWarningMessage(cmd.getUserId(), violationType);
return ViolationHandleResultCO.builder()
.recordId(recordId)
.bizNo(bizNo)
.success(true)
.originalContent(originalContent)
.message("警告已发送")
.build();
}
private String getOriginalContent(UserProfile userProfile, ViolationTypeEnum violationType) {
switch (violationType) {
case USER_NICKNAME:
return userProfile.getUserNickname();
case USER_AVATAR:
return userProfile.getUserAvatar();
default:
return null;
}
}
private void sendWarningMessage(Long userId, ViolationTypeEnum violationType) {
String content = buildWarningContent(violationType);
officialNoticeClient.send(
NoticeExtTemplateCustomizeCmd.builder()
.toAccount(userId)
.noticeType(OfficialNoticeTypeEnum.VIOLATION_WARNING)
.content(content)
.title("System")
.build());
}
private String buildWarningContent(ViolationTypeEnum violationType) {
switch (violationType) {
case USER_NICKNAME:
return "Your username is in violation of the rules. Please correct the current username immediately.";
case USER_AVATAR:
return "Your user profile picture is in violation of the rules. Please correct your current user profile picture immediately.";
default:
return "Your information violates the rules. Please correct it immediately.";
}
}
private ViolationRecord buildViolationRecord(UserViolationHandleCmd cmd, UserProfile reqUser,
String bizNo, String originalContent, String adjustedContent) {
ViolationRecord record = new ViolationRecord();
record.setBizNo(bizNo);
record.setTargetType(TargetTypeEnum.USER.getCode());
record.setTargetId(String.valueOf(cmd.getUserId()));
record.setViolationType(cmd.getViolationType());
record.setOperationType(OperationTypeEnum.WARNING.getCode());
record.setDescription(cmd.getDescription());
record.setScreenshotUrls(cmd.getScreenshotUrls() != null ? JSON.toJSONString(cmd.getScreenshotUrls()) : null);
record.setOriginalContent(originalContent);
record.setAdjustedContent(adjustedContent);
record.setOperatorId(reqUser.getId());
record.setOperatorName(reqUser.getUserNickname());
record.setStatus(1);
record.setCreateTime(LocalDateTime.now());
record.setUpdateTime(LocalDateTime.now());
return record;
}
}

View File

@ -0,0 +1,56 @@
package com.red.circle.other.app.convertor.violation;
import com.alibaba.fastjson.JSON;
import com.red.circle.other.app.dto.clientobject.violation.ViolationRecordCO;
import com.red.circle.other.domain.violation.ViolationRecord;
import org.mapstruct.Mapper;
import java.util.List;
import java.util.stream.Collectors;
/**
* 违规记录转换器
*/
@Mapper(componentModel = "spring")
public interface ViolationRecordConvertor {
/**
* 领域对象转客户端对象
*/
default ViolationRecordCO toCO(ViolationRecord record) {
if (record == null) {
return null;
}
ViolationRecordCO co = new ViolationRecordCO();
co.setId(record.getId());
co.setBizNo(record.getBizNo());
co.setTargetType(record.getTargetType());
co.setTargetId(record.getTargetId());
co.setViolationType(record.getViolationType());
co.setOperationType(record.getOperationType());
co.setDescription(record.getDescription());
// JSON 字符串转换为 List
co.setScreenshotUrls(record.getScreenshotUrls() != null ?
JSON.parseArray(record.getScreenshotUrls(), String.class) : null);
co.setOriginalContent(record.getOriginalContent());
co.setAdjustedContent(record.getAdjustedContent());
co.setOperatorId(record.getOperatorId());
co.setOperatorName(record.getOperatorName());
co.setCreateTime(record.getCreateTime());
return co;
}
/**
* 批量转换
*/
default List<ViolationRecordCO> toCOList(List<ViolationRecord> records) {
if (records == null) {
return null;
}
return records.stream()
.map(this::toCO)
.collect(Collectors.toList());
}
}

View File

@ -0,0 +1,50 @@
package com.red.circle.other.app.enums.violation;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 违规操作类型枚举
*
* @author Claude
* @date 2025-01-15
*/
@Getter
@AllArgsConstructor
public enum OperationTypeEnum {
/**
* 警告
*/
WARNING(1, "警告"),
/**
* 调整
*/
ADJUST(2, "调整");
/**
* 类型编码
*/
private final Integer code;
/**
* 类型描述
*/
private final String desc;
/**
* 根据编码获取枚举
*/
public static OperationTypeEnum getByCode(Integer code) {
if (code == null) {
return null;
}
return Arrays.stream(values())
.filter(e -> e.getCode().equals(code))
.findFirst()
.orElse(null);
}
}

View File

@ -0,0 +1,50 @@
package com.red.circle.other.app.enums.violation;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 违规目标类型枚举
*
* @author Claude
* @date 2025-01-15
*/
@Getter
@AllArgsConstructor
public enum TargetTypeEnum {
/**
* 用户
*/
USER(1, "用户"),
/**
* 房间
*/
ROOM(2, "房间");
/**
* 类型编码
*/
private final Integer code;
/**
* 类型描述
*/
private final String desc;
/**
* 根据编码获取枚举
*/
public static TargetTypeEnum getByCode(Integer code) {
if (code == null) {
return null;
}
return Arrays.stream(values())
.filter(e -> e.getCode().equals(code))
.findFirst()
.orElse(null);
}
}

View File

@ -0,0 +1,82 @@
package com.red.circle.other.app.enums.violation;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 违规类型枚举
*
* @author Claude
* @date 2025-01-15
*/
@Getter
@AllArgsConstructor
public enum ViolationTypeEnum {
/**
* 用户名
*/
USER_NICKNAME(1, "用户名", TargetTypeEnum.USER),
/**
* 用户头像
*/
USER_AVATAR(2, "用户头像", TargetTypeEnum.USER),
/**
* 房间名称
*/
ROOM_NICKNAME(3, "房间名称", TargetTypeEnum.ROOM),
/**
* 房间头像
*/
ROOM_COVER(4, "房间头像", TargetTypeEnum.ROOM),
/**
* 房间公告
*/
ROOM_NOTICE(5, "房间公告", TargetTypeEnum.ROOM),
/**
* 房间主题
*/
ROOM_THEME(6, "房间主题", TargetTypeEnum.ROOM);
/**
* 类型编码
*/
private final Integer code;
/**
* 类型描述
*/
private final String desc;
/**
* 所属目标类型
*/
private final TargetTypeEnum targetType;
/**
* 根据编码获取枚举
*/
public static ViolationTypeEnum getByCode(Integer code) {
if (code == null) {
return null;
}
return Arrays.stream(values())
.filter(e -> e.getCode().equals(code))
.findFirst()
.orElse(null);
}
/**
* 验证违规类型是否匹配目标类型
*/
public boolean matchTargetType(TargetTypeEnum targetType) {
return this.targetType == targetType;
}
}

View File

@ -0,0 +1,43 @@
package com.red.circle.other.app.service.violation;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.other.app.command.violation.query.ViolationRecordPageQryExe;
import com.red.circle.other.app.command.violation.room.RoomViolationAdjustCmdExe;
import com.red.circle.other.app.command.violation.room.RoomViolationWarningCmdExe;
import com.red.circle.other.app.dto.cmd.violation.RoomViolationHandleCmd;
import com.red.circle.other.app.dto.cmd.violation.ViolationRecordPageQueryCmd;
import com.red.circle.other.app.dto.clientobject.violation.ViolationHandleResultCO;
import com.red.circle.other.app.dto.clientobject.violation.ViolationRecordCO;
import com.red.circle.other.app.enums.violation.OperationTypeEnum;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Objects;
/**
* 房间违规服务实现
*/
@Service
@RequiredArgsConstructor
public class RoomViolationServiceImpl implements RoomViolationService {
private final RoomViolationWarningCmdExe roomViolationWarningCmdExe;
private final RoomViolationAdjustCmdExe roomViolationAdjustCmdExe;
private final ViolationRecordPageQryExe violationRecordPageQryExe;
@Override
@Transactional(rollbackFor = Exception.class)
public ViolationHandleResultCO handle(RoomViolationHandleCmd cmd) {
if (Objects.equals(cmd.getOperationType(), OperationTypeEnum.WARNING.getCode())) {
return roomViolationWarningCmdExe.execute(cmd);
} else {
return roomViolationAdjustCmdExe.execute(cmd);
}
}
@Override
public PageResult<ViolationRecordCO> pageQuery(ViolationRecordPageQueryCmd cmd) {
return violationRecordPageQryExe.execute(cmd);
}
}

View File

@ -0,0 +1,43 @@
package com.red.circle.other.app.service.violation;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.other.app.command.violation.query.ViolationRecordPageQryExe;
import com.red.circle.other.app.command.violation.user.UserViolationAdjustCmdExe;
import com.red.circle.other.app.command.violation.user.UserViolationWarningCmdExe;
import com.red.circle.other.app.dto.cmd.violation.UserViolationHandleCmd;
import com.red.circle.other.app.dto.cmd.violation.ViolationRecordPageQueryCmd;
import com.red.circle.other.app.dto.clientobject.violation.ViolationHandleResultCO;
import com.red.circle.other.app.dto.clientobject.violation.ViolationRecordCO;
import com.red.circle.other.app.enums.violation.OperationTypeEnum;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Objects;
/**
* 用户违规服务实现
*/
@Service
@RequiredArgsConstructor
public class UserViolationServiceImpl implements UserViolationService {
private final UserViolationWarningCmdExe userViolationWarningCmdExe;
private final UserViolationAdjustCmdExe userViolationAdjustCmdExe;
private final ViolationRecordPageQryExe violationRecordPageQryExe;
@Override
@Transactional(rollbackFor = Exception.class)
public ViolationHandleResultCO handle(UserViolationHandleCmd cmd) {
if (Objects.equals(cmd.getOperationType(), OperationTypeEnum.WARNING.getCode())) {
return userViolationWarningCmdExe.execute(cmd);
} else {
return userViolationAdjustCmdExe.execute(cmd);
}
}
@Override
public PageResult<ViolationRecordCO> pageQuery(ViolationRecordPageQueryCmd cmd) {
return violationRecordPageQryExe.execute(cmd);
}
}

View File

@ -0,0 +1,49 @@
package com.red.circle.other.app.dto.clientobject.violation;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 违规处理结果客户端对象
*
* @author Claude
* @date 2025-01-15
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ViolationHandleResultCO {
/**
* 记录ID
*/
private Long recordId;
/**
* 业务单号
*/
private String bizNo;
/**
* 处理消息
*/
private String message;
/**
* 是否成功
*/
private Boolean success;
/**
* 原始内容
*/
private String originalContent;
/**
* 调整后内容
*/
private String adjustedContent;
}

View File

@ -0,0 +1,106 @@
package com.red.circle.other.app.dto.clientobject.violation;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
/**
* 违规记录客户端对象
*
* @author Claude
* @date 2025-01-15
*/
@Data
public class ViolationRecordCO {
/**
* 记录ID
*/
private Long id;
/**
* 业务单号
*/
private String bizNo;
/**
* 目标类型1-用户 2-房间
*/
private Integer targetType;
/**
* 目标类型描述
*/
private String targetTypeDesc;
/**
* 目标ID
*/
private String targetId;
/**
* 违规类型
*/
private Integer violationType;
/**
* 违规类型描述
*/
private String violationTypeDesc;
/**
* 操作类型1-警告 2-调整
*/
private Integer operationType;
/**
* 操作类型描述
*/
private String operationTypeDesc;
/**
* 问题描述
*/
private String description;
/**
* 截图地址列表
*/
private List<String> screenshotUrls;
/**
* 原始内容
*/
private String originalContent;
/**
* 调整后内容
*/
private String adjustedContent;
/**
* 操作管理员ID
*/
private Long operatorId;
/**
* 操作管理员名称
*/
private String operatorName;
/**
* 状态
*/
private Integer status;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
}

View File

@ -0,0 +1,49 @@
package com.red.circle.other.app.dto.cmd.violation;
import com.red.circle.common.business.dto.cmd.AppExtCommand;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* 房间违规处理命令
*
* @author Claude
* @date 2025-01-15
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class RoomViolationHandleCmd extends AppExtCommand {
/**
* 房间ID
*/
@NotBlank(message = "房间ID不能为空")
private String roomId;
/**
* 违规类型3-房间名 4-房间头像 5-房间公告 6-房间主题
*/
@NotNull(message = "违规类型不能为空")
private Integer violationType;
/**
* 操作类型1-警告 2-调整
*/
@NotNull(message = "操作类型不能为空")
private Integer operationType;
/**
* 问题描述
*/
@NotBlank(message = "问题描述不能为空")
private String description;
/**
* 截图URL列表
*/
private List<String> screenshotUrls;
}

View File

@ -0,0 +1,49 @@
package com.red.circle.other.app.dto.cmd.violation;
import com.red.circle.common.business.dto.cmd.AppExtCommand;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* 用户违规处理命令
*
* @author Claude
* @date 2025-01-15
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class UserViolationHandleCmd extends AppExtCommand {
/**
* 用户ID
*/
@NotNull(message = "用户ID不能为空")
private Long userId;
/**
* 违规类型1-用户名 2-用户头像
*/
@NotNull(message = "违规类型不能为空")
private Integer violationType;
/**
* 操作类型1-警告 2-调整
*/
@NotNull(message = "操作类型不能为空")
private Integer operationType;
/**
* 问题描述
*/
@NotBlank(message = "问题描述不能为空")
private String description;
/**
* 截图URL列表
*/
private List<String> screenshotUrls;
}

View File

@ -0,0 +1,41 @@
package com.red.circle.other.app.dto.cmd.violation;
import com.red.circle.common.business.dto.PageQueryCmd;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 违规记录分页查询命令
*
* @author Claude
* @date 2025-01-15
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ViolationRecordPageQueryCmd extends PageQueryCmd {
/**
* 目标类型1-用户 2-房间
*/
private Integer targetType;
/**
* 目标ID
*/
private String targetId;
/**
* 违规类型
*/
private Integer violationType;
/**
* 操作类型
*/
private Integer operationType;
/**
* 操作管理员ID
*/
private Long operatorId;
}

View File

@ -0,0 +1,32 @@
package com.red.circle.other.app.service.violation;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.other.app.dto.clientobject.violation.ViolationHandleResultCO;
import com.red.circle.other.app.dto.clientobject.violation.ViolationRecordCO;
import com.red.circle.other.app.dto.cmd.violation.RoomViolationHandleCmd;
import com.red.circle.other.app.dto.cmd.violation.ViolationRecordPageQueryCmd;
/**
* 房间违规服务接口
*
* @author Claude
* @date 2025-01-15
*/
public interface RoomViolationService {
/**
* 处理房间违规
*
* @param cmd 处理命令
* @return 处理结果
*/
ViolationHandleResultCO handle(RoomViolationHandleCmd cmd);
/**
* 分页查询违规记录
*
* @param cmd 查询命令
* @return 分页结果
*/
PageResult<ViolationRecordCO> pageQuery(ViolationRecordPageQueryCmd cmd);
}

View File

@ -0,0 +1,32 @@
package com.red.circle.other.app.service.violation;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.other.app.dto.clientobject.violation.ViolationHandleResultCO;
import com.red.circle.other.app.dto.clientobject.violation.ViolationRecordCO;
import com.red.circle.other.app.dto.cmd.violation.UserViolationHandleCmd;
import com.red.circle.other.app.dto.cmd.violation.ViolationRecordPageQueryCmd;
/**
* 用户违规服务接口
*
* @author Claude
* @date 2025-01-15
*/
public interface UserViolationService {
/**
* 处理用户违规
*
* @param cmd 处理命令
* @return 处理结果
*/
ViolationHandleResultCO handle(UserViolationHandleCmd cmd);
/**
* 分页查询违规记录
*
* @param cmd 查询命令
* @return 分页结果
*/
PageResult<ViolationRecordCO> pageQuery(ViolationRecordPageQueryCmd cmd);
}

View File

@ -0,0 +1,67 @@
package com.red.circle.other.domain.gateway.violation;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.other.domain.violation.ViolationRecord;
import java.util.List;
/**
* 违规记录领域网关
*
* @author Claude
* @date 2025-01-15
*/
public interface ViolationRecordGateway {
/**
* 保存违规记录
*
* @param record 违规记录
* @return 记录ID
*/
Long save(ViolationRecord record);
/**
* 根据业务单号查询
*
* @param bizNo 业务单号
* @return 违规记录
*/
// ViolationRecord getByBizNo(String bizNo);
/**
* 根据ID查询
*
* @param id 记录ID
* @return 违规记录
*/
// ViolationRecord getById(Long id);
/**
* 分页查询
*
* @param targetType 目标类型
* @param targetId 目标ID
* @param violationType 违规类型
* @param operationType 操作类型
* @param operatorId 操作人ID
* @param pageNum 页码
* @param pageSize 每页数量
* @return 记录列表
*/
PageResult<ViolationRecord> pageQuery(Integer targetType, String targetId, Integer violationType,
Integer operationType, Long operatorId, Integer pageNum, Integer pageSize);
/**
* 统计总数
*
* @param targetType 目标类型
* @param targetId 目标ID
* @param violationType 违规类型
* @param operationType 操作类型
* @param operatorId 操作人ID
* @return 总数
*/
// Long count(Integer targetType, String targetId, Integer violationType,
// Integer operationType, Long operatorId);
}

View File

@ -0,0 +1,96 @@
package com.red.circle.other.domain.violation;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* 违规记录领域对象
*
* @author Claude
* @date 2025-01-15
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ViolationRecord {
/**
* 主键ID
*/
private Long id;
/**
* 业务单号
*/
private String bizNo;
/**
* 目标类型1-用户 2-房间
*/
private Integer targetType;
/**
* 目标ID
*/
private String targetId;
/**
* 违规类型
*/
private Integer violationType;
/**
* 操作类型1-警告 2-调整
*/
private Integer operationType;
/**
* 问题描述
*/
private String description;
/**
* 截图地址JSON数组
*/
private String screenshotUrls;
/**
* 原始内容
*/
private String originalContent;
/**
* 调整后内容
*/
private String adjustedContent;
/**
* 操作管理员ID
*/
private Long operatorId;
/**
* 操作管理员名称
*/
private String operatorName;
/**
* 状态
*/
private Integer status;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
}

View File

@ -0,0 +1,16 @@
package com.red.circle.other.infra.database.rds.dao.violation;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.red.circle.framework.mybatis.dao.BaseDAO;
import com.red.circle.other.infra.database.rds.entity.violation.ViolationRecordEntity;
import org.apache.ibatis.annotations.Mapper;
/**
* 违规记录Mapper
*
* @author Claude
* @date 2025-01-15
*/
@Mapper
public interface ViolationRecordDAO extends BaseDAO<ViolationRecordEntity> {
}

View File

@ -0,0 +1,95 @@
package com.red.circle.other.infra.database.rds.entity.violation;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 违规处理记录表
*
* @author Claude
* @date 2025-01-15
*/
@Data
@TableName("t_violation_record")
public class ViolationRecordEntity {
/**
* 主键ID
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 业务单号幂等
*/
private String bizNo;
/**
* 目标类型1-用户 2-房间
*/
private Integer targetType;
/**
* 目标ID用户ID或房间ID
*/
private String targetId;
/**
* 违规类型1-用户名 2-用户头像 3-房间名 4-房间头像 5-房间公告 6-房间主题
*/
private Integer violationType;
/**
* 操作类型1-警告 2-调整
*/
private Integer operationType;
/**
* 问题描述
*/
private String description;
/**
* 截图地址JSON数组
*/
private String screenshotUrls;
/**
* 原始内容
*/
private String originalContent;
/**
* 调整后内容
*/
private String adjustedContent;
/**
* 操作管理员ID
*/
private Long operatorId;
/**
* 操作管理员名称
*/
private String operatorName;
/**
* 状态1-已处理
*/
private Integer status;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
}

View File

@ -0,0 +1,125 @@
package com.red.circle.other.infra.gateway.violation;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.other.domain.gateway.violation.ViolationRecordGateway;
import com.red.circle.other.domain.violation.ViolationRecord;
import com.red.circle.other.infra.database.rds.dao.violation.ViolationRecordDAO;
import com.red.circle.other.infra.database.rds.entity.violation.ViolationRecordEntity;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
/**
* 违规记录网关实现
*/
@Component
@RequiredArgsConstructor
public class ViolationRecordGatewayImpl implements ViolationRecordGateway {
private final ViolationRecordDAO violationRecordDAO;
@Override
public Long save(ViolationRecord record) {
ViolationRecordEntity entity = toEntity(record);
entity.setBizNo(generateBizNo(record));
entity.setCreateTime(LocalDateTime.now());
entity.setUpdateTime(LocalDateTime.now());
violationRecordDAO.insert(entity);
return entity.getId();
}
@Override
public PageResult<ViolationRecord> pageQuery(Integer targetType, String targetId, Integer violationType,
Integer operationType, Long operatorId, Integer pageNum, Integer pageSize) {
LambdaQueryWrapper<ViolationRecordEntity> wrapper = new LambdaQueryWrapper<>();
if (targetType != null) {
wrapper.eq(ViolationRecordEntity::getTargetType, targetType);
}
if (targetId != null) {
wrapper.eq(ViolationRecordEntity::getTargetId, targetId);
}
if (violationType != null) {
wrapper.eq(ViolationRecordEntity::getViolationType, violationType);
}
if (operationType != null) {
wrapper.eq(ViolationRecordEntity::getOperationType, operationType);
}
wrapper.orderByDesc(ViolationRecordEntity::getCreateTime);
IPage<ViolationRecordEntity> page = new Page<>(pageNum, pageSize);
IPage<ViolationRecordEntity> result = violationRecordDAO.selectPage(page, wrapper);
List<ViolationRecord> records = result.getRecords().stream()
.map(this::toDomain)
.collect(Collectors.toList());
PageResult<ViolationRecord> pageResult = new PageResult<>();
pageResult.setRecords(records);
pageResult.setTotal(page.getTotal());
pageResult.setCurrent(page.getCurrent());
pageResult.setSize(page.getSize());
return pageResult;
}
/**
* 生成业务单号
*/
private String generateBizNo(ViolationRecord record) {
return String.format("violation_%d_%s_%s",
record.getTargetType(),
record.getTargetId(),
IdUtil.getSnowflakeNextIdStr());
}
/**
* 领域对象转实体
*/
private ViolationRecordEntity toEntity(ViolationRecord record) {
ViolationRecordEntity entity = new ViolationRecordEntity();
entity.setTargetType(record.getTargetType());
entity.setTargetId(record.getTargetId());
entity.setViolationType(record.getViolationType());
entity.setOperationType(record.getOperationType());
entity.setDescription(record.getDescription());
entity.setScreenshotUrls(record.getScreenshotUrls() != null ?
JSON.toJSONString(record.getScreenshotUrls()) : null);
entity.setOriginalContent(record.getOriginalContent());
entity.setAdjustedContent(record.getAdjustedContent());
entity.setOperatorId(record.getOperatorId());
entity.setOperatorName(record.getOperatorName());
entity.setStatus(1);
return entity;
}
/**
* 实体转领域对象
*/
private ViolationRecord toDomain(ViolationRecordEntity entity) {
return ViolationRecord.builder()
.id(entity.getId())
.bizNo(entity.getBizNo())
.targetType(entity.getTargetType())
.targetId(entity.getTargetId())
.violationType(entity.getViolationType())
.operationType(entity.getOperationType())
.description(entity.getDescription())
.screenshotUrls(entity.getScreenshotUrls())
.originalContent(entity.getOriginalContent())
.adjustedContent(entity.getAdjustedContent())
.operatorId(entity.getOperatorId())
.operatorName(entity.getOperatorName())
.createTime(entity.getCreateTime())
.build();
}
}