无关风月
2025-03-18 da81542b6dc8984f639687f30e7e610dc139b085
Merge branch 'master' of https://gitee.com/xiaochen991015/xizang

# Conflicts:
# ruoyi-system/src/main/java/com/ruoyi/system/service/impl/FlowListenerService.java
24个文件已修改
1个文件已添加
487 ■■■■ 已修改文件
generator/src/test/java/com/xizang/CodeGeneratorTests.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TFaultDescribeDicController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TInvoiceController.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/TaskUtil.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application-prod.yml 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application-test.yml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TBillController.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TFaultRepairMessageController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/resources/application-prod.yml 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/resources/application-test.yml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/resources/application.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/TencentMailUtil.java 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/dto/BatchBillDTO.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/dto/OfflinePayCheckDto.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TBillMapper.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/query/TBillQuery.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/TBillService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/TInvoiceService.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/FlowListenerService.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TBillServiceImpl.java 51 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TInvoiceServiceImpl.java 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/TBillMapper.xml 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/THouseMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
generator/src/test/java/com/xizang/CodeGeneratorTests.java
@@ -35,9 +35,9 @@
        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = "F:\\workSpace\\xizang\\generator";
        String projectPath = "D:\\workspaces\\Project\\company\\changyun\\xizang\\xizang\\generator";
        gc.setOutputDir(projectPath + "/src/main/java")
                .setAuthor("xiaochen")
                .setAuthor("yupeng")
                .setMapperName("%sMapper")
                .setXmlName("%sMapper")
                .setServiceName("%sService")
@@ -144,7 +144,7 @@
//         strategy.setTablePrefix(pc.getModuleName() + "");
//        strategy.setLikeTable(new LikeTable("room"));
        //strategy.setLikeTable(new LikeTable("member"));
        strategy.setLikeTable(new LikeTable("t_bank_flow"));// 生成表名
        strategy.setLikeTable(new LikeTable("sys_file"));// 生成表名
//        strategy.setLikeTable(new LikeTable("t_hotel"));// 生成表名
//        strategy.setLikeTable(new LikeTable("t_scan_message"));// 生成表名
//        strategy.setNotLikeTable(new LikeTable("hotel_info"));// 不生成表名
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TFaultDescribeDicController.java
@@ -16,6 +16,7 @@
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.List;
/**
@@ -67,6 +68,7 @@
    @ApiOperation(value = "修改故障描述")
    @PostMapping(value = "/update")
    public R<Boolean> update(@Validated @RequestBody TFaultDescribeDic dto) {
        dto.setUpdateTime(LocalDateTime.now());
        return R.ok(faultDescribeDicService.updateById(dto));
    }
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TInvoiceController.java
@@ -5,6 +5,7 @@
import com.ruoyi.common.basic.PageInfo;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.TencentMailUtil;
import com.ruoyi.system.dto.TBillDto;
import com.ruoyi.system.model.TBill;
import com.ruoyi.system.model.TInvoice;
@@ -57,13 +58,7 @@
    @PostMapping("/uploadVoucher")
    @PreAuthorize("@ss.hasPermi('invoice:list:payment')")
    public R<Boolean> uploadVoucher(@RequestBody TInvoiceQuery query) {
        TInvoice tInvoice = new TInvoice();
        tInvoice.setId(query.getId());
        tInvoice.setInvoiceVoucher(query.getInvoiceVoucher());
        tInvoice.setInvoiceVoucherName(query.getInvoiceVoucherName());
        tInvoice.setInvoiceTime(query.getInvoiceTime());
        tInvoice.setStatus(2);
        return R.ok(invoiceService.updateById(tInvoice));
        return R.ok(invoiceService.uploadVoucher(query));
    }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java
@@ -156,7 +156,7 @@
    @PostMapping("/add")
    public AjaxResult add(@Validated @RequestBody SysUser user)
    {
        user.setUserName(user.getPhonenumber());
        user.setUserName(user.getUserName());
        if (!userService.checkUserNameUnique(user))
        {
            return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/TaskUtil.java
@@ -2,6 +2,7 @@
//
//
//import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
//import com.ruoyi.common.utils.SmsUtil;
//import com.ruoyi.system.mapper.TBillMapper;
//import com.ruoyi.system.model.TBill;
//import com.ruoyi.system.model.TContract;
@@ -40,7 +41,6 @@
//    // 用于更新违约金账单
//    // 每分钟执行一次的定时任务
//
////    @Scheduled(cron = "0 0 0 * * ?")
//    @Scheduled(cron = "0 * * * * ?")
//    public void dayOfProportionBill() {
//        try {
@@ -79,23 +79,5 @@
//        }
//    }
//
//    public static void main(String[] args) {
//
////        LocalDateTime now = LocalDateTime.now().minusMonths(1).withDayOfMonth(31);
////        System.err.println(now);
////        LocalDateTime now2 = now.plusMonths(1);
////        System.err.println(now2);
////
////        LocalDateTime now1 = LocalDateTime.now();
////        long days = ChronoUnit.DAYS.between(now, now1);
////        long days2 = ChronoUnit.DAYS.between(now.plusDays(1), now1);
////
////        System.err.println(days);
////        System.err.println(days2);
////        LocalDateTime endTime = now.with(TemporalAdjusters.lastDayOfMonth()).withSecond(59).withHour(23).withMinute(59);
////
////        System.err.println(endTime);
//
//    }
//
//}
ruoyi-admin/src/main/resources/application-prod.yml
@@ -14,14 +14,13 @@
  addressEnabled: false
  # 验证码类型 math 数字计算 char 字符验证
  captchaType: math
# 开发环境配置
server:
  # 服务器的HTTP端口,默认为8080
  port: 8081
  port: 8080
  servlet:
    # 应用的访问路径
    context-path: /admin
    context-path: /
  tomcat:
    # tomcat的URI编码
    uri-encoding: UTF-8
@@ -70,13 +69,20 @@
  # redis 配置
  redis:
    # 地址
    #    host: 127.0.0.1
    #    # 端口,默认为6379
    #    port: 6379
    #    # 数据库索引
    #    database: 0
    #    # 密码
    #    password: 123456
    host: 127.0.0.1
    # 端口,默认为6379
    port: 6379
    port: 16379
    # 数据库索引
    database: 0
    # 密码
    password: 123456
    password: 8f5z9g52gx4bg
    # 连接超时时间
    timeout: 10s
    lettuce:
@@ -96,9 +102,9 @@
    druid:
      # 主库数据源
      master:
        url: jdbc:mysql://127.0.0.1:10633/xizang?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=Asia/Shanghai
        username: root
        password: XiZang@2025!
        url: jdbc:mysql://172.27.0.13:3306/xizang?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai
        username: xzgt
        password: changyun!6f2gshj6h3j
      # 从库数据源
      slave:
        # 从数据源开关/默认关闭
@@ -149,7 +155,6 @@
        wall:
          config:
            multi-statement-allow: true
# token配置
token:
  # 令牌自定义标识
@@ -167,7 +172,7 @@
    db-config:
      logic-not-delete-value: 0
      logic-delete-value: 1
  type-aliases-package: com.ruoyi.**.domain,com.ruoyi.**.vo
  type-aliases-package: com.ruoyi.**.domain,com.ruoyi.**.vo,com.ruoyi.**.model
  # 指定Mapper文件位置
  mapper-locations: classpath*:mapper/**/*.xml
@@ -193,10 +198,12 @@
    qrLocation: /file/qrCode/
    accessPath: /file/
    allowExt: .jpg|.png|.gif|.jpeg|.doc|.docx|.apk|.MP4|.mp4|.pdf|.PDF
  url:
    prefix: https://xzgt.test.591taxi.cn:${server.port}${server.servlet.context-path}
wx:
  config:
    appId: wxc3985a05da7d86dc
    secret: 5cca42633c25439613b328c08ef20cc9
  conf:
    appId: wxe91f1af7638aa5dd
    secretId: a787e1a462715604e0c9528b6d8960d1
#OSS及短信配置
code:
  config:
@@ -214,6 +221,12 @@
    bucketAddr: ap-chengdu
    rootSrc: https://xzgttest-1305134071.cos.ap-chengdu.myqcloud.com/
    location: /xizang
sms:
  enable: true
  appId: 1400957506
  secretid: AKIDCF5EF2c0DE1e5JK8r4EGJF4mNsMgp26x
  secretkey: lLl184rUyFOOE0d5KNGC3kmfNsCWk4GU
  sign: 畅云出行
com:
  taxi591:
    bank:
ruoyi-admin/src/main/resources/application-test.yml
@@ -162,7 +162,7 @@
  # 令牌密钥
  secret: abcdefghijklmnopqrstuvwxyz
  # 令牌有效期(默认30分钟)
  expireTime: 120
  expireTime: 1
mybatis-plus:
  # 此处在多数据源中生效
@@ -199,7 +199,8 @@
    accessPath: /file/
    allowExt: .jpg|.png|.gif|.jpeg|.doc|.docx|.apk|.MP4|.mp4|.pdf|.PDF
  url:
    prefix: http://localhost:${server.port}${server.servlet.context-path}
#    prefix: http://localhost:${server.port}${server.servlet.context-path}
    prefix: https://xzgt.test.591taxi.cn:${server.port}${server.servlet.context-path}
wx:
  conf:
    appId: wxe91f1af7638aa5dd
ruoyi-admin/src/main/resources/application.yml
@@ -1,4 +1,4 @@
# 项目相关配置
spring:
  profiles:
    active: test
    active: prod
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TBillController.java
@@ -1,6 +1,7 @@
package com.ruoyi.web.controller.api;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.basic.PageInfo;
import com.ruoyi.common.constant.DictConstants;
@@ -10,6 +11,7 @@
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.dto.BatchBillDTO;
import com.ruoyi.system.dto.TBillDto;
import com.ruoyi.system.dto.TInvoiceDTO;
import com.ruoyi.system.model.*;
@@ -82,6 +84,18 @@
        return R.ok(billIds);
    }
    @ApiOperation(value = "跳转批量缴费")
    @PostMapping("/batchBill")
    public R<String> batchBill(@RequestBody BatchBillDTO dto){
        String userId = tokenService.getLoginUserApplet().getUserId();
        List<String> billIds = dto.getBillIds();
        Integer count = tBillService.batchBillCount(userId, billIds);
        if(count>0){
            return R.fail("请优先缴纳水电费");
        }
        return R.ok();
    }
    @ApiOperation(value = "查看缴费账单详情")
    @GetMapping(value = "/getDetailById")
    public R<TBillVO> getDetailById(@RequestParam String id) {
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TFaultRepairMessageController.java
@@ -134,7 +134,7 @@
            for (SysUser sysUser : sysUsers) {
                if (StringUtils.hasLength(sysUser.getPhonenumber())){
                    System.err.println("发送短信");
                    smsUtil.sendSms(sysUser.getPhonenumber(),"2375194",new String[]{""});
                    smsUtil.sendSms(sysUser.getPhonenumber(),"2375194",new String[]{});
                }
            }
        }
ruoyi-applet/src/main/resources/application-prod.yml
@@ -70,13 +70,20 @@
  # redis 配置
  redis:
    # 地址
    #    host: 127.0.0.1
    #    # 端口,默认为6379
    #    port: 6379
    #    # 数据库索引
    #    database: 0
    #    # 密码
    #    password: 123456
    host: 127.0.0.1
    # 端口,默认为6379
    port: 6379
    port: 16379
    # 数据库索引
    database: 0
    # 密码
    password: 123456
    password: 8f5z9g52gx4bg
    # 连接超时时间
    timeout: 10s
    lettuce:
@@ -96,9 +103,9 @@
    druid:
      # 主库数据源
      master:
        url: jdbc:mysql://127.0.0.1:10633/xizang?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=Asia/Shanghai
        username: root
        password: XiZang@2025!
        url: jdbc:mysql://172.27.0.13:3306/xizang?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai
        username: xzgt
        password: changyun!6f2gshj6h3j
      # 从库数据源
      slave:
        # 从数据源开关/默认关闭
@@ -149,7 +156,6 @@
        wall:
          config:
            multi-statement-allow: true
# token配置
token:
  # 令牌自定义标识
@@ -167,7 +173,7 @@
    db-config:
      logic-not-delete-value: 0
      logic-delete-value: 1
  type-aliases-package: com.ruoyi.**.domain,com.ruoyi.**.vo
  type-aliases-package: com.ruoyi.**.domain,com.ruoyi.**.vo,com.ruoyi.**.model
  # 指定Mapper文件位置
  mapper-locations: classpath*:mapper/**/*.xml
@@ -193,10 +199,12 @@
    qrLocation: /file/qrCode/
    accessPath: /file/
    allowExt: .jpg|.png|.gif|.jpeg|.doc|.docx|.apk|.MP4|.mp4|.pdf|.PDF
  url:
    prefix: https://xzgt.test.591taxi.cn:${server.port}${server.servlet.context-path}
wx:
  config:
    appId: wxc3985a05da7d86dc
    secret: 5cca42633c25439613b328c08ef20cc9
  conf:
    appId: wxe91f1af7638aa5dd
    secretId: a787e1a462715604e0c9528b6d8960d1
#OSS及短信配置
code:
  config:
ruoyi-applet/src/main/resources/application-test.yml
@@ -199,6 +199,9 @@
    qrLocation: /file/qrCode/
    accessPath: /file/
    allowExt: .jpg|.png|.gif|.jpeg|.doc|.docx|.apk|.MP4|.mp4|.pdf|.PDF
  url:
#    prefix: http://localhost:${server.port}${server.servlet.context-path}
    prefix: https://xzgt.test.591taxi.cn:${server.port}${server.servlet.context-path}
wx:
  conf:
    appId: wxe91f1af7638aa5dd
ruoyi-applet/src/main/resources/application.yml
@@ -1,4 +1,4 @@
# 项目相关配置
spring:
  profiles:
    active: test
    active: prod
ruoyi-common/src/main/java/com/ruoyi/common/utils/TencentMailUtil.java
@@ -6,11 +6,20 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import java.net.URLEncoder;
import javax.activation.URLDataSource;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.*;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
@@ -43,7 +52,8 @@
        // 访问SMTP服务时需要提供的密码(在控制台选择发信地址进行设置)
        props.put("mail.password", properties.getPassword());
        props.setProperty("mail.smtp.socketFactory.fallback", "false");
        props.put("mail.smtp.ssl.enable", "false");
        props.put("mail.smtp.ssl.enable", "true");
        props.put("mail.smtp.ssl.protocols", "TLSv1.2");
        // 构建授权信息,用于进行SMTP进行身份验证
        Authenticator authenticator = new Authenticator() {
            @Override
@@ -94,17 +104,100 @@
            e.printStackTrace();
            log.error("发送邮件发生异常",e);
        }
    }
    public static void main(String[] args) {
        TencentMailUtil tencentMailUtil = new TencentMailUtil();
        MailProperties properties = new MailProperties();
        tencentMailUtil.properties = properties;
        tencentMailUtil.send("214491528@qq.com","大学城揽院24栋");
    public void sendInvoice(String emailAddress, List<Map<String, String>> list) {
        // 异步发送邮件
        CompletableFuture.runAsync(() -> {
            try {
                sendEmail(emailAddress, list);
            } catch (ServiceException e) {
                log.error("邮件发送失败", e);
            }
        });
    }
    private void sendEmail(String emailAddress, List<Map<String, String>> list) throws ServiceException {
        try {
            // 创建邮件会话
            Session mailSession = Session.getInstance(getMailProperties(), getAuthenticator());
            // 创建邮件消息
            MimeMessage message = new MimeMessage(mailSession);
            // 设置发件人
            InternetAddress from = new InternetAddress(properties.getUserAddr(), properties.getUserName());
            message.setFrom(from);
            // 设置收件人
            InternetAddress to = new InternetAddress(emailAddress);
            message.setRecipient(MimeMessage.RecipientType.TO, to);
            // 设置邮件标题
            message.setSubject("发票");
            // 创建邮件内容
            Multipart multipart = createMultipart(list);
            // 设置邮件内容
            message.setContent(multipart);
            // 发送邮件
            Transport.send(message);
        } catch (MessagingException | UnsupportedEncodingException | MalformedURLException e) {
            log.error("发送邮件发生异常", e);
            throw new ServiceException("发送邮件失败, 请检查");
        }
    }
    private Properties getMailProperties() {
        Properties props = new Properties();
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.host", properties.getSmtpHost());
        props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
        props.put("mail.smtp.socketFactory.port", properties.getSmtpPort());
        props.put("mail.smtp.port", properties.getSmtpPort());
        props.put("mail.user", properties.getUserAddr());
        props.put("mail.password", properties.getPassword());
        props.setProperty("mail.smtp.socketFactory.fallback", "false");
        props.put("mail.smtp.ssl.enable", "true");
        props.put("mail.smtp.ssl.protocols", "TLSv1.2");
        return props;
    }
    private Authenticator getAuthenticator() {
        return new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                String userName = properties.getUserAddr();
                String password = properties.getPassword();
                return new PasswordAuthentication(userName, password);
            }
        };
    }
    private Multipart createMultipart(List<Map<String, String>> list) throws MessagingException, UnsupportedEncodingException, MalformedURLException {
        Multipart multipart = new MimeMultipart();
        // 添加文本消息部分
        BodyPart messageBodyPart = new MimeBodyPart();
        messageBodyPart.setHeader("Content-Type", "text/plain;charset=utf-8");
        messageBodyPart.setContent("您在小程序提交的开票申请已开票成功,请查看附件内容","text/html;charset=UTF-8");
        multipart.addBodyPart(messageBodyPart);
        // 添加附件部分
        for (Map<String, String> map : list) {
            messageBodyPart = new MimeBodyPart();
            String url = map.get("url");
            String fileName = map.get("fileName");
            URLDataSource source = new URLDataSource(new URL(url));
            messageBodyPart.setDataHandler(new DataHandler(source));
            String filenameEncode = MimeUtility.encodeText(fileName, "UTF-8", "base64");
            messageBodyPart.setFileName(filenameEncode);
            messageBodyPart.setHeader("Content-Transfer-Encoding", "base64");
            messageBodyPart.setHeader("Content-Disposition", "attachment");
            messageBodyPart.setHeader("Content-Type", "application/octet-stream;name=\"" + filenameEncode + "\"");
            multipart.addBodyPart(messageBodyPart);
        }
        return multipart;
    }
   // public static void main(String[] args) throws UnsupportedEncodingException {
   //     TencentMailUtil tencentMailUtil = new TencentMailUtil();
   //     MailProperties properties = new MailProperties();
   //     tencentMailUtil.properties = properties;
   //     tencentMailUtil.send("645025773@qq.com","大学城揽院24栋");
   // }
}
ruoyi-system/src/main/java/com/ruoyi/system/dto/BatchBillDTO.java
New file
@@ -0,0 +1,18 @@
package com.ruoyi.system.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
@ApiModel(value = "批量缴费校验")
public class BatchBillDTO implements Serializable {
    @ApiModelProperty(value = "账单id集合")
    private List<String> billIds;
}
ruoyi-system/src/main/java/com/ruoyi/system/dto/OfflinePayCheckDto.java
@@ -38,7 +38,11 @@
    @ApiModelProperty("支付凭证")
    private String voucher;
    @ApiModelProperty("银行流水ID")
    @NotEmpty(message = "银行流水ID不能为空")
    private String flowId;
    @ApiModelProperty("支付类型")
    @NotEmpty(message = "支付类型不能为空")
    private Integer payType;
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TBillMapper.java
@@ -44,4 +44,6 @@
    BigDecimal statisticsPayed();
    BigDecimal statisticsOverdue();
    Integer batchBillCount(@Param("userId")String userId, @Param("billIds")List<String> billIds);
}
ruoyi-system/src/main/java/com/ruoyi/system/query/TBillQuery.java
@@ -22,6 +22,11 @@
     */
    @ApiModelProperty("租户ID")
    private String userId;
    /**
     * 账单类型
     */
    @ApiModelProperty("账单类型 1=租金 2=押金 3=生活费用 4=房屋验收")
    private Integer billType;
ruoyi-system/src/main/java/com/ruoyi/system/service/TBillService.java
@@ -85,4 +85,6 @@
    Boolean cashPay(CachPayDto offlinePayDto);
    BillStatisticsDto statistics();
    Integer batchBillCount(String userId, List<String> billIds);
}
ruoyi-system/src/main/java/com/ruoyi/system/service/TInvoiceService.java
@@ -19,4 +19,5 @@
public interface TInvoiceService extends IService<TInvoice> {
    PageInfo<TInvoice> pageList(TInvoiceQuery query);
    List<TInvoice> makeQuery(TInvoiceQuery query);
    Boolean uploadVoucher(TInvoiceQuery query);
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/FlowListenerService.java
@@ -31,14 +31,8 @@
import com.ruoyi.common.enums.SubmitStatusEnum;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.system.mapper.TCheckAcceptRecordMapper;
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.service.ISysRoleService;
import com.ruoyi.system.service.TBillService;
import com.ruoyi.system.service.TContractRentTypeService;
import com.ruoyi.system.service.TContractService;
import com.ruoyi.system.model.*;
import com.ruoyi.system.service.*;
import com.ruoyi.system.task.base.QuartzManager;
import com.ruoyi.system.task.base.TimeJobType;
import com.ruoyi.system.task.jobs.StateProcessJob;
@@ -84,6 +78,7 @@
    private final TContractRentTypeService contractRentTypeService;
    private final TBillService billService;
    private final TCheckAcceptRecordMapper checkAcceptRecordMapper;
    private final THouseService houseService;
    public static void main(String[] args) {
//        LocalDate localDate1 = LocalDate.now().withYear(2025).withMonth(4).withDayOfMonth(1);
@@ -268,16 +263,24 @@
        switch (categoryEnum) {
            case CATEGORY1: {
                // 合同新增审批
                int submitStatus = status == 0 ? 2 : (status == 1 ? 3 : 5);
                int submitStatus = status == 0 ? 2 : (status == 1 ? 3 : 1);
                contractService.updateContractAuditStatus(processParameter.getString("projectId"), submitStatus);
                // TODO 发短信
                break;
            }
            case CATEGORY2: {
                // 合同签订审批
                int submitStatus = status == 0 ? 3 : (status == 1 ? 4 : 5);
                int submitStatus = status == 0 ? 3 : (status == 1 ? 4 : 1);
                contractService.updateContractAuditStatus(processParameter.getString("projectId"), submitStatus);
                TContract contract = contractService.getById(processParameter.getString("projectId"));
                if(contract.getStatus().equals("4")){
                    // 修改房屋状态
                    THouse house = houseService.getById(contract.getHouseId());
                    if(Objects.nonNull(house)){
                        house.setLeaseStatus("2");
                        houseService.updateById(house);
                    }
                List<TContractRentType> contractRentTypes = contractRentTypeService.list();
                TContractRentType tContractRentType = contractRentTypes.stream().filter(e -> e.getContractId().equals(contract.getId())).findFirst().orElse(null);
@@ -387,9 +390,7 @@
                            if (beforeBill.getEndTime().plusMonths(contract.getPayType().equals("1") ? 1 : contract.getPayType().equals("2") ? 3 : 12).getDayOfMonth() <= 15) {
                                tBill.setPayableFeesTime(contract.getEndTime().toLocalDate());
                            } else {
                                tBill.setPayableFeesTime((contract.getPayType().equals("1") ?
                                        beforeBill.getEndTime().plusMonths(1).withDayOfMonth(15).toLocalDate() : contract.getPayType().equals("2") ?
                                        beforeBill.getEndTime().plusMonths(3).withDayOfMonth(15).toLocalDate() : beforeBill.getEndTime().withDayOfMonth(15).plusMonths(12).toLocalDate()));
                                    tBill.setPayableFeesTime(beforeBill.getEndTime().plusMonths(1).withDayOfMonth(15).toLocalDate());
                            }
                            tBill.setContractId(contract.getId());
                            if (contract.getIsIncreasing()) {
@@ -457,6 +458,7 @@
                                            // 租金递增递减的时长 天
                                            long moneyDays = Math.abs(ChronoUnit.DAYS.between(tContractRentType.getChangeTime(), tBill.getEndTime()))+1;
                                            // 递增递减的租金
                                            BigDecimal contractRentTypeMoney = new BigDecimal("0");
                                            // 不递增递减的租金
                                            BigDecimal originalMoney = new BigDecimal("0");
@@ -639,6 +641,7 @@
                                    tBill.setPayableFeesMoney(contractRentTypeMoney.add(originalMoney));
                                    tBill.setOutstandingMoney(tBill.getPayableFeesMoney());
                                    contractService.updateById(contract);
                                }
                            }
                        } else {
@@ -667,6 +670,7 @@
                    }
                    billService.save(tBill);
                }
                }
                break;
            }
            case CATEGORY3: {
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TBillServiceImpl.java
@@ -241,8 +241,12 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean checkOfflinePay(OfflinePayCheckDto dto) {
        TBankFlow bankflow = tBankFlowService.getById(dto.getFlowId());
        TBillDto bill = getDetailByBillId(dto.getBillId());
        if (dto.getPayType()==1){ //银行
            if (StringUtils.isEmpty(dto.getFlowId())){
                throw new ServiceException("银行流水ID不能为空");
            }
            TBankFlow bankflow = tBankFlowService.getById(dto.getFlowId());
        if (bankflow.getRemainingMoney().compareTo(BigDecimal.ZERO)<=0){
            throw new ServiceException("该流水已无可抵扣剩余金额");
        }
@@ -253,7 +257,6 @@
        if (dto.getAmount().compareTo(bill.getOutstandingMoney())>0){
            throw new ServiceException("实付金额不能高于该账单欠费金额");
        }
        TBill billSave = new TBill();
        billSave.setId(bill.getId());
        billSave.setPayFeesMoney(dto.getAmount());
@@ -262,13 +265,17 @@
        billSave.setVoucher(dto.getVoucher());
        billSave.setPayFeesType(2);
        TBill back = lockAndUpdateInfo(billSave, 2);
        //更新银行流水的已抵扣金额和剩余可抵扣金额
        TBankFlow saveBankFlow = new TBankFlow();
        saveBankFlow.setId(bankflow.getId());
        saveBankFlow.setDeductionMoney(bankflow.getDeductionMoney().add(dto.getAmount()));
        saveBankFlow.setRemainingMoney(bankflow.getRemainingMoney().subtract(dto.getAmount()));
            BigDecimal subtract = bankflow.getRemainingMoney().subtract(dto.getAmount());
            saveBankFlow.setRemainingMoney(subtract);
            if (BigDecimal.ZERO.compareTo(subtract) == 0){
                saveBankFlow.setFlowStatus(1);
            }
        tBankFlowService.updateById(saveBankFlow);
            //更新银行流水的已抵扣金额和剩余可抵扣金额
        //存流水
        TFlowManagement save = new TFlowManagement();
        save.setPayType(3);
@@ -277,6 +284,29 @@
        save.setSysSerialNumber(OrderNos.getDid(30));
        save.setBankSerialNumber(bankflow.getBankSerialNumber());
        save.setFlowType(2);
            save.setPaymentBillId(back.getId());
            save.setDeductionMoney(back.getDeductionMoney());
            save.setFlowMoney(dto.getAmount());
            save.setRemainingMoney(back.getOutstandingMoney());
            save.setPreOutstand(back.getPreOutstand());
            tFlowManagementService.save(save);
            return true;
        }
        //现金支付
        TBill billSave = new TBill();
        billSave.setId(bill.getId());
        billSave.setPayFeesMoney(dto.getAmount());
        billSave.setPayFeesTime(dto.getPayTime()!=null?dto.getPayTime():DateUtils.dateToLocalDateTime(new Date()));
        billSave.setVoucher(dto.getVoucher());
        billSave.setPayFeesType(2);
        TBill back = lockAndUpdateInfo(billSave, 2);
        TFlowManagement save = new TFlowManagement();
        save.setPayType(3);
        save.setPayer(dto.getPayer());
        save.setPayTime(billSave.getPayFeesTime());
        save.setSysSerialNumber(OrderNos.getDid(30));
        save.setFlowType(1);
        save.setPaymentBillId(back.getId());
        save.setDeductionMoney(back.getDeductionMoney());
        save.setFlowMoney(dto.getAmount());
@@ -483,7 +513,11 @@
            TBankFlow saveBankFlow = new TBankFlow();
            saveBankFlow.setId(bankflow.getId());
            saveBankFlow.setDeductionMoney(bankflow.getDeductionMoney().add(dto.getAmount()));
            saveBankFlow.setRemainingMoney(bankflow.getRemainingMoney().subtract(dto.getAmount()));
            BigDecimal subtract = bankflow.getRemainingMoney().subtract(dto.getAmount());
            saveBankFlow.setRemainingMoney(subtract);
            if (BigDecimal.ZERO.compareTo(subtract) == 0){
                saveBankFlow.setFlowStatus(1);
            }
            tBankFlowService.updateById(saveBankFlow);
        }
        //存流水
@@ -513,5 +547,10 @@
        return dto;
    }
    @Override
    public Integer batchBillCount(String userId, List<String> billIds) {
        return this.baseMapper.batchBillCount(userId,billIds);
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TInvoiceServiceImpl.java
@@ -3,7 +3,9 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.basic.PageInfo;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.TencentMailUtil;
import com.ruoyi.system.mapper.TInvoiceMapper;
import com.ruoyi.system.model.TInvoice;
import com.ruoyi.system.query.TInvoiceQuery;
@@ -12,7 +14,10 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
/**
 * <p>
@@ -28,6 +33,8 @@
    TInvoiceToBillServiceImpl tInvoiceToBillService;
    @Autowired
    TBillService tBillService;
    @Resource
    TencentMailUtil mailUtil;
    @Override
    public PageInfo<TInvoice> pageList(TInvoiceQuery query) {
@@ -49,4 +56,94 @@
                .orderByDesc(TInvoice::getCreateTime);
        return this.baseMapper.selectList(queryWrapper);
    }
    @Override
    public Boolean uploadVoucher(TInvoiceQuery query) {
        // 检查是否存在对应的发票记录
        TInvoice preexist = getById(query.getId());
        if (preexist == null) {
            log.error("未找到对应的发票记录,ID: {}"+query.getId());
            return false;
        }
        // 更新发票信息
        TInvoice tInvoice = new TInvoice();
        tInvoice.setId(query.getId());
        tInvoice.setInvoiceVoucher(query.getInvoiceVoucher());
        tInvoice.setInvoiceVoucherName(query.getInvoiceVoucherName());
        tInvoice.setInvoiceTime(query.getInvoiceTime());
        tInvoice.setStatus(2);
        // 处理附件信息
        List<Map<String, String>> attachments = buildAttachments(query.getInvoiceVoucher(), query.getInvoiceVoucherName());
        if (attachments.isEmpty()) {
            log.warn("未找到有效的附件信息");
            return updateById(tInvoice);
        }
        // 异步发送邮件
        CompletableFuture.runAsync(() -> {
            try {
                mailUtil.sendInvoice(preexist.getEmail(), attachments);
            } catch (ServiceException e) {
                log.error("邮件发送失败", e);
            }
        });
        // 更新数据库
        return updateById(tInvoice);
    }
    private List<Map<String, String>> buildAttachments(String invoiceVoucher, String invoiceVoucherName) {
        if (invoiceVoucher == null || invoiceVoucherName == null) {
            return Collections.emptyList();
        }
        String[] voucherUrls = invoiceVoucher.split(",");
        String[] voucherNames = invoiceVoucherName.split(",");
        // 确保两个数组长度一致
        int length = Math.min(voucherUrls.length, voucherNames.length);
        if (length == 0) {
            return Collections.emptyList();
        }
        // 构建附件列表
        List<Map<String, String>> attachments = new ArrayList<>(length);
        for (int i = 0; i < length; i++) {
            Map<String, String> attachment = new HashMap<>(2); // 初始容量为2,避免扩容
            attachment.put("url", voucherUrls[i]);
            attachment.put("fileName", voucherNames[i]);
            attachments.add(attachment);
        }
        return attachments;
    }
    // @Override
    // public Boolean uploadVoucher(TInvoiceQuery query) {
    //     TInvoice preexist = getById(query.getId());
    //     if (preexist == null){
    //         return false;
    //     }
    //     TInvoice tInvoice = new TInvoice();
    //     tInvoice.setId(query.getId());
    //     tInvoice.setInvoiceVoucher(query.getInvoiceVoucher());
    //     tInvoice.setInvoiceVoucherName(query.getInvoiceVoucherName());
    //     tInvoice.setInvoiceTime(query.getInvoiceTime());
    //     tInvoice.setStatus(2);
    //     List<Map<String, String>> mapArrayList = new ArrayList<>();
    //     String invoiceVoucher = query.getInvoiceVoucher();
    //     String invoiceVoucherName = query.getInvoiceVoucherName();
    //
    //     List<String> list = Arrays.stream(invoiceVoucher.split(",")).collect(Collectors.toList());
    //     for (int i = 0; i < list.size()-1; i++) {
    //         Map<String, String> map = new HashMap<>();
    //         map.put("url", list.get(i));
    //         map.put("fileName",invoiceVoucherName.split(",")[i]);
    //         mapArrayList.add(map);
    //     }
    //     mailUtil.sendInvoice(preexist.getEmail(),mapArrayList);
    //     return updateById(tInvoice);
    // }
}
ruoyi-system/src/main/resources/mapper/system/TBillMapper.xml
@@ -93,6 +93,9 @@
            <if test="query.userId != null and query.userId !=''">
                and t.id = #{query.userId}
            </if>
            <if test="query.billType != null">
                and b.bill_type = #{query.billType}
            </if>
            and b.disabled = ${@com.ruoyi.common.enums.DisabledEnum@NO.getCode()}
        </where>
        order by b.bill_type,b.payable_fees_time
@@ -162,4 +165,25 @@
    <select id="statisticsOverdue" resultType="java.math.BigDecimal">
        SELECT ifnull(sum(outstanding_money),0) as amount FROM t_bill where pay_fees_status=4
    </select>
    <select id="batchBillCount" resultType="java.lang.Integer">
        SELECT
            count(b.id)
        FROM
            t_bill b
        LEFT JOIN t_contract c ON c.contract_number = b.contract_number and c.disabled=0
        LEFT JOIN t_house h ON h.id = c.house_id and h.disabled=0
        LEFT JOIN t_tenant t ON t.id = c.tenant_id and t.disabled=0
        <where>
            <if test="userId != null and userId !=''">
                AND t.id = #{userId}
            </if>
            <if test="billIds != null and billIds.size() > 0">
                AND b.id NOT IN
                <foreach collection="billIds" item="item" index="index" open="(" separator="," close=")">
                    #{item}
                </foreach>
            </if>
            AND b.bill_type = 3
        </where>
    </select>
</mapper>
ruoyi-system/src/main/resources/mapper/system/THouseMapper.xml
@@ -53,7 +53,7 @@
                and (t2.start_time is not null)
            </if>
            <if test="req.leaseStatus == 1">
                and (t2.start_time is null)
                and (t2.start_time is null) and t1.lease_status = 1
            </if>
            AND t1.disabled = ${@com.ruoyi.common.enums.DisabledEnum@NO.getCode()}
        </where>