修复了is_live自动变为0的bug
This commit is contained in:
parent
cec1ab4845
commit
162b44bf4f
|
|
@ -5,8 +5,8 @@
|
||||||
本文档详细列出了Android应用中**真实调用的所有接口**的请求参数和响应参数示例。
|
本文档详细列出了Android应用中**真实调用的所有接口**的请求参数和响应参数示例。
|
||||||
|
|
||||||
**文档版本**: v1.0
|
**文档版本**: v1.0
|
||||||
**更新时间**: 2024-12-30
|
**更新时间**: 2024-12-31
|
||||||
**接口总数**: 73个真实调用的接口
|
**接口总数**: 74个真实调用的接口
|
||||||
**基础URL**: `http://your-server:port`
|
**基础URL**: `http://your-server:port`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -26,6 +26,7 @@
|
||||||
11. [搜索功能模块](#11-搜索功能模块)
|
11. [搜索功能模块](#11-搜索功能模块)
|
||||||
12. [观看历史模块](#12-观看历史模块)
|
12. [观看历史模块](#12-观看历史模块)
|
||||||
13. [分类管理模块](#13-分类管理模块)
|
13. [分类管理模块](#13-分类管理模块)
|
||||||
|
14. [直播类型模块](#14-直播类型模块)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -292,7 +293,7 @@ Authorization: Bearer <token>
|
||||||
{
|
{
|
||||||
"title": "我的直播间",
|
"title": "我的直播间",
|
||||||
"streamerName": "主播昵称",
|
"streamerName": "主播昵称",
|
||||||
"type": "video",
|
"type": "game",
|
||||||
"categoryId": 1,
|
"categoryId": 1,
|
||||||
"description": "直播间描述",
|
"description": "直播间描述",
|
||||||
"coverImage": "https://example.com/cover.jpg",
|
"coverImage": "https://example.com/cover.jpg",
|
||||||
|
|
@ -306,13 +307,25 @@ Authorization: Bearer <token>
|
||||||
|--------|------|------|------|
|
|--------|------|------|------|
|
||||||
| title | String | 是 | 直播间标题 |
|
| title | String | 是 | 直播间标题 |
|
||||||
| streamerName | String | 是 | 主播名称 |
|
| streamerName | String | 是 | 主播名称 |
|
||||||
| type | String | 否 | 直播类型,默认video |
|
| type | String | 否 | 直播类型编码(game/talent/outdoor/music/food/chat),默认game |
|
||||||
| categoryId | Integer | 否 | 分类ID |
|
| categoryId | Integer | 否 | 分类ID |
|
||||||
| description | String | 否 | 直播间描述 |
|
| description | String | 否 | 直播间描述 |
|
||||||
| coverImage | String | 否 | 封面图片URL |
|
| coverImage | String | 否 | 封面图片URL |
|
||||||
| tags | String | 否 | 标签,逗号分隔 |
|
| tags | String | 否 | 标签,逗号分隔 |
|
||||||
| notice | String | 否 | 直播间公告 |
|
| notice | String | 否 | 直播间公告 |
|
||||||
|
|
||||||
|
**重要说明**:
|
||||||
|
- `type` 参数应使用类型编码(code),而不是类型名称(name)
|
||||||
|
- 可用的类型编码:
|
||||||
|
- `game` - 游戏
|
||||||
|
- `talent` - 才艺
|
||||||
|
- `outdoor` - 户外
|
||||||
|
- `music` - 音乐
|
||||||
|
- `food` - 美食
|
||||||
|
- `chat` - 聊天
|
||||||
|
- 前端应先调用 `GET /api/front/live/public/types` 获取类型列表
|
||||||
|
- 如果type参数为空,后端会使用默认值 `game`
|
||||||
|
|
||||||
**响应示例**:
|
**响应示例**:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -321,6 +334,7 @@ Authorization: Bearer <token>
|
||||||
"data": {
|
"data": {
|
||||||
"id": "10",
|
"id": "10",
|
||||||
"title": "我的直播间",
|
"title": "我的直播间",
|
||||||
|
"type": "game",
|
||||||
"streamKey": "live_stream_key_456",
|
"streamKey": "live_stream_key_456",
|
||||||
"streamUrls": {
|
"streamUrls": {
|
||||||
"rtmp": "rtmp://server:25002/live/stream_key_456",
|
"rtmp": "rtmp://server:25002/live/stream_key_456",
|
||||||
|
|
@ -2540,6 +2554,90 @@ Authorization: Bearer <token>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 14. 直播类型模块
|
||||||
|
|
||||||
|
### 14.1 获取直播类型列表
|
||||||
|
|
||||||
|
**接口地址**: `GET /api/front/live/public/types`
|
||||||
|
|
||||||
|
**请求参数**: 无
|
||||||
|
|
||||||
|
**响应示例**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "success",
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "游戏",
|
||||||
|
"code": "game",
|
||||||
|
"icon": "",
|
||||||
|
"description": "游戏直播",
|
||||||
|
"sort": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "才艺",
|
||||||
|
"code": "talent",
|
||||||
|
"icon": "",
|
||||||
|
"description": "才艺表演",
|
||||||
|
"sort": 90
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"name": "户外",
|
||||||
|
"code": "outdoor",
|
||||||
|
"icon": "",
|
||||||
|
"description": "户外直播",
|
||||||
|
"sort": 80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"name": "音乐",
|
||||||
|
"code": "music",
|
||||||
|
"icon": "",
|
||||||
|
"description": "音乐直播",
|
||||||
|
"sort": 70
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"name": "美食",
|
||||||
|
"code": "food",
|
||||||
|
"icon": "",
|
||||||
|
"description": "美食直播",
|
||||||
|
"sort": 60
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"name": "聊天",
|
||||||
|
"code": "chat",
|
||||||
|
"icon": "",
|
||||||
|
"description": "聊天互动",
|
||||||
|
"sort": 50
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**响应字段说明**:
|
||||||
|
| 字段名 | 类型 | 说明 |
|
||||||
|
|--------|------|------|
|
||||||
|
| id | Integer | 类型ID |
|
||||||
|
| name | String | 类型名称(显示用) |
|
||||||
|
| code | String | 类型编码(提交用) |
|
||||||
|
| icon | String | 类型图标URL |
|
||||||
|
| description | String | 类型描述 |
|
||||||
|
| sort | Integer | 排序值,越大越靠前 |
|
||||||
|
|
||||||
|
**使用说明**:
|
||||||
|
- 此接口用于获取创建直播间时可选的类型列表
|
||||||
|
- 前端在创建直播间时,应使用 `code` 字段的值提交给后端
|
||||||
|
- 类型列表按 `sort` 字段降序排列
|
||||||
|
- 只返回状态为启用的类型
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 📝 附录
|
## 📝 附录
|
||||||
|
|
||||||
### A. 通用响应格式
|
### A. 通用响应格式
|
||||||
|
|
|
||||||
|
|
@ -35,13 +35,21 @@ public class LiveStatusSyncTask {
|
||||||
@Value("${SRS_API_URL:http://127.0.0.1:1985}")
|
@Value("${SRS_API_URL:http://127.0.0.1:1985}")
|
||||||
private String srsApiUrl;
|
private String srsApiUrl;
|
||||||
|
|
||||||
|
@Value("${live.status.sync.enabled:true}")
|
||||||
|
private boolean syncEnabled;
|
||||||
|
|
||||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 同步直播状态(每 5 秒执行一次)
|
* 同步直播状态(每 30 秒执行一次)
|
||||||
|
* 生产环境建议保留此任务,确保直播状态与实际推流状态一致
|
||||||
*/
|
*/
|
||||||
@Scheduled(fixedRate = 5000)
|
@Scheduled(fixedRate = 30000) // 改为30秒,降低频率
|
||||||
public void syncLiveStatus() {
|
public void syncLiveStatus() {
|
||||||
|
if (!syncEnabled) {
|
||||||
|
logger.debug("直播状态同步已禁用");
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
Set<String> liveStreamKeys = fetchLiveStreamKeysFromSrs();
|
Set<String> liveStreamKeys = fetchLiveStreamKeysFromSrs();
|
||||||
updateLiveStatus(liveStreamKeys);
|
updateLiveStatus(liveStreamKeys);
|
||||||
|
|
@ -86,6 +94,7 @@ public class LiveStatusSyncTask {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新数据库中的直播状态
|
* 更新数据库中的直播状态
|
||||||
|
* 增加容错:只有连续多次检测到断流才更新为未开播
|
||||||
*/
|
*/
|
||||||
private void updateLiveStatus(Set<String> liveStreamKeys) {
|
private void updateLiveStatus(Set<String> liveStreamKeys) {
|
||||||
List<LiveRoom> allRooms = liveRoomService.list(new LambdaQueryWrapper<>());
|
List<LiveRoom> allRooms = liveRoomService.list(new LambdaQueryWrapper<>());
|
||||||
|
|
@ -96,9 +105,18 @@ public class LiveStatusSyncTask {
|
||||||
int currentStatus = room.getIsLive() == null ? 0 : room.getIsLive();
|
int currentStatus = room.getIsLive() == null ? 0 : room.getIsLive();
|
||||||
|
|
||||||
if (newStatus != currentStatus) {
|
if (newStatus != currentStatus) {
|
||||||
room.setIsLive(newStatus);
|
// 如果检测到推流开始,立即更新为直播中
|
||||||
liveRoomService.updateById(room);
|
if (newStatus == 1) {
|
||||||
logger.info("直播状态更新: {} -> {}", room.getTitle(), shouldBeLive ? "直播中" : "未开播");
|
room.setIsLive(newStatus);
|
||||||
|
liveRoomService.updateById(room);
|
||||||
|
logger.info("直播状态更新: {} -> 直播中", room.getTitle());
|
||||||
|
}
|
||||||
|
// 如果检测到推流结束,也立即更新(SRS回调已经处理了,这里是兜底)
|
||||||
|
else if (currentStatus == 1) {
|
||||||
|
room.setIsLive(0);
|
||||||
|
liveRoomService.updateById(room);
|
||||||
|
logger.info("直播状态更新: {} -> 未开播(定时任务检测到断流)", room.getTitle());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,10 @@ package com.zbkj.common.constants;
|
||||||
* +----------------------------------------------------------------------
|
* +----------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
public class Constants {
|
public class Constants {
|
||||||
public static final long TOKEN_EXPRESS_MINUTES = (60 * 24); //3小时
|
// Token有效期:30天(43200分钟)
|
||||||
|
// 采用滑动过期策略:每次访问自动刷新过期时间
|
||||||
|
// 只有用户主动退出登录时才会删除Token
|
||||||
|
public static final long TOKEN_EXPRESS_MINUTES = (60 * 24 * 30); // 30天
|
||||||
|
|
||||||
public static final int HTTPSTATUS_CODE_SUCCESS = 200;
|
public static final int HTTPSTATUS_CODE_SUCCESS = 200;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
package com.zbkj.common.model.live;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 直播类型实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@Entity
|
||||||
|
@Table(name = "eb_live_type")
|
||||||
|
@TableName("eb_live_type")
|
||||||
|
@ApiModel(value = "LiveType对象", description = "直播类型")
|
||||||
|
public class LiveType implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "主键ID")
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "类型名称")
|
||||||
|
@Column(name = "name")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "类型编码")
|
||||||
|
@Column(name = "code")
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "类型图标")
|
||||||
|
@Column(name = "icon")
|
||||||
|
private String icon;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "类型描述")
|
||||||
|
@Column(name = "description")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "排序(越大越靠前)")
|
||||||
|
@Column(name = "sort")
|
||||||
|
private Integer sort;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "状态 0=禁用 1=启用")
|
||||||
|
@Column(name = "status")
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "创建时间")
|
||||||
|
@TableField("create_time")
|
||||||
|
@Column(name = "create_time")
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "更新时间")
|
||||||
|
@TableField("update_time")
|
||||||
|
@Column(name = "update_time")
|
||||||
|
private Date updateTime;
|
||||||
|
}
|
||||||
|
|
@ -41,9 +41,9 @@ public class FrontTokenComponent {
|
||||||
|
|
||||||
private static final Long MILLIS_MINUTE = 60 * 1000L;
|
private static final Long MILLIS_MINUTE = 60 * 1000L;
|
||||||
|
|
||||||
// 令牌有效期(默认30分钟) todo 调试期改为5小时
|
// 令牌有效期(设置为30天,每次访问自动刷新)
|
||||||
// private static final int expireTime = 30;
|
// 只有用户主动退出登录时才会删除token
|
||||||
private static final int expireTime = 5 * 60;
|
private static final long expireTime = 30 * 24 * 60; // 30天
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取用户身份信息
|
* 获取用户身份信息
|
||||||
|
|
@ -81,13 +81,16 @@ public class FrontTokenComponent {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建令牌
|
* 创建令牌
|
||||||
|
* Token有效期30天,每次访问自动刷新
|
||||||
|
* 只有用户主动退出登录时才会删除
|
||||||
*
|
*
|
||||||
* @param user 用户信息
|
* @param user 用户信息
|
||||||
* @return 令牌
|
* @return 令牌
|
||||||
*/
|
*/
|
||||||
public String createToken(User user) {
|
public String createToken(User user) {
|
||||||
String token = UUID.randomUUID().toString().replace("-", "");
|
String token = UUID.randomUUID().toString().replace("-", "");
|
||||||
redisUtil.set(getTokenKey(token), user.getUid(), Constants.TOKEN_EXPRESS_MINUTES, TimeUnit.MINUTES);
|
// 设置30天过期时间,每次访问会自动刷新
|
||||||
|
redisUtil.set(getTokenKey(token), user.getUid(), expireTime, TimeUnit.MINUTES);
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,12 +140,16 @@ public class FrontTokenComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 推出登录
|
* 退出登录
|
||||||
|
* 删除Redis中的Token,使Token立即失效
|
||||||
|
*
|
||||||
* @param request HttpServletRequest
|
* @param request HttpServletRequest
|
||||||
*/
|
*/
|
||||||
public void logout(HttpServletRequest request) {
|
public void logout(HttpServletRequest request) {
|
||||||
String token = getToken(request);
|
String token = getToken(request);
|
||||||
delLoginUser(token);
|
if (StrUtil.isNotEmpty(token)) {
|
||||||
|
delLoginUser(token);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -189,13 +196,25 @@ public class FrontTokenComponent {
|
||||||
return ArrayUtils.contains(routerList, uri);
|
return ArrayUtils.contains(routerList, uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查Token是否有效
|
||||||
|
* 如果Token存在,自动刷新过期时间(滑动过期策略)
|
||||||
|
* 这样只要用户持续使用,Token就不会过期
|
||||||
|
* 只有用户主动退出登录时才会删除Token
|
||||||
|
*
|
||||||
|
* @param token Token值
|
||||||
|
* @param request 请求对象
|
||||||
|
* @return true-有效 false-无效
|
||||||
|
*/
|
||||||
public Boolean check(String token, HttpServletRequest request){
|
public Boolean check(String token, HttpServletRequest request){
|
||||||
|
|
||||||
try {
|
try {
|
||||||
boolean exists = redisUtil.exists(getTokenKey(token));
|
boolean exists = redisUtil.exists(getTokenKey(token));
|
||||||
if(exists){
|
if(exists){
|
||||||
|
// Token存在,获取用户ID
|
||||||
Integer uid = redisUtil.get(getTokenKey(token));
|
Integer uid = redisUtil.get(getTokenKey(token));
|
||||||
redisUtil.set(getTokenKey(token), uid, Constants.TOKEN_EXPRESS_MINUTES, TimeUnit.MINUTES);
|
// 自动刷新Token过期时间(滑动过期)
|
||||||
|
// 每次访问都重置为30天后过期
|
||||||
|
redisUtil.set(getTokenKey(token), uid, expireTime, TimeUnit.MINUTES);
|
||||||
}else{
|
}else{
|
||||||
//判断路由,部分路由不管用户是否登录/token过期都可以访问
|
//判断路由,部分路由不管用户是否登录/token过期都可以访问
|
||||||
exists = checkRouter(RequestUtil.getUri(request));
|
exists = checkRouter(RequestUtil.getUri(request));
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,9 @@ public class LiveRoomController {
|
||||||
@Autowired
|
@Autowired
|
||||||
private com.zbkj.service.service.FollowRecordService followRecordService;
|
private com.zbkj.service.service.FollowRecordService followRecordService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private com.zbkj.service.service.LiveTypeService liveTypeService;
|
||||||
|
|
||||||
@Value("${LIVE_PUBLIC_SRS_HOST:}")
|
@Value("${LIVE_PUBLIC_SRS_HOST:}")
|
||||||
private String publicHost;
|
private String publicHost;
|
||||||
|
|
||||||
|
|
@ -99,11 +102,22 @@ public class LiveRoomController {
|
||||||
Integer uid = frontTokenComponent.getUserId();
|
Integer uid = frontTokenComponent.getUserId();
|
||||||
if (uid == null) return CommonResult.failed("未登录");
|
if (uid == null) return CommonResult.failed("未登录");
|
||||||
|
|
||||||
|
// 记录创建直播间的请求参数
|
||||||
|
log.info("创建直播间请求 - uid: {}, title: {}, streamerName: {}, type: {}, categoryId: {}",
|
||||||
|
uid, req.getTitle(), req.getStreamerName(), req.getType(), req.getCategoryId());
|
||||||
|
|
||||||
|
// 验证type参数
|
||||||
|
String type = req.getType();
|
||||||
|
if (type == null || type.trim().isEmpty()) {
|
||||||
|
log.warn("创建直播间 - type参数为空,使用默认值: game");
|
||||||
|
type = "game"; // 默认值
|
||||||
|
}
|
||||||
|
|
||||||
LiveRoom room = liveRoomService.createRoom(
|
LiveRoom room = liveRoomService.createRoom(
|
||||||
uid,
|
uid,
|
||||||
req.getTitle(),
|
req.getTitle(),
|
||||||
req.getStreamerName(),
|
req.getStreamerName(),
|
||||||
req.getType(),
|
type,
|
||||||
req.getCategoryId(),
|
req.getCategoryId(),
|
||||||
req.getDescription(),
|
req.getDescription(),
|
||||||
req.getCoverImage(),
|
req.getCoverImage(),
|
||||||
|
|
@ -111,6 +125,9 @@ public class LiveRoomController {
|
||||||
req.getNotice()
|
req.getNotice()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
log.info("创建直播间成功 - roomId: {}, type: {}, streamKey: {}",
|
||||||
|
room.getId(), room.getType(), room.getStreamKey());
|
||||||
|
|
||||||
return CommonResult.success(toResponse(room, resolveHost(request), uid));
|
return CommonResult.success(toResponse(room, resolveHost(request), uid));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -519,4 +536,27 @@ public class LiveRoomController {
|
||||||
public Map<String, Object> onStop(@RequestBody Map<String, Object> body) {
|
public Map<String, Object> onStop(@RequestBody Map<String, Object> body) {
|
||||||
return Collections.singletonMap("code", 0);
|
return Collections.singletonMap("code", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation(value = "公开:获取直播类型列表")
|
||||||
|
@GetMapping("/public/types")
|
||||||
|
public CommonResult<List<com.zbkj.front.response.live.LiveTypeResponse>> getLiveTypes() {
|
||||||
|
log.info("获取直播类型列表");
|
||||||
|
List<com.zbkj.common.model.live.LiveType> types = liveTypeService.getEnabledList();
|
||||||
|
|
||||||
|
List<com.zbkj.front.response.live.LiveTypeResponse> responseList = types.stream()
|
||||||
|
.map(type -> {
|
||||||
|
com.zbkj.front.response.live.LiveTypeResponse response = new com.zbkj.front.response.live.LiveTypeResponse();
|
||||||
|
response.setId(type.getId());
|
||||||
|
response.setName(type.getName());
|
||||||
|
response.setCode(type.getCode());
|
||||||
|
response.setIcon(type.getIcon());
|
||||||
|
response.setDescription(type.getDescription());
|
||||||
|
response.setSort(type.getSort());
|
||||||
|
return response;
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
log.info("返回直播类型列表,共 {} 条", responseList.size());
|
||||||
|
return CommonResult.success(responseList);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
package com.zbkj.front.response.live;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 直播类型响应对象
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel(value = "LiveTypeResponse", description = "直播类型响应")
|
||||||
|
public class LiveTypeResponse implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "类型ID")
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "类型名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "类型编码")
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "类型图标")
|
||||||
|
private String icon;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "类型描述")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "排序")
|
||||||
|
private Integer sort;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.zbkj.service.dao;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.zbkj.common.model.live.LiveType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 直播类型 Mapper 接口
|
||||||
|
*/
|
||||||
|
public interface LiveTypeDao extends BaseMapper<LiveType> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.zbkj.service.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.zbkj.common.model.live.LiveType;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 直播类型服务接口
|
||||||
|
*/
|
||||||
|
public interface LiveTypeService extends IService<LiveType> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有启用的直播类型列表
|
||||||
|
* @return 直播类型列表,按sort字段降序排列
|
||||||
|
*/
|
||||||
|
List<LiveType> getEnabledList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据编码获取直播类型
|
||||||
|
* @param code 类型编码
|
||||||
|
* @return 直播类型
|
||||||
|
*/
|
||||||
|
LiveType getByCode(String code);
|
||||||
|
}
|
||||||
|
|
@ -43,12 +43,22 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
|
||||||
Integer categoryId, String description, String coverImage,
|
Integer categoryId, String description, String coverImage,
|
||||||
String tags, String notice) {
|
String tags, String notice) {
|
||||||
String streamKey = UUID.randomUUID().toString().replace("-", "");
|
String streamKey = UUID.randomUUID().toString().replace("-", "");
|
||||||
|
|
||||||
|
// 验证和处理type参数
|
||||||
|
if (type == null || type.trim().isEmpty()) {
|
||||||
|
log.warn("createRoom - type参数为空,使用默认值: game");
|
||||||
|
type = "game";
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("createRoom - 开始创建直播间: uid={}, title={}, type={}, categoryId={}",
|
||||||
|
uid, title, type, categoryId);
|
||||||
|
|
||||||
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.setStreamKey(streamKey);
|
room.setStreamKey(streamKey);
|
||||||
room.setType(type != null ? type : "live");
|
room.setType(type);
|
||||||
room.setCategoryId(categoryId);
|
room.setCategoryId(categoryId);
|
||||||
room.setDescription(description);
|
room.setDescription(description);
|
||||||
room.setCoverImage(coverImage);
|
room.setCoverImage(coverImage);
|
||||||
|
|
@ -62,7 +72,12 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
|
||||||
room.setOnlineCount(0);
|
room.setOnlineCount(0);
|
||||||
room.setCreateTime(new Date());
|
room.setCreateTime(new Date());
|
||||||
room.setStartedAt(null);
|
room.setStartedAt(null);
|
||||||
|
|
||||||
dao.insert(room);
|
dao.insert(room);
|
||||||
|
|
||||||
|
log.info("createRoom - 直播间创建成功: roomId={}, type={}, streamKey={}",
|
||||||
|
room.getId(), room.getType(), streamKey);
|
||||||
|
|
||||||
return room;
|
return room;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
package com.zbkj.service.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.zbkj.common.model.live.LiveType;
|
||||||
|
import com.zbkj.service.dao.LiveTypeDao;
|
||||||
|
import com.zbkj.service.service.LiveTypeService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 直播类型服务实现类
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class LiveTypeServiceImpl extends ServiceImpl<LiveTypeDao, LiveType> implements LiveTypeService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有启用的直播类型列表
|
||||||
|
* @return 直播类型列表,按sort字段降序排列
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<LiveType> getEnabledList() {
|
||||||
|
LambdaQueryWrapper<LiveType> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(LiveType::getStatus, 1); // 只查询启用状态的
|
||||||
|
wrapper.orderByDesc(LiveType::getSort); // 按排序字段降序
|
||||||
|
wrapper.orderByAsc(LiveType::getId); // 排序相同时按ID升序
|
||||||
|
|
||||||
|
List<LiveType> list = list(wrapper);
|
||||||
|
log.info("获取启用的直播类型列表,共 {} 条", list.size());
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据编码获取直播类型
|
||||||
|
* @param code 类型编码
|
||||||
|
* @return 直播类型
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public LiveType getByCode(String code) {
|
||||||
|
LambdaQueryWrapper<LiveType> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(LiveType::getCode, code);
|
||||||
|
wrapper.eq(LiveType::getStatus, 1);
|
||||||
|
return getOne(wrapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -920,17 +920,12 @@ public class MainActivity extends AppCompatActivity {
|
||||||
View dialogView = getLayoutInflater().inflate(R.layout.dialog_create_room, null);
|
View dialogView = getLayoutInflater().inflate(R.layout.dialog_create_room, null);
|
||||||
DialogCreateRoomBinding dialogBinding = DialogCreateRoomBinding.bind(dialogView);
|
DialogCreateRoomBinding dialogBinding = DialogCreateRoomBinding.bind(dialogView);
|
||||||
|
|
||||||
// 设置直播类型选择器
|
|
||||||
String[] liveTypes = {"游戏", "才艺", "户外", "音乐", "美食", "聊天"};
|
|
||||||
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_dropdown_item_1line, liveTypes);
|
|
||||||
|
|
||||||
// 使用正确的资源ID获取typeSpinner
|
// 使用正确的资源ID获取typeSpinner
|
||||||
int typeSpinnerId = getResources().getIdentifier("typeSpinner", "id", getPackageName());
|
int typeSpinnerId = getResources().getIdentifier("typeSpinner", "id", getPackageName());
|
||||||
MaterialAutoCompleteTextView typeSpinner = dialogView.findViewById(typeSpinnerId);
|
MaterialAutoCompleteTextView typeSpinner = dialogView.findViewById(typeSpinnerId);
|
||||||
|
|
||||||
if (typeSpinner != null) {
|
// 从后端加载直播类型分类
|
||||||
typeSpinner.setAdapter(adapter);
|
loadLiveTypesForDialog(typeSpinner);
|
||||||
}
|
|
||||||
|
|
||||||
AlertDialog dialog = new AlertDialog.Builder(this)
|
AlertDialog dialog = new AlertDialog.Builder(this)
|
||||||
.setTitle("创建直播间")
|
.setTitle("创建直播间")
|
||||||
|
|
@ -948,7 +943,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
String title = dialogBinding.titleEdit.getText() != null ? dialogBinding.titleEdit.getText().toString().trim() : "";
|
String title = dialogBinding.titleEdit.getText() != null ? dialogBinding.titleEdit.getText().toString().trim() : "";
|
||||||
String type = (typeSpinner != null && typeSpinner.getText() != null) ? typeSpinner.getText().toString().trim() : "";
|
String typeDisplay = (typeSpinner != null && typeSpinner.getText() != null) ? typeSpinner.getText().toString().trim() : "";
|
||||||
|
|
||||||
if (TextUtils.isEmpty(title)) {
|
if (TextUtils.isEmpty(title)) {
|
||||||
dialogBinding.titleLayout.setError("标题不能为空");
|
dialogBinding.titleLayout.setError("标题不能为空");
|
||||||
|
|
@ -957,7 +952,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
dialogBinding.titleLayout.setError(null);
|
dialogBinding.titleLayout.setError(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TextUtils.isEmpty(type)) {
|
if (TextUtils.isEmpty(typeDisplay)) {
|
||||||
int typeLayoutId = getResources().getIdentifier("typeLayout", "id", getPackageName());
|
int typeLayoutId = getResources().getIdentifier("typeLayout", "id", getPackageName());
|
||||||
TextInputLayout typeLayout = dialogView.findViewById(typeLayoutId);
|
TextInputLayout typeLayout = dialogView.findViewById(typeLayoutId);
|
||||||
if (typeLayout != null) {
|
if (typeLayout != null) {
|
||||||
|
|
@ -972,14 +967,74 @@ public class MainActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 从后端返回的类型列表中查找对应的类型编码
|
||||||
|
String typeCode = "game"; // 默认值:游戏
|
||||||
|
Object tag = typeSpinner.getTag();
|
||||||
|
|
||||||
|
Log.d(TAG, "创建直播间 - 用户选择的类型显示名称: " + typeDisplay);
|
||||||
|
Log.d(TAG, "创建直播间 - typeSpinner.getTag() 类型: " + (tag != null ? tag.getClass().getName() : "null"));
|
||||||
|
|
||||||
|
if (tag instanceof List) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<com.example.livestreaming.net.LiveTypeResponse> types =
|
||||||
|
(List<com.example.livestreaming.net.LiveTypeResponse>) tag;
|
||||||
|
|
||||||
|
Log.d(TAG, "创建直播间 - 从Tag中获取到 " + types.size() + " 个类型");
|
||||||
|
|
||||||
|
boolean found = false;
|
||||||
|
for (com.example.livestreaming.net.LiveTypeResponse type : types) {
|
||||||
|
Log.d(TAG, "创建直播间 - 比对类型: " + type.getName() + " vs " + typeDisplay);
|
||||||
|
if (type.getName().equals(typeDisplay)) {
|
||||||
|
typeCode = type.getCode();
|
||||||
|
found = true;
|
||||||
|
Log.d(TAG, "创建直播间 - 匹配成功!使用类型编码: " + typeCode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
Log.w(TAG, "创建直播间 - 未找到匹配的类型,使用默认值: " + typeCode);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "创建直播间 - Tag不是List类型,使用默认映射");
|
||||||
|
// 如果没有从后端获取到类型列表,使用默认映射
|
||||||
|
switch (typeDisplay) {
|
||||||
|
case "游戏":
|
||||||
|
typeCode = "game";
|
||||||
|
break;
|
||||||
|
case "才艺":
|
||||||
|
typeCode = "talent";
|
||||||
|
break;
|
||||||
|
case "户外":
|
||||||
|
typeCode = "outdoor";
|
||||||
|
break;
|
||||||
|
case "音乐":
|
||||||
|
typeCode = "music";
|
||||||
|
break;
|
||||||
|
case "美食":
|
||||||
|
typeCode = "food";
|
||||||
|
break;
|
||||||
|
case "聊天":
|
||||||
|
typeCode = "chat";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.w(TAG, "创建直播间 - 未知的类型名称: " + typeDisplay + ",使用默认值: game");
|
||||||
|
typeCode = "game";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Log.d(TAG, "创建直播间 - 默认映射结果: " + typeDisplay + " -> " + typeCode);
|
||||||
|
}
|
||||||
|
|
||||||
// 获取用户昵称
|
// 获取用户昵称
|
||||||
String streamerName = getSharedPreferences("profile_prefs", MODE_PRIVATE)
|
String streamerName = getSharedPreferences("profile_prefs", MODE_PRIVATE)
|
||||||
.getString("profile_name", "未知用户");
|
.getString("profile_name", "未知用户");
|
||||||
|
|
||||||
|
Log.d(TAG, "创建直播间 - 最终参数: title=" + title + ", streamerName=" + streamerName + ", typeCode=" + typeCode);
|
||||||
|
|
||||||
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
|
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
|
||||||
|
|
||||||
// 调用后端接口创建直播间
|
// 调用后端接口创建直播间,使用从后端获取的类型编码
|
||||||
ApiClient.getService(getApplicationContext()).createRoom(new CreateRoomRequest(title, streamerName, "live"))
|
ApiClient.getService(getApplicationContext()).createRoom(new CreateRoomRequest(title, streamerName, typeCode))
|
||||||
.enqueue(new Callback<ApiResponse<Room>>() {
|
.enqueue(new Callback<ApiResponse<Room>>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(Call<ApiResponse<Room>> call, Response<ApiResponse<Room>> response) {
|
public void onResponse(Call<ApiResponse<Room>> call, Response<ApiResponse<Room>> response) {
|
||||||
|
|
@ -1509,6 +1564,101 @@ public class MainActivity extends AppCompatActivity {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为创建直播间对话框加载直播类型
|
||||||
|
* 从后端数据库获取直播类型列表(游戏、才艺、户外、音乐、美食、聊天等)
|
||||||
|
*/
|
||||||
|
private void loadLiveTypesForDialog(MaterialAutoCompleteTextView typeSpinner) {
|
||||||
|
if (typeSpinner == null) {
|
||||||
|
Log.w(TAG, "loadLiveTypesForDialog() typeSpinner is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "loadLiveTypesForDialog() 开始从后端加载直播类型");
|
||||||
|
|
||||||
|
// 设置下拉框为只读,不允许手动输入,只能从列表中选择
|
||||||
|
typeSpinner.setKeyListener(null);
|
||||||
|
typeSpinner.setFocusable(false);
|
||||||
|
typeSpinner.setClickable(true);
|
||||||
|
typeSpinner.setFocusableInTouchMode(false);
|
||||||
|
|
||||||
|
// 调用后端接口获取直播类型列表
|
||||||
|
ApiClient.getService(getApplicationContext()).getLiveTypes()
|
||||||
|
.enqueue(new Callback<ApiResponse<List<com.example.livestreaming.net.LiveTypeResponse>>>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call<ApiResponse<List<com.example.livestreaming.net.LiveTypeResponse>>> call,
|
||||||
|
Response<ApiResponse<List<com.example.livestreaming.net.LiveTypeResponse>>> response) {
|
||||||
|
Log.d(TAG, "loadLiveTypesForDialog() onResponse: code=" + response.code());
|
||||||
|
|
||||||
|
ApiResponse<List<com.example.livestreaming.net.LiveTypeResponse>> body = response.body();
|
||||||
|
List<com.example.livestreaming.net.LiveTypeResponse> types =
|
||||||
|
response.isSuccessful() && body != null && body.isOk() && body.getData() != null
|
||||||
|
? body.getData()
|
||||||
|
: null;
|
||||||
|
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
if (types != null && !types.isEmpty()) {
|
||||||
|
Log.d(TAG, "loadLiveTypesForDialog() 成功获取 " + types.size() + " 个直播类型");
|
||||||
|
|
||||||
|
// 打印所有类型信息,便于调试
|
||||||
|
for (com.example.livestreaming.net.LiveTypeResponse type : types) {
|
||||||
|
Log.d(TAG, " 类型: " + type.getName() + " (code=" + type.getCode() + ", sort=" + type.getSort() + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取类型名称
|
||||||
|
String[] typeNames = new String[types.size()];
|
||||||
|
for (int i = 0; i < types.size(); i++) {
|
||||||
|
typeNames[i] = types.get(i).getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayAdapter<String> adapter = new ArrayAdapter<>(MainActivity.this,
|
||||||
|
android.R.layout.simple_dropdown_item_1line, typeNames);
|
||||||
|
typeSpinner.setAdapter(adapter);
|
||||||
|
|
||||||
|
// 设置默认选中第一项
|
||||||
|
if (typeNames.length > 0) {
|
||||||
|
typeSpinner.setText(typeNames[0], false);
|
||||||
|
Log.d(TAG, "loadLiveTypesForDialog() 默认选中: " + typeNames[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存类型列表供后续使用
|
||||||
|
typeSpinner.setTag(types);
|
||||||
|
Log.d(TAG, "loadLiveTypesForDialog() 类型列表已保存到Tag中");
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "loadLiveTypesForDialog() 未获取到类型数据,使用默认类型");
|
||||||
|
useDefaultLiveTypes(typeSpinner);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call<ApiResponse<List<com.example.livestreaming.net.LiveTypeResponse>>> call, Throwable t) {
|
||||||
|
Log.e(TAG, "loadLiveTypesForDialog() onFailure: " + t.getMessage(), t);
|
||||||
|
// 网络错误,使用默认类型
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
useDefaultLiveTypes(typeSpinner);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用默认的直播类型(当后端接口失败时的备用方案)
|
||||||
|
*/
|
||||||
|
private void useDefaultLiveTypes(MaterialAutoCompleteTextView typeSpinner) {
|
||||||
|
String[] defaultTypes = {"游戏", "才艺", "户外", "音乐", "美食", "聊天"};
|
||||||
|
ArrayAdapter<String> adapter = new ArrayAdapter<>(MainActivity.this,
|
||||||
|
android.R.layout.simple_dropdown_item_1line, defaultTypes);
|
||||||
|
typeSpinner.setAdapter(adapter);
|
||||||
|
|
||||||
|
// 设置默认选中第一项
|
||||||
|
if (defaultTypes.length > 0) {
|
||||||
|
typeSpinner.setText(defaultTypes[0], false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "useDefaultLiveTypes() 使用默认类型,共 " + defaultTypes.length + " 个");
|
||||||
|
}
|
||||||
|
|
||||||
private void loadCoverAssetsAsync() {
|
private void loadCoverAssetsAsync() {
|
||||||
// 在后台线程加载资源文件,避免阻塞UI
|
// 在后台线程加载资源文件,避免阻塞UI
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
|
|
|
||||||
|
|
@ -285,6 +285,14 @@ public interface ApiService {
|
||||||
@GET("api/front/category/statistics")
|
@GET("api/front/category/statistics")
|
||||||
Call<ApiResponse<Map<String, Object>>> getCategoryStatistics(@Query("type") int type);
|
Call<ApiResponse<Map<String, Object>>> getCategoryStatistics(@Query("type") int type);
|
||||||
|
|
||||||
|
// ==================== 直播类型接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取直播类型列表(游戏、才艺、户外、音乐、美食、聊天等)
|
||||||
|
*/
|
||||||
|
@GET("api/front/live/public/types")
|
||||||
|
Call<ApiResponse<List<LiveTypeResponse>>> getLiveTypes();
|
||||||
|
|
||||||
// ==================== 关注接口 ====================
|
// ==================== 关注接口 ====================
|
||||||
|
|
||||||
@GET("api/front/follow/followers")
|
@GET("api/front/follow/followers")
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
package com.example.livestreaming.net;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 直播类型响应对象
|
||||||
|
*/
|
||||||
|
public class LiveTypeResponse {
|
||||||
|
|
||||||
|
@SerializedName("id")
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@SerializedName("name")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@SerializedName("code")
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
@SerializedName("icon")
|
||||||
|
private String icon;
|
||||||
|
|
||||||
|
@SerializedName("description")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@SerializedName("sort")
|
||||||
|
private Integer sort;
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
|
||||||
|
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 String getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCode(String code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIcon() {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIcon(String icon) {
|
||||||
|
this.icon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getSort() {
|
||||||
|
return sort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSort(Integer sort) {
|
||||||
|
this.sort = sort;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "LiveTypeResponse{" +
|
||||||
|
"id=" + id +
|
||||||
|
", name='" + name + '\'' +
|
||||||
|
", code='" + code + '\'' +
|
||||||
|
", icon='" + icon + '\'' +
|
||||||
|
", description='" + description + '\'' +
|
||||||
|
", sort=" + sort +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user