diff --git a/README.md b/README.md
index af05177..442bf08 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,5 @@
-# vertical
-vertical [vərtikəl],取纵横之`纵`,寓为奔放自如、笔意纵横
-## 开发计划
-### 已完成
+> 我们正在构建一个即严谨又活泼、专业又不失有趣的开源嵌入式知识平台。在这里我们可以畅所欲言、以平等、自由的身份获取或分享知识。在这里共同学习、交流、进步、成长。
+## 已完成
- [x] 首页
- [x] 会员登录/注册
- [x] 文章发布/编辑/详情/删除
@@ -9,18 +7,30 @@ vertical [vərtikəl],取纵横之`纵`,寓为奔放自如、笔意纵横
- [x] 找回密码
- [x] 用户管理
- [x] 角色/权限管理 (2019/12/05 21:52 更新)
-- [x] 专题管理 (2019/12/05 21:52 更新)
-- [x] 标签管理 (2019/12/26 00:11 更新)
-- [x] 专题-标签管理 (2019/12/26 00:11 更新)
-- [x] 用户-标签管理 (2019/12/26 00:11 更新)
-### 待完成
-- [ ] 个人信息修改
-- [ ] 消息系统
-- [ ] 评论系统
+- [x] 专题管理 (2019/12/05 21:52 更新)
+- [x] 标签管理 (2019/12/26 00:11 更新)
+- [x] 专题-标签管理 (2019/12/26 00:11 更新)
+- [x] 用户-标签管理 (2019/12/26 00:11 更新)
+- [x] 个人信息修改 (2020/01/09 14:20 更新)
+- [ ] 消息系统
+ - [x] 系统公告 (2020/03/12 14:20 更新)
+ - [x] 回帖提醒 (2020/03/12 14:20 更新)
+ - [ ] 关注提醒 (2020/03/12 14:20 更新)
+- [x] 评论系统 (2020/03/12 14:20 更新)
+- [x] 我的草稿 (2020/03/16 00:20 更新)
+- [x] 分享功能
+ - [x] 分享链接 (2020/03/16 12:20 更新)
+ - [x] 分享至微信 (2020/03/16 12:20 更新)
+## 待完成
- [ ] SEO 优化
-- [ ] 分享功能
-- [ ] 数据统计
-### 构想
+- [ ] 关注功能
+ - [ ] 关注用户
+ - [ ] 关注文章
+ - [ ] 关注主题
+ - [ ] 关注标签
+- [ ] 数据统计
+- [ ] 作品集功能
+## 构想
- [ ] 专业知识题库
- [ ] 社区贡献系统
- [ ] 会员系统
diff --git a/pom.xml b/pom.xml
index 27c3657..811dd49 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,6 +24,11 @@
org.springframework.boot
spring-boot-starter-data-redis
+
+ redis.clients
+ jedis
+ 2.9.3
+
org.springframework.boot
spring-boot-starter-mail
@@ -123,6 +128,7 @@
commons-codec
commons-codec
+ 1.14
io.jsonwebtoken
diff --git a/src/main/java/com/rymcu/vertical/service/ArticleService.java b/src/main/java/com/rymcu/vertical/service/ArticleService.java
index a901362..30325a3 100644
--- a/src/main/java/com/rymcu/vertical/service/ArticleService.java
+++ b/src/main/java/com/rymcu/vertical/service/ArticleService.java
@@ -26,10 +26,10 @@ public interface ArticleService extends Service {
/**
* 查询文章详情信息
* @param id
- * @param i
+ * @param type
* @return
* */
- ArticleDTO findArticleDTOById(Integer id, int i);
+ ArticleDTO findArticleDTOById(Integer id, Integer type);
/**
* 查询主题下文章列表
@@ -74,4 +74,17 @@ public interface ArticleService extends Service {
* @param id
*/
void incrementArticleViewCount(Integer id);
+
+ /**
+ * 获取分享链接数据
+ * @param id
+ * @return
+ */
+ Map share(Integer id) throws BaseApiException;
+
+ /**
+ * 查询草稿文章类别
+ * @return
+ */
+ List findDrafts() throws BaseApiException;
}
diff --git a/src/main/java/com/rymcu/vertical/service/CommentService.java b/src/main/java/com/rymcu/vertical/service/CommentService.java
new file mode 100644
index 0000000..5324a42
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/service/CommentService.java
@@ -0,0 +1,16 @@
+package com.rymcu.vertical.service;
+
+import com.rymcu.vertical.core.service.Service;
+import com.rymcu.vertical.dto.CommentDTO;
+import com.rymcu.vertical.entity.Comment;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+import java.util.Map;
+
+public interface CommentService extends Service {
+
+ List getArticleComments(Integer idArticle);
+
+ Map postComment(Comment comment, HttpServletRequest request);
+}
diff --git a/src/main/java/com/rymcu/vertical/service/TagService.java b/src/main/java/com/rymcu/vertical/service/TagService.java
index 7c0f4c4..6664bbb 100644
--- a/src/main/java/com/rymcu/vertical/service/TagService.java
+++ b/src/main/java/com/rymcu/vertical/service/TagService.java
@@ -1,11 +1,13 @@
package com.rymcu.vertical.service;
import com.rymcu.vertical.core.service.Service;
+import com.rymcu.vertical.dto.LabelModel;
import com.rymcu.vertical.entity.Article;
import com.rymcu.vertical.entity.Tag;
import com.rymcu.vertical.web.api.exception.BaseApiException;
import java.io.UnsupportedEncodingException;
+import java.util.List;
import java.util.Map;
/**
@@ -34,4 +36,10 @@ public interface TagService extends Service {
* @return
*/
Map saveTag(Tag tag);
+
+ /**
+ * 获取标签列表
+ * @return
+ */
+ List findTagLabels();
}
diff --git a/src/main/java/com/rymcu/vertical/service/impl/CommentServiceImpl.java b/src/main/java/com/rymcu/vertical/service/impl/CommentServiceImpl.java
new file mode 100644
index 0000000..f7268e1
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/service/impl/CommentServiceImpl.java
@@ -0,0 +1,114 @@
+package com.rymcu.vertical.service.impl;
+
+import com.rymcu.vertical.core.constant.NotificationConstant;
+import com.rymcu.vertical.core.service.AbstractService;
+import com.rymcu.vertical.dto.Author;
+import com.rymcu.vertical.dto.CommentDTO;
+import com.rymcu.vertical.entity.Article;
+import com.rymcu.vertical.entity.Comment;
+import com.rymcu.vertical.mapper.CommentMapper;
+import com.rymcu.vertical.service.ArticleService;
+import com.rymcu.vertical.service.CommentService;
+import com.rymcu.vertical.util.Html2TextUtil;
+import com.rymcu.vertical.util.NotificationUtils;
+import com.rymcu.vertical.util.Utils;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.*;
+
+/**
+ * @author ronger
+ */
+@Service
+public class CommentServiceImpl extends AbstractService implements CommentService {
+
+ @Resource
+ private CommentMapper commentMapper;
+ @Resource
+ private ArticleService articleService;
+
+ private static final int MAX_PREVIEW = 200;
+
+ @Override
+ public List getArticleComments(Integer idArticle) {
+ List commentDTOList = commentMapper.selectArticleComments(idArticle);
+ commentDTOList.forEach(commentDTO -> {
+ commentDTO.setTimeAgo(Utils.getTimeAgo(commentDTO.getCreatedTime()));
+ if (commentDTO.getCommentAuthorId() != null) {
+ Author author = commentMapper.selectAuthor(commentDTO.getCommentAuthorId());
+ if (author != null) {
+ commentDTO.setCommenter(author);
+ }
+ }
+ if (commentDTO.getCommentOriginalCommentId() != null) {
+ Author commentOriginalAuthor = commentMapper.selectCommentOriginalAuthor(commentDTO.getCommentOriginalCommentId());
+ if (commentOriginalAuthor != null) {
+ commentDTO.setCommentOriginalAuthorThumbnailURL(commentOriginalAuthor.getUserAvatarURL());
+ commentDTO.setCommentOriginalAuthorNickname(commentOriginalAuthor.getUserNickname());
+ }
+ }
+ });
+ return commentDTOList;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Map postComment(Comment comment, HttpServletRequest request) {
+ Map map = new HashMap(1);
+ if(comment.getCommentArticleId() == null){
+ map.put("message","非法访问,文章主键异常!");
+ return map;
+ }
+ if(comment.getCommentAuthorId() == null){
+ map.put("message","非法访问,用户未登录!");
+ return map;
+ }
+ if(StringUtils.isBlank(comment.getCommentContent())){
+ map.put("message","回帖内容不能为空!");
+ return map;
+ }
+ Article article = articleService.findById(comment.getCommentArticleId().toString());
+ if (article == null) {
+ map.put("message","文章不存在!");
+ return map;
+ }
+ String ip = Utils.getIpAddress(request);
+ String ua = request.getHeader("user-agent");
+ comment.setCommentIP(ip);
+ comment.setCommentUA(ua);
+ comment.setCreatedTime(new Date());
+ commentMapper.insertSelective(comment);
+ StringBuilder commentSharpUrl = new StringBuilder(article.getArticlePermalink());
+ commentSharpUrl.append("/comment/").append(comment.getIdComment());
+ commentMapper.updateCommentSharpUrl(comment.getIdComment(), commentSharpUrl.toString());
+
+ String commentContent = comment.getCommentContent();
+ if(StringUtils.isNotBlank(commentContent)){
+ Integer length = commentContent.length();
+ if(length > MAX_PREVIEW){
+ length = 200;
+ }
+ String commentPreviewContent = commentContent.substring(0,length);
+ commentContent = Html2TextUtil.getContent(commentPreviewContent);
+ // 评论者不是作者本人则进行消息通知
+ if (!article.getArticleAuthorId().equals(comment.getCommentAuthorId())) {
+ NotificationUtils.saveNotification(article.getArticleAuthorId(),comment.getIdComment(), NotificationConstant.Comment, commentContent);
+ }
+ // 判断是否是回复消息
+ if (comment.getCommentOriginalCommentId() != null && comment.getCommentOriginalCommentId() != 0) {
+ Comment originalComment = commentMapper.selectByPrimaryKey(comment.getCommentOriginalCommentId());
+ // 回复消息时,评论者不是上级评论作者则进行消息通知
+ if (!comment.getCommentAuthorId().equals(originalComment.getCommentAuthorId())) {
+ NotificationUtils.saveNotification(originalComment.getCommentAuthorId(),comment.getIdComment(), NotificationConstant.Comment, commentContent);
+ }
+ }
+ }
+
+
+ return map;
+ }
+}
diff --git a/src/main/java/com/rymcu/vertical/service/impl/JavaMailServiceImpl.java b/src/main/java/com/rymcu/vertical/service/impl/JavaMailServiceImpl.java
index c930885..7c3bc57 100644
--- a/src/main/java/com/rymcu/vertical/service/impl/JavaMailServiceImpl.java
+++ b/src/main/java/com/rymcu/vertical/service/impl/JavaMailServiceImpl.java
@@ -63,7 +63,8 @@ public class JavaMailServiceImpl implements JavaMailService {
private Integer sendCode(String to, Integer type) throws MessagingException {
Properties props = new Properties();
// 表示SMTP发送邮件,需要进行身份验证
- props.put("mail.smtp.auth", "true");
+ props.put("mail.smtp.auth", true);
+ props.put("mail.smtp.ssl.enable", true);
props.put("mail.smtp.host", SERVER_HOST);
props.put("mail.smtp.port", SERVER_PORT);
// 如果使用ssl,则去掉使用25端口的配置,进行如下配置,
diff --git a/src/main/java/com/rymcu/vertical/task/BaiduCronTask.java b/src/main/java/com/rymcu/vertical/task/BaiduCronTask.java
index b261652..1094982 100644
--- a/src/main/java/com/rymcu/vertical/task/BaiduCronTask.java
+++ b/src/main/java/com/rymcu/vertical/task/BaiduCronTask.java
@@ -1,24 +1,32 @@
package com.rymcu.vertical.task;
+import com.rymcu.vertical.core.constant.ProjectConstant;
import com.rymcu.vertical.util.BaiDuUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
+/**
+ * @author ronger
+ */
@Component
@Slf4j
public class BaiduCronTask {
@Value("${resource.domain}")
private String domain;
+ @Value(("${env}"))
+ private String env;
/**
* 定时推送首页更新
* */
@Scheduled(cron = "0 0 10,14,18 * * ?")
public void pushHome() {
- BaiDuUtils.updateSEOData(domain);
+ if (!ProjectConstant.ENV.equals(env)) {
+ BaiDuUtils.updateSEOData(domain);
+ }
}
}
diff --git a/src/main/java/com/rymcu/vertical/util/CacheUtils.java b/src/main/java/com/rymcu/vertical/util/CacheUtils.java
new file mode 100644
index 0000000..c532cd7
--- /dev/null
+++ b/src/main/java/com/rymcu/vertical/util/CacheUtils.java
@@ -0,0 +1,139 @@
+package com.rymcu.vertical.util;
+
+import org.apache.shiro.cache.Cache;
+import org.apache.shiro.cache.CacheManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Cache工具类
+ */
+public class CacheUtils {
+
+ private static Logger logger = LoggerFactory.getLogger(CacheUtils.class);
+ private static CacheManager cacheManager = SpringContextHolder.getBean(CacheManager.class);
+
+ private static final String SYS_CACHE = "system";
+
+ /**
+ * 获取SYS_CACHE缓存
+ * @param key
+ * @return
+ */
+ public static Object get(String key) {
+ return get(SYS_CACHE, key);
+ }
+
+ /**
+ * 获取SYS_CACHE缓存
+ * @param key
+ * @param defaultValue
+ * @return
+ */
+ public static Object get(String key, Object defaultValue) {
+ Object value = get(key);
+ return value != null ? value : defaultValue;
+ }
+
+ /**
+ * 写入SYS_CACHE缓存
+ * @param key
+ * @return
+ */
+ public static void put(String key, Object value) {
+ put(SYS_CACHE, key, value);
+ }
+
+ /**
+ * 从SYS_CACHE缓存中移除
+ * @param key
+ * @return
+ */
+ public static void remove(String key) {
+ remove(SYS_CACHE, key);
+ }
+
+ /**
+ * 获取缓存
+ * @param cacheName
+ * @param key
+ * @return
+ */
+ public static Object get(String cacheName, String key) {
+ return getCache(cacheName).get(getKey(key));
+ }
+
+ /**
+ * 获取缓存
+ * @param cacheName
+ * @param key
+ * @param defaultValue
+ * @return
+ */
+ public static Object get(String cacheName, String key, Object defaultValue) {
+ Object value = get(cacheName, getKey(key));
+ return value != null ? value : defaultValue;
+ }
+
+ /**
+ * 写入缓存
+ * @param cacheName
+ * @param key
+ * @param value
+ */
+ public static void put(String cacheName, String key, Object value) {
+ getCache(cacheName).put(getKey(key), value);
+ }
+
+ /**
+ * 从缓存中移除
+ * @param cacheName
+ * @param key
+ */
+ public static void remove(String cacheName, String key) {
+ getCache(cacheName).remove(getKey(key));
+ }
+
+ /**
+ * 从缓存中移除所有
+ * @param cacheName
+ */
+ public static void removeAll(String cacheName) {
+ Cache cache = getCache(cacheName);
+ Set keys = cache.keys();
+ for (Iterator it = keys.iterator(); it.hasNext();){
+ cache.remove(it.next());
+ }
+ logger.info("清理缓存: {} => {}", cacheName, keys);
+ }
+
+ /**
+ * 获取缓存键名,多数据源下增加数据源名称前缀
+ * @param key
+ * @return
+ */
+ private static String getKey(String key){
+// String dsName = DataSourceHolder.getDataSourceName();
+// if (StringUtils.isNotBlank(dsName)){
+// return dsName + "_" + key;
+// }
+ return key;
+ }
+
+ /**
+ * 获得一个Cache,没有则显示日志。
+ * @param cacheName
+ * @return
+ */
+ private static Cache getCache(String cacheName){
+ Cache cache = cacheManager.getCache(cacheName);
+ if (cache == null){
+ throw new RuntimeException("当前系统中没有定义“"+cacheName+"”这个缓存。");
+ }
+ return cache;
+ }
+
+}
diff --git a/src/main/java/com/rymcu/vertical/util/NotificationUtils.java b/src/main/java/com/rymcu/vertical/util/NotificationUtils.java
index 112f20b..7a42c0c 100644
--- a/src/main/java/com/rymcu/vertical/util/NotificationUtils.java
+++ b/src/main/java/com/rymcu/vertical/util/NotificationUtils.java
@@ -42,7 +42,7 @@ public class NotificationUtils {
},executor);
}
- private static void saveNotification(Integer idUser, Integer dataId, String dataType, String dataSummary) {
+ public static void saveNotification(Integer idUser, Integer dataId, String dataType, String dataSummary) {
ExecutorService executor= new ThreadPoolExecutor(1,1,0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
CompletableFuture.supplyAsync(()-> {
try {
diff --git a/src/main/java/com/rymcu/vertical/util/SpringContextHolder.java b/src/main/java/com/rymcu/vertical/util/SpringContextHolder.java
index e35b0d1..b5dbf14 100644
--- a/src/main/java/com/rymcu/vertical/util/SpringContextHolder.java
+++ b/src/main/java/com/rymcu/vertical/util/SpringContextHolder.java
@@ -92,6 +92,6 @@ public class SpringContextHolder implements ApplicationContextAware, DisposableB
* 检查ApplicationContext不为空.
*/
private static void assertContextInjected() {
- Validate.validState(applicationContext != null, "applicaitonContext属性未注入, 请在applicationContext.xml中定义SpringContextHolder.");
+ Validate.validState(applicationContext != null, "application Context属性未注入, 请在applicationContext.xml中定义SpringContextHolder.");
}
}
\ No newline at end of file
diff --git a/src/main/java/com/rymcu/vertical/web/api/article/ArticleController.java b/src/main/java/com/rymcu/vertical/web/api/article/ArticleController.java
index 631e1c5..13aab2c 100644
--- a/src/main/java/com/rymcu/vertical/web/api/article/ArticleController.java
+++ b/src/main/java/com/rymcu/vertical/web/api/article/ArticleController.java
@@ -1,9 +1,14 @@
package com.rymcu.vertical.web.api.article;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
import com.rymcu.vertical.core.result.GlobalResult;
import com.rymcu.vertical.core.result.GlobalResultGenerator;
import com.rymcu.vertical.dto.ArticleDTO;
+import com.rymcu.vertical.dto.CommentDTO;
import com.rymcu.vertical.service.ArticleService;
+import com.rymcu.vertical.service.CommentService;
+import com.rymcu.vertical.util.Utils;
import com.rymcu.vertical.web.api.exception.BaseApiException;
import org.springframework.web.bind.annotation.*;
@@ -11,6 +16,7 @@ import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/**
@@ -22,12 +28,12 @@ public class ArticleController {
@Resource
private ArticleService articleService;
-
-
+ @Resource
+ private CommentService commentService;
@GetMapping("/detail/{id}")
- public GlobalResult