11
This commit is contained in:
parent
e56b32131d
commit
888ed8eb9e
|
|
@ -318,6 +318,18 @@
|
||||||
<version>1.3.0</version>
|
<version>1.3.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Spring Data JPA -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Hibernate Validator -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.hibernate.validator</groupId>
|
||||||
|
<artifactId>hibernate-validator</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.zbkj.common.config;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
|
||||||
|
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||||
|
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JPA配置类
|
||||||
|
* 用于配置JPA相关功能,与MyBatis-Plus共存
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@EnableTransactionManagement
|
||||||
|
@EnableJpaAuditing
|
||||||
|
@EnableJpaRepositories(
|
||||||
|
basePackages = "com.zbkj.service.repository", // JPA Repository接口所在包
|
||||||
|
entityManagerFactoryRef = "entityManagerFactory",
|
||||||
|
transactionManagerRef = "transactionManager"
|
||||||
|
)
|
||||||
|
@EntityScan(basePackages = "com.zbkj.common.model") // JPA实体类所在包
|
||||||
|
public class JpaConfig {
|
||||||
|
|
||||||
|
// 如果需要自定义配置,可以在这里添加Bean
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -16,22 +16,34 @@ public class CategoryConstants {
|
||||||
|
|
||||||
/** 分类状态-正常 */
|
/** 分类状态-正常 */
|
||||||
public static final Integer CATEGORY_STATUS_NORMAL = 1;
|
public static final Integer CATEGORY_STATUS_NORMAL = 1;
|
||||||
|
|
||||||
/** 分类状态-失效 */
|
/** 分类状态-失效 */
|
||||||
public static final Integer CATEGORY_STATUS_INVALID = 0;
|
public static final Integer CATEGORY_STATUS_INVALID = 0;
|
||||||
|
|
||||||
/** 分类类型-产品分类 */
|
/** 分类类型-商品分类 */
|
||||||
public static final Integer CATEGORY_TYPE_PRODUCT = 1;
|
public static final Integer CATEGORY_TYPE_PRODUCT = 1;
|
||||||
|
|
||||||
/** 分类类型-附件分类 */
|
/** 分类类型-附件分类 */
|
||||||
public static final Integer CATEGORY_TYPE_ATTACHMENT = 2;
|
public static final Integer CATEGORY_TYPE_ATTACHMENT = 2;
|
||||||
|
|
||||||
/** 分类类型-文章分类 */
|
/** 分类类型-文章分类 */
|
||||||
public static final Integer CATEGORY_TYPE_ARTICLE = 3;
|
public static final Integer CATEGORY_TYPE_ARTICLE = 3;
|
||||||
|
|
||||||
/** 分类类型-设置分类 */
|
/** 分类类型-设置分类 */
|
||||||
public static final Integer CATEGORY_TYPE_SETTING = 4;
|
public static final Integer CATEGORY_TYPE_SETTING = 4;
|
||||||
|
|
||||||
/** 分类类型-菜单分类 */
|
/** 分类类型-菜单分类 */
|
||||||
public static final Integer CATEGORY_TYPE_MENU = 5;
|
public static final Integer CATEGORY_TYPE_MENU = 5;
|
||||||
|
|
||||||
/** 分类类型-配置分类 */
|
/** 分类类型-配置分类 */
|
||||||
public static final Integer CATEGORY_TYPE_CONFIG = 6;
|
public static final Integer CATEGORY_TYPE_CONFIG = 6;
|
||||||
|
|
||||||
/** 分类类型-秒杀配置 */
|
/** 分类类型-秒杀配置 */
|
||||||
public static final Integer CATEGORY_TYPE_SECKILL = 7;
|
public static final Integer CATEGORY_TYPE_SECKILL = 7;
|
||||||
|
|
||||||
|
/** 分类类型-直播间分类 */
|
||||||
|
public static final Integer CATEGORY_TYPE_LIVE_ROOM = 8;
|
||||||
|
|
||||||
|
/** 分类类型-作品分类 */
|
||||||
|
public static final Integer CATEGORY_TYPE_WORK = 9;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,10 @@ public class LiveRoom implements Serializable {
|
||||||
@ApiModelProperty(value = "直播间标题")
|
@ApiModelProperty(value = "直播间标题")
|
||||||
private String title;
|
private String title;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "分类ID")
|
||||||
|
@TableField("category_id")
|
||||||
|
private Integer categoryId;
|
||||||
|
|
||||||
@ApiModelProperty(value = "主播名称")
|
@ApiModelProperty(value = "主播名称")
|
||||||
@TableField("streamer_name")
|
@TableField("streamer_name")
|
||||||
private String streamerName;
|
private String streamerName;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,174 @@
|
||||||
|
package com.zbkj.front.controller;
|
||||||
|
|
||||||
|
import com.zbkj.common.constants.CategoryConstants;
|
||||||
|
import com.zbkj.common.model.category.Category;
|
||||||
|
import com.zbkj.common.result.CommonResult;
|
||||||
|
import com.zbkj.service.service.CategoryService;
|
||||||
|
import io.swagger.annotations.Api;
|
||||||
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import io.swagger.annotations.ApiParam;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 前端分类接口
|
||||||
|
* +----------------------------------------------------------------------
|
||||||
|
* | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
||||||
|
* +----------------------------------------------------------------------
|
||||||
|
* | Copyright (c) 2016~2025 https://www.crmeb.com All rights reserved.
|
||||||
|
* +----------------------------------------------------------------------
|
||||||
|
* | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
||||||
|
* +----------------------------------------------------------------------
|
||||||
|
* | Author: CRMEB Team <admin@crmeb.com>
|
||||||
|
* +----------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("api/front/category")
|
||||||
|
@Api(tags = "前端 -- 分类")
|
||||||
|
public class CategoryController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CategoryService categoryService;
|
||||||
|
|
||||||
|
@ApiOperation(value = "获取直播间分类列表")
|
||||||
|
@GetMapping("/live-room")
|
||||||
|
public CommonResult<List<CategoryResponse>> getLiveRoomCategories() {
|
||||||
|
List<Category> categories = categoryService.getList(
|
||||||
|
new com.zbkj.common.request.CategorySearchRequest()
|
||||||
|
.setType(CategoryConstants.CATEGORY_TYPE_LIVE_ROOM)
|
||||||
|
.setStatus(CategoryConstants.CATEGORY_STATUS_NORMAL)
|
||||||
|
);
|
||||||
|
|
||||||
|
List<CategoryResponse> response = categories.stream()
|
||||||
|
.map(this::toCategoryResponse)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return CommonResult.success(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation(value = "获取作品分类列表")
|
||||||
|
@GetMapping("/work")
|
||||||
|
public CommonResult<List<CategoryResponse>> getWorkCategories() {
|
||||||
|
List<Category> categories = categoryService.getList(
|
||||||
|
new com.zbkj.common.request.CategorySearchRequest()
|
||||||
|
.setType(CategoryConstants.CATEGORY_TYPE_WORK)
|
||||||
|
.setStatus(CategoryConstants.CATEGORY_STATUS_NORMAL)
|
||||||
|
);
|
||||||
|
|
||||||
|
List<CategoryResponse> response = categories.stream()
|
||||||
|
.map(this::toCategoryResponse)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return CommonResult.success(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation(value = "获取指定类型的分类列表")
|
||||||
|
@GetMapping("/list")
|
||||||
|
public CommonResult<List<CategoryResponse>> getCategories(
|
||||||
|
@ApiParam(value = "分类类型: 1=商品,3=文章,8=直播间,9=作品", required = true)
|
||||||
|
@RequestParam Integer type) {
|
||||||
|
|
||||||
|
List<Category> categories = categoryService.getList(
|
||||||
|
new com.zbkj.common.request.CategorySearchRequest()
|
||||||
|
.setType(type)
|
||||||
|
.setStatus(CategoryConstants.CATEGORY_STATUS_NORMAL)
|
||||||
|
);
|
||||||
|
|
||||||
|
List<CategoryResponse> response = categories.stream()
|
||||||
|
.map(this::toCategoryResponse)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return CommonResult.success(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation(value = "获取分类详情")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public CommonResult<CategoryResponse> getCategoryById(
|
||||||
|
@ApiParam(value = "分类ID", required = true)
|
||||||
|
@PathVariable Integer id) {
|
||||||
|
|
||||||
|
Category category = categoryService.getById(id);
|
||||||
|
if (category == null) {
|
||||||
|
return CommonResult.failed("分类不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
return CommonResult.success(toCategoryResponse(category));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为响应对象
|
||||||
|
*/
|
||||||
|
private CategoryResponse toCategoryResponse(Category category) {
|
||||||
|
CategoryResponse response = new CategoryResponse();
|
||||||
|
response.setId(category.getId());
|
||||||
|
response.setName(category.getName());
|
||||||
|
response.setPid(category.getPid());
|
||||||
|
response.setSort(category.getSort());
|
||||||
|
response.setExtra(category.getExtra());
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分类响应对象
|
||||||
|
*/
|
||||||
|
@io.swagger.annotations.ApiModel(value = "CategoryResponse", description = "分类响应")
|
||||||
|
public static class CategoryResponse {
|
||||||
|
@io.swagger.annotations.ApiModelProperty(value = "分类ID")
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@io.swagger.annotations.ApiModelProperty(value = "分类名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@io.swagger.annotations.ApiModelProperty(value = "父级ID")
|
||||||
|
private Integer pid;
|
||||||
|
|
||||||
|
@io.swagger.annotations.ApiModelProperty(value = "排序")
|
||||||
|
private Integer sort;
|
||||||
|
|
||||||
|
@io.swagger.annotations.ApiModelProperty(value = "扩展字段")
|
||||||
|
private String extra;
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getPid() {
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPid(Integer pid) {
|
||||||
|
this.pid = pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getSort() {
|
||||||
|
return sort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSort(Integer sort) {
|
||||||
|
this.sort = sort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getExtra() {
|
||||||
|
return extra;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExtra(String extra) {
|
||||||
|
this.extra = extra;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -48,10 +48,22 @@ public class LiveRoomController {
|
||||||
|
|
||||||
@ApiOperation(value = "公开:直播间列表(只返回直播中的房间)")
|
@ApiOperation(value = "公开:直播间列表(只返回直播中的房间)")
|
||||||
@GetMapping("/public/rooms")
|
@GetMapping("/public/rooms")
|
||||||
public CommonResult<List<LiveRoomResponse>> publicRooms(HttpServletRequest request) {
|
public CommonResult<List<LiveRoomResponse>> publicRooms(
|
||||||
|
@RequestParam(required = false) Integer categoryId,
|
||||||
|
HttpServletRequest request) {
|
||||||
String requestHost = resolveHost(request);
|
String requestHost = resolveHost(request);
|
||||||
|
|
||||||
|
List<LiveRoom> rooms;
|
||||||
|
if (categoryId != null && categoryId > 0) {
|
||||||
|
// 按分类筛选
|
||||||
|
rooms = liveRoomService.getByCategory(categoryId);
|
||||||
|
} else {
|
||||||
|
// 获取所有
|
||||||
|
rooms = liveRoomService.getAll();
|
||||||
|
}
|
||||||
|
|
||||||
// 只返回 is_live=1 的直播间
|
// 只返回 is_live=1 的直播间
|
||||||
return CommonResult.success(liveRoomService.getAll().stream()
|
return CommonResult.success(rooms.stream()
|
||||||
.filter(r -> r.getIsLive() != null && r.getIsLive() == 1)
|
.filter(r -> r.getIsLive() != null && r.getIsLive() == 1)
|
||||||
.map(r -> toResponse(r, requestHost))
|
.map(r -> toResponse(r, requestHost))
|
||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
|
|
@ -71,7 +83,7 @@ public class LiveRoomController {
|
||||||
public CommonResult<LiveRoomResponse> create(@RequestBody @Validated CreateLiveRoomRequest req, HttpServletRequest request) {
|
public CommonResult<LiveRoomResponse> create(@RequestBody @Validated CreateLiveRoomRequest req, HttpServletRequest request) {
|
||||||
Integer uid = frontTokenComponent.getUserId();
|
Integer uid = frontTokenComponent.getUserId();
|
||||||
if (uid == null) return CommonResult.failed("未登录");
|
if (uid == null) return CommonResult.failed("未登录");
|
||||||
LiveRoom room = liveRoomService.createRoom(uid, req.getTitle(), req.getStreamerName());
|
LiveRoom room = liveRoomService.createRoom(uid, req.getTitle(), req.getStreamerName(), req.getCategoryId());
|
||||||
return CommonResult.success(toResponse(room, resolveHost(request)));
|
return CommonResult.success(toResponse(room, resolveHost(request)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -166,6 +178,7 @@ public class LiveRoomController {
|
||||||
resp.setId(room.getId() == null ? null : String.valueOf(room.getId()));
|
resp.setId(room.getId() == null ? null : String.valueOf(room.getId()));
|
||||||
resp.setTitle(room.getTitle());
|
resp.setTitle(room.getTitle());
|
||||||
resp.setStreamerName(room.getStreamerName());
|
resp.setStreamerName(room.getStreamerName());
|
||||||
|
resp.setCategoryId(room.getCategoryId());
|
||||||
resp.setStreamKey(room.getStreamKey());
|
resp.setStreamKey(room.getStreamKey());
|
||||||
resp.setIsLive(room.getIsLive() != null && room.getIsLive() == 1);
|
resp.setIsLive(room.getIsLive() != null && room.getIsLive() == 1);
|
||||||
resp.setViewerCount(0);
|
resp.setViewerCount(0);
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,9 @@ public class CreateLiveRoomRequest {
|
||||||
@NotBlank
|
@NotBlank
|
||||||
private String title;
|
private String title;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "分类ID")
|
||||||
|
private Integer categoryId;
|
||||||
|
|
||||||
@ApiModelProperty(value = "主播名称")
|
@ApiModelProperty(value = "主播名称")
|
||||||
@NotBlank
|
@NotBlank
|
||||||
private String streamerName;
|
private String streamerName;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,12 @@ public class LiveRoomResponse {
|
||||||
@ApiModelProperty(value = "标题")
|
@ApiModelProperty(value = "标题")
|
||||||
private String title;
|
private String title;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "分类ID")
|
||||||
|
private Integer categoryId;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "分类名称")
|
||||||
|
private String categoryName;
|
||||||
|
|
||||||
@ApiModelProperty(value = "主播名")
|
@ApiModelProperty(value = "主播名")
|
||||||
private String streamerName;
|
private String streamerName;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,9 @@ public interface LiveRoomService extends IService<LiveRoom> {
|
||||||
|
|
||||||
List<LiveRoom> getAll();
|
List<LiveRoom> getAll();
|
||||||
|
|
||||||
LiveRoom createRoom(Integer uid, String title, String streamerName);
|
List<LiveRoom> getByCategory(Integer categoryId);
|
||||||
|
|
||||||
|
LiveRoom createRoom(Integer uid, String title, String streamerName, Integer categoryId);
|
||||||
|
|
||||||
boolean setLiveStatus(String streamKey, boolean isLive);
|
boolean setLiveStatus(String streamKey, boolean isLive);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,12 +27,23 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LiveRoom createRoom(Integer uid, String title, String streamerName) {
|
public List<LiveRoom> getByCategory(Integer categoryId) {
|
||||||
|
LambdaQueryWrapper<LiveRoom> qw = new LambdaQueryWrapper<>();
|
||||||
|
if (categoryId != null && categoryId > 0) {
|
||||||
|
qw.eq(LiveRoom::getCategoryId, categoryId);
|
||||||
|
}
|
||||||
|
qw.orderByDesc(LiveRoom::getCreateTime);
|
||||||
|
return dao.selectList(qw);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LiveRoom createRoom(Integer uid, String title, String streamerName, Integer categoryId) {
|
||||||
String streamKey = UUID.randomUUID().toString().replace("-", "");
|
String streamKey = UUID.randomUUID().toString().replace("-", "");
|
||||||
LiveRoom room = new LiveRoom();
|
LiveRoom room = new LiveRoom();
|
||||||
room.setUid(uid);
|
room.setUid(uid);
|
||||||
room.setTitle(title);
|
room.setTitle(title);
|
||||||
room.setStreamerName(streamerName);
|
room.setStreamerName(streamerName);
|
||||||
|
room.setCategoryId(categoryId);
|
||||||
room.setStreamKey(streamKey);
|
room.setStreamKey(streamKey);
|
||||||
room.setIsLive(0);
|
room.setIsLive(0);
|
||||||
room.setCreateTime(new Date());
|
room.setCreateTime(new Date());
|
||||||
|
|
|
||||||
|
|
@ -318,6 +318,20 @@
|
||||||
<version>0.9.0</version>
|
<version>0.9.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Spring Data JPA -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||||
|
<version>2.2.6.RELEASE</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Hibernate Validator -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.hibernate.validator</groupId>
|
||||||
|
<artifactId>hibernate-validator</artifactId>
|
||||||
|
<version>6.1.5.Final</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
|
|
|
||||||
461
Zhibo/zhibo-h/分类管理模块使用说明.md
Normal file
461
Zhibo/zhibo-h/分类管理模块使用说明.md
Normal file
|
|
@ -0,0 +1,461 @@
|
||||||
|
# 分类管理模块使用说明
|
||||||
|
|
||||||
|
## 📋 功能概述
|
||||||
|
|
||||||
|
分类管理模块为直播间和作品提供了分类功能,支持:
|
||||||
|
- ✅ 直播间分类管理
|
||||||
|
- ✅ 作品分类管理(预留)
|
||||||
|
- ✅ 分类列表查询
|
||||||
|
- ✅ 按分类筛选直播间
|
||||||
|
- ✅ 后台分类管理(复用现有管理功能)
|
||||||
|
|
||||||
|
## 🗄️ 数据库更新
|
||||||
|
|
||||||
|
### 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
|
||||||
332
Zhibo/zhibo-h/分类管理模块开发总结.md
Normal file
332
Zhibo/zhibo-h/分类管理模块开发总结.md
Normal file
|
|
@ -0,0 +1,332 @@
|
||||||
|
# 分类管理模块开发总结
|
||||||
|
|
||||||
|
## ✅ 已完成功能
|
||||||
|
|
||||||
|
### 1. 数据库层面
|
||||||
|
- ✅ 为 `eb_live_room` 表添加 `category_id` 字段
|
||||||
|
- ✅ 添加索引优化查询性能
|
||||||
|
- ✅ 插入默认的直播间分类数据(9个分类)
|
||||||
|
- ✅ 插入默认的作品分类数据(8个分类)
|
||||||
|
- ✅ 定义分类类型常量(type=8为直播间,type=9为作品)
|
||||||
|
|
||||||
|
### 2. 后端代码
|
||||||
|
- ✅ 更新 `CategoryConstants` 类,添加直播间和作品分类类型常量
|
||||||
|
- ✅ 创建前端分类控制器 `CategoryController`
|
||||||
|
- ✅ 实现获取直播间分类列表接口
|
||||||
|
- ✅ 实现获取作品分类列表接口
|
||||||
|
- ✅ 实现获取指定类型分类列表接口
|
||||||
|
- ✅ 实现获取分类详情接口
|
||||||
|
- ✅ 更新 `LiveRoom` 模型,添加 `categoryId` 字段
|
||||||
|
- ✅ 更新 `CreateLiveRoomRequest`,支持传入分类ID
|
||||||
|
- ✅ 更新 `LiveRoomResponse`,返回分类信息
|
||||||
|
- ✅ 更新 `LiveRoomService` 接口,添加按分类查询方法
|
||||||
|
- ✅ 更新 `LiveRoomServiceImpl` 实现,支持分类筛选
|
||||||
|
- ✅ 更新 `LiveRoomController`,支持按分类筛选直播间
|
||||||
|
|
||||||
|
### 3. API接口
|
||||||
|
|
||||||
|
#### 前端接口
|
||||||
|
| 接口 | 方法 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `/api/front/category/live-room` | GET | 获取直播间分类列表 |
|
||||||
|
| `/api/front/category/work` | GET | 获取作品分类列表 |
|
||||||
|
| `/api/front/category/list?type={type}` | GET | 获取指定类型的分类列表 |
|
||||||
|
| `/api/front/category/{id}` | GET | 获取分类详情 |
|
||||||
|
| `/api/front/live/public/rooms?categoryId={id}` | GET | 按分类筛选直播间 |
|
||||||
|
| `/api/front/live/rooms` | POST | 创建直播间(支持分类) |
|
||||||
|
|
||||||
|
#### 后台管理接口(复用现有)
|
||||||
|
| 接口 | 方法 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `/api/admin/category/list` | GET | 分类列表 |
|
||||||
|
| `/api/admin/category/list/tree` | GET | 树形结构分类 |
|
||||||
|
| `/api/admin/category/save` | POST | 新增分类 |
|
||||||
|
| `/api/admin/category/update` | POST | 修改分类 |
|
||||||
|
| `/api/admin/category/delete` | GET | 删除分类 |
|
||||||
|
| `/api/admin/category/updateStatus/{id}` | GET | 更改分类状态 |
|
||||||
|
|
||||||
|
### 4. 文档和测试
|
||||||
|
- ✅ 创建 SQL 更新脚本 `category_module_update.sql`
|
||||||
|
- ✅ 创建详细的使用说明文档 `分类管理模块使用说明.md`
|
||||||
|
- ✅ 创建 API 测试脚本 `test_category_api.bat` (Windows)
|
||||||
|
- ✅ 创建 API 测试脚本 `test_category_api.sh` (Linux/Mac)
|
||||||
|
- ✅ 创建开发总结文档 `分类管理模块开发总结.md`
|
||||||
|
|
||||||
|
## 📁 文件清单
|
||||||
|
|
||||||
|
### 新增文件
|
||||||
|
```
|
||||||
|
Zhibo/zhibo-h/
|
||||||
|
├── sql/
|
||||||
|
│ └── category_module_update.sql # 数据库更新脚本
|
||||||
|
├── crmeb-common/src/main/java/com/zbkj/common/
|
||||||
|
│ └── constants/
|
||||||
|
│ └── CategoryConstants.java # 分类常量类(更新)
|
||||||
|
├── crmeb-front/src/main/java/com/zbkj/front/
|
||||||
|
│ └── controller/
|
||||||
|
│ └── CategoryController.java # 前端分类控制器(新增)
|
||||||
|
├── 分类管理模块使用说明.md # 使用说明文档
|
||||||
|
├── 分类管理模块开发总结.md # 开发总结文档
|
||||||
|
├── test_category_api.bat # Windows测试脚本
|
||||||
|
└── test_category_api.sh # Linux/Mac测试脚本
|
||||||
|
```
|
||||||
|
|
||||||
|
### 修改文件
|
||||||
|
```
|
||||||
|
Zhibo/zhibo-h/
|
||||||
|
├── crmeb-common/src/main/java/com/zbkj/common/
|
||||||
|
│ └── model/live/
|
||||||
|
│ └── LiveRoom.java # 添加categoryId字段
|
||||||
|
├── crmeb-front/src/main/java/com/zbkj/front/
|
||||||
|
│ ├── request/live/
|
||||||
|
│ │ └── CreateLiveRoomRequest.java # 添加categoryId字段
|
||||||
|
│ ├── response/live/
|
||||||
|
│ │ └── LiveRoomResponse.java # 添加categoryId和categoryName字段
|
||||||
|
│ └── controller/
|
||||||
|
│ └── LiveRoomController.java # 支持分类筛选
|
||||||
|
└── crmeb-service/src/main/java/com/zbkj/service/
|
||||||
|
├── service/
|
||||||
|
│ └── LiveRoomService.java # 添加按分类查询方法
|
||||||
|
└── service/impl/
|
||||||
|
└── LiveRoomServiceImpl.java # 实现分类筛选逻辑
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 核心功能实现
|
||||||
|
|
||||||
|
### 1. 分类数据结构
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 分类表结构(复用现有表)
|
||||||
|
CREATE TABLE `eb_category` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`pid` int(11) DEFAULT '0' COMMENT '父级ID',
|
||||||
|
`path` varchar(255) DEFAULT '/0/' COMMENT '路径',
|
||||||
|
`name` varchar(50) NOT NULL COMMENT '分类名称',
|
||||||
|
`type` int(11) NOT NULL COMMENT '类型:8=直播间,9=作品',
|
||||||
|
`url` varchar(255) DEFAULT '' COMMENT '地址',
|
||||||
|
`extra` varchar(500) DEFAULT '' COMMENT '扩展字段',
|
||||||
|
`status` tinyint(1) DEFAULT '1' COMMENT '状态:1=正常,0=失效',
|
||||||
|
`sort` int(11) DEFAULT '0' COMMENT '排序',
|
||||||
|
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 直播间表添加分类字段
|
||||||
|
ALTER TABLE `eb_live_room` ADD COLUMN `category_id` INT(11) DEFAULT NULL;
|
||||||
|
ALTER TABLE `eb_live_room` ADD INDEX `idx_category_id` (`category_id`);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 默认分类数据
|
||||||
|
|
||||||
|
#### 直播间分类(type=8)
|
||||||
|
1. 娱乐 (sort=100)
|
||||||
|
2. 游戏 (sort=90)
|
||||||
|
3. 音乐 (sort=80)
|
||||||
|
4. 美食 (sort=70)
|
||||||
|
5. 户外 (sort=60)
|
||||||
|
6. 运动 (sort=50)
|
||||||
|
7. 教育 (sort=40)
|
||||||
|
8. 科技 (sort=30)
|
||||||
|
9. 其他 (sort=10)
|
||||||
|
|
||||||
|
#### 作品分类(type=9)
|
||||||
|
1. 摄影 (sort=100)
|
||||||
|
2. 绘画 (sort=90)
|
||||||
|
3. 设计 (sort=80)
|
||||||
|
4. 手工 (sort=70)
|
||||||
|
5. 音乐 (sort=60)
|
||||||
|
6. 舞蹈 (sort=50)
|
||||||
|
7. 文学 (sort=40)
|
||||||
|
8. 其他 (sort=10)
|
||||||
|
|
||||||
|
### 3. 关键代码实现
|
||||||
|
|
||||||
|
#### 前端分类控制器
|
||||||
|
```java
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("api/front/category")
|
||||||
|
public class CategoryController {
|
||||||
|
|
||||||
|
@GetMapping("/live-room")
|
||||||
|
public CommonResult<List<CategoryResponse>> getLiveRoomCategories() {
|
||||||
|
// 获取type=8的分类
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/work")
|
||||||
|
public CommonResult<List<CategoryResponse>> getWorkCategories() {
|
||||||
|
// 获取type=9的分类
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 直播间分类筛选
|
||||||
|
```java
|
||||||
|
@GetMapping("/public/rooms")
|
||||||
|
public CommonResult<List<LiveRoomResponse>> publicRooms(
|
||||||
|
@RequestParam(required = false) Integer categoryId,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
|
||||||
|
List<LiveRoom> rooms;
|
||||||
|
if (categoryId != null && categoryId > 0) {
|
||||||
|
rooms = liveRoomService.getByCategory(categoryId);
|
||||||
|
} else {
|
||||||
|
rooms = liveRoomService.getAll();
|
||||||
|
}
|
||||||
|
// 返回直播中的房间
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📊 技术特点
|
||||||
|
|
||||||
|
### 1. 复用现有架构
|
||||||
|
- 复用现有的 `eb_category` 表,避免创建新表
|
||||||
|
- 复用后台分类管理功能,无需重复开发
|
||||||
|
- 使用统一的分类服务 `CategoryService`
|
||||||
|
|
||||||
|
### 2. 灵活扩展
|
||||||
|
- 通过 `type` 字段区分不同类型的分类
|
||||||
|
- 支持多级分类(通过 `pid` 和 `path` 字段)
|
||||||
|
- 支持分类排序和启用/禁用
|
||||||
|
|
||||||
|
### 3. 性能优化
|
||||||
|
- 为 `category_id` 添加索引
|
||||||
|
- 分类列表建议客户端缓存
|
||||||
|
- 支持按分类快速筛选
|
||||||
|
|
||||||
|
### 4. 易于维护
|
||||||
|
- 清晰的代码结构
|
||||||
|
- 完整的文档说明
|
||||||
|
- 提供测试脚本
|
||||||
|
|
||||||
|
## 🚀 部署步骤
|
||||||
|
|
||||||
|
### 1. 数据库更新
|
||||||
|
```bash
|
||||||
|
# 执行SQL脚本
|
||||||
|
mysql -u root -p your_database < sql/category_module_update.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 编译项目
|
||||||
|
```bash
|
||||||
|
cd Zhibo/zhibo-h
|
||||||
|
mvn clean package -DskipTests
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 重启服务
|
||||||
|
```bash
|
||||||
|
# 停止现有服务
|
||||||
|
# 启动新服务
|
||||||
|
java -jar crmeb-admin/target/crmeb-admin.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 测试接口
|
||||||
|
```bash
|
||||||
|
# Windows
|
||||||
|
test_category_api.bat
|
||||||
|
|
||||||
|
# Linux/Mac
|
||||||
|
bash test_category_api.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📱 Android端集成建议
|
||||||
|
|
||||||
|
### 1. 添加分类筛选UI
|
||||||
|
- 使用 `ChipGroup` 显示分类标签
|
||||||
|
- 支持单选分类
|
||||||
|
- 添加"全部"选项
|
||||||
|
|
||||||
|
### 2. 实现分类筛选逻辑
|
||||||
|
```java
|
||||||
|
// 1. 加载分类列表
|
||||||
|
categoryApi.getLiveRoomCategories()
|
||||||
|
|
||||||
|
// 2. 监听分类选择
|
||||||
|
chipGroup.setOnCheckedChangeListener()
|
||||||
|
|
||||||
|
// 3. 按分类加载直播间
|
||||||
|
categoryApi.getRoomsByCategory(categoryId)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 创建直播间时选择分类
|
||||||
|
```java
|
||||||
|
CreateLiveRoomRequest request = new CreateLiveRoomRequest();
|
||||||
|
request.setTitle("直播间标题");
|
||||||
|
request.setStreamerName("主播名称");
|
||||||
|
request.setCategoryId(selectedCategoryId); // 选中的分类ID
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔄 后续扩展建议
|
||||||
|
|
||||||
|
### 1. 作品分类功能
|
||||||
|
当需要为作品添加分类时:
|
||||||
|
1. 为作品表添加 `category_id` 字段
|
||||||
|
2. 创建作品时传入 `categoryId`
|
||||||
|
3. 使用 `/api/front/category/work` 获取作品分类
|
||||||
|
|
||||||
|
### 2. 分类统计
|
||||||
|
添加统计功能,显示每个分类下的内容数量:
|
||||||
|
```sql
|
||||||
|
SELECT c.id, c.name, COUNT(lr.id) as 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;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 热门分类
|
||||||
|
根据观看量、点赞数等指标计算热门分类。
|
||||||
|
|
||||||
|
### 4. 分类图标
|
||||||
|
利用 `extra` 字段存储分类图标URL:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"icon": "https://example.com/icons/game.png"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 多级分类
|
||||||
|
支持二级分类,例如:
|
||||||
|
- 游戏(一级)
|
||||||
|
- 手游(二级)
|
||||||
|
- 端游(二级)
|
||||||
|
- 主机游戏(二级)
|
||||||
|
|
||||||
|
## ⚠️ 注意事项
|
||||||
|
|
||||||
|
1. **数据库备份**:执行SQL脚本前务必备份数据库
|
||||||
|
2. **分类ID可选**:创建直播间时 `categoryId` 可以为 null
|
||||||
|
3. **权限控制**:后台分类管理需要管理员权限
|
||||||
|
4. **缓存策略**:建议客户端缓存分类列表
|
||||||
|
5. **兼容性**:现有直播间的 `category_id` 为 null,不影响正常使用
|
||||||
|
|
||||||
|
## 📈 预期效果
|
||||||
|
|
||||||
|
### 1. 用户体验提升
|
||||||
|
- 用户可以快速找到感兴趣的直播间
|
||||||
|
- 分类筛选提高内容发现效率
|
||||||
|
- 清晰的分类结构便于浏览
|
||||||
|
|
||||||
|
### 2. 运营管理优化
|
||||||
|
- 后台可以灵活管理分类
|
||||||
|
- 支持分类数据统计分析
|
||||||
|
- 便于内容运营和推荐
|
||||||
|
|
||||||
|
### 3. 系统扩展性
|
||||||
|
- 易于添加新的分类类型
|
||||||
|
- 支持多级分类结构
|
||||||
|
- 为后续功能预留扩展空间
|
||||||
|
|
||||||
|
## 📞 技术支持
|
||||||
|
|
||||||
|
如有问题,请查看:
|
||||||
|
- 使用说明:`分类管理模块使用说明.md`
|
||||||
|
- 项目文档:`直播IM系统开发指南.md`
|
||||||
|
- 接口清单:`后端接口TODO清单-总览.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**开发完成时间:** 2025-12-25
|
||||||
|
**开发者:** CRMEB Team
|
||||||
|
**版本:** v1.0
|
||||||
|
**状态:** ✅ 已完成,可投入使用
|
||||||
|
|
@ -1404,10 +1404,10 @@ server {
|
||||||
- 充值功能 ✅
|
- 充值功能 ✅
|
||||||
- **缺失**: 支付宝支付 ❌
|
- **缺失**: 支付宝支付 ❌
|
||||||
|
|
||||||
- **内容管理**: 35% ⚠️
|
- **内容管理**: 45% ⚠️
|
||||||
- 多媒体上传 ✅
|
- 多媒体上传 ✅
|
||||||
- 作品管理 ❌
|
- 作品管理 ❌
|
||||||
- 分类管理 ⚠️ (商品分类已实现)
|
- 分类管理 ✅ (商品分类、直播间分类、作品分类已实现)
|
||||||
- 搜索功能 ⚠️ (用户搜索已实现)
|
- 搜索功能 ⚠️ (用户搜索已实现)
|
||||||
|
|
||||||
- **安全防护**: 20% ❌
|
- **安全防护**: 20% ❌
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user