yupeng
2025-04-09 db7e077ea8f2d995e922bc11b77dc149592a7455
Merge remote-tracking branch 'origin/master' into xizang-changyun

# Conflicts:
# ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TBillController.java
# ruoyi-system/src/main/java/com/ruoyi/system/service/TBillService.java
39个文件已修改
11个文件已添加
1961 ■■■■ 已修改文件
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/ScreenController.java 255 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TBillController.java 33 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TCheckAcceptRecordController.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TContractController.java 199 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/THouseController.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TRentalReturnRecordController.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TTenantController.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordTemplateProcessor.java 174 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordUtil.java 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/IndexController.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TBillController.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TRentalReturnRecordController.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/CodeGenerateUtils.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/dto/TerminateContractDTO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TBillMapper.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TContractMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/THouseMapper.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TRentalReturnRecordMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TBill.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TContract.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TFaultRepairMessage.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/THouse.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TRentalReturnRecord.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TTenant.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/query/RentalReturnAuditQuery.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/query/RentalReturnRecordQuery.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ITRentalReturnRecordService.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/TBillService.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/TContractService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/THouseService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/FlowListenerService.java 112 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ScreenService.java 162 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/StateProcessTemplateServiceImpl.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TBillServiceImpl.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TContractServiceImpl.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/THouseServiceImpl.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TRentalReturnRecordServiceImpl.java 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TTenantServiceImpl.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/task/utils/TaskUtil.java 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/vo/HouseMapDistributionVO.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/vo/MyHouseVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/vo/RentalRetureApplyVO.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/vo/RentalReturnRecordVO.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/TBillMapper.xml 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/TCheckAcceptRecordMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/TContractMapper.xml 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/TFaultRepairMessageMapper.xml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/THouseMapper.xml 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/TRentalReturnRecordMapper.xml 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/ScreenController.java
@@ -1,8 +1,12 @@
package com.ruoyi.web.controller.api;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.system.model.TBill;
import com.ruoyi.system.model.TContract;
import com.ruoyi.system.model.THouse;
@@ -14,7 +18,6 @@
import com.ruoyi.system.service.impl.ScreenService;
import com.ruoyi.system.vo.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Lazy;
@@ -30,6 +33,8 @@
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
import static org.checkerframework.checker.units.qual.Prefix.one;
/**
 * @author mitao
@@ -64,49 +69,29 @@
        return R.ok(screenService.rentIncomeTrend());
    }
    @GetMapping("/getTenantCountTrend")
    @ApiModelProperty(value = "租户数量趋势统计")
    @ApiOperation(value = "租户数量趋势统计")
    public R<List<TenantCountTrendVO>> getTenantCountTrend() {
        Date currentDate = new Date();
        Date targetDate = DateUtils.addMonths(currentDate, -3 * 6);
        Map<String, Date> startQuarterDate = DateUtils.getQuarterDate(targetDate);
        Date targetDate2 = DateUtils.addMonths(currentDate, 0);
        Map<String, Date> endQuarterDate = DateUtils.getQuarterDate(targetDate2);
        List<TContract> contracts = contractService.list(new LambdaQueryWrapper<TContract>()
                .isNotNull(TContract::getSignTime)
                .between(TContract::getSignTime, startQuarterDate.get("first"), endQuarterDate.get("last"))
                .orderByAsc(TContract::getSignTime));
        // 创建季度格式化工具(示例:2025-Q1)
        DateTimeFormatter quarterFormatter = DateTimeFormatter.ofPattern("yyyy-'Q'Q");
        List<TenantCountTrendVO> trendData = contracts.stream()
                .collect(Collectors.groupingBy(contract -> {
                    LocalDate date = contract.getSignTime().toLocalDate();
                    int quarter = (date.getMonthValue() - 1) / 3 + 1;
                    return YearQuarter.from(date.withMonth(quarter * 3 - 2));
                }, TreeMap::new, Collectors.counting()))
                .entrySet().stream()
                .map(entry -> new TenantCountTrendVO(
                        entry.getKey().format(quarterFormatter),
                        entry.getValue()))
                .collect(Collectors.toList());
        return R.ok(trendData);
        return screenService.getTenantCountTrend();
    }
    /**
     * 实时租赁数据
     */
    @GetMapping("/getRealTimeRentData")
    @ApiOperation("获取实时租赁数据")
    public R<List<RealTimeRentDataVO>> getRealTimeRentData() {
        // 随机获取十条房源
        String businessDeptId = SecurityUtils.getBusinessDeptId();
        List<THouse> houses = houseService.list(new LambdaQueryWrapper<THouse>()
                .eq(!"0".equals(businessDeptId),THouse::getBusinessDeptId, businessDeptId)
                .last("ORDER BY RAND() LIMIT 10"));
        if (CollectionUtil.isEmpty(houses)){
            return R.ok(new ArrayList<>());
        }
        // 提取streetIds
        List<String> streetIds = houses.stream()
@@ -121,7 +106,7 @@
        List<RealTimeRentDataVO> result = houses.stream().map(house -> {
            RealTimeRentDataVO vo = new RealTimeRentDataVO();
            vo.setStreetName(streetMap.getOrDefault(house.getStreetId(), "未知"));
            vo.setRoomName(house.getRoomNumber());
            vo.setRoomName(house.getHouseName());
            vo.setLeaseStatus(house.getLeaseStatus());
            return vo;
        }).collect(Collectors.toList());
@@ -129,103 +114,147 @@
        return R.ok(result);
    }
    public static void main(String[] args) {
        // 获取当前季度的开始和结束时间
        // 获取当前季度的开始和结束时间的实现示例:
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate now = LocalDate.now();
        int month = now.getMonthValue();
        int year = now.getYear();
        // 计算季度起始月份(1,4,7,10)
        int quarterStartMonth = ((month - 1) / 3) * 3 + 1;
        int quarterEndMonth = quarterStartMonth + 2;
        // 构建季度起止日期
        LocalDate quarterStart = YearMonth.of(year, quarterStartMonth).atDay(1);
        LocalDate quarterEnd = YearMonth.of(year, quarterEndMonth).atEndOfMonth();
        System.out.println("季度开始:" + quarterStart.format(formatter));
        System.out.println("季度结束:" + quarterEnd.format(formatter));
    }
    /**
     * 获取房屋地图分布
     */
    @GetMapping("/getHouseMapDistribution")
    @ApiOperation("获取房屋地图分布")
    public R<List<HouseMapDistributionVO>> getHouseMapDistribution() {
        String businessDeptId = SecurityUtils.getBusinessDeptId();
        List<TContract> tContracts = contractService.list(new LambdaQueryWrapper<TContract>()
                .eq(!"0".equals(businessDeptId), TContract::getBusinessDeptId, businessDeptId)
                .eq(TContract::getPayType, 2));
        List<String> houseIds = tContracts.stream().map(TContract::getHouseId).collect(Collectors.toList());
        // 获取所有房屋信息
        List<THouse> houses = houseService.list();
        List<THouse> houses = houseService.list(new LambdaQueryWrapper<THouse>()
                        .and(wrapper -> wrapper.in(!houseIds.isEmpty(),THouse::getId, houseIds)
                                        .or(!houseIds.isEmpty())
                                        .eq(THouse::getLeaseStatus, "1")
                        )
                .eq(!"0".equals(businessDeptId),THouse::getBusinessDeptId, businessDeptId)
        );
        List<HouseMapDistributionVO> result = new ArrayList<>();
        for (THouse house : houses) {
            HouseMapDistributionVO houseMapDistributionVO = new HouseMapDistributionVO();
            houseMapDistributionVO.setHouseName(house.getHouseName());
            houseMapDistributionVO.setHouseAddress(house.getHouseAddress());
            houseMapDistributionVO.setHouseStatus(house.getLeaseStatus());
            TContract contract = contractService.getOne(new LambdaQueryWrapper<TContract>()
                    .eq(TContract::getHouseId, house.getId()));
            TBill bill = billService.getOne(new LambdaQueryWrapper<TBill>()
                    .eq(TBill::getContractId, contract.getId())
                    .eq(TBill::getBillType, 1));
            houseMapDistributionVO.setTenant(contract.getPartyTwoName());
            LocalDateTime startTime = contract.getStartTime();
            LocalDateTime endTime = contract.getEndTime();
            BigDecimal monthRent = contract.getMonthRent();
            // 计算相差月份
            long monthsBetween = ChronoUnit.MONTHS.between(startTime, endTime);
            BigDecimal payableFeesMoney = monthRent.multiply(new BigDecimal(monthsBetween));
            BigDecimal remainingPayment = bill.getPayableFeesMoney();
            BigDecimal paidAlready = payableFeesMoney.subtract(remainingPayment);
            String rentStatus = String.format("%.2f/%.2f", paidAlready, payableFeesMoney);
            houseMapDistributionVO.setRentStatus(rentStatus);
            String payType = contract.getPayType();
            String rent = "";
            LocalDateTime payFeesTime = bill.getPayFeesTime();
            switch (payType) {
                case "1":
                    if (isCurrentMonth(payFeesTime)) {
                        rent = String.format("%.2f/%.2f", monthRent, monthRent);
                    } else {
                        rent = String.format("%.2f/%.2f", new BigDecimal("0"), monthRent);
                    }
                    break;
                case "2":
                    // 季付价格
                    BigDecimal quarterRent = monthRent.multiply(new BigDecimal(3));
                    if (isCurrentQuarter(payFeesTime)) {
                        rent = String.format("%.2f/%.2f", quarterRent, quarterRent);
                    } else {
                        rent = String.format("%.2f/%.2f", new BigDecimal("0"), quarterRent);
                    }
                    break;
                case "3":
                    // 年付价格
                    BigDecimal yearRent = monthRent.multiply(new BigDecimal(12));
                    if (isCurrentYear(payFeesTime)) {
                        rent = String.format("%.2f/%.2f", yearRent, yearRent);
                    } else {
                        rent = String.format("%.2f/%.2f", new BigDecimal("0"), yearRent);
                    }
                    break;
            }
            houseMapDistributionVO.setRent(rent);
            houseMapDistributionVO.setLongitude(house.getLongitude());
            houseMapDistributionVO.setLatitude(house.getLatitude());
            TContract contract = contractService.getOne(new LambdaQueryWrapper<TContract>()
                    .eq(!"0".equals(businessDeptId),TContract::getBusinessDeptId, businessDeptId)
                    .eq(TContract::getPayType, 2)
                    .gt(TContract::getEndTime, LocalDate.now())
                    .eq(TContract::getHouseId, house.getId())
                    .eq(TContract::getStatus, 4)
                    .last("limit 1"));
            if (contract != null){
                List<TBill> tBills = billService.list(new LambdaQueryWrapper<TBill>()
                        .eq(!"0".equals(businessDeptId),TBill::getBusinessDeptId, businessDeptId)
                        .eq(TBill::getContractId, contract.getId())
                        .eq(TBill::getBillType, 1));
                houseMapDistributionVO.setTenant(contract.getPartyTwoName());
                BigDecimal payFeesMoney = tBills.stream()
                        .map(TBill::getPayFeesMoney)
                        .reduce(BigDecimal::add)
                        .orElse(BigDecimal.ZERO);
                BigDecimal payableFeesMoney = tBills.stream()
                        .map(TBill::getPayableFeesMoney)
                        .reduce(BigDecimal::add)
                        .orElse(BigDecimal.ZERO);
                String rentStatus = String.format("%.2f/%.2f", payFeesMoney, payableFeesMoney);
                houseMapDistributionVO.setRentStatus(rentStatus);
//                TBill one = billService.getOne(new LambdaQueryWrapper<TBill>()
//                        .le(TBill::getStartTime, LocalDate.now())
//                        .ge(TBill::getEndTime, LocalDate.now())
//                        .eq(TBill::getBillType, 1)
//                        .eq(TBill::getContractId, contract.getId()));
                // 获取当前季度的开始和结束时间
                // 获取当前季度的开始和结束时间的实现示例:
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
                LocalDate now = LocalDate.now();
                int month = now.getMonthValue();
                int year = now.getYear();
                // 计算季度起始月份(1,4,7,10)
                int quarterStartMonth = ((month - 1) / 3) * 3 + 1;
                int quarterEndMonth = quarterStartMonth + 2;
                // 构建季度起止日期
                LocalDate quarterStart = YearMonth.of(year, quarterStartMonth).atDay(1);
                LocalDate quarterEnd = YearMonth.of(year, quarterEndMonth).atEndOfMonth();
                System.out.println("季度开始:" + quarterStart.format(formatter));
                System.out.println("季度结束:" + quarterEnd.format(formatter));
                List<TBill> ones = billService.list(new LambdaQueryWrapper<TBill>()
                        .eq(!"0".equals(businessDeptId),TBill::getBusinessDeptId, businessDeptId)
                        .between(TBill::getStartTime, quarterStart, quarterEnd)
                        .eq(TBill::getBillType, 1)
                        .eq(TBill::getContractId, contract.getId()));
                if (!ones.isEmpty()){
                    long count = ones.stream().filter(tBill -> "4".equals(tBill.getPayFeesStatus())).count();
                    if (count > 0){
                        houseMapDistributionVO.setHouseStatus("4");
                    }
                    BigDecimal payFeesMoney1 = ones.stream()
                            .map(TBill::getPayFeesMoney)
                            .reduce(BigDecimal::add)
                            .orElse(BigDecimal.ZERO);
                    BigDecimal payableFeesMoney1 = ones.stream()
                            .map(TBill::getPayableFeesMoney)
                            .reduce(BigDecimal::add)
                            .orElse(BigDecimal.ZERO);
                    String rent = String.format("%.2f/%.2f", payFeesMoney1, payableFeesMoney1);
                    houseMapDistributionVO.setRent(rent);
                }else {
                    houseMapDistributionVO.setRent("欠费");
                }
            }else {
                houseMapDistributionVO.setTenant("暂无");
                houseMapDistributionVO.setRentStatus("待出租");
                houseMapDistributionVO.setRent("暂无");
            }
            result.add(houseMapDistributionVO);
        }
        return R.ok(result);
    }
    /**
     * 判断是否是当前月份
     * @param dateTime 日期时间
     * @return boolean
     */
    public static boolean isCurrentMonth(LocalDateTime dateTime) {
        YearMonth currentYearMonth = YearMonth.now();
        YearMonth targetYearMonth = YearMonth.from(dateTime);
        return currentYearMonth.equals(targetYearMonth);
    }
    public static boolean isCurrentQuarter(LocalDateTime dateTime) {
        int currentMonth = LocalDateTime.now().getMonthValue();
        int targetMonth = dateTime.getMonthValue();
        // 计算当前季度和目标时间所属季度
        int currentQuarter = (currentMonth - 1) / 3 + 1;
        int targetQuarter = (targetMonth - 1) / 3 + 1;
        return LocalDateTime.now().getYear() == dateTime.getYear() && currentQuarter == targetQuarter;
    }
    public static boolean isCurrentYear(LocalDateTime dateTime) {
        return LocalDateTime.now().getYear() == dateTime.getYear();
    }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TBillController.java
@@ -3,27 +3,39 @@
import com.ruoyi.common.basic.PageInfo;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.dto.*;
import com.ruoyi.system.model.TBill;
import com.ruoyi.system.dto.BillStatisticsDto;
import com.ruoyi.system.dto.CachPayDto;
import com.ruoyi.system.dto.OfflinePayCheckDto;
import com.ruoyi.system.dto.SmsByBillDto;
import com.ruoyi.system.dto.TBillDto;
import com.ruoyi.system.dto.TbillSaveDto;
import com.ruoyi.system.model.TBillConfirm;
import com.ruoyi.system.model.TBillDetail;
import com.ruoyi.system.model.TContract;
import com.ruoyi.system.query.TBillQuery;
import com.ruoyi.system.query.TContractQuery;
import com.ruoyi.system.service.*;
import com.ruoyi.system.service.TBillConfirmService;
import com.ruoyi.system.service.TBillDetailService;
import com.ruoyi.system.service.TBillService;
import com.ruoyi.system.service.TContractService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.constraints.NotEmpty;
import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;
/**
 * <p>
@@ -78,8 +90,15 @@
    @PreAuthorize("@ss.hasPermi('bill:list:addRent')")
    @PostMapping("add")
    @ApiOperation("新增账单")
    public R add(@Validated @RequestBody TbillSaveDto bill){
        bill.setBusinessDeptId(SecurityUtils.getBusinessDeptId());
    public R<PageInfo<TBillDto>> add(@Validated @RequestBody TbillSaveDto bill){
        String businessDeptId = SecurityUtils.getBusinessDeptId();
        if (SecurityUtils.getBusinessDeptId().equals("0")) {
            TContract contract = contractService.getById(bill.getContractId());
            if (Objects.nonNull(contract)) {
                businessDeptId = contract.getBusinessDeptId();
            }
        }
        bill.setBusinessDeptId(businessDeptId);
        tBillService.saveBill(bill);
        return R.ok();
    }
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TCheckAcceptRecordController.java
@@ -7,15 +7,9 @@
import com.ruoyi.common.basic.PageInfo;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.model.TBill;
import com.ruoyi.system.model.TCheckAcceptRecord;
import com.ruoyi.system.model.TContract;
import com.ruoyi.system.model.THouse;
import com.ruoyi.system.model.*;
import com.ruoyi.system.query.TCheckAcceptRecordQuery;
import com.ruoyi.system.service.TBillService;
import com.ruoyi.system.service.TCheckAcceptRecordService;
import com.ruoyi.system.service.TContractService;
import com.ruoyi.system.service.THouseService;
import com.ruoyi.system.service.*;
import com.ruoyi.system.vo.TCheckAcceptRecordVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -29,6 +23,7 @@
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
/**
 * <p>
@@ -48,6 +43,8 @@
    private final THouseService houseService;
    @Autowired
    private TBillService billService;
    @Autowired
    private TTenantService tenantService;
    @Autowired
    public TCheckAcceptRecordController(TCheckAcceptRecordService checkAcceptRecordService, TContractService contractService, THouseService houseService) {
        this.checkAcceptRecordService = checkAcceptRecordService;
@@ -73,6 +70,10 @@
    public R<THouse> getHouseByContractId(@RequestParam String contractId) {
        TContract contract = contractService.getById(contractId);
        THouse house = houseService.getById(contract.getHouseId());
        TTenant tenant = tenantService.getById(contract.getTenantId());
        if(Objects.nonNull(tenant)){
            house.setTenantType(tenant.getTenantType());
        }
        return R.ok(house);
    }
@@ -118,6 +119,7 @@
            tBill.setPayFeesStatus("1");
            tBill.setBillType("4");
            tBill.setSmsStatus(0);
            tBill.setBusinessDeptId(contract.getBusinessDeptId());
            billService.save(tBill);
        }
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TContractController.java
@@ -14,22 +14,31 @@
import com.ruoyi.common.constant.DictConstants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.enums.DisabledEnum;
import com.ruoyi.common.enums.ProcessCategoryEnum;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.WebUtils;
import com.ruoyi.common.utils.*;
import com.ruoyi.system.bo.ProcessStartBO;
import com.ruoyi.system.dto.RevokeDTO;
import com.ruoyi.system.dto.SetContractDto;
import com.ruoyi.system.dto.TContractDTO;
import com.ruoyi.system.dto.TerminateContractDTO;
import com.ruoyi.system.export.ContractExport;
import com.ruoyi.system.model.*;
import com.ruoyi.system.model.TBill;
import com.ruoyi.system.model.TCheckAcceptRecord;
import com.ruoyi.system.model.TContract;
import com.ruoyi.system.model.TContractRentType;
import com.ruoyi.system.model.THouse;
import com.ruoyi.system.model.TTenant;
import com.ruoyi.system.query.TContractBillQuery;
import com.ruoyi.system.query.TContractQuery;
import com.ruoyi.system.service.*;
import com.ruoyi.system.service.StateProcessTemplateService;
import com.ruoyi.system.service.TBillService;
import com.ruoyi.system.service.TCheckAcceptRecordService;
import com.ruoyi.system.service.TContractRentTypeService;
import com.ruoyi.system.service.TContractService;
import com.ruoyi.system.service.THouseService;
import com.ruoyi.system.service.TTenantService;
import com.ruoyi.system.task.base.QuartzManager;
import com.ruoyi.system.task.base.TimeJobType;
import com.ruoyi.system.task.jobs.StateProcessJob;
@@ -45,17 +54,30 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
 * <p>
@@ -98,7 +120,8 @@
    @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()));
        long count = contractService.count(new LambdaQueryWrapper<TContract>()
                .eq(TContract::getContractNumber, dto.getContractNumber()));
        if (count!=0){
            return R.fail("合同编号不可重复");
        }
@@ -109,7 +132,11 @@
        if (Objects.isNull(house)) {
            throw new ServiceException("房产信息不存在");
        }
        house.setRentalReturnStatus("1");
        houseService.updateById(house);
        dto.setBusinessDeptId(house.getBusinessDeptId());
        // 生成合同编号
        dto.setContractNumber(CodeGenerateUtils.generateVolumeSn(house.getBusinessDeptId()));
        contractService.save(dto);
        if (dto.getStatus().equals("2")){
            //发起合同新增审批
@@ -137,7 +164,13 @@
                            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
                    );
                }
            }
        }
@@ -204,7 +237,13 @@
                            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
                    );
                }
            }
        }
@@ -228,7 +267,9 @@
        TContractVO res = new TContractVO();
        TContract contract = contractService.getById(id);
        BeanUtils.copyProperties(contract,res);
        TContractRentType contractRentType = contractRentTypeService.lambdaQuery().eq(TContractRentType::getContractId, id).one();
        TContractRentType contractRentType = contractRentTypeService.lambdaQuery()
                .eq(TContractRentType::getContractId, id)
                .one();
        if (contractRentType!=null){
            BeanUtils.copyProperties(contractRentType,res);
        }
@@ -248,8 +289,15 @@
        for (TBill tBill : list) {
            payMoney = payMoney.add(tBill.getOutstandingMoney()).add(tBill.getPayableFeesPenalty());
        }
        TCheckAcceptRecord tCheckAcceptRecord = checkAcceptRecordService.lambdaQuery().eq(TCheckAcceptRecord::getContractId, id).one();
        res.setCheckResult(Objects.nonNull(tCheckAcceptRecord)&&Objects.nonNull(tCheckAcceptRecord.getCheckResult())?tCheckAcceptRecord.getCheckResult():null);
        TCheckAcceptRecord tCheckAcceptRecord = checkAcceptRecordService.lambdaQuery()
                .eq(TCheckAcceptRecord::getContractId, id)
                .one();
        res.setCheckResult(
                Objects.nonNull(tCheckAcceptRecord)
                        &&Objects.nonNull(tCheckAcceptRecord.getCheckResult())
                        ?tCheckAcceptRecord.getCheckResult()
                        :null
        );
        res.setPayMoney(payMoney);
        return R.ok(res);
@@ -271,7 +319,6 @@
    @Log(title = "合同管理-确认结算", businessType =  BusinessType.UPDATE)
    @ApiOperation(value = "确认结算")
    @PostMapping(value = "/confirmSettlement")
    public R<Boolean> confirmSettlement(String id) {
        TContract contract = contractService.getById(id);
        contract.setStatus("8");
@@ -279,11 +326,17 @@
        // 将所有未缴费账单设置未已失效
        List<TBill> tBills = billService.list(new LambdaQueryWrapper<TBill>()
                .ne(TBill::getPayFeesStatus, 3)
                        .ne(TBill::getBillType,4)
                .eq(TBill::getContractId, contract.getId()));
        for (TBill tBill : tBills) {
            tBill.setPayFeesStatus("5");
        }
        billService.updateBatchById(tBills);        return R.ok();
        billService.updateBatchById(tBills);
        // 将房屋改成待出租
        THouse house = houseService.getById(contract.getHouseId());
        house.setLeaseStatus("1");
        houseService.updateById(house);
        return R.ok();
    }
    @ApiOperation(value = "终止合同剩余未缴费账单列表")
    @PostMapping(value = "/contractBillList")
@@ -319,6 +372,18 @@
        return R.ok(res);
    }
    // 计算两个日期相差天数的方法实现:
    public static long calculateDaysBetween(LocalDateTime start, LocalDateTime end) {
        return ChronoUnit.DAYS.between(start, end);
    }
    public static void main(String[] args) {
        LocalDateTime start = LocalDateTime.of(2024, 1, 1, 0, 0);
        LocalDateTime end = LocalDateTime.of(2024, 1, 5, 12, 0);
        long days = calculateDaysBetween(start, end); // 返回4天(不满一天不计)
    }
    private String generateContract(TContract contract) {
        String templateFileName = "1_yzj_租赁合同_个人.docx";
        String contractId = contract.getId();
@@ -328,7 +393,9 @@
        if (StringUtils.isNotEmpty(contractId)) {
            firstBill = billService.lambdaQuery()
                    .eq(TBill::getContractId, contractId)
                    .orderByDesc(TBill::getStartTime)
                    .eq(TBill::getBillType, 1)
                    .orderByAsc(TBill::getStartTime)
                    .ne(TBill::getManualAddition, DisabledEnum.YES.getCode())
                    .last("limit 1")
                    .one();
            tCheckAcceptRecord = checkAcceptRecordService.lambdaQuery()
@@ -347,7 +414,7 @@
        Map<String, Object> templateParam = new HashMap<>(5);
        fill(templateParam, "contractNumber", contract.getContractNumber());
        fill(templateParam, "partyOneName", contract.getPartyOneName());
        fill(templateParam, "partyTwoName", contract.getPartyTwoName());
        if (Objects.nonNull(tenant)) {
            fill(templateParam, "mailAddress", tenant.getMailAddress());
@@ -355,6 +422,8 @@
            fill(templateParam, "residentName", tenant.getResidentName());
            fill(templateParam, "bankNumber", tenant.getBankNumber());
            fill(templateParam, "bankName", tenant.getBankName());
            fill(templateParam, "partyTwoName", tenant.getLessee());
            fill(templateParam, "email", tenant.getEmail());
            // 企业、政府机构、国有企业
            if (Objects.nonNull(tenant.getTenantType())
@@ -418,19 +487,27 @@
        // 首期租金处理
        if (firstBill != null) {
            double 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, RoundingMode.DOWN).doubleValue();
            fill(templateParam, "firstRent", "¥" + firstBill.getPayableFeesMoney() + "元");
            // 其他财务字段
            fill(templateParam, "firstRentString", "人民币" + NumberToChineseUtils.numberToChinese(firstBill.getPayableFeesMoney().doubleValue()));
        } else {
            fill(templateParam, "firstRent", "");
//            fill(templateParam, "firstRent", "");
        }
        // 其他财务字段
        fill(templateParam, "firstRentString", "人民币" + NumberToChineseUtils.numberToChinese(
                (contract.getPayType().equals("1")
        fill(templateParam, "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, RoundingMode.DOWN).doubleValue()));
                        :contract.getPayType().equals("2")
                        ?contract.getMonthRent().multiply(new BigDecimal("3"))
                        :contract.getMonthRent().multiply(new BigDecimal("12"))
                        .setScale(2, RoundingMode.DOWN)).doubleValue()+"元");
        fill(templateParam, "firstRentString",
                "人民币"+NumberToChineseUtils.numberToChinese(
@@ -476,7 +553,7 @@
        if (StringUtils.isEmpty(key)){
            throw new RuntimeException("key不能为空");
        }
        templateParam.put(StringUtils.format("${{}}", "contractNumber"), value != null ? value : "");
        templateParam.put("${"+key+"}", value != null ? value : "");
    }
    /**
@@ -498,10 +575,10 @@
    @PreAuthorize("@ss.hasPermi('contract:list:export')")
    @Log(title = "导出", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void exportOpticalInspection(@RequestBody TContractQuery query)
    {
    public void exportOpticalInspection(@RequestBody TContractQuery query) throws UnsupportedEncodingException {
        List<ContractExport> contractExports = new ArrayList<>();
        List<TContract> exportList = contractService.contractExportList(query);
        for (TContract contract : exportList) {
            ContractExport contractExport = new ContractExport();
            contractExport.setContractNumber(contract.getContractNumber());
@@ -511,33 +588,61 @@
            contractExport.setCreateTime(DateUtils.localDateTimeToStringYear(contract.getCreateTime()));
            contractExport.setStartTime(DateUtils.localDateTimeToStringYear(contract.getStartTime()));
            contractExport.setEndTime(DateUtils.localDateTimeToStringYear(contract.getEndTime()));
            contractExport.setPayType(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CONTRACT_PAY_TYPE,contract.getPayType()));
            contractExport.setDeposit(contract.getDeposit()+"");
            contractExport.setStatus(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CONTRACT_STATUS,contract.getStatus()));
            contractExport.setDeposit(contract.getDeposit() + "");
            contractExports.add(contractExport);
            contractExport.setPayType(
                    DictUtils.getDictLabel(
                            DictConstants.DICT_TYPE_CONTRACT_PAY_TYPE, contract.getPayType())
            );
            contractExport.setStatus(
                    DictUtils.getDictLabel(DictConstants.DICT_TYPE_CONTRACT_STATUS, contract.getStatus()));
        }
        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), ContractExport.class, contractExports);
        HttpServletResponse response = WebUtils.response();
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        ServletOutputStream outputStream = null;
        try {
            String fileName = URLEncoder.encode("合同列表.xls", "utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
            response.setContentType("application/vnd.ms-excel;charset=UTF-8");
            response.setHeader("Pragma", "no-cache");
            response.setHeader("Cache-Control", "no-cache");
            outputStream = response.getOutputStream();
        response.setHeader("Content-Disposition",
                "attachment;filename=" + URLEncoder.encode("合同列表.xls", "utf-8"));
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
        try (Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), ContractExport.class, contractExports);
             ServletOutputStream outputStream = response.getOutputStream()) {
            workbook.write(outputStream);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    @ApiOperation("上传合同附件")
    @PostMapping(value = "/upload-file")
    public R<Boolean> uploadFile(@RequestBody TContractDTO dto ) {
        if (Objects.isNull(dto.getId())) {
            throw new ServiceException("合同id不能为空");
        }
        TContract contract = contractService.getById(dto.getId());
        if (Objects.isNull(contract)) {
            throw new ServiceException("合同不存在");
        }
  /*      if (StringUtils.isNotBlank(contract.getContractFile())) {
            List<String> contractFileList = Arrays.stream(contract.getContractFile().split(",")).collect(Collectors.toList());
            List<String> memoryList = Arrays.stream(contract.getMemory().split(",")).collect(Collectors.toList());
            List<String> contractNameList = Arrays.stream(contract.getContractFileName().split(",")).collect(Collectors.toList());
            contractFileList.addAll(Arrays.asList(dto.getContractFile().split(",")));
            contractNameList.addAll(Arrays.asList(dto.getContractFileName().split(",")));
            memoryList.addAll(Arrays.asList(dto.getMemory().split(",")));
            contract.setContractFile(String.join(",", contractFileList));
            contract.setContractFileName(String.join(",", contractNameList));
            contract.setMemory(String.join(",", memoryList));
        } else {
            contract.setContractFile(dto.getContractFile());
            contract.setContractFileName(dto.getContractFileName());
            contract.setMemory(dto.getMemory());
        }*/
        contract.setContractFile(dto.getContractFile());
        contract.setContractFileName(dto.getContractFileName());
        contract.setMemory(dto.getMemory());
        return R.ok(contractService.updateById(contract));
    }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/THouseController.java
@@ -1,6 +1,7 @@
package com.ruoyi.web.controller.api;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.basic.PageInfo;
import com.ruoyi.common.constant.DictConstants;
@@ -77,7 +78,7 @@
    @ApiOperation(value = "获取房屋分页列表")
    @PostMapping(value = "/houseList")
    @PreAuthorize("@ss.hasPermi('house:list')")
    public R<PageInfo<THouse>> houseList(@RequestBody THouseQuery query) {
    public R<IPage<THouse>> houseList(@RequestBody THouseQuery query) {
        return R.ok(tHouseService.houseList(query));
    }
    @ApiOperation(value = "历史租户列表")
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TRentalReturnRecordController.java
New file
@@ -0,0 +1,79 @@
package com.ruoyi.web.controller.api;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.model.TRentalReturnRecord;
import com.ruoyi.system.query.RentalReturnAuditQuery;
import com.ruoyi.system.query.RentalReturnRecordQuery;
import com.ruoyi.system.service.ITRentalReturnRecordService;
import com.ruoyi.system.vo.RentalReturnRecordVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/rentalReturnRecord")
@RequiredArgsConstructor(onConstructor_ = {@Lazy})
@Api(tags = {"房屋管理-退租申请"})
public class TRentalReturnRecordController extends BaseController {
    private final ITRentalReturnRecordService rentalReturnRecordService;
    /**
     * 申请记录列表
     */
    @ApiOperation(value = "申请记录列表")
    @GetMapping("list")
    @PreAuthorize("@ss.hasPermi('houseManage:apply:list')")
    public R<IPage<RentalReturnRecordVO>> list(Page<RentalReturnRecordVO> page, RentalReturnRecordQuery query) {
        return R.ok(rentalReturnRecordService.queryRentalReturnRecordList(page, query));
    }
    /**
     * 审核记录删除
     */
    @ApiOperation(value = "审核记录删除")
    @GetMapping("delete")
    @PreAuthorize("@ss.hasPermi('houseManage:apply:delete')")
    public R<String> delete(@ApiParam (value = "id") String id) {
        boolean remove = rentalReturnRecordService.update(new LambdaUpdateWrapper<TRentalReturnRecord>()
                .set(TRentalReturnRecord::getIsAdminDelete, true)
                .eq(TRentalReturnRecord::getId, id)
                .ne(TRentalReturnRecord::getAuditStatus, 1));
        if (!remove){
            return R.fail("删除失败");
        }
        return R.ok("删除成功");
    }
    /**
     * 申请记录详情
     */
    @ApiOperation(value = "申请记录详情")
    @GetMapping("detail")
    @PreAuthorize("@ss.hasPermi('houseManage:apply:detail')")
    public R<RentalReturnRecordVO> detail(@ApiParam (value = "id") String id) {
        RentalReturnRecordVO rentalReturnRecordVO = rentalReturnRecordService.queryRentalReturnRecordById(id);
        return R.ok(rentalReturnRecordVO);
    }
    /**
     * 申请审核
     */
    @ApiOperation(value = "申请审核")
    @PostMapping("audit")
    @PreAuthorize("@ss.hasPermi('houseManage:apply:audit')")
    public R<String> audit(@RequestBody RentalReturnAuditQuery returnAuditQuery) {
        rentalReturnRecordService.audit(returnAuditQuery, getUserId(), getUsername());
        return R.ok("审核成功");
    }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TTenantController.java
@@ -115,7 +115,10 @@
    @ApiOperation(value = "获取合同分页列表")
    @GetMapping(value = "/contractListByTenantId")
    public R<List<TContract>> contractListByTenantId(@RequestParam String tenantId) {
        return R.ok(contractService.list(Wrappers.lambdaQuery(TContract.class).eq(TContract::getTenantId, tenantId)));
        String businessDeptId = SecurityUtils.getBusinessDeptId();
        return R.ok(contractService.list(Wrappers.lambdaQuery(TContract.class)
                .eq(!businessDeptId.equals("0"), TContract::getBusinessDeptId, businessDeptId)
                .eq(TContract::getTenantId, tenantId)));
    }
    /**
ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordTemplateProcessor.java
@@ -1,87 +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);
    }
}
//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
@@ -5,18 +5,31 @@
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.poi.xwpf.usermodel.UnderlinePatterns;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/IndexController.java
@@ -202,6 +202,7 @@
                myToDoVO.setStartTime(DateUtils.localDateTimeToStringYear(contract.getStartTime()));
                myToDoVO.setPropertyRightPerson(tHouse.getPropertyRightPerson());
                myToDoVO.setPhone(tHouse.getPhone());
                myToDoVO.setRentalReturnStatus(tHouse.getRentalReturnStatus() == null ? "1" : tHouse.getRentalReturnStatus());
                List<TBill> billList = bills.stream().filter(e -> e.getContractId().equals(contract.getId())).collect(Collectors.toList());
                List<PayListVO> payList = new ArrayList<>();
                for (TBill tBill : billList) {
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TBillController.java
@@ -9,7 +9,6 @@
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;
@@ -22,12 +21,14 @@
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
 * <p>
@@ -129,17 +130,39 @@
    @PostMapping(value = "/invoice")
    public R<String> invoice(@RequestBody TInvoiceDTO dto) {
        String userId = tokenService.getLoginUserApplet().getUserId();
        List<String> billIds = dto.getBillIds();
        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());
        String deptId = "";
        List<TBill> list = tBillService.list(Wrappers.lambdaQuery(TBill.class).in(TBill::getId, billIds));
        for (String billId : billIds) {
            TBill tBill = list.stream().filter(bill -> bill.getId().equals(billId)).findFirst().orElse(null);
            if(StringUtils.hasLength(deptId)){
                if(Objects.nonNull(tBill)){
                    if(!deptId.equals(tBill.getBusinessDeptId())){
                        return R.fail("请选择同一运营部门账单开票");
                    }else {
                        deptId = tBill.getBusinessDeptId();
                    }
                }
            }else {
                if(Objects.nonNull(tBill)){
                    deptId = tBill.getBusinessDeptId();
                }else {
                    return R.fail("请选择同一运营部门账单开票");
                }
            }
        }
        dto.setBusinessDeptId(deptId);
        // 添加开票信息
        invoiceService.save(dto);
        // 添加开票信息中间表信息
        List<String> billIds = dto.getBillIds();
        List<TInvoiceToBill> sysInvoiceToBills = new ArrayList<>();
        for (String billId : billIds) {
            TInvoiceToBill tInvoiceToBill = new TInvoiceToBill();
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TRentalReturnRecordController.java
New file
@@ -0,0 +1,57 @@
package com.ruoyi.web.controller.api;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.model.LoginUserApplet;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.model.TContract;
import com.ruoyi.system.model.TRentalReturnRecord;
import com.ruoyi.system.service.ITRentalReturnRecordService;
import com.ruoyi.system.service.TContractService;
import com.ruoyi.system.vo.RentalRetureApplyVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
@RestController
@RequestMapping("/rentalReturnRecord")
@RequiredArgsConstructor(onConstructor_ = {@Lazy})
@Api(tags = {"房屋管理-退租申请"})
public class TRentalReturnRecordController extends BaseController {
    private final ITRentalReturnRecordService rentalReturnRecordService;
    private final TokenService tokenService;
    /**
     * 申请退租
     */
    @ApiOperation(value = "申请退租")
    @PostMapping("/apply")
    public R<?> apply(@RequestBody RentalRetureApplyVO rentalReture) {
        rentalReturnRecordService.apply(rentalReture, tokenService.getLoginUserApplet().getUserId());
        return R.ok();
    }
    /**
     * 申请详情
     */
    @ApiOperation(value = "申请详情")
    @GetMapping("/detail")
    public R<TRentalReturnRecord> detail(@ApiParam (value = "合同id") @RequestParam String contractId) {
        TRentalReturnRecord rentalReturnRecord = rentalReturnRecordService.getOne(new LambdaQueryWrapper<TRentalReturnRecord>()
                .eq(TRentalReturnRecord::getContractId, contractId)
                .last("limit 1")
                .orderByDesc(TRentalReturnRecord::getCreateTime));
        if (rentalReturnRecord == null){
            return R.fail("暂无申请记录");
        }
        TRentalReturnRecord detail = rentalReturnRecordService.getOne(new LambdaQueryWrapper<TRentalReturnRecord>()
                .eq(TRentalReturnRecord::getId, rentalReturnRecord.getId()));
        return R.ok(detail);
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/utils/CodeGenerateUtils.java
@@ -72,5 +72,24 @@
        timestampPart = timestampPart.substring(0, 0);
        return dateTime + timestampPart;
    }
    /**
     * @return
     * @Description 生成统一支付单号  规则:年(2)月(2)日(2)时(2)分(2)+timestamp*5位随机整数取后5位
     * @Author xiaochen
     */
    public static String generateVolumeSn(String code) {
        Calendar calendar = Calendar.getInstance();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddhhmmss");
        String dateTime = dateFormat.format(calendar.getTime());
        dateTime = dateTime.substring(0,8);
        String timestampPart = "" + (Math.random() * 10000) * (System.currentTimeMillis() / 10000);
        timestampPart = timestampPart.replace(".", "").replace("E", "");
        timestampPart = timestampPart.substring(0, 6);
        return dateTime + code + timestampPart;
    }
    public static void main(String[] args) {
        System.err.println(generateVolumeSn("2"));
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/dto/TerminateContractDTO.java
@@ -1,5 +1,6 @@
package com.ruoyi.system.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@@ -20,6 +21,7 @@
    @ApiModelProperty(value = "终止日期")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime terminateTime;
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TBillMapper.java
@@ -10,6 +10,7 @@
import org.apache.ibatis.annotations.Param;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/**
@@ -52,5 +53,11 @@
     * 街道租金排行
     * @return
     */
    List<ScreenRentRankVO> getStreetRentRank();
    List<ScreenRentRankVO> getStreetRentRank(@Param("businessDeptId") String businessDeptId);
    /**
     * 查询季付账单
     * @param businessDeptId
     * @return
     */
    List<TBill> getJiFuBillList(@Param("businessDeptId") String businessDeptId, @Param("first") Date first, @Param("last") Date last);
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TContractMapper.java
@@ -35,7 +35,7 @@
     * 本月新增租户数
     * @return
     */
    Integer getCurrentMonthRentCount();
    Integer getCurrentMonthRentCount(@Param("businessDeptId") String businessDeptId);
    PageInfo<TContract> page(@Param("pageInfo") PageInfo<TContract> pageInfo,@Param("query") TContractQuery query);
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/THouseMapper.java
@@ -23,5 +23,10 @@
    List<THouse> houseList(@Param("req") THouseQuery query, @Param("pageInfo") PageInfo<THouse> pageInfo);
    List<HouseVO> userHistoryList(@Param("req")TUserHistoryQuery query, @Param("pageInfo")PageInfo<HouseVO> pageInfo);
    /**
     * 获取本季度已出租面积
     * @param businessDeptId
     * @return
     */
    Double getRentedArea(@Param("businessDeptId") String businessDeptId);
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TRentalReturnRecordMapper.java
New file
@@ -0,0 +1,18 @@
package com.ruoyi.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.ruoyi.system.model.TRentalReturnRecord;
import com.ruoyi.system.query.RentalReturnRecordQuery;
import com.ruoyi.system.vo.RentalReturnRecordVO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface TRentalReturnRecordMapper extends BaseMapper<TRentalReturnRecord> {
    IPage<RentalReturnRecordVO> queryRentalReturnRecordList(@Param("page") IPage<RentalReturnRecordVO> page,
                                                           @Param("query") RentalReturnRecordQuery query);
    RentalReturnRecordVO queryRentalReturnRecordById(@Param("id") String id);
}
ruoyi-system/src/main/java/com/ruoyi/system/model/TBill.java
@@ -1,24 +1,23 @@
package com.ruoyi.system.model;
import com.baomidou.mybatisplus.annotation.*;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
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;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.data.annotation.Transient;
import springfox.documentation.annotations.ApiIgnore;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
/**
 * <p>
@@ -184,6 +183,12 @@
    @TableField(exist = false)
    private BigDecimal preOutstand;
    @ApiModelProperty(value = "是否管理后台添加 1=是 0=否 用于生成合同附件时过滤管理后台添加的账单,查询首个账单")
    @TableField("is_manual_addition")
    private Integer manualAddition;
ruoyi-system/src/main/java/com/ruoyi/system/model/TContract.java
@@ -11,7 +11,6 @@
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@@ -126,7 +125,6 @@
    @ApiModelProperty(value = "合同附件,多个逗号拼接")
    @TableField("contract_file")
    @NotBlank(message = "合同附件不能为空")
    private String contractFile;
    @ApiModelProperty(value = "备注")
ruoyi-system/src/main/java/com/ruoyi/system/model/TFaultRepairMessage.java
@@ -119,4 +119,8 @@
    @TableField("status")
    private Integer status;
    @ApiModelProperty(value = "故障因素: 1-人为因素 2-市政因素")
    @TableField("fault_cause")
    private Integer faultCause;
}
ruoyi-system/src/main/java/com/ruoyi/system/model/THouse.java
@@ -81,6 +81,11 @@
    @ApiModelProperty(value = "租赁状态 1=待出租 2=已出租 3=维修中")
    @TableField("lease_status")
    private String leaseStatus;
    @ApiModelProperty(value = "退租状态 1=未退租 2=退租申请中 3=已退租")
    @TableField("rental_return_status")
    private String rentalReturnStatus;
    @ApiModelProperty(value = "楼栋")
    @TableField("building")
    private String building;
ruoyi-system/src/main/java/com/ruoyi/system/model/TRentalReturnRecord.java
New file
@@ -0,0 +1,85 @@
package com.ruoyi.system.model;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ruoyi.common.core.domain.BaseModel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
@EqualsAndHashCode(callSuper = true)
@Data
@TableName("t_rental_return_record")
@ApiModel("退租记录")
public class TRentalReturnRecord extends BaseModel {
    private static final long serialVersionUID = 1L;
    @TableId(value = "id", type = IdType.ASSIGN_ID)
    @ApiModelProperty(value = "退租记录id")
    private String id;
    /**
     * 租户id
     */
    @ApiModelProperty(value = "租户id")
    private String tenantId;
    /**
     * 房屋id
     */
    @ApiModelProperty(value = "房屋id")
    private String houseId;
    /**
     * 合同id
     */
    @ApiModelProperty(value = "合同id")
    private String contractId;
    /**
     * 图片
     */
    @ApiModelProperty(value = "图片")
    private String images;
    /**
     * 退租说明
     */
    @ApiModelProperty(value = "退租说明")
    private String reasonForReturn;
    /**
     * 审批意见
     */
    @ApiModelProperty(value = "审批意见")
    private String auditOpinion;
    /**
     * 审批时间
     */
    @ApiModelProperty(value = "审批时间")
    private LocalDateTime auditTime;
    /**
     * 审批状态: 1-待审核 2-审核通过 3-审核驳回
     */
    @ApiModelProperty(value = "审批状态: 1-待审核 2-审核通过 3-审核驳回")
    private Integer auditStatus;
    /**
     * 审批人id
     */
    @ApiModelProperty(value = "审批人id")
    private Long auditUserId;
    /**
     * 管理后台删除标识
     */
    @ApiModelProperty(value = "管理后台删除标识")
    private Boolean isAdminDelete;
}
ruoyi-system/src/main/java/com/ruoyi/system/model/TTenant.java
@@ -100,4 +100,8 @@
    @TableField("bank_name")
    private String bankName;
    @ApiModelProperty(value = "承租人")
    @TableField("lessee")
    private String lessee;
}
ruoyi-system/src/main/java/com/ruoyi/system/query/RentalReturnAuditQuery.java
New file
@@ -0,0 +1,27 @@
package com.ruoyi.system.query;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel("退租审核查询")
public class RentalReturnAuditQuery {
    /**
     * id
     */
    @ApiModelProperty(value = "id")
    private String id;
    /**
     * 审核结果:2-审核通过 3-审核驳回
     */
    @ApiModelProperty(value = "审核结果:2-审核通过 3-审核驳回")
    private Integer auditResult;
    /**
     * 审核意见
     */
    @ApiModelProperty(value = "审核意见")
    private String auditOpinion;
}
ruoyi-system/src/main/java/com/ruoyi/system/query/RentalReturnRecordQuery.java
New file
@@ -0,0 +1,51 @@
package com.ruoyi.system.query;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
@Data
@ApiModel("退租记录查询")
public class RentalReturnRecordQuery {
    /**
     * 申请开始日期
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty(value = "申请开始日期")
    private LocalDateTime startDate;
    /**
     * 申请结束日期
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty(value = "申请结束日期")
    private LocalDateTime endDate;
    /**
     * 房屋名称
     */
    @ApiModelProperty(value = "房屋名称")
    private String houseName;
    /**
     * 合同编号
     */
    @ApiModelProperty(value = "合同编号")
    private String contractNumber;
    /**
     * 提交人
     */
    @ApiModelProperty(value = "提交人")
    private String submitter;
    private String businessDeptId;
    private Boolean isAdminDelete;
}
ruoyi-system/src/main/java/com/ruoyi/system/service/ITRentalReturnRecordService.java
New file
@@ -0,0 +1,21 @@
package com.ruoyi.system.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.system.model.TRentalReturnRecord;
import com.ruoyi.system.query.RentalReturnAuditQuery;
import com.ruoyi.system.query.RentalReturnRecordQuery;
import com.ruoyi.system.vo.RentalRetureApplyVO;
import com.ruoyi.system.vo.RentalReturnRecordVO;
public interface ITRentalReturnRecordService extends IService<TRentalReturnRecord> {
    IPage<RentalReturnRecordVO> queryRentalReturnRecordList(Page<RentalReturnRecordVO> page, RentalReturnRecordQuery query);
    RentalReturnRecordVO queryRentalReturnRecordById(String id);
    void apply(RentalRetureApplyVO rentalReture,String tenantId);
    void audit(RentalReturnAuditQuery returnAuditQuery, Long userId, String userName);
}
ruoyi-system/src/main/java/com/ruoyi/system/service/TBillService.java
@@ -15,6 +15,7 @@
import javax.validation.constraints.NotEmpty;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.function.Consumer;
@@ -98,6 +99,23 @@
     * 查询街道租金排行
     * @return
     */
    List<ScreenRentRankVO> getStreetRentRank(String businessDeptId);
    /**
     * 查询季付账单
     * @param businessDeptId
     * @return
     */
    List<TBill> getJiFuBillList(String businessDeptId);
    /**
     * 查询当前季度的季付账单
     * @param businessDeptId
     * @param first
     * @param last
     * @return
     */
    List<TBill> getJiFuBillListByTime(String businessDeptId, Date first, Date last);
    List<ScreenRentRankVO> getStreetRentRank();
    void editAmount(TbillSaveDto bill);
ruoyi-system/src/main/java/com/ruoyi/system/service/TContractService.java
@@ -11,6 +11,7 @@
import com.ruoyi.system.query.TContractQuery;
import com.ruoyi.system.vo.BillVO;
import com.ruoyi.system.vo.CheckAcceptRecordVO;
import com.ruoyi.system.vo.ScreenRentIncomeTrendVO;
import java.util.List;
@@ -47,7 +48,6 @@
     * 本月新增租户数
     * @return
     */
    Integer getCurrentMonthRentCount();
    Integer getCurrentMonthRentCount(String businessDeptId);
    PageInfo<TContract> queryPage(TContractQuery query);
}
ruoyi-system/src/main/java/com/ruoyi/system/service/THouseService.java
@@ -20,4 +20,11 @@
    PageInfo<THouse> houseList(THouseQuery query);
    PageInfo<HouseVO> userHistoryList(TUserHistoryQuery query);
    /**
     * 获取本季度已出租面积
     * @param businessDeptId
     * @return
     */
    Double getRentedArea(String businessDeptId);
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/FlowListenerService.java
@@ -91,19 +91,22 @@
    private final THouseService houseService;
    public static void main(String[] args) {
        LocalDate localDate1 = LocalDate.now().withYear(2025).withMonth(1).withDayOfMonth(31);
        System.err.println(localDate1.plusMonths(1));
//        LocalDate localDate1 = LocalDate.now().withYear(2025).withMonth(2).withDayOfMonth(10).with;
//        System.err.println(localDate1.plusMonths(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);
//
//        LocalDateTime localDate1 = LocalDateTime.now().withYear(2025).withMonth(2).withDayOfMonth(10).withHour(00).withMinute(00).withSecond(00);
//        LocalDateTime localDate2 = LocalDateTime.now().withYear(2025).withMonth(3).withDayOfMonth(31).withHour(00).withMinute(00).withSecond(00);
//        LocalDateTime with = localDate1.plusMonths(1).with(TemporalAdjusters.lastDayOfMonth());
//        boolean before = with.isBefore(localDate2);
//        System.err.println(before);
//        long between = ChronoUnit.DAYS.between(localDate1, localDate2)+1;
        int monthValue = LocalDateTime.now().getMonthValue();
         LocalDateTime.now();
         LocalDateTime.now();
        System.err.println(LocalDateTime.now().isBefore(LocalDateTime.now()));
//        int monthValue = LocalDateTime.now().getMonthValue();
//         LocalDateTime.now();
//         LocalDateTime.now();
        int dayOfMonth = LocalDateTime.now().getMonthValue();
        System.err.println(dayOfMonth);
//        System.err.println(LocalDateTime.now().isBefore(LocalDateTime.now()));
    }
    @Override
@@ -269,6 +272,7 @@
    }
    /**
     * 业务状态变更
     */
@@ -303,6 +307,7 @@
                    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());
@@ -424,22 +429,41 @@
                            // 第一个月计算天
                            int dayOfMonth = rentBill.getStartTime().getDayOfMonth();
                            if (dayOfMonth == 1) {
                                System.err.println("第一笔账单 1号计算整月:");
                                money = money.add(contract.getMonthRent());
                            } else {
                                long allDays = ChronoUnit.DAYS.between(contract.getStartPayTime(), contract.getStartPayTime().with(TemporalAdjusters.lastDayOfMonth())) + 1;
                                long allDays = Math.abs(ChronoUnit.DAYS.between(rentBill.getStartTime(), rentBill.getStartTime().with(TemporalAdjusters.lastDayOfMonth())) + 1);
                                System.err.println("第一笔账单 计算天数"+allDays);
                                money =money.add(contract.getMonthRent().divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(allDays)));
                                System.err.println("第一笔账单 计算天数金额"+money);
                            }
                            // 后续
                            if (contract.getStartPayTime().getMonthValue()==3||contract.getStartPayTime().getMonthValue()==6||contract.getStartPayTime().getMonthValue()==9||contract.getStartPayTime().getMonthValue()==12){
                                System.err.println("后续账单 月为3 6 9 12金额"+money);
                                rentBill.setPayableFeesMoney(money);
                                rentBill.setOutstandingMoney(rentBill.getPayableFeesMoney());
                            }else{
                                LocalDateTime localDateTime = rentBill.getStartTime().plusMonths(1).with(TemporalAdjusters.lastDayOfMonth());
                                System.err.println("后续账单逻辑时间"+localDateTime);
                                while (true){
                                    if (localDateTime.isBefore(rentBill.getEndTime())){
                                    if (localDateTime.toLocalDate().isBefore(rentBill.getEndTime().toLocalDate())){
                                        System.err.println("后续while 在结束之前");
                                        money = money.add(contract.getMonthRent());
                                    }else{
                                        money = money.add(contract.getMonthRent().divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(ChronoUnit.DAYS.between(rentBill.getEndTime(),localDateTime.with(TemporalAdjusters.firstDayOfMonth()))+1)));
                                    }else if(localDateTime.toLocalDate().equals(rentBill.getEndTime().toLocalDate())){
                                        System.err.println("后续while 结束");
                                        money = money.add(contract.getMonthRent());
                                        break;
                                    }else {
                                        System.err.println("后续while 加一个月大于结束时间");
                                        if (localDateTime.with(TemporalAdjusters.firstDayOfMonth()).isBefore(rentBill.getEndTime())){
                                            long a = ChronoUnit.DAYS.between(localDateTime.with(TemporalAdjusters.firstDayOfMonth()),rentBill.getEndTime())+1;
                                            System.err.println("后续while 加一个月大于结束时间 计算天数"+a);
                                            money = money.add(contract.getMonthRent().divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(
                                                    new BigDecimal(ChronoUnit.DAYS.between(localDateTime.with(TemporalAdjusters.firstDayOfMonth()),rentBill.getEndTime())+1))
                                            );
                                        }
                                        break;
                                    }
                                    localDateTime = localDateTime.plusMonths(1).with(TemporalAdjusters.lastDayOfMonth());
@@ -502,6 +526,8 @@
                    depositBill.setBillType("2");
                    contractService.updateById(contract);
                    rentBill.setBusinessDeptId(contract.getBusinessDeptId());
                    depositBill.setBusinessDeptId(contract.getBusinessDeptId());
                    billService.save(rentBill);
                    billService.save(depositBill);
                    // 生成后续账单
@@ -721,6 +747,7 @@
                                tBill.setContractNumber(contract.getContractNumber());
                                tBill.setPayFeesStatus("1");
                                tBill.setBillType("1");
                                tBill.setBusinessDeptId(contract.getBusinessDeptId());
                                billService.save(tBill);
                                beforeBill.setEndTime(beforeBill.getEndTime().plusMonths(contract.getPayType().equals("1") ? 1 : contract.getPayType().equals("2") ? 3 : 12).with(TemporalAdjusters.lastDayOfMonth()));
@@ -929,10 +956,16 @@
                                BigDecimal originalMoney = new BigDecimal("0");
                                // 不需要涨租金的时间段
                                long originalDays = 0;
                                if (tBill.getStartTime().with(TemporalAdjusters.lastDayOfMonth()).isBefore(tBill.getEndTime())){
                                    originalDays = Math.abs(ChronoUnit.DAYS.between(tBill.getStartTime(), tBill.getStartTime().with(TemporalAdjusters.lastDayOfMonth())));
                                if (tBill.getStartTime().getDayOfMonth()==1&&(tBill.getStartTime().with(TemporalAdjusters.lastDayOfMonth()).toLocalDate().isAfter(tBill.getEndTime().toLocalDate())||tBill.getStartTime().with(TemporalAdjusters.lastDayOfMonth()).toLocalDate().equals(tBill.getEndTime().toLocalDate()))){
                                    // 计算整月
                                    originalMoney = originalMoney.add(contract.getChangeRent());
                                }else{
                                    originalDays = Math.abs(ChronoUnit.DAYS.between(tBill.getStartTime(), tBill.getEndTime()));
                                    // 计算天
                                    if (tBill.getStartTime().with(TemporalAdjusters.lastDayOfMonth()).isBefore(tBill.getEndTime())){
                                        originalDays = Math.abs(ChronoUnit.DAYS.between(tBill.getStartTime(), tBill.getStartTime().with(TemporalAdjusters.lastDayOfMonth())))+1;
                                    }else{
                                        originalDays = Math.abs(ChronoUnit.DAYS.between(tBill.getStartTime(), tBill.getEndTime()))+1;
                                    }
                                }
                                originalMoney = originalMoney.add(contract.getChangeRent().divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(originalDays)));
                                LocalDateTime originalTime = tBill.getStartTime().with(TemporalAdjusters.lastDayOfMonth()).plusDays(1);
@@ -957,12 +990,24 @@
                            BigDecimal originalMoney = new BigDecimal("0");
                            // 不需要涨租金的时间段
                            long originalDays = 0;
                            if (tBill.getStartTime().with(TemporalAdjusters.lastDayOfMonth()).isBefore(tBill.getEndTime())){
                                originalDays = Math.abs(ChronoUnit.DAYS.between(tBill.getStartTime(), tBill.getStartTime().with(TemporalAdjusters.lastDayOfMonth())))+1;
                            if (tBill.getStartTime().getDayOfMonth()==1
                                    &&
                                    (
                                            tBill.getStartTime().with(TemporalAdjusters.lastDayOfMonth()).toLocalDate().isBefore(tBill.getEndTime().toLocalDate())
                                            ||tBill.getStartTime().with(TemporalAdjusters.lastDayOfMonth()).toLocalDate().equals(tBill.getEndTime().toLocalDate())
                                    )
                            ){
                                // 计算整月
                                originalMoney = originalMoney.add(contract.getChangeRent());
                            }else{
                                originalDays = Math.abs(ChronoUnit.DAYS.between(tBill.getStartTime(), tBill.getEndTime()))+1;
                                // 计算天
                                if (tBill.getStartTime().with(TemporalAdjusters.lastDayOfMonth()).isBefore(tBill.getEndTime())){
                                    originalDays = Math.abs(ChronoUnit.DAYS.between(tBill.getStartTime(), tBill.getStartTime().with(TemporalAdjusters.lastDayOfMonth())))+1;
                                }else{
                                    originalDays = Math.abs(ChronoUnit.DAYS.between(tBill.getStartTime(), tBill.getEndTime()))+1;
                                }
                                originalMoney = originalMoney.add(contract.getChangeRent().divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(originalDays)));
                            }
                            originalMoney = originalMoney.add(contract.getChangeRent().divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(originalDays)));
                            LocalDateTime originalTime = tBill.getStartTime().with(TemporalAdjusters.lastDayOfMonth()).plusDays(1);
                            while(true){
                                if (originalTime.isBefore(tBill.getEndTime())){
@@ -976,21 +1021,22 @@
                                    break;
                                }
                            }
                            if (originalTime.isBefore(tBill.getEndTime())){
                                long tempOriginal  = ChronoUnit.DAYS.between(originalTime,tBill.getEndTime());
                            if (originalTime.isBefore(tBill.getEndTime())||originalTime.toLocalDate().equals(tBill.getEndTime().toLocalDate())){
                                long tempOriginal  = ChronoUnit.DAYS.between(originalTime,tBill.getEndTime())+1;
                                originalMoney = originalMoney.add(contract.getChangeRent().divide(new BigDecimal(30), 2, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(tempOriginal)));
                            }
                            tBill.setPayableFeesMoney(originalMoney);
                            tBill.setOutstandingMoney(tBill.getPayableFeesMoney());
                        }
                        if (tBill.getEndTime().getDayOfMonth() >= 15) {
//                        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());
                        } 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());
                        }
//                        }
                        tBill.setBusinessDeptId(contract.getBusinessDeptId());
                        billService.save(tBill);
                    }
                }
@@ -998,13 +1044,17 @@
            }
            case CATEGORY3: {
                // 合同提前终止审批
                int submitStatus = status == 0 ? 4 : (status == 1 ? 7 : 5);
                int submitStatus = status == 0 ? 4 : (status == 1 ? 7 : 4);
                contractService.updateContractAuditStatus(processParameter.getString("projectId"), submitStatus);
                // 生成验收记录
                TContract contract = contractService.getById(processParameter.getString("projectId"));
                //更新合同结束时间
                contract.setEndTime(contract.getTerminateTime());
                contractService.updateById(contract);
                // 修改房屋状态
                THouse house = houseService.getById(contract.getHouseId());
                house.setLeaseStatus("1");
                houseService.updateById(house);
                TCheckAcceptRecord tCheckAcceptRecord = new TCheckAcceptRecord();
                tCheckAcceptRecord.setContractId(contract.getId());
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ScreenService.java
@@ -1,6 +1,9 @@
package com.ruoyi.system.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.system.model.TBill;
import com.ruoyi.system.model.TContract;
import com.ruoyi.system.model.THouse;
@@ -10,6 +13,7 @@
import com.ruoyi.system.vo.ScreenRentIncomeTrendVO;
import com.ruoyi.system.vo.ScreenRentRankVO;
import com.ruoyi.system.vo.ScreenTopStaticsDataVO;
import com.ruoyi.system.vo.TenantCountTrendVO;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
@@ -33,57 +37,81 @@
    private final THouseService tHouseService;
    private final TContractService tContractService;
    private final TBillService tBillService;
    private final TContractService contractService;
    /**
     * 获取顶部统计数据
     * @return
     */
    public ScreenTopStaticsDataVO getTopStaticsData() {
        ScreenTopStaticsDataVO vo = new ScreenTopStaticsDataVO();
        //房屋总面积
        List<THouse> houseList = tHouseService.list();
        Double totalArea = houseList.stream().map(item -> Double.parseDouble(item.getHouseArea())).reduce(0D, Double::sum);
        vo.setHouseTotalArea(totalArea);
        //已出租面积
        Double totalRentedArea = houseList.stream().filter(item -> !item.getLeaseStatus().equals("1"))
                .map(item -> Double.parseDouble(item.getHouseArea())).reduce(0D, Double::sum);
        vo.setHouseRentedArea(totalRentedArea);
        //总计应收租金
        List<TBill> billList = tBillService.list();
        BigDecimal totalReceivableRent = billList.stream().filter(item -> !item.getPayFeesStatus().equals("5"))
                .map(TBill::getPayableFeesMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
        vo.setTotalReceivableRent(totalReceivableRent);
        //总计已收租金
        BigDecimal totalReceivedRent = billList.stream().map(TBill::getPayFeesMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
        vo.setTotalReceivedRent(totalReceivedRent);
        //本月新增租户数
        Integer newTenantCount = tContractService.getCurrentMonthRentCount();
        vo.setNewTenantCount(newTenantCount);
        //总计租户数 系统租户列表里有生效合同绑定的租户总数。
        Long count = tContractService.lambdaQuery().in(TContract::getStatus, "4", "5", "6", "7", "8", "9").groupBy(TContract::getTenantId).count();
        vo.setTotalTenantCount(count.intValue());
        String businessDeptId = SecurityUtils.getBusinessDeptId();
        Map<String, Date> quarterDate = DateUtils.getQuarterDate(new Date());
        Date first = quarterDate.get("first");
        Date last = quarterDate.get("last");
        List<TBill> currentQuarterBillList = tBillService.lambdaQuery().between(TBill::getPayableFeesTime, first, last).list();
        ScreenTopStaticsDataVO vo = new ScreenTopStaticsDataVO();
        //房屋总面积
        List<THouse> houseList = tHouseService.lambdaQuery()
                .eq(!businessDeptId.equals("0"), THouse::getBusinessDeptId, businessDeptId)
                .list();
        Double totalArea = houseList.stream()
                .map(item -> Double.parseDouble(item.getHouseArea()))
                .reduce(0D, Double::sum);
        vo.setHouseTotalArea(totalArea);
        //已出租面积
        Double totalRentedArea =  tHouseService.getRentedArea(businessDeptId);
        vo.setHouseRentedArea(totalRentedArea);
        //总计应收租金
        List<TBill> billList = tBillService.getJiFuBillList(businessDeptId);
        BigDecimal totalReceivableRent = billList.stream()
                .filter(item -> !item.getPayFeesStatus().equals("5"))
                .map(TBill::getPayableFeesMoney)
                .reduce(BigDecimal.ZERO, BigDecimal::add)
                .divide(new BigDecimal("10000"),2, RoundingMode.DOWN);
        vo.setTotalReceivableRent(totalReceivableRent);
        //总计已收租金
        BigDecimal totalReceivedRent = billList.stream()
                .map(TBill::getPayFeesMoney)
                .reduce(BigDecimal.ZERO, BigDecimal::add)
                .divide(new BigDecimal("10000"),2, RoundingMode.DOWN);
        vo.setTotalReceivedRent(totalReceivedRent);
        //本月新增租户数
        Integer newTenantCount = tContractService.getCurrentMonthRentCount(businessDeptId);
        vo.setNewTenantCount(newTenantCount);
        //总计租户数 系统租户列表里有生效合同绑定的租户总数。
        List<TContract> tContracts = tContractService.lambdaQuery()
                .eq(TContract::getPayType,2) //季付
                .in(TContract::getStatus, "4", "5", "6", "7", "8", "9")
                .eq(!businessDeptId.equals("0"), TContract::getBusinessDeptId, businessDeptId)
                .list();
        long count = tContracts.stream()
                .map(TContract::getTenantId)
                .distinct()
                .count();
        vo.setTotalTenantCount((int) count);
        List<TBill> currentQuarterBillList = tBillService.getJiFuBillListByTime(businessDeptId, first, last);
        //本季度已交租金
        BigDecimal totalRentPaid = currentQuarterBillList.stream().map(TBill::getPayFeesMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal totalRentPaid = currentQuarterBillList.stream()
                .map(TBill::getPayFeesMoney)
                .reduce(BigDecimal.ZERO, BigDecimal::add)
                .divide(new BigDecimal("10000"),2, RoundingMode.DOWN);
        vo.setTotalRentPaid(totalRentPaid);
        //本季度应交租金
        BigDecimal totalRentShould = currentQuarterBillList.stream().filter(item -> !item.getPayFeesStatus().equals("5"))
                .map(TBill::getPayableFeesMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal totalRentShould = currentQuarterBillList.stream()
                .filter(item -> !item.getPayFeesStatus().equals("5"))
                .map(TBill::getPayableFeesMoney)
                .reduce(BigDecimal.ZERO, BigDecimal::add)
                .divide(new BigDecimal("10000"),2, RoundingMode.DOWN);
        vo.setTotalRentShould(totalRentShould);
        //本季度欠费
        BigDecimal totalRentOwe = currentQuarterBillList.stream()
                .map(TBill::getOutstandingMoney)
                .reduce(BigDecimal.ZERO, BigDecimal::add)
                .divide(BigDecimal.valueOf(10000L), 2, RoundingMode.HALF_UP);
                .divide(BigDecimal.valueOf(10000L), 2, RoundingMode.DOWN);
        vo.setTotalRentOwe(totalRentOwe);
        //总计欠费
        List<TBill> allBillList = tBillService.lambdaQuery().le(TBill::getPayableFeesTime, last).list();
        BigDecimal totalRentOweAll = allBillList.stream()
        BigDecimal totalRentOweAll = billList.stream()
                .map(TBill::getOutstandingMoney)
                .reduce(BigDecimal.ZERO, BigDecimal::add)
                .divide(BigDecimal.valueOf(10000L), 2, RoundingMode.HALF_UP);
                .divide(BigDecimal.valueOf(10000L), 2, RoundingMode.DOWN);
        vo.setTotalRentOweAll(totalRentOweAll);
        return vo;
    }
@@ -93,7 +121,8 @@
     * @return
     */
    public List<ScreenRentRankVO> streetRentRank() {
        return  tBillService.getStreetRentRank();
        String businessDeptId = SecurityUtils.getBusinessDeptId();
        return  tBillService.getStreetRentRank(businessDeptId);
    }
    /**
@@ -102,7 +131,7 @@
     */
    public ScreenRentIncomeTrendVO rentIncomeTrend() {
        ScreenRentIncomeTrendVO vo = new ScreenRentIncomeTrendVO();
        String businessDeptId = SecurityUtils.getBusinessDeptId();
        // 获取当前日期
        Date currentDate = new Date();
        List<String> quarterLabels = new ArrayList<>(); // 季度标签列表
@@ -118,23 +147,21 @@
            Date quarterEnd = quarterDate.get("last");
            // 获取该季度的账单数据
            List<TBill> quarterBills = tBillService.lambdaQuery()
                    .between(TBill::getPayableFeesTime, quarterStart, quarterEnd)
                    .list();
            List<TBill> quarterBills = tBillService.getJiFuBillListByTime(businessDeptId, quarterStart, quarterEnd);
            // 计算季度租金收入总和
            BigDecimal quarterIncome = quarterBills.stream()
                    .map(TBill::getPayFeesMoney)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add)
                    .divide(BigDecimal.valueOf(10000L), 2, RoundingMode.HALF_UP);
                    .divide(BigDecimal.valueOf(10000L), 2, RoundingMode.DOWN);
            // 计算季度欠费总和
            BigDecimal quarterOutstanding = quarterBills.stream()
                    .map(TBill::getOutstandingMoney)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add)
                    .divide(BigDecimal.valueOf(10000L), 2, RoundingMode.HALF_UP);
                    .divide(BigDecimal.valueOf(10000L), 2, RoundingMode.DOWN);
            // 生成季度标签 (格式: YY-MM月)
            Calendar cal = Calendar.getInstance();
@@ -154,6 +181,65 @@
        return vo;
    }
    public R<List<TenantCountTrendVO>> getTenantCountTrend() {
        // 获取当前日期
        Date currentDate = new Date();
        String businessDeptId = SecurityUtils.getBusinessDeptId();
        List<TenantCountTrendVO> list = new ArrayList<>();
        for (int i = 6; i >= 0; i--) {
            Date targetDate = DateUtils.addMonths(currentDate, -3 * i);
            Map<String, Date> quarterDate = DateUtils.getQuarterDate(targetDate);
            Date quarterStart = quarterDate.get("first");
            Date quarterEnd = quarterDate.get("last");
            System.out.println("第" + (i + 1) + "季度的起止时间:" + quarterStart + " - " + quarterEnd);
            List<TContract> contracts = contractService.list(new LambdaQueryWrapper<TContract>()
                    .eq(!"0".equals(businessDeptId), TContract::getBusinessDeptId, businessDeptId)
                    .eq(TContract::getPayType, 2)
                    .isNotNull(TContract::getSignTime)
                    .between(TContract::getSignTime, quarterStart, quarterEnd)
                    .orderByAsc(TContract::getSignTime));
            TenantCountTrendVO vo = new TenantCountTrendVO();
            // 生成季度标签 (格式: YY-MM月)
            Calendar cal = Calendar.getInstance();
            cal.setTime(quarterEnd);
            String label = String.format("%02d-%d月",
                    cal.get(Calendar.YEAR) % 100,
                    cal.get(Calendar.MONTH) + 1);
            vo.setDate(label);
            long count = contracts.stream().map(TContract::getTenantId).distinct().count();
            vo.setCount(count);
            list.add(vo);
        }
        return R.ok(list);
    }
//
//
//
//
//
//
//
//        DateTimeFormatter quarterFormatter = DateTimeFormatter.ofPattern("yyyy-MM");
//
//        List<TenantCountTrendVO> trendData = contracts.stream()
//                .collect(Collectors.groupingBy(contract -> {
//                    LocalDate date = contract.getSignTime().toLocalDate();
//                    int quarter = (date.getMonthValue() - 1) / 3 + 1;
//                    return YearQuarter.from(date.withMonth(quarter * 3 - 2));
//                }, TreeMap::new, Collectors.counting()))
//                .entrySet().stream()
//                .map(entry -> new TenantCountTrendVO(
//                        entry.getKey().format(quarterFormatter),
//                        entry.getValue()))
//                .collect(Collectors.toList());
//
//        return R.ok(trendData);
//    }
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/StateProcessTemplateServiceImpl.java
@@ -580,6 +580,13 @@
        if (processTaskListBO.getSortBy() == 2) {
            stateTaskQuery.orderByDesc(BaseModel::getCreateTime);
        }
        // 查询合同信息
        List<TContract> tContracts = contractMapper.selectList(Wrappers.<TContract>lambdaQuery());
        List<String> contractIds = tContracts.stream().map(TContract::getId).collect(Collectors.toList());
        if(CollectionUtils.isEmpty(contractIds)){
            return new PageInfo<>();
        }
        stateTaskQuery.in(StateTaskCenter::getProjectId, contractIds);
        stateTaskQuery.in(StateTaskCenter::getFlowId, instanceIds);
        PageInfo pageInfo = new PageInfo(processTaskListBO.getPageNum(), processTaskListBO.getPageSize());
        PageInfo<StateTaskCenter> taskCenters = stateTaskCenterService.page(pageInfo,stateTaskQuery);
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TBillServiceImpl.java
@@ -5,6 +5,7 @@
import com.ruoyi.common.constant.AmountConstant;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.DisabledEnum;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.*;
import com.ruoyi.common.utils.uuid.UUID;
@@ -251,6 +252,7 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveBill(TbillSaveDto bill) {
        bill.setManualAddition(DisabledEnum.YES.getCode());
        save(bill);
        if (bill.getBillType().equals("3")){
            if (bill.getDetails()==null || bill.getDetails().size()==0){
@@ -590,8 +592,29 @@
     * @return
     */
    @Override
    public List<ScreenRentRankVO> getStreetRentRank() {
        return baseMapper.getStreetRentRank();
    public List<ScreenRentRankVO> getStreetRentRank(String businessDeptId) {
        return baseMapper.getStreetRentRank(businessDeptId);
    }
    /**
     * 查询季付账单
     * @param businessDeptId
     * @return
     */
    @Override
    public List<TBill> getJiFuBillList(String businessDeptId) {
        return baseMapper.getJiFuBillList(businessDeptId,null,null);
    }
    /**
     * 查询当前季度的季付账单
     * @param businessDeptId
     * @param first
     * @param last
     * @return
     */
    @Override
    public List<TBill> getJiFuBillListByTime(String businessDeptId, Date first, Date last) {
        return baseMapper.getJiFuBillList(businessDeptId,first,last);
    }
    @Override
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TContractServiceImpl.java
@@ -31,6 +31,7 @@
import com.ruoyi.system.service.TContractService;
import com.ruoyi.system.vo.BillVO;
import com.ruoyi.system.vo.CheckAcceptRecordVO;
import com.ruoyi.system.vo.ScreenRentIncomeTrendVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -92,7 +93,9 @@
        PageInfo<TContract> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize());
        List<TContract> list = this.baseMapper.contractAppletList(query,pageInfo);
        for (TContract tContract : list) {
            tContract.setPayType(DictUtils.getDictLabel(DictConstants.DICT_TYPE_CONTRACT_PAY_TYPE,tContract.getPayType()));
            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())
@@ -122,6 +125,7 @@
    public void terminateContract(TerminateContractDTO dto) {
        TContract contract = this.baseMapper.selectById(dto.getId());
        contract.setTerminateRemark(dto.getTerminateRemark());
        contract.setTerminateTime(dto.getTerminateTime());
        this.baseMapper.updateById(contract);
        // 进入合同提前终止审批流程
        ProcessStartBO processStartBO = new ProcessStartBO();
@@ -209,8 +213,8 @@
     * @return
     */
    @Override
    public Integer getCurrentMonthRentCount() {
        return baseMapper.getCurrentMonthRentCount();
    public Integer getCurrentMonthRentCount(String businessDeptId) {
        return baseMapper.getCurrentMonthRentCount(businessDeptId);
    }
    @Override
@@ -219,4 +223,5 @@
        pageInfo = this.baseMapper.page(pageInfo,query);
        return pageInfo;
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/THouseServiceImpl.java
@@ -54,7 +54,12 @@
                tHouse.setLeaseStatus(DictUtils.getDictLabel(DictConstants.DICT_TYPE_LEASE_STATUS,"2"));
            }else{
                tHouse.setLeaseStatus(DictUtils.getDictLabel(DictConstants.DICT_TYPE_LEASE_STATUS,tHouse.getLeaseStatus()));
                if (tHouse.getLeaseStatus().equals("3")){
                    tHouse.setLeaseStatus(DictUtils.getDictLabel(DictConstants.DICT_TYPE_LEASE_STATUS,tHouse.getLeaseStatus()));
                }else{
                    tHouse.setLeaseStatus(DictUtils.getDictLabel(DictConstants.DICT_TYPE_LEASE_STATUS,"1"));
                }
            }
        }
@@ -73,4 +78,14 @@
        pageInfo.setRecords(list);
        return pageInfo;
    }
    /**
     * 获取本季度已出租面积
     *
     * @param businessDeptId
     * @return
     */
    @Override
    public Double getRentedArea(String businessDeptId) {
        return baseMapper.getRentedArea(businessDeptId);
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TRentalReturnRecordServiceImpl.java
New file
@@ -0,0 +1,121 @@
package com.ruoyi.system.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.model.LoginUserApplet;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.enums.ProcessCategoryEnum;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.system.bo.ProcessStartBO;
import com.ruoyi.system.mapper.TRentalReturnRecordMapper;
import com.ruoyi.system.model.TContract;
import com.ruoyi.system.model.THouse;
import com.ruoyi.system.model.TRentalReturnRecord;
import com.ruoyi.system.query.RentalReturnAuditQuery;
import com.ruoyi.system.query.RentalReturnRecordQuery;
import com.ruoyi.system.service.ITRentalReturnRecordService;
import com.ruoyi.system.service.StateProcessTemplateService;
import com.ruoyi.system.service.TContractService;
import com.ruoyi.system.service.THouseService;
import com.ruoyi.system.vo.RentalRetureApplyVO;
import com.ruoyi.system.vo.RentalReturnRecordVO;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
@Service
@RequiredArgsConstructor(onConstructor_ = @__(@Lazy))
public class TRentalReturnRecordServiceImpl extends ServiceImpl<TRentalReturnRecordMapper, TRentalReturnRecord>
        implements ITRentalReturnRecordService {
    private final TContractService contractService;
    private final StateProcessTemplateService stateProcessTemplateService;
    private final THouseService houseService;
    @Override
    public IPage<RentalReturnRecordVO> queryRentalReturnRecordList(Page<RentalReturnRecordVO> page, RentalReturnRecordQuery query) {
        String businessDeptId = SecurityUtils.getBusinessDeptId();
        query.setBusinessDeptId(businessDeptId);
        query.setIsAdminDelete(true);
        return this.baseMapper.queryRentalReturnRecordList(page, query);
    }
    @Override
    public RentalReturnRecordVO queryRentalReturnRecordById(String id) {
        return this.baseMapper.queryRentalReturnRecordById(id);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void apply(RentalRetureApplyVO rentalReture,String tenantId) {
        TContract contract = contractService.getById(rentalReture.getContractId());
        if (contract == null) {
            throw new ServiceException("该房屋暂无合同");
        }
        TRentalReturnRecord tRentalReturnRecord = new TRentalReturnRecord();
        tRentalReturnRecord.setTenantId(tenantId);
        tRentalReturnRecord.setHouseId(contract.getHouseId());
        tRentalReturnRecord.setContractId(contract.getId());
        tRentalReturnRecord.setReasonForReturn(rentalReture.getReasonForReturn());
        tRentalReturnRecord.setImages(rentalReture.getImages());
        tRentalReturnRecord.setAuditStatus(1);
        save(tRentalReturnRecord);
        boolean update = houseService.update(new LambdaUpdateWrapper<THouse>()
                .set(THouse::getRentalReturnStatus, "2")
                .eq(THouse::getId, contract.getHouseId()));
        if (!update){
            throw new ServiceException("更新房屋状态失败");
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void audit(RentalReturnAuditQuery returnAuditQuery, Long userId,String userName) {
        TRentalReturnRecord record = getById(returnAuditQuery.getId());
        if (record == null){
            throw new ServiceException("申请记录不存在");
        }
        if (record.getAuditStatus() != 1){
            throw new ServiceException("申请记录已审核");
        }
        THouse house = houseService.getById(record.getHouseId());
        if (returnAuditQuery.getAuditResult() == 2){
            TContract contract = contractService.getById(record.getContractId());
            contract.setTerminateRemark(returnAuditQuery.getAuditOpinion());
            contractService.updateById(contract);
            // 进入合同提前终止审批流程
            ProcessStartBO processStartBO = new ProcessStartBO();
            processStartBO.setCategory(ProcessCategoryEnum.CATEGORY3.getValue().toString());
            processStartBO.setModuleName("房屋退租合同终止审批");
            processStartBO.setName(contract.getContractName());
            processStartBO.setRemark("");
            Map<String, Object> variable = new HashMap<>();
            variable.put("projectId", record.getContractId());
            processStartBO.setVariable(variable);
            //开启工作流程
            stateProcessTemplateService.start(processStartBO);
            house.setRentalReturnStatus("3");
            houseService.updateById(house);
        }else {
            house.setRentalReturnStatus("1");
            houseService.updateById(house);
        }
        record.setAuditTime(LocalDateTime.now());
        record.setAuditStatus(returnAuditQuery.getAuditResult());
        record.setAuditOpinion(returnAuditQuery.getAuditOpinion());
        record.setAuditUserId(userId);
        record.setUpdateBy(userName);
        record.setUpdateTime(LocalDateTime.now());
        updateById(record);
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TTenantServiceImpl.java
@@ -6,7 +6,6 @@
import com.ruoyi.common.basic.PageInfo;
import com.ruoyi.common.constant.DictConstants;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.dto.TTenantDTO;
import com.ruoyi.system.mapper.TContractMapper;
@@ -49,14 +48,15 @@
    @Override
    public PageInfo<TenantVO> pageList(TTenantQuery query) {
        PageInfo<TenantVO> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize());
        String businessDeptId = SecurityUtils.getBusinessDeptId();
 /*       String businessDeptId = SecurityUtils.getBusinessDeptId();
        List<TenantVO> list = null;
        if (StringUtils.isBlank(businessDeptId) || "0".equals(businessDeptId)) {
            list = this.baseMapper.pageList(query, pageInfo);
        } else {
            query.setBusinessDeptId(businessDeptId);
            list = this.baseMapper.pageListByBusinessDeptId(query,pageInfo);
        }
        }*/
        List<TenantVO> list = this.baseMapper.pageList(query, pageInfo);
        for (TenantVO tenantVO : list) {
            tenantVO.setTenantAttributesName(StringUtils.isNotBlank(tenantVO.getTenantAttributes())?DictUtils.getDictLabel(DictConstants.DICT_TYPE_TENANT_ATTRIBUTE,tenantVO.getTenantAttributes()):"");
            tenantVO.setTenantTypeName(StringUtils.isNotBlank(tenantVO.getTenantType())?DictUtils.getDictLabel(DictConstants.DICT_TYPE_TENANT_TYPE,tenantVO.getTenantType()):"");
ruoyi-system/src/main/java/com/ruoyi/system/task/utils/TaskUtil.java
@@ -2,6 +2,9 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.uuid.UUID;
import com.ruoyi.system.mapper.TBillMapper;
import com.ruoyi.system.model.TBill;
import com.ruoyi.system.model.TContract;
@@ -27,6 +30,8 @@
    private TContractService contractService;
    @Autowired
    private TBillMapper billMapper;
    @Autowired
    RedisCache redisCache;
    // 用于更新违约金账单
    // 每分钟执行一次的定时任务
@@ -50,18 +55,26 @@
                    // 违约金比例
                    BigDecimal proportion = contract.getProportion();
                    // 按每天 待缴费金额 * XX% 增加违约金费用
                    if (tBill.getOutstandingMoney().compareTo(new BigDecimal("0"))==0){
                    if (contract.getTotalYear().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);
                    String requestId = UUID.fastUUID().toString();
                    String lockkey = CacheConstants.BILL_UPDATE_LOCK_KEY + tBill.getId();
                    boolean isok = redisCache.trylockLoop(lockkey, requestId, 60);
                    if (isok){
                        try {
                            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);
                        }finally {
                            redisCache.unlock(lockkey,requestId);
                        }
                    }
                }
            }
        } catch (Exception e) {
ruoyi-system/src/main/java/com/ruoyi/system/vo/HouseMapDistributionVO.java
@@ -15,7 +15,7 @@
    @ApiModelProperty(value = "房屋地址")
    private String houseAddress;
    @ApiModelProperty(value = "房屋状态 1=待出租 2=已出租 3=维修中")
    @ApiModelProperty(value = "房屋状态 1=待出租 2=已出租 3=维修中 4=欠费")
    private String houseStatus;
    @ApiModelProperty(value = "租户")
ruoyi-system/src/main/java/com/ruoyi/system/vo/MyHouseVO.java
@@ -1,5 +1,6 @@
package com.ruoyi.system.vo;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.system.model.TBill;
import io.swagger.annotations.ApiModel;
@@ -35,6 +36,8 @@
    private String propertyRightPerson;
    @ApiModelProperty(value = "房东联系方式")
    private String phone;
    @ApiModelProperty(value = "退租状态 1=未退租 2=退租申请中 3=已退租")
    private String rentalReturnStatus;
    @ApiModelProperty(value = "交租记录")
    private List<PayListVO> payList;
}
ruoyi-system/src/main/java/com/ruoyi/system/vo/RentalRetureApplyVO.java
New file
@@ -0,0 +1,30 @@
package com.ruoyi.system.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel("退租申请")
public class RentalRetureApplyVO {
    /**
     * 合同id
     */
    @ApiModelProperty(value = "合同id")
    private String contractId;
    /**
     * 退租说明
     */
    @ApiModelProperty(value = "退租说明")
    private String reasonForReturn;
    /**
     * 图片
     */
    @ApiModelProperty(value = "图片")
    private String images;
}
ruoyi-system/src/main/java/com/ruoyi/system/vo/RentalReturnRecordVO.java
New file
@@ -0,0 +1,90 @@
package com.ruoyi.system.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
@Data
@ApiModel("退租记录")
public class RentalReturnRecordVO {
    /**
     * 退租记录id
     */
    @ApiModelProperty(value = "退租记录id")
    private String id;
    /**
     * 提交时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty(value = "提交时间")
    private LocalDateTime applicationDate;
    /**
     * 房屋名称
     */
    @ApiModelProperty(value = "房屋名称")
    private String houseName;
    /**
     * 房屋地址
     */
    @ApiModelProperty(value = "房屋地址")
    private String houseAddress;
    /**
     * 租赁合同编号
     */
    @ApiModelProperty(value = "租赁合同编号")
    private String contractNumber;
    /**
     * 图片
     */
    @ApiModelProperty(value = "图片")
    private String images;
    /**
     * 提交人
     */
    @ApiModelProperty(value = "提交人")
    private String submitter;
    /**
     * 联系方式
     */
    @ApiModelProperty(value = "联系方式")
    private String submitterPhone;
    /**
     * 退租说明
     */
    @ApiModelProperty(value = "退租说明")
    private String reasonForReturn;
    /**
     * 验收状态
     */
    @ApiModelProperty(value = "验收状态: 1-待审核 2-审核通过 3-审核驳回")
    private String auditStatus;
    /**
     * 提交时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty(value = "提交时间")
    private LocalDateTime createTime;
    /**
     * 审批意见
     */
    @ApiModelProperty(value = "审批意见")
    private String auditOpinion;
}
ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
@@ -225,7 +225,8 @@
        select u.user_id AS userId, u.dept_id AS deptId, u.user_name AS userName, u.nick_name AS nickName, u.email AS email, u.avatar AS avatar,u.disable_remark AS disableRemark,
               u.phonenumber AS phonenumber, u.sex AS sex, u.status AS status, u.del_flag AS delFlag, u.login_ip AS loginIp,u.operating_time AS operatingTime,u.operating_person AS operatingPerson,
               u.login_date AS loginDate, u.create_by AS createBy, u.create_time AS createTime, u.remark AS remark,u.ifBlack AS ifBlack, u.districtId AS districtId,
               r.role_id AS roleId, r.role_name AS roleName, r.role_key AS roleKey, r.role_sort AS roleSort, r.data_scope AS dataScope, r.status as role_status,u.deptName as deptName
               r.role_id AS roleId, r.role_name AS roleName, r.role_key AS roleKey, r.role_sort AS roleSort, r.data_scope AS dataScope, r.status as role_status,u.deptName as deptName,
               u.business_dept_id AS businessDeptId
        from sys_user u
        left join sys_user_role ur on u.user_id = ur.user_id
        left join sys_role r on r.role_id = ur.role_id
@@ -249,7 +250,7 @@
                #{roleId}
            </foreach>
        </if>
        <if test="query.businessDeptId != null and query.businessDeptId != '' and query.businessDeptId != '0'">
        <if test="query.businessDeptId != null and query.businessDeptId != '' and query.businessDeptId != 0">
            AND u.business_dept_id = #{query.businessDeptId}
        </if>
        ORDER BY u.create_time DESC
@@ -275,6 +276,7 @@
             <if test="remark != null and remark != ''">remark,</if>
             <if test="ifBlack != null">ifBlack,</if>
             <if test="districtId != null">districtId,</if>
            <if test="businessDeptId != null">business_dept_id,</if>
             create_time
         )values(
             <if test="userId != null and userId != ''">#{userId},</if>
@@ -293,6 +295,7 @@
             <if test="remark != null and remark != ''">#{remark},</if>
            <if test="ifBlack != null">#{ifBlack},</if>
            <if test="districtId != null">#{districtId},</if>
            <if test="businessDeptId != null">#{businessDeptId},</if>
             sysdate()
         )
    </insert>
@@ -319,6 +322,7 @@
            <if test="disableRemark != null">disable_remark = #{disableRemark},</if>
            <if test="operatingTime != null">operating_time = #{operatingTime},</if>
            <if test="operatingPerson != null">operating_person = #{operatingPerson},</if>
            <if test="businessDeptId != null">business_dept_id = #{businessDeptId},</if>
             update_time = sysdate()
         </set>
         where user_id = #{userId}
ruoyi-system/src/main/resources/mapper/system/TBillMapper.xml
@@ -63,7 +63,7 @@
            <if test="query.userId != null and query.userId !=''">
                and t.id = #{query.userId}
            </if>
            <if test='query.businessDeptId != null and query.businessDeptId != "" and query.businessDeptId!="0"'>
            <if test='query.businessDeptId != null and query.businessDeptId != "" and query.businessDeptId!=0'>
                and b.business_dept_id = #{query.businessDeptId}
            </if>
            <if test="query.billType != null">
@@ -350,9 +350,26 @@
        FROM
            t_street ts
                LEFT JOIN t_house th ON ts.id = th.street_id
                LEFT JOIN t_contract tc ON tc.house_id = th.id
                LEFT JOIN t_bill tb ON tc.id = tb.contract_id
                LEFT JOIN t_contract tc ON tc.house_id = th.id AND tc.pay_type = 2
                LEFT JOIN t_bill tb ON tc.id = tb.contract_id AND tb.pay_fees_status != 5
            <where>
                <if test="businessDeptId != 0">
                    AND th.business_dept_id = #{businessDeptId}
                </if>
            </where>
        GROUP BY ts.id
        ORDER BY rentAmount DESC
    </select>
    <select id="getJiFuBillList" resultType="com.ruoyi.system.model.TBill" parameterType="java.lang.String">
        SELECT tb.* FROM t_bill tb LEFT JOIN t_contract tt ON tb.contract_id = tt.id
        <where>
            tt.pay_type = 2 AND tb.bill_type = ${@com.ruoyi.common.enums.BillTypeEnum@Zujin.getCode()} AND tb.pay_fees_status != 5
            <if test="businessDeptId != null and businessDeptId != 0">
                AND tb.business_dept_id = #{businessDeptId}
            </if>
            <if test="first!=null and last !=null">
                AND tb.payable_fees_time BETWEEN #{first} AND #{last}
            </if>
        </where>
    </select>
</mapper>
ruoyi-system/src/main/resources/mapper/system/TCheckAcceptRecordMapper.xml
@@ -76,7 +76,7 @@
                AND t.check_time &gt;= #{query.startTime}
                AND t.check_time &lt;= #{query.endTime}
            </if>
            <if test="query.businessDeptId != null and query.businessDeptId != '' and query.businessDeptId != '0'">
            <if test="query.businessDeptId != null and query.businessDeptId != '' and query.businessDeptId != 0">
                AND c.business_dept_id = #{query.businessDeptId}
            </if>
            AND t.disabled = ${@com.ruoyi.common.enums.DisabledEnum@NO.getCode()}
ruoyi-system/src/main/resources/mapper/system/TContractMapper.xml
@@ -68,7 +68,7 @@
                    #{item}
                </foreach>
            </if>
            <if test="businessDeptId!=null and businessDeptId !='' and businessDeptId != '0'">
            <if test="businessDeptId!=null and businessDeptId !='' and businessDeptId != 0">
                and t1.business_dept_id = #{businessDeptId}
            </if>
            AND t1.disabled = ${@com.ruoyi.common.enums.DisabledEnum@NO.getCode()}
@@ -130,16 +130,21 @@
    <select id="getCurrentMonthRentCount" resultType="java.lang.Integer">
        SELECT COUNT(DISTINCT tc.tenant_id) AS new_tenant_count
        FROM t_contract tc
        WHERE
        -- 筛选本月签订的合同
        DATE_FORMAT(tc.sign_time, '%Y%m') = DATE_FORMAT(CURDATE(), '%Y%m') AND tc.status IN ("4", "5", "6", "7", "8", "9")
        -- 且租户在本月前从未签订过任何合同
        AND NOT EXISTS (
        SELECT 1
        FROM t_contract tc_hist
        WHERE tc_hist.tenant_id = tc.tenant_id
        AND tc_hist.sign_time <![CDATA[ < ]]> DATE_FORMAT(CURDATE(), '%Y-%m-01') AND tc_hist.status IN ("4", "5", "6", "7", "8", "9")
        )
        <where>
            -- 筛选本月签订的合同
            DATE_FORMAT(tc.sign_time, '%Y%m') = DATE_FORMAT(CURDATE(), '%Y%m') AND tc.status IN ("4", "5", "6", "7", "8", "9") AND tc.pay_type = 2
            -- 且租户在本月前从未签订过任何合同
            AND NOT EXISTS (
            SELECT 1
            FROM t_contract tc_hist
            WHERE tc_hist.tenant_id = tc.tenant_id
            AND tc_hist.sign_time <![CDATA[ < ]]> DATE_FORMAT(CURDATE(), '%Y-%m-01') AND tc_hist.status IN ("4", "5", "6", "7", "8", "9")
            )
            <if test="businessDeptId!=0">
                AND tc.business_dept_id = #{businessDeptId}
            </if>
        </where>
    </select>
    <select id="page" resultType="com.ruoyi.system.model.TContract">
ruoyi-system/src/main/resources/mapper/system/TFaultRepairMessageMapper.xml
@@ -67,6 +67,7 @@
        t.create_by,
        t.update_by,
        t.disabled,
        t.fault_cause,
        i.item_name AS itemName,
        it.type_name AS itemTypeName,
        tnt.resident_name AS residentName
@@ -129,7 +130,7 @@
            <if test="query.handlePerson != null and query.handlePerson != ''">
                AND t.handle_person LIKE CONCAT('%', #{query.handlePerson}, '%')
            </if>
            <if test="query.businessDeptId != null and query.businessDeptId != '' and query.businessDeptId != '0'">
            <if test="query.businessDeptId != null and query.businessDeptId != '' and query.businessDeptId != 0">
                AND tc.business_dept_id = #{query.businessDeptId}
            </if>
            AND t.disabled = ${@com.ruoyi.common.enums.DisabledEnum@NO.getCode()}
ruoyi-system/src/main/resources/mapper/system/THouseMapper.xml
@@ -55,7 +55,7 @@
            <if test="req.leaseStatus == 1">
                and (t2.start_time is null) and t1.lease_status = 1
            </if>
            <if test="req.businessDeptId != null and req.businessDeptId != '' and req.businessDeptId!='0'">
            <if test="req.businessDeptId != null and req.businessDeptId != '' and req.businessDeptId!=0">
                and t1.business_dept_id = #{req.businessDeptId}
            </if>
            AND t1.disabled = ${@com.ruoyi.common.enums.DisabledEnum@NO.getCode()}
@@ -86,5 +86,16 @@
          AND t1.disabled = ${@com.ruoyi.common.enums.DisabledEnum@NO.getCode()}
    </select>
    <select id="getRentedArea" resultType="java.lang.Double">
        SELECT COALESCE(SUM(t1.house_area),0) FROM t_house t1
        LEFT JOIN t_contract t2 ON t1.id = t2.house_id AND t2.status = 4 AND t2.pay_type = 2
        <where>
            t1.lease_status != 1
            <if test="businessDeptId!=0">
                AND t1.business_dept_id = #{businessDeptId}
            </if>
            AND t1.disabled = ${@com.ruoyi.common.enums.DisabledEnum@NO.getCode()}
        </where>
    </select>
</mapper>
ruoyi-system/src/main/resources/mapper/system/TRentalReturnRecordMapper.xml
New file
@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.system.mapper.TRentalReturnRecordMapper">
    <select id="queryRentalReturnRecordList" resultType="com.ruoyi.system.vo.RentalReturnRecordVO">
        SELECT
        trrr.id,
        trrr.tenant_id,
        trrr.house_id,
        trrr.contract_id,
        trrr.reason_for_return,
        trrr.audit_status,
        trrr.audit_user_id,
        trrr.create_time applicationDate,
        th.house_name,
        th.house_address,
        tc.contract_number,
        tt.resident_name submitter,
        tt.phone submitterPhone
        FROM
        t_rental_return_record trrr
        LEFT JOIN t_house th ON trrr.house_id = th.id
        LEFT JOIN t_contract tc ON trrr.contract_id = tc.id
        LEFT JOIN t_tenant tt ON tt.id = trrr.tenant_id
        <where>
            trrr.disabled = 0
            <if test="query.startDate != null and query.endDate !=null">
            AND trrr.create_time BETWEEN #{query.startDate} AND DATE_ADD(#{query.endDate}, INTERVAL 1 DAY)
            </if>
            <if test="query.isAdminDelete != null and query.isAdminDelete">
            AND trrr.is_admin_delete = 0
            </if>
            <if test="query.houseName != null and query.houseName != ''">
            AND th.house_name LIKE CONCAT('%',#{query.houseName},'%')
            </if>
            <if test="query.contractNumber != null and query.contractNumber != ''">
            AND tc.contract_number LIKE CONCAT('%',#{query.contractNumber},'%')
            </if>
            <if test="query.businessDeptId != null and query.businessDeptId != '' and query.businessDeptId != 0">
                AND tc.business_dept_id = #{query.businessDeptId}
            </if>
            <if test="query.submitter != null and query.submitter != ''">
            AND tt.resident_name LIKE CONCAT('%',#{query.submitter},'%')
            </if>
        </where>
        order by trrr.create_time desc
    </select>
    <select id="queryRentalReturnRecordById" resultType="com.ruoyi.system.vo.RentalReturnRecordVO"
            parameterType="java.lang.String">
        SELECT
            trrr.id,
            trrr.tenant_id,
            trrr.house_id,
            trrr.contract_id,
            trrr.reason_for_return,
            trrr.audit_status,
            trrr.audit_user_id,
            trrr.images,
            trrr.create_time,
            trrr.audit_opinion,
            th.house_name,
            th.house_address,
            tc.contract_number,
            tt.resident_name submitter,
            tt.phone submitterPhone
        FROM
            t_rental_return_record trrr
                LEFT JOIN t_house th ON trrr.house_id = th.id
                LEFT JOIN t_contract tc ON trrr.contract_id = tc.id
                LEFT JOIN t_tenant tt ON tt.id = trrr.tenant_id
        WHERE trrr.disabled = 0 AND trrr.id = #{id}
    </select>
</mapper>