lsw 9 bulan lalu
induk
melakukan
5cc8a3c0d1

+ 20 - 7
admin-ui/src/views/work/pay/cash_out.vue

@@ -2,7 +2,10 @@
   <div class="app-container">
     <el-form :model="queryParams" ref="queryForm" :inline="true" @submit.native.prevent v-show="showSearch">
       <el-form-item label="提现编号" prop="nums">
-        <el-input v-model="queryParams.nums" placeholder="请输入充值编号" @keyup.enter.native="handleQuery" clearable />
+        <el-input v-model="queryParams.nums" placeholder="充值编号" @keyup.enter.native="handleQuery" clearable />
+      </el-form-item>
+      <el-form-item label="提现人" prop="name">
+        <el-input v-model="queryParams.name" placeholder="提现人姓名" @keyup.enter.native="handleQuery" class="inp" clearable />
       </el-form-item>
       <el-form-item label="提现状态" prop="state">
         <el-select v-model="queryParams.state" placeholder="提现状态" clearable style="width: 117px">
@@ -22,16 +25,21 @@
     <el-table :data="response.rows" border height="calc(100vh - 235px)">
       <el-table-column type="index" label="序号" align="center" width="80" />
       <el-table-column label="提现编号" align="center" prop="nums" />
+      <el-table-column label="提现人" align="center" prop="money" width="110">
+        <template slot-scope="scope">
+          <span class="pon" @click="op('info', scope.row)">{{ scope.row.name }}</span>
+        </template>
+      </el-table-column>
       <el-table-column label="提现方式" align="center" width="130">
         <template slot-scope="scope">
           <span class="icon" v-if="scope.row.way == 0">&#xe66b; 提现到银行卡</span>
           <span class="icon" v-else>&#xe620; 提现到微信</span>
         </template>
       </el-table-column>
-      <el-table-column label="提现金额" align="center" prop="money" width="130" />
-      <el-table-column label="手续费" align="center" prop="serviceMoney" width="130" />
-      <el-table-column label="提现费率(%)" align="center" prop="rate" width="130" />
-      <el-table-column label="实际到账" align="center" prop="money" width="130">
+      <el-table-column label="提现金额" align="center" prop="money" width="110" />
+      <el-table-column label="手续费" align="center" prop="serviceMoney" width="110" />
+      <el-table-column label="提现费率(%)" align="center" prop="rate" width="110" />
+      <el-table-column label="实际到账" align="center" prop="money" width="110">
         <template slot-scope="scope">
           {{ (scope.row.money - scope.row.serviceMoney).toFixed(2) }}
         </template>
@@ -48,8 +56,8 @@
           </el-popover>
         </template>
       </el-table-column>
-      <el-table-column label="提现时间" align="center" prop="createTime" width="200" />
-      <el-table-column label="操作" align="center" width="200">
+      <el-table-column label="提现时间" align="center" prop="createTime" width="160" />
+      <el-table-column label="操作" align="center" width="160">
         <template slot-scope="scope">
           <el-button size="mini" type="text" icon="el-icon-view" @click="op('detail', scope.row)" v-hasPermi="['work:pay:list']">详情</el-button>
           <el-button size="mini" type="text" icon="el-icon-edit" @click="op('edit', scope.row)" v-hasPermi="['work:pay:audit']" v-if="scope.row.state == 0">确认</el-button>
@@ -65,6 +73,7 @@
 
 <script>
 import edit from './edit';
+import info from '@/views/work/user/edit';
 export default {
   name: 'Pay',
   data() {
@@ -76,6 +85,7 @@ export default {
         pageNum: 1,
         pageSize: 10,
         nums: null,
+        name: null,
         state: null,
         type: 2,
         orderByColumn: 'id',
@@ -113,6 +123,9 @@ export default {
       if (tag == 'detail') {
         this.iframe({ obj: edit, param: { id: row.id, detail: true }, title: '提现详情', width: '35%', height: '80%' });
       }
+      if (tag == 'info') {
+        this.iframe({ obj: info, param: { id: row.userId, detail: true }, title: '用户详情', width: '55%', height: '65%' });
+      }
       if (tag == 'edit') {
         this.iframe({ obj: edit, param: { id: row.id }, title: '提现确认(以实际到账为准)', width: '35%', height: '80%' });
       }

+ 19 - 6
admin-ui/src/views/work/pay/index.vue

@@ -2,7 +2,10 @@
   <div class="app-container">
     <el-form :model="queryParams" ref="queryForm" :inline="true" @submit.native.prevent v-show="showSearch">
       <el-form-item label="充值编号" prop="nums">
-        <el-input v-model="queryParams.nums" placeholder="请输入充值编号" @keyup.enter.native="handleQuery" clearable />
+        <el-input v-model="queryParams.nums" placeholder="充值编号" @keyup.enter.native="handleQuery" clearable />
+      </el-form-item>
+      <el-form-item label="充值人" prop="name">
+        <el-input v-model="queryParams.name" placeholder="充值人姓名" @keyup.enter.native="handleQuery" class="inp" clearable />
       </el-form-item>
       <el-form-item label="充值状态" prop="state">
         <el-select v-model="queryParams.state" placeholder="充值状态" clearable style="width: 117px">
@@ -21,20 +24,25 @@
     <el-table :data="response.rows" border height="calc(100vh - 235px)">
       <el-table-column type="index" label="序号" align="center" width="80" />
       <el-table-column label="充值编号" align="center" prop="nums" />
-      <el-table-column label="充值金额" align="center" prop="money" width="160" />
-      <el-table-column label="充值方式" align="center" width="160">
+      <el-table-column label="提现人" align="center" prop="money" width="110">
+        <template slot-scope="scope">
+          <span class="pon" @click="op('info', scope.row)">{{ scope.row.name }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="充值金额" align="center" prop="money" width="130" />
+      <el-table-column label="充值方式" align="center" width="130">
         <template slot-scope="scope">
           <span class="icon">&#xe620; 微信充值</span>
         </template>
       </el-table-column>
-      <el-table-column label="充值状态" align="center" prop="state" width="160">
+      <el-table-column label="充值状态" align="center" prop="state" width="130">
         <template slot-scope="scope">
           <el-tag type="danger" v-if="scope.row.state == 0">未支付</el-tag>
           <el-tag type="success" v-if="scope.row.state == 1">充值成功</el-tag>
         </template>
       </el-table-column>
-      <el-table-column label="充值时间" align="center" prop="createTime" width="200" />
-      <el-table-column label="操作" align="center" width="200">
+      <el-table-column label="充值时间" align="center" prop="createTime" width="160" />
+      <el-table-column label="操作" align="center" width="160">
         <template slot-scope="scope">
           <el-button size="mini" type="text" icon="el-icon-view" @click="op('detail', scope.row)" v-hasPermi="['work:pay:list']">详情</el-button>
         </template>
@@ -49,6 +57,7 @@
 
 <script>
 import detail from './detail.vue';
+import info from '@/views/work/user/edit';
 export default {
   name: 'Pay',
   data() {
@@ -60,6 +69,7 @@ export default {
         pageNum: 1,
         pageSize: 10,
         nums: null,
+        name: null,
         state: null,
         type: 0,
         orderByColumn: 'id',
@@ -90,6 +100,9 @@ export default {
       this.handleQuery();
     },
     op(tag, row) {
+      if (tag == 'info') {
+        this.iframe({ obj: info, param: { id: row.userId, detail: true }, title: '用户详情', width: '55%', height: '65%' });
+      }
       if (tag == 'detail') {
         this.iframe({ obj: detail, param: { id: row.id, detail: true }, title: '充值详情', width: '35%', height: '45%' });
       }

+ 2 - 2
app/common/http.js

@@ -1,6 +1,6 @@
 //const ip = 'http://127.0.0.1:9191';
-//const ip = 'https://chenglantimes.com/prod-api';
-const ip = 'http://192.168.0.105:9191';
+const ip = 'https://chenglantimes.com/prod-api';
+//const ip = 'http://192.168.1.21:9191';
 /**
  * 封装的http请求
  */

+ 0 - 1
app/pages/user/money/add.vue

@@ -41,7 +41,6 @@ export default {
 							});
 						},
 						fail: (r) => {
-							console.log('asd:' + JSON.stringify(r));
 							uni.showModal({
 								title: '提示',
 								content: r.errMsg.includes('cancel') ? '支付取消' : '支付异常',

+ 1 - 2
ruoyi-admin/src/main/java/com/ruoyi/web/work/controller/PayController.java

@@ -15,7 +15,6 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
-import java.rmi.ServerException;
 import java.util.List;
 
 /**
@@ -52,7 +51,7 @@ public class PayController extends BaseController {
     @PreAuthorize("@ss.hasPermi('work:pay:audit')")
     @Log(title = "余额提现", businessType = BusinessType.UPDATE)
     @PostMapping("/audit")
-    public AjaxResult audit(@Validated @RequestBody PayAuditDto dto) throws ServerException {
+    public AjaxResult audit(@Validated @RequestBody PayAuditDto dto) throws Exception {
         return payService.audit(dto);
     }
 

+ 4 - 0
ruoyi-admin/src/main/java/com/ruoyi/web/work/domain/Pay.java

@@ -76,4 +76,8 @@ public class Pay extends BaseData {
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     @ApiModelProperty(value = "完成时间")
     private Date completeTime;
+
+    @TableField(exist = false)
+    @ApiModelProperty(value = "用户姓名")
+    private String name;
 }

+ 1 - 1
ruoyi-admin/src/main/java/com/ruoyi/web/work/service/IPayService.java

@@ -32,7 +32,7 @@ public interface IPayService extends IService<Pay> {
      * @param dto
      * @return
      */
-    AjaxResult audit(PayAuditDto dto) throws ServerException;
+    AjaxResult audit(PayAuditDto dto) throws Exception;
 
     public void sendMessage(Pay pay);
 

+ 49 - 3
ruoyi-admin/src/main/java/com/ruoyi/web/work/service/impl/PayServiceImpl.java

@@ -1,9 +1,19 @@
 package com.ruoyi.web.work.service.impl;
 
+import cn.hutool.core.util.StrUtil;
 import cn.hutool.http.HttpUtil;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ijpay.core.IJPayHttpResponse;
+import com.ijpay.core.enums.RequestMethodEnum;
+import com.ijpay.core.kit.PayKit;
+import com.ijpay.core.kit.WxPayKit;
+import com.ijpay.wxpay.WxPayApi;
+import com.ijpay.wxpay.enums.WxDomainEnum;
+import com.ijpay.wxpay.enums.v3.TransferApiEnum;
+import com.ijpay.wxpay.model.v3.BatchTransferModel;
+import com.ijpay.wxpay.model.v3.TransferDetailInput;
 import com.ruoyi.common.constant.CacheConstants;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.redis.RedisCache;
@@ -18,14 +28,18 @@ import com.ruoyi.web.work.domain.dto.PayCashOutDto;
 import com.ruoyi.web.work.mapper.PayMapper;
 import com.ruoyi.web.work.service.IPayService;
 import com.ruoyi.web.work.service.IUserService;
+import com.ruoyi.web.work.wxpay.WxPayBean;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.env.Environment;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import javax.annotation.Resource;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.rmi.ServerException;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 
@@ -35,6 +49,10 @@ import java.util.List;
  */
 @Service
 public class PayServiceImpl extends ServiceImpl<PayMapper, Pay> implements IPayService {
+
+    @Resource
+    WxPayBean wxPayV3Bean;
+
     @Autowired
     private PayMapper payMapper;
 
@@ -47,6 +65,8 @@ public class PayServiceImpl extends ServiceImpl<PayMapper, Pay> implements IPayS
     @Autowired
     private Environment env;
 
+    private String serialNo;
+
     @Override
     public List<Pay> selectList(Pay pay) {
         return payMapper.selectList(pay);
@@ -98,12 +118,13 @@ public class PayServiceImpl extends ServiceImpl<PayMapper, Pay> implements IPayS
         if (!userService.updateById(user)) {
             throw new ServerException("更新账户余额失败,请联系平台");
         }
+
         return AjaxResult.success();
     }
 
-    @Transactional
+    @Transactional(rollbackFor = Exception.class)
     @Override
-    public AjaxResult audit(PayAuditDto dto) throws ServerException {
+    public AjaxResult audit(PayAuditDto dto) throws Exception {
         Pay pay = getById(dto.getId());
         if (pay == null || pay.getType() != 2) {
             return AjaxResult.error("提现信息不存在");
@@ -117,14 +138,29 @@ public class PayServiceImpl extends ServiceImpl<PayMapper, Pay> implements IPayS
         if (!updateById(pay)) {
             throw new ServerException("提现失败,请联系平台");
         }
+        User user = userService.getById(pay.getUserId());
         //提现驳回需要退回到用户账户余额
         if (dto.getState() == 2) {
-            User user = userService.getById(pay.getUserId());
             user.setMoney(user.getMoney().add(pay.getMoney()));
             if (!userService.updateById(user)) {
                 throw new ServerException("更新用户账户余额失败,请联系平台");
             }
         }
+        //转账到微信零钱
+        if (pay.getWay() == 1 && pay.getState() == 1) {
+            BatchTransferModel batchTransferModel = new BatchTransferModel().setAppid(wxPayV3Bean.getAppId()).setOut_batch_no(PayKit.generateStr()).setBatch_name("账户余额提现到零钱").setBatch_remark("账户余额提现到零钱").setTotal_amount(pay.getMoney().multiply(new BigDecimal("100")).intValue()).setTotal_num(1).setTransfer_detail_list(Collections.singletonList(new TransferDetailInput().setOut_detail_no(PayKit.generateStr()).setTransfer_amount(pay.getMoney().multiply(new BigDecimal("100")).intValue()).setTransfer_remark("账户余额提现到零钱").setOpenid(user.getOpenId())));
+            IJPayHttpResponse response = WxPayApi.v3(RequestMethodEnum.POST, WxDomainEnum.CHINA.toString(), TransferApiEnum.TRANSFER_BATCHES.toString(), wxPayV3Bean.getMchId(), getSerialNumber(), null, wxPayV3Bean.getKeyPath(), JSONUtil.toJsonStr(batchTransferModel));
+            String body = response.getBody();
+            // 根据证书序列号查询对应的证书来验证签名结果
+            boolean verifySignature = WxPayKit.verifySignature(response, wxPayV3Bean.getPlatformCertPath());
+            if (response.getStatus() == 200 && verifySignature) {
+                sendMessage(pay);
+                return AjaxResult.success("转账到微信零钱成功");
+            } else {
+                com.alibaba.fastjson2.JSONObject jsonObject =com.alibaba.fastjson2.JSONObject.parseObject(body);
+                throw new ServerException("转账到微信零钱失败:"+jsonObject.getString("message"));
+            }
+        }
         sendMessage(pay);
         return AjaxResult.success();
     }
@@ -161,4 +197,14 @@ public class PayServiceImpl extends ServiceImpl<PayMapper, Pay> implements IPayS
         System.out.println("body:" + body);
         System.out.println("小程序订阅消息:" + result);
     }
+
+    private String getSerialNumber() {
+        if (StrUtil.isEmpty(serialNo)) {
+            X509Certificate certificate = PayKit.getCertificate(wxPayV3Bean.getCertPath());
+            if (certificate != null) {
+                serialNo = certificate.getSerialNumber().toString(16).toUpperCase();
+            }
+        }
+        return serialNo;
+    }
 }

+ 13 - 8
ruoyi-admin/src/main/resources/mapper/work/PayMapper.xml

@@ -5,16 +5,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 <mapper namespace="com.ruoyi.web.work.mapper.PayMapper">
     
     <select id="selectList" resultType="com.ruoyi.web.work.domain.Pay">
-        select * from tb_pay
+        SELECT
+        p.*,
+        u.name
+        FROM
+        tb_pay p
+        LEFT JOIN tb_user u ON u.id = p.user_id
         <where>  
-            <if test="userId != null "> and user_id = #{userId}</if>
-            <if test="nums != null  and nums != ''"> and nums like concat('%', #{nums}, '%')</if>
-            <if test="money != null "> and money = #{money}</if>
-            <if test="state != null "> and state = #{state}</if>
-            <if test="type != null "> and type = #{type}</if>
-            <if test="dateBegin != null  and dateBegin != ''"> AND create_time BETWEEN #{dateBegin} AND #{dateEnd} + INTERVAL 1 DAY</if>
+            <if test="userId != null "> and p.user_id = #{userId}</if>
+            <if test="nums != null  and nums != ''"> and p.nums like concat('%', #{nums}, '%')</if>
+            <if test="name != null  and name != ''"> and u.name like concat('%', #{name}, '%')</if>
+            <if test="state != null "> and p.state = #{state}</if>
+            <if test="type != null "> and p.type = #{type}</if>
+            <if test="dateBegin != null  and dateBegin != ''"> AND p.create_time BETWEEN #{dateBegin} AND #{dateEnd} + INTERVAL 1 DAY</if>
         </where>
-        order by id desc
+        order by p.id desc
     </select>
 
     <select id="selectByNums" resultType="com.ruoyi.web.work.domain.Pay">