From 22065758d4cd6872d63f9b8d977dddfaaf324d9a Mon Sep 17 00:00:00 2001 From: 2991692032 Date: Fri, 23 May 2025 10:42:50 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=AA=E4=BA=BA=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Front/vue-unilife/FORUM_FEATURES.md | 100 +++-- Front/vue-unilife/src/api/user.ts | 22 + Front/vue-unilife/src/router/index.ts | 4 +- Front/vue-unilife/src/views/Home.vue | 247 ++++++++--- Front/vue-unilife/src/views/MessagesView.vue | 313 +++++++++++++ Front/vue-unilife/src/views/SettingsView.vue | 410 ++++++++++++++++++ .../unilife/controller/UserController.java | 22 + .../java/com/unilife/service/UserService.java | 6 + .../unilife/service/impl/UserServiceImpl.java | 82 ++++ 9 files changed, 1108 insertions(+), 98 deletions(-) create mode 100644 Front/vue-unilife/src/views/MessagesView.vue create mode 100644 Front/vue-unilife/src/views/SettingsView.vue diff --git a/Front/vue-unilife/FORUM_FEATURES.md b/Front/vue-unilife/FORUM_FEATURES.md index 9fe3303..736a43d 100644 --- a/Front/vue-unilife/FORUM_FEATURES.md +++ b/Front/vue-unilife/FORUM_FEATURES.md @@ -18,11 +18,31 @@ - ✅ 评论时间的友好显示(几分钟前、几小时前等) - ✅ 未登录用户会提示登录 -### 3. 用户体验优化 +### 3. 个人中心系统 +- ✅ 完善了个人主页 (`/src/views/Home.vue`) + - ✅ 真实的用户统计数据API接口 + - ✅ 最近发布帖子的展示 + - ✅ 加载状态和错误处理 + - ✅ 响应式设计 +- ✅ 创建了消息中心页面 (`/src/views/MessagesView.vue`) + - ✅ 系统通知、评论回复、点赞通知分类 + - ✅ 消息已读/未读状态管理 + - ✅ 批量操作功能 +- ✅ 创建了设置页面 (`/src/views/SettingsView.vue`) + - ✅ 通知设置 + - ✅ 隐私设置 + - ✅ 界面设置 + - ✅ 账户安全设置 + - ✅ 数据管理功能 +- ✅ 完善了个人帖子管理页面 (`/src/views/forum/MyPostsView.vue`) +- ✅ 完善了账号管理页面 (`/src/views/AccountManager.vue`) + +### 4. 用户体验优化 - ✅ 添加了加载状态和错误处理 - ✅ 优化了UI布局和样式 - ✅ 支持未登录用户浏览但限制互动功能 - ✅ 完善了错误提示和成功提示 +- ✅ 响应式设计适配移动端 ## 技术实现 @@ -32,16 +52,38 @@ - `POST /comments` - 发表评论 - `DELETE /comments/{id}` - 删除评论 - `POST /comments/{id}/like` - 点赞/取消点赞评论 +- `GET /users/stats` - 获取用户统计数据 +- `GET /users/recent-posts` - 获取用户最近帖子 ### 组件结构 ``` -PostListView.vue - 帖子列表页面 +个人中心布局 (PersonalLayout.vue) +├── 个人主页 (Home.vue) +│ ├── 用户统计卡片 +│ └── 最近帖子列表 +├── 账号管理 (AccountManager.vue) +│ ├── 个人资料编辑 +│ └── 密码修改 +├── 我的帖子 (MyPostsView.vue) +│ ├── 帖子列表 +│ └── 编辑/删除操作 +├── 消息中心 (MessagesView.vue) +│ ├── 系统通知 +│ ├── 评论回复 +│ └── 点赞通知 +└── 设置 (SettingsView.vue) + ├── 通知设置 + ├── 隐私设置 + ├── 界面设置 + └── 账户安全 + +帖子列表页面 (PostListView.vue) ├── 点赞按钮 └── 帖子卡片交互 -PostDetailView.vue - 帖子详情页面 +帖子详情页面 (PostDetailView.vue) ├── 点赞按钮 -└── CommentSection.vue - 评论区组件 +└── 评论区组件 (CommentSection.vue) ├── 评论表单 ├── 评论列表 ├── 回复功能 @@ -50,33 +92,29 @@ PostDetailView.vue - 帖子详情页面 ### 状态管理 - 在PostStore中添加了`likePost`方法 -- 实现了帖子点赞状态的同步更新 -- 在评论组件中管理评论数据和状态 - -## 功能特点 - -### 点赞系统 -- 实时更新点赞数量 -- 视觉反馈(已点赞的按钮变为主色调) -- 防重复点击(loading状态) -- 同步更新列表和详情页状态 +- 在UserStore中完善了用户信息管理 +- 评论数据通过CommentSection组件本地管理 -### 评论系统 -- 支持多层嵌套回复 -- 评论和回复的独立点赞 -- 用户权限控制(只能删除自己的评论) -- 富文本内容支持 -- 实时评论数量更新 +## 数据流设计 +1. **用户统计数据**: API → UserStore → 个人主页显示 +2. **帖子点赞**: 用户点击 → PostStore处理 → API调用 → 状态更新 +3. **评论系统**: 用户操作 → CommentSection → API调用 → 界面更新 +4. **个人设置**: 用户修改 → 本地状态 → API保存 → 成功提示 -### 安全和权限 -- 所有交互功能都需要登录 -- 后端API有用户身份验证 -- 前端有相应的权限检查和提示 +## 已解决的问题 +- ✅ 个人主页假数据问题 +- ✅ 消息中心页面缺失 +- ✅ 设置页面缺失 +- ✅ 用户统计数据获取 +- ✅ 帖子交互功能不完整 +- ✅ 响应式布局适配 -## 待优化项目 -- [ ] 添加评论分页功能 -- [ ] 实现评论的编辑功能 -- [ ] 添加评论的举报功能 -- [ ] 优化评论的实时更新 -- [ ] 添加富文本编辑器支持图片等 -- [ ] 实现评论的@功能 \ No newline at end of file +## 待开发功能(建议) +- 🔲 消息系统的后端API实现 +- 🔲 设置数据的持久化存储 +- 🔲 两步验证功能 +- 🔲 会话管理功能 +- 🔲 数据导出功能 +- 🔲 实时通知推送 +- 🔲 好友系统 +- 🔲 私信功能 \ No newline at end of file diff --git a/Front/vue-unilife/src/api/user.ts b/Front/vue-unilife/src/api/user.ts index 355d8a5..5bee6cb 100644 --- a/Front/vue-unilife/src/api/user.ts +++ b/Front/vue-unilife/src/api/user.ts @@ -5,6 +5,7 @@ export interface UserInfo { id: number; username: string; email: string; + nickname?: string; avatar?: string; bio?: string; gender?: number; @@ -57,6 +58,13 @@ export interface UpdatePasswordParams { newPassword: string; } +export interface UserStats { + totalPosts: number; + totalLikes: number; + totalComments: number; + totalViews: number; +} + // 用户API export default { // 登录 @@ -114,6 +122,20 @@ export default { ); }, + // 获取用户统计数据 + getUserStats() { + return get<{code: number; data: UserStats}>( + '/users/stats' + ); + }, + + // 获取用户最近帖子 + getUserRecentPosts(limit: number = 5) { + return get<{code: number; data: {list: any[]}}>( + `/users/recent-posts?limit=${limit}` + ); + }, + // 上传头像 uploadAvatar(formData: FormData) { return post<{code: number; data: {avatarUrl: string}}>( diff --git a/Front/vue-unilife/src/router/index.ts b/Front/vue-unilife/src/router/index.ts index 7eb3348..ead0d3a 100644 --- a/Front/vue-unilife/src/router/index.ts +++ b/Front/vue-unilife/src/router/index.ts @@ -136,13 +136,13 @@ const routes: Array = [ { path: 'messages', // URL: /personal/messages name: 'Messages', - component: () => import('../views/NotFound.vue'), // 占位符 + component: () => import('../views/MessagesView.vue'), meta: { title: '消息中心 - UniLife' } }, { path: 'settings', // URL: /personal/settings name: 'Settings', - component: () => import('../views/NotFound.vue'), // 占位符 + component: () => import('../views/SettingsView.vue'), meta: { title: '设置 - UniLife' } }, // 默认重定向到个人主页 diff --git a/Front/vue-unilife/src/views/Home.vue b/Front/vue-unilife/src/views/Home.vue index c731056..6e1e503 100644 --- a/Front/vue-unilife/src/views/Home.vue +++ b/Front/vue-unilife/src/views/Home.vue @@ -1,52 +1,116 @@ @@ -54,48 +118,80 @@ onMounted(async () => {
- +
-
{{ userStats.totalPosts }}
-
发布帖子
+ + + +
- +
-
{{ userStats.totalLikes }}
-
获得点赞
+ + + +
- +
-
{{ userStats.totalComments }}
-
收到评论
+ + + +
- +
-
{{ userStats.totalViews }}
-
帖子浏览
+ + + +
@@ -104,39 +200,51 @@ onMounted(async () => {

最近发布的帖子

- + 发布新帖
-
-

你还没有发布过帖子

- -
+ -
+ + 立即发布 + + +
-

{{ post.title }}

- +

{{ post.title }}

+
-

{{ post.content }}

+

{{ post.summary || post.content?.substring(0, 100) + '...' || '暂无摘要' }}

@@ -259,6 +367,15 @@ onMounted(async () => { margin: 0; } +.post-title.clickable { + cursor: pointer; + transition: color 0.2s; +} + +.post-title.clickable:hover { + color: var(--primary-color); +} + .post-date { font-size: var(--font-size-sm); color: var(--text-light); diff --git a/Front/vue-unilife/src/views/MessagesView.vue b/Front/vue-unilife/src/views/MessagesView.vue new file mode 100644 index 0000000..0b5b247 --- /dev/null +++ b/Front/vue-unilife/src/views/MessagesView.vue @@ -0,0 +1,313 @@ + + + + + \ No newline at end of file diff --git a/Front/vue-unilife/src/views/SettingsView.vue b/Front/vue-unilife/src/views/SettingsView.vue new file mode 100644 index 0000000..94b4a56 --- /dev/null +++ b/Front/vue-unilife/src/views/SettingsView.vue @@ -0,0 +1,410 @@ + + + + + \ No newline at end of file diff --git a/unilife-server/src/main/java/com/unilife/controller/UserController.java b/unilife-server/src/main/java/com/unilife/controller/UserController.java index 4608806..e05de7f 100644 --- a/unilife-server/src/main/java/com/unilife/controller/UserController.java +++ b/unilife-server/src/main/java/com/unilife/controller/UserController.java @@ -139,4 +139,26 @@ public class UserController { } return userService.updateEmail(userId, emailDTO); } + + @Operation(summary = "获取用户统计数据") + @GetMapping("stats") + public Result getUserStats() { + // 从当前上下文获取用户ID + Long userId = BaseContext.getId(); + if (userId == null) { + return Result.error(401, "未登录"); + } + return userService.getUserStats(userId); + } + + @Operation(summary = "获取用户最近帖子") + @GetMapping("recent-posts") + public Result getUserRecentPosts(@RequestParam(value = "limit", defaultValue = "5") Integer limit) { + // 从当前上下文获取用户ID + Long userId = BaseContext.getId(); + if (userId == null) { + return Result.error(401, "未登录"); + } + return userService.getUserRecentPosts(userId, limit); + } } diff --git a/unilife-server/src/main/java/com/unilife/service/UserService.java b/unilife-server/src/main/java/com/unilife/service/UserService.java index d211512..32fcb90 100644 --- a/unilife-server/src/main/java/com/unilife/service/UserService.java +++ b/unilife-server/src/main/java/com/unilife/service/UserService.java @@ -30,4 +30,10 @@ public interface UserService { Result updateAvatar(Long userId, MultipartFile file); Result updateEmail(Long userId, UpdateEmailDTO emailDTO); + + // 用户统计数据 + Result getUserStats(Long userId); + + // 用户最近帖子 + Result getUserRecentPosts(Long userId, Integer limit); } diff --git a/unilife-server/src/main/java/com/unilife/service/impl/UserServiceImpl.java b/unilife-server/src/main/java/com/unilife/service/impl/UserServiceImpl.java index 49fbb52..8b06523 100644 --- a/unilife-server/src/main/java/com/unilife/service/impl/UserServiceImpl.java +++ b/unilife-server/src/main/java/com/unilife/service/impl/UserServiceImpl.java @@ -5,6 +5,9 @@ import cn.hutool.core.util.RandomUtil; import com.unilife.common.constant.RedisConstant; import com.unilife.common.result.Result; import com.unilife.mapper.UserMapper; +import com.unilife.mapper.PostMapper; +import com.unilife.mapper.CommentMapper; +import com.unilife.mapper.PostLikeMapper; import com.unilife.model.dto.LoginDTO; import com.unilife.model.dto.LoginEmailDTO; import com.unilife.model.dto.RegisterDTO; @@ -12,6 +15,7 @@ import com.unilife.model.dto.UpdateEmailDTO; import com.unilife.model.dto.UpdatePasswordDTO; import com.unilife.model.dto.UpdateProfileDTO; import com.unilife.model.entity.User; +import com.unilife.model.entity.Post; import com.unilife.model.vo.LoginVO; import com.unilife.model.vo.RegisterVO; import com.unilife.service.IPLocationService; @@ -37,6 +41,7 @@ import java.time.Duration; import java.time.LocalDateTime; import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Map; import static com.unilife.common.constant.RedisConstant.LOGIN_EMAIL_KEY; @@ -52,6 +57,15 @@ public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; + @Autowired + private PostMapper postMapper; + + @Autowired + private CommentMapper commentMapper; + + @Autowired + private PostLikeMapper postLikeMapper; + @Autowired private JavaMailSender mailSender; @@ -489,4 +503,72 @@ public class UserServiceImpl implements UserService { return Result.success(null, "邮箱更新成功"); } + + @Override + public Result getUserStats(Long userId) { + // 检查用户是否存在 + User user = userMapper.getUserById(userId); + if (user == null) { + return Result.error(404, "用户不存在"); + } + + // 获取用户统计数据 + Integer totalPosts = postMapper.getCountByUserId(userId); + + // 获取用户所有帖子的总点赞数 + List userPosts = postMapper.getListByUserId(userId, "latest"); + Integer totalLikes = userPosts.stream() + .mapToInt(post -> post.getLikeCount() != null ? post.getLikeCount() : 0) + .sum(); + + // 获取用户所有帖子的总评论数 + Integer totalComments = userPosts.stream() + .mapToInt(post -> post.getCommentCount() != null ? post.getCommentCount() : 0) + .sum(); + + // 获取用户所有帖子的总浏览数 + Integer totalViews = userPosts.stream() + .mapToInt(post -> post.getViewCount() != null ? post.getViewCount() : 0) + .sum(); + + // 构建统计数据 + Map stats = new HashMap<>(); + stats.put("totalPosts", totalPosts != null ? totalPosts : 0); + stats.put("totalLikes", totalLikes); + stats.put("totalComments", totalComments); + stats.put("totalViews", totalViews); + + return Result.success(stats); + } + + @Override + public Result getUserRecentPosts(Long userId, Integer limit) { + // 检查用户是否存在 + User user = userMapper.getUserById(userId); + if (user == null) { + return Result.error(404, "用户不存在"); + } + + // 参数校验 + if (limit == null || limit <= 0) { + limit = 5; + } + if (limit > 20) { + limit = 20; // 限制最大数量 + } + + // 获取用户最近的帖子 + List recentPosts = postMapper.getListByUserId(userId, "latest"); + + // 限制数量 + if (recentPosts.size() > limit) { + recentPosts = recentPosts.subList(0, limit); + } + + // 返回结果 + Map data = new HashMap<>(); + data.put("list", recentPosts); + + return Result.success(data); + } }