浏览代码

云闪付O2O补贴申请

FengChaoYu 1 周之前
父节点
当前提交
53cdcca813
共有 22 个文件被更改,包括 1587 次插入31 次删除
  1. 6 0
      pom.xml
  2. 93 0
      src/main/java/com/gree/mall/manager/bean/ums/SubsidyApplyTraceBean.java
  3. 153 0
      src/main/java/com/gree/mall/manager/bean/ums/UmsSupplementRecordVO.java
  4. 3 0
      src/main/java/com/gree/mall/manager/commonmapper/CommonMapper.java
  5. 94 0
      src/main/java/com/gree/mall/manager/controller/ums/SupplementRecordController.java
  6. 30 0
      src/main/java/com/gree/mall/manager/enums/ums/UmsCheckStatusEnum.java
  7. 23 0
      src/main/java/com/gree/mall/manager/enums/ums/UmsGrantFlagEnum.java
  8. 23 0
      src/main/java/com/gree/mall/manager/enums/ums/UmsTraceStatusEnum.java
  9. 112 26
      src/main/java/com/gree/mall/manager/logic/common/UMSLogic.java
  10. 3 3
      src/main/java/com/gree/mall/manager/logic/order/OrderLogic.java
  11. 1 1
      src/main/java/com/gree/mall/manager/logic/order/OrderRefundLogic.java
  12. 215 0
      src/main/java/com/gree/mall/manager/logic/ums/FileSftpUploaderLogic.java
  13. 255 0
      src/main/java/com/gree/mall/manager/logic/ums/SupplementRecordLogic.java
  14. 341 0
      src/main/java/com/gree/mall/manager/schedule/YueHuanXinSchedule.java
  15. 160 0
      src/main/java/com/gree/mall/manager/utils/EncryptUtils.java
  16. 37 0
      src/main/java/com/gree/mall/manager/utils/StringUtil.java
  17. 3 0
      src/main/java/com/gree/mall/manager/zfire/bean/ZfireParamBean.java
  18. 1 0
      src/main/java/com/gree/mall/manager/zfire/util/FieldUtils.java
  19. 3 0
      src/main/resources/bootstrap-dev.properties
  20. 4 1
      src/main/resources/bootstrap-prd.properties
  21. 3 0
      src/main/resources/bootstrap-test.properties
  22. 24 0
      src/main/resources/mapper/CommonMapper.xml

+ 6 - 0
pom.xml

@@ -415,6 +415,12 @@
                 </exclusion>
             </exclusions>
         </dependency>
+        <!-- ftp -->
+        <dependency>
+            <groupId>com.jcraft</groupId>
+            <artifactId>jsch</artifactId>
+            <version>0.1.55</version>
+        </dependency>
 
 
     </dependencies>

+ 93 - 0
src/main/java/com/gree/mall/manager/bean/ums/SubsidyApplyTraceBean.java

@@ -0,0 +1,93 @@
+package com.gree.mall.manager.bean.ums;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class SubsidyApplyTraceBean {
+    @ApiModelProperty(value = "流水号")
+    private String traceId;
+
+    @ApiModelProperty(value = "商户订单号")
+    private String merOrderId;
+
+    @ApiModelProperty(value = "活动大类")
+    private String activeBigClass;
+
+    @ApiModelProperty(value = "收货地的省编码")
+    private String provinceCode;
+
+    @ApiModelProperty(value = "收货地的市编码")
+    private String cityCode;
+
+    @ApiModelProperty(value = "收货地的区编码")
+    private String districtCode;
+
+    @ApiModelProperty(value = "收货/安装详细地址")
+    private String address;
+
+    @ApiModelProperty(value = "证件图片(头像面)")
+    private String idCardFrontPicKey;
+
+    @ApiModelProperty(value = "证件图片(国徽面)")
+    private String idCardBackPicKey;
+
+    @ApiModelProperty(value = "购买商品数量")
+    private Integer newElecAppCnt;
+
+    @ApiModelProperty(value = "商品品牌名称")
+    private String brand;
+
+    @ApiModelProperty(value = "能效(水效) L1:一级能效,L2:二级能效")
+    private String newEei;
+
+    @ApiModelProperty(value = "品牌及型号")
+    private String newElecAppBrandAndModel;
+
+    @ApiModelProperty(value = "订单交易日期。格式:yyyyMMdd")
+    private String transDate;
+
+    @ApiModelProperty(value = "发票图片")
+    private String newCelecAppInvPicKey;
+
+    @ApiModelProperty(value = "发票代码")
+    private String newInvCode;
+
+    @ApiModelProperty(value = "发票号码")
+    private String newInvNo;
+
+    @ApiModelProperty(value = "开票日期")
+    private String invoiceIssueDate;
+
+    @ApiModelProperty(value = "含税发票金额")
+    private BigDecimal newTotalPrice;
+
+    @ApiModelProperty(value = "不含税发票金额")
+    private BigDecimal newTotalPriceExcTax;
+
+    @ApiModelProperty(value = "销售企业(网点)名称")
+    private String salesCompName;
+
+    @ApiModelProperty(value = "销售方纳税人识别号")
+    private String salesCompCode;
+
+    @ApiModelProperty(value = "购买商品照片 家电大类时:购买商品照片(必填)(需含外包装、产品主体、能效标识、SN码等)。")
+    private String newElecAppPicKey;
+
+    @ApiModelProperty(value = "其他资料-图片1")
+    private String newOtherFile1Key;
+
+    @ApiModelProperty(value = "备注")
+    private String remark;
+
+    @ApiModelProperty(value = "客户端IP地址")
+    private String clientIp;
+
+    @ApiModelProperty(value = "支付购买凭证图片")
+    private String newTransRecPicKey;
+
+    @ApiModelProperty(value = "平台ID")
+    private String apId;
+}

+ 153 - 0
src/main/java/com/gree/mall/manager/bean/ums/UmsSupplementRecordVO.java

@@ -0,0 +1,153 @@
+package com.gree.mall.manager.bean.ums;
+
+import com.gree.mall.manager.annotation.ZfireField;
+import com.gree.mall.manager.enums.ums.UmsCheckStatusEnum;
+import com.gree.mall.manager.enums.ums.UmsGrantFlagEnum;
+import com.gree.mall.manager.enums.ums.UmsTraceStatusEnum;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+@ApiModel
+@ZfireField(tbName = "a")
+public class UmsSupplementRecordVO {
+
+    @ZfireField(hide = true)
+    @ApiModelProperty(value = "id")
+    private String id;
+
+    @ZfireField(hide = true)
+    @ApiModelProperty(value = "商户id")
+    private String companyWechatId;
+
+    @ZfireField(hide = true)
+    @ApiModelProperty(value = "商家id")
+    private String websitId;
+
+    @ApiModelProperty(value = "流水号")
+    private String traceId;
+
+    @ZfireField(hide = true)
+    @ApiModelProperty(value = "发票图片")
+    private String newCelecAppInvPicKey;
+
+    @ZfireField(hide = true)
+    @ApiModelProperty(value = "单据状态 SAVE=保存 SUBMIT=提交 END=执行完结")
+    private String status;
+
+    @ApiModelProperty(value = "流水状态")
+    private UmsTraceStatusEnum traceStatus;
+
+    @ApiModelProperty(value = "审核状态")
+    private UmsCheckStatusEnum checkStatus;
+
+    @ApiModelProperty(value = "发放状态")
+    private UmsGrantFlagEnum grantFlag;
+
+    @ApiModelProperty(value = "订单号")
+    private String orderId;
+
+    @ApiModelProperty(value = "商户订单号")
+    private String merOrderId;
+
+    @ApiModelProperty(value = "活动大类")
+    private String activeBigClass;
+
+    @ZfireField(hide = true)
+    @ApiModelProperty(value = "收货地的省编码")
+    private String provinceCode;
+
+    @ApiModelProperty(value = "省名称")
+    private String provinceName;
+
+    @ZfireField(hide = true)
+    @ApiModelProperty(value = "收货地的市编码")
+    private String cityCode;
+
+    @ApiModelProperty(value = "市名称")
+    private String cityName;
+
+    @ZfireField(hide = true)
+    @ApiModelProperty(value = "收货地的区编码")
+    private String districtCode;
+
+    @ApiModelProperty(value = "区名称")
+    private String districtName;
+
+    @ApiModelProperty(value = "详细地址")
+    private String address;
+
+    @ApiModelProperty(value = "购买商品数量")
+    private Integer newElecAppCnt;
+
+    @ApiModelProperty(value = "商品品牌名称")
+    private String brand;
+
+    @ApiModelProperty(value = "能效(水效) L1:一级能效,L2:二级能效")
+    private String newEei;
+
+    @ApiModelProperty(value = "品牌及型号")
+    private String newElecAppBrandAndModel;
+
+    @ApiModelProperty(value = "交易日期")
+    private String transDate;
+
+    @ApiModelProperty(value = "发票代码")
+    private String newInvCode;
+
+    @ApiModelProperty(value = "发票号码")
+    private String newInvNo;
+
+    @ApiModelProperty(value = "开票日期")
+    private String invoiceIssueDate;
+
+    @ApiModelProperty(value = "含税发票金额")
+    private BigDecimal newTotalPrice;
+
+    @ApiModelProperty(value = "不含税发票金额")
+    private BigDecimal newTotalPriceExcTax;
+
+    @ApiModelProperty(value = "销售企业(网点)名称")
+    private String salesCompName;
+
+    @ApiModelProperty(value = "销售方纳税人识别号")
+    private String salesCompCode;
+
+    @ApiModelProperty(value = "客户端IP地址")
+    private String clientIp;
+
+    @ApiModelProperty(value = "备注")
+    private String remark;
+
+    @ApiModelProperty(value = "创建人")
+    private String createBy;
+
+    @ApiModelProperty(value = "创建时间")
+    private Date createTime;
+
+    @ApiModelProperty(value = "审核失败原因")
+    private String reason;
+
+    @ApiModelProperty(value = "处理备注")
+    private String traceMsg;
+
+    @ZfireField(hide = true)
+    @ApiModelProperty(value = "补贴申请单据ID")
+    private String billId;
+
+    @ZfireField(hide = true)
+    @ApiModelProperty(value = "请求流水")
+    private String reqSn;
+
+    @ZfireField(hide = true)
+    @ApiModelProperty(value = "系统错误")
+    private String sysErr;
+
+    @ZfireField(hide = true)
+    @ApiModelProperty(value = "错误次数")
+    private Integer errCount;
+}

+ 3 - 0
src/main/java/com/gree/mall/manager/commonmapper/CommonMapper.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.gree.mall.manager.bean.admin.AdminCompanyPayConfigVO;
 import com.gree.mall.manager.bean.admin.AdminCompanyVO;
+import com.gree.mall.manager.bean.ums.UmsSupplementRecordVO;
 import com.gree.mall.manager.zfire.bean.ZfireParamBean;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
@@ -14,4 +15,6 @@ public interface CommonMapper {
     IPage<AdminCompanyPayConfigVO> adminCompanyPayConfigPage(Page page, @Param("ex") ZfireParamBean zfireParamBean);
 
     IPage<AdminCompanyVO> adminCompanyPage(Page page, @Param("ex") ZfireParamBean zfireParamBean);
+
+    IPage<UmsSupplementRecordVO> supplementRecordList(Page page, @Param("ex") ZfireParamBean zfireParam);
 }

+ 94 - 0
src/main/java/com/gree/mall/manager/controller/ums/SupplementRecordController.java

@@ -0,0 +1,94 @@
+package com.gree.mall.manager.controller.ums;
+
+import cn.hutool.core.lang.TypeReference;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.gree.mall.manager.annotation.ZfireList;
+import com.gree.mall.manager.bean.ums.UmsSupplementRecordVO;
+import com.gree.mall.manager.exception.RemoteServiceException;
+import com.gree.mall.manager.helper.ResponseHelper;
+import com.gree.mall.manager.logic.ums.FileSftpUploaderLogic;
+import com.gree.mall.manager.logic.ums.SupplementRecordLogic;
+import com.gree.mall.manager.plus.entity.UmsRegionCode;
+import com.gree.mall.manager.plus.entity.UmsSupplementRecord;
+import com.gree.mall.manager.zfire.bean.ZfireParamBean;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+
+@Slf4j
+@RestController
+@Api(value = "粤焕新补贴管理", tags = {"粤焕新补贴管理"})
+@RequestMapping(value = "/ums/supplement", produces = "application/json; charset=utf-8")
+public class SupplementRecordController {
+
+    @Resource
+    SupplementRecordLogic supplementRecordLogic;
+    @Resource
+    FileSftpUploaderLogic fileSftpUploaderLogic;
+
+    @PostMapping("/generate")
+    @ApiOperation("生成记录")
+    public ResponseHelper<String> generate(
+            @ApiParam(value = "订单id", required = true) @RequestParam String orderId
+    ) {
+        String id = supplementRecordLogic.generateSupplementRecord(orderId);
+        return ResponseHelper.success(id);
+    }
+
+    @ZfireList
+    @PostMapping("/list")
+    @ApiOperation(value = "列表")
+    public ResponseHelper<IPage<UmsSupplementRecordVO>> list(
+            @RequestBody ZfireParamBean zfireParamBean
+    ) throws RemoteServiceException {
+        IPage<UmsSupplementRecordVO> adminDeptVOIPage = supplementRecordLogic.list(new Page(zfireParamBean.getPageNum(), zfireParamBean.getPageSize()), zfireParamBean);
+        return ResponseHelper.success(adminDeptVOIPage, new TypeReference<UmsSupplementRecordVO>() {});
+    }
+
+    @PostMapping("/detail")
+    @ApiOperation("详情")
+    public ResponseHelper<UmsSupplementRecord> detail(
+            @ApiParam(value = "id", required = true) @RequestParam String id
+    ) {
+        UmsSupplementRecord detail = supplementRecordLogic.detail(id);
+        return ResponseHelper.success(detail);
+    }
+
+    @PostMapping("/submit")
+    @ApiOperation("提交")
+    public ResponseHelper submit(
+            @RequestBody UmsSupplementRecord record
+    ) {
+        supplementRecordLogic.submit(record);
+        return ResponseHelper.success();
+    }
+
+    @PostMapping("/region/code/list")
+    @ApiOperation("补贴区域码列表")
+    public ResponseHelper<List<UmsRegionCode>> regionCodeList(
+            @ApiParam(value = "省code") @RequestParam(required = false) String provinceCode,
+            @ApiParam(value = "市code") @RequestParam(required = false) String cityCode,
+            HttpServletRequest request
+    ) throws RemoteServiceException {
+        List<UmsRegionCode> list = supplementRecordLogic.regionCodeList(request, provinceCode, cityCode);
+        return ResponseHelper.success(list);
+    }
+
+
+
+    @GetMapping("/upload/test")
+    @ApiOperation("上传测试")
+    public ResponseHelper<String> uploadTest(
+            @ApiParam(value = "img") @RequestParam(required = false) String url
+    ) throws Exception {
+        String name = fileSftpUploaderLogic.uploadFromUrl(url, "GEJLS", "o2o_GEJLS", "5=*kIK6bQ2");
+        return ResponseHelper.success(name);
+    }
+}

+ 30 - 0
src/main/java/com/gree/mall/manager/enums/ums/UmsCheckStatusEnum.java

@@ -0,0 +1,30 @@
+package com.gree.mall.manager.enums.ums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.gree.mall.manager.enums.base.BaseEnum;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+public enum UmsCheckStatusEnum implements BaseEnum {
+    D1("D1","待复审"),
+    D2("D2","复审成功"),
+    D3("D3","复审失败"),
+    E1("E1","待终审"),
+    E2("E2","终审成功"),
+    E3("E3","终审失败"),
+    E("E","已放款"),
+    ER("ER","异常"),
+    ED("ED","失败"),
+    UN("UN","未知"),
+    QN("QN","退资格未知"),;
+
+    @JsonValue
+    @EnumValue
+    private final String key;
+    private final String remark;
+
+}

+ 23 - 0
src/main/java/com/gree/mall/manager/enums/ums/UmsGrantFlagEnum.java

@@ -0,0 +1,23 @@
+package com.gree.mall.manager.enums.ums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.gree.mall.manager.enums.base.BaseEnum;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+public enum UmsGrantFlagEnum implements BaseEnum {
+
+    NOT("0","未发放"),
+    ING("1","发放中"),
+    SUCCESS("2","发放成功"),;
+
+    @JsonValue
+    @EnumValue
+    private final String key;
+    private final String remark;
+
+}

+ 23 - 0
src/main/java/com/gree/mall/manager/enums/ums/UmsTraceStatusEnum.java

@@ -0,0 +1,23 @@
+package com.gree.mall.manager.enums.ums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.gree.mall.manager.enums.base.BaseEnum;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+public enum UmsTraceStatusEnum implements BaseEnum {
+
+    NOT("0","待处理"),
+    SUCCESS("1","处理成功"),
+    FAIL("2","处理失败"),;
+
+    @JsonValue
+    @EnumValue
+    private final String key;
+    private final String remark;
+
+}

+ 112 - 26
src/main/java/com/gree/mall/manager/logic/common/UMSLogic.java

@@ -1,12 +1,20 @@
 package com.gree.mall.manager.logic.common;
 
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.http.HttpUtil;
+import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
-import com.gree.mall.manager.plus.entity.AdminCompanyWechat;
+import com.gree.mall.manager.exception.RemoteServiceException;
+import com.gree.mall.manager.plus.entity.AdminWebsit;
 import com.gree.mall.manager.plus.service.AdminCompanyWechatService;
+import com.gree.mall.manager.plus.service.AdminWebsitService;
+import com.gree.mall.manager.utils.ArithUtils;
+import com.gree.mall.manager.utils.EncryptUtils;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.codec.digest.DigestUtils;
 import org.apache.commons.lang.RandomStringUtils;
+import org.apache.commons.lang3.time.DateFormatUtils;
 import org.apache.http.HttpEntity;
 import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.client.methods.HttpPost;
@@ -34,39 +42,42 @@ public class UMSLogic {
 
     @Value("${ums.url}")
     private String url;
+    @Value("${yue.huan.xin.url}")
+    private String supplementUrl;
     @Resource
     AdminCompanyWechatService adminCompanyWechatService;
+    @Resource
+    AdminWebsitService adminWebsitService;
 
     /**
      * 云闪付退款
      */
 
-    public void refund(String orderId, double totalFee, double refundFee, String companyWechatId) throws Exception {
-        final AdminCompanyWechat company = adminCompanyWechatService.getById(companyWechatId);
+    public void refund(String orderId, double totalFee, double refundFee, String websitId) throws Exception {
+        final AdminWebsit websit = adminWebsitService.getById(websitId);
         JSONObject json = new JSONObject();
-        // TODO 改云闪付退款
-//
-//        json.put("requestTimestamp", DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));    // 报文请求时间
-//        json.put("mid", company.getUmsMid()); // 商户号
-//        json.put("tid", company.getUmsTid());    // 终端号
-//        json.put("merOrderId", company.getUmsSysCode() + orderId); // 商户订单号
-//        json.put("refundAmount", (int) ArithUtils.mul(refundFee, 100));    // 退货的金额
-//        json.put("refundOrderId", getMerchantOrderId(company.getUmsSysCode())); // 退款订单号
-//
-//        //OPEN-BODY-SIG 方式
-//        String authorization = getOpenBodySig(company.getUmsAppid(), company.getUmsAppKey(), json.toString());
-////        String authorization = AppletOrders.getOpenBodySig(company.getUmsAppid(), company.getUmsAppKey(), json.toString());
-//        String send = "";
-//        try {
-//            send = send(url + "/v1/netpay/refund", json.toString(), authorization);
-//
-//        } catch (Exception e) {
-//            throw new RemoteServiceException("发送请求云闪付失败, 错误信息: " + e.getMessage());
-//        }
-//
-//        if (!send.contains("成功")) {
-//            throw new RemoteServiceException("请求云闪付失败, 平台错误信息: " + send);
-//        }
+
+        json.put("requestTimestamp", DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));    // 报文请求时间
+        json.put("mid", websit.getYunCompany()); // 商户号
+        json.put("tid", websit.getYunNumber());    // 终端号
+        json.put("merOrderId", websit.getYunSystem() + orderId); // 商户订单号
+        json.put("refundAmount", (int) ArithUtils.mul(refundFee, 100));    // 退货的金额
+        json.put("refundOrderId", getMerchantOrderId(websit.getYunSystem())); // 退款订单号
+
+        //OPEN-BODY-SIG 方式
+        String authorization = getOpenBodySig(websit.getYunAppid(), websit.getYunAppkey(), json.toString());
+//        String authorization = AppletOrders.getOpenBodySig(websit.getUmsAppid(), websit.getUmsAppKey(), json.toString());
+        String send = "";
+        try {
+            send = send(url + "/v1/netpay/refund", json.toString(), authorization);
+
+        } catch (Exception e) {
+            throw new RemoteServiceException("发送请求云闪付失败, 错误信息: " + e.getMessage());
+        }
+
+        if (!send.contains("成功")) {
+            throw new RemoteServiceException("请求云闪付失败, 平台错误信息: " + send);
+        }
     }
 
     /**
@@ -144,6 +155,81 @@ public class UMSLogic {
         //需要以来源编号做前四位
         return code + org.apache.commons.lang.time.DateFormatUtils.format(new Date(), "yyyyMMddHHmmssSSS") + RandomStringUtils.randomNumeric(7);
     }
+
+
+    /**
+     * 接收补贴申请补录资料流水
+     */
+    public JSONObject submitSubsidyApplyTrace(String userId, String reqSn, String encBizReqData, String servicePublicKey,
+                                              String privateKey) throws Exception {
+        String url = supplementUrl + "/o2o/submitSubsidyApplTrace";
+        return this.commonReqYueHuanXin(userId, reqSn, encBizReqData, servicePublicKey, privateKey);
+    }
+
+    /**
+     * 查询补贴申请补录资料流水
+     */
+    public JSONObject querySubsidyApplyTrace(String userId, String reqSn, String encBizReqData, String servicePublicKey,
+                                             String privateKey) throws Exception {
+        String url = supplementUrl + "/o2o/querySubsidyApplTrace";
+        return this.commonReqYueHuanXin(userId, reqSn, encBizReqData, servicePublicKey, privateKey);
+    }
+
+    /**
+     * 查询补贴申请补录资料流水
+     */
+    public JSONObject querySubsidyApply(String userId, String reqSn, String encBizReqData, String servicePublicKey,
+                                        String privateKey) throws Exception {
+        String url = supplementUrl + "/o2o/querySubsidyAppl";
+
+        return this.commonReqYueHuanXin(userId, reqSn, encBizReqData, servicePublicKey, privateKey);
+    }
+
+    private JSONObject commonReqYueHuanXin(String userId, String reqSn, String encBizReqData, String servicePublicKey,
+                                           String privateKey) throws Exception {
+        JSONObject json = new JSONObject();
+        json.put("userId", userId);
+        json.put("reqSn", reqSn);
+        json.put("timestamp", DateUtil.formatDateTime(DateUtil.date()));
+        final String encryptBizReqData = EncryptUtils.encryptBizReqData(servicePublicKey, encBizReqData);
+        final String sign = EncryptUtils.generateSign(privateKey, encryptBizReqData);
+        json.put("encBizReqData", encryptBizReqData);
+        json.put("signAlg", "1");
+        json.put("sign", sign);
+
+        String post;
+        JSONObject respBody;
+
+        try {
+            post = HttpUtil.post(url, json.toString());
+            respBody = JSON.parseObject(post);
+        } catch (Exception e) {
+            throw new RemoteServiceException("公共应答参数异常: " + e.getMessage());
+        }
+
+        final boolean isSuccess = respBody.containsValue("00000");
+
+        if (!isSuccess) {
+            final String msg = respBody.getObject("msg", String.class);
+
+            throw new RemoteServiceException("应码码错误: " + msg);
+        }
+
+        final String encBizRespData = respBody.getObject("encBizRespData", String.class);
+        final String respSign = respBody.getObject("sign", String.class);
+
+        final String respData = EncryptUtils.decryptEncBizRespData(privateKey, encBizRespData);
+
+        final boolean verifySign = EncryptUtils.verifySign(servicePublicKey, encBizRespData, respSign);
+
+        if (!verifySign) {
+            throw new RemoteServiceException("验签签名串失败");
+        }
+
+        final JSONObject respDataJson = JSON.parseObject(respData);
+
+        return respDataJson;
+    }
 }
 
 

+ 3 - 3
src/main/java/com/gree/mall/manager/logic/order/OrderLogic.java

@@ -404,10 +404,10 @@ public class OrderLogic {
         log.info("【退款开始:】退款金额{}", refundAmount);
         if (!orderInfo.getPayType().equals("云闪付")) {
             wechatLogic.refund(orderId, orderId + StringUtil.getUUID(), orderInfo.getPayAmount().doubleValue()
-                    , refundAmount.doubleValue(), orderInfo.getCompanyWechatId());
+                    , refundAmount.doubleValue(), orderInfo.getWebsitId());
         } else {
             umsLogic.refund(orderId, orderInfo.getPayAmount().doubleValue()
-                    , refundAmount.doubleValue(), orderInfo.getCompanyWechatId());
+                    , refundAmount.doubleValue(), orderInfo.getWebsitId());
         }
         return refundAmount;
     }
@@ -523,7 +523,7 @@ public class OrderLogic {
                     , refundAmount.doubleValue(), orderInfo.getCompanyWechatId());
         } else {
             umsLogic.refund(orderId, orderInfo.getPayAmount().doubleValue()
-                    , refundAmount.doubleValue(), orderInfo.getCompanyWechatId());
+                    , refundAmount.doubleValue(), orderInfo.getWebsitId());
         }
         return refundAmount;
     }

+ 1 - 1
src/main/java/com/gree/mall/manager/logic/order/OrderRefundLogic.java

@@ -302,7 +302,7 @@ public class OrderRefundLogic {
                     , orderRefund.getRefundAmount().doubleValue(), orderRefund.getCompanyWechatId());
         } else {
             umsLogic.refund(orderRefund.getOrderId(), orderRefund.getPayAmount().doubleValue()
-                    , orderRefund.getRefundAmount().doubleValue(), orderRefund.getCompanyWechatId());
+                    , orderRefund.getRefundAmount().doubleValue(), orderRefund.getWebsitId());
         }
     }
 

+ 215 - 0
src/main/java/com/gree/mall/manager/logic/ums/FileSftpUploaderLogic.java

@@ -0,0 +1,215 @@
+package com.gree.mall.manager.logic.ums;
+
+import com.gree.mall.manager.exception.RemoteServiceException;
+import com.gree.mall.manager.utils.StringUtil;
+import com.jcraft.jsch.*;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.http.client.SimpleClientHttpRequestFactory;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+import javax.annotation.Resource;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.rmi.RemoteException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Objects;
+import java.util.UUID;
+
+@Service
+@Slf4j
+public class FileSftpUploaderLogic {
+
+    // 粤焕新平台上传接口地址
+    @Value("${yue.huan.xin.sftp}")
+    private String sftpUpload;
+    @Value("${yue.huan.xin.sftp.port}")
+    private String sftpPort;
+
+    @Resource
+    RestTemplate restTemplate;
+
+    private static final String TMP_DIR = "tmp"; // 指定临时目录名称
+
+    @Bean
+    public RestTemplate restTemplate() {
+        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
+        factory.setConnectTimeout(5000);    // 5秒连接超时
+        factory.setReadTimeout(10000);      // 10秒读取超时
+        return new RestTemplate(factory);
+    }
+
+    /**
+     * 从下载地址获取文件并上传
+     * @param downloadUrl 文件下载地址
+     * @param platformId 平台id
+     * @param username 上传账号
+     * @param password 上传密码
+     * @return 上传结果
+     */
+    public String uploadFromUrl(String downloadUrl, String platformId, String username, String password) throws IOException {
+        File downloadedFile = null;
+        try {
+            // 1. 下载文件到临时目录
+            downloadedFile = downloadFile(downloadUrl);
+            if (Objects.isNull(downloadedFile)) {
+                throw new RemoteServiceException("文件下载失败");
+            }
+
+            // 2. 验证文件大小
+            if (downloadedFile.length() > 1024 * 1024) {
+                Files.deleteIfExists(downloadedFile.toPath());
+                throw new RemoteServiceException("文件大小超过1MB限制");
+            }
+
+            // 3. 生成符合规范的文件名
+            String fileName = generateFileName(downloadedFile.getName(), platformId);
+
+            // 4. 上传文件
+//            boolean uploadSuccess = uploadFile(downloadedFile, fileName, username, password);
+            // SFTP上传
+            boolean uploadSuccess = sftpUpload(downloadedFile, fileName, username, password);
+
+            if (!uploadSuccess) {
+                throw new RemoteServiceException("上传失败");
+            }
+
+            return fileName;
+        } catch (IOException e) {
+            throw new RemoteServiceException("上传处理异常: " + e.getMessage());
+        } finally {
+            // 5. 清理临时文件
+            if (Objects.nonNull(downloadedFile)) {
+                Files.deleteIfExists(downloadedFile.toPath());
+            }
+        }
+    }
+
+    /**
+     * 下载文件到本地临时目录
+     */
+    private File downloadFile(String fileUrl) throws IOException {
+        log.info("yuehuanxin补贴图片开始下载文件: {}", fileUrl);
+        // 1. 检查并创建临时目录
+        Path tmpDirPath = Paths.get(TMP_DIR);
+        if (!Files.exists(tmpDirPath)) {
+            Files.createDirectories(tmpDirPath); // 创建目录(包含父目录)
+        }
+        String extension = fileUrl.contains(".") ?
+                fileUrl.substring(fileUrl.lastIndexOf(".")) : "";
+
+        if (StringUtils.isBlank(extension)
+                || !StringUtil.containsIgnoreCase(".JPG|.PNG", extension)) {
+            throw new RemoteException("下载文件格式异常");
+        }
+
+        // 2. 生成唯一文件名(避免覆盖)
+        String fileName = "download_" + System.currentTimeMillis() + "_" + UUID.randomUUID().toString().substring(0, 8) + extension;
+        Path targetPath = tmpDirPath.resolve(fileName);
+
+        // 3. 下载文件到指定目录
+        URL url = new URL(fileUrl);
+        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+        connection.setRequestMethod("GET");
+        connection.setConnectTimeout(5000);
+        connection.setReadTimeout(10000);
+
+        try (InputStream inputStream = connection.getInputStream()) {
+            Files.copy(inputStream, targetPath, StandardCopyOption.REPLACE_EXISTING);
+        } finally {
+            connection.disconnect();
+        }
+
+        return targetPath.toFile();
+    }
+
+    /**
+     * 生成符合规范的文件名
+     */
+    private String generateFileName(String originalName, String platformId) {
+        String extension = originalName.contains(".") ?
+                originalName.substring(originalName.lastIndexOf(".")) : "";
+        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
+        String uploadDate = dateFormat.format(new Date());
+        String uuid = UUID.randomUUID().toString().replace("-", "");
+        return String.format("%s_%s_%s%s", platformId, uploadDate, uuid, extension);
+    }
+
+    /**
+     * 以http方式上传文件到粤焕新平台
+     */
+//    private boolean uploadFile(File file, String fileName, String username, String password) {
+//        try {
+//            log.info("yuehuanxin开始上传文件,生成的文件名: {}", fileName);
+//            MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
+//
+//            // 关键修改:覆盖 FileSystemResource 的 getFilename() 方法
+//            body.add("file", new FileSystemResource(file) {
+//                @Override
+//                public String getFilename() {
+//                    return fileName; // 返回生成的规范文件名
+//                }
+//            });
+//
+//            HttpHeaders headers = new HttpHeaders();
+//            headers.setContentType(MediaType.MULTIPART_FORM_DATA);
+//            headers.setBasicAuth(username, password);
+//
+//            HttpEntity<MultiValueMap<String, Object>> requestEntity =
+//                    new HttpEntity<>(body, headers);
+//
+//            ResponseEntity<String> response = restTemplate.exchange(
+//                    uploadUrl,
+//                    HttpMethod.POST,
+//                    requestEntity,
+//                    String.class
+//            );
+//
+//            return response.getStatusCode() == HttpStatus.OK;
+//        } catch (Exception e) {
+//            log.error("yuehuanxin文件上传失败: {}", e.getMessage());
+//            return false;
+//        }
+//    }
+
+    public boolean sftpUpload(File localFile, String fileName, String username, String password) {
+        Session session = null;
+        ChannelSftp channel = null;
+
+        try {
+            JSch jsch = new JSch();
+            session = jsch.getSession(username, sftpUpload, Integer.parseInt(sftpPort));
+            session.setPassword(password);
+            session.setConfig("StrictHostKeyChecking", "no");
+            session.connect();
+
+            channel = (ChannelSftp) session.openChannel("sftp");
+            channel.connect();
+
+            // 保持原有文件名逻辑不变
+            channel.put(localFile.getAbsolutePath(), fileName);
+
+            return true;
+        } catch (JSchException | SftpException e) {
+            throw new RemoteServiceException("yuehuanxin文件上传失败: " + e.getMessage());
+        } finally {
+            if (channel != null && channel.isConnected()) {
+                channel.disconnect();
+            }
+            if (session != null && session.isConnected()) {
+                session.disconnect();
+            }
+        }
+    }
+}

+ 255 - 0
src/main/java/com/gree/mall/manager/logic/ums/SupplementRecordLogic.java

@@ -0,0 +1,255 @@
+package com.gree.mall.manager.logic.ums;
+
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.IdUtil;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.gree.mall.manager.bean.admin.AdminUserCom;
+import com.gree.mall.manager.bean.ums.UmsSupplementRecordVO;
+import com.gree.mall.manager.commonmapper.CommonMapper;
+import com.gree.mall.manager.enums.UMSDiscountCodeEnum;
+import com.gree.mall.manager.exception.RemoteServiceException;
+import com.gree.mall.manager.logic.common.CommonLogic;
+import com.gree.mall.manager.plus.entity.*;
+import com.gree.mall.manager.plus.service.*;
+import com.gree.mall.manager.utils.StringUtil;
+import com.gree.mall.manager.zfire.bean.ZfireParamBean;
+import com.gree.mall.manager.zfire.util.FieldUtils;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.servlet.http.HttpServletRequest;
+import java.math.BigDecimal;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Objects;
+
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class SupplementRecordLogic {
+
+    CommonMapper commonMapper;
+    UmsSupplementRecordService umsSupplementRecordService;
+    UmsRegionCodeService umsRegionCodeService;
+    OrderDetailService orderDetailService;
+    CommonLogic commonLogic;
+    AdminCompanyWechatService adminCompanyWechatService;
+    OrderInfoService orderInfoService;
+    AdminWebsitService adminWebsitService;
+
+    @Transactional(rollbackFor = Exception.class)
+    public String generateSupplementRecord(String orderId) {
+        final OrderInfo orderInfo = orderInfoService.getById(orderId);
+        AdminCompanyWechat companyWechat = adminCompanyWechatService.getById(orderInfo.getCompanyWechatId());
+
+        return this.generateSupplementRecord(orderInfo, companyWechat);
+    }
+
+    public String generateSupplementRecord(OrderInfo orderInfo, AdminCompanyWechat companyWechat) {
+        final AdminUserCom adminUser = commonLogic.getAdminUser();
+        final AdminWebsit adminWebsit = adminWebsitService.getById(orderInfo.getWebsitId());
+        if (!orderInfo.getPayType().equals("云闪付")
+                || StringUtils.isBlank(adminWebsit.getYunSpCode())
+                || orderInfo.getCouponMerchantContribute().compareTo(BigDecimal.ZERO) <= 0
+                || StringUtils.isBlank(orderInfo.getBuyerIdCard())) {
+            // 不生成补贴申请记录
+            throw new RemoteServiceException("非云闪付补贴订单,生成记录失败");
+        }
+
+        final Integer count = umsSupplementRecordService.lambdaQuery()
+                .eq(UmsSupplementRecord::getOrderId, orderInfo.getOrderId())
+                .count();
+
+        if (count > 0) {
+            throw new RemoteServiceException("已生成补贴申请记录");
+        }
+
+        UmsSupplementRecord record = new UmsSupplementRecord();
+        String traceId = adminWebsit.getYunSpCode() + IdUtil.fastSimpleUUID();
+        String merOrderId = adminWebsit.getYunCompany() + orderInfo.getOrderId();
+        final UmsRegionCode province = umsRegionCodeService.lambdaQuery()
+                .eq(UmsRegionCode::getProvinceName, orderInfo.getProvince())
+                .last("limit 1")
+                .one();
+        final UmsRegionCode city = umsRegionCodeService.lambdaQuery()
+                .eq(UmsRegionCode::getCityName, orderInfo.getCity())
+                .last("limit 1")
+                .one();
+        final UmsRegionCode country = umsRegionCodeService.lambdaQuery()
+                .eq(UmsRegionCode::getCountryName, orderInfo.getArea())
+                .last("limit 1")
+                .one();
+        String provinceCode = Objects.isNull(province) ? null : province.getProvinceId();
+        String provinceName = Objects.isNull(province) ? null : province.getProvinceName();
+        String cityCode = Objects.isNull(city) ? null : city.getCityId();
+        String cityName = Objects.isNull(city) ? null : city.getCityName();
+        String districtCode = Objects.isNull(country) ? null : country.getCountryId();
+        String districtName = Objects.isNull(country) ? null : country.getCountryName();
+
+        final OrderDetail orderDetail = orderDetailService.lambdaQuery()
+                .eq(OrderDetail::getOrderId, orderInfo.getOrderId())
+                .last("limit 1")
+                .one();
+
+        final String newEei = UMSDiscountCodeEnum.findNameByCode(orderDetail.getUmsDiscountCode()).contains("一级") ? "L1" : "L2";
+
+        String goodsName = orderDetail.getGoodsName().replace("以旧换新", "")
+                .replace("一级能效", "")
+                .replace("二级能效", "")
+                .replace("格力", "")
+                .replace("品牌", "")
+                .replace("【", "")
+                .replace("】", "")
+                .replaceAll(" ", "");
+
+        final boolean validStr = StringUtil.isByteLengthValid(goodsName, 100, StandardCharsets.UTF_8);
+
+        final String newElecAppBrandAndModel = validStr ? goodsName : null;
+
+        record.setCompanyWechatId(orderInfo.getCompanyWechatId())
+                .setWebsitId(orderInfo.getWebsitId())
+                .setOrderId(orderInfo.getOrderId())
+                .setTraceId(traceId)
+                .setMerOrderId(merOrderId)
+                .setActiveBigClass("ELEC")
+                .setProvinceCode(provinceCode)
+                .setProvinceName(provinceName)
+                .setCityCode(cityCode)
+                .setCityName(cityName)
+                .setDistrictCode(districtCode)
+                .setDistrictName(districtName)
+                .setAddress(orderInfo.getReceAddress())
+                .setNewElecAppCnt(1)
+                .setBrand("格力")
+                .setNewEei(newEei)
+                .setNewElecAppBrandAndModel(newElecAppBrandAndModel)
+                .setTransDate(DateUtil.formatDate(orderInfo.getPayTime()))
+                .setSalesCompName(adminWebsit.getUmsApName())
+                .setSalesCompCode(adminWebsit.getYunTax())
+                .setClientIp(orderInfo.getClientIp())
+                .setApId(adminWebsit.getUmsApId())
+                .setCreateBy(adminUser.getNickName())
+                .setCreateTime(DateUtil.date());
+
+        record.insert();
+
+        orderInfoService.lambdaUpdate()
+                .set(OrderInfo::getIsGenerateSupplement, true)
+                .eq(OrderInfo::getOrderId, orderInfo.getOrderId())
+                .update();
+
+        return record.getId();
+    }
+
+    public IPage<UmsSupplementRecordVO> list(Page page, ZfireParamBean zfireParam) {
+        AdminUserCom adminUser = commonLogic.getAdminUser();
+        //1.组装查询条件
+        FieldUtils.supplyParam(zfireParam, UmsSupplementRecordVO.class, adminUser);
+        return commonMapper.supplementRecordList(page, zfireParam);
+    }
+
+    public UmsSupplementRecord detail(String id) {
+        return umsSupplementRecordService.getById(id);
+    }
+
+    public void submit(UmsSupplementRecord record) {
+        final AdminUserCom adminUser = commonLogic.getAdminUser();
+        final AdminWebsit adminWebsit = adminWebsitService.getById(record.getWebsitId());
+        if (StringUtils.isNotBlank(record.getId())) {
+            final UmsSupplementRecord oldData = umsSupplementRecordService.getById(record.getId());
+            if (!oldData.getStatus().equals("SAVE")) {
+                throw new RemoteServiceException("记录状态已更新,请刷新");
+            }
+        }
+
+        if (StringUtils.isBlank(record.getProvinceCode())) {
+            throw new RemoteServiceException("收货地的省编码不能为空");
+        }
+
+        if (StringUtils.isBlank(record.getCityCode())) {
+            throw new RemoteServiceException("收货地的市编码不能为空");
+        }
+
+        if (StringUtils.isBlank(record.getDistrictCode())) {
+            throw new RemoteServiceException("收货地的区编码不能为空");
+        }
+
+        if (StringUtils.isBlank(record.getNewElecAppBrandAndModel())) {
+            throw new RemoteServiceException("品牌及型号不能为空");
+        }
+
+        if (!StringUtil.isByteLengthValid(record.getNewElecAppBrandAndModel(), 100, StandardCharsets.UTF_8)) {
+            throw new RemoteServiceException("品牌及型号不能超100字节");
+        }
+
+        if (StringUtils.isBlank(record.getNewCelecAppInvPicKey())) {
+            throw new RemoteServiceException("发票图片不能为空");
+        }
+
+        if (StringUtils.isBlank(record.getNewInvNo())) {
+            throw new RemoteServiceException("发票号码不能为空");
+        }
+
+        if (StringUtils.isBlank(record.getInvoiceIssueDate())) {
+            throw new RemoteServiceException("开票日期不能为空");
+        }
+
+        if (Objects.isNull(record.getNewTotalPrice())) {
+            throw new RemoteServiceException("含税发票金额不能为空");
+        }
+
+        if (Objects.isNull(record.getNewTotalPriceExcTax())) {
+            throw new RemoteServiceException("不含税发票金额不能为空");
+        }
+
+        if (StringUtils.isBlank(record.getNewElecAppPicKey1())
+                && StringUtils.isBlank(record.getNewElecAppPicKey2())
+                && StringUtils.isBlank(record.getNewElecAppPicKey3())
+                && StringUtils.isBlank(record.getNewElecAppPicKey4())
+                && StringUtils.isBlank(record.getNewElecAppPicKey5())) {
+            throw new RemoteServiceException("购买商品照片不能为空");
+        }
+
+        if (StringUtils.isBlank(record.getNewTransRecPicKey1())
+                && StringUtils.isBlank(record.getNewTransRecPicKey2())
+                && StringUtils.isBlank(record.getNewTransRecPicKey3())
+                && StringUtils.isBlank(record.getNewTransRecPicKey4())
+                && StringUtils.isBlank(record.getNewTransRecPicKey5())) {
+            throw new RemoteServiceException("支付购买凭证图片不能为空");
+        }
+
+        record.setStatus("SUBMIT");
+        if (StringUtils.isBlank(record.getId())) {
+            final AdminCompanyWechat companyWechat = adminCompanyWechatService.getById(record.getCompanyWechatId());
+            String traceId = adminWebsit.getYunSpCode() + IdUtil.fastSimpleUUID();
+            record.setCreateTime(DateUtil.date())
+                    .setCreateBy(adminUser.getNickName())
+                    .setTraceId(traceId)
+                    .setTraceStatus("")
+                    .setTraceMsg("")
+                    .setBillId("")
+                    .setCheckStatus("")
+                    .setGrantFlag("")
+                    .setReason("")
+                    .insert();
+        } else {
+            record.updateById();
+        }
+    }
+
+    public List<UmsRegionCode> regionCodeList(HttpServletRequest request, String provinceCode, String cityCode) {
+        final List<UmsRegionCode> list = umsRegionCodeService.lambdaQuery()
+                .eq(StringUtils.isNotBlank(provinceCode), UmsRegionCode::getProvinceId, provinceCode)
+                .eq(StringUtils.isNotBlank(cityCode), UmsRegionCode::getCityId, cityCode)
+                .groupBy(StringUtils.isBlank(provinceCode) && StringUtils.isBlank(cityCode), UmsRegionCode::getProvinceId)
+                .groupBy(StringUtils.isNotBlank(provinceCode) && StringUtils.isBlank(cityCode), UmsRegionCode::getProvinceId, UmsRegionCode::getCityId)
+                .list();
+
+        return list;
+    }
+
+}

+ 341 - 0
src/main/java/com/gree/mall/manager/schedule/YueHuanXinSchedule.java

@@ -0,0 +1,341 @@
+package com.gree.mall.manager.schedule;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.gree.mall.manager.bean.ums.SubsidyApplyTraceBean;
+import com.gree.mall.manager.logic.common.UMSLogic;
+import com.gree.mall.manager.logic.ums.FileSftpUploaderLogic;
+import com.gree.mall.manager.plus.entity.AdminCompanyWechat;
+import com.gree.mall.manager.plus.entity.AdminWebsit;
+import com.gree.mall.manager.plus.entity.UmsSupplementRecord;
+import com.gree.mall.manager.plus.service.AdminCompanyWechatService;
+import com.gree.mall.manager.plus.service.AdminWebsitService;
+import com.gree.mall.manager.plus.service.UmsSupplementRecordService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.io.IOException;
+import java.time.format.DateTimeFormatter;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+@Component
+@Slf4j
+public class YueHuanXinSchedule {
+
+    @Resource
+    UmsSupplementRecordService umsSupplementRecordService;
+    @Resource
+    FileSftpUploaderLogic fileSftpUploaderLogic;
+    @Resource
+    UMSLogic umsLogic;
+    @Resource
+    AdminCompanyWechatService adminCompanyWechatService;
+    @Resource
+    AdminWebsitService adminWebsitService;
+
+    /**
+     * 对提交的补贴记录上传
+     */
+    @Scheduled(fixedDelay = 60 * 1000)
+    public void recordUploadTask() {
+        List<UmsSupplementRecord> recordList = umsSupplementRecordService.lambdaQuery()
+                .eq(UmsSupplementRecord::getStatus,"SUBMIT")
+                .lt(UmsSupplementRecord::getErrCount, 5)
+                .list();
+
+        if (CollectionUtil.isEmpty(recordList)) {
+            return;
+        }
+
+        final List<AdminCompanyWechat> companyWechatList = adminCompanyWechatService.lambdaQuery()
+                .list();
+
+        final List<AdminWebsit> websitList = adminWebsitService.lambdaQuery()
+                .in(AdminWebsit::getWebsitId, recordList.stream()
+                        .map(UmsSupplementRecord::getWebsitId)
+                        .collect(Collectors.toList()))
+                .list();
+
+        final Map<String, AdminCompanyWechat> companyWechatMap = companyWechatList.stream()
+                .collect(Collectors.toMap(AdminCompanyWechat::getCompanyWechatId, Function.identity()));
+
+        final Map<String, AdminWebsit> adminWebsitMap = websitList.stream()
+                .collect(Collectors.toMap(AdminWebsit::getWebsitId, Function.identity()));
+
+        for (UmsSupplementRecord record : recordList) {
+            final AdminCompanyWechat companyWechat = companyWechatMap.get(record.getCompanyWechatId());
+            final AdminWebsit websit = adminWebsitMap.get(record.getWebsitId());
+            if (Objects.isNull(companyWechat)) {
+                record.setErrCount(record.getErrCount() + 1)
+                        .setSysErr("缺少商户信息");
+                continue;
+            }
+            if (Objects.isNull(websit)) {
+                record.setErrCount(record.getErrCount() + 1)
+                        .setSysErr("缺少商家信息");
+                continue;
+            }
+
+            record.setReqSn((DateUtil.format(DateUtil.date(), DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")) + IdUtil.fastSimpleUUID()));
+
+            SubsidyApplyTraceBean reqBean = new SubsidyApplyTraceBean();
+            BeanUtils.copyProperties(record, reqBean);
+
+            try {
+                // 上传文件到粤换新平台
+                this.handleImageUpload(websit, record, reqBean);
+
+                // 调起申请接口
+                final JSONObject resultJson = umsLogic.querySubsidyApplyTrace(websit.getYunSpCode(), record.getReqSn(), JSONUtil.toJsonStr(reqBean),
+                        websit.getUmsPublicKey(), websit.getUmsPrivateKey());
+
+                final String traceStatus = resultJson.getObject("traceStatus", String.class);
+                final String traceMsg = resultJson.getObject("traceMsg", String.class);
+
+                record.setTraceStatus(traceStatus)
+                        .setTraceMsg(traceMsg)
+                        .setErrCount(0)
+                        .setSysErr("");
+
+            } catch (Exception e) {
+                record.setErrCount(record.getErrCount() + 1)
+                        .setSysErr(e.getMessage());
+            }
+        }
+
+        umsSupplementRecordService.saveOrUpdateBatch(recordList);
+    }
+
+    private void handleImageUpload(AdminWebsit websit, UmsSupplementRecord record,
+                                   SubsidyApplyTraceBean reqBean) throws IOException {
+        if (StringUtils.isNotBlank(record.getNewCelecAppInvPicKey())) {
+            reqBean.setNewCelecAppInvPicKey(fileSftpUploaderLogic.uploadFromUrl(record.getNewCelecAppInvPicKey(),
+                    websit.getYunSpCode(), websit.getUmsUser(), websit.getUmsPassword()));
+        }
+
+        StringBuilder goodsSb = new StringBuilder();
+        if (StringUtils.isNotBlank(record.getNewElecAppPicKey1())) {
+            goodsSb.append(fileSftpUploaderLogic.uploadFromUrl(record.getNewElecAppPicKey1(),
+                    websit.getYunSpCode(), websit.getUmsUser(), websit.getUmsPassword()))
+                    .append("|");
+        }
+        if (StringUtils.isNotBlank(record.getNewElecAppPicKey2())) {
+            goodsSb.append(fileSftpUploaderLogic.uploadFromUrl(record.getNewElecAppPicKey2(),
+                    websit.getYunSpCode(), websit.getUmsUser(), websit.getUmsPassword()))
+                    .append("|");
+        }
+        if (StringUtils.isNotBlank(record.getNewElecAppPicKey3())) {
+            goodsSb.append(fileSftpUploaderLogic.uploadFromUrl(record.getNewElecAppPicKey3(),
+                    websit.getYunSpCode(), websit.getUmsUser(), websit.getUmsPassword()))
+                    .append("|");
+        }
+        if (StringUtils.isNotBlank(record.getNewElecAppPicKey4())) {
+            goodsSb.append(fileSftpUploaderLogic.uploadFromUrl(record.getNewElecAppPicKey4(),
+                    websit.getYunSpCode(), websit.getUmsUser(), websit.getUmsPassword()))
+                    .append("|");
+        }
+        if (StringUtils.isNotBlank(record.getNewElecAppPicKey5())) {
+            goodsSb.append(fileSftpUploaderLogic.uploadFromUrl(record.getNewElecAppPicKey5(),
+                    websit.getYunSpCode(), websit.getUmsUser(), websit.getUmsPassword()))
+                    .append("|");
+        }
+        if (StringUtils.isNotBlank(goodsSb.toString())) {
+            final String goodsKeys = StringUtils.chop(goodsSb.toString());
+            if (StringUtils.isNotBlank(goodsKeys)) {
+                reqBean.setNewElecAppPicKey(goodsKeys);
+            }
+        }
+
+        StringBuilder otherSb = new StringBuilder();
+        if (StringUtils.isNotBlank(record.getNewOtherFile1Key1())) {
+            goodsSb.append(fileSftpUploaderLogic.uploadFromUrl(record.getNewOtherFile1Key1(),
+                    websit.getYunSpCode(), websit.getUmsUser(), websit.getUmsPassword()))
+                    .append("|");
+        }
+        if (StringUtils.isNotBlank(record.getNewOtherFile1Key2())) {
+            goodsSb.append(fileSftpUploaderLogic.uploadFromUrl(record.getNewOtherFile1Key2(),
+                    websit.getYunSpCode(), websit.getUmsUser(), websit.getUmsPassword()))
+                    .append("|");
+        }
+        if (StringUtils.isNotBlank(record.getNewOtherFile1Key3())) {
+            goodsSb.append(fileSftpUploaderLogic.uploadFromUrl(record.getNewOtherFile1Key3(),
+                    websit.getYunSpCode(), websit.getUmsUser(), websit.getUmsPassword()))
+                    .append("|");
+        }
+        if (StringUtils.isNotBlank(record.getNewOtherFile1Key4())) {
+            goodsSb.append(fileSftpUploaderLogic.uploadFromUrl(record.getNewOtherFile1Key4(),
+                    websit.getYunSpCode(), websit.getUmsUser(), websit.getUmsPassword()))
+                    .append("|");
+        }
+        if (StringUtils.isNotBlank(record.getNewOtherFile1Key5())) {
+            goodsSb.append(fileSftpUploaderLogic.uploadFromUrl(record.getNewOtherFile1Key5(),
+                    websit.getYunSpCode(), websit.getUmsUser(), websit.getUmsPassword()))
+                    .append("|");
+        }
+        if (StringUtils.isNotBlank(otherSb.toString())) {
+            final String otherKeys = StringUtils.chop(otherSb.toString());
+            if (StringUtils.isNotBlank(otherKeys)) {
+                reqBean.setNewOtherFile1Key(otherKeys);
+            }
+        }
+    }
+
+    /**
+     * 对已补贴记录上传的轮询
+     */
+    @Scheduled(fixedDelay = 60 * 1000)
+    public void uploadTraceRecordTask() {
+        List<UmsSupplementRecord> recordList = umsSupplementRecordService.lambdaQuery()
+                .eq(UmsSupplementRecord::getStatus,"END")
+                .eq(UmsSupplementRecord::getTraceStatus, "0")
+                .lt(UmsSupplementRecord::getErrCount, 5)
+                .list();
+
+        if (CollectionUtil.isEmpty(recordList)) {
+            return;
+        }
+
+        final List<AdminCompanyWechat> companyWechatList = adminCompanyWechatService.lambdaQuery()
+                .list();
+
+        final List<AdminWebsit> websitList = adminWebsitService.lambdaQuery()
+                .in(AdminWebsit::getWebsitId, recordList.stream()
+                        .map(UmsSupplementRecord::getWebsitId)
+                        .collect(Collectors.toList()))
+                .list();
+
+        final Map<String, AdminCompanyWechat> companyWechatMap = companyWechatList.stream()
+                .collect(Collectors.toMap(AdminCompanyWechat::getCompanyWechatId, Function.identity()));
+
+        final Map<String, AdminWebsit> adminWebsitMap = websitList.stream()
+                .collect(Collectors.toMap(AdminWebsit::getWebsitId, Function.identity()));
+
+        for (UmsSupplementRecord record : recordList) {
+            final AdminCompanyWechat companyWechat = companyWechatMap.get(record.getCompanyWechatId());
+            final AdminWebsit websit = adminWebsitMap.get(record.getWebsitId());
+            if (Objects.isNull(companyWechat)) {
+                record.setErrCount(record.getErrCount() + 1)
+                        .setSysErr("缺少商户信息");
+                continue;
+            }
+            if (Objects.isNull(websit)) {
+                record.setErrCount(record.getErrCount() + 1)
+                        .setSysErr("缺少商家信息");
+                continue;
+            }
+
+            record.setReqSn((DateUtil.format(DateUtil.date(), DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")) + IdUtil.fastSimpleUUID()));
+
+            Map<String, Object> reqMap = new HashMap<>();
+            reqMap.put("traceId", record.getTraceId());
+
+            try {
+                final JSONObject resultJson = umsLogic.querySubsidyApplyTrace(websit.getYunSpCode(), record.getReqSn(), JSONUtil.toJsonStr(reqMap),
+                        websit.getUmsPublicKey(), websit.getUmsPrivateKey());
+
+                final String traceStatus = resultJson.getObject("traceStatus", String.class);
+                final String billId = resultJson.getObject("billId", String.class);
+                final String remark = resultJson.getObject("Remark", String.class);
+
+                record.setTraceStatus(traceStatus)
+                        .setBillId(billId)
+                        .setRemark(remark)
+                        .setErrCount(0)
+                        .setSysErr("");
+
+            } catch (Exception e) {
+                record.setErrCount(record.getErrCount() + 1)
+                        .setSysErr(e.getMessage());
+            }
+        }
+
+        umsSupplementRecordService.saveOrUpdateBatch(recordList);
+    }
+
+    /**
+     * 对已补贴记录上传处理成功的轮询
+     */
+    @Scheduled(fixedDelay = 60 * 1000)
+    public void uploadTraceRecordSuccessTask() {
+        // 审核状态 状态  D1:待复审 D2:复审成功 D3:复审失败 E1:待终审 E2:终审成功 E3:终审失败 E:已放款  ER:异常 ED:失败 UN:未知 QN:退资格未知
+        List<UmsSupplementRecord> recordList = umsSupplementRecordService.lambdaQuery()
+                .eq(UmsSupplementRecord::getStatus,"END")
+                .eq(UmsSupplementRecord::getTraceStatus, "1")
+                .in(UmsSupplementRecord::getCheckStatus, "", "D1", "D2", "E1", "E2")
+                .lt(UmsSupplementRecord::getErrCount, 5)
+                .list();
+
+        if (CollectionUtil.isEmpty(recordList)) {
+            return;
+        }
+
+        final List<AdminCompanyWechat> companyWechatList = adminCompanyWechatService.lambdaQuery()
+                .list();
+
+        final List<AdminWebsit> websitList = adminWebsitService.lambdaQuery()
+                .in(AdminWebsit::getWebsitId, recordList.stream()
+                        .map(UmsSupplementRecord::getWebsitId)
+                        .collect(Collectors.toList()))
+                .list();
+
+        final Map<String, AdminCompanyWechat> companyWechatMap = companyWechatList.stream()
+                .collect(Collectors.toMap(AdminCompanyWechat::getCompanyWechatId, Function.identity()));
+
+        final Map<String, AdminWebsit> adminWebsitMap = websitList.stream()
+                .collect(Collectors.toMap(AdminWebsit::getWebsitId, Function.identity()));
+
+        for (UmsSupplementRecord record : recordList) {
+            final AdminCompanyWechat companyWechat = companyWechatMap.get(record.getCompanyWechatId());
+            final AdminWebsit websit = adminWebsitMap.get(record.getWebsitId());
+            if (Objects.isNull(companyWechat)) {
+                record.setErrCount(record.getErrCount() + 1)
+                        .setSysErr("缺少商户信息");
+                continue;
+            }
+            if (Objects.isNull(websit)) {
+                record.setErrCount(record.getErrCount() + 1)
+                        .setSysErr("缺少商家信息");
+                continue;
+            }
+
+            record.setReqSn((DateUtil.format(DateUtil.date(), DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")) + IdUtil.fastSimpleUUID()));
+
+            Map<String, Object> reqMap = new HashMap<>();
+            reqMap.put("billId", record.getBillId());
+            reqMap.put("activeBigClass", "ELEC");
+
+            try {
+                final JSONObject resultJson = umsLogic.querySubsidyApply(websit.getYunSpCode(), record.getReqSn(), JSONUtil.toJsonStr(reqMap),
+                        websit.getUmsPublicKey(), websit.getUmsPrivateKey());
+
+                final String checkStatus = resultJson.getObject("checkStatus", String.class);
+                final String grantFlag = resultJson.getObject("grantFlag", String.class);
+                final String reason = resultJson.getObject("reason", String.class);
+
+                record.setCheckStatus(checkStatus)
+                        .setGrantFlag(grantFlag)
+                        .setReason(reason)
+                        .setErrCount(0)
+                        .setSysErr("");
+
+            } catch (Exception e) {
+                record.setErrCount(record.getErrCount() + 1)
+                        .setSysErr(e.getMessage());
+            }
+        }
+
+        umsSupplementRecordService.saveOrUpdateBatch(recordList);
+    }
+}

+ 160 - 0
src/main/java/com/gree/mall/manager/utils/EncryptUtils.java

@@ -0,0 +1,160 @@
+package com.gree.mall.manager.utils;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import javax.crypto.Cipher;
+import java.nio.charset.StandardCharsets;
+import java.security.*;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Base64;
+
+public class EncryptUtils {
+
+    static {
+        Security.addProvider(new BouncyCastleProvider()); // 注册 Bouncy Castle 提供者
+    }
+
+    /**
+     * 加密 encBizReqData(直接使用 Base64 公钥字符串,不含 PEM 头尾)
+     * @param publicKeyBase64  粤焕新提供的公钥(Base64 字符串,无头尾标识)
+     * @param jsonData        原始业务 JSON 数据
+     */
+    public static String encryptBizReqData(String publicKeyBase64, String jsonData) throws Exception {
+        // 1. 加载公钥
+        byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyBase64);
+        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
+        PublicKey publicKey = KeyFactory.getInstance("RSA", "BC").generatePublic(keySpec); // 指定 BC 提供者
+
+        // 2. 使用 RSA/ECB/PKCS1Padding 加密
+        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC");
+        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
+        byte[] encryptedBytes = cipher.doFinal(jsonData.getBytes(StandardCharsets.UTF_8));
+
+        // 3. 返回 Base64 编码结果
+        return Base64.getEncoder().encodeToString(encryptedBytes);
+    }
+
+    /**
+     * 生成 sign 签名(直接使用 Base64 私钥字符串,不含 PEM 头尾)
+     * @param privateKeyBase64 客户系统的私钥(Base64 字符串,无头尾标识)
+     * @param encBizReqData    加密后的 encBizReqData 字符串
+     */
+    public static String generateSign(String privateKeyBase64, String encBizReqData) throws Exception {
+        // 1. 加载私钥
+        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyBase64);
+        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
+        PrivateKey privateKey = KeyFactory.getInstance("RSA", "BC").generatePrivate(keySpec);
+
+        // 2. 使用 SHA256withRSA 签名
+        Signature signature = Signature.getInstance("SHA256withRSA", "BC");
+        signature.initSign(privateKey);
+        signature.update(encBizReqData.getBytes(StandardCharsets.UTF_8));
+        byte[] signBytes = signature.sign();
+
+        // 3. 返回 Base64 编码结果
+        return Base64.getEncoder().encodeToString(signBytes);
+    }
+
+    //------------------------- 解密 encBizRespData -------------------------
+    /**
+     * 解密 encBizRespData(使用客户系统私钥字符串)
+     * @param privateKeyBase64 客户系统的私钥(Base64 字符串,无 PEM 头尾)
+     * @param encBizRespData   Base64 加密数据
+     * @return 解密后的明文 JSON 字符串
+     */
+    public static String decryptEncBizRespData(String privateKeyBase64, String encBizRespData)
+            throws GeneralSecurityException {
+
+        // 1. 加载客户系统私钥
+        PrivateKey privateKey = loadPrivateKey(privateKeyBase64);
+
+        // 2. 解码 Base64 数据
+        byte[] encryptedBytes = Base64.getDecoder().decode(encBizRespData);
+
+        // 3. 使用 RSA 私钥解密
+        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC");
+        cipher.init(Cipher.DECRYPT_MODE, privateKey);
+        byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
+
+        return new String(decryptedBytes, StandardCharsets.UTF_8);
+    }
+
+    //------------------------- 验证签名 sign -------------------------
+    /**
+     * 验证签名 sign(使用粤焕新服务端公钥字符串)
+     * @param publicKeyBase64 粤焕新服务端的公钥(Base64 字符串,无 PEM 头尾)
+     * @param encBizRespData  加密数据原文(Base64 字符串)
+     * @param sign            Base64 签名值
+     */
+    public static boolean verifySign(String publicKeyBase64, String encBizRespData, String sign)
+            throws GeneralSecurityException {
+
+        // 1. 加载粤焕新公钥
+        PublicKey publicKey = loadPublicKey(publicKeyBase64);
+
+        // 2. 解码签名
+        byte[] signatureBytes = Base64.getDecoder().decode(sign);
+
+        // 3. 使用 SHA256withRSA 验证签名(注意文档中 signMsg 参数为 "SHAZ56withRSA",需确认是否笔误)
+        Signature signature = Signature.getInstance("SHA256withRSA", "BC");
+        signature.initVerify(publicKey);
+        signature.update(encBizRespData.getBytes(StandardCharsets.UTF_8));
+
+        return signature.verify(signatureBytes);
+    }
+
+    //------------------------- 密钥加载方法 -------------------------
+    // 加载客户系统私钥(Base64 字符串 -> PrivateKey)
+    private static PrivateKey loadPrivateKey(String privateKeyBase64) throws GeneralSecurityException {
+        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyBase64);
+        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
+        return KeyFactory.getInstance("RSA", "BC").generatePrivate(keySpec);
+    }
+
+    // 加载粤焕新公钥(Base64 字符串 -> PublicKey)
+    private static PublicKey loadPublicKey(String publicKeyBase64) throws GeneralSecurityException {
+        byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyBase64);
+        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
+        return KeyFactory.getInstance("RSA", "BC").generatePublic(keySpec);
+    }
+
+    public static void main(String[] args) throws Exception {
+        boolean isEnc = true;
+        if (isEnc) {
+            // 示例:密钥为 Base64 字符串(不包含 PEM 头尾)
+            String publicKeyBase64 = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuV3tO3C...";  // 粤焕新公钥
+            String privateKeyBase64 = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASC..."; // 客户私钥
+
+            // 1. 加密业务数据
+            String jsonData = "{\"productCode\":\"A1001\", \"orderId\":\"20231001123456\"}";
+            String encBizReqData = encryptBizReqData(publicKeyBase64, jsonData);
+            System.out.println("encBizReqData: " + encBizReqData);
+
+            // 2. 生成签名
+            String sign = generateSign(privateKeyBase64, encBizReqData);
+            System.out.println("sign: " + sign);
+        } else {
+
+            try {
+                // 示例参数(需替换为实际值)
+                String clientPrivateKeyBase64 = "MIIEvQIBADANBgkqhki..."; // 客户私钥 Base64
+                String yuehuanxinPublicKeyBase64 = "MIIBIjANBgkqhki..."; // 粤焕新公钥 Base64
+                String encBizRespData = "MIIBIjANBgkqhkiG9w0BAQE...";   // 加密数据
+                String sign = "Ehc/VlLZ6v4X2...";                       // 签名值
+
+                // 1. 解密数据
+                String plainJson = decryptEncBizRespData(clientPrivateKeyBase64, encBizRespData);
+                System.out.println("解密后的明文: " + plainJson);
+
+                // 2. 验证签名
+                boolean isValid = verifySign(yuehuanxinPublicKeyBase64, encBizRespData, sign);
+                System.out.println("签名是否有效? " + isValid);
+
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+}

+ 37 - 0
src/main/java/com/gree/mall/manager/utils/StringUtil.java

@@ -835,4 +835,41 @@ public class StringUtil {
         return count;
     }
 
+    /**
+     * 检查字符串的字节长度是否不超过指定限制
+     * @param input     待检查的字符串
+     * @param maxBytes  最大允许的字节数
+     * @param charset   编码格式(如 StandardCharsets.UTF_8)
+     * @return          true - 符合要求,false - 超过限制
+     */
+    public static boolean isByteLengthValid(String input, int maxBytes, java.nio.charset.Charset charset) {
+        if (input == null) {
+            throw new IllegalArgumentException("输入字符串不能为 null");
+        }
+        byte[] bytes = input.getBytes(charset);
+        return bytes.length <= maxBytes;
+    }
+
+    /**
+     * 判断字符串是否包含指定值忽略大小写, 无需创建临时字符串,内存开销更小。
+     * @param str
+     * @param searchStr
+     * @return
+     */
+    public static boolean containsIgnoreCase(String str, String searchStr) {
+        if (str == null || searchStr == null) {
+            return false;
+        }
+        int searchLength = searchStr.length();
+        if (searchLength == 0) {
+            return true; // 空字符串视为包含
+        }
+        for (int i = 0; i <= str.length() - searchLength; i++) {
+            // 从位置 i 开始,比较长度为 searchLength 的段
+            if (str.regionMatches(true, i, searchStr, 0, searchLength)) {
+                return true;
+            }
+        }
+        return false;
+    }
 }

+ 3 - 0
src/main/java/com/gree/mall/manager/zfire/bean/ZfireParamBean.java

@@ -48,6 +48,9 @@ public class ZfireParamBean {
     private String companyWechatId;
 
     @JsonIgnore
+    private List<String> companyIds;
+
+    @JsonIgnore
     private List<String> adminWebsitIds;
 
     /**

+ 1 - 0
src/main/java/com/gree/mall/manager/zfire/util/FieldUtils.java

@@ -113,6 +113,7 @@ public class FieldUtils {
         if (Objects.nonNull(adminUser.getAdminCompanyWechat())) {
             bean.setCompanyWechatId(adminUser.getAdminCompanyWechat().getCompanyWechatId());
         }
+        bean.setCompanyIds(adminUser.getAdminCompanyIds());
         bean.setAdminWebsitIds(adminUser.getAdminWebsitIds());
         //限制最多查询10w条
         if (bean.getPageSize() != null && (bean.getPageSize().equals(-1) || bean.getPageSize() > Constant.PAGE_SIZE)) {

+ 3 - 0
src/main/resources/bootstrap-dev.properties

@@ -148,4 +148,7 @@ front.top.url=
 
 #云闪付
 ums.url=https://api-mop.chinaums.com
+yue.huan.xin.url=https://mgw-test.gnete.com/gdhlgo2o
+yue.huan.xin.sftp=180.76.208.207
+yue.huan.xin.sftp.port=40980
 

+ 4 - 1
src/main/resources/bootstrap-prd.properties

@@ -114,4 +114,7 @@ tax.invoice.title.url=${gjmall.url}/interface/invoice/title
 express.callback=${gjmall.url}/api/common/express/callback
 
 #云闪付
-ums.url=https://api-mop.chinaums.com
+ums.url=https://api-mop.chinaums.com
+yue.huan.xin.url=https://mgw-test.gnete.com/gdhlgo2o
+yue.huan.xin.sftp=180.76.208.207
+yue.huan.xin.sftp.port=40980

+ 3 - 0
src/main/resources/bootstrap-test.properties

@@ -148,4 +148,7 @@ front.top.url=
 
 #云闪付
 ums.url=https://api-mop.chinaums.com
+yue.huan.xin.url=https://mgw-test.gnete.com/gdhlgo2o
+yue.huan.xin.sftp=180.76.208.207
+yue.huan.xin.sftp.port=40980
 

+ 24 - 0
src/main/resources/mapper/CommonMapper.xml

@@ -21,4 +21,28 @@
             admin_company a
         ${ex.query}
     </select>
+
+    <select id="supplementRecordList" resultType="com.gree.mall.manager.bean.ums.UmsSupplementRecordVO">
+        SELECT
+            ${ex.selected}
+        FROM
+            ums_supplement_record a
+        ${ex.query}
+        <if test="ex.companyIds != null and ex.companyIds.size > 0">
+            and company_wechat_id in
+            <foreach item="item" index="index" collection="ex.companyIds" open="(" separator="," close=")">
+                #{item}
+            </foreach>
+        </if>
+        <if test="ex.adminWebsitIds != null and ex.adminWebsitIds.size > 0">
+            and websit_id in
+            <foreach item="item" index="index" collection="ex.adminWebsitIds" open="(" separator="," close=")">
+                #{item}
+            </foreach>
+        </if>
+        <if test="ex.orderBy == null or ex.orderBy ==''">
+            ORDER BY a.create_time DESC
+        </if>
+        ${ex.orderBy}
+    </select>
 </mapper>