chengf
2025-09-10 35fc2330421c1ea217f00e0cb973c1b30e2363c0
service-fee/src/main/java/com/java110/fee/cmd/fee/PayFeeCmd.java
@@ -8,6 +8,7 @@
import com.java110.core.context.ICmdDataFlowContext;
import com.java110.core.event.cmd.Cmd;
import com.java110.core.event.cmd.CmdEvent;
import com.java110.core.factory.CommunitySettingFactory;
import com.java110.core.factory.GenerateCodeFactory;
import com.java110.core.factory.Java110TransactionalFactory;
import com.java110.core.log.LoggerFactory;
@@ -39,6 +40,7 @@
import com.java110.po.owner.RepairUserPo;
import com.java110.po.payFee.PayFeeDetailDiscountPo;
import com.java110.utils.cache.CommonCache;
import com.java110.utils.cache.MappingCache;
import com.java110.utils.constant.FeeFlagTypeConstant;
import com.java110.utils.exception.CmdException;
import com.java110.utils.lock.DistributedLock;
@@ -46,6 +48,7 @@
import com.java110.vo.ResultVo;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import java.math.BigDecimal;
import java.text.ParseException;
@@ -126,6 +129,18 @@
    @Autowired
    private IFinishFeeNotify finishFeeNotifyImpl;
    //域
    public static final String DOMAIN_COMMON = "DOMAIN.COMMON";
    //键
    public static final String TOTAL_FEE_PRICE = "TOTAL_FEE_PRICE";
    //键
    public static final String RECEIVED_AMOUNT_SWITCH = "RECEIVED_AMOUNT_SWITCH";
    //禁用电脑端提交收费按钮
    public static final String OFFLINE_PAY_FEE_SWITCH = "OFFLINE_PAY_FEE_SWITCH";
    @Override
    public void validate(CmdEvent event, ICmdDataFlowContext cmdDataFlowContext, JSONObject reqJson) throws CmdException {
        Assert.jsonObjectHaveKey(reqJson, "communityId", "请求报文中未包含communityId节点");
@@ -136,6 +151,15 @@
        Assert.hasLength(reqJson.getString("cycles"), "周期不能为空");
        Assert.hasLength(reqJson.getString("receivedAmount"), "实收金额不能为空");
        Assert.hasLength(reqJson.getString("feeId"), "费用ID不能为空");
        //当支付方式为转账时,需要校验转账人是否为空
        String primeRate = reqJson.getString("primeRate");
        if(!StringUtils.isEmpty(primeRate) && primeRate.equals("7")){
            String transferorName = reqJson.getString("transferorName");
            if(StringUtils.isEmpty(transferorName)){
                Assert.hasLength(reqJson.getString("transferorName"), "支付方式为转账时,转账人不能为空");
            }
        }
        //判断是否 费用状态为缴费结束
        FeeDto feeDto = new FeeDto();
        feeDto.setFeeId(reqJson.getString("feeId"));
@@ -193,6 +217,13 @@
                Assert.hasKeyAndValue(param, "discountId", "未包含优惠ID");
                Assert.hasKeyAndValue(param, "discountPrice", "未包含优惠金额");
            }
        }
        //校验实付金额不能大于欠款金额
        computeFeePrice(feeDtos);
        BigDecimal amountOwed = new BigDecimal(feeDtos.get(0).getAmountOwed()); //应收款
        BigDecimal receivedAmount = new BigDecimal(reqJson.getString("receivedAmount"));
        if(receivedAmount.compareTo(amountOwed) >= 1){
            throw new IllegalArgumentException("实付金额不能大于欠费金额");
        }
    }
@@ -315,6 +346,82 @@
        cmdDataFlowContext.setResponseEntity(ResultVo.createResponseEntity(feeReceiptDetailDto));
    }
    private void computeFeePrice(List<FeeDto> feeDtos) {
        if (ListUtil.isNull(feeDtos)) {
            return;
        }
        String val = CommunitySettingFactory.getValue(feeDtos.get(0).getCommunityId(), TOTAL_FEE_PRICE);
        if (StringUtil.isEmpty(val)) {
            val = MappingCache.getValue(DOMAIN_COMMON, TOTAL_FEE_PRICE);
        }
        //先取单小区的如果没有配置 取 全局的
        String received_amount_switch = CommunitySettingFactory.getValue(feeDtos.get(0).getCommunityId(), RECEIVED_AMOUNT_SWITCH);
        if (StringUtil.isEmpty(received_amount_switch)) {
            received_amount_switch = MappingCache.getValue(DOMAIN_COMMON, RECEIVED_AMOUNT_SWITCH);
        }
        //先取单小区的如果没有配置 取 全局的
        String offlinePayFeeSwitch = CommunitySettingFactory.getValue(feeDtos.get(0).getCommunityId(), OFFLINE_PAY_FEE_SWITCH);
        if (StringUtil.isEmpty(offlinePayFeeSwitch)) {
            offlinePayFeeSwitch = MappingCache.getValue(DOMAIN_COMMON, OFFLINE_PAY_FEE_SWITCH);
        }
        for (FeeDto feeDto : feeDtos) {
            try {
                // 轮数 * 周期 * 30 + 开始时间 = 目标 到期时间
                Map<String, Object> targetEndDateAndOweMonth = computeFeeSMOImpl.getTargetEndDateAndOweMonth(feeDto);
                Date targetEndDate = (Date) targetEndDateAndOweMonth.get("targetEndDate");
                double oweMonth = (double) targetEndDateAndOweMonth.get("oweMonth");
                feeDto.setCycle(feeDto.getPaymentCycle());
                //todo 这里考虑 账单模式的场景
                if (FeeDto.FEE_FLAG_ONCE.equals(feeDto.getFeeFlag())) {
                    feeDto.setCycle(oweMonth + "");
                }
                feeDto.setDeadlineTime(targetEndDate);
                //todo 算费
                doComputeFeePrice(feeDto, oweMonth);
                feeDto.setVal(val);
                //关闭 线下收银功能
                if (StringUtil.isEmpty(received_amount_switch)) {
                    feeDto.setReceivedAmountSwitch("1");//默认启用实收款输入框
                } else {
                    feeDto.setReceivedAmountSwitch(received_amount_switch);
                }
                feeDto.setOfflinePayFeeSwitch(offlinePayFeeSwitch);
            } catch (Exception e) {
                logger.error("查询费用信息 ,费用信息错误", e);
            }
            //去掉多余0
            feeDto.setSquarePrice(Double.parseDouble(feeDto.getSquarePrice()) + "");
            feeDto.setAdditionalAmount(Double.parseDouble(feeDto.getAdditionalAmount()) + "");
        }
    }
    /**
     * 根据房屋来算单价
     *
     * @param feeDto
     */
    private void doComputeFeePrice(FeeDto feeDto, double oweMonth) {
        String computingFormula = feeDto.getComputingFormula();
        Map feePriceAll = computeFeeSMOImpl.getFeePrice(feeDto);
        feeDto.setFeePrice(Double.parseDouble(feePriceAll.get("feePrice").toString()));
        //BigDecimal feeTotalPrice = new BigDecimal(Double.parseDouble(feePriceAll.get("feeTotalPrice").toString()));
        feeDto.setFeeTotalPrice(MoneyUtil.computePriceScale(Double.parseDouble(feePriceAll.get("feeTotalPrice").toString()),
                feeDto.getScale(),
                Integer.parseInt(feeDto.getDecimalPlace())));
        BigDecimal curFeePrice = new BigDecimal(feeDto.getFeePrice());
        curFeePrice = curFeePrice.multiply(new BigDecimal(oweMonth));
        feeDto.setAmountOwed(MoneyUtil.computePriceScale(curFeePrice.doubleValue(), feeDto.getScale(), Integer.parseInt(feeDto.getDecimalPlace())) + "");
        //动态费用
        if ("4004".equals(computingFormula)
                && FeeDto.FEE_FLAG_ONCE.equals(feeDto.getFeeFlag())
                && !FeeDto.STATE_FINISH.equals(feeDto.getState())
                && feeDto.getDeadlineTime() == null) {
            feeDto.setDeadlineTime(DateUtil.getCurrentDate());
        }
        //考虑租金递增
        computeFeeSMOImpl.dealRentRate(feeDto);
    }
    private void dealUserAccount(JSONObject paramObj, PayFeeDetailPo payFeeDetailPo) {
        //判断选择的账号
@@ -712,7 +819,8 @@
        }
        FeeConfigDto tmpFeeConfigDto = new FeeConfigDto();
        tmpFeeConfigDto.setFeeNameEq(feeConfigDto.getFeeName() + "欠费");
//        tmpFeeConfigDto.setFeeNameEq(feeConfigDto.getFeeName() + "欠费");
        tmpFeeConfigDto.setFeeNameEq(feeConfigDto.getFeeName());
        tmpFeeConfigDto.setFeeFlag(FeeDto.FEE_FLAG_CYCLE_ONCE);
        tmpFeeConfigDto.setComputingFormula(feeConfigDto.getComputingFormula());
        List<FeeConfigDto> feeConfigDtos = feeConfigInnerServiceSMOImpl.queryFeeConfigs(tmpFeeConfigDto);
@@ -729,7 +837,7 @@
     * @param payFeeDetailPo
     * @param payFeePo
     */
    private void ifCustomStartEndTimePayFee(String cycles, Date endTime, PayFeeDetailPo payFeeDetailPo, PayFeePo payFeePo, JSONObject reqJson) {
    public void ifCustomStartEndTimePayFee(String cycles, Date endTime, PayFeeDetailPo payFeeDetailPo, PayFeePo payFeePo, JSONObject reqJson) {
        if (!"-105".equals(cycles)) {
            return;
        }
@@ -770,7 +878,7 @@
        }
        int flag = payFeeDetailNewV1InnerServiceSMOImpl.savePayFeeDetailNew(tmpPayFeeDetailPo);
        //3
        if (flag < 1) {
            throw new CmdException("生成欠费失败");
        }
@@ -778,7 +886,17 @@
        //todo 生成费用
        PayFeePo tmpPayFeePo = BeanConvertUtil.covertBean(feeInfo, PayFeePo.class);
        tmpPayFeePo.setFeeId(GenerateCodeFactory.getGeneratorId(GenerateCodeFactory.CODE_PREFIX_feeId));
        tmpPayFeePo.setEndTime(DateUtil.getFormatTimeStringB(endTime));
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(endTime);
// 减去一天
        calendar.add(Calendar.DAY_OF_MONTH, -1);
// 获取减去一天后的日期
        Date dayBefore = calendar.getTime();
        tmpPayFeePo.setEndTime(DateUtil.getFormatTimeStringB(dayBefore));
        tmpPayFeePo.setState(FeeDto.STATE_DOING);
        // todo 处理configId
@@ -829,6 +947,113 @@
    }
    public void ifCustomStartEndTimePayFee(String cycles, Date endTime, FeeDetailDto payFeeDetailPo, FeeDto payFeePo, String customStartTime) {
        //todo 如果是同一天不创建
        if (DateUtil.getFormatTimeStringB(payFeePo.getEndTime()).equals(customStartTime.split(" ")[0])) {
            return;
        }
        FeeDto feeInfo = payFeePo;
        String payObjNameRemark = "房屋";
        if (FeeDto.PAYER_OBJ_TYPE_CAR.equals(feeInfo.getPayerObjType())) {
            payObjNameRemark = "车辆";
        } else if (FeeDto.PAYER_OBJ_TYPE_CONTRACT.equals(feeInfo.getPayerObjType())) {
            payObjNameRemark = "合同";
        }
        //todo 补充一条 缴费记录数据
        PayFeeDetailPo tmpPayFeeDetailPo = BeanConvertUtil.covertBean(payFeeDetailPo, PayFeeDetailPo.class);
        tmpPayFeeDetailPo.setDetailId(GenerateCodeFactory.getGeneratorId(GenerateCodeFactory.CODE_PREFIX_detailId));
        tmpPayFeeDetailPo.setCycles("0");
        tmpPayFeeDetailPo.setReceivableAmount("0");
        tmpPayFeeDetailPo.setReceivedAmount("0");
        tmpPayFeeDetailPo.setPayableAmount("0");
        tmpPayFeeDetailPo.setStartTime(DateUtil.getFormatTimeStringB(payFeePo.getEndTime()));
        tmpPayFeeDetailPo.setEndTime(DateUtil.getAddDayString(payFeeDetailPo.getStartTime(),"yyyy-MM-dd HH:mm:ss",-1).split(" ")[0] + "23:59:59");
        tmpPayFeeDetailPo.setState(FeeDetailDto.STATE_OWE);
        tmpPayFeeDetailPo.setOpenInvoice("N");
        tmpPayFeeDetailPo.setRemark("按缴费时间段缴费,这部分费用按欠费的方式重新生成,请在" + payObjNameRemark + "上查看");
//        if (!StringUtil.isEmpty(reqJson.getString("cashAmount"))) {
//            BigDecimal cashAmount = new BigDecimal(reqJson.getString("cashAmount")).setScale(2, BigDecimal.ROUND_HALF_UP);
//            if (!StringUtil.isEmpty(tmpPayFeeDetailPo.getRemark())) {
//                tmpPayFeeDetailPo.setRemark(tmpPayFeeDetailPo.getRemark() + ",现金账户抵扣" + cashAmount + "元");
//            } else {
//                tmpPayFeeDetailPo.setRemark("现金账户抵扣" + cashAmount + "元");
//            }
//        }
        int flag = payFeeDetailNewV1InnerServiceSMOImpl.savePayFeeDetailNew(tmpPayFeeDetailPo);
        //3
        if (flag < 1) {
            throw new CmdException("生成欠费失败");
        }
        //todo 生成费用
        PayFeePo tmpPayFeePo = BeanConvertUtil.covertBean(feeInfo, PayFeePo.class);
        tmpPayFeePo.setFeeId(GenerateCodeFactory.getGeneratorId(GenerateCodeFactory.CODE_PREFIX_feeId));
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(payFeePo.getEndTime());
// 减去一天
//        calendar.add(Calendar.DAY_OF_MONTH, -1);
// 获取减去一天后的日期
        Date dayBefore = calendar.getTime();
        tmpPayFeePo.setEndTime(DateUtil.getFormatTimeStringB(dayBefore));
        tmpPayFeePo.setState(FeeDto.STATE_DOING);
        // todo 处理configId
        doChangeConfigId(tmpPayFeePo, feeInfo);
        flag = payFeeV1InnerServiceSMOImpl.savePayFee(tmpPayFeePo);
        if (flag < 1) {
            throw new CmdException("生成欠费失败");
        }
        //todo 补充 费用属性
        FeeAttrDto feeAttrDto = new FeeAttrDto();
        feeAttrDto.setFeeId(payFeePo.getFeeId());
        feeAttrDto.setCommunityId(payFeePo.getCommunityId());
        List<FeeAttrDto> feeAttrDtos = feeAttrInnerServiceSMOImpl.queryFeeAttrs(feeAttrDto);
        if (feeAttrDtos == null || feeAttrDtos.size() < 1) {
            return;
        }
        List<FeeAttrPo> tmpFeeAttrPos = new ArrayList<>();
        FeeAttrPo tmpFeeAttrPo = null;
        boolean hasDeadLineTime = false;
        for (FeeAttrDto tmpFeeAttrDto : feeAttrDtos) {
            tmpFeeAttrDto.setFeeId(tmpPayFeePo.getFeeId());
            tmpFeeAttrDto.setAttrId(GenerateCodeFactory.getGeneratorId(GenerateCodeFactory.CODE_PREFIX_attrId, true));
            if (FeeAttrDto.SPEC_CD_ONCE_FEE_DEADLINE_TIME.equals(tmpFeeAttrDto.getSpecCd())) {
                tmpFeeAttrDto.setValue(DateUtil.getAddHoursStringA(DateUtil.getDateFromStringB(customStartTime.split(" ")[0]),-24).split(" ")[0]+" 23:59:59");
                hasDeadLineTime = true;
            }
            tmpFeeAttrPo = BeanConvertUtil.covertBean(tmpFeeAttrDto, FeeAttrPo.class);
            tmpFeeAttrPos.add(tmpFeeAttrPo);
        }
        //todo 没有结束时间时
        if (!hasDeadLineTime) {
            tmpFeeAttrPo = new FeeAttrPo();
            tmpFeeAttrPo.setAttrId(GenerateCodeFactory.getGeneratorId(GenerateCodeFactory.CODE_PREFIX_attrId, true));
            tmpFeeAttrPo.setFeeId(tmpPayFeePo.getFeeId());
            tmpFeeAttrPo.setCommunityId(tmpFeeAttrPo.getCommunityId());
            tmpFeeAttrPo.setSpecCd(FeeAttrDto.SPEC_CD_ONCE_FEE_DEADLINE_TIME);
            tmpFeeAttrPo.setValue(customStartTime);
            tmpFeeAttrPos.add(tmpFeeAttrPo);
        }
        feeAttrInnerServiceSMOImpl.saveFeeAttrs(tmpFeeAttrPos);
    }
    /**
     * 处理费用项ID
     *
@@ -847,9 +1072,11 @@
        FeeConfigDto tmpFeeConfigDto = new FeeConfigDto();
        tmpFeeConfigDto.setFeeNameEq(feeConfigDtos.get(0).getFeeName() + "欠费");
        tmpFeeConfigDto.setFeeFlag(FeeDto.FEE_FLAG_CYCLE_ONCE);
        tmpFeeConfigDto.setComputingFormula(feeConfigDto.getComputingFormula());
//        tmpFeeConfigDto.setFeeNameEq(feeConfigDtos.get(0).getFeeName() + "欠费");
        tmpFeeConfigDto.setFeeNameEq(feeConfigDtos.get(0).getFeeName());
        tmpFeeConfigDto.setCommunityId(feeInfo.getCommunityId());
//        tmpFeeConfigDto.setFeeFlag(FeeDto.FEE_FLAG_CYCLE_ONCE);
//        tmpFeeConfigDto.setComputingFormula(feeConfigDto.getComputingFormula());
        //todo 校验的时候校验过了 所以这里不可能为空
        feeConfigDtos = feeConfigInnerServiceSMOImpl.queryFeeConfigs(tmpFeeConfigDto);