Răsfoiți Sursa

基础的提交代付模块

test 10 luni în urmă
părinte
comite
6e411ba958

+ 32 - 0
build/cool/temp/eps.d.ts

@@ -587,10 +587,26 @@ declare namespace Eps {
 		 * 手续费
 		 */
 		rate?: number;
+		/**
+		 * 代付手续费
+		 */
+		withdrawRate?: number;
+		/**
+		 * 代付单笔固定费用
+		 */
+		withdrawBasicFee?: number;
+		/**
+		 * 代付单笔最低费用
+		 */
+		withdrawFeeMin?: number;
 		/**
 		 * 状态 0-禁用 1-启用
 		 */
 		status?: number;
+		/**
+		 * 代付状态 0-禁用 1-启用
+		 */
+		withdrawStatus?: number;
 		/**
 		 * MD5Key
 		 */
@@ -765,6 +781,10 @@ declare namespace Eps {
 		 * 订单状态 1-已受理 2-银行处理中 3-失败 8-成功
 		 */
 		status?: boolean;
+		/**
+		 * 通知地址
+		 */
+		notifyUrl?: string;
 		/**
 		 * 备注
 		 */
@@ -2445,10 +2465,18 @@ declare namespace Eps {
 		 * kyc topay
 		 */
 		toPay(data?: any): Promise<any>;
+		/**
+		 * 代收查询
+		 */
+		withdrawQuery(data?: any): Promise<any>;
 		/**
 		 * 回调通知
 		 */
 		notifyTest(data?: any): Promise<any>;
+		/**
+		 * 发起代付
+		 */
+		withdraw(data?: any): Promise<any>;
 		/**
 		 * 代收下单
 		 */
@@ -2498,7 +2526,9 @@ declare namespace Eps {
 			level: string;
 			basic: string;
 			toPay: string;
+			withdrawQuery: string;
 			notifyTest: string;
+			withdraw: string;
 			order: string;
 			query: string;
 			list: string;
@@ -2521,7 +2551,9 @@ declare namespace Eps {
 			level: boolean;
 			basic: boolean;
 			toPay: boolean;
+			withdrawQuery: boolean;
 			notifyTest: boolean;
+			withdraw: boolean;
 			order: boolean;
 			query: boolean;
 			list: boolean;

Fișier diff suprimat deoarece este prea mare
+ 0 - 0
build/cool/temp/eps.json


+ 61 - 5
src/modules/dj/views/merchant.vue

@@ -182,21 +182,26 @@ function makeKey() {
 // cl-upsert
 const Upsert = useUpsert({
 	dialog: {
-		width: "700px"
+		width: "800px"
+	},
+	props: {
+		labelWidth: 120
 	},
 	items: [
 		{
 			prop: "name",
 			label: "商户名",
 			required: true,
-			component: { name: "el-input" }
+			component: { name: "el-input" },
+			span: 12,
 		},
 		() => {
 			return {
 				prop: "mchId",
 				label: "商户号",
 				required: true,
-				component: { name: "el-input", props: { disabled: Upsert.value?.mode !== "add" } }
+				component: { name: "el-input", props: { disabled: Upsert.value?.mode !== "add" } },
+				span: 12,
 			};
 		},
 		{ prop: "key", label: "密钥", required: true, component: { name: "slot-key" } },
@@ -207,7 +212,8 @@ const Upsert = useUpsert({
 				name: "el-input-number",
 				props: { style: { width: "200px" }, precision: 2, min: 0 }
 			},
-			required: true
+			required: true,
+			span: 12,
 		},
 		() => {
 			return {
@@ -246,7 +252,57 @@ const Upsert = useUpsert({
 						value: 0
 					}
 				]
-			}
+			},
+
+		},
+		{
+			prop: "status",
+			label: "代付状态",
+			value: 1,
+			component: {
+				name: "el-radio-group",
+				options: [
+					{
+						label: "开启",
+						value: 1
+					},
+					{
+						label: "关闭",
+						value: 0
+					}
+				]
+			},
+
+		},
+		{
+			prop: "withdrawRate",
+			label: "代付手续费(%)",
+			component: {
+				name: "el-input-number",
+				props: { style: { width: "200px" }, precision: 2, min: 0 }
+			},
+			required: true,
+			span: 12,
+		},
+		{
+			prop: "withdrawBasicFee",
+			label: "单笔固定费用",
+			component: {
+				name: "el-input-number",
+				props: { style: { width: "200px" }, precision: 2, min: 0 }
+			},
+			required: true,
+			span: 12,
+		},
+		{
+			prop: "withdrawFeeMin",
+			label: "单笔最低费用",
+			component: {
+				name: "el-input-number",
+				props: { style: { width: "200px" }, precision: 2, min: 0 }
+			},
+			required: true,
+			span: 12,
 		},
 		{
 			prop: "remark",

+ 89 - 169
src/modules/dj/views/toWithdraw.vue

@@ -9,62 +9,54 @@
           <span>在线代付</span>
         </div>
       </template>
-      <el-form ref="ruleFormRef" :model="form" :rules="rules" label-width="200px">
-        <el-form-item label="商户号(mchId)">
-          <el-select v-model="form.mchId" clearable>
-            <el-option v-for="item in merchantOptions" :key="item.id" :label="`${item.name}(${item.mchId})`"
-              :value="item.mchId">
-              <el-row type="flex">
-                <el-col :span="12">{{ item.name }}</el-col>
-                <el-col :span="12" style="text-align: right">{{ item.mchId }}</el-col>
-              </el-row>
-            </el-option>
-          </el-select>
+      <el-form :model="form" label-width="200px">
+        <el-form-item label="商户号(mchId)" required>
+          <el-input v-model="form.mchId" />
         </el-form-item>
-        <el-form-item label="系统余额">
-          <el-input v-model="form.balance" readonly style="width: 300px; color:red"></el-input>
-          <el-button type="primary" style="margin-left: 2px;" :loading="loading"
-            @click="getBalance()">查询商户余额</el-button>
+        <el-form-item label="密钥(key)" required>
+          <el-input v-model="key" />
         </el-form-item>
-        <el-form-item label="通道" prop="code">
-          <el-select v-model="form.code">
-            <el-option v-for="item in codeOpitons" :key="item.id" :label="`${item.name}(${item.code})`"
-              :value="item.code">
-              <el-row type="flex">
-                <el-col :span="12">{{ item.code }}</el-col>
-                <el-col :span="12" style="text-align: right">{{ item.name }}</el-col>
-              </el-row>
-            </el-option>
-          </el-select>
+        <el-form-item label="商户订单号(outOrderNo)" required>
+          <el-input v-model="form.outOrderNo" style="width: 80%;"></el-input>
+          <el-button type="primary" style="margin-left: 2px;" @click="makeKey()">自动生成</el-button>
         </el-form-item>
-        <el-form-item label="通道余额" prop="channelBalance">
-          <el-input v-model="form.channelBalance" readonly style="width: 300px; color:red"></el-input>
-          <el-button type="primary" style="margin-left: 2px;" :loading="loading"
-            @click="getChannelBalance()">查询通道余额</el-button>
+        <el-form-item label="金额(amount)" required>
+          <el-input-number :min="1" :max="99999" :precision="2" v-model="form.amount" />
         </el-form-item>
-        <el-divider />
-        <el-form-item label="银行卡" prop="accountNo">
-          <el-select v-model="form.accountNo" style="min-width: 50%">
-            <el-option v-for="item in bankOptions" :key="item.id" :label="`${item.accountName}(${item.accountNo})`"
-              :value="item.accountNo">
-              <el-row type="flex">
-                <el-col :span="6">{{ item.accountName }}</el-col>
-                <el-col :span="12"> {{ item.accountNo }}</el-col>
-                <el-col :span="6" style="text-align: right">{{ item.bankName }}</el-col>
-              </el-row>
-            </el-option>
-          </el-select>
+        <el-form-item label="货币单位(currency)" required>
+          <el-input v-model="form.currency" />
         </el-form-item>
-        <el-form-item label=" 金额" prop="amount">
-          <el-input-number :min="1" :max="99999" :precision="2" v-model="form.amount" />
+        <el-form-item label="代付类型(payType)">
+          <el-input v-model="form.payType" />
+        </el-form-item>
+        <el-form-item label="银行账号(accountNo)" required>
+          <el-input v-model="form.accountNo" />
+        </el-form-item>
+        <el-form-item label="收款人姓名(accountName)" required>
+          <el-input v-model="form.accountName" />
+        </el-form-item>
+        <el-form-item label="银行名称(bankName)" required>
+          <el-input v-model="form.bankName" />
+        </el-form-item>
+        <el-form-item label="银行编码(bankCode)" required>
+          <el-input v-model="form.bankCode" />
         </el-form-item>
-        <el-form-item label="备注">
-          <el-input v-model="form.remark" :rows="3" type="textarea" />
+        <el-form-item label="代付回调地址(notifyUrl)" required>
+          <el-input v-model="form.notifyUrl" />
+        </el-form-item>
+        <el-form-item label="待签字符串">
+          <code>{{ signStr }}</code>
+        </el-form-item>
+        <el-form-item label="签名(sign)" required>
+          <el-input v-model="sign" />
         </el-form-item>
         <el-form-item>
-          <el-button type="primary" :loading="loading" @click="onSubmit(ruleFormRef)">确认</el-button>
+          <el-button type="primary" :loading="loading" @click="onSubmit">确认</el-button>
           <el-button @click="onReset" :loading="loading">重置</el-button>
         </el-form-item>
+        <el-form-item label="接口返回(JSON)" required>
+          <el-input v-model="data" :rows="6" type="textarea" />
+        </el-form-item>
       </el-form>
     </el-card>
   </div>
@@ -72,153 +64,81 @@
 
 <script lang="ts" name="test" setup>
 import { useCool } from "/@/cool";
-import { ref, onMounted, reactive } from "vue";
-import { ElMessage, ElMessageBox } from "element-plus";
-import type { FormInstance, FormRules } from "element-plus";
-const { router } = useCool();
-
-const ruleFormRef = ref<FormInstance>();
+import { ref, computed } from "vue";
+import md5 from "md5";
+import dayjs from "dayjs";
 
 const { service } = useCool();
 const loading = ref(false);
 
-const bankOptions = ref([]);
-const codeOpitons = ref([]);
-const merchantOptions = ref([]);
+function makeKey() {
+  form.value.outOrderNo = "TEST" + dayjs().format("YYYYMMDDHHmmss");
+}
+
+const key = ref("7bde11c1e5be0fca046f099c4264076e");
 
 const defaultForm = {
-  balance: "",
-  channelBalance: "",
-  code: "",
-  mchId: "",
-  amount: 1,
-  accountNo: "",
-  remark: ""
+  mchId: "test",
+  outOrderNo: "",
+  notifyUrl: "http://157.175.73.225/api/admin/dj/open/notifyTest",
+  returnUrl: "https://www.baidu.com",
+  amount: 100,
+  payType: "",
+  currency: 'USD',
+  accountNo: "accountNo",
+  accountName: "accountName",
+  bankName: "bankName",
+  bankCode: "bankCode"
 };
 const form = ref({
   ...defaultForm
 });
 
-const rules = reactive<FormRules<RuleForm>>({
-  code: [{ required: true, message: "请选择通道编码", trigger: "change" }],
-  channelBalance: [{ required: true, message: "请先获取通道余额", trigger: "blur" }],
-  accountNo: [{ required: true, message: "请选择银行卡", trigger: "change" }],
-  amount: [{ required: true, message: "请输入金额", trigger: "blur" }]
-});
-
-const onSubmit = async (formEl: FormInstance | undefined) => {
-  if (!formEl) return;
-  const valid = await formEl.validate();
-  if (!valid) {
-    return;
-  }
+const onSubmit = async () => {
+  loading.value = true;
   try {
-    // if (form.value.mchId && +form.value.amount > +form.value.balance) {
-    //   throw new Error("商户余额不足");
-    // }
-    // if (+form.value.amount > +form.value.channelBalance) {
-    //   throw new Error("通道余额不足");
-    // }
-    loading.value = true;
-    const bank = bankOptions.value.find((item) => item.accountNo === form.value.accountNo);
-    const param = {
-      code: form.value.code,
-      mchId: form.value.mchId,
-      amount: form.value.amount,
-      accountNo: form.value.accountNo,
-      bankCode: bank.bankCode,
-      bankName: bank.bankName,
-      accountName: bank.accountName,
-      remark: form.value.remark
-    };
-    const res = await service.dj.withdraw.withdraw(param);
-    console.log("代付接口返回:", res);
-    ElMessageBox.confirm(
-      "发起代付成功,代付结果请到代付列表查询! \n是否前往代付列表?",
-      "Success",
-      {
-        confirmButtonText: "前往代付列表",
-        cancelButtonText: "取消",
-        type: "success"
-      }
-    )
-      .then(() => {
-        router.push("/dj/withdraw");
-      })
-      .catch(() => {
-        onReset();
-      });
-  } catch (err) {
-    ElMessage.error(err.message);
+    const res = await service.dj.open.withdraw({
+      ...form.value,
+      sign: sign.value
+    });
+    data.value = JSON.stringify(res);
+  } catch (err: any) {
+    data.value = err.message;
   }
   loading.value = false;
 };
 
-const onReset = () => {
-  form.value = {
-    ...defaultForm
-  };
-};
+const signStr = computed(() => {
+  return signSort(form.value) + `&key=${key.value}`;
+});
 
-async function getCodes() {
-  bankOptions.value = await service.dj.bank.list({});
-}
+const sign = computed(() => {
+  return md5(signSort(form.value) + `&key=${key.value}`);
+});
 
-async function getBanks() {
-  codeOpitons.value = await service.dj.channel.list({
-    status: 1
-  });
-  form.value.code = codeOpitons.value[0].code || "";
-}
+const data = ref("");
 
-async function getMerchants() {
-  merchantOptions.value = await service.dj.merchant.list({
-    status: 1
+function signSort(args: any) {
+  var keys = Object.keys(args);
+  keys = keys.sort();
+  var newArgs = {};
+  keys.forEach(function (key) {
+    newArgs[key] = args[key];
   });
-}
 
-async function getBalance() {
-  loading.value = true;
-  try {
-    const param = {};
-    if (form.value.mchId) {
-      param.keyWord = form.value.mchId;
-    }
-    const list = await service.dj.mchBalance.list(param);
-    form.value.balance = list.reduce((total, item) => {
-      return (total += +item.balance);
-    }, 0);
-  } catch (e) {
-    console.log(e);
+  var string = "";
+  for (var k in newArgs) {
+    string += "&" + k + "=" + newArgs[k];
   }
-  loading.value = false;
+  string = string.substr(1);
+  return string;
 }
 
-async function getChannelBalance() {
-  if (!form.value.code) {
-    ElMessage.error("请先选择通道编码");
-    return;
-  }
-  loading.value = true;
-  try {
-    const data = await service.dj.channel.queryChannelBalance({ code: form.value.code });
-    if (+data.status) {
-      form.value.channelBalance = +data.balance;
-    } else {
-      ElMessage.error(data.message);
-    }
-  } catch (e) {
-    ElMessage.error(e.message);
-  }
-  ruleFormRef.value.clearValidate();
-  loading.value = false;
-}
-
-onMounted(() => {
-  getCodes();
-  getBanks();
-  getMerchants();
-});
+const onReset = () => {
+  form.value = {
+    ...defaultForm
+  };
+};
 </script>
 
 <style lang="scss">

+ 201 - 0
src/modules/dj/views/withdrawChannel.vue

@@ -0,0 +1,201 @@
+<template>
+	<cl-crud ref="Crud">
+		<cl-row>
+			<!-- 刷新按钮 -->
+			<cl-refresh-btn />
+			<!-- 新增按钮 -->
+			<cl-add-btn />
+			<!-- 删除按钮 -->
+			<cl-multi-delete-btn />
+			<cl-flex1 />
+			<!-- 关键字搜索 -->
+			<cl-search-key />
+		</cl-row>
+
+		<cl-row>
+			<!-- 数据表格 -->
+			<cl-table ref="Table" :contextMenu="[]">
+				<template #column-balance="{ scope }">
+					<el-link v-if="!scope.row.balance" type="primary" @click="getBalanceByCode(scope.row)">查询</el-link>
+					<el-link v-else type="primary" @click="getBalanceByCode(scope.row)">
+						余额:{{ scope.row.balance.balance }} <br /> 冻结:{{ scope.row.balance.freeze }}
+					</el-link>
+				</template>
+			</cl-table>
+		</cl-row>
+
+		<cl-row>
+			<cl-flex1 />
+			<!-- 分页控件 -->
+			<cl-pagination />
+		</cl-row>
+
+		<!-- 新增、编辑 -->
+		<cl-upsert ref="Upsert">
+			<template #slot-range="{ scope }">
+				<el-row :gutter="10">
+					<el-col :span="11">
+						<el-input-number style="width: 180px" v-model="scope.min" :min="0" :precision="2"
+							:controls="false" />
+					</el-col>
+					<el-col :span="2" style="text-align: center">
+						<b>-</b>
+					</el-col>
+					<el-col :span="11">
+						<el-input-number style="width: 180px" v-model="scope.max" :min="0" :precision="2"
+							:controls="false" />
+					</el-col>
+				</el-row>
+			</template>
+		</cl-upsert>
+	</cl-crud>
+</template>
+
+<script lang="ts" name="dj-withdrawChannel" setup>
+import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
+import { useCool } from "/@/cool";
+import { ElMessage } from "element-plus";
+
+
+const { service } = useCool();
+
+// cl-upsert
+const Upsert = useUpsert({
+	dialog: {
+		width: "800px"
+	},
+	props: {
+		labelWidth: "120px"
+	},
+	items: [
+		{
+			prop: "name",
+			label: "通道名",
+			required: true,
+			component: { name: "el-input" },
+			span: 12
+		},
+		{
+			prop: "code",
+			label: "通道编码",
+			required: true,
+			component: { name: "el-input" },
+			span: 12
+		},
+		{
+			prop: "service",
+			label: "代码服务",
+			required: true,
+			component: { name: "el-input" },
+			span: 12
+		},
+		{
+			prop: "currency",
+			label: "货币",
+			required: true,
+			component: { name: "el-input" },
+			span: 12
+		},
+		{
+			prop: "rate",
+			label: "费率",
+			component: {
+				name: "el-input-number",
+				props: { style: { width: "200px" }, precision: 2, min: 0 }
+			},
+			required: true
+		},
+		{
+			prop: "basicFee",
+			label: "单笔固定费用",
+			required: true,
+			component: {
+				name: "el-input-number",
+				props: { style: { width: "200px" }, precision: 2, min: 0 }
+			},
+		},
+		{
+			prop: "feeMin",
+			label: "单笔最低费用",
+			required: true,
+			component: {
+				name: "el-input-number",
+				props: { style: { width: "200px" }, precision: 2, min: 0 }
+			},
+		},
+		{
+			prop: "min",
+			label: "单笔限额",
+			required: true,
+			component: { name: "slot-range" },
+			span: 16
+		},
+		{
+			prop: "status",
+			label: "状态",
+			component: { name: "cl-switch" },
+			required: true
+		},
+		{
+			prop: "remark",
+			label: "备注",
+			component: { name: "el-input", props: { type: "textarea", rows: 4 } }
+		}
+	]
+});
+
+// cl-table
+const Table = useTable({
+	columns: [
+		{ type: "selection" },
+		{ prop: "name", label: "通道名" },
+		{ prop: "code", label: "通道编码", },
+		// { prop: "service", label: "代码服务" },
+		{ prop: "currency", label: "货币" },
+		{
+			prop: "rate",
+			label: "费率",
+			formatter(row) {
+				return row.rate + "%";
+			}
+		},
+		{ prop: "basicFee", label: "单笔固定费用" },
+		{ prop: "feeMin", label: "单笔最低费用" },
+		{
+			prop: "min",
+			label: "单笔限额",
+			formatter(row) {
+				return "[" + +row.min + "," + +row.max + "]";
+			}
+		},
+		{
+			prop: "balance",
+			label: "通道余额",
+		},
+		{ prop: "status", label: "状态", component: { name: "cl-switch" } },
+		// { prop: "remark", label: "备注", showOverflowTooltip: true },
+		// { prop: "createTime", label: "创建时间", sortable: "desc", width: 160 },
+		{ type: "op", buttons: ["edit", "delete"] }
+	]
+});
+
+// cl-crud
+const Crud = useCrud(
+	{
+		service: service.dj.withdrawChannel
+	},
+	(app) => {
+		app.refresh();
+	}
+);
+const getBalanceByCode = async (row: any) => {
+	try {
+		const data = await service.dj.withdrawChannel.queryChannelBalance({ code: row.code })
+		if (data) {
+			row.balance = data;
+		}
+	} catch (e: any) {
+		ElMessage.error(e.message);
+	}
+}
+</script>

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff