guomengjiao hai 4 meses
pai
achega
b02a591fca

+ 28 - 4
renren-admin/src/main/java/io/renren/modules/qyh/controller/WithdrawController.java

@@ -3,18 +3,18 @@ package io.renren.modules.qyh.controller;
 import io.renren.common.utils.Constant;
 import io.renren.common.utils.PageUtils;
 import io.renren.common.utils.R;
+import io.renren.modules.qyh.entity.WithdrawInfoEntity;
 import io.renren.modules.qyh.service.WithdrawInfoService;
 import io.renren.modules.sys.controller.AbstractController;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiImplicitParam;
 import io.swagger.annotations.ApiImplicitParams;
 import io.swagger.annotations.ApiOperation;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
+import java.math.BigDecimal;
 import java.util.Map;
 
 /**
@@ -34,9 +34,33 @@ public class WithdrawController extends AbstractController {
             @ApiImplicitParam(name = Constant.PAGE, value = "当前页码,从1开始", paramType = "query", required = true, dataType = "int"),
             @ApiImplicitParam(name = Constant.LIMIT, value = "每页显示记录数", paramType = "query", required = true, dataType = "int"),
     })
+    @RequiresPermissions("qyh:withdrawinfo:list")
     public R page(@RequestParam Map<String, Object> params) {
         PageUtils page = withdrawInfoService.queryPage(params);
         return R.ok().put("page", page);
     }
 
+    @GetMapping("/statistics")
+    @ApiOperation("统计提现金额")
+    @RequiresPermissions("qyh:withdrawinfo:list")
+    public R statistics(@RequestParam Map<String, Object> params) {
+        Map<String, BigDecimal> statistics = withdrawInfoService.getWithdrawStatistics(params);
+        return R.ok().put("data", statistics);
+    }
+
+    @GetMapping("/info/{id}")
+    @ApiOperation("详情")
+    @RequiresPermissions("qyh:withdrawinfo:info")
+    public R info(@PathVariable("id") Long id) {
+        return R.ok().put("data", withdrawInfoService.info(id));
+    }
+
+    @PostMapping("/audit")
+    @ApiOperation("审核提现")
+    @RequiresPermissions("qyh:withdrawinfo:audit")
+    public R audit(@RequestBody WithdrawInfoEntity dto) {
+        dto.setAuditPer(getUserId());
+        withdrawInfoService.audit(dto);
+        return R.ok();
+    }
 }

+ 8 - 0
renren-admin/src/main/java/io/renren/modules/qyh/entity/WithdrawConfigEntity.java

@@ -39,6 +39,14 @@ public class WithdrawConfigEntity implements Serializable {
      * 单笔最高提现金额
      */
     private BigDecimal maxWithdrawAmount;
+    /**
+     * 单日限额
+     */
+    private BigDecimal dailyMaxAmount;
+    /**
+     * 每日最多提现次数
+     */
+    private Integer dailyMaxCount;
 
     /**
      * 创建人

+ 0 - 5
renren-admin/src/main/java/io/renren/modules/qyh/entity/WithdrawInfoEntity.java

@@ -75,11 +75,6 @@ public class WithdrawInfoEntity implements Serializable {
      */
     private LocalDateTime auditTime;
 
-    /**
-     * 打款状态 0-未打款 1-已打款
-     */
-    private Integer remitStatus;
-
     /**
      * 打款时间
      */

+ 3 - 0
renren-admin/src/main/java/io/renren/modules/qyh/mapper/WithdrawInfoMapper.java

@@ -7,6 +7,7 @@ import io.renren.modules.qyh.model.vo.WithdrawInfoVO;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
+import java.math.BigDecimal;
 import java.util.Map;
 
 /**
@@ -19,4 +20,6 @@ import java.util.Map;
 public interface WithdrawInfoMapper extends BaseMapper<WithdrawInfoEntity> {
 
     IPage<WithdrawInfoVO> queryPage(IPage<WithdrawInfoEntity> iPage, @Param("params") Map<String, Object> params);
+
+    Map<String, BigDecimal> getWithdrawStatistics(@Param("params") Map<String, Object> params);
 }

+ 22 - 0
renren-admin/src/main/java/io/renren/modules/qyh/model/vo/WithdrawInfoVO.java

@@ -10,6 +10,8 @@ import java.time.LocalDateTime;
 public class WithdrawInfoVO implements Serializable {
     private static final long serialVersionUID = 1L;
 
+    private Long id;
+
     /**
      * 用户名称
      */
@@ -42,4 +44,24 @@ public class WithdrawInfoVO implements Serializable {
      * 审核状态 1-待审核 2-已通过 3-已拒绝 4-已打款
      */
     private Integer auditState;
+
+    /**
+     * 审核说明/备注
+     */
+    private String auditExplain;
+
+    /**
+     * 审核时间
+     */
+    private LocalDateTime auditTime;
+
+    /**
+     * 打款时间
+     */
+    private LocalDateTime remitTime;
+
+    /**
+     * 可提现余额
+     */
+    private BigDecimal availableAmount;
 }

+ 7 - 0
renren-admin/src/main/java/io/renren/modules/qyh/service/MemberWalletService.java

@@ -33,4 +33,11 @@ public interface MemberWalletService extends IService<MemberWalletEntity> {
     void freezeBalance(Long memberId, BigDecimal money);
 
     MyWalletVO getMyWallet(Long memberId);
+
+    /**
+     * 解冻钱包余额并退款
+     * @param memberId
+     * @param money
+     */
+    void unfreezeAndRefund(Long memberId, BigDecimal money);
 }

+ 6 - 0
renren-admin/src/main/java/io/renren/modules/qyh/service/WithdrawInfoService.java

@@ -3,7 +3,9 @@ package io.renren.modules.qyh.service;
 import com.baomidou.mybatisplus.extension.service.IService;
 import io.renren.common.utils.PageUtils;
 import io.renren.modules.qyh.entity.WithdrawInfoEntity;
+import io.renren.modules.qyh.model.vo.WithdrawInfoVO;
 
+import java.math.BigDecimal;
 import java.util.Map;
 
 /**
@@ -28,4 +30,8 @@ public interface WithdrawInfoService extends IService<WithdrawInfoEntity> {
      * 后台审核提现
      */
     void audit(WithdrawInfoEntity withdrawInfoEntity);
+
+    Map<String, BigDecimal> getWithdrawStatistics(Map<String, Object> params);
+
+    WithdrawInfoVO info(Long id);
 }

+ 10 - 0
renren-admin/src/main/java/io/renren/modules/qyh/service/impl/MemberWalletServiceImpl.java

@@ -115,4 +115,14 @@ public class MemberWalletServiceImpl extends ServiceImpl<MemberWalletMapper, Mem
         vo.setPendingAmount(wallet.getPendingAmount());
         return vo;
     }
+
+    @Override
+    public void unfreezeAndRefund(Long memberId, BigDecimal money) {
+        MemberWalletEntity wallet = this.getOrCreateWallet(memberId);
+        // 减少待结算金额,增加可提现余额
+        wallet.setBalance(wallet.getBalance().add(money));
+        wallet.setPendingAmount(wallet.getPendingAmount().subtract(money));
+        wallet.setUpdateTime(LocalDateTime.now());
+        baseMapper.updateById(wallet);
+    }
 }

+ 61 - 13
renren-admin/src/main/java/io/renren/modules/qyh/service/impl/WithdrawInfoServiceImpl.java

@@ -2,6 +2,7 @@ package io.renren.modules.qyh.service.impl;
 
 import cn.hutool.core.lang.Snowflake;
 import cn.hutool.core.map.MapUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -9,11 +10,16 @@ import io.renren.common.enums.WithdrawAuditStateEnum;
 import io.renren.common.enums.WithdrawTypeEnum;
 import io.renren.common.exception.RRException;
 import io.renren.common.utils.Constant;
+import io.renren.common.utils.LocalDateTimeUtils;
 import io.renren.common.utils.PageUtils;
+import io.renren.modules.qmgj.entity.MemberInfoEntity;
+import io.renren.modules.qmgj.service.MemberInfoService;
+import io.renren.modules.qmjz.utils.BeanCopyUtils;
 import io.renren.modules.qyh.entity.MemberWalletEntity;
 import io.renren.modules.qyh.entity.WithdrawConfigEntity;
 import io.renren.modules.qyh.entity.WithdrawInfoEntity;
 import io.renren.modules.qyh.mapper.WithdrawInfoMapper;
+import io.renren.modules.qyh.model.vo.MyWalletVO;
 import io.renren.modules.qyh.model.vo.WithdrawInfoVO;
 import io.renren.modules.qyh.service.MemberWalletService;
 import io.renren.modules.qyh.service.WithdrawConfigService;
@@ -24,6 +30,7 @@ import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
 import java.time.LocalDateTime;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -42,6 +49,9 @@ public class WithdrawInfoServiceImpl extends ServiceImpl<WithdrawInfoMapper, Wit
     @Autowired
     private WithdrawConfigService withdrawConfigService;
 
+    @Autowired
+    private MemberInfoService memberInfoService;
+
     @Override
     public PageUtils queryPage(Map<String, Object> params) {
         Integer page = MapUtil.getInt(params, Constant.PAGE);
@@ -65,6 +75,23 @@ public class WithdrawInfoServiceImpl extends ServiceImpl<WithdrawInfoMapper, Wit
         if (money.compareTo(config.getMaxWithdrawAmount()) > 0) {
             throw new RRException("提现金额不能高于" + config.getMaxWithdrawAmount() + "元");
         }
+        //单日限额
+        LocalDateTime now = LocalDateTime.now();
+        List<WithdrawInfoEntity> todayWithdraws = baseMapper.selectList(
+                new LambdaQueryWrapper<WithdrawInfoEntity>()
+                        .eq(WithdrawInfoEntity::getMemberId, memberId)
+                        .ge(WithdrawInfoEntity::getApplyTime, LocalDateTimeUtils.getDayStart(now))
+                        .le(WithdrawInfoEntity::getApplyTime, LocalDateTimeUtils.getDayEnd(now)));
+        BigDecimal todayTotalAmount = todayWithdraws.stream()
+                .map(WithdrawInfoEntity::getMoney)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        if (todayTotalAmount.add(money).compareTo(config.getDailyMaxAmount()) > 0) {
+            throw new RRException("今日提现金额已达上限(单日限额" + config.getDailyMaxAmount() + "元)");
+        }
+        // 校验每日最多提现次数
+        if (todayWithdraws.size() >= config.getDailyMaxCount()) {
+            throw new RRException("今日提现次数已达上限(每日最多" + config.getDailyMaxCount() + "次)");
+        }
         // 3. 获取钱包并校验余额
         MemberWalletEntity wallet = memberWalletService.getOrCreateWallet(memberId);
         if (wallet.getBalance().compareTo(money) < 0) {
@@ -92,25 +119,46 @@ public class WithdrawInfoServiceImpl extends ServiceImpl<WithdrawInfoMapper, Wit
         if (entity == null) {
             throw new RRException("提现记录不存在");
         }
-        if (!entity.getAuditState().equals(1)) {
+        if (!entity.getAuditState().equals(WithdrawAuditStateEnum.WAIT_AUDIT.value())) {
             throw new RRException("当前状态不可审核");
         }
-
-        entity.setAuditState(dto.getAuditState());
+        Integer auditState = dto.getAuditState();
+        entity.setAuditState(auditState);
         entity.setAuditExplain(dto.getAuditExplain());
         entity.setAuditPer(dto.getAuditPer());
         entity.setAuditTime(LocalDateTime.now());
-
-        // 如果是拒绝,需要退回余额 (这里简单处理,具体看业务逻辑是否需要退回)
-        // 正常逻辑是:审核通过 -> 调用微信打款 -> 打款成功 -> 更新状态
-        // 或者:审核通过 -> 状态变为已打款
-        if (dto.getAuditState().equals(2)) {
-            // 假设审核通过即视为已打款(如果是手动打款流程)
-            // 实际对接微信企业付款需要异步回调
-            entity.setRemitStatus(1);
-            entity.setRemitTime(LocalDateTime.now());
+        // 审核通过
+        if (auditState.equals(WithdrawAuditStateEnum.AUDIT_PASS.value())) {
+            // TODO: 这里调用微信接口进行打款
+        } else if (auditState.equals(WithdrawAuditStateEnum.AUDIT_REFUSE.value())) {
+            // 拒绝后需要解冻并退回钱包余额
+            memberWalletService.unfreezeAndRefund(entity.getMemberId(), entity.getMoney());
+        } else {
+            throw new RRException("审核状态不正确");
         }
-
         baseMapper.updateById(entity);
     }
+
+    @Override
+    public Map<String, BigDecimal> getWithdrawStatistics(Map<String, Object> params) {
+        return baseMapper.getWithdrawStatistics(params);
+    }
+
+    @Override
+    public WithdrawInfoVO info(Long id) {
+        WithdrawInfoEntity withdrawInfoEntity = baseMapper.selectById(id);
+        if (withdrawInfoEntity == null) {
+            return null;
+        }
+        WithdrawInfoVO withdrawInfoVO = new WithdrawInfoVO();
+        BeanCopyUtils.copyPropertiesIgnoreNull(withdrawInfoEntity, withdrawInfoVO);
+        // 获取会员信息
+        MemberInfoEntity memberInfo = memberInfoService.getById(withdrawInfoEntity.getMemberId());
+        withdrawInfoVO.setUserName(memberInfo.getVipname());
+        withdrawInfoVO.setPhone(memberInfo.getPhone());
+        //获取余额
+        MyWalletVO myWallet = memberWalletService.getMyWallet(withdrawInfoEntity.getMemberId());
+        withdrawInfoVO.setAvailableAmount(myWallet.getAvailableAmount());
+        return withdrawInfoVO;
+    }
 }

+ 15 - 3
renren-admin/src/main/resources/mapper/qyh/WithdrawInfoDao.xml

@@ -10,6 +10,20 @@
         mi.PHONE AS phone
         FROM qyh_withdraw_info cr
         LEFT JOIN member_info mi ON cr.member_id = mi.id
+        <include refid="where"/>
+        ORDER BY cr.apply_time DESC
+    </select>
+
+    <select id="getWithdrawStatistics" resultType="java.util.Map">
+        SELECT
+        COALESCE(SUM(CASE WHEN cr.audit_state = 4 THEN cr.money ELSE 0 END), 0) AS successAmount,
+        COALESCE(SUM(CASE WHEN cr.audit_state = 1 THEN cr.money ELSE 0 END), 0) AS pendingAmount
+        FROM qyh_withdraw_info cr
+        LEFT JOIN member_info mi ON cr.member_id = mi.id
+        <include refid="where"/>
+    </select>
+
+    <sql id="where">
         <where>
             <if test="params.memberId != null and params.memberId != ''">
                 AND cr.member_id = #{params.memberId}
@@ -33,7 +47,5 @@
                 AND cr.audit_state = #{params.auditState}
             </if>
         </where>
-        ORDER BY cr.apply_time DESC
-    </select>
-
+    </sql>
 </mapper>

+ 8 - 0
renren-admin/src/main/resources/statics/js/modules/qyh/withdrawalConfig.js

@@ -24,6 +24,14 @@ let vm = new Vue({
                 layer.msg('[单笔最高提现金额]不能为空',{icon:5});
                 return;
             }
+            if (vm.withdrawConfig.dailyMaxAmount == ''){
+                layer.msg('[单日限额]不能为空',{icon:5});
+                return;
+            }
+            if (vm.withdrawConfig.dailyMaxCount == ''){
+                layer.msg('[每日最多提现次数]不能为空',{icon:5});
+                return;
+            }
             $.ajax({
                 type: "POST",
                 url: baseURL + url,

+ 12 - 0
renren-admin/src/main/resources/templates/modules/qyh/withdrawalConfig.html

@@ -27,6 +27,18 @@
                     <input type="text" class="form-control" v-model="withdrawConfig.maxWithdrawAmount" placeholder="请输入单笔最高提现金额" oninput="num(this)"/>
                 </div>
             </div>
+            <div class="form-group">
+                <div class="col-sm-2 control-label">单日限额 (¥)</div>
+                <div class="col-sm-4">
+                    <input type="text" class="form-control" v-model="withdrawConfig.dailyMaxAmount" placeholder="请输入单日限额" oninput="num(this)"/>
+                </div>
+            </div>
+            <div class="form-group">
+                <div class="col-sm-2 control-label">每日最多提现次数</div>
+                <div class="col-sm-4">
+                    <input type="text" class="form-control" v-model="withdrawConfig.dailyMaxCount" placeholder="请输入每日最多提现次数" oninput="intnum(this)"/>
+                </div>
+            </div>
             <div class="form-group">
                 <div class="col-sm-2 control-label"></div>
                 <input type="button" class="btn btn-primary" id="btnSaveOrUpdate" data-loading-text="保存中..."

+ 0 - 40
renren-common/src/main/java/io/renren/common/enums/WithdrawRemitStatusEnum.java

@@ -1,40 +0,0 @@
-package io.renren.common.enums;
-
-import cn.hutool.core.util.ObjectUtil;
-import com.fasterxml.jackson.annotation.JsonCreator;
-
-/**
- * 打款状态 0-未打款 1-已打款
- *
- * @author lijiahe
- */
-public enum WithdrawRemitStatusEnum {
-    UNREMITTED(0, "未打款"),
-    REMITTED(1, "已打款")
-    ;
-    private Integer value;
-    private String desc;
-
-    WithdrawRemitStatusEnum(Integer value, String desc) {
-        this.value = value;
-        this.desc = desc;
-    }
-
-    public Integer value() {
-        return this.value;
-    }
-
-    public String desc() {
-        return this.desc;
-    }
-
-    @JsonCreator
-    public static WithdrawRemitStatusEnum getByCode(Integer value) {
-        for (WithdrawRemitStatusEnum ynEnum : WithdrawRemitStatusEnum.values()) {
-            if (ObjectUtil.equal(value, ynEnum.value())) {
-                return ynEnum;
-            }
-        }
-        return null;
-    }
-}