luodangjia
2024-11-05 0c569ab4b38d972213eeaf7e8965f33f8ec650fd
11.5
19个文件已修改
12个文件已添加
1256 ■■■■■ 已修改文件
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/factory/ExchangeDispatchFallbackFactory.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/factory/OrderFallbackFactory.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/feignClient/ExchangeDispatchClient.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/feignClient/OrderClient.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/Constants.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-admin/pom.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/config/WeChatConfig.java 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/ChangeDispatchController.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/FranchiseeController.java 434 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/OrderController.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/SiteController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/Franchisee.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/Site.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/TFranchiseeBalanceChange.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/TFranchiseeBalanceChangeMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/TFranchiseeBalanceChangeService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/TFranchiseeBalanceChangeServiceImpl.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/utils/MD5AndKL.java 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/utils/WeChatUtil.java 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/utils/WechatPaySignature.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/vo/InfoDto.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/vo/MoneyRecentQuery.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/vo/OrderReasinDto.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/vo/RencentBalance.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-admin/src/main/resources/template/加盟商余额记录.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/ChangeDispatchController.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/OrderController.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/request/ChangeDispatchRequest.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/resources/mapper/order/ChangeDispatchMapper.xml 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/controller/OrderController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/controller/OssController.java 136 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/factory/ExchangeDispatchFallbackFactory.java
@@ -61,6 +61,11 @@
            public R<Boolean> refuseChangeDispatch(String id) {
                return R.fail(cause.getMessage());
            }
            @Override
            public void changeReason(String id, String reason) {
            }
        };
    }
}
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/factory/OrderFallbackFactory.java
@@ -12,6 +12,7 @@
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/**
@@ -247,6 +248,11 @@
            }
            @Override
            public R<Boolean> updateArrivalTime(String orderId, Date arriveTime) {
                return null;
            }
            @Override
            public R<Boolean> changeOrderState(String orderId, Integer state) {
                return R.fail(cause.getMessage());
            }
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/feignClient/ExchangeDispatchClient.java
@@ -108,4 +108,6 @@
    @GetMapping(value = "changeDispatch/refuseChangeDispatch")
    R<Boolean> refuseChangeDispatch(@RequestParam("id") String id);
    @GetMapping(value = "changeDispatch/changeReason")
    void changeReason(@RequestParam("id")String id,@RequestParam("reason") String reason);
}
ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/feignClient/OrderClient.java
@@ -17,6 +17,7 @@
import org.springframework.web.bind.annotation.RequestParam;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/**
@@ -425,5 +426,6 @@
    R<String> oldData(@RequestParam("index") Integer index,
                      @RequestParam("pageNum") Integer pageNum,
                      @RequestParam("pageSize") Integer pageSize);
    @GetMapping(value = "/order/updateArrivalTime")
    R<Boolean> updateArrivalTime(@RequestParam("orderId")String orderId,@RequestParam("arriveTime")Date arriveTime);
}
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/Constants.java
@@ -146,6 +146,7 @@
     * 订单记录excel模板名称
     */
    public static final String EXCEL_ORDER_FILE_NAME = "订单记录";
    public static final String EXCEL_ORDER_FILE_NAME1 = "加盟商余额记录";
    /**
     * 用户编号生成前缀
ruoyi-service/ruoyi-admin/pom.xml
@@ -19,6 +19,12 @@
    <dependencies>
        <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-java</artifactId>
            <version>0.2.12</version>
        </dependency>
        <dependency>
            <groupId>org.openeuler</groupId>
            <artifactId>bgmprovider</artifactId>
            <version>1.0.4</version>
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/config/WeChatConfig.java
New file
@@ -0,0 +1,121 @@
package com.ruoyi.admin.config;
import javax.annotation.PostConstruct;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.java.service.refund.RefundService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.service.payments.h5.H5Service;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import lombok.Getter;
import java.security.PrivateKey;
/**
 * @desc: 微信config
 * @author: shy
 * @date: 2024/4/9 10:06
 */
@Configuration
@Getter
public class WeChatConfig {
    /**
     * 商户号
     */
    @Value("${wx.mchId}")
    public String merchantId;
    /**
     * 商户API私钥路径
     */
    @Value("${wx.privateKeyPath}")
    public String privateKeyPath;
    /**
     * 商户证书序列号
     */
    @Value("${wx.merchantSerialNumber}")
    public String merchantSerialNumber;
    /**
     * 商户APIV3密钥
     */
    @Value("${wx.apiV3Key}")
    public String apiV3Key;
    /**
     * AppId
     */
    @Value("${wx.appId}")
    public String appId;
    private Config config;
    @Getter
    private PrivateKey privateKey;
    @PostConstruct
    public void initConfig() {
        // 使用自动更新平台证书的RSA配置
        // 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错
        config = new RSAAutoCertificateConfig.Builder()
                .merchantId(merchantId)
                .privateKeyFromPath(privateKeyPath)
                .merchantSerialNumber(merchantSerialNumber)
                .apiV3Key(apiV3Key)
                .build();
    }
    @Primary
    @Bean()
    public H5Service h5Service() {
        return new H5Service.Builder()
                .config(config)
                .build();
    }
    @Primary
    @Bean()
    public JsapiService jsapiService() {
        return new JsapiService.Builder()
                .config(config)
                .build();
    }
    @Primary
    @Bean()
    public NativePayService nativePayService() {
        return new NativePayService.Builder()
                .config(config)
                .build();
    }
    @Primary
    @Bean()
    public RefundService refundService() {
        return new RefundService.Builder()
                .config(config)
                .build();
    }
    @Primary
    @Bean
    public NotificationParser notificationParser() {
        return new NotificationParser((NotificationConfig) config);
    }
    @Primary
    @Bean
    public PrivateKeySigner privateKeySigner() {
        return new PrivateKeySigner(merchantSerialNumber, privateKey);
    }
}
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/ChangeDispatchController.java
@@ -60,10 +60,11 @@
            @ApiImplicitParam(value = "师傅名称", name = "workerName", dataType = "String"),
            @ApiImplicitParam(value = "订单编号", name = "orderNumber", dataType = "String"),
            @ApiImplicitParam(value = "下单用户名称", name = "userName", dataType = "String"),
            @ApiImplicitParam(value = "地址", name = "reservationAddress", dataType = "String"),
            @ApiImplicitParam(value = "页码", name = "pageNum", dataType = "Integer", required = true),
            @ApiImplicitParam(value = "每页条数", name = "pageSize", dataType = "Integer", required = true)
    })
    public R<Page<ChangeDispatch>> queryPageList(String workerName, String orderNumber, String userName,
    public R<Page<ChangeDispatch>> queryPageList(String workerName, String orderNumber, String userName,String reservationAddress,
                                                 @RequestParam(name = "pageNum", defaultValue = "1") Integer pageNum,
                                                 @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
        LoginUser loginUser = tokenService.getLoginUser();
@@ -75,6 +76,7 @@
        changeDispatchRequest.setWorkerName(workerName);
        changeDispatchRequest.setOrderNumber(orderNumber);
        changeDispatchRequest.setUserName(userName);
        changeDispatchRequest.setReservationAddress(reservationAddress);
        changeDispatchRequest.setPageNum(pageNum);
        changeDispatchRequest.setPageSize(pageSize);
        changeDispatchRequest.setCityList(cityList);
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/FranchiseeController.java
@@ -1,29 +1,69 @@
package com.ruoyi.admin.controller;
import cn.hutool.http.HttpException;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alipay.api.AlipayApiException;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.admin.config.WeChatConfig;
import com.ruoyi.admin.entity.*;
import com.ruoyi.admin.service.*;
import com.ruoyi.admin.utils.MD5AndKL;
import com.ruoyi.admin.utils.WeChatUtil;
import com.ruoyi.admin.vo.InfoDto;
import com.ruoyi.admin.vo.MoneyRecentQuery;
import com.ruoyi.admin.vo.RencentBalance;
import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.exception.GlobalException;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.security.annotation.RequiresPermissions;
import com.ruoyi.common.security.service.TokenService;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.service.partnerpayments.app.model.Transaction;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com. wechat. pay. java. service. payments. jsapi. model. PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.Getter;
import org.apache.commons.codec.CharEncoding;
import org.apache.poi.util.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
 * <p>
@@ -50,6 +90,338 @@
    private MasterWorkerService masterWorkerService;
    @Resource
    private SiteService siteService;
    @Resource
    private JsapiService jsapiService;
    @Resource
    private WeChatConfig weChatConfig;
    @Resource
    private PrivateKeySigner privateKeySigner;
    @Resource
    private TFranchiseeBalanceChangeService balanceChangeService;
    private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final Random RANDOM = new SecureRandom();
    @ApiOperation(value = "加盟商列表余额", tags = {"后台2.0-加盟商列表余额"})
    @PostMapping(value = "/recent/money")
    public R<RencentBalance> recentmoney() {
        BigDecimal balance = new BigDecimal(0);
        List<Franchisee> list = franchiseeService.lambdaQuery().list();
        for (Franchisee franchisee : list) {
            balance = balance.add(franchisee.getBalance());
        }
        //充值的金额
        BigDecimal balance1 = new BigDecimal(0);
        List<TFranchiseeBalanceChange> list1 = balanceChangeService.lambdaQuery().eq(TFranchiseeBalanceChange::getType, 1).list();
        for (TFranchiseeBalanceChange franchiseeBalanceChange : list1) {
            balance1 = balance1.add(franchiseeBalanceChange.getAmount());
        }
        //扣除的金额
        BigDecimal balance2 = new BigDecimal(0);
        List<TFranchiseeBalanceChange> list2 = balanceChangeService.lambdaQuery().eq(TFranchiseeBalanceChange::getType, 2).list();
        for (TFranchiseeBalanceChange franchiseeBalanceChange : list1) {
            balance2 = balance2.add(franchiseeBalanceChange.getAmount());
        }
        RencentBalance balance3 = new RencentBalance();
        balance3.setBalance1(balance);
        balance3.setBalance2(balance1);
        balance3.setBalance3(balance2);
        return R.ok(balance3);
    }
    @ApiOperation(value = "扣款记录及充值记录", tags = {"后台2.0-统一充值扣款列表","师傅段2.0-统一充值扣款列表"})
    @PostMapping(value = "/recent/money/list")
    public R<Page<TFranchiseeBalanceChange>> recentmoneylist(@RequestBody MoneyRecentQuery moneyRecentQuery) {
        Page<TFranchiseeBalanceChange> page = balanceChangeService.lambdaQuery().eq(moneyRecentQuery.getType() != null, TFranchiseeBalanceChange::getType, moneyRecentQuery.getType())
                .eq(moneyRecentQuery.getFranchId() != null, TFranchiseeBalanceChange::getFranchiseeId, moneyRecentQuery.getFranchId())
                .eq(moneyRecentQuery.getFranchName() != null && !"".equals(moneyRecentQuery.getFranchName()), TFranchiseeBalanceChange::getFranchiseeName, moneyRecentQuery.getFranchName())
                .ge(moneyRecentQuery.getDate1() != null, TFranchiseeBalanceChange::getCreateTime, moneyRecentQuery.getDate1())
                .le(moneyRecentQuery.getDate2() != null, TFranchiseeBalanceChange::getCreateTime, moneyRecentQuery.getDate2())
                .page(Page.of(moneyRecentQuery.getPageNum(), moneyRecentQuery.getPageSize()));
        for (TFranchiseeBalanceChange record : page.getRecords()) {
            Franchisee franchisee = franchiseeService.getById(record.getFranchiseeId());
            String siteIds = franchisee.getSiteIds();
            String cityCode = franchisee.getCityCode();
            List<Region> list = regionService.lambdaQuery().in(Region::getCode, cityCode.split(",")).list();
            List<Site> list1 = siteService.lambdaQuery().in(Site::getId, siteIds.split(",")).list();
            record.setList(list);
            record.setList1(list1);
            record.setFranchiseeName(franchisee.getName());
        }
        return R.ok(page);
    }
    @ApiOperation(value = "导出", tags = {"后台2.0-统一充值扣款列表"})
    @PostMapping(value = "/recent/money/list/export")
    public R<Page<TFranchiseeBalanceChange>> export(@RequestBody MoneyRecentQuery moneyRecentQuery, HttpServletResponse response) {
        List<TFranchiseeBalanceChange> page = balanceChangeService.lambdaQuery().eq(moneyRecentQuery.getType() != null, TFranchiseeBalanceChange::getType, moneyRecentQuery.getType())
                .eq(moneyRecentQuery.getFranchId() != null, TFranchiseeBalanceChange::getFranchiseeId, moneyRecentQuery.getFranchId())
                .eq(moneyRecentQuery.getFranchName() != null && !"".equals(moneyRecentQuery.getFranchName()), TFranchiseeBalanceChange::getFranchiseeName, moneyRecentQuery.getFranchName())
                .ge(moneyRecentQuery.getDate1() != null, TFranchiseeBalanceChange::getCreateTime, moneyRecentQuery.getDate1())
                .le(moneyRecentQuery.getDate2() != null, TFranchiseeBalanceChange::getCreateTime, moneyRecentQuery.getDate2())
                .list();
        try {
            response.setCharacterEncoding(Constants.UTF8);
            response.setContentType("application/vnd.ms-excel");
            response.setHeader("Access-Control-Expose-Headers", "Content-disposition");
            response.setHeader("Content-Disposition", "attachment;filename=" +
                    URLEncoder.encode(Constants.EXCEL_ORDER_FILE_NAME, CharEncoding.UTF_8) + ".xlsx");
        } catch (UnsupportedEncodingException e) {
            return R.fail("excel导出失败!");
        }
        try {
            // excel模板封装
            ExcelWriterBuilder excelWriterBuilder = EasyExcelFactory.write(response.getOutputStream());
            InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream("template/" + Constants.EXCEL_ORDER_FILE_NAME1 + ".xlsx");
            // 自动释放资源
            try (ExcelWriter excelWriter = excelWriterBuilder.withTemplate(stream).build()) {
                WriteSheet writeSheet = EasyExcelFactory.writerSheet().build();
                excelWriter.fill(page, writeSheet);
                excelWriter.finish();
            } catch (Exception e) {
                return R.fail("excel导出失败!");
            }
        } catch (IOException e) {
            return R.fail("excel导出失败!");
        }
        return R.ok();
    }
    @ApiOperation(value = "扣余额", tags = {"后台2.0-加盟商列表余额"})
    @PostMapping(value = "/recent/money/consume")
    public R<Page<TFranchiseeBalanceChange>> consume(@RequestBody TFranchiseeBalanceChange franchiseeBalanceChange) {
        Franchisee byId = franchiseeService.getById(franchiseeBalanceChange.getFranchiseeId());
        franchiseeBalanceChange.setFranchiseeName(byId.getName());
        balanceChangeService.save(franchiseeBalanceChange);
        byId.setBalance(byId.getBalance().subtract(franchiseeBalanceChange.getAmount()));
        franchiseeService.updateById(byId);
        return R.ok();
    }
    @Getter
    private PrivateKey privateKey;
    @ApiOperation(value = "js支付", tags = {"2.0-支付"})
    @GetMapping(value = "/js/wxPay")
    public R jsPay(@RequestParam String openId,@RequestParam BigDecimal money,@RequestParam Integer userId) {
        SysUser byId1 = sysUserService.getById(userId);
        this.privateKey = PemUtil.loadPrivateKey(getPrivateKeyStream());
        this.privateKeySigner = new PrivateKeySigner(weChatConfig.merchantSerialNumber, privateKey);
        String code = generateTradeNumber();
        int i = money.multiply(BigDecimal.valueOf(100)).intValue();
        Franchisee byId = franchiseeService.getById(byId1.getFranchiseeId());
        PrepayRequest prepayRequest = new PrepayRequest();
        prepayRequest.setAppid(weChatConfig.appId);
        prepayRequest.setMchid(weChatConfig.merchantId);
        prepayRequest.setDescription("加盟商充值");
        prepayRequest.setOutTradeNo(code);
        prepayRequest.setNotifyUrl("http://www.zhipingwang.com.cn:9090/admin/franchisee/callBack");
        Amount amount = new Amount();
        amount.setTotal(i);
        prepayRequest.setAmount(amount);
        Payer payer = new Payer();
        payer.setOpenid(openId);
        prepayRequest.setPayer(payer);
        PrepayResponse prepay = jsapiService.prepay(prepayRequest);
        String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
        String packageStr = "prepay_id=" + prepay.getPrepayId();
        String prepay_id = prepay.getPrepayId();
        //重新进行签名后返回给前端
        Map<String, Object> map2 = new HashMap<>();
        map2.put("appid", weChatConfig.getAppId());
        map2.put("noncestr", code);
        map2.put("package", "Sign=WXPay");
        map2.put("partnerid", weChatConfig.getMerchantId());
        map2.put("prepayid", prepay_id);
        map2.put("timestamp", timeStamp);
//        String s1 = this.weixinSignature(map2);
        String signStr = Stream.of(weChatConfig.getAppId(), timeStamp, code, packageStr).collect(Collectors.joining("\n", "", "\n"));
        String packageSign = privateKeySigner.sign(signStr.getBytes(StandardCharsets.UTF_8)).getSign();
        map2.put("sign", packageSign);
        System.err.println(map2);
        TFranchiseeBalanceChange tFranchiseeBalanceChange = new TFranchiseeBalanceChange();
        tFranchiseeBalanceChange.setAmount(money);
        tFranchiseeBalanceChange.setIs_pay(0);
        tFranchiseeBalanceChange.setFranchiseeName(byId.getName());
        tFranchiseeBalanceChange.setFranchiseeId(String.valueOf(byId.getId()));
        tFranchiseeBalanceChange.setType(2);
        tFranchiseeBalanceChange.setCode(code);
        balanceChangeService.save(tFranchiseeBalanceChange);
        return R.ok(map2);
    }
    public InputStream getPrivateKeyStream() {
        // 需要证书释放
        byte[] certData;
//        InputStream certStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(this.privateKeyPath);
        InputStream certStream = null;
        try {
            certStream = new FileInputStream(weChatConfig.privateKeyPath);
            certData = IOUtils.toByteArray(certStream);
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("私钥文件未找到");
        }finally {
            if(null != certStream){
                try {
                    certStream.close();
                } catch (IOException e) {
                }
            }
        }
        return new ByteArrayInputStream(certData);
    }
    @Resource
    private TokenService tokenService;
    @Resource
    private NativePayService nativePayService;
    @ApiOperation(value = "扫码支付",tags = {"后台2.0-加盟商列表余额"})
    @PostMapping(value = "/code/buy")
    public R buy(@RequestParam BigDecimal money) throws AlipayApiException {
        Long userid = tokenService.getLoginUser().getUserid();
        SysUser byId1 = sysUserService.getById(userid);
        Franchisee byId = franchiseeService.getById(byId1.getFranchiseeId());
        String code = generateTradeNumber();
        int i = money.multiply(BigDecimal.valueOf(100)).intValue();
        com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest prepayRequest = new com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest();
            prepayRequest.setAppid(weChatConfig.appId);
            prepayRequest.setMchid(weChatConfig.merchantId);
            prepayRequest.setOutTradeNo(code);
            prepayRequest.setDescription("购买资料");
            prepayRequest.setNotifyUrl("http://www.zhipingwang.com.cn:9090/admin/franchisee/callBack");
            com.wechat.pay.java.service.payments.nativepay.model.Amount amount = new com.wechat.pay.java.service.payments.nativepay.model.Amount();
            amount.setTotal(i);
            prepayRequest.setAmount(amount);
            // 调用下单方法,得到应答
            com.wechat.pay.java.service.partnerpayments.app.model.PrepayResponse response;
            try {
                com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse prepay = nativePayService.prepay(prepayRequest);
                //预支付成功,创建预支付订单
                TFranchiseeBalanceChange tFranchiseeBalanceChange = new TFranchiseeBalanceChange();
                tFranchiseeBalanceChange.setAmount(money);
                tFranchiseeBalanceChange.setIs_pay(0);
                tFranchiseeBalanceChange.setFranchiseeName(byId.getName());
                tFranchiseeBalanceChange.setFranchiseeId(String.valueOf(userid));
                tFranchiseeBalanceChange.setType(2);
                tFranchiseeBalanceChange.setCode(code);
                balanceChangeService.save(tFranchiseeBalanceChange);
                return R.ok(prepay.getCodeUrl());
            } catch (HttpException e) { // 发送HTTP请求失败
//                log.error("发送HTTP请求失败: {}", e.getHttpRequest());
            } catch (ServiceException e) { // 服务返回状态小于200或大于等于300,例如500
//                log.error("服务返回状态异常: {}", e.getResponseBody());
            } catch (MalformedMessageException e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败
//                log.error("返回体类型不合法: {}", e.getMessage());
            } catch (Exception e) {
//                log.error("预下单异常: {}", e.getMessage());
            }
            return null;
    }
    @Resource
    private NotificationParser notificationParser;
    @ApiOperation(value = "支付回调",tags = {"微信支付回调"})
    @RequestMapping (value = "/callBack")
    @Transactional
    public R payNotify(HttpServletRequest request) throws Exception{
        System.err.println("======回调开始");
        Transaction transaction;
        transaction = notificationParser.parse(WeChatUtil.handleNodifyRequestParam(request), Transaction.class);
        if (transaction.getTradeState() == Transaction.TradeStateEnum.SUCCESS) {
            //将记录变为已支付
            TFranchiseeBalanceChange one = balanceChangeService.lambdaQuery().eq(TFranchiseeBalanceChange::getCode, transaction.getOutTradeNo()).one();
            if (one.getIs_pay()==0) {
                //将加盟商的余额增加
                Franchisee byId = franchiseeService.getById(one.getFranchiseeId());
                byId.setBalance(byId.getBalance().add(one.getAmount()));
                franchiseeService.updateById(byId);
                one.setIs_pay(1);
                balanceChangeService.updateById(one);
            }
        }
        return R.ok(null,"SUCCESS");
    }
    public static String generateTradeNumber() {
        // 定义订单号前缀
        // 当前年月日
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
        String currentTimeStr =now.format(formatter);
        // 获取当前时间戳
        long timestamp = System.currentTimeMillis();
        // 构造订单号
        return currentTimeStr + timestamp;
    }
    private String weixinSignature(Map<String, Object> map){
        try {
            Set<Map.Entry<String, Object>> entries = map.entrySet();
            List<Map.Entry<String, Object>> infoIds = new ArrayList<Map.Entry<String, Object>>(entries);
            // 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)
            Collections.sort(infoIds, new Comparator<Map.Entry<String, Object>>() {
                public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) {
                    return (o1.getKey()).toString().compareTo(o2.getKey());
                }
            });
            // 构造签名键值对的格式
            StringBuilder sb = new StringBuilder();
            for (Map.Entry<String, Object> item : infoIds) {
                if (item.getKey() != null || item.getKey() != "") {
                    String key = item.getKey();
                    Object val = item.getValue();
                    if (!(val == "" || val == null)) {
                        sb.append(key + "=" + val + "&");
                    }
                }
            }
            sb.append("key=" + "TA2npSNWmS0GcB0tFFRWA94rm1M0iSFs");
            String sign = MD5AndKL.MD5Encode(sb.toString(), "UTF-8").toUpperCase(); //注:MD5签名方式
            return sign;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    public static String generateNonceStr() {
        char[] nonceChars = new char[32];
        for (int index = 0; index < nonceChars.length; ++index) {
            nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
        }
        return new String(nonceChars);
    }
    /**
     * 加盟商信息分页列表
@@ -103,8 +475,13 @@
            @ApiImplicitParam(value = "加盟商信息id", name = "id", dataType = "Integer", required = true)
    })
    public R<Franchisee> detail(@RequestParam("id") Integer id) {
        return R.ok(franchiseeService.lambdaQuery()
                .eq(Franchisee::getId, id).eq(Franchisee::getIsDelete, 0).one());
        Franchisee one = franchiseeService.lambdaQuery()
                .eq(Franchisee::getId, id).eq(Franchisee::getIsDelete, 0).one();
        List<Site> list = siteService.lambdaQuery().in(Site::getId, one.getSiteIds().split(",")).list();
        //用 , 拼接
        one.setSiteStr(list.stream().map(Site::getSiteName).collect(Collectors.joining(",")));
        return R.ok(one);
    }
    /**
@@ -121,6 +498,53 @@
        return R.ok(Arrays.stream(franchisee.getCityCode().split(",")).collect(Collectors.toList()));
    }
    @ApiOperation(value = "主页信息", tags = {"2.0师傅端"})
    @GetMapping(value = "/info")
    public R<InfoDto> info() {
        Long userid = tokenService.getLoginUser().getUserid();
        SysUser byId = sysUserService.getById(userid);
        Franchisee franchisee = franchiseeService.lambdaQuery()
                .eq(Franchisee::getId, byId.getFranchiseeId()).eq(Franchisee::getIsDelete, 0).one();
        String siteIds = franchisee.getSiteIds();
        String cityCode = franchisee.getCityCode();
        List<Region> list = regionService.lambdaQuery().in(Region::getCode, cityCode.split(",")).list();
        List<Site> list1 = siteService.lambdaQuery().in(Site::getId, siteIds.split(",")).list();
        InfoDto infoDto = new InfoDto();
        infoDto.setList(list);
        infoDto.setList1(list1);
        infoDto.setFranchisee(franchisee);
        return R.ok(infoDto);
    }
    @ApiOperation(value = "主页信息", tags = {"2.0师傅端"})
    @GetMapping(value = "/info1")
    public R<InfoDto> info1(Integer id) {
        Franchisee franchisee = franchiseeService.lambdaQuery()
                .eq(Franchisee::getId, id).eq(Franchisee::getIsDelete, 0).one();
        String siteIds = franchisee.getSiteIds();
        String cityCode = franchisee.getCityCode();
        List<Region> list = regionService.lambdaQuery().in(Region::getCode, cityCode.split(",")).list();
        List<Site> list1 = siteService.lambdaQuery().in(Site::getId, siteIds.split(",")).list();
        InfoDto infoDto = new InfoDto();
        infoDto.setList(list);
        infoDto.setList1(list1);
        infoDto.setFranchisee(franchisee);
        return R.ok(infoDto);
    }
    @ApiOperation(value = "当前余额", tags = {"后台2.0-加盟商列表余额"})
    @GetMapping(value = "/rencentBalance")
    public R<Franchisee> rencentBalance() {
        Long userid = tokenService.getLoginUser().getUserid();
        SysUser byId = sysUserService.getById(userid);
        Franchisee franchisee = franchiseeService.lambdaQuery()
                .eq(Franchisee::getId, byId.getFranchiseeId()).eq(Franchisee::getIsDelete, 0).one();
        return R.ok(franchisee);
    }
    @GetMapping(value = "/getWorkPic")
    public R<String> getWorkPic(@RequestParam("id") Integer id) {
        MasterWorker byId = masterWorkerService.getById(id);
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/OrderController.java
@@ -404,12 +404,7 @@
    @RequiresPermissions("order_reassignment")
    @ApiOperation(value = "订单列表-订单派单/改派", tags = {"后台-订单管理"})
    @PostMapping(value = "/reassignment")
    @ApiImplicitParams({
            @ApiImplicitParam(value = "操作类型(1:订单派单;2:订单改派)", name = "type", dataType = "Integer", required = true),
            @ApiImplicitParam(value = "订单id", name = "orderId", dataType = "Integer", required = true),
            @ApiImplicitParam(value = "服务人员id", name = "workerId", dataType = "Integer", required = true),
            @ApiImplicitParam(value = "改派原因", name = "applyReason", dataType = "String")
    })
    @Transactional(rollbackFor = Exception.class)
    public R<String> reassignment(@RequestBody OrderReasinDto orderReasinDto) {
//        String[] split = orderIds.split(",");
@@ -426,11 +421,22 @@
        if (Constants.TWO.equals(orderId.getType())) {
            if (order.getState().equals(Constants.SIX) || order.getState().equals(Constants.THREE)) {
                orderClient.updateState(order.getId(), Constants.ONE);
                //如果是待改派,将上门时间设置为最新的,并且更新再投原因
                if (order.getState().equals(Constants.SIX)){
                    dispatchClient.changeReason(order.getId(), orderReasinDto.getReason());
                    orderClient.updateArrivalTime(order.getId(),orderReasinDto.getArriveTime());
                }
            }
            // 订单状态为 待完工时,需要更改状态 待上门且清空师傅到达预约点时间
            if (order.getState().equals(Constants.TWO)) {
                orderClient.updateStateAndArrivalTime(orderId.getOrderId(), Constants.ONE);
            }
            // 生成改派信息
//            ChangeDispatch changeDispatch = new ChangeDispatch();
//            changeDispatch.setWorkerId(item.getServerId());
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/SiteController.java
@@ -55,7 +55,7 @@
    public R<IPage<Site>> queryPageList(@RequestParam(name = "pageNum", defaultValue = "1") Integer pageNum,
                                        @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
        return R.ok(siteService.lambdaQuery().orderByDesc(Site::getCreateTime)
                .eq(Site::getIsDelete, 0)
                .eq(Site::getIsDelete, 0).orderByDesc(Site::getSort)
                .page(Page.of(pageNum, pageSize)));
    }
@@ -85,7 +85,7 @@
        return R.ok(siteService.lambdaQuery().orderByDesc(Site::getCreateTime).in(siteIds.length>0, Site::getId, siteIds).like(siteName!=null&&siteName!="", Site::getSiteName, siteName)
                .eq(Site::getIsDelete, 0)
                .eq(Site::getIsDelete, 0).orderByDesc(Site::getSort)
                .list());
    }
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/Franchisee.java
@@ -9,9 +9,11 @@
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import org.apache.poi.hpsf.Decimal;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
/**
 * <p>
@@ -89,6 +91,9 @@
    @ApiModelProperty("管辖城市二维数组")
    @TableField("site_ids")
    private String siteIds;
    @ApiModelProperty("余额")
    @TableField("balance")
    private BigDecimal balance;
    @TableField(exist = false)
    private String siteStr;
}
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/Site.java
@@ -26,6 +26,9 @@
    @TableId(value = "id",type = IdType.AUTO)
    private Integer id;
    @ApiModelProperty("排序")
    @TableField("sort")
    private Integer sort;
    @ApiModelProperty("站点名称")
    @TableField("site_name")
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/TFranchiseeBalanceChange.java
New file
@@ -0,0 +1,91 @@
package com.ruoyi.admin.entity;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
/**
 * <p>
 *
 * </p>
 *
 * @author luodangjia
 * @since 2024-11-02
 */
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("t_franchisee_balance_change")
@ApiModel(value="TFranchiseeBalanceChange对象", description="")
public class TFranchiseeBalanceChange implements Serializable {
    private static final long serialVersionUID = 1L;
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    @ApiModelProperty(value = "扣除金额")
    @TableField("amount")
    private BigDecimal amount;
    @TableField("region_name")
    private String regionName;
    @TableField("region_id")
    private Integer regionId;
    @TableField("createBy")
    private String createBy;
    @TableField("pics")
    private String pics;
    @TableField("site_id")
    private Integer siteId;
    @TableField("is_pay")
    private Integer is_pay;
    @TableField("franchisee_name")
    private String franchiseeName;
    @TableField("franchisee_id")
    private String franchiseeId;
    @TableField("code")
    private String code;
    @TableField("remark")
    private String remark;
    @TableField("updateBy")
    private String updateBy;
    @TableField("createTime")
    private LocalDateTime createTime;
    @TableField("updateTime")
    private LocalDateTime updateTime;
    @ApiModelProperty(value = "1扣款2充值")
    @TableField("type")
    private Integer type;
    @ApiModelProperty(value = "是否删除 0未删除;1已删除")
    @TableField("is_delete")
    @TableLogic
    private Integer isDelete;
    @TableField(exist = false)
    private String payStr = "微信支付";
    @TableField(exist = false)
    private List<Region> list;
    @TableField(exist = false)
    private List<Site> list1;
    @TableField(exist = false)
    private List<Site> franchiseName;
}
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/TFranchiseeBalanceChangeMapper.java
New file
@@ -0,0 +1,16 @@
package com.ruoyi.admin.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.admin.entity.TFranchiseeBalanceChange;
/**
 * <p>
 *  Mapper 接口
 * </p>
 *
 * @author luodangjia
 * @since 2024-11-02
 */
public interface TFranchiseeBalanceChangeMapper extends BaseMapper<TFranchiseeBalanceChange> {
}
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/TFranchiseeBalanceChangeService.java
New file
@@ -0,0 +1,16 @@
package com.ruoyi.admin.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.admin.entity.TFranchiseeBalanceChange;
/**
 * <p>
 *  服务类
 * </p>
 *
 * @author luodangjia
 * @since 2024-11-02
 */
public interface TFranchiseeBalanceChangeService extends IService<TFranchiseeBalanceChange> {
}
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/TFranchiseeBalanceChangeServiceImpl.java
New file
@@ -0,0 +1,21 @@
package com.ruoyi.admin.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.admin.entity.TFranchiseeBalanceChange;
import com.ruoyi.admin.mapper.TFranchiseeBalanceChangeMapper;
import com.ruoyi.admin.service.TFranchiseeBalanceChangeService;
import org.springframework.stereotype.Service;
/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author luodangjia
 * @since 2024-11-02
 */
@Service
public class TFranchiseeBalanceChangeServiceImpl extends ServiceImpl<TFranchiseeBalanceChangeMapper, TFranchiseeBalanceChange> implements TFranchiseeBalanceChangeService {
}
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/utils/MD5AndKL.java
New file
@@ -0,0 +1,112 @@
package com.ruoyi.admin.utils;
import java.security.MessageDigest;
public class MD5AndKL {
    /**
     * MD5加码。32位
     *
     * @param inStr
     * @return
     */
    public static String MD5(String inStr) {
        MessageDigest md5 = null;
        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (Exception e) {
            throw new RuntimeException(e.toString());
        }
        byte[] md5Bytes = md5.digest(inStr.getBytes());
        StringBuffer hexValue = new StringBuffer();
        for (int i = 0; i < md5Bytes.length; i++) {
            int val = ((int) md5Bytes[i]) & 0xff;
            if (val < 16) {
                hexValue.append("0");
            }
            hexValue.append(Integer.toHexString(val));
        }
        return hexValue.toString();
    }
    /**
     * 可逆的加密算法
     *
     * @param inStr
     * @return
     */
    public static String KL(String inStr) {
        char[] a = inStr.toCharArray();
        for (int i = 0; i < a.length; i++) {
            a[i] = (char) (a[i] ^ 't');
        }
        String s = new String(a);
        return s;
    }
    /**
     * 加密后解密
     *
     * @param inStr
     * @return
     */
    public static String JM(String inStr) {
        char[] a = inStr.toCharArray();
        for (int i = 0; i < a.length; i++) {
            a[i] = (char) (a[i] ^ 't');
        }
        String k = new String(a);
        return k;
    }
    private static String byteArrayToHexString(byte b[]) {
        StringBuffer resultSb = new StringBuffer();
        for (int i = 0; i < b.length; i++)
            resultSb.append(byteToHexString(b[i]));
        return resultSb.toString();
    }
    private static String byteToHexString(byte b) {
        int n = b;
        if (n < 0)
            n += 256;
        int d1 = n / 16;
        int d2 = n % 16;
        return hexDigits[d1] + hexDigits[d2];
    }
    public static String MD5Encode(String origin, String charsetname) {
        String resultString = null;
        try {
            resultString = new String(origin);
            MessageDigest md = MessageDigest.getInstance("MD5");
            if (charsetname == null || "".equals(charsetname)){
                resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
            }else{
                resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
            }
        } catch (Exception exception) {
            exception.printStackTrace();
        }
        return resultString;
    }
    private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
            "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
    public static void main(String args[]) {
        System.out.println("MD5后再加密:" + KL(MD5("123456")));
        System.out.println(MD5("123456"));
        // System.out.println("加密:" + KL(MD5("123456")));
        // s = KL(s);
        // System.out.println("解密:" + KL("81dc9bdb52d04dc20036dbd8313ed055"));
        // System.out.println("解密:" + JM(KL(s)));
        // System.out.println("解密为MD5后的:" + KL(KL(MD5(s))));
        // System.out.println(JM("5d62957bb57d3e49dcf48a0df064be4c"));
        // System.out.println(MD5AndKL.KL(MD5AndKL.MD5("admin"+"87654321")));
    }
}
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/utils/WeChatUtil.java
New file
@@ -0,0 +1,128 @@
package com.ruoyi.admin.utils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Random;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import com.wechat.pay.java.core.cipher.RSASigner;
import com.wechat.pay.java.core.cipher.SignatureResult;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.core.util.NonceUtil;
import com.wechat.pay.java.core.util.PemUtil;
/**
 * @desc: 微信工具类
 * @author: shy
 * @date: 2024/4/8 16:10
 */
public class WeChatUtil {
    private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final Random RANDOM = new SecureRandom();
    /**
     * 生成订单号
     *
     * @param
     * @return String
     * @author shy
     * @date 2024/4/8 16:15
     */
    public static String generateTradeNumber() {
        // 定义订单号前缀
        // 当前年月日
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
        String currentTimeStr =now.format(formatter);
        // 获取当前时间戳
        long timestamp = System.currentTimeMillis();
        // 构造订单号
        return currentTimeStr + timestamp;
    }
    /**
     * 获取随机字符串 Nonce Str
     *
     * @param
     * @return String
     * @author shy
     * @date 2024/4/16 17:07
     */
    public static String generateNonceStr() {
        return NonceUtil.createNonce(32);
    }
    /**
     * 获取当前时间戳,单位秒
     * @param
     * @return long
     * @author shy
     * @date 2024/4/16 17:10
     */
    public static long getCurrentTimestamp() {
        return System.currentTimeMillis() / 1000;
    }
    public static String getSign(String signatureStr, String privateKeyPath, String merchantSerialNumber) {
        PrivateKey privateKey = PemUtil.loadPrivateKeyFromPath(privateKeyPath);
        RSASigner rsaSigner = new RSASigner(merchantSerialNumber, privateKey);
        SignatureResult signatureResult = rsaSigner.sign(signatureStr);
        return signatureResult.getSign();
    }
    /**
     * 构造 RequestParam
     *
     * @param request
     * @return RequestParam
     * @author shy
     * @date 2024/4/9 11:16
     */
    public static RequestParam handleNodifyRequestParam(HttpServletRequest request) throws IOException {
        // 请求头Wechatpay-Signature
        String signature = request.getHeader("Wechatpay-Signature");
        // 请求头Wechatpay-nonce
        String nonce = request.getHeader("Wechatpay-Nonce");
        // 请求头Wechatpay-Timestamp
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        // 微信支付证书序列号
        String serial = request.getHeader("Wechatpay-Serial");
        // 签名方式
        String signType = request.getHeader("Wechatpay-Signature-Type");
        // 构造 RequestParam
        return new RequestParam.Builder().serialNumber(serial).nonce(nonce).signature(signature).timestamp(timestamp).signType(signType).body(getRequestBody(request)).build();
    }
    public static String getRequestBody(HttpServletRequest request) throws IOException {
        ServletInputStream stream;
        BufferedReader reader = null;
        StringBuilder sb = new StringBuilder();
        try {
            stream = request.getInputStream();
            // 获取响应
            reader = new BufferedReader(new InputStreamReader(stream));
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            throw new IOException("读取返回支付接口数据流出现异常!");
        } finally {
            if (reader != null) {
                reader.close();
            }
        }
        return sb.toString();
    }
}
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/utils/WechatPaySignature.java
New file
@@ -0,0 +1,36 @@
package com.ruoyi.admin.utils;
import org.apache.commons.codec.digest.DigestUtils;
import java.util.Map;
import java.util.TreeMap;
public class WechatPaySignature {
    // 模拟的签名生成函数,实际应用中应使用微信提供的签名算法
    public static String generateSignature(Map<String, String> parameters, String apiKey) {
        // 这里应该使用微信官方提供的签名算法来生成sign
        // 示例仅为说明,实际中不要直接如此使用
        StringBuilder toSign = new StringBuilder();
        for (Map.Entry<String, String> entry : parameters.entrySet()) {
            toSign.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
        }
        toSign.append("key=").append(apiKey);
        // 对字符串进行MD5或其他加密
        return DigestUtils.md5Hex(toSign.toString()).toUpperCase();
    }
    public static void main(String[] args) {
        Map<String, String> params = new TreeMap<>();
        params.put("appId", "wx98563d0ec9cf21c8");
        params.put("timeStamp", "1392689076");
        params.put("nonceStr", "e614eka7e99d027403216e8dfa37ddc2");
        params.put("package", "prepay_id=wx01181404626776e879e6764f2760370000");
        params.put("signType", "MD5");
        String apiKey = "TA2npSNWmS0GcB0tFFRWA94rm1M0iSFs"; // 你的API密钥
        String paySign = generateSignature(params, apiKey);
        System.out.println("paySign: " + paySign);
    }
}
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/vo/InfoDto.java
New file
@@ -0,0 +1,15 @@
package com.ruoyi.admin.vo;
import com.ruoyi.admin.entity.Franchisee;
import com.ruoyi.admin.entity.Region;
import com.ruoyi.admin.entity.Site;
import lombok.Data;
import java.util.List;
@Data
public class InfoDto {
    Franchisee franchisee;
    List<Region> list ;
    List<Site> list1 ;
}
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/vo/MoneyRecentQuery.java
New file
@@ -0,0 +1,25 @@
package com.ruoyi.admin.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class MoneyRecentQuery {
    @ApiModelProperty("加盟商id")
    private Integer franchId;
    @ApiModelProperty("加盟商名字,查询用")
    private Integer franchName;
    @ApiModelProperty("1扣款2充值")
    private Integer type;
    @ApiModelProperty("扣款时间1")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime date1;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty("扣款时间2")
    private LocalDateTime date2;
    private Integer pageNum;
    private Integer pageSize;
}
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/vo/OrderReasinDto.java
@@ -2,9 +2,11 @@
import lombok.Data;
import java.util.Date;
import java.util.List;
@Data
public class OrderReasinDto {
    Integer workerId;
    List<ReassinDto> reassinDtos;
}
    String reason;
     Date arriveTime;}
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/vo/RencentBalance.java
New file
@@ -0,0 +1,14 @@
package com.ruoyi.admin.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.apache.poi.hpsf.Decimal;
import java.math.BigDecimal;
@Data
public class RencentBalance {
    private BigDecimal balance1;
    private BigDecimal balance2;
    private BigDecimal balance3;
}
ruoyi-service/ruoyi-admin/src/main/resources/template/加盟商余额记录.xlsx
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/ChangeDispatchController.java
@@ -3,6 +3,7 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.exception.GlobalException;
import com.ruoyi.common.security.annotation.RequiresPermissions;
@@ -195,6 +196,14 @@
        return R.ok(update && orderService.updateById(order));
    }
    @GetMapping(value = "/changeReason")
    public void changeReason(@RequestParam("id")String id,@RequestParam("reason") String reason) {
        ChangeDispatch one = changeDispatchService.lambdaQuery().eq(ChangeDispatch::getOrderId, id).orderByDesc(BaseEntity::getCreateTime).last("limit 1").one();
        one.setState(1);
        one.setApplyReason(reason);
        changeDispatchService.updateById(one);
    }
    /**
     * 根据id批量删除订单改派
     *
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/OrderController.java
@@ -516,6 +516,13 @@
        return R.ok(!list.isEmpty());
    }
    @GetMapping(value = "/updateArrivalTime")
    public R<Boolean> updateArrivalTime(@RequestParam("orderId")String orderId,@RequestParam("arriveTime")Date arriveTime) {
        Order byId = orderService.getById(orderId);
        byId.setArriveTime(arriveTime);
        return R.ok();
    }
    /**
     * 回收订单统计
     *
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/request/ChangeDispatchRequest.java
@@ -15,6 +15,7 @@
    private String workerName;
    private String orderNumber;
    private String reservationAddress;
    private String userName;
ruoyi-service/ruoyi-order/src/main/resources/mapper/order/ChangeDispatchMapper.xml
@@ -19,10 +19,13 @@
        <result column="user_id" property="userId"/>
        <result column="user_name" property="userName"/>
        <result column="orderTime" property="orderTime"/>
        <result column="reservation_name" property="reservationName"/>
        <result column="reservation_phone" property="reservationPhone"/>
        <result column="reservation_address" property="reservationAddress"/>
    </resultMap>
    <select id="queryPageList" resultMap="BaseResultMap">
        select c.*, o.createTime as orderTime,o.type as type
        select c.*, o.createTime as orderTime,o.type as type,o.reservation_name,o.reservation_phone,o.reservation_address
        from sys_change_dispatch c
                 left join t_order o on c.order_id = o.id
        <where>
@@ -36,6 +39,9 @@
            <if test="data.userName != null and data.userName != ''">
                and c.user_name like concat('%', #{data.userName}, '%')
            </if>
            <if test="data.reservationAddress != null and data.reservationAddress != ''">
                and o.reservationAddress like concat('%', #{data.reservationAddress}, '%')
            </if>
            <if test="data.cityList != null and data.cityList.size() != 0">
                and o.city_code in
                <foreach collection="data.cityList" item="city" open="(" separator="," close=")">
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/controller/OrderController.java
@@ -390,7 +390,7 @@
        String nowStr = now.format(formatter);
        redisService.setCacheObject("MARK:"+workerId+":"+nowStr,longitude + "," + latitude);
        redisService.setCacheObject("MARK:"+workerId+":"+nowStr,longitude + "," + latitude, 10L, TimeUnit.MINUTES);
        String cacheObject = redisService.getCacheObject("MARK:" + workerId + ":" + formattedTime);
        if (cacheObject!=null){
@@ -430,7 +430,7 @@
            System.err.println("-----当前距离"+distance);
            //如果超出一定范围,存入Mark文件
            if (distance<50){
                redisService.setCacheObject("TIME:"+workerId,LocalDateTime.now());
                redisService.setCacheObject("TIME:"+workerId,LocalDateTime.now(), 10L, TimeUnit.MINUTES);
                marker(longitude,latitude);
            }
            }
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/controller/OssController.java
@@ -12,7 +12,9 @@
import io.swagger.annotations.ApiOperation;
import lombok.Synchronized;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@@ -30,6 +32,7 @@
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.concurrent.CompletableFuture;
/**
@@ -71,42 +74,125 @@
        return R.ok(tencentCosUtil.upLoadFile(file));
    }
//    @PostMapping(value = "/pic", produces = "application/json;charset=UTF-8")
//    @ApiOperation(value = "文件上传加水印", tags = "师傅端-文件上传")
//    @ApiImplicitParams({
//            @ApiImplicitParam(value = "文件", name = "file", dataType = "MultipartFile", required = true)
//    })
//    public R<String> pic(@RequestParam("file") MultipartFile file,String longitude,
//                         String latitude) {
////        Result<String> address = GaoDeMapUtil.getAddress(longitude, latitude);
//////            return R.fail();
////
////        // 获取当前时间
////
////        LocalDateTime date = LocalDateTime.now();
////
////        // 设置日期格式
////
////        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
////
////        // 通过format调用转换的方法
////
////        String dateWatermark =  formatter.format(date);
//////        System.err.println("==========="+LocalDateTime.now());
////        log.error("==========="+LocalDateTime.now());
////
////        MultipartFile watermarkFile = FileImageWatermarkUtils.markWithContent(file,dateWatermark,address.getDatas());
//////        System.err.println("==========="+LocalDateTime.now());
////        log.error("==========="+LocalDateTime.now());
////
////
////        String url = tencentCosUtil.upLoadFile(watermarkFile);
//////        String url = tencentCosUtil.upLoadFile(file);
////        log.error("==========="+LocalDateTime.now());
////        return R.ok(url);
//
//
//        // 参数校验
//        if (file == null || file.isEmpty()) {
//            return R.fail("文件不能为空");
//        }
//        if (longitude == null || longitude.isEmpty() || latitude == null || latitude.isEmpty()) {
//            return R.fail("经纬度参数不能为空");
//        }
//
//        try {
//            // 获取地址信息
//            Result<String> addressResult = GaoDeMapUtil.getAddress(longitude, latitude);
//            if (addressResult == null || addressResult.getDatas() == null) {
//                return R.fail("无法获取地址信息");
//            }
//
//            // 获取当前时间
//            LocalDateTime date = LocalDateTime.now();
//            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
//            String dateWatermark = formatter.format(date);
//            log.info("===========" + dateWatermark);
//
//            // 给图片加水印
//            MultipartFile watermarkFile = FileImageWatermarkUtils.markWithContent(file, dateWatermark, addressResult.getDatas());
//            log.info("水印添加成功");
//
//            // 上传到腾讯云COS
//            String url = tencentCosUtil.upLoadFile(watermarkFile);
//            log.info("文件上传成功,URL: " + url);
//
//            return R.ok(url);
//        } catch (Exception e) {
//            log.error("文件上传加水印失败: ", e);
//            return R.fail("文件上传加水印失败: " + e.getMessage());
//        }
//    }
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;
    @PostMapping(value = "/pic", produces = "application/json;charset=UTF-8")
    @ApiOperation(value = "文件上传加水印", tags = "师傅端-文件上传")
    @ApiImplicitParams({
            @ApiImplicitParam(value = "文件", name = "file", dataType = "MultipartFile", required = true)
    })
    @Synchronized
    public R<String> pic(@RequestParam("file") MultipartFile file,String longitude,String address,
                         String latitude) {
//        Result<String> address = GaoDeMapUtil.getAddress(longitude, latitude);
//            return R.fail();
    public CompletableFuture<R<String>> pic(@RequestParam("file") MultipartFile file, String longitude, String latitude) {
        // 参数校验
        if (file == null || file.isEmpty()) {
            return CompletableFuture.completedFuture(R.fail("文件不能为空"));
        }
        if (longitude == null || longitude.isEmpty() || latitude == null || latitude.isEmpty()) {
            return CompletableFuture.completedFuture(R.fail("经纬度参数不能为空"));
        }
        // 获取当前时间
        return CompletableFuture.supplyAsync(() -> {
            try {
                // 获取地址信息
                Result<String> addressResult = GaoDeMapUtil.getAddress(longitude, latitude);
                if (addressResult == null || addressResult.getDatas() == null) {
                    return R.fail("无法获取地址信息");
                }
        LocalDateTime date = LocalDateTime.now();
                // 获取当前时间
                LocalDateTime date = LocalDateTime.now();
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                String dateWatermark = formatter.format(date);
                log.info("===========" + dateWatermark);
        // 设置日期格式
                // 给图片加水印
                MultipartFile watermarkFile = FileImageWatermarkUtils.markWithContent(file, dateWatermark, addressResult.getDatas());
                log.info("水印添加成功");
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                // 上传到腾讯云COS
                String url = tencentCosUtil.upLoadFile(watermarkFile);
                log.info("文件上传成功,URL: " + url);
        // 通过format调用转换的方法
        String dateWatermark =  formatter.format(date);
//        System.err.println("==========="+LocalDateTime.now());
        log.error("==========="+LocalDateTime.now());
        MultipartFile watermarkFile = FileImageWatermarkUtils.markWithContent(file,dateWatermark,address);
//        System.err.println("==========="+LocalDateTime.now());
        log.error("==========="+LocalDateTime.now());
        String url = tencentCosUtil.upLoadFile(watermarkFile);
//        String url = tencentCosUtil.upLoadFile(file);
        log.error("==========="+LocalDateTime.now());
        return R.ok(url);
                return R.ok(url);
            } catch (Exception e) {
                log.error("文件上传加水印失败: ", e);
                return R.fail("文件上传加水印失败: " + e.getMessage());
            }
        }, taskExecutor);
    }
}