新增批量封禁功能
This commit is contained in:
parent
3f72f33445
commit
fa93075a8c
@ -22,6 +22,18 @@
|
||||
<groupId>com.red.circle</groupId>
|
||||
<artifactId>component-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Apache POI for Excel processing -->
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi</artifactId>
|
||||
<version>5.2.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>5.2.3</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@ -1,26 +1,461 @@
|
||||
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);
|
||||
@ -30,4 +465,34 @@ public class UserDataViolationTest {
|
||||
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)
|
||||
;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,45 @@
|
||||
package dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 封禁结果数据传输对象
|
||||
*/
|
||||
@Data
|
||||
public class BanResultDTO {
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 设备指纹
|
||||
*/
|
||||
private String deviceFingerprint;
|
||||
|
||||
/**
|
||||
* 用户身份
|
||||
*/
|
||||
private String userIdentity;
|
||||
|
||||
/**
|
||||
* 是否封禁
|
||||
*/
|
||||
private String banned;
|
||||
|
||||
/**
|
||||
* 封禁原因/不封禁原因
|
||||
*/
|
||||
private String reason;
|
||||
|
||||
/**
|
||||
* 处理状态
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
package dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 设备指纹数据传输对象
|
||||
* 对应 SQL 查询结果的结构
|
||||
*/
|
||||
@Data
|
||||
public class DeviceFingerprintDTO {
|
||||
|
||||
/**
|
||||
* 设备指纹(请求平台|手机型号|IP)
|
||||
*/
|
||||
private String deviceFingerprint;
|
||||
|
||||
/**
|
||||
* 请求平台
|
||||
*/
|
||||
private String requestClient;
|
||||
|
||||
/**
|
||||
* 手机型号
|
||||
*/
|
||||
private String phoneModel;
|
||||
|
||||
/**
|
||||
* IP地址
|
||||
*/
|
||||
private String ipAddress;
|
||||
|
||||
/**
|
||||
* 共同设备指纹用户数
|
||||
*/
|
||||
private Integer userCount;
|
||||
|
||||
/**
|
||||
* 共同设备指纹用户ID(逗号分隔)
|
||||
*/
|
||||
private String userIds;
|
||||
|
||||
/**
|
||||
* 总抽奖次数
|
||||
*/
|
||||
private Integer totalDrawCount;
|
||||
}
|
||||
@ -0,0 +1,193 @@
|
||||
package util;
|
||||
|
||||
import dto.BanResultDTO;
|
||||
import dto.DeviceFingerprintDTO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.poi.ss.usermodel.*;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Excel 工具类
|
||||
* 用于导入设备指纹数据和导出封禁结果
|
||||
*/
|
||||
@Slf4j
|
||||
public class ExcelUtil {
|
||||
|
||||
/**
|
||||
* 导入设备指纹数据
|
||||
*
|
||||
* @param filePath Excel 文件路径
|
||||
* @return 设备指纹数据列表
|
||||
*/
|
||||
public static List<DeviceFingerprintDTO> importDeviceFingerprint(String filePath) {
|
||||
List<DeviceFingerprintDTO> result = new ArrayList<>();
|
||||
|
||||
try (FileInputStream fis = new FileInputStream(filePath);
|
||||
Workbook workbook = new XSSFWorkbook(fis)) {
|
||||
|
||||
Sheet sheet = workbook.getSheetAt(0);
|
||||
|
||||
// 跳过表头,从第二行开始读取
|
||||
for (int i = 1; i <= sheet.getLastRowNum(); i++) {
|
||||
Row row = sheet.getRow(i);
|
||||
if (row == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DeviceFingerprintDTO dto = new DeviceFingerprintDTO();
|
||||
dto.setDeviceFingerprint(getCellValueAsString(row.getCell(0)));
|
||||
dto.setRequestClient(getCellValueAsString(row.getCell(1)));
|
||||
dto.setPhoneModel(getCellValueAsString(row.getCell(2)));
|
||||
dto.setIpAddress(getCellValueAsString(row.getCell(3)));
|
||||
dto.setUserCount(getCellValueAsInteger(row.getCell(4)));
|
||||
dto.setUserIds(getCellValueAsString(row.getCell(5)));
|
||||
dto.setTotalDrawCount(getCellValueAsInteger(row.getCell(6)));
|
||||
|
||||
result.add(dto);
|
||||
}
|
||||
|
||||
log.info("成功导入 {} 条设备指纹数据", result.size());
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("导入 Excel 文件失败: {}", filePath, e);
|
||||
throw new RuntimeException("导入 Excel 文件失败", e);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出封禁结果
|
||||
*
|
||||
* @param results 封禁结果列表
|
||||
* @param filePath 导出文件路径
|
||||
*/
|
||||
public static void exportBanResults(List<BanResultDTO> results, String filePath) {
|
||||
try (Workbook workbook = new XSSFWorkbook()) {
|
||||
Sheet sheet = workbook.createSheet("封禁结果");
|
||||
|
||||
// 创建表头样式
|
||||
CellStyle headerStyle = createHeaderStyle(workbook);
|
||||
|
||||
// 创建表头
|
||||
Row headerRow = sheet.createRow(0);
|
||||
String[] headers = {"用户ID", "设备指纹", "用户身份", "是否封禁", "原因", "处理状态", "备注"};
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
Cell cell = headerRow.createCell(i);
|
||||
cell.setCellValue(headers[i]);
|
||||
cell.setCellStyle(headerStyle);
|
||||
}
|
||||
|
||||
// 创建数据行
|
||||
for (int i = 0; i < results.size(); i++) {
|
||||
Row row = sheet.createRow(i + 1);
|
||||
BanResultDTO dto = results.get(i);
|
||||
|
||||
row.createCell(0).setCellValue(dto.getUserId() != null ? dto.getUserId().toString() : "");
|
||||
row.createCell(1).setCellValue(dto.getDeviceFingerprint() != null ? dto.getDeviceFingerprint() : "");
|
||||
row.createCell(2).setCellValue(dto.getUserIdentity() != null ? dto.getUserIdentity() : "");
|
||||
row.createCell(3).setCellValue(dto.getBanned() != null ? dto.getBanned() : "");
|
||||
row.createCell(4).setCellValue(dto.getReason() != null ? dto.getReason() : "");
|
||||
row.createCell(5).setCellValue(dto.getStatus() != null ? dto.getStatus() : "");
|
||||
row.createCell(6).setCellValue(dto.getRemark() != null ? dto.getRemark() : "");
|
||||
}
|
||||
|
||||
// 自动调整列宽
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
sheet.autoSizeColumn(i);
|
||||
}
|
||||
|
||||
// 写入文件
|
||||
try (FileOutputStream fos = new FileOutputStream(filePath)) {
|
||||
workbook.write(fos);
|
||||
}
|
||||
|
||||
log.info("成功导出 {} 条封禁结果到: {}", results.size(), filePath);
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("导出 Excel 文件失败: {}", filePath, e);
|
||||
throw new RuntimeException("导出 Excel 文件失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建表头样式
|
||||
*/
|
||||
private static CellStyle createHeaderStyle(Workbook workbook) {
|
||||
CellStyle style = workbook.createCellStyle();
|
||||
|
||||
// 设置背景色
|
||||
style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
|
||||
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
||||
|
||||
// 设置边框
|
||||
style.setBorderBottom(BorderStyle.THIN);
|
||||
style.setBorderLeft(BorderStyle.THIN);
|
||||
style.setBorderRight(BorderStyle.THIN);
|
||||
style.setBorderTop(BorderStyle.THIN);
|
||||
|
||||
// 设置字体
|
||||
Font font = workbook.createFont();
|
||||
font.setBold(true);
|
||||
style.setFont(font);
|
||||
|
||||
// 设置居中
|
||||
style.setAlignment(HorizontalAlignment.CENTER);
|
||||
style.setVerticalAlignment(VerticalAlignment.CENTER);
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单元格字符串值
|
||||
*/
|
||||
private static String getCellValueAsString(Cell cell) {
|
||||
if (cell == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
switch (cell.getCellType()) {
|
||||
case STRING:
|
||||
return cell.getStringCellValue().trim();
|
||||
case NUMERIC:
|
||||
if (DateUtil.isCellDateFormatted(cell)) {
|
||||
return cell.getDateCellValue().toString();
|
||||
}
|
||||
return String.valueOf((long) cell.getNumericCellValue());
|
||||
case BOOLEAN:
|
||||
return String.valueOf(cell.getBooleanCellValue());
|
||||
case FORMULA:
|
||||
return cell.getCellFormula();
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单元格整数值
|
||||
*/
|
||||
private static Integer getCellValueAsInteger(Cell cell) {
|
||||
if (cell == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (cell.getCellType()) {
|
||||
case NUMERIC:
|
||||
return (int) cell.getNumericCellValue();
|
||||
case STRING:
|
||||
try {
|
||||
return Integer.parseInt(cell.getStringCellValue().trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user