package com.xiaopeng.operation.controller; import cn.afterturn.easypoi.excel.ExcelExportUtil; import cn.afterturn.easypoi.excel.ExcelImportUtil; import cn.afterturn.easypoi.excel.entity.ExportParams; import cn.afterturn.easypoi.excel.entity.ImportParams; import cn.hutool.core.util.PhoneUtil; import com.xiaopeng.api.model.account.SysUser; import com.xiaopeng.api.model.operation.XpWhitelist; import com.xiaopeng.api.model.operation.XpWhitelistOperlog; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.exception.ServiceException; import com.xiaopeng.common.core.utils.WebUtils; import com.xiaopeng.common.core.utils.bean.BeanUtils; import com.xiaopeng.common.core.utils.ip.IpUtils; import com.xiaopeng.common.core.web.domain.AjaxResult; import com.xiaopeng.common.core.web.page.PageInfo; import com.xiaopeng.common.log.annotation.Log; import com.xiaopeng.common.log.enums.BusinessType; import com.xiaopeng.common.security.service.TokenService; import com.xiaopeng.operation.dto.WhitelistAddDto; import com.xiaopeng.operation.dto.WhitelistEditDto; import com.xiaopeng.operation.exportExcle.XpWhitelistExportExcel; import com.xiaopeng.operation.importExcel.WhitelistImportExcel; import com.xiaopeng.operation.query.WhitelistPageListQuery; import com.xiaopeng.operation.service.IXpWhitelistOperlogService; import com.xiaopeng.operation.service.IXpWhitelistService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.apache.poi.ss.usermodel.Workbook; import org.apache.shardingsphere.transaction.annotation.ShardingSphereTransactionType; import org.apache.shardingsphere.transaction.core.TransactionType; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.net.URLEncoder; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; /** *

* 白名单管理 前端控制器 *

* * @author mitao * @since 2026-02-03 */ @RequestMapping("") @RestController @Api(tags = "白名单管理") @Slf4j public class XpWhitelistController { @Resource private IXpWhitelistService xpWhitelistService; @Resource private IXpWhitelistOperlogService xpWhitelistOperlogService; @Resource private TokenService tokenService; /** * 获取所有白名单手机号 * @return */ @PostMapping("/logan/api/v1/whitelist/inner/getAllWhiteList") public R> getAllWhiteList(){ return R.ok(xpWhitelistService.getAllWhiteList()); } @ApiOperation(value = "白名单管理详情", tags = {"管理后台-白名单管理详情"}) @GetMapping("/api/v1/whitelist/detail/{id}") public R detail(@PathVariable("id") Long id) { return R.ok(xpWhitelistService.getById(id)); } @ApiOperation(value = "白名单管理分页列表", response = XpWhitelist.class) @PostMapping("/api/v1/whitelist/pageList") public R> pageList(@RequestBody WhitelistPageListQuery query) { PageInfo pageList = xpWhitelistService.pageList(query); return R.ok(pageList); } /** * 新增白名单 */ @PostMapping("/api/v1/whitelist/add") @ResponseBody @ApiOperation(value = "新增白名单") public R add(@Validated @RequestBody WhitelistAddDto dto) { if (!StringUtils.hasLength(dto.getAccount())) { return R.fail("请输入账号"); } SysUser sysUser = tokenService.getLoginUser().getSysUser(); return xpWhitelistService.addWhitelist(dto, sysUser); } /** * 编辑白名单 */ @PostMapping("/api/v1/whitelist/edit") @ResponseBody @ApiOperation(value = "编辑白名单") @Transactional public R edit(@Validated @RequestBody WhitelistEditDto dto) { if (!StringUtils.hasLength(dto.getAccount())) { return R.fail("请输入账号"); } SysUser sysUser = tokenService.getLoginUser().getSysUser(); if (dto.getExpireTime() != null && dto.getExpireTime().isBefore(LocalDate.now())) { return R.fail("有效期不能早于当前日期"); } XpWhitelist xpWhitelist = new XpWhitelist(); BeanUtils.copyProperties(dto, xpWhitelist); xpWhitelist.setUpdateBy(tokenService.getLoginUser().getSysUser().getNickName()); return xpWhitelistService.editWhitelist(xpWhitelist, sysUser); } /** * 冻结解冻 */ @GetMapping("/api/v1/whitelist/freezeUnfreeze") @ResponseBody @ApiOperation(value = "冻结解冻") @Transactional public R freezeUnfreeze(@RequestParam("id") Long id) { SysUser sysUser = tokenService.getLoginUser().getSysUser(); return xpWhitelistService.freezeUnfreeze(id, sysUser); } /** * 批量删除 */ @GetMapping("/api/v1/whitelist/deleteByIds") @ResponseBody @ApiOperation(value = "批量删除") public R deleteByIds(String ids) { SysUser sysUser = tokenService.getLoginUser().getSysUser(); List whiteIds = Arrays.stream(ids.split(",")).map(Long::parseLong).collect(Collectors.toList()); return xpWhitelistService.deleteByIds(whiteIds, sysUser); } /** * 批量冻结 */ @GetMapping("/api/v1/whitelist/freezeByIds") @ResponseBody @ApiOperation(value = "批量冻结") public R freezeByIds(String ids) { SysUser sysUser = tokenService.getLoginUser().getSysUser(); List whiteIds = Arrays.stream(ids.split(",")).map(Long::parseLong).collect(Collectors.toList()); return xpWhitelistService.freezeByIds(whiteIds, sysUser); } @ApiOperation(value = "白名单导入模板下载") @GetMapping("/api/v1/whitelist/import-template") public void importTemplate() { List whitelistImportExcels = new ArrayList<>(); WhitelistImportExcel whitelistImportExcel = new WhitelistImportExcel(); whitelistImportExcels.add(whitelistImportExcel); Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), WhitelistImportExcel.class, whitelistImportExcels); HttpServletResponse response = WebUtils.response(); ServletOutputStream outputStream = null; try { String fileName = URLEncoder.encode("白名单导入模板.xls", "utf-8"); response.setHeader("Content-Disposition", "attachment;filename=" + fileName); response.setHeader("content-Type", "application/vnd.ms-excel"); response.setHeader("Pragma", "no-cache"); response.setHeader("Cache-Control", "no-cache"); outputStream = response.getOutputStream(); workbook.write(outputStream); } catch (IOException e) { e.printStackTrace(); } finally { try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } @Transactional @ShardingSphereTransactionType(TransactionType.BASE) @Log(title = "白名单导入", businessType = BusinessType.IMPORT) @ApiOperation(value = "白名单导入") @PostMapping("/base/v1/whitelist/importExcel") public AjaxResult importExcel(@RequestParam("file") MultipartFile file) { ImportParams params = new ImportParams(); // params.setTitleRows(1); // 标题行数 params.setHeadRows(1); //表头行数 List whitelistImportExcels; try { try (InputStream inputStream = file.getInputStream()) { whitelistImportExcels = ExcelImportUtil.importExcel(inputStream, WhitelistImportExcel.class, params); } } catch (Exception e) { e.printStackTrace(); log.error("白名单导入失败:{}", e.getMessage()); throw new ServiceException("白名单导入失败!"); } if (CollectionUtils.isEmpty(whitelistImportExcels)) { throw new ServiceException("白名单数据为空!"); } int sampleLimitPerReason = 20; int totalCount = whitelistImportExcels.size(); Map reasonCountMap = new HashMap<>(); Map> reasonSampleMap = new HashMap<>(); // 先做基础校验 + 文件内去重(允许部分成功) Set seenAccounts = new HashSet<>(); List candidates = new ArrayList<>(); for (int i = 0; i < whitelistImportExcels.size(); i++) { WhitelistImportExcel row = whitelistImportExcels.get(i); String account = trimToNull(row.getAccount()); String nickName = trimToNull(row.getNickName()); row.setAccount(account); row.setNickName(nickName); int excelRowNumber = i + params.getHeadRows() + 1; if (!StringUtils.hasLength(account)) { recordFail(reasonCountMap, reasonSampleMap, "账号为空", "第" + excelRowNumber + "行", sampleLimitPerReason); continue; } if (!PhoneUtil.isMobile(account)) { recordFail(reasonCountMap, reasonSampleMap, "账号格式不正确", account, sampleLimitPerReason); continue; } if (!seenAccounts.add(account)) { recordFail(reasonCountMap, reasonSampleMap, "文件内账号重复", account, sampleLimitPerReason); continue; } candidates.add(row); } // 批量查询数据库已存在账号(避免全表扫描) Set existsAccounts = Collections.emptySet(); if (!CollectionUtils.isEmpty(candidates)) { Set candidateAccounts = candidates.stream() .map(WhitelistImportExcel::getAccount) .filter(StringUtils::hasLength) .collect(Collectors.toSet()); if (!CollectionUtils.isEmpty(candidateAccounts)) { existsAccounts = xpWhitelistService.lambdaQuery() .select(XpWhitelist::getAccount) .in(XpWhitelist::getAccount, candidateAccounts) .list() .stream() .map(XpWhitelist::getAccount) .filter(StringUtils::hasLength) .collect(Collectors.toSet()); } } List addList = new ArrayList<>(); SysUser sysUser = tokenService.getLoginUser().getSysUser(); for (WhitelistImportExcel whitelistImportExcel : candidates) { if (existsAccounts.contains(whitelistImportExcel.getAccount())) { recordFail(reasonCountMap, reasonSampleMap, "账号已存在", whitelistImportExcel.getAccount(), sampleLimitPerReason); continue; } XpWhitelist xpWhitelist = new XpWhitelist(); xpWhitelist.setNickName(whitelistImportExcel.getNickName()); xpWhitelist.setAccount(whitelistImportExcel.getAccount()); xpWhitelist.setStatus(true); xpWhitelist.setIsPriorityDispatch(true); xpWhitelist.setWhitelistSource(1); xpWhitelist.setCreateBy(sysUser.getNickName()); addList.add(xpWhitelist); } int successCount = addList.size(); int failCount = totalCount - successCount; String message; if (failCount == 0) { message = "成功添加" + successCount + "条白名单记录"; } else if (successCount == 0) { message = "所有帐号添加失败,请检查格式或唯一性"; } else { String reasonDetail = buildReasonDetail(reasonCountMap, reasonSampleMap); message = "成功添加" + successCount + "条," + failCount + "条添加失败:原因" + reasonDetail; } if (successCount == 0) { return AjaxResult.error(message, message); } xpWhitelistService.saveBatch(addList); StringBuilder operationContent = new StringBuilder("白名单导入:成功添加") .append(successCount).append("条,失败").append(failCount).append("条"); if (failCount > 0) { operationContent.append(";原因").append(buildReasonSummary(reasonCountMap)); } String opContent = truncate(operationContent.toString(), 200); XpWhitelistOperlog xpWhitelistOperlog = new XpWhitelistOperlog(); xpWhitelistOperlog.setOperationName(sysUser.getNickName()); xpWhitelistOperlog.setOperationAccount(sysUser.getPhoneNumber()); xpWhitelistOperlog.setOperationType(4); xpWhitelistOperlog.setOperationIp(IpUtils.getIpAddr()); xpWhitelistOperlog.setOperationAccountId(sysUser.getId()); xpWhitelistOperlog.setOperationContent(opContent); xpWhitelistOperlog.setCreateBy(sysUser.getNickName()); xpWhitelistOperlogService.save(xpWhitelistOperlog); return AjaxResult.success(opContent, opContent); } private static String trimToNull(String value) { if (!StringUtils.hasLength(value)) { return null; } String trimmed = value.trim(); return StringUtils.hasLength(trimmed) ? trimmed : null; } private static void incReason(Map reasonCountMap, String reason) { reasonCountMap.merge(reason, 1L, Long::sum); } private static void recordFail(Map reasonCountMap, Map> reasonSampleMap, String reason, String sample, int sampleLimitPerReason) { incReason(reasonCountMap, reason); if (!StringUtils.hasLength(sample) || sampleLimitPerReason <= 0) { return; } LinkedHashSet samples = reasonSampleMap.computeIfAbsent(reason, k -> new LinkedHashSet<>()); if (samples.size() >= sampleLimitPerReason) { return; } samples.add(sample); } private static String buildReasonSummary(Map reasonCountMap) { if (reasonCountMap == null || reasonCountMap.isEmpty()) { return "请检查格式或唯一性"; } return reasonCountMap.entrySet().stream() .sorted(Comparator.>comparingLong(e -> e.getValue() == null ? 0L : e.getValue()) .reversed() .thenComparing(Map.Entry::getKey)) .map(e -> e.getKey() + "(" + (e.getValue() == null ? 0L : e.getValue()) + ")") .collect(Collectors.joining(",")); } private static String buildReasonDetail(Map reasonCountMap, Map> reasonSampleMap) { if (reasonCountMap == null || reasonCountMap.isEmpty()) { return "请检查格式或唯一性"; } Map> safeSamples = reasonSampleMap == null ? Collections.emptyMap() : reasonSampleMap; return reasonCountMap.entrySet().stream() .sorted(Comparator.>comparingLong(e -> e.getValue() == null ? 0L : e.getValue()) .reversed() .thenComparing(Map.Entry::getKey)) .map(entry -> { String reason = entry.getKey(); long count = entry.getValue() == null ? 0L : entry.getValue(); LinkedHashSet samples = safeSamples.getOrDefault(reason, new LinkedHashSet<>()); String samplePart = ""; if (!samples.isEmpty()) { long remaining = count - samples.size(); String suffix = remaining > 0 ? "等" + remaining + "个" : ""; samplePart = "[" + String.join(",", samples) + (StringUtils.hasLength(suffix) ? ("," + suffix) : "") + "]"; } return reason + "(" + count + ")" + samplePart; }) .collect(Collectors.joining(";")); } private static String truncate(String value, int maxLength) { if (value == null || maxLength <= 0) { return value; } if (value.length() <= maxLength) { return value; } return value.substring(0, maxLength); } @ApiOperation(value = "白名单导出") @PostMapping(value = "/api/v1/whitelist/exportExcels") public void exportExcels(@RequestBody WhitelistPageListQuery query) { List exportList = xpWhitelistService.exportList(query); List xpWhitelistExportExcels = new ArrayList<>(); long sequence = 1L; for (XpWhitelist xpWhitelist : exportList) { XpWhitelistExportExcel xpWhitelistExportExcel = new XpWhitelistExportExcel(); xpWhitelistExportExcel.setId(sequence++); xpWhitelistExportExcel.setAccount(xpWhitelist.getAccount()); xpWhitelistExportExcel.setNickName(xpWhitelist.getNickName()); xpWhitelistExportExcel.setStatus(xpWhitelist.getStatus()); xpWhitelistExportExcel.setIsPriorityDispatch(xpWhitelist.getIsPriorityDispatch()); xpWhitelistExportExcel.setExpireTime(xpWhitelist.getExpireTime().toString()); xpWhitelistExportExcel.setMaxOrderNum(xpWhitelist.getMaxOrderNum()); xpWhitelistExportExcel.setCreateBy(xpWhitelist.getCreateBy()); LocalDateTime createTime = xpWhitelist.getCreateTime(); // 将LocalDateTime转化为yyyy-MM-dd HH:mm:ss格式字符串 xpWhitelistExportExcel.setCreateTime(createTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); xpWhitelistExportExcels.add(xpWhitelistExportExcel); } SysUser sysUser = tokenService.getLoginUser().getSysUser(); StringBuilder operationContent = new StringBuilder("共计导出"+exportList.size()+"条数据"); XpWhitelistOperlog xpWhitelistOperlog = new XpWhitelistOperlog(); xpWhitelistOperlog.setOperationName(sysUser.getNickName()); xpWhitelistOperlog.setOperationAccount(sysUser.getPhoneNumber()); xpWhitelistOperlog.setOperationType(5); xpWhitelistOperlog.setOperationIp(IpUtils.getIpAddr()); xpWhitelistOperlog.setOperationAccountId(sysUser.getId()); xpWhitelistOperlog.setOperationContent(operationContent.toString()); xpWhitelistOperlogService.save(xpWhitelistOperlog); //1.获取excel模板 Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), XpWhitelistExportExcel.class, xpWhitelistExportExcels); HttpServletResponse response = WebUtils.response(); response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("utf-8"); ServletOutputStream outputStream = null; try { String fileName = URLEncoder.encode("白名单列表.xls", "utf-8"); response.setHeader("Content-dispodition", "attachment;filename=" + fileName); outputStream = response.getOutputStream(); workbook.write(outputStream); } catch (IOException e) { e.printStackTrace(); } finally { try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }