wesmiler 2 周之前
父節點
當前提交
cef4eb3856

+ 574 - 0
addons/admin/src/views/system/financial/accountLog.vue

@@ -0,0 +1,574 @@
+<template>
+	<div class="ele-body">
+		<el-card shadow="never">
+			<!-- 状态 Tab -->
+			<el-tabs v-model="activeTab" @tab-click="handleTabClick" class="ele-tabs-card">
+				<el-tab-pane label="全部" name="all">
+					<template slot="label">
+						<span><i class="el-icon-s-order"></i> 全部</span>
+					</template>
+				</el-tab-pane>
+				<el-tab-pane label="已完成" name="1">
+					<template slot="label">
+						<span><i class="el-icon-success"></i> 已完成</span>
+					</template>
+				</el-tab-pane>
+				<el-tab-pane label="待处理" name="2">
+					<template slot="label">
+						<span><i class="el-icon-loading"></i> 待处理</span>
+					</template>
+				</el-tab-pane>
+				<el-tab-pane label="失败/取消" name="3">
+					<template slot="label">
+						<span><i class="el-icon-circle-close"></i> 失败/取消</span>
+					</template>
+				</el-tab-pane>
+			</el-tabs>
+
+			<!-- 搜索表单 -->
+			<el-form :model="table.where" label-width="90px" class="ele-form-search"
+				@keyup.enter.native="$refs.table.reload()" @submit.native.prevent>
+				<el-row :gutter="15">
+					<el-col :md="4" :sm="12">
+						<el-form-item label="交易类型:">
+							<el-select v-model="table.where.type" placeholder="请选择交易类型" clearable class="ele-fluid">
+								<el-option v-for="(v,k) in getTypes()" :key="k" :label="v.name" :value="v.id" />
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :md="5" :sm="12" v-if="user.id<=0">
+						<el-form-item label="关键词:">
+							<el-input v-model="table.where.keyword" placeholder="用户昵称/手机号" clearable />
+						</el-form-item>
+					</el-col>
+					<el-col :md="4" :sm="12">
+						<el-form-item label="订单号:">
+							<el-input v-model="table.where.order_no" placeholder="请输入订单号" clearable />
+						</el-form-item>
+					</el-col>
+          <el-col :md="5" :sm="12">
+            <el-form-item label="起止日期:" prop="date">
+              <el-date-picker
+                  v-model="table.where.date"
+                  type="daterange"
+                  placeholder="选择起止日期"
+                  size="small"
+                  style="width: 180px;"
+                  value-format="yyyy-MM-dd"
+                  format="yyyy-MM-dd">
+              </el-date-picker>
+            </el-form-item>
+          </el-col>
+					<el-col :md="6" :sm="12">
+						<div class="ele-form-actions">
+							<el-button type="primary" @click="$refs.table.reload()" icon="el-icon-search"
+								class="ele-btn-icon">查询</el-button>
+							<el-button @click="handleReset">重置</el-button>
+						</div>
+					</el-col>
+				</el-row>
+			</el-form>
+
+			<!-- 统计信息 -->
+			<div class="stat-cards" v-if="counts">
+				<el-row :gutter="15">
+					<el-col :span="12">
+						<div class="stat-card">
+							<div class="stat-label">总记录数</div>
+							<div class="stat-value">{{ counts.count || 0 }}笔</div>
+						</div>
+					</el-col>
+					<el-col :span="12">
+						<div class="stat-card">
+							<div class="stat-label">总金额</div>
+							<div class="stat-value amount">{{ counts.total || 0 }}元</div>
+						</div>
+					</el-col>
+				</el-row>
+			</div>
+
+			<!-- 数据表格 -->
+			<ele-data-table ref="table" :config="table" :choose.sync="selection" @loaded="parseData"
+				height="calc(100vh - 400px)">
+				<template slot-scope="{index}">
+					<el-table-column type="selection" width="45" align="center" fixed="left" />
+					<el-table-column prop="id" label="ID" width="60" align="center" sortable="custom" />
+					<el-table-column prop="source_order_no" label="订单号" min-width="180" align="center" show-overflow-tooltip />
+					<el-table-column prop="nickname" :label="$store.state.user.user.type==2?'来源用户':'用户昵称'" min-width="100" align="center">
+            <template slot-scope="{row}">
+              <span class="ele-text-primary" v-if="$store.state.user.user.type==2">{{row.bill && row.bill.user? row.bill.user.nickname+'('+row.bill.user.mobile+')':'-'}}</span>
+              <span class="ele-text-primary" v-else>{{row.nickname}}</span>
+            </template>
+          </el-table-column>
+					<el-table-column prop="mobile" v-if="$store.state.user.user.type!=2" label="手机号" min-width="120" align="center">
+          </el-table-column>
+          <!-- 金额 -->
+          <el-table-column label="金额" width="120" align="center">
+            <template slot-scope="{row}">
+              <span style="font-weight: bold;">{{ row.money }}元</span>
+            </template>
+          </el-table-column>
+
+          <!-- 变更前金额 -->
+          <el-table-column label="变更后金额" width="120" align="center">
+            <template slot-scope="{row}">
+              <span style="color: #909399;">{{ row.after_money }}元</span>
+            </template>
+          </el-table-column>
+					<!-- 交易类型 -->
+					<el-table-column label="交易类型" width="100" align="center">
+						<template slot-scope="{row}">
+							<el-tag :type="['','success','primary','info','warning','danger','success','warning','info','success','paimary'][row.type]" size="mini">{{row.type_name}}</el-tag>
+						</template>
+					</el-table-column>
+          <el-table-column prop="remark" label="备注" min-width="120" align="center">
+          </el-table-column>
+
+
+					<!-- 状态 -->
+					<el-table-column label="状态" width="100" align="center">
+						<template slot-scope="{row}">
+							<el-tag v-if="row.status === 1" type="success" size="mini">已完成</el-tag>
+							<el-tag v-else-if="row.status === 2" type="warning" size="mini">待处理</el-tag>
+							<el-tag v-else-if="row.status === 3" type="danger" size="mini">失败/取消</el-tag>
+						</template>
+					</el-table-column>
+
+					<el-table-column prop="create_time" label="创建时间" min-width="160" align="center" show-overflow-tooltip />
+
+					<!-- 操作 -->
+					<el-table-column label="操作" width="100" align="center" fixed="right">
+						<template slot-scope="{row}">
+							<el-link @click="viewDetail(row)" icon="el-icon-view" type="primary" :underline="false">查看</el-link>
+						</template>
+					</el-table-column>
+				</template>
+			</ele-data-table>
+		</el-card>
+
+		<!-- 查看详情弹窗 -->
+		<el-dialog title="财务明细详情" :visible.sync="detailVisible" width="700px" :destroy-on-close="true"
+			:close-on-click-modal="false" custom-class="account-detail-dialog">
+
+			<!-- 头部信息卡片 -->
+			<div class="detail-header">
+				<div class="header-item">
+					<div class="header-label">记录ID</div>
+					<div class="header-value">#{{ detailData.id }}</div>
+				</div>
+				<div class="header-item">
+					<div class="header-label">交易类型</div>
+					<div class="header-value">
+						<el-tag  type="success" size="medium">{{detailData.type_name}}</el-tag>
+					</div>
+				</div>
+				<div class="header-item">
+					<div class="header-label">状态</div>
+					<div class="header-value">
+						<el-tag v-if="detailData.status === 1" type="success" size="medium">已完成</el-tag>
+						<el-tag v-else-if="detailData.status === 2" type="warning" size="medium">待处理</el-tag>
+						<el-tag v-else-if="detailData.status === 3" type="danger" size="medium">失败/取消</el-tag>
+					</div>
+				</div>
+			</div>
+
+			<!-- 金额信息 -->
+			<div class="amount-section">
+				<div class="amount-card main-amount">
+					<div class="amount-label">交易金额</div>
+					<div class="amount-value">
+						¥{{ detailData.money }}
+					</div>
+				</div>
+				<div class="amount-card">
+					<div class="amount-label">变更后金额</div>
+					<div class="amount-value secondary">¥{{ detailData.after_money }}</div>
+				</div>
+			</div>
+
+			<!-- 详细信息 -->
+			<div class="detail-section">
+				<div class="section-title">
+					<i class="el-icon-user"></i> 基本信息
+				</div>
+				<el-descriptions :column="2" border size="medium">
+					<el-descriptions-item label="关联订单号" :span="2">
+						<span class="order-no">{{ detailData.source_order_no || '-' }}</span>
+					</el-descriptions-item>
+          <template v-if="$store.state.user.user.type==2">
+            <el-descriptions-item label="来源用户">{{ detailData.bill && detailData.bill.user?detailData.bill.nickname+'(ID:'+detailData.bill.id+'-'+detailData.bill.mobile+')':'-'}}</el-descriptions-item>
+          </template>
+					<template v-else>
+            <el-descriptions-item label="用户ID">{{ detailData.user_id || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="用户昵称">{{ detailData.nickname || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="手机号">{{ detailData.mobile || '-' }}</el-descriptions-item>
+          </template>
+					<el-descriptions-item label="交易类型" :span="2">
+						<span>{{ detailData.type_name }}</span>
+					</el-descriptions-item>
+				</el-descriptions>
+			</div>
+
+			<div class="detail-section">
+				<div class="section-title">
+					<i class="el-icon-time"></i> 时间信息
+				</div>
+				<el-descriptions :column="2" border size="medium">
+					<el-descriptions-item label="创建时间">
+						<i class="el-icon-time"></i> {{ detailData.create_time_text }}
+					</el-descriptions-item>
+					<el-descriptions-item label="更新时间">
+						<i class="el-icon-time"></i> {{ detailData.update_time_text }}
+					</el-descriptions-item>
+				</el-descriptions>
+			</div>
+
+			<div class="detail-section" v-if="detailData.remark">
+				<div class="section-title">
+					<i class="el-icon-document"></i> 备注
+				</div>
+				<el-descriptions :column="1" border size="medium">
+					<el-descriptions-item label="备注" :span="1">
+						<div class="remark-text">{{ detailData.remark }}</div>
+					</el-descriptions-item>
+				</el-descriptions>
+			</div>
+
+			<div slot="footer" class="dialog-footer">
+				<el-button type="primary" size="medium" @click="detailVisible = false">
+					<i class="el-icon-check"></i> 关闭
+				</el-button>
+			</div>
+		</el-dialog>
+	</div>
+</template>
+
+<script>
+import { mapGetters } from "vuex";
+
+export default {
+	name: "AccountLog",
+	computed: {
+		...mapGetters(["permission"]),
+		// 判断是否为商户用户
+		isStoreUser() {
+			return this.$store.state.user.user && this.$store.state.user.user.store_id > 0;
+		}
+	},
+  props: {
+    user: {
+      type: Object,
+      default() {
+        return {id:0, mobile: ''};
+      }
+    },
+  },
+	data() {
+		return {
+			activeTab: 'all', // 当前激活的 Tab
+			// 表格配置
+			table: {
+				url: '/account/index',
+				where: {
+					type: '',
+					status: '',
+          user_id: this.user.id,
+					keyword: '',
+					order_no: ''
+				}
+			},
+			selection: [], // 表格选中数据
+			counts: null, // 统计数据
+			detailVisible: false, // 是否显示详情弹窗
+			detailData: {} // 详情数据
+		};
+	},
+	methods: {
+		/* 解析表格数据 */
+		parseData(data,counts) {
+			if (data && counts) {
+				this.counts = counts;
+			}
+			return data;
+		},
+
+		/* Tab 切换 */
+		handleTabClick(tab) {
+			// 根据 Tab 设置状态
+			if (tab.name === 'all') {
+				this.table.where.status = '';
+			} else {
+				this.table.where.status = parseInt(tab.name);
+			}
+			// 重新加载数据
+			this.$refs.table.reload();
+		},
+
+		/* 重置搜索 */
+		handleReset() {
+			this.activeTab = 'all';
+			this.table.where = {
+				type: '',
+				status: '',
+        user_id: this.user.id,
+				keyword: '',
+				order_no: ''
+			};
+			this.$refs.table.reload();
+		},
+
+		/* 查看详情 */
+		viewDetail(row) {
+			this.detailData = Object.assign({}, row);
+			this.detailVisible = true;
+		},
+
+		/* 获取交易类型文本 */
+		getTypes() {
+			const typeMap =[
+        {id: 1,name: '订单付款'},
+        {id: 2,name: '余额充值'},
+        {id: 3,name: '订单退款'},
+        {id: 4,name: '余额转出'},
+        {id: 5,name: '余额转出驳回'},
+        {id: 6,name: '平台转入'},
+        {id: 7,name: '企业结算'},
+        {id: 8,name: '收益'},
+        {id: 9,name: '其他'},
+        {id: 10,name: '余额转账'},
+      ];
+			return this.$store.state.user.user.type==2? [{id: 7,name:'企业结算'}] : typeMap;
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+// Tab 标签页样式
+::v-deep .ele-tabs-card {
+	margin-bottom: 20px;
+
+	.el-tabs__header {
+		margin: 0;
+		border-bottom: 2px solid #e4e7ed;
+	}
+
+	.el-tabs__nav-wrap::after {
+		height: 0;
+	}
+
+	.el-tabs__item {
+		height: 50px;
+		line-height: 50px;
+		font-size: 14px;
+
+		i {
+			margin-right: 5px;
+			font-size: 16px;
+		}
+
+		&:hover {
+			color: #409eff;
+		}
+
+		&.is-active {
+			color: #409eff;
+			font-weight: bold;
+		}
+	}
+
+	.el-tabs__active-bar {
+		height: 3px;
+		background: linear-gradient(90deg, #409eff 0%, #66b1ff 100%);
+	}
+}
+
+.stat-cards {
+	margin-bottom: 15px;
+
+	.stat-card {
+		background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+		border-radius: 8px;
+		padding: 20px;
+		color: white;
+
+		.stat-label {
+			font-size: 14px;
+			opacity: 0.9;
+			margin-bottom: 8px;
+		}
+
+		.stat-value {
+			font-size: 28px;
+			font-weight: bold;
+
+			&.amount {
+				color: #ffd700;
+			}
+		}
+	}
+}
+
+// 详情弹窗样式
+::v-deep .account-detail-dialog {
+	.el-dialog__header {
+		background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+		padding: 20px;
+
+		.el-dialog__title {
+			color: white;
+			font-size: 18px;
+			font-weight: bold;
+		}
+
+		.el-dialog__headerbtn .el-dialog__close {
+			color: white;
+			font-size: 20px;
+
+			&:hover {
+				color: #f0f0f0;
+			}
+		}
+	}
+
+	.el-dialog__body {
+		padding: 20px;
+		background: #f5f7fa;
+	}
+
+	.el-dialog__footer {
+		padding: 15px 20px;
+		background: #fff;
+		border-top: 1px solid #e4e7ed;
+	}
+}
+
+.detail-header {
+	display: flex;
+	justify-content: space-around;
+	margin-bottom: 20px;
+	background: white;
+	border-radius: 8px;
+	padding: 20px;
+	box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+
+	.header-item {
+		text-align: center;
+		flex: 1;
+
+		&:not(:last-child) {
+			border-right: 1px solid #e4e7ed;
+		}
+
+		.header-label {
+			font-size: 13px;
+			color: #909399;
+			margin-bottom: 10px;
+		}
+
+		.header-value {
+			font-size: 16px;
+			font-weight: bold;
+			color: #303133;
+		}
+	}
+}
+
+.amount-section {
+	display: flex;
+	gap: 15px;
+	margin-bottom: 20px;
+
+	.amount-card {
+		flex: 1;
+		background: white;
+		border-radius: 8px;
+		padding: 20px;
+		box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+
+		&.main-amount {
+			background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
+
+			.amount-label {
+				color: rgba(255, 255, 255, 0.9);
+			}
+
+			.amount-value {
+				color: white !important;
+			}
+		}
+
+		.amount-label {
+			font-size: 13px;
+			color: #909399;
+			margin-bottom: 10px;
+		}
+
+		.amount-value {
+			font-size: 28px;
+			font-weight: bold;
+
+			&.secondary {
+				color: #606266;
+				font-size: 24px;
+			}
+		}
+	}
+}
+
+.detail-section {
+	margin-bottom: 20px;
+
+	.section-title {
+		font-size: 15px;
+		font-weight: bold;
+		color: #303133;
+		margin-bottom: 12px;
+		padding-left: 10px;
+		border-left: 3px solid #409eff;
+
+		i {
+			margin-right: 5px;
+			color: #409eff;
+		}
+	}
+
+	::v-deep .el-descriptions {
+		background: white;
+		border-radius: 8px;
+		overflow: hidden;
+		box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+
+		.el-descriptions__label {
+			background: #fafafa;
+			font-weight: 500;
+			color: #606266;
+		}
+
+		.el-descriptions__content {
+			color: #303133;
+		}
+	}
+
+	.order-no {
+		font-family: 'Courier New', monospace;
+		color: #409eff;
+		font-weight: 500;
+	}
+
+	.remark-text {
+		line-height: 1.6;
+		color: #606266;
+		white-space: pre-wrap;
+		word-break: break-all;
+	}
+}
+
+.dialog-footer {
+	text-align: center;
+
+	.el-button {
+		min-width: 120px;
+	}
+}
+</style>

+ 430 - 0
addons/admin/src/views/system/financial/balanceLogs.vue

@@ -0,0 +1,430 @@
+<template>
+	<div class="ele-body">
+		<el-card shadow="never">
+			<!-- 状态 Tab 标签页 -->
+			<el-tabs v-model="activeTab" @tab-click="handleTabClick" class="ele-tabs-card">
+				<el-tab-pane label="全部" name="all"></el-tab-pane>
+				<el-tab-pane label="待审核" name="1"></el-tab-pane>
+				<el-tab-pane label="已审核/到账" name="2"></el-tab-pane>
+				<el-tab-pane label="审核失败" name="3"></el-tab-pane>
+				<el-tab-pane label="已取消" name="-1"></el-tab-pane>
+			</el-tabs>
+
+			<!-- 搜索表单 -->
+			<el-form :model="table.where" label-width="80px" class="ele-form-search" @keyup.enter.native="$refs.table.reload()" @submit.native.prevent>
+				<el-row :gutter="15">
+					<el-col :md="4" :sm="12" v-if="showTypeFilter">
+						<el-form-item label="类型:">
+							<el-select clearable v-model="table.where.type" placeholder="请选择类型" class="ele-fluid">
+								<el-option label="充值" :value="1"/>
+								<el-option label="提现" :value="2"/>
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :md="6" :sm="12">
+						<el-form-item label="关键词:">
+							<el-input clearable v-model="table.where.keyword" placeholder="订单号/姓名/手机号"/>
+						</el-form-item>
+					</el-col>
+					<el-col :md="6" :sm="12">
+						<el-form-item label="时间范围:">
+							<el-date-picker
+								v-model="dateRange"
+								type="daterange"
+								range-separator="至"
+								start-placeholder="开始日期"
+								end-placeholder="结束日期"
+								value-format="yyyy-MM-dd"
+								class="ele-fluid">
+							</el-date-picker>
+						</el-form-item>
+					</el-col>
+					<el-col :md="4" :sm="12">
+						<div class="ele-form-actions">
+							<el-button type="primary" icon="el-icon-search" class="ele-btn-icon" @click="$refs.table.reload()">查询</el-button>
+							<el-button @click="reset">重置</el-button>
+						</div>
+					</el-col>
+				</el-row>
+			</el-form>
+
+			<!-- 数据表格 -->
+			<ele-data-table
+				ref="table"
+				:config="table"
+				:choose.sync="selection"
+				height="calc(100vh - 350px)">
+				<template slot-scope="{index}">
+					<el-table-column type="selection" width="45" align="center" fixed="left"/>
+					<el-table-column prop="id" label="ID" width="60" align="center" sortable="custom"/>
+					<el-table-column prop="order_no" label="订单号" min-width="180" align="center" show-overflow-tooltip/>
+					
+					<!-- 账户类型 -->
+					<el-table-column label="账户类型" width="100" align="center">
+						<template slot-scope="{row}">
+							<el-tag v-if="row.account_type === 1" type="success" size="mini">会员</el-tag>
+							<el-tag v-else-if="row.account_type === 2" type="warning" size="mini">代理</el-tag>
+							<el-tag v-else-if="row.account_type === 3" type="info" size="mini">商户</el-tag>
+						</template>
+					</el-table-column>
+					
+					<!-- 类型 -->
+					<el-table-column label="类型" width="80" align="center">
+						<template slot-scope="{row}">
+							<el-tag v-if="row.type === 1" type="success" size="mini">充值</el-tag>
+							<el-tag v-else-if="row.type === 2" type="primary" size="mini">提现</el-tag>
+						</template>
+					</el-table-column>
+					
+					<el-table-column prop="realname" label="姓名/商户" min-width="120" align="center" show-overflow-tooltip/>
+					
+					<!-- 金额 -->
+					<el-table-column label="金额" min-width="120" align="center">
+						<template slot-scope="{row}">
+							<span :style="{color: row.type === 1 ? '#67C23A' : '#F56C6C', fontWeight: 'bold'}">
+								{{ row.type === 1 ? '+' : '-' }}¥{{ row.money }}
+							</span>
+						</template>
+					</el-table-column>
+					
+					<!-- 实际到账 -->
+					<el-table-column label="实际到账" min-width="120" align="center">
+						<template slot-scope="{row}">
+							<span style="color: #409EFF; font-weight: bold;">¥{{ row.actual_money }}</span>
+						</template>
+					</el-table-column>
+					
+					<!-- 支付方式 -->
+					<el-table-column label="支付方式" width="100" align="center">
+						<template slot-scope="{row}">
+							<el-tag v-if="row.pay_type === 10" type="success" size="mini">微信</el-tag>
+							<el-tag v-else-if="row.pay_type === 20" type="primary" size="mini">支付宝</el-tag>
+							<el-tag v-else-if="row.pay_type === 50" type="info" size="mini">银行卡</el-tag>
+						</template>
+					</el-table-column>
+					
+					<!-- 状态 -->
+					<el-table-column label="状态" width="120" align="center">
+						<template slot-scope="{row}">
+							<el-tag v-if="row.status === -1" type="info" size="mini">已取消</el-tag>
+							<el-tag v-else-if="row.status === 1" type="warning" size="mini">待审核</el-tag>
+							<el-tag v-else-if="row.status === 2" type="success" size="mini">已审核/到账</el-tag>
+							<el-tag v-else-if="row.status === 3" type="danger" size="mini">审核失败</el-tag>
+						</template>
+					</el-table-column>
+					
+					<el-table-column prop="create_time_text" label="申请时间" min-width="160" align="center" show-overflow-tooltip/>
+					
+					<!-- 操作 -->
+					<el-table-column label="操作" width="240" align="center" fixed="right">
+						<template slot-scope="{row}">
+							<el-link @click="openDetail(row)" icon="el-icon-view" type="primary" :underline="false" style="margin-right: 10px;">查看</el-link>
+							<el-link v-if="row.status === 1 && row.type === 2 && permission.includes(permissionPrefix + ':edit')" 
+								@click="openAudit(row)" icon="el-icon-check" type="success" :underline="false" style="margin-right: 10px;">审核</el-link>
+							<el-popconfirm title="确定要删除此记录吗?" @confirm="remove(row)" 
+								v-if="row.status !== 1 && permission.includes(permissionPrefix + ':delete')">
+								<el-link slot="reference" icon="el-icon-delete" type="danger" :underline="false">删除</el-link>
+							</el-popconfirm>
+						</template>
+					</el-table-column>
+				</template>
+			</ele-data-table>
+		</el-card>
+
+		<!-- 详情弹窗 -->
+		<el-dialog :visible.sync="detailVisible" title="详情" width="700px" :destroy-on-close="true">
+			<el-descriptions :column="2" border v-if="detailData">
+				<el-descriptions-item label="订单号">{{ detailData.order_no }}</el-descriptions-item>
+				<el-descriptions-item label="账户类型">
+					<el-tag v-if="detailData.account_type === 1" type="success" size="mini">会员</el-tag>
+					<el-tag v-else-if="detailData.account_type === 2" type="warning" size="mini">代理</el-tag>
+					<el-tag v-else-if="detailData.account_type === 3" type="info" size="mini">商户</el-tag>
+				</el-descriptions-item>
+				<el-descriptions-item label="类型">
+					<el-tag v-if="detailData.type === 1" type="success" size="mini">充值</el-tag>
+					<el-tag v-else-if="detailData.type === 2" type="primary" size="mini">提现</el-tag>
+				</el-descriptions-item>
+				<el-descriptions-item label="姓名">{{ detailData.realname }}</el-descriptions-item>
+				<el-descriptions-item label="金额">
+					<span :style="{color: detailData.type === 1 ? '#67C23A' : '#F56C6C', fontWeight: 'bold'}">
+						{{ detailData.type === 1 ? '+' : '-' }}¥{{ detailData.money }}
+					</span>
+				</el-descriptions-item>
+				<el-descriptions-item label="实际到账金额">
+					<span style="color: #409EFF; font-weight: bold;">¥{{ detailData.actual_money }}</span>
+				</el-descriptions-item>
+				<el-descriptions-item label="支付方式">
+					<el-tag v-if="detailData.pay_type === 10" type="success" size="mini">微信</el-tag>
+					<el-tag v-else-if="detailData.pay_type === 20" type="primary" size="mini">支付宝</el-tag>
+					<el-tag v-else-if="detailData.pay_type === 50" type="info" size="mini">银行卡</el-tag>
+				</el-descriptions-item>
+				<el-descriptions-item label="支付状态">
+					<el-tag v-if="detailData.pay_status === 10" type="warning" size="mini">待支付</el-tag>
+					<el-tag v-else-if="detailData.pay_status === 20" type="success" size="mini">已支付</el-tag>
+				</el-descriptions-item>
+				<el-descriptions-item label="提现账号" v-if="detailData.type === 2">{{ detailData.account }}</el-descriptions-item>
+				<el-descriptions-item label="账号备注" v-if="detailData.type === 2">{{ detailData.account_remark }}</el-descriptions-item>
+				<el-descriptions-item label="状态">
+					<el-tag v-if="detailData.status === -1" type="info" size="mini">已取消</el-tag>
+					<el-tag v-else-if="detailData.status === 1" type="warning" size="mini">待审核</el-tag>
+					<el-tag v-else-if="detailData.status === 2" type="success" size="mini">已审核/到账</el-tag>
+					<el-tag v-else-if="detailData.status === 3" type="danger" size="mini">审核失败</el-tag>
+				</el-descriptions-item>
+				<el-descriptions-item label="申请时间">{{ detailData.create_time_text }}</el-descriptions-item>
+				<el-descriptions-item label="处理时间">{{ detailData.update_time_text }}</el-descriptions-item>
+				<el-descriptions-item label="付款时间" v-if="detailData.pay_at">{{ detailData.pay_at }}</el-descriptions-item>
+				<el-descriptions-item label="支付交易号" v-if="detailData.transaction_id">{{ detailData.transaction_id }}</el-descriptions-item>
+				<el-descriptions-item label="打款凭证" :span="2" v-if="detailData.pay_img">
+					<el-image :src="detailData.pay_img" style="width: 200px; height: 200px;" :preview-src-list="[detailData.pay_img]"></el-image>
+				</el-descriptions-item>
+				<el-descriptions-item label="审核备注" :span="2" v-if="detailData.confirm_remark">{{ detailData.confirm_remark }}</el-descriptions-item>
+			</el-descriptions>
+		</el-dialog>
+
+		<!-- 审核弹窗 -->
+		<el-dialog :visible.sync="auditVisible" title="审核" width="500px" :destroy-on-close="true">
+			<el-form :model="auditForm" :rules="auditRules" ref="auditForm" label-width="100px">
+				<el-form-item label="金额:">
+					<span style="color: #F56C6C; font-weight: bold; font-size: 18px;">¥{{ auditForm.money }}</span>
+				</el-form-item>
+				<el-form-item label="审核结果:" prop="status">
+					<el-radio-group v-model="auditForm.status">
+						<el-radio :label="2">通过</el-radio>
+						<el-radio :label="3">驳回</el-radio>
+					</el-radio-group>
+				</el-form-item>
+				<el-form-item label="审核备注:" prop="remark">
+					<el-input type="textarea" v-model="auditForm.remark" :rows="3" placeholder="请输入审核备注"/>
+				</el-form-item>
+				<el-form-item label="打款凭证:" v-if="auditForm.status === 2">
+					<upload-image v-model="auditForm.pay_img" :limit="1" />
+					<div class="el-form-item__tip">建议尺寸:800x600像素,支持jpg、png格式</div>
+				</el-form-item>
+			</el-form>
+			<div slot="footer">
+				<el-button @click="auditVisible = false">取消</el-button>
+				<el-button type="primary" @click="submitAudit" :loading="auditLoading">提交</el-button>
+			</div>
+		</el-dialog>
+	</div>
+</template>
+
+<script>
+import { mapGetters } from "vuex";
+import UploadImage from "@/components/uploadImage";
+
+export default {
+	name: "BalanceLogs",
+	components: {
+		UploadImage
+	},
+	props: {
+		// 默认账户类型(1-会员,2-代理,3-商户)
+		defaultAccountType: {
+			type: Number,
+			default: null
+		},
+		// 默认类型(1-充值,2-提现)
+		defaultType: {
+			type: Number,
+			default: 2
+		},
+		// 是否显示账户类型筛选
+		showAccountTypeFilter: {
+			type: Boolean,
+			default: true
+		},
+		// 是否显示类型筛选
+		showTypeFilter: {
+			type: Boolean,
+			default: true
+		},
+		// 页面标题
+		title: {
+			type: String,
+			default: ''
+		},
+		// 权限前缀(如:sys:agentWithdraw, sys:storesWithdraw, sys:balanceLogs)
+		permissionPrefix: {
+			type: String,
+			default: 'sys:balanceLogs'
+		}
+	},
+	computed: {
+		...mapGetters(["permission"])
+	},
+	data() {
+		return {
+			// 表格配置
+			table: {
+				url: '/financial/balanceLogs/index',
+				where: {}
+			},
+			// 当前激活的 Tab
+			activeTab: 'all',
+			// 日期范围
+			dateRange: [],
+			// 表格选中数据
+			selection: [],
+			// 详情弹窗
+			detailVisible: false,
+			detailData: null,
+			// 审核弹窗
+			auditVisible: false,
+			auditForm: {
+				id: null,
+				account_type: null,
+				money: '',
+				status: 2,
+				remark: '',
+				pay_img: ''
+			},
+			auditRules: {
+				status: [
+					{required: true, message: '请选择审核结果', trigger: 'change'}
+				]
+			},
+			auditLoading: false
+		}
+	},
+	mounted() {
+		// 初始化默认筛选条件
+		if (this.defaultAccountType !== null) {
+			this.table.where.account_type = this.defaultAccountType;
+		}
+		if (this.defaultType !== null) {
+			this.table.where.type = this.defaultType;
+		}
+	},
+	watch: {
+		dateRange(val) {
+			if (val && val.length === 2) {
+				this.table.where.start_time = val[0];
+				this.table.where.end_time = val[1];
+			} else {
+				this.table.where.start_time = '';
+				this.table.where.end_time = '';
+			}
+		}
+	},
+	methods: {
+		/* Tab 切换 */
+		handleTabClick(tab) {
+			if (this.activeTab === 'all') {
+				// 全部 - 不筛选状态
+				delete this.table.where.status;
+			} else {
+				// 按状态筛选
+				this.table.where.status = parseInt(this.activeTab);
+			}
+			this.$refs.table.reload();
+		},
+		/* 重置搜索 */
+		reset() {
+			this.table.where = {};
+			this.activeTab = 'all';
+			// 重置时恢复默认值
+			if (this.defaultAccountType !== null) {
+				this.table.where.account_type = this.defaultAccountType;
+			}
+			if (this.defaultType !== null) {
+				this.table.where.type = this.defaultType;
+			}
+			this.dateRange = [];
+			this.$refs.table.reload();
+		},
+		/* 打开详情 */
+		openDetail(row) {
+			this.$http.get('/financial/balanceLogs/info', {params: {id: row.id}}).then(res => {
+				if (res.data.code === 0) {
+					this.detailData = res.data.data;
+					this.detailVisible = true;
+				} else {
+					this.$message.error(res.data.msg);
+				}
+			});
+		},
+		/* 打开审核弹窗 */
+		openAudit(row) {
+			this.auditForm = {
+				id: row.id,
+				account_type: row.account_type,
+				money: row.money,
+				status: 2,
+				remark: '',
+				pay_img: ''
+			};
+			this.auditVisible = true;
+		},
+		/* 提交审核 */
+		submitAudit() {
+			this.$refs.auditForm.validate(valid => {
+				if (valid) {
+					this.auditLoading = true;
+					this.$http.post('/financial/balanceLogs/audit', this.auditForm).then(res => {
+						this.auditLoading = false;
+						if (res.data.code === 0) {
+							this.$message.success(res.data.msg);
+							this.auditVisible = false;
+							this.$refs.table.reload();
+						} else {
+							this.$message.error(res.data.msg);
+						}
+					}).catch(() => {
+						this.auditLoading = false;
+					});
+				}
+			});
+		},
+		/* 删除 */
+		remove(row) {
+			if (!row) {
+				// 批量删除
+				if (!this.selection.length) {
+					this.$message.error('请至少选择一条数据');
+					return;
+				}
+				const ids = this.selection.map(d => d.id);
+				this.$confirm('确定要删除选中的记录吗?', '提示', {type: 'warning'}).then(() => {
+					this.$http.post('/financial/balanceLogs/deleteAll', {ids}).then(res => {
+						if (res.data.code === 0) {
+							this.$message.success(res.data.msg);
+							this.$refs.table.reload();
+						} else {
+							this.$message.error(res.data.msg);
+						}
+					});
+				}).catch(() => {});
+			} else {
+				// 单个删除
+				this.$http.post('/financial/balanceLogs/delete', {id: row.id}).then(res => {
+					if (res.data.code === 0) {
+						this.$message.success(res.data.msg);
+						this.$refs.table.reload();
+					} else {
+						this.$message.error(res.data.msg);
+					}
+				});
+			}
+		}
+	}
+}
+</script>
+
+<style scoped>
+/* Tab 标签页样式 */
+.ele-tabs-card {
+	margin-bottom: 15px;
+}
+.ele-tabs-card >>> .el-tabs__header {
+	margin: 0 0 15px;
+}
+.ele-tabs-card >>> .el-tabs__item {
+	height: 40px;
+	line-height: 40px;
+	font-size: 14px;
+}
+.ele-tabs-card >>> .el-tabs__nav-wrap::after {
+	height: 1px;
+}
+</style>

+ 162 - 0
addons/admin/src/views/system/financial/components/StatisticsTable.vue

@@ -0,0 +1,162 @@
+<template>
+	<div style="margin-top: 20px;">
+		<!-- 筛选条件 -->
+		<el-form :model="query" inline class="filter-form">
+			<el-form-item label="分组方式">
+				<el-select v-model="query.group_by" @change="loadData" style="width: 120px;">
+					<el-option label="按年" value="year"></el-option>
+					<el-option label="按月" value="month"></el-option>
+					<el-option label="按日" value="day"></el-option>
+				</el-select>
+			</el-form-item>
+			<el-form-item label="时间范围">
+				<el-date-picker v-model="query.date_range" type="daterange" range-separator="至" start-placeholder="开始日期"
+					end-placeholder="结束日期" value-format="yyyy-MM-dd" @change="loadData">
+				</el-date-picker>
+			</el-form-item>
+			<el-form-item>
+				<el-button type="primary" icon="el-icon-search" @click="loadData">查询</el-button>
+				<el-button icon="el-icon-refresh" @click="handleReset">重置</el-button>
+			</el-form-item>
+		</el-form>
+
+		<!-- 统计表格 -->
+		<el-table :data="tableData" v-loading="loading" border stripe>
+			<el-table-column prop="date" label="时间" min-width="150" align="center" />
+			<template v-if="type === 'order'">
+				<el-table-column prop="count" label="订单数" min-width="120" align="center" />
+			</template>
+			<template v-else-if="type === 'revenue'">
+				<el-table-column prop="amount" label="营业额" min-width="150" align="center">
+					<template slot-scope="{row}">
+						<span style="color: #67C23A; font-weight: bold;">¥{{ row.amount }}</span>
+					</template>
+				</el-table-column>
+			</template>
+			<template v-else-if="type === 'refund'">
+				<el-table-column prop="count" label="退款订单数" min-width="120" align="center" />
+				<el-table-column prop="amount" label="退款金额" min-width="150" align="center">
+					<template slot-scope="{row}">
+						<span style="color: #F56C6C; font-weight: bold;">¥{{ row.amount }}</span>
+					</template>
+				</el-table-column>
+			</template>
+			<template v-else-if="type === 'settlement'">
+				<el-table-column prop="amount" label="结算金额" min-width="150" align="center">
+					<template slot-scope="{row}">
+						<span style="color: #409EFF; font-weight: bold;">¥{{ row.amount }}</span>
+					</template>
+				</el-table-column>
+			</template>
+			<template v-else-if="type === 'member'">
+				<el-table-column prop="count" label="新增会员数" min-width="120" align="center" />
+			</template>
+		</el-table>
+
+		<!-- 分页 -->
+		<el-pagination v-if="total > 0" @size-change="handleSizeChange" @current-change="handleCurrentChange"
+			:current-page="query.page" :page-sizes="[10, 20, 50, 100]" :page-size="query.limit" :total="total"
+			layout="total, sizes, prev, pager, next, jumper" style="margin-top: 20px; text-align: right;">
+		</el-pagination>
+	</div>
+</template>
+
+<script>
+export default {
+	name: 'StatisticsTable',
+	props: {
+		type: {
+			type: String,
+			required: true
+		}
+	},
+	data() {
+		return {
+			loading: false,
+			query: {
+				group_by: 'day',
+				date_range: [],
+				page: 1,
+				limit: 20
+			},
+			tableData: [],
+			total: 0
+		}
+	},
+	mounted() {
+		// 默认查询最近30天
+		const end = new Date();
+		const start = new Date();
+		start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
+		this.query.date_range = [
+			this.formatDate(start),
+			this.formatDate(end)
+		];
+		this.loadData();
+	},
+	methods: {
+		loadData() {
+			this.loading = true;
+			const params = {
+				type: this.type,
+				group_by: this.query.group_by,
+				page: this.query.page,
+				limit: this.query.limit
+			};
+
+			if (this.query.date_range && this.query.date_range.length === 2) {
+				params.start_date = this.query.date_range[0];
+				params.end_date = this.query.date_range[1];
+			}
+
+			this.$http.get('/financial/list', { params }).then(res => {
+				this.loading = false;
+				if (res.data.code === 0) {
+					this.tableData = res.data.data || [];
+					this.total = res.data.count || 0;
+				} else {
+					this.$message.error(res.data.msg || '数据加载失败');
+				}
+			}).catch(() => {
+				this.loading = false;
+				this.$message.error('数据加载失败');
+			});
+		},
+		handleReset() {
+			const end = new Date();
+			const start = new Date();
+			start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
+			this.query = {
+				group_by: 'day',
+				date_range: [
+					this.formatDate(start),
+					this.formatDate(end)
+				],
+				page: 1,
+				limit: 20
+			};
+			this.loadData();
+		},
+		handleSizeChange(val) {
+			this.query.limit = val;
+			this.loadData();
+		},
+		handleCurrentChange(val) {
+			this.query.page = val;
+			this.loadData();
+		},
+		formatDate(date) {
+			const year = date.getFullYear();
+			const month = String(date.getMonth() + 1).padStart(2, '0');
+			const day = String(date.getDate()).padStart(2, '0');
+			return `${year}-${month}-${day}`;
+		}
+	}
+}
+</script>
+
+<style scoped>
+.filter-form {
+	margin-bottom: 20px;
+}
+</style>

+ 48 - 0
addons/admin/src/views/system/financial/financial.vue

@@ -0,0 +1,48 @@
+<template>
+	<div class="ele-body">
+		<el-card shadow="never">
+			<!-- Tab 切换 -->
+			<el-tabs v-model="activeTab">
+				<el-tab-pane label="订单统计" name="order">
+					<statistics-table type="order" />
+				</el-tab-pane>
+				<el-tab-pane label="营业额统计" name="revenue">
+					<statistics-table type="revenue" />
+				</el-tab-pane>
+				<el-tab-pane label="结算统计" name="settlement">
+					<statistics-table type="settlement" />
+				</el-tab-pane>
+				<el-tab-pane label="会员统计" name="member" v-if="!isStoreUser">
+					<statistics-table type="member" />
+				</el-tab-pane>
+			</el-tabs>
+		</el-card>
+	</div>
+</template>
+
+<script>
+import { mapGetters } from "vuex";
+import StatisticsTable from "./components/StatisticsTable.vue";
+
+export default {
+	name: "Financial",
+	components: {
+		StatisticsTable
+	},
+	computed: {
+		...mapGetters(["permission"]),
+		// 判断是否为商户用户
+		isStoreUser() {
+			return this.$store.state.user.user && this.$store.state.user.user.store_id > 0;
+		}
+	},
+	data() {
+		return {
+			activeTab: 'order'
+		}
+	}
+}
+</script>
+
+<style scoped>
+</style>

+ 24 - 0
addons/admin/src/views/system/financial/withdraw.vue

@@ -0,0 +1,24 @@
+<template>
+	<balance-logs 
+		:default-account-type="2" 
+		:default-type="2"
+		:show-account-type-filter="false"
+		:show-type-filter="false"
+		title="提现管理"
+		permission-prefix="sys:withdraw"
+	/>
+</template>
+
+<script>
+import BalanceLogs from './balanceLogs.vue';
+
+export default {
+	name: "AgentWithdraw",
+	components: {
+		BalanceLogs
+	}
+}
+</script>
+
+<style scoped>
+</style>

+ 0 - 52
app/Http/Controllers/Admin/ArticleConsultRecordController.php

@@ -1,52 +0,0 @@
-<?php
-
-namespace App\Http\Controllers\Admin;
-
-use App\Services\Common\ArticleConsultRecordService;
-
-/**
- * 行业咨询记录
- */
-class ArticleConsultRecordController extends Backend
-{
-    public function __construct()
-    {
-        parent::__construct();
-        $this->service = new ArticleConsultRecordService();
-    }
-
-    /**
-     * 列表
-     */
-    public function index()
-    {
-        $params = request()->all();
-        $pageSize = isset($params['limit']) ? intval($params['limit']) : PERPAGE;
-        return $this->service->getDataList($params, $pageSize);
-    }
-
-    /**
-     * 编辑(处理投诉)
-     */
-    public function edit()
-    {
-        return $this->service->edit();
-    }
-
-    /**
-     * 删除
-     */
-    public function delete()
-    {
-        return $this->service->delete();
-    }
-
-    /**
-     * 修改状态
-     */
-    public function status()
-    {
-        return $this->service->status($this->userId);
-    }
-
-}

+ 0 - 70
app/Http/Controllers/Admin/MeetingController.php

@@ -1,70 +0,0 @@
-<?php
-
-namespace App\Http\Controllers\Admin;
-
-
-use App\Services\Common\MeetingService;
-
-/**
- * 会议
- */
-class MeetingController extends Backend
-{
-    public function __construct()
-    {
-        parent::__construct();
-        $this->service = new MeetingService();
-    }
-
-    /**
-     * 列表
-     */
-    public function index()
-    {
-        $params = request()->all();
-        $pageSize = isset($params['limit']) ? intval($params['limit']) : PERPAGE;
-        return $this->service->getDataList($params, $pageSize);
-    }
-
-    /**
-     * 编辑(处理投诉)
-     */
-    public function edit()
-    {
-        return $this->service->edit();
-    }
-
-    /**
-     * 删除
-     */
-    public function delete()
-    {
-        return $this->service->delete();
-    }
-
-    /**
-     * 修改状态
-     */
-    public function status()
-    {
-        return $this->service->status();
-    }
-
-    /**
-     * 获取详情
-     */
-    public function info()
-    {
-        $id = request()->get('id');
-        return $this->service->getInfo($id);
-    }
-
-    /**
-     * 获取
-     */
-    public function qrcode()
-    {
-        $id = request()->get('id');
-        return $this->service->getInfo($id);
-    }
-}

+ 0 - 62
app/Http/Controllers/Admin/MeetingRecordController.php

@@ -1,62 +0,0 @@
-<?php
-
-namespace App\Http\Controllers\Admin;
-
-use App\Services\Common\ComplaintService;
-use App\Services\Common\MeetingRecordService;
-use App\Services\Common\MeetingService;
-
-/**
- * 会议签到记录
- */
-class MeetingRecordController extends Backend
-{
-    public function __construct()
-    {
-        parent::__construct();
-        $this->service = new MeetingRecordService();
-    }
-
-    /**
-     * 列表
-     */
-    public function index()
-    {
-        $params = request()->all();
-        $pageSize = isset($params['limit']) ? intval($params['limit']) : PERPAGE;
-        return $this->service->getDataList($params, $pageSize);
-    }
-
-    /**
-     * 编辑(处理投诉)
-     */
-    public function edit()
-    {
-        return $this->service->edit();
-    }
-
-    /**
-     * 删除
-     */
-    public function delete()
-    {
-        return $this->service->delete();
-    }
-
-    /**
-     * 修改状态
-     */
-    public function status()
-    {
-        return $this->service->status();
-    }
-
-    /**
-     * 获取详情
-     */
-    public function info()
-    {
-        $id = request()->get('id');
-        return $this->service->getInfo($id);
-    }
-}

+ 0 - 53
app/Http/Controllers/Admin/SupervisorConsultRecordController.php

@@ -1,53 +0,0 @@
-<?php
-
-namespace App\Http\Controllers\Admin;
-
-use App\Services\Common\MeetingRecordService;
-use App\Services\Common\SupervisorConsultRecordService;
-
-/**
- * 导师咨询记录
- */
-class SupervisorConsultRecordController extends Backend
-{
-    public function __construct()
-    {
-        parent::__construct();
-        $this->service = new SupervisorConsultRecordService();
-    }
-
-    /**
-     * 列表
-     */
-    public function index()
-    {
-        $params = request()->all();
-        $pageSize = isset($params['limit']) ? intval($params['limit']) : PERPAGE;
-        return $this->service->getDataList($params, $pageSize);
-    }
-
-    /**
-     * 编辑(处理投诉)
-     */
-    public function edit()
-    {
-        return $this->service->edit();
-    }
-
-    /**
-     * 删除
-     */
-    public function delete()
-    {
-        return $this->service->delete();
-    }
-
-    /**
-     * 修改状态
-     */
-    public function status()
-    {
-        return $this->service->status($this->userId);
-    }
-
-}

+ 0 - 111
app/Http/Controllers/Admin/SupervisorsController.php

@@ -1,111 +0,0 @@
-<?php
-// +----------------------------------------------------------------------
-// | LARAVEL8.0 框架 [ LARAVEL ][ RXThinkCMF ]
-// +----------------------------------------------------------------------
-// | 版权所有 2017~2021 LARAVEL研发中心
-// +----------------------------------------------------------------------
-// | 官方网站: http://www.laravel.cn
-// +----------------------------------------------------------------------
-// | Author: laravel开发员 <laravel.qq.com>
-// +----------------------------------------------------------------------
-
-namespace App\Http\Controllers\Admin;
-use App\Services\Common\SupervisorsService;
-
-/**
- * 导师管理-控制器
- * @author laravel开发员
- * @since 2020/11/11
- * @package App\Http\Controllers
- */
-class SupervisorsController extends Backend
-{
-    /**
-     * 构造函数
-     * @author laravel开发员
-     * @since 2020/11/11
-     * MemberController constructor.
-     */
-    public function __construct()
-    {
-        parent::__construct();
-        $this->service = new SupervisorsService();
-    }
-
-    /**
-     * 列表
-     * @return array
-     */
-    public function index()
-    {
-        $result = $this->service->getList();
-        return showJson($result['msg'], $result['code'] == 0, $result['data'] ?? [], $result['count'] ?? 0);
-    }
-
-    /**
-     * 选项列表
-     * @return mixed
-     */
-    public function options(){
-        $result = $this->service->options();
-        return message(1002,true, $result);
-    }
-
-    /**
-     * 搜索用户(用于下拉选择)
-     * @return array
-     */
-    public function search()
-    {
-        $keyword = request()->input('keyword', '');
-        $limit = request()->input('limit', 20);
-        
-        $result = $this->service->searchUsers($keyword, $limit);
-        return showJson($result['msg'], $result['code'] == 0, $result['data'] ?? []);
-    }
-
-    /**
-     * 获取详情
-     */
-    public function read()
-    {
-        $result = $this->service->getInfo();
-        return showJson($result['msg'], $result['code'] == 0, $result['data'] ?? []);
-    }
-
-    /**
-     * 添加
-     */
-    public function add()
-    {
-        $result = $this->service->edit();
-        return showJson($result['msg'], $result['code'] == 0);
-    }
-
-    /**
-     * 设置状态
-     */
-    public function status()
-    {
-        $result = $this->service->status();
-        return showJson($result['msg'], $result['code'] == 0);
-    }
-
-    /**
-     * 设置认证
-     */
-    public function auth()
-    {
-        $result = $this->service->auth();
-        return showJson($result['msg'], $result['code'] == 0);
-    }
-
-    /**
-     * 设置推荐
-     */
-    public function recommend()
-    {
-        $result = $this->service->recommend();
-        return showJson($result['msg'], $result['code'] == 0);
-    }
-}

+ 0 - 84
app/Http/Controllers/Api/v1/SupervisorsController.php

@@ -1,84 +0,0 @@
-<?php
-
-namespace App\Http\Controllers\Api\v1;
-
-use App\Http\Controllers\Api\webApp;
-use App\Http\Validator\ConsultValidator;
-use App\Services\Api\SupervisorsService;
-
-/**
- * 导师信息
- * @package App\Http\Controllers\Api
- */
-class SupervisorsController extends webApp
-{
-
-    /**
-     * 列表
-     * @return array
-     */
-    public function index()
-    {
-        $params =request()->post();
-        $pageSize = request()->post('pageSize', 15);
-        $datas = SupervisorsService::make()->getDataList($params, $pageSize);
-        return showJson(1010, true, $datas);
-    }
-
-    /**
-     * 分类
-     * @return array
-     */
-    public function categorys()
-    {
-        $datas = [
-            ['id'=>1,'name'=>'专项导师'],
-            ['id'=>2,'name'=>'研究导师'],
-            ['id'=>3,'name'=>'发展导师'],
-        ];
-        return message(1010, true, $datas);
-    }
-
-    /**
-     * 详情
-     * @return array
-     */
-    public function info()
-    {
-        $params = request()->all();
-        $id = isset($params['id'])? $params['id'] : 0;
-        try {
-            if(!$result = SupervisorsService::make()->getInfo($id, $this->userId)){
-                return showJson(1009, false);
-            }else{
-                return showJson(1010, true, $result);
-            }
-        } catch (\Exception $exception) {
-            $error = ['data' => $exception->getTrace(), 'err' => $exception->getMessage()];
-            return showJson(1046, false, $error);
-        }
-    }
-
-    /**
-     * 咨询提交
-     */
-    public function consult(ConsultValidator $validator)
-    {
-        $params = request()->post();
-        $params = $validator->check($params, 'submit');
-        if (!is_array($params)) {
-            return showJson($params, false);
-        }
-
-        try {
-            if (SupervisorsService::make()->consultSubmit($this->userId, $params)) {
-                return showJson(SupervisorsService::make()->getError(), true);
-            } else {
-                return showJson(SupervisorsService::make()->getError(), false);
-            }
-        }  catch (\Exception $exception) {
-            $error = ['data' => $exception->getTrace(), 'err' => $exception->getMessage()];
-            return showJson(1046, false, $error);
-        }
-    }
-}

+ 1 - 0
app/Models/OrderModel.php

@@ -98,6 +98,7 @@ class OrderModel extends BaseModel
     public function subOrders()
     {
         return $this->hasMany(OrderModel::class, 'main_no', 'order_no')
+            ->with(['user'])
             ->where(['mark' => 1]);
     }
 }

+ 344 - 0
app/Services/Common/BalanceLogService.php

@@ -0,0 +1,344 @@
+<?php
+// +----------------------------------------------------------------------
+// | LARAVEL8.0 框架 [ LARAVEL ][ RXThinkCMF ]
+// +----------------------------------------------------------------------
+// | 版权所有 2017~2021 LARAVEL研发中心
+// +----------------------------------------------------------------------
+// | 官方网站: http://www.laravel.cn
+// +----------------------------------------------------------------------
+// | Author: laravel开发员 <laravel.qq.com>
+// +----------------------------------------------------------------------
+
+namespace App\Services\Common;
+
+use App\Models\AccountLogModel;
+use App\Models\ActionLogModel;
+use App\Models\BalanceLogModel;
+use App\Models\MemberModel;
+use App\Models\MessageModel;
+use App\Services\BaseService;
+use App\Services\RedisService;
+use Illuminate\Support\Facades\DB;
+
+/**
+ * 余额管理-服务类
+ * @author laravel开发员
+ * @since 2020/11/11
+ * @package App\Services\Common
+ */
+class BalanceLogService extends BaseService
+{
+    public static $instance = null;
+
+    /**
+     * 构造函数
+     * @author laravel开发员
+     * @since 2020/11/11
+     * AccountService constructor.
+     */
+    public function __construct()
+    {
+        $this->model = new BalanceLogModel();
+    }
+
+    /**
+     * 静态入口
+     * @return static|null
+     */
+    public static function make()
+    {
+        if (!self::$instance) {
+            self::$instance = (new static());
+        }
+        return self::$instance;
+    }
+
+    /**
+     * @param $params
+     * @param int $pageSize
+     * @return array
+     */
+    public function getDataList($params, $pageSize = 15)
+    {
+        $query = $this->getQuery($params);
+
+        $model = clone $query;
+        $counts = [
+            'count'=> $model->count('a.id'),
+            'total'=> $model->sum('a.money'),
+        ];
+
+        $list = $query->select(['a.*'])
+            ->orderBy('a.create_time','desc')
+            ->orderBy('a.id','desc')
+            ->paginate($pageSize > 0 ? $pageSize : 9999999);
+        $list = $list? $list->toArray() :[];
+        if($list){
+            foreach($list['data'] as &$item){
+                $item['create_time'] = $item['create_time']? datetime($item['create_time'],'Y-m-d H.i.s') : '';
+                $item['pay_img'] = $item['pay_img']? get_image_url($item['pay_img']) : '';
+                $item['member'] = isset($item['member'])? $item['member'] : [];
+            }
+        }
+
+        return [
+            'pageSize'=> $pageSize,
+            'total'=>isset($list['total'])? $list['total'] : 0,
+            'counts'=>$counts,
+            'list'=> isset($list['data'])? $list['data'] : []
+        ];
+    }
+
+    /**
+     * 查询
+     * @param $params
+     * @return \Illuminate\Database\Eloquent\Builder
+     */
+    public function getQuery($params)
+    {
+        $where = ['a.mark' => 1];
+        $type = isset($params['type'])? $params['type'] : 0;
+        if($type>0){
+            $where['a.type'] = $type;
+        }
+
+       return $this->model->with(['member'])->from("balance_logs as a")
+            ->leftJoin('member as b','b.id','=','a.user_id')
+            ->where($where)
+            ->where(function ($query) use($params) {
+                $keyword = isset($params['keyword']) ? $params['keyword'] : '';
+                $userId = isset($params['user_id'])? $params['user_id'] : 0;
+                if($userId){
+                    $query->where('a.user_id',$userId);
+                }
+
+                if ($keyword) {
+                    $query->where(function($query) use($keyword){
+                        $query->where('b.nickname','like',"%{$keyword}%")
+                            ->orWhere('b.mobile','like',"%{$keyword}%");
+                    });
+                }
+
+                $orderNo = isset($params['order_no'])? trim($params['order_no']) : '';
+                if($orderNo){
+                    $query->where(function($query) use($orderNo){
+                        $query->where('a.order_no','like',"%{$orderNo}%");
+                    });
+                }
+
+                $status = isset($params['status'])? $params['status'] : 0;
+                if($status==0){
+                    $query->whereIn('a.status',[2,4]);
+                }else if($status<0){
+                    $query->whereIn('a.status', [1,3]);
+                }else if($status>0){
+                    $query->where('a.status', $status);
+                }
+            })
+            ->where(function ($query) use($params){
+
+                // 日期
+                $date = isset($params['date']) ? $params['date'] : [];
+                $start = isset($date[0])? $date[0] : '';
+                $end = isset($date[1])? $date[1] : '';
+                $end = $start>=$end? '' : $end;
+                if ($start) {
+                    $query->where('a.create_time','>=', strtotime($start));
+                }
+                if($end){
+                    $query->where('a.create_time','<=', strtotime($end));
+                }
+            });
+    }
+
+
+    /**
+     * 添加或编辑
+     * @return array
+     * @since 2020/11/11
+     * @author laravel开发员
+     */
+    public function edit()
+    {
+        $data = request()->all();
+        return parent::edit($data); // TODO: Change the autogenerated stub
+    }
+
+    /**
+     * 审核
+     * @param $adminId
+     * @param $params
+     * @return array|false
+     */
+    public function confirm($adminId, $params)
+    {
+        $id = isset($params['id'])? intval($params['id']) : 0;
+        $status = isset($params['status'])? intval($params['status']) : 0;
+        $payStatus = isset($params['pay_status'])? intval($params['pay_status']) : 0;
+        $confirmRemark = isset($params['confirm_remark'])? trim($params['confirm_remark']) : '';
+        $payImg = isset($params['pay_img'])? trim($params['pay_img']) : '';
+        $payImg = $payImg? get_image_path($payImg) : '';
+
+        $info = $this->model->with(['member'])->where(['id'=> $id,'mark'=>1])->first();
+        $userInfo = isset($info['member'])? $info['member'] : [];
+        $orderStatus = isset($info['status'])? $info['status'] : 0;
+        $orderUserId = isset($info['user_id'])? $info['user_id'] : 0;
+        $balance = isset($userInfo['balance'])? $userInfo['balance'] : 0;
+        $money = isset($info['money'])? $info['money'] : 0;
+        if(empty($info) || empty($userInfo) || $orderUserId<=0 || $money<=0){
+            $this->error = '提现信息不存在或参数错误';
+            return false;
+        }
+
+        if($orderStatus != 1){
+            $this->error = '提现订单状态不可操作';
+            return false;
+        }
+
+        if(!in_array($status,[2,3])){
+            $this->error = '审核状态错误';
+            return false;
+        }
+
+        if($status == 3 && empty($confirmRemark)){
+            $this->error = '请填写审核驳回备注';
+            return false;
+        }
+
+        DB::beginTransaction();
+        $status = $payStatus==20 && $status==2?4:$status;
+        $updateData = ['status'=>$status,'pay_status'=> $payStatus,'pay_img'=> $payImg,'confirm_admin_id'=>$adminId,'update_time'=>time(),'confirm_remark'=>$confirmRemark];
+        if(!$this->model->where(['id'=> $id])->update($updateData)){
+            DB::rollBack();
+            $this->error = '提现审核失败';
+            return false;
+        }
+
+        // 如果驳回
+        if($status == 3){
+            $updateData = ['balance'=>DB::raw("balance + {$money}"),'update_time'=>time()];
+            if(!MemberModel::where(['id'=> $orderUserId])->update($updateData)){
+                DB::rollBack();
+                $this->error = '提现审核处理失败';
+                return false;
+            }
+
+            $log = [
+                'user_id' => $orderUserId,
+                'source_order_no' => isset($info['order_no']) ? $info['order_no'] : '',
+                'type' => 5,
+                'money' => $money,
+                'after_money' => moneyFormat($balance+$money,2),
+                'date'=> date('Y-m-d'),
+                'create_time' => time(),
+                'remark' => '提现驳回',
+                'status' => 1,
+                'mark' => 1,
+            ];
+            if(!AccountLogModel::insertGetId($log)){
+                DB::rollBack();
+                $this->error = '提现审核处理失败';
+                return false;
+            }
+        }else if($status == 2){
+            // 线上直接打款逻辑
+        }
+
+
+
+        DB::commit();
+        $this->error = '提现审核成功';
+        return ['id'=>$id,'money'=>$money,'status'=>$status];
+    }
+
+    /**
+     * 打款
+     * @param $adminId
+     * @param $params
+     * @return array|false
+     */
+    public function payment($adminId, $params)
+    {
+        $id = isset($params['id'])? intval($params['id']) : 0;
+        $payStatus = isset($params['pay_status'])? intval($params['pay_status']) : 0;
+        $payImg = isset($params['pay_img'])? trim($params['pay_img']) : '';
+        $payImg = $payImg? get_image_path($payImg) : '';
+
+        $info = $this->model->with(['member'])->where(['id'=> $id,'mark'=>1])->first();
+        $userInfo = isset($info['member'])? $info['member'] : [];
+        $orderStatus = isset($info['status'])? $info['status'] : 0;
+        $orderUserId = isset($info['user_id'])? $info['user_id'] : 0;
+        $balance = isset($userInfo['balance'])? $userInfo['balance'] : 0;
+        $money = isset($info['money'])? $info['money'] : 0;
+        if(empty($info) || empty($userInfo) || $orderUserId<=0 || $money<=0){
+            $this->error = '提现信息不存在或参数错误';
+            return false;
+        }
+
+        if($orderStatus != 2){
+            $this->error = '提现订单状态不可操作';
+            return false;
+        }
+
+        if($payStatus != 20){
+            $this->error = '请选择已打款';
+            return false;
+        }
+
+
+        DB::beginTransaction();
+        $updateData = ['pay_status'=> $payStatus,'pay_img'=> $payImg,'update_time'=>time()];
+        if($payStatus == 20){
+            $updateData['status'] = 4;
+        }
+        if(!$this->model->where(['id'=> $id])->update($updateData)){
+            DB::rollBack();
+            $this->error = '提现打款处理失败';
+            return false;
+        }
+
+        DB::commit();
+        $this->error = '提现打款状态更新成功';
+        return ['id'=>$id,'money'=>$money,'status'=>$payStatus];
+    }
+
+    /**
+     * 删除
+     * @return array
+     */
+    public function delete()
+    {
+        // 设置日志标题
+        ActionLogModel::setRecord(session('userId'), ['type' => 1, 'title' => "删除余额明细", 'content' => json_encode(request()->post(), 256), 'module' => 'admin']);
+        ActionLogModel::record();
+        $this->model->where('mark', 0)->where('update_time', '<=', time() - 7 * 86400)->delete();
+        return parent::delete(); // TODO: Change the autogenerated stub
+    }
+
+    /*\提现统计
+     * @param int $type
+     * @return array|mixed
+     */
+    public function getTotal($type=1)
+    {
+        $cacheKey = "caches:balances:total_{$type}";
+        $data = RedisService::get($cacheKey);
+        if($data){
+            return $data;
+        }
+
+        $data = $this->model->where(['mark'=>1])
+            ->where(function($query) use($type){
+                if($type== 1){
+                    $query->where(['type'=>2])->whereIn('status',[2,4]);
+                }else {
+                    $query->where(['type'=>2]);
+                }
+            })->sum('money');
+        if($data){
+            RedisService::set($cacheKey, $data, rand(300, 600));
+        }
+
+        return $data;
+    }
+}

+ 0 - 1
routes/web.php

@@ -233,7 +233,6 @@ Route::post('/withdraw/status', [\App\Http\Controllers\Admin\WithdrawController:
 Route::post('/withdraw/delete', [\App\Http\Controllers\Admin\WithdrawController::class, 'delete']);
 
 
-
 // 操作日志
 Route::get('/actionlog/index', [ActionLogController::class, 'index']);
 Route::post('/actionlog/delete', [ActionLogController::class, 'delete']);