Jelajahi Sumber

feat(property): 添加收据和退款相关功能

- 新增上传签约合同功能
- 新增收据和退款生成功能
- 新增收据和退款查询功能
- 优化收据和退款删除功能,支持批量删除附件
- 新增收据和退款分页查询接口,支持按合同号筛选
nahida 10 bulan lalu
induk
melakukan
5e5c68c80c
32 mengubah file dengan 1097 tambahan dan 31 penghapusan
  1. 24 0
      pom.xml
  2. 34 0
      src/main/java/com/zksy/controller/property/AContractInfoController.java
  3. 7 1
      src/main/java/com/zksy/controller/property/AReceiptInfoController.java
  4. 7 1
      src/main/java/com/zksy/controller/property/ARefundController.java
  5. 12 0
      src/main/java/com/zksy/property/domain/AReceiptInfo.java
  6. 12 0
      src/main/java/com/zksy/property/domain/ARefund.java
  7. 23 0
      src/main/java/com/zksy/property/domain/bo/RentalTempBo.java
  8. 23 0
      src/main/java/com/zksy/property/domain/dto/ReceiptDto.java
  9. 21 0
      src/main/java/com/zksy/property/domain/dto/ReturnReceiptDto.java
  10. 15 0
      src/main/java/com/zksy/property/domain/vo/AReceiptInfoVo.java
  11. 15 0
      src/main/java/com/zksy/property/domain/vo/ARefundVo.java
  12. 17 0
      src/main/java/com/zksy/property/domain/vo/CanBeGetReceiptVo.java
  13. 16 0
      src/main/java/com/zksy/property/domain/vo/CanBeGetReturnReceiptVo.java
  14. 8 0
      src/main/java/com/zksy/property/mapper/AReceiptInfoMapper.java
  15. 7 0
      src/main/java/com/zksy/property/mapper/ARefundMapper.java
  16. 15 0
      src/main/java/com/zksy/property/service/AContractInfoService.java
  17. 4 0
      src/main/java/com/zksy/property/service/AReceiptInfoService.java
  18. 5 0
      src/main/java/com/zksy/property/service/ARefundService.java
  19. 3 1
      src/main/java/com/zksy/property/service/ARentalContractService.java
  20. 205 17
      src/main/java/com/zksy/property/service/impl/AContractInfoServiceImpl.java
  21. 26 0
      src/main/java/com/zksy/property/service/impl/AReceiptInfoServiceImpl.java
  22. 26 0
      src/main/java/com/zksy/property/service/impl/ARefundServiceImpl.java
  23. 35 2
      src/main/java/com/zksy/property/service/impl/ARentalContractServiceImpl.java
  24. 10 1
      src/main/java/com/zksy/utils/CommonExceptionAdvice.java
  25. 322 0
      src/main/java/com/zksy/utils/WordUtils.java
  26. 36 0
      src/main/java/com/zksy/utils/exception/BusinessException.java
  27. 62 3
      src/main/resources/mapper/property/AReceiptInfoMapper.xml
  28. 55 5
      src/main/resources/mapper/property/ARefundMapper.xml
  29. TEMPAT SAMPAH
      src/main/resources/templates/1.docx
  30. TEMPAT SAMPAH
      src/main/resources/templates/4.docx
  31. 18 0
      src/test/java/com/zksy/property/service/impl/AContractInfoServiceImplTest.java
  32. 34 0
      src/test/java/com/zksy/property/service/impl/ARentalContractServiceImplTest.java

+ 24 - 0
pom.xml

@@ -89,6 +89,30 @@
             <artifactId>poi-scratchpad</artifactId>
             <version>4.1.2</version>
         </dependency>
+        <!-- poi-tl dependency -->
+        <dependency>
+            <groupId>com.deepoove</groupId>
+            <artifactId>poi-tl</artifactId>
+            <version>1.12.2</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.luhuiguo</groupId>
+            <artifactId>aspose-words</artifactId>
+            <version>23.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.itextpdf</groupId>
+            <artifactId>itextpdf</artifactId>
+            <version>5.5.13</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.itextpdf</groupId>
+            <artifactId>itext-asian</artifactId>
+            <version>5.2.0</version>
+        </dependency>
     </dependencies>
 
 

+ 34 - 0
src/main/java/com/zksy/controller/property/AContractInfoController.java

@@ -2,12 +2,15 @@ package com.zksy.controller.property;
 
 import com.zksy.property.domain.AContractInfo;
 import com.zksy.property.domain.dto.ContractFormDTO;
+import com.zksy.property.domain.dto.ReceiptDto;
+import com.zksy.property.domain.dto.ReturnReceiptDto;
 import com.zksy.property.service.AContractInfoService;
 import com.zksy.utils.AjaxResult;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 
 import java.time.LocalDateTime;
 import java.util.Arrays;
@@ -75,4 +78,35 @@ public class AContractInfoController {
         return AjaxResult.success(service.returnRent(houseId));
     }
 
+    @PostMapping("/uploadSignContract")
+    @ApiOperation(value = "上传签约合同")
+    public AjaxResult uploadSignContract(String contractId, MultipartFile file) {
+        return AjaxResult.success(service.uploadSignContract(contractId,file));
+    }
+
+
+    @GetMapping("/canBeDetReceiptData")
+    @ApiOperation(value = "可被获取的收据数据")
+    public AjaxResult canBeDetReceiptData(String contractId) {
+        return AjaxResult.success(service.canBeDetReceiptData(contractId));
+    }
+
+    @PostMapping("/getReceipt")
+    @ApiOperation(value = "获取收据")
+    public AjaxResult getReceipt(String contractId, @RequestBody ReceiptDto dto) {
+        return AjaxResult.success("成功开具",service.getReceipt(contractId, dto));
+    }
+
+    @GetMapping("/canBeDetReturnReceiptData")
+    @ApiOperation(value = "可被获取的退据数据")
+    public AjaxResult canBeDetReturnReceiptData(String contractId) {
+        return AjaxResult.success(service.canBeDetReturnReceiptData(contractId));
+    }
+
+    @PostMapping("/getReturnReceipt")
+    @ApiOperation(value = "获取退据")
+    public AjaxResult getReturnReceipt(String contractId, @RequestBody ReturnReceiptDto dto) {
+        return AjaxResult.success("成功开具",service.getReturnReceipt(contractId,dto));
+    }
+
 }

+ 7 - 1
src/main/java/com/zksy/controller/property/AReceiptInfoController.java

@@ -29,6 +29,12 @@ public class AReceiptInfoController {
     public AjaxResult findByPage(long pageNum, long pageSize, String receiptNumber,String payer,String paymentMethod,String generationDate){
         return AjaxResult.success(service.findByPage(pageNum, pageSize, receiptNumber,payer,paymentMethod,generationDate));
     }
+
+    @GetMapping("/findByPageWithContract")
+    @ApiOperation(value = "收据信息查询分页")
+    public AjaxResult findByPageWithContract(long pageNum, long pageSize, String receiptNumber,String payer,String paymentMethod,String contractNumber){
+        return AjaxResult.success(service.findByPageWithContract(pageNum, pageSize, receiptNumber,payer,paymentMethod,contractNumber));
+    }
     @GetMapping("/getAReceiptInfoList")
     @ApiOperation(value = "收据信息查询")
     public AjaxResult getAReceiptInfoList(String receiptNumber,String payer,String paymentMethod,String generationDate){
@@ -53,7 +59,7 @@ public class AReceiptInfoController {
     @PostMapping("/deleteBatch")
     @ApiOperation(value = "收据信息删除")
     public AjaxResult delete(@RequestBody String[] ids) {
-        return service.removeBatchByIds(Arrays.asList(ids)) ? AjaxResult.success("删除成功") : AjaxResult.error("删除失败");
+        return service.removeBatchByIdsWithUrl(Arrays.asList(ids)) ? AjaxResult.success("删除成功") : AjaxResult.error("删除失败");
     }
 
 }

+ 7 - 1
src/main/java/com/zksy/controller/property/ARefundController.java

@@ -29,6 +29,12 @@ public class ARefundController {
     public AjaxResult findByPage(long pageNum, long pageSize, String unit,String tenant,String generationDate){
         return AjaxResult.success(service.findByPage(pageNum, pageSize, unit,tenant,generationDate));
     }
+
+    @GetMapping("/findByPageWithContract")
+    @ApiOperation(value = "收据信息查询分页")
+    public AjaxResult findByPageWithContract(long pageNum, long pageSize, String unit,String tenant,String contractNumber){
+        return AjaxResult.success(service.findByPageWithContract(pageNum, pageSize, unit,tenant,contractNumber));
+    }
     @GetMapping("/getARefundList")
     @ApiOperation(value = "退款信息查询")
     public AjaxResult getARefundList(String unit,String tenant,String generationDate){
@@ -53,7 +59,7 @@ public class ARefundController {
     @PostMapping("/deleteBatch")
     @ApiOperation(value = "退款信息删除")
     public AjaxResult delete(@RequestBody String[] ids) {
-        return service.removeBatchByIds(Arrays.asList(ids)) ? AjaxResult.success("删除成功") : AjaxResult.error("删除失败");
+        return service.removeBatchByIdsWithUrl(Arrays.asList(ids)) ? AjaxResult.success("删除成功") : AjaxResult.error("删除失败");
     }
 
 }

+ 12 - 0
src/main/java/com/zksy/property/domain/AReceiptInfo.java

@@ -27,6 +27,12 @@ public class AReceiptInfo implements Serializable {
     @ApiModelProperty(value = "主键")
     private String id;
 
+    /**
+     * 合同id
+     */
+    @ApiModelProperty(value = "合同id")
+    private String contractId;
+
     /**
      * 收据单号
      */
@@ -81,6 +87,12 @@ public class AReceiptInfo implements Serializable {
     @ApiModelProperty(value = "收款事由")
     private String receiptReason;
 
+    /**
+     * 附件url
+     */
+    @ApiModelProperty(value = "附件url")
+    private String attachmentUrl;
+
     /**
      * 入账日期
      */

+ 12 - 0
src/main/java/com/zksy/property/domain/ARefund.java

@@ -27,6 +27,12 @@ public class ARefund implements Serializable {
     @ApiModelProperty(value = "主键")
     private String id;
 
+    /**
+     * 合同id
+     */
+    @ApiModelProperty(value = "合同id")
+    private String contractId;
+
     /**
      * 单位
      */
@@ -70,6 +76,12 @@ public class ARefund implements Serializable {
     @JsonFormat(pattern = "yyyy-MM-dd")
     private LocalDate generationDate;
 
+    /**
+     * 附件url
+     */
+    @ApiModelProperty(value = "附件url")
+    private String attachmentUrl;
+
     /**
      * 创建时间
      */

+ 23 - 0
src/main/java/com/zksy/property/domain/bo/RentalTempBo.java

@@ -103,5 +103,28 @@ public class RentalTempBo {
     private String c25;
     private String c26;
     private String c27;
+
+    private String d1;
+    private String d2;
+    private String d3;
+    private String d4;
+    private String d5;
+    private String d6;
+    private String d7;
+    private String d8;
+    private String d9;
+    private String d10;
+    private String d11;
+    private String d12;
+
+    private String e1;
+    private String e2;
+    private String e3;
+    private String e4;
+    private String e5;
+    private String e6;
+    private String e7;
+    private String e8;
+
 }
 

+ 23 - 0
src/main/java/com/zksy/property/domain/dto/ReceiptDto.java

@@ -0,0 +1,23 @@
+package com.zksy.property.domain.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class ReceiptDto {
+
+    private String paymentUnit;
+    private String paymentMethod;
+    private BigDecimal rmb;
+    private BigDecimal yj;
+    private BigDecimal zj;
+    private BigDecimal wyf;
+    private BigDecimal sf;
+    private String paymentReason;
+
+}

+ 21 - 0
src/main/java/com/zksy/property/domain/dto/ReturnReceiptDto.java

@@ -0,0 +1,21 @@
+package com.zksy.property.domain.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class ReturnReceiptDto {
+
+    private String dw;
+    private String zh;
+    private BigDecimal yj;
+    private BigDecimal zj;
+    private BigDecimal wyf;
+    private BigDecimal rmb;
+
+}

+ 15 - 0
src/main/java/com/zksy/property/domain/vo/AReceiptInfoVo.java

@@ -0,0 +1,15 @@
+package com.zksy.property.domain.vo;
+
+import com.zksy.property.domain.AReceiptInfo;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class AReceiptInfoVo extends AReceiptInfo {
+    private String contactNumber;
+}

+ 15 - 0
src/main/java/com/zksy/property/domain/vo/ARefundVo.java

@@ -0,0 +1,15 @@
+package com.zksy.property.domain.vo;
+
+import com.zksy.property.domain.ARefund;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+@EqualsAndHashCode(callSuper = true)
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class ARefundVo extends ARefund {
+    private String contractNumber;
+}

+ 17 - 0
src/main/java/com/zksy/property/domain/vo/CanBeGetReceiptVo.java

@@ -0,0 +1,17 @@
+package com.zksy.property.domain.vo;
+
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class CanBeGetReceiptVo {
+    private String jkdw;
+    private BigDecimal zj;
+    private BigDecimal yj;
+}

+ 16 - 0
src/main/java/com/zksy/property/domain/vo/CanBeGetReturnReceiptVo.java

@@ -0,0 +1,16 @@
+package com.zksy.property.domain.vo;
+
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class CanBeGetReturnReceiptVo {
+    private String zh;
+    private BigDecimal yj;
+}

+ 8 - 0
src/main/java/com/zksy/property/mapper/AReceiptInfoMapper.java

@@ -1,7 +1,10 @@
 package com.zksy.property.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.zksy.property.domain.AReceiptInfo;
+import com.zksy.property.domain.vo.AReceiptInfoVo;
+import org.apache.ibatis.annotations.Param;
 
 /**
 * @author Administrator
@@ -11,6 +14,11 @@ import com.zksy.property.domain.AReceiptInfo;
 */
 public interface AReceiptInfoMapper extends BaseMapper<AReceiptInfo> {
 
+    Page<AReceiptInfoVo> selectReceiptWithContractByPage(Page<AReceiptInfoVo> page,
+                                                         @Param("receiptNumber") String receiptNumber,
+                                                         @Param("payer") String payer,
+                                                         @Param("paymentMethod") String paymentMethod,
+                                                         @Param("contractNumber") String contractNumber);
 }
 
 

+ 7 - 0
src/main/java/com/zksy/property/mapper/ARefundMapper.java

@@ -1,7 +1,10 @@
 package com.zksy.property.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.zksy.property.domain.ARefund;
+import com.zksy.property.domain.vo.ARefundVo;
+import org.apache.ibatis.annotations.Param;
 
 /**
 * @author Administrator
@@ -11,6 +14,10 @@ import com.zksy.property.domain.ARefund;
 */
 public interface ARefundMapper extends BaseMapper<ARefund> {
 
+    Page<ARefundVo> selectRefundWithContractByPage(Page<ARefundVo> page,
+                                                   @Param("unit") String unit,
+                                                   @Param("tenant") String tenant,
+                                                   @Param("contractNumber") String contractNumber);
 }
 
 

+ 15 - 0
src/main/java/com/zksy/property/service/AContractInfoService.java

@@ -4,6 +4,11 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.zksy.property.domain.AContractInfo;
 import com.zksy.property.domain.dto.ContractFormDTO;
+import com.zksy.property.domain.dto.ReceiptDto;
+import com.zksy.property.domain.dto.ReturnReceiptDto;
+import com.zksy.property.domain.vo.CanBeGetReceiptVo;
+import com.zksy.property.domain.vo.CanBeGetReturnReceiptVo;
+import org.springframework.web.multipart.MultipartFile;
 
 import java.util.List;
 
@@ -21,4 +26,14 @@ public interface AContractInfoService extends IService<AContractInfo> {
     String signContract(ContractFormDTO dto);
 
     String returnRent(String houseId);
+
+    String uploadSignContract(String contractId, MultipartFile file);
+
+    String getReceipt(String contractId, ReceiptDto dto);
+
+    CanBeGetReceiptVo canBeDetReceiptData(String contractId);
+
+    CanBeGetReturnReceiptVo canBeDetReturnReceiptData(String contractId);
+
+    String getReturnReceipt(String contractId, ReturnReceiptDto dto);
 }

+ 4 - 0
src/main/java/com/zksy/property/service/AReceiptInfoService.java

@@ -3,6 +3,7 @@ package com.zksy.property.service;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.zksy.property.domain.AReceiptInfo;
+import com.zksy.property.domain.vo.AReceiptInfoVo;
 
 import java.util.List;
 
@@ -15,4 +16,7 @@ public interface AReceiptInfoService extends IService<AReceiptInfo> {
     Page<AReceiptInfo> findByPage(long pageNum, long pageSize, String receiptNumber,String payer,String paymentMethod,String generationDate);
     List<AReceiptInfo> getAReceiptInfoList(String receiptNumber,String payer,String paymentMethod,String generationDate);
 
+    boolean removeBatchByIdsWithUrl(List<String> list);
+
+    Page<AReceiptInfoVo> findByPageWithContract(long pageNum, long pageSize, String receiptNumber, String payer, String paymentMethod,String contractNumber);
 }

+ 5 - 0
src/main/java/com/zksy/property/service/ARefundService.java

@@ -3,6 +3,7 @@ package com.zksy.property.service;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.zksy.property.domain.ARefund;
+import com.zksy.property.domain.vo.ARefundVo;
 
 import java.util.List;
 
@@ -14,4 +15,8 @@ import java.util.List;
 public interface ARefundService extends IService<ARefund> {
     Page<ARefund> findByPage(long pageNum, long pageSize, String unit,String tenant,String generationDate);
     List<ARefund> getARefundList(String unit, String tenant, String generationDate);
+
+    boolean removeBatchByIdsWithUrl(List<String> list);
+
+    Page<ARefundVo> findByPageWithContract(long pageNum, long pageSize, String unit, String tenant, String contractNumber);
 }

+ 3 - 1
src/main/java/com/zksy/property/service/ARentalContractService.java

@@ -16,5 +16,7 @@ public interface ARentalContractService extends IService<ARentalContract> {
     Page<ARentalContract> findByPage(long pageNum, long pageSize, String tenantName, String contractNumber, String contractStatus);
     List<ARentalContract> getARentalContractList(String tenantName, String contractNumber, String contractStatus);
 
-    String generatorRental(String id, RentalTempBo bo);
+    String generatorRental(String mark, RentalTempBo bo);
+
+    String generatorRental(String mark, RentalTempBo bo,boolean isPrintPdf);
 }

+ 205 - 17
src/main/java/com/zksy/property/service/impl/AContractInfoServiceImpl.java

@@ -2,25 +2,34 @@ package com.zksy.property.service.impl;
 
 import cn.hutool.core.bean.BeanUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.zksy.property.domain.AContractInfo;
-import com.zksy.property.domain.AHouseInfoDetail;
-import com.zksy.property.domain.ASimplifiedHouseInfo;
-import com.zksy.property.domain.ATenantInfo;
+import com.zksy.property.domain.*;
 import com.zksy.property.domain.bo.*;
 import com.zksy.property.domain.dto.ContractFormDTO;
+import com.zksy.property.domain.dto.ReceiptDto;
+import com.zksy.property.domain.dto.ReturnReceiptDto;
+import com.zksy.property.domain.vo.CanBeGetReceiptVo;
+import com.zksy.property.domain.vo.CanBeGetReturnReceiptVo;
 import com.zksy.property.factory.ContractFactory;
 import com.zksy.property.mapper.AContractInfoMapper;
 import com.zksy.property.service.*;
+import com.zksy.service.MinioFileStorageService;
+import com.zksy.utils.exception.BusinessException;
+import lombok.SneakyThrows;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
 
 import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.math.RoundingMode;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.List;
 
 import static com.zksy.utils.util.generateAssetNumber;
@@ -33,6 +42,16 @@ import static com.zksy.utils.util.generateAssetNumber;
 @Service
 public class AContractInfoServiceImpl extends ServiceImpl<AContractInfoMapper, AContractInfo>
         implements AContractInfoService {
+
+    @Autowired
+    private MinioFileStorageService minioFileStorageService;
+
+    @Autowired
+    private AReceiptInfoService aReceiptInfoService;
+
+    @Autowired
+    private ARefundService aRefundService;
+
     @Override
     public String returnRent(String houseId) {
         ASimplifiedHouseInfo houseInfo = aSimplifiedHouseInfoService.getById(houseId);
@@ -55,6 +74,152 @@ public class AContractInfoServiceImpl extends ServiceImpl<AContractInfoMapper, A
         return "退租成功";
     }
 
+    @Override
+    @SneakyThrows
+    public String uploadSignContract(String contractId, MultipartFile file) {
+        AContractInfo contractInfo = this.getById(contractId);
+        if (contractInfo == null){
+            throw new RuntimeException("数据不存在");
+        }
+        if(StringUtils.isNotBlank(contractInfo.getSignContractUrl())){
+            minioFileStorageService.deleteFile(contractInfo.getSignContractUrl());
+        }
+        String path = minioFileStorageService.uploadFile(file, "合同");
+        contractInfo.setSignContractUrl(path);
+        contractInfo.setUpdateTime(LocalDateTime.now());
+        return this.updateById(contractInfo) ? "上传成功" : "上传失败";
+    }
+
+    @Override
+    public String getReceipt(String contractId, ReceiptDto dto) {
+        LambdaQueryWrapper<AReceiptInfo> aReceiptInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
+        aReceiptInfoLambdaQueryWrapper.eq(AReceiptInfo::getContractId,contractId);
+        var res = aReceiptInfoService.getOne(aReceiptInfoLambdaQueryWrapper);
+        if(res != null){
+            throw new BusinessException(999,"此合同已生成收据",res.getAttachmentUrl());
+        }
+        AContractInfo contractInfo = this.getById(contractId);
+        if (contractInfo == null){
+            throw new RuntimeException("数据不存在");
+        }
+        ASimplifiedHouseInfo houseInfo = aSimplifiedHouseInfoService.getById(contractInfo.getSimplifiedHouseId());
+        if(houseInfo == null){
+            throw new RuntimeException("数据不存在");
+        }
+        String receiptNumber = generateAssetNumber();
+        RentalTempBo bo = new RentalTempBo();
+
+        bo.setD1(dto.getPaymentUnit());
+        bo.setD2(dto.getPaymentMethod());
+        bo.setD3(convertToUppercase(dto.getRmb()));
+        bo.setD4(String.valueOf(dto.getRmb()));
+        bo.setD5(String.valueOf(dto.getZj()));
+        bo.setD6(String.valueOf(dto.getWyf()));
+        bo.setD7(String.valueOf(dto.getYj()));
+        bo.setD8(String.valueOf(dto.getSf()));
+        bo.setD9(dto.getPaymentReason());
+        bo.setD10(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy年M月d日")));
+        bo.setD11("");
+        bo.setD12(receiptNumber);
+
+        String attachmentUrl = aRentalContractService.generatorRental("4", bo, true);
+        AReceiptInfo receiptInfo = new AReceiptInfo();
+        receiptInfo.setContractId(contractId);
+        receiptInfo.setReceiptNumber(receiptNumber);
+        receiptInfo.setPayer(dto.getPaymentUnit());
+        receiptInfo.setPaymentMethod(dto.getPaymentMethod());
+        receiptInfo.setTotalAmount(dto.getRmb());
+        receiptInfo.setRent(dto.getZj());
+        receiptInfo.setPropertyFee(dto.getWyf());
+        receiptInfo.setDeposit(dto.getYj());
+        receiptInfo.setWaterFee(dto.getSf());
+        receiptInfo.setReceiptReason(dto.getPaymentReason());
+        receiptInfo.setAttachmentUrl(attachmentUrl);
+        receiptInfo.setGenerationDate(LocalDate.now());
+        receiptInfo.setCreateTime(LocalDateTime.now());
+        receiptInfo.setUpdateTime(LocalDateTime.now());
+        aReceiptInfoService.save(receiptInfo);
+        return attachmentUrl;
+    }
+
+
+    @Override
+    public CanBeGetReceiptVo canBeDetReceiptData(String contractId) {
+        AContractInfo contractInfo = this.getById(contractId);
+        if (contractInfo == null){
+            throw new RuntimeException("数据不存在");
+        }
+        ASimplifiedHouseInfo houseInfo = aSimplifiedHouseInfoService.getById(contractInfo.getSimplifiedHouseId());
+        if (houseInfo == null) {
+            throw new RuntimeException("数据不存在");
+        }
+        CanBeGetReceiptVo vo = new CanBeGetReceiptVo();
+        vo.setJkdw(houseInfo.getHouseName());
+        vo.setZj(houseInfo.getRentRange());
+        vo.setYj(contractInfo.getContractDeposit());
+        return vo;
+    }
+
+    @Override
+    public CanBeGetReturnReceiptVo canBeDetReturnReceiptData(String contractId) {
+        AContractInfo contractInfo = this.getById(contractId);
+        if (contractInfo == null){
+            throw new RuntimeException("数据不存在");
+        }
+        ASimplifiedHouseInfo houseInfo = aSimplifiedHouseInfoService.getById(contractInfo.getSimplifiedHouseId());
+        if (houseInfo == null) {
+            throw new RuntimeException("数据不存在");
+        }
+        return new CanBeGetReturnReceiptVo(
+                houseInfo.getHouseName(),
+                contractInfo.getContractDeposit()
+        );
+    }
+
+    @Override
+    public String getReturnReceipt(String contractId, ReturnReceiptDto dto) {
+        LambdaQueryWrapper<ARefund> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(ARefund::getContractId,contractId);
+        var  res = aRefundService.getOne(queryWrapper);
+        if(res != null){
+            throw new BusinessException(999,"此合同已生成退据",res.getAttachmentUrl());
+        }
+        AContractInfo contractInfo = this.getById(contractId);
+        if (contractInfo == null){
+            throw new RuntimeException("数据不存在");
+        }
+        ASimplifiedHouseInfo houseInfo = aSimplifiedHouseInfoService.getById(contractInfo.getSimplifiedHouseId());
+        if(houseInfo == null){
+            throw new RuntimeException("数据不存在");
+        }
+
+        RentalTempBo bo = new RentalTempBo();
+        bo.setE1(dto.getDw());
+        bo.setE2(dto.getZh());
+        bo.setE3(String.valueOf(dto.getYj()));
+        bo.setE4(String.valueOf(dto.getYj()));
+        bo.setE5(String.valueOf(dto.getWyf()));
+        bo.setE6(convertToUppercase(dto.getRmb()));
+        bo.setE7(String.valueOf(dto.getRmb()));
+        bo.setE8(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy年M月d日")));
+
+        String attachmentUrl = aRentalContractService.generatorRental("5", bo, true);
+        ARefund refund = new ARefund();
+        refund.setContractId(contractId);
+        refund.setUnit(dto.getDw());
+        refund.setTenant(dto.getZh());
+        refund.setDeposit(dto.getYj());
+        refund.setRent(dto.getZj());
+        refund.setPropertyFee(dto.getWyf());
+        refund.setTotalAmount(dto.getRmb());
+        refund.setAttachmentUrl(attachmentUrl);
+        refund.setGenerationDate(LocalDate.now());
+        refund.setCreateTime(LocalDateTime.now());
+        refund.setUpdateTime(LocalDateTime.now());
+        aRefundService.save(refund);
+        return attachmentUrl;
+    }
+
     @Autowired
     @Lazy
     private ASimplifiedHouseInfoService aSimplifiedHouseInfoService;
@@ -330,21 +495,25 @@ public class AContractInfoServiceImpl extends ServiceImpl<AContractInfoMapper, A
         // 添加负号前缀
         return isNegative ? "负" + result : result;
     }
+    public static String convertToUppercase(BigDecimal number) {
+        if (number == null) {
+            return "零元整";
+        }
 
-    // 数字转中文大写的主方法
-    public static String convertToUppercase(double number) {
-        if (number < 0) {
-            return "负" + convertToUppercase(-number);
+        if (number.compareTo(BigDecimal.ZERO) < 0) {
+            return "负" + convertToUppercase(number.abs());
         }
 
         String[] units = {"", "拾", "佰", "仟", "万", "拾", "佰", "仟", "亿", "拾", "佰", "仟", "兆"};
         String[] digits = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};
 
-        long integerPart = (long) number;
-        long decimalPart = Math.round((number - integerPart) * 100);
+        // 分离整数部分和小数部分
+        BigDecimal roundedNumber = number.setScale(2, RoundingMode.HALF_UP);
+        BigInteger integerPart = roundedNumber.toBigInteger();
+        int decimalPart = roundedNumber.remainder(BigDecimal.ONE).multiply(BigDecimal.valueOf(100)).intValue();
 
         StringBuilder result = new StringBuilder();
-        String integerStr = String.valueOf(integerPart);
+        String integerStr = integerPart.toString();
 
         // 处理整数部分
         boolean zeroFlag = false;
@@ -355,8 +524,18 @@ public class AContractInfoServiceImpl extends ServiceImpl<AContractInfoMapper, A
             if (digit == 0) {
                 zeroFlag = true;
                 // 处理单位:万、亿、兆
-                if (unitIndex % 4 == 0) {
-                    result.append(units[unitIndex]);
+                if (unitIndex % 4 == 0 && unitIndex > 0) {
+                    // 检查是否需要添加单位
+                    boolean needUnit = false;
+                    for (int j = i + 1; j < Math.min(i + 5, integerStr.length()); j++) {
+                        if (integerStr.charAt(j) != '0') {
+                            needUnit = true;
+                            break;
+                        }
+                    }
+                    if (!needUnit) {
+                        result.append(units[unitIndex]);
+                    }
                 }
             } else {
                 if (zeroFlag) {
@@ -374,22 +553,31 @@ public class AContractInfoServiceImpl extends ServiceImpl<AContractInfoMapper, A
         result.append("元");
 
         // 处理小数部分
-        long jiao = decimalPart / 10;
-        long fen = decimalPart % 10;
+        int jiao = decimalPart / 10;
+        int fen = decimalPart % 10;
 
         if (jiao == 0 && fen == 0) {
             result.append("整");
         } else {
             if (jiao != 0) {
-                result.append(digits[(int) jiao]).append("角");
+                result.append(digits[jiao]).append("角");
             }
             if (fen != 0) {
-                result.append(digits[(int) fen]).append("分");
+                result.append(digits[fen]).append("分");
             }
         }
 
         return result.toString();
     }
+
+    public static String convertToUppercase(double number) {
+        return convertToUppercase(BigDecimal.valueOf(number));
+    }
+
+
+
+
+
 }
 
 

+ 26 - 0
src/main/java/com/zksy/property/service/impl/AReceiptInfoServiceImpl.java

@@ -4,11 +4,16 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zksy.property.domain.AReceiptInfo;
+import com.zksy.property.domain.vo.AReceiptInfoVo;
 import com.zksy.property.mapper.AReceiptInfoMapper;
 import com.zksy.property.service.AReceiptInfoService;
+import com.zksy.service.MinioFileStorageService;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
 * @author Administrator
@@ -19,6 +24,9 @@ import java.util.List;
 public class AReceiptInfoServiceImpl extends ServiceImpl<AReceiptInfoMapper, AReceiptInfo>
     implements AReceiptInfoService{
 
+    @Autowired
+    private AReceiptInfoMapper areceiptInfoMapper;
+
     @Override
     public Page<AReceiptInfo> findByPage(long pageNum, long pageSize, String receiptNumber, String payer, String paymentMethod, String generationDate) {
         Page<AReceiptInfo> page = new Page<>(pageNum,pageSize);
@@ -41,6 +49,24 @@ public class AReceiptInfoServiceImpl extends ServiceImpl<AReceiptInfoMapper, ARe
         List<AReceiptInfo> list = this.list(queryWrapper);
         return list;
     }
+
+    @Autowired
+    private MinioFileStorageService minioFileStorageService;
+
+    @Override
+    @Transactional
+    public boolean removeBatchByIdsWithUrl(List<String> ids) {
+        List<AReceiptInfo> aReceiptInfoList = listByIds(ids);
+        List<String> list = aReceiptInfoList.stream().map(AReceiptInfo::getAttachmentUrl).collect(Collectors.toList());
+        minioFileStorageService.deleteFileBatch(list);
+        return removeBatchByIds(ids);
+    }
+
+    @Override
+    public Page<AReceiptInfoVo> findByPageWithContract(long pageNum, long pageSize, String receiptNumber, String payer, String paymentMethod, String contractNumber) {
+        Page<AReceiptInfoVo> page = new Page<>(pageNum, pageSize);
+        return areceiptInfoMapper.selectReceiptWithContractByPage(page, receiptNumber, payer, paymentMethod, contractNumber);
+    }
 }
 
 

+ 26 - 0
src/main/java/com/zksy/property/service/impl/ARefundServiceImpl.java

@@ -4,11 +4,16 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zksy.property.domain.ARefund;
+import com.zksy.property.domain.vo.ARefundVo;
 import com.zksy.property.mapper.ARefundMapper;
 import com.zksy.property.service.ARefundService;
+import com.zksy.service.MinioFileStorageService;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
 * @author Administrator
@@ -19,6 +24,9 @@ import java.util.List;
 public class ARefundServiceImpl extends ServiceImpl<ARefundMapper, ARefund>
     implements ARefundService{
 
+    @Autowired
+    private ARefundMapper aRefundMapper;
+
     @Override
     public Page<ARefund> findByPage(long pageNum, long pageSize, String unit, String tenant, String generationDate) {
         Page<ARefund> page = new Page<>(pageNum,pageSize);
@@ -39,6 +47,24 @@ public class ARefundServiceImpl extends ServiceImpl<ARefundMapper, ARefund>
         List<ARefund> list = this.list(queryWrapper);
         return list;
     }
+
+    @Autowired
+    private MinioFileStorageService minioFileStorageService;
+
+    @Override
+    @Transactional
+    public boolean removeBatchByIdsWithUrl(List<String> ids) {
+        List<ARefund> aReceiptInfoList = listByIds(ids);
+        List<String> list = aReceiptInfoList.stream().map(ARefund::getAttachmentUrl).collect(Collectors.toList());
+        minioFileStorageService.deleteFileBatch(list);
+        return removeBatchByIds(ids);
+    }
+
+    @Override
+    public Page<ARefundVo> findByPageWithContract(long pageNum, long pageSize, String unit, String tenant, String contractNumber) {
+        Page<ARefundVo> page = new Page<>(pageNum, pageSize);
+        return aRefundMapper.selectRefundWithContractByPage(page, unit, tenant, contractNumber);
+    }
 }
 
 

+ 35 - 2
src/main/java/com/zksy/property/service/impl/ARentalContractServiceImpl.java

@@ -9,6 +9,7 @@ import com.zksy.property.mapper.ARentalContractMapper;
 import com.zksy.property.service.ARentalContentService;
 import com.zksy.property.service.ARentalContractService;
 import com.zksy.service.MinioFileStorageService;
+import com.zksy.utils.WordUtils;
 import org.apache.poi.xwpf.usermodel.XWPFDocument;
 import org.apache.poi.xwpf.usermodel.XWPFParagraph;
 import org.apache.poi.xwpf.usermodel.XWPFRun;
@@ -63,12 +64,14 @@ public class ARentalContractServiceImpl extends ServiceImpl<ARentalContractMappe
     }
 
     @Override
-    public String generatorRental(String mark, RentalTempBo bo) {
+    public String generatorRental(String mark, RentalTempBo bo,boolean isPrintPdf) {
         // 定义标记与字段数量的映射关系
         Map<String, Integer> markToFieldCount = new HashMap<>();
         markToFieldCount.put("1", 9);
         markToFieldCount.put("2", 56);
         markToFieldCount.put("3", 27);
+        markToFieldCount.put("4", 12);
+        markToFieldCount.put("5", 8);
 
         // 验证标记类型
         if (!markToFieldCount.containsKey(mark)) {
@@ -89,6 +92,12 @@ public class ARentalContractServiceImpl extends ServiceImpl<ARentalContractMappe
             case "3":
                 prefix = "c";
                 break;
+            case "4":
+                prefix = "d";
+                break;
+            case "5":
+                prefix = "e";
+                break;
             default:
                 throw new IllegalStateException("非法 mark 值:" + mark);
         }
@@ -110,12 +119,36 @@ public class ARentalContractServiceImpl extends ServiceImpl<ARentalContractMappe
                 replacePlaceholders(document, contentMap);
                 document.write(outputStream);
             }
-            return minioFileStorageService.uploadFileByFile(tempFile,"合同");
+            if(isPrintPdf){
+                File file = WordUtils.wordToPdf(tempFile);
+                return minioFileStorageService.uploadFileByFile(file,getFileTypeDescription(mark));
+            }
+            return minioFileStorageService.uploadFileByFile(tempFile,getFileTypeDescription(mark));
         } catch (Exception e) {
             throw new RuntimeException("处理文档时发生错误", e);
         }
     }
 
+    @Override
+    public String generatorRental(String mark, RentalTempBo bo) {
+        return generatorRental(mark, bo,false);
+    }
+
+    private String getFileTypeDescription(String mark) {
+        switch (mark) {
+            case "1":
+            case "2":
+            case "3":
+                return "合同";
+            case "4":
+                return "收据";
+            case "5":
+                return "退据";
+            default:
+                throw new IllegalArgumentException("不支持的标记类型: " + mark);
+        }
+    }
+
     private Map<String, String> buildContentMap(RentalTempBo bo, String prefix, int count) {
         Map<String, String> contentMap = new HashMap<>();
         for (int i = 1; i <= count; i++) {

+ 10 - 1
src/main/java/com/zksy/utils/CommonExceptionAdvice.java

@@ -2,6 +2,7 @@ package com.zksy.utils;
 
 
 import com.zksy.utils.exception.BadRequestException;
+import com.zksy.utils.exception.BusinessException;
 import com.zksy.utils.exception.CommonException;
 import com.zksy.utils.exception.DbException;
 import lombok.extern.slf4j.Slf4j;
@@ -29,7 +30,8 @@ public class CommonExceptionAdvice {
     public Object handleBadRequestException(CommonException e) {
         log.error("自定义异常 -> {} , 异常原因:{}  ",e.getClass().getName(), e.getMessage());
         log.debug("", e);
-        return processResponse(e);
+//        return processResponse(e);
+        return AjaxResult.error(e.getCode(),e.getMessage());
     }
 
     @ExceptionHandler(MethodArgumentNotValidException.class)
@@ -61,6 +63,13 @@ public class CommonExceptionAdvice {
         return AjaxResult.error(e.getMessage() != null ? e.getMessage() : "服务器内部异常");
     }
 
+    @ExceptionHandler(BusinessException.class)
+    public Object handleBusinessException(BusinessException e) {
+        log.error("业务异常 -> {}", e.getMessage());
+        log.debug("", e);
+        return new AjaxResult(e.getCode(),e.getMessage(),e.getData());
+    }
+
     private ResponseEntity<AjaxResult> processResponse(CommonException e){
         return ResponseEntity.status(e.getCode()).body(AjaxResult.error(String.valueOf(e)));
     }

+ 322 - 0
src/main/java/com/zksy/utils/WordUtils.java

@@ -0,0 +1,322 @@
+package com.zksy.utils;
+
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.StrUtil;
+import com.aspose.words.Document;
+import com.aspose.words.FontSettings;
+import com.aspose.words.SaveFormat;
+import com.deepoove.poi.XWPFTemplate;
+import com.itextpdf.text.BaseColor;
+import com.itextpdf.text.DocumentException;
+import com.itextpdf.text.pdf.*;
+import org.apache.poi.xwpf.usermodel.XWPFDocument;
+import org.apache.poi.xwpf.usermodel.XWPFParagraph;
+
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class WordUtils {
+    /*
+     * @Description:
+     * @Param:
+     * @param inputPath word文件路径
+     * @param keysToCheck 需要校验的键
+     * @return:
+     **/
+    public static void checkKeys(String inputPath, Set<String> keysToCheck) {
+        XWPFDocument document;
+        try (FileInputStream fis = new FileInputStream(inputPath)) {
+            document = new XWPFDocument(fis);
+        } catch (IOException e) {
+            throw new RuntimeException("读取模板文件失败: " + e.getMessage());
+        }
+        // 查找模板中的所有占位符
+        Set<String> placeholders = findPlaceholders(document);
+        // 校验模板中是否包含所有替换变量!
+        for (String key : keysToCheck) {
+            if (!placeholders.contains(key)) {
+                throw new RuntimeException("缺少模板占位符对应的替换内容: " + key);
+            }
+        }
+    }
+
+    /*
+     * @Description: 插入word
+     * @param inputPath 输入文件路径
+     * @param outputPath 输出文件路径
+     * @param hashMap 替换内容
+     * @param keysToCheck 需要校验的键
+     **/
+    public static void insertWord(String inputPath, String outputPath, HashMap<String, Object> hashMap) {
+        // 使用poi-tl模板引擎进行渲染
+        XWPFTemplate template = XWPFTemplate.compile(inputPath).render(hashMap);
+        try {
+            // 将渲染后的模板写入输出文件
+            template.writeAndClose(Files.newOutputStream(Paths.get(outputPath)));
+        } catch (IOException e) {
+            // 捕获异常并抛出运行时异常
+            throw new RuntimeException("写入输出文件失败: " + e.getMessage());
+        }
+    }
+
+    /*
+     * @Description: word转pdf
+     * @Param:
+     * @param wordFilePath word文件路径
+     * @param pdfFilePath  pdf文件路径
+     * @return:
+     **/
+    public static void wordToPdf(String wordFilePath, String pdfFilePath) {
+        FileOutputStream fileOS = null;
+        try {
+            Document doc = new Document(wordFilePath);
+            fileOS = new FileOutputStream(pdfFilePath);
+
+            // 保存转换的pdf文件
+            doc.save(fileOS, SaveFormat.PDF);
+//            addWaterMark(pdfFilePath);
+        } catch (Exception e) {
+            throw new RuntimeException("转换Word文档为PDF失败: " + e.getMessage());
+        } finally {
+            try {
+                if (fileOS != null) {
+                    fileOS.close();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+                System.out.println("关闭文件输出流时发生错误");
+            }
+        }
+    }
+
+    /*
+     * @Description: word转pdf并返回File对象
+     * @Param:
+     * @param wordFilePath word文件路径
+     * @return: java.io.File 转换后的PDF文件
+     **/
+    public static File wordToPdfAndGetFile(String wordFilePath) {
+        FileOutputStream fileOS = null;
+        try {
+            Document doc = new Document(wordFilePath);
+
+            // 创建临时PDF文件
+            File pdfFile = File.createTempFile("converted_", ".pdf");
+            fileOS = new FileOutputStream(pdfFile);
+
+            // 保存转换的pdf文件
+            doc.save(fileOS, SaveFormat.PDF);
+            fileOS.close();
+
+            return pdfFile;
+        } catch (Exception e) {
+            throw new RuntimeException("转换Word文档为PDF失败: " + e.getMessage());
+        } finally {
+            try {
+                if (fileOS != null) {
+                    fileOS.close();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+                System.out.println("关闭文件输出流时发生错误");
+            }
+        }
+    }
+
+    /*
+     * @Description: word文件转pdf文件(生成临时文件)
+     * @Param:
+     * @param wordFile word文件对象
+     * @return: java.io.File 转换后的PDF文件
+     **/
+    public static File wordToPdf(File wordFile) {
+        FileOutputStream fileOS = null;
+        try {
+            Document doc = new Document(wordFile.getAbsolutePath());
+            // 创建临时PDF文件
+            File pdfFile = File.createTempFile("converted_", ".pdf");
+            fileOS = new FileOutputStream(pdfFile);
+            // 保存转换的pdf文件
+            doc.save(fileOS, SaveFormat.PDF);
+            return pdfFile;
+        } catch (Exception e) {
+            throw new RuntimeException("转换Word文档为PDF失败: " + e.getMessage());
+        } finally {
+            try {
+                if (fileOS != null) {
+                    fileOS.close();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+                System.out.println("关闭文件输出流时发生错误");
+            }
+        }
+    }
+
+    public static ByteArrayOutputStream wordToPdf(InputStream wordFile, String fontFolderPath, String fontName) {
+        ByteArrayOutputStream pdfFile = new ByteArrayOutputStream();
+        try {
+            Document doc = new Document(wordFile);
+            FontSettings fontSettings = new FontSettings();
+            String os = System.getProperty("os.name").toLowerCase();
+            if (StrUtil.isNotBlank(os) && os.contains("win")) {
+                String path = WordUtils.class.getResource(fontFolderPath + fontName).getPath();
+                if (StrUtil.isNotBlank(path)) {
+                    // 配置字体
+                    path = path.substring(0, path.lastIndexOf("/") + 1);
+                    fontSettings.setFontsFolder(path, true);
+                }
+            } else {
+                fontSettings.setFontsFolder(fontFolderPath, true);
+            }
+            doc.setFontSettings(fontSettings);
+            // 保存转换的pdf文件
+            doc.save(pdfFile, SaveFormat.PDF);
+            return pdfFile;
+        } catch (Exception e) {
+            throw new RuntimeException("转换Word文档为PDF失败: " + e.getMessage());
+        } finally {
+            try {
+                pdfFile.close();
+            } catch (IOException e) {
+                System.out.println("关闭文件输出流时发生错误");
+            }
+        }
+    }
+
+    public static ByteArrayOutputStream wordToPdfWithWaterMark(InputStream wordFile, String... watermarkTexts) {
+        ByteArrayOutputStream pdfFile = new ByteArrayOutputStream();
+        try {
+            Document doc = new Document(wordFile);
+            // 保存转换的pdf文件
+            doc.save(pdfFile, SaveFormat.PDF);
+            return addWaterMark(pdfFile, watermarkTexts);
+        } catch (Exception e) {
+            throw new RuntimeException("转换Word文档为PDF失败: " + e.getMessage());
+        } finally {
+            try {
+                pdfFile.close();
+            } catch (IOException e) {
+                System.out.println("关闭文件输出流时发生错误");
+            }
+        }
+    }
+
+    public static ByteArrayOutputStream addWaterMark(ByteArrayOutputStream pdfFile, String... watermarkTexts) throws IOException, DocumentException {
+        PdfReader reader = new PdfReader(pdfFile.toByteArray());
+        ByteArrayOutputStream pdfFileWithWaterMark = new ByteArrayOutputStream();
+        PdfStamper stamper = new PdfStamper(reader, pdfFileWithWaterMark);
+        doAddWaterMark(reader, stamper, watermarkTexts);
+        return pdfFileWithWaterMark;
+    }
+
+    private static void addWaterMark(String pdfFilePath, String... watermarkTexts) throws IOException, DocumentException {
+        PdfReader reader = new PdfReader(pdfFilePath);
+        PdfStamper stamper = new PdfStamper(reader, new FileOutputStream("F:\\tempDocument\\temp\\result.pdf"));
+        doAddWaterMark(reader, stamper, watermarkTexts);
+    }
+
+    private static void doAddWaterMark(PdfReader reader, PdfStamper stamper, String... watermarkTexts) throws DocumentException, IOException {
+        if (ArrayUtil.isEmpty(watermarkTexts)) {
+            watermarkTexts = new String[]{"水印"};
+        }
+
+        String fontPath = WordUtils.class.getResource("/fonts/SimSun.ttf").getPath();
+        BaseFont bf = BaseFont.createFont(fontPath, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
+
+        PdfContentByte over;
+        PdfTemplate template;
+        PdfGState gs = new PdfGState();
+        // 设置透明度为30%
+        gs.setFillOpacity(0.3f);
+
+        float x = 298;
+        float y;
+        float rotation = 45;
+        // 设置每行水印之间的间隔
+        float lineSpacing = 160;
+
+        for (int i = 1; i <= reader.getNumberOfPages(); i++) {
+            over = stamper.getOverContent(i);
+            // 应用透明度设置
+            over.setGState(gs);
+            // 重置y坐标为每页的初始位置
+            y = 421;
+
+            for (String watermarkText : watermarkTexts) {
+                template = over.createTemplate(500, 500);
+                template.setLineWidth(0.25f);
+                template.setColorStroke(BaseColor.LIGHT_GRAY);
+                template.beginText();
+                template.setFontAndSize(bf, 60);
+                template.setColorFill(BaseColor.GRAY);
+                template.showTextAligned(PdfContentByte.ALIGN_CENTER, watermarkText, x, y, rotation);
+                template.endText();
+                over.addTemplate(template, 0, 0);
+                // 调整每行水印的垂直位置
+                y -= lineSpacing;
+            }
+        }
+
+        stamper.close();
+        reader.close();
+    }
+
+
+    /*
+     * @Description: 查找word中的所有占位符
+     * @param document
+     * @return java.util.Set<java.lang.String>
+     **/
+    private static Set<String> findPlaceholders(XWPFDocument document) {
+        Set<String> placeholders = new HashSet<>();
+        Pattern pattern = Pattern.compile("\\{\\{\\s*(@?\\w+)\\s*\\}\\}");
+        List<XWPFParagraph> paragraphs = document.getParagraphs();
+        for (XWPFParagraph paragraph : paragraphs) {
+            String paragraphText = paragraph.getText();
+            Matcher matcher = pattern.matcher(paragraphText);
+            while (matcher.find()) {
+                placeholders.add(matcher.group(1).trim());
+            }
+        }
+        return placeholders;
+    }
+
+    public static void main(String[] args) {
+//        // 定义输入和输出文件的路径
+//        String inputPath = "D:\\software\\Java\\projects\\word-utils\\src\\main\\java\\org\\example\\utils\\demo.docx";
+        String outputPath = "C:\\Users\\hxb\\Downloads\\6357971575460482300.docx";
+        String outputPdfPath = "C:\\Users\\hxb\\Downloads\\sex.pdf";
+//        // 定义替换内容的键值对
+//        HashMap<String, Object> hashMap = new HashMap<>();
+//        // 承租方
+//        hashMap.put("name", "Hi, poi-tl Word模板引擎");
+//        // 房屋基本情况
+//        hashMap.put("list", "2层1户、"+"2层2户、"+"2层3户");
+//        // 租约起止日期
+//        hashMap.put("zuFromToDate", "2022年一月一日至2023年一月一日");
+//        // 押金
+//        hashMap.put("zj", "5000");
+//        // 押金大写
+//        hashMap.put("dxzj", "伍佰");
+//        // 押金
+//        hashMap.put("yj", "500");
+//        // 约定租金
+//        hashMap.put("ydzj", "500");
+//        // 调用insertWord方法来处理文档
+//        HashSet<String> strings = new HashSet<>();
+//        strings.add("yz1");
+//        checkKeys(inputPath, strings);
+//        insertWord(inputPath, outputPath, hashMap);
+        // 将Word文档转换为PDF
+        wordToPdf(outputPath, outputPdfPath);
+    }
+
+}

+ 36 - 0
src/main/java/com/zksy/utils/exception/BusinessException.java

@@ -0,0 +1,36 @@
+package com.zksy.utils.exception;
+
+import lombok.Data;
+
+@Data
+public class BusinessException extends RuntimeException{
+    private Object data;
+    private Integer code;
+
+    public BusinessException() {
+        super();
+    }
+
+    public BusinessException(String message) {
+        super(message);
+    }
+
+    public BusinessException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public BusinessException(Throwable cause) {
+        super(cause);
+    }
+
+    public BusinessException(Integer code,String message, Object data) {
+        super(message);
+        this.data = data;
+        this.code = code;
+    }
+
+    public BusinessException(String message, Object data, Throwable cause) {
+        super(message, cause);
+        this.data = data;
+    }
+}

+ 62 - 3
src/main/resources/mapper/property/AReceiptInfoMapper.xml

@@ -6,6 +6,7 @@
 
     <resultMap id="BaseResultMap" type="com.zksy.property.domain.AReceiptInfo">
             <id property="id" column="id" jdbcType="VARCHAR"/>
+            <result property="contractId" column="contract_id" jdbcType="VARCHAR"/>
             <result property="receiptNumber" column="receipt_number" jdbcType="VARCHAR"/>
             <result property="payer" column="payer" jdbcType="VARCHAR"/>
             <result property="paymentMethod" column="payment_method" jdbcType="VARCHAR"/>
@@ -14,6 +15,7 @@
             <result property="propertyFee" column="property_fee" jdbcType="DECIMAL"/>
             <result property="deposit" column="deposit" jdbcType="DECIMAL"/>
             <result property="waterFee" column="water_fee" jdbcType="DECIMAL"/>
+            <result property="attachmentUrl" column="attachment_url" jdbcType="VARCHAR"/>
             <result property="receiptReason" column="receipt_reason" jdbcType="VARCHAR"/>
             <result property="generationDate" column="generation_date" javaType="DATE"/>
             <result property="accountingDate" column="accounting_date" jdbcType="DATE"/>
@@ -21,11 +23,68 @@
             <result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
     </resultMap>
 
+    <resultMap id="VoResultMap" type="com.zksy.property.domain.vo.AReceiptInfoVo">
+        <id property="id" column="id" jdbcType="VARCHAR"/>
+        <result property="contractId" column="contract_id" jdbcType="VARCHAR"/>
+        <result property="receiptNumber" column="receipt_number" jdbcType="VARCHAR"/>
+        <result property="payer" column="payer" jdbcType="VARCHAR"/>
+        <result property="paymentMethod" column="payment_method" jdbcType="VARCHAR"/>
+        <result property="totalAmount" column="total_amount" jdbcType="DECIMAL"/>
+        <result property="rent" column="rent" jdbcType="DECIMAL"/>
+        <result property="propertyFee" column="property_fee" jdbcType="DECIMAL"/>
+        <result property="deposit" column="deposit" jdbcType="DECIMAL"/>
+        <result property="waterFee" column="water_fee" jdbcType="DECIMAL"/>
+        <result property="attachmentUrl" column="attachment_url" jdbcType="VARCHAR"/>
+        <result property="receiptReason" column="receipt_reason" jdbcType="VARCHAR"/>
+        <result property="generationDate" column="generation_date" jdbcType="DATE" javaType="java.time.LocalDate"/>
+        <result property="accountingDate" column="accounting_date" jdbcType="DATE" javaType="java.time.LocalDate"/>
+        <result property="createTime" column="create_time" jdbcType="TIMESTAMP" javaType="java.time.LocalDateTime"/>
+        <result property="updateTime" column="update_time" jdbcType="TIMESTAMP" javaType="java.time.LocalDateTime"/>
+        <result property="contactNumber" column="contract_number" jdbcType="VARCHAR"/>
+    </resultMap>
+
     <sql id="Base_Column_List">
-        id,receipt_number,payer,
+        id,contract_id,receipt_number,payer,
         payment_method,total_amount,rent,
         property_fee,deposit,water_fee,
-        receipt_reason,accounting_date,generation_date,create_time,
-        update_time
+        receipt_reason,attachment_url,accounting_date,
+        generation_date,create_time,update_time
     </sql>
+    <select id="selectReceiptWithContractByPage" resultMap="VoResultMap">
+        SELECT
+        r.id,
+        r.contract_id,
+        r.receipt_number,
+        r.payer,
+        r.payment_method,
+        r.total_amount,
+        r.rent,
+        r.property_fee,
+        r.deposit,
+        r.water_fee,
+        r.receipt_reason,
+        r.attachment_url,
+        r.accounting_date,
+        r.generation_date,
+        r.create_time,
+        r.update_time,
+        c.contract_number
+        FROM a_receipt_info r
+        LEFT JOIN a_contract_info c ON r.contract_id = c.id
+        <where>
+            <if test="receiptNumber != null and receiptNumber != ''">
+                AND r.receipt_number LIKE CONCAT('%', #{receiptNumber}, '%')
+            </if>
+            <if test="payer != null and payer != ''">
+                AND r.payer LIKE CONCAT('%', #{payer}, '%')
+            </if>
+            <if test="paymentMethod != null and paymentMethod != ''">
+                AND r.payment_method LIKE CONCAT('%', #{paymentMethod}, '%')
+            </if>
+            <if test="contractNumber != null and contractNumber != ''">
+                AND c.contract_number LIKE CONCAT('%', #{contractNumber}, '%')
+            </if>
+        </where>
+        ORDER BY r.create_time DESC
+    </select>
 </mapper>

+ 55 - 5
src/main/resources/mapper/property/ARefundMapper.xml

@@ -6,20 +6,70 @@
 
     <resultMap id="BaseResultMap" type="com.zksy.property.domain.ARefund">
             <id property="id" column="id" jdbcType="VARCHAR"/>
+            <result property="contractId" column="contract_id" jdbcType="VARCHAR"/>
             <result property="unit" column="unit" jdbcType="VARCHAR"/>
             <result property="tenant" column="tenant" jdbcType="VARCHAR"/>
             <result property="deposit" column="deposit" jdbcType="DECIMAL"/>
             <result property="rent" column="rent" jdbcType="DECIMAL"/>
             <result property="propertyFee" column="property_fee" jdbcType="DECIMAL"/>
             <result property="totalAmount" column="total_amount" jdbcType="DECIMAL"/>
-            <result property="generationDate" column="generation_date" javaType="DATE"/>
-            <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
-            <result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
+            <result property="generationDate" column="generation_date" jdbcType="DATE" javaType="java.time.LocalDate"/>
+            <result property="attachmentUrl" column="attachment_url" jdbcType="VARCHAR"/>
+            <result property="createTime" column="create_time" jdbcType="TIMESTAMP" javaType="java.time.LocalDateTime"/>
+            <result property="updateTime" column="update_time" jdbcType="TIMESTAMP" javaType="java.time.LocalDateTime"/>
+    </resultMap>
+
+    <resultMap id="VoResultMap" type="com.zksy.property.domain.vo.ARefundVo">
+        <id property="id" column="id" jdbcType="VARCHAR"/>
+        <result property="contractId" column="contract_id" jdbcType="VARCHAR"/>
+        <result property="unit" column="unit" jdbcType="VARCHAR"/>
+        <result property="tenant" column="tenant" jdbcType="VARCHAR"/>
+        <result property="deposit" column="deposit" jdbcType="DECIMAL"/>
+        <result property="rent" column="rent" jdbcType="DECIMAL"/>
+        <result property="propertyFee" column="property_fee" jdbcType="DECIMAL"/>
+        <result property="totalAmount" column="total_amount" jdbcType="DECIMAL"/>
+        <result property="generationDate" column="generation_date" jdbcType="DATE" javaType="java.time.LocalDate"/>
+        <result property="attachmentUrl" column="attachment_url" jdbcType="VARCHAR"/>
+        <result property="createTime" column="create_time" jdbcType="TIMESTAMP" javaType="java.time.LocalDateTime"/>
+        <result property="updateTime" column="update_time" jdbcType="TIMESTAMP" javaType="java.time.LocalDateTime"/>
+        <result property="contractNumber" column="contract_number" jdbcType="VARCHAR"/>
     </resultMap>
 
     <sql id="Base_Column_List">
-        id,unit,tenant,
+        id,contract_id,unit,tenant,
         deposit,rent,property_fee,
-        total_amount,generation_date,create_time,update_time
+        total_amount,generation_date,attachment_url,
+        create_time,update_time
     </sql>
+
+    <select id="selectRefundWithContractByPage" resultMap="VoResultMap">
+        SELECT
+            r.id,
+            r.contract_id,
+            r.unit,
+            r.tenant,
+            r.deposit,
+            r.rent,
+            r.property_fee,
+            r.total_amount,
+            r.generation_date,
+            r.attachment_url,
+            r.create_time,
+            r.update_time,
+            c.contract_number
+        FROM a_refund r
+        LEFT JOIN a_contract_info c ON r.contract_id = c.id
+        <where>
+            <if test="unit != null and unit != ''">
+                AND r.unit LIKE CONCAT('%', #{unit}, '%')
+            </if>
+            <if test="tenant != null and tenant != ''">
+                AND r.tenant LIKE CONCAT('%', #{tenant}, '%')
+            </if>
+            <if test="contractNumber != null and contractNumber != ''">
+                AND c.contract_number LIKE CONCAT('%', #{contractNumber}, '%')
+            </if>
+        </where>
+        ORDER BY r.create_time DESC
+    </select>
 </mapper>

TEMPAT SAMPAH
src/main/resources/templates/1.docx


TEMPAT SAMPAH
src/main/resources/templates/4.docx


+ 18 - 0
src/test/java/com/zksy/property/service/impl/AContractInfoServiceImplTest.java

@@ -1,11 +1,13 @@
 package com.zksy.property.service.impl;
 
 import com.zksy.property.domain.dto.ContractFormDTO;
+import com.zksy.property.domain.dto.ReceiptDto;
 import com.zksy.property.service.AContractInfoService;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
 
+import java.math.BigDecimal;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -128,4 +130,20 @@ class AContractInfoServiceImplTest {
         aContractInfoService.signContract(dto);
     }
 
+    @Test
+    void getReceipt(){
+        var dto = new ReceiptDto(
+                "1",
+                "1",
+                new BigDecimal(1),
+                new BigDecimal(1),
+                new BigDecimal(1),
+                new BigDecimal(1),
+                new BigDecimal(1),
+                "1"
+        );
+        String receipt = aContractInfoService.getReceipt("31c7f3fdfc236328bce3307645d566dd",dto);
+        System.out.println(receipt);
+    }
+
 }

+ 34 - 0
src/test/java/com/zksy/property/service/impl/ARentalContractServiceImplTest.java

@@ -122,4 +122,38 @@ class ARentalContractServiceImplTest {
         t.setC27("27");
         aRentalContractService.generatorRental("3",t);
     }
+
+    @Test
+    void generatorRental4() {
+        var t = new RentalTempBo();
+        t.setD1("1");
+        t.setD2("2");
+        t.setD3("3");
+        t.setD4("4");
+        t.setD5("5");
+        t.setD6("6");
+        t.setD7("7");
+        t.setD8("8");
+        t.setD9("9");
+        t.setD10("10");
+        t.setD11("11");
+        t.setD12("12");
+        var res = aRentalContractService.generatorRental("4",t);
+        System.out.println(res);
+    }
+
+    @Test
+    void generatorRental5(){
+        var t = new RentalTempBo();
+        t.setE1("1");
+        t.setE2("2");
+        t.setE3("3");
+        t.setE4("4");
+        t.setE5("5");
+        t.setE6("6");
+        t.setE7("7");
+        t.setE8("8");
+        var res = aRentalContractService.generatorRental("5",t);
+        System.out.println(res);
+    }
 }