用户浏览记录功能

This commit is contained in:
ronger 2020-01-14 14:24:18 +08:00
parent 07509807b9
commit ca259f80ac
18 changed files with 392 additions and 31 deletions

View File

@ -1,12 +0,0 @@
package com.rymcu.vertical.core.service;
/**
* 体检者日志接口
*/
public interface LogService {
//void log(LogInfo logInfo);
}

View File

@ -0,0 +1,104 @@
package com.rymcu.vertical.core.service.log;
import com.rymcu.vertical.core.service.log.constant.LoggerConstant;
import com.rymcu.vertical.dto.TokenUser;
import com.rymcu.vertical.entity.Visit;
import com.rymcu.vertical.jwt.def.JwtConstants;
import com.rymcu.vertical.service.ArticleService;
import com.rymcu.vertical.service.VisitService;
import com.rymcu.vertical.util.UserUtils;
import com.rymcu.vertical.util.Utils;
import com.rymcu.vertical.web.api.exception.BaseApiException;
import com.rymcu.vertical.web.api.exception.ErrorCode;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.HandlerMapping;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
/**
* 浏览
* @author ronger
*/
@Aspect
@Component
public class VisitAspect {
@Resource
private ArticleService articleService;
@Resource
private VisitService visitService;
@Pointcut("@annotation(com.rymcu.vertical.core.service.log.annotation.VisitLogger)")
public void pointCut() {}
/**
* 保存系统操作日志
*
* @param joinPoint 连接点
* @return 方法执行结果
* @throws Throwable 调用出错
*/
@AfterReturning(value = "pointCut()", returning="obj")
public void save(JoinPoint joinPoint, Object obj) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String ip = Utils.getIpAddress(request);
String url = request.getRequestURL().toString();
String ua = request.getHeader("user-agent");
String referer = request.getHeader("Referer");
String methodName = joinPoint.getSignature().getName();
Map params = getParams(request);
if (params.isEmpty()) {
params = (Map) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
} else {
params.putAll((Map) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE));
}
switch (methodName) {
case LoggerConstant.ARTICLE:
Integer id = Integer.parseInt(String.valueOf(params.get("id")));
articleService.incrementArticleViewCount(id);
break;
default:
break;
}
Visit visit = new Visit();
visit.setVisitUrl(url);
visit.setVisitIp(ip);
visit.setVisitUa(ua);
visit.setVisitCity("");
visit.setVisitDeviceId("");
visit.setVisitRefererUrl(referer);
visit.setCreatedTime(new Date());
String authHeader = request.getHeader(JwtConstants.AUTHORIZATION);
if(StringUtils.isNotBlank(authHeader)){
TokenUser tokenUser = UserUtils.getTokenUser(authHeader);
visit.setVisitUserId(tokenUser.getIdUser());
}
visitService.save(visit);
}
private Map<String, String> getParams(HttpServletRequest request) {
Map<String, String> paramsMap = new HashMap<>(10);
Enumeration<String> paraNames = request.getParameterNames();
while (paraNames.hasMoreElements()) {
String key = paraNames.nextElement();
if ("password".equals(key)) {
continue;
}
paramsMap.put(key, request.getParameter(key));
}
return paramsMap;
}
}

View File

@ -0,0 +1,12 @@
package com.rymcu.vertical.core.service.log.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* 浏览记录器
* @author ronger
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface VisitLogger {
}

View File

@ -0,0 +1,12 @@
package com.rymcu.vertical.core.service.log.constant;
/**
* @author ronger
*/
public class LoggerConstant {
public final static String ARTICLE = "article";
public final static String ARTICLES = "articles";
}

View File

@ -0,0 +1,42 @@
package com.rymcu.vertical.entity;
import lombok.Data;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.Date;
/**
* 浏览表
* @author ronger
*/
@Data
@Table(name="vertical_visit")
public class Visit implements Serializable,Cloneable {
/** 主键 */
@Id
@GeneratedValue(generator = "JDBC")
private Integer id;
/** 浏览链接 */
private String visitUrl;
/** IP */
private String visitIp;
/** User-Agent */
private String visitUa;
/** 城市 */
private String visitCity;
/** 设备唯一标识 */
private String visitDeviceId;
/** 浏览者 id */
private Integer visitUserId;
/** 上游链接 */
private String visitRefererUrl;
/** 创建时间 */
private Date createdTime;
/** 过期时间 */
private Date expiredTime;
}

View File

@ -32,4 +32,12 @@ public interface ArticleMapper extends Mapper<Article> {
Integer deleteTagArticle(@Param("id") Integer id);
List<ArticleTagDTO> selectTags(@Param("idArticle") Integer idArticle);
/**
*
* @param id
* @param articleViewCount
* @return
*/
Integer updateArticleViewCount(@Param("id") Integer id, @Param("articleViewCount") Integer articleViewCount);
}

View File

@ -0,0 +1,10 @@
package com.rymcu.vertical.mapper;
import com.rymcu.vertical.core.mapper.Mapper;
import com.rymcu.vertical.entity.Visit;
/**
* @author ronger
*/
public interface VisitMapper extends Mapper<Visit> {
}

View File

@ -68,4 +68,10 @@ public interface ArticleService extends Service<Article> {
* @return
* */
Map delete(Integer id);
/**
* 增量文章浏览数
* @param id
*/
void incrementArticleViewCount(Integer id);
}

View File

@ -0,0 +1,10 @@
package com.rymcu.vertical.service;
import com.rymcu.vertical.core.service.Service;
import com.rymcu.vertical.entity.Visit;
/**
* @author ronger
*/
public interface VisitService extends Service<Visit> {
}

View File

@ -154,6 +154,14 @@ public class ArticleServiceImpl extends AbstractService<Article> implements Arti
return map;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void incrementArticleViewCount(Integer id) {
Article article = articleMapper.selectByPrimaryKey(id);
Integer articleViewCount = article.getArticleViewCount() + 1;
articleMapper.updateArticleViewCount(article.getIdArticle(), articleViewCount);
}
private ArticleDTO genArticle(ArticleDTO article,Integer type) {
Author author = articleMapper.selectAuthor(article.getArticleAuthorId());
article.setArticleAuthor(author);

View File

@ -0,0 +1,13 @@
package com.rymcu.vertical.service.impl;
import com.rymcu.vertical.core.service.AbstractService;
import com.rymcu.vertical.entity.Visit;
import com.rymcu.vertical.service.VisitService;
import org.springframework.stereotype.Service;
/**
* @author ronger
*/
@Service
public class VisitServiceImpl extends AbstractService<Visit> implements VisitService {
}

View File

@ -10,6 +10,7 @@ import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.core.env.Environment;
import javax.servlet.http.HttpServletRequest;
import java.time.*;
import java.util.Date;
import java.util.HashMap;
@ -20,6 +21,7 @@ import java.util.Map;
*/
public class Utils {
public static final String HASH_ALGORITHM = "SHA-1";
public static final String UNKOWN = "unknown";
public static final int HASH_INTERATIONS = 1024;
public static final int SALT_SIZE = 8;
@ -155,4 +157,29 @@ public class Utils {
map.put("pagination", pagination);
return map;
}
public static String getIpAddress(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || UNKOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || UNKOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || UNKOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || UNKOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || UNKOWN.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
// 如果是多级代理那么取第一个ip为客户端ip
if (ip != null && ip.indexOf(",") != -1) {
ip = ip.substring(0, ip.indexOf(",")).trim();
}
return ip;
}
}

View File

@ -10,6 +10,7 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
/**
@ -22,6 +23,16 @@ public class ArticleController {
@Resource
private ArticleService articleService;
@GetMapping("/detail/{id}")
public GlobalResult<Map<String, Object>> detail(@PathVariable Integer id){
ArticleDTO articleDTO = articleService.findArticleDTOById(id,2);
Map map = new HashMap<>(1);
map.put("article", articleDTO);
return GlobalResultGenerator.genSuccessResult(map);
}
@PostMapping("/post")
public GlobalResult postArticle(@RequestBody ArticleDTO article, HttpServletRequest request) throws BaseApiException, UnsupportedEncodingException {
Map map = articleService.postArticle(article,request);

View File

@ -5,6 +5,7 @@ import com.github.pagehelper.PageInfo;
import com.rymcu.vertical.core.result.GlobalResult;
import com.rymcu.vertical.core.result.GlobalResultGenerator;
import com.rymcu.vertical.core.result.GlobalResultMessage;
import com.rymcu.vertical.core.service.log.annotation.VisitLogger;
import com.rymcu.vertical.dto.ArticleDTO;
import com.rymcu.vertical.dto.ForgetPasswordDTO;
import com.rymcu.vertical.dto.TokenUser;
@ -89,6 +90,7 @@ public class CommonApiController {
}
@GetMapping("/articles")
@VisitLogger
public GlobalResult<Map> articles(@RequestParam(defaultValue = "0") Integer page, @RequestParam(defaultValue = "10") Integer rows, @RequestParam(defaultValue = "") String searchText, @RequestParam(defaultValue = "") String tag){
PageHelper.startPage(page, rows);
List<ArticleDTO> list = articleService.findArticles(searchText,tag);
@ -98,21 +100,14 @@ public class CommonApiController {
}
@GetMapping("/article/{id}")
public GlobalResult<Map<String, Object>> detail(@PathVariable Integer id){
@VisitLogger
public GlobalResult<Map<String, Object>> article(@PathVariable Integer id){
ArticleDTO articleDTO = articleService.findArticleDTOById(id,1);
Map<String, Object> map = new HashMap<>(1);
map.put("article", articleDTO);
return GlobalResultGenerator.genSuccessResult(map);
}
@GetMapping("/update/{id}")
public GlobalResult<Map<String, Object>> update(@PathVariable Integer id){
ArticleDTO articleDTO = articleService.findArticleDTOById(id,2);
Map map = new HashMap<>(1);
map.put("article", articleDTO);
return GlobalResultGenerator.genSuccessResult(map);
}
@GetMapping("/token/{token}")
public GlobalResult<TokenUser> token(@PathVariable String token){
TokenUser tokenUser = UserUtils.getTokenUser(token);

View File

@ -4,6 +4,7 @@ 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.core.service.log.annotation.VisitLogger;
import com.rymcu.vertical.dto.ArticleDTO;
import com.rymcu.vertical.entity.Topic;
import com.rymcu.vertical.service.ArticleService;
@ -33,6 +34,7 @@ public class TopicController {
}
@GetMapping("/{name}")
@VisitLogger
public GlobalResult articles(@RequestParam(defaultValue = "0") Integer page, @RequestParam(defaultValue = "10") Integer rows, @PathVariable String name){
PageHelper.startPage(page, rows);
List<ArticleDTO> list = articleService.findArticlesByTopicUri(name);

View File

@ -63,6 +63,9 @@
<update id="updateArticleContent">
update vertical_article_content set article_content = #{articleContent},article_content_html = #{articleContentHtml},updated_time = sysdate() where id_article = #{idArticle}
</update>
<update id="updateArticleViewCount">
update vertical_article set article_view_count = #{articleViewCount} where id = #{id}
</update>
<delete id="deleteTagArticle">
delete from vertical_tag_article where id_article = #{id}
</delete>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.rymcu.vertical.mapper.VisitMapper">
</mapper>

View File

@ -98,16 +98,6 @@
"autoIncrement": false,
"defaultValue": ""
},
{
"name": "time_ago",
"type": "VARCHAR_32",
"chnname": "过去时长",
"remark": "",
"pk": false,
"notNull": false,
"autoIncrement": false,
"defaultValue": ""
},
{
"name": "article_permalink",
"type": "VARCHAR_128",
@ -1486,6 +1476,122 @@
}
],
"chnname": "关注表"
},
{
"title": "vertical_visit",
"fields": [
{
"name": "id",
"type": "BigInt",
"remark": "",
"chnname": "主键",
"pk": true,
"notNull": true,
"autoIncrement": true
},
{
"name": "visit_url",
"type": "VARCHAR_256",
"remark": "",
"chnname": "浏览链接"
},
{
"name": "visit_ip",
"type": "ShortString",
"remark": "",
"chnname": "IP"
},
{
"name": "visit_ua",
"type": "VARCHAR_256",
"remark": "",
"chnname": "User-Agent"
},
{
"name": "visit_city",
"type": "IdOrKey",
"remark": "",
"chnname": "城市"
},
{
"name": "visit_device_id",
"type": "VARCHAR_256",
"remark": "",
"chnname": "设备唯一标识"
},
{
"name": "visit_user_id",
"type": "BigInt",
"remark": "",
"chnname": "浏览者 id"
},
{
"name": "visit_referer_url",
"type": "VARCHAR_256",
"remark": "",
"chnname": "上游链接"
},
{
"name": "created_time",
"type": "DateTime",
"remark": "",
"chnname": "创建时间"
},
{
"name": "expired_time",
"type": "DateTime",
"remark": "",
"chnname": "过期时间"
}
],
"indexs": [],
"headers": [
{
"fieldName": "chnname",
"relationNoShow": false
},
{
"fieldName": "name",
"relationNoShow": false
},
{
"fieldName": "type",
"relationNoShow": false
},
{
"fieldName": "dataType",
"relationNoShow": true
},
{
"fieldName": "remark",
"relationNoShow": true
},
{
"fieldName": "pk",
"relationNoShow": false
},
{
"fieldName": "notNull",
"relationNoShow": true
},
{
"fieldName": "autoIncrement",
"relationNoShow": true
},
{
"fieldName": "defaultValue",
"relationNoShow": true
},
{
"fieldName": "relationNoShow",
"relationNoShow": true
},
{
"fieldName": "uiHint",
"relationNoShow": true
}
],
"chnname": "浏览表"
}
],
"graphCanvas": {