462 lines
11 KiB
Markdown
462 lines
11 KiB
Markdown
|
|
# 分类管理模块使用说明
|
|||
|
|
|
|||
|
|
## 📋 功能概述
|
|||
|
|
|
|||
|
|
分类管理模块为直播间和作品提供了分类功能,支持:
|
|||
|
|
- ✅ 直播间分类管理
|
|||
|
|
- ✅ 作品分类管理(预留)
|
|||
|
|
- ✅ 分类列表查询
|
|||
|
|
- ✅ 按分类筛选直播间
|
|||
|
|
- ✅ 后台分类管理(复用现有管理功能)
|
|||
|
|
|
|||
|
|
## 🗄️ 数据库更新
|
|||
|
|
|
|||
|
|
### 1. 执行SQL脚本
|
|||
|
|
|
|||
|
|
运行 `sql/category_module_update.sql` 文件:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
mysql -u root -p your_database < sql/category_module_update.sql
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
该脚本会:
|
|||
|
|
- 为 `eb_live_room` 表添加 `category_id` 字段
|
|||
|
|
- 插入默认的直播间分类数据(娱乐、游戏、音乐等)
|
|||
|
|
- 插入默认的作品分类数据(摄影、绘画、设计等)
|
|||
|
|
|
|||
|
|
### 2. 分类类型说明
|
|||
|
|
|
|||
|
|
| type | 说明 |
|
|||
|
|
|------|------|
|
|||
|
|
| 1 | 商品分类 |
|
|||
|
|
| 2 | 附件分类 |
|
|||
|
|
| 3 | 文章分类 |
|
|||
|
|
| 4 | 设置分类 |
|
|||
|
|
| 5 | 菜单分类 |
|
|||
|
|
| 6 | 配置分类 |
|
|||
|
|
| 7 | 秒杀配置 |
|
|||
|
|
| **8** | **直播间分类(新增)** |
|
|||
|
|
| **9** | **作品分类(新增)** |
|
|||
|
|
|
|||
|
|
## 📡 API接口
|
|||
|
|
|
|||
|
|
### 前端接口
|
|||
|
|
|
|||
|
|
#### 1. 获取直播间分类列表
|
|||
|
|
|
|||
|
|
```http
|
|||
|
|
GET /api/front/category/live-room
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**响应示例:**
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"code": 200,
|
|||
|
|
"message": "success",
|
|||
|
|
"data": [
|
|||
|
|
{
|
|||
|
|
"id": 1,
|
|||
|
|
"name": "娱乐",
|
|||
|
|
"pid": 0,
|
|||
|
|
"sort": 100,
|
|||
|
|
"extra": ""
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"id": 2,
|
|||
|
|
"name": "游戏",
|
|||
|
|
"pid": 0,
|
|||
|
|
"sort": 90,
|
|||
|
|
"extra": ""
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 2. 获取作品分类列表
|
|||
|
|
|
|||
|
|
```http
|
|||
|
|
GET /api/front/category/work
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**响应示例:**
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"code": 200,
|
|||
|
|
"message": "success",
|
|||
|
|
"data": [
|
|||
|
|
{
|
|||
|
|
"id": 10,
|
|||
|
|
"name": "摄影",
|
|||
|
|
"pid": 0,
|
|||
|
|
"sort": 100,
|
|||
|
|
"extra": ""
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"id": 11,
|
|||
|
|
"name": "绘画",
|
|||
|
|
"pid": 0,
|
|||
|
|
"sort": 90,
|
|||
|
|
"extra": ""
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 3. 获取指定类型的分类列表
|
|||
|
|
|
|||
|
|
```http
|
|||
|
|
GET /api/front/category/list?type=8
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**参数说明:**
|
|||
|
|
- `type`: 分类类型(8=直播间,9=作品)
|
|||
|
|
|
|||
|
|
#### 4. 获取分类详情
|
|||
|
|
|
|||
|
|
```http
|
|||
|
|
GET /api/front/category/{id}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 5. 按分类筛选直播间
|
|||
|
|
|
|||
|
|
```http
|
|||
|
|
GET /api/front/live/public/rooms?categoryId=1
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**参数说明:**
|
|||
|
|
- `categoryId`: 分类ID(可选,不传则返回所有直播间)
|
|||
|
|
|
|||
|
|
**响应示例:**
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"code": 200,
|
|||
|
|
"message": "success",
|
|||
|
|
"data": [
|
|||
|
|
{
|
|||
|
|
"id": "1",
|
|||
|
|
"title": "精彩直播",
|
|||
|
|
"streamerName": "主播A",
|
|||
|
|
"categoryId": 1,
|
|||
|
|
"categoryName": null,
|
|||
|
|
"streamKey": "abc123",
|
|||
|
|
"isLive": true,
|
|||
|
|
"viewerCount": 0,
|
|||
|
|
"streamUrls": {
|
|||
|
|
"rtmp": "rtmp://localhost:25002/live/abc123",
|
|||
|
|
"flv": "http://localhost:25003/live/abc123.flv",
|
|||
|
|
"hls": "http://localhost:25003/live/abc123.m3u8"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 6. 创建直播间(带分类)
|
|||
|
|
|
|||
|
|
```http
|
|||
|
|
POST /api/front/live/rooms
|
|||
|
|
Authorization: Bearer {token}
|
|||
|
|
Content-Type: application/json
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
"title": "我的直播间",
|
|||
|
|
"streamerName": "主播名称",
|
|||
|
|
"categoryId": 1
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 后台管理接口
|
|||
|
|
|
|||
|
|
后台分类管理复用现有的 `/api/admin/category` 接口:
|
|||
|
|
|
|||
|
|
#### 1. 获取分类列表
|
|||
|
|
|
|||
|
|
```http
|
|||
|
|
GET /api/admin/category/list?type=8&status=1
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 2. 获取树形结构分类
|
|||
|
|
|
|||
|
|
```http
|
|||
|
|
GET /api/admin/category/list/tree?type=8&status=1
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 3. 新增分类
|
|||
|
|
|
|||
|
|
```http
|
|||
|
|
POST /api/admin/category/save
|
|||
|
|
Content-Type: application/json
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
"name": "新分类",
|
|||
|
|
"type": 8,
|
|||
|
|
"pid": 0,
|
|||
|
|
"sort": 100,
|
|||
|
|
"status": true
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 4. 修改分类
|
|||
|
|
|
|||
|
|
```http
|
|||
|
|
POST /api/admin/category/update?id=1
|
|||
|
|
Content-Type: application/json
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
"name": "修改后的分类名",
|
|||
|
|
"sort": 90
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 5. 删除分类
|
|||
|
|
|
|||
|
|
```http
|
|||
|
|
GET /api/admin/category/delete?id=1
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 6. 更改分类状态
|
|||
|
|
|
|||
|
|
```http
|
|||
|
|
GET /api/admin/category/updateStatus/1
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🔧 代码集成
|
|||
|
|
|
|||
|
|
### Android端集成示例
|
|||
|
|
|
|||
|
|
#### 1. 定义API接口
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
public interface CategoryApi {
|
|||
|
|
@GET("api/front/category/live-room")
|
|||
|
|
Call<ApiResponse<List<Category>>> getLiveRoomCategories();
|
|||
|
|
|
|||
|
|
@GET("api/front/category/work")
|
|||
|
|
Call<ApiResponse<List<Category>>> getWorkCategories();
|
|||
|
|
|
|||
|
|
@GET("api/front/live/public/rooms")
|
|||
|
|
Call<ApiResponse<List<Room>>> getRoomsByCategory(
|
|||
|
|
@Query("categoryId") Integer categoryId
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 2. 分类数据模型
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
public class Category {
|
|||
|
|
private Integer id;
|
|||
|
|
private String name;
|
|||
|
|
private Integer pid;
|
|||
|
|
private Integer sort;
|
|||
|
|
private String extra;
|
|||
|
|
|
|||
|
|
// getters and setters
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 3. 使用示例
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
// 获取直播间分类列表
|
|||
|
|
categoryApi.getLiveRoomCategories().enqueue(new Callback<ApiResponse<List<Category>>>() {
|
|||
|
|
@Override
|
|||
|
|
public void onResponse(Call<ApiResponse<List<Category>>> call,
|
|||
|
|
Response<ApiResponse<List<Category>>> response) {
|
|||
|
|
if (response.isSuccessful() && response.body() != null) {
|
|||
|
|
List<Category> categories = response.body().getData();
|
|||
|
|
// 更新UI显示分类列表
|
|||
|
|
updateCategoryList(categories);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@Override
|
|||
|
|
public void onFailure(Call<ApiResponse<List<Category>>> call, Throwable t) {
|
|||
|
|
// 处理错误
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 按分类筛选直播间
|
|||
|
|
categoryApi.getRoomsByCategory(categoryId).enqueue(new Callback<ApiResponse<List<Room>>>() {
|
|||
|
|
@Override
|
|||
|
|
public void onResponse(Call<ApiResponse<List<Room>>> call,
|
|||
|
|
Response<ApiResponse<List<Room>>> response) {
|
|||
|
|
if (response.isSuccessful() && response.body() != null) {
|
|||
|
|
List<Room> rooms = response.body().getData();
|
|||
|
|
// 更新直播间列表
|
|||
|
|
updateRoomList(rooms);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@Override
|
|||
|
|
public void onFailure(Call<ApiResponse<List<Room>>> call, Throwable t) {
|
|||
|
|
// 处理错误
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 📱 Android端UI实现建议
|
|||
|
|
|
|||
|
|
### 1. 分类筛选器
|
|||
|
|
|
|||
|
|
```xml
|
|||
|
|
<!-- 水平滚动的分类标签 -->
|
|||
|
|
<HorizontalScrollView
|
|||
|
|
android:layout_width="match_parent"
|
|||
|
|
android:layout_height="wrap_content"
|
|||
|
|
android:scrollbars="none">
|
|||
|
|
|
|||
|
|
<com.google.android.material.chip.ChipGroup
|
|||
|
|
android:id="@+id/categoryChipGroup"
|
|||
|
|
android:layout_width="wrap_content"
|
|||
|
|
android:layout_height="wrap_content"
|
|||
|
|
android:padding="8dp"
|
|||
|
|
app:singleSelection="true"
|
|||
|
|
app:selectionRequired="false">
|
|||
|
|
|
|||
|
|
<!-- 动态添加Chip -->
|
|||
|
|
|
|||
|
|
</com.google.android.material.chip.ChipGroup>
|
|||
|
|
</HorizontalScrollView>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 分类筛选逻辑
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
public class RoomListActivity extends AppCompatActivity {
|
|||
|
|
|
|||
|
|
private ChipGroup categoryChipGroup;
|
|||
|
|
private Integer selectedCategoryId = null;
|
|||
|
|
|
|||
|
|
private void setupCategoryFilter() {
|
|||
|
|
// 加载分类列表
|
|||
|
|
loadCategories();
|
|||
|
|
|
|||
|
|
// 监听分类选择
|
|||
|
|
categoryChipGroup.setOnCheckedChangeListener((group, checkedId) -> {
|
|||
|
|
if (checkedId == View.NO_ID) {
|
|||
|
|
// 未选择任何分类,显示全部
|
|||
|
|
selectedCategoryId = null;
|
|||
|
|
} else {
|
|||
|
|
Chip chip = findViewById(checkedId);
|
|||
|
|
selectedCategoryId = (Integer) chip.getTag();
|
|||
|
|
}
|
|||
|
|
// 重新加载直播间列表
|
|||
|
|
loadRooms(selectedCategoryId);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void loadCategories() {
|
|||
|
|
categoryApi.getLiveRoomCategories().enqueue(new Callback<ApiResponse<List<Category>>>() {
|
|||
|
|
@Override
|
|||
|
|
public void onResponse(Call<ApiResponse<List<Category>>> call,
|
|||
|
|
Response<ApiResponse<List<Category>>> response) {
|
|||
|
|
if (response.isSuccessful() && response.body() != null) {
|
|||
|
|
List<Category> categories = response.body().getData();
|
|||
|
|
|
|||
|
|
// 添加"全部"选项
|
|||
|
|
Chip allChip = new Chip(RoomListActivity.this);
|
|||
|
|
allChip.setText("全部");
|
|||
|
|
allChip.setCheckable(true);
|
|||
|
|
allChip.setChecked(true);
|
|||
|
|
categoryChipGroup.addView(allChip);
|
|||
|
|
|
|||
|
|
// 添加分类选项
|
|||
|
|
for (Category category : categories) {
|
|||
|
|
Chip chip = new Chip(RoomListActivity.this);
|
|||
|
|
chip.setText(category.getName());
|
|||
|
|
chip.setTag(category.getId());
|
|||
|
|
chip.setCheckable(true);
|
|||
|
|
categoryChipGroup.addView(chip);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@Override
|
|||
|
|
public void onFailure(Call<ApiResponse<List<Category>>> call, Throwable t) {
|
|||
|
|
Toast.makeText(RoomListActivity.this, "加载分类失败", Toast.LENGTH_SHORT).show();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void loadRooms(Integer categoryId) {
|
|||
|
|
categoryApi.getRoomsByCategory(categoryId).enqueue(new Callback<ApiResponse<List<Room>>>() {
|
|||
|
|
@Override
|
|||
|
|
public void onResponse(Call<ApiResponse<List<Room>>> call,
|
|||
|
|
Response<ApiResponse<List<Room>>> response) {
|
|||
|
|
if (response.isSuccessful() && response.body() != null) {
|
|||
|
|
List<Room> rooms = response.body().getData();
|
|||
|
|
roomAdapter.submitList(rooms);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@Override
|
|||
|
|
public void onFailure(Call<ApiResponse<List<Room>>> call, Throwable t) {
|
|||
|
|
Toast.makeText(RoomListActivity.this, "加载直播间失败", Toast.LENGTH_SHORT).show();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 🎯 功能特点
|
|||
|
|
|
|||
|
|
### 1. 灵活的分类管理
|
|||
|
|
- 支持多级分类(通过pid字段)
|
|||
|
|
- 支持分类排序(sort字段)
|
|||
|
|
- 支持分类启用/禁用(status字段)
|
|||
|
|
- 支持分类扩展字段(extra字段,可存储图标URL等)
|
|||
|
|
|
|||
|
|
### 2. 高效的查询
|
|||
|
|
- 为category_id字段添加了索引
|
|||
|
|
- 支持按分类快速筛选
|
|||
|
|
|
|||
|
|
### 3. 易于扩展
|
|||
|
|
- 可以轻松添加新的分类类型
|
|||
|
|
- 可以为作品、商品等其他实体添加分类
|
|||
|
|
|
|||
|
|
## 📝 注意事项
|
|||
|
|
|
|||
|
|
1. **数据库迁移**:确保在生产环境执行SQL脚本前做好数据备份
|
|||
|
|
2. **分类ID**:创建直播间时categoryId可以为null,表示未分类
|
|||
|
|
3. **权限控制**:后台分类管理接口需要管理员权限
|
|||
|
|
4. **缓存策略**:建议对分类列表进行缓存,减少数据库查询
|
|||
|
|
|
|||
|
|
## 🔄 后续扩展
|
|||
|
|
|
|||
|
|
### 1. 作品分类功能
|
|||
|
|
|
|||
|
|
当需要为作品添加分类时,只需:
|
|||
|
|
1. 为作品表添加category_id字段
|
|||
|
|
2. 创建作品时传入categoryId
|
|||
|
|
3. 使用 `/api/front/category/work` 获取作品分类列表
|
|||
|
|
|
|||
|
|
### 2. 分类统计
|
|||
|
|
|
|||
|
|
可以添加统计功能,显示每个分类下的直播间数量:
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
SELECT c.id, c.name, COUNT(lr.id) as room_count
|
|||
|
|
FROM eb_category c
|
|||
|
|
LEFT JOIN eb_live_room lr ON c.id = lr.category_id
|
|||
|
|
WHERE c.type = 8 AND c.status = 1
|
|||
|
|
GROUP BY c.id, c.name
|
|||
|
|
ORDER BY c.sort DESC;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. 热门分类
|
|||
|
|
|
|||
|
|
可以根据直播间观看量、点赞数等指标计算热门分类。
|
|||
|
|
|
|||
|
|
## 📞 技术支持
|
|||
|
|
|
|||
|
|
如有问题,请查看:
|
|||
|
|
- 项目文档:`直播IM系统开发指南.md`
|
|||
|
|
- 后端接口清单:`后端接口TODO清单-总览.md`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**版本:** v1.0
|
|||
|
|
**更新日期:** 2025-12-25
|
|||
|
|
**开发者:** CRMEB Team
|