diff --git a/UniLife接口文档.md b/UniLife接口文档.md index ab25376..cf6edac 100644 --- a/UniLife接口文档.md +++ b/UniLife接口文档.md @@ -1,5 +1,25 @@ # UniLife接口文档 +## 更新日志 + +### v1.2.0 (2025-01-27) +- **修复资源点赞功能**: 实现了完整的资源点赞表 (`resource_likes`),防止重复点赞 +- **优化响应数据结构**: 统一时间格式为ISO 8601格式 (`yyyy-MM-ddTHH:mm:ss`) +- **完善isLiked字段**: 资源列表和详情接口中的 `isLiked` 字段现在基于当前用户的真实点赞状态 +- **增强文件存储**: 文件存储从本地上传改为阿里云OSS,支持临时访问链接 +- **添加数据库设计说明**: 详细说明了资源表和点赞表的设计 +- **修正API参数说明**: 更新了资源列表接口中 `user` 参数的含义,改为 `uploaderUserId` + +### v1.1.0 (2025-01-26) +- 完成论坛功能模块的前后端集成 +- 实现帖子、评论、点赞等核心功能 +- 添加用户认证和权限管理 + +### v1.0.0 (2025-01-25) +- 初始版本发布 +- 实现基础的用户认证功能 +- 定义核心数据结构和API接口 + ## 目录 - [1. 基础信息](#1-基础信息) - [2. 用户认证模块](#2-用户认证模块) @@ -328,7 +348,7 @@ "title": "最新帖子标题", "content": "帖子内容摘要...", "categoryId": 1, - "createTime": "2024-01-15T10:30:00", + "createdAt": "2023-05-01T12:00:00", "viewsCount": 50, "likesCount": 10, "commentsCount": 5 @@ -373,7 +393,7 @@ "viewCount": 100, "likeCount": 20, "commentCount": 5, - "createdAt": "2023-05-01 12:00:00" + "createdAt": "2023-05-01T12:00:00" } ], "pages": 10 @@ -404,8 +424,8 @@ "likeCount": 20, "commentCount": 5, "isLiked": true, - "createdAt": "2023-05-01 12:00:00", - "updatedAt": "2023-05-01 12:00:00" + "createdAt": "2023-05-01T12:00:00", + "updatedAt": "2023-05-01T12:00:00" } } ``` @@ -521,7 +541,7 @@ "viewCount": 100, "likeCount": 20, "commentCount": 5, - "createdAt": "2023-05-01 12:00:00" + "createdAt": "2023-05-01T12:00:00" } ], "pages": 3 @@ -552,7 +572,7 @@ "avatar": "https://example.com/avatar.jpg", "likeCount": 5, "isLiked": false, - "createdAt": "2023-05-01 12:30:00", + "createdAt": "2023-05-01T12:30:00", "replies": [ { "id": 2, @@ -562,7 +582,7 @@ "avatar": "https://example.com/avatar2.jpg", "likeCount": 2, "isLiked": true, - "createdAt": "2023-05-01 12:35:00" + "createdAt": "2023-05-01T12:35:00" } ] } @@ -652,8 +672,8 @@ "icon": "icon-study", "sort": 1, "status": 1, - "createdAt": "2023-05-01 12:00:00", - "updatedAt": "2023-05-01 12:00:00" + "createdAt": "2023-05-01T12:00:00", + "updatedAt": "2023-05-01T12:00:00" } ] } @@ -677,8 +697,8 @@ "icon": "icon-study", "sort": 1, "status": 1, - "createdAt": "2023-05-01 12:00:00", - "updatedAt": "2023-05-01 12:00:00" + "createdAt": "2023-05-01T12:00:00", + "updatedAt": "2023-05-01T12:00:00" } } ``` @@ -792,7 +812,7 @@ "id": 1, "title": "资源标题", "description": "资源描述", - "fileUrl": "uploads/resources/file.pdf", + "fileUrl": "https://oss-example.aliyuncs.com/resources/file.pdf", "fileSize": 1024000, "fileType": "application/pdf", "userId": 12345, @@ -803,12 +823,16 @@ "downloadCount": 10, "likeCount": 5, "isLiked": false, - "createdAt": "2023-05-01 12:00:00", - "updatedAt": "2023-05-01 12:00:00" + "createdAt": "2023-05-01T12:00:00", + "updatedAt": "2023-05-01T12:00:00" } } ``` +**说明**: +- `isLiked` 字段表示当前登录用户是否已点赞该资源,未登录用户该字段为false +- `fileUrl` 为阿里云OSS存储的文件访问URL + ### 5.3 获取资源列表 - **URL**: `/resources` - **方法**: GET @@ -816,7 +840,7 @@ 请求参数: - **category** (query, 可选): 分类ID -- **user** (query, 可选): 用户ID +- **user** (query, 可选): 上传者用户ID(用于筛选特定用户上传的资源) - **keyword** (query, 可选): 搜索关键词 - **page** (query, 可选): 页码,默认为1 - **size** (query, 可选): 每页大小,默认为10 @@ -833,7 +857,7 @@ "id": 1, "title": "资源标题", "description": "资源描述", - "fileUrl": "uploads/resources/file.pdf", + "fileUrl": "https://oss-example.aliyuncs.com/resources/file.pdf", "fileSize": 1024000, "fileType": "application/pdf", "userId": 12345, @@ -844,8 +868,8 @@ "downloadCount": 10, "likeCount": 5, "isLiked": false, - "createdAt": "2023-05-01 12:00:00", - "updatedAt": "2023-05-01 12:00:00" + "createdAt": "2023-05-01T12:00:00", + "updatedAt": "2023-05-01T12:00:00" } ], "pages": 10 @@ -853,6 +877,10 @@ } ``` +**说明**: +- `isLiked` 字段基于当前登录用户的点赞状态,通过查询`resource_likes`表获得 +- 未登录用户获取列表时,所有资源的`isLiked`字段均为false + ### 5.4 更新资源 - **URL**: `/resources/{id}` - **方法**: PUT @@ -892,6 +920,8 @@ } ``` +**说明**: 删除资源时会同时删除阿里云OSS中的文件和数据库中的相关记录(包括点赞记录) + ### 5.6 下载资源 - **URL**: `/resources/{id}/download` - **方法**: GET @@ -903,13 +933,17 @@ "code": 200, "message": "获取下载链接成功", "data": { - "fileUrl": "uploads/resources/file.pdf", - "fileName": "资源标题", + "fileUrl": "https://oss-example.aliyuncs.com/resources/file.pdf?Expires=1684737600&OSSAccessKeyId=xxx&Signature=xxx", + "fileName": "资源标题.pdf", "fileType": "application/pdf" } } ``` +**说明**: +- `fileUrl` 为生成的临时访问URL,有效期为1小时 +- 每次下载会增加资源的下载计数 + ### 5.7 点赞/取消点赞资源 - **URL**: `/resources/{id}/like` - **方法**: POST @@ -925,6 +959,21 @@ } ``` +或 + +```json +{ + "code": 200, + "message": "取消点赞成功", + "data": null +} +``` + +**实现说明**: +- 使用`resource_likes`表记录用户点赞状态,确保一个用户对同一资源只能点赞一次 +- 点赞时会增加资源的`like_count`字段,取消点赞时会减少 +- 通过唯一键约束防止重复点赞:`UNIQUE KEY uk_user_resource (user_id, resource_id)` + ### 5.8 获取用户上传的资源列表 - **URL**: `/resources/user/{userId}` - **方法**: GET @@ -946,7 +995,7 @@ "id": 1, "title": "资源标题", "description": "资源描述", - "fileUrl": "uploads/resources/file.pdf", + "fileUrl": "https://oss-example.aliyuncs.com/resources/file.pdf", "fileSize": 1024000, "fileType": "application/pdf", "userId": 12345, @@ -957,8 +1006,8 @@ "downloadCount": 10, "likeCount": 5, "isLiked": false, - "createdAt": "2023-05-01 12:00:00", - "updatedAt": "2023-05-01 12:00:00" + "createdAt": "2023-05-01T12:00:00", + "updatedAt": "2023-05-01T12:00:00" } ], "pages": 2 @@ -988,7 +1037,7 @@ "id": 1, "title": "资源标题", "description": "资源描述", - "fileUrl": "uploads/resources/file.pdf", + "fileUrl": "https://oss-example.aliyuncs.com/resources/file.pdf", "fileSize": 1024000, "fileType": "application/pdf", "userId": 12345, @@ -999,8 +1048,8 @@ "downloadCount": 10, "likeCount": 5, "isLiked": false, - "createdAt": "2023-05-01 12:00:00", - "updatedAt": "2023-05-01 12:00:00" + "createdAt": "2023-05-01T12:00:00", + "updatedAt": "2023-05-01T12:00:00" } ], "pages": 2 @@ -1008,6 +1057,44 @@ } ``` +### 5.10 数据库设计说明 + +#### 资源表 (resources) +```sql +CREATE TABLE `resources` ( + `id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '资源ID', + `user_id` BIGINT NOT NULL COMMENT '上传用户ID', + `title` VARCHAR(100) NOT NULL COMMENT '资源标题', + `description` TEXT DEFAULT NULL COMMENT '资源描述', + `file_url` VARCHAR(255) NOT NULL COMMENT '文件URL(OSS)', + `file_size` BIGINT NOT NULL COMMENT '文件大小(字节)', + `file_type` VARCHAR(50) NOT NULL COMMENT '文件类型', + `category_id` BIGINT NOT NULL COMMENT '分类ID', + `download_count` INT DEFAULT 0 COMMENT '下载次数', + `like_count` INT DEFAULT 0 COMMENT '点赞次数', + `status` TINYINT DEFAULT 1 COMMENT '状态(0-删除, 1-正常)', + `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间' +); +``` + +#### 资源点赞表 (resource_likes) +```sql +CREATE TABLE `resource_likes` ( + `id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '点赞ID', + `user_id` BIGINT NOT NULL COMMENT '用户ID', + `resource_id` BIGINT NOT NULL COMMENT '资源ID', + `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + UNIQUE KEY `uk_user_resource` (`user_id`, `resource_id`), + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + FOREIGN KEY (`resource_id`) REFERENCES `resources` (`id`) ON DELETE CASCADE +); +``` + +**特性**: +- 通过唯一键约束确保用户不能重复点赞同一资源 +- 级联删除保证数据一致性 + ## 6. 课程表与日程管理模块 ### 6.1 课程管理 @@ -1067,10 +1154,11 @@ "endTime": "09:40:00", "startWeek": 1, "endWeek": 16, + "semester": "2023-1", "color": "#4CAF50", "status": 1, - "createdAt": "2023-05-01 12:00:00", - "updatedAt": "2023-05-01 12:00:00" + "createdAt": "2023-05-01T12:00:00", + "updatedAt": "2023-05-01T12:00:00" } } ``` @@ -1100,10 +1188,11 @@ "endTime": "09:40:00", "startWeek": 1, "endWeek": 16, + "semester": "2023-1", "color": "#4CAF50", "status": 1, - "createdAt": "2023-05-01 12:00:00", - "updatedAt": "2023-05-01 12:00:00" + "createdAt": "2023-05-01T12:00:00", + "updatedAt": "2023-05-01T12:00:00" } ] } @@ -1135,10 +1224,11 @@ "endTime": "09:40:00", "startWeek": 1, "endWeek": 16, + "semester": "2023-1", "color": "#4CAF50", "status": 1, - "createdAt": "2023-05-01 12:00:00", - "updatedAt": "2023-05-01 12:00:00" + "createdAt": "2023-05-01T12:00:00", + "updatedAt": "2023-05-01T12:00:00" } ] } @@ -1173,8 +1263,8 @@ "semester": "2023-1", "color": "#4CAF50", "status": 1, - "createdAt": "2023-05-01 12:00:00", - "updatedAt": "2023-05-01 12:00:00" + "createdAt": "2023-05-01T12:00:00", + "updatedAt": "2023-05-01T12:00:00" } ] } @@ -1198,6 +1288,7 @@ "endTime": "11:40:00", "startWeek": 1, "endWeek": 16, + "semester": "2023-1", "color": "#2196F3" } ``` @@ -1306,8 +1397,8 @@ "reminder": 30, "color": "#FF5722", "status": 1, - "createdAt": "2023-05-01 12:00:00", - "updatedAt": "2023-05-01 12:00:00" + "createdAt": "2023-05-01T12:00:00", + "updatedAt": "2023-05-01T12:00:00" } } ``` @@ -1338,8 +1429,8 @@ "reminder": 30, "color": "#FF5722", "status": 1, - "createdAt": "2023-05-01 12:00:00", - "updatedAt": "2023-05-01 12:00:00" + "createdAt": "2023-05-01T12:00:00", + "updatedAt": "2023-05-01T12:00:00" } ] } @@ -1376,8 +1467,8 @@ "reminder": 30, "color": "#FF5722", "status": 1, - "createdAt": "2023-05-01 12:00:00", - "updatedAt": "2023-05-01 12:00:00" + "createdAt": "2023-05-01T12:00:00", + "updatedAt": "2023-05-01T12:00:00" } ] } @@ -1468,6 +1559,54 @@ } ``` +### 6.3 数据库设计说明 + +#### 课程表 (courses) +```sql +CREATE TABLE `courses` ( + `id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '课程ID', + `user_id` BIGINT NOT NULL COMMENT '用户ID', + `name` VARCHAR(100) NOT NULL COMMENT '课程名称', + `teacher` VARCHAR(50) DEFAULT NULL COMMENT '教师姓名', + `location` VARCHAR(100) DEFAULT NULL COMMENT '上课地点', + `day_of_week` TINYINT NOT NULL COMMENT '星期几(1-7)', + `start_time` TIME NOT NULL COMMENT '开始时间', + `end_time` TIME NOT NULL COMMENT '结束时间', + `start_week` TINYINT NOT NULL COMMENT '开始周次', + `end_week` TINYINT NOT NULL COMMENT '结束周次', + `semester` VARCHAR(20) DEFAULT NULL COMMENT '学期(如:2023-1)', + `color` VARCHAR(20) DEFAULT NULL COMMENT '显示颜色', + `status` TINYINT DEFAULT 1 COMMENT '状态(0-删除, 1-正常)', + `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间' +); +``` + +#### 日程表 (schedules) +```sql +CREATE TABLE `schedules` ( + `id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '日程ID', + `user_id` BIGINT NOT NULL COMMENT '用户ID', + `title` VARCHAR(100) NOT NULL COMMENT '日程标题', + `description` TEXT DEFAULT NULL COMMENT '日程描述', + `start_time` DATETIME NOT NULL COMMENT '开始时间', + `end_time` DATETIME NOT NULL COMMENT '结束时间', + `location` VARCHAR(100) DEFAULT NULL COMMENT '地点', + `is_all_day` TINYINT DEFAULT 0 COMMENT '是否全天(0-否, 1-是)', + `reminder` TINYINT DEFAULT NULL COMMENT '提醒时间(分钟)', + `color` VARCHAR(20) DEFAULT NULL COMMENT '显示颜色', + `status` TINYINT DEFAULT 1 COMMENT '状态(0-删除, 1-正常)', + `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间' +); +``` + +**特性**: +- 支持学期管理和多学期课程数据 +- 支持时间冲突检测 +- 支持课程和日程的颜色标记 +- 支持日程提醒功能 + ## 7. 待实现模块 以下模块尚未实现,将在后续开发中完成: diff --git a/unilife-frontend/src/router/index.ts b/unilife-frontend/src/router/index.ts index 07b3f10..7eac47f 100644 --- a/unilife-frontend/src/router/index.ts +++ b/unilife-frontend/src/router/index.ts @@ -37,6 +37,12 @@ const router = createRouter({ component: () => import('@/views/resources/ResourcesView.vue'), meta: { requiresAuth: true } }, + { + path: '/resources/:id', + name: 'resource-detail', + component: () => import('@/views/resources/ResourceDetailView.vue'), + meta: { requiresAuth: true } + }, { path: '/schedule', name: 'schedule', diff --git a/unilife-frontend/src/views/resources/ResourceDetailView.vue b/unilife-frontend/src/views/resources/ResourceDetailView.vue new file mode 100644 index 0000000..9282bd5 --- /dev/null +++ b/unilife-frontend/src/views/resources/ResourceDetailView.vue @@ -0,0 +1,659 @@ + + + + + \ No newline at end of file diff --git a/unilife-frontend/src/views/resources/ResourcesView.vue b/unilife-frontend/src/views/resources/ResourcesView.vue index 4f8eb7d..72fbb9f 100644 --- a/unilife-frontend/src/views/resources/ResourcesView.vue +++ b/unilife-frontend/src/views/resources/ResourcesView.vue @@ -62,6 +62,17 @@

资源分类

+
+ + + + 全部资源 + {{ totalResources }} +
{{ category.name }} - {{ category.count }} + {{ category.count || 0 }}
@@ -108,19 +119,20 @@ placeholder="搜索资源标题或描述..." size="large" class="search-input" - @keyup.enter="searchResources()" + @keyup.enter="loadResources" + clearable >
- + @@ -133,71 +145,93 @@
- -
-
-
-
- - - -
- -
-

{{ resource.title }}

-

{{ resource.description }}

+ +
+ +
+
+
+
+ + + +
+ +
+

{{ resource.title }}

+

{{ resource.description }}

+ +
+ {{ formatFileSize(resource.fileSize) }} + {{ getFileTypeLabel(resource.fileType) }} + {{ formatDate(resource.createdAt) }} +
+
-
- {{ formatFileSize(resource.fileSize) }} - {{ getFileTypeLabel(resource.fileType) }} - {{ resource.createdAt }} +
+ + + 下载 + + + + {{ resource.likeCount }} +
- -
- - - 下载 - - - - {{ resource.likeCount }} - + +
- - -
- - - 上传第一个资源 - - + +
+
@@ -211,35 +245,58 @@ width="600px" class="upload-dialog" > - - - + + + - - - - - - + + + - - + + - +
- 将文件拖到此处,或点击上传 + 将文件拖到此处,或点击选择文件
@@ -651,6 +934,7 @@ onMounted(() => { .resource-card { padding: 24px; transition: var(--transition-base); + cursor: pointer; } .resource-card:hover { @@ -745,6 +1029,20 @@ onMounted(() => { color: var(--gray-500); } +/* 分页 */ +.pagination-container { + display: flex; + justify-content: center; + padding: 32px 0; +} + +/* 表单行样式 */ +.form-row { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 16px; +} + .empty-state { text-align: center; padding: 60px 20px; diff --git a/unilife-frontend/src/views/schedule/ScheduleView.vue b/unilife-frontend/src/views/schedule/ScheduleView.vue index 8a59e48..6f39da7 100644 --- a/unilife-frontend/src/views/schedule/ScheduleView.vue +++ b/unilife-frontend/src/views/schedule/ScheduleView.vue @@ -65,8 +65,9 @@
- + + @@ -84,7 +85,7 @@
-
+
时间
-
{{ timeSlot.period }}
-
{{ timeSlot.time }}
+
第{{ timeIndex + 1 }}节
+
{{ timeSlot }}
{{ getCourseForSlot(timeIndex, dayIndex)?.name }}
{{ getCourseForSlot(timeIndex, dayIndex)?.location }}
@@ -149,6 +150,10 @@
{{ course.name }}
{{ course.location }} · {{ course.teacher }}
+
+ 编辑 + 删除 +
@@ -164,11 +169,15 @@ :key="schedule.id" class="schedule-card" > -
{{ schedule.startTime }}
+
{{ formatTime(schedule.startTime) }}
{{ schedule.title }}
{{ schedule.location }}
+
+ 编辑 + 删除 +
@@ -182,229 +191,358 @@ - - - + + + - - + + - - + + -
- - - - - - - - - +
+ + + - + + + +
+ +
+ - + + +
+ +
+ + + + + +
- - - + + + - - + - - + + - - + + + +
+ + + + + + + +
+ +
+ + + +
+ +
+ + + + + + + + + + + + + + +
@@ -853,4 +1286,11 @@ onMounted(() => { font-size: 9px; } } + +/* 表单行样式 */ +.form-row { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 16px; +} \ No newline at end of file diff --git a/unilife-server/src/main/java/com/unilife/controller/ResourceController.java b/unilife-server/src/main/java/com/unilife/controller/ResourceController.java index e09e95c..881abe6 100644 --- a/unilife-server/src/main/java/com/unilife/controller/ResourceController.java +++ b/unilife-server/src/main/java/com/unilife/controller/ResourceController.java @@ -53,11 +53,13 @@ public class ResourceController { @GetMapping public Result getResourceList( @RequestParam(value = "category", required = false) Long categoryId, - @RequestParam(value = "user", required = false) Long userId, + @RequestParam(value = "user", required = false) Long uploaderUserId, @RequestParam(value = "keyword", required = false) String keyword, @RequestParam(value = "page", defaultValue = "1") Integer page, @RequestParam(value = "size", defaultValue = "10") Integer size) { - return resourceService.getResourceList(categoryId, userId, keyword, page, size); + // 从当前上下文获取用户ID,可能为null(未登录用户) + Long currentUserId = BaseContext.getId(); + return resourceService.getResourceList(categoryId, uploaderUserId, keyword, page, size, currentUserId); } @Operation(summary = "更新资源") diff --git a/unilife-server/src/main/java/com/unilife/mapper/ResourceLikeMapper.java b/unilife-server/src/main/java/com/unilife/mapper/ResourceLikeMapper.java new file mode 100644 index 0000000..5aafaf9 --- /dev/null +++ b/unilife-server/src/main/java/com/unilife/mapper/ResourceLikeMapper.java @@ -0,0 +1,39 @@ +package com.unilife.mapper; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * 资源点赞数据访问层 + */ +@Mapper +public interface ResourceLikeMapper { + /** + * 检查用户是否已点赞资源 + * @param resourceId 资源ID + * @param userId 用户ID + * @return 是否已点赞 + */ + boolean isLiked(@Param("resourceId") Long resourceId, @Param("userId") Long userId); + + /** + * 添加点赞记录 + * @param resourceId 资源ID + * @param userId 用户ID + */ + void insert(@Param("resourceId") Long resourceId, @Param("userId") Long userId); + + /** + * 删除点赞记录 + * @param resourceId 资源ID + * @param userId 用户ID + */ + void delete(@Param("resourceId") Long resourceId, @Param("userId") Long userId); + + /** + * 获取资源的点赞用户数量 + * @param resourceId 资源ID + * @return 点赞数量 + */ + int getLikeCount(@Param("resourceId") Long resourceId); +} \ No newline at end of file diff --git a/unilife-server/src/main/java/com/unilife/service/ResourceService.java b/unilife-server/src/main/java/com/unilife/service/ResourceService.java index 15c9910..8139b79 100644 --- a/unilife-server/src/main/java/com/unilife/service/ResourceService.java +++ b/unilife-server/src/main/java/com/unilife/service/ResourceService.java @@ -28,13 +28,14 @@ public interface ResourceService { /** * 获取资源列表 * @param categoryId 分类ID,可为null - * @param userId 用户ID,可为null + * @param uploaderUserId 上传者用户ID,可为null(用于筛选特定用户上传的资源) * @param keyword 关键词,可为null * @param page 页码 * @param size 每页大小 + * @param currentUserId 当前登录用户ID,可为null(用于查询点赞状态) * @return 结果 */ - Result getResourceList(Long categoryId, Long userId, String keyword, Integer page, Integer size); + Result getResourceList(Long categoryId, Long uploaderUserId, String keyword, Integer page, Integer size, Long currentUserId); /** * 更新资源 diff --git a/unilife-server/src/main/java/com/unilife/service/impl/ResourceServiceImpl.java b/unilife-server/src/main/java/com/unilife/service/impl/ResourceServiceImpl.java index 18033e1..7af6634 100644 --- a/unilife-server/src/main/java/com/unilife/service/impl/ResourceServiceImpl.java +++ b/unilife-server/src/main/java/com/unilife/service/impl/ResourceServiceImpl.java @@ -5,6 +5,7 @@ import com.github.pagehelper.PageInfo; import com.unilife.common.result.Result; import com.unilife.mapper.CategoryMapper; import com.unilife.mapper.ResourceMapper; +import com.unilife.mapper.ResourceLikeMapper; import com.unilife.mapper.UserMapper; import com.unilife.model.dto.CreateResourceDTO; import com.unilife.model.entity.Category; @@ -47,6 +48,9 @@ public class ResourceServiceImpl implements ResourceService { @Autowired private OssService ossService; + @Autowired + private ResourceLikeMapper resourceLikeMapper; + // 文件存储路径,实际项目中应该配置在application.yml中 private static final String UPLOAD_DIR = "uploads/resources/"; // OSS存储目录 @@ -131,7 +135,7 @@ public class ResourceServiceImpl implements ResourceService { .categoryName(category != null ? category.getName() : "未知分类") .downloadCount(resource.getDownloadCount()) .likeCount(resource.getLikeCount()) - .isLiked(false) // 实际项目中应该查询用户是否点赞 + .isLiked(userId != null ? resourceLikeMapper.isLiked(resourceId, userId) : false) // 查询用户是否点赞 .createdAt(resource.getCreatedAt()) .updatedAt(resource.getUpdatedAt()) .build(); @@ -140,14 +144,14 @@ public class ResourceServiceImpl implements ResourceService { } @Override - public Result getResourceList(Long categoryId, Long userId, String keyword, Integer page, Integer size) { + public Result getResourceList(Long categoryId, Long uploaderUserId, String keyword, Integer page, Integer size, Long currentUserId) { // 参数校验 if (page == null || page < 1) page = 1; if (size == null || size < 1 || size > 50) size = 10; // 分页查询 PageHelper.startPage(page, size); - List resources = resourceMapper.getList(categoryId, userId, keyword); + List resources = resourceMapper.getList(categoryId, uploaderUserId, keyword); PageInfo pageInfo = new PageInfo<>(resources); // 转换为VO @@ -170,7 +174,7 @@ public class ResourceServiceImpl implements ResourceService { .categoryName(category != null ? category.getName() : "未知分类") .downloadCount(resource.getDownloadCount()) .likeCount(resource.getLikeCount()) - .isLiked(false) // 实际项目中应该查询用户是否点赞 + .isLiked(currentUserId != null ? resourceLikeMapper.isLiked(resource.getId(), currentUserId) : false) // 查询当前用户是否点赞 .createdAt(resource.getCreatedAt()) .updatedAt(resource.getUpdatedAt()) .build(); @@ -298,17 +302,16 @@ public class ResourceServiceImpl implements ResourceService { } // 检查用户是否已点赞 - // 注意:这里需要创建一个资源点赞表和相应的Mapper,实际开发中需要先创建 - boolean isLiked = false; // resourceLikeMapper.isLiked(resourceId, userId); + boolean isLiked = resourceLikeMapper.isLiked(resourceId, userId); if (isLiked) { // 取消点赞 - // resourceLikeMapper.delete(resourceId, userId); + resourceLikeMapper.delete(resourceId, userId); resourceMapper.decrementLikeCount(resourceId); return Result.success(null, "取消点赞成功"); } else { // 添加点赞 - // resourceLikeMapper.insert(resourceId, userId); + resourceLikeMapper.insert(resourceId, userId); resourceMapper.incrementLikeCount(resourceId); return Result.success(null, "点赞成功"); } diff --git a/unilife-server/src/main/resources/db/init.sql b/unilife-server/src/main/resources/db/init.sql index 4f96b17..ea19796 100644 --- a/unilife-server/src/main/resources/db/init.sql +++ b/unilife-server/src/main/resources/db/init.sql @@ -112,6 +112,17 @@ CREATE TABLE IF NOT EXISTS `comment_likes` ( FOREIGN KEY (`comment_id`) REFERENCES `comments` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='评论点赞表'; +-- 点赞表(用户-资源) +CREATE TABLE IF NOT EXISTS `resource_likes` ( + `id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '点赞ID', + `user_id` BIGINT NOT NULL COMMENT '用户ID', + `resource_id` BIGINT NOT NULL COMMENT '资源ID', + `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + UNIQUE KEY `uk_user_resource` (`user_id`, `resource_id`), + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + FOREIGN KEY (`resource_id`) REFERENCES `resources` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='资源点赞表'; + -- 资源表 CREATE TABLE IF NOT EXISTS `resources` ( `id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '资源ID', @@ -188,6 +199,3 @@ INSERT INTO `categories` (`name`, `description`, `icon`, `sort`, `status`) VALUE INSERT INTO `users` (`username`, `email`, `password`, `nickname`, `role`, `status`, `is_verified`) VALUES ('admin', 'admin@unilife.com', '123456', '系统管理员', 2, 1, 1); --- 数据库迁移:为现有courses表添加semester字段 -ALTER TABLE `courses` ADD COLUMN `semester` VARCHAR(20) DEFAULT NULL COMMENT '学期(如:2023-1)' AFTER `end_week`; -ALTER TABLE `courses` ADD INDEX `idx_semester` (`semester`); \ No newline at end of file diff --git a/unilife-server/src/main/resources/db/test-data.sql b/unilife-server/src/main/resources/db/test-data.sql new file mode 100644 index 0000000..eda2dbf --- /dev/null +++ b/unilife-server/src/main/resources/db/test-data.sql @@ -0,0 +1,36 @@ +-- 测试数据插入脚本 +-- 使用数据库 +USE UniLife; + +-- 插入测试用户数据 +INSERT INTO `users` (`username`, `email`, `password`, `nickname`, `role`, `status`, `is_verified`, `student_id`, `department`, `major`, `grade`) VALUES +('testuser1', 'test1@student.edu.cn', '123456', '张小明', 0, 1, 1, '2021001001', '计算机科学与技术学院', '计算机科学与技术', '2021'), +('testuser2', 'test2@student.edu.cn', '123456', '李小红', 0, 1, 1, '2021001002', '数学与统计学院', '数学与应用数学', '2021'), +('testuser3', 'test3@student.edu.cn', '123456', '王小刚', 0, 1, 1, '2021001003', '物理学院', '物理学', '2021'); + +-- 插入测试资源数据 +INSERT INTO `resources` (`user_id`, `title`, `description`, `file_url`, `file_size`, `file_type`, `category_id`, `download_count`, `like_count`, `status`) VALUES +(2, '数据结构课程设计报告', '包含完整的数据结构课程设计实验报告,涵盖栈、队列、树、图等数据结构的实现和应用。', 'https://example.com/files/data-structure-report.pdf', 2048576, 'application/pdf', 1, 15, 8, 1), +(2, '算法导论学习笔记', '详细的算法导论学习笔记,包含排序算法、图算法、动态规划等重要算法的分析和实现。', 'https://example.com/files/algorithm-notes.docx', 1572864, 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 1, 25, 12, 1), +(3, '高等数学期末复习资料', '高等数学期末考试复习资料合集,包含重要公式、定理证明和典型习题解答。', 'https://example.com/files/calculus-review.pdf', 3145728, 'application/pdf', 1, 32, 18, 1), +(3, '线性代数PPT课件', '线性代数完整PPT课件,包含矩阵运算、向量空间、特征值等核心内容。', 'https://example.com/files/linear-algebra.pptx', 5242880, 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 1, 20, 15, 1), +(4, '校园生活指南', '新生校园生活指南,包含宿舍管理、食堂介绍、图书馆使用等实用信息。', 'https://example.com/files/campus-guide.pdf', 1048576, 'application/pdf', 2, 45, 28, 1), +(2, '计算机网络实验代码', '计算机网络课程实验代码合集,包含Socket编程、HTTP协议实现等。', 'https://example.com/files/network-lab-code.zip', 4194304, 'application/zip', 5, 18, 10, 1); + +-- 插入测试课程数据 +INSERT INTO `courses` (`user_id`, `name`, `teacher`, `location`, `day_of_week`, `start_time`, `end_time`, `start_week`, `end_week`, `semester`, `color`, `status`) VALUES +(2, '数据结构', '张教授', '教学楼A201', 1, '08:00:00', '09:40:00', 1, 16, '2024-1', '#409EFF', 1), +(2, '算法设计与分析', '李老师', '教学楼B301', 3, '10:00:00', '11:40:00', 1, 16, '2024-1', '#67C23A', 1), +(2, '计算机网络', '王教授', '实验楼C102', 5, '14:00:00', '15:40:00', 1, 16, '2024-1', '#E6A23C', 1), +(3, '高等数学', '赵老师', '教学楼A101', 2, '08:00:00', '09:40:00', 1, 16, '2024-1', '#F56C6C', 1), +(3, '线性代数', '钱教授', '教学楼A203', 4, '10:00:00', '11:40:00', 1, 16, '2024-1', '#909399', 1), +(4, '大学物理', '孙老师', '物理楼101', 1, '14:00:00', '15:40:00', 1, 16, '2024-1', '#9B59B6', 1), +(4, '物理实验', '周教授', '物理实验室', 3, '16:00:00', '17:40:00', 1, 16, '2024-1', '#3498DB', 1); + +-- 插入测试日程数据 +INSERT INTO `schedules` (`user_id`, `title`, `description`, `start_time`, `end_time`, `location`, `is_all_day`, `reminder`, `color`, `status`) VALUES +(2, '期末考试复习', '准备数据结构期末考试', '2024-06-15 19:00:00', '2024-06-15 22:00:00', '图书馆', 0, 30, '#409EFF', 1), +(2, '项目讨论会', '讨论课程设计项目进展', '2024-06-20 14:00:00', '2024-06-20 16:00:00', '实验室', 0, 15, '#67C23A', 1), +(3, '社团活动', '参加数学建模社团活动', '2024-06-18 00:00:00', '2024-06-18 23:59:59', '学生活动中心', 1, 60, '#E6A23C', 1), +(3, '导师面谈', '与导师讨论学习进度', '2024-06-22 10:00:00', '2024-06-22 11:00:00', '办公楼A305', 0, 30, '#F56C6C', 1), +(4, '实验报告提交', '提交物理实验报告', '2024-06-25 23:59:00', '2024-06-25 23:59:59', '在线提交', 0, 1440, '#9B59B6', 1); \ No newline at end of file diff --git a/unilife-server/src/main/resources/mappers/ResourceLikeMapper.xml b/unilife-server/src/main/resources/mappers/ResourceLikeMapper.xml new file mode 100644 index 0000000..badeb46 --- /dev/null +++ b/unilife-server/src/main/resources/mappers/ResourceLikeMapper.xml @@ -0,0 +1,25 @@ + + + + + + + INSERT INTO resource_likes (resource_id, user_id, created_at) + VALUES (#{resourceId}, #{userId}, NOW()) + + + + DELETE FROM resource_likes + WHERE resource_id = #{resourceId} AND user_id = #{userId} + + + + \ No newline at end of file