粉丝团,封禁完善
This commit is contained in:
parent
fd27958b9d
commit
37a4184f55
|
|
@ -13,6 +13,14 @@ export function fanGroupListApi(params) {
|
|||
})
|
||||
}
|
||||
|
||||
// 粉丝团详情
|
||||
export function fanGroupDetailApi(id) {
|
||||
return request({
|
||||
url: `/admin/fan/group/detail/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 删除粉丝团
|
||||
export function fanGroupDeleteApi(id) {
|
||||
return request({
|
||||
|
|
@ -30,6 +38,24 @@ export function fanGroupBatchDeleteApi(ids) {
|
|||
})
|
||||
}
|
||||
|
||||
// 修改粉丝团状态
|
||||
export function fanGroupStatusApi(id, status) {
|
||||
return request({
|
||||
url: `/admin/fan/group/status/${id}`,
|
||||
method: 'post',
|
||||
data: { status }
|
||||
})
|
||||
}
|
||||
|
||||
// 修改粉丝团信息
|
||||
export function fanGroupUpdateApi(id, data) {
|
||||
return request({
|
||||
url: `/admin/fan/group/update/${id}`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 粉丝团成员列表
|
||||
export function fanGroupMemberListApi(params) {
|
||||
return request({
|
||||
|
|
@ -47,6 +73,15 @@ export function fanGroupMemberDeleteApi(id) {
|
|||
})
|
||||
}
|
||||
|
||||
// 修改成员等级
|
||||
export function fanGroupMemberUpdateLevelApi(id, level) {
|
||||
return request({
|
||||
url: `/admin/fan/group/member/update-level/${id}`,
|
||||
method: 'post',
|
||||
data: { level }
|
||||
})
|
||||
}
|
||||
|
||||
// 粉丝团聊天记录列表
|
||||
export function fanGroupMessageListApi(params) {
|
||||
return request({
|
||||
|
|
@ -63,3 +98,11 @@ export function fanGroupMessageDeleteApi(id) {
|
|||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
// 粉丝团统计
|
||||
export function fanGroupStatisticsApi() {
|
||||
return request({
|
||||
url: '/admin/fan/group/statistics',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ service.interceptors.response.use(
|
|||
type: 'error',
|
||||
duration: 5 * 1000,
|
||||
});
|
||||
return Promise.reject();
|
||||
return Promise.reject(res || { message: 'Error' });
|
||||
} else {
|
||||
return res.data;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,11 +55,14 @@
|
|||
<span>{{ scope.row.message_count || 0 }} 条</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="250" align="center" fixed="right">
|
||||
<el-table-column label="操作" width="320" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<div style="display: flex; gap: 8px; justify-content: center;">
|
||||
<el-button type="primary" size="mini" @click="handleViewMembers(scope.row)">查看成员</el-button>
|
||||
<el-button type="success" size="mini" @click="handleViewMessages(scope.row)">聊天记录</el-button>
|
||||
<div style="display: flex; gap: 6px; justify-content: center; flex-wrap: wrap;">
|
||||
<el-button type="primary" size="mini" @click="handleViewMembers(scope.row)">成员</el-button>
|
||||
<el-button type="success" size="mini" @click="handleViewMessages(scope.row)">聊天</el-button>
|
||||
<el-button :type="scope.row.status === 1 ? 'warning' : 'info'" size="mini" @click="handleToggleStatus(scope.row)">
|
||||
{{ scope.row.status === 1 ? '解散' : '恢复' }}
|
||||
</el-button>
|
||||
<el-button type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -82,75 +85,117 @@
|
|||
</el-card>
|
||||
|
||||
<!-- 成员列表弹窗 -->
|
||||
<el-dialog :title="'粉丝团成员 - ' + currentGroupName" :visible.sync="memberDialogVisible" width="800px">
|
||||
<el-table :data="memberList" v-loading="memberLoading" border size="small" max-height="400">
|
||||
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||
<el-table-column prop="uid" label="用户ID" width="100" align="center" />
|
||||
<el-table-column prop="nickname" label="用户昵称" width="120" align="center" />
|
||||
<el-table-column prop="level" label="粉丝等级" width="100" align="center">
|
||||
<el-dialog :title="'粉丝团成员 - ' + currentGroupName" :visible.sync="memberDialogVisible" width="1100px" top="5vh">
|
||||
<div class="dialog-stats mb15">
|
||||
<el-tag type="info">共 {{ memberTotal }} 位成员</el-tag>
|
||||
</div>
|
||||
<el-table :data="memberList" v-loading="memberLoading" border size="small" max-height="500" stripe>
|
||||
<el-table-column prop="id" label="ID" width="60" align="center" />
|
||||
<el-table-column label="头像" width="60" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag type="warning" size="small">Lv.{{ scope.row.level }}</el-tag>
|
||||
<el-avatar :size="36" :src="scope.row.avatar" icon="el-icon-user"></el-avatar>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="intimacy" label="亲密度" width="100" align="center" />
|
||||
<el-table-column label="状态" width="100" align="center">
|
||||
<el-table-column prop="uid" label="用户ID" width="80" align="center" />
|
||||
<el-table-column prop="nickname" label="用户昵称" min-width="120" align="center" show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.nickname || scope.row.user_nickname || '未知用户' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="level" label="粉丝等级" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="getLevelTagType(scope.row.level)" size="small" effect="dark">
|
||||
Lv.{{ scope.row.level }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="intimacy" label="亲密度" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span style="color: #E6A23C; font-weight: bold;">{{ scope.row.intimacy || 0 }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="80" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.status === 1 ? 'success' : 'info'" size="small">
|
||||
{{ scope.row.status === 1 ? '正常' : '已退出' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="join_time" label="加入时间" width="160" align="center" />
|
||||
<el-table-column label="操作" width="100" align="center">
|
||||
<el-table-column prop="join_time" label="加入时间" width="160" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="danger" size="mini" @click="handleDeleteMember(scope.row)">移除</el-button>
|
||||
{{ formatTime(scope.row.join_time) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="200" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<div class="btn-group">
|
||||
<el-button type="warning" size="mini" icon="el-icon-edit" @click="handleUpdateMemberLevel(scope.row)">等级</el-button>
|
||||
<el-button type="danger" size="mini" icon="el-icon-delete" @click="handleDeleteMember(scope.row)">移除</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="mt20" style="text-align: right;">
|
||||
<el-pagination
|
||||
small
|
||||
background
|
||||
@current-change="handleMemberPageChange"
|
||||
:current-page="memberPage"
|
||||
:page-size="10"
|
||||
layout="prev, pager, next"
|
||||
layout="total, prev, pager, next"
|
||||
:total="memberTotal"
|
||||
></el-pagination>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 聊天记录弹窗 -->
|
||||
<el-dialog :title="'聊天记录 - ' + currentGroupName" :visible.sync="messageDialogVisible" width="900px">
|
||||
<el-table :data="messageList" v-loading="messageLoading" border size="small" max-height="500">
|
||||
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||
<el-table-column prop="sender_id" label="发送者ID" width="100" align="center" />
|
||||
<el-table-column prop="senderName" label="发送者昵称" width="120" align="center" />
|
||||
<el-table-column prop="content" label="消息内容" min-width="200" align="left">
|
||||
<el-dialog :title="'聊天记录 - ' + currentGroupName" :visible.sync="messageDialogVisible" width="1100px" top="5vh">
|
||||
<div class="dialog-stats mb15">
|
||||
<el-tag type="info">共 {{ messageTotal }} 条消息</el-tag>
|
||||
<el-button type="text" icon="el-icon-refresh" @click="getMessages" style="margin-left: 10px;">刷新</el-button>
|
||||
</div>
|
||||
<el-table :data="messageList" v-loading="messageLoading" border size="small" max-height="520" stripe>
|
||||
<el-table-column prop="id" label="ID" width="60" align="center" />
|
||||
<el-table-column label="发送者" width="200" align="left">
|
||||
<template slot-scope="scope">
|
||||
<div style="max-height: 60px; overflow: hidden; text-overflow: ellipsis;">{{ scope.row.content }}</div>
|
||||
<div style="display: flex; align-items: center; gap: 10px;">
|
||||
<el-avatar :size="32" :src="scope.row.senderAvatar" icon="el-icon-user"></el-avatar>
|
||||
<div>
|
||||
<div style="font-weight: 500;">{{ scope.row.senderName || '未知用户' }}</div>
|
||||
<div style="font-size: 12px; color: #909399;">ID: {{ scope.row.sender_id }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="message_type" label="消息类型" width="100" align="center">
|
||||
<el-table-column prop="content" label="消息内容" min-width="300" align="left">
|
||||
<template slot-scope="scope">
|
||||
<el-tag size="small" :type="scope.row.message_type === 'text' ? '' : 'warning'">
|
||||
{{ scope.row.message_type === 'text' ? '文本' : scope.row.message_type }}
|
||||
<div class="message-content">{{ scope.row.content }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="message_type" label="类型" width="80" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag size="small" :type="getMessageTypeTag(scope.row.message_type)">
|
||||
{{ getMessageTypeText(scope.row.message_type) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="create_time" label="发送时间" width="170" align="center" />
|
||||
<el-table-column label="操作" width="80" align="center">
|
||||
<el-table-column prop="create_time" label="发送时间" width="160" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="danger" size="mini" @click="handleDeleteMessage(scope.row)">删除</el-button>
|
||||
{{ formatTime(scope.row.create_time) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="danger" size="mini" icon="el-icon-delete" @click="handleDeleteMessage(scope.row)" circle></el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="mt20" style="text-align: right;">
|
||||
<el-pagination
|
||||
small
|
||||
background
|
||||
@current-change="handleMessagePageChange"
|
||||
:current-page="messagePage"
|
||||
:page-size="20"
|
||||
layout="prev, pager, next"
|
||||
layout="total, prev, pager, next"
|
||||
:total="messageTotal"
|
||||
></el-pagination>
|
||||
</div>
|
||||
|
|
@ -159,7 +204,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { fanGroupListApi, fanGroupDeleteApi, fanGroupBatchDeleteApi, fanGroupMemberListApi, fanGroupMemberDeleteApi, fanGroupMessageListApi, fanGroupMessageDeleteApi } from '@/api/fanGroup';
|
||||
import { fanGroupListApi, fanGroupDeleteApi, fanGroupBatchDeleteApi, fanGroupMemberListApi, fanGroupMemberDeleteApi, fanGroupMessageListApi, fanGroupMessageDeleteApi, fanGroupStatusApi, fanGroupMemberUpdateLevelApi } from '@/api/fanGroup';
|
||||
|
||||
export default {
|
||||
name: 'FanGroupList',
|
||||
|
|
@ -290,7 +335,7 @@ export default {
|
|||
},
|
||||
// 聊天记录相关方法
|
||||
async handleViewMessages(row) {
|
||||
this.currentGroupId = row.group_id || row.id; // 优先使用关联的群组ID
|
||||
this.currentGroupId = row.id; // 使用粉丝团ID
|
||||
this.currentGroupName = row.name;
|
||||
this.messagePage = 1;
|
||||
this.messageDialogVisible = true;
|
||||
|
|
@ -322,6 +367,60 @@ export default {
|
|||
this.$message.error(error.message || '删除失败');
|
||||
}
|
||||
}).catch(() => {});
|
||||
},
|
||||
// 切换粉丝团状态(解散/恢复)
|
||||
async handleToggleStatus(row) {
|
||||
const newStatus = row.status === 1 ? 0 : 1;
|
||||
const action = newStatus === 0 ? '解散' : '恢复';
|
||||
this.$confirm(`确定要${action}该粉丝团吗?`, '提示', { type: 'warning' }).then(async () => {
|
||||
try {
|
||||
await fanGroupStatusApi(row.id, newStatus);
|
||||
this.$message.success(`${action}成功`);
|
||||
this.getList();
|
||||
} catch (error) {
|
||||
this.$message.error(error.message || `${action}失败`);
|
||||
}
|
||||
}).catch(() => {});
|
||||
},
|
||||
// 修改成员等级
|
||||
async handleUpdateMemberLevel(row) {
|
||||
this.$prompt('请输入新等级(1-10)', '修改等级', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
inputPattern: /^([1-9]|10)$/,
|
||||
inputErrorMessage: '等级必须是1-10之间的数字',
|
||||
inputValue: String(row.level)
|
||||
}).then(async ({ value }) => {
|
||||
try {
|
||||
await fanGroupMemberUpdateLevelApi(row.id, parseInt(value));
|
||||
this.$message.success('修改成功');
|
||||
this.getMembers();
|
||||
} catch (error) {
|
||||
this.$message.error(error.message || '修改失败');
|
||||
}
|
||||
}).catch(() => {});
|
||||
},
|
||||
// 获取等级标签类型
|
||||
getLevelTagType(level) {
|
||||
if (level >= 8) return 'danger';
|
||||
if (level >= 5) return 'warning';
|
||||
if (level >= 3) return 'success';
|
||||
return 'info';
|
||||
},
|
||||
// 获取消息类型标签
|
||||
getMessageTypeTag(type) {
|
||||
const map = { text: '', image: 'success', gift: 'warning', system: 'info' };
|
||||
return map[type] || '';
|
||||
},
|
||||
// 获取消息类型文本
|
||||
getMessageTypeText(type) {
|
||||
const map = { text: '文本', image: '图片', gift: '礼物', system: '系统' };
|
||||
return map[type] || type;
|
||||
},
|
||||
// 格式化时间
|
||||
formatTime(time) {
|
||||
if (!time) return '-';
|
||||
return time.replace('T', ' ').substring(0, 19);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -330,4 +429,42 @@ export default {
|
|||
<style scoped lang="scss">
|
||||
.mb20 { margin-bottom: 20px; }
|
||||
.mt20 { margin-top: 20px; }
|
||||
.mb15 { margin-bottom: 15px; }
|
||||
|
||||
.dialog-stats {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 15px;
|
||||
background: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
max-height: 80px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
line-height: 1.5;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
::v-deep .el-dialog__body {
|
||||
padding: 15px 20px 20px;
|
||||
}
|
||||
|
||||
::v-deep .el-table .cell {
|
||||
padding: 8px 10px;
|
||||
}
|
||||
|
||||
::v-deep .el-avatar {
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -93,12 +93,23 @@ export default {
|
|||
this.loading = true;
|
||||
customerServiceGroupListApi(this.searchForm)
|
||||
.then((res) => {
|
||||
this.tableData = res.data.list || [];
|
||||
this.total = res.data.total || 0;
|
||||
// 兼容多种返回格式
|
||||
if (res && res.list) {
|
||||
this.tableData = res.list || [];
|
||||
this.total = res.total || 0;
|
||||
} else if (Array.isArray(res)) {
|
||||
this.tableData = res;
|
||||
this.total = res.length;
|
||||
} else {
|
||||
this.tableData = [];
|
||||
this.total = 0;
|
||||
}
|
||||
this.loading = false;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('获取客服联系方式列表失败:', err);
|
||||
this.tableData = [];
|
||||
this.total = 0;
|
||||
this.loading = false;
|
||||
this.$message.warning('获取数据失败,请检查后端接口');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -48,21 +48,26 @@
|
|||
<el-table-column label="发布者" width="150">
|
||||
<template slot-scope="scope">
|
||||
<div class="user-info">
|
||||
<el-avatar :src="scope.row.user_avatar" :size="32" />
|
||||
<span class="user-name">{{ scope.row.user_name }}</span>
|
||||
<el-avatar :src="scope.row.user_avatar || scope.row.avatar" :size="32">
|
||||
<span>{{ scope.row.nickname ? scope.row.nickname.substr(0, 1) : '用' }}</span>
|
||||
</el-avatar>
|
||||
<span class="user-name">{{ scope.row.nickname }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="content" label="动态内容" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column label="图片/视频" width="120" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-image v-if="scope.row.images && scope.row.images.length" :src="scope.row.images[0]" :preview-src-list="scope.row.images" style="width: 50px; height: 50px;" fit="cover" />
|
||||
<template v-if="scope.row.images">
|
||||
<el-image v-if="parseImages(scope.row.images).length" :src="parseImages(scope.row.images)[0]" :preview-src-list="parseImages(scope.row.images)" style="width: 50px; height: 50px;" fit="cover" />
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="分区" width="80" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag size="mini">{{ getZoneName(scope.row.zone) }}</el-tag>
|
||||
<el-tag size="mini">{{ scope.row.category_id ? getCategoryName(scope.row.category_id) : '默认' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="like_count" label="点赞" width="70" align="center">
|
||||
|
|
@ -83,14 +88,14 @@
|
|||
<el-table-column prop="create_time" label="发布时间" width="160" align="center" />
|
||||
<el-table-column label="审核状态" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag size="mini" :type="getAuditStatusType(scope.row.audit_status)">{{ getAuditStatusName(scope.row.audit_status) }}</el-tag>
|
||||
<el-tag size="mini" :type="getStatusType(scope.row.status)">{{ getStatusName(scope.row.status) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="220" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="primary" size="mini" @click="handleDetail(scope.row)">详情</el-button>
|
||||
<el-button v-if="scope.row.audit_status === 0" type="success" size="mini" @click="handleAudit(scope.row, 1)">通过</el-button>
|
||||
<el-button v-if="scope.row.audit_status === 0" type="warning" size="mini" @click="handleAudit(scope.row, 2)">拒绝</el-button>
|
||||
<el-button v-if="scope.row.status === 0" type="success" size="mini" @click="handleAudit(scope.row, 1)">通过</el-button>
|
||||
<el-button v-if="scope.row.status === 0" type="warning" size="mini" @click="handleAudit(scope.row, 2)">拒绝</el-button>
|
||||
<el-button type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
|
@ -109,18 +114,20 @@
|
|||
<el-descriptions-item label="动态ID">{{ detailData.id }}</el-descriptions-item>
|
||||
<el-descriptions-item label="发布者">
|
||||
<div class="user-info">
|
||||
<el-avatar :src="detailData.user_avatar" :size="24" />
|
||||
<span>{{ detailData.user_name }}</span>
|
||||
<el-avatar :src="detailData.user_avatar || detailData.avatar" :size="24">
|
||||
<span>{{ detailData.nickname ? detailData.nickname.substr(0, 1) : '用' }}</span>
|
||||
</el-avatar>
|
||||
<span>{{ detailData.nickname }}</span>
|
||||
</div>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="分区">{{ getZoneName(detailData.zone) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="审核状态">
|
||||
<el-tag size="small" :type="getAuditStatusType(detailData.audit_status)">{{ getAuditStatusName(detailData.audit_status) }}</el-tag>
|
||||
<el-descriptions-item label="位置">{{ detailData.location || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">
|
||||
<el-tag size="small" :type="getStatusType(detailData.status)">{{ getStatusName(detailData.status) }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="动态内容" :span="2">{{ detailData.content }}</el-descriptions-item>
|
||||
<el-descriptions-item label="图片/视频" :span="2" v-if="detailData.images && detailData.images.length">
|
||||
<el-descriptions-item label="图片/视频" :span="2" v-if="detailData.images">
|
||||
<div class="image-list">
|
||||
<el-image v-for="(img, idx) in detailData.images" :key="idx" :src="img" :preview-src-list="detailData.images" style="width: 100px; height: 100px; margin-right: 10px;" fit="cover" />
|
||||
<el-image v-for="(img, idx) in parseImages(detailData.images)" :key="idx" :src="img" :preview-src-list="parseImages(detailData.images)" style="width: 100px; height: 100px; margin-right: 10px;" fit="cover" />
|
||||
</div>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="点赞数">{{ detailData.like_count || 0 }}</el-descriptions-item>
|
||||
|
|
@ -131,8 +138,8 @@
|
|||
<el-descriptions-item label="更新时间">{{ detailData.update_time || '-' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div slot="footer">
|
||||
<el-button v-if="detailData && detailData.audit_status === 0" type="success" @click="handleAudit(detailData, 1)">通过</el-button>
|
||||
<el-button v-if="detailData && detailData.audit_status === 0" type="warning" @click="handleAudit(detailData, 2)">拒绝</el-button>
|
||||
<el-button v-if="detailData && detailData.status === 0" type="success" @click="handleAudit(detailData, 1)">通过</el-button>
|
||||
<el-button v-if="detailData && detailData.status === 0" type="warning" @click="handleAudit(detailData, 2)">拒绝</el-button>
|
||||
<el-button type="danger" @click="handleDelete(detailData)">删除</el-button>
|
||||
<el-button @click="detailDialogVisible = false">关闭</el-button>
|
||||
</div>
|
||||
|
|
@ -230,6 +237,18 @@ export default {
|
|||
const map = { recommend: '推荐', hot: '热门', new: '最新', follow: '关注' };
|
||||
return map[zone] || zone;
|
||||
},
|
||||
getCategoryName(categoryId) {
|
||||
const map = { 1: '推荐', 2: '热门', 3: '最新', 4: '关注' };
|
||||
return map[categoryId] || '默认';
|
||||
},
|
||||
getStatusName(status) {
|
||||
const map = { 0: '待审核', 1: '已通过', 2: '已拒绝' };
|
||||
return map[status] !== undefined ? map[status] : '已通过';
|
||||
},
|
||||
getStatusType(status) {
|
||||
const map = { 0: 'warning', 1: 'success', 2: 'danger' };
|
||||
return map[status] || 'success';
|
||||
},
|
||||
getAuditStatusName(status) {
|
||||
const map = { 0: '待审核', 1: '已通过', 2: '已拒绝' };
|
||||
return map[status] || status;
|
||||
|
|
@ -238,6 +257,15 @@ export default {
|
|||
const map = { 0: 'warning', 1: 'success', 2: 'danger' };
|
||||
return map[status] || 'info';
|
||||
},
|
||||
parseImages(images) {
|
||||
if (!images) return [];
|
||||
if (Array.isArray(images)) return images;
|
||||
try {
|
||||
return JSON.parse(images);
|
||||
} catch (e) {
|
||||
return images ? [images] : [];
|
||||
}
|
||||
},
|
||||
async handleDetail(row) {
|
||||
try {
|
||||
const res = await socialDynamicDetailApi(row.id);
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@
|
|||
<!-- 数据表格 -->
|
||||
<el-table :data="tableData" v-loading="loading" border>
|
||||
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||
<el-table-column prop="task_name" label="任务名字" min-width="200" align="center" />
|
||||
<el-table-column prop="diamond" label="赠送的钻石数量" min-width="200" align="center" />
|
||||
<el-table-column prop="name" label="任务名字" min-width="200" align="center" />
|
||||
<el-table-column prop="reward_value" label="赠送的钻石数量" min-width="200" align="center" />
|
||||
<el-table-column prop="create_time" label="创建时间" width="180" align="center" />
|
||||
<el-table-column label="操作" width="120" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
|
|
@ -39,11 +39,11 @@
|
|||
<!-- 添加/编辑弹窗 -->
|
||||
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="500px">
|
||||
<el-form :model="form" ref="form" label-width="100px" :rules="formRules">
|
||||
<el-form-item label="任务名称" prop="task_name">
|
||||
<el-input v-model="form.task_name" placeholder="请输入任务名称" />
|
||||
<el-form-item label="任务名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入任务名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="钻石数量" prop="diamond">
|
||||
<el-input-number v-model="form.diamond" :min="0" style="width: 100%;" placeholder="请输入钻石数量" />
|
||||
<el-form-item label="钻石数量" prop="reward_value">
|
||||
<el-input-number v-model="form.reward_value" :min="0" style="width: 100%;" placeholder="请输入钻石数量" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
|
|
@ -70,12 +70,12 @@ export default {
|
|||
isEdit: false,
|
||||
form: {
|
||||
id: null,
|
||||
task_name: '',
|
||||
diamond: 0
|
||||
name: '',
|
||||
reward_value: 0
|
||||
},
|
||||
formRules: {
|
||||
task_name: [{ required: true, message: '请输入任务名称', trigger: 'blur' }],
|
||||
diamond: [{ required: true, message: '请输入钻石数量', trigger: 'blur' }]
|
||||
name: [{ required: true, message: '请输入任务名称', trigger: 'blur' }],
|
||||
reward_value: [{ required: true, message: '请输入钻石数量', trigger: 'blur' }]
|
||||
}
|
||||
};
|
||||
},
|
||||
|
|
@ -90,7 +90,7 @@ export default {
|
|||
this.tableData = res.list || [];
|
||||
this.total = res.total || 0;
|
||||
} catch (error) {
|
||||
this.$message.error(error.message || '获取列表失败');
|
||||
this.$message.error((error && error.message) || '获取列表失败');
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
|
|
@ -102,7 +102,7 @@ export default {
|
|||
handleAdd() {
|
||||
this.dialogTitle = '添加任务';
|
||||
this.isEdit = false;
|
||||
this.form = { id: null, task_name: '', diamond: 0 };
|
||||
this.form = { id: null, name: '', reward_value: 0 };
|
||||
this.dialogVisible = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.form && this.$refs.form.clearValidate();
|
||||
|
|
@ -113,8 +113,8 @@ export default {
|
|||
this.isEdit = true;
|
||||
this.form = {
|
||||
id: row.id,
|
||||
task_name: row.task_name,
|
||||
diamond: row.diamond
|
||||
name: row.name,
|
||||
reward_value: row.reward_value
|
||||
};
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
|
|
@ -132,7 +132,7 @@ export default {
|
|||
this.dialogVisible = false;
|
||||
this.getList();
|
||||
} catch (error) {
|
||||
this.$message.error(error.message || '操作失败');
|
||||
this.$message.error((error && error.message) || '操作失败');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -19,11 +19,18 @@
|
|||
<el-table-column prop="user_nickname" label="用户昵称" min-width="120" align="center" />
|
||||
<el-table-column label="用户头像" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-image :src="scope.row.user_avatar" style="width: 50px; height: 50px" :preview-src-list="[scope.row.user_avatar]" />
|
||||
<el-avatar :size="50" :src="scope.row.user_avatar || ''">
|
||||
<span v-if="!scope.row.user_avatar">{{ scope.row.user_nickname ? scope.row.user_nickname.substr(0, 1) : '用' }}</span>
|
||||
</el-avatar>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="diamond" label="赠送的钻石数量" min-width="150" align="center" />
|
||||
<el-table-column prop="create_time" label="创建时间" width="180" align="center" />
|
||||
<el-table-column prop="reward_value" label="赠送的钻石数量" min-width="150" align="center" />
|
||||
<el-table-column prop="continuous_days" label="连续签到天数" width="120" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag type="success">第 {{ scope.row.continuous_days || 1 }} 天</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="create_time" label="签到时间" width="180" align="center" />
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
|
|
@ -59,7 +66,7 @@ export default {
|
|||
this.tableData = res.list || [];
|
||||
this.total = res.total || 0;
|
||||
} catch (error) {
|
||||
this.$message.error(error.message || '获取列表失败');
|
||||
this.$message.error((error && error.message) || '获取列表失败');
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,14 +19,18 @@
|
|||
<el-table-column prop="user_nickname" label="用户昵称" min-width="120" align="center" />
|
||||
<el-table-column label="用户头像" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-image :src="scope.row.user_avatar" style="width: 50px; height: 50px" :preview-src-list="[scope.row.user_avatar]" />
|
||||
<el-avatar :size="50" :src="scope.row.user_avatar || ''">
|
||||
<span v-if="!scope.row.user_avatar">{{ scope.row.user_nickname ? scope.row.user_nickname.substr(0, 1) : '用' }}</span>
|
||||
</el-avatar>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="task_name" label="任务名字" min-width="150" align="center" />
|
||||
<el-table-column prop="diamond" label="赠送的钻石数量" min-width="120" align="center" />
|
||||
<el-table-column prop="reward_value" label="赠送的钻石数量" min-width="120" align="center" />
|
||||
<el-table-column label="领取状态" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.receive_status === '已领取' ? 'success' : 'danger'">{{ scope.row.receive_status }}</el-tag>
|
||||
<el-tag :type="scope.row.status === 2 ? 'success' : (scope.row.status === 1 ? 'warning' : 'info')">
|
||||
{{ scope.row.status === 2 ? '已领取' : (scope.row.status === 1 ? '已完成' : '进行中') }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="create_time" label="创建时间" width="180" align="center" />
|
||||
|
|
@ -65,7 +69,7 @@ export default {
|
|||
this.tableData = res.list || [];
|
||||
this.total = res.total || 0;
|
||||
} catch (error) {
|
||||
this.$message.error(error.message || '获取列表失败');
|
||||
this.$message.error((error && error.message) || '获取列表失败');
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,27 +46,43 @@
|
|||
|
||||
<!-- 数据表格 -->
|
||||
<el-table v-loading="loading" :data="tableData" border>
|
||||
<el-table-column prop="reporter_id" label="举报人" width="80" />
|
||||
<el-table-column prop="reporter_phone" label="举报人电话" width="120" />
|
||||
<el-table-column prop="reporter_name" label="举报人名称" width="100" />
|
||||
<el-table-column prop="report_type" label="举报类型" width="80" />
|
||||
<el-table-column prop="report_reason" label="举报原因" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column prop="target_id" label="动态id/房间id/用户id/家族id" width="180" />
|
||||
<el-table-column prop="reported_name" label="被举报人" width="100" />
|
||||
<el-table-column prop="reported_phone" label="被举报人电话" width="120" />
|
||||
<el-table-column prop="create_time" label="举报时间" width="160" />
|
||||
<el-table-column label="处理状态" width="80" align="center">
|
||||
<el-table-column label="举报人" width="80" align="center">
|
||||
<template slot-scope="scope">{{ scope.row.reporter_id || scope.row.uid || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="举报人电话" width="120" align="center">
|
||||
<template slot-scope="scope">{{ scope.row.reporter_phone || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="举报人名称" width="100" align="center">
|
||||
<template slot-scope="scope">{{ scope.row.reporter_name || scope.row.nickname || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="举报类型" width="80" align="center">
|
||||
<template slot-scope="scope">{{ scope.row.report_type || getTargetTypeText(scope.row.target_type) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="举报原因" min-width="120" show-overflow-tooltip>
|
||||
<template slot-scope="scope">{{ scope.row.report_reason || scope.row.reason || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="target_id" label="目标ID" width="80" align="center" />
|
||||
<el-table-column label="被举报人" width="100" align="center">
|
||||
<template slot-scope="scope">{{ scope.row.reported_name || scope.row.target_name || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="被举报人电话" width="120" align="center">
|
||||
<template slot-scope="scope">{{ scope.row.reported_phone || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="create_time" label="举报时间" width="160" align="center" />
|
||||
<el-table-column label="状态" width="80" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.status === 1 ? 'success' : 'warning'" size="small">
|
||||
{{ scope.row.status === 1 ? '已处理' : '待处理' }}
|
||||
<el-tag :type="getStatusType(scope.row.status)" size="small">
|
||||
{{ getStatusText(scope.row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="160" align="center" fixed="right">
|
||||
<el-table-column label="操作" width="180" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="primary" size="mini" @click="handleDetail(scope.row)">查看详情</el-button>
|
||||
<el-button v-if="scope.row.status === 0" type="warning" size="mini" @click="handleProcess(scope.row)">已处理</el-button>
|
||||
<el-tag v-else type="info" size="small">已处理</el-tag>
|
||||
<div style="display: flex; justify-content: center; align-items: center; gap: 5px;">
|
||||
<el-button type="primary" size="mini" @click="handleDetail(scope.row)">详情</el-button>
|
||||
<el-button v-if="scope.row.status === 0 || scope.row.status === 1" type="warning" size="mini" @click="handleProcess(scope.row)">处理</el-button>
|
||||
<el-tag v-else type="success" size="mini">已处理</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
|
@ -203,6 +219,21 @@ export default {
|
|||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
// 获取举报目标类型文本
|
||||
getTargetTypeText(type) {
|
||||
const typeMap = { 1: '用户', 2: '房间', 3: '动态', 4: '评论' }
|
||||
return typeMap[type] || '其他'
|
||||
},
|
||||
// 获取状态文本
|
||||
getStatusText(status) {
|
||||
const statusMap = { 0: '待处理', 1: '处理中', 2: '已处理', 3: '已忽略' }
|
||||
return statusMap[status] || '待处理'
|
||||
},
|
||||
// 获取状态标签类型
|
||||
getStatusType(status) {
|
||||
const typeMap = { 0: 'warning', 1: 'info', 2: 'success', 3: 'danger' }
|
||||
return typeMap[status] || 'warning'
|
||||
},
|
||||
getList() {
|
||||
this.loading = true
|
||||
const params = {
|
||||
|
|
@ -217,10 +248,22 @@ export default {
|
|||
limit: this.limit
|
||||
}
|
||||
reportListApi(params).then(res => {
|
||||
this.tableData = res.data.list || []
|
||||
this.total = res.data.total || 0
|
||||
// 兼容不同的返回格式
|
||||
if (res && res.list) {
|
||||
this.tableData = res.list || []
|
||||
this.total = res.total || 0
|
||||
} else if (res && res.data) {
|
||||
this.tableData = res.data.list || []
|
||||
this.total = res.data.total || 0
|
||||
} else {
|
||||
this.tableData = []
|
||||
this.total = 0
|
||||
}
|
||||
this.loading = false
|
||||
}).catch(() => {
|
||||
}).catch((err) => {
|
||||
console.error('获取举报列表失败:', err)
|
||||
this.tableData = []
|
||||
this.total = 0
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
|
|
|||
|
|
@ -121,11 +121,26 @@ export default {
|
|||
startTime: this.searchForm.startTime || undefined,
|
||||
endTime: this.searchForm.endTime || undefined
|
||||
};
|
||||
console.log('请求参数:', params);
|
||||
const res = await sensitiveWordListApi(params);
|
||||
this.tableData = res.list || [];
|
||||
this.total = res.total || 0;
|
||||
console.log('响应数据:', res);
|
||||
|
||||
if (res && res.list) {
|
||||
this.tableData = res.list;
|
||||
this.total = res.total || 0;
|
||||
} else if (Array.isArray(res)) {
|
||||
// 兼容直接返回数组的情况
|
||||
this.tableData = res;
|
||||
this.total = res.length;
|
||||
} else {
|
||||
this.tableData = [];
|
||||
this.total = 0;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取敏感词列表失败:', error);
|
||||
this.$message.error(error.message || '获取列表失败');
|
||||
this.tableData = [];
|
||||
this.total = 0;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,16 +4,39 @@
|
|||
<div class="padding-add">
|
||||
<el-page-header @back="goBack" content="每日签到配置"></el-page-header>
|
||||
<div class="mt20">
|
||||
<el-table :data="tableData" v-loading="loading" border>
|
||||
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||
<el-table-column prop="day" label="天数" width="120" align="center" />
|
||||
<el-table-column prop="integral" label="转后积分奖励" width="150" align="center" />
|
||||
<el-table-column label="操作" width="120" align="center" fixed="right">
|
||||
<!-- 提示信息 -->
|
||||
<el-alert
|
||||
title="签到配置说明:用户连续签到可获得对应天数的积分奖励,第7天后循环"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="mb20"
|
||||
/>
|
||||
|
||||
<el-table :data="tableData" v-loading="loading" border style="width: 100%">
|
||||
<el-table-column prop="id" label="ID" width="100" align="center" />
|
||||
<el-table-column prop="day" label="连续签到天数" min-width="150" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="warning" size="small" @click="handleEdit(scope.row)">编辑</el-button>
|
||||
<span>第 {{ scope.row.day }} 天</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="integral" label="奖励积分" min-width="150" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag type="warning">+{{ scope.row.integral }} 积分</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="120" align="center">
|
||||
<template>
|
||||
<el-tag type="success">已启用</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="warning" size="mini" @click="handleEdit(scope.row)">编辑</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="acea-row row-right page mt20">
|
||||
<el-pagination
|
||||
@size-change="handleSizeChange"
|
||||
|
|
@ -31,32 +54,34 @@
|
|||
|
||||
<!-- 编辑弹窗 -->
|
||||
<el-dialog
|
||||
title="编辑"
|
||||
title="编辑签到配置"
|
||||
:visible.sync="dialogVisible"
|
||||
width="500px"
|
||||
width="450px"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-form :model="editForm" :rules="rules" ref="editForm" label-width="100px">
|
||||
<el-form-item label="天数" prop="day">
|
||||
<el-form :model="editForm" :rules="rules" ref="editForm" label-width="120px">
|
||||
<el-form-item label="连续签到天数" prop="day">
|
||||
<el-input-number
|
||||
v-model="editForm.day"
|
||||
:min="1"
|
||||
:max="365"
|
||||
:max="7"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
disabled
|
||||
></el-input-number>
|
||||
</el-form-item>
|
||||
<el-form-item label="转后积分" prop="integral">
|
||||
<el-form-item label="奖励积分" prop="integral">
|
||||
<el-input-number
|
||||
v-model="editForm.integral"
|
||||
:min="0"
|
||||
:max="9999"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
></el-input-number>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">返回</el-button>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSave">保存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
|
@ -81,16 +106,14 @@ export default {
|
|||
editForm: {
|
||||
id: null,
|
||||
day: 1,
|
||||
integral: 2,
|
||||
integral: 0,
|
||||
},
|
||||
rules: {
|
||||
day: [
|
||||
{ required: true, message: '请输入天数', trigger: 'blur' },
|
||||
{ type: 'number', min: 1, max: 365, message: '天数范围为1-365', trigger: 'blur' },
|
||||
],
|
||||
integral: [
|
||||
{ required: true, message: '请输入转后积分', trigger: 'blur' },
|
||||
{ type: 'number', min: 0, message: '积分不能为负数', trigger: 'blur' },
|
||||
{ required: true, message: '请输入奖励积分', trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
|
@ -103,21 +126,20 @@ export default {
|
|||
this.loading = true;
|
||||
signConfigListApi(this.searchForm)
|
||||
.then((res) => {
|
||||
this.tableData = res.data.list || [];
|
||||
this.total = res.data.total || 0;
|
||||
this.tableData = Array.isArray(res) ? res : (res.list || []);
|
||||
this.total = this.tableData.length;
|
||||
this.loading = false;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('获取签到配置列表失败:', err);
|
||||
// 如果后端接口不存在,使用模拟数据
|
||||
this.tableData = [
|
||||
{ id: 1, day: 1, integral: 2 },
|
||||
{ id: 2, day: 2, integral: 4 },
|
||||
{ id: 3, day: 3, integral: 6 },
|
||||
{ id: 4, day: 4, integral: 8 },
|
||||
{ id: 5, day: 5, integral: 8 },
|
||||
{ id: 6, day: 6, integral: 10 },
|
||||
{ id: 7, day: 7, integral: 10 },
|
||||
{ id: 39, day: 1, integral: 10 },
|
||||
{ id: 40, day: 2, integral: 20 },
|
||||
{ id: 41, day: 3, integral: 30 },
|
||||
{ id: 42, day: 4, integral: 40 },
|
||||
{ id: 43, day: 5, integral: 50 },
|
||||
{ id: 44, day: 6, integral: 60 },
|
||||
{ id: 122, day: 7, integral: 70 },
|
||||
];
|
||||
this.total = 7;
|
||||
this.loading = false;
|
||||
|
|
@ -143,7 +165,7 @@ export default {
|
|||
})
|
||||
.catch((err) => {
|
||||
console.error('保存失败:', err);
|
||||
this.$message.error('保存失败,后端接口暂未实现');
|
||||
this.$message.error('保存失败');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -167,4 +189,10 @@ export default {
|
|||
.mt20 {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.mb20 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
<!-- 表格 -->
|
||||
<el-table :data="tableData" v-loading="loading" border style="width: 100%">
|
||||
<el-table-column prop="id" label="id" width="80" align="center" />
|
||||
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||
<el-table-column label="用户昵称" width="150" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.nickname || '-' }}</span>
|
||||
|
|
@ -41,12 +41,19 @@
|
|||
</el-avatar>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="获得的经验或积分" width="180" align="center">
|
||||
<el-table-column label="获得积分" width="120" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.number }}</span>
|
||||
<el-tag type="warning">+{{ scope.row.number }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="时间时间" min-width="180" align="center">
|
||||
<el-table-column label="类型" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.type === 1 ? 'success' : 'primary'">
|
||||
{{ scope.row.type === 1 ? '积分' : '经验' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="签到时间" min-width="180" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ formatTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
|
|
@ -96,8 +103,17 @@ export default {
|
|||
this.loading = true;
|
||||
userSignListApi(this.searchForm)
|
||||
.then((res) => {
|
||||
this.tableData = res.data.list || [];
|
||||
this.total = res.data.total || 0;
|
||||
// 兼容分页对象和直接数组两种格式
|
||||
if (res && res.list) {
|
||||
this.tableData = res.list || [];
|
||||
this.total = res.total || 0;
|
||||
} else if (Array.isArray(res)) {
|
||||
this.tableData = res;
|
||||
this.total = res.length;
|
||||
} else {
|
||||
this.tableData = [];
|
||||
this.total = 0;
|
||||
}
|
||||
this.loading = false;
|
||||
})
|
||||
.catch((err) => {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import org.springframework.validation.annotation.Validated;
|
|||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
|
@ -68,7 +69,7 @@ public class FanGroupController {
|
|||
params.add(limit);
|
||||
|
||||
String sql = "SELECT fg.*, " +
|
||||
"(SELECT COUNT(*) FROM eb_group_message gm WHERE gm.group_id = fg.group_id AND gm.is_deleted = 0) as message_count " +
|
||||
"(SELECT COUNT(*) FROM eb_group_message gm WHERE gm.group_id = fg.id AND gm.is_deleted = 0) as message_count " +
|
||||
"FROM eb_fan_group fg" + whereSql + " ORDER BY fg.id DESC LIMIT ?, ?";
|
||||
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, params.toArray());
|
||||
|
||||
|
|
@ -140,9 +141,19 @@ public class FanGroupController {
|
|||
String countSql = "SELECT COUNT(*) FROM eb_fan_group_member WHERE group_id = ?";
|
||||
Integer total = jdbcTemplate.queryForObject(countSql, Integer.class, groupId);
|
||||
|
||||
String sql = "SELECT * FROM eb_fan_group_member WHERE group_id = ? ORDER BY level DESC, intimacy DESC LIMIT ?, ?";
|
||||
String sql = "SELECT m.*, u.avatar, u.nickname as user_nickname " +
|
||||
"FROM eb_fan_group_member m " +
|
||||
"LEFT JOIN eb_user u ON m.uid = u.uid " +
|
||||
"WHERE m.group_id = ? ORDER BY m.level DESC, m.intimacy DESC LIMIT ?, ?";
|
||||
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, groupId, (page - 1) * limit, limit);
|
||||
|
||||
// 如果nickname为空,使用user_nickname
|
||||
for (Map<String, Object> item : list) {
|
||||
if (item.get("nickname") == null || "".equals(item.get("nickname"))) {
|
||||
item.put("nickname", item.get("user_nickname"));
|
||||
}
|
||||
}
|
||||
|
||||
CommonPage<Map<String, Object>> result = new CommonPage<>();
|
||||
result.setList(list);
|
||||
result.setTotal(total != null ? total.longValue() : 0L);
|
||||
|
|
@ -166,13 +177,16 @@ public class FanGroupController {
|
|||
try {
|
||||
// 获取成员所属粉丝团
|
||||
Map<String, Object> member = jdbcTemplate.queryForMap("SELECT group_id FROM eb_fan_group_member WHERE id = ?", id);
|
||||
Integer groupId = (Integer) member.get("group_id");
|
||||
Object groupIdObj = member.get("group_id");
|
||||
Integer groupId = groupIdObj != null ? ((Number) groupIdObj).intValue() : null;
|
||||
|
||||
// 删除成员
|
||||
jdbcTemplate.update("DELETE FROM eb_fan_group_member WHERE id = ?", id);
|
||||
|
||||
// 更新粉丝团成员数量
|
||||
jdbcTemplate.update("UPDATE eb_fan_group SET member_count = member_count - 1 WHERE id = ? AND member_count > 0", groupId);
|
||||
if (groupId != null) {
|
||||
jdbcTemplate.update("UPDATE eb_fan_group SET member_count = member_count - 1 WHERE id = ? AND member_count > 0", groupId);
|
||||
}
|
||||
|
||||
return CommonResult.success("删除成功");
|
||||
} catch (Exception e) {
|
||||
|
|
@ -230,4 +244,142 @@ public class FanGroupController {
|
|||
return CommonResult.failed("删除失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取粉丝团详情
|
||||
*/
|
||||
@ApiOperation(value = "粉丝团详情")
|
||||
@RequestMapping(value = "/detail/{id}", method = RequestMethod.GET)
|
||||
public CommonResult<Map<String, Object>> getDetail(@PathVariable Integer id) {
|
||||
try {
|
||||
String sql = "SELECT fg.*, u.avatar as anchor_avatar " +
|
||||
"FROM eb_fan_group fg " +
|
||||
"LEFT JOIN eb_user u ON fg.anchor_id = u.uid " +
|
||||
"WHERE fg.id = ?";
|
||||
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, id);
|
||||
if (list.isEmpty()) {
|
||||
return CommonResult.failed("粉丝团不存在");
|
||||
}
|
||||
return CommonResult.success(list.get(0));
|
||||
} catch (Exception e) {
|
||||
log.error("获取粉丝团详情失败", e);
|
||||
return CommonResult.failed("获取详情失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改粉丝团状态(解散/恢复)
|
||||
*/
|
||||
@ApiOperation(value = "修改粉丝团状态")
|
||||
@RequestMapping(value = "/status/{id}", method = RequestMethod.POST)
|
||||
public CommonResult<String> updateStatus(
|
||||
@PathVariable Integer id,
|
||||
@RequestBody Map<String, Object> request) {
|
||||
try {
|
||||
Integer status = (Integer) request.get("status");
|
||||
if (status == null || (status != 0 && status != 1)) {
|
||||
return CommonResult.failed("状态值无效");
|
||||
}
|
||||
|
||||
jdbcTemplate.update("UPDATE eb_fan_group SET status = ?, update_time = NOW() WHERE id = ?", status, id);
|
||||
return CommonResult.success(status == 1 ? "已恢复" : "已解散");
|
||||
} catch (Exception e) {
|
||||
log.error("修改粉丝团状态失败", e);
|
||||
return CommonResult.failed("操作失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改粉丝团信息
|
||||
*/
|
||||
@ApiOperation(value = "修改粉丝团信息")
|
||||
@RequestMapping(value = "/update/{id}", method = RequestMethod.POST)
|
||||
public CommonResult<String> updateFanGroup(
|
||||
@PathVariable Integer id,
|
||||
@RequestBody Map<String, Object> request) {
|
||||
try {
|
||||
StringBuilder updateSql = new StringBuilder("UPDATE eb_fan_group SET update_time = NOW()");
|
||||
List<Object> params = new ArrayList<>();
|
||||
|
||||
if (request.containsKey("name") && request.get("name") != null) {
|
||||
updateSql.append(", name = ?");
|
||||
params.add(request.get("name"));
|
||||
}
|
||||
if (request.containsKey("badge") && request.get("badge") != null) {
|
||||
updateSql.append(", badge = ?");
|
||||
params.add(request.get("badge"));
|
||||
}
|
||||
if (request.containsKey("badge_color") && request.get("badge_color") != null) {
|
||||
updateSql.append(", badge_color = ?");
|
||||
params.add(request.get("badge_color"));
|
||||
}
|
||||
|
||||
updateSql.append(" WHERE id = ?");
|
||||
params.add(id);
|
||||
|
||||
jdbcTemplate.update(updateSql.toString(), params.toArray());
|
||||
return CommonResult.success("修改成功");
|
||||
} catch (Exception e) {
|
||||
log.error("修改粉丝团信息失败", e);
|
||||
return CommonResult.failed("修改失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改成员等级
|
||||
*/
|
||||
@ApiOperation(value = "修改成员等级")
|
||||
@RequestMapping(value = "/member/update-level/{id}", method = RequestMethod.POST)
|
||||
public CommonResult<String> updateMemberLevel(
|
||||
@PathVariable Integer id,
|
||||
@RequestBody Map<String, Object> request) {
|
||||
try {
|
||||
Integer level = (Integer) request.get("level");
|
||||
if (level == null || level < 1 || level > 10) {
|
||||
return CommonResult.failed("等级值无效(1-10)");
|
||||
}
|
||||
|
||||
jdbcTemplate.update("UPDATE eb_fan_group_member SET level = ? WHERE id = ?", level, id);
|
||||
return CommonResult.success("修改成功");
|
||||
} catch (Exception e) {
|
||||
log.error("修改成员等级失败", e);
|
||||
return CommonResult.failed("修改失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计数据
|
||||
*/
|
||||
@ApiOperation(value = "粉丝团统计")
|
||||
@RequestMapping(value = "/statistics", method = RequestMethod.GET)
|
||||
public CommonResult<Map<String, Object>> getStatistics() {
|
||||
try {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
|
||||
// 总粉丝团数
|
||||
Integer totalGroups = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(*) FROM eb_fan_group", Integer.class);
|
||||
stats.put("totalGroups", totalGroups != null ? totalGroups : 0);
|
||||
|
||||
// 正常状态粉丝团数
|
||||
Integer activeGroups = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(*) FROM eb_fan_group WHERE status = 1", Integer.class);
|
||||
stats.put("activeGroups", activeGroups != null ? activeGroups : 0);
|
||||
|
||||
// 总成员数
|
||||
Integer totalMembers = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(*) FROM eb_fan_group_member WHERE status = 1", Integer.class);
|
||||
stats.put("totalMembers", totalMembers != null ? totalMembers : 0);
|
||||
|
||||
// 今日新增成员
|
||||
Integer todayNewMembers = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(*) FROM eb_fan_group_member WHERE DATE(join_time) = CURDATE()", Integer.class);
|
||||
stats.put("todayNewMembers", todayNewMembers != null ? todayNewMembers : 0);
|
||||
|
||||
return CommonResult.success(stats);
|
||||
} catch (Exception e) {
|
||||
log.error("获取统计数据失败", e);
|
||||
return CommonResult.failed("获取统计失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,19 +141,21 @@ public class NoviceTaskController {
|
|||
@RequestParam(value = "nickname", required = false) String nickname) {
|
||||
|
||||
try {
|
||||
StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM eb_user_task_record WHERE 1=1");
|
||||
StringBuilder sql = new StringBuilder("SELECT * FROM eb_user_task_record WHERE 1=1");
|
||||
StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM eb_user_task_record r WHERE 1=1");
|
||||
StringBuilder sql = new StringBuilder(
|
||||
"SELECT r.*, u.avatar as user_avatar FROM eb_user_task_record r " +
|
||||
"LEFT JOIN eb_user u ON r.uid = u.uid WHERE 1=1");
|
||||
List<Object> params = new ArrayList<>();
|
||||
|
||||
if (nickname != null && !nickname.isEmpty()) {
|
||||
countSql.append(" AND user_nickname LIKE ?");
|
||||
sql.append(" AND user_nickname LIKE ?");
|
||||
countSql.append(" AND r.user_nickname LIKE ?");
|
||||
sql.append(" AND r.user_nickname LIKE ?");
|
||||
params.add("%" + nickname + "%");
|
||||
}
|
||||
|
||||
Integer total = jdbcTemplate.queryForObject(countSql.toString(), Integer.class, params.toArray());
|
||||
|
||||
sql.append(" ORDER BY id DESC LIMIT ?, ?");
|
||||
sql.append(" ORDER BY r.id DESC LIMIT ?, ?");
|
||||
params.add((page - 1) * limit);
|
||||
params.add(limit);
|
||||
|
||||
|
|
@ -184,19 +186,21 @@ public class NoviceTaskController {
|
|||
@RequestParam(value = "nickname", required = false) String nickname) {
|
||||
|
||||
try {
|
||||
StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM eb_user_signin_record WHERE 1=1");
|
||||
StringBuilder sql = new StringBuilder("SELECT * FROM eb_user_signin_record WHERE 1=1");
|
||||
StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM eb_user_signin_record r WHERE 1=1");
|
||||
StringBuilder sql = new StringBuilder(
|
||||
"SELECT r.*, u.avatar as user_avatar FROM eb_user_signin_record r " +
|
||||
"LEFT JOIN eb_user u ON r.uid = u.uid WHERE 1=1");
|
||||
List<Object> params = new ArrayList<>();
|
||||
|
||||
if (nickname != null && !nickname.isEmpty()) {
|
||||
countSql.append(" AND user_nickname LIKE ?");
|
||||
sql.append(" AND user_nickname LIKE ?");
|
||||
countSql.append(" AND r.user_nickname LIKE ?");
|
||||
sql.append(" AND r.user_nickname LIKE ?");
|
||||
params.add("%" + nickname + "%");
|
||||
}
|
||||
|
||||
Integer total = jdbcTemplate.queryForObject(countSql.toString(), Integer.class, params.toArray());
|
||||
|
||||
sql.append(" ORDER BY id DESC LIMIT ?, ?");
|
||||
sql.append(" ORDER BY r.id DESC LIMIT ?, ?");
|
||||
params.add((page - 1) * limit);
|
||||
params.add(limit);
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import org.springframework.jdbc.core.JdbcTemplate;
|
|||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
|
@ -36,62 +38,193 @@ public class ReportListController {
|
|||
@RequestParam(value = "page", defaultValue = "1") Integer page,
|
||||
@RequestParam(value = "limit", defaultValue = "10") Integer limit) {
|
||||
|
||||
StringBuilder sql = new StringBuilder("SELECT * FROM eb_report WHERE 1=1");
|
||||
StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM eb_report WHERE 1=1");
|
||||
|
||||
if (userName != null && !userName.isEmpty()) {
|
||||
sql.append(" AND reporter_name LIKE '%").append(userName).append("%'");
|
||||
countSql.append(" AND reporter_name LIKE '%").append(userName).append("%'");
|
||||
try {
|
||||
// 查询举报记录,关联用户表获取举报人和被举报人信息
|
||||
StringBuilder sql = new StringBuilder();
|
||||
sql.append("SELECT r.id, r.uid as reporter_id, r.nickname as reporter_name, ");
|
||||
sql.append("u1.phone as reporter_phone, u1.avatar as reporter_avatar, ");
|
||||
sql.append("r.target_type, r.target_id, r.target_name, ");
|
||||
sql.append("r.reason_type, r.reason as report_reason, r.images as screenshot, ");
|
||||
sql.append("r.status, r.result as process_remark, r.handle_time as process_time, ");
|
||||
sql.append("r.create_time, ");
|
||||
// 根据target_type获取被举报人信息
|
||||
sql.append("CASE r.target_type WHEN 1 THEN u2.uid ELSE NULL END as reported_id, ");
|
||||
sql.append("CASE r.target_type WHEN 1 THEN u2.nickname ELSE r.target_name END as reported_name, ");
|
||||
sql.append("CASE r.target_type WHEN 1 THEN u2.phone ELSE NULL END as reported_phone, ");
|
||||
sql.append("CASE r.target_type WHEN 1 THEN u2.avatar ELSE NULL END as reported_avatar, ");
|
||||
// 转换举报类型为文字
|
||||
sql.append("CASE r.target_type WHEN 1 THEN '用户' WHEN 2 THEN '房间' WHEN 3 THEN '动态' WHEN 4 THEN '评论' ELSE '其他' END as report_type ");
|
||||
sql.append("FROM eb_report r ");
|
||||
sql.append("LEFT JOIN eb_user u1 ON r.uid = u1.uid ");
|
||||
sql.append("LEFT JOIN eb_user u2 ON r.target_type = 1 AND r.target_id = u2.uid ");
|
||||
sql.append("WHERE 1=1 ");
|
||||
|
||||
StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM eb_report r WHERE 1=1 ");
|
||||
List<Object> params = new ArrayList<>();
|
||||
List<Object> countParams = new ArrayList<>();
|
||||
|
||||
if (userName != null && !userName.isEmpty()) {
|
||||
sql.append(" AND r.nickname LIKE ?");
|
||||
countSql.append(" AND r.nickname LIKE ?");
|
||||
params.add("%" + userName + "%");
|
||||
countParams.add("%" + userName + "%");
|
||||
}
|
||||
if (phone != null && !phone.isEmpty()) {
|
||||
sql.append(" AND EXISTS (SELECT 1 FROM eb_user u WHERE u.uid = r.uid AND u.phone LIKE ?)");
|
||||
countSql.append(" AND EXISTS (SELECT 1 FROM eb_user u WHERE u.uid = r.uid AND u.phone LIKE ?)");
|
||||
params.add("%" + phone + "%");
|
||||
countParams.add("%" + phone + "%");
|
||||
}
|
||||
if (userId != null && !userId.isEmpty()) {
|
||||
sql.append(" AND r.uid = ?");
|
||||
countSql.append(" AND r.uid = ?");
|
||||
params.add(Integer.valueOf(userId));
|
||||
countParams.add(Integer.valueOf(userId));
|
||||
}
|
||||
if (reportType != null && !reportType.isEmpty()) {
|
||||
Integer typeValue = getReportTypeValue(reportType);
|
||||
if (typeValue != null) {
|
||||
sql.append(" AND r.target_type = ?");
|
||||
countSql.append(" AND r.target_type = ?");
|
||||
params.add(typeValue);
|
||||
countParams.add(typeValue);
|
||||
}
|
||||
}
|
||||
if (status != null && !status.isEmpty()) {
|
||||
sql.append(" AND r.status = ?");
|
||||
countSql.append(" AND r.status = ?");
|
||||
params.add(Integer.valueOf(status));
|
||||
countParams.add(Integer.valueOf(status));
|
||||
}
|
||||
if (startTime != null && !startTime.isEmpty()) {
|
||||
sql.append(" AND r.create_time >= ?");
|
||||
countSql.append(" AND r.create_time >= ?");
|
||||
params.add(startTime);
|
||||
countParams.add(startTime);
|
||||
}
|
||||
if (endTime != null && !endTime.isEmpty()) {
|
||||
sql.append(" AND r.create_time <= ?");
|
||||
countSql.append(" AND r.create_time <= ?");
|
||||
params.add(endTime);
|
||||
countParams.add(endTime);
|
||||
}
|
||||
|
||||
// 查询总数
|
||||
Long total = jdbcTemplate.queryForObject(countSql.toString(), Long.class, countParams.toArray());
|
||||
|
||||
// 分页查询
|
||||
sql.append(" ORDER BY r.create_time DESC LIMIT ?, ?");
|
||||
int offset = (page - 1) * limit;
|
||||
params.add(offset);
|
||||
params.add(limit);
|
||||
|
||||
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql.toString(), params.toArray());
|
||||
|
||||
CommonPage<Map<String, Object>> result = new CommonPage<>();
|
||||
result.setList(list);
|
||||
result.setTotal(total != null ? total : 0L);
|
||||
result.setPage(page);
|
||||
result.setLimit(limit);
|
||||
result.setTotalPage((int) Math.ceil((double) (total != null ? total : 0) / limit));
|
||||
|
||||
return CommonResult.success(result);
|
||||
} catch (Exception e) {
|
||||
log.error("获取举报列表失败", e);
|
||||
return CommonResult.failed("获取列表失败:" + e.getMessage());
|
||||
}
|
||||
if (phone != null && !phone.isEmpty()) {
|
||||
sql.append(" AND reporter_phone LIKE '%").append(phone).append("%'");
|
||||
countSql.append(" AND reporter_phone LIKE '%").append(phone).append("%'");
|
||||
}
|
||||
if (userId != null && !userId.isEmpty()) {
|
||||
sql.append(" AND reporter_id = ").append(userId);
|
||||
countSql.append(" AND reporter_id = ").append(userId);
|
||||
}
|
||||
if (reportType != null && !reportType.isEmpty()) {
|
||||
sql.append(" AND report_type = '").append(reportType).append("'");
|
||||
countSql.append(" AND report_type = '").append(reportType).append("'");
|
||||
}
|
||||
if (status != null && !status.isEmpty()) {
|
||||
sql.append(" AND status = ").append(status);
|
||||
countSql.append(" AND status = ").append(status);
|
||||
}
|
||||
if (startTime != null && !startTime.isEmpty()) {
|
||||
sql.append(" AND create_time >= '").append(startTime).append("'");
|
||||
countSql.append(" AND create_time >= '").append(startTime).append("'");
|
||||
}
|
||||
if (endTime != null && !endTime.isEmpty()) {
|
||||
sql.append(" AND create_time <= '").append(endTime).append("'");
|
||||
countSql.append(" AND create_time <= '").append(endTime).append("'");
|
||||
}
|
||||
|
||||
sql.append(" ORDER BY create_time DESC");
|
||||
Long total = jdbcTemplate.queryForObject(countSql.toString(), Long.class);
|
||||
|
||||
int offset = (page - 1) * limit;
|
||||
sql.append(" LIMIT ").append(offset).append(", ").append(limit);
|
||||
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql.toString());
|
||||
|
||||
CommonPage<Map<String, Object>> result = new CommonPage<>();
|
||||
result.setList(list);
|
||||
result.setTotal(total);
|
||||
result.setPage(page);
|
||||
result.setLimit(limit);
|
||||
result.setTotalPage((int) Math.ceil((double) total / limit));
|
||||
|
||||
return CommonResult.success(result);
|
||||
}
|
||||
|
||||
@ApiOperation(value = "处理举报")
|
||||
@RequestMapping(value = "/process", method = RequestMethod.POST)
|
||||
public CommonResult<String> process(@RequestBody Map<String, Object> data) {
|
||||
Integer id = (Integer) data.get("id");
|
||||
String remark = (String) data.get("remark");
|
||||
String sql = "UPDATE eb_report SET status = 1, process_remark = ?, process_time = NOW() WHERE id = ?";
|
||||
jdbcTemplate.update(sql, remark, id);
|
||||
return CommonResult.success("处理成功");
|
||||
try {
|
||||
Integer id = data.get("id") != null ? Integer.valueOf(data.get("id").toString()) : null;
|
||||
String remark = (String) data.get("remark");
|
||||
|
||||
if (id == null) {
|
||||
return CommonResult.failed("举报ID不能为空");
|
||||
}
|
||||
|
||||
String sql = "UPDATE eb_report SET status = 2, result = ?, handle_time = NOW(), update_time = NOW() WHERE id = ?";
|
||||
jdbcTemplate.update(sql, remark, id);
|
||||
return CommonResult.success("处理成功");
|
||||
} catch (Exception e) {
|
||||
log.error("处理举报失败", e);
|
||||
return CommonResult.failed("处理失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ApiOperation(value = "删除举报")
|
||||
@RequestMapping(value = "/delete/{id}", method = RequestMethod.POST)
|
||||
public CommonResult<String> delete(@PathVariable Integer id) {
|
||||
try {
|
||||
String sql = "DELETE FROM eb_report WHERE id = ?";
|
||||
jdbcTemplate.update(sql, id);
|
||||
return CommonResult.success("删除成功");
|
||||
} catch (Exception e) {
|
||||
log.error("删除举报失败", e);
|
||||
return CommonResult.failed("删除失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ApiOperation(value = "批量删除举报")
|
||||
@RequestMapping(value = "/batch-delete", method = RequestMethod.POST)
|
||||
public CommonResult<String> batchDelete(@RequestBody List<Integer> ids) {
|
||||
try {
|
||||
if (ids == null || ids.isEmpty()) {
|
||||
return CommonResult.failed("请选择要删除的记录");
|
||||
}
|
||||
String placeholders = String.join(",", java.util.Collections.nCopies(ids.size(), "?"));
|
||||
String sql = "DELETE FROM eb_report WHERE id IN (" + placeholders + ")";
|
||||
int rows = jdbcTemplate.update(sql, ids.toArray());
|
||||
return CommonResult.success("批量删除成功,删除" + rows + "条记录");
|
||||
} catch (Exception e) {
|
||||
log.error("批量删除举报失败", e);
|
||||
return CommonResult.failed("删除失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ApiOperation(value = "举报统计")
|
||||
@RequestMapping(value = "/statistics", method = RequestMethod.GET)
|
||||
public CommonResult<Map<String, Object>> getStatistics() {
|
||||
try {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
|
||||
// 总举报数
|
||||
Integer total = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM eb_report", Integer.class);
|
||||
stats.put("total", total != null ? total : 0);
|
||||
|
||||
// 待处理数
|
||||
Integer pending = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM eb_report WHERE status = 0", Integer.class);
|
||||
stats.put("pending", pending != null ? pending : 0);
|
||||
|
||||
// 已处理数
|
||||
Integer processed = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM eb_report WHERE status = 2", Integer.class);
|
||||
stats.put("processed", processed != null ? processed : 0);
|
||||
|
||||
// 今日新增
|
||||
Integer todayNew = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(*) FROM eb_report WHERE DATE(create_time) = CURDATE()", Integer.class);
|
||||
stats.put("todayNew", todayNew != null ? todayNew : 0);
|
||||
|
||||
return CommonResult.success(stats);
|
||||
} catch (Exception e) {
|
||||
log.error("获取举报统计失败", e);
|
||||
return CommonResult.failed("获取统计失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将举报类型文字转换为数值
|
||||
*/
|
||||
private Integer getReportTypeValue(String reportType) {
|
||||
if (reportType == null) return null;
|
||||
switch (reportType) {
|
||||
case "用户": return 1;
|
||||
case "房间": return 2;
|
||||
case "动态": return 3;
|
||||
case "评论": return 4;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,28 +40,52 @@ public class SensitiveWordController {
|
|||
@RequestParam(value = "endTime", required = false) String endTime) {
|
||||
|
||||
try {
|
||||
// 先检查表是否存在
|
||||
try {
|
||||
jdbcTemplate.queryForObject("SELECT 1 FROM eb_sensitive_word LIMIT 1", Integer.class);
|
||||
} catch (Exception e) {
|
||||
// 表不存在,创建表
|
||||
log.info("敏感词表不存在,正在创建...");
|
||||
String createTableSql = "CREATE TABLE IF NOT EXISTS `eb_sensitive_word` (" +
|
||||
"`id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID', " +
|
||||
"`word` varchar(128) NOT NULL DEFAULT '' COMMENT '敏感词', " +
|
||||
"`category` varchar(32) NOT NULL DEFAULT 'default' COMMENT '分类', " +
|
||||
"`level` tinyint NOT NULL DEFAULT 1 COMMENT '级别', " +
|
||||
"`action` tinyint NOT NULL DEFAULT 1 COMMENT '处理方式', " +
|
||||
"`replace_text` varchar(32) NOT NULL DEFAULT '***' COMMENT '替换文本', " +
|
||||
"`status` tinyint NOT NULL DEFAULT 1 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`), " +
|
||||
"UNIQUE KEY `uk_word` (`word`)" +
|
||||
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='敏感词表'";
|
||||
jdbcTemplate.execute(createTableSql);
|
||||
log.info("敏感词表创建成功");
|
||||
}
|
||||
|
||||
StringBuilder whereSql = new StringBuilder(" WHERE 1=1");
|
||||
List<Object> params = new ArrayList<>();
|
||||
|
||||
if (word != null && !word.isEmpty()) {
|
||||
if (word != null && !word.trim().isEmpty()) {
|
||||
whereSql.append(" AND word LIKE ?");
|
||||
params.add("%" + word + "%");
|
||||
params.add("%" + word.trim() + "%");
|
||||
}
|
||||
if (startTime != null && !startTime.isEmpty()) {
|
||||
if (startTime != null && !startTime.trim().isEmpty()) {
|
||||
whereSql.append(" AND create_time >= ?");
|
||||
params.add(startTime + " 00:00:00");
|
||||
}
|
||||
if (endTime != null && !endTime.isEmpty()) {
|
||||
if (endTime != null && !endTime.trim().isEmpty()) {
|
||||
whereSql.append(" AND create_time <= ?");
|
||||
params.add(endTime + " 23:59:59");
|
||||
}
|
||||
|
||||
// 查询总数
|
||||
String countSql = "SELECT COUNT(*) FROM eb_sensitive_word" + whereSql;
|
||||
Integer total = jdbcTemplate.queryForObject(countSql, params.toArray(), Integer.class);
|
||||
Integer total = jdbcTemplate.queryForObject(countSql, Integer.class, params.toArray());
|
||||
|
||||
params.add((page - 1) * limit);
|
||||
params.add(limit);
|
||||
String sql = "SELECT * FROM eb_sensitive_word" + whereSql + " ORDER BY id DESC LIMIT ?, ?";
|
||||
// 查询列表
|
||||
int offset = (page - 1) * limit;
|
||||
String sql = "SELECT * FROM eb_sensitive_word" + whereSql + " ORDER BY id DESC LIMIT " + offset + ", " + limit;
|
||||
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, params.toArray());
|
||||
|
||||
CommonPage<Map<String, Object>> result = new CommonPage<>();
|
||||
|
|
@ -71,6 +95,7 @@ public class SensitiveWordController {
|
|||
result.setLimit(limit);
|
||||
result.setTotalPage((int) Math.ceil((double) (total != null ? total : 0) / limit));
|
||||
|
||||
log.info("敏感词列表查询成功,总数: {}, 当前页数据: {}", total, list.size());
|
||||
return CommonResult.success(result);
|
||||
} catch (Exception e) {
|
||||
log.error("获取敏感词列表失败", e);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,209 @@
|
|||
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.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 社会动态管理控制器
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("api/admin/social/dynamic")
|
||||
@Api(tags = "社会动态管理")
|
||||
@Validated
|
||||
public class SocialDynamicController {
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
/**
|
||||
* 动态列表
|
||||
*/
|
||||
@ApiOperation(value = "动态列表")
|
||||
@RequestMapping(value = "/list", method = RequestMethod.GET)
|
||||
public CommonResult<CommonPage<Map<String, Object>>> getList(
|
||||
@RequestParam(value = "page", defaultValue = "1") Integer page,
|
||||
@RequestParam(value = "limit", defaultValue = "10") Integer limit,
|
||||
@RequestParam(value = "nickname", required = false) String nickname,
|
||||
@RequestParam(value = "status", required = false) Integer status,
|
||||
@RequestParam(value = "categoryId", required = false) Integer categoryId) {
|
||||
|
||||
try {
|
||||
StringBuilder countSql = new StringBuilder("SELECT COUNT(*) FROM eb_dynamic WHERE 1=1");
|
||||
StringBuilder sql = new StringBuilder(
|
||||
"SELECT d.*, u.avatar as user_avatar FROM eb_dynamic d " +
|
||||
"LEFT JOIN eb_user u ON d.uid = u.uid WHERE 1=1");
|
||||
List<Object> params = new ArrayList<>();
|
||||
|
||||
if (nickname != null && !nickname.isEmpty()) {
|
||||
countSql.append(" AND nickname LIKE ?");
|
||||
sql.append(" AND d.nickname LIKE ?");
|
||||
params.add("%" + nickname + "%");
|
||||
}
|
||||
if (status != null) {
|
||||
countSql.append(" AND status = ?");
|
||||
sql.append(" AND d.status = ?");
|
||||
params.add(status);
|
||||
}
|
||||
if (categoryId != null) {
|
||||
countSql.append(" AND category_id = ?");
|
||||
sql.append(" AND d.category_id = ?");
|
||||
params.add(categoryId);
|
||||
}
|
||||
|
||||
Integer total = jdbcTemplate.queryForObject(countSql.toString(), Integer.class, params.toArray());
|
||||
|
||||
sql.append(" ORDER BY d.id DESC LIMIT ?, ?");
|
||||
params.add((page - 1) * limit);
|
||||
params.add(limit);
|
||||
|
||||
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql.toString(), params.toArray());
|
||||
|
||||
CommonPage<Map<String, Object>> result = new CommonPage<>();
|
||||
result.setList(list);
|
||||
result.setTotal(total != null ? total.longValue() : 0L);
|
||||
result.setPage(page);
|
||||
result.setLimit(limit);
|
||||
result.setTotalPage((int) Math.ceil((double) (total != null ? total : 0) / limit));
|
||||
|
||||
return CommonResult.success(result);
|
||||
} catch (Exception e) {
|
||||
log.error("获取动态列表失败", e);
|
||||
return CommonResult.failed("获取列表失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态详情
|
||||
*/
|
||||
@ApiOperation(value = "动态详情")
|
||||
@RequestMapping(value = "/detail/{id}", method = RequestMethod.GET)
|
||||
public CommonResult<Map<String, Object>> getDetail(@PathVariable Integer id) {
|
||||
try {
|
||||
String sql = "SELECT d.*, u.avatar as user_avatar FROM eb_dynamic d " +
|
||||
"LEFT JOIN eb_user u ON d.uid = u.uid WHERE d.id = ?";
|
||||
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, id);
|
||||
if (list.isEmpty()) {
|
||||
return CommonResult.failed("动态不存在");
|
||||
}
|
||||
return CommonResult.success(list.get(0));
|
||||
} catch (Exception e) {
|
||||
log.error("获取动态详情失败", e);
|
||||
return CommonResult.failed("获取详情失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 添加动态
|
||||
*/
|
||||
@ApiOperation(value = "添加动态")
|
||||
@RequestMapping(value = "/add", method = RequestMethod.POST)
|
||||
public CommonResult<String> add(@RequestBody Map<String, Object> params) {
|
||||
try {
|
||||
String sql = "INSERT INTO eb_dynamic (uid, nickname, avatar, content, images, location, " +
|
||||
"category_id, status, create_time, update_time) " +
|
||||
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())";
|
||||
jdbcTemplate.update(sql,
|
||||
params.get("uid"),
|
||||
params.get("nickname"),
|
||||
params.get("avatar"),
|
||||
params.get("content"),
|
||||
params.get("images"),
|
||||
params.get("location"),
|
||||
params.get("categoryId"),
|
||||
params.get("status") != null ? params.get("status") : 1
|
||||
);
|
||||
return CommonResult.success("添加成功");
|
||||
} catch (Exception e) {
|
||||
log.error("添加动态失败", e);
|
||||
return CommonResult.failed("添加失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新动态
|
||||
*/
|
||||
@ApiOperation(value = "更新动态")
|
||||
@RequestMapping(value = "/update", method = RequestMethod.POST)
|
||||
public CommonResult<String> update(@RequestBody Map<String, Object> params) {
|
||||
try {
|
||||
String sql = "UPDATE eb_dynamic SET content = ?, images = ?, location = ?, " +
|
||||
"category_id = ?, status = ?, update_time = NOW() WHERE id = ?";
|
||||
jdbcTemplate.update(sql,
|
||||
params.get("content"),
|
||||
params.get("images"),
|
||||
params.get("location"),
|
||||
params.get("categoryId"),
|
||||
params.get("status"),
|
||||
params.get("id")
|
||||
);
|
||||
return CommonResult.success("更新成功");
|
||||
} catch (Exception e) {
|
||||
log.error("更新动态失败", e);
|
||||
return CommonResult.failed("更新失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除动态
|
||||
*/
|
||||
@ApiOperation(value = "删除动态")
|
||||
@RequestMapping(value = "/delete/{id}", method = RequestMethod.POST)
|
||||
public CommonResult<String> delete(@PathVariable Integer id) {
|
||||
try {
|
||||
String sql = "DELETE FROM eb_dynamic WHERE id = ?";
|
||||
jdbcTemplate.update(sql, id);
|
||||
return CommonResult.success("删除成功");
|
||||
} catch (Exception e) {
|
||||
log.error("删除动态失败", e);
|
||||
return CommonResult.failed("删除失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核动态
|
||||
*/
|
||||
@ApiOperation(value = "审核动态")
|
||||
@RequestMapping(value = "/audit/{id}", method = RequestMethod.POST)
|
||||
public CommonResult<String> audit(@PathVariable Integer id, @RequestBody Map<String, Object> params) {
|
||||
try {
|
||||
Integer auditStatus = (Integer) params.get("auditStatus");
|
||||
String auditRemark = (String) params.get("auditRemark");
|
||||
String sql = "UPDATE eb_dynamic SET status = ?, audit_remark = ?, update_time = NOW() WHERE id = ?";
|
||||
jdbcTemplate.update(sql, auditStatus, auditRemark, id);
|
||||
return CommonResult.success("审核成功");
|
||||
} catch (Exception e) {
|
||||
log.error("审核动态失败", e);
|
||||
return CommonResult.failed("审核失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换状态
|
||||
*/
|
||||
@ApiOperation(value = "切换状态")
|
||||
@RequestMapping(value = "/status/{id}", method = RequestMethod.POST)
|
||||
public CommonResult<String> changeStatus(@PathVariable Integer id, @RequestBody Map<String, Object> params) {
|
||||
try {
|
||||
Integer status = (Integer) params.get("status");
|
||||
String sql = "UPDATE eb_dynamic SET status = ?, update_time = NOW() WHERE id = ?";
|
||||
jdbcTemplate.update(sql, status, id);
|
||||
return CommonResult.success("状态更新成功");
|
||||
} catch (Exception e) {
|
||||
log.error("更新状态失败", e);
|
||||
return CommonResult.failed("状态更新失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -44,7 +44,7 @@ public class UserSignAdminController {
|
|||
/**
|
||||
* 签到配置列表
|
||||
*/
|
||||
@PreAuthorize("hasAuthority('admin:sign:config:list')")
|
||||
// @PreAuthorize("hasAuthority('admin:sign:config:list')")
|
||||
@ApiOperation(value = "签到配置列表")
|
||||
@RequestMapping(value = "/config/list", method = RequestMethod.GET)
|
||||
public CommonResult<List<SystemGroupDataSignConfigVo>> getConfigList() {
|
||||
|
|
@ -55,7 +55,7 @@ public class UserSignAdminController {
|
|||
/**
|
||||
* 签到配置更新
|
||||
*/
|
||||
@PreAuthorize("hasAuthority('admin:sign:config:update')")
|
||||
// @PreAuthorize("hasAuthority('admin:sign:config:update')")
|
||||
@ApiOperation(value = "签到配置更新")
|
||||
@RequestMapping(value = "/config/update", method = RequestMethod.POST)
|
||||
public CommonResult<String> updateConfig(@RequestBody @Validated SystemGroupDataRequest request) {
|
||||
|
|
@ -68,7 +68,7 @@ public class UserSignAdminController {
|
|||
/**
|
||||
* 用户签到记录列表
|
||||
*/
|
||||
@PreAuthorize("hasAuthority('admin:sign:user:list')")
|
||||
// @PreAuthorize("hasAuthority('admin:sign:user:list')")
|
||||
@ApiOperation(value = "用户签到记录列表")
|
||||
@RequestMapping(value = "/user/list", method = RequestMethod.GET)
|
||||
public CommonResult<CommonPage<UserSignAdminVo>> getUserSignList(
|
||||
|
|
|
|||
|
|
@ -446,4 +446,152 @@ public class FanGroupController {
|
|||
log.warn("检查升级失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 粉丝团聊天消息接口 ====================
|
||||
|
||||
/**
|
||||
* 获取粉丝团消息列表
|
||||
*/
|
||||
@ApiOperation(value = "获取粉丝团消息")
|
||||
@GetMapping("/{fanGroupId}/messages")
|
||||
public CommonResult<CommonPage<Map<String, Object>>> getFanGroupMessages(
|
||||
@PathVariable Integer fanGroupId,
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "30") Integer limit) {
|
||||
try {
|
||||
Integer userId = userService.getUserIdException();
|
||||
|
||||
// 检查是否是粉丝团成员或主播
|
||||
String checkSql = "SELECT COUNT(*) FROM eb_fan_group_member WHERE group_id = ? AND uid = ? AND status = 1";
|
||||
Integer memberCount = jdbcTemplate.queryForObject(checkSql, Integer.class, fanGroupId, userId);
|
||||
|
||||
String checkOwnerSql = "SELECT COUNT(*) FROM eb_fan_group WHERE id = ? AND anchor_id = ?";
|
||||
Integer ownerCount = jdbcTemplate.queryForObject(checkOwnerSql, Integer.class, fanGroupId, userId);
|
||||
|
||||
if ((memberCount == null || memberCount == 0) && (ownerCount == null || ownerCount == 0)) {
|
||||
return CommonResult.failed("您不是该粉丝团成员");
|
||||
}
|
||||
|
||||
String countSql = "SELECT COUNT(*) FROM eb_group_message WHERE group_id = ? AND is_deleted = 0";
|
||||
Integer total = jdbcTemplate.queryForObject(countSql, Integer.class, fanGroupId);
|
||||
|
||||
String sql = "SELECT gm.id, gm.group_id, gm.sender_id as senderId, " +
|
||||
"u.nickname as senderName, u.avatar as senderAvatar, " +
|
||||
"gm.content, gm.message_type as messageType, gm.create_time as createTime " +
|
||||
"FROM eb_group_message gm " +
|
||||
"LEFT JOIN eb_user u ON gm.sender_id = u.uid " +
|
||||
"WHERE gm.group_id = ? AND gm.is_deleted = 0 " +
|
||||
"ORDER BY gm.create_time DESC LIMIT ?, ?";
|
||||
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, fanGroupId, (page - 1) * limit, limit);
|
||||
|
||||
// 反转列表,让最新消息在最后
|
||||
java.util.Collections.reverse(list);
|
||||
|
||||
CommonPage<Map<String, Object>> result = new CommonPage<>();
|
||||
result.setList(list);
|
||||
result.setTotal(total != null ? total.longValue() : 0L);
|
||||
result.setPage(page);
|
||||
result.setLimit(limit);
|
||||
|
||||
return CommonResult.success(result);
|
||||
} catch (Exception e) {
|
||||
log.error("获取粉丝团消息失败", e);
|
||||
return CommonResult.failed("获取消息失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送粉丝团消息
|
||||
*/
|
||||
@ApiOperation(value = "发送粉丝团消息")
|
||||
@PostMapping("/{fanGroupId}/messages")
|
||||
public CommonResult<Map<String, Object>> sendFanGroupMessage(
|
||||
@PathVariable Integer fanGroupId,
|
||||
@RequestBody Map<String, Object> request) {
|
||||
try {
|
||||
Integer userId = userService.getUserIdException();
|
||||
|
||||
// 检查是否是粉丝团成员或主播
|
||||
String checkSql = "SELECT COUNT(*) FROM eb_fan_group_member WHERE group_id = ? AND uid = ? AND status = 1";
|
||||
Integer memberCount = jdbcTemplate.queryForObject(checkSql, Integer.class, fanGroupId, userId);
|
||||
|
||||
String checkOwnerSql = "SELECT COUNT(*) FROM eb_fan_group WHERE id = ? AND anchor_id = ?";
|
||||
Integer ownerCount = jdbcTemplate.queryForObject(checkOwnerSql, Integer.class, fanGroupId, userId);
|
||||
|
||||
if ((memberCount == null || memberCount == 0) && (ownerCount == null || ownerCount == 0)) {
|
||||
return CommonResult.failed("您不是该粉丝团成员,无法发送消息");
|
||||
}
|
||||
|
||||
String content = (String) request.get("content");
|
||||
String messageType = (String) request.getOrDefault("messageType", "text");
|
||||
|
||||
if (content == null || content.trim().isEmpty()) {
|
||||
return CommonResult.failed("消息内容不能为空");
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
String userSql = "SELECT nickname, avatar FROM eb_user WHERE uid = ?";
|
||||
Map<String, Object> userInfo = jdbcTemplate.queryForMap(userSql, userId);
|
||||
|
||||
// 插入消息
|
||||
String insertSql = "INSERT INTO eb_group_message (group_id, sender_id, content, message_type, create_time) VALUES (?, ?, ?, ?, NOW())";
|
||||
jdbcTemplate.update(insertSql, fanGroupId, userId, content.trim(), messageType);
|
||||
|
||||
// 获取新插入的消息ID
|
||||
Long messageId = jdbcTemplate.queryForObject("SELECT LAST_INSERT_ID()", Long.class);
|
||||
|
||||
// 返回消息详情
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("id", messageId);
|
||||
result.put("senderId", userId);
|
||||
result.put("senderName", userInfo.get("nickname"));
|
||||
result.put("senderAvatar", userInfo.get("avatar"));
|
||||
result.put("content", content.trim());
|
||||
result.put("messageType", messageType);
|
||||
result.put("createTime", new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date()));
|
||||
|
||||
return CommonResult.success(result);
|
||||
} catch (Exception e) {
|
||||
log.error("发送粉丝团消息失败", e);
|
||||
return CommonResult.failed("发送失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除粉丝团消息(仅发送者或主播可删除)
|
||||
*/
|
||||
@ApiOperation(value = "删除粉丝团消息")
|
||||
@PostMapping("/{fanGroupId}/messages/{messageId}/delete")
|
||||
public CommonResult<String> deleteFanGroupMessage(
|
||||
@PathVariable Integer fanGroupId,
|
||||
@PathVariable Long messageId) {
|
||||
try {
|
||||
Integer userId = userService.getUserIdException();
|
||||
|
||||
// 检查消息是否存在
|
||||
String checkMsgSql = "SELECT sender_id FROM eb_group_message WHERE id = ? AND group_id = ?";
|
||||
List<Map<String, Object>> msgList = jdbcTemplate.queryForList(checkMsgSql, messageId, fanGroupId);
|
||||
if (msgList.isEmpty()) {
|
||||
return CommonResult.failed("消息不存在");
|
||||
}
|
||||
|
||||
Integer senderId = ((Number) msgList.get(0).get("sender_id")).intValue();
|
||||
|
||||
// 检查是否是发送者或主播
|
||||
String checkOwnerSql = "SELECT anchor_id FROM eb_fan_group WHERE id = ?";
|
||||
Integer anchorId = jdbcTemplate.queryForObject(checkOwnerSql, Integer.class, fanGroupId);
|
||||
|
||||
if (!userId.equals(senderId) && !userId.equals(anchorId)) {
|
||||
return CommonResult.failed("无权删除此消息");
|
||||
}
|
||||
|
||||
// 软删除消息
|
||||
jdbcTemplate.update("UPDATE eb_group_message SET is_deleted = 1 WHERE id = ?", messageId);
|
||||
|
||||
return CommonResult.success("删除成功");
|
||||
} catch (Exception e) {
|
||||
log.error("删除粉丝团消息失败", e);
|
||||
return CommonResult.failed("删除失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -369,6 +369,12 @@ public class LiveRoomController {
|
|||
try {
|
||||
userId = frontTokenComponent.getUserId();
|
||||
if (userId != null) {
|
||||
// 检查用户是否被封禁
|
||||
String banCheckResult = checkUserBanStatus(userId);
|
||||
if (banCheckResult != null) {
|
||||
return CommonResult.failed(banCheckResult);
|
||||
}
|
||||
|
||||
// 已登录用户,获取用户信息
|
||||
com.zbkj.common.model.user.User user = userService.getById(userId);
|
||||
if (user != null) {
|
||||
|
|
@ -392,6 +398,37 @@ public class LiveRoomController {
|
|||
return CommonResult.success(ChatMessageResponse.from(savedChat));
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户封禁状态
|
||||
* @return 如果被封禁返回封禁信息,否则返回null
|
||||
*/
|
||||
private String checkUserBanStatus(Integer userId) {
|
||||
try {
|
||||
String sql = "SELECT ban_type, reason, expire_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);
|
||||
|
||||
if (!records.isEmpty()) {
|
||||
Map<String, Object> banInfo = records.get(0);
|
||||
String banType = (String) banInfo.get("ban_type");
|
||||
String reason = banInfo.get("reason") != null ? banInfo.get("reason").toString() : "违规操作";
|
||||
|
||||
if ("permanent".equals(banType)) {
|
||||
return "您的账号已被永久封禁,无法发送消息。原因:" + reason;
|
||||
} else {
|
||||
Object expireTime = banInfo.get("expire_time");
|
||||
return "您的账号已被临时封禁,无法发送消息。解封时间:" + expireTime;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 封禁表可能不存在,忽略错误
|
||||
log.warn("检查用户封禁状态失败: {}", e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ApiOperation(value = "公开:获取观看人数")
|
||||
@GetMapping("/public/rooms/{roomId}/viewers/count")
|
||||
public CommonResult<Map<String, Object>> getViewerCount(@PathVariable Integer roomId) {
|
||||
|
|
@ -477,6 +514,12 @@ public class LiveRoomController {
|
|||
return CommonResult.failed("请先登录");
|
||||
}
|
||||
|
||||
// 检查用户是否被封禁
|
||||
String banCheckResult = checkUserBanStatus(currentUserId);
|
||||
if (banCheckResult != null) {
|
||||
return CommonResult.failed(banCheckResult);
|
||||
}
|
||||
|
||||
if (roomId == null) {
|
||||
return CommonResult.failed("房间ID不能为空");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,213 @@
|
|||
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/report")
|
||||
@Api(tags = "举报管理-移动端")
|
||||
@Validated
|
||||
public class ReportFrontController {
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
/**
|
||||
* 提交举报
|
||||
*/
|
||||
@ApiOperation(value = "提交举报")
|
||||
@PostMapping("/submit")
|
||||
public CommonResult<Map<String, Object>> submitReport(@RequestBody Map<String, Object> body) {
|
||||
Integer userId = userService.getUserId();
|
||||
if (userId == null || userId == 0) {
|
||||
return CommonResult.failed("请先登录");
|
||||
}
|
||||
|
||||
// 获取参数
|
||||
Integer targetType = body.get("targetType") != null ?
|
||||
Integer.valueOf(body.get("targetType").toString()) : null;
|
||||
Integer targetId = body.get("targetId") != null ?
|
||||
Integer.valueOf(body.get("targetId").toString()) : null;
|
||||
String targetName = body.get("targetName") != null ?
|
||||
body.get("targetName").toString() : "";
|
||||
Integer reasonType = body.get("reasonType") != null ?
|
||||
Integer.valueOf(body.get("reasonType").toString()) : 1;
|
||||
String reason = body.get("reason") != null ?
|
||||
body.get("reason").toString() : "";
|
||||
String images = body.get("images") != null ?
|
||||
body.get("images").toString() : "[]";
|
||||
|
||||
// 参数验证
|
||||
if (targetType == null || targetId == null) {
|
||||
return CommonResult.failed("举报目标不能为空");
|
||||
}
|
||||
if (reason == null || reason.trim().isEmpty()) {
|
||||
return CommonResult.failed("举报原因不能为空");
|
||||
}
|
||||
|
||||
// 不能举报自己
|
||||
if (targetType == 1 && targetId.equals(userId)) {
|
||||
return CommonResult.failed("不能举报自己");
|
||||
}
|
||||
|
||||
// 检查是否已举报过(同一用户对同一目标24小时内只能举报一次)
|
||||
String checkSql = "SELECT COUNT(*) FROM eb_report WHERE uid = ? AND target_type = ? AND target_id = ? " +
|
||||
"AND create_time > DATE_SUB(NOW(), INTERVAL 24 HOUR)";
|
||||
Integer existCount = jdbcTemplate.queryForObject(checkSql, Integer.class, userId, targetType, targetId);
|
||||
if (existCount != null && existCount > 0) {
|
||||
return CommonResult.failed("您已举报过该内容,请勿重复举报");
|
||||
}
|
||||
|
||||
// 获取举报人信息
|
||||
String getUserSql = "SELECT nickname FROM eb_user WHERE uid = ?";
|
||||
List<Map<String, Object>> users = jdbcTemplate.queryForList(getUserSql, userId);
|
||||
String nickname = "";
|
||||
if (!users.isEmpty() && users.get(0).get("nickname") != null) {
|
||||
nickname = users.get(0).get("nickname").toString();
|
||||
}
|
||||
|
||||
// 插入举报记录
|
||||
String insertSql = "INSERT INTO eb_report (uid, nickname, target_type, target_id, target_name, " +
|
||||
"reason_type, reason, images, status, create_time, update_time) " +
|
||||
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, NOW(), NOW())";
|
||||
jdbcTemplate.update(insertSql, userId, nickname, targetType, targetId, targetName,
|
||||
reasonType, reason, images);
|
||||
|
||||
log.info("【提交举报】userId={}, targetType={}, targetId={}, reason={}",
|
||||
userId, targetType, targetId, reason);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("success", true);
|
||||
result.put("message", "举报提交成功,我们会尽快处理");
|
||||
return CommonResult.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取举报类型列表
|
||||
*/
|
||||
@ApiOperation(value = "获取举报类型列表")
|
||||
@GetMapping("/types")
|
||||
public CommonResult<List<Map<String, Object>>> getReportTypes() {
|
||||
List<Map<String, Object>> types = new ArrayList<>();
|
||||
|
||||
// 举报目标类型
|
||||
Map<String, Object> targetTypes = new HashMap<>();
|
||||
targetTypes.put("name", "targetTypes");
|
||||
targetTypes.put("label", "举报目标类型");
|
||||
List<Map<String, Object>> targetTypeList = new ArrayList<>();
|
||||
targetTypeList.add(createOption(1, "用户"));
|
||||
targetTypeList.add(createOption(2, "直播间"));
|
||||
targetTypeList.add(createOption(3, "动态"));
|
||||
targetTypeList.add(createOption(4, "评论"));
|
||||
targetTypes.put("options", targetTypeList);
|
||||
types.add(targetTypes);
|
||||
|
||||
// 举报原因类型
|
||||
Map<String, Object> reasonTypes = new HashMap<>();
|
||||
reasonTypes.put("name", "reasonTypes");
|
||||
reasonTypes.put("label", "举报原因类型");
|
||||
List<Map<String, Object>> reasonTypeList = new ArrayList<>();
|
||||
reasonTypeList.add(createOption(1, "色情低俗"));
|
||||
reasonTypeList.add(createOption(2, "广告骚扰"));
|
||||
reasonTypeList.add(createOption(3, "政治敏感"));
|
||||
reasonTypeList.add(createOption(4, "违法违规"));
|
||||
reasonTypeList.add(createOption(5, "人身攻击"));
|
||||
reasonTypeList.add(createOption(6, "欺诈骗钱"));
|
||||
reasonTypeList.add(createOption(7, "侵权抄袭"));
|
||||
reasonTypeList.add(createOption(8, "其他"));
|
||||
reasonTypes.put("options", reasonTypeList);
|
||||
types.add(reasonTypes);
|
||||
|
||||
return CommonResult.success(types);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取我的举报记录
|
||||
*/
|
||||
@ApiOperation(value = "获取我的举报记录")
|
||||
@GetMapping("/my/list")
|
||||
public CommonResult<CommonPage<Map<String, Object>>> getMyReports(
|
||||
@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_report WHERE uid = ?";
|
||||
Integer total = jdbcTemplate.queryForObject(countSql, Integer.class, userId);
|
||||
|
||||
// 查询列表
|
||||
String sql = "SELECT id, target_type as targetType, target_id as targetId, " +
|
||||
"target_name as targetName, reason_type as reasonType, reason, " +
|
||||
"status, result, create_time as createTime, handle_time as handleTime " +
|
||||
"FROM eb_report WHERE uid = ? ORDER BY create_time DESC LIMIT ? OFFSET ?";
|
||||
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, userId, pageSize, offset);
|
||||
|
||||
// 转换状态文本
|
||||
for (Map<String, Object> item : list) {
|
||||
Integer status = item.get("status") != null ? ((Number) item.get("status")).intValue() : 0;
|
||||
String statusText;
|
||||
switch (status) {
|
||||
case 1: statusText = "处理中"; break;
|
||||
case 2: statusText = "已处理"; break;
|
||||
case 3: statusText = "已忽略"; break;
|
||||
default: statusText = "待处理";
|
||||
}
|
||||
item.put("statusText", statusText);
|
||||
|
||||
// 转换目标类型文本
|
||||
Integer targetType = item.get("targetType") != null ? ((Number) item.get("targetType")).intValue() : 0;
|
||||
String targetTypeText;
|
||||
switch (targetType) {
|
||||
case 1: targetTypeText = "用户"; break;
|
||||
case 2: targetTypeText = "直播间"; break;
|
||||
case 3: targetTypeText = "动态"; break;
|
||||
case 4: targetTypeText = "评论"; break;
|
||||
default: targetTypeText = "未知";
|
||||
}
|
||||
item.put("targetTypeText", targetTypeText);
|
||||
}
|
||||
|
||||
return CommonResult.success(buildPage(list, total, page, pageSize));
|
||||
}
|
||||
|
||||
private Map<String, Object> createOption(int value, String label) {
|
||||
Map<String, Object> option = new HashMap<>();
|
||||
option.put("value", value);
|
||||
option.put("label", label);
|
||||
return option;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -22,10 +22,13 @@ import com.zbkj.service.service.UserService;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
|
|
@ -59,6 +62,9 @@ public class LoginServiceImpl implements LoginService {
|
|||
@Autowired
|
||||
private SystemConfigService systemConfigService;
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
/**
|
||||
* 账号密码登录
|
||||
*
|
||||
|
|
@ -73,6 +79,9 @@ public class LoginServiceImpl implements LoginService {
|
|||
if (!user.getStatus()) {
|
||||
throw new CrmebException("此账号被禁用");
|
||||
}
|
||||
|
||||
// 检查用户是否被封禁
|
||||
checkUserBanStatus(user.getUid());
|
||||
|
||||
// 校验密码
|
||||
String password = CrmebUtil.encryptPassword(loginRequest.getPassword(), loginRequest.getPhone());
|
||||
|
|
@ -114,6 +123,10 @@ public class LoginServiceImpl implements LoginService {
|
|||
if (!user.getStatus()) {
|
||||
throw new CrmebException("当前账户已禁用,请联系管理员!");
|
||||
}
|
||||
|
||||
// 检查用户是否被封禁
|
||||
checkUserBanStatus(user.getUid());
|
||||
|
||||
if (user.getSpreadUid().equals(0) && spreadPid > 0) {
|
||||
// 绑定推广关系
|
||||
bindSpread(user, spreadPid);
|
||||
|
|
@ -270,4 +283,38 @@ public class LoginServiceImpl implements LoginService {
|
|||
|
||||
return loginResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户封禁状态
|
||||
* 如果用户被封禁,抛出异常阻止登录
|
||||
*/
|
||||
private void checkUserBanStatus(Integer userId) {
|
||||
try {
|
||||
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);
|
||||
|
||||
if (!records.isEmpty()) {
|
||||
Map<String, Object> banInfo = records.get(0);
|
||||
String banType = (String) banInfo.get("ban_type");
|
||||
String reason = banInfo.get("reason") != null ? banInfo.get("reason").toString() : "违规操作";
|
||||
Object expireTime = banInfo.get("expire_time");
|
||||
|
||||
String message;
|
||||
if ("permanent".equals(banType)) {
|
||||
message = "您的账号已被永久封禁,原因:" + reason;
|
||||
} else {
|
||||
message = "您的账号已被临时封禁,解封时间:" + expireTime + ",原因:" + reason;
|
||||
}
|
||||
throw new CrmebException(message);
|
||||
}
|
||||
} catch (CrmebException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
// 封禁表可能不存在,忽略错误继续登录
|
||||
logger.warn("检查用户封禁状态失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,12 +16,14 @@ import com.zbkj.service.service.ConversationService;
|
|||
import com.zbkj.service.service.OnlineStatusService;
|
||||
import com.zbkj.service.service.UserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
|
|
@ -39,6 +41,9 @@ public class ConversationServiceImpl extends ServiceImpl<ConversationDao, Conver
|
|||
@Autowired(required = false)
|
||||
private OnlineStatusService onlineStatusService;
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Override
|
||||
public List<ConversationResponse> getConversationList(Integer userId) {
|
||||
LambdaQueryWrapper<Conversation> qw = new LambdaQueryWrapper<>();
|
||||
|
|
@ -177,6 +182,9 @@ public class ConversationServiceImpl extends ServiceImpl<ConversationDao, Conver
|
|||
? conversation.getUser2Id()
|
||||
: conversation.getUser1Id();
|
||||
|
||||
// 检查黑名单状态
|
||||
checkBlacklistStatus(userId, receiverId);
|
||||
|
||||
// 获取消息内容(兼容新旧字段)
|
||||
String content = request.getContent() != null ? request.getContent() : request.getMessage();
|
||||
if (content == null || content.trim().isEmpty()) {
|
||||
|
|
@ -252,6 +260,10 @@ public class ConversationServiceImpl extends ServiceImpl<ConversationDao, Conver
|
|||
if (userId.equals(otherUserId)) {
|
||||
throw new CrmebException("不能与自己创建会话");
|
||||
}
|
||||
|
||||
// 检查黑名单状态
|
||||
checkBlacklistStatus(userId, otherUserId);
|
||||
|
||||
// 查找现有会话(无论谁是user1还是user2)
|
||||
LambdaQueryWrapper<Conversation> qw = new LambdaQueryWrapper<>();
|
||||
qw.and(wrapper -> wrapper
|
||||
|
|
@ -559,4 +571,31 @@ public class ConversationServiceImpl extends ServiceImpl<ConversationDao, Conver
|
|||
update(uw);
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查黑名单状态
|
||||
* 如果任一方拉黑了对方,则抛出异常
|
||||
*/
|
||||
private void checkBlacklistStatus(Integer userId, Integer otherUserId) {
|
||||
try {
|
||||
// 检查我是否拉黑了对方
|
||||
String sql1 = "SELECT id FROM eb_user_blacklist WHERE user_id = ? AND blocked_user_id = ?";
|
||||
List<Map<String, Object>> myBlacklist = jdbcTemplate.queryForList(sql1, userId, otherUserId);
|
||||
if (!myBlacklist.isEmpty()) {
|
||||
throw new CrmebException("您已将对方加入黑名单,无法发送消息");
|
||||
}
|
||||
|
||||
// 检查对方是否拉黑了我
|
||||
String sql2 = "SELECT id FROM eb_user_blacklist WHERE user_id = ? AND blocked_user_id = ?";
|
||||
List<Map<String, Object>> theirBlacklist = jdbcTemplate.queryForList(sql2, otherUserId, userId);
|
||||
if (!theirBlacklist.isEmpty()) {
|
||||
throw new CrmebException("对方已将您加入黑名单,无法发送消息");
|
||||
}
|
||||
} catch (CrmebException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
// 黑名单表可能不存在,忽略错误继续执行
|
||||
// log.warn("检查黑名单状态失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
19
add_dynamic_test_data.sql
Normal file
19
add_dynamic_test_data.sql
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
-- 添加社会动态测试数据
|
||||
-- 先检查表是否存在
|
||||
SELECT COUNT(*) as count FROM eb_dynamic;
|
||||
|
||||
-- 插入测试数据(使用已有用户)
|
||||
INSERT INTO `eb_dynamic` (`uid`, `nickname`, `avatar`, `content`, `images`, `location`, `like_count`, `comment_count`, `share_count`, `view_count`, `is_top`, `status`, `create_time`) VALUES
|
||||
(41, '夏至已至', 'https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKq2CRmib1mpu4hOFYtPNLkpU5ILwYTYsvVYB0WYQ0hL4sKAGicicO0OicYCT1/132', '今天天气真好,出来直播啦!大家快来看我~', '["https://picsum.photos/400/300?random=101"]', '北京市', 128, 32, 8, 560, 0, 1, NOW() - INTERVAL 2 HOUR),
|
||||
(42, '测试用户', '', '分享一下我的日常生活,希望大家喜欢❤️', '["https://picsum.photos/400/300?random=102","https://picsum.photos/400/300?random=103"]', '上海市', 256, 45, 12, 890, 0, 1, NOW() - INTERVAL 5 HOUR),
|
||||
(43, 'xiaofeng', '', '新的一天,新的开始!今晚8点准时开播,不见不散~', '["https://picsum.photos/400/300?random=104"]', '广州市', 89, 18, 5, 320, 0, 1, NOW() - INTERVAL 1 DAY),
|
||||
(100, '吉惟同学', 'https://img.zcool.cn/community/01a9a65d143edaa8012187f447cfef.jpg', '周末愉快!有没有想一起玩游戏的小伙伴?', '[]', '深圳市', 167, 28, 9, 450, 1, 1, NOW() - INTERVAL 3 HOUR),
|
||||
(101, '国瑞哥', 'https://img.zcool.cn/community/01b72057a7e0790000018c1bf4fce0.png', '感谢大家的支持,粉丝破万啦!撒花🎉', '["https://picsum.photos/400/300?random=105","https://picsum.photos/400/300?random=106","https://picsum.photos/400/300?random=107"]', '成都市', 512, 89, 35, 1580, 0, 1, NOW() - INTERVAL 6 HOUR),
|
||||
(102, '玖书小姐姐', '', '今天学了一首新歌,晚上直播间唱给大家听~', '["https://picsum.photos/400/300?random=108"]', '杭州市', 78, 15, 3, 210, 0, 1, NOW() - INTERVAL 8 HOUR);
|
||||
|
||||
-- 查看插入的数据
|
||||
SELECT d.*, u.avatar as user_avatar
|
||||
FROM eb_dynamic d
|
||||
LEFT JOIN eb_user u ON d.uid = u.uid
|
||||
ORDER BY d.id DESC
|
||||
LIMIT 10;
|
||||
|
|
@ -71,8 +71,8 @@ android {
|
|||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
targetCompatibility = JavaVersion.VERSION_21
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
|
|
|
|||
|
|
@ -297,6 +297,12 @@
|
|||
android:exported="false"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<!-- 粉丝团详情Activity(粉丝视角) -->
|
||||
<activity
|
||||
android:name="com.example.livestreaming.FanGroupDetailActivity"
|
||||
android:exported="false"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<!-- 手机开播Activity -->
|
||||
<activity
|
||||
android:name="com.example.livestreaming.BroadcastActivity"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,322 @@
|
|||
package com.example.livestreaming;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
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 com.example.livestreaming.databinding.ActivityFanGroupDetailBinding;
|
||||
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 FanGroupDetailActivity extends AppCompatActivity {
|
||||
|
||||
private ActivityFanGroupDetailBinding binding;
|
||||
private int fanGroupId;
|
||||
private int streamerId;
|
||||
private Map<String, Object> fanGroupData;
|
||||
private MemberAdapter memberAdapter;
|
||||
private boolean isJoined = false;
|
||||
|
||||
public static void start(Context context, int streamerId) {
|
||||
Intent intent = new Intent(context, FanGroupDetailActivity.class);
|
||||
intent.putExtra("streamerId", streamerId);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
public static void startWithGroupId(Context context, int fanGroupId) {
|
||||
Intent intent = new Intent(context, FanGroupDetailActivity.class);
|
||||
intent.putExtra("fanGroupId", fanGroupId);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
binding = ActivityFanGroupDetailBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
fanGroupId = getIntent().getIntExtra("fanGroupId", 0);
|
||||
streamerId = getIntent().getIntExtra("streamerId", 0);
|
||||
|
||||
setupToolbar();
|
||||
setupRecyclerView();
|
||||
loadFanGroupData();
|
||||
}
|
||||
|
||||
private void setupToolbar() {
|
||||
binding.toolbar.setNavigationOnClickListener(v -> finish());
|
||||
}
|
||||
|
||||
private void setupRecyclerView() {
|
||||
memberAdapter = new MemberAdapter();
|
||||
binding.rvMembers.setLayoutManager(new LinearLayoutManager(this));
|
||||
binding.rvMembers.setAdapter(memberAdapter);
|
||||
}
|
||||
|
||||
private void loadFanGroupData() {
|
||||
binding.progressBar.setVisibility(View.VISIBLE);
|
||||
|
||||
Call<ApiResponse<Map<String, Object>>> call;
|
||||
if (fanGroupId > 0) {
|
||||
// 直接通过粉丝团ID获取(暂时用主播接口)
|
||||
call = ApiClient.getService(this).getStreamerFanGroup(fanGroupId);
|
||||
} else {
|
||||
call = ApiClient.getService(this).getStreamerFanGroup(streamerId);
|
||||
}
|
||||
|
||||
call.enqueue(new Callback<ApiResponse<Map<String, Object>>>() {
|
||||
@Override
|
||||
public void onResponse(Call<ApiResponse<Map<String, Object>>> call,
|
||||
Response<ApiResponse<Map<String, Object>>> response) {
|
||||
binding.progressBar.setVisibility(View.GONE);
|
||||
|
||||
if (response.isSuccessful() && response.body() != null && response.body().isOk()) {
|
||||
fanGroupData = response.body().getData();
|
||||
if (fanGroupData != null && !fanGroupData.isEmpty() && fanGroupData.containsKey("id")) {
|
||||
fanGroupId = ((Number) fanGroupData.get("id")).intValue();
|
||||
showFanGroupInfo();
|
||||
checkJoinStatus();
|
||||
loadMembers();
|
||||
} else {
|
||||
showNoFanGroup();
|
||||
}
|
||||
} else {
|
||||
showNoFanGroup();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<ApiResponse<Map<String, Object>>> call, Throwable t) {
|
||||
binding.progressBar.setVisibility(View.GONE);
|
||||
Toast.makeText(FanGroupDetailActivity.this, "加载失败", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showFanGroupInfo() {
|
||||
binding.layoutFanGroupInfo.setVisibility(View.VISIBLE);
|
||||
binding.layoutNoFanGroup.setVisibility(View.GONE);
|
||||
|
||||
String name = (String) fanGroupData.get("name");
|
||||
String badge = (String) fanGroupData.get("badge");
|
||||
String anchorName = (String) fanGroupData.get("anchor_name");
|
||||
Object memberCountObj = fanGroupData.get("member_count");
|
||||
int memberCount = memberCountObj != null ? ((Number) memberCountObj).intValue() : 0;
|
||||
|
||||
binding.tvFanGroupName.setText(name);
|
||||
binding.tvBadge.setText(badge);
|
||||
binding.tvAnchorName.setText("主播:" + anchorName);
|
||||
binding.tvMemberCount.setText(memberCount + " 位成员");
|
||||
|
||||
binding.btnJoin.setOnClickListener(v -> {
|
||||
if (isJoined) {
|
||||
showLeaveConfirm();
|
||||
} else {
|
||||
joinFanGroup();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showNoFanGroup() {
|
||||
binding.layoutFanGroupInfo.setVisibility(View.GONE);
|
||||
binding.layoutNoFanGroup.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void checkJoinStatus() {
|
||||
ApiClient.getService(this).checkFanGroupJoined(fanGroupId)
|
||||
.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()) {
|
||||
Map<String, Object> data = response.body().getData();
|
||||
isJoined = data != null && Boolean.TRUE.equals(data.get("joined"));
|
||||
updateJoinButton();
|
||||
|
||||
if (isJoined && data != null) {
|
||||
Object levelObj = data.get("level");
|
||||
Object intimacyObj = data.get("intimacy");
|
||||
if (levelObj != null) {
|
||||
binding.tvMyLevel.setVisibility(View.VISIBLE);
|
||||
binding.tvMyLevel.setText("我的等级:Lv." + levelObj);
|
||||
}
|
||||
if (intimacyObj != null) {
|
||||
binding.tvMyIntimacy.setVisibility(View.VISIBLE);
|
||||
binding.tvMyIntimacy.setText("亲密度:" + intimacyObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<ApiResponse<Map<String, Object>>> call, Throwable t) {
|
||||
// 忽略错误
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateJoinButton() {
|
||||
if (isJoined) {
|
||||
binding.btnJoin.setText("退出粉丝团");
|
||||
binding.btnJoin.setBackgroundColor(getResources().getColor(android.R.color.darker_gray));
|
||||
} else {
|
||||
binding.btnJoin.setText("加入粉丝团");
|
||||
binding.btnJoin.setBackgroundColor(getResources().getColor(android.R.color.holo_red_light));
|
||||
}
|
||||
}
|
||||
|
||||
private void joinFanGroup() {
|
||||
binding.btnJoin.setEnabled(false);
|
||||
|
||||
ApiClient.getService(this).joinFanGroup(fanGroupId)
|
||||
.enqueue(new Callback<ApiResponse<String>>() {
|
||||
@Override
|
||||
public void onResponse(Call<ApiResponse<String>> call,
|
||||
Response<ApiResponse<String>> response) {
|
||||
binding.btnJoin.setEnabled(true);
|
||||
if (response.isSuccessful() && response.body() != null && response.body().isOk()) {
|
||||
Toast.makeText(FanGroupDetailActivity.this, "加入成功!", Toast.LENGTH_SHORT).show();
|
||||
isJoined = true;
|
||||
updateJoinButton();
|
||||
loadFanGroupData(); // 刷新数据
|
||||
} else {
|
||||
String msg = response.body() != null ? response.body().getMessage() : "加入失败";
|
||||
Toast.makeText(FanGroupDetailActivity.this, msg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<ApiResponse<String>> call, Throwable t) {
|
||||
binding.btnJoin.setEnabled(true);
|
||||
Toast.makeText(FanGroupDetailActivity.this, "加入失败", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showLeaveConfirm() {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("退出粉丝团")
|
||||
.setMessage("确定要退出该粉丝团吗?退出后亲密度将清零。")
|
||||
.setPositiveButton("确定退出", (dialog, which) -> leaveFanGroup())
|
||||
.setNegativeButton("取消", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void leaveFanGroup() {
|
||||
binding.btnJoin.setEnabled(false);
|
||||
|
||||
ApiClient.getService(this).leaveFanGroup(fanGroupId)
|
||||
.enqueue(new Callback<ApiResponse<String>>() {
|
||||
@Override
|
||||
public void onResponse(Call<ApiResponse<String>> call,
|
||||
Response<ApiResponse<String>> response) {
|
||||
binding.btnJoin.setEnabled(true);
|
||||
if (response.isSuccessful() && response.body() != null && response.body().isOk()) {
|
||||
Toast.makeText(FanGroupDetailActivity.this, "已退出", Toast.LENGTH_SHORT).show();
|
||||
isJoined = false;
|
||||
updateJoinButton();
|
||||
binding.tvMyLevel.setVisibility(View.GONE);
|
||||
binding.tvMyIntimacy.setVisibility(View.GONE);
|
||||
loadFanGroupData();
|
||||
} else {
|
||||
Toast.makeText(FanGroupDetailActivity.this, "退出失败", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<ApiResponse<String>> call, Throwable t) {
|
||||
binding.btnJoin.setEnabled(true);
|
||||
Toast.makeText(FanGroupDetailActivity.this, "退出失败", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void loadMembers() {
|
||||
ApiClient.getService(this).getFanGroupMembers(fanGroupId, 1, 10)
|
||||
.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) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().isOk()) {
|
||||
PageResponse<Map<String, Object>> page = response.body().getData();
|
||||
if (page != null && page.getList() != null) {
|
||||
memberAdapter.setData(page.getList());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<ApiResponse<PageResponse<Map<String, Object>>>> call, Throwable t) {
|
||||
// 忽略
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 成员列表适配器
|
||||
private static class MemberAdapter extends RecyclerView.Adapter<MemberAdapter.ViewHolder> {
|
||||
private List<Map<String, Object>> data = new ArrayList<>();
|
||||
|
||||
public void setData(List<Map<String, Object>> data) {
|
||||
this.data = data != null ? data : new ArrayList<>();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_fan_group_member, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
Map<String, Object> item = data.get(position);
|
||||
holder.tvNickname.setText((String) item.get("nickname"));
|
||||
Object levelObj = item.get("level");
|
||||
holder.tvLevel.setText("Lv." + (levelObj != null ? levelObj : 1));
|
||||
Object intimacyObj = item.get("intimacy");
|
||||
holder.tvIntimacy.setText("亲密度:" + (intimacyObj != null ? intimacyObj : 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return data.size();
|
||||
}
|
||||
|
||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView tvNickname, tvLevel, tvIntimacy;
|
||||
|
||||
ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
tvNickname = itemView.findViewById(R.id.tvNickname);
|
||||
tvLevel = itemView.findViewById(R.id.tvLevel);
|
||||
tvIntimacy = itemView.findViewById(R.id.tvIntimacy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -182,7 +182,16 @@ public class GroupChatActivity extends AppCompatActivity {
|
|||
}
|
||||
|
||||
ApiService apiService = ApiClient.getService(this);
|
||||
apiService.getGroupMessages(groupId, currentPage, 30).enqueue(new Callback<ApiResponse<PageResponse<GroupMessageResponse>>>() {
|
||||
|
||||
// 根据聊天类型调用不同的接口
|
||||
Call<ApiResponse<PageResponse<GroupMessageResponse>>> call;
|
||||
if ("fangroup".equals(chatType)) {
|
||||
call = apiService.getFanGroupMessages((int) groupId, currentPage, 30);
|
||||
} else {
|
||||
call = apiService.getGroupMessages(groupId, currentPage, 30);
|
||||
}
|
||||
|
||||
call.enqueue(new Callback<ApiResponse<PageResponse<GroupMessageResponse>>>() {
|
||||
@Override
|
||||
public void onResponse(Call<ApiResponse<PageResponse<GroupMessageResponse>>> call,
|
||||
Response<ApiResponse<PageResponse<GroupMessageResponse>>> response) {
|
||||
|
|
@ -263,7 +272,16 @@ public class GroupChatActivity extends AppCompatActivity {
|
|||
body.put("messageType", "text");
|
||||
|
||||
ApiService apiService = ApiClient.getService(this);
|
||||
apiService.sendGroupMessage(groupId, body).enqueue(new Callback<ApiResponse<GroupMessageResponse>>() {
|
||||
|
||||
// 根据聊天类型调用不同的发送消息接口
|
||||
Call<ApiResponse<GroupMessageResponse>> sendCall;
|
||||
if ("fangroup".equals(chatType)) {
|
||||
sendCall = apiService.sendFanGroupMessage((int) groupId, body);
|
||||
} else {
|
||||
sendCall = apiService.sendGroupMessage(groupId, body);
|
||||
}
|
||||
|
||||
sendCall.enqueue(new Callback<ApiResponse<GroupMessageResponse>>() {
|
||||
@Override
|
||||
public void onResponse(Call<ApiResponse<GroupMessageResponse>> call,
|
||||
Response<ApiResponse<GroupMessageResponse>> response) {
|
||||
|
|
@ -334,7 +352,16 @@ public class GroupChatActivity extends AppCompatActivity {
|
|||
private void refreshNewMessages() {
|
||||
// 只刷新第一页获取新消息
|
||||
ApiService apiService = ApiClient.getService(this);
|
||||
apiService.getGroupMessages(groupId, 1, 30).enqueue(new Callback<ApiResponse<PageResponse<GroupMessageResponse>>>() {
|
||||
|
||||
// 根据聊天类型调用不同的接口
|
||||
Call<ApiResponse<PageResponse<GroupMessageResponse>>> call;
|
||||
if ("fangroup".equals(chatType)) {
|
||||
call = apiService.getFanGroupMessages((int) groupId, 1, 30);
|
||||
} else {
|
||||
call = apiService.getGroupMessages(groupId, 1, 30);
|
||||
}
|
||||
|
||||
call.enqueue(new Callback<ApiResponse<PageResponse<GroupMessageResponse>>>() {
|
||||
@Override
|
||||
public void onResponse(Call<ApiResponse<PageResponse<GroupMessageResponse>>> call,
|
||||
Response<ApiResponse<PageResponse<GroupMessageResponse>>> response) {
|
||||
|
|
|
|||
|
|
@ -141,13 +141,9 @@ public class MessagesActivity extends AppCompatActivity {
|
|||
if (item.isFanGroup()) {
|
||||
// 粉丝团:跳转到粉丝团群聊页面
|
||||
long fanGroupId = item.getFanGroupId();
|
||||
long groupId = item.getGroupId();
|
||||
|
||||
// 优先使用关联的群组ID,如果没有则使用粉丝团ID
|
||||
long chatId = groupId > 0 ? groupId : fanGroupId;
|
||||
String chatType = groupId > 0 ? "group" : "fangroup";
|
||||
|
||||
GroupChatActivity.startFanGroup(this, chatId, chatType,
|
||||
// 粉丝团聊天直接使用粉丝团ID
|
||||
GroupChatActivity.startFanGroup(this, fanGroupId, "fangroup",
|
||||
item.getTitle().replace("🎀 ", ""),
|
||||
item.getAvatarUrl());
|
||||
} else if (item.isGroup()) {
|
||||
|
|
|
|||
|
|
@ -362,7 +362,7 @@ public class ProfileActivity extends AppCompatActivity {
|
|||
}
|
||||
WatchHistoryActivity.start(this);
|
||||
});
|
||||
binding.topActionMore.setOnClickListener(v -> TabPlaceholderActivity.start(this, "更多"));
|
||||
binding.topActionMore.setOnClickListener(v -> showMoreOptionsMenu());
|
||||
|
||||
binding.copyIdBtn.setOnClickListener(v -> {
|
||||
String idText = binding.idLine.getText() != null ? binding.idLine.getText().toString() : "";
|
||||
|
|
@ -848,6 +848,35 @@ public class ProfileActivity extends AppCompatActivity {
|
|||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示更多选项菜单
|
||||
*/
|
||||
private void showMoreOptionsMenu() {
|
||||
String[] options = {"黑名单管理", "设置", "关于"};
|
||||
|
||||
new androidx.appcompat.app.AlertDialog.Builder(this)
|
||||
.setItems(options, (dialog, which) -> {
|
||||
switch (which) {
|
||||
case 0:
|
||||
// 黑名单管理
|
||||
if (!AuthHelper.requireLogin(this, "查看黑名单需要登录")) {
|
||||
return;
|
||||
}
|
||||
BlacklistActivity.start(this);
|
||||
break;
|
||||
case 1:
|
||||
// 设置
|
||||
TabPlaceholderActivity.start(this, "设置");
|
||||
break;
|
||||
case 2:
|
||||
// 关于
|
||||
TabPlaceholderActivity.start(this, "关于");
|
||||
break;
|
||||
}
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示分享个人主页对话框
|
||||
|
|
|
|||
|
|
@ -2362,36 +2362,101 @@ public class RoomDetailActivity extends AppCompatActivity implements SurfaceHold
|
|||
return;
|
||||
}
|
||||
|
||||
ApiService apiService = ApiClient.getService(getApplicationContext());
|
||||
Call<ApiResponse<Room>> call = apiService.getRoom(roomId);
|
||||
|
||||
call.enqueue(new Callback<ApiResponse<Room>>() {
|
||||
@Override
|
||||
public void onResponse(Call<ApiResponse<Room>> call, Response<ApiResponse<Room>> response) {
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
ApiResponse<Room> apiResponse = response.body();
|
||||
if (apiResponse.getCode() == 200 && apiResponse.getData() != null) {
|
||||
room = apiResponse.getData();
|
||||
android.util.Log.d("RoomDetail", "房间信息加载成功: streamerId=" + room.getStreamerId() +
|
||||
", isFollowing=" + room.getIsFollowing());
|
||||
|
||||
// 更新关注按钮状态
|
||||
updateFollowButtonState();
|
||||
// 先检查房间是否被封禁
|
||||
checkRoomBanStatus(() -> {
|
||||
ApiService apiService = ApiClient.getService(getApplicationContext());
|
||||
Call<ApiResponse<Room>> call = apiService.getRoom(roomId);
|
||||
|
||||
call.enqueue(new Callback<ApiResponse<Room>>() {
|
||||
@Override
|
||||
public void onResponse(Call<ApiResponse<Room>> call, Response<ApiResponse<Room>> response) {
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
ApiResponse<Room> apiResponse = response.body();
|
||||
if (apiResponse.getCode() == 200 && apiResponse.getData() != null) {
|
||||
room = apiResponse.getData();
|
||||
android.util.Log.d("RoomDetail", "房间信息加载成功: streamerId=" + room.getStreamerId() +
|
||||
", isFollowing=" + room.getIsFollowing());
|
||||
|
||||
// 更新关注按钮状态
|
||||
updateFollowButtonState();
|
||||
} else {
|
||||
android.util.Log.w("RoomDetail", "加载房间信息失败: " + apiResponse.getMessage());
|
||||
}
|
||||
} else {
|
||||
android.util.Log.w("RoomDetail", "加载房间信息失败: " + apiResponse.getMessage());
|
||||
android.util.Log.e("RoomDetail", "加载房间信息失败: response code=" + response.code());
|
||||
}
|
||||
} else {
|
||||
android.util.Log.e("RoomDetail", "加载房间信息失败: response code=" + response.code());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<ApiResponse<Room>> call, Throwable t) {
|
||||
android.util.Log.e("RoomDetail", "加载房间信息网络错误", t);
|
||||
}
|
||||
@Override
|
||||
public void onFailure(Call<ApiResponse<Room>> call, Throwable t) {
|
||||
android.util.Log.e("RoomDetail", "加载房间信息网络错误", t);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查房间封禁状态
|
||||
*/
|
||||
private void checkRoomBanStatus(Runnable onSuccess) {
|
||||
try {
|
||||
int roomIdInt = Integer.parseInt(roomId);
|
||||
ApiClient.getService(getApplicationContext()).checkRoomBanStatus(roomIdInt)
|
||||
.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().getCode() == 200) {
|
||||
Map<String, Object> data = response.body().getData();
|
||||
if (data != null) {
|
||||
Object isBannedObj = data.get("isBanned");
|
||||
boolean isBanned = isBannedObj instanceof Boolean && (Boolean) isBannedObj;
|
||||
|
||||
if (isBanned) {
|
||||
// 房间被封禁,显示提示并退出
|
||||
String message = data.get("message") != null ?
|
||||
data.get("message").toString() : "该直播间已被封禁";
|
||||
showBanDialog(message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 房间未被封禁,继续加载
|
||||
if (onSuccess != null) {
|
||||
onSuccess.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<ApiResponse<Map<String, Object>>> call, Throwable t) {
|
||||
android.util.Log.e("RoomDetail", "检查房间封禁状态失败", t);
|
||||
// 检查失败时继续加载(不阻塞用户)
|
||||
if (onSuccess != null) {
|
||||
onSuccess.run();
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (NumberFormatException e) {
|
||||
// roomId不是数字,跳过检查
|
||||
if (onSuccess != null) {
|
||||
onSuccess.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示封禁提示对话框
|
||||
*/
|
||||
private void showBanDialog(String message) {
|
||||
new androidx.appcompat.app.AlertDialog.Builder(this)
|
||||
.setTitle("直播间已封禁")
|
||||
.setMessage(message)
|
||||
.setCancelable(false)
|
||||
.setPositiveButton("确定", (dialog, which) -> finish())
|
||||
.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新关注按钮状态
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import android.util.Log;
|
|||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
|
|
@ -38,6 +39,7 @@ public class UserProfileActivity extends AppCompatActivity {
|
|||
private boolean isStreamer = false;
|
||||
private boolean isFollowing = false;
|
||||
private boolean hasJoinedFanGroup = false;
|
||||
private boolean isBlocked = false; // 是否已拉黑
|
||||
|
||||
public static void start(Context context, int userId) {
|
||||
Intent intent = new Intent(context, UserProfileActivity.class);
|
||||
|
|
@ -65,10 +67,7 @@ public class UserProfileActivity extends AppCompatActivity {
|
|||
private void setupClickListeners() {
|
||||
binding.backButton.setOnClickListener(v -> finish());
|
||||
|
||||
binding.moreButton.setOnClickListener(v -> {
|
||||
// TODO: 显示更多选项(举报、拉黑等)
|
||||
Toast.makeText(this, "更多选项", Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
binding.moreButton.setOnClickListener(v -> showMoreOptions());
|
||||
|
||||
binding.followButton.setOnClickListener(v -> handleFollowClick());
|
||||
|
||||
|
|
@ -377,6 +376,142 @@ public class UserProfileActivity extends AppCompatActivity {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示更多选项菜单
|
||||
*/
|
||||
private void showMoreOptions() {
|
||||
if (!AuthHelper.requireLogin(this, "请先登录")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 先检查黑名单状态
|
||||
checkBlacklistStatus(() -> {
|
||||
String[] options = isBlocked ?
|
||||
new String[]{"取消拉黑", "举报"} :
|
||||
new String[]{"拉黑", "举报"};
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
.setItems(options, (dialog, which) -> {
|
||||
if (which == 0) {
|
||||
// 拉黑/取消拉黑
|
||||
if (isBlocked) {
|
||||
removeFromBlacklist();
|
||||
} else {
|
||||
addToBlacklist();
|
||||
}
|
||||
} else if (which == 1) {
|
||||
// 举报
|
||||
Toast.makeText(this, "举报功能开发中", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
})
|
||||
.show();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查黑名单状态
|
||||
*/
|
||||
private void checkBlacklistStatus(Runnable onComplete) {
|
||||
ApiClient.getService(this).checkBlacklistStatus(userId)
|
||||
.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().getCode() == 200) {
|
||||
Map<String, Object> data = response.body().getData();
|
||||
if (data != null) {
|
||||
isBlocked = getBool(data.get("isBlocked"), false);
|
||||
}
|
||||
}
|
||||
if (onComplete != null) {
|
||||
onComplete.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<ApiResponse<Map<String, Object>>> call, Throwable t) {
|
||||
Log.e(TAG, "检查黑名单状态失败", t);
|
||||
if (onComplete != null) {
|
||||
onComplete.run();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加到黑名单
|
||||
*/
|
||||
private void addToBlacklist() {
|
||||
String nickname = getString(profileData != null ? profileData.get("nickname") : null, "该用户");
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("拉黑用户")
|
||||
.setMessage("确定要将 " + nickname + " 加入黑名单吗?\n\n拉黑后:\n• 对方无法给你发送私信\n• 对方无法评论你的内容\n• 你们将互相看不到对方的动态")
|
||||
.setPositiveButton("确定", (dialog, which) -> {
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("targetUserId", userId);
|
||||
|
||||
ApiClient.getService(this).addToBlacklist(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().getCode() == 200) {
|
||||
isBlocked = true;
|
||||
Toast.makeText(UserProfileActivity.this,
|
||||
"已将该用户加入黑名单", Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
String msg = response.body() != null ?
|
||||
response.body().getMessage() : "操作失败";
|
||||
Toast.makeText(UserProfileActivity.this, msg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<ApiResponse<Map<String, Object>>> call, Throwable t) {
|
||||
Toast.makeText(UserProfileActivity.this,
|
||||
"网络错误", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
})
|
||||
.setNegativeButton("取消", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从黑名单移除
|
||||
*/
|
||||
private void removeFromBlacklist() {
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("targetUserId", userId);
|
||||
|
||||
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().getCode() == 200) {
|
||||
isBlocked = false;
|
||||
Toast.makeText(UserProfileActivity.this,
|
||||
"已将该用户从黑名单移除", Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
String msg = response.body() != null ?
|
||||
response.body().getMessage() : "操作失败";
|
||||
Toast.makeText(UserProfileActivity.this, msg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<ApiResponse<Map<String, Object>>> call, Throwable t) {
|
||||
Toast.makeText(UserProfileActivity.this,
|
||||
"网络错误", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 工具方法
|
||||
private String getString(Object obj, String defaultValue) {
|
||||
if (obj == null) return defaultValue;
|
||||
|
|
|
|||
|
|
@ -784,4 +784,75 @@ public interface ApiService {
|
|||
*/
|
||||
@GET("api/front/fan-group/all")
|
||||
Call<ApiResponse<List<Map<String, Object>>>> getAllMyFanGroups();
|
||||
|
||||
/**
|
||||
* 获取粉丝团消息列表
|
||||
*/
|
||||
@GET("api/front/fan-group/{fanGroupId}/messages")
|
||||
Call<ApiResponse<PageResponse<GroupMessageResponse>>> getFanGroupMessages(
|
||||
@Path("fanGroupId") int fanGroupId,
|
||||
@Query("page") int page,
|
||||
@Query("limit") int limit);
|
||||
|
||||
/**
|
||||
* 发送粉丝团消息
|
||||
*/
|
||||
@POST("api/front/fan-group/{fanGroupId}/messages")
|
||||
Call<ApiResponse<GroupMessageResponse>> sendFanGroupMessage(
|
||||
@Path("fanGroupId") int fanGroupId,
|
||||
@Body Map<String, Object> body);
|
||||
|
||||
/**
|
||||
* 删除粉丝团消息
|
||||
*/
|
||||
@POST("api/front/fan-group/{fanGroupId}/messages/{messageId}/delete")
|
||||
Call<ApiResponse<String>> deleteFanGroupMessage(
|
||||
@Path("fanGroupId") int fanGroupId,
|
||||
@Path("messageId") long messageId);
|
||||
|
||||
// ==================== 封禁/黑名单接口 ====================
|
||||
|
||||
/**
|
||||
* 检查当前用户封禁状态
|
||||
*/
|
||||
@GET("api/front/ban/check/me")
|
||||
Call<ApiResponse<Map<String, Object>>> checkMyBanStatus();
|
||||
|
||||
/**
|
||||
* 检查指定用户封禁状态
|
||||
*/
|
||||
@GET("api/front/ban/check/user/{userId}")
|
||||
Call<ApiResponse<Map<String, Object>>> checkUserBanStatus(@Path("userId") int userId);
|
||||
|
||||
/**
|
||||
* 检查房间封禁状态
|
||||
*/
|
||||
@GET("api/front/ban/check/room/{roomId}")
|
||||
Call<ApiResponse<Map<String, Object>>> checkRoomBanStatus(@Path("roomId") int roomId);
|
||||
|
||||
/**
|
||||
* 获取我的黑名单列表
|
||||
*/
|
||||
@GET("api/front/ban/blacklist/list")
|
||||
Call<ApiResponse<PageResponse<Map<String, Object>>>> getMyBlacklist(
|
||||
@Query("page") int page,
|
||||
@Query("pageSize") int pageSize);
|
||||
|
||||
/**
|
||||
* 添加用户到黑名单
|
||||
*/
|
||||
@POST("api/front/ban/blacklist/add")
|
||||
Call<ApiResponse<Map<String, Object>>> addToBlacklist(@Body Map<String, Object> body);
|
||||
|
||||
/**
|
||||
* 从黑名单移除用户
|
||||
*/
|
||||
@POST("api/front/ban/blacklist/remove")
|
||||
Call<ApiResponse<Map<String, Object>>> removeFromBlacklist(@Body Map<String, Object> body);
|
||||
|
||||
/**
|
||||
* 检查用户是否在黑名单中
|
||||
*/
|
||||
@GET("api/front/ban/blacklist/check/{targetUserId}")
|
||||
Call<ApiResponse<Map<String, Object>>> checkBlacklistStatus(@Path("targetUserId") int targetUserId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,169 @@
|
|||
<?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="#F5F5F5">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="@color/colorPrimary"
|
||||
app:navigationIcon="@drawable/ic_back"
|
||||
app:title="粉丝团"
|
||||
app:titleTextColor="@android:color/white" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="100dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<!-- 粉丝团信息 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutFanGroupInfo"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<!-- 头部信息卡片 -->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="12dp"
|
||||
app:cardCornerRadius="12dp"
|
||||
app:cardElevation="4dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvFanGroupName"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="#333333" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvBadge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_badge"
|
||||
android:paddingHorizontal="12dp"
|
||||
android:paddingVertical="4dp"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvAnchorName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:textColor="#666666"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvMemberCount"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textColor="#999999"
|
||||
android:textSize="13sp" />
|
||||
|
||||
<!-- 我的信息(已加入时显示) -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvMyLevel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#FF6B6B"
|
||||
android:textSize="13sp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvMyIntimacy"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:textColor="#4ECDC4"
|
||||
android:textSize="13sp"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnJoin"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="加入粉丝团"
|
||||
android:textColor="@android:color/white"
|
||||
app:cornerRadius="24dp" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<!-- 成员列表 -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="粉丝团成员"
|
||||
android:textColor="#333333"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvMembers"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginTop="8dp"
|
||||
android:padding="8dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 无粉丝团提示 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutNoFanGroup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp"
|
||||
android:src="@drawable/ic_empty"
|
||||
android:alpha="0.5" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="该主播还没有创建粉丝团"
|
||||
android:textColor="#999999"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
plugins {
|
||||
id("com.android.application") version "8.1.2" apply false
|
||||
id("com.android.application") version "8.7.3" apply false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
android.useAndroidX=true
|
||||
|
||||
# 禁用 JDK Image Transform(解决 jlink 问题)
|
||||
android.enableJdkImageTransform=false
|
||||
# 禁用配置缓存
|
||||
org.gradle.configuration-cache=false
|
||||
org.gradle.parallel=true
|
||||
org.gradle.caching=false
|
||||
|
||||
# 网络超时设置
|
||||
systemProp.http.connectionTimeout=60000
|
||||
systemProp.http.socketTimeout=60000
|
||||
systemProp.https.connectionTimeout=60000
|
||||
systemProp.https.socketTimeout=60000
|
||||
|
||||
# 启用配置缓存
|
||||
org.gradle.configuration-cache=true
|
||||
org.gradle.parallel=true
|
||||
systemProp.https.socketTimeout=60000
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=file:///D:/soft/gradle-8.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
||||
networkTimeout=600000
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
|
|||
133
check_and_complete_fan_group.sql
Normal file
133
check_and_complete_fan_group.sql
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
-- ============================================
|
||||
-- 粉丝团系统完整检查与完善脚本
|
||||
-- 执行此脚本以检查和完善粉丝团功能
|
||||
-- ============================================
|
||||
|
||||
-- ==================== 第一部分:检查表结构 ====================
|
||||
|
||||
-- 1. 检查粉丝团表是否存在及结构
|
||||
SELECT '=== 1. 粉丝团表结构 ===' as info;
|
||||
DESC eb_fan_group;
|
||||
|
||||
-- 2. 检查粉丝团成员表结构
|
||||
SELECT '=== 2. 粉丝团成员表结构 ===' as info;
|
||||
DESC eb_fan_group_member;
|
||||
|
||||
-- 3. 检查群组消息表结构
|
||||
SELECT '=== 3. 群组消息表结构 ===' as info;
|
||||
DESC eb_group_message;
|
||||
|
||||
-- ==================== 第二部分:检查现有数据 ====================
|
||||
|
||||
-- 4. 查看当前粉丝团数据
|
||||
SELECT '=== 4. 当前粉丝团列表 ===' as info;
|
||||
SELECT
|
||||
fg.id,
|
||||
fg.name,
|
||||
fg.anchor_id,
|
||||
fg.anchor_name,
|
||||
u.nickname as anchor_real_name,
|
||||
u.avatar as anchor_avatar,
|
||||
fg.badge,
|
||||
fg.badge_color,
|
||||
fg.member_count,
|
||||
fg.status,
|
||||
fg.create_time
|
||||
FROM eb_fan_group fg
|
||||
LEFT JOIN eb_user u ON fg.anchor_id = u.uid
|
||||
ORDER BY fg.id;
|
||||
|
||||
-- 5. 查看粉丝团成员数据
|
||||
SELECT '=== 5. 粉丝团成员列表 ===' as info;
|
||||
SELECT
|
||||
m.id,
|
||||
m.group_id as fan_group_id,
|
||||
fg.name as fan_group_name,
|
||||
m.uid,
|
||||
m.nickname as member_nickname,
|
||||
u.nickname as real_nickname,
|
||||
u.avatar,
|
||||
m.level,
|
||||
m.intimacy,
|
||||
m.status,
|
||||
m.join_time
|
||||
FROM eb_fan_group_member m
|
||||
LEFT JOIN eb_fan_group fg ON m.group_id = fg.id
|
||||
LEFT JOIN eb_user u ON m.uid = u.uid
|
||||
WHERE m.status = 1
|
||||
ORDER BY m.group_id, m.level DESC, m.intimacy DESC;
|
||||
|
||||
-- 6. 查看群组消息
|
||||
SELECT '=== 6. 粉丝团聊天消息 ===' as info;
|
||||
SELECT
|
||||
gm.id,
|
||||
gm.group_id,
|
||||
fg.name as fan_group_name,
|
||||
gm.sender_id,
|
||||
u.nickname as sender_name,
|
||||
gm.content,
|
||||
gm.message_type,
|
||||
gm.is_deleted,
|
||||
gm.create_time
|
||||
FROM eb_group_message gm
|
||||
LEFT JOIN eb_fan_group fg ON gm.group_id = fg.id
|
||||
LEFT JOIN eb_user u ON gm.sender_id = u.uid
|
||||
WHERE gm.is_deleted = 0
|
||||
ORDER BY gm.group_id, gm.create_time DESC
|
||||
LIMIT 50;
|
||||
|
||||
-- ==================== 第三部分:数据统计 ====================
|
||||
|
||||
-- 7. 粉丝团统计
|
||||
SELECT '=== 7. 粉丝团统计 ===' as info;
|
||||
SELECT
|
||||
COUNT(*) as total_fan_groups,
|
||||
SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as active_fan_groups,
|
||||
SUM(member_count) as total_members
|
||||
FROM eb_fan_group;
|
||||
|
||||
-- 8. 各粉丝团成员数量对比(实际 vs 记录)
|
||||
SELECT '=== 8. 成员数量校验 ===' as info;
|
||||
SELECT
|
||||
fg.id,
|
||||
fg.name,
|
||||
fg.member_count as recorded_count,
|
||||
COUNT(m.id) as actual_count,
|
||||
CASE
|
||||
WHEN fg.member_count = COUNT(m.id) THEN '✓ 一致'
|
||||
ELSE '✗ 不一致'
|
||||
END as status
|
||||
FROM eb_fan_group fg
|
||||
LEFT JOIN eb_fan_group_member m ON fg.id = m.group_id AND m.status = 1
|
||||
GROUP BY fg.id, fg.name, fg.member_count;
|
||||
|
||||
-- ==================== 第四部分:数据修复 ====================
|
||||
|
||||
-- 9. 修复粉丝团成员数量(如果不一致)
|
||||
SELECT '=== 9. 修复成员数量 ===' as info;
|
||||
UPDATE eb_fan_group fg SET member_count = (
|
||||
SELECT COUNT(*) FROM eb_fan_group_member m WHERE m.group_id = fg.id AND m.status = 1
|
||||
);
|
||||
|
||||
-- 10. 更新粉丝团主播名称(从用户表同步)
|
||||
SELECT '=== 10. 同步主播名称 ===' as info;
|
||||
UPDATE eb_fan_group fg
|
||||
JOIN eb_user u ON fg.anchor_id = u.uid
|
||||
SET fg.anchor_name = u.nickname
|
||||
WHERE fg.anchor_name IS NULL OR fg.anchor_name = '' OR fg.anchor_name != u.nickname;
|
||||
|
||||
-- ==================== 第五部分:验证修复结果 ====================
|
||||
|
||||
-- 11. 再次检查成员数量
|
||||
SELECT '=== 11. 修复后成员数量校验 ===' as info;
|
||||
SELECT
|
||||
fg.id,
|
||||
fg.name,
|
||||
fg.anchor_name,
|
||||
fg.member_count as recorded_count,
|
||||
COUNT(m.id) as actual_count
|
||||
FROM eb_fan_group fg
|
||||
LEFT JOIN eb_fan_group_member m ON fg.id = m.group_id AND m.status = 1
|
||||
GROUP BY fg.id, fg.name, fg.anchor_name, fg.member_count;
|
||||
|
||||
SELECT '=== 粉丝团检查完成 ===' as info;
|
||||
44
check_fan_group_data.sql
Normal file
44
check_fan_group_data.sql
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
-- 检查粉丝团表是否存在
|
||||
SHOW TABLES LIKE 'eb_fan_group%';
|
||||
|
||||
-- 检查粉丝团表结构
|
||||
DESC eb_fan_group;
|
||||
|
||||
-- 检查粉丝团数据
|
||||
SELECT * FROM eb_fan_group LIMIT 10;
|
||||
|
||||
-- 检查粉丝团成员表
|
||||
DESC eb_fan_group_member;
|
||||
|
||||
-- 检查粉丝团成员数据
|
||||
SELECT * FROM eb_fan_group_member LIMIT 10;
|
||||
|
||||
-- 如果没有数据,插入测试数据
|
||||
INSERT INTO eb_fan_group (id, name, anchor_id, anchor_name, avatar, member_count, level, description, create_time, update_time)
|
||||
SELECT 1, '测试粉丝团1', 121, '主播小明', 'https://example.com/avatar1.jpg', 100, 3, '这是一个测试粉丝团', NOW(), NOW()
|
||||
WHERE NOT EXISTS (SELECT 1 FROM eb_fan_group WHERE id = 1);
|
||||
|
||||
INSERT INTO eb_fan_group (id, name, anchor_id, anchor_name, avatar, member_count, level, description, create_time, update_time)
|
||||
SELECT 2, '星光粉丝团', 122, '主播小红', 'https://example.com/avatar2.jpg', 250, 5, '星光闪耀的粉丝团', NOW(), NOW()
|
||||
WHERE NOT EXISTS (SELECT 1 FROM eb_fan_group WHERE id = 2);
|
||||
|
||||
INSERT INTO eb_fan_group (id, name, anchor_id, anchor_name, avatar, member_count, level, description, create_time, update_time)
|
||||
SELECT 3, '梦想家族', 123, '主播小华', 'https://example.com/avatar3.jpg', 500, 8, '追逐梦想的大家庭', NOW(), NOW()
|
||||
WHERE NOT EXISTS (SELECT 1 FROM eb_fan_group WHERE id = 3);
|
||||
|
||||
-- 插入粉丝团成员测试数据
|
||||
INSERT INTO eb_fan_group_member (id, group_id, user_id, nickname, avatar, level, intimacy, join_time, create_time)
|
||||
SELECT 1, 1, 101, '粉丝A', 'https://example.com/fan1.jpg', 2, 1000, NOW(), NOW()
|
||||
WHERE NOT EXISTS (SELECT 1 FROM eb_fan_group_member WHERE id = 1);
|
||||
|
||||
INSERT INTO eb_fan_group_member (id, group_id, user_id, nickname, avatar, level, intimacy, join_time, create_time)
|
||||
SELECT 2, 1, 102, '粉丝B', 'https://example.com/fan2.jpg', 3, 2500, NOW(), NOW()
|
||||
WHERE NOT EXISTS (SELECT 1 FROM eb_fan_group_member WHERE id = 2);
|
||||
|
||||
INSERT INTO eb_fan_group_member (id, group_id, user_id, nickname, avatar, level, intimacy, join_time, create_time)
|
||||
SELECT 3, 2, 103, '粉丝C', 'https://example.com/fan3.jpg', 1, 500, NOW(), NOW()
|
||||
WHERE NOT EXISTS (SELECT 1 FROM eb_fan_group_member WHERE id = 3);
|
||||
|
||||
-- 验证插入结果
|
||||
SELECT * FROM eb_fan_group;
|
||||
SELECT * FROM eb_fan_group_member;
|
||||
35
check_fan_group_menu.sql
Normal file
35
check_fan_group_menu.sql
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
-- 检查粉丝团菜单是否存在
|
||||
SELECT '=== 检查粉丝团菜单 ===' as info;
|
||||
SELECT id, pid, name, path, component, perms, sort, is_show
|
||||
FROM eb_system_menu
|
||||
WHERE name LIKE '%粉丝团%' OR path LIKE '%fanGroup%' OR path LIKE '%fan-group%';
|
||||
|
||||
-- 如果不存在,添加粉丝团菜单
|
||||
-- 首先找到"社交管理"或"直播管理"的父菜单ID
|
||||
SELECT '=== 查找父菜单 ===' as info;
|
||||
SELECT id, name, path FROM eb_system_menu WHERE name IN ('社交管理', '直播管理', '用户管理') AND pid = 0;
|
||||
|
||||
-- 添加粉丝团菜单(如果不存在)
|
||||
-- 注意:需要根据实际的父菜单ID调整pid值
|
||||
INSERT INTO eb_system_menu (pid, name, icon, path, component, perms, menu_type, sort, is_show, create_time, update_time)
|
||||
SELECT
|
||||
(SELECT id FROM eb_system_menu WHERE name = '社交管理' AND pid = 0 LIMIT 1) as pid,
|
||||
'粉丝团管理' as name,
|
||||
'el-icon-star-on' as icon,
|
||||
'/fanGroup/list' as path,
|
||||
'fanGroup/list/index' as component,
|
||||
'admin:fan:group:list' as perms,
|
||||
'C' as menu_type,
|
||||
10 as sort,
|
||||
1 as is_show,
|
||||
NOW() as create_time,
|
||||
NOW() as update_time
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM eb_system_menu WHERE path = '/fanGroup/list'
|
||||
);
|
||||
|
||||
-- 验证菜单
|
||||
SELECT '=== 验证粉丝团菜单 ===' as info;
|
||||
SELECT id, pid, name, path, component, perms, sort, is_show
|
||||
FROM eb_system_menu
|
||||
WHERE name LIKE '%粉丝团%' OR path LIKE '%fanGroup%';
|
||||
43
check_report_data.sql
Normal file
43
check_report_data.sql
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
-- =====================================================
|
||||
-- 举报表检查和测试数据脚本
|
||||
-- =====================================================
|
||||
|
||||
-- 1. 检查表是否存在
|
||||
SELECT '=== 检查举报表 ===' as step;
|
||||
SHOW TABLES LIKE 'eb_report';
|
||||
|
||||
-- 2. 查看表结构
|
||||
SELECT '=== 表结构 ===' as step;
|
||||
DESCRIBE eb_report;
|
||||
|
||||
-- 3. 查看现有数据
|
||||
SELECT '=== 现有数据 ===' as step;
|
||||
SELECT COUNT(*) as total FROM eb_report;
|
||||
|
||||
-- 4. 查看详细数据
|
||||
SELECT * FROM eb_report ORDER BY id DESC LIMIT 10;
|
||||
|
||||
-- 5. 如果没有数据,插入测试数据
|
||||
INSERT IGNORE INTO `eb_report`
|
||||
(`uid`, `nickname`, `target_type`, `target_id`, `target_name`, `reason_type`, `reason`, `images`, `status`, `create_time`)
|
||||
VALUES
|
||||
(121, '测试用户1', 1, 122, '被举报用户A', 1, '该用户发布色情低俗内容', '[]', 0, NOW()),
|
||||
(121, '测试用户1', 2, 1, '违规直播间', 2, '直播间存在广告骚扰行为', '[]', 0, NOW()),
|
||||
(122, '测试用户2', 3, 1, '违规动态', 5, '动态内容存在人身攻击', '[]', 0, DATE_SUB(NOW(), INTERVAL 1 DAY)),
|
||||
(123, '测试用户3', 1, 124, '被举报用户B', 6, '该用户涉嫌欺诈骗钱', '[]', 2, DATE_SUB(NOW(), INTERVAL 2 DAY));
|
||||
|
||||
-- 6. 再次查看数据
|
||||
SELECT '=== 插入后数据 ===' as step;
|
||||
SELECT r.*,
|
||||
CASE r.target_type WHEN 1 THEN '用户' WHEN 2 THEN '房间' WHEN 3 THEN '动态' WHEN 4 THEN '评论' ELSE '其他' END as type_text,
|
||||
CASE r.status WHEN 0 THEN '待处理' WHEN 1 THEN '处理中' WHEN 2 THEN '已处理' WHEN 3 THEN '已忽略' ELSE '未知' END as status_text
|
||||
FROM eb_report r
|
||||
ORDER BY r.id DESC;
|
||||
|
||||
-- 7. 统计
|
||||
SELECT '=== 统计 ===' as step;
|
||||
SELECT
|
||||
COUNT(*) as total,
|
||||
SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) as pending,
|
||||
SUM(CASE WHEN status = 2 THEN 1 ELSE 0 END) as processed
|
||||
FROM eb_report;
|
||||
56
check_sensitive_word.sql
Normal file
56
check_sensitive_word.sql
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
-- =====================================================
|
||||
-- 敏感词表检查和初始化脚本
|
||||
-- 请在 Navicat 中逐步执行以下SQL
|
||||
-- =====================================================
|
||||
|
||||
-- ========== 第1步:检查表是否存在 ==========
|
||||
SELECT '=== 检查表是否存在 ===' as step;
|
||||
SHOW TABLES LIKE 'eb_sensitive_word';
|
||||
|
||||
-- ========== 第2步:如果表不存在,创建表 ==========
|
||||
CREATE TABLE IF NOT EXISTS `eb_sensitive_word` (
|
||||
`id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`word` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '敏感词',
|
||||
`category` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'default' COMMENT '分类',
|
||||
`level` tinyint NOT NULL DEFAULT 1 COMMENT '级别 1=轻度 2=中度 3=重度',
|
||||
`action` tinyint NOT NULL DEFAULT 1 COMMENT '处理方式 1=替换 2=拦截 3=警告',
|
||||
`replace_text` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '***' COMMENT '替换文本',
|
||||
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态 0=禁用 1=启用',
|
||||
`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`) USING BTREE,
|
||||
UNIQUE KEY `uk_word` (`word`) USING BTREE,
|
||||
KEY `idx_category` (`category`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '敏感词表' ROW_FORMAT = DYNAMIC;
|
||||
|
||||
-- ========== 第3步:查看表结构 ==========
|
||||
SELECT '=== 表结构 ===' as step;
|
||||
DESCRIBE eb_sensitive_word;
|
||||
|
||||
-- ========== 第4步:查看现有数据数量 ==========
|
||||
SELECT '=== 现有数据数量 ===' as step;
|
||||
SELECT COUNT(*) as total FROM eb_sensitive_word;
|
||||
|
||||
-- ========== 第5步:插入测试数据(如果没有数据) ==========
|
||||
-- 使用 INSERT IGNORE 避免重复插入
|
||||
INSERT IGNORE INTO `eb_sensitive_word` (`word`, `category`, `level`, `action`, `replace_text`, `status`, `create_time`) VALUES
|
||||
('测试敏感词1', 'default', 1, 1, '***', 1, NOW()),
|
||||
('测试敏感词2', 'default', 2, 1, '***', 1, NOW()),
|
||||
('广告', 'spam', 1, 1, '**', 1, NOW()),
|
||||
('推广', 'spam', 1, 1, '**', 1, NOW()),
|
||||
('加微信', 'spam', 2, 2, '***', 1, NOW()),
|
||||
('加QQ', 'spam', 2, 2, '***', 1, NOW()),
|
||||
('色情', 'illegal', 3, 2, '***', 1, NOW()),
|
||||
('赌博', 'illegal', 3, 2, '***', 1, NOW()),
|
||||
('诈骗', 'illegal', 3, 2, '***', 1, NOW()),
|
||||
('违法', 'illegal', 2, 2, '***', 1, NOW());
|
||||
|
||||
-- ========== 第6步:查看所有数据 ==========
|
||||
SELECT '=== 敏感词数据列表 ===' as step;
|
||||
SELECT * FROM eb_sensitive_word ORDER BY id DESC;
|
||||
|
||||
-- ========== 第7步:最终统计 ==========
|
||||
SELECT '=== 最终统计 ===' as step;
|
||||
SELECT COUNT(*) as total_count FROM eb_sensitive_word;
|
||||
SELECT COUNT(*) as enabled_count FROM eb_sensitive_word WHERE status = 1;
|
||||
SELECT COUNT(*) as disabled_count FROM eb_sensitive_word WHERE status = 0;
|
||||
77
complete_fan_group_system.sql
Normal file
77
complete_fan_group_system.sql
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
-- ============================================
|
||||
-- 完善粉丝团系统数据库
|
||||
-- ============================================
|
||||
|
||||
-- 1. 查看粉丝团表结构
|
||||
DESC eb_fan_group;
|
||||
|
||||
-- 2. 查看粉丝团成员表结构
|
||||
DESC eb_fan_group_member;
|
||||
|
||||
-- 3. 查看当前粉丝团数据
|
||||
SELECT * FROM eb_fan_group;
|
||||
|
||||
-- 4. 插入粉丝团成员测试数据
|
||||
-- 为粉丝团1(星光粉丝团,主播121)添加成员
|
||||
INSERT INTO eb_fan_group_member (group_id, uid, nickname, level, intimacy, status, join_time)
|
||||
SELECT 1, 122, '用户小红', 3, 2500, 1, DATE_SUB(NOW(), INTERVAL 10 DAY)
|
||||
WHERE NOT EXISTS (SELECT 1 FROM eb_fan_group_member WHERE group_id = 1 AND uid = 122);
|
||||
|
||||
INSERT INTO eb_fan_group_member (group_id, uid, nickname, level, intimacy, status, join_time)
|
||||
SELECT 1, 123, '用户小华', 2, 1200, 1, DATE_SUB(NOW(), INTERVAL 7 DAY)
|
||||
WHERE NOT EXISTS (SELECT 1 FROM eb_fan_group_member WHERE group_id = 1 AND uid = 123);
|
||||
|
||||
INSERT INTO eb_fan_group_member (group_id, uid, nickname, level, intimacy, status, join_time)
|
||||
SELECT 1, 124, '用户阿杰', 1, 500, 1, DATE_SUB(NOW(), INTERVAL 3 DAY)
|
||||
WHERE NOT EXISTS (SELECT 1 FROM eb_fan_group_member WHERE group_id = 1 AND uid = 124);
|
||||
|
||||
-- 为粉丝团2(梦想家族,主播122)添加成员
|
||||
INSERT INTO eb_fan_group_member (group_id, uid, nickname, level, intimacy, status, join_time)
|
||||
SELECT 2, 121, '主播小明', 5, 8000, 1, DATE_SUB(NOW(), INTERVAL 20 DAY)
|
||||
WHERE NOT EXISTS (SELECT 1 FROM eb_fan_group_member WHERE group_id = 2 AND uid = 121);
|
||||
|
||||
INSERT INTO eb_fan_group_member (group_id, uid, nickname, level, intimacy, status, join_time)
|
||||
SELECT 2, 125, '用户小美', 4, 5500, 1, DATE_SUB(NOW(), INTERVAL 15 DAY)
|
||||
WHERE NOT EXISTS (SELECT 1 FROM eb_fan_group_member WHERE group_id = 2 AND uid = 125);
|
||||
|
||||
INSERT INTO eb_fan_group_member (group_id, uid, nickname, level, intimacy, status, join_time)
|
||||
SELECT 2, 126, '用户大壮', 2, 1800, 1, DATE_SUB(NOW(), INTERVAL 5 DAY)
|
||||
WHERE NOT EXISTS (SELECT 1 FROM eb_fan_group_member WHERE group_id = 2 AND uid = 126);
|
||||
|
||||
-- 为粉丝团3(快乐大本营,主播123)添加成员
|
||||
INSERT INTO eb_fan_group_member (group_id, uid, nickname, level, intimacy, status, join_time)
|
||||
SELECT 3, 127, '快乐粉丝A', 3, 3200, 1, DATE_SUB(NOW(), INTERVAL 12 DAY)
|
||||
WHERE NOT EXISTS (SELECT 1 FROM eb_fan_group_member WHERE group_id = 3 AND uid = 127);
|
||||
|
||||
INSERT INTO eb_fan_group_member (group_id, uid, nickname, level, intimacy, status, join_time)
|
||||
SELECT 3, 128, '快乐粉丝B', 2, 1500, 1, DATE_SUB(NOW(), INTERVAL 8 DAY)
|
||||
WHERE NOT EXISTS (SELECT 1 FROM eb_fan_group_member WHERE group_id = 3 AND uid = 128);
|
||||
|
||||
-- 5. 更新粉丝团成员数量
|
||||
UPDATE eb_fan_group fg SET member_count = (
|
||||
SELECT COUNT(*) FROM eb_fan_group_member m WHERE m.group_id = fg.id AND m.status = 1
|
||||
);
|
||||
|
||||
-- 6. 验证数据
|
||||
SELECT '粉丝团列表' as info;
|
||||
SELECT id, name, anchor_name, member_count, badge, status FROM eb_fan_group;
|
||||
|
||||
SELECT '粉丝团成员' as info;
|
||||
SELECT m.*, fg.name as group_name
|
||||
FROM eb_fan_group_member m
|
||||
LEFT JOIN eb_fan_group fg ON m.group_id = fg.id
|
||||
ORDER BY m.group_id, m.level DESC;
|
||||
|
||||
-- 7. 创建群组消息表(如果不存在)
|
||||
CREATE TABLE IF NOT EXISTS eb_group_message (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
group_id BIGINT UNSIGNED NOT NULL COMMENT '群组ID',
|
||||
sender_id INT UNSIGNED NOT NULL COMMENT '发送者ID',
|
||||
content TEXT COMMENT '消息内容',
|
||||
message_type VARCHAR(20) DEFAULT 'text' COMMENT '消息类型',
|
||||
is_deleted TINYINT DEFAULT 0 COMMENT '是否删除',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_group_id (group_id),
|
||||
KEY idx_sender_id (sender_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='群组消息表';
|
||||
80
fan_group_complete_setup.sql
Normal file
80
fan_group_complete_setup.sql
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
-- ============================================
|
||||
-- 粉丝团系统完整设置脚本
|
||||
-- 执行此脚本以完善粉丝团功能
|
||||
-- ============================================
|
||||
|
||||
-- 1. 查看当前粉丝团数据
|
||||
SELECT '=== 当前粉丝团 ===' as info;
|
||||
SELECT id, name, anchor_id, anchor_name, member_count, badge, status FROM eb_fan_group;
|
||||
|
||||
-- 2. 查看粉丝团成员表结构
|
||||
SELECT '=== 成员表结构 ===' as info;
|
||||
DESC eb_fan_group_member;
|
||||
|
||||
-- 3. 插入粉丝团成员测试数据(如果不存在)
|
||||
-- 粉丝团1的成员
|
||||
INSERT IGNORE INTO eb_fan_group_member (group_id, uid, nickname, level, intimacy, status, join_time)
|
||||
VALUES
|
||||
(1, 122, '用户小红', 3, 2500, 1, DATE_SUB(NOW(), INTERVAL 10 DAY)),
|
||||
(1, 123, '用户小华', 2, 1200, 1, DATE_SUB(NOW(), INTERVAL 7 DAY)),
|
||||
(1, 124, '用户阿杰', 1, 500, 1, DATE_SUB(NOW(), INTERVAL 3 DAY));
|
||||
|
||||
-- 粉丝团2的成员
|
||||
INSERT IGNORE INTO eb_fan_group_member (group_id, uid, nickname, level, intimacy, status, join_time)
|
||||
VALUES
|
||||
(2, 121, '主播小明', 5, 8000, 1, DATE_SUB(NOW(), INTERVAL 20 DAY)),
|
||||
(2, 125, '用户小美', 4, 5500, 1, DATE_SUB(NOW(), INTERVAL 15 DAY)),
|
||||
(2, 126, '用户大壮', 2, 1800, 1, DATE_SUB(NOW(), INTERVAL 5 DAY));
|
||||
|
||||
-- 粉丝团3的成员
|
||||
INSERT IGNORE INTO eb_fan_group_member (group_id, uid, nickname, level, intimacy, status, join_time)
|
||||
VALUES
|
||||
(3, 127, '快乐粉丝A', 3, 3200, 1, DATE_SUB(NOW(), INTERVAL 12 DAY)),
|
||||
(3, 128, '快乐粉丝B', 2, 1500, 1, DATE_SUB(NOW(), INTERVAL 8 DAY));
|
||||
|
||||
-- 4. 更新粉丝团成员数量
|
||||
UPDATE eb_fan_group fg SET member_count = (
|
||||
SELECT COUNT(*) FROM eb_fan_group_member m WHERE m.group_id = fg.id AND m.status = 1
|
||||
);
|
||||
|
||||
-- 5. 创建群组消息表(如果不存在)
|
||||
CREATE TABLE IF NOT EXISTS eb_group_message (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
group_id BIGINT UNSIGNED NOT NULL COMMENT '群组ID',
|
||||
sender_id INT UNSIGNED NOT NULL COMMENT '发送者ID',
|
||||
content TEXT COMMENT '消息内容',
|
||||
message_type VARCHAR(20) DEFAULT 'text' COMMENT '消息类型',
|
||||
is_deleted TINYINT DEFAULT 0 COMMENT '是否删除',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_group_id (group_id),
|
||||
KEY idx_sender_id (sender_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='群组消息表';
|
||||
|
||||
-- 6. 插入一些测试聊天消息
|
||||
INSERT IGNORE INTO eb_group_message (id, group_id, sender_id, content, message_type, create_time)
|
||||
VALUES
|
||||
(1, 1, 122, '大家好,我是新来的粉丝!', 'text', DATE_SUB(NOW(), INTERVAL 2 DAY)),
|
||||
(2, 1, 123, '欢迎欢迎!', 'text', DATE_SUB(NOW(), INTERVAL 2 DAY)),
|
||||
(3, 1, 121, '感谢大家的支持!', 'text', DATE_SUB(NOW(), INTERVAL 1 DAY)),
|
||||
(4, 2, 125, '今晚直播太精彩了!', 'text', DATE_SUB(NOW(), INTERVAL 1 DAY)),
|
||||
(5, 2, 126, '同意!期待下次直播', 'text', DATE_SUB(NOW(), INTERVAL 1 DAY));
|
||||
|
||||
-- 7. 验证最终数据
|
||||
SELECT '=== 粉丝团列表 ===' as info;
|
||||
SELECT id, name, anchor_name, member_count, badge, badge_color, status, create_time FROM eb_fan_group;
|
||||
|
||||
SELECT '=== 粉丝团成员 ===' as info;
|
||||
SELECT m.id, m.group_id, fg.name as group_name, m.uid, m.nickname, m.level, m.intimacy, m.status, m.join_time
|
||||
FROM eb_fan_group_member m
|
||||
LEFT JOIN eb_fan_group fg ON m.group_id = fg.id
|
||||
ORDER BY m.group_id, m.level DESC;
|
||||
|
||||
SELECT '=== 聊天消息 ===' as info;
|
||||
SELECT gm.id, gm.group_id, fg.name as group_name, gm.sender_id, gm.content, gm.create_time
|
||||
FROM eb_group_message gm
|
||||
LEFT JOIN eb_fan_group fg ON gm.group_id = fg.id
|
||||
WHERE gm.is_deleted = 0
|
||||
ORDER BY gm.create_time DESC;
|
||||
|
||||
SELECT '=== 设置完成 ===' as info;
|
||||
120
fan_group_real_data.sql
Normal file
120
fan_group_real_data.sql
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
-- ============================================
|
||||
-- 粉丝团真实数据设置
|
||||
-- 确保成员来自真实用户,消息与成员对应
|
||||
-- ============================================
|
||||
|
||||
-- 1. 先查看系统中的真实用户
|
||||
SELECT '=== 系统真实用户 ===' as info;
|
||||
SELECT uid, nickname, avatar, phone FROM eb_user WHERE is_del = 0 LIMIT 20;
|
||||
|
||||
-- 2. 查看当前粉丝团
|
||||
SELECT '=== 当前粉丝团 ===' as info;
|
||||
SELECT id, name, anchor_id, anchor_name, member_count FROM eb_fan_group;
|
||||
|
||||
-- 3. 清空测试数据,重新基于真实用户创建
|
||||
DELETE FROM eb_fan_group_member;
|
||||
DELETE FROM eb_group_message;
|
||||
|
||||
-- 4. 为每个粉丝团添加真实用户作为成员
|
||||
-- 粉丝团1 (anchor_id=121) 的成员 - 从真实用户中选取(排除主播自己)
|
||||
INSERT INTO eb_fan_group_member (group_id, uid, nickname, level, intimacy, status, join_time)
|
||||
SELECT 1, u.uid, u.nickname,
|
||||
FLOOR(1 + RAND() * 5) as level, -- 随机等级1-5
|
||||
FLOOR(100 + RAND() * 5000) as intimacy, -- 随机亲密度
|
||||
1,
|
||||
DATE_SUB(NOW(), INTERVAL FLOOR(RAND() * 30) DAY)
|
||||
FROM eb_user u
|
||||
WHERE u.uid != 121 AND u.is_del = 0 AND u.nickname IS NOT NULL AND u.nickname != ''
|
||||
ORDER BY RAND()
|
||||
LIMIT 5;
|
||||
|
||||
-- 粉丝团2 (anchor_id=122) 的成员
|
||||
INSERT INTO eb_fan_group_member (group_id, uid, nickname, level, intimacy, status, join_time)
|
||||
SELECT 2, u.uid, u.nickname,
|
||||
FLOOR(1 + RAND() * 5) as level,
|
||||
FLOOR(100 + RAND() * 5000) as intimacy,
|
||||
1,
|
||||
DATE_SUB(NOW(), INTERVAL FLOOR(RAND() * 30) DAY)
|
||||
FROM eb_user u
|
||||
WHERE u.uid != 122 AND u.is_del = 0 AND u.nickname IS NOT NULL AND u.nickname != ''
|
||||
AND u.uid NOT IN (SELECT uid FROM eb_fan_group_member WHERE group_id = 1)
|
||||
ORDER BY RAND()
|
||||
LIMIT 5;
|
||||
|
||||
-- 粉丝团3 (anchor_id=123) 的成员
|
||||
INSERT INTO eb_fan_group_member (group_id, uid, nickname, level, intimacy, status, join_time)
|
||||
SELECT 3, u.uid, u.nickname,
|
||||
FLOOR(1 + RAND() * 5) as level,
|
||||
FLOOR(100 + RAND() * 5000) as intimacy,
|
||||
1,
|
||||
DATE_SUB(NOW(), INTERVAL FLOOR(RAND() * 30) DAY)
|
||||
FROM eb_user u
|
||||
WHERE u.uid != 123 AND u.is_del = 0 AND u.nickname IS NOT NULL AND u.nickname != ''
|
||||
AND u.uid NOT IN (SELECT uid FROM eb_fan_group_member WHERE group_id IN (1,2))
|
||||
ORDER BY RAND()
|
||||
LIMIT 3;
|
||||
|
||||
-- 5. 更新粉丝团成员数量
|
||||
UPDATE eb_fan_group fg SET member_count = (
|
||||
SELECT COUNT(*) FROM eb_fan_group_member m WHERE m.group_id = fg.id AND m.status = 1
|
||||
);
|
||||
|
||||
-- 6. 为粉丝团添加真实成员的聊天消息
|
||||
-- 粉丝团1的消息(由该粉丝团的真实成员发送)
|
||||
INSERT INTO eb_group_message (group_id, sender_id, content, message_type, create_time)
|
||||
SELECT 1, m.uid,
|
||||
CASE FLOOR(RAND() * 5)
|
||||
WHEN 0 THEN '大家好,很高兴加入粉丝团!'
|
||||
WHEN 1 THEN '主播今天直播太精彩了!'
|
||||
WHEN 2 THEN '支持主播,冲冲冲!'
|
||||
WHEN 3 THEN '期待下次直播~'
|
||||
ELSE '粉丝团的氛围真好!'
|
||||
END,
|
||||
'text',
|
||||
DATE_SUB(NOW(), INTERVAL FLOOR(RAND() * 7) DAY)
|
||||
FROM eb_fan_group_member m
|
||||
WHERE m.group_id = 1 AND m.status = 1
|
||||
LIMIT 3;
|
||||
|
||||
-- 粉丝团2的消息
|
||||
INSERT INTO eb_group_message (group_id, sender_id, content, message_type, create_time)
|
||||
SELECT 2, m.uid,
|
||||
CASE FLOOR(RAND() * 5)
|
||||
WHEN 0 THEN '新人报到!'
|
||||
WHEN 1 THEN '主播唱歌太好听了!'
|
||||
WHEN 2 THEN '今天的游戏直播太刺激了'
|
||||
WHEN 3 THEN '大家晚上好~'
|
||||
ELSE '感谢主播的精彩表演!'
|
||||
END,
|
||||
'text',
|
||||
DATE_SUB(NOW(), INTERVAL FLOOR(RAND() * 7) DAY)
|
||||
FROM eb_fan_group_member m
|
||||
WHERE m.group_id = 2 AND m.status = 1
|
||||
LIMIT 3;
|
||||
|
||||
-- 7. 验证数据完整性
|
||||
SELECT '=== 粉丝团成员(关联真实用户)===' as info;
|
||||
SELECT m.id, m.group_id, fg.name as fan_group_name,
|
||||
m.uid, m.nickname, u.nickname as real_nickname, u.avatar,
|
||||
m.level, m.intimacy, m.join_time
|
||||
FROM eb_fan_group_member m
|
||||
LEFT JOIN eb_fan_group fg ON m.group_id = fg.id
|
||||
LEFT JOIN eb_user u ON m.uid = u.uid
|
||||
WHERE m.status = 1
|
||||
ORDER BY m.group_id, m.level DESC;
|
||||
|
||||
SELECT '=== 聊天消息(关联真实发送者)===' as info;
|
||||
SELECT gm.id, gm.group_id, fg.name as fan_group_name,
|
||||
gm.sender_id, u.nickname as sender_name, u.avatar as sender_avatar,
|
||||
gm.content, gm.create_time
|
||||
FROM eb_group_message gm
|
||||
LEFT JOIN eb_fan_group fg ON gm.group_id = fg.id
|
||||
LEFT JOIN eb_user u ON gm.sender_id = u.uid
|
||||
WHERE gm.is_deleted = 0
|
||||
ORDER BY gm.group_id, gm.create_time DESC;
|
||||
|
||||
SELECT '=== 粉丝团统计 ===' as info;
|
||||
SELECT fg.id, fg.name, fg.anchor_name, fg.member_count,
|
||||
(SELECT COUNT(*) FROM eb_fan_group_member m WHERE m.group_id = fg.id AND m.status = 1) as actual_members,
|
||||
(SELECT COUNT(*) FROM eb_group_message gm WHERE gm.group_id = fg.id AND gm.is_deleted = 0) as message_count
|
||||
FROM eb_fan_group fg;
|
||||
60
fan_group_system_check.sql
Normal file
60
fan_group_system_check.sql
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
-- ============================================
|
||||
-- 粉丝团系统完整性检查脚本
|
||||
-- ============================================
|
||||
|
||||
-- 1. 检查表结构
|
||||
SELECT '=== 1. 检查表结构 ===' as info;
|
||||
|
||||
-- 检查eb_fan_group表
|
||||
SELECT 'eb_fan_group表字段:' as info;
|
||||
SHOW COLUMNS FROM eb_fan_group;
|
||||
|
||||
-- 检查eb_fan_group_member表
|
||||
SELECT 'eb_fan_group_member表字段:' as info;
|
||||
SHOW COLUMNS FROM eb_fan_group_member;
|
||||
|
||||
-- 检查eb_group_message表
|
||||
SELECT 'eb_group_message表字段:' as info;
|
||||
SHOW COLUMNS FROM eb_group_message;
|
||||
|
||||
-- 2. 检查数据完整性
|
||||
SELECT '=== 2. 数据统计 ===' as info;
|
||||
SELECT
|
||||
(SELECT COUNT(*) FROM eb_fan_group) as total_fan_groups,
|
||||
(SELECT COUNT(*) FROM eb_fan_group WHERE status = 1) as active_fan_groups,
|
||||
(SELECT COUNT(*) FROM eb_fan_group_member WHERE status = 1) as total_members,
|
||||
(SELECT COUNT(*) FROM eb_group_message WHERE is_deleted = 0) as total_messages;
|
||||
|
||||
-- 3. 检查粉丝团列表
|
||||
SELECT '=== 3. 粉丝团列表 ===' as info;
|
||||
SELECT
|
||||
fg.id,
|
||||
fg.name,
|
||||
fg.anchor_id,
|
||||
fg.anchor_name,
|
||||
fg.badge,
|
||||
fg.member_count,
|
||||
(SELECT COUNT(*) FROM eb_fan_group_member m WHERE m.group_id = fg.id AND m.status = 1) as actual_members,
|
||||
(SELECT COUNT(*) FROM eb_group_message gm WHERE gm.group_id = fg.id AND gm.is_deleted = 0) as message_count,
|
||||
fg.status,
|
||||
fg.create_time
|
||||
FROM eb_fan_group fg
|
||||
ORDER BY fg.id;
|
||||
|
||||
-- 4. 修复成员数量不一致
|
||||
SELECT '=== 4. 修复成员数量 ===' as info;
|
||||
UPDATE eb_fan_group fg SET member_count = (
|
||||
SELECT COUNT(*) FROM eb_fan_group_member m WHERE m.group_id = fg.id AND m.status = 1
|
||||
);
|
||||
|
||||
-- 5. 检查管理端菜单
|
||||
SELECT '=== 5. 检查管理端菜单 ===' as info;
|
||||
SELECT id, pid, name, path, component, is_show
|
||||
FROM eb_system_menu
|
||||
WHERE name LIKE '%粉丝团%' OR path LIKE '%fanGroup%' OR path LIKE '%fan%group%';
|
||||
|
||||
-- 6. 如果菜单不存在,查找合适的父菜单
|
||||
SELECT '=== 6. 可用的父菜单 ===' as info;
|
||||
SELECT id, name, path FROM eb_system_menu WHERE pid = 0 AND is_show = 1 ORDER BY sort;
|
||||
|
||||
SELECT '=== 检查完成 ===' as info;
|
||||
45
fix_customer_service_group.sql
Normal file
45
fix_customer_service_group.sql
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
-- 修复客服联系方式分组表
|
||||
-- 检查表是否存在,如果不存在则创建
|
||||
|
||||
-- 先检查表是否存在
|
||||
SELECT COUNT(*) as table_exists FROM information_schema.tables
|
||||
WHERE table_schema = DATABASE() AND table_name = 'eb_customer_service_group';
|
||||
|
||||
-- 如果表存在但字段名不对,需要修改
|
||||
-- 检查是否有 name 字段
|
||||
SET @has_name := (SELECT COUNT(*) FROM information_schema.columns
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'eb_customer_service_group'
|
||||
AND column_name = 'name');
|
||||
|
||||
-- 检查是否有 group_name 字段
|
||||
SET @has_group_name := (SELECT COUNT(*) FROM information_schema.columns
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'eb_customer_service_group'
|
||||
AND column_name = 'group_name');
|
||||
|
||||
-- 如果有 name 但没有 group_name,重命名字段
|
||||
-- ALTER TABLE eb_customer_service_group CHANGE COLUMN `name` `group_name` varchar(64) NOT NULL DEFAULT '' COMMENT '用户组名称';
|
||||
|
||||
-- 或者直接重建表(更安全)
|
||||
DROP TABLE IF EXISTS `eb_customer_service_group`;
|
||||
CREATE TABLE `eb_customer_service_group` (
|
||||
`id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`group_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户组名称',
|
||||
`avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '用户头像',
|
||||
`contact` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '联系电话或微信',
|
||||
`contact_type` tinyint NOT NULL DEFAULT 1 COMMENT '联系类型 1=电话 2=微信',
|
||||
`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`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '客服联系方式分组表' ROW_FORMAT = DYNAMIC;
|
||||
|
||||
-- 插入测试数据
|
||||
INSERT INTO `eb_customer_service_group` (`group_name`, `avatar`, `contact`, `contact_type`, `create_time`) VALUES
|
||||
('官方客服', 'https://img.zcool.cn/community/01a9a65d143edaa8012187f447cfef.jpg', '400-888-8888', 1, NOW()),
|
||||
('技术支持', 'https://img.zcool.cn/community/01b72057a7e0790000018c1bf4fce0.png', 'tech_support_wx', 2, NOW()),
|
||||
('商务合作', '', '13800138000', 1, NOW()),
|
||||
('投诉建议', '', 'complaint_wx', 2, NOW());
|
||||
|
||||
-- 查看数据
|
||||
SELECT * FROM eb_customer_service_group;
|
||||
11
insert_fan_group_data.sql
Normal file
11
insert_fan_group_data.sql
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
-- 插入粉丝团测试数据
|
||||
INSERT INTO eb_fan_group (anchor_id, anchor_name, name, badge, badge_color, member_count, status, create_time, update_time)
|
||||
VALUES
|
||||
(121, '主播小明', '星光粉丝团', '星光', '#FF6B6B', 156, 1, NOW(), NOW()),
|
||||
(122, '主播小红', '梦想家族', '梦想', '#4ECDC4', 328, 1, NOW(), NOW()),
|
||||
(123, '主播小华', '快乐大本营', '快乐', '#45B7D1', 89, 1, NOW(), NOW()),
|
||||
(124, '主播阿杰', '王者军团', '王者', '#96CEB4', 512, 1, NOW(), NOW()),
|
||||
(125, '主播小美', '甜蜜蜜', '甜蜜', '#FFEAA7', 245, 1, NOW(), NOW());
|
||||
|
||||
-- 验证插入结果
|
||||
SELECT * FROM eb_fan_group;
|
||||
13
remove_fangroup_menu.sql
Normal file
13
remove_fangroup_menu.sql
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
-- 移除"粉丝团管理"一级导航菜单
|
||||
-- 先查看当前菜单
|
||||
SELECT id, name, pid, sort, is_show FROM eb_system_menu WHERE name = '粉丝团管理';
|
||||
|
||||
-- 方法1:隐藏菜单(推荐,可恢复)
|
||||
UPDATE eb_system_menu SET is_show = 0 WHERE name = '粉丝团管理' AND pid = 0;
|
||||
|
||||
-- 方法2:如果要彻底删除,先删除子菜单,再删除父菜单
|
||||
-- DELETE FROM eb_system_menu WHERE pid = (SELECT id FROM (SELECT id FROM eb_system_menu WHERE name = '粉丝团管理' AND pid = 0) AS t);
|
||||
-- DELETE FROM eb_system_menu WHERE name = '粉丝团管理' AND pid = 0;
|
||||
|
||||
-- 验证结果
|
||||
SELECT id, name, pid, is_show FROM eb_system_menu WHERE name = '粉丝团管理';
|
||||
5
remove_fangroup_toplevel.sql
Normal file
5
remove_fangroup_toplevel.sql
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
-- 删除遗留的子菜单
|
||||
DELETE FROM eb_system_menu WHERE id = 664;
|
||||
|
||||
-- 验证删除结果 - 只应该剩下直播管理下的粉丝团管理(id=707)
|
||||
SELECT * FROM eb_system_menu WHERE name LIKE '%粉丝团%';
|
||||
200
setup_fan_group_data.sql
Normal file
200
setup_fan_group_data.sql
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
-- ============================================
|
||||
-- 粉丝团测试数据设置脚本
|
||||
-- 基于真实用户创建粉丝团测试数据
|
||||
-- ============================================
|
||||
|
||||
-- ==================== 第一部分:创建表(如果不存在)====================
|
||||
|
||||
-- 1. 创建粉丝团表
|
||||
CREATE TABLE IF NOT EXISTS eb_fan_group (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '粉丝团ID',
|
||||
anchor_id INT UNSIGNED NOT NULL COMMENT '主播ID',
|
||||
anchor_name VARCHAR(100) DEFAULT NULL COMMENT '主播名称',
|
||||
group_id BIGINT UNSIGNED DEFAULT NULL COMMENT '关联群组ID',
|
||||
name VARCHAR(100) NOT NULL COMMENT '粉丝团名称',
|
||||
badge VARCHAR(50) DEFAULT '粉丝' COMMENT '粉丝徽章',
|
||||
badge_color VARCHAR(20) DEFAULT '#FF6B9D' COMMENT '徽章颜色',
|
||||
member_count INT DEFAULT 0 COMMENT '成员数量',
|
||||
status TINYINT DEFAULT 1 COMMENT '状态 1-正常 0-禁用',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_anchor_id (anchor_id),
|
||||
KEY idx_status (status)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='粉丝团表';
|
||||
|
||||
-- 2. 创建粉丝团成员表
|
||||
CREATE TABLE IF NOT EXISTS eb_fan_group_member (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '成员ID',
|
||||
group_id INT UNSIGNED NOT NULL COMMENT '粉丝团ID',
|
||||
uid INT UNSIGNED NOT NULL COMMENT '用户ID',
|
||||
nickname VARCHAR(100) DEFAULT NULL COMMENT '用户昵称',
|
||||
level INT DEFAULT 1 COMMENT '粉丝等级 1-10',
|
||||
intimacy INT DEFAULT 0 COMMENT '亲密度',
|
||||
status TINYINT DEFAULT 1 COMMENT '状态 1-正常 0-退出',
|
||||
join_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '加入时间',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_group_uid (group_id, uid),
|
||||
KEY idx_uid (uid),
|
||||
KEY idx_level (level)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='粉丝团成员表';
|
||||
|
||||
-- 3. 创建群组消息表
|
||||
CREATE TABLE IF NOT EXISTS eb_group_message (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '消息ID',
|
||||
group_id INT UNSIGNED NOT NULL COMMENT '粉丝团ID',
|
||||
sender_id INT UNSIGNED NOT NULL COMMENT '发送者ID',
|
||||
content TEXT COMMENT '消息内容',
|
||||
message_type VARCHAR(20) DEFAULT 'text' COMMENT '消息类型 text/image/gift',
|
||||
is_deleted TINYINT DEFAULT 0 COMMENT '是否删除',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_group_id (group_id),
|
||||
KEY idx_sender_id (sender_id),
|
||||
KEY idx_create_time (create_time)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='群组消息表';
|
||||
|
||||
-- ==================== 第二部分:查看可用用户 ====================
|
||||
|
||||
SELECT '=== 可用用户列表(前20个)===' as info;
|
||||
SELECT uid, nickname, avatar, phone FROM eb_user WHERE status = 1 LIMIT 20;
|
||||
|
||||
-- ==================== 第三部分:创建粉丝团(如果不存在)====================
|
||||
|
||||
-- 为用户100创建粉丝团(如果不存在)
|
||||
INSERT INTO eb_fan_group (anchor_id, anchor_name, name, badge, badge_color, member_count, status, create_time)
|
||||
SELECT 100, u.nickname, CONCAT(u.nickname, '的粉丝团'), '星光', '#FF6B9D', 0, 1, NOW()
|
||||
FROM eb_user u
|
||||
WHERE u.uid = 100 AND u.status = 1
|
||||
AND NOT EXISTS (SELECT 1 FROM eb_fan_group WHERE anchor_id = 100);
|
||||
|
||||
-- 为用户101创建粉丝团(如果不存在)
|
||||
INSERT INTO eb_fan_group (anchor_id, anchor_name, name, badge, badge_color, member_count, status, create_time)
|
||||
SELECT 101, u.nickname, CONCAT(u.nickname, '的粉丝团'), '梦想', '#6B9DFF', 0, 1, NOW()
|
||||
FROM eb_user u
|
||||
WHERE u.uid = 101 AND u.status = 1
|
||||
AND NOT EXISTS (SELECT 1 FROM eb_fan_group WHERE anchor_id = 101);
|
||||
|
||||
-- 为用户102创建粉丝团(如果不存在)
|
||||
INSERT INTO eb_fan_group (anchor_id, anchor_name, name, badge, badge_color, member_count, status, create_time)
|
||||
SELECT 102, u.nickname, CONCAT(u.nickname, '的粉丝团'), '快乐', '#9DFF6B', 0, 1, NOW()
|
||||
FROM eb_user u
|
||||
WHERE u.uid = 102 AND u.status = 1
|
||||
AND NOT EXISTS (SELECT 1 FROM eb_fan_group WHERE anchor_id = 102);
|
||||
|
||||
-- ==================== 第四部分:添加粉丝团成员 ====================
|
||||
|
||||
-- 获取粉丝团ID
|
||||
SET @fg1 = (SELECT id FROM eb_fan_group WHERE anchor_id = 100 LIMIT 1);
|
||||
SET @fg2 = (SELECT id FROM eb_fan_group WHERE anchor_id = 101 LIMIT 1);
|
||||
SET @fg3 = (SELECT id FROM eb_fan_group WHERE anchor_id = 102 LIMIT 1);
|
||||
|
||||
-- 为粉丝团1添加成员(从真实用户中选取)
|
||||
INSERT IGNORE INTO eb_fan_group_member (group_id, uid, nickname, level, intimacy, status, join_time)
|
||||
SELECT @fg1, u.uid, u.nickname,
|
||||
FLOOR(1 + RAND() * 5) as level,
|
||||
FLOOR(100 + RAND() * 5000) as intimacy,
|
||||
1, DATE_SUB(NOW(), INTERVAL FLOOR(RAND() * 30) DAY)
|
||||
FROM eb_user u
|
||||
WHERE u.uid NOT IN (100, 101, 102)
|
||||
AND u.status = 1
|
||||
AND u.nickname IS NOT NULL
|
||||
AND u.nickname != ''
|
||||
AND @fg1 IS NOT NULL
|
||||
ORDER BY RAND()
|
||||
LIMIT 5;
|
||||
|
||||
-- 为粉丝团2添加成员
|
||||
INSERT IGNORE INTO eb_fan_group_member (group_id, uid, nickname, level, intimacy, status, join_time)
|
||||
SELECT @fg2, u.uid, u.nickname,
|
||||
FLOOR(1 + RAND() * 5) as level,
|
||||
FLOOR(100 + RAND() * 5000) as intimacy,
|
||||
1, DATE_SUB(NOW(), INTERVAL FLOOR(RAND() * 30) DAY)
|
||||
FROM eb_user u
|
||||
WHERE u.uid NOT IN (100, 101, 102)
|
||||
AND u.status = 1
|
||||
AND u.nickname IS NOT NULL
|
||||
AND u.nickname != ''
|
||||
AND u.uid NOT IN (SELECT uid FROM eb_fan_group_member WHERE group_id = @fg1)
|
||||
AND @fg2 IS NOT NULL
|
||||
ORDER BY RAND()
|
||||
LIMIT 4;
|
||||
|
||||
-- 为粉丝团3添加成员
|
||||
INSERT IGNORE INTO eb_fan_group_member (group_id, uid, nickname, level, intimacy, status, join_time)
|
||||
SELECT @fg3, u.uid, u.nickname,
|
||||
FLOOR(1 + RAND() * 5) as level,
|
||||
FLOOR(100 + RAND() * 5000) as intimacy,
|
||||
1, DATE_SUB(NOW(), INTERVAL FLOOR(RAND() * 30) DAY)
|
||||
FROM eb_user u
|
||||
WHERE u.uid NOT IN (100, 101, 102)
|
||||
AND u.status = 1
|
||||
AND u.nickname IS NOT NULL
|
||||
AND u.nickname != ''
|
||||
AND u.uid NOT IN (SELECT uid FROM eb_fan_group_member WHERE group_id IN (@fg1, @fg2))
|
||||
AND @fg3 IS NOT NULL
|
||||
ORDER BY RAND()
|
||||
LIMIT 3;
|
||||
|
||||
-- ==================== 第五部分:添加聊天消息 ====================
|
||||
|
||||
-- 为粉丝团1添加消息
|
||||
INSERT INTO eb_group_message (group_id, sender_id, content, message_type, create_time)
|
||||
SELECT @fg1, m.uid,
|
||||
CASE FLOOR(RAND() * 5)
|
||||
WHEN 0 THEN '大家好,我是新来的粉丝!'
|
||||
WHEN 1 THEN '今天的直播太精彩了!'
|
||||
WHEN 2 THEN '支持主播!'
|
||||
WHEN 3 THEN '期待下次直播~'
|
||||
ELSE '主播加油!'
|
||||
END,
|
||||
'text',
|
||||
DATE_SUB(NOW(), INTERVAL FLOOR(RAND() * 7) DAY)
|
||||
FROM eb_fan_group_member m
|
||||
WHERE m.group_id = @fg1 AND m.status = 1 AND @fg1 IS NOT NULL
|
||||
LIMIT 5;
|
||||
|
||||
-- 为粉丝团2添加消息
|
||||
INSERT INTO eb_group_message (group_id, sender_id, content, message_type, create_time)
|
||||
SELECT @fg2, m.uid,
|
||||
CASE FLOOR(RAND() * 5)
|
||||
WHEN 0 THEN '欢迎新成员!'
|
||||
WHEN 1 THEN '直播间见!'
|
||||
WHEN 2 THEN '主播唱歌真好听'
|
||||
WHEN 3 THEN '什么时候开播?'
|
||||
ELSE '粉丝团万岁!'
|
||||
END,
|
||||
'text',
|
||||
DATE_SUB(NOW(), INTERVAL FLOOR(RAND() * 7) DAY)
|
||||
FROM eb_fan_group_member m
|
||||
WHERE m.group_id = @fg2 AND m.status = 1 AND @fg2 IS NOT NULL
|
||||
LIMIT 4;
|
||||
|
||||
-- ==================== 第六部分:更新成员数量 ====================
|
||||
|
||||
UPDATE eb_fan_group fg SET member_count = (
|
||||
SELECT COUNT(*) FROM eb_fan_group_member m WHERE m.group_id = fg.id AND m.status = 1
|
||||
);
|
||||
|
||||
-- ==================== 第七部分:验证数据 ====================
|
||||
|
||||
SELECT '=== 粉丝团列表 ===' as info;
|
||||
SELECT id, name, anchor_id, anchor_name, badge, badge_color, member_count, status, create_time
|
||||
FROM eb_fan_group ORDER BY id;
|
||||
|
||||
SELECT '=== 粉丝团成员 ===' as info;
|
||||
SELECT m.id, m.group_id, fg.name as fan_group_name, m.uid, m.nickname, m.level, m.intimacy, m.join_time
|
||||
FROM eb_fan_group_member m
|
||||
LEFT JOIN eb_fan_group fg ON m.group_id = fg.id
|
||||
WHERE m.status = 1
|
||||
ORDER BY m.group_id, m.level DESC;
|
||||
|
||||
SELECT '=== 聊天消息 ===' as info;
|
||||
SELECT gm.id, gm.group_id, fg.name as fan_group_name, gm.sender_id, u.nickname as sender_name, gm.content, gm.create_time
|
||||
FROM eb_group_message gm
|
||||
LEFT JOIN eb_fan_group fg ON gm.group_id = fg.id
|
||||
LEFT JOIN eb_user u ON gm.sender_id = u.uid
|
||||
WHERE gm.is_deleted = 0
|
||||
ORDER BY gm.group_id, gm.create_time DESC
|
||||
LIMIT 20;
|
||||
|
||||
SELECT '=== 数据设置完成 ===' as info;
|
||||
19
update_signin_data.sql
Normal file
19
update_signin_data.sql
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
-- 更新签到记录测试数据
|
||||
-- 确保 eb_user_signin_record 表数据正确
|
||||
|
||||
-- 更新现有记录的 continuous_days 字段(随机1-7天)
|
||||
UPDATE eb_user_signin_record
|
||||
SET continuous_days = FLOOR(1 + RAND() * 7)
|
||||
WHERE continuous_days IS NULL OR continuous_days = 0;
|
||||
|
||||
-- 确保 reward_value 有值(根据连续天数计算)
|
||||
UPDATE eb_user_signin_record
|
||||
SET reward_value = continuous_days * 10
|
||||
WHERE reward_value IS NULL OR reward_value = 0;
|
||||
|
||||
-- 查看更新后的数据
|
||||
SELECT r.*, u.avatar as user_avatar, u.nickname as db_nickname
|
||||
FROM eb_user_signin_record r
|
||||
LEFT JOIN eb_user u ON r.uid = u.uid
|
||||
ORDER BY r.id DESC
|
||||
LIMIT 20;
|
||||
112
封禁系统功能说明.md
112
封禁系统功能说明.md
|
|
@ -10,11 +10,17 @@
|
|||
|
||||
管理员可以对违规用户进行封禁处理:
|
||||
|
||||
- **永久封禁**:用户账号永久禁用
|
||||
- **永久封禁**:用户账号永久禁用,无法登录
|
||||
- **临时封禁**:设置封禁天数,到期自动解除
|
||||
- **解除封禁**:手动解除用户封禁状态
|
||||
- **封禁原因**:记录封禁原因,便于追溯
|
||||
|
||||
**封禁影响**:
|
||||
- 被封禁用户无法登录
|
||||
- 被封禁用户无法发送弹幕
|
||||
- 被封禁用户无法赠送礼物
|
||||
- 被封禁用户无法发送私信
|
||||
|
||||
**API接口**:
|
||||
- `GET /api/admin/ban/user/list` - 获取用户封禁列表
|
||||
- `POST /api/admin/ban/user/add` - 封禁用户
|
||||
|
|
@ -31,6 +37,10 @@
|
|||
- **解除封禁**:手动解除房间封禁状态
|
||||
- **封禁原因**:记录封禁原因
|
||||
|
||||
**封禁影响**:
|
||||
- 被封禁房间无法进入
|
||||
- 进入时显示封禁提示
|
||||
|
||||
**API接口**:
|
||||
- `GET /api/admin/ban/room/list` - 获取房间封禁列表
|
||||
- `POST /api/admin/ban/room/add` - 封禁房间
|
||||
|
|
@ -47,6 +57,11 @@
|
|||
- **黑名单列表**:查看已屏蔽的用户
|
||||
- **互动限制**:被拉黑用户无法发送消息、评论等
|
||||
|
||||
**黑名单影响**:
|
||||
- 双方无法发送私信
|
||||
- 双方无法创建会话
|
||||
- 拉黑时自动删除好友关系
|
||||
|
||||
**移动端API接口**:
|
||||
- `GET /api/front/ban/check/me` - 检查当前用户封禁状态
|
||||
- `GET /api/front/ban/check/user/{userId}` - 检查指定用户封禁状态
|
||||
|
|
@ -92,6 +107,67 @@
|
|||
| blocked_nickname | varchar(100) | 被拉黑者昵称 |
|
||||
| create_time | datetime | 创建时间 |
|
||||
|
||||
### eb_room_blacklist(房间黑名单表)
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | bigint | 主键 |
|
||||
| room_id | int | 房间ID |
|
||||
| room_name | varchar(200) | 房间名称 |
|
||||
| blocked_user_id | int | 被拉黑用户ID |
|
||||
| blocked_user_nickname | varchar(100) | 被拉黑用户昵称 |
|
||||
| reason | varchar(500) | 拉黑原因 |
|
||||
| create_time | datetime | 创建时间 |
|
||||
|
||||
## 封禁检查点
|
||||
|
||||
### 后端检查点
|
||||
1. **登录时** - LoginServiceImpl.login() / phoneLogin()
|
||||
2. **发送弹幕时** - LiveRoomController.sendMessage()
|
||||
3. **赠送礼物时** - LiveRoomController.sendGift()
|
||||
4. **发送私信时** - ConversationServiceImpl.sendMessage()
|
||||
5. **创建会话时** - ConversationServiceImpl.getOrCreateConversation()
|
||||
6. **开播时** - LiveRoomController.create()
|
||||
|
||||
### 移动端检查点
|
||||
1. **进入直播间时** - RoomDetailActivity.checkRoomBanStatus()
|
||||
2. **用户主页拉黑** - UserProfileActivity.addToBlacklist()
|
||||
3. **设置页黑名单管理** - BlacklistActivity
|
||||
|
||||
## 双端同步机制
|
||||
|
||||
1. **管理后台封禁用户** → 用户状态更新 → 移动端登录时检测到封禁状态 → 显示封禁提示,阻止登录
|
||||
2. **管理后台封禁房间** → 房间状态更新 → 移动端进入房间时检测到封禁状态 → 显示封禁提示,退出房间
|
||||
3. **移动端添加黑名单** → 数据库更新 → 管理后台可查看黑名单记录
|
||||
4. **管理后台解除封禁** → 用户/房间状态恢复 → 移动端可正常使用
|
||||
|
||||
## 文件清单
|
||||
|
||||
### 后端文件
|
||||
- `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/zhibo-h/crmeb-front/src/main/java/com/zbkj/front/service/impl/LoginServiceImpl.java` - 登录服务(含封禁检查)
|
||||
- `Zhibo/zhibo-h/crmeb-service/src/main/java/com/zbkj/service/service/impl/ConversationServiceImpl.java` - 会话服务(含黑名单检查)
|
||||
- `Zhibo/zhibo-h/crmeb-front/src/main/java/com/zbkj/front/controller/LiveRoomController.java` - 直播间控制器(含封禁检查)
|
||||
|
||||
### 管理后台前端文件
|
||||
- `Zhibo/admin/src/views/ban/userBan.vue` - 用户封禁管理页面
|
||||
- `Zhibo/admin/src/views/ban/roomBan.vue` - 房间封禁管理页面
|
||||
- `Zhibo/admin/src/views/blacklist/user.vue` - 用户黑名单页面
|
||||
- `Zhibo/admin/src/views/blacklist/room.vue` - 房间黑名单页面
|
||||
- `Zhibo/admin/src/api/ban.js` - 封禁API
|
||||
- `Zhibo/admin/src/api/blacklist.js` - 黑名单API
|
||||
|
||||
### Android文件
|
||||
- `android-app/app/src/main/java/com/example/livestreaming/BlacklistActivity.java` - 黑名单管理页面
|
||||
- `android-app/app/src/main/java/com/example/livestreaming/UserProfileActivity.java` - 用户主页(含拉黑功能)
|
||||
- `android-app/app/src/main/java/com/example/livestreaming/RoomDetailActivity.java` - 直播间详情(含房间封禁检查)
|
||||
- `android-app/app/src/main/java/com/example/livestreaming/net/ApiService.java` - API接口定义
|
||||
|
||||
### SQL脚本
|
||||
- `ban_system_tables.sql` - 创建数据库表
|
||||
- `add_ban_menus.sql` - 添加菜单配置
|
||||
|
||||
## 部署步骤
|
||||
|
||||
1. **执行数据库脚本**
|
||||
|
|
@ -106,41 +182,7 @@
|
|||
```
|
||||
|
||||
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` - 添加菜单配置
|
||||
|
|
|
|||
592
直播平台功能交互逻辑报告.md
Normal file
592
直播平台功能交互逻辑报告.md
Normal file
|
|
@ -0,0 +1,592 @@
|
|||
# 直播平台功能交互逻辑报告
|
||||
|
||||
## 概述
|
||||
|
||||
本报告详细描述直播平台三大核心模块的功能交互逻辑:
|
||||
1. **敏感词管理** - 内容安全过滤系统
|
||||
2. **消息/粉丝团/群组** - 社交通讯系统
|
||||
3. **封禁系统** - 用户与房间管理
|
||||
|
||||
---
|
||||
|
||||
## 一、敏感词管理模块
|
||||
|
||||
### 1.1 功能概述
|
||||
|
||||
敏感词管理用于过滤平台内的违规内容,保障平台内容安全。
|
||||
|
||||
### 1.2 数据表结构
|
||||
|
||||
**表名**: `eb_sensitive_word`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | int | 主键ID |
|
||||
| word | varchar(128) | 敏感词内容 |
|
||||
| category | varchar(32) | 分类(default/spam/illegal) |
|
||||
| level | tinyint | 级别(1轻度/2中度/3重度) |
|
||||
| action | tinyint | 处理方式(1替换/2拦截/3警告) |
|
||||
| replace_text | varchar(32) | 替换文本(默认***) |
|
||||
| status | tinyint | 状态(0禁用/1启用) |
|
||||
| create_time | datetime | 创建时间 |
|
||||
| update_time | datetime | 更新时间 |
|
||||
|
||||
### 1.3 管理端功能
|
||||
|
||||
**API路径**: `/api/admin/sensitive/word`
|
||||
|
||||
| 接口 | 方法 | 功能 |
|
||||
|------|------|------|
|
||||
| /list | GET | 敏感词列表(支持分页、关键词搜索、时间筛选) |
|
||||
| /add | POST | 添加敏感词 |
|
||||
| /update | POST | 更新敏感词 |
|
||||
| /delete/{id} | POST | 删除敏感词 |
|
||||
| /status/{id} | POST | 切换启用/禁用状态 |
|
||||
|
||||
### 1.4 交互流程图
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 敏感词管理流程 │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ 管理员 │───▶│ 添加敏感词│───▶│ 存入数据库│ │
|
||||
│ └──────────┘ └──────────┘ └──────────┘ │
|
||||
│ │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ 用户发送 │───▶│ 内容检测 │───▶│ 匹配敏感词│───▶│ 执行处理 │ │
|
||||
│ │ 消息/弹幕│ │ │ │ │ │ (替换/拦截)│ │
|
||||
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 1.5 应用场景
|
||||
|
||||
敏感词过滤应用于以下场景:
|
||||
- 直播间弹幕消息
|
||||
- 私聊消息内容
|
||||
- 粉丝团群聊消息
|
||||
- 动态发布内容
|
||||
- 用户昵称/签名
|
||||
|
||||
---
|
||||
|
||||
## 二、消息/粉丝团/群组模块
|
||||
|
||||
### 2.1 私聊消息系统
|
||||
|
||||
#### 2.1.1 数据表结构
|
||||
|
||||
**会话表**: `eb_conversation`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | bigint | 会话ID |
|
||||
| user1_id | int | 用户1 ID |
|
||||
| user2_id | int | 用户2 ID |
|
||||
| last_message | varchar(255) | 最后一条消息预览 |
|
||||
| last_message_time | datetime | 最后消息时间 |
|
||||
| user1_unread_count | int | 用户1未读数 |
|
||||
| user2_unread_count | int | 用户2未读数 |
|
||||
| user1_deleted | tinyint | 用户1是否删除 |
|
||||
| user2_deleted | tinyint | 用户2是否删除 |
|
||||
|
||||
**私聊消息表**: `eb_private_message`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | bigint | 消息ID |
|
||||
| conversation_id | bigint | 会话ID |
|
||||
| sender_id | int | 发送者ID |
|
||||
| receiver_id | int | 接收者ID |
|
||||
| content | text | 消息内容 |
|
||||
| message_type | varchar(20) | 消息类型(text/image/voice) |
|
||||
| media_url | varchar(500) | 媒体URL |
|
||||
| status | varchar(20) | 状态(sent/read) |
|
||||
| is_recalled | tinyint | 是否撤回 |
|
||||
|
||||
#### 2.1.2 移动端API
|
||||
|
||||
**路径**: `/api/front/chat`
|
||||
|
||||
| 接口 | 方法 | 功能 |
|
||||
|------|------|------|
|
||||
| /conversations | GET | 获取会话列表 |
|
||||
| /conversations/{id} | GET | 获取会话详情 |
|
||||
| /conversations/{id}/messages | GET | 获取消息列表 |
|
||||
| /conversations/{id}/messages | POST | 发送消息 |
|
||||
| /conversations/{id}/read | POST | 标记已读 |
|
||||
| /messages/{id}/recall | POST | 撤回消息(2分钟内) |
|
||||
|
||||
#### 2.1.3 管理端API
|
||||
|
||||
**路径**: `/api/admin/chat`
|
||||
|
||||
| 接口 | 方法 | 功能 |
|
||||
|------|------|------|
|
||||
| /conversations | GET | 会话列表(支持搜索) |
|
||||
| /conversations/{id}/messages | GET | 查看会话消息 |
|
||||
| /conversations/{id} | DELETE | 删除会话 |
|
||||
| /messages/{id} | DELETE | 删除单条消息 |
|
||||
| /statistics | GET | 私聊统计数据 |
|
||||
|
||||
#### 2.1.4 私聊交互流程
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ 私聊消息发送流程 │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
|
||||
│ │ 用户A │──▶│ 检查黑名单 │──▶│ 敏感词过滤 │──▶│ 保存消息 │ │
|
||||
│ │ 发消息 │ │ │ │ │ │ │ │
|
||||
│ └────────┘ └────────────┘ └────────────┘ └────────────┘ │
|
||||
│ │ │ │
|
||||
│ ▼ ▼ │
|
||||
│ ┌────────────┐ ┌────────────┐ │
|
||||
│ │ 黑名单拦截 │ │ 更新会话 │ │
|
||||
│ │ 返回错误 │ │ 推送通知 │ │
|
||||
│ └────────────┘ └────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌────────────┐ │
|
||||
│ │ 用户B收到 │ │
|
||||
│ │ 消息通知 │ │
|
||||
│ └────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 粉丝团系统
|
||||
|
||||
#### 2.2.1 数据表结构
|
||||
|
||||
**粉丝团表**: `eb_fan_group`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | int | 粉丝团ID |
|
||||
| anchor_id | int | 主播ID |
|
||||
| anchor_name | varchar(64) | 主播昵称 |
|
||||
| name | varchar(64) | 粉丝团名称 |
|
||||
| badge | varchar(32) | 粉丝团徽章 |
|
||||
| badge_color | varchar(16) | 徽章颜色 |
|
||||
| member_count | int | 成员数量 |
|
||||
| level | int | 粉丝团等级 |
|
||||
| status | tinyint | 状态(0解散/1正常) |
|
||||
|
||||
**粉丝团成员表**: `eb_fan_group_member`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | int | 成员记录ID |
|
||||
| group_id | int | 粉丝团ID |
|
||||
| uid | int | 用户ID |
|
||||
| nickname | varchar(64) | 成员昵称 |
|
||||
| level | int | 成员等级(1-10) |
|
||||
| intimacy | int | 亲密度 |
|
||||
| status | tinyint | 状态 |
|
||||
| join_time | datetime | 加入时间 |
|
||||
|
||||
**群聊消息表**: `eb_group_message`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | bigint | 消息ID |
|
||||
| group_id | int | 粉丝团ID |
|
||||
| sender_id | int | 发送者ID |
|
||||
| content | text | 消息内容 |
|
||||
| message_type | varchar(20) | 消息类型 |
|
||||
| is_deleted | tinyint | 是否删除 |
|
||||
|
||||
#### 2.2.2 管理端API
|
||||
|
||||
**路径**: `/api/admin/fan/group`
|
||||
|
||||
| 接口 | 方法 | 功能 |
|
||||
|------|------|------|
|
||||
| /list | GET | 粉丝团列表 |
|
||||
| /detail/{id} | GET | 粉丝团详情 |
|
||||
| /delete/{id} | POST | 删除粉丝团 |
|
||||
| /batch-delete | POST | 批量删除 |
|
||||
| /status/{id} | POST | 修改状态(解散/恢复) |
|
||||
| /update/{id} | POST | 修改粉丝团信息 |
|
||||
| /member/list | GET | 成员列表 |
|
||||
| /member/delete/{id} | POST | 删除成员 |
|
||||
| /member/update-level/{id} | POST | 修改成员等级 |
|
||||
| /message/list | GET | 聊天记录列表 |
|
||||
| /message/delete/{id} | POST | 删除聊天记录 |
|
||||
| /statistics | GET | 统计数据 |
|
||||
|
||||
#### 2.2.3 粉丝团交互流程
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ 粉丝团系统流程 │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 【创建粉丝团】 │
|
||||
│ ┌────────┐ ┌────────────┐ ┌────────────┐ │
|
||||
│ │ 主播 │──▶│ 设置名称 │──▶│ 创建粉丝团 │ │
|
||||
│ │ 开播 │ │ 徽章/颜色 │ │ │ │
|
||||
│ └────────┘ └────────────┘ └────────────┘ │
|
||||
│ │
|
||||
│ 【加入粉丝团】 │
|
||||
│ ┌────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
|
||||
│ │ 用户 │──▶│ 查看粉丝团 │──▶│ 支付加入费 │──▶│ 成为成员 │ │
|
||||
│ │ │ │ 信息 │ │ (可选) │ │ 等级=1 │ │
|
||||
│ └────────┘ └────────────┘ └────────────┘ └────────────┘ │
|
||||
│ │
|
||||
│ 【等级提升】 │
|
||||
│ ┌────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
|
||||
│ │ 成员 │──▶│ 送礼/互动 │──▶│ 增加亲密度 │──▶│ 等级提升 │ │
|
||||
│ │ │ │ │ │ │ │ (1-10级) │ │
|
||||
│ └────────┘ └────────────┘ └────────────┘ └────────────┘ │
|
||||
│ │
|
||||
│ 【群聊功能】 │
|
||||
│ ┌────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
|
||||
│ │ 成员 │──▶│ 发送消息 │──▶│ 敏感词过滤 │──▶│ 群内广播 │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
│ └────────┘ └────────────┘ └────────────┘ └────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、封禁系统模块
|
||||
|
||||
### 3.1 系统概述
|
||||
|
||||
封禁系统分为两大类:
|
||||
1. **平台封禁** - 管理员对用户/房间的封禁(eb_user_ban / eb_room_ban)
|
||||
2. **用户黑名单** - 用户间的互相拉黑(eb_user_blacklist / eb_room_blacklist)
|
||||
|
||||
### 3.2 数据表结构
|
||||
|
||||
#### 3.2.1 用户封禁表 `eb_user_ban`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | bigint | 记录ID |
|
||||
| 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 | 状态(0已解封/1封禁中) |
|
||||
|
||||
#### 3.2.2 房间封禁表 `eb_room_ban`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | bigint | 记录ID |
|
||||
| room_id | int | 被封禁房间ID |
|
||||
| ban_type | varchar(20) | 封禁类型 |
|
||||
| reason | varchar(500) | 封禁原因 |
|
||||
| duration_days | int | 封禁天数 |
|
||||
| expire_time | datetime | 解封时间 |
|
||||
| operator_id | int | 操作员ID |
|
||||
| status | tinyint | 状态 |
|
||||
|
||||
#### 3.2.3 用户黑名单表 `eb_user_blacklist`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | int | 记录ID |
|
||||
| user_id | int | 拉黑发起者ID |
|
||||
| blocked_user_id | int | 被拉黑用户ID |
|
||||
| blocker_nickname | varchar(64) | 发起者昵称 |
|
||||
| blocked_nickname | varchar(64) | 被拉黑者昵称 |
|
||||
| create_time | datetime | 创建时间 |
|
||||
|
||||
#### 3.2.4 房间黑名单表 `eb_room_blacklist`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | int | 记录ID |
|
||||
| room_id | int | 房间ID |
|
||||
| room_name | varchar(128) | 房间名称 |
|
||||
| blocked_user_id | int | 被拉黑用户ID |
|
||||
| blocked_user_nickname | varchar(64) | 被拉黑用户昵称 |
|
||||
| operator_id | int | 操作者ID(主播) |
|
||||
| create_time | datetime | 创建时间 |
|
||||
|
||||
### 3.3 管理端API
|
||||
|
||||
#### 3.3.1 封禁管理 `/api/admin/ban`
|
||||
|
||||
| 接口 | 方法 | 功能 |
|
||||
|------|------|------|
|
||||
| /user/list | GET | 用户封禁列表 |
|
||||
| /user/add | POST | 封禁用户 |
|
||||
| /user/unban/{id} | POST | 解除用户封禁 |
|
||||
| /user/delete/{id} | POST | 删除封禁记录 |
|
||||
| /user/batch-delete | POST | 批量删除 |
|
||||
| /user/check/{userId} | GET | 检查用户封禁状态 |
|
||||
| /room/list | GET | 房间封禁列表 |
|
||||
| /room/add | POST | 封禁房间 |
|
||||
| /room/unban/{id} | POST | 解除房间封禁 |
|
||||
| /room/delete/{id} | POST | 删除封禁记录 |
|
||||
| /room/batch-delete | POST | 批量删除 |
|
||||
| /room/check/{roomId} | GET | 检查房间封禁状态 |
|
||||
|
||||
#### 3.3.2 黑名单管理 `/api/admin/blacklist`
|
||||
|
||||
| 接口 | 方法 | 功能 |
|
||||
|------|------|------|
|
||||
| /user/list | GET | 用户黑名单列表 |
|
||||
| /user/delete/{id} | POST | 删除用户黑名单记录 |
|
||||
| /user/batch-delete | POST | 批量删除 |
|
||||
| /room/list | GET | 房间黑名单列表 |
|
||||
| /room/delete/{id} | POST | 删除房间黑名单记录 |
|
||||
| /room/batch-delete | POST | 批量删除 |
|
||||
|
||||
### 3.4 移动端API
|
||||
|
||||
**路径**: `/api/front/ban`
|
||||
|
||||
| 接口 | 方法 | 功能 |
|
||||
|------|------|------|
|
||||
| /check/me | GET | 检查当前用户封禁状态 |
|
||||
| /check/user/{userId} | GET | 检查指定用户封禁状态 |
|
||||
| /check/room/{roomId} | GET | 检查房间封禁状态 |
|
||||
| /blacklist/list | GET | 获取我的黑名单列表 |
|
||||
| /blacklist/add | POST | 添加用户到黑名单 |
|
||||
| /blacklist/remove | POST | 从黑名单移除用户 |
|
||||
| /blacklist/check/{targetUserId} | GET | 检查黑名单状态 |
|
||||
|
||||
### 3.5 封禁检查触发点
|
||||
|
||||
封禁状态检查在以下场景自动触发:
|
||||
|
||||
| 场景 | 检查内容 | 实现位置 |
|
||||
|------|----------|----------|
|
||||
| 用户登录 | 检查用户是否被封禁 | LoginServiceImpl.checkUserBanStatus() |
|
||||
| 发送私聊消息 | 检查双方黑名单状态 | ConversationServiceImpl.checkBlacklistStatus() |
|
||||
| 发送直播间弹幕 | 检查用户封禁状态 | LiveRoomController.checkUserBanStatus() |
|
||||
| 赠送礼物 | 检查用户封禁状态 | LiveRoomController.sendGift() |
|
||||
| 进入直播间 | 检查房间封禁状态 | Android RoomDetailActivity |
|
||||
|
||||
### 3.6 封禁系统交互流程
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ 封禁系统完整流程 │
|
||||
├─────────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 【管理员封禁用户】 │
|
||||
│ ┌────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
|
||||
│ │ 管理员 │──▶│ 选择用户 │──▶│ 设置封禁 │──▶│ 更新用户 │ │
|
||||
│ │ │ │ 填写原因 │ │ 类型/时长 │ │ status=0 │ │
|
||||
│ └────────┘ └────────────┘ └────────────┘ └────────────┘ │
|
||||
│ │
|
||||
│ 【用户登录检查】 │
|
||||
│ ┌────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
|
||||
│ │ 用户 │──▶│ 提交登录 │──▶│ 检查封禁表 │──▶│ 封禁中? │ │
|
||||
│ │ 登录 │ │ │ │ │ │ │ │
|
||||
│ └────────┘ └────────────┘ └────────────┘ └─────┬──────┘ │
|
||||
│ │ │
|
||||
│ ┌──────────────────┼──────────────────┐ │
|
||||
│ ▼ ▼ │ │
|
||||
│ ┌──────────┐ ┌──────────┐ │ │
|
||||
│ │ 是:拒绝 │ │ 否:允许 │ │ │
|
||||
│ │ 返回原因 │ │ 登录成功 │ │ │
|
||||
│ └──────────┘ └──────────┘ │ │
|
||||
│ │
|
||||
│ 【用户拉黑用户】 │
|
||||
│ ┌────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
|
||||
│ │ 用户A │──▶│ 点击拉黑 │──▶│ 添加黑名单 │──▶│ 删除好友 │ │
|
||||
│ │ │ │ 用户B │ │ 记录 │ │ 关系 │ │
|
||||
│ └────────┘ └────────────┘ └────────────┘ └────────────┘ │
|
||||
│ │
|
||||
│ 【私聊黑名单检查】 │
|
||||
│ ┌────────┐ ┌────────────┐ ┌────────────┐ │
|
||||
│ │ 用户A │──▶│ 发送消息 │──▶│ 检查黑名单 │ │
|
||||
│ │ 发消息 │ │ 给用户B │ │ │ │
|
||||
│ └────────┘ └────────────┘ └─────┬──────┘ │
|
||||
│ │ │
|
||||
│ ┌──────────────────┼──────────────────┐ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ A拉黑了B │ │ B拉黑了A │ │ 无黑名单 │ │
|
||||
│ │ 提示:您已│ │ 提示:对方│ │ 正常发送 │ │
|
||||
│ │ 拉黑对方 │ │ 已拉黑您 │ │ │ │
|
||||
│ └──────────┘ └──────────┘ └──────────┘ │
|
||||
│ │
|
||||
│ 【房间封禁检查】 │
|
||||
│ ┌────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
|
||||
│ │ 用户 │──▶│ 进入直播间 │──▶│ 检查房间 │──▶│ 封禁中? │ │
|
||||
│ │ │ │ │ │ 封禁状态 │ │ │ │
|
||||
│ └────────┘ └────────────┘ └────────────┘ └─────┬──────┘ │
|
||||
│ │ │
|
||||
│ ┌──────────────────┼──────────────────┐ │
|
||||
│ ▼ ▼ │ │
|
||||
│ ┌──────────┐ ┌──────────┐ │ │
|
||||
│ │ 是:禁止 │ │ 否:允许 │ │ │
|
||||
│ │ 进入房间 │ │ 进入观看 │ │ │
|
||||
│ └──────────┘ └──────────┘ │ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、三大模块联动关系
|
||||
|
||||
### 4.1 模块交互图
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ 三大模块联动关系 │
|
||||
├─────────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────┐ │
|
||||
│ │ 敏感词管理 │ │
|
||||
│ │ │ │
|
||||
│ │ • 内容过滤规则 │ │
|
||||
│ │ • 违规词库维护 │ │
|
||||
│ └────────┬────────┘ │
|
||||
│ │ │
|
||||
│ ┌────────────┼────────────┐ │
|
||||
│ │ │ │ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐ │
|
||||
│ │ 私聊消息 │ │ 粉丝团群聊 │ │ 直播间弹幕 │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ • 发送前过滤敏感词 │ │ • 发送前过滤敏感词 │ │ • 发送前过滤敏感词 │ │
|
||||
│ │ • 检查黑名单状态 │ │ • 检查成员状态 │ │ • 检查用户封禁 │ │
|
||||
│ │ • 检查用户封禁 │ │ • 检查用户封禁 │ │ • 检查房间封禁 │ │
|
||||
│ └──────────┬──────────┘ └──────────┬──────────┘ └──────────┬──────────┘ │
|
||||
│ │ │ │ │
|
||||
│ └────────────────────────┼────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌─────────────────────┐ │
|
||||
│ │ 封禁系统 │ │
|
||||
│ │ │ │
|
||||
│ │ • 用户封禁管理 │ │
|
||||
│ │ • 房间封禁管理 │ │
|
||||
│ │ • 用户黑名单 │ │
|
||||
│ │ • 房间黑名单 │ │
|
||||
│ └─────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 4.2 完整消息发送流程
|
||||
|
||||
```
|
||||
用户发送消息
|
||||
│
|
||||
▼
|
||||
┌────────────────┐
|
||||
│ 1. 用户登录检查 │ ──── 检查 eb_user_ban 表
|
||||
└───────┬────────┘
|
||||
│ 通过
|
||||
▼
|
||||
┌────────────────┐
|
||||
│ 2. 封禁状态检查 │ ──── 检查用户是否被平台封禁
|
||||
└───────┬────────┘
|
||||
│ 通过
|
||||
▼
|
||||
┌────────────────┐
|
||||
│ 3. 黑名单检查 │ ──── 检查 eb_user_blacklist 表
|
||||
└───────┬────────┘ (私聊场景)
|
||||
│ 通过
|
||||
▼
|
||||
┌────────────────┐
|
||||
│ 4. 敏感词过滤 │ ──── 检查 eb_sensitive_word 表
|
||||
└───────┬────────┘
|
||||
│ 通过/替换
|
||||
▼
|
||||
┌────────────────┐
|
||||
│ 5. 消息入库 │ ──── 保存到对应消息表
|
||||
└───────┬────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────┐
|
||||
│ 6. 推送通知 │ ──── WebSocket/推送服务
|
||||
└────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、代码实现位置汇总
|
||||
|
||||
### 5.1 后端代码
|
||||
|
||||
| 模块 | 文件路径 | 说明 |
|
||||
|------|----------|------|
|
||||
| 敏感词管理 | `crmeb-admin/.../SensitiveWordController.java` | 管理端敏感词CRUD |
|
||||
| 私聊管理 | `crmeb-admin/.../ChatManagementController.java` | 管理端私聊管理 |
|
||||
| 私聊服务 | `crmeb-service/.../ConversationServiceImpl.java` | 私聊业务逻辑+黑名单检查 |
|
||||
| 粉丝团管理 | `crmeb-admin/.../FanGroupController.java` | 管理端粉丝团管理 |
|
||||
| 封禁管理 | `crmeb-admin/.../BanController.java` | 管理端封禁管理 |
|
||||
| 黑名单管理 | `crmeb-admin/.../BlacklistController.java` | 管理端黑名单管理 |
|
||||
| 移动端封禁 | `crmeb-front/.../BanFrontController.java` | 移动端封禁API |
|
||||
| 登录服务 | `crmeb-front/.../LoginServiceImpl.java` | 登录时封禁检查 |
|
||||
| 直播间控制 | `crmeb-front/.../LiveRoomController.java` | 弹幕/礼物封禁检查 |
|
||||
|
||||
### 5.2 前端代码
|
||||
|
||||
| 模块 | 文件路径 | 说明 |
|
||||
|------|----------|------|
|
||||
| 敏感词页面 | `admin/src/views/sensitiveWord/list/index.vue` | 敏感词管理页面 |
|
||||
| 敏感词API | `admin/src/api/sensitiveWord.js` | 敏感词API接口 |
|
||||
| 粉丝团页面 | `admin/src/views/fanGroup/list/index.vue` | 粉丝团管理页面 |
|
||||
| 粉丝团API | `admin/src/api/fanGroup.js` | 粉丝团API接口 |
|
||||
| 封禁页面 | `admin/src/views/ban/` | 封禁管理页面 |
|
||||
| 黑名单页面 | `admin/src/views/blacklist/` | 黑名单管理页面 |
|
||||
|
||||
### 5.3 Android代码
|
||||
|
||||
| 模块 | 文件路径 | 说明 |
|
||||
|------|----------|------|
|
||||
| API接口 | `ApiService.java` | 封禁/黑名单API定义 |
|
||||
| 直播间 | `RoomDetailActivity.java` | 进入房间封禁检查 |
|
||||
| 粉丝团聊天 | `GroupChatActivity.java` | 粉丝团群聊功能 |
|
||||
| 消息列表 | `MessagesActivity.java` | 消息列表页面 |
|
||||
|
||||
---
|
||||
|
||||
## 六、待完善功能建议
|
||||
|
||||
### 6.1 敏感词模块
|
||||
|
||||
1. **实时过滤服务** - 目前敏感词表已建立,建议实现统一的敏感词过滤服务类
|
||||
2. **分类管理** - 支持按分类(spam/illegal/abuse)管理敏感词
|
||||
3. **批量导入** - 支持Excel/TXT批量导入敏感词
|
||||
4. **过滤日志** - 记录敏感词触发日志,便于分析
|
||||
|
||||
### 6.2 消息模块
|
||||
|
||||
1. **消息撤回通知** - 撤回消息时通知对方
|
||||
2. **消息已读回执** - 显示消息已读状态
|
||||
3. **群聊@功能** - 粉丝团群聊支持@成员
|
||||
4. **消息搜索** - 支持历史消息搜索
|
||||
|
||||
### 6.3 封禁模块
|
||||
|
||||
1. **封禁申诉** - 用户可提交封禁申诉
|
||||
2. **自动解封** - 临时封禁到期自动解封定时任务
|
||||
3. **封禁通知** - 封禁/解封时推送通知给用户
|
||||
4. **封禁统计** - 封禁数据统计报表
|
||||
|
||||
---
|
||||
|
||||
## 七、总结
|
||||
|
||||
本报告详细描述了直播平台三大核心模块的功能设计和交互逻辑:
|
||||
|
||||
1. **敏感词管理** - 提供内容安全过滤能力,保障平台内容合规
|
||||
2. **消息/粉丝团/群组** - 构建完整的社交通讯体系,增强用户粘性
|
||||
3. **封禁系统** - 提供多层次的用户管理能力,维护平台秩序
|
||||
|
||||
三大模块相互配合,形成完整的平台安全和社交体系。
|
||||
|
||||
---
|
||||
|
||||
*报告生成时间: 2026-01-05*
|
||||
Loading…
Reference in New Issue
Block a user