huliguo
8 天以前 2485a4875adda2ffd0e8cfccdf749f15fe8d48cb
bug修改
1 文件已重命名
8个文件已删除
9个文件已添加
42个文件已修改
2813 ■■■■■ 已修改文件
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/FinanceController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/IndexController.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/OrderController.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/QuestionController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SystemConfigController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application.yml 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/wx/CloseOrderResult.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/wx/FrpCodeEnum.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/wx/PaymentUtil.java 286 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/wx/QueryOrderResult.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/wx/QueryRefundResult.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/wx/RefundCallbackResult.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/wx/RefundResult.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/wx/UniPayResult.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/pom.xml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/Qichacha.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/OrderMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/QuestionMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/pojo/dto/AppUserPageDTO.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/pojo/dto/FinanceFlowsDTO.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/pojo/vo/ChangeVO.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/pojo/vo/ErrorVO.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/pojo/vo/InvoiceVO.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/pojo/vo/LicencePageVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/pojo/vo/OrderDetailVO.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/pojo/vo/OrderPageVO.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/pojo/vo/QuestionPageVO.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/pojo/vo/TaxVO.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/OrderService.java 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/QuestionService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/AppUserServiceImpl.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/LicenceServiceImpl.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/OrderServiceImpl.java 475 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/QuestionServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/wx/HttpUtil.java 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/wx/MD5AndKL.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/wx/PayResult.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/wx/RefundCallbackResult.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/wx/WechatPayConfig.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/wx/WechatPayService.java 726 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/wx/XMLUtil.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/cert/apiclient_cert.p12 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/cert/apiclient_cert.pem 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/cert/apiclient_key.pem 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/AppUserMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/OrderMapper.xml 178 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/QuestionMapper.xml 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/FinanceController.java
@@ -36,8 +36,8 @@
    @PostMapping("/top")
    @ApiOperation(value = "财务流水-顶部")
    @PreAuthorize("@ss.hasPermi('finance:flows')")
    public R<FinanceFlowsTopVO> top(){
        return R.ok(orderService.financeTop());
    public R<FinanceFlowsTopVO> top(@RequestBody FinanceFlowsDTO dto){
        return R.ok(orderService.financeTop(dto));
    }
    /**
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/IndexController.java
@@ -19,6 +19,7 @@
import javax.annotation.Resource;
import javax.validation.Valid;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
@Slf4j
@@ -60,21 +61,25 @@
    @ApiOperation(value = "收入统计-折线图")
    @PreAuthorize("@ss.hasPermi('index:manage')")
    public R<IndexLineChartVO> chart(@RequestParam(required = false, defaultValue = "7") Integer days,
                                     @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd ") LocalDate startDate,
                                     @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) {
                                     @RequestParam(required = false)  @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime startTime,
                                     @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime endTime) {
        // 处理日期范围
        LocalDate now = LocalDate.now();
        if (startDate == null || endDate == null) {
            startDate = now.minusDays(days - 1);
            endDate = now;
        LocalDate start;
        LocalDate end ;
        if (startTime == null || endTime == null) {
            start = now.minusDays(days - 1);
            end = now;
        } else {
            // 验证日期范围不超过30天
            long daysBetween = ChronoUnit.DAYS.between(startDate, endDate);
            long daysBetween = ChronoUnit.DAYS.between(startTime, endTime);
            start=startTime.toLocalDate();
            end = endTime.toLocalDate();
            if (daysBetween > 30) {
                endDate = startDate.plusDays(30);
                endTime = startTime.plusDays(30);
            }
        }
        return R.ok(orderService.chart(startDate,endDate));
        return R.ok(orderService.chart(start,end));
    }
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/OrderController.java
@@ -2,8 +2,6 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.utils.wx.RefundCallbackResult;
import com.ruoyi.system.pojo.dto.AppUserPageDTO;
import com.ruoyi.system.pojo.dto.OrderPageDTO;
import com.ruoyi.system.pojo.vo.*;
import com.ruoyi.system.service.OrderService;
@@ -14,9 +12,7 @@
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@Slf4j
@RestController
@@ -57,41 +53,41 @@
    /**
     * todo redis 企业异常信息
     *  企业异常信息
     */
    @GetMapping("/error/{id}")
    @ApiOperation(value = "查看详情-企业异常信息")
    @PreAuthorize("@ss.hasPermi('order:manage')")
    public R<ErrorVO> error(@PathVariable("id") String id) {
        return R.ok(orderService.error(id));
    public R error(@PathVariable("id") String id) {
        return orderService.error(id);
    }
    /**
     * todo redis 企业变更信息
     *  企业变更信息
     */
    @GetMapping("/change/{id}")
    @ApiOperation(value = "查看详情-企业变更信息")
    @PreAuthorize("@ss.hasPermi('order:manage')")
    public R<ChangeVO> change(@PathVariable("id") String id) {
        return R.ok(orderService.change(id));
    public R change(@PathVariable("id") String id) {
        return orderService.change(id);
    }
    /**
     * todo redis 企业纳税信息
     *  企业纳税信息
     */
    @GetMapping("/tax/{id}")
    @ApiOperation(value = "查看详情-企业纳税信息")
    @PreAuthorize("@ss.hasPermi('order:manage')")
    public R<TaxVO> tax(@PathVariable("id") String id) {
        return R.ok(orderService.tax(id));
    public R tax(@PathVariable("id") String id) {
        return orderService.tax(id);
    }
    /**
     * todo redis 企业发票信息
     * 企业发票信息
     */
    @GetMapping("/invoice/{id}")
    @ApiOperation(value = "查看详情-企业纳税信息")
    @ApiOperation(value = "查看详情-企业发票信息")
    @PreAuthorize("@ss.hasPermi('order:manage')")
    public R<InvoiceVO> invoice(@PathVariable("id") String id) {
        return R.ok(orderService.invoice(id));
    public R invoice(@PathVariable("id") String id) {
        return orderService.invoice(id);
    }
    /**
     *  删除
@@ -127,28 +123,21 @@
        return R.ok();
    }
    /**
     * 订单取消支付回退
     *
     * @param refundCallbackResult
     * @param response
     * 平台取消订单支付回退
     * @return
     */
    @ResponseBody
    @GetMapping("/refundPayMoneyCallback")
    public void refundPayMoneyCallback(RefundCallbackResult refundCallbackResult, HttpServletResponse response) {
        R callback = orderService.refundPayMoneyCallback(refundCallbackResult);
    @PostMapping("/refundPayMoneyCallback")
    public String refundPayMoneyCallback(@RequestBody(required = false) String xmlData) {
        System.out.println("平台取消订单:" + xmlData);
        R callback = orderService.refundPayMoneyCallback(xmlData);
        if (callback.getCode() == 200) {
            response.setStatus(200);
            PrintWriter out = null;
            try {
                out = response.getWriter();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            out.println("success");
            out.flush();
            out.close();
            return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
        }else {
            System.err.println("支付回退错误:"+callback.getMsg());
            return "<xml><return_code><![CDATA[FAIL]]></return_code></xml>";
        }
    }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/QuestionController.java
@@ -38,8 +38,8 @@
    public R<IPage<QuestionPageVO>> getQuestionPage(
            @RequestParam(value = "pageNum",defaultValue = "1") Integer pageNum,
            @RequestParam(value = "pageSize",defaultValue = "10") Integer pageSize,
            @RequestParam(value = "name",required = false) String name) {
        return R.ok(questionService.getQuestionPage(pageNum,pageSize,name));
            @RequestParam(value = "title",required = false) String title) {
        return R.ok(questionService.getQuestionPage(pageNum,pageSize,title));
    }
    /**
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
@@ -48,6 +48,8 @@
    @Autowired
    private TokenService tokenService;
    @Autowired
    private ISysConfigService configService;
@@ -74,7 +76,7 @@
     * 
     * @return 用户信息
     */
    @GetMapping("getInfo")
    @GetMapping("/getInfo")
    public AjaxResult getInfo()
    {
        LoginUser loginUser = SecurityUtils.getLoginUser();
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java
@@ -1,7 +1,15 @@
package com.ruoyi.web.controller.system;
import java.awt.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.system.mapper.SysMenuMapper;
import com.ruoyi.system.pojo.dto.UpdatePwdDTO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -48,6 +56,8 @@
    @Autowired
    private TokenService tokenService;
    @Autowired
    private SysMenuMapper sysMenuMapper;
    /**
     * 个人信息
@@ -61,8 +71,33 @@
        AjaxResult ajax = AjaxResult.success(user);
        ajax.put("roleGroup", userService.selectUserRoleGroup(loginUser.getUsername()));
        ajax.put("postGroup", userService.selectUserPostGroup(loginUser.getUsername()));
        ajax.put("permissions" ,getUserPaths(user.getUserId()));
        return ajax;
    }
    /**
     * 获取用户拥有的所有path
     * @param userId 用户ID
     * @return 用户拥有的所有path列表
     */
    public List<String> getUserPaths(Long userId) {
        List<Long> ids = new ArrayList<>();
        // 根据用户id查询出menus
        List<SysMenu> menus = sysMenuMapper.selectMenusByUserId(userId);
        //将id和父类id取出
        menus.forEach(menu -> {
            ids.add(menu.getMenuId());
           if (null!= menu.getParentId() && menu.getParentId() != 0) {
               ids.add(menu.getParentId());
           }
        });
        if (menus.isEmpty()) {
            return Collections.emptyList();
        }
        return sysMenuMapper.getPathsByMenuIds(ids);
    }
    /**
     * 修改用户
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java
@@ -327,7 +327,7 @@
        // 更新缓存用户权限
        LoginUser loginUser = getLoginUser();
        if (StringUtils.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin()) {
            loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getUserName()));
            loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getPhonenumber()));
            loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser()));
            tokenService.setLoginUser(loginUser);
        }
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java
@@ -270,9 +270,9 @@
    @GetMapping("/page/list")
    public R<IPage<SysUserPageListVO>> getSysUserPageList(@RequestParam("pageNum")Integer pageNum,
                                                          @RequestParam("pageSize")Integer pageSize,
                                                          @RequestParam("nickName")String nickName,
                                                          @RequestParam("phone")Integer phone,
                                                          @RequestParam("status")String status) {
                                                          @RequestParam(value = "nickName",required = false)String nickName,
                                                          @RequestParam(value = "phone",required = false)Integer phone,
                                                          @RequestParam(value = "status",required = false)String status) {
        IPage<SysUserPageListVO> page=new Page<>(pageNum,pageSize);
        return R.ok(userService.getSysUserPageList(page,nickName,phone,status));
    }
@@ -310,7 +310,7 @@
    /**
     * 新增
     */
    @ApiOperation(value = "账号管理-查看详情(回显)", tags = "系统后台-权限管理")
    @ApiOperation(value = "账号管理-新增", tags = "系统后台-权限管理")
    @PreAuthorize("@ss.hasPermi('permission:account')")
    @Log(title = "账号管理", businessType = BusinessType.INSERT)
    @PostMapping("/add")
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SystemConfigController.java
@@ -34,7 +34,7 @@
     * 系统配置-查看
     */
    @GetMapping("/getSystemConfigByType")
    @ApiOperation(value = "根据类型获取配置内容")
    @ApiOperation(value = "根据类型获取配置内容(1-启动页 2-客服 3-抽单)")
    public R<SystemConfigVO> getSystemConfigByType(@RequestParam(value = "type") Integer type) {
        return R.ok(systemConfigService.getSystemConfigByType(type));
    }
ruoyi-admin/src/main/resources/application.yml
@@ -16,7 +16,7 @@
# 开发环境配置
server:
  # 服务器的HTTP端口,默认为8080
  port: 8081
  port: 8084
  servlet:
    # 应用的访问路径
    context-path: /
@@ -94,8 +94,8 @@
  header: Authorization
  # 令牌密钥
  secret: abcdefghijklmnopqrstuvwxyz
  # 令牌有效期(默认30分钟)
  expireTime: 30
  # 令牌有效期(默认72000分钟)
  expireTime: 72000
# MyBatis配置
mybatis:
@@ -117,7 +117,7 @@
  # 是否开启swagger
  enabled: true
  # 请求前缀
  pathMapping: /dev-api
  pathMapping:
# 防止XSS攻击
xss:
@@ -127,3 +127,12 @@
  excludes: /system/notice
  # 匹配链接
  urlPatterns: /system/*,/monitor/*,/tool/*
wx:
  appid: wx180c41e1915992e8
  appletsAppSecret: 8c6cc3410891d3096988b92c154ba5e9
  mchId: 1720552698
  key: 5Kb8zX9qR3r4D7Yw3vHnJgLp6sA4cE1M
  callbackPath: http://221.182.45.100:8084
  certPath: classpath:cert/apiclient_cert.p12
  RASPath:
ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java
@@ -178,11 +178,12 @@
    }
    public static void main(String[] args) {
        System.out.println("19923261698".substring(5, 11));
        String hash = Md5Utils.hash("123456");
        System.out.println(hash);
        System.out.println(encryptPassword(hash));
        System.out.println(matchesPassword(hash, "$2a$10$VhdvkOtOTUq1NndvwPC36./D9bPVhDJWbt.tVWtGdqW3.sA07Ikx."));
        System.out.println(matchesPassword(hash, "$2a$10$rk6XzddjDljPCCTghplExe2rgcLCJHVEEOAOuUaHalsIp/FCWObkG"));
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/utils/wx/CloseOrderResult.java
File was deleted
ruoyi-common/src/main/java/com/ruoyi/common/utils/wx/FrpCodeEnum.java
File was deleted
ruoyi-common/src/main/java/com/ruoyi/common/utils/wx/PaymentUtil.java
File was deleted
ruoyi-common/src/main/java/com/ruoyi/common/utils/wx/QueryOrderResult.java
File was deleted
ruoyi-common/src/main/java/com/ruoyi/common/utils/wx/QueryRefundResult.java
File was deleted
ruoyi-common/src/main/java/com/ruoyi/common/utils/wx/RefundCallbackResult.java
File was deleted
ruoyi-common/src/main/java/com/ruoyi/common/utils/wx/RefundResult.java
File was deleted
ruoyi-common/src/main/java/com/ruoyi/common/utils/wx/UniPayResult.java
File was deleted
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java
@@ -29,6 +29,8 @@
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysUserService;
import java.util.List;
/**
 * 登录校验方法
 * 
ruoyi-system/pom.xml
@@ -22,6 +22,16 @@
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common</artifactId>
        </dependency>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15on</artifactId>
            <version>1.70</version>
        </dependency>
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
    </dependencies>
ruoyi-system/src/main/java/com/ruoyi/system/domain/Qichacha.java
@@ -1,9 +1,12 @@
package com.ruoyi.system.domain;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Date;
@Data
@@ -21,5 +24,6 @@
    @TableField(value = "time")
    @ApiModelProperty(value = "调用时间")
    private Date time;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime time;
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/OrderMapper.java
@@ -27,7 +27,7 @@
    List<DailyStatistics> getDailyStatistics(@Param("startTime") LocalDateTime startTime, @Param("endTime") LocalDateTime endTime);
    FinanceFlowsTopVO financeTop();
    FinanceFlowsTopVO financeTop(@Param("dto") FinanceFlowsDTO dto);
    IPage<FinanceFlowsPageVO> flowsPage(@Param("page") IPage<FinanceFlowsPageVO> page, @Param("dto")FinanceFlowsDTO dto);
ruoyi-system/src/main/java/com/ruoyi/system/mapper/QuestionMapper.java
@@ -12,5 +12,5 @@
@Mapper
public interface QuestionMapper extends BaseMapper<Question> {
    IPage<QuestionPageVO> getQuestionPage(@Param("page")IPage<BannerPageVO> page,@Param("name") String name);
    IPage<QuestionPageVO> getQuestionPage(@Param("page")IPage<BannerPageVO> page,@Param("title") String title);
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java
@@ -128,4 +128,9 @@
    List<MenuTreeVO> selectMenusByParentId(@Param("menuId")Long menuId);
    List<MenuTreeVO> getAllRootMenu();
    List<SysMenu> selectMenusByUserId(@Param("userId")Long userId);
    List<String> getPathsByMenuIds(@Param("ids")List<Long> ids);
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java
@@ -134,7 +134,7 @@
    IPage<SysUserPageListVO> getSysUserPageList(@Param("page") IPage<SysUserPageListVO> page, @Param("nickName") String nickName,@Param("phone")  Integer phone, @Param("status") String status);
    R<SysUserVO> getSysUserVO(@Param("userId") Long userId);
    SysUserVO getSysUserVO(@Param("userId") Long userId);
    SysUser selectuserByPhone(@Param("phonenumber") String phonenumber);
}
ruoyi-system/src/main/java/com/ruoyi/system/pojo/dto/AppUserPageDTO.java
@@ -15,7 +15,7 @@
    @ApiModelProperty("分页大小")
    private Integer pageSize=10;
    @ApiModelProperty("用户姓名")
    private String username;
    private String userName;
    @ApiModelProperty("联系电话")
    private String phone;
    @ApiModelProperty("状态")
ruoyi-system/src/main/java/com/ruoyi/system/pojo/dto/FinanceFlowsDTO.java
@@ -21,8 +21,8 @@
    private String orderNo;
    @ApiModelProperty(value = "类型:0-全部 1-收入 2-退款")
    private Integer type = 0;
    @ApiModelProperty(value = "类型: 1-收入 2-退款")
    private Integer type ;
    @ApiModelProperty(value = "操作时间-开始")
@@ -34,4 +34,7 @@
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime operateTimeEnd;
    @ApiModelProperty(hidden = true)
    private Integer offset;
}
ruoyi-system/src/main/java/com/ruoyi/system/pojo/vo/ChangeVO.java
@@ -1,9 +1,18 @@
package com.ruoyi.system.pojo.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
@ApiModel("企业变更信息")
public class ChangeVO {
    @ApiModelProperty("银税互动授权记录")
    private List<Object> bankAndTaxInteractive;
    @ApiModelProperty("股东明细")
    private List<Object> sharesRatioList;
    @ApiModelProperty("法人/股东变更")
    private List<Object> legalPersonGdChange;
}
ruoyi-system/src/main/java/com/ruoyi/system/pojo/vo/ErrorVO.java
@@ -1,10 +1,17 @@
package com.ruoyi.system.pojo.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import java.util.List;
@Data
@ApiModel("企业异常信息VO")
public class ErrorVO {
    @ApiModelProperty("数据是否存在(1-存在 0-不存在)")
    private Integer VerifyResult;
    @ApiModelProperty("数据信息")
    private Object Data;
}
ruoyi-system/src/main/java/com/ruoyi/system/pojo/vo/InvoiceVO.java
@@ -1,9 +1,73 @@
package com.ruoyi.system.pojo.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
@Data
@ApiModel("企业发票信息")
public class InvoiceVO {
    @ApiModelProperty("近45日是否有开票记录")
    private Boolean anyPre45daysFpRcd;
    @ApiModelProperty("近一个月开票金额(元)")
    private BigDecimal hjjeZc1;
    @ApiModelProperty("近3个月开票环比增长率")
    private BigDecimal avgMonJshjYxMomRate3;
    @ApiModelProperty("近3个月开票金额(元)")
    private BigDecimal hjjeZc3;
    @ApiModelProperty("近6个月开票环比增长率")
    private BigDecimal avgMonJshjYxMomRate6;
    @ApiModelProperty("近6个月开票金额(元)")
    private BigDecimal hjjeZc6;
    @ApiModelProperty("近12个月开票环比增长率")
    private BigDecimal avgMonJshjYxMomRate12;
    @ApiModelProperty("近12个月开票金额(元)")
    private BigDecimal hjjeZc12;
    @ApiModelProperty("近24个月开票金额(元)")
    private BigDecimal hjjeZc24;
    @ApiModelProperty("近3个月下游客户统计")
    private Integer downCusCnt3;
    @ApiModelProperty("近12月下游客户数量(家)")
    private Integer downCusCnt12;
    @ApiModelProperty("近12个月下游开票张数")
    private Integer kpslQb;
    @ApiModelProperty("近12月作废发票数量占比")
    private BigDecimal kpslFpRatio;
    @ApiModelProperty("近12月月均开票金额增长率")
    private BigDecimal avgMonJshjYxYoyRate;
    @ApiModelProperty("近12月红冲发票张数占比")
    private BigDecimal kpslHpRatio;
    @ApiModelProperty("近12个月红冲金额占比")
    private BigDecimal hjjeHpRatio;
    @ApiModelProperty("近12个月最大连续未开票间隔天数(销项)")
    private Integer nokpslYxMaxdayCnt;
    //todo 近12个月最长连续断票月数
    //todo 近12个月断票月数(不含2月份)
    @ApiModelProperty("近三年开票信息报表(元)")
    private List<Object> f4yPer1mDownFpInfoList;
}
ruoyi-system/src/main/java/com/ruoyi/system/pojo/vo/LicencePageVO.java
@@ -3,8 +3,11 @@
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class LicencePageVO {
    @ApiModelProperty(value = "主键ID")
ruoyi-system/src/main/java/com/ruoyi/system/pojo/vo/OrderDetailVO.java
@@ -21,7 +21,7 @@
    @ApiModelProperty(value = "公司主键ID")
    private String id;
    @ApiModelProperty(value = "订单状态:1-待交易 2待确认(预订) 3已确认(待支付) 4办理中 5-已完成 6-已下架 ")
    @ApiModelProperty(value = "订单状态: 2待确认(预订) 3已确认(待支付) 4办理中 5-卖家已完成 6-卖家已完成 7-已下架 ")
    private Integer status;
ruoyi-system/src/main/java/com/ruoyi/system/pojo/vo/OrderPageVO.java
@@ -43,7 +43,7 @@
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime bookTime;
    @ApiModelProperty(value = "订单状态:1-待交易 2待确认(预订) 3已确认(待支付) 4办理中 5-已完成 6-已下架 ")
    @ApiModelProperty(value = "订单状态:-1-已取消 1-待交易 2待确认(预订) 3已确认(待支付) 4办理中  6-已完成 7-已下架 ")
    private Integer status;
    @ApiModelProperty(value = "完成时间")
ruoyi-system/src/main/java/com/ruoyi/system/pojo/vo/QuestionPageVO.java
@@ -11,7 +11,7 @@
    @ApiModelProperty("主键ID")
    private Integer id;
    @ApiModelProperty("问题标题")
    private String name;
    private String title;
    @ApiModelProperty("排序")
    private Integer orderNum;
ruoyi-system/src/main/java/com/ruoyi/system/pojo/vo/TaxVO.java
@@ -1,9 +1,66 @@
package com.ruoyi.system.pojo.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
@Data
@ApiModel("企业纳税信息")
public class TaxVO {
    @ApiModelProperty("纳税信用等级")
    private String taxCreditRating;
    @ApiModelProperty("纳税人种类")
    private String taxpayerType;
    @ApiModelProperty("近12月增税销售额")
    private BigDecimal vatTaxShouldsaleAmt12;
    @ApiModelProperty("近24月增税销售额")
    private BigDecimal vatTaxShouldsaleAmt24;
    @ApiModelProperty("近12月纳税总额(元)")
    private BigDecimal taxAmt12;
    @ApiModelProperty("近24月纳税总额(元)")
    private BigDecimal taxAmt24;
    @ApiModelProperty("近12月增税应纳额(元)")
    private BigDecimal vatTaxShouldpay;
    @ApiModelProperty("近3月滞纳金次数")
    private Integer overdueFineInfoCnt3;
    @ApiModelProperty("近12个月滞纳金金额(元)")
    private BigDecimal overdueFineInfoAmt12;
    @ApiModelProperty("近12月滞纳金次数")
    private Integer overdueFineInfoCnt12;
    @ApiModelProperty("近12月0申报月数(月)")
    private Integer vatTax0dclrMonCnt;
    // todo 近12月0申报月数(月)
    // todo 当前欠税余额(元)
    // todo 近12月最长连续0纳税申报月数
    //todo 资产金额(去年年报)
    //todo 负债率(去年年报)
    //todo 营业利润额(去年年报)
    //todo 营业净利率(去年年报)
    @ApiModelProperty("滞纳金情况")
    private List<Object> overdueFineDetailsList;
    @ApiModelProperty("近四年纳税信息完税表")
    private List<Object> taxPrev4yearsTaxInfoDict;
    @ApiModelProperty("税务处罚")
    private Object enterprise;
}
ruoyi-system/src/main/java/com/ruoyi/system/service/OrderService.java
@@ -3,13 +3,13 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.utils.wx.RefundCallbackResult;
import com.ruoyi.system.domain.Agreement;
import com.ruoyi.system.domain.Order;
import com.ruoyi.system.pojo.dto.AddAgreementDTO;
import com.ruoyi.system.pojo.dto.FinanceFlowsDTO;
import com.ruoyi.system.pojo.dto.OrderPageDTO;
import com.ruoyi.system.pojo.dto.WithdrawPageDTO;
import com.ruoyi.system.pojo.vo.*;
import java.time.LocalDate;
@@ -23,13 +23,13 @@
    BusinessVO business(String id);
    ErrorVO error(String id);
    R  error(String id);
    ChangeVO change(String id);
    R change(String id);
    TaxVO tax(String id);
    R tax(String id);
    InvoiceVO invoice(String id);
    R invoice(String id);
    void delete(String id);
@@ -37,13 +37,13 @@
    void cancel(String id);
    R refundPayMoneyCallback(RefundCallbackResult refundCallbackResult);
    R refundPayMoneyCallback(String xmlData);
    TodayStatisticsVO today();
    IndexLineChartVO chart(LocalDate startDate, LocalDate endDate);
    FinanceFlowsTopVO financeTop();
    FinanceFlowsTopVO financeTop(FinanceFlowsDTO dto);
    IPage<FinanceFlowsPageVO> flowsPage(FinanceFlowsDTO dto);
ruoyi-system/src/main/java/com/ruoyi/system/service/QuestionService.java
@@ -15,7 +15,7 @@
public interface QuestionService extends IService<Question>  {
    IPage<QuestionPageVO> getQuestionPage(Integer pageNum, Integer pageSize, String name);
    IPage<QuestionPageVO> getQuestionPage(Integer pageNum, Integer pageSize, String title);
    void add(AddQuestionDTO dto);
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/AppUserServiceImpl.java
@@ -62,9 +62,12 @@
        //2.2计算订单金额 扣减超时订单金额
        for(BuyerInfoAndOrder order :orders) {
            BigDecimal total = new BigDecimal("0.00");
            order.setPrice(order.getPrice()==null?BigDecimal.ZERO:order.getPrice());
            order.setCommissionPrice(order.getCommissionPrice()==null?BigDecimal.ZERO:order.getCommissionPrice());
            order.setCommissionPlatform(order.getCommissionPlatform()==null?BigDecimal.ZERO:order.getCommissionPlatform());
            total = total.add(order.getPrice()).subtract(order.getCommissionPrice()).subtract(order.getCommissionPlatform());
            System.out.println(total);
            //扣减超时订单
            /*//扣减超时订单
            //到期时间 支付时间 + 预计时间 + 新增时间
            LocalDateTime end = order.getPayTime().plusDays(order.getEstimatedDays()).plusDays(order.getAddDay());
            LocalDateTime now = LocalDateTime.now();//计算扣费时间
@@ -78,7 +81,7 @@
            long  days= (seconds + 86399) / 86400;//24小时
            //计算天数差
            BigDecimal dailyAmount = new BigDecimal("100.00");
            total = total.subtract((dailyAmount.multiply(BigDecimal.valueOf(days))));
            total = total.subtract((dailyAmount.multiply(BigDecimal.valueOf(days))));*/
            recorded = recorded.add(total);
        }
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/LicenceServiceImpl.java
@@ -27,10 +27,11 @@
    public void add(AddLicenceDTO dto) {
        //判断是否存在
        Licence licence = this.baseMapper.selectOne(new LambdaQueryWrapper<Licence>().eq(Licence::getName, dto.getName()).eq(Licence::getDelFlag, 0));
        if (null == licence) {
        if (null != licence) {
            throw new ServiceException("许可证名称重复");
        }
        licence = new Licence();
        BeanUtils.copyProperties(dto, licence);
        String grand = String.join(";", dto.getGrandNameList());//多个以分号相隔
        licence.setGradeName(grand);
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/OrderServiceImpl.java
@@ -1,29 +1,32 @@
package com.ruoyi.system.service.impl;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.wx.PaymentUtil;
import com.ruoyi.common.utils.wx.RefundCallbackResult;
import com.ruoyi.common.utils.wx.RefundResult;
import com.ruoyi.system.domain.*;
import com.ruoyi.system.mapper.*;
import com.ruoyi.system.pojo.dto.AddAgreementDTO;
import com.ruoyi.system.pojo.dto.FinanceFlowsDTO;
import com.ruoyi.system.pojo.dto.OrderPageDTO;
import com.ruoyi.system.pojo.dto.WithdrawPageDTO;
import com.ruoyi.system.pojo.model.DailyStatistics;
import com.ruoyi.system.pojo.model.DrawSheet;
import com.ruoyi.system.pojo.vo.*;
import com.ruoyi.system.service.AgreementService;
import com.ruoyi.system.service.OrderService;
import com.ruoyi.system.wx.RefundCallbackResult;
import com.ruoyi.system.wx.WechatPayService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -32,9 +35,8 @@
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.*;
import java.util.concurrent.TimeUnit;
@Slf4j
@Service
@@ -53,12 +55,15 @@
    private ScheduleMapper scheduleMapper;
    @Resource
    private CompanyMapper companyMapper;
    @Autowired
    private SystemConfigMapper systemConfigMapper;
    @Autowired
    private AppUserMapper appUserMapper;
    @Autowired
    private WithdrawMapper withdrawMapper;
    @Resource
    private RedisCache redisCache;
    @Resource
    private QichachaMapper qichachaMapper;
    @Override
    public IPage<OrderPageVO> getOrderPage(OrderPageDTO dto) {
@@ -77,8 +82,11 @@
        List<PermitVO> permitVOs = new ArrayList<>();
        LocalDateTime now = LocalDateTime.now();
        for (Permit permit : permits) {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            LocalDateTime expireTime = LocalDateTime.parse(permit.getExpireTime(), formatter);
            DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
            LocalDate expireDate = LocalDate.parse(permit.getExpireTime(), dateFormatter);
            // 如果需要转为 LocalDateTime(添加默认时间)
            LocalDateTime expireTime = expireDate.atTime(23,59,59); // 默认00:00:00
            if (now.isAfter(expireTime)) {
                //过滤 未过期的
                PermitVO permitVO = new PermitVO();
@@ -98,7 +106,7 @@
        }
        vo.setOpeningBankVOList(openingBankVOs);
        if (vo.getStatus() != 4 && vo.getStatus() != 5) {
        if (vo.getStatus() != 4 && vo.getStatus() != 5&& vo.getStatus() != 6) {
            return vo;
        }
@@ -146,6 +154,7 @@
    @Override
    public BusinessVO business(String id) {
        //先查询数据库中的
        Company company = companyMapper.selectById(id);
        if (null == company || company.getIsDelete() != 0) {
            throw new ServiceException("公司不存在");
@@ -153,29 +162,347 @@
        BusinessVO vo = new BusinessVO();
        BeanUtils.copyProperties(company, vo);
        vo.setPlace(company.getCity()+company.getProvince()+company.getArea());
        //todo 再查企业工商信息 覆盖掉之前的
        return vo;
    }
    @Override
    public ErrorVO error(String id) {
        return null;
    public R  error(String id) {
        ErrorVO errorVO = new ErrorVO();
        Company company = companyMapper.selectById(id);
        if (null == company || company.getIsDelete() != 0) {
            throw new ServiceException("公司不存在");
        }
        JSONObject result = getQiChaChaCompanyExceptionCheck(company.getCompanyName());
        if("200".equals(result.get("Status"))){
            //查询成功
            JSONObject data = (JSONObject) result.get("Result");
            Integer verifyResult = (Integer) data.get("VerifyResult");
            errorVO.setVerifyResult(verifyResult);
            if (verifyResult==1){
                //该企业有异常数据
                JSONArray list = (JSONArray) data.get("Data");
                errorVO.setData(list);
            }
            return R.ok(errorVO);
        }else {
            //查询失败
            return R.fail(result.get("Message"));
        }
    }
    /**
     * 经营异常核查
     * @param companyName
     * @return
     * VerifyResult  int    数据是否存在(1-存在,0-不存在)
     * Data      List<Object>  数据信息
     * Data:[{
     *     AddReason  列入经营异常名录原因
     *     AddDate    列入日期
     *     RomoveReason  移出经营异常名录原因(保留字段)
     *     RemoveDate   移出日期(保留字段)
     *     DecisionOffice      作出决定机关
     *     RemoveDecisionOffice  移出决定机关(保留字段)
     * }]
     * Status
     * Message
     * OrderNumber
     */
    public  JSONObject getQiChaChaBasicDetailsCompany(String companyName){
        Object cacheObject = redisCache.getCacheObject("qichacha_" +"basic_"+ companyName);
        if(cacheObject != null){
            JSONObject jsonObject = JSONObject.parseObject(cacheObject.toString());
            return jsonObject;
        }
        String url = "https://api.qichacha.com/ECIV4/GetBasicDetailsByName?key=642987ca3faf4a7daeac70463ae22695&keyword="+companyName;
        HttpRequest get = HttpUtil.createGet(url);
        String timeStr = String.valueOf(System.currentTimeMillis() / 1000);
        get.header("Token", SecureUtil.md5("642987ca3faf4a7daeac70463ae22695"+timeStr+"AC776F2957291EAE3B4161702E89A9F3").toUpperCase());
        get.header("Timespan", timeStr);
        HttpResponse execute = get.execute();
        String body = execute.body();
        JSONObject jsonObject = JSONObject.parseObject(body);
        if("200".equals(jsonObject.get("Status"))){
            //查询成功
            String string = jsonObject.toString();
            redisCache.setCacheObject("qichacha_"+"basic_"+companyName,string,24, TimeUnit.HOURS);
            //统计调用次数
            Qichacha qichacha = new Qichacha();
            qichacha.setTime(LocalDateTime.now());
            qichacha.setType(1);//企业异常查询
            qichachaMapper.insert(qichacha);
            return jsonObject;
        }
        return jsonObject;
    }
    /**
     * 经营异常核查
     * @param companyName
     * @return
     * VerifyResult  int    数据是否存在(1-存在,0-不存在)
     * Data      List<Object>  数据信息
     * Data:[{
     *     AddReason  列入经营异常名录原因
     *     AddDate    列入日期
     *     RomoveReason  移出经营异常名录原因(保留字段)
     *     RemoveDate   移出日期(保留字段)
     *     DecisionOffice      作出决定机关
     *     RemoveDecisionOffice  移出决定机关(保留字段)
     * }]
     * Status
     * Message
     * OrderNumber
     */
    public  JSONObject getQiChaChaCompanyExceptionCheck(String companyName){
        Object cacheObject = redisCache.getCacheObject("qichacha_" + companyName);
        if(cacheObject != null){
            JSONObject jsonObject = JSONObject.parseObject(cacheObject.toString());
            return jsonObject;
        }
        String url = "https://api.qichacha.com/ExceptionCheck/GetList?key=642987ca3faf4a7daeac70463ae22695&searchKey="+companyName;
        HttpRequest get = HttpUtil.createGet(url);
        String timeStr = String.valueOf(System.currentTimeMillis() / 1000);
        get.header("Token", SecureUtil.md5("642987ca3faf4a7daeac70463ae22695"+timeStr+"AC776F2957291EAE3B4161702E89A9F3").toUpperCase());
        get.header("Timespan", timeStr);
        HttpResponse execute = get.execute();
        String body = execute.body();
        JSONObject jsonObject = JSONObject.parseObject(body);
        if("200".equals(jsonObject.get("Status"))){
            //查询成功
            String string = jsonObject.toString();
            redisCache.setCacheObject("qichacha_"+companyName,string,24, TimeUnit.HOURS);
            //统计调用次数
            Qichacha qichacha = new Qichacha();
            qichacha.setTime(LocalDateTime.now());
            qichacha.setType(2);//企业异常查询
            qichachaMapper.insert(qichacha);
            return jsonObject;
        }
        return jsonObject;
    }
    private  JSONObject getReport(String sign){
        Object cacheObject = redisCache.getCacheObject("shuimu_" + sign);
        if(cacheObject != null){
            JSONObject jsonObject = JSONObject.parseObject(cacheObject.toString());
            return jsonObject;
        }
        HttpRequest post = HttpUtil.createPost("https://shuimui.szsmjr.com/index/index/result");
        HashMap<String, String> stringStringHashMap = new HashMap<>();
        stringStringHashMap.put("Origin","https://shuimui.szsmjr.com");
        post.addHeaders(stringStringHashMap);
        post.body("{\"sn\":\""+sign+"\"}");
        HttpResponse execute = post.execute();
        String body = execute.body();
        JSONObject jsonObject = JSONObject.parseObject(body);
        if("1001".equals(jsonObject.get("code"))){
            //查询成功
            String string = jsonObject.toString();
            redisCache.setCacheObject("shuimu_"+sign,string,1, TimeUnit.MINUTES);
            return jsonObject;
        }
        return jsonObject;
    }
    @Override
    public R change(String id) {
//        System.err.println(getReport("ZZD20250508017232394218445"));
        ChangeVO changeVO = new ChangeVO();
       /* Company companyInfo = companyMapper.selectById(id);
        if (null == companyInfo || companyInfo.getIsDelete() != 0) {
            throw new ServiceException("公司不存在");
        }*/
//        JSONObject jsonObject=getReport(companyInfo.getLink().split("=")[1]);
        JSONObject jsonObject=getReport("ZZD20250508017232394218445");
        if (!"1001".equals(jsonObject.getString("code"))){
            return R.fail(jsonObject.get("msg"));
        }
        JSONObject data1 = (JSONObject) jsonObject.get("data");
        JSONObject data2 = (JSONObject) data1.get("data");
        JSONObject company = (JSONObject) data2.get("company");
        // 银税互动
        JSONArray bankAndTaxInteractive = (JSONArray)  company.get("bankAndTaxInteractive");
        changeVO.setBankAndTaxInteractive(bankAndTaxInteractive);
        //股东明细
        JSONArray sharesRatioList = (JSONArray)  company.get("sharesRatioList");
        changeVO.setSharesRatioList(sharesRatioList);
        //法人/股东变更
        JSONArray legalPersonGdChange = (JSONArray) company.get("legalPersonGdChange");
        changeVO.setLegalPersonGdChange(legalPersonGdChange);
        return R.ok(changeVO);
    }
    @Override
    public ChangeVO change(String id) {
        return null;
    public R tax(String id) {
        TaxVO taxVO = new TaxVO();
       /* Company companyInfo = companyMapper.selectById(id);
        if (null == companyInfo || companyInfo.getIsDelete() != 0) {
            throw new ServiceException("公司不存在");
        }*/
//        JSONObject jsonObject=getReport(companyInfo.getLink().split("=")[1]);
        JSONObject jsonObject=getReport("ZZD20250508017232394218445");
        if (!"1001".equals(jsonObject.getString("code"))){
            return R.fail(jsonObject.get("msg"));
        }
        JSONObject data1 = (JSONObject) jsonObject.get("data");
        JSONObject data2 = (JSONObject) data1.get("data");
        JSONObject taxInfo = (JSONObject) data2.get("taxInfo");
        //税务处罚
        JSONObject enterprise = (JSONObject) data2.get("enterprise");
        taxVO.setEnterprise(enterprise);
        //滞纳金情况
        JSONArray overdueFineDetailsList = (JSONArray) taxInfo.get("overdueFineDetailsList");
        taxVO.setOverdueFineDetailsList(overdueFineDetailsList);
        //近四年纳税信息完税表
        JSONArray taxPrev4yearsTaxInfoDict = (JSONArray) taxInfo.get("taxPrev4yearsTaxInfoDict");
        taxVO.setTaxPrev4yearsTaxInfoDict(taxPrev4yearsTaxInfoDict);
        //纳税信用等级
        String taxCreditRating = (String) taxInfo.get("taxCreditRating");
        taxVO.setTaxCreditRating(taxCreditRating);
        //纳税人种类
        String taxpayerType = (String) taxInfo.get("taxpayerType");
        taxVO.setTaxpayerType(taxpayerType);
        //近12月增税销售额
        JSONObject taxPrevMnMthsAllTaxDclrPrjInfoDict = (JSONObject) taxInfo.get("taxPrevMnMthsAllTaxDclrPrjInfoDict");
        JSONObject taxPrev12mthsAllTaxDclrPrjInfoDict = (JSONObject) taxPrevMnMthsAllTaxDclrPrjInfoDict.get("taxPrev12mthsAllTaxDclrPrjInfoDict");
        taxVO.setVatTaxShouldsaleAmt12((BigDecimal) taxPrev12mthsAllTaxDclrPrjInfoDict.get("vatTaxShouldsaleAmt"));
        //近24月增税销售额
        JSONObject taxPrev24mthsAllTaxDclrPrjInfoDict = (JSONObject) taxPrevMnMthsAllTaxDclrPrjInfoDict.get("taxPrev24mthsAllTaxDclrPrjInfoDict");
        taxVO.setVatTaxShouldsaleAmt24( (BigDecimal) taxPrev24mthsAllTaxDclrPrjInfoDict.get("vatTaxShouldsaleAmt"));
        JSONObject taxPrevMnMthsTaxInfoDict = (JSONObject) taxInfo.get("taxPrevMnMthsTaxInfoDict");
        //近12月纳税总额(元)
        JSONObject taxPrev12mthsTaxInfo = (JSONObject) taxPrevMnMthsTaxInfoDict.get("taxPrev12mthsTaxInfo");
        taxVO.setTaxAmt12( (BigDecimal) taxPrev12mthsTaxInfo.get("taxAmt"));
        //近24月纳税总额(元)
        JSONObject taxPrev24mthsTaxInfo = (JSONObject) taxPrevMnMthsTaxInfoDict.get("taxPrev24mthsTaxInfo");
        taxVO.setTaxAmt24( (BigDecimal) taxPrev24mthsTaxInfo.get("taxAmt"));
        //近12月增税应纳额(元)
        taxVO.setVatTaxShouldpay( (BigDecimal) taxPrev12mthsAllTaxDclrPrjInfoDict.get("vatTaxShouldpay"));
        //近12个月滞纳金金额(元) 次数
        JSONObject taxPrevMnMthsOverdueFineInfoDict = (JSONObject) taxInfo.get("taxPrevMnMthsOverdueFineInfoDict");
        JSONObject taxPrev12mthsOverdueFineInfo = (JSONObject) taxPrevMnMthsOverdueFineInfoDict.get("taxPrev12mthsOverdueFineInfo");
        taxVO.setOverdueFineInfoAmt12( (BigDecimal) taxPrev12mthsOverdueFineInfo.get("overdueFineInfoAmt"));
        taxVO.setOverdueFineInfoCnt12( (Integer) taxPrev12mthsOverdueFineInfo.get("overdueFineInfoCnt"));
        // 近3个月滞纳金金额(元)
        JSONObject taxPrev3mthsOverdueFineInfo = (JSONObject) taxPrevMnMthsOverdueFineInfoDict.get("taxPrev3mthsOverdueFineInfo");
        taxVO.setOverdueFineInfoCnt3( (Integer) taxPrev3mthsOverdueFineInfo.get("overdueFineInfoCnt"));
        //近12月0申报月数(月数)
        JSONObject taxPrevMnMthsAllTaxDclrInfoDict = (JSONObject) taxInfo.get("taxPrevMnMthsAllTaxDclrInfoDict");
        JSONObject taxPrev12mthsAllTaxDclrInfoDict = (JSONObject) taxPrevMnMthsAllTaxDclrInfoDict.get("taxPrev12mthsAllTaxDclrInfoDict");
        taxVO.setVatTax0dclrMonCnt( (Integer) taxPrev12mthsAllTaxDclrInfoDict.get("vatTax0dclrMonCnt"));
        return R.ok(taxVO);
    }
    @Override
    public TaxVO tax(String id) {
        return null;
    public R invoice(String id) {
        InvoiceVO vo = new InvoiceVO();
       /* Company companyInfo = companyMapper.selectById(id);
        if (null == companyInfo || companyInfo.getIsDelete() != 0) {
            throw new ServiceException("公司不存在");
        }*/
//        JSONObject jsonObject=getReport(companyInfo.getLink().split("=")[1]);
        JSONObject jsonObject=getReport("ZZD20250508017232394218445");
        if (!"1001".equals(jsonObject.getString("code"))){
            return R.fail(jsonObject.get("msg"));
        }
        JSONObject data1 = (JSONObject) jsonObject.get("data");
        JSONObject data2 = (JSONObject) data1.get("data");
        JSONObject invoiceInfo = (JSONObject) data2.get("invoiceInfo");
        //近45日是否有开票记录
        vo.setAnyPre45daysFpRcd((Boolean) invoiceInfo.get("anyPre45daysFpRcd"));
        //近一个月开票金额(元)
        JSONArray f4yPer1mDownFpInfoList = (JSONArray) invoiceInfo.get("f4yPer1mDownFpInfoList");
        JSONObject f4yPer1mDownFpInfo1 = (JSONObject) f4yPer1mDownFpInfoList.get(1);
        vo.setHjjeZc1( ObjectToBigDecimal(f4yPer1mDownFpInfo1.get("hjjeZc")) );
        JSONObject fpPrevMnMthsDownFpInfoList = (JSONObject) invoiceInfo.get("fpPrevMnMthsDownFpInfoList");
        JSONObject fpPrev3mthsDownFpInfoList = (JSONObject) fpPrevMnMthsDownFpInfoList.get("fpPrev3mthsDownFpInfoList");
        //近3个月开票环比增长率
        vo.setAvgMonJshjYxMomRate3( ObjectToBigDecimal(fpPrev3mthsDownFpInfoList.get("avgMonJshjYxMomRate")) );
        //近3个月开票金额(元)
        vo.setHjjeZc3( ObjectToBigDecimal( fpPrev3mthsDownFpInfoList.get("hjjeZc") ));
        JSONObject fpPrev6mthsDownFpInfoList = (JSONObject) fpPrevMnMthsDownFpInfoList.get("fpPrev6mthsDownFpInfoList");
        //近6个月开票环比增长率
        vo.setAvgMonJshjYxMomRate6( ObjectToBigDecimal(fpPrev6mthsDownFpInfoList.get("avgMonJshjYxMomRate")) );
        //近6月开票金额(元)
        vo.setHjjeZc6( ObjectToBigDecimal(fpPrev6mthsDownFpInfoList.get("hjjeZc"))  );
        JSONObject fpPrev12mthsDownFpInfoList = (JSONObject) fpPrevMnMthsDownFpInfoList.get("fpPrev12mthsDownFpInfoList");
        //近12个月开票环比增长率
        vo.setAvgMonJshjYxMomRate12( ObjectToBigDecimal(fpPrev12mthsDownFpInfoList.get("avgMonJshjYxMomRate")) );
        //近12月开票金额(元)
        vo.setHjjeZc12( ObjectToBigDecimal(fpPrev12mthsDownFpInfoList.get("hjjeZc"))  );
        //近24个月开票金额(元)
        JSONObject fpPrev24mthsDownFpInfoList = (JSONObject) fpPrevMnMthsDownFpInfoList.get("fpPrev24mthsDownFpInfoList");
        vo.setHjjeZc24( ObjectToBigDecimal(fpPrev24mthsDownFpInfoList.get("hjjeZc") ));
        //近12个月下游客户统计
        vo.setDownCusCnt3( (Integer) fpPrev3mthsDownFpInfoList.get("downCusCnt"));
        //近12个月下游客户统计
        vo.setDownCusCnt12( (Integer) fpPrev12mthsDownFpInfoList.get("downCusCnt"));
        //近12个月下游开票张数
        vo.setKpslQb( (Integer) fpPrev12mthsDownFpInfoList.get("kpslQb"));
        //近12月作废发票数量占比
        vo.setKpslFpRatio(ObjectToBigDecimal(fpPrev12mthsDownFpInfoList.get("kpslFpRatio")));
        //近12月月均开票金额增长率
        vo.setAvgMonJshjYxYoyRate(ObjectToBigDecimal(fpPrev12mthsDownFpInfoList.get("avgMonJshjYxYoyRate")) );
        //近12月红冲发票张数占比
        vo.setKpslHpRatio(ObjectToBigDecimal(fpPrev12mthsDownFpInfoList.get("kpslHpRatio")));
        //近12个月红冲金额占比
        vo.setHjjeHpRatio(ObjectToBigDecimal(fpPrev12mthsDownFpInfoList.get("hjjeHpRatio")) );
        //近12个月最大连续未开票间隔天数(销项)
        vo.setNokpslYxMaxdayCnt((Integer) fpPrev12mthsDownFpInfoList.get("nokpslYxMaxdayCnt"));
        //近三年开票信息报表(元)
        vo.setF4yPer1mDownFpInfoList(f4yPer1mDownFpInfoList);
        return R.ok(vo);
    }
    @Override
    public InvoiceVO invoice(String id) {
        return null;
    }
    private BigDecimal ObjectToBigDecimal(Object o) {
        // 处理不同类型的情况
        if ( o instanceof BigDecimal) {
            return (BigDecimal) o;
        } else if (o instanceof Number) {
            // 将其他数字类型(如 Integer、Double)转换为 BigDecimal
            return BigDecimal.valueOf(((Number) o).doubleValue());
        }
        return BigDecimal.ZERO;
    }
    @Transactional
    @Override
    public void delete(String id) {
@@ -191,6 +518,7 @@
            //查看订单状态是否为待确认
            Order order = this.baseMapper.selectOne(new LambdaQueryWrapper<Order>()
                    .eq(Order::getCompanyId, company.getId())
                            .eq(Order::getIsRefund,0)
                    .ne(Order::getStatus,-1));//取消的订单不要
            if (null == order) {
                throw new ServiceException("订单不存在");
@@ -254,7 +582,7 @@
        if (!company.getStatus().equals(4)){// 4-锁定中
            throw new ServiceException("订单状态错误,不能操作");
        }
        //查看订单 取消的不要
        //查看订单
        Order order = this.baseMapper.selectOne(new LambdaQueryWrapper<Order>()
                .eq(Order::getCompanyId, company.getId())
                .ne(Order::getStatus,-1));
@@ -262,10 +590,10 @@
            throw new ServiceException("订单不存在");
        }
        //检查状态  3已确认(未付款) 4办理中 5卖家已完成
        if (!order.getStatus().equals(3)||!order.getStatus().equals(4)||!order.getStatus().equals(5)) {
        if (!order.getStatus().equals(3)&&!order.getStatus().equals(4)&&!order.getStatus().equals(5)) {
            throw new ServiceException("订单状态错误,不能操作");
        }
        if (company.getStatus().equals(3)){
        if (order.getStatus().equals(3)){
            //未付款 直接取消订单
            order.setStatus(-1);
            this.baseMapper.updateById(order);
@@ -275,52 +603,91 @@
            appUserMapper.updateById(user);
            //商品状态修改
            company.setStatus(1);
            order.setIsRefund(1);
            order.setRefundTime(LocalDateTime.now());
            companyMapper.updateById(company);
        }else{
            R r = refundPayMoney(order);//退款
            if (200 == r.getCode()) {
                //退款成功
                order.setStatus(-1);//订单取消状态
                order.setIsRefund(1);//退款标志
                order.setRefundTime(LocalDateTime.now());//退款时间
                this.updateById(order);
                //将分佣次数加回
                User user = appUserMapper.selectById(order.getUserId());
                user.setInviteNum(user.getInviteNum()+1);
                appUserMapper.updateById(user);
                //商品状态修改
                company.setStatus(1);
                companyMapper.updateById(company);
                //退款申请成功
                log.info("退款申请成功,订单id:{}",order.getId());
            }
        }
    }
    @Override
    public R refundPayMoneyCallback(RefundCallbackResult refundCallbackResult) {
        String code = refundCallbackResult.getR3_RefundOrderNo().substring(1);
        log.info("退款回调:{}",code);
        Order order = this.getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderNo, code));
    public R refundPayMoneyCallback(String xmlData ) {
        RefundCallbackResult result = wechatPayService.processRefundCallback(xmlData);
        if (!result.isSuccess()) {
            System.out.println("会员退费错误:"+result.getMsg());
            return R.fail(result.getMsg());
        }
        Order order = this.getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderNo, result.getOrderNo()));
        if (null == order || order.getStatus() == -1) {
            return R.ok();
        }
        order.setStatus(-1);
        order.setIsRefund(1);
        order.setRefundTime(LocalDateTime.now());
        this.updateById(order);
        //查找公司
        Company company = companyMapper.selectById(order.getCompanyId());
        //取消订单
        order.setStatus(-1);
        order.setIsRefund(1);
        order.setRefundTime(LocalDateTime.now());
        this.baseMapper.updateById(order);
        //将分佣次数加回
        User user = appUserMapper.selectById(order.getUserId());
        User inviter = appUserMapper.selectById(user.getInviteId());
        if (inviter!=null && inviter.getIsDelete()==0){
            inviter.setInviteNum(inviter.getInviteNum()+1);
            appUserMapper.updateById(inviter);
        }
        //商品状态修改
        company.setStatus(1);
        companyMapper.updateById(company);
        return R.ok();
    }
    /**
     * 返回订单支付金额
     */
    @Autowired
    private WechatPayService wechatPayService;
    @Transactional
    public R refundPayMoney(Order order) {
        //开始退款
        BigDecimal paymentAmount = order.getPrice();
        if (BigDecimal.ZERO.compareTo(order.getPrice()) < 0) {//支付的金额是否大于0
            //微信退款
            RefundResult refund = PaymentUtil.refund(order.getOrderNo(), "R" + order.getOrderNo(), paymentAmount.doubleValue(),
                    "/system/order/refundPayMoneyCallback");
            if (!"100".equals(refund.getRa_Status())) {
                return R.fail(refund.getRc_CodeMsg());//退款失败
            Map<String,String> map = wechatPayService.refund(order.getOrderNo(), order.getOrderNo(), order.getPrice().toString(), order.getPrice().toString(), "平台取消订单", "/system/order/refundPayMoneyCallback");
            if (!"SUCCESS".equals(map.get("return_code"))) {
                return R.fail(map.get("return_msg"));//退款失败
            }
        }
        //查找公司
        Company company = companyMapper.selectById(order.getCompanyId());
        //取消订单
        order.setStatus(-1);
        order.setIsRefund(1);
        order.setRefundTime(LocalDateTime.now());
        this.baseMapper.updateById(order);
        //将分佣次数加回
        User user = appUserMapper.selectById(order.getUserId());
        User inviter = appUserMapper.selectById(user.getInviteId());
        if (inviter!=null && inviter.getIsDelete()==0){
            inviter.setInviteNum(inviter.getInviteNum()+1);
            appUserMapper.updateById(inviter);
        }
        //商品状态修改
        company.setStatus(1);
        companyMapper.updateById(company);
        return R.ok();
    }
@@ -369,14 +736,16 @@
    }
    @Override
    public FinanceFlowsTopVO financeTop() {
        return this.baseMapper.financeTop();
    public FinanceFlowsTopVO financeTop(FinanceFlowsDTO dto) {
        return this.baseMapper.financeTop(dto);
    }
    @Override
    public IPage<FinanceFlowsPageVO> flowsPage(FinanceFlowsDTO dto) {
        IPage<FinanceFlowsPageVO> page = new Page<>(dto.getPageNum(), dto.getPageSize());
        Long total  = this.baseMapper.countFlowsPage(dto);
        //分页处理
        dto.setOffset((dto.getPageNum()-1)*dto.getPageSize());
        IPage<FinanceFlowsPageVO> financeFlowsPageVOIPage = this.baseMapper.flowsPage(page, dto);
        financeFlowsPageVOIPage.setTotal(total);
        return financeFlowsPageVOIPage;
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/QuestionServiceImpl.java
@@ -20,9 +20,9 @@
    @Override
    public IPage<QuestionPageVO> getQuestionPage(Integer pageNum, Integer pageSize, String name) {
    public IPage<QuestionPageVO> getQuestionPage(Integer pageNum, Integer pageSize, String title) {
        IPage<BannerPageVO> page=new Page<>(pageNum, pageSize);
        return this.baseMapper.getQuestionPage(page,name);
        return this.baseMapper.getQuestionPage(page,title);
    }
    @Override
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java
@@ -480,14 +480,21 @@
        //判断角色名称是否存在
        SysRole sysRole = new SysRole();
        sysRole.setRoleName(dto.getRoleName());
        List<SysRole> sysRoles = roleMapper.selectRoleList(sysRole);
        if (sysRoles!=null && !sysRoles.isEmpty()){
            throw new ServiceException("该角色名称重复");
            sysRoles.forEach(role -> {
                if (!role.getRoleId().equals(dto.getRoleId())){
                    throw new ServiceException("该角色名称重复");
                }
            });
        }
        //删除之前的
        //删除之前的角色菜单关联
        roleMenuMapper.deleteRoleMenuByRoleId(dto.getRoleId());
        //修改角色名称
        roleMapper.updateRole(sysRole);
        //添加角色菜单
        roleMenuMapper.insert(sysRole.getRoleId(),dto.getMenuIds());
        roleMenuMapper.insert(dto.getRoleId(),dto.getMenuIds());
    }
    @Override
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
@@ -1,6 +1,7 @@
package com.ruoyi.system.service.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@@ -565,7 +566,7 @@
    @Override
    public R<SysUserVO> getInfo(Long userId) {
        return userMapper.getSysUserVO(userId);
        return R.ok(userMapper.getSysUserVO(userId));
    }
    @Override
@@ -591,7 +592,7 @@
        sysUser.setPhonenumber(dto.getPhonenumber());
        sysUser.setNickName(dto.getNickName());
        //后六位为默认密码
        sysUser.setPassword(SecurityUtils.encryptPassword(Md5Utils.hash(dto.getPhonenumber().substring(6,12))));
        sysUser.setPassword(SecurityUtils.encryptPassword(Md5Utils.hash(dto.getPhonenumber().substring(5,11))));
        sysUser.setStatus("0");
        sysUser.setDeptId(dto.getDeptId());  //添加用户-部门关系
        userMapper.insertUser(sysUser);
@@ -662,7 +663,7 @@
        if (sysUser==null){
            throw new ServiceException("该用户不存在");
        }
        sysUser.setPassword(SecurityUtils.encryptPassword(Md5Utils.hash(sysUser.getPassword().substring(6,12))));
        sysUser.setPassword(SecurityUtils.encryptPassword(Md5Utils.hash(sysUser.getPhonenumber().substring(5,11))));
        userMapper.updateUser(sysUser);
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/wx/HttpUtil.java
New file
@@ -0,0 +1,99 @@
package com.ruoyi.system.wx;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import javax.net.ssl.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
/**
 * HTTP工具类
 */
public class HttpUtil {
    /**
     * 发送POST请求
     */
    public static String post(String urlStr, String data) throws Exception {
        // 设置超时时间(单位:毫秒)
        int timeout = 5000; // 5秒
        // 发送 POST 请求
        try (HttpResponse response = HttpRequest.post(urlStr)
                .body(data, "application/xml") // 设置 XML 请求体
                .timeout(timeout)
                .execute()) {
            // 检查 HTTP 状态码
            if (!response.isOk()) {
                throw new RuntimeException("HTTP请求失败,状态码: " + response.getStatus()
                        + ", 响应: " + response.body());
            }
            return response.body();
        }
    }
    /**
     * 发送HTTPS请求
     */
    public static String postHttps(String urlStr, String data, String certPath, String certPassword) throws Exception {
        // 创建SSL上下文
        SSLContext sslContext = SSLContext.getInstance("SSL");
        TrustManager[] trustManagers = {new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }
            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }
            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        }};
        sslContext.init(null, trustManagers, new java.security.SecureRandom());
        // 创建HTTPS连接
        URL url = new URL(urlStr);
        HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
        conn.setSSLSocketFactory(sslContext.getSocketFactory());
        conn.setHostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        });
        conn.setRequestMethod("POST");
        conn.setDoOutput(true);
        conn.setDoInput(true);
        conn.setUseCaches(false);
        conn.setRequestProperty("Content-Type", "application/xml");
        conn.setRequestProperty("Connection", "Keep-Alive");
        conn.setRequestProperty("Charset", "UTF-8");
        // 发送请求
        OutputStream os = conn.getOutputStream();
        os.write(data.getBytes("UTF-8"));
        os.flush();
        os.close();
        // 获取响应
        StringBuilder result = new StringBuilder();
        BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
        String line;
        while ((line = br.readLine()) != null) {
            result.append(line);
        }
        br.close();
        return result.toString();
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/wx/MD5AndKL.java
File was renamed from ruoyi-common/src/main/java/com/ruoyi/common/utils/wx/MD5AndKL.java
@@ -1,4 +1,4 @@
package com.ruoyi.common.utils.wx;
package com.ruoyi.system.wx;
import java.security.MessageDigest;
ruoyi-system/src/main/java/com/ruoyi/system/wx/PayResult.java
New file
@@ -0,0 +1,16 @@
package com.ruoyi.system.wx;
import lombok.Data;
/**
 * 支付结果VO
 */
@Data
public class PayResult {
    // 订单ID
    private String orderNumber;
    // 微信交易ID
    private String transactionId;
    // 支付金额
    private String totalFee;
}
ruoyi-system/src/main/java/com/ruoyi/system/wx/RefundCallbackResult.java
New file
@@ -0,0 +1,38 @@
package com.ruoyi.system.wx;
import lombok.Data;
/**
 * 退款回调结果实体
 */
@Data
public class RefundCallbackResult {
    private boolean success;        // 处理是否成功
    private String msg;             // 结果描述
    private String orderNo;         // 原订单号
    private String refundNo;        // 退款订单号
    private String refundId;        // 微信退款ID
    private String totalFee;        // 原订单金额(分)
    private String refundFee;       // 退款金额(分)
    private String refundStatus;    // 退款状态
    // 成功响应
    public static RefundCallbackResult success() {
        return success("处理成功");
    }
    public static RefundCallbackResult success(String msg) {
        RefundCallbackResult result = new RefundCallbackResult();
        result.setSuccess(true);
        result.setMsg(msg);
        return result;
    }
    // 失败响应
    public static RefundCallbackResult fail(String msg) {
        RefundCallbackResult result = new RefundCallbackResult();
        result.setSuccess(false);
        result.setMsg(msg);
        return result;
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/wx/WechatPayConfig.java
New file
@@ -0,0 +1,27 @@
package com.ruoyi.system.wx;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
 * 微信支付配置类
 */
@Data
@Component
@ConfigurationProperties(prefix = "wx")
public class WechatPayConfig {
    // 小程序APPID
    private String appId;
    // 商户号
    private String mchId;
    // 商户API密钥
    private String key;
    // 支付结果通知地址
    private String callbackPath;
    // 证书路径
    private String certPath;
    // 商户RAS加密公钥路径
    private String RASPath;
    private String wxAppletsAppSecret;
}
ruoyi-system/src/main/java/com/ruoyi/system/wx/WechatPayService.java
New file
@@ -0,0 +1,726 @@
package com.ruoyi.system.wx;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.core.domain.R;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.dom4j.DocumentException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import sun.security.util.DerInputStream;
import sun.security.util.DerValue;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
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 java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.RSAPublicKeySpec;
import java.util.*;
/**
 * 微信支付服务类
 */
@Service
public class WechatPayService {
    @Autowired
    private WechatPayConfig wechatPayConfig;
    private static final String RSA_PUBLIC_KEY_FILENAME = "wechat_rsa_public_key.pem";
    private static final String CERT_FOLDER = "cert/";
    /**
     * 统一下单
     * @param orderNumber 订单号
     * @param totalFee 总金额(分)
     * @param body 商品描述
     * @param openid 用户openid
     * @return 预支付订单信息
     */
    public R unifiedOrder(String orderId,String orderNumber, String totalFee, String body, String openid, String callbackPath) throws Exception {
        int i = new BigDecimal(totalFee).multiply(new BigDecimal("100")).intValue();
        String hostAddress = null;
        try {
            hostAddress = InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        // 构建请求参数
        Map<String, String> params = new HashMap<>();
        params.put("appid", wechatPayConfig.getAppId());
        params.put("mch_id", wechatPayConfig.getMchId());
        params.put("nonce_str", generateNonceStr());
        params.put("body", body);
        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);
        // 生成签名
        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("微信支付签名验证失败");
        }
        if (!resultMap.get("return_code").equals("SUCCESS")) {
            return R.fail(resultMap.get("return_msg"));
        }
        // 构建小程序支付所需参数
        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");
        payParams.put("orderId", orderId);
        return R.ok(JSON.toJSONString(payParams));
    }
    /**
     * 微信下单的签名算法
     * @param map
     * @return
     */
    private String weixinSignature(Map<String, String> map){
        try {
            Set<Map.Entry<String, String>> entries = map.entrySet();
            List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(entries);
            // 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)
            Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>() {
                public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
                    return (o1.getKey()).toString().compareTo(o2.getKey());
                }
            });
            // 构造签名键值对的格式
            StringBuilder sb = new StringBuilder();
            for (Map.Entry<String, String> 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=" + wechatPayConfig.getKey());
            String sign = MD5AndKL.MD5Encode(sb.toString(), "UTF-8").toUpperCase(); //注:MD5签名方式
            return sign;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 处理支付结果通知
     * @param request HTTP请求
     * @return 处理结果
     */
    public PayResult processNotify(HttpServletRequest request) throws Exception {
        // 读取请求内容
        BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8));
        String line;
        StringBuilder xml = new StringBuilder();
        while ((line = br.readLine()) != null) {
            xml.append(line);
        }
        br.close();
        // 解析XML
        Map<String, String> resultMap = XMLUtil.xmlToMap(xml.toString());
        // 验证签名
        if (!verifySign(resultMap, wechatPayConfig.getKey())) {
            throw new Exception("微信支付签名验证失败");
        }
        // 验证支付结果
        if (!"SUCCESS".equals(resultMap.get("return_code")) || !"SUCCESS".equals(resultMap.get("result_code"))) {
            throw new Exception("微信支付结果异常");
        }
        // 处理业务逻辑
        PayResult payResult = new PayResult();
        payResult.setOrderNumber(resultMap.get("out_trade_no"));
        payResult.setTransactionId(resultMap.get("transaction_id"));
        payResult.setTotalFee(resultMap.get("total_fee"));
        return payResult;
    }
    /**
     * 生成随机字符串
     */
    private String generateNonceStr() {
        return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
    }
    /**
     * 微信支付API V2签名算法
     * @param params 请求参数
     * @param apiKey 商户API密钥
     * @return 签名结果
     */
    public static String generateSign(Map<String, String> params, String apiKey) throws Exception {
        // 1. 过滤空值参数
        Map<String, String> filteredParams = new HashMap<>();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            // 排除sign字段和空值字段
            if (!"sign".equals(key) && value != null && !value.isEmpty()) {
                filteredParams.put(key, value);
            }
        }
        // 2. 按照ASCII码排序参数名
        List<String> keys = new ArrayList<>(filteredParams.keySet());
        Collections.sort(keys);
        // 3. 构建签名原始字符串
        StringBuilder sb = new StringBuilder();
        for (String key : keys) {
            String value = filteredParams.get(key);
            sb.append(key).append("=").append(value).append("&");
        }
        // 4. 添加API密钥
        sb.append("key=").append(apiKey);
        // 5. MD5加密并转为大写
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] digest = md.digest(sb.toString().getBytes("UTF-8"));
        // 6. 转换为十六进制字符串
        StringBuilder sign = new StringBuilder();
        for (byte b : digest) {
            sign.append(String.format("%02x", b & 0xff));
        }
        return sign.toString().toUpperCase();
    }
    /**
     * 验证签名
     */
    private boolean verifySign(Map<String, String> params, String key) throws Exception {
        String sign = params.get("sign");
        if (StringUtils.isEmpty(sign)) {
            return false;
        }
        // 移除sign字段
        Map<String, String> newParams = new HashMap<>(params);
        newParams.remove("sign");
        // 生成新签名
        String newSign = generateSign(newParams, key);
        return sign.equals(newSign);
    }
    /**
     * 关闭订单
     */
    public Map<String, String> closeOrder(String orderId){
        // 构建请求参数
        Map<String, String> params = new HashMap<>();
        params.put("appid", wechatPayConfig.getAppId());
        params.put("mch_id", wechatPayConfig.getMchId());
        params.put("nonce_str", generateNonceStr());
        params.put("out_trade_no", orderId);
        // 生成签名
        String sign = weixinSignature(params);
        params.put("sign", sign);
        // 将参数转换为XML
        String xmlParams = XMLUtil.mapToXml(params);
        // 发送请求到微信支付关闭订单接口
        String url = "https://api.mch.weixin.qq.com/pay/closeorder";
        String result = null;
        try {
            result = HttpUtil.post(url, xmlParams);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        // 解析返回结果
        try {
            return XMLUtil.xmlToMap(result);
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 查询订单
     */
    public Map<String, String> queryOrder(String orderId) throws Exception {
        // 构建请求参数
        Map<String, String> params = new HashMap<>();
        params.put("appid", wechatPayConfig.getAppId());
        params.put("mch_id", wechatPayConfig.getMchId());
        params.put("nonce_str", generateNonceStr());
        params.put("out_trade_no", orderId);
        // 生成签名
        String sign = generateSign(params, wechatPayConfig.getKey());
        params.put("sign", sign);
        // 将参数转换为XML
        String xmlParams = XMLUtil.mapToXml(params);
        // 发送请求到微信支付查询订单接口
        String url = "https://api.mch.weixin.qq.com/pay/orderquery";
        String result = HttpUtil.post(url, xmlParams);
        // 解析返回结果
        return XMLUtil.xmlToMap(result);
    }
    /**
     * 申请退款 - 使用证书
     */
    public Map<String, String>  refund(String orderNo, String refundNo, String totalFee, String refundFee, String refundDesc,String callbackPath) {
        int i = new BigDecimal(totalFee).multiply(new BigDecimal("100")).intValue();
        int j = new BigDecimal(refundFee).multiply(new BigDecimal("100")).intValue();
        try {
            // 构建请求参数
            Map<String, String> params = new HashMap<>();
            params.put("appid", wechatPayConfig.getAppId());
            params.put("mch_id", wechatPayConfig.getMchId());
            params.put("nonce_str", UUID.randomUUID().toString().replaceAll("-", ""));
            params.put("out_trade_no", orderNo);
            params.put("out_refund_no", refundNo);
            params.put("total_fee", String.valueOf(i));
            params.put("refund_fee", String.valueOf(j));
            params.put("refund_desc", refundDesc);
            params.put("notify_url", wechatPayConfig.getCallbackPath() + callbackPath); // 退款结果
            // 生成签名
            String sign = weixinSignature(params);
            params.put("sign", sign);
            // 转换为XML
            String xmlParams = XMLUtil.mapToXml(params);
            // 使用证书发送请求
            String result = postWithCert("https://api.mch.weixin.qq.com/secapi/pay/refund", xmlParams);
            // 解析结果
            Map<String, String> resultMap = XMLUtil.xmlToMap(result);
            System.out.println("申请退款结果"+resultMap);
            // 验证签名
            if (!verifySign(resultMap, wechatPayConfig.getKey())) {
                resultMap.put("return_code","FAILED");
                resultMap.put("return_msg","申请退款结果签名验证失败");
                return resultMap;
            }
            return resultMap;
        } catch (Exception e) {
            Map<String, String> resultMap=new HashMap<>();
            resultMap.put("return_code","FAILED");
            resultMap.put("return_msg","申请退款失败");
            return resultMap;
        }
    }
    /**
     * 使用证书发送请求
     */
    private String postWithCert(String url, String xmlData) throws Exception {
        // 证书类型为PKCS12
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        // 获取证书路径
        String certPath = wechatPayConfig.getCertPath();
        // 如果是classpath路径,使用ClassLoader加载
        if (certPath.startsWith("classpath:")) {
            String path = certPath.substring("classpath:".length());
            try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(path)) {
                if (inputStream == null) {
                    throw new FileNotFoundException("证书文件不存在: " + path);
                }
                keyStore.load(inputStream, wechatPayConfig.getMchId().toCharArray());
            }
        } else {
            // 传统文件路径
            try (FileInputStream inputStream = new FileInputStream(new File(certPath))) {
                keyStore.load(inputStream, wechatPayConfig.getMchId().toCharArray());
            }
        }
        // 实例化密钥库 & 初始化密钥工厂
        SSLContext sslContext = SSLContext.getInstance("TLS");
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(keyStore, wechatPayConfig.getMchId().toCharArray());
        sslContext.init(kmf.getKeyManagers(), null, new SecureRandom());
        // 创建HttpsURLConnection对象
        URL httpsUrl = new URL(url);
        HttpsURLConnection conn = (HttpsURLConnection) httpsUrl.openConnection();
        conn.setSSLSocketFactory(sslContext.getSocketFactory());
        conn.setDoOutput(true);
        conn.setDoInput(true);
        conn.setUseCaches(false);
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        // 发送请求
        OutputStream outputStream = conn.getOutputStream();
        outputStream.write(xmlData.getBytes("UTF-8"));
        outputStream.flush();
        outputStream.close();
        // 获取响应
        InputStream inputStream = conn.getInputStream();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
        StringBuilder result = new StringBuilder();
        String line;
        while ((line = bufferedReader.readLine()) != null) {
            result.append(line);
        }
        bufferedReader.close();
        inputStream.close();
        conn.disconnect();
        return result.toString();
    }
    /**
     * 处理退款回调
     */
    public RefundCallbackResult processRefundCallback(String xmlData) {
        try {
            // 1. 解析回调XML数据
            if (StringUtils.isEmpty(xmlData)) {
                return RefundCallbackResult.fail("回调数据为空");
            }
            //2.解析参数
            System.out.println(xmlData);
            System.out.println("----------------------------------------");
            Map<String, String> resultMap = XMLUtil.xmlToMap(xmlData);
            System.out.println(resultMap.get("req_info"));
            // 3. 检查返回状态
            String returnCode = resultMap.get("return_code");
            if (!"SUCCESS".equals(returnCode)) {
                String errMsg = resultMap.get("return_msg");
                return RefundCallbackResult.fail("通信失败:" + errMsg);
            }
            //4 使用商户API密钥解密req_info(AES-256-CBC算法)
            String decryptData = decrypt(resultMap.get("req_info"), wechatPayConfig.getKey());
            Map<String, String> refundDetail = XMLUtil.xmlToMap(decryptData);
            // 4. 提取退款信息
            String orderNo = refundDetail.get("out_trade_no");        // 原订单号
            String refundNo = refundDetail.get("out_refund_no");      // 退款订单号
            String refundId = refundDetail.get("refund_id");          // 微信退款ID
            System.err.println("退款回调成功,订单号:"+orderNo+",退款号:"+refundNo+",状态:{}"+refundId);
            RefundCallbackResult refundCallbackResult = RefundCallbackResult.success();
            refundCallbackResult.setOrderNo(orderNo);
            refundCallbackResult.setRefundNo(refundId);
            return refundCallbackResult;
        } catch (Exception e) {
            return RefundCallbackResult.fail("系统异常:" + e.getMessage());
        }
    }
    private static String wxDecrypt(String req_info, String key)  throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException,
            InvalidKeyException, BadPaddingException, IllegalBlockSizeException{
        byte[] decode = Base64.getDecoder().decode(req_info);
        System.out.println(Arrays.toString(decode));
        String sign = MD5AndKL.MD5Encode(key, "UTF-8").toLowerCase();
        System.out.println(sign);
        if (Security.getProvider("BC") == null){
            Security.addProvider(new BouncyCastleProvider());
        }
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
        SecretKeySpec secretKeySpec = new SecretKeySpec(sign.getBytes(), "AES");
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
        return new String(cipher.doFinal(decode));
    }
    /**
     * 获取RSA加密公钥
     */
    public String getRsaPublicKey() {
        int maxRetries = 3;
        for (int retryCount = 0; retryCount < maxRetries; retryCount++) {
            try {
                System.out.println("尝试获取RSA公钥,第" + (retryCount + 1) + "次");
                // 构建请求参数
                Map<String, String> params = new HashMap<>();
                params.put("mch_id", wechatPayConfig.getMchId());
                params.put("sign_type", "MD5");
                params.put("nonce_str", generateNonceStr());
                // 生成签名
                String sign = weixinSignature(params);
                params.put("sign", sign);
                // 转换为XML
                String xmlParams = XMLUtil.mapToXml(params);
                // 打印请求参数
                System.out.println("请求参数: " + xmlParams);
                // 使用证书发送请求(关键修改:使用postWithCert而非普通HTTP请求)
                String result = postWithCert("https://fraud.mch.weixin.qq.com/risk/getpublickey", xmlParams);
                // 打印响应结果
                System.out.println("响应结果: " + result);
                // 解析结果
                Map<String, String> resultMap = XMLUtil.xmlToMap(result);
                System.out.println("获取RSA公钥结果: " + resultMap);
                // 检查返回状态
                if (!"SUCCESS".equals(resultMap.get("return_code"))) {
                    throw new Exception("RSA公钥获取失败: " + resultMap.get("return_msg"));
                }
                // 保存公钥到本地文件
                savePublicKeyToClasspath(resultMap.get("pub_key"));
                return resultMap.get("pub_key");
            } catch (Exception e) {
                System.err.println("获取RSA公钥异常: " + e.getMessage() + ", 重试次数: " + (retryCount + 1));
                e.printStackTrace();
                // 如果是最后一次重试,抛出异常
                if (retryCount == maxRetries - 1) {
                    return null;
                }
                // 重试前等待一段时间
                try {
                    Thread.sleep(1000 * (retryCount + 1)); // 指数退避
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    return null;
                }
            }
        }
        return null;
    }
    /**
     * 保存RSA公钥到文件夹
     * @param publicKey RSA公钥内容
     */
    private static void savePublicKeyToClasspath(String publicKey) throws IOException {
        // 创建 cert 目录(如果不存在)
        Path certDir = Paths.get(CERT_FOLDER);
        if (!Files.exists(certDir)) {
            Files.createDirectories(certDir); // 自动创建目录
        }
        // 写入公钥文件(UTF-8 编码)
        Path keyFile = certDir.resolve(RSA_PUBLIC_KEY_FILENAME);
        Files.write(keyFile, publicKey.getBytes(StandardCharsets.UTF_8));
    }
    /**
     * 从文件夹加载RSA公钥
     * @return RSA公钥内容
     */
    private static String loadPublicKeyFromClasspath() throws IOException {
        Path keyFile = Paths.get(CERT_FOLDER + RSA_PUBLIC_KEY_FILENAME);
        byte[] bytes = Files.readAllBytes(keyFile); // 读取所有字节
        return new String(bytes, StandardCharsets.UTF_8); // 转为UTF-8字符串
    }
    /**
     * 加载公钥 返回PublicKey对象
     */
    public static PublicKey loadPublicKey(String pemContent) throws Exception {
        // 读取PEM文件内容
//        String pemContent = new String(Files.readAllBytes(Paths.get(CERT_FOLDER + RSA_PUBLIC_KEY_FILENAME)), StandardCharsets.UTF_8);
        // 移除PEM头尾标记
        String publicKeyPEM = pemContent
                .replace("-----BEGIN RSA PUBLIC KEY-----", "")
                .replace("-----END RSA PUBLIC KEY-----", "")
                .replaceAll("\\s", ""); // 去除换行/空格
        // 解码Base64
        byte[] encoded = Base64.getDecoder().decode(publicKeyPEM);
        // 手动解析PKCS#1格式
        DerInputStream derReader = new DerInputStream(encoded);
        DerValue[] seq = derReader.getSequence(0);
        BigInteger modulus = seq[0].getBigInteger();
        BigInteger exponent = seq[1].getBigInteger();
        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, exponent);
        return KeyFactory.getInstance("RSA").generatePublic(keySpec);
    }
    /**
     * 使用RSA-OAEP加密数据
     * @param plaintext 待加密的明文
     * @return Base64编码的密文
     */
    public static String encrypt(String plaintext, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encryptedBytes);
    }
    /**
     * 商户付款到银行卡(优先使用本地保存的公钥)
     * @param partnerTradeNo 商户订单号
     * @param bankNo 银行卡号
     * @param trueName 银行卡真实姓名
     * @param bankCode 银行编码
     * @param amount 金额(分)
     * @param desc 付款说明
     * @return 付款结果
     */
    public Map<String, String> payToBankCard(String partnerTradeNo, String bankNo, String trueName,
                                             String bankCode, BigDecimal amount, String desc) throws Exception {
        int i = amount.multiply(new BigDecimal("100")).intValue();
        // 1. 尝试从本地加载RSA公钥
        String pubKey = loadPublicKeyFromClasspath();
        // 2. 如果本地没有公钥或公钥无效,则从微信获取新公钥
        if (pubKey == null || pubKey.isEmpty()) {
            pubKey = getRsaPublicKey();
        }
        //公钥对象
        PublicKey publicKey = loadPublicKey(pubKey);
        // 3. 使用RSA公钥加密银行卡号和真实姓名
        String encryptedBankNo = encrypt(bankNo, publicKey);
        String encryptedTrueName = encrypt(trueName, publicKey);
        // 4. 构建请求参数
        Map<String, String> params = new HashMap<>();
        params.put("mch_id", wechatPayConfig.getMchId());
        params.put("partner_trade_no", partnerTradeNo);
        params.put("enc_bank_no", encryptedBankNo);
        params.put("enc_true_name", encryptedTrueName);
        params.put("bank_code", bankCode);
        params.put("amount", String.valueOf(i));
        params.put("desc", desc);
        params.put("nonce_str", generateNonceStr());
        // 生成签名
        String sign = weixinSignature(params);
        params.put("sign", sign);
        // 将参数转换为XML
        String xmlParams = XMLUtil.mapToXml(params);
        // 5. 发送请求到微信支付付款到银行卡接口
        String url = "https://api.mch.weixin.qq.com/mmpaysptrans/pay_bank";
        String result = postWithCert(url, xmlParams);
        // 解析返回结果
        Map<String, String> resultMap = XMLUtil.xmlToMap(result);
        // 验证签名
        if (!verifySign(resultMap, wechatPayConfig.getKey())) {
            throw new Exception("付款到银行卡签名验证失败");
        }
        return resultMap;
    }
    public static void main(String[] args) throws IOException {
        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";
        String key="fD0JzscfMf295SYtRK3MnPRjSCA4Gahr";
        try {
            String decrypted = decrypt(info, key);
            System.out.println("解密结果: " + decrypted);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    public static String decrypt(String encryptedStringA, String merchantKey) throws Exception {
        // 1. 对加密串A做base64解码,得到加密串B
        byte[] decode = Base64.getDecoder().decode(encryptedStringA);
        // 2. 对商户key做md5,得到32位小写key*
        String sign = MD5AndKL.MD5Encode(merchantKey, "UTF-8").toLowerCase();
        // 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-system/src/main/java/com/ruoyi/system/wx/XMLUtil.java
New file
@@ -0,0 +1,52 @@
package com.ruoyi.system.wx;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
 * XML工具类
 */
public class XMLUtil {
    /**
     * 将Map转换为XML字符串
     */
    public static String mapToXml(Map<String, String> params) {
        Document document = DocumentHelper.createDocument();
        // 禁用XML声明
        document.setXMLEncoding(null);  // 关键设置
        Element root = document.addElement("xml");
        for (Map.Entry<String, String> entry : params.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue() != null ? entry.getValue() : "";
            root.addElement(key).setText(value);
        }
        return document.asXML();
    }
    /**
     * 将XML字符串转换为Map
     */
    public static Map<String, String> xmlToMap(String xmlStr) throws DocumentException {
        Map<String, String> map = new HashMap<>();
        Document document = DocumentHelper.parseText(xmlStr);
        Element root = document.getRootElement();
        for (Iterator<?> iterator = root.elementIterator(); iterator.hasNext();) {
            Element element = (Element) iterator.next();
            // 关键修改:获取元素内所有内容(包括CDATA)
            String value = element.getStringValue(); // 自动处理CDATA
            map.put(element.getName(), value);
        }
        return map;
    }
}
ruoyi-system/src/main/resources/cert/apiclient_cert.p12
Binary files differ
ruoyi-system/src/main/resources/cert/apiclient_cert.pem
New file
@@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIEJDCCAwygAwIBAgIUPzcz2XhuGAdLsR4fKcRiVtSVTEkwDQYJKoZIhvcNAQEL
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
Q0EwHhcNMjUwNjIzMDkwNDI4WhcNMzAwNjIyMDkwNDI4WjB+MRMwEQYDVQQDDAox
NzIwNTUyNjk4MRswGQYDVQQKDBLlvq7kv6HllYbmiLfns7vnu58xKjAoBgNVBAsM
IeWbm+W3neS4gOivgeaIkOenkeaKgOaciemZkOWFrOWPuDELMAkGA1UEBhMCQ04x
ETAPBgNVBAcMCFNoZW5aaGVuMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAu5sppa1rG+Xc0XPW78kKw0Bq+oYjuzEfjh3pwurmwrOsqmPaQj4aHXS8M2eW
nRG6oP4BR3dQnphDRCGTdBoVTHOrNJt3xBJ+iSkbnQLH7aHhymyZxo5bZ7q9LnzZ
SMqojYJ9CoDeriubrNWR5pXTB02xJsSPXKo4KwrJIgUQJ0bc2EuyngwZ5rJKeetW
yA7Pw4NZ5X3X0iBXTfhItaW1RLcBegny+K14AneYjpvAqMt3ynV6xQqvl3s0dCDr
8Bo33ds0vi1dr40gM7HvBaQ2oMXIvVdl4xX1H4OM84JwVOKyG/hTC9PWIznxV+es
vDKenuBhRJ0I1N/fy7q0aYDiIwIDAQABo4G5MIG2MAkGA1UdEwQCMAAwCwYDVR0P
BAQDAgP4MIGbBgNVHR8EgZMwgZAwgY2ggYqggYeGgYRodHRwOi8vZXZjYS5pdHJ1
cy5jb20uY24vcHVibGljL2l0cnVzY3JsP0NBPTFCRDQyMjBFNTBEQkMwNEIwNkFE
Mzk3NTQ5ODQ2QzAxQzNFOEVCRDImc2c9SEFDQzQ3MUI2NTQyMkUxMkIyN0E5RDMz
QTg3QUQxQ0RGNTkyNkUxNDAzNzEwDQYJKoZIhvcNAQELBQADggEBAAiJH9rQLYd9
3OX8FDxGLetiUd8tIS+Y3fjZhxYiGTF6wz6mR5L3RMYqxZcbb2OWFi1dzYQ97lrY
1spt4+1a5pjeMNlEX9hPUVriWTDN0YhNIvQ2vQ3IbXgWTve0cuwSfc/cyWqhdSCq
7V7jzVs+LRewXsvUyXgqG3Tshzadhi6CL1njAa3PUcSkzgLHKptaD/3irx/nniLB
j6ktq7e/0Orj3BJYQueYtA0LZrk3OacnbDiIeHgw6qLGounfSHbuIWSSLUa6ghW9
JovmWWENapQF7kT6kh+FalMi0au1xpgKnEgRDjuVyXt9PfLWi+2GWKtsy2gLCzq7
wS2ob3v4ey8=
-----END CERTIFICATE-----
ruoyi-system/src/main/resources/cert/apiclient_key.pem
New file
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC7mymlrWsb5dzR
c9bvyQrDQGr6hiO7MR+OHenC6ubCs6yqY9pCPhoddLwzZ5adEbqg/gFHd1CemENE
IZN0GhVMc6s0m3fEEn6JKRudAsftoeHKbJnGjltnur0ufNlIyqiNgn0KgN6uK5us
1ZHmldMHTbEmxI9cqjgrCskiBRAnRtzYS7KeDBnmskp561bIDs/Dg1nlfdfSIFdN
+Ei1pbVEtwF6CfL4rXgCd5iOm8Coy3fKdXrFCq+XezR0IOvwGjfd2zS+LV2vjSAz
se8FpDagxci9V2XjFfUfg4zzgnBU4rIb+FML09YjOfFX56y8Mp6e4GFEnQjU39/L
urRpgOIjAgMBAAECggEBAIn+kd3J97UnmQt/16O9D2ubYJUyQeoZMgrJOf8j5eVl
YCZdGFgNcgwkp6JwGbsjJVHyRGKbAvQFL8VXpSyLWwtHMbGagbf5mpWtZSBSwVir
8HeC9oG0iMt98lXVg8YlO2ILj4pATIt0jCfPWGhx4C3JGftYLdONaY9Pea7KZp1c
IgOsXA84+7V+ndyOTDbXTot6UOjOccwfQ3qUHDtCWmGoauoO0ftCqMa4SxADKiMg
auQup9wYpApnqvIZuD2TzXUCGjXxmfTYM9pesrq11zWH2u7vJ6fbFLlM+zKgmxkp
lqIWChDpzMrUjMaAW4ufIN/ekuGMdAyYKGET3pK5F2ECgYEA2zLGzwB6BZ87ponR
bcULQCQ6dsZt6X+dTSTO6Qopsabcdxtqb5W0iopMSVK18byV6eVZxYIUaHnATIry
KKpLEM4N0ghjG6e0zv2yGEXfdEnmGAy/HPaRh2osPT9zXA+Kofliv9MUbIb4h+bl
EtGOsIlp/juUOoBPh3kGAUkvOAsCgYEA2xqKS/LaNnHUR38SHvCJKAJlZxqFFAuf
AdL2jCo5BKsFAt0BOhx/gdaw7z0uf4oGYs1RxOcF9Cun6IHC+mvM8tKrbyL0Xv3x
/syEuVpjJtxSctQUVgOq14jqJSsBZpMr8bJhK3sAs7BoPqIFPCoTljtGxL+lb+9o
VIkQMQvzFUkCgYBC2zDiMD4HevLBr+Vtupzc2m/ujEU349GBlwl48L2YztENjxIe
qKWhX982ugB+k8uRxH9mC2/YEdKCRhroJlCw9ngmqKI57CPswlpQpvcpy+RfT3v3
BImMqOiHYdP4r1DUaHEjGulu1Jf3M4iI/dusoqzuexOR6FLZeiNHvx+k0wKBgFD2
YK4p4hqGO3Pj77VBGt2O7RuJQC2lEa+h3frzSzh/XY2uTtrDVxaKV70/tWPpg+yU
hCenWprcWuJeuMbCeOwtjq21k+laG0cVGeJIFSVx+oF1/sG9/y4/6I2tc4oMzgs0
6BUiEaNUh2DFi/usbKpx4vem/aH8LGg4caQ+DygJAoGBAJsZ265oRTC2LshrGVeD
yXCmTs4DjkK7EXgMrl8OyIymG64XdEUnpnN50dvFYnW2a7jlUhzBcwVAf4/gJ0k4
gfymOwkk9dsAUEkuiWXNVpUFi1Fdf39ADqeh3kwRpbBnOv3bwJ7h+i8i590TIGNK
RINSd54kN0iLwgVC5/d+lSv1
-----END PRIVATE KEY-----
ruoyi-system/src/main/resources/mapper/system/AppUserMapper.xml
@@ -19,8 +19,9 @@
        left join (select user_id, count(1) as num from tb_order  where  status = 6  group by user_id) buy on tbu.id = buy.user_id
        left join (select c.user_id,count(o.company_id) as num from tb_company c left join tb_order o on c.id=o.company_id and o.status=6  group by c.user_id) sell on tbu.id = sell.user_id
        where tbu.is_delete =0
        <if test="null != dto.username and dto.username != '' ">
            and tbu.user_name like concat('%',#{dto.username},'%')
            and tbu.status != 3
        <if test="null != dto.userName and dto.userName != '' ">
            and tbu.user_name like concat('%',#{dto.userName},'%')
        </if>
        <if test="null != dto.phone and dto.phone != '' ">
            and tbu.phone like concat('%',#{dto.phone},'%')
ruoyi-system/src/main/resources/mapper/system/OrderMapper.xml
@@ -7,86 +7,84 @@
        select  COALESCE(SUM(commission_price), 0.00) AS total_commission from tb_order where status in (4,5) and share_user_id =#{id}
    </select>
    <select id="getOrderPage" resultType="com.ruoyi.system.pojo.vo.OrderPageVO">
        select
            c.id,
            o.order_no,
            publish.user_name as publishName,
            publish.phone as publishPhone,
            o.price,
            book.user_name as bookName,
            book.phone as bookPhone,
            c.create_time as createTime,
            o.create_time as bookTime,
            o.finish_time as finishTime,
            -- 状态映射逻辑
        SELECT
        c.id,
        o.order_no,
        publish.user_name AS publishName,
        publish.phone AS publishPhone,
        o.price,
        book.user_name AS bookName,
        book.phone AS bookPhone,
        c.create_time AS createTime,
        o.create_time AS bookTime,
        o.finish_time AS finishTime,
        -- 状态映射逻辑
        CASE
        WHEN c.status = -1 THEN -1
        WHEN c.status = 1 THEN 1
        WHEN c.status = 2 THEN 7
        WHEN c.status = 3 THEN 6
        WHEN c.status = 4 THEN
        CASE
        WHEN o.status = 1 THEN 1
        WHEN o.status = 2 THEN 2
        WHEN o.status = 3 THEN 3
        WHEN o.status IN (4, 5) THEN 4
        WHEN o.status = 6 THEN 6
        ELSE c.status
        END
        ELSE c.status
        END AS status
        FROM
        tb_company c
        LEFT JOIN tb_order o ON o.company_id = c.id AND o.status != -1
        LEFT JOIN (SELECT id, user_name, phone FROM tb_user GROUP BY id) publish ON c.user_id = publish.id
        LEFT JOIN (SELECT id, user_name, phone FROM tb_user GROUP BY id) book ON o.user_id = book.id
        WHERE
        c.is_delete = 0
        <if test="null != dto.orderNo">
            AND o.order_no LIKE CONCAT('%', #{dto.orderNo}, '%')
        </if>
        <if test="null != dto.publishName and '' != dto.publishName">
            AND publish.user_name LIKE CONCAT('%', #{dto.publishName}, '%')
        </if>
        <if test="null != dto.publishPhone and '' != dto.publishPhone">
            AND publish.phone LIKE CONCAT('%', #{dto.publishPhone}, '%')
        </if>
        <if test="null != dto.bookName and '' != dto.bookName">
            AND book.user_name LIKE CONCAT('%', #{dto.bookName}, '%')
        </if>
        <if test="null != dto.bookPhone and '' != dto.bookPhone">
            AND book.phone LIKE CONCAT('%', #{dto.bookPhone}, '%')
        </if>
        <if test="null != dto.createTimeStart and null != dto.createTimeEnd">
            AND c.create_time BETWEEN #{dto.createTimeStart} AND #{dto.createTimeEnd}
        </if>
        <if test="null != dto.bookTimeStart and null != dto.bookTimeEnd">
            AND o.create_time BETWEEN #{dto.bookTimeStart} AND #{dto.bookTimeEnd}
        </if>
        <if test="null != dto.finishTimeStart and null != dto.finishTimeEnd">
            AND o.finish_time BETWEEN #{dto.finishTimeStart} AND #{dto.finishTimeEnd}
        </if>
        <if test="null != dto.status">
            AND (
            -- 直接匹配原始状态
            (c.status IN (1,2,3) AND
            CASE
                WHEN c.status = 1 THEN 1
                WHEN c.status = 2 THEN 6
                WHEN c.status = 3 THEN 5
                WHEN c.status = 4 THEN
                    CASE
                        WHEN o.status = 2 THEN 2
                        WHEN o.status = 3 THEN 3
                        WHEN o.status IN (4, 5) THEN 4
                        ELSE c.status  -- 默认保持原状态(可根据需求调整)
                        END
                ELSE c.status  -- 其他状态保持不变
                END AS status
        from
            tb_company c
        left join tb_order o on c.id = o.company_id and o.status != -1
        left join (select id,user_name,phone from tb_user group by id) publish on c.user_id = publish.id
        left join (select id,user_name,phone from tb_user group by id) book on o.user_id = book.id
        where
            c.is_delete=0
            <if test="null != dto.orderNo">
                and o.order_no like concat('%',#{dto.orderNo},'%')
            </if>
            <if test="null != dto.publishName and '' != dto.publishName">
                and publish.user_name like concat('%',#{dto.publishName},'%')
            </if>
            <if test="null != dto.publishPhone and '' != dto.publishPhone">
                and publish.phone like concat('%',#{dto.publishPhone},'%')
            </if>
            <if test="null != dto.bookName and '' != dto.bookName">
                and book.user_name like concat('%',#{dto.bookName},'%')
            </if>
            <if test="null != dto.bookPhone and '' != dto.bookPhone">
                and book.phone like concat('%',#{dto.bookPhone},'%')
            </if>
            <if test="null != dto.createTimeStart and null != dto.createTimeEnd">
                and c.create_time between #{dto.createTimeStart} and  #{dto.createTimeEnd}
            </if>
            <if test="null != dto.bookTimeStart and null != dto.bookTimeEnd">
                and o.create_time between #{dto.bookTimeStart} and  #{dto.bookTimeEnd}
            </if>
            <if test="null != dto.finishTimeStart and null != dto.finishTimeEnd">
                and o.finish_time between #{dto.finishTimeStart} and  #{dto.finishTimeEnd}
            </if>
            <if test="null != dto.status">
                AND (
                <!-- 直接匹配原始状态 -->
                (c.status IN (1,2,3) AND
                CASE
                WHEN c.status = 1 THEN 1
                WHEN c.status = 2 THEN 6
                WHEN c.status = 3 THEN 5
                ELSE c.status
                END = #{dto.status})
                <!-- 匹配映射后的状态4(c.status=4且o.status=4/5) -->
                OR (c.status = 4 AND o.status IN (4,5) AND 4 = #{dto.status})
                <!-- 匹配映射后的状态2(c.status=4且o.status=2) -->
                OR (c.status = 4 AND o.status = 2 AND 2 = #{dto.status})
                <!-- 匹配映射后的状态3(c.status=4且o.status=3) -->
                OR (c.status = 4 AND o.status = 3 AND 3 = #{dto.status})
                )
            </if>
            WHEN c.status = 1 THEN 1
            WHEN c.status = 2 THEN 6
            WHEN c.status = 3 THEN 5
            ELSE c.status
            END = #{dto.status})
            -- 匹配映射后的状态4(c.status=4且o.status=4/5)
            OR (c.status = 4 AND o.status IN (4,5) AND 4 = #{dto.status})
            -- 匹配映射后的状态2(c.status=4且o.status=2)
            OR (c.status = 4 AND o.status = 2 AND 2 = #{dto.status})
            -- 匹配映射后的状态3(c.status=4且o.status=3)
            OR (c.status = 4 AND o.status = 3 AND 3 = #{dto.status})
            )
        order by c.create_time,o.create_time
        </if>
    </select>
    <select id="getDetailById" resultType="com.ruoyi.system.pojo.vo.OrderDetailVO">
@@ -95,8 +93,8 @@
            -- 状态映射逻辑
            CASE
                WHEN c.status = 1 THEN 1
                WHEN c.status = 2 THEN 6
                WHEN c.status = 3 THEN 5
                WHEN c.status = 2 THEN 7
                WHEN c.status = 3 THEN 6
                WHEN c.status = 4 THEN
                    CASE
                        WHEN o.status = 2 THEN 2
@@ -107,6 +105,7 @@
                ELSE c.status  -- 其他状态保持不变
                END AS status,
            o.id as orderId,
            c.company_name,
            c.establish_time,
            concat(c.city,c.province,c.area) as place,
@@ -144,8 +143,8 @@
                    (
                        CASE
                            WHEN c.status = 1 THEN 1
                            WHEN c.status = 2 THEN 6
                            WHEN c.status = 3 THEN 5
                            WHEN c.status = 2 THEN 7
                            WHEN c.status = 3 THEN 6
                            WHEN c.status = 4 THEN
                                CASE
                                    WHEN o.status = 2 THEN 2
@@ -204,6 +203,13 @@
        FROM tb_order
        WHERE status IN (4, 5, 6)
          AND is_refund = 0
        <if test="null != dto.orderNo and '' != dto.orderNo">
            and order_no LIKE CONCAT('%', #{dto.orderNo}, '%')
        </if>
        <if test="null != dto.operateTimeStart and null != dto.operateTimeEnd">
            and pay_time between #{dto.operateTimeStart} and #{dto.operateTimeEnd}
        </if>
        AND ( #{dto.type} IS NULL  OR 1 = #{dto.type} )
    </select>
    <select id="flowsPage" resultType="com.ruoyi.system.pojo.vo.FinanceFlowsPageVO">
        SELECT
@@ -225,7 +231,7 @@
            <if test="null != dto.operateTimeStart and null != dto.operateTimeEnd">
                and pay_time between #{dto.operateTimeStart} and #{dto.operateTimeEnd}
            </if>
        AND ( #{dto.type} IS NULL OR 1 = #{dto.type} )
        AND ( #{dto.type} IS NULL  OR 1 = #{dto.type} )
        UNION ALL
        SELECT
            order_no,
@@ -243,9 +249,9 @@
        <if test="null != dto.operateTimeStart and null != dto.operateTimeEnd">
            and refund_time between #{dto.operateTimeStart} and #{dto.operateTimeEnd}
        </if>
        AND ( #{dto.type} IS NULL OR 2 = #{dto.type} )
        AND ( #{dto.type} IS NULL  OR 2 = #{dto.type} )
        ORDER BY operateTime DESC
        LIMIT #{dto.pageNum}, #{dto.pageSize};
        LIMIT #{dto.offset}, #{dto.pageSize};
    </select>
    <select id="countFlowsPage" resultType="java.lang.Long">
        SELECT COUNT(*) FROM (
ruoyi-system/src/main/resources/mapper/system/QuestionMapper.xml
@@ -4,12 +4,12 @@
    <select id="getQuestionPage" resultType="com.ruoyi.system.pojo.vo.QuestionPageVO">
        select id,name ,order_num
        select id,title ,order_num
         from tb_question
         where
             del_flg=0
         <if test="null != name and '' != name">
             and name like concat('%',#{name},'%')
             del_flag=0
         <if test="null != title and '' != title">
             and title like concat('%',#{title},'%')
         </if>
    </select>
ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml
@@ -142,6 +142,21 @@
        where
            sm.parent_id = 0 order by sm.order_num     asc
    </select>
    <select id="selectMenusByUserId" resultType="com.ruoyi.common.core.domain.entity.SysMenu">
        select
            m.menu_id, m.parent_id
        from  sys_user_role ur
                  left join sys_role_menu rm on ur.role_id = rm.role_id
                  left join sys_menu m on rm.menu_id =  m.menu_id
        where
            ur.user_id=#{userId}
    </select>
    <select id="getPathsByMenuIds" resultType="java.lang.String">
        select path from sys_menu where menu_id in
        <foreach collection='ids' item='id' open='(' separator=',' close=')'>
            #{id}
        </foreach>
    </select>
    <update id="updateMenu" parameterType="SysMenu">
        update sys_menu
ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml
@@ -14,9 +14,12 @@
    </select>
    <select id="selectMenuIdsByRoleIds" resultType="java.lang.Long">
        select menu_id from sys_role_menu where role_id in
        <foreach collection="roleList" item="roleId" open="(" separator="," close=")">
            #{roleId}
        </foreach>
        <if test="roleList != null  and !roleList.isEmpty">
            <foreach collection="roleList" item="roleId" open="(" separator="," close=")">
                #{roleId}
            </foreach>
        </if>
    </select>
    <delete id="deleteRoleMenuByRoleId" parameterType="Long">
ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
@@ -123,7 +123,7 @@
    
    <select id="selectUserByUserName" parameterType="String" resultMap="SysUserResult">
        <include refid="selectUserVo"/>
        where u.user_name = #{userName} and u.del_flag = '0'
        where u.phonenumber = #{userName}
    </select>
    
    <select id="selectUserById" parameterType="Long" resultMap="SysUserResult">