1. 前端页面优化, 增强统计功能, 支持生成二维码

2. 添加统计接口
This commit is contained in:
QAIU
2024-09-24 17:08:29 +08:00
parent 0d5c9651f0
commit e002d19f1b
15 changed files with 5380 additions and 3849 deletions

View File

@@ -3,6 +3,8 @@ package cn.qaiu.entity;
public class ShareLinkInfo {
private String shareKey; // 分享键
private String panName; // 网盘名称
private String type; // 分享类型
private String sharePassword; // 分享密码(如果存在)
private String shareUrl; // 原始分享链接
@@ -10,6 +12,7 @@ public class ShareLinkInfo {
private ShareLinkInfo(Builder builder) {
this.shareKey = builder.shareKey;
this.panName = builder.panName;
this.type = builder.type;
this.sharePassword = builder.sharePassword;
this.shareUrl = builder.shareUrl;
@@ -22,10 +25,18 @@ public class ShareLinkInfo {
return shareKey;
}
public String getPanName() {
return panName;
}
public void setShareKey(String shareKey) {
this.shareKey = shareKey;
}
public void setPanName(String panName) {
this.panName = panName;
}
public String getType() {
return type;
}
@@ -58,6 +69,12 @@ public class ShareLinkInfo {
this.standardUrl = standardUrl;
}
public String getCacheKey() {
// 将type和shareKey组合成一个字符串作为缓存key
return type + ":" + shareKey;
}
// 静态方法创建建造者对象
public static ShareLinkInfo.Builder newBuilder() {
return new ShareLinkInfo.Builder();
@@ -65,6 +82,7 @@ public class ShareLinkInfo {
// 建造者类
public static class Builder {
public String panName; // 分享网盘名称
private String shareKey; // 分享键
private String type; // 分享类型 (网盘模板枚举的小写)
private String sharePassword = ""; // 分享密码(如果存在)
@@ -76,6 +94,11 @@ public class ShareLinkInfo {
return this;
}
public Builder panName(String panName) {
this.panName = panName;
return this;
}
public Builder type(String type) {
this.type = type;
return this;
@@ -105,6 +128,7 @@ public class ShareLinkInfo {
public String toString() {
return "ShareLinkInfo{" +
"shareKey='" + shareKey + '\'' +
", panName='" + panName + '\'' +
", type='" + type + '\'' +
", sharePassword='" + sharePassword + '\'' +
", shareUrl='" + shareUrl + '\'' +

View File

@@ -90,6 +90,7 @@ public class ParserCreate {
if (shareUrl.matches(panDomainTemplate.getRegexPattern())) {
ShareLinkInfo shareLinkInfo = ShareLinkInfo.newBuilder()
.type(panDomainTemplate.name().toLowerCase())
.panName(panDomainTemplate.getDisplayName())
.shareUrl(shareUrl).build();
ParserCreate parserCreate = new ParserCreate(panDomainTemplate, shareLinkInfo);
return parserCreate.normalizeShareLink();

View File

@@ -11,6 +11,7 @@
"axios": "^1.7.4",
"core-js": "^3.8.3",
"element-ui": "^2.15.14",
"qrcode": "^1.5.4",
"vue": "^2.7.16",
"vue-clipboard2": "^0.3.3",
"vue-json-viewer": "^2.2.22"
@@ -3310,7 +3311,6 @@
"version": "5.0.1",
"resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"engines": {
"node": ">=8"
}
@@ -4907,6 +4907,14 @@
}
}
},
"node_modules/decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz",
@@ -5150,6 +5158,11 @@
"integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
"dev": true
},
"node_modules/dijkstrajs": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
"integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA=="
},
"node_modules/dir-glob": {
"version": "3.0.1",
"resolved": "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz",
@@ -5312,8 +5325,7 @@
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"node_modules/emojis-list": {
"version": "3.0.0",
@@ -6388,7 +6400,6 @@
"version": "4.1.0",
"resolved": "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"dev": true,
"dependencies": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
@@ -6562,7 +6573,6 @@
"version": "2.0.5",
"resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true,
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
@@ -7157,7 +7167,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"engines": {
"node": ">=8"
}
@@ -7570,7 +7579,6 @@
"version": "5.0.0",
"resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"dev": true,
"dependencies": {
"p-locate": "^4.1.0"
},
@@ -8608,7 +8616,6 @@
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"dependencies": {
"p-try": "^2.0.0"
},
@@ -8620,7 +8627,6 @@
"version": "4.1.0",
"resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"dev": true,
"dependencies": {
"p-limit": "^2.2.0"
},
@@ -8657,7 +8663,6 @@
"version": "2.2.0",
"resolved": "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true,
"engines": {
"node": ">=6"
}
@@ -8743,7 +8748,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true,
"engines": {
"node": ">=8"
}
@@ -8813,6 +8817,14 @@
"node": ">=8"
}
},
"node_modules/pngjs": {
"version": "5.0.0",
"resolved": "https://registry.npmmirror.com/pngjs/-/pngjs-5.0.0.tgz",
"integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==",
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/portfinder": {
"version": "1.0.32",
"resolved": "https://registry.npmmirror.com/portfinder/-/portfinder-1.0.32.tgz",
@@ -9504,6 +9516,121 @@
"node": ">=6"
}
},
"node_modules/qrcode": {
"version": "1.5.4",
"resolved": "https://registry.npmmirror.com/qrcode/-/qrcode-1.5.4.tgz",
"integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==",
"dependencies": {
"dijkstrajs": "^1.0.1",
"pngjs": "^5.0.0",
"yargs": "^15.3.1"
},
"bin": {
"qrcode": "bin/qrcode"
},
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/qrcode/node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/qrcode/node_modules/camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"engines": {
"node": ">=6"
}
},
"node_modules/qrcode/node_modules/cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmmirror.com/cliui/-/cliui-6.0.0.tgz",
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^6.2.0"
}
},
"node_modules/qrcode/node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/qrcode/node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/qrcode/node_modules/wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/qrcode/node_modules/y18n": {
"version": "4.0.3",
"resolved": "https://registry.npmmirror.com/y18n/-/y18n-4.0.3.tgz",
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
},
"node_modules/qrcode/node_modules/yargs": {
"version": "15.4.1",
"resolved": "https://registry.npmmirror.com/yargs/-/yargs-15.4.1.tgz",
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
"dependencies": {
"cliui": "^6.0.0",
"decamelize": "^1.2.0",
"find-up": "^4.1.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^4.2.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^18.1.2"
},
"engines": {
"node": ">=8"
}
},
"node_modules/qrcode/node_modules/yargs-parser": {
"version": "18.1.3",
"resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-18.1.3.tgz",
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"dependencies": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/qs": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
@@ -9773,7 +9900,6 @@
"version": "2.1.1",
"resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -9787,6 +9913,11 @@
"node": ">=0.10.0"
}
},
"node_modules/require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
},
"node_modules/requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz",
@@ -10088,6 +10219,11 @@
"node": ">= 0.8.0"
}
},
"node_modules/set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
},
"node_modules/set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
@@ -10399,7 +10535,6 @@
"version": "4.2.3",
"resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@@ -10413,7 +10548,6 @@
"version": "6.0.1",
"resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
@@ -11792,6 +11926,11 @@
"which": "bin/which"
}
},
"node_modules/which-module": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/which-module/-/which-module-2.0.1.tgz",
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ=="
},
"node_modules/wildcard": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/wildcard/-/wildcard-2.0.0.tgz",

View File

@@ -11,6 +11,7 @@
"axios": "^1.7.4",
"core-js": "^3.8.3",
"element-ui": "^2.15.14",
"qrcode": "^1.5.4",
"vue": "^2.7.16",
"vue-clipboard2": "^0.3.3",
"vue-json-viewer": "^2.2.22"
@@ -21,12 +22,11 @@
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"compression-webpack-plugin": "^6.1.1",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3",
"vue-template-compiler": "^2.7.16",
"compression-webpack-plugin": "^6.1.1",
"filemanager-webpack-plugin": "8.0.0"
"filemanager-webpack-plugin": "8.0.0",
"vue-template-compiler": "^2.7.16"
},
"eslintConfig": {
"root": true,

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

View File

@@ -4,18 +4,29 @@
<el-card class="box-card">
<div class="demo-basic--circle">
<div class="block" style="text-align: center;">
<el-avatar :size="150" :src="avatar"></el-avatar>
<img :height="150" src="../public/images/lanzou111.png" alt="lz"></img>
</div>
</div>
<h3 style="text-align: center;">NFD网盘直链解析0.1.8_bate(API演示)</h3>
<h3 style="text-align: center;">NFD网盘直链解析0.1.8_bate2(API演示)</h3>
<div class="typo">
<p><strong>项目GitHub </strong><a href="https://github.com/qaiu/netdisk-fast-download" target="_blank"
rel="nofollow"><u>netdisk-fast-download</u></a></p>
<p style="text-align: center;">
<span>
<el-link href="https://github.com/qaiu/netdisk-fast-download" target="_blank" rel="nofollow">
<u>GitHub</u></el-link>
</span>
<span style="margin-left: 30px">
<el-link href="https://blog.qaiu.top/archives/netdisk-fast-download-bao-ta-an-zhuang-jiao-cheng" target="_blank"
rel="nofollow"><u>宝塔部署安装教程</u>
</el-link>
</span>
<span style="margin-left: 30px">
<el-link href="https://blog.qaiu.top" target="_blank"
rel="nofollow"><u>QAIU博客</u>
</el-link>
</span></p>
<p><strong>目前支持 </strong>蓝奏云/蓝奏云优享/小飞机盘/123云盘/奶牛快传/移动云云空间/亿方云/文叔叔/QQ邮箱文件中转站</p>
<p>已加入缓存机制, 如果遇到解析出的下载链接失效的情况请及时到项目GitHub反馈</p>
<p>
<el-button><strong @click="getInfo">刷新API调用统计</strong></el-button>
</p>
<p>已加入缓存机制, 如果遇到解析出的下载链接失效的情况请及时到<a href="https://github.com/qaiu/netdisk-fast-download/issues">
<u><strong>项目GitHub反馈</strong></u></a></p>
<p>节点1: 回源请求数:{{ node1Info.parserTotal }}, 缓存请求数:{{ node1Info.cacheTotal }}, 总数:{{ node1Info.total }}</p>
<!-- <p>节点2: 成功:{{ node2Info.success }},失败:{{ node2Info.fail }},总数:{{ node2Info.total }}</p>-->
</div>
@@ -24,17 +35,24 @@
<div class="grid-content">
<el-input placeholder="请粘贴分享链接" v-model="link" id="url" lass="input-with-select">
<strong slot="prepend">分享链接</strong>
<el-button slot="append" @click="onSubmit">解析</el-button>
</el-input>
<el-input placeholder="请输入密码" v-model="password" id="url" lass="input-with-select"></el-input>
<el-input placeholder="请输入密码" v-model="password" id="url" lass="input-with-select">
<strong slot="prepend">分享密码</strong>
</el-input>
<el-input v-show="respData.data" placeholder="解析地址" :value="getLink2" id="url" lass="input-with-select">
<el-button slot="append" v-clipboard:copy="getLink2"
v-clipboard:success="onCopy"
v-clipboard:error="onError">点我复制
</el-button>
</el-input>
<p style="text-align: center">
<el-button style="" @click="onSubmit">解析测试</el-button>
<el-button style="margin-left: 20px" @click="genMd">生成Markdown链接</el-button>
<el-button style="margin-left: 20px" @click="generateQRCode">生成二维码</el-button>
<el-button style="margin-left: 20px" @click="getTj">链接信息统计</el-button>
</p>
</div>
<div v-show="respData.code" style="margin-top: 10px">
<div v-if="respData.code" style="margin-top: 10px">
<strong>解析结果: </strong>
<json-viewer
:value="respData"
@@ -45,6 +63,44 @@
/>
<a :href="downUrl" v-show="downUrl">点击下载</a>
</div>
<el-row v-if="mdText" style="text-align: center">
<el-col span="22">
<el-input
type="textarea"
:autosize="{ minRows: 4, maxRows: 8}"
placeholder="请输入内容"
v-model="mdText">
</el-input>
</el-col>
<el-col span="2">
<el-button v-clipboard:copy="mdText"
v-clipboard:success="onCopy"
v-clipboard:error="onError">复制
</el-button>
</el-col>
</el-row>
<div style="text-align: center" v-show="showQrc">
<div style="text-align: center"><el-link target="_blank" :href="codeUrl">{{ codeUrl }}</el-link></div>
<canvas ref="qrcodeCanvas"></canvas>
<div style="text-align: center"> 扫码下载 </div>
</div>
<div v-if="tjData.shareLinkInfo">
<el-descriptions class="margin-top" title="分享详情" :column="1" border>
<template slot="extra">
<el-button type="primary" size="small">操作</el-button>
</template>
<el-descriptions-item label="网盘名称">{{ tjData.shareLinkInfo.panName }}</el-descriptions-item>
<el-descriptions-item label="网盘标识">{{ tjData.shareLinkInfo.type }}</el-descriptions-item>
<el-descriptions-item label="分享Key">{{ tjData.shareLinkInfo.shareKey }}</el-descriptions-item>
<el-descriptions-item label="分享链接"> <el-link target="_blank" :href="tjData.shareLinkInfo.shareUrl">{{ tjData.shareLinkInfo.shareUrl }}</el-link></el-descriptions-item>
<el-descriptions-item label="jsonApi链接"> <el-link target="_blank" :href="tjData.apiLink">{{ tjData.apiLink }}</el-link></el-descriptions-item>
<el-descriptions-item label="302下载链接"> <el-link target="_blank" :href="tjData.downLink">{{ tjData.downLink }}</el-link></el-descriptions-item>
<el-descriptions-item label="解析次数">{{ tjData.parserTotal }}</el-descriptions-item>
<el-descriptions-item label="缓存命中次数">{{ tjData.cacheHitTotal }}</el-descriptions-item>
<el-descriptions-item label="总请求次数">{{ tjData.sumTotal }}</el-descriptions-item>
</el-descriptions>
</div>
</div>
</el-card>
</el-row>
@@ -53,6 +109,7 @@
<script>
import axios from 'axios'
import QRCode from 'qrcode'
/*
蓝奏云 (lz)
登录, 上传, 下载, 分享
@@ -79,13 +136,18 @@ export default {
name: 'App',
data() {
return {
baseAPI: `${location.protocol}//${location.hostname}:6400`,
current: {}, // 当前分享
showQrc: false,
codeUrl: '',
mdText: '',
link: "",
password: "",
isLoading: false,
downUrl: null,
avatar: "https://q2.qlogo.cn/headimg_dl?dst_uin=736226400&spec=640",
select: "lz",
respData: {},
tjData: {},
panList: [
{
name: "蓝奏云",
@@ -117,28 +179,36 @@ export default {
value: 'ye'
},
],
getLink: '',
getLink: null,
getLink2: '',
getLinkInfo: null,
node1Info: {},
node2Info: {},
}
},
methods: {
onSubmit() {
check() {
this.mdText = ''
this.showQrc = false
this.respData = {}
this.tjData = {}
if (!this.link.startsWith("https://")) {
this.$message.error("请输入有效链接!")
return
throw new Error('请输入有效链接')
}
},
onSubmit() {
this.check()
this.isLoading = true
this.downUrl = ''
this.respData = {}
this.getLink2 = `${location.protocol}//${location.host}/parser?url=${this.link}`
this.getLink2 = `${this.baseAPI}/parser?url=${this.link}`
// this.getLink = `${location.protocol}//${location.host}/api/json/parser?url=${this.link}`
this.getLink = `${location.protocol}//${location.host}/json/parser`
// this.getLink = `${location.protocol}//${location.host}/json/parser`
if (this.password) {
this.getLink += `&pwd=${this.password}`
this.getLink2 += `&pwd=${this.password}`
}
axios.get(this.getLink, {params: {url: this.link}}).then(
axios.get(this.getLink, {params: {url: this.link, pwd: this.password}}).then(
response => {
this.isLoading = false
this.respData = response.data
@@ -179,9 +249,73 @@ export default {
// this.node2Info = response.data.data
// }
// })
},
genMd() {
this.check()
axios.get(this.getLinkInfo, {params: {url: this.link, pwd: this.password}}).then(
response => {
this.isLoading = false
if (response.data.code === 200) {
this.$message({
message: response.data.msg,
type: 'success'
})
this.mdText = this.buildMd('快速下载地址',response.data.data.downLink)
} else {
this.$message.error(response.data.msg)
}
});
},
buildMd(title, url) {
return `[${title}](${url})`
},
generateQRCode() {
this.check()
const options = { // 设置二维码的参数,例如大小、边距等
width: 150,
height: 150,
margin: 2,
colorDark : "#8e0fb7",
colorLight : "#fff2ad",
};
axios.get(this.getLinkInfo, {params: {url: this.link, pwd: this.password}}).then(
response => {
this.isLoading = false
if (response.data.code === 200) {
this.$message({
message: response.data.msg,
type: 'success'
})
this.codeUrl = response.data.data.downLink
QRCode.toCanvas(this.$refs.qrcodeCanvas, this.codeUrl, options, error => {
if (error) console.error(error);
});
this.showQrc = true
} else {
this.$message.error(response.data.msg)
}
});
},
getTj() {
this.check()
axios.get(this.getLinkInfo, {params: {url: this.link, pwd: this.password}}).then(
response => {
this.isLoading = false
if (response.data.code === 200) {
this.$message({
message: response.data.msg,
type: 'success'
})
this.tjData = response.data.data
} else {
this.$message.error(response.data.msg)
}
});
}
},
mounted() {
this.getLinkInfo = `${this.baseAPI}/v2/linkInfo`
this.getLink = `${this.baseAPI}/json/parser`
this.getInfo()
}
}
@@ -255,8 +389,7 @@ body:before {
}
.typo a {
color: #2c3e50;
text-decoration: none;
color: #0077ff;
}
hr {

File diff suppressed because it is too large Load Diff

View File

@@ -51,7 +51,7 @@ public class CacheManager {
.mapEmpty();
}
// 统计网盘厂商API解析次数
// 写入网盘厂商API解析次数
public Future<Integer> updateTotalByCached(String shareKey) {
Promise<Integer> promise = Promise.promise();
String sql = """
@@ -81,7 +81,7 @@ public class CacheManager {
return fullShareKey.split(":")[0];
}
// 统计网盘厂商API解析次数
// 写入网盘厂商API解析次数
public Future<Integer> updateTotalByParser(String shareKey) {
Promise<Integer> promise = Promise.promise();
String sql = """
@@ -125,5 +125,31 @@ public class CacheManager {
return promise.future();
}
public Future<Map<String, Integer>> getShareKeyTotal(String shareKey) {
String sql = """
select `share_key`, sum(cache_hit_total) hit_total, sum(api_parser_total) parser_total,
from `api_statistics_info`
group by `share_key` having `share_key` = #{shareKey}
""";
Promise<Map<String, Integer>> promise = Promise.promise();
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("shareKey", shareKey);
SqlTemplate.forQuery(jdbcPool, sql)
.mapTo(Row::toJson)
.execute(paramMap)
.onSuccess(res -> {
if(res.iterator().hasNext()) {
JsonObject next = res.iterator().next();
Map<String, Integer> resp = new HashMap<>(){{
put("hit_total" ,next.getInteger("hit_total"));
put("parser_total" ,next.getInteger("parser_total"));
}};
promise.complete(resp);
} else {
promise.complete();
}
});
return promise.future();
}
}

View File

@@ -0,0 +1,83 @@
package cn.qaiu.lz.web.controller;
import cn.qaiu.entity.ShareLinkInfo;
import cn.qaiu.lz.common.cache.CacheManager;
import cn.qaiu.lz.common.util.URLParamUtil;
import cn.qaiu.lz.web.model.LinkInfoResp;
import cn.qaiu.lz.web.model.SysUser;
import cn.qaiu.lz.web.service.DbService;
import cn.qaiu.lz.web.service.UserService;
import cn.qaiu.lz.web.model.StatisticsInfo;
import cn.qaiu.parser.ParserCreate;
import cn.qaiu.vx.core.annotaions.RouteHandler;
import cn.qaiu.vx.core.annotaions.RouteMapping;
import cn.qaiu.vx.core.enums.RouteMethod;
import cn.qaiu.vx.core.util.AsyncServiceUtil;
import cn.qaiu.vx.core.util.ResponseUtil;
import cn.qaiu.vx.core.util.SharedDataUtil;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.net.HostAndPort;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@RouteHandler(value = "/v2", order = 10)
@Slf4j
public class ParserApi {
private final UserService userService = AsyncServiceUtil.getAsyncServiceInstance(UserService.class);
private final DbService dbService = AsyncServiceUtil.getAsyncServiceInstance(DbService.class);
@RouteMapping(value = "/login", method = RouteMethod.POST)
public Future<SysUser> login(SysUser user) {
log.info("<------- login: {}", user.getUsername());
return userService.login(user);
}
@RouteMapping(value = "/statisticsInfo", method = RouteMethod.GET, order = 99)
public Future<StatisticsInfo> statisticsInfo() {
return dbService.getStatisticsInfo();
}
private final CacheManager cacheManager = new CacheManager();
@RouteMapping(value = "/linkInfo", method = RouteMethod.GET)
public Future<LinkInfoResp> parse(HttpServerRequest request, String pwd) {
Promise<LinkInfoResp> promise = Promise.promise();
String url = URLParamUtil.parserParams(request);
ShareLinkInfo shareLinkInfo = ParserCreate.fromShareUrl(url).setShareLinkInfoPwd(pwd).getShareLinkInfo();
LinkInfoResp build = LinkInfoResp.builder()
.downLink(getDownLink(shareLinkInfo, false))
.apiLink(getDownLink(shareLinkInfo, true))
.shareLinkInfo(shareLinkInfo).build();
// 解析次数统计
cacheManager.getShareKeyTotal(shareLinkInfo.getCacheKey()).onSuccess(res -> {
if (res != null) {
build.setCacheHitTotal(res.get("hit_total") == null ? 0: res.get("hit_total"));
build.setParserTotal(res.get("parser_total") == null ? 0: res.get("parser_total"));
build.setSumTotal(build.getCacheHitTotal() + build.getParserTotal());
}
promise.complete(build);
}).onFailure(t->{
t.printStackTrace();
promise.complete(build);
});
return promise.future();
}
private static String getDownLink(ShareLinkInfo shareLinkInfo, boolean isJson) {
String linkPrefix = SharedDataUtil.getJsonConfig("server")
.getString("domainName");
if (StringUtils.isBlank(linkPrefix)) {
linkPrefix = "http://127.0.0.1";
}
String pwd = shareLinkInfo.getSharePassword();
return linkPrefix + (isJson ? "/json/" : "/") + shareLinkInfo.getType() + "/" + shareLinkInfo.getShareKey() +
(StringUtils.isBlank(pwd) ? "" : ("@" + pwd));
}
}

View File

@@ -1,4 +1,4 @@
package cn.qaiu.lz.web.http;
package cn.qaiu.lz.web.controller;
import cn.qaiu.lz.common.util.URLParamUtil;
import cn.qaiu.lz.web.model.CacheLinkInfo;
@@ -26,7 +26,7 @@ public class ServerApi {
private final CacheService cacheService = AsyncServiceUtil.getAsyncServiceInstance(CacheService.class);
@RouteMapping(value = "/parser", method = RouteMethod.GET, order = 4)
@RouteMapping(value = "/parser", method = RouteMethod.GET, order = 1)
public Future<Void> parse(HttpServerResponse response, HttpServerRequest request, String pwd) {
Promise<Void> promise = Promise.promise();
String url = URLParamUtil.parserParams(request);
@@ -40,13 +40,13 @@ public class ServerApi {
return promise.future();
}
@RouteMapping(value = "/json/parser", method = RouteMethod.GET, order = 3)
@RouteMapping(value = "/json/parser", method = RouteMethod.GET, order = 1)
public Future<CacheLinkInfo> parseJson(HttpServerRequest request, String pwd) {
String url = URLParamUtil.parserParams(request);
return cacheService.getCachedByShareUrlAndPwd(url, pwd);
}
@RouteMapping(value = "/json/:type/:key", method = RouteMethod.GET, order = 2)
@RouteMapping(value = "/json/:type/:key", method = RouteMethod.GET)
public Future<CacheLinkInfo> parseKeyJson(String type, String key) {
String pwd = "";
if (key.contains("@")) {
@@ -57,7 +57,7 @@ public class ServerApi {
return cacheService.getCachedByShareKeyAndPwd(type, key, pwd);
}
@RouteMapping(value = "/:type/:key", method = RouteMethod.GET, order = 1)
@RouteMapping(value = "/:type/:key", method = RouteMethod.GET)
public Future<Void> parseKey(HttpServerResponse response, String type, String key) {
Promise<Void> promise = Promise.promise();
String pwd = "";

View File

@@ -1,32 +0,0 @@
package cn.qaiu.lz.web.http;
import cn.qaiu.lz.web.model.SysUser;
import cn.qaiu.lz.web.service.DbService;
import cn.qaiu.lz.web.service.UserService;
import cn.qaiu.lz.web.model.StatisticsInfo;
import cn.qaiu.vx.core.annotaions.RouteHandler;
import cn.qaiu.vx.core.annotaions.RouteMapping;
import cn.qaiu.vx.core.enums.RouteMethod;
import cn.qaiu.vx.core.util.AsyncServiceUtil;
import io.vertx.core.Future;
import lombok.extern.slf4j.Slf4j;
@RouteHandler(value = "/v2", order = 10)
@Slf4j
public class ParserApi {
private final UserService userService = AsyncServiceUtil.getAsyncServiceInstance(UserService.class);
private final DbService dbService = AsyncServiceUtil.getAsyncServiceInstance(DbService.class);
@RouteMapping(value = "/login", method = RouteMethod.POST)
public Future<SysUser> login(SysUser user) {
log.info("<------- login: {}", user.getUsername());
return userService.login(user);
}
@RouteMapping(value = "/statisticsInfo", method = RouteMethod.GET, order = 99)
public Future<StatisticsInfo> statisticsInfo() {
return dbService.getStatisticsInfo();
}
}

View File

@@ -0,0 +1,17 @@
package cn.qaiu.lz.web.model;
import cn.qaiu.entity.ShareLinkInfo;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class LinkInfoResp {
// 解析链接
private String downLink;
private String apiLink;
private Integer cacheHitTotal;
private Integer parserTotal;
private Integer sumTotal;
private ShareLinkInfo shareLinkInfo;
}

View File

@@ -23,8 +23,8 @@ public class CacheServiceImpl implements CacheService {
Promise<CacheLinkInfo> promise = Promise.promise();
// 构建组合的缓存key
ShareLinkInfo shareLinkInfo = parserCreate.getShareLinkInfo();
String cacheKey = generateCacheKey(shareLinkInfo.getType(), shareLinkInfo.getShareKey());
// 尝试从缓存中获取
String cacheKey = shareLinkInfo.getCacheKey();
cacheManager.get(cacheKey).onSuccess(result -> {
// 判断是否已过期
// 未命中或者过期
@@ -57,11 +57,6 @@ public class CacheServiceImpl implements CacheService {
return promise.future();
}
private String generateCacheKey(String type, String shareKey) {
// 将type和shareKey组合成一个字符串作为缓存key
return type + ":" + shareKey;
}
private String generateDate(Long ts) {
return DateFormatUtils.format(new Date(ts), "yyyy-MM-dd HH:mm:ss");
}

View File

@@ -2,11 +2,14 @@
server:
port: 6400
contextPath: /
# 使用静态页面
enableStaticHtmlService: false
# 使用数据库
enableDatabase: true
# 使用静态页面
enableStaticHtmlService: false
staticResourcePath: webroot/
# 服务域名或者IP
domainName: https://lz.qaiu.top
# 反向代理服务器配置路径(不用加后缀)
proxyConf: server-proxy

View File

@@ -152,6 +152,9 @@ GET http://127.0.0.1:6400/json/parser?url=https://iwx.mail.qq.com/ftn/download?f
###
GET http://127.0.0.1:6400/v2/statisticsInfo
###
GET http://127.0.0.1:6400/v2/linkInfo?url=https://www.123pan.com/s/iaKtVv-6OECd.html&pwd=DcGe
###
POST http://127.0.0.1:6400/v2/login?username=asd