package com.xiaopeng.operation.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.PhoneUtil; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.xiaopeng.api.dto.account.UcUserInfo; import com.xiaopeng.api.feignClient.account.UserResourcesClient; 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.SensitiveDataCryptoUtil; import com.xiaopeng.common.core.utils.ip.IpUtils; import com.xiaopeng.common.core.web.page.PageInfo; import com.xiaopeng.operation.constant.I18nKeyConstant; import com.xiaopeng.operation.dto.WhitelistAddDto; import com.xiaopeng.operation.mapper.XpWhitelistMapper; import com.xiaopeng.operation.mapper.XpWhitelistOperlogMapper; import com.xiaopeng.operation.query.WhitelistPageListQuery; import com.xiaopeng.operation.service.IXpWhitelistService; import org.apache.shardingsphere.transaction.annotation.ShardingSphereTransactionType; import org.apache.shardingsphere.transaction.core.TransactionType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import java.io.Serializable; import java.time.LocalDate; import java.util.*; import java.util.stream.Collectors; /** *

* 白名单管理 服务实现类 *

* * @author mitao * @since 2026-02-03 */ @Service public class XpWhitelistServiceImpl extends ServiceImpl implements IXpWhitelistService { @Autowired private XpWhitelistOperlogMapper xpWhitelistOperlogMapper; @Autowired private UserResourcesClient userResourcesClient; @Override public PageInfo pageList(WhitelistPageListQuery query) { query.setAccountQueryValues(SensitiveDataCryptoUtil.queryValues(query.getAccount())); PageInfo pageInfo = new PageInfo<>(query.getPageNum(),query.getPageSize()); List list = this.baseMapper.pageList(pageInfo,query); decryptWhitelistAccounts(list); pageInfo.setRecords(list); return pageInfo; } @Override public List exportList(WhitelistPageListQuery query) { query.setAccountQueryValues(SensitiveDataCryptoUtil.queryValues(query.getAccount())); List list = this.baseMapper.exportList(query); decryptWhitelistAccounts(list); return list; } //根据用户账号 通过接口获取用户id public UcUserInfo getUserByAccount (String account){ UcUserInfo userByMobile = userResourcesClient.getUserByMobile(account); if(userByMobile == null){ throw new ServiceException(account+"未获取到用户信息"); } return userByMobile; } @Override @Transactional @ShardingSphereTransactionType(TransactionType.BASE) public R addWhitelist(WhitelistAddDto dto, SysUser sysUser) { List accounts = Arrays.stream(dto.getAccount().split(",")) .map(String::trim) .filter(StringUtils::hasLength) .collect(Collectors.toList()); if (CollUtil.isEmpty(accounts)) { return R.fail("所有帐号添加失败,请检查帐号格式或唯一性"); } if (dto.getExpireTime() != null && dto.getExpireTime().isBefore(LocalDate.now())) { return R.fail("有效期不能早于当前日期"); } int sampleLimitPerReason = 20; int totalCount = accounts.size(); Map reasonCountMap = new HashMap<>(); Map> reasonSampleMap = new HashMap<>(); Map userMap = new HashMap<>(); Set seenAccounts = new HashSet<>(); List candidates = new ArrayList<>(); for (String account : accounts) { if (!PhoneUtil.isMobile(account)) { recordFail(reasonCountMap, reasonSampleMap, "帐号格式不正确", account, sampleLimitPerReason); continue; } if (!seenAccounts.add(account)) { recordFail(reasonCountMap, reasonSampleMap, "文件内重复", account, sampleLimitPerReason); continue; } //根据用户账号 通过接口获取用户id UcUserInfo userByAccount = this.getUserByAccount(account); if(userByAccount != null){ candidates.add(account); userMap.put(account, userByAccount); } } if (CollUtil.isEmpty(candidates)) { return R.fail("所有帐号添加失败,请检查帐号格式或唯一性"); } Set existsAccounts = new HashSet<>(); if (CollUtil.isNotEmpty(candidates)) { Set existsStoredAccounts = this.lambdaQuery() .select(XpWhitelist::getAccount) .in(XpWhitelist::getAccount, buildQueryValues(candidates)) .list() .stream() .map(XpWhitelist::getAccount) .filter(StringUtils::hasLength) .collect(Collectors.toSet()); existsAccounts = existsStoredAccounts.stream() .map(SensitiveDataCryptoUtil::decryptIfPossible) .collect(Collectors.toSet()); } List addList = new ArrayList<>(); for (String account : candidates) { if (existsAccounts.contains(account)) { recordFail(reasonCountMap, reasonSampleMap, "帐号已存在", account, sampleLimitPerReason); continue; } XpWhitelist xpWhitelist = new XpWhitelist(); xpWhitelist.setAccount(SensitiveDataCryptoUtil.encrypt(account)); xpWhitelist.setNickName(userMap.get(account).getNickName()); xpWhitelist.setAppUserId(userMap.get(account).getUid()); xpWhitelist.setStatus(true); xpWhitelist.setIsPriorityDispatch(dto.getIsPriorityDispatch()); xpWhitelist.setWhitelistSource(1); xpWhitelist.setCreateBy(sysUser.getNickName()); xpWhitelist.setExpireTime(dto.getExpireTime()); xpWhitelist.setMaxOrderNum(dto.getMaxOrderNum()); addList.add(xpWhitelist); } int successCount = addList.size(); int failCount = totalCount - successCount; String message; if (failCount == 0) { message = "成功添加" + successCount + "条白名单记录"; } else if (successCount == 0) { message = "所有帐号添加失败,请检查帐号格式或唯一性"; } else { message = "成功加" + successCount + "条," + failCount + "条添加失败,失败原因:" + buildReasonDetail(reasonCountMap, reasonSampleMap); } if (successCount == 0) { return R.fail(message); } this.saveBatch(addList); StringBuilder operationContent = new StringBuilder("新增账号:"); StringBuilder accountBuilder = new StringBuilder(); for (XpWhitelist item : addList) { accountBuilder.append("\"").append(SensitiveDataCryptoUtil.decryptIfPossible(item.getAccount())).append("\"").append(","); } if (accountBuilder.length() > 0) { operationContent.append(accountBuilder.substring(0, accountBuilder.length() - 1)); } if (failCount > 0) { operationContent.append(";失败").append(failCount).append("条"); operationContent.append(";原因").append(buildReasonSummary(reasonCountMap)); } XpWhitelistOperlog xpWhitelistOperlog = new XpWhitelistOperlog(); xpWhitelistOperlog.setOperationName(sysUser.getNickName()); xpWhitelistOperlog.setOperationAccount(sysUser.getPhoneNumber()); xpWhitelistOperlog.setOperationType(1); xpWhitelistOperlog.setOperationIp(IpUtils.getIpAddr()); xpWhitelistOperlog.setOperationAccountId(sysUser.getId()); xpWhitelistOperlog.setOperationContent(truncate(operationContent.toString(), 200)); xpWhitelistOperlogMapper.insert(xpWhitelistOperlog); return R.ok(null, message); } private static void recordFail(Map reasonCountMap, Map> reasonSampleMap, String reason, String sample, int sampleLimitPerReason) { reasonCountMap.merge(reason, 1L, Long::sum); 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 ? new HashMap<>() : 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<>()); if (samples.isEmpty()) { return reason + "(" + count + ")"; } long remaining = count - samples.size(); String suffix = remaining > 0 ? "等" + remaining + "个" : ""; String suffixPart = StringUtils.hasLength(suffix) ? ("," + suffix) : ""; return reason + "(" + count + ")[" + String.join(",", samples) + suffixPart + "]"; }) .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); } @Override public R editWhitelist(XpWhitelist dto, SysUser sysUser) { if (!PhoneUtil.isMobile(dto.getAccount())) { throw new ServiceException("以下帐号格式不正确:", I18nKeyConstant.ACCOUNT_FORMAT_ERROR, new Object[]{dto.getAccount()}); } UcUserInfo userByAccount = this.getUserByAccount(dto.getAccount()); XpWhitelist old = this.lambdaQuery() .in(XpWhitelist::getAccount, SensitiveDataCryptoUtil.queryValues(dto.getAccount())) .ne(XpWhitelist::getId, dto.getId()) .last("limit 1").one(); if (old != null) { return R.fail("该账号已存在"); } XpWhitelist whitelist = this.getById(dto.getId()); whitelist.setAccount(SensitiveDataCryptoUtil.decryptIfPossible(whitelist.getAccount())); whitelist.setAppUserId(userByAccount.getUid()); StringBuilder operationContent = new StringBuilder("编辑账号:"); StringBuilder accountBuilder = new StringBuilder(); if (!whitelist.getAccount().equals(dto.getAccount())) { accountBuilder.append("白名单账号" + "\"" + whitelist.getAccount() + "\"" + "为" + "\"" + dto.getAccount() + "\""); } if (StringUtils.hasLength(whitelist.getNickName()) || StringUtils.hasLength(dto.getNickName())) { if (StringUtils.hasLength(whitelist.getNickName())) { if (!whitelist.getNickName().equals(dto.getNickName())) { accountBuilder.append("用户名称" + "\"" + whitelist.getNickName() + "\"" + "为" + "\"" + dto.getNickName() + "\""); } } else { if (!whitelist.getNickName().equals(dto.getNickName())) { accountBuilder.append("用户名称" + "\"" + "为" + "\"" + dto.getNickName() + "\""); } } } if (whitelist.getStatus() != dto.getStatus()) { accountBuilder.append("状态" + "\"" + (whitelist.getIsPriorityDispatch() ? "正常" : "冻结") + "\"" + "为" + "\"" + (dto.getIsPriorityDispatch() ? "正常" : "冻结") + "\""); } if (whitelist.getIsPriorityDispatch() != dto.getIsPriorityDispatch()) { accountBuilder.append("优先派单" + "\"" + (whitelist.getIsPriorityDispatch() ? "是" : "否") + "\"" + "为" + "\"" + (dto.getIsPriorityDispatch() ? "是" : "否") + "\""); } if ( !dto.getMaxOrderNum().equals(whitelist.getMaxOrderNum()) ) { accountBuilder.append("最大下单数量" + "\"" + whitelist.getMaxOrderNum() + "\"" + "为" + "\"" + dto.getMaxOrderNum() + "\""); whitelist.setMaxOrderNum(dto.getMaxOrderNum()); } if (!whitelist.getExpireTime().isEqual(dto.getExpireTime())) { accountBuilder.append("有限期" + "\"" + whitelist.getExpireTime() + "\"" + "为" + "\"" + dto.getExpireTime() + "\""); whitelist.setExpireTime(dto.getExpireTime()); } XpWhitelistOperlog xpWhitelistOperlog = new XpWhitelistOperlog(); xpWhitelistOperlog.setOperationName(sysUser.getNickName()); xpWhitelistOperlog.setOperationAccount(sysUser.getPhoneNumber()); xpWhitelistOperlog.setOperationType(2); xpWhitelistOperlog.setOperationIp(IpUtils.getIpAddr()); xpWhitelistOperlog.setOperationAccountId(sysUser.getId()); accountBuilder.append("\"").append(dto.getAccount()).append("\""); StringBuilder content = operationContent.append(accountBuilder); xpWhitelistOperlog.setOperationContent(content.toString()); xpWhitelistOperlogMapper.insert(xpWhitelistOperlog); whitelist.setNickName(dto.getNickName()); whitelist.setIsPriorityDispatch(dto.getIsPriorityDispatch()); whitelist.setAccount(SensitiveDataCryptoUtil.encrypt(dto.getAccount())); whitelist.setStatus(dto.getStatus()); this.updateById(whitelist); return R.ok(); } @Override public R freezeUnfreeze(Long id, SysUser sysUser) { XpWhitelist whitelist = this.getById(id); whitelist.setAccount(SensitiveDataCryptoUtil.decryptIfPossible(whitelist.getAccount())); StringBuilder operationContent = new StringBuilder("编辑账号:"+"\""+whitelist.getAccount()+"\""); StringBuilder accountBuilder = new StringBuilder(); accountBuilder.append("状态" + "\"" + (whitelist.getIsPriorityDispatch() ? "正常" : "冻结") + "\"" + "为" + "\"" + (!whitelist.getIsPriorityDispatch() ? "正常" : "冻结") + "\""); XpWhitelistOperlog xpWhitelistOperlog = new XpWhitelistOperlog(); xpWhitelistOperlog.setOperationName(sysUser.getNickName()); xpWhitelistOperlog.setOperationAccount(sysUser.getPhoneNumber()); xpWhitelistOperlog.setOperationType(2); xpWhitelistOperlog.setOperationIp(IpUtils.getIpAddr()); xpWhitelistOperlog.setOperationAccountId(sysUser.getId()); StringBuilder content = operationContent.append(accountBuilder); xpWhitelistOperlog.setOperationContent(content.toString()); xpWhitelistOperlogMapper.insert(xpWhitelistOperlog); whitelist.setStatus(!whitelist.getStatus()); this.updateById(whitelist); return R.ok(); } @Override public R deleteByIds(List ids, SysUser sysUser) { List accounts = this.lambdaQuery().in(XpWhitelist::getId, ids).list() .stream().map(XpWhitelist::getAccount) .map(SensitiveDataCryptoUtil::decryptIfPossible) .collect(Collectors.toList()); this.removeBatchByIds(ids); StringBuilder operationContent = new StringBuilder("删除账号:"); StringBuilder accountBuilder = new StringBuilder(); for (String account : accounts) { accountBuilder.append("\"" + account + "\""+","); } XpWhitelistOperlog xpWhitelistOperlog = new XpWhitelistOperlog(); xpWhitelistOperlog.setOperationName(sysUser.getNickName()); xpWhitelistOperlog.setOperationAccount(sysUser.getPhoneNumber()); xpWhitelistOperlog.setOperationType(3); xpWhitelistOperlog.setOperationIp(IpUtils.getIpAddr()); xpWhitelistOperlog.setOperationAccountId(sysUser.getId()); StringBuilder content = operationContent.append(accountBuilder.substring(0, accountBuilder.length() - 1)); xpWhitelistOperlog.setOperationContent(content.toString()); xpWhitelistOperlogMapper.insert(xpWhitelistOperlog); return R.ok(); } @Override public R freezeByIds(List ids, SysUser sysUser) { List list = this.lambdaQuery().in(XpWhitelist::getId, ids).list(); for (XpWhitelist xpWhitelist : list) { xpWhitelist.setStatus(false); } this.updateBatchById(list); StringBuilder operationContent = new StringBuilder("共计冻结"+ids.size()+"条数据"); XpWhitelistOperlog xpWhitelistOperlog = new XpWhitelistOperlog(); xpWhitelistOperlog.setOperationName(sysUser.getNickName()); xpWhitelistOperlog.setOperationAccount(sysUser.getPhoneNumber()); xpWhitelistOperlog.setOperationType(6); xpWhitelistOperlog.setOperationIp(IpUtils.getIpAddr()); xpWhitelistOperlog.setOperationAccountId(sysUser.getId()); xpWhitelistOperlog.setOperationContent(operationContent.toString()); xpWhitelistOperlogMapper.insert(xpWhitelistOperlog); return R.ok(); } /** * 获取所有白名单 * @return */ @Override public List getAllWhiteList() { return lambdaQuery().select(XpWhitelist::getAccount).list() .stream().map(XpWhitelist::getAccount) .map(SensitiveDataCryptoUtil::decryptIfPossible) .collect(Collectors.toList()); } private static List buildQueryValues(Collection plainValues) { Set values = new LinkedHashSet<>(); if (plainValues == null) { return new ArrayList<>(); } for (String plainValue : plainValues) { if (StringUtils.hasLength(plainValue)) { values.addAll(SensitiveDataCryptoUtil.queryValues(plainValue)); } } return new ArrayList<>(values); } private static void decryptWhitelistAccounts(List list) { if (CollUtil.isEmpty(list)) { return; } for (XpWhitelist item : list) { item.setAccount(SensitiveDataCryptoUtil.decryptIfPossible(item.getAccount())); } } @Override public XpWhitelist getById(Serializable id) { XpWhitelist whitelist = super.getById(id); decryptWhitelistAccount(whitelist); return whitelist; } @Override public boolean save(XpWhitelist entity) { encryptWhitelistAccount(entity); try { return super.save(entity); } finally { decryptWhitelistAccount(entity); } } @Override public boolean saveBatch(Collection entityList) { if (entityList != null) { entityList.forEach(this::encryptWhitelistAccount); } try { return super.saveBatch(entityList); } finally { decryptWhitelistAccounts(entityList); } } @Override public boolean updateById(XpWhitelist entity) { encryptWhitelistAccount(entity); try { return super.updateById(entity); } finally { decryptWhitelistAccount(entity); } } @Override public boolean updateBatchById(Collection entityList) { if (entityList != null) { entityList.forEach(this::encryptWhitelistAccount); } try { return super.updateBatchById(entityList); } finally { decryptWhitelistAccounts(entityList); } } @Override public List list(Wrapper queryWrapper) { List list = super.list(queryWrapper); decryptWhitelistAccounts(list); return list; } @Override public List list() { List list = super.list(); decryptWhitelistAccounts(list); return list; } private void encryptWhitelistAccount(XpWhitelist whitelist) { if (whitelist != null) { whitelist.setAccount(SensitiveDataCryptoUtil.encrypt(whitelist.getAccount())); } } private static void decryptWhitelistAccount(XpWhitelist whitelist) { if (whitelist != null) { whitelist.setAccount(SensitiveDataCryptoUtil.decryptIfPossible(whitelist.getAccount())); } } private void decryptWhitelistAccounts(Collection list) { if (CollUtil.isEmpty(list)) { return; } for (XpWhitelist item : list) { decryptWhitelistAccount(item); } } }