From 86126699d3ec9c008788507e265396a8ea35e5ee Mon Sep 17 00:00:00 2001 From: WangHe <51102@163.com> Date: Wed, 20 Aug 2025 15:42:52 +0800 Subject: [PATCH 01/27] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=B6=85?= =?UTF-8?q?=E9=95=BF=E6=96=87=E6=9C=AC=E9=80=A0=E6=88=90ui=E5=AE=BD?= =?UTF-8?q?=E5=BA=A6=E6=92=91=E5=BC=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Related #602 --- frontend_nuxt/assets/global.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend_nuxt/assets/global.css b/frontend_nuxt/assets/global.css index 6b29dcabd..edfad14a8 100644 --- a/frontend_nuxt/assets/global.css +++ b/frontend_nuxt/assets/global.css @@ -184,7 +184,7 @@ body { font-family: 'Maple Mono', monospace; font-size: 13px; border-radius: 4px; - white-space: no-wrap; + white-space: break-spaces; background-color: var(--code-highlight-background-color); color: var(--text-color); } From 1b892828f13ceb80ba01de3aa268077ce0afb62a Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Wed, 20 Aug 2025 16:23:22 +0800 Subject: [PATCH 02/27] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e1d721d01..16e4516b7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@

+ Image OpenIsle

高效的开源社区前后端端平台 From 06ffb180fedfc33fad66a38f085977a9e8f1c9bd Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Wed, 20 Aug 2025 16:24:05 +0800 Subject: [PATCH 03/27] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 16e4516b7..362c7f841 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,9 @@

- Image OpenIsle

高效的开源社区前后端端平台

- + Image

## 💡 简介 From f60f184c8448123c121d4cef85fe9cfe702bf04f Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Wed, 20 Aug 2025 16:24:33 +0800 Subject: [PATCH 04/27] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 362c7f841..60ed15bb0 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@

OpenIsle -

高效的开源社区前后端端平台 -

+


Image

From fa1148bc4e4cc8208346c4b135dff34403cb9521 Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Wed, 20 Aug 2025 16:25:12 +0800 Subject: [PATCH 05/27] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 60ed15bb0..f6552472d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@

OpenIsle +
高效的开源社区前后端端平台


Image From 66035447a8a054e8add0f791b36d413791265651 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 20 Aug 2025 16:28:28 +0800 Subject: [PATCH 06/27] feat: add CONTRIBUTING --- frontend_nuxt/CONTRIBUTING.md | 116 ++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 frontend_nuxt/CONTRIBUTING.md diff --git a/frontend_nuxt/CONTRIBUTING.md b/frontend_nuxt/CONTRIBUTING.md new file mode 100644 index 000000000..9b9804d2c --- /dev/null +++ b/frontend_nuxt/CONTRIBUTING.md @@ -0,0 +1,116 @@ +#### **⚠️注意:仅想修改前端的朋友可不用部署后端服务** + +## 如何部署 + +> Step1 先克隆仓库 + +```shell +git clone https://github.com/nagisa77/OpenIsle.git +cd OpenIsle +``` + +> Step2 后端部署 + +```shell +cd backend +``` + +以IDEA编辑器为例,IDEA打开backend文件夹。 + +- 设置VM Option,最好运行在其他端口,非8080,这里设置8081 + +```shell +-Dserver.port=8081 +``` + +![CleanShot 2025-08-04 at 11 .35.49.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/4cf210cfc6ea478a80dfc744c85ccdc4.png) + +- 设置jdk版本为java 17 + +![CleanShot 2025-08-04 at 11 .38.03@2x.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/392eeec753ae436ca12a78f750dfea2d.png) + +- 本机配置MySQL服务(网上很多教程,忽略) +- 设置环境变量.env 文件 或.properties 文件(二选一) + +1. 环境变量文件生成 + +```shell +cp open-isle.env.example open-isle.env +``` + +修改环境变量,留下需要的,比如你要开发Google登录业务,就需要谷歌相关的变量,数据库是一定要的 + +![CleanShot 2025-08-04 at 11 .41.36@2x.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/896c8363b6e64ea19d18c12ec4dae2b4.png) + +应用环境文件, 选择刚刚的`open-isle.env` + +![CleanShot 2025-08-04 at 11 .44.41.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/f588e37838014a6684c141605639b9fa.png) + +2. 直接修改 .properities 文件 + +位置src/main/application.properties, 数据库需要修改标红处,其他按需修改 + +![CleanShot 2025-08-04 at 11 .47.11@2x.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/28c3104448a245419e0b06aee861abb4.png) + +处理完环境问题直接跑起来就能通了 + +![CleanShot 2025-08-04 at 11 .49.01@2x.png](https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/2c945eae44b1477db09e80fc96b5e02d.png) + +> Step3 前端部署 + +前端可以依赖本机部署的后端,也可以直接调用线上的后端接口 + +```shell +cd ../frontend_nuxt/ +``` + +copy环境.env文件 + +```shell +cp .env.staging.example .env +``` + +1. 依赖本机部署的后端:打开本文件夹,修改.env 修改为瞄准本机后端端口 + +```yaml +; 本地部署后端 +NUXT_PUBLIC_API_BASE_URL=https://127.0.0.1:8081 +; 预发环境后端 +; NUXT_PUBLIC_API_BASE_URL=https://staging.open-isle.com +; 生产环境后端 +; NUXT_PUBLIC_API_BASE_URL=https://www.open-isle.com +``` + +2. 依赖预发环境后台环境 + +**(⚠️强烈推荐只部署前端的朋友使用该环境)** + +```yaml +; 本地部署后端 +; NUXT_PUBLIC_API_BASE_URL=https://127.0.0.1:8081 +; 预发环境后端 +NUXT_PUBLIC_API_BASE_URL=https://staging.open-isle.com +; 生产环境后端 +; NUXT_PUBLIC_API_BASE_URL=https://www.open-isle.com +``` + +4. 依赖线上后台环境 + +```yaml +; 本地部署后端 +; NUXT_PUBLIC_API_BASE_URL=https://127.0.0.1:8081 +; 预发环境后端 +; NUXT_PUBLIC_API_BASE_URL=https://staging.open-isle.com +; 生产环境后端 +NUXT_PUBLIC_API_BASE_URL=https://www.open-isle.com +``` + +```shell +# 安装依赖 +npm install --verbose + +# 运行前端服务 +npm run dev +``` + +如此一来,浏览器访问 http://127.0.0.1:3000 即可访问前端页面 From d717ce03c16f4b9ee4a556a1575ecad21b66d573 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 20 Aug 2025 16:29:45 +0800 Subject: [PATCH 07/27] feat: add CONTRIBUTING --- frontend_nuxt/CONTRIBUTING.md => CONTRIBUTING.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename frontend_nuxt/CONTRIBUTING.md => CONTRIBUTING.md (100%) diff --git a/frontend_nuxt/CONTRIBUTING.md b/CONTRIBUTING.md similarity index 100% rename from frontend_nuxt/CONTRIBUTING.md rename to CONTRIBUTING.md From 81e3a80d351a4a19c037e6704e6dd6638550a36a Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Wed, 20 Aug 2025 16:31:49 +0800 Subject: [PATCH 08/27] Update README.md --- README.md | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index f6552472d..8fa54b96b 100644 --- a/README.md +++ b/README.md @@ -10,36 +10,9 @@ OpenIsle 是一个使用 Spring Boot 和 Vue 3 构建的全栈开源社区平台,提供用户注册、登录、贴文发布、评论交互等完整功能,可用于项目社区或直接打造自主社区站点。 -## 🚧 开发 +## 🚧 开发 & 部署 -### 后端 - -1. 确保安装 JDK 17 及 Maven -2. 信息配置修改 `src/main/resources/application.properties`,或通过环境变量设置数据库等参数 -3. 执行 `mvn clean package` 生成包,之后使用 `java -jar target/openisle-0.0.1-SNAPSHOT.jar`启动,或在开发时直接使用 `mvn spring-boot:run` - -### 前端 - -1. 进入前端目录 - ```bash - cd frontend_nuxt - ``` -2. 安装依赖 - ```bash - npm install - ``` -3. 启动开发服务 - ```bash - npm run dev - ``` - - 生产版本使用如下命令编译: - - ```bash - npm run build - ``` - - 会在 `.output` 目录生成文件,配合线上网站方式部署 +详细见 [Contributing](https://github.com/nagisa77/OpenIsle?tab=contributing-ov-file) ## ✨ 项目特点 From 4969a759aa3d3043d05025eb4a1bbb8e2d533b52 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 20 Aug 2025 19:33:31 +0800 Subject: [PATCH 09/27] =?UTF-8?q?fix:=20=E5=B7=B2=E5=85=B3=E9=97=AD?= =?UTF-8?q?=E7=9A=84=E5=B8=96=E5=AD=90=E4=B8=8D=E9=9C=80=E8=A6=81=E5=B1=95?= =?UTF-8?q?=E7=A4=BA=E8=AE=A2=E9=98=85=E6=8C=89=E9=92=AE=20#651?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_nuxt/pages/posts/[id]/index.vue | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend_nuxt/pages/posts/[id]/index.vue b/frontend_nuxt/pages/posts/[id]/index.vue index 128b8a208..b117e6e20 100644 --- a/frontend_nuxt/pages/posts/[id]/index.vue +++ b/frontend_nuxt/pages/posts/[id]/index.vue @@ -17,7 +17,7 @@

已拒绝
已关闭
@@ -27,7 +27,7 @@
@@ -1067,6 +1067,7 @@ onMounted(async () => { white-space: nowrap; } +.article-closed-button, .article-subscribe-button-text, .article-unsubscribe-button-text { white-space: nowrap; From 91ffacc3358886c785cd23bb5f940b0ed9b48437 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 20 Aug 2025 19:43:31 +0800 Subject: [PATCH 10/27] =?UTF-8?q?fix:=20=E5=B7=B2=E7=BB=8F=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E7=9A=84=E5=B8=96=E5=AD=90=20=E9=87=8D=E6=96=B0?= =?UTF-8?q?=E8=BF=9B=E5=85=A5=20=E6=B2=A1=E6=9C=89=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=E8=AF=84=E8=AE=BA=E5=AE=9A=E4=BD=8D=E9=80=BB=E8=BE=91=20#652?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend_nuxt/pages/posts/[id]/index.vue | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/frontend_nuxt/pages/posts/[id]/index.vue b/frontend_nuxt/pages/posts/[id]/index.vue index b117e6e20..45f4e9bbe 100644 --- a/frontend_nuxt/pages/posts/[id]/index.vue +++ b/frontend_nuxt/pages/posts/[id]/index.vue @@ -876,12 +876,7 @@ const gotoProfile = () => { navigateTo(`/users/${author.value.id}`, { replace: true }) } -onActivated(async () => { - await refreshPost() - await fetchComments() -}) - -onMounted(async () => { +const initPage = async () => { await fetchComments() const hash = location.hash const id = hash.startsWith('#comment-') ? hash.substring('#comment-'.length) : null @@ -889,6 +884,14 @@ onMounted(async () => { updateCurrentIndex() window.addEventListener('scroll', updateCurrentIndex) jumpToHashComment() +} + +onActivated(async () => { + await initPage() +}) + +onMounted(async () => { + await initPage() }) From 959b0f6a489e5e63a21d794b77a13547e2022e97 Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Wed, 20 Aug 2025 20:21:31 +0800 Subject: [PATCH 11/27] feat: notify authors when admin deletes post --- .../com/openisle/model/NotificationType.java | 2 + .../com/openisle/service/PostService.java | 9 +++- .../com/openisle/service/PostServiceTest.java | 52 +++++++++++++++++++ frontend_nuxt/pages/message.vue | 20 +++++++ frontend_nuxt/utils/notification.js | 13 +++++ 5 files changed, 95 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/com/openisle/model/NotificationType.java b/backend/src/main/java/com/openisle/model/NotificationType.java index 132af5176..d8e3a99a1 100644 --- a/backend/src/main/java/com/openisle/model/NotificationType.java +++ b/backend/src/main/java/com/openisle/model/NotificationType.java @@ -14,6 +14,8 @@ public enum NotificationType { POST_REVIEW_REQUEST, /** Your post under review was approved or rejected */ POST_REVIEWED, + /** An administrator deleted your post */ + POST_DELETED, /** A subscribed post received a new comment */ POST_UPDATED, /** Someone subscribed to your post */ diff --git a/backend/src/main/java/com/openisle/service/PostService.java b/backend/src/main/java/com/openisle/service/PostService.java index e9a205fb9..f0238d29e 100644 --- a/backend/src/main/java/com/openisle/service/PostService.java +++ b/backend/src/main/java/com/openisle/service/PostService.java @@ -579,7 +579,9 @@ public class PostService { .orElseThrow(() -> new com.openisle.exception.NotFoundException("Post not found")); User user = userRepository.findByUsername(username) .orElseThrow(() -> new com.openisle.exception.NotFoundException("User not found")); - if (!user.getId().equals(post.getAuthor().getId()) && user.getRole() != Role.ADMIN) { + User author = post.getAuthor(); + boolean adminDeleting = !user.getId().equals(author.getId()) && user.getRole() == Role.ADMIN; + if (!user.getId().equals(author.getId()) && user.getRole() != Role.ADMIN) { throw new IllegalArgumentException("Unauthorized"); } for (Comment c : commentRepository.findByPostAndParentIsNullOrderByCreatedAtAsc(post)) { @@ -596,7 +598,12 @@ public class PostService { future.cancel(false); } } + String title = post.getTitle(); postRepository.delete(post); + if (adminDeleting) { + notificationService.createNotification(author, NotificationType.POST_DELETED, + null, null, null, user, null, title); + } } public java.util.List getPostsByIds(java.util.List ids) { diff --git a/backend/src/test/java/com/openisle/service/PostServiceTest.java b/backend/src/test/java/com/openisle/service/PostServiceTest.java index e1dbfd297..4ad621b18 100644 --- a/backend/src/test/java/com/openisle/service/PostServiceTest.java +++ b/backend/src/test/java/com/openisle/service/PostServiceTest.java @@ -61,6 +61,58 @@ class PostServiceTest { verify(postRepo).delete(post); } + @Test + void deletePostByAdminNotifiesAuthor() { + PostRepository postRepo = mock(PostRepository.class); + UserRepository userRepo = mock(UserRepository.class); + CategoryRepository catRepo = mock(CategoryRepository.class); + TagRepository tagRepo = mock(TagRepository.class); + LotteryPostRepository lotteryRepo = mock(LotteryPostRepository.class); + NotificationService notifService = mock(NotificationService.class); + SubscriptionService subService = mock(SubscriptionService.class); + CommentService commentService = mock(CommentService.class); + CommentRepository commentRepo = mock(CommentRepository.class); + ReactionRepository reactionRepo = mock(ReactionRepository.class); + PostSubscriptionRepository subRepo = mock(PostSubscriptionRepository.class); + NotificationRepository notificationRepo = mock(NotificationRepository.class); + PostReadService postReadService = mock(PostReadService.class); + ImageUploader imageUploader = mock(ImageUploader.class); + TaskScheduler taskScheduler = mock(TaskScheduler.class); + EmailSender emailSender = mock(EmailSender.class); + ApplicationContext context = mock(ApplicationContext.class); + + PostService service = new PostService(postRepo, userRepo, catRepo, tagRepo, lotteryRepo, + notifService, subService, commentService, commentRepo, + reactionRepo, subRepo, notificationRepo, postReadService, + imageUploader, taskScheduler, emailSender, context, PublishMode.DIRECT); + when(context.getBean(PostService.class)).thenReturn(service); + + Post post = new Post(); + post.setId(1L); + post.setTitle("T"); + post.setContent(""); + User author = new User(); + author.setId(2L); + author.setRole(Role.USER); + post.setAuthor(author); + + User admin = new User(); + admin.setId(1L); + admin.setRole(Role.ADMIN); + + when(postRepo.findById(1L)).thenReturn(Optional.of(post)); + when(userRepo.findByUsername("admin")).thenReturn(Optional.of(admin)); + when(commentRepo.findByPostAndParentIsNullOrderByCreatedAtAsc(post)).thenReturn(List.of()); + when(reactionRepo.findByPost(post)).thenReturn(List.of()); + when(subRepo.findByPost(post)).thenReturn(List.of()); + when(notificationRepo.findByPost(post)).thenReturn(List.of()); + + service.deletePost(1L, "admin"); + + verify(notifService).createNotification(eq(author), eq(NotificationType.POST_DELETED), isNull(), + isNull(), isNull(), eq(admin), isNull(), eq("T")); + } + @Test void createPostRespectsRateLimit() { PostRepository postRepo = mock(PostRepository.class); diff --git a/frontend_nuxt/pages/message.vue b/frontend_nuxt/pages/message.vue index 4b89b6ec5..096d3b935 100644 --- a/frontend_nuxt/pages/message.vue +++ b/frontend_nuxt/pages/message.vue @@ -495,6 +495,24 @@ 已被管理员拒绝 +