From b8de8e31dc38b731306de297f6dcff52bebefe25 Mon Sep 17 00:00:00 2001 From: Naccl Date: Wed, 24 Mar 2021 22:52:54 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=AE=8C=E6=88=90=E7=9F=AD=E9=93=BE?= =?UTF-8?q?=E6=8E=A5=E7=94=9F=E6=88=90=E5=9F=BA=E6=9C=AC=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + pom.xml | 10 ++++ .../naccl/dwz/controller/IndexController.java | 47 +++++++++++++++ src/main/java/top/naccl/dwz/entity/R.java | 40 +++++++++++++ .../java/top/naccl/dwz/entity/UrlMap.java | 30 ++++++++++ .../java/top/naccl/dwz/mapper/UrlMapper.java | 18 ++++++ .../top/naccl/dwz/service/UrlService.java | 7 +++ .../dwz/service/impl/UrlServiceImpl.java | 60 +++++++++++++++++++ .../java/top/naccl/dwz/util/HashUtils.java | 33 ++++++++++ .../java/top/naccl/dwz/util/UrlUtils.java | 16 +++++ src/main/resources/application-dev.properties | 19 ++++++ src/main/resources/application.properties | 2 + src/main/resources/mapper/UrlMapper.xml | 11 ++++ src/main/resources/static/css/base.css | 32 ++++++++++ src/main/resources/templates/index.html | 36 +++++++++++ 15 files changed, 363 insertions(+) create mode 100644 src/main/java/top/naccl/dwz/controller/IndexController.java create mode 100644 src/main/java/top/naccl/dwz/entity/R.java create mode 100644 src/main/java/top/naccl/dwz/entity/UrlMap.java create mode 100644 src/main/java/top/naccl/dwz/mapper/UrlMapper.java create mode 100644 src/main/java/top/naccl/dwz/service/UrlService.java create mode 100644 src/main/java/top/naccl/dwz/service/impl/UrlServiceImpl.java create mode 100644 src/main/java/top/naccl/dwz/util/HashUtils.java create mode 100644 src/main/java/top/naccl/dwz/util/UrlUtils.java create mode 100644 src/main/resources/application-dev.properties create mode 100644 src/main/resources/mapper/UrlMapper.xml create mode 100644 src/main/resources/static/css/base.css create mode 100644 src/main/resources/templates/index.html diff --git a/.gitignore b/.gitignore index 549e00a..1064884 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,5 @@ build/ ### VS Code ### .vscode/ + +application-prod.properties \ No newline at end of file diff --git a/pom.xml b/pom.xml index f7c8cc2..284b7b9 100644 --- a/pom.xml +++ b/pom.xml @@ -30,6 +30,16 @@ org.springframework.boot spring-boot-starter-web + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 2.1.4 + + + cn.hutool + hutool-all + 5.6.1 + org.springframework.boot diff --git a/src/main/java/top/naccl/dwz/controller/IndexController.java b/src/main/java/top/naccl/dwz/controller/IndexController.java new file mode 100644 index 0000000..f8f1bf5 --- /dev/null +++ b/src/main/java/top/naccl/dwz/controller/IndexController.java @@ -0,0 +1,47 @@ +package top.naccl.dwz.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import top.naccl.dwz.entity.R; +import top.naccl.dwz.service.UrlService; +import top.naccl.dwz.util.HashUtils; +import top.naccl.dwz.util.UrlUtils; + +/** + * @Description: + * @Author: Naccl + * @Date: 2021-03-21 + */ +@Controller +public class IndexController { + @Autowired + UrlService urlService; + private static String host; + + @Value("${server.host}") + public void setHost(String host) { + this.host = host; + } + + @GetMapping("/") + public String index() { + return "index"; + } + + @PostMapping("/generate") + @ResponseBody + public R generateShortURL(@RequestParam String longURL) { + if (UrlUtils.checkURL(longURL)) { + String shortURL = urlService.saveUrlMap(HashUtils.hashToBase62(longURL), longURL); + return R.ok("请求成功", host + shortURL); + } else { + return R.create(400, "URL有误"); + } + + } +} diff --git a/src/main/java/top/naccl/dwz/entity/R.java b/src/main/java/top/naccl/dwz/entity/R.java new file mode 100644 index 0000000..9ddbaa9 --- /dev/null +++ b/src/main/java/top/naccl/dwz/entity/R.java @@ -0,0 +1,40 @@ +package top.naccl.dwz.entity; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +/** + * @Description: 响应结果封装 + * @Author: Naccl + * @Date: 2021-03-21 + */ +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@ToString +public class R { + private Integer code; + private String msg; + private Object data; + + public R(Integer code, String msg) { + this.code = code; + this.msg = msg; + } + + public static R ok(String msg, Object data) { + return new R(200, msg, data); + } + + public static R create(Integer code, String msg, Object data) { + return new R(code, msg, data); + } + + public static R create(Integer code, String msg) { + return new R(code, msg); + } +} diff --git a/src/main/java/top/naccl/dwz/entity/UrlMap.java b/src/main/java/top/naccl/dwz/entity/UrlMap.java new file mode 100644 index 0000000..ba1c266 --- /dev/null +++ b/src/main/java/top/naccl/dwz/entity/UrlMap.java @@ -0,0 +1,30 @@ +package top.naccl.dwz.entity; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +import java.util.Date; + +/** + * @Description: 长短链接映射 + * @Author: Naccl + * @Date: 2021-03-22 + */ +@NoArgsConstructor +@Getter +@Setter +@ToString +public class UrlMap { + private Long id; + private String surl;//短链接 + private String lurl;//长链接 + private Date createTime;//创建时间 + + public UrlMap(String surl, String lurl, Date createTime) { + this.surl = surl; + this.lurl = lurl; + this.createTime = createTime; + } +} diff --git a/src/main/java/top/naccl/dwz/mapper/UrlMapper.java b/src/main/java/top/naccl/dwz/mapper/UrlMapper.java new file mode 100644 index 0000000..9e07a91 --- /dev/null +++ b/src/main/java/top/naccl/dwz/mapper/UrlMapper.java @@ -0,0 +1,18 @@ +package top.naccl.dwz.mapper; + +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; +import top.naccl.dwz.entity.UrlMap; + +/** + * @Description: 长短链接映射持久层接口 + * @Author: Naccl + * @Date: 2021-03-22 + */ +@Mapper +@Repository +public interface UrlMapper { + String getLongUrlByShortUrl(String surl); + + int saveUrlMap(UrlMap urlMap); +} diff --git a/src/main/java/top/naccl/dwz/service/UrlService.java b/src/main/java/top/naccl/dwz/service/UrlService.java new file mode 100644 index 0000000..526f3bc --- /dev/null +++ b/src/main/java/top/naccl/dwz/service/UrlService.java @@ -0,0 +1,7 @@ +package top.naccl.dwz.service; + +public interface UrlService { + String getLongUrlByShortUrl(String shortURL); + + String saveUrlMap(String shortURL, String longURL); +} diff --git a/src/main/java/top/naccl/dwz/service/impl/UrlServiceImpl.java b/src/main/java/top/naccl/dwz/service/impl/UrlServiceImpl.java new file mode 100644 index 0000000..b56b547 --- /dev/null +++ b/src/main/java/top/naccl/dwz/service/impl/UrlServiceImpl.java @@ -0,0 +1,60 @@ +package top.naccl.dwz.service.impl; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.stereotype.Service; +import top.naccl.dwz.entity.UrlMap; +import top.naccl.dwz.mapper.UrlMapper; +import top.naccl.dwz.service.UrlService; +import top.naccl.dwz.util.HashUtils; + +import java.util.Date; + +/** + * @Description: 长短链接映射业务层实现 + * @Author: Naccl + * @Date: 2021-03-22 + */ +@Service +public class UrlServiceImpl implements UrlService { + @Autowired + UrlMapper urlMapper; + private static final String DUPLICATE = " *"; + + @Override + public String getLongUrlByShortUrl(String shortURL) { + return urlMapper.getLongUrlByShortUrl(shortURL).replace(DUPLICATE, ""); + } + + @Override + public String saveUrlMap(String shortURL, String longURL) { + //在布隆过滤器中查找是否存在 + if (judgeExist()) { + //存在,在长链接后加上指定字符串,重新hash + longURL += DUPLICATE; + shortURL = HashUtils.hashToBase62(longURL); + shortURL = saveUrlMap(shortURL, longURL); + return shortURL; + } else { + //不存在,直接存入数据库 + try { + urlMapper.saveUrlMap(new UrlMap(shortURL, longURL, new Date())); + } catch (Exception e) { + if (e instanceof DuplicateKeyException) { + //数据库已经存在此短链接,则可能是布隆过滤器误判,在长链接后加上指定字符串,重新hash + longURL += DUPLICATE; + shortURL = HashUtils.hashToBase62(longURL); + shortURL = saveUrlMap(shortURL, longURL); + return shortURL; + } else { + throw e; + } + } + } + return shortURL; + } + + public boolean judgeExist() { + return false; + } +} diff --git a/src/main/java/top/naccl/dwz/util/HashUtils.java b/src/main/java/top/naccl/dwz/util/HashUtils.java new file mode 100644 index 0000000..0933e7c --- /dev/null +++ b/src/main/java/top/naccl/dwz/util/HashUtils.java @@ -0,0 +1,33 @@ +package top.naccl.dwz.util; + +import cn.hutool.core.lang.hash.MurmurHash; + +/** + * @Description: URL hash并转换base62 + * @Author: Naccl + * @Date: 2021-03-22 + */ +public class HashUtils { + private static char[] CHARS = new char[]{ + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' + }; + private static int SIZE = CHARS.length; + + private static String convertDecToBase62(long num) { + StringBuilder sb = new StringBuilder(); + while (num > 0) { + int i = (int) (num % SIZE); + sb.append(CHARS[i]); + num /= SIZE; + } + return sb.reverse().toString(); + } + + public static String hashToBase62(String str) { + int i = MurmurHash.hash32(str); + long num = i < 0 ? Integer.MAX_VALUE - (long) i : i; + return convertDecToBase62(num); + } +} \ No newline at end of file diff --git a/src/main/java/top/naccl/dwz/util/UrlUtils.java b/src/main/java/top/naccl/dwz/util/UrlUtils.java new file mode 100644 index 0000000..7fa20e9 --- /dev/null +++ b/src/main/java/top/naccl/dwz/util/UrlUtils.java @@ -0,0 +1,16 @@ +package top.naccl.dwz.util; + +import java.util.regex.Pattern; + +/** + * @Description: URL校验 + * @Author: Naccl + * @Date: 2021-03-24 + */ +public class UrlUtils { + private static final Pattern URL_REG = Pattern.compile("^(((ht|f)tps?):\\/\\/)?[\\w-]+(\\.[\\w-]+)+([\\w.,@?^=%&:/~+#-]*[\\w@?^=%&/~+#-])?$"); + + public static boolean checkURL(String url) { + return URL_REG.matcher(url).matches(); + } +} diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties new file mode 100644 index 0000000..3e142cd --- /dev/null +++ b/src/main/resources/application-dev.properties @@ -0,0 +1,19 @@ +server.port=8060 +server.host=http://localhost:8060/ + +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver +spring.datasource.url=jdbc:mysql://localhost:3306/dwz?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8 +spring.datasource.username=root +spring.datasource.password=root + +spring.redis.host=192.168.17.132 +spring.redis.password=123456 +spring.redis.port=6379 +spring.redis.database=1 +spring.redis.timeout=10000ms + +mybatis.mapper-locations=classpath:mapper/*.xml +mybatis.configuration.map-underscore-to-camel-case=true + +logging.level.root=info +logging.level.top.naccl.dwz=debug \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8b13789..62b54ed 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,3 @@ +spring.thymeleaf.mode=HTML +spring.profiles.active=dev \ No newline at end of file diff --git a/src/main/resources/mapper/UrlMapper.xml b/src/main/resources/mapper/UrlMapper.xml new file mode 100644 index 0000000..652fee1 --- /dev/null +++ b/src/main/resources/mapper/UrlMapper.xml @@ -0,0 +1,11 @@ + + + + + + + insert into url_map (surl, lurl, create_time) values (#{surl}, #{lurl}, #{createTime}) + + \ No newline at end of file diff --git a/src/main/resources/static/css/base.css b/src/main/resources/static/css/base.css new file mode 100644 index 0000000..5f78068 --- /dev/null +++ b/src/main/resources/static/css/base.css @@ -0,0 +1,32 @@ +body { + box-sizing: border-box; + width: 100vw; + height: 100vh; + margin: 0; + padding: 0; + background: linear-gradient( + 135deg, + hsl(170, 80%, 70%), + hsl(190, 80%, 70%), + hsl(250, 80%, 70%), + hsl(320, 80%, 70%) + ); + background-size: 200% 200%; + animation: gradient-move 15s ease alternate infinite; +} + +@keyframes gradient-move { + 0% { + background-position: 0% 0%; + } + 100% { + background-position: 100% 100%; + } +} + +.box { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} \ No newline at end of file diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html new file mode 100644 index 0000000..fbc9b55 --- /dev/null +++ b/src/main/resources/templates/index.html @@ -0,0 +1,36 @@ + + + + + 短链接生成 + + + +
+ + + +
+ + + + \ No newline at end of file