✨ 用户浏览记录功能
This commit is contained in:
parent
07509807b9
commit
ca259f80ac
@ -1,12 +0,0 @@
|
||||
package com.rymcu.vertical.core.service;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 体检者日志接口
|
||||
*/
|
||||
public interface LogService {
|
||||
|
||||
//void log(LogInfo logInfo);
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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 {
|
||||
}
|
@ -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";
|
||||
|
||||
}
|
42
src/main/java/com/rymcu/vertical/entity/Visit.java
Normal file
42
src/main/java/com/rymcu/vertical/entity/Visit.java
Normal 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;
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
10
src/main/java/com/rymcu/vertical/mapper/VisitMapper.java
Normal file
10
src/main/java/com/rymcu/vertical/mapper/VisitMapper.java
Normal 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> {
|
||||
}
|
@ -68,4 +68,10 @@ public interface ArticleService extends Service<Article> {
|
||||
* @return
|
||||
* */
|
||||
Map delete(Integer id);
|
||||
|
||||
/**
|
||||
* 增量文章浏览数
|
||||
* @param id
|
||||
*/
|
||||
void incrementArticleViewCount(Integer id);
|
||||
}
|
||||
|
10
src/main/java/com/rymcu/vertical/service/VisitService.java
Normal file
10
src/main/java/com/rymcu/vertical/service/VisitService.java
Normal 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> {
|
||||
}
|
@ -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);
|
||||
|
@ -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 {
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
4
src/main/java/mapper/VisitMapper.xml
Normal file
4
src/main/java/mapper/VisitMapper.xml
Normal 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>
|
@ -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": {
|
||||
|
Loading…
Reference in New Issue
Block a user