解决合并冲突:许愿树对话框布局文件
This commit is contained in:
commit
8345dca1a1
304
.kiro/specs/wishtree-mobile/requirements.md
Normal file
304
.kiro/specs/wishtree-mobile/requirements.md
Normal file
|
|
@ -0,0 +1,304 @@
|
||||||
|
# 许愿树移动端设计规范
|
||||||
|
|
||||||
|
> 版本:V2.1
|
||||||
|
> 更新日期:2025-12-31
|
||||||
|
> 设计范围:Android移动端许愿树功能完善
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、设计原则
|
||||||
|
|
||||||
|
1. **不修改后端**:复用现有API,不做任何后端改动
|
||||||
|
2. **不修改管理端**:管理端功能保持不变
|
||||||
|
3. **功能完善**:确保所有功能逻辑正确
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、现有后端API(不可修改)
|
||||||
|
|
||||||
|
| 接口 | 方法 | 路径 | 说明 |
|
||||||
|
|------|------|------|------|
|
||||||
|
| 获取节日列表 | GET | `/api/front/wishtree/festivals` | 获取所有启用的节日 |
|
||||||
|
| 获取当前节日 | GET | `/api/front/wishtree/festivals/current` | 获取当前进行中的节日 |
|
||||||
|
| 获取心愿列表 | GET | `/api/front/wishtree/wishes` | 分页获取心愿列表 |
|
||||||
|
| 获取我的心愿 | GET | `/api/front/wishtree/wishes/my` | 获取当前用户的心愿 |
|
||||||
|
| 发布心愿 | POST | `/api/front/wishtree/wishes` | 发布新心愿 |
|
||||||
|
| 获取心愿详情 | GET | `/api/front/wishtree/wishes/{id}` | 获取心愿详情 |
|
||||||
|
| 删除心愿 | DELETE | `/api/front/wishtree/wishes/{id}` | 删除我的心愿 |
|
||||||
|
| 点赞心愿 | POST | `/api/front/wishtree/wishes/{id}/like` | 点赞 |
|
||||||
|
| 取消点赞 | DELETE | `/api/front/wishtree/wishes/{id}/like` | 取消点赞 |
|
||||||
|
| 获取评论 | GET | `/api/front/wishtree/wishes/{id}/comments` | 获取评论列表 |
|
||||||
|
| 发表评论 | POST | `/api/front/wishtree/wishes/{id}/comments` | 发表评论 |
|
||||||
|
| 获取背景素材 | GET | `/api/front/wishtree/backgrounds` | 获取背景素材列表 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、用户故事与验收标准
|
||||||
|
|
||||||
|
### US-001: 查看许愿树主页
|
||||||
|
|
||||||
|
**作为** 用户
|
||||||
|
**我想要** 进入许愿树页面查看许愿树和我的心愿
|
||||||
|
**以便于** 了解当前活动和管理我的心愿
|
||||||
|
|
||||||
|
**验收标准:**
|
||||||
|
- [x] 页面显示许愿树背景图
|
||||||
|
- [x] 显示当前节日活动横幅(调用 `/festivals/current`)
|
||||||
|
- [x] 显示我的心愿卡片(调用 `/wishes/my`,最多7个)
|
||||||
|
- [x] 显示"添加心愿"按钮
|
||||||
|
- [x] 底部导航栏正确高亮"许愿树"选项
|
||||||
|
- [x] 显示倒计时
|
||||||
|
- [x] 显示祈愿值统计
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### US-002: 发布心愿
|
||||||
|
|
||||||
|
**作为** 已登录用户
|
||||||
|
**我想要** 发布一个新的心愿
|
||||||
|
**以便于** 表达我的愿望
|
||||||
|
|
||||||
|
**验收标准:**
|
||||||
|
- [x] 点击"添加心愿"或空白卡片弹出许愿对话框
|
||||||
|
- [x] 输入框限制50字,显示字数统计
|
||||||
|
- [x] 未登录时提示登录并跳转登录页
|
||||||
|
- [x] 调用 `POST /wishes` 发布心愿
|
||||||
|
- [x] 提交成功后显示成功动画
|
||||||
|
- [x] 心愿列表自动刷新
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### US-003: 查看我的心愿
|
||||||
|
|
||||||
|
**作为** 用户
|
||||||
|
**我想要** 查看我已发布的心愿详情
|
||||||
|
**以便于** 回顾我的愿望
|
||||||
|
|
||||||
|
**验收标准:**
|
||||||
|
- [x] 点击心愿卡片弹出详情对话框
|
||||||
|
- [x] 显示心愿内容、点赞数、评论数
|
||||||
|
- [x] 提供"删除心愿"和"愿望达成"操作
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### US-004: 删除心愿
|
||||||
|
|
||||||
|
**作为** 用户
|
||||||
|
**我想要** 删除我不想要的心愿
|
||||||
|
**以便于** 管理我的心愿列表
|
||||||
|
|
||||||
|
**验收标准:**
|
||||||
|
- [x] 点击"删除心愿"弹出确认对话框
|
||||||
|
- [x] 确认后调用 `DELETE /wishes/{id}` 删除
|
||||||
|
- [x] 删除成功后刷新列表
|
||||||
|
- [x] 显示删除成功提示
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### US-005: 愿望达成
|
||||||
|
|
||||||
|
**作为** 用户
|
||||||
|
**我想要** 标记我的愿望已达成
|
||||||
|
**以便于** 庆祝愿望实现
|
||||||
|
|
||||||
|
**验收标准:**
|
||||||
|
- [x] 点击"愿望达成"弹出确认对话框
|
||||||
|
- [x] 确认后调用 `DELETE /wishes/{id}` 删除心愿
|
||||||
|
- [x] 显示庆祝动画(持续2秒)
|
||||||
|
- [x] 刷新心愿列表
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### US-006: 查看节日活动
|
||||||
|
|
||||||
|
**作为** 用户
|
||||||
|
**我想要** 看到当前进行中的节日活动
|
||||||
|
**以便于** 参与节日许愿
|
||||||
|
|
||||||
|
**验收标准:**
|
||||||
|
- [x] 页面顶部显示活动横幅
|
||||||
|
- [x] 横幅显示节日名称
|
||||||
|
- [x] 显示倒计时
|
||||||
|
- [x] 无活动时显示默认文案
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、移动端文件结构
|
||||||
|
|
||||||
|
```
|
||||||
|
android-app/app/src/main/java/com/example/livestreaming/
|
||||||
|
├── WishTreeActivity.java # 许愿树主页面
|
||||||
|
└── net/
|
||||||
|
├── ApiService.java # API接口定义
|
||||||
|
├── WishtreeRequest.java # 请求类
|
||||||
|
└── WishtreeResponse.java # 响应类
|
||||||
|
|
||||||
|
android-app/app/src/main/res/layout/
|
||||||
|
├── activity_wish_tree.xml # 主页面布局
|
||||||
|
├── dialog_make_wish.xml # 许愿对话框
|
||||||
|
├── dialog_view_wish.xml # 查看心愿对话框
|
||||||
|
├── dialog_wish_success.xml # 成功提示对话框
|
||||||
|
└── dialog_wish_complete.xml # 愿望达成对话框
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、数据模型
|
||||||
|
|
||||||
|
### 5.1 WishtreeResponse.java
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class WishtreeResponse {
|
||||||
|
// 节日信息
|
||||||
|
public static class Festival {
|
||||||
|
public int id;
|
||||||
|
public String name;
|
||||||
|
public String icon;
|
||||||
|
public String startDate;
|
||||||
|
public String endDate;
|
||||||
|
public int isLunar;
|
||||||
|
public String themeColor;
|
||||||
|
public int status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 心愿信息
|
||||||
|
public static class Wish {
|
||||||
|
public long id;
|
||||||
|
public int uid;
|
||||||
|
public String nickname;
|
||||||
|
public String avatar;
|
||||||
|
public int festivalId;
|
||||||
|
public String festivalName;
|
||||||
|
public String festivalIcon;
|
||||||
|
public String content;
|
||||||
|
public int backgroundId;
|
||||||
|
public String backgroundImage;
|
||||||
|
public int status; // 0待审核 1通过 2拒绝
|
||||||
|
public int likeCount;
|
||||||
|
public int commentCount;
|
||||||
|
public Boolean isLiked;
|
||||||
|
public String createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页数据
|
||||||
|
public static class WishPage {
|
||||||
|
public List<Wish> list;
|
||||||
|
public int total;
|
||||||
|
public int pageNum;
|
||||||
|
public int pageSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 WishtreeRequest.java
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class WishtreeRequest {
|
||||||
|
// 发布心愿请求
|
||||||
|
public static class PublishWish {
|
||||||
|
public Integer festivalId;
|
||||||
|
public String content;
|
||||||
|
public Integer backgroundId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、核心交互流程
|
||||||
|
|
||||||
|
### 6.1 页面加载
|
||||||
|
|
||||||
|
```
|
||||||
|
onCreate()
|
||||||
|
├── 初始化ApiService
|
||||||
|
├── 启动横幅倒计时
|
||||||
|
├── 设置底部导航
|
||||||
|
├── 设置心愿卡片点击事件
|
||||||
|
├── loadCurrentFestival() → 更新横幅文字
|
||||||
|
└── loadMyWishes() → 更新心愿卡片、祈愿值
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 发布心愿
|
||||||
|
|
||||||
|
```
|
||||||
|
点击"添加心愿"
|
||||||
|
├── checkLogin() → 未登录跳转登录页
|
||||||
|
├── showMakeWishInputDialog()
|
||||||
|
├── 输入内容(≤50字)
|
||||||
|
├── publishWish() → POST /wishes
|
||||||
|
├── 成功 → showSuccessDialog() → loadMyWishes()
|
||||||
|
└── 失败 → Toast提示
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.3 删除/达成心愿
|
||||||
|
|
||||||
|
```
|
||||||
|
点击心愿卡片
|
||||||
|
├── showViewWishDialog()
|
||||||
|
├── 点击"删除心愿"
|
||||||
|
│ ├── 确认对话框
|
||||||
|
│ ├── DELETE /wishes/{id}
|
||||||
|
│ └── 成功 → Toast → loadMyWishes()
|
||||||
|
└── 点击"愿望达成"
|
||||||
|
├── 确认对话框
|
||||||
|
├── DELETE /wishes/{id}
|
||||||
|
└── 成功 → 庆祝动画 → loadMyWishes()
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 七、错误处理
|
||||||
|
|
||||||
|
| 场景 | 处理方式 |
|
||||||
|
|------|----------|
|
||||||
|
| 网络错误 | Toast "网络错误" |
|
||||||
|
| 业务错误 | Toast 显示后端返回的message |
|
||||||
|
| 未登录 | 跳转LoginActivity |
|
||||||
|
| 空数据 | 显示空列表,卡片显示空白 |
|
||||||
|
| 内容为空 | Toast "请输入心愿内容" |
|
||||||
|
| 内容超长 | Toast "心愿内容不能超过50字" |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 八、实现任务清单
|
||||||
|
|
||||||
|
### Phase 1: 数据层 ✅
|
||||||
|
- [x] WishtreeResponse.java - 响应类
|
||||||
|
- [x] WishtreeRequest.java - 请求类
|
||||||
|
- [x] ApiService.java - 接口定义
|
||||||
|
|
||||||
|
### Phase 2: UI层 ✅
|
||||||
|
- [x] activity_wish_tree.xml - 主页面
|
||||||
|
- [x] dialog_make_wish.xml - 许愿对话框
|
||||||
|
- [x] dialog_view_wish.xml - 查看心愿对话框
|
||||||
|
- [x] dialog_wish_success.xml - 成功提示
|
||||||
|
- [x] dialog_wish_complete.xml - 愿望达成
|
||||||
|
|
||||||
|
### Phase 3: 逻辑层 ✅
|
||||||
|
- [x] WishTreeActivity.java - 核心逻辑
|
||||||
|
- [x] 页面加载与数据刷新
|
||||||
|
- [x] 发布心愿功能
|
||||||
|
- [x] 删除心愿功能
|
||||||
|
- [x] 愿望达成功能
|
||||||
|
- [x] 登录检查
|
||||||
|
- [x] 错误处理
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 九、测试要点
|
||||||
|
|
||||||
|
### 功能测试
|
||||||
|
- [x] 页面正常加载
|
||||||
|
- [x] 节日信息正确显示
|
||||||
|
- [x] 心愿卡片正确显示(最多7个)
|
||||||
|
- [x] 发布心愿成功
|
||||||
|
- [x] 删除心愿成功
|
||||||
|
- [x] 愿望达成功能正常
|
||||||
|
- [x] 底部导航切换正常
|
||||||
|
|
||||||
|
### 异常测试
|
||||||
|
- [x] 网络断开时的提示
|
||||||
|
- [x] 未登录时跳转登录页
|
||||||
|
- [x] 空数据时的显示
|
||||||
|
- [x] 心愿内容校验(空、超长)
|
||||||
|
|
@ -68,14 +68,6 @@ export function wishTreeNodeListApi(params) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 节点详情
|
|
||||||
export function wishTreeNodeInfoApi(id) {
|
|
||||||
return request({
|
|
||||||
url: `/admin/wish/tree/node/info/${id}`,
|
|
||||||
method: 'get',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 新增节点
|
// 新增节点
|
||||||
export function wishTreeNodeSaveApi(data) {
|
export function wishTreeNodeSaveApi(data) {
|
||||||
return request({
|
return request({
|
||||||
|
|
@ -112,7 +104,7 @@ export function wishTreeNodeStatusApi(id) {
|
||||||
|
|
||||||
// ========== 用户留言管理 ==========
|
// ========== 用户留言管理 ==========
|
||||||
|
|
||||||
// 留言列表(按许愿树查询)
|
// 留言列表
|
||||||
export function wishTreeMessageListApi(params) {
|
export function wishTreeMessageListApi(params) {
|
||||||
return request({
|
return request({
|
||||||
url: '/admin/wish/tree/message/list',
|
url: '/admin/wish/tree/message/list',
|
||||||
|
|
@ -121,14 +113,6 @@ export function wishTreeMessageListApi(params) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 留言详情
|
|
||||||
export function wishTreeMessageInfoApi(id) {
|
|
||||||
return request({
|
|
||||||
url: `/admin/wish/tree/message/info/${id}`,
|
|
||||||
method: 'get',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除留言
|
// 删除留言
|
||||||
export function wishTreeMessageDeleteApi(id) {
|
export function wishTreeMessageDeleteApi(id) {
|
||||||
return request({
|
return request({
|
||||||
|
|
@ -144,3 +128,107 @@ export function wishTreeMessageStatusApi(id) {
|
||||||
method: 'post',
|
method: 'post',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========== 节日管理 ==========
|
||||||
|
|
||||||
|
// 节日列表
|
||||||
|
export function festivalList(params) {
|
||||||
|
return request({
|
||||||
|
url: '/admin/wishtree/festival/list',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存节日
|
||||||
|
export function festivalSave(data) {
|
||||||
|
return request({
|
||||||
|
url: '/admin/wishtree/festival/save',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除节日 - 后端使用DELETE方法
|
||||||
|
export function festivalDelete(id) {
|
||||||
|
return request({
|
||||||
|
url: `/admin/wishtree/festival/delete/${id}`,
|
||||||
|
method: 'delete',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改节日状态 - 后端接收RequestBody
|
||||||
|
export function festivalStatus(id, status) {
|
||||||
|
return request({
|
||||||
|
url: '/admin/wishtree/festival/status',
|
||||||
|
method: 'post',
|
||||||
|
data: { id, status },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 心愿管理 ==========
|
||||||
|
|
||||||
|
// 心愿列表 - 后端使用POST方法
|
||||||
|
export function wishList(data) {
|
||||||
|
return request({
|
||||||
|
url: '/admin/wishtree/wish/list',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 审核心愿
|
||||||
|
export function wishAudit(data) {
|
||||||
|
return request({
|
||||||
|
url: '/admin/wishtree/wish/audit',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除心愿 - 后端使用DELETE方法
|
||||||
|
export function wishDelete(id) {
|
||||||
|
return request({
|
||||||
|
url: `/admin/wishtree/wish/delete/${id}`,
|
||||||
|
method: 'delete',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 背景管理 ==========
|
||||||
|
|
||||||
|
// 背景列表
|
||||||
|
export function backgroundList(params) {
|
||||||
|
return request({
|
||||||
|
url: '/admin/wishtree/background/list',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存背景
|
||||||
|
export function backgroundSave(data) {
|
||||||
|
return request({
|
||||||
|
url: '/admin/wishtree/background/save',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除背景 - 后端使用DELETE方法
|
||||||
|
export function backgroundDelete(id) {
|
||||||
|
return request({
|
||||||
|
url: `/admin/wishtree/background/delete/${id}`,
|
||||||
|
method: 'delete',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 统计 ==========
|
||||||
|
|
||||||
|
// 获取统计数据
|
||||||
|
export function getStatistics(params) {
|
||||||
|
return request({
|
||||||
|
url: '/admin/wishtree/statistics',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
||||||
BIN
Zhibo/admin/src/assets/imgs/logo-lefttop.png
Normal file
BIN
Zhibo/admin/src/assets/imgs/logo-lefttop.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.5 MiB |
BIN
Zhibo/admin/src/assets/imgs/logo-original.png
Normal file
BIN
Zhibo/admin/src/assets/imgs/logo-original.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.5 MiB |
BIN
Zhibo/admin/src/assets/imgs/logo-square.png
Normal file
BIN
Zhibo/admin/src/assets/imgs/logo-square.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.5 MiB |
|
|
@ -4,21 +4,20 @@
|
||||||
v-if="$store.state.themeConfig.themeConfig.layout !== 'columns' && !$store.state.themeConfig.themeConfig.isCollapse"
|
v-if="$store.state.themeConfig.themeConfig.layout !== 'columns' && !$store.state.themeConfig.themeConfig.isCollapse"
|
||||||
@click="onThemeConfigChange"
|
@click="onThemeConfigChange"
|
||||||
>
|
>
|
||||||
<img v-if="platMerLoginInfo" class="layout-logo-medium-img" :src="platMerLoginInfo.siteLogoSquare" />
|
<!-- 直接使用本地Logo图片,替换Logo只需修改 logo-square.png 文件 -->
|
||||||
|
<img class="layout-logo-medium-img" src="@/assets/imgs/logo-square.png" />
|
||||||
</div>
|
</div>
|
||||||
<div class="layout-logo-size" v-else @click="onThemeConfigChange">
|
<div class="layout-logo-size" v-else @click="onThemeConfigChange">
|
||||||
<img v-if="platMerLoginInfo" class="layout-logo-size-img" :src="platMerLoginInfo.siteLogoLeftTop" />
|
<!-- 直接使用本地Logo图片,替换Logo只需修改 logo-lefttop.png 文件 -->
|
||||||
|
<img class="layout-logo-size-img" src="@/assets/imgs/logo-lefttop.png" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Cookies from 'js-cookie';
|
|
||||||
export default {
|
export default {
|
||||||
name: 'layoutLogo',
|
name: 'layoutLogo',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {};
|
||||||
platMerLoginInfo: JSON.parse(Cookies.get('logoInfo')), //登录后的信息
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
// 获取布局配置信息
|
// 获取布局配置信息
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,31 @@ const wishtreeManageRouter = {
|
||||||
name: 'WishtreeStatistics',
|
name: 'WishtreeStatistics',
|
||||||
meta: { title: '数据统计' },
|
meta: { title: '数据统计' },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'tree',
|
||||||
|
component: () => import('@/views/wishtree/tree/index'),
|
||||||
|
name: 'WishTreeList',
|
||||||
|
meta: { title: '许愿树列表' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'tree/detail/:id',
|
||||||
|
component: () => import('@/views/wishtree/tree/detail'),
|
||||||
|
name: 'WishTreeDetail',
|
||||||
|
meta: { title: '许愿树详情' },
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'node',
|
||||||
|
component: () => import('@/views/wishtree/node/index'),
|
||||||
|
name: 'WishTreeNode',
|
||||||
|
meta: { title: '节点管理' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'message',
|
||||||
|
component: () => import('@/views/wishtree/message/index'),
|
||||||
|
name: 'WishTreeMessage',
|
||||||
|
meta: { title: '留言管理' },
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ const VUE_APP_WS_URL =
|
||||||
const SettingMer = {
|
const SettingMer = {
|
||||||
// 服务器地址
|
// 服务器地址
|
||||||
httpUrl: VUE_APP_API_URL,
|
httpUrl: VUE_APP_API_URL,
|
||||||
// 接口请求地址
|
// 接口请求地址 - 不再重复添加/api
|
||||||
apiBaseURL: VUE_APP_API_URL + '/api/',
|
apiBaseURL: VUE_APP_API_URL,
|
||||||
// socket连接
|
// socket连接
|
||||||
wsSocketUrl: VUE_APP_WS_URL,
|
wsSocketUrl: VUE_APP_WS_URL,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleViewMessages(row) {
|
handleViewMessages(row) {
|
||||||
this.$router.push({ path: `/wishTree/tree/detail/${row.id}` });
|
this.$router.push({ path: `/wishtree/tree/detail/${row.id}` });
|
||||||
},
|
},
|
||||||
addNode() {
|
addNode() {
|
||||||
this.formData.nodes.push({ title: '', description: '', open_time: null, sort: 0, status: 1 });
|
this.formData.nodes.push({ title: '', description: '', open_time: null, sort: 0, status: 1 });
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { backgroundList, backgroundSave, backgroundDelete, festivalList } from '@/api/wishtree';
|
import { backgroundList, backgroundSave, backgroundDelete, festivalList } from '@/api/wishTree';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WishtreeBackground',
|
name: 'WishtreeBackground',
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { festivalList, festivalSave, festivalDelete, festivalStatus } from '@/api/wishtree';
|
import { festivalList, festivalSave, festivalDelete, festivalStatus } from '@/api/wishTree';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WishtreeFestival',
|
name: 'WishtreeFestival',
|
||||||
|
|
@ -132,7 +132,7 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async handleStatusChange(row) {
|
async handleStatusChange(row) {
|
||||||
await festivalStatus({ id: row.id, status: row.status });
|
await festivalStatus(row.id, row.status);
|
||||||
this.$message.success('状态更新成功');
|
this.$message.success('状态更新成功');
|
||||||
},
|
},
|
||||||
handleDelete(row) {
|
handleDelete(row) {
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getStatistics } from '@/api/wishtree';
|
import { getStatistics } from '@/api/wishTree';
|
||||||
import * as echarts from 'echarts';
|
import * as echarts from 'echarts';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { wishList, wishAudit, wishDelete, festivalList } from '@/api/wishtree';
|
import { wishList, wishAudit, wishDelete, festivalList } from '@/api/wishTree';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WishtreeWish',
|
name: 'WishtreeWish',
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,12 @@ module.exports = {
|
||||||
warnings: false,
|
warnings: false,
|
||||||
errors: true,
|
errors: true,
|
||||||
},
|
},
|
||||||
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
target: 'http://localhost:30001',
|
||||||
|
changeOrigin: true
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
configureWebpack: {
|
configureWebpack: {
|
||||||
// provide the app's title in webpack's name field, so that
|
// provide the app's title in webpack's name field, so that
|
||||||
|
|
|
||||||
|
|
@ -296,8 +296,9 @@ public class WishtreeServiceImpl implements WishtreeService {
|
||||||
WishtreeStatisticsVO vo = new WishtreeStatisticsVO();
|
WishtreeStatisticsVO vo = new WishtreeStatisticsVO();
|
||||||
|
|
||||||
// 总心愿数
|
// 总心愿数
|
||||||
vo.setTotalWishes(Long.valueOf(wishDao.selectCount(new LambdaQueryWrapper<WishtreeWish>()
|
Integer totalWishes = wishDao.selectCount(new LambdaQueryWrapper<WishtreeWish>()
|
||||||
.eq(WishtreeWish::getIsDelete, 0))));
|
.eq(WishtreeWish::getIsDelete, 0));
|
||||||
|
vo.setTotalWishes(totalWishes != null ? totalWishes.longValue() : 0L);
|
||||||
|
|
||||||
// 今日新增
|
// 今日新增
|
||||||
vo.setTodayWishes(wishDao.countTodayWishes());
|
vo.setTodayWishes(wishDao.countTodayWishes());
|
||||||
|
|
@ -306,11 +307,13 @@ public class WishtreeServiceImpl implements WishtreeService {
|
||||||
vo.setPendingWishes(wishDao.countPendingWishes());
|
vo.setPendingWishes(wishDao.countPendingWishes());
|
||||||
|
|
||||||
// 总点赞数
|
// 总点赞数
|
||||||
vo.setTotalLikes(Long.valueOf(likeDao.selectCount(null)));
|
Integer totalLikes = likeDao.selectCount(null);
|
||||||
|
vo.setTotalLikes(totalLikes != null ? totalLikes.longValue() : 0L);
|
||||||
|
|
||||||
// 总评论数
|
// 总评论数
|
||||||
vo.setTotalComments(Long.valueOf(commentDao.selectCount(new LambdaQueryWrapper<WishtreeComment>()
|
Integer totalComments = commentDao.selectCount(new LambdaQueryWrapper<WishtreeComment>()
|
||||||
.eq(WishtreeComment::getIsDelete, 0))));
|
.eq(WishtreeComment::getIsDelete, 0));
|
||||||
|
vo.setTotalComments(totalComments != null ? totalComments.longValue() : 0L);
|
||||||
|
|
||||||
// 心愿趋势
|
// 心愿趋势
|
||||||
vo.setWishTrend(wishDao.selectWishTrend(days));
|
vo.setWishTrend(wishDao.selectWishTrend(days));
|
||||||
|
|
|
||||||
98
Zhibo/zhibo-h/sql/wishtree_quick_setup.sql
Normal file
98
Zhibo/zhibo-h/sql/wishtree_quick_setup.sql
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
-- ============================================
|
||||||
|
-- 许愿树快速建表脚本
|
||||||
|
-- 执行此脚本创建许愿树所需的所有表
|
||||||
|
-- ============================================
|
||||||
|
|
||||||
|
-- 1. 节日表
|
||||||
|
CREATE TABLE IF NOT EXISTS `eb_wishtree_festival` (
|
||||||
|
`id` int NOT NULL AUTO_INCREMENT COMMENT '节日ID',
|
||||||
|
`name` varchar(50) NOT NULL COMMENT '节日名称',
|
||||||
|
`icon` varchar(255) DEFAULT '' COMMENT '节日图标',
|
||||||
|
`start_date` varchar(20) DEFAULT '' COMMENT '开始日期',
|
||||||
|
`end_date` varchar(20) DEFAULT '' COMMENT '结束日期',
|
||||||
|
`is_lunar` tinyint DEFAULT 0 COMMENT '是否农历',
|
||||||
|
`theme_color` varchar(20) DEFAULT '#FF6B6B' COMMENT '主题色',
|
||||||
|
`sort` int DEFAULT 0 COMMENT '排序',
|
||||||
|
`status` tinyint DEFAULT 1 COMMENT '状态 0禁用 1启用',
|
||||||
|
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='许愿树节日表';
|
||||||
|
|
||||||
|
-- 2. 心愿表
|
||||||
|
CREATE TABLE IF NOT EXISTS `eb_wishtree_wish` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '心愿ID',
|
||||||
|
`uid` int NOT NULL COMMENT '用户ID',
|
||||||
|
`festival_id` int DEFAULT 0 COMMENT '节日ID',
|
||||||
|
`content` varchar(500) NOT NULL COMMENT '心愿内容',
|
||||||
|
`background_id` int DEFAULT 0 COMMENT '背景ID',
|
||||||
|
`status` tinyint DEFAULT 1 COMMENT '状态 0待审核 1通过 2拒绝',
|
||||||
|
`audit_type` tinyint DEFAULT 0 COMMENT '审核方式 0自动 1人工',
|
||||||
|
`audit_remark` varchar(255) DEFAULT '' COMMENT '审核备注',
|
||||||
|
`like_count` int DEFAULT 0 COMMENT '点赞数',
|
||||||
|
`comment_count` int DEFAULT 0 COMMENT '评论数',
|
||||||
|
`is_delete` tinyint DEFAULT 0 COMMENT '是否删除',
|
||||||
|
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_uid` (`uid`),
|
||||||
|
KEY `idx_festival` (`festival_id`),
|
||||||
|
KEY `idx_status` (`status`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='许愿树心愿表';
|
||||||
|
|
||||||
|
-- 3. 点赞表
|
||||||
|
CREATE TABLE IF NOT EXISTS `eb_wishtree_like` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`uid` int NOT NULL COMMENT '用户ID',
|
||||||
|
`wish_id` bigint NOT NULL COMMENT '心愿ID',
|
||||||
|
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_uid_wish` (`uid`, `wish_id`),
|
||||||
|
KEY `idx_wish` (`wish_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='许愿树点赞表';
|
||||||
|
|
||||||
|
-- 4. 评论表
|
||||||
|
CREATE TABLE IF NOT EXISTS `eb_wishtree_comment` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`wish_id` bigint NOT NULL COMMENT '心愿ID',
|
||||||
|
`uid` int NOT NULL COMMENT '用户ID',
|
||||||
|
`content` varchar(255) NOT NULL COMMENT '评论内容',
|
||||||
|
`parent_id` bigint DEFAULT 0 COMMENT '父评论ID',
|
||||||
|
`status` tinyint DEFAULT 1 COMMENT '状态',
|
||||||
|
`is_delete` tinyint DEFAULT 0 COMMENT '是否删除',
|
||||||
|
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_wish` (`wish_id`),
|
||||||
|
KEY `idx_uid` (`uid`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='许愿树评论表';
|
||||||
|
|
||||||
|
-- 5. 背景素材表
|
||||||
|
CREATE TABLE IF NOT EXISTS `eb_wishtree_background` (
|
||||||
|
`id` int NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` varchar(50) NOT NULL COMMENT '背景名称',
|
||||||
|
`image` varchar(255) NOT NULL COMMENT '背景图片',
|
||||||
|
`festival_id` int DEFAULT 0 COMMENT '关联节日ID',
|
||||||
|
`sort` int DEFAULT 0 COMMENT '排序',
|
||||||
|
`status` tinyint DEFAULT 1 COMMENT '状态',
|
||||||
|
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='许愿树背景素材表';
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- 初始化节日数据
|
||||||
|
-- ============================================
|
||||||
|
INSERT IGNORE INTO `eb_wishtree_festival` (`id`, `name`, `icon`, `start_date`, `end_date`, `is_lunar`, `theme_color`, `sort`, `status`) VALUES
|
||||||
|
(1, '元旦', '🎉', '12-31', '01-03', 0, '#FF6B6B', 100, 1),
|
||||||
|
(2, '春节', '🧧', '除夕', '正月十五', 1, '#E74C3C', 99, 1),
|
||||||
|
(3, '情人节', '💕', '02-13', '02-15', 0, '#FF69B4', 98, 1),
|
||||||
|
(4, '生日祝福', '🎂', '全年', '全年', 0, '#FF69B4', 50, 1),
|
||||||
|
(5, '日常祝福', '✨', '全年', '全年', 0, '#9B59B6', 49, 1);
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- 验证表是否创建成功
|
||||||
|
-- ============================================
|
||||||
|
SELECT 'eb_wishtree_festival' AS table_name, COUNT(*) AS count FROM eb_wishtree_festival;
|
||||||
|
SELECT 'eb_wishtree_wish' AS table_name, COUNT(*) AS count FROM eb_wishtree_wish;
|
||||||
|
SELECT 'eb_wishtree_like' AS table_name, COUNT(*) AS count FROM eb_wishtree_like;
|
||||||
|
SELECT 'eb_wishtree_comment' AS table_name, COUNT(*) AS count FROM eb_wishtree_comment;
|
||||||
|
SELECT 'eb_wishtree_background' AS table_name, COUNT(*) AS count FROM eb_wishtree_background;
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
package com.example.livestreaming;
|
package com.example.livestreaming;
|
||||||
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
|
import android.app.ProgressDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.drawable.ColorDrawable;
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
@ -16,24 +16,48 @@ import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
import com.example.livestreaming.databinding.ActivityWishTreeBinding;
|
import com.example.livestreaming.databinding.ActivityWishTreeBinding;
|
||||||
|
import com.example.livestreaming.net.ApiClient;
|
||||||
|
import com.example.livestreaming.net.ApiResponse;
|
||||||
|
import com.example.livestreaming.net.AuthStore;
|
||||||
|
import com.example.livestreaming.net.WishtreeRequest;
|
||||||
|
import com.example.livestreaming.net.WishtreeResponse;
|
||||||
|
import com.example.livestreaming.net.ApiService;
|
||||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.Callback;
|
||||||
|
import retrofit2.Response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 许愿树页面
|
||||||
|
* 数据通过API实时同步到服务端
|
||||||
|
*/
|
||||||
public class WishTreeActivity extends AppCompatActivity {
|
public class WishTreeActivity extends AppCompatActivity {
|
||||||
|
|
||||||
private ActivityWishTreeBinding binding;
|
private ActivityWishTreeBinding binding;
|
||||||
private final Handler handler = new Handler(Looper.getMainLooper());
|
private final Handler handler = new Handler(Looper.getMainLooper());
|
||||||
private Runnable timerRunnable;
|
private Runnable timerRunnable;
|
||||||
private SharedPreferences prefs;
|
private ApiService apiService;
|
||||||
private String[] wishes = new String[7];
|
|
||||||
|
// 心愿数据(从服务端获取)
|
||||||
|
private List<WishtreeResponse.Wish> myWishes = new ArrayList<>();
|
||||||
|
// 当前节日
|
||||||
|
private WishtreeResponse.Festival currentFestival;
|
||||||
|
// 是否正在加载
|
||||||
|
private boolean isLoading = false;
|
||||||
|
|
||||||
public static void start(Context context) {
|
public static void start(Context context) {
|
||||||
context.startActivity(new Intent(context, WishTreeActivity.class));
|
context.startActivity(new Intent(context, WishTreeActivity.class));
|
||||||
|
|
@ -45,36 +69,112 @@ public class WishTreeActivity extends AppCompatActivity {
|
||||||
binding = ActivityWishTreeBinding.inflate(getLayoutInflater());
|
binding = ActivityWishTreeBinding.inflate(getLayoutInflater());
|
||||||
setContentView(binding.getRoot());
|
setContentView(binding.getRoot());
|
||||||
|
|
||||||
prefs = getSharedPreferences("wishes", MODE_PRIVATE);
|
apiService = ApiClient.getService(this);
|
||||||
loadWishes();
|
|
||||||
|
|
||||||
startBannerCountdown();
|
startBannerCountdown();
|
||||||
setupBottomNav();
|
setupBottomNav();
|
||||||
setupWishCards();
|
setupWishCards();
|
||||||
setupMakeWishButton();
|
setupMakeWishButton();
|
||||||
|
setupTopBarButtons();
|
||||||
|
|
||||||
|
// 加载数据
|
||||||
|
loadCurrentFestival();
|
||||||
|
loadMyWishes();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadWishes() {
|
/**
|
||||||
for (int i = 0; i < 7; i++) {
|
* 设置顶部栏按钮
|
||||||
wishes[i] = prefs.getString("wish_" + i, "");
|
*/
|
||||||
|
private void setupTopBarButtons() {
|
||||||
|
// 搜索按钮
|
||||||
|
binding.searchButton.setOnClickListener(v -> {
|
||||||
|
SearchActivity.start(this);
|
||||||
|
});
|
||||||
|
// 通知按钮
|
||||||
|
binding.notifyButton.setOnClickListener(v -> {
|
||||||
|
NotificationsActivity.start(this);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否已登录
|
||||||
|
*/
|
||||||
|
private boolean checkLogin() {
|
||||||
|
String token = AuthStore.getToken(this);
|
||||||
|
if (token == null || token.isEmpty()) {
|
||||||
|
Toast.makeText(this, "请先登录", Toast.LENGTH_SHORT).show();
|
||||||
|
startActivity(new Intent(this, LoginActivity.class));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载当前节日
|
||||||
|
*/
|
||||||
|
private void loadCurrentFestival() {
|
||||||
|
apiService.getCurrentFestival().enqueue(new Callback<ApiResponse<WishtreeResponse.Festival>>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<ApiResponse<WishtreeResponse.Festival>> call,
|
||||||
|
@NonNull Response<ApiResponse<WishtreeResponse.Festival>> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null && response.body().getData() != null) {
|
||||||
|
currentFestival = response.body().getData();
|
||||||
|
updateBannerText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<ApiResponse<WishtreeResponse.Festival>> call, @NonNull Throwable t) {
|
||||||
|
// 使用默认文案
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新横幅文字
|
||||||
|
*/
|
||||||
|
private void updateBannerText() {
|
||||||
|
if (currentFestival != null && binding != null) {
|
||||||
|
String text = currentFestival.name + "许愿树 | 许下心愿,愿望成真";
|
||||||
|
binding.bannerText.setText(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从服务端加载我的心愿
|
||||||
|
*/
|
||||||
|
private void loadMyWishes() {
|
||||||
|
apiService.getMyWishes(1, 10).enqueue(new Callback<ApiResponse<WishtreeResponse.WishPage>>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<ApiResponse<WishtreeResponse.WishPage>> call,
|
||||||
|
@NonNull Response<ApiResponse<WishtreeResponse.WishPage>> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null && response.body().getData() != null) {
|
||||||
|
myWishes = response.body().getData().list;
|
||||||
|
if (myWishes == null) myWishes = new ArrayList<>();
|
||||||
updateWishCards();
|
updateWishCards();
|
||||||
|
updateWishCount();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveWish(int index, String wish) {
|
@Override
|
||||||
wishes[index] = wish;
|
public void onFailure(@NonNull Call<ApiResponse<WishtreeResponse.WishPage>> call, @NonNull Throwable t) {
|
||||||
prefs.edit().putString("wish_" + index, wish).apply();
|
Toast.makeText(WishTreeActivity.this, "加载心愿失败", Toast.LENGTH_SHORT).show();
|
||||||
updateWishCards();
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新心愿卡片显示
|
||||||
|
*/
|
||||||
private void updateWishCards() {
|
private void updateWishCards() {
|
||||||
TextView[] cards = {
|
TextView[] cards = {
|
||||||
binding.wishCard1, binding.wishCard2, binding.wishCard3,
|
binding.wishCard1, binding.wishCard2, binding.wishCard3,
|
||||||
binding.wishCard4, binding.wishCard5, binding.wishCard6, binding.wishCard7
|
binding.wishCard4, binding.wishCard5, binding.wishCard6, binding.wishCard7
|
||||||
};
|
};
|
||||||
for (int i = 0; i < cards.length; i++) {
|
for (int i = 0; i < cards.length; i++) {
|
||||||
if (wishes[i] != null && !wishes[i].isEmpty()) {
|
if (i < myWishes.size() && myWishes.get(i) != null) {
|
||||||
String text = wishes[i].length() > 8 ? wishes[i].substring(0, 8) + "..." : wishes[i];
|
String content = myWishes.get(i).content;
|
||||||
|
String text = content.length() > 8 ? content.substring(0, 8) + "..." : content;
|
||||||
cards[i].setText(text);
|
cards[i].setText(text);
|
||||||
} else {
|
} else {
|
||||||
cards[i].setText("");
|
cards[i].setText("");
|
||||||
|
|
@ -82,6 +182,17 @@ public class WishTreeActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新祈愿值显示
|
||||||
|
*/
|
||||||
|
private void updateWishCount() {
|
||||||
|
int count = myWishes != null ? myWishes.size() : 0;
|
||||||
|
binding.tvWishCount.setText("祈愿值:" + count + "/100");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置心愿卡片点击事件
|
||||||
|
*/
|
||||||
private void setupWishCards() {
|
private void setupWishCards() {
|
||||||
TextView[] cards = {
|
TextView[] cards = {
|
||||||
binding.wishCard1, binding.wishCard2, binding.wishCard3,
|
binding.wishCard1, binding.wishCard2, binding.wishCard3,
|
||||||
|
|
@ -91,29 +202,36 @@ public class WishTreeActivity extends AppCompatActivity {
|
||||||
final int index = i;
|
final int index = i;
|
||||||
cards[i].setOnClickListener(v -> onWishCardClick(index));
|
cards[i].setOnClickListener(v -> onWishCardClick(index));
|
||||||
}
|
}
|
||||||
binding.addWishCard.setOnClickListener(v -> showMakeWishInputDialog(-1));
|
binding.addWishCard.setOnClickListener(v -> showMakeWishInputDialog());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 心愿卡片点击
|
||||||
|
*/
|
||||||
private void onWishCardClick(int index) {
|
private void onWishCardClick(int index) {
|
||||||
if (wishes[index] != null && !wishes[index].isEmpty()) {
|
if (index < myWishes.size() && myWishes.get(index) != null) {
|
||||||
showViewWishDialog(index);
|
showViewWishDialog(index);
|
||||||
} else {
|
} else {
|
||||||
showMakeWishInputDialog(index);
|
showMakeWishInputDialog();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showMakeWishInputDialog(int index) {
|
/**
|
||||||
|
* 显示许愿输入对话框
|
||||||
|
*/
|
||||||
|
private void showMakeWishInputDialog() {
|
||||||
|
// 检查登录状态
|
||||||
|
if (!checkLogin()) return;
|
||||||
|
|
||||||
Dialog dialog = new Dialog(this);
|
Dialog dialog = new Dialog(this);
|
||||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||||
dialog.setContentView(R.layout.dialog_make_wish);
|
dialog.setContentView(R.layout.dialog_make_wish);
|
||||||
if (dialog.getWindow() != null) {
|
if (dialog.getWindow() != null) {
|
||||||
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||||
// 设置对话框宽度为屏幕宽度的85%
|
|
||||||
int width = (int) (getResources().getDisplayMetrics().widthPixels * 0.85);
|
int width = (int) (getResources().getDisplayMetrics().widthPixels * 0.85);
|
||||||
dialog.getWindow().setLayout(width, android.view.WindowManager.LayoutParams.WRAP_CONTENT);
|
dialog.getWindow().setLayout(width, android.view.WindowManager.LayoutParams.WRAP_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
EditText input = dialog.findViewById(R.id.editWish);
|
EditText input = dialog.findViewById(R.id.editWish);
|
||||||
TextView tvCharCount = dialog.findViewById(R.id.tvCharCount);
|
TextView tvCharCount = dialog.findViewById(R.id.tvCharCount);
|
||||||
View btnCancel = dialog.findViewById(R.id.btnCancel);
|
View btnCancel = dialog.findViewById(R.id.btnCancel);
|
||||||
|
|
@ -136,14 +254,11 @@ public class WishTreeActivity extends AppCompatActivity {
|
||||||
btnMakeWish.setOnClickListener(v -> {
|
btnMakeWish.setOnClickListener(v -> {
|
||||||
String wish = input.getText().toString().trim();
|
String wish = input.getText().toString().trim();
|
||||||
if (!wish.isEmpty()) {
|
if (!wish.isEmpty()) {
|
||||||
int saveIndex = index >= 0 ? index : findEmptySlot();
|
if (wish.length() > 50) {
|
||||||
if (saveIndex >= 0) {
|
Toast.makeText(this, "心愿内容不能超过50字", Toast.LENGTH_SHORT).show();
|
||||||
saveWish(saveIndex, wish);
|
return;
|
||||||
dialog.dismiss();
|
|
||||||
showSuccessDialog();
|
|
||||||
} else {
|
|
||||||
Toast.makeText(this, "心愿牌已满", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
}
|
||||||
|
publishWish(wish, dialog);
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(this, "请输入心愿内容", Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, "请输入心愿内容", Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
|
|
@ -152,13 +267,42 @@ public class WishTreeActivity extends AppCompatActivity {
|
||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int findEmptySlot() {
|
/**
|
||||||
for (int i = 0; i < wishes.length; i++) {
|
* 发布心愿到服务端
|
||||||
if (wishes[i] == null || wishes[i].isEmpty()) return i;
|
*/
|
||||||
|
private void publishWish(String content, Dialog dialog) {
|
||||||
|
Integer festivalId = currentFestival != null ? currentFestival.id : null;
|
||||||
|
WishtreeRequest.PublishWish request = new WishtreeRequest.PublishWish(festivalId, content, null);
|
||||||
|
|
||||||
|
apiService.publishWish(request).enqueue(new Callback<ApiResponse<WishtreeResponse.Wish>>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<ApiResponse<WishtreeResponse.Wish>> call,
|
||||||
|
@NonNull Response<ApiResponse<WishtreeResponse.Wish>> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
|
if (response.body().getCode() == 200) {
|
||||||
|
dialog.dismiss();
|
||||||
|
showSuccessDialog();
|
||||||
|
loadMyWishes(); // 重新加载心愿列表
|
||||||
|
} else {
|
||||||
|
Toast.makeText(WishTreeActivity.this,
|
||||||
|
response.body().getMessage() != null ? response.body().getMessage() : "发布失败",
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Toast.makeText(WishTreeActivity.this, "发布失败", Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<ApiResponse<WishtreeResponse.Wish>> call, @NonNull Throwable t) {
|
||||||
|
Toast.makeText(WishTreeActivity.this, "网络错误", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示成功对话框
|
||||||
|
*/
|
||||||
private void showSuccessDialog() {
|
private void showSuccessDialog() {
|
||||||
Dialog dialog = new Dialog(this);
|
Dialog dialog = new Dialog(this);
|
||||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||||
|
|
@ -170,12 +314,20 @@ public class WishTreeActivity extends AppCompatActivity {
|
||||||
handler.postDelayed(dialog::dismiss, 1500);
|
handler.postDelayed(dialog::dismiss, 1500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示查看心愿对话框
|
||||||
|
*/
|
||||||
private void showViewWishDialog(int index) {
|
private void showViewWishDialog(int index) {
|
||||||
|
if (index >= myWishes.size()) return;
|
||||||
|
WishtreeResponse.Wish wish = myWishes.get(index);
|
||||||
|
|
||||||
Dialog dialog = new Dialog(this);
|
Dialog dialog = new Dialog(this);
|
||||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||||
dialog.setContentView(R.layout.dialog_view_wish);
|
dialog.setContentView(R.layout.dialog_view_wish);
|
||||||
if (dialog.getWindow() != null) {
|
if (dialog.getWindow() != null) {
|
||||||
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||||
|
int width = (int) (getResources().getDisplayMetrics().widthPixels * 0.85);
|
||||||
|
dialog.getWindow().setLayout(width, android.view.WindowManager.LayoutParams.WRAP_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
TextView tvContent = dialog.findViewById(R.id.tvWishContent);
|
TextView tvContent = dialog.findViewById(R.id.tvWishContent);
|
||||||
|
|
@ -183,27 +335,94 @@ public class WishTreeActivity extends AppCompatActivity {
|
||||||
TextView btnDelete = dialog.findViewById(R.id.btnDeleteWish);
|
TextView btnDelete = dialog.findViewById(R.id.btnDeleteWish);
|
||||||
TextView btnComplete = dialog.findViewById(R.id.btnCompleteWish);
|
TextView btnComplete = dialog.findViewById(R.id.btnCompleteWish);
|
||||||
|
|
||||||
tvContent.setText(wishes[index]);
|
tvContent.setText(wish.content);
|
||||||
|
|
||||||
|
// 显示点赞数和评论数
|
||||||
|
TextView tvLikeCount = dialog.findViewById(R.id.tvLikeCount);
|
||||||
|
TextView tvCommentCount = dialog.findViewById(R.id.tvCommentCount);
|
||||||
|
if (tvLikeCount != null) {
|
||||||
|
tvLikeCount.setText("❤ " + wish.likeCount);
|
||||||
|
}
|
||||||
|
if (tvCommentCount != null) {
|
||||||
|
tvCommentCount.setText("💬 " + wish.commentCount);
|
||||||
|
}
|
||||||
|
|
||||||
btnClose.setOnClickListener(v -> dialog.dismiss());
|
btnClose.setOnClickListener(v -> dialog.dismiss());
|
||||||
|
|
||||||
|
// 删除心愿 - 添加确认对话框
|
||||||
btnDelete.setOnClickListener(v -> {
|
btnDelete.setOnClickListener(v -> {
|
||||||
saveWish(index, "");
|
new AlertDialog.Builder(this)
|
||||||
Toast.makeText(this, "心愿已删除", Toast.LENGTH_SHORT).show();
|
.setTitle("确认删除")
|
||||||
dialog.dismiss();
|
.setMessage("确定要删除这个心愿吗?")
|
||||||
|
.setPositiveButton("删除", (d, w) -> {
|
||||||
|
deleteWish(wish.id, dialog, false);
|
||||||
|
})
|
||||||
|
.setNegativeButton("取消", null)
|
||||||
|
.show();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 愿望达成 - 添加确认对话框
|
||||||
btnComplete.setOnClickListener(v -> {
|
btnComplete.setOnClickListener(v -> {
|
||||||
saveWish(index, "");
|
new AlertDialog.Builder(this)
|
||||||
Toast.makeText(this, "恭喜愿望达成!", Toast.LENGTH_SHORT).show();
|
.setTitle("愿望达成")
|
||||||
dialog.dismiss();
|
.setMessage("恭喜!确认愿望已经达成了吗?")
|
||||||
|
.setPositiveButton("确认", (d, w) -> {
|
||||||
|
deleteWish(wish.id, dialog, true);
|
||||||
|
})
|
||||||
|
.setNegativeButton("取消", null)
|
||||||
|
.show();
|
||||||
});
|
});
|
||||||
|
|
||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除心愿
|
||||||
|
* @param wishId 心愿ID
|
||||||
|
* @param dialog 对话框
|
||||||
|
* @param isComplete 是否是愿望达成
|
||||||
|
*/
|
||||||
|
private void deleteWish(long wishId, Dialog dialog, boolean isComplete) {
|
||||||
|
apiService.deleteMyWish(wishId).enqueue(new Callback<ApiResponse<String>>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<ApiResponse<String>> call,
|
||||||
|
@NonNull Response<ApiResponse<String>> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null && response.body().getCode() == 200) {
|
||||||
|
dialog.dismiss();
|
||||||
|
if (isComplete) {
|
||||||
|
showCompleteSuccessDialog();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(WishTreeActivity.this, "心愿已删除", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
loadMyWishes(); // 重新加载
|
||||||
|
} else {
|
||||||
|
Toast.makeText(WishTreeActivity.this, "操作失败", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<ApiResponse<String>> call, @NonNull Throwable t) {
|
||||||
|
Toast.makeText(WishTreeActivity.this, "网络错误", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示愿望达成成功对话框
|
||||||
|
*/
|
||||||
|
private void showCompleteSuccessDialog() {
|
||||||
|
Dialog dialog = new Dialog(this);
|
||||||
|
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||||
|
dialog.setContentView(R.layout.dialog_wish_complete);
|
||||||
|
if (dialog.getWindow() != null) {
|
||||||
|
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||||
|
}
|
||||||
|
dialog.show();
|
||||||
|
handler.postDelayed(dialog::dismiss, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
private void setupMakeWishButton() {
|
private void setupMakeWishButton() {
|
||||||
binding.btnMakeWish.setOnClickListener(v -> showMakeWishInputDialog(-1));
|
binding.btnMakeWish.setOnClickListener(v -> showMakeWishInputDialog());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupBottomNav() {
|
private void setupBottomNav() {
|
||||||
|
|
@ -295,5 +514,7 @@ public class WishTreeActivity extends AppCompatActivity {
|
||||||
binding.bottomNavInclude.bottomNavigation.setSelectedItemId(R.id.nav_wish_tree);
|
binding.bottomNavInclude.bottomNavigation.setSelectedItemId(R.id.nav_wish_tree);
|
||||||
UnreadMessageManager.updateBadge(binding.bottomNavInclude.bottomNavigation);
|
UnreadMessageManager.updateBadge(binding.bottomNavInclude.bottomNavigation);
|
||||||
}
|
}
|
||||||
|
// 每次返回页面时刷新数据
|
||||||
|
loadMyWishes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -387,4 +387,88 @@ public interface ApiService {
|
||||||
|
|
||||||
@POST("api/front/live/rooms/{roomId}/broadcast/online")
|
@POST("api/front/live/rooms/{roomId}/broadcast/online")
|
||||||
Call<ApiResponse<Map<String, Object>>> broadcastOnlineCount(@Path("roomId") String roomId);
|
Call<ApiResponse<Map<String, Object>>> broadcastOnlineCount(@Path("roomId") String roomId);
|
||||||
|
|
||||||
|
// ==================== 许愿树接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取节日列表
|
||||||
|
*/
|
||||||
|
@GET("api/front/wishtree/festivals")
|
||||||
|
Call<ApiResponse<List<WishtreeResponse.Festival>>> getWishtreeFestivals();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前进行中的节日
|
||||||
|
*/
|
||||||
|
@GET("api/front/wishtree/festivals/current")
|
||||||
|
Call<ApiResponse<WishtreeResponse.Festival>> getCurrentFestival();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取心愿列表
|
||||||
|
*/
|
||||||
|
@GET("api/front/wishtree/wishes")
|
||||||
|
Call<ApiResponse<WishtreeResponse.WishPage>> getWishtreeWishes(
|
||||||
|
@Query("festivalId") Integer festivalId,
|
||||||
|
@Query("page") int page,
|
||||||
|
@Query("limit") int limit);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取我的心愿列表
|
||||||
|
*/
|
||||||
|
@GET("api/front/wishtree/wishes/my")
|
||||||
|
Call<ApiResponse<WishtreeResponse.WishPage>> getMyWishes(
|
||||||
|
@Query("page") int page,
|
||||||
|
@Query("limit") int limit);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发布心愿
|
||||||
|
*/
|
||||||
|
@POST("api/front/wishtree/wishes")
|
||||||
|
Call<ApiResponse<WishtreeResponse.Wish>> publishWish(@Body WishtreeRequest.PublishWish body);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取心愿详情
|
||||||
|
*/
|
||||||
|
@GET("api/front/wishtree/wishes/{id}")
|
||||||
|
Call<ApiResponse<WishtreeResponse.Wish>> getWishDetail(@Path("id") long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除我的心愿
|
||||||
|
*/
|
||||||
|
@DELETE("api/front/wishtree/wishes/{id}")
|
||||||
|
Call<ApiResponse<String>> deleteMyWish(@Path("id") long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点赞心愿
|
||||||
|
*/
|
||||||
|
@POST("api/front/wishtree/wishes/{id}/like")
|
||||||
|
Call<ApiResponse<String>> likeWish(@Path("id") long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消点赞
|
||||||
|
*/
|
||||||
|
@DELETE("api/front/wishtree/wishes/{id}/like")
|
||||||
|
Call<ApiResponse<String>> unlikeWish(@Path("id") long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取评论列表
|
||||||
|
*/
|
||||||
|
@GET("api/front/wishtree/wishes/{id}/comments")
|
||||||
|
Call<ApiResponse<WishtreeResponse.CommentPage>> getWishComments(
|
||||||
|
@Path("id") long wishId,
|
||||||
|
@Query("page") int page,
|
||||||
|
@Query("limit") int limit);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发表评论
|
||||||
|
*/
|
||||||
|
@POST("api/front/wishtree/wishes/{id}/comments")
|
||||||
|
Call<ApiResponse<WishtreeResponse.Comment>> addWishComment(
|
||||||
|
@Path("id") long wishId,
|
||||||
|
@Body WishtreeRequest.AddComment body);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取背景素材列表
|
||||||
|
*/
|
||||||
|
@GET("api/front/wishtree/backgrounds")
|
||||||
|
Call<ApiResponse<List<WishtreeResponse.Background>>> getWishtreeBackgrounds();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
package com.example.livestreaming.net;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 许愿树相关请求类
|
||||||
|
*/
|
||||||
|
public class WishtreeRequest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发布心愿请求
|
||||||
|
*/
|
||||||
|
public static class PublishWish {
|
||||||
|
@SerializedName("festivalId")
|
||||||
|
public Integer festivalId;
|
||||||
|
@SerializedName("content")
|
||||||
|
public String content;
|
||||||
|
@SerializedName("backgroundId")
|
||||||
|
public Integer backgroundId;
|
||||||
|
|
||||||
|
public PublishWish(String content) {
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PublishWish(Integer festivalId, String content, Integer backgroundId) {
|
||||||
|
this.festivalId = festivalId;
|
||||||
|
this.content = content;
|
||||||
|
this.backgroundId = backgroundId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发表评论请求
|
||||||
|
*/
|
||||||
|
public static class AddComment {
|
||||||
|
@SerializedName("content")
|
||||||
|
public String content;
|
||||||
|
@SerializedName("parentId")
|
||||||
|
public Long parentId;
|
||||||
|
|
||||||
|
public AddComment(String content) {
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AddComment(String content, Long parentId) {
|
||||||
|
this.content = content;
|
||||||
|
this.parentId = parentId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,136 @@
|
||||||
|
package com.example.livestreaming.net;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 许愿树相关响应类
|
||||||
|
*/
|
||||||
|
public class WishtreeResponse {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 节日信息
|
||||||
|
*/
|
||||||
|
public static class Festival {
|
||||||
|
@SerializedName("id")
|
||||||
|
public int id;
|
||||||
|
@SerializedName("name")
|
||||||
|
public String name;
|
||||||
|
@SerializedName("icon")
|
||||||
|
public String icon;
|
||||||
|
@SerializedName("startDate")
|
||||||
|
public String startDate;
|
||||||
|
@SerializedName("endDate")
|
||||||
|
public String endDate;
|
||||||
|
@SerializedName("isLunar")
|
||||||
|
public int isLunar;
|
||||||
|
@SerializedName("themeColor")
|
||||||
|
public String themeColor;
|
||||||
|
@SerializedName("status")
|
||||||
|
public int status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 心愿信息
|
||||||
|
*/
|
||||||
|
public static class Wish {
|
||||||
|
@SerializedName("id")
|
||||||
|
public long id;
|
||||||
|
@SerializedName("uid")
|
||||||
|
public int uid;
|
||||||
|
@SerializedName("nickname")
|
||||||
|
public String nickname;
|
||||||
|
@SerializedName("avatar")
|
||||||
|
public String avatar;
|
||||||
|
@SerializedName("festivalId")
|
||||||
|
public int festivalId;
|
||||||
|
@SerializedName("festivalName")
|
||||||
|
public String festivalName;
|
||||||
|
@SerializedName("festivalIcon")
|
||||||
|
public String festivalIcon;
|
||||||
|
@SerializedName("content")
|
||||||
|
public String content;
|
||||||
|
@SerializedName("backgroundId")
|
||||||
|
public int backgroundId;
|
||||||
|
@SerializedName("backgroundImage")
|
||||||
|
public String backgroundImage;
|
||||||
|
@SerializedName("status")
|
||||||
|
public int status;
|
||||||
|
@SerializedName("likeCount")
|
||||||
|
public int likeCount;
|
||||||
|
@SerializedName("commentCount")
|
||||||
|
public int commentCount;
|
||||||
|
@SerializedName("isLiked")
|
||||||
|
public Boolean isLiked;
|
||||||
|
@SerializedName("createTime")
|
||||||
|
public String createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 背景素材
|
||||||
|
*/
|
||||||
|
public static class Background {
|
||||||
|
@SerializedName("id")
|
||||||
|
public int id;
|
||||||
|
@SerializedName("name")
|
||||||
|
public String name;
|
||||||
|
@SerializedName("image")
|
||||||
|
public String image;
|
||||||
|
@SerializedName("festivalId")
|
||||||
|
public int festivalId;
|
||||||
|
@SerializedName("status")
|
||||||
|
public int status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 评论信息
|
||||||
|
*/
|
||||||
|
public static class Comment {
|
||||||
|
@SerializedName("id")
|
||||||
|
public long id;
|
||||||
|
@SerializedName("wishId")
|
||||||
|
public long wishId;
|
||||||
|
@SerializedName("uid")
|
||||||
|
public int uid;
|
||||||
|
@SerializedName("nickname")
|
||||||
|
public String nickname;
|
||||||
|
@SerializedName("avatar")
|
||||||
|
public String avatar;
|
||||||
|
@SerializedName("content")
|
||||||
|
public String content;
|
||||||
|
@SerializedName("parentId")
|
||||||
|
public long parentId;
|
||||||
|
@SerializedName("status")
|
||||||
|
public int status;
|
||||||
|
@SerializedName("createTime")
|
||||||
|
public String createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页数据
|
||||||
|
*/
|
||||||
|
public static class WishPage {
|
||||||
|
@SerializedName("list")
|
||||||
|
public List<Wish> list;
|
||||||
|
@SerializedName("total")
|
||||||
|
public int total;
|
||||||
|
@SerializedName("pageNum")
|
||||||
|
public int pageNum;
|
||||||
|
@SerializedName("pageSize")
|
||||||
|
public int pageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 评论分页数据
|
||||||
|
*/
|
||||||
|
public static class CommentPage {
|
||||||
|
@SerializedName("list")
|
||||||
|
public List<Comment> list;
|
||||||
|
@SerializedName("total")
|
||||||
|
public int total;
|
||||||
|
@SerializedName("pageNum")
|
||||||
|
public int pageNum;
|
||||||
|
@SerializedName("pageSize")
|
||||||
|
public int pageSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
android-app/app/src/main/res/drawable/app_logo.png
Normal file
BIN
android-app/app/src/main/res/drawable/app_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.5 MiB |
|
|
@ -1,5 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
<solid android:color="#F0F0F0" />
|
<solid android:color="#F0F0F0" />
|
||||||
<corners android:radius="22dp" />
|
<corners android:radius="22dp" />
|
||||||
</shape>
|
</shape>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
<solid android:color="#FF6B9D" />
|
<solid android:color="#FF6B9D" />
|
||||||
<corners android:radius="22dp" />
|
<corners android:radius="22dp" />
|
||||||
</shape>
|
</shape>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
<solid android:color="#FFFFFF" />
|
<solid android:color="#FFFFFF" />
|
||||||
<corners android:radius="16dp" />
|
<corners android:radius="16dp" />
|
||||||
</shape>
|
</shape>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
<solid android:color="#F5F5F5" />
|
<solid android:color="#F5F5F5" />
|
||||||
<corners android:radius="8dp" />
|
<corners android:radius="8dp" />
|
||||||
<stroke android:width="1dp" android:color="#E0E0E0" />
|
<stroke android:width="1dp" android:color="#E0E0E0" />
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="许下你的愿望"
|
android:text="许下你的愿望"
|
||||||
android:textSize="18sp"
|
android:textSize="18sp"
|
||||||
android:textStyle="bold"
|
|
||||||
android:textColor="#333333"
|
android:textColor="#333333"
|
||||||
|
android:textStyle="bold"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:layout_marginBottom="16dp" />
|
android:layout_marginBottom="16dp" />
|
||||||
|
|
||||||
|
|
@ -21,11 +21,14 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:hint="写下你的愿望..."
|
android:hint="写下你的愿望..."
|
||||||
|
android:textColorHint="#999999"
|
||||||
|
android:textColor="#333333"
|
||||||
android:minLines="3"
|
android:minLines="3"
|
||||||
android:maxLength="50"
|
android:maxLength="50"
|
||||||
android:gravity="top"
|
android:gravity="top"
|
||||||
android:padding="12dp"
|
android:padding="12dp"
|
||||||
android:background="@drawable/bg_edit_text" />
|
android:background="@drawable/bg_edit_text"
|
||||||
|
android:layout_marginBottom="8dp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvCharCount"
|
android:id="@+id/tvCharCount"
|
||||||
|
|
@ -35,13 +38,12 @@
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
android:textColor="#999999"
|
android:textColor="#999999"
|
||||||
android:gravity="end"
|
android:gravity="end"
|
||||||
android:layout_marginTop="4dp" />
|
android:layout_marginBottom="16dp" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal">
|
||||||
android:layout_marginTop="16dp">
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/btnCancel"
|
android:id="@+id/btnCancel"
|
||||||
|
|
@ -66,5 +68,7 @@
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:background="@drawable/bg_btn_primary"
|
android:background="@drawable/bg_btn_primary"
|
||||||
android:layout_marginStart="8dp" />
|
android:layout_marginStart="8dp" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
|
||||||
|
|
@ -8,16 +8,17 @@
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
android:text="我的心愿"
|
android:text="我的心愿"
|
||||||
android:textSize="18sp"
|
android:textSize="18sp"
|
||||||
android:textStyle="bold"
|
|
||||||
android:textColor="#333333"
|
android:textColor="#333333"
|
||||||
android:gravity="center" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/btnClose"
|
android:id="@+id/btnClose"
|
||||||
|
|
@ -26,26 +27,52 @@
|
||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
android:src="@android:drawable/ic_menu_close_clear_cancel"
|
android:src="@android:drawable/ic_menu_close_clear_cancel"
|
||||||
android:contentDescription="关闭" />
|
android:contentDescription="关闭" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvWishContent"
|
android:id="@+id/tvWishContent"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="20dp"
|
android:minHeight="80dp"
|
||||||
android:layout_marginBottom="20dp"
|
android:text=""
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:textColor="#333333"
|
android:textColor="#333333"
|
||||||
android:gravity="center"
|
|
||||||
android:minHeight="60dp"
|
|
||||||
android:padding="12dp"
|
android:padding="12dp"
|
||||||
android:background="@drawable/bg_wish_content" />
|
android:background="@drawable/bg_edit_text"
|
||||||
|
android:layout_marginBottom="12dp" />
|
||||||
|
|
||||||
|
<!-- 点赞数和评论数 -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:layout_marginTop="8dp">
|
android:layout_marginBottom="16dp"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvLikeCount"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="❤ 0"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="#FF6B6B"
|
||||||
|
android:layout_marginEnd="20dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvCommentCount"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="💬 0"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="#666666" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/btnDeleteWish"
|
android:id="@+id/btnDeleteWish"
|
||||||
|
|
@ -70,5 +97,7 @@
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:background="@drawable/bg_btn_primary"
|
android:background="@drawable/bg_btn_primary"
|
||||||
android:layout_marginStart="8dp" />
|
android:layout_marginStart="8dp" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
|
||||||
33
android-app/app/src/main/res/layout/dialog_wish_complete.xml
Normal file
33
android-app/app/src/main/res/layout/dialog_wish_complete.xml
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@drawable/bg_dialog_rounded"
|
||||||
|
android:padding="32dp"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="🎉"
|
||||||
|
android:textSize="48sp"
|
||||||
|
android:layout_marginBottom="16dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="恭喜愿望达成!"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textColor="#FF6D00"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginBottom="8dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="愿你心想事成,万事如意"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="#666666" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
|
|
||||||
# 使用 Android Studio 自带的 JDK 17
|
# JDK配置由Android Studio自动管理
|
||||||
org.gradle.java.home=C:/Program Files/Android/Android Studio/jbr
|
# 请在Android Studio中编译:Build -> Rebuild Project
|
||||||
|
|
||||||
systemProp.gradle.wrapperUser=myuser
|
systemProp.gradle.wrapperUser=myuser
|
||||||
systemProp.gradle.wrapperPassword=mypassword
|
systemProp.gradle.wrapperPassword=mypassword
|
||||||
537
android-app/移动端许愿树功能设计文档.md
Normal file
537
android-app/移动端许愿树功能设计文档.md
Normal file
|
|
@ -0,0 +1,537 @@
|
||||||
|
# 许愿树移动端功能设计文档
|
||||||
|
|
||||||
|
> 版本:V1.0
|
||||||
|
> 更新日期:2025-12-31
|
||||||
|
> 设计范围:Android移动端许愿树功能
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、功能概述
|
||||||
|
|
||||||
|
### 1.1 核心特性
|
||||||
|
|
||||||
|
- **服务端实时存储**:心愿数据通过API实时同步到服务端
|
||||||
|
- **节日主题**:支持不同节日的许愿活动
|
||||||
|
- **社交互动**:支持点赞、评论功能
|
||||||
|
- **多端同步**:数据云端存储,多设备共享
|
||||||
|
|
||||||
|
### 1.2 数据流架构
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────┐ API请求 ┌─────────────────┐
|
||||||
|
│ WishTreeActivity│ ◄──────────────► │ 后端服务 │
|
||||||
|
└─────────────────┘ └─────────────────┘
|
||||||
|
│ │
|
||||||
|
│ Retrofit │ MyBatis
|
||||||
|
▼ ▼
|
||||||
|
ApiService MySQL数据库
|
||||||
|
(许愿树接口) (eb_wishtree_*)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、页面设计
|
||||||
|
|
||||||
|
### 2.1 主页面布局 (activity_wish_tree.xml)
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ [许愿树标题] [🔍] [🔔] │ ← 顶部栏
|
||||||
|
├─────────────────────────────────────────────────────────┤
|
||||||
|
│ ┌─────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ 🎉 元旦许愿树 | 许下心愿,愿望成真 [12:30 05:00] │ │ ← 活动横幅
|
||||||
|
│ └─────────────────────────────────────────────────────┘ │
|
||||||
|
├─────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ [心愿1] [心愿2] [心愿3] │
|
||||||
|
│ │
|
||||||
|
│ [心愿4] [心愿+] [心愿5] │ ← 许愿树区域
|
||||||
|
│ │
|
||||||
|
│ [心愿6] [心愿7] │
|
||||||
|
│ │
|
||||||
|
│ 元 │
|
||||||
|
│ 旦 │
|
||||||
|
│ 许 │
|
||||||
|
│ 愿 │
|
||||||
|
│ 树 │
|
||||||
|
│ │
|
||||||
|
├─────────────────────────────────────────────────────────┤
|
||||||
|
│ 祈愿值:3/100 │ ← 底部区域
|
||||||
|
│ [ 前往祈愿 ] │
|
||||||
|
├─────────────────────────────────────────────────────────┤
|
||||||
|
│ [首页] [缘池] [许愿树] [消息] [我的] │ ← 底部导航
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 许愿对话框 (dialog_make_wish.xml)
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ 许下心愿 │
|
||||||
|
├─────────────────────────────────────┤
|
||||||
|
│ ┌─────────────────────────────────┐ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ 请输入您的心愿... │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ └─────────────────────────────────┘ │
|
||||||
|
│ 0/50 │
|
||||||
|
├─────────────────────────────────────┤
|
||||||
|
│ [取消] [许愿] │
|
||||||
|
└─────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3 查看心愿对话框 (dialog_view_wish.xml)
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ 我的心愿 [✕] │
|
||||||
|
├─────────────────────────────────────┤
|
||||||
|
│ ┌─────────────────────────────────┐ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ 新年快乐,万事如意! │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ └─────────────────────────────────┘ │
|
||||||
|
│ ❤ 128 💬 32 │
|
||||||
|
├─────────────────────────────────────┤
|
||||||
|
│ [删除心愿] [愿望达成] │
|
||||||
|
└─────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.4 成功提示对话框 (dialog_wish_success.xml)
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ │
|
||||||
|
│ ✨ │
|
||||||
|
│ 许愿成功! │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、API接口设计
|
||||||
|
|
||||||
|
### 3.1 接口列表
|
||||||
|
|
||||||
|
| 接口 | 方法 | 路径 | 说明 |
|
||||||
|
|------|------|------|------|
|
||||||
|
| 获取节日列表 | GET | `/api/front/wishtree/festivals` | 获取所有启用的节日 |
|
||||||
|
| 获取当前节日 | GET | `/api/front/wishtree/festivals/current` | 获取当前进行中的节日 |
|
||||||
|
| 获取心愿列表 | GET | `/api/front/wishtree/wishes` | 分页获取心愿列表 |
|
||||||
|
| 获取我的心愿 | GET | `/api/front/wishtree/wishes/my` | 获取当前用户的心愿 |
|
||||||
|
| 发布心愿 | POST | `/api/front/wishtree/wishes` | 发布新心愿 |
|
||||||
|
| 获取心愿详情 | GET | `/api/front/wishtree/wishes/{id}` | 获取心愿详情 |
|
||||||
|
| 删除心愿 | DELETE | `/api/front/wishtree/wishes/{id}` | 删除我的心愿 |
|
||||||
|
| 点赞心愿 | POST | `/api/front/wishtree/wishes/{id}/like` | 点赞 |
|
||||||
|
| 取消点赞 | DELETE | `/api/front/wishtree/wishes/{id}/like` | 取消点赞 |
|
||||||
|
| 获取评论 | GET | `/api/front/wishtree/wishes/{id}/comments` | 获取评论列表 |
|
||||||
|
| 发表评论 | POST | `/api/front/wishtree/wishes/{id}/comments` | 发表评论 |
|
||||||
|
| 获取背景素材 | GET | `/api/front/wishtree/backgrounds` | 获取背景素材列表 |
|
||||||
|
|
||||||
|
### 3.2 请求/响应示例
|
||||||
|
|
||||||
|
#### 发布心愿
|
||||||
|
|
||||||
|
```json
|
||||||
|
// POST /api/front/wishtree/wishes
|
||||||
|
// Request:
|
||||||
|
{
|
||||||
|
"festivalId": 1,
|
||||||
|
"content": "新年快乐,万事如意!",
|
||||||
|
"backgroundId": 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response:
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "success",
|
||||||
|
"data": {
|
||||||
|
"id": 10001,
|
||||||
|
"uid": 1001,
|
||||||
|
"festivalId": 1,
|
||||||
|
"content": "新年快乐,万事如意!",
|
||||||
|
"status": 1,
|
||||||
|
"likeCount": 0,
|
||||||
|
"commentCount": 0,
|
||||||
|
"createTime": "2025-12-31 12:00:00"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 获取我的心愿列表
|
||||||
|
|
||||||
|
```json
|
||||||
|
// GET /api/front/wishtree/wishes/my?page=1&limit=10
|
||||||
|
// Response:
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"data": {
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"id": 10001,
|
||||||
|
"uid": 1001,
|
||||||
|
"nickname": "用户昵称",
|
||||||
|
"avatar": "https://xxx/avatar.jpg",
|
||||||
|
"festivalId": 1,
|
||||||
|
"festivalName": "元旦",
|
||||||
|
"festivalIcon": "🎉",
|
||||||
|
"content": "新年快乐,万事如意!",
|
||||||
|
"backgroundImage": "https://xxx/bg1.jpg",
|
||||||
|
"status": 1,
|
||||||
|
"likeCount": 128,
|
||||||
|
"commentCount": 32,
|
||||||
|
"isLiked": false,
|
||||||
|
"createTime": "2025-12-31 12:00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 5,
|
||||||
|
"pageNum": 1,
|
||||||
|
"pageSize": 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、代码结构
|
||||||
|
|
||||||
|
### 4.1 文件清单
|
||||||
|
|
||||||
|
```
|
||||||
|
android-app/app/src/main/java/com/example/livestreaming/
|
||||||
|
├── WishTreeActivity.java # 许愿树主页面
|
||||||
|
└── net/
|
||||||
|
├── ApiService.java # API接口定义(含许愿树接口)
|
||||||
|
├── WishtreeRequest.java # 请求类
|
||||||
|
│ ├── PublishWish # 发布心愿请求
|
||||||
|
│ └── AddComment # 发表评论请求
|
||||||
|
└── WishtreeResponse.java # 响应类
|
||||||
|
├── Festival # 节日信息
|
||||||
|
├── Wish # 心愿信息
|
||||||
|
├── Background # 背景素材
|
||||||
|
├── Comment # 评论信息
|
||||||
|
├── WishPage # 心愿分页数据
|
||||||
|
└── CommentPage # 评论分页数据
|
||||||
|
|
||||||
|
android-app/app/src/main/res/layout/
|
||||||
|
├── activity_wish_tree.xml # 主页面布局
|
||||||
|
├── dialog_make_wish.xml # 许愿对话框
|
||||||
|
├── dialog_view_wish.xml # 查看心愿对话框
|
||||||
|
└── dialog_wish_success.xml # 成功提示对话框
|
||||||
|
|
||||||
|
android-app/app/src/main/res/drawable/
|
||||||
|
├── bg_wish_tree_banner.xml # 横幅背景
|
||||||
|
├── bg_wish_tree_timer.xml # 倒计时背景
|
||||||
|
├── bg_wish_card.xml # 心愿卡片背景
|
||||||
|
├── bg_wish_card_add.xml # 添加心愿卡片背景
|
||||||
|
├── bg_trunk_text.xml # 树干文字背景
|
||||||
|
├── bg_dialog_rounded.xml # 对话框圆角背景
|
||||||
|
├── bg_edit_text.xml # 输入框背景
|
||||||
|
├── bg_btn_primary.xml # 主按钮背景
|
||||||
|
└── bg_btn_cancel.xml # 取消按钮背景
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 核心类设计
|
||||||
|
|
||||||
|
#### WishTreeActivity.java
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class WishTreeActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private ActivityWishTreeBinding binding;
|
||||||
|
private ApiService apiService;
|
||||||
|
private List<WishtreeResponse.Wish> myWishes;
|
||||||
|
private WishtreeResponse.Festival currentFestival;
|
||||||
|
|
||||||
|
// 生命周期方法
|
||||||
|
onCreate() // 初始化页面、加载数据
|
||||||
|
onResume() // 刷新数据
|
||||||
|
onStart() // 启动倒计时
|
||||||
|
onStop() // 停止倒计时
|
||||||
|
|
||||||
|
// 数据加载
|
||||||
|
loadCurrentFestival() // 加载当前节日
|
||||||
|
loadMyWishes() // 加载我的心愿
|
||||||
|
|
||||||
|
// UI更新
|
||||||
|
updateBannerText() // 更新横幅文字
|
||||||
|
updateWishCards() // 更新心愿卡片
|
||||||
|
updateWishCount() // 更新祈愿值
|
||||||
|
updateBannerTimer() // 更新倒计时
|
||||||
|
|
||||||
|
// 用户交互
|
||||||
|
showMakeWishInputDialog() // 显示许愿对话框
|
||||||
|
showViewWishDialog() // 显示查看心愿对话框
|
||||||
|
showSuccessDialog() // 显示成功提示
|
||||||
|
|
||||||
|
// API调用
|
||||||
|
publishWish() // 发布心愿
|
||||||
|
deleteWish() // 删除心愿
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### WishtreeRequest.java
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class WishtreeRequest {
|
||||||
|
|
||||||
|
// 发布心愿请求
|
||||||
|
public static class PublishWish {
|
||||||
|
public Integer festivalId;
|
||||||
|
public String content;
|
||||||
|
public Integer backgroundId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发表评论请求
|
||||||
|
public static class AddComment {
|
||||||
|
public String content;
|
||||||
|
public Long parentId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### WishtreeResponse.java
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class WishtreeResponse {
|
||||||
|
|
||||||
|
// 节日信息
|
||||||
|
public static class Festival {
|
||||||
|
public int id;
|
||||||
|
public String name;
|
||||||
|
public String icon;
|
||||||
|
public String startDate;
|
||||||
|
public String endDate;
|
||||||
|
public String themeColor;
|
||||||
|
public int status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 心愿信息
|
||||||
|
public static class Wish {
|
||||||
|
public long id;
|
||||||
|
public int uid;
|
||||||
|
public String nickname;
|
||||||
|
public String avatar;
|
||||||
|
public int festivalId;
|
||||||
|
public String festivalName;
|
||||||
|
public String festivalIcon;
|
||||||
|
public String content;
|
||||||
|
public String backgroundImage;
|
||||||
|
public int status;
|
||||||
|
public int likeCount;
|
||||||
|
public int commentCount;
|
||||||
|
public Boolean isLiked;
|
||||||
|
public String createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 背景素材
|
||||||
|
public static class Background {
|
||||||
|
public int id;
|
||||||
|
public String name;
|
||||||
|
public String image;
|
||||||
|
public int festivalId;
|
||||||
|
public int status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 评论信息
|
||||||
|
public static class Comment {
|
||||||
|
public long id;
|
||||||
|
public long wishId;
|
||||||
|
public int uid;
|
||||||
|
public String nickname;
|
||||||
|
public String avatar;
|
||||||
|
public String content;
|
||||||
|
public long parentId;
|
||||||
|
public String createTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、交互流程
|
||||||
|
|
||||||
|
### 5.1 页面加载流程
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────┐
|
||||||
|
│ onCreate │
|
||||||
|
└──────┬──────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────┐
|
||||||
|
│ 1. 初始化ApiService │
|
||||||
|
│ 2. 设置底部导航 │
|
||||||
|
│ 3. 设置心愿卡片点击事件 │
|
||||||
|
│ 4. 启动横幅倒计时 │
|
||||||
|
│ 5. 加载当前节日 (loadCurrentFestival) │
|
||||||
|
│ 6. 加载我的心愿 (loadMyWishes) │
|
||||||
|
└─────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 发布心愿流程
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||||
|
│ 点击许愿按钮 │ ──► │ 显示许愿对话框│ ──► │ 输入心愿内容 │
|
||||||
|
└─────────────┘ └─────────────┘ └──────┬──────┘
|
||||||
|
│
|
||||||
|
┌───────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||||
|
│ 点击许愿按钮 │ ──► │ 调用API发布 │ ──► │ 显示成功提示 │
|
||||||
|
└─────────────┘ └─────────────┘ └──────┬──────┘
|
||||||
|
│
|
||||||
|
┌───────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌─────────────────────┐
|
||||||
|
│ 重新加载心愿列表 │
|
||||||
|
│ 更新心愿卡片显示 │
|
||||||
|
└─────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.3 查看/删除心愿流程
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────┐ ┌─────────────┐
|
||||||
|
│ 点击心愿卡片 │ ──► │ 显示心愿详情 │
|
||||||
|
└─────────────┘ └──────┬──────┘
|
||||||
|
│
|
||||||
|
┌─────────────────┼─────────────────┐
|
||||||
|
│ │ │
|
||||||
|
▼ ▼ ▼
|
||||||
|
┌───────────┐ ┌───────────┐ ┌───────────┐
|
||||||
|
│ 点击关闭 │ │ 点击删除 │ │ 愿望达成 │
|
||||||
|
└─────┬─────┘ └─────┬─────┘ └─────┬─────┘
|
||||||
|
│ │ │
|
||||||
|
▼ ▼ ▼
|
||||||
|
┌───────────┐ ┌───────────┐ ┌───────────┐
|
||||||
|
│ 关闭对话框 │ │ 调用API删除│ │ 调用API删除│
|
||||||
|
└───────────┘ │ 刷新列表 │ │ 显示祝贺 │
|
||||||
|
└───────────┘ └───────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、状态管理
|
||||||
|
|
||||||
|
### 6.1 心愿状态
|
||||||
|
|
||||||
|
| 状态值 | 说明 | 显示 |
|
||||||
|
|--------|------|------|
|
||||||
|
| 0 | 待审核 | 审核中 |
|
||||||
|
| 1 | 已通过 | 正常显示 |
|
||||||
|
| 2 | 已拒绝 | 不显示 |
|
||||||
|
|
||||||
|
### 6.2 页面状态
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 心愿数据
|
||||||
|
private List<WishtreeResponse.Wish> myWishes = new ArrayList<>();
|
||||||
|
|
||||||
|
// 当前节日
|
||||||
|
private WishtreeResponse.Festival currentFestival;
|
||||||
|
|
||||||
|
// 状态更新时机
|
||||||
|
- onCreate: 初始加载
|
||||||
|
- onResume: 每次返回页面刷新
|
||||||
|
- 发布/删除心愿后: 重新加载
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 七、错误处理
|
||||||
|
|
||||||
|
### 7.1 网络错误
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call<...> call, Throwable t) {
|
||||||
|
Toast.makeText(context, "网络错误", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.2 业务错误
|
||||||
|
|
||||||
|
```java
|
||||||
|
if (response.body().getCode() != 200) {
|
||||||
|
String msg = response.body().getMessage();
|
||||||
|
Toast.makeText(context, msg != null ? msg : "操作失败", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.3 空数据处理
|
||||||
|
|
||||||
|
```java
|
||||||
|
if (myWishes == null) {
|
||||||
|
myWishes = new ArrayList<>();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 八、后续扩展
|
||||||
|
|
||||||
|
### 8.1 待实现功能
|
||||||
|
|
||||||
|
- [ ] 心愿列表页面(查看所有用户心愿)
|
||||||
|
- [ ] 点赞功能
|
||||||
|
- [ ] 评论功能
|
||||||
|
- [ ] 背景素材选择
|
||||||
|
- [ ] 节日主题切换
|
||||||
|
- [ ] 心愿分享功能
|
||||||
|
|
||||||
|
### 8.2 优化方向
|
||||||
|
|
||||||
|
- 添加加载状态指示器
|
||||||
|
- 添加下拉刷新
|
||||||
|
- 添加心愿卡片动画效果
|
||||||
|
- 支持心愿图片上传
|
||||||
|
- 添加本地缓存减少网络请求
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 九、数据库表结构
|
||||||
|
|
||||||
|
### 9.1 相关表
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 节日表
|
||||||
|
eb_wishtree_festival (id, name, icon, start_date, end_date, theme_color, status, ...)
|
||||||
|
|
||||||
|
-- 心愿表
|
||||||
|
eb_wishtree_wish (id, uid, festival_id, content, background_id, status, like_count, comment_count, ...)
|
||||||
|
|
||||||
|
-- 点赞表
|
||||||
|
eb_wishtree_like (id, uid, wish_id, create_time)
|
||||||
|
|
||||||
|
-- 评论表
|
||||||
|
eb_wishtree_comment (id, wish_id, uid, content, parent_id, status, ...)
|
||||||
|
|
||||||
|
-- 背景素材表
|
||||||
|
eb_wishtree_background (id, name, image, festival_id, status, ...)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 十、测试要点
|
||||||
|
|
||||||
|
### 10.1 功能测试
|
||||||
|
|
||||||
|
- [ ] 页面正常加载
|
||||||
|
- [ ] 节日信息正确显示
|
||||||
|
- [ ] 心愿卡片正确显示
|
||||||
|
- [ ] 发布心愿成功
|
||||||
|
- [ ] 删除心愿成功
|
||||||
|
- [ ] 愿望达成功能正常
|
||||||
|
- [ ] 底部导航切换正常
|
||||||
|
|
||||||
|
### 10.2 异常测试
|
||||||
|
|
||||||
|
- [ ] 网络断开时的提示
|
||||||
|
- [ ] 未登录时的处理
|
||||||
|
- [ ] 空数据时的显示
|
||||||
|
- [ ] 心愿内容为空时的校验
|
||||||
Loading…
Reference in New Issue
Block a user