历史记录与用户拉黑,用户封禁功能完善。

This commit is contained in:
cxytw 2026-01-04 19:40:45 +08:00
parent 86c37cd688
commit 25274c523b
81 changed files with 5968 additions and 0 deletions

View File

View File

131
Zhibo/admin/src/api/ban.js Normal file
View File

@ -0,0 +1,131 @@
import request from '@/utils/request';
// ==================== 用户封禁 ====================
/**
* 获取用户封禁列表
*/
export function userBanListApi(params) {
return request({
url: '/admin/ban/user/list',
method: 'get',
params
});
}
/**
* 封禁用户
*/
export function userBanAddApi(data) {
return request({
url: '/admin/ban/user/add',
method: 'post',
data
});
}
/**
* 解除用户封禁
*/
export function userBanUnbanApi(id) {
return request({
url: `/admin/ban/user/unban/${id}`,
method: 'post'
});
}
/**
* 删除用户封禁记录
*/
export function userBanDeleteApi(id) {
return request({
url: `/admin/ban/user/delete/${id}`,
method: 'post'
});
}
/**
* 批量删除用户封禁记录
*/
export function userBanBatchDeleteApi(ids) {
return request({
url: '/admin/ban/user/batch-delete',
method: 'post',
data: ids
});
}
/**
* 检查用户封禁状态
*/
export function checkUserBanApi(userId) {
return request({
url: `/admin/ban/user/check/${userId}`,
method: 'get'
});
}
// ==================== 房间封禁 ====================
/**
* 获取房间封禁列表
*/
export function roomBanListApi(params) {
return request({
url: '/admin/ban/room/list',
method: 'get',
params
});
}
/**
* 封禁房间
*/
export function roomBanAddApi(data) {
return request({
url: '/admin/ban/room/add',
method: 'post',
data
});
}
/**
* 解除房间封禁
*/
export function roomBanUnbanApi(id) {
return request({
url: `/admin/ban/room/unban/${id}`,
method: 'post'
});
}
/**
* 删除房间封禁记录
*/
export function roomBanDeleteApi(id) {
return request({
url: `/admin/ban/room/delete/${id}`,
method: 'post'
});
}
/**
* 批量删除房间封禁记录
*/
export function roomBanBatchDeleteApi(ids) {
return request({
url: '/admin/ban/room/batch-delete',
method: 'post',
data: ids
});
}
/**
* 检查房间封禁状态
*/
export function checkRoomBanApi(roomId) {
return request({
url: `/admin/ban/room/check/${roomId}`,
method: 'get'
});
}

View File

@ -0,0 +1,315 @@
<template>
<div class="divBox">
<el-card shadow="never" class="ivu-mt">
<div class="padding-add">
<!-- 搜索区域 -->
<div class="mb20">
<el-form :inline="true" :model="searchForm" size="small">
<el-form-item>
<el-input v-model="searchForm.roomName" placeholder="房间名称" clearable style="width: 150px;"></el-input>
</el-form-item>
<el-form-item>
<el-input v-model="searchForm.streamerName" placeholder="主播名称" clearable style="width: 150px;"></el-input>
</el-form-item>
<el-form-item>
<el-select v-model="searchForm.banType" placeholder="封禁类型" clearable style="width: 120px;">
<el-option label="永久封禁" value="permanent"></el-option>
<el-option label="临时封禁" value="temporary"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-select v-model="searchForm.status" placeholder="状态" clearable style="width: 100px;">
<el-option label="生效中" :value="1"></el-option>
<el-option label="已解除" :value="0"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleSearch">搜索</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</div>
<!-- 操作按钮 -->
<div class="mb20">
<el-button type="primary" size="small" icon="el-icon-plus" @click="handleAdd">封禁房间</el-button>
<el-button type="danger" size="small" :disabled="!selectedIds.length" @click="handleBatchDelete">批量删除</el-button>
</div>
<!-- 数据表格 -->
<el-table :data="tableData" v-loading="loading" border @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column prop="id" label="ID" width="80" align="center" />
<el-table-column label="房间信息" min-width="200" align="center">
<template slot-scope="scope">
<div class="room-info">
<el-image
v-if="scope.row.cover_image"
:src="scope.row.cover_image"
style="width: 60px; height: 40px; border-radius: 4px;"
fit="cover">
</el-image>
<div class="room-detail">
<div class="room-name">{{ scope.row.room_name || '未知房间' }}</div>
<div class="streamer-name">主播{{ scope.row.streamer_name || '-' }}</div>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="封禁类型" width="100" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.ban_type === 'permanent' ? 'danger' : 'warning'" size="small">
{{ scope.row.ban_type === 'permanent' ? '永久封禁' : '临时封禁' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="reason" label="封禁原因" min-width="200" align="center" show-overflow-tooltip />
<el-table-column label="封禁时长" width="100" align="center">
<template slot-scope="scope">
{{ scope.row.ban_type === 'permanent' ? '永久' : (scope.row.duration_days + '天') }}
</template>
</el-table-column>
<el-table-column prop="expire_time" label="到期时间" width="160" align="center">
<template slot-scope="scope">
{{ scope.row.expire_time || '-' }}
</template>
</el-table-column>
<el-table-column label="状态" width="90" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.status === 1 ? 'danger' : 'info'" size="small">
{{ scope.row.status === 1 ? '生效中' : '已解除' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="create_time" label="封禁时间" width="160" align="center" />
<el-table-column label="操作" width="180" align="center" fixed="right">
<template slot-scope="scope">
<el-button
v-if="scope.row.status === 1"
type="success"
size="mini"
@click="handleUnban(scope.row)">解除封禁</el-button>
<el-button type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="acea-row row-right page mt20">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="searchForm.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="searchForm.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
></el-pagination>
</div>
</div>
</el-card>
<!-- 封禁房间对话框 -->
<el-dialog title="封禁房间" :visible.sync="dialogVisible" width="500px">
<el-form :model="formData" :rules="formRules" ref="formRef" label-width="100px">
<el-form-item label="房间ID" prop="roomId">
<el-input v-model="formData.roomId" placeholder="请输入要封禁的房间ID"></el-input>
</el-form-item>
<el-form-item label="封禁类型" prop="banType">
<el-radio-group v-model="formData.banType">
<el-radio label="permanent">永久封禁</el-radio>
<el-radio label="temporary">临时封禁</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="formData.banType === 'temporary'" label="封禁天数" prop="duration">
<el-input-number v-model="formData.duration" :min="1" :max="365" placeholder="天数"></el-input-number>
<span class="ml10"></span>
</el-form-item>
<el-form-item label="封禁原因" prop="reason">
<el-input type="textarea" v-model="formData.reason" :rows="3" placeholder="请输入封禁原因"></el-input>
</el-form-item>
</el-form>
<span slot="footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit" :loading="submitLoading">确定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { roomBanListApi, roomBanAddApi, roomBanUnbanApi, roomBanDeleteApi, roomBanBatchDeleteApi } from '@/api/ban';
export default {
name: 'RoomBan',
data() {
return {
searchForm: {
roomName: '',
streamerName: '',
banType: '',
status: '',
page: 1,
limit: 10
},
tableData: [],
total: 0,
loading: false,
selectedIds: [],
dialogVisible: false,
submitLoading: false,
formData: {
roomId: '',
banType: 'permanent',
duration: 7,
reason: ''
},
formRules: {
roomId: [{ required: true, message: '请输入房间ID', trigger: 'blur' }],
banType: [{ required: true, message: '请选择封禁类型', trigger: 'change' }],
reason: [{ required: true, message: '请输入封禁原因', trigger: 'blur' }]
}
};
},
mounted() {
this.getList();
},
methods: {
async getList() {
this.loading = true;
try {
const params = {
page: this.searchForm.page,
limit: this.searchForm.limit,
roomName: this.searchForm.roomName || undefined,
streamerName: this.searchForm.streamerName || undefined,
banType: this.searchForm.banType || undefined,
status: this.searchForm.status !== '' ? this.searchForm.status : undefined
};
const res = await roomBanListApi(params);
if (res) {
this.tableData = res.list || [];
this.total = res.total || 0;
} else {
this.tableData = [];
this.total = 0;
}
} catch (error) {
console.error('获取房间封禁列表失败:', error);
this.tableData = [];
this.total = 0;
if (error && error.message) {
this.$message.error(error.message);
}
} finally {
this.loading = false;
}
},
handleSearch() {
this.searchForm.page = 1;
this.getList();
},
handleReset() {
this.searchForm = { roomName: '', streamerName: '', banType: '', status: '', page: 1, limit: 10 };
this.getList();
},
handleSelectionChange(selection) {
this.selectedIds = selection.map(item => item.id);
},
handleAdd() {
this.formData = { roomId: '', banType: 'permanent', duration: 7, reason: '' };
this.dialogVisible = true;
},
async handleSubmit() {
this.$refs.formRef.validate(async (valid) => {
if (!valid) return;
this.submitLoading = true;
try {
await roomBanAddApi(this.formData);
this.$message.success('封禁成功');
this.dialogVisible = false;
this.getList();
} catch (error) {
this.$message.error(error.message || '封禁失败');
} finally {
this.submitLoading = false;
}
});
},
handleUnban(row) {
this.$confirm('确定要解除该房间的封禁吗?', '提示', { type: 'warning' }).then(async () => {
try {
await roomBanUnbanApi(row.id);
this.$message.success('解除封禁成功');
this.getList();
} catch (error) {
this.$message.error(error.message || '解除封禁失败');
}
}).catch(() => {});
},
handleDelete(row) {
this.$confirm('确定要删除该封禁记录吗?', '提示', { type: 'warning' }).then(async () => {
try {
await roomBanDeleteApi(row.id);
this.$message.success('删除成功');
this.getList();
} catch (error) {
this.$message.error(error.message || '删除失败');
}
}).catch(() => {});
},
handleBatchDelete() {
if (!this.selectedIds.length) {
this.$message.warning('请先选择要删除的记录');
return;
}
this.$confirm(`确定要删除选中的 ${this.selectedIds.length} 条记录吗?`, '提示', { type: 'warning' }).then(async () => {
try {
await roomBanBatchDeleteApi(this.selectedIds);
this.$message.success('批量删除成功');
this.selectedIds = [];
this.getList();
} catch (error) {
this.$message.error(error.message || '批量删除失败');
}
}).catch(() => {});
},
handleSizeChange(val) {
this.searchForm.limit = val;
this.getList();
},
handleCurrentChange(val) {
this.searchForm.page = val;
this.getList();
}
}
};
</script>
<style scoped lang="scss">
.mb20 { margin-bottom: 20px; }
.mt20 { margin-top: 20px; }
.ml10 { margin-left: 10px; }
.room-info {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
.room-detail {
text-align: left;
.room-name {
font-weight: 500;
color: #303133;
}
.streamer-name {
font-size: 12px;
color: #909399;
}
}
}
</style>

View File

@ -0,0 +1,310 @@
<template>
<div class="divBox">
<el-card shadow="never" class="ivu-mt">
<div class="padding-add">
<!-- 搜索区域 -->
<div class="mb20">
<el-form :inline="true" :model="searchForm" size="small">
<el-form-item>
<el-input v-model="searchForm.nickname" placeholder="用户昵称" clearable style="width: 150px;"></el-input>
</el-form-item>
<el-form-item>
<el-input v-model="searchForm.phone" placeholder="手机号" clearable style="width: 150px;"></el-input>
</el-form-item>
<el-form-item>
<el-select v-model="searchForm.banType" placeholder="封禁类型" clearable style="width: 120px;">
<el-option label="永久封禁" value="permanent"></el-option>
<el-option label="临时封禁" value="temporary"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-select v-model="searchForm.status" placeholder="状态" clearable style="width: 100px;">
<el-option label="生效中" :value="1"></el-option>
<el-option label="已解除" :value="0"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleSearch">搜索</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</div>
<!-- 操作按钮 -->
<div class="mb20">
<el-button type="primary" size="small" icon="el-icon-plus" @click="handleAdd">封禁用户</el-button>
<el-button type="danger" size="small" :disabled="!selectedIds.length" @click="handleBatchDelete">批量删除</el-button>
</div>
<!-- 数据表格 -->
<el-table :data="tableData" v-loading="loading" border @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column prop="id" label="ID" width="80" align="center" />
<el-table-column label="用户信息" min-width="180" align="center">
<template slot-scope="scope">
<div class="user-info">
<el-avatar :src="scope.row.avatar" :size="40" icon="el-icon-user"></el-avatar>
<div class="user-detail">
<div class="nickname">{{ scope.row.nickname || '未知用户' }}</div>
<div class="phone">{{ scope.row.phone || '-' }}</div>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="封禁类型" width="100" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.ban_type === 'permanent' ? 'danger' : 'warning'" size="small">
{{ scope.row.ban_type === 'permanent' ? '永久封禁' : '临时封禁' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="reason" label="封禁原因" min-width="200" align="center" show-overflow-tooltip />
<el-table-column label="封禁时长" width="100" align="center">
<template slot-scope="scope">
{{ scope.row.ban_type === 'permanent' ? '永久' : (scope.row.duration_days + '天') }}
</template>
</el-table-column>
<el-table-column prop="expire_time" label="到期时间" width="160" align="center">
<template slot-scope="scope">
{{ scope.row.expire_time || '-' }}
</template>
</el-table-column>
<el-table-column label="状态" width="90" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.status === 1 ? 'danger' : 'info'" size="small">
{{ scope.row.status === 1 ? '生效中' : '已解除' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="create_time" label="封禁时间" width="160" align="center" />
<el-table-column label="操作" width="180" align="center" fixed="right">
<template slot-scope="scope">
<el-button
v-if="scope.row.status === 1"
type="success"
size="mini"
@click="handleUnban(scope.row)">解除封禁</el-button>
<el-button type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="acea-row row-right page mt20">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="searchForm.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="searchForm.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
></el-pagination>
</div>
</div>
</el-card>
<!-- 封禁用户对话框 -->
<el-dialog title="封禁用户" :visible.sync="dialogVisible" width="500px">
<el-form :model="formData" :rules="formRules" ref="formRef" label-width="100px">
<el-form-item label="用户ID" prop="userId">
<el-input v-model="formData.userId" placeholder="请输入要封禁的用户ID"></el-input>
</el-form-item>
<el-form-item label="封禁类型" prop="banType">
<el-radio-group v-model="formData.banType">
<el-radio label="permanent">永久封禁</el-radio>
<el-radio label="temporary">临时封禁</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="formData.banType === 'temporary'" label="封禁天数" prop="duration">
<el-input-number v-model="formData.duration" :min="1" :max="365" placeholder="天数"></el-input-number>
<span class="ml10"></span>
</el-form-item>
<el-form-item label="封禁原因" prop="reason">
<el-input type="textarea" v-model="formData.reason" :rows="3" placeholder="请输入封禁原因"></el-input>
</el-form-item>
</el-form>
<span slot="footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit" :loading="submitLoading">确定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { userBanListApi, userBanAddApi, userBanUnbanApi, userBanDeleteApi, userBanBatchDeleteApi } from '@/api/ban';
export default {
name: 'UserBan',
data() {
return {
searchForm: {
nickname: '',
phone: '',
banType: '',
status: '',
page: 1,
limit: 10
},
tableData: [],
total: 0,
loading: false,
selectedIds: [],
dialogVisible: false,
submitLoading: false,
formData: {
userId: '',
banType: 'permanent',
duration: 7,
reason: ''
},
formRules: {
userId: [{ required: true, message: '请输入用户ID', trigger: 'blur' }],
banType: [{ required: true, message: '请选择封禁类型', trigger: 'change' }],
reason: [{ required: true, message: '请输入封禁原因', trigger: 'blur' }]
}
};
},
mounted() {
this.getList();
},
methods: {
async getList() {
this.loading = true;
try {
const params = {
page: this.searchForm.page,
limit: this.searchForm.limit,
nickname: this.searchForm.nickname || undefined,
phone: this.searchForm.phone || undefined,
banType: this.searchForm.banType || undefined,
status: this.searchForm.status !== '' ? this.searchForm.status : undefined
};
const res = await userBanListApi(params);
if (res) {
this.tableData = res.list || [];
this.total = res.total || 0;
} else {
this.tableData = [];
this.total = 0;
}
} catch (error) {
console.error('获取用户封禁列表失败:', error);
this.tableData = [];
this.total = 0;
if (error && error.message) {
this.$message.error(error.message);
}
} finally {
this.loading = false;
}
},
handleSearch() {
this.searchForm.page = 1;
this.getList();
},
handleReset() {
this.searchForm = { nickname: '', phone: '', banType: '', status: '', page: 1, limit: 10 };
this.getList();
},
handleSelectionChange(selection) {
this.selectedIds = selection.map(item => item.id);
},
handleAdd() {
this.formData = { userId: '', banType: 'permanent', duration: 7, reason: '' };
this.dialogVisible = true;
},
async handleSubmit() {
this.$refs.formRef.validate(async (valid) => {
if (!valid) return;
this.submitLoading = true;
try {
await userBanAddApi(this.formData);
this.$message.success('封禁成功');
this.dialogVisible = false;
this.getList();
} catch (error) {
this.$message.error(error.message || '封禁失败');
} finally {
this.submitLoading = false;
}
});
},
handleUnban(row) {
this.$confirm('确定要解除该用户的封禁吗?', '提示', { type: 'warning' }).then(async () => {
try {
await userBanUnbanApi(row.id);
this.$message.success('解除封禁成功');
this.getList();
} catch (error) {
this.$message.error(error.message || '解除封禁失败');
}
}).catch(() => {});
},
handleDelete(row) {
this.$confirm('确定要删除该封禁记录吗?', '提示', { type: 'warning' }).then(async () => {
try {
await userBanDeleteApi(row.id);
this.$message.success('删除成功');
this.getList();
} catch (error) {
this.$message.error(error.message || '删除失败');
}
}).catch(() => {});
},
handleBatchDelete() {
if (!this.selectedIds.length) {
this.$message.warning('请先选择要删除的记录');
return;
}
this.$confirm(`确定要删除选中的 ${this.selectedIds.length} 条记录吗?`, '提示', { type: 'warning' }).then(async () => {
try {
await userBanBatchDeleteApi(this.selectedIds);
this.$message.success('批量删除成功');
this.selectedIds = [];
this.getList();
} catch (error) {
this.$message.error(error.message || '批量删除失败');
}
}).catch(() => {});
},
handleSizeChange(val) {
this.searchForm.limit = val;
this.getList();
},
handleCurrentChange(val) {
this.searchForm.page = val;
this.getList();
}
}
};
</script>
<style scoped lang="scss">
.mb20 { margin-bottom: 20px; }
.mt20 { margin-top: 20px; }
.ml10 { margin-left: 10px; }
.user-info {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
.user-detail {
text-align: left;
.nickname {
font-weight: 500;
color: #303133;
}
.phone {
font-size: 12px;
color: #909399;
}
}
}
</style>

View File

@ -0,0 +1,370 @@
package com.zbkj.admin.controller;
import com.zbkj.common.page.CommonPage;
import com.zbkj.common.result.CommonResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.*;
/**
* 封禁管理控制器管理后台
* 提供用户封禁房间封禁功能
*/
@Slf4j
@RestController
@RequestMapping("api/admin/ban")
@Api(tags = "封禁管理")
@Validated
public class BanController {
@Autowired
private JdbcTemplate jdbcTemplate;
// ==================== 用户封禁 ====================
@ApiOperation(value = "获取用户封禁列表")
@GetMapping("/user/list")
public CommonResult<CommonPage<Map<String, Object>>> getUserBanList(
@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "limit", defaultValue = "10") Integer limit,
@RequestParam(value = "nickname", required = false) String nickname,
@RequestParam(value = "phone", required = false) String phone,
@RequestParam(value = "banType", required = false) String banType,
@RequestParam(value = "status", required = false) Integer status) {
StringBuilder sql = new StringBuilder(
"SELECT ub.*, u.nickname, u.avatar, u.phone " +
"FROM eb_user_ban ub " +
"LEFT JOIN eb_user u ON ub.user_id = u.uid " +
"WHERE 1=1");
List<Object> params = new ArrayList<>();
if (nickname != null && !nickname.trim().isEmpty()) {
sql.append(" AND u.nickname LIKE ?");
params.add("%" + nickname.trim() + "%");
}
if (phone != null && !phone.trim().isEmpty()) {
sql.append(" AND u.phone LIKE ?");
params.add("%" + phone.trim() + "%");
}
if (banType != null && !banType.trim().isEmpty()) {
sql.append(" AND ub.ban_type = ?");
params.add(banType);
}
if (status != null) {
sql.append(" AND ub.status = ?");
params.add(status);
}
// 计算总数
String countSql = sql.toString().replace("SELECT ub.*, u.nickname, u.avatar, u.phone", "SELECT COUNT(*)");
Integer total = jdbcTemplate.queryForObject(countSql, Integer.class, params.toArray());
// 分页查询
sql.append(" ORDER BY ub.create_time DESC LIMIT ?, ?");
params.add((page - 1) * limit);
params.add(limit);
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql.toString(), params.toArray());
return CommonResult.success(buildPage(list, total, page, limit));
}
@ApiOperation(value = "封禁用户")
@PostMapping("/user/add")
public CommonResult<String> banUser(@RequestBody Map<String, Object> body) {
Integer userId = body.get("userId") != null ? Integer.valueOf(body.get("userId").toString()) : null;
String banType = body.get("banType") != null ? body.get("banType").toString() : "permanent";
String reason = body.get("reason") != null ? body.get("reason").toString() : "";
Integer duration = body.get("duration") != null ? Integer.valueOf(body.get("duration").toString()) : null;
Integer operatorId = body.get("operatorId") != null ? Integer.valueOf(body.get("operatorId").toString()) : 0;
if (userId == null) {
return CommonResult.failed("用户ID不能为空");
}
// 检查用户是否存在
String checkSql = "SELECT uid, nickname FROM eb_user WHERE uid = ?";
List<Map<String, Object>> users = jdbcTemplate.queryForList(checkSql, userId);
if (users.isEmpty()) {
return CommonResult.failed("用户不存在");
}
// 检查是否已被封禁
String existSql = "SELECT id FROM eb_user_ban WHERE user_id = ? AND status = 1";
List<Map<String, Object>> existing = jdbcTemplate.queryForList(existSql, userId);
if (!existing.isEmpty()) {
return CommonResult.failed("该用户已被封禁");
}
// 计算封禁结束时间
String expireTime = null;
if ("temporary".equals(banType) && duration != null && duration > 0) {
expireTime = String.format("DATE_ADD(NOW(), INTERVAL %d DAY)", duration);
}
// 插入封禁记录
String insertSql;
if (expireTime != null) {
insertSql = "INSERT INTO eb_user_ban (user_id, ban_type, reason, duration_days, expire_time, operator_id, status, create_time, update_time) " +
"VALUES (?, ?, ?, ?, " + expireTime + ", ?, 1, NOW(), NOW())";
jdbcTemplate.update(insertSql, userId, banType, reason, duration, operatorId);
} else {
insertSql = "INSERT INTO eb_user_ban (user_id, ban_type, reason, duration_days, operator_id, status, create_time, update_time) " +
"VALUES (?, ?, ?, ?, ?, 1, NOW(), NOW())";
jdbcTemplate.update(insertSql, userId, banType, reason, duration, operatorId);
}
// 更新用户状态
jdbcTemplate.update("UPDATE eb_user SET status = 0 WHERE uid = ?", userId);
log.info("【用户封禁】userId={}, banType={}, reason={}, operatorId={}", userId, banType, reason, operatorId);
return CommonResult.success("封禁成功");
}
@ApiOperation(value = "解除用户封禁")
@PostMapping("/user/unban/{id}")
public CommonResult<String> unbanUser(@PathVariable Long id) {
// 获取封禁记录
String querySql = "SELECT user_id FROM eb_user_ban WHERE id = ?";
List<Map<String, Object>> records = jdbcTemplate.queryForList(querySql, id);
if (records.isEmpty()) {
return CommonResult.failed("封禁记录不存在");
}
Integer userId = ((Number) records.get(0).get("user_id")).intValue();
// 更新封禁状态
String updateSql = "UPDATE eb_user_ban SET status = 0, update_time = NOW() WHERE id = ?";
jdbcTemplate.update(updateSql, id);
// 恢复用户状态
jdbcTemplate.update("UPDATE eb_user SET status = 1 WHERE uid = ?", userId);
log.info("【解除用户封禁】id={}, userId={}", id, userId);
return CommonResult.success("解除封禁成功");
}
@ApiOperation(value = "删除用户封禁记录")
@PostMapping("/user/delete/{id}")
public CommonResult<String> deleteUserBan(@PathVariable Long id) {
String sql = "DELETE FROM eb_user_ban WHERE id = ?";
int rows = jdbcTemplate.update(sql, id);
return rows > 0 ? CommonResult.success("删除成功") : CommonResult.failed("删除失败");
}
@ApiOperation(value = "批量删除用户封禁记录")
@PostMapping("/user/batch-delete")
public CommonResult<String> batchDeleteUserBan(@RequestBody List<Long> ids) {
if (ids == null || ids.isEmpty()) {
return CommonResult.failed("请选择要删除的记录");
}
String placeholders = String.join(",", Collections.nCopies(ids.size(), "?"));
String sql = "DELETE FROM eb_user_ban WHERE id IN (" + placeholders + ")";
int rows = jdbcTemplate.update(sql, ids.toArray());
return rows > 0 ? CommonResult.success("批量删除成功,删除" + rows + "条记录") : CommonResult.failed("删除失败");
}
// ==================== 房间封禁 ====================
@ApiOperation(value = "获取房间封禁列表")
@GetMapping("/room/list")
public CommonResult<CommonPage<Map<String, Object>>> getRoomBanList(
@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "limit", defaultValue = "10") Integer limit,
@RequestParam(value = "roomName", required = false) String roomName,
@RequestParam(value = "streamerName", required = false) String streamerName,
@RequestParam(value = "banType", required = false) String banType,
@RequestParam(value = "status", required = false) Integer status) {
StringBuilder sql = new StringBuilder(
"SELECT rb.*, lr.title as room_name, lr.streamer_name, lr.cover_image " +
"FROM eb_room_ban rb " +
"LEFT JOIN eb_live_room lr ON rb.room_id = lr.id " +
"WHERE 1=1");
List<Object> params = new ArrayList<>();
if (roomName != null && !roomName.trim().isEmpty()) {
sql.append(" AND lr.title LIKE ?");
params.add("%" + roomName.trim() + "%");
}
if (streamerName != null && !streamerName.trim().isEmpty()) {
sql.append(" AND lr.streamer_name LIKE ?");
params.add("%" + streamerName.trim() + "%");
}
if (banType != null && !banType.trim().isEmpty()) {
sql.append(" AND rb.ban_type = ?");
params.add(banType);
}
if (status != null) {
sql.append(" AND rb.status = ?");
params.add(status);
}
// 计算总数
String countSql = sql.toString().replace("SELECT rb.*, lr.title as room_name, lr.streamer_name, lr.cover_image", "SELECT COUNT(*)");
Integer total = jdbcTemplate.queryForObject(countSql, Integer.class, params.toArray());
// 分页查询
sql.append(" ORDER BY rb.create_time DESC LIMIT ?, ?");
params.add((page - 1) * limit);
params.add(limit);
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql.toString(), params.toArray());
return CommonResult.success(buildPage(list, total, page, limit));
}
@ApiOperation(value = "封禁房间")
@PostMapping("/room/add")
public CommonResult<String> banRoom(@RequestBody Map<String, Object> body) {
Integer roomId = body.get("roomId") != null ? Integer.valueOf(body.get("roomId").toString()) : null;
String banType = body.get("banType") != null ? body.get("banType").toString() : "permanent";
String reason = body.get("reason") != null ? body.get("reason").toString() : "";
Integer duration = body.get("duration") != null ? Integer.valueOf(body.get("duration").toString()) : null;
Integer operatorId = body.get("operatorId") != null ? Integer.valueOf(body.get("operatorId").toString()) : 0;
if (roomId == null) {
return CommonResult.failed("房间ID不能为空");
}
// 检查房间是否存在
String checkSql = "SELECT id, title FROM eb_live_room WHERE id = ?";
List<Map<String, Object>> rooms = jdbcTemplate.queryForList(checkSql, roomId);
if (rooms.isEmpty()) {
return CommonResult.failed("房间不存在");
}
// 检查是否已被封禁
String existSql = "SELECT id FROM eb_room_ban WHERE room_id = ? AND status = 1";
List<Map<String, Object>> existing = jdbcTemplate.queryForList(existSql, roomId);
if (!existing.isEmpty()) {
return CommonResult.failed("该房间已被封禁");
}
// 计算封禁结束时间
String expireTime = null;
if ("temporary".equals(banType) && duration != null && duration > 0) {
expireTime = String.format("DATE_ADD(NOW(), INTERVAL %d DAY)", duration);
}
// 插入封禁记录
String insertSql;
if (expireTime != null) {
insertSql = "INSERT INTO eb_room_ban (room_id, ban_type, reason, duration_days, expire_time, operator_id, status, create_time, update_time) " +
"VALUES (?, ?, ?, ?, " + expireTime + ", ?, 1, NOW(), NOW())";
jdbcTemplate.update(insertSql, roomId, banType, reason, duration, operatorId);
} else {
insertSql = "INSERT INTO eb_room_ban (room_id, ban_type, reason, duration_days, operator_id, status, create_time, update_time) " +
"VALUES (?, ?, ?, ?, ?, 1, NOW(), NOW())";
jdbcTemplate.update(insertSql, roomId, banType, reason, duration, operatorId);
}
// 更新房间状态关闭直播
jdbcTemplate.update("UPDATE eb_live_room SET is_live = 0, status = 0 WHERE id = ?", roomId);
log.info("【房间封禁】roomId={}, banType={}, reason={}, operatorId={}", roomId, banType, reason, operatorId);
return CommonResult.success("封禁成功");
}
@ApiOperation(value = "解除房间封禁")
@PostMapping("/room/unban/{id}")
public CommonResult<String> unbanRoom(@PathVariable Long id) {
// 获取封禁记录
String querySql = "SELECT room_id FROM eb_room_ban WHERE id = ?";
List<Map<String, Object>> records = jdbcTemplate.queryForList(querySql, id);
if (records.isEmpty()) {
return CommonResult.failed("封禁记录不存在");
}
Integer roomId = ((Number) records.get(0).get("room_id")).intValue();
// 更新封禁状态
String updateSql = "UPDATE eb_room_ban SET status = 0, update_time = NOW() WHERE id = ?";
jdbcTemplate.update(updateSql, id);
// 恢复房间状态
jdbcTemplate.update("UPDATE eb_live_room SET status = 1 WHERE id = ?", roomId);
log.info("【解除房间封禁】id={}, roomId={}", id, roomId);
return CommonResult.success("解除封禁成功");
}
@ApiOperation(value = "删除房间封禁记录")
@PostMapping("/room/delete/{id}")
public CommonResult<String> deleteRoomBan(@PathVariable Long id) {
String sql = "DELETE FROM eb_room_ban WHERE id = ?";
int rows = jdbcTemplate.update(sql, id);
return rows > 0 ? CommonResult.success("删除成功") : CommonResult.failed("删除失败");
}
@ApiOperation(value = "批量删除房间封禁记录")
@PostMapping("/room/batch-delete")
public CommonResult<String> batchDeleteRoomBan(@RequestBody List<Long> ids) {
if (ids == null || ids.isEmpty()) {
return CommonResult.failed("请选择要删除的记录");
}
String placeholders = String.join(",", Collections.nCopies(ids.size(), "?"));
String sql = "DELETE FROM eb_room_ban WHERE id IN (" + placeholders + ")";
int rows = jdbcTemplate.update(sql, ids.toArray());
return rows > 0 ? CommonResult.success("批量删除成功,删除" + rows + "条记录") : CommonResult.failed("删除失败");
}
// ==================== 检查封禁状态供前端API调用 ====================
@ApiOperation(value = "检查用户封禁状态")
@GetMapping("/user/check/{userId}")
public CommonResult<Map<String, Object>> checkUserBanStatus(@PathVariable Integer userId) {
String sql = "SELECT * FROM eb_user_ban WHERE user_id = ? AND status = 1 " +
"AND (expire_time IS NULL OR expire_time > NOW()) ORDER BY create_time DESC LIMIT 1";
List<Map<String, Object>> records = jdbcTemplate.queryForList(sql, userId);
Map<String, Object> result = new HashMap<>();
if (records.isEmpty()) {
result.put("isBanned", false);
} else {
result.put("isBanned", true);
result.put("banInfo", records.get(0));
}
return CommonResult.success(result);
}
@ApiOperation(value = "检查房间封禁状态")
@GetMapping("/room/check/{roomId}")
public CommonResult<Map<String, Object>> checkRoomBanStatus(@PathVariable Integer roomId) {
String sql = "SELECT * FROM eb_room_ban WHERE room_id = ? AND status = 1 " +
"AND (expire_time IS NULL OR expire_time > NOW()) ORDER BY create_time DESC LIMIT 1";
List<Map<String, Object>> records = jdbcTemplate.queryForList(sql, roomId);
Map<String, Object> result = new HashMap<>();
if (records.isEmpty()) {
result.put("isBanned", false);
} else {
result.put("isBanned", true);
result.put("banInfo", records.get(0));
}
return CommonResult.success(result);
}
// ==================== 工具方法 ====================
private CommonPage<Map<String, Object>> buildPage(List<Map<String, Object>> list, Integer total, Integer page, Integer limit) {
CommonPage<Map<String, Object>> result = new CommonPage<>();
result.setList(list);
result.setTotal(total != null ? total.longValue() : 0L);
result.setPage(page);
result.setLimit(limit);
result.setTotalPage((int) Math.ceil((double) (total != null ? total : 0) / limit));
return result;
}
}

View File

@ -0,0 +1,287 @@
package com.zbkj.front.controller;
import com.zbkj.common.page.CommonPage;
import com.zbkj.common.result.CommonResult;
import com.zbkj.service.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.*;
/**
* 封禁功能控制器移动端API
* 提供用户封禁检查黑名单管理等功能
*/
@Slf4j
@RestController
@RequestMapping("api/front/ban")
@Api(tags = "封禁管理-移动端")
@Validated
public class BanFrontController {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private UserService userService;
// ==================== 封禁状态检查 ====================
/**
* 检查当前用户是否被封禁
*/
@ApiOperation(value = "检查当前用户封禁状态")
@GetMapping("/check/me")
public CommonResult<Map<String, Object>> checkMyBanStatus() {
Integer userId = userService.getUserId();
if (userId == null || userId == 0) {
return CommonResult.failed("请先登录");
}
return checkUserBanStatus(userId);
}
/**
* 检查指定用户是否被封禁
*/
@ApiOperation(value = "检查用户封禁状态")
@GetMapping("/check/user/{userId}")
public CommonResult<Map<String, Object>> checkUserBanStatus(@PathVariable Integer userId) {
String sql = "SELECT id, ban_type, reason, expire_time, create_time " +
"FROM eb_user_ban WHERE user_id = ? AND status = 1 " +
"AND (expire_time IS NULL OR expire_time > NOW()) " +
"ORDER BY create_time DESC LIMIT 1";
List<Map<String, Object>> records = jdbcTemplate.queryForList(sql, userId);
Map<String, Object> result = new HashMap<>();
if (records.isEmpty()) {
result.put("isBanned", false);
result.put("message", "用户状态正常");
} else {
Map<String, Object> banInfo = records.get(0);
result.put("isBanned", true);
result.put("banType", banInfo.get("ban_type"));
result.put("reason", banInfo.get("reason"));
result.put("expireTime", banInfo.get("expire_time"));
result.put("createTime", banInfo.get("create_time"));
String banType = (String) banInfo.get("ban_type");
if ("permanent".equals(banType)) {
result.put("message", "您的账号已被永久封禁");
} else {
result.put("message", "您的账号已被临时封禁,解封时间:" + banInfo.get("expire_time"));
}
}
return CommonResult.success(result);
}
/**
* 检查房间是否被封禁
*/
@ApiOperation(value = "检查房间封禁状态")
@GetMapping("/check/room/{roomId}")
public CommonResult<Map<String, Object>> checkRoomBanStatus(@PathVariable Integer roomId) {
String sql = "SELECT id, ban_type, reason, expire_time, create_time " +
"FROM eb_room_ban WHERE room_id = ? AND status = 1 " +
"AND (expire_time IS NULL OR expire_time > NOW()) " +
"ORDER BY create_time DESC LIMIT 1";
List<Map<String, Object>> records = jdbcTemplate.queryForList(sql, roomId);
Map<String, Object> result = new HashMap<>();
if (records.isEmpty()) {
result.put("isBanned", false);
result.put("message", "房间状态正常");
} else {
Map<String, Object> banInfo = records.get(0);
result.put("isBanned", true);
result.put("banType", banInfo.get("ban_type"));
result.put("reason", banInfo.get("reason"));
result.put("expireTime", banInfo.get("expire_time"));
result.put("createTime", banInfo.get("create_time"));
String banType = (String) banInfo.get("ban_type");
if ("permanent".equals(banType)) {
result.put("message", "该直播间已被永久封禁");
} else {
result.put("message", "该直播间已被临时封禁,解封时间:" + banInfo.get("expire_time"));
}
}
return CommonResult.success(result);
}
// ==================== 用户黑名单管理用户间拉黑 ====================
/**
* 获取我的黑名单列表
*/
@ApiOperation(value = "获取我的黑名单列表")
@GetMapping("/blacklist/list")
public CommonResult<CommonPage<Map<String, Object>>> getMyBlacklist(
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "20") Integer pageSize) {
Integer userId = userService.getUserId();
if (userId == null || userId == 0) {
return CommonResult.failed("请先登录");
}
int offset = (page - 1) * pageSize;
// 查询总数
String countSql = "SELECT COUNT(*) FROM eb_user_blacklist WHERE user_id = ?";
Integer total = jdbcTemplate.queryForObject(countSql, Integer.class, userId);
// 查询列表
String sql = "SELECT ub.id, ub.blocked_user_id as blockedUserId, " +
"u.nickname as blockedNickname, u.avatar as blockedAvatar, " +
"ub.create_time as createTime " +
"FROM eb_user_blacklist ub " +
"LEFT JOIN eb_user u ON ub.blocked_user_id = u.uid " +
"WHERE ub.user_id = ? " +
"ORDER BY ub.create_time DESC LIMIT ? OFFSET ?";
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, userId, pageSize, offset);
return CommonResult.success(buildPage(list, total, page, pageSize));
}
/**
* 添加用户到黑名单
*/
@ApiOperation(value = "添加用户到黑名单")
@PostMapping("/blacklist/add")
public CommonResult<Map<String, Object>> addToBlacklist(@RequestBody Map<String, Object> body) {
Integer userId = userService.getUserId();
if (userId == null || userId == 0) {
return CommonResult.failed("请先登录");
}
Integer targetUserId = body.get("targetUserId") != null ?
Integer.valueOf(body.get("targetUserId").toString()) : null;
if (targetUserId == null) {
return CommonResult.failed("目标用户ID不能为空");
}
if (userId.equals(targetUserId)) {
return CommonResult.failed("不能拉黑自己");
}
// 检查目标用户是否存在
String checkUserSql = "SELECT uid, nickname FROM eb_user WHERE uid = ?";
List<Map<String, Object>> users = jdbcTemplate.queryForList(checkUserSql, targetUserId);
if (users.isEmpty()) {
return CommonResult.failed("目标用户不存在");
}
// 检查是否已在黑名单
String checkSql = "SELECT id FROM eb_user_blacklist WHERE user_id = ? AND blocked_user_id = ?";
List<Map<String, Object>> existing = jdbcTemplate.queryForList(checkSql, userId, targetUserId);
if (!existing.isEmpty()) {
return CommonResult.failed("该用户已在黑名单中");
}
// 获取用户昵称
String blockerNickname = "";
String blockedNickname = users.get(0).get("nickname") != null ?
users.get(0).get("nickname").toString() : "";
String getUserSql = "SELECT nickname FROM eb_user WHERE uid = ?";
List<Map<String, Object>> blockerUsers = jdbcTemplate.queryForList(getUserSql, userId);
if (!blockerUsers.isEmpty() && blockerUsers.get(0).get("nickname") != null) {
blockerNickname = blockerUsers.get(0).get("nickname").toString();
}
// 添加到黑名单
String insertSql = "INSERT INTO eb_user_blacklist (user_id, blocked_user_id, blocker_nickname, blocked_nickname, create_time) " +
"VALUES (?, ?, ?, ?, NOW())";
jdbcTemplate.update(insertSql, userId, targetUserId, blockerNickname, blockedNickname);
// 删除好友关系如果存在
String deleteFriendSql = "DELETE FROM eb_friend WHERE (user_id = ? AND friend_id = ?) OR (user_id = ? AND friend_id = ?)";
jdbcTemplate.update(deleteFriendSql, userId, targetUserId, targetUserId, userId);
log.info("【添加黑名单】userId={}, targetUserId={}", userId, targetUserId);
Map<String, Object> result = new HashMap<>();
result.put("success", true);
result.put("message", "已将该用户加入黑名单");
return CommonResult.success(result);
}
/**
* 从黑名单移除用户
*/
@ApiOperation(value = "从黑名单移除用户")
@PostMapping("/blacklist/remove")
public CommonResult<Map<String, Object>> removeFromBlacklist(@RequestBody Map<String, Object> body) {
Integer userId = userService.getUserId();
if (userId == null || userId == 0) {
return CommonResult.failed("请先登录");
}
Integer targetUserId = body.get("targetUserId") != null ?
Integer.valueOf(body.get("targetUserId").toString()) : null;
if (targetUserId == null) {
return CommonResult.failed("目标用户ID不能为空");
}
String deleteSql = "DELETE FROM eb_user_blacklist WHERE user_id = ? AND blocked_user_id = ?";
int rows = jdbcTemplate.update(deleteSql, userId, targetUserId);
Map<String, Object> result = new HashMap<>();
if (rows > 0) {
result.put("success", true);
result.put("message", "已将该用户从黑名单移除");
log.info("【移除黑名单】userId={}, targetUserId={}", userId, targetUserId);
} else {
result.put("success", false);
result.put("message", "该用户不在黑名单中");
}
return CommonResult.success(result);
}
/**
* 检查用户是否在我的黑名单中
*/
@ApiOperation(value = "检查用户是否在黑名单中")
@GetMapping("/blacklist/check/{targetUserId}")
public CommonResult<Map<String, Object>> checkBlacklist(@PathVariable Integer targetUserId) {
Integer userId = userService.getUserId();
if (userId == null || userId == 0) {
return CommonResult.failed("请先登录");
}
// 检查我是否拉黑了对方
String sql1 = "SELECT id FROM eb_user_blacklist WHERE user_id = ? AND blocked_user_id = ?";
List<Map<String, Object>> myBlacklist = jdbcTemplate.queryForList(sql1, userId, targetUserId);
// 检查对方是否拉黑了我
String sql2 = "SELECT id FROM eb_user_blacklist WHERE user_id = ? AND blocked_user_id = ?";
List<Map<String, Object>> theirBlacklist = jdbcTemplate.queryForList(sql2, targetUserId, userId);
Map<String, Object> result = new HashMap<>();
result.put("isBlocked", !myBlacklist.isEmpty()); // 我是否拉黑了对方
result.put("isBlockedByTarget", !theirBlacklist.isEmpty()); // 对方是否拉黑了我
result.put("canInteract", myBlacklist.isEmpty() && theirBlacklist.isEmpty()); // 是否可以互动
return CommonResult.success(result);
}
// ==================== 工具方法 ====================
private CommonPage<Map<String, Object>> buildPage(List<Map<String, Object>> list, Integer total, Integer page, Integer pageSize) {
CommonPage<Map<String, Object>> result = new CommonPage<>();
result.setList(list);
result.setTotal(total != null ? total.longValue() : 0L);
result.setPage(page);
result.setLimit(pageSize);
result.setTotalPage((int) Math.ceil((double) (total != null ? total : 0) / pageSize));
return result;
}
}

43
add_ban_menus.sql Normal file
View File

@ -0,0 +1,43 @@
-- =====================================================
-- 添加封禁管理菜单(替换原有的拉黑菜单)
-- eb_system_menu 表结构id, pid, name, icon, perms, component, menu_type, sort, is_show, is_delte
-- =====================================================
-- 1. 查找用户管理的父菜单ID
SET @userManagePid = (SELECT id FROM eb_system_menu WHERE `name` = '用户管理' AND pid = 0 LIMIT 1);
-- 如果没有找到用户管理,尝试查找"用户"菜单
SET @userManagePid = IFNULL(@userManagePid, (SELECT id FROM eb_system_menu WHERE `name` = '用户' AND pid = 0 LIMIT 1));
-- 如果还是没有找到使用默认值4通常是用户菜单的ID
SET @userManagePid = IFNULL(@userManagePid, 4);
SELECT CONCAT('用户管理菜单ID: ', @userManagePid) as info;
-- 2. 删除旧的拉黑菜单(如果存在)
DELETE FROM eb_system_menu WHERE `name` IN ('房间拉黑', '用户拉黑') AND pid = @userManagePid;
-- 3. 添加新的封禁菜单
-- 用户封禁
INSERT INTO eb_system_menu (`pid`, `name`, `icon`, `perms`, `component`, `menu_type`, `sort`, `is_show`, `is_delte`, `create_time`, `update_time`)
SELECT @userManagePid, '用户封禁', '', 'admin:ban:user', '/user/ban/userBan', 'C', 92, 1, 0, NOW(), NOW()
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM eb_system_menu WHERE `name` = '用户封禁' AND pid = @userManagePid);
-- 房间封禁
INSERT INTO eb_system_menu (`pid`, `name`, `icon`, `perms`, `component`, `menu_type`, `sort`, `is_show`, `is_delte`, `create_time`, `update_time`)
SELECT @userManagePid, '房间封禁', '', 'admin:ban:room', '/user/ban/roomBan', 'C', 91, 1, 0, NOW(), NOW()
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM eb_system_menu WHERE `name` = '房间封禁' AND pid = @userManagePid);
-- 4. 验证菜单
SELECT '=== 封禁管理菜单 ===' as info;
SELECT id, pid, `name`, perms, component, sort, is_show
FROM eb_system_menu
WHERE pid = @userManagePid AND (`name` LIKE '%封禁%' OR `name` LIKE '%黑名单%' OR `name` LIKE '%拉黑%')
ORDER BY sort DESC;
-- 5. 显示所有用户管理子菜单
SELECT '=== 用户管理所有子菜单 ===' as info;
SELECT id, pid, `name`, perms, component, sort, is_show
FROM eb_system_menu
WHERE pid = @userManagePid
ORDER BY sort DESC;

70
add_ban_test_data.sql Normal file
View File

@ -0,0 +1,70 @@
-- =====================================================
-- 添加封禁系统测试数据
-- =====================================================
-- 1. 确保封禁表存在
CREATE TABLE IF NOT EXISTS eb_user_ban (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL COMMENT '被封禁用户ID',
ban_type VARCHAR(20) DEFAULT 'permanent' COMMENT '封禁类型: permanent-永久, temporary-临时',
reason VARCHAR(500) COMMENT '封禁原因',
duration_days INT DEFAULT NULL COMMENT '封禁天数(临时封禁)',
expire_time DATETIME DEFAULT NULL COMMENT '封禁到期时间',
operator_id INT DEFAULT 0 COMMENT '操作人ID',
status TINYINT DEFAULT 1 COMMENT '状态: 1-生效中, 0-已解除',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_user_id (user_id),
INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户封禁表';
CREATE TABLE IF NOT EXISTS eb_room_ban (
id INT AUTO_INCREMENT PRIMARY KEY,
room_id INT NOT NULL COMMENT '被封禁房间ID',
ban_type VARCHAR(20) DEFAULT 'permanent' COMMENT '封禁类型: permanent-永久, temporary-临时',
reason VARCHAR(500) COMMENT '封禁原因',
duration_days INT DEFAULT NULL COMMENT '封禁天数(临时封禁)',
expire_time DATETIME DEFAULT NULL COMMENT '封禁到期时间',
operator_id INT DEFAULT 0 COMMENT '操作人ID',
status TINYINT DEFAULT 1 COMMENT '状态: 1-生效中, 0-已解除',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_room_id (room_id),
INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='房间封禁表';
-- 2. 添加用户封禁测试数据
INSERT INTO eb_user_ban (user_id, ban_type, reason, duration_days, expire_time, operator_id, status, create_time) VALUES
(43, 'permanent', '发布违规内容', NULL, NULL, 1, 1, NOW() - INTERVAL 2 DAY),
(100, 'temporary', '恶意刷屏', 7, NOW() + INTERVAL 5 DAY, 1, 1, NOW() - INTERVAL 2 DAY),
(101, 'temporary', '骚扰其他用户', 30, NOW() + INTERVAL 28 DAY, 1, 1, NOW() - INTERVAL 2 DAY),
(102, 'permanent', '传播不良信息', NULL, NULL, 1, 0, NOW() - INTERVAL 10 DAY)
ON DUPLICATE KEY UPDATE reason = VALUES(reason);
-- 3. 添加房间封禁测试数据
INSERT INTO eb_room_ban (room_id, ban_type, reason, duration_days, expire_time, operator_id, status, create_time) VALUES
(1, 'temporary', '直播内容违规', 3, NOW() + INTERVAL 1 DAY, 1, 1, NOW() - INTERVAL 2 DAY),
(2, 'permanent', '严重违规行为', NULL, NULL, 1, 1, NOW() - INTERVAL 5 DAY),
(3, 'temporary', '未经授权的商业推广', 7, NOW() + INTERVAL 5 DAY, 1, 1, NOW() - INTERVAL 2 DAY),
(4, 'permanent', '涉及敏感内容', NULL, NULL, 1, 0, NOW() - INTERVAL 15 DAY)
ON DUPLICATE KEY UPDATE reason = VALUES(reason);
-- 4. 验证数据
SELECT '=== 用户封禁数据 ===' as info;
SELECT ub.*, u.nickname, u.phone
FROM eb_user_ban ub
LEFT JOIN eb_user u ON ub.user_id = u.uid
ORDER BY ub.create_time DESC;
SELECT '=== 房间封禁数据 ===' as info;
SELECT rb.*, lr.title as room_name, lr.streamer_name
FROM eb_room_ban rb
LEFT JOIN eb_live_room lr ON rb.room_id = lr.id
ORDER BY rb.create_time DESC;
SELECT '=== 统计 ===' as info;
SELECT
(SELECT COUNT(*) FROM eb_user_ban) as ,
(SELECT COUNT(*) FROM eb_user_ban WHERE status = 1) as ,
(SELECT COUNT(*) FROM eb_room_ban) as ,
(SELECT COUNT(*) FROM eb_room_ban WHERE status = 1) as ;

View File

@ -0,0 +1,50 @@
-- =====================================================
-- 立即添加拉黑管理菜单到社交互动模块
-- =====================================================
-- 1. 先查看社交互动菜单的ID
SELECT id, pid, `name`, component, menu_type FROM eb_system_menu WHERE `name` = '社交互动';
-- 2. 查看现有的社交互动子菜单
SELECT id, pid, `name`, component, sort FROM eb_system_menu WHERE pid = (SELECT id FROM eb_system_menu WHERE `name` = '社交互动' LIMIT 1) ORDER BY sort;
-- 3. 检查是否已存在拉黑菜单
SELECT id, `name`, component FROM eb_system_menu WHERE `name` IN ('用户拉黑', '房间拉黑');
-- 4. 删除可能存在的旧记录(避免重复)
DELETE FROM eb_system_menu WHERE `name` = '用户拉黑' AND component LIKE '%blacklist%';
DELETE FROM eb_system_menu WHERE `name` = '房间拉黑' AND component LIKE '%blacklist%';
-- 5. 插入用户拉黑菜单假设社交互动ID为某个值需要先查询
-- 先获取社交互动的ID
SET @social_id = (SELECT id FROM eb_system_menu WHERE `name` = '社交互动' AND menu_type = 'M' LIMIT 1);
SELECT @social_id as '社交互动菜单ID';
-- 6. 插入用户拉黑
INSERT INTO eb_system_menu (`pid`, `name`, `icon`, `perms`, `component`, `menu_type`, `sort`, `is_show`)
VALUES (@social_id, '用户拉黑', 'el-icon-user', 'admin:blacklist:user', '/socialManage/blacklist/user', 'C', 100, 1);
-- 7. 插入房间拉黑
INSERT INTO eb_system_menu (`pid`, `name`, `icon`, `perms`, `component`, `menu_type`, `sort`, `is_show`)
VALUES (@social_id, '房间拉黑', 'el-icon-house', 'admin:blacklist:room', '/socialManage/blacklist/room', 'C', 101, 1);
-- 8. 为管理员角色(rid=1)添加权限
INSERT IGNORE INTO eb_system_role_menu (rid, menu_id)
SELECT 1, id FROM eb_system_menu WHERE `name` = '用户拉黑' AND component = '/socialManage/blacklist/user';
INSERT IGNORE INTO eb_system_role_menu (rid, menu_id)
SELECT 1, id FROM eb_system_menu WHERE `name` = '房间拉黑' AND component = '/socialManage/blacklist/room';
-- 9. 验证插入结果
SELECT '=== 新增的拉黑菜单 ===' as info;
SELECT id, pid, `name`, component, sort, is_show
FROM eb_system_menu
WHERE `name` IN ('用户拉黑', '房间拉黑')
ORDER BY id DESC;
-- 10. 验证社交互动下所有子菜单
SELECT '=== 社交互动下所有子菜单 ===' as info;
SELECT id, `name`, component, sort, is_show
FROM eb_system_menu
WHERE pid = @social_id
ORDER BY sort;

View File

@ -0,0 +1,35 @@
-- =====================================================
-- 在"社交互动"模块下添加"拉黑管理"菜单
-- =====================================================
-- 1. 查找"社交互动"菜单的ID
SELECT id, pid, `name`, component FROM eb_system_menu WHERE `name` = '社交互动';
-- 2. 查找现有的拉黑管理菜单(如果有的话)
SELECT id, pid, `name`, component FROM eb_system_menu WHERE `name` LIKE '%拉黑%';
-- 3. 添加"用户拉黑"菜单到社交互动下
-- 假设社交互动的ID需要查询这里用变量方式
SET @social_pid = (SELECT id FROM eb_system_menu WHERE `name` = '社交互动' AND menu_type = 'M' LIMIT 1);
-- 如果社交互动菜单存在,添加拉黑子菜单
INSERT INTO eb_system_menu (`pid`, `name`, `icon`, `perms`, `component`, `menu_type`, `sort`, `is_show`, `is_delete`)
SELECT @social_pid, '用户拉黑', '', 'admin:blacklist:user', '/socialManage/blacklist/user', 'C', 100, 1, 0
FROM DUAL WHERE @social_pid IS NOT NULL
ON DUPLICATE KEY UPDATE `name` = '用户拉黑';
INSERT INTO eb_system_menu (`pid`, `name`, `icon`, `perms`, `component`, `menu_type`, `sort`, `is_show`, `is_delete`)
SELECT @social_pid, '房间拉黑', '', 'admin:blacklist:room', '/socialManage/blacklist/room', 'C', 101, 1, 0
FROM DUAL WHERE @social_pid IS NOT NULL
ON DUPLICATE KEY UPDATE `name` = '房间拉黑';
-- 4. 为管理员角色添加权限
INSERT IGNORE INTO eb_system_role_menu (rid, menu_id)
SELECT 1, id FROM eb_system_menu WHERE `name` IN ('用户拉黑', '房间拉黑') AND pid = @social_pid;
-- 5. 验证结果
SELECT '=== 社交互动下的菜单 ===' as info;
SELECT id, pid, `name`, component, sort
FROM eb_system_menu
WHERE pid = @social_pid
ORDER BY sort;

View File

@ -0,0 +1,34 @@
-- =====================================================
-- 在"社交互动"模块下添加"拉黑管理"菜单 (修正版)
-- =====================================================
-- 1. 查看菜单表结构
DESC eb_system_menu;
-- 2. 查找"社交互动"菜单的ID
SELECT id, pid, `name`, component FROM eb_system_menu WHERE `name` = '社交互动';
-- 3. 获取社交互动的ID
SET @social_pid = (SELECT id FROM eb_system_menu WHERE `name` = '社交互动' AND menu_type = 'M' LIMIT 1);
SELECT @social_pid as social_menu_id;
-- 4. 添加"用户拉黑"菜单
INSERT INTO eb_system_menu (`pid`, `name`, `icon`, `perms`, `component`, `menu_type`, `sort`, `is_show`)
VALUES (@social_pid, '用户拉黑', '', 'admin:blacklist:user', '/socialManage/blacklist/user', 'C', 100, 1)
ON DUPLICATE KEY UPDATE `component` = '/socialManage/blacklist/user';
-- 5. 添加"房间拉黑"菜单
INSERT INTO eb_system_menu (`pid`, `name`, `icon`, `perms`, `component`, `menu_type`, `sort`, `is_show`)
VALUES (@social_pid, '房间拉黑', '', 'admin:blacklist:room', '/socialManage/blacklist/room', 'C', 101, 1)
ON DUPLICATE KEY UPDATE `component` = '/socialManage/blacklist/room';
-- 6. 为管理员角色添加权限
INSERT IGNORE INTO eb_system_role_menu (rid, menu_id)
SELECT 1, id FROM eb_system_menu WHERE `name` IN ('用户拉黑', '房间拉黑') AND pid = @social_pid;
-- 7. 验证结果
SELECT '=== 社交互动下的菜单 ===' as info;
SELECT id, pid, `name`, component, sort, is_show
FROM eb_system_menu
WHERE pid = @social_pid
ORDER BY sort;

125
add_test_data_for_user.sql Normal file
View File

@ -0,0 +1,125 @@
-- 为指定用户添加测试数据
-- 使用方法: 将 @USER_ID 替换为实际的用户ID
-- 设置用户ID变量 (修改这里的值为移动端登录的用户ID)
SET @USER_ID = 121;
-- 查看当前用户信息
SELECT uid, nickname, phone FROM eb_user WHERE uid = @USER_ID;
-- ========================================
-- 0. 检查必要的表是否存在
-- ========================================
-- 如果 eb_view_history 表不存在,先创建它
CREATE TABLE IF NOT EXISTS eb_view_history (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL COMMENT '用户ID',
target_type VARCHAR(20) NOT NULL COMMENT '目标类型: room-直播间, work-作品, profile-用户主页',
target_id VARCHAR(50) NOT NULL COMMENT '目标ID',
target_title VARCHAR(200) DEFAULT NULL COMMENT '目标标题',
view_duration INT DEFAULT 0 COMMENT '观看时长(秒)',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
INDEX idx_user_id (user_id),
INDEX idx_target (target_type, target_id),
UNIQUE KEY uk_user_target (user_id, target_type, target_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户观看历史';
-- ========================================
-- 1. 添加观看历史
-- ========================================
-- 先检查是否有直播间数据
SELECT COUNT(*) as room_count FROM eb_live_room;
-- 添加直播间观看历史
INSERT IGNORE INTO eb_view_history (user_id, target_type, target_id, target_title, view_duration, create_time, update_time)
SELECT @USER_ID, 'room', id, title, FLOOR(RAND() * 3600), NOW() - INTERVAL FLOOR(RAND() * 7) DAY, NOW()
FROM eb_live_room LIMIT 5;
-- 检查是否有作品数据
SELECT COUNT(*) as works_count FROM eb_works;
-- 添加作品观看历史
INSERT IGNORE INTO eb_view_history (user_id, target_type, target_id, target_title, view_duration, create_time, update_time)
SELECT @USER_ID, 'work', id, title, FLOOR(RAND() * 600), NOW() - INTERVAL FLOOR(RAND() * 7) DAY, NOW()
FROM eb_works LIMIT 5;
-- ========================================
-- 2. 添加直播间点赞
-- ========================================
-- 检查 eb_live_room_like 表是否存在
CREATE TABLE IF NOT EXISTS eb_live_room_like (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL COMMENT '用户ID',
room_id INT NOT NULL COMMENT '直播间ID',
like_count INT DEFAULT 1 COMMENT '点赞次数',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
INDEX idx_user_id (user_id),
INDEX idx_room_id (room_id),
UNIQUE KEY uk_user_room (user_id, room_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='直播间点赞记录';
INSERT IGNORE INTO eb_live_room_like (user_id, room_id, like_count, create_time)
SELECT @USER_ID, id, FLOOR(RAND() * 10) + 1, NOW() - INTERVAL FLOOR(RAND() * 7) DAY
FROM eb_live_room LIMIT 5;
-- ========================================
-- 3. 添加作品点赞 (type=1)
-- ========================================
INSERT IGNORE INTO eb_works_relation (uid, works_id, type, create_time)
SELECT @USER_ID, id, 1, NOW() - INTERVAL FLOOR(RAND() * 7) DAY
FROM eb_works LIMIT 5;
-- ========================================
-- 4. 添加作品收藏 (type=2)
-- ========================================
INSERT IGNORE INTO eb_works_relation (uid, works_id, type, create_time)
SELECT @USER_ID, id, 2, NOW() - INTERVAL FLOOR(RAND() * 7) DAY
FROM eb_works WHERE id NOT IN (SELECT works_id FROM eb_works_relation WHERE uid = @USER_ID AND type = 2) LIMIT 4;
-- ========================================
-- 5. 添加关注记录
-- ========================================
-- 检查 eb_follow_record 表是否存在
CREATE TABLE IF NOT EXISTS eb_follow_record (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
follower_id INT NOT NULL COMMENT '关注者ID',
follower_nickname VARCHAR(100) DEFAULT NULL COMMENT '关注者昵称',
followed_id INT NOT NULL COMMENT '被关注者ID',
followed_nickname VARCHAR(100) DEFAULT NULL COMMENT '被关注者昵称',
follow_status VARCHAR(20) DEFAULT '1' COMMENT '关注状态: 1或关注=已关注',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
INDEX idx_follower (follower_id),
INDEX idx_followed (followed_id),
UNIQUE KEY uk_follow (follower_id, followed_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='关注记录';
INSERT IGNORE INTO eb_follow_record (follower_id, follower_nickname, followed_id, followed_nickname, follow_status, create_time)
SELECT @USER_ID,
(SELECT nickname FROM eb_user WHERE uid = @USER_ID),
uid,
nickname,
1,
NOW() - INTERVAL FLOOR(RAND() * 30) DAY
FROM eb_user
WHERE uid != @USER_ID
AND uid NOT IN (SELECT followed_id FROM eb_follow_record WHERE follower_id = @USER_ID)
LIMIT 6;
-- ========================================
-- 验证数据
-- ========================================
SELECT '观看历史' as type, COUNT(*) as count FROM eb_view_history WHERE user_id = @USER_ID
UNION ALL
SELECT '直播间点赞', COUNT(*) FROM eb_live_room_like WHERE user_id = @USER_ID
UNION ALL
SELECT '作品点赞', COUNT(*) FROM eb_works_relation WHERE uid = @USER_ID AND type = 1
UNION ALL
SELECT '作品收藏', COUNT(*) FROM eb_works_relation WHERE uid = @USER_ID AND type = 2
UNION ALL
SELECT '关注记录', COUNT(*) FROM eb_follow_record WHERE follower_id = @USER_ID AND (follow_status = 1 OR follow_status = '关注');
-- ========================================
-- 查看所有用户列表用于确定移动端登录的用户ID
-- ========================================
SELECT uid, nickname, phone, create_time FROM eb_user ORDER BY uid DESC LIMIT 20;

18
add_view_history_121.sql Normal file
View File

@ -0,0 +1,18 @@
-- =====================================================
-- 为用户"道玄"ID: 121添加观看历史到 eb_view_history 表
-- =====================================================
-- 插入观看历史数据
INSERT INTO eb_view_history (user_id, target_type, target_id, target_title, view_duration, create_time, update_time) VALUES
(121, 'room', '1', '欢乐游戏直播', 1800, '2026-01-03 20:30:00', '2026-01-03 20:30:00'),
(121, 'room', '2', '音乐分享会', 2400, '2026-01-03 21:00:00', '2026-01-03 21:00:00'),
(121, 'room', '3', '户外探险直播', 900, '2026-01-02 19:00:00', '2026-01-02 19:00:00'),
(121, 'room', '4', '美食制作教程', 3600, '2026-01-02 12:00:00', '2026-01-02 12:00:00'),
(121, 'room', '5', '编程技术分享', 5400, '2026-01-01 14:00:00', '2026-01-01 14:00:00'),
(121, 'work', '1', '搞笑短视频合集', 180, '2026-01-03 22:00:00', '2026-01-03 22:00:00'),
(121, 'work', '2', '旅行Vlog-云南之旅', 600, '2026-01-03 18:00:00', '2026-01-03 18:00:00'),
(121, 'work', '3', '美妆教程分享', 420, '2026-01-02 16:00:00', '2026-01-02 16:00:00');
-- 验证数据
SELECT COUNT(*) as FROM eb_view_history WHERE user_id = 121;
SELECT * FROM eb_view_history WHERE user_id = 121 ORDER BY update_time DESC;

View File

@ -0,0 +1,27 @@
-- =====================================================
-- 为用户"道玄"uid=121添加观看历史数据
-- =====================================================
-- 1. 确保表存在
CREATE TABLE IF NOT EXISTS `eb_view_history` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`target_type` varchar(20) NOT NULL,
`target_id` varchar(50) NOT NULL,
`target_title` varchar(255) DEFAULT NULL,
`view_duration` int(11) DEFAULT 0,
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 2. 插入观看历史数据
INSERT INTO eb_view_history (user_id, target_type, target_id, target_title, view_duration, create_time, update_time)
VALUES
(121, 'room', '1', '欢乐游戏直播', 1800, NOW() - INTERVAL 1 HOUR, NOW() - INTERVAL 1 HOUR),
(121, 'room', '2', '音乐分享会', 2400, NOW() - INTERVAL 2 HOUR, NOW() - INTERVAL 2 HOUR),
(121, 'room', '8', '火影忍者', 3600, NOW() - INTERVAL 30 MINUTE, NOW() - INTERVAL 30 MINUTE);
-- 3. 验证结果
SELECT * FROM eb_view_history WHERE user_id = 121 ORDER BY update_time DESC;

View File

@ -0,0 +1,73 @@
package com.example.livestreaming;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import java.util.Map;
/**
* 余额记录列表适配器
*/
public class BalanceRecordAdapter extends RecyclerView.Adapter<BalanceRecordAdapter.ViewHolder> {
private List<Map<String, Object>> records;
public BalanceRecordAdapter(List<Map<String, Object>> records) {
this.records = records;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_balance_record, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Map<String, Object> record = records.get(position);
// 标题
String title = (String) record.get("title");
if (title == null) title = (String) record.get("remark");
if (title == null) title = "交易记录";
holder.tvTitle.setText(title);
// 金额
Object amountObj = record.get("amount");
if (amountObj == null) amountObj = record.get("price");
double amount = amountObj != null ? ((Number) amountObj).doubleValue() : 0;
String amountStr = amount >= 0 ? String.format("+%.2f", amount) : String.format("%.2f", amount);
holder.tvAmount.setText(amountStr);
holder.tvAmount.setTextColor(amount >= 0 ? 0xFF4CAF50 : 0xFFE53935);
// 时间
String time = (String) record.get("createTime");
if (time == null) time = (String) record.get("create_time");
if (time == null) time = "";
holder.tvTime.setText(time);
}
@Override
public int getItemCount() {
return records != null ? records.size() : 0;
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView tvTitle;
TextView tvAmount;
TextView tvTime;
ViewHolder(View itemView) {
super(itemView);
tvTitle = itemView.findViewById(R.id.tv_title);
tvAmount = itemView.findViewById(R.id.tv_amount);
tvTime = itemView.findViewById(R.id.tv_time);
}
}
}

View File

@ -0,0 +1,117 @@
package com.example.livestreaming;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.example.livestreaming.net.ApiClient;
import com.example.livestreaming.net.ApiResponse;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 余额记录Fragment充值记录/消费记录
*/
public class BalanceRecordFragment extends Fragment {
private static final String ARG_TYPE = "type";
private String recordType; // "recharge" or "consume"
private SwipeRefreshLayout swipeRefresh;
private RecyclerView recyclerView;
private TextView tvEmpty;
private BalanceRecordAdapter adapter;
private List<Map<String, Object>> recordList = new ArrayList<>();
public static BalanceRecordFragment newInstance(String type) {
BalanceRecordFragment fragment = new BalanceRecordFragment();
Bundle args = new Bundle();
args.putString(ARG_TYPE, type);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
recordType = getArguments().getString(ARG_TYPE, "recharge");
}
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_balance_record, container, false);
initViews(view);
loadRecords();
return view;
}
private void initViews(View view) {
swipeRefresh = view.findViewById(R.id.swipe_refresh);
recyclerView = view.findViewById(R.id.recycler_view);
tvEmpty = view.findViewById(R.id.tv_empty);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
adapter = new BalanceRecordAdapter(recordList);
recyclerView.setAdapter(adapter);
swipeRefresh.setOnRefreshListener(this::loadRecords);
}
private void loadRecords() {
swipeRefresh.setRefreshing(true);
Call<ApiResponse<List<Map<String, Object>>>> call;
if ("recharge".equals(recordType)) {
call = ApiClient.getService(requireContext()).getRechargeRecords(1, 20);
} else {
call = ApiClient.getService(requireContext()).getConsumeRecords(1, 20);
}
call.enqueue(new Callback<ApiResponse<List<Map<String, Object>>>>() {
@Override
public void onResponse(Call<ApiResponse<List<Map<String, Object>>>> call, Response<ApiResponse<List<Map<String, Object>>>> response) {
swipeRefresh.setRefreshing(false);
if (response.isSuccessful() && response.body() != null && response.body().isOk()) {
List<Map<String, Object>> data = response.body().getData();
recordList.clear();
if (data != null) {
recordList.addAll(data);
}
adapter.notifyDataSetChanged();
updateEmptyView();
}
}
@Override
public void onFailure(Call<ApiResponse<List<Map<String, Object>>>> call, Throwable t) {
swipeRefresh.setRefreshing(false);
updateEmptyView();
}
});
}
private void updateEmptyView() {
if (recordList.isEmpty()) {
tvEmpty.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
tvEmpty.setText("recharge".equals(recordType) ? "暂无充值记录" : "暂无消费记录");
} else {
tvEmpty.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
}
}
}

View File

@ -0,0 +1,305 @@
package com.example.livestreaming;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.CircleCrop;
import com.example.livestreaming.net.ApiClient;
import com.example.livestreaming.net.ApiResponse;
import com.example.livestreaming.net.PageResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
/**
* 黑名单管理页面
*/
public class BlacklistActivity extends AppCompatActivity {
private static final String TAG = "BlacklistActivity";
private ImageView backButton;
private SwipeRefreshLayout swipeRefreshLayout;
private RecyclerView recyclerView;
private View loadingView;
private View emptyView;
private BlacklistAdapter adapter;
private final List<Map<String, Object>> blacklist = new ArrayList<>();
private int currentPage = 1;
private boolean isLoading = false;
private boolean hasMore = true;
public static void start(Context context) {
Intent intent = new Intent(context, BlacklistActivity.class);
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_blacklist);
initViews();
setupRecyclerView();
setupSwipeRefresh();
loadBlacklist();
}
private void initViews() {
backButton = findViewById(R.id.backButton);
swipeRefreshLayout = findViewById(R.id.swipeRefreshLayout);
recyclerView = findViewById(R.id.recyclerView);
loadingView = findViewById(R.id.loadingView);
emptyView = findViewById(R.id.emptyView);
backButton.setOnClickListener(v -> finish());
}
private void setupRecyclerView() {
adapter = new BlacklistAdapter(this::handleRemoveFromBlacklist);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
if (dy > 0 && !isLoading && hasMore) {
LinearLayoutManager lm = (LinearLayoutManager) recyclerView.getLayoutManager();
if (lm != null) {
int visible = lm.getChildCount();
int total = lm.getItemCount();
int first = lm.findFirstVisibleItemPosition();
if ((visible + first) >= total - 2) {
loadMore();
}
}
}
}
});
}
private void setupSwipeRefresh() {
swipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary);
swipeRefreshLayout.setOnRefreshListener(() -> {
currentPage = 1;
hasMore = true;
loadBlacklist();
});
}
private void loadBlacklist() {
if (isLoading) return;
isLoading = true;
if (currentPage == 1) {
loadingView.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
emptyView.setVisibility(View.GONE);
}
ApiClient.getService(this).getMyBlacklist(currentPage, 20)
.enqueue(new Callback<ApiResponse<PageResponse<Map<String, Object>>>>() {
@Override
public void onResponse(Call<ApiResponse<PageResponse<Map<String, Object>>>> call,
Response<ApiResponse<PageResponse<Map<String, Object>>>> response) {
isLoading = false;
loadingView.setVisibility(View.GONE);
swipeRefreshLayout.setRefreshing(false);
if (response.isSuccessful() && response.body() != null && response.body().isOk()) {
PageResponse<Map<String, Object>> data = response.body().getData();
if (data != null && data.getList() != null) {
if (currentPage == 1) {
blacklist.clear();
}
blacklist.addAll(data.getList());
adapter.setData(new ArrayList<>(blacklist));
hasMore = data.getList().size() >= 20;
}
} else {
String msg = response.body() != null ? response.body().getMessage() : "获取黑名单失败";
Toast.makeText(BlacklistActivity.this, msg, Toast.LENGTH_SHORT).show();
}
updateEmptyState();
}
@Override
public void onFailure(Call<ApiResponse<PageResponse<Map<String, Object>>>> call, Throwable t) {
isLoading = false;
loadingView.setVisibility(View.GONE);
swipeRefreshLayout.setRefreshing(false);
updateEmptyState();
Log.e(TAG, "加载黑名单失败", t);
Toast.makeText(BlacklistActivity.this, "网络错误", Toast.LENGTH_SHORT).show();
}
});
}
private void loadMore() {
currentPage++;
loadBlacklist();
}
private void updateEmptyState() {
if (blacklist.isEmpty()) {
emptyView.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
} else {
emptyView.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
}
}
private void handleRemoveFromBlacklist(Map<String, Object> item) {
Object blockedUserId = item.get("blockedUserId");
String nickname = item.get("blockedNickname") != null ? item.get("blockedNickname").toString() : "该用户";
new AlertDialog.Builder(this)
.setTitle("移除黑名单")
.setMessage("确定要将 " + nickname + " 从黑名单中移除吗?")
.setPositiveButton("确定", (dialog, which) -> {
if (blockedUserId != null) {
removeFromBlacklist(((Number) blockedUserId).intValue(), item);
}
})
.setNegativeButton("取消", null)
.show();
}
private void removeFromBlacklist(int targetUserId, Map<String, Object> item) {
java.util.Map<String, Object> body = new java.util.HashMap<>();
body.put("targetUserId", targetUserId);
ApiClient.getService(this).removeFromBlacklist(body)
.enqueue(new Callback<ApiResponse<Map<String, Object>>>() {
@Override
public void onResponse(Call<ApiResponse<Map<String, Object>>> call,
Response<ApiResponse<Map<String, Object>>> response) {
if (response.isSuccessful() && response.body() != null && response.body().isOk()) {
blacklist.remove(item);
adapter.setData(new ArrayList<>(blacklist));
updateEmptyState();
Toast.makeText(BlacklistActivity.this, "已移除", Toast.LENGTH_SHORT).show();
} else {
String msg = response.body() != null ? response.body().getMessage() : "移除失败";
Toast.makeText(BlacklistActivity.this, msg, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<ApiResponse<Map<String, Object>>> call, Throwable t) {
Log.e(TAG, "移除黑名单失败", t);
Toast.makeText(BlacklistActivity.this, "网络错误", Toast.LENGTH_SHORT).show();
}
});
}
/**
* 黑名单列表适配器
*/
private static class BlacklistAdapter extends RecyclerView.Adapter<BlacklistAdapter.ViewHolder> {
private List<Map<String, Object>> data = new ArrayList<>();
private final OnItemClickListener listener;
interface OnItemClickListener {
void onRemoveClick(Map<String, Object> item);
}
BlacklistAdapter(OnItemClickListener listener) {
this.listener = listener;
}
void setData(List<Map<String, Object>> data) {
this.data = data;
notifyDataSetChanged();
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_blacklist, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Map<String, Object> item = data.get(position);
holder.bind(item);
holder.btnRemove.setOnClickListener(v -> {
if (listener != null) listener.onRemoveClick(item);
});
}
@Override
public int getItemCount() {
return data.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
ImageView ivAvatar;
TextView tvNickname;
TextView tvTime;
TextView btnRemove;
ViewHolder(@NonNull View itemView) {
super(itemView);
ivAvatar = itemView.findViewById(R.id.ivAvatar);
tvNickname = itemView.findViewById(R.id.tvNickname);
tvTime = itemView.findViewById(R.id.tvTime);
btnRemove = itemView.findViewById(R.id.btnRemove);
}
void bind(Map<String, Object> item) {
String nickname = item.get("blockedNickname") != null ?
item.get("blockedNickname").toString() : "未知用户";
String avatar = item.get("blockedAvatar") != null ?
item.get("blockedAvatar").toString() : "";
String createTime = item.get("createTime") != null ?
item.get("createTime").toString() : "";
tvNickname.setText(nickname);
if (createTime.length() > 10) {
tvTime.setText("拉黑于 " + createTime.substring(0, 10));
} else {
tvTime.setText("拉黑于 " + createTime);
}
if (!avatar.isEmpty()) {
Glide.with(itemView.getContext())
.load(avatar)
.placeholder(R.drawable.ic_default_avatar)
.error(R.drawable.ic_default_avatar)
.transform(new CircleCrop())
.into(ivAvatar);
} else {
ivAvatar.setImageResource(R.drawable.ic_default_avatar);
}
}
}
}
}

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/colorPrimary" />
<corners android:radius="14dp" />
<padding
android:left="12dp"
android:top="4dp"
android:right="12dp"
android:bottom="4dp" />
</shape>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:width="1dp"
android:color="@color/colorPrimary" />
<corners android:radius="16dp" />
<solid android:color="@android:color/transparent" />
</shape>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#F5F5F5" />
<corners android:radius="8dp" />
</shape>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/tag_live" />
<corners android:radius="10dp" />
<padding
android:left="6dp"
android:top="2dp"
android:right="6dp"
android:bottom="2dp" />
</shape>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/tag_profile" />
<corners android:radius="10dp" />
<padding
android:left="6dp"
android:top="2dp"
android:right="6dp"
android:bottom="2dp" />
</shape>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/tag_room" />
<corners android:radius="10dp" />
<padding
android:left="6dp"
android:top="2dp"
android:right="6dp"
android:bottom="2dp" />
</shape>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/tag_wish" />
<corners android:radius="10dp" />
<padding
android:left="6dp"
android:top="2dp"
android:right="6dp"
android:bottom="2dp" />
</shape>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/tag_work" />
<corners android:radius="10dp" />
<padding
android:left="6dp"
android:top="2dp"
android:right="6dp"
android:bottom="2dp" />
</shape>

View File

@ -0,0 +1,108 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/background_light">
<!-- 顶部标题栏 -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="56dp"
android:background="@color/white"
android:elevation="2dp">
<ImageView
android:id="@+id/backButton"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_centerVertical="true"
android:padding="12dp"
android:src="@drawable/ic_arrow_back_24"
android:contentDescription="返回"
app:tint="@color/text_primary" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="黑名单"
android:textColor="@color/text_primary"
android:textSize="18sp"
android:textStyle="bold" />
</RelativeLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 下拉刷新 -->
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:padding="8dp" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<!-- 加载中 -->
<LinearLayout
android:id="@+id/loadingView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
<ProgressBar
android:layout_width="48dp"
android:layout_height="48dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="加载中..."
android:textColor="@color/text_secondary" />
</LinearLayout>
<!-- 空状态 -->
<LinearLayout
android:id="@+id/emptyView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
<ImageView
android:layout_width="80dp"
android:layout_height="80dp"
android:src="@drawable/ic_people_24"
android:alpha="0.3"
app:tint="@color/text_hint" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="暂无黑名单用户"
android:textColor="@color/text_secondary"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="被加入黑名单的用户将无法与您互动"
android:textColor="@color/text_hint"
android:textSize="14sp" />
</LinearLayout>
</FrameLayout>
</LinearLayout>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F5F5F5">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="8dp"
android:clipToPadding="false" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<TextView
android:id="@+id/tv_empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="暂无记录"
android:textSize="14sp"
android:textColor="#999999"
android:visibility="gone" />
</FrameLayout>

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp"
android:layout_marginVertical="4dp"
app:cardCornerRadius="8dp"
app:cardElevation="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="16dp"
android:gravity="center_vertical">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15sp"
android:textColor="#333333"
android:text="交易记录" />
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textSize="12sp"
android:textColor="#999999"
android:text="2024-01-01 12:00" />
</LinearLayout>
<TextView
android:id="@+id/tv_amount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="#4CAF50"
android:text="+100.00" />
</LinearLayout>
</androidx.cardview.widget.CardView>

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp"
android:layout_marginVertical="4dp"
app:cardCornerRadius="12dp"
app:cardElevation="2dp"
app:cardBackgroundColor="@color/white">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<!-- 用户头像 -->
<ImageView
android:id="@+id/ivAvatar"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:src="@drawable/ic_default_avatar"
android:contentDescription="用户头像" />
<!-- 用户信息 -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="12dp"
android:layout_toStartOf="@+id/btnRemove"
android:layout_toEndOf="@id/ivAvatar"
android:orientation="vertical">
<TextView
android:id="@+id/tvNickname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:text="用户昵称"
android:textColor="@color/text_primary"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="拉黑于 2024-01-01"
android:textColor="@color/text_hint"
android:textSize="12sp" />
</LinearLayout>
<!-- 移除按钮 -->
<TextView
android:id="@+id/btnRemove"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:background="@drawable/bg_button_outline_primary"
android:paddingHorizontal="16dp"
android:paddingVertical="8dp"
android:text="移除"
android:textColor="@color/colorPrimary"
android:textSize="14sp" />
</RelativeLayout>
</androidx.cardview.widget.CardView>

102
ban_system_tables.sql Normal file
View File

@ -0,0 +1,102 @@
-- =====================================================
-- 封禁系统数据库表结构
-- 包含用户封禁、房间封禁功能
-- =====================================================
-- 1. 用户封禁表(管理员对用户的封禁)
CREATE TABLE IF NOT EXISTS `eb_user_ban` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`user_id` int(11) NOT NULL COMMENT '被封禁的用户ID',
`ban_type` varchar(20) NOT NULL DEFAULT 'permanent' COMMENT '封禁类型permanent-永久, temporary-临时',
`reason` varchar(500) DEFAULT '' COMMENT '封禁原因',
`duration_days` int(11) DEFAULT NULL COMMENT '封禁天数(临时封禁时使用)',
`expire_time` datetime DEFAULT NULL COMMENT '封禁到期时间(临时封禁时使用)',
`operator_id` int(11) DEFAULT 0 COMMENT '操作人ID管理员',
`status` tinyint(1) NOT NULL DEFAULT 1 COMMENT '状态1-生效中, 0-已解除',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_status` (`status`),
KEY `idx_expire_time` (`expire_time`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户封禁记录表';
-- 2. 房间封禁表(管理员对直播间的封禁)
CREATE TABLE IF NOT EXISTS `eb_room_ban` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`room_id` int(11) NOT NULL COMMENT '被封禁的房间ID',
`ban_type` varchar(20) NOT NULL DEFAULT 'permanent' COMMENT '封禁类型permanent-永久, temporary-临时',
`reason` varchar(500) DEFAULT '' COMMENT '封禁原因',
`duration_days` int(11) DEFAULT NULL COMMENT '封禁天数(临时封禁时使用)',
`expire_time` datetime DEFAULT NULL COMMENT '封禁到期时间(临时封禁时使用)',
`operator_id` int(11) DEFAULT 0 COMMENT '操作人ID管理员',
`status` tinyint(1) NOT NULL DEFAULT 1 COMMENT '状态1-生效中, 0-已解除',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_room_id` (`room_id`),
KEY `idx_status` (`status`),
KEY `idx_expire_time` (`expire_time`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='房间封禁记录表';
-- 3. 确保用户黑名单表存在(用户间的拉黑)
CREATE TABLE IF NOT EXISTS `eb_user_blacklist` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`user_id` int(11) NOT NULL COMMENT '用户ID发起拉黑的用户',
`blocked_user_id` int(11) NOT NULL COMMENT '被拉黑的用户ID',
`blocker_nickname` varchar(100) DEFAULT '' COMMENT '拉黑者昵称',
`blocker_avatar` varchar(500) DEFAULT '' COMMENT '拉黑者头像',
`blocked_nickname` varchar(100) DEFAULT '' COMMENT '被拉黑者昵称',
`blocked_avatar` varchar(500) DEFAULT '' COMMENT '被拉黑者头像',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_user_blocked` (`user_id`, `blocked_user_id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_blocked_user_id` (`blocked_user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户黑名单表';
-- 4. 确保房间黑名单表存在(直播间对用户的拉黑)
CREATE TABLE IF NOT EXISTS `eb_room_blacklist` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`room_id` int(11) NOT NULL COMMENT '房间ID',
`room_name` varchar(200) DEFAULT '' COMMENT '房间名称',
`blocked_user_id` int(11) NOT NULL COMMENT '被拉黑的用户ID',
`blocked_user_nickname` varchar(100) DEFAULT '' COMMENT '被拉黑用户昵称',
`blocked_user_avatar` varchar(500) DEFAULT '' COMMENT '被拉黑用户头像',
`reason` varchar(500) DEFAULT '' COMMENT '拉黑原因',
`operator_id` int(11) DEFAULT 0 COMMENT '操作人ID主播或管理员',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_room_user` (`room_id`, `blocked_user_id`),
KEY `idx_room_id` (`room_id`),
KEY `idx_blocked_user_id` (`blocked_user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='房间黑名单表';
-- 5. 添加测试数据
-- 用户封禁测试数据
INSERT IGNORE INTO eb_user_ban (user_id, ban_type, reason, duration_days, operator_id, status) VALUES
(999, 'permanent', '违规发布不良内容', NULL, 1, 1),
(998, 'temporary', '多次发送垃圾信息', 7, 1, 1);
-- 更新临时封禁的到期时间
UPDATE eb_user_ban SET expire_time = DATE_ADD(create_time, INTERVAL duration_days DAY)
WHERE ban_type = 'temporary' AND duration_days IS NOT NULL AND expire_time IS NULL;
-- 6. 验证表结构
SELECT '=== 封禁系统表结构 ===' as info;
SHOW TABLES LIKE 'eb_%ban%';
SHOW TABLES LIKE 'eb_%blacklist%';
SELECT '=== 用户封禁记录 ===' as info;
SELECT * FROM eb_user_ban ORDER BY create_time DESC LIMIT 5;
SELECT '=== 房间封禁记录 ===' as info;
SELECT * FROM eb_room_ban ORDER BY create_time DESC LIMIT 5;
SELECT '=== 用户黑名单 ===' as info;
SELECT * FROM eb_user_blacklist ORDER BY create_time DESC LIMIT 5;
SELECT '=== 房间黑名单 ===' as info;
SELECT * FROM eb_room_blacklist ORDER BY create_time DESC LIMIT 5;

43
check_and_fix_records.sql Normal file
View File

@ -0,0 +1,43 @@
-- =====================================================
-- 检查并修复"我的记录"数据问题
-- =====================================================
-- 1. 首先找到当前登录用户的真实ID
-- 从截图看用户ID是 24187196但这可能是显示ID需要找到数据库中的真实uid
SELECT uid, id, nickname, phone, avatar
FROM eb_user
WHERE nickname LIKE '%道玄%'
OR uid = 24187196
OR id = 121
OR id = 43
ORDER BY id DESC
LIMIT 10;
-- 2. 检查观看历史表
SELECT '=== eb_view_history 表数据 ===' as info;
SELECT * FROM eb_view_history ORDER BY update_time DESC LIMIT 20;
-- 3. 检查点赞记录表
SELECT '=== eb_live_room_like 表数据 ===' as info;
SELECT * FROM eb_live_room_like ORDER BY create_time DESC LIMIT 20;
-- 4. 检查关注记录表
SELECT '=== eb_follow_record 表数据 ===' as info;
SELECT * FROM eb_follow_record ORDER BY create_time DESC LIMIT 20;
-- 5. 检查收藏表(如果存在)
SELECT '=== 检查收藏相关表 ===' as info;
SHOW TABLES LIKE '%collect%';
SHOW TABLES LIKE '%favorite%';
-- 6. 查看所有用户的活动数据统计
SELECT '=== 各用户活动数据统计 ===' as info;
SELECT
u.uid,
u.nickname,
(SELECT COUNT(*) FROM eb_view_history vh WHERE vh.user_id = u.uid) as ,
(SELECT COUNT(*) FROM eb_live_room_like lrl WHERE lrl.user_id = u.uid) as ,
(SELECT COUNT(*) FROM eb_follow_record fr WHERE fr.follower_id = u.uid) as
FROM eb_user u
WHERE u.uid IN (121, 43, 24187196)
OR u.nickname LIKE '%道玄%';

View File

@ -0,0 +1,24 @@
-- 检查封禁菜单的完整配置
SELECT '=== 封禁菜单配置 ===' as info;
SELECT id, pid, `name`, perms, component, menu_type, sort, is_show, is_delte
FROM eb_system_menu
WHERE `name` LIKE '%封禁%';
-- 检查管理员角色权限
SELECT '=== 管理员角色 ===' as info;
SELECT * FROM eb_system_role WHERE id = 1;
-- 检查角色菜单关联
SELECT '=== 角色菜单关联 ===' as info;
SELECT * FROM eb_system_role_menu WHERE rid = 1 ORDER BY menu_id DESC LIMIT 20;
-- 为管理员角色添加封禁菜单权限
INSERT IGNORE INTO eb_system_role_menu (rid, menu_id)
SELECT 1, id FROM eb_system_menu WHERE `name` IN ('用户封禁', '房间封禁');
-- 验证添加结果
SELECT '=== 添加权限后 ===' as info;
SELECT rm.rid, rm.menu_id, m.name, m.component
FROM eb_system_role_menu rm
JOIN eb_system_menu m ON rm.menu_id = m.id
WHERE m.name LIKE '%封禁%';

26
check_blacklist_menu.sql Normal file
View File

@ -0,0 +1,26 @@
-- 检查拉黑菜单状态
-- 1. 查看社交互动菜单ID
SELECT id, pid, `name`, component, menu_type, is_show FROM eb_system_menu WHERE `name` = '社交互动';
-- 2. 查看社交互动下所有子菜单(包括隐藏的)
SELECT id, pid, `name`, component, sort, is_show, is_delete
FROM eb_system_menu
WHERE pid = (SELECT id FROM eb_system_menu WHERE `name` = '社交互动' LIMIT 1)
ORDER BY sort;
-- 3. 查看拉黑菜单是否存在
SELECT id, pid, `name`, component, is_show, is_delete
FROM eb_system_menu
WHERE `name` IN ('用户拉黑', '房间拉黑');
-- 4. 查看角色权限
SELECT rm.rid, rm.menu_id, m.`name`
FROM eb_system_role_menu rm
JOIN eb_system_menu m ON rm.menu_id = m.id
WHERE m.`name` IN ('用户拉黑', '房间拉黑');
-- 5. 查看群组管理的配置作为参考
SELECT id, pid, `name`, component, sort, is_show, menu_type
FROM eb_system_menu
WHERE `name` = '群组管理';

View File

@ -0,0 +1,29 @@
-- 检查拉黑菜单状态 (修正版)
-- 1. 查看社交互动菜单ID
SELECT id, pid, `name`, component, menu_type, is_show FROM eb_system_menu WHERE `name` = '社交互动';
-- 2. 查看社交互动下所有子菜单
SELECT id, pid, `name`, component, sort, is_show
FROM eb_system_menu
WHERE pid = (SELECT id FROM eb_system_menu WHERE `name` = '社交互动' LIMIT 1)
ORDER BY sort;
-- 3. 查看拉黑菜单是否存在
SELECT id, pid, `name`, component, is_show, menu_type
FROM eb_system_menu
WHERE `name` IN ('用户拉黑', '房间拉黑');
-- 4. 查看角色权限
SELECT rm.rid, rm.menu_id, m.`name`
FROM eb_system_role_menu rm
JOIN eb_system_menu m ON rm.menu_id = m.id
WHERE m.`name` IN ('用户拉黑', '房间拉黑');
-- 5. 查看群组管理的配置作为参考
SELECT id, pid, `name`, component, sort, is_show, menu_type
FROM eb_system_menu
WHERE `name` = '群组管理';
-- 6. 查看表结构
DESC eb_system_menu;

View File

@ -0,0 +1,19 @@
-- 检查 eb_follow_record 表结构
DESCRIBE eb_follow_record;
-- 检查 follow_status 字段的实际值
SELECT DISTINCT follow_status, COUNT(*) as cnt
FROM eb_follow_record
GROUP BY follow_status;
-- 检查用户121的关注记录
SELECT * FROM eb_follow_record WHERE follower_id = 121;
-- 测试SQL查询
SELECT fr.id, fr.followed_id as followedId, fr.followed_nickname as followedNickname,
u.avatar as followedAvatar, u.phone, fr.follow_status as followStatus,
fr.create_time as createTime
FROM eb_follow_record fr
LEFT JOIN eb_user u ON fr.followed_id = u.uid
WHERE fr.follower_id = 121 AND (fr.follow_status = 1 OR fr.follow_status = '关注')
ORDER BY fr.create_time DESC LIMIT 10 OFFSET 0;

View File

@ -0,0 +1,6 @@
-- 检查各表结构
DESCRIBE eb_live_room_like;
DESCRIBE eb_works_relation;
DESCRIBE eb_follow_record;
DESCRIBE eb_search_history;
DESCRIBE eb_friend;

21
check_tables_for_121.sql Normal file
View File

@ -0,0 +1,21 @@
-- 检查关键表的数据情况
-- 1. 检查直播间点赞表
SELECT '=== eb_live_room_like ===' as ;
SELECT * FROM eb_live_room_like WHERE user_id = 121;
-- 2. 检查作品关系表
SELECT '=== eb_works_relation ===' as ;
SELECT * FROM eb_works_relation WHERE uid = 121;
-- 3. 检查关注记录表
SELECT '=== eb_follow_record ===' as ;
SELECT * FROM eb_follow_record WHERE follower_id = 121;
-- 4. 检查直播间表是否存在
SELECT '=== eb_room / eb_live_room ===' as ;
SHOW TABLES LIKE '%room%';
-- 5. 检查作品表
SELECT '=== eb_works ===' as ;
SELECT id, title, user_id FROM eb_works LIMIT 5;

33
check_user_121_data.sql Normal file
View File

@ -0,0 +1,33 @@
-- =====================================================
-- 检查用户"道玄"ID: 121的测试数据
-- =====================================================
-- 检查用户基本信息
SELECT uid, nickname, phone, now_money, integral, experience
FROM eb_user WHERE uid = 121;
-- 检查各表数据统计
SELECT '观看历史' as , COUNT(*) as FROM eb_watch_history WHERE uid = 121
UNION ALL
SELECT '直播间点赞', COUNT(*) FROM eb_live_room_like WHERE uid = 121
UNION ALL
SELECT '作品点赞', COUNT(*) FROM eb_works_relation WHERE uid = 121 AND type = 1
UNION ALL
SELECT '作品收藏', COUNT(*) FROM eb_works_relation WHERE uid = 121 AND type = 2
UNION ALL
SELECT '关注数', COUNT(*) FROM eb_follow_record WHERE uid = 121
UNION ALL
SELECT '粉丝数', COUNT(*) FROM eb_follow_record WHERE follow_uid = 121
UNION ALL
SELECT '搜索历史', COUNT(*) FROM eb_search_history WHERE uid = 121
UNION ALL
SELECT '好友数', COUNT(*) FROM eb_friend WHERE uid = 121;
-- 检查礼物记录表是否存在
SELECT COUNT(*) as FROM eb_gift_record WHERE uid = 121;
-- 检查私聊会话
SELECT COUNT(*) as FROM eb_conversation WHERE user1_id = 121 OR user2_id = 121;
-- 检查充值记录
SELECT COUNT(*) as FROM eb_recharge_order WHERE uid = 121;

16
check_user_records.sql Normal file
View File

@ -0,0 +1,16 @@
-- 检查用户"道玄"(ID: 24187196 或 uid) 的活动记录数据
-- 先查找用户ID
SELECT uid, nickname, phone FROM eb_user WHERE nickname LIKE '%道玄%' OR uid = 24187196 LIMIT 5;
-- 查看观看历史表结构
SHOW TABLES LIKE '%view%';
SHOW TABLES LIKE '%history%';
-- 查看点赞表
SHOW TABLES LIKE '%like%';
-- 查看收藏表
SHOW TABLES LIKE '%collect%';
-- 查看关注表
SHOW TABLES LIKE '%follow%';

5
check_working_menu.sql Normal file
View File

@ -0,0 +1,5 @@
-- 查看正常工作的菜单配置(如黑名单)
SELECT id, pid, `name`, perms, component, menu_type, sort, is_show
FROM eb_system_menu
WHERE `name` IN ('房间拉黑', '用户拉黑', '用户封禁', '房间封禁', '用户列表', '私聊管理')
ORDER BY id;

View File

@ -0,0 +1,22 @@
-- =====================================================
-- 为用户"道玄"ID: 121创建完整测试数据
-- 确保与后端查询逻辑一致
-- =====================================================
-- 先检查现有数据
SELECT '=== 检查现有数据 ===' as ;
-- 1. 检查直播间表名和结构
SHOW TABLES LIKE '%room%';
-- 2. 检查作品表
SELECT COUNT(*) as FROM eb_works;
-- 3. 检查用户表中的测试用户
SELECT uid, nickname FROM eb_user WHERE uid IN (100, 101, 102, 103, 104, 105, 121) LIMIT 10;
-- =====================================================
-- 创建测试直播间数据(如果不存在)
-- =====================================================
-- 先检查直播间表结构
DESCRIBE eb_room;

View File

@ -0,0 +1,119 @@
-- =====================================================
-- 完整的用户活动数据初始化脚本
-- 确保移动端"我的记录"功能正常显示
-- =====================================================
-- 1. 确保所有必要的表存在
-- =====================================================
-- 观看历史表
CREATE TABLE IF NOT EXISTS `eb_view_history` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL COMMENT '用户ID',
`target_type` varchar(20) NOT NULL COMMENT '目标类型room-直播间, work-作品, profile-用户主页',
`target_id` varchar(50) NOT NULL COMMENT '目标ID',
`target_title` varchar(255) DEFAULT NULL COMMENT '目标标题',
`view_duration` int(11) DEFAULT 0 COMMENT '观看时长(秒)',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_target` (`target_type`, `target_id`),
KEY `idx_update_time` (`update_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='观看历史记录表';
-- 直播间点赞表
CREATE TABLE IF NOT EXISTS `eb_live_room_like` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL COMMENT '用户ID',
`room_id` varchar(50) NOT NULL COMMENT '直播间ID',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_user_room` (`user_id`, `room_id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_room_id` (`room_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='直播间点赞记录表';
-- 心愿点赞表
CREATE TABLE IF NOT EXISTS `eb_wish_like` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL COMMENT '用户ID',
`wish_id` bigint(20) NOT NULL COMMENT '心愿ID',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_user_wish` (`user_id`, `wish_id`),
KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='心愿点赞记录表';
-- 搜索历史表
CREATE TABLE IF NOT EXISTS `eb_search_history` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL COMMENT '用户ID',
`keyword` varchar(100) NOT NULL COMMENT '搜索关键词',
`search_type` varchar(20) DEFAULT 'all' COMMENT '搜索类型',
`is_deleted` tinyint(1) DEFAULT 0 COMMENT '是否删除',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_keyword` (`keyword`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='搜索历史记录表';
-- 2. 为用户121道玄添加完整测试数据
-- =====================================================
SET @USER_ID = 121;
-- 清除旧数据(可选)
-- DELETE FROM eb_view_history WHERE user_id = @USER_ID;
-- DELETE FROM eb_live_room_like WHERE user_id = @USER_ID;
-- 添加观看历史
INSERT IGNORE INTO eb_view_history (user_id, target_type, target_id, target_title, view_duration, create_time, update_time) VALUES
(@USER_ID, 'room', '1', '欢乐游戏直播', 1800, NOW() - INTERVAL 1 HOUR, NOW() - INTERVAL 1 HOUR),
(@USER_ID, 'room', '2', '音乐分享会', 2400, NOW() - INTERVAL 2 HOUR, NOW() - INTERVAL 2 HOUR),
(@USER_ID, 'room', '3', '户外探险', 1200, NOW() - INTERVAL 3 HOUR, NOW() - INTERVAL 3 HOUR),
(@USER_ID, 'room', '4', '美食制作教程', 3600, NOW() - INTERVAL 4 HOUR, NOW() - INTERVAL 4 HOUR),
(@USER_ID, 'room', '5', '编程技术分享', 5400, NOW() - INTERVAL 5 HOUR, NOW() - INTERVAL 5 HOUR);
-- 添加直播间点赞
INSERT IGNORE INTO eb_live_room_like (user_id, room_id, create_time) VALUES
(@USER_ID, '1', NOW() - INTERVAL 1 HOUR),
(@USER_ID, '2', NOW() - INTERVAL 2 HOUR),
(@USER_ID, '3', NOW() - INTERVAL 3 HOUR);
-- 3. 验证数据
-- =====================================================
SELECT '=== 用户121的观看历史 ===' as info;
SELECT id, user_id, target_type, target_id, target_title, view_duration,
DATE_FORMAT(update_time, '%Y-%m-%d %H:%i:%s') as update_time
FROM eb_view_history
WHERE user_id = @USER_ID
ORDER BY update_time DESC;
SELECT '=== 用户121的直播间点赞 ===' as info;
SELECT rl.id, rl.user_id, rl.room_id, lr.title as room_title,
DATE_FORMAT(rl.create_time, '%Y-%m-%d %H:%i:%s') as create_time
FROM eb_live_room_like rl
LEFT JOIN eb_live_room lr ON rl.room_id = lr.id
WHERE rl.user_id = @USER_ID;
SELECT '=== 用户121的关注记录 ===' as info;
SELECT fr.id, fr.follower_id, fr.followed_id, fr.followed_nickname, fr.follow_status,
DATE_FORMAT(fr.create_time, '%Y-%m-%d %H:%i:%s') as create_time
FROM eb_follow_record fr
WHERE fr.follower_id = @USER_ID AND (fr.follow_status = 1 OR fr.follow_status = '关注');
SELECT '=== 用户121的收藏作品 ===' as info;
SELECT wr.id, wr.uid, wr.works_id, w.title,
DATE_FORMAT(wr.create_time, '%Y-%m-%d %H:%i:%s') as create_time
FROM eb_works_relation wr
LEFT JOIN eb_works w ON wr.works_id = w.id
WHERE wr.uid = @USER_ID AND wr.type = 2;
SELECT '=== 数据汇总 ===' as info;
SELECT
(SELECT COUNT(*) FROM eb_view_history WHERE user_id = @USER_ID) as ,
(SELECT COUNT(*) FROM eb_live_room_like WHERE user_id = @USER_ID) as ,
(SELECT COUNT(*) FROM eb_follow_record WHERE follower_id = @USER_ID AND (follow_status = 1 OR follow_status = '关注')) as ,
(SELECT COUNT(*) FROM eb_works_relation WHERE uid = @USER_ID AND type = 2) as ;

View File

@ -0,0 +1,159 @@
-- =====================================================
-- 为用户"道玄"ID: 121创建缺失的表并添加测试数据
-- =====================================================
-- =====================================================
-- 1. 创建观看历史表 (eb_watch_history)
-- =====================================================
CREATE TABLE IF NOT EXISTS `eb_watch_history` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`uid` int(11) NOT NULL COMMENT '用户ID',
`target_type` varchar(20) NOT NULL COMMENT '目标类型room-直播间, work-作品',
`target_id` varchar(50) NOT NULL COMMENT '目标ID',
`target_title` varchar(200) DEFAULT NULL COMMENT '目标标题',
`target_cover` varchar(500) DEFAULT NULL COMMENT '目标封面',
`duration` int(11) DEFAULT 0 COMMENT '观看时长(秒)',
`watch_time` datetime DEFAULT NULL COMMENT '观看时间',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`),
KEY `idx_uid` (`uid`),
KEY `idx_target` (`target_type`, `target_id`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='观看历史表';
-- =====================================================
-- 2. 插入观看历史数据
-- =====================================================
INSERT INTO eb_watch_history (uid, target_type, target_id, target_title, target_cover, duration, watch_time, create_time) VALUES
(121, 'room', '1', '欢乐游戏直播', 'https://example.com/cover1.jpg', 1800, '2026-01-03 20:30:00', '2026-01-03 20:30:00'),
(121, 'room', '2', '音乐分享会', 'https://example.com/cover2.jpg', 2400, '2026-01-03 21:00:00', '2026-01-03 21:00:00'),
(121, 'room', '3', '户外探险直播', 'https://example.com/cover3.jpg', 900, '2026-01-02 19:00:00', '2026-01-02 19:00:00'),
(121, 'room', '4', '美食制作教程', 'https://example.com/cover4.jpg', 3600, '2026-01-02 12:00:00', '2026-01-02 12:00:00'),
(121, 'room', '5', '编程技术分享', 'https://example.com/cover5.jpg', 5400, '2026-01-01 14:00:00', '2026-01-01 14:00:00'),
(121, 'work', '1', '搞笑短视频合集', 'https://example.com/work1.jpg', 180, '2026-01-03 22:00:00', '2026-01-03 22:00:00'),
(121, 'work', '2', '旅行Vlog-云南之旅', 'https://example.com/work2.jpg', 600, '2026-01-03 18:00:00', '2026-01-03 18:00:00'),
(121, 'work', '3', '美妆教程分享', 'https://example.com/work3.jpg', 420, '2026-01-02 16:00:00', '2026-01-02 16:00:00');
-- =====================================================
-- 3. 检查并创建直播间点赞表
-- =====================================================
CREATE TABLE IF NOT EXISTS `eb_live_room_like` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`uid` int(11) NOT NULL COMMENT '用户ID',
`room_id` int(11) NOT NULL COMMENT '直播间ID',
`like_count` int(11) DEFAULT 1 COMMENT '点赞次数',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_uid_room` (`uid`, `room_id`),
KEY `idx_room_id` (`room_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='直播间点赞表';
-- 插入直播间点赞数据
INSERT IGNORE INTO eb_live_room_like (uid, room_id, like_count, create_time) VALUES
(121, 1, 15, '2026-01-03 20:35:00'),
(121, 2, 28, '2026-01-03 21:15:00'),
(121, 3, 8, '2026-01-02 19:10:00'),
(121, 4, 42, '2026-01-02 12:30:00'),
(121, 5, 20, '2026-01-01 14:30:00');
-- =====================================================
-- 4. 插入作品关系数据(点赞和收藏)
-- =====================================================
-- 点赞作品 (type=1 或 type='like')
INSERT IGNORE INTO eb_works_relation (uid, works_id, type, create_time) VALUES
(121, 1, 1, '2026-01-03 22:05:00'),
(121, 2, 1, '2026-01-03 18:10:00'),
(121, 3, 1, '2026-01-02 16:15:00'),
(121, 4, 1, '2026-01-01 20:00:00'),
(121, 5, 1, '2026-01-01 15:00:00');
-- 收藏作品 (type=2 或 type='collect')
INSERT IGNORE INTO eb_works_relation (uid, works_id, type, create_time) VALUES
(121, 1, 2, '2026-01-03 22:06:00'),
(121, 2, 2, '2026-01-03 18:12:00'),
(121, 6, 2, '2026-01-02 10:00:00'),
(121, 7, 2, '2026-01-01 11:00:00');
-- =====================================================
-- 5. 插入关注记录
-- =====================================================
INSERT IGNORE INTO eb_follow_record (uid, follow_uid, create_time) VALUES
(121, 100, '2026-01-03 20:00:00'),
(121, 101, '2026-01-03 15:00:00'),
(121, 102, '2026-01-02 18:00:00'),
(121, 103, '2026-01-02 12:00:00'),
(121, 104, '2026-01-01 20:00:00'),
(121, 105, '2026-01-01 10:00:00');
-- 被关注记录
INSERT IGNORE INTO eb_follow_record (uid, follow_uid, create_time) VALUES
(100, 121, '2026-01-03 21:00:00'),
(101, 121, '2026-01-03 16:00:00'),
(106, 121, '2026-01-02 20:00:00'),
(107, 121, '2026-01-02 14:00:00');
-- =====================================================
-- 6. 插入搜索历史
-- =====================================================
INSERT IGNORE INTO eb_search_history (uid, keyword, search_type, search_count, create_time) VALUES
(121, '游戏直播', 1, 3, '2026-01-03 20:00:00'),
(121, '音乐', 1, 2, '2026-01-03 19:00:00'),
(121, '美食教程', 2, 1, '2026-01-02 11:00:00'),
(121, '旅行Vlog', 2, 2, '2026-01-02 10:00:00'),
(121, '编程学习', 1, 4, '2026-01-01 13:00:00'),
(121, '户外运动', 1, 1, '2026-01-01 09:00:00');
-- =====================================================
-- 7. 创建并插入好友关系
-- =====================================================
CREATE TABLE IF NOT EXISTS `eb_friend` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`uid` int(11) NOT NULL COMMENT '用户ID',
`friend_uid` int(11) NOT NULL COMMENT '好友用户ID',
`remark` varchar(50) DEFAULT NULL COMMENT '备注名',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_uid_friend` (`uid`, `friend_uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='好友关系表';
INSERT IGNORE INTO eb_friend (uid, friend_uid, remark, create_time) VALUES
(121, 100, '游戏主播', '2026-01-03 21:35:00'),
(121, 101, '音乐达人', '2026-01-03 16:10:00'),
(100, 121, '道玄', '2026-01-03 21:35:00'),
(101, 121, '道玄', '2026-01-03 16:10:00');
-- =====================================================
-- 8. 更新用户余额和积分
-- =====================================================
UPDATE eb_user SET
now_money = 500.00,
integral = 1200,
experience = 350
WHERE uid = 121;
-- =====================================================
-- 9. 验证数据
-- =====================================================
SELECT '=== 用户道玄(ID:121)数据统计 ===' as ;
SELECT '观看历史' as , COUNT(*) as FROM eb_watch_history WHERE uid = 121
UNION ALL
SELECT '直播间点赞', COUNT(*) FROM eb_live_room_like WHERE uid = 121
UNION ALL
SELECT '作品点赞', COUNT(*) FROM eb_works_relation WHERE uid = 121 AND type = 1
UNION ALL
SELECT '作品收藏', COUNT(*) FROM eb_works_relation WHERE uid = 121 AND type = 2
UNION ALL
SELECT '关注数', COUNT(*) FROM eb_follow_record WHERE uid = 121
UNION ALL
SELECT '粉丝数', COUNT(*) FROM eb_follow_record WHERE follow_uid = 121
UNION ALL
SELECT '搜索历史', COUNT(*) FROM eb_search_history WHERE uid = 121
UNION ALL
SELECT '好友数', COUNT(*) FROM eb_friend WHERE uid = 121;
-- 检查用户余额
SELECT uid, nickname, now_money as , integral as , experience as
FROM eb_user WHERE uid = 121;
SELECT '数据插入完成!' as ;

View File

@ -0,0 +1,12 @@
-- =====================================================
-- 创建测试直播间和作品数据
-- =====================================================
-- 1. 检查 eb_live_room 表结构
DESCRIBE eb_live_room;
-- 2. 检查现有直播间
SELECT id, room_title, owner_nickname, status FROM eb_live_room LIMIT 10;
-- 3. 检查 eb_works 表结构
DESCRIBE eb_works;

23
debug_menu_structure.sql Normal file
View File

@ -0,0 +1,23 @@
-- 查看黑名单菜单的完整层级结构
-- 黑名单管理 (id=569) 的父菜单是什么?
SELECT id, pid, `name`, component, menu_type
FROM eb_system_menu
WHERE id = 569;
-- 查看 pid=0 的顶级菜单
SELECT id, pid, `name`, component, menu_type
FROM eb_system_menu
WHERE pid = 0 AND is_show = 1
ORDER BY sort;
-- 查看用户管理 (id=674) 的子菜单
SELECT id, pid, `name`, component, menu_type, sort
FROM eb_system_menu
WHERE pid = 674
ORDER BY sort;
-- 查看封禁菜单的父菜单
SELECT id, pid, `name`, component, menu_type
FROM eb_system_menu
WHERE id = 674;

38
debug_user_token.sql Normal file
View File

@ -0,0 +1,38 @@
-- 调试用户Token和数据问题
-- =====================================================
-- 1. 确认用户"道玄"的真实uid
SELECT uid, nickname, phone, avatar, status
FROM eb_user
WHERE nickname LIKE '%道玄%';
-- 2. 检查该用户的观看历史数据
SELECT vh.*, lr.title as room_title
FROM eb_view_history vh
LEFT JOIN eb_live_room lr ON vh.target_id = lr.id AND vh.target_type = 'room'
WHERE vh.user_id = 121
ORDER BY vh.update_time DESC
LIMIT 10;
-- 3. 检查该用户的点赞数据
SELECT lrl.*, lr.title as room_title
FROM eb_live_room_like lrl
LEFT JOIN eb_live_room lr ON lrl.room_id = lr.id
WHERE lrl.user_id = 121
ORDER BY lrl.create_time DESC
LIMIT 10;
-- 4. 检查该用户的关注数据
SELECT fr.*, u.nickname as followed_nickname
FROM eb_follow_record fr
LEFT JOIN eb_user u ON fr.followed_id = u.uid
WHERE fr.follower_id = 121
ORDER BY fr.create_time DESC
LIMIT 10;
-- 5. 汇总
SELECT
'道玄(uid=121)' as ,
(SELECT COUNT(*) FROM eb_view_history WHERE user_id = 121) as ,
(SELECT COUNT(*) FROM eb_live_room_like WHERE user_id = 121) as ,
(SELECT COUNT(*) FROM eb_follow_record WHERE follower_id = 121) as ;

50
deploy_ban_system.bat Normal file
View File

@ -0,0 +1,50 @@
@echo off
chcp 65001 >nul
echo ========================================
echo 部署封禁系统功能
echo ========================================
echo.
echo 步骤1: 编译后端项目...
cd Zhibo\zhibo-h
call mvn clean package -DskipTests -pl crmeb-front,crmeb-admin -am
if %ERRORLEVEL% NEQ 0 (
echo [错误] 编译失败!
pause
exit /b 1
)
echo.
echo [成功] 编译完成!
echo.
echo 步骤2: 上传JAR包到服务器...
scp crmeb-front\target\crmeb-front.jar root@1.15.149.240:/www/wwwroot/zhibo/
scp crmeb-admin\target\crmeb-admin.jar root@1.15.149.240:/www/wwwroot/zhibo/
if %ERRORLEVEL% NEQ 0 (
echo [错误] 上传失败!
pause
exit /b 1
)
echo.
echo [成功] 上传完成!
echo.
echo 步骤3: 重启后端服务...
ssh root@1.15.149.240 "cd /www/wwwroot/zhibo && ./restart.sh"
echo.
echo ========================================
echo 后端部署完成!
echo ========================================
echo.
echo 请执行以下操作:
echo 1. 在MySQL中执行 ban_system_tables.sql 创建数据库表
echo 2. 在MySQL中执行 add_ban_menus.sql 添加菜单
echo 3. 重新编译并部署管理后台前端
echo 4. 重新编译Android App
echo.
pause

64
deploy_complete.bat Normal file
View File

@ -0,0 +1,64 @@
@echo off
chcp 65001
echo ========================================
echo 完整部署 - 用户活动记录功能
echo ========================================
cd /d %~dp0
echo.
echo [1/4] 编译后端项目...
cd Zhibo\zhibo-h
call mvn clean package -DskipTests -q
if %errorlevel% neq 0 (
echo 后端编译失败!请检查代码错误。
pause
exit /b 1
)
echo 后端编译成功!
echo.
echo [2/4] 编译前端管理端...
cd ..\admin
call npm install --silent 2>nul
call npm run build
if %errorlevel% neq 0 (
echo 前端编译警告,可能需要手动检查。
)
echo 前端编译完成!
cd ..\..
echo.
echo [3/4] 文件位置
echo ========================================
echo 后端JAR: Zhibo\zhibo-h\crmeb-admin\target\crmeb-admin-*.jar
echo 前端dist: Zhibo\admin\dist\
echo ========================================
echo.
echo [4/4] 部署说明
echo ========================================
echo.
echo 管理端API端点:
echo - 关注记录: GET /api/admin/user/activity/follow?userId=xxx
echo - 点赞记录: GET /api/admin/user/activity/like?userId=xxx
echo - 查看历史: GET /api/admin/user/activity/view?userId=xxx
echo - 收藏记录: GET /api/admin/user/activity/collect?userId=xxx
echo.
echo 移动端API端点:
echo - 记录观看: POST /api/front/activity/view/record
echo - 观看历史: GET /api/front/activity/view/history
echo - 点赞记录: GET /api/front/activity/like/records
echo - 关注记录: GET /api/front/activity/follow/records
echo - 收藏记录: GET /api/front/activity/collect/works
echo.
echo 部署步骤:
echo 1. 停止后端服务
echo 2. 上传新的JAR文件到服务器
echo 3. 重启后端服务
echo 4. 上传前端dist目录到服务器
echo 5. 刷新浏览器缓存 (Ctrl+F5)
echo ========================================
pause

49
deploy_my_records_fix.bat Normal file
View File

@ -0,0 +1,49 @@
@echo off
chcp 65001 >nul
echo ========================================
echo 部署"我的记录"功能修复
echo ========================================
echo.
echo 步骤1: 编译后端项目...
cd Zhibo\zhibo-h
call mvn clean package -DskipTests -pl crmeb-front -am
if %ERRORLEVEL% NEQ 0 (
echo [错误] 编译失败!
pause
exit /b 1
)
echo.
echo [成功] 编译完成!
echo.
echo 步骤2: 上传JAR包到服务器...
scp crmeb-front\target\crmeb-front.jar root@1.15.149.240:/www/wwwroot/zhibo/
if %ERRORLEVEL% NEQ 0 (
echo [错误] 上传失败!
pause
exit /b 1
)
echo.
echo [成功] 上传完成!
echo.
echo 步骤3: 重启后端服务...
ssh root@1.15.149.240 "cd /www/wwwroot/zhibo && ./restart.sh"
echo.
echo ========================================
echo 部署完成!
echo ========================================
echo.
echo 请执行以下操作验证:
echo 1. 在MySQL中执行 complete_user_activity_data.sql 添加测试数据
echo 2. 在App上退出登录重新登录
echo 3. 进入"我的记录"页面查看数据
echo 4. 查看服务器日志: tail -f /www/wwwroot/zhibo/logs/crmeb-front.log ^| grep "API"
echo.
pause

52
deploy_records_debug.bat Normal file
View File

@ -0,0 +1,52 @@
@echo off
chcp 65001 >nul
echo ========================================
echo 部署"我的记录"调试版本
echo ========================================
echo.
echo [1/3] 编译后端项目...
cd /d "%~dp0Zhibo\zhibo-h"
call mvn clean package -DskipTests -pl crmeb-front -am
if errorlevel 1 (
echo 后端编译失败!
pause
exit /b 1
)
echo.
echo [2/3] 重启后端服务...
echo 请手动重启后端服务,或使用以下命令:
echo java -jar crmeb-front/target/crmeb-front.jar
echo.
echo [3/3] 编译Android应用...
cd /d "%~dp0android-app"
call gradlew assembleDebug
if errorlevel 1 (
echo Android编译失败
pause
exit /b 1
)
echo.
echo ========================================
echo 部署完成!
echo ========================================
echo.
echo 调试步骤:
echo 1. 重启后端服务
echo 2. 安装新的APK到设备/模拟器
echo 3. 登录应用
echo 4. 进入"我的记录"页面
echo 5. 查看Logcat日志过滤TAG: MyRecords, ApiClient, FrontTokenInterceptor
echo 6. 查看后端日志(搜索关键字: [FrontTokenInterceptor], [FrontTokenComponent], 【观看历史】)
echo.
echo 预期日志输出:
echo - Android端: "Token状态: 存在, 长度=32, 前20字符=xxx..."
echo - Android端: "添加Token到请求头: xxx..."
echo - 后端: "[FrontTokenInterceptor] URI=api/front/activity/view/history, rawToken=xxx..."
echo - 后端: "[FrontTokenComponent] getUserId: rawToken=xxx..., userId=121"
echo - 后端: "【观看历史】返回结果: userId=121, total=18, listSize=18"
echo.
pause

54
deploy_user_activity.bat Normal file
View File

@ -0,0 +1,54 @@
@echo off
chcp 65001
echo ========================================
echo 部署用户活动记录功能
echo ========================================
cd /d %~dp0
echo.
echo [1/4] 编译后端项目...
cd Zhibo\zhibo-h
call mvn clean package -DskipTests -q
if %errorlevel% neq 0 (
echo 编译失败!请检查代码错误。
pause
exit /b 1
)
echo 后端编译成功!
echo.
echo [2/4] 编译前端项目...
cd ..\admin
call npm run build
if %errorlevel% neq 0 (
echo 前端编译失败!
echo 如果不需要重新编译前端,可以忽略此错误。
)
echo 前端编译完成!
cd ..\..
echo.
echo [3/4] 部署文件位置
echo ========================================
echo 后端JAR: Zhibo\zhibo-h\crmeb-admin\target\crmeb-admin-*.jar
echo 前端dist: Zhibo\admin\dist\
echo ========================================
echo.
echo [4/4] 部署说明
echo ========================================
echo 1. 停止当前运行的后端服务
echo 2. 将新的JAR文件上传到服务器
echo 3. 重启后端服务
echo 4. 如果前端有更新将dist目录内容上传到服务器
echo.
echo API端点:
echo - 关注记录: GET /api/admin/user/activity/follow
echo - 点赞记录: GET /api/admin/user/activity/like
echo - 查看历史: GET /api/admin/user/activity/view
echo - 收藏记录: GET /api/admin/user/activity/collect
echo ========================================
pause

View File

@ -0,0 +1,30 @@
@echo off
echo ========================================
echo 部署观看历史修复
echo ========================================
cd Zhibo\zhibo-h
echo.
echo 1. 编译项目...
call mvn clean package -DskipTests -pl crmeb-front -am
if %ERRORLEVEL% NEQ 0 (
echo 编译失败!
pause
exit /b 1
)
echo.
echo 2. 上传到服务器...
scp crmeb-front\target\crmeb-front.jar root@1.15.149.240:/www/wwwroot/zhibo/
echo.
echo 3. 重启服务...
ssh root@1.15.149.240 "cd /www/wwwroot/zhibo && ./restart.sh"
echo.
echo ========================================
echo 部署完成!请查看服务器日志确认
echo ========================================
pause

86
diagnose_my_records.sql Normal file
View File

@ -0,0 +1,86 @@
-- =====================================================
-- 诊断"我的记录"不显示数据问题
-- =====================================================
-- 1. 查看所有用户,找到"道玄"用户的真实ID
SELECT id, uid, nickname, phone, avatar, create_time
FROM eb_user
WHERE nickname LIKE '%道玄%'
OR uid = 24187196
OR id = 121
ORDER BY id DESC;
-- 2. 检查 eb_view_history 表是否存在
SHOW TABLES LIKE 'eb_view_history';
-- 3. 查看表结构
DESC eb_view_history;
-- 4. 查看所有观看历史数据
SELECT * FROM eb_view_history ORDER BY update_time DESC LIMIT 30;
-- 5. 按用户分组统计观看历史
SELECT user_id, COUNT(*) as count
FROM eb_view_history
GROUP BY user_id
ORDER BY count DESC;
-- 6. 查看直播间列表
SELECT id, title, streamer_id, is_live, cover_image
FROM eb_live_room
ORDER BY id
LIMIT 10;
-- =====================================================
-- 为用户121道玄添加测试数据
-- =====================================================
-- 先检查用户121是否存在
SELECT id, nickname FROM eb_user WHERE id = 121;
-- 删除旧的测试数据(可选)
-- DELETE FROM eb_view_history WHERE user_id = 121;
-- 插入观看历史测试数据
INSERT INTO eb_view_history (user_id, target_type, target_id, target_title, view_duration, create_time, update_time)
SELECT 121, 'room', id, title, FLOOR(RAND() * 3600) + 300,
NOW() - INTERVAL FLOOR(RAND() * 24) HOUR,
NOW() - INTERVAL FLOOR(RAND() * 24) HOUR
FROM eb_live_room
WHERE id <= 10
ON DUPLICATE KEY UPDATE
view_duration = view_duration + VALUES(view_duration),
update_time = NOW();
-- 验证插入结果
SELECT '=== 用户121的观看历史 ===' as info;
SELECT * FROM eb_view_history WHERE user_id = 121 ORDER BY update_time DESC;
-- =====================================================
-- 检查点赞记录
-- =====================================================
SELECT '=== 用户121的直播间点赞 ===' as info;
SELECT * FROM eb_live_room_like WHERE user_id = 121;
-- 如果没有点赞记录,添加一些
INSERT IGNORE INTO eb_live_room_like (user_id, room_id, create_time)
SELECT 121, id, NOW() - INTERVAL FLOOR(RAND() * 48) HOUR
FROM eb_live_room
WHERE id <= 5;
SELECT * FROM eb_live_room_like WHERE user_id = 121;
-- =====================================================
-- 检查关注记录
-- =====================================================
SELECT '=== 用户121的关注记录 ===' as info;
SELECT * FROM eb_follow_record WHERE follower_id = 121;
-- =====================================================
-- 汇总统计
-- =====================================================
SELECT '=== 用户121数据汇总 ===' as info;
SELECT
(SELECT COUNT(*) FROM eb_view_history WHERE user_id = 121) as ,
(SELECT COUNT(*) FROM eb_live_room_like WHERE user_id = 121) as ,
(SELECT COUNT(*) FROM eb_follow_record WHERE follower_id = 121) as ;

View File

@ -0,0 +1,19 @@
-- =====================================================
-- 修复封禁菜单的component路径
-- =====================================================
-- 更新用户封禁菜单的component路径
UPDATE eb_system_menu
SET component = 'ban/userBan'
WHERE `name` = '用户封禁';
-- 更新房间封禁菜单的component路径
UPDATE eb_system_menu
SET component = 'ban/roomBan'
WHERE `name` = '房间封禁';
-- 验证更新结果
SELECT id, pid, `name`, perms, component, sort, is_show
FROM eb_system_menu
WHERE `name` LIKE '%封禁%'
ORDER BY sort DESC;

View File

@ -0,0 +1,28 @@
-- =====================================================
-- 修复封禁菜单的component路径 (v2)
-- 参考blacklist的配置格式
-- =====================================================
-- 更新用户封禁菜单的component路径
UPDATE eb_system_menu
SET component = '/userManage/ban/userBan'
WHERE `name` = '用户封禁';
-- 更新房间封禁菜单的component路径
UPDATE eb_system_menu
SET component = '/userManage/ban/roomBan'
WHERE `name` = '房间封禁';
-- 验证更新结果
SELECT '=== 封禁菜单配置 ===' as info;
SELECT id, pid, `name`, perms, component, sort, is_show
FROM eb_system_menu
WHERE `name` LIKE '%封禁%'
ORDER BY sort DESC;
-- 对比blacklist菜单配置
SELECT '=== 黑名单菜单配置(参考) ===' as info;
SELECT id, pid, `name`, perms, component, sort, is_show
FROM eb_system_menu
WHERE `name` LIKE '%拉黑%'
ORDER BY sort DESC;

View File

@ -0,0 +1,59 @@
-- =====================================================
-- 修复拉黑菜单 - 最终版
-- =====================================================
-- 1. 查看表结构
DESC eb_system_menu;
-- 2. 查看社交互动菜单
SELECT id, pid, `name`, component, menu_type, sort, is_show FROM eb_system_menu WHERE `name` = '社交互动';
-- 3. 查看群组管理作为参考(它能正常显示)
SELECT * FROM eb_system_menu WHERE `name` = '群组管理';
-- 4. 查看现有的拉黑菜单
SELECT * FROM eb_system_menu WHERE `name` IN ('用户拉黑', '房间拉黑');
-- 5. 删除旧的拉黑菜单记录
DELETE FROM eb_system_menu WHERE `name` = '用户拉黑';
DELETE FROM eb_system_menu WHERE `name` = '房间拉黑';
-- 6. 获取社交互动的ID
SET @social_id = (SELECT id FROM eb_system_menu WHERE `name` = '社交互动' AND menu_type = 'M' LIMIT 1);
SELECT @social_id as '社交互动ID';
-- 7. 获取群组管理的sort值在其后面添加
SET @group_sort = (SELECT sort FROM eb_system_menu WHERE `name` = '群组管理' LIMIT 1);
SELECT @group_sort as '群组管理排序';
-- 8. 插入用户拉黑菜单(参考群组管理的格式)
INSERT INTO eb_system_menu (`pid`, `name`, `icon`, `perms`, `component`, `menu_type`, `sort`, `is_show`)
VALUES (@social_id, '用户拉黑', '', 'admin:blacklist:user:list', '/socialManage/blacklist/user', 'C', IFNULL(@group_sort, 0) + 1, 1);
SET @user_blacklist_id = LAST_INSERT_ID();
SELECT @user_blacklist_id as '用户拉黑菜单ID';
-- 9. 插入房间拉黑菜单
INSERT INTO eb_system_menu (`pid`, `name`, `icon`, `perms`, `component`, `menu_type`, `sort`, `is_show`)
VALUES (@social_id, '房间拉黑', '', 'admin:blacklist:room:list', '/socialManage/blacklist/room', 'C', IFNULL(@group_sort, 0) + 2, 1);
SET @room_blacklist_id = LAST_INSERT_ID();
SELECT @room_blacklist_id as '房间拉黑菜单ID';
-- 10. 为管理员角色添加权限rid=1是超级管理员
INSERT IGNORE INTO eb_system_role_menu (rid, menu_id) VALUES (1, @user_blacklist_id);
INSERT IGNORE INTO eb_system_role_menu (rid, menu_id) VALUES (1, @room_blacklist_id);
-- 11. 验证结果
SELECT '=== 社交互动下所有子菜单 ===' as info;
SELECT id, pid, `name`, component, sort, is_show, menu_type
FROM eb_system_menu
WHERE pid = @social_id
ORDER BY sort;
-- 12. 验证权限
SELECT '=== 拉黑菜单权限 ===' as info;
SELECT rm.rid, rm.menu_id, m.`name`, m.component
FROM eb_system_role_menu rm
JOIN eb_system_menu m ON rm.menu_id = m.id
WHERE m.`name` IN ('用户拉黑', '房间拉黑');

17
fix_data_and_tables.sql Normal file
View File

@ -0,0 +1,17 @@
-- =====================================================
-- 修复数据和表结构,确保与后端服务一致
-- =====================================================
-- 1. 检查观看历史表名
SHOW TABLES LIKE '%history%';
SHOW TABLES LIKE '%view%';
-- 2. 检查 eb_works_relation 的 type 字段类型
DESCRIBE eb_works_relation;
-- 3. 检查 eb_follow_record 的 follow_status 字段
SELECT DISTINCT follow_status FROM eb_follow_record LIMIT 10;
-- 4. 检查直播间表名
SHOW TABLES LIKE '%room%';
SHOW TABLES LIKE '%live%';

49
fix_records_v2.sql Normal file
View File

@ -0,0 +1,49 @@
-- =====================================================
-- 修复"我的记录"不显示数据问题 - V2
-- =====================================================
-- 1. 先查看 eb_user 表结构,确认列名
DESC eb_user;
-- 2. 查询用户(使用 uid 作为主键)
SELECT uid, nickname, phone, avatar FROM eb_user WHERE nickname LIKE '%道玄%';
-- 3. 检查观看历史表是否存在
SHOW TABLES LIKE 'eb_view_history';
-- 4. 如果表不存在,创建它
CREATE TABLE IF NOT EXISTS `eb_view_history` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`user_id` int(11) NOT NULL COMMENT '用户ID',
`target_type` varchar(20) NOT NULL COMMENT '目标类型room-直播间, work-作品, profile-用户主页',
`target_id` varchar(50) NOT NULL COMMENT '目标ID',
`target_title` varchar(255) DEFAULT NULL COMMENT '目标标题',
`view_duration` int(11) DEFAULT 0 COMMENT '观看时长(秒)',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '查看时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_target` (`target_type`, `target_id`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='查看历史记录表';
-- 5. 查看现有观看历史数据
SELECT * FROM eb_view_history ORDER BY update_time DESC LIMIT 20;
-- 6. 查看直播间列表
SELECT id, title, streamer_id FROM eb_live_room LIMIT 10;
-- =====================================================
-- 为用户121添加测试数据假设道玄的uid是121
-- 如果不是121请替换为实际的uid值
-- =====================================================
-- 插入观看历史
INSERT INTO eb_view_history (user_id, target_type, target_id, target_title, view_duration, create_time, update_time)
VALUES
(121, 'room', '1', '欢乐游戏直播', 1800, NOW(), NOW()),
(121, 'room', '2', '音乐分享会', 2400, NOW(), NOW()),
(121, 'room', '8', '火影忍者', 3600, NOW(), NOW());
-- 验证插入结果
SELECT * FROM eb_view_history WHERE user_id = 121;

View File

@ -0,0 +1,82 @@
-- =====================================================
-- 修复"我的记录"不显示数据问题
-- =====================================================
-- 1. 首先检查当前用户的数据
-- 从截图看用户ID显示为 24187196但这可能是显示ID实际数据库ID可能不同
-- 先查询用户表确认
-- 查看所有用户
SELECT id, uid, nickname, phone FROM eb_user ORDER BY id DESC LIMIT 20;
-- 2. 检查 eb_view_history 表是否存在
SHOW TABLES LIKE 'eb_view_history';
-- 3. 检查表结构
DESC eb_view_history;
-- 4. 查看现有的观看历史数据
SELECT * FROM eb_view_history ORDER BY id DESC LIMIT 20;
-- 5. 查看直播间列表(用于添加测试数据)
SELECT id, title, streamer_id, is_live FROM eb_live_room LIMIT 10;
-- =====================================================
-- 为用户添加测试观看历史数据
-- 请根据实际用户ID修改 @USER_ID 的值
-- =====================================================
-- 设置用户ID请根据实际情况修改
-- 如果用户昵称是"道玄"先查询其真实ID
SELECT id, uid, nickname FROM eb_user WHERE nickname LIKE '%道玄%' OR uid = 24187196;
-- 假设用户ID是121道玄添加观看历史
SET @USER_ID = 121;
-- 先删除旧数据(可选)
-- DELETE FROM eb_view_history WHERE user_id = @USER_ID;
-- 插入观看历史测试数据
INSERT INTO eb_view_history (user_id, target_type, target_id, target_title, view_duration, create_time, update_time) VALUES
(@USER_ID, 'room', '1', '欢乐游戏直播', 1800, NOW() - INTERVAL 1 HOUR, NOW() - INTERVAL 1 HOUR),
(@USER_ID, 'room', '2', '音乐分享会', 2400, NOW() - INTERVAL 2 HOUR, NOW() - INTERVAL 2 HOUR),
(@USER_ID, 'room', '3', '户外探险', 1200, NOW() - INTERVAL 3 HOUR, NOW() - INTERVAL 3 HOUR),
(@USER_ID, 'room', '8', '火影忍者', 3600, NOW() - INTERVAL 30 MINUTE, NOW() - INTERVAL 30 MINUTE)
ON DUPLICATE KEY UPDATE
target_title = VALUES(target_title),
view_duration = view_duration + VALUES(view_duration),
update_time = NOW();
-- 验证插入结果
SELECT * FROM eb_view_history WHERE user_id = @USER_ID ORDER BY update_time DESC;
-- =====================================================
-- 同时检查点赞记录表
-- =====================================================
-- 检查直播间点赞表
SELECT * FROM eb_live_room_like WHERE user_id = @USER_ID;
-- 检查作品点赞表(如果存在)
SELECT * FROM eb_work_like WHERE user_id = @USER_ID;
-- 检查关注记录
SELECT * FROM eb_follow_record WHERE follower_id = @USER_ID;
-- =====================================================
-- 如果表不存在,创建它
-- =====================================================
CREATE TABLE IF NOT EXISTS `eb_view_history` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`user_id` int(11) NOT NULL COMMENT '用户ID',
`target_type` varchar(20) NOT NULL COMMENT '目标类型room-直播间, work-作品, profile-用户主页',
`target_id` varchar(50) NOT NULL COMMENT '目标ID',
`target_title` varchar(255) DEFAULT NULL COMMENT '目标标题',
`view_duration` int(11) DEFAULT 0 COMMENT '观看时长(秒)',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '查看时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_target` (`target_type`, `target_id`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='查看历史记录表';

View File

@ -0,0 +1,54 @@
-- =====================================================
-- 为用户"道玄"ID: 121添加测试数据 - 完整修正版
-- =====================================================
-- =====================================================
-- 1. 搜索历史 (eb_search_history) - 使用 user_id
-- =====================================================
INSERT IGNORE INTO eb_search_history (user_id, keyword, search_type, search_count, create_time) VALUES
(121, '游戏直播', 1, 3, '2026-01-03 20:00:00'),
(121, '音乐', 1, 2, '2026-01-03 19:00:00'),
(121, '美食教程', 2, 1, '2026-01-02 11:00:00'),
(121, '旅行Vlog', 2, 2, '2026-01-02 10:00:00'),
(121, '编程学习', 1, 4, '2026-01-01 13:00:00'),
(121, '户外运动', 1, 1, '2026-01-01 09:00:00');
-- =====================================================
-- 2. 好友关系 (eb_friend) - 使用 uid 和 friend_uid
-- =====================================================
INSERT IGNORE INTO eb_friend (uid, friend_uid, remark, user_id, create_time) VALUES
(121, 100, '游戏主播', 121, '2026-01-03 21:35:00'),
(121, 101, '音乐达人', 121, '2026-01-03 16:10:00'),
(100, 121, '道玄', 100, '2026-01-03 21:35:00'),
(101, 121, '道玄', 101, '2026-01-03 16:10:00');
-- =====================================================
-- 3. 更新用户余额和积分
-- =====================================================
UPDATE eb_user SET
now_money = 500.00,
integral = 1200,
experience = 350
WHERE uid = 121;
-- =====================================================
-- 4. 验证所有数据
-- =====================================================
SELECT '直播间点赞' as , COUNT(*) as FROM eb_live_room_like WHERE user_id = 121
UNION ALL
SELECT '作品点赞', COUNT(*) FROM eb_works_relation WHERE uid = 121 AND type = 1
UNION ALL
SELECT '作品收藏', COUNT(*) FROM eb_works_relation WHERE uid = 121 AND type = 2
UNION ALL
SELECT '关注数', COUNT(*) FROM eb_follow_record WHERE follower_id = 121
UNION ALL
SELECT '粉丝数', COUNT(*) FROM eb_follow_record WHERE followed_id = 121
UNION ALL
SELECT '搜索历史', COUNT(*) FROM eb_search_history WHERE user_id = 121
UNION ALL
SELECT '好友数', COUNT(*) FROM eb_friend WHERE uid = 121;
SELECT uid, nickname, now_money as , integral as , experience as
FROM eb_user WHERE uid = 121;
SELECT '=== 数据插入完成!===' as ;

73
insert_data_121_final.sql Normal file
View File

@ -0,0 +1,73 @@
-- =====================================================
-- 为用户"道玄"ID: 121添加测试数据 - 最终版
-- =====================================================
-- =====================================================
-- 1. 关注记录 (eb_follow_record) - 使用 follower_id 和 followed_id
-- =====================================================
-- 道玄关注其他用户 (follower_id=121)
INSERT IGNORE INTO eb_follow_record (follower_id, follower_nickname, followed_id, followed_nickname, follow_status, create_time) VALUES
(121, '道玄', 100, '测试用户100', '关注', '2026-01-03 20:00:00'),
(121, '道玄', 101, '测试用户101', '关注', '2026-01-03 15:00:00'),
(121, '道玄', 102, '测试用户102', '关注', '2026-01-02 18:00:00'),
(121, '道玄', 103, '测试用户103', '关注', '2026-01-02 12:00:00'),
(121, '道玄', 104, '测试用户104', '关注', '2026-01-01 20:00:00'),
(121, '道玄', 105, '测试用户105', '关注', '2026-01-01 10:00:00');
-- 其他用户关注道玄 (followed_id=121)
INSERT IGNORE INTO eb_follow_record (follower_id, follower_nickname, followed_id, followed_nickname, follow_status, create_time) VALUES
(100, '测试用户100', 121, '道玄', '关注', '2026-01-03 21:00:00'),
(101, '测试用户101', 121, '道玄', '关注', '2026-01-03 16:00:00'),
(106, '测试用户106', 121, '道玄', '关注', '2026-01-02 20:00:00'),
(107, '测试用户107', 121, '道玄', '关注', '2026-01-02 14:00:00');
-- =====================================================
-- 2. 搜索历史 (eb_search_history)
-- =====================================================
INSERT IGNORE INTO eb_search_history (uid, keyword, search_type, search_count, create_time) VALUES
(121, '游戏直播', 1, 3, '2026-01-03 20:00:00'),
(121, '音乐', 1, 2, '2026-01-03 19:00:00'),
(121, '美食教程', 2, 1, '2026-01-02 11:00:00'),
(121, '旅行Vlog', 2, 2, '2026-01-02 10:00:00'),
(121, '编程学习', 1, 4, '2026-01-01 13:00:00'),
(121, '户外运动', 1, 1, '2026-01-01 09:00:00');
-- =====================================================
-- 3. 好友关系 (eb_friend)
-- =====================================================
INSERT IGNORE INTO eb_friend (uid, friend_uid, remark, create_time) VALUES
(121, 100, '游戏主播', '2026-01-03 21:35:00'),
(121, 101, '音乐达人', '2026-01-03 16:10:00'),
(100, 121, '道玄', '2026-01-03 21:35:00'),
(101, 121, '道玄', '2026-01-03 16:10:00');
-- =====================================================
-- 4. 更新用户余额和积分
-- =====================================================
UPDATE eb_user SET
now_money = 500.00,
integral = 1200,
experience = 350
WHERE uid = 121;
-- =====================================================
-- 5. 验证数据
-- =====================================================
SELECT '直播间点赞' as , COUNT(*) as FROM eb_live_room_like WHERE user_id = 121
UNION ALL
SELECT '作品点赞', COUNT(*) FROM eb_works_relation WHERE uid = 121 AND type = 1
UNION ALL
SELECT '作品收藏', COUNT(*) FROM eb_works_relation WHERE uid = 121 AND type = 2
UNION ALL
SELECT '关注数', COUNT(*) FROM eb_follow_record WHERE follower_id = 121
UNION ALL
SELECT '粉丝数', COUNT(*) FROM eb_follow_record WHERE followed_id = 121
UNION ALL
SELECT '搜索历史', COUNT(*) FROM eb_search_history WHERE uid = 121
UNION ALL
SELECT '好友数', COUNT(*) FROM eb_friend WHERE uid = 121;
SELECT uid, nickname, now_money as , integral as , experience as
FROM eb_user WHERE uid = 121;
SELECT '数据插入完成!' as ;

100
insert_data_121_fixed.sql Normal file
View File

@ -0,0 +1,100 @@
-- =====================================================
-- 为用户"道玄"ID: 121添加测试数据 - 修正版
-- =====================================================
-- =====================================================
-- 1. 直播间点赞 (eb_live_room_like) - 使用 user_id 字段
-- =====================================================
INSERT IGNORE INTO eb_live_room_like (user_id, room_id, user_nickname, like_count, create_time) VALUES
(121, 1, '道玄', 15, '2026-01-03 20:35:00'),
(121, 2, '道玄', 28, '2026-01-03 21:15:00'),
(121, 3, '道玄', 8, '2026-01-02 19:10:00'),
(121, 4, '道玄', 42, '2026-01-02 12:30:00'),
(121, 5, '道玄', 20, '2026-01-01 14:30:00');
-- =====================================================
-- 2. 作品关系 (eb_works_relation) - 点赞和收藏
-- =====================================================
-- 点赞作品 (type=1)
INSERT IGNORE INTO eb_works_relation (uid, works_id, type, create_time) VALUES
(121, 1, 1, '2026-01-03 22:05:00'),
(121, 2, 1, '2026-01-03 18:10:00'),
(121, 3, 1, '2026-01-02 16:15:00'),
(121, 4, 1, '2026-01-01 20:00:00'),
(121, 5, 1, '2026-01-01 15:00:00');
-- 收藏作品 (type=2)
INSERT IGNORE INTO eb_works_relation (uid, works_id, type, create_time) VALUES
(121, 1, 2, '2026-01-03 22:06:00'),
(121, 2, 2, '2026-01-03 18:12:00'),
(121, 6, 2, '2026-01-02 10:00:00'),
(121, 7, 2, '2026-01-01 11:00:00');
-- =====================================================
-- 3. 关注记录 (eb_follow_record)
-- =====================================================
INSERT IGNORE INTO eb_follow_record (uid, follow_uid, create_time) VALUES
(121, 100, '2026-01-03 20:00:00'),
(121, 101, '2026-01-03 15:00:00'),
(121, 102, '2026-01-02 18:00:00'),
(121, 103, '2026-01-02 12:00:00'),
(121, 104, '2026-01-01 20:00:00'),
(121, 105, '2026-01-01 10:00:00');
-- 被关注记录
INSERT IGNORE INTO eb_follow_record (uid, follow_uid, create_time) VALUES
(100, 121, '2026-01-03 21:00:00'),
(101, 121, '2026-01-03 16:00:00'),
(106, 121, '2026-01-02 20:00:00'),
(107, 121, '2026-01-02 14:00:00');
-- =====================================================
-- 4. 搜索历史 (eb_search_history)
-- =====================================================
INSERT IGNORE INTO eb_search_history (uid, keyword, search_type, search_count, create_time) VALUES
(121, '游戏直播', 1, 3, '2026-01-03 20:00:00'),
(121, '音乐', 1, 2, '2026-01-03 19:00:00'),
(121, '美食教程', 2, 1, '2026-01-02 11:00:00'),
(121, '旅行Vlog', 2, 2, '2026-01-02 10:00:00'),
(121, '编程学习', 1, 4, '2026-01-01 13:00:00'),
(121, '户外运动', 1, 1, '2026-01-01 09:00:00');
-- =====================================================
-- 5. 好友关系 (eb_friend)
-- =====================================================
INSERT IGNORE INTO eb_friend (uid, friend_uid, remark, create_time) VALUES
(121, 100, '游戏主播', '2026-01-03 21:35:00'),
(121, 101, '音乐达人', '2026-01-03 16:10:00'),
(100, 121, '道玄', '2026-01-03 21:35:00'),
(101, 121, '道玄', '2026-01-03 16:10:00');
-- =====================================================
-- 6. 更新用户余额和积分
-- =====================================================
UPDATE eb_user SET
now_money = 500.00,
integral = 1200,
experience = 350
WHERE uid = 121;
-- =====================================================
-- 7. 验证数据
-- =====================================================
SELECT '直播间点赞' as , COUNT(*) as FROM eb_live_room_like WHERE user_id = 121
UNION ALL
SELECT '作品点赞', COUNT(*) FROM eb_works_relation WHERE uid = 121 AND type = 1
UNION ALL
SELECT '作品收藏', COUNT(*) FROM eb_works_relation WHERE uid = 121 AND type = 2
UNION ALL
SELECT '关注数', COUNT(*) FROM eb_follow_record WHERE uid = 121
UNION ALL
SELECT '粉丝数', COUNT(*) FROM eb_follow_record WHERE follow_uid = 121
UNION ALL
SELECT '搜索历史', COUNT(*) FROM eb_search_history WHERE uid = 121
UNION ALL
SELECT '好友数', COUNT(*) FROM eb_friend WHERE uid = 121;
SELECT uid, nickname, now_money as , integral as , experience as
FROM eb_user WHERE uid = 121;
SELECT '数据插入完成!' as ;

6
insert_data_121_v2.sql Normal file
View File

@ -0,0 +1,6 @@
-- =====================================================
-- 为用户"道玄"ID: 121添加测试数据 - 第二版
-- 先查看 eb_follow_record 表结构
-- =====================================================
DESCRIBE eb_follow_record;

3
insert_data_121_v3.sql Normal file
View File

@ -0,0 +1,3 @@
-- 查看 eb_search_history 和 eb_friend 表结构
DESCRIBE eb_search_history;
DESCRIBE eb_friend;

View File

@ -0,0 +1,17 @@
-- =====================================================
-- 创建测试直播间和作品数据
-- =====================================================
-- 1. 插入测试直播间 (eb_live_room)
INSERT IGNORE INTO eb_live_room (id, uid, title, streamer_name, stream_key, is_live, cover_image, description, create_time) VALUES
(1, 100, '欢乐游戏直播', '游戏达人', 'stream_key_1', 1, 'https://example.com/room1.jpg', '欢迎来到游戏直播间', '2026-01-01 10:00:00'),
(2, 101, '音乐分享会', '音乐主播', 'stream_key_2', 1, 'https://example.com/room2.jpg', '分享好听的音乐', '2026-01-01 11:00:00'),
(3, 102, '户外探险直播', '探险家', 'stream_key_3', 0, 'https://example.com/room3.jpg', '带你看世界', '2026-01-01 12:00:00'),
(4, 103, '美食制作教程', '美食达人', 'stream_key_4', 1, 'https://example.com/room4.jpg', '教你做美食', '2026-01-01 13:00:00'),
(5, 104, '编程技术分享', '程序员', 'stream_key_5', 0, 'https://example.com/room5.jpg', '分享编程技术', '2026-01-01 14:00:00');
-- 2. 查看 eb_works 表结构
DESCRIBE eb_works;
-- 3. 检查插入的直播间
SELECT id, title, streamer_name, is_live FROM eb_live_room WHERE id IN (1,2,3,4,5);

22
insert_test_works.sql Normal file
View File

@ -0,0 +1,22 @@
-- =====================================================
-- 创建测试作品数据
-- =====================================================
-- 先查看 eb_works 表完整结构(需要知道 user_id 字段)
SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'zhibo' AND TABLE_NAME = 'eb_works'
ORDER BY ORDINAL_POSITION;
-- 插入测试作品
INSERT IGNORE INTO eb_works (id, user_id, title, description, cover_image, like_count, collect_count, view_count, comment_count, is_deleted, create_time) VALUES
(1, 100, '搞笑短视频合集', '超级搞笑的视频合集', 'https://example.com/work1.jpg', 100, 50, 1000, 20, 0, '2026-01-01 10:00:00'),
(2, 101, '旅行Vlog-云南之旅', '云南旅行记录', 'https://example.com/work2.jpg', 200, 80, 2000, 30, 0, '2026-01-01 11:00:00'),
(3, 102, '美妆教程分享', '日常妆容教程', 'https://example.com/work3.jpg', 150, 60, 1500, 25, 0, '2026-01-01 12:00:00'),
(4, 103, '健身打卡日记', '每日健身记录', 'https://example.com/work4.jpg', 80, 30, 800, 15, 0, '2026-01-01 13:00:00'),
(5, 104, '美食探店', '探索城市美食', 'https://example.com/work5.jpg', 120, 45, 1200, 18, 0, '2026-01-01 14:00:00'),
(6, 105, '宠物日常', '可爱宠物的日常', 'https://example.com/work6.jpg', 300, 100, 3000, 50, 0, '2026-01-01 15:00:00'),
(7, 100, '游戏精彩时刻', '游戏高光集锦', 'https://example.com/work7.jpg', 180, 70, 1800, 28, 0, '2026-01-01 16:00:00');
-- 验证插入的作品
SELECT id, title, user_id, like_count, collect_count FROM eb_works WHERE id IN (1,2,3,4,5,6,7);

View File

@ -0,0 +1,46 @@
@echo off
chcp 65001
echo ========================================
echo 重新编译并重启后端服务
echo ========================================
cd /d E:\zhibo_1.0\6.0\zhibo\Zhibo\zhibo-h
echo.
echo [1/3] 停止现有后端进程...
for /f "tokens=5" %%a in ('netstat -ano ^| findstr ":30001" ^| findstr "LISTENING"') do (
echo 正在停止进程 %%a
taskkill /F /PID %%a 2>nul
)
timeout /t 2 /nobreak >nul
echo.
echo [2/3] 重新编译项目...
call mvn clean package -DskipTests -q
if %errorlevel% neq 0 (
echo 编译失败!
pause
exit /b 1
)
echo 编译成功!
echo.
echo [3/3] 启动后端服务...
cd crmeb-admin
start "CRMEB Admin Backend" java -jar target/crmeb-admin.jar
echo 后端服务已启动请等待约30秒...
echo.
echo ========================================
echo 部署完成!
echo 后端地址: http://localhost:30001
echo 前端地址: http://localhost:9527
echo ========================================
echo.
echo 请在管理后台测试用户活动记录功能:
echo 1. 登录管理后台
echo 2. 进入 用户管理 -^> 用户列表
echo 3. 点击用户121道玄的详情
echo 4. 切换到"关注记录""点赞记录"等标签页
echo.
pause

28
rebuild_backend.bat Normal file
View File

@ -0,0 +1,28 @@
@echo off
chcp 65001
echo ========================================
echo 重新编译后端项目
echo ========================================
cd /d %~dp0
echo.
echo [1/2] 编译后端项目...
cd Zhibo\zhibo-h
call mvn clean package -DskipTests -q
if %errorlevel% neq 0 (
echo 编译失败!请检查代码错误。
pause
exit /b 1
)
echo 后端编译成功!
echo.
echo [2/2] JAR文件位置
echo ========================================
dir /b crmeb-admin\target\*.jar 2>nul
echo.
echo 请将JAR文件部署到服务器并重启后端服务
echo ========================================
pause

27
test_activity_api.bat Normal file
View File

@ -0,0 +1,27 @@
@echo off
echo ========================================
echo 测试用户活动记录API
echo ========================================
echo.
echo 1. 检查后端服务是否运行...
netstat -an | findstr "30001"
echo.
echo 2. 测试API需要登录token
echo 请在浏览器中登录管理后台然后在开发者工具中查看Network请求
echo 找到任意请求的 Authori-zation 头复制token值
echo.
echo 3. 手动测试命令替换YOUR_TOKEN为实际token:
echo curl -X GET "http://localhost:30001/api/admin/user/activity/follow?userId=121&page=1&limit=10" -H "Authori-zation: YOUR_TOKEN"
echo.
echo 4. 或者直接在管理后台测试:
echo - 打开 http://localhost:9527
echo - 登录后进入 用户管理 -^> 用户列表
echo - 点击用户121道玄的详情
echo - 切换到"关注记录""点赞记录"等标签页
echo.
pause

71
test_activity_api.md Normal file
View File

@ -0,0 +1,71 @@
# 用户活动记录API测试
## API路径
后端Controller路径: `api/admin/user/activity`
完整API端点:
- 关注记录: `GET /api/admin/user/activity/follow?userId=121&page=1&limit=10`
- 点赞记录: `GET /api/admin/user/activity/like?userId=121&page=1&limit=10`
- 查看历史: `GET /api/admin/user/activity/view?userId=121&page=1&limit=10`
- 收藏记录: `GET /api/admin/user/activity/collect?userId=121&page=1&limit=10`
## 测试步骤
### 1. 重新编译后端
```bash
cd zhibo/Zhibo/zhibo-h
mvn clean package -DskipTests
```
### 2. 重启后端服务
### 3. 使用浏览器开发者工具测试
打开管理后台按F12打开开发者工具切换到Network标签。
然后:
1. 进入用户管理页面
2. 点击用户121道玄的"详情"按钮
3. 切换到"关注记录"标签
4. 查看Network中的请求和响应
### 4. 使用curl测试需要先获取token
```bash
# 替换YOUR_TOKEN为实际的登录token
curl -X GET "http://localhost:30001/api/admin/user/activity/follow?userId=121&page=1&limit=10" \
-H "Authori-zation: YOUR_TOKEN"
```
## 预期响应格式
```json
{
"code": 200,
"message": "success",
"data": {
"list": [
{
"id": 1,
"followedId": 43,
"followedNickname": "测试用户",
"followedAvatar": "...",
"followStatus": "1",
"createTime": "2026-01-03 10:00:00"
}
],
"total": 6,
"page": 1,
"limit": 10,
"totalPage": 1
}
}
```
## 常见问题
1. **404错误**: 后端没有重新编译部署
2. **空数据**: 检查数据库中是否有数据
3. **401错误**: token无效或过期
4. **500错误**: 后端代码有bug查看后端日志

49
test_api_curl.sh Normal file
View File

@ -0,0 +1,49 @@
#!/bin/bash
# 测试观看历史API
SERVER="http://1.15.149.240:8080"
echo "=========================================="
echo "测试观看历史API"
echo "=========================================="
# 首先登录获取Token
echo ""
echo "1. 登录获取Token..."
LOGIN_RESPONSE=$(curl -s -X POST "$SERVER/api/front/login/mobile" \
-H "Content-Type: application/json" \
-d '{"phone":"17629942950","code":"123456"}')
echo "登录响应: $LOGIN_RESPONSE"
# 提取Token (需要根据实际响应格式调整)
TOKEN=$(echo $LOGIN_RESPONSE | grep -o '"token":"[^"]*"' | cut -d'"' -f4)
echo "Token: $TOKEN"
if [ -z "$TOKEN" ]; then
echo "登录失败无法获取Token"
exit 1
fi
echo ""
echo "2. 测试Token调试接口..."
curl -s -X GET "$SERVER/api/front/activity/debug/token" \
-H "Authori-zation: $TOKEN" \
-H "Content-Type: application/json" | python3 -m json.tool
echo ""
echo "3. 测试获取观看历史..."
curl -s -X GET "$SERVER/api/front/activity/view/history?page=1&pageSize=20" \
-H "Authori-zation: $TOKEN" \
-H "Content-Type: application/json" | python3 -m json.tool
echo ""
echo "4. 测试获取点赞记录..."
curl -s -X GET "$SERVER/api/front/activity/like/records?page=1&pageSize=20" \
-H "Authori-zation: $TOKEN" \
-H "Content-Type: application/json" | python3 -m json.tool
echo ""
echo "=========================================="
echo "测试完成"
echo "=========================================="

173
test_data_for_user_121.sql Normal file
View File

@ -0,0 +1,173 @@
-- =====================================================
-- 为用户"道玄"ID: 121添加测试数据
-- 执行时间: 2026-01-04
-- =====================================================
-- 先检查用户是否存在
SELECT uid, nickname, phone FROM eb_user WHERE uid = 121;
-- 检查现有的直播间
SELECT id, room_title, owner_nickname, status FROM eb_room LIMIT 10;
-- 检查现有的作品
SELECT id, title, user_id FROM eb_works LIMIT 10;
-- 检查现有的主播
SELECT id, uid, nickname FROM eb_streamer LIMIT 10;
-- =====================================================
-- 1. 观看历史记录 (eb_watch_history)
-- =====================================================
INSERT INTO eb_watch_history (uid, target_type, target_id, target_title, target_cover, duration, watch_time, create_time) VALUES
-- 观看直播间
(121, 'room', 1, '欢乐游戏直播', 'https://example.com/cover1.jpg', 1800, '2026-01-03 20:30:00', '2026-01-03 20:30:00'),
(121, 'room', 2, '音乐分享会', 'https://example.com/cover2.jpg', 2400, '2026-01-03 21:00:00', '2026-01-03 21:00:00'),
(121, 'room', 3, '户外探险直播', 'https://example.com/cover3.jpg', 900, '2026-01-02 19:00:00', '2026-01-02 19:00:00'),
(121, 'room', 4, '美食制作教程', 'https://example.com/cover4.jpg', 3600, '2026-01-02 12:00:00', '2026-01-02 12:00:00'),
(121, 'room', 5, '编程技术分享', 'https://example.com/cover5.jpg', 5400, '2026-01-01 14:00:00', '2026-01-01 14:00:00'),
-- 观看作品
(121, 'work', 1, '搞笑短视频合集', 'https://example.com/work1.jpg', 180, '2026-01-03 22:00:00', '2026-01-03 22:00:00'),
(121, 'work', 2, '旅行Vlog-云南之旅', 'https://example.com/work2.jpg', 600, '2026-01-03 18:00:00', '2026-01-03 18:00:00'),
(121, 'work', 3, '美妆教程分享', 'https://example.com/work3.jpg', 420, '2026-01-02 16:00:00', '2026-01-02 16:00:00');
-- =====================================================
-- 2. 直播间点赞记录 (eb_live_room_like)
-- =====================================================
INSERT INTO eb_live_room_like (uid, room_id, like_count, create_time) VALUES
(121, 1, 15, '2026-01-03 20:35:00'),
(121, 2, 28, '2026-01-03 21:15:00'),
(121, 3, 8, '2026-01-02 19:10:00'),
(121, 4, 42, '2026-01-02 12:30:00'),
(121, 5, 20, '2026-01-01 14:30:00');
-- =====================================================
-- 3. 作品关系记录 (eb_works_relation) - 点赞和收藏
-- =====================================================
-- 点赞作品 (type=1)
INSERT INTO eb_works_relation (uid, works_id, type, create_time) VALUES
(121, 1, 1, '2026-01-03 22:05:00'),
(121, 2, 1, '2026-01-03 18:10:00'),
(121, 3, 1, '2026-01-02 16:15:00'),
(121, 4, 1, '2026-01-01 20:00:00'),
(121, 5, 1, '2026-01-01 15:00:00');
-- 收藏作品 (type=2)
INSERT INTO eb_works_relation (uid, works_id, type, create_time) VALUES
(121, 1, 2, '2026-01-03 22:06:00'),
(121, 2, 2, '2026-01-03 18:12:00'),
(121, 6, 2, '2026-01-02 10:00:00'),
(121, 7, 2, '2026-01-01 11:00:00');
-- =====================================================
-- 4. 关注记录 (eb_follow_record)
-- =====================================================
INSERT INTO eb_follow_record (uid, follow_uid, create_time) VALUES
(121, 100, '2026-01-03 20:00:00'),
(121, 101, '2026-01-03 15:00:00'),
(121, 102, '2026-01-02 18:00:00'),
(121, 103, '2026-01-02 12:00:00'),
(121, 104, '2026-01-01 20:00:00'),
(121, 105, '2026-01-01 10:00:00');
-- 被关注记录(其他用户关注道玄)
INSERT INTO eb_follow_record (uid, follow_uid, create_time) VALUES
(100, 121, '2026-01-03 21:00:00'),
(101, 121, '2026-01-03 16:00:00'),
(106, 121, '2026-01-02 20:00:00'),
(107, 121, '2026-01-02 14:00:00');
-- =====================================================
-- 5. 搜索历史 (eb_search_history)
-- =====================================================
INSERT INTO eb_search_history (uid, keyword, search_type, search_count, create_time) VALUES
(121, '游戏直播', 1, 3, '2026-01-03 20:00:00'),
(121, '音乐', 1, 2, '2026-01-03 19:00:00'),
(121, '美食教程', 2, 1, '2026-01-02 11:00:00'),
(121, '旅行Vlog', 2, 2, '2026-01-02 10:00:00'),
(121, '编程学习', 1, 4, '2026-01-01 13:00:00'),
(121, '户外运动', 1, 1, '2026-01-01 09:00:00');
-- =====================================================
-- 6. 礼物打赏记录 (eb_gift_record)
-- =====================================================
INSERT INTO eb_gift_record (uid, streamer_id, room_id, gift_id, gift_name, gift_price, quantity, total_price, create_time) VALUES
(121, 1, 1, 1, '小红心', 1, 10, 10, '2026-01-03 20:40:00'),
(121, 1, 1, 2, '鲜花', 5, 5, 25, '2026-01-03 20:45:00'),
(121, 2, 2, 3, '棒棒糖', 10, 3, 30, '2026-01-03 21:20:00'),
(121, 3, 3, 4, '火箭', 100, 1, 100, '2026-01-02 19:15:00'),
(121, 4, 4, 1, '小红心', 1, 20, 20, '2026-01-02 12:35:00');
-- =====================================================
-- 7. 私聊会话记录 (eb_conversation)
-- =====================================================
INSERT INTO eb_conversation (user1_id, user2_id, last_message, last_message_time, user1_unread, user2_unread, create_time) VALUES
(121, 100, '你好,直播很精彩!', '2026-01-03 21:30:00', 0, 1, '2026-01-03 20:30:00'),
(121, 101, '什么时候再开播?', '2026-01-03 16:00:00', 1, 0, '2026-01-03 15:30:00'),
(102, 121, '感谢关注!', '2026-01-02 18:30:00', 0, 0, '2026-01-02 18:00:00');
-- =====================================================
-- 8. 私聊消息记录 (eb_private_message)
-- =====================================================
INSERT INTO eb_private_message (conversation_id, sender_id, receiver_id, content, msg_type, is_read, create_time) VALUES
-- 与用户100的对话
(1, 121, 100, '你好,直播很精彩!', 'text', 1, '2026-01-03 21:30:00'),
(1, 100, 121, '谢谢支持!', 'text', 1, '2026-01-03 21:31:00'),
-- 与用户101的对话
(2, 101, 121, '什么时候再开播?', 'text', 0, '2026-01-03 16:00:00'),
(2, 121, 101, '明天晚上8点', 'text', 1, '2026-01-03 16:05:00');
-- =====================================================
-- 9. 好友关系 (eb_friend)
-- =====================================================
INSERT INTO eb_friend (uid, friend_uid, remark, create_time) VALUES
(121, 100, '游戏主播', '2026-01-03 21:35:00'),
(121, 101, '音乐达人', '2026-01-03 16:10:00'),
(100, 121, '道玄', '2026-01-03 21:35:00'),
(101, 121, '道玄', '2026-01-03 16:10:00');
-- =====================================================
-- 10. 心愿记录 (eb_wish) - 许愿树功能
-- =====================================================
INSERT INTO eb_wish (uid, festival_id, content, background_id, like_count, comment_count, status, create_time) VALUES
(121, 1, '希望新的一年事业顺利,身体健康!', 1, 15, 3, 1, '2026-01-01 00:05:00'),
(121, 1, '愿家人平安幸福', 2, 8, 1, 1, '2026-01-01 00:10:00');
-- =====================================================
-- 11. 虚拟货币余额更新 (eb_user表)
-- =====================================================
UPDATE eb_user SET
now_money = 500.00,
integral = 1200,
experience = 350
WHERE uid = 121;
-- =====================================================
-- 12. 充值记录 (eb_recharge_order)
-- =====================================================
INSERT INTO eb_recharge_order (uid, order_no, price, give_price, pay_type, paid, pay_time, create_time) VALUES
(121, 'RC202601030001', 100.00, 10.00, 'alipay', 1, '2026-01-03 10:00:00', '2026-01-03 09:58:00'),
(121, 'RC202601020001', 50.00, 5.00, 'wechat', 1, '2026-01-02 15:00:00', '2026-01-02 14:55:00'),
(121, 'RC202601010001', 200.00, 30.00, 'alipay', 1, '2026-01-01 12:00:00', '2026-01-01 11:50:00');
-- =====================================================
-- 验证插入的数据
-- =====================================================
SELECT '观看历史' as , COUNT(*) as FROM eb_watch_history WHERE uid = 121
UNION ALL
SELECT '直播间点赞', COUNT(*) FROM eb_live_room_like WHERE uid = 121
UNION ALL
SELECT '作品点赞', COUNT(*) FROM eb_works_relation WHERE uid = 121 AND type = 1
UNION ALL
SELECT '作品收藏', COUNT(*) FROM eb_works_relation WHERE uid = 121 AND type = 2
UNION ALL
SELECT '关注数', COUNT(*) FROM eb_follow_record WHERE uid = 121
UNION ALL
SELECT '粉丝数', COUNT(*) FROM eb_follow_record WHERE follow_uid = 121
UNION ALL
SELECT '搜索历史', COUNT(*) FROM eb_search_history WHERE uid = 121
UNION ALL
SELECT '礼物打赏', COUNT(*) FROM eb_gift_record WHERE uid = 121
UNION ALL
SELECT '好友数', COUNT(*) FROM eb_friend WHERE uid = 121;
SELECT '数据插入完成!用户道玄(ID:121)的测试数据已添加。' as ;

33
test_view_history_api.bat Normal file
View File

@ -0,0 +1,33 @@
@echo off
echo ========================================
echo 测试观看历史API
echo ========================================
REM 请替换为实际的Token和服务器地址
set SERVER=http://1.15.149.240:8080
set TOKEN=你的Token
echo.
echo 1. 测试Token调试接口
curl -X GET "%SERVER%/api/front/activity/debug/token" -H "Authori-zation: %TOKEN%" -H "Content-Type: application/json"
echo.
echo.
echo 2. 测试获取观看历史
curl -X GET "%SERVER%/api/front/activity/view/history?page=1&pageSize=20" -H "Authori-zation: %TOKEN%" -H "Content-Type: application/json"
echo.
echo.
echo 3. 测试获取点赞记录
curl -X GET "%SERVER%/api/front/activity/like/records?page=1&pageSize=20" -H "Authori-zation: %TOKEN%" -H "Content-Type: application/json"
echo.
echo.
echo 4. 测试获取关注记录
curl -X GET "%SERVER%/api/front/activity/follow/records?page=1&pageSize=20" -H "Authori-zation: %TOKEN%" -H "Content-Type: application/json"
echo.
echo ========================================
echo 测试完成
echo ========================================
pause

248
user_activity_test_data.sql Normal file
View File

@ -0,0 +1,248 @@
-- ============================================
-- 用户活动记录测试数据
-- 基于已有用户 (uid: 100-109) 生成
-- ============================================
-- 先查看已有用户
SELECT uid, nickname, phone FROM eb_user WHERE uid BETWEEN 100 AND 120 LIMIT 20;
-- 先查看已有直播间
SELECT id, title, uid, streamer_name FROM eb_live_room LIMIT 10;
-- 先查看已有作品
SELECT id, title, user_id FROM eb_works LIMIT 10;
-- ============================================
-- 1. 观看历史测试数据 (eb_view_history)
-- ============================================
INSERT INTO `eb_view_history` (`user_id`, `target_type`, `target_id`, `target_title`, `view_duration`, `create_time`, `update_time`) VALUES
-- 用户100的观看历史
(100, 'room', '1', '欢乐直播间', 1800, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY)),
(100, 'room', '2', '音乐之夜', 3600, DATE_SUB(NOW(), INTERVAL 2 DAY), DATE_SUB(NOW(), INTERVAL 2 DAY)),
(100, 'room', '3', '游戏竞技场', 2400, DATE_SUB(NOW(), INTERVAL 3 DAY), DATE_SUB(NOW(), INTERVAL 3 DAY)),
(100, 'work', '1', '我的第一个作品', 120, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY)),
(100, 'work', '2', '精彩瞬间', 90, DATE_SUB(NOW(), INTERVAL 2 DAY), DATE_SUB(NOW(), INTERVAL 2 DAY)),
(100, 'profile', '101', '林国瑞', 60, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY)),
-- 用户101的观看历史
(101, 'room', '1', '欢乐直播间', 2700, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY)),
(101, 'room', '4', '美食探店', 1500, DATE_SUB(NOW(), INTERVAL 2 DAY), DATE_SUB(NOW(), INTERVAL 2 DAY)),
(101, 'work', '3', '旅行日记', 180, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY)),
(101, 'profile', '100', '张吉惟', 45, DATE_SUB(NOW(), INTERVAL 3 DAY), DATE_SUB(NOW(), INTERVAL 3 DAY)),
-- 用户102的观看历史
(102, 'room', '2', '音乐之夜', 4200, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY)),
(102, 'room', '5', '户外探险', 1800, DATE_SUB(NOW(), INTERVAL 4 DAY), DATE_SUB(NOW(), INTERVAL 4 DAY)),
(102, 'work', '1', '我的第一个作品', 150, DATE_SUB(NOW(), INTERVAL 2 DAY), DATE_SUB(NOW(), INTERVAL 2 DAY)),
-- 用户103的观看历史
(103, 'room', '1', '欢乐直播间', 900, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY)),
(103, 'room', '3', '游戏竞技场', 5400, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY)),
(103, 'work', '2', '精彩瞬间', 200, DATE_SUB(NOW(), INTERVAL 3 DAY), DATE_SUB(NOW(), INTERVAL 3 DAY)),
(103, 'profile', '102', '王小明', 30, DATE_SUB(NOW(), INTERVAL 5 DAY), DATE_SUB(NOW(), INTERVAL 5 DAY)),
-- 用户104的观看历史
(104, 'room', '2', '音乐之夜', 2100, DATE_SUB(NOW(), INTERVAL 2 DAY), DATE_SUB(NOW(), INTERVAL 2 DAY)),
(104, 'work', '4', '日常生活', 80, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY)),
-- 用户105的观看历史
(105, 'room', '1', '欢乐直播间', 3300, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY)),
(105, 'room', '4', '美食探店', 2400, DATE_SUB(NOW(), INTERVAL 3 DAY), DATE_SUB(NOW(), INTERVAL 3 DAY)),
(105, 'profile', '103', '李小红', 55, DATE_SUB(NOW(), INTERVAL 2 DAY), DATE_SUB(NOW(), INTERVAL 2 DAY))
ON DUPLICATE KEY UPDATE
view_duration = view_duration + VALUES(view_duration),
update_time = NOW();
-- ============================================
-- 2. 直播间点赞记录测试数据 (eb_live_room_like)
-- ============================================
INSERT INTO `eb_live_room_like` (`user_id`, `room_id`, `like_count`, `create_time`, `update_time`) VALUES
-- 用户100点赞的直播间
(100, 1, 15, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY)),
(100, 2, 8, DATE_SUB(NOW(), INTERVAL 2 DAY), DATE_SUB(NOW(), INTERVAL 2 DAY)),
(100, 3, 22, DATE_SUB(NOW(), INTERVAL 3 DAY), DATE_SUB(NOW(), INTERVAL 3 DAY)),
-- 用户101点赞的直播间
(101, 1, 30, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY)),
(101, 4, 12, DATE_SUB(NOW(), INTERVAL 2 DAY), DATE_SUB(NOW(), INTERVAL 2 DAY)),
-- 用户102点赞的直播间
(102, 2, 45, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY)),
(102, 5, 18, DATE_SUB(NOW(), INTERVAL 4 DAY), DATE_SUB(NOW(), INTERVAL 4 DAY)),
-- 用户103点赞的直播间
(103, 1, 5, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY)),
(103, 3, 88, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY)),
-- 用户104点赞的直播间
(104, 2, 25, DATE_SUB(NOW(), INTERVAL 2 DAY), DATE_SUB(NOW(), INTERVAL 2 DAY)),
(104, 1, 10, DATE_SUB(NOW(), INTERVAL 3 DAY), DATE_SUB(NOW(), INTERVAL 3 DAY)),
-- 用户105点赞的直播间
(105, 1, 50, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY)),
(105, 4, 33, DATE_SUB(NOW(), INTERVAL 3 DAY), DATE_SUB(NOW(), INTERVAL 3 DAY))
ON DUPLICATE KEY UPDATE
like_count = like_count + VALUES(like_count),
update_time = NOW();
-- ============================================
-- 3. 作品关系测试数据 (eb_works_relation) - 点赞和收藏
-- ============================================
INSERT INTO `eb_works_relation` (`uid`, `works_id`, `type`, `create_time`) VALUES
-- 用户100的点赞和收藏
(100, 1, 'like', DATE_SUB(NOW(), INTERVAL 1 DAY)),
(100, 2, 'like', DATE_SUB(NOW(), INTERVAL 2 DAY)),
(100, 3, 'like', DATE_SUB(NOW(), INTERVAL 3 DAY)),
(100, 1, 'collect', DATE_SUB(NOW(), INTERVAL 1 DAY)),
(100, 2, 'collect', DATE_SUB(NOW(), INTERVAL 2 DAY)),
-- 用户101的点赞和收藏
(101, 1, 'like', DATE_SUB(NOW(), INTERVAL 1 DAY)),
(101, 3, 'like', DATE_SUB(NOW(), INTERVAL 2 DAY)),
(101, 4, 'like', DATE_SUB(NOW(), INTERVAL 3 DAY)),
(101, 3, 'collect', DATE_SUB(NOW(), INTERVAL 1 DAY)),
-- 用户102的点赞和收藏
(102, 1, 'like', DATE_SUB(NOW(), INTERVAL 2 DAY)),
(102, 2, 'like', DATE_SUB(NOW(), INTERVAL 3 DAY)),
(102, 1, 'collect', DATE_SUB(NOW(), INTERVAL 2 DAY)),
(102, 4, 'collect', DATE_SUB(NOW(), INTERVAL 4 DAY)),
-- 用户103的点赞和收藏
(103, 2, 'like', DATE_SUB(NOW(), INTERVAL 1 DAY)),
(103, 5, 'like', DATE_SUB(NOW(), INTERVAL 2 DAY)),
(103, 2, 'collect', DATE_SUB(NOW(), INTERVAL 1 DAY)),
-- 用户104的点赞和收藏
(104, 1, 'like', DATE_SUB(NOW(), INTERVAL 1 DAY)),
(104, 4, 'like', DATE_SUB(NOW(), INTERVAL 2 DAY)),
(104, 5, 'like', DATE_SUB(NOW(), INTERVAL 3 DAY)),
(104, 4, 'collect', DATE_SUB(NOW(), INTERVAL 2 DAY)),
-- 用户105的点赞和收藏
(105, 2, 'like', DATE_SUB(NOW(), INTERVAL 1 DAY)),
(105, 3, 'like', DATE_SUB(NOW(), INTERVAL 2 DAY)),
(105, 3, 'collect', DATE_SUB(NOW(), INTERVAL 2 DAY)),
(105, 5, 'collect', DATE_SUB(NOW(), INTERVAL 3 DAY))
ON DUPLICATE KEY UPDATE create_time = VALUES(create_time);
-- ============================================
-- 4. 搜索历史测试数据 (eb_search_history)
-- ============================================
INSERT INTO `eb_search_history` (`user_id`, `keyword`, `search_type`, `search_count`, `is_deleted`, `create_time`, `update_time`) VALUES
-- 用户100的搜索历史
(100, '游戏直播', 2, 5, 0, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY)),
(100, '音乐', 0, 3, 0, DATE_SUB(NOW(), INTERVAL 2 DAY), DATE_SUB(NOW(), INTERVAL 2 DAY)),
(100, '美食', 3, 2, 0, DATE_SUB(NOW(), INTERVAL 3 DAY), DATE_SUB(NOW(), INTERVAL 3 DAY)),
(100, '林国瑞', 1, 1, 0, DATE_SUB(NOW(), INTERVAL 4 DAY), DATE_SUB(NOW(), INTERVAL 4 DAY)),
-- 用户101的搜索历史
(101, '户外', 2, 4, 0, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY)),
(101, '旅行', 3, 6, 0, DATE_SUB(NOW(), INTERVAL 2 DAY), DATE_SUB(NOW(), INTERVAL 2 DAY)),
(101, '张吉惟', 1, 2, 0, DATE_SUB(NOW(), INTERVAL 3 DAY), DATE_SUB(NOW(), INTERVAL 3 DAY)),
-- 用户102的搜索历史
(102, '音乐直播', 2, 8, 0, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY)),
(102, '唱歌', 0, 3, 0, DATE_SUB(NOW(), INTERVAL 2 DAY), DATE_SUB(NOW(), INTERVAL 2 DAY)),
-- 用户103的搜索历史
(103, '电竞', 2, 10, 0, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY)),
(103, 'LOL', 0, 5, 0, DATE_SUB(NOW(), INTERVAL 2 DAY), DATE_SUB(NOW(), INTERVAL 2 DAY)),
(103, '王者荣耀', 0, 7, 0, DATE_SUB(NOW(), INTERVAL 3 DAY), DATE_SUB(NOW(), INTERVAL 3 DAY)),
-- 用户104的搜索历史
(104, '美食探店', 2, 4, 0, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY)),
(104, '火锅', 3, 2, 0, DATE_SUB(NOW(), INTERVAL 2 DAY), DATE_SUB(NOW(), INTERVAL 2 DAY)),
-- 用户105的搜索历史
(105, '舞蹈', 3, 6, 0, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY)),
(105, '健身', 0, 3, 0, DATE_SUB(NOW(), INTERVAL 2 DAY), DATE_SUB(NOW(), INTERVAL 2 DAY))
ON DUPLICATE KEY UPDATE
search_count = search_count + 1,
update_time = NOW();
-- ============================================
-- 5. 热门搜索测试数据 (eb_hot_search)
-- ============================================
INSERT INTO `eb_hot_search` (`keyword`, `search_type`, `hot_score`, `search_count`, `sort_order`, `status`, `create_time`, `update_time`) VALUES
('游戏直播', 2, 1000, 500, 1, 1, NOW(), NOW()),
('音乐', 0, 850, 420, 2, 1, NOW(), NOW()),
('美食探店', 2, 780, 380, 3, 1, NOW(), NOW()),
('户外探险', 2, 650, 320, 4, 1, NOW(), NOW()),
('电竞比赛', 2, 600, 300, 5, 1, NOW(), NOW()),
('舞蹈', 3, 550, 280, 6, 1, NOW(), NOW()),
('旅行日记', 3, 500, 250, 7, 1, NOW(), NOW()),
('健身教程', 3, 450, 220, 8, 1, NOW(), NOW()),
('唱歌', 0, 400, 200, 9, 1, NOW(), NOW()),
('搞笑视频', 3, 380, 190, 10, 1, NOW(), NOW())
ON DUPLICATE KEY UPDATE
hot_score = VALUES(hot_score),
search_count = VALUES(search_count),
update_time = NOW();
-- ============================================
-- 6. 关注记录补充数据 (eb_follow_record)
-- 确保有足够的关注数据用于测试
-- ============================================
INSERT IGNORE INTO `eb_follow_record` (`follower_id`, `follower_nickname`, `followed_id`, `followed_nickname`, `follow_status`, `is_deleted`, `create_time`) VALUES
-- 用户100关注的人
(100, '吉惟同学', 101, '林国瑞', 1, 0, DATE_SUB(NOW(), INTERVAL 10 DAY)),
(100, '吉惟同学', 102, '王小明', 1, 0, DATE_SUB(NOW(), INTERVAL 8 DAY)),
(100, '吉惟同学', 103, '李小红', 1, 0, DATE_SUB(NOW(), INTERVAL 5 DAY)),
-- 用户101关注的人
(101, '林国瑞', 100, '吉惟同学', 1, 0, DATE_SUB(NOW(), INTERVAL 9 DAY)),
(101, '林国瑞', 104, '赵小刚', 1, 0, DATE_SUB(NOW(), INTERVAL 7 DAY)),
-- 用户102关注的人
(102, '王小明', 100, '吉惟同学', 1, 0, DATE_SUB(NOW(), INTERVAL 6 DAY)),
(102, '王小明', 101, '林国瑞', 1, 0, DATE_SUB(NOW(), INTERVAL 4 DAY)),
(102, '王小明', 105, '孙小美', 1, 0, DATE_SUB(NOW(), INTERVAL 3 DAY)),
-- 用户103关注的人
(103, '李小红', 100, '吉惟同学', 1, 0, DATE_SUB(NOW(), INTERVAL 5 DAY)),
(103, '李小红', 102, '王小明', 1, 0, DATE_SUB(NOW(), INTERVAL 2 DAY)),
-- 用户104关注的人
(104, '赵小刚', 101, '林国瑞', 1, 0, DATE_SUB(NOW(), INTERVAL 4 DAY)),
(104, '赵小刚', 103, '李小红', 1, 0, DATE_SUB(NOW(), INTERVAL 3 DAY)),
-- 用户105关注的人
(105, '孙小美', 100, '吉惟同学', 1, 0, DATE_SUB(NOW(), INTERVAL 3 DAY)),
(105, '孙小美', 101, '林国瑞', 1, 0, DATE_SUB(NOW(), INTERVAL 2 DAY)),
(105, '孙小美', 104, '赵小刚', 1, 0, DATE_SUB(NOW(), INTERVAL 1 DAY));
-- ============================================
-- 7. 验证数据插入结果
-- ============================================
SELECT '观看历史' as table_name, COUNT(*) as count FROM eb_view_history
UNION ALL
SELECT '直播间点赞' as table_name, COUNT(*) as count FROM eb_live_room_like
UNION ALL
SELECT '作品关系(点赞/收藏)' as table_name, COUNT(*) as count FROM eb_works_relation
UNION ALL
SELECT '搜索历史' as table_name, COUNT(*) as count FROM eb_search_history
UNION ALL
SELECT '热门搜索' as table_name, COUNT(*) as count FROM eb_hot_search
UNION ALL
SELECT '关注记录' as table_name, COUNT(*) as count FROM eb_follow_record;
-- ============================================
-- 8. 查看用户100的活动统计
-- ============================================
SELECT
'用户100活动统计' as title,
(SELECT COUNT(*) FROM eb_view_history WHERE user_id = 100) as view_count,
(SELECT COUNT(*) FROM eb_live_room_like WHERE user_id = 100) as liked_room_count,
(SELECT COUNT(*) FROM eb_works_relation WHERE uid = 100 AND type = 'like') as liked_work_count,
(SELECT COUNT(*) FROM eb_works_relation WHERE uid = 100 AND type = 'collect') as collected_work_count,
(SELECT COUNT(*) FROM eb_follow_record WHERE follower_id = 100 AND follow_status = 1) as following_count,
(SELECT COUNT(*) FROM eb_follow_record WHERE followed_id = 100 AND follow_status = 1) as follower_count,
(SELECT COUNT(*) FROM eb_search_history WHERE user_id = 100 AND is_deleted = 0) as search_history_count;
SELECT '测试数据插入完成!' as message;

47
verify_all_data_121.sql Normal file
View File

@ -0,0 +1,47 @@
-- =====================================================
-- 验证用户121的所有数据
-- =====================================================
-- 1. 关注记录
SELECT '=== 关注记录 ===' as ;
SELECT fr.id, fr.followed_id as followedId, fr.followed_nickname as followedNickname,
fr.follow_status as followStatus, fr.create_time as createTime
FROM eb_follow_record fr
WHERE fr.follower_id = 121
AND (fr.follow_status = 1 OR fr.follow_status = '关注')
ORDER BY fr.create_time DESC;
-- 2. 点赞记录 - 直播间
SELECT '=== 直播间点赞 ===' as ;
SELECT 'room' as targetType, CAST(rl.room_id AS CHAR) as targetId,
lr.title as targetTitle, rl.create_time as createTime
FROM eb_live_room_like rl
LEFT JOIN eb_live_room lr ON rl.room_id = lr.id
WHERE rl.user_id = 121;
-- 3. 点赞记录 - 作品
SELECT '=== 作品点赞 ===' as ;
SELECT 'work' as targetType, CAST(wr.works_id AS CHAR) as targetId,
w.title as targetTitle, wr.create_time as createTime
FROM eb_works_relation wr
LEFT JOIN eb_works w ON wr.works_id = w.id
WHERE wr.uid = 121 AND wr.type = 1;
-- 4. 收藏记录
SELECT '=== 收藏记录 ===' as ;
SELECT wr.works_id as workId, w.title, w.user_id as authorId,
u.nickname as authorName, w.like_count as likeCount,
w.collect_count as collectCount, wr.create_time as collectTime
FROM eb_works_relation wr
LEFT JOIN eb_works w ON wr.works_id = w.id
LEFT JOIN eb_user u ON w.user_id = u.uid
WHERE wr.uid = 121 AND wr.type = 2;
-- 5. 观看历史
SELECT '=== 观看历史 ===' as ;
SELECT target_type as targetType, target_id as targetId,
target_title as targetTitle, view_duration as viewDuration,
create_time as createTime
FROM eb_view_history
WHERE user_id = 121
ORDER BY update_time DESC;

70
verify_user_121_data.sql Normal file
View File

@ -0,0 +1,70 @@
-- 验证用户121的所有测试数据
-- ========================================
-- 1. 验证用户信息
SELECT '=== 用户信息 ===' as info;
SELECT uid, nickname, phone, avatar FROM eb_user WHERE uid = 121;
-- 2. 验证观看历史
SELECT '=== 观看历史 (eb_view_history) ===' as info;
SELECT COUNT(*) as total FROM eb_view_history WHERE user_id = 121;
SELECT id, user_id, target_type, target_id, target_title, view_duration, create_time
FROM eb_view_history WHERE user_id = 121 ORDER BY create_time DESC LIMIT 5;
-- 3. 验证直播间点赞
SELECT '=== 直播间点赞 (eb_live_room_like) ===' as info;
SELECT COUNT(*) as total FROM eb_live_room_like WHERE user_id = 121;
SELECT rl.id, rl.user_id, rl.room_id, lr.title as room_title, rl.create_time
FROM eb_live_room_like rl
LEFT JOIN eb_live_room lr ON rl.room_id = lr.id
WHERE rl.user_id = 121 ORDER BY rl.create_time DESC LIMIT 5;
-- 4. 验证作品点赞 (type=1)
SELECT '=== 作品点赞 (eb_works_relation type=1) ===' as info;
SELECT COUNT(*) as total FROM eb_works_relation WHERE uid = 121 AND type = 1;
SELECT wr.id, wr.uid, wr.works_id, w.title as work_title, wr.type, wr.create_time
FROM eb_works_relation wr
LEFT JOIN eb_works w ON wr.works_id = w.id
WHERE wr.uid = 121 AND wr.type = 1 ORDER BY wr.create_time DESC LIMIT 5;
-- 5. 验证作品收藏 (type=2)
SELECT '=== 作品收藏 (eb_works_relation type=2) ===' as info;
SELECT COUNT(*) as total FROM eb_works_relation WHERE uid = 121 AND type = 2;
SELECT wr.id, wr.uid, wr.works_id, w.title as work_title, wr.type, wr.create_time
FROM eb_works_relation wr
LEFT JOIN eb_works w ON wr.works_id = w.id
WHERE wr.uid = 121 AND wr.type = 2 ORDER BY wr.create_time DESC LIMIT 5;
-- 6. 验证关注记录
SELECT '=== 关注记录 (eb_follow_record) ===' as info;
SELECT COUNT(*) as total FROM eb_follow_record WHERE follower_id = 121 AND (follow_status = 1 OR follow_status = '关注');
SELECT fr.id, fr.follower_id, fr.followed_id, fr.followed_nickname, fr.follow_status, fr.create_time
FROM eb_follow_record fr
WHERE fr.follower_id = 121 AND (fr.follow_status = 1 OR fr.follow_status = '关注')
ORDER BY fr.create_time DESC LIMIT 5;
-- 7. 验证搜索历史
SELECT '=== 搜索历史 (eb_search_history) ===' as info;
SELECT COUNT(*) as total FROM eb_search_history WHERE user_id = 121 AND is_deleted = 0;
SELECT id, user_id, keyword, search_type, create_time
FROM eb_search_history WHERE user_id = 121 AND is_deleted = 0 ORDER BY create_time DESC LIMIT 5;
-- 8. 验证直播间数据
SELECT '=== 直播间 (eb_live_room) ===' as info;
SELECT COUNT(*) as total FROM eb_live_room;
SELECT id, title, streamer_name, is_live, like_count, view_count FROM eb_live_room LIMIT 5;
-- 9. 验证作品数据
SELECT '=== 作品 (eb_works) ===' as info;
SELECT COUNT(*) as total FROM eb_works;
SELECT id, title, user_id, like_count, collect_count, view_count FROM eb_works LIMIT 5;
-- 10. 汇总统计
SELECT '=== 数据汇总 ===' as info;
SELECT
(SELECT COUNT(*) FROM eb_view_history WHERE user_id = 121) as view_history_count,
(SELECT COUNT(*) FROM eb_live_room_like WHERE user_id = 121) as room_like_count,
(SELECT COUNT(*) FROM eb_works_relation WHERE uid = 121 AND type = 1) as work_like_count,
(SELECT COUNT(*) FROM eb_works_relation WHERE uid = 121 AND type = 2) as work_collect_count,
(SELECT COUNT(*) FROM eb_follow_record WHERE follower_id = 121 AND (follow_status = 1 OR follow_status = '关注')) as follow_count,
(SELECT COUNT(*) FROM eb_search_history WHERE user_id = 121 AND is_deleted = 0) as search_count;

146
封禁系统功能说明.md Normal file
View File

@ -0,0 +1,146 @@
# 封禁系统功能说明
## 功能概述
封禁系统提供用户封禁、房间封禁和黑名单管理功能,支持管理后台和移动端双端实时同步。
## 功能模块
### 1. 用户封禁(管理员功能)
管理员可以对违规用户进行封禁处理:
- **永久封禁**:用户账号永久禁用
- **临时封禁**:设置封禁天数,到期自动解除
- **解除封禁**:手动解除用户封禁状态
- **封禁原因**:记录封禁原因,便于追溯
**API接口**
- `GET /api/admin/ban/user/list` - 获取用户封禁列表
- `POST /api/admin/ban/user/add` - 封禁用户
- `POST /api/admin/ban/user/unban/{id}` - 解除封禁
- `POST /api/admin/ban/user/delete/{id}` - 删除记录
- `GET /api/admin/ban/user/check/{userId}` - 检查封禁状态
### 2. 房间封禁(管理员功能)
管理员可以对违规直播间进行封禁处理:
- **永久封禁**:直播间永久关闭
- **临时封禁**:设置封禁天数,到期自动解除
- **解除封禁**:手动解除房间封禁状态
- **封禁原因**:记录封禁原因
**API接口**
- `GET /api/admin/ban/room/list` - 获取房间封禁列表
- `POST /api/admin/ban/room/add` - 封禁房间
- `POST /api/admin/ban/room/unban/{id}` - 解除封禁
- `POST /api/admin/ban/room/delete/{id}` - 删除记录
- `GET /api/admin/ban/room/check/{roomId}` - 检查封禁状态
### 3. 用户黑名单(用户功能)
用户可以将其他用户加入黑名单:
- **添加黑名单**:屏蔽指定用户
- **移除黑名单**:解除屏蔽
- **黑名单列表**:查看已屏蔽的用户
- **互动限制**:被拉黑用户无法发送消息、评论等
**移动端API接口**
- `GET /api/front/ban/check/me` - 检查当前用户封禁状态
- `GET /api/front/ban/check/user/{userId}` - 检查指定用户封禁状态
- `GET /api/front/ban/check/room/{roomId}` - 检查房间封禁状态
- `GET /api/front/ban/blacklist/list` - 获取我的黑名单
- `POST /api/front/ban/blacklist/add` - 添加到黑名单
- `POST /api/front/ban/blacklist/remove` - 从黑名单移除
- `GET /api/front/ban/blacklist/check/{targetUserId}` - 检查黑名单状态
## 数据库表结构
### eb_user_ban用户封禁表
| 字段 | 类型 | 说明 |
|------|------|------|
| id | bigint | 主键 |
| user_id | int | 被封禁用户ID |
| ban_type | varchar(20) | 封禁类型permanent/temporary |
| reason | varchar(500) | 封禁原因 |
| duration_days | int | 封禁天数 |
| expire_time | datetime | 到期时间 |
| operator_id | int | 操作人ID |
| status | tinyint | 状态1-生效0-已解除 |
### eb_room_ban房间封禁表
| 字段 | 类型 | 说明 |
|------|------|------|
| id | bigint | 主键 |
| room_id | int | 被封禁房间ID |
| ban_type | varchar(20) | 封禁类型 |
| reason | varchar(500) | 封禁原因 |
| duration_days | int | 封禁天数 |
| expire_time | datetime | 到期时间 |
| operator_id | int | 操作人ID |
| status | tinyint | 状态 |
### eb_user_blacklist用户黑名单表
| 字段 | 类型 | 说明 |
|------|------|------|
| id | bigint | 主键 |
| user_id | int | 用户ID发起拉黑 |
| blocked_user_id | int | 被拉黑用户ID |
| blocker_nickname | varchar(100) | 拉黑者昵称 |
| blocked_nickname | varchar(100) | 被拉黑者昵称 |
| create_time | datetime | 创建时间 |
## 部署步骤
1. **执行数据库脚本**
```bash
mysql -u root -p zhibo < ban_system_tables.sql
mysql -u root -p zhibo < add_ban_menus.sql
```
2. **部署后端**
```bash
deploy_ban_system.bat
```
3. **部署管理后台前端**
- 确保 `Zhibo/admin/src/views/ban/userBan.vue``roomBan.vue` 已添加
- 确保 `Zhibo/admin/src/api/ban.js` 已添加
- 重新编译部署前端
4. **编译Android App**
- 确保 `BlacklistActivity.java` 已添加
- 确保布局文件已添加
- 重新编译APK
## 双端同步机制
1. **管理后台封禁用户** → 用户状态更新 → 移动端检测到封禁状态 → 显示封禁提示
2. **移动端添加黑名单** → 数据库更新 → 管理后台可查看黑名单记录
3. **管理后台解除封禁** → 用户状态恢复 → 移动端可正常使用
## 文件清单
### 后端文件
- `Zhibo/zhibo-h/crmeb-admin/src/main/java/com/zbkj/admin/controller/BanController.java`
- `Zhibo/zhibo-h/crmeb-front/src/main/java/com/zbkj/front/controller/BanFrontController.java`
- `Zhibo/zhibo-h/crmeb-admin/src/main/java/com/zbkj/admin/controller/BlacklistController.java`(已更新)
### 管理后台前端文件
- `Zhibo/admin/src/views/ban/userBan.vue`
- `Zhibo/admin/src/views/ban/roomBan.vue`
- `Zhibo/admin/src/api/ban.js`
- `Zhibo/admin/src/api/blacklist.js`
### Android文件
- `android-app/app/src/main/java/com/example/livestreaming/BlacklistActivity.java`
- `android-app/app/src/main/java/com/example/livestreaming/net/ApiService.java`(已更新)
- `android-app/app/src/main/res/layout/activity_blacklist.xml`
- `android-app/app/src/main/res/layout/item_blacklist.xml`
- `android-app/app/src/main/res/drawable/bg_button_outline_primary.xml`
### SQL脚本
- `ban_system_tables.sql` - 创建数据库表
- `add_ban_menus.sql` - 添加菜单配置

View File

@ -0,0 +1,130 @@
# 我的记录数据不显示问题排查
## 问题描述
- 管理端显示用户"道玄"(ID: 121)有完整的活动记录数据
- 移动端"我的记录"页面显示"暂无记录"
## 问题分析
### 数据流程
```
Android App → HTTP请求(带Token) → 后端拦截器验证Token → Controller获取userId → Service查询数据 → 返回结果
```
### 关键代码路径
1. **Android端Token发送**: `ApiClient.java` 中的 `auth` 拦截器
2. **后端Token验证**: `FrontTokenInterceptor.java``FrontTokenComponent.check()`
3. **获取用户ID**: `UserService.getUserId()``FrontTokenComponent.getUserId()`
4. **数据查询**: `UserActivityRecordService.getViewHistory()` 等方法
### 可能的问题原因
1. **Token未正确发送**
- `ApiClient.appContext` 为 null
- `AuthStore.getToken()` 返回 null
2. **Token在Redis中不存在或已过期**
- Token过期时间: 24小时 (`TOKEN_EXPRESS_MINUTES = 60 * 24`)
- Redis key格式: `TOKEN_USER:` + token
3. **Token格式不匹配**
- 后端期望的Header: `Authori-zation`(注意中间有连字符)
## 已添加的调试日志
### Android端 (Logcat TAG: MyRecords, ApiClient)
```
加载记录: type=view, page=1, userId=xxx
Token状态: 存在, 长度=32, 前20字符=xxx...
请求URL: http://xxx/api/front/activity/view/history
请求头Authori-zation: xxx
添加Token到请求头: xxx...
```
### 后端 (搜索关键字)
```
[FrontTokenInterceptor] URI=xxx, rawToken=xxx, processedToken=xxx
[FrontTokenInterceptor] Token验证结果: true/false
[FrontTokenComponent] getUserId: rawToken=xxx, processedToken=xxx, redisKey=xxx, exists=true/false, userId=xxx
【观看历史】请求头Token: xxx
【观看历史】请求参数: userId=xxx, targetType=xxx, page=1, pageSize=20
【观看历史】返回结果: userId=xxx, total=xxx, listSize=xxx
```
## 调试步骤
### 1. 检查Android端Token
```bash
# 在Logcat中过滤
adb logcat -s MyRecords ApiClient AuthStore
```
预期输出:
- Token状态: 存在
- 添加Token到请求头: xxx...
### 2. 检查后端Token验证
查看后端日志,搜索 `[FrontTokenInterceptor]`
预期输出:
- rawToken 不为 null
- Token验证结果: true
### 3. 检查Redis中的Token
```bash
# 连接Redis
redis-cli
# 查看Token是否存在
EXISTS TOKEN_USER:your_token_here
# 查看Token对应的userId
GET TOKEN_USER:your_token_here
```
### 4. 使用调试接口
Android端会自动调用 `api/front/activity/debug/token` 接口,查看返回结果:
```json
{
"header_Authori-zation": "xxx",
"header_Authorization": "xxx",
"userId": 121,
"isLoggedIn": true
}
```
## 解决方案
### 如果Token未发送
1. 确保登录成功后Token已保存到SharedPreferences
2. 检查 `AuthStore.setToken()` 是否被正确调用
### 如果Token已过期
1. 重新登录获取新Token
2. 或者在Redis中手动延长Token过期时间
### 如果Token格式不匹配
1. 确保Android端发送的Header名称是 `Authori-zation`
2. 确保Token值不包含前缀 `TOKEN_USER:`
## 修改的文件列表
1. `zhibo/android-app/app/src/main/java/com/example/livestreaming/net/ApiClient.java`
- 增强Token拦截器日志
2. `zhibo/android-app/app/src/main/java/com/example/livestreaming/MyRecordsActivity.java`
- 增加Token调试方法
- 增强请求日志
3. `zhibo/android-app/app/src/main/java/com/example/livestreaming/net/ApiService.java`
- 添加调试接口 `debugToken()`
4. `zhibo/Zhibo/zhibo-h/crmeb-front/src/main/java/com/zbkj/front/interceptor/FrontTokenInterceptor.java`
- 增强Token验证日志
5. `zhibo/Zhibo/zhibo-h/crmeb-common/src/main/java/com/zbkj/common/token/FrontTokenComponent.java`
- 增强 `getUserId()` 方法日志
6. `zhibo/Zhibo/zhibo-h/crmeb-front/src/main/java/com/zbkj/front/controller/UserActivityRecordController.java`
- 增强各接口的请求日志
- 添加调试接口 `/debug/token`

View File

@ -0,0 +1,79 @@
# 接口配置检查报告
## 一、后端接口配置
### 1. 前端 API (UserActivityRecordController)
路径前缀: `api/front/activity`
| 接口 | 方法 | 路径 | 状态 |
|------|------|------|------|
| 记录观看历史 | POST | /view/record | ✅ |
| 获取观看历史 | GET | /view/history | ✅ |
| 删除单条观看历史 | DELETE | /view/history/{historyId} | ✅ |
| 清空观看历史 | DELETE | /view/history | ✅ |
| 获取点赞记录 | GET | /like/records | ✅ |
| 获取点赞的直播间 | GET | /like/rooms | ✅ |
| 获取点赞的作品 | GET | /like/works | ✅ |
| 获取点赞的心愿 | GET | /like/wishes | ✅ |
| 获取关注记录 | GET | /follow/records | ✅ |
| 获取收藏的作品 | GET | /collect/works | ✅ |
| 获取用户活动统计 | GET | /stats | ✅ |
### 2. 管理端 API (UserActivityController)
路径前缀: `api/admin/user`
| 接口 | 方法 | 路径 | 状态 |
|------|------|------|------|
| 获取用户关注记录 | GET | /follow/records | ✅ |
| 获取用户点赞记录 | GET | /like/records | ✅ |
| 获取用户查看历史 | GET | /view/history | ✅ |
| 获取用户收藏的作品 | GET | /collect/works | ✅ |
| 获取用户活动统计 | GET | /activity/stats | ✅ |
## 二、Android 端接口配置 (ApiService.java)
| 接口 | 方法 | 路径 | 与后端匹配 |
|------|------|------|------|
| recordViewHistoryNew | POST | api/front/activity/view/record | ✅ |
| getViewHistory | GET | api/front/activity/view/history | ✅ |
| deleteViewHistory | DELETE | api/front/activity/view/history/{historyId} | ✅ |
| clearViewHistory | DELETE | api/front/activity/view/history | ✅ |
| getLikeRecords | GET | api/front/activity/like/records | ✅ |
| getLikedRoomsNew | GET | api/front/activity/like/rooms | ✅ |
| getLikedWorks | GET | api/front/activity/like/works | ✅ |
| getLikedWishes | GET | api/front/activity/like/wishes | ✅ |
| getFollowRecords | GET | api/front/activity/follow/records | ✅ |
| getCollectedWorks | GET | api/front/activity/collect/works | ✅ |
| getUserActivityStats | GET | api/front/activity/stats | ✅ |
## 三、管理端前端接口配置 (userActivity.js)
| 接口 | 方法 | 路径 | 与后端匹配 |
|------|------|------|------|
| getFollowRecords | GET | /admin/user/follow/records | ✅ |
| getLikeRecords | GET | /admin/user/like/records | ✅ |
| getViewHistory | GET | /admin/user/view/history | ✅ |
| getCollectedWorks | GET | /admin/user/collect/works | ✅ |
| getUserActivityStats | GET | /admin/user/activity/stats | ✅ |
> 注:管理端前端 baseURL 为 `/api`,所以实际请求路径为 `/api/admin/user/...`
## 四、数据库表配置
| 表名 | 用途 | 状态 |
|------|------|------|
| eb_view_history | 查看历史记录 | ✅ 已创建 |
| eb_live_room_like | 直播间点赞记录 | ✅ 已创建 |
| eb_wish_like | 心愿点赞记录 | ✅ 已创建 |
| eb_search_history | 搜索历史 | ✅ 已创建 |
| eb_hot_search | 热门搜索 | ✅ 已创建 |
| eb_works_relation | 作品关系(点赞、收藏) | ✅ 已创建 |
## 五、检查结论
✅ **所有接口配置正确,前后端路径匹配**
### 部署注意事项:
1. 后端需要重新编译部署
2. 管理端前端需要重新构建部署
3. Android 端需要重新编译 APK

View File

@ -0,0 +1,554 @@
# 直播平台功能清单报告
> 生成日期2026年1月4日
> 项目状态:开发中
> 整体完成度82%
---
## 项目概览
本项目是一个完整的直播社交平台,包含:
- **Android移动端**40+ Activity95+ 布局文件60+ API接口
- **Java后端**:完整的直播间、礼物、用户、社交等模块
- **Node.js直播服务**SRS服务器 + WebSocket实时通信
- **数据库**50+ 表,覆盖用户、直播、社交、交易等业务
---
# 一、Android App 功能
## 1. 用户系统
| 功能 | 状态 | 说明 |
|------|:----:|------|
| 手机号验证码登录 | ✅ | 已实现 |
| 账号密码登录 | ✅ | 已实现 |
| 用户注册 | ✅ | 手机号+验证码 |
| 个人中心 | ✅ | 头像、昵称、签名、性别、生日、地区 |
| 编辑个人资料 | ✅ | 已实现 |
| 查看他人主页 | ✅ | 显示关注/粉丝/获赞数 |
| 设置页面 | ✅ | 账号、隐私、通知、关于 |
| 第三方登录(微信/QQ | ⏳ | 待实现 |
| 忘记密码 | ⏳ | 待实现 |
**模块完成度90%**
---
## 2. 直播功能
| 功能 | 状态 | 说明 |
|------|:----:|------|
| 首页直播间列表 | ✅ | 瀑布流2列展示 |
| 分类标签筛选 | ✅ | 推荐/游戏/才艺/户外/音乐 |
| 顶部Tab切换 | ✅ | 关注/推荐/附近 |
| 直播间详情页 | ✅ | 双引擎播放ExoPlayer+IjkPlayer |
| 实时弹幕 | ✅ | WebSocket心跳检测、断线重连 |
| 礼物打赏 | ✅ | 礼物列表、发送、动画效果 |
| 搜索主播/直播间 | ✅ | 文字+语音搜索 |
| 手机开播RTMP推流 | ⚠️ | 前端已实现,后端接口待完善 |
| 直播间点赞 | ✅ | 无限次点赞,带动画 |
| 在线人数显示 | ✅ | 实时更新 |
| 下拉刷新/上拉加载 | ✅ | 已实现 |
| 全屏切换 | ✅ | 已实现 |
| 观众列表 | ⏳ | 待实现 |
| 直播间公告 | ⏳ | 待实现 |
| 直播间分享 | ⏳ | 待实现 |
**模块完成度85%**
---
## 3. 主播系统
| 功能 | 状态 | 说明 |
|------|:----:|------|
| 主播认证申请 | ✅ | 已实现 |
| 主播中心 | ✅ | 粉丝数、获赞数、收益统计 |
| 主播等级显示 | ✅ | 已实现 |
| 魅力值统计 | ✅ | 已实现 |
| 创建粉丝团 | ⏳ | 待实现 |
| 粉丝团成员管理 | ⏳ | 待实现 |
| 粉丝团等级 | ⏳ | 待实现 |
**模块完成度75%**
---
## 4. 社交功能
| 功能 | 状态 | 说明 |
|------|:----:|------|
| 关注/取消关注 | ✅ | 已实现 |
| 关注列表 | ✅ | 已实现 |
| 粉丝列表 | ✅ | 已实现 |
| 好友列表 | ✅ | 显示在线状态 |
| 添加好友 | ✅ | 好友请求处理(同意/拒绝) |
| 删除好友 | ✅ | 已实现 |
| 拉黑好友 | ✅ | 已实现 |
| 黑名单管理 | ✅ | 已实现 |
| 点赞的直播间列表 | ⏳ | 待实现 |
| 好友分组 | ⏳ | 待实现 |
| 好友备注 | ⏳ | 待实现 |
**模块完成度85%**
---
## 5. 消息系统
| 功能 | 状态 | 说明 |
|------|:----:|------|
| 消息/会话列表 | ✅ | 头像、昵称、最后消息、未读数 |
| 会话搜索 | ✅ | 已实现 |
| 私信聊天 | ✅ | 文本/图片/语音消息 |
| 消息已读状态 | ✅ | 已实现 |
| 会话删除 | ✅ | 已实现 |
| 系统通知 | ✅ | 已实现 |
| 通知设置 | ✅ | 已实现 |
| WebSocket实时推送 | ✅ | 已实现 |
| 删除单条消息 | ⏳ | 待实现 |
| 消息表情回应 | ⏳ | 待实现 |
| 视频消息 | ⏳ | 待实现 |
**模块完成度80%**
---
## 6. 通话功能
| 功能 | 状态 | 说明 |
|------|:----:|------|
| 语音通话 | ✅ | 已实现 |
| 视频通话 | ✅ | 已实现 |
| 来电界面 | ✅ | 已实现 |
| 通话记录 | ✅ | 已实现 |
| 通话管理 | ✅ | CallManager |
| WebRTC优化 | ⏳ | 待优化 |
**模块完成度90%**
---
## 7. 群组功能
| 功能 | 状态 | 说明 |
|------|:----:|------|
| 群组列表 | ✅ | 已实现 |
| 创建群组 | ✅ | 已实现 |
| 群组详情 | ✅ | 成员列表、群信息 |
| 群聊天 | ✅ | 已实现 |
| 添加/移除成员 | ✅ | 已实现 |
| 转让群主 | ✅ | 已实现 |
| 解散/退出群组 | ✅ | 已实现 |
| 群组信息编辑 | ✅ | 已实现 |
| 群组公告 | ⏳ | 待实现 |
| 群组权限管理 | ⏳ | 待实现 |
**模块完成度85%**
---
## 8. 钱包系统
| 功能 | 状态 | 说明 |
|------|:----:|------|
| 钱包余额显示 | ✅ | 金币余额 |
| 充值功能 | ✅ | 支付宝/微信支付 |
| 充值套餐 | ✅ | 已实现 |
| 创建充值订单 | ✅ | 已实现 |
| 交易/充值记录 | ✅ | 已实现 |
| 提现功能 | ⏳ | 待实现 |
| 收益明细 | ⏳ | 待完善 |
**模块完成度80%**
---
## 9. 娱乐功能
| 功能 | 状态 | 说明 |
|------|:----:|------|
| 缘池(鱼塘匹配) | ✅ | 轨道式用户展示6头像环绕动画 |
| 许愿树主页 | ✅ | 节日横幅、倒计时、祈愿值 |
| 发布心愿 | ✅ | 50字限制最多7个 |
| 查看/删除心愿 | ✅ | 已实现 |
| 愿望达成 | ✅ | 带庆祝动画 |
| 许愿树心愿列表 | ⏳ | 待实现 |
| 心愿点赞/评论 | ⏳ | 待实现 |
| 语音匹配 | ⚠️ | 页面已有,功能待完善 |
| 心动信号 | ⚠️ | 页面已有,功能待完善 |
| 在线交友 | ⚠️ | 页面已有,功能待完善 |
| 游戏大厅 | ⚠️ | KTV、你画我猜、桌游入口已有 |
| 动态社区 | ⏳ | 待实现 |
**模块完成度60%**
---
## 10. 作品系统
| 功能 | 状态 | 说明 |
|------|:----:|------|
| 发布作品 | ✅ | 图片/视频,标题、描述、分类、标签、位置 |
| 作品详情 | ✅ | 已实现 |
| 作品编辑 | ✅ | 已实现 |
| 作品删除 | ✅ | 已实现 |
| 作品点赞 | ✅ | 已实现 |
| 作品收藏 | ✅ | 已实现 |
| 观看历史 | ✅ | 已实现 |
| 用户作品列表 | ✅ | 已实现 |
| 收藏列表 | ✅ | 已实现 |
| 赞过列表 | ✅ | 已实现 |
| 作品评论 | ⏳ | 待实现 |
| 作品分享 | ⏳ | 待实现 |
**模块完成度85%**
---
## 11. 搜索模块
| 功能 | 状态 | 说明 |
|------|:----:|------|
| 文字搜索 | ✅ | 已实现 |
| 语音搜索 | ✅ | 已实现 |
| 搜索建议 | ✅ | 已实现 |
| 搜索历史 | ✅ | 已实现 |
| 热搜词显示 | ✅ | 已实现 |
| 搜索结果分类 | ✅ | 用户/房间/作品 |
| 综合搜索 | ⏳ | 待优化 |
**模块完成度90%**
---
# 二、管理后台功能
## 1. 用户管理
| 功能 | 状态 | 说明 |
|------|:----:|------|
| 用户列表 | ✅ | 查看、编辑、禁用 |
| 用户详情 | ✅ | 已实现 |
| 用户等级管理 | ✅ | 已实现 |
| 用户分组管理 | ✅ | 已实现 |
| 用户签到管理 | ✅ | 已实现 |
| 用户聊天记录 | ✅ | 已实现 |
| 用户认证审核 | ✅ | 已实现 |
| 用户黑名单 | ✅ | 已实现 |
**模块完成度95%**
---
## 2. 直播管理
| 功能 | 状态 | 说明 |
|------|:----:|------|
| 直播间列表 | ✅ | 查看、关闭、封禁 |
| 直播间详情 | ✅ | 已实现 |
| 直播间分类管理 | ✅ | 已实现 |
| 直播间背景管理 | ✅ | 已实现 |
| 直播状态管理 | ✅ | 已实现 |
| 直播间搜索 | ✅ | 已实现 |
| 开始/结束直播接口 | ⏳ | 后端待实现 |
| 实时监控 | ⏳ | 待完善 |
**模块完成度80%**
---
## 3. 主播管理
| 功能 | 状态 | 说明 |
|------|:----:|------|
| 主播列表 | ✅ | 审核、管理 |
| 主播详情 | ✅ | 已实现 |
| 主播认证审核 | ✅ | 已实现 |
| 主播等级管理 | ✅ | 已实现 |
| 主播家族管理 | ✅ | 已实现 |
| 主播统计数据 | ✅ | 粉丝数、魅力值 |
| 魅力等级配置 | ✅ | 已实现 |
**模块完成度90%**
---
## 4. 礼物系统
| 功能 | 状态 | 说明 |
|------|:----:|------|
| 礼物列表管理 | ✅ | 添加、编辑、上下架 |
| 礼物配置 | ✅ | 图标、价格、动画 |
| 礼物打赏记录 | ✅ | 已实现 |
| 充值套餐配置 | ✅ | 已实现 |
| 虚拟货币管理 | ✅ | 已实现 |
| 礼物排行榜 | ⏳ | 待实现 |
**模块完成度90%**
---
## 5. 财务管理
| 功能 | 状态 | 说明 |
|------|:----:|------|
| 充值订单管理 | ✅ | 已实现 |
| 交易记录 | ✅ | 已实现 |
| 收益明细 | ✅ | 礼物、钻石、金币 |
| 提现审核 | ⏳ | 待审核、已审核 |
| 佣金管理 | ⏳ | 待实现 |
| 财务统计报表 | ⏳ | 待完善 |
**模块完成度70%**
---
## 6. 社交管理
| 功能 | 状态 | 说明 |
|------|:----:|------|
| 好友关系管理 | ✅ | 已实现 |
| 关注记录管理 | ✅ | 已实现 |
| 群组管理 | ✅ | 已实现 |
| 群组成员管理 | ✅ | 已实现 |
| 粉丝团管理 | ⏳ | 待实现 |
**模块完成度85%**
---
## 7. 娱乐功能管理
| 功能 | 状态 | 说明 |
|------|:----:|------|
| 许愿树节日管理 | ✅ | 已实现 |
| 心愿管理 | ✅ | 发布、审核 |
| 心愿点赞管理 | ✅ | 已实现 |
| 心愿评论管理 | ✅ | 已实现 |
| 背景素材管理 | ✅ | 已实现 |
| 缘池话题管理 | ⏳ | 待实现 |
| 匹配配置 | ⏳ | 待实现 |
| CP配对管理 | ⏳ | 待实现 |
**模块完成度70%**
---
## 8. 系统设置
| 功能 | 状态 | 说明 |
|------|:----:|------|
| 管理员权限管理 | ✅ | 已实现 |
| 菜单管理 | ✅ | 已实现 |
| 系统消息管理 | ✅ | 已实现 |
| 短信配置 | ✅ | 已实现 |
| 版本管理 | ✅ | 已实现 |
| 黑名单管理 | ✅ | 已实现 |
| 验证码管理 | ✅ | 已实现 |
| 推送通知配置 | ⏳ | 待实现 |
**模块完成度90%**
---
## 9. 数据统计
| 功能 | 状态 | 说明 |
|------|:----:|------|
| 仪表盘概览 | ✅ | 已实现 |
| 用户统计 | ✅ | 活跃度、新增、留存 |
| 收益统计 | ✅ | 礼物收入、充值统计 |
| 直播间热度统计 | ✅ | 已实现 |
| 主播数据统计 | ✅ | 已实现 |
| 通话统计 | ⏳ | 待实现 |
**模块完成度85%**
---
# 三、技术架构
## 已实现的核心技术
### 1. 实时通信
- ✅ WebSocket弹幕系统心跳检测、断线重连
- ✅ WebSocket私聊消息推送
- ✅ WebSocket在线状态推送
- ✅ WebSocket在线人数推送
### 2. 多媒体支持
- ✅ 双引擎视频播放ExoPlayer + IjkPlayer
- ✅ HLS流支持
- ✅ FLV流支持
- ✅ RTMP推流
- ✅ 图片上传和加载
- ✅ 视频上传和处理
### 3. 支付系统
- ✅ 支付宝集成
- ✅ 微信支付集成
- ✅ 虚拟货币充值
- ✅ 交易记录管理
### 4. 动画效果
- ✅ 轨道式用户展示动画
- ✅ 呼吸脉冲动画
- ✅ 点赞缩放动画
- ✅ 庆祝动画
- ✅ 列表加载动画
- ✅ 礼物动画效果
---
# 四、API接口统计
| 分类 | 已实现 | 待实现 |
|------|:------:|:------:|
| 用户认证 | 5 | 2 |
| 直播间 | 10 | 3 |
| 礼物打赏 | 6 | 1 |
| 私聊会话 | 7 | 4 |
| 好友管理 | 9 | 2 |
| 关注功能 | 7 | 1 |
| 作品管理 | 12 | 2 |
| 文件上传 | 2 | 0 |
| 在线状态 | 4 | 1 |
| 离线消息 | 3 | 0 |
| 搜索功能 | 5 | 1 |
| 观看历史 | 1 | 0 |
| 分类管理 | 2 | 0 |
| **合计** | **73** | **17** |
**接口完成率81%**
---
# 五、数据库表结构
## 表分类统计
| 分类 | 表数量 | 说明 |
|------|:------:|------|
| 用户相关 | 10+ | eb_user, eb_user_level, eb_user_address 等 |
| 直播相关 | 8+ | eb_live_room, eb_live_category, eb_live_danmu 等 |
| 社交相关 | 8+ | eb_friend, eb_follow_record, eb_group 等 |
| 消息相关 | 5+ | eb_private_message, eb_conversation 等 |
| 礼物相关 | 5+ | eb_gift, eb_gift_record, eb_recharge_package 等 |
| 许愿树相关 | 5+ | eb_wishtree_festival, eb_wishtree_wish 等 |
| 作品相关 | 4+ | eb_works, eb_works_like, eb_works_collect 等 |
| 系统配置 | 10+ | eb_system_version, eb_lottery_config 等 |
| **合计** | **55+** | - |
---
# 六、完成度汇总
## Android App 模块
| 模块 | 完成度 |
|------|:------:|
| 用户系统 | 90% |
| 直播功能 | 85% |
| 主播系统 | 75% |
| 社交功能 | 85% |
| 消息系统 | 80% |
| 通话功能 | 90% |
| 群组功能 | 85% |
| 钱包系统 | 80% |
| 娱乐功能 | 60% |
| 作品系统 | 85% |
| 搜索模块 | 90% |
| **App整体** | **82%** |
## 管理后台模块
| 模块 | 完成度 |
|------|:------:|
| 用户管理 | 95% |
| 直播管理 | 80% |
| 主播管理 | 90% |
| 礼物系统 | 90% |
| 财务管理 | 70% |
| 社交管理 | 85% |
| 娱乐功能管理 | 70% |
| 系统设置 | 90% |
| 数据统计 | 85% |
| **后台整体** | **84%** |
## 项目总体完成度82%
---
# 七、待完成功能清单
## P0 - 紧急(影响核心功能)
| 序号 | 功能 | 模块 | 说明 |
|:----:|------|------|------|
| 1 | 开始/结束直播接口 | 直播管理 | 后端接口未实现 |
| 2 | 许愿树心愿列表页面 | 娱乐功能 | 查看所有用户心愿 |
| 3 | 点赞的直播间列表 | 社交功能 | 我的点赞页面 |
## P1 - 重要(影响用户体验)
| 序号 | 功能 | 模块 | 说明 |
|:----:|------|------|------|
| 1 | 粉丝团功能 | 主播系统 | 创建、管理、等级 |
| 2 | 提现功能 | 钱包系统 | 主播收益提现 |
| 3 | 推送通知 | 系统设置 | 消息推送集成 |
| 4 | 删除单条消息 | 消息系统 | 私聊消息删除 |
| 5 | 消息表情回应 | 消息系统 | 点赞、爱心等 |
| 6 | 观众列表 | 直播功能 | 直播间观众显示 |
| 7 | 直播间公告 | 直播功能 | 主播公告功能 |
| 8 | 群组公告 | 群组功能 | 群公告发布 |
## P2 - 一般(功能完善)
| 序号 | 功能 | 模块 | 说明 |
|:----:|------|------|------|
| 1 | 第三方登录 | 用户系统 | 微信/QQ登录 |
| 2 | 忘记密码 | 用户系统 | 密码找回 |
| 3 | 作品评论 | 作品系统 | 评论功能 |
| 4 | 作品分享 | 作品系统 | 分享到社交平台 |
| 5 | 好友分组 | 社交功能 | 好友分类管理 |
| 6 | 财务报表 | 财务管理 | 统计报表完善 |
| 7 | 通话统计 | 数据统计 | 通话数据分析 |
## P3 - 可选(优化提升)
| 序号 | 功能 | 模块 | 说明 |
|:----:|------|------|------|
| 1 | 语音匹配完善 | 娱乐功能 | 匹配算法优化 |
| 2 | 心动信号完善 | 娱乐功能 | 功能逻辑完善 |
| 3 | 游戏大厅完善 | 娱乐功能 | 各游戏功能实现 |
| 4 | 礼物排行榜 | 礼物系统 | 排行榜展示 |
| 5 | 推荐算法优化 | 直播功能 | 个性化推荐 |
| 6 | WebRTC优化 | 通话功能 | 通话质量提升 |
---
# 八、项目优势
1. **完整的直播生态**:从推流、播放、互动到社交,功能齐全
2. **丰富的社交功能**:好友、群组、关注、许愿树等多维度社交
3. **实时通信能力**WebSocket实现真正的实时互动
4. **完善的支付系统**:支持多种支付方式和虚拟货币
5. **优秀的用户体验**:动画效果、防抖处理、缓存优先
6. **可扩展的架构**:模块化设计,易于扩展新功能
---
# 九、建议优先级
1. **立即完成**:后端开始/结束直播接口(影响开播功能)
2. **本周完成**:许愿树心愿列表、点赞列表页面
3. **下周完成**:粉丝团功能、提现功能
4. **持续优化**:推送通知、娱乐功能完善
---
> 报告生成工具Kiro AI
> 最后更新2026年1月4日