yupeng
2025-03-13 96a7015065775e5c3bc3d6458b86b28ad7bfb46b
Merge remote-tracking branch 'origin/master' into xizang-changyun
35个文件已修改
5个文件已添加
2598 ■■■■ 已修改文件
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TContractController.java 114 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TDeptController.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TFaultDescribeDicController.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/TaskUtil.java 187 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/PdfUtils.java 247 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordPdfGenerator.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordTemplateProcessor.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordUtil.java 313 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application-test.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/IndexController.java 59 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TBillController.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TFaultRepairMessageController.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TInformationController.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/MyFileUtil.java 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/constant/DictConstants.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/CodeGenerateUtils.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/dto/BatchBillDTO.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TBillMapper.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TBill.java 41 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TContract.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/query/TBillQuery.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/TBillService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/FlowListenerService.java 461 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/StateProcessTemplateServiceImpl.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TBillServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TCheckAcceptRecordServiceImpl.java 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TContractServiceImpl.java 498 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/THouseServiceImpl.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/task/jobs/StateProcessJob.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/task/utils/TaskUtil.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/vo/HouseVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/vo/ProcessTaskListVO.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/StateTaskCenterMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/TBillMapper.xml 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/TCheckAcceptRecordMapper.xml 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/TFaultRepairMessageMapper.xml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/THouseMapper.xml 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TContractController.java
@@ -92,11 +92,13 @@
    @PostMapping(value = "/addContract")
    @PreAuthorize("@ss.hasPermi('contract:list:add')")
    public R<Boolean> addContract(@Validated @RequestBody TContractDTO dto) {
        LocalDateTime changeTime = dto.getChangeTime();
        long count = contractService.count(new LambdaQueryWrapper<TContract>().eq(TContract::getContractNumber, dto.getContractNumber()));
        if (count!=0){
            return R.fail("合同编号不可重复");
        }
        dto.setChangeRent(dto.getMonthRent());
        dto.setChangeTime(null);
        contractService.save(dto);
        if (dto.getStatus().equals("2")){
            //发起合同新增审批
@@ -134,7 +136,7 @@
            tContractRentType.setIncreasingDecreasing(dto.getIncreasingDecreasing());
            tContractRentType.setIncreasingDecreasingType(dto.getIncreasingDecreasingType());
            tContractRentType.setNumericalValue(dto.getNumericalValue());
            tContractRentType.setChangeTime(dto.getChangeTime());
            tContractRentType.setChangeTime(changeTime);
            tContractRentType.setCycleTime(dto.getCycleTime());
            contractRentTypeService.save(tContractRentType);
        }
@@ -145,6 +147,7 @@
    @PostMapping(value = "/updateContract")
    @PreAuthorize("@ss.hasPermi('contract:list:edit')")
    public R<Boolean> updateContract(@Validated @RequestBody TContractDTO dto) {
        dto.setChangeTime(null);
        contractService.updateById(dto);
        contractRentTypeService.remove(new LambdaQueryWrapper<TContractRentType>()
                .eq(TContractRentType::getContractId,dto.getId()));
@@ -157,6 +160,36 @@
            tContractRentType.setChangeTime(dto.getChangeTime());
            tContractRentType.setCycleTime(dto.getCycleTime());
            contractRentTypeService.save(tContractRentType);
        }
        if (dto.getStatus().equals("2")){
            //发起合同新增审批
            ProcessStartBO processStartBO = new ProcessStartBO();
            processStartBO.setCategory(ProcessCategoryEnum.CATEGORY1.getValue().toString());
            processStartBO.setModuleName("合同新增审批");
            processStartBO.setName(dto.getContractName());
            //需要显示发起申请人所在单位
//            String cedName = SecurityUtils.getLoginUser().getUser().getDept().getDeptName();
//            String remark = String.format("【镇/街】:%s,【征收实施单位】:%s,【申请金额】:%s万元", stateProject.getStreet(), cedName, stateApplyRecord.getAmount());
            processStartBO.setRemark("");
            Map<String, Object> variable = new HashMap<>();
            variable.put("projectId", dto.getId());
            processStartBO.setVariable(variable);
            //开启工作流程
            Boolean start = stateProcessTemplateService.start(processStartBO);
            if(start){
                FlwTask flwTask = flwTaskMapper.selectOne(Wrappers.lambdaQuery(FlwTask.class)
                        .like(FlwTask::getVariable, dto.getId())
                        .orderByDesc(FlwTask::getCreateTime)
                        .last("LIMIT 1"));
                if(Objects.nonNull(flwTask)){
                    // 添加定时任务
                    Map<String, ? extends Object> maps =
                            new ImmutableMap.Builder<String, Long>().
                                    put("id", flwTask.getId())
                                    .build();
                    QuartzManager.addJob(StateProcessJob.class, (StateProcessJob.name+flwTask.getId()).toUpperCase(), TimeJobType.AUTO_AUDIT,new Date(new Date().getTime()+48*60*60*1000L), maps);
                }
            }
        }
        return R.ok();
    }
@@ -178,8 +211,6 @@
        TContractVO res = new TContractVO();
        TContract contract = contractService.getById(id);
        BeanUtils.copyProperties(contract,res);
        res.setPayType(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CONTRACT_PAY_TYPE,res.getPayType()));
        res.setStatus(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CONTRACT_STATUS,res.getStatus()));
        TContractRentType contractRentType = contractRentTypeService.lambdaQuery().eq(TContractRentType::getContractId, id).one();
        if (contractRentType!=null){
            BeanUtils.copyProperties(contractRentType,res);
@@ -194,14 +225,14 @@
        res.setHouse(house);
        List<TBill> list = billService.lambdaQuery()
                .eq(TBill::getContractId, id)
                .in(TBill::getPayFeesStatus, Arrays.asList("1,4"))
                .ne(TBill::getPayFeesStatus, 3)
                .list();
        BigDecimal payMoney = new BigDecimal("0");
        for (TBill tBill : list) {
            payMoney = payMoney.add(tBill.getPayFeesMoney()).add(tBill.getPayableFeesPenalty());
            payMoney = payMoney.add(tBill.getOutstandingMoney()).add(tBill.getPayableFeesPenalty());
        }
        TCheckAcceptRecord tCheckAcceptRecord = checkAcceptRecordService.lambdaQuery().eq(TCheckAcceptRecord::getContractId, id).one();
        res.setCheckResult(Objects.nonNull(tCheckAcceptRecord)?tCheckAcceptRecord.getCheckResult():false);
        res.setCheckResult(Objects.nonNull(tCheckAcceptRecord)&&Objects.nonNull(tCheckAcceptRecord.getCheckResult())?tCheckAcceptRecord.getCheckResult():null);
        res.setPayMoney(payMoney);
        return R.ok(res);
@@ -228,7 +259,14 @@
        TContract contract = contractService.getById(id);
        contract.setStatus("8");
        contractService.updateById(contract);
        return R.ok();
        // 将所有未缴费账单设置未已失效
        List<TBill> tBills = billService.list(new LambdaQueryWrapper<TBill>()
                .ne(TBill::getPayFeesStatus, 3)
                .eq(TBill::getContractId, contract.getId()));
        for (TBill tBill : tBills) {
            tBill.setPayFeesStatus("5");
        }
        billService.updateBatchById(tBills);        return R.ok();
    }
    @ApiOperation(value = "终止合同剩余未缴费账单列表")
    @PostMapping(value = "/contractBillList")
@@ -257,31 +295,51 @@
        List<TContract> list = contractService.lambdaQuery().in(TContract::getId, dto.getIds()).list();
        List<String> res = new ArrayList<>();
        for (TContract contract : list) {
            TBill firstBill = billService.lambdaQuery().eq(TBill::getContractId, contract.getId())
                    .orderByDesc(TBill::getStartTime).last("limit 1").one();
            THouse tHouse = houseService.getById(contract.getHouseId());
            Map<String, Object> templateParam = new HashMap<>(5);
            templateParam.put("partyOneName", contract.getPartyOneName());
            templateParam.put("partyTwoName", contract.getPartyTwoName());
            templateParam.put("houseAddress", tHouse.getHouseAddress());
            templateParam.put("houseArea", tHouse.getHouseArea()+"m²");
            templateParam.put("startTime", DateUtils.localDateTimeToStringYear(contract.getStartTime()));
            templateParam.put("endTime", DateUtils.localDateTimeToStringYear(contract.getEndTime()));
            templateParam.put("monthRent", "¥¥"+contract.getMonthRent()+"元");
            templateParam.put("monthRentString", "人民币"+NumberToChineseUtils.numberToChinese(contract.getMonthRent().setScale(2, BigDecimal.ROUND_DOWN).doubleValue()));
            templateParam.put("${partyOneName}", contract.getPartyOneName());
            templateParam.put("${partyTwoName}", contract.getPartyTwoName());
            templateParam.put("${houseAddress}", tHouse.getHouseAddress());
            templateParam.put("${houseArea}", tHouse.getHouseArea()+"m²");
            long between = ChronoUnit.DAYS.between(contract.getStartTime(), contract.getStartPayTime())+1;
            templateParam.put("${day}", between);
            templateParam.put("${endTimeFree}", DateUtils.localDateTimeToStringYear(contract.getStartPayTime().plusDays(1)));
            templateParam.put("${startPayTime}", DateUtils.localDateTimeToStringYear(contract.getStartPayTime()));
            templateParam.put("${startTime}", DateUtils.localDateTimeToStringYear(contract.getStartTime()));
            templateParam.put("${endTime}", DateUtils.localDateTimeToStringYear(contract.getEndTime()));
            templateParam.put("${monthRent}", "¥"+contract.getMonthRent()+"元");
            templateParam.put("${monthRentString}", "人民币"+NumberToChineseUtils.numberToChinese(contract.getMonthRent().setScale(2, BigDecimal.ROUND_DOWN).doubleValue()));
            String totalYear = Objects.nonNull(contract.getTotalYear())?contract.getTotalYear().toString():"";
            templateParam.put("totalYear", "¥¥"+totalYear+"元");
            templateParam.put("${totalYear}", "¥"+totalYear+"元");
            String totalYearString = StringUtils.isNotEmpty(totalYear)?NumberToChineseUtils.numberToChinese(contract.getTotalYear().setScale(2, BigDecimal.ROUND_DOWN).doubleValue()):"";
            templateParam.put("totalYearString", "人民币"+totalYearString);
            templateParam.put("payType", contract.getPayType().equals("1")?"月":contract.getPayType().equals("2")?"季":"年");
            templateParam.put("firstRent", "¥"+(contract.getPayType().equals("1")?contract.getMonthRent():contract.getPayType().equals("2")?contract.getMonthRent().multiply(new BigDecimal("3")):contract.getMonthRent().multiply(new BigDecimal("12"))).setScale(2,BigDecimal.ROUND_DOWN)+"元");
            templateParam.put("firstRentString", "人民币"+NumberToChineseUtils.numberToChinese((contract.getPayType().equals("1")?contract.getMonthRent():contract.getPayType().equals("2")?contract.getMonthRent().multiply(new BigDecimal("3")):contract.getMonthRent().multiply(new BigDecimal("12")).setScale(2,BigDecimal.ROUND_DOWN)).doubleValue()));
            templateParam.put("nextPayTime", contract.getPayType().equals("1")?"月":contract.getPayType().equals("2")?"季":"年");
            templateParam.put("deposit", "¥"+contract.getDeposit()+"元");
            templateParam.put("depositString", NumberToChineseUtils.numberToChinese(contract.getDeposit().setScale(2, BigDecimal.ROUND_DOWN).doubleValue()));
            templateParam.put("partyOnePerson", contract.getPartyOnePerson());
            templateParam.put("partyOnePhone", contract.getPartyOnePhone());
            templateParam.put("partyTwoPerson", contract.getPartyTwoPerson());
            templateParam.put("partyTwoPhone", contract.getPartyTwoPhone());
            String url = wordUtil.generatePdf("/template", "1_yzj_租赁合同.xml", templateParam, "租赁合同", "E:\\");
            templateParam.put("${totalYearString}", "人民币"+totalYearString);
            templateParam.put("${payType}", contract.getPayType().equals("1")?"月":contract.getPayType().equals("2")?"季":"年");
            if(firstBill!=null){
                templateParam.put("${firstRent}", "¥"+(firstBill.getPayableFeesMoney())+"元");
            }else{
                templateParam.put("${firstRent}", "");
            }
            templateParam.put("${firstRentString}", "人民币"+NumberToChineseUtils.numberToChinese((contract.getPayType().equals("1")?contract.getMonthRent():contract.getPayType().equals("2")?contract.getMonthRent().multiply(new BigDecimal("3")):contract.getMonthRent().multiply(new BigDecimal("12")).setScale(2,BigDecimal.ROUND_DOWN)).doubleValue()));
            templateParam.put("${nextPayTime}", contract.getPayType().equals("1")?"月":contract.getPayType().equals("2")?"季":"年");
            templateParam.put("${deposit}", "¥"+contract.getDeposit()+"元");
            templateParam.put("${depositString}", NumberToChineseUtils.numberToChinese(contract.getDeposit().setScale(2, BigDecimal.ROUND_DOWN).doubleValue()));
            templateParam.put("${partyOnePerson}", contract.getPartyOnePerson());
            templateParam.put("${partyOnePhone}", contract.getPartyOnePhone());
            templateParam.put("${partyTwoPerson}", contract.getPartyTwoPerson());
            templateParam.put("${partyTwoPhone}", contract.getPartyTwoPhone());
            // 验收时间
            TCheckAcceptRecord tCheckAcceptRecord = checkAcceptRecordService.lambdaQuery().eq(TCheckAcceptRecord::getContractId, contract.getId()).last("limit 1").one();
            if (tCheckAcceptRecord!=null &&tCheckAcceptRecord.getCheckTime()!=null ){
                templateParam.put("${checkTime}", DateUtils.localDateTimeToStringYear(tCheckAcceptRecord.getCheckTime()));
            }else{
                templateParam.put("${checkTime}", "");
            }
            String url = wordUtil.generatePdf("/usr/local/project/file/", "1_yzj_租赁合同.docx", templateParam, "租赁合同", "/usr/local/project/file/");
            res.add(url);
        }
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TDeptController.java
@@ -61,12 +61,20 @@
    /**
     * 获取部门管理管理列表
     */
    @ApiOperation(value = "获取部门管理列表")
    @ApiOperation(value = "获取部门管理列表-启用状态")
    @PostMapping(value = "/list")
    public R<List<TDept>> list() {
        return R.ok(deptService.list(Wrappers.lambdaQuery(TDept.class)
                .eq(TDept::getStatus, 1)));
    }
    /**
     * 获取部门管理管理列表
     */
    @ApiOperation(value = "获取部门管理列表-所有状态")
    @PostMapping(value = "/listAll")
    public R<List<TDept>> listAll() {
        return R.ok(deptService.list(Wrappers.lambdaQuery(TDept.class)));
    }
    /**
     * 添加部门管理管理
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TFaultDescribeDicController.java
@@ -16,6 +16,7 @@
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.List;
/**
@@ -67,6 +68,7 @@
    @ApiOperation(value = "修改故障描述")
    @PostMapping(value = "/update")
    public R<Boolean> update(@Validated @RequestBody TFaultDescribeDic dto) {
        dto.setUpdateTime(LocalDateTime.now());
        return R.ok(faultDescribeDicService.updateById(dto));
    }
@@ -101,6 +103,6 @@
    public R<Boolean> deleteByIds(@RequestBody List<String> ids) {
        return R.ok(faultDescribeDicService.removeByIds(ids));
    }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java
@@ -156,7 +156,7 @@
    @PostMapping("/add")
    public AjaxResult add(@Validated @RequestBody SysUser user)
    {
        user.setUserName(user.getPhonenumber());
        user.setUserName(user.getUserName());
        if (!userService.checkUserNameUnique(user))
        {
            return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/TaskUtil.java
@@ -1,110 +1,83 @@
package com.ruoyi.web.controller.task;
import com.ruoyi.system.model.TBill;
import com.ruoyi.system.model.TContract;
import com.ruoyi.system.model.TContractRentType;
import com.ruoyi.system.service.TBillService;
import com.ruoyi.system.service.TContractRentTypeService;
import com.ruoyi.system.service.TContractService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
/**
 * @author zhibing.pu
 * @date 2023/7/11 8:39
 */
@Component
public class TaskUtil {
    @Autowired
    private TContractService contractService;
    @Autowired
    private TBillService billService;
    @Autowired
    private TContractRentTypeService contractRentTypeService;
    // 每天凌晨00点执行的定时任务 用于生成违约金
    @Scheduled(cron = "0 0 0 * * ?")
    public void dayOfProportionBill() {
        try {
            // 查询所有未缴费账单
            List<TBill> list = billService.lambdaQuery().eq(TBill::getPayFeesStatus, 1).list();
            for (TBill tBill : list) {
                TContract contract = contractService.getById(tBill.getContractId());
                LocalDate payableFeesTime = tBill.getPayableFeesTime();
                LocalDateTime now = LocalDateTime.now();
                // 计算两个时间相差多少个小时
                long hours = ChronoUnit.HOURS.between(payableFeesTime, now);
                long l = hours / 72;
                if (l>0){
                    // 违约金比例
                    BigDecimal proportion = contract.getProportion();
                    // 应缴违约金
                    BigDecimal money = tBill.getOutstandingMoney().multiply(proportion);
                    TBill changeBill = new TBill();
                    changeBill.setId(tBill.getId());
                    changeBill.setPayableFeesPenalty(money);
                    billService.lockAndUpdateInfo(changeBill,2);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // 每天凌晨00点执行的定时任务 根据应缴费日期修改账单状态
    @Scheduled(cron = "0 0 0 * * ?")
    public void dayOfEndBill() {
        try {
            List<TBill> list = billService.lambdaQuery().eq(TBill::getPayFeesStatus, "2").list();
            for (TBill tBill : list) {
                if (tBill.getPayableFeesTime().equals(LocalDate.now())){
                    tBill.setPayFeesStatus("1");
                }
            }
            billService.updateBatchById(list);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
//        LocalDateTime now = LocalDateTime.now().minusMonths(1).withDayOfMonth(31);
//        System.err.println(now);
//        LocalDateTime now2 = now.plusMonths(1);
//        System.err.println(now2);
//package com.ruoyi.web.controller.task;
//
//        LocalDateTime now1 = LocalDateTime.now();
//        long days = ChronoUnit.DAYS.between(now, now1);
//        long days2 = ChronoUnit.DAYS.between(now.plusDays(1), now1);
//
//        System.err.println(days);
//        System.err.println(days2);
//        LocalDateTime endTime = now.with(TemporalAdjusters.lastDayOfMonth()).withSecond(59).withHour(23).withMinute(59);
//import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
//import com.ruoyi.common.utils.SmsUtil;
//import com.ruoyi.system.mapper.TBillMapper;
//import com.ruoyi.system.model.TBill;
//import com.ruoyi.system.model.TContract;
//import com.ruoyi.system.model.TContractRentType;
//import com.ruoyi.system.service.TBillService;
//import com.ruoyi.system.service.TContractRentTypeService;
//import com.ruoyi.system.service.TContractService;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.scheduling.annotation.Scheduled;
//import org.springframework.stereotype.Component;
//
//        System.err.println(endTime);
    }
}
//import javax.annotation.Resource;
//import java.math.BigDecimal;
//import java.time.LocalDate;
//import java.time.LocalDateTime;
//import java.time.LocalTime;
//import java.time.ZoneId;
//import java.time.temporal.ChronoUnit;
//import java.time.temporal.TemporalAdjusters;
//import java.util.ArrayList;
//import java.util.Date;
//import java.util.List;
//import java.util.Random;
//import java.util.stream.Collectors;
//
///**
// * @author zhibing.pu
// * @date 2023/7/11 8:39
// */
//@Component
//public class TaskUtil {
//    @Autowired
//    private TContractService contractService;
//    @Autowired
//    private TBillMapper billMapper;
//    // 用于更新违约金账单
//    // 每分钟执行一次的定时任务
//
//    @Scheduled(cron = "0 * * * * ?")
//    public void dayOfProportionBill() {
//        try {
//            // 查询所有未缴费账单
//            List<TBill> list = billMapper.selectList(new LambdaQueryWrapper<TBill>().eq(TBill::getPayFeesStatus, 1)
//                    .le(TBill::getPayableFeesTime,LocalDate.now()));
//            for (TBill tBill : list) {
//                tBill.setPayFeesStatus("4");
//                TContract contract = contractService.getById(tBill.getContractId());
//                LocalDate payableFeesTime = tBill.getPayableFeesTime();
//                // 将LocalDate转化为LocalDateTime
//                LocalDateTime payableFeesTime1 = LocalDateTime.of(payableFeesTime, LocalTime.of(0, 0, 0));
//                LocalDateTime now = LocalDateTime.now();
//                // 计算两个时间相差多少个小时
//                long hours = ChronoUnit.HOURS.between(payableFeesTime1, now);
//                long l = hours / 24;
//                if (l>=3){
//                    // 违约金比例
//                    BigDecimal proportion = contract.getProportion();
//                    // 按每天 待缴费金额 * XX% 增加违约金费用
//                    if (tBill.getOutstandingMoney().compareTo(new BigDecimal("0"))==0){
//                        tBill.setPayFeesStatus("3");
//                        billMapper.updateById(tBill);
//                        continue;
//                    }
//                    BigDecimal money = tBill.getOutstandingMoney().multiply(new BigDecimal(100).add(proportion)).divide(new BigDecimal(100),2, BigDecimal.ROUND_DOWN);
//                    tBill.setOverDays((int) l);
//                    tBill.setPayableFeesPenalty((tBill.getPayableFeesPenalty()!=null?tBill.getPayableFeesPenalty():BigDecimal.ZERO).add(money));
//                    tBill.setOutstandingMoney(money);
//                    billMapper.updateById(tBill);
//
//                }
//            }
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }
//
//
//}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/PdfUtils.java
@@ -4,6 +4,7 @@
import com.documents4j.api.IConverter;
import com.documents4j.job.LocalConverter;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xwpf.usermodel.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Component;
@@ -11,7 +12,13 @@
import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
@@ -21,47 +28,223 @@
    /**
     * word 转 pdf
     *
     * @param url
     */
    public  String wordToPdf(String url,String filePath, String fileName) {
//    public  String wordToPdf(String url,String filePath, String fileName) {
//        try {
//            DocumentType documentType = DocumentType.DOC;
//            if(url.contains(".docx")){
//                documentType = DocumentType.DOCX;
//            }
//            if(url.contains(".doc")){
//                documentType = DocumentType.DOC;
//            }
//            if(url.contains(".xlsx")){
//                documentType = DocumentType.XLSX;
//            }else {
//                if(url.contains(".xls")){
//                    documentType = DocumentType.XLS;
//                }
//            }
//            InputStream inputStream = new URL(url).openStream();
//            ByteArrayOutputStream stream = new ByteArrayOutputStream();
//            IConverter converter = LocalConverter.builder().build();
//            converter.convert(inputStream)
//                    .as(documentType)
//                    .to(stream)
//                    .as(DocumentType.PDF).execute();
//
//            //上传图片
//            byte2File(stream.toByteArray(),filePath + "/pdf",fileName.substring(0,fileName.lastIndexOf(".")) + ".pdf");
//            MultipartFile multipartFile = convertToMultipartFile(stream,fileName.substring(0,fileName.lastIndexOf(".")) );
//            String s = tencentCosUtil.upLoadFile(multipartFile,"/wordToPdf");
//
//            stream.close();
//            inputStream.close();
//            return s;
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//        return null;
//    }
    public String wordToPdf(String filePath, String fileName) {
        try {
            DocumentType documentType = DocumentType.DOC;
            if(url.contains(".docx")){
                documentType = DocumentType.DOCX;
            }
            if(url.contains(".doc")){
                documentType = DocumentType.DOC;
            }
            if(url.contains(".xlsx")){
                documentType = DocumentType.XLSX;
            }else {
                if(url.contains(".xls")){
                    documentType = DocumentType.XLS;
            // 确保路径正确性
            String inputFile = new File(filePath, fileName).getAbsolutePath();
            String outputDir = new File(filePath, "pdf").getAbsolutePath();
            // 创建输出目录
            new File(outputDir).mkdirs();
            // 使用完整的转换参数
            List<String> command = Arrays.asList(
                    "/usr/bin/libreoffice",  // 使用完整路径
                    "--headless",
                    "--norestore",
                    "--convert-to",
                    "pdf:writer_pdf_Export:PDFExport{" +
                            "EmbedStandardFonts=1;" +
                            "EmbedFonts=1;" +
                            "EmbedOnlyUsedFonts=0;" +
                            "UseTaggedPDF=1" +
                            "}",
                    "--outdir",
                    outputDir,
                    inputFile
            );
            // 创建进程构建器
            ProcessBuilder pb = new ProcessBuilder(command);
            // 设置环境变量
            Map<String, String> env = pb.environment();
            env.put("LC_ALL", "zh_CN.UTF-8");
            env.put("LANG", "zh_CN.UTF-8");
            env.put("LANGUAGE", "zh_CN.UTF-8");
            // 重定向错误流到标准输出
            pb.redirectErrorStream(true);
            // 启动进程
            Process process = pb.start();
            // 读取输出
            StringBuilder output = new StringBuilder();
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    output.append(line).append("\n");
                    System.out.println(line);
                }
            }
            InputStream inputStream = new URL(url).openStream();
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            IConverter converter = LocalConverter.builder().build();
            converter.convert(inputStream)
                    .as(documentType)
                    .to(stream)
                    .as(DocumentType.PDF).execute();
            //上传图片
            byte2File(stream.toByteArray(),filePath + "/pdf",fileName.substring(0,fileName.lastIndexOf(".")) + ".pdf");
            MultipartFile multipartFile = convertToMultipartFile(stream,fileName.substring(0,fileName.lastIndexOf(".")) );
            // 等待进程完成,设置超时
            if (!process.waitFor(120, TimeUnit.SECONDS)) {
                process.destroyForcibly();
                throw new RuntimeException("转换超时");
            }
            int exitCode = process.exitValue();
            if (exitCode != 0) {
                throw new RuntimeException("转换失败,退出码:" + exitCode + "\n输出:" + output);
            }
            // 检查生成的PDF文件
            String pdfFileName = fileName.substring(0, fileName.lastIndexOf(".")) + ".pdf";
            File pdfFile = new File(outputDir, pdfFileName);
            if (!pdfFile.exists() || pdfFile.length() == 0) {
                throw new RuntimeException("PDF文件未生成或为空");
            }
            String absolutePath = pdfFile.getAbsolutePath();
            MultipartFile multipartFile = convertFileToMultipartFile(pdfFile);
            String s = tencentCosUtil.upLoadFile(multipartFile,"/wordToPdf");
            stream.close();
            inputStream.close();
            return s;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("PDF转换失败: " + e.getMessage(), e);
        }
        return null;
    }
    public MultipartFile convertFileToMultipartFile(File file) throws IOException {
        // 读取文件内容到字节数组
        byte[] fileContent = Files.readAllBytes(file.toPath());
        // 创建 MultipartFile 对象
        MultipartFile multipartFile = new MockMultipartFile(
                file.getName(),       // 文件名
                file.getName(),       // 原始文件名
                "application/pdf",  // 内容类型,根据实际情况调整
                fileContent           // 文件内容
        );
        return multipartFile;
    }
    // 在使用前检查和配置环境
    public static void setupEnvironment() {
        try {
            // 1. 检查LibreOffice安装
            checkLibreOffice();
            // 2. 检查和安装字体
            installFonts();
            // 3. 配置字体
            configureFonts();
            // 4. 验证环境变量
            checkEnvironment();
        } catch (Exception e) {
            throw new RuntimeException("环境设置失败: " + e.getMessage(), e);
        }
    }
    private static void checkLibreOffice() throws IOException, InterruptedException {
        Process process = Runtime.getRuntime().exec("which libreoffice");
        if (process.waitFor() != 0) {
            throw new RuntimeException("LibreOffice未安装");
        }
    }
    private static void installFonts() throws IOException, InterruptedException {
        // 创建字体安装脚本
        String scriptContent =
                "#!/bin/bash\n" +
                        "apt-get update\n" +
                        "apt-get install -y fonts-wqy-zenhei fonts-wqy-microhei fonts-arphic-ukai fonts-arphic-uming\n" +
                        "fc-cache -fv\n";
        File script = new File("/tmp/install_fonts.sh");
        Files.write(script.toPath(), scriptContent.getBytes());
        script.setExecutable(true);
        // 执行脚本
        Process process = Runtime.getRuntime().exec("sudo /tmp/install_fonts.sh");
        process.waitFor();
        // 清理脚本
        script.delete();
    }
    private static void configureFonts() throws IOException {
        // 创建字体配置文件
        String fontConfig =
                "<?xml version=\"1.0\"?>\n" +
                        "<!DOCTYPE fontconfig SYSTEM \"fonts.dtd\">\n" +
                        "<fontconfig>\n" +
                        "  <match target=\"pattern\">\n" +
                        "    <test name=\"family\"><string>serif</string></test>\n" +
                        "    <edit name=\"family\" mode=\"prepend\">\n" +
                        "      <string>WenQuanYi Zen Hei</string>\n" +
                        "    </edit>\n" +
                        "  </match>\n" +
                        "  <match target=\"pattern\">\n" +
                        "    <test name=\"family\"><string>sans-serif</string></test>\n" +
                        "    <edit name=\"family\" mode=\"prepend\">\n" +
                        "      <string>WenQuanYi Zen Hei</string>\n" +
                        "    </edit>\n" +
                        "  </match>\n" +
                        "</fontconfig>";
        // 写入配置文件
        File configFile = new File(System.getProperty("user.home") + "/.fonts.conf");
        Files.write(configFile.toPath(), fontConfig.getBytes());
    }
    private static void checkEnvironment() {
        // 检查环境变量
        String[] requiredVars = {"LANG", "LC_ALL", "LANGUAGE"};
        for (String var : requiredVars) {
            String value = System.getenv(var);
            if (value == null || !value.contains("zh_CN")) {
                System.err.println("警告: " + var + " 环境变量未正确设置");
            }
        }
    }
    public static MultipartFile convertToMultipartFile(ByteArrayOutputStream baos, String fileName) throws IOException {
        // 创建一个临时文件
@@ -209,12 +392,12 @@
    }
    public String test(String fileName){
        String url = "file:///E:\\"+fileName;
//        String url = "file:///usr/local/project/file/"+fileName;
//        String filePath = "E:\\qiyeweixin\\WXWork\\1688855207501340\\Cache\\File\\2024-09";
//        String fileName = "专业技术工作总结.docx";4
        String filePath = "E:\\";
        String filePath = "/usr/local/project/file/";
        String s = wordToPdf(url, filePath, fileName);
        String s = wordToPdf(filePath, fileName);
        System.err.println(s);
        return s;
ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordPdfGenerator.java
New file
@@ -0,0 +1,106 @@
//package com.ruoyi.web.controller.tool;
//
//import org.apache.poi.xwpf.usermodel.*;
//import java.io.*;
//import java.time.LocalDateTime;
//import java.time.format.DateTimeFormatter;
//
//public class WordPdfGenerator {
//
//    // 示例数据类
//    public static class UserData {
//        private String name;
//        private String id;
//        private String department;
//        private LocalDateTime createTime;
//
//        public UserData(String name, String id, String department) {
//            this.name = name;
//            this.id = id;
//            this.department = department;
//            this.createTime = LocalDateTime.now();
//        }
//
//        // getter方法省略
//    }
//
//    public static void generateDocument(UserData userData, String outputPath) {
//        try (XWPFDocument document = new XWPFDocument()) {
//            // 创建标题
//            XWPFParagraph title = document.createParagraph();
//            title.setAlignment(ParagraphAlignment.CENTER);
//            XWPFRun titleRun = title.createRun();
//            titleRun.setText("用户信息表");
//            titleRun.setBold(true);
//            titleRun.setFontSize(20);
//            titleRun.setFontFamily("宋体");
//
//            // 添加空行
//            document.createParagraph();
//
//            // 创建表格
//            XWPFTable table = document.createTable(5, 2);
//            table.setWidth("100%");
//
//            // 设置表格数据
//            setCellText(table, 0, "姓名", userData.name);
//            setCellText(table, 1, "ID", userData.id);
//            setCellText(table, 2, "部门", userData.department);
//            setCellText(table, 3, "创建时间",
//                userData.createTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
//
//            // 保存Word文档
//            String wordFile = outputPath + ".docx";
//            try (FileOutputStream out = new FileOutputStream(wordFile)) {
//                document.write(out);
//            }
//
//            // 转换为PDF
//            convertToPdf(wordFile, outputPath + ".pdf");
//
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }
//
//    private static void setCellText(XWPFTable table, int row, String label, String value) {
//        XWPFTableRow tableRow = table.getRow(row);
//        tableRow.getCell(0).setText(label);
//        tableRow.getCell(1).setText(value);
//    }
//
//    private static void convertToPdf(String wordPath, String pdfPath) {
//        try {
//            // 使用LibreOffice进行转换
//            ProcessBuilder pb = new ProcessBuilder(
//                "soffice",
//                "--headless",
//                "--convert-to", "pdf",
//                "--outdir", new File(pdfPath).getParent(),
//                wordPath
//            );
//            Process process = pb.start();
//
//            // 等待转换完成
//            int exitCode = process.waitFor();
//            if (exitCode == 0) {
//                System.out.println("PDF转换成功!");
//            } else {
//                System.out.println("PDF转换失败!");
//            }
//
//            // 删除临时Word文件
//            new File(wordPath).delete();
//
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }
//
//    public static void main(String[] args) {
//        // 示例使用
//        UserData userData = new UserData("张三", "EMP001", "技术部");
//        String outputPath = "E:\\";
//        generateDocument(userData, outputPath);
//    }
//}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordTemplateProcessor.java
New file
@@ -0,0 +1,87 @@
package com.ruoyi.web.controller.tool;
import org.apache.poi.xwpf.usermodel.*;
import java.io.*;
import java.util.*;
public class WordTemplateProcessor {
    public static void fillTemplate(String templatePath, String outputPath,Map<String, String> dataMap) {
        try {
            // 读取模板文件
            FileInputStream fis = new FileInputStream(templatePath);
            XWPFDocument document = new XWPFDocument(fis);
            // 替换段落中的标记
            for (XWPFParagraph paragraph : document.getParagraphs()) {
                replaceParagraph(paragraph, dataMap);
            }
            // 替换表格中的标记
            for (XWPFTable table : document.getTables()) {
                for (XWPFTableRow row : table.getRows()) {
                    for (XWPFTableCell cell : row.getTableCells()) {
                        for (XWPFParagraph paragraph : cell.getParagraphs()) {
                            replaceParagraph(paragraph, dataMap);
                        }
                    }
                }
            }
            // 保存文件
            FileOutputStream fos = new FileOutputStream(outputPath);
            document.write(fos);
            // 关闭资源
            fos.close();
            fis.close();
            document.close();
            System.out.println("模板填充完成!文件保存在: " + outputPath);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    private static void replaceParagraph(XWPFParagraph paragraph, Map<String, String> dataMap) {
        String paragraphText = paragraph.getText();
        for (Map.Entry<String, String> entry : dataMap.entrySet()) {
            if (paragraphText.contains(entry.getKey())) {
                List<XWPFRun> runs = paragraph.getRuns();
                TextSegment found = paragraph.searchText(entry.getKey(), new PositionInParagraph());
                if (found != null) {
                    // 替换文本
                    int beginRun = found.getBeginRun();
                    int endRun = found.getEndRun();
                    if (beginRun >= 0 && endRun >= 0) {
                        // 删除原有runs
                        for (int runPos = beginRun; runPos <= endRun; runPos++) {
                            paragraph.removeRun(runPos);
                        }
                        // 创建新run
                        XWPFRun newRun = paragraph.insertNewRun(beginRun);
                        newRun.setText(entry.getValue());
                        // 复制原有格式
                        if (runs.size() > 0 && runs.get(0) != null) {
                            XWPFRun styleRun = runs.get(0);
                            newRun.setFontFamily(styleRun.getFontFamily());
                            newRun.setFontSize(styleRun.getFontSize());
                            newRun.setBold(styleRun.isBold());
                            newRun.setItalic(styleRun.isItalic());
                        }
                    }
                }
            }
        }
    }
    public static void main(String[] args) {
        String templatePath = "/path/to/template.docx";
        String outputPath = "/path/to/output.docx";
//        fillTemplate(templatePath, outputPath, user);
    }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordUtil.java
@@ -6,6 +6,7 @@
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xwpf.usermodel.*;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
@@ -15,7 +16,9 @@
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Slf4j
@Component
@@ -110,35 +113,38 @@
    }
    public String generatePdf(String basePackagePath, String templateFileName, Object templateParam, String fileName, String saveDirectory) {
    public String generatePdf(String basePackagePath, String templateFileName, Map<String,Object> templateParam, String fileName, String saveDirectory) {
        try {
            fillTemplate(basePackagePath+templateFileName, saveDirectory+fileName+".docx", templateParam);
            // 创建 Freemarker 的 Configuration 对象,设置默认的不兼容改进选项
            Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
            configuration.setDefaultEncoding("utf-8");
            // 设置模板加载器,加载模板文件
            configuration.setTemplateLoader(new ClassTemplateLoader(getClass(), basePackagePath));
            Template t = configuration.getTemplate(templateFileName, "utf-8");
            // 使用 URLEncoder 对文件名进行编码,以防止中文文件名在不同浏览器和操作系统下出现乱码问题
//            String encodedFileName = URLEncoder.encode(fileName + "_" + System.currentTimeMillis(), "utf-8");
            String encodedFileName =fileName ;
            // 定义保存文件的路径
            File saveDir = new File(saveDirectory);
            if (!saveDir.exists()) {
                saveDir.mkdirs();
            }
            // 定义文件名
            String filePath = saveDir.getAbsolutePath() + File.separator + encodedFileName + ".doc";
            // 创建 Writer 对象,用于将生成的文档写到文件中,缓存区大小设为 10KB
            Writer out = new BufferedWriter(new FileWriter(filePath), 10240);
            // 将模型数据与模板结合生成 Word 文档,写入到 Writer 对象中
            t.process(templateParam, out);
            out.close();
//            Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
//            configuration.setDefaultEncoding("utf-8");
//            // 设置模板加载器,加载模板文件
//            configuration.setTemplateLoader(new ClassTemplateLoader(getClass(), basePackagePath));
//            Template t = configuration.getTemplate(templateFileName, "utf-8");
//
//            // 使用 URLEncoder 对文件名进行编码,以防止中文文件名在不同浏览器和操作系统下出现乱码问题
////            String encodedFileName = URLEncoder.encode(fileName + "_" + System.currentTimeMillis(), "utf-8");
//            String encodedFileName =fileName ;
//
//            // 定义保存文件的路径
//            File saveDir = new File(saveDirectory);
//            if (!saveDir.exists()) {
//                saveDir.mkdirs();
//            }
//
//            // 定义文件名
//            String filePath = saveDir.getAbsolutePath() + File.separator + encodedFileName + ".doc";
//
//            // 创建 Writer 对象,用于将生成的文档写到文件中,缓存区大小设为 10KB
//            Writer out = new BufferedWriter(new FileWriter(filePath), 10240);
//
//            // 将模型数据与模板结合生成 Word 文档,写入到 Writer 对象中
//            t.process(templateParam, out);
//            out.close();
            String filePath = saveDirectory + File.separator + fileName + ".docx";
            File file = new File(filePath);
            // 检查文件是否存在
@@ -152,16 +158,263 @@
                fis.read(fileContent);
            }
            String test = pdfUtils.test(encodedFileName + ".doc");
            String test = pdfUtils.test(fileName + ".docx");
//            MultipartFile mockMultipartFile = new MockMultipartFile(encodedFileName+".doc", fileContent);
//            String s = ObsUploadUtil.obsUpload(mockMultipartFile);
            return test;
        } catch (IOException | TemplateException e) {
        } catch (IOException e) {
            log.error("生成pdf异常,异常原因:{}", e.getMessage(), e);
            throw new RuntimeException("生成pdf异常,异常原因:" + e.getMessage());
        }
    }
    public static void fillTemplate(String templatePath, String outputPath,Map<String, Object> dataMap) {
        try (FileInputStream fis = new FileInputStream(templatePath)) {
            // 设置默认编码为UTF-8
            System.setProperty("file.encoding", "UTF-8");
            XWPFDocument document = new XWPFDocument(fis);
            // 处理段落
            for (XWPFParagraph paragraph : document.getParagraphs()) {
                replaceParagraph(paragraph, dataMap);
            }
            // 处理表格
            for (XWPFTable table : document.getTables()) {
                for (XWPFTableRow row : table.getRows()) {
                    for (XWPFTableCell cell : row.getTableCells()) {
                        for (XWPFParagraph paragraph : cell.getParagraphs()) {
                            replaceParagraph(paragraph, dataMap);
                        }
                    }
                }
            }
            // 使用UTF-8编码保存文件
            try (FileOutputStream fos = new FileOutputStream(outputPath)) {
                document.write(fos);
            }
            System.out.println("模板填充完成!文件保存在: " + outputPath);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    private static void replaceParagraph(XWPFParagraph paragraph, Map<String, Object> dataMap) {
        // 获取段落中所有runs
        List<XWPFRun> runs = paragraph.getRuns();
        if (runs == null || runs.isEmpty()) return;
        // 首先合并所有runs的文本,以便正确识别占位符
        StringBuilder fullText = new StringBuilder();
        for (XWPFRun run : runs) {
            String text = run.getText(0);
            if (text != null) {
                fullText.append(text);
            }
        }
        String paragraphText = fullText.toString();
        // 使用正则表达式查找所有占位符,包括括号内的
        Pattern pattern = Pattern.compile("\\$\\{[^}]+\\}|\\([^)]*\\$\\{[^}]+\\}[^)]*\\)");
        Matcher matcher = pattern.matcher(paragraphText);
        List<ReplacementInfo> replacements = new ArrayList<>();
        // 收集所有需要替换的信息
        while (matcher.find()) {
            String matched = matcher.group();
            int start = matcher.start();
            int end = matcher.end();
            // 找出涉及到的runs
            int startRun = -1;
            int endRun = -1;
            int currentPos = 0;
            for (int i = 0; i < runs.size(); i++) {
                XWPFRun run = runs.get(i);
                String runText = run.getText(0);
                if (runText == null) continue;
                int runLength = runText.length();
                if (startRun == -1 && currentPos + runLength > start) {
                    startRun = i;
                }
                if (currentPos + runLength >= end) {
                    endRun = i;
                    break;
                }
                currentPos += runLength;
            }
            if (startRun != -1 && endRun != -1) {
                // 处理括号内的占位符
                String replacement = processPlaceholder(matched, dataMap);
                replacements.add(new ReplacementInfo(startRun, endRun, matched, replacement));
            }
        }
        // 从后向前替换,避免位置变化影响
        Collections.sort(replacements, (a, b) -> b.startRun - a.startRun);
        for (ReplacementInfo info : replacements) {
            replaceRunRange(paragraph, info);
        }
    }
    private static String processPlaceholder(String text, Map<String, Object> dataMap) {
        // 处理括号内的占位符
        Pattern placeholderPattern = Pattern.compile("\\$\\{([^}]+)\\}");
        Matcher matcher = placeholderPattern.matcher(text);
        StringBuffer result = new StringBuffer();
        while (matcher.find()) {
            String placeholder = matcher.group(0); // 完整的占位符
            String key = matcher.group(1); // 占位符中的键
            String replacement = Objects.nonNull(dataMap.get("${" + key + "}"))?String.valueOf(dataMap.get("${" + key + "}")):"";
            if (replacement != null) {
                // 如果在括号内,保留括号
                if (text.startsWith("(") && text.endsWith(")")) {
                    matcher.appendReplacement(result, replacement);
                } else {
                    matcher.appendReplacement(result, Matcher.quoteReplacement(replacement));
                }
            }
        }
        matcher.appendTail(result);
        return result.toString();
    }
    private static class ReplacementInfo {
        int startRun;
        int endRun;
        String originalText;
        String replacementText;
        ReplacementInfo(int startRun, int endRun, String originalText, String replacementText) {
            this.startRun = startRun;
            this.endRun = endRun;
            this.originalText = originalText;
            this.replacementText = replacementText;
        }
    }
    private static void replaceRunRange(XWPFParagraph paragraph, ReplacementInfo info) {
        List<XWPFRun> runs = paragraph.getRuns();
        // 保存第一个run的样式
        XWPFRun styleRun = runs.get(info.startRun);
        RunStyle style = new RunStyle(styleRun);
        // 删除范围内的所有runs
        for (int i = info.endRun; i >= info.startRun; i--) {
            paragraph.removeRun(i);
        }
        // 创建新的run并设置文本
        XWPFRun newRun = paragraph.insertNewRun(info.startRun);
        newRun.setText(info.replacementText);
        // 应用样式
        style.applyStyle(newRun);
    }
    // 用于保存和恢复运行样式的辅助类
    private static class RunStyle {
        String fontFamily;
        int fontSize;
        boolean bold;
        boolean italic;
        String color;
        UnderlinePatterns underline;
        RunStyle(XWPFRun run) {
            this.fontFamily = run.getFontFamily();
            this.fontSize = run.getFontSize();
            this.bold = run.isBold();
            this.italic = run.isItalic();
            this.color = run.getColor();
            this.underline = run.getUnderline();
        }
        void applyStyle(XWPFRun run) {
            if (fontFamily != null) {
                run.setFontFamily(fontFamily);
                run.setFontFamily(fontFamily, XWPFRun.FontCharRange.eastAsia);
            }
            if (fontSize != -1) {
                run.setFontSize(fontSize);
            }
            run.setBold(bold);
            run.setItalic(italic);
            if (color != null) {
                run.setColor(color);
            }
            if (underline != null) {
                run.setUnderline(underline);
            }
        }
    }
//    public String generatePdf(String basePackagePath, String templateFileName, Object templateParam, String fileName, String saveDirectory) {
//        try {
//            // 创建 Freemarker 的 Configuration 对象,设置默认的不兼容改进选项
//            Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
//            configuration.setDefaultEncoding("utf-8");
//            // 设置模板加载器,加载模板文件
//            configuration.setTemplateLoader(new ClassTemplateLoader(getClass(), basePackagePath));
//            Template t = configuration.getTemplate(templateFileName, "utf-8");
//
//            // 使用 URLEncoder 对文件名进行编码,以防止中文文件名在不同浏览器和操作系统下出现乱码问题
////            String encodedFileName = URLEncoder.encode(fileName + "_" + System.currentTimeMillis(), "utf-8");
//            String encodedFileName =fileName ;
//
//            // 定义保存文件的路径
//            File saveDir = new File(saveDirectory);
//            if (!saveDir.exists()) {
//                saveDir.mkdirs();
//            }
//
//            // 定义文件名
//            String filePath = saveDir.getAbsolutePath() + File.separator + encodedFileName + ".doc";
//
//            // 创建 Writer 对象,用于将生成的文档写到文件中,缓存区大小设为 10KB
//            Writer out = new BufferedWriter(new FileWriter(filePath), 10240);
//
//            // 将模型数据与模板结合生成 Word 文档,写入到 Writer 对象中
//            t.process(templateParam, out);
//            out.close();
//
//            File file = new File(filePath);
//
//            // 检查文件是否存在
//            if (!file.exists()) {
//                throw new FileNotFoundException("文件不存在: " + filePath);
//            }
//
//            // 读取文件内容
//            byte[] fileContent = new byte[(int) file.length()];
//            try (FileInputStream fis = new FileInputStream(file)) {
//                fis.read(fileContent);
//            }
//
//            String test = pdfUtils.test(encodedFileName + ".doc");
////            MultipartFile mockMultipartFile = new MockMultipartFile(encodedFileName+".doc", fileContent);
////            String s = ObsUploadUtil.obsUpload(mockMultipartFile);
//            return test;
//        } catch (IOException | TemplateException e) {
//            log.error("生成pdf异常,异常原因:{}", e.getMessage(), e);
//            throw new RuntimeException("生成pdf异常,异常原因:" + e.getMessage());
//        }
//    }
ruoyi-admin/src/main/resources/application-test.yml
@@ -17,7 +17,7 @@
# 开发环境配置
server:
  # 服务器的HTTP端口,默认为8080
  port: 8080
  port: 8081
  servlet:
    # 应用的访问路径
    context-path: /
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/IndexController.java
@@ -14,12 +14,14 @@
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.bo.ProcessTaskListBO;
import com.ruoyi.system.dto.TBillDto;
import com.ruoyi.system.dto.TInvoiceDTO;
import com.ruoyi.system.model.*;
import com.ruoyi.system.query.*;
import com.ruoyi.system.service.*;
import com.ruoyi.system.vo.*;
import com.ruoyi.web.controller.tool.MyFileUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import jdk.nashorn.internal.parser.Token;
@@ -27,9 +29,13 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@@ -72,6 +78,13 @@
    private ISysUserService sysUserService;
    @Autowired
    private TContractRentTypeService contractRentTypeService;
    @Autowired
    private StateProcessTemplateService processTemplateService;
    public static void main(String[] args) throws IOException {
        File file = new File("D:\\wechatFiles\\WeChat Files\\wxid_25nztsudcon722\\FileStorage\\File\\2025-03\\2.mp3");
        MultipartFile multipartFile = MyFileUtil.fileToMultipartFile(file, "3333");
    }
    /**
     * 获取轮播图管理列表
     */
@@ -109,6 +122,8 @@
        return R.ok(myToDoVO);
    }
    @ApiOperation(value = "租户-当前在租房源")
    @PostMapping(value = "/tenant/myHouse")
    public R<List<MyHouseVO>> myHouse() {
@@ -132,7 +147,7 @@
                    myToDoVO.setHouseAddress(tHouse.getHouseAddress());
                    myToDoVO.setMonthRent(contract.getMonthRent());
                    myToDoVO.setPayType(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CONTRACT_PAY_TYPE, contract.getPayType()));
                    myToDoVO.setMonth(bill.getPayFeesTime() == null ? bill.getPayableFeesTime().getMonth() + "月" : bill.getPayFeesTime().getMonth() + "月");
                    myToDoVO.setMonth(bill.getPayFeesTime() == null ? bill.getPayableFeesTime().getMonth().getValue() + "月" : bill.getPayFeesTime().getMonth().getValue() + "月");
                    myToDoVO.setHouseArea(tHouse.getHouseArea());
                    myToDoVO.setHouseType(tHouse.getHouseType());
                    myToDoVO.setEndTime(DateUtils.localDateTimeToStringYear(contract.getEndTime()));
@@ -180,7 +195,7 @@
                myToDoVO.setHouseAddress(tHouse.getHouseAddress());
                myToDoVO.setMonthRent(contract.getMonthRent());
                myToDoVO.setPayType(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CONTRACT_PAY_TYPE, contract.getPayType()));
                myToDoVO.setMonth(bill.getPayFeesTime() == null ? bill.getPayableFeesTime().getMonth() + "月" : bill.getPayFeesTime().getMonth() + "月");
                myToDoVO.setMonth(bill.getPayFeesTime() == null ? bill.getPayableFeesTime().getMonth().getValue() + "月" : bill.getPayFeesTime().getMonth().getValue() + "月");
                myToDoVO.setHouseArea(tHouse.getHouseArea());
                myToDoVO.setHouseType(tHouse.getHouseType());
                myToDoVO.setEndTime(DateUtils.localDateTimeToStringYear(contract.getEndTime()));
@@ -211,16 +226,24 @@
    @PostMapping(value = "/admin/myToDo")
    public R<MyToDoVO> adminMyToDo() {
        MyToDoVO myToDoVO = new MyToDoVO();
        LoginUserApplet loginUserApplet = tokenService.getLoginUserApplet();
        if (loginUserApplet != null) {
            long examineCount = contractService.count(new LambdaQueryWrapper<TContract>()
                    .eq(TContract::getStatus, 2));
            myToDoVO.setExamineCount((int) examineCount);
        LoginUser loginUser = tokenService.getLoginUser();
        if (loginUser != null) {
//            long examineCount = contractService.count(new LambdaQueryWrapper<TContract>()
//                    .eq(TContract::getStatus, 2));
            ProcessTaskListBO processTaskListBO = new ProcessTaskListBO();
            PageInfo<ProcessTaskListVO> processTaskListVOPageInfo = processTemplateService.waitTaskPage(processTaskListBO);
            myToDoVO.setExamineCount((int) processTaskListVOPageInfo.getTotal());
            List<String> contractIds = billService.lambdaQuery().eq(TBill::getPayFeesStatus, 4).list()
                    .stream().map(TBill::getContractId).collect(Collectors.toList());
            int overdueCount = contractService.lambdaQuery().in(TContract::getId, contractIds).list()
                    .stream().map(TContract::getTenantId).distinct().collect(Collectors.toList()).size();
            myToDoVO.setOverdueCount(overdueCount);
            if(contractIds.isEmpty()){
                myToDoVO.setOverdueCount(0);
            }else{
                int overdueCount = contractService.lambdaQuery().in(TContract::getId, contractIds).list()
                        .stream().map(TContract::getTenantId).distinct().collect(Collectors.toList()).size();
                myToDoVO.setOverdueCount(overdueCount);
            }
        } else {
            myToDoVO.setExamineCount(0);
            myToDoVO.setOverdueCount(0);
@@ -244,10 +267,11 @@
    @GetMapping(value = "/getDetailById")
    public R<TCheckAcceptRecordVO> getDetailById(@RequestParam String id) {
        TCheckAcceptRecord checkAcceptRecord = checkAcceptRecordService.getById(id);
        checkAcceptRecord.setCleanSituation(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION, checkAcceptRecord.getCleanSituation()));
        checkAcceptRecord.setOverallSituation(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION, checkAcceptRecord.getOverallSituation()));
        checkAcceptRecord.setDeviceSituation(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION, checkAcceptRecord.getDeviceSituation()));
        checkAcceptRecord.setFurnitureSituation(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION, checkAcceptRecord.getFurnitureSituation()));
//        checkAcceptRecord.setCleanSituation(checkAcceptRecord.getCleanSituation());
//        checkAcceptRecord.setOverallSituation(StringUtils.hasLength(checkAcceptRecord.getOverallSituation())?(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,checkAcceptRecord.getOverallSituation())):"");
//        checkAcceptRecord.setDeviceSituation(StringUtils.hasLength(checkAcceptRecord.getDeviceSituation())?(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,checkAcceptRecord.getDeviceSituation())):"");
//        checkAcceptRecord.setFurnitureSituation(StringUtils.hasLength(checkAcceptRecord.getDeviceSituation())?(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,checkAcceptRecord.getFurnitureSituation())):"");
        TCheckAcceptRecordVO checkAcceptRecordVO = new TCheckAcceptRecordVO();
        BeanUtils.copyProperties(checkAcceptRecord, checkAcceptRecordVO);
        // 查询合同信息
@@ -269,6 +293,7 @@
        dto.setCode(replace.substring(2)+String.format("%03d", size+1));
        dto.setStatus(true);
        // 添加验收记录
        dto.setCheckPerson(tokenService.getLoginUser().getUser().getNickName());
        checkAcceptRecordService.updateById(dto);
        return R.ok();
    }
@@ -350,6 +375,12 @@
        TContract contract = contractService.getById(bill.getContractId());
        THouse tHouse = houseService.getById(contract.getHouseId());
        res.setHouse(tHouse);
        res.setConcatStartTime(contract.getStartTime());
        res.setConcatEndTime(contract.getEndTime());
        res.setPartyOnePhone(contract.getPartyOnePhone());
        res.setPartyOnePerson(contract.getPartyTwoPerson());
        res.setMonthRent(bill.getPayableFeesMoney());
        res.setPayType(contract.getPayType());
        return R.ok(res);
    }
    @ApiOperation(value = "管理员-我的审批分页列表")
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TBillController.java
@@ -1,14 +1,17 @@
package com.ruoyi.web.controller.api;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.basic.PageInfo;
import com.ruoyi.common.constant.DictConstants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.CodeGenerateUtils;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.dto.BatchBillDTO;
import com.ruoyi.system.dto.TBillDto;
import com.ruoyi.system.dto.TInvoiceDTO;
import com.ruoyi.system.model.*;
@@ -53,6 +56,8 @@
    TInvoiceToBillService invoiceToBillService;
    @Autowired
    TokenService tokenService;
    @Autowired
    TTenantService tenantService;
    @ApiOperation(value = "缴费账单查询分页列表")
    @PostMapping("list")
@@ -68,7 +73,6 @@
        LocalDateTime localDateTime2 = LocalDateTime.now().withYear(2025).withMonth(11).withDayOfMonth(28);
        long allDays = ChronoUnit.DAYS.between(localDateTime1, localDateTime2);
        System.err.println(allDays);
    }
    @ApiOperation(value = "缴费账单查询列表")
@@ -78,6 +82,18 @@
        query.setUserId(userId);
        List<String> billIds = tBillService.getBillIds(query);
        return R.ok(billIds);
    }
    @ApiOperation(value = "跳转批量缴费")
    @PostMapping("/batchBill")
    public R<String> batchBill(@RequestBody BatchBillDTO dto){
        String userId = tokenService.getLoginUserApplet().getUserId();
        List<String> billIds = dto.getBillIds();
        Integer count = tBillService.batchBillCount(userId, billIds);
        if(count>0){
            return R.fail("请优先缴纳水电费");
        }
        return R.ok();
    }
    @ApiOperation(value = "查看缴费账单详情")
@@ -112,6 +128,13 @@
    @ApiOperation(value = "缴费账单开票")
    @PostMapping(value = "/invoice")
    public R<String> invoice(@RequestBody TInvoiceDTO dto) {
        String userId = tokenService.getLoginUserApplet().getUserId();
        dto.setApplyName(tenantService.getById(userId).getResidentName());
        String code;
        do {
            code = CodeGenerateUtils.generateVolumeSn();
        } while (invoiceService.count(Wrappers.lambdaQuery(TInvoice.class).eq(TInvoice::getInvoiceNumber, code)) > 0);
        dto.setInvoiceNumber(CodeGenerateUtils.generateVolumeSn());
        // 添加开票信息
        invoiceService.save(dto);
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TFaultRepairMessageController.java
@@ -4,6 +4,8 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.SmsUtil;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.dto.TFaultRepairMessageDTO;
import com.ruoyi.system.model.*;
@@ -14,6 +16,7 @@
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
@@ -41,9 +44,13 @@
    private final TokenService tokenService;
    private final TContractService contractService;
    private final THouseService houseService;
    private final SmsUtil smsUtil;
    private final ISysUserService sysUserService;
    @Autowired
    public TFaultRepairMessageController(TFaultRepairMessageService tFaultRepairMessageService, TItemService itemService, TItemTypeService itemTypeService, TFaultAreaDicService faultAreaDicService, TFaultDescribeDicService faultDescribeDicService, TokenService tokenService, TContractService contractService, THouseService houseService) {
    public TFaultRepairMessageController(TFaultRepairMessageService tFaultRepairMessageService, TItemService itemService, TItemTypeService itemTypeService, TFaultAreaDicService faultAreaDicService, TFaultDescribeDicService faultDescribeDicService, TokenService tokenService, TContractService contractService, THouseService houseService,SmsUtil smsUtil
    ,ISysUserService sysUserService) {
        this.tFaultRepairMessageService = tFaultRepairMessageService;
        this.itemService = itemService;
        this.itemTypeService = itemTypeService;
@@ -52,6 +59,8 @@
        this.tokenService = tokenService;
        this.contractService = contractService;
        this.houseService = houseService;
        this.smsUtil = smsUtil;
        this.sysUserService = sysUserService;
    }
    /**
@@ -63,7 +72,7 @@
        List<TItemTypeVO> itemTypes = itemTypeService.getItemList(itemName);
        List<TItem> items = itemService.list();
        itemTypes.forEach(itemType -> {
            itemType.setItemList(items.stream().filter(item -> itemType.getId().equals(item.getTypeId())).collect(Collectors.toList()));
            itemType.setItemList(items.stream().filter(item -> itemType.getId().equals(item.getTypeId()) && item.getItemName().contains(itemName)).collect(Collectors.toList()));
        });
        return R.ok(itemTypes);
    }
@@ -120,6 +129,15 @@
                .likeRight(TFaultRepairMessage::getCreateTime, LocalDate.now())).size();
        dto.setCode(replace.substring(2)+String.format("%03d", size+1));
        tFaultRepairMessageService.save(dto);
        List<SysUser> sysUsers = sysUserService.selectList();
        if (dto.getRepairType()==2){
            for (SysUser sysUser : sysUsers) {
                if (StringUtils.hasLength(sysUser.getPhonenumber())){
                    System.err.println("发送短信");
                    smsUtil.sendSms(sysUser.getPhonenumber(),"2375194",new String[]{});
                }
            }
        }
        return R.ok();
    }
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TInformationController.java
@@ -51,7 +51,9 @@
    public R<TInformation> getDetailById(@RequestParam String id) {
        // 处理查看次数
        redisCache.increment(Constants.INFORMATION_VIEW + id);
        return R.ok(informationService.getById(id));
        TInformation information = informationService.getById(id);
        information.setViewCount(redisCache.getCacheObject(Constants.INFORMATION_VIEW + information.getId()));
        return R.ok(information);
    }
}
ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
@@ -9,6 +9,7 @@
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.domain.model.LoginUserApplet;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.SmsUtil;
import com.ruoyi.framework.web.service.TokenService;
@@ -101,16 +102,8 @@
    {
        AjaxResult ajax = AjaxResult.success();
        // 生成令牌
        LoginUser loginUser = loginService.loginCode(loginBody.getUsername(), loginBody.getCode());
        ajax.put(Constants.TOKEN, tokenService.createToken(loginUser));
        List<SysRole> roles = loginUser.getUser().getRoles();
        if(CollectionUtils.isEmpty(roles)){
            return AjaxResult.error("请关联角色!");
        }
        List<SysMenu> menus = roleService.roleInfoFromUserId(loginUser.getUserId());
        ajax.put("menus",menus);
        ajax.put("roleName",roles.get(0).getRoleName());
        LoginUserApplet loginUser = loginService.loginCodeApplet(loginBody.getUsername(), loginBody.getCode());
        ajax.put(Constants.TOKEN, tokenService.createTokenApplet(loginUser));
        ajax.put("userInfo",loginUser);
        return ajax;
    }
ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/MyFileUtil.java
New file
@@ -0,0 +1,128 @@
package com.ruoyi.web.controller.tool;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.nio.file.Files;
/**
 * @author RainbowCloud
 */
public class MyFileUtil {
    /**
     * 将 File 转换为 MultipartFile。
     *
     * @param file      要转换的文件
     * @param fieldName 字段名,通常用于表单中的文件字段名
     * @return 转换后的 MultipartFile
     * @throws IOException 如果发生I/O错误
     */
    public static MultipartFile fileToMultipartFile(File file, String fieldName) throws IOException {
        try {
            if (file == null || !file.exists()) {
                throw new FileNotFoundException("文件未找到:" + file);
            }
            byte[] content = Files.readAllBytes(file.toPath());
            return new ByteArrayMultipartFile(content, file.getName(), fieldName, Files.probeContentType(file.toPath()));
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            // 删除临时文件
            file.delete();
        }
    }
    /**
     * 将 MultipartFile 转换为 File。
     *
     * @param multipartFile 要转换的 MultipartFile
     * @return 转换后的 File
     * @throws IOException 如果发生I/O错误
     */
    public static File multipartFileToFile(MultipartFile multipartFile) throws IOException {
        if (multipartFile.isEmpty()) {
            throw new IOException("传入的MultipartFile为空");
        }
        String originalFilename = multipartFile.getOriginalFilename();
        String tempFileSuffix = originalFilename != null ? originalFilename.substring(originalFilename.lastIndexOf('.')) : ".tmp";
        File tempFile = File.createTempFile("temp", tempFileSuffix);
        try (InputStream ins = multipartFile.getInputStream();
             OutputStream os = new FileOutputStream(tempFile)) {
            byte[] buffer = new byte[8192];
            int bytesRead;
            while ((bytesRead = ins.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
        }
        return tempFile;
    }
    /**
     * 内置一个简单的 MultipartFile 实现类,用于File转换
     */
    private static class ByteArrayMultipartFile implements MultipartFile {
        private final byte[] content;
        private final String name;
        private final String originalFilename;
        private final String contentType;
        /**
         * 构造函数
         *
         * @param content         文件内容
         * @param originalFilename 文件原始名字
         * @param name            字段名
         * @param contentType     文件类型
         */
        public ByteArrayMultipartFile(byte[] content, String originalFilename, String name, String contentType) {
            this.content = content;
            this.originalFilename = originalFilename;
            this.name = name;
            this.contentType = contentType;
        }
        @Override
        public String getName() {
            return this.name;
        }
        @Override
        public String getOriginalFilename() {
            return this.originalFilename;
        }
        @Override
        public String getContentType() {
            return this.contentType;
        }
        @Override
        public boolean isEmpty() {
            return (this.content == null || this.content.length == 0);
        }
        @Override
        public long getSize() {
            return this.content.length;
        }
        @Override
        public byte[] getBytes() {
            return this.content;
        }
        @Override
        public InputStream getInputStream() {
            return new ByteArrayInputStream(this.content);
        }
        @Override
        public void transferTo(File dest) throws IOException, IllegalStateException {
            try (OutputStream os = new FileOutputStream(dest)) {
                os.write(this.content);
            }
        }
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/constant/DictConstants.java
@@ -28,7 +28,7 @@
     */
    public static final String DICT_TYPE_BUSINESS_ATTRIBUTES = "t_business_attributes";
    /**
     * 合同状态 1=待提交 2=待审批 3=未签订 4=已签订 5=已驳回 6=已终止 7=待结算 8=已结算
     * 合同状态 1=待提交 2=待审批 3=未签订 4=已签订 5=已驳回 6=已终止 7=待结算 8=已结算 9合同已签订待审
     */
    public static final String DICT_TYPE_CONTRACT_STATUS = "t_contract_status";
    /**
ruoyi-common/src/main/java/com/ruoyi/common/utils/CodeGenerateUtils.java
@@ -3,6 +3,8 @@
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;
/**
 * @Description
@@ -67,7 +69,7 @@
        dateTime = dateTime.substring(2);
        String timestampPart = "" + (Math.random() * 10000) * (System.currentTimeMillis() / 10000);
        timestampPart = timestampPart.replace(".", "").replace("E", "");
        timestampPart = timestampPart.substring(0, 5);
        timestampPart = timestampPart.substring(0, 0);
        return dateTime + timestampPart;
    }
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java
@@ -2,8 +2,14 @@
import javax.annotation.Resource;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.core.domain.entity.TTenantResp;
import com.ruoyi.common.core.domain.model.LoginUserApplet;
import com.ruoyi.common.enums.UserStatus;
import com.ruoyi.system.model.TTenant;
import com.ruoyi.system.service.TTenantService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
@@ -52,6 +58,8 @@
    
    @Autowired
    private ISysUserService userService;
    @Autowired
    private TTenantService tenantService;
    @Autowired
    private ISysConfigService configService;
@@ -127,8 +135,6 @@
     */
    public LoginUser loginCode(String username,String code)
    {
        // 登录前置校验
        if (StringUtils.isEmpty(username)){
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));
@@ -163,6 +169,43 @@
        return loginUser;
    }
    /**
     * 登录验证
     *
     * @param username 用户名
     * @param code 验证码
     * @return 结果
     */
    public LoginUserApplet loginCodeApplet(String username, String code)
    {
        // 登录前置校验
        if (StringUtils.isEmpty(username)){
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));
            throw new UserNotExistsException();
        }
        // 用户验证
        TTenant user = tenantService.getOne(Wrappers.<TTenant>lambdaQuery().eq(TTenant::getAccount,username));
        if (StringUtils.isNull(user)){
            log.info("登录用户:{} 不存在.", username);
            throw new ServiceException(MessageUtils.message("user.not.exists"));
        } else if (user.getDisabled()) {
            log.info("登录用户:{} 已被删除.", username);
            throw new ServiceException(MessageUtils.message("user.password.delete"));
        }
        // 校验验证码
        Object cacheObject = redisCache.getCacheObject(user.getAccount());
        if(!code.equals(String.valueOf(cacheObject))){
            log.info("登录用户:{} 短信验证码错误{}", username,code);
            throw new ServiceException("短信验证码错误");
        }
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
        TTenantResp tTenantResp = new TTenantResp();
        BeanUtils.copyProperties(user,tTenantResp);
        LoginUserApplet loginUser = new LoginUserApplet(user.getId(), null, tTenantResp, null);
        // 生成token
        return loginUser;
    }
    /**
     * 校验验证码
ruoyi-system/src/main/java/com/ruoyi/system/dto/BatchBillDTO.java
New file
@@ -0,0 +1,18 @@
package com.ruoyi.system.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
@ApiModel(value = "批量缴费校验")
public class BatchBillDTO implements Serializable {
    @ApiModelProperty(value = "账单id集合")
    private List<String> billIds;
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TBillMapper.java
@@ -44,4 +44,6 @@
    BigDecimal statisticsPayed();
    BigDecimal statisticsOverdue();
    Integer batchBillCount(@Param("userId")String userId, @Param("billIds")List<String> billIds);
}
ruoyi-system/src/main/java/com/ruoyi/system/model/TBill.java
@@ -1,18 +1,16 @@
package com.ruoyi.system.model;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.*;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.TableField;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.ruoyi.common.core.domain.BaseModel;
import com.ruoyi.common.core.domain.BasePage;
import io.swagger.annotations.ApiModel;
@@ -34,10 +32,43 @@
@EqualsAndHashCode(callSuper = false)
@TableName("t_bill")
@ApiModel(value="TBill对象", description="租金账单")
public class TBill extends BaseModel {
public class TBill implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 新增执行
     */
    @ApiModelProperty(value = "记录创建人,前端忽略")
    @JsonIgnore
    @TableField(value = "create_by", fill = FieldFill.INSERT)
    private String createBy;
    /**
     * 新增和更新执行
     */
    @ApiModelProperty(value = "记录修改人,前端忽略")
    @TableField(value = "update_by", fill = FieldFill.INSERT_UPDATE)
    private String updateBy;
    /**
     * 删除  未删除
     */
    @JsonIgnore
    @TableField("`disabled`")
    @TableLogic
    private Boolean disabled;
    @ApiModelProperty(value = "记录创建时间,前端忽略")
    @TableField("create_time")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime createTime;
    /**
     * 最后修改时间
     */
    @ApiModelProperty(value = "记录修改时间,前端忽略")
    @TableField("update_time")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime updateTime;
    @TableId(value = "id", type = IdType.ASSIGN_ID)
    private String id;
ruoyi-system/src/main/java/com/ruoyi/system/model/TContract.java
@@ -146,7 +146,7 @@
     * 7    待结算
     * 8    已结算
     */
    @ApiModelProperty(value = "合同状态 1=待提交 2=待审批 3=未签订 4=已签订 5=已驳回 6=已终止 7=待结算 8=已结算")
    @ApiModelProperty(value = "合同状态 1=待提交 2=待审批 3=未签订 4=已签订 5=已驳回 6=已终止 7=待结算 8=已结算 9 = 签订待审核")
    @TableField("status")
    private String status;
    @ApiModelProperty(value = "内存大小多个文件逗号拼接")
@@ -172,6 +172,6 @@
    private String houseAddress;
    @ApiModelProperty(value = "审批流实例id")
    @TableField(exist = false)
    private Long instanceId;
    private String instanceId;
}
ruoyi-system/src/main/java/com/ruoyi/system/query/TBillQuery.java
@@ -22,6 +22,11 @@
     */
    @ApiModelProperty("租户ID")
    private String userId;
    /**
     * 账单类型
     */
    @ApiModelProperty("账单类型 1=租金 2=押金 3=生活费用 4=房屋验收")
    private Integer billType;
ruoyi-system/src/main/java/com/ruoyi/system/service/TBillService.java
@@ -85,4 +85,6 @@
    Boolean cashPay(CachPayDto offlinePayDto);
    BillStatisticsDto statistics();
    Integer batchBillCount(String userId, List<String> billIds);
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/FlowListenerService.java
@@ -30,15 +30,22 @@
import com.ruoyi.common.enums.ProcessCategoryEnum;
import com.ruoyi.common.enums.SubmitStatusEnum;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.system.service.ISysRoleService;
import com.ruoyi.system.service.TContractService;
import com.ruoyi.system.mapper.TCheckAcceptRecordMapper;
import com.ruoyi.system.model.*;
import com.ruoyi.system.service.*;
import com.ruoyi.system.task.base.QuartzManager;
import com.ruoyi.system.task.base.TimeJobType;
import com.ruoyi.system.task.jobs.StateProcessJob;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@@ -68,8 +75,20 @@
    private final ISysRoleService sysRoleService;
    private final FlwTaskMapper flwTaskMapper;
    private final TContractService contractService;
    private final TContractRentTypeService contractRentTypeService;
    private final TBillService billService;
    private final TCheckAcceptRecordMapper checkAcceptRecordMapper;
    private final THouseService houseService;
    public static void main(String[] args) {
//        LocalDate localDate1 = LocalDate.now().withYear(2025).withMonth(4).withDayOfMonth(1);
//        LocalDate localDate2 = LocalDate.now().withYear(2025).withMonth(4).withDayOfMonth(16);
        LocalDateTime localDate1 = LocalDateTime.now().withYear(2025).withMonth(4).withDayOfMonth(16);
        LocalDateTime localDate2 = LocalDateTime.now().withYear(2025).withMonth(10).withDayOfMonth(24);
        long between = ChronoUnit.DAYS.between(localDate1, localDate2)+1;
        System.err.println(between);
    }
    @Override
    public boolean notify(EventType eventType, Supplier<FlwTask> supplier, NodeModel nodeModel, FlowCreator flowCreator) {
        FlwTask flwTask = supplier.get();
@@ -101,7 +120,7 @@
                            new ImmutableMap.Builder<String, Long>().
                                    put("id", flwTask.getId())
                                    .build();
                    QuartzManager.addJob(StateProcessJob.class, (StateProcessJob.name+flwTask.getId()).toUpperCase(), TimeJobType.AUTO_AUDIT,new Date(new Date().getTime()+48*60*60*1000L), maps);
                    QuartzManager.addJob(StateProcessJob.class, (StateProcessJob.name + flwTask.getId()).toUpperCase(), TimeJobType.AUTO_AUDIT, new Date(new Date().getTime() + 48 * 60 * 60 * 1000L), maps);
                    //对比发起人和节点审批人
@@ -124,9 +143,9 @@
                    List<String> roleIds = nodeAssigneeList.stream().map(NodeAssignee::getId).collect(Collectors.toList());
                    List<SysRole> actorList = sysRoleService.selectRoleByUserIds(roleIds);
                    List<FlwTaskActor> flwTaskActors = new ArrayList<>();
                    if(ObjectUtils.isNotEmpty(actorList)) {
                    if (ObjectUtils.isNotEmpty(actorList)) {
                        for (SysRole sysRole : actorList) {
                            if(ObjectUtil.isEmpty(sysRole.getRoleName())) {
                            if (ObjectUtil.isEmpty(sysRole.getRoleName())) {
                                continue;
                            }
                            NodeAssignee nodeAssignee = new NodeAssignee();
@@ -152,7 +171,7 @@
                            new ImmutableMap.Builder<String, Long>().
                                    put("id", flwTask.getId())
                                    .build();
                    QuartzManager.addJob(StateProcessJob.class, (StateProcessJob.name+flwTask.getId()).toUpperCase(), TimeJobType.AUTO_AUDIT,new Date(new Date().getTime()+48*60*60*1000L), maps);
                    QuartzManager.addJob(StateProcessJob.class, (StateProcessJob.name + flwTask.getId()).toUpperCase(), TimeJobType.AUTO_AUDIT, new Date(new Date().getTime() + 48 * 60 * 60 * 1000L), maps);
                    //对比发起人和节点审批人
@@ -185,8 +204,8 @@
                log.error("节点不存在 TaskName:{} InstanceId:{}", flwTask.getTaskName(), flwTask.getInstanceId());
                return true;
            }
            long count = flwTaskMapper.selectCount(new LambdaQueryWrapper<FlwTask>().eq(FlwTask::getInstanceId,flwTask.getInstanceId()));
            if(count > 0){
            long count = flwTaskMapper.selectCount(new LambdaQueryWrapper<FlwTask>().eq(FlwTask::getInstanceId, flwTask.getInstanceId()));
            if (count > 0) {
                return true;
            }
            //最后一个节点
@@ -209,7 +228,7 @@
                System.out.println("流程完成:" + flwTask.getVariable());
                handlerBusiness(flwTask.getVariable(), 1);
            }
        } else if(eventType.eq(EventType.autoJump)){
        } else if (eventType.eq(EventType.autoJump)) {
            // 查询流程模型 自动跳转
            FlwExtInstance flwExtInstance = flwExtInstanceMapper.selectById(flwTask.getInstanceId());
            String modelContent = flwExtInstance.getModelContent();
@@ -219,8 +238,8 @@
                log.error("节点不存在 TaskName:{} InstanceId:{}", flwTask.getTaskName(), flwTask.getInstanceId());
                return true;
            }
            long count = flwTaskMapper.selectCount(new LambdaQueryWrapper<FlwTask>().eq(FlwTask::getInstanceId,flwTask.getInstanceId()));
            if(count > 0){
            long count = flwTaskMapper.selectCount(new LambdaQueryWrapper<FlwTask>().eq(FlwTask::getInstanceId, flwTask.getInstanceId()));
            if (count > 0) {
                return true;
            }
            //最后一个节点
@@ -244,21 +263,435 @@
        switch (categoryEnum) {
            case CATEGORY1: {
                // 合同新增审批
                int submitStatus = status==0?2:(status==1?3:5);
                int submitStatus = status == 0 ? 2 : (status == 1 ? 3 : 1);
                contractService.updateContractAuditStatus(processParameter.getString("projectId"), submitStatus);
                // TODO 发短信
                break;
            }
            case CATEGORY2: {
                // 合同签订审批
                int submitStatus = status==0?3:(status==1?4:5);
                int submitStatus = status == 0 ? 3 : (status == 1 ? 4 : 1);
                contractService.updateContractAuditStatus(processParameter.getString("projectId"), submitStatus);
                TContract contract = contractService.getById(processParameter.getString("projectId"));
                if(contract.getStatus().equals("4")){
                    // 修改房屋状态
                    THouse house = houseService.getById(contract.getHouseId());
                    if(Objects.nonNull(house)){
                        house.setLeaseStatus("2");
                        houseService.updateById(house);
                    }
                    List<TContractRentType> contractRentTypes = contractRentTypeService.list();
                    TContractRentType tContractRentType = contractRentTypes.stream().filter(e -> e.getContractId().equals(contract.getId())).findFirst().orElse(null);
                    // 生成第一笔账单
                    // 第一次应缴费日期
                    LocalDateTime firstPayTime = contract.getStartTime().plusDays(10).withHour(0).withMinute(0).withSecond(0);
                    TBill rentBill = new TBill();
                    rentBill.setContractId(contract.getId());
                    rentBill.setContractNumber(contract.getContractNumber());
                    rentBill.setPayableFeesTime(firstPayTime.toLocalDate());
                    rentBill.setPayFeesStatus("1");
                    rentBill.setBillType("1");
                    rentBill.setStartTime(contract.getStartPayTime());
                    if (rentBill.getStartTime().with(TemporalAdjusters.lastDayOfMonth()).isAfter(contract.getEndTime())) {
                        if (rentBill.getStartTime().getDayOfMonth() != 1) {
                            rentBill.setEndTime(rentBill.getStartTime().with(TemporalAdjusters.lastDayOfMonth()));
                        } else {
                            rentBill.setEndTime(rentBill.getStartTime().plusMonths(contract.getPayType().equals("1") ? 1 : contract.getPayType().equals("2") ? 3 : 12).with(TemporalAdjusters.lastDayOfMonth()));
                        }
                    } else {
                        rentBill.setEndTime(rentBill.getStartTime().with(TemporalAdjusters.lastDayOfMonth()));
                    }
                    if (tContractRentType != null && rentBill.getEndTime().isAfter(tContractRentType.getChangeTime())) {
                        long moneyDays = 0;
                        moneyDays = ChronoUnit.DAYS.between(tContractRentType.getChangeTime(), rentBill.getEndTime())+1;
                        // 计算租金变动的天数
                        contract.setChangeTime(tContractRentType.getChangeTime());
                        // 递增递减的租金
                        BigDecimal contractRentTypeMoney = new BigDecimal("0");
                        // 不递增递减的租金
                        BigDecimal originalMoney = new BigDecimal("0");
                        // 原租金
                        switch (tContractRentType.getIncreasingDecreasingType()) {
                            case 1:
                                switch (tContractRentType.getIncreasingDecreasing()) {
                                    case 1:
                                        contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().multiply(new BigDecimal(100).add(tContractRentType.getNumericalValue())).divide(new BigDecimal(100), 2, BigDecimal.ROUND_DOWN).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).setScale(2, BigDecimal.ROUND_DOWN));
                                        // 变动后的每月租金
                                        contract.setChangeRent(contract.getChangeRent().multiply(new BigDecimal(100).add(tContractRentType.getNumericalValue())).divide(new BigDecimal(100), 2, BigDecimal.ROUND_DOWN));
                                        break;
                                    case 2:
                                        contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().multiply((new BigDecimal(100).subtract(tContractRentType.getNumericalValue()))).divide(new BigDecimal(100), 2, BigDecimal.ROUND_DOWN).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).setScale(2, BigDecimal.ROUND_DOWN));
                                        contract.setChangeRent(contract.getChangeRent().multiply((new BigDecimal(100).subtract(tContractRentType.getNumericalValue()))).divide(new BigDecimal(100), 2, BigDecimal.ROUND_DOWN));
                                        break;
                                }
                                break;
                            case 2:
                                switch (tContractRentType.getIncreasingDecreasing()) {
                                    case 1:
                                        contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().add(tContractRentType.getNumericalValue())).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).setScale(2, BigDecimal.ROUND_DOWN);
                                        contract.setChangeRent(contract.getChangeRent().add(tContractRentType.getNumericalValue()));
                                        break;
                                    case 2:
                                        contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().subtract(tContractRentType.getNumericalValue())).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).setScale(2, BigDecimal.ROUND_DOWN);
                                        contract.setChangeRent(contract.getChangeRent().subtract(tContractRentType.getNumericalValue()));
                                        break;
                                }
                                break;
                        }
                        // 不需要涨租金的时间段
                        long originalDays = Math.abs(ChronoUnit.DAYS.between(contract.getFirstPayTime(), tContractRentType.getChangeTime()));
                        originalMoney = originalMoney.add(contract.getMonthRent().divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN))
                                .multiply(new BigDecimal(originalDays));
                        rentBill.setPayableFeesMoney(contractRentTypeMoney.add(originalMoney));
                        rentBill.setOutstandingMoney(rentBill.getPayableFeesMoney());
                    } else {
                        // 不走递增递减
                        long allDays = ChronoUnit.DAYS.between(contract.getStartPayTime(), rentBill.getEndTime())+1;
                        int dayOfMonth = rentBill.getStartTime().getDayOfMonth();
                        if (dayOfMonth == 1) {
                            rentBill.setPayableFeesMoney(contract.getMonthRent().multiply(new BigDecimal(contract.getPayType().equals("1") ? 1 : contract.getPayType().equals("2") ? 3 : 12)));
                        } else {
                            rentBill.setPayableFeesMoney(contract.getMonthRent().divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(allDays)));
                        }
                        rentBill.setOutstandingMoney(rentBill.getPayableFeesMoney());
                    }
                    // 押金账单
                    TBill depositBill = new TBill();
                    depositBill.setContractId(contract.getId());
                    depositBill.setContractNumber(contract.getContractNumber());
                    depositBill.setPayableFeesMoney(contract.getDeposit());
                    depositBill.setOutstandingMoney(depositBill.getPayableFeesMoney());
                    depositBill.setStartTime(contract.getStartPayTime());
                    depositBill.setEndTime(contract.getEndTime());
                    depositBill.setPayableFeesTime(firstPayTime.toLocalDate());
                    depositBill.setPayFeesStatus("1");
                    depositBill.setBillType("2");
                    contractService.updateById(contract);
                    billService.save(rentBill);
                    billService.save(depositBill);
                    // 生成后续账单
                    try {
                        TBill beforeBill = billService.lambdaQuery().eq(TBill::getContractId, contract.getId()).eq(TBill::getBillType, 1).orderByDesc(TBill::getStartTime)
                                .last("limit 1").one();
                        if (!beforeBill.getEndTime().toLocalDate().equals(contract.getEndTime().toLocalDate())) {
                            while (beforeBill.getEndTime().plusMonths(contract.getPayType().equals("1") ? 1 : contract.getPayType().equals("2") ? 3 : 12).isBefore(contract.getEndTime())) {
                                System.err.println("生成后续账单");
                                TBill tBill = new TBill();
                                tBill.setContractId(contract.getId());
                                tBill.setStartTime(beforeBill.getEndTime().plusDays(1));
                                tBill.setEndTime(beforeBill.getEndTime().plusMonths(contract.getPayType().equals("1") ? 1 : contract.getPayType().equals("2") ? 3 : 12).with(TemporalAdjusters.lastDayOfMonth()));
                                tBill.setContractNumber(contract.getContractNumber());
                                if (beforeBill.getEndTime().plusMonths(contract.getPayType().equals("1") ? 1 : contract.getPayType().equals("2") ? 3 : 12).getDayOfMonth() <= 15) {
                                    tBill.setPayableFeesTime(contract.getEndTime().toLocalDate());
                                } else {
                                    tBill.setPayableFeesTime(beforeBill.getEndTime().plusMonths(1).withDayOfMonth(15).toLocalDate());
                                }
                                tBill.setContractId(contract.getId());
                                if (contract.getIsIncreasing()) {
                                    System.err.println("执行递增递减");
                                    if (tContractRentType != null) {
                                        // 如果变过 并且时间在递增递减时间段内
                                        if (contract.getChangeTime() != null) {
                                            // 下次递增递减时间
                                            LocalDateTime changeTime = contract.getChangeTime().plusYears(tContractRentType.getCycleTime());
                                            // 之前已经涨、跌过租金了 需要判断周期是否还需要再涨、跌
                                            if (changeTime.isBefore(tBill.getEndTime()) && changeTime.isAfter(tBill.getStartTime())) {
                                                contract.setChangeTime(changeTime);
                                                // 租金递增递减的时长 天
                                                long moneyDays = Math.abs(ChronoUnit.DAYS.between(tContractRentType.getChangeTime(), tBill.getEndTime()))+1;
                                                // 递增递减的租金
                                                BigDecimal contractRentTypeMoney = new BigDecimal("0");
                                                // 不递增递减的租金
                                                BigDecimal originalMoney = new BigDecimal("0");
                                                // 原租金
                                                switch (tContractRentType.getIncreasingDecreasingType()) {
                                                    case 1:
                                                        switch (tContractRentType.getIncreasingDecreasing()) {
                                                            case 1:
                                                                contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().multiply(new BigDecimal(100).add(tContractRentType.getNumericalValue())).divide(new BigDecimal(100), 2, BigDecimal.ROUND_DOWN).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).setScale(2, BigDecimal.ROUND_DOWN));
                                                                // 变动后的每月租金
                                                                contract.setChangeRent(contract.getChangeRent().multiply(new BigDecimal(100).add(tContractRentType.getNumericalValue())).divide(new BigDecimal(100), 2, BigDecimal.ROUND_DOWN));
                                                                break;
                                                            case 2:
                                                                contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().multiply((new BigDecimal(100).subtract(tContractRentType.getNumericalValue()))).divide(new BigDecimal(100), 2, BigDecimal.ROUND_DOWN).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).setScale(2, BigDecimal.ROUND_DOWN));
                                                                contract.setChangeRent(contract.getChangeRent().multiply((new BigDecimal(100).subtract(tContractRentType.getNumericalValue()))).divide(new BigDecimal(100), 2, BigDecimal.ROUND_DOWN));
                                                                break;
                                                        }
                                                        break;
                                                    case 2:
                                                        switch (tContractRentType.getIncreasingDecreasing()) {
                                                            case 1:
                                                                contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().add(tContractRentType.getNumericalValue())).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).setScale(2, BigDecimal.ROUND_DOWN);
                                                                contract.setChangeRent(contract.getChangeRent().add(tContractRentType.getNumericalValue()));
                                                                break;
                                                            case 2:
                                                                contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().subtract(tContractRentType.getNumericalValue())).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).setScale(2, BigDecimal.ROUND_DOWN);
                                                                contract.setChangeRent(contract.getChangeRent().subtract(tContractRentType.getNumericalValue()));
                                                                break;
                                                        }
                                                        break;
                                                }
                                                // 不需要涨租金的时间段
                                                long originalDays = ChronoUnit.DAYS.between(tBill.getStartTime(), tContractRentType.getChangeTime());
                                                if (originalDays > 0) {
                                                    originalMoney = originalMoney.add(contract.getChangeRent().divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN))
                                                            .multiply(new BigDecimal(originalDays));
                                                }
                                                tBill.setPayableFeesMoney(contractRentTypeMoney.add(originalMoney));
                                                tBill.setOutstandingMoney(tBill.getPayableFeesMoney());
                                                contractService.updateById(contract);
                                            } else {
                                                // 不涨租金 用上次的
                                                tBill.setPayableFeesMoney(contract.getChangeRent().multiply(new BigDecimal(contract.getPayType().equals("1")?1:contract.getPayType().equals("2")?3:12)));
                                                tBill.setOutstandingMoney(tBill.getPayableFeesMoney());
                                            }
                                        }else{
                                            if (tContractRentType.getChangeTime().isBefore(tBill.getEndTime()) && tContractRentType.getChangeTime().isAfter(tBill.getStartTime())) {
                                                System.err.println("首次递增递减");
                                                contract.setChangeTime(tContractRentType.getChangeTime());
                                                // 租金递增递减的时长 天
                                                long moneyDays = Math.abs(ChronoUnit.DAYS.between(tContractRentType.getChangeTime(), tBill.getEndTime()))+1;
                                                // 递增递减的租金
                                                BigDecimal contractRentTypeMoney = new BigDecimal("0");
                                                // 不递增递减的租金
                                                BigDecimal originalMoney = new BigDecimal("0");
                                                // 原租金
                                                switch (tContractRentType.getIncreasingDecreasingType()) {
                                                    case 1:
                                                        switch (tContractRentType.getIncreasingDecreasing()) {
                                                            case 1:
                                                                contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().multiply(new BigDecimal(100).add(tContractRentType.getNumericalValue())).divide(new BigDecimal(100), 2, BigDecimal.ROUND_DOWN).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).setScale(2, BigDecimal.ROUND_DOWN));
                                                                // 变动后的每月租金
                                                                contract.setChangeRent(contract.getChangeRent().multiply(new BigDecimal(100).add(tContractRentType.getNumericalValue())).divide(new BigDecimal(100), 2, BigDecimal.ROUND_DOWN));
                                                                break;
                                                            case 2:
                                                                contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().multiply((new BigDecimal(100).subtract(tContractRentType.getNumericalValue()))).divide(new BigDecimal(100), 2, BigDecimal.ROUND_DOWN).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).setScale(2, BigDecimal.ROUND_DOWN));
                                                                contract.setChangeRent(contract.getChangeRent().multiply((new BigDecimal(100).subtract(tContractRentType.getNumericalValue()))).divide(new BigDecimal(100), 2, BigDecimal.ROUND_DOWN));
                                                                break;
                                                        }
                                                        break;
                                                    case 2:
                                                        switch (tContractRentType.getIncreasingDecreasing()) {
                                                            case 1:
                                                                contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().add(tContractRentType.getNumericalValue())).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).setScale(2, BigDecimal.ROUND_DOWN);
                                                                contract.setChangeRent(contract.getChangeRent().add(tContractRentType.getNumericalValue()));
                                                                break;
                                                            case 2:
                                                                contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().subtract(tContractRentType.getNumericalValue())).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).setScale(2, BigDecimal.ROUND_DOWN);
                                                                contract.setChangeRent(contract.getChangeRent().subtract(tContractRentType.getNumericalValue()));
                                                                break;
                                                        }
                                                        break;
                                                }
                                                // 不需要涨租金的时间段
                                                long originalDays = ChronoUnit.DAYS.between(tBill.getStartTime(), tContractRentType.getChangeTime());
                                                System.err.println("不需要长租金时长"+originalDays);
                                                if (originalDays > 0) {
                                                    originalMoney = originalMoney.add(contract.getMonthRent().divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN))
                                                            .multiply(new BigDecimal(originalDays));
                                                }
                                                tBill.setPayableFeesMoney(contractRentTypeMoney.add(originalMoney));
                                                tBill.setOutstandingMoney(tBill.getPayableFeesMoney());
                                                contractService.updateById(contract);
                                            }else{
                                                tBill.setPayableFeesMoney(contract.getChangeRent().multiply(new BigDecimal(contract.getPayType().equals("1") ? 1 : contract.getPayType().equals("2") ? 3 : 12)));
                                                tBill.setOutstandingMoney(tBill.getPayableFeesMoney());
                                            }
                                        }
                                    } else {
                                        tBill.setPayableFeesMoney(contract.getChangeRent().multiply(new BigDecimal(contract.getPayType().equals("1") ? 1 : contract.getPayType().equals("2") ? 3 : 12)));
                                        tBill.setOutstandingMoney(tBill.getPayableFeesMoney());
                                    }
                                }else {
                                    System.err.println("不执行递增递减");
                                    tBill.setPayableFeesMoney(contract.getChangeRent().multiply(new BigDecimal(contract.getPayType().equals("1") ? 1 : contract.getPayType().equals("2") ? 3 : 12)));
                                    tBill.setOutstandingMoney(tBill.getPayableFeesMoney());
                                }
                                tBill.setContractNumber(contract.getContractNumber());
                                tBill.setPayFeesStatus("1");
                                tBill.setBillType("1");
                                billService.save(tBill);
                                beforeBill.setEndTime(beforeBill.getEndTime().plusMonths(contract.getPayType().equals("1") ? 1 : contract.getPayType().equals("2") ? 3 : 12).with(TemporalAdjusters.lastDayOfMonth()));
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    TBill beforeBill = billService.lambdaQuery().eq(TBill::getContractId, contract.getId()).eq(TBill::getBillType, 1).orderByDesc(TBill::getStartTime)
                            .last("limit 1").one();
                    // 生成最后一笔账单
                    if (!(beforeBill.getEndTime().toLocalDate().equals(contract.getEndTime().toLocalDate()))
                            && beforeBill.getEndTime().isBefore(contract.getEndTime())
                    ) {
                        TBill tBill = new TBill();
                        tBill.setContractId(contract.getId());
                        tBill.setPayFeesStatus("1");
                        tBill.setBillType("1");
                        tBill.setStartTime(beforeBill.getEndTime().plusDays(1));
                        tBill.setEndTime(contract.getEndTime());
                        tBill.setContractNumber(contract.getContractNumber());
                        if (contract.getIsIncreasing()) {
                            if (tContractRentType != null) {
                                // 如果变过 并且时间在递增递减时间段内
                                if (contract.getChangeTime() != null) {
                                    // 下次递增递减时间
                                    LocalDateTime changeTime = contract.getChangeTime().plusYears(tContractRentType.getCycleTime());
                                    // 之前已经涨、跌过租金了 需要判断周期是否还需要再涨、跌
                                    if (changeTime.isBefore(tBill.getEndTime()) && changeTime.isAfter(tBill.getStartTime())) {
                                        contract.setChangeTime(changeTime);
                                        // 租金递增递减的时长 天
                                        long moneyDays = Math.abs(ChronoUnit.DAYS.between(changeTime, tBill.getEndTime()))+1;
                                        // 递增递减的租金
                                        BigDecimal contractRentTypeMoney = new BigDecimal("0");
                                        // 不递增递减的租金
                                        BigDecimal originalMoney = new BigDecimal("0");
                                        // 原租金
                                        switch (tContractRentType.getIncreasingDecreasingType()) {
                                            case 1:
                                                switch (tContractRentType.getIncreasingDecreasing()) {
                                                    case 1:
                                                        contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().multiply(new BigDecimal(100).add(tContractRentType.getNumericalValue())).divide(new BigDecimal(100), 2, BigDecimal.ROUND_DOWN).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).setScale(2, BigDecimal.ROUND_DOWN));
                                                        // 变动后的每月租金
                                                        contract.setChangeRent(contract.getChangeRent().multiply(new BigDecimal(100).add(tContractRentType.getNumericalValue())).divide(new BigDecimal(100), 2, BigDecimal.ROUND_DOWN));
                                                        break;
                                                    case 2:
                                                        contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().multiply((new BigDecimal(100).subtract(tContractRentType.getNumericalValue()))).divide(new BigDecimal(100), 2, BigDecimal.ROUND_DOWN).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).setScale(2, BigDecimal.ROUND_DOWN));
                                                        contract.setChangeRent(contract.getChangeRent().multiply((new BigDecimal(100).subtract(tContractRentType.getNumericalValue()))).divide(new BigDecimal(100), 2, BigDecimal.ROUND_DOWN));
                                                        break;
                                                }
                                                break;
                                            case 2:
                                                switch (tContractRentType.getIncreasingDecreasing()) {
                                                    case 1:
                                                        contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().add(tContractRentType.getNumericalValue())).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).setScale(2, BigDecimal.ROUND_DOWN);
                                                        contract.setChangeRent(contract.getChangeRent().add(tContractRentType.getNumericalValue()));
                                                        break;
                                                    case 2:
                                                        contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().subtract(tContractRentType.getNumericalValue())).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).setScale(2, BigDecimal.ROUND_DOWN);
                                                        contract.setChangeRent(contract.getChangeRent().subtract(tContractRentType.getNumericalValue()));
                                                        break;
                                                }
                                                break;
                                        }
                                        // 不需要涨租金的时间段
                                        long originalDays = ChronoUnit.DAYS.between(tBill.getStartTime(), changeTime);
                                        if (originalDays > 0) {
                                            originalMoney = originalMoney.add(contract.getMonthRent().divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN))
                                                    .multiply(new BigDecimal(originalDays));
                                        }
                                        tBill.setPayableFeesMoney(contractRentTypeMoney.add(originalMoney));
                                        tBill.setOutstandingMoney(tBill.getPayableFeesMoney());
                                        contractService.updateById(contract);
                                    } else {
                                        long finalDays = ChronoUnit.DAYS.between(beforeBill.getEndTime(), contract.getEndTime());
                                        tBill.setPayableFeesMoney(contract.getChangeRent().divide(new BigDecimal("30"),2,BigDecimal.ROUND_DOWN).multiply(new BigDecimal(finalDays)));
                                        tBill.setOutstandingMoney(tBill.getPayableFeesMoney());
                                    }
                                }else{
                                    if (tContractRentType.getChangeTime().isBefore(tBill.getEndTime()) && tContractRentType.getChangeTime().isAfter(tBill.getStartTime())) {
                                        contract.setChangeTime(tContractRentType.getChangeTime());
                                        // 租金递增递减的时长 天
                                        long moneyDays = Math.abs(ChronoUnit.DAYS.between(tContractRentType.getChangeTime(), tBill.getEndTime()))+1;
                                        // 递增递减的租金
                                        BigDecimal contractRentTypeMoney = new BigDecimal("0");
                                        // 不递增递减的租金
                                        BigDecimal originalMoney = new BigDecimal("0");
                                        // 原租金
                                        switch (tContractRentType.getIncreasingDecreasingType()) {
                                            case 1:
                                                switch (tContractRentType.getIncreasingDecreasing()) {
                                                    case 1:
                                                        contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().multiply(new BigDecimal(100).add(tContractRentType.getNumericalValue())).divide(new BigDecimal(100), 2, BigDecimal.ROUND_DOWN).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).setScale(2, BigDecimal.ROUND_DOWN));
                                                        // 变动后的每月租金
                                                        contract.setChangeRent(contract.getChangeRent().multiply(new BigDecimal(100).add(tContractRentType.getNumericalValue())).divide(new BigDecimal(100), 2, BigDecimal.ROUND_DOWN));
                                                        break;
                                                    case 2:
                                                        contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().multiply((new BigDecimal(100).subtract(tContractRentType.getNumericalValue()))).divide(new BigDecimal(100), 2, BigDecimal.ROUND_DOWN).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).setScale(2, BigDecimal.ROUND_DOWN));
                                                        contract.setChangeRent(contract.getChangeRent().multiply((new BigDecimal(100).subtract(tContractRentType.getNumericalValue()))).divide(new BigDecimal(100), 2, BigDecimal.ROUND_DOWN));
                                                        break;
                                                }
                                                break;
                                            case 2:
                                                switch (tContractRentType.getIncreasingDecreasing()) {
                                                    case 1:
                                                        contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().add(tContractRentType.getNumericalValue())).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).setScale(2, BigDecimal.ROUND_DOWN);
                                                        contract.setChangeRent(contract.getChangeRent().add(tContractRentType.getNumericalValue()));
                                                        break;
                                                    case 2:
                                                        contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().subtract(tContractRentType.getNumericalValue())).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).setScale(2, BigDecimal.ROUND_DOWN);
                                                        contract.setChangeRent(contract.getChangeRent().subtract(tContractRentType.getNumericalValue()));
                                                        break;
                                                }
                                                break;
                                        }
                                        // 不需要涨租金的时间段
                                        long originalDays = ChronoUnit.DAYS.between(tBill.getStartTime(), tContractRentType.getChangeTime());
                                        if (originalDays > 0) {
                                            originalMoney = originalMoney.add(contract.getMonthRent().divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN))
                                                    .multiply(new BigDecimal(originalDays)).setScale(2,BigDecimal.ROUND_DOWN);
                                        }
                                        tBill.setPayableFeesMoney(contractRentTypeMoney.add(originalMoney));
                                        tBill.setOutstandingMoney(tBill.getPayableFeesMoney());
                                        contractService.updateById(contract);
                                    }
                                }
                            } else {
                                if (tBill.getStartTime().getDayOfMonth()==1 && tBill.getEndTime().toLocalDate().equals(tBill.getEndTime().with(TemporalAdjusters.lastDayOfMonth()).toLocalDate())){
                                    tBill.setPayableFeesMoney(contract.getChangeRent().multiply(new BigDecimal(contract.getPayType().equals("1") ? 1 : contract.getPayType().equals("2") ? 3 : 12)));
                                    tBill.setOutstandingMoney(tBill.getPayableFeesMoney());
                                }else{
                                    // 最后一笔账单时间
                                    long finalDays = ChronoUnit.DAYS.between(beforeBill.getEndTime(), contract.getEndTime());
                                    tBill.setPayableFeesMoney(contract.getChangeRent().divide(new BigDecimal("30"),2,BigDecimal.ROUND_DOWN).multiply(new BigDecimal(finalDays)));
                                    tBill.setOutstandingMoney(tBill.getPayableFeesMoney());
                                }
                            }
                        }else{
                            long finalDays = ChronoUnit.DAYS.between(beforeBill.getEndTime(), contract.getEndTime());
                            tBill.setPayableFeesMoney(contract.getChangeRent().divide(new BigDecimal("30"),2,BigDecimal.ROUND_DOWN).multiply(new BigDecimal(finalDays)));
                            tBill.setOutstandingMoney(tBill.getPayableFeesMoney());
                        }
                        if (tBill.getEndTime().getDayOfMonth()>=15){
                            tBill.setPayableFeesTime(tBill.getStartTime().withDayOfMonth(15).toLocalDate());
                        }else if (tBill.getStartTime().getYear()==tBill.getEndTime().getYear()&& tBill.getStartTime().getMonthValue()==tBill.getEndTime().getMonthValue()){
                            // 如果同年同月 且日小于15 缴费时间取合同
                            tBill.setPayableFeesTime(tBill.getStartTime().toLocalDate());
                        }else{
                            tBill.setPayableFeesTime(tBill.getStartTime().withDayOfMonth(15).toLocalDate());
                        }
                        billService.save(tBill);
                    }
                }
                break;
            }
            case CATEGORY3: {
                // 合同提前终止审批
                int submitStatus = status==0?4:(status==1?6:5);
                int submitStatus = status == 0 ? 4 : (status == 1 ? 7 : 5);
                contractService.updateContractAuditStatus(processParameter.getString("projectId"), submitStatus);
                // 生成验收记录
                TContract contract = contractService.getById(processParameter.getString("projectId"));
                TCheckAcceptRecord tCheckAcceptRecord = new TCheckAcceptRecord();
                tCheckAcceptRecord.setContractId(contract.getId());
                tCheckAcceptRecord.setHouseId(contract.getHouseId());
                tCheckAcceptRecord.setLeaseReason("后台终止合同");
                tCheckAcceptRecord.setAcceptanceTime(LocalDateTime.now());
                LocalDate now = LocalDate.now();
                String replace = (now + "").replace("-", "");
                int size = checkAcceptRecordMapper.selectList(new LambdaQueryWrapper<TCheckAcceptRecord>()
                        .likeRight(TCheckAcceptRecord::getAcceptanceTime, LocalDate.now())).size();
                tCheckAcceptRecord.setCode(replace.substring(2) + String.format("%03d", size + 1));
                checkAcceptRecordMapper.insert(tCheckAcceptRecord);
                break;
            }
            default:
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/StateProcessTemplateServiceImpl.java
@@ -1281,7 +1281,7 @@
        // 查询历史任务
        List<FlwHisTask> flwHisTasks = flwHisTaskMapper.selectList(Wrappers.<FlwHisTask>lambdaQuery().eq(FlwHisTask::getInstanceId, instanceId));
        processDetailVO.setInstanceId(String.valueOf(instanceId));
        processDetailVO.setFlwHisTasks(flwHisTasks);
        List<StateProcessInstanceAction> list = stateProcessInstanceActionService.list(Wrappers.<StateProcessInstanceAction>lambdaQuery().eq(StateProcessInstanceAction::getInstanceId, instanceId)
@@ -1365,6 +1365,7 @@
            sysUserMap = new HashMap<>();
        }
        for (ProcessTaskListVO processTaskListVO : processTaskListVOS) {
            processTaskListVO.setStatus(processTaskListVO.getTaskState() == 10 ? "2" : processTaskListVO.getTaskState().toString());
            processTaskListVO.setTaskId(processTaskListVO.getId());
            SysUser sysUser = sysUserMap.get(processTaskListVO.getCreateBy());
            if (Objects.nonNull(sysUser)) {
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TBillServiceImpl.java
@@ -547,5 +547,10 @@
        return dto;
    }
    @Override
    public Integer batchBillCount(String userId, List<String> billIds) {
        return this.baseMapper.batchBillCount(userId,billIds);
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TCheckAcceptRecordServiceImpl.java
@@ -17,6 +17,7 @@
import com.ruoyi.system.vo.TCheckAcceptRecordVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.List;
@@ -39,12 +40,11 @@
    public PageInfo<TCheckAcceptRecordVO> pageList(TCheckAcceptRecordQuery query) {
        PageInfo<TCheckAcceptRecordVO> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize());
        List<TCheckAcceptRecordVO> list = this.baseMapper.pageList(query,pageInfo);
        list.forEach(item -> {
            item.setCleanSituation(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,item.getCleanSituation()));
            item.setOverallSituation(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,item.getOverallSituation()));
            item.setDeviceSituation(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,item.getDeviceSituation()));
            item.setFurnitureSituation(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,item.getFurnitureSituation()));
            item.setCleanSituation(StringUtils.hasLength(item.getCleanSituation()) ?(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,item.getCleanSituation())):"");
            item.setOverallSituation(StringUtils.hasLength(item.getOverallSituation())?(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,item.getOverallSituation())):"");
            item.setDeviceSituation(StringUtils.hasLength(item.getDeviceSituation())?(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,item.getDeviceSituation())):"");
            item.setFurnitureSituation(StringUtils.hasLength(item.getDeviceSituation())?(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,item.getFurnitureSituation())):"");
        });
        pageInfo.setRecords(list);
@@ -58,10 +58,10 @@
        PageInfo<TCheckAcceptRecordVO> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize());
        List<TCheckAcceptRecordVO> list = this.baseMapper.pageListApplet(query,pageInfo);
        list.forEach(item -> {
            item.setCleanSituation(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,item.getCleanSituation()));
            item.setOverallSituation(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,item.getOverallSituation()));
            item.setDeviceSituation(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,item.getDeviceSituation()));
            item.setFurnitureSituation(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,item.getFurnitureSituation()));
            item.setCleanSituation(StringUtils.hasLength(item.getCleanSituation()) ?(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,item.getCleanSituation())):"");
            item.setOverallSituation(StringUtils.hasLength(item.getOverallSituation())?(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,item.getOverallSituation())):"");
            item.setDeviceSituation(StringUtils.hasLength(item.getDeviceSituation())?(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,item.getDeviceSituation())):"");
            item.setFurnitureSituation(StringUtils.hasLength(item.getDeviceSituation())?(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CHECK_SITUATION,item.getFurnitureSituation())):"");
            item.setContract(tContracts.stream().filter(contract -> contract.getId().equals(item.getContractId())).findFirst().orElse(null));
            item.setHouse(tHouses.stream().filter(house -> house.getId().equals(item.getHouseId())).findFirst().orElse(null));
        });
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TContractServiceImpl.java
@@ -73,6 +73,12 @@
        for (TContract tContract : list) {
            tContract.setPayType(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CONTRACT_PAY_TYPE,tContract.getPayType()));
            tContract.setStatus(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CONTRACT_STATUS,tContract.getStatus()));
            FlwHisTask flwHisTask = flwHisTaskMapper.selectOne(new LambdaQueryWrapper<FlwHisTask>()
                    .like(FlwHisTask::getVariable, tContract.getId())
                    .last("LIMIT 1"));
            if (Objects.nonNull(flwHisTask)){
                tContract.setInstanceId(Objects.nonNull(flwHisTask.getInstanceId())?String.valueOf(flwHisTask.getInstanceId()):"");
            }
        }
        pageInfo.setRecords(list);
        return pageInfo;
@@ -90,7 +96,7 @@
                    .like(FlwHisTask::getVariable, tContract.getId())
                    .last("LIMIT 1"));
            if (Objects.nonNull(flwHisTask)){
                tContract.setInstanceId(flwHisTask.getInstanceId());
                tContract.setInstanceId(Objects.nonNull(flwHisTask.getInstanceId())?String.valueOf(flwHisTask.getInstanceId()):"");
            }
        }
        pageInfo.setRecords(list);
@@ -114,9 +120,7 @@
    public void terminateContract(TerminateContractDTO dto) {
        TContract contract = this.baseMapper.selectById(dto.getId());
        contract.setTerminateRemark(dto.getTerminateRemark());
        contract.setStatus("4");
        this.baseMapper.updateById(contract);
        // 进入合同提前终止审批流程
        ProcessStartBO processStartBO = new ProcessStartBO();
        processStartBO.setCategory(ProcessCategoryEnum.CATEGORY3.getValue().toString());
@@ -128,38 +132,11 @@
        processStartBO.setVariable(variable);
        //开启工作流程
        stateProcessTemplateService.start(processStartBO);
        // 生成验收记录
        TCheckAcceptRecord tCheckAcceptRecord = new TCheckAcceptRecord();
        tCheckAcceptRecord.setContractId(dto.getId());
        tCheckAcceptRecord.setHouseId(contract.getHouseId());
        tCheckAcceptRecord.setLeaseReason("后台终止合同");
        tCheckAcceptRecord.setStatus(false);
        tCheckAcceptRecord.setAcceptanceTime(LocalDateTime.now());
        LocalDate now = LocalDate.now();
        String replace = (now + "").replace("-", "");
        int size = checkAcceptRecordMapper.selectList(new LambdaQueryWrapper<TCheckAcceptRecord>()
                .likeRight(TCheckAcceptRecord::getAcceptanceTime, LocalDate.now())).size();
        tCheckAcceptRecord.setCode(replace.substring(2)+String.format("%03d", size+1));
        checkAcceptRecordMapper.insert(tCheckAcceptRecord);
        // 将所有未缴费账单设置未已失效
        List<TBill> tBills = billMapper.selectList(new LambdaQueryWrapper<TBill>()
                .in(TBill::getPayFeesStatus, Arrays.asList("1,4"))
                .eq(TBill::getContractId, dto.getId()));
        for (TBill tBill : tBills) {
            tBill.setPayFeesStatus("5");
        }
        billService.updateBatchById(tBills);
    }
    public static void main(String[] args) {
//        LocalDate now = LocalDate.now();
//        String replace = (now + "").replace("-", "");
//        System.err.println(replace.substring(2));
//
//        System.err.println(String.format("%03d",1));
        String t = "1000438";
        System.err.println("XN" + String.valueOf(t).substring(1));
    }
    @Override
    public CheckAcceptRecordVO getCheckByContractId(String id) {
@@ -182,8 +159,8 @@
        if (contract==null)return R.fail("合同不存在");
        if (contract.getStatus().equals("4"))return R.fail("该合同已签订");
        contract.setSignature(dto.getSignature());
        contract.setStatus("4");
        contract.setFirstPayTime(contract.getStartTime().plusDays(10));
        contract.setStatus("9");
        contractMapper.updateById(contract);
        // 进入签订审批流程
@@ -198,462 +175,7 @@
        //开启工作流程
        stateProcessTemplateService.startApplet(processStartBO);
        List<TContractRentType> contractRentTypes = contractRentTypeService.list();
        // 生成第一笔账单
        // 第一次应缴费日期
        LocalDateTime firstPayTime = contract.getStartTime().plusDays(10).withHour(0).withMinute(0).withSecond(0);
        TBill rentBill = new TBill();
        rentBill.setContractId(contract.getId());
        rentBill.setContractNumber(contract.getContractNumber());
        rentBill.setPayableFeesTime(firstPayTime.toLocalDate());
        rentBill.setPayFeesStatus("1");
        rentBill.setBillType("1");
        rentBill.setStartTime(contract.getStartPayTime());
        TContractRentType tContractRentType = contractRentTypes.stream().filter(e -> e.getContractId().equals(contract.getId())).findFirst().orElse(null);
        if (tContractRentType!=null && contract.getStartPayTime().plusMonths(contract.getPayType().equals("1")? 1:contract.getPayType().equals("2")? 3:12).isAfter(tContractRentType.getChangeTime())){
            // 计算租金变动的天数
            long moneyDays = ChronoUnit.DAYS.between(tContractRentType.getChangeTime(), contract.getStartPayTime().plusMonths(contract.getPayType().equals("1")? 1:contract.getPayType().equals("2")? 3:12))+1L;
            contract.setChangeTime(LocalDateTime.now());
            // 递增递减的租金
            BigDecimal contractRentTypeMoney = new BigDecimal("0");
            // 不递增递减的租金
            BigDecimal originalMoney = new BigDecimal("0");
            // 原租金
            switch (tContractRentType.getIncreasingDecreasingType()){
                case 1:
                    switch (tContractRentType.getIncreasingDecreasing()){
                        case 1:
                            contractRentTypeMoney =contractRentTypeMoney.add(contract.getChangeRent().multiply(new BigDecimal(100).add(tContractRentType.getNumericalValue())).divide(new BigDecimal(100),2,BigDecimal.ROUND_DOWN).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)));
                            contract.setChangeRent(contractRentTypeMoney);
                            break;
                        case 2:
                            contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().multiply((new BigDecimal(100).subtract(tContractRentType.getNumericalValue()))).divide(new BigDecimal(100),2,BigDecimal.ROUND_DOWN).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).divide(new BigDecimal(contract.getPayType().equals("1")? 1:contract.getPayType().equals("2")? 3:12),2,BigDecimal.ROUND_DOWN));
                            contract.setChangeRent(contractRentTypeMoney);
                            break;
                    }
                    break;
                case 2:
                    switch (tContractRentType.getIncreasingDecreasing()){
                        case 1:
                            contractRentTypeMoney =contractRentTypeMoney.add(contract.getChangeRent().add(tContractRentType.getNumericalValue())).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays));
                            contract.setChangeRent(contractRentTypeMoney);
                            break;
                        case 2:
                            contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().subtract(tContractRentType.getNumericalValue())).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays));
                            contract.setChangeRent(contractRentTypeMoney);
                            break;
                    }
                    break;
            }
            // 不需要涨租金的时间段
            long originalDays = ChronoUnit.DAYS.between(contract.getFirstPayTime(), tContractRentType.getChangeTime());
            originalMoney=originalMoney.add(contract.getMonthRent().divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN))
                    .multiply(new BigDecimal(originalDays));
            rentBill.setPayableFeesMoney(contractRentTypeMoney.add(originalMoney));
            rentBill.setOutstandingMoney(rentBill.getPayableFeesMoney());
            if (contract.getFirstPayTime().plusMonths(contract.getPayType().equals("1")? 1:contract.getPayType().equals("2")? 3:12).isAfter(contract.getEndTime())){
                rentBill.setEndTime(contract.getFirstPayTime().plusMonths(contract.getPayType().equals("1")? 1:contract.getPayType().equals("2")? 3:12));
            }else{
                rentBill.setEndTime(contract.getEndTime());
            }
        }else{
            if (contract.getFirstPayTime().plusMonths(contract.getPayType().equals("1")? 1:contract.getPayType().equals("2")? 3:12).isAfter(contract.getEndTime())){
                rentBill.setEndTime(contract.getEndTime());
            }else{
                LocalDateTime firstPayTime1 = contract.getFirstPayTime();
                // 将firstPayTime1的日设置为当月最后一天
                rentBill.setEndTime(firstPayTime1.with(TemporalAdjusters.lastDayOfMonth()));
            }
            // 不走递增递减
            long allDays = ChronoUnit.DAYS.between(contract.getStartPayTime().minusDays(1), rentBill.getEndTime());
            rentBill.setPayableFeesMoney(contract.getMonthRent().divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(allDays)));
            rentBill.setOutstandingMoney(rentBill.getPayableFeesMoney());
        }
        // 押金账单
        TBill depositBill = new TBill();
        depositBill.setContractId(contract.getId());
        depositBill.setContractNumber(contract.getContractNumber());
        depositBill.setPayableFeesMoney(contract.getDeposit());
        depositBill.setOutstandingMoney(depositBill.getPayableFeesMoney());
        depositBill.setStartTime(contract.getStartPayTime());
        depositBill.setEndTime(contract.getEndTime());
        depositBill.setPayableFeesTime(firstPayTime.toLocalDate());
        depositBill.setPayFeesStatus("1");
        depositBill.setBillType("2");
        this.updateById(contract);
        billService.save(rentBill);
        billService.save(depositBill);
        // 生成后续账单
        try {
            TBill beforeBill = billService.lambdaQuery().eq(TBill::getContractId, contract.getId()).eq(TBill::getBillType, 1).orderByDesc(TBill::getCreateTime)
                    .last("limit 1").one();
            if (!beforeBill.getEndTime().toLocalDate().equals(contract.getEndTime().toLocalDate())){
            while(beforeBill.getEndTime().plusMonths(contract.getPayType().equals("1")? 1:contract.getPayType().equals("2")? 3:12).isBefore(contract.getEndTime())){
                    TBill tBill = new TBill();
                    tBill.setContractId(contract.getId());
                    tBill.setContractNumber(contract.getContractNumber());
                    // 根据支付方式判断需不需要生成订单
                    if (!(beforeBill.getEndTime().toLocalDate().equals(contract.getEndTime().toLocalDate()))
                            &&
                            (contract.getPayType().equals("1")?
                                    beforeBill.getEndTime().plusMonths(1):contract.getPayType().equals("2")?
                                    beforeBill.getEndTime().plusMonths(3):beforeBill.getEndTime().plusMonths(12))
                                    .with(TemporalAdjusters.lastDayOfMonth()).isBefore(contract.getEndTime())
                            && beforeBill.getEndTime().isBefore(contract.getEndTime())
                    ){
                        tBill.setContractId(contract.getId());
                        if (contract.getIsIncreasing()){
                            if (tContractRentType!=null
                                    && beforeBill.getEndTime().isBefore(tContractRentType.getChangeTime())
                                    && beforeBill.getEndTime().plusMonths(contract.getPayType().equals("1")? 1:contract.getPayType().equals("2")? 3:12).with(TemporalAdjusters.lastDayOfMonth()).isAfter(tContractRentType.getChangeTime())){
                                // 如果没变过
                                if (contract.getChangeTime()==null){
                                    contract.setChangeTime(LocalDateTime.now());
                                    // 租金递增递减的时长 天
                                    long moneyDays = ChronoUnit.DAYS.between(tContractRentType.getChangeTime(), contract.getEndTime());
                                    // 递增递减的租金
                                    BigDecimal contractRentTypeMoney = new BigDecimal("0");
                                    // 不递增递减的租金
                                    BigDecimal originalMoney = new BigDecimal("0");
                                    // 原租金
                                    switch (tContractRentType.getIncreasingDecreasingType()){
                                        case 1:
                                            switch (tContractRentType.getIncreasingDecreasing()){
                                                case 1:
                                                    contractRentTypeMoney =contractRentTypeMoney.add(contract.getChangeRent().multiply(new BigDecimal(100).add(tContractRentType.getNumericalValue())).divide(new BigDecimal(100),2,BigDecimal.ROUND_DOWN).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)));
                                                    contract.setChangeRent(contractRentTypeMoney);
                                                    break;
                                                case 2:
                                                    contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().multiply((new BigDecimal(100).subtract(tContractRentType.getNumericalValue()))).divide(new BigDecimal(100),2,BigDecimal.ROUND_DOWN).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).divide(new BigDecimal(contract.getPayType().equals("1")? 1:contract.getPayType().equals("2")? 3:12),2,BigDecimal.ROUND_DOWN));
                                                    contract.setChangeRent(contractRentTypeMoney);
                                                    break;
                                            }
                                            break;
                                        case 2:
                                            switch (tContractRentType.getIncreasingDecreasing()){
                                                case 1:
                                                    contractRentTypeMoney =contractRentTypeMoney.add(contract.getChangeRent().add(tContractRentType.getNumericalValue())).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays));
                                                    contract.setChangeRent(contractRentTypeMoney);
                                                    break;
                                                case 2:
                                                    contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().subtract(tContractRentType.getNumericalValue())).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays));
                                                    contract.setChangeRent(contractRentTypeMoney);
                                                    break;
                                            }
                                            break;
                                    }
                                    // 不需要涨租金的时间段
                                    long originalDays = ChronoUnit.DAYS.between(beforeBill.getEndTime(), tContractRentType.getChangeTime());
                                    originalMoney=originalMoney.add(contract.getMonthRent().divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN))
                                            .multiply(new BigDecimal(originalDays));
                                    tBill.setPayableFeesMoney(contractRentTypeMoney.add(originalMoney));
                                    tBill.setOutstandingMoney(tBill.getPayableFeesMoney());
                                }else{
                                    // 之前已经涨、跌过租金了 需要判断周期是否还需要再涨、跌
                                    if ((LocalDateTime.now().getYear() - contract.getChangeTime().getYear())%tContractRentType.getCycleTime()==0){
                                        contract.setChangeTime(LocalDateTime.now());
                                        // 租金递增递减的时长 天
                                        long moneyDays = ChronoUnit.DAYS.between(tContractRentType.getChangeTime(), contract.getEndTime());
                                        // 递增递减的租金
                                        BigDecimal contractRentTypeMoney = new BigDecimal("0");
                                        // 不递增递减的租金
                                        BigDecimal originalMoney = new BigDecimal("0");
                                        // 原租金
                                        switch (tContractRentType.getIncreasingDecreasingType()){
                                            case 1:
                                                switch (tContractRentType.getIncreasingDecreasing()){
                                                    case 1:
                                                        contractRentTypeMoney =contractRentTypeMoney.add(contract.getChangeRent().multiply(new BigDecimal(100).add(tContractRentType.getNumericalValue())).divide(new BigDecimal(100),2,BigDecimal.ROUND_DOWN).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)));
                                                        contract.setChangeRent(contractRentTypeMoney);
                                                        break;
                                                    case 2:
                                                        contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().multiply((new BigDecimal(100).subtract(tContractRentType.getNumericalValue()))).divide(new BigDecimal(100),2,BigDecimal.ROUND_DOWN).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).divide(new BigDecimal(contract.getPayType().equals("1")? 1:contract.getPayType().equals("2")? 3:12),2,BigDecimal.ROUND_DOWN));
                                                        contract.setChangeRent(contractRentTypeMoney);
                                                        break;
                                                }
                                                break;
                                            case 2:
                                                switch (tContractRentType.getIncreasingDecreasing()){
                                                    case 1:
                                                        contractRentTypeMoney =contractRentTypeMoney.add(contract.getChangeRent().add(tContractRentType.getNumericalValue())).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays));
                                                        contract.setChangeRent(contractRentTypeMoney);
                                                        break;
                                                    case 2:
                                                        contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().subtract(tContractRentType.getNumericalValue())).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays));
                                                        contract.setChangeRent(contractRentTypeMoney);
                                                        break;
                                                }
                                                break;
                                        }
                                        // 不需要涨租金的时间段
                                        long originalDays = ChronoUnit.DAYS.between(beforeBill.getEndTime(), tContractRentType.getChangeTime());
                                        originalMoney=originalMoney.add(contract.getMonthRent().divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN))
                                                .multiply(new BigDecimal(originalDays));
                                        tBill.setPayableFeesMoney(contractRentTypeMoney.add(originalMoney));
                                        tBill.setOutstandingMoney(tBill.getPayableFeesMoney());
                                    }else{
                                        // 不涨租金 用上次的
                                        // 租金递增递减的时长 天
                                        long moneyDays = ChronoUnit.DAYS.between(tContractRentType.getChangeTime(), contract.getEndTime());
                                        // 递增递减的租金
                                        BigDecimal contractRentTypeMoney = new BigDecimal("0");
                                        // 不递增递减的租金
                                        BigDecimal originalMoney = new BigDecimal("0");
                                        // 原租金
                                        switch (tContractRentType.getIncreasingDecreasingType()){
                                            case 1:
                                                switch (tContractRentType.getIncreasingDecreasing()){
                                                    case 1:
                                                        contractRentTypeMoney =contractRentTypeMoney.add(contract.getChangeRent().multiply(new BigDecimal(100).add(tContractRentType.getNumericalValue())).divide(new BigDecimal(100),2,BigDecimal.ROUND_DOWN).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)));
                                                        contract.setChangeRent(contractRentTypeMoney);
                                                        break;
                                                    case 2:
                                                        contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().multiply((new BigDecimal(100).subtract(tContractRentType.getNumericalValue()))).divide(new BigDecimal(100),2,BigDecimal.ROUND_DOWN).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).divide(new BigDecimal(contract.getPayType().equals("1")? 1:contract.getPayType().equals("2")? 3:12),2,BigDecimal.ROUND_DOWN));
                                                        contract.setChangeRent(contractRentTypeMoney);
                                                        break;
                                                }
                                                break;
                                            case 2:
                                                switch (tContractRentType.getIncreasingDecreasing()){
                                                    case 1:
                                                        contractRentTypeMoney =contractRentTypeMoney.add(contract.getChangeRent().add(tContractRentType.getNumericalValue())).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays));
                                                        contract.setChangeRent(contractRentTypeMoney);
                                                        break;
                                                    case 2:
                                                        contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().subtract(tContractRentType.getNumericalValue())).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays));
                                                        contract.setChangeRent(contractRentTypeMoney);
                                                        break;
                                                }
                                                break;
                                        }
                                        // 不需要涨租金的时间段
                                        long originalDays = ChronoUnit.DAYS.between(beforeBill.getEndTime(), tContractRentType.getChangeTime());
                                        originalMoney=originalMoney.add(contract.getMonthRent().divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN))
                                                .multiply(new BigDecimal(originalDays));
                                        tBill.setPayableFeesMoney(contractRentTypeMoney.add(originalMoney));
                                        tBill.setOutstandingMoney(tBill.getPayableFeesMoney());
                                    }
                                }
                            }
                        }else{
                            tBill.setPayableFeesMoney(contract.getMonthRent());
                            tBill.setOutstandingMoney(tBill.getPayableFeesMoney());
                        }
                        tBill.setContractNumber(contract.getContractNumber());
                        if (beforeBill.getEndTime().plusMonths(contract.getPayType().equals("1")? 1:contract.getPayType().equals("2")? 3:12).getDayOfMonth()<=15){
                            tBill.setPayableFeesTime(contract.getEndTime().toLocalDate());
                        }else{
                            tBill.setPayableFeesTime((contract.getPayType().equals("1")?
                                    beforeBill.getEndTime().plusMonths(1).withDayOfMonth(15).toLocalDate():contract.getPayType().equals("2")?
                                    beforeBill.getEndTime().plusMonths(3).withDayOfMonth(15).toLocalDate():beforeBill.getEndTime().withDayOfMonth(15).plusMonths(12).toLocalDate()));
                        }
                        tBill.setPayFeesStatus("1");
                        tBill.setBillType("1");
                        tBill.setStartTime(beforeBill.getEndTime().plusDays(1));
                        tBill.setEndTime(beforeBill.getEndTime().plusMonths(contract.getPayType().equals("1")? 1:contract.getPayType().equals("2")? 3:12).with(TemporalAdjusters.lastDayOfMonth()));
                        billMapper.insert(tBill);
                    }
                beforeBill.setEndTime(beforeBill.getEndTime().plusMonths(contract.getPayType().equals("1")? 1:contract.getPayType().equals("2")? 3:12).with(TemporalAdjusters.lastDayOfMonth()));
            }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        TBill beforeBill = billService.lambdaQuery().eq(TBill::getContractId, contract.getId()).eq(TBill::getBillType, 1).orderByDesc(TBill::getStartTime)
                .last("limit 1").one();
        // 生成最后一笔账单
        if (!(beforeBill.getEndTime().toLocalDate().equals(contract.getEndTime().toLocalDate()))
                && beforeBill.getEndTime().isBefore(contract.getEndTime())
        ){
            TBill tBill = new TBill();
            tBill.setContractId(contract.getId());
            if (contract.getIsIncreasing()){
                if (tContractRentType!=null
                        && beforeBill.getEndTime().isBefore(tContractRentType.getChangeTime())
                        && beforeBill.getEndTime().plusMonths(contract.getPayType().equals("1")? 1:contract.getPayType().equals("2")? 3:12).with(TemporalAdjusters.lastDayOfMonth()).isAfter(tContractRentType.getChangeTime())){
                    // 如果没变过
                    if (contract.getChangeTime()==null){
                        contract.setChangeTime(LocalDateTime.now());
                        // 租金递增递减的时长 天
                        long moneyDays = ChronoUnit.DAYS.between(tContractRentType.getChangeTime(), contract.getEndTime());
                        // 递增递减的租金
                        BigDecimal contractRentTypeMoney = new BigDecimal("0");
                        // 不递增递减的租金
                        BigDecimal originalMoney = new BigDecimal("0");
                        // 原租金
                        switch (tContractRentType.getIncreasingDecreasingType()){
                            case 1:
                                switch (tContractRentType.getIncreasingDecreasing()){
                                    case 1:
                                        contractRentTypeMoney =contractRentTypeMoney.add(contract.getChangeRent().multiply(new BigDecimal(100).add(tContractRentType.getNumericalValue())).divide(new BigDecimal(100),2,BigDecimal.ROUND_DOWN).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)));
                                        contract.setChangeRent(contractRentTypeMoney);
                                        break;
                                    case 2:
                                        contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().multiply((new BigDecimal(100).subtract(tContractRentType.getNumericalValue()))).divide(new BigDecimal(100),2,BigDecimal.ROUND_DOWN).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).divide(new BigDecimal(contract.getPayType().equals("1")? 1:contract.getPayType().equals("2")? 3:12),2,BigDecimal.ROUND_DOWN));
                                        contract.setChangeRent(contractRentTypeMoney);
                                        break;
                                }
                                break;
                            case 2:
                                switch (tContractRentType.getIncreasingDecreasing()){
                                    case 1:
                                        contractRentTypeMoney =contractRentTypeMoney.add(contract.getChangeRent().add(tContractRentType.getNumericalValue())).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays));
                                        contract.setChangeRent(contractRentTypeMoney);
                                        break;
                                    case 2:
                                        contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().subtract(tContractRentType.getNumericalValue())).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays));
                                        contract.setChangeRent(contractRentTypeMoney);
                                        break;
                                }
                                break;
                        }
                        // 不需要涨租金的时间段
                        long originalDays = ChronoUnit.DAYS.between(beforeBill.getEndTime(), tContractRentType.getChangeTime());
                        // 不需要涨租金的时间段
                        if (contract.getFirstPayTime().isBefore(tContractRentType.getChangeTime())){
                            originalMoney=originalMoney.add(contract.getMonthRent().divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN))
                                    .multiply(new BigDecimal(originalDays));
                            tBill.setPayableFeesMoney(contractRentTypeMoney.add(originalMoney));
                            tBill.setOutstandingMoney(tBill.getPayableFeesMoney());
                        }else{
                            tBill.setPayableFeesMoney(contractRentTypeMoney);
                            tBill.setOutstandingMoney(tBill.getPayableFeesMoney());
                        }
                    }else{
                        // 之前已经涨、跌过租金了 需要判断周期是否还需要再涨、跌
                        if ((LocalDateTime.now().getYear() - contract.getChangeTime().getYear())%tContractRentType.getCycleTime()==0){
                            contract.setChangeTime(LocalDateTime.now());
                            // 租金递增递减的时长 天
                            long moneyDays = ChronoUnit.DAYS.between(tContractRentType.getChangeTime(), contract.getEndTime());
                            // 递增递减的租金
                            BigDecimal contractRentTypeMoney = new BigDecimal("0");
                            // 不递增递减的租金
                            BigDecimal originalMoney = new BigDecimal("0");
                            // 原租金
                            switch (tContractRentType.getIncreasingDecreasingType()){
                                case 1:
                                    switch (tContractRentType.getIncreasingDecreasing()){
                                        case 1:
                                            contractRentTypeMoney =contractRentTypeMoney.add(contract.getChangeRent().multiply(new BigDecimal(100).add(tContractRentType.getNumericalValue())).divide(new BigDecimal(100),2,BigDecimal.ROUND_DOWN).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)));
                                            contract.setChangeRent(contractRentTypeMoney);
                                            break;
                                        case 2:
                                            contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().multiply((new BigDecimal(100).subtract(tContractRentType.getNumericalValue()))).divide(new BigDecimal(100),2,BigDecimal.ROUND_DOWN).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).divide(new BigDecimal(contract.getPayType().equals("1")? 1:contract.getPayType().equals("2")? 3:12),2,BigDecimal.ROUND_DOWN));
                                            contract.setChangeRent(contractRentTypeMoney);
                                            break;
                                    }
                                    break;
                                case 2:
                                    switch (tContractRentType.getIncreasingDecreasing()){
                                        case 1:
                                            contractRentTypeMoney =contractRentTypeMoney.add(contract.getChangeRent().add(tContractRentType.getNumericalValue())).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays));
                                            contract.setChangeRent(contractRentTypeMoney);
                                            break;
                                        case 2:
                                            contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().subtract(tContractRentType.getNumericalValue())).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays));
                                            contract.setChangeRent(contractRentTypeMoney);
                                            break;
                                    }
                                    break;
                            }
                            // 不需要涨租金的时间段
                            long originalDays = ChronoUnit.DAYS.between(beforeBill.getEndTime(), tContractRentType.getChangeTime());
                            originalMoney=originalMoney.add(contract.getMonthRent().divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN))
                                    .multiply(new BigDecimal(originalDays));
                            tBill.setPayableFeesMoney(contractRentTypeMoney.add(originalMoney));
                            tBill.setOutstandingMoney(tBill.getPayableFeesMoney());
                        }else{
                            // 不涨租金 用上次的
                            // 租金递增递减的时长 天
                            long moneyDays = ChronoUnit.DAYS.between(tContractRentType.getChangeTime(), contract.getEndTime());
                            // 递增递减的租金
                            BigDecimal contractRentTypeMoney = new BigDecimal("0");
                            // 不递增递减的租金
                            BigDecimal originalMoney = new BigDecimal("0");
                            // 原租金
                            switch (tContractRentType.getIncreasingDecreasingType()){
                                case 1:
                                    switch (tContractRentType.getIncreasingDecreasing()){
                                        case 1:
                                            contractRentTypeMoney =contractRentTypeMoney.add(contract.getChangeRent().multiply(new BigDecimal(100).add(tContractRentType.getNumericalValue())).divide(new BigDecimal(100),2,BigDecimal.ROUND_DOWN).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)));
                                            contract.setChangeRent(contractRentTypeMoney);
                                            break;
                                        case 2:
                                            contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().multiply((new BigDecimal(100).subtract(tContractRentType.getNumericalValue()))).divide(new BigDecimal(100),2,BigDecimal.ROUND_DOWN).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays)).divide(new BigDecimal(contract.getPayType().equals("1")? 1:contract.getPayType().equals("2")? 3:12),2,BigDecimal.ROUND_DOWN));
                                            contract.setChangeRent(contractRentTypeMoney);
                                            break;
                                    }
                                    break;
                                case 2:
                                    switch (tContractRentType.getIncreasingDecreasing()){
                                        case 1:
                                            contractRentTypeMoney =contractRentTypeMoney.add(contract.getChangeRent().add(tContractRentType.getNumericalValue())).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays));
                                            contract.setChangeRent(contractRentTypeMoney);
                                            break;
                                        case 2:
                                            contractRentTypeMoney = contractRentTypeMoney.add(contract.getChangeRent().subtract(tContractRentType.getNumericalValue())).divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(moneyDays));
                                            contract.setChangeRent(contractRentTypeMoney);
                                            break;
                                    }
                                    break;
                            }
                            // 不需要涨租金的时间段
                            long originalDays = ChronoUnit.DAYS.between(beforeBill.getEndTime(), tContractRentType.getChangeTime());
                            originalMoney=originalMoney.add(contract.getMonthRent().divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN))
                                    .multiply(new BigDecimal(originalDays));
                            tBill.setPayableFeesMoney(contractRentTypeMoney.add(originalMoney));
                            tBill.setOutstandingMoney(tBill.getPayableFeesMoney());
                        }
                    }
                }
            }else{
                long allDays = ChronoUnit.DAYS.between(beforeBill.getEndTime(), contract.getEndTime());
                tBill.setPayableFeesMoney(contract.getMonthRent().divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(allDays)));
                tBill.setOutstandingMoney(tBill.getPayableFeesMoney());
            }
            tBill.setContractNumber(contract.getContractNumber());
            if (contract.getEndTime().getDayOfMonth()<=15){
                tBill.setPayableFeesTime(contract.getEndTime().withHour(0).withMinute(0).withSecond(0).toLocalDate());
            }else{
                tBill.setPayableFeesTime((contract.getPayType().equals("1")?
                        beforeBill.getEndTime().plusMonths(1).withDayOfMonth(15).toLocalDate():contract.getPayType().equals("2")?
                        beforeBill.getEndTime().plusMonths(3).withDayOfMonth(15).toLocalDate():beforeBill.getEndTime().withDayOfMonth(15).plusMonths(12).withHour(0).withMinute(0).withSecond(0).toLocalDate()));
            }
            tBill.setPayFeesStatus("1");
            tBill.setBillType("1");
            tBill.setStartTime(beforeBill.getEndTime().plusDays(1));
            tBill.setEndTime(contract.getEndTime());
            billService.save(tBill);
        }
        return R.ok();
    }
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/THouseServiceImpl.java
@@ -41,12 +41,16 @@
                .le(TContract::getStartTime, LocalDateTime.now())
                .ge(TContract::getEndTime, LocalDateTime.now()));
        for (THouse tHouse : list) {
            tHouse.setLeaseStatus(DictUtils.getDictLabel(DictConstants.DICT_TYPE_LEASE_STATUS,tHouse.getLeaseStatus()));
            tHouse.setBusinessAttributes(DictUtils.getDictLabel(DictConstants.DICT_TYPE_BUSINESS_ATTRIBUTES,tHouse.getBusinessAttributes()));
            TContract tContract = tContracts.stream().filter(e -> e.getHouseId().equals(tHouse.getId())).findFirst().orElse(null);
            if (tContract!=null){
                tHouse.setTenantType(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CONTRACT_PAY_TYPE,tContract.getPayType()));
//                tHouse.setLeaseStatus(DictUtils.getDictLabel(DictConstants.DICT_TYPE_LEASE_STATUS,"2"));
            }else{
//                tHouse.setLeaseStatus(DictUtils.getDictLabel(DictConstants.DICT_TYPE_LEASE_STATUS,tHouse.getLeaseStatus()));
            }
                            tHouse.setLeaseStatus(DictUtils.getDictLabel(DictConstants.DICT_TYPE_LEASE_STATUS,tHouse.getLeaseStatus()));
        }
        pageInfo.setRecords(list);
        return pageInfo;
@@ -58,6 +62,7 @@
        List<HouseVO> list = this.baseMapper.userHistoryList(query,pageInfo);
        for (HouseVO houseVO : list) {
            houseVO.setTenantAttributes(StringUtils.isNotEmpty(houseVO.getTenantAttributes())?DictUtils.getDictLabel(DictConstants.DICT_TYPE_TENANT_ATTRIBUTE,houseVO.getTenantAttributes()):"");
            houseVO.setProductType(StringUtils.isNotEmpty(houseVO.getTenantType())?DictUtils.getDictLabel(DictConstants.DICT_TYPE_TENANT_TYPE,houseVO.getTenantType()):"");
        }
        pageInfo.setRecords(list);
        return pageInfo;
ruoyi-system/src/main/java/com/ruoyi/system/task/jobs/StateProcessJob.java
@@ -40,7 +40,7 @@
            stateProcessTemplateService.agree(processAgreeBO);
            // 短信发送
            SysUser sysUser = sysUserService.selectUserById(Long.valueOf(flwTaskActor.getActorId()));
            smsUtil.sendSms(sysUser.getPhonenumber(), "2369951", new String[]{""});
            smsUtil.sendSms(sysUser.getPhonenumber(), "2369951", new String[]{});
        }catch(Exception e){
            e.printStackTrace();
        }
ruoyi-system/src/main/java/com/ruoyi/system/task/utils/TaskUtil.java
New file
@@ -0,0 +1,92 @@
package com.ruoyi.system.task.utils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.system.mapper.TBillMapper;
import com.ruoyi.system.model.TBill;
import com.ruoyi.system.model.TContract;
import com.ruoyi.system.service.TContractService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
/**
 * @author zhibing.pu
 * @date 2023/7/11 8:39
 */
@Component
public class TaskUtil {
    @Autowired
    private TContractService contractService;
    @Autowired
    private TBillMapper billMapper;
    // 用于更新违约金账单
    // 每分钟执行一次的定时任务
    @Scheduled(cron = "0 0 0 * * ?")
//    @Scheduled(cron = "0 * * * * ?")
    public void dayOfProportionBill() {
        try {
            // 查询所有未缴费账单
            List<TBill> list = billMapper.selectList(new LambdaQueryWrapper<TBill>().eq(TBill::getPayFeesStatus, 1)
                    .le(TBill::getPayableFeesTime,LocalDate.now()));
            for (TBill tBill : list) {
                tBill.setPayFeesStatus("4");
                TContract contract = contractService.getById(tBill.getContractId());
                LocalDate payableFeesTime = tBill.getPayableFeesTime();
                // 将LocalDate转化为LocalDateTime
                LocalDateTime payableFeesTime1 = LocalDateTime.of(payableFeesTime, LocalTime.of(0, 0, 0));
                LocalDateTime now = LocalDateTime.now();
                // 计算两个时间相差多少个小时
                long hours = ChronoUnit.HOURS.between(payableFeesTime1, now);
                long l = hours / 24;
                if (l>=3){
                    // 违约金比例
                    BigDecimal proportion = contract.getProportion();
                    // 按每天 待缴费金额 * XX% 增加违约金费用
                    if (tBill.getOutstandingMoney().compareTo(new BigDecimal("0"))==0){
                        tBill.setPayFeesStatus("3");
                        billMapper.updateById(tBill);
                        continue;
                    }
                    BigDecimal money = tBill.getOutstandingMoney().multiply(new BigDecimal(100).add(proportion)).divide(new BigDecimal(100),2, BigDecimal.ROUND_DOWN);
                    tBill.setOverDays((int) l);
                    BigDecimal bigDecimal = tBill.getOutstandingMoney().multiply(proportion).setScale(2, BigDecimal.ROUND_DOWN);
                    tBill.setPayableFeesPenalty(tBill.getPayableFeesPenalty()!=null?tBill.getPayableFeesPenalty():new BigDecimal("0").add(bigDecimal));
                    tBill.setOutstandingMoney(money);
                    billMapper.updateById(tBill);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
//        LocalDateTime now = LocalDateTime.now().minusMonths(1).withDayOfMonth(31);
//        System.err.println(now);
//        LocalDateTime now2 = now.plusMonths(1);
//        System.err.println(now2);
//
//        LocalDateTime now1 = LocalDateTime.now();
//        long days = ChronoUnit.DAYS.between(now, now1);
//        long days2 = ChronoUnit.DAYS.between(now.plusDays(1), now1);
//
//        System.err.println(days);
//        System.err.println(days2);
//        LocalDateTime endTime = now.with(TemporalAdjusters.lastDayOfMonth()).withSecond(59).withHour(23).withMinute(59);
//
//        System.err.println(endTime);
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/vo/HouseVO.java
@@ -30,6 +30,8 @@
    private String tenantAttributes;
    @ApiModelProperty(value = "类型")
    private String productType;
    @ApiModelProperty(value = "类型")
    private String tenantType;
    @ApiModelProperty(value = "入住日期")
    @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
    private LocalDateTime startTime;
ruoyi-system/src/main/java/com/ruoyi/system/vo/ProcessTaskListVO.java
@@ -36,6 +36,7 @@
    private Date createTime;
    private String status;
    private Integer taskState;
    private String taskId;
@@ -148,4 +149,8 @@
    @ApiModelProperty(value = "合计年租金")
    private BigDecimal totalYear;
    @ApiModelProperty(value = "合同时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime contractTime;
}
ruoyi-system/src/main/resources/mapper/system/StateTaskCenterMapper.xml
@@ -23,7 +23,7 @@
               tc.id AS contractId, tc.contract_number, tc.contract_name, tc.start_time, tc.end_time,tc.deposit, tc.pay_type,
               tc.first_pay_time, tc.isIncreasing,tc.isIncreasing_deposit,tc.proportion, tc.house_id, tc.party_one_name, tc.party_one_person,
               tc.party_one_phone, tc.tenant_id, tc.party_two_name,tc.party_two_person, tc.party_two_phone,tc.memory, tc.contract_file_name,
               tc.signature, tc.terminate_remark, tc.total_year,tc.status AS contractStatus
               tc.signature, tc.terminate_remark, tc.total_year,tc.status AS contractStatus,tc.create_time AS contractTime
        from state_task_center stc
        LEFT JOIN t_contract tc ON stc.project_id = tc.id
        <where>
ruoyi-system/src/main/resources/mapper/system/TBillMapper.xml
@@ -93,6 +93,9 @@
            <if test="query.userId != null and query.userId !=''">
                and t.id = #{query.userId}
            </if>
            <if test="query.billType != null">
                and b.bill_type = #{query.billType}
            </if>
            and b.disabled = ${@com.ruoyi.common.enums.DisabledEnum@NO.getCode()}
        </where>
        order by b.bill_type,b.payable_fees_time
@@ -162,4 +165,25 @@
    <select id="statisticsOverdue" resultType="java.math.BigDecimal">
        SELECT ifnull(sum(outstanding_money),0) as amount FROM t_bill where pay_fees_status=4
    </select>
    <select id="batchBillCount" resultType="java.lang.Integer">
        SELECT
            count(b.id)
        FROM
            t_bill b
        LEFT JOIN t_contract c ON c.contract_number = b.contract_number and c.disabled=0
        LEFT JOIN t_house h ON h.id = c.house_id and h.disabled=0
        LEFT JOIN t_tenant t ON t.id = c.tenant_id and t.disabled=0
        <where>
            <if test="userId != null and userId !=''">
                AND t.id = #{userId}
            </if>
            <if test="billIds != null and billIds.size() > 0">
                AND b.id NOT IN
                <foreach collection="billIds" item="item" index="index" open="(" separator="," close=")">
                    #{item}
                </foreach>
            </if>
            AND b.bill_type = 3
        </where>
    </select>
</mapper>
ruoyi-system/src/main/resources/mapper/system/TCheckAcceptRecordMapper.xml
@@ -90,8 +90,11 @@
        left join t_house h on t.house_id = h.id
        <where>
            <if test="query.status != null">
                AND t.status = #{query.status}
            <if test="query.status != null and query.status != '' and query.status == 1">
                AND t.status = 0
            </if>
            <if test="query.status != null and query.status != '' and query.status == 2">
                AND t.status = 1
            </if>
            <if test="query.houseNameOrAddress != null and query.houseNameOrAddress != ''">
                AND (h.house_name LIKE concat('%', #{query.houseNameOrAddress}, '%') or h.house_address LIKE concat('%', #{query.houseNameOrAddress}, '%'))
ruoyi-system/src/main/resources/mapper/system/TFaultRepairMessageMapper.xml
@@ -61,6 +61,7 @@
        t.attachment,
        t.attachment_name,
        t.status,
        t.code,
        t.create_time,
        t.update_time,
        t.create_by,
@@ -103,6 +104,7 @@
            t.create_by,
            t.update_by,
            t.disabled,
            t.code,
            i.item_name AS itemName,
            it.type_name AS itemTypeName,
            tnt.resident_name AS residentName
@@ -158,6 +160,7 @@
        t.create_by,
        t.update_by,
        t.disabled,
        t.code,
        i.item_name AS itemName,
        it.type_name AS itemTypeName,
        tnt.resident_name AS residentName
ruoyi-system/src/main/resources/mapper/system/THouseMapper.xml
@@ -29,8 +29,16 @@
        id, house_picture, house_name, house_address, house_area, house_type, business_attributes, product_type, property_right_person, property_right_number, property_right_start_time, property_right_duration, lease_status, create_time, update_time, create_by, update_by, disabled
    </sql>
    <select id="houseList" resultType="com.ruoyi.system.model.THouse">
        select t1.* from
            t_house t1
        select t1.*,t2.start_time as startTime
        from t_house t1
        left join t_contract t2
        on (t2.house_id = t1.id
        and t2.status != 1
        and t2.status != 2
        and t2.status != 3
        and t2.status != 5
        and t2.status != 8
        and NOW() between t2.start_time and t2.end_time)
        <where>
            <if test="req.houseName != null and req.houseName != ''">
                and t1.house_name like concat('%', #{req.houseName}, '%')
@@ -38,8 +46,14 @@
            <if test="req.propertyRightPerson != null and req.propertyRightPerson != ''">
                and t1.property_right_person like concat('%', #{req.propertyRightPerson}, '%')
            </if>
            <if test="req.leaseStatus != null">
            <if test="req.leaseStatus == 3">
                and t1.lease_status = #{req.leaseStatus}
            </if>
            <if test="req.leaseStatus == 2">
                and (t2.start_time is not null)
            </if>
            <if test="req.leaseStatus == 1">
                and (t2.start_time is null) and t1.lease_status = 1
            </if>
            AND t1.disabled = ${@com.ruoyi.common.enums.DisabledEnum@NO.getCode()}
        </where>
@@ -56,6 +70,7 @@
            t3.room_number as roomNumber,
            t3.house_area as houseArea,
            t2.phone as loginAccount,
            t2.tenant_type as tenantType,
            t2.id as tenantId,
            t2.id_card as idCard,
            t2.bank_number as bankNumber,