功能:礼物功能基本搭建好

This commit is contained in:
xiao12feng8 2026-01-03 19:22:42 +08:00
parent 8b377c53e2
commit b08765e035
62 changed files with 4436 additions and 284 deletions

54
FINAL_FIX_LIVE_404.sql Normal file
View File

@ -0,0 +1,54 @@
-- ========================================
-- 最终修复直播管理404问题
-- ========================================
-- 问题根源:
-- 礼物打赏菜单(id=823)的component是 /liveManage/gift/records/index
-- 这与前端独立的 giftManageRouter (/gift) 冲突
-- 解决方案:将礼物打赏从直播管理中移除
-- 步骤1隐藏直播管理下的礼物打赏菜单
UPDATE eb_system_menu
SET is_show = 0
WHERE id = 823
AND pid = 675
AND name = '礼物打赏';
-- 步骤2确保直播管理菜单本身是显示的
UPDATE eb_system_menu
SET is_show = 1
WHERE id = 675
AND name = '直播管理';
-- 步骤3确保所有直播管理的子菜单都是显示的除了礼物打赏
UPDATE eb_system_menu
SET is_show = 1
WHERE pid = 675
AND id != 823;
-- 验证修复结果
SELECT
'=== 直播管理菜单 ===' as section,
id,
pid,
name,
component,
is_show,
sort,
CASE WHEN is_show = 1 THEN '✓ 显示' ELSE '✗ 隐藏' END as status
FROM eb_system_menu
WHERE id = 675 OR pid = 675
ORDER BY pid, sort;
-- 检查是否还有其他冲突
SELECT
'=== 可能的路径冲突 ===' as section,
component,
COUNT(*) as count,
GROUP_CONCAT(name SEPARATOR ' | ') as menu_names
FROM eb_system_menu
WHERE is_show = 1
AND component LIKE '/liveManage%'
GROUP BY component
HAVING count > 1;

View File

@ -1,97 +1,125 @@
import request from '@/utils/request';
import request from '@/utils/request'
/**
* 礼物列表
*/
// 获取礼物打赏记录列表
export function getGiftRecords(params) {
return request({
url: '/admin/gift/records',
method: 'get',
params
})
}
// 获取礼物统计数据
export function getGiftStatistics() {
return request({
url: '/admin/gift/statistics',
method: 'get'
})
}
// 获取礼物配置列表
export function getGiftConfigList() {
return request({
url: '/admin/gift/config/list',
method: 'get'
})
}
// 添加礼物配置
export function addGiftConfig(data) {
return request({
url: '/admin/gift/config/add',
method: 'post',
data
})
}
// 更新礼物配置
export function updateGiftConfig(data) {
return request({
url: '/admin/gift/config/update',
method: 'post',
data
})
}
// 删除礼物配置
export function deleteGiftConfig(id) {
return request({
url: `/admin/gift/config/delete/${id}`,
method: 'post'
})
}
// 获取充值套餐列表
export function getRechargePackages() {
return request({
url: '/admin/gift/recharge/packages',
method: 'get'
})
}
// 更新充值套餐
export function updateRechargePackage(data) {
return request({
url: '/admin/gift/recharge/package/update',
method: 'post',
data
})
}
// 获取充值记录
export function getRechargeRecords(params) {
return request({
url: '/admin/gift/recharge/records',
method: 'get',
params
})
}
// ========== 礼物管理 ==========
// 获取礼物列表
export function giftListApi(params) {
return request({
url: '/admin/gift/list',
method: 'get',
params
});
})
}
/**
* 礼物详情
*/
export function giftDetailApi(id) {
return request({
url: `/admin/gift/detail/${id}`,
method: 'get'
});
}
/**
* 添加礼物
*/
// 添加礼物
export function giftAddApi(data) {
return request({
url: '/admin/gift/add',
method: 'post',
data
});
})
}
/**
* 更新礼物
*/
export function giftUpdateApi(id, data) {
// 更新礼物
export function giftUpdateApi(data) {
return request({
url: `/admin/gift/update/${id}`,
url: '/admin/gift/update',
method: 'post',
data
});
})
}
/**
* 更新礼物状态
*/
// 删除礼物
export function giftDeleteApi(ids) {
return request({
url: '/admin/gift/delete',
method: 'post',
data: { ids: Array.isArray(ids) ? ids : [ids] }
})
}
// 更新礼物状态
export function giftStatusApi(id, status) {
return request({
url: `/admin/gift/status/${id}`,
url: '/admin/gift/status',
method: 'post',
params: { status }
});
}
/**
* 更新心动礼物状态
*/
export function giftHeartbeatApi(id, isHeartbeat) {
return request({
url: `/admin/gift/heartbeat/${id}`,
method: 'post',
params: { isHeartbeat }
});
}
/**
* 删除礼物
*/
export function giftDeleteApi(id) {
return request({
url: `/admin/gift/${id}`,
method: 'delete'
});
}
/**
* 批量删除礼物
*/
export function giftBatchDeleteApi(ids) {
return request({
url: '/admin/gift/batch-delete',
method: 'post',
data: { ids }
});
}
/**
* 礼物统计
*/
export function giftStatisticsApi() {
return request({
url: '/admin/gift/statistics',
method: 'get'
});
data: { id, status }
})
}

View File

@ -21,7 +21,6 @@ import monitorRouter from './modules/monitor'; // 数据监控
import userManageRouter from './modules/userManage'; // 用户管理
import liveManageRouter from './modules/liveManage'; // 直播管理
import socialManageRouter from './modules/socialManage'; // 社交互动
import giftManageRouter from './modules/giftManage'; // 礼物打赏
import virtualPropsRouter from './modules/virtualProps'; // 虚拟道具
import activityManageRouter from './modules/activityManage'; // 营销活动
import taskManageRouter from './modules/taskManage'; // 任务系统
@ -86,9 +85,7 @@ export const constantRoutes = [
liveManageRouter,
// 4. 社交互动
socialManageRouter,
// 5. 礼物打赏
giftManageRouter,
// 6. 虚拟道具
// 5. 虚拟道具
virtualPropsRouter,
// 7. 营销活动
activityManageRouter,

View File

@ -1,40 +1,24 @@
import Layout from '@/layout';
/**
* 礼物打赏 - 整合路由
* 包含礼物礼物数量打赏记录
* 礼物打赏管理
*/
const giftManageRouter = {
path: '/giftManage',
path: '/gift',
component: Layout,
redirect: '/giftManage/list',
redirect: '/gift/records/index',
name: 'GiftManage',
alwaysShow: true,
meta: {
title: '礼物打赏',
icon: 'el-icon-present',
},
children: [
// 礼物列表
{
path: 'list',
component: () => import('@/views/gift/index'),
name: 'GiftList',
meta: { title: '礼物列表', icon: '' },
},
// 礼物数量
{
path: 'num',
component: () => import('@/views/giftnum/index'),
name: 'GiftNum',
meta: { title: '礼物数量', icon: '' },
},
// 打赏记录
{
path: 'reward/record',
component: () => import('@/views/giftreward/record/index'),
name: 'GiftRewardRecord',
meta: { title: '打赏记录', icon: '' },
path: 'records/index',
component: () => import('@/views/gift/records/index.vue'),
name: 'GiftRecords',
meta: { title: '打赏记录', icon: 'el-icon-document' },
},
],
};

View File

@ -18,43 +18,57 @@ const liveManageRouter = {
// 房间管理(包含分类管理)
{
path: 'room/list',
component: () => import('@/views/room/list/index'),
component: () => import('@/views/room/list/index.vue'),
name: 'RoomList',
meta: { title: '房间列表', icon: '' },
},
// 家族管理
{
path: 'family/list',
component: () => import('@/views/family/list/index'),
component: () => import('@/views/family/list/index.vue'),
name: 'FamilyList',
meta: { title: '家族列表', icon: '' },
},
{
path: 'family/level',
component: () => import('@/views/family/level/index'),
component: () => import('@/views/family/level/index.vue'),
name: 'FamilyLevel',
meta: { title: '家族级别', icon: '' },
},
{
path: 'family/member',
component: () => import('@/views/family/member/index'),
component: () => import('@/views/family/member/index.vue'),
name: 'FamilyMember',
meta: { title: '家族成员', icon: '' },
},
// 粉丝团
{
path: 'fanGroup/list',
component: () => import('@/views/fanGroup/list/index'),
component: () => import('@/views/fanGroup/list/index.vue'),
name: 'FanGroupList',
meta: { title: '粉丝团管理', icon: '' },
},
// 主播管理
{
path: 'streamer/list',
component: () => import('@/views/streamer/list/index'),
component: () => import('@/views/streamer/list/index.vue'),
name: 'StreamerList',
meta: { title: '主播管理', icon: '' },
},
// 礼物打赏
{
path: 'gift/records/index',
component: () => import('@/views/gift/records/index.vue'),
name: 'GiftRecords',
meta: { title: '打赏记录', icon: '' },
},
// 礼物管理
{
path: 'gift/manage/index',
component: () => import('@/views/gift/manage/index.vue'),
name: 'GiftManage',
meta: { title: '礼物管理', icon: '' },
},
],
};

View File

@ -0,0 +1,200 @@
<template>
<div class="app-container">
<!-- 操作按钮 -->
<el-row style="margin-bottom: 20px;">
<el-button type="primary" icon="el-icon-plus" @click="handleAdd">添加礼物</el-button>
</el-row>
<!-- 礼物列表 -->
<el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%;">
<el-table-column label="ID" width="80" align="center">
<template slot-scope="{row}">{{ row.id }}</template>
</el-table-column>
<el-table-column label="礼物图标" width="100" align="center">
<template slot-scope="{row}">
<img v-if="row.icon" :src="row.icon" style="width: 50px; height: 50px; object-fit: cover;">
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="礼物名称" width="150" align="center">
<template slot-scope="{row}">{{ row.name }}</template>
</el-table-column>
<el-table-column label="价格(虚拟币)" width="150" align="center">
<template slot-scope="{row}">
<span style="color: #F56C6C; font-weight: bold;">{{ row.price }}</span>
</template>
</el-table-column>
<el-table-column label="排序" width="100" align="center">
<template slot-scope="{row}">{{ row.sort_order }}</template>
</el-table-column>
<el-table-column label="状态" width="100" align="center">
<template slot-scope="{row}">
<el-tag :type="row.is_enabled === 1 ? 'success' : 'danger'">
{{ row.is_enabled === 1 ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="创建时间" width="180" align="center">
<template slot-scope="{row}">{{ row.create_time }}</template>
</el-table-column>
<el-table-column label="操作" align="center" min-width="200">
<template slot-scope="{row}">
<el-button type="primary" size="mini" @click="handleEdit(row)">编辑</el-button>
<el-button v-if="row.is_enabled === 1" type="warning" size="mini" @click="handleToggleStatus(row, 0)">禁用</el-button>
<el-button v-else type="success" size="mini" @click="handleToggleStatus(row, 1)">启用</el-button>
<el-button type="danger" size="mini" @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 添加/编辑对话框 -->
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="600px">
<el-form ref="form" :model="form" :rules="rules" label-width="120px">
<el-form-item label="礼物名称" prop="name">
<el-input v-model="form.name" placeholder="请输入礼物名称" />
</el-form-item>
<el-form-item label="礼物图标" prop="icon">
<el-input v-model="form.icon" placeholder="请输入图标URL" />
</el-form-item>
<el-form-item label="价格" prop="price">
<el-input-number v-model="form.price" :min="0" :precision="2" placeholder="请输入价格" />
</el-form-item>
<el-form-item label="动画效果">
<el-input v-model="form.animation" placeholder="请输入动画效果URL可选" />
</el-form-item>
<el-form-item label="排序">
<el-input-number v-model="form.sortOrder" :min="0" placeholder="数字越小越靠前" />
</el-form-item>
<el-form-item label="状态">
<el-radio-group v-model="form.isEnabled">
<el-radio :label="1">启用</el-radio>
<el-radio :label="0">禁用</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div slot="footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit">确定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { getGiftConfigList, addGiftConfig, updateGiftConfig, deleteGiftConfig } from '@/api/gift'
export default {
name: 'GiftConfig',
data() {
return {
list: [],
listLoading: false,
dialogVisible: false,
dialogTitle: '添加礼物',
form: {
id: null,
name: '',
icon: '',
price: 0,
animation: '',
sortOrder: 0,
isEnabled: 1
},
rules: {
name: [{ required: true, message: '请输入礼物名称', trigger: 'blur' }],
icon: [{ required: true, message: '请输入图标URL', trigger: 'blur' }],
price: [{ required: true, message: '请输入价格', trigger: 'blur' }]
}
}
},
created() {
this.getList()
},
methods: {
getList() {
this.listLoading = true
getGiftConfigList().then(response => {
this.list = response || []
this.listLoading = false
}).catch(() => {
this.listLoading = false
})
},
handleAdd() {
this.dialogTitle = '添加礼物'
this.form = {
id: null,
name: '',
icon: '',
price: 0,
animation: '',
sortOrder: 0,
isEnabled: 1
}
this.dialogVisible = true
},
handleEdit(row) {
this.dialogTitle = '编辑礼物'
this.form = {
id: row.id,
name: row.name,
icon: row.icon,
price: row.price,
animation: row.animation || '',
sortOrder: row.sort_order,
isEnabled: row.is_enabled
}
this.dialogVisible = true
},
handleSubmit() {
this.$refs.form.validate(valid => {
if (valid) {
const api = this.form.id ? updateGiftConfig : addGiftConfig
api(this.form).then(() => {
this.$message.success(this.form.id ? '更新成功' : '添加成功')
this.dialogVisible = false
this.getList()
})
}
})
},
handleToggleStatus(row, status) {
const form = {
id: row.id,
name: row.name,
icon: row.icon,
price: row.price,
animation: row.animation || '',
sortOrder: row.sort_order,
isEnabled: status
}
updateGiftConfig(form).then(() => {
this.$message.success('状态更新成功')
this.getList()
})
},
handleDelete(row) {
this.$confirm('确定要删除这个礼物吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteGiftConfig(row.id).then(() => {
this.$message.success('删除成功')
this.getList()
})
})
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,451 @@
<template>
<div class="app-container">
<!-- 统计卡片 -->
<el-row :gutter="20" class="mb20">
<el-col :span="6">
<el-card shadow="hover">
<div class="stat-card">
<i class="el-icon-present stat-icon" style="color: #409EFF"></i>
<div class="stat-content">
<div class="stat-value">{{ statistics.total }}</div>
<div class="stat-label">礼物总数</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card shadow="hover">
<div class="stat-card">
<i class="el-icon-check stat-icon" style="color: #67C23A"></i>
<div class="stat-content">
<div class="stat-value">{{ statistics.enabled }}</div>
<div class="stat-label">启用中</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card shadow="hover">
<div class="stat-card">
<i class="el-icon-close stat-icon" style="color: #F56C6C"></i>
<div class="stat-content">
<div class="stat-value">{{ statistics.disabled }}</div>
<div class="stat-label">已禁用</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card shadow="hover">
<div class="stat-card">
<i class="el-icon-coin stat-icon" style="color: #E6A23C"></i>
<div class="stat-content">
<div class="stat-value">{{ statistics.totalValue }}</div>
<div class="stat-label">总价值(钻石)</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
<!-- 搜索和操作栏 -->
<el-card class="mb20">
<el-form :inline="true" :model="queryParams" class="demo-form-inline">
<el-form-item label="礼物名称">
<el-input v-model="queryParams.name" placeholder="请输入礼物名称" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="状态">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
<el-option label="全部" value="" />
<el-option label="启用" :value="1" />
<el-option label="禁用" :value="0" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row>
<el-button type="primary" icon="el-icon-plus" @click="handleAdd">添加礼物</el-button>
<el-button type="danger" icon="el-icon-delete" :disabled="multiple" @click="handleDelete">批量删除</el-button>
</el-row>
</el-card>
<!-- 礼物列表 -->
<el-card>
<el-table v-loading="loading" :data="giftList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="ID" align="center" prop="id" width="80" />
<el-table-column label="礼物图标" align="center" width="100">
<template slot-scope="scope">
<el-image
:src="scope.row.image"
:preview-src-list="[scope.row.image]"
style="width: 50px; height: 50px"
fit="cover"
/>
</template>
</el-table-column>
<el-table-column label="礼物名称" align="center" prop="name" />
<el-table-column label="价格(钻石)" align="center" prop="diamondPrice" width="120">
<template slot-scope="scope">
<el-tag type="warning">{{ scope.row.diamondPrice }}</el-tag>
</template>
</el-table-column>
<el-table-column label="亲密度" align="center" prop="intimacy" width="100" />
<el-table-column label="等级" align="center" prop="level" width="80">
<template slot-scope="scope">
<el-tag :type="['', 'success', 'info', 'warning', 'danger'][scope.row.level] || 'info'">
{{ scope.row.level }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="排序" align="center" prop="sort" width="80" />
<el-table-column label="状态" align="center" width="100">
<template slot-scope="scope">
<el-switch
v-model="scope.row.status"
:active-value="1"
:inactive-value="0"
@change="handleStatusChange(scope.row)"
/>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180" />
<el-table-column label="操作" align="center" width="200" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)">编辑</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" style="color: #F56C6C">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
:page.sync="queryParams.page"
:limit.sync="queryParams.limit"
@pagination="getList"
/>
</el-card>
<!-- 添加或修改礼物对话框 -->
<el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
<el-form-item label="礼物名称" prop="name">
<el-input v-model="form.name" placeholder="请输入礼物名称" />
</el-form-item>
<el-form-item label="礼物图标" prop="image">
<el-upload
class="avatar-uploader"
action="#"
:show-file-list="false"
:http-request="handleUpload"
:before-upload="beforeUpload"
>
<img v-if="form.image" :src="form.image" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
<div class="el-upload__tip">建议上传200x200像素的PNG图片</div>
</el-form-item>
<el-form-item label="价格(钻石)" prop="diamondPrice">
<el-input-number v-model="form.diamondPrice" :min="0.01" :max="999999" :precision="2" />
</el-form-item>
<el-form-item label="亲密度" prop="intimacy">
<el-input-number v-model="form.intimacy" :min="0" :max="999999" />
</el-form-item>
<el-form-item label="礼物等级" prop="level">
<el-select v-model="form.level" placeholder="请选择等级">
<el-option label="1级" :value="1" />
<el-option label="2级" :value="2" />
<el-option label="3级" :value="3" />
<el-option label="4级" :value="4" />
<el-option label="5级" :value="5" />
</el-select>
</el-form-item>
<el-form-item label="是否心动礼物" prop="isHeartbeat">
<el-switch v-model="form.isHeartbeat" :active-value="1" :inactive-value="0" />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number v-model="form.sort" :min="0" :max="9999" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio :label="1">启用</el-radio>
<el-radio :label="0">禁用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="cancel"> </el-button>
<el-button type="primary" @click="submitForm"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { giftListApi, giftAddApi, giftUpdateApi, giftDeleteApi, giftStatusApi } from '@/api/gift';
import DataPagination from '@/components/common/DataPagination';
export default {
name: 'GiftManage',
components: {
pagination: DataPagination
},
data() {
return {
loading: false,
multiple: true,
ids: [],
total: 0,
giftList: [],
title: '',
open: false,
statistics: {
total: 0,
enabled: 0,
disabled: 0,
totalValue: 0
},
queryParams: {
page: 1,
limit: 10,
name: '',
status: ''
},
form: {},
rules: {
name: [
{ required: true, message: '礼物名称不能为空', trigger: 'blur' }
],
image: [
{ required: true, message: '请上传礼物图标', trigger: 'change' }
],
diamondPrice: [
{ required: true, message: '价格不能为空', trigger: 'blur' }
]
}
};
},
created() {
this.getList();
this.getStatistics();
},
methods: {
getList() {
this.loading = true;
giftListApi(this.queryParams).then(response => {
const data = response.data || {};
this.giftList = data.list || [];
this.total = data.total || 0;
this.loading = false;
}).catch(() => {
this.loading = false;
});
},
getStatistics() {
//
giftListApi({ page: 1, limit: 9999 }).then(response => {
console.log('统计数据响应:', response);
const data = response.data || {};
const list = data.list || [];
console.log('礼物列表:', list);
console.log('列表长度:', list.length);
this.statistics.total = list.length;
this.statistics.enabled = list.filter(item => item.status === true).length;
this.statistics.disabled = list.filter(item => item.status === false).length;
this.statistics.totalValue = list.reduce((sum, item) => sum + (parseFloat(item.diamondPrice) || 0), 0);
console.log('统计结果:', this.statistics);
}).catch(error => {
console.error('获取统计数据失败:', error);
});
},
handleQuery() {
this.queryParams.page = 1;
this.getList();
},
resetQuery() {
this.queryParams = {
page: 1,
limit: 10,
name: '',
status: ''
};
this.getList();
},
handleAdd() {
this.reset();
this.open = true;
this.title = '添加礼物';
},
handleUpdate(row) {
this.reset();
this.form = { ...row };
this.open = true;
this.title = '修改礼物';
},
handleDelete(row) {
const ids = row.id || this.ids;
this.$confirm('是否确认删除选中的礼物?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
return giftDeleteApi(ids);
}).then(() => {
this.getList();
this.getStatistics();
this.$message.success('删除成功');
});
},
handleStatusChange(row) {
const text = row.status === 1 ? '启用' : '禁用';
this.$confirm('确认要' + text + '该礼物吗?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
return giftStatusApi(row.id, row.status);
}).then(() => {
this.$message.success(text + '成功');
this.getStatistics();
}).catch(() => {
row.status = row.status === 0 ? 1 : 0;
});
},
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id);
this.multiple = !selection.length;
},
submitForm() {
this.$refs['form'].validate(valid => {
if (valid) {
if (this.form.id) {
giftUpdateApi(this.form).then(() => {
this.$message.success('修改成功');
this.open = false;
this.getList();
this.getStatistics();
});
} else {
giftAddApi(this.form).then(() => {
this.$message.success('新增成功');
this.open = false;
this.getList();
this.getStatistics();
});
}
}
});
},
cancel() {
this.open = false;
this.reset();
},
reset() {
this.form = {
id: null,
name: '',
image: '',
diamondPrice: 1,
intimacy: 1,
level: 1,
isHeartbeat: 0,
sort: 0,
status: 1,
remark: '',
buyType: '钻石',
belong: '平台'
};
this.$nextTick(() => {
if (this.$refs['form']) {
this.$refs['form'].clearValidate();
}
});
},
handleUpload(param) {
//
const formData = new FormData();
formData.append('file', param.file);
//
this.$message.info('图片上传功能需要配置上传接口');
},
beforeUpload(file) {
const isImage = file.type.indexOf('image/') === 0;
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isImage) {
this.$message.error('只能上传图片文件!');
}
if (!isLt2M) {
this.$message.error('上传图片大小不能超过 2MB!');
}
return isImage && isLt2M;
}
}
};
</script>
<style scoped>
.mb20 {
margin-bottom: 20px;
}
.stat-card {
display: flex;
align-items: center;
}
.stat-icon {
font-size: 48px;
margin-right: 20px;
}
.stat-content {
flex: 1;
}
.stat-value {
font-size: 28px;
font-weight: bold;
color: #303133;
}
.stat-label {
font-size: 14px;
color: #909399;
margin-top: 5px;
}
.avatar-uploader {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader:hover {
border-color: #409EFF;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 120px;
height: 120px;
line-height: 120px;
text-align: center;
display: block;
}
.avatar {
width: 120px;
height: 120px;
display: block;
}
</style>

View File

@ -0,0 +1,147 @@
<template>
<div class="app-container">
<el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%;">
<el-table-column label="ID" width="80" align="center">
<template slot-scope="{row}">{{ row.id }}</template>
</el-table-column>
<el-table-column label="套餐标题" width="150" align="center">
<template slot-scope="{row}">{{ row.title }}</template>
</el-table-column>
<el-table-column label="充值金额(元)" width="150" align="center">
<template slot-scope="{row}">
<span style="color: #F56C6C; font-weight: bold;">¥{{ row.amount }}</span>
</template>
</el-table-column>
<el-table-column label="获得虚拟币" width="150" align="center">
<template slot-scope="{row}">{{ row.virtual_amount }}</template>
</el-table-column>
<el-table-column label="赠送虚拟币" width="150" align="center">
<template slot-scope="{row}">
<span v-if="row.bonus_amount > 0" style="color: #67C23A;">+{{ row.bonus_amount }}</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="套餐描述" min-width="200">
<template slot-scope="{row}">{{ row.description }}</template>
</el-table-column>
<el-table-column label="热门推荐" width="100" align="center">
<template slot-scope="{row}">
<el-tag :type="row.is_hot === 1 ? 'danger' : 'info'">
{{ row.is_hot === 1 ? '是' : '否' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="状态" width="100" align="center">
<template slot-scope="{row}">
<el-tag :type="row.is_enabled === 1 ? 'success' : 'danger'">
{{ row.is_enabled === 1 ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="120">
<template slot-scope="{row}">
<el-button type="primary" size="mini" @click="handleEdit(row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<!-- 编辑对话框 -->
<el-dialog title="编辑充值套餐" :visible.sync="dialogVisible" width="600px">
<el-form ref="form" :model="form" label-width="120px">
<el-form-item label="充值金额">
<el-input-number v-model="form.amount" :min="0" :precision="2" />
</el-form-item>
<el-form-item label="获得虚拟币">
<el-input-number v-model="form.virtualAmount" :min="0" :precision="2" />
</el-form-item>
<el-form-item label="赠送虚拟币">
<el-input-number v-model="form.bonusAmount" :min="0" :precision="2" />
</el-form-item>
<el-form-item label="套餐标题">
<el-input v-model="form.title" />
</el-form-item>
<el-form-item label="套餐描述">
<el-input v-model="form.description" />
</el-form-item>
<el-form-item label="热门推荐">
<el-radio-group v-model="form.isHot">
<el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="排序">
<el-input-number v-model="form.sortOrder" :min="0" />
</el-form-item>
<el-form-item label="状态">
<el-radio-group v-model="form.isEnabled">
<el-radio :label="1">启用</el-radio>
<el-radio :label="0">禁用</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div slot="footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit">确定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { getRechargePackages, updateRechargePackage } from '@/api/gift'
export default {
name: 'RechargePackage',
data() {
return {
list: [],
listLoading: false,
dialogVisible: false,
form: {}
}
},
created() {
this.getList()
},
methods: {
getList() {
this.listLoading = true
getRechargePackages().then(response => {
this.list = response || []
this.listLoading = false
}).catch(() => {
this.listLoading = false
})
},
handleEdit(row) {
this.form = {
id: row.id,
amount: row.amount,
virtualAmount: row.virtual_amount,
bonusAmount: row.bonus_amount,
title: row.title,
description: row.description,
isHot: row.is_hot,
sortOrder: row.sort_order,
isEnabled: row.is_enabled
}
this.dialogVisible = true
},
handleSubmit() {
updateRechargePackage(this.form).then(() => {
this.$message.success('更新成功')
this.dialogVisible = false
this.getList()
})
}
}
}
</script>

View File

@ -0,0 +1,307 @@
<template>
<div class="app-container">
<!-- 统计卡片 -->
<el-row :gutter="20" style="margin-bottom: 20px;">
<el-col :span="6">
<el-card shadow="hover">
<div class="stat-card">
<div class="stat-icon" style="background: #409EFF;"><i class="el-icon-present"></i></div>
<div class="stat-content">
<div class="stat-value">{{ statistics.totalCount || 0 }}</div>
<div class="stat-label">总礼物数</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card shadow="hover">
<div class="stat-card">
<div class="stat-icon" style="background: #67C23A;"><i class="el-icon-coin"></i></div>
<div class="stat-content">
<div class="stat-value">{{ statistics.totalValue || 0 }}</div>
<div class="stat-label">总价值虚拟币</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card shadow="hover">
<div class="stat-card">
<div class="stat-icon" style="background: #E6A23C;"><i class="el-icon-present"></i></div>
<div class="stat-content">
<div class="stat-value">{{ statistics.todayCount || 0 }}</div>
<div class="stat-label">今日礼物数</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card shadow="hover">
<div class="stat-card">
<div class="stat-icon" style="background: #F56C6C;"><i class="el-icon-coin"></i></div>
<div class="stat-content">
<div class="stat-value">{{ statistics.todayValue || 0 }}</div>
<div class="stat-label">今日价值虚拟币</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
<!-- 搜索栏 -->
<el-card shadow="never" style="margin-bottom: 20px;">
<el-form :inline="true" :model="listQuery" class="demo-form-inline">
<el-form-item label="关键词">
<el-input v-model="listQuery.keyword" placeholder="送礼者/接收者/礼物名称" clearable style="width: 200px;" />
</el-form-item>
<el-form-item label="开始日期">
<el-date-picker v-model="listQuery.startDate" type="date" placeholder="选择日期" value-format="yyyy-MM-dd" />
</el-form-item>
<el-form-item label="结束日期">
<el-date-picker v-model="listQuery.endDate" type="date" placeholder="选择日期" value-format="yyyy-MM-dd" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleFilter">搜索</el-button>
<el-button icon="el-icon-refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 礼物记录表格 -->
<el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%;">
<el-table-column label="ID" width="80" align="center">
<template slot-scope="{row}">{{ row.id }}</template>
</el-table-column>
<el-table-column label="礼物信息" min-width="150">
<template slot-scope="{row}">
<div class="gift-info">
<img v-if="row.gift_icon" :src="row.gift_icon" class="gift-icon">
<div>
<div class="gift-name">{{ row.gift_name }}</div>
<div class="gift-detail">单价: {{ row.gift_price }} × {{ row.quantity }}</div>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="总价值" width="100" align="center">
<template slot-scope="{row}">
<span class="price-text">{{ row.total_price }}</span>
</template>
</el-table-column>
<el-table-column label="送礼者" min-width="150">
<template slot-scope="{row}">
<div v-if="row.is_anonymous" class="user-info">
<div class="info">
<div class="nickname">匿名用户</div>
</div>
</div>
<div v-else class="user-info">
<img :src="row.sender_avatar || defaultAvatar" class="avatar">
<div class="info">
<div class="nickname">{{ row.sender_nickname }}</div>
<div class="phone">{{ row.sender_phone }}</div>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="接收者(主播)" min-width="150">
<template slot-scope="{row}">
<div class="user-info">
<img :src="row.receiver_avatar || defaultAvatar" class="avatar">
<div class="info">
<div class="nickname">{{ row.receiver_nickname }}</div>
<div class="phone">{{ row.receiver_phone }}</div>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="直播间" width="150" align="center">
<template slot-scope="{row}">
<span v-if="row.room_title">{{ row.room_title }}</span>
<span v-else style="color: #999;">-</span>
</template>
</el-table-column>
<el-table-column label="赠送时间" width="160" align="center">
<template slot-scope="{row}">{{ row.create_time }}</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination
background
:current-page="listQuery.page"
:page-sizes="[10, 20, 30, 50]"
:page-size="listQuery.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</template>
<script>
import { getGiftRecords, getGiftStatistics } from '@/api/gift'
export default {
name: 'GiftRecords',
data() {
return {
defaultAvatar: 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png',
list: [],
total: 0,
listLoading: false,
listQuery: {
page: 1,
limit: 20,
keyword: '',
startDate: '',
endDate: ''
},
statistics: {}
}
},
created() {
this.getList()
this.getStatistics()
},
methods: {
getList() {
this.listLoading = true
getGiftRecords(this.listQuery).then(response => {
this.list = response.list || []
this.total = response.total || 0
this.listLoading = false
}).catch(() => {
this.listLoading = false
})
},
getStatistics() {
getGiftStatistics().then(response => {
this.statistics = response || {}
})
},
handleFilter() {
this.listQuery.page = 1
this.getList()
},
handleReset() {
this.listQuery = {
page: 1,
limit: 20,
keyword: '',
startDate: '',
endDate: ''
}
this.getList()
},
handleSizeChange(val) {
this.listQuery.limit = val
this.getList()
},
handleCurrentChange(val) {
this.listQuery.page = val
this.getList()
}
}
}
</script>
<style scoped>
.stat-card {
display: flex;
align-items: center;
}
.stat-icon {
width: 60px;
height: 60px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 28px;
color: white;
margin-right: 16px;
}
.stat-content {
flex: 1;
}
.stat-value {
font-size: 24px;
font-weight: bold;
color: #303133;
margin-bottom: 4px;
}
.stat-label {
font-size: 14px;
color: #909399;
}
.gift-info {
display: flex;
align-items: center;
}
.gift-icon {
width: 40px;
height: 40px;
border-radius: 4px;
margin-right: 10px;
object-fit: cover;
}
.gift-name {
font-weight: bold;
color: #303133;
margin-bottom: 4px;
}
.gift-detail {
font-size: 12px;
color: #909399;
}
.price-text {
font-size: 16px;
font-weight: bold;
color: #F56C6C;
}
.user-info {
display: flex;
align-items: center;
}
.user-info .avatar {
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 10px;
object-fit: cover;
}
.user-info .info {
flex: 1;
}
.user-info .nickname {
font-weight: bold;
color: #303133;
margin-bottom: 4px;
}
.user-info .phone {
font-size: 12px;
color: #909399;
}
</style>

View File

@ -10,12 +10,11 @@ import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.math.BigDecimal;
import java.util.*;
/**
* 礼物管理控制器
* 礼物管理控制器后台管理
*/
@Slf4j
@RestController
@ -28,61 +27,79 @@ public class GiftAdminController {
private JdbcTemplate jdbcTemplate;
/**
* 礼物列表
* 获取礼物打赏记录列表
*/
@ApiOperation(value = "礼物列表")
@GetMapping("/list")
public CommonResult<CommonPage<Map<String, Object>>> getGiftList(
@RequestParam(value = "keywords", required = false) String keywords,
@RequestParam(value = "status", required = false) String statusStr,
@ApiOperation(value = "礼物打赏记录列表")
@GetMapping("/records")
public CommonResult<CommonPage<Map<String, Object>>> getGiftRecords(
@RequestParam(value = "keyword", required = false) String keyword,
@RequestParam(value = "startDate", required = false) String startDate,
@RequestParam(value = "endDate", required = false) String endDate,
@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "limit", defaultValue = "20") Integer limit) {
StringBuilder sql = new StringBuilder("SELECT * FROM eb_gift WHERE 1=1");
StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM eb_gift WHERE 1=1");
StringBuilder sql = new StringBuilder();
sql.append("SELECT g.id, g.gift_name, g.gift_image as gift_icon, g.gift_price, g.quantity, g.total_price, ");
sql.append("g.sender_id, COALESCE(g.sender_nickname, sender.nickname, '') as sender_nickname, ");
sql.append("COALESCE(g.sender_avatar, sender.avatar, '') as sender_avatar, COALESCE(sender.phone, '') as sender_phone, ");
sql.append("g.receiver_id, COALESCE(g.receiver_nickname, receiver.nickname, '') as receiver_nickname, ");
sql.append("COALESCE(receiver.avatar, '') as receiver_avatar, COALESCE(receiver.phone, '') as receiver_phone, ");
sql.append("g.room_id, COALESCE(r.title, '') as room_title, ");
sql.append("COALESCE(g.is_deleted, 0) as is_anonymous, g.create_time ");
sql.append("FROM eb_gift_record g ");
sql.append("LEFT JOIN eb_user sender ON g.sender_id = sender.uid ");
sql.append("LEFT JOIN eb_user receiver ON g.receiver_id = receiver.uid ");
sql.append("LEFT JOIN eb_live_room r ON g.room_id = r.id ");
sql.append("WHERE 1=1 ");
if (keywords != null && !keywords.trim().isEmpty()) {
String condition = " AND name LIKE '%" + keywords.trim() + "%'";
StringBuilder countSql = new StringBuilder();
countSql.append("SELECT COUNT(*) FROM eb_gift_record g ");
countSql.append("LEFT JOIN eb_user sender ON g.sender_id = sender.uid ");
countSql.append("LEFT JOIN eb_user receiver ON g.receiver_id = receiver.uid ");
countSql.append("WHERE 1=1 ");
List<Object> params = new ArrayList<>();
List<Object> countParams = new ArrayList<>();
if (keyword != null && !keyword.isEmpty()) {
String condition = " AND (g.sender_nickname LIKE ? OR g.receiver_nickname LIKE ? OR g.gift_name LIKE ?) ";
sql.append(condition);
countSql.append(condition);
String keywordPattern = "%" + keyword + "%";
params.add(keywordPattern);
params.add(keywordPattern);
params.add(keywordPattern);
countParams.add(keywordPattern);
countParams.add(keywordPattern);
countParams.add(keywordPattern);
}
// 处理status参数空字符串视为null
Integer status = null;
if (statusStr != null && !statusStr.trim().isEmpty()) {
try {
status = Integer.parseInt(statusStr.trim());
} catch (NumberFormatException e) {
// 忽略无效的status值
}
}
if (status != null) {
String condition = " AND status = " + status;
if (startDate != null && !startDate.isEmpty()) {
String condition = " AND g.create_time >= ? ";
sql.append(condition);
countSql.append(condition);
params.add(startDate + " 00:00:00");
countParams.add(startDate + " 00:00:00");
}
sql.append(" ORDER BY id ASC");
if (endDate != null && !endDate.isEmpty()) {
String condition = " AND g.create_time <= ? ";
sql.append(condition);
countSql.append(condition);
params.add(endDate + " 23:59:59");
countParams.add(endDate + " 23:59:59");
}
Long total = jdbcTemplate.queryForObject(countSql.toString(), Long.class);
sql.append(" ORDER BY g.create_time DESC ");
Long total = jdbcTemplate.queryForObject(countSql.toString(), Long.class, countParams.toArray());
int offset = (page - 1) * limit;
sql.append(" LIMIT ").append(offset).append(", ").append(limit);
sql.append(" LIMIT ? OFFSET ? ");
params.add(limit);
params.add(offset);
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql.toString());
// 转换字段名
list.forEach(item -> {
item.put("diamondPrice", item.get("diamond_price"));
item.put("isHeartbeat", item.get("is_heartbeat") != null && ((Integer) item.get("is_heartbeat")) == 1);
item.put("buyType", item.get("buy_type"));
item.put("effectVersion", item.get("effect_version"));
item.put("createTime", item.get("create_time"));
item.put("updateTime", item.get("update_time"));
// 状态转换为布尔值
item.put("status", item.get("status") != null && ((Integer) item.get("status")) == 1);
});
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql.toString(), params.toArray());
CommonPage<Map<String, Object>> result = new CommonPage<>();
result.setList(list);
@ -95,17 +112,288 @@ public class GiftAdminController {
}
/**
* 礼物详情
* 获取礼物统计数据
*/
@ApiOperation(value = "礼物详情")
@GetMapping("/detail/{id}")
public CommonResult<Map<String, Object>> getGiftDetail(@PathVariable Integer id) {
String sql = "SELECT * FROM eb_gift WHERE id = ?";
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, id);
if (list.isEmpty()) {
return CommonResult.failed("礼物不存在");
@ApiOperation(value = "礼物统计数据")
@GetMapping("/statistics")
public CommonResult<Map<String, Object>> getGiftStatistics() {
try {
Map<String, Object> stats = new HashMap<>();
// 总礼物数量
String totalCountSql = "SELECT COUNT(*) FROM eb_gift_record";
Integer totalCount = jdbcTemplate.queryForObject(totalCountSql, Integer.class);
stats.put("totalCount", totalCount != null ? totalCount : 0);
// 总礼物价值
String totalValueSql = "SELECT COALESCE(SUM(total_price), 0) FROM eb_gift_record";
BigDecimal totalValue = jdbcTemplate.queryForObject(totalValueSql, BigDecimal.class);
stats.put("totalValue", totalValue != null ? totalValue : BigDecimal.ZERO);
// 今日礼物数量
String todayCountSql = "SELECT COUNT(*) FROM eb_gift_record WHERE DATE(create_time) = CURDATE()";
Integer todayCount = jdbcTemplate.queryForObject(todayCountSql, Integer.class);
stats.put("todayCount", todayCount != null ? todayCount : 0);
// 今日礼物价值
String todayValueSql = "SELECT COALESCE(SUM(total_price), 0) FROM eb_gift_record WHERE DATE(create_time) = CURDATE()";
BigDecimal todayValue = jdbcTemplate.queryForObject(todayValueSql, BigDecimal.class);
stats.put("todayValue", todayValue != null ? todayValue : BigDecimal.ZERO);
return CommonResult.success(stats);
} catch (Exception e) {
log.error("获取礼物统计失败", e);
return CommonResult.failed("获取统计失败");
}
return CommonResult.success(list.get(0));
}
/**
* 获取礼物配置列表
*/
@ApiOperation(value = "礼物配置列表")
@GetMapping("/config/list")
public CommonResult<List<Map<String, Object>>> getGiftConfigList() {
try {
String sql = "SELECT id, name, icon, price, animation, sort_order, is_enabled, create_time " +
"FROM eb_gift_config ORDER BY sort_order";
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
return CommonResult.success(list);
} catch (Exception e) {
log.error("获取礼物配置失败", e);
return CommonResult.failed("获取礼物配置失败");
}
}
/**
* 添加礼物配置
*/
@ApiOperation(value = "添加礼物配置")
@PostMapping("/config/add")
public CommonResult<String> addGiftConfig(@RequestBody Map<String, Object> request) {
try {
String name = (String) request.get("name");
String icon = (String) request.get("icon");
BigDecimal price = new BigDecimal(request.get("price").toString());
String animation = (String) request.get("animation");
Integer sortOrder = request.get("sortOrder") != null ? (Integer) request.get("sortOrder") : 0;
String sql = "INSERT INTO eb_gift_config (name, icon, price, animation, sort_order) VALUES (?, ?, ?, ?, ?)";
jdbcTemplate.update(sql, name, icon, price, animation, sortOrder);
return CommonResult.success("添加成功");
} catch (Exception e) {
log.error("添加礼物配置失败", e);
return CommonResult.failed("添加失败");
}
}
/**
* 更新礼物配置
*/
@ApiOperation(value = "更新礼物配置")
@PostMapping("/config/update")
public CommonResult<String> updateGiftConfig(@RequestBody Map<String, Object> request) {
try {
Integer id = (Integer) request.get("id");
String name = (String) request.get("name");
String icon = (String) request.get("icon");
BigDecimal price = new BigDecimal(request.get("price").toString());
String animation = (String) request.get("animation");
Integer sortOrder = request.get("sortOrder") != null ? (Integer) request.get("sortOrder") : 0;
Integer isEnabled = request.get("isEnabled") != null ? (Integer) request.get("isEnabled") : 1;
String sql = "UPDATE eb_gift_config SET name = ?, icon = ?, price = ?, animation = ?, sort_order = ?, is_enabled = ? WHERE id = ?";
jdbcTemplate.update(sql, name, icon, price, animation, sortOrder, isEnabled, id);
return CommonResult.success("更新成功");
} catch (Exception e) {
log.error("更新礼物配置失败", e);
return CommonResult.failed("更新失败");
}
}
/**
* 删除礼物配置
*/
@ApiOperation(value = "删除礼物配置")
@PostMapping("/config/delete/{id}")
public CommonResult<String> deleteGiftConfig(@PathVariable Integer id) {
try {
String sql = "DELETE FROM eb_gift_config WHERE id = ?";
jdbcTemplate.update(sql, id);
return CommonResult.success("删除成功");
} catch (Exception e) {
log.error("删除礼物配置失败", e);
return CommonResult.failed("删除失败");
}
}
/**
* 获取充值套餐列表
*/
@ApiOperation(value = "充值套餐列表")
@GetMapping("/recharge/packages")
public CommonResult<List<Map<String, Object>>> getRechargePackages() {
try {
String sql = "SELECT id, amount, virtual_amount, bonus_amount, title, description, is_hot, sort_order, is_enabled " +
"FROM eb_recharge_package ORDER BY sort_order";
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
return CommonResult.success(list);
} catch (Exception e) {
log.error("获取充值套餐失败", e);
return CommonResult.failed("获取充值套餐失败");
}
}
/**
* 更新充值套餐
*/
@ApiOperation(value = "更新充值套餐")
@PostMapping("/recharge/package/update")
public CommonResult<String> updateRechargePackage(@RequestBody Map<String, Object> request) {
try {
Integer id = (Integer) request.get("id");
BigDecimal amount = new BigDecimal(request.get("amount").toString());
BigDecimal virtualAmount = new BigDecimal(request.get("virtualAmount").toString());
BigDecimal bonusAmount = new BigDecimal(request.get("bonusAmount").toString());
String title = (String) request.get("title");
String description = (String) request.get("description");
Integer isHot = request.get("isHot") != null ? (Integer) request.get("isHot") : 0;
Integer sortOrder = request.get("sortOrder") != null ? (Integer) request.get("sortOrder") : 0;
Integer isEnabled = request.get("isEnabled") != null ? (Integer) request.get("isEnabled") : 1;
String sql = "UPDATE eb_recharge_package SET amount = ?, virtual_amount = ?, bonus_amount = ?, " +
"title = ?, description = ?, is_hot = ?, sort_order = ?, is_enabled = ? WHERE id = ?";
jdbcTemplate.update(sql, amount, virtualAmount, bonusAmount, title, description, isHot, sortOrder, isEnabled, id);
return CommonResult.success("更新成功");
} catch (Exception e) {
log.error("更新充值套餐失败", e);
return CommonResult.failed("更新失败");
}
}
/**
* 获取充值记录
*/
@ApiOperation(value = "充值记录列表")
@GetMapping("/recharge/records")
public CommonResult<CommonPage<Map<String, Object>>> getRechargeRecords(
@RequestParam(value = "keyword", required = false) String keyword,
@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "limit", defaultValue = "20") Integer limit) {
StringBuilder sql = new StringBuilder();
sql.append("SELECT r.id, r.order_no, r.amount, r.virtual_amount, r.payment_method, r.payment_status, ");
sql.append("r.create_time, r.pay_time, ");
sql.append("u.uid as user_id, u.nickname, u.phone, u.avatar ");
sql.append("FROM eb_virtual_currency_recharge r ");
sql.append("LEFT JOIN eb_user u ON r.user_id = u.uid ");
sql.append("WHERE 1=1 ");
StringBuilder countSql = new StringBuilder();
countSql.append("SELECT COUNT(*) FROM eb_virtual_currency_recharge r ");
countSql.append("LEFT JOIN eb_user u ON r.user_id = u.uid ");
countSql.append("WHERE 1=1 ");
List<Object> params = new ArrayList<>();
List<Object> countParams = new ArrayList<>();
if (keyword != null && !keyword.isEmpty()) {
String condition = " AND (u.nickname LIKE ? OR u.phone LIKE ? OR r.order_no LIKE ?) ";
sql.append(condition);
countSql.append(condition);
String keywordPattern = "%" + keyword + "%";
params.add(keywordPattern);
params.add(keywordPattern);
params.add(keywordPattern);
countParams.add(keywordPattern);
countParams.add(keywordPattern);
countParams.add(keywordPattern);
}
sql.append(" ORDER BY r.create_time DESC ");
Long total = jdbcTemplate.queryForObject(countSql.toString(), Long.class, countParams.toArray());
int offset = (page - 1) * limit;
sql.append(" LIMIT ? OFFSET ? ");
params.add(limit);
params.add(offset);
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql.toString(), params.toArray());
CommonPage<Map<String, Object>> result = new CommonPage<>();
result.setList(list);
result.setTotal(total != null ? total : 0L);
result.setPage(page);
result.setLimit(limit);
result.setTotalPage((int) Math.ceil((double) (total != null ? total : 0) / limit));
return CommonResult.success(result);
}
// ========== 礼物管理接口eb_gift表 ==========
/**
* 获取礼物列表
*/
@ApiOperation(value = "礼物列表")
@GetMapping("/list")
public CommonResult<CommonPage<Map<String, Object>>> getGiftList(
@RequestParam(value = "name", required = false) String name,
@RequestParam(value = "status", required = false) Integer status,
@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "limit", defaultValue = "20") Integer limit) {
StringBuilder sql = new StringBuilder();
sql.append("SELECT id, name, image, diamond_price as diamondPrice, intimacy, status, ");
sql.append("is_heartbeat as isHeartbeat, buy_type as buyType, belong, remark, ");
sql.append("level, sort, create_time as createTime, update_time as updateTime ");
sql.append("FROM eb_gift WHERE is_deleted = 0 ");
StringBuilder countSql = new StringBuilder();
countSql.append("SELECT COUNT(*) FROM eb_gift WHERE is_deleted = 0 ");
List<Object> params = new ArrayList<>();
List<Object> countParams = new ArrayList<>();
if (name != null && !name.isEmpty()) {
String condition = " AND name LIKE ? ";
sql.append(condition);
countSql.append(condition);
String namePattern = "%" + name + "%";
params.add(namePattern);
countParams.add(namePattern);
}
if (status != null) {
String condition = " AND status = ? ";
sql.append(condition);
countSql.append(condition);
params.add(status);
countParams.add(status);
}
sql.append(" ORDER BY sort ASC, id DESC ");
Long total = jdbcTemplate.queryForObject(countSql.toString(), Long.class, countParams.toArray());
int offset = (page - 1) * limit;
sql.append(" LIMIT ? OFFSET ? ");
params.add(limit);
params.add(offset);
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql.toString(), params.toArray());
CommonPage<Map<String, Object>> result = new CommonPage<>();
result.setList(list);
result.setTotal(total != null ? total : 0L);
result.setPage(page);
result.setLimit(limit);
result.setTotalPage((int) Math.ceil((double) (total != null ? total : 0) / limit));
return CommonResult.success(result);
}
/**
@ -113,28 +401,28 @@ public class GiftAdminController {
*/
@ApiOperation(value = "添加礼物")
@PostMapping("/add")
public CommonResult<Boolean> addGift(@RequestBody Map<String, Object> data) {
public CommonResult<String> addGift(@RequestBody Map<String, Object> request) {
try {
String name = (String) request.get("name");
String image = (String) request.get("image");
BigDecimal diamondPrice = new BigDecimal(request.get("diamondPrice").toString());
Integer intimacy = request.get("intimacy") != null ? Integer.parseInt(request.get("intimacy").toString()) : 0;
Integer level = request.get("level") != null ? Integer.parseInt(request.get("level").toString()) : 1;
Integer isHeartbeat = request.get("isHeartbeat") != null ? Integer.parseInt(request.get("isHeartbeat").toString()) : 0;
Integer sort = request.get("sort") != null ? Integer.parseInt(request.get("sort").toString()) : 0;
Integer status = request.get("status") != null ? Integer.parseInt(request.get("status").toString()) : 1;
String remark = (String) request.get("remark");
String buyType = request.get("buyType") != null ? (String) request.get("buyType") : "钻石";
String belong = request.get("belong") != null ? (String) request.get("belong") : "平台";
String sql = "INSERT INTO eb_gift (name, image, diamond_price, intimacy, status, is_heartbeat, " +
"buy_type, belong, remark, effect_version, effect_url, create_time, update_time) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())";
jdbcTemplate.update(sql,
data.get("name"),
data.get("image"),
data.get("diamondPrice"),
data.get("intimacy"),
data.get("status") != null && (Boolean) data.get("status") ? 1 : 0,
data.get("isHeartbeat") != null && (Boolean) data.get("isHeartbeat") ? 1 : 0,
data.get("buyType"),
data.get("belong"),
data.get("remark"),
data.get("effectVersion"),
data.get("effectUrl") != null ? data.get("effectUrl") : ""
);
return CommonResult.success(true);
"buy_type, belong, remark, level, sort) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
jdbcTemplate.update(sql, name, image, diamondPrice, intimacy, status, isHeartbeat,
buyType, belong, remark, level, sort);
return CommonResult.success("添加成功");
} catch (Exception e) {
log.error("添加礼物失败: {}", e.getMessage());
log.error("添加礼物失败", e);
return CommonResult.failed("添加失败: " + e.getMessage());
}
}
@ -143,136 +431,75 @@ public class GiftAdminController {
* 更新礼物
*/
@ApiOperation(value = "更新礼物")
@PostMapping("/update/{id}")
public CommonResult<Boolean> updateGift(@PathVariable Integer id, @RequestBody Map<String, Object> data) {
@PostMapping("/update")
public CommonResult<String> updateGift(@RequestBody Map<String, Object> request) {
try {
String sql = "UPDATE eb_gift SET name = ?, image = ?, diamond_price = ?, intimacy = ?, " +
"status = ?, is_heartbeat = ?, buy_type = ?, belong = ?, remark = ?, " +
"effect_version = ?, effect_url = ?, update_time = NOW() WHERE id = ?";
jdbcTemplate.update(sql,
data.get("name"),
data.get("image"),
data.get("diamondPrice"),
data.get("intimacy"),
data.get("status") != null && (Boolean) data.get("status") ? 1 : 0,
data.get("isHeartbeat") != null && (Boolean) data.get("isHeartbeat") ? 1 : 0,
data.get("buyType"),
data.get("belong"),
data.get("remark"),
data.get("effectVersion"),
data.get("effectUrl") != null ? data.get("effectUrl") : "",
id
);
return CommonResult.success(true);
Integer id = Integer.parseInt(request.get("id").toString());
String name = (String) request.get("name");
String image = (String) request.get("image");
BigDecimal diamondPrice = new BigDecimal(request.get("diamondPrice").toString());
Integer intimacy = request.get("intimacy") != null ? Integer.parseInt(request.get("intimacy").toString()) : 0;
Integer level = request.get("level") != null ? Integer.parseInt(request.get("level").toString()) : 1;
Integer isHeartbeat = request.get("isHeartbeat") != null ? Integer.parseInt(request.get("isHeartbeat").toString()) : 0;
Integer sort = request.get("sort") != null ? Integer.parseInt(request.get("sort").toString()) : 0;
Integer status = request.get("status") != null ? Integer.parseInt(request.get("status").toString()) : 1;
String remark = (String) request.get("remark");
String buyType = request.get("buyType") != null ? (String) request.get("buyType") : "钻石";
String belong = request.get("belong") != null ? (String) request.get("belong") : "平台";
String sql = "UPDATE eb_gift SET name = ?, image = ?, diamond_price = ?, intimacy = ?, status = ?, " +
"is_heartbeat = ?, buy_type = ?, belong = ?, remark = ?, level = ?, sort = ? WHERE id = ?";
jdbcTemplate.update(sql, name, image, diamondPrice, intimacy, status, isHeartbeat,
buyType, belong, remark, level, sort, id);
return CommonResult.success("更新成功");
} catch (Exception e) {
log.error("更新礼物失败: {}", e.getMessage());
log.error("更新礼物失败", e);
return CommonResult.failed("更新失败: " + e.getMessage());
}
}
/**
* 更新礼物状态
*/
@ApiOperation(value = "更新礼物状态")
@PostMapping("/status/{id}")
public CommonResult<Boolean> updateStatus(@PathVariable Integer id, @RequestParam Integer status) {
try {
jdbcTemplate.update("UPDATE eb_gift SET status = ?, update_time = NOW() WHERE id = ?", status, id);
return CommonResult.success(true);
} catch (Exception e) {
log.error("更新状态失败: {}", e.getMessage());
return CommonResult.failed("操作失败");
}
}
/**
* 更新心动礼物状态
*/
@ApiOperation(value = "更新心动礼物状态")
@PostMapping("/heartbeat/{id}")
public CommonResult<Boolean> updateHeartbeat(@PathVariable Integer id, @RequestParam Integer isHeartbeat) {
try {
jdbcTemplate.update("UPDATE eb_gift SET is_heartbeat = ?, update_time = NOW() WHERE id = ?", isHeartbeat, id);
return CommonResult.success(true);
} catch (Exception e) {
log.error("更新心动状态失败: {}", e.getMessage());
return CommonResult.failed("操作失败");
}
}
/**
* 删除礼物
*/
@ApiOperation(value = "删除礼物")
@DeleteMapping("/{id}")
public CommonResult<Boolean> deleteGift(@PathVariable Integer id) {
try {
jdbcTemplate.update("DELETE FROM eb_gift WHERE id = ?", id);
return CommonResult.success(true);
} catch (Exception e) {
log.error("删除礼物失败: {}", e.getMessage());
return CommonResult.failed("删除失败");
}
}
/**
* 批量删除礼物
*/
@ApiOperation(value = "批量删除礼物")
@PostMapping("/batch-delete")
public CommonResult<Boolean> batchDeleteGift(@RequestBody Map<String, Object> data) {
@PostMapping("/delete")
public CommonResult<String> deleteGift(@RequestBody Map<String, Object> request) {
try {
@SuppressWarnings("unchecked")
List<Integer> ids = (List<Integer>) data.get("ids");
List<Integer> ids = (List<Integer>) request.get("ids");
if (ids == null || ids.isEmpty()) {
return CommonResult.failed("请选择要删除的礼物");
}
String placeholders = String.join(",", ids.stream().map(id -> "?").toArray(String[]::new));
String sql = "DELETE FROM eb_gift WHERE id IN (" + placeholders + ")";
String placeholders = String.join(",", Collections.nCopies(ids.size(), "?"));
String sql = "UPDATE eb_gift SET is_deleted = 1 WHERE id IN (" + placeholders + ")";
jdbcTemplate.update(sql, ids.toArray());
return CommonResult.success(true);
return CommonResult.success("删除成功");
} catch (Exception e) {
log.error("批量删除礼物失败: {}", e.getMessage());
return CommonResult.failed("批量删除失败: " + e.getMessage());
log.error("删除礼物失败", e);
return CommonResult.failed("删除失败: " + e.getMessage());
}
}
/**
* 礼物统计
* 更新礼物状态
*/
@ApiOperation(value = "礼物统计")
@GetMapping("/statistics")
public CommonResult<Map<String, Object>> getStatistics() {
Map<String, Object> stats = new HashMap<>();
@ApiOperation(value = "更新礼物状态")
@PostMapping("/status")
public CommonResult<String> updateGiftStatus(@RequestBody Map<String, Object> request) {
try {
// 礼物总数
Long totalGifts = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM eb_gift", Long.class);
stats.put("totalGifts", totalGifts != null ? totalGifts : 0);
Integer id = Integer.parseInt(request.get("id").toString());
Integer status = Integer.parseInt(request.get("status").toString());
// 启用礼物数
Long enabledGifts = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM eb_gift WHERE status = 1", Long.class);
stats.put("enabledGifts", enabledGifts != null ? enabledGifts : 0);
// 心动礼物数
Long heartbeatGifts = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM eb_gift WHERE is_heartbeat = 1", Long.class);
stats.put("heartbeatGifts", heartbeatGifts != null ? heartbeatGifts : 0);
// 今日打赏次数
Long todayRewards = jdbcTemplate.queryForObject(
"SELECT COUNT(*) FROM eb_gift_reward_record WHERE DATE(create_time) = CURDATE()", Long.class);
stats.put("todayRewards", todayRewards != null ? todayRewards : 0);
// 今日打赏金额
Double todayAmount = jdbcTemplate.queryForObject(
"SELECT IFNULL(SUM(reward_amount), 0) FROM eb_gift_reward_record WHERE DATE(create_time) = CURDATE()", Double.class);
stats.put("todayAmount", todayAmount != null ? todayAmount : 0);
String sql = "UPDATE eb_gift SET status = ? WHERE id = ?";
jdbcTemplate.update(sql, status, id);
return CommonResult.success("状态更新成功");
} catch (Exception e) {
log.error("获取礼物统计失败: {}", e.getMessage());
log.error("更新礼物状态失败", e);
return CommonResult.failed("状态更新失败: " + e.getMessage());
}
return CommonResult.success(stats);
}
}

View File

@ -1,7 +1,7 @@
package com.zbkj.front.controller;
import com.zbkj.common.result.CommonResult;
import com.zbkj.front.component.FrontTokenComponent;
import com.zbkj.common.token.FrontTokenComponent;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
@ -18,7 +18,7 @@ import java.util.*;
*/
@Slf4j
@RestController
@RequestMapping("/api/front/gift")
@RequestMapping("/api/front/gift-system")
@Api(tags = "礼物系统")
public class GiftSystemController {

View File

@ -1,7 +1,7 @@
package com.zbkj.front.controller;
import com.zbkj.common.result.CommonResult;
import com.zbkj.front.component.FrontTokenComponent;
import com.zbkj.common.token.FrontTokenComponent;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;

44
add_gift_manage_menu.sql Normal file
View File

@ -0,0 +1,44 @@
-- ========================================
-- 添加礼物管理菜单到直播管理下
-- ========================================
-- 1. 启用打赏记录菜单
UPDATE eb_system_menu
SET is_show = 1, name = '打赏记录'
WHERE id = 823
AND pid = 675;
-- 2. 添加礼物管理菜单
INSERT INTO eb_system_menu (pid, name, icon, perms, component, menu_type, sort, is_show, is_delte)
SELECT 675, '礼物管理', '', '', '/liveManage/gift/manage/index', 'C', 801, 1, 0
WHERE NOT EXISTS (
SELECT 1 FROM eb_system_menu
WHERE pid = 675 AND component = '/liveManage/gift/manage/index'
);
-- 3. 验证菜单结构
SELECT
'=== 直播管理 - 礼物相关菜单 ===' as section,
id,
pid,
name,
component,
is_show,
sort,
CASE WHEN is_show = 1 THEN '✓ 显示' ELSE '✗ 隐藏' END as status
FROM eb_system_menu
WHERE pid = 675 AND (name LIKE '%礼物%' OR name LIKE '%打赏%')
ORDER BY sort;
-- 4. 查看完整的直播管理菜单
SELECT
'=== 直播管理完整菜单 ===' as section,
id,
pid,
name,
component,
is_show,
sort
FROM eb_system_menu
WHERE id = 675 OR pid = 675
ORDER BY sort;

54
add_gift_menu.sql Normal file
View File

@ -0,0 +1,54 @@
-- 添加礼物管理菜单到后台管理系统
-- 1. 查看现有菜单,找到合适的插入位置
SELECT id, pid, name, component, sort, menu_type FROM eb_system_menu WHERE is_delte = 0 ORDER BY sort;
-- 2. 添加礼物管理父菜单(目录类型)
INSERT INTO eb_system_menu (pid, name, icon, perms, component, menu_type, sort, is_show, is_delte, create_time, update_time)
VALUES (0, '礼物管理', 'el-icon-present', '', 'Layout', 'M', 100, 1, 0, NOW(), NOW());
-- 3. 获取刚插入的父菜单ID
SET @parent_id = LAST_INSERT_ID();
-- 4. 添加礼物打赏子菜单(菜单类型)
INSERT INTO eb_system_menu (pid, name, icon, perms, component, menu_type, sort, is_show, is_delte, create_time, update_time)
VALUES (@parent_id, '礼物打赏', 'el-icon-present', 'admin:gift:records', 'gift/records/index', 'C', 1, 1, 0, NOW(), NOW());
-- 5. 验证菜单是否添加成功
SELECT
m1.id as parent_id,
m1.name as parent_name,
m1.menu_type as parent_type,
m2.id as child_id,
m2.name as child_name,
m2.component,
m2.menu_type as child_type
FROM eb_system_menu m1
LEFT JOIN eb_system_menu m2 ON m1.id = m2.pid
WHERE m1.name = '礼物管理' AND m1.is_delte = 0;
-- 6. 查看所有礼物相关菜单
SELECT id, pid, name, icon, component, menu_type, sort, is_show
FROM eb_system_menu
WHERE name LIKE '%礼物%' AND is_delte = 0
ORDER BY pid, sort;
-- 7. 如果需要给管理员角色分配权限假设管理员角色ID为1
-- 先查看角色ID
SELECT * FROM eb_system_role;
-- 获取礼物菜单的ID
SELECT @gift_parent_id := id FROM eb_system_menu WHERE name = '礼物管理' AND is_delte = 0 LIMIT 1;
SELECT @gift_child_id := id FROM eb_system_menu WHERE name = '礼物打赏' AND is_delte = 0 LIMIT 1;
-- 给管理员角色rid=1分配礼物管理权限
INSERT INTO eb_system_role_menu (rid, menu_id) VALUES (1, @gift_parent_id);
INSERT INTO eb_system_role_menu (rid, menu_id) VALUES (1, @gift_child_id);
-- 8. 验证角色权限
SELECT r.id as role_id, r.role_name, m.id as menu_id, m.name as menu_name
FROM eb_system_role r
JOIN eb_system_role_menu rm ON r.id = rm.rid
JOIN eb_system_menu m ON rm.menu_id = m.id
WHERE m.name LIKE '%礼物%';

View File

View File

@ -0,0 +1,42 @@
-- 添加礼物打赏菜单到数据库
-- 1. 查看当前最大的菜单ID
SELECT MAX(id) as max_id FROM eb_system_menu;
-- 2. 删除旧的礼物菜单(如果存在)
DELETE FROM eb_system_menu WHERE path LIKE '/gift%' OR path LIKE '/giftManage%';
-- 3. 添加礼物打赏主菜单
INSERT INTO eb_system_menu (pid, menu_name, path, component, is_show, sort, icon, create_time, update_time)
VALUES (0, '礼物打赏', '/gift', 'Layout', 1, 50, 'el-icon-present', NOW(), NOW());
-- 获取刚插入的主菜单ID
SET @gift_menu_id = LAST_INSERT_ID();
-- 4. 添加子菜单 - 礼物列表
INSERT INTO eb_system_menu (pid, menu_name, path, component, is_show, sort, icon, create_time, update_time)
VALUES (@gift_menu_id, '礼物列表', 'config', 'gift/config/index', 1, 1, 'el-icon-goods', NOW(), NOW());
-- 5. 添加子菜单 - 打赏记录
INSERT INTO eb_system_menu (pid, menu_name, path, component, is_show, sort, icon, create_time, update_time)
VALUES (@gift_menu_id, '打赏记录', 'records', 'gift/records/index', 1, 2, 'el-icon-document', NOW(), NOW());
-- 6. 添加子菜单 - 充值套餐
INSERT INTO eb_system_menu (pid, menu_name, path, component, is_show, sort, icon, create_time, update_time)
VALUES (@gift_menu_id, '充值套餐', 'recharge', 'gift/recharge/index', 1, 3, 'el-icon-coin', NOW(), NOW());
-- 7. 验证插入结果
SELECT
m1.id,
m1.menu_name as '主菜单',
m2.id as '子菜单ID',
m2.menu_name as '子菜单',
m2.path as '路径',
m2.component as '组件'
FROM eb_system_menu m1
LEFT JOIN eb_system_menu m2 ON m1.id = m2.pid
WHERE m1.path = '/gift'
ORDER BY m2.sort;
-- 8. 查看所有礼物相关菜单
SELECT * FROM eb_system_menu WHERE path LIKE '/gift%' OR pid IN (SELECT id FROM eb_system_menu WHERE path = '/gift');

View File

@ -0,0 +1,24 @@
-- ========================================
-- 正确地将礼物打赏添加到直播管理下
-- ========================================
-- 1. 重新启用直播管理下的礼物打赏菜单
UPDATE eb_system_menu
SET is_show = 1
WHERE id = 823
AND pid = 675
AND name = '礼物打赏';
-- 2. 验证结果
SELECT
'=== 直播管理完整菜单 ===' as section,
id,
pid,
name,
component,
is_show,
sort,
CASE WHEN is_show = 1 THEN '✓ 显示' ELSE '✗ 隐藏' END as status
FROM eb_system_menu
WHERE id = 675 OR pid = 675
ORDER BY sort;

View File

@ -0,0 +1,51 @@
-- 检查当前菜单状态找出可能导致404的原因
-- 1. 检查直播管理及其所有子菜单的显示状态
SELECT
id,
pid,
name,
component,
is_show,
sort,
CASE WHEN is_show = 1 THEN '✓ 显示' ELSE '✗ 隐藏' END as status
FROM eb_system_menu
WHERE id = 675 OR pid = 675
ORDER BY pid, sort;
-- 2. 检查是否有其他菜单使用了相同或相似的component路径
SELECT
id,
pid,
name,
component,
is_show
FROM eb_system_menu
WHERE component LIKE '/liveManage%'
AND is_show = 1
ORDER BY component;
-- 3. 检查礼物相关菜单
SELECT
id,
pid,
name,
component,
is_show,
CASE WHEN is_show = 1 THEN '✓ 显示' ELSE '✗ 隐藏' END as status
FROM eb_system_menu
WHERE component LIKE '%gift%'
ORDER BY component;
-- 4. 查看是否有独立的礼物管理顶级菜单
SELECT
id,
pid,
name,
component,
is_show,
sort
FROM eb_system_menu
WHERE (name LIKE '%礼物%' OR component LIKE '/gift%')
AND pid = 0
ORDER BY sort;

47
check_gift_records.sql Normal file
View File

@ -0,0 +1,47 @@
-- 检查礼物打赏记录数据
-- 1. 检查礼物记录表是否存在
SHOW TABLES LIKE '%gift%';
-- 2. 查看礼物记录表结构
DESC eb_gift_record;
-- 3. 查看礼物记录数量
SELECT COUNT(*) as total_records FROM eb_gift_record;
-- 4. 查看最近的礼物记录
SELECT * FROM eb_gift_record ORDER BY create_time DESC LIMIT 10;
-- 5. 如果没有数据,插入一些测试数据
-- 用户43送给主播41一个玫瑰
INSERT INTO eb_gift_record (sender_id, receiver_id, room_id, gift_id, gift_name, gift_icon, gift_price, quantity, total_price, is_anonymous, create_time)
VALUES
(43, 41, 8, 1, '玫瑰', 'https://example.com/gifts/rose.png', 1.00, 5, 5.00, 0, NOW()),
(43, 41, 8, 2, '巧克力', 'https://example.com/gifts/chocolate.png', 5.00, 2, 10.00, 0, NOW()),
(42, 41, 8, 3, '棒棒糖', 'https://example.com/gifts/lollipop.png', 10.00, 1, 10.00, 0, NOW()),
(44, 41, 8, 5, '蛋糕', 'https://example.com/gifts/cake.png', 50.00, 1, 50.00, 0, NOW());
-- 6. 再次查看礼物记录
SELECT
g.id,
g.gift_name,
g.quantity,
g.total_price,
sender.nickname as sender_name,
receiver.nickname as receiver_name,
r.title as room_title,
g.create_time
FROM eb_gift_record g
LEFT JOIN eb_user sender ON g.sender_id = sender.uid
LEFT JOIN eb_user receiver ON g.receiver_id = receiver.uid
LEFT JOIN eb_live_room r ON g.room_id = r.id
ORDER BY g.create_time DESC
LIMIT 10;
-- 7. 查看统计数据
SELECT
COUNT(*) as total_count,
SUM(total_price) as total_value,
SUM(CASE WHEN DATE(create_time) = CURDATE() THEN 1 ELSE 0 END) as today_count,
SUM(CASE WHEN DATE(create_time) = CURDATE() THEN total_price ELSE 0 END) as today_value
FROM eb_gift_record;

View File

@ -0,0 +1,29 @@
-- ========================================
-- 查看礼物表的详细结构和数据
-- ========================================
-- 1. 查看 eb_gift 表结构
SHOW FULL COLUMNS FROM eb_gift;
-- 2. 查看 eb_gift_config 表结构
SHOW FULL COLUMNS FROM eb_gift_config;
-- 3. 查看 eb_gift 现有数据
SELECT * FROM eb_gift LIMIT 10;
-- 4. 查看 eb_gift_config 现有数据
SELECT * FROM eb_gift_config LIMIT 10;
-- 5. 统计礼物数量
SELECT
'=== 礼物统计 ===' as section,
COUNT(*) as total_gifts,
SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as enabled_gifts,
SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) as disabled_gifts
FROM eb_gift;
-- 6. 统计礼物配置数量
SELECT
'=== 礼物配置统计 ===' as section,
COUNT(*) as total_configs
FROM eb_gift_config;

View File

@ -0,0 +1,39 @@
-- 检查直播管理相关的菜单配置
SELECT
id,
pid,
name,
path,
component,
is_show,
sort
FROM eb_system_menu
WHERE path LIKE '%live%'
OR path LIKE '%room%'
OR name LIKE '%直播%'
OR name LIKE '%房间%'
ORDER BY sort, id;
-- 检查是否有重复或冲突的路径
SELECT
path,
COUNT(*) as count,
GROUP_CONCAT(name SEPARATOR ', ') as menu_names
FROM eb_system_menu
WHERE path LIKE '%live%' OR path LIKE '%room%'
GROUP BY path
HAVING count > 1;
-- 检查礼物菜单配置
SELECT
id,
pid,
name,
path,
component,
is_show,
sort
FROM eb_system_menu
WHERE path LIKE '%gift%'
OR name LIKE '%礼物%'
ORDER BY sort, id;

View File

@ -0,0 +1,5 @@
-- 查看菜单表的实际字段结构
DESCRIBE eb_system_menu;
-- 或者使用这个
SHOW COLUMNS FROM eb_system_menu;

View File

@ -0,0 +1,8 @@
-- 查看菜单表结构
DESC eb_system_menu;
-- 查看现有菜单数据示例
SELECT * FROM eb_system_menu LIMIT 5;
-- 查看所有列名
SHOW COLUMNS FROM eb_system_menu;

38
clear_frontend_cache.bat Normal file
View File

@ -0,0 +1,38 @@
@echo off
echo ========================================
echo 清除前端缓存并重新构建
echo ========================================
cd Zhibo\admin
echo.
echo [1/4] 删除 node_modules/.cache 目录...
if exist node_modules\.cache (
rmdir /s /q node_modules\.cache
echo 缓存目录已删除
) else (
echo 缓存目录不存在,跳过
)
echo.
echo [2/4] 删除 dist 目录...
if exist dist (
rmdir /s /q dist
echo dist目录已删除
) else (
echo dist目录不存在跳过
)
echo.
echo [3/4] 重新构建生产环境...
call npm run build:prod
echo.
echo [4/4] 完成!
echo.
echo 请执行以下操作:
echo 1. 清除浏览器缓存 (Ctrl+Shift+Delete)
echo 2. 重新登录后台
echo 3. 测试直播管理菜单
echo.
pause

39
compare_working_menu.sql Normal file
View File

@ -0,0 +1,39 @@
-- 对比正常工作的菜单和礼物菜单
-- 1. 查看直播管理菜单(这个应该是正常工作的)
SELECT
m.id,
m.pid,
m.name as '菜单名',
m.component as '组件路径',
m.menu_type as '类型',
m.icon,
m.perms as '权限标识'
FROM eb_system_menu m
WHERE m.name = '直播管理' OR m.pid = (SELECT id FROM eb_system_menu WHERE name = '直播管理' LIMIT 1)
ORDER BY m.pid, m.sort;
-- 2. 查看礼物打赏菜单
SELECT
m.id,
m.pid,
m.name as '菜单名',
m.component as '组件路径',
m.menu_type as '类型',
m.icon,
m.perms as '权限标识'
FROM eb_system_menu m
WHERE m.name = '礼物打赏' OR m.pid = (SELECT id FROM eb_system_menu WHERE name = '礼物打赏' LIMIT 1)
ORDER BY m.pid, m.sort;
-- 3. 查看用户管理菜单(另一个正常工作的)
SELECT
m.id,
m.pid,
m.name as '菜单名',
m.component as '组件路径',
m.menu_type as '类型'
FROM eb_system_menu m
WHERE m.name = '用户管理' OR m.pid = (SELECT id FROM eb_system_menu WHERE name = '用户管理' LIMIT 1)
ORDER BY m.pid, m.sort
LIMIT 5;

126
complete_gift_diagnosis.sql Normal file
View File

@ -0,0 +1,126 @@
-- 礼物系统完整诊断报告
SELECT '========== 礼物系统诊断报告 ==========' as '';
-- 1. 礼物记录统计
SELECT '1. 礼物记录统计' as '';
SELECT
COUNT(*) as '总记录数',
COUNT(DISTINCT sender_id) as '送礼用户数',
COUNT(DISTINCT receiver_id) as '收礼用户数',
SUM(total_price) as '总金额',
AVG(total_price) as '平均金额'
FROM eb_gift_record;
-- 2. 最受欢迎的礼物
SELECT '2. 最受欢迎的礼物 TOP 5' as '';
SELECT
gift_name,
COUNT(*) as '送出次数',
SUM(quantity) as '总数量',
SUM(total_price) as '总金额'
FROM eb_gift_record
GROUP BY gift_name
ORDER BY COUNT(*) DESC
LIMIT 5;
-- 3. 收礼排行榜
SELECT '3. 收礼排行榜 TOP 5' as '';
SELECT
receiver_id,
receiver_name,
COUNT(*) as '收礼次数',
SUM(total_price) as '收礼总额'
FROM eb_gift_record
GROUP BY receiver_id, receiver_name
ORDER BY SUM(total_price) DESC
LIMIT 5;
-- 4. 送礼排行榜
SELECT '4. 送礼排行榜 TOP 5' as '';
SELECT
sender_id,
sender_name,
COUNT(*) as '送礼次数',
SUM(total_price) as '送礼总额'
FROM eb_gift_record
GROUP BY sender_id, sender_name
ORDER BY SUM(total_price) DESC
LIMIT 5;
-- 5. 按直播间统计
SELECT '5. 直播间礼物统计' as '';
SELECT
r.room_id,
lr.title as '直播间名称',
COUNT(*) as '礼物数量',
SUM(r.total_price) as '礼物总额'
FROM eb_gift_record r
LEFT JOIN eb_live_room lr ON r.room_id = lr.id
GROUP BY r.room_id, lr.title
ORDER BY SUM(r.total_price) DESC;
-- 6. 礼物配置检查
SELECT '6. 礼物配置列表' as '';
SELECT
id,
name,
price,
sort_order,
is_enabled,
CASE is_enabled WHEN 1 THEN '启用' ELSE '禁用' END as '状态'
FROM eb_gift_config
ORDER BY sort_order;
-- 7. 用户虚拟货币余额
SELECT '7. 测试用户虚拟货币余额' as '';
SELECT
uid,
nickname,
virtual_balance as '余额',
CASE
WHEN virtual_balance >= 10000 THEN '充足'
WHEN virtual_balance >= 1000 THEN '正常'
WHEN virtual_balance > 0 THEN '偏低'
ELSE '不足'
END as '状态'
FROM eb_user
WHERE uid IN (43, 44, 45)
ORDER BY uid;
-- 8. 最近礼物记录
SELECT '8. 最近 10 条礼物记录' as '';
SELECT
id,
sender_name as '送礼者',
receiver_name as '接收者',
gift_name as '礼物',
quantity as '数量',
total_price as '总价',
DATE_FORMAT(create_time, '%Y-%m-%d %H:%i:%s') as '时间'
FROM eb_gift_record
ORDER BY create_time DESC
LIMIT 10;
-- 9. 充值记录统计
SELECT '9. 充值记录统计' as '';
SELECT
COUNT(*) as '充值记录数',
COALESCE(SUM(virtual_amount), 0) as '充值总额',
COUNT(CASE WHEN payment_status = 1 THEN 1 END) as '成功支付数'
FROM eb_virtual_currency_recharge;
-- 10. 充值套餐配置
SELECT '10. 充值套餐配置' as '';
SELECT
id,
title as '套餐名称',
amount as '金额',
virtual_amount as '虚拟币',
bonus_amount as '赠送',
CASE is_hot WHEN 1 THEN '' ELSE '' END as '热门',
CASE is_enabled WHEN 1 THEN '启用' ELSE '禁用' END as '状态'
FROM eb_recharge_package
ORDER BY sort_order;
SELECT '========== 诊断完成 ==========' as '';

63
create_gift_menu.sql Normal file
View File

@ -0,0 +1,63 @@
-- ========================================
-- 创建独立的礼物打赏管理菜单
-- ========================================
-- 1. 先检查是否已存在礼物打赏顶级菜单
SELECT id, pid, name, component, is_show, sort
FROM eb_system_menu
WHERE name = '礼物打赏' AND pid = 0;
-- 2. 如果不存在,创建礼物打赏顶级菜单
-- 注意sort值设置为99放在直播管理(98)之后
INSERT INTO eb_system_menu (pid, name, icon, perms, component, menu_type, sort, is_show, is_delte)
SELECT 0, '礼物打赏', 'el-icon-present', '', '/gift', 'M', 99, 1, 0
WHERE NOT EXISTS (
SELECT 1 FROM eb_system_menu WHERE name = '礼物打赏' AND pid = 0
);
-- 3. 获取新创建的礼物打赏菜单ID
SET @gift_menu_id = (SELECT id FROM eb_system_menu WHERE name = '礼物打赏' AND pid = 0 LIMIT 1);
-- 4. 检查是否已有打赏记录子菜单
SELECT id, pid, name, component, is_show
FROM eb_system_menu
WHERE name = '打赏记录' AND component = '/gift/records/index';
-- 5. 如果不存在,创建打赏记录子菜单
INSERT INTO eb_system_menu (pid, name, icon, perms, component, menu_type, sort, is_show, is_delte)
SELECT @gift_menu_id, '打赏记录', '', '', '/gift/records/index', 'C', 1, 1, 0
WHERE NOT EXISTS (
SELECT 1 FROM eb_system_menu WHERE name = '打赏记录' AND component = '/gift/records/index'
);
-- 6. 验证创建结果
SELECT
'=== 礼物打赏菜单结构 ===' as section,
m1.id as parent_id,
m1.name as parent_name,
m1.component as parent_component,
m2.id as child_id,
m2.name as child_name,
m2.component as child_component,
CASE WHEN m1.is_show = 1 AND m2.is_show = 1 THEN '✓ 都显示'
WHEN m1.is_show = 1 THEN '父显示,子隐藏'
ELSE '✗ 父隐藏'
END as status
FROM eb_system_menu m1
LEFT JOIN eb_system_menu m2 ON m2.pid = m1.id
WHERE m1.name = '礼物打赏' AND m1.pid = 0
ORDER BY m2.sort;
-- 7. 显示所有礼物相关菜单
SELECT
'=== 所有礼物相关菜单 ===' as section,
id,
pid,
name,
component,
is_show,
sort,
CASE WHEN is_show = 1 THEN '✓ 显示' ELSE '✗ 隐藏' END as status
FROM eb_system_menu
WHERE name LIKE '%礼物%' OR name LIKE '%打赏%' OR component LIKE '%gift%'
ORDER BY pid, sort;

52
create_gift_table.sql Normal file
View File

@ -0,0 +1,52 @@
-- ========================================
-- 创建礼物表
-- ========================================
-- 检查表是否存在
SELECT TABLE_NAME
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'zhibo'
AND TABLE_NAME = 'eb_gift';
-- 创建礼物表
CREATE TABLE IF NOT EXISTS `eb_gift` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '礼物ID',
`name` varchar(100) NOT NULL COMMENT '礼物名称',
`icon` varchar(500) DEFAULT NULL COMMENT '礼物图标URL',
`price` int NOT NULL DEFAULT '0' COMMENT '价格(钻石)',
`sort` int NOT NULL DEFAULT '0' COMMENT '排序',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态1-启用0-禁用',
`description` varchar(500) DEFAULT NULL COMMENT '礼物描述',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`is_delete` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否删除0-否1-是',
PRIMARY KEY (`id`),
KEY `idx_status` (`status`),
KEY `idx_sort` (`sort`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='礼物表';
-- 插入一些示例礼物数据
INSERT INTO `eb_gift` (`name`, `icon`, `price`, `sort`, `status`, `description`) VALUES
('玫瑰', 'https://example.com/gifts/rose.png', 1, 1, 1, '送你一朵玫瑰'),
('巧克力', 'https://example.com/gifts/chocolate.png', 5, 2, 1, '甜蜜的巧克力'),
('棒棒糖', 'https://example.com/gifts/lollipop.png', 10, 3, 1, '甜甜的棒棒糖'),
('冰淇淋', 'https://example.com/gifts/icecream.png', 20, 4, 1, '清凉的冰淇淋'),
('蛋糕', 'https://example.com/gifts/cake.png', 50, 5, 1, '美味的蛋糕'),
('香水', 'https://example.com/gifts/perfume.png', 100, 6, 1, '迷人的香水'),
('口红', 'https://example.com/gifts/lipstick.png', 200, 7, 1, '魅力口红'),
('钻戒', 'https://example.com/gifts/ring.png', 500, 8, 1, '闪耀的钻戒'),
('跑车', 'https://example.com/gifts/car.png', 1000, 9, 1, '豪华跑车'),
('城堡', 'https://example.com/gifts/castle.png', 5000, 10, 1, '梦幻城堡')
ON DUPLICATE KEY UPDATE name=name;
-- 验证数据
SELECT
'=== 礼物列表 ===' as section,
id,
name,
price,
sort,
CASE WHEN status = 1 THEN '启用' ELSE '禁用' END as status,
create_time
FROM eb_gift
ORDER BY sort;

View File

@ -0,0 +1,39 @@
-- 调试礼物API返回的数据
-- 1. 检查eb_gift表中的数据
SELECT
id,
name,
image,
diamond_price,
intimacy,
status,
is_heartbeat,
level,
sort,
is_deleted
FROM eb_gift
WHERE is_deleted = 0
ORDER BY sort ASC, id DESC
LIMIT 10;
-- 2. 检查是否所有礼物都被标记为删除
SELECT
COUNT(*) as total_gifts,
SUM(CASE WHEN is_deleted = 0 THEN 1 ELSE 0 END) as active_gifts,
SUM(CASE WHEN is_deleted = 1 THEN 1 ELSE 0 END) as deleted_gifts
FROM eb_gift;
-- 3. 如果is_deleted字段不存在检查表结构
SHOW COLUMNS FROM eb_gift LIKE 'is_deleted';
-- 4. 如果没有is_deleted字段查看所有礼物
SELECT
id,
name,
image,
diamond_price,
status
FROM eb_gift
ORDER BY sort ASC, id DESC
LIMIT 10;

27
deploy-gift-fix.bat Normal file
View File

@ -0,0 +1,27 @@
@echo off
echo ===================================
echo 部署礼物管理修复
echo ===================================
set SERVER=root@1.15.149.240
set JAR_PATH=Zhibo\zhibo-h\crmeb-admin\target\crmeb-admin.jar
set REMOTE_PATH=/root/zhibo/admin/
echo.
echo [1/3] 上传JAR文件...
scp %JAR_PATH% %SERVER%:%REMOTE_PATH%
echo.
echo [2/3] 重启后台服务...
ssh %SERVER% "cd /root/zhibo/admin && ./restart.sh"
echo.
echo [3/3] 等待服务启动...
timeout /t 10
echo.
echo ===================================
echo 部署完成!
echo 请刷新管理后台页面查看礼物记录
echo ===================================
pause

View File

@ -0,0 +1,32 @@
@echo off
echo ========================================
echo 修复礼物记录显示问题 - 快速部署
echo ========================================
cd Zhibo\zhibo-h
echo.
echo [1/3] 清理并编译项目...
call mvn clean package -DskipTests -Pdev
if %errorlevel% neq 0 (
echo 编译失败!
pause
exit /b 1
)
echo.
echo [2/3] 停止远程服务...
ssh root@1.15.149.240 "cd /www/server/java && ./stop.sh"
echo.
echo [3/3] 上传并启动服务...
scp crmeb-admin\target\crmeb-admin.jar root@1.15.149.240:/www/server/java/
ssh root@1.15.149.240 "cd /www/server/java && ./start.sh"
echo.
echo ========================================
echo 部署完成!
echo 礼物记录页面现在应该可以正常显示数据了
echo ========================================
pause

View File

@ -0,0 +1,50 @@
@echo off
chcp 65001 >nul
echo ========================================
echo 礼物系统完整部署
echo ========================================
echo.
echo [1/4] 编译后端...
cd Zhibo\zhibo-h
call mvn clean package -DskipTests -Pdev
if %errorlevel% neq 0 (
echo 后端编译失败!
pause
exit /b 1
)
echo.
echo [2/4] 编译前端...
cd ..\admin
call npm run build:prod
if %errorlevel% neq 0 (
echo 前端编译失败!
pause
exit /b 1
)
echo.
echo [3/4] 停止远程服务并上传后端...
ssh root@1.15.149.240 "cd /www/server/java && ./stop.sh"
scp ..\zhibo-h\crmeb-admin\target\crmeb-admin.jar root@1.15.149.240:/www/server/java/
echo.
echo [4/4] 上传前端并启动服务...
scp -r dist/* root@1.15.149.240:/www/wwwroot/admin/
ssh root@1.15.149.240 "cd /www/server/java && ./start.sh"
echo.
echo ========================================
echo 部署完成!
echo.
echo 访问地址http://1.15.149.240:9527
echo.
echo 礼物系统菜单:
echo - 礼物列表:管理礼物配置
echo - 打赏记录:查看打赏记录和统计
echo - 充值套餐:管理充值套餐
echo ========================================
pause

38
deploy_gift_in_live.bat Normal file
View File

@ -0,0 +1,38 @@
@echo off
echo ========================================
echo 部署礼物管理到直播管理下
echo ========================================
echo.
echo [步骤1] 请先执行 add_gift_to_live_manage_correct.sql
echo 这会在数据库中启用礼物打赏菜单
echo.
pause
echo.
echo [步骤2] 清除前端缓存...
cd Zhibo\admin
if exist node_modules\.cache (
rmdir /s /q node_modules\.cache
echo 缓存已清除
)
if exist dist (
rmdir /s /q dist
echo dist目录已清除
)
echo.
echo [步骤3] 重新构建前端...
call npm run build:prod
echo.
echo [步骤4] 完成!
echo.
echo 现在请:
echo 1. 清除浏览器缓存 (Ctrl+Shift+Delete)
echo 2. 重新登录后台
echo 3. 在"直播管理"菜单下查看"礼物打赏"
echo.
pause

View File

@ -0,0 +1,44 @@
@echo off
echo ========================================
echo 部署完整的礼物管理功能
echo ========================================
echo.
echo [步骤1] 执行数据库脚本
echo 请依次执行以下SQL文件
echo 1. create_gift_table.sql - 创建礼物表
echo 2. add_gift_manage_menu.sql - 添加菜单
echo.
pause
echo.
echo [步骤2] 清除前端缓存...
cd Zhibo\admin
if exist node_modules\.cache (
rmdir /s /q node_modules\.cache
echo 前端缓存已清除
)
if exist dist (
rmdir /s /q dist
echo dist目录已清除
)
echo.
echo [步骤3] 重新构建前端...
call npm run build:prod
echo.
echo ========================================
echo 部署完成!
echo ========================================
echo.
echo 现在请:
echo 1. 清除浏览器缓存 (Ctrl+Shift+Delete)
echo 2. 重新登录后台
echo 3. 在"直播管理"下查看:
echo - 打赏记录:查看礼物打赏记录
echo - 礼物管理:管理礼物列表(添加/编辑/删除)
echo.
pause

View File

@ -0,0 +1,98 @@
-- ========================================
-- 诊断直播管理菜单404问题
-- ========================================
-- 1. 查找所有直播相关的菜单
SELECT
id,
pid,
name,
component,
is_show,
sort,
CASE
WHEN is_show = 1 THEN '显示'
ELSE '隐藏'
END as display_status
FROM eb_system_menu
WHERE name LIKE '%直播%'
OR component LIKE '%live%'
OR component LIKE '%room%'
OR component LIKE '%family%'
OR component LIKE '%fanGroup%'
OR component LIKE '%streamer%'
ORDER BY pid, sort, id;
-- 2. 查找礼物相关的菜单
SELECT
id,
pid,
name,
component,
is_show,
sort
FROM eb_system_menu
WHERE name LIKE '%礼物%'
OR component LIKE '%gift%'
ORDER BY pid, sort, id;
-- 3. 检查是否有component路径冲突
SELECT
component,
COUNT(*) as count,
GROUP_CONCAT(CONCAT(id, ':', name) SEPARATOR ' | ') as conflicting_menus
FROM eb_system_menu
WHERE is_show = 1
AND component IS NOT NULL
AND component != ''
GROUP BY component
HAVING count > 1;
-- ========================================
-- 修复方案确保component字段与前端路由path匹配
-- ========================================
-- 前端路由配置:
-- liveManageRouter.path = '/liveManage'
-- 子路由:
-- - 'room/list' -> '/liveManage/room/list'
-- - 'family/list' -> '/liveManage/family/list'
-- - 'family/level' -> '/liveManage/family/level'
-- - 'family/member' -> '/liveManage/family/member'
-- - 'fanGroup/list' -> '/liveManage/fanGroup/list'
-- - 'streamer/list' -> '/liveManage/streamer/list'
-- 礼物路由配置:
-- giftManageRouter.path = '/gift'
-- 子路由:
-- - 'records/index' -> '/gift/records/index'
-- 4. 查看当前直播管理菜单的component配置
SELECT
id,
pid,
name,
component,
'应该是: /liveManage' as expected_component
FROM eb_system_menu
WHERE name = '直播管理'
LIMIT 1;
-- 5. 查看直播管理的子菜单
SELECT
id,
pid,
name,
component,
CASE
WHEN name LIKE '%房间%' THEN '应该是: /liveManage/room/list'
WHEN name LIKE '%家族列表%' THEN '应该是: /liveManage/family/list'
WHEN name LIKE '%家族级别%' THEN '应该是: /liveManage/family/level'
WHEN name LIKE '%家族成员%' THEN '应该是: /liveManage/family/member'
WHEN name LIKE '%粉丝团%' THEN '应该是: /liveManage/fanGroup/list'
WHEN name LIKE '%主播%' THEN '应该是: /liveManage/streamer/list'
ELSE '未知'
END as expected_component
FROM eb_system_menu
WHERE pid IN (SELECT id FROM eb_system_menu WHERE name = '直播管理')
ORDER BY sort;

106
diagnose_gift_system.sql Normal file
View File

@ -0,0 +1,106 @@
-- 礼物系统完整诊断脚本
-- 1. 检查礼物记录表
SELECT '=== 礼物记录表检查 ===' as '';
SELECT COUNT(*) as '礼物记录总数' FROM eb_gift_record;
SELECT * FROM eb_gift_record ORDER BY create_time DESC LIMIT 5;
-- 2. 检查礼物配置表
SELECT '=== 礼物配置表检查 ===' as '';
SELECT COUNT(*) as '礼物配置总数' FROM eb_gift_config;
SELECT * FROM eb_gift_config ORDER BY sort_order LIMIT 5;
-- 3. 检查用户虚拟货币
SELECT '=== 用户虚拟货币检查 ===' as '';
SELECT u.uid, u.nickname, u.phone, u.virtual_balance
FROM eb_user u
WHERE u.uid IN (43, 44, 45)
ORDER BY u.uid;
-- 4. 检查充值记录
SELECT '=== 充值记录检查 ===' as '';
SELECT COUNT(*) as '充值记录总数' FROM eb_virtual_currency_recharge;
SELECT * FROM eb_virtual_currency_recharge ORDER BY create_time DESC LIMIT 5;
-- 5. 检查直播间
SELECT '=== 直播间检查 ===' as '';
SELECT id, title, uid as streamer_id, is_live as status FROM eb_live_room WHERE id IN (8, 9, 10) ORDER BY id;
-- 6. 如果没有测试数据,插入一些
SELECT '=== 开始插入测试数据 ===' as '';
-- 确保用户有虚拟货币余额
UPDATE eb_user
SET virtual_balance = 10000
WHERE uid IN (43, 44, 45) AND (virtual_balance IS NULL OR virtual_balance = 0);
-- 插入测试礼物记录
INSERT INTO eb_gift_record (sender_id, sender_name, receiver_id, receiver_name, room_id, gift_id, gift_name, gift_price, quantity, total_price, create_time)
SELECT
43 as sender_id,
'测试用户43' as sender_name,
44 as receiver_id,
'测试用户44' as receiver_name,
8 as room_id,
1 as gift_id,
'玫瑰花' as gift_name,
10 as gift_price,
5 as quantity,
50 as total_price,
NOW() as create_time
FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM eb_gift_record WHERE sender_id = 43 AND receiver_id = 44 LIMIT 1);
INSERT INTO eb_gift_record (sender_id, sender_name, receiver_id, receiver_name, room_id, gift_id, gift_name, gift_price, quantity, total_price, create_time)
SELECT
45 as sender_id,
'测试用户45' as sender_name,
44 as receiver_id,
'测试用户44' as receiver_name,
8 as room_id,
5 as gift_id,
'跑车' as gift_name,
500 as gift_price,
1 as quantity,
500 as total_price,
NOW() - INTERVAL 1 HOUR as create_time
FROM DUAL
WHERE NOT EXISTS (SELECT 1 FROM eb_gift_record WHERE sender_id = 45 AND receiver_id = 44 LIMIT 1);
INSERT INTO eb_gift_record (sender_id, sender_name, receiver_id, receiver_name, room_id, gift_id, gift_name, gift_price, quantity, total_price, create_time)
SELECT
43 as sender_id,
'测试用户43' as sender_name,
45 as receiver_id,
'测试用户45' as receiver_name,
9 as room_id,
1 as gift_id,
'爱心' as gift_name,
1 as gift_price,
100 as quantity,
100 as total_price,
NOW() - INTERVAL 2 HOUR as create_time
FROM DUAL
WHERE (SELECT COUNT(*) FROM eb_gift_record) < 3;
-- 7. 验证插入结果
SELECT '=== 验证测试数据 ===' as '';
SELECT COUNT(*) as '当前礼物记录总数' FROM eb_gift_record;
SELECT
g.id,
g.gift_name,
g.gift_price,
g.quantity,
g.total_price,
sender.nickname as sender_name,
receiver.nickname as receiver_name,
g.room_id,
g.create_time
FROM eb_gift_record g
LEFT JOIN eb_user sender ON g.sender_id = sender.uid
LEFT JOIN eb_user receiver ON g.receiver_id = receiver.uid
ORDER BY g.create_time DESC
LIMIT 10;
SELECT '=== 诊断完成 ===' as '';

76
diagnose_menu_404.sql Normal file
View File

@ -0,0 +1,76 @@
-- 完整诊断菜单404问题
-- 1. 检查直播管理菜单及其子菜单
SELECT
m.id,
m.pid,
m.name,
m.path,
m.component,
m.is_show,
m.sort,
CASE
WHEN m.pid = 0 THEN '顶级菜单'
ELSE CONCAT('子菜单(父ID:', m.pid, ')')
END as menu_level
FROM eb_system_menu m
WHERE m.name LIKE '%直播%'
OR m.path LIKE '%live%'
OR m.path LIKE '%room%'
OR m.path LIKE '%family%'
OR m.path LIKE '%fanGroup%'
OR m.path LIKE '%streamer%'
ORDER BY m.pid, m.sort, m.id;
-- 2. 检查礼物管理菜单
SELECT
m.id,
m.pid,
m.name,
m.path,
m.component,
m.is_show,
m.sort
FROM eb_system_menu m
WHERE m.name LIKE '%礼物%'
OR m.path LIKE '%gift%'
ORDER BY m.pid, m.sort, m.id;
-- 3. 检查是否有路径冲突
SELECT
path,
COUNT(*) as count,
GROUP_CONCAT(CONCAT(id, ':', name) SEPARATOR ' | ') as conflicting_menus
FROM eb_system_menu
WHERE is_show = 1
GROUP BY path
HAVING count > 1;
-- 4. 检查component路径格式
SELECT
id,
name,
path,
component,
CASE
WHEN component LIKE '%.vue' THEN '有.vue扩展名'
WHEN component LIKE '%/%' THEN '无.vue扩展名'
ELSE '其他格式'
END as component_format
FROM eb_system_menu
WHERE (name LIKE '%直播%' OR path LIKE '%live%' OR path LIKE '%room%')
AND component IS NOT NULL
AND component != '';
-- 5. 检查菜单是否被禁用
SELECT
id,
name,
path,
is_show,
CASE
WHEN is_show = 1 THEN '显示'
ELSE '隐藏'
END as display_status
FROM eb_system_menu
WHERE name LIKE '%直播%' OR path LIKE '%live%';

15
final_fix_gift_menu.sql Normal file
View File

@ -0,0 +1,15 @@
-- 最终修复:使用系统能识别的路径格式
-- 删除礼物菜单
DELETE FROM eb_system_menu WHERE name = '礼物打赏';
-- 重新添加,使用简单的路径
INSERT INTO eb_system_menu (pid, name, icon, perms, component, menu_type, sort, is_show, is_delte)
VALUES (0, '礼物打赏', 'el-icon-present', 'admin:gift:records', 'gift/records/index', 'C', 50, 1, 0);
-- 给管理员分配权限
INSERT IGNORE INTO eb_system_role_menu (rid, menu_id)
SELECT 1, id FROM eb_system_menu WHERE name = '礼物打赏';
-- 验证
SELECT id, name, component FROM eb_system_menu WHERE name = '礼物打赏';

View File

@ -0,0 +1,14 @@
-- 正确地将礼物打赏移到直播管理下,同时修改路径
-- 1. 获取直播管理ID
SELECT @live_id := id FROM eb_system_menu WHERE name = '直播管理' AND pid = 0;
-- 2. 更新礼物打赏改变父级ID AND 修改component路径
UPDATE eb_system_menu
SET pid = @live_id,
component = '/liveManage/gift/records/index',
sort = 800
WHERE name = '礼物打赏';
-- 3. 验证
SELECT name, component, pid FROM eb_system_menu WHERE name = '礼物打赏';

View File

@ -0,0 +1,44 @@
-- ========================================
-- 查找现有的礼物相关表
-- ========================================
-- 1. 查找所有包含gift的表
SELECT
TABLE_NAME,
TABLE_COMMENT,
CREATE_TIME
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'zhibo'
AND TABLE_NAME LIKE '%gift%'
ORDER BY TABLE_NAME;
-- 2. 查找所有包含reward的表打赏相关
SELECT
TABLE_NAME,
TABLE_COMMENT,
CREATE_TIME
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'zhibo'
AND TABLE_NAME LIKE '%reward%'
ORDER BY TABLE_NAME;
-- 3. 查找所有包含present的表礼物相关
SELECT
TABLE_NAME,
TABLE_COMMENT,
CREATE_TIME
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'zhibo'
AND TABLE_NAME LIKE '%present%'
ORDER BY TABLE_NAME;
-- 4. 如果找到了礼物表,查看其结构
-- 请根据上面的结果,取消下面对应表的注释来查看结构
-- SHOW FULL COLUMNS FROM eb_gift;
-- SHOW FULL COLUMNS FROM eb_gift_record;
-- SHOW FULL COLUMNS FROM eb_gift_reward;
-- 5. 如果找到了礼物表,查看现有数据
-- SELECT * FROM eb_gift LIMIT 10;
-- SELECT * FROM eb_gift_record LIMIT 10;

View File

@ -0,0 +1,38 @@
-- ========================================
-- 修复404问题将礼物打赏从直播管理中移除
-- ========================================
-- 问题分析:
-- 1. 礼物打赏菜单(id=823)的component是 /liveManage/gift/records/index
-- 2. 但前端有独立的 giftManageRouter路径是 /gift
-- 3. 这导致路由匹配冲突
-- 解决方案1将礼物打赏移到独立的礼物管理菜单下
-- 首先检查是否已有礼物管理顶级菜单
SELECT id, pid, name, component, is_show
FROM eb_system_menu
WHERE name = '礼物打赏' AND pid = 0;
-- 如果没有,创建礼物管理顶级菜单
-- INSERT INTO eb_system_menu (pid, name, icon, component, menu_type, sort, is_show)
-- VALUES (0, '礼物打赏', 'el-icon-present', '/gift', 'M', 99, 1);
-- 方案2直接删除礼物打赏菜单如果不需要在直播管理下显示
-- DELETE FROM eb_system_menu WHERE id = 823;
-- 方案3隐藏礼物打赏菜单推荐保留数据
UPDATE eb_system_menu
SET is_show = 0
WHERE id = 823;
-- 验证修改
SELECT
id,
pid,
name,
component,
is_show,
CASE WHEN is_show = 1 THEN '显示' ELSE '隐藏' END as status
FROM eb_system_menu
WHERE pid = 675 -- 直播管理的子菜单
ORDER BY sort;

View File

@ -0,0 +1,43 @@
-- 完整修复礼物菜单
-- 步骤1: 查看eb_system_menu表结构
DESC eb_system_menu;
-- 步骤2: 查看当前礼物相关菜单
SELECT * FROM eb_system_menu WHERE menu_name LIKE '%礼物%' OR path LIKE '%gift%';
-- 步骤3: 删除旧的礼物菜单
DELETE FROM eb_system_menu WHERE menu_name LIKE '%礼物%' OR path LIKE '%gift%';
-- 步骤4: 插入礼物打赏主菜单
INSERT INTO eb_system_menu (pid, menu_name, path, component, is_show, sort, icon, create_time, update_time)
VALUES (0, '礼物打赏', '/gift', 'Layout', 1, 50, 'el-icon-present', NOW(), NOW());
-- 步骤5: 获取主菜单ID并插入子菜单
SET @gift_pid = (SELECT id FROM eb_system_menu WHERE path = '/gift' AND pid = 0);
INSERT INTO eb_system_menu (pid, menu_name, path, component, is_show, sort, icon, create_time, update_time) VALUES
(@gift_pid, '礼物列表', 'config', 'gift/config/index', 1, 1, 'el-icon-goods', NOW(), NOW()),
(@gift_pid, '打赏记录', 'records', 'gift/records/index', 1, 2, 'el-icon-document', NOW(), NOW()),
(@gift_pid, '充值套餐', 'recharge', 'gift/recharge/index', 1, 3, 'el-icon-coin', NOW(), NOW());
-- 步骤6: 验证结果
SELECT
CASE WHEN m.pid = 0 THEN '主菜单' ELSE '子菜单' END as '类型',
m.id,
m.pid,
m.menu_name as '菜单名称',
m.path as '路径',
m.component as '组件',
m.icon as '图标',
m.sort as '排序',
m.is_show as '显示'
FROM eb_system_menu m
WHERE m.path = '/gift' OR m.pid = @gift_pid
ORDER BY m.pid, m.sort;
-- 步骤7: 查看所有一级菜单(确认礼物打赏菜单的位置)
SELECT id, menu_name, path, icon, sort
FROM eb_system_menu
WHERE pid = 0
ORDER BY sort;

38
fix_gift_menu_final.sql Normal file
View File

@ -0,0 +1,38 @@
-- 最终修复礼物菜单 - 确保字段完全匹配
-- 1. 先查看现有的工作菜单示例
SELECT * FROM eb_system_menu WHERE name = '直播管理' LIMIT 1;
SELECT * FROM eb_system_menu WHERE pid = (SELECT id FROM eb_system_menu WHERE name = '直播管理' LIMIT 1) LIMIT 3;
-- 2. 删除旧的礼物菜单
DELETE FROM eb_system_menu WHERE name IN ('礼物打赏', '礼物列表', '打赏记录', '充值套餐', '礼物管理');
-- 3. 添加礼物打赏主菜单
INSERT INTO eb_system_menu (pid, name, icon, perms, component, menu_type, sort, is_show, is_delte)
VALUES (0, '礼物打赏', 'el-icon-present', '', '/gift', 'M', 50, 1, 0);
SET @gift_pid = LAST_INSERT_ID();
-- 4. 添加子菜单
INSERT INTO eb_system_menu (pid, name, icon, perms, component, menu_type, sort, is_show, is_delte) VALUES
(@gift_pid, '礼物列表', '', 'admin:gift:config', 'gift/config/index', 'C', 1, 1, 0),
(@gift_pid, '打赏记录', '', 'admin:gift:records', 'gift/records/index', 'C', 2, 1, 0),
(@gift_pid, '充值套餐', '', 'admin:gift:recharge', 'gift/recharge/index', 'C', 3, 1, 0);
-- 5. 验证
SELECT
m.id,
m.pid,
m.name,
m.component,
m.menu_type,
m.sort
FROM eb_system_menu m
WHERE m.name = '礼物打赏' OR m.pid = @gift_pid
ORDER BY m.pid, m.sort;
-- 6. 给管理员分配权限
INSERT IGNORE INTO eb_system_role_menu (rid, menu_id)
SELECT 1, id FROM eb_system_menu WHERE name IN ('礼物打赏', '礼物列表', '打赏记录', '充值套餐');
SELECT '菜单添加完成!请退出登录后重新登录查看' as message;

27
fix_gift_menu_paths.sql Normal file
View File

@ -0,0 +1,27 @@
-- 修复礼物菜单路径 - 使用完整路径
-- 1. 更新子菜单的component路径为完整路径
UPDATE eb_system_menu
SET component = '/gift/config/index'
WHERE name = '礼物列表' AND pid = (SELECT id FROM (SELECT id FROM eb_system_menu WHERE name = '礼物打赏' AND pid = 0) AS temp);
UPDATE eb_system_menu
SET component = '/gift/records/index'
WHERE name = '打赏记录' AND pid = (SELECT id FROM (SELECT id FROM eb_system_menu WHERE name = '礼物打赏' AND pid = 0) AS temp);
UPDATE eb_system_menu
SET component = '/gift/recharge/index'
WHERE name = '充值套餐' AND pid = (SELECT id FROM (SELECT id FROM eb_system_menu WHERE name = '礼物打赏' AND pid = 0) AS temp);
-- 2. 验证修复结果
SELECT
m.id,
m.pid,
m.name as '菜单名',
m.component as '组件路径',
m.menu_type as '类型'
FROM eb_system_menu m
WHERE m.name = '礼物打赏' OR m.pid = (SELECT id FROM eb_system_menu WHERE name = '礼物打赏' LIMIT 1)
ORDER BY m.pid, m.sort;
SELECT '路径已修复!请刷新浏览器页面' as message;

20
fix_live_menu_404.sql Normal file
View File

@ -0,0 +1,20 @@
-- 先查看菜单表结构
SHOW COLUMNS FROM eb_system_menu;
-- 查看所有菜单数据,了解字段名称
SELECT * FROM eb_system_menu LIMIT 5;
-- 查找直播相关的菜单(使用可能的字段名)
-- 可能的字段menu_name/name, menu_path/path/component, pid/parent_id
SELECT
id,
pid,
menu_name,
component,
is_show,
sort
FROM eb_system_menu
WHERE menu_name LIKE '%直播%'
OR component LIKE '%live%'
OR component LIKE '%room%'
ORDER BY sort, id;

View File

@ -0,0 +1,67 @@
-- 正确添加礼物打赏菜单
-- 1. 删除旧的礼物菜单(如果存在)
DELETE FROM eb_system_menu WHERE name LIKE '%礼物%';
-- 2. 添加礼物打赏主菜单(目录类型 M
INSERT INTO eb_system_menu (pid, name, icon, perms, component, menu_type, sort, is_show, is_delte, create_time, update_time)
VALUES (0, '礼物打赏', 'el-icon-present', '', '/gift', 'M', 50, 1, 0, NOW(), NOW());
-- 3. 获取刚插入的主菜单ID
SET @gift_pid = LAST_INSERT_ID();
-- 4. 添加子菜单 - 礼物列表(菜单类型 C
INSERT INTO eb_system_menu (pid, name, icon, perms, component, menu_type, sort, is_show, is_delte, create_time, update_time)
VALUES (@gift_pid, '礼物列表', 'el-icon-goods', 'admin:gift:config', 'gift/config/index', 'C', 1, 1, 0, NOW(), NOW());
-- 5. 添加子菜单 - 打赏记录(菜单类型 C
INSERT INTO eb_system_menu (pid, name, icon, perms, component, menu_type, sort, is_show, is_delte, create_time, update_time)
VALUES (@gift_pid, '打赏记录', 'el-icon-document', 'admin:gift:records', 'gift/records/index', 'C', 2, 1, 0, NOW(), NOW());
-- 6. 添加子菜单 - 充值套餐(菜单类型 C
INSERT INTO eb_system_menu (pid, name, icon, perms, component, menu_type, sort, is_show, is_delte, create_time, update_time)
VALUES (@gift_pid, '充值套餐', 'el-icon-coin', 'admin:gift:recharge', 'gift/recharge/index', 'C', 3, 1, 0, NOW(), NOW());
-- 7. 验证插入结果
SELECT
CASE WHEN m.pid = 0 THEN '主菜单' ELSE ' └─ 子菜单' END as '层级',
m.id as 'ID',
m.name as '菜单名称',
m.component as '组件路径',
m.icon as '图标',
m.menu_type as '类型',
m.sort as '排序',
CASE WHEN m.is_show = 1 THEN '显示' ELSE '隐藏' END as '状态'
FROM eb_system_menu m
WHERE m.name = '礼物打赏' OR m.pid = @gift_pid
ORDER BY m.pid, m.sort;
-- 8. 给管理员角色分配权限假设管理员角色ID为1
-- 先查看角色
SELECT * FROM eb_system_role LIMIT 5;
-- 获取所有礼物菜单ID
SELECT @gift_main_id := id FROM eb_system_menu WHERE name = '礼物打赏' AND pid = 0 LIMIT 1;
SELECT @gift_config_id := id FROM eb_system_menu WHERE name = '礼物列表' AND pid = @gift_main_id LIMIT 1;
SELECT @gift_records_id := id FROM eb_system_menu WHERE name = '打赏记录' AND pid = @gift_main_id LIMIT 1;
SELECT @gift_recharge_id := id FROM eb_system_menu WHERE name = '充值套餐' AND pid = @gift_main_id LIMIT 1;
-- 给管理员角色rid=1分配权限
INSERT IGNORE INTO eb_system_role_menu (rid, menu_id) VALUES
(1, @gift_main_id),
(1, @gift_config_id),
(1, @gift_records_id),
(1, @gift_recharge_id);
-- 9. 最终验证
SELECT '========== 菜单添加完成 ==========' as '';
SELECT
m.id,
m.pid,
m.name as '菜单名称',
m.component as '组件',
m.icon as '图标',
m.sort as '排序'
FROM eb_system_menu m
WHERE m.name LIKE '%礼物%' OR m.pid IN (SELECT id FROM eb_system_menu WHERE name = '礼物打赏')
ORDER BY m.pid, m.sort;

View File

@ -0,0 +1,7 @@
-- 保持礼物打赏为独立菜单(最简单可靠的方案)
UPDATE eb_system_menu
SET pid = 0, sort = 51
WHERE name = '礼物打赏';
SELECT '礼物打赏已恢复为独立菜单,可以正常使用' as message;

View File

@ -0,0 +1,21 @@
-- 正确地将礼物打赏移到直播管理下
-- 1. 获取直播管理的ID
SELECT @live_id := id FROM eb_system_menu WHERE name = '直播管理' AND pid = 0;
-- 2. 更新礼物打赏保持其类型为C菜单只改变父级
UPDATE eb_system_menu
SET pid = @live_id,
sort = 800
WHERE name = '礼物打赏';
-- 3. 验证结果
SELECT
'直播管理子菜单' as '类别',
m.name as '菜单名',
m.menu_type as '类型',
m.component as '组件路径',
m.sort as '排序'
FROM eb_system_menu m
WHERE m.pid = @live_id
ORDER BY m.sort;

View File

@ -0,0 +1,19 @@
-- 将礼物打赏移到直播管理菜单下
-- 1. 获取直播管理的ID
SELECT @live_manage_id := id FROM eb_system_menu WHERE name = '直播管理' AND pid = 0;
-- 2. 更新礼物打赏的pid让它成为直播管理的子菜单
UPDATE eb_system_menu
SET pid = @live_manage_id, sort = 999
WHERE name = '礼物打赏';
-- 3. 验证结果
SELECT
m1.name as '父菜单',
m2.name as '子菜单',
m2.component as '路径'
FROM eb_system_menu m1
JOIN eb_system_menu m2 ON m1.id = m2.pid
WHERE m1.name = '直播管理'
ORDER BY m2.sort;

View File

@ -0,0 +1,17 @@
-- 快速测试礼物系统
-- 1. 查看表结构
DESC eb_gift_record;
-- 2. 查看现有数据
SELECT COUNT(*) as total FROM eb_gift_record;
-- 3. 查看最新5条记录
SELECT * FROM eb_gift_record ORDER BY create_time DESC LIMIT 5;
-- 4. 检查用户虚拟货币
SELECT uid, nickname, virtual_balance FROM eb_user WHERE uid IN (43, 44, 45);
-- 5. 测试插入(如果需要)
-- INSERT INTO eb_gift_record (sender_id, sender_name, receiver_id, receiver_name, room_id, gift_id, gift_name, gift_price, quantity, total_price, create_time)
-- VALUES (43, '测试用户43', 44, '测试用户44', 8, 1, '测试礼物', 10, 1, 10, NOW());

20
rebuild_backend_gift.bat Normal file
View File

@ -0,0 +1,20 @@
@echo off
echo ========================================
echo 重新编译后端礼物管理接口
echo ========================================
cd Zhibo\zhibo-h
echo.
echo [1/2] 清理并编译...
call mvn clean package -DskipTests
echo.
echo [2/2] 完成!
echo.
echo 现在请重启后端服务:
echo 1. 停止当前运行的Java进程
echo 2. 重新启动后端服务
echo 3. 刷新前端页面
echo.
pause

29
restart_frontend.bat Normal file
View File

@ -0,0 +1,29 @@
@echo off
chcp 65001 >nul
echo ========================================
echo 清理前端缓存并重启
echo ========================================
cd Zhibo\admin
echo.
echo [1/3] 停止开发服务器...
taskkill /F /IM node.exe 2>nul
echo.
echo [2/3] 清理缓存...
rmdir /s /q node_modules\.cache 2>nul
rmdir /s /q dist 2>nul
echo.
echo [3/3] 重新启动开发服务器...
echo 请手动运行: npm run dev
echo.
echo 或者在新窗口中运行:
start cmd /k "cd /d %cd% && npm run dev"
echo.
echo ========================================
echo 完成!
echo ========================================
pause

8
restore_gift_menu.sql Normal file
View File

@ -0,0 +1,8 @@
-- 恢复礼物打赏为独立菜单
UPDATE eb_system_menu
SET pid = 0, sort = 50
WHERE name = '礼物打赏';
-- 验证
SELECT id, pid, name, component FROM eb_system_menu WHERE name = '礼物打赏';

View File

@ -0,0 +1,17 @@
-- 简化版:只添加打赏记录菜单(最重要的功能)
-- 删除所有礼物相关菜单
DELETE FROM eb_system_menu WHERE name LIKE '%礼物%';
-- 只添加打赏记录菜单(作为一级菜单,不需要子菜单)
INSERT INTO eb_system_menu (pid, name, icon, perms, component, menu_type, sort, is_show, is_delte)
VALUES (0, '礼物打赏', 'el-icon-present', 'admin:gift:records', '/gift/records/index', 'C', 50, 1, 0);
-- 给管理员分配权限
INSERT IGNORE INTO eb_system_role_menu (rid, menu_id)
SELECT 1, id FROM eb_system_menu WHERE name = '礼物打赏';
-- 验证
SELECT id, pid, name, component, menu_type FROM eb_system_menu WHERE name = '礼物打赏';
SELECT '简化菜单创建完成!刷新浏览器后,点击"礼物打赏"直接进入打赏记录页面' as message;

59
test_gift_api.md Normal file
View File

@ -0,0 +1,59 @@
# 测试礼物管理API
## 1. 测试后端接口
在浏览器中打开开发者工具F12然后访问礼物管理页面查看Network标签页中的请求
### 检查请求
- URL: `/api/admin/gift/list?page=1&limit=20`
- Method: GET
- Status: 应该是 200
### 检查响应格式
后端应该返回这样的格式:
```json
{
"code": 200,
"message": "success",
"data": {
"list": [...],
"total": 20,
"page": 1,
"limit": 20,
"totalPage": 1
}
}
```
## 2. 如果返回格式不对
检查后端日志,看是否有错误信息。
## 3. 直接测试SQL
在数据库中执行:
```sql
SELECT id, name, image, diamond_price as diamondPrice, intimacy, status,
is_heartbeat as isHeartbeat, buy_type as buyType, belong, remark,
level, sort, create_time as createTime, update_time as updateTime
FROM eb_gift
WHERE is_deleted = 0
ORDER BY sort ASC, id DESC
LIMIT 20 OFFSET 0;
```
应该能看到20条礼物记录。
## 4. 常见问题
### 问题1: 404错误
- 确认后端服务已重启
- 确认GiftAdminController已编译
### 问题2: 数据为空
- 检查数据库中是否有数据
- 检查is_deleted字段是否都是0
### 问题3: 前端报错
- 清除浏览器缓存
- 重新构建前端:`npm run build:prod`

View File

@ -0,0 +1,37 @@
-- 更新现有的"打赏记录"菜单,使用我们新创建的页面
-- 1. 查看现有的打赏记录菜单
SELECT id, pid, name, component, menu_type, is_show
FROM eb_system_menu
WHERE name LIKE '%打赏%' OR name LIKE '%记录%'
AND is_delte = 0
ORDER BY pid, sort;
-- 2. 找到"打赏记录"菜单ID应该是633在礼物打赏管理532下
-- 更新它的component为我们新创建的页面
UPDATE eb_system_menu
SET component = 'gift/records/index',
name = '礼物打赏记录',
update_time = NOW()
WHERE id = 633;
-- 3. 验证更新
SELECT id, pid, name, component, menu_type, is_show
FROM eb_system_menu
WHERE id = 633;
-- 4. 确保父菜单(礼物打赏管理)是显示的
UPDATE eb_system_menu
SET is_show = 1
WHERE id = 532;
-- 5. 查看更新后的菜单结构
SELECT
m1.id as parent_id,
m1.name as parent_name,
m2.id as child_id,
m2.name as child_name,
m2.component
FROM eb_system_menu m1
LEFT JOIN eb_system_menu m2 ON m1.id = m2.pid
WHERE m1.id = 532 AND m1.is_delte = 0;

53
verify_gift_menu.sql Normal file
View File

@ -0,0 +1,53 @@
-- 验证礼物管理菜单是否正确添加
-- 1. 查看礼物管理菜单
SELECT
id,
pid,
name,
icon,
component,
menu_type,
sort,
is_show
FROM eb_system_menu
WHERE (name LIKE '%礼物%' OR pid IN (SELECT id FROM eb_system_menu WHERE name = '礼物管理'))
AND is_delte = 0
ORDER BY pid, sort;
-- 2. 查看菜单层级结构
SELECT
CONCAT(REPEAT(' ', CASE WHEN m.pid = 0 THEN 0 ELSE 1 END), m.name) as menu_tree,
m.id,
m.component,
m.menu_type,
m.is_show
FROM eb_system_menu m
WHERE (m.name = '礼物管理' OR m.pid = (SELECT id FROM eb_system_menu WHERE name = '礼物管理' LIMIT 1))
AND m.is_delte = 0
ORDER BY m.pid, m.sort;
-- 3. 查看管理员角色的礼物管理权限
SELECT
r.id as role_id,
r.role_name,
m.id as menu_id,
m.name as menu_name,
m.component
FROM eb_system_role r
JOIN eb_system_role_menu rm ON r.id = rm.rid
JOIN eb_system_menu m ON rm.menu_id = m.id
WHERE m.name LIKE '%礼物%'
AND r.id = 1;
-- 4. 查看礼物配置数据
SELECT id, name, price, sort_order, is_enabled
FROM eb_gift_config
ORDER BY sort_order
LIMIT 10;
-- 5. 查看充值套餐数据
SELECT id, amount, virtual_amount, bonus_amount, title, is_enabled
FROM eb_recharge_package
ORDER BY sort_order
LIMIT 10;

20
verify_menu_after_fix.sql Normal file
View File

@ -0,0 +1,20 @@
-- 验证菜单修复后的状态
SELECT
'直播管理(参考)' as '对比',
m.id,
m.name,
m.component
FROM eb_system_menu m
WHERE m.name = '直播管理' OR m.pid = (SELECT id FROM eb_system_menu WHERE name = '直播管理' LIMIT 1)
ORDER BY m.pid, m.sort
LIMIT 3;
SELECT
'礼物打赏(当前)' as '对比',
m.id,
m.name,
m.component
FROM eb_system_menu m
WHERE m.name = '礼物打赏' OR m.pid = (SELECT id FROM eb_system_menu WHERE name = '礼物打赏' LIMIT 1)
ORDER BY m.pid, m.sort;

View File

@ -0,0 +1,214 @@
# 添加礼物打赏菜单配置说明
## 步骤1在数据库中添加菜单
执行以下SQL在后台管理系统中添加"礼物打赏"菜单:
```sql
-- 添加礼物管理菜单
INSERT INTO eb_system_menu (pid, name, path, component, is_show, sort, icon, create_time, update_time)
VALUES (0, '礼物管理', '/gift', 'Layout', 1, 100, 'el-icon-present', NOW(), NOW());
-- 获取刚插入的父菜单ID假设为最新的ID
SET @parent_id = LAST_INSERT_ID();
-- 添加礼物打赏子菜单
INSERT INTO eb_system_menu (pid, name, path, component, is_show, sort, icon, create_time, update_time)
VALUES (@parent_id, '礼物打赏', 'records', 'gift/records/index', 1, 1, 'el-icon-present', NOW(), NOW());
-- 添加礼物配置子菜单(可选)
INSERT INTO eb_system_menu (pid, name, path, component, is_show, sort, icon, create_time, update_time)
VALUES (@parent_id, '礼物配置', 'config', 'gift/config/index', 1, 2, 'el-icon-setting', NOW(), NOW());
-- 添加充值套餐子菜单(可选)
INSERT INTO eb_system_menu (pid, name, path, component, is_show, sort, icon, create_time, update_time)
VALUES (@parent_id, '充值套餐', 'packages', 'gift/packages/index', 1, 3, 'el-icon-coin', NOW(), NOW());
-- 查看添加的菜单
SELECT * FROM eb_system_menu WHERE name LIKE '%礼物%' OR name LIKE '%充值%';
```
## 步骤2如果没有菜单表手动配置路由
如果你的系统不是从数据库读取菜单,需要手动在前端路由文件中添加。
找到 `Zhibo/admin/src/router/index.js` 文件,添加以下路由配置:
```javascript
{
path: '/gift',
component: Layout,
redirect: '/gift/records',
name: 'Gift',
meta: {
title: '礼物管理',
icon: 'el-icon-present'
},
children: [
{
path: 'records',
name: 'GiftRecords',
component: () => import('@/views/gift/records/index'),
meta: {
title: '礼物打赏',
icon: 'el-icon-present'
}
}
]
}
```
## 步骤3验证菜单是否添加成功
1. 重新登录后台管理系统
2. 在左侧菜单栏应该能看到"礼物管理"菜单
3. 点击"礼物打赏"子菜单
4. 应该能看到礼物打赏记录页面
## 礼物打赏记录页面功能
页面会显示以下信息:
### 统计卡片
- **总礼物数**:所有时间的礼物总数量
- **总价值**:所有礼物的总价值(虚拟币)
- **今日礼物数**:今天收到的礼物数量
- **今日价值**:今天礼物的总价值
### 搜索功能
- 关键词搜索:送礼者昵称、接收者昵称、礼物名称
- 日期筛选:按开始日期和结束日期筛选
### 礼物记录表格
显示字段:
- ID
- 礼物信息(图标、名称、单价×数量)
- 总价值
- 送礼者(头像、昵称、手机号)
- 接收者/主播(头像、昵称、手机号)
- 直播间名称
- 赠送时间
### 统计功能
- 可以统计哪个用户送了多少礼物
- 可以统计哪个主播收到了多少礼物
- 可以按日期范围查看礼物记录
## 如果菜单还是没有显示
### 方法1检查菜单表
```sql
-- 查看菜单表名
SHOW TABLES LIKE '%menu%';
-- 查看菜单表结构
DESC eb_system_menu;
-- 查看现有菜单
SELECT * FROM eb_system_menu ORDER BY sort;
```
### 方法2检查权限
确保当前登录的管理员账号有权限访问新添加的菜单。
### 方法3清除缓存
1. 清除浏览器缓存
2. 重新登录后台管理系统
3. 按 Ctrl+F5 强制刷新页面
### 方法4直接访问URL
即使菜单没有显示,也可以直接在浏览器地址栏输入:
```
http://localhost:8080/#/gift/records
```
## 礼物配置说明
礼物数据已经在数据库中初始化了,包括:
- 玫瑰1虚拟币
- 巧克力5虚拟币
- 棒棒糖10虚拟币
- 冰淇淋20虚拟币
- 蛋糕50虚拟币
- 香水100虚拟币
- 口红200虚拟币
- 钻戒520虚拟币
- 跑车1314虚拟币
- 城堡5200虚拟币
可以通过以下SQL查看
```sql
SELECT * FROM eb_gift_config ORDER BY sort_order;
```
## 测试流程
### 1. 准备测试数据
```sql
-- 查看礼物配置
SELECT * FROM eb_gift_config;
-- 查看充值套餐
SELECT * FROM eb_recharge_package;
-- 查看用户余额
SELECT uid, nickname, virtual_balance FROM eb_user WHERE uid IN (41, 42, 43);
```
### 2. 通过APP端送礼物
1. 用户A登录APP
2. 充值虚拟货币
3. 进入主播B的直播间
4. 点击礼物按钮
5. 选择礼物并发送
### 3. 在后台查看记录
1. 登录后台管理系统
2. 进入"礼物管理" -> "礼物打赏"
3. 查看刚才的送礼记录
4. 可以搜索用户A或主播B
5. 查看统计数据是否正确
## 常见问题
### Q1: 菜单添加后看不到?
**A:**
1. 检查数据库中菜单是否添加成功
2. 检查`is_show`字段是否为1
3. 清除浏览器缓存并重新登录
4. 检查当前用户是否有权限
### Q2: 点击菜单报404错误
**A:**
1. 检查前端页面文件是否存在:`Zhibo/admin/src/views/gift/records/index.vue`
2. 检查路由配置是否正确
3. 重新编译前端:`npm run build:prod`
### Q3: 页面显示"暂无数据"
**A:**
1. 检查后端服务是否启动
2. 检查API接口是否正常`/api/admin/gift/records`
3. 查看浏览器控制台是否有错误
4. 检查数据库中是否有礼物记录
### Q4: 统计数据不准确?
**A:**
1. 检查数据库中的数据是否正确
2. 检查后端SQL查询是否正确
3. 清除缓存重新加载
## 相关文件
- 后端Controller`GiftAdminController.java`
- 前端API`Zhibo/admin/src/api/gift.js`
- 前端页面:`Zhibo/admin/src/views/gift/records/index.vue`
- 数据库脚本:`virtual_currency_and_gift_system.sql`
## 下一步扩展
可以继续添加以下功能:
1. 礼物配置管理页面(添加、编辑、删除礼物)
2. 充值套餐管理页面(配置充值金额和赠送比例)
3. 充值记录查看页面
4. 礼物统计图表(趋势图、排行榜)
5. 数据导出功能导出Excel

View File

@ -0,0 +1,299 @@
# 礼物系统后台管理部署说明
## 已完成的工作
### 1. 后端API ✅
- **GiftAdminController.java** - 礼物管理后台控制器
- 礼物打赏记录列表(支持搜索和日期筛选)
- 礼物统计数据(总数、总价值、今日数据)
- 礼物配置管理(增删改查)
- 充值套餐管理
- 充值记录查询
### 2. 前端页面 ✅
- **gift.js** - 礼物管理API接口
- **gift/records/index.vue** - 礼物打赏记录页面
- 统计卡片(总礼物数、总价值、今日数据)
- 搜索功能(关键词、日期范围)
- 礼物记录表格(礼物信息、送礼者、接收者、直播间、时间)
- 分页功能
## 部署步骤
### 步骤1执行数据库脚本如果还没执行
```bash
mysql -h 1.15.149.240 -u root -p zhibo < virtual_currency_and_gift_system.sql
```
### 步骤2编译后端
```bash
cd Zhibo/zhibo-h
mvn clean package -DskipTests
```
### 步骤3部署后端服务
```bash
# 停止服务
ssh root@1.15.149.240 "cd /root/zhibo && docker-compose stop crmeb-admin"
# 上传jar包
scp crmeb-admin/target/Crmeb-admin.jar root@1.15.149.240:/root/zhibo/
# 启动服务
ssh root@1.15.149.240 "cd /root/zhibo && docker-compose up -d crmeb-admin"
```
### 步骤4编译前端
```bash
cd Zhibo/admin
npm run build:prod
```
### 步骤5部署前端
```bash
# 上传到服务器
scp -r dist/* root@1.15.149.240:/root/zhibo/admin/
```
### 步骤6配置路由
`Zhibo/admin/src/router/index.js` 中添加礼物管理路由:
```javascript
{
path: '/gift',
component: Layout,
redirect: '/gift/records',
name: 'Gift',
meta: { title: '礼物管理', icon: 'el-icon-present' },
children: [
{
path: 'records',
name: 'GiftRecords',
component: () => import('@/views/gift/records/index'),
meta: { title: '礼物打赏', icon: 'el-icon-present' }
}
]
}
```
## 页面功能说明
### 礼物打赏记录页面
#### 统计卡片
- **总礼物数**:所有时间的礼物总数量
- **总价值**:所有礼物的总价值(虚拟币)
- **今日礼物数**:今天收到的礼物数量
- **今日价值**:今天礼物的总价值
#### 搜索功能
- **关键词搜索**:可搜索送礼者昵称、接收者昵称、礼物名称
- **日期筛选**:可按开始日期和结束日期筛选记录
#### 礼物记录表格
显示字段:
- ID
- 礼物信息(图标、名称、单价、数量)
- 总价值
- 送礼者(头像、昵称、手机号)
- 接收者/主播(头像、昵称、手机号)
- 直播间名称
- 赠送时间
#### 特殊处理
- 匿名送礼显示为"匿名用户"
- 没有直播间的显示"-"
- 支持分页查看
## API接口说明
### 1. 获取礼物打赏记录
```
GET /api/admin/gift/records
参数:
- keyword: 关键词(可选)
- startDate: 开始日期(可选)
- endDate: 结束日期(可选)
- page: 页码默认1
- limit: 每页数量默认20
```
### 2. 获取礼物统计
```
GET /api/admin/gift/statistics
返回:
- totalCount: 总礼物数
- totalValue: 总价值
- todayCount: 今日礼物数
- todayValue: 今日价值
```
### 3. 获取礼物配置列表
```
GET /api/admin/gift/config/list
```
### 4. 添加礼物配置
```
POST /api/admin/gift/config/add
请求体:
{
"name": "礼物名称",
"icon": "图标URL",
"price": 价格,
"animation": "动画效果",
"sortOrder": 排序
}
```
### 5. 更新礼物配置
```
POST /api/admin/gift/config/update
```
### 6. 删除礼物配置
```
POST /api/admin/gift/config/delete/{id}
```
### 7. 获取充值套餐列表
```
GET /api/admin/gift/recharge/packages
```
### 8. 更新充值套餐
```
POST /api/admin/gift/recharge/package/update
```
### 9. 获取充值记录
```
GET /api/admin/gift/recharge/records
```
## 测试流程
### 1. 测试礼物打赏记录查看
1. 登录后台管理系统
2. 进入"礼物管理" -> "礼物打赏"
3. 查看统计卡片数据是否正确
4. 查看礼物记录列表是否显示
5. 测试搜索功能
6. 测试日期筛选功能
7. 测试分页功能
### 2. 测试数据准备
如果没有测试数据可以通过APP端送礼物来生成
1. 在APP中充值虚拟货币
2. 进入直播间
3. 送礼物给主播
4. 返回后台查看记录
### 3. 验证数据准确性
```sql
-- 查询礼物记录
SELECT * FROM eb_gift_record ORDER BY create_time DESC LIMIT 10;
-- 查询统计数据
SELECT
COUNT(*) as total_count,
SUM(total_price) as total_value,
SUM(CASE WHEN DATE(create_time) = CURDATE() THEN 1 ELSE 0 END) as today_count,
SUM(CASE WHEN DATE(create_time) = CURDATE() THEN total_price ELSE 0 END) as today_value
FROM eb_gift_record;
```
## 后续扩展功能
### 1. 礼物配置管理页面
创建独立页面管理礼物配置:
- 添加新礼物
- 编辑礼物信息
- 上传礼物图标
- 设置礼物价格
- 启用/禁用礼物
### 2. 充值套餐管理页面
创建独立页面管理充值套餐:
- 添加新套餐
- 编辑套餐信息
- 设置赠送比例
- 设置热门推荐
- 启用/禁用套餐
### 3. 充值记录查看页面
创建独立页面查看充值记录:
- 用户充值记录
- 充值金额统计
- 支付方式统计
- 充值趋势图表
### 4. 数据导出功能
- 导出礼物记录为Excel
- 导出充值记录为Excel
- 生成统计报表
### 5. 图表统计
- 礼物趋势图(按日期)
- 热门礼物排行榜
- 送礼用户排行榜
- 收礼主播排行榜
## 注意事项
1. **权限控制**
- 确保只有管理员可以访问礼物管理页面
- 敏感操作需要二次确认
2. **数据安全**
- 礼物记录不可删除,只能查看
- 充值记录不可修改
- 所有操作记录日志
3. **性能优化**
- 大量数据时使用分页
- 统计数据可以缓存
- 定期归档历史数据
4. **用户体验**
- 加载状态提示
- 错误信息友好提示
- 操作成功反馈
## 常见问题
### Q1: 后台看不到礼物记录?
**A:** 检查以下几点:
1. 数据库表是否创建成功
2. 后端服务是否重启
3. 前端是否重新编译
4. 是否有测试数据
### Q2: 统计数据不准确?
**A:**
1. 检查SQL查询是否正确
2. 检查数据库数据是否完整
3. 清除浏览器缓存重新加载
### Q3: 搜索功能不工作?
**A:**
1. 检查后端API是否正确处理参数
2. 检查前端是否正确传递参数
3. 查看浏览器控制台错误信息
## 文件清单
### 后端文件
- `Zhibo/zhibo-h/crmeb-admin/src/main/java/com/zbkj/admin/controller/GiftAdminController.java`
### 前端文件
- `Zhibo/admin/src/api/gift.js`
- `Zhibo/admin/src/views/gift/records/index.vue`
### 数据库文件
- `virtual_currency_and_gift_system.sql`
## 联系方式
如有问题,请查看相关文档或联系开发团队。

View File

@ -0,0 +1,301 @@
# 礼物系统完整开发文档
## 📋 目录
1. [系统概述](#系统概述)
2. [数据库设计](#数据库设计)
3. [后端API接口](#后端api接口)
4. [前端页面](#前端页面)
5. [APP端集成](#app端集成)
6. [部署指南](#部署指南)
7. [测试验证](#测试验证)
---
## 系统概述
### 功能简介
礼物系统是直播平台的核心功能之一,支持用户在直播间送礼物给主播,包含以下核心功能:
- **礼物管理**:后台管理员可以添加、编辑、删除、启用/禁用礼物
- **打赏记录**:记录所有用户的礼物打赏行为
- **统计分析**:礼物数量、启用/禁用状态、总价值统计
- **APP集成**:移动端获取礼物列表并发送礼物
### 技术栈
- **后端**Java + Spring Boot + MyBatis + MySQL
- **前端管理**Vue.js + Element UI
- **移动端**Android (Java)
- **数据库**MySQL 5.7+
### 核心数据表
- `eb_gift` - 礼物主数据表20个礼物
- `eb_gift_record` - 礼物打赏记录表
---
## 数据库设计
### 1. eb_gift 表(礼物主数据表)
```sql
CREATE TABLE `eb_gift` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '礼物ID',
`name` varchar(50) NOT NULL COMMENT '礼物名称',
`image` varchar(255) DEFAULT NULL COMMENT '礼物图片URL',
`diamond_price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '钻石价格',
`intimacy` int(11) NOT NULL DEFAULT '0' COMMENT '亲密度',
`is_heartbeat` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否心动礼物 0-否 1-是',
`level` int(11) NOT NULL DEFAULT '1' COMMENT '礼物等级',
`sort` int(11) NOT NULL DEFAULT '0' COMMENT '排序',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态 0-禁用 1-启用',
`is_deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否删除 0-否 1-是',
`buy_type` varchar(20) DEFAULT 'diamond' COMMENT '购买类型',
`belong` varchar(20) DEFAULT 'common' COMMENT '归属',
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_status` (`status`),
KEY `idx_sort` (`sort`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='礼物表';
```
**字段说明:**
| 字段 | 类型 | 说明 |
|------|------|------|
| id | int | 礼物唯一标识 |
| name | varchar(50) | 礼物名称,如"玫瑰花"、"钻戒" |
| image | varchar(255) | 礼物图片URL |
| diamond_price | decimal(10,2) | 钻石价格 |
| intimacy | int | 赠送礼物增加的亲密度 |
| is_heartbeat | tinyint | 是否为心动礼物(特殊标记) |
| level | int | 礼物等级 |
| sort | int | 显示排序 |
| status | tinyint | 启用状态0-禁用1-启用) |
| is_deleted | tinyint | 软删除标记 |
| buy_type | varchar(20) | 购买类型diamond-钻石) |
| belong | varchar(20) | 归属common-通用live-直播专属) |
| remark | varchar(255) | 备注信息 |
### 2. eb_gift_record 表(打赏记录表)
```sql
CREATE TABLE `eb_gift_record` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '记录ID',
`sender_id` int(11) NOT NULL COMMENT '送礼用户ID',
`sender_nickname` varchar(50) DEFAULT NULL COMMENT '送礼用户昵称',
`receiver_id` int(11) NOT NULL COMMENT '收礼用户ID',
`receiver_nickname` varchar(50) DEFAULT NULL COMMENT '收礼用户昵称',
`gift_id` int(11) NOT NULL COMMENT '礼物ID',
`gift_count` int(11) NOT NULL DEFAULT '1' COMMENT '礼物数量',
`total_price` decimal(10,2) NOT NULL COMMENT '总价格',
`room_id` int(11) DEFAULT NULL COMMENT '直播间ID',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`),
KEY `idx_sender` (`sender_id`),
KEY `idx_receiver` (`receiver_id`),
KEY `idx_gift` (`gift_id`),
KEY `idx_room` (`room_id`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='礼物打赏记录表';
```
**字段说明:**
| 字段 | 类型 | 说明 |
|------|------|------|
| id | int | 记录唯一标识 |
| sender_id | int | 送礼用户ID |
| sender_nickname | varchar(50) | 送礼用户昵称 |
| receiver_id | int | 收礼用户ID主播 |
| receiver_nickname | varchar(50) | 收礼用户昵称 |
| gift_id | int | 礼物ID关联eb_gift表 |
| gift_count | int | 礼物数量 |
| total_price | decimal(10,2) | 总价格(单价×数量) |
| room_id | int | 直播间ID |
| create_time | timestamp | 打赏时间 |
### 3. 初始化数据
系统预置了20个礼物
1. 玫瑰花 - 10钻石
2. 爱心 - 20钻石
3. 火箭 - 100钻石
4. 皇冠 - 500钻石
5. 跑车 - 1000钻石
6. 城堡 - 5000钻石
7. 棒棒糖 - 5钻石
8. 啤酒 - 15钻石
9. 蛋糕 - 50钻石
10. 钻戒 - 2000钻石
11-20. 其他礼物...
---
## 后端API接口
### 基础路径
```
http://your-server.com/api/admin/gift
```
### 1. 礼物列表(管理端)
**接口地址:** `GET /list`
**请求参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| page | int | 否 | 页码默认1 |
| limit | int | 否 | 每页数量默认10 |
| name | string | 否 | 礼物名称(模糊搜索) |
| status | int | 否 | 状态0-禁用1-启用) |
**响应示例:**
```json
{
"code": 200,
"message": null,
"data": {
"page": 1,
"limit": 10,
"totalPage": 2,
"total": 20,
"list": [
{
"id": 1,
"name": "玫瑰花",
"image": "https://example.com/gifts/rose.png",
"diamondPrice": "10.00",
"intimacy": 10,
"status": true,
"isHeartbeat": false,
"buyType": "diamond",
"belong": "common",
"remark": "浪漫玫瑰",
"level": 1,
"sort": 0,
"createTime": "2025-12-30 20:25:16",
"updateTime": "2025-12-30 20:25:16"
}
]
}
}
```
### 2. 添加礼物
**接口地址:** `POST /add`
**请求体:**
```json
{
"name": "新礼物",
"image": "https://example.com/gift.png",
"diamondPrice": 100,
"intimacy": 100,
"isHeartbeat": false,
"level": 1,
"sort": 0,
"status": true,
"buyType": "diamond",
"belong": "common",
"remark": "备注信息"
}
```
**响应示例:**
```json
{
"code": 200,
"message": "添加成功",
"data": null
}
```
### 3. 编辑礼物
**接口地址:** `POST /update`
**请求体:**
```json
{
"id": 1,
"name": "更新后的名称",
"image": "https://example.com/new-gift.png",
"diamondPrice": 150,
"intimacy": 150,
"isHeartbeat": true,
"level": 2,
"sort": 1,
"status": true,
"buyType": "diamond",
"belong": "live",
"remark": "更新后的备注"
}
```
### 4. 删除礼物
**接口地址:** `POST /delete/{id}`
**路径参数:**
- `id`: 礼物ID
**响应示例:**
```json
{
"code": 200,
"message": "删除成功",
"data": null
}
```
### 5. 修改礼物状态
**接口地址:** `POST /status/{id}`
**路径参数:**
- `id`: 礼物ID
**请求参数:**
- `status`: 状态0-禁用1-启用)
**响应示例:**
```json
{
"code": 200,
"message": "状态修改成功",
"data": null
}
```
### 6. 打赏记录列表
**接口地址:** `GET /records`
**请求参数:**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| page | int | 否 | 页码默认1 |
| limit | int | 否 | 每页数量默认20 |
| senderName | string | 否 | 送礼用户昵称(模糊搜索) |
| receiverName | string | 否 | 收礼用户昵称(模糊搜索) |
| giftName | string | 否 | 礼物名称(模糊搜索) |
**响应示例:**

View File

@ -0,0 +1,145 @@
# 礼物系统完整部署指南
## 一、系统功能
### 1. 礼物列表(礼物配置)
- 显示所有可用礼物(玫瑰、巧克力、跑车等)
- 可以添加、编辑、删除礼物
- 设置礼物价格、图标、排序
- 启用/禁用礼物
### 2. 打赏记录
- 显示所有用户的打赏记录
- 谁送了什么礼物给哪个主播
- 在哪个直播间送的
- 礼物数量和总价值
- 支持按关键词、日期筛选
- 显示统计数据(总礼物数、总价值、今日数据)
### 3. 充值套餐
- 显示所有充值套餐配置
- 可以编辑套餐金额、赠送比例
- 设置热门推荐
## 二、部署步骤
### 步骤1部署后端
```bash
# 运行部署脚本
deploy-gift-records-fix.bat
```
或手动执行:
```bash
cd Zhibo/zhibo-h
mvn clean package -DskipTests -Pdev
# 上传到服务器
scp crmeb-admin/target/crmeb-admin.jar root@1.15.149.240:/www/server/java/
# 重启服务
ssh root@1.15.149.240 "cd /www/server/java && ./stop.sh && ./start.sh"
```
### 步骤2部署前端
```bash
cd Zhibo/admin
npm run build:prod
# 上传到服务器
scp -r dist/* root@1.15.149.240:/www/wwwroot/admin/
```
### 步骤3验证数据
运行SQL检查数据
```sql
-- 检查礼物配置
SELECT * FROM eb_gift_config ORDER BY sort_order;
-- 检查礼物记录
SELECT COUNT(*) FROM eb_gift_record;
-- 检查充值套餐
SELECT * FROM eb_recharge_package ORDER BY sort_order;
```
## 三、菜单结构
```
礼物打赏
├── 礼物列表 - 管理礼物配置(玫瑰、巧克力等)
├── 打赏记录 - 查看所有打赏记录和统计
└── 充值套餐 - 管理充值套餐配置
```
## 四、API接口
### 礼物配置
- GET `/api/admin/gift/config/list` - 获取礼物列表
- POST `/api/admin/gift/config/add` - 添加礼物
- POST `/api/admin/gift/config/update` - 更新礼物
- POST `/api/admin/gift/config/delete/{id}` - 删除礼物
### 打赏记录
- GET `/api/admin/gift/records` - 获取打赏记录列表
- GET `/api/admin/gift/statistics` - 获取统计数据
### 充值套餐
- GET `/api/admin/gift/recharge/packages` - 获取充值套餐
- POST `/api/admin/gift/recharge/package/update` - 更新套餐
## 五、数据库表
### eb_gift_config - 礼物配置表
存储礼物的基本信息(名称、图标、价格等)
### eb_gift_record - 礼物记录表
存储所有打赏记录,包括:
- sender_id, sender_name - 送礼者
- receiver_id, receiver_name - 接收者(主播)
- room_id - 直播间
- gift_id, gift_name - 礼物信息
- quantity - 数量
- total_price - 总价
### eb_recharge_package - 充值套餐表
存储充值套餐配置
### eb_user.virtual_balance - 用户虚拟货币余额
存储在用户表的 virtual_balance 字段
## 六、测试数据
数据库中已有测试数据:
- 10个礼物配置玫瑰、巧克力、棒棒糖等
- 12条打赏记录
- 6个充值套餐
- 测试用户43、44有10000虚拟币余额
## 七、常见问题
### 1. 页面显示"暂无数据"
- 检查后端是否正常启动
- 检查API接口是否返回数据
- 查看浏览器控制台是否有错误
### 2. 数据不显示
- 确认数据库中有数据
- 检查后端SQL查询是否正确
- 查看后端日志
### 3. 字段不匹配错误
- 已修复quantity 字段
- 已修复sender_nickname, receiver_nickname 使用 COALESCE
- 已修复is_live 替代 status
## 八、后续优化
1. 添加礼物图片上传功能
2. 添加礼物动画效果预览
3. 添加打赏记录导出功能
4. 添加收礼/送礼排行榜
5. 添加礼物收益统计图表