Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
016c0f4886 |
@@ -122,14 +122,6 @@ const apiRouter: IMenu[] = [
|
||||
title: '会员管理'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/super/vipSetMeal',
|
||||
name: 'vipSetMeal',
|
||||
component: '/super/vipSetMeal/index',
|
||||
meta: {
|
||||
title: 'vip套餐'
|
||||
}
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -169,14 +161,6 @@ const apiRouter: IMenu[] = [
|
||||
meta: {
|
||||
title: '域名管理'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/super/payRecord',
|
||||
name: 'payRecord',
|
||||
component: '/super/payRecord/index',
|
||||
meta: {
|
||||
title: '付款记录'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -25,7 +25,6 @@ const { state, sizeChangeHandle, currentChangeHandle } = useCurd({
|
||||
>
|
||||
<el-table-column prop="id" label="ID" width="60"></el-table-column>
|
||||
<el-table-column prop="username" label="手机号"></el-table-column>
|
||||
<el-table-column prop="agent_package.name" label="当前套餐"> </el-table-column>
|
||||
<el-table-column prop="credit" label="剩余积分"></el-table-column>
|
||||
<el-table-column prop="accumulate_credit" label="累计充值积分"> </el-table-column>
|
||||
<el-table-column prop="commission" label="剩余佣金" width="120"> </el-table-column>
|
||||
|
||||
@@ -71,9 +71,8 @@ const getHomeData = async (type: any) => {
|
||||
member_count: res.member_count,
|
||||
agent_count: res.agent_count,
|
||||
user_count: res.user_count,
|
||||
pay_vip: res.pay_vip,
|
||||
pay_agent: res.pay_agent,
|
||||
pay_count: res.pay_count,
|
||||
card_count: res.card_count,
|
||||
uv_count: res.uv_count,
|
||||
}
|
||||
// 用户增长趋势
|
||||
if (res.user_growth.length > 0) {
|
||||
@@ -116,24 +115,24 @@ onMounted(() => {
|
||||
</div>
|
||||
<img src="@/assets/admin-home/use-count.png" alt="" />
|
||||
</div>
|
||||
<!-- 会员数 -->
|
||||
<!-- 卡片数量 -->
|
||||
<div class="item-box vip-count-bg">
|
||||
<div class="item-box-l">
|
||||
<span class="name">支付会员数</span>
|
||||
<span class="name">卡片数量</span>
|
||||
<div>
|
||||
<span class="value">{{ dataValue?.pay_count || 0 }}</span>
|
||||
<span>人</span>
|
||||
<span class="value">{{ dataValue?.card_count || 0}}</span>
|
||||
<span>个</span>
|
||||
</div>
|
||||
</div>
|
||||
<img src="@/assets/admin-home/use-count.png" alt="" />
|
||||
</div>
|
||||
<!-- 会员收入 -->
|
||||
<!-- 卡片过期数量 -->
|
||||
<div class="item-box vip-inp-bg">
|
||||
<div class="item-box-l">
|
||||
<span class="name">购买会员收入</span>
|
||||
<span class="name">UV次数</span>
|
||||
<div>
|
||||
<span class="value">{{ dataValue?.pay_vip }}</span>
|
||||
<span>元</span>
|
||||
<span class="value">{{ dataValue?.uv_count || 0}}</span>
|
||||
<span>次</span>
|
||||
</div>
|
||||
</div>
|
||||
<img src="@/assets/admin-home/use-count.png" alt="" />
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { configStore } from '@/stores'
|
||||
import { data_get } from '@/utils'
|
||||
import PayButton from '@/views/home/components/PayButton.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<table class="equity-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>权益/版本</th>
|
||||
<th :class="`th-${index + 1}`" v-for="(item, index) in configStore.package" :key="index">
|
||||
<div class="margin-b-5">{{ item.name }}</div>
|
||||
<div>¥{{ item.price }}/月</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="td-1 padding-l-20">每月访问数量(UV)上限</td>
|
||||
<td :class="`td-${index + 2}`" v-for="(item, index) in configStore.package" :key="index">
|
||||
{{ data_get(item, 'config.uv_limit') }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="td-1 padding-l-20">链接创建数量上限(同时)</td>
|
||||
<td :class="`td-${index + 2}`" v-for="(item, index) in configStore.package" :key="index">
|
||||
{{ data_get(item, 'config.count_limit') }}条
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="td-1 padding-l-20">自建小程序池可创建数量</td>
|
||||
<td :class="`td-${index + 2}`" v-for="(item, index) in configStore.package" :key="index">
|
||||
{{ data_get(item, 'config.min_count_limit') }}/个
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="td-1 padding-l-20">链接保留时长</td>
|
||||
<td :class="`td-${index + 2}`" v-for="(item, index) in configStore.package" :key="item.id">
|
||||
永久
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="td-1 padding-l-20">支持的类型</td>
|
||||
<td :class="`td-${index + 2}`" v-for="(item, index) in configStore.package" :key="index">
|
||||
<div v-if="data_get(item, 'config.allow_type.CLI_QR')">草料二维码</div>
|
||||
<div v-if="data_get(item, 'config.allow_type.KING_DOC')">金山文档</div>
|
||||
<div v-if="data_get(item, 'config.allow_type.WORK_WECHAT')">企业微信</div>
|
||||
<div v-if="data_get(item, 'config.allow_type.LANDING_MINI')">微信二维码</div>
|
||||
<div v-if="data_get(item, 'config.allow_type.MINI_PROGRAM')">自定义小程序</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="td-1 padding-l-20">官方小程序池使用</td>
|
||||
<td :class="`td-${index + 2}`" v-for="(item, index) in configStore.package" :key="index">
|
||||
<span v-if="data_get(item, 'config.pre_min')">支持</span>
|
||||
<span v-else class="red">不支持</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="td-1 padding-l-20">数据统计/分析</td>
|
||||
<td :class="`td-${index + 2}`" v-for="(item, index) in configStore.package" :key="item.id">
|
||||
支持
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="td-1 padding-l-20">自定义落地页</td>
|
||||
<td :class="`td-${index + 2}`" v-for="(item, index) in configStore.package" :key="index">
|
||||
<span v-if="data_get(item, 'config.cur_index')">支持</span>
|
||||
<span v-else class="red">不支持</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="td-1 padding-l-20">自建小程序池下架自动检测</td>
|
||||
<td :class="`td-${index + 2}`" v-for="(item, index) in configStore.package" :key="index">
|
||||
<span v-if="data_get(item, 'config.min_disabled_check')">支持</span>
|
||||
<span v-else class="red">不支持</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="td-1 padding-l-20">免费技术协助</td>
|
||||
<td :class="`td-${index + 2}`" v-for="(item, index) in configStore.package" :key="item.id">
|
||||
支持
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="td-1"></td>
|
||||
<td :class="`td-${index + 2}`" v-for="(item, index) in configStore.package" :key="item.id">
|
||||
<PayButton v-if="item.price > 0" type="pay-vip" :info="item">
|
||||
<el-button type="primary">立即开通/续费</el-button>
|
||||
</PayButton>
|
||||
<el-button v-else>免费</el-button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
table,
|
||||
tr,
|
||||
td,
|
||||
th {
|
||||
border: 1px solid #eaebee;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.equity-table {
|
||||
width: 100%;
|
||||
}
|
||||
thead tr {
|
||||
height: 80px;
|
||||
font-size: 16px;
|
||||
.th-1 {
|
||||
background-color: #d7e9ff;
|
||||
}
|
||||
.th-2 {
|
||||
background-color: #ffebcc;
|
||||
}
|
||||
.th-3 {
|
||||
background-color: #ffe6e6;
|
||||
}
|
||||
}
|
||||
|
||||
tbody tr {
|
||||
td {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
.td-1 {
|
||||
width: 260px;
|
||||
text-align: left;
|
||||
}
|
||||
.td-2 {
|
||||
background-color: #f9fcff;
|
||||
}
|
||||
.td-3 {
|
||||
background-color: #fffdf8;
|
||||
}
|
||||
.td-4 {
|
||||
background-color: #fff9fa;
|
||||
}
|
||||
}
|
||||
.red {
|
||||
color: #ff0000;
|
||||
}
|
||||
</style>
|
||||
+12
-114
@@ -5,7 +5,6 @@ import { userStore } from '@/stores'
|
||||
import ChangePassword from '@/views/home/components/ChangePassword.vue'
|
||||
import Welcome from '@/views/home/components/Welcome.vue'
|
||||
import { data_get, setClipboard } from '@/utils'
|
||||
import BuyVip from '@/views/home/components/BuyVip.vue'
|
||||
import { getNotify } from '@/api/super'
|
||||
// 获取概况数据
|
||||
const dataList = ref<any>({})
|
||||
@@ -25,13 +24,7 @@ const copyCode = () => {
|
||||
var path = window.location.pathname
|
||||
setClipboard(root + path + '#/register?code=' + code)
|
||||
}
|
||||
const success = ref(false)
|
||||
// 开通会员
|
||||
const buyVipVisible = ref(false)
|
||||
const tiyan = () => {
|
||||
success.value = false
|
||||
localStorage.removeItem('register')
|
||||
}
|
||||
|
||||
const notify = ref({} as any)
|
||||
const noticeShow = ref(false)
|
||||
onMounted(() => {
|
||||
@@ -43,21 +36,15 @@ onMounted(() => {
|
||||
noticeShow.value = true
|
||||
}
|
||||
})
|
||||
if (localStorage.getItem('register')) {
|
||||
success.value = true
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="home_container" v-if="initFlag">
|
||||
<!-- 公告 -->
|
||||
<!-- <HomeNotice :notices="dataList.notices" />-->
|
||||
|
||||
<el-row :gutter="10">
|
||||
<el-col :xs="24" :sm="16">
|
||||
<el-row :gutter="12">
|
||||
<el-col :xs="24" :sm="24">
|
||||
<el-row :gutter="10">
|
||||
<el-col :xs="24"><Welcome /></el-col>
|
||||
<el-col :xs="22"><Welcome /></el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="10" style="margin-top: 10px">
|
||||
<el-col :xs="24">
|
||||
@@ -69,23 +56,17 @@ onMounted(() => {
|
||||
<el-row :gutter="10">
|
||||
<el-col :xs="24" :sm="12">
|
||||
<div class="home-card" style="background-color: #f6fafe; --num-color: #2b6bff">
|
||||
<div class="home-card-title">本月链接访问UV</div>
|
||||
<div class="home-card-title">链接访问UV</div>
|
||||
<div class="home-card-warp">
|
||||
<div class="home-card-item">
|
||||
<div class="home-card-num">{{ dataList.used_uv ?? 0 }}</div>
|
||||
<div class="home-num-title">已使用UV次数</div>
|
||||
<div class="home-num-title">总UV次数</div>
|
||||
</div>
|
||||
<div class="home-card-item">
|
||||
<div class="home-card-num">
|
||||
{{ data_get(userStore.userInfo, 'vip_package.config.uv_limit', 0) }}
|
||||
{{ dataList.month_uv ?? 0 }}
|
||||
</div>
|
||||
<div class="home-num-title">会员套餐UV次数</div>
|
||||
</div>
|
||||
<div class="home-card-item">
|
||||
<div class="home-card-num">
|
||||
{{ data_get(userStore.userInfo, 'vip_package.config.uv_limit', 0) - (dataList.used_uv ?? 0) }}
|
||||
</div>
|
||||
<div class="home-num-title">剩余可访问UV次数</div>
|
||||
<div class="home-num-title">本月UV次数</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -95,20 +76,14 @@ onMounted(() => {
|
||||
<div class="home-card-title">已创建数量</div>
|
||||
<div class="home-card-warp">
|
||||
<div class="home-card-item">
|
||||
<div class="home-card-num"> {{ data_get(userStore.userInfo, 'link_total', 0) }}</div>
|
||||
<div class="home-num-title">总链接</div>
|
||||
<div class="home-card-num"> {{ data_get(userStore.userInfo, 'link_created', 0) }}</div>
|
||||
<div class="home-num-title">卡片数量</div>
|
||||
</div>
|
||||
<div class="home-card-item">
|
||||
<div class="home-card-num">
|
||||
{{ data_get(userStore.userInfo, 'link_created', 0) }}
|
||||
{{ data_get(userStore.userInfo, 'link_expire', 0) }}
|
||||
</div>
|
||||
<div class="home-num-title">已创建</div>
|
||||
</div>
|
||||
<div class="home-card-item">
|
||||
<div class="home-card-num">
|
||||
{{ data_get(userStore.userInfo, 'link_amount', 0) }}
|
||||
</div>
|
||||
<div class="home-num-title">未创建</div>
|
||||
<div class="home-num-title">到期卡片</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -118,40 +93,6 @@ onMounted(() => {
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="8">
|
||||
<div class="vip-info bgw-box">
|
||||
<div class="right-h margin-b-20">
|
||||
<img class="margin-right-5" src="@/assets/home/vip-icon.png" alt="" />
|
||||
<span class="title">会员信息</span>
|
||||
</div>
|
||||
<div class="right-m margin-b-20">
|
||||
{{
|
||||
userStore.userInfo.site_name
|
||||
}}作为一款私域引流工具,凭借其强大的功能和灵活的应用场景,成为了推广营销领域的热门选择,它支持从抖音直接跳转到个人微信、微信群、小程序以及企业微信客服,有效打通了不同平台之间的壁垒,实现了流量的高效转化和利用,推广营销必备!
|
||||
</div>
|
||||
<div class="right-f margin-b-20">
|
||||
<span class="margin-right-10">引流平台功能:</span>
|
||||
<img class="margin-right-10" src="@/assets/home/q-wx.png" alt="" />
|
||||
<img class="margin-right-10" src="@/assets/home/wx.png" alt="" />
|
||||
</div>
|
||||
<div class="right-s margin-b-20">
|
||||
<span class="margin-right-10">当前会员套餐:</span>
|
||||
<span>{{ data_get(userStore.userInfo, 'vip_package.name', '未开通会员') }}</span>
|
||||
</div>
|
||||
<div class="right-s margin-b-20">
|
||||
<span class="margin-right-10">会员到期日期:</span>
|
||||
<span>{{ userStore.userInfo.end_at }}</span>
|
||||
</div>
|
||||
<el-row :gutter="10">
|
||||
<el-col :xs="24" :sm="24">
|
||||
<el-button class="btn" type="primary" @click="buyVipVisible = true">开通会员/续费会员</el-button>
|
||||
<el-dialog title="开通会员" width="1000px" append-to-body v-model="buyVipVisible">
|
||||
<BuyVip />
|
||||
</el-dialog>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="10">
|
||||
@@ -173,35 +114,6 @@ onMounted(() => {
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row class="m-t-10">
|
||||
<el-col :span="24">
|
||||
<div class="bgw-box">
|
||||
<div class="right-h margin-b-20">
|
||||
<img class="margin-right-5" src="@/assets/home/vip-icon.png" alt="" />
|
||||
<span class="title">会员订单记录</span>
|
||||
</div>
|
||||
<el-table
|
||||
height="249"
|
||||
:data="dataList.vip_logs"
|
||||
:header-cell-style="{
|
||||
'text-align': 'center',
|
||||
'background-color': '#F1F1F1',
|
||||
height: '50px',
|
||||
color: '#333'
|
||||
}"
|
||||
:cell-style="{ padding: '0', 'text-align': 'center', height: '50px', color: '#333' }"
|
||||
>
|
||||
<el-table-column prop="vip_package.name" label="类型"></el-table-column>
|
||||
<el-table-column prop="amount" label="金额"></el-table-column>
|
||||
<el-table-column prop="id" label="支付方式">
|
||||
<template v-slot:default>微信</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="start_at" label="生效时间"></el-table-column>
|
||||
<el-table-column prop="end_at" label="到期时间"></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-dialog :title="notify.title" :show-close="true" :width="700" append-to-body v-model="noticeShow">
|
||||
<div class="notify">
|
||||
@@ -212,20 +124,6 @@ onMounted(() => {
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="" :show-close="false" :width="500" append-to-body v-model="success">
|
||||
<div class="successCard">
|
||||
<div style="flex: 1">
|
||||
<h2 class="margin-b-10">注册成功</h2>
|
||||
<h3>恭喜您获得1天体验会员,100uv访问量</h3>
|
||||
</div>
|
||||
<div style="text-align: center">
|
||||
<img class="margin-b-20" src="@/assets/home/success.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align: right; margin-right: 30px">
|
||||
<el-button type="primary" @click="tiyan">立即体验</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<el-dialog :title="notify.title" :show-close="true" :width="700" append-to-body v-model="noticeShow">
|
||||
<div class="notify">
|
||||
<div class="notify" v-html="notify.content"></div>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
:row-key="state.primaryKey"
|
||||
>
|
||||
<el-table-column prop="id" label="ID" width="60"></el-table-column>
|
||||
<el-table-column prop="username" label="手机号"></el-table-column>
|
||||
<el-table-column prop="username" label="账号"></el-table-column>
|
||||
<el-table-column prop="created_at" label="注册时间"></el-table-column>
|
||||
|
||||
<template #empty>
|
||||
|
||||
@@ -99,15 +99,6 @@ const commissionShow = (user_id: any) => {
|
||||
>
|
||||
<el-table-column prop="id" label="ID" width="60"></el-table-column>
|
||||
<el-table-column prop="username" label="手机号" width="120"> </el-table-column>
|
||||
<el-table-column prop="agent_package.name" label="当前套餐"> </el-table-column>
|
||||
<!--<el-table-column prop="agent_level.name" label="级别">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link :icon="Edit" @click="changeForm(row, 'level_id')">
|
||||
<span v-if="row.agent_level && row.agent_level.name">{{ row.agent_level.name }}</span>
|
||||
<span v-else></span>
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>-->
|
||||
<el-table-column prop="credit" label="剩余积分">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link :icon="Edit" @click="changeForm(row, 'credit')">
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import useCurd from '@/hooks/curd'
|
||||
|
||||
const { state, resetInput, research, sizeChangeHandle, currentChangeHandle } = useCurd({
|
||||
url: '/payments'
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card>
|
||||
<div class="header">
|
||||
<div class="header-left">
|
||||
<el-form>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="3">
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="state.queryForm.status" placeholder="请选择">
|
||||
<el-option key="1" label="已下单" value="1"></el-option>
|
||||
<el-option key="2" label="已支付" value="2"></el-option>
|
||||
<el-option key="3" label="已取消" value="3"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="3">
|
||||
<el-form-item label="付款人">
|
||||
<el-input
|
||||
v-model="state.queryForm.payer_username"
|
||||
placeholder="输入付款人"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<el-form-item label="下单时间">
|
||||
<el-date-picker
|
||||
v-model="state.queryForm.created_at"
|
||||
placeholder="下单时间"
|
||||
type="daterange"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<el-form-item label="支付时间">
|
||||
<el-date-picker
|
||||
v-model="state.queryForm.success_at"
|
||||
placeholder="支付时间"
|
||||
type="daterange"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<el-button type="primary" @click="research">搜索</el-button>
|
||||
<el-button @click="resetInput">重置</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
<el-table
|
||||
:data="state.dataList"
|
||||
:header-cell-style="{
|
||||
'text-align': 'center',
|
||||
'background-color': '#F1F1F1',
|
||||
height: '50px',
|
||||
color: '#333'
|
||||
}"
|
||||
:cell-style="{ padding: '0', 'text-align': 'center', height: '50px', color: '#333' }"
|
||||
v-loading="state.listLoading"
|
||||
:row-key="state.primaryKey"
|
||||
>
|
||||
<el-table-column prop="id" label="ID"></el-table-column>
|
||||
<el-table-column prop="no" label="编号" width="200"></el-table-column>
|
||||
<el-table-column prop="status" label="状态">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.status === 1">已下单</el-tag>
|
||||
<el-tag v-if="row.status === 2" type="success" effect="dark">已支付</el-tag>
|
||||
<el-tag v-if="row.status === 3" type="info" effect="dark">已取消</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="job" label="套餐">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.job === 'App\\Jobs\\PayAgent'" type="warning">
|
||||
{{ state.meta && state.meta['agent'][row.attach.agent_id] }}
|
||||
</el-tag>
|
||||
<el-tag v-if="row.job === 'App\\Jobs\\PayVip' && row.attach.vip_id" type="success">
|
||||
{{ state.meta && state.meta['vip'][row.attach.vip_id] }}
|
||||
</el-tag>
|
||||
<el-tag v-if="row.job === 'App\\Jobs\\PayCredit'" type="danger">充值积分</el-tag>
|
||||
<span v-else></span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="amount" label="金额"> </el-table-column>
|
||||
<el-table-column prop="payer.username" label="付款人">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.payer && row.payer.username"> {{ row.payer.username }}</span>
|
||||
<span v-else style="color: #999">{{ row.attach.username }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="payer.type" label="付款人类型">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.payer && row.payer.type === 1">会员</el-tag>
|
||||
<el-tag v-if="row.payer && row.payer.type === 2" type="warning">代理</el-tag>
|
||||
<el-tag v-if="row.payer && row.payer.type === 3">管理员</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="success_at" label="支付时间"></el-table-column>
|
||||
<el-table-column prop="created_at" label="下单时间"></el-table-column>
|
||||
<template #empty>
|
||||
<el-empty></el-empty>
|
||||
</template>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<el-pagination
|
||||
:current-page="state.page"
|
||||
:total="state.total"
|
||||
:page-sizes="state.pageSizes"
|
||||
:page-size="state.limit"
|
||||
background
|
||||
layout="total, sizes, prev, pager, next"
|
||||
@size-change="sizeChangeHandle"
|
||||
@current-change="currentChangeHandle"
|
||||
/>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.header-left {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-form) {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,69 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import { ApiSaveSet } from '@/api/comment'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { configStore } from '@/stores'
|
||||
|
||||
const props = defineProps<{
|
||||
data: any
|
||||
}>()
|
||||
const webFormData = ref({
|
||||
wechat_pay_app_id: '',
|
||||
wechat_pay_mch_id: '',
|
||||
wechat_pay_secret_key: '',
|
||||
wechat_pay_private_cert: '',
|
||||
wechat_pay_certificate: ''
|
||||
})
|
||||
watch(
|
||||
() => props.data,
|
||||
() => {
|
||||
webFormData.value.wechat_pay_app_id = props.data.wechat_pay_app_id
|
||||
webFormData.value.wechat_pay_mch_id = props.data.wechat_pay_mch_id
|
||||
webFormData.value.wechat_pay_secret_key = props.data.wechat_pay_secret_key
|
||||
webFormData.value.wechat_pay_private_cert = props.data.wechat_pay_private_cert
|
||||
webFormData.value.wechat_pay_certificate = props.data.wechat_pay_certificate
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
const clickSubmit = () => {
|
||||
ApiSaveSet(webFormData.value).then(() => {
|
||||
ElMessage.success('保存成功')
|
||||
configStore.refreshConfig()
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card>
|
||||
<div style="width: 500px">
|
||||
<div>
|
||||
<div class="margin-b-10" style="margin-top: 20px">微信商户appId</div>
|
||||
<el-input v-model="webFormData.wechat_pay_app_id" placeholder="请输入微信商户appId"></el-input>
|
||||
</div>
|
||||
<div>
|
||||
<div class="margin-b-10" style="margin-top: 20px">微信商户号</div>
|
||||
<el-input v-model="webFormData.wechat_pay_mch_id" placeholder="请输入微信商户号"></el-input>
|
||||
</div>
|
||||
<div>
|
||||
<div class="margin-b-10" style="margin-top: 20px">微信支付密钥</div>
|
||||
<el-input v-model="webFormData.wechat_pay_secret_key" placeholder="请输入微信支付密钥"></el-input>
|
||||
</div>
|
||||
<div>
|
||||
<div class="margin-b-10" style="margin-top: 20px">微信支付私钥证书</div>
|
||||
<el-input v-model="webFormData.wechat_pay_private_cert" placeholder="请输入微信支付私钥证书"></el-input>
|
||||
</div>
|
||||
<div>
|
||||
<div class="margin-b-10" style="margin-top: 20px">微信支付公钥证书</div>
|
||||
<el-input v-model="webFormData.wechat_pay_certificate" placeholder="请输入微信支付公钥证书"></el-input>
|
||||
</div>
|
||||
<div class="m-b-20 text-center" style="margin-top: 60px">
|
||||
<el-button type="primary" size="large" @click="clickSubmit">保存</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
@@ -1,69 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { ApiVipList } from '@/api/super'
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import { ApiSaveSet } from '@/api/comment'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { configStore } from '@/stores'
|
||||
|
||||
const props = defineProps<{
|
||||
data: any
|
||||
}>()
|
||||
const formData = ref({
|
||||
is_give_vip: false,
|
||||
give_vip_id: undefined,
|
||||
give_vip_days: 0,
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.data,
|
||||
() => {
|
||||
formData.value.is_give_vip = props.data.is_give_vip
|
||||
formData.value.give_vip_id = props.data.give_vip_id
|
||||
formData.value.give_vip_days = props.data.give_vip_days
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
const vipPackages = ref<any>([])
|
||||
const clickSubmit = () => {
|
||||
ApiSaveSet(formData.value).then(() => {
|
||||
ElMessage.success('保存成功')
|
||||
configStore.refreshConfig()
|
||||
})
|
||||
}
|
||||
onMounted(() => {
|
||||
ApiVipList().then((res: any) => {
|
||||
vipPackages.value = vipPackages.value.concat(res)
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card>
|
||||
<div style="width: 500px">
|
||||
<div>
|
||||
<div class="margin-b-10" style="margin-top: 20px">是否开启赠送会员</div>
|
||||
<el-switch style="width: 100%" v-model="formData.is_give_vip"></el-switch>
|
||||
</div>
|
||||
<div>
|
||||
<div class="margin-b-10" style="margin-top: 20px">注册赠送会员</div>
|
||||
|
||||
<el-select v-model="formData.give_vip_id" placeholder="注册赠送会员" style="width: 100%">
|
||||
<el-option v-for="vip in vipPackages" :key="vip.id" :label="vip.name" :value="vip.id" />
|
||||
</el-select>
|
||||
</div>
|
||||
<div>
|
||||
<div class="margin-b-10" style="margin-top: 20px">注册赠送天数</div>
|
||||
<el-input-number style="width: 100%" v-model="formData.give_vip_days" :min="1" :max="365" />
|
||||
</div>
|
||||
<div class="m-b-20 text-center" style="margin-top: 60px">
|
||||
<el-button type="primary" size="large" @click="clickSubmit">保存</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
@@ -4,8 +4,6 @@ import { ApiGetSetALL } from '@/api/comment'
|
||||
|
||||
import Web from '@/views/super/setting/components/web.vue'
|
||||
import CodeSetUp from '@/views/super/setting/components/codeSetUp.vue'
|
||||
import Pay from '@/views/super/setting/components/pay.vue'
|
||||
import Sales from '@/views/super/setting/components/sales.vue'
|
||||
|
||||
const formData = ref<any>({})
|
||||
|
||||
@@ -27,12 +25,6 @@ const tab = ref('web')
|
||||
<el-tab-pane label="验证码配置" name="code">
|
||||
<CodeSetUp :data="formData"></CodeSetUp>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="支付配置" name="pay">
|
||||
<Pay :data="formData"></Pay>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="注册赠送会员" name="sales">
|
||||
<Sales :data="formData"></Sales>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
@@ -104,13 +104,6 @@ const vipPackageChange = (data: any, detail?: any) => {
|
||||
<el-tag v-if="row.type == 3" type="danger">管理员</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="vip_package.name" label="当前套餐">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link :icon="Edit" @click="changeForm(row, 'vip_id')">
|
||||
{{ row.vip_package && row.vip_package.name }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="start_at" label="生效时间"></el-table-column>
|
||||
<el-table-column prop="end_at" label="过期时间">
|
||||
<template #default="{ row }">
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import useForm from '@/hooks/form'
|
||||
import { ref } from 'vue'
|
||||
import type { vipConfig } from '@/models/link'
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const { formData, formLoading } = useForm({
|
||||
formRef,
|
||||
defVal(detail) {
|
||||
return {
|
||||
name: detail?.name, // 套餐名称
|
||||
price: detail?.price ? detail?.price : 0, // 价格
|
||||
level: detail?.level ? detail?.level : 0, // 等级
|
||||
config: detail?.config
|
||||
? detail?.config
|
||||
: ({
|
||||
pre_min: false,
|
||||
min_disabled_check: false,
|
||||
support: false,
|
||||
cur_index: false,
|
||||
allow_type: {
|
||||
MINI_PROGRAM: false,
|
||||
KING_DOC: false,
|
||||
CLI_QR: false,
|
||||
WORK_WECHAT: false,
|
||||
LANDING_MINI: false,
|
||||
QR_QQ: false
|
||||
}
|
||||
} as vipConfig) // 权限配置
|
||||
}
|
||||
}
|
||||
})
|
||||
// 表单校验
|
||||
const rules: FormRules = {
|
||||
name: [{ required: true, message: '请输入套餐名称', trigger: 'blur' }],
|
||||
price: [{ required: true, message: '请输入套餐价格', trigger: 'blur' }],
|
||||
level: [{ required: true, message: '请填写等级', trigger: 'blur' }]
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-form :model="formData" :rules="rules" ref="formRef" v-loading="formLoading" label-width="180px">
|
||||
<el-form-item prop="name" label="套餐名称">
|
||||
<el-input v-model="formData.name" placeholder="输入套餐名称"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="price" label="价格/月">
|
||||
<el-input v-model="formData.price" placeholder="0.00"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="level" label="等级">
|
||||
<el-input-number v-model="formData.level" placeholder="0"></el-input-number>
|
||||
</el-form-item>
|
||||
<h3>权限配置</h3>
|
||||
<el-divider></el-divider>
|
||||
<el-form-item :rules="[{ required: true, message: '请输入链接数量限制', trigger: 'blur' }]" label="链接数量限制">
|
||||
<el-input-number v-model="formData.config.count_limit" placeholder="0"></el-input-number>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item-->
|
||||
<!-- :rules="[{ required: true, message: '请输入小圆码数量限制', trigger: 'blur' }]"-->
|
||||
<!-- label="小圆码数量限制"-->
|
||||
<!-- >-->
|
||||
<!-- <el-input-number v-model="formData.config.yuanma_limit" placeholder="0"></el-input-number>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item-->
|
||||
<!-- :rules="[{ required: true, message: '请输入抖音自动回复卡片数量限制', trigger: 'blur' }]"-->
|
||||
<!-- label="抖音自动回复卡片"-->
|
||||
<!-- >-->
|
||||
<!-- <el-input-number v-model="formData.config.douyin_reply_limit" placeholder="0"></el-input-number>-->
|
||||
<!-- </el-form-item>-->
|
||||
<el-form-item :rules="[{ required: true, message: '请输入UV限制', trigger: 'blur' }]" label="UV限制">
|
||||
<el-input-number v-model="formData.config.uv_limit" placeholder="0"></el-input-number>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:rules="[{ required: true, message: '请输入自建小程序池可创建数量', trigger: 'blur' }]"
|
||||
label="自建小程序池可创建数量"
|
||||
>
|
||||
<el-input-number v-model="formData.config.min_count_limit" placeholder="0"></el-input-number>
|
||||
</el-form-item>
|
||||
<el-form-item :rules="[{ required: true, message: '请选择创建链接类型', trigger: 'blur' }]" label="创建链接类型">
|
||||
<el-checkbox v-model="formData.config.allow_type.MINI_PROGRAM" :label="true">跳转到小程序</el-checkbox>
|
||||
<el-checkbox v-model="formData.config.allow_type.LANDING_MINI" :label="true">跳转到微信二维码</el-checkbox>
|
||||
<el-checkbox v-model="formData.config.allow_type.WORK_WECHAT" :label="true">跳转到企业微信</el-checkbox>
|
||||
<el-checkbox v-model="formData.config.allow_type.KING_DOC" :label="true">跳转到金山文档</el-checkbox>
|
||||
<el-checkbox v-model="formData.config.allow_type.CLI_QR" :label="true">跳转到草料二维码</el-checkbox>
|
||||
<el-checkbox v-model="formData.config.allow_type.QR_QQ" :label="true">跳转到腾讯优码</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item :rules="[{ required: true, message: '请选择链接保留时长', trigger: 'blur' }]" label="链接保留时长">
|
||||
<el-checkbox :model-value="true" disabled>永久</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:rules="[{ required: true, message: '请选择官方小程序池使用', trigger: 'blur' }]"
|
||||
label="官方小程序池使用"
|
||||
>
|
||||
<el-checkbox v-model="formData.config.pre_min" :label="true">支持</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:rules="[{ required: true, message: '请选择自建小程序池下架自动检测', trigger: 'blur' }]"
|
||||
label="自建小程序池下架自动检测"
|
||||
>
|
||||
<el-checkbox v-model="formData.config.min_disabled_check" :label="true">支持</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item :rules="[{ required: true, message: '请选择免费技术协助', trigger: 'blur' }]" label="免费技术协助">
|
||||
<el-checkbox v-model="formData.config.support" :label="true">支持</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item :rules="[{ required: true, message: '请选择数据统计/分析', trigger: 'blur' }]" label="数据统计/分析">
|
||||
<el-checkbox :model-value="true" disabled>支持</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item :rules="[{ required: true, message: '请选择自定义落地页', trigger: 'blur' }]" label="自定义落地页">
|
||||
<el-checkbox v-model="formData.config.cur_index" :label="true">支持</el-checkbox>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
@@ -1,120 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { Plus, Edit } from '@element-plus/icons-vue'
|
||||
import useCurd from '@/hooks/curd'
|
||||
import FormModal from '@/components/FormModal.vue'
|
||||
import { ref } from 'vue'
|
||||
import CreateOrLook from '@/views/super/vipSetMeal/components/create-or-look.vue'
|
||||
|
||||
const {
|
||||
state,
|
||||
|
||||
showForm,
|
||||
submitForm,
|
||||
deleteHandle,
|
||||
query,
|
||||
inlineEdit
|
||||
} = useCurd({
|
||||
url: '/vip-packages',
|
||||
openPage: false
|
||||
})
|
||||
|
||||
// 行内编辑
|
||||
// 表格排序switch和按钮的切换
|
||||
const isShow = () => {
|
||||
flag.value = false
|
||||
}
|
||||
// 实现表格排序及数值变化
|
||||
const flag = ref(true)
|
||||
const handleSort = async (row: any) => {
|
||||
await inlineEdit(row.id, 'level', row.level)
|
||||
await query()
|
||||
flag.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card>
|
||||
<div class="header-right">
|
||||
<el-button type="primary" :icon="Plus" @click="showForm">新增套餐</el-button>
|
||||
</div>
|
||||
<!-- 表格 -->
|
||||
<el-table
|
||||
:data="state.dataList"
|
||||
:header-cell-style="{
|
||||
'text-align': 'center',
|
||||
'background-color': '#F1F1F1',
|
||||
height: '50px',
|
||||
color: '#333'
|
||||
}"
|
||||
:cell-style="{ padding: '0', 'text-align': 'center', height: '50px', color: '#333' }"
|
||||
v-loading="state.listLoading"
|
||||
:row-key="state.primaryKey"
|
||||
>
|
||||
<el-table-column prop="name" label="套餐名称"></el-table-column>
|
||||
<el-table-column prop="price" label="价格/月"> </el-table-column>
|
||||
<el-table-column prop="level" label="等级">
|
||||
<template #default="{ row }">
|
||||
<div v-if="flag === false">
|
||||
<el-input-number v-model="row.level"></el-input-number>
|
||||
<el-button type="primary" link @click="flag = true">取消</el-button>
|
||||
<el-button type="primary" link @click="handleSort(row)">确认</el-button>
|
||||
</div>
|
||||
<el-button v-else type="primary" link :icon="Edit" @click="isShow">
|
||||
{{ row.level }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="权限配置">
|
||||
<template #default="{ row }">
|
||||
<div>链接数量:{{ row.config?.count_limit }}</div>
|
||||
<div>UV限制:{{ row.config?.uv_limit }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="created_at" label="创建时间"> </el-table-column>
|
||||
<el-table-column prop="" label="操作" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="showForm(row)">编辑</el-button>
|
||||
<el-button type="primary" link @click="deleteHandle(row.id)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template #empty>
|
||||
<el-empty></el-empty>
|
||||
</template>
|
||||
</el-table>
|
||||
</el-card>
|
||||
<!-- 添加标签 -->
|
||||
<form-modal
|
||||
v-if="state.formVisible === true"
|
||||
v-model:visible="state.formVisible"
|
||||
:loading="state.formLoading"
|
||||
:detail="state.detailData"
|
||||
@submit="submitForm"
|
||||
type="dialog"
|
||||
width="1000"
|
||||
:title="state.detailData?.id ? '编辑' : '新增'"
|
||||
cancelBtnName="取消"
|
||||
okBtnName="确定"
|
||||
>
|
||||
<create-or-look></create-or-look>
|
||||
</form-modal>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.top_card {
|
||||
margin-bottom: 20px;
|
||||
height: 72px;
|
||||
}
|
||||
//.header {
|
||||
// display: flex;
|
||||
// justify-content: space-between;
|
||||
// .header-left {
|
||||
// flex: 1;
|
||||
// }
|
||||
//}
|
||||
.header-right {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
:deep(.el-form) {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
</style>
|
||||
@@ -67,21 +67,6 @@ class SystemInit extends Command
|
||||
'value' => '',
|
||||
'desc' => '阿里短信签名',
|
||||
],
|
||||
[
|
||||
'slug' => 'give_vip_days',
|
||||
'value' => '3',
|
||||
'desc' => '注册赠送会员有效期/天',
|
||||
],
|
||||
[
|
||||
'slug' => 'give_vip_id',
|
||||
'value' => '1',
|
||||
'desc' => '赠送套餐',
|
||||
],
|
||||
[
|
||||
'slug' => 'is_give_vip',
|
||||
'value' => '1',
|
||||
'desc' => '开启注册赠送会员',
|
||||
],
|
||||
[
|
||||
'slug' => 'mail_from_address',
|
||||
'value' => '',
|
||||
@@ -142,31 +127,6 @@ class SystemInit extends Command
|
||||
'value' => '蚂蚁快链-智慧引流工具',
|
||||
'desc' => '网站名称',
|
||||
],
|
||||
[
|
||||
'slug' => 'wechat_pay_app_id',
|
||||
'value' => '',
|
||||
'desc' => '微信商户appId',
|
||||
],
|
||||
[
|
||||
'slug' => 'wechat_pay_certificate',
|
||||
'value' => '',
|
||||
'desc' => '微信支付公钥证书',
|
||||
],
|
||||
[
|
||||
'slug' => 'wechat_pay_mch_id',
|
||||
'value' => '',
|
||||
'desc' => '微信商户号',
|
||||
],
|
||||
[
|
||||
'slug' => 'wechat_pay_private_cert',
|
||||
'value' => '',
|
||||
'desc' => '微信支付私钥证书',
|
||||
],
|
||||
[
|
||||
'slug' => 'wechat_pay_secret_key',
|
||||
'value' => '',
|
||||
'desc' => '微信支付密钥',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Enums\VipStatus;
|
||||
use App\Models\User;
|
||||
use App\Models\VipLogs;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class VipExpired extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'app:vip-expired';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = '处理vip套餐的过期和生效';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
// 已生效的,要过期掉, 未生效的,要生效
|
||||
VipLogs::query()
|
||||
->where(function ($query) {
|
||||
$query->where('status', VipStatus::ACTIVE)
|
||||
->where('end_at', '<=', now());
|
||||
})
|
||||
->orWhere(function ($query) {
|
||||
$query->where('status', VipStatus::PENDING)
|
||||
->where('start_at', '<=', now());
|
||||
})
|
||||
->chunkById(30, function ($logs) {
|
||||
foreach ($logs as $log) {
|
||||
if ($log->status === VipStatus::ACTIVE) {
|
||||
// 生效中的要过期
|
||||
$log->status = VipStatus::EXPIRED;
|
||||
$log->save();
|
||||
User::query()->where('user_id', $log->user_id)->update([
|
||||
'vip_id' => null,
|
||||
'start_at' => null,
|
||||
'end_at' => null,
|
||||
]);
|
||||
} else {
|
||||
// 未生效的,要生效
|
||||
$log->status = VipStatus::ACTIVE;
|
||||
$log->save();
|
||||
User::query()->where('user_id', $log->user_id)->update([
|
||||
'vip_id' => $log->vip_id,
|
||||
'start_at' => $log->start_at,
|
||||
'end_at' => $log->ent_at,
|
||||
]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,6 @@ class Kernel extends ConsoleKernel
|
||||
*/
|
||||
protected function schedule(Schedule $schedule): void
|
||||
{
|
||||
// 会员到期
|
||||
$schedule->command('app:vip-expired')->hourly();
|
||||
// URL状态检测
|
||||
Schedule::command('app:chk-link')->daily();
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum CommissionType: int
|
||||
{
|
||||
// 申请提现
|
||||
case Withdraw = 1;
|
||||
|
||||
// 邀请的代理消费提成
|
||||
case Commission = 2;
|
||||
|
||||
// 推荐返佣
|
||||
case Rebates = 3;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum VipStatus: int
|
||||
{
|
||||
// 会员状态: 1未生效 2已生效 3已过期
|
||||
case PENDING = 1;
|
||||
case ACTIVE = 2;
|
||||
case EXPIRED = 3;
|
||||
}
|
||||
@@ -11,9 +11,6 @@ class BaseConfig implements SimpleForm
|
||||
public function policy(Request $request): array
|
||||
{
|
||||
return $request->validate([
|
||||
'is_give_vip' => 'nullable', // 开启赠送
|
||||
'give_vip_days' => 'nullable|integer|min:0|max:365', //赠送天数
|
||||
'give_vip_id' => 'nullable|integer', //赠送套餐
|
||||
'recommend_commission' => 'nullable|integer', // 推荐佣金
|
||||
|
||||
'web_site_customer_service' => 'nullable',
|
||||
@@ -34,12 +31,6 @@ class BaseConfig implements SimpleForm
|
||||
'mail_from_address' => 'nullable',
|
||||
'mail_username' => 'nullable',
|
||||
'mail_password' => 'nullable',
|
||||
|
||||
'wechat_pay_app_id' => 'nullable',
|
||||
'wechat_pay_mch_id' => 'nullable',
|
||||
'wechat_pay_secret_key' => 'nullable',
|
||||
'wechat_pay_private_cert' => 'nullable',
|
||||
'wechat_pay_certificate' => 'nullable',
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -54,10 +45,6 @@ class BaseConfig implements SimpleForm
|
||||
public function default(): array
|
||||
{
|
||||
return [
|
||||
'is_give_vip' => (bool) SystemConfig::get('is_give_vip'),
|
||||
'give_vip_id' => SystemConfig::get('give_vip_id'),
|
||||
'give_vip_days' => SystemConfig::get('give_vip_days'),
|
||||
|
||||
'web_site_customer_service' => SystemConfig::get('web_site_customer_service'),
|
||||
'web_site_title' => SystemConfig::get('web_site_title'),
|
||||
'web_site_logo' => SystemConfig::get('web_site_logo'),
|
||||
@@ -76,12 +63,6 @@ class BaseConfig implements SimpleForm
|
||||
'mail_from_address' => SystemConfig::get('mail_from_address'),
|
||||
'mail_username' => SystemConfig::get('mail_username'),
|
||||
'mail_password' => SystemConfig::get('mail_password'),
|
||||
|
||||
'wechat_pay_app_id' => SystemConfig::get('wechat_pay_app_id'),
|
||||
'wechat_pay_mch_id' => SystemConfig::get('wechat_pay_mch_id'),
|
||||
'wechat_pay_secret_key' => SystemConfig::get('wechat_pay_secret_key'),
|
||||
'wechat_pay_private_cert' => SystemConfig::get('wechat_pay_secret_key'), // 私钥证书
|
||||
'wechat_pay_certificate' => SystemConfig::get('wechat_pay_certificate'), // 公钥证书
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,23 +3,16 @@
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Enums\UserType;
|
||||
use App\Enums\VipStatus;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ChangePasswordRequest;
|
||||
use App\Http\Requests\LoginRequest;
|
||||
use App\Http\Requests\RegisterRequest;
|
||||
use App\Http\Requests\ResetPasswordRequest;
|
||||
use App\Http\Resources\UserInfoResource;
|
||||
use App\Jobs\PayVip;
|
||||
use App\Models\Link;
|
||||
use App\Models\User;
|
||||
use App\Models\VipLogs;
|
||||
use App\Models\VipPackage;
|
||||
use App\PayChannels\WeChatPayNative;
|
||||
use App\Services\SystemConfig;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Ugly\Base\Models\Payment;
|
||||
use Ugly\Base\Traits\ApiResource;
|
||||
|
||||
class AuthController extends Controller
|
||||
@@ -58,26 +51,7 @@ class AuthController extends Controller
|
||||
$data['parent_id'] = $agent->id;
|
||||
}
|
||||
// 会员注册
|
||||
$user = User::query()->create($data);
|
||||
|
||||
if (SystemConfig::get('is_give_vip')) {
|
||||
// 赠送体验会员
|
||||
$vip_days = (int) SystemConfig::get('give_vip_days');
|
||||
$vip_id = (int) SystemConfig::get('give_vip_id');
|
||||
if ($vip_days && $vip_id) {
|
||||
$log = VipLogs::query()->create([
|
||||
'user_id' => $user->id,
|
||||
'vip_id' => $vip_id,
|
||||
'status' => VipStatus::ACTIVE,
|
||||
'start_at' => now(),
|
||||
'end_at' => now()->addDays($vip_days),
|
||||
]);
|
||||
$user->vip_id = $vip_id;
|
||||
$user->start_at = $log->start_at;
|
||||
$user->end_at = $log->end_at;
|
||||
$user->save();
|
||||
}
|
||||
}
|
||||
User::query()->create($data);
|
||||
|
||||
return $this->success();
|
||||
}
|
||||
@@ -106,52 +80,15 @@ class AuthController extends Controller
|
||||
{
|
||||
$user = $request->user('api');
|
||||
if ($user->type === UserType::MEMBER) {
|
||||
$user->load('vipPackage:id,name,config');
|
||||
$user->link_amount = $user->link_total = $user->link_created = 0;
|
||||
$config = $user->vipPackage['config'] ?? null;
|
||||
if ($config) {
|
||||
$link_count = Link::query()->where('user_id', $user->id)->count();
|
||||
$user->link_total = (int) $config['count_limit'];
|
||||
$user->link_created = $link_count;
|
||||
$user->link_amount = (int) bcsub($config['count_limit'], $link_count);
|
||||
}
|
||||
|
||||
$link_count = Link::query()->where('user_id', $user->id)->count();
|
||||
$link_expire = Link::query()->where('user_id', $user->id)->where('expired_at', '<', now())->count();
|
||||
$user->link_created = $link_count;
|
||||
$user->link_expire = $link_expire;
|
||||
}
|
||||
$user->login_time = $user->currentAccessToken()->created_at;
|
||||
|
||||
return $this->success(UserInfoResource::make($user));
|
||||
}
|
||||
|
||||
// 购买会员
|
||||
public function buyVip(Request $request): JsonResponse
|
||||
{
|
||||
$user = $request->user('api');
|
||||
$vip = VipPackage::query()->findOrFail($request->input('vip_id'));
|
||||
|
||||
if (SystemConfig::get('wechat_pay_app_id') == null ||
|
||||
SystemConfig::get('wechat_pay_mch_id') == null ||
|
||||
SystemConfig::get('wechat_pay_secret_key') == null ||
|
||||
SystemConfig::get('wechat_pay_secret_key') == null ||
|
||||
SystemConfig::get('wechat_pay_certificate') == null) {
|
||||
return $this->failed('清设置支付信息');
|
||||
}
|
||||
try {
|
||||
$res = Payment::pay(
|
||||
channel: WeChatPayNative::class,
|
||||
amount: $vip->price,
|
||||
job: PayVip::class,
|
||||
attach: [
|
||||
'vip_id' => $vip->id,
|
||||
// 生效类型:1-立即生效 2-延期生效
|
||||
'type' => $request->integer('type', 1),
|
||||
],
|
||||
payer: $user
|
||||
)->send(['description' => '购买会员']);
|
||||
|
||||
return $this->success($res);
|
||||
} catch (\Exception $e) {
|
||||
info($e->getMessage());
|
||||
|
||||
return $this->failed('支付失败!');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,22 +4,16 @@ namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Enums\UserType;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Jobs\PayVip;
|
||||
use App\Models\Domain;
|
||||
use App\Models\Link;
|
||||
use App\Models\LinkVisitLog;
|
||||
use App\Models\MiniProgram;
|
||||
use App\Models\Notice;
|
||||
use App\Models\User;
|
||||
use App\Models\VipLogs;
|
||||
use App\Models\VipPackage;
|
||||
use App\Services\SystemConfig;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Ugly\Base\Enums\PaymentStatus;
|
||||
use Ugly\Base\Models\Payment;
|
||||
use Ugly\Base\Traits\ApiResource;
|
||||
|
||||
class IndexController extends Controller
|
||||
@@ -32,15 +26,31 @@ class IndexController extends Controller
|
||||
$user = auth('api')->user();
|
||||
// 超级管理的首页统计
|
||||
if ($user->type === UserType::Admin) {
|
||||
$res['user_count'] = (string) User::query()->where('type', UserType::MEMBER)->count();
|
||||
$res['pay_count'] = User::query()->where('type', UserType::MEMBER)->whereNotNull('vip_id')->count();
|
||||
$res['pay_vip'] = bcdiv(Payment::query()
|
||||
->where('status', PaymentStatus::Success)
|
||||
->where('job', PayVip::class)
|
||||
->sum('amount'), 100, 2);
|
||||
|
||||
// 用户增长曲线 按月统计
|
||||
$type = $request->integer('type', 1); // 1 最近30天;2 最近12个月
|
||||
|
||||
$res['user_count'] = User::query()
|
||||
->where('type', UserType::MEMBER)
|
||||
->where('created_at', '>=',
|
||||
$type === 1 ?
|
||||
now()->subDays(30)->startOfDay()
|
||||
: now()->subYear()->startOfDay()
|
||||
)
|
||||
->count();
|
||||
$res['card_count'] = Link::query()
|
||||
->where('created_at', '>=',
|
||||
$type === 1 ?
|
||||
now()->subDays(30)->startOfDay()
|
||||
: now()->subYear()->startOfDay()
|
||||
)
|
||||
->count();
|
||||
$res['uv_count'] = LinkVisitLog::query()
|
||||
->where('created_at', '>=',
|
||||
$type === 1 ?
|
||||
now()->subDays(30)->startOfDay()
|
||||
: now()->subYear()->startOfDay()
|
||||
)
|
||||
->count();
|
||||
$res['user_growth'] = User::query()
|
||||
->select([
|
||||
DB::raw('DATE_FORMAT(`created_at`, '.($type === 1 ? "'%Y-%m-%d'" : "'%Y-%m'").') AS date'),
|
||||
@@ -62,39 +72,14 @@ class IndexController extends Controller
|
||||
|
||||
// 会员的统计
|
||||
if ($user->type === UserType::MEMBER) {
|
||||
$vip = VipPackage::query()->find($user->vip_id);
|
||||
$res = [];
|
||||
$res['link_count'] = Link::query()->where('user_id', $user->id)->count();
|
||||
|
||||
if ($vip) {
|
||||
$res['used_uv'] = LinkVisitLog::query()
|
||||
->where('user_id', $user->id)
|
||||
->when($user->start_at, function ($query, $start_at) {
|
||||
$query->where('created_at', '>=', $start_at);
|
||||
})
|
||||
->when($user->end_at, function ($query, $end_at) {
|
||||
$query->where('created_at', '>=', $end_at);
|
||||
})
|
||||
->groupBy('device_uid')
|
||||
->count();
|
||||
}
|
||||
$res['vip_logs'] = VipLogs::with(['payment', 'vipPackage'])
|
||||
$res['used_uv'] = LinkVisitLog::query()
|
||||
->where('user_id', $user->id)
|
||||
->orderByDesc('id')
|
||||
->limit(30)
|
||||
->get()->transform(function ($item) {
|
||||
return [
|
||||
'id' => $item->id,
|
||||
'amount' => $item->payment?->amount ?: 0,
|
||||
'start_at' => $item->start_at,
|
||||
'end_at' => $item->end_at,
|
||||
'status' => $item->status,
|
||||
'vip_package' => [
|
||||
'id' => $item->vipPackage?->id,
|
||||
'name' => $item->vipPackage?->name,
|
||||
],
|
||||
];
|
||||
});
|
||||
->count();
|
||||
$res['month_uv'] = LinkVisitLog::query()
|
||||
->where('user_id', $user->id)
|
||||
->whereBetween('created_at', [now()->startOfMonth(), now()])
|
||||
->count();
|
||||
|
||||
$res['notices'] = Notice::query()
|
||||
->where('type', UserType::MEMBER)
|
||||
@@ -114,7 +99,6 @@ class IndexController extends Controller
|
||||
$configs = [
|
||||
'code_mode' => SystemConfig::get('send_code_mode'),
|
||||
'verify_code_is_open' => SystemConfig::get('verify_code_is_open'),
|
||||
'package' => VipPackage::query()->orderBy('level')->get(['id', 'name', 'price', 'config']),
|
||||
'web_site_customer_service' => SystemConfig::get('web_site_customer_service'),
|
||||
'web_site_title' => SystemConfig::get('web_site_title'),
|
||||
'web_site_logo' => SystemConfig::get('web_site_logo'),
|
||||
@@ -130,7 +114,6 @@ class IndexController extends Controller
|
||||
$configs['mini_programs'] = MiniProgram::query()
|
||||
->where(
|
||||
fn ($query) => $query->where('user_id', $user->id)
|
||||
->when($user->getPackageConfig('pre_min'), fn ($query) => $query->orWhere('is_pre_min', true))
|
||||
)
|
||||
->get(['id', 'name']);
|
||||
}
|
||||
|
||||
@@ -4,14 +4,12 @@ namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Enums\LinkType;
|
||||
use App\Enums\SwitchType;
|
||||
use App\Enums\UserType;
|
||||
use App\Enums\UVLimitType;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Link;
|
||||
use App\Models\LinkVisitLog;
|
||||
use App\Models\MiniProgram;
|
||||
use App\Models\User;
|
||||
use App\Models\VipPackage;
|
||||
use EasyWeChat\MiniApp\Application as WechatApp;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -44,10 +42,6 @@ class JumpController extends Controller
|
||||
if (empty($user->status)) {
|
||||
return $this->failed('账号被禁用!');
|
||||
}
|
||||
$isAdmin = $user->type === UserType::Admin;
|
||||
if (! $isAdmin && ! $user->vip_id) {
|
||||
return $this->failed('会员已到期,禁止访问!');
|
||||
}
|
||||
|
||||
$device_uid = $request->input('device_uid') ?: Str::uuid();
|
||||
$cache = data_get(LinkVisitLog::query()
|
||||
@@ -56,20 +50,6 @@ class JumpController extends Controller
|
||||
->orderByDesc('id')
|
||||
->first(), 'cache', []);
|
||||
if (empty($cache)) {
|
||||
if (! $isAdmin) {
|
||||
// 会员UV限制
|
||||
$totalUV = LinkVisitLog::query()
|
||||
->where('link_id', $link->id)
|
||||
->groupBy('device_uid')
|
||||
->whereBetween('created_at', [$user->start_at, $user->end_at])
|
||||
->count();
|
||||
$limit = (int) data_get(VipPackage::query()->find($user->vip_id), 'config.uv_limit');
|
||||
|
||||
if ($limit > 0 && $totalUV >= $limit) {
|
||||
return $this->failed('当前访问已达上限!');
|
||||
}
|
||||
}
|
||||
|
||||
$cache = [
|
||||
'title' => $link->title,
|
||||
'description' => $link->description,
|
||||
|
||||
@@ -64,36 +64,6 @@ class LinkController extends FormController
|
||||
if ($user->type === UserType::Admin) {
|
||||
return true;
|
||||
}
|
||||
if ($form->isCreate()) {
|
||||
$config = data_get($user->getPackageConfig(), 'config');
|
||||
|
||||
// 类型限制
|
||||
$typeName = LinkType::from((int) request()->input('type'))->name;
|
||||
if (! data_get($config, 'allow_type.'.$typeName)) {
|
||||
return '当前套餐不支持该类型链接!';
|
||||
}
|
||||
|
||||
// 会员,数量限制
|
||||
if (
|
||||
$user->type === UserType::MEMBER &&
|
||||
data_get($config, 'count_limit') <= Link::query()->where('user_id', $user->id)->count()
|
||||
) {
|
||||
return '拥有的链接数量已达上限!';
|
||||
}
|
||||
|
||||
// 是否允许自定义落地页
|
||||
$inputConfig = request()->input('config');
|
||||
if (
|
||||
! data_get($config, 'cur_index') &&
|
||||
(
|
||||
data_get($inputConfig, 'wx.avatar') ||
|
||||
data_get($inputConfig, 'wx.title') ||
|
||||
data_get($inputConfig, 'wx.sub_title')
|
||||
)
|
||||
) {
|
||||
return '当前套餐不允许自定义落地页!';
|
||||
}
|
||||
}
|
||||
|
||||
return $form->isCreate() || $form->getModel()->user_id === $user->id;
|
||||
});
|
||||
|
||||
@@ -16,22 +16,12 @@ class MiniProgramController extends FormController
|
||||
public function home(): JsonResponse
|
||||
{
|
||||
$user = auth('api')->user();
|
||||
$is_pre_min = false;
|
||||
if ($user->start_at < now() && $user->end_at > now()) {
|
||||
// 套餐包含官方小程序池使用
|
||||
$is_pre_min = $user->getPackageConfig('pre_min');
|
||||
}
|
||||
$query = MiniProgram::search([
|
||||
'name' => 'like',
|
||||
'type' => '=',
|
||||
'is_enable' => '=',
|
||||
], ['user:id,username'])
|
||||
->where(fn ($query) => $query->where('user_id', $user->id)
|
||||
->when(
|
||||
$is_pre_min,
|
||||
fn ($query) => $query->orWhere('is_pre_min', true)
|
||||
)
|
||||
)
|
||||
->where(fn ($query) => $query->where('user_id', $user->id))
|
||||
->orderByDesc('id')
|
||||
->get();
|
||||
|
||||
@@ -74,16 +64,8 @@ class MiniProgramController extends FormController
|
||||
if ($user->type === UserType::Admin) {
|
||||
return true;
|
||||
}
|
||||
if ($form->isCreate()) {
|
||||
$min_count_limit = (int) $user->getPackageConfig('min_count_limit');
|
||||
if ($min_count_limit <= MiniProgram::query()->where('user_id', $user->id)->count()) {
|
||||
return '创建小程序数量已达上限!';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return $form->getModel()->user_id === $user->id;
|
||||
return $form->isCreate() || $form->getModel()->user_id === $user->id;
|
||||
});
|
||||
|
||||
$form->handleInput(function (FormService $formService) use ($user) {
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\PaymentResource;
|
||||
use App\Models\VipPackage;
|
||||
use Ugly\Base\Models\Payment;
|
||||
use Ugly\Base\Traits\ApiResource;
|
||||
|
||||
class PaymentController extends Controller
|
||||
{
|
||||
use ApiResource;
|
||||
|
||||
public function index()
|
||||
{
|
||||
$query = Payment::search([
|
||||
'status' => '=',
|
||||
'payer.username' => 'like',
|
||||
'created_at' => 'between',
|
||||
'success_at' => 'between',
|
||||
], ['payer:id,username,type'])->orderByDesc('id');
|
||||
|
||||
return $this->paginate($query, PaymentResource::class, [
|
||||
'vip' => VipPackage::query()->pluck('name', 'id'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ use App\Enums\UserType;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\UserResource;
|
||||
use App\Models\User;
|
||||
use App\Models\VipPackage;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Ugly\Base\Traits\ApiResource;
|
||||
@@ -20,7 +19,7 @@ class UserController extends Controller
|
||||
$query = User::search([
|
||||
'type' => '=',
|
||||
'username' => 'like',
|
||||
], ['vipPackage:id,name', 'parent'])
|
||||
], ['parent'])
|
||||
->whereIn('type', [UserType::MEMBER])
|
||||
->orderByDesc('id');
|
||||
|
||||
@@ -57,23 +56,6 @@ class UserController extends Controller
|
||||
|
||||
$params = $request->only(['status', 'vip_id', 'end_at', 'credit']);
|
||||
|
||||
$pack_id = $params['vip_id'] ?? null;
|
||||
$model = VipPackage::class;
|
||||
|
||||
$pak = $user->getPackageConfig();
|
||||
if ($pak?->id == $pack_id) {
|
||||
return $this->failed('没有修改');
|
||||
}
|
||||
if ($pack_id) {
|
||||
if (app($model)->where('id', $pack_id)->doesntExist()) {
|
||||
return $this->failed('套餐不存在');
|
||||
}
|
||||
$params['end_at'] = now()->addMonth();
|
||||
if (empty($user->start_at)) {
|
||||
$user->start_at = now();
|
||||
}
|
||||
}
|
||||
|
||||
$user->update($params);
|
||||
|
||||
return $this->success();
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Enums\LinkType;
|
||||
use App\Models\User;
|
||||
use App\Models\VipPackage;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Ugly\Base\Exceptions\ApiCustomError;
|
||||
use Ugly\Base\Http\Controllers\FormController;
|
||||
use Ugly\Base\Services\FormService;
|
||||
|
||||
class VipPackageController extends FormController
|
||||
{
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
return $this->success(VipPackage::query()->get());
|
||||
}
|
||||
|
||||
protected function form(): FormService
|
||||
{
|
||||
$form = FormService::make(VipPackage::class);
|
||||
|
||||
$form->validate(fn (FormService $form) => [
|
||||
'name' => ['required', $form->unique()],
|
||||
'price' => 'required|numeric|min:0',
|
||||
'level' => 'required|integer|min:0',
|
||||
'config' => 'required|array',
|
||||
'config.uv_limit' => 'required|integer|min:0',
|
||||
'config.count_limit' => 'required|integer|min:0', // 链接数量限制
|
||||
'config.min_count_limit' => 'required|integer|min:0',
|
||||
'config.pre_min' => 'required|boolean', // 平台落地小程序
|
||||
'config.min_disabled_check' => 'required|boolean',
|
||||
'config.support' => 'required|boolean',
|
||||
'config.cur_index' => 'required|boolean',
|
||||
'config.allow_type' => 'required_array_keys:'.implode(',', LinkType::getAllType()),
|
||||
'config.allow_type.*' => 'required|boolean',
|
||||
]);
|
||||
$form->deleting(function (FormService $form) {
|
||||
throw_if(
|
||||
User::query()->where('vip_id', $form->getKey())->exists(),
|
||||
new ApiCustomError('该套餐下有未过期的会员,不能删除!')
|
||||
);
|
||||
});
|
||||
|
||||
return $form;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
class JumpController extends Controller
|
||||
class HomeController extends Controller
|
||||
{
|
||||
public function show(string $code): \Illuminate\View\View
|
||||
{
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Enums\UserType;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
@@ -22,10 +21,6 @@ class UserResource extends JsonResource
|
||||
'type' => $this->type,
|
||||
'start_at' => $this->start_at,
|
||||
'end_at' => $this->end_at,
|
||||
// 'credit' => $this->credit,
|
||||
$this->mergeWhen($this->type === UserType::MEMBER, [
|
||||
'vip_package' => $this->vipPackage,
|
||||
]),
|
||||
'parent' => new UserSimpleResource($this->whenLoaded('parent')),
|
||||
'created_at' => $this->created_at?->format('Y-m-d H:i:s'),
|
||||
'login_time' => $this->login_time?->format('Y-m-d H:i:s'),
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Enums\CommissionType;
|
||||
use App\Enums\VipStatus;
|
||||
use App\Enums\WithdrawStatus;
|
||||
use App\Models\VipLogs;
|
||||
use App\Services\DistributorsService;
|
||||
use App\Services\SystemConfig;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class PayVip implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct(protected $payment)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$attach = data_get($this->payment, 'attach', []);
|
||||
$user = $this->payment->payer;
|
||||
$type = data_get($attach, 'type', 1); // 1-立即生效 2-延期生效
|
||||
if ($type === 1) {
|
||||
// 1-立即生效
|
||||
$start_at = now();
|
||||
$status = VipStatus::ACTIVE;
|
||||
} else {
|
||||
// 2-延期生效
|
||||
$info = VipLogs::query()
|
||||
->where('user_id', $user->id)
|
||||
->where('status', [VipStatus::PENDING, VipStatus::ACTIVE])
|
||||
->orderByDesc('end_at')
|
||||
->first();
|
||||
$start_at = $info ? Carbon::parse($info->end_at) : now();
|
||||
$status = VipStatus::PENDING;
|
||||
}
|
||||
|
||||
// 创建记录
|
||||
VipLogs::query()->create([
|
||||
'user_id' => $user->id,
|
||||
'payment_id' => $this->payment->id,
|
||||
'vip_id' => $attach['vip_id'],
|
||||
'status' => $status,
|
||||
'start_at' => $start_at,
|
||||
'end_at' => $start_at->addMonth(),
|
||||
]);
|
||||
if ($status === VipStatus::ACTIVE) {
|
||||
$user->vip_id = $attach['vip_id'];
|
||||
$user->start_at = now();
|
||||
$user->end_at = now()->addMonth();
|
||||
$user->save();
|
||||
}
|
||||
// 一级分销
|
||||
$this->distributors(
|
||||
(int) SystemConfig::get('member_pay_commission_1'),
|
||||
$user,
|
||||
);
|
||||
// 二级分销
|
||||
$this->distributors(
|
||||
(int) SystemConfig::get('member_pay_commission_2'),
|
||||
$user->parent,
|
||||
'二级分销邀请人消费提成',
|
||||
);
|
||||
}
|
||||
|
||||
private function distributors($rate, $user, $title = null): void
|
||||
{
|
||||
if (! empty($rate)) {
|
||||
$commission = bcdiv(bcmul($this->payment->amount, $rate, 4), 100, 4);
|
||||
|
||||
DistributorsService::commission(
|
||||
$commission,
|
||||
$user,
|
||||
[
|
||||
'title' => $title ?? '邀请人消费提成',
|
||||
'type' => CommissionType::Commission,
|
||||
'is_withdraw' => false,
|
||||
'status' => WithdrawStatus::NONE,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,12 +35,6 @@ class User extends Authenticatable
|
||||
});
|
||||
}
|
||||
|
||||
// vip套餐
|
||||
public function vipPackage(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(VipPackage::class, 'vip_id');
|
||||
}
|
||||
|
||||
// 推荐人
|
||||
public function parent(): BelongsTo
|
||||
{
|
||||
@@ -52,21 +46,4 @@ class User extends Authenticatable
|
||||
{
|
||||
return $this->hasMany(self::class, 'parent_id');
|
||||
}
|
||||
|
||||
// 佣金记录
|
||||
public function commissionLogs(): HasMany
|
||||
{
|
||||
return $this->hasMany(CommissionLog::class, 'user_id');
|
||||
}
|
||||
|
||||
// 获取权益配置
|
||||
public function getPackageConfig(?string $key = null)
|
||||
{
|
||||
$package = null;
|
||||
if ($this->vip_id) {
|
||||
$package = $this->vipPackage;
|
||||
}
|
||||
|
||||
return $key ? data_get($package, 'config.'.$key) : $package;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\VipStatus;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Ugly\Base\Models\Payment;
|
||||
|
||||
class VipLogs extends Model
|
||||
{
|
||||
protected $guarded = [];
|
||||
|
||||
protected $casts = [
|
||||
'status' => VipStatus::class,
|
||||
];
|
||||
|
||||
// 对应的payment
|
||||
public function payment(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Payment::class);
|
||||
}
|
||||
|
||||
// 对应的会员套餐
|
||||
public function vipPackage(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(VipPackage::class, 'vip_id');
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Ugly\Base\Casts\Amount;
|
||||
use Ugly\Base\Traits\SerializeDate;
|
||||
|
||||
class VipPackage extends Model
|
||||
{
|
||||
use SerializeDate;
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
protected $casts = [
|
||||
'price' => Amount::class,
|
||||
'config' => 'json',
|
||||
];
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\PayChannels;
|
||||
|
||||
use App\Services\SystemConfig;
|
||||
use EasyWeChat\Pay\Application;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Ugly\Base\Exceptions\ApiCustomError;
|
||||
use Ugly\Base\Models\Payment;
|
||||
use Ugly\Base\Traits\ApiResource;
|
||||
|
||||
class WeChatPayNative
|
||||
{
|
||||
use ApiResource;
|
||||
|
||||
private Application $payApp;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$config = [
|
||||
'appid' => SystemConfig::get('wechat_pay_app_id'),
|
||||
'mch_id' => SystemConfig::get('wechat_pay_mch_id'),
|
||||
'secret_key' => SystemConfig::get('wechat_pay_secret_key'),
|
||||
// 商户证书
|
||||
'private_key' => SystemConfig::get('wechat_pay_secret_key'),
|
||||
'certificate' => SystemConfig::get('wechat_pay_certificate'),
|
||||
/*'platform_certs' => [
|
||||
storage_path('/certs/wechatpay.pem'),
|
||||
],*/
|
||||
'http' => [
|
||||
'throw' => false,
|
||||
],
|
||||
];
|
||||
$this->payApp = new Application($config);
|
||||
}
|
||||
|
||||
// 支付
|
||||
public function pay($payment, array $data = []): array
|
||||
{
|
||||
$payInfo = $this->payApp->getClient()->postJson('v3/pay/transactions/native', [
|
||||
'appid' => config('services.wechat_pay.appid'),
|
||||
'mchid' => config('services.wechat_pay.mch_id'),
|
||||
'description' => data_get($data, 'description', ''),
|
||||
'out_trade_no' => $payment->no,
|
||||
'notify_url' => config('app.url').'/api/wechat/payment_notify',
|
||||
'time_expire' => $payment->expired_at->toRfc3339String(),
|
||||
'amount' => [
|
||||
'total' => (int) bcmul($payment->amount, 100, 0),
|
||||
'currency' => 'CNY',
|
||||
],
|
||||
])->toArray();
|
||||
|
||||
throw_if(
|
||||
! isset($payInfo['code_url']),
|
||||
new ApiCustomError(data_get($payInfo, 'message', '微信下单失败!'), Response::HTTP_INTERNAL_SERVER_ERROR, Response::HTTP_INTERNAL_SERVER_ERROR)
|
||||
);
|
||||
|
||||
return [
|
||||
'no' => $payment->no,
|
||||
'amount' => $payment->amount,
|
||||
'desc' => $data['description'] ?? '',
|
||||
'expired_at' => $payment->expired_at->toDateTimeString(),
|
||||
'code_url' => $payInfo['code_url'],
|
||||
];
|
||||
}
|
||||
|
||||
// 成功后通知.
|
||||
public function notify()
|
||||
{
|
||||
$server = $this->payApp->getServer();
|
||||
|
||||
// 支付成功
|
||||
$server->handlePaid(function ($message) {
|
||||
Payment::success($message['out_trade_no'], [
|
||||
'notification_no' => $message['transaction_id'],
|
||||
'notification_data' => $message,
|
||||
'success_at' => Carbon::parse($message['success_time']),
|
||||
]);
|
||||
});
|
||||
|
||||
// 退款成功
|
||||
$server->handleRefunded(function ($message) {
|
||||
if ($message['refund_status'] === 'SUCCESS') {
|
||||
Payment::success($message['out_trade_no'], [
|
||||
'notification_no' => $message['refund_id'],
|
||||
'notification_data' => $message,
|
||||
'success_at' => Carbon::parse($message['success_time']),
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
return $server->serve();
|
||||
}
|
||||
}
|
||||
@@ -31,21 +31,6 @@ return [
|
||||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||||
],
|
||||
|
||||
// 微信支付
|
||||
'wechat_pay' => [
|
||||
'appid' => env('WECHAT_PAY_APP_ID'),
|
||||
'mch_id' => env('WECHAT_PAY_MCH_ID'),
|
||||
'secret_key' => env('WECHAT_PAY_SECRET_KEY'),
|
||||
// 商户证书
|
||||
'private_key' => storage_path('/certs/apiclient_key.pem'),
|
||||
'certificate' => storage_path('/certs/apiclient_cert.pem'),
|
||||
'platform_certs' => [
|
||||
storage_path('/certs/wechatpay.pem'),
|
||||
],
|
||||
'http' => [
|
||||
'throw' => false,
|
||||
],
|
||||
],
|
||||
// 阿里短信配置
|
||||
'ali_sms' => [
|
||||
'key' => env('ALI_SMS_KEY'),
|
||||
|
||||
@@ -253,16 +253,13 @@ INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (1, '0001_01_01_000
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (2, '0001_01_01_000001_create_cache_table', 1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (3, '0001_01_01_000002_create_jobs_table', 1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (4, '2019_12_14_000001_create_personal_access_tokens_table', 1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (5, '2024_04_10_095245_create_vip_packages_table', 1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (6, '2024_04_10_101023_create_notices_table', 1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (7, '2024_04_10_101140_create_domains_table', 1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (8, '2024_04_10_101217_create_mini_programs_table', 1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (9, '2024_04_10_101507_create_links_table', 1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (10, '2024_04_10_103926_create_link_visit_logs_table', 1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (11, '2024_04_10_104736_create_payments_table', 1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (12, '2024_04_10_104736_create_sys_configs_table', 1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (13, '2024_04_15_140100_create_materials_table', 1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (14, '2024_04_16_084221_create_vip_logs_table', 1);
|
||||
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (15, '2024_05_08_101122_create_material_categories_table', 1);
|
||||
COMMIT;
|
||||
|
||||
@@ -313,47 +310,6 @@ CREATE TABLE `notices` (
|
||||
BEGIN;
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for payments
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `payments`;
|
||||
CREATE TABLE `payments` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
`no` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '支付编号',
|
||||
`order_no` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '内部订单号',
|
||||
`channel` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '支付渠道',
|
||||
`amount` bigint unsigned NOT NULL COMMENT '支付金额/分',
|
||||
`type` tinyint unsigned NOT NULL COMMENT '支付类型: 1付款 2退款 3转账',
|
||||
`status` tinyint unsigned NOT NULL COMMENT '状态: 1处理中 2成功 3失败',
|
||||
`success_at` timestamp NULL DEFAULT NULL COMMENT '成功时间',
|
||||
`fail_at` timestamp NULL DEFAULT NULL COMMENT '失败时间',
|
||||
`expired_at` timestamp NULL DEFAULT NULL COMMENT '过期时间',
|
||||
`remark` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
|
||||
`notification_no` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '第三方支付通知单号',
|
||||
`notification_data` json DEFAULT NULL COMMENT '第三方支付通知原始数据',
|
||||
`job` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '回调任务',
|
||||
`attach` json DEFAULT NULL COMMENT '附加信息',
|
||||
`payment_id` bigint unsigned DEFAULT NULL COMMENT '退款时关联支付单ID',
|
||||
`merchant_type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`merchant_id` bigint unsigned DEFAULT NULL,
|
||||
`payer_type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`payer_id` bigint unsigned DEFAULT NULL,
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `payments_no_unique` (`no`),
|
||||
KEY `payments_merchant_type_merchant_id_index` (`merchant_type`,`merchant_id`),
|
||||
KEY `payments_payer_type_payer_id_index` (`payer_type`,`payer_id`),
|
||||
KEY `payments_type_index` (`type`),
|
||||
KEY `payments_status_index` (`status`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='支付记录表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of payments
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for personal_access_tokens
|
||||
-- ----------------------------
|
||||
@@ -400,9 +356,6 @@ BEGIN;
|
||||
INSERT INTO `sys_configs` (`slug`, `value`, `desc`, `created_at`, `updated_at`) VALUES ('ali_sms_key', '', '阿里短信key', NULL, NULL);
|
||||
INSERT INTO `sys_configs` (`slug`, `value`, `desc`, `created_at`, `updated_at`) VALUES ('ali_sms_secret', '', '阿里短信secret', NULL, NULL);
|
||||
INSERT INTO `sys_configs` (`slug`, `value`, `desc`, `created_at`, `updated_at`) VALUES ('ali_sms_sign_name', '', '阿里短信签名', NULL, NULL);
|
||||
INSERT INTO `sys_configs` (`slug`, `value`, `desc`, `created_at`, `updated_at`) VALUES ('give_vip_days', '3', '注册赠送会员有效期/天', NULL, NULL);
|
||||
INSERT INTO `sys_configs` (`slug`, `value`, `desc`, `created_at`, `updated_at`) VALUES ('give_vip_id', '1', '赠送套餐', NULL, NULL);
|
||||
INSERT INTO `sys_configs` (`slug`, `value`, `desc`, `created_at`, `updated_at`) VALUES ('is_give_vip', '1', '开启注册赠送会员', NULL, NULL);
|
||||
INSERT INTO `sys_configs` (`slug`, `value`, `desc`, `created_at`, `updated_at`) VALUES ('mail_from_address', '', '发信地址', NULL, NULL);
|
||||
INSERT INTO `sys_configs` (`slug`, `value`, `desc`, `created_at`, `updated_at`) VALUES ('mail_from_name', '', '发信名称', NULL, NULL);
|
||||
INSERT INTO `sys_configs` (`slug`, `value`, `desc`, `created_at`, `updated_at`) VALUES ('mail_host', '', '服务器地址', NULL, NULL);
|
||||
@@ -415,11 +368,6 @@ INSERT INTO `sys_configs` (`slug`, `value`, `desc`, `created_at`, `updated_at`)
|
||||
INSERT INTO `sys_configs` (`slug`, `value`, `desc`, `created_at`, `updated_at`) VALUES ('web_site_customer_service', '/image/web-qr.png', '客服二维码', NULL, NULL);
|
||||
INSERT INTO `sys_configs` (`slug`, `value`, `desc`, `created_at`, `updated_at`) VALUES ('web_site_logo', '/image/toplogo.png', '网站logo浅色', NULL, NULL);
|
||||
INSERT INTO `sys_configs` (`slug`, `value`, `desc`, `created_at`, `updated_at`) VALUES ('web_site_title', '蚂蚁快链-智慧引流工具', '网站名称', NULL, NULL);
|
||||
INSERT INTO `sys_configs` (`slug`, `value`, `desc`, `created_at`, `updated_at`) VALUES ('wechat_pay_app_id', '', '微信商户appId', NULL, NULL);
|
||||
INSERT INTO `sys_configs` (`slug`, `value`, `desc`, `created_at`, `updated_at`) VALUES ('wechat_pay_certificate', '', '微信支付公钥证书', NULL, NULL);
|
||||
INSERT INTO `sys_configs` (`slug`, `value`, `desc`, `created_at`, `updated_at`) VALUES ('wechat_pay_mch_id', '', '微信商户号', NULL, NULL);
|
||||
INSERT INTO `sys_configs` (`slug`, `value`, `desc`, `created_at`, `updated_at`) VALUES ('wechat_pay_private_cert', '', '微信支付私钥证书', NULL, NULL);
|
||||
INSERT INTO `sys_configs` (`slug`, `value`, `desc`, `created_at`, `updated_at`) VALUES ('wechat_pay_secret_key', '', '微信支付密钥', NULL, NULL);
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
@@ -457,51 +405,4 @@ BEGIN;
|
||||
INSERT INTO `users` (`id`, `username`, `password`, `status`, `type`, `credit`, `accumulate_credit`, `commission`, `accumulate_commission`, `vip_id`, `agent_id`, `level_id`, `start_at`, `end_at`, `parent_id`, `referral_code`, `created_at`, `updated_at`) VALUES (1, 'admin', '$2y$12$DQNA/1BJcPdmpJ9ylifXHO9X.RDaovOj4SHL5M6S/vr9lvnFND5Gy', 1, 3, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, '100000', '2025-01-15 08:41:32', '2025-01-15 08:41:32');
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for vip_logs
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `vip_logs`;
|
||||
CREATE TABLE `vip_logs` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
`user_id` bigint unsigned NOT NULL COMMENT '会员id',
|
||||
`payment_id` bigint unsigned DEFAULT NULL COMMENT '支付ID',
|
||||
`vip_id` bigint unsigned DEFAULT NULL COMMENT '会员ID',
|
||||
`status` tinyint unsigned NOT NULL COMMENT '状态',
|
||||
`start_at` timestamp NOT NULL COMMENT '开始时间',
|
||||
`end_at` timestamp NOT NULL COMMENT '结束时间',
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='VIP购买记录表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of vip_logs
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for vip_packages
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `vip_packages`;
|
||||
CREATE TABLE `vip_packages` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '套餐名称',
|
||||
`price` bigint unsigned NOT NULL COMMENT '价格/月',
|
||||
`level` tinyint unsigned NOT NULL COMMENT '等级',
|
||||
`config` json NOT NULL COMMENT '权益配置',
|
||||
`created_at` timestamp NULL DEFAULT NULL,
|
||||
`updated_at` timestamp NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='vip套餐配置';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of vip_packages
|
||||
-- ----------------------------
|
||||
BEGIN;
|
||||
INSERT INTO `vip_packages` (`id`, `name`, `price`, `level`, `config`, `created_at`, `updated_at`) VALUES (1, '体验套餐', 0, 0, '{\"pre_min\": false, \"support\": true, \"uv_limit\": 9, \"cur_index\": false, \"allow_type\": {\"CLI_QR\": false, \"KING_DOC\": false, \"WORK_WECHAT\": false, \"LANDING_MINI\": false, \"MINI_PROGRAM\": true}, \"count_limit\": 1, \"min_count_limit\": 1, \"min_disabled_check\": true}', '2024-04-11 12:49:52', '2024-09-29 08:28:55');
|
||||
INSERT INTO `vip_packages` (`id`, `name`, `price`, `level`, `config`, `created_at`, `updated_at`) VALUES (2, '初级会员', 4300, 1, '{\"pre_min\": true, \"support\": true, \"uv_limit\": 50, \"cur_index\": true, \"allow_type\": {\"CLI_QR\": true, \"KING_DOC\": true, \"WORK_WECHAT\": true, \"LANDING_MINI\": true, \"MINI_PROGRAM\": true}, \"count_limit\": 5, \"min_count_limit\": 5, \"min_disabled_check\": true}', '2024-04-17 14:27:19', '2024-09-29 08:29:32');
|
||||
INSERT INTO `vip_packages` (`id`, `name`, `price`, `level`, `config`, `created_at`, `updated_at`) VALUES (3, '高级会员', 29800, 2, '{\"pre_min\": true, \"support\": true, \"uv_limit\": 100000, \"cur_index\": true, \"allow_type\": {\"CLI_QR\": true, \"KING_DOC\": true, \"WORK_WECHAT\": true, \"LANDING_MINI\": true, \"MINI_PROGRAM\": true}, \"count_limit\": 50, \"min_count_limit\": 10, \"min_disabled_check\": true}', '2024-04-22 17:40:28', '2024-09-29 08:29:49');
|
||||
COMMIT;
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('vip_packages', function (Blueprint $table) {
|
||||
$table->comment('vip套餐配置');
|
||||
$table->id();
|
||||
$table->string('name')->comment('套餐名称');
|
||||
$table->unsignedBigInteger('price')->comment('价格/月');
|
||||
$table->unsignedTinyInteger('level')->comment('等级');
|
||||
$table->json('config')->comment('权益配置');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('vip_packages');
|
||||
}
|
||||
};
|
||||
@@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('vip_logs', function (Blueprint $table) {
|
||||
$table->comment('VIP购买记录表');
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('user_id')->comment('会员id');
|
||||
$table->unsignedBigInteger('payment_id')->nullable()->comment('支付ID');
|
||||
$table->unsignedBigInteger('vip_id')->nullable()->comment('会员ID');
|
||||
$table->unsignedTinyInteger('status')->comment('状态');
|
||||
$table->timestamp('start_at')->comment('开始时间');
|
||||
$table->timestamp('end_at')->comment('结束时间');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('vip_logs');
|
||||
}
|
||||
};
|
||||
@@ -11,13 +11,10 @@ use App\Http\Controllers\Api\MaterialCateController;
|
||||
use App\Http\Controllers\Api\MaterialController;
|
||||
use App\Http\Controllers\Api\MiniProgramController;
|
||||
use App\Http\Controllers\Api\NoticeController;
|
||||
use App\Http\Controllers\Api\PaymentController;
|
||||
use App\Http\Controllers\Api\UploadController;
|
||||
use App\Http\Controllers\Api\UserController;
|
||||
use App\Http\Controllers\Api\VipPackageController;
|
||||
use App\Http\Controllers\UpgradeController;
|
||||
use App\Http\Middleware\ApiAuth;
|
||||
use App\PayChannels\WeChatPayNative;
|
||||
use Illuminate\Routing\Router;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
@@ -27,7 +24,6 @@ Route::post('/register', [AuthController::class, 'register']); // 注册
|
||||
Route::post('/reset-password', [AuthController::class, 'resetPassword']); // 重置密码
|
||||
Route::get('/captcha/image', [CaptchaController::class, 'image']); // 登录
|
||||
Route::post('/captcha/sms', [CaptchaController::class, 'sms']); // 获取短信验证码
|
||||
Route::post('/wechat/payment_notify', [WeChatPayNative::class, 'notify']); // 微信支付回调
|
||||
|
||||
// 获取链接重定向目标
|
||||
Route::get('/link-target/{code}', [JumpController::class, 'target']); // 获取链接跳转地址
|
||||
@@ -58,9 +54,6 @@ Route::middleware(ApiAuth::class)->group(function (Router $router) {
|
||||
$router->get('link-list', [LinkController::class, 'link_list']); // 获取下拉数据源
|
||||
|
||||
$router->get('/agent-invite', [UserController::class, 'invite']); // 邀请记录
|
||||
|
||||
// 购买会员
|
||||
$router->post('/buy-vip', [AuthController::class, 'buyVip']);
|
||||
});
|
||||
|
||||
// 仅管理员可访问
|
||||
@@ -69,12 +62,8 @@ Route::middleware(ApiAuth::class.':admin')->group(function (Router $router) {
|
||||
$router->apiResource('notices', NoticeController::class)->except('show');
|
||||
// 添加/编辑公告
|
||||
$router->put('notice', [NoticeController::class, 'set_notice']);
|
||||
|
||||
// 域名
|
||||
$router->apiResource('domains', DomainController::class)->except(['index', 'show']);
|
||||
// VIP套餐
|
||||
$router->apiResource('vip-packages', VipPackageController::class)->except('show');
|
||||
|
||||
// 设置
|
||||
$router->get('config/{type}', [ConfigController::class, 'getForm']);
|
||||
// 保存设置
|
||||
@@ -82,7 +71,5 @@ Route::middleware(ApiAuth::class.':admin')->group(function (Router $router) {
|
||||
// 用户
|
||||
$router->apiResource('users', UserController::class)->only(['index', 'store', 'update']);
|
||||
$router->get('agent-tree', [UserController::class, 'agent_tree']); // 树状用户
|
||||
|
||||
$router->get('payments', [PaymentController::class, 'index']); // 付款记录
|
||||
});
|
||||
Route::get('upgrade_status', [UpgradeController::class, 'index']); // 请求最新版本
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?php
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Controllers\HomeController;
|
||||
use App\Http\Controllers\Install\IndexController;
|
||||
use App\Http\Controllers\JumpController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::get('/', [Controller::class, 'welcome']);
|
||||
Route::get('/j/{code}', [JumpController::class, 'show']);
|
||||
Route::get('/j/{code}', [HomeController::class, 'show']);
|
||||
|
||||
Route::any('/install', [IndexController::class, 'index']);
|
||||
|
||||
@@ -2,17 +2,15 @@
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Enums\CommissionType;
|
||||
use App\Jobs\SendEmailJobs;
|
||||
use App\Models\User;
|
||||
use App\Models\VipPackage;
|
||||
use Tests\TestCase;
|
||||
|
||||
class UserTest extends TestCase
|
||||
{
|
||||
public function test_send_mail()
|
||||
{
|
||||
SendEmailJobs::dispatch('724323954@qq.com', '123321');
|
||||
SendEmailJobs::dispatch('123321@qq.com', '123321');
|
||||
}
|
||||
|
||||
public function test_get_token()
|
||||
@@ -39,14 +37,6 @@ class UserTest extends TestCase
|
||||
$this->assertDatabaseHas('users', [
|
||||
'username' => $phone,
|
||||
]);
|
||||
if (! empty($referral_code)) {
|
||||
$child = User::query()->where('parent_id', $agent->id)->orderByDesc('id')->first();
|
||||
$this->assertDatabaseHas('commission_logs', [
|
||||
'user_id' => $agent->id,
|
||||
'type' => CommissionType::Rebates->value,
|
||||
'children_user_id' => $child->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function get_admin_token()
|
||||
@@ -55,36 +45,4 @@ class UserTest extends TestCase
|
||||
|
||||
return $admin->createToken('api')->plainTextToken;
|
||||
}
|
||||
|
||||
public function test_user_package_fix(): void
|
||||
{
|
||||
$token = $this->get_admin_token();
|
||||
|
||||
$user = User::query()
|
||||
->inRandomOrder()
|
||||
->where('id', 2)
|
||||
->first();
|
||||
$id = $user->id;
|
||||
$u_vip_id = $user->vip_id;
|
||||
$vip = VipPackage::query()
|
||||
->when(! empty($u_vip_id), function ($query) use ($u_vip_id) {
|
||||
$query->whereNot('id', $u_vip_id);
|
||||
})
|
||||
->inRandomOrder()
|
||||
->first();
|
||||
|
||||
$response = $this
|
||||
->withHeaders([
|
||||
'Authorization' => "Bearer {$token}",
|
||||
])
|
||||
->putJson("/api/users/{$id}", [
|
||||
'vip_id' => $vip->id,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$this->assertDatabaseHas('users', [
|
||||
'vip_id' => $vip->id,
|
||||
// 'end_at' => '', // TODO
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user