395 lines
12 KiB
Markdown
395 lines
12 KiB
Markdown
# 礼物打赏模块
|
||
|
||
## 模块概述
|
||
礼物打赏模块负责处理直播间内的虚拟礼物赠送功能,包括礼物列表、用户余额、礼物赠送、充值等功能。
|
||
|
||
## 相关文件
|
||
- `RoomDetailActivity.java` - 直播间详情(礼物赠送)
|
||
- `Gift.java` - 礼物模型
|
||
- `GiftAdapter.java` - 礼物列表适配器
|
||
- `GiftResponse.java` - 礼物响应模型
|
||
- `SendGiftRequest.java` - 赠送礼物请求模型
|
||
- `SendGiftResponse.java` - 赠送礼物响应模型
|
||
- `UserBalanceResponse.java` - 用户余额响应模型
|
||
- `RechargeOption.java` - 充值选项模型
|
||
- `RechargeAdapter.java` - 充值选项适配器
|
||
|
||
---
|
||
|
||
## 接口列表
|
||
|
||
### 1. 获取礼物列表
|
||
**接口地址**: `GET /api/front/gift/list`
|
||
|
||
**请求参数**: 无
|
||
|
||
**返回数据**:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "success",
|
||
"data": [
|
||
{
|
||
"id": "integer", // 礼物ID
|
||
"name": "string", // 礼物名称
|
||
"price": "integer", // 礼物价格(金币)
|
||
"icon": "string", // 礼物图标URL
|
||
"level": "integer", // 礼物等级(1-5)
|
||
"description": "string", // 礼物描述
|
||
"animation": "string", // 动画效果URL
|
||
"sort": "integer" // 排序
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 2. 获取用户余额
|
||
**接口地址**: `GET /api/front/gift/balance`
|
||
|
||
**请求参数**: 无
|
||
|
||
**返回数据** (UserBalanceResponse):
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "success",
|
||
"data": {
|
||
"userId": "integer", // 用户ID
|
||
"coinBalance": "integer", // 金币余额
|
||
"totalRecharge": "integer",// 累计充值金币
|
||
"totalConsume": "integer", // 累计消费金币
|
||
"vipLevel": "integer" // VIP等级
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 3. 赠送礼物
|
||
**接口地址**: `POST /api/front/gift/send`
|
||
|
||
**请求参数** (SendGiftRequest):
|
||
```json
|
||
{
|
||
"giftId": "integer", // 礼物ID(必填)
|
||
"receiverId": "integer", // 接收者ID(必填)
|
||
"roomId": "string", // 直播间ID(必填)
|
||
"count": "integer", // 赠送数量(必填)
|
||
"message": "string" // 附加消息(可选)
|
||
}
|
||
```
|
||
|
||
**返回数据** (SendGiftResponse):
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "赠送成功",
|
||
"data": {
|
||
"giftRecordId": "long", // 礼物记录ID
|
||
"giftId": "integer", // 礼物ID
|
||
"giftName": "string", // 礼物名称
|
||
"count": "integer", // 赠送数量
|
||
"totalPrice": "integer", // 总价格
|
||
"newBalance": "integer", // 赠送后的余额
|
||
"sendTime": "long" // 赠送时间戳
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 4. 获取充值选项
|
||
**接口地址**: `GET /api/front/gift/recharge/options`
|
||
|
||
**请求参数**: 无
|
||
|
||
**返回数据**:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "success",
|
||
"data": [
|
||
{
|
||
"id": "string", // 选项ID
|
||
"coinAmount": "integer", // 金币数量
|
||
"price": "number", // 价格(元)
|
||
"discountLabel": "string",// 优惠标签(可选)
|
||
"isHot": "boolean", // 是否热门
|
||
"sort": "integer" // 排序
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 5. 创建充值订单
|
||
**接口地址**: `POST /api/front/gift/recharge/create`
|
||
|
||
**请求参数** (CreateRechargeRequest):
|
||
```json
|
||
{
|
||
"optionId": "string", // 充值选项ID(必填)
|
||
"coinAmount": "integer", // 金币数量(必填)
|
||
"price": "number" // 价格(必填)
|
||
}
|
||
```
|
||
|
||
**返回数据** (CreateRechargeResponse):
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "订单创建成功",
|
||
"data": {
|
||
"orderId": "string", // 订单ID
|
||
"orderNo": "string", // 订单号
|
||
"coinAmount": "integer", // 金币数量
|
||
"price": "number", // 价格
|
||
"paymentUrl": "string", // 支付URL
|
||
"createTime": "long" // 创建时间
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 功能说明
|
||
|
||
### 礼物等级
|
||
|
||
| 等级 | 价格范围 | 说明 |
|
||
|------|---------|------|
|
||
| 1 | 1-50金币 | 普通礼物 |
|
||
| 2 | 51-100金币 | 精美礼物 |
|
||
| 3 | 101-500金币 | 豪华礼物 |
|
||
| 4 | 501-1000金币 | 贵族礼物 |
|
||
| 5 | 1000+金币 | 至尊礼物 |
|
||
|
||
### 赠送流程
|
||
1. 用户点击礼物按钮
|
||
2. 显示礼物列表和当前余额
|
||
3. 选择礼物和数量
|
||
4. 检查余额是否足够
|
||
5. 调用赠送接口
|
||
6. 扣除金币,更新余额
|
||
7. 在直播间显示礼物动画
|
||
8. 通过WebSocket推送给其他观众
|
||
|
||
### 充值流程
|
||
1. 用户点击充值按钮
|
||
2. 显示充值选项列表
|
||
3. 选择充值金额
|
||
4. 创建充值订单
|
||
5. 选择支付方式(支付宝/微信)
|
||
6. 调用支付接口
|
||
7. 支付成功后更新余额
|
||
|
||
---
|
||
|
||
## 使用场景
|
||
|
||
### 1. 加载礼物列表
|
||
```java
|
||
private void loadGiftsFromBackend() {
|
||
ApiService apiService = ApiClient.getService(getApplicationContext());
|
||
Call<ApiResponse<List<GiftResponse>>> call = apiService.getGiftList();
|
||
|
||
call.enqueue(new Callback<ApiResponse<List<GiftResponse>>>() {
|
||
@Override
|
||
public void onResponse(Call<ApiResponse<List<GiftResponse>>> call,
|
||
Response<ApiResponse<List<GiftResponse>>> response) {
|
||
if (response.isSuccessful() && response.body() != null) {
|
||
ApiResponse<List<GiftResponse>> apiResponse = response.body();
|
||
if (apiResponse.getCode() == 200 && apiResponse.getData() != null) {
|
||
availableGifts = new ArrayList<>();
|
||
for (GiftResponse giftResponse : apiResponse.getData()) {
|
||
Gift gift = new Gift(
|
||
String.valueOf(giftResponse.getId()),
|
||
giftResponse.getName(),
|
||
giftResponse.getPrice().intValue(),
|
||
R.drawable.ic_gift_rose,
|
||
giftResponse.getLevel() != null ? giftResponse.getLevel() : 1
|
||
);
|
||
availableGifts.add(gift);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(Call<ApiResponse<List<GiftResponse>>> call, Throwable t) {
|
||
Log.e("Gift", "加载礼物列表失败: " + t.getMessage());
|
||
}
|
||
});
|
||
}
|
||
```
|
||
|
||
### 2. 获取用户余额
|
||
```java
|
||
private void loadUserBalance(TextView coinBalanceView) {
|
||
ApiService apiService = ApiClient.getService(getApplicationContext());
|
||
Call<ApiResponse<UserBalanceResponse>> call = apiService.getUserBalance();
|
||
|
||
call.enqueue(new Callback<ApiResponse<UserBalanceResponse>>() {
|
||
@Override
|
||
public void onResponse(Call<ApiResponse<UserBalanceResponse>> call,
|
||
Response<ApiResponse<UserBalanceResponse>> response) {
|
||
if (response.isSuccessful() && response.body() != null) {
|
||
ApiResponse<UserBalanceResponse> apiResponse = response.body();
|
||
if (apiResponse.getCode() == 200 && apiResponse.getData() != null) {
|
||
userCoinBalance = apiResponse.getData().getCoinBalance().intValue();
|
||
coinBalanceView.setText(String.valueOf(userCoinBalance));
|
||
}
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(Call<ApiResponse<UserBalanceResponse>> call, Throwable t) {
|
||
Log.e("Gift", "获取余额失败: " + t.getMessage());
|
||
}
|
||
});
|
||
}
|
||
```
|
||
|
||
### 3. 赠送礼物
|
||
```java
|
||
private void sendGiftToBackend(Gift selectedGift, int count, int totalPrice) {
|
||
// 检查余额
|
||
if (userCoinBalance < totalPrice) {
|
||
Toast.makeText(this, "金币不足,请先充值", Toast.LENGTH_SHORT).show();
|
||
showRechargeDialog();
|
||
return;
|
||
}
|
||
|
||
ApiService apiService = ApiClient.getService(getApplicationContext());
|
||
|
||
SendGiftRequest request = new SendGiftRequest();
|
||
request.setGiftId(Integer.parseInt(selectedGift.getId()));
|
||
request.setReceiverId(room.getStreamerId());
|
||
request.setRoomId(roomId);
|
||
request.setCount(count);
|
||
|
||
Call<ApiResponse<SendGiftResponse>> call = apiService.sendGift(request);
|
||
|
||
call.enqueue(new Callback<ApiResponse<SendGiftResponse>>() {
|
||
@Override
|
||
public void onResponse(Call<ApiResponse<SendGiftResponse>> call,
|
||
Response<ApiResponse<SendGiftResponse>> response) {
|
||
if (response.isSuccessful() && response.body() != null) {
|
||
ApiResponse<SendGiftResponse> apiResponse = response.body();
|
||
if (apiResponse.getCode() == 200 && apiResponse.getData() != null) {
|
||
SendGiftResponse giftResponse = apiResponse.getData();
|
||
|
||
// 更新余额
|
||
userCoinBalance = giftResponse.getNewBalance().intValue();
|
||
|
||
// 显示赠送消息
|
||
String giftMessage = String.format("送出了 %d 个 %s",
|
||
count, selectedGift.getName());
|
||
addChatMessage(new ChatMessage("我", giftMessage, true));
|
||
|
||
Toast.makeText(RoomDetailActivity.this,
|
||
"赠送成功!", Toast.LENGTH_SHORT).show();
|
||
}
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(Call<ApiResponse<SendGiftResponse>> call, Throwable t) {
|
||
Toast.makeText(RoomDetailActivity.this,
|
||
"网络错误: " + t.getMessage(), Toast.LENGTH_SHORT).show();
|
||
}
|
||
});
|
||
}
|
||
```
|
||
|
||
### 4. 创建充值订单
|
||
```java
|
||
private void createRechargeOrder(RechargeOption selectedOption) {
|
||
ApiService apiService = ApiClient.getService(getApplicationContext());
|
||
|
||
CreateRechargeRequest request = new CreateRechargeRequest();
|
||
request.setOptionId(selectedOption.getId());
|
||
request.setCoinAmount(selectedOption.getCoinAmount());
|
||
request.setPrice(selectedOption.getPrice());
|
||
|
||
Call<ApiResponse<CreateRechargeResponse>> call =
|
||
apiService.createRecharge(request);
|
||
|
||
call.enqueue(new Callback<ApiResponse<CreateRechargeResponse>>() {
|
||
@Override
|
||
public void onResponse(Call<ApiResponse<CreateRechargeResponse>> call,
|
||
Response<ApiResponse<CreateRechargeResponse>> response) {
|
||
if (response.isSuccessful() && response.body() != null) {
|
||
ApiResponse<CreateRechargeResponse> apiResponse = response.body();
|
||
if (apiResponse.getCode() == 200 && apiResponse.getData() != null) {
|
||
CreateRechargeResponse rechargeResponse = apiResponse.getData();
|
||
String orderId = rechargeResponse.getOrderId();
|
||
|
||
// 显示支付选择对话框
|
||
showPaymentMethodDialog(orderId, selectedOption);
|
||
}
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void onFailure(Call<ApiResponse<CreateRechargeResponse>> call, Throwable t) {
|
||
Toast.makeText(RoomDetailActivity.this,
|
||
"网络错误: " + t.getMessage(), Toast.LENGTH_SHORT).show();
|
||
}
|
||
});
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## WebSocket推送
|
||
|
||
礼物赠送后会通过WebSocket推送给直播间内的所有用户,用于显示礼物动画。
|
||
|
||
**推送消息格式**:
|
||
```json
|
||
{
|
||
"type": "gift",
|
||
"giftId": 1,
|
||
"giftName": "玫瑰",
|
||
"count": 10,
|
||
"senderId": 123,
|
||
"senderNickname": "张三",
|
||
"receiverId": 456,
|
||
"receiverNickname": "主播",
|
||
"roomId": "room123"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 默认礼物列表
|
||
|
||
如果接口加载失败,可以使用以下默认礼物:
|
||
|
||
| ID | 名称 | 价格 | 等级 |
|
||
|----|------|------|------|
|
||
| 1 | 玫瑰 | 10 | 1 |
|
||
| 2 | 爱心 | 20 | 1 |
|
||
| 3 | 蛋糕 | 50 | 2 |
|
||
| 4 | 星星 | 100 | 2 |
|
||
| 5 | 钻石 | 200 | 3 |
|
||
| 6 | 皇冠 | 500 | 4 |
|
||
| 7 | 跑车 | 1000 | 5 |
|
||
| 8 | 火箭 | 2000 | 5 |
|
||
|
||
---
|
||
|
||
## 注意事项
|
||
1. 所有接口都需要登录认证
|
||
2. 赠送礼物前必须检查余额是否足够
|
||
3. 礼物价格以金币为单位,不是人民币
|
||
4. 充值需要集成第三方支付SDK(支付宝/微信)
|
||
5. 礼物动画效果需要前端实现
|
||
6. 建议缓存礼物列表,减少网络请求
|
||
7. 余额变化后及时更新UI显示
|
||
8. 赠送失败时不扣除金币
|