From aeb0866ec1e61c35485b5d7f51dd2ddda6495c12 Mon Sep 17 00:00:00 2001 From: Chuyaoyuan Date: Wed, 13 May 2026 17:40:26 +0800 Subject: [PATCH] Update Search footer header index user --- .../jiscuss/config/WebSecurityConfig.java | 2 +- .../jiscuss/controller/GlobalModelAdvice.java | 30 +++++ .../controller/UserPageController.java | 49 ++++++++ .../controller/UserPostController.java | 22 ++++ .../controller/UserSystemController.java | 34 ++++++ .../repository/DiscussionsRepository.java | 11 ++ src/main/resources/templates/comm/footer.ftl | 14 ++- src/main/resources/templates/comm/header.ftl | 49 ++++---- src/main/resources/templates/discussions.ftl | 107 ++++++------------ src/main/resources/templates/index.ftl | 90 ++++++++++++++- src/main/resources/templates/search.ftl | 94 +++++++++++++++ src/main/resources/templates/user.ftl | 82 ++++++++++++++ 12 files changed, 477 insertions(+), 107 deletions(-) create mode 100644 src/main/java/com/yaoyuan/jiscuss/controller/GlobalModelAdvice.java create mode 100644 src/main/resources/templates/search.ftl diff --git a/src/main/java/com/yaoyuan/jiscuss/config/WebSecurityConfig.java b/src/main/java/com/yaoyuan/jiscuss/config/WebSecurityConfig.java index e64581f..5470232 100644 --- a/src/main/java/com/yaoyuan/jiscuss/config/WebSecurityConfig.java +++ b/src/main/java/com/yaoyuan/jiscuss/config/WebSecurityConfig.java @@ -88,7 +88,7 @@ public class WebSecurityConfig { auth.requestMatchers( "/css/**", "/js/**", "/images/**", "/static/**", "/favicon.ico", "/login/**", "/register", "/registerDo", "/initUserData", - "/", "/main", "/index", "/getdiscussionsbyid", + "/", "/main", "/index", "/getdiscussionsbyid", "/search", "/profile", // SpringDoc OpenAPI UI and spec endpoint (developer tools, no sensitive data) "/swagger-ui/**", "/swagger-ui.html", "/v3/api-docs/**" ).permitAll(); diff --git a/src/main/java/com/yaoyuan/jiscuss/controller/GlobalModelAdvice.java b/src/main/java/com/yaoyuan/jiscuss/controller/GlobalModelAdvice.java new file mode 100644 index 0000000..29c66d6 --- /dev/null +++ b/src/main/java/com/yaoyuan/jiscuss/controller/GlobalModelAdvice.java @@ -0,0 +1,30 @@ +package com.yaoyuan.jiscuss.controller; + +import com.yaoyuan.jiscuss.entity.UserInfo; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ModelAttribute; + +/** + * Injects common model attributes (e.g. isAdmin) into every rendered view. + */ +@ControllerAdvice +public class GlobalModelAdvice { + + @ModelAttribute("isAdmin") + public boolean isAdmin(HttpServletRequest request) { + HttpSession session = request.getSession(false); + if (session == null) return false; + Object ctx = session.getAttribute("SPRING_SECURITY_CONTEXT"); + if (!(ctx instanceof SecurityContext sc)) return false; + Authentication auth = sc.getAuthentication(); + if (auth == null || !(auth.getPrincipal() instanceof UserInfo)) return false; + return auth.getAuthorities().stream() + .map(GrantedAuthority::getAuthority) + .anyMatch(a -> a.equals("ROLE_ADMIN")); + } +} diff --git a/src/main/java/com/yaoyuan/jiscuss/controller/UserPageController.java b/src/main/java/com/yaoyuan/jiscuss/controller/UserPageController.java index 0c56e0e..5f195cf 100644 --- a/src/main/java/com/yaoyuan/jiscuss/controller/UserPageController.java +++ b/src/main/java/com/yaoyuan/jiscuss/controller/UserPageController.java @@ -18,6 +18,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -100,6 +101,54 @@ public class UserPageController extends BaseController { map.put("likedDiscussions", liked); } + map.put("isOwn", true); + return "user"; + } + + /** + * 公开用户主页 /profile?uid=xxx (无需登录) + */ + @GetMapping("/profile") + public String profile(@RequestParam Integer uid, + @RequestParam(defaultValue = "discussion") String type, + HttpServletRequest request, ModelMap map) { + User userEntity = usersService.findOne(uid); + if (userEntity == null) return "redirect:/"; + + long liveDiscCount = discussionsRepository.countByStartUserId(uid); + long liveReplyCount = postsRepository.countByCreateId(uid); + map.put("username", userEntity.getUsername()); + map.put("userEntity", userEntity); + map.put("liveDiscCount", liveDiscCount); + map.put("liveReplyCount", liveReplyCount); + map.put("type", type); + map.put("isOwn", false); + + if ("discussion".equals(type)) { + Page discussions = discussionsRepository.findByStartUserIdOrderByStartTimeDesc( + uid, PageRequest.of(0, 20)); + map.put("discussions", discussions.getContent()); + } else if ("reply".equals(type)) { + Page posts = postsRepository.findByCreateIdOrderByCreateTimeDesc( + uid, PageRequest.of(0, 20)); + map.put("posts", posts.getContent()); + List discTitles = new ArrayList<>(); + for (Post p : posts.getContent()) { + Discussion d = p.getDiscussionId() != null + ? discussionsRepository.findById(p.getDiscussionId()).orElse(null) : null; + discTitles.add(d != null ? d.getTitle() : ""); + } + map.put("discTitles", discTitles); + } else if ("like".equals(type)) { + List likedIds = likeCollectRepository.findLikedDiscussionIdsByUser(uid); + List liked = new ArrayList<>(); + for (Integer id : likedIds) { + Discussion d = discussionsRepository.findById(id).orElse(null); + if (d != null) liked.add(d); + } + map.put("likedDiscussions", liked); + } + return "user"; } } diff --git a/src/main/java/com/yaoyuan/jiscuss/controller/UserPostController.java b/src/main/java/com/yaoyuan/jiscuss/controller/UserPostController.java index 8a2e7e4..cf63d24 100644 --- a/src/main/java/com/yaoyuan/jiscuss/controller/UserPostController.java +++ b/src/main/java/com/yaoyuan/jiscuss/controller/UserPostController.java @@ -7,6 +7,8 @@ import com.yaoyuan.jiscuss.entity.Tag; import com.yaoyuan.jiscuss.entity.User; import com.yaoyuan.jiscuss.entity.UserInfo; import com.yaoyuan.jiscuss.entity.custom.DiscussionCustom; +import com.yaoyuan.jiscuss.repository.DiscussionsRepository; +import com.yaoyuan.jiscuss.repository.PostsRepository; import com.yaoyuan.jiscuss.service.IDiscussionsService; import com.yaoyuan.jiscuss.service.IDiscussionsTagsService; import com.yaoyuan.jiscuss.service.IPostsService; @@ -19,6 +21,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.PostMapping; @@ -59,6 +62,12 @@ public class UserPostController extends BaseController { @Autowired private IUsersService usersService; + @Autowired + private DiscussionsRepository discussionsRepository; + + @Autowired + private PostsRepository postsRepository; + @Autowired private IpRegionService ipRegionService; @@ -112,6 +121,19 @@ public class UserPostController extends BaseController { map.put("discussions", newdd); map.put("posts", postsObj); map.put("sort", sort); + + // Sidebar: author stats and top discussions + if (newdd.getStartUserId() != null) { + Integer authorId = newdd.getStartUserId(); + long authorDiscCount = discussionsRepository.countByStartUserId(authorId); + long authorReplyCount = postsRepository.countByCreateId(authorId); + java.util.List authorTopDiscs = discussionsRepository + .findTopByStartUserIdOrderByLikes(authorId, PageRequest.of(0, 5)); + map.put("authorDiscCount", authorDiscCount); + map.put("authorReplyCount", authorReplyCount); + map.put("authorTopDiscs", authorTopDiscs); + } + UserInfo user = getUserInfo(request); if (user != null) { map.put("username", user.getUsername()); diff --git a/src/main/java/com/yaoyuan/jiscuss/controller/UserSystemController.java b/src/main/java/com/yaoyuan/jiscuss/controller/UserSystemController.java index 49a94eb..a70679f 100644 --- a/src/main/java/com/yaoyuan/jiscuss/controller/UserSystemController.java +++ b/src/main/java/com/yaoyuan/jiscuss/controller/UserSystemController.java @@ -6,6 +6,7 @@ import com.yaoyuan.jiscuss.entity.User; import com.yaoyuan.jiscuss.entity.UserInfo; import com.yaoyuan.jiscuss.entity.custom.DiscussionCustom; import com.yaoyuan.jiscuss.entity.custom.TagCustom; +import com.yaoyuan.jiscuss.repository.DiscussionsRepository; import com.yaoyuan.jiscuss.service.IDiscussionsService; import com.yaoyuan.jiscuss.service.ITagsService; import com.yaoyuan.jiscuss.service.IUsersService; @@ -54,6 +55,9 @@ public class UserSystemController extends BaseController { @Autowired private BCryptPasswordEncoder passwordEncoder; + @Autowired + private DiscussionsRepository discussionsRepository; + /** * 首页index * @param tag @@ -276,6 +280,36 @@ public class UserSystemController extends BaseController { return "register"; } + /** + * 搜索页 + */ + @GetMapping("/search") + public String search(@RequestParam(defaultValue = "") String q, + @RequestParam(defaultValue = "1") Integer pageNum, + HttpServletRequest request, ModelMap map) { + String keyword = q.trim(); + int pageSize = 10; + int page = Math.max(pageNum - 1, 0); + Page resultPage = keyword.isEmpty() + ? Page.empty() + : discussionsRepository.searchByKeyword(keyword, + org.springframework.data.domain.PageRequest.of(page, pageSize)); + + List results = new ArrayList<>(); + if (!keyword.isEmpty()) { + setTagAndUserList(results, resultPage.getContent()); + } + + map.put("q", keyword); + map.put("results", results); + map.put("totalPages", resultPage.getTotalPages()); + map.put("totalElements", resultPage.getTotalElements()); + map.put("pageNum", pageNum); + UserInfo user = getUserInfo(request); + if (user != null) map.put("username", user.getUsername()); + return "search"; + } + //获取设置信息 //获取首页统计 diff --git a/src/main/java/com/yaoyuan/jiscuss/repository/DiscussionsRepository.java b/src/main/java/com/yaoyuan/jiscuss/repository/DiscussionsRepository.java index b9f0452..8b2787c 100644 --- a/src/main/java/com/yaoyuan/jiscuss/repository/DiscussionsRepository.java +++ b/src/main/java/com/yaoyuan/jiscuss/repository/DiscussionsRepository.java @@ -10,6 +10,7 @@ import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import java.util.Date; +import java.util.List; @Repository public interface DiscussionsRepository extends JpaRepository { @@ -49,4 +50,14 @@ public interface DiscussionsRepository extends JpaRepository findTopByStartUserIdOrderByLikes( + @org.springframework.data.repository.query.Param("userId") Integer userId, + Pageable pageable); + + /** Full-text search by title or content. */ + @Query("SELECT d FROM Discussion d WHERE d.title LIKE %:q% OR d.content LIKE %:q% ORDER BY d.startTime DESC") + Page searchByKeyword(@org.springframework.data.repository.query.Param("q") String q, Pageable pageable); } diff --git a/src/main/resources/templates/comm/footer.ftl b/src/main/resources/templates/comm/footer.ftl index a0687b0..1ee29a7 100644 --- a/src/main/resources/templates/comm/footer.ftl +++ b/src/main/resources/templates/comm/footer.ftl @@ -29,20 +29,22 @@
-

这里是页脚预留信息

-

This is about Jiscuus Page - .

+

关于 Jiscuss

+

一个简单、易用的 Java 开源论坛,基于 Spring Boot + Semantic UI 构建。

- - @ 2019-2020   - diff --git a/src/main/resources/templates/comm/header.ftl b/src/main/resources/templates/comm/header.ftl index 9647a77..60841b4 100644 --- a/src/main/resources/templates/comm/header.ftl +++ b/src/main/resources/templates/comm/header.ftl @@ -6,22 +6,11 @@ 首页 论坛 - @@ -66,6 +67,8 @@ var saved = localStorage.getItem('jiscuss-theme') || 'light'; if (saved === 'dark') { document.documentElement.setAttribute('data-theme', 'dark'); } document.addEventListener('DOMContentLoaded', function() { + // Initialize user dropdown + $('.ui.dropdown').dropdown(); var btn = document.getElementById('themeToggle'); var icon = document.getElementById('themeIcon'); function applyTheme(t) { diff --git a/src/main/resources/templates/discussions.ftl b/src/main/resources/templates/discussions.ftl index 1a6c7ac..86945d2 100644 --- a/src/main/resources/templates/discussions.ftl +++ b/src/main/resources/templates/discussions.ftl @@ -187,10 +187,10 @@
-
- -
搜索
-
+
+ + +
@@ -199,90 +199,48 @@
作者
-

测试人员1

-
-
-
-
- Jiscuss 一直, Jiscuss 一感谢支持! -
-
+

+ ${discussions.realname!''} +

+
+
+
${authorDiscCount!0}
+
发帖
- -
-
-
- 2019 测试内容 -
-
+
+
${authorReplyCount!0}
+
回复
-
- -
+ <#if authorTopDiscs?? && authorTopDiscs?size gt 0>
-
作者热门主题
+
作者其他主题
-
+
-
- + <#list authorTopDiscs as d> + <#if d.id != discussions.id> + - - -
-
-
-

预留信息

-
-
- - -
-
- -
Jiscuss Demo
-
简单、易用、开源
-
一款基于JAVA的开源论坛 框架采用:Spring Boot + H2 - + Semantic UI -
-
-
-
-
-
-
1
-
用户数
-
-
-
-
-
10
-
发帖数
-
-
- + +
+
@@ -333,7 +291,10 @@ '
' + (u.username || '') + '
' + '
发帖: ' + (u.discussionsCount || 0) + '  回复: ' + (u.commentsCount || 0) + '
' + - '' + '
' + + '主页' + + '发私信' + + '
' ).css({ left: x + 14, top: y + 14 }).show(); } diff --git a/src/main/resources/templates/index.ftl b/src/main/resources/templates/index.ftl index 3d4b7b6..4563795 100644 --- a/src/main/resources/templates/index.ftl +++ b/src/main/resources/templates/index.ftl @@ -333,7 +333,8 @@
- ${discussions.username}  •   最后由 ${discussions.usernameLast} 回复于${discussions.lastTime}. @@ -387,7 +388,7 @@
- ${discussions.username}  •   + ${discussions.username}  •   最后由 ${discussions.usernameLast} 回复于${discussions.lastTime}.
@@ -439,7 +440,7 @@
- ${discussions.username}  •   + ${discussions.username}  •   最后由 ${discussions.usernameLast} 回复于${discussions.lastTime}.
@@ -505,7 +506,7 @@
${discussions.startTime}
- ${discussions.username} •  + ${discussions.username} •  ${discussions.commentsCount!0} 回复  ${discussions.viewCount!0} 浏览
@@ -556,6 +557,87 @@ + +<#-- 用户悬浮卡片 --> + + +<#-- 私信弹窗 Modal --> + + diff --git a/src/main/resources/templates/search.ftl b/src/main/resources/templates/search.ftl new file mode 100644 index 0000000..749790f --- /dev/null +++ b/src/main/resources/templates/search.ftl @@ -0,0 +1,94 @@ + + + +搜索 - Jiscuss + + + <#include "comm/commjs.ftl"/> + + +<#include "comm/header.ftl"/> + +
+ +
+
+
+ + +
+
+
+ + <#if q?? && q != ""> +
+ <#if totalElements?? && totalElements gt 0> + 找到 ${totalElements} 个关于 "${q}" 的结果 + <#else> + 没有找到关于 "${q}" 的相关结果 + +
+ + + <#if results?? && results?size gt 0> +
+ <#list results as disc> +
+
+ + ${disc.title} + +
+ ${disc.content!''} +
+
+ ${disc.username!''} +   •   + ${disc.startTime!''} +    + ${disc.likeCount!0} +   + ${disc.commentsCount!0} 回复 +   + ${disc.viewCount!0} 浏览 + <#if disc.tagList?? && disc.tagList?size gt 0> +    + <#list disc.tagList as tag> + ${tag.name} + + +
+
+
+ +
+ + <#if totalPages?? && totalPages gt 1> + + + + <#elseif q?? && q != ""> +
+
+ + 暂无搜索结果 +
+ 返回首页 +
+ + +
+ +<#include "comm/footer.ftl"/> + + diff --git a/src/main/resources/templates/user.ftl b/src/main/resources/templates/user.ftl index 8285e92..8d75cb6 100644 --- a/src/main/resources/templates/user.ftl +++ b/src/main/resources/templates/user.ftl @@ -11,6 +11,52 @@ .profile-stat .number { font-size:1.6em; font-weight:bold; color:var(--color-primary,#2185d0); } .profile-stat .label2 { color:#888; font-size:0.85em; } .tab-content { background:#fff; border-radius:8px; padding:1.2em; min-height:200px; } + + /* 彩蛋按钮特效 */ + @keyframes rainbowBg { + 0% { background-position: 0% 50%; } + 50% { background-position: 100% 50%; } + 100% { background-position: 0% 50%; } + } + @keyframes btnGlow { + 0%, 100% { box-shadow: 0 0 8px 2px rgba(41,128,185,.6); } + 33% { box-shadow: 0 0 10px 3px rgba(192,57,43,.6); } + 66% { box-shadow: 0 0 10px 3px rgba(39,174,96,.6); } + } + @keyframes btnWiggle { + 0%,100% { transform: rotate(0deg) scale(1); } + 20% { transform: rotate(-4deg) scale(1.08); } + 40% { transform: rotate(4deg) scale(1.08); } + 60% { transform: rotate(-3deg) scale(1.05); } + 80% { transform: rotate(3deg) scale(1.05); } + } + @keyframes ripple { + to { transform: scale(3); opacity: 0; } + } + #userButton { + position: relative; overflow: hidden; + border: 2px solid transparent !important; + background: linear-gradient(#fff,#fff) padding-box, + linear-gradient(270deg,#e74c3c,#f39c12,#27ae60,#2980b9,#8e44ad,#e74c3c) border-box; + background-size: auto, 400% 400%; + animation: rainbowBg 4s ease infinite, btnGlow 3s ease-in-out infinite; + color: #333 !important; + font-weight: bold; + letter-spacing: .5px; + transition: transform .15s; + } + #userButton:hover { + animation: btnWiggle .5s ease, rainbowBg 4s ease infinite, btnGlow 3s ease-in-out infinite; + cursor: pointer; + } + #userButton .ripple-circle { + position: absolute; border-radius: 50%; + background: rgba(100,100,255,.25); + width: 60px; height: 60px; + margin-top: -30px; margin-left: -30px; + animation: ripple .6s linear; + pointer-events: none; + } @@ -39,6 +85,13 @@ 加入于 ${userEntity.joinTime?string("yyyy-MM-dd")}
+
+ <#if isOwn!true> + + +
@@ -148,5 +201,34 @@
<#include "comm/footer.ftl"/> + +<#if isOwn!true> + + + + +