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