diff --git a/rc-service/rc-service-other/other-start/pom.xml b/rc-service/rc-service-other/other-start/pom.xml
index eb8605f3..d667949a 100644
--- a/rc-service/rc-service-other/other-start/pom.xml
+++ b/rc-service/rc-service-other/other-start/pom.xml
@@ -22,6 +22,18 @@
com.red.circle
component-redis
+
+
+
+ org.apache.poi
+ poi
+ 5.2.3
+
+
+ org.apache.poi
+ poi-ooxml
+ 5.2.3
+
diff --git a/rc-service/rc-service-other/other-start/src/test/java/UserDataViolationTest.java b/rc-service/rc-service-other/other-start/src/test/java/UserDataViolationTest.java
index 21693d56..71eab2b4 100644
--- a/rc-service/rc-service-other/other-start/src/test/java/UserDataViolationTest.java
+++ b/rc-service/rc-service-other/other-start/src/test/java/UserDataViolationTest.java
@@ -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 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 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 deviceData = ExcelUtil.importDeviceFingerprint(inputFilePath);
+
+ for (DeviceFingerprintDTO device : deviceData) {
+ List 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 deviceData = ExcelUtil.importDeviceFingerprint(inputFilePath);
+ log.info("成功导入 {} 条设备指纹数据", deviceData.size());
+
+ // 步骤2: 处理每个设备指纹的用户
+ log.info("步骤2: 开始处理用户封禁逻辑...");
+ List allResults = new ArrayList<>();
+
+ int processedCount = 0;
+ for (DeviceFingerprintDTO device : deviceData) {
+ processedCount++;
+ log.info("处理进度: {}/{} - 设备指纹: {}", processedCount, deviceData.size(), device.getDeviceFingerprint());
+
+ // 解析用户ID列表
+ List userIds = parseUserIds(device.getUserIds());
+ log.info(" 该设备指纹下有 {} 个用户", userIds.size());
+
+ // 判断该设备指纹下的主播数量
+ List anchorUserIds = new ArrayList<>();
+ Map 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 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 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)
+ ;
+ }
+
}
diff --git a/rc-service/rc-service-other/other-start/src/test/java/dto/BanResultDTO.java b/rc-service/rc-service-other/other-start/src/test/java/dto/BanResultDTO.java
new file mode 100644
index 00000000..e6ab2b3d
--- /dev/null
+++ b/rc-service/rc-service-other/other-start/src/test/java/dto/BanResultDTO.java
@@ -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;
+}
\ No newline at end of file
diff --git a/rc-service/rc-service-other/other-start/src/test/java/dto/DeviceFingerprintDTO.java b/rc-service/rc-service-other/other-start/src/test/java/dto/DeviceFingerprintDTO.java
new file mode 100644
index 00000000..dd6f06c9
--- /dev/null
+++ b/rc-service/rc-service-other/other-start/src/test/java/dto/DeviceFingerprintDTO.java
@@ -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;
+}
\ No newline at end of file
diff --git a/rc-service/rc-service-other/other-start/src/test/java/util/ExcelUtil.java b/rc-service/rc-service-other/other-start/src/test/java/util/ExcelUtil.java
new file mode 100644
index 00000000..914764ea
--- /dev/null
+++ b/rc-service/rc-service-other/other-start/src/test/java/util/ExcelUtil.java
@@ -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 importDeviceFingerprint(String filePath) {
+ List 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 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;
+ }
+ }
+}
\ No newline at end of file