Browse Source

Merge branch 'master' of http://git.dayaedu.com/yonge/cooleshow

liujunchi 3 years ago
parent
commit
a757504a76
23 changed files with 743 additions and 20 deletions
  1. 14 1
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/HomeController.java
  2. 12 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dao/CourseScheduleDao.java
  3. 7 1
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dao/UserWithdrawalDao.java
  4. 1 1
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/LiveRoom.java
  5. 7 4
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/CourseScheduleEnum.java
  6. 1 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/RoomTypeEnum.java
  7. 15 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/HomeService.java
  8. 5 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/LiveRoomService.java
  9. 8 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/UserWithdrawalService.java
  10. 2 1
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CourseScheduleServiceImpl.java
  11. 95 5
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/HomeServiceImpl.java
  12. 59 2
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/LiveRoomServiceImpl.java
  13. 15 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/SysUserContractRecordServiceImpl.java
  14. 10 4
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/UserWithdrawalServiceImpl.java
  15. 85 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/CourseHomeVo.java
  16. 32 0
      cooleshow-user/user-biz/src/main/resources/config/mybatis/CourseScheduleMapper.xml
  17. 7 0
      cooleshow-user/user-biz/src/main/resources/config/mybatis/UserWithdrawalMapper.xml
  18. 6 0
      cooleshow-user/user-student/src/main/java/com/yonge/cooleshow/student/controller/StudentLiveRoomController.java
  19. 1 1
      cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/config/ResourceServerConfig.java
  20. 6 0
      cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/controller/TeacherLiveRoomController.java
  21. 95 0
      cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/controller/WithdrawController.java
  22. 113 0
      toolset/thirdparty-component/src/main/java/com/yonge/toolset/thirdparty/lingxinpay/Md5EncryptUtils.java
  23. 147 0
      toolset/thirdparty-component/src/main/java/com/yonge/toolset/thirdparty/lingxinpay/Withdraw.java

+ 14 - 1
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/HomeController.java

@@ -3,6 +3,7 @@ package com.yonge.cooleshow.admin.controller;
 import com.yonge.cooleshow.biz.dal.dto.req.TotalReq;
 import com.yonge.cooleshow.biz.dal.service.HomeService;
 import com.yonge.cooleshow.biz.dal.service.MusicSheetService;
+import com.yonge.cooleshow.biz.dal.vo.CourseHomeVo;
 import com.yonge.cooleshow.biz.dal.vo.HomeMusicSheetVo;
 import com.yonge.cooleshow.biz.dal.vo.res.HomeTotalStudent;
 import com.yonge.cooleshow.biz.dal.vo.res.HomeTotalTeacher;
@@ -15,6 +16,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
 import javax.validation.Valid;
+import java.util.Map;
 
 @RestController
 @RequestMapping("/home")
@@ -47,11 +49,22 @@ public class HomeController extends BaseController {
         return homeService.totalStudent(totalReq);
     }
 
-
     @ApiOperation(value = "首页曲目点播数据")
     @PostMapping("/musicSheet")
     public HttpResponseResult<HomeMusicSheetVo> musicSheet() {
         return succeed(musicSheetService.getMusicSheetHome());
     }
 
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "year", dataType = "Integer", value = "年"),
+            @ApiImplicitParam(name = "month", dataType = "Integer", value = "月"),
+            @ApiImplicitParam(name = "type", dataType = "String", value = "类型  PRACTICE陪练课  LIVE直播课"),
+    })
+    @ApiOperation(value = "获取首页课程数据")
+    @PostMapping("/courseHome")
+    @PreAuthorize("@pcs.hasPermissions('home/courseHome')")
+    public HttpResponseResult<CourseHomeVo> queryCourseHomeData(@RequestBody Map<String,Object> param) {
+        return succeed(homeService.queryCourseHomeData(param));
+    }
+
 }

+ 12 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dao/CourseScheduleDao.java

@@ -185,5 +185,17 @@ public interface CourseScheduleDao extends BaseMapper<CourseSchedule> {
 
     //根据id统计评价
     Integer countReplies(Long studentId);
+
+    /**
+     * 按年查询首页课程数据
+     * @param param
+     */
+    List<CourseHomeVo.CourseHomeInfoVo> queryCourseHomeOfYear(@Param("param") Map<String, Object> param);
+
+    /**
+     * 按月查询首页课程数据
+     * @param param
+     */
+    List<CourseHomeVo.CourseHomeInfoVo> queryCourseHomeOfMonth(@Param("param") Map<String, Object> param);
 }
 

+ 7 - 1
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dao/UserWithdrawalDao.java

@@ -1,6 +1,7 @@
 package com.yonge.cooleshow.biz.dal.dao;
 
 import java.util.List;
+import java.util.Map;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -35,5 +36,10 @@ public interface UserWithdrawalDao extends BaseMapper<UserWithdrawal>{
 	 */
 	List<UserWithdrawalVo> selectList(@Param("param") TeacherWithdrawalSearch teacherWithdrawal);
 
-	
+	/**
+	 * @Description: 提现成功回调
+	 * @Author: cy
+	 * @Date: 2022/5/9
+	 */
+    void withdrawSuccess(Map<String, Object> withdrawRecord);
 }

+ 1 - 1
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/LiveRoom.java

@@ -63,7 +63,7 @@ public class LiveRoom implements Serializable {
     private Integer roomState;
 
     @TableField("type_")
-    @ApiModelProperty(value = "房间类型 live直播课  temp临时直播间")
+    @ApiModelProperty(value = "房间类型 live直播课  temp临时直播间 practice陪练课")
     private String type;
 
     @TableField("cover_pic_")

+ 7 - 4
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/CourseScheduleEnum.java

@@ -2,6 +2,7 @@ package com.yonge.cooleshow.biz.dal.enums;
 
 import com.baomidou.mybatisplus.annotation.EnumValue;
 import com.yonge.toolset.base.enums.BaseEnum;
+import com.yonge.toolset.base.exception.BizException;
 
 import java.util.Arrays;
 import java.util.List;
@@ -39,7 +40,7 @@ public enum CourseScheduleEnum implements BaseEnum<String,CourseScheduleEnum> {
     public static CourseScheduleEnum existCourseType(String code, String errMsg) {
         CourseScheduleEnum[] values = {PRACTICE, LIVE};
         existCourse(values, code, errMsg);
-        //陪练课-校验学生时间是否交集
+        //返回枚举对象
         if (code.equals(CourseScheduleEnum.PRACTICE.getCode())) {
             return CourseScheduleEnum.PRACTICE;
         } else {
@@ -59,10 +60,12 @@ public enum CourseScheduleEnum implements BaseEnum<String,CourseScheduleEnum> {
     }
 
     private static void existCourse(CourseScheduleEnum[] values, String code, String errMsg) {
-        List<String> collect = Arrays.stream(values).map(CourseScheduleEnum::getCode).collect(Collectors.toList());
-        boolean typeFlag = collect.contains(code);
+        List<String> collect = Arrays.stream(values)
+                .map(CourseScheduleEnum::getCode)
+                .collect(Collectors.toList());
+        boolean typeFlag = collect.contains(code.toUpperCase());
         if (!typeFlag) {
-            throw new RuntimeException(errMsg);
+            throw new BizException(errMsg);
         }
     }
 

+ 1 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/RoomTypeEnum.java

@@ -7,6 +7,7 @@ import java.util.stream.Collectors;
 public enum RoomTypeEnum {
 
     LIVE("LIVE", "直播课"),
+    PRACTICE("PRACTICE", "陪练课"),
     TEMP("TEMP", "临时直播间");
 
     private String code;

+ 15 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/HomeService.java

@@ -1,11 +1,14 @@
 package com.yonge.cooleshow.biz.dal.service;
 
 import com.yonge.cooleshow.biz.dal.dto.req.TotalReq;
+import com.yonge.cooleshow.biz.dal.vo.CourseHomeVo;
 import com.yonge.cooleshow.biz.dal.vo.res.HomeTotalStudent;
 import com.yonge.cooleshow.biz.dal.vo.res.HomeTotalTeacher;
 import com.yonge.cooleshow.biz.dal.vo.res.HomeUserToDoNum;
 import com.yonge.cooleshow.common.entity.HttpResponseResult;
 
+import java.util.Map;
+
 /**
  * @Author: liweifan
  * @Data: 2022/3/30 18:07
@@ -35,4 +38,16 @@ public interface HomeService {
      * @return: com.yonge.cooleshow.common.entity.HttpResponseResult<com.yonge.cooleshow.biz.dal.vo.res.HomeTotalTeacher>
      */
     HttpResponseResult<HomeTotalStudent> totalStudent(TotalReq totalReq);
+
+    /**
+     * 获取首页课程数据
+     * <p>未完成  未开始&进行中
+     * <p>已完成  已完成课程
+     *
+     * @param param 传入参数
+     *              <p> - year 年
+     *              <p> - month 月
+     *              <p> - type 类型  PRACTICE陪练课  LIVE直播课
+     */
+    CourseHomeVo queryCourseHomeData(Map<String, Object> param);
 }

+ 5 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/LiveRoomService.java

@@ -39,6 +39,11 @@ public interface LiveRoomService extends IService<LiveRoom> {
     void destroyExpiredLiveRoom();
 
     /**
+     * 定时任务-清理过期的房间-陪练课
+     */
+    void destroyExpiredPracticeRoom();
+
+    /**
      * 创建临时房间-直播间
      */
     String createTempLiveRoom(Map<String, Object> param);

+ 8 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/UserWithdrawalService.java

@@ -12,6 +12,7 @@ import com.yonge.cooleshow.common.entity.HttpResponseResult;
 import com.yonge.toolset.utils.easyexcel.ExcelDataReaderProperty;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * 用户账户提现表 服务类
@@ -67,4 +68,11 @@ public interface UserWithdrawalService extends IService<UserWithdrawal>  {
 	 * @return: com.yonge.cooleshow.common.entity.HttpResponseResult<java.util.List<com.yonge.toolset.utils.easyexcel.ErrMsg>>
 	 */
     void importExcel(List<ExcelDataReaderProperty<UserWithdrawalExport>> dataList, Long userId);
+
+	/**
+	 * @Description: 提现成功回调
+	 * @Author: cy
+	 * @Date: 2022/5/9
+	 */
+	void withdrawSuccess(Map<String, Object> withdrawRecord);
 }

+ 2 - 1
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CourseScheduleServiceImpl.java

@@ -124,6 +124,7 @@ public class CourseScheduleServiceImpl extends ServiceImpl<CourseScheduleDao, Co
         param.put("endDate", lastDay.toString());
         param.put("teacherId", getSysUser().getId());
         param.put("type", CourseScheduleEnum.LIVE.getCode());
+        log.info("queryTeacherLiveCourse:{}", param);
         Page<TeacherLiveCourseInfoVo> pageInfo = PageUtil.getPageInfo(param);
         pageInfo.setAsc("a.start_time_");
         IPage<TeacherLiveCourseInfoVo> page = baseMapper.queryLiveTeacherCourse(pageInfo, param);
@@ -845,7 +846,7 @@ public class CourseScheduleServiceImpl extends ServiceImpl<CourseScheduleDao, Co
 
         String orderNo = orderReqInfo.getOrderNo();
         scheduleDto.setType(CourseScheduleEnum.PRACTICE.getCode());
-        scheduleDto.setStatus(CourseGroupEnum.NOT_SALE.getCode());
+        scheduleDto.setStatus(CourseGroupEnum.ING.getCode());
         scheduleDto.setMixStudentNum(1);
         scheduleDto.setStudentId(studentId);
 

+ 95 - 5
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/HomeServiceImpl.java

@@ -1,23 +1,29 @@
 package com.yonge.cooleshow.biz.dal.service.impl;
 
+import com.yonge.cooleshow.biz.dal.dao.CourseScheduleDao;
 import com.yonge.cooleshow.biz.dal.dao.HomeDao;
 import com.yonge.cooleshow.biz.dal.dto.req.TotalReq;
-import com.yonge.cooleshow.biz.dal.enums.PeriodEnum;
+import com.yonge.cooleshow.biz.dal.enums.CourseScheduleEnum;
+import com.yonge.cooleshow.biz.dal.service.CourseScheduleService;
 import com.yonge.cooleshow.biz.dal.service.HomeService;
+import com.yonge.cooleshow.biz.dal.support.WrapperUtil;
+import com.yonge.cooleshow.biz.dal.vo.CourseHomeVo;
 import com.yonge.cooleshow.biz.dal.vo.res.HomeTotalStudent;
 import com.yonge.cooleshow.biz.dal.vo.res.HomeTotalTeacher;
 import com.yonge.cooleshow.biz.dal.vo.res.HomeUserToDoNum;
 import com.yonge.cooleshow.common.entity.HttpResponseResult;
-import com.yonge.toolset.base.exception.BizException;
-import io.swagger.annotations.ApiModelProperty;
+import org.apache.commons.collections.CollectionUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.LocalTime;
 import java.time.temporal.TemporalAdjusters;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.stream.Collectors;
 
 /**
  * @Author: liweifan
@@ -27,6 +33,8 @@ import java.util.List;
 public class HomeServiceImpl implements HomeService {
     @Autowired
     private HomeDao baserMapper;
+    @Autowired
+    private CourseScheduleService courseScheduleService;
 
     @Override
     public HomeUserToDoNum getUserToDoNum() {
@@ -106,4 +114,86 @@ public class HomeServiceImpl implements HomeService {
         total.setInfoList(totalList);
         return HttpResponseResult.succeed(total);
     }
+
+    /**
+     * 获取首页课程数据
+     * <p>未完成  未开始&进行中
+     * <p>已完成  已完成课程
+     *
+     * @param param 传入参数
+     *              <p> - year 年
+     *              <p> - month 月
+     *              <p> - type 类型  PRACTICE陪练课  LIVE直播课
+     */
+    public CourseHomeVo queryCourseHomeData(Map<String, Object> param) {
+        CourseScheduleEnum.existCourseType(WrapperUtil.toStr(param, "type"), "课程类型参数错误");
+        Integer year = WrapperUtil.toInt(param, "year", "年份不能为空!");
+        Integer monthParam = WrapperUtil.toInt(param, "month");
+        //按月查询true  年查询false
+        boolean isYear = monthParam == 0;
+        int month = isYear ? 1 : monthParam;
+
+        LocalDate firstDate;
+        LocalDate endDate;
+        CourseHomeVo result = new CourseHomeVo();
+        CourseScheduleDao courseScheduleServiceDao = courseScheduleService.getDao();
+        firstDate = LocalDate.of(year, month, 1);
+        //获取开始时间
+        if (isYear) {
+            //查询当年最后一天
+            endDate = firstDate.with(TemporalAdjusters.lastDayOfYear());
+        } else {
+            //查询当月最后一天
+            endDate = firstDate.with(TemporalAdjusters.lastDayOfMonth());
+        }
+        param.put("startDate", firstDate);
+        param.put("endDate", endDate);
+
+        //查询数据
+        List<CourseHomeVo.CourseHomeInfoVo> courseYearInfoList;
+        if (isYear) {
+            courseYearInfoList = courseScheduleServiceDao.queryCourseHomeOfYear(param);
+        } else {
+            courseYearInfoList = courseScheduleServiceDao.queryCourseHomeOfMonth(param);
+        }
+        if (CollectionUtils.isEmpty(courseYearInfoList)) {
+            courseYearInfoList = new ArrayList<>();
+        }
+        Map<String, CourseHomeVo.CourseHomeInfoVo> collect = courseYearInfoList.stream()
+                .collect(Collectors.toMap(CourseHomeVo.CourseHomeInfoVo::getDate, Function.identity(), (key1, key2) -> key2));
+        //补全数据
+        while (firstDate.isBefore(endDate)) {
+            CourseHomeVo.CourseHomeInfoVo infoVo = collect.get(firstDate.toString());
+            if (Objects.isNull(infoVo)) {
+                infoVo = new CourseHomeVo.CourseHomeInfoVo();
+                if (isYear) {
+                    infoVo.setDate(firstDate.toString().substring(0, 7));
+                } else {
+                    infoVo.setDate(firstDate.toString());
+                }
+                infoVo.setDoneCount(0);
+                infoVo.setUndoneCount(0);
+                courseYearInfoList.add(infoVo);
+            } else {
+                if (isYear) {
+                    infoVo.setDate(firstDate.toString().substring(0, 7));
+                }
+            }
+            if (isYear) {
+                firstDate = firstDate.plusMonths(1L);
+            } else {
+                firstDate = firstDate.plusDays(1L);
+            }
+        }
+        result.setCourseHomeInfoList(courseYearInfoList);
+        //计算总数方法
+        Function<Function<CourseHomeVo.CourseHomeInfoVo, Integer>, Integer> reduceFunc = (c) -> result.getCourseHomeInfoList().stream()
+                .map(c)
+                .reduce(0, Integer::sum);
+        result.setTotalDoneCount(reduceFunc.apply(CourseHomeVo.CourseHomeInfoVo::getDoneCount));
+        result.setTotalUndoneCount(reduceFunc.apply(CourseHomeVo.CourseHomeInfoVo::getUndoneCount));
+        result.setTotalCount(result.getTotalDoneCount() + result.getTotalUndoneCount());
+        return result;
+    }
+
 }

+ 59 - 2
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/LiveRoomServiceImpl.java

@@ -33,8 +33,7 @@ import java.util.function.BiFunction;
 import java.util.stream.Collectors;
 
 import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.*;
-import static com.yonge.cooleshow.common.constant.SysConfigConstant.DESTROY_EXPIRED_LIVE_ROOM_MINUTE;
-import static com.yonge.cooleshow.common.constant.SysConfigConstant.PRE_CREATE_LIVE_ROOM_MINUTE;
+import static com.yonge.cooleshow.common.constant.SysConfigConstant.*;
 
 /**
  * 直播房间与课程的关系表表(LiveRoom)表服务实现类
@@ -340,6 +339,64 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
     }
 
     /**
+     * 定时任务-清理过期的房间-陪练课
+     */
+    public void destroyExpiredPracticeRoom() {
+        //查询房间过期时间
+        String expiredMinuteStr = sysConfigService.findConfigValue(PRE_CREATE_PRACTICE_ROOM_MINUTE);
+        if (StringUtils.isEmpty(expiredMinuteStr)) {
+            log.info("roomDestroy>>>> 未查询到配置:{}", PRE_CREATE_PRACTICE_ROOM_MINUTE);
+            return;
+        }
+        Date now = new Date();
+        //查询已经开始并且没有删除及销毁的直播间
+        List<LiveRoom> list = this.list(Wrappers.<LiveRoom>lambdaQuery()
+                .eq(LiveRoom::getRoomState, 0)
+                .eq(LiveRoom::getLiveState, 1)
+                .eq(LiveRoom::getType, RoomTypeEnum.PRACTICE.getCode())
+                .le(LiveRoom::getLiveEndTime, now));
+        if (CollectionUtils.isEmpty(list)) {
+            return;
+        }
+        list.forEach(room -> {
+            Date expiredDate = DateUtil.addMinutes(room.getLiveEndTime(), Integer.parseInt(expiredMinuteStr));
+            //当前时间 大于(结束播时间 + 设置的过期分钟数)
+            if (now.getTime() >= expiredDate.getTime()) {
+                //删除房间
+                destroyLiveRoom(room.getRoomUid());
+                //查询老师分润表
+                CourseScheduleTeacherSalary salary = courseScheduleTeacherSalaryService.getOne(Wrappers.<CourseScheduleTeacherSalary>lambdaQuery()
+                        .eq(CourseScheduleTeacherSalary::getCourseScheduleId, room.getCourseId())
+                );
+                if (Objects.isNull(salary)) {
+                    return;
+                }
+                //查询该学生及课程id 对应的支付订单号
+                CourseScheduleStudentPayment payment = courseScheduleStudentPaymentService.getOne(Wrappers.<CourseScheduleStudentPayment>lambdaQuery()
+                        .eq(CourseScheduleStudentPayment::getCourseId, room.getCourseId())
+                        .eq(CourseScheduleStudentPayment::getUserId, salary.getStudentId())
+                );
+                if (Objects.isNull(payment)) {
+                    return;
+                }
+                //获取教师课酬写入到金额变更表
+                UserAccountRecordDto userAccountRecord = new UserAccountRecordDto();
+                userAccountRecord.setUserId(room.getSpeakerId());
+                userAccountRecord.setInOrOut(InOrOutEnum.IN);
+                userAccountRecord.setBizType(AccountBizTypeEnum.PRACTICE);
+                userAccountRecord.setBizId(room.getCourseId());
+                userAccountRecord.setBizName(room.getRoomTitle());
+                userAccountRecord.setTransAmount(salary.getActualSalary());//扣除手续费后所得金额
+                userAccountRecord.setOrderNo(payment.getOrderNo());
+                userAccountService.accountChange(userAccountRecord);
+                //修改老师课酬表
+                salary.setStatus(TeacherSalaryEnum.COMPLETE.getCode());
+                courseScheduleTeacherSalaryService.updateById(salary);
+            }
+        });
+    }
+
+    /**
      * 销毁房间-聊天室
      *
      * @param roomId 房间Uid

+ 15 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/SysUserContractRecordServiceImpl.java

@@ -3,7 +3,9 @@ package com.yonge.cooleshow.biz.dal.service.impl;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.yonge.cooleshow.biz.dal.dao.SysUserContractRecordDao;
 import com.yonge.cooleshow.biz.dal.entity.SysUserContractRecord;
+import com.yonge.cooleshow.biz.dal.service.ContractTemplateService;
 import com.yonge.cooleshow.biz.dal.service.SysUserContractRecordService;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import org.slf4j.Logger;
@@ -20,10 +22,23 @@ public class SysUserContractRecordServiceImpl extends ServiceImpl<SysUserContrac
 
     private final static Logger log = LoggerFactory.getLogger(SysUserContractRecordServiceImpl.class);
 
+    @Autowired
+    private ContractTemplateService contractTemplateService;
+
     @Override
     public SysUserContractRecordDao getDao() {
         return this.baseMapper;
     }
 
+    /**
+     * 校验当前用户签署的协议是否是最新版本
+     */
+    public void checkContractRecord(Long userId,String type) {
+        //根据人员id及协议类型查询最新协议记录
+        //查询对应类型的最新的协议
+        //对比当前类型及人员id的协议版本
+        //如果不是最新版本,则返回false
+    }
+
 }
 

+ 10 - 4
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/UserWithdrawalServiceImpl.java

@@ -39,10 +39,7 @@ import org.springframework.util.DigestUtils;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.List;
+import java.util.*;
 
 
 @Service
@@ -282,4 +279,13 @@ public class UserWithdrawalServiceImpl extends ServiceImpl<UserWithdrawalDao, Us
         return subtract;
     }
 
+    /**
+     * @Description: 提现成功回调
+     * @Author: cy
+     * @Date: 2022/5/9
+     */
+    @Override
+    public void withdrawSuccess(Map<String, Object> withdrawRecord) {
+        baseMapper.withdrawSuccess(withdrawRecord);
+    }
 }

+ 85 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/CourseHomeVo.java

@@ -0,0 +1,85 @@
+package com.yonge.cooleshow.biz.dal.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.List;
+
+@ApiModel(description = "首页课程信息")
+public class CourseHomeVo {
+
+    @ApiModelProperty(value = "未完成课程总数")
+    private Integer totalUndoneCount;
+    @ApiModelProperty(value = "已完成课程总数")
+    private Integer totalDoneCount;
+    @ApiModelProperty(value = "总课程数")
+    private Integer totalCount;
+    @ApiModelProperty(value = "数据明细")
+    private List<CourseHomeInfoVo> courseHomeInfoList;
+
+    @ApiModel(description = "首页课程明细")
+    public static class CourseHomeInfoVo {
+        @ApiModelProperty(value = "日期-按年查询返回年月 按月查询返回年月日")
+        private String date;
+        @ApiModelProperty(value = "未完成课程数")
+        private Integer undoneCount;
+        @ApiModelProperty(value = "已完成课程数")
+        private Integer doneCount;
+
+        public String getDate() {
+            return date;
+        }
+
+        public void setDate(String date) {
+            this.date = date;
+        }
+
+        public Integer getUndoneCount() {
+            return undoneCount;
+        }
+
+        public void setUndoneCount(Integer undoneCount) {
+            this.undoneCount = undoneCount;
+        }
+
+        public Integer getDoneCount() {
+            return doneCount;
+        }
+
+        public void setDoneCount(Integer doneCount) {
+            this.doneCount = doneCount;
+        }
+    }
+
+    public Integer getTotalUndoneCount() {
+        return totalUndoneCount;
+    }
+
+    public void setTotalUndoneCount(Integer totalUndoneCount) {
+        this.totalUndoneCount = totalUndoneCount;
+    }
+
+    public Integer getTotalDoneCount() {
+        return totalDoneCount;
+    }
+
+    public void setTotalDoneCount(Integer totalDoneCount) {
+        this.totalDoneCount = totalDoneCount;
+    }
+
+    public Integer getTotalCount() {
+        return totalCount;
+    }
+
+    public void setTotalCount(Integer totalCount) {
+        this.totalCount = totalCount;
+    }
+
+    public List<CourseHomeInfoVo> getCourseHomeInfoList() {
+        return courseHomeInfoList;
+    }
+
+    public void setCourseHomeInfoList(List<CourseHomeInfoVo> courseHomeInfoList) {
+        this.courseHomeInfoList = courseHomeInfoList;
+    }
+}

+ 32 - 0
cooleshow-user/user-biz/src/main/resources/config/mybatis/CourseScheduleMapper.xml

@@ -655,4 +655,36 @@
         FROM course_schedule_replied
         WHERE student_id_=#{studentId}
     </select>
+
+
+    <select id="queryCourseHomeOfYear" parameterType="map"
+            resultType="com.yonge.cooleshow.biz.dal.vo.CourseHomeVo$CourseHomeInfoVo">
+        select `date`,
+               sum(a.not_start_count) as undoneCount,
+               sum(a.complete_count)  as doneCount
+        from (select date_format(class_date_, '%Y-%m-01')                                      as `date`,
+                     ifnull(case when status_ in ('NOT_START', 'ING') then count(1) end, 0) as not_start_count,
+                     ifnull(case when status_ = 'COMPLETE' then count(1) end, 0)            as complete_count
+              from course_schedule
+              where type_ = #{param.type}
+                and status_ in ('NOT_START', 'ING', 'COMPLETE')
+        <![CDATA[ AND class_date_ >= #{param.startDate} ]]>
+        <![CDATA[ AND class_date_ <= #{param.endDate} ]]>
+        group by class_date_) as a
+        group by date
+    </select>
+
+    <select id="queryCourseHomeOfMonth" parameterType="map"
+            resultType="com.yonge.cooleshow.biz.dal.vo.CourseHomeVo$CourseHomeInfoVo">
+        select class_date_                                                            as `date`,
+               ifnull(case when status_ in ('NOT_START', 'ING') then count(1) end, 0) as undoneCount,
+               ifnull(case when status_ = 'COMPLETE' then count(1) end, 0)            as doneCount
+        from course_schedule
+        where type_ = #{param.type}
+          and status_ in ('NOT_START', 'ING', 'COMPLETE')
+        <![CDATA[ AND class_date_ >= #{param.startDate} ]]>
+        <![CDATA[ AND class_date_ <= #{param.endDate} ]]>
+        group by class_date_
+    </select>
+
 </mapper>

+ 7 - 0
cooleshow-user/user-biz/src/main/resources/config/mybatis/UserWithdrawalMapper.xml

@@ -81,4 +81,11 @@
     <select id="selectList" resultType="com.yonge.cooleshow.biz.dal.vo.UserWithdrawalVo">
         <include refid="selectSql"/>
     </select>
+
+    <insert id="withdrawSuccess" parameterType="java.util.Map">
+        INSERT INTO user_withdrawal_history
+        VALUES (#{outMemberNo},#{outerOrderNo},#{orderNo},#{name},#{mobile},#{certificateNo},
+                #{status},#{payType},#{payAccount},#{predictAmount},#{actualAmount},#{endTime})
+    </insert>
+
 </mapper>

+ 6 - 0
cooleshow-user/user-student/src/main/java/com/yonge/cooleshow/student/controller/StudentLiveRoomController.java

@@ -80,6 +80,12 @@ public class StudentLiveRoomController extends BaseController {
         liveRoomService.destroyExpiredLiveRoom();
     }
 
+    @ApiOperation("定时任务-销毁房间-直播间-陪练课")
+    @GetMapping("/destroyExpiredPracticeRoom")
+    public void destroyExpiredPracticeRoom() {
+        liveRoomService.destroyExpiredPracticeRoom();
+    }
+
     /**
      * 同步融云用户状态变更
      *

+ 1 - 1
cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/config/ResourceServerConfig.java

@@ -33,7 +33,7 @@ public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
 				.authorizeRequests()
 				.antMatchers("/task/**").hasIpAddress("0.0.0.0/0")
 				.antMatchers("/v2/api-docs", "/code/*","/payment/callback",
-                        "/liveRoom/test","/liveRoom/syncUserStatus","/courseGroup/getLockCache")
+                        "/liveRoom/test","/liveRoom/syncUserStatus","/courseGroup/getLockCache","/withdraw/callback")
                 .permitAll().anyRequest().authenticated().and().httpBasic();
 	}
 

+ 6 - 0
cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/controller/TeacherLiveRoomController.java

@@ -80,6 +80,12 @@ public class TeacherLiveRoomController extends BaseController {
         liveRoomService.destroyExpiredLiveRoom();
     }
 
+    @ApiOperation("定时任务-销毁房间-直播间-陪练课")
+    @GetMapping("/destroyExpiredPracticeRoom")
+    public void destroyExpiredPracticeRoom() {
+        liveRoomService.destroyExpiredPracticeRoom();
+    }
+
     /**
      * 同步融云用户状态变更
      *

+ 95 - 0
cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/controller/WithdrawController.java

@@ -0,0 +1,95 @@
+package com.yonge.cooleshow.teacher.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.yonge.cooleshow.biz.dal.service.UserWithdrawalService;
+import com.yonge.cooleshow.common.controller.BaseController;
+import com.yonge.toolset.thirdparty.lingxinpay.RSA;
+import com.yonge.toolset.thirdparty.lingxinpay.Withdraw;
+import io.swagger.annotations.Api;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * @Description: 提现回调
+ * @Author: cy
+ * @Date: 2022/5/9
+ */
+@RestController
+@RequestMapping("/withdraw")
+@Api(value = "提现回调", tags = "提现回调")
+public class WithdrawController extends BaseController {
+    private final static Logger log = LoggerFactory.getLogger(WithdrawController.class);
+
+    //这里需要填写商户自己生成的私钥
+    private String privateKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANf/X1s2raYQAmY5VY929XqOIRESFlpLzWbltA08EI2i0jnJY3/kcCpo1sCHqkyWpDFGRgM1WYE90ayzEpS6EdZpyJ2/N5JFJzx4wMil5KHLdtQUmVv9si+xuYNOKfJW6Xn6zI/Wh81J1+hMlUY4WigU5Qci7DjdOjg5OD6e5DO3AgMBAAECgYEAor9ENhII3SsK48MneKWFaQZWW+po9ThQV8uT0rUDM/UOuYPIeMDC0vGTfhW6d2K57Haqohg8jGjr51g2E+HvNV+fARaBfCwy00DGcxjI6N8gEInj2AppsGV6a0ZtzGBh3BxGhEFV7x3NmTylDk3WkGnkGDqDNyrBUgK0BzCJEmECQQD+gYckYRevVfrZEHDQHRNzzMUlV9/ljA9x8dt0LoCNbd/wFvs0Ekjhas/2lUBkewEd4Kr0jaFcYzlUO/qihcpNAkEA2UP5W44yLuIo8ttPpdsfkH/8Ax64IywQHrXWq+thH7I91VwY2vomTduw8x0PafZtp8xryF3LixTZwQ7gsYbwEwJAQbb8SB5x2SogPVALcREw5qOm+/92pnTFwCws+BDRzLLkMcAdWNKn0tybmhXrrIY+QZKzUbYIRiywrtlV3AUjuQJBAJbnFnfX4NUdchGT79Mjyd2kdxZ3rK+JOD0MUWkhWFkahMX/bKgTXK1xLIr/ISiY53rHigkl1Gzqc4Aa5EeJkI8CQFlzlBOfoVnq3fPMZCCZcZSm97L12MgHho0AzoNj9sw9YYn9WPM7bw2HT8GUheiU3aiZGCyGGpYuVHMiBGa7l9U=";
+
+    @Autowired
+    private UserWithdrawalService userWithdrawalService;
+
+    /**
+     * 异步回调接收
+     *
+     * @param content
+     * @param request
+     * @return
+     */
+    @PostMapping("/callback")
+    public String test(@RequestBody String content, HttpServletRequest request) {
+        log.info("交易回调请求地址:{} 请求参数:{}", request.getRemoteAddr(), content);
+        try {
+            if (StringUtils.isBlank(content)) {
+                throw new Exception();
+            }
+            Map<String, Object> map = JSONObject.parseObject(content);
+            String jsonStr = RSA.decryptPri((String) map.get("sign"), privateKey);
+            log.info("jsonStr:{}", jsonStr);
+
+//            Map<String, Object> withdrawRecord = JSONObject.parseObject(jsonStr);
+//            userWithdrawalService.withdrawSuccess(withdrawRecord);
+            return jsonStr;
+        } catch (Exception e) {
+            log.error("解密失败e:{}", e);
+            return "failed";
+        }
+    }
+    @GetMapping("/test")
+    public String a(){
+        Withdraw withdraw = new Withdraw();
+
+        //输入商户订单号
+        String outerOrderNo = UUID.randomUUID().toString().substring(0, 12);
+        System.out.println("商户订单号:"+outerOrderNo);
+        //输入收款人手机号
+        String name = "何亮";
+        //输入收款人姓名
+        String mobile = "17600220933";
+        //输入收款人身份证号
+        String certificateNo = "130423199206192818";
+        //输入转账金额(单位分)
+        String predictAmount = "1";
+        //输入收款人账号
+        String payAccount = "6228480018864836772";
+        //输入卡类型:DC借记卡,CC信用卡(暂不支持)
+        String cardType = "DC";
+        //输入发放类型(0:工资,1:奖金,2:绩效,3:劳务,4:个人经营所得,5:其他)
+        String salaryType = "4";
+        //输入项目名称
+        String projectName = "测试";
+        //输入支付类型(1:银行卡)
+        String payType = "1";
+        //输入卡属性:(C:对私 ,B:对公)暂时不支持对公
+        String cardAttribute = "C";
+
+        String requestParam = withdraw.withdraw(outerOrderNo, name, mobile, certificateNo, predictAmount, payAccount, cardType,
+                salaryType, projectName, payType, cardAttribute);
+        log.info("单笔请求返回参数:{}", requestParam);
+        return requestParam;
+    }
+}

+ 113 - 0
toolset/thirdparty-component/src/main/java/com/yonge/toolset/thirdparty/lingxinpay/Md5EncryptUtils.java

@@ -0,0 +1,113 @@
+package com.yonge.toolset.thirdparty.lingxinpay;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+
+/**
+ * MD5加密算法
+ * @author xumum
+ *
+ */
+public class Md5EncryptUtils {
+	/**
+	 * Used building output as Hex
+	 */
+	private static final char[] DIGITS = { '0', '1', '2', '3', '4', '5', '6',
+			'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+	/**
+	 * 对字符串进行MD5加密
+	 * @param text 明文
+	 * @return 密文
+	 */
+	public static String md5(String text) {
+		MessageDigest msgDigest = null;
+		try {
+			msgDigest = MessageDigest.getInstance("MD5");
+		} catch (NoSuchAlgorithmException e) {
+			throw new IllegalStateException(
+					"System doesn't support MD5 algorithm.");
+		}
+		try {
+			msgDigest.update(text.getBytes("UTF-8"));
+		} catch (UnsupportedEncodingException e) {
+			throw new IllegalStateException(
+					"System doesn't support your  EncodingException.");
+		}
+		byte[] bytes = msgDigest.digest();
+		String md5Str = new String(encodeHex(bytes));
+		return md5Str;
+	}
+
+	public static char[] encodeHex(byte[] data) {
+		int l = data.length;
+		char[] out = new char[l << 1];
+		for (int i = 0, j = 0; i < l; i++) {
+			out[j++] = DIGITS[(0xF0 & data[i]) >>> 4];
+			out[j++] = DIGITS[0x0F & data[i]];
+		}
+		return out;
+	}
+	
+	
+	/** 
+     * 方法描述:签名字符串 
+     * @author xumum
+     * @param params 需要签名的参数 
+     * @param key 签名密钥 
+     * @return 
+     */  
+    public static String sign(Map<String, Object> params, String key) {  
+        StringBuilder valueSb = new StringBuilder();  
+       
+        // 将参数以参数名的字典升序排序  
+        Map<String, Object> sortParams = new TreeMap<String, Object>(params);  
+        Set<Entry<String, Object>> entrys = sortParams.entrySet();  
+        // 参数以参数名的字典升序排序 ,并拼接param1=value1&param2=value2...&key=value格式  
+        for (Entry<String, Object> entry : entrys) {  
+        	valueSb.append(entry.getKey());
+        	valueSb.append("=");
+            valueSb.append(entry.getValue());  
+            valueSb.append("&");  
+        }  
+        String paramStr = valueSb.toString();
+        paramStr = paramStr.substring(0, paramStr.length()-1);
+        return MD5sign(paramStr,key);  
+    }  
+    
+    /** 
+     * MD5生成签名字符串 
+     * @param parmas 需签名参数
+     * @param key MD5key 
+     * @return 
+     */  
+    public static String MD5sign(String parmas, String key) {  
+        String sign = "";
+        try {  
+        	parmas += "&key=" + key;  
+        	sign = md5(parmas);  
+        } catch (Exception e) {  
+            e.printStackTrace();  
+        }  
+        return sign;  
+    }  
+    
+    public static void main(String[] args) {  
+        Map<String ,Object> map = new HashMap<String,Object>();  
+        map.put("companyNo", "1504a229bb034f19b6bf21738976981d");  
+        map.put("salaryOrderCode", "2018040353359360");  
+        map.put("predictNumber", 1);  
+        map.put("predictTotalSum", "1.00");  
+        /***MD5签名与验签**/  
+        String key="7d50522f57df480c80c16703493bf259";  
+        String sign= sign(map,key);  
+        System.out.println("生成的MD5签名:"+sign);  
+    }  
+
+}

+ 147 - 0
toolset/thirdparty-component/src/main/java/com/yonge/toolset/thirdparty/lingxinpay/Withdraw.java

@@ -0,0 +1,147 @@
+package com.yonge.toolset.thirdparty.lingxinpay;
+
+import com.alibaba.fastjson.JSONObject;
+import com.yonge.toolset.utils.http.HttpUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * @author: cy
+ * @date: 2022/5/9 11:24
+ */
+@Service
+public class Withdraw {
+    private static final Logger logger = LoggerFactory.getLogger(Withdraw.class);
+
+    private String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYT5eCY6r8sGWgbiId/VqSZmS6XkBNGMkzUqTIkpkecOzsFBxFXTQmgDeR991YfgqmyOaHsJ/ons/H+e8l+RmHsOm4eErFU+9qXFq+k195YFV1vAR9O7MIG+FR5vmLDuhgimPsgqscWhUrGinc8RUpi5KwClgx7d+d8ZJ4GmkR0QIDAQAB";
+    private String md5Key = "0fd42370bad6485e46718b97f3dd1536";
+    private String notifyUrl = "http://47.114.1.200:8000/teacher-server/withdraw/callback";//回调地址
+    private String memberNo = "1491663782974988288";//商户号
+    private String apiUrl = "http://39.107.15.64:8090";//第三方url
+
+    /**
+     * 单笔提现
+     *
+     * @param outerOrderNo  商户唯一订单号
+     * @param name          收款方姓名(银行预留姓名等)
+     * @param mobile        收款方电话
+     * @param certificateNo 收款方身份证号
+     * @param predictAmount 应发金额(单位为:分,范围: 1~10000000000)
+     * @param payAccount    收款方账号(银行卡号/支付宝账号 /open_id)以实际业务为准
+     * @param cardType      卡类型:DC 借记卡
+     * @param salaryType    发放类型(0:个人经营所得)
+     * @param projectName   项目名称
+     * @param payType       支付类型(1:银行卡,2:支付宝,4:微信) 以实际业务为准
+     * @param cardAttribute 卡属性:(C:对私)
+     * @return
+     */
+    public String withdraw(String outerOrderNo, String name, String mobile, String certificateNo, String predictAmount,
+                           String payAccount, String cardType, String salaryType, String projectName, String payType, String cardAttribute) {
+        Map<String, Object> map = new HashMap<>();
+        map.put("outMemberNo", memberNo);
+        map.put("outerOrderNo", outerOrderNo);
+        map.put("name", name);
+        map.put("certificateNo", certificateNo);
+        map.put("predictAmount", predictAmount);
+        String signs = Md5EncryptUtils.sign(map, md5Key);
+
+        map.put("charset", "UTF-8");
+        map.put("mobile", mobile);
+        map.put("version", "1.1");
+        map.put("service", "bpotop.zx.pay.order");
+        map.put("Md5Key", signs);
+        map.put("notifyUrl", notifyUrl);
+        map.put("cardType", cardType);
+        map.put("salaryType", salaryType);
+        map.put("projectName", projectName);
+        map.put("payType", payType);
+        map.put("cardAttribute", cardAttribute);
+        map.put("payAccount", payAccount);
+        String jsonStr = JSONObject.toJSONString(map);
+
+        //签名
+        JSONObject mapParam = new JSONObject();
+        try {
+            //使用公钥加密
+            String encryptStr = RSA.encryptPub(jsonStr, publicKey);
+            mapParam.put("outMemberNo", memberNo);
+            mapParam.put("signType", "RSA");
+            mapParam.put("sign", encryptStr);
+            logger.info("单笔请求请求参数:{}", JSONObject.toJSONString(mapParam));
+        } catch (Exception e) {
+            logger.info("加密失败:{}", e);
+        }
+
+        //发送
+        try {
+            String resultJsonStr = HttpUtil.postForHttp(apiUrl + "/bpotop_trade/single", JSONObject.toJSONString(mapParam), null);
+            logger.info("单笔请求返回参数:{}", resultJsonStr);
+            return resultJsonStr;
+        } catch (IOException e) {
+            logger.info("发送失败:{}", e);
+        }
+        return null;
+    }
+
+    /**
+     * 查询接口
+     *
+     * @param outerOrderNo 商户唯一订单号
+     * @throws Exception
+     */
+    public void query(String outerOrderNo) throws Exception {
+        Map<String, Object> requestMap = new HashMap<>();
+        requestMap.put("outMemberNo", memberNo);
+        requestMap.put("outerOrderNo", outerOrderNo);
+        requestMap.put("service", "bpotop.zx.pay.order");
+        requestMap.put("version", "1.0");
+        requestMap.put("signType", "RSA");
+        requestMap.put("charset", "UTF-8");
+        String jsonStr = JSONObject.toJSONString(requestMap);
+        String encryptStr = RSA.encryptPub(jsonStr, publicKey);
+
+        Map<String, Object> requestMap2 = new HashMap<>();
+        requestMap2.put("outMemberNo", memberNo);
+        requestMap2.put("sign", encryptStr);
+        logger.info("单笔查询请求参数:{}", JSONObject.toJSONString(requestMap2));
+        String resultJsonStr = HttpUtil.postForHttp(apiUrl + "/bpotop_trade/order_query", JSONObject.toJSONString(requestMap2), null);
+        logger.info("单笔查询响应参数:{}", resultJsonStr);
+    }
+
+    public static void main(String[] args) throws Exception {
+        Withdraw withdraw = new Withdraw();
+
+        //输入商户订单号
+        String outerOrderNo = UUID.randomUUID().toString().substring(0, 12);
+        //输入收款人手机号
+        String name = "何亮";
+        //输入收款人姓名
+        String mobile = "17600220933";
+        //输入收款人身份证号
+        String certificateNo = "130423199206192818";
+        //输入转账金额(单位分)
+        String predictAmount = "1";
+        //输入收款人账号
+        String payAccount = "6228480018864836772";
+        //输入卡类型:DC借记卡,CC信用卡(暂不支持)
+        String cardType = "DC";
+        //输入发放类型(0:工资,1:奖金,2:绩效,3:劳务,4:个人经营所得,5:其他)
+        String salaryType = "4";
+        //输入项目名称
+        String projectName = "测试";
+        //输入支付类型(1:银行卡)
+        String payType = "1";
+        //输入卡属性:(C:对私 ,B:对公)暂时不支持对公
+        String cardAttribute = "C";
+
+        String requestParam = withdraw.withdraw(outerOrderNo, name, mobile, certificateNo, predictAmount, payAccount, cardType,
+                salaryType, projectName, payType, cardAttribute);
+        logger.info("单笔请求返回参数:{}", requestParam);
+    }
+}