Browse Source

- Add
1. 增加自定义短链接口,前端支持自定义短链
2. 短链请求时自动续期1D,每天允许续期1次

CareyWong 5 years ago
parent
commit
cc716e3074
2 changed files with 55 additions and 12 deletions
  1. 49 11
      main.go
  2. 6 1
      public/index.html

+ 49 - 11
main.go

@@ -35,6 +35,11 @@ const defaultPort int = 8002
 const defaultExpire = 90
 const defaultRedisConfig = "127.0.0.1:6379"
 
+const defaultLockPrefix = "myurls:lock:"
+const defaultRenewal = 1
+
+const secondsPerDay = 24 * 3600
+
 var redisPool *redis.Pool
 var redisPoolConfig *redisPoolConf
 var redisClient redis.Conn
@@ -83,6 +88,7 @@ func main() {
 		}
 
 		longUrl := context.PostForm("longUrl")
+		shortKey := context.PostForm("shortKey")
 		if longUrl == "" {
 			res.Message = "longUrl为空"
 			context.JSON(400, *res)
@@ -91,18 +97,26 @@ func main() {
 
 		_longUrl, _ := base64.StdEncoding.DecodeString(longUrl)
 		longUrl = string(_longUrl)
+		res.LongUrl = longUrl
 
-		shortKey := longToShort(longUrl, *ttl*24*3600)
-		if shortKey == "" {
-			res.Code = 0
-			res.Message = "短链接生成失败"
-			context.JSON(500, *res)
-			return
-		}
+		// 根据有没有填写 short key,分别执行
+		if shortKey != "" {
+			redisClient := redisPool.Get()
 
-		log.Println(longUrl, shortKey)
+			// 检测短链是否已存在
+			_exists, _ := redis.String(redisClient.Do("get", shortKey))
+			if _exists != "" && _exists != longUrl {
+				res.Message = "短链接已存在,请更换key"
+				context.JSON(400, *res)
+				return
+			}
 
-		res.LongUrl = longUrl
+			// 存储
+			_, _ = redisClient.Do("set", shortKey, longUrl)
+
+		} else {
+			shortKey = longToShort(longUrl, *ttl*secondsPerDay)
+		}
 
 		protocol := "http://"
 		if *https != 0 {
@@ -110,6 +124,7 @@ func main() {
 		}
 		res.ShortUrl = protocol + *domain + "/" + shortKey
 
+		context.Header("Access-Control-Allow-Origin", "*")
 		context.JSON(200, *res)
 	})
 
@@ -133,6 +148,12 @@ func shortToLong(shortKey string) string {
 	defer redisClient.Close()
 
 	longUrl, _ := redis.String(redisClient.Do("get", shortKey))
+
+	// 获取到长链接后,续命1天。每天仅允许续命1次。
+	if longUrl != "" {
+		renew(shortKey)
+	}
+
 	return longUrl
 }
 
@@ -153,7 +174,7 @@ func longToShort(longUrl string, ttl int) string {
 	// 重试三次
 	var shortKey string
 	for i := 0; i < 3; i++ {
-		shortKey = generate(6)
+		shortKey = generate(7)
 
 		_existsLongUrl, _ := redis.String(redisClient.Do("get", shortKey))
 		if _existsLongUrl == "" {
@@ -165,7 +186,7 @@ func longToShort(longUrl string, ttl int) string {
 		_, _ = redisClient.Do("mset", shortKey, longUrl, longUrl, shortKey)
 
 		_, _ = redisClient.Do("expire", shortKey, ttl)
-		_, _ = redisClient.Do("expire", longUrl, ttl)
+		_, _ = redisClient.Do("expire", longUrl, secondsPerDay)
 	}
 
 	return shortKey
@@ -205,3 +226,20 @@ func initRedisPool() {
 		},
 	}
 }
+
+func renew(shortKey string) {
+	redisClient = redisPool.Get()
+	defer redisClient.Close()
+
+	// 加锁
+	lockKey := defaultLockPrefix + shortKey
+	lock, _ := redis.Int(redisClient.Do("setnx", lockKey, 1))
+	if lock == 1 {
+		// 设置锁过期时间
+		_, _ = redisClient.Do("expire", lockKey, defaultRenewal*secondsPerDay)
+
+		// 续命
+		ttl, _ := redis.Int(redisClient.Do("ttl", shortKey))
+		_, _ = redisClient.Do("expire", shortKey, ttl+defaultRenewal*secondsPerDay)
+	}
+}

+ 6 - 1
public/index.html

@@ -23,7 +23,7 @@
           <el-input ref="long" v-model="longUrl" size="medium" @keyup.enter.native="enterToDoShort">
             <el-button slot="append" icon="el-icon-magic-stick" @click="doShort" :loading="loading"></el-button>
           </el-input>
-          <el-input class="copy-content" v-model="shortUrl" size="medium" readonly v-if="shortUrl !== ''">
+          <el-input ref="shortUrl" @dblclick.native="changeDisableStatus" class="copy-content" v-model="shortUrl" size="medium">
             <el-button slot="append" v-clipboard:copy="shortUrl" v-clipboard:success="onCopy" ref="copy-btn"
               icon="el-icon-document-copy"></el-button>
           </el-input>
@@ -71,6 +71,7 @@
 
           let data = new FormData();
           data.append("longUrl", btoa(this.longUrl));
+          data.append("shortKey", this.shortUrl.indexOf('http') < 0 ? this.shortUrl : '');
           axios.post(backend + '/short', data, {
             header: {
               "Content-Type": "application/form-data; charset=utf-8"
@@ -80,6 +81,7 @@
               if (res.data.Code === 1 && res.data.ShortUrl !== "") {
                 this.shortUrl = res.data.ShortUrl;
                 this.$copyText(this.shortUrl)
+                this.$refs.shortUrl.disabled = true
                 this.$message.success("短链接已复制到剪贴板");
               } else {
                 this.$message.error("短链接获取失败:" + res.data.Message);
@@ -119,6 +121,9 @@
         onCopy() {
           this.$message.success("Copied!");
         },
+        changeDisableStatus(event) {
+          this.$refs.shortUrl.disabled = false
+        }
       },
     })
   </script>