diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index 35410cac..00000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,8 +0,0 @@
-# 默认忽略的文件
-/shelf/
-/workspace.xml
-# 基于编辑器的 HTTP 客户端请求
-/httpRequests/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
diff --git a/.idea/modules.xml b/.idea/modules.xml
deleted file mode 100644
index e3ea9106..00000000
--- a/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 35eb1ddf..00000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/zhibo.iml b/.idea/zhibo.iml
deleted file mode 100644
index d6ebd480..00000000
--- a/.idea/zhibo.iml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.kiro/specs/live-streaming-system/design.md b/.kiro/specs/live-streaming-system/design.md
deleted file mode 100644
index a1ce2591..00000000
--- a/.kiro/specs/live-streaming-system/design.md
+++ /dev/null
@@ -1,353 +0,0 @@
-# Design Document
-
-## Overview
-
-本设计文档描述了基于 SRS 的个人直播系统的技术架构和实现方案。系统采用前后端分离架构,使用 SRS 作为核心流媒体服务器处理视频流的接收和分发,Node.js 后端提供业务 API,React 前端提供用户界面。
-
-### 技术栈选型
-
-| 组件 | 技术选择 | 理由 |
-|------|----------|------|
-| 流媒体服务器 | SRS 5.0 | 高性能、低延迟、支持多协议、Docker 部署简单 |
-| 后端框架 | Node.js + Express | 轻量级、异步处理能力强、生态丰富 |
-| 前端框架 | React 18 | 组件化开发、生态成熟、性能优秀 |
-| 视频播放器 | flv.js + hls.js | flv.js 支持 HTTP-FLV 低延迟播放,hls.js 提供 HLS 兼容性 |
-| 容器化 | Docker + Docker Compose | 简化部署、环境一致性 |
-
-## Architecture
-
-```
-┌─────────────────────────────────────────────────────────────────┐
-│ 用户层 (Users) │
-├─────────────────────────────────────────────────────────────────┤
-│ ┌─────────────┐ ┌─────────────────┐ │
-│ │ 主播 │ │ 观众 │ │
-│ │ (OBS/FFmpeg)│ │ (浏览器) │ │
-│ └──────┬──────┘ └────────┬────────┘ │
-│ │ RTMP 推流 │ HTTP 请求 │
-│ │ rtmp://host:1935/live/{streamKey} │ │
-└─────────┼─────────────────────────────────────────┼────────────┘
- │ │
-┌─────────┼─────────────────────────────────────────┼────────────┐
-│ ▼ ▼ │
-│ ┌─────────────────┐ ┌─────────────────┐ │
-│ │ SRS Server │ │ React Frontend │ │
-│ │ (Docker) │ │ (Port 3000) │ │
-│ │ │ │ │ │
-│ │ RTMP: 1935 │◄───HTTP-FLV/HLS───│ flv.js/hls.js │ │
-│ │ HTTP: 8080 │ │ │ │
-│ └────────┬────────┘ └────────┬────────┘ │
-│ │ │ │
-│ │ HTTP Callback │ REST API │
-│ │ (on_publish/on_unpublish) │ │
-│ ▼ ▼ │
-│ ┌─────────────────────────────────────────────────────────┐ │
-│ │ Node.js API Server │ │
-│ │ (Port 3001) │ │
-│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │ │
-│ │ │ Room API │ │ Callback │ │ Room Store │ │ │
-│ │ │ Controller │ │ Handler │ │ (In-Memory) │ │ │
-│ │ └─────────────┘ └─────────────┘ └─────────────────┘ │ │
-│ └─────────────────────────────────────────────────────────┘ │
-│ 服务层 (Services) │
-└─────────────────────────────────────────────────────────────────┘
-```
-
-### 数据流说明
-
-1. **推流流程**: 主播 → RTMP → SRS → HTTP Callback → API Server → 更新房间状态
-2. **观看流程**: 观众 → React App → 获取房间信息 → flv.js/hls.js → SRS HTTP-FLV/HLS
-3. **管理流程**: 用户 → React App → REST API → API Server → 房间 CRUD
-
-## Components and Interfaces
-
-### 1. SRS 流媒体服务器
-
-**职责**: 接收 RTMP 推流,转换并分发 HTTP-FLV 和 HLS 流
-
-**配置接口**:
-```conf
-# srs.conf
-listen 1935;
-max_connections 1000;
-daemon off;
-srs_log_tank console;
-
-http_server {
- enabled on;
- listen 8080;
- dir ./objs/nginx/html;
-}
-
-vhost __defaultVhost__ {
- hls {
- enabled on;
- hls_fragment 2;
- hls_window 10;
- hls_path ./objs/nginx/html;
- hls_m3u8_file [app]/[stream].m3u8;
- hls_ts_file [app]/[stream]-[seq].ts;
- }
-
- http_remux {
- enabled on;
- mount [vhost]/[app]/[stream].flv;
- }
-
- http_hooks {
- enabled on;
- on_publish http://api-server:3001/api/srs/on_publish;
- on_unpublish http://api-server:3001/api/srs/on_unpublish;
- }
-}
-```
-
-### 2. API Server (Node.js + Express)
-
-**职责**: 提供业务 API,处理 SRS 回调,管理房间数据
-
-#### 2.1 Room API Controller
-
-```typescript
-// 接口定义
-interface RoomController {
- // 获取所有房间
- GET /api/rooms -> Room[]
-
- // 创建房间
- POST /api/rooms { title: string, streamerName: string } -> Room
-
- // 获取单个房间
- GET /api/rooms/:id -> Room
-
- // 删除房间
- DELETE /api/rooms/:id -> { success: boolean }
-}
-```
-
-#### 2.2 SRS Callback Handler
-
-```typescript
-// SRS 回调接口
-interface SRSCallbackHandler {
- // 推流开始回调
- POST /api/srs/on_publish {
- action: 'on_publish',
- app: string, // 应用名,如 'live'
- stream: string // 流名称,即 streamKey
- } -> { code: 0 }
-
- // 推流结束回调
- POST /api/srs/on_unpublish {
- action: 'on_unpublish',
- app: string,
- stream: string
- } -> { code: 0 }
-}
-```
-
-### 3. React Frontend
-
-**职责**: 提供用户界面,包括房间列表、直播播放、房间创建
-
-#### 3.1 组件结构
-
-```
-src/
-├── components/
-│ ├── RoomList.jsx # 房间列表组件
-│ ├── RoomCard.jsx # 房间卡片组件
-│ ├── LivePlayer.jsx # 直播播放器组件
-│ ├── CreateRoom.jsx # 创建房间弹窗
-│ └── StreamInfo.jsx # 推流信息展示
-├── pages/
-│ ├── HomePage.jsx # 首页(房间列表)
-│ └── RoomPage.jsx # 房间详情页
-├── services/
-│ └── api.js # API 调用封装
-└── App.jsx # 应用入口
-```
-
-#### 3.2 LivePlayer 组件接口
-
-```typescript
-interface LivePlayerProps {
- room: Room;
- preferFlv?: boolean; // 优先使用 FLV(低延迟)
-}
-
-// 播放器内部逻辑
-// 1. 检测浏览器是否支持 flv.js (需要 MSE 支持)
-// 2. 支持则使用 HTTP-FLV,否则降级到 HLS
-// 3. 监听播放错误,自动重连
-```
-
-## Data Models
-
-### Room 数据模型
-
-```typescript
-interface Room {
- id: string; // 房间唯一标识 (UUID)
- title: string; // 房间标题
- streamerName: string; // 主播名称
- streamKey: string; // 推流密钥 (与 id 相同)
- isLive: boolean; // 是否正在直播
- viewerCount: number; // 观看人数
- createdAt: string; // 创建时间 (ISO 8601)
- startedAt?: string; // 开播时间 (ISO 8601)
-}
-```
-
-### API 响应格式
-
-```typescript
-// 成功响应
-interface SuccessResponse {
- success: true;
- data: T;
-}
-
-// 错误响应
-interface ErrorResponse {
- success: false;
- error: {
- code: string;
- message: string;
- };
-}
-
-// SRS 回调响应
-interface SRSCallbackResponse {
- code: number; // 0 表示成功
-}
-```
-
-### 流地址格式
-
-```typescript
-interface StreamUrls {
- rtmp: string; // rtmp://host:1935/live/{streamKey}
- flv: string; // http://host:8080/live/{streamKey}.flv
- hls: string; // http://host:8080/live/{streamKey}.m3u8
-}
-```
-
-
-
-## Correctness Properties
-
-*A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.*
-
-### Property 1: Room Creation Round-Trip Consistency
-
-*For any* valid room creation request with non-empty title and streamer name, creating the room and then retrieving it by ID SHALL return a room with matching title, streamer name, a unique stream key, and a valid creation timestamp.
-
-**Validates: Requirements 1.1, 1.4**
-
-### Property 2: New Rooms Start Offline
-
-*For any* newly created room, the initial `isLive` status SHALL be `false`.
-
-**Validates: Requirements 1.2**
-
-### Property 3: Empty Title Rejection
-
-*For any* room creation request where the title is empty or consists only of whitespace characters, the system SHALL reject the request and return a validation error without creating a room.
-
-**Validates: Requirements 1.3**
-
-### Property 4: Publish Callback Status Transition
-
-*For any* room that exists in the system, when a publish callback is received with that room's stream key, the room's `isLive` status SHALL transition to `true`. When an unpublish callback is received, the status SHALL transition to `false`.
-
-**Validates: Requirements 2.3, 2.5**
-
-### Property 5: Stream URLs Contain Required Formats
-
-*For any* room returned by the API, the stream URLs SHALL include both HTTP-FLV (`.flv`) and HLS (`.m3u8`) format URLs containing the room's stream key.
-
-**Validates: Requirements 3.1**
-
-### Property 6: Room List Completeness
-
-*For any* set of created rooms, requesting the room list SHALL return all rooms with their current status, title, and streamer name intact.
-
-**Validates: Requirements 4.1, 4.2**
-
-## Error Handling
-
-### API Error Codes
-
-| 错误码 | 描述 | HTTP 状态码 |
-|--------|------|-------------|
-| `VALIDATION_ERROR` | 请求参数验证失败 | 400 |
-| `ROOM_NOT_FOUND` | 房间不存在 | 404 |
-| `STREAM_KEY_INVALID` | 推流密钥无效 | 401 |
-| `INTERNAL_ERROR` | 服务器内部错误 | 500 |
-
-### 错误处理策略
-
-1. **API 层**: 统一错误响应格式,包含错误码和消息
-2. **SRS 回调**: 返回 `{ code: 0 }` 表示成功,非零表示失败
-3. **前端**: 显示友好的错误提示,支持重试操作
-4. **播放器**: 自动重连机制,最多重试 3 次
-
-## Testing Strategy
-
-### 单元测试
-
-使用 Jest 进行单元测试,覆盖以下模块:
-
-1. **Room Service**: 房间 CRUD 操作
-2. **Validation**: 输入验证逻辑
-3. **URL Generator**: 流地址生成
-
-### 属性测试 (Property-Based Testing)
-
-使用 fast-check 库进行属性测试:
-
-1. **Property 1**: 房间创建往返一致性
-2. **Property 2**: 新房间初始状态
-3. **Property 3**: 空标题拒绝
-4. **Property 4**: 回调状态转换
-5. **Property 5**: 流地址格式
-6. **Property 6**: 房间列表完整性
-
-### 测试配置
-
-```javascript
-// jest.config.js
-module.exports = {
- testEnvironment: 'node',
- testMatch: ['**/*.test.js', '**/*.property.test.js'],
- collectCoverageFrom: ['server/**/*.js'],
- coverageThreshold: {
- global: { branches: 80, functions: 80, lines: 80 }
- }
-};
-```
-
-### 属性测试示例
-
-```javascript
-// 每个属性测试运行 100 次迭代
-// 标注格式: **Feature: live-streaming-system, Property {number}: {property_text}**
-
-import fc from 'fast-check';
-
-// **Feature: live-streaming-system, Property 2: New Rooms Start Offline**
-test('newly created rooms should have isLive = false', () => {
- fc.assert(
- fc.property(
- fc.string({ minLength: 1 }), // title
- fc.string({ minLength: 1 }), // streamerName
- (title, streamerName) => {
- const room = createRoom({ title, streamerName });
- return room.isLive === false;
- }
- ),
- { numRuns: 100 }
- );
-});
-```
\ No newline at end of file
diff --git a/.kiro/specs/live-streaming-system/requirements.md b/.kiro/specs/live-streaming-system/requirements.md
deleted file mode 100644
index a8f53208..00000000
--- a/.kiro/specs/live-streaming-system/requirements.md
+++ /dev/null
@@ -1,89 +0,0 @@
-# Requirements Document
-
-## Introduction
-
-本文档定义了一个基于 SRS(Simple Realtime Server)的个人直播系统的功能需求。该系统允许用户创建直播间、进行实时视频推流、观看直播内容,并提供基本的直播间管理功能。系统采用 SRS 作为核心流媒体服务器,支持 RTMP 推流和 HTTP-FLV/HLS 播放,具有高性能、低延迟的特点。
-
-## Glossary
-
-- **Live Streaming System(直播系统)**: 提供视频直播功能的完整系统,包含前端播放器、后端 API 服务和 SRS 流媒体服务
-- **SRS(Simple Realtime Server)**: 开源的高性能流媒体服务器,支持 RTMP、HLS、HTTP-FLV、WebRTC 等协议
-- **Streamer(主播)**: 创建直播间并推送视频流的用户
-- **Viewer(观众)**: 观看直播内容的用户
-- **Live Room(直播间)**: 主播进行直播的虚拟空间,包含标题、主播信息和直播状态
-- **Stream Key(推流密钥)**: 用于验证主播身份的唯一标识符
-- **RTMP(Real-Time Messaging Protocol)**: 用于主播推流的实时消息传输协议
-- **HTTP-FLV**: 基于 HTTP 的 FLV 流协议,延迟低于 HLS,适合实时直播
-- **HLS(HTTP Live Streaming)**: 基于 HTTP 的直播流协议,兼容性好但延迟较高
-- **SRS Callback**: SRS 的 HTTP 回调机制,用于通知业务服务器推流状态变化
-
-## Requirements
-
-### Requirement 1
-
-**User Story:** As a streamer, I want to create a live room, so that I can start broadcasting my content to viewers.
-
-#### Acceptance Criteria
-
-1. WHEN a streamer submits a room creation request with title and streamer name, THE Live Streaming System SHALL create a new live room and return a unique stream key
-2. WHEN a live room is created, THE Live Streaming System SHALL initialize the room status as "offline"
-3. WHEN a streamer provides an empty title, THE Live Streaming System SHALL reject the creation request and return a validation error
-4. WHEN a live room is created, THE Live Streaming System SHALL store the room information including title, streamer name, stream key, and creation timestamp
-
-### Requirement 2
-
-**User Story:** As a streamer, I want to push my video stream to the server, so that viewers can watch my live content.
-
-#### Acceptance Criteria
-
-1. WHEN a streamer connects to the SRS RTMP endpoint with a valid stream key, THE SRS Server SHALL accept the connection and begin receiving the video stream
-2. WHEN SRS receives a publish event, THE SRS Server SHALL trigger an HTTP callback to notify the API service of the stream start
-3. WHEN the API service receives a publish callback, THE Live Streaming System SHALL update the corresponding room status to "live"
-4. WHEN SRS receives an unpublish event, THE SRS Server SHALL trigger an HTTP callback to notify the API service of the stream end
-5. WHEN the API service receives an unpublish callback, THE Live Streaming System SHALL update the room status to "offline"
-
-### Requirement 3
-
-**User Story:** As a viewer, I want to watch live streams, so that I can enjoy the content broadcasted by streamers.
-
-#### Acceptance Criteria
-
-1. WHEN a viewer requests to watch a live room, THE Live Streaming System SHALL provide both HTTP-FLV and HLS stream URLs for playback
-2. WHEN a viewer accesses an HTTP-FLV stream URL, THE Live Streaming System SHALL deliver the video with latency under 3 seconds
-3. WHEN a viewer accesses an HLS stream URL, THE Live Streaming System SHALL deliver the video with latency under 10 seconds
-4. WHEN a viewer attempts to watch an offline room, THE Live Streaming System SHALL display an appropriate offline message
-5. WHEN the stream quality changes, THE Live Streaming System SHALL continue playback without interruption
-
-### Requirement 4
-
-**User Story:** As a viewer, I want to browse available live rooms, so that I can discover content to watch.
-
-#### Acceptance Criteria
-
-1. WHEN a viewer requests the room list, THE Live Streaming System SHALL return all available rooms with their current status
-2. WHEN displaying the room list, THE Live Streaming System SHALL show room title, streamer name, and live status for each room
-3. WHEN a room's live status changes, THE Live Streaming System SHALL reflect the updated status within 5 seconds on subsequent requests
-4. WHEN no rooms exist, THE Live Streaming System SHALL return an empty list with appropriate messaging
-
-### Requirement 5
-
-**User Story:** As a system administrator, I want to deploy and configure SRS, so that the streaming service runs reliably.
-
-#### Acceptance Criteria
-
-1. WHEN deploying SRS, THE Live Streaming System SHALL use Docker for containerized deployment
-2. WHEN configuring SRS, THE Live Streaming System SHALL enable RTMP input on port 1935
-3. WHEN configuring SRS, THE Live Streaming System SHALL enable HTTP-FLV output on port 8080
-4. WHEN configuring SRS, THE Live Streaming System SHALL enable HLS output with 2-second segments
-5. WHEN configuring SRS, THE Live Streaming System SHALL set up HTTP callbacks for publish and unpublish events
-
-### Requirement 6
-
-**User Story:** As a user, I want a responsive web interface, so that I can access the live streaming platform from any device.
-
-#### Acceptance Criteria
-
-1. WHEN a user accesses the platform, THE Live Streaming System SHALL display a responsive interface that adapts to screen sizes from 320px to 1920px width
-2. WHEN a user interacts with the video player, THE Live Streaming System SHALL provide play, pause, and volume controls
-3. WHEN a user navigates between pages, THE Live Streaming System SHALL maintain smooth transitions without full page reloads
-4. WHEN the interface loads, THE Live Streaming System SHALL display the main content within 3 seconds on a standard broadband connection
diff --git a/.kiro/specs/live-streaming-system/tasks.md b/.kiro/specs/live-streaming-system/tasks.md
deleted file mode 100644
index 51926c48..00000000
--- a/.kiro/specs/live-streaming-system/tasks.md
+++ /dev/null
@@ -1,66 +0,0 @@
-# Implementation Tasks
-
-## Task 1: 项目初始化和 Docker 配置
-
-- [ ] 1.1 创建项目根目录结构 (server/, client/, docker/)
-- [ ] 1.2 创建 package.json 配置后端依赖
-- [ ] 1.3 创建 SRS 配置文件 (docker/srs/srs.conf)
-- [ ] 1.4 创建 docker-compose.yml 配置 SRS 和 API 服务
-- [ ] 1.5 创建 .env.example 环境变量模板
-
-## Task 2: 后端 API 服务 - 房间管理
-
-- [ ] 2.1 创建 Express 服务入口 (server/index.js)
-- [ ] 2.2 实现房间数据存储模块 (server/store/roomStore.js)
-- [ ] 2.3 实现房间 API 路由 (server/routes/rooms.js)
- - GET /api/rooms - 获取所有房间
- - POST /api/rooms - 创建房间
- - GET /api/rooms/:id - 获取单个房间
- - DELETE /api/rooms/:id - 删除房间
-- [ ] 2.4 实现输入验证中间件 (server/middleware/validate.js)
-- [ ] 2.5 实现统一错误处理 (server/middleware/errorHandler.js)
-
-## Task 3: 后端 API 服务 - SRS 回调处理
-
-- [ ] 3.1 实现 SRS 回调路由 (server/routes/srs.js)
- - POST /api/srs/on_publish - 推流开始回调
- - POST /api/srs/on_unpublish - 推流结束回调
-- [ ] 3.2 实现流地址生成工具 (server/utils/streamUrl.js)
-- [ ] 3.3 添加回调日志记录
-
-## Task 4: 前端 - 项目搭建和基础组件
-
-- [ ] 4.1 初始化 React 项目 (client/)
-- [ ] 4.2 安装依赖 (flv.js, hls.js, axios)
-- [ ] 4.3 创建 API 服务封装 (client/src/services/api.js)
-- [ ] 4.4 创建全局样式 (client/src/index.css)
-- [ ] 4.5 创建 App 入口和路由 (client/src/App.jsx)
-
-## Task 5: 前端 - 房间列表页面
-
-- [ ] 5.1 创建 RoomCard 组件 (client/src/components/RoomCard.jsx)
-- [ ] 5.2 创建 RoomList 组件 (client/src/components/RoomList.jsx)
-- [ ] 5.3 创建 HomePage 页面 (client/src/pages/HomePage.jsx)
-- [ ] 5.4 实现房间列表自动刷新
-
-## Task 6: 前端 - 创建直播间功能
-
-- [ ] 6.1 创建 CreateRoom 弹窗组件 (client/src/components/CreateRoom.jsx)
-- [ ] 6.2 创建 StreamInfo 推流信息组件 (client/src/components/StreamInfo.jsx)
-- [ ] 6.3 实现复制推流地址功能
-
-## Task 7: 前端 - 直播播放器
-
-- [ ] 7.1 创建 LivePlayer 组件 (client/src/components/LivePlayer.jsx)
-- [ ] 7.2 实现 flv.js 播放器集成 (HTTP-FLV 低延迟)
-- [ ] 7.3 实现 hls.js 降级播放 (HLS 兼容性)
-- [ ] 7.4 实现播放器自动重连机制
-- [ ] 7.5 创建 RoomPage 直播间页面 (client/src/pages/RoomPage.jsx)
-
-## Task 8: 集成测试和部署
-
-- [ ] 8.1 编写属性测试 (server/tests/room.property.test.js)
-- [ ] 8.2 编写 API 集成测试 (server/tests/api.test.js)
-- [ ] 8.3 创建启动脚本 (scripts/start.sh, scripts/start.bat)
-- [ ] 8.4 编写 README.md 使用文档
-- [ ] 8.5 测试完整推流和观看流程
diff --git a/.vscode/settings.json b/.vscode/settings.json
deleted file mode 100644
index 7a73a41b..00000000
--- a/.vscode/settings.json
+++ /dev/null
@@ -1,2 +0,0 @@
-{
-}
\ No newline at end of file
diff --git a/Zhibo/zhibo-h/crmeb-front/src/main/java/com/zbkj/front/controller/LiveRoomController.java b/Zhibo/zhibo-h/crmeb-front/src/main/java/com/zbkj/front/controller/LiveRoomController.java
index c96d936a..3dea540a 100644
--- a/Zhibo/zhibo-h/crmeb-front/src/main/java/com/zbkj/front/controller/LiveRoomController.java
+++ b/Zhibo/zhibo-h/crmeb-front/src/main/java/com/zbkj/front/controller/LiveRoomController.java
@@ -9,7 +9,6 @@ import com.zbkj.front.response.live.StreamUrlsResponse;
import com.zbkj.service.service.LiveRoomService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
-import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.annotation.Validated;
@@ -79,7 +78,10 @@ public class LiveRoomController {
private LiveRoomResponse toResponse(LiveRoom room, String requestHost) {
LiveRoomResponse resp = new LiveRoomResponse();
- BeanUtils.copyProperties(room, resp);
+ resp.setId(room.getId() == null ? null : String.valueOf(room.getId()));
+ resp.setTitle(room.getTitle());
+ resp.setStreamerName(room.getStreamerName());
+ resp.setStreamKey(room.getStreamKey());
resp.setIsLive(room.getIsLive() != null && room.getIsLive() == 1);
resp.setViewerCount(0);
diff --git a/Zhibo/zhibo-h/crmeb-front/src/main/java/com/zbkj/front/response/live/LiveRoomResponse.java b/Zhibo/zhibo-h/crmeb-front/src/main/java/com/zbkj/front/response/live/LiveRoomResponse.java
index c27e1c2f..6ffa5c93 100644
--- a/Zhibo/zhibo-h/crmeb-front/src/main/java/com/zbkj/front/response/live/LiveRoomResponse.java
+++ b/Zhibo/zhibo-h/crmeb-front/src/main/java/com/zbkj/front/response/live/LiveRoomResponse.java
@@ -9,7 +9,7 @@ import lombok.Data;
public class LiveRoomResponse {
@ApiModelProperty(value = "房间ID")
- private Integer id;
+ private String id;
@ApiModelProperty(value = "标题")
private String title;
diff --git a/android-app/app/build.gradle.kts b/android-app/app/build.gradle.kts
index abd573d8..58a1001e 100644
--- a/android-app/app/build.gradle.kts
+++ b/android-app/app/build.gradle.kts
@@ -32,10 +32,10 @@ android {
}
val apiBaseUrlEmulator = (localProps.getProperty("api.base_url_emulator")
- ?: "http://10.0.2.2:25001/api/").trim()
+ ?: "http://10.0.2.2:8081/").trim()
val apiBaseUrlDevice = (localProps.getProperty("api.base_url_device")
- ?: "http://192.168.1.100:25001/api/").trim()
+ ?: "http://192.168.1.100:8081/").trim()
// 模拟器使用服务器地址
buildConfigField("String", "API_BASE_URL_EMULATOR", "\"$apiBaseUrlEmulator\"")
diff --git a/android-app/app/src/main/java/com/example/livestreaming/MainActivity.java b/android-app/app/src/main/java/com/example/livestreaming/MainActivity.java
index 3e39b0a7..ca186135 100644
--- a/android-app/app/src/main/java/com/example/livestreaming/MainActivity.java
+++ b/android-app/app/src/main/java/com/example/livestreaming/MainActivity.java
@@ -62,6 +62,8 @@ public class MainActivity extends AppCompatActivity {
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
+ ApiClient.getService(getApplicationContext());
+
// 立即显示缓存数据,提升启动速度
setupRecyclerView();
setupUI();
@@ -417,7 +419,7 @@ public class MainActivity extends AppCompatActivity {
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
- ApiClient.getService().createRoom(new CreateRoomRequest(title, streamer))
+ ApiClient.getService(getApplicationContext()).createRoom(new CreateRoomRequest(title, streamer))
.enqueue(new Callback>() {
@Override
public void onResponse(Call> call, Response> response) {
@@ -425,8 +427,9 @@ public class MainActivity extends AppCompatActivity {
ApiResponse body = response.body();
Room room = body != null ? body.getData() : null;
- if (!response.isSuccessful() || room == null) {
- Toast.makeText(MainActivity.this, "创建失败", Toast.LENGTH_SHORT).show();
+ if (!response.isSuccessful() || body == null || !body.isOk() || room == null) {
+ String msg = body != null && !TextUtils.isEmpty(body.getMessage()) ? body.getMessage() : "创建失败";
+ Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
return;
}
@@ -535,14 +538,16 @@ public class MainActivity extends AppCompatActivity {
binding.loading.setVisibility(View.VISIBLE);
}
- ApiClient.getService().getRooms().enqueue(new Callback>>() {
+ ApiClient.getService(getApplicationContext()).getRooms().enqueue(new Callback>>() {
@Override
public void onResponse(Call>> call, Response>> response) {
binding.loading.setVisibility(View.GONE);
binding.swipeRefresh.setRefreshing(false);
isFetching = false;
ApiResponse> body = response.body();
- List rooms = body != null && body.getData() != null ? body.getData() : Collections.emptyList();
+ List rooms = response.isSuccessful() && body != null && body.isOk() && body.getData() != null
+ ? body.getData()
+ : Collections.emptyList();
if (rooms == null || rooms.isEmpty()) {
rooms = buildDemoRooms(12);
}
diff --git a/android-app/app/src/main/java/com/example/livestreaming/RoomDetailActivity.java b/android-app/app/src/main/java/com/example/livestreaming/RoomDetailActivity.java
index 07d43522..60c86949 100644
--- a/android-app/app/src/main/java/com/example/livestreaming/RoomDetailActivity.java
+++ b/android-app/app/src/main/java/com/example/livestreaming/RoomDetailActivity.java
@@ -91,6 +91,8 @@ public class RoomDetailActivity extends AppCompatActivity {
binding = ActivityRoomDetailNewBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
+ ApiClient.getService(getApplicationContext());
+
roomId = getIntent().getStringExtra(EXTRA_ROOM_ID);
triedAltUrl = false;
@@ -310,19 +312,21 @@ public class RoomDetailActivity extends AppCompatActivity {
binding.loading.setVisibility(View.VISIBLE);
}
- ApiClient.getService().getRoom(roomId).enqueue(new Callback>() {
+ ApiClient.getService(getApplicationContext()).getRoom(roomId).enqueue(new Callback>() {
@Override
public void onResponse(Call> call, Response> response) {
if (isFinishing() || isDestroyed()) return;
binding.loading.setVisibility(View.GONE);
+ boolean firstLoad = isFirstLoad;
isFirstLoad = false;
ApiResponse body = response.body();
Room data = body != null ? body.getData() : null;
- if (!response.isSuccessful() || data == null) {
- if (isFirstLoad) {
- Toast.makeText(RoomDetailActivity.this, "房间不存在", Toast.LENGTH_SHORT).show();
+ if (!response.isSuccessful() || body == null || !body.isOk() || data == null) {
+ if (firstLoad) {
+ String msg = body != null && !TextUtils.isEmpty(body.getMessage()) ? body.getMessage() : "房间不存在";
+ Toast.makeText(RoomDetailActivity.this, msg, Toast.LENGTH_SHORT).show();
finish();
}
return;
diff --git a/android-app/app/src/main/java/com/example/livestreaming/net/ApiClient.java b/android-app/app/src/main/java/com/example/livestreaming/net/ApiClient.java
index b0ded619..978ef8d0 100644
--- a/android-app/app/src/main/java/com/example/livestreaming/net/ApiClient.java
+++ b/android-app/app/src/main/java/com/example/livestreaming/net/ApiClient.java
@@ -3,9 +3,14 @@ package com.example.livestreaming.net;
import android.os.Build;
import android.util.Log;
+import android.content.Context;
+
import com.example.livestreaming.BuildConfig;
+import androidx.annotation.Nullable;
+
import okhttp3.OkHttpClient;
+import okhttp3.Interceptor;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
@@ -15,6 +20,7 @@ public final class ApiClient {
private static final String TAG = "ApiClient";
private static volatile Retrofit retrofit;
private static volatile ApiService service;
+ private static volatile Context appContext;
private ApiClient() {
}
@@ -47,6 +53,13 @@ public final class ApiClient {
}
public static ApiService getService() {
+ return getService(null);
+ }
+
+ public static ApiService getService(@Nullable Context context) {
+ if (context != null) {
+ appContext = context.getApplicationContext();
+ }
if (service != null) return service;
synchronized (ApiClient.class) {
if (service != null) return service;
@@ -54,8 +67,19 @@ public final class ApiClient {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
+ Interceptor auth = chain -> {
+ Context ctx = ApiClient.appContext;
+ String token = ctx != null ? AuthStore.getToken(ctx) : null;
+ okhttp3.Request.Builder req = chain.request().newBuilder();
+ if (token != null && !token.trim().isEmpty()) {
+ req.header("Authori-zation", token.trim());
+ }
+ return chain.proceed(req.build());
+ };
+
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder()
.addInterceptor(logging)
+ .addInterceptor(auth)
.connectTimeout(10, java.util.concurrent.TimeUnit.SECONDS)
.readTimeout(15, java.util.concurrent.TimeUnit.SECONDS)
.writeTimeout(15, java.util.concurrent.TimeUnit.SECONDS)
diff --git a/android-app/app/src/main/java/com/example/livestreaming/net/ApiResponse.java b/android-app/app/src/main/java/com/example/livestreaming/net/ApiResponse.java
index eb7ce882..65849c86 100644
--- a/android-app/app/src/main/java/com/example/livestreaming/net/ApiResponse.java
+++ b/android-app/app/src/main/java/com/example/livestreaming/net/ApiResponse.java
@@ -4,17 +4,28 @@ import com.google.gson.annotations.SerializedName;
public class ApiResponse {
- @SerializedName("success")
- private Boolean success;
+ @SerializedName("code")
+ private Long code;
+
+ @SerializedName("message")
+ private String message;
@SerializedName("data")
private T data;
- public Boolean getSuccess() {
- return success;
+ public Long getCode() {
+ return code;
+ }
+
+ public String getMessage() {
+ return message;
}
public T getData() {
return data;
}
+
+ public boolean isOk() {
+ return code != null && code == 200L;
+ }
}
diff --git a/android-app/app/src/main/java/com/example/livestreaming/net/ApiService.java b/android-app/app/src/main/java/com/example/livestreaming/net/ApiService.java
index e18816d0..d447ea44 100644
--- a/android-app/app/src/main/java/com/example/livestreaming/net/ApiService.java
+++ b/android-app/app/src/main/java/com/example/livestreaming/net/ApiService.java
@@ -10,15 +10,18 @@ import retrofit2.http.Path;
import retrofit2.http.POST;
public interface ApiService {
- @GET("rooms")
+ @POST("api/front/login")
+ Call> login(@Body LoginRequest body);
+
+ @GET("api/front/live/public/rooms")
Call>> getRooms();
- @POST("rooms")
+ @POST("api/front/live/rooms")
Call> createRoom(@Body CreateRoomRequest body);
- @GET("rooms/{id}")
+ @GET("api/front/live/public/rooms/{id}")
Call> getRoom(@Path("id") String id);
- @DELETE("rooms/{id}")
+ @DELETE("api/front/live/rooms/{id}")
Call> deleteRoom(@Path("id") String id);
}
diff --git a/android-app/app/src/main/java/com/example/livestreaming/net/AuthStore.java b/android-app/app/src/main/java/com/example/livestreaming/net/AuthStore.java
new file mode 100644
index 00000000..1ce09365
--- /dev/null
+++ b/android-app/app/src/main/java/com/example/livestreaming/net/AuthStore.java
@@ -0,0 +1,36 @@
+package com.example.livestreaming.net;
+
+import android.content.Context;
+
+import androidx.annotation.Nullable;
+
+public final class AuthStore {
+
+ private static final String PREFS = "auth_prefs";
+ private static final String KEY_TOKEN = "token";
+
+ private AuthStore() {
+ }
+
+ public static void setToken(Context context, @Nullable String token) {
+ if (context == null) return;
+ if (token == null || token.trim().isEmpty()) {
+ context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
+ .edit()
+ .remove(KEY_TOKEN)
+ .apply();
+ return;
+ }
+ context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
+ .edit()
+ .putString(KEY_TOKEN, token.trim())
+ .apply();
+ }
+
+ @Nullable
+ public static String getToken(Context context) {
+ if (context == null) return null;
+ return context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
+ .getString(KEY_TOKEN, null);
+ }
+}
diff --git a/android-app/app/src/main/java/com/example/livestreaming/net/LoginRequest.java b/android-app/app/src/main/java/com/example/livestreaming/net/LoginRequest.java
new file mode 100644
index 00000000..f9ac7106
--- /dev/null
+++ b/android-app/app/src/main/java/com/example/livestreaming/net/LoginRequest.java
@@ -0,0 +1,25 @@
+package com.example.livestreaming.net;
+
+import com.google.gson.annotations.SerializedName;
+
+public class LoginRequest {
+
+ @SerializedName("account")
+ private final String account;
+
+ @SerializedName("password")
+ private final String password;
+
+ public LoginRequest(String account, String password) {
+ this.account = account;
+ this.password = password;
+ }
+
+ public String getAccount() {
+ return account;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+}
diff --git a/android-app/app/src/main/java/com/example/livestreaming/net/LoginResponse.java b/android-app/app/src/main/java/com/example/livestreaming/net/LoginResponse.java
new file mode 100644
index 00000000..ea835cf5
--- /dev/null
+++ b/android-app/app/src/main/java/com/example/livestreaming/net/LoginResponse.java
@@ -0,0 +1,13 @@
+package com.example.livestreaming.net;
+
+import com.google.gson.annotations.SerializedName;
+
+public class LoginResponse {
+
+ @SerializedName("token")
+ private String token;
+
+ public String getToken() {
+ return token;
+ }
+}