Feat: [后台]:多模块架构完成,注册登录,书签备份html备份文件解析完成

This commit is contained in:
fanxb 2019-07-09 16:43:00 +08:00
parent 8cf9723149
commit 58e43d0f39
24 changed files with 522 additions and 40 deletions

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>bookmark-business</artifactId>
<groupId>com.fanxb</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>bookmark-business-bookmark</artifactId>
<dependencies>
<!--html文件解析-->
<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.12.1</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,50 @@
package com.fanxb.bookmark.business.bookmark.controller;
import com.fanxb.bookmark.business.bookmark.service.BookmarkService;
import com.fanxb.bookmark.common.entity.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
/**
* 类功能简述
* 类功能详述
*
* @author fanxb
* @date 2019/7/8 15:12
*/
@RestController
@RequestMapping("/bookmark")
public class BookmarkController {
@Autowired
private BookmarkService bookmarkService;
/**
* Description: 获取某个用户的节点树
*
* @return com.fanxb.bookmark.common.entity.Result
* @author fanxb
* @date 2019/7/9 14:20
*/
@GetMapping("/currentUser")
public Result getUserBookmarkTree() {
return null;
}
/**
* Description: 上传书签备份文件并解析
*
* @param file 文件
* @param path 存放节点路径(根节点为空字符串
* @return com.fanxb.bookmark.common.entity.Result
* @author fanxb
* @date 2019/7/8 15:17
*/
@PutMapping("/uploadBookmarkFile")
public Result uploadFile(@RequestParam("file") MultipartFile file, @RequestParam("path") String path) throws Exception {
bookmarkService.parseBookmarkFile(file.getInputStream(), path);
return Result.success(null);
}
}

View File

@ -0,0 +1,49 @@
package com.fanxb.bookmark.business.bookmark.dao;
import com.fanxb.bookmark.common.entity.Bookmark;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Component;
/**
* 类功能简述
* 类功能详述
*
* @author fanxb
* @date 2019/7/8 16:39
*/
@Component
public interface BookmarkDao {
/**
* Description: 插入一条书签记录
*
* @param node node
* @return void
* @author fanxb
* @date 2019/7/8 16:49
*/
void insertOne(Bookmark node);
/**
* Description: 根据用户名和name获取节点id
*
* @param userId 用户名
* @param name 姓名
* @param path 当前节点路径
* @return Integer
* @author fanxb
* @date 2019/7/8 17:18
*/
Integer selectIdByUserIdAndNameAndPath(@Param("userId") int userId, @Param("name") String name, @Param("path") String path);
/**
* Description:
*
* @param userId 用户id
* @param path 父节点路径
* @return java.lang.Integer
* @author fanxb
* @date 2019/7/8 17:35
*/
Integer selectMaxSort(@Param("userId") int userId, @Param("path") String path);
}

View File

@ -0,0 +1,113 @@
package com.fanxb.bookmark.business.bookmark.service;
import com.fanxb.bookmark.business.bookmark.dao.BookmarkDao;
import com.fanxb.bookmark.common.entity.Bookmark;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.InputStream;
/**
* 类功能简述
* 类功能详述
*
* @author fanxb
* @date 2019/7/8 15:00
*/
@Service
public class BookmarkService {
/**
* chrome导出书签tag
*/
private static final String DT = "dt";
private static final String A = "a";
@Autowired
private BookmarkDao bookmarkDao;
@Transactional(rollbackFor = Exception.class)
public void parseBookmarkFile(InputStream stream, String path) throws Exception {
Document doc = Jsoup.parse(stream, "utf-8", "");
Elements elements = doc.select("html>body>dl>dt");
//获取当前层sort最大值
Integer sortBase = bookmarkDao.selectMaxSort(1, path);
if (sortBase == null) {
sortBase = 0;
}
int count = 0;
for (int i = 0, length = elements.size(); i < length; i++) {
if (i == 0) {
Elements firstChildren = elements.get(0).child(1).children();
count = firstChildren.size();
for (int j = 0; j < count; j++) {
dealBookmark(firstChildren.get(j), path, sortBase + j);
}
} else {
dealBookmark(elements.get(i), path, sortBase + count + i - 1);
}
}
}
/**
* Description: 处理html节点解析出文件夹和书签
*
* @param ele 待处理节点
* @param path 节点路径不包含自身
* @param sort 当前层级中的排序序号
* @author fanxb
* @date 2019/7/8 14:49
*/
private void dealBookmark(Element ele, String path, int sort) {
if (!DT.equalsIgnoreCase(ele.tagName())) {
return;
}
Element first = ele.child(0);
if (A.equalsIgnoreCase(first.tagName())) {
//说明为链接
Bookmark node = new Bookmark(1, path, first.ownText(), first.attr("href"), first.attr("icon")
, Long.valueOf(first.attr("add_date")) * 1000, sort);
//存入数据库
insertOne(node);
} else {
//说明为文件夹
Bookmark node = new Bookmark(1, path, first.ownText(), Long.valueOf(first.attr("add_date")) * 1000, sort);
Integer sortBase = 0;
if (insertOne(node)) {
sortBase = bookmarkDao.selectMaxSort(node.getUserId(), path);
if (sortBase == null) {
sortBase = 0;
}
}
String childPath = path.length() == 0 ? node.getBookmarkId().toString() : path + "." + node.getBookmarkId();
Elements children = ele.child(1).children();
for (int i = 0, size = children.size(); i < size; i++) {
dealBookmark(children.get(i), childPath, sortBase + i + 1);
}
}
}
/**
* Description:
*
* @param node node
* @return boolean 如果已经存在返回true否则false
* @author fanxb
* @date 2019/7/8 17:25
*/
private boolean insertOne(Bookmark node) {
//先根据name,userId,parentId获取此节点id
Integer id = bookmarkDao.selectIdByUserIdAndNameAndPath(node.getUserId(), node.getName(), node.getPath());
if (id == null) {
bookmarkDao.insertOne(node);
return false;
} else {
node.setBookmarkId(id);
return true;
}
}
}

View File

@ -0,0 +1,32 @@
<?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.fanxb.bookmark.business.bookmark.dao.BookmarkDao">
<insert id="insertOne" useGeneratedKeys="true" keyColumn="bookmarkId" keyProperty="bookmarkId">
insert into bookmark(userId,path,type,name,url,icon,sort,addTime,createTime)
value
( #{userId},#{path},#{type},#{name},
<if test="url == null">
"","",
</if>
<if test="url != null">
#{url},#{icon},
</if>
#{sort},#{addTime},#{createTime})
</insert>
<select id="selectIdByUserIdAndNameAndPath" resultType="java.lang.Integer">
select bookmarkId
from bookmark
where userId = #{userId} and path = #{path} and name = #{name};
</select>
<select id="selectMaxSort" resultType="java.lang.Integer">
select max(sort)
from bookmark
where userId = #{userId} and path = #{path}
</select>
</mapper>

View File

@ -13,6 +13,7 @@
<packaging>pom</packaging>
<modules>
<module>user</module>
<module>bookmark</module>
</modules>
<dependencies>

View File

@ -4,7 +4,6 @@ import com.fanxb.bookmark.business.user.entity.LoginBody;
import com.fanxb.bookmark.business.user.entity.RegisterBody;
import com.fanxb.bookmark.business.user.service.UserService;
import com.fanxb.bookmark.common.entity.Result;
import com.fanxb.bookmark.common.entity.User;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@ -50,7 +49,6 @@ public class UserController {
return Result.success(userService.login(body));
}
/**
* Description: 获取验证码
*

View File

@ -26,12 +26,13 @@ public interface UserDao {
/**
* Description: 通过用户名或者email获取用户信息
*
* @param str username/email
* @param name username
* @param email email
* @return com.fanxb.bookmark.common.entity.User
* @author fanxb
* @date 2019/7/6 16:45
*/
User selectByUsernameOrEmail(String str);
User selectByUsernameOrEmail(@Param("name") String name,@Param("email") String email);
/**
* Description: 更新用户上次登录时间

View File

@ -75,7 +75,16 @@ public class UserService {
throw new CustomException("验证码错误");
}
RedisUtil.delete(codeKey);
User user = new User();
User user = userDao.selectByUsernameOrEmail(body.getUsername(), body.getEmail());
if (user != null) {
if (user.getUsername().equals(body.getUsername())) {
throw new FormDataException("用户名已经被注册");
}
if (user.getEmail().equals(body.getEmail())) {
throw new FormDataException("邮箱已经被注册");
}
}
user = new User();
user.setUsername(body.getUsername());
user.setEmail(body.getEmail());
user.setIcon(DEFAULT_ICON);
@ -94,7 +103,7 @@ public class UserService {
* @date 2019/7/6 16:37
*/
public LoginRes login(LoginBody body) {
User userInfo = userDao.selectByUsernameOrEmail(body.getStr());
User userInfo = userDao.selectByUsernameOrEmail(body.getStr(), body.getStr());
if (userInfo == null) {
throw new FormDataException("账号/密码错误");
}

View File

@ -18,7 +18,7 @@
password,
createTime
from user
where username = #{str} or email = #{str}
where username = #{name} or email = #{email}
</select>
<update id="updateLastLoginTime">

View File

@ -13,10 +13,7 @@
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--http请求工具依赖-->
@ -98,6 +95,9 @@
<version>1.2.56</version>
</dependency>
<!--单元测试-->
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
<dependency>

View File

@ -0,0 +1,26 @@
package com.fanxb.bookmark.common.dao;
import com.fanxb.bookmark.common.entity.Url;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 类功能简述
* 类功能详述
*
* @author fanxb
* @date 2019/7/9 14:47
*/
@Component
public interface UrlDao {
/**
* Description: 获取公共接口
*
* @author fanxb
* @date 2019/7/9 14:52
* @return java.util.List<com.fanxb.bookmark.common.entity.Url>
*/
List<Url> getPublicUrl();
}

View File

@ -0,0 +1,58 @@
package com.fanxb.bookmark.common.entity;
import lombok.Data;
import java.util.ArrayList;
/**
* 类功能简述书签树
* 类功能详述
*
* @author fanxb
* @date 2019/7/8 11:19
*/
@Data
public class Bookmark {
public static final int BOOKMARK_TYPE = 0;
public static final int FOLDER_TYPE = 1;
private Integer bookmarkId;
/**
* 类型0文件夹1具体的书签
*/
private int type;
private int userId;
private String path;
private String name;
private String url;
private String icon;
private int sort;
private long addTime;
private long createTime;
private ArrayList<Bookmark> children;
public Bookmark() {
}
public Bookmark(int userId, String path, String name, long addTime, int sort) {
this.setUserId(userId);
this.setPath(path);
this.setType(FOLDER_TYPE);
this.setName(name);
this.setAddTime(addTime);
this.setSort(sort);
this.setCreateTime(System.currentTimeMillis());
}
public Bookmark(int userId, String path, String name, String url, String icon, long addTime, int sort) {
this.setUserId(userId);
this.setPath(path);
this.setType(BOOKMARK_TYPE);
this.setName(name);
this.setUrl(url);
this.setIcon(icon);
this.setSort(sort);
this.setAddTime(addTime);
this.setCreateTime(System.currentTimeMillis());
}
}

View File

@ -0,0 +1,18 @@
package com.fanxb.bookmark.common.entity;
import lombok.Data;
/**
* 类功能简述
* 类功能详述
*
* @author fanxb
* @date 2019/7/9 14:47
*/
@Data
public class Url {
private int userId;
private String method;
private String url;
private int type;
}

View File

@ -12,7 +12,6 @@ import lombok.Data;
@Data
public class UserContext {
private User user;
private int userId;
private String jwt;
private String sessionId;
}

View File

@ -8,7 +8,9 @@ package com.fanxb.bookmark.common.exception;
* @date 2019/7/5 15:49
*/
public class NoLoginException extends CustomException {
NoLoginException() {
super("您尚未登录", -1, null);
public static final int CODE = -1;
public static final String MESSAGE= "您尚未登录";
public NoLoginException() {
super(MESSAGE, CODE, null);
}
}

View File

@ -1,12 +1,31 @@
package com.fanxb.bookmark.common.filter;
import com.alibaba.fastjson.JSON;
import com.auth0.jwt.interfaces.Claim;
import com.fanxb.bookmark.common.Constant;
import com.fanxb.bookmark.common.dao.UrlDao;
import com.fanxb.bookmark.common.entity.Result;
import com.fanxb.bookmark.common.entity.Url;
import com.fanxb.bookmark.common.entity.UserContext;
import com.fanxb.bookmark.common.exception.NoLoginException;
import com.fanxb.bookmark.common.util.JwtUtil;
import com.fanxb.bookmark.common.util.StringUtil;
import com.fanxb.bookmark.common.util.UserContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
* 类功能简述
@ -17,14 +36,26 @@ import java.io.IOException;
*/
@Component
@WebFilter(urlPatterns = "/*", filterName = "loginFilter")
@Slf4j
@Order(100)
@WebFilter(urlPatterns = "/*", filterName = "loginFilter")
@Order(1000)
public class LoginFilter implements Filter {
@Value("${server.servlet.context-path}")
private String urlPrefix;
@Value("${jwtSecret}")
private String secret;
@Autowired
private UrlDao urlDao;
private static AntPathMatcher matcher = new AntPathMatcher();
volatile private static List<Url> publicUrl;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
@ -34,6 +65,56 @@ public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
filterChain.doFilter(servletRequest, servletResponse);
UserContextHolder.remove();
List<Url> publicUrl = this.getPublicUrl();
HttpServletRequest request = (HttpServletRequest) servletRequest;
String requestUrl = request.getRequestURI().replace(urlPrefix, "");
String requestMethod = request.getMethod();
for (Url url : publicUrl) {
if (url.getMethod().equalsIgnoreCase(requestMethod) && matcher.match(url.getUrl(), requestUrl)) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
}
if (this.checkJwt(request.getHeader(Constant.JWT_KEY))) {
filterChain.doFilter(servletRequest, servletResponse);
} else {
HttpServletResponse response = (HttpServletResponse) servletResponse;
response.setStatus(HttpStatus.OK.value());
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
response.getWriter().write(JSON.toJSONString(new Result(NoLoginException.CODE, NoLoginException.MESSAGE, null)));
}
}
private List<Url> getPublicUrl() {
if (publicUrl == null) {
synchronized (LoginFilter.class) {
if (publicUrl == null) {
publicUrl = this.urlDao.getPublicUrl();
}
}
}
return publicUrl;
}
private boolean checkJwt(String jwt) {
if (StringUtil.isEmpty(jwt)) {
log.error("jwt为空");
return false;
}
try {
Map<String, Claim> map = JwtUtil.decode(jwt, secret);
int userId = Integer.valueOf(map.get("userId").asString());
UserContext context = new UserContext();
context.setJwt(jwt);
context.setUserId(userId);
UserContextHolder.set(context);
return true;
} catch (Exception e) {
log.error("jwt解密失败{}", jwt, e);
return false;
}
}
}

View File

@ -0,0 +1,13 @@
<?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.fanxb.bookmark.common.dao.UrlDao">
<select id="getPublicUrl" resultType="com.fanxb.bookmark.common.entity.Url">
select *
from url
where type = 0
</select>
</mapper>

View File

@ -26,4 +26,12 @@
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -12,15 +12,17 @@
<artifactId>bookmark-web</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.fanxb</groupId>
<artifactId>bookmark-business-user</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.fanxb</groupId>
<artifactId>bookmark-business-bookmark</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>

View File

@ -4,6 +4,7 @@ package com.fanxb.bookmark.web;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* 类功能简述
@ -12,8 +13,9 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
* @author fanxb
* @date 2019/7/4 19:38
*/
@SpringBootApplication(scanBasePackages = "com.fanxb.bookmark")
@SpringBootApplication(scanBasePackages = {"com.fanxb.bookmark"})
@MapperScan(basePackages = "com.fanxb.bookmark.**.dao")
@EnableTransactionManagement
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);

View File

@ -3,8 +3,14 @@ jwtSecret: abcdefgh
server:
port: 8088
servlet:
context-path: /bookmark/api/
# 不要在最后加/
context-path: /bookmark/api
spring:
servlet:
# 表单配置
multipart:
max-request-size: 10MB
max-file-size: 10MB
profiles:
active: dev
application:
@ -48,5 +54,7 @@ spring:
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl
mapper-locations: classpath:mapper/*.xml
log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
# classpath后面加*,值里面的*才起作用
mapper-locations: classpath*:mapper/*.xml
debug: false

View File

@ -1,13 +0,0 @@
CREATE TABLE `user` (
`userId` int UNSIGNED NOT NULL AUTO_INCREMENT,
`username` varchar(20) NOT NULL
COMMENT '用户名20位以内数字字母组合',
`email` varchar(40) NOT NULL,
`icon` varchar(50) NOT NULL,
`password` char(40) NOT NULL
COMMENT '明文6-20位数字密码组合+userId 进行sha1签名',
`createTime` bigint NOT NULL DEFAULT 0,
`lastLoginTime` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`userId`)
)
ENGINE = InnoDB;