package com.ruoyi.system.service.impl; import cn.hutool.core.date.DateUtil; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.common.basic.PageInfo; import com.ruoyi.common.config.SmsProperties; import com.ruoyi.common.constant.AmountConstant; import com.ruoyi.common.constant.CacheConstants; import com.ruoyi.common.core.redis.RedisCache; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.*; import com.ruoyi.common.utils.uuid.UUID; import com.ruoyi.system.dto.*; import com.ruoyi.system.mapper.TBillMapper; import com.ruoyi.system.model.*; import com.ruoyi.system.query.TBillQuery; import com.ruoyi.system.query.TInvoiceToBillQuery; import com.ruoyi.system.service.*; import com.taxi591.bankapi.dto.ChargeBillRequest; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import javax.validation.constraints.NotEmpty; import java.math.BigDecimal; import java.text.ParseException; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.function.Consumer; import java.util.stream.Collectors; /** *

* 租金账单 服务实现类 *

* * @author xiaochen * @since 2025-01-17 */ @Service @Slf4j public class TBillServiceImpl extends ServiceImpl implements TBillService { @Autowired RedisCache redisCache; @Autowired TBillMapper tBillMapper; @Autowired TBillDetailService tBillDetailService; @Autowired TFlowManagementService tFlowManagementService; @Autowired TBankFlowService tBankFlowService; @Autowired TBillConfirmService tBillConfirmService; @Autowired TPayOrderService tPayOrderService; @Autowired TOrderBillService orderBillService; @Autowired TInvoiceToBillService tInvoiceToBillService; @Resource SmsUtil smsUtil; @Resource TencentMailUtil mailUtil; public PageInfo queryPage(TBillQuery query){ PageInfo pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize()); PageInfo info = tBillMapper.page(pageInfo, query); return info; } @Override public List getBillIds(TBillQuery query) { List billDtos = tBillMapper.getBillList(query); return billDtos.stream().map(TBillDto::getId).collect(Collectors.toList()); } @Override public PageInfo invoiceList(TBillQuery query) { PageInfo pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize()); List list = tBillMapper.invoiceList(query,pageInfo); pageInfo.setRecords(list); return pageInfo; } private static final String[] ignorePro = {"payableFeesMoney","payableFeesPenalty","payFeesMoney","outstandingMoney"}; /** * * 更新类型 1.仅更新信息及状态 2.更新信息、金额及状态 * 为1时,仅更新非金额之外的信息及状态 * 为2时,传入的金额,是需要增加或减少的金额,非计算后的金额 * 当账单状态为已缴费后,不做任何更新 * @param tBill * @param type * @return */ public TBill lockAndUpdateInfo(@NotNull TBill tBill, @NotNull Integer type){ if (StringUtils.isEmpty(tBill.getId())){ throw new ServiceException("账单主键ID不能为空"); } String requestId = UUID.fastUUID().toString(); String lockkey = CacheConstants.BILL_UPDATE_LOCK_KEY + tBill.getId(); boolean isok = redisCache.trylockLoop(lockkey, requestId, 60); if (isok){ try { TBill save = new TBill(); TBill presist = getById(tBill.getId()); //如果账单是已缴费状态,本方法不再进行更新账单 if (presist.getPayFeesStatus().equals("3")){ throw new ServiceException("该账单已缴费完成"); } switch (type){ // 仅更新除金额字段外的属性 case 1: { BeanUtils.copyProperties(tBill,save,ignorePro); } break; // 计算并更新金额、存入违约金、已缴费金额会自动计算欠费,如计算出无欠费则会更新状态为已缴费 case 2: { BeanUtils.copyProperties(tBill,save); // 处理应缴费 ,注意一般情况下不会修改该金额 if (tBill.getPayableFeesMoney()!=null){ presist.setPayableFeesMoney(presist.getPayableFeesMoney()!=null?presist.getPayableFeesMoney():BigDecimal.ZERO); BigDecimal result = presist.getPayableFeesMoney().add(tBill.getPayableFeesMoney()); save.setPayableFeesMoney(result); //计算欠费 = 应缴费(新)+违约金-已缴费 BigDecimal outstand = save.getPayableFeesMoney() .add(presist.getPayableFeesPenalty()) .subtract(presist.getPayFeesMoney()); save.setOutstandingMoney(outstand); } //处理应缴违约金 ,不能和缴费金额一起传 if (tBill.getPayableFeesPenalty()!=null){ presist.setPayableFeesPenalty(presist.getPayableFeesPenalty()!=null?presist.getPayableFeesPenalty():BigDecimal.ZERO); BigDecimal result = presist.getPayableFeesPenalty().add(tBill.getPayableFeesPenalty()); save.setPayableFeesPenalty(result); //计算欠费 = 应缴费+违约金(新)-已缴费 BigDecimal outstand = presist.getPayableFeesMoney() .add(save.getPayableFeesPenalty()) .subtract(presist.getPayFeesMoney()); save.setOutstandingMoney(outstand); } //处理缴费金额 if (tBill.getPayFeesMoney()!=null){ presist.setPayFeesMoney(presist.getPayFeesMoney()!=null?presist.getPayFeesMoney():BigDecimal.ZERO); BigDecimal result = presist.getPayFeesMoney().add(tBill.getPayFeesMoney()); save.setPayFeesMoney(result); //缴费后的欠费 =(应缴费+违约金)-已缴费金额 BigDecimal outstand = presist.getPayableFeesMoney() .add(presist.getPayableFeesPenalty()) .subtract(save.getPayFeesMoney()); save.setOutstandingMoney(outstand); //抵扣金额就是缴费金额 save.setDeductionMoney(tBill.getPayableFeesMoney()); save.setPreOutstand(presist.getOutstandingMoney()); if (outstand.compareTo(BigDecimal.ZERO)<=0){ save.setPayFeesStatus("3"); } } } break; } updateById(save); return save; }finally { redisCache.unlock(lockkey,requestId); } } return null; } @Override @Transactional(rollbackFor = Exception.class) public Boolean lockAndUpdateByAmountBatch(List bills, BigDecimal amount, Consumer consumer) { List result = bills.stream().sorted(Comparator.comparing(TBill::getCreateTime)) .collect(Collectors.toList()); BigDecimal remain = amount; for (TBill bill : result) { //如果剩余金额小于账单欠费金额,则 if (remain.compareTo(bill.getOutstandingMoney())<=0){ TBill param = new TBill(); param.setId(bill.getId()); param.setPayFeesMoney(remain); TBill tBill = lockAndUpdateInfo(param, 2); if (consumer!=null){ consumer.accept(tBill); } break; } remain = remain.subtract(bill.getOutstandingMoney()); TBill param = new TBill(); param.setId(bill.getId()); param.setPayFeesMoney(bill.getOutstandingMoney()); TBill tBill = lockAndUpdateInfo(param, 2); if (consumer!=null){ consumer.accept(tBill); } } return true; } @Override @Transactional(rollbackFor = Exception.class) public void saveBill(TbillSaveDto bill) { save(bill); if (bill.getBillType().equals("3")){ if (bill.getDetails()==null || bill.getDetails().size()==0){ throw new ServiceException("生活费用列表不能为空"); } for (TBillDetail detail : bill.getDetails()) { detail.setBillId(bill.getId()); tBillDetailService.save(detail); } } } @Override @Transactional(rollbackFor = Exception.class) public boolean checkOfflinePay(OfflinePayCheckDto dto) { TBankFlow bankflow = tBankFlowService.getById(dto.getFlowId()); TBillDto bill = getDetailByBillId(dto.getBillId()); if (bankflow.getRemainingMoney().compareTo(BigDecimal.ZERO)<=0){ throw new ServiceException("该流水已无可抵扣剩余金额"); } if (bankflow.getRemainingMoney().compareTo(dto.getAmount())<0){ throw new ServiceException("实付金额不能高于于流水可抵扣剩余金额"); } //如果实付金额大于欠费金额 if (dto.getAmount().compareTo(bill.getOutstandingMoney())>0){ throw new ServiceException("实付金额不能高于该账单欠费金额"); } TBill billSave = new TBill(); billSave.setId(bill.getId()); billSave.setPayFeesMoney(dto.getAmount()); billSave.setBankSerialNumber(bankflow.getBankSerialNumber()); billSave.setPayFeesTime(bankflow.getPayTime()); billSave.setVoucher(dto.getVoucher()); billSave.setPayFeesType(2); TBill back = lockAndUpdateInfo(billSave, 2); //更新银行流水的已抵扣金额和剩余可抵扣金额 TBankFlow saveBankFlow = new TBankFlow(); saveBankFlow.setId(bankflow.getId()); saveBankFlow.setDeductionMoney(bankflow.getDeductionMoney().add(dto.getAmount())); saveBankFlow.setRemainingMoney(bankflow.getRemainingMoney().subtract(dto.getAmount())); tBankFlowService.updateById(saveBankFlow); //存流水 TFlowManagement save = new TFlowManagement(); save.setPayType(3); save.setPayer(dto.getPayer()); save.setPayTime(bankflow.getPayTime()); save.setSysSerialNumber(OrderNos.getDid(30)); save.setBankSerialNumber(bankflow.getBankSerialNumber()); save.setFlowType(2); save.setPaymentBillId(back.getId()); save.setDeductionMoney(back.getDeductionMoney()); save.setFlowMoney(dto.getAmount()); save.setRemainingMoney(back.getOutstandingMoney()); save.setPreOutstand(back.getPreOutstand()); tFlowManagementService.save(save); return true; } @Override @Transactional(rollbackFor = Exception.class) public void completePay(ChargeBillRequest billRequest) { String orderNo = billRequest.getMessage().getInfo().getInput1(); String uuid = UUID.fastUUID().toString(); boolean lock = redisCache.trylockLoop(CacheConstants.COMPLETE_PAY_LOCK_KEY + orderNo, uuid, 60); if (lock){ TPayOrder order = tPayOrderService.getById(orderNo); if (order==null){ throw new ServiceException("订单不存在"); } if (StringUtils.isNotEmpty(order.getPayNo())){ log.info("订单号已处理:{}",orderNo); return; } /** * 更新订单状态 */ TPayOrder save = new TPayOrder(); save.setId(order.getId()); save.setStatus(1); save.setPayNo(billRequest.getMessage().getInfo().getTraceNo()); save.setPayType(billRequest.getMessage().getHead().getChannel()); try { save.setPayTime(DateUtils.parseDate(billRequest.getMessage().getHead().getTimeStamp(),"yyyyMMddHHmmssSSS")); } catch (ParseException e) { throw new ServiceException("日期格式化错误"); } save.setCallbackTime(new Date()); BigDecimal payAmount = new BigDecimal(billRequest.getMessage().getInfo().getPayBillAmt()); save.setActPayAmount(payAmount .multiply(AmountConstant.b100).longValue()); save.setStatus(1); save.setPayInfo(billRequest.getMessage().toString()); tPayOrderService.updateById(save); /** * 更新账单状态 */ List orderBills = orderBillService.getByOrderNo(order.getId()); List bills = orderBills.stream().map(ob -> getById(ob.getBillId())).collect(Collectors.toList()); lockAndUpdateByAmountBatch(bills,payAmount,(bill)->{ TFlowManagement saveFlow = new TFlowManagement(); saveFlow.setPayType(1); saveFlow.setPayer(order.getUserId()); saveFlow.setPayTime(DateUtils.dateToLocalDateTime(save.getPayTime())); saveFlow.setSysSerialNumber(OrderNos.getDid(30)); saveFlow.setBankSerialNumber(save.getPayNo()); saveFlow.setFlowType(2); saveFlow.setPaymentBillId(bill.getId()); saveFlow.setDeductionMoney(bill.getDeductionMoney()); saveFlow.setFlowMoney(payAmount); saveFlow.setRemainingMoney(bill.getOutstandingMoney()); saveFlow.setPreOutstand(bill.getPreOutstand()); tFlowManagementService.save(saveFlow); }); TBankFlow bankFlow = new TBankFlow(); bankFlow.setPayType(1); bankFlow.setPayer(order.getUserId()); bankFlow.setPayTime(DateUtils.dateToLocalDateTime(save.getPayTime())); bankFlow.setBankSerialNumber(save.getPayNo()); bankFlow.setFlowMoney(payAmount); bankFlow.setFlowStatus(1); tBankFlowService.save(bankFlow); } } /** * 根据发票编号查询账单列表 * @param invoiceId * @return */ @Override public PageInfo getBillByInvoiceId(String invoiceId){ PageInfo pageInfo = new PageInfo<>(); ArrayList bills = new ArrayList<>(); TInvoiceToBillQuery query = new TInvoiceToBillQuery(); query.setInvoiceId(invoiceId); List tInvoiceToBills = tInvoiceToBillService.makeQuery(query); for (TInvoiceToBill tInvoiceToBill : tInvoiceToBills) { TBill bill = getById(tInvoiceToBill.getBillId()); if (bill != null && bill.getId() != null){ TBillDto detailByBillId = getDetailByBillId(bill.getId()); bills.add(detailByBillId); } } pageInfo.setRecords(bills); return pageInfo; } @Override public Integer sendSmsByBillIds(SmsByBillDto dto) { int failNum = 0; for (String billId : dto.getBillIds()) { TBillDto bill = getDetailByBillId(billId); if (bill.getSmsLastTime()!=null && bill.getSmsStatus()==1 && (System.currentTimeMillis()-bill.getSmsLastTime().getTime()5?bill.getPartyTwoName().substring(0,5):bill.getPartyTwoName(); smsUtil.sendSms(bill.getPhone(), "2365726", new String[]{name}); save.setSmsStatus(1); }catch (ServiceException e){ failNum++; save.setSmsStatus(2); } save.setSmsLastTime(new Date()); save.setSmsSendUserid(dto.getSendUserId()); lockAndUpdateInfo(save,1); } return failNum; } @Override public Integer sendMailBatchByBillIds(SmsByBillDto dto) { int failNum = 0; for (String billId : dto.getBillIds()) { TBillDto bill = getDetailByBillId(billId); if (bill.getMailLastTime()!=null && bill.getMailStatus()==1 && (System.currentTimeMillis()-bill.getMailLastTime().getTime()0){ throw new ServiceException("实付金额不能高于该账单欠费金额"); } } TBillDto bill = getDetailByBillId(dto.getBillId()); TBill billSave = new TBill(); billSave.setId(bill.getId()); billSave.setPayFeesMoney(dto.getAmount()); billSave.setBankSerialNumber(bankflow!=null?bankflow.getBankSerialNumber():null); billSave.setPayFeesTime(bankflow!=null?bankflow.getPayTime():DateUtils.dateToLocalDateTime(new Date())); billSave.setVoucher(dto.getVoucher()); billSave.setPayFeesType(2); back = lockAndUpdateInfo(billSave, 2); if (dto.getPayType()==1){ //更新银行流水的已抵扣金额和剩余可抵扣金额 TBankFlow saveBankFlow = new TBankFlow(); saveBankFlow.setId(bankflow.getId()); saveBankFlow.setDeductionMoney(bankflow.getDeductionMoney().add(dto.getAmount())); saveBankFlow.setRemainingMoney(bankflow.getRemainingMoney().subtract(dto.getAmount())); tBankFlowService.updateById(saveBankFlow); } //存流水 TFlowManagement save = new TFlowManagement(); save.setPayType(3); save.setPayer(dto.getPayer()); save.setPayTime(bankflow!=null?bankflow.getPayTime():DateUtils.dateToLocalDateTime(new Date())); save.setSysSerialNumber(OrderNos.getDid()); save.setBankSerialNumber(bankflow!=null?bankflow.getBankSerialNumber():null); save.setFlowType(dto.getPayType()==1?2:1); save.setPaymentBillId(back.getId()); save.setDeductionMoney(back.getDeductionMoney()); save.setFlowMoney(dto.getAmount()); save.setRemainingMoney(back.getOutstandingMoney()); save.setPreOutstand(back.getPreOutstand()); tFlowManagementService.save(save); return true; } @Override public BillStatisticsDto statistics() { BillStatisticsDto dto = new BillStatisticsDto(); dto.setRent(getBaseMapper().statisticsAllRent()); dto.setNopay(getBaseMapper().statisticsNoPay()); dto.setPayed(getBaseMapper().statisticsPayed()); dto.setOverdue(getBaseMapper().statisticsOverdue()); return dto; } }