关于URLEncoder.encode编码后比对结果不一致的问题

本文介绍如何在service.xml中设置useBodyEncodingForURI参数为true,并修改catalina.bat文件,将JAVA_OPTS参数更新为包含-Dfile.encoding=UTF-8,以确保应用程序正确处理UTF-8编码。

除了设置utf-8编码外,service.xml需要添加useBodyEncodingForURI="true"

catalina.bat文件编辑将set "JAVA_OPTS=%JAVA_OPTS% %LOGGING_MANAGER%"修改为set "JAVA_OPTS=%JAVA_OPTS% -Dfile.encoding=UTF-8"

package com.hvlink.service.impl; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.google.common.collect.Lists; import com.hvlink.common.Result; import com.hvlink.entity.dto.master.PackageExportDTO; import com.hvlink.entity.dto.master.PackageImportDTO; import com.hvlink.entity.function.PackingQuantity; import com.hvlink.entity.param.master.PackageParam; import com.hvlink.entity.po.master.CompanyPO; import com.hvlink.entity.po.master.FactoryPO; import com.hvlink.entity.po.master.PackagePO; import com.hvlink.entity.po.master.PartPO; import com.hvlink.entity.vo.master.ImportErrorVO; import com.hvlink.entity.vo.master.PackageImportResultVO; import com.hvlink.entity.vo.master.PackageVO; import com.hvlink.enums.ResultCode; import com.hvlink.exceptions.BusinessException; import com.hvlink.mapper.master.CompanyMapper; import com.hvlink.mapper.master.FactoryMapper; import com.hvlink.mapper.master.PackageMapper; import com.hvlink.mapper.master.PartMapper; import com.hvlink.pagination.PageResult; import com.hvlink.service.IPackageService; import com.hvlink.service.constant.RfcSyncConstant; import com.hvlink.utils.BeanCopyUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.DateFormatUtils; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.codec.multipart.FilePart; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import reactor.core.publisher.Mono; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.net.URLEncoder; import java.nio.file.Files; import java.nio.file.Path; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.function.BinaryOperator; import java.util.function.Function; import java.util.stream.Collectors; /** * 包装数量主数据(Package)表服务实现类 * * @author sunzhuang * @since 2025-09-10 21:54:19 */ @Slf4j @RequiredArgsConstructor @Service("packageService") public class PackageServiceImpl extends ServiceImpl<PackageMapper, PackagePO> implements IPackageService { private final PackageMapper packageMapper; private final CompanyMapper companyMapper; private final FactoryMapper factoryMapper; private final PartMapper partMapper; /** * 通过ID查询单条数据 * * @param id 主键 * @return 实例对象 */ @Override public PackageVO selectById(Integer id) { PackagePO po = packageMapper.selectById(id); if (Objects.isNull(po)) { throw new BusinessException(ResultCode.PARAM_ERROR); } return BeanCopyUtils.copyBean(po, PackageVO.class); } /** * 修改数据 * * @param param 更新参数 */ @Override public void update(PackageParam param) { PackagePO po = BeanCopyUtils.copyBean(param, PackagePO.class); packageMapper.updateById(po); } /** * 分页查询 * * @param param 查询参数 * @return 物料包装数 */ @Override public PageResult<PackageVO> selectPage(PackageParam param) { PageResult<PackageVO> pageResult = new PageResult<>(); LambdaQueryWrapper<PackagePO> wrapper = Wrappers.<PackagePO>lambdaQuery() .eq(StringUtils.isNotBlank(param.getCompanyCode()), PackagePO::getCompanyCode, param.getCompanyCode()) .eq(StringUtils.isNotBlank(param.getFactoryCode()), PackagePO::getFactoryCode, param.getFactoryCode()) .eq(StringUtils.isNotBlank(param.getPartCode()), PackagePO::getFactoryCode, param.getFactoryCode()) .orderByDesc(PackagePO::getCreateTime); Page<PackagePO> poPage = packageMapper.selectPage(new Page<>(param.getPageIndex(), param.getPageSize()), wrapper); if (poPage.getTotal() > 0) { // 查询所有公司 Map<String, CompanyPO> companyMap = companyMapper.selectList(Wrappers.emptyWrapper()).stream().collect(Collectors.toMap(CompanyPO::getCompanyCode, Function.identity(), BinaryOperator.maxBy(Comparator.comparing(CompanyPO::getCreateTime)))); Map<String, FactoryPO> factoryMap = factoryMapper.selectList(Wrappers.emptyWrapper()).stream().collect(Collectors.toMap(FactoryPO::getFactoryCode, Function.identity(), BinaryOperator.maxBy(Comparator.comparing(FactoryPO::getCreateTime)))); pageResult.setTotal(poPage.getTotal()); pageResult.setPageIndex(poPage.getCurrent()); pageResult.setPageSize(poPage.getSize()); List<PackageVO> voList = Lists.newArrayList(); poPage.getRecords().forEach(po -> { PackageVO vo = BeanCopyUtils.copyBean(po, PackageVO.class); CompanyPO companyPO = companyMap.get(po.getCompanyCode()); if (Objects.nonNull(companyPO)) { vo.setCompanyCode(companyPO.getCompanyCode() + "-" + companyPO.getCompanyName()); } FactoryPO factoryPO = factoryMap.get(po.getFactoryCode()); if (Objects.nonNull(factoryPO)) { vo.setFactoryCode(factoryPO.getFactoryCode()); vo.setFactoryName(factoryPO.getFactoryName()); } voList.add(vo); }); pageResult.setRecords(voList); } return pageResult; } /** * 处理包装数量数据 * * @param list 同步的包装数据集合 * @return 本次更新时间 */ @Override @Transactional(rollbackFor = Exception.class) public String handlePackingQuantityData(List<PackingQuantity> list) { if (CollectionUtils.isEmpty(list)) { log.info("存在更新的包装信息"); } //查询数据库中的所有的包装信息 List<PackagePO> poList = packageMapper.selectPackageKey(); // 按照公司代码 + 工厂代码 + 物料编码 分组 Map<String, Integer> warehouseMap = poList.stream().collect(Collectors.toMap(x -> x.getCompanyCode() + x.getFactoryCode() + x.getPartCode(), PackagePO::getId, (v1, v2) -> v1)); // 新增数据集合 List<PackagePO> insertList = Lists.newArrayList(); // 更新数据集合 List<PackagePO> updateList = Lists.newArrayList(); list.forEach(x -> { // 构建主键 String key = x.getCompanyCode() + x.getFactoryCode() + x.getPartCode(); PackagePO po = BeanCopyUtils.copyBean(x, PackagePO.class); if (warehouseMap.containsKey(key)) { po.setId(warehouseMap.get(key)); po.setUpdateBy(RfcSyncConstant.DEFAULT_RFC_UPDATE_BY); updateList.add(po); } else { po.setUpdateBy(RfcSyncConstant.DEFAULT_RFC_UPDATE_BY); po.setCreateBy(RfcSyncConstant.DEFAULT_RFC_UPDATE_BY); insertList.add(po); } }); // 新增 if (CollectionUtils.isNotEmpty(insertList)) { this.saveBatch(insertList, 500); } if (CollectionUtils.isNotEmpty(updateList)) { this.updateBatchById(updateList, 500); } return DateFormatUtils.ISO_8601_EXTENDED_DATE_FORMAT.format(new Date()); } /** * 批量导出Excel */ @Override public Mono<Void> exportPackageExcel(List<Integer> selectedIds, ServerHttpResponse response) { return Mono.fromCallable(() -> { setupResponse(response, "包装数据导出"); return selectedIds; }) .flatMap(ids -> { try { // 查询数据 List<PackagePO> packageList = this.listByIds(ids); // 转换为导出DTO List<PackageExportDTO> exportData = packageList.stream() .map(po -> BeanCopyUtils.copyBean(po, PackageExportDTO.class)) .collect(Collectors.toList()); // 生成Excel数据 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); EasyExcel.write(outputStream, PackageExportDTO.class) .sheet("包装数据") .doWrite(exportData); byte[] data = outputStream.toByteArray(); DataBuffer buffer = response.bufferFactory().wrap(data); return response.writeWith(Mono.just(buffer)); } catch (Exception e) { log.error("包装数据导出失败", e); return Mono.error(new RuntimeException("导出Excel失败", e)); } }) .doOnSuccess(v -> log.info("包装数据导出成功")) .doOnError(e -> log.error("包装数据导出失败", e)); } /** * 下载导入模板 */ @Override public Mono<Void> downloadTemplate(ServerHttpResponse response) { return Mono.fromCallable(() -> { setupResponse(response, "包装数据导入模板"); return new ArrayList<PackageImportDTO>(); }) .flatMap(templateData -> { try { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); // 创建空的模板数据 EasyExcel.write(outputStream, PackageImportDTO.class) .sheet("包装数据导入模板") .doWrite(templateData); byte[] data = outputStream.toByteArray(); DataBuffer buffer = response.bufferFactory().wrap(data); return response.writeWith(Mono.just(buffer)); } catch (Exception e) { log.error("模板下载失败", e); return Mono.error(new RuntimeException("模板下载失败", e)); } }) .doOnSuccess(v -> log.info("包装数据导入模板下载成功")) .doOnError(e -> log.error("包装数据导入模板下载失败", e)); } /** * 导入包装数据 */ @Override public Mono<Result<PackageImportResultVO>> importPackage(FilePart filePart) { return Mono.defer(() -> { // 验证文件 if (filePart == null) { return Mono.just(Result.<PackageImportResultVO>fail("导入文件能为空")); } String fileName = filePart.filename(); if (fileName == null || (!fileName.endsWith(".xlsx") && !fileName.endsWith(".xls"))) { return Mono.just(Result.<PackageImportResultVO>fail("请上传Excel文件")); } log.info("开始处理包装数据文件导入: {}", fileName); try { Path tempDir = Files.createTempDirectory("package_import"); Path tempFile = tempDir.resolve(System.currentTimeMillis() + "_" + fileName); return filePart.transferTo(tempFile) .then(Mono.fromCallable(() -> { try { File file = tempFile.toFile(); Result<PackageImportResultVO> importResult = processPackageExcelImport(file); log.info("包装数据文件导入完成: {}, 结果: {}", fileName, importResult.getMsg()); return importResult; } finally { // 清理临时文件 try { Files.deleteIfExists(tempFile); Files.deleteIfExists(tempDir); } catch (Exception e) { log.warn("清理临时文件失败: {}", e.getMessage()); } } })); } catch (Exception e) { log.error("创建临时文件失败", e); return Mono.just(Result.<PackageImportResultVO>fail("文件处理失败: " + e.getMessage())); } }) .onErrorResume(e -> { log.error("包装数据导入异常", e); return Mono.just(Result.<PackageImportResultVO>fail("包装数据导入异常: " + e.getMessage())); }); } /** * 处理包装数据Excel导入逻辑 */ private Result<PackageImportResultVO> processPackageExcelImport(File file) { PackageImportResultVO result = new PackageImportResultVO(); List<ImportErrorVO> errorList = new ArrayList<>(); try (InputStream inputStream = new FileInputStream(file)) { // 读取Excel数据 List<PackageImportDTO> importData = new ArrayList<>(); List<PackageImportDTO> validData = new ArrayList<>(); EasyExcel.read(inputStream, PackageImportDTO.class, new AnalysisEventListener<PackageImportDTO>() { private final List<PackageImportDTO> cachedDataList = new ArrayList<>(); @Override public void invoke(PackageImportDTO data, AnalysisContext context) { int rowIndex = context.readRowHolder().getRowIndex() + 1; // 跳过空行 if (StringUtils.isBlank(data.getCompanyCode()) && StringUtils.isBlank(data.getFactoryCode()) && StringUtils.isBlank(data.getPartCode())) { return; } // 数据校验 String errorMsg = validatePackageImportData(data, rowIndex); if (StringUtils.isNotBlank(errorMsg)) { ImportErrorVO error = new ImportErrorVO(); error.setRowNum(rowIndex); error.setErrorMsg(errorMsg); error.setPartCode(data.getPartCode()); errorList.add(error); } else { data.setRowNum(rowIndex); cachedDataList.add(data); } } @Override public void doAfterAllAnalysed(AnalysisContext context) { importData.addAll(cachedDataList); validData.addAll(cachedDataList); } @Override public void onException(Exception exception, AnalysisContext context) throws Exception { log.error("Excel读取异常,行号: {}", context.readRowHolder().getRowIndex(), exception); throw exception; } }).sheet().doRead(); // 如果有错误数据,终止导入 if (!errorList.isEmpty()) { result.setTotalCount(importData.size() + errorList.size()); result.setSuccessCount(0); result.setFailCount(errorList.size()); result.setErrorList(errorList); result.setMessage("导入失败:存在错误数据,请修正后重新导入"); return Result.fail("导入失败:存在错误数据"); } // 处理有效数据 if (!validData.isEmpty()) { processPackageImportData(validData, errorList); } // 设置返回结果 result.setTotalCount(importData.size()); result.setSuccessCount(validData.size() - errorList.size()); result.setFailCount(errorList.size()); result.setErrorList(errorList); result.setMessage(String.format("导入完成:成功%d条,失败%d条", result.getSuccessCount(), result.getFailCount())); log.info("包装数据导入完成:总数={}, 成功={}, 失败={}", result.getTotalCount(), result.getSuccessCount(), result.getFailCount()); return Result.success(result); } catch (Exception e) { log.error("处理Excel文件异常", e); return Result.fail("处理Excel文件失败: " + e.getMessage()); } } /** * 验证包装数据导入数据 */ private String validatePackageImportData(PackageImportDTO data, int rowNum) { StringBuilder errorMsg = new StringBuilder(); // 必填字段校验 if (StringUtils.isBlank(data.getCompanyCode())) { errorMsg.append("公司代码能为空; "); } if (StringUtils.isBlank(data.getFactoryCode())) { errorMsg.append("工厂代码能为空; "); } else { // 工厂校验 FactoryPO factory = factoryMapper.selectOne( Wrappers.<FactoryPO>lambdaQuery() .eq(FactoryPO::getFactoryCode, data.getFactoryCode()) .eq(FactoryPO::getIsDeleted, 0) ); if (factory == null) { errorMsg.append("工厂代码存在; "); } else if ("已禁用".equals(factory.getStatus())) { errorMsg.append("工厂代码已禁用; "); } } if (StringUtils.isBlank(data.getPartCode())) { errorMsg.append("零件号能为空; "); } else { // 零件校验 Long partCount = partMapper.selectCount( Wrappers.<PartPO>lambdaQuery() .eq(PartPO::getPartCode, data.getPartCode()) .eq(PartPO::getIsDeleted, 0) ); if (partCount == 0) { errorMsg.append("零件号存在; "); } } // 数校验 if (data.getBoxQuantity() != null && data.getBoxQuantity() < 0) { errorMsg.append("箱数量能小于0; "); } if (data.getPackageQuantity() != null && data.getPackageQuantity() < 0) { errorMsg.append("包数量能小于0; "); } if (data.getDragQuantity() != null && data.getDragQuantity() < 0) { errorMsg.append("托数量能小于0; "); } // 重复性校验 if (StringUtils.isNotBlank(data.getCompanyCode()) && StringUtils.isNotBlank(data.getFactoryCode()) && StringUtils.isNotBlank(data.getPartCode())) { Long count = this.baseMapper.selectCount( Wrappers.<PackagePO>lambdaQuery() .eq(PackagePO::getCompanyCode, data.getCompanyCode()) .eq(PackagePO::getFactoryCode, data.getFactoryCode()) .eq(PackagePO::getPartCode, data.getPartCode()) .eq(PackagePO::getIsDeleted, 0) ); if (count > 0) { errorMsg.append("公司代码+工厂代码+零件号组合已存在; "); } } return errorMsg.length() > 0 ? errorMsg.toString() : null; } /** * 处理包装数据导入 */ private void processPackageImportData(List<PackageImportDTO> validData, List<ImportErrorVO> errorList) { String currentUser = "import_user"; for (PackageImportDTO dto : validData) { try { // 转换为PO PackagePO packagePO = BeanCopyUtils.copyBean(dto, PackagePO.class); packagePO.setCreateBy(currentUser); packagePO.setUpdateBy(currentUser); // 保存到数据库 this.baseMapper.insert(packagePO); log.info("包装数据导入成功 - 公司代码: {}, 工厂代码: {}, 零件号: {}", dto.getCompanyCode(), dto.getFactoryCode(), dto.getPartCode()); } catch (Exception e) { log.error("包装数据导入失败 - 公司代码: {}, 工厂代码: {}, 零件号: {}", dto.getCompanyCode(), dto.getFactoryCode(), dto.getPartCode(), e); ImportErrorVO error = new ImportErrorVO(); error.setRowNum(dto.getRowNum()); error.setErrorMsg("系统错误: " + e.getMessage()); error.setPartCode(dto.getPartCode()); errorList.add(error); } } } /** * 设置HTTP响应头 */ private void setupResponse(ServerHttpResponse response, String filePrefix) { try { response.getHeaders().setContentType(MediaType.APPLICATION_OCTET_STREAM); String fileName = URLEncoder.encode(filePrefix + "_" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")), "UTF-8"); String contentDisposition = "attachment; filename=" + fileName + ".xlsx"; response.getHeaders().set(HttpHeaders.CONTENT_DISPOSITION, contentDisposition); response.getHeaders().set(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, must-revalidate"); response.getHeaders().set(HttpHeaders.PRAGMA, "no-cache"); response.getHeaders().set(HttpHeaders.EXPIRES, "0"); } catch (Exception e) { log.error("响应头设置失败", e); throw new RuntimeException("响应头设置失败", e); } } } 加个校验,如果表头对上,直接执行导入,提示导入模板错误
09-29
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值