UserBalanceService.java 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. package com.izouma.nineth.service;
  2. import cn.hutool.core.util.ZipUtil;
  3. import com.alibaba.excel.EasyExcel;
  4. import com.izouma.nineth.domain.*;
  5. import com.izouma.nineth.dto.SandPaySettle;
  6. import com.izouma.nineth.dto.UserBankCard;
  7. import com.izouma.nineth.dto.UserWithdraw;
  8. import com.izouma.nineth.enums.BalanceType;
  9. import com.izouma.nineth.enums.CollectionSource;
  10. import com.izouma.nineth.enums.OrderStatus;
  11. import com.izouma.nineth.exception.BusinessException;
  12. import com.izouma.nineth.repo.*;
  13. import com.izouma.nineth.service.storage.StorageService;
  14. import com.izouma.nineth.utils.DateTimeUtils;
  15. import lombok.AllArgsConstructor;
  16. import lombok.extern.slf4j.Slf4j;
  17. import org.apache.commons.io.IOUtils;
  18. import org.apache.commons.lang3.RandomStringUtils;
  19. import org.apache.commons.lang3.StringUtils;
  20. import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
  21. import org.apache.poi.ss.usermodel.Row;
  22. import org.apache.poi.ss.usermodel.Sheet;
  23. import org.apache.poi.ss.usermodel.Workbook;
  24. import org.apache.poi.ss.usermodel.WorkbookFactory;
  25. import org.apache.poi.util.TempFile;
  26. import org.springframework.stereotype.Service;
  27. import org.springframework.web.bind.annotation.RequestPart;
  28. import org.springframework.web.multipart.MultipartFile;
  29. import javax.servlet.http.HttpServletResponse;
  30. import javax.transaction.Transactional;
  31. import java.io.*;
  32. import java.math.BigDecimal;
  33. import java.math.RoundingMode;
  34. import java.time.LocalDate;
  35. import java.time.LocalDateTime;
  36. import java.time.temporal.ChronoUnit;
  37. import java.util.ArrayList;
  38. import java.util.Arrays;
  39. import java.util.List;
  40. import java.util.Optional;
  41. import java.util.regex.Pattern;
  42. import java.util.stream.Collectors;
  43. import java.util.zip.ZipEntry;
  44. import java.util.zip.ZipOutputStream;
  45. @Service
  46. @Slf4j
  47. @AllArgsConstructor
  48. public class UserBalanceService {
  49. private final UserBalanceRepo userBalanceRepo;
  50. private final BalanceRecordRepo balanceRecordRepo;
  51. private final OrderRepo orderRepo;
  52. private final AssetRepo assetRepo;
  53. private final UserBankCardRepo userBankCardRepo;
  54. private final SettleRecordRepo settleRecordRepo;
  55. private final StorageService storageService;
  56. private final ExportWithdrawRepo exportWithdrawRepo;
  57. public void settle(LocalDate start, LocalDate end) {
  58. for (long i = 0; i <= ChronoUnit.DAYS.between(start, end); i++) {
  59. LocalDate date = start.plusDays(i);
  60. if (settleRecordRepo.findByDate(date).isPresent()) {
  61. throw new BusinessException(DateTimeUtils.format(date, "yyyy-MM-dd") + "已经结算过");
  62. }
  63. }
  64. for (long i = 0; i <= ChronoUnit.DAYS.between(start, end); i++) {
  65. LocalDate date = start.plusDays(i);
  66. settle(date);
  67. }
  68. }
  69. public void settle(LocalDate date) {
  70. if (settleRecordRepo.findByDate(date).isPresent()) {
  71. throw new BusinessException(DateTimeUtils.format(date, "yyyy-MM-dd") + "已经结算过");
  72. }
  73. List<Order> orders = orderRepo.findByCreatedAtBetweenAndSourceAndStatusIn(date.atStartOfDay(),
  74. date.atTime(23, 59, 59, 9999), CollectionSource.TRANSFER,
  75. Arrays.asList(OrderStatus.PROCESSING, OrderStatus.FINISH));
  76. BigDecimal totalAmount = BigDecimal.ZERO;
  77. BigDecimal royaltiesAmount = BigDecimal.ZERO;
  78. BigDecimal serviceChargeAmount = BigDecimal.ZERO;
  79. for (Order order : orders) {
  80. Asset asset = assetRepo.findById(order.getAssetId()).orElseThrow(new BusinessException("藏品不存在"));
  81. UserBalance userBalance = userBalanceRepo.findById(asset.getUserId()).orElse(null);
  82. if (userBalance == null) {
  83. userBalance = userBalanceRepo.saveAndFlush(new UserBalance(asset.getUserId(), BigDecimal.ZERO, BigDecimal.ZERO));
  84. }
  85. BigDecimal amount = order.getTotalPrice()
  86. .subtract(order.getGasPrice())
  87. .multiply(BigDecimal.valueOf(100 - order.getRoyalties() - order.getServiceCharge()))
  88. .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
  89. totalAmount = totalAmount.add(order.getTotalPrice());
  90. royaltiesAmount = order.getTotalPrice()
  91. .subtract(order.getGasPrice())
  92. .multiply(BigDecimal.valueOf(order.getRoyalties()))
  93. .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
  94. serviceChargeAmount = order.getTotalPrice()
  95. .subtract(order.getGasPrice())
  96. .multiply(BigDecimal.valueOf(order.getServiceCharge()))
  97. .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
  98. userBalance.setLastBalance(userBalance.getBalance());
  99. userBalance.setBalance(userBalance.getBalance().add(amount));
  100. userBalanceRepo.saveAndFlush(userBalance);
  101. balanceRecordRepo.save(BalanceRecord.builder()
  102. .time(LocalDateTime.now())
  103. .userId(asset.getUserId())
  104. .orderId(order.getId())
  105. .amount(amount)
  106. .balance(userBalance.getBalance())
  107. .lastBalance(userBalance.getLastBalance())
  108. .type(BalanceType.SELL)
  109. .build());
  110. }
  111. settleRecordRepo.save(new SettleRecord(date, orders.size(), totalAmount, royaltiesAmount, serviceChargeAmount));
  112. }
  113. @Transactional
  114. public ExportWithdraw exportWithdraw(String remark) throws IOException, InvalidFormatException {
  115. List<UserBalance> balanceList = userBalanceRepo.findByBalanceGreaterThan(BigDecimal.ZERO);
  116. List<UserWithdraw> withdrawList = new ArrayList<>();
  117. balanceList.forEach(userBalance -> {
  118. userBankCardRepo.findByUserId(userBalance.getUserId()).stream().findFirst()
  119. .ifPresent(userBankCard -> withdrawList.add(new UserWithdraw(userBalance.getUserId(), userBankCard.getRealName(),
  120. userBankCard.getBankNo(), userBalance.getBalance())));
  121. });
  122. File tmpDir = TempFile.createTempDirectory("export_" + RandomStringUtils.randomAlphabetic(8));
  123. File file1 = new File(tmpDir, DateTimeUtils.format(LocalDate.now(), "yyyyMMdd") + "结算.xlsx");
  124. File file2 = new File(tmpDir, DateTimeUtils.format(LocalDate.now(), "yyyyMMdd") + "结算导入.xls");
  125. EasyExcel.write(file1, UserWithdraw.class)
  126. .sheet("sheet").doWrite(withdrawList);
  127. InputStream inputStream = getClass().getResourceAsStream("/批量付款到对私银行账户模板.xls");
  128. Workbook workbook = WorkbookFactory.create(inputStream);
  129. Sheet sheet = workbook.getSheetAt(0);
  130. for (int i = 0; i < withdrawList.size(); i++) {
  131. Row row = Optional.ofNullable(sheet.getRow(i + 1)).orElse(sheet.createRow(i + 1));
  132. Optional.ofNullable(row.getCell(1))
  133. .orElse(row.createCell(1))
  134. .setCellValue(withdrawList.get(i).getName());
  135. Optional.ofNullable(row.getCell(2))
  136. .orElse(row.createCell(2))
  137. .setCellValue(withdrawList.get(i).getBankNo());
  138. Optional.ofNullable(row.getCell(3))
  139. .orElse(row.createCell(3))
  140. .setCellValue(withdrawList.get(i).getAmount().doubleValue());
  141. Optional.ofNullable(row.getCell(4))
  142. .orElse(row.createCell(4))
  143. .setCellValue(withdrawList.get(i).getUserId());
  144. }
  145. inputStream.close();
  146. FileOutputStream os = new FileOutputStream(file2);
  147. workbook.write(os);
  148. workbook.close();
  149. File zipFile = ZipUtil.zip(tmpDir);
  150. String url = storageService.uploadFromInputStream(new FileInputStream(zipFile),
  151. "upload/" + DateTimeUtils.format(LocalDateTime.now(), "yyyyMMddHHmm") + "_" + RandomStringUtils.randomNumeric(8) + ".zip");
  152. ExportWithdraw exportWithdraw = exportWithdrawRepo.save(new ExportWithdraw(url, remark));
  153. for (UserBalance userBalance : balanceList) {
  154. BigDecimal amount = userBalance.getBalance();
  155. userBalance.setLastBalance(userBalance.getBalance());
  156. userBalance.setBalance(BigDecimal.ZERO);
  157. userBalanceRepo.saveAndFlush(userBalance);
  158. balanceRecordRepo.save(BalanceRecord.builder()
  159. .time(LocalDateTime.now())
  160. .userId(userBalance.getUserId())
  161. .amount(amount.negate())
  162. .balance(BigDecimal.ZERO)
  163. .lastBalance(userBalance.getLastBalance())
  164. .type(BalanceType.WITHDRAW)
  165. .build());
  166. }
  167. tmpDir.delete();
  168. zipFile.delete();
  169. return exportWithdraw;
  170. }
  171. @Transactional
  172. public void importFail(MultipartFile withdrawFile, MultipartFile settleFile) throws IOException {
  173. List<SandPaySettle> failSettleList = EasyExcel.read(settleFile.getInputStream())
  174. .head(SandPaySettle.class).sheet()
  175. .doReadSync();
  176. failSettleList = failSettleList.stream()
  177. .filter(s -> "失败".equals(s.getStatus().trim()))
  178. .collect(Collectors.toList());
  179. List<UserWithdraw> withdrawList = EasyExcel.read(withdrawFile.getInputStream())
  180. .head(UserWithdraw.class).sheet()
  181. .doReadSync();
  182. List<UserWithdraw> failWithdraw = new ArrayList<>();
  183. for (SandPaySettle sandPaySettle : failSettleList) {
  184. List<UserWithdraw> list;
  185. if (StringUtils.isNotBlank(sandPaySettle.getRemark()) && Pattern.matches("^\\d+$", sandPaySettle.getRemark().trim())) {
  186. Long userId = Long.parseLong(sandPaySettle.getRemark().trim());
  187. list = withdrawList.stream().filter(i -> i.getUserId().equals(userId))
  188. .collect(Collectors.toList());
  189. } else {
  190. list = withdrawList.stream().filter(i -> i.getBankNo().equals(sandPaySettle.getBankNo())
  191. && i.getName().equals(sandPaySettle.getName())
  192. && i.getAmount().compareTo(sandPaySettle.getAmount()) == 0)
  193. .collect(Collectors.toList());
  194. }
  195. if (list.size() != 1) {
  196. throw new BusinessException("不唯一:" + sandPaySettle.getName() + "," + sandPaySettle.getBankNo());
  197. } else {
  198. failWithdraw.add(list.get(0));
  199. }
  200. }
  201. for (UserWithdraw withdraw : failWithdraw) {
  202. UserBalance userBalance = userBalanceRepo.findById(withdraw.getUserId())
  203. .orElse(new UserBalance(withdraw.getUserId(), BigDecimal.ZERO, BigDecimal.ZERO));
  204. userBalance.setLastBalance(userBalance.getBalance());
  205. userBalance.setBalance(userBalance.getBalance().add(withdraw.getAmount()));
  206. userBalanceRepo.saveAndFlush(userBalance);
  207. balanceRecordRepo.save(BalanceRecord.builder()
  208. .time(LocalDateTime.now())
  209. .userId(userBalance.getUserId())
  210. .amount(withdraw.getAmount())
  211. .balance(userBalance.getBalance())
  212. .lastBalance(userBalance.getLastBalance())
  213. .type(BalanceType.RETURN)
  214. .build());
  215. }
  216. }
  217. }