Ver código fonte

调整发送红包提现

yfh 3 semanas atrás
pai
commit
eb5cb8881a

+ 3 - 0
fs-company-app/src/main/java/com/fs/app/controller/FsUserController.java

@@ -391,4 +391,7 @@ public class FsUserController extends AppBaseController {
         System.out.println(s);
     }
 
+    public static void main(String[] args) {
+        System.out.println(encryptPhone("15580815820"));
+    }
 }

+ 19 - 0
fs-service/src/main/java/com/fs/course/param/FsWithdrawalParam.java

@@ -0,0 +1,19 @@
+package com.fs.course.param;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class FsWithdrawalParam {
+    private Long userId;
+
+    //申请提现金额
+    private BigDecimal applicationAmount;
+
+    private String appId;
+
+    private Integer source;//来源 1:h5 2:彩虹汇医小程序 3:APP
+
+
+}

+ 2 - 0
fs-service/src/main/java/com/fs/course/service/IFsUserCourseVideoService.java

@@ -18,6 +18,7 @@ import com.fs.course.vo.newfs.FsUserVideoListVO;
 import com.fs.his.domain.FsUser;
 import com.fs.his.vo.OptionsVO;
 import com.fs.qw.param.FsUserCourseRedPageParam;
+import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.util.List;
@@ -284,4 +285,5 @@ public interface IFsUserCourseVideoService extends IService<FsUserCourseVideo> {
 
     R registerQwFsUserFinish(FsUserCourseVideoAddKfUParam param);
 
+    R withdrawal(FsCourseSendRewardUParam param);
 }

+ 153 - 5
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -105,6 +105,7 @@ import org.springframework.beans.factory.annotation.Value;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.RequestBody;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
@@ -2002,11 +2003,6 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
         packetParam.setOpenId(user.getMpOpenId());
         // 来源是小程序切换openId
         if (param.getSource() == 2) {
-            //处理多小程序问题
-//            Company company = companyMapper.selectCompanyById(param.getCompanyId());
-//            if (company.getCourseMiniAppId()==null){
-//                return R.error("销售公司参数错误,未绑定小程序");
-//            }
             if (user.getMpOpenId() != null && !isNewWxMerchant) {
                 packetParam.setOpenId(user.getMpOpenId());
             } else {
@@ -4859,5 +4855,157 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
 
         return record;
     }
+
+
+
+
+    /**
+     * 用户提现
+     * @param param
+     */
+    @Override
+    @Transactional
+    public R withdrawal(FsCourseSendRewardUParam param) {
+        Long userId = param.getUserId();
+        // 生成锁的key,基于用户ID和视频ID确保同一用户同一视频的请求被锁定
+        String lockKey = "reward_red_lock:user:" + userId;
+        RLock lock = redissonClient.getLock(lockKey);
+
+        try {
+            // 尝试获取锁,等待时间5秒,锁过期时间30秒
+            boolean isLocked = lock.tryLock(5, 300, TimeUnit.SECONDS);
+            if (!isLocked) {
+                logger.warn("获取锁失败,用户ID:{}", userId);
+                return R.error("操作频繁,请稍后再试!");
+            }
+
+            logger.info("成功获取锁,开始处理奖励发放,用户ID:{}", userId);
+            return executeWithdrawal(param);
+
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            logger.error("获取锁被中断,用户ID:{}", userId, e);
+            return R.error("系统繁忙,请重试!");
+        } finally {
+            // 释放锁
+            if (lock.isHeldByCurrentThread()) {
+                lock.unlock();
+                logger.info("释放锁成功,用户ID:{}", userId);
+            }
+        }
+
+
+    }
+
+
+    private R executeWithdrawal(FsCourseSendRewardUParam param){
+        log.info("进入用户判断");
+        FsUser user = fsUserMapper.selectFsUserByUserId(param.getUserId());
+        if (user == null) {
+            return R.error("未识别到用户信息");
+        }
+
+        FsCourseWatchLog log = courseWatchLogMapper.getWatchCourseVideoByFsUser(param.getUserId(), param.getVideoId(), param.getCompanyUserId());
+        if (log == null) {
+            return R.error("无记录");
+        }
+
+        if (log.getLogType() != 2) {
+            return R.error("未完课");
+        }
+
+        FsCourseAnswerLogs rightLog = courseAnswerLogsMapper.selectRightLogByCourseVideo(param.getVideoId(), param.getUserId(), param.getQwUserId());
+        if (rightLog == null) {
+            logger.error("未答题:{}", param.getUserId());
+            return R.error("未答题");
+        }
+
+        FsCourseRedPacketLog fsCourseRedPacketLog = redPacketLogMapper.selectUserFsCourseRedPacketLog(param.getVideoId(), param.getUserId(), param.getPeriodId());
+
+        if (log.getRewardType() != null) {
+            if (log.getRewardType() == 1) {
+                if (fsCourseRedPacketLog != null && fsCourseRedPacketLog.getStatus() == 1) {
+                    return R.error("已领取该课程奖励,不可重复领取!");
+                }
+                if (fsCourseRedPacketLog != null && fsCourseRedPacketLog.getStatus() == 0) {
+                    if (StringUtils.isNotEmpty(fsCourseRedPacketLog.getResult())) {
+                        R r = JSON.parseObject(fsCourseRedPacketLog.getResult(), R.class);
+                        return r;
+                    } else {
+                        return R.error("操作频繁,请稍后再试!");
+                    }
+                }
+            } else if (log.getRewardType() == 2) {
+                return R.error("已领取该课程奖励,不可重复领取!");
+            }
+        }
+
+        // 获取视频信息
+        FsUserCourseVideo video = fsUserCourseVideoMapper.selectFsUserCourseVideoByVideoId(param.getVideoId());
+
+        // 获取配置信息
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+
+        // 判断来源是否是app,如是app,则发放积分奖励
+        int sourceApp = 3;
+        if (sourceApp == param.getSource() /*&& !CloudHostUtils.hasCloudHostName("中康")*/) {
+            return sendIntegralReward(param, user, log, config);
+        }
+
+        // 根据奖励类型发放不同奖励
+        switch (config.getRewardType()) {
+            // 红包奖励
+            case 1:
+                //来源是小程序切换openId
+                WxSendRedPacketParam packetParam = new WxSendRedPacketParam();
+                String openId = getOpenId(param, user);
+                if (StringUtils.isBlank(openId)) {
+                    return R.error("请重新使用微信登录");
+                }
+                packetParam.setOpenId(openId);
+                packetParam.setAmount(fsCourseRedPacketLog.getAmount());
+                packetParam.setSource(param.getSource());
+                packetParam.setAppId(param.getAppId());
+                return paymentService.sendAppRedPacket(packetParam);
+            // 积分奖励
+            case 2:
+                return sendIntegralReward(param, user, log, config);
+            // 红包+积分
+            case 3:
+                R sendRed = sendRedPacketRewardFsUser(param, user, log, video, config);
+                if (!Objects.equals(sendRed.get("code"), 200)) {
+                    return sendRed;
+                }
+                return sendIntegralReward(param, user, log, config);
+            default:
+                return R.error("参数错误!");
+        }
+    }
+
+    /**
+     * 获取用户openId
+     */
+    private String getOpenId(FsCourseSendRewardUParam param, FsUser user) {
+        if (param.getSource()==2){
+            FsUserWx fsUserWx = fsUserWxService.selectByAppIdAndUserId(param.getAppId(),user.getUserId(),1);
+            if (Objects.nonNull(fsUserWx) && StringUtils.isNotBlank(fsUserWx.getOpenId())) {
+                return fsUserWx.getOpenId();
+            }
+
+            if (StringUtils.isNotBlank(user.getCourseMaOpenId())) {
+                try {
+                    handleFsUserWx(user,param.getAppId());
+                } catch (Exception e){
+                    log.error("【更新或插入用户与小程序的绑定关系失败】:{}", user.getUserId(), e);
+                }
+                return user.getCourseMaOpenId();
+            }
+        } else if (param.getSource()==3){
+            return user.getAppOpenId();
+        }
+
+        return user.getMpOpenId();
+    }
 }
 

+ 49 - 0
fs-service/src/main/java/com/fs/his/config/AppRedPacketConfig.java

@@ -0,0 +1,49 @@
+package com.fs.his.config;
+
+import lombok.Data;
+
+@Data
+public class AppRedPacketConfig {
+    //积分提现商户配置
+    private Integer isNew;//0:老商户 商家转账到零钱 1:新商户 商家转账
+
+    /**
+     * 商户号.
+     */
+    private String mchId;
+    /**
+     * 商户密钥.
+     */
+    private String mchKey;
+
+    /**
+     * p12证书文件的绝对路径或者以classpath:开头的类路径.
+     */
+    private String keyPath;
+
+    /**
+     * apiclient_key.pem证书文件的绝对路径或者以classpath:开头的类路径.
+     */
+    private String privateKeyPath;
+
+    /**
+     * apiclient_cert.pem证书文件的绝对路径或者以classpath:开头的类路径.
+     */
+    private String privateCertPath;
+
+    /**
+     * apiV3 秘钥值.
+     */
+    private String apiV3Key;
+    /**
+     * 公钥ID
+     */
+    private String publicKeyId;
+
+    /**
+     * pub_key.pem证书文件的绝对路径或者以classpath:开头的类路径.
+     */
+    private String publicKeyPath;
+
+    private String notifyUrl;
+}

+ 4 - 0
fs-service/src/main/java/com/fs/his/service/IFsStorePaymentService.java

@@ -142,4 +142,8 @@ public interface IFsStorePaymentService
     void synchronizePayStatus();
 
     List<FsStorePayment> selectAllPayment();
+
+    String integralV3TransferNotify(String notifyData, HttpServletRequest request);
+
+    R sendAppRedPacket(WxSendRedPacketParam packetParam);
 }

+ 1 - 0
fs-service/src/main/java/com/fs/his/service/IFsUserService.java

@@ -10,6 +10,7 @@ import com.fs.common.core.domain.ResponseResult;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.param.LoginMaWxParam;
 import com.fs.course.param.CourseAnalysisParam;
+import com.fs.course.param.FsWithdrawalParam;
 import com.fs.course.param.newfs.FsUserCourseBeMemberParam;
 import com.fs.course.vo.newfs.FsCourseAnalysisVO;
 import com.fs.his.domain.FsUser;

+ 163 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsStorePaymentServiceImpl.java

@@ -45,6 +45,8 @@ import com.fs.course.mapper.FsCoursePlaySourceConfigMapper;
 import com.fs.course.service.IFsCourseRedPacketLogService;
 import com.fs.course.service.IFsUserCourseOrderService;
 import com.fs.course.service.IFsUserVipOrderService;
+import com.fs.his.config.AppRedPacketConfig;
+import com.fs.his.config.IntegralConfig;
 import com.fs.his.domain.*;
 import com.fs.his.enums.PaymentMethodEnum;
 import com.fs.his.mapper.*;
@@ -1910,5 +1912,166 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
         return R.error("支付失败!");
 
     }
+    @Override
+    public String integralV3TransferNotify(String notifyData, HttpServletRequest request) {
+        logger.info("zyp \n【收到转账回调V3】:{}",notifyData);
+        try {
+            String json = configService.selectConfigByKey("his.integral");
+            IntegralConfig config = JSONUtil.toBean(json, IntegralConfig.class);
+            //创建微信订单
+            WxPayConfig payConfig = new WxPayConfig();
+            BeanUtils.copyProperties(config,payConfig);
+            WxPayService wxPayService = new WxPayServiceImpl();
+            wxPayService.setConfig(payConfig);
+            SignatureHeader signatureHeader = new SignatureHeader();
+            signatureHeader.setTimeStamp(request.getHeader("Wechatpay-Timestamp"));
+            signatureHeader.setNonce(request.getHeader("Wechatpay-Nonce"));
+            signatureHeader.setSerial(request.getHeader("Wechatpay-Serial"));
+            signatureHeader.setSignature(request.getHeader("Wechatpay-Signature"));
+            TransferBillsNotifyResult result = wxPayService.parseTransferBillsNotifyV3Result(notifyData,signatureHeader);
+            if (result.getResult().getState().equals("SUCCESS")) {
+                return WxPayNotifyResponse.success("处理成功");
+            }else {
+                return WxPayNotifyResponse.fail("");
+            }
+        } catch (WxPayException e) {
+            e.printStackTrace();
+            logger.error("zyp \n【转账回调异常】:{}", e.getReturnMsg());
+            return WxPayNotifyResponse.fail(e.getMessage());
+        }
+    }
+
+    @Override
+    @Transactional
+    public R sendAppRedPacket(WxSendRedPacketParam param) {
+        //组合返回参数
+        R result = new R();
+        String json = configService.selectConfigByKey("his.AppRedPacket");
+        AppRedPacketConfig config = JSONUtil.toBean(json, AppRedPacketConfig.class);
+        if (config.getIsNew() != null && config.getIsNew() == 1) {
+            result = sendRedPacketV3(param, config);
+        } else {
+            result= sendRedPacketLegacy(param, config);
+        }
+
+        result.put("mchId", config.getMchId());
+        result.put("isNew",config.getIsNew());
+        logger.info("App提现返回:{}",result);
+        return result;
+    }
+
+    // 内部方法:处理新版本的发红包逻辑
+    private R sendRedPacketV3(WxSendRedPacketParam param,AppRedPacketConfig config) {
+
+        WxPayConfig payConfig = new WxPayConfig();
+        BeanUtils.copyProperties(config, payConfig);
+        WxPayService wxPayService = new WxPayServiceImpl();
+        wxPayService.setConfig(payConfig);
+        TransferService transferService = wxPayService.getTransferService();
+
+        TransferBillsRequest request = new TransferBillsRequest();
+        request.setAppid(param.getAppId());
+        request.setOpenid(param.getOpenId());
+
+        String code =  OrderCodeUtils.getOrderSn();
+        if(StringUtils.isEmpty(code)){
+            return R.error("订单生成失败,请重试");
+        }
+//        String code = String.valueOf(IdUtil.getSnowflake(0, 0).nextId());
+        request.setOutBillNo("fsAppRed" + code);
+        if (param.getAmount() == null) {
+            return R.error();
+        }
+        Integer amount = WxPayUnifiedOrderRequest.yuanToFen(param.getAmount().toString());
+        request.setTransferAmount(amount);
+        request.setTransferRemark("积分提现");
+        request.setUserRecvPerception("活动奖励");
+        request.setNotifyUrl(config.getNotifyUrl());
+        request.setTransferSceneId("1000");
+
+        // 设置场景信息
+        List<TransferBillsRequest.TransferSceneReportInfo> transferSceneReportInfos = new ArrayList<>();
+        TransferBillsRequest.TransferSceneReportInfo info1 = new TransferBillsRequest.TransferSceneReportInfo();
+        info1.setInfoType("活动名称");
+        info1.setInfoContent("积分提现");
+        transferSceneReportInfos.add(info1);
+
+        TransferBillsRequest.TransferSceneReportInfo info2 = new TransferBillsRequest.TransferSceneReportInfo();
+        info2.setInfoType("奖励说明");
+        info2.setInfoContent("积分提现");
+        transferSceneReportInfos.add(info2);
+        request.setTransferSceneReportInfos(transferSceneReportInfos);
+
+
+        try {
+            TransferBillsResult transferBillsResult = transferService.transferBills(request);
+            logger.info("商家转账支付完成:[msg:{}]", transferBillsResult);
+            return R.ok("发送红包成功").put("data", transferBillsResult).put("mchId", config.getMchId())
+                    .put("package",transferBillsResult.getPackageInfo())
+                    .put("appId",param.getAppId())
+                    .put("orderCode",request.getOutBillNo());
+        } catch (Exception e) {
+            logger.error("商家转账支付失败:参数: {} :原因: {}", com.alibaba.fastjson.JSON.toJSONString(param), e.getMessage(),e);
+            throw new RuntimeException(e);
+        }
+    }
+
+    private R sendRedPacketLegacy(WxSendRedPacketParam param, AppRedPacketConfig config) {
+        //如果服务号的配置存在,小程序红包接口可以使用服务号来发红包,重新赋值
+        //仅老商户支持
+        if (param.getOpenId()!=null && StringUtils.isNotEmpty(param.getAppId())){
+//            config.setAppId(param.getAppId());
+            param.setOpenId(param.getOpenId());
+        }
+        WxPayConfig payConfig = new WxPayConfig();
+        BeanUtils.copyProperties(config, payConfig);
+        WxPayService wxPayService = new WxPayServiceImpl();
+        wxPayService.setConfig(payConfig);
+        TransferService transferService = wxPayService.getTransferService();
+
+        TransferBatchesRequest request = new TransferBatchesRequest();
+//        request.setAppid(config.getAppId());
+
+
+        // todo 如果未配置负载均衡请还原原本的单号方式
+//        String code = IdUtil.getSnowflake(0, 0).nextIdStr();
+        String code =  OrderCodeUtils.getOrderSn();
+        if(StringUtils.isEmpty(code)){
+            return R.error("红包单号生成失败,请重试");
+        }
+        request.setOutBatchNo("fsIntegral" + code);
+        request.setBatchRemark("积分提现");
+        request.setBatchName("积分提现");
+        Integer amount = WxPayUnifiedOrderRequest.yuanToFen(param.getAmount().toString());
+        request.setTotalAmount(amount);
+        request.setTotalNum(1);
+        request.setNotifyUrl(config.getNotifyUrl());
+
+        ArrayList<TransferBatchesRequest.TransferDetail> transferDetailList = new ArrayList<>();
+        TransferBatchesRequest.TransferDetail transferDetail = new TransferBatchesRequest.TransferDetail();
+        transferDetail.setOpenid(param.getOpenId());
+        String code1 = IdUtil.getSnowflake(0, 0).nextIdStr();
+        transferDetail.setOutDetailNo("fsCourse" + code1);
+        transferDetail.setTransferAmount(amount);
+        transferDetail.setTransferRemark("积分提现成功!");
+        transferDetailList.add(transferDetail);
+        request.setTransferDetailList(transferDetailList);
+
+        try {
+            TransferBatchesResult transferBatchesResult = transferService.transferBatches(request);
+            return R.ok("积分提现成功").put("orderCode", transferBatchesResult.getOutBatchNo()).put("batchId", transferBatchesResult.getBatchId()).put("mchId", config.getMchId());
+        } catch (Exception e) {
+            logger.error("商家转账支付失败:参数: {} :原因: {}", com.alibaba.fastjson.JSON.toJSONString(param), e.getMessage(),e);
+            if (e instanceof WxPayException) {
+//            if (e instanceof WxPayException && "济南联志健康".equals(signProjectName)) {
+                WxPayException wxPayException = (WxPayException) e;
+                String customErrorMsg = wxPayException.getCustomErrorMsg();
+                if (null != customErrorMsg && customErrorMsg.startsWith("商户运营账户资金不足")) {
+                    return R.error("[积分提现] 账户余额不足,请联系管理员!");
+                }
+            }
+            throw new RuntimeException(e);
+        }
+    }
 
 }

+ 16 - 5
fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java

@@ -9,11 +9,13 @@ import java.time.LocalDateTime;
 import java.util.*;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
 import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
 import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
@@ -36,9 +38,11 @@ import com.fs.company.cache.ICompanyUserCacheService;
 import com.fs.company.domain.*;
 import com.fs.company.mapper.*;
 import com.fs.company.service.ICompanyTagService;
-import com.fs.course.domain.FsUserCompanyUser;
+import com.fs.course.config.CourseConfig;
+import com.fs.course.domain.*;
 import com.fs.course.mapper.FsUserCourseVideoMapper;
 import com.fs.course.param.CourseAnalysisParam;
+import com.fs.course.param.FsWithdrawalParam;
 import com.fs.course.param.newfs.FsUserCourseBeMemberParam;
 import com.fs.course.param.newfs.UserCourseVideoPageParam;
 import com.fs.course.service.IFsUserCompanyUserService;
@@ -55,9 +59,8 @@ import com.fs.his.mapper.*;
 import com.fs.his.param.FindUserByParam;
 import com.fs.his.param.FsUserAddIntegralTemplateParam;
 import com.fs.his.param.FsUserParam;
-import com.fs.his.service.IFsUserIntegralLogsService;
-import com.fs.his.service.IFsUserProjectTagService;
-import com.fs.his.service.IFsUserWxService;
+import com.fs.his.param.WxSendRedPacketParam;
+import com.fs.his.service.*;
 import com.fs.his.vo.FsUserVO;
 import com.fs.his.vo.FsUserExportListVO;
 import com.fs.his.vo.FsUserFollowDoctorVO;
@@ -88,8 +91,10 @@ import com.fs.system.service.ISysConfigService;
 import com.fs.watch.domain.WatchUser;
 import com.fs.watch.domain.vo.FsUserAndCompanyAndDoctorVo;
 import com.fs.watch.service.WatchUserService;
+import com.github.binarywang.wxpay.bean.transfer.TransferBillsResult;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.http.client.ClientProtocolException;
 import org.apache.http.client.methods.CloseableHttpResponse;
@@ -99,12 +104,13 @@ import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
 import org.apache.http.util.Asserts;
 import org.apache.http.util.EntityUtils;
+import org.redisson.api.RLock;
+import org.redisson.api.RedissonClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
-import com.fs.his.service.IFsUserService;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -119,10 +125,15 @@ import static com.fs.hisStore.enums.BillDetailEnum.CATEGORY_3;
  * @date 2023-06-07
  */
 @Service
+@Slf4j
 public class FsUserServiceImpl implements IFsUserService {
     Logger logger = LoggerFactory.getLogger(getClass());
     ;
     @Autowired
+    private IFsStorePaymentService paymentService;
+    @Autowired
+    private RedissonClient redissonClient;
+    @Autowired
     private FsUserMapper fsUserMapper;
     @Autowired
     private CompanyUserMapper companyUserMapper;

+ 2 - 0
fs-service/src/main/resources/application-druid-nmgyt.yml

@@ -157,3 +157,5 @@ im:
     type: NONE
 #是否为新商户,新商户不走mpOpenId
 isNewWxMerchant: true
+push:
+    url:

+ 7 - 0
fs-user-app/src/main/java/com/fs/app/controller/WxPayController.java

@@ -21,10 +21,12 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 
 @Api("微信支付接口")
 @RestController
@@ -118,5 +120,10 @@ public class WxPayController {
         }
     }
 
+    @PostMapping( "/integralV3TransferNotify")
+    public String integralV3TransferNotify(@RequestBody String notifyData, HttpServletRequest request, HttpServletResponse response) throws Exception {
+        return paymentService.integralV3TransferNotify(notifyData,request);
+    }
+
 
 }

+ 13 - 0
fs-user-app/src/main/java/com/fs/app/controller/course/CourseFsUserController.java

@@ -192,4 +192,17 @@ public class CourseFsUserController extends AppBaseController {
         return R.error();
     }
 
+    @Login
+    @ApiOperation("发送红包(以积分提现的形式)")
+    @PostMapping("/withdrawal")
+    @RepeatSubmit
+    public R withdrawal(@RequestBody FsCourseSendRewardUParam param){
+        String userId = getUserId();
+        if(userId == null){
+            return R.error("请先登录!");
+        }
+        param.setUserId(Long.parseLong(userId));
+        return R.ok(courseVideoService.withdrawal(param));
+    }
+
 }