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. 赠送失败时不扣除金币
|