1. 增加自定义短链接口,前端支持自定义短链
2. 短链请求时自动续期1D,每天允许续期1次
This commit is contained in:
CareyWong 2020-05-28 00:42:14 +08:00
parent effc71cec0
commit cc716e3074
2 changed files with 57 additions and 14 deletions

64
main.go
View File

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

View File

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