499 lines
18 KiB
Java
499 lines
18 KiB
Java
import com.red.circle.OtherServiceApplication;
|
||
import com.red.circle.common.business.enums.ConsoleAccountStatusEnum;
|
||
import com.red.circle.framework.core.asserts.ResponseAssert;
|
||
import com.red.circle.other.app.dto.h5.UserIdentityVO;
|
||
import com.red.circle.other.app.service.user.user.AppUserDataViolationService;
|
||
import com.red.circle.other.infra.database.rds.entity.sys.Administrator;
|
||
import com.red.circle.other.infra.database.rds.service.sys.AdministratorAuthService;
|
||
import com.red.circle.other.infra.database.rds.service.sys.AdministratorService;
|
||
import com.red.circle.other.inner.endpoint.team.bd.BdTeamInfoClient;
|
||
import com.red.circle.other.inner.endpoint.team.bd.BdTeamLeaderClient;
|
||
import com.red.circle.other.inner.endpoint.team.target.TeamProfileClient;
|
||
import com.red.circle.other.inner.enums.sys.appmanager.RoomRolesEnum;
|
||
import com.red.circle.other.inner.enums.team.TeamMemberRole;
|
||
import com.red.circle.other.inner.model.cmd.user.ApprovalAccountCmd;
|
||
import com.red.circle.other.inner.model.dto.agency.agency.TeamMemberDTO;
|
||
import com.red.circle.wallet.inner.endpoint.freight.FreightGoldClient;
|
||
import dto.BanResultDTO;
|
||
import dto.DeviceFingerprintDTO;
|
||
import lombok.extern.slf4j.Slf4j;
|
||
import org.junit.Test;
|
||
import org.junit.runner.RunWith;
|
||
import org.springframework.beans.factory.annotation.Autowired;
|
||
import org.springframework.boot.test.context.SpringBootTest;
|
||
import org.springframework.test.context.junit4.SpringRunner;
|
||
import util.ExcelUtil;
|
||
|
||
import java.util.*;
|
||
import java.util.stream.Collectors;
|
||
|
||
@Slf4j
|
||
@SpringBootTest(classes = OtherServiceApplication.class)
|
||
@RunWith(SpringRunner.class)
|
||
public class UserDataViolationTest {
|
||
|
||
@Autowired
|
||
private AppUserDataViolationService userDataViolationService;
|
||
@Autowired
|
||
private TeamProfileClient teamProfileClient;
|
||
@Autowired
|
||
private FreightGoldClient freightGoldClient;
|
||
@Autowired
|
||
private BdTeamInfoClient bdTeamInfoClient;
|
||
@Autowired
|
||
private AdministratorService administratorService;
|
||
@Autowired
|
||
private AdministratorAuthService administratorAuthService;
|
||
@Autowired
|
||
private BdTeamLeaderClient bdTeamLeaderClient;
|
||
|
||
/**
|
||
* 测试 Excel 导入功能
|
||
* 读取从数据库导出的设备指纹数据
|
||
*/
|
||
@Test
|
||
public void testImportDeviceFingerprint() {
|
||
// TODO: 修改为你实际的 Excel 文件路径
|
||
String inputFilePath = "C:\\Users\\Administrator\\Documents\\DataGripSql\\11.11同设备指纹用户统计英文版.xlsx";
|
||
|
||
try {
|
||
List<DeviceFingerprintDTO> dataList = ExcelUtil.importDeviceFingerprint(inputFilePath);
|
||
|
||
log.info("==================== 导入测试结果 ====================");
|
||
log.info("总共导入 {} 条设备指纹数据", dataList.size());
|
||
|
||
// 打印前 5 条数据作为示例
|
||
int displayCount = Math.min(5, dataList.size());
|
||
log.info("显示前 {} 条数据:", displayCount);
|
||
|
||
for (int i = 0; i < displayCount; i++) {
|
||
DeviceFingerprintDTO dto = dataList.get(i);
|
||
log.info("数据 {}: 设备指纹={}, 用户数={}, 用户IDs={}",
|
||
i + 1,
|
||
dto.getDeviceFingerprint(),
|
||
dto.getUserCount(),
|
||
dto.getUserIds());
|
||
}
|
||
|
||
log.info("==================================================");
|
||
|
||
} catch (Exception e) {
|
||
log.error("导入测试失败", e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 测试 Excel 导出功能
|
||
* 生成示例封禁结果数据并导出
|
||
*/
|
||
@Test
|
||
public void testExportBanResults() {
|
||
// 创建示例数据
|
||
List<BanResultDTO> results = new ArrayList<>();
|
||
|
||
// 示例1:被封禁的用户
|
||
BanResultDTO banned = new BanResultDTO();
|
||
banned.setUserId(1954059793963393025L);
|
||
banned.setDeviceFingerprint("iOS|iPhone 14|192.168.1.100");
|
||
banned.setUserIdentity("主播");
|
||
banned.setBanned("是");
|
||
banned.setReason("同设备指纹下存在多个主播");
|
||
banned.setStatus("封禁成功");
|
||
banned.setRemark("设备指纹下共2个主播");
|
||
results.add(banned);
|
||
|
||
// 示例2:不封禁的用户(管理员)
|
||
BanResultDTO notBanned1 = new BanResultDTO();
|
||
notBanned1.setUserId(1000000000000000001L);
|
||
notBanned1.setDeviceFingerprint("Android|Xiaomi 13|192.168.1.101");
|
||
notBanned1.setUserIdentity("超级管理员");
|
||
notBanned1.setBanned("否");
|
||
notBanned1.setReason("管理员身份,不封禁");
|
||
notBanned1.setStatus("跳过");
|
||
notBanned1.setRemark("-");
|
||
results.add(notBanned1);
|
||
|
||
// 示例3:不封禁的用户(非主播)
|
||
BanResultDTO notBanned2 = new BanResultDTO();
|
||
notBanned2.setUserId(2000000000000000001L);
|
||
notBanned2.setDeviceFingerprint("iOS|iPhone 15|192.168.1.102");
|
||
notBanned2.setUserIdentity("普通用户");
|
||
notBanned2.setBanned("否");
|
||
notBanned2.setReason("非主播身份,不封禁");
|
||
notBanned2.setStatus("跳过");
|
||
notBanned2.setRemark("-");
|
||
results.add(notBanned2);
|
||
|
||
// TODO: 修改为你想要保存的文件路径
|
||
String outputFilePath = "D:\\ban_results_test.xlsx";
|
||
|
||
try {
|
||
ExcelUtil.exportBanResults(results, outputFilePath);
|
||
|
||
log.info("==================== 导出测试结果 ====================");
|
||
log.info("成功导出 {} 条封禁结果数据", results.size());
|
||
log.info("导出文件路径: {}", outputFilePath);
|
||
log.info("请打开 Excel 文件检查导出结果");
|
||
log.info("==================================================");
|
||
|
||
} catch (Exception e) {
|
||
log.error("导出测试失败", e);
|
||
}
|
||
}
|
||
|
||
@Test
|
||
public void testUnBanUsers() {
|
||
String inputFilePath = "C:\\Users\\Administrator\\Documents\\DataGripSql\\11.11同设备指纹用户统计英文版 - 副本.xlsx";
|
||
String outputFilePath = "D:\\ban_results_" + System.currentTimeMillis() + ".xlsx";
|
||
List<DeviceFingerprintDTO> deviceData = ExcelUtil.importDeviceFingerprint(inputFilePath);
|
||
|
||
for (DeviceFingerprintDTO device : deviceData) {
|
||
List<Long> userIds = parseUserIds(device.getUserIds());
|
||
for (Long userId : userIds) {
|
||
|
||
if (userId == 1963790037740466178L) {
|
||
System.out.println();
|
||
}
|
||
// 执行解封
|
||
ApprovalAccountCmd cmd = new ApprovalAccountCmd();
|
||
cmd.setAccountStatusEnum(ConsoleAccountStatusEnum.UNTIE_DEVICE_AND_ACCOUNT);
|
||
cmd.setDescription("1");
|
||
cmd.setBeApprovalUserId(userId);
|
||
cmd.setReqUserId(userId);
|
||
|
||
userDataViolationService.updateAccountStatus(cmd);
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
/**
|
||
* 批量用户封禁(完整流程)
|
||
* 1. 导入设备指纹数据
|
||
* 2. 判断用户身份
|
||
* 3. 执行封禁操作
|
||
* 4. 导出封禁结果
|
||
*/
|
||
@Test
|
||
public void batchBanUsers() {
|
||
// TODO: 修改为你实际的文件路径
|
||
String inputFilePath = "C:\\Users\\Administrator\\Documents\\DataGripSql\\11.11同设备指纹用户统计英文版.xlsx";
|
||
String outputFilePath = "D:\\ban_results_" + System.currentTimeMillis() + ".xlsx";
|
||
|
||
try {
|
||
log.info("==================== 开始批量封禁流程 ====================");
|
||
|
||
// 步骤1: 导入设备指纹数据
|
||
log.info("步骤1: 导入设备指纹数据...");
|
||
List<DeviceFingerprintDTO> deviceData = ExcelUtil.importDeviceFingerprint(inputFilePath);
|
||
log.info("成功导入 {} 条设备指纹数据", deviceData.size());
|
||
|
||
// 步骤2: 处理每个设备指纹的用户
|
||
log.info("步骤2: 开始处理用户封禁逻辑...");
|
||
List<BanResultDTO> allResults = new ArrayList<>();
|
||
|
||
int processedCount = 0;
|
||
for (DeviceFingerprintDTO device : deviceData) {
|
||
processedCount++;
|
||
log.info("处理进度: {}/{} - 设备指纹: {}", processedCount, deviceData.size(), device.getDeviceFingerprint());
|
||
|
||
// 解析用户ID列表
|
||
List<Long> userIds = parseUserIds(device.getUserIds());
|
||
log.info(" 该设备指纹下有 {} 个用户", userIds.size());
|
||
|
||
// 判断该设备指纹下的主播数量
|
||
List<Long> anchorUserIds = new ArrayList<>();
|
||
Map<Long, UserIdentityVO> identityMap = new HashMap<>();
|
||
|
||
for (Long userId : userIds) {
|
||
try {
|
||
UserIdentityVO identity = getUserIdentity(userId);
|
||
identityMap.put(userId, identity);
|
||
|
||
// 如果是主播,加入主播列表
|
||
if (Boolean.TRUE.equals(identity.getAnchor())) {
|
||
anchorUserIds.add(userId);
|
||
}
|
||
} catch (Exception e) {
|
||
log.error(" 获取用户 {} 身份失败", userId, e);
|
||
}
|
||
}
|
||
|
||
log.info(" 该设备指纹下有 {} 个主播", anchorUserIds.size());
|
||
|
||
// 处理每个用户
|
||
for (Long userId : userIds) {
|
||
BanResultDTO result = processUser(userId, device, identityMap.get(userId), anchorUserIds.size());
|
||
allResults.add(result);
|
||
}
|
||
}
|
||
|
||
// 步骤3: 导出结果
|
||
log.info("步骤3: 导出封禁结果...");
|
||
ExcelUtil.exportBanResults(allResults, outputFilePath);
|
||
|
||
// 步骤4: 统计结果
|
||
log.info("==================== 封禁流程完成 ====================");
|
||
long bannedCount = allResults.stream().filter(r -> "是".equals(r.getBanned())).count();
|
||
long skippedCount = allResults.stream().filter(r -> "否".equals(r.getBanned())).count();
|
||
long successCount = allResults.stream().filter(r -> "封禁成功".equals(r.getStatus())).count();
|
||
long failedCount = allResults.stream().filter(r -> "封禁失败".equals(r.getStatus())).count();
|
||
|
||
log.info("总处理用户数: {}", allResults.size());
|
||
log.info("封禁用户数: {}", bannedCount);
|
||
log.info("跳过用户数: {}", skippedCount);
|
||
log.info("封禁成功数: {}", successCount);
|
||
log.info("封禁失败数: {}", failedCount);
|
||
log.info("结果文件路径: {}", outputFilePath);
|
||
log.info("====================================================");
|
||
|
||
} catch (Exception e) {
|
||
log.error("批量封禁流程失败", e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理单个用户
|
||
*/
|
||
private BanResultDTO processUser(Long userId, DeviceFingerprintDTO device,
|
||
UserIdentityVO identity, int anchorCount) {
|
||
BanResultDTO result = new BanResultDTO();
|
||
result.setUserId(userId);
|
||
result.setDeviceFingerprint(device.getDeviceFingerprint());
|
||
|
||
// 如果获取身份失败
|
||
if (identity == null) {
|
||
result.setUserIdentity("未知");
|
||
result.setBanned("否");
|
||
result.setReason("获取用户身份失败");
|
||
result.setStatus("跳过");
|
||
result.setRemark("身份查询异常");
|
||
return result;
|
||
}
|
||
|
||
// 设置用户身份描述
|
||
result.setUserIdentity(buildIdentityDescription(identity));
|
||
|
||
// 判断是否需要封禁
|
||
boolean shouldBan = shouldBanUser(identity, anchorCount);
|
||
|
||
if (!shouldBan) {
|
||
result.setBanned("否");
|
||
result.setReason(getSkipReason(identity, anchorCount));
|
||
result.setStatus("跳过");
|
||
result.setRemark(String.format("设备指纹下共%d个用户,%d个主播", device.getUserCount(), anchorCount));
|
||
return result;
|
||
}
|
||
|
||
// 需要封禁,执行封禁操作
|
||
result.setBanned("是");
|
||
result.setReason(String.format("设备指纹下存在%d个主播,符合封禁条件", anchorCount));
|
||
result.setRemark(String.format("设备指纹下共%d个用户,%d个主播", device.getUserCount(), anchorCount));
|
||
|
||
try {
|
||
// 执行封禁
|
||
ApprovalAccountCmd cmd = new ApprovalAccountCmd();
|
||
cmd.setAccountStatusEnum(ConsoleAccountStatusEnum.ARCHIVE);
|
||
cmd.setDescription("Multi-device fingerprint blocking");
|
||
cmd.setBeApprovalUserId(userId);
|
||
cmd.setReqUserId(userId);
|
||
cmd.setDays(365);
|
||
|
||
userDataViolationService.updateAccountStatus(cmd);
|
||
|
||
result.setStatus("封禁成功");
|
||
log.info(" 用户 {} 封禁成功", userId);
|
||
|
||
} catch (Exception e) {
|
||
result.setStatus("封禁失败");
|
||
result.setRemark(result.getRemark() + " | 错误: " + e.getMessage());
|
||
log.error(" 用户 {} 封禁失败", userId, e);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 判断是否应该封禁用户
|
||
* 封禁条件:同设备指纹下存在2个及以上主播,且该用户是主播
|
||
* 不封禁条件:
|
||
* 1. 超级管理员
|
||
* 2. 管理员
|
||
* 3. BD Leader
|
||
* 4. BD
|
||
* 5. 金币代理(FreightAgent)
|
||
* 6. 不是主播
|
||
*/
|
||
private boolean shouldBanUser(UserIdentityVO identity, int anchorCount) {
|
||
// 条件1: 设备指纹下必须有2个及以上主播
|
||
if (anchorCount < 2) {
|
||
return false;
|
||
}
|
||
|
||
// 条件2: 该用户必须是主播
|
||
if (!Boolean.TRUE.equals(identity.getAnchor())) {
|
||
return false;
|
||
}
|
||
|
||
// 条件3: 排除特殊身份用户
|
||
if (Boolean.TRUE.equals(identity.getSuperAdmin())) {
|
||
return false; // 超级管理员不封禁
|
||
}
|
||
|
||
if (Boolean.TRUE.equals(identity.getAdmin())) {
|
||
return false; // 管理员不封禁
|
||
}
|
||
|
||
if (Boolean.TRUE.equals(identity.getBdLeader())) {
|
||
return false; // BD Leader 不封禁
|
||
}
|
||
|
||
if (Boolean.TRUE.equals(identity.getBd())) {
|
||
return false; // BD 不封禁
|
||
}
|
||
|
||
if (Boolean.TRUE.equals(identity.getFreightAgent()) || Boolean.TRUE.equals(identity.getSuperFreightAgent())) {
|
||
return false; // 金币代理不封禁
|
||
}
|
||
|
||
// 通过所有检查,应该封禁
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 获取跳过原因
|
||
*/
|
||
private String getSkipReason(UserIdentityVO identity, int anchorCount) {
|
||
if (anchorCount < 2) {
|
||
return "设备指纹下主播数量不足2个";
|
||
}
|
||
|
||
if (!Boolean.TRUE.equals(identity.getAnchor())) {
|
||
return "用户不是主播";
|
||
}
|
||
|
||
if (Boolean.TRUE.equals(identity.getSuperAdmin())) {
|
||
return "超级管理员身份,不封禁";
|
||
}
|
||
|
||
if (Boolean.TRUE.equals(identity.getAdmin())) {
|
||
return "管理员身份,不封禁";
|
||
}
|
||
|
||
if (Boolean.TRUE.equals(identity.getBdLeader())) {
|
||
return "BD Leader 身份,不封禁";
|
||
}
|
||
|
||
if (Boolean.TRUE.equals(identity.getBd())) {
|
||
return "BD 身份,不封禁";
|
||
}
|
||
|
||
if (Boolean.TRUE.equals(identity.getFreightAgent()) || Boolean.TRUE.equals(identity.getSuperFreightAgent())) {
|
||
return "金币代理身份,不封禁";
|
||
}
|
||
|
||
return "其他原因";
|
||
}
|
||
|
||
/**
|
||
* 构建用户身份描述
|
||
*/
|
||
private String buildIdentityDescription(UserIdentityVO identity) {
|
||
List<String> identities = new ArrayList<>();
|
||
|
||
if (Boolean.TRUE.equals(identity.getSuperAdmin())) {
|
||
identities.add("超级管理员");
|
||
}
|
||
if (Boolean.TRUE.equals(identity.getAdmin())) {
|
||
identities.add("管理员");
|
||
}
|
||
if (Boolean.TRUE.equals(identity.getManager())) {
|
||
identities.add("经理");
|
||
}
|
||
if (Boolean.TRUE.equals(identity.getBdLeader())) {
|
||
identities.add("BD Leader");
|
||
}
|
||
if (Boolean.TRUE.equals(identity.getBd())) {
|
||
identities.add("BD");
|
||
}
|
||
if (Boolean.TRUE.equals(identity.getAgent())) {
|
||
identities.add("代理");
|
||
}
|
||
if (Boolean.TRUE.equals(identity.getAnchor())) {
|
||
identities.add("主播");
|
||
}
|
||
if (Boolean.TRUE.equals(identity.getSuperFreightAgent())) {
|
||
identities.add("超级金币代理");
|
||
}
|
||
if (Boolean.TRUE.equals(identity.getFreightAgent())) {
|
||
identities.add("金币代理");
|
||
}
|
||
|
||
if (identities.isEmpty()) {
|
||
return "普通用户";
|
||
}
|
||
|
||
return String.join(", ", identities);
|
||
}
|
||
|
||
/**
|
||
* 解析用户ID字符串(逗号分隔)
|
||
*/
|
||
private List<Long> parseUserIds(String userIdsStr) {
|
||
if (userIdsStr == null || userIdsStr.trim().isEmpty()) {
|
||
return new ArrayList<>();
|
||
}
|
||
|
||
return Arrays.stream(userIdsStr.split(","))
|
||
.map(String::trim)
|
||
.filter(s -> !s.isEmpty())
|
||
.map(Long::parseLong)
|
||
.collect(Collectors.toList());
|
||
}
|
||
|
||
/**
|
||
* 单个用户封禁测试
|
||
*/
|
||
@Test
|
||
public void updateUserDataViolation() {
|
||
Long userId = 1954059793963393025L;
|
||
ApprovalAccountCmd cmd = new ApprovalAccountCmd();
|
||
cmd.setAccountStatusEnum(ConsoleAccountStatusEnum.ARCHIVE_DEVICE);
|
||
cmd.setDescription("Multi-device fingerprint blocking");
|
||
cmd.setBeApprovalUserId(userId);
|
||
cmd.setReqUserId(userId);
|
||
userDataViolationService.updateAccountStatus(cmd);
|
||
}
|
||
|
||
/**
|
||
* 获取用户身份信息
|
||
*/
|
||
public UserIdentityVO getUserIdentity(Long userId) {
|
||
TeamMemberDTO teamMember = ResponseAssert.requiredSuccess(
|
||
teamProfileClient.getByMemberId(userId));
|
||
|
||
Administrator administrator = administratorService.getByUserId(userId);
|
||
boolean isValidAdmin = administrator != null && Boolean.TRUE.equals(administrator.getStatus());
|
||
|
||
boolean isSuperAdmin = isValidAdmin && Optional.of(administrator)
|
||
.map(e -> RoomRolesEnum.MANAGER.eq(e.getRoles()) || RoomRolesEnum.SUPER_ADMIN.eq(e.getRoles()))
|
||
.orElse(Boolean.FALSE);
|
||
boolean isManager = isValidAdmin && Optional.of(administrator)
|
||
.map(e -> RoomRolesEnum.MANAGER.eq(e.getRoles()))
|
||
.orElse(Boolean.FALSE);
|
||
|
||
return new UserIdentityVO()
|
||
.setAgent(Objects.nonNull(teamMember) && TeamMemberRole.OWN.eq(teamMember.getRole()))
|
||
.setAnchor(Objects.nonNull(teamMember))
|
||
.setBd(ResponseAssert.requiredSuccess(bdTeamInfoClient.check(userId)))
|
||
.setFreightAgent(ResponseAssert.requiredSuccess(freightGoldClient.checkFreightAgent(userId)))
|
||
.setSuperFreightAgent(ResponseAssert.requiredSuccess(freightGoldClient.checkSuperFreightAgent(userId)))
|
||
.setSuperAdmin(isSuperAdmin)
|
||
.setAdmin(isValidAdmin)
|
||
.setBdLeader(ResponseAssert.requiredSuccess(bdTeamLeaderClient.check(userId)))
|
||
.setManager(isManager)
|
||
;
|
||
}
|
||
|
||
}
|