mirror of
https://github.com/certd/certd.git
synced 2026-07-15 02:07:38 +08:00
chore: 佣金等级功能
This commit is contained in:
@@ -22,6 +22,7 @@ export default {
|
|||||||
mySuite: "My Suite",
|
mySuite: "My Suite",
|
||||||
suiteBuy: "Suite Purchase",
|
suiteBuy: "Suite Purchase",
|
||||||
myTrade: "My Orders",
|
myTrade: "My Orders",
|
||||||
|
inviteCommission: "Incentive Plan",
|
||||||
paymentReturn: "Payment Return",
|
paymentReturn: "Payment Return",
|
||||||
source: "Source Code",
|
source: "Source Code",
|
||||||
github: "GitHub",
|
github: "GitHub",
|
||||||
@@ -46,6 +47,10 @@ export default {
|
|||||||
suiteSetting: "Suite Settings",
|
suiteSetting: "Suite Settings",
|
||||||
orderManager: "Order Management",
|
orderManager: "Order Management",
|
||||||
userSuites: "User Suites",
|
userSuites: "User Suites",
|
||||||
|
inviteCommissionSetting: "Incentive Plan Settings",
|
||||||
|
inviteLevel: "Promotion Levels",
|
||||||
|
inviteUserLevel: "User Promotion Levels",
|
||||||
|
inviteWithdraw: "Withdrawal Requests",
|
||||||
netTest: "Network Test",
|
netTest: "Network Test",
|
||||||
|
|
||||||
enterpriseSetting: "Enterprise Settings",
|
enterpriseSetting: "Enterprise Settings",
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export default {
|
|||||||
suiteBuy: "套餐购买",
|
suiteBuy: "套餐购买",
|
||||||
myTrade: "我的订单",
|
myTrade: "我的订单",
|
||||||
myWallet: "我的钱包",
|
myWallet: "我的钱包",
|
||||||
inviteCommission: "邀请返佣",
|
inviteCommission: "激励计划",
|
||||||
paymentReturn: "支付返回",
|
paymentReturn: "支付返回",
|
||||||
source: "源码",
|
source: "源码",
|
||||||
github: "github",
|
github: "github",
|
||||||
@@ -48,7 +48,9 @@ export default {
|
|||||||
suiteSetting: "套餐设置",
|
suiteSetting: "套餐设置",
|
||||||
orderManager: "订单管理",
|
orderManager: "订单管理",
|
||||||
userSuites: "用户套餐",
|
userSuites: "用户套餐",
|
||||||
inviteCommissionSetting: "邀请返佣设置",
|
inviteCommissionSetting: "激励计划设置",
|
||||||
|
inviteLevel: "推广等级",
|
||||||
|
inviteUserLevel: "用户推广等级",
|
||||||
inviteWithdraw: "提现申请记录",
|
inviteWithdraw: "提现申请记录",
|
||||||
netTest: "网络测试",
|
netTest: "网络测试",
|
||||||
enterpriseManager: "企业管理设置",
|
enterpriseManager: "企业管理设置",
|
||||||
|
|||||||
@@ -302,6 +302,38 @@ export const sysResources = [
|
|||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "certd.sysResources.inviteLevel",
|
||||||
|
name: "SysInviteLevel",
|
||||||
|
path: "/sys/suite/invite/level",
|
||||||
|
component: "/sys/suite/invite/level.vue",
|
||||||
|
meta: {
|
||||||
|
show: () => {
|
||||||
|
const settingStore = useSettingStore();
|
||||||
|
return settingStore.isComm;
|
||||||
|
},
|
||||||
|
icon: "ion:ribbon-outline",
|
||||||
|
permission: "sys:settings:edit",
|
||||||
|
auth: true,
|
||||||
|
keepAlive: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "certd.sysResources.inviteUserLevel",
|
||||||
|
name: "SysInviteUserLevel",
|
||||||
|
path: "/sys/suite/invite/user-level",
|
||||||
|
component: "/sys/suite/invite/user-level.vue",
|
||||||
|
meta: {
|
||||||
|
show: () => {
|
||||||
|
const settingStore = useSettingStore();
|
||||||
|
return settingStore.isComm;
|
||||||
|
},
|
||||||
|
icon: "ion:people-outline",
|
||||||
|
permission: "sys:settings:edit",
|
||||||
|
auth: true,
|
||||||
|
keepAlive: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "certd.sysResources.inviteWithdraw",
|
title: "certd.sysResources.inviteWithdraw",
|
||||||
name: "SysInviteWithdraw",
|
name: "SysInviteWithdraw",
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ export async function GetMyInvite() {
|
|||||||
return await request({ url: "/invite/my", method: "post" });
|
return await request({ url: "/invite/my", method: "post" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function OpenInvitePlan() {
|
||||||
|
return await request({ url: "/invite/open", method: "post" });
|
||||||
|
}
|
||||||
|
|
||||||
export async function GetInvitees(query: any) {
|
export async function GetInvitees(query: any) {
|
||||||
return await request({ url: "/invite/invitees/page", method: "post", data: query });
|
return await request({ url: "/invite/invitees/page", method: "post", data: query });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,17 +14,17 @@ export default function (): CreateCrudOptionsRet {
|
|||||||
rowHandle: { show: false },
|
rowHandle: { show: false },
|
||||||
columns: {
|
columns: {
|
||||||
inviteeUserId: {
|
inviteeUserId: {
|
||||||
title: "被邀请人ID",
|
title: "被推广用户ID",
|
||||||
type: "number",
|
type: "number",
|
||||||
column: { width: 140 },
|
column: { width: 140 },
|
||||||
},
|
},
|
||||||
inviteCode: {
|
inviteCode: {
|
||||||
title: "邀请码",
|
title: "推广码",
|
||||||
type: "text",
|
type: "text",
|
||||||
column: { width: 160 },
|
column: { width: 160 },
|
||||||
},
|
},
|
||||||
createTime: {
|
createTime: {
|
||||||
title: "邀请时间",
|
title: "推广时间",
|
||||||
type: "datetime",
|
type: "datetime",
|
||||||
column: { width: 180 },
|
column: { width: 180 },
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export default function (): CreateCrudOptionsRet {
|
|||||||
rowHandle: { show: false },
|
rowHandle: { show: false },
|
||||||
columns: {
|
columns: {
|
||||||
amount: {
|
amount: {
|
||||||
title: "金额",
|
title: "收益金额",
|
||||||
type: "number",
|
type: "number",
|
||||||
column: {
|
column: {
|
||||||
width: 120,
|
width: 120,
|
||||||
@@ -23,7 +23,7 @@ export default function (): CreateCrudOptionsRet {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
simpleUser: {
|
simpleUser: {
|
||||||
title: "被邀请用户",
|
title: "被推广用户",
|
||||||
type: "text",
|
type: "text",
|
||||||
column: {
|
column: {
|
||||||
width: 170,
|
width: 170,
|
||||||
@@ -43,7 +43,7 @@ export default function (): CreateCrudOptionsRet {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
consumeAmount: {
|
consumeAmount: {
|
||||||
title: "消费金额",
|
title: "推广金额",
|
||||||
type: "number",
|
type: "number",
|
||||||
column: {
|
column: {
|
||||||
width: 120,
|
width: 120,
|
||||||
|
|||||||
@@ -1,57 +1,185 @@
|
|||||||
<template>
|
<template>
|
||||||
<fs-page class="page-invite">
|
<fs-page class="page-invite">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="title">邀请返佣</div>
|
<div class="title">激励计划</div>
|
||||||
</template>
|
</template>
|
||||||
<div v-if="loaded && enabled" class="invite-body">
|
<div v-if="loaded && enabled && inviteInfo.enabled" class="invite-body">
|
||||||
<div class="invite-link-row flex-o">
|
<div class="invite-summary">
|
||||||
<span class="label">邀请码:</span>
|
<a-row :gutter="16">
|
||||||
<fs-copyable v-model="inviteInfo.inviteCode" />
|
<a-col :span="6">
|
||||||
</div>
|
<a-statistic title="累计收益" :value="amountToYuan(inviteInfo.summary.totalIncomeAmount)" suffix="元" />
|
||||||
<div class="invite-link-row flex-o mt-10">
|
</a-col>
|
||||||
<span class="label">邀请链接:</span>
|
<a-col :span="6">
|
||||||
<fs-copyable v-model="inviteInfo.inviteLink" />
|
<a-statistic title="本月收益" :value="amountToYuan(inviteInfo.summary.monthIncomeAmount)" suffix="元" />
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-statistic title="推广人数" :value="inviteInfo.summary.inviteeCount || 0" suffix="人" />
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-statistic title="累计推广金额" :value="amountToYuan(inviteInfo.summary.promotionAmount)" suffix="元" />
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a-tabs v-model:active-key="activeTab" class="invite-tabs mt-6">
|
<div class="invite-main">
|
||||||
<a-tab-pane key="invitees" tab="邀请成功">
|
<div class="invite-link-row flex-o">
|
||||||
|
<span class="label">邀请码:</span>
|
||||||
|
<fs-copyable v-model="inviteInfo.inviteCode" />
|
||||||
|
</div>
|
||||||
|
<div class="invite-link-row flex-o mt-10">
|
||||||
|
<span class="label">邀请链接:</span>
|
||||||
|
<fs-copyable v-model="inviteInfo.inviteLink" />
|
||||||
|
</div>
|
||||||
|
<div class="invite-link-row flex-o mt-10">
|
||||||
|
<span class="label">我的等级:</span>
|
||||||
|
<a-button type="link" class="level-button" @click="levelDialogOpen = true">
|
||||||
|
{{ inviteInfo.currentLevel?.name || "未设置" }}
|
||||||
|
<span v-if="inviteInfo.currentLevel">({{ inviteInfo.currentLevel.commissionRate }}%)</span>
|
||||||
|
</a-button>
|
||||||
|
<a-button size="small" @click="openAgreementDialog(false)">查看推广协议</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a-tabs v-model:active-key="activeTab" class="invite-tabs">
|
||||||
|
<a-tab-pane key="invitees" tab="推广成功">
|
||||||
<fs-crud v-if="activeTab === 'invitees'" ref="inviteesCrudRef" class="invite-crud" v-bind="inviteesCrudBinding" />
|
<fs-crud v-if="activeTab === 'invitees'" ref="inviteesCrudRef" class="invite-crud" v-bind="inviteesCrudBinding" />
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="logs" tab="佣金记录">
|
<a-tab-pane key="logs" tab="收益记录">
|
||||||
<fs-crud v-if="activeTab === 'logs'" ref="logsCrudRef" class="invite-crud" v-bind="logsCrudBinding" />
|
<fs-crud v-if="activeTab === 'logs'" ref="logsCrudRef" class="invite-crud" v-bind="logsCrudBinding" />
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
</div>
|
</div>
|
||||||
<a-empty v-else-if="loaded" description="邀请返佣未开启" />
|
<div v-else-if="loaded && enabled" class="invite-disabled">
|
||||||
|
<a-empty description="请先开通激励计划">
|
||||||
|
<a-button type="primary" @click="openAgreementDialog(true)">开通激励计划</a-button>
|
||||||
|
</a-empty>
|
||||||
|
</div>
|
||||||
|
<a-empty v-else-if="loaded" description="激励计划未开启" />
|
||||||
|
|
||||||
|
<a-modal v-model:open="levelDialogOpen" title="推广等级" width="720px" :footer="null">
|
||||||
|
<div class="level-list">
|
||||||
|
<div v-for="level in inviteInfo.levelList" :key="level.id" class="level-item" :class="{ active: level.id === inviteInfo.currentLevel?.id }">
|
||||||
|
<div class="level-title">
|
||||||
|
<span>{{ level.name }}</span>
|
||||||
|
<a-tag v-if="level.id === inviteInfo.currentLevel?.id" color="green">当前等级</a-tag>
|
||||||
|
<a-tag v-if="level.isHidden" color="orange">专属等级</a-tag>
|
||||||
|
</div>
|
||||||
|
<div class="level-meta">佣金比例 {{ level.commissionRate }}%,累计推广金额达到 {{ amountToYuan(level.minAmount) }} 元</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="inviteInfo.nextLevel" class="next-level">距离下一等级「{{ inviteInfo.nextLevel.name }}」还差 {{ amountToYuan(inviteInfo.nextLevel.gapAmount) }} 元推广金额</div>
|
||||||
|
<div v-else class="next-level">已达到当前可自动升级的最高等级</div>
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
</fs-page>
|
</fs-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, nextTick, onActivated, onMounted, reactive, ref } from "vue";
|
import { h, nextTick, onActivated, onMounted, reactive, ref } from "vue";
|
||||||
import { useFs } from "@fast-crud/fast-crud";
|
import { useFs } from "@fast-crud/fast-crud";
|
||||||
|
import { notification } from "ant-design-vue";
|
||||||
import * as api from "./api";
|
import * as api from "./api";
|
||||||
import createInviteesCrudOptions from "./crud-invitees";
|
import createInviteesCrudOptions from "./crud-invitees";
|
||||||
import createLogsCrudOptions from "./crud-logs";
|
import createLogsCrudOptions from "./crud-logs";
|
||||||
import { useSettingStore } from "/@/store/settings";
|
import { useSettingStore } from "/@/store/settings";
|
||||||
|
import { util } from "/@/utils";
|
||||||
|
import { useFormDialog } from "/@/use/use-dialog";
|
||||||
|
|
||||||
defineOptions({ name: "InviteCommission" });
|
defineOptions({ name: "InviteCommission" });
|
||||||
|
|
||||||
const settingStore = useSettingStore();
|
const settingStore = useSettingStore();
|
||||||
const enabled = computed(() => settingStore.isInviteCommissionEnabled);
|
const enabled = ref(false);
|
||||||
const activeTab = ref("invitees");
|
const activeTab = ref("invitees");
|
||||||
const inviteInfo = reactive<any>({ inviteCode: "", inviteLink: "" });
|
|
||||||
const loaded = ref(false);
|
const loaded = ref(false);
|
||||||
|
const levelDialogOpen = ref(false);
|
||||||
|
const { openFormDialog } = useFormDialog();
|
||||||
|
|
||||||
|
const inviteInfo = reactive<any>({
|
||||||
|
enabled: false,
|
||||||
|
inviteCode: "",
|
||||||
|
inviteLink: "",
|
||||||
|
agreementContent: "",
|
||||||
|
summary: { totalIncomeAmount: 0, monthIncomeAmount: 0, promotionAmount: 0, inviteeCount: 0 },
|
||||||
|
currentLevel: null,
|
||||||
|
nextLevel: null,
|
||||||
|
levelList: [],
|
||||||
|
});
|
||||||
|
|
||||||
const { crudBinding: inviteesCrudBinding, crudExpose: inviteesCrudExpose, crudRef: inviteesCrudRef } = useFs({ createCrudOptions: createInviteesCrudOptions });
|
const { crudBinding: inviteesCrudBinding, crudExpose: inviteesCrudExpose, crudRef: inviteesCrudRef } = useFs({ createCrudOptions: createInviteesCrudOptions });
|
||||||
const { crudBinding: logsCrudBinding, crudExpose: logsCrudExpose, crudRef: logsCrudRef } = useFs({ createCrudOptions: createLogsCrudOptions });
|
const { crudBinding: logsCrudBinding, crudExpose: logsCrudExpose, crudRef: logsCrudRef } = useFs({ createCrudOptions: createLogsCrudOptions });
|
||||||
|
|
||||||
async function loadMyInvite() {
|
function amountToYuan(amount: number) {
|
||||||
|
return util.amount.toYuan(amount || 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderAgreement() {
|
||||||
|
return h("div", { class: "invite-agreement-content" }, inviteInfo.agreementContent || "暂无推广协议内容");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openAgreementDialog(needOpenPlan: boolean) {
|
||||||
|
await openFormDialog({
|
||||||
|
title: needOpenPlan ? "开通激励计划" : "推广协议",
|
||||||
|
wrapper: {
|
||||||
|
width: 720,
|
||||||
|
maskClosable: !needOpenPlan,
|
||||||
|
keyboard: !needOpenPlan,
|
||||||
|
},
|
||||||
|
initialForm: {
|
||||||
|
agree: false,
|
||||||
|
},
|
||||||
|
body: renderAgreement,
|
||||||
|
columns: needOpenPlan
|
||||||
|
? {
|
||||||
|
agree: {
|
||||||
|
title: "确认",
|
||||||
|
type: "text",
|
||||||
|
form: {
|
||||||
|
col: { span: 24 },
|
||||||
|
component: {
|
||||||
|
name: "a-checkbox",
|
||||||
|
vModel: "checked",
|
||||||
|
},
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
validator: async (_rule: any, value: boolean) => {
|
||||||
|
if (value === true) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
throw new Error("请勾选同意推广协议");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
helper: "我已阅读并同意推广协议",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {},
|
||||||
|
async onSubmit(form: any) {
|
||||||
|
if (!needOpenPlan) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (form.agree !== true) {
|
||||||
|
throw new Error("请勾选同意推广协议");
|
||||||
|
}
|
||||||
|
await api.OpenInvitePlan();
|
||||||
|
notification.success({ message: "激励计划已开通" });
|
||||||
|
await refreshInvitePage(true, false);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadMyInvite(autoOpenAgreement = false) {
|
||||||
const res: any = await api.GetMyInvite();
|
const res: any = await api.GetMyInvite();
|
||||||
inviteInfo.inviteCode = res.inviteCode;
|
Object.assign(inviteInfo, res || {});
|
||||||
inviteInfo.inviteLink = res.inviteLink;
|
if (autoOpenAgreement && !inviteInfo.enabled) {
|
||||||
|
await nextTick();
|
||||||
|
await openAgreementDialog(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refreshActiveList() {
|
async function refreshActiveList() {
|
||||||
|
if (!inviteInfo.enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (activeTab.value === "invitees") {
|
if (activeTab.value === "invitees") {
|
||||||
await inviteesCrudExpose.doRefresh();
|
await inviteesCrudExpose.doRefresh();
|
||||||
} else if (activeTab.value === "logs") {
|
} else if (activeTab.value === "logs") {
|
||||||
@@ -59,13 +187,17 @@ async function refreshActiveList() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refreshInvitePage(refreshAll = false) {
|
async function refreshInvitePage(refreshAll = false, autoOpenAgreement = true) {
|
||||||
await settingStore.initOnce();
|
await settingStore.initOnce();
|
||||||
|
enabled.value = settingStore.isInviteCommissionEnabled;
|
||||||
loaded.value = true;
|
loaded.value = true;
|
||||||
if (!enabled.value) {
|
if (!enabled.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await loadMyInvite();
|
await loadMyInvite(autoOpenAgreement);
|
||||||
|
if (!inviteInfo.enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
await nextTick();
|
await nextTick();
|
||||||
if (refreshAll) {
|
if (refreshAll) {
|
||||||
await Promise.all([inviteesCrudExpose.doRefresh(), logsCrudExpose.doRefresh()]);
|
await Promise.all([inviteesCrudExpose.doRefresh(), logsCrudExpose.doRefresh()]);
|
||||||
@@ -104,6 +236,26 @@ onActivated(async () => {
|
|||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.invite-disabled {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invite-summary,
|
||||||
|
.invite-main {
|
||||||
|
flex: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invite-summary {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invite-main {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.invite-tabs {
|
.invite-tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@@ -127,11 +279,59 @@ onActivated(async () => {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
flex: none;
|
flex: none;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.level-button {
|
||||||
|
padding-left: 0;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.invite-agreement-content {
|
||||||
|
max-height: 360px;
|
||||||
|
padding: 12px;
|
||||||
|
overflow: auto;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #fafafa;
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.level-list {
|
||||||
|
.level-item {
|
||||||
|
padding: 12px 14px;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.level-item.active {
|
||||||
|
border-color: #52c41a;
|
||||||
|
background: #f6ffed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.level-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.level-meta {
|
||||||
|
margin-top: 6px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.next-level {
|
||||||
|
margin-top: 12px;
|
||||||
|
color: #1677ff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -8,6 +8,30 @@ export async function SaveSettings(data: any) {
|
|||||||
return await request({ url: "/sys/invite/settings/save", method: "post", data });
|
return await request({ url: "/sys/invite/settings/save", method: "post", data });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function GetLevels(query: any) {
|
||||||
|
return await request({ url: "/sys/invite/level/page", method: "post", data: query });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function GetLevelList() {
|
||||||
|
return await request({ url: "/sys/invite/level/list", method: "post", data: {} });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function AddLevel(data: any) {
|
||||||
|
return await request({ url: "/sys/invite/level/add", method: "post", data });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function UpdateLevel(data: any) {
|
||||||
|
return await request({ url: "/sys/invite/level/update", method: "post", data });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function GetUserLevels(query: any) {
|
||||||
|
return await request({ url: "/sys/invite/user/page", method: "post", data: query });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function SetUserLevel(data: any) {
|
||||||
|
return await request({ url: "/sys/invite/user/setLevel", method: "post", data });
|
||||||
|
}
|
||||||
|
|
||||||
export async function GetWithdraws(query: any) {
|
export async function GetWithdraws(query: any) {
|
||||||
return await request({ url: "/sys/wallet/withdraw/page", method: "post", data: query });
|
return await request({ url: "/sys/wallet/withdraw/page", method: "post", data: query });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
import { AddReq, compute, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes, dict } from "@fast-crud/fast-crud";
|
||||||
|
import * as api from "./api";
|
||||||
|
import PriceInput from "/@/views/sys/suite/product/price-input.vue";
|
||||||
|
|
||||||
|
export default function (): CreateCrudOptionsRet {
|
||||||
|
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||||
|
return await api.GetLevels(query);
|
||||||
|
};
|
||||||
|
const addRequest = async ({ form }: AddReq) => {
|
||||||
|
return await api.AddLevel(form);
|
||||||
|
};
|
||||||
|
const editRequest = async ({ form, row }: EditReq) => {
|
||||||
|
form.id = row.id;
|
||||||
|
return await api.UpdateLevel(form);
|
||||||
|
};
|
||||||
|
const delRequest = async ({ row }: DelReq) => {
|
||||||
|
row.disabled = true;
|
||||||
|
return await api.UpdateLevel(row);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
crudOptions: {
|
||||||
|
request: { pageRequest, addRequest, editRequest, delRequest },
|
||||||
|
rowHandle: {
|
||||||
|
width: 180,
|
||||||
|
fixed: "right",
|
||||||
|
buttons: {
|
||||||
|
view: { show: false },
|
||||||
|
copy: { show: false },
|
||||||
|
remove: {
|
||||||
|
text: "禁用",
|
||||||
|
show: compute(({ row }) => row.disabled !== true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
columns: {
|
||||||
|
id: {
|
||||||
|
title: "ID",
|
||||||
|
type: "number",
|
||||||
|
form: { show: false },
|
||||||
|
column: { width: 90 },
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
title: "等级名称",
|
||||||
|
type: "text",
|
||||||
|
search: { show: true },
|
||||||
|
form: {
|
||||||
|
rules: [{ required: true, message: "请输入等级名称" }],
|
||||||
|
},
|
||||||
|
column: { width: 140 },
|
||||||
|
},
|
||||||
|
minAmount: {
|
||||||
|
title: "升级金额",
|
||||||
|
type: "number",
|
||||||
|
form: {
|
||||||
|
component: { name: PriceInput, vModel: "modelValue", edit: true },
|
||||||
|
rules: [{ required: true, message: "请输入升级金额" }],
|
||||||
|
},
|
||||||
|
column: {
|
||||||
|
width: 140,
|
||||||
|
component: { name: PriceInput, vModel: "modelValue", edit: false },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
commissionRate: {
|
||||||
|
title: "佣金比例",
|
||||||
|
type: "number",
|
||||||
|
form: {
|
||||||
|
component: { min: 0, max: 100, addonAfter: "%" },
|
||||||
|
rules: [{ required: true, message: "请输入佣金比例" }],
|
||||||
|
},
|
||||||
|
column: { width: 110, align: "center", cellRender: ({ value }) => `${value || 0}%` },
|
||||||
|
},
|
||||||
|
isHidden: {
|
||||||
|
title: "隐藏等级",
|
||||||
|
type: "dict-switch",
|
||||||
|
dict: dict({
|
||||||
|
data: [
|
||||||
|
{ label: "普通等级", value: false, color: "success" },
|
||||||
|
{ label: "隐藏等级", value: true, color: "warning" },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
form: { value: false },
|
||||||
|
column: { width: 120, align: "center" },
|
||||||
|
},
|
||||||
|
sort: {
|
||||||
|
title: "排序",
|
||||||
|
type: "number",
|
||||||
|
form: { value: 10 },
|
||||||
|
column: { width: 90, align: "center" },
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
title: "状态",
|
||||||
|
type: "dict-switch",
|
||||||
|
dict: dict({
|
||||||
|
data: [
|
||||||
|
{ label: "启用", value: false, color: "success" },
|
||||||
|
{ label: "禁用", value: true, color: "error" },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
form: { value: false },
|
||||||
|
column: { width: 100, align: "center" },
|
||||||
|
},
|
||||||
|
createTime: { title: "创建时间", type: "datetime", form: { show: false }, column: { width: 170 } },
|
||||||
|
updateTime: { title: "更新时间", type: "datetime", form: { show: false }, column: { width: 170 } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
import { compute, CreateCrudOptionsProps, CreateCrudOptionsRet, dict, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||||
|
import { notification } from "ant-design-vue";
|
||||||
|
import * as api from "./api";
|
||||||
|
import PriceInput from "/@/views/sys/suite/product/price-input.vue";
|
||||||
|
import { useFormDialog } from "/@/use/use-dialog";
|
||||||
|
|
||||||
|
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
|
const { openFormDialog } = useFormDialog();
|
||||||
|
const levelDict = dict({
|
||||||
|
url: "/sys/invite/level/list",
|
||||||
|
value: "id",
|
||||||
|
label: "name",
|
||||||
|
});
|
||||||
|
|
||||||
|
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||||
|
return await api.GetUserLevels(query);
|
||||||
|
};
|
||||||
|
|
||||||
|
async function openSetLevel(row: any) {
|
||||||
|
await openFormDialog({
|
||||||
|
title: "指定推广等级",
|
||||||
|
wrapper: { width: 560 },
|
||||||
|
initialForm: {
|
||||||
|
userId: row.userId,
|
||||||
|
levelId: row.levelId,
|
||||||
|
levelLocked: row.levelLocked === true,
|
||||||
|
},
|
||||||
|
columns: {
|
||||||
|
levelId: {
|
||||||
|
title: "推广等级",
|
||||||
|
type: "dict-select",
|
||||||
|
dict: levelDict,
|
||||||
|
form: {
|
||||||
|
col: { span: 24 },
|
||||||
|
rules: [{ required: true, message: "请选择推广等级" }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
levelLocked: {
|
||||||
|
title: "锁定等级",
|
||||||
|
type: "dict-switch",
|
||||||
|
dict: dict({
|
||||||
|
data: [
|
||||||
|
{ label: "自动升级", value: false, color: "success" },
|
||||||
|
{ label: "锁定", value: true, color: "warning" },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
form: {
|
||||||
|
col: { span: 24 },
|
||||||
|
helper: "隐藏等级会自动锁定,不参与自动升级。",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async onSubmit(form: any) {
|
||||||
|
await api.SetUserLevel(form);
|
||||||
|
await crudExpose.doRefresh();
|
||||||
|
notification.success({ message: "推广等级已更新" });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
crudOptions: {
|
||||||
|
request: { pageRequest },
|
||||||
|
actionbar: { show: false },
|
||||||
|
toolbar: { show: false },
|
||||||
|
rowHandle: {
|
||||||
|
width: 130,
|
||||||
|
fixed: "right",
|
||||||
|
buttons: {
|
||||||
|
view: { show: false },
|
||||||
|
edit: { show: false },
|
||||||
|
copy: { show: false },
|
||||||
|
remove: { show: false },
|
||||||
|
setLevel: {
|
||||||
|
text: "指定等级",
|
||||||
|
type: "link",
|
||||||
|
click: ({ row }) => openSetLevel(row),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
columns: {
|
||||||
|
userId: { title: "用户ID", type: "number", search: { show: true }, column: { width: 100 } },
|
||||||
|
username: { title: "用户名", type: "text", search: { show: true }, column: { width: 160 } },
|
||||||
|
userDisplay: { title: "显示名称", type: "text", column: { width: 160 } },
|
||||||
|
enabled: {
|
||||||
|
title: "开通状态",
|
||||||
|
type: "dict-switch",
|
||||||
|
dict: dict({
|
||||||
|
data: [
|
||||||
|
{ label: "未开通", value: false, color: "default" },
|
||||||
|
{ label: "已开通", value: true, color: "success" },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
column: { width: 110, align: "center" },
|
||||||
|
},
|
||||||
|
levelId: {
|
||||||
|
title: "当前等级",
|
||||||
|
type: "dict-select",
|
||||||
|
dict: levelDict,
|
||||||
|
search: { show: true },
|
||||||
|
column: { width: 130 },
|
||||||
|
},
|
||||||
|
levelLocked: {
|
||||||
|
title: "升级方式",
|
||||||
|
type: "dict-switch",
|
||||||
|
dict: dict({
|
||||||
|
data: [
|
||||||
|
{ label: "自动升级", value: false, color: "success" },
|
||||||
|
{ label: "锁定", value: true, color: "warning" },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
column: { width: 110, align: "center" },
|
||||||
|
},
|
||||||
|
isHidden: {
|
||||||
|
title: "隐藏等级",
|
||||||
|
type: "dict-switch",
|
||||||
|
dict: dict({
|
||||||
|
data: [
|
||||||
|
{ label: "否", value: false, color: "default" },
|
||||||
|
{ label: "是", value: true, color: "warning" },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
column: { width: 100, align: "center", show: compute(({ row }) => row.levelId) },
|
||||||
|
},
|
||||||
|
commissionRate: { title: "佣金比例", type: "number", column: { width: 110, align: "center", cellRender: ({ value }) => (value == null ? "-" : `${value}%`) } },
|
||||||
|
promotionAmount: {
|
||||||
|
title: "累计推广金额",
|
||||||
|
type: "number",
|
||||||
|
column: { width: 140, component: { name: PriceInput, vModel: "modelValue", edit: false } },
|
||||||
|
},
|
||||||
|
createTime: { title: "开通时间", type: "datetime", column: { width: 170 } },
|
||||||
|
updateTime: { title: "更新时间", type: "datetime", column: { width: 170 } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<template>
|
||||||
|
<fs-page class="page-sys-invite-level">
|
||||||
|
<template #header>
|
||||||
|
<div class="title">推广等级</div>
|
||||||
|
</template>
|
||||||
|
<fs-crud ref="crudRef" v-bind="crudBinding" />
|
||||||
|
</fs-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onActivated, onMounted } from "vue";
|
||||||
|
import { useFs } from "@fast-crud/fast-crud";
|
||||||
|
import createCrudOptions from "./crud-level";
|
||||||
|
|
||||||
|
defineOptions({ name: "SysInviteLevel" });
|
||||||
|
|
||||||
|
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
crudExpose.doRefresh();
|
||||||
|
});
|
||||||
|
onActivated(() => {
|
||||||
|
crudExpose.doRefresh();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<fs-page class="page-sys-invite-setting">
|
<fs-page class="page-sys-invite-setting">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="title">邀请返佣设置</div>
|
<div class="title">激励计划设置</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="page-body">
|
<div class="page-body">
|
||||||
<a-form ref="formRef" :model="settings" :label-col="{ style: { width: '140px' } }" class="settings-form">
|
<a-form ref="formRef" :model="settings" :label-col="{ style: { width: '140px' } }" class="settings-form">
|
||||||
<a-form-item label="开启返佣" name="enabled">
|
<a-form-item label="开启激励计划" name="enabled">
|
||||||
<a-switch v-model:checked="settings.enabled" />
|
<a-switch v-model:checked="settings.enabled" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="返佣比例" name="commissionRate">
|
<a-form-item label="推广协议" name="agreementContent">
|
||||||
<a-input-number v-model:value="settings.commissionRate" :min="0" :max="100" addon-after="%" />
|
<a-textarea v-model:value="settings.agreementContent" :rows="10" placeholder="请输入用户开通激励计划前需要确认的推广协议内容" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="最低提现金额" name="minWithdrawAmountYuan">
|
<a-form-item label="最低提现金额" name="minWithdrawAmountYuan">
|
||||||
<a-input-number v-model:value="settings.minWithdrawAmountYuan" :min="0" addon-after="元" />
|
<a-input-number v-model:value="settings.minWithdrawAmountYuan" :min="0" addon-after="元" />
|
||||||
@@ -34,7 +34,8 @@ import { useSettingStore } from "/@/store/settings";
|
|||||||
|
|
||||||
defineOptions({ name: "SysInviteCommissionSetting" });
|
defineOptions({ name: "SysInviteCommissionSetting" });
|
||||||
|
|
||||||
const settings = reactive<any>({ enabled: false, commissionRate: 0, minWithdrawAmountYuan: 0, withdrawChannels: ["alipay", "bank"] });
|
const defaultAgreement = "请遵守平台推广规则,不得通过虚假注册、刷单、恶意诱导等方式获取收益。平台有权对异常推广行为进行核查,并根据实际情况暂停结算或关闭激励计划资格。";
|
||||||
|
const settings = reactive<any>({ enabled: false, agreementContent: "", minWithdrawAmountYuan: 0, withdrawChannels: ["alipay", "bank"] });
|
||||||
const withdrawChannelOptions = [
|
const withdrawChannelOptions = [
|
||||||
{ label: "支付宝", value: "alipay" },
|
{ label: "支付宝", value: "alipay" },
|
||||||
{ label: "银行卡", value: "bank" },
|
{ label: "银行卡", value: "bank" },
|
||||||
@@ -43,7 +44,7 @@ const withdrawChannelOptions = [
|
|||||||
async function loadSettings() {
|
async function loadSettings() {
|
||||||
const data: any = await api.GetSettings();
|
const data: any = await api.GetSettings();
|
||||||
settings.enabled = !!data?.enabled;
|
settings.enabled = !!data?.enabled;
|
||||||
settings.commissionRate = data?.commissionRate || 0;
|
settings.agreementContent = data?.agreementContent || defaultAgreement;
|
||||||
settings.minWithdrawAmountYuan = util.amount.toYuan(data?.minWithdrawAmount || 0);
|
settings.minWithdrawAmountYuan = util.amount.toYuan(data?.minWithdrawAmount || 0);
|
||||||
settings.withdrawChannels = data?.withdrawChannels?.length ? data.withdrawChannels : ["alipay", "bank"];
|
settings.withdrawChannels = data?.withdrawChannels?.length ? data.withdrawChannels : ["alipay", "bank"];
|
||||||
}
|
}
|
||||||
@@ -51,7 +52,7 @@ async function loadSettings() {
|
|||||||
async function saveSettings() {
|
async function saveSettings() {
|
||||||
await api.SaveSettings({
|
await api.SaveSettings({
|
||||||
enabled: settings.enabled,
|
enabled: settings.enabled,
|
||||||
commissionRate: settings.commissionRate || 0,
|
agreementContent: settings.agreementContent || "",
|
||||||
minWithdrawAmount: util.amount.toCent(settings.minWithdrawAmountYuan || 0),
|
minWithdrawAmount: util.amount.toCent(settings.minWithdrawAmountYuan || 0),
|
||||||
withdrawChannels: settings.withdrawChannels || [],
|
withdrawChannels: settings.withdrawChannels || [],
|
||||||
});
|
});
|
||||||
@@ -68,7 +69,7 @@ onMounted(loadSettings);
|
|||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
.settings-form {
|
.settings-form {
|
||||||
max-width: 720px;
|
max-width: 860px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<template>
|
||||||
|
<fs-page class="page-sys-invite-user-level">
|
||||||
|
<template #header>
|
||||||
|
<div class="title">用户推广等级</div>
|
||||||
|
</template>
|
||||||
|
<fs-crud ref="crudRef" v-bind="crudBinding" />
|
||||||
|
</fs-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onActivated, onMounted } from "vue";
|
||||||
|
import { useFs } from "@fast-crud/fast-crud";
|
||||||
|
import createCrudOptions from "./crud-user-level";
|
||||||
|
|
||||||
|
defineOptions({ name: "SysInviteUserLevel" });
|
||||||
|
|
||||||
|
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
crudExpose.doRefresh();
|
||||||
|
});
|
||||||
|
onActivated(() => {
|
||||||
|
crudExpose.doRefresh();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
ALTER TABLE cd_invite_commission_log ADD COLUMN level_id bigint NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE cd_invite_commission_log ADD COLUMN commission_rate bigint NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
CREATE TABLE `cd_invite_level`
|
||||||
|
(
|
||||||
|
`id` bigint PRIMARY KEY AUTO_INCREMENT NOT NULL,
|
||||||
|
`name` varchar(100),
|
||||||
|
`sort` bigint NOT NULL DEFAULT 0,
|
||||||
|
`min_amount` bigint NOT NULL DEFAULT 0,
|
||||||
|
`commission_rate` bigint NOT NULL DEFAULT 0,
|
||||||
|
`is_hidden` boolean NOT NULL DEFAULT false,
|
||||||
|
`disabled` boolean NOT NULL DEFAULT false,
|
||||||
|
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
CREATE INDEX `index_invite_level_sort` ON `cd_invite_level` (`sort`);
|
||||||
|
|
||||||
|
CREATE TABLE `cd_invite_user_plan`
|
||||||
|
(
|
||||||
|
`id` bigint PRIMARY KEY AUTO_INCREMENT NOT NULL,
|
||||||
|
`user_id` bigint,
|
||||||
|
`enabled` boolean NOT NULL DEFAULT false,
|
||||||
|
`level_id` bigint NOT NULL DEFAULT 0,
|
||||||
|
`level_locked` boolean NOT NULL DEFAULT false,
|
||||||
|
`agreement_time` bigint NOT NULL DEFAULT 0,
|
||||||
|
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
CREATE UNIQUE INDEX `index_invite_user_plan_user_id` ON `cd_invite_user_plan` (`user_id`);
|
||||||
|
|
||||||
|
INSERT INTO `cd_invite_level` (`name`, `sort`, `min_amount`, `commission_rate`, `is_hidden`, `disabled`)
|
||||||
|
VALUES ('青铜', 10, 0, 10, false, false),
|
||||||
|
('白银', 20, 100000, 15, false, false),
|
||||||
|
('黄金', 30, 500000, 20, false, false),
|
||||||
|
('钻石', 40, 1000000, 30, false, false);
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
ALTER TABLE cd_invite_commission_log ADD COLUMN level_id bigint NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE cd_invite_commission_log ADD COLUMN commission_rate bigint NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
CREATE TABLE "cd_invite_level"
|
||||||
|
(
|
||||||
|
"id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
||||||
|
"name" varchar(100),
|
||||||
|
"sort" bigint NOT NULL DEFAULT 0,
|
||||||
|
"min_amount" bigint NOT NULL DEFAULT 0,
|
||||||
|
"commission_rate" bigint NOT NULL DEFAULT 0,
|
||||||
|
"is_hidden" boolean NOT NULL DEFAULT (false),
|
||||||
|
"disabled" boolean NOT NULL DEFAULT (false),
|
||||||
|
"create_time" timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP),
|
||||||
|
"update_time" timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP)
|
||||||
|
);
|
||||||
|
CREATE INDEX "index_invite_level_sort" ON "cd_invite_level" ("sort");
|
||||||
|
|
||||||
|
CREATE TABLE "cd_invite_user_plan"
|
||||||
|
(
|
||||||
|
"id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
||||||
|
"user_id" bigint,
|
||||||
|
"enabled" boolean NOT NULL DEFAULT (false),
|
||||||
|
"level_id" bigint NOT NULL DEFAULT 0,
|
||||||
|
"level_locked" boolean NOT NULL DEFAULT (false),
|
||||||
|
"agreement_time" bigint NOT NULL DEFAULT 0,
|
||||||
|
"create_time" timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP),
|
||||||
|
"update_time" timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP)
|
||||||
|
);
|
||||||
|
CREATE UNIQUE INDEX "index_invite_user_plan_user_id" ON "cd_invite_user_plan" ("user_id");
|
||||||
|
|
||||||
|
INSERT INTO "cd_invite_level" ("name", "sort", "min_amount", "commission_rate", "is_hidden", "disabled")
|
||||||
|
VALUES ('青铜', 10, 0, 10, false, false),
|
||||||
|
('白银', 20, 100000, 15, false, false),
|
||||||
|
('黄金', 30, 500000, 20, false, false),
|
||||||
|
('钻石', 40, 1000000, 30, false, false);
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
ALTER TABLE cd_invite_commission_log ADD COLUMN level_id integer NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE cd_invite_commission_log ADD COLUMN commission_rate integer NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
CREATE TABLE "cd_invite_level"
|
||||||
|
(
|
||||||
|
"id" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
"name" varchar(100),
|
||||||
|
"sort" integer NOT NULL DEFAULT 0,
|
||||||
|
"min_amount" integer NOT NULL DEFAULT 0,
|
||||||
|
"commission_rate" integer NOT NULL DEFAULT 0,
|
||||||
|
"is_hidden" boolean NOT NULL DEFAULT (false),
|
||||||
|
"disabled" boolean NOT NULL DEFAULT (false),
|
||||||
|
"create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP),
|
||||||
|
"update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP)
|
||||||
|
);
|
||||||
|
CREATE INDEX "index_invite_level_sort" ON "cd_invite_level" ("sort");
|
||||||
|
|
||||||
|
CREATE TABLE "cd_invite_user_plan"
|
||||||
|
(
|
||||||
|
"id" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
"user_id" integer,
|
||||||
|
"enabled" boolean NOT NULL DEFAULT (false),
|
||||||
|
"level_id" integer NOT NULL DEFAULT 0,
|
||||||
|
"level_locked" boolean NOT NULL DEFAULT (false),
|
||||||
|
"agreement_time" integer NOT NULL DEFAULT 0,
|
||||||
|
"create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP),
|
||||||
|
"update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP)
|
||||||
|
);
|
||||||
|
CREATE UNIQUE INDEX "index_invite_user_plan_user_id" ON "cd_invite_user_plan" ("user_id");
|
||||||
|
|
||||||
|
INSERT INTO "cd_invite_level" ("name", "sort", "min_amount", "commission_rate", "is_hidden", "disabled")
|
||||||
|
VALUES ('青铜', 10, 0, 10, false, false),
|
||||||
|
('白银', 20, 100000, 15, false, false),
|
||||||
|
('黄金', 30, 500000, 20, false, false),
|
||||||
|
('钻石', 40, 1000000, 30, false, false);
|
||||||
@@ -124,7 +124,9 @@ export class MainConfiguration {
|
|||||||
|
|
||||||
this.app.getMiddleware().insertFirst(async (ctx: IMidwayKoaContext, next: NextFunction) => {
|
this.app.getMiddleware().insertFirst(async (ctx: IMidwayKoaContext, next: NextFunction) => {
|
||||||
await next();
|
await next();
|
||||||
if (shouldSetDefaultNoCache(ctx.path, ctx.response.get('Cache-Control'))) {
|
const path = ctx.path;
|
||||||
|
// 如果是首页则强制设置为不缓存
|
||||||
|
if (path === '/' || path === '/index.html' || shouldSetDefaultNoCache(path, ctx.response.get('Cache-Control')) ) {
|
||||||
ctx.response.set('Cache-Control', 'public,max-age=0');
|
ctx.response.set('Cache-Control', 'public,max-age=0');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user