package com.pj.project.tb_account; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.RandomUtil; import cn.hutool.extra.spring.SpringUtil; import cn.hutool.log.StaticLog; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.pj.api.open.ResultJson; import com.pj.project.tb_business.TbBusiness; import com.pj.project.tb_business.TbBusinessService; import com.pj.project.tb_business_car.TbBusinessCar; import com.pj.project.tb_business_car.TbBusinessCarService; import com.pj.project.tb_business_item.TbBusinessItem; import com.pj.project.tb_business_item.TbBusinessItemService; import com.pj.project.tb_deduction_bind.TbDeductionBind; import com.pj.project.tb_deduction_bind.TbDeductionBindService; import com.pj.project.tb_deduction_record.TbDeductionRecord; import com.pj.project.tb_fee_details.TbFeeDetails; import com.pj.project.tb_fee_details.TbFeeDetailsService; import com.pj.project.tb_goods.TbGoods; import com.pj.project.tb_goods.TbGoodsService; import com.pj.project.tb_invoice_order.TbInvoiceOrder; import com.pj.project.tb_invoice_order.TbInvoiceOrderService; import com.pj.project.tb_item.TbItem; import com.pj.utils.AesUtil; import com.pj.utils.cache.RedisUtil; import com.pj.utils.sg.NbUtil; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.math.BigDecimal; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.stream.Collectors; /** * 预充值自动缴费 */ @Component @Transactional public class AutomaticPay { @Resource TbBusinessCarService tbBusinessCarService; @Resource TbBusinessService tbBusinessService; @Resource TbDeductionBindService deductionBindService; @Resource TbBusinessItemService tbBusinessItemService; @Resource TbFeeDetailsService tbFeeDetailsService; @Resource TbAccountService tbAccountService; @Resource TbInvoiceOrderService invoiceOrderService; @Resource TbGoodsService tbGoodsService; private Integer feeType; /** * 异步扣费入口 * @param businessId 业务id * @param plate 车牌号 * @param feeType 收费类型,1-只收停车费、2-只收业务费,3-停车和业务费 */ //TODO 不要在此方法写逻辑 @Async public void run(String businessId, String plate, Integer feeType) { this.feeType = feeType; task(businessId,plate,feeType); } /** * 异步解绑入口 * @param plate */ @Async public void unbindRun(String plate){ if(isExistTask(null,plate)){ return; } autoUnbindCarByPlate(plate); delRedisTask(null,plate); } /** * 分配任务 */ private void task(String businessId, String plate, Integer feeType){ if(isExistTask(businessId,plate)){ return; } if(feeType==1){ doParkingFee(plate,0.0); }else if(feeType==2){ List cars = tbBusinessCarService.findOtherBusinessCar(businessId); for (TbBusinessCar car : cars){ this.doBusinessFee(businessId,car.getCarNo()); } }else if(feeType==3){ doOutFee(plate); } delRedisTask(businessId,plate); } /** * 收业务费 * @param businessId */ private void doBusinessFee(String businessId,String plate){ if(!NumberUtil.isNumber(businessId) || NbUtil.isNull(plate)){ return; } StaticLog.info("开始收取业务费:{}" , businessId); TbBusiness tbBusiness = tbBusinessService.getById(businessId); if(TbBusiness.PayStatus.NO_PAY.getCode()!=tbBusiness.getPayStatus()){ StaticLog.info("已收取过业务费,退出收费程序:{}" , businessId); return; } if(!NumberUtil.isNumber(tbBusiness.getItemPrice().toString())){ StaticLog.info("未获取到收费总金额或收费总金额非法,退出收费程序:{}" , businessId); return; } if(!deductionBindService.isBindCarByPlate(plate)){ StaticLog.info("业务车辆未绑定指定客户的预存款账户,退出收费程序:{}{}",businessId,tbBusiness.getCustomerName()); return; } List cars = tbBusinessCarService.findOtherBusinessCar(businessId); TbDeductionBind bind = deductionBindService.getBindCarByPlate(plate); TbAccount tbAccount = tbAccountService.getByCustomerId(bind.getCustomerId()); String key = AesUtil.reverse(tbAccount.getAccSalt()); String totalMoney = tbAccount.getTotalMoney(); BigDecimal parkingMoneyBig = new BigDecimal(0); BigDecimal totalMoneyBig = new BigDecimal(totalMoney); BigDecimal balance = totalMoneyBig.subtract(tbBusiness.getItemPrice()); boolean isOut = (TbGoods.DeductionTypeEnum.OUT_KK.getCode()==tbBusiness.getAutoDeductionType()) && this.feeType==3; if(isOut){ parkingMoneyBig = getParkings(cars); balance = balance.subtract(parkingMoneyBig); } if(balance.doubleValue()<0){ StaticLog.info("支付账户余额不足!,退出收费程序:{}" , businessId); deductionBindService.setFeeFailRecord(bind.getCustomerId(),plate, AesUtil.toStr(tbBusiness.getGoodsName())+":因支付账户余额不足,自动缴费失败。。"); return; } Date now = new Date(); String no = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + RandomUtil.randomNumbers(4); tbBusiness.setPayMoney(tbBusiness.getItemPrice()).setPayTime(now).setPayType(5) .setPayNo(no).setConfirmInput(1).setConfirmInputTime(now).setPayStatus( TbBusiness.PayStatus.HAS_PAY_CONFIRM.getCode()); tbBusiness.updateById(); List businessItems = tbBusinessItemService.findByBusinessId(businessId); for(TbBusinessItem businessItem:businessItems){ if(businessItem.getPayStatus()==1){ continue; } businessItem.setPayStatus(1).setPayTime(now); } tbBusinessItemService.updateBatchById(businessItems); if(isOut){ this.updateTbBusinessCars(cars); } //更新账户余额 tbAccount.setTotalMoney(AesUtil.encryptECB(balance.toString(),key)); tbAccount.updateById(); deductionBindService.setDeductMoney(bind.getCustomerId(),plate,tbBusiness.getItemPrice()); //生成收费明细 List tbFeeDetailsList = tbFeeDetailsService.autoChargeBusinessFee( businessItems,null,null,now); //生成扣费记录 createTbDeductionRecord(tbFeeDetailsList,tbAccount, totalMoneyBig,plate,bind.getCustomerName(),no); //生成开票信息 createTbInvoiceOrder(tbBusiness, cars, parkingMoneyBig, plate,bind.getCustomerId(),no,isOut); if(isOut){ deductionBindService.setDeductMoney(bind.getCustomerId(),plate,parkingMoneyBig); List parkFeeDetailsList = tbFeeDetailsService.autoChargeParkFee( cars,null,null,now); createTbDeductionRecord(parkFeeDetailsList,tbAccount, totalMoneyBig.subtract(tbBusiness.getItemPrice()),plate,bind.getCustomerName(),no); deductionBindService.autoUnbindCar(cars); } StaticLog.info("预充值自动缴费成功!,退出收费程序:{}{}" , businessId,tbAccount.getCustomerId()); } /** * 出场所收费,根据车牌号 * @param plate */ private void doOutFee(String plate){ if(NbUtil.isNull(plate)){ return; } TbBusinessCar tbBusinessCar = tbBusinessCarService.findTheLastRecord(plate); if(tbBusinessCar==null){ return; } List businessList = tbBusinessService.findOtherBusinessByCarId(tbBusinessCar.getId()); if(businessList==null){return;} for (TbBusiness business: businessList){ List cars = tbBusinessCarService.findOtherBusinessCar(business.getId()); if(cars==null)continue; for (TbBusinessCar car:cars){ doBusinessFee(business.getId(), car.getCarNo()); } } } /** * 收停车费,不判断免费车辆 * @param plate * @param parkingFee */ private void doParkingFee(String plate, double parkingFee){ if(NbUtil.isNull(plate)){ return; } TbBusinessCar car = tbBusinessCarService.findTheLastRecord(plate); if(car==null){ return; } StaticLog.info("开始自动缴纳停车费:{}" , car.getCarNo()); // if(car.getPay()==1 || car.getPayTime()!=null){ // StaticLog.info("该车辆已缴纳过停车费!,退出收费程序:{}" , car.getCarNo()); // return; // } TbAccount account = tbAccountService.getTbAccountByPlate(car.getCarNo()); if (account==null){ StaticLog.info("该车辆还未绑定客户预存款账户!,退出收费程序:{}" , car.getCarNo()); return; } BigDecimal parkFeeBig = null; if(parkingFee>0){ parkFeeBig = new BigDecimal(String.valueOf(parkingFee)); }else { parkFeeBig = caulatePrice(car, new Date()); } if(parkFeeBig==null ||parkFeeBig.doubleValue()<=0){ StaticLog.info("该车辆无需缴纳停车费!,退出收费程序:{}" , car.getCarNo()); return; } TbDeductionBind bind = deductionBindService.getBindCarByPlate(plate); BigDecimal totalBig = new BigDecimal("0"); String key = null; if(!NumberUtil.isNumber(account.getTotalMoney())){ key = AesUtil.reverse(account.getAccSalt()); totalBig = new BigDecimal(AesUtil.decryptECB(account.getTotalMoney(),key)); } BigDecimal balance = totalBig.subtract(parkFeeBig); if(balance.doubleValue()<0){ StaticLog.info("支付账户余额不足!,退出收费程序:{}" , car.getCarNo()); return; } String no = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + RandomUtil.randomNumbers(4); car.setMoney(parkFeeBig); updateTbBusinessCar(car); account.setTotalMoney(AesUtil.encryptECB(balance.toString(),key)); account.updateById(); deductionBindService.setDeductMoney(bind.getCustomerId(),plate,parkFeeBig); List cars = new ArrayList(); cars.add(car); List parkFeeDetailsList = tbFeeDetailsService.autoChargeParkFee( cars,null,null,new Date()); createTbDeductionRecord(parkFeeDetailsList,account, totalBig,plate,bind.getCustomerName(),no); createTbInvoiceOrderPark(car,account.getCustomerId(),no); deductionBindService.autoUnbindCar(cars); StaticLog.info("停车费自动缴费成功!,退出收费程序:{}" , car.getCarNo()); } /** * 生成扣费记录 * @param tbFeeDetailsList * @param tbAccount * @param totalMoneyBig */ private void createTbDeductionRecord(List tbFeeDetailsList,TbAccount tbAccount, BigDecimal totalMoneyBig,String plate,String customerName,String no ){ if(tbFeeDetailsList==null)return; int i = 0; String tempMoney = null; String bindIdStr = deductionBindService.getBindId(plate); for (TbFeeDetails feeDetails : tbFeeDetailsList){ TbDeductionRecord deductionRecord = BeanUtil.toBean(feeDetails,TbDeductionRecord.class); String totalStr = null; if(totalMoneyBig!=null && feeDetails.getNoTaxPrice()!=null){ if(i!=0){ totalMoneyBig = new BigDecimal(tempMoney); } totalStr = totalMoneyBig.subtract(feeDetails.getItemPrice()).toString(); tempMoney = totalStr; } deductionRecord.setId(null); deductionRecord.setDeductionBindId(bindIdStr); deductionRecord.setCustomerId(tbAccount.getCustomerId()); deductionRecord.setCustomerName(customerName); deductionRecord.setFeeDetailsId(feeDetails.getId()); deductionRecord.setOriginalMoney(totalMoneyBig.toString()); deductionRecord.setDeductMoney(feeDetails.getItemPrice()); deductionRecord.setTotalMoney(totalStr); deductionRecord.setReviewStatus(0); deductionRecord.setCarNo(plate); deductionRecord.setPreOrderNum(no); deductionRecord.insert(); feeDetails.setCarNo(plate); feeDetails.setPreOrderNum(no); feeDetails.setCustomerId(tbAccount.getCustomerId()); feeDetails.setCustomerName(customerName); tbFeeDetailsService.updateById(feeDetails); i++; } } /** * 更新停车费状态 */ private void updateTbBusinessCar(TbBusinessCar car){ if(car == null)return; car.setPay(1).setPayTime(new Date()); car.updateById(); } /** * 更新停车费状态 */ private void updateTbBusinessCars(List cars){ if(cars == null)return; for (TbBusinessCar car:cars){ updateTbBusinessCar(car); } } /** * 生成开票订单信息 * @param tbBusiness */ private void createTbInvoiceOrder(TbBusiness tbBusiness,List cars, BigDecimal parkingFee, String plate, String customerId, String no, boolean isOut){ TbInvoiceOrder invoiceOrder = new TbInvoiceOrder(); List businessNameList = new ArrayList<>(); List businessNoList = new ArrayList<>(); List carNos = new ArrayList<>(); businessNoList.add(tbBusiness.getNo()); carNos.add(plate); businessNameList.add(tbBusiness.getGoodsName()); if(cars!=null){ for (TbBusinessCar car : cars) { if(isOut){ if(TbBusinessCar.PayTypeEnum.FEE_TYPE.getType().equals(car.getPayType())){ continue; } if(!businessNameList.contains("停车费")){ businessNameList.add("停车费"); } }else { if(NbUtil.isNull(car.getCarNo()) || !car.getCarNo().equals(plate)){ continue; } } if(!businessNoList.contains(car.getNo())){ businessNoList.add(car.getNo()); } if(!carNos.contains(car.getCarNo())){ carNos.add(car.getCarNo()); } } } String businessNameStr = businessNameList.stream().map(String::valueOf).collect(Collectors.joining(",")); String businessNoStr = businessNoList.stream().map(String::valueOf).collect(Collectors.joining(",")); String carNoStr = carNos.stream().map(String::valueOf).collect(Collectors.joining(",")); BigDecimal billMoney = tbBusiness.getItemPrice(); if(parkingFee!=null){ billMoney = parkingFee.add(billMoney); } invoiceOrder.setBusinessName(businessNameStr).setBusinessNo(businessNoStr).setCarNo(carNoStr) .setTransactionId(null).setBillMoney(billMoney).setBusinessId(tbBusiness.getId()) .setStatus(0).setCreateTime(new Date()).setCustomerId(customerId).setPreOrderNum(no); invoiceOrderService.save(invoiceOrder); } /** * 无业务车辆收停车费生成发票信息 */ private void createTbInvoiceOrderPark(TbBusinessCar car, String customerId,String no){ TbInvoiceOrder invoiceOrder = new TbInvoiceOrder(); invoiceOrder.setBusinessName("停车费").setBusinessNo(car.getNo()).setCarNo(car.getCarNo()) .setTransactionId(null).setBillMoney(car.getMoney()).setBusinessId(null) .setStatus(0).setCreateTime(new Date()).setCustomerId(customerId).setPreOrderNum(no); invoiceOrderService.save(invoiceOrder); } /** * 生成停车费 * @param tbBusinessCar * @param now * @return */ private BigDecimal caulatePrice(TbBusinessCar tbBusinessCar, Date now) { if(tbBusinessCar==null || now==null) new BigDecimal("0"); Date inTime = tbBusinessCar.getRealInTime(); if (tbBusinessCar.getPay() == 1 && tbBusinessCar.getRealOutTime() == null && tbBusinessCar.getPayTime() != null) { inTime = tbBusinessCar.getPayTime(); } BigDecimal price = tbBusinessService.calculationPartMoney(inTime, now); tbBusinessCar.setMoney(price); return price; } /** * 获取所有停车费总和 * @param cars * @return */ private BigDecimal getParkings(List cars){ if(cars==null) new BigDecimal("0"); Date now = new Date(); BigDecimal value = new BigDecimal("0"); for (TbBusinessCar car:cars){ value = value.add(this.caulatePrice(car,now)); car.setPayType(TbBusinessCar.PayTypeEnum.HAS_PAY_TYPE.getType()); } return value; } /** * 获取所有停车费总和,过滤免费车 * @param cars * @return */ private BigDecimal getParkingsByBS(List cars,TbBusiness tbBusiness){ if(cars==null) new BigDecimal("0"); Date now = new Date(); BigDecimal value = new BigDecimal("0"); for (TbBusinessCar car:cars){ if(isParkingFeeByBS(tbBusiness,car.getCarType())){ car.setPayType(TbBusinessCar.PayTypeEnum.FEE_TYPE.getType()); }else { value = value.add(this.caulatePrice(car,now)); car.setPayType(TbBusinessCar.PayTypeEnum.HAS_PAY_TYPE.getType()); } } return value; } /** * 判断有业务的车是否收停车费 * @return true是免费 */ private boolean isParkingFeeByBS(TbBusiness tbBusiness,String carType){ if (TbItem.ItemTypeEnum.EMPTY_TYPE.getType().equals(carType)) { TbGoods tbGoods = tbGoodsService.getById(tbBusiness.getGoodsId()); return tbGoods.getChinaCarPay() == 1; } else {//越南车=重车 TbGoods tbGoods = tbGoodsService.getById(tbBusiness.getGoodsId()); return tbGoods.getVietnamCarPay() == 1; } } /** * 补录进场解绑逻辑 * @return */ private void autoUnbindCarByPlate(String plate){ if(NbUtil.isNull(plate)) return; TbBusinessCar tbBusinessCar = tbBusinessCarService.findTheLastRecord(plate); //离场逻辑 if (tbBusinessCar!=null){ if(doUnbindCarByPlate(tbBusinessCar)){ return; } } //进场逻辑 TbDeductionBind bind = deductionBindService.getOne(new LambdaQueryWrapper().eq( TbDeductionBind::getBindCar,plate).isNull(TbDeductionBind::getUnbindTime)); if(bind != null){ TbDeductionRecord record = new TbDeductionRecord(); Integer count = record.selectCount(new LambdaQueryWrapper().like( TbDeductionRecord::getDeductionBindId,bind.getId())); if(count>0){ bind.setUpdateBy("预充值自动缴费解绑"); bind.setUpdateTime(new Date()); bind.setUnbindTime(new Date()); bind.updateById(); }else { Integer buCount = tbBusinessService.count(new LambdaQueryWrapper().ne( TbBusiness::getPayStatus,1) .between(TbBusiness::getCreateTime,bind.getBindTime(),NbUtil.getNow())); if(buCount>0){ bind.setUpdateBy("预充值自动缴费解绑"); bind.setUpdateTime(new Date()); bind.setUnbindTime(new Date()); bind.updateById(); } } } } /** * 补录出场解绑逻辑 * @param tbBusinessCar */ private boolean doUnbindCarByPlate(TbBusinessCar tbBusinessCar){ if (tbBusinessCar==null || tbBusinessCar.getRealOutTime()==null){ return false; } TbDeductionBind bind = deductionBindService.getOne(new LambdaQueryWrapper().eq( TbDeductionBind::getBindCar,tbBusinessCar.getCarNo()) .lt(TbDeductionBind::getBindTime,tbBusinessCar.getRealOutTime()) .isNull(TbDeductionBind::getUnbindTime)); if(bind==null){ return false; } bind.setUpdateBy("预充值自动缴费解绑"); bind.setUpdateTime(new Date()); bind.setUnbindTime(new Date()); bind.updateById(); return true; } /** * 缓存任务,如果存在任务不继续往下执行 * @param businessId * @param plate * @return */ private boolean isExistTask(String businessId, String plate){ String key = getRedisKey(businessId,plate); if(NbUtil.isNullStr(RedisUtil.get(key))){ RedisUtil.setByMINUTES(key,key,5); return false; } return true; } private void delRedisTask(String businessId, String plate){ String key = getRedisKey(businessId,plate); RedisUtil.del(key); } private String getRedisKey(String businessId, String plate){ if(NbUtil.isNullStr(businessId)){ businessId = "100"; } if(NbUtil.isNullStr(plate)){ plate = "100"; } String key = "autoPay:"+businessId+plate; return key; } }