fix 恶意刷新查看量

全局搜索代替了原来长连接的方式,增加了很多隐藏的门路。
This commit is contained in:
limqhz
2022-05-10 15:23:32 +08:00
parent b0b9e80f8a
commit 759843e83d
22 changed files with 258 additions and 84 deletions

View File

@@ -14,7 +14,17 @@ public interface QuinnConstant {
String GUN = "The emperor's new clothes"; String GUN = "The emperor's new clothes";
/**
* 登录超时时间
*/
int SESSION_TIME_OUT = 30 * 60;
String SESSION_LOCK = "LOCK";
String SOURCE_KEY = "SOURCE_KEY_"; String SOURCE_KEY = "SOURCE_KEY_";
/**
* SESSION_ID
*/
String SESSION_ID = "SESSION_ID_";
/** /**
* PASSWORD //TODO 可以配置数据库MD5加密 * PASSWORD //TODO 可以配置数据库MD5加密
*/ */

View File

@@ -27,7 +27,6 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
http.authorizeRequests() http.authorizeRequests()
.antMatchers("/","/index").permitAll() .antMatchers("/","/index").permitAll()
.antMatchers("/register","/login","/toLogin").permitAll() .antMatchers("/register","/login","/toLogin").permitAll()
.antMatchers("/tracy/mcgrady/lmq/love/wn").permitAll()
.antMatchers("/blog/**").authenticated() .antMatchers("/blog/**").authenticated()
.antMatchers("/source/**").authenticated() .antMatchers("/source/**").authenticated()
.antMatchers("/user/**").authenticated() .antMatchers("/user/**").authenticated()

View File

@@ -36,7 +36,7 @@ public class AboutController extends BaseModelController {
@GetMapping("/about") @GetMapping("/about")
public String userIndexBlog(Model model){ public String userIndexBlog(HttpServletRequest request,Model model){
Page<About> pageParam = new Page<>(1, 20); Page<About> pageParam = new Page<>(1, 20);
aboutService.page(pageParam,new QueryWrapper<About>().orderByDesc("gmt_create")); aboutService.page(pageParam,new QueryWrapper<About>().orderByDesc("gmt_create"));
// 结果 // 结果
@@ -50,7 +50,7 @@ public class AboutController extends BaseModelController {
@PostMapping("/about") @PostMapping("/about")
public String saveSay(HttpServletRequest request, About about){ public String saveSay(HttpServletRequest request, About about){
String loginUserId = getLoginUserId(request); String loginUserId = getLoginUserId(request);
User user = userService.getOne(new QueryWrapper<User>().eq("uid", "")); User user = userService.getOne(new QueryWrapper<User>().eq("uid", loginUserId));
// 防止请求提交 // 防止请求提交
if (!RoleType.ADMIN.name().equals(user)){ if (!RoleType.ADMIN.name().equals(user)){
return "redirect:/about"; return "redirect:/about";

View File

@@ -1,5 +1,6 @@
package com.quinn.controller; package com.quinn.controller;
import com.quinn.common.QuinnConstant;
import com.quinn.pojo.User; import com.quinn.pojo.User;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@@ -14,4 +15,8 @@ public class BaseModelController {
return ""; return "";
} }
protected String getSessionId(HttpServletRequest request){
return QuinnConstant.SESSION_ID + request.getSession().getId();
}
} }

View File

@@ -119,8 +119,8 @@ public class BlogController extends BaseModelController{
@GetMapping("/blog/read/{bid}") @GetMapping("/blog/read/{bid}")
public String read(HttpServletRequest request, @PathVariable("bid") String bid, Model model){ public String read(HttpServletRequest request, @PathVariable("bid") String bid, Model model){
Blog blog = blogService.getOne(new QueryWrapper<Blog>().eq("bid", bid)); Blog blog = blogService.getOne(new QueryWrapper<Blog>().eq("bid", bid));
blog.setViews(blog.getViews()+1); String sessionId = getSessionId(request);
blogService.updateById(blog); blogService.addRecord(blog,sessionId);
model.addAttribute("blog",blog); model.addAttribute("blog",blog);
StarValue starValue = starService.isStar(bid, getLoginUserId(request), Category.BLOG); StarValue starValue = starService.isStar(bid, getLoginUserId(request), Category.BLOG);

View File

@@ -28,7 +28,6 @@ public class LoginController {
UserInfoService userInfoService; UserInfoService userInfoService;
@GetMapping({"/","/index","/source/view/index", @GetMapping({"/","/index","/source/view/index",
"/tracy/mcgrady/lmq/love/wn/index",
"/blog/read/index" "/blog/read/index"
}) })
public String index(){ public String index(){

View File

@@ -3,14 +3,17 @@ package com.quinn.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.quinn.common.QuinnConstant; import com.quinn.common.QuinnConstant;
import com.quinn.pojo.Source; import com.quinn.pojo.*;
import com.quinn.service.SourceService; import com.quinn.service.*;
import com.quinn.vo.MyPageParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.List;
/** /**
* <p> * <p>
@@ -25,6 +28,14 @@ public class SearchController {
@Resource @Resource
SourceService sourceService; SourceService sourceService;
@Autowired
BlogCategoryService blogCategoryService;
@Autowired
BlogService blogService;
@Autowired
CommentService commentService;
@Resource
SourceCategoryService sourceCategoryService;
@PostMapping("/search") @PostMapping("/search")
public String searchAll(String findWhat,Model model){ public String searchAll(String findWhat,Model model){
@@ -32,6 +43,8 @@ public class SearchController {
return "index"; return "index";
} }
if (QuinnConstant.NEW_SOURCE_PASSWORD.equals(findWhat)){ if (QuinnConstant.NEW_SOURCE_PASSWORD.equals(findWhat)){
List<SourceCategory> categoryList = sourceCategoryService.list(null);
model.addAttribute("categoryList",categoryList);
return "source/uploadSource"; return "source/uploadSource";
} }
if (QuinnConstant.APPEND_PASSWORD.equals(findWhat)){ if (QuinnConstant.APPEND_PASSWORD.equals(findWhat)){
@@ -43,13 +56,38 @@ public class SearchController {
findWhat = findWhat.substring(0,findWhat.lastIndexOf(QuinnConstant.EDIT_SOURCE_LAST)); findWhat = findWhat.substring(0,findWhat.lastIndexOf(QuinnConstant.EDIT_SOURCE_LAST));
} }
if (!StringUtils.isEmpty(findWhat)){ if (!StringUtils.isEmpty(findWhat)){
Source sid = sourceService.getOne(new QueryWrapper<Source>().eq("sid", findWhat)); Source source = sourceService.getOne(new QueryWrapper<Source>().eq("sid", findWhat));
if (sid!=null){ if (source!=null){
return "redirect:/tracy/mcgrady/lmq/love/wn/" + findWhat; source.setKeyWord1(concatKeyWord(source.getKeyWord2()) + concatKeyWord(source.getKeyWord2()) + concatKeyWord(source.getKeyWord3()));
model.addAttribute("source",source);
// 分类信息
List<SourceCategory> categoryList = sourceCategoryService.list(null);
model.addAttribute("categoryList",categoryList);
return "source/editorSource";
} }
} }
} }
return "index"; //TODO
MyPageParam myPageParam = new MyPageParam(1, 10);
List<BlogWithUser> blogList = blogService.getBlogWithUserOrderBySort(myPageParam);
// 结果
model.addAttribute("blogList",blogList);
model.addAttribute("pageParam",myPageParam);
List<Blog> topBlogList = blogService.getTopBlog();
model.addAttribute("topBlogList",topBlogList);
// 分类信息
List<BlogCategory> categoryList = blogCategoryService.list(null);
model.addAttribute("categoryList",categoryList);
return "page/allsearch";
}
private String concatKeyWord(String keyWord) {
if (!StringUtils.isEmpty(keyWord)) {
return keyWord + QuinnConstant.LINK_KEY_WORD;
}
return "";
} }
} }

View File

@@ -15,7 +15,6 @@ import com.quinn.vo.*;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
@@ -83,7 +82,7 @@ public class SourceController extends BaseModelController {
// 列表展示 // 列表展示
@GetMapping("/hotspot") @GetMapping("/hotspot")
public String sourceHotPot(Model model){ public String sourceHotPot(Model model){
Page<Source> pageParam = new Page<>(1, 9); Page<Source> pageParam = new Page<>(1, 21);
QueryWrapper<Source> sourceQuery = new QueryWrapper<>(); QueryWrapper<Source> sourceQuery = new QueryWrapper<>();
sourceQuery.orderByDesc("down_record"); sourceQuery.orderByDesc("down_record");
sourceService.page(pageParam,sourceQuery); sourceService.page(pageParam,sourceQuery);
@@ -110,8 +109,9 @@ public class SourceController extends BaseModelController {
* @return * @return
*/ */
@GetMapping("/source/view/{sid}") @GetMapping("/source/view/{sid}")
public String read(HttpServletRequest request, @PathVariable("sid") String sid, Model model){ public String view(HttpServletRequest request, @PathVariable("sid") String sid, Model model){
Source source = sourceService.view(sid); String sessionId = getSessionId(request);
Source source = sourceService.view(sid,sessionId);
if(source != null){ if(source != null){
source.setSourceLink(QuinnConstant.GUN); source.setSourceLink(QuinnConstant.GUN);
source.setKeyWord1(QuinnConstant.GUN); source.setKeyWord1(QuinnConstant.GUN);

View File

@@ -1,20 +1,13 @@
package com.quinn.controller; package com.quinn.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.quinn.common.QuinnConstant; import com.quinn.common.QuinnConstant;
import com.quinn.pojo.Source;
import com.quinn.pojo.SourceCategory;
import com.quinn.service.SourceCategoryService; import com.quinn.service.SourceCategoryService;
import com.quinn.service.SourceService; import com.quinn.service.SourceService;
import com.quinn.vo.SourceDeleteForm; import com.quinn.vo.SourceDeleteForm;
import com.quinn.vo.SourceUpdateForm; import com.quinn.vo.SourceUpdateForm;
import com.quinn.vo.SourceWriteForm; import com.quinn.vo.SourceWriteForm;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@@ -22,7 +15,6 @@ import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.List;
/** /**
* <p> * <p>
@@ -40,15 +32,6 @@ public class SourceUploadController {
@Resource @Resource
SourceService sourceService; SourceService sourceService;
// 写文章
@GetMapping("/tracy/mcgrady/lmq/love/wn")
public String toWrite(Model model){
// 分类信息
List<SourceCategory> categoryList = sourceCategoryService.list(null);
model.addAttribute("categoryList",categoryList);
return "source/uploadSource";
}
@PostMapping("/tracy/mcgrady/lmq/love/wn") @PostMapping("/tracy/mcgrady/lmq/love/wn")
public synchronized String write(MultipartFile file, SourceWriteForm sourceWriteForm) throws IOException { public synchronized String write(MultipartFile file, SourceWriteForm sourceWriteForm) throws IOException {
if (!QuinnConstant.SOURCE_PASSWORD.equals(sourceWriteForm.getUploadPassWord())){ if (!QuinnConstant.SOURCE_PASSWORD.equals(sourceWriteForm.getUploadPassWord())){
@@ -59,25 +42,6 @@ public class SourceUploadController {
return "redirect:/source"; return "redirect:/source";
} }
// 编辑信息
@GetMapping("/tracy/mcgrady/lmq/love/wn/{sid}")
public String toEdit(@PathVariable("sid") String sid, Model model){
Source source = sourceService.getOne(new QueryWrapper<Source>().eq("sid",sid));
source.setKeyWord1(concatKeyWord(source.getKeyWord2()) + concatKeyWord(source.getKeyWord2()) + concatKeyWord(source.getKeyWord3()));
model.addAttribute("source",source);
// 分类信息
List<SourceCategory> categoryList = sourceCategoryService.list(null);
model.addAttribute("categoryList",categoryList);
return "source/editorSource";
}
private String concatKeyWord(String keyWord) {
if (!StringUtils.isEmpty(keyWord)) {
return keyWord + QuinnConstant.LINK_KEY_WORD;
}
return "";
}
// 编辑信息 // 编辑信息
@PostMapping("/tracy/mcgrady/lmq/love/wn/update") @PostMapping("/tracy/mcgrady/lmq/love/wn/update")
public String toEdit(MultipartFile file, SourceUpdateForm sourceUpdateForm) throws IOException { public String toEdit(MultipartFile file, SourceUpdateForm sourceUpdateForm) throws IOException {

View File

@@ -26,4 +26,5 @@ public interface BlogService extends IService<Blog> {
List<BlogWithUser> getMyBlogs(String userId,MyPageParam myPageParam); List<BlogWithUser> getMyBlogs(String userId,MyPageParam myPageParam);
void addRecord(Blog blog, String sessionId);
} }

View File

@@ -40,7 +40,7 @@ public interface SourceService extends IService<Source> {
* @throws IOException * @throws IOException
* @return * @return
*/ */
Source view(String sid); Source view(String sid,String sessionId);
/** /**
* 上传新资源 * 上传新资源

View File

@@ -1,14 +1,17 @@
package com.quinn.service.impl; package com.quinn.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.quinn.common.QuinnConstant;
import com.quinn.pojo.Blog; import com.quinn.pojo.Blog;
import com.quinn.mapper.BlogMapper; import com.quinn.mapper.BlogMapper;
import com.quinn.pojo.BlogWithUser; import com.quinn.pojo.BlogWithUser;
import com.quinn.service.BlogService; import com.quinn.service.BlogService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.quinn.utils.RedisUtils;
import com.quinn.vo.MyPageParam; import com.quinn.vo.MyPageParam;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.List; import java.util.List;
@@ -24,6 +27,8 @@ import java.util.List;
@Service @Service
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements BlogService { public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements BlogService {
@Resource
RedisUtils redisUtils;
@Resource @Resource
BlogMapper blogMapper; BlogMapper blogMapper;
@@ -59,4 +64,14 @@ public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements Bl
return blogMapper.getMyBlogs(userId,myPageParam); return blogMapper.getMyBlogs(userId,myPageParam);
} }
@Override
public void addRecord(Blog blog, String sessionId) {
String value = redisUtils.get(sessionId);
if (StringUtils.isEmpty(value)){
redisUtils.set(sessionId, QuinnConstant.SESSION_LOCK,QuinnConstant.SESSION_TIME_OUT);
blog.setViews(blog.getViews()+1);
updateById(blog);
}
}
} }

View File

@@ -81,9 +81,13 @@ public class SourceServiceImpl extends ServiceImpl<SourceMapper, Source> impleme
} }
@Override @Override
public Source view(String sid) { public Source view(String sid,String sessionId) {
Source source = getOne(new QueryWrapper<Source>().eq("sid", sid)); Source source = getOne(new QueryWrapper<Source>().eq("sid", sid));
addDownLoadRecord(source); String value = redisUtils.get(sessionId);
if (StringUtils.isEmpty(value)){
redisUtils.set(sessionId,QuinnConstant.SESSION_LOCK,QuinnConstant.SESSION_TIME_OUT);
addDownLoadRecord(source);
}
return source; return source;
} }
@@ -214,13 +218,16 @@ public class SourceServiceImpl extends ServiceImpl<SourceMapper, Source> impleme
* @param source * @param source
*/ */
private void addDownLoadRecord(Source source) { private void addDownLoadRecord(Source source) {
String downLoadTime = redisUtils.get(QuinnConstant.SOURCE_KEY + source.getSid()); /** 使用redis记录访问量可以降低数据库压力,但是时效性就会降低,
int downTimes = 0; * 需要不断跑批处理,用户量小或者单实例不推荐这么做
if (StringUtils.isEmpty(downLoadTime)){ */
downTimes = source.getDownRecord() + 1; // int downTimes = 0;
}else { // if (StringUtils.isEmpty(downLoadTime)){
downTimes = Integer.parseInt(downLoadTime) + 1; // downTimes = source.getDownRecord() + 1;
} // }else {
// downTimes = Integer.parseInt(downLoadTime) + 1;
// }
int downTimes = source.getDownRecord() + 1;
redisUtils.set(QuinnConstant.SOURCE_KEY + source.getSid(),downTimes + ""); redisUtils.set(QuinnConstant.SOURCE_KEY + source.getSid(),downTimes + "");
source.setDownRecord(source.getDownRecord() + 1); source.setDownRecord(source.getDownRecord() + 1);
updateById(source); updateById(source);

View File

@@ -53,6 +53,7 @@ public class StarServiceImpl extends ServiceImpl<StarMapper, Star> implements St
save(blogStar); save(blogStar);
starValue.setStar(true); starValue.setStar(true);
} }
starValue.setTotal(getTotalStar(topicId,category));
return starValue; return starValue;
} }
@@ -68,9 +69,16 @@ public class StarServiceImpl extends ServiceImpl<StarMapper, Star> implements St
starValue.setStar(true); starValue.setStar(true);
} }
} }
starValue.setTotal(getTotalStar(topicId,category));
return starValue; return starValue;
} }
private int getTotalStar(String topicId,Category category){
return count(new QueryWrapper<Star>()
.eq("topic_category",category.name())
.eq("topic_id",topicId));
}
@Override @Override
public List<StarWithTopic> listStars(String userId, MyPageParam myPageParam) { public List<StarWithTopic> listStars(String userId, MyPageParam myPageParam) {
int count = count(new QueryWrapper<Star>().eq("user_id",userId)); int count = count(new QueryWrapper<Star>().eq("user_id",userId));

View File

@@ -55,6 +55,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
user.setPassword(QuinnConstant.GUN); user.setPassword(QuinnConstant.GUN);
// 放入session // 放入session
session.setAttribute("loginUser",user); session.setAttribute("loginUser",user);
session.setMaxInactiveInterval(QuinnConstant.SESSION_TIME_OUT);
//创建一个集合来存放权限 //创建一个集合来存放权限
List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>(); List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();
RoleType[] values = RoleType.values(); RoleType[] values = RoleType.values();

View File

@@ -13,4 +13,7 @@ public class StarValue {
@ApiModelProperty(value = "是否收藏") @ApiModelProperty(value = "是否收藏")
private boolean star; private boolean star;
@ApiModelProperty(value = "总收藏量")
private int total;
} }

View File

@@ -5,7 +5,14 @@
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>论坛-Quinn</title> <title>论坛-Quinn</title>
<link rel="stylesheet" th:href="@{/bootstrap/css/bootstrap.min.css}"> <link rel="stylesheet" th:href="@{/bootstrap/css/bootstrap.min.css}">
<style>
#maxLength {
max-width: 500px;
white-space: nowrap; /* 不换行 */
overflow: hidden; /* 超出部分不显示 */
text-overflow: ellipsis; /* 超出部分显示为... */
}
</style>
</head> </head>
<body style="background: #f2f2f2;"> <body style="background: #f2f2f2;">
@@ -25,7 +32,7 @@
<img th:src="${blog.getAvatar()}" style="border-radius: 5px " width="32" height="32"> <img th:src="${blog.getAvatar()}" style="border-radius: 5px " width="32" height="32">
<div class="media-body small pl-2"> <div class="media-body small pl-2">
<!-- 标题 --> <!-- 标题 -->
<a th:href="@{'/blog/read/'+${blog.getBid()}}" <a id="maxLength" th:href="@{'/blog/read/'+${blog.getBid()}}"
class="text-dark font-weight-bold text-decoration-none"> class="text-dark font-weight-bold text-decoration-none">
<span th:if="${blog.getSort() == 1}" class="badge badge-danger">置顶</span> <span th:if="${blog.getSort() == 1}" class="badge badge-danger">置顶</span>
[[${blog.getTitle()}]] [[${blog.getTitle()}]]

View File

@@ -17,7 +17,7 @@
<div class="jumbotron" style="background-color: #ffffff;"> <div class="jumbotron" style="background-color: #ffffff;">
<div class="container"> <div class="container">
<h1 class="display-3">关于我们 <h1 class="display-3">关于我们
<a style="font-size: 12px;" href="javascript:donate();" class="badge badge-pill badge-danger text-decoration-none">赞助服务器</a> <a style="font-size: 14px;" href="javascript:donate();" class="badge badge-danger text-decoration-none">赞助服务器</a>
</h1> </h1>
<div class="alert alert-primary mt-3" role="alert"> <div class="alert alert-primary mt-3" role="alert">
广罗优质资源,服务优质的你! 广罗优质资源,服务优质的你!

View File

@@ -0,0 +1,99 @@
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>论坛-Quinn</title>
<link rel="stylesheet" th:href="@{/bootstrap/css/bootstrap.min.css}">
<style>
#maxLength {
max-width: 500px;
white-space: nowrap; /* 不换行 */
overflow: hidden; /* 超出部分不显示 */
text-overflow: ellipsis; /* 超出部分显示为... */
}
</style>
</head>
<body style="background: #f2f2f2;">
<div th:replace="~{common/header::header(activeUrl='blog')}"></div>
<main role="main" class="container">
<div class="row">
<div class="col-md-12 blog-main">
<div class="my-3 p-3 bg-white rounded shadow-sm">
<div th:fragment="user_table_refresh" th:id="id_user_table_refresh" >
<h6 class="border-bottom border-gray pb-2 mb-0">
论坛累计:
<span th:text="${pageParam.getTotal()}"></span>
</h6>
<div th:each="star:${starList}" class="media text-muted pt-1">
<p class="media-body pb-1 mb-0 small lh-125 border-bottom border-gray" style="margin-left: 5px">
<!-- 标题 -->
<a th:text="${star.getTopicName()}"
th:href="@{${star.getTopicCategory()=='BLOG'?'/blog/read/':'/source/view/'} + ${star.getTopicId()}}"
class="text-dark font-weight-bold text-decoration-none d-block">
</a>
<!-- 类型 -->
<span th:text="${star.getTopicCategory()=='BLOG'?'论坛':'资源'}"
th:class="${star.getTopicCategory()=='BLOG'?'badge badge-success':'badge badge-primary'}">
</span>
<!-- 时间 -->
<span th:text="${#dates.format(star.getGmtCreate(),'yyyy-MM-dd HH:mm:ss')}"
class="float-right">
</span>
</p>
</div>
<!--分页-->
<nav aria-label="Page navigation example" class="mt-4">
<ul class="pagination justify-content-center pagination-sm">
<li th:class="${pageParam.hasPrevious()==true?'page-item':'page-item disabled'}">
<a class="page-link" href="javascript:navChange(-1);" tabindex="">Previous</a>
</li>
<li class="page-item" th:if="${pageParam.hasPrevious()}">
<a class="page-link" href="javascript:navChange(-1);" th:text="${pageParam.getCurrent()-1}"></a>
</li>
<li class="page-item active">
<a id = "current" class="page-link" href="javascript:navChange(0);" th:text="${pageParam.getCurrent()}"></a>
</li>
<li class="page-item" th:if="${pageParam.hasNext()}">
<a class="page-link" href="javascript:navChange(1);" th:text="${pageParam.getCurrent()+1}"></a>
</li>
<li th:class="${pageParam.hasNext()==true?'page-item':'page-item disabled'}">
<a class="page-link" href="javascript:navChange(1);">Next</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</main>
<div th:replace="~{common/footer::footer}"></div>
<a class="to-top">返回顶部</a>
<script th:src="@{/js/jquery-3.5.1.min.js}"></script>
<script th:src="@{/bootstrap/js/bootstrap.bundle.min.js}"></script>
<script th:src="@{/js/toTop.js}"></script>
<script th:src="@{/js/jquery-ui.min.js}"></script>
<script th:src="@{/live/js/addlive2d.js}"></script>
<script type="text/javascript">
function navChange(page){
var current = $('#current').text();
var pageNum = parseInt(current) + page;
$.ajax({
url: "/blog",
async: false,
type: "post",
data: {"pageNum": pageNum, "limit": 10},
success: function (data) {
$('#id_s_table_refresh').html(data);
}
});
}
</script>
</body>
</html>

View File

@@ -5,7 +5,14 @@
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>资源库-Quinn</title> <title>资源库-Quinn</title>
<link rel="stylesheet" th:href="@{/bootstrap/css/bootstrap.min.css}"> <link rel="stylesheet" th:href="@{/bootstrap/css/bootstrap.min.css}">
<style>
#maxLength {
max-width: 500px;
white-space: nowrap; /* 不换行 */
overflow: hidden; /* 超出部分不显示 */
text-overflow: ellipsis; /* 超出部分显示为... */
}
</style>
</head> </head>
<body> <body>
@@ -18,17 +25,20 @@
</div> </div>
<div class="row row-cols-1 row-cols-md-3 text-center"> <div class="row row-cols-1 row-cols-md-3 text-center">
<div class="col mb-4" th:each="source:${sourceList}"> <div class="col mb-3" th:each="source:${sourceList}">
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<h4 class="font-weight-normal"> <a class="text-dark font-weight-bold text-decoration-none" th:href="@{'/source/view/'+${source.getSid()}}">
<h4 class="font-weight-normal d-inline">
[[${source.getSourceName()}]] [[${source.getSourceName()}]]
<span style="font-size: 8px;" class="badge badge-pill badge-danger">Quinn</span>
</h4> </h4>
<span class="badge badge-danger">
[[${source.getCategoryName()}]]
</span>
</a>
</div> </div>
<div class="card-body"> <div class="card-body">
<p th:text="${source.getDetail()}"></p> <p id="maxLength" th:text="${source.getDetail()}"></p>
<a th:href="@{'/source/view/'+${source.getSid()}}" class="btn btn-sm btn-outline-success">查看详情</a>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -14,7 +14,7 @@
} }
</style> </style>
</head> </head>
<body> <body style="background: #f2f2f2;">
<div th:replace="~{common/header::header(activeUrl='source')}"></div> <div th:replace="~{common/header::header(activeUrl='source')}"></div>
@@ -36,7 +36,7 @@
[[${source.getSourceName()}]] [[${source.getSourceName()}]]
</a> </a>
<!-- 收藏 --> <!-- 收藏 -->
<span class="badge badge-dark"> <span class="badge badge-primary">
[[${source.getCategoryName()}]] [[${source.getCategoryName()}]]
( (
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" class="bi bi-heart-fill" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" class="bi bi-heart-fill" viewBox="0 0 16 16">
@@ -77,7 +77,7 @@
</div> </div>
</div> </div>
<aside class="col-md-3 blog-sidebar"> <aside class="col-md-3 blog-sidebar">
<div class="p-1 my-2 bg-white rounded"> <div class="p-4 my-3 bg-white rounded">
<strong class="mb-0"> <strong class="mb-0">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-bookmarks-fill" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-bookmarks-fill" viewBox="0 0 16 16">
<path d="M2 4a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v11.5a.5.5 0 0 1-.777.416L7 13.101l-4.223 2.815A.5.5 0 0 1 2 15.5V4z"/> <path d="M2 4a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v11.5a.5.5 0 0 1-.777.416L7 13.101l-4.223 2.815A.5.5 0 0 1 2 15.5V4z"/>
@@ -86,12 +86,12 @@
敲黑板 敲黑板
</strong> </strong>
<br/> <br/>
<span>如果发的帖子水分过大,可能会被封禁账号,请酌情发帖评论!</span> <span>按照资源名称进行模糊查询,如需含内容模糊,您可以使用【↑全站搜索↑】</span>
</div> </div>
<div class="p-1 my-2 bg-white rounded"> <div class="p-3 my-3 bg-white rounded">
<form action="javascript:navChange(0);" method="post"> <form action="javascript:navChange(0);" method="post">
<div class="form-row align-items-center"> <div class="form-row">
<div class="col-md-12 form-control-sm"> <div class="col-md-12 mt-2">
<label class="sr-only" for="name">KEYWORD</label> <label class="sr-only" for="name">KEYWORD</label>
<div class="input-group"> <div class="input-group">
<div class="input-group-prepend"> <div class="input-group-prepend">
@@ -100,7 +100,7 @@
<input type="text" class="form-control" id="name" name="name" placeholder="请输入关键字"/> <input type="text" class="form-control" id="name" name="name" placeholder="请输入关键字"/>
</div> </div>
</div> </div>
<div class="col-md-12 mt-3 form-control-sm"> <div class="col-md-12 mt-2">
<label class="sr-only" for="category">SOURCE-TYPE</label> <label class="sr-only" for="category">SOURCE-TYPE</label>
<div class="input-group"> <div class="input-group">
<div class="input-group-prepend"> <div class="input-group-prepend">
@@ -113,9 +113,12 @@
</div> </div>
</div> </div>
<div class="col-md-12 mt-3 form-control-sm"> <div class="col-md-12 mt-2">
<button type="submit" class="btn btn-primary btn-block">查询</button> <button type="submit" class="btn btn-primary btn-block">查询</button>
</div> </div>
<div class="col-md-12 mt-2">
<button onclick="clearAll(this)" type="submit" class="btn btn-primary btn-block">清空</button>
</div>
</div> </div>
</form> </form>
</div> </div>
@@ -147,6 +150,11 @@
} }
}); });
} }
function clearAll(btn){
$('#name').val('');
$('#category').val(-1);
this.submit();
}
</script> </script>
</body> </body>
</html> </html>

View File

@@ -20,10 +20,9 @@
<div class="source-post"> <div class="source-post">
<h2 class="source-post-title" th:text="${source.getSourceName()}"></h2> <h2 class="source-post-title" th:text="${source.getSourceName()}"></h2>
<p class="source-post-meta"> <p class="source-post-meta">
上传时间: <span class="badge-pill badge-primary" th:text="${source.getCategoryName()}">
</span>
<span th:text="${#dates.format(source.getGmtUpdate(),'yyyy-MM-dd HH:mm:ss')}"></span> <span th:text="${#dates.format(source.getGmtUpdate(),'yyyy-MM-dd HH:mm:ss')}"></span>
类别-
<span th:text="${source.getCategoryName()}"></span>
<span class="ml-1"> <span class="ml-1">
<a class="text-danger text-decoration-none" th:fragment="star_refresh" th:id="id_star_refresh" <a class="text-danger text-decoration-none" th:fragment="star_refresh" th:id="id_star_refresh"
href="javascript:starFun(this);"> href="javascript:starFun(this);">
@@ -33,7 +32,7 @@
<svg th:if="${starValue.isStar() != true}" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-heart" viewBox="0 0 16 16"> <svg th:if="${starValue.isStar() != true}" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-heart" viewBox="0 0 16 16">
<path d="m8 2.748-.717-.737C5.6.281 2.514.878 1.4 3.053c-.523 1.023-.641 2.5.314 4.385.92 1.815 2.834 3.989 6.286 6.357 3.452-2.368 5.365-4.542 6.286-6.357.955-1.886.838-3.362.314-4.385C13.486.878 10.4.28 8.717 2.01L8 2.748zM8 15C-7.333 4.868 3.279-3.04 7.824 1.143c.06.055.119.112.176.171a3.12 3.12 0 0 1 .176-.17C12.72-3.042 23.333 4.867 8 15z"/> <path d="m8 2.748-.717-.737C5.6.281 2.514.878 1.4 3.053c-.523 1.023-.641 2.5.314 4.385.92 1.815 2.834 3.989 6.286 6.357 3.452-2.368 5.365-4.542 6.286-6.357.955-1.886.838-3.362.314-4.385C13.486.878 10.4.28 8.717 2.01L8 2.748zM8 15C-7.333 4.868 3.279-3.04 7.824 1.143c.06.055.119.112.176.171a3.12 3.12 0 0 1 .176-.17C12.72-3.042 23.333 4.867 8 15z"/>
</svg> </svg>
收藏 [[${starValue.getTotal()}]]
</a> </a>
</span> </span>
<a href="javascript:downloadFile(this);" class="btn btn-outline-success col-md-2 float-right">下载</a> <a href="javascript:downloadFile(this);" class="btn btn-outline-success col-md-2 float-right">下载</a>
@@ -134,6 +133,7 @@
async: false, async: false,
data: {"userId": userId, "topicId": topicId}, data: {"userId": userId, "topicId": topicId},
success: function (data) { success: function (data) {
console.log(data);
$('#id_star_refresh').html(data); $('#id_star_refresh').html(data);
} }
}); });