111
k94314517
2025-07-17 0b7c409aff156bc4d5605893e860c0c3652b9cd8
111
已添加7个文件
已修改21个文件
1344 ■■■■■ 文件已修改
server/admin/src/main/java/com/doumee/job/AutoCommentJob.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/admin/src/main/java/com/doumee/job/AutoConfirmJob.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/admin/src/main/java/com/doumee/job/AutoGrabOrdersJob.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/admin/src/main/java/com/doumee/job/InitializeCodeJob.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/pom.xml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/api/common/PublicCloudController.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/config/jwt/JwtTokenUtil.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/config/wx/TransferToUser.java 312 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/config/wx/WXPayUtility.java 456 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/config/wx/WxMiniConfig.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/config/wx/WxPayProperties.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/model/Member.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/model/MemberRevenue.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/model/Orders.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/model/WithdrawalOrders.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/OrderReleaseVO.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/MemberService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/OrdersService.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/WithdrawalOrdersService.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/CategoryServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/MemberServiceImpl.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java 152 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/WithdrawalOrdersServiceImpl.java 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/resources/application-dev.yml 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/AccountApi.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/ConfigApi.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/OrdersApi.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/UserApi.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/admin/src/main/java/com/doumee/job/AutoCommentJob.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,34 @@
package com.doumee.job;
import com.doumee.core.job.BaseJob;
import com.doumee.core.job.JobContext;
import com.doumee.core.job.JobParam;
import com.doumee.service.business.OrdersService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
 * è‡ªåŠ¨è¯„ä»·
 * @author  dm
 * @since 2025/03/31 16:44
 */
@Slf4j
@Component("AutoCommentJob")
public class AutoCommentJob extends BaseJob {
    @Autowired
    private OrdersService ordersService;
    @Override
    public JobContext execute(JobParam param) {
        JobContext jobContext = new JobContext();
        try {
            ordersService.autoComment();
            jobContext.setContext("自动评价");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return jobContext;
    }
}
server/admin/src/main/java/com/doumee/job/AutoConfirmJob.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,34 @@
package com.doumee.job;
import com.doumee.core.job.BaseJob;
import com.doumee.core.job.JobContext;
import com.doumee.core.job.JobParam;
import com.doumee.service.business.OrdersService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
 * è‡ªåŠ¨ç¡®è®¤è®¢å•ä¿®æ”¹
 * @author  dm
 * @since 2025/03/31 16:44
 */
@Slf4j
@Component("AutoConfirmJob")
public class AutoConfirmJob extends BaseJob {
    @Autowired
    private OrdersService ordersService;
    @Override
    public JobContext execute(JobParam param) {
        JobContext jobContext = new JobContext();
        try {
            ordersService.autoConfirm();
            jobContext.setContext("自动确认订单修改");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return jobContext;
    }
}
server/admin/src/main/java/com/doumee/job/AutoGrabOrdersJob.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,34 @@
package com.doumee.job;
import com.doumee.core.job.BaseJob;
import com.doumee.core.job.JobContext;
import com.doumee.core.job.JobParam;
import com.doumee.service.business.OrdersService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
 * è‡ªåŠ¨æ´¾å•
 * @author  dm
 * @since 2025/03/31 16:44
 */
@Slf4j
@Component("AutoGrabOrdersJob")
public class AutoGrabOrdersJob extends BaseJob {
    @Autowired
    private OrdersService ordersService;
    @Override
    public JobContext execute(JobParam param) {
        JobContext jobContext = new JobContext();
        try {
            ordersService.autoGrabOrders();
            jobContext.setContext("自动派单");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return jobContext;
    }
}
server/admin/src/main/java/com/doumee/job/InitializeCodeJob.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,34 @@
package com.doumee.job;
import com.doumee.core.job.BaseJob;
import com.doumee.core.job.JobContext;
import com.doumee.core.job.JobParam;
import com.doumee.service.business.OrdersService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
 * åˆå§‹åŒ–订单编号
 * @author  dm
 * @since 2025/03/31 16:44
 */
@Slf4j
@Component("InitializeCodeJob")
public class InitializeCodeJob extends BaseJob {
    @Autowired
    private OrdersService ordersService;
    @Override
    public JobContext execute(JobParam param) {
        JobContext jobContext = new JobContext();
        try {
            ordersService.initializeCode();
            jobContext.setContext("初始化订单编号");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return jobContext;
    }
}
server/pom.xml
@@ -262,6 +262,8 @@
      <artifactId>wechatpay-java</artifactId>
      <version>0.2.12</version>
    </dependency>
 <!--   <dependency>
      <groupId>com.azure.spring</groupId>
      <artifactId>spring-cloud-azure-starter-storage-blob</artifactId>
server/services/src/main/java/com/doumee/api/common/PublicCloudController.java
@@ -61,7 +61,7 @@
    })
    @PostMapping(value = "/upload", headers = "content-type=multipart/form-data")
    @ResponseBody
    public void upload(HttpServletRequest request, HttpServletResponse response, String folder) throws Exception {
    public void upload(HttpServletRequest request, HttpServletResponse response, String folder,String flag) throws Exception {
        Date d1 = new Date();
        log.error("总得上传文件成功=============开始========="+DateUtil.getPlusTime2(d1));
        if(Objects.isNull(folder)){
@@ -105,6 +105,7 @@
                        fileJSON.put("imgaddr", fName);
                        fileJSON.put("imgname", fileName);
                        fileJSON.put("originname", originname);
                        fileJSON.put("flag", flag);
                        context.put("data",fileJSON);
                        context.put("message","请求成功");
                        writerJson(response, context);
server/services/src/main/java/com/doumee/config/jwt/JwtTokenUtil.java
@@ -112,7 +112,8 @@
                .setExpiration(expirationDate)
                .signWith(SignatureAlgorithm.HS512, jwtProperties.getSecret())
                .compact();
        redisTemplate.opsForValue().set(Constants.REDIS_TOKEN_KEY+token,JSONObject.toJSONString(member),jwtProperties.getExpiration(), TimeUnit.MILLISECONDS);
        redisTemplate.opsForValue().set(Constants.REDIS_TOKEN_KEY+token,JSONObject.toJSONString(member),
                jwtProperties.getExpiration(), TimeUnit.MILLISECONDS);
        return token;
    }
server/services/src/main/java/com/doumee/config/wx/TransferToUser.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,312 @@
package com.doumee.config.wx;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.List;
/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Rk
 * @create 2025/7/17 9:19
 */
public class TransferToUser {
    private static String HOST = "https://api.mch.weixin.qq.com";
    private static String METHOD = "POST";
    private static String PATH = "/v3/fund-app/mch-transfer/transfer-bills";
    private static String CANCEL_PATH = "/v3/fund-app/mch-transfer/transfer-bills/out-bill-no/{out_bill_no}/cancel";
    public static void main(String[] args) {
        // TODO: è¯·å‡†å¤‡å•†æˆ·å¼€å‘必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756
        TransferToUser client = new TransferToUser(
                "1229817002",                    // å•†æˆ·å·ï¼Œæ˜¯ç”±å¾®ä¿¡æ”¯ä»˜ç³»ç»Ÿç”Ÿæˆå¹¶åˆ†é…ç»™æ¯ä¸ªå•†æˆ·çš„唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
                "3FE90C2F3D40A56E1C51926F31B8A8D22426CCE0",         // å•†æˆ·API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
                "d://wechatApiclient_key.pem",    // å•†æˆ·API证书私钥文件路径,本地文件路径
                "PUB_KEY_ID_0112298170022025071700291836000600",      // å¾®ä¿¡æ”¯ä»˜å…¬é’¥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
                "d://pub_key.pem"          // å¾®ä¿¡æ”¯ä»˜å…¬é’¥æ–‡ä»¶è·¯å¾„,本地文件路径
        );
        TransferToUserRequest request = new TransferToUserRequest();
        request.appid = "wxcd2b89fd2ff065f8"; //小程序id
        request.outBillNo = "plfk2020042016";
        request.transferSceneId = "1005";
        request.openid = "oKKHU5IFKpss_DIbFX1lqghFJOEg";
        request.userName = client.encrypt("施旭辉");
        request.transferAmount = 91L;
        request.transferRemark = "~~~";
        request.notifyUrl = "https://www.weixin.qq.com/wxpay/pay.php";
        request.userRecvPerception = "劳务报酬";
        request.transferSceneReportInfos = new ArrayList<>();
        {
            TransferSceneReportInfo item0 = new TransferSceneReportInfo();
            item0.infoType = "岗位类型";
            item0.infoContent = "完成订单";
            request.transferSceneReportInfos.add(item0);
            TransferSceneReportInfo item1 = new TransferSceneReportInfo();
            item1.infoType = "报酬说明";
            item1.infoContent = "订单报酬";
            request.transferSceneReportInfos.add(item1);
        };
        try {
            TransferToUserResponse response = client.testRun(request);
            // TODO: è¯·æ±‚成功,继续业务逻辑
            System.out.println(JSONObject.toJSONString(response));
        } catch (WXPayUtility.ApiException e) {
            // TODO: è¯·æ±‚失败,根据状态码执行不同的逻辑
            e.printStackTrace();
        }
    }
    private final String mchid;
    private final String certificateSerialNo;
    private final PrivateKey privateKey;
    private final String wechatPayPublicKeyId;
    private final PublicKey wechatPayPublicKey;
    public TransferToUser(String mchid, String certificateSerialNo, String privateKeyFilePath, String wechatPayPublicKeyId, String wechatPayPublicKeyFilePath) {
        this.mchid = mchid;
        this.certificateSerialNo = certificateSerialNo;
        this.privateKey = WXPayUtility.loadPrivateKeyFromPath(privateKeyFilePath);
        this.wechatPayPublicKeyId = wechatPayPublicKeyId;
        this.wechatPayPublicKey = WXPayUtility.loadPublicKeyFromPath(wechatPayPublicKeyFilePath);
    }
    public TransferToUserResponse testRun(TransferToUserRequest request) {
        String uri = PATH;
        String reqBody = WXPayUtility.toJson(request);
        Request.Builder reqBuilder = new Request.Builder().url(HOST + uri);
        reqBuilder.addHeader("Accept", "application/json");
        reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId);
        reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchid, certificateSerialNo,privateKey, METHOD, uri, reqBody));
        reqBuilder.addHeader("Content-Type", "application/json");
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody);
        reqBuilder.method(METHOD, requestBody);
        Request httpRequest = reqBuilder.build();
        // å‘送HTTP请求
        OkHttpClient client = new OkHttpClient.Builder().build();
        try (Response httpResponse = client.newCall(httpRequest).execute()) {
            String respBody = WXPayUtility.extractBody(httpResponse);
            if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
                // 2XX æˆåŠŸï¼ŒéªŒè¯åº”ç­”ç­¾å
                WXPayUtility.validateResponse(this.wechatPayPublicKeyId, this.wechatPayPublicKey,
                        httpResponse.headers(), respBody);
                // ä»ŽHTTP应答报文构建返回数据
                return WXPayUtility.fromJson(respBody, TransferToUserResponse.class);
            } else {
                throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
            }
        } catch (IOException e) {
            throw new UncheckedIOException("Sending request to " + uri + " failed.", e);
        }
    }
    public TransferToUserResponse run(TransferToUserRequest request,String name) {
        String uri = PATH;
        request.appid = WxMiniConfig.wxProperties.getSubAppId();
        request.transferSceneId = "1005";
        request.userRecvPerception = "劳务报酬";
        if(request.transferAmount >= 30){
            request.userName = this.encrypt(name);
        }
        request.transferSceneReportInfos = new ArrayList<>();
        {
            TransferSceneReportInfo item0 = new TransferSceneReportInfo();
            item0.infoType = "岗位类型";
            item0.infoContent = "完成订单";
            request.transferSceneReportInfos.add(item0);
            TransferSceneReportInfo item1 = new TransferSceneReportInfo();
            item1.infoType = "报酬说明";
            item1.infoContent = "订单报酬";
            request.transferSceneReportInfos.add(item1);
        };
        String reqBody = WXPayUtility.toJson(request);
        Request.Builder reqBuilder = new Request.Builder().url(HOST + uri);
        reqBuilder.addHeader("Accept", "application/json");
        reqBuilder.addHeader("Wechatpay-Serial", WxMiniConfig.wxProperties.getWechatPayPublicKeyId());
        reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(WxMiniConfig.wxProperties.getMchId(),
                WxMiniConfig.wxProperties.getWechatSerialNumer(),
                WXPayUtility.loadPrivateKeyFromPath(WxMiniConfig.wxProperties.getWechatPrivateKeyPath()),
                METHOD, uri, reqBody));
//        reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchid, certificateSerialNo,privateKey, METHOD, uri, reqBody));
        reqBuilder.addHeader("Content-Type", "application/json");
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody);
        reqBuilder.method(METHOD, requestBody);
        Request httpRequest = reqBuilder.build();
        // å‘送HTTP请求
        OkHttpClient client = new OkHttpClient.Builder().build();
        try (Response httpResponse = client.newCall(httpRequest).execute()) {
            String respBody = WXPayUtility.extractBody(httpResponse);
            if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
                // 2XX æˆåŠŸï¼ŒéªŒè¯åº”ç­”ç­¾å
                WXPayUtility.validateResponse(WxMiniConfig.wxProperties.getWechatPayPublicKeyId(), WXPayUtility.loadPublicKeyFromPath(WxMiniConfig.wxProperties.getWechatPubKeyPath()),
                        httpResponse.headers(), respBody);
                // ä»ŽHTTP应答报文构建返回数据
                return WXPayUtility.fromJson(respBody, TransferToUserResponse.class);
            } else {
                throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
            }
        } catch (IOException e) {
            throw new UncheckedIOException("Sending request to " + uri + " failed.", e);
        }
    }
    public TransferToUser.CancelTransferResponse cancelRun(TransferToUser.CancelTransferRequest request) {
        String uri = CANCEL_PATH;
        uri = uri.replace("{out_bill_no}", WXPayUtility.urlEncode(request.outBillNo));
        Request.Builder reqBuilder = new Request.Builder().url(HOST + uri);
        reqBuilder.addHeader("Accept", "application/json");
        reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId);
        reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(WxMiniConfig.wxProperties.getMchId(),
                WxMiniConfig.wxProperties.getWechatSerialNumer(),
                WXPayUtility.loadPrivateKeyFromPath(WxMiniConfig.wxProperties.getWechatPrivateKeyPath()),
                METHOD, uri, null));
        reqBuilder.addHeader("Content-Type", "application/json");
        RequestBody emptyBody = RequestBody.create(null, "");
        reqBuilder.method(METHOD, emptyBody);
        Request httpRequest = reqBuilder.build();
        // å‘送HTTP请求
        OkHttpClient client = new OkHttpClient.Builder().build();
        try (Response httpResponse = client.newCall(httpRequest).execute()) {
            String respBody = WXPayUtility.extractBody(httpResponse);
            if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
                // 2XX æˆåŠŸï¼ŒéªŒè¯åº”ç­”ç­¾å
                WXPayUtility.validateResponse(WxMiniConfig.wxProperties.getWechatPayPublicKeyId(), WXPayUtility.loadPublicKeyFromPath(WxMiniConfig.wxProperties.getWechatPubKeyPath()),
                        httpResponse.headers(), respBody);
                // ä»ŽHTTP应答报文构建返回数据
                return WXPayUtility.fromJson(respBody, TransferToUser.CancelTransferResponse.class);
            } else {
                throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
            }
        } catch (IOException e) {
            throw new UncheckedIOException("Sending request to " + uri + " failed.", e);
        }
    }
    public String encrypt(String plainText) {
        return WXPayUtility.encrypt(this.wechatPayPublicKey, plainText);
    }
    public static class TransferToUserResponse {
        @SerializedName("out_bill_no")
        public String outBillNo;
        @SerializedName("transfer_bill_no")
        public String transferBillNo;
        @SerializedName("create_time")
        public String createTime;
        @SerializedName("state")
        public TransferBillStatus state;
        @SerializedName("package_info")
        public String packageInfo;
    }
    public enum TransferBillStatus {
        @SerializedName("ACCEPTED")
        ACCEPTED,
        @SerializedName("PROCESSING")
        PROCESSING,
        @SerializedName("WAIT_USER_CONFIRM")
        WAIT_USER_CONFIRM,
        @SerializedName("TRANSFERING")
        TRANSFERING,
        @SerializedName("SUCCESS")
        SUCCESS,
        @SerializedName("FAIL")
        FAIL,
        @SerializedName("CANCELING")
        CANCELING,
        @SerializedName("CANCELLED")
        CANCELLED
    }
    public static class TransferSceneReportInfo {
        @SerializedName("info_type")
        public String infoType;
        @SerializedName("info_content")
        public String infoContent;
    }
    public static class TransferToUserRequest {
        @SerializedName("appid")
        public String appid;
        @SerializedName("out_bill_no")
        public String outBillNo;
        @SerializedName("transfer_scene_id")
        public String transferSceneId;
        @SerializedName("openid")
        public String openid;
        @SerializedName("user_name")
        public String userName;
        @SerializedName("transfer_amount")
        public Long transferAmount;
        @SerializedName("transfer_remark")
        public String transferRemark;
        @SerializedName("notify_url")
        public String notifyUrl;
        @SerializedName("user_recv_perception")
        public String userRecvPerception;
        @SerializedName("transfer_scene_report_infos")
        public List<TransferSceneReportInfo> transferSceneReportInfos;
    }
    public static class CancelTransferResponse {
        @SerializedName("out_bill_no")
        public String outBillNo;
        @SerializedName("transfer_bill_no")
        public String transferBillNo;
        @SerializedName("state")
        public String state;
        @SerializedName("update_time")
        public String updateTime;
    }
    public static class CancelTransferRequest {
        @SerializedName("out_bill_no")
        @Expose(serialize = false)
        public String outBillNo;
    }
}
server/services/src/main/java/com/doumee/config/wx/WXPayUtility.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,456 @@
package com.doumee.config.wx;
import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import com.google.gson.annotations.Expose;
import com.wechat.pay.java.core.util.GsonUtil;
import okhttp3.Headers;
import okhttp3.Response;
import okio.BufferedSource;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.time.DateTimeException;
import java.time.Duration;
import java.time.Instant;
import java.util.Base64;
import java.util.Map;
import java.util.Objects;
public class WXPayUtility {
    private static final Gson gson = new GsonBuilder()
            .disableHtmlEscaping()
            .addSerializationExclusionStrategy(new ExclusionStrategy() {
                @Override
                public boolean shouldSkipField(FieldAttributes fieldAttributes) {
                    final Expose expose = fieldAttributes.getAnnotation(Expose.class);
                    return expose != null && !expose.serialize();
                }
                @Override
                public boolean shouldSkipClass(Class<?> aClass) {
                    return false;
                }
            })
            .addDeserializationExclusionStrategy(new ExclusionStrategy() {
                @Override
                public boolean shouldSkipField(FieldAttributes fieldAttributes) {
                    final Expose expose = fieldAttributes.getAnnotation(Expose.class);
                    return expose != null && !expose.deserialize();
                }
                @Override
                public boolean shouldSkipClass(Class<?> aClass) {
                    return false;
                }
            })
            .create();
    private static final char[] SYMBOLS =
            "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
    private static final SecureRandom random = new SecureRandom();
    /**
     * å°† Object è½¬æ¢ä¸º JSON å­—符串
     */
    public static String toJson(Object object) {
        return gson.toJson(object);
    }
    /**
     * å°† JSON å­—符串解析为特定类型的实例
     */
    public static <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException {
        return gson.fromJson(json, classOfT);
    }
    /**
     * ä»Žå…¬ç§é’¥æ–‡ä»¶è·¯å¾„中读取文件内容
     *
     * @param keyPath æ–‡ä»¶è·¯å¾„
     * @return æ–‡ä»¶å†…容
     */
    private static String readKeyStringFromPath(String keyPath) {
        try {
            return new String(Files.readAllBytes(Paths.get(keyPath)), StandardCharsets.UTF_8);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
    /**
     * è¯»å– PKCS#8 æ ¼å¼çš„私钥字符串并加载为私钥对象
     *
     * @param keyString ç§é’¥æ–‡ä»¶å†…容,以 -----BEGIN PRIVATE KEY----- å¼€å¤´
     * @return PrivateKey å¯¹è±¡
     */
    public static PrivateKey loadPrivateKeyFromString(String keyString) {
        try {
            keyString = keyString.replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");
            return KeyFactory.getInstance("RSA").generatePrivate(
                    new PKCS8EncodedKeySpec(Base64.getDecoder().decode(keyString)));
        } catch (NoSuchAlgorithmException e) {
            throw new UnsupportedOperationException(e);
        } catch (InvalidKeySpecException e) {
            throw new IllegalArgumentException(e);
        }
    }
    /**
     * ä»Ž PKCS#8 æ ¼å¼çš„私钥文件中加载私钥
     *
     * @param keyPath ç§é’¥æ–‡ä»¶è·¯å¾„
     * @return PrivateKey å¯¹è±¡
     */
    public static PrivateKey loadPrivateKeyFromPath(String keyPath) {
        return loadPrivateKeyFromString(readKeyStringFromPath(keyPath));
    }
    /**
     * è¯»å– PKCS#8 æ ¼å¼çš„公钥字符串并加载为公钥对象
     *
     * @param keyString å…¬é’¥æ–‡ä»¶å†…容,以 -----BEGIN PUBLIC KEY----- å¼€å¤´
     * @return PublicKey å¯¹è±¡
     */
    public static PublicKey loadPublicKeyFromString(String keyString) {
        try {
            keyString = keyString.replace("-----BEGIN PUBLIC KEY-----", "")
                    .replace("-----END PUBLIC KEY-----", "")
                    .replaceAll("\\s+", "");
            return KeyFactory.getInstance("RSA").generatePublic(
                    new X509EncodedKeySpec(Base64.getDecoder().decode(keyString)));
        } catch (NoSuchAlgorithmException e) {
            throw new UnsupportedOperationException(e);
        } catch (InvalidKeySpecException e) {
            throw new IllegalArgumentException(e);
        }
    }
    /**
     * ä»Ž PKCS#8 æ ¼å¼çš„公钥文件中加载公钥
     *
     * @param keyPath å…¬é’¥æ–‡ä»¶è·¯å¾„
     * @return PublicKey å¯¹è±¡
     */
    public static PublicKey loadPublicKeyFromPath(String keyPath) {
        return loadPublicKeyFromString(readKeyStringFromPath(keyPath));
    }
    /**
     * åˆ›å»ºæŒ‡å®šé•¿åº¦çš„随机字符串,字符集为[0-9a-zA-Z],可用于安全相关用途
     */
    public static String createNonce(int length) {
        char[] buf = new char[length];
        for (int i = 0; i < length; ++i) {
            buf[i] = SYMBOLS[random.nextInt(SYMBOLS.length)];
        }
        return new String(buf);
    }
    /**
     * ä½¿ç”¨å…¬é’¥æŒ‰ç…§ RSA_PKCS1_OAEP_PADDING ç®—法进行加密
     *
     * @param publicKey åŠ å¯†ç”¨å…¬é’¥å¯¹è±¡
     * @param plaintext å¾…加密明文
     * @return åŠ å¯†åŽå¯†æ–‡
     */
    public static String encrypt(PublicKey publicKey, String plaintext) {
        final String transformation = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";
        try {
            Cipher cipher = Cipher.getInstance(transformation);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            return Base64.getEncoder().encodeToString(cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8)));
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new IllegalArgumentException("The current Java environment does not support " + transformation, e);
        } catch (InvalidKeyException e) {
            throw new IllegalArgumentException("RSA encryption using an illegal publicKey", e);
        } catch (BadPaddingException | IllegalBlockSizeException e) {
            throw new IllegalArgumentException("Plaintext is too long", e);
        }
    }
    /**
     * ä½¿ç”¨ç§é’¥æŒ‰ç…§æŒ‡å®šç®—法进行签名
     *
     * @param message å¾…签名串
     * @param algorithm ç­¾åç®—法,如 SHA256withRSA
     * @param privateKey ç­¾åç”¨ç§é’¥å¯¹è±¡
     * @return ç­¾åç»“æžœ
     */
    public static String sign(String message, String algorithm, PrivateKey privateKey) {
        byte[] sign;
        try {
            Signature signature = Signature.getInstance(algorithm);
            signature.initSign(privateKey);
            signature.update(message.getBytes(StandardCharsets.UTF_8));
            sign = signature.sign();
        } catch (NoSuchAlgorithmException e) {
            throw new UnsupportedOperationException("The current Java environment does not support " + algorithm, e);
        } catch (InvalidKeyException e) {
            throw new IllegalArgumentException(algorithm + " signature uses an illegal privateKey.", e);
        } catch (SignatureException e) {
            throw new RuntimeException("An error occurred during the sign process.", e);
        }
        return Base64.getEncoder().encodeToString(sign);
    }
    /**
     * ä½¿ç”¨å…¬é’¥æŒ‰ç…§ç‰¹å®šç®—法验证签名
     *
     * @param message å¾…签名串
     * @param signature å¾…验证的签名内容
     * @param algorithm ç­¾åç®—法,如:SHA256withRSA
     * @param publicKey éªŒç­¾ç”¨å…¬é’¥å¯¹è±¡
     * @return ç­¾åéªŒè¯æ˜¯å¦é€šè¿‡
     */
    public static boolean verify(String message, String signature, String algorithm,
                                 PublicKey publicKey) {
        try {
            Signature sign = Signature.getInstance(algorithm);
            sign.initVerify(publicKey);
            sign.update(message.getBytes(StandardCharsets.UTF_8));
            return sign.verify(Base64.getDecoder().decode(signature));
        } catch (SignatureException e) {
            return false;
        } catch (InvalidKeyException e) {
            throw new IllegalArgumentException("verify uses an illegal publickey.", e);
        } catch (NoSuchAlgorithmException e) {
            throw new UnsupportedOperationException("The current Java environment does not support" + algorithm, e);
        }
    }
    /**
     * æ ¹æ®å¾®ä¿¡æ”¯ä»˜APIv3请求签名规则构造 Authorization ç­¾å
     *
     * @param mchid å•†æˆ·å·
     * @param certificateSerialNo å•†æˆ·API证书序列号
     * @param privateKey å•†æˆ·API证书私钥
     * @param method è¯·æ±‚接口的HTTP方法,请使用全大写表述,如 GET、POST、PUT、DELETE
     * @param uri è¯·æ±‚接口的URL
     * @param body è¯·æ±‚接口的Body
     * @return æž„造好的微信支付APIv3 Authorization å¤´
     */
    public static String buildAuthorization(String mchid, String certificateSerialNo,
                                            PrivateKey privateKey,
                                            String method, String uri, String body) {
        String nonce = createNonce(32);
        long timestamp = Instant.now().getEpochSecond();
        String message = String.format("%s\n%s\n%d\n%s\n%s\n", method, uri, timestamp, nonce,
                body == null ? "" : body);
        String signature = sign(message, "SHA256withRSA", privateKey);
        return String.format(
                "WECHATPAY2-SHA256-RSA2048 mchid=\"%s\",nonce_str=\"%s\",signature=\"%s\"," +
                        "timestamp=\"%d\",serial_no=\"%s\"",
                mchid, nonce, signature, timestamp, certificateSerialNo);
    }
    /**
     * å¯¹å‚数进行 URL ç¼–码
     *
     * @param content å‚数内容
     * @return ç¼–码后的内容
     */
    public static String urlEncode(String content) {
        try {
            return URLEncoder.encode(content, StandardCharsets.UTF_8.name());
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * å¯¹å‚æ•°Map进行 URL ç¼–码,生成 QueryString
     *
     * @param params Query参数Map
     * @return QueryString
     */
    public static String urlEncode(Map<String, Object> params) {
        if (params == null || params.isEmpty()) {
            return "";
        }
        int index = 0;
        StringBuilder result = new StringBuilder();
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            result.append(entry.getKey())
                    .append("=")
                    .append(urlEncode(entry.getValue().toString()));
            index++;
            if (index < params.size()) {
                result.append("&");
            }
        }
        return result.toString();
    }
    /**
     * ä»Žåº”答中提取 Body
     *
     * @param response HTTP è¯·æ±‚应答对象
     * @return åº”答中的Body内容,Body为空时返回空字符串
     */
    public static String extractBody(Response response) {
        if (response.body() == null) {
            return "";
        }
        try {
            BufferedSource source = response.body().source();
            return source.readUtf8();
        } catch (IOException e) {
            throw new RuntimeException(String.format("An error occurred during reading response body. Status: %d", response.code()), e);
        }
    }
    /**
     * æ ¹æ®å¾®ä¿¡æ”¯ä»˜APIv3应答验签规则对应答签名进行验证,验证不通过时抛出异常
     *
     * @param wechatpayPublicKeyId å¾®ä¿¡æ”¯ä»˜å…¬é’¥ID
     * @param wechatpayPublicKey å¾®ä¿¡æ”¯ä»˜å…¬é’¥å¯¹è±¡
     * @param headers å¾®ä¿¡æ”¯ä»˜åº”ç­” Header åˆ—表
     * @param body å¾®ä¿¡æ”¯ä»˜åº”ç­” Body
     */
    public static void validateResponse(String wechatpayPublicKeyId, PublicKey wechatpayPublicKey,
                                        Headers headers,
                                        String body) {
        String timestamp = headers.get("Wechatpay-Timestamp");
        try {
            Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestamp));
            // æ‹’绝过期请求
            if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= 5) {
                throw new IllegalArgumentException(
                        String.format("Validate http response,timestamp[%s] of httpResponse is expires, "
                                        + "request-id[%s]",
                                timestamp, headers.get("Request-ID")));
            }
        } catch (DateTimeException | NumberFormatException e) {
            throw new IllegalArgumentException(
                    String.format("Validate http response,timestamp[%s] of httpResponse is invalid, " +
                                    "request-id[%s]", timestamp,
                            headers.get("Request-ID")));
        }
        String message = String.format("%s\n%s\n%s\n", timestamp, headers.get("Wechatpay-Nonce"),
                body == null ? "" : body);
        String serialNumber = headers.get("Wechatpay-Serial");
        if (!Objects.equals(serialNumber, wechatpayPublicKeyId)) {
            throw new IllegalArgumentException(
                    String.format("Invalid Wechatpay-Serial, Local: %s, Remote: %s", wechatpayPublicKeyId,
                            serialNumber));
        }
        String signature = headers.get("Wechatpay-Signature");
        boolean success = verify(message, signature, "SHA256withRSA", wechatpayPublicKey);
        if (!success) {
            throw new IllegalArgumentException(
                    String.format("Validate response failed,the WechatPay signature is incorrect.%n"
                                    + "Request-ID[%s]\tresponseHeader[%s]\tresponseBody[%.1024s]",
                            headers.get("Request-ID"), headers, body));
        }
    }
    /**
     * å¾®ä¿¡æ”¯ä»˜API错误异常,发送HTTP请求成功,但返回状态码不是 2XX æ—¶æŠ›å‡ºæœ¬å¼‚常
     */
    public static class ApiException extends RuntimeException {
        private static final long serialVersionUID = 2261086748874802175L;
        private final int statusCode;
        private final String body;
        private final Headers headers;
        private final String errorCode;
        private final String errorMessage;
        public ApiException(int statusCode, String body, Headers headers) {
            super(String.format("微信支付API访问失败,StatusCode: [%s], Body: [%s], Headers: [%s]", statusCode, body, headers));
            this.statusCode = statusCode;
            this.body = body;
            this.headers = headers;
            if (body != null && !body.isEmpty()) {
                JsonElement code;
                JsonElement message;
                try {
                    JsonObject jsonObject = GsonUtil.getGson().fromJson(body, JsonObject.class);
                    code = jsonObject.get("code");
                    message = jsonObject.get("message");
                } catch (JsonSyntaxException ignored) {
                    code = null;
                    message = null;
                }
                this.errorCode = code == null ? null : code.getAsString();
                this.errorMessage = message == null ? null : message.getAsString();
            } else {
                this.errorCode = null;
                this.errorMessage = null;
            }
        }
        /**
         * èŽ·å– HTTP åº”答状态码
         */
        public int getStatusCode() {
            return statusCode;
        }
        /**
         * èŽ·å– HTTP åº”答包体内容
         */
        public String getBody() {
            return body;
        }
        /**
         * èŽ·å– HTTP åº”ç­” Header
         */
        public Headers getHeaders() {
            return headers;
        }
        /**
         * èŽ·å– é”™è¯¯ç  ï¼ˆé”™è¯¯åº”答中的 code å­—段)
         */
        public String getErrorCode() {
            return errorCode;
        }
        /**
         * èŽ·å– é”™è¯¯æ¶ˆæ¯ ï¼ˆé”™è¯¯åº”答中的 message å­—段)
         */
        public String getErrorMessage() {
            return errorMessage;
        }
    }
}
server/services/src/main/java/com/doumee/config/wx/WxMiniConfig.java
@@ -36,7 +36,7 @@
    public static JsapiServiceExtension jsapiExtService;
    public static BillDownloadService billDownloadService;
    public static WxPayProperties wxProperties;
    public static  TransferToUser transferToUser;
    @Autowired
    private WxPayProperties wxPayProperties;
@@ -49,6 +49,8 @@
        this.load_WxMaService();
        this.load_wxPayService();
        this.load_wxPayV2Service();
        this.load_transferToUser();
        this.load_transferToUser();
        this.wxProperties = wxPayProperties;
    }
    /**
@@ -134,7 +136,18 @@
//    }
    public void load_transferToUser()
    {
        TransferToUser transferToUser = new TransferToUser(
                StringUtils.trimToNull(wxPayProperties.getMchId()), //商户id
                StringUtils.trimToNull(wxPayProperties.getWechatSerialNumer()), //商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
                StringUtils.trimToNull(wxPayProperties.getWechatPrivateKeyPath()), // å•†æˆ·API证书私钥文件路径,本地文件路径
                StringUtils.trimToNull(wxPayProperties.getWechatPayPublicKeyId()),   // å¾®ä¿¡æ”¯ä»˜å…¬é’¥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
                StringUtils.trimToNull(wxPayProperties.getWechatPubKeyPath()) // å¾®ä¿¡æ”¯ä»˜å…¬é’¥æ–‡ä»¶è·¯å¾„,本地文件路径
        );
        this.transferToUser = transferToUser;
    }
}
server/services/src/main/java/com/doumee/config/wx/WxPayProperties.java
@@ -63,4 +63,10 @@
    private String privateKeyPath ;
    private String privateCertPath;// gybike
    private String wechatSerialNumer;
    private String wechatPayPublicKeyId; // å¾®ä¿¡æ”¯ä»˜å…¬é’¥ID
    private String wechatPubKeyPath; //微信支付公钥文件路径
    private String wechatPrivateKeyPath;
}
server/services/src/main/java/com/doumee/dao/business/model/Member.java
@@ -190,4 +190,11 @@
    @TableField(exist = false)
    private IdentityInfo chefIdentityModel;
    @ApiModelProperty(value = "接单权重", example = "1")
    @TableField(exist = false)
    private Integer level;
    @ApiModelProperty(value = "距离", example = "1")
    @TableField(exist = false)
    private BigDecimal distance;
}
server/services/src/main/java/com/doumee/dao/business/model/MemberRevenue.java
@@ -47,7 +47,7 @@
    @ApiModelProperty(value = "会员主键", example = "1")
    private Integer memberId;
    @ApiModelProperty(value = "变动类型:0=用工单收入;1=货运单收入;2=供餐单收入;3=提现申请;", example = "1")
    @ApiModelProperty(value = "变动类型:0=用工单收入;1=货运单收入;2=供餐单收入;3=提现申请;4=提现失败退回", example = "1")
    private Integer type;
    @ApiModelProperty(value = "收支类型:1=收入;-1=支出;", example = "1")
server/services/src/main/java/com/doumee/dao/business/model/Orders.java
@@ -90,10 +90,10 @@
    @ApiModelProperty(value = "用工类型:0=采摘工;1=分拣工;2=包装工;(用工订单)", example = "1")
    private Integer workType;
    @ApiModelProperty(value = "计价数量1(天数/用车次数/小时/斤数)", example = "1")
    @ApiModelProperty(value = "计价数量1((重量/人数/(天数/小时/重量)/(天数/次数)/用餐天数)", example = "1")
    private Integer priceNum1;
    @ApiModelProperty(value = "计价数量2(人数/用餐份数)", example = "1")
    @ApiModelProperty(value = "计价数量2(分拣工/包装工 äººæ•°)", example = "1")
    private Integer priceNum2;
    @ApiModelProperty(value = "需求补充")
@@ -102,10 +102,8 @@
    @ApiModelProperty(value = "费用标准", example = "1")
    private Long price;
    @ApiModelProperty(value = "预估费用", example = "1")
    private Long estimatedAccount;
    @ApiModelProperty(value = "实际支付费用", example = "1")
    private Long payAccount;
server/services/src/main/java/com/doumee/dao/business/model/WithdrawalOrders.java
@@ -27,7 +27,7 @@
    @ApiModelProperty(value = "是否已删除 0未删除 1已删除", example = "1")
    @ExcelColumn(name="是否已删除 0未删除 1已删除")
    private Byte deleted;
    private Integer deleted;
    @ApiModelProperty(value = "创建人编码", example = "1")
    @ExcelColumn(name="创建人编码")
@@ -84,4 +84,10 @@
    @ApiModelProperty(value = "关联订单主键 orders", example = "1")
    @ExcelColumn(name="关联订单主键 orders")
    private Integer objId;
    @ApiModelProperty(value = "提现申请单号")
    @ExcelColumn(name="提现申请单号")
    private String outBillNo;
}
server/services/src/main/java/com/doumee/dao/vo/OrderReleaseVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package com.doumee.dao.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Rk
 * @create 2025/7/10 15:09
 */
@Data
public class OrderReleaseVO {
    @ApiModelProperty(value = "订单主键")
    private Integer id;
    @ApiModelProperty(value = "支付详情")
    private Object object;
}
server/services/src/main/java/com/doumee/service/business/MemberService.java
@@ -149,4 +149,6 @@
    Member getMemberInfo(Integer memberId);
    UserCenterVO getPlatformAboutUs();
    void logOff(String token,Integer memberId);
}
server/services/src/main/java/com/doumee/service/business/OrdersService.java
@@ -9,6 +9,7 @@
import com.doumee.dao.dto.ConfirmUpdOrderDTO;
import com.doumee.dao.dto.DoneOrderDTO;
import com.doumee.dao.dto.UpdOrderDataDTO;
import com.doumee.dao.vo.OrderReleaseVO;
import com.wechat.pay.java.service.refund.model.RefundNotification;
import java.util.List;
@@ -26,7 +27,7 @@
     * @param orders å®žä½“对象
     * @return Integer
     */
    Object create(Orders orders);
    OrderReleaseVO create(Orders orders);
    /**
     * ä¸»é”®åˆ é™¤
@@ -137,4 +138,10 @@
    void autoComment();
    void autoConfirm();
    Long getTotal(Orders orders);
    void autoGrabOrders();
    void initializeCode();
}
server/services/src/main/java/com/doumee/service/business/WithdrawalOrdersService.java
@@ -1,5 +1,6 @@
package com.doumee.service.business;
import com.doumee.config.wx.TransferToUser;
import com.doumee.core.model.PageData;
import com.doumee.core.model.PageWrap;
import com.doumee.dao.business.model.WithdrawalOrders;
@@ -97,6 +98,8 @@
     */
    long count(WithdrawalOrders withdrawalOrders);
    void  applyWithdrawal(WithdrawalDTO withdrawalDTO);
    TransferToUser.TransferToUserResponse  applyWithdrawal(WithdrawalDTO withdrawalDTO);
    void cancelTransfer(TransferToUser.CancelTransferRequest request);
}
server/services/src/main/java/com/doumee/service/business/impl/CategoryServiceImpl.java
@@ -254,10 +254,15 @@
        List<Category> categoryList = categoryMapper.selectList(new QueryWrapper<Category>().lambda().eq(Category::getDeleted,Constants.ZERO).eq(Category::getStatus,Constants.ZERO)
                .eq(Objects.nonNull(type),Category::getType,type));
        if(com.github.xiaoymin.knife4j.core.util.CollectionUtils.isNotEmpty(categoryList)){
            String path  = systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.RESOURCE_PATH).getCode()
                    +systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.CATEGORY_FILES).getCode();
            for (Category category:categoryList) {
                if(StringUtils.isNotBlank(category.getDetail())){
                    category.setDetailList(JSONArray.parseArray(category.getDetail()));
                }
                if(StringUtils.isNotBlank(category.getIcon())){
                    category.setIconFull(path + category.getIcon());
                }
            }
        }
        return categoryList;
server/services/src/main/java/com/doumee/service/business/impl/MemberServiceImpl.java
@@ -2,6 +2,7 @@
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import com.alibaba.fastjson.JSONObject;
import com.doumee.biz.system.SystemDictDataBiz;
import com.doumee.config.jwt.JwtTokenUtil;
import com.doumee.config.wx.WxMiniConfig;
@@ -33,13 +34,16 @@
import nonapi.io.github.classgraph.json.Id;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
@@ -342,7 +346,10 @@
    @Override
    public void editMemberInfo(Member member){
        if(Objects.isNull(member)
            || ( StringUtils.isBlank(member.getCoverImage()) || StringUtils.isBlank(member.getName()) || StringUtils.isBlank(member.getNickName()) || Objects.isNull(member.getAutoReceiveStatus()) )){
            || ( StringUtils.isBlank(member.getCoverImage())
                && StringUtils.isBlank(member.getName())
                && StringUtils.isBlank(member.getNickName())
                && Objects.isNull(member.getAutoReceiveStatus()) )){
            throw new BusinessException(ResponseStatus.BAD_REQUEST);
        }
        memberMapper.update(new UpdateWrapper<Member>().lambda()
@@ -381,6 +388,11 @@
        Member member  = this.findById(memberId);
        if(Objects.isNull(member)){
            throw new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        if(StringUtils.isNotBlank(member.getCoverImage())){
            String path  = systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.RESOURCE_PATH).getCode()
                    +systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.MEMBER_FILES).getCode();
            member.setFullCoverImage(path + member.getCoverImage());
        }
        UserCenterVO userCenterVO = new UserCenterVO();
        userCenterVO.setReleaseTaskTotal(Constants.ZERO);
@@ -422,6 +434,11 @@
        return userCenterVO;
    }
    @Override
    public void logOff(String token,Integer memberId){
        memberMapper.update(new UpdateWrapper<Member>().lambda().setSql(" openid = null ").eq(Member::getId,memberId));
        jwtTokenUtil.logoutForH5(token);
    }
server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java
@@ -17,6 +17,7 @@
import com.doumee.dao.business.*;
import com.doumee.dao.business.model.*;
import com.doumee.dao.dto.*;
import com.doumee.dao.vo.OrderReleaseVO;
import com.doumee.service.business.OrdersService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
@@ -42,10 +43,7 @@
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
@@ -91,7 +89,8 @@
    @Override
    @Transactional(rollbackFor = {Exception.class,BusinessException.class})
    public Object create(Orders orders) {
    public OrderReleaseVO create(Orders orders) {
        OrderReleaseVO orderReleaseVO = new OrderReleaseVO();
        Object objects = null;
        this.initVerification(orders);
        orders.setCreateTime(new Date());
@@ -120,15 +119,16 @@
            orders.setStatus(Constants.ordersStatus.waitPay.getKey());
            //唤起支付业务
            objects = this.getWxPayResponse(orders,orders.getMember().getOpenid());
            orderReleaseVO.setObject(objects);
        }else{
            orders.setStatus(Constants.ordersStatus.wait.getKey());
        }
        ordersMapper.insert(orders);
        orderReleaseVO.setId(orders.getId());
        if(com.github.xiaoymin.knife4j.core.util.CollectionUtils.isNotEmpty(orders.getMultifileList())){
            List<Multifile> multifileList = orders.getMultifileList();
            for (Multifile multifile:multifileList) {
                if(Objects.isNull(multifile)
                || Objects.isNull(multifile.getObjType())
                || StringUtils.isEmpty(multifile.getFileurl())
                || StringUtils.isEmpty(multifile.getName())){
                    throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(),"附件信息错误");
@@ -159,7 +159,7 @@
                .replace("{param}",orderTypeInfo);
        this.saveOrderLog(orders,ordersLog,
                logInfo,orders.getReleaseMemberId(),null);
        return objects;
        return orderReleaseVO;
    }
    private Object getWxPayResponse(Orders orders,String openid){
@@ -219,7 +219,7 @@
                || Objects.isNull(orders.getStartDate())
                || Objects.isNull(orders.getEndDate())
                || StringUtil.isBlank(orders.getLocation())
                || StringUtil.isBlank(orders.getLocationRemark())
                || (!Constants.equalsInteger(Constants.ONE,orders.getType())&&StringUtil.isBlank(orders.getLocationRemark()))
                || StringUtil.isBlank(orders.getLinkPhone())
                || Objects.isNull(orders.getLat())
                || Objects.isNull(orders.getLgt())
@@ -263,9 +263,9 @@
            for (WayInfoDTO wayInfoDTO:orders.getWayInfoDTOList()) {
                if(Objects.isNull(wayInfoDTO)
                        ||StringUtils.isEmpty(wayInfoDTO.getLocation())
                        ||StringUtils.isEmpty(wayInfoDTO.getProvince())
                        ||StringUtils.isEmpty(wayInfoDTO.getCity())
                        ||StringUtils.isEmpty(wayInfoDTO.getArea())
//                        ||StringUtils.isEmpty(wayInfoDTO.getProvince())
//                        ||StringUtils.isEmpty(wayInfoDTO.getCity())
//                        ||StringUtils.isEmpty(wayInfoDTO.getArea())
                        ||Objects.isNull(wayInfoDTO.getLat())
                        ||Objects.isNull(wayInfoDTO.getLgt())
                ){
@@ -298,6 +298,9 @@
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(),"金额计算错误!");
        }
    }
@@ -600,12 +603,17 @@
                throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(),"身份信息异常,请联系管理员");
            }
        }
        orders.setUpdateTime(new Date());
        orders.setAcceptType(Constants.ZERO);
        orders.setAcceptTime(new Date());
        orders.setAcceptMemberId(member.getId());
        orders.setStatus(Constants.ordersStatus.accept.getKey());
        ordersMapper.updateById(orders);
        ordersMapper.update(new UpdateWrapper<Orders>().lambda().eq(Orders::getId,orders.getId())
                .set(Orders::getUpdateTime,new Date())
                .set(Orders::getAcceptType,Constants.ZERO)
                .set(Orders::getAcceptTime,new Date())
                .set(Orders::getAcceptMemberId,member.getId())
                .set(Orders::getStatus,Constants.ordersStatus.accept.getKey())
        );
        //更新接单量
        memberMapper.update(new UpdateWrapper<Member>().lambda().setSql(" publish_num = (publish_num + 1 )").eq(Member::getId,member.getId()));
@@ -701,8 +709,8 @@
                .set(Orders::getEndDate,updOrderDataDTO.getEndDate())
                .set(Orders::getTotalDays,updOrderDataDTO.getTotalDays())
                .set(Orders::getIsUpdate,Constants.ONE)
                .set(Orders::getIsUpdateTime,"now()")
                .set(Orders::getUpdateTime,"now()")
                .set(Orders::getIsUpdateTime,new Date())
                .set(Orders::getUpdateTime,new Date())
                .set(Orders::getEstimatedAccount,total)
                .eq(Orders::getId,orders.getId())
        );
@@ -715,12 +723,22 @@
    }
    @Override
    public Long getTotal(Orders orders){
        if(Objects.isNull(orders)
                ||Objects.isNull(orders.getPrice())
                ||Objects.isNull(orders.getPriceNum1())){
            throw new BusinessException(ResponseStatus.BAD_REQUEST);
        }
        if(
            (Constants.equalsInteger(orders.getType(),Constants.ZERO) && Constants.equalsInteger(orders.getWorkType(),Constants.ZERO ))
            || Constants.equalsInteger(orders.getType(),Constants.ONE)
        ){
            orders.setPriceNum2(Constants.ONE);
        }else{
            if(Objects.isNull(orders.getPriceNum2())){
                throw new BusinessException(ResponseStatus.BAD_REQUEST);
            }
        }
        return orders.getPrice() * orders.getPriceNum1() * orders.getPriceNum2();
    }
@@ -757,7 +775,7 @@
        if( Constants.equalsInteger(confirmUpdOrderDTO.getStatus(),Constants.ONE)){
            ordersMapper.update(new UpdateWrapper<Orders>().lambda()
                    .set(Orders::getIsUpdate,Constants.TWO)
                    .set(Orders::getUpdateTime,"now()")
                    .set(Orders::getUpdateTime,new Date())
                    .eq(Orders::getId,orders.getId())
            );
            //记录同意修改的日志
@@ -767,7 +785,7 @@
        }else{
            ordersMapper.update(new UpdateWrapper<Orders>().lambda()
                    .set(Orders::getIsUpdate,Constants.TWO)
                    .set(Orders::getUpdateTime,"now()")
                    .set(Orders::getUpdateTime,new Date())
                    .set(Orders::getStatus,Constants.ordersStatus.wait.getKey())
                    .set(Orders::getAcceptMemberId,null)
                    .set(Orders::getAcceptType,null)
@@ -824,8 +842,8 @@
            }
            ordersMapper.update(new UpdateWrapper<Orders>().lambda()
                    .set(Orders::getStatus,Constants.ordersStatus.cancel.getKey())
                    .set(Orders::getUpdateTime,"now()")
                    .set(Orders::getCancelTime,"now()")
                    .set(Orders::getUpdateTime,new Date())
                    .set(Orders::getCancelTime,new Date())
                    .set(Orders::getCancelType,Constants.ZERO)
                    .eq(Orders::getId,orders.getId())
            );
@@ -862,8 +880,8 @@
            }
            ordersMapper.update(new UpdateWrapper<Orders>().lambda()
                    .set(Orders::getStatus,Constants.ordersStatus.cancel.getKey())
                    .set(Orders::getUpdateTime,"now()")
                    .set(Orders::getCancelTime,"now()")
                    .set(Orders::getUpdateTime,new Date())
                    .set(Orders::getCancelTime,new Date())
                    .set(Orders::getCancelType,Constants.ONE)
                    .eq(Orders::getId,orders.getId())
            );
@@ -917,7 +935,7 @@
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(),"距离订单开始时间不足"+cancelTimeHour+"小时,无法取消订单,如需处理请联系客服");
        }
        ordersMapper.update(new UpdateWrapper<Orders>().lambda()
                .set(Orders::getUpdateTime,"now()")
                .set(Orders::getUpdateTime,new Date())
                .set(Orders::getStatus,Constants.ordersStatus.wait.getKey())
                .set(Orders::getAcceptMemberId,null)
                .set(Orders::getAcceptType,null)
@@ -1059,8 +1077,8 @@
        ordersMapper.update(new UpdateWrapper<Orders>().lambda()
                .set(Orders::getCommentStatus,Constants.ONE)
                .set(Orders::getUpdateTime,"now()")
                .set(Orders::getCommentTime,"now()")
                .set(Orders::getUpdateTime,new Date())
                .set(Orders::getCommentTime,new Date())
                .set(Orders::getCommentLevel,commentDTO.getLevel())
                .set(Orders::getCommentType,Constants.ZERO)
                .eq(Orders::getId,orders.getId())
@@ -1098,10 +1116,10 @@
            }else{
                //处理支付完成逻辑
                ordersMapper.update(new UpdateWrapper<Orders>().lambda()
                        .set(Orders::getUpdateTime,"now()")
                        .set(Orders::getUpdateTime,new Date())
                        .set(Orders::getStatus,Constants.ordersStatus.wait)
                        .set(Orders::getPayStatus,Constants.ONE)
                        .set(Orders::getPayTime,"now()")
                        .set(Orders::getPayTime,new Date())
                        .set(Orders::getPayMethod,Constants.ZERO)
                        .set(Orders::getWxExternalNo,paymentNo)
                        .eq(Orders::getId,orders.getId())
@@ -1119,13 +1137,13 @@
            }else{
                //处理支付完成逻辑
                ordersMapper.update(new UpdateWrapper<Orders>().lambda()
                        .set(Orders::getUpdateTime,"now()")
                        .set(Orders::getUpdateTime,new Date())
                        .set(Orders::getStatus,Constants.FOUR)
                        .set(Orders::getPayStatus,Constants.ONE)
                        .set(Orders::getPayTime,"now()")
                        .set(Orders::getPayTime,new Date())
                        .set(Orders::getPayMethod,Constants.ZERO)
                        .set(Orders::getWxExternalNo,paymentNo)
                        .set(Orders::getFinishTime,"now()")
                        .set(Orders::getFinishTime,new Date())
                        .eq(Orders::getId,orders.getId())
                );
@@ -1367,19 +1385,60 @@
    }
    //todo è‡ªåŠ¨æ´¾å•
    /**
     * è‡ªåŠ¨æ´¾å•
     */
    @Override
    public void autoGrabOrders(){
//        String autoConfirmTime = systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.AUTO_DISPATCH_DISTANCE).getCode();
//        List<Orders> ordersList = ordersMapper.selectList(new QueryWrapper<Orders>().lambda()
//                .eq(Orders::getStatus,Constants.FOUR).eq(Orders::getCommentStatus,Constants.ZERO)
//                .apply(" DATE_ADD(finish_time, INTERVAL 7 DAY) < now() ")
//                .last("limit 100")
//        );
        String autoConfirmTime = systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.AUTO_DISPATCH).getCode();
        List<Orders> ordersList = ordersMapper.selectList(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getStatus,Constants.ONE).eq(Orders::getCommentStatus,Constants.ZERO)
                .apply(" DATE_ADD(create_time, INTERVAL "+autoConfirmTime+" MINUTE) < now() ")
                .last("limit 100")
        );
        for (Orders orders:ordersList) {
            BigDecimal lat = orders.getLat();
            BigDecimal lgt = orders.getLgt();
            //查询范围内的会员
            List<Member> memberList = memberMapper.selectList(new MPJLambdaWrapper<Member>().selectAll(Member.class)
                            .select(" ifnull((select r.level from receive_weight r where r.RECEIVE_MAX > t.RECEIVE_NUM and t.RECEIVE_NUM > r.RECEIVE_MIN limit 1  ),0) " ,Member::getLevel)
                            .select(  " ifnull( (select CONVERT( ST_Distance_Sphere ( POINT ( ii.lgt, ii.lat ), POINT ( "+lgt+", "+lat+" )) /1000,DECIMAL(15,2)) from identity_info ii where ii.AUDIT_STATUS = 2 and type = 0 and ii.member_id = t.ID limit  1 ),0) ",Member::getDistance )
                    .apply(" id in (" +
                            " select ii.member_id from identity_info ii where ii.AUDIT_STATUS = 2 and type = '"+orders.getType()+"' " +
                            " and ( CONVERT( ST_Distance_Sphere ( POINT ( ii.lgt, ii.lat ), POINT ( "+lgt+", "+lat+" )) /1000,DECIMAL(15,2))) < 100 " +
                            ") ")
                    .orderByDesc(Member::getLevel)
                    .orderByDesc(Member::getScore)
                    .orderByAsc(Member::getDistance)
                    .last(" limit 1 ")
            );
            if(CollectionUtils.isEmpty(memberList)){
                continue;
            }
            Member member = memberList.get(Constants.ZERO);
            //自动派单
            Orders model = ordersMapper.selectById(orders.getStatus());
            if(!Constants.equalsInteger(model.getStatus(),Constants.ONE)){
                continue;
            }
            ordersMapper.update(new UpdateWrapper<Orders>().lambda().eq(Orders::getId,model.getId())
                    .set(Orders::getUpdateTime,new Date())
                    .set(Orders::getAcceptType,Constants.ONE)
                    .set(Orders::getAcceptTime,new Date())
                    .set(Orders::getAcceptMemberId,member.getId())
                    .set(Orders::getStatus,Constants.ordersStatus.accept.getKey())
            );
            //更新接单量
            memberMapper.update(new UpdateWrapper<Member>().lambda().setSql(" publish_num = (publish_num + 1 )").eq(Member::getId,member.getId()));
            //创建操作日志
            Constants.OrdersLog ordersLog = Constants.OrdersLog.AUTO;
            this.saveOrderLog(orders,ordersLog,
                    ordersLog.getInfo(),member.getId(),null);
        }
    }
    //自动评价 è®¢å•完成7天后自动评价4星
@@ -1395,8 +1454,8 @@
        for (Orders orders:ordersList) {
            ordersMapper.update(new UpdateWrapper<Orders>().lambda()
                    .set(Orders::getCommentStatus,Constants.ONE)
                    .set(Orders::getUpdateTime,"now()")
                    .set(Orders::getCommentTime,"now()")
                    .set(Orders::getUpdateTime,new Date())
                    .set(Orders::getCommentTime,new Date())
                    .set(Orders::getCommentLevel,Constants.FOUR)
                    .set(Orders::getCommentType,Constants.ZERO)
                    .eq(Orders::getId,orders.getId())
@@ -1430,7 +1489,7 @@
            for (Orders orders:ordersList) {
                ordersMapper.update(new UpdateWrapper<Orders>().lambda()
                        .set(Orders::getIsUpdate,Constants.TWO)
                        .set(Orders::getUpdateTime,"now()")
                        .set(Orders::getUpdateTime,new Date())
                        .eq(Orders::getId,orders.getId())
                );
                //记录同意修改的日志
@@ -1442,6 +1501,11 @@
    }
    @Override
    public  void initializeCode(){
        //更新缓存
        redisTemplate.opsForValue().set(Constants.RedisKeys.ORDER_CODE,0);
    }
server/services/src/main/java/com/doumee/service/business/impl/WithdrawalOrdersServiceImpl.java
@@ -1,5 +1,10 @@
package com.doumee.service.business.impl;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.doumee.config.wx.TransferToUser;
import com.doumee.config.wx.WXPayUtility;
import com.doumee.config.wx.WxMiniConfig;
import com.doumee.core.constants.Constants;
import com.doumee.core.constants.ResponseStatus;
import com.doumee.core.exception.BusinessException;
@@ -22,10 +27,12 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
/**
 * æçŽ°ç”³è¯·è®°å½•Service实现
@@ -171,10 +178,9 @@
     * æçŽ°ç”³è¯·
     * @param withdrawalDTO
     */
    @Override
    @Transactional(rollbackFor = {BusinessException.class,Exception.class})
    public void  applyWithdrawal(WithdrawalDTO withdrawalDTO){
    public TransferToUser.TransferToUserResponse  applyWithdrawal(WithdrawalDTO withdrawalDTO){
        if(Objects.isNull(withdrawalDTO)
        || Objects.isNull(withdrawalDTO.getAmount())){
            throw new BusinessException(ResponseStatus.BAD_REQUEST);
@@ -186,23 +192,108 @@
        if(member.getAmount() < withdrawalDTO.getAmount()){
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(),"对不起,可提现余额不足。");
        }
        if(StringUtils.isEmpty(member.getName())){
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(),"请先去维护真实姓名后进行提现申请");
        }
        WithdrawalOrders withdrawalOrders = new WithdrawalOrders();
        withdrawalOrders.setCreateTime(new Date());
        withdrawalOrders.setMemberId(member.getId());
        withdrawalOrders.setOutBillNo(UUID.randomUUID().toString().replace("-",""));
        withdrawalOrders.setAmount(withdrawalDTO.getAmount());
        withdrawalOrders.setStatus(Constants.ZERO);
        withdrawalOrders.setType(Constants.ZERO);
        withdrawalOrders.setDeleted(Constants.ZERO);
        //TODO å‘起提现申请
        //发起提现申请
        TransferToUser.TransferToUserRequest transferToUserRequest = new TransferToUser.TransferToUserRequest();
        transferToUserRequest.openid = member.getOpenid();
        transferToUserRequest.outBillNo =  withdrawalOrders.getOutBillNo();
        transferToUserRequest.transferAmount = withdrawalDTO.getAmount();
        transferToUserRequest.transferRemark = "提现申请";
//        transferToUserRequest.userName = member.getName();
        try {
            TransferToUser.TransferToUserResponse response =  WxMiniConfig.transferToUser.run(transferToUserRequest,member.getName());
            withdrawalOrders.setRemark(JSONObject.toJSONString(response));
            if(response.state.name().equals("WAIT_USER_CONFIRM") || response.state.name().equals("ACCEPTED")){
                withdrawalOrders.setWxExternalNo(response.transferBillNo);
            }
            withdrawalOrdersMapper.insert(withdrawalOrders);
            //更新用户余额
            memberMapper.update(new UpdateWrapper<Member>().lambda().setSql(" AMOUNT = AMOUNT -  " + withdrawalDTO.getAmount() ).eq(Member::getId,member.getId()));
            //存储流水记录
            MemberRevenue memberRevenue = new MemberRevenue();
            memberRevenue.setCreateTime(new Date());
            memberRevenue.setMemberId(member.getId());
            memberRevenue.setType(Constants.THREE);
            memberRevenue.setOptType(-Constants.ONE);
            memberRevenue.setBeforeAmount(member.getAmount());
            memberRevenue.setAmount(withdrawalOrders.getAmount());
            memberRevenue.setAfterAmount(member.getAmount() - withdrawalOrders.getAmount());
            memberRevenue.setObjId(withdrawalOrders.getId());
            memberRevenue.setObjType(Constants.ONE);
            memberRevenue.setDeleted(Constants.ZERO);
            memberRevenue.setStatus(Constants.TWO);
            memberRevenueMapper.insert(memberRevenue);
            return response;
        } catch (WXPayUtility.ApiException e) {
            String message = JSONObject.parseObject(e.getBody()).getString("message");
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(),message);
        }
    }
    @Override
    public void cancelTransfer(TransferToUser.CancelTransferRequest request){
        if(Objects.isNull(request)
        || StringUtils.isEmpty(request.outBillNo)){
            throw new BusinessException(ResponseStatus.BAD_REQUEST);
        }
        WithdrawalOrders withdrawalOrders = withdrawalOrdersMapper.selectOne(new QueryWrapper<WithdrawalOrders>().lambda().eq(WithdrawalOrders::getOutBillNo,request.outBillNo).last("limit 1"));
        if(Objects.isNull(withdrawalOrders)){
            throw new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        Member member = memberMapper.selectById(withdrawalOrders.getMemberId());
        if(Objects.isNull(member)){
            throw new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        try {
            TransferToUser.CancelTransferResponse response =  WxMiniConfig.transferToUser.cancelRun(request);
            if(response.state.equals("CANCELING")){
                //标记提现失败 åŠ å›žå¯¹åº”çš„æçŽ°é‡‘é¢
                withdrawalOrdersMapper.update(new UpdateWrapper<WithdrawalOrders>()
                        .lambda().set(WithdrawalOrders::getStatus,Constants.TWO)
                        .eq(WithdrawalOrders::getId,withdrawalOrders.getId()));
        //更新用户余额
                memberRevenueMapper.update(new UpdateWrapper<MemberRevenue>().lambda().set(MemberRevenue::getStatus,Constants.ONE)
                                .set(MemberRevenue::getUpdateTime,new Date())
                        .eq(MemberRevenue::getObjId,withdrawalOrders.getId()))
                ;
                //更新用户余额
                memberMapper.update(new UpdateWrapper<Member>().lambda().setSql(" AMOUNT = AMOUNT + " + withdrawalOrders.getAmount() ).eq(Member::getId,withdrawalOrders.getMemberId()));
        //存储流水记录
                //存储流水记录
                MemberRevenue memberRevenue = new MemberRevenue();
                memberRevenue.setCreateTime(new Date());
                memberRevenue.setMemberId(withdrawalOrders.getMemberId());
                memberRevenue.setType(Constants.FOUR);
                memberRevenue.setOptType(Constants.ONE);
                memberRevenue.setBeforeAmount(member.getAmount());
                memberRevenue.setAmount(withdrawalOrders.getAmount());
                memberRevenue.setAfterAmount(member.getAmount() + withdrawalOrders.getAmount());
                memberRevenue.setObjId(withdrawalOrders.getId());
                memberRevenue.setObjType(Constants.ONE);
                memberRevenue.setDeleted(Constants.ZERO);
                memberRevenue.setStatus(Constants.ZERO);
                memberRevenueMapper.insert(memberRevenue);
            }
            System.out.println(JSONObject.toJSONString(response));
        } catch (WXPayUtility.ApiException e) {
            String message = JSONObject.parseObject(e.getBody()).getString("message");
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(),message);
        }
    }
server/services/src/main/resources/application-dev.yml
@@ -85,7 +85,7 @@
  pay:
    #服务商---------start-------
    appId: wx48fd8faa35cc8277
    mchId: 1661770902
    mchId: 1229817002 #1661770902
    apiV3Key: iF3kC8pL8dZ9iU3hN5fX9zI6eF4xQ6fT
    serialNumer: 368B835A194384FD583B83B77977B84127D2F655
    mchKey: W97N53Q71326D6JZ2E9HY5M4VT4BAC8S
@@ -96,9 +96,17 @@
    #keyPath: /usr/local/aliConfig/bike/apiclient_cert.p12
    #privateCertPath: /usr/local/aliConfig/bike/apiclient_cert.pem
    #privateKeyPath: /usr/local/aliConfig/bike/apiclient_key.pem
    #    keyPath: d://apiclient_cert.p12
    #    privateCertPath: d://apiclient_cert.pem
    #    privateKeyPath: d://apiclient_key.pem
    keyPath: d://apiclient_cert.p12
    privateCertPath: d://apiclient_cert.pem
    privateKeyPath: d://apiclient_key.pem
    #商户信息
    wechatSerialNumer: 3FE90C2F3D40A56E1C51926F31B8A8D22426CCE0
    wechatPayPublicKeyId: PUB_KEY_ID_0112298170022025071700291836000600
    wechatPubKeyPath: d://pub_key.pem
    wechatPrivateKeyPath: d://wechatApiclient_key.pem
    #服务商-------------end---
    existsSub: 1
    appSecret: 1ceb7c9dff3c4330d653adc3ca55ea24
server/web/src/main/java/com/doumee/api/web/AccountApi.java
@@ -1,5 +1,6 @@
package com.doumee.api.web;
import com.doumee.config.jwt.JwtTokenUtil;
import com.doumee.core.annotation.LoginRequired;
import com.doumee.core.annotation.trace.Trace;
import com.doumee.core.model.ApiResponse;
@@ -53,6 +54,16 @@
        return  ApiResponse.success("操作成功",memberService.wxAuthPhone(wxPhoneRequest));
    }
    @LoginRequired
    @ApiOperation(value = "退出登录", notes = "小程序端")
    @GetMapping("/logOff")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true),
    })
    public ApiResponse logOff() {
        String token = this.getRequest().getHeader(JwtTokenUtil.HEADER_KEY);
        memberService.logOff(token,getMemberId());
        return  ApiResponse.success("操作成功");
    }
}
server/web/src/main/java/com/doumee/api/web/ConfigApi.java
@@ -1,5 +1,6 @@
package com.doumee.api.web;
import com.doumee.core.annotation.LoginRequired;
import com.doumee.core.annotation.trace.Trace;
import com.doumee.core.model.ApiResponse;
import com.doumee.dao.business.model.Category;
@@ -34,6 +35,7 @@
    @Autowired
    private CategoryService categoryService;
    @LoginRequired
    @ApiOperation(value = "获取分类列表", notes = "小程序端")
    @GetMapping("/getCategoryList")
    @ApiImplicitParams({
server/web/src/main/java/com/doumee/api/web/OrdersApi.java
@@ -13,6 +13,7 @@
import com.doumee.dao.dto.ConfirmUpdOrderDTO;
import com.doumee.dao.dto.DoneOrderDTO;
import com.doumee.dao.dto.UpdOrderDataDTO;
import com.doumee.dao.vo.OrderReleaseVO;
import com.doumee.service.business.IdentityInfoService;
import com.doumee.service.business.MemberService;
import com.doumee.service.business.OrdersService;
@@ -51,7 +52,7 @@
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true),
    })
    public ApiResponse<Object> release(@RequestBody Orders orders) {
    public ApiResponse<OrderReleaseVO> release(@RequestBody Orders orders) {
        orders.setMember(this.getMemberResponse());
        return  ApiResponse.success("操作成功",ordersService.create(orders));
    }
@@ -185,6 +186,15 @@
        return  ApiResponse.success("操作成功",ordersService.getDetail(orderId,this.getMemberResponse().getUseIdentity()));
    }
    @LoginRequired
    @ApiOperation("获取预计金额(分)")
    @PostMapping("/getTotal")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true),
    })
    public ApiResponse<Long> getTotal(@RequestBody Orders orders) {
        return ApiResponse.success(ordersService.getTotal(orders));
    }
}
server/web/src/main/java/com/doumee/api/web/UserApi.java
@@ -1,5 +1,6 @@
package com.doumee.api.web;
import com.doumee.config.wx.TransferToUser;
import com.doumee.core.annotation.LoginRequired;
import com.doumee.core.annotation.trace.Trace;
import com.doumee.core.model.ApiResponse;
@@ -8,12 +9,14 @@
import com.doumee.dao.business.model.IdentityInfo;
import com.doumee.dao.business.model.Member;
import com.doumee.dao.business.model.MemberRevenue;
import com.doumee.dao.dto.WithdrawalDTO;
import com.doumee.dao.dto.WxPhoneRequest;
import com.doumee.dao.vo.AccountResponse;
import com.doumee.dao.vo.UserCenterVO;
import com.doumee.service.business.IdentityInfoService;
import com.doumee.service.business.MemberRevenueService;
import com.doumee.service.business.MemberService;
import com.doumee.service.business.WithdrawalOrdersService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
@@ -46,6 +49,9 @@
    @Autowired
    private MemberRevenueService memberRevenueService;
    @Autowired
    private WithdrawalOrdersService withdrawalOrdersService;
    @ApiOperation(value = "获取系统配置", notes = "小程序端")
@@ -153,6 +159,29 @@
        return ApiResponse.success(memberRevenueService.findPage(pageWrap));
    }
    @LoginRequired
    @ApiOperation("提现申请")
    @PostMapping("/applyWithdrawal")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true),
    })
    public ApiResponse<TransferToUser.TransferToUserResponse> applyWithdrawal (@RequestBody WithdrawalDTO withdrawalDTO) {
        withdrawalDTO.setMember(this.getMemberResponse());
        return ApiResponse.success("操作成功",withdrawalOrdersService.applyWithdrawal(withdrawalDTO));
    }
    @LoginRequired
    @ApiOperation("撤销提现申请")
    @PostMapping("/cancelTransfer")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true),
    })
    public ApiResponse cancelTransfer (@RequestBody TransferToUser.CancelTransferRequest request) {
        withdrawalOrdersService.cancelTransfer(request);
        return ApiResponse.success("操作成功");
    }
}