无关风月
4 天以前 74f666605450658b86e1b5ca076500aa341b6f49
yml活动管理代码
34个文件已添加
16个文件已修改
2229 ■■■■■ 已修改文件
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/factory/AppUserClientFallbackFactory.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/feignClient/AppUserClient.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/IntegralPay.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/IntegralRecord.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/ServicePay.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/TIntegralRule.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysUser.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-auth/src/main/resources/bootstrap.yml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-gateway/src/main/resources/bootstrap.yml 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/bootstrap.yml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-system/src/main/resources/mapping/system/SysUserMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/pom.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/AppUserController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-account/src/main/resources/bootstrap.yml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/resources/bootstrap.yml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/pom.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/AgreementController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TIntegralController.java 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TServiceController.java 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/WXCallBackController.java 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/mapper/IntegralPayMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/mapper/IntegralRecordMapper.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/mapper/IntegralRuleMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/mapper/ServicePayMapper.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/query/IntegralListQuery.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/query/ServiceListQuery.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/TIntegralPayService.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/TIntegralRecordService.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/TIntegralRuleService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/TServicePayService.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/impl/IntegralPayServiceImpl.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/impl/IntegralRecordServiceImpl.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/impl/IntegralRuleServiceImpl.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/impl/ServicePayServiceImpl.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/MyQrCodeUtil.java 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/ObsUploadUtil.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/QRCodeUtil.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/UUIDUtil.java 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/httpClinet/HttpClientUtil.java 303 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/httpClinet/HttpResult.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/PaymentUtil.java 158 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/wx/WechatPayService.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/vo/IntegralListVO.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/vo/IntegralVO.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/vo/ServiceListVO.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/vo/ServiceVO.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/resources/bootstrap.yml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/resources/mapper/other/IntegralRecord.xml 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/resources/mapper/other/ServicePay.xml 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/test/com/ruoyi/other/RuoYiOtherApplicationTests.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/factory/AppUserClientFallbackFactory.java
@@ -28,7 +28,12 @@
                throw new RuntimeException("根据id获取用户失败");
            }
            @Override
            @Override
            public List<AppUser> getAppUserAll() {
                log.error("获取所有用户失败:{}", cause.getMessage());
                throw new RuntimeException("获取所有用户失败");            }
            @Override
            public R<Void> editAppUserById(AppUser appUser) {
                log.error("编辑用户信息失败:{}", cause.getMessage());
                return R.fail("编辑用户信息失败");
ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/feignClient/AppUserClient.java
@@ -23,6 +23,12 @@
     */
    @PostMapping("/app-user/getAppUserById")
    AppUser getAppUserById(@RequestParam("id") Long id);
    /**
     * 查询所有用户
     * @return
     */
    @PostMapping("/app-user/getAppUserAll")
    List<AppUser> getAppUserAll();
    /**
     * 根据id编辑用户
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/IntegralPay.java
New file
@@ -0,0 +1,68 @@
package com.ruoyi.other.api.domain;
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.fasterxml.jackson.annotation.JsonFormat;
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 luodangjia
 * @since 2024-11-20
 */
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("t_integral_pay")
@ApiModel(value="IntegralPay对象", description="积分充值记录")
public class IntegralPay implements Serializable {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value = "主键")
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    @ApiModelProperty(value = "系统用户id")
    @TableField("user_id")
    private Long userId;
    @ApiModelProperty(value = "积分数量")
    @TableField("integral_count")
    private Integer integralCount;
    @ApiModelProperty(value = "支付金额")
    @TableField("amount")
    private BigDecimal amount;
    @ApiModelProperty(value = "支付时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @TableField("pay_time")
    private LocalDateTime payTime;
    @ApiModelProperty(value = "订单号")
    @TableField("code")
    private String code;
    @ApiModelProperty(value = "流水号")
    @TableField("order_number")
    private String orderNumber;
    @ApiModelProperty(value = "支付状态 1待支付 2已支付")
    @TableField("pay_status")
    private Integer payStatus;
    @ApiModelProperty(value = "删除标识(0=否,1=是)")
    @TableField("del_flag")
    private Integer delFlag;
    @ApiModelProperty(value = "添加时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @TableField("create_time")
    private LocalDateTime createTime;
}
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/IntegralRecord.java
New file
@@ -0,0 +1,59 @@
package com.ruoyi.other.api.domain;
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.fasterxml.jackson.annotation.JsonFormat;
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 luodangjia
 * @since 2024-11-20
 */
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("t_integral_record")
@ApiModel(value="IntegralRecord对象", description="积分明细")
public class IntegralRecord implements Serializable {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value = "主键")
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    @ApiModelProperty(value = "站点id")
    @TableField("site_id")
    private Integer siteId;
    @ApiModelProperty(value = "用户id")
    @TableField("app_user_id")
    private Long appUserId;
    @ApiModelProperty(value = "支付记录id")
    @TableField("pay_id")
    private Integer payId;
    @ApiModelProperty(value = "积分类型 1充值积分 2充电订单扣除")
    @TableField("integral_type")
    private Integer integralType;
    @ApiModelProperty(value = "积分数量")
    @TableField("integral_count")
    private Integer integralCount;
    @ApiModelProperty(value = "删除标识(0=否,1=是)")
    @TableField("del_flag")
    private Integer delFlag;
    @ApiModelProperty(value = "添加时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @TableField("create_time")
    private LocalDateTime createTime;
}
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/ServicePay.java
New file
@@ -0,0 +1,75 @@
package com.ruoyi.other.api.domain;
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.fasterxml.jackson.annotation.JsonFormat;
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 luodangjia
 * @since 2024-11-20
 */
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("t_service_pay")
@ApiModel(value="ServicePay对象", description="服务费充值记录")
public class ServicePay implements Serializable {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value = "主键")
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    @ApiModelProperty(value = "系统用户id")
    @TableField("user_id")
    private Long userId;
    @ApiModelProperty(value = "支付金额")
    @TableField("amount")
    private BigDecimal amount;
    @ApiModelProperty(value = "支付时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @TableField("pay_time")
    private LocalDateTime payTime;
    @ApiModelProperty(value = "到期时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @TableField("end_time")
    private LocalDateTime endTime;
    @ApiModelProperty(value = "订单号")
    @TableField("code")
    private String code;
    @ApiModelProperty(value = "流水号")
    @TableField("order_number")
    private String orderNumber;
    @ApiModelProperty(value = "支付状态 1待支付 2已支付")
    @TableField("pay_status")
    private Integer payStatus;
    @ApiModelProperty(value = "支付方式 1微信 2支付宝 当前只有微信")
    @TableField("pay_type")
    private Integer payType;
    @ApiModelProperty(value = "删除标识(0=否,1=是)")
    @TableField("del_flag")
    private Integer delFlag;
    @ApiModelProperty(value = "添加时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @TableField("create_time")
    private LocalDateTime createTime;
}
ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/TIntegralRule.java
New file
@@ -0,0 +1,51 @@
package com.ruoyi.other.api.domain;
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 无关风月
 * @since 2024-08-06
 */
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("t_integral_rule")
@ApiModel(value="TIntegralRule对象", description="")
public class TIntegralRule implements Serializable {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value = "主键")
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    @ApiModelProperty(value = "充电得积分{\"num1\":1}")
    @TableField("charge_credit")
    private String chargeCredit;
    @ApiModelProperty(value = "好评得积分{\"num1\":1}")
    @TableField("credit")
    private String credit;
    @ApiModelProperty(value = "添加车辆得积分{\"num1\":1,\"num2\":1,\"num3\":1,\"num4\":1}")
    @TableField("add_vehicles_earns_points")
    private String addVehiclesEarnsPoints;
    @ApiModelProperty(value = "积分规则说明")
    @TableField("content")
    private String content;
    @ApiModelProperty(value = "站点id")
    @TableField("site_id")
    private Integer siteId;
}
ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysUser.java
@@ -10,6 +10,7 @@
import com.ruoyi.common.core.web.domain.BaseEntity;
import com.ruoyi.common.core.xss.Xss;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.springframework.format.annotation.DateTimeFormat;
@@ -20,6 +21,7 @@
import java.util.Date;
import java.util.List;
@Data
/**
 * 用户对象 sys_user
 * 
@@ -38,6 +40,12 @@
    @Excel(name = "部门编号", type = Type.IMPORT)
    @TableField("dept_id")
    private Long deptId;
    // 站点id
    @TableField("site_id")
    private Integer siteId;
    // 积分
    @TableField("integral")
    private Integer integral;
    /** 用户账号 */
    @Excel(name = "登录名称")
ruoyi-auth/src/main/resources/bootstrap.yml
@@ -6,8 +6,8 @@
spring:
  profiles:
    # 环境配置
#    active: dev
    active: prod
    active: dev
#    active: prod
  application:
    # 应用名称
    name: ruoyi-auth
ruoyi-gateway/src/main/resources/bootstrap.yml
@@ -1,5 +1,5 @@
server:
  port: 8080
  port: 8084
  tomcat:
    basedir: /mnt/tomcat
# Spring
@@ -12,8 +12,8 @@
    allow-circular-references: true
  profiles:
    # 环境配置
#    active: dev
    active: prod
    active: dev
#    active: prod
---
spring:
  config:
ruoyi-modules/ruoyi-system/src/main/resources/bootstrap.yml
@@ -11,8 +11,8 @@
    allow-bean-definition-overriding: true
  profiles:
    # 环境配置
#    active: dev
    active: prod
    active: dev
#    active: prod
---
spring:
  config:
ruoyi-modules/ruoyi-system/src/main/resources/mapping/system/SysUserMapper.xml
@@ -52,6 +52,7 @@
        select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.role_type, u.create_by, u.create_time, u.remark,
        d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status as dept_status,
        r.role_id, r.role_name, r.role_key, r.data_scope, r.status as role_status ,u.role_type as roleType,u.objectId AS objectId
        ,u.site_id as siteId
        from sys_user u
            left join sys_dept d on u.dept_id = d.dept_id
            left join sys_user_role ur on u.user_id = ur.user_id
@@ -217,6 +218,7 @@
             <if test="remark != null and remark != ''">remark,</if>
             <if test="objectId != null and objectId != ''">objectId,</if>
            <if test="appUserId != null and appUserId != ''">app_user_id,</if>
            <if test="siteId != null and siteId != ''">site_id,</if>
             create_time
         )values(
             <if test="userId != null and userId != ''">#{userId},</if>
@@ -234,6 +236,7 @@
             <if test="remark != null and remark != ''">#{remark},</if>
             <if test="objectId != null and objectId != ''">#{objectId},</if>
            <if test="appUserId != null and appUserId != ''">#{appUserId},</if>
            <if test="siteId != null and siteId != ''">#{siteId},</if>
             sysdate()
         )
    </insert>
@@ -257,6 +260,8 @@
            <if test="forbiddenRemark != null">forbidden_remark = #{forbiddenRemark},</if>
            <if test="roleType != null">role_type = #{roleType},</if>
            <if test="objectId != null">objectId = #{objectId},</if>
            <if test="siteId != null">site_id = #{siteId},</if>
            <if test="integral != null">integral = #{integral},</if>
             update_time = sysdate()
         </set>
         where user_id = #{userId}
ruoyi-service/pom.xml
@@ -22,6 +22,11 @@
    </description>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--lombok-->
        <dependency>
ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/AppUserController.java
@@ -143,6 +143,11 @@
        System.out.println("根据id获取用户:"+id);
        return appUserService.getById(id);
    }
    // 获取所有用户
    @PostMapping("/getAppUserAll")
    public List<AppUser> getAppUserAll() {
        return appUserService.list();
    }
    @PostMapping("/getBaseUserById")
    public R<Map<String, Object>> getBaseUserById(@RequestParam("id") Long id) {
ruoyi-service/ruoyi-account/src/main/resources/bootstrap.yml
@@ -12,8 +12,8 @@
    allow-circular-references: true
  profiles:
    # 环境配置
#    active: dev
    active: prod
    active: dev
#    active: prod
---
spring:
  config:
ruoyi-service/ruoyi-order/src/main/resources/bootstrap.yml
@@ -12,8 +12,8 @@
    allow-circular-references: true
  profiles:
    # 环境配置
#    active: dev
    active: prod
    active: dev
#    active: prod
---
spring:
  config:
ruoyi-service/ruoyi-other/pom.xml
@@ -159,6 +159,10 @@
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
        </dependency>
    </dependencies>
    <build>
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/AgreementController.java
@@ -33,7 +33,7 @@
    @GetMapping("/getAgreement/{type}")
    @ApiOperation(value = "获取协议", tags = {"小程序-登录注册"})
    @ApiImplicitParams({
            @ApiImplicitParam(value = "类型(1=用户协议,2=隐私协议,4=注销协议,5门店提现免责声明)", name = "type", required = true, dataType = "int"),
            @ApiImplicitParam(value = "类型(1=用户协议,2=隐私协议,4=注销协议,5门店提现免责声明,6=积分规则说明)", name = "type", required = true, dataType = "int"),
    })
    public R<String> getAgreement(@PathVariable("type") Integer type){
        Agreement one = agreementService.getOne(new LambdaQueryWrapper<Agreement>().eq(Agreement::getType, type));
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TIntegralController.java
New file
@@ -0,0 +1,191 @@
package com.ruoyi.other.controller;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.common.security.service.TokenService;
import com.ruoyi.other.api.domain.IntegralPay;
import com.ruoyi.other.api.domain.TIntegralRule;
import com.ruoyi.other.query.IntegralListQuery;
import com.ruoyi.other.service.TIntegralPayService;
import com.ruoyi.other.service.TIntegralRecordService;
import com.ruoyi.other.service.TIntegralRuleService;
import com.ruoyi.other.util.MyQrCodeUtil;
import com.ruoyi.other.util.ObsUploadUtil;
import com.ruoyi.other.util.QRCodeUtil;
import com.ruoyi.other.util.UUIDUtil;
import com.ruoyi.other.util.payment.wx.WechatPayService;
import com.ruoyi.other.vo.IntegralListVO;
import com.ruoyi.other.vo.IntegralVO;
import com.ruoyi.system.api.domain.SysUser;
import com.ruoyi.system.api.feignClient.SysUserClient;
import io.swagger.annotations.ApiOperation;
import org.apache.tomcat.util.http.fileupload.ByteArrayOutputStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Map;
/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author 无关风月
 * @since 2024-08-06
 */
@RestController
@RequestMapping("/integral")
public class TIntegralController {
    @Autowired
    private TIntegralRuleService integralRuleService;
    @Autowired
    private TIntegralRecordService integralRecordService;
    @Autowired
    private TIntegralPayService integralPayService;
    @Autowired
    private TokenService tokenService;
    @Resource
    private SysUserClient sysUserClient;
    @Resource
    private WechatPayService wechatPayService;
    @PostMapping("/integralPageList")
    @ApiOperation(tags = {"2.0-积分钱包"},value = "积分钱包")
    public R<IntegralVO> integralPageList(@RequestBody IntegralListQuery query) {
        Long userid = tokenService.getLoginUser().getUserid();
        R<SysUser> sysUser = sysUserClient.getSysUser(userid);
        query.setSiteId(sysUser.getData().getSiteId());
        Integer integral = sysUser.getData().getIntegral();
        IntegralVO integralVO = new IntegralVO();
        PageInfo<IntegralListVO> integralListVOPageInfo = integralRecordService.integralPageList(query);
        integralVO.setIntegralList(integralListVOPageInfo);
        integralVO.setIntegral(integral);
        return R.ok(integralVO);
    }
    @PostMapping("/queryPayStatus")
    @ApiOperation(tags = {"2.0-积分钱包"},value = "查询支付结果 true成功 false失败")
    public R<Boolean> integralPageList(@RequestParam("code") String code) throws Exception {
        Map<String, String> resMap = wechatPayService.queryOrder(code);
        if("SUCCESS".equals(resMap.get("return_code"))){
            return R.ok(true);
        }else{
            return R.ok(false);
        }
    }
    @PostMapping("/nativePay")
    @ApiOperation(tags = {"2.0-积分钱包"},value = "获取支付二维码")
    public R nativePay(@RequestParam("amount") String amount) throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        String code = sdf.format(new Date()) + UUIDUtil.getNumberRandom(5);
        Map<String, String> res =  wechatPayService.unifiedOrder(code, amount, "积分充值", "/other/wx/integralCallback");
        Long userid = tokenService.getLoginUser().getUserid();
        R<SysUser> sysUser = sysUserClient.getSysUser(userid);
        SysUser data = sysUser.getData();
        // 生成待支付记录
        IntegralPay integralPay = new IntegralPay();
        integralPay.setUserId(data.getUserId());
        TIntegralRule one = integralRuleService.lambdaQuery().eq(TIntegralRule::getSiteId, 0).one();
        if (one==null){
            return R.fail("获取支付失败,平台未设置充值积分比例");
        }
        String chargeCredit = one.getChargeCredit();
        JSONObject jsonObject = JSONObject.parseObject(chargeCredit);
        Integer num1 = jsonObject.getInteger("num1");
        BigDecimal bigDecimal = new BigDecimal(amount);
        // 向上取整 比如5.1 当作6 5.9也当作6
        integralPay.setIntegralCount(bigDecimal.divide(new BigDecimal(num1), RoundingMode.UP).intValue());
        int i = bigDecimal.multiply(new BigDecimal(num1)).intValue();
        integralPay.setIntegralCount(i);
        integralPay.setAmount(new BigDecimal(amount));
        integralPay.setCode(code);
        integralPay.setPayStatus(1);
        integralPay.setDelFlag(0);
        integralPay.setCreateTime(LocalDateTime.now());
        integralPayService.save(integralPay);
        String codeUrl = res.get("code_url");
        MyQrCodeUtil.createCodeToFile(codeUrl);
        BufferedImage blueImage = QRCodeUtil.createImage(codeUrl);
        MultipartFile blueFile = convert(blueImage, new Date().getTime() + UUIDUtil.getRandomCode(3) + ".PNG");
        String s = ObsUploadUtil.obsUpload(blueFile);
        System.err.println(s);
        return R.ok(s);
    }
    public static MultipartFile convert(BufferedImage bufferedImage, String fileName) throws IOException {
        // 将 BufferedImage 转换为字节数组
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(bufferedImage, "png", baos);
        byte[] bytes = baos.toByteArray();
        // 创建 ByteArrayResource
        ByteArrayResource resource = new ByteArrayResource(bytes);
        // 创建 MockMultipartFile
        MockMultipartFile multipartFile = new MockMultipartFile(
                "file",
                fileName,
                "image/png",
                resource.getInputStream()
        );
        return multipartFile;
    }
    @GetMapping("/getSet")
    @ApiOperation(tags = {"2.0-积分管理"},value = "获取积分设置")
    public R<TIntegralRule> getSet() {
        Long userid = tokenService.getLoginUser().getUserid();
        SysUser data = sysUserClient.getSysUser(userid).getData();
        if (data!=null&&data.getSiteId()!=null){
            if (data.getRoleType()==1){
                // 平台
                TIntegralRule res = integralRuleService.lambdaQuery().eq(TIntegralRule::getSiteId, 0).one();
                return R.ok(res);
            }else{
                TIntegralRule res = integralRuleService.lambdaQuery().eq(TIntegralRule::getSiteId, data.getSiteId()).one();
                return R.ok(res);
            }
        }else {
            return R.ok(new TIntegralRule());
        }
    }
    @PostMapping("/saveSet")
    @ApiOperation(tags = {"2.0-积分管理"},value = "保存积分设置")
    @Log(title = "【积分管理】保存积分设置", businessType = BusinessType.INSERT)
    public R saveSet(@RequestBody TIntegralRule dto) {
        Long userid = tokenService.getLoginUser().getUserid();
        SysUser data = sysUserClient.getSysUser(userid).getData();
        TIntegralRule one;
        if (data.getRoleType()==1){
            one = integralRuleService.lambdaQuery().eq(TIntegralRule::getSiteId, 0).one();
        }else{
            one = integralRuleService.lambdaQuery().eq(TIntegralRule::getSiteId, data.getSiteId()).one();
        }
        if (one!=null){
            dto.setId(one.getId());
            integralRuleService.saveOrUpdate(dto);
        }else{
            integralRuleService.saveOrUpdate(dto);
        }
        return R.ok();
    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TServiceController.java
New file
@@ -0,0 +1,153 @@
package com.ruoyi.other.controller;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.common.security.service.TokenService;
import com.ruoyi.other.api.domain.ServicePay;
import com.ruoyi.other.api.domain.TIntegralRule;
import com.ruoyi.other.query.IntegralListQuery;
import com.ruoyi.other.query.ServiceListQuery;
import com.ruoyi.other.service.TIntegralRecordService;
import com.ruoyi.other.service.TIntegralRuleService;
import com.ruoyi.other.service.TServicePayService;
import com.ruoyi.other.util.MyQrCodeUtil;
import com.ruoyi.other.util.ObsUploadUtil;
import com.ruoyi.other.util.QRCodeUtil;
import com.ruoyi.other.util.UUIDUtil;
import com.ruoyi.other.util.payment.wx.WechatPayService;
import com.ruoyi.other.vo.IntegralListVO;
import com.ruoyi.other.vo.IntegralVO;
import com.ruoyi.other.vo.ServiceListVO;
import com.ruoyi.other.vo.ServiceVO;
import com.ruoyi.system.api.domain.SysUser;
import com.ruoyi.system.api.feignClient.SysUserClient;
import io.swagger.annotations.ApiOperation;
import org.apache.tomcat.util.http.fileupload.ByteArrayOutputStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Map;
/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author 无关风月
 * @since 2024-08-06
 */
@RestController
@RequestMapping("/integralRule")
public class TServiceController {
    @Autowired
    private TIntegralRuleService integralRuleService;
    @Autowired
    private TIntegralRecordService integralRecordService;
    @Autowired
    private TokenService tokenService;
    @Resource
    private SysUserClient sysUserClient;
    @Resource
    private WechatPayService wechatPayService;
    @Resource
    private TServicePayService servicePayService;
    @PostMapping("/servicePageList")
    @ApiOperation(tags = {"2.0-服务费管理"},value = "服务费管理")
    public R<ServiceVO> integralPageList(@RequestBody ServiceListQuery query) {
        Long userid = tokenService.getLoginUser().getUserid();
        R<SysUser> sysUser = sysUserClient.getSysUser(userid);
        query.setUserId(userid);
        ServiceVO serviceVO = new ServiceVO();
        PageInfo<ServiceListVO> serviceListVOPageInfo = servicePayService.servicePageList(query);
        serviceVO.setServiceList(serviceListVOPageInfo);
        ServicePay servicePayBefore = servicePayService.lambdaQuery()
                .eq(ServicePay::getUserId, userid)
                .eq(ServicePay::getPayStatus,2)
                .orderByDesc(ServicePay::getCreateTime)
                .last("limit 1")
                .one();
        if (servicePayBefore==null){
            serviceVO.setStatus(1);
        } else if (servicePayBefore.getEndTime().isBefore(LocalDateTime.now())) {
            serviceVO.setStatus(2);
        }else{
            serviceVO.setStatus(3);
        }
        if (servicePayBefore!=null){
            serviceVO.setEndTime(servicePayBefore.getEndTime());
        }
        return R.ok(serviceVO);
    }
    @PostMapping("/queryPayStatus")
    @ApiOperation(tags = {"2.0-服务费"},value = "查询支付结果 true成功 false失败")
    public R<Boolean> integralPageList(@RequestParam("code") String code) throws Exception {
        Map<String, String> resMap = wechatPayService.queryOrder(code);
        if("SUCCESS".equals(resMap.get("return_code"))){
            return R.ok(true);
        }else{
            return R.ok(false);
        }
    }
    @PostMapping("/nativePay")
    @ApiOperation(tags = {"2.0-服务费"},value = "获取支付二维码")
    public R nativePay(@RequestParam("amount") String amount) throws Exception {
        Long userid = tokenService.getLoginUser().getUserid();
        R<SysUser> sysUser = sysUserClient.getSysUser(userid);
        SysUser data = sysUser.getData();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        String code = sdf.format(new Date()) + UUIDUtil.getNumberRandom(5);
        Map<String,String> res = wechatPayService.unifiedOrder(code, amount, "服务费缴纳", "/other/wx/serviceCallback");
        ServicePay servicePay = new ServicePay();
        servicePay.setUserId(data.getUserId());
        servicePay.setAmount(new BigDecimal( amount));
        servicePay.setCode(code);
        servicePay.setPayStatus(1);
        servicePay.setPayType(1);
        servicePay.setDelFlag(0);
        servicePay.setCreateTime(LocalDateTime.now());
        servicePayService.save(servicePay);
        String codeUrl = res.get("code_url");
        MyQrCodeUtil.createCodeToFile(codeUrl);
        BufferedImage blueImage = QRCodeUtil.createImage(codeUrl);
        MultipartFile blueFile = convert(blueImage, new Date().getTime() + UUIDUtil.getRandomCode(3) + ".PNG");
        String s = ObsUploadUtil.obsUpload(blueFile);
        System.err.println(s);
        return R.ok(s);
    }
    public static MultipartFile convert(BufferedImage bufferedImage, String fileName) throws IOException {
        // 将 BufferedImage 转换为字节数组
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(bufferedImage, "png", baos);
        byte[] bytes = baos.toByteArray();
        // 创建 ByteArrayResource
        ByteArrayResource resource = new ByteArrayResource(bytes);
        // 创建 MockMultipartFile
        MockMultipartFile multipartFile = new MockMultipartFile(
                "file",
                fileName,
                "image/png",
                resource.getInputStream()
        );
        return multipartFile;
    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/WXCallBackController.java
New file
@@ -0,0 +1,148 @@
package com.ruoyi.other.controller;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.common.security.service.TokenService;
import com.ruoyi.other.api.domain.IntegralPay;
import com.ruoyi.other.api.domain.IntegralRecord;
import com.ruoyi.other.api.domain.ServicePay;
import com.ruoyi.other.query.ServiceListQuery;
import com.ruoyi.other.service.TIntegralPayService;
import com.ruoyi.other.service.TIntegralRecordService;
import com.ruoyi.other.service.TIntegralRuleService;
import com.ruoyi.other.service.TServicePayService;
import com.ruoyi.other.util.UUIDUtil;
import com.ruoyi.other.util.payment.wx.PayResult;
import com.ruoyi.other.util.payment.wx.WechatPayService;
import com.ruoyi.other.vo.ServiceListVO;
import com.ruoyi.other.vo.ServiceVO;
import com.ruoyi.system.api.domain.SysUser;
import com.ruoyi.system.api.feignClient.SysUserClient;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Map;
/**
 * <p>
 *  微信回调
 * </p>
 *
 * @author 无关风月
 * @since 2024-08-06
 */
@RestController
@RequestMapping("/wx")
public class WXCallBackController {
    @Autowired
    private TIntegralRuleService integralRuleService;
    @Autowired
    private TIntegralRecordService integralRecordService;
    @Autowired
    private TIntegralPayService integralPayService;
    @Autowired
    private TokenService tokenService;
    @Resource
    private SysUserClient sysUserClient;
    @Resource
    private WechatPayService wechatPayService;
    @Resource
    private TServicePayService servicePayService;
    @ResponseBody
    @PostMapping("/integralCallback")
    public void integralCallback(HttpServletRequest request, HttpServletResponse response){
        System.err.println("积分充值回调");
        PayResult payResult= null;
        try {
            payResult = wechatPayService.processNotify(request);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        IntegralPay integralPay = integralPayService.lambdaQuery().eq(IntegralPay::getCode, payResult.getOrderNumber()).one();
        if (integralPay != null && integralPay.getPayStatus() == 1){
            SysUser data = sysUserClient.getSysUser(integralPay.getUserId()).getData();
            integralPay.setPayStatus(2);
            integralPay.setPayTime(LocalDateTime.now());
            integralPay.setOrderNumber(payResult.getTransactionId());
            integralPayService.updateById(integralPay);
            IntegralRecord integralRecord = new IntegralRecord();
            integralRecord.setPayId(integralPay.getId());
            integralRecord.setSiteId(data.getSiteId());
            integralRecord.setAppUserId(integralPay.getUserId());
            integralRecord.setIntegralType(1);
            integralRecord.setIntegralCount(integralPay.getIntegralCount());
            integralRecord.setDelFlag(0);
            integralRecord.setCreateTime(LocalDateTime.now());
            integralRecordService.save(integralRecord);
            data.setIntegral(data.getIntegral()+integralPay.getIntegralCount());
            sysUserClient.updateSysUser(data);
        }
            response.setStatus(200);
            PrintWriter out = null;
            try {
                out = response.getWriter();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            out.println("success");
            out.flush();
            out.close();
    }
    @ResponseBody
    @PostMapping("/serviceCallback")
    public void serviceCallback(HttpServletRequest request, HttpServletResponse response){
        System.err.println("服务费缴纳回调");
        PayResult payResult= null;
        try {
            payResult = wechatPayService.processNotify(request);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        ServicePay servicePay = servicePayService.lambdaQuery().eq(ServicePay::getCode, payResult.getOrderNumber()).one();
        if (servicePay != null && servicePay.getPayStatus() == 1){
            servicePay.setPayStatus(2);
            ServicePay servicePayBefore = servicePayService.lambdaQuery()
                    .eq(ServicePay::getUserId, servicePay.getUserId())
                    .eq(ServicePay::getPayStatus,2)
                    .orderByDesc(ServicePay::getCreateTime)
                    .last("limit 1")
                    .one();
            if (servicePayBefore!=null){
                servicePay.setEndTime(servicePayBefore.getEndTime().plusDays(365));
            }else{
                servicePay.setEndTime(LocalDateTime.now().plusDays(365));
            }
            servicePay.setPayType(1);
            servicePay.setPayTime(LocalDateTime.now());
            servicePay.setOrderNumber(payResult.getTransactionId());
            servicePayService.updateById(servicePay);
        }
            response.setStatus(200);
            PrintWriter out = null;
            try {
                out = response.getWriter();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            out.println("success");
            out.flush();
            out.close();
    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/mapper/IntegralPayMapper.java
New file
@@ -0,0 +1,18 @@
package com.ruoyi.other.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.other.api.domain.IntegralPay;
import com.ruoyi.other.api.domain.IntegralRecord;
import com.ruoyi.other.query.IntegralListQuery;
import com.ruoyi.other.vo.IntegralListVO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * @author zhibing.pu
 * @Date 2024/11/21 10:46
 */
public interface IntegralPayMapper extends BaseMapper<IntegralPay> {
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/mapper/IntegralRecordMapper.java
New file
@@ -0,0 +1,20 @@
package com.ruoyi.other.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.other.api.domain.IntegralRecord;
import com.ruoyi.other.api.domain.TIntegralRule;
import com.ruoyi.other.query.IntegralListQuery;
import com.ruoyi.other.vo.IntegralListVO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * @author zhibing.pu
 * @Date 2024/11/21 10:46
 */
public interface IntegralRecordMapper extends BaseMapper<IntegralRecord> {
    List<IntegralListVO> integralPageList(@Param("query") IntegralListQuery query,
                                          @Param("pageInfo") PageInfo<IntegralListVO> pageInfo);
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/mapper/IntegralRuleMapper.java
New file
@@ -0,0 +1,12 @@
package com.ruoyi.other.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.other.api.domain.Agreement;
import com.ruoyi.other.api.domain.TIntegralRule;
/**
 * @author zhibing.pu
 * @Date 2024/11/21 10:46
 */
public interface IntegralRuleMapper extends BaseMapper<TIntegralRule> {
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/mapper/ServicePayMapper.java
New file
@@ -0,0 +1,20 @@
package com.ruoyi.other.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.other.api.domain.IntegralPay;
import com.ruoyi.other.api.domain.ServicePay;
import com.ruoyi.other.query.ServiceListQuery;
import com.ruoyi.other.vo.ServiceListVO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * @author zhibing.pu
 * @Date 2024/11/21 10:46
 */
public interface ServicePayMapper extends BaseMapper<ServicePay> {
    List<ServiceListVO> servicePageList(@Param("query") ServiceListQuery query, @Param("pageInfo") PageInfo<ServiceListVO> pageInfo);
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/query/IntegralListQuery.java
New file
@@ -0,0 +1,15 @@
package com.ruoyi.other.query;
import com.ruoyi.common.core.web.page.BasePage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(value = "积分明细查询query")
public class IntegralListQuery extends BasePage {
    @ApiModelProperty("类型 1充值 2扣除 全部不传")
    private Integer type;
    @ApiModelProperty("站点 前端忽略")
    private Integer siteId;
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/query/ServiceListQuery.java
New file
@@ -0,0 +1,14 @@
package com.ruoyi.other.query;
import com.ruoyi.common.core.web.page.BasePage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(value = "服务费缴纳明细查询query")
public class ServiceListQuery extends BasePage {
    @ApiModelProperty("用户id 前端忽略")
    private Long userId;
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/TIntegralPayService.java
New file
@@ -0,0 +1,21 @@
package com.ruoyi.other.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.other.api.domain.IntegralPay;
import com.ruoyi.other.api.domain.IntegralRecord;
import com.ruoyi.other.query.IntegralListQuery;
import com.ruoyi.other.vo.IntegralListVO;
/**
 * <p>
 *  服务类
 * </p>
 *
 * @author luodangjia
 * @since 2024-11-20
 */
public interface TIntegralPayService extends IService<IntegralPay> {
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/TIntegralRecordService.java
New file
@@ -0,0 +1,22 @@
package com.ruoyi.other.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.other.api.domain.IntegralRecord;
import com.ruoyi.other.api.domain.TIntegralRule;
import com.ruoyi.other.query.IntegralListQuery;
import com.ruoyi.other.vo.IntegralListVO;
/**
 * <p>
 *  服务类
 * </p>
 *
 * @author luodangjia
 * @since 2024-11-20
 */
public interface TIntegralRecordService extends IService<IntegralRecord> {
    PageInfo<IntegralListVO> integralPageList(IntegralListQuery query);
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/TIntegralRuleService.java
New file
@@ -0,0 +1,16 @@
package com.ruoyi.other.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.other.api.domain.TIntegralRule;
/**
 * <p>
 *  服务类
 * </p>
 *
 * @author luodangjia
 * @since 2024-11-20
 */
public interface TIntegralRuleService extends IService<TIntegralRule> {
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/TServicePayService.java
New file
@@ -0,0 +1,24 @@
package com.ruoyi.other.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.other.api.domain.IntegralRecord;
import com.ruoyi.other.api.domain.ServicePay;
import com.ruoyi.other.query.IntegralListQuery;
import com.ruoyi.other.query.ServiceListQuery;
import com.ruoyi.other.vo.IntegralListVO;
import com.ruoyi.other.vo.ServiceListVO;
/**
 * <p>
 *  服务类
 * </p>
 *
 * @author luodangjia
 * @since 2024-11-20
 */
public interface TServicePayService extends IService<ServicePay> {
    PageInfo<ServiceListVO> servicePageList(ServiceListQuery query);
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/impl/IntegralPayServiceImpl.java
New file
@@ -0,0 +1,27 @@
package com.ruoyi.other.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.account.api.feignClient.AppUserClient;
import com.ruoyi.account.api.model.AppUser;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.other.api.domain.IntegralPay;
import com.ruoyi.other.api.domain.IntegralRecord;
import com.ruoyi.other.mapper.IntegralPayMapper;
import com.ruoyi.other.mapper.IntegralRecordMapper;
import com.ruoyi.other.query.IntegralListQuery;
import com.ruoyi.other.service.TIntegralPayService;
import com.ruoyi.other.service.TIntegralRecordService;
import com.ruoyi.other.vo.IntegralListVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
 * @author zhibing.pu
 * @Date 2024/11/21 10:48
 */
@Service
public class IntegralPayServiceImpl extends ServiceImpl<IntegralPayMapper, IntegralPay> implements TIntegralPayService {
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/impl/IntegralRecordServiceImpl.java
New file
@@ -0,0 +1,48 @@
package com.ruoyi.other.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.account.api.feignClient.AppUserClient;
import com.ruoyi.account.api.model.AppUser;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.other.api.domain.IntegralRecord;
import com.ruoyi.other.api.domain.TIntegralRule;
import com.ruoyi.other.mapper.IntegralRecordMapper;
import com.ruoyi.other.mapper.IntegralRuleMapper;
import com.ruoyi.other.query.IntegralListQuery;
import com.ruoyi.other.service.TIntegralRecordService;
import com.ruoyi.other.service.TIntegralRuleService;
import com.ruoyi.other.vo.IntegralListVO;
import com.ruoyi.system.api.domain.SysUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
 * @author zhibing.pu
 * @Date 2024/11/21 10:48
 */
@Service
public class IntegralRecordServiceImpl extends ServiceImpl<IntegralRecordMapper, IntegralRecord> implements TIntegralRecordService {
    @Autowired
    private AppUserClient appUserClient;
    @Override
    public PageInfo<IntegralListVO> integralPageList(IntegralListQuery query) {
        PageInfo<IntegralListVO> pageInfo = new PageInfo<>(query.getPageCurr(), query.getPageSize());
        List<IntegralListVO> list = this.baseMapper.integralPageList(query,pageInfo);
        List<AppUser> appUserAll = appUserClient.getAppUserAll();
        for (IntegralListVO integralListVO : list) {
            if (integralListVO.getAppUserId()!=null){
                AppUser appUser = appUserAll.stream().filter(e -> e.getId().equals(integralListVO.getAppUserId()))
                        .findFirst().orElse(null);
                if (appUser!=null){
                    integralListVO.setAppUserName(appUser.getName());
                    integralListVO.setPhone(appUser.getPhone());
                }
            }
        }
        pageInfo.setRecords(list);
        return pageInfo;
    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/impl/IntegralRuleServiceImpl.java
New file
@@ -0,0 +1,18 @@
package com.ruoyi.other.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.other.api.domain.Agreement;
import com.ruoyi.other.api.domain.TIntegralRule;
import com.ruoyi.other.mapper.AgreementMapper;
import com.ruoyi.other.mapper.IntegralRuleMapper;
import com.ruoyi.other.service.IAgreementService;
import com.ruoyi.other.service.TIntegralRuleService;
import org.springframework.stereotype.Service;
/**
 * @author zhibing.pu
 * @Date 2024/11/21 10:48
 */
@Service
public class IntegralRuleServiceImpl extends ServiceImpl<IntegralRuleMapper, TIntegralRule> implements TIntegralRuleService {
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/impl/ServicePayServiceImpl.java
New file
@@ -0,0 +1,33 @@
package com.ruoyi.other.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.account.api.model.AppUser;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.other.api.domain.IntegralPay;
import com.ruoyi.other.api.domain.ServicePay;
import com.ruoyi.other.mapper.IntegralPayMapper;
import com.ruoyi.other.mapper.ServicePayMapper;
import com.ruoyi.other.query.ServiceListQuery;
import com.ruoyi.other.service.TIntegralPayService;
import com.ruoyi.other.service.TServicePayService;
import com.ruoyi.other.vo.IntegralListVO;
import com.ruoyi.other.vo.ServiceListVO;
import org.springframework.stereotype.Service;
import java.util.List;
/**
 * @author zhibing.pu
 * @Date 2024/11/21 10:48
 */
@Service
public class ServicePayServiceImpl extends ServiceImpl<ServicePayMapper, ServicePay> implements TServicePayService {
    @Override
    public PageInfo<ServiceListVO> servicePageList(ServiceListQuery query) {
        PageInfo<ServiceListVO> pageInfo = new PageInfo<>(query.getPageCurr(), query.getPageSize());
        List<ServiceListVO> list = this.baseMapper.servicePageList(query,pageInfo);
        pageInfo.setRecords(list);
        return pageInfo;
    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/MyQrCodeUtil.java
New file
@@ -0,0 +1,93 @@
package com.ruoyi.other.util;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
public class MyQrCodeUtil {
    //CODE_WIDTH:二维码宽度,单位像素
    private static final int CODE_WIDTH = 400;
    //CODE_HEIGHT:二维码高度,单位像素
    private static final int CODE_HEIGHT = 400;
    //FRONT_COLOR:二维码前景色,0x000000 表示黑色
    private static final int FRONT_COLOR = 0x000000;
    //BACKGROUND_COLOR:二维码背景色,0xFFFFFF 表示白色
    //演示用 16 进制表示,和前端页面 CSS 的取色是一样的,注意前后景颜色应该对比明显,如常见的黑白
    private static final int BACKGROUND_COLOR = 0xFFFFFF;
    public static BufferedImage createCodeToFile(String content) {
        try {
            content = content.trim();
            //核心代码-生成二维码
            BufferedImage bufferedImage = getBufferedImage(content);
            return bufferedImage;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 生成二维码并输出到输出流, 通常用于输出到网页上进行显示,输出到网页与输出到磁盘上的文件中,区别在于最后一句 ImageIO.write
     * write(RenderedImage im,String formatName,File output):写到文件中
     * write(RenderedImage im,String formatName,OutputStream output):输出到输出流中
     * @param content  :二维码内容
     * @param outputStream :输出流,比如 HttpServletResponse 的 getOutputStream
     */
    public static void createCodeToOutputStream(String content, OutputStream outputStream) {
        try {
            if (StringUtils.isBlank(content)) {
                return;
            }
            content = content.trim();
            //核心代码-生成二维码
            BufferedImage bufferedImage = getBufferedImage(content);
            //区别就是这一句,输出到输出流中,如果第三个参数是 File,则输出到文件中
            ImageIO.write(bufferedImage, "png", outputStream);
            System.out.println("二维码图片生成到输出流成功...");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //核心代码-生成二维码
    private static BufferedImage getBufferedImage(String content) throws WriterException {
        //com.google.zxing.EncodeHintType:编码提示类型,枚举类型
        Map<EncodeHintType, Object> hints = new HashMap();
        //EncodeHintType.CHARACTER_SET:设置字符编码类型
        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
        //EncodeHintType.ERROR_CORRECTION:设置误差校正
        //ErrorCorrectionLevel:误差校正等级,L = ~7% correction、M = ~15% correction、Q = ~25% correction、H = ~30% correction
        //不设置时,默认为 L 等级,等级不一样,生成的图案不同,但扫描的结果是一样的
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
        //EncodeHintType.MARGIN:设置二维码边距,单位像素,值越小,二维码距离四周越近
        hints.put(EncodeHintType.MARGIN, 1);
        MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
        BitMatrix bitMatrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, CODE_WIDTH, CODE_HEIGHT, hints);
        BufferedImage bufferedImage = new BufferedImage(CODE_WIDTH, CODE_HEIGHT, BufferedImage.TYPE_INT_BGR);
        for (int x = 0; x < CODE_WIDTH; x++) {
            for (int y = 0; y < CODE_HEIGHT; y++) {
                bufferedImage.setRGB(x, y, bitMatrix.get(x, y) ? FRONT_COLOR : BACKGROUND_COLOR);
            }
        }
        return bufferedImage;
    }
//    public static void main(String[] args) {
//        String param = "{\n" +
//                "    \"scan_type\": 1,\n" +
//                "    \"space_id\": 2,\n" +
//                "    \"sutu_id\": 3\n" +
//                "}";
//        createCodeToFile(param);
//    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/ObsUploadUtil.java
New file
@@ -0,0 +1,91 @@
package com.ruoyi.other.util;
import com.obs.services.ObsClient;
import com.obs.services.model.ObjectMetadata;
import com.obs.services.model.PutObjectResult;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;
public class ObsUploadUtil {
    //OBS图片访问域名
    public static String endPoint = "obs.cn-southwest-2.myhuaweicloud.com";
    public static String accessKeyId = "F95RSIZQ1B77MGRTPWSU";
    public static String accessKeySecret = "rMhmXnuDGPwI5ptq0jGCZ6QwaoUDQ5ZJm3RFrZuy";
    public static String bucketName = "qijishenghuiyuan";
    public static String oss_domain = "http://qijishenghuiyuan.obs.cn-southwest-2.myhuaweicloud.com/";
    // 创建ObsClient实例
    public static ObsClient obsClient = new ObsClient(accessKeyId, accessKeySecret, endPoint);
    public static String obsUpload(MultipartFile file) throws IOException{
        String fileName = "";
        if(file!=null && !"".equals(file.getOriginalFilename()) && file.getOriginalFilename()!=null){
            InputStream content = file.getInputStream();//获得指定文件的输入流
            ObjectMetadata meta = new ObjectMetadata();// 创建上传Object的Metadata
            meta.setContentLength(file.getSize());  // 必须设置ContentLength
            String originalFilename = file.getOriginalFilename();
            fileName =  UUID.randomUUID().toString().replaceAll("-","") + originalFilename.subSequence(originalFilename.lastIndexOf("."), originalFilename.length());
            obsClient.putObject(bucketName,"admin/"+fileName,content,meta);
            if(fileName != null && !"".equals(fileName)){
                System.out.println(fileName);
                fileName = oss_domain+"admin/"+fileName;
            }
        }
        return fileName;
    }
    public static String obsUpload(String suffix, InputStream content){
        String fileName =  UUID.randomUUID().toString().replaceAll("-","") + "." + suffix;
        try {
            PutObjectResult putObjectResult = obsClient.putObject(bucketName, "admin/" + fileName, content);
            String objectUrl = putObjectResult.getObjectUrl();
            return oss_domain+"admin/"+fileName;
        }catch (Exception e){
            e.printStackTrace();
        }
        return "";
    }
    /**
     * 删除某个Object
     *
     * @param bucketUrl
     * @return
     */
    public static boolean deleteObject(String bucketUrl) {
        try {
            bucketUrl=bucketUrl.replace(oss_domain+"web","");
            // 删除Object.
            obsClient.deleteObject(bucketName, bucketUrl);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            //ossClient.shutdown();
        }
        return true;
    }
//     public static void createBucket(String bucketName)
//     {
//         //初始化 OSSClient
////          ossClient = new OssClient(endPoint, accessKeyId, accessKeySecret);
//
//         // 新建一个Bucket
//         Bucket bucket = ossClient.createBucket(bucketName);
//         System.out.println(bucket.getName());
//         System.out.println(bucket.getCreationDate());
//     }
//
//     public static void main(String[] args) {
//         OssUploadUtil.createBucket("ssfdfsd");
//    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/QRCodeUtil.java
New file
@@ -0,0 +1,50 @@
package com.ruoyi.other.util;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import java.awt.image.BufferedImage;
import java.util.Hashtable;
/**
 * @author chengst
 * @date 2019/11/4
 **/
public class QRCodeUtil {
    private final static String CHARSET = "utf-8";
    private final static int QRSIZEE = 300;
    // 二维码颜色
    private static final int BLACK = 0xFF000000;
    // 二维码颜色
    private static final int WHITE = 0xFFFFFFFF;
    public static BufferedImage createImage(String content){
        Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
        hints.put(EncodeHintType.MARGIN, 1);
        BitMatrix bitMatrix = null;
        try {
            bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRSIZEE, QRSIZEE,hints);
        }catch (Exception e){
            e.printStackTrace();
        }
        int width = bitMatrix.getWidth();
        int height = bitMatrix.getHeight();
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                image.setRGB(x, y, bitMatrix.get(x, y) ? BLACK : WHITE);
            }
        }
        return image;
    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/UUIDUtil.java
New file
@@ -0,0 +1,103 @@
package com.ruoyi.other.util;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
/**
 * 定义生成随机码的工具类
 */
public class UUIDUtil {
    private int i = 1;
    /**
     * 定义生成原生的UUID随机码
     *
     * @return
     */
    public static String getNativeUUID() {
        return UUID.randomUUID().toString();
    }
    /**
     * 生成32位随机码
     *
     * @return
     */
    public static String getRandomCode() {
        return UUIDUtil.getNativeUUID().replaceAll("-", "");
    }
    /**
     * 获取给定长度的随机码
     *
     * @param num
     * @return
     * @throws Exception
     */
    public static String getRandomCode(Integer num) throws Exception {
        String str = null;
        if (0 < num) {
            if (num % 32 > 0) {
                Integer s = num / 32;
                Integer l = num % 32;
                StringBuffer sb = new StringBuffer();
                for (int i = 0; i < s; i++) {
                    sb.append(UUIDUtil.getRandomCode());
                }
                sb.append(UUIDUtil.getRandomCode().substring(0, l));
                str = sb.toString();
            } else if (num % 32 == 0) {
                Integer s = num / 32;
                StringBuffer sb = new StringBuffer();
                for (int i = 0; i < s; i++) {
                    sb.append(UUIDUtil.getRandomCode());
                }
                str = sb.toString();
            } else {
                str = UUIDUtil.getRandomCode().substring(0, num);
            }
        } else {
            throw new Exception("参数只能大于0");
        }
        return str;
    }
    /**
     * 获取根据当前时间的字符串数据
     *
     * @return
     */
    public synchronized static String getTimeStr() {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddhhmmssS");
        return simpleDateFormat.format(new Date());
    }
    /**
     * @Description: 获取数字随机码
     * @Author pzb
     * @Date 2021/8/11 16:52
     * @Param
     * @Return
     * @Exception
     */
    public static String getNumberRandom(Integer num) {
        if (null == num) {
            num = 32;
        }
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < num; i++) {
            sb.append(Double.valueOf(Math.random() * 10).intValue());
        }
        return sb.toString();
    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/httpClinet/HttpClientUtil.java
New file
@@ -0,0 +1,303 @@
package com.ruoyi.other.util.httpClinet;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.NameValuePair;
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.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.security.KeyStore;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
 * http工具类
 */
public class HttpClientUtil {
    private static Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);
    private static PoolingHttpClientConnectionManager connectionManager;
    {
        //1.创建连接池管理器
        connectionManager = new PoolingHttpClientConnectionManager(60000,
                TimeUnit.MILLISECONDS);
        connectionManager.setMaxTotal(1000);
        connectionManager.setDefaultMaxPerRoute(50);
    }
    /**
     * 创建一个httpClient对象
     */
    private static CloseableHttpClient getHttpCline() {
        return HttpClients.custom()
                .setConnectionManager(connectionManager)
                .disableAutomaticRetries()
                .build();
    }
    private static RequestConfig getRequestConfig() {
        RequestConfig.Builder builder = RequestConfig.custom();
        builder.setSocketTimeout(60000)//3.1设置客户端等待服务端返回数据的超时时间
                .setConnectTimeout(30000)//3.2设置客户端发起TCP连接请求的超时时间
                .setExpectContinueEnabled(true)
                .setConnectionRequestTimeout(30000);//3.3设置客户端从连接池获取链接的超时时间
        return builder.build();
    }
    /**
     * 创建一个POST请求实例
     *
     * @param url    请求地址
     * @param params 请求参数
     */
    public static CloseableHttpResponse setPostHttpRequset(String url, Map<String, Object> params, Map<String, String> header, String contentType) throws Exception {
        HttpPost httpPost = new HttpPost(url);
        httpPost.setConfig(getRequestConfig());
        if (null != header) {
            for (String key : header.keySet()) {
                httpPost.setHeader(key, header.get(key));
            }
        }
        List<NameValuePair> list = new ArrayList<>();
        if (null != params) {
            Set<String> keys = params.keySet();
            for (String key : keys) {
                list.add(new BasicNameValuePair(key, null == params.get(key) ? null : params.get(key).toString()));
            }
        }
        switch (contentType) {
            case "form":
                httpPost.setEntity(new UrlEncodedFormEntity(list, "UTF-8"));
                break;
            case "json":
                ObjectMapper objectMapper = new ObjectMapper();
                String s = objectMapper.writeValueAsString(params);
                httpPost.setEntity(new StringEntity(s, ContentType.create(ContentType.APPLICATION_JSON.getMimeType(), Charset.forName("UTF-8"))));
                break;
        }
        return getHttpCline().execute(httpPost);
    }
    /**
     * 获取get请求实例
     *
     * @param url    请求地址
     * @param params 请求参数
     */
    private static CloseableHttpResponse setGetHttpRequset(String url, Map<String, Object> params, Map<String, String> header) throws Exception {
        StringBuffer sb = new StringBuffer();
        String p = "";
        if (null != params) {
            Set<String> keys = params.keySet();
            for (String key : keys) {
                sb.append(key + "=" + params.get(key) + "&");
            }
            p = "?" + sb.substring(0, sb.length() - 1);
        }
        HttpGet httpGet = new HttpGet(url + p);
        httpGet.setConfig(getRequestConfig());
        if (null != header) {
            for (String key : header.keySet()) {
                httpGet.setHeader(key, header.get(key));
            }
        }
        return getHttpCline().execute(httpGet);
    }
    /**
     * 发送http请求
     *
     * @param mothed      "GET、POST、PUT、HEAD、DELETE、HEAD、OPTIONS"
     * @param url         请求地址
     * @param params      请求参数
     * @param header      请求头
     * @param contentType 参数请求方式form/json
     * @return
     */
    public static HttpResult pushHttpRequset(String mothed, String url, Map<String, Object> params, Map<String, String> header, String contentType) throws Exception {
        String randome = UUID.randomUUID().toString();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.S");
        logger.info(sdf.format(new Date()) + "----(" + randome + ")请求参数:" + JSON.toJSONString(params));
        CloseableHttpResponse httpResponse = null;
        switch (mothed) {
            case "GET":
                httpResponse = setGetHttpRequset(url, params, header);
                break;
            case "POST":
                httpResponse = setPostHttpRequset(url, params, header, contentType);
                break;
        }
        int statusCode = httpResponse.getStatusLine().getStatusCode();
        String content = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
        logger.info(sdf.format(new Date()) + "----(" + randome + ")返回结果:" + content);
        HttpResult httpResult = HttpResult.getHttpResult(statusCode, content);
        close(httpResponse);
        return httpResult;
    }
    /**
     * 发送XML请求
     *
     * @param url    请求地址
     * @param xml    XML数据
     * @param header 自定义请求头
     * @return
     */
    public static HttpResult pushHttpRequsetXml(String url, String xml, Map<String, String> header) throws Exception {
        HttpPost httpPost = new HttpPost(url);
        httpPost.setConfig(getRequestConfig());
        for (String key : header.keySet()) {
            httpPost.setHeader(key, header.get(key));
        }
        httpPost.setHeader("Content-Type", "application/xml");
        httpPost.setEntity(new StringEntity(xml, "UTF-8"));
        CloseableHttpResponse httpResponse = getHttpCline().execute(httpPost);
        int statusCode = httpResponse.getStatusLine().getStatusCode();
        String content = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
        HttpResult httpResult = HttpResult.getHttpResult(statusCode, content);
        close(httpResponse);
        return httpResult;
    }
    /**
     * 请求https发送XML请求
     *
     * @param url          接口路径
     * @param xml          内容
     * @param header       请求头
     * @param certPassword 证书密码
     * @param certPath     证书路径
     * @param certType     证书类型
     * @return
     * @throws Exception
     */
    public static String pushHttpsRequsetXml(String url, String xml, Map<String, String> header, String certPassword, String certPath, String certType) throws Exception {
        HttpPost httpPost = new HttpPost(url);
        for (String key : header.keySet()) {
            httpPost.setHeader(key, header.get(key));
        }
        httpPost.setHeader("Content-Type", "application/xml");
        httpPost.setEntity(new StringEntity(xml, "UTF-8"));
        CloseableHttpClient httpCline = initCert(certPassword, certPath, certType);
        CloseableHttpResponse httpResponse = httpCline.execute(httpPost);
        String content = null;
        if (httpResponse.getStatusLine().getStatusCode() == 200) {
            content = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
        } else {
            content = "返回状态码:" + httpResponse.getStatusLine() + "。" + EntityUtils.toString(httpResponse.getEntity());
        }
        close(httpResponse);
        httpCline.close();
        return content;
    }
    public static String pushHttpsRequsetXml1(String url, String xml, Map<String, String> header, String certPassword, String certPath, String certType) throws Exception {
        HttpPost httpPost = new HttpPost(url);
        for (String key : header.keySet()) {
            httpPost.setHeader(key, header.get(key));
        }
        httpPost.setHeader("Content-Type", "application/xml");
        httpPost.setEntity(new StringEntity(xml, "UTF-8"));
        CloseableHttpClient httpCline = initCert1(certPassword, certPath, certType);
        CloseableHttpResponse httpResponse = httpCline.execute(httpPost);
        String content = null;
        if (httpResponse.getStatusLine().getStatusCode() == 200) {
            content = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
        } else {
            content = "返回状态码:" + httpResponse.getStatusLine() + "。" + EntityUtils.toString(httpResponse.getEntity());
        }
        close(httpResponse);
        httpCline.close();
        return content;
    }
    private static CloseableHttpClient initCert1(String key, String certPath, String certType) throws Exception {
        KeyStore keyStore = KeyStore.getInstance(certType);
        InputStream inputStream = new FileInputStream(new File(certPath));
        try {
            keyStore.load(inputStream, key.toCharArray());
        } finally {
            inputStream.close();
        }
        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, key.toCharArray()).build();
        SSLConnectionSocketFactory sslsf =
                new SSLConnectionSocketFactory(sslcontext, null, null,
                        SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        return HttpClients.custom().setSSLSocketFactory(sslsf).build();
    }
    /**
     * 初始化https对象(带证书)
     *
     * @param key      证书密码
     * @param certPath 证书路径
     * @param certType 证书类型
     * @throws Exception
     */
    private static CloseableHttpClient initCert(String key, String certPath, String certType) throws Exception {
        KeyStore keyStore = KeyStore.getInstance(certType);
        InputStream inputStream = new FileInputStream(new File(certPath));
        try {
            keyStore.load(inputStream, key.toCharArray());
        } finally {
            inputStream.close();
        }
        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, key.toCharArray()).build();
        SSLConnectionSocketFactory sslsf =
                new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null,
                        SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        return HttpClients.custom().setSSLSocketFactory(sslsf).build();
    }
    /**
     * 关闭资源
     */
    private static void close(CloseableHttpResponse httpResponse) {
        try {
            if (null != httpResponse) {
                EntityUtils.consume(httpResponse.getEntity());//此处高能,通过源码分析,由EntityUtils是否回收HttpEntity
                httpResponse.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != httpResponse) {
                    httpResponse.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/httpClinet/HttpResult.java
New file
@@ -0,0 +1,32 @@
package com.ruoyi.other.util.httpClinet;
import lombok.Data;
/**
 * http请求返回封装
 */
@Data
public class HttpResult {
    /**
     * 返回状态码
     */
    private Integer code;
    /**
     * 返回结果
     */
    private String data;
    /**
     * 返回封装结果
     *
     * @param code
     * @param data
     * @return
     */
    public static HttpResult getHttpResult(Integer code, String data) {
        HttpResult httpResult = new HttpResult();
        httpResult.setCode(code);
        httpResult.setData(data);
        return httpResult;
    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/PaymentUtil.java
@@ -7,9 +7,13 @@
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.other.util.UUIDUtil;
import com.ruoyi.other.util.payment.model.*;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.text.SimpleDateFormat;
import java.util.*;
/**
@@ -19,25 +23,25 @@
 */
@Slf4j
public class PaymentUtil {
    //微信公众号、微信小程序、微信 APP+/H5、云微小程序支付
    private static final String appId = "wxdeed472c98e42a54";
    private static final String appId = "wx049faf9c5234f31c";
    /**
     * 商户密钥
     */
    private static final String key = "925899fcc374430f9e4b4ba3db05b448";
    private static final String key = "ss369875124965782f148539657826321";
    /**
     * 商户号
     */
    private static final String merchantNo = "888122600004175";
    private static final String merchantNo = "1717539630";
    /**
     * 平台-报备商户号
     */
    private static final String sysTradeMerchantNo = "777168500885852";
    private static final String sysTradeMerchantNo = "";
    /**
     * 支付回调地址
     */
    private static final String callbackUrl = "https://www.qijisheng.top";
    private static final String callbackUrl = "https://221.182.45.100:8084";
    
    
    /**
@@ -101,8 +105,138 @@
        UniPayResult uniPayResult = JSON.parseObject(execute.body(), UniPayResult.class);
        return uniPayResult;
    }
    /**
     * native支付
     * @param orderNo           商户订单号
     * @param amount            订单金额
     * @param notifyUrl         服务器异步通知地址
     * @return
     */
    public static String nativePay(String orderNo, BigDecimal amount, String notifyUrl) throws Exception {
        int totalFee = amount.multiply(new BigDecimal("100")).intValue();
        String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        // 构建 XML 请求体
        Map<String, Object> params = new HashMap<>();
        params.put("appid", appId);
        params.put("mch_id", merchantNo);
        params.put("nonce_str", UUIDUtil.getRandomCode(16));
        params.put("body", "积分充值");
        params.put("out_trade_no", orderNo);
        params.put("total_fee", totalFee);
//        params.put("spbill_create_ip", InetAddress.getLocalHost().getHostAddress());
        params.put("spbill_create_ip", "221.182.45.100");
        params.put("notify_url", "https://221.182.45.100:8084/undif");
        params.put("trade_type", "NATIVE");
        params.put("product_id", "1");
        String sign = sign1(JSONObject.from(params)); // 使用原来的 sign 方法
        params.put("sign", sign);
        String xmlBody = mapToXml(params); // 转换为 XML 字符串
        // 发送请求
        HttpRequest post = HttpUtil.createPost(url);
        post.header(Header.CONTENT_TYPE, "application/xml");
        post.body(xmlBody); // 发送原始 XML 字符串
        log.info("Native支付接口请求参数:" + xmlBody);
        HttpResponse execute = post.execute();
        log.info("Native支付接口请求响应:" + execute.body());
        if (execute.getStatus() != 200) {
            log.error("Native支付接口异常:" + execute.body());
            return null;
        }
        return execute.body();
    }
    private static String mapToXml(Map<String, Object> map) {
        StringBuilder sb = new StringBuilder();
        sb.append("<xml>");
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            if (value != null && !value.toString().isEmpty()) {
                sb.append("<").append(key).append(">");
                sb.append(value);
                sb.append("</").append(key).append(">");
            }
        }
        sb.append("</xml>");
        return sb.toString();
    }
    public static String sign1(JSONObject body) {
        Set<Map.Entry<String, Object>> entries = body.entrySet();
        List<Map.Entry<String, Object>> infoIds = new ArrayList<>(entries);
        // 排除 sign 字段本身
        infoIds.removeIf(entry -> "sign".equals(entry.getKey()));
        // 按 ASCII 顺序排序
        infoIds.sort(Map.Entry.comparingByKey());
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, Object> entry : infoIds) {
            Object val = entry.getValue();
            if (val != null && !val.toString().trim().isEmpty()) {
                sb.append(entry.getKey()).append("=").append(val).append("&");
            }
        }
        // 最后拼接 &key=商户密钥
        sb.append("key=").append(key);
        String stringSignTemp = sb.toString();
        log.info("待签名串:{}", stringSignTemp);
        // 使用 MD5 加密
        return MD5AndKL.MD5(stringSignTemp);
    }
    /**
     * 微信下单的签名算法
     *
     * @param map
     * @return
     */
    private String weixinSignature(Map<String, Object> map) {
        try {
            Set<Map.Entry<String, Object>> entries = map.entrySet();
            List<Map.Entry<String, Object>> infoIds = new ArrayList<Map.Entry<String, Object>>(entries);
            // 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)
            Collections.sort(infoIds, new Comparator<Map.Entry<String, Object>>() {
                public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) {
                    return (o1.getKey()).toString().compareTo(o2.getKey());
                }
            });
            // 构造签名键值对的格式
            StringBuilder sb = new StringBuilder();
            for (Map.Entry<String, Object> item : infoIds) {
                if (item.getKey() != null || item.getKey() != "") {
                    String key = item.getKey();
                    Object val = item.getValue();
                    if (!(val == "" || val == null)) {
                        sb.append(key + "=" + val + "&");
                    }
                }
            }
            sb.append("key=" + key);
            String sign = MD5AndKL.MD5Encode(sb.toString(), "UTF-8").toUpperCase(); //注:MD5签名方式
            return sign;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    public static void main(String[] args) throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        String code = sdf.format(new Date()) + UUIDUtil.getNumberRandom(5);
        String string = PaymentUtil.nativePay(code,new BigDecimal("0.1"),
                "/test"
        );
        System.err.println(string);
    }
    /**
     * 查询支付订单
     * @param orderNo   订单号
@@ -282,11 +416,5 @@
    }
    
    
    public static void main(String[] args) {
//        UniPayResult uniPayResult = PaymentUtil.uniPay("852963742", 0.01D, "测试商品", "这是用于对接支付测试的商品描述",
//                "", "/order/shopping-cart/shoppingCartPaymentCallback", "ooOrs64zHLuInkZ_GF0LpIN9_Rxc", "777168500885852");
//        PaymentUtil.queryOrder("852963742");
//        PaymentUtil.closeOrder("852963742");
    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/payment/wx/WechatPayService.java
@@ -4,7 +4,14 @@
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.other.util.payment.MD5AndKL;
import com.ruoyi.other.util.payment.wx.WechatPayConfig;
import com.ruoyi.other.util.payment.wx.XMLUtil;
import org.apache.commons.codec.digest.DigestUtils;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.paddings.PKCS7Padding;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
@@ -12,6 +19,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import sun.misc.BASE64Decoder;
import sun.security.util.DerInputStream;
import sun.security.util.DerValue;
@@ -19,16 +27,19 @@
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.URL;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@@ -38,6 +49,7 @@
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;
import java.util.stream.Collectors;
/**
 * 微信支付服务类
@@ -48,16 +60,17 @@
    @Autowired
    private WechatPayConfig wechatPayConfig;
    private static final String RSA_PUBLIC_KEY_FILENAME = "wechat_rsa_public_key.pem";
    private static final String CERT_FOLDER = "C:\\cert\\";
    private static final String CERT_FOLDER = "cert/";
    /**
     * 统一下单
     * native统一下单
     * @param orderNumber 订单号
     * @param totalFee 总金额(分)
     * @param body 商品描述
     * @param openid 用户openid
     * @return 预支付订单信息
     */
    public R unifiedOrder(String orderNumber, String totalFee, String body, String openid, String callbackPath) throws Exception {
    public Map<String, String> unifiedOrder(String orderNumber, String totalFee,
                          String body,
                           String callbackPath) throws Exception {
        int i = new BigDecimal(totalFee).multiply(new BigDecimal("100")).intValue();
        String hostAddress = null;
        try {
@@ -74,45 +87,28 @@
        params.put("out_trade_no", orderNumber);
        params.put("total_fee", String.valueOf(i) );
        params.put("spbill_create_ip", "221.182.45.100"); // 实际应用中应获取客户端IP
        params.put("notify_url", wechatPayConfig.getCallbackPath()+callbackPath);
        params.put("trade_type", "JSAPI");
        params.put("openid", openid);
        params.put("notify_url", "http://221.182.45.100:8084"+callbackPath);
        params.put("trade_type", "NATIVE");
        // 生成签名
        String sign = weixinSignature(params);
        params.put("sign", sign);
        // 将参数转换为XML
        String xmlParams = XMLUtil.mapToXml(params).replaceFirst("^<\\?xml.+?\\?>\\s*", "");
        // 发送请求到微信支付统一下单接口
        String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        String result = HttpUtil.post(url, xmlParams);
        // 解析返回结果
        Map<String, String> resultMap = XMLUtil.xmlToMap(result);
        // 验证签名
        if (!verifySign(resultMap, wechatPayConfig.getKey())) {
            return R.fail("微信支付签名验证失败");
//            throw new Exception("微信支付签名验证失败");
            throw new Exception("微信支付签名验证失败");
        }
        if (!resultMap.get("return_code").equals("SUCCESS")) {
            throw new Exception("拉取支付失败");
        // 构建小程序支付所需参数
        Map<String, String> payParams = new HashMap<>();
        payParams.put("appId", wechatPayConfig.getAppId());
        payParams.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
        payParams.put("nonceStr", generateNonceStr());
        payParams.put("package", "prepay_id=" + resultMap.get("prepay_id"));
        payParams.put("signType", "MD5");
        // 生成支付签名
        String paySign = weixinSignature(payParams);
        payParams.put("paySign", paySign);
        //给前端标识
        payParams.put("payMethod","1");
        return R.ok(JSON.toJSONString(payParams));
        }
        resultMap.put("code",orderNumber);
        return resultMap;
    }
    /**
     * 微信下单的签名算法
@@ -436,12 +432,11 @@
        try {
            // 1. 解析回调XML数据
            if (StringUtils.isEmpty(xmlData)) {
//                logger.error("退款回调数据为空");
                return RefundCallbackResult.fail("回调数据为空");
            }
            //2.解析参数
            System.err.println(xmlData);
            System.out.println(xmlData);
            System.out.println("----------------------------------------");
            Map<String, String> resultMap = XMLUtil.xmlToMap(xmlData);
            System.out.println(resultMap.get("req_info"));
@@ -454,7 +449,7 @@
            }
            //4 使用商户API密钥解密req_info(AES-256-CBC算法)
            String decryptData = wxDecrypt(resultMap.get("req_info"), wechatPayConfig.getKey());
            String decryptData = decrypt(resultMap.get("req_info"), wechatPayConfig.getKey());
            Map<String, String> refundDetail = XMLUtil.xmlToMap(decryptData);
@@ -581,7 +576,7 @@
    }
    /**
     * 加载公钥 返回PublicKey
     * 加载公钥 返回PublicKey对象
     */
    public static PublicKey loadPublicKey(String pemContent) throws Exception {
        // 读取PEM文件内容
@@ -683,21 +678,9 @@
    }
    public static void main(String[] args) throws IOException {
/*
        try {
            // 1. 加载公钥
            PublicKey publicKey = loadPublicKey();
            // 2. 加密数据
            String sensitiveData = "用户名";
            String encryptedData = encrypt(sensitiveData, publicKey);
        String info="CjlaS7RVnPn7zzP5ByZDxUN7OrXGp1/DEdO0qahpIqDH/gTNHb/U7VmrVV0S4lXrIa0N8FEREC3CdIeT4XB5P4D0E8TSURu6J/cD01hFu28f0JDRfeips3vSpTgznRGyCfnUBDPYwyrVeP29Wac7WAb3CCcJf7OZWaweOUkaKjaBRa1GzMZcguSZnQJz0cD5Jb4HbTMvM0VAebfCY9aXdfFBIbm+cPYESo3awqwkNTQeT4V+FViw8f8sjkH0TScMgWBiSKmQC837BLD27yIGklqlYkDP2IMeiNw+b12qCAGszfp2vYd3X+HpViXkQQet3PJWYlAm55R+IgvschP7Ub65XzLINfQrJKrQUXiKKO2LwoSRSwZvfDkR8G8E8X59CnU2XvWKeos5Y0q8ckbJb97yI+09nNgMjYyJoVCVjTFgFMDEQ4+e3CpYRhD6V/3RBp+TvBwszldbRav2XEuCXL2kCJyJEAqLPMNyfYBSNF8z1btjyz0+y/xQQcySKlQInZ710FxSE7KwRSBQ92j9nDdlR7UxCrPVCkEd+GrVNSqqnyjNh1J/rPJPHvvGwkPPq72TKiw6ZgaIgIDhy0/lWHTclo4sjYAWuUVfg3CJ8dqkuQwVZ7i0+NiahIl78RtcUph8NR48yUgBkN7WhCcu5wLbg2tu8Qe0SIwHF+RW1x9Yc8akEkNbMd4xzs8lY5MYEU9V16U8RyWJuwPDph3RnmV8HQ+2hfzmjCvPkBwtfR8P5VdK86OIsHfnfQxAcPM2a86tOBBzFXPrLHgd2CRcDKH+MXTw7RSH/bk1PiMUAWF8TQsNDzgUlznJnkjiQxoym/4ZUf4C6072KKQHbp6bgBYkBhJLT2lmjVMNSX5b1SXM9eTQixRfq6MKGw3P8XJnKdofktVv+KtSzWQlW0C8p504NWACiExupF5EII7FG+xCWt7urWUbc4NRI36UFrKToQCLVv6UBCXt/t9iWlvs6SfuZhpCexeMmZWeiIldzRu87U9rXR46Hu7DAL8dZ+0ItsIZYThSIABzZgaLKggXlkjyAcbcPYKO7egrCmDtFhzHuh4uA3VeBylL3/ZLZ4FUedn/8L4e2iAu22Qj46ORlu17W5R8Ez9kubydeAgC9PkWnjptaubPxE0bjPN69tec";
            System.out.println("加密结果(Base64):\n" + encryptedData);
        } catch (Exception e) {
            e.printStackTrace();
        }*/
        String info="CjlaS7RVnPn7zzP5ByZDxUN7OrXGp1/DEdO0qahpIqDH/gTNHb/U7VmrVV0S4lXrIa0N8FEREC3CdIeT4XB5P4D0E8TSURu6J/cD01hFu2/uJOvcE6EeQH2xiRg/Wir4qcW7c6uTiLoqyirCQXcGzQb3CCcJf7OZWaweOUkaKjaBRa1GzMZcguSZnQJz0cD5jTMx+Tch5+b7jBq5PrTFxtMSH/DAG+kgkRazDFnEzkMeT4V+FViw8f8sjkH0TScMgWBiSKmQC837BLD27yIGklqlYkDP2IMeiNw+b12qCAGszfp2vYd3X+HpViXkQQet3PJWYlAm55R+IgvschP7Ub65XzLINfQrJKrQUXiKKO2LwoSRSwZvfDkR8G8E8X59CnU2XvWKeos5Y0q8ckbJb97yI+09nNgMjYyJoVCVjTGc7ghcYvWKbqanJ8bSFqiBCIqLSXsRR2DmJIxHq9fGE72kCJyJEAqLPMNyfYBSNF8z1btjyz0+y/xQQcySKlQInZ710FxSE7KwRSBQ92j9nDdlR7UxCrPVCkEd+GrVNSqqnyjNh1J/rPJPHvvGwkPPq72TKiw6ZgaIgIDhy0/lWHTclo4sjYAWuUVfg3CJ8dqkuQwVZ7i0+NiahIl78RtcUph8NR48yUgBkN7WhCcu5wLbg2tu8Qe0SIwHF+RW1x9Yc8akEkNbMd4xzs8lY5MYEU9V16U8RyWJuwPDph3RnmV8HQ+2hfzmjCvPkBwtfR8P5VdK86OIsHfnfQxAcPM2a86tOBBzFXPrLHgd2CRcDKH+MXTw7RSH/bk1PiMUAWF8TQsNDzgUlznJnkjiQxoym/4ZUf4C6072KKQHbp6bgBYkBhJLT2lmjVMNSX5b1SXM9eTQixRfq6MKGw3P8XJnKdofktVv+KtSzWQlW0C8p504NWACiExupF5EII7FG+xbTa/s7vxXCP7R98tpcQTGoQCLVv6UBCXt/t9iWlvs6SfuZhpCexeMmZWeiIldzRu87U9rXR46Hu7DAL8dZ+0ItsIZYThSIABzZgaLKggXlkjyAcbcPYKO7egrCmDtFhwN50V7hoXEQB8G5kf/lMuT5+xNE2FRmv7H2a0ttZiv4u17W5R8Ez9kubydeAgC9PkWnjptaubPxE0bjPN69tec";
        String key="fD0JzscfMf295SYtRK3MnPRjSCA4Gahr";
        try {
            String decrypted = decrypt(info, key);
@@ -709,26 +692,31 @@
    public static String decrypt(String encryptedStringA, String merchantKey) throws Exception {
        try {
            byte[] decode = Base64.getDecoder().decode(encryptedStringA);
            String sign = MD5AndKL.MD5Encode(merchantKey, "UTF-8").toLowerCase();
            System.out.println("MD5 Key: " + sign); // 调试输出
        // 1. 对加密串A做base64解码,得到加密串B
        byte[] decode = Base64.getDecoder().decode(encryptedStringA);
            if (Security.getProvider("BC") == null) {
                Security.addProvider(new BouncyCastleProvider());
            }
        // 2. 对商户key做md5,得到32位小写key*
        String sign = MD5AndKL.MD5Encode(merchantKey, "UTF-8").toLowerCase();
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
            byte[] aesKey = Arrays.copyOf(sign.getBytes("UTF-8"), 16); // 明确指定 UTF-8
            SecretKeySpec secretKeySpec = new SecretKeySpec(aesKey, "AES");
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
            byte[] decryptedBytes = cipher.doFinal(decode);
            return new String(decryptedBytes, "UTF-8"); // 明确指定 UTF-8
        } catch (Exception e) {
            System.err.println("解密失败: " + e.getMessage());
            throw e;
        // 3. 确保BouncyCastle提供者已添加
        if (Security.getProvider("BC") == null) {
            Security.addProvider(new BouncyCastleProvider());
        }
        // 4. 使用AES-256-ECB解密(PKCS7Padding)
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
        // 注意:微信要求使用AES-256,所以密钥应为32字节(256位)
        // 如果MD5结果是32字节(256位),直接使用
        byte[] aesKey = sign.getBytes(StandardCharsets.UTF_8);
        SecretKeySpec secretKeySpec = new SecretKeySpec(aesKey, "AES");
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
        // 执行解密并指定UTF-8编码
        byte[] decryptedBytes = cipher.doFinal(decode);
        return new String(decryptedBytes, StandardCharsets.UTF_8);
    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/vo/IntegralListVO.java
New file
@@ -0,0 +1,17 @@
package com.ruoyi.other.vo;
import com.ruoyi.common.core.web.page.BasePage;
import com.ruoyi.other.api.domain.IntegralRecord;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(value = "积分明细列表返回VO")
public class IntegralListVO extends IntegralRecord {
    @ApiModelProperty("用户名")
    private String appUserName;
    @ApiModelProperty("手机号")
    private String phone;
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/vo/IntegralVO.java
New file
@@ -0,0 +1,17 @@
package com.ruoyi.other.vo;
import com.ruoyi.common.core.web.page.PageInfo;
import com.ruoyi.other.api.domain.IntegralRecord;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(value = "积分钱包VO")
public class IntegralVO {
    @ApiModelProperty("积分数")
    private Integer integral;
    @ApiModelProperty("积分明细")
    private PageInfo<IntegralListVO> integralList;
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/vo/ServiceListVO.java
New file
@@ -0,0 +1,14 @@
package com.ruoyi.other.vo;
import com.ruoyi.other.api.domain.IntegralRecord;
import com.ruoyi.other.api.domain.ServicePay;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(value = "积分明细列表返回VO")
public class ServiceListVO extends ServicePay {
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/vo/ServiceVO.java
New file
@@ -0,0 +1,22 @@
package com.ruoyi.other.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.core.web.page.PageInfo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@ApiModel(value = "服务费VO")
public class ServiceVO {
    @ApiModelProperty("到期时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime endTime;
    @ApiModelProperty("状态 1未缴纳 2已过期 3已缴纳")
    private Integer status;
    @ApiModelProperty("缴纳明细")
    private PageInfo<ServiceListVO> serviceList;
}
ruoyi-service/ruoyi-other/src/main/resources/bootstrap.yml
@@ -12,8 +12,8 @@
    allow-circular-references: true
  profiles:
    # 环境配置
#    active: dev
    active: prod
    active: dev
#    active: prod
---
spring:
  config:
ruoyi-service/ruoyi-other/src/main/resources/mapper/other/IntegralRecord.xml
New file
@@ -0,0 +1,16 @@
<?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.other.mapper.IntegralRecordMapper">
    <select id="integralPageList" resultType="com.ruoyi.other.vo.IntegralListVO">
        select * from t_integral_record
        where 1=1
        <if test="query.integralType != null ">
            and integral_type = 1
        </if>
          and site_id = #{query.siteId}
        and delFlag = 0
        order by create_time desc
    </select>
</mapper>
ruoyi-service/ruoyi-other/src/main/resources/mapper/other/ServicePay.xml
New file
@@ -0,0 +1,16 @@
<?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.other.mapper.ServicePayMapper">
    <select id="servicePageList" resultType="com.ruoyi.other.vo.ServiceListVO">
        select * from
        t_service_pay
        where 1= 1
        <if test="query.userId != null">
            and user_id = #{query.userId}
        </if>
        and payStatus =1
        order by pay_time desc
    </select>
</mapper>
ruoyi-service/ruoyi-other/src/main/test/com/ruoyi/other/RuoYiOtherApplicationTests.java
New file
@@ -0,0 +1,62 @@
package com.ruoyi.other;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.other.RuoYiOtherApplication;
import com.ruoyi.other.util.MyQrCodeUtil;
import com.ruoyi.other.util.ObsUploadUtil;
import com.ruoyi.other.util.QRCodeUtil;
import com.ruoyi.other.util.UUIDUtil;
import com.ruoyi.other.util.payment.wx.WechatPayService;
import org.apache.tomcat.util.http.fileupload.ByteArrayOutputStream;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = RuoYiOtherApplication.class)
public class RuoYiOtherApplicationTests {
    @Resource
    private WechatPayService wechatPayService;
    @Test
    public void contextLoads() throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        String code = sdf.format(new Date()) + UUIDUtil.getNumberRandom(5);
        Map<String,String> r = wechatPayService.unifiedOrder(code, "0.01", "积分充值", "/other/wx/integralCallback");
        System.err.println(r);
        String codeUrl = r.get("code_url");
        MyQrCodeUtil.createCodeToFile(codeUrl);
        BufferedImage blueImage = QRCodeUtil.createImage(codeUrl);
        MultipartFile blueFile = convert(blueImage, new Date().getTime() + UUIDUtil.getRandomCode(3) + ".PNG");
        String s = ObsUploadUtil.obsUpload(blueFile);
        System.err.println(s);
    }
    public static MultipartFile convert(BufferedImage bufferedImage, String fileName) throws IOException {
        // 将 BufferedImage 转换为字节数组
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(bufferedImage, "png", baos);
        byte[] bytes = baos.toByteArray();
        // 创建 ByteArrayResource
        ByteArrayResource resource = new ByteArrayResource(bytes);
        // 创建 MockMultipartFile
        MockMultipartFile multipartFile = new MockMultipartFile(
                "file",
                fileName,
                "image/png",
                resource.getInputStream()
        );
        return multipartFile;
    }
}