无关风月
2025-02-11 f1b421b67ecf52431388d7b66e5e950d6d139ac4
Merge branches 'master' and 'master' of https://gitee.com/xiaochen991015/xizang
30个文件已修改
57个文件已添加
3657 ■■■■■ 已修改文件
bankapi/pom.xml 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
bankapi/src/main/java/com/taxi591/bankapi/BankConfig.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
bankapi/src/main/java/com/taxi591/bankapi/BankProperties.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
bankapi/src/main/java/com/taxi591/bankapi/dto/ChargeBillRequest.java 175 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
bankapi/src/main/java/com/taxi591/bankapi/dto/ChargeBillResponse.java 158 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
bankapi/src/main/java/com/taxi591/bankapi/dto/CovertPayBackResult.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
bankapi/src/main/java/com/taxi591/bankapi/service/BankService.java 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
bankapi/src/main/java/com/taxi591/bankapi/service/SignatureAndVerification.java 198 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
bankapi/src/main/java/com/taxi591/bankapi/utils/Base64.java 210 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
bankapi/src/main/java/com/taxi591/bankapi/utils/Base64Util.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
bankapi/src/main/java/com/taxi591/bankapi/utils/HttpClientUtils.java 302 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
bankapi/src/main/resources/META-INF/spring.factories 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generator/src/test/java/com/xizang/CodeGeneratorTests.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/pom.xml 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/BankOutController.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TBankFlowController.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TBillConfirmController.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TBillController.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TBillDetailController.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TFlowManagementController.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TInvoiceController.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TInvoiceToBillController.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/task/TbillTask.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application-prod.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application-test.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/pom.xml 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/PayController.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TBillConfirmController.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TBillController.java 84 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TInformationController.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/constant/AmountConstant.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/constant/DictConstants.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/constant/WxConstant.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java 46 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/OrderNos.java 164 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/dto/MakeOrderDto.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/dto/MakeOrderResp.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/dto/TBillDto.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/dto/TInvoiceDTO.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TBankFlowMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TBillConfirmMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TBillDetailMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TBillMapper.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TInvoiceToBillMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TOrderBillMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TPayOrderMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TBankFlow.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TBillConfirm.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TBillDetail.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TInvoiceToBill.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TOrderBill.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TPayOrder.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/query/TBankFlowQuery.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/query/TBillQuery.java 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/query/TFlowManagementQuery.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/query/TInvoiceQuery.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/TBankFlowService.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/TBillConfirmService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/TBillDetailService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/TBillService.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/TFlowManagementService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/TInvoiceService.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/TInvoiceToBillService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/TOrderBillService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/TPayOrderService.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TBankFlowServiceImpl.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TBillConfirmServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TBillDetailServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TBillServiceImpl.java 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TFlowManagementServiceImpl.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TInvoiceServiceImpl.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TInvoiceToBillServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TOrderBillServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TPayOrderServiceImpl.java 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/vo/TBillVO.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/TBankFlowMapper.xml 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/TBillConfirmMapper.xml 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/TBillDetailMapper.xml 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/TBillMapper.xml 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/TInvoiceToBillMapper.xml 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/vue.config.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
bankapi/pom.xml
New file
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.ruoyi</groupId>
        <artifactId>ruoyi</artifactId>
        <version>3.8.6</version>
    </parent>
    <description>
        银行接口模块
    </description>
    <groupId>org.taxi591</groupId>
    <artifactId>bankapi</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <properties>
        <httpclient_version>4.5.2</httpclient_version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-framework</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>${httpclient_version}</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.5.15</version>
                <configuration>
                    <fork>true</fork> <!-- 如果没有该配置,devtools不会生效 -->
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.1.0</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                    <warName>${project.artifactId}</warName>
                </configuration>
            </plugin>
        </plugins>
        <finalName>${project.artifactId}</finalName>
    </build>
</project>
bankapi/src/main/java/com/taxi591/bankapi/BankConfig.java
New file
@@ -0,0 +1,17 @@
package com.taxi591.bankapi;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties({BankProperties.class})
@ConditionalOnProperty(value = BankProperties.ENBALE_PREFIX, matchIfMissing = true)
@ComponentScan("com.taxi591.bankapi.service")
public class BankConfig {
}
bankapi/src/main/java/com/taxi591/bankapi/BankProperties.java
New file
@@ -0,0 +1,36 @@
package com.taxi591.bankapi;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import static com.taxi591.bankapi.BankProperties.PREFIX;
@Data
@ConfigurationProperties(prefix = PREFIX)
public class BankProperties {
    public static final String PREFIX = "com.taxi591.bank";
    public static final String ENBALE_PREFIX = PREFIX+".enable";
    private Boolean enable = true;
    private String baseUrl="http://hello.enjoy.abchina.com";
    /**
     * 证书路径
     */
    private String pfxPath;
    /**
     * 密码
     */
    private String keystorePassword;
    /**
     * 公钥路径
     */
    private String cerPath;
}
bankapi/src/main/java/com/taxi591/bankapi/dto/ChargeBillRequest.java
New file
@@ -0,0 +1,175 @@
package com.taxi591.bankapi.dto;
import com.alibaba.fastjson2.JSON;
import lombok.Data;
import java.io.Serializable;
/**
 *  直连商户平台缴费销账输入对象,需要转换成json串发送给第三方系统
 *  @author DELL
 *
 */
@Data
public class ChargeBillRequest implements Serializable {
    private static final long serialVersionUID = 1L;
    /** 格式 */
    private String format;
    /** 消息 */
    private Message message;
    @Override
    public String toString() {
        return "ChargeBillRequest[format=" + format + ",message=" + message.toString() + "]";
    }
    /**
     *
     * 账单查询内部消息对象实体message内部类
     *
     */
    @Data
    public class Message implements Serializable {
        private static final long serialVersionUID = 1L;
        /** 消息头部 */
        private Head head;
        /** 消息体  */
        private Info info;
        @Override
        public String toString() {
            return JSON.toJSONString(this);
        }
        /**
         *  message子对象head消息头内部类
         */
        @Data
        public class Head implements Serializable {
            private static final long serialVersionUID = 1L;
            /**  渠道编码 */
            private String channel;
            /**  交易码  */
            private String transCode;
            /**  交易上行下送标志位  */
            private String transFlag;
            /**  缴费中心交易序列号 */
            private String transSeqNum;
            /**   时间戳  */
            private String timeStamp;
            /**   4为分行iGoal码  */
            private String branchCode;
            @Override
            public String toString() {
                return JSON.toJSONString(this);
            }
        }
        /**
         * message子对象info消息实体内部类
         */
        @Data
        public class Info implements Serializable {
            private static final long serialVersionUID = 1L;
            /** 缴费项目编号*/
            private String epayCode;
            /** 第三方商户编号*/
            private String merchantId;
            /** 缴费中心流水号*/
            private String traceNo;
            /** 输入要素1*/
            private String input1;
            /** 输入要素2*/
            private String input2;
            /** 输入要素3*/
            private String input3;
            /** 输入要素4*/
            private String input4;
            /** 输入要素5*/
            private String input5;
            /** 农行16位客户号*/
            private String userId;
            /** 缴费金额计算规则*/
            private String amtRule;
            /** 合并支付的子账单数*/
            private String payBillCount;
            /** 合并支付的子账单累加总金额*/
            private String payBillAmt;
            /** 合并支付的子账单*/
            private String payBillNo;
            /** 套餐名称*/
            private String optionName;
            /** 套餐编码*/
            private String optionCode;
            /** 套餐金额*/
            private String optionAmt;
            /** 支付方式交易码*/
            private String payType;
            /** 缴费支付账号*/
            private String payAcc;
            /** 支付系统流水号*/
            private String transPaySeq;
            /** 支付系统日期*/
            private String transDate;
            /** 支付系统时间*/
            private String transTime;
            /** 会计日期*/
            private String settleDate;
            /** 清算模式*/
            private String clearType;
            /** 缓存域信息*/
            private String cacheMem;
            /** 销账报文重发次数,通过此字段识别销账报文是否为重发的,0表示首次、1表示重发一次,2表示重发2次,最多重发3次*/
            private String resendTimes;
            /** 第三方支付平台商户订单号 第三方平台例如微信支付宝的支付订单号 add 2020-01-13*/
            private String numOpenMerchantOrder;
            @Override
            public String toString() {
                return JSON.toJSONString(this);
            }
        }
    }
}
bankapi/src/main/java/com/taxi591/bankapi/dto/ChargeBillResponse.java
New file
@@ -0,0 +1,158 @@
package com.taxi591.bankapi.dto;
import com.alibaba.fastjson2.JSON;
import lombok.Data;
import java.io.Serializable;
/**
 *  直连商户平台账单销账返回对象
 *  @author DELL
 *
 */
@Data
public class ChargeBillResponse implements Serializable {
    private static final long serialVersionUID = 1L;
    /** 格式 */
    private String format;
    /** 消息体 */
    private Message message;
    public ChargeBillResponse(){
    }
    /**
     * 构造函数,通过输入对象,构造返回对象数据信息
     * @param request
     */
    public ChargeBillResponse(ChargeBillRequest request) {
        this.setFormat(request.getFormat());
        this.setMessage(new Message(request.getMessage()));
    }
    @Override
    public String toString() {
        return JSON.toJSONString(this);
    }
    /**
     *
     * 账单查询内部消息对象返回实体message内部类
     *
     */
    @Data
    public class Message implements Serializable {
        private static final long serialVersionUID = 1L;
        /** 消息头部 */
        private Head head;
        /** 消息实体  */
        private Info info;
        public Message() {
            this.head = new Head();
            this.info = new Info();
        }
        public Message(ChargeBillRequest.Message requestMessage){
            this.setHead(new Head(requestMessage.getHead()));
            this.setInfo(new Info(requestMessage.getInfo()));
        }
        @Override
        public String toString() {
            return JSON.toJSONString(this);
        }
        /**
         *
         * 账单销账内部消息对象返回实体Head内部类
         *
         */
        @Data
        public class Head implements Serializable {
            private static final long serialVersionUID = 1L;
            /**  渠道 */
            private String channel;
            /**  交易码  */
            private String transCode;
            /**  交易上行下送标志  */
            private String transFlag;
            /**  缴费中心交易序列号 */
            private String transSeqNum;
            /** 时间戳  */
            private String timeStamp;
            /**  查询返回码 */
            private String returnCode ;
            /**  返回提示信息  */
            private String returnMessage;
            public Head() {
            }
            public Head(ChargeBillRequest.Message.Head reqMessHead) {
                this.setChannel(reqMessHead.getChannel());
                this.setTransSeqNum(reqMessHead.getTransSeqNum());
                this.setTransCode(reqMessHead.getTransCode());
            }
            @Override
            public String toString() {
                return JSON.toJSONString(this);
            }
        }
        /**
         *
         * 账单查询内部消息对象返回实体Info内部类
         *
         */
        @Data
        public class Info implements Serializable {
            private static final long serialVersionUID = 1L;
            /** 缴费项目唯一标识号*/
            private String epayCode;
            /** 缴费中心流水号*/
            private String traceNo;
            /** 退款标志位*/
            private String refundFlag;
            /** 第三方支付平台商户订单号 第三方平台例如微信支付宝的支付订单号 add 2020-01-13*/
            private String numOpenMerchantOrder;
            public Info() {
            }
            public Info(ChargeBillRequest.Message.Info reqMessInfo) {
                this.setEpayCode(reqMessInfo.getEpayCode());
                this.setTraceNo(reqMessInfo.getTraceNo());
            }
            @Override
            public String toString() {
                return JSON.toJSONString(this);
            }
        }
    }
}
bankapi/src/main/java/com/taxi591/bankapi/dto/CovertPayBackResult.java
New file
@@ -0,0 +1,18 @@
package com.taxi591.bankapi.dto;
import lombok.Data;
import java.io.Serializable;
@Data
public class CovertPayBackResult implements Serializable {
    /**
     * 解析结果
     */
    private ChargeBillRequest result;
    /**
     * 返回银行应答内容
     */
    private String back;
}
bankapi/src/main/java/com/taxi591/bankapi/service/BankService.java
New file
@@ -0,0 +1,117 @@
package com.taxi591.bankapi.service;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.taxi591.bankapi.dto.ChargeBillRequest;
import com.taxi591.bankapi.dto.ChargeBillResponse;
import com.taxi591.bankapi.dto.CovertPayBackResult;
import com.taxi591.bankapi.utils.Base64;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Service
@Slf4j
public class BankService {
    @Autowired
    SignatureAndVerification signatureAndVerification;
    static final String TIMESTAMP_PATTERN = "yyyyMMddHHmmssSSS";
    /**
     * 创建银行应答
     * @param request 银行请求对象
     * @param dealResult 是否处理成功
     * @return
     */
    public String createResponse(ChargeBillRequest request,Boolean dealResult){
        ChargeBillResponse response = new ChargeBillResponse(request);
        response.getMessage().getHead().setReturnCode(dealResult?"0000":"1111");
        response.getMessage().getHead().setReturnMessage(dealResult?"处理成功":"处理失败");
        response.getMessage().getInfo().setRefundFlag("false");
        response.getMessage().getHead().setTimeStamp(DateUtils.dateTimeNow(TIMESTAMP_PATTERN));
        if (!dealResult){
            if  (Integer.parseInt(request.getMessage().getInfo().getResendTimes())==3){
                response.getMessage().getInfo().setRefundFlag("true");
            }else{
                response.getMessage().getInfo().setRefundFlag("false");
            }
        }
        String respJSON = JSON.toJSONString(response);
        String sign = signatureAndVerification.signWhithsha1withrsa(respJSON);
        String respStr = null;
        try {
            respStr = sign + "||" + new String(org.apache.commons.codec.binary.Base64.encodeBase64(respJSON.getBytes("utf-8")));
        } catch (UnsupportedEncodingException e) {
        }
        return respStr;
    }
    /**
     * 处理支付回调数据
     * @param httpRequest  http请求对象
     * @param consumer 处理函数
     * @return
     */
    public CovertPayBackResult covertPayCallBack(HttpServletRequest httpRequest, Function<ChargeBillRequest,Boolean> consumer) {
        CovertPayBackResult result = new CovertPayBackResult();
        String requestContent = "";
        try {
            // 接收报文
            requestContent = SignatureAndVerification.getRequestBody(httpRequest).trim();
            String sign = requestContent.substring(0,
                    requestContent.indexOf("||"));;
            String requestBody = requestContent.substring(sign
                    .length() + 2);;
            Pattern p=Pattern.compile("\"");
            Matcher m=p.matcher(requestBody);
            while(m.find()){
                requestBody=requestBody.replace(m.group(), "");
            }
            String request = new String(Base64.decodeFast(requestBody));
            log.info("-----ChargeBillController------------解析完成后的requestBody-------{}" + request);
            ChargeBillRequest chargeBillRequest = JSON.parseObject(request,
                    new TypeReference<ChargeBillRequest>() {
                    });
            if (chargeBillRequest==null){
                log.error("支付回调解析失败:{}",requestContent);
                throw new ServiceException("支付回调解析失败");
            }
            boolean isok = signatureAndVerification.read_cer_and_verify_sign(requestBody,sign);
            if (!isok){
                throw new ServiceException("支付回调验签失败");
            }
            Boolean dealBack = true;
            if (consumer!=null){
                dealBack = consumer.apply(chargeBillRequest);
            }
            result.setResult(chargeBillRequest);
            result.setBack(createResponse(chargeBillRequest,dealBack));
        }catch (ServiceException e){
            result.setBack(e.getMessage());
            throw e;
        }catch (Exception e){
            log.error("解析异常:{}",requestContent,e);
            throw new ServiceException("支付回调解析异常");
        }
        return result;
    }
    public void covertBillTo(){
    }
}
bankapi/src/main/java/com/taxi591/bankapi/service/SignatureAndVerification.java
New file
@@ -0,0 +1,198 @@
package com.taxi591.bankapi.service;
import com.taxi591.bankapi.BankProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
/**
 * 验签和加签工具类
 * @author yzz
 *
 */
@Component
@Slf4j
public class SignatureAndVerification {
    @Autowired
    BankProperties bankProperties;
    public static String getRequestBody(HttpServletRequest request)
            throws IOException {
        /** 读取httpbody内容 */
        StringBuilder httpBody = new StringBuilder();
        BufferedReader br = null;
        try {
            br = new BufferedReader(new InputStreamReader(
                    request.getInputStream()));
            String line = null;
            while ((line = br.readLine()) != null) {
                httpBody.append(line);
            }
        } catch (IOException ex) {
            throw ex;
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        }
        return httpBody.toString();
    }
    /**
     * 加签名
     * @param dataString
     * @return
     */
    public  String signWhithsha1withrsa(String dataString) {
        String signatureString = null;
        String filePath=bankProperties.getPfxPath();
        try {
            KeyStore ks = KeyStore.getInstance("PKCS12");
            FileInputStream fis = new FileInputStream(filePath);
            char[] nPassword = null;
            if ((bankProperties.getKeystorePassword() == null)
                    || bankProperties.getKeystorePassword().trim().equals("")) {
                nPassword = null;
            } else {
                nPassword = bankProperties.getKeystorePassword().toCharArray();
            }
            ks.load(fis, nPassword);
            fis.close();
            Enumeration<String> enums = ks.aliases();
            String keyAlias = null;
            if (enums.hasMoreElements())
            {
                keyAlias = (String) enums.nextElement();
            }
            System.out.println("is key entry=" + ks.isKeyEntry(keyAlias));
            PrivateKey prikey = (PrivateKey) ks.getKey(keyAlias, nPassword);
            java.security.cert.Certificate cert = ks.getCertificate(keyAlias);
            // SHA1withRSA算法进行签名
            Signature sign = Signature.getInstance("SHA1withRSA");
            sign.initSign(prikey);
            byte[] data = dataString.getBytes("utf-8");
            byte[] dataBase= Base64.encodeBase64(data);
            // 更新用于签名的数据
            sign.update(dataBase);
            byte[] signature = sign.sign();
            signatureString = new String(Base64.encodeBase64(signature));
            System.out.println("加密完成,signature is : " + signatureString);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return signatureString;
    }
    /**
     * 读取cer并验证公钥签名
     * @return
     */
    public  boolean read_cer_and_verify_sign(String requestBody, String signature) {
        String filePath=bankProperties.getCerPath();
        X509Certificate cert = null;
        boolean flag = false;
        try {
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            cert = (X509Certificate) cf
                    .generateCertificate(new FileInputStream(new File(
                            filePath)));
            PublicKey publicKey = cert.getPublicKey();
            String publicKeyString = new String(Base64.encodeBase64(publicKey
                    .getEncoded()));
            System.out.println("-----------------公钥--------------------");
            System.out.println(publicKeyString);
            System.out.println("-----------------公钥--------------------");
            Signature verifySign = Signature.getInstance("SHA1withRSA");
            verifySign.initVerify(publicKey);
            // 用于验签的数据
            verifySign.update(requestBody.getBytes("utf-8"));
            flag = verifySign.verify(Base64
                    .decodeBase64(signature));
        } catch (Exception e) {
            log.error("验签失败,发生异常:{},{}",requestBody,signature,e);
            flag = false;
        }
        return flag;
    }
    /**
     * 接收报文返回requestBody和使用base64解析后的requestBody以及缴费中心传送的签名
     */
    public Map<String,String> requestBodyOfBase64(HttpServletRequest request){
        Map<String,String> requestMap=new HashMap<String,String>();
        // 接收报文
        String requestContent=null;
        try {
            requestContent = getRequestBody(request)
                    .trim();
        } catch (IOException e) {
            e.printStackTrace();
        }
        String signatureString = null;
        String requestBody = null;
        if (requestContent.contains("||")) {
            signatureString = requestContent.substring(0,
                    requestContent.indexOf("||"));
            requestBody = requestContent.substring(signatureString
                    .length() + 2);
        }else {
            try {
                requestBody = new String(requestContent.getBytes("GB2312"));
                log.info("转码后的报文:{}",requestBody);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        log.info("截取报文的requestBody解密前:{}", requestBody);
        String requestBodyOfDecoded = new String(
                Base64.decodeBase64(requestBody));
        /*if (requestBodyOfDecoded.contains("</Message>")) {
            requestBody = requestBodyOfDecoded.substring(
                    requestContent.indexOf("</Message>"),requestContent.indexOf("</Message>")+10);
            signatureString = requestBodyOfDecoded.substring(
                    requestContent.indexOf("<Signature>")+11,requestContent.indexOf("</Signature>"));
        }*/
        log.info("截取报文的signatureString:{}", signatureString);
        System.out.println("-----解析完成后的requestBody-------" + requestBodyOfDecoded);
        //使用base64解析完成后的requestBody
        requestMap.put("requestBodyOfDecoded",requestBodyOfDecoded);
        //解析前的requestBody
        requestMap.put("requestBody",requestBody);
        //获取缴费中心传送过来的签名
        requestMap.put("signatureString",signatureString);
        return requestMap;
    }
}
bankapi/src/main/java/com/taxi591/bankapi/utils/Base64.java
New file
@@ -0,0 +1,210 @@
package com.taxi591.bankapi.utils;
import java.util.Arrays;
/**
 *
 * @version 2.2
 * @author Mikael Grev Date: 2004-aug-02 Time: 11:31:11
 */
public class Base64 {
    public static final char[] CA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
    public static final int[]  IA = new int[256];
    static {
        Arrays.fill(IA, -1);
        for (int i = 0, iS = CA.length; i < iS; i++)
            IA[CA[i]] = i;
        IA['='] = 0;
    }
    /**
     * Decodes a BASE64 encoded char array that is known to be resonably well formatted. The method is about twice as
     * fast as #decode(char[]). The preconditions are:<br>
     * + The array must have a line length of 76 chars OR no line separators at all (one line).<br>
     * + Line separator must be "\r\n", as specified in RFC 2045 + The array must not contain illegal characters within
     * the encoded string<br>
     * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br>
     *
     * @param chars The source array. Length 0 will return an empty array. <code>null</code> will throw an exception.
     * @return The decoded array of bytes. May be of length 0.
     */
    public static byte[] decodeFast(char[] chars, int offset, int charsLen) {
        // Check special case
        if (charsLen == 0) {
            return new byte[0];
        }
        int sIx = offset, eIx = offset + charsLen - 1; // Start and end index after trimming.
        // Trim illegal chars from start
        while (sIx < eIx && IA[chars[sIx]] < 0)
            sIx++;
        // Trim illegal chars from end
        while (eIx > 0 && IA[chars[eIx]] < 0)
            eIx--;
        // get the padding count (=) (0, 1 or 2)
        int pad = chars[eIx] == '=' ? (chars[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end.
        int cCnt = eIx - sIx + 1; // Content count including possible separators
        int sepCnt = charsLen > 76 ? (chars[76] == '\r' ? cCnt / 78 : 0) << 1 : 0;
        int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
        byte[] bytes = new byte[len]; // Preallocate byte[] of exact length
        // Decode all but the last 0 - 2 bytes.
        int d = 0;
        for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) {
            // Assemble three bytes into an int from four "valid" characters.
            int i = IA[chars[sIx++]] << 18 | IA[chars[sIx++]] << 12 | IA[chars[sIx++]] << 6 | IA[chars[sIx++]];
            // Add the bytes
            bytes[d++] = (byte) (i >> 16);
            bytes[d++] = (byte) (i >> 8);
            bytes[d++] = (byte) i;
            // If line separator, jump over it.
            if (sepCnt > 0 && ++cc == 19) {
                sIx += 2;
                cc = 0;
            }
        }
        if (d < len) {
            // Decode last 1-3 bytes (incl '=') into 1-3 bytes
            int i = 0;
            for (int j = 0; sIx <= eIx - pad; j++)
                i |= IA[chars[sIx++]] << (18 - j * 6);
            for (int r = 16; d < len; r -= 8)
                bytes[d++] = (byte) (i >> r);
        }
        return bytes;
    }
    public static byte[] decodeFast(String chars, int offset, int charsLen) {
        // Check special case
        if (charsLen == 0) {
            return new byte[0];
        }
        int sIx = offset, eIx = offset + charsLen - 1; // Start and end index after trimming.
        // Trim illegal chars from start
        while (sIx < eIx && IA[chars.charAt(sIx)] < 0)
            sIx++;
        // Trim illegal chars from end
        while (eIx > 0 && IA[chars.charAt(eIx)] < 0)
            eIx--;
        // get the padding count (=) (0, 1 or 2)
        int pad = chars.charAt(eIx) == '=' ? (chars.charAt(eIx - 1) == '=' ? 2 : 1) : 0; // Count '=' at end.
        int cCnt = eIx - sIx + 1; // Content count including possible separators
        int sepCnt = charsLen > 76 ? (chars.charAt(76) == '\r' ? cCnt / 78 : 0) << 1 : 0;
        int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
        byte[] bytes = new byte[len]; // Preallocate byte[] of exact length
        // Decode all but the last 0 - 2 bytes.
        int d = 0;
        for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) {
            // Assemble three bytes into an int from four "valid" characters.
            int i = IA[chars.charAt(sIx++)] << 18 | IA[chars.charAt(sIx++)] << 12 | IA[chars.charAt(sIx++)] << 6 | IA[chars.charAt(sIx++)];
            // Add the bytes
            bytes[d++] = (byte) (i >> 16);
            bytes[d++] = (byte) (i >> 8);
            bytes[d++] = (byte) i;
            // If line separator, jump over it.
            if (sepCnt > 0 && ++cc == 19) {
                sIx += 2;
                cc = 0;
            }
        }
        if (d < len) {
            // Decode last 1-3 bytes (incl '=') into 1-3 bytes
            int i = 0;
            for (int j = 0; sIx <= eIx - pad; j++)
                i |= IA[chars.charAt(sIx++)] << (18 - j * 6);
            for (int r = 16; d < len; r -= 8)
                bytes[d++] = (byte) (i >> r);
        }
        return bytes;
    }
    /**
     * Decodes a BASE64 encoded string that is known to be resonably well formatted. The method is about twice as fast
     * as decode(String). The preconditions are:<br>
     * + The array must have a line length of 76 chars OR no line separators at all (one line).<br>
     * + Line separator must be "\r\n", as specified in RFC 2045 + The array must not contain illegal characters within
     * the encoded string<br>
     * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br>
     *
     * @param s The source string. Length 0 will return an empty array. <code>null</code> will throw an exception.
     * @return The decoded array of bytes. May be of length 0.
     */
    public static byte[] decodeFast(String s) {
        // Check special case
        int sLen = s.length();
        if (sLen == 0) {
            return new byte[0];
        }
        int sIx = 0, eIx = sLen - 1; // Start and end index after trimming.
        // Trim illegal chars from start
        while (sIx < eIx && IA[s.charAt(sIx) & 0xff] < 0)
            sIx++;
        // Trim illegal chars from end
        while (eIx > 0 && IA[s.charAt(eIx) & 0xff] < 0)
            eIx--;
        // get the padding count (=) (0, 1 or 2)
        int pad = s.charAt(eIx) == '=' ? (s.charAt(eIx - 1) == '=' ? 2 : 1) : 0; // Count '=' at end.
        int cCnt = eIx - sIx + 1; // Content count including possible separators
        int sepCnt = sLen > 76 ? (s.charAt(76) == '\r' ? cCnt / 78 : 0) << 1 : 0;
        int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
        byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
        // Decode all but the last 0 - 2 bytes.
        int d = 0;
        for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) {
            // Assemble three bytes into an int from four "valid" characters.
            int i = IA[s.charAt(sIx++)] << 18 | IA[s.charAt(sIx++)] << 12 | IA[s.charAt(sIx++)] << 6
                    | IA[s.charAt(sIx++)];
            // Add the bytes
            dArr[d++] = (byte) (i >> 16);
            dArr[d++] = (byte) (i >> 8);
            dArr[d++] = (byte) i;
            // If line separator, jump over it.
            if (sepCnt > 0 && ++cc == 19) {
                sIx += 2;
                cc = 0;
            }
        }
        if (d < len) {
            // Decode last 1-3 bytes (incl '=') into 1-3 bytes
            int i = 0;
            for (int j = 0; sIx <= eIx - pad; j++)
                i |= IA[s.charAt(sIx++)] << (18 - j * 6);
            for (int r = 16; d < len; r -= 8)
                dArr[d++] = (byte) (i >> r);
        }
        return dArr;
    }
}
bankapi/src/main/java/com/taxi591/bankapi/utils/Base64Util.java
New file
@@ -0,0 +1,50 @@
package com.taxi591.bankapi.utils;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.UnsupportedEncodingException;
/**
 * 将String进行base64编码解码,使用utf-8
 * yuan
 */
public class Base64Util {
    private static final Logger LOGGER = LoggerFactory.getLogger(Base64Util.class);
    private static final String UTF_8 = "UTF-8";
    /**
     * 对给定的字符串进行base64解码操作
     */
    public static String decodeData(String inputData) {
        try {
            if (null == inputData) {
                return null;
            }
            return new String(Base64.decodeBase64(inputData.getBytes(UTF_8)), UTF_8);
        } catch (UnsupportedEncodingException e) {
            LOGGER.error(inputData, e);
        }
        return null;
    }
    /**
     * 对给定的字符串进行base64加密操作
     */
    public static String encodeData(String inputData) {
        try {
            if (null == inputData) {
                return null;
            }
            return new String(Base64.encodeBase64(inputData.getBytes(UTF_8)), UTF_8);
        } catch (UnsupportedEncodingException e) {
            LOGGER.error(inputData, e);
        }
        return null;
    }
}
bankapi/src/main/java/com/taxi591/bankapi/utils/HttpClientUtils.java
New file
@@ -0,0 +1,302 @@
package com.taxi591.bankapi.utils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
import java.util.Map.Entry;
/**
 *httpClient操作远程url工具类
 */
public class HttpClientUtils {
    private static Logger logger = LoggerFactory.getLogger(HttpClientUtils.class);
    /**
     * 发送str格式的post请求
     * @param url
     * @param jsonStr
     * @return
     */
    public static String doPostStr(String url, String str) {
        // 建立HttpPost对象
        HttpPost httppost = new HttpPost(url);
        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
        CloseableHttpClient client = httpClientBuilder.build();
//        int statusCode = 0;
        String responseBody = null;
        try {
            // 设置发送内容、编码等
            StringEntity stringEntity = new StringEntity(str, "utf-8");
            stringEntity.setContentType("text/plain");
            httppost.setEntity(stringEntity);
            // 发送Post,并返回一个HttpResponse对象
            HttpResponse response = client.execute(httppost);
//            // 获取返回码
//            statusCode = response.getStatusLine().getStatusCode();
            // 获取返回报文
            responseBody = EntityUtils.toString(response.getEntity());
            return responseBody;
        }catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 下载文件
     * @param url
     * @param jsonStr
     * @param 前台的返回流
     * @return
     */
    public static void doPostFile(String url, String str,HttpServletResponse httpResponse) {
        ServletOutputStream out = null;
        // 建立HttpPost对象
        HttpPost httppost = new HttpPost(url);
        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
        CloseableHttpClient client = httpClientBuilder.build();
        try {
            // 设置发送内容、编码等
            StringEntity stringEntity = new StringEntity(str, "utf-8");
            stringEntity.setContentType("text/plain");
            httppost.setEntity(stringEntity);
            // 发送Post,并返回一个HttpResponse对象
            HttpResponse response = client.execute(httppost);
            // 获取返回报文
            String responseStr = EntityUtils.toString(response.getEntity());
            //如果系统返回未加密异常则会返回"{"+json+"}"
            if (responseStr.startsWith("{")){
                //设置响应头信息为页面形式
                httpResponse.setCharacterEncoding("UTF-8");
                httpResponse.setContentType("text/html;charset=utf-8");
                httpResponse.sendRedirect("downloadTraceFail.jsp");
            //如果返回内容为异常信息则返回内容为"签名||base64加密"形式
            }else if (responseStr.contains("||")) {
                //截取签名信息
                String headSub = responseStr.substring(0, responseStr.indexOf("||"));
                logger.warn("获取签名的前半部分:"+headSub);
                //截取加密的json信息,进行解密
                String tailSub = responseStr.substring(responseStr.indexOf("||")+2);
                logger.warn("获取签名的后半部分:"+tailSub);
                //将获取到的json报文进行解析,获取报文体信息
                responseStr = Base64Util.decodeData(tailSub);
                logger.warn("获取签名解密后:"+responseStr);
                //设置响应头信息为页面形式
                httpResponse.setCharacterEncoding("UTF-8");
                httpResponse.setContentType("text/html;charset=utf-8");
                httpResponse.sendRedirect("downloadTraceFail.jsp");
            //返回内容为正常内容,将response消息头读取直接写入httpResponse中
            }else{
                Header[] encode = response.getAllHeaders();
                for (Header header : encode) {
                    httpResponse.setHeader(header.getName(), header.getValue());
                }
            }
            //将获取到的信息封装到httpEntity中,反映到页面中去
            logger.warn("最终获取到的信息:"+responseStr);
            HttpEntity entity = new StringEntity(responseStr,"UTF-8");
            //通过输出流将结果反映到页面上
            out = httpResponse.getOutputStream();
            entity.writeTo(out);
            out.flush();
        }catch (Exception e) {
            e.printStackTrace();
        }finally{
            if(out != null){
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    /**
     * get 请求方法
     * @param url
     * @return
     */
    public static String doGet(String url) {
        CloseableHttpClient httpClient = null;
        CloseableHttpResponse response = null;
        String result = "";
        try {
            //通过址默认配置创建一个httpClient实例
            httpClient = HttpClients.createDefault();
            //创建httpGet远程连接实例
            HttpGet httpGet = new HttpGet(url);
            //设置配置请求参数
            RequestConfig requestConfig = RequestConfig.custom()
                    .setConnectTimeout(35000)//连接主机服务超时时间
                    .setConnectionRequestTimeout(35000)//请求超时时间
                    .setSocketTimeout(60000)//数据读取超时时间
                    .build();
            //为httpGet实例设置配置
            httpGet.setConfig(requestConfig);
            //执行get请求得到返回对象
            response = httpClient.execute(httpGet);
            //通过返回对象获取返回数据
            HttpEntity entity = response.getEntity();
            //通过EntityUtils中的toString方法将结果转换为字符串
            result = EntityUtils.toString(entity);
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            if(null != response) {
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(null != httpClient) {
                try {
                    httpClient.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }
    /**
     * post请求
     * @param url
     * @param paramMap
     * @return
     */
    public static String doPost(String url, Map<String,Object> paramMap) {
        CloseableHttpClient httpClient = null;
        CloseableHttpResponse response = null;
        String result = "";
        try {
            //创建httpClient实例
            httpClient = HttpClients.createDefault();
            //创建httpPost远程连接实例
            HttpPost httpPost = new HttpPost(url);
            //配置请求参数实例
            RequestConfig requestConfig = RequestConfig.custom()
                    .setConnectTimeout(35000)//设置连接主机服务超时时间
                    .setConnectionRequestTimeout(35000)//设置连接请求超时时间
                    .setSocketTimeout(60000)//设置读取数据连接超时时间
                    .build();
            //为httpPost实例设置配置
            httpPost.setConfig(requestConfig);
            //封装post请求参数
            if(null != paramMap && paramMap.size() > 0) {
                List<NameValuePair> nvps = new ArrayList<NameValuePair>();
                //通过map集成entrySet方法获取entity
                Set<Entry<String, Object>> entrySet = paramMap.entrySet();
                //循环遍历,获取迭代器
                Iterator<Entry<String, Object>> iterator = entrySet.iterator();
                while (iterator.hasNext()) {
                    Entry<String, Object> mapEntry = iterator.next();
                    nvps.add(new BasicNameValuePair(mapEntry.getKey(), mapEntry.getValue().toString()));
                }
                //为httpPost设置封装好的请求参数
                httpPost.setEntity(new UrlEncodedFormEntity(nvps, "UTF-8"));
            }
            //执行post请求得到返回对象
            response = httpClient.execute(httpPost);
            //通过返回对象获取数据
            HttpEntity entity = response.getEntity();
            //将返回的数据转换为字符串
            result = EntityUtils.toString(entity);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            if(null != response) {
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(null != httpClient) {
                try {
                    httpClient.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }
    /**
     *
     * @param request
     * @return
     * @throws IOException
     */
    public static String getRequestBody(HttpServletRequest request)
            throws IOException {
        /** 读取httpbody内容 */
        StringBuilder httpBody = new StringBuilder();
        BufferedReader br = null;
        try {
            br = new BufferedReader(new InputStreamReader(
                    request.getInputStream()));
            String line = null;
            while ((line = br.readLine()) != null) {
                httpBody.append(line);
            }
        } catch (IOException ex) {
            throw ex;
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        }
        return httpBody.toString();
    }
}
bankapi/src/main/resources/META-INF/spring.factories
New file
@@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.taxi591.bankapi.BankConfig
generator/src/test/java/com/xizang/CodeGeneratorTests.java
@@ -144,7 +144,7 @@
//         strategy.setTablePrefix(pc.getModuleName() + "");
//        strategy.setLikeTable(new LikeTable("room"));
        //strategy.setLikeTable(new LikeTable("member"));
        strategy.setLikeTable(new LikeTable("t_"));// 生成表名
        strategy.setLikeTable(new LikeTable("t_bank_flow"));// 生成表名
//        strategy.setLikeTable(new LikeTable("t_hotel"));// 生成表名
//        strategy.setLikeTable(new LikeTable("t_scan_message"));// 生成表名
//        strategy.setNotLikeTable(new LikeTable("hotel_info"));// 不生成表名
pom.xml
@@ -187,6 +187,7 @@
        <module>ruoyi-generator</module>
        <module>ruoyi-common</module>
        <module>ruoyi-applet</module>
        <module>bankapi</module>
    </modules>
    <packaging>pom</packaging>
ruoyi-admin/pom.xml
@@ -16,7 +16,11 @@
    </description>
    <dependencies>
        <dependency>
            <groupId>org.taxi591</groupId>
            <artifactId>bankapi</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/BankOutController.java
New file
@@ -0,0 +1,36 @@
package com.ruoyi.web.controller.api;
import com.ruoyi.system.service.TBillService;
import com.taxi591.bankapi.dto.CovertPayBackResult;
import com.taxi591.bankapi.service.BankService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping("open/bank")
public class BankOutController {
    @Autowired
    BankService bankService;
    @Autowired
    TBillService tBillService;
    @PostMapping(value = "payCallback")
    public @ResponseBody String payCallback(HttpServletRequest request){
        CovertPayBackResult result = bankService.covertPayCallBack(request, (billRequest) -> {
            String orderno = billRequest.getMessage().getInfo().getInput1();
            return true;
        });
        return result.getBack();
    }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TBankFlowController.java
New file
@@ -0,0 +1,43 @@
package com.ruoyi.web.controller.api;
import com.ruoyi.common.basic.PageInfo;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.model.TBankFlow;
import com.ruoyi.system.model.TFlowManagement;
import com.ruoyi.system.query.TBankFlowQuery;
import com.ruoyi.system.query.TFlowManagementQuery;
import com.ruoyi.system.service.TBankFlowService;
import com.ruoyi.system.service.TFlowManagementService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * <p>
 * 流水管理 前端控制器
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-07
 */
@RestController
@RequestMapping("/t-bank-flow")
public class TBankFlowController {
    @Autowired
    private TBankFlowService flowService;
    /**
     * 获取流水列表
     */
    @PreAuthorize("@ss.hasPermi('system:bankFlow:list')")
    @ApiOperation(value = "获取银行流水列表")
    @PostMapping("/list")
    public R<PageInfo<TBankFlow>> list(@RequestBody TBankFlowQuery query) {
        return R.ok(flowService.pageList(query));
    }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TBillConfirmController.java
New file
@@ -0,0 +1,20 @@
package com.ruoyi.web.controller.api;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * <p>
 * 账单线下缴费关联表 前端控制器
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-10
 */
@RestController
@RequestMapping("/t-bill-confirm")
public class TBillConfirmController {
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TBillController.java
@@ -1,6 +1,15 @@
package com.ruoyi.web.controller.api;
import com.ruoyi.common.basic.PageInfo;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.dto.TBillDto;
import com.ruoyi.system.model.TBill;
import com.ruoyi.system.query.TBillQuery;
import com.ruoyi.system.service.TBillService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -16,5 +25,15 @@
@RequestMapping("/t-bill")
public class TBillController {
    @Autowired
    TBillService tBillService;
    @PostMapping("list")
    public R<PageInfo<TBillDto>> list(@RequestBody TBillQuery query){
        PageInfo<TBillDto> pageInfo = tBillService.queryPage(query);
        return R.ok(pageInfo);
    }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TBillDetailController.java
New file
@@ -0,0 +1,20 @@
package com.ruoyi.web.controller.api;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * <p>
 * 账单水电费子表 前端控制器
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-10
 */
@RestController
@RequestMapping("/t-bill-detail")
public class TBillDetailController {
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TFlowManagementController.java
@@ -1,6 +1,16 @@
package com.ruoyi.web.controller.api;
import com.ruoyi.common.basic.PageInfo;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.model.TFlowManagement;
import com.ruoyi.system.query.TFlowManagementQuery;
import com.ruoyi.system.service.TFlowManagementService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -16,6 +26,18 @@
@RestController
@RequestMapping("/t-flow-management")
public class TFlowManagementController {
    @Autowired
    private TFlowManagementService flowService;
    /**
     * 获取流水列表
     */
    @PreAuthorize("@ss.hasPermi('system:flow:list')")
    @ApiOperation(value = "获取流水列表")
    @PostMapping("/list")
    public R<PageInfo<TFlowManagement>> list(@RequestBody TFlowManagementQuery query) {
        return R.ok(flowService.pageList(query));
    }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TInvoiceController.java
@@ -1,9 +1,17 @@
package com.ruoyi.web.controller.api;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.basic.PageInfo;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.model.TInvoice;
import com.ruoyi.system.query.TInvoiceQuery;
import com.ruoyi.system.service.TInvoiceService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
/**
 * <p>
@@ -16,6 +24,22 @@
@RestController
@RequestMapping("/t-invoice")
public class TInvoiceController {
    @Autowired
    private TInvoiceService invoiceService;
    @PreAuthorize("@ss.hasPermi('system:invoice:list')")
    @ApiOperation(value = "获取开票列表")
    @PostMapping("/list")
    public R<PageInfo<TInvoice>> list(@RequestBody TInvoiceQuery query) {
        return R.ok(invoiceService.pageList(query));
    }
    @PreAuthorize("@ss.hasPermi('system:invoice:delete')")
    @Log(title = "开票信息-删除开票", businessType = BusinessType.DELETE)
    @ApiOperation(value = "删除开票")
    @DeleteMapping(value = "/deleteById")
    public R<Boolean> deleteById(@RequestParam String id) {
        return R.ok(invoiceService.removeById(id));
    }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/TInvoiceToBillController.java
New file
@@ -0,0 +1,20 @@
package com.ruoyi.web.controller.api;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * <p>
 * 账单开票中间表 前端控制器
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-10
 */
@RestController
@RequestMapping("/t-invoice-to-bill")
public class TInvoiceToBillController {
}
ruoyi-admin/src/main/java/com/ruoyi/web/task/TbillTask.java
New file
@@ -0,0 +1,104 @@
package com.ruoyi.web.task;
import com.ruoyi.common.basic.PageInfo;
import com.ruoyi.system.dto.TBillDto;
import com.ruoyi.system.model.TBill;
import com.ruoyi.system.model.TContract;
import com.ruoyi.system.query.TBillQuery;
import com.ruoyi.system.query.TContractQuery;
import com.ruoyi.system.service.TBillService;
import com.ruoyi.system.service.TContractService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
@Slf4j
@Component("billTask")
public class TbillTask {
    @Autowired
    TBillService tBillService;
    @Autowired
    TContractService tContractService;
    /**
     * 判断账单是否到期
     */
    public void checkBillOverdue(){
        try {
            int index = 1;
            int rows = 20;
            long pages = 0;
            TBillQuery query = new TBillQuery();
            query.setPageSize(rows);
            //查询未缴费的
            query.setPayFeesStatus(1);
            Calendar c = Calendar.getInstance();
            Date time = c.getTime();
            do {
                query.setPageNum(index);
                PageInfo<TBillDto> pageinfo = tBillService.queryPage(query);
                if (pages==0){
                    pages = pageinfo.getPages();
                }
                if (pageinfo.getRecords()!=null && pageinfo.getRecords().size()>0){
                    pageinfo.getRecords().forEach(tBill -> {
                        //todo 生成账单
                    });
                }
                index++;
            }while (index<=pages);
        }catch (Exception e){
            log.error("");
        }
    }
    /**
     * 计算违约金
     */
    public void caculatePenaltyFee(){
        try {
            int index = 1;
            int rows = 20;
            long pages = 0;
            TBillQuery query = new TBillQuery();
            query.setPageSize(rows);
            //查询已逾期的
            query.setPayFeesStatus(4);
            Calendar c = Calendar.getInstance();
            Date time = c.getTime();
            do {
                query.setPageNum(index);
                PageInfo<TBillDto> pageinfo = tBillService.queryPage(query);
                if (pages==0){
                    pages = pageinfo.getPages();
                }
                if (pageinfo.getRecords()!=null && pageinfo.getRecords().size()>0){
                    pageinfo.getRecords().forEach(tContract -> {
                        //todo 生成账单
                    });
                }
                index++;
            }while (index<=pages);
        }catch (Exception e){
            log.error("");
        }
    }
}
ruoyi-admin/src/main/resources/application-prod.yml
@@ -21,7 +21,7 @@
  port: 8081
  servlet:
    # 应用的访问路径
    context-path: /
    context-path: /admin
  tomcat:
    # tomcat的URI编码
    uri-encoding: UTF-8
ruoyi-admin/src/main/resources/application-test.yml
@@ -21,7 +21,7 @@
  port: 8081
  servlet:
    # 应用的访问路径
    context-path: /
    context-path: /admin
  tomcat:
    # tomcat的URI编码
    uri-encoding: UTF-8
ruoyi-applet/pom.xml
@@ -16,7 +16,11 @@
    </description>
    <dependencies>
        <dependency>
            <groupId>org.taxi591</groupId>
            <artifactId>bankapi</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/PayController.java
New file
@@ -0,0 +1,35 @@
package com.ruoyi.web.controller.api;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.dto.MakeOrderDto;
import com.ruoyi.system.dto.MakeOrderResp;
import com.ruoyi.system.service.TPayOrderService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/t-pay")
public class PayController {
    @Autowired
    TPayOrderService tPayOrderService;
    @ApiOperation(value = "创建支付订单")
    @PostMapping("makeOrder")
    public R<MakeOrderResp> makeOrder(@Validated @RequestBody MakeOrderDto dto){
        MakeOrderResp resp = tPayOrderService.makeOrder(dto);
        return R.ok(resp);
    }
}
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TBillConfirmController.java
New file
@@ -0,0 +1,20 @@
package com.ruoyi.web.controller.api;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * <p>
 * 账单线下缴费关联表 前端控制器
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-10
 */
@RestController
@RequestMapping("/t-bill-confirm")
public class TBillConfirmController {
}
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TBillController.java
@@ -1,21 +1,30 @@
package com.ruoyi.web.controller.api;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.basic.PageInfo;
import com.ruoyi.common.constant.DictConstants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.dto.TBillDto;
import com.ruoyi.system.dto.TInvoiceDTO;
import com.ruoyi.system.model.*;
import com.ruoyi.system.query.TBillQuery;
import com.ruoyi.system.query.TTenantQuery;
import com.ruoyi.system.service.TBillDetailService;
import com.ruoyi.system.service.TBillService;
import com.ruoyi.system.service.TInvoiceService;
import com.ruoyi.system.service.TInvoiceToBillService;
import com.ruoyi.system.vo.TBillVO;
import com.ruoyi.system.vo.TenantVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
/**
 * <p>
@@ -25,28 +34,65 @@
 * @author xiaochen
 * @since 2025-01-17
 */
@Api(tags = "租金账单管理")
@Api(tags = "缴费账单")
@RestController
@RequestMapping("/t-bill")
public class TBillController {
    private final TBillService billService;
    @Autowired
    public TBillController(TBillService billService) {
        this.billService = billService;
    TBillService tBillService;
    @Autowired
    TBillDetailService billDetailService;
    @Autowired
    TInvoiceService invoiceService;
    @Autowired
    TInvoiceToBillService invoiceToBillService;
    @ApiOperation(value = "缴费账单查询分页列表")
    @PostMapping("list")
    public R<PageInfo<TBillDto>> list(@RequestBody TBillQuery query){
        if (StringUtils.isEmpty(query.getUserId())){
            throw new ServiceException("用户ID不能为空");
        }
        PageInfo<TBillDto> pageInfo = tBillService.queryPage(query);
        return R.ok(pageInfo);
    }
    /**
     * 获取账单列表
     */
    @ApiOperation(value = "获取账单列表")
    @PostMapping(value = "/pageList")
    public R<PageInfo<TBillVO>> pageList(@RequestBody TBillQuery query) {
        return R.ok(billService.pageList(query));
    @ApiOperation(value = "查看缴费账单详情")
    @GetMapping(value = "/getDetailById")
    public R<TBillVO> getDetailById(@RequestParam String id) {
        TBill bill = tBillService.getById(id);
        TBillVO billVO = new TBillVO();
        BeanUtils.copyProperties(bill, billVO);
        // 查询水电费列表
        if("3".equals(bill.getBillType())){
            List<TBillDetail> list = billDetailService.list(Wrappers.lambdaQuery(TBillDetail.class)
                    .eq(TBillDetail::getBillId, id));
            billVO.setBillDetailList(list);
        }
        billVO.setBillType(DictUtils.getDictLabel(DictConstants.DICT_TYPE_BILL_TYPE,billVO.getBillType()));
        billVO.setPayFeesStatus(DictUtils.getDictLabel(DictConstants.DICT_TYPE_PAY_FEES_STATUS,billVO.getPayFeesStatus()));
        return R.ok(billVO);
    }
    @ApiOperation(value = "缴费账单开票")
    @PostMapping(value = "/invoice")
    public R<String> invoice(@RequestBody TInvoiceDTO dto) {
        // 添加开票信息
        invoiceService.save(dto);
        // 添加开票信息中间表信息
        List<String> billIds = dto.getBillIds();
        List<TInvoiceToBill> sysInvoiceToBills = new ArrayList<>();
        for (String billId : billIds) {
            TInvoiceToBill tInvoiceToBill = new TInvoiceToBill();
            tInvoiceToBill.setInvoiceId(dto.getId());
            tInvoiceToBill.setBillId(billId);
            sysInvoiceToBills.add(tInvoiceToBill);
        }
        invoiceToBillService.saveBatch(sysInvoiceToBills);
        return R.ok();
    }
}
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TInformationController.java
@@ -1,6 +1,17 @@
package com.ruoyi.web.controller.api;
import com.ruoyi.common.basic.PageInfo;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.model.TInformation;
import com.ruoyi.system.query.TInformationQuery;
import com.ruoyi.system.service.TInformationService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -13,9 +24,25 @@
 * @author xiaochen
 * @since 2025-01-17
 */
@Api(tags = "资讯管理")
@RestController
@RequestMapping("/t-information")
public class TInformationController {
    private final TInformationService informationService;
    @Autowired
    public TInformationController(TInformationService informationService) {
        this.informationService = informationService;
    }
    /**
     * 获取资讯管理管理列表
     */
    @ApiOperation(value = "获取资讯管理分页列表")
    @PostMapping(value = "/pageList")
    public R<PageInfo<TInformation>> pageList(@RequestBody TInformationQuery query) {
        return R.ok(informationService.pageList(query));
    }
}
ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java
@@ -66,7 +66,7 @@
    @PostMapping("/list")
    public AjaxResult list(@RequestBody SysRoleQuery query)
    {
        PageInfo<SysRole> list = roleService.selectList(query);
        PageInfo<SysRole> list = roleService.selectPageList(query);
        return AjaxResult.success(list);
    }
ruoyi-common/src/main/java/com/ruoyi/common/constant/AmountConstant.java
New file
@@ -0,0 +1,15 @@
package com.ruoyi.common.constant;
import java.math.BigDecimal;
public class AmountConstant {
    public static final BigDecimal b100 = new BigDecimal("100");
    public static final BigDecimal b1000 = new BigDecimal("1000");
    public static final BigDecimal b001 = new BigDecimal("0.01");
}
ruoyi-common/src/main/java/com/ruoyi/common/constant/DictConstants.java
@@ -39,4 +39,8 @@
     * 缴费状态 1=未缴费 2=待确认 3=已缴费 4=已逾期 5=已失效
     */
    public static final String DICT_TYPE_PAY_FEES_STATUS = "t_pay_fees_status";
    /**
     * 账单类型 1=租金 2=押金 3=生活费用
     */
    public static final String DICT_TYPE_BILL_TYPE = "t_bill_type";
}
ruoyi-common/src/main/java/com/ruoyi/common/constant/WxConstant.java
@@ -16,4 +16,7 @@
     * 高德地图坐标转换
     */
    public static final String ADDRESS_CONVERT_TO_COORDINATE = "https://restapi.amap.com/v3/geocode/geo?key=KEY&address=ADDRESS";
}
ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java
@@ -1,16 +1,14 @@
package com.ruoyi.common.core.redis;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;
/**
@@ -22,6 +20,15 @@
@Component
public class RedisCache
{
    private static final String LOCK_PREFIX = "lock:";
    private static final RedisScript<Long> UNLOCK_SCRIPT = new DefaultRedisScript<>(
            "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                    "  return redis.call('del', KEYS[1]) " +
                    "else " +
                    "  return 0 " +
                    "end",
            Long.class
    );
    @Autowired
    public RedisTemplate redisTemplate;
@@ -265,4 +272,33 @@
    {
        return redisTemplate.keys(pattern);
    }
    /**
     * 尝试加锁
     *
     * @param lockKey   锁的key
     * @param requestId 锁的持有者标识符(如UUID)
     * @param expireTime 锁的过期时间(秒)
     * @return 加锁成功返回true,否则返回false
     */
    public boolean tryLock(String lockKey, String requestId, long expireTime) {
        String lockKeyWithPrefix = LOCK_PREFIX + lockKey;
        // 使用SET命令的NX和PX选项来加锁
        Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKeyWithPrefix, requestId, expireTime, TimeUnit.SECONDS);
        return result != null && result;
    }
    /**
     * 解锁
     *
     * @param lockKey   锁的key
     * @param requestId 锁的持有者标识符(如UUID)
     * @return 解锁成功返回true,否则返回false
     */
    public boolean unlock(String lockKey, String requestId) {
        String lockKeyWithPrefix = LOCK_PREFIX + lockKey;
        // 使用Lua脚本来验证锁的持有者并解锁
        Long result = (Long) redisTemplate.execute(UNLOCK_SCRIPT, Collections.singletonList(lockKeyWithPrefix), requestId);
        return result != null && result == 1L;
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/utils/OrderNos.java
New file
@@ -0,0 +1,164 @@
package com.ruoyi.common.utils;
import com.ruoyi.common.utils.ip.IpUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.atomic.AtomicLong;
public class OrderNos {
    public static final char PADCHAR = '0';
    /**
     * 长度24位全球唯一标识
     */
    /**
     * 统一不重复内部订单号
     *
     * @param systemCode
     *            4字节系统编码
     * @return 24字节不重复编码
     */
    public static String oid(String systemCode) {
        StringBuilder sb = new StringBuilder();
        sb.append(padding(systemCode, 4));
        sb.append(Did.getInstance().getId(20));
        return sb.toString();
    }
    public static String oid() {
        return oid("O001");
    }
    /**
     * 获取分布式Id
     *
     * @return 默认20字符长度的数字不重复编码
     */
    public static String getDid() {
        return Did.getInstance().getId();
    }
    public static String getDid(int size) {
        return Did.getInstance().getId(size);
    }
    public static String getDid(String prefix) {
        return prefix + Did.getInstance().getId();
    }
    private static String padding(String text, int size) {
        String txt = StringUtils.trimToEmpty(text);
        if (StringUtils.length(txt) > size) {
            txt = StringUtils.substring(txt, StringUtils.length(txt) - size);
        }
        return StringUtils.leftPad(txt, size, PADCHAR);
    }
    /**
     * ID生成器
     *
     * 20字符长度 yyMMddHHmmss+3位本机IP末三位+5位随机数字
     *
     * @author zhangpu
     *
     */
    public static class Did {
        private static final Logger logger = LoggerFactory.getLogger(OrderNos.class);
        private static final int MIN_LENGTH = 19;
        private static final int SEQU_MAX = 99999;
        private static final int SEQU_19_MAX = 9999;
        private volatile static long BASE = 0;
        private AtomicLong sequence = new AtomicLong(0);
        private String nodeFlag;
        private Object nodeFlagLock = new Object();
        private static Did did = new Did();
        public static Did getInstance() {
            return did;
        }
        /**
         * 生产新Id
         *
         * @return
         */
        public  String getId() {
            return getId(20);
        }
        public  String getId(int size) {
            if (size < MIN_LENGTH) {
                throw new RuntimeException("did最小长度为" + MIN_LENGTH);
            }
            StringBuilder sb = new StringBuilder();
            sb.append(DateUtils.dateTimeNow("yyMMddHHmmss"));
            sb.append(getNodeFlag());
            sb.append(getSequ(size-15));
            return sb.toString();
        }
        public synchronized String getSequ(int size) {
            long timeCount = System.currentTimeMillis() / 1000;
            while (true) {
                long now = sequence.get();
                if (timeCount > now) {
                    // 已经过了本秒,则设置新的值
                    if (sequence.compareAndSet(now, timeCount)) {
                        break;
                    }
                } else {
                    if (sequence.compareAndSet(now, now + 1)) {
                        timeCount = now + 1;
                        break;
                    }
                }
            }
            int sn = (int) (timeCount % (size>=5?SEQU_MAX:SEQU_19_MAX));
            return StringUtils.leftPad(String.valueOf(sn), size, '0');
        }
        private  String  getNodeFlag(){
            if (this.nodeFlag == null) {
                synchronized (Did.class){
                    if (this.nodeFlag==null){
                        this.nodeFlag = generateNodeFlag();
                    }
                }
            }
            return this.nodeFlag;
        }
        /**
         * 简单节点编码
         *
         * 逻辑:Ip地址后三位,便于快速知道是哪个节点
         *
         * @return
         */
        private String generateNodeFlag() {
            String ipPostfix = null;
            try {
                String ip = IpUtils.getFirstNoLoopbackIPV4Address();
                ipPostfix = StringUtils.substringAfterLast(ip, ".");
            } catch (Exception e) {
                logger.warn("生产DID要素本机IP获取失败:" + e.getMessage());
            }
            return StringUtils.leftPad(ipPostfix, 3, "0");
        }
        private Did() {
            super();
        }
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java
@@ -1,16 +1,20 @@
package com.ruoyi.common.utils.ip;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.net.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import javax.servlet.http.HttpServletRequest;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
/**
 * 获取IP方法
 * 
 * @author ruoyi
 */
@Slf4j
public class IpUtils
{
    public final static String REGX_0_255 = "(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)";
@@ -379,4 +383,72 @@
        }
        return false;
    }
    private static final String LOOP_BACK = "127.0.0.1";
    private static String firstNoLoopbackIPV4Address = null;
    private static Collection<InetAddress> allHostIPV4Address = null;
    /**
     * 获取ipv4地址,如果有多个网卡的情况,获取第一个非loopback ip地址
     *
     * @return
     */
    public static String getFirstNoLoopbackIPV4Address() {
        if (firstNoLoopbackIPV4Address != null) {
            return firstNoLoopbackIPV4Address;
        }
        Collection<String> allNoLoopbackAddresses = null;
        try {
            allNoLoopbackAddresses = getAllNoLoopbackIPV4Addresses();
        } catch (Exception e) {
            log.error("获取ip失败", e);
            return LOOP_BACK;
        }
        if (allNoLoopbackAddresses.isEmpty()) {
            return LOOP_BACK;
        }
        return firstNoLoopbackIPV4Address = allNoLoopbackAddresses.iterator().next();
    }
    public static Collection<String> getAllNoLoopbackIPV4Addresses() {
        Collection<String> noLoopbackAddresses = new ArrayList<String>();
        Collection<InetAddress> allInetAddresses = getAllHostIPV4Address();
        for (InetAddress address : allInetAddresses) {
            if (!address.isLoopbackAddress()) {
                noLoopbackAddresses.add(address.getHostAddress());
            }
        }
        return noLoopbackAddresses;
    }
    public static Collection<InetAddress> getAllHostIPV4Address() {
        if (allHostIPV4Address == null) {
            try {
                Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
                Collection<InetAddress> addresses = new ArrayList<InetAddress>();
                while (networkInterfaces.hasMoreElements()) {
                    NetworkInterface networkInterface = networkInterfaces.nextElement();
                    Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
                    while (inetAddresses.hasMoreElements()) {
                        InetAddress inetAddress = inetAddresses.nextElement();
                        if (inetAddress instanceof Inet4Address) {
                            addresses.add(inetAddress);
                        }
                    }
                }
                allHostIPV4Address = addresses;
            } catch (SocketException e) {
                log.error("获取ip地址失败", e);
                throw new RuntimeException(e.getMessage(), e);
            }
        }
        return allHostIPV4Address;
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
@@ -1,93 +1,10 @@
package com.ruoyi.common.utils.poi;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import com.sun.rowset.internal.Row;
import javafx.scene.control.Cell;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.RegExUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
//import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
//import org.apache.poi.hssf.usermodel.HSSFPicture;
//import org.apache.poi.hssf.usermodel.HSSFPictureData;
//import org.apache.poi.hssf.usermodel.HSSFShape;
//import org.apache.poi.hssf.usermodel.HSSFSheet;
//import org.apache.poi.hssf.usermodel.HSSFWorkbook;
//import org.apache.poi.ooxml.POIXMLDocumentPart;
//import org.apache.poi.ss.usermodel.BorderStyle;
//import org.apache.poi.ss.usermodel.Cell;
//import org.apache.poi.ss.usermodel.CellStyle;
//import org.apache.poi.ss.usermodel.CellType;
//import org.apache.poi.ss.usermodel.ClientAnchor;
//import org.apache.poi.ss.usermodel.DataValidation;
//import org.apache.poi.ss.usermodel.DataValidationConstraint;
//import org.apache.poi.ss.usermodel.DataValidationHelper;
//import org.apache.poi.ss.usermodel.DateUtil;
//import org.apache.poi.ss.usermodel.Drawing;
//import org.apache.poi.ss.usermodel.FillPatternType;
//import org.apache.poi.ss.usermodel.Font;
//import org.apache.poi.ss.usermodel.HorizontalAlignment;
//import org.apache.poi.ss.usermodel.IndexedColors;
//import org.apache.poi.ss.usermodel.Name;
//import org.apache.poi.ss.usermodel.PictureData;
//import org.apache.poi.ss.usermodel.Row;
//import org.apache.poi.ss.usermodel.Sheet;
//import org.apache.poi.ss.usermodel.VerticalAlignment;
//import org.apache.poi.ss.usermodel.Workbook;
//import org.apache.poi.ss.usermodel.WorkbookFactory;
//import org.apache.poi.ss.util.CellRangeAddress;
//import org.apache.poi.ss.util.CellRangeAddressList;
//import org.apache.poi.util.IOUtils;
//import org.apache.poi.xssf.streaming.SXSSFWorkbook;
//import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
//import org.apache.poi.xssf.usermodel.XSSFDataValidation;
//import org.apache.poi.xssf.usermodel.XSSFDrawing;
//import org.apache.poi.xssf.usermodel.XSSFPicture;
//import org.apache.poi.xssf.usermodel.XSSFShape;
//import org.apache.poi.xssf.usermodel.XSSFSheet;
//import org.apache.poi.xssf.usermodel.XSSFWorkbook;
//import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
//import com.ruoyi.common.annotation.Excel;
//import com.ruoyi.common.annotation.Excel.ColumnType;
//import com.ruoyi.common.annotation.Excel.Type;
import com.ruoyi.common.annotation.Excels;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.exception.UtilException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileTypeUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.common.utils.file.ImageUtils;
import com.ruoyi.common.utils.reflect.ReflectUtils;
import java.util.HashMap;
import java.util.Map;
/**
 * Excel相关处理
ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
@@ -111,8 +111,15 @@
                // 过滤请求
                .authorizeRequests()
                // 对于登录login 注册register 验证码captchaImage 允许匿名访问
                .antMatchers("/getPrivacyAgreement/{agreementType}","/applet/queryProtocolConfigByType","/applet/login","/login","/applet/queryProtocolConfigByType", "/register","/applet/getCode","/applet/loginCode","/applet/changepwd", "/captchaImage","/getCode","/loginCode","/operations/getBySingleNum/**",
                        "/user/getUserInfoByNumber/**").permitAll()
                .antMatchers("/getPrivacyAgreement/{agreementType}",
                        "/applet/queryProtocolConfigByType","/applet/login",
                        "/login","/applet/queryProtocolConfigByType",
                        "/register","/applet/getCode","/applet/loginCode",
                        "/applet/changepwd", "/captchaImage","/getCode","/loginCode",
                        "/operations/getBySingleNum/**",
                        "/user/getUserInfoByNumber/**",
                        "/open/**"
                ).permitAll()
                // 静态资源,可匿名访问
                .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
                .antMatchers("/swagger-ui.html","/doc.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java
@@ -25,4 +25,10 @@
    {
        System.out.println("执行无参方法");
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/dto/MakeOrderDto.java
New file
@@ -0,0 +1,38 @@
package com.ruoyi.system.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.List;
@Data
@ApiModel(value = "使用账单创建订单请求")
public class MakeOrderDto implements Serializable {
    /**
     * 用户ID
     */
    @ApiModelProperty(value = "用户ID")
    @NotBlank(message = "用户ID不能为空")
    private String userId;
    /**
     * 用户openid
     */
    @ApiModelProperty(value = "用户微信或支付宝openid")
    private String openId;
    @ApiModelProperty(value = "支付金额,单位:分")
    @NotNull(message = "用户ID不能为空")
    private Long amount;
    @ApiModelProperty(value = "账单ID列表",notes = "如果只有1个账单ID,金额可以小于账单金额,进行部分缴费;如果是多个账单,将会核对金额,未交费金额需要与缴费金额一致")
    @NotEmpty(message = "用户ID不能为空")
    private List<String> billIds;
}
ruoyi-system/src/main/java/com/ruoyi/system/dto/MakeOrderResp.java
New file
@@ -0,0 +1,23 @@
package com.ruoyi.system.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
@Data
@ApiModel(value = "使用账单创建订单请求")
public class MakeOrderResp implements Serializable {
    /**
     * 订单编号
     */
    @ApiModelProperty("订单编号")
    private String orderNo;
    @ApiModelProperty("金额,分")
    private Long amount;
    @ApiModelProperty("微信小程序支付跳转链接,用于生成二维码")
    private String appletUrl;
}
ruoyi-system/src/main/java/com/ruoyi/system/dto/TBillDto.java
New file
@@ -0,0 +1,19 @@
package com.ruoyi.system.dto;
import com.ruoyi.system.model.TBill;
import lombok.Data;
import java.util.List;
@Data
public class TBillDto extends TBill {
    private String residentName;
    private String phone;
    private String account;
    private String houseName;
}
ruoyi-system/src/main/java/com/ruoyi/system/dto/TInvoiceDTO.java
New file
@@ -0,0 +1,17 @@
package com.ruoyi.system.dto;
import com.ruoyi.system.model.TInvoice;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
@ApiModel(value = "开票DTO")
public class TInvoiceDTO extends TInvoice {
    @ApiModelProperty(value = "账单id集合")
    private List<String> billIds;
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TBankFlowMapper.java
New file
@@ -0,0 +1,16 @@
package com.ruoyi.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.system.model.TBankFlow;
/**
 * <p>
 * 流水管理 Mapper 接口
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-07
 */
public interface TBankFlowMapper extends BaseMapper<TBankFlow> {
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TBillConfirmMapper.java
New file
@@ -0,0 +1,16 @@
package com.ruoyi.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.system.model.TBillConfirm;
/**
 * <p>
 * 账单线下缴费关联表 Mapper 接口
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-10
 */
public interface TBillConfirmMapper extends BaseMapper<TBillConfirm> {
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TBillDetailMapper.java
New file
@@ -0,0 +1,16 @@
package com.ruoyi.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.system.model.TBillDetail;
/**
 * <p>
 * 账单水电费子表 Mapper 接口
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-10
 */
public interface TBillDetailMapper extends BaseMapper<TBillDetail> {
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TBillMapper.java
@@ -1,7 +1,12 @@
package com.ruoyi.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.common.basic.PageInfo;
import com.ruoyi.system.dto.TBillDto;
import com.ruoyi.system.model.TBill;
import com.ruoyi.system.query.TBillQuery;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
/**
 * <p>
@@ -13,4 +18,6 @@
 */
public interface TBillMapper extends BaseMapper<TBill> {
    PageInfo<TBillDto> page(@Param("pageInfo") PageInfo<TBill> pageInfo, @Param("query") TBillQuery query);
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TInvoiceToBillMapper.java
New file
@@ -0,0 +1,16 @@
package com.ruoyi.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.system.model.TInvoiceToBill;
/**
 * <p>
 * 账单开票中间表 Mapper 接口
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-10
 */
public interface TInvoiceToBillMapper extends BaseMapper<TInvoiceToBill> {
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TOrderBillMapper.java
New file
@@ -0,0 +1,16 @@
package com.ruoyi.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.system.model.TOrderBill;
/**
 * <p>
 * 订单表与账单的关联表 Mapper 接口
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-07
 */
public interface TOrderBillMapper extends BaseMapper<TOrderBill> {
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TPayOrderMapper.java
New file
@@ -0,0 +1,16 @@
package com.ruoyi.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.system.model.TPayOrder;
/**
 * <p>
 * 支付订单表 Mapper 接口
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-07
 */
public interface TPayOrderMapper extends BaseMapper<TPayOrder> {
}
ruoyi-system/src/main/java/com/ruoyi/system/model/TBankFlow.java
New file
@@ -0,0 +1,90 @@
package com.ruoyi.system.model;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
 * <p>
 * 流水管理
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-07
 */
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("t_bank_flow")
@ApiModel(value="TBankFlow对象", description="流水管理")
public class TBankFlow implements Serializable {
    private static final long serialVersionUID = 1L;
    @TableId("id")
    private String id;
    @ApiModelProperty(value = "银行流水号")
    @TableField("bank_serial_number")
    private String bankSerialNumber;
    @ApiModelProperty(value = "流水金额")
    @TableField("flow_money")
    private Double flowMoney;
    @ApiModelProperty(value = "抵扣金额")
    @TableField("deduction_money")
    private Double deductionMoney;
    @ApiModelProperty(value = "剩余金额")
    @TableField("remaining_money")
    private Double remainingMoney;
    @ApiModelProperty(value = "支付时间")
    @TableField("pay_time")
    private LocalDateTime payTime;
    @ApiModelProperty(value = "付款人")
    @TableField("payer")
    private String payer;
    @ApiModelProperty(value = "流水状态 1=正常 2=异常")
    @TableField("flow_status")
    private Integer flowStatus;
    @ApiModelProperty(value = "支付方式 1=微信支付 2=支付宝支付 3=线下支付")
    @TableField("pay_type")
    private Integer payType;
    @ApiModelProperty(value = "缴费账单id")
    @TableField("payment_bill_id")
    private String paymentBillId;
    @ApiModelProperty(value = "创建时间")
    @TableField("create_time")
    private LocalDateTime createTime;
    @ApiModelProperty(value = "修改时间")
    @TableField("update_time")
    private LocalDateTime updateTime;
    @ApiModelProperty(value = "创建人")
    @TableField("create_by")
    private String createBy;
    @ApiModelProperty(value = "修改人")
    @TableField("update_by")
    private String updateBy;
    @ApiModelProperty(value = "是否删除 0=否 1=是")
    @TableField("disabled")
    private Integer disabled;
}
ruoyi-system/src/main/java/com/ruoyi/system/model/TBillConfirm.java
New file
@@ -0,0 +1,46 @@
package com.ruoyi.system.model;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
/**
 * <p>
 * 账单线下缴费关联表
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-10
 */
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("t_bill_confirm")
@ApiModel(value="TBillConfirm对象", description="账单线下缴费关联表")
public class TBillConfirm implements Serializable {
    private static final long serialVersionUID = 1L;
    @TableId(value = "id", type = IdType.ASSIGN_ID)
    private String id;
    @ApiModelProperty(value = "账单id,逗号分隔")
    @TableField("bill_id")
    private String billId;
    @ApiModelProperty(value = "缴费金额")
    @TableField("pay_fees_money")
    private Double payFeesMoney;
    @ApiModelProperty(value = "凭证上传")
    @TableField("voucher")
    private String voucher;
}
ruoyi-system/src/main/java/com/ruoyi/system/model/TBillDetail.java
New file
@@ -0,0 +1,64 @@
package com.ruoyi.system.model;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ruoyi.common.core.domain.BaseModel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * <p>
 * 账单水电费子表
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-10
 */
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("t_bill_detail")
@ApiModel(value="TBillDetail对象", description="账单水电费子表")
public class TBillDetail extends BaseModel {
    private static final long serialVersionUID = 1L;
    @TableId(value = "id", type = IdType.ASSIGN_ID)
    private String id;
    @ApiModelProperty(value = "账单id")
    @TableField("bill_id")
    private String billId;
    @ApiModelProperty(value = "初始读数")
    @TableField("start_reading")
    private BigDecimal startReading;
    @ApiModelProperty(value = "结束读数")
    @TableField("end_reading")
    private BigDecimal endReading;
    @ApiModelProperty(value = "使用量")
    @TableField("usage_measure")
    private BigDecimal usageMeasure;
    @ApiModelProperty(value = "单价")
    @TableField("unit_price")
    private BigDecimal unitPrice;
    @ApiModelProperty(value = "总金额")
    @TableField("total_amount")
    private BigDecimal totalAmount;
    @ApiModelProperty(value = "费用类型 1=水费 2=电费")
    @TableField("live_type")
    private Integer liveType;
}
ruoyi-system/src/main/java/com/ruoyi/system/model/TInvoiceToBill.java
New file
@@ -0,0 +1,42 @@
package com.ruoyi.system.model;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
/**
 * <p>
 * 账单开票中间表
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-10
 */
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("t_invoice_to_bill")
@ApiModel(value="TInvoiceToBill对象", description="账单开票中间表")
public class TInvoiceToBill implements Serializable {
    private static final long serialVersionUID = 1L;
    @TableId(value = "id", type = IdType.ASSIGN_ID)
    private String id;
    @ApiModelProperty(value = "开票id")
    @TableField("invoice_id")
    private String invoiceId;
    @ApiModelProperty(value = "账单id")
    @TableField("bill_id")
    private String billId;
}
ruoyi-system/src/main/java/com/ruoyi/system/model/TOrderBill.java
New file
@@ -0,0 +1,54 @@
package com.ruoyi.system.model;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
 * <p>
 * 订单表与账单的关联表
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-07
 */
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
@TableName("t_order_bill")
@ApiModel(value="TOrderBill对象", description="订单表与账单的关联表")
public class TOrderBill implements Serializable {
    private static final long serialVersionUID = 1L;
    @TableId(value = "id", type = IdType.ASSIGN_ID)
    private String id;
    @ApiModelProperty(value = "订单号")
    @TableField("order_no")
    private String orderNo;
    @ApiModelProperty(value = "账单编号")
    @TableField("bill_id")
    private String billId;
    @TableField("create_time")
    private LocalDateTime createTime;
    @TableField("update_time")
    private LocalDateTime updateTime;
    public TOrderBill(String orderNo, String billId) {
        this.orderNo = orderNo;
        this.billId = billId;
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/model/TPayOrder.java
New file
@@ -0,0 +1,88 @@
package com.ruoyi.system.model;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
 * <p>
 * 支付订单表
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-07
 */
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("t_pay_order")
@ApiModel(value="TPayOrder对象", description="支付订单表")
public class TPayOrder implements Serializable {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value = "订单号")
    @TableId("id")
    private String id;
    @ApiModelProperty(value = "发起支付的用户ID")
    @TableField("user_id")
    private String userId;
    @ApiModelProperty(value = "用户名称")
    @TableField("user_name")
    private String userName;
    @ApiModelProperty(value = "用户微信openid或支付宝userid")
    @TableField("open_id")
    private String openId;
    @ApiModelProperty(value = "用户手机号码")
    @TableField("phone")
    private String phone;
    @ApiModelProperty(value = "订单金额")
    @TableField("amount")
    private Long amount;
    @ApiModelProperty(value = "实付金额")
    @TableField("act_pay_amount")
    private Long actPayAmount;
    @ApiModelProperty(value = "支付时间")
    @TableField("pay_time")
    private LocalDateTime payTime;
    @ApiModelProperty(value = "支付方式")
    @TableField("pay_type")
    private String payType;
    @ApiModelProperty(value = "支付的回调信息")
    @TableField("pay_info")
    private String payInfo;
    @ApiModelProperty(value = "银行的支付流水编号")
    @TableField("pay_no")
    private String payNo;
    @ApiModelProperty(value = "支付的回调时间")
    @TableField("callback_time")
    private LocalDateTime callbackTime;
    @ApiModelProperty(value = "订单创建时间")
    @TableField("create_time")
    private LocalDateTime createTime;
    @ApiModelProperty(value = "订单更新时间")
    @TableField("update_time")
    private LocalDateTime updateTime;
}
ruoyi-system/src/main/java/com/ruoyi/system/query/TBankFlowQuery.java
New file
@@ -0,0 +1,60 @@
package com.ruoyi.system.query;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ruoyi.common.core.domain.BasePage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
 * <p>
 * 流水管理
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-07
 */
@Data
@ApiModel(value="银行流水列表query")
public class TBankFlowQuery extends BasePage {
    @ApiModelProperty(value = "银行流水号")
    private String bankSerialNumber;
    @ApiModelProperty(value = "流水金额")
    private Double flowMoney;
    @ApiModelProperty(value = "抵扣金额")
    private Double deductionMoney;
    @ApiModelProperty(value = "剩余金额")
    private Double remainingMoney;
    @ApiModelProperty(value = "支付时间")
    private LocalDateTime payTime;
    @ApiModelProperty(value = "付款人")
    private String payer;
    @ApiModelProperty(value = "流水状态 1=正常 2=异常")
    private Integer flowStatus;
    @ApiModelProperty(value = "支付方式 1=微信支付 2=支付宝支付 3=线下支付")
    private Integer payType;
    @ApiModelProperty(value = "缴费账单id")
    private String paymentBillId;
    @ApiModelProperty(value = "支付开始时间")
    private String payStartTime;
    @ApiModelProperty(value = "支付结束时间")
    private String payEndTime;
}
ruoyi-system/src/main/java/com/ruoyi/system/query/TBillQuery.java
@@ -1,15 +1,29 @@
package com.ruoyi.system.query;
import com.ruoyi.common.core.domain.BasePage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(value = "账单查询对象query")
public class TBillQuery extends BasePage {
    /**
     * 缴费状态 1=未缴费 2=待确认 3=已缴费 4=已逾期
     */
    @ApiModelProperty("缴费状态 1=未缴费 2=待确认 3=已缴费 4=已逾期")
    private Integer payFeesStatus;
    @ApiModelProperty("联系电话")
    private String phone;
    @ApiModelProperty("租户名称")
    private String residentName;
    @ApiModelProperty("合同编号")
    private String contractNumber;
    /**
     * 租户ID
     */
    @ApiModelProperty("租户ID")
    private String userId;
    @ApiModelProperty(value = "缴费状态 1=未缴费 2=待确认 3=已缴费 4=已逾期 5=已失效")
    private String payFeesStatus;
}
ruoyi-system/src/main/java/com/ruoyi/system/query/TFlowManagementQuery.java
New file
@@ -0,0 +1,63 @@
package com.ruoyi.system.query;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.core.domain.BasePage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * <p>
 * 流水管理
 * </p>
 *
 * @author xiaochen
 * @since 2025-01-20
 */
@Data
@ApiModel(value="流水列表query")
public class TFlowManagementQuery extends BasePage {
    @ApiModelProperty(value = "系统流水号")
    private String sysSerialNumber;
    @ApiModelProperty(value = "银行流水号")
    private String bankSerialNumber;
    @ApiModelProperty(value = "流水金额")
    private BigDecimal flowMoney;
    @ApiModelProperty(value = "抵扣金额")
    private BigDecimal deductionMoney;
    @ApiModelProperty(value = "剩余金额")
    private BigDecimal remainingMoney;
    @ApiModelProperty(value = "支付时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime payTime;
    @ApiModelProperty(value = "付款人")
    private String payer;
    @ApiModelProperty(value = "流水状态 1=正常 2=异常")
    private Integer flowStatus;
    @ApiModelProperty(value = "支付方式 1=微信支付 2=支付宝支付 3=线下支付")
    private Integer payType;
    @ApiModelProperty(value = "缴费账单id")
    private String paymentBillId;
    @ApiModelProperty(value = "支付开始时间")
    private String payStartTime;
    @ApiModelProperty(value = "支付结束时间")
    private String payEndTime;
}
ruoyi-system/src/main/java/com/ruoyi/system/query/TInvoiceQuery.java
New file
@@ -0,0 +1,55 @@
package com.ruoyi.system.query;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.core.domain.BasePage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@ApiModel(value="开票查询列表Query")
public class TInvoiceQuery extends BasePage {
    @ApiModelProperty(value = "申请编号")
    private String invoiceNumber;
    @ApiModelProperty(value = "金额")
    private Double invoiceMoney;
    @ApiModelProperty(value = "申请人")
    private String applyName;
    @ApiModelProperty(value = "抬头类型 1=企业 2=个人")
    private Integer titleType;
    @ApiModelProperty(value = "抬头名称")
    private String titleName;
    @ApiModelProperty(value = "税号/身份证号")
    private String serialNumber;
    @ApiModelProperty(value = "开票状态  1=待开票 2=已开票")
    private Integer status;
    @ApiModelProperty(value = "开票日期")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime invoiceTime;
    @ApiModelProperty(value = "开票凭证")
    private String invoiceVoucher;
    @ApiModelProperty(value = "邮箱")
    private String email;
    @ApiModelProperty(value = "开票合同编号逗号分割")
    private String contractNumber;
    @ApiModelProperty(value = "开票开始日期")
    private String invoiceStartTime;
    @ApiModelProperty(value = "开票结束日期")
    private String invoiceEndTime;
}
ruoyi-system/src/main/java/com/ruoyi/system/service/TBankFlowService.java
New file
@@ -0,0 +1,26 @@
package com.ruoyi.system.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.basic.PageInfo;
import com.ruoyi.system.model.TBankFlow;
import com.ruoyi.system.model.TFlowManagement;
import com.ruoyi.system.query.TBankFlowQuery;
import com.ruoyi.system.query.TFlowManagementQuery;
import java.util.List;
/**
 * <p>
 * 流水管理 服务类
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-07
 */
public interface TBankFlowService extends IService<TBankFlow> {
    PageInfo<TBankFlow> pageList(TBankFlowQuery query);
    List<TBankFlow> makeQuery(TBankFlowQuery query);
}
ruoyi-system/src/main/java/com/ruoyi/system/service/TBillConfirmService.java
New file
@@ -0,0 +1,16 @@
package com.ruoyi.system.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.system.model.TBillConfirm;
/**
 * <p>
 * 账单线下缴费关联表 服务类
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-10
 */
public interface TBillConfirmService extends IService<TBillConfirm> {
}
ruoyi-system/src/main/java/com/ruoyi/system/service/TBillDetailService.java
New file
@@ -0,0 +1,16 @@
package com.ruoyi.system.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.system.model.TBillDetail;
/**
 * <p>
 * 账单水电费子表 服务类
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-10
 */
public interface TBillDetailService extends IService<TBillDetail> {
}
ruoyi-system/src/main/java/com/ruoyi/system/service/TBillService.java
@@ -2,9 +2,9 @@
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.basic.PageInfo;
import com.ruoyi.system.dto.TBillDto;
import com.ruoyi.system.model.TBill;
import com.ruoyi.system.query.TBillQuery;
import com.ruoyi.system.vo.TBillVO;
/**
 * <p>
@@ -16,11 +16,6 @@
 */
public interface TBillService extends IService<TBill> {
    /**
     * 获取账单列表
     * @param query
     * @return
     */
    PageInfo<TBillVO> pageList(TBillQuery query);
    PageInfo<TBillDto> queryPage(TBillQuery query);
}
ruoyi-system/src/main/java/com/ruoyi/system/service/TFlowManagementService.java
@@ -1,7 +1,11 @@
package com.ruoyi.system.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.basic.PageInfo;
import com.ruoyi.system.model.TFlowManagement;
import com.ruoyi.system.query.TFlowManagementQuery;
import java.util.List;
/**
 * <p>
@@ -12,5 +16,9 @@
 * @since 2025-01-17
 */
public interface TFlowManagementService extends IService<TFlowManagement> {
    PageInfo<TFlowManagement> pageList(TFlowManagementQuery query);
    List<TFlowManagement> makeQuery(TFlowManagementQuery query);
}
ruoyi-system/src/main/java/com/ruoyi/system/service/TInvoiceService.java
@@ -1,7 +1,11 @@
package com.ruoyi.system.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.basic.PageInfo;
import com.ruoyi.system.model.TInvoice;
import com.ruoyi.system.query.TInvoiceQuery;
import java.util.List;
/**
 * <p>
@@ -12,5 +16,6 @@
 * @since 2025-01-17
 */
public interface TInvoiceService extends IService<TInvoice> {
    PageInfo<TInvoice> pageList(TInvoiceQuery query);
    List<TInvoice> makeQuery(TInvoiceQuery query);
}
ruoyi-system/src/main/java/com/ruoyi/system/service/TInvoiceToBillService.java
New file
@@ -0,0 +1,16 @@
package com.ruoyi.system.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.system.model.TInvoiceToBill;
/**
 * <p>
 * 账单开票中间表 服务类
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-10
 */
public interface TInvoiceToBillService extends IService<TInvoiceToBill> {
}
ruoyi-system/src/main/java/com/ruoyi/system/service/TOrderBillService.java
New file
@@ -0,0 +1,16 @@
package com.ruoyi.system.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.system.model.TOrderBill;
/**
 * <p>
 * 订单表与账单的关联表 服务类
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-07
 */
public interface TOrderBillService extends IService<TOrderBill> {
}
ruoyi-system/src/main/java/com/ruoyi/system/service/TPayOrderService.java
New file
@@ -0,0 +1,19 @@
package com.ruoyi.system.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.system.dto.MakeOrderDto;
import com.ruoyi.system.dto.MakeOrderResp;
import com.ruoyi.system.model.TPayOrder;
/**
 * <p>
 * 支付订单表 服务类
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-07
 */
public interface TPayOrderService extends IService<TPayOrder> {
    MakeOrderResp makeOrder(MakeOrderDto dto);
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TBankFlowServiceImpl.java
New file
@@ -0,0 +1,46 @@
package com.ruoyi.system.service.impl;
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.utils.StringUtils;
import com.ruoyi.system.mapper.TBankFlowMapper;
import com.ruoyi.system.model.TBankFlow;
import com.ruoyi.system.model.TFlowManagement;
import com.ruoyi.system.query.TBankFlowQuery;
import com.ruoyi.system.service.TBankFlowService;
import org.springframework.stereotype.Service;
import java.util.List;
/**
 * <p>
 * 流水管理 服务实现类
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-07
 */
@Service
public class TBankFlowServiceImpl extends ServiceImpl<TBankFlowMapper, TBankFlow> implements TBankFlowService {
    @Override
    public PageInfo<TBankFlow> pageList(TBankFlowQuery query) {
        PageInfo<TBankFlow> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize());
        List<TBankFlow> list = makeQuery(query);
        pageInfo.setRecords(list);
        pageInfo.setTotal(list.size());
        return pageInfo;
    }
    @Override
    public List<TBankFlow> makeQuery(TBankFlowQuery query) {
        LambdaQueryWrapper<TBankFlow> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StringUtils.isNotEmpty(query.getBankSerialNumber()),TBankFlow::getBankSerialNumber,query.getBankSerialNumber())
                .like(StringUtils.isNotEmpty(query.getPayer()),TBankFlow::getPayer,query.getPayer())
                .ge(StringUtils.isNotEmpty(query.getPayStartTime()),TBankFlow::getPayTime,query.getPayStartTime())
                .lt(StringUtils.isNotEmpty(query.getPayEndTime()),TBankFlow::getPayTime,query.getPayEndTime())
                .eq(null != query.getFlowStatus(),TBankFlow::getFlowStatus,query.getFlowStatus())
                .orderByDesc(TBankFlow::getCreateTime)
        ;
        return this.baseMapper.selectList(queryWrapper);
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TBillConfirmServiceImpl.java
New file
@@ -0,0 +1,20 @@
package com.ruoyi.system.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.system.mapper.TBillConfirmMapper;
import com.ruoyi.system.model.TBillConfirm;
import com.ruoyi.system.service.TBillConfirmService;
import org.springframework.stereotype.Service;
/**
 * <p>
 * 账单线下缴费关联表 服务实现类
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-10
 */
@Service
public class TBillConfirmServiceImpl extends ServiceImpl<TBillConfirmMapper, TBillConfirm> implements TBillConfirmService {
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TBillDetailServiceImpl.java
New file
@@ -0,0 +1,20 @@
package com.ruoyi.system.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.system.mapper.TBillDetailMapper;
import com.ruoyi.system.model.TBillDetail;
import com.ruoyi.system.service.TBillDetailService;
import org.springframework.stereotype.Service;
/**
 * <p>
 * 账单水电费子表 服务实现类
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-10
 */
@Service
public class TBillDetailServiceImpl extends ServiceImpl<TBillDetailMapper, TBillDetail> implements TBillDetailService {
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TBillServiceImpl.java
@@ -1,13 +1,22 @@
package com.ruoyi.system.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.basic.PageInfo;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.system.dto.TBillDto;
import com.ruoyi.system.mapper.TBillMapper;
import com.ruoyi.system.model.TBill;
import com.ruoyi.system.model.TContract;
import com.ruoyi.system.model.THouse;
import com.ruoyi.system.query.TBillQuery;
import com.ruoyi.system.service.TBillService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.system.vo.TBillVO;
import io.jsonwebtoken.lang.Assert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
/**
 * <p>
@@ -20,8 +29,30 @@
@Service
public class TBillServiceImpl extends ServiceImpl<TBillMapper, TBill> implements TBillService {
    @Override
    public PageInfo<TBillVO> pageList(TBillQuery query) {
        return null;
    @Autowired
    RedisCache redisCache;
    @Autowired
    TBillMapper tBillMapper;
    public PageInfo<TBillDto> queryPage(TBillQuery query){
        PageInfo<TBill> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize());
        PageInfo<TBillDto> info = tBillMapper.page(pageInfo, query);
        return info;
    }
    /**
     * 传的金额
     * @param tBill
     * @return
     */
    public Boolean checkAndUpdateBill(TBill tBill){
        return true;
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TFlowManagementServiceImpl.java
@@ -1,10 +1,16 @@
package com.ruoyi.system.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.basic.PageInfo;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.mapper.TFlowManagementMapper;
import com.ruoyi.system.model.TFlowManagement;
import com.ruoyi.system.query.TFlowManagementQuery;
import com.ruoyi.system.service.TFlowManagementService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
/**
 * <p>
@@ -17,4 +23,25 @@
@Service
public class TFlowManagementServiceImpl extends ServiceImpl<TFlowManagementMapper, TFlowManagement> implements TFlowManagementService {
    @Override
    public PageInfo<TFlowManagement> pageList(TFlowManagementQuery query) {
        PageInfo<TFlowManagement> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize());
        List<TFlowManagement> list = makeQuery(query);
        pageInfo.setRecords(list);
        pageInfo.setTotal(list.size());
        return pageInfo;
    }
    @Override
    public List<TFlowManagement> makeQuery(TFlowManagementQuery query){
        LambdaQueryWrapper<TFlowManagement> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StringUtils.isNotEmpty(query.getSysSerialNumber()),TFlowManagement::getSysSerialNumber,query.getSysSerialNumber())
                .eq(StringUtils.isNotEmpty(query.getBankSerialNumber()),TFlowManagement::getBankSerialNumber,query.getBankSerialNumber())
                .like(StringUtils.isNotEmpty(query.getPayer()),TFlowManagement::getPayer,query.getPayer())
                .ge(StringUtils.isNotEmpty(query.getPayStartTime()),TFlowManagement::getPayTime,query.getPayStartTime())
                .lt(StringUtils.isNotEmpty(query.getPayEndTime()),TFlowManagement::getPayTime,query.getPayEndTime())
                .eq(null != query.getFlowStatus(),TFlowManagement::getFlowStatus,query.getFlowStatus())
                .orderByDesc(TFlowManagement::getCreateTime)
        ;
        return this.baseMapper.selectList(queryWrapper);
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TInvoiceServiceImpl.java
@@ -1,10 +1,16 @@
package com.ruoyi.system.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.basic.PageInfo;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.mapper.TInvoiceMapper;
import com.ruoyi.system.model.TInvoice;
import com.ruoyi.system.query.TInvoiceQuery;
import com.ruoyi.system.service.TInvoiceService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
/**
 * <p>
@@ -17,4 +23,24 @@
@Service
public class TInvoiceServiceImpl extends ServiceImpl<TInvoiceMapper, TInvoice> implements TInvoiceService {
    @Override
    public PageInfo<TInvoice> pageList(TInvoiceQuery query) {
        PageInfo<TInvoice> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize());
        List<TInvoice> list = makeQuery(query);
        pageInfo.setRecords(list);
        pageInfo.setTotal(list.size());
        return pageInfo;
    }
    @Override
    public List<TInvoice> makeQuery(TInvoiceQuery query) {
        LambdaQueryWrapper<TInvoice> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(null != query.getTitleType(),TInvoice::getTitleType,query.getTitleType())
                .like(StringUtils.isNotEmpty(query.getTitleName()),TInvoice::getTitleName,query.getTitleName())
                .eq(null != query.getStatus(),TInvoice::getStatus,query.getStatus())
                .ge(StringUtils.isNotEmpty(query.getInvoiceStartTime()),TInvoice::getInvoiceTime,query.getInvoiceStartTime())
                .le(StringUtils.isNotEmpty(query.getInvoiceEndTime()),TInvoice::getInvoiceTime,query.getInvoiceEndTime())
                .orderByDesc(TInvoice::getCreateTime);
        return this.baseMapper.selectList(queryWrapper);
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TInvoiceToBillServiceImpl.java
New file
@@ -0,0 +1,20 @@
package com.ruoyi.system.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.system.mapper.TInvoiceToBillMapper;
import com.ruoyi.system.model.TInvoiceToBill;
import com.ruoyi.system.service.TInvoiceToBillService;
import org.springframework.stereotype.Service;
/**
 * <p>
 * 账单开票中间表 服务实现类
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-10
 */
@Service
public class TInvoiceToBillServiceImpl extends ServiceImpl<TInvoiceToBillMapper, TInvoiceToBill> implements TInvoiceToBillService {
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TOrderBillServiceImpl.java
New file
@@ -0,0 +1,20 @@
package com.ruoyi.system.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.system.mapper.TOrderBillMapper;
import com.ruoyi.system.model.TOrderBill;
import com.ruoyi.system.service.TOrderBillService;
import org.springframework.stereotype.Service;
/**
 * <p>
 * 订单表与账单的关联表 服务实现类
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-07
 */
@Service
public class TOrderBillServiceImpl extends ServiceImpl<TOrderBillMapper, TOrderBill> implements TOrderBillService {
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TPayOrderServiceImpl.java
New file
@@ -0,0 +1,129 @@
package com.ruoyi.system.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.constant.AmountConstant;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.OrderNos;
import com.ruoyi.system.dto.MakeOrderDto;
import com.ruoyi.system.dto.MakeOrderResp;
import com.ruoyi.system.mapper.TPayOrderMapper;
import com.ruoyi.system.model.TBill;
import com.ruoyi.system.model.TOrderBill;
import com.ruoyi.system.model.TPayOrder;
import com.ruoyi.system.model.TTenant;
import com.ruoyi.system.service.TBillService;
import com.ruoyi.system.service.TOrderBillService;
import com.ruoyi.system.service.TPayOrderService;
import com.ruoyi.system.service.TTenantService;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
 * <p>
 * 支付订单表 服务实现类
 * </p>
 *
 * @author xiaochen
 * @since 2025-02-07
 */
@Service
public class TPayOrderServiceImpl extends ServiceImpl<TPayOrderMapper, TPayOrder> implements TPayOrderService {
    @Autowired
    TBillService billService;
    @Autowired
    TTenantService tTenantService;
    @Autowired
    TOrderBillService tOrderBillService;
    public long caculateRentFee(TBill bill){
        return bill.getOutstandingMoney()
                .longValue();
    }
    public String createAppletUrl(String orderNo){
        return ""+orderNo;
    }
    @Override
    public MakeOrderResp makeOrder(MakeOrderDto dto) {
        TTenant user = tTenantService.getById(dto.getUserId());
        if (user==null){
            throw new ServiceException("租户不存在");
        }
        MakeOrderResp resp = new MakeOrderResp();
        if (dto.getBillIds().size()==1){
            TBill bill = billService.getById(dto.getBillIds().get(0));
            if (bill == null) {
                throw new ServiceException("账单不存在");
            }
            //计算欠费金额:租金+违约金-实收金额
            long rent = caculateRentFee(bill);
            if (rent==0){
                throw new ServiceException("该订单已缴费");
            }
            if (rent>dto.getAmount()){
                throw new ServiceException("支付金额超过了账单欠费金额");
            }
            TPayOrder order = new TPayOrder();
            order.setId(OrderNos.getDid(32));
            order.setAmount(dto.getAmount());
            order.setUserId(user.getId());
            order.setPhone(user.getPhone());
            order.setOpenId(dto.getOpenId());
            order.setUserName(user.getResidentName());
            save(order);
            TOrderBill tOrderBill = new TOrderBill();
            tOrderBill.setBillId(bill.getId());
            tOrderBill.setOrderNo(order.getId());
            tOrderBillService.save(tOrderBill);
            resp.setAmount(dto.getAmount());
            resp.setOrderNo(order.getId());
            resp.setAppletUrl(createAppletUrl(order.getId()));
            return resp;
        }
        List<TOrderBill> orderBills = new ArrayList<>();
        String orderNo = OrderNos.getDid(32);
        List<TBill> bills = dto.getBillIds().stream().map(id -> {
            TBill bill = billService.getById(id);
            if (bill == null) {
                throw new ServiceException("billId:" + id + "不存在");
            }
            orderBills.add(new TOrderBill(orderNo,bill.getId()));
            return bill;
        }).collect(Collectors.toList());
        long sumRent = bills.stream().mapToLong((bill) -> caculateRentFee(bill)).sum();
        if (sumRent==0){
            throw new ServiceException("账单已缴费");
        }
        if (dto.getAmount()>sumRent){
            throw new ServiceException("支付金额超过所选账单欠费金额");
        }
        TPayOrder order = new TPayOrder();
        order.setId(orderNo);
        order.setAmount(dto.getAmount());
        order.setUserId(user.getId());
        order.setPhone(user.getPhone());
        order.setOpenId(dto.getOpenId());
        order.setUserName(user.getResidentName());
        save(order);
        tOrderBillService.saveBatch(orderBills);
        resp.setAmount(dto.getAmount());
        resp.setOrderNo(orderNo);
        resp.setAppletUrl(createAppletUrl(orderNo));
        return resp;
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/vo/TBillVO.java
@@ -1,10 +1,18 @@
package com.ruoyi.system.vo;
import com.ruoyi.system.model.TBill;
import com.ruoyi.system.model.TBillDetail;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
@ApiModel(value = "缴费账单VO")
public class TBillVO extends TBill {
    @ApiModelProperty(value = "水电费明细")
    private List<TBillDetail> billDetailList;
}
ruoyi-system/src/main/resources/mapper/system/TBankFlowMapper.xml
New file
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.system.mapper.TBankFlowMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.ruoyi.system.model.TBankFlow">
        <id column="id" property="id" />
        <result column="bank_serial_number" property="bankSerialNumber" />
        <result column="flow_money" property="flowMoney" />
        <result column="deduction_money" property="deductionMoney" />
        <result column="remaining_money" property="remainingMoney" />
        <result column="pay_time" property="payTime" />
        <result column="payer" property="payer" />
        <result column="flow_status" property="flowStatus" />
        <result column="pay_type" property="payType" />
        <result column="payment_bill_id" property="paymentBillId" />
        <result column="create_time" property="createTime" />
        <result column="update_time" property="updateTime" />
        <result column="create_by" property="createBy" />
        <result column="update_by" property="updateBy" />
        <result column="disabled" property="disabled" />
    </resultMap>
    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        id, bank_serial_number, flow_money, deduction_money, remaining_money, pay_time, payer, flow_status, pay_type, payment_bill_id, create_time, update_time, create_by, update_by, disabled
    </sql>
</mapper>
ruoyi-system/src/main/resources/mapper/system/TBillConfirmMapper.xml
New file
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.system.mapper.TBillConfirmMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.ruoyi.system.model.TBillConfirm">
        <id column="id" property="id" />
        <result column="bill_id" property="billId" />
        <result column="pay_fees_money" property="payFeesMoney" />
        <result column="voucher" property="voucher" />
    </resultMap>
    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        id, bill_id, pay_fees_money, voucher
    </sql>
</mapper>
ruoyi-system/src/main/resources/mapper/system/TBillDetailMapper.xml
New file
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.system.mapper.TBillDetailMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.ruoyi.system.model.TBillDetail">
        <id column="id" property="id" />
        <result column="bill_id" property="billId" />
        <result column="start_reading" property="startReading" />
        <result column="end_reading" property="endReading" />
        <result column="usage_measure" property="usageMeasure" />
        <result column="unit_price" property="unitPrice" />
        <result column="total_amount" property="totalAmount" />
        <result column="live_type" property="liveType" />
        <result column="create_time" property="createTime" />
        <result column="update_time" property="updateTime" />
        <result column="create_by" property="createBy" />
        <result column="update_by" property="updateBy" />
        <result column="disabled" property="disabled" />
    </resultMap>
    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        id, bill_id, start_reading, end_reading, usage_measure, unit_price, total_amount,live_type, create_time, update_time, create_by, update_by, disabled
    </sql>
</mapper>
ruoyi-system/src/main/resources/mapper/system/TBillMapper.xml
@@ -33,4 +33,34 @@
        id, contract_id,contract_number, payable_fees_money, payable_fees_time, pay_fees_status, pay_fees_money, pay_fees_time, pay_fees_type, bill_type, over_days, payable_fees_penalty, start_time, end_time, bank_serial_number, outstanding_money, voucher, create_time, update_time, create_by, update_by, disabled
    </sql>
    <select id="page" resultType="com.ruoyi.system.dto.TBillDto">
        SELECT
            b.*,
            t.resident_name as residentName,
            t.phone,
            t.account,
            h.house_name as houseName
        FROM
            t_bill b
        LEFT JOIN t_contract c ON c.contract_number = b.contract_number
        LEFT JOIN t_house h ON h.id = c.house_id
        LEFT JOIN t_tenant t ON t.id = c.tenant_id
        <where>
            <if test="query.payFeesStatus != null">
                and b.pay_fees_status = #{query.payFeesStatus}
            </if>
            <if test="query.phone != null and query.phone !=''">
                and t.phone = #{query.phone}
            </if>
            <if test="query.residentName != null and query.residentName !=''">
                and t.resident_name like concat('%',#{query.residentName},'%')
            </if>
            <if test="query.contractNumber != null and query.contractNumber !=''">
                and b.contract_number = #{query.contractNumber}
            </if>
            <if test="query.userId != null and query.userId !=''">
                and t.id = #{query.userId}
            </if>
        </where>
    </select>
</mapper>
ruoyi-system/src/main/resources/mapper/system/TInvoiceToBillMapper.xml
New file
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.system.mapper.TInvoiceToBillMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.ruoyi.system.model.TInvoiceToBill">
        <id column="id" property="id" />
        <result column="invoice_id" property="invoiceId" />
        <result column="bill_id" property="billId" />
    </resultMap>
    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        id, invoice_id, bill_id
    </sql>
</mapper>
ruoyi-ui/vue.config.js
@@ -35,7 +35,7 @@
    proxy: {
      // detail: https://cli.vuejs.org/config/#devserver-proxy
      [process.env.VUE_APP_BASE_API]: {
        target: `http://localhost:9001`,
        target: `http://localhost:8081`,
        changeOrigin: true,
        pathRewrite: {
          ['^' + process.env.VUE_APP_BASE_API]: ''