package com.ruoyi.system.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.common.basic.PageInfo; import com.ruoyi.common.constant.AmountConstant; import com.ruoyi.common.constant.CacheConstants; import com.ruoyi.common.core.redis.RedisCache; import com.ruoyi.common.enums.DisabledEnum; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.*; import com.ruoyi.common.utils.uuid.UUID; import com.ruoyi.system.dto.BillStatisticsDto; import com.ruoyi.system.dto.CachPayDto; import com.ruoyi.system.dto.OfflinePayCheckDto; import com.ruoyi.system.dto.SmsByBillDto; import com.ruoyi.system.dto.TBillDto; import com.ruoyi.system.dto.TbillSaveDto; import com.ruoyi.system.mapper.TBillMapper; import com.ruoyi.system.model.TBankFlow; import com.ruoyi.system.model.TBill; import com.ruoyi.system.model.TBillDetail; import com.ruoyi.system.model.TFlowManagement; import com.ruoyi.system.model.TInvoiceToBill; import com.ruoyi.system.model.TOrderBill; import com.ruoyi.system.model.TPayOrder; import com.ruoyi.system.query.TBillQuery; import com.ruoyi.system.query.TInvoiceToBillQuery; import com.ruoyi.system.service.TBankFlowService; import com.ruoyi.system.service.TBillConfirmService; import com.ruoyi.system.service.TBillDetailService; import com.ruoyi.system.service.TBillService; import com.ruoyi.system.service.TFlowManagementService; import com.ruoyi.system.service.TInvoiceToBillService; import com.ruoyi.system.service.TOrderBillService; import com.ruoyi.system.service.TPayOrderService; import com.ruoyi.system.vo.ScreenRentRankVO; import com.taxi591.bankapi.dto.ChargeBillRequest; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import org.joda.time.LocalDateTime; 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.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; } public PageInfo queryPageForApplet(TBillQuery query){ PageInfo pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize()); PageInfo info = tBillMapper.pageForApplet(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(); save.setId(tBill.getId()); 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.getPayFeesMoney()); save.setPreOutstand(presist.getOutstandingMoney()); if (outstand.compareTo(BigDecimal.ZERO)<=0){ save.setPayFeesStatus("3"); } } } break; } save.setBusinessDeptId(presist.getBusinessDeptId()); 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) { bill.setManualAddition(DisabledEnum.YES.getCode()); 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) { TBillDto bill = getDetailByBillId(dto.getBillId()); if (dto.getPayType()==1){ //银行 if (StringUtils.isEmpty(dto.getFlowId())){ throw new ServiceException("银行流水ID不能为空"); } TBankFlow bankflow = tBankFlowService.getById(dto.getFlowId()); 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())); BigDecimal subtract = bankflow.getRemainingMoney().subtract(dto.getAmount()); saveBankFlow.setRemainingMoney(subtract); if (BigDecimal.ZERO.compareTo(subtract) == 0){ saveBankFlow.setFlowStatus(1); } tBankFlowService.updateById(saveBankFlow); //更新银行流水的已抵扣金额和剩余可抵扣金额 //存流水 TFlowManagement save = new TFlowManagement(); save.setPayType(3); save.setPayer(dto.getPayer()); save.setBusinessDeptId(bill.getBusinessDeptId()); 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; } //现金支付 TBill billSave = new TBill(); billSave.setId(bill.getId()); billSave.setPayFeesMoney(dto.getAmount()); billSave.setPayFeesTime(dto.getPayTime()!=null?dto.getPayTime():DateUtils.dateToLocalDateTime(new Date())); billSave.setVoucher(dto.getVoucher()); billSave.setPayFeesType(2); TBill back = lockAndUpdateInfo(billSave, 2); TFlowManagement save = new TFlowManagement(); save.setPayType(3); save.setPayer(dto.getPayer()); save.setPayTime(billSave.getPayFeesTime()); save.setBusinessDeptId(bill.getBusinessDeptId()); save.setSysSerialNumber(OrderNos.getDid(30)); save.setFlowType(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 @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){ try { 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.setBusinessDeptId(bill.getBusinessDeptId()); 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); }finally { redisCache.unlock(CacheConstants.COMPLETE_PAY_LOCK_KEY + orderNo,uuid); } } } /** * 根据发票编号查询账单列表 * @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())); BigDecimal subtract = bankflow.getRemainingMoney().subtract(dto.getAmount()); saveBankFlow.setRemainingMoney(subtract); if (BigDecimal.ZERO.compareTo(subtract) == 0){ saveBankFlow.setFlowStatus(1); } tBankFlowService.updateById(saveBankFlow); } //存流水 TFlowManagement save = new TFlowManagement(); save.setPayType(3); save.setPayer(dto.getPayer()); save.setBusinessDeptId(bill.getBusinessDeptId()); 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(TBillQuery query) { BillStatisticsDto dto = new BillStatisticsDto(); dto.setRent(getBaseMapper().statisticsAllRent(query)); dto.setNopay(getBaseMapper().statisticsNoPay(query)); dto.setPayed(getBaseMapper().statisticsPayed(query)); dto.setOverdue(getBaseMapper().statisticsOverdue(query)); return dto; } @Override public Integer batchBillCount(String userId, List billIds) { return this.baseMapper.batchBillCount(userId,billIds); } /** * 街道租金排行 * @return */ @Override public List getStreetRentRank(String businessDeptId) { return baseMapper.getStreetRentRank(businessDeptId); } /** * 查询季付账单 * @param businessDeptId * @return */ @Override public List getJiFuBillList(String businessDeptId) { return baseMapper.getJiFuBillList(businessDeptId,null,null); } /** * 查询当前季度的季付账单 * @param businessDeptId * @param first * @param last * @return */ @Override public List getJiFuBillListByTime(String businessDeptId, Date first, Date last) { return baseMapper.getJiFuBillList(businessDeptId,first,last); } @Override public void editAmount(TbillSaveDto bill) { String requestId = UUID.fastUUID().toString(); String lockkey = CacheConstants.BILL_UPDATE_LOCK_KEY + bill.getId(); boolean isok = redisCache.trylockLoop(lockkey, requestId, 60); if (isok){ try { TBill presist = getById(bill.getId()); TBill save = new TBill(); save.setId(bill.getId()); BigDecimal preOutstand = presist.getOutstandingMoney(); // 如果传入的金额小于0,则是扣减,如果大于0,是增加金额,增加金额只加入欠费金额中 if (bill.getEditAmount().compareTo(BigDecimal.ZERO)<0 && presist.getPayableFeesPenalty().compareTo(BigDecimal.ZERO)>0){ BigDecimal prePayableFeesPenalty = presist.getPayableFeesPenalty(); //违约金大于调整金额,够减 if (prePayableFeesPenalty.compareTo(bill.getEditAmount().abs())>=0){ BigDecimal afterPenalty = prePayableFeesPenalty.add(bill.getEditAmount()); save.setPayableFeesPenalty(afterPenalty); }else{ BigDecimal afterPenalty = BigDecimal.ZERO; save.setPayableFeesPenalty(afterPenalty); } } BigDecimal afterOutstand = preOutstand.add(bill.getEditAmount()); save.setOutstandingMoney(afterOutstand); updateById(save); }finally { redisCache.unlock(lockkey,requestId); } } //todo 记录金额修改记录 // TFlowManagement flow = new TFlowManagement(); // flow.setPayType(3); // flow.setPayer("管理员修改"); // flow.setBusinessDeptId(presist.getBusinessDeptId()); // flow.setPayTime(DateUtils.dateToLocalDateTime(new Date())); // flow.setSysSerialNumber(OrderNos.getDid(30)); // flow.setFlowType(1); // flow.setPaymentBillId(back.getId()); // flow.setDeductionMoney(back.getDeductionMoney()); // flow.setFlowMoney(save.getOutstandingMoney()); // flow.setRemainingMoney(back.getOutstandingMoney()); // flow.setPreOutstand(back.getPreOutstand()); // flow.setCreateBy(SecurityUtils.getUsername()); // tFlowManagementService.save(flow); } }