package com.izouma.nineth.service; import cn.hutool.core.util.ZipUtil; import com.alibaba.excel.EasyExcel; import com.izouma.nineth.domain.*; import com.izouma.nineth.dto.SandPaySettle; import com.izouma.nineth.dto.UserBankCard; import com.izouma.nineth.dto.UserWithdraw; import com.izouma.nineth.enums.BalanceType; import com.izouma.nineth.enums.CollectionSource; import com.izouma.nineth.enums.OrderStatus; import com.izouma.nineth.exception.BusinessException; import com.izouma.nineth.repo.*; import com.izouma.nineth.service.storage.StorageService; import com.izouma.nineth.utils.DateTimeUtils; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.WorkbookFactory; import org.apache.poi.util.TempFile; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import javax.transaction.Transactional; import java.io.*; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @Service @Slf4j @AllArgsConstructor public class UserBalanceService { private final UserBalanceRepo userBalanceRepo; private final BalanceRecordRepo balanceRecordRepo; private final OrderRepo orderRepo; private final AssetRepo assetRepo; private final UserBankCardRepo userBankCardRepo; private final SettleRecordRepo settleRecordRepo; private final StorageService storageService; private final ExportWithdrawRepo exportWithdrawRepo; public void settle(LocalDate start, LocalDate end) { for (long i = 0; i <= ChronoUnit.DAYS.between(start, end); i++) { LocalDate date = start.plusDays(i); if (settleRecordRepo.findByDate(date).isPresent()) { throw new BusinessException(DateTimeUtils.format(date, "yyyy-MM-dd") + "已经结算过"); } } for (long i = 0; i <= ChronoUnit.DAYS.between(start, end); i++) { LocalDate date = start.plusDays(i); settle(date); } } public void settle(LocalDate date) { if (settleRecordRepo.findByDate(date).isPresent()) { throw new BusinessException(DateTimeUtils.format(date, "yyyy-MM-dd") + "已经结算过"); } List orders = orderRepo.findByCreatedAtBetweenAndSourceAndStatusIn(date.atStartOfDay(), date.atTime(23, 59, 59, 9999), CollectionSource.TRANSFER, Arrays.asList(OrderStatus.PROCESSING, OrderStatus.FINISH)); BigDecimal totalAmount = BigDecimal.ZERO; BigDecimal royaltiesAmount = BigDecimal.ZERO; BigDecimal serviceChargeAmount = BigDecimal.ZERO; for (Order order : orders) { Asset asset = assetRepo.findById(order.getAssetId()).orElseThrow(new BusinessException("藏品不存在")); UserBalance userBalance = userBalanceRepo.findById(asset.getUserId()).orElse(null); if (userBalance == null) { userBalance = userBalanceRepo.saveAndFlush(new UserBalance(asset.getUserId(), BigDecimal.ZERO, BigDecimal.ZERO)); } BigDecimal amount = order.getTotalPrice() .subtract(order.getGasPrice()) .multiply(BigDecimal.valueOf(100 - order.getRoyalties() - order.getServiceCharge())) .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP); totalAmount = totalAmount.add(order.getTotalPrice()); royaltiesAmount = order.getTotalPrice() .subtract(order.getGasPrice()) .multiply(BigDecimal.valueOf(order.getRoyalties())) .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP); serviceChargeAmount = order.getTotalPrice() .subtract(order.getGasPrice()) .multiply(BigDecimal.valueOf(order.getServiceCharge())) .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP); userBalance.setLastBalance(userBalance.getBalance()); userBalance.setBalance(userBalance.getBalance().add(amount)); userBalanceRepo.saveAndFlush(userBalance); balanceRecordRepo.save(BalanceRecord.builder() .time(LocalDateTime.now()) .userId(asset.getUserId()) .orderId(order.getId()) .amount(amount) .balance(userBalance.getBalance()) .lastBalance(userBalance.getLastBalance()) .type(BalanceType.SELL) .build()); } settleRecordRepo.save(new SettleRecord(date, orders.size(), totalAmount, royaltiesAmount, serviceChargeAmount)); } @Transactional public ExportWithdraw exportWithdraw(String remark) throws IOException, InvalidFormatException { List balanceList = userBalanceRepo.findByBalanceGreaterThan(BigDecimal.ZERO); List withdrawList = new ArrayList<>(); balanceList.forEach(userBalance -> { userBankCardRepo.findByUserId(userBalance.getUserId()).stream().findFirst() .ifPresent(userBankCard -> withdrawList.add(new UserWithdraw(userBalance.getUserId(), userBankCard.getRealName(), userBankCard.getBankNo(), userBalance.getBalance()))); }); File tmpDir = TempFile.createTempDirectory("export_" + RandomStringUtils.randomAlphabetic(8)); File file1 = new File(tmpDir, DateTimeUtils.format(LocalDate.now(), "yyyyMMdd") + "结算.xlsx"); File file2 = new File(tmpDir, DateTimeUtils.format(LocalDate.now(), "yyyyMMdd") + "结算导入.xls"); EasyExcel.write(file1, UserWithdraw.class) .sheet("sheet").doWrite(withdrawList); InputStream inputStream = getClass().getResourceAsStream("/批量付款到对私银行账户模板.xls"); Workbook workbook = WorkbookFactory.create(inputStream); Sheet sheet = workbook.getSheetAt(0); for (int i = 0; i < withdrawList.size(); i++) { Row row = Optional.ofNullable(sheet.getRow(i + 1)).orElse(sheet.createRow(i + 1)); Optional.ofNullable(row.getCell(1)) .orElse(row.createCell(1)) .setCellValue(withdrawList.get(i).getName()); Optional.ofNullable(row.getCell(2)) .orElse(row.createCell(2)) .setCellValue(withdrawList.get(i).getBankNo()); Optional.ofNullable(row.getCell(3)) .orElse(row.createCell(3)) .setCellValue(withdrawList.get(i).getAmount().doubleValue()); Optional.ofNullable(row.getCell(4)) .orElse(row.createCell(4)) .setCellValue(withdrawList.get(i).getUserId()); } inputStream.close(); FileOutputStream os = new FileOutputStream(file2); workbook.write(os); workbook.close(); File zipFile = ZipUtil.zip(tmpDir); String url = storageService.uploadFromInputStream(new FileInputStream(zipFile), "upload/" + DateTimeUtils.format(LocalDateTime.now(), "yyyyMMddHHmm") + "_" + RandomStringUtils.randomNumeric(8) + ".zip"); ExportWithdraw exportWithdraw = exportWithdrawRepo.save(new ExportWithdraw(url, remark)); for (UserBalance userBalance : balanceList) { BigDecimal amount = userBalance.getBalance(); userBalance.setLastBalance(userBalance.getBalance()); userBalance.setBalance(BigDecimal.ZERO); userBalanceRepo.saveAndFlush(userBalance); balanceRecordRepo.save(BalanceRecord.builder() .time(LocalDateTime.now()) .userId(userBalance.getUserId()) .amount(amount.negate()) .balance(BigDecimal.ZERO) .lastBalance(userBalance.getLastBalance()) .type(BalanceType.WITHDRAW) .build()); } tmpDir.delete(); zipFile.delete(); return exportWithdraw; } @Transactional public void importFail(MultipartFile withdrawFile, MultipartFile settleFile) throws IOException { List failSettleList = EasyExcel.read(settleFile.getInputStream()) .head(SandPaySettle.class).sheet() .doReadSync(); failSettleList = failSettleList.stream() .filter(s -> "失败".equals(s.getStatus().trim())) .collect(Collectors.toList()); List withdrawList = EasyExcel.read(withdrawFile.getInputStream()) .head(UserWithdraw.class).sheet() .doReadSync(); List failWithdraw = new ArrayList<>(); for (SandPaySettle sandPaySettle : failSettleList) { List list; if (StringUtils.isNotBlank(sandPaySettle.getRemark()) && Pattern.matches("^\\d+$", sandPaySettle.getRemark().trim())) { Long userId = Long.parseLong(sandPaySettle.getRemark().trim()); list = withdrawList.stream().filter(i -> i.getUserId().equals(userId)) .collect(Collectors.toList()); } else { list = withdrawList.stream().filter(i -> i.getBankNo().equals(sandPaySettle.getBankNo()) && i.getName().equals(sandPaySettle.getName()) && i.getAmount().compareTo(sandPaySettle.getAmount()) == 0) .collect(Collectors.toList()); } if (list.size() != 1) { throw new BusinessException("不唯一:" + sandPaySettle.getName() + "," + sandPaySettle.getBankNo()); } else { failWithdraw.add(list.get(0)); } } for (UserWithdraw withdraw : failWithdraw) { UserBalance userBalance = userBalanceRepo.findById(withdraw.getUserId()) .orElse(new UserBalance(withdraw.getUserId(), BigDecimal.ZERO, BigDecimal.ZERO)); userBalance.setLastBalance(userBalance.getBalance()); userBalance.setBalance(userBalance.getBalance().add(withdraw.getAmount())); userBalanceRepo.saveAndFlush(userBalance); balanceRecordRepo.save(BalanceRecord.builder() .time(LocalDateTime.now()) .userId(userBalance.getUserId()) .amount(withdraw.getAmount()) .balance(userBalance.getBalance()) .lastBalance(userBalance.getLastBalance()) .type(BalanceType.RETURN) .build()); } } }