├── .gitignore ├── .idea ├── compiler.xml ├── encodings.xml ├── jarRepositories.xml └── misc.xml ├── Devlopment.md ├── LICENSE ├── LICENSE.md ├── README.md ├── img.png ├── pom.xml ├── script.sql ├── url-common ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── hk │ │ └── surl │ │ └── common │ │ ├── entity │ │ ├── Location.java │ │ ├── LongUrlDTO.java │ │ └── VisitorAgent.java │ │ ├── exception │ │ └── BaseException.java │ │ ├── generator │ │ └── CodeGenerator.java │ │ ├── log │ │ └── LogThreadLocal.java │ │ ├── response │ │ ├── ErrorResult.java │ │ ├── PageResult.java │ │ ├── ResponseResult.java │ │ └── ResultCode.java │ │ └── util │ │ ├── IPUtil.java │ │ ├── LocationUtil.java │ │ ├── ParseURLPairUtil.java │ │ ├── QRCodeUtil.java │ │ ├── RSAUtil.java │ │ ├── SecretKeyUtil.java │ │ ├── StringUtil.java │ │ └── TranceIdUtil.java │ └── test │ └── java │ └── com │ └── hk │ └── surl │ └── common │ ├── IPUtilTest.java │ └── SecretKeyTest.java ├── url-core ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── hk │ │ │ └── surl │ │ │ ├── core │ │ │ ├── common │ │ │ │ ├── excption │ │ │ │ │ └── SUrlCoreExcption.java │ │ │ │ └── util │ │ │ │ │ ├── CompressUtil.java │ │ │ │ │ ├── DatabaseUtil.java │ │ │ │ │ ├── DateTimeUtil.java │ │ │ │ │ ├── EncryptUtil.java │ │ │ │ │ ├── IdWorker.java │ │ │ │ │ └── LeafSegmentUtil.java │ │ │ ├── enums │ │ │ │ ├── param │ │ │ │ │ └── UrlSourceType.java │ │ │ │ └── strategy │ │ │ │ │ ├── CacheStrategy.java │ │ │ │ │ ├── CompressStrategy.java │ │ │ │ │ ├── EncryptStrategy.java │ │ │ │ │ ├── ExpirationStrategy.java │ │ │ │ │ ├── LengthStrategy.java │ │ │ │ │ └── SyncStrategy.java │ │ │ ├── generator │ │ │ │ ├── IShortUrlGenerator.java │ │ │ │ ├── ShortUrlGenerator.java │ │ │ │ ├── builder │ │ │ │ │ └── ShortUrlGeneratorBuilder.java │ │ │ │ └── template │ │ │ │ │ ├── DefaultShortUrlGenerator.java │ │ │ │ │ └── ShortUrlGeneratorTemplate.java │ │ │ ├── provider │ │ │ │ ├── GenerateProvider.java │ │ │ │ ├── compress │ │ │ │ │ └── CompressProvider.java │ │ │ │ ├── distributed │ │ │ │ │ └── SnowFlakeProvider.java │ │ │ │ ├── encryp │ │ │ │ │ └── EncryptionProvider.java │ │ │ │ ├── leaf │ │ │ │ │ └── LeafSegmentProvider.java │ │ │ │ └── random │ │ │ │ │ └── RandomStringProvider.java │ │ │ └── strategy │ │ │ │ ├── cache │ │ │ │ └── CacheUrlStrategy.java │ │ │ │ └── encrypt │ │ │ │ └── EncryptUrlStrategy.java │ │ │ ├── entity │ │ │ ├── ShortUrlExt.java │ │ │ └── TinyId.java │ │ │ ├── util │ │ │ └── LongUrlUtil.java │ │ │ └── v2 │ │ │ ├── core │ │ │ ├── config │ │ │ │ ├── BloomFilterConfig.java │ │ │ │ ├── CommonConfig.java │ │ │ │ ├── DistributeConfig.java │ │ │ │ ├── HashModeConfig.java │ │ │ │ ├── RandomConfig.java │ │ │ │ └── RecyclingModeConfig.java │ │ │ ├── generator │ │ │ │ ├── AbstractShortUrlGenerator.java │ │ │ │ ├── IShortUrlGenerator.java │ │ │ │ ├── distribute │ │ │ │ │ └── DistributedShortUrlGenerator.java │ │ │ │ ├── hash │ │ │ │ │ ├── MD5ShortUrlGenerator.java │ │ │ │ │ └── MurmurHashShortUrlGenerator.java │ │ │ │ ├── random │ │ │ │ │ └── RandomShortUrlGenerator.java │ │ │ │ └── simple │ │ │ │ │ └── DefaultShortUrlGenerator.java │ │ │ ├── provider │ │ │ │ └── IShortUrlProvider.java │ │ │ └── strategy │ │ │ │ ├── cache │ │ │ │ └── CacheStrategy.java │ │ │ │ └── hash │ │ │ │ ├── HashStrategy.java │ │ │ │ └── HashType.java │ │ │ ├── entity │ │ │ ├── ShortURI.java │ │ │ ├── ShortURL.java │ │ │ └── ShortURLExt.java │ │ │ ├── filter │ │ │ └── ShortUrlBloomFilter.java │ │ │ └── util │ │ │ ├── BloomFilterUtil.java │ │ │ ├── HashUtil.java │ │ │ └── SnowFlakeIdWorker.java │ └── resources │ │ ├── application.properties │ │ └── direct │ │ ├── .back_Short-Url │ │ ├── Short-Url-backup-2022523160939.pdma.json │ │ ├── Short-Url-backup-2022523161158.pdma.json │ │ └── Short-Url-backup-2022523161314.pdma.json │ │ ├── ER模型.drawio │ │ ├── ER模型.png │ │ ├── Short-Url.pdma.json │ │ ├── homepage.png │ │ ├── readme.md │ │ └── 短链生成算法.drawio │ └── test │ └── java │ └── com │ └── hk │ └── surl │ ├── CompressTest.java │ ├── EncryptTest.java │ ├── GenerateStrategyTest.java │ ├── GeneratorBuilderTest.java │ ├── JDBCTest.java │ ├── LeafSegmentTest.java │ ├── LombokTest.java │ ├── RandomTest.java │ ├── ShortUrlTest.java │ ├── SnowFlakeTest.java │ └── v2 │ ├── core │ └── generator │ │ └── AbstractShortUrlGeneratorTest.java │ └── util │ └── HashUtilTest.java ├── url-domain ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── hk │ │ └── surl │ │ └── domain │ │ ├── entity │ │ ├── AnonymousUser.java │ │ ├── AppUser.java │ │ ├── LogTrance.java │ │ ├── LongUrl.java │ │ ├── ShortUrl.java │ │ ├── UrlMap.java │ │ └── VisitLog.java │ │ ├── mapper │ │ ├── AnonymousUserMapper.java │ │ ├── AppUserMapper.java │ │ ├── LogTranceMapper.java │ │ ├── LongUrlMapper.java │ │ ├── ShortUrlMapper.java │ │ ├── UrlMapMapper.java │ │ └── VisitLogMapper.java │ │ └── vo │ │ └── ShortUrlVo.java │ └── resources │ └── mapper │ ├── AnonymousUserMapper.xml │ ├── AppUserMapper.xml │ ├── LogTranceMapper.xml │ ├── LongUrlMapper.xml │ ├── ShortUrlMapper.xml │ ├── UrlMapMapper.xml │ └── VisitLogMapper.xml ├── url-interface ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── hk │ │ └── surl │ │ └── api │ │ ├── common │ │ └── FileService.java │ │ └── core │ │ ├── IAnonymousUserService.java │ │ ├── IAppUserService.java │ │ ├── ILogTranceService.java │ │ ├── ILongUrlService.java │ │ ├── IShortUrlService.java │ │ ├── IUrlMapService.java │ │ └── IVisitLogService.java │ └── resources │ └── application.properties ├── url-service ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── hk │ │ │ └── surl │ │ │ └── service │ │ │ ├── core │ │ │ ├── AccessService.java │ │ │ ├── AnonymousUserServiceImpl.java │ │ │ ├── AppUserServiceImpl.java │ │ │ ├── LogTranceServiceImpl.java │ │ │ ├── LongUrlServiceImpl.java │ │ │ ├── ShortUrlServiceImpl.java │ │ │ ├── UrlMapServiceImpl.java │ │ │ └── VisitLogServiceImpl.java │ │ │ └── util │ │ │ ├── AsyncTaskService.java │ │ │ └── ExcelResolveService.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── hk │ └── surl │ └── service │ └── test │ └── ExcelTest.java ├── url-system └── pom.xml └── url-web ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── hk │ │ └── surl │ │ └── web │ │ ├── ShortUrlWebApplication.java │ │ ├── aop │ │ ├── EnableSysLog.java │ │ ├── SysLog.java │ │ ├── SysLogAspect.java │ │ └── processor │ │ │ ├── AccessProcessor.java │ │ │ ├── DefaultProcessor.java │ │ │ └── LogProcessor.java │ │ ├── config │ │ ├── AopAspectConfig.java │ │ ├── AsyncTaskConfig.java │ │ ├── DruidDatasourceConfig.java │ │ ├── LocalDateTimeConfiguration.java │ │ ├── MobileConfig.java │ │ ├── MybatisPlusConfig.java │ │ ├── RedisTemplateConfig.java │ │ ├── ShortUrlGeneratorConfig.java │ │ └── WebConfig.java │ │ ├── controller │ │ ├── AccessController.java │ │ ├── AnonymousUserController.java │ │ ├── AppUserController.java │ │ ├── LongUrlController.java │ │ ├── QRCodeController.java │ │ ├── ShortUrlController.java │ │ ├── UrlMapController.java │ │ └── VisitLogController.java │ │ ├── exception │ │ └── UrlWebException.java │ │ ├── handler │ │ └── UrlWebExceptionHandler.java │ │ ├── interceptor │ │ ├── FaviconInterceptor.java │ │ ├── LogAndTranceInterceptor.java │ │ └── LoginInterceptor.java │ │ └── util │ │ └── RedisUtil.java └── resources │ ├── application-dev.properties │ ├── application-prod.properties │ ├── application-test.properties │ ├── application.properties │ ├── smart-doc.json │ ├── static │ └── doc │ │ ├── AllInOne.css │ │ ├── db │ │ ├── db_short_url_tb_anonymous_user.sql │ │ ├── db_short_url_tb_app_user.sql │ │ ├── db_short_url_tb_log_trance.sql │ │ ├── db_short_url_tb_long_url.sql │ │ ├── db_short_url_tb_short_url.sql │ │ ├── db_short_url_tb_tiny_id.sql │ │ ├── db_short_url_tb_url_map.sql │ │ ├── db_short_url_tb_url_type.sql │ │ └── db_short_url_tb_visit_log.sql │ │ ├── debug.js │ │ ├── font.css │ │ ├── highlight.min.js │ │ ├── index.html │ │ ├── index.html.md │ │ ├── jquery.min.js │ │ ├── script.sql │ │ ├── search.js │ │ └── xt256.min.css │ └── template │ └── longUrlTemplate.xlsx └── test └── java └── com └── hk └── surl └── web └── test ├── ExcelTest.java ├── StringTest.java └── UrlTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.db 2 | *.mmdb 3 | # IntelliJ project files 4 | /target/ 5 | **/target/ 6 | /url-frontend 7 | .idea 8 | *.iml 9 | # Compiled class file 10 | *.class 11 | 12 | # Log file 13 | *.log 14 | 15 | # BlueJ files 16 | *.ctxt 17 | 18 | # Mobile Tools for Java (J2ME) 19 | .mtj.tmp/ 20 | 21 | # Package Files # 22 | *.jar 23 | *.war 24 | *.nar 25 | *.ear 26 | *.zip 27 | *.tar.gz 28 | *.rar 29 | 30 | *.db 31 | 32 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 33 | hs_err_pid* 34 | 35 | # some document 36 | document/ -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 HK意境 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 HK意境 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HK-hub/Short-URL/e666ded026b8439fad95dfb0d3d50516e7d6ad9f/img.png -------------------------------------------------------------------------------- /script.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HK-hub/Short-URL/e666ded026b8439fad95dfb0d3d50516e7d6ad9f/script.sql -------------------------------------------------------------------------------- /url-common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Short-URL 7 | com.hk.surl 8 | 1.0.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | url-common 13 | 0.1.0 14 | 15 | 16 | 17 | com.baomidou 18 | mybatis-plus-generator 19 | 3.4.1 20 | 21 | 22 | javax.servlet 23 | javax.servlet-api 24 | 4.0.1 25 | 26 | 27 | 28 | 29 | 30 | com.google.zxing 31 | core 32 | 3.3.3 33 | 34 | 35 | 36 | com.google.zxing 37 | javase 38 | 3.3.3 39 | 40 | 41 | 42 | 43 | 44 | com.maxmind.geoip2 45 | geoip2 46 | 3.0.0 47 | 48 | 49 | 50 | org.lionsoul 51 | ip2region 52 | 1.7.2 53 | 54 | 55 | 56 | 57 | com.google.code.gson 58 | gson 59 | 60 | 61 | 62 | com.alibaba 63 | fastjson 64 | 1.2.78 65 | 66 | 67 | org.aspectj 68 | aspectjweaver 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /url-common/src/main/java/com/hk/surl/common/entity/Location.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.common.entity; 2 | 3 | import lombok.Data; 4 | import lombok.ToString; 5 | 6 | /** 7 | * @author : HK意境 8 | * @ClassName : Location 9 | * @date : 2022/6/14 11:19 10 | * @description : 11 | * @Todo : 12 | * @Bug : 13 | * @Modified : 14 | * @Version : 1.0 15 | */ 16 | @Data 17 | @ToString 18 | public class Location { 19 | 20 | private String country ; 21 | private String province ; 22 | private String city ; 23 | private String latitude; 24 | private String longitude ; 25 | 26 | 27 | } 28 | -------------------------------------------------------------------------------- /url-common/src/main/java/com/hk/surl/common/entity/LongUrlDTO.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.common.entity; 2 | import lombok.AllArgsConstructor; 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | 6 | /** 7 | * @author : HK意境 8 | * @ClassName : LongUrlDTO 9 | * @date : 2022/6/15 21:26 10 | * @description : 长链接excel 模板解析数据接收类 11 | * @Todo : 12 | * @Bug : 13 | * @Modified : 14 | * @Version : 1.0 15 | */ 16 | @Data 17 | @NoArgsConstructor 18 | @AllArgsConstructor 19 | public class LongUrlDTO{ 20 | 21 | private String longUrl ; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /url-common/src/main/java/com/hk/surl/common/entity/VisitorAgent.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.common.entity; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import lombok.ToString; 7 | 8 | /** 9 | * @author : HK意境 10 | * @ClassName : VisitorAgent 11 | * @date : 2022/6/14 11:32 12 | * @description : 13 | * @Todo : 14 | * @Bug : 15 | * @Modified : 16 | * @Version : 1.0 17 | */ 18 | @Data 19 | @ToString 20 | @NoArgsConstructor 21 | @AllArgsConstructor 22 | public class VisitorAgent { 23 | 24 | // 浏览器 25 | private String browser ; 26 | // 平台操作系统 27 | private String os ; 28 | 29 | // 设备平台 30 | private String platform ; 31 | 32 | 33 | public VisitorAgent(String browser, String os) { 34 | this.browser = browser; 35 | this.os = os; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /url-common/src/main/java/com/hk/surl/common/exception/BaseException.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.common.exception; 2 | 3 | import com.hk.surl.common.response.ResultCode; 4 | import lombok.Data; 5 | 6 | import java.time.LocalDateTime; 7 | 8 | /** 9 | * @author : HK意境 10 | * @ClassName : BaseException 11 | * @date : 2022/6/10 9:37 12 | * @description : 13 | * @Todo : 14 | * @Bug : 15 | * @Modified : 16 | * @Version : 1.0 17 | */ 18 | @Data 19 | public class BaseException extends RuntimeException{ 20 | 21 | // 是否成功 22 | protected Boolean success = false; 23 | // 错误代码 24 | protected Integer code; 25 | // 错误信息 26 | protected String message; 27 | 28 | // 错误时间 29 | protected LocalDateTime time = LocalDateTime.now(); 30 | 31 | // 错误链路 32 | protected String traceId; 33 | 34 | 35 | // 基本异常类型错误码 36 | private static final int BASE_EXCEPTION_CODE = ResultCode.SERVER_ERROR.code(); 37 | // 基本类型异常错误消息 38 | private static final String BASE_EXCEPTION_MESSAGE = ResultCode.SERVER_ERROR.message(); 39 | 40 | 41 | // 服务器内部错误异常 42 | public BaseException() { 43 | super(BASE_EXCEPTION_MESSAGE); 44 | this.code = BASE_EXCEPTION_CODE; 45 | this.message = BASE_EXCEPTION_MESSAGE; 46 | } 47 | 48 | public BaseException(String message) { 49 | super(message); 50 | this.code = BASE_EXCEPTION_CODE; 51 | this.message = message; 52 | } 53 | 54 | public BaseException(ResultCode resultCode) { 55 | super(resultCode.message()); 56 | this.code = resultCode.code(); 57 | this.message = resultCode.message(); 58 | this.success = resultCode.success(); 59 | } 60 | 61 | public BaseException(Throwable cause) { 62 | super(cause); 63 | this.code = BASE_EXCEPTION_CODE; 64 | this.message = BASE_EXCEPTION_MESSAGE; 65 | } 66 | 67 | public BaseException(String message, Throwable cause) { 68 | super(message, cause); 69 | this.code = BASE_EXCEPTION_CODE; 70 | this.message = message; 71 | } 72 | 73 | public BaseException(Integer code, String message) { 74 | super(message); 75 | this.code = code; 76 | this.message = message; 77 | } 78 | 79 | public BaseException(Integer code, String message, Throwable cause) { 80 | super(message, cause); 81 | this.code = code; 82 | this.message = message; 83 | } 84 | 85 | 86 | 87 | } -------------------------------------------------------------------------------- /url-common/src/main/java/com/hk/surl/common/log/LogThreadLocal.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.common.log; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.Stack; 6 | 7 | /** 8 | * @author : HK意境 9 | * @ClassName : LogThreadLocal 10 | * @date : 2022/6/14 9:57 11 | * @description : 存放请求线程资源 12 | * @Todo : 13 | * @Bug : 14 | * @Modified : 15 | * @Version : 1.0 16 | */ 17 | public class LogThreadLocal { 18 | 19 | // threadlocal 日志缓存 20 | private static final ThreadLocal> LOG_CACHE = new ThreadLocal<>(); 21 | 22 | // 清除缓存,清除数据 23 | public void clear(){ 24 | LOG_CACHE.remove(); 25 | } 26 | 27 | 28 | // 添加 29 | public static void setAttribute(String key, Object value) { 30 | Map cache = LogThreadLocal.getCache(); 31 | cache.put(key, value); 32 | } 33 | 34 | // 获取数据 35 | public static Object getAttribute(String key) { 36 | Map cache = getCache(); 37 | return cache.get(key); 38 | } 39 | 40 | 41 | // 获取字符串数据 42 | public static String getAttributeStr(String key) { 43 | Map cache = getCache(); 44 | Object value = cache.get(key); 45 | return value == null ? null : String.valueOf(value); 46 | } 47 | 48 | // 获取缓存 map 数据 49 | public static Map getCache() { 50 | Map cache = LOG_CACHE.get(); 51 | if (cache == null) { 52 | cache = new HashMap<>(); 53 | LOG_CACHE.set(cache); 54 | } 55 | return cache; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /url-common/src/main/java/com/hk/surl/common/response/ErrorResult.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.common.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | import lombok.Data; 6 | import org.springframework.format.annotation.DateTimeFormat; 7 | 8 | import java.io.Serializable; 9 | import java.time.LocalDateTime; 10 | 11 | @Data 12 | public class ErrorResult implements Serializable { 13 | 14 | private boolean success = false; 15 | private Integer code; 16 | private String message; 17 | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") 18 | private LocalDateTime time = LocalDateTime.now(); 19 | @JsonIgnore 20 | private ResultCode resultCode; 21 | 22 | 23 | public static ErrorResult error() { 24 | ErrorResult result = new ErrorResult(); 25 | result.setResultCode(ResultCode.SERVER_ERROR); 26 | return result; 27 | } 28 | 29 | public static ErrorResult error(String message) { 30 | ErrorResult result = new ErrorResult(); 31 | result.setCode(ResultCode.SERVER_ERROR.code()); 32 | result.setMessage(message); 33 | return result; 34 | } 35 | 36 | 37 | public static ErrorResult error(Integer code, String message) { 38 | ErrorResult result = new ErrorResult(); 39 | result.setCode(code); 40 | result.setMessage(message); 41 | return result; 42 | } 43 | 44 | 45 | public static ErrorResult error(ResultCode resultCode, String message) { 46 | ErrorResult result = new ErrorResult(); 47 | result.setResultCode(resultCode); 48 | result.setMessage(message); 49 | return result; 50 | } 51 | 52 | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8") 53 | public LocalDateTime getTime() { 54 | return time; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /url-common/src/main/java/com/hk/surl/common/response/PageResult.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.common.response; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | import lombok.ToString; 6 | 7 | import java.io.Serializable; 8 | 9 | /** 10 | * @author : HK意境 11 | * @ClassName : PageResult 12 | * @date : 2022/6/24 20:53 13 | * @description : 14 | * @Todo : 15 | * @Bug : 16 | * @Modified : 17 | * @Version : 1.0 18 | */ 19 | @Data 20 | @ToString 21 | @NoArgsConstructor 22 | public class PageResult implements Serializable { 23 | 24 | // 本页多少条 25 | private Integer perPageSize ; 26 | // 当前是第几页 27 | private Integer pageNumber ; 28 | // 是否有上一页 29 | private Boolean hasPreviousPage ; 30 | // 是否还有下一页 31 | private Boolean hasNextPage ; 32 | // 一页多少条 33 | private Integer pageSize ; 34 | // 共多少页 35 | private Integer totalPages ; 36 | // 一共多少条 37 | private Long totalSize ; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /url-common/src/main/java/com/hk/surl/common/response/ResponseResult.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.common.response; 2 | 3 | 4 | import com.fasterxml.jackson.annotation.JsonFormat; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import lombok.ToString; 8 | import org.springframework.format.annotation.DateTimeFormat; 9 | 10 | import java.time.LocalDateTime; 11 | import java.util.UUID; 12 | 13 | /** 14 | * 数据响应对象 15 | * { 16 | * success:成功, 17 | * code :响应码, 18 | * message:返回信息, 19 | * //响应数据 20 | * data:{ 21 | * 22 | * } 23 | * 24 | * } 25 | * 26 | * 27 | * @author HK意境**/ 28 | 29 | @Data 30 | @ToString 31 | @NoArgsConstructor 32 | public class ResponseResult { 33 | 34 | 35 | //是否成功 36 | private boolean success; 37 | //返回码 38 | private Integer code; 39 | //返回信息 40 | private String message; 41 | //返回数据,分页数据 42 | private T data; 43 | 44 | // 是否分页查询 45 | private Boolean pageable = false; 46 | // 分页参数 47 | private PageResult pageResult; 48 | 49 | // 响应时间 50 | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") 51 | private LocalDateTime dateTime = LocalDateTime.now() ; 52 | // 响应ID: 后期进行链路追踪 53 | private String traceId = UUID.randomUUID().toString().replaceAll("-","").substring(0,10); 54 | 55 | 56 | // 静态返回对象 57 | public static ResponseResult SuccessResponse = new ResponseResult(ResultCode.SUCCESS,"ok"); 58 | public static ResponseResult FailResponse = new ResponseResult(ResultCode.FAIL,"failed"); 59 | public static ResponseResult ErrorResponse = new ResponseResult(ResultCode.REMOTE_INTERFACE_ERROR,"exception"); 60 | 61 | // 快捷返回对象 62 | public static ResponseResult SUCCESS(){ 63 | return new ResponseResult(ResultCode.SUCCESS); 64 | } 65 | 66 | public static ResponseResult ERROR(){ 67 | return new ResponseResult(ResultCode.SERVER_ERROR); 68 | } 69 | 70 | public static ResponseResult FAIL(){ 71 | return new ResponseResult(ResultCode.FAIL); 72 | } 73 | 74 | 75 | public ResponseResult(boolean flag){ 76 | if (flag){ 77 | // 成功 78 | this.setResultCode(ResultCode.SUCCESS); 79 | }else{ 80 | this.setResultCode(ResultCode.FAIL); 81 | } 82 | } 83 | 84 | public ResponseResult(ResultCode code) { 85 | this.success = code.success(); 86 | this.code = code.code(); 87 | this.message = code.message(); 88 | } 89 | 90 | public ResponseResult(ResultCode code, T data) { 91 | this.success = code.success(); 92 | this.code = code.code(); 93 | this.message = code.message(); 94 | this.data = data; 95 | } 96 | 97 | public ResponseResult(Integer code, String message, boolean success) { 98 | this.code = code; 99 | this.message = message; 100 | this.success = success; 101 | } 102 | 103 | public ResponseResult setResultCode(ResultCode code){ 104 | this.success = code.success(); 105 | this.code = code.code(); 106 | this.message = code.message(); 107 | return this; 108 | } 109 | 110 | public ResponseResult setData(T data){ 111 | this.data = data ; 112 | return this; 113 | } 114 | 115 | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8") 116 | public LocalDateTime getDateTime() { 117 | return dateTime; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /url-common/src/main/java/com/hk/surl/common/response/ResultCode.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.common.response; 2 | 3 | public enum ResultCode { 4 | 5 | // 成功请求 6 | SUCCESS(true ,200, "success"), 7 | //已创建 请求成功并且服务器创建了新的资源 8 | SUCCESS_CREATE(true ,201,"success create resource"), 9 | //请求执行成功, 但是没有数据返回 10 | SUCCESS_NO_CONTENT(true,204,"success and no data response"), 11 | // 永久重定向 12 | REDIRECT_PERMANENT(true,301, "permanent redirection"), 13 | // 临时重定向 14 | REDIRECT_TEMPORARY(true, 302 ,"temporary redirection"), 15 | //请求语法错误,请求参数错误 16 | BAD_REQUEST(false, 400 ,"request parameters error"), 17 | // 请求参数错误异常 18 | PARAMETER_ERROR(false,444, "request parameters is not valid"), 19 | //未授权 20 | UNAUTHORIZED(false,401,"unauthorized"), 21 | //未认证 22 | UNAUTHENTICATED(false, 102, "unauthenticated") , 23 | // 用户登录已过期 24 | ACCESS_TOKEN_EXPIRED(false,106,"the access token is expired, please try login aging"), 25 | USER_HAS_EXITS(false,104,"user has exits"), 26 | USER_NOT_FOUND(false , 103, "user not found"), 27 | USER_LOGIN_ERROR(false, 105,"username or password or shortUrl or secretKey error"), 28 | //资源禁止访问, 可以用来控制权限 29 | FORBIDDEN(false,403,"no permission"), 30 | // 资源未找到 31 | NOT_FOUND(false,404, "not found"), 32 | // 文件上传下载失败 33 | RESOURCE_DOWNLOAD_OR_UPLOAD_ERROR(false,424,"resouce downlowd or upload error"), 34 | //账号或者密码错误 35 | ACCOUNT_PASSWORDS_ERROR(false,101,"account or password error"), 36 | //token 校验不合法 37 | TOKEN_ERROR(false,108,"token is illegal or invalid"), 38 | // 远程过程调用接口异常 39 | REMOTE_INTERFACE_ERROR(false,700, "接口调用异常"), 40 | // 服务器错误 41 | SERVER_ERROR(false,500,"server error"), 42 | SERVER_BUSY(false,999,"服务器繁忙"), 43 | //请求失败 44 | FAIL(false,-100,"fail"), 45 | //上传失败,上传文件不能为空 46 | SC_EXPECTATION_FAILED(false,-110,"file is empty") ; 47 | 48 | 49 | 50 | //---用户操作返回码---- 51 | //---企业操作返回码---- 52 | //---权限操作返回码---- 53 | //---其他操作返回码---- 54 | 55 | //操作是否成功 56 | boolean success; 57 | //操作代码 58 | int code; 59 | //提示信息 60 | String message; 61 | 62 | ResultCode(boolean success, int code, String message){ 63 | this.success = success; 64 | this.code = code; 65 | this.message = message; 66 | } 67 | 68 | public boolean success() { 69 | return success; 70 | } 71 | 72 | public int code() { 73 | return code; 74 | } 75 | 76 | public String message() { 77 | return message; 78 | } 79 | 80 | 81 | } 82 | -------------------------------------------------------------------------------- /url-common/src/main/java/com/hk/surl/common/util/ParseURLPairUtil.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.common.util; 2 | 3 | import java.lang.reflect.Field; 4 | import java.net.MalformedURLException; 5 | import java.net.URL; 6 | import java.util.Iterator; 7 | import java.util.Map; 8 | import java.util.Set; 9 | import java.util.TreeMap; 10 | 11 | 12 | /** 13 | * @ClassName : ParseURLPairUtil 14 | * @author : HK意境 15 | * @date : 2022/5/25 16 | * @description : 解析 URL 路径值的工具类 17 | * @Todo : 18 | * @Bug : 19 | * @Modified : 20 | * @Version : 1.0 21 | */ 22 | public class ParseURLPairUtil { 23 | 24 | 25 | // 获取所有 pair 单元 26 | public static String parseURLPair(Object o) throws Exception{ 27 | Class c = o.getClass(); 28 | Field[] fields = c.getDeclaredFields(); 29 | Map map = new TreeMap(); 30 | for (Field field : fields) { 31 | field.setAccessible(true); 32 | String name = field.getName(); 33 | Object value = field.get(o); 34 | if(value != null) { 35 | map.put(name, value); 36 | } 37 | } 38 | Set> set = map.entrySet(); 39 | Iterator> it = set.iterator(); 40 | StringBuffer sb = new StringBuffer(); 41 | while (it.hasNext()) { 42 | Map.Entry e = it.next(); 43 | sb.append(e.getKey()).append("=").append(e.getValue()).append("&"); 44 | } 45 | return sb.deleteCharAt(sb.length()-1).toString(); 46 | } 47 | 48 | // 获取 uri 49 | public static String getUri(String urlString) throws MalformedURLException { 50 | 51 | // uri 的请求路径 52 | String path = new URL(urlString).getPath(); 53 | 54 | // 获取最后一段 : 因为这里只考虑 url 链接后面直接就是短链接地址,没有二级地址 55 | String uri = path.substring(path.indexOf("/")); 56 | return uri ; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /url-common/src/main/java/com/hk/surl/common/util/SecretKeyUtil.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.common.util; 2 | 3 | import cn.hutool.crypto.digest.DigestUtil; 4 | 5 | import java.util.Random; 6 | 7 | /** 8 | * @author : HK意境 9 | * @ClassName : SecretKeyUtil 10 | * @date : 2022/6/22 0:00 11 | * @description : 生产 secretKey 工具类 12 | * @Todo : 生产算法:讲 shortUrl , longUrl 作为payload , 以时间戳为随机盐 13 | * @Bug : longUrl 过长不适合作为payload 负载,应该优化为使用 短链接+动态长度的随机盐 14 | * @Modified : 15 | * @Version : 1.0 16 | */ 17 | public class SecretKeyUtil { 18 | 19 | private static final String salt = "zxcvbnmasdfghjklqwertyuiopZXCVBNMASDFGHJKLQWERTYUIOP1234567890,.<>:?"; 20 | private static final int saltLength = 16; 21 | 22 | /*** 23 | * 使用随机盐与短链接进行 md5 加密 24 | * @param shortUrl 25 | * @param longUrl 26 | * @return 27 | */ 28 | public static String generateSecretKey(String shortUrl , String longUrl){ 29 | 30 | // 获取随机盐 31 | Random random = new Random(); 32 | StringBuffer stringBuffer = new StringBuffer(); 33 | //循环16次,共取出16个随机字符 34 | for (int i = 0; i < 16; i++) { 35 | //每次生成一个67以内的随机数 36 | int number = random.nextInt(68); 37 | //生成的随机数作为 str 字符串的下标;从 str 中取出随机字符后追加到 stringBuffer 38 | stringBuffer.append(salt.charAt(number)); 39 | } 40 | 41 | // 通过 短链接+随机盐进行md5 加密 42 | String secretKey = DigestUtil.md5Hex(shortUrl + stringBuffer.toString()); 43 | 44 | return secretKey ; 45 | } 46 | 47 | 48 | 49 | } 50 | -------------------------------------------------------------------------------- /url-common/src/main/java/com/hk/surl/common/util/TranceIdUtil.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.common.util; 2 | 3 | import java.util.UUID; 4 | 5 | /** 6 | * @ClassName : TranceIdUtil 7 | * @author : HK意境 8 | * @date : 2022/6/14 9:24 9 | * @description : 链路id 生成器 10 | * @Todo : 11 | * @Bug : 12 | * @Modified : 13 | * @Version : 1.0 14 | */ 15 | public class TranceIdUtil { 16 | 17 | /** 18 | * 生成traceId 19 | * 20 | * @return TraceId 基于UUID 21 | */ 22 | public static String getTraceId() { 23 | return UUID.randomUUID().toString().replace("-", ""); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /url-common/src/test/java/com/hk/surl/common/IPUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.common; 2 | 3 | import cn.hutool.core.io.resource.ClassPathResource; 4 | import com.hk.surl.common.entity.Location; 5 | import com.hk.surl.common.util.IPUtil; 6 | import org.junit.Test; 7 | 8 | import java.io.*; 9 | import java.lang.reflect.InvocationTargetException; 10 | 11 | /** 12 | * @author : HK意境 13 | * @ClassName : IPUtilTest 14 | * @date : 2022/6/14 14:42 15 | * @description : 16 | * @Todo : 17 | * @Bug : 18 | * @Modified : 19 | * @Version : 1.0 20 | */ 21 | public class IPUtilTest { 22 | 23 | 24 | @Test 25 | public void fileReadTest(){ 26 | 27 | File file = new File("url-common/src/main/resources/ip2region.db"); 28 | if (file == null) { 29 | System.out.println("file is null"); 30 | }else{ 31 | 32 | System.out.println("file is not null"); 33 | System.out.println(file.getName()); 34 | System.out.println(file.getAbsolutePath()); 35 | 36 | } 37 | 38 | } 39 | 40 | 41 | @Test 42 | public void getCityInfoTest() throws IOException, InvocationTargetException, IllegalAccessException { 43 | 44 | InputStream stream = new FileInputStream("location/GeoLite2-City.mmdb"); 45 | 46 | if (stream != null) { 47 | System.out.println("stream is not nll"); 48 | }else{ 49 | System.out.println("stram is null"); 50 | } 51 | 52 | String ipAddressLocation = IPUtil.getCityInfo("127.0.0.1"); 53 | 54 | } 55 | 56 | 57 | @Test 58 | public void ipAddressTest() throws IOException, InvocationTargetException, IllegalAccessException { 59 | 60 | 61 | 62 | //IPUtil.getIpAddress() 63 | //System.out.println(ipAddressLocation); 64 | 65 | } 66 | 67 | 68 | } 69 | -------------------------------------------------------------------------------- /url-common/src/test/java/com/hk/surl/common/SecretKeyTest.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.common; 2 | 3 | import com.hk.surl.common.util.SecretKeyUtil; 4 | import org.junit.Test; 5 | 6 | 7 | /** 8 | * @author : HK意境 9 | * @ClassName : SecretKeyTest 10 | * @date : 2022/6/22 0:17 11 | * @description : 12 | * @Todo : 13 | * @Bug : 14 | * @Modified : 15 | * @Version : 1.0 16 | */ 17 | public class SecretKeyTest { 18 | 19 | @Test 20 | public void secretKeyGenerateTest(){ 21 | 22 | String secretKey = SecretKeyUtil.generateSecretKey("su.io/@eV_Tt", ""); 23 | 24 | System.out.println(secretKey); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/core/common/excption/SUrlCoreExcption.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.core.common.excption; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | 6 | /** 7 | * @author : HK意境 8 | * @ClassName : SUrlCoreExcption 9 | * @date : 2022/5/30 13:33 10 | * @description : 核心模块异常 11 | * @Todo : 12 | * @Bug : 13 | * @Modified : 14 | * @Version : 1.0 15 | */ 16 | @Data 17 | @NoArgsConstructor 18 | public class SUrlCoreExcption extends RuntimeException{ 19 | 20 | 21 | // 错误信息 22 | private String message ; 23 | // 错误代码 24 | private Integer errorCode ; 25 | 26 | public SUrlCoreExcption(String message , Integer code){ 27 | this.errorCode = code; 28 | this.message = message ; 29 | } 30 | 31 | public SUrlCoreExcption(ExcptionInstance excption){ 32 | this(excption.getMessage(),excption.getErrorCode()); 33 | } 34 | 35 | public static enum ExcptionInstance{ 36 | 37 | LeafSegment_DB_Error("没有剩余号码可以发送, 可能是出现了数据库服务不可用,数据库号段资源耗尽",100101), 38 | LeafSegment_BIZ_Error("没有指定业务标识的发号器可以使用",100102) 39 | 40 | ; 41 | 42 | // 错误信息 43 | private String message ; 44 | // 错误代码 45 | private Integer errorCode ; 46 | 47 | ExcptionInstance(String message, Integer errorCode) { 48 | this.message = message; 49 | this.errorCode = errorCode; 50 | } 51 | 52 | public String getMessage() { 53 | return message; 54 | } 55 | 56 | public void setMessage(String message) { 57 | this.message = message; 58 | } 59 | 60 | public Integer getErrorCode() { 61 | return errorCode; 62 | } 63 | 64 | public void setErrorCode(Integer errorCode) { 65 | this.errorCode = errorCode; 66 | } 67 | 68 | } 69 | 70 | 71 | } 72 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/core/common/util/DatabaseUtil.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.core.common.util; 2 | 3 | import com.alibaba.druid.pool.DruidDataSource; 4 | import lombok.Data; 5 | 6 | import javax.sql.DataSource; 7 | import java.sql.Driver; 8 | 9 | /** 10 | * @author : HK意境 11 | * @ClassName : DatabaseUtil 12 | * @date : 2022/5/30 9:46 13 | * @description : 数据库工具类 14 | * @Todo : 连接数据库,获取数据源 15 | * @Bug : 16 | * @Modified : 17 | * @Version : 1.0 18 | */ 19 | @Data 20 | public class DatabaseUtil { 21 | 22 | // 数据库用户名: 默认 root 23 | private String username = "root"; 24 | 25 | // 数据库密码: 默认 root 26 | private String password = "root"; 27 | 28 | // 数据库 url 29 | private String url ; 30 | 31 | 32 | /** 33 | * @methodName : getDataSource 34 | * @author : HK意境 35 | * @date : 2022/5/30 9:56 36 | * @description : 获取 druid 数据源,获取数据源 37 | * @Todo : 通过外部参数获取数据源 38 | * @params : 39 | * @param : String username, String password, String url, Class driverClass 40 | * @return : DataSource 41 | * @throws: 42 | * @Bug : 43 | * @Modified : 44 | * @Version : 1.0.0 45 | */ 46 | public static DataSource getDataSource(String username, String password, String url, Class driverClass){ 47 | 48 | return getDatasource(username,password,url,driverClass.getName()); 49 | } 50 | 51 | 52 | // 获取数据源 53 | public static DataSource getDatasource(String username, String password, String url,String driverClassName){ 54 | 55 | DruidDataSource dataSource = new DruidDataSource(); 56 | dataSource.setUsername(username); 57 | dataSource.setPassword(password); 58 | dataSource.setUrl(url); 59 | 60 | dataSource.setDriverClassName(driverClassName); 61 | 62 | return dataSource ; 63 | } 64 | 65 | 66 | 67 | } 68 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/core/common/util/LeafSegmentUtil.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.core.common.util; 2 | 3 | import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; 4 | import com.hk.surl.entity.TinyId; 5 | import lombok.Data; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.jdbc.core.BeanPropertyRowMapper; 8 | import org.springframework.jdbc.core.JdbcTemplate; 9 | 10 | import javax.sql.DataSource; 11 | import java.util.List; 12 | 13 | /** 14 | * @author : HK意境 15 | * @ClassName : LeafSegmentUtil 16 | * @date : 2022/5/30 10:59 17 | * @description : 号段发号器算法 18 | * @Todo : 19 | * @Bug : 20 | * @Modified : 21 | * @Version : 1.0 22 | */ 23 | @Data 24 | @Slf4j 25 | public class LeafSegmentUtil { 26 | 27 | // 数据源,用来操作数据库——> 通过 jdbcTemplate 28 | private DataSource dataSource ; 29 | 30 | 31 | /** 32 | * @methodName : 33 | * @author : HK意境 34 | * @date : 2022/5/30 11:15 35 | * @description: 单个:获取业务标识下的 发号器 进行发号 36 | * @Todo : 37 | * @params : 38 | * @param : String bizType 业务标识 39 | * @return : null 40 | * @throws: 41 | * @Bug : 42 | * @Modified : 43 | * @Version : 1.0.0 44 | */ 45 | public static Long nextId(String bizType){ 46 | 47 | return nextId(bizType, 1).get(0); 48 | } 49 | 50 | /** 51 | * @methodName : nextId 52 | * @author : HK意境 53 | * @date : 2022/5/30 11:15 54 | * @description : 批量获取 id 55 | * @Todo : 56 | * @params : 57 | * @param : String bizType 业务标识 58 | * @param: int batchSize 批量个数 59 | * @return : null 60 | * @throws: 61 | * @Bug : 62 | * @Modified : 63 | * @Version : 1.0.0 64 | */ 65 | public static List nextId(String bizType, int batchSize){ 66 | 67 | return null; 68 | } 69 | 70 | // 获取下一个号段 71 | public static Long getNextSegment(String bizType, DataSource dataSource){ 72 | 73 | // 获取jdbc 74 | JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); 75 | 76 | // 获取下一个号段: nextSegment(step)=当前号段最大值+step 77 | // = (begin_id+step)+step 78 | // begin_id = 79 | 80 | return null ; 81 | } 82 | 83 | 84 | // 根据业务号获取 发号器配置,没有对应业务那就异步生产一个 85 | public static TinyId getTinyIdConfigByBizType(String bizType, DataSource dataSource){ 86 | 87 | JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); 88 | 89 | // 获取 tinyId 信息 90 | String sql = "select * from tb_tiny_id where biz_type = ?"; 91 | List tinyIdList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(TinyId.class), bizType); 92 | 93 | TinyId tinyId = new TinyId(); 94 | // 存在该业务配置 95 | if (tinyIdList.size() >= 1){ 96 | tinyId = tinyIdList.get(0); 97 | 98 | }else { 99 | // 不存在对应业务的配置, 默认生产一个该业务配置 100 | // biz_type=bizType, begin_id = 10000 , max_id=10000, step = 10000,delta=1,remainder=10000 101 | tinyId.setBizType(bizType); 102 | tinyId.setBeginId(10000L); 103 | tinyId.setMaxId(tinyId.getBeginId()); 104 | tinyId.setDelta(1); 105 | tinyId.setStep(10000); 106 | tinyId.setRemainder(tinyId.getStep()); 107 | 108 | } 109 | 110 | return tinyId; 111 | } 112 | 113 | 114 | } 115 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/core/enums/param/UrlSourceType.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.core.enums.param; 2 | 3 | /** 4 | * @author : HK意境 5 | * @ClassName : UrlSourceType 6 | * @date : 2022/5/31 22:58 7 | * @description : 8 | * @Todo : 9 | * @Bug : 10 | * @Modified : 11 | * @Version : 1.0 12 | */ 13 | public enum UrlSourceType { 14 | // url 链接,base64 数据,二维码 15 | URL("url", 0),BASE64("base64",1),QRCODE("qrcode",2) 16 | ; 17 | 18 | // url 路径字符串的 类型 19 | private String type ; 20 | private Integer code ; 21 | 22 | UrlSourceType(String type) { 23 | this.type = type; 24 | } 25 | 26 | UrlSourceType(String type, Integer code) { 27 | this.type = type; 28 | this.code = code; 29 | } 30 | public String getType() { 31 | return type; 32 | } 33 | 34 | public void setType(String type) { 35 | this.type = type; 36 | } 37 | 38 | public Integer getCode() { 39 | return code; 40 | } 41 | 42 | public void setCode(Integer code) { 43 | this.code = code; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/core/enums/strategy/CacheStrategy.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.core.enums.strategy; 2 | 3 | /** 4 | * @author : HK意境 5 | * @ClassName : CacheStrategy 6 | * @date : 2022/5/31 21:39 7 | * @description : 缓存策略 8 | * @Todo : 9 | * @Bug : 10 | * @Modified : 11 | * @Version : 1.0 12 | */ 13 | public enum CacheStrategy { 14 | 15 | 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/core/enums/strategy/CompressStrategy.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.core.enums.strategy; 2 | 3 | /** 4 | * @author : HK意境 5 | * @ClassName : CompressStrategy 6 | * @date : 2022/4/18 21:07 7 | * @description : 压缩算法策略: 8 | * 压缩算法整体上的效率不是很高,并且只有再字符串长度较长的情况下才能看到压缩效果,如果字符串长度较短则反而起不到压缩的效果 9 | * @Todo : gzip 字符串压缩,zlib 压缩字符串 10 | * @Bug : 这里使用 gzip 进行直接字符串的压缩可能会有乱码问题,所以建议使用 zlib 压缩 11 | * @Modified : 12 | * @Version : 1.0 13 | */ 14 | public enum CompressStrategy { 15 | 16 | // 不压缩 17 | NONE , 18 | // 路径压缩 19 | PATH, 20 | // zlib 压缩:支持对于数据流的压缩,对于字符串压缩建议采用这种压缩方式 21 | ZLIB, 22 | // gzip 压缩:gzip 一般用于文件的压缩,对于数据流,字符串等的压缩效果不是很好,只有再字符串长度很长的情况下才能看到压缩效果 23 | // GZip压缩 256字节以上才有压缩效果 24 | GZIP, 25 | // zlib 压缩: 26 | ; 27 | 28 | 29 | 30 | } 31 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/core/enums/strategy/EncryptStrategy.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.core.enums.strategy; 2 | 3 | /** 4 | * @author : HK意境 5 | * @ClassName : EncryptStrategy 6 | * @date : 2022/4/18 20:11 7 | * @description : 加密策略 8 | * @Todo : 9 | * @Bug : 10 | * @Modified : 11 | * @Version : 1.0 12 | */ 13 | public enum EncryptStrategy { 14 | 15 | // 不加密 16 | NONE , 17 | // 使用MD5 加密 18 | MD5, 19 | // 使用hash加密 20 | HASH256 , 21 | // 22 | SHA , 23 | // Base64 24 | BASE64, 25 | // AES 26 | AES , 27 | ; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/core/enums/strategy/ExpirationStrategy.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.core.enums.strategy; 2 | 3 | 4 | import java.util.concurrent.TimeUnit; 5 | 6 | /** 7 | * @author : HK意境 8 | * @ClassName : ExpirationStrategy 9 | * @date : 2022/4/18 13:40 10 | * @description : 数据库保存时间:短链接Short-Url 的过期时间,短链接地址能够生效的时间,是否可见等 11 | * @Todo : 12 | * @Bug : 13 | * @Modified : 14 | * @Version : 1.0 15 | */ 16 | public enum ExpirationStrategy { 17 | // 永不过期 18 | FOREVER(-1L, null), 19 | // 一小时过期 20 | ONE_HOUR(1L, TimeUnit.HOURS), 21 | // 三小时过期 22 | THREE_HOUR(3L,TimeUnit.HOURS), 23 | // 一天过期 24 | ONE_DAYS(1L,TimeUnit.DAYS), 25 | // 三天过期 26 | THREE_DAYS(3L,TimeUnit.DAYS), 27 | // 五天过期 28 | FIVE_DAYS(5L,TimeUnit.DAYS), 29 | // 十天过期 30 | TEN_DAYS(10L,TimeUnit.DAYS), 31 | // 一个月过期 32 | ONE_MONTH(30L, TimeUnit.DAYS); 33 | 34 | //过期时间 35 | private Long expirationTime ; 36 | // 过期时间单位 37 | private TimeUnit timeUnit ; 38 | 39 | 40 | ExpirationStrategy(Long expirationTime , TimeUnit timeUnit) { 41 | this.expirationTime = expirationTime; 42 | this.timeUnit = timeUnit; 43 | } 44 | 45 | 46 | ExpirationStrategy() { 47 | } 48 | 49 | public Long getExpirationTime() { 50 | return expirationTime; 51 | } 52 | 53 | public void setExpirationTime(Long expirationTime) { 54 | this.expirationTime = expirationTime; 55 | } 56 | 57 | 58 | public TimeUnit getTimeUnit() { 59 | return timeUnit; 60 | } 61 | 62 | public void setTimeUnit(TimeUnit timeUnit) { 63 | this.timeUnit = timeUnit; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/core/enums/strategy/LengthStrategy.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.core.enums.strategy; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * @author : HK意境 7 | * @ClassName : LengthStrategy 8 | * @date : 2022/4/18 13:48 9 | * @description : 小型企业选择4-6 个长度即可,中大型企业应该选择10 个字符及以上的,也可以选择进行自定义操作 10 | * @Todo : 11 | * @Bug : 12 | * @Modified : 13 | * @Version : 1.0 14 | */ 15 | @Getter 16 | public enum LengthStrategy { 17 | 18 | // 中小型企业:建议4-6 个长度即可:当使用 62 进制的字符来进行选择时候可能有:44,261,653,680 种组合情况 19 | SMES(6), 20 | 21 | // 大型企业:建议选择 10 个长度以上, 22 | LARGE_ENTERPRISE(10); 23 | 24 | 25 | private Integer length ; 26 | 27 | // 自定义长度 28 | LengthStrategy(Integer length) { 29 | this.length = length; 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/core/enums/strategy/SyncStrategy.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.core.enums.strategy; 2 | 3 | /** 4 | * @author : HK意境 5 | * @ClassName : SyncStrategy 6 | * @date : 2022/4/18 13:35 7 | * @description : 8 | * @Todo : 9 | * @Bug : 10 | * @Modified : 11 | * @Version : 1.0 12 | */ 13 | public enum SyncStrategy { 14 | 15 | // 不使用同步策略 16 | DISABLE, 17 | // 使用乐观锁策略 18 | CAS , 19 | // 使用加锁策略 20 | SYNC, 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/core/generator/IShortUrlGenerator.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.core.generator; 2 | 3 | import com.hk.surl.domain.entity.ShortUrl; 4 | 5 | /** 6 | * @author : HK意境 7 | * @ClassName : IShortUrlGenerator 8 | * @date : 2022/4/18 13:29 9 | * @description : 10 | * @Todo : 11 | * @Bug : 12 | * @Modified : 13 | * @Version : 1.0 14 | */ 15 | @FunctionalInterface 16 | public interface IShortUrlGenerator { 17 | 18 | /** 19 | * @methodName : 生成方法 20 | * @author : HK意境 21 | * @date : 2022/4/18 13:29 22 | * @description : 23 | * @Todo : 24 | * @params : 25 | * @param : null 26 | * @return : null 27 | * @throws: 28 | * @Bug : 29 | * @Modified : 30 | * @Version : 1.0 31 | */ 32 | public abstract ShortUrl generate(); 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/core/generator/builder/ShortUrlGeneratorBuilder.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.core.generator.builder; 2 | 3 | import com.hk.surl.core.enums.strategy.*; 4 | import com.hk.surl.core.generator.ShortUrlGenerator; 5 | import com.hk.surl.core.provider.GenerateProvider; 6 | import com.hk.surl.core.provider.random.RandomStringProvider; 7 | import com.hk.surl.entity.ShortUrlExt; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | import lombok.experimental.Accessors; 11 | import org.springframework.beans.BeanUtils; 12 | 13 | /** 14 | * @author : HK意境 15 | * @ClassName : ShortUrlGeneratorBuilder 16 | * @date : 2022/4/14 9:58 17 | * @description : 18 | * @Todo : 19 | * @Bug : 20 | * @Modified : 21 | * @Version : 1.0 22 | */ 23 | @Data 24 | @Accessors(fluent = true) 25 | @NoArgsConstructor 26 | public class ShortUrlGeneratorBuilder { 27 | 28 | // 短链接扩展实体 29 | protected ShortUrlExt urlExt ; 30 | 31 | // 选择的算法策略 : 默认使用 randomStringProvider 32 | protected GenerateProvider provider = new RandomStringProvider(); 33 | 34 | // 是否开启缓存策略, 该缓存策略是指是否缓存在 Redis 里面, 如果有指定Short-URL过期策略的,则动态更具时间决定是否存放在缓存中 35 | protected Boolean enableCache = false; 36 | 37 | // 其他配置 38 | // 生成后的 短链接 URI 部分的长度 39 | protected Integer length = LengthStrategy.SMES.getLength(); 40 | 41 | // 选择Short-URL 生成后的有效时间 42 | protected ExpirationStrategy expireStrategy= ExpirationStrategy.FOREVER ; 43 | 44 | // 是否选择使用线程安全的模式:这里准备弃用了 45 | protected SyncStrategy syncStrategy = SyncStrategy.DISABLE; 46 | 47 | // 是否需要对生成后的 短链接 再次进行加密-> 这里准备弃用了 48 | protected EncryptStrategy encryptStrategy=EncryptStrategy.NONE ; 49 | 50 | // 采用的压缩策略: 默认采用不压缩-> 这里准备弃用了 51 | protected CompressStrategy compressStrategy = CompressStrategy.NONE ; 52 | 53 | 54 | // 构造函数: 传入一个 ShortUrlExt 55 | public ShortUrlGeneratorBuilder(ShortUrlExt urlExt) { 56 | this.urlExt = urlExt; 57 | } 58 | 59 | // 传入一个 长链接 longUrl 60 | public ShortUrlGeneratorBuilder(String longUrl){ 61 | this(new ShortUrlExt(longUrl)); 62 | } 63 | 64 | 65 | 66 | 67 | /** 68 | * @methodName : build() 69 | * @author : HK意境 70 | * @date : 2022/4/18 18:55 71 | * @description : 根据参数构建Generator 类 72 | * @Todo : 73 | * @params : 74 | * @param : null 75 | * @return : null 76 | * @throws: 77 | * @Bug : 78 | * @Modified : 79 | * @Version : 1.0 80 | */ 81 | public ShortUrlGenerator build(){ 82 | ShortUrlGenerator generator = new ShortUrlGenerator(); 83 | BeanUtils.copyProperties(this, generator); 84 | return generator ; 85 | } 86 | 87 | 88 | } 89 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/core/generator/template/DefaultShortUrlGenerator.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.core.generator.template; 2 | 3 | import com.hk.surl.core.enums.strategy.ExpirationStrategy; 4 | import com.hk.surl.core.enums.strategy.LengthStrategy; 5 | import com.hk.surl.core.enums.strategy.SyncStrategy; 6 | import com.hk.surl.core.generator.ShortUrlGenerator; 7 | import com.hk.surl.core.generator.builder.ShortUrlGeneratorBuilder; 8 | import com.hk.surl.core.provider.random.RandomStringProvider; 9 | import com.hk.surl.domain.entity.ShortUrl; 10 | import com.hk.surl.entity.ShortUrlExt; 11 | import lombok.Data; 12 | 13 | import javax.sql.DataSource; 14 | 15 | /** 16 | * @author : HK意境 17 | * @ClassName : DefaultShortUrlGenerator 18 | * @date : 2022/6/10 21:47 19 | * @description : 20 | * @Todo : 21 | * @Bug : 22 | * @Modified : 23 | * @Version : 1.0 24 | */ 25 | public class DefaultShortUrlGenerator extends ShortUrlGenerator{ 26 | 27 | // 默认 provider :随机字符串provider 28 | private ShortUrlGenerator generator ; 29 | 30 | public DefaultShortUrlGenerator() { 31 | 32 | this.urlExt = new ShortUrlExt(); 33 | 34 | // 通过建造者模式 获取生成器 35 | this.generator = new ShortUrlGeneratorBuilder() 36 | .provider(new RandomStringProvider()) 37 | .length(LengthStrategy.SMES.getLength()) 38 | .syncStrategy(SyncStrategy.DISABLE) 39 | .expireStrategy(ExpirationStrategy.FOREVER) 40 | .build(); 41 | } 42 | 43 | // 域名系统 配置 44 | public DefaultShortUrlGenerator(String domain){ 45 | 46 | // 域名系统:用于生成短链接后的访问前缀部分 47 | this() ; 48 | this.urlExt.domain(domain) ; 49 | } 50 | 51 | 52 | public DefaultShortUrlGenerator(ShortUrlGenerator generator){ 53 | this.generator = generator; 54 | } 55 | 56 | 57 | /** 58 | * @methodName : generate 59 | * @author : HK意境 60 | * @date : 2022/6/11 12:22 61 | * @description : 62 | * @Todo : 63 | * @apiNote : 64 | * @params : 65 | * @param longUrl 转换的长链接字符串 66 | * @return ShortUrl 短链接实体 67 | * @throws: 68 | * @Bug : 69 | * @Modified : 70 | * @Version : 1.0.0 71 | */ 72 | public ShortUrl generate(String longUrl){ 73 | // 使用默认provider 进行生产 74 | ShortUrl shortUrl = this.generator.longUrl(longUrl).generate(); 75 | shortUrl.setShortUrl(this.urlExt.domain()+"/"+shortUrl.getShortUrl()); 76 | 77 | return shortUrl ; 78 | } 79 | 80 | 81 | public DefaultShortUrlGenerator(ShortUrlGenerator generator, DataSource dataSource) { 82 | this.generator = generator; 83 | } 84 | 85 | public ShortUrlGenerator getGenerator() { 86 | return generator; 87 | } 88 | 89 | public void setGenerator(ShortUrlGenerator generator) { 90 | this.generator = generator; 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/core/generator/template/ShortUrlGeneratorTemplate.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.core.generator.template; 2 | 3 | /** 4 | * @author : HK意境 5 | * @ClassName : ShortUrlGeneratorTemplate 6 | * @date : 2022/6/1 14:27 7 | * @description : 8 | * @Todo : 9 | * @Bug : 10 | * @Modified : 11 | * @Version : 1.0 12 | */ 13 | public class ShortUrlGeneratorTemplate { 14 | 15 | 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/core/provider/GenerateProvider.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.core.provider; 2 | 3 | import com.hk.surl.core.generator.ShortUrlGenerator; 4 | 5 | /** 6 | * @author : HK意境 7 | * @ClassName : GenerateProvider 8 | * @date : 2022/4/18 14:00 9 | * @description : 短链接生成算法策略 10 | * @Todo : 11 | * @Bug : 12 | * @Modified : 13 | * @Version : 1.0 14 | */ 15 | @FunctionalInterface 16 | public interface GenerateProvider { 17 | 18 | public abstract String provideShortUrl(ShortUrlGenerator generator ); 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/core/provider/compress/CompressProvider.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.core.provider.compress; 2 | 3 | import com.hk.surl.core.common.util.CompressUtil; 4 | import com.hk.surl.core.provider.GenerateProvider; 5 | import com.hk.surl.core.generator.ShortUrlGenerator; 6 | import com.hk.surl.core.enums.strategy.CompressStrategy; 7 | 8 | import java.nio.charset.StandardCharsets; 9 | 10 | /** 11 | * @author : HK意境 12 | * @ClassName : CompressProvider 13 | * @date : 2022/4/18 21:01 14 | * @description : 使用压缩算法:这里仅仅提供常见的字符串压缩算法 15 | * - gzip 算法 16 | * - zlib 算法 17 | * - path 压缩算法 18 | * @Todo : 19 | * @Bug : 20 | * @Modified : 21 | * @Version : 1.0 22 | */ 23 | public class CompressProvider implements GenerateProvider { 24 | 25 | // 使用 压缩工具类进行压缩数据 26 | 27 | @Override 28 | public String provideShortUrl(ShortUrlGenerator generator) { 29 | 30 | // 获取长链接字符串 31 | String longUrl = generator.urlExt().longUrl(); 32 | 33 | // 压缩后的数据缓冲buffer 34 | String shortUrl; 35 | 36 | // 获取压缩策略 37 | CompressStrategy compressStrategy = generator.compressStrategy(); 38 | 39 | // 更具选择的压缩算法来执行具体的压缩行为 40 | switch (compressStrategy){ 41 | 42 | case NONE: 43 | // 不压缩 44 | shortUrl = longUrl ; 45 | break; 46 | case PATH: 47 | shortUrl = CompressUtil.stringPathCompress(longUrl); 48 | break; 49 | case ZLIB: 50 | // 使用 zlib 压缩 51 | shortUrl = new String(CompressUtil.zlibCompress(longUrl), StandardCharsets.UTF_8); 52 | break; 53 | case GZIP: 54 | shortUrl = CompressUtil.gzipCompress(longUrl); 55 | break; 56 | default: 57 | // 默认不采用压缩方式 58 | shortUrl = longUrl ; 59 | break; 60 | } 61 | 62 | // 返回压缩后的短链接 63 | return shortUrl ; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/core/provider/encryp/EncryptionProvider.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.core.provider.encryp; 2 | 3 | import com.hk.surl.core.provider.GenerateProvider; 4 | import com.hk.surl.core.generator.ShortUrlGenerator; 5 | import com.hk.surl.core.strategy.encrypt.EncryptUrlStrategy; 6 | 7 | /** 8 | * @author : HK意境 9 | * @ClassName : EncryptionProvider 10 | * @date : 2022/4/18 20:57 11 | * @description : 12 | * @Todo : 13 | * @Bug : 14 | * @Modified : 15 | * @Version : 1.0 16 | */ 17 | public class EncryptionProvider implements GenerateProvider { 18 | 19 | @Override 20 | public String provideShortUrl(ShortUrlGenerator generator) { 21 | // 获取源字符串 22 | String longUrl = generator.urlExt().longUrl(); 23 | // 获取加密策略 24 | //EncryptStrategy encryptStrategy = generator.getEncryptStrategy(); 25 | 26 | // 拿到的就是目标短链接 27 | String encryptUrl = EncryptUrlStrategy.encryptString(generator); 28 | return encryptUrl ; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/core/provider/random/RandomStringProvider.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.core.provider.random; 2 | 3 | import com.hk.surl.core.generator.ShortUrlGenerator; 4 | import com.hk.surl.core.provider.GenerateProvider; 5 | import lombok.experimental.Accessors; 6 | 7 | import java.util.*; 8 | 9 | /** 10 | * @author : HK意境 11 | * @ClassName : RandomStringProvider 12 | * @date : 2022/4/14 9:57 13 | * @description : 14 | * @Todo : 15 | * @Bug : 16 | * @Modified : 17 | * @Version : 1.0 18 | */ 19 | @Accessors(fluent = true) 20 | public class RandomStringProvider implements GenerateProvider { 21 | 22 | // 使用配置文件的方式解耦 23 | //@Value("${short.url.string.pool}") 24 | private String stringPool = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_#&%$*()+=@!~"; 25 | 26 | // 首先对于StringPool 字符串池种的字符进行混淆后,进行随机 27 | private Integer shuffleNum = 5; 28 | 29 | // 是否选择需要进行shuffle 30 | private Boolean enableShuffle = true; 31 | 32 | public static void main(String[] args) { 33 | 34 | new RandomStringProvider().provideShortUrl(null); 35 | 36 | } 37 | 38 | 39 | /** 40 | * @methodName : shuffle() 41 | * @author : HK意境 42 | * @date : 2022/4/18 18:01 43 | * @description : 44 | * @Todo : 45 | * @params : 46 | * @param : null 47 | * @return : null 48 | * @throws: 49 | * @Bug : 50 | * @Modified : 51 | * @Version : 1.0 52 | */ 53 | private void shuffleStringPool(){ 54 | String[] strPool = this.stringPool.split(""); 55 | List stringList = Arrays.asList(strPool); 56 | 57 | for (Integer i = 0; i < this.shuffleNum; i++) { 58 | Collections.shuffle(stringList); 59 | } 60 | 61 | StringBuilder sb = new StringBuilder(); 62 | stringList.forEach(sb::append); 63 | 64 | this.stringPool = sb.toString(); 65 | } 66 | 67 | 68 | /** 69 | * @methodName : 提供短链接的入口程序 70 | * @author : HK意境 71 | * @date : 2022/4/18 18:33 72 | * @description : 73 | * @Todo : 74 | * @params : 75 | * @param : null 76 | * @return : null 77 | * @throws: 78 | * @Bug : 79 | * @Modified : 80 | * @Version : 1.0 81 | */ 82 | @Override 83 | public String provideShortUrl(ShortUrlGenerator generator) { 84 | // 先进行扰乱 85 | if(this.enableShuffle){ 86 | shuffleStringPool(); 87 | } 88 | String shortUrl = this.createWithRandom(generator.length()); 89 | 90 | return shortUrl ; 91 | } 92 | 93 | public String createWithRandom(int length){ 94 | 95 | // 开始进行随机生成 96 | Random random = new Random(); 97 | StringBuilder sb = new StringBuilder(); 98 | 99 | for (int i = 0; i < length; i++) { 100 | sb.append(this.stringPool.charAt(random.nextInt(this.stringPool.length()))); 101 | } 102 | return sb.toString(); 103 | } 104 | 105 | 106 | 107 | } 108 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/core/strategy/cache/CacheUrlStrategy.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.core.strategy.cache; 2 | 3 | /** 4 | * @author : HK意境 5 | * @ClassName : CacheUrlStrategy 6 | * @date : 2022/5/31 21:57 7 | * @description : 8 | * @Todo : 9 | * @Bug : 10 | * @Modified : 11 | * @Version : 1.0 12 | */ 13 | public class CacheUrlStrategy { 14 | 15 | 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/core/strategy/encrypt/EncryptUrlStrategy.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.core.strategy.encrypt; 2 | 3 | 4 | import com.hk.surl.core.common.util.EncryptUtil; 5 | import com.hk.surl.core.generator.ShortUrlGenerator; 6 | import com.hk.surl.core.enums.strategy.EncryptStrategy; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /** 11 | * @author : HK意境 12 | * @ClassName : EncryptUrlStrategy 13 | * @date : 2022/4/18 20:36 14 | * @description : 加密字符串算法 15 | * @Todo : 16 | * @Bug : 17 | * @Modified : 18 | * @Version : 1.0 19 | */ 20 | public class EncryptUrlStrategy{ 21 | 22 | private static final Logger LOGGER = LoggerFactory.getLogger(EncryptUrlStrategy.class); 23 | 24 | 25 | public static String encryptString(ShortUrlGenerator generator){ 26 | 27 | // 获取长链接 28 | String longUrl = generator.urlExt().longUrl(); 29 | 30 | // 获取加密策略 31 | EncryptStrategy encryptStrategy = generator.encryptStrategy(); 32 | 33 | // 根据加密策略选择加密算法 34 | if(EncryptStrategy.NONE == encryptStrategy){ 35 | // 不加密 36 | return longUrl; 37 | 38 | }else if(EncryptStrategy.MD5 == encryptStrategy){ 39 | //System.out.println("MD5"); 40 | return EncryptUtil.MD5(longUrl); 41 | 42 | }else if(EncryptStrategy.SHA == encryptStrategy || EncryptStrategy.HASH256==encryptStrategy){ 43 | //System.out.println("Hash256"); 44 | return EncryptUtil.SHA1(longUrl); 45 | 46 | }else if(EncryptStrategy.BASE64 == encryptStrategy){ 47 | //System.out.println("BASE64"); 48 | return EncryptUtil.Base64Encode(longUrl); 49 | }else{ 50 | //默认不使用加密 51 | } 52 | 53 | return longUrl ; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/entity/ShortUrlExt.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.entity; 2 | 3 | import com.hk.surl.core.enums.param.UrlSourceType; 4 | import com.hk.surl.core.enums.strategy.ExpirationStrategy; 5 | import com.hk.surl.domain.entity.ShortUrl; 6 | import io.swagger.annotations.ApiModelProperty; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | import lombok.experimental.Accessors; 10 | 11 | import java.time.LocalDateTime; 12 | 13 | /** 14 | * @author : HK意境 15 | * @ClassName : ShortUrlExt 16 | * @date : 2022/4/14 9:53 17 | * @description : 18 | * @Todo : 19 | * @Bug : 20 | * @Modified : 21 | * @Version : 1.0 22 | */ 23 | @Data 24 | @Accessors(fluent = true) 25 | @NoArgsConstructor 26 | public class ShortUrlExt { 27 | 28 | @ApiModelProperty(value = "原始的长链接") 29 | private String longUrl ; 30 | 31 | @ApiModelProperty(value = "生成后的短链接") 32 | private String shortUrl; 33 | 34 | @ApiModelProperty(value = "短链接域名") 35 | private String domain = ""; 36 | 37 | @ApiModelProperty(value = "短链接类型: http 请求链接,二维码,base64") 38 | private Integer type; 39 | 40 | @ApiModelProperty(value = "编号:目前作用未知") 41 | private String no; 42 | 43 | @ApiModelProperty(value = "创建时间") 44 | private LocalDateTime createTime; 45 | 46 | @ApiModelProperty(value = "过期时间单位,时间度量") 47 | private ExpirationStrategy expiration ; 48 | 49 | @ApiModelProperty(value = "过期时间:表示短链接从创建经过到使用到消亡的时间,是指失效的时间: expiration_time=create_time+有效时间") 50 | private LocalDateTime expirationTime; 51 | 52 | @ApiModelProperty(value = "跟新时间") 53 | private LocalDateTime updateTime; 54 | 55 | @ApiModelProperty(value = "可见性:1可见,0不可见") 56 | private Boolean visible; 57 | 58 | 59 | // 设置长链接 60 | public ShortUrlExt(String longUrl) { 61 | this.longUrl = longUrl; 62 | } 63 | 64 | // 设置源数据,和数据类型 65 | public ShortUrlExt(String sourceUrl, Integer type){ 66 | this.longUrl = sourceUrl; 67 | this.type = type ; 68 | } 69 | 70 | 71 | // 枚举方式 72 | public ShortUrlExt(String sourceUrl, UrlSourceType type){ 73 | this(sourceUrl, type.getCode()); 74 | } 75 | 76 | 77 | public ShortUrlExt(String longUrl, String shortUrl) { 78 | this.longUrl = longUrl; 79 | this.shortUrl = shortUrl; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/entity/TinyId.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.entity; 2 | 3 | import java.io.Serializable; 4 | import java.time.LocalDateTime; 5 | import lombok.Data; 6 | import lombok.ToString; 7 | 8 | /** 9 | * 滴滴TinyId 算法,分段发号器算法 10 | * @TableName tb_tiny_id 11 | */ 12 | @Data 13 | @ToString 14 | public class TinyId implements Serializable { 15 | 16 | private static final long serialVersionUID = 1L; 17 | 18 | /** 19 | * 自增id 20 | */ 21 | private Long id; 22 | 23 | /** 24 | * 业务类型标识,唯一 25 | */ 26 | private String bizType; 27 | 28 | /** 29 | * 开始id,仅记录初始值,初始时,值应该等于max_id 30 | */ 31 | private Long beginId; 32 | 33 | /** 34 | * 当前最大id, 发号器目前发到的ID 35 | */ 36 | private Long maxId; 37 | 38 | /** 39 | * 步长,号段长度 40 | */ 41 | private Integer step; 42 | 43 | /** 44 | * 每次id 增量 45 | */ 46 | private Integer delta; 47 | 48 | /** 49 | * 余量 50 | */ 51 | private Integer remainder; 52 | 53 | /** 剩余率 **/ 54 | private Double remainRate ; 55 | 56 | /** 57 | * 修改时间 58 | */ 59 | private LocalDateTime createTime; 60 | 61 | /** 62 | * 跟新时间 63 | */ 64 | private LocalDateTime updateTime; 65 | 66 | /** 67 | * 乐观锁 68 | */ 69 | private Long version; 70 | 71 | } -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/util/LongUrlUtil.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.util; 2 | 3 | import com.hk.surl.domain.entity.LongUrl; 4 | 5 | import java.net.MalformedURLException; 6 | import java.net.URL; 7 | 8 | /** 9 | * @author : HK意境 10 | * @ClassName : LongUrlUtil 11 | * @date : 2022/6/11 13:52 12 | * @description : 长链接生成工具, 13 | * @Todo :通过给定 长链接字符串,解析出各个参数配置,返回长链接对象 14 | * @Bug : 15 | * @Modified : 16 | * @Version : 1.0 17 | */ 18 | public class LongUrlUtil { 19 | 20 | /** 21 | * @methodName : analyzeStringToLongUrl 22 | * @author : HK意境 23 | * @date : 2022/6/11 13:54 24 | * @description : 解析长链接字符串生成长链接对象 25 | * @Todo : 26 | * @apiNote : 27 | * @params : 28 | * @return LongUrl 29 | * @throws: 30 | * @Bug : 31 | * @Modified : 32 | * @Version : 1.0.0 33 | */ 34 | public static LongUrl analyzeStringToLongUrl(String longUrlStr){ 35 | 36 | LongUrl longUrl = null; 37 | 38 | try { 39 | // 构造 url 对象: 可以兼容多种协议,查询参数 40 | URL url = new URL(longUrlStr); 41 | longUrl = new LongUrl(longUrlStr,url); 42 | 43 | } catch (MalformedURLException e) { 44 | return null ; 45 | } 46 | 47 | return longUrl; 48 | } 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/v2/core/config/BloomFilterConfig.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.v2.core.config; 2 | 3 | import lombok.Data; 4 | import lombok.experimental.Accessors; 5 | 6 | /** 7 | * @author : HK意境 8 | * @ClassName : BloomFilterConfig 9 | * @date : 2023/6/6 20:10 10 | * @description : 布隆过滤器配置 11 | * @Todo : 12 | * @Bug : 13 | * @Modified : 14 | * @Version : 1.0 15 | */ 16 | @Data 17 | @Accessors(chain = true) 18 | public class BloomFilterConfig { 19 | 20 | /** 21 | * 预估数据量: 100 万。一天 10000 短链接生成,可以使用 100 天,> 3 个月,缓存一个月即可 22 | */ 23 | private int insertions = 10000 * 100; 24 | 25 | 26 | /** 27 | * 判重错误率:一万条里面出现 1 条 28 | */ 29 | private double fpp = 0.0001; 30 | 31 | 32 | } 33 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/v2/core/config/CommonConfig.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.v2.core.config; 2 | 3 | import com.hk.surl.core.enums.strategy.LengthStrategy; 4 | import lombok.Data; 5 | import lombok.experimental.Accessors; 6 | 7 | /** 8 | * @author : HK意境 9 | * @ClassName : CommonConfig 10 | * @date : 2023/6/3 23:38 11 | * @description : 通用配置 12 | * @Todo : 13 | * @Bug : 14 | * @Modified : 15 | * @Version : 1.0 16 | */ 17 | @Data 18 | @Accessors(chain = true) 19 | public class CommonConfig { 20 | 21 | /** 22 | * 生成后短链接的code 长度(除去协议,域名后的部分), 默认6 23 | * 此生产长度只是尽可能的满足,可能会因为选择的生成算法等原因限制而无法满足,例如你选择了分布式id, 发号器等算法 24 | */ 25 | protected Integer length = LengthStrategy.SMES.getLength(); 26 | 27 | /** 28 | * 默认开启缓存 29 | */ 30 | protected Boolean enableCache = Boolean.TRUE; 31 | 32 | /** 33 | * 当采用不安全的生成算法进行短链接生产的时候是否需要对生产的短链接进行二次加密操作 34 | */ 35 | protected Boolean enableEncrypt = Boolean.FALSE; 36 | 37 | 38 | /** 39 | * 是否开启池化:预先生产一些短链接集合,然后后续直接从缓存获取短链接,异步补充,回收短码即可 40 | */ 41 | protected Boolean enablePooling = Boolean.TRUE; 42 | 43 | 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/v2/core/config/DistributeConfig.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.v2.core.config; 2 | 3 | import lombok.Data; 4 | import lombok.experimental.Accessors; 5 | 6 | /** 7 | * @author : HK意境 8 | * @ClassName : DistributeConfig 9 | * @date : 2023/6/7 20:05 10 | * @description : 11 | * @Todo : 12 | * @Bug : 13 | * @Modified : 14 | * @Version : 1.0 15 | */ 16 | @Data 17 | @Accessors(chain = true) 18 | public class DistributeConfig { 19 | 20 | /** 21 | * 是否开启安全模式:生成后的分布式id,因为单调递增,所以可能存在风险,如果开启安全模式,会对生成后的分布式id进行扰乱: 22 | * 1.转置 23 | * 2.加密 24 | * 3.转换为 16 进制 25 | */ 26 | protected boolean safe = false; 27 | 28 | /** 29 | * 仅在开启安全模式的情况下起作用 30 | * 1.转置 31 | * 2.转换为 16 进制 32 | */ 33 | protected int mode = 3; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/v2/core/config/HashModeConfig.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.v2.core.config; 2 | 3 | import com.hk.surl.v2.core.strategy.hash.HashStrategy; 4 | import com.hk.surl.v2.core.strategy.hash.HashType; 5 | import com.hk.surl.v2.util.HashUtil; 6 | import lombok.Data; 7 | import lombok.experimental.Accessors; 8 | 9 | /** 10 | * @author : HK意境 11 | * @ClassName : HashModeConfig 12 | * @date : 2023/6/5 19:04 13 | * @description : Hash 算法配置 14 | * @Todo : 15 | * @Bug : 16 | * @Modified : 17 | * @Version : 1.0 18 | */ 19 | @Data 20 | @Accessors(chain = true) 21 | public class HashModeConfig { 22 | 23 | /** 24 | * hash 算法采用类型,默认 MD5 25 | */ 26 | protected HashType type = HashType.MurmurHash; 27 | 28 | /** 29 | * 自动长度控制:对于原始链接较短的url 采用hash 生成之后可能长度(md5 生成 32字符,murmurHash生成8个字符)会超过原来的长度。 30 | * true: 打开长度自动控制之后,将会对这种情况进行处理,对生成的 hash 值采用截取头,中,尾的方式进行生成小于原始链接的短链。 31 | * false: 关闭自动长度控制,将不会对生成之后的短链进行长度处理。 32 | */ 33 | protected Boolean autoLengthControl = false; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/v2/core/config/RandomConfig.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.v2.core.config; 2 | 3 | import lombok.Data; 4 | import lombok.experimental.Accessors; 5 | 6 | /** 7 | * @author : HK意境 8 | * @ClassName : RandomConfig 9 | * @date : 2023/6/6 22:04 10 | * @description : 11 | * @Todo : 12 | * @Bug : 13 | * @Modified : 14 | * @Version : 1.0 15 | */ 16 | @Data 17 | @Accessors(chain = true) 18 | public class RandomConfig { 19 | 20 | /** 21 | * 随机字符串池 22 | */ 23 | protected char[] pooling = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-=+".toCharArray(); 24 | 25 | 26 | /** 27 | * 快速随机模式:一次生成全部位置的随机字符 28 | */ 29 | protected boolean fast = false; 30 | 31 | /** 32 | * 使用线程安全的随机 33 | */ 34 | protected boolean threadSafe = true; 35 | 36 | 37 | protected boolean shuffle = false; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/v2/core/config/RecyclingModeConfig.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.v2.core.config; 2 | 3 | import lombok.Data; 4 | import lombok.experimental.Accessors; 5 | 6 | /** 7 | * @author : HK意境 8 | * @ClassName : RecyclingModeConfig 9 | * @date : 2023/6/4 15:18 10 | * @description : 进制算法配置: 73进制 11 | * @Todo : 12 | * @Bug : 13 | * @Modified : 14 | * @Version : 1.0 15 | */ 16 | @Data 17 | @Accessors(chain = true) 18 | public class RecyclingModeConfig { 19 | 20 | /** 21 | * 字符串池 22 | */ 23 | private char[] pooling = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_#$*()+=@!~".toCharArray(); 24 | 25 | 26 | /** 27 | * 初始化的计数: 默认 1111111, 能够生成4位的code 28 | */ 29 | private Long initNumber = 10000000L; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/v2/core/generator/AbstractShortUrlGenerator.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.v2.core.generator; 2 | 3 | import com.google.common.hash.BloomFilter; 4 | import com.hk.surl.v2.core.config.CommonConfig; 5 | import com.hk.surl.v2.entity.ShortURI; 6 | import com.hk.surl.v2.filter.ShortUrlBloomFilter; 7 | 8 | /** 9 | * @author : HK意境 10 | * @ClassName : AbstractShortUrlGenerator 11 | * @date : 2023/6/3 23:04 12 | * @description : 如果需要自定义实现自己的生成器,建议继承此类进行设计 13 | * @Todo : 14 | * @Bug : 15 | * @Modified : 16 | * @Version : 1.0 17 | */ 18 | public abstract class AbstractShortUrlGenerator implements IShortUrlGenerator { 19 | 20 | 21 | /** 22 | * 目标短链接实体 23 | */ 24 | protected T shortUrl; 25 | 26 | /** 27 | * 通用配置 28 | */ 29 | protected CommonConfig commonConfig; 30 | 31 | /** 32 | * 布隆过滤器 33 | */ 34 | protected ShortUrlBloomFilter bloomFilter; 35 | 36 | 37 | /** 38 | * 生成短链接之前的前置处理步骤,准备步骤等操作 39 | * @return T {@link ShortURI} 40 | */ 41 | public abstract T preProcess(); 42 | 43 | 44 | /** 45 | * 生成短链接实体模板方法 46 | * @return T {@link ShortURI} 47 | */ 48 | @Override 49 | public T generate(T shortUrl) { 50 | 51 | // 前置处理 52 | this.preProcess(); 53 | 54 | // 生成方法 55 | this.doGenerate(shortUrl); 56 | 57 | // 后置处理 58 | this.postProcess(); 59 | 60 | return this.shortUrl; 61 | } 62 | 63 | 64 | /** 65 | * 生成短链接 66 | * @return 67 | */ 68 | public abstract T doGenerate(T shortUrl); 69 | 70 | 71 | /** 72 | * 生成短链接数据之后的后置处理 73 | * @return T {@link ShortURI} 74 | */ 75 | public abstract T postProcess(); 76 | 77 | } 78 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/v2/core/generator/IShortUrlGenerator.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.v2.core.generator; 2 | 3 | import com.hk.surl.v2.entity.ShortURI; 4 | 5 | /** 6 | * @author : HK意境 7 | * @ClassName : IShortUrlGenerator 8 | * @date : 2023/6/3 22:44 9 | * @description : 10 | * @Todo : 11 | * @Bug : 12 | * @Modified : 13 | * @Version : 1.0 14 | */ 15 | @FunctionalInterface 16 | public interface IShortUrlGenerator { 17 | 18 | /** 19 | * 生成短链接实体抽象方法 20 | * @return {@link ShortURI} 21 | */ 22 | public T generate(T shortURI); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/v2/core/generator/distribute/DistributedShortUrlGenerator.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.v2.core.generator.distribute; 2 | 3 | import com.hk.surl.v2.core.config.DistributeConfig; 4 | import com.hk.surl.v2.core.generator.AbstractShortUrlGenerator; 5 | import com.hk.surl.v2.entity.ShortURLExt; 6 | import com.hk.surl.v2.util.SnowFlakeIdWorker; 7 | import lombok.Data; 8 | import lombok.experimental.Accessors; 9 | 10 | /** 11 | * @author : HK意境 12 | * @ClassName : DistributedShortUrlGenerator 13 | * @date : 2023/6/7 20:04 14 | * @description : 用于原始链接非常长的情况,并且系统每天生产大量短链接,对于短链接的数据安全敏感层度较低的业务场景 15 | * 因为雪花算法获取到的分布式ID 是递增的,存在生成后的短链接不安全的问题,所以需要更具加密策略进行选择 16 | * @Todo : 17 | * @Bug : 18 | * @Modified : 19 | * @Version : 1.0 20 | */ 21 | @Data 22 | @Accessors(chain = true) 23 | public class DistributedShortUrlGenerator extends AbstractShortUrlGenerator { 24 | 25 | protected DistributeConfig distributeConfig; 26 | 27 | protected SnowFlakeIdWorker idWorker; 28 | 29 | public DistributedShortUrlGenerator() { 30 | 31 | this.idWorker = new SnowFlakeIdWorker(); 32 | } 33 | 34 | @Override 35 | public ShortURLExt preProcess() { 36 | return null; 37 | } 38 | 39 | @Override 40 | public ShortURLExt doGenerate(ShortURLExt shortUrl) { 41 | 42 | long id = this.idWorker.nextId(); 43 | String code = String.valueOf(id); 44 | 45 | if (this.distributeConfig.isSafe()) { 46 | // 安全控制: 47 | code = Long.toHexString(id); 48 | } 49 | 50 | // 设置 51 | this.shortUrl.setShortCode(code) 52 | .setShortURI(code); 53 | 54 | return this.shortUrl; 55 | } 56 | 57 | @Override 58 | public ShortURLExt postProcess() { 59 | return null; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/v2/core/generator/hash/MD5ShortUrlGenerator.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.v2.core.generator.hash; 2 | 3 | import com.hk.surl.v2.core.config.HashModeConfig; 4 | import com.hk.surl.v2.core.generator.AbstractShortUrlGenerator; 5 | import com.hk.surl.v2.core.strategy.hash.HashType; 6 | import com.hk.surl.v2.entity.ShortURLExt; 7 | import lombok.Data; 8 | import lombok.experimental.Accessors; 9 | import lombok.extern.slf4j.Slf4j; 10 | 11 | /** 12 | * @author : HK意境 13 | * @ClassName : MD5ShortUrlGenerator 14 | * @date : 2023/6/5 19:03 15 | * @description : Hash 算法,采用 MD5 进行hash,生成某个长度的 32 位的字符串 16 | * @Todo : 17 | * @Bug : 18 | * @Modified : 19 | * @Version : 1.0 20 | */ 21 | @Slf4j 22 | @Data 23 | @Accessors(chain = true) 24 | public class MD5ShortUrlGenerator extends AbstractShortUrlGenerator { 25 | 26 | /** 27 | * 采用 MD5 算法 28 | */ 29 | protected HashModeConfig hashModeConfig = new HashModeConfig() 30 | .setType(HashType.MD5); 31 | 32 | @Override 33 | public ShortURLExt preProcess() { 34 | return null; 35 | } 36 | 37 | 38 | /** 39 | * 此处需要携带长链接进入进行hash 生成 40 | * @param shortURLExt 41 | * @return 42 | */ 43 | @Override 44 | public ShortURLExt doGenerate(ShortURLExt shortURLExt) { 45 | 46 | HashType hashMode = this.hashModeConfig.getType(); 47 | 48 | // 原始长链接 49 | String longURI = shortURLExt.getLongURI(); 50 | 51 | // hash 生成值 52 | String hash = hashMode.hash(longURI); 53 | 54 | 55 | // 长度控制: 32 * 4 = 128 bit, 32 bit / 4 = 8 字符 56 | boolean autoLengthControl = this.hashModeConfig.getAutoLengthControl() && hash.length() > longURI.length(); 57 | if (autoLengthControl) { 58 | // 生成的长度比原始链接长度还长,需要进行控制: 59 | this.lengthControl(hash); 60 | } 61 | 62 | // 设置短链接hash值 63 | this.shortUrl.setShortCode(hash).setShortURI(hash); 64 | 65 | return this.shortUrl; 66 | } 67 | 68 | 69 | /** 70 | * 长度控制:前中后各截取3字符,一共3 * 3 = 9字符 71 | * head: [0, 2] 72 | * body: [10, 12], 73 | * tail: [, len - 3] 74 | */ 75 | private void lengthControl(String hash) { 76 | 77 | String head = hash.substring(0, 2); 78 | String body = hash.substring(10, 12); 79 | String tail = hash.substring(hash.length() - 3); 80 | 81 | // 设置新的编码 82 | String newHash = head + body + tail; 83 | this.shortUrl.setShortCode(newHash) 84 | .setShortURI(newHash); 85 | } 86 | 87 | 88 | /** 89 | * 后置处理,如果hash 冲突,则再后置处理进行循环再次hash ,使用 布隆过滤器进行判断 90 | * @return 91 | */ 92 | @Override 93 | public ShortURLExt postProcess() { 94 | 95 | // 在这里进行 bloom filter 布隆过滤器的校验 96 | 97 | 98 | return null; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/v2/core/generator/hash/MurmurHashShortUrlGenerator.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.v2.core.generator.hash; 2 | 3 | import com.hk.surl.v2.core.config.HashModeConfig; 4 | import com.hk.surl.v2.core.generator.AbstractShortUrlGenerator; 5 | import com.hk.surl.v2.core.strategy.hash.HashType; 6 | import com.hk.surl.v2.entity.ShortURLExt; 7 | import lombok.Data; 8 | import lombok.experimental.Accessors; 9 | import lombok.extern.slf4j.Slf4j; 10 | 11 | /** 12 | * @author : HK意境 13 | * @ClassName : MurmurHashShortUrlGenerator 14 | * @date : 2023/6/6 20:42 15 | * @description : 16 | * @Todo : 17 | * @Bug : 18 | * @Modified : 19 | * @Version : 1.0 20 | */ 21 | @Slf4j 22 | @Data 23 | @Accessors(chain = true) 24 | public class MurmurHashShortUrlGenerator extends AbstractShortUrlGenerator { 25 | 26 | /** 27 | * 采用 murmurHash 算法 28 | */ 29 | protected HashModeConfig hashModeConfig = new HashModeConfig(); 30 | 31 | @Override 32 | public ShortURLExt preProcess() { 33 | return null; 34 | } 35 | 36 | 37 | /** 38 | * 此处需要携带长链接进入进行hash 生成 39 | * @param shortURLExt 40 | * @return 41 | */ 42 | @Override 43 | public ShortURLExt doGenerate(ShortURLExt shortURLExt) { 44 | 45 | HashType hashMode = this.hashModeConfig.getType(); 46 | 47 | // 原始长链接 48 | String longURI = shortURLExt.getLongURI(); 49 | 50 | // hash 生成值 51 | String hash = hashMode.hash(longURI); 52 | 53 | // 长度控制: 32 bit / 4 = 8 字符, 所以 murmurHash 没有必要进行二次长度控制 54 | 55 | // 设置短链接hash值 56 | this.shortUrl.setShortCode(hash).setShortURI(hash); 57 | 58 | return this.shortUrl; 59 | } 60 | 61 | 62 | /** 63 | * 长度控制:前中后各截取3字符,一共3 * 3 = 9字符 64 | * head: [0, 2] 65 | * body: [10, 12], 66 | * tail: [, len - 3] 67 | */ 68 | private void lengthControl(String hash) { 69 | 70 | String head = hash.substring(0, 2); 71 | String body = hash.substring(10, 12); 72 | String tail = hash.substring(hash.length() - 3); 73 | 74 | // 设置新的编码 75 | String newHash = head + body + tail; 76 | this.shortUrl.setShortCode(newHash) 77 | .setShortURI(newHash); 78 | } 79 | 80 | 81 | /** 82 | * 后置处理,如果hash 冲突,则再后置处理进行循环再次hash ,使用 布隆过滤器进行判断 83 | * @return 84 | */ 85 | @Override 86 | public ShortURLExt postProcess() { 87 | 88 | // 在这里进行 bloom filter 布隆过滤器的校验 89 | 90 | return this.shortUrl; 91 | } 92 | 93 | 94 | } 95 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/v2/core/generator/random/RandomShortUrlGenerator.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.v2.core.generator.random; 2 | 3 | import com.hk.surl.v2.core.config.RandomConfig; 4 | import com.hk.surl.v2.core.generator.AbstractShortUrlGenerator; 5 | import com.hk.surl.v2.entity.ShortURLExt; 6 | import lombok.Data; 7 | import lombok.experimental.Accessors; 8 | import java.util.*; 9 | import java.util.concurrent.ThreadLocalRandom; 10 | 11 | /** 12 | * @author : HK意境 13 | * @ClassName : RandomShortUrlGenerator 14 | * @date : 2023/6/6 22:20 15 | * @description : 16 | * @Todo : 17 | * @Bug : 18 | * @Modified : 19 | * @Version : 1.0 20 | */ 21 | @Data 22 | @Accessors(chain = true) 23 | public class RandomShortUrlGenerator extends AbstractShortUrlGenerator { 24 | 25 | /** 26 | * 随机策略配置 27 | */ 28 | protected RandomConfig randomConfig; 29 | 30 | /** 31 | * 随机数 32 | */ 33 | protected Random random; 34 | 35 | public RandomShortUrlGenerator() { 36 | 37 | this.random = this.randomConfig.isThreadSafe() ? ThreadLocalRandom.current() : new Random(); 38 | } 39 | 40 | @Override 41 | public ShortURLExt preProcess() { 42 | return null; 43 | } 44 | 45 | @Override 46 | public ShortURLExt doGenerate(ShortURLExt shortUrl) { 47 | 48 | // 获取配置 49 | boolean fast = this.randomConfig.isFast(); 50 | Integer length = this.commonConfig.getLength(); 51 | 52 | String code = null; 53 | 54 | // 普通模式:普通模式更加均匀 55 | char[] pooling = this.randomConfig.getPooling().clone(); 56 | 57 | // 是否需要混匀 58 | if (this.randomConfig.isShuffle()) { 59 | pooling = this.shuffle(pooling); 60 | } 61 | 62 | // 生成随机短码 63 | StringBuilder sb = new StringBuilder(); 64 | for (int i = 0; i < length; i++) { 65 | char ch = pooling[random.nextInt(pooling.length)]; 66 | sb.append(ch); 67 | } 68 | 69 | // 设置结果 70 | code = sb.toString(); 71 | this.shortUrl.setShortCode(code) 72 | .setShortURI(code); 73 | return this.shortUrl; 74 | } 75 | 76 | 77 | /** 78 | * 混匀字符串池 79 | * @param pooling 80 | * @return 81 | */ 82 | private char[] shuffle(char[] pooling) { 83 | // 混匀 84 | ArrayList list = new ArrayList<>(); 85 | for (char c : pooling) { 86 | list.add(c); 87 | } 88 | // 扰乱 89 | Collections.shuffle(list); 90 | // 放回 91 | StringBuilder sb = new StringBuilder(); 92 | for (Character c : list) { 93 | sb.append(c); 94 | } 95 | 96 | return sb.toString().toCharArray(); 97 | } 98 | 99 | @Override 100 | public ShortURLExt postProcess() { 101 | 102 | // 在此处使用布隆过滤器进行重复判断 103 | 104 | return null; 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/v2/core/generator/simple/DefaultShortUrlGenerator.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.v2.core.generator.simple; 2 | 3 | 4 | import com.hk.surl.v2.core.config.RecyclingModeConfig; 5 | import com.hk.surl.v2.core.generator.AbstractShortUrlGenerator; 6 | import com.hk.surl.v2.entity.ShortURLExt; 7 | import lombok.extern.slf4j.Slf4j; 8 | 9 | import java.util.concurrent.atomic.AtomicLong; 10 | 11 | /** 12 | * @author : HK意境 13 | * @ClassName : DefaultShortUrlGenerator 14 | * @date : 2023/6/3 23:33 15 | * @description : 默认短链接生成器,采用 64 进制算法 16 | * @Todo : 17 | * @Bug : 18 | * @Modified : 19 | * @Version : 1.0 20 | */ 21 | @Slf4j 22 | public class DefaultShortUrlGenerator extends AbstractShortUrlGenerator { 23 | 24 | 25 | /** 26 | * 进制配置 27 | */ 28 | protected RecyclingModeConfig recyclingModeConfig = new RecyclingModeConfig(); 29 | 30 | 31 | /** 32 | * 进制累加器 33 | */ 34 | protected AtomicLong adder; 35 | 36 | 37 | /** 38 | * 初始化adder 39 | */ 40 | public DefaultShortUrlGenerator() { 41 | 42 | // 初始化实体 43 | this.shortUrl = new ShortURLExt(); 44 | 45 | Long initNumber = this.recyclingModeConfig.getInitNumber(); 46 | this.adder = new AtomicLong(initNumber); 47 | } 48 | 49 | 50 | /** 51 | * 52 | * @return 53 | */ 54 | @Override 55 | public ShortURLExt preProcess() { 56 | return null; 57 | } 58 | 59 | @Override 60 | public ShortURLExt doGenerate(ShortURLExt shortURLExt) { 61 | 62 | // 循环取余 63 | long number = this.adder.incrementAndGet(); 64 | int length = this.recyclingModeConfig.getPooling().length; 65 | 66 | // builder 67 | StringBuilder sb = new StringBuilder(); 68 | 69 | while (number != 0L) { 70 | int mode = (int) (number % length); 71 | number = number / length; 72 | 73 | sb.append(this.recyclingModeConfig.getPooling()[mode]); 74 | } 75 | 76 | // 设置短链接码 77 | String code = sb.toString(); 78 | this.shortUrl.setShortCode(code) 79 | .setShortURI(code); 80 | 81 | return this.shortUrl; 82 | } 83 | 84 | @Override 85 | public ShortURLExt postProcess() { 86 | log.info("short code:{}", this.shortUrl.getShortCode()); 87 | return null; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/v2/core/provider/IShortUrlProvider.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.v2.core.provider; 2 | 3 | import com.hk.surl.v2.core.generator.IShortUrlGenerator; 4 | import com.hk.surl.v2.entity.ShortURI; 5 | 6 | /** 7 | * @author : HK意境 8 | * @ClassName : IShortUrlProvider 9 | * @date : 2023/6/3 22:32 10 | * @description : 11 | * @Todo : 12 | * @Bug : 13 | * @Modified : 14 | * @Version : 1.0 15 | */ 16 | @FunctionalInterface 17 | public interface IShortUrlProvider { 18 | 19 | public T provide(IShortUrlGenerator generator); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/v2/core/strategy/cache/CacheStrategy.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.v2.core.strategy.cache; 2 | 3 | import lombok.Data; 4 | import lombok.experimental.Accessors; 5 | 6 | import java.time.LocalDateTime; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | /** 10 | * @author : HK意境 11 | * @ClassName : CacheStrategy 12 | * @date : 2023/6/4 0:13 13 | * @description : 缓存策略 14 | * @Todo : 15 | * @Bug : 16 | * @Modified : 17 | * @Version : 1.0 18 | */ 19 | @Data 20 | @Accessors(chain = true) 21 | public class CacheStrategy { 22 | 23 | /** 24 | * 缓存类型 25 | */ 26 | protected StorageType storageType; 27 | 28 | /** 29 | * 到期期时间: 可以通过指定具体的过期时间 30 | */ 31 | protected LocalDateTime expireTime; 32 | 33 | /** 34 | * 过期时间:指定缓存时长, 默认七天 35 | */ 36 | protected Long ttl = 7L; 37 | 38 | 39 | /** 40 | * 过期时间单位:默认天 41 | */ 42 | protected TimeUnit timeUnit = TimeUnit.DAYS; 43 | 44 | 45 | /** 46 | * 缓存选择类型 47 | */ 48 | static enum StorageType { 49 | 50 | // 不用缓存 51 | NONE, 52 | // 用内存作为缓存,采用Map类型 53 | Simple, 54 | // Caffeine中间件缓存 55 | Caffeine, 56 | // Redis 进行缓存 57 | Redis, 58 | 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/v2/core/strategy/hash/HashStrategy.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.v2.core.strategy.hash; 2 | 3 | /** 4 | * @author : HK意境 5 | * @ClassName : HashStrategy 6 | * @date : 2023/6/5 19:50 7 | * @description : hash 接口,用于枚举策略的实现 8 | * @Todo : 9 | * @Bug : 10 | * @Modified : 11 | * @Version : 1.0 12 | */ 13 | public interface HashStrategy { 14 | 15 | public String hash(String plain); 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/v2/core/strategy/hash/HashType.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.v2.core.strategy.hash; 2 | 3 | import com.hk.surl.v2.util.HashUtil; 4 | 5 | /** 6 | * @ClassName : HashType 7 | * @author : HK意境 8 | * @date : 2023/6/5 19:54 9 | * @description : Hash 枚举算法实现 10 | * @Todo : 11 | * @Bug : 12 | * @Modified : 13 | * @Version : 1.0 14 | */ 15 | public enum HashType implements HashStrategy { 16 | 17 | // MD5 摘要算法 18 | MD5() { 19 | @Override 20 | public String hash(String plain) { 21 | return HashUtil.md5(plain); 22 | } 23 | }, 24 | 25 | // MurmurHash 一种非加密型哈希函数,适用于一般的哈希检索操作 26 | MurmurHash() { 27 | @Override 28 | public String hash(String plain) { 29 | return HashUtil.murmurHash(plain); 30 | } 31 | }, 32 | 33 | // 针对短消息设计的伪随机函数 34 | SipHash() { 35 | @Override 36 | public String hash(String plain) { 37 | return null; 38 | } 39 | }, 40 | } -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/v2/entity/ShortURI.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.v2.entity; 2 | 3 | 4 | import lombok.Data; 5 | import lombok.experimental.Accessors; 6 | 7 | 8 | /** 9 | * @author : HK意境 10 | * @ClassName : ShortURI 11 | * @date : 2023/6/3 22:35 12 | * @description : 13 | * @Todo : 14 | * @Bug : 15 | * @Modified : 16 | * @Version : 1.0 17 | */ 18 | @Data 19 | @Accessors(chain = true) 20 | public class ShortURI { 21 | 22 | protected String longURI; 23 | 24 | protected String shortCode; 25 | 26 | protected String shortURI; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/v2/entity/ShortURL.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.v2.entity; 2 | 3 | import lombok.Data; 4 | import lombok.experimental.Accessors; 5 | 6 | /** 7 | * @author : HK意境 8 | * @ClassName : ShortURL 9 | * @date : 2023/6/4 14:54 10 | * @description : 11 | * @Todo : 12 | * @Bug : 13 | * @Modified : 14 | * @Version : 1.0 15 | */ 16 | @Data 17 | @Accessors(chain = true) 18 | public class ShortURL extends ShortURI{ 19 | } 20 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/v2/entity/ShortURLExt.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.v2.entity; 2 | 3 | import lombok.Data; 4 | import lombok.ToString; 5 | 6 | /** 7 | * @author : HK意境 8 | * @ClassName : ShortURLExt 9 | * @date : 2023/6/4 14:54 10 | * @description : 11 | * @Todo : 12 | * @Bug : 13 | * @Modified : 14 | * @Version : 1.0 15 | */ 16 | @Data 17 | @ToString 18 | public class ShortURLExt extends ShortURL { 19 | } 20 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/v2/filter/ShortUrlBloomFilter.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.v2.filter; 2 | 3 | import com.google.common.hash.BloomFilter; 4 | import com.google.common.hash.Funnels; 5 | import com.hk.surl.v2.core.config.BloomFilterConfig; 6 | import lombok.Data; 7 | import lombok.experimental.Accessors; 8 | import java.nio.charset.StandardCharsets; 9 | 10 | /** 11 | * @author : HK意境 12 | * @ClassName : ShortUrlBloomFilter 13 | * @date : 2023/6/6 19:46 14 | * @description : 15 | * @Todo : 16 | * @Bug : 17 | * @Modified : 18 | * @Version : 1.0 19 | */ 20 | @Data 21 | @Accessors(chain = true) 22 | public class ShortUrlBloomFilter { 23 | 24 | private static BloomFilter instance; 25 | 26 | /** 27 | * 布隆过滤器配置 28 | */ 29 | private BloomFilterConfig config; 30 | 31 | public ShortUrlBloomFilter() { 32 | // 采用默认配置 33 | this(new BloomFilterConfig()); 34 | } 35 | 36 | /** 37 | * 传入 bloom filter 配置 38 | * @param config 39 | */ 40 | public ShortUrlBloomFilter(BloomFilterConfig config) { 41 | this.config = config; 42 | 43 | // 初始化 BloomFilter 44 | this.init(); 45 | } 46 | 47 | 48 | /** 49 | * 初始化 50 | */ 51 | private void init() { 52 | instance = BloomFilter.create(Funnels.stringFunnel(StandardCharsets.UTF_8), 53 | this.config.getInsertions(), this.config.getFpp()); 54 | } 55 | 56 | 57 | public static BloomFilter getInstance() { 58 | return instance; 59 | } 60 | 61 | /** 62 | * 添加元素 63 | * @param value 64 | */ 65 | public boolean addElement(String value) { 66 | return instance.put(value); 67 | } 68 | 69 | /** 70 | * 是否可能包含元素:如果不存在则一定不存在 71 | * @param value 72 | * @return 73 | */ 74 | public boolean containsElement(String value) { 75 | return instance.mightContain(value); 76 | } 77 | 78 | 79 | } 80 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/v2/util/BloomFilterUtil.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.v2.util; 2 | 3 | /** 4 | * @author : HK意境 5 | * @ClassName : BloomFilterUtil 6 | * @date : 2023/6/6 19:56 7 | * @description : 8 | * @Todo : 9 | * @Bug : 10 | * @Modified : 11 | * @Version : 1.0 12 | */ 13 | public class BloomFilterUtil { 14 | 15 | 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /url-core/src/main/java/com/hk/surl/v2/util/HashUtil.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.v2.util; 2 | 3 | import com.google.common.hash.HashFunction; 4 | import com.google.common.hash.Hashing; 5 | import java.nio.charset.StandardCharsets; 6 | 7 | 8 | /** 9 | * @author : HK意境 10 | * @ClassName : HashUtil 11 | * @date : 2023/6/5 19:11 12 | * @description : Hash 算法实现工具 13 | * @Todo : 14 | * @Bug : 15 | * @Modified : 16 | * @Version : 1.0 17 | */ 18 | public class HashUtil { 19 | 20 | 21 | /** 22 | * 进行md5摘要 23 | * @param plain 24 | * @return 25 | */ 26 | public static String md5(String plain) { 27 | 28 | HashFunction hashFunction = Hashing.md5(); 29 | String md5 = hashFunction.hashString(plain, StandardCharsets.UTF_8).toString(); 30 | 31 | return md5; 32 | } 33 | 34 | 35 | /** 36 | * 进行 MurmurHash 算法生成 32 位 37 | * @param plain 38 | * @return 39 | */ 40 | public static String murmurHash(String plain) { 41 | 42 | String hash = Hashing.murmur3_32_fixed() 43 | .hashString(plain, StandardCharsets.UTF_8) 44 | .toString(); 45 | 46 | return hash; 47 | } 48 | 49 | 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /url-core/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=short-url 2 | 3 | server.address=localhost 4 | server.port=8888 5 | 6 | spring.mvc.servlet.path=/ 7 | 8 | # mysql 9 | spring.datasource.url=jdbc:mysql://localhost:3306/short_url?useUnicode=true&characterEncoding=utf8&useSSL=false 10 | spring.datasource.username=root 11 | spring.datasource.password=root 12 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 13 | 14 | 15 | # redis 16 | spring.redis.port=6379 17 | spring.redis.host=localhost 18 | 19 | 20 | # short-url 21 | short.url.string.pool=0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_ 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /url-core/src/main/resources/direct/ER模型.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HK-hub/Short-URL/e666ded026b8439fad95dfb0d3d50516e7d6ad9f/url-core/src/main/resources/direct/ER模型.png -------------------------------------------------------------------------------- /url-core/src/main/resources/direct/homepage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HK-hub/Short-URL/e666ded026b8439fad95dfb0d3d50516e7d6ad9f/url-core/src/main/resources/direct/homepage.png -------------------------------------------------------------------------------- /url-core/src/main/resources/direct/readme.md: -------------------------------------------------------------------------------- 1 | # Short-Url 短链接生产器 2 | ## 简介 3 | `Short-Url` 短链接生成器是个人制作的一个集合了多种生成策略,加密安全配置,缓存配置等可选可自定义的短链接生成工具包。 4 | 目前已经开源出来啦,开源地址如下: 5 | 6 | https://github.com/HK-hub/Short-URL 7 | ## 功能 8 | **Short-Url** 短链接生成工具包提供了多种短链接生成算法,包括:字符串池随机算法,短地址发号器算法,MD5加密截取算法,BASE64编码截取算法,URL压缩算法,分布式雪花算法等。 9 | 此外集成了线程安全配置,缓存配置,加密配置,其他配置等可选可自定义的配置,可以根据自己的需求进行配置。实现对于普通 `URL` 长链接到短链接的转换,缓存,加密,以及对于已经生成的短链接的解密解析,重定向等功能。 10 | 11 | ## 安装 12 | 目前 `Short-Url` 短链接生成工具包还处于开发,天使测试阶段,暂时不支持编译安装,只支持使用 `maven` 或者 `gradle` 安装。`Short-Url` 目前已经打包成为一个 `Starter`, 使用时候只需要导入依赖 `Jar`包即可,如下: 13 | 14 | ```xml 15 | 16 | com.hk 17 | short-url-boot-starter 18 | 1.0.0 19 | 20 | ``` 21 | 22 | ## 使用 23 | **Short-Url** 短链接生成器内置了多种短链接生成算法策略,可以根据自己的需求选择使用,如下: 24 | - 字符串池随机算法:`StringPoolRandomStrategy` 25 | - 发号器算法:`SnowFlakeStrategy` 26 | - MD5加密截取算法:`Md5Strategy` 27 | - BASE64编码截取算法:`Base64Strategy` 28 | - URL压缩算法:`UrlCompressStrategy` 29 | - 分布式ID生成算法:`DistributedIdStrategy` 30 | 31 | 除此之外还聚合了多种缓存配置,加密配置,线程安全配置等可选可自定义的配置,可以根据自己的需求进行配置。 32 | 33 | 34 | | 线程安全配置 | 缓存配置 | 其他配置 | 35 | | ---- | ---- | ---- | 36 | | 默认配置 | 永久有效 | 短链多级长度配置 | 37 | | 乐观锁配置 | 多级过期时间 | 短链接加密配置 | 38 | | 加锁配置 | 自定义过期时间 | 短链接解析配置 | 39 | | |不缓存 | 短链接访问配置 | 40 | 41 | 42 | 除此之外,`Short-Url`短链接生成器也提供了几个已经配置好的短链接生成模板,可以直接使用来快速生成和控制。 43 | 44 | 45 | ## 其他 46 | 47 | 48 | ## 反馈 49 | 任何有关于 **Short-Url** 工具包的相关疑问,出现的Bug, 错误等都可以联系邮箱:3161880795@qq.com 进行及时的交流与反馈。 50 | 51 | 52 | ## 关于 53 | - 关于作者:作者是一个潜心修炼的 Java 学生,路漫漫其修远兮,吾将上下而求索。 54 | - 作者邮箱:3161880795@qq.com 55 | - Github地址:https://www.github.com/hk-hub/ 56 | ## 版本 57 | **Short-Url** 目前还是处于第一个版本(天使版) ,还有很多的缺陷和漏洞需要进行修复弥补,也还有很多的功能需要进行扩展,所有静待等候哦。 58 | 59 | 60 | ## 更新日志 61 | 2022-4-20:发布了具备核心功能,基本功能的第一个版本。 62 | -------------------------------------------------------------------------------- /url-core/src/main/resources/direct/短链生成算法.drawio: -------------------------------------------------------------------------------- 1 | 7Vxbj9o4FP41lroPrZI4F+cxgdCuxGyrGXXbPq084AnZhpgNZoD++tqOPRDiAdoCobOpRmp8bAdyzneuPgHA3nT1tsSzyQ0dkxw41ngFYB84jm05kP8nKOuK4kOnIqRlNlaLNoS77BvROxV1kY3JvLaQUZqzbFYnjmhRkBGr0XBZ0mV92QPN6586wylpEO5GOG9SP2VjNqmoyAk29HckSyf6k20/rGamWC9WTzKf4DFdbpFgAmCvpJRVV9NVj+SCeZov1b7BM7NPX6wkBTtmw7vJ5w9pehsOZuzvdDgYxA+f3r92lXjmbK2fmIw5A9SQlmxCU1rgPNlQ45IuijERt7X4aLNmSOmME21O/JcwtlbSxAtGOWnCprmaJauMfRbb33hq9GVrpr9Sd5aDtRo0H1dxYE4X5YjseUYNG1ymhO3jhUKmYMDWJyhuviV0Sli55gtKkmOWPdYRghXQ0qd1G1nwCyWOHxCNuu8jzhfqk0DigcgFoQ+SECAIIiQuQg/EA3kBQZyAxAcoAZHXkGtdastJxsjdDEvOLbnu1iX0LLcfScnIai971CyECvhK80M1XG7UyNa6MdlSId86E0Md9PKx7hyJdf+qoO4YoR5bANkgQSCCAHkS2AhwFCUuiBGIo4+3w2TF+D6pFwkIEznVF4qQBCC2QRxfnRY4qG01gAZmByDkdmWw4bFj3U042DmLJX+vlZuh3zY3bb9No2JvmZSNgTEbFc71cr1licTwy/bcZpsc/box8o80RsF1GaOgVTfxIiRqh1clUtiqkl7G8x8rGue6ROMbvBEPbrnrD6U3ikEUGvwTp0R9GQkHIgA2hLs83ZqJy1lJR2Q+P+ye7vHoayqF/n7B8qwgij6v5Gu9sf2df+fxabD1CCEwRghxJPOMQKQdXCxVzCWk5InoLGoG1/NlNs2x4GP8QAumtU3AfDTJ8vEQr+lCPMuccdbrUczjjuwbX4+1YPh0yZSWOX5txZ3Yqe5ZEiGpD1oU9g7pBq9qC4d4zvS3oXmOZ/PsPtcyn3ItyoqYMkanhzTzeEl7Tl3SrmuQtGuQtG25ZxI1Mqofj/RCJCPvRCScDWV7ZcKDjMpRvwmMPxrI4PxiUrAl/Up6NKclpxe0gkqW5zsknGdpwYc5eRDbBMOzEc4jRWbCHsdzrthZkQ7lmr67odwqPgoS5XsfclmFmWTjMSmknWeY4fsnbM5oVjDJZy/mf1wcPWHBPf7Fe3xsb8b8TywvWY8W/FlwJpFBOLCWRIDLgJm9+nYYSDqJ8I/DjV53ctiEBthw+YfSNHPYBABJIHGrLVK4g8a6A8SvAcJzWgaELtfupvA9YSaarkIj4pUkRSCy9qClsx+nhkuA2oaLyYAEIsLjot+UMo1hXwLCQFBiKOCxJ+g4gce2rcMe27FMHjs4WxXTXCwTKhNI/vRA5OsamSMZZWv9igCSzAyf1PK5XQf53KmgGdBH6yA8Ekln00HnmUJgpYOeBIksrYZ9kY8JQz0Aoa0u4kBOhdqad2g5K1q8ZsR3YbS4DbTc9D1VfFcenNuTAUC+MtdRpDHjdwg5P0JQMwS8MEI8o2PiSBCQ8GR2CFVQiAZ/9pugeCWdVCyRgwSoYru5qIsGTw4d227dFzXLgDGeE98VBsbhT25pYxOI4gRypfOxBLg603J+fMC2vQ9sSrU7Zdsp6B+s/EN1xyup/LutyvRC7RhHy8a6Ktk4pvwciXovClQCGQ4Mdb3m0c2uiNvuGPBMNfeLnq64doMpnS3bUYPD+tJaz8Drafp29s89/vhf/vH2r+HN45fsw+vf7Hx5h2tnlpUbtGnKoKkyHYgkJIS6BIZ+S1Pmt35QrCOKH3UTe4q23UlxzUtpr2Vqnr3sSbHu26/J2hclQp7YV4d/VZMmF3qlNaFM/rtsbG82Bo/HxrUc/kJT4bjqTI9UY0AUmOyAL9L0WHaviwu3w8apsdH6OTBslollLS9Q5WBRqom0uQg7kLQBktZPf+EzDWa/HpJ1gcSBQCIwHDteOpAw9ZzJaEEcN/qihYxbCXFOENXxoA+TOoOw3yD8fu1k0NwOsmsQfBDZ6nwxTKSP6LBxbmy0HlG4pvy9GW020NIh4cRIaD1scJux4FVX2X6mqKbfQT78Ggdoqf6592sfNOCiu8iTLcL8gu/wc4HZ+5JfpRK9+yKB5vYrq8jZl3zH2ViIbvfdtXYK0W5wpM7AC6mIUTLNZOVFnvqcTaJGpjqtGj1TGis73OOqlTbWP9XgyfdiAvFuOjdo12a1kNO21TJ5j59g5EvM/htiNQj/6OwfBUdm/zrWO7mk9/YcP/X9NxvTn37mRAYD0f890TuAih+xlS3UAIxfz1Q49kXUGA1Us1/kG3qNOxycFgdnzPf5cPNrV3Ju6zfDYPId -------------------------------------------------------------------------------- /url-core/src/test/java/com/hk/surl/EncryptTest.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl; 2 | 3 | import com.hk.surl.core.common.util.EncryptUtil; 4 | import org.junit.Test; 5 | 6 | /** 7 | * @author : HK意境 8 | * @ClassName : EncryptTest 9 | * @date : 2022/4/18 20:52 10 | * @description : 11 | * @Todo : 12 | * @Bug : 13 | * @Modified : 14 | * @Version : 1.0 15 | */ 16 | public class EncryptTest { 17 | 18 | @Test 19 | public void encryptUrl(){ 20 | 21 | String url = "https://www.enjoytrader89.com/?jskey=%1Cr%86%3C%CC%E8%DF%045%5B%D2H%86%963t%FA%0E%9F%CF%F1a%DD%01%A3uw%9Es%BAi%94J%1Ce%D6%CC%FCQ%DF%03#/"; 22 | String url2 = "https://blog.csdn.net/lilizhou2008/article/details/107625083" + 23 | "?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-107625083-blog-114280025.pc_relevant_paycolumn_v3&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-107625083-blog-114280025.pc_relevant_paycolumn_v3&utm_relevant_index=2"; 24 | System.out.println(EncryptUtil.MD5(url)); 25 | 26 | 27 | 28 | System.out.println(EncryptUtil.SHA1(url2)); 29 | 30 | } 31 | 32 | 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /url-core/src/test/java/com/hk/surl/GenerateStrategyTest.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl; 2 | 3 | import com.hk.surl.core.generator.ShortUrlGenerator; 4 | import com.hk.surl.core.generator.builder.ShortUrlGeneratorBuilder; 5 | import com.hk.surl.core.enums.strategy.LengthStrategy; 6 | import com.hk.surl.core.provider.distributed.SnowFlakeProvider; 7 | import com.hk.surl.domain.entity.ShortUrl; 8 | import org.junit.Test; 9 | 10 | /** 11 | * @author : HK意境 12 | * @ClassName : GenerateStrategyTest 13 | * @date : 2022/4/18 18:52 14 | * @description : 15 | * @Todo : 16 | * @Bug : 17 | * @Modified : 18 | * @Version : 1.0 19 | */ 20 | public class GenerateStrategyTest { 21 | 22 | @Test 23 | public void randomStrategyTest(){ 24 | ShortUrlGenerator generator = new ShortUrlGeneratorBuilder() 25 | .provider(new SnowFlakeProvider()) 26 | .length(LengthStrategy.LARGE_ENTERPRISE.getLength()).build(); 27 | ShortUrl shortURL = generator.generate(); 28 | 29 | System.out.println(shortURL); 30 | 31 | } 32 | 33 | 34 | // 测试 函数接口 35 | public void functionalInterfaceTest(){ 36 | 37 | new ShortUrlGeneratorBuilder().provider(); 38 | 39 | } 40 | 41 | 42 | 43 | } 44 | -------------------------------------------------------------------------------- /url-core/src/test/java/com/hk/surl/GeneratorBuilderTest.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl; 2 | 3 | import com.hk.surl.core.enums.strategy.ExpirationStrategy; 4 | import com.hk.surl.core.enums.strategy.SyncStrategy; 5 | import com.hk.surl.core.generator.ShortUrlGenerator; 6 | import com.hk.surl.core.generator.builder.ShortUrlGeneratorBuilder; 7 | import com.hk.surl.core.provider.random.RandomStringProvider; 8 | import com.hk.surl.domain.entity.ShortUrl; 9 | import org.junit.Test; 10 | 11 | /** 12 | * @author : HK意境 13 | * @ClassName : GeneratorBuilderTest 14 | * @date : 2022/6/1 14:13 15 | * @description : 建造者测试 16 | * @Todo : 17 | * @Bug : 18 | * @Modified : 19 | * @Version : 1.0 20 | */ 21 | public class GeneratorBuilderTest { 22 | 23 | static String longUrl = "http://localhost:8080/cloud/45464uy6terju54grfea464uy6terju54grfea44hytejorders64uy6terju54grfea44hytejorders64uy6terju54grfea44hytejorders64uy6terju54grfea44hytejorders64uy6terju54grfea44hytejorders64uy6terju54grfea44hytejorders64uy6terju54grfea44hytejorders64uy6terju54grfea44hytejorders64uy6terju54grfea44hytejorders64uy6terju54grfea44hytejorders64uy6terju54grfea44hytejorders64uy6terju54grfea44hytejorders64uy6terju54grfea44hytejorders64uy6terju54grfea44hytejorders64uy6terju54grfea44hytejorders64uy6terju54grfea44hytejorders64uy6terju54grfea44hytejorders64uy6terju54grfea44hytejorders64uy6terju54grfea44hytejorders64uy6terju54grfea44hytejorders64uy6terju54grfea44hytejorders64uy6terju54grfea44hytejorders64uy6terju54grfea44hytejorders64uy6terju54grfea44hytejorders64uy6terju54grfea44hytejorders64uy6terju54grfea44hytejorders64uy6terju54grfea44hytejorders4hytejorders/user/15645juytkuy4648/dehtrehgvsfd114545glete"; 24 | 25 | // 最小 API 方式 26 | @Test 27 | public void minApiBuildTest(){ 28 | 29 | // 全部使用默认配置,只需要传入一个 长链接即可 30 | ShortUrlGenerator generator = new ShortUrlGeneratorBuilder(longUrl).build(); 31 | 32 | // 生产短链接 33 | ShortUrl shortUrl = generator.generate(); 34 | 35 | System.out.println(shortUrl); 36 | 37 | } 38 | 39 | // 较为完整的API 40 | @Test 41 | public void fullConfigApi(){ 42 | 43 | // 通过建造者模式 获取生成器 44 | ShortUrlGenerator generator = new ShortUrlGeneratorBuilder(longUrl) 45 | .provider(new RandomStringProvider()) 46 | .length(8) 47 | .syncStrategy(SyncStrategy.DISABLE) 48 | .expireStrategy(ExpirationStrategy.ONE_DAYS) 49 | .build(); 50 | 51 | // 生产短链接 52 | ShortUrl shortUrl = generator.generate(); 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /url-core/src/test/java/com/hk/surl/JDBCTest.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl; 2 | 3 | import com.hk.surl.core.common.util.DatabaseUtil; 4 | import com.hk.surl.domain.entity.LongUrl; 5 | import com.mysql.cj.jdbc.Driver; 6 | import org.junit.Test; 7 | import org.springframework.jdbc.core.BeanPropertyRowMapper; 8 | import org.springframework.jdbc.core.JdbcTemplate; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * @author : HK意境 14 | * @ClassName : JDBCTest 15 | * @date : 2022/5/30 9:44 16 | * @description : 数据库操作测试 17 | * @Todo : 18 | * @Bug : 19 | * @Modified : 20 | * @Version : 1.0 21 | */ 22 | public class JDBCTest { 23 | 24 | private static final String username = "root"; 25 | private static final String password = "root" ; 26 | private static final String url = "jdbc:mysql://localhost:3306/db_short_url"; 27 | private static final Class driver = Driver.class ; 28 | 29 | 30 | // 测试数据库连接,查询 31 | @Test 32 | public void jdbcConnectTest(){ 33 | 34 | JdbcTemplate jdbcTemplate = new JdbcTemplate(DatabaseUtil.getDataSource(username, password, url, driver)); 35 | 36 | //String sql = "select count(*) from tb_long_url"; 37 | String sql = "select * from tb_long_url"; 38 | //Integer count = jdbcTemplate.queryFor 39 | // Object(sql, Integer.class); 40 | 41 | List urlList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(LongUrl.class)); 42 | 43 | 44 | System.out.println(urlList); 45 | } 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /url-core/src/test/java/com/hk/surl/LombokTest.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl; 2 | 3 | import com.hk.surl.domain.entity.ShortUrl; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.junit.jupiter.api.Test; 6 | 7 | /** 8 | * @author : HK意境 9 | * @ClassName : LombokTest 10 | * @date : 2022/4/13 21:23 11 | * @description : 12 | * @Todo : 13 | * @Bug : 14 | * @Modified : 15 | * @Version : 1.0 16 | */ 17 | @Slf4j(topic = "LombokTest") 18 | public class LombokTest { 19 | 20 | // 测试 lombok 的可用性和稳定性 21 | @Test 22 | public void getterAndSetterTest(){ 23 | 24 | System.out.println("test lombok"); 25 | ShortUrl shortURL = new ShortUrl("http://localhost:8080/123456-fioggreo"); 26 | System.out.println(shortURL.getShortUrl()+","+shortURL.getShortUrl()); 27 | log.info("url:{}, shortUrl:{}",shortURL.getShortUrl(),shortURL.getShortUrl()); 28 | 29 | } 30 | 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /url-core/src/test/java/com/hk/surl/RandomTest.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl; 2 | 3 | import com.hk.surl.core.provider.distributed.SnowFlakeProvider; 4 | import com.hk.surl.core.provider.random.RandomStringProvider; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.util.HashSet; 8 | import java.util.Random; 9 | import java.util.Set; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | /** 13 | * @author : HK意境 14 | * @ClassName : RandomTest 15 | * @date : 2022/5/29 15:28 16 | * @description : 17 | * @Todo : 18 | * @Bug : 19 | * @Modified : 20 | * @Version : 1.0 21 | */ 22 | public class RandomTest { 23 | 24 | 25 | // 测试随机算法稳定性 26 | @Test 27 | public void testRandomProvider(){ 28 | 29 | // 测试长度 30 | int lenght = 6; 31 | // 测试次数 32 | int sum = 100000 ; 33 | RandomStringProvider provider = new RandomStringProvider(); 34 | 35 | for (int i = 0; i < sum; i++) { 36 | String shotUrl = provider.createWithRandom(6); 37 | System.out.println(shotUrl); 38 | } 39 | 40 | } 41 | 42 | 43 | 44 | // 观察短链接冲突率 45 | @Test 46 | public void conflictRate() throws InterruptedException { 47 | 48 | // 记录 49 | Set set = new HashSet<>(); 50 | 51 | // 冲突率计算公式: 52 | // conflictRate=冲突个数/元素总数: 53 | // 对于 set 集合,不会存储 重复元数-> cr = (总数-set集合个数)/总数 54 | 55 | // 生产 1000000 百万数据 56 | int sum = 1000000; 57 | // 随机产生模拟 58 | RandomStringProvider provider = new RandomStringProvider(); 59 | // 测试长度 60 | int length = 12; 61 | 62 | for (int i = 0; i < sum; i++) { 63 | String shotUrl = provider.createWithRandom(length); 64 | //System.out.println(withSnowFlake); 65 | set.add(shotUrl); 66 | //TimeUnit.MILLISECONDS.sleep(random.nextInt(1000)); 67 | 68 | } 69 | 70 | System.out.println("元素总数:1000000(一百万)"); 71 | System.out.println("set 集合中个数: " +set.size()); 72 | System.out.println("重复元素出现次数:"+(sum-set.size())); 73 | // 计算冲突率 74 | double cr = (double)(sum-set.size())/sum; 75 | System.out.println("冲突率: " + cr); 76 | 77 | 78 | } 79 | 80 | 81 | 82 | 83 | } 84 | -------------------------------------------------------------------------------- /url-core/src/test/java/com/hk/surl/ShortUrlTest.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl; 2 | 3 | import com.hk.surl.core.generator.ShortUrlGenerator; 4 | import com.hk.surl.core.generator.builder.ShortUrlGeneratorBuilder; 5 | import com.hk.surl.core.enums.strategy.EncryptStrategy; 6 | import com.hk.surl.core.provider.random.RandomStringProvider; 7 | import com.hk.surl.domain.entity.ShortUrl; 8 | import com.hk.surl.entity.ShortUrlExt; 9 | import org.junit.Test; 10 | 11 | /** 12 | * @author : HK意境 13 | * @ClassName : ShortUrlTest 14 | * @date : 2022/4/18 21:33 15 | * @description : 16 | * @Todo : 17 | * @Bug : 18 | * @Modified : 19 | * @Version : 1.0 20 | */ 21 | public class ShortUrlTest { 22 | 23 | // builder 构造器 V1.0.0 版本 24 | @Test 25 | public void testBuilderApiV1_0_0(){ 26 | 27 | /*// 获取 generator 生成器 28 | ShortUrlGenerator generator = new ShortUrlGeneratorBuilder(new ShortUrlExt("https://www.github.com/hk-hub")) 29 | .length(6) 30 | .generateStrategy(new RandomStringProvider()) 31 | .enableCache(false) 32 | .encryptStrategy(EncryptStrategy.NONE) 33 | .build(); 34 | 35 | // 使用生成器进行链接生成 36 | ShortUrl shortURL = generator.generate(); 37 | 38 | System.out.println(shortURL);*/ 39 | } 40 | 41 | 42 | // builder 构造器 V1.0.1 版本 43 | @Test 44 | public void testBuilderApiV1_0_1(){ 45 | 46 | 47 | } 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /url-core/src/test/java/com/hk/surl/v2/core/generator/AbstractShortUrlGeneratorTest.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.v2.core.generator; 2 | 3 | import cn.hutool.core.date.StopWatch; 4 | import com.hk.surl.v2.core.generator.simple.DefaultShortUrlGenerator; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.junit.jupiter.api.Test; 7 | 8 | 9 | /** 10 | * @author : HK意境 11 | * @ClassName : AbstractShortUrlGeneratorTest 12 | * @date : 2023/6/4 18:08 13 | * @description : 14 | * @Todo : 15 | * @Bug : 16 | * @Modified : 17 | * @Version : 1.0 18 | */ 19 | @Slf4j 20 | class AbstractShortUrlGeneratorTest { 21 | 22 | @Test 23 | void generate() { 24 | DefaultShortUrlGenerator generator = new DefaultShortUrlGenerator(); 25 | StopWatch stopWatch = new StopWatch(); 26 | stopWatch.start(); 27 | for (int i = 0; i < 1000000; i++) { 28 | generator.generate(null); 29 | } 30 | stopWatch.stop(); 31 | 32 | long totalTimeMillis = stopWatch.getTotalTimeMillis(); 33 | log.info("Total time: {}", totalTimeMillis); 34 | } 35 | } -------------------------------------------------------------------------------- /url-core/src/test/java/com/hk/surl/v2/util/HashUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.v2.util; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.junit.jupiter.api.Assertions.*; 6 | 7 | /** 8 | * @author : HK意境 9 | * @ClassName : HashUtilTest 10 | * @date : 2023/6/5 19:27 11 | * @description : 12 | * @Todo : 13 | * @Bug : 14 | * @Modified : 15 | * @Version : 1.0 16 | */ 17 | class HashUtilTest { 18 | 19 | @Test 20 | void md5() { 21 | 22 | System.out.println(HashUtil.md5("个仍俄国i看图软32件后343467果i具体oh给!gerthytrh")); 23 | 24 | } 25 | } -------------------------------------------------------------------------------- /url-domain/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Short-URL 7 | com.hk.surl 8 | 1.0.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | url-domain 13 | 14 | 15 | 16 | cn.hutool 17 | hutool-crypto 18 | 5.7.15 19 | 20 | 21 | com.hk.surl 22 | url-common 23 | 0.1.0 24 | compile 25 | 26 | 27 | 28 | com.github.jeffreyning 29 | mybatisplus-plus 30 | 1.5.1-RELEASE 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /url-domain/src/main/java/com/hk/surl/domain/entity/AnonymousUser.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.domain.entity; 2 | 3 | 4 | import com.baomidou.mybatisplus.annotation.*; 5 | import com.fasterxml.jackson.annotation.JsonFormat; 6 | import io.swagger.annotations.ApiModel; 7 | import io.swagger.annotations.ApiModelProperty; 8 | import lombok.Data; 9 | import lombok.EqualsAndHashCode; 10 | import lombok.NoArgsConstructor; 11 | import org.springframework.format.annotation.DateTimeFormat; 12 | 13 | import java.io.Serializable; 14 | import java.time.LocalDateTime; 15 | 16 | /** 17 | *

18 | * 匿名用户,临时用户 19 | *

20 | * 21 | * @author HK意境 22 | * @since 2022-05-26 23 | */ 24 | @Data 25 | @EqualsAndHashCode(callSuper = false) 26 | @TableName("tb_anonymous_user") 27 | @ApiModel(value="AnonymousUser对象", description="匿名用户,临时用户") 28 | @NoArgsConstructor 29 | public class AnonymousUser implements Serializable { 30 | 31 | private static final long serialVersionUID = 1L; 32 | 33 | @ApiModelProperty(value = "密钥") 34 | private String secretKey; 35 | 36 | @ApiModelProperty(value = "短链接") 37 | private String shortUrl; 38 | 39 | @ApiModelProperty(value = "长链接,可能带有参数") 40 | private String longUrl; 41 | 42 | @ApiModelProperty(value = "匿名用户创建时间") 43 | @TableField(fill = FieldFill.INSERT) 44 | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") 45 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 46 | private LocalDateTime createTime = LocalDateTime.now(); 47 | 48 | @ApiModelProperty(value = "更新时间") 49 | @TableField(fill = FieldFill.INSERT_UPDATE) 50 | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") 51 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 52 | private LocalDateTime updateTime = this.createTime; 53 | 54 | @ApiModelProperty(value = "逻辑删除") 55 | @TableLogic 56 | private Boolean deleted; 57 | 58 | 59 | public AnonymousUser(String shortUrl, String longUrl, String secretKey) { 60 | this.shortUrl = shortUrl ; 61 | this.longUrl = longUrl ; 62 | this.secretKey = secretKey ; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /url-domain/src/main/java/com/hk/surl/domain/entity/AppUser.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.domain.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.*; 4 | import com.fasterxml.jackson.annotation.JsonFormat; 5 | import io.swagger.annotations.ApiModel; 6 | import io.swagger.annotations.ApiModelProperty; 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | import org.springframework.format.annotation.DateTimeFormat; 10 | 11 | import java.io.Serializable; 12 | import java.time.LocalDateTime; 13 | 14 | /** 15 | *

16 | * 用户User,Sass对外提供接口的使用用户 17 | *

18 | * 19 | * @author HK意境 20 | * @since 2022-05-26 21 | */ 22 | @Data 23 | @EqualsAndHashCode(callSuper = false) 24 | @TableName("tb_app_user") 25 | @ApiModel(value="AppUser对象", description="用户User,Sass对外提供接口的使用用户") 26 | public class AppUser implements Serializable { 27 | 28 | private static final long serialVersionUID = 1L; 29 | 30 | @ApiModelProperty(value = "id 号") 31 | @TableId(type = IdType.ASSIGN_ID) 32 | private String id ; 33 | 34 | @ApiModelProperty(value = "用户名") 35 | private String username; 36 | 37 | @ApiModelProperty(value = "密码") 38 | private String password; 39 | 40 | @ApiModelProperty(value = "注册邮箱:日后进行数据报表发送") 41 | private String email; 42 | 43 | @ApiModelProperty(value = "是否为会员") 44 | private Boolean vip; 45 | 46 | @ApiModelProperty(value = "密钥,用户哪来获取报表数据信息的验证") 47 | private String secretKey; 48 | 49 | @ApiModelProperty(value = "可见性") 50 | private Boolean visible; 51 | 52 | @ApiModelProperty(value = "创建时间") 53 | @TableField(fill = FieldFill.INSERT) 54 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 55 | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") 56 | private LocalDateTime createTime; 57 | 58 | @ApiModelProperty(value = "更新时间") 59 | @TableField(fill = FieldFill.INSERT_UPDATE) 60 | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") 61 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 62 | private LocalDateTime updateTime; 63 | 64 | @ApiModelProperty(value = "逻辑删除") 65 | @TableLogic 66 | private Boolean deleted; 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /url-domain/src/main/java/com/hk/surl/domain/entity/LogTrance.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.domain.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.FieldFill; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import java.io.Serializable; 8 | import java.time.LocalDateTime; 9 | 10 | import com.fasterxml.jackson.annotation.JsonFormat; 11 | import com.hk.surl.common.util.TranceIdUtil; 12 | import lombok.Data; 13 | import lombok.ToString; 14 | import org.springframework.format.annotation.DateTimeFormat; 15 | 16 | /** 17 | * 系统调用链路日志 18 | * @TableName tb_log_trance 19 | */ 20 | @ToString 21 | @Data 22 | @TableName(value ="tb_log_trance") 23 | public class LogTrance implements Serializable { 24 | 25 | @TableField(exist = false) 26 | private static final long serialVersionUID = 1L; 27 | 28 | 29 | /** 30 | * 链路追踪id 31 | */ 32 | @TableId(value = "trance_id") 33 | private String tranceId = TranceIdUtil.getTraceId(); 34 | 35 | /** 36 | * 业务类型 37 | */ 38 | @TableField(value = "business_type") 39 | private String businessType; 40 | 41 | @TableField(value = "business_method") 42 | private String businessMethod ; 43 | 44 | /** 45 | * 业务日志级别 46 | * 0 紧急, 1 重要, 2普通 47 | */ 48 | @TableField(value = "level") 49 | private String level; 50 | 51 | /** 52 | * 请求路径 53 | */ 54 | @TableField(value = "path") 55 | private String path; 56 | 57 | /** 58 | * ipv4 的客户端ip 地址 59 | */ 60 | @TableField(value = "ip_address") 61 | private String ipAddress; 62 | 63 | /** 64 | * 客户端浏览器,操作系统 65 | */ 66 | @TableField(value = "user_agent") 67 | private String userAgent; 68 | 69 | /** 70 | * 客户端地址:国家,省份,城市 71 | */ 72 | @TableField(value = "location") 73 | private String location; 74 | 75 | /** 76 | * 请求参数 77 | */ 78 | @TableField(value = "parameters") 79 | private String parameters; 80 | 81 | /** 82 | * 请求方法 83 | */ 84 | @TableField(value = "request_method") 85 | private String requestMethod; 86 | 87 | /** 88 | * 执行的操作 89 | */ 90 | @TableField(value = "operate") 91 | private String operate; 92 | 93 | /** 94 | * 操作用户 95 | */ 96 | @TableField(value = "operator") 97 | private String operator; 98 | 99 | /** 100 | * 响应结果 101 | */ 102 | @TableField(value = "result") 103 | private String result; 104 | 105 | /** 106 | * 响应状态码 107 | */ 108 | @TableField(value = "code") 109 | private Integer code; 110 | 111 | /** 112 | * 请求备注,出错原因 113 | */ 114 | @TableField(value = "msg") 115 | private String msg; 116 | 117 | /** 118 | * 请求创建时间 119 | */ 120 | @TableField(value = "create_time", fill = FieldFill.INSERT) 121 | private LocalDateTime createTime; 122 | 123 | /** 124 | * 请求调用链路执行时间 125 | */ 126 | @TableField(value = "execute_time") 127 | private String executeTime; 128 | 129 | /** 130 | * 结束时间 131 | */ 132 | @TableField(value = "end_time") 133 | private LocalDateTime endTime; 134 | 135 | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") 136 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 137 | public LocalDateTime getCreateTime() { 138 | return createTime; 139 | } 140 | 141 | 142 | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") 143 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 144 | public LocalDateTime getEndTime() { 145 | return endTime; 146 | } 147 | } -------------------------------------------------------------------------------- /url-domain/src/main/java/com/hk/surl/domain/entity/LongUrl.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.domain.entity; 2 | 3 | import cn.hutool.http.Method; 4 | import com.baomidou.mybatisplus.annotation.*; 5 | import com.fasterxml.jackson.annotation.JsonFormat; 6 | import io.swagger.annotations.ApiModel; 7 | import io.swagger.annotations.ApiModelProperty; 8 | import lombok.Data; 9 | import lombok.EqualsAndHashCode; 10 | import lombok.NoArgsConstructor; 11 | import org.springframework.format.annotation.DateTimeFormat; 12 | 13 | import java.io.Serializable; 14 | import java.net.URL; 15 | import java.time.LocalDateTime; 16 | 17 | /** 18 | *

19 | * 长链接 20 | *

21 | * 22 | * @author HK意境 23 | * @since 2022-05-26 24 | */ 25 | @Data 26 | @EqualsAndHashCode(callSuper = false) 27 | @TableName("tb_long_url") 28 | @ApiModel(value="LongUrl对象", description="长链接") 29 | @NoArgsConstructor 30 | public class LongUrl implements Serializable { 31 | 32 | private static final long serialVersionUID = 1L; 33 | 34 | @ApiModelProperty(value = "id 号") 35 | @TableId(type = IdType.ASSIGN_ID) 36 | private String id ; 37 | 38 | @ApiModelProperty(value = "完整的URL链接,可以是普通http 请求url ,qrcode ,base64等") 39 | private String url; 40 | 41 | @ApiModelProperty(value = "URI 统一资源标识符,url 最后一部分") 42 | private String uri; 43 | 44 | @ApiModelProperty(value = "url 资源类型: request 请求类型,二维码类型,base64类型") 45 | private Integer type; 46 | 47 | @ApiModelProperty(value = "调用协议类型:HHTP,RPC") 48 | private String protocol; 49 | 50 | @ApiModelProperty(value = "远端调用,长链接的请求协议版本") 51 | private String callerVersion; 52 | 53 | @ApiModelProperty(value = "长链接主机地址") 54 | private String host; 55 | 56 | @ApiModelProperty(value = "长链接的端口") 57 | private Integer port; 58 | 59 | @ApiModelProperty(value = "长链接的请求端口") 60 | private String method; 61 | 62 | @ApiModelProperty(value = "url 链接地址请求参数,? 分割") 63 | private String params; 64 | 65 | @ApiModelProperty(value = "是否可见1可见,0不可见") 66 | private Boolean visible; 67 | 68 | @ApiModelProperty(value = "乐观锁") 69 | private Integer version; 70 | 71 | @ApiModelProperty(value = "创建时间") 72 | @TableField(fill = FieldFill.INSERT) 73 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 74 | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") 75 | private LocalDateTime createTime; 76 | 77 | @ApiModelProperty(value = "更新时间") 78 | @TableField(fill = FieldFill.INSERT_UPDATE) 79 | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") 80 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 81 | private LocalDateTime updateTime; 82 | 83 | @ApiModelProperty(value = "逻辑删除:0未删除,1已删除 ") 84 | @TableLogic 85 | private Boolean deleted; 86 | 87 | 88 | // 通过 URL 类对象 建立 longUrl 对象 89 | public LongUrl(String content ,URL url){ 90 | // 设置长链接 91 | this.setUrl(content); 92 | this.setHost(url.getHost()); 93 | this.setParams(url.getQuery()); 94 | this.setMethod(Method.GET.name()); 95 | this.setPort(url.getPort()); 96 | this.setProtocol(url.getProtocol()); 97 | this.setUri(url.getPath()); 98 | } 99 | 100 | 101 | } 102 | -------------------------------------------------------------------------------- /url-domain/src/main/java/com/hk/surl/domain/entity/ShortUrl.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.domain.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.*; 4 | import com.fasterxml.jackson.annotation.JsonFormat; 5 | import io.swagger.annotations.ApiModel; 6 | import io.swagger.annotations.ApiModelProperty; 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | import lombok.NoArgsConstructor; 10 | import org.springframework.format.annotation.DateTimeFormat; 11 | 12 | import java.io.Serializable; 13 | import java.time.LocalDateTime; 14 | 15 | /** 16 | *

17 | * 短链接表 18 | *

19 | * 20 | * @author HK意境 21 | * @since 2022-05-26 22 | */ 23 | @Data 24 | @NoArgsConstructor 25 | @EqualsAndHashCode(callSuper = false) 26 | @TableName("tb_short_url") 27 | @ApiModel(value="ShortUrl对象", description="短链接表") 28 | public class ShortUrl implements Serializable { 29 | 30 | private static final long serialVersionUID = 1L; 31 | 32 | @ApiModelProperty(value = "id 号") 33 | @TableId(type = IdType.ASSIGN_ID) 34 | private String id ; 35 | 36 | @ApiModelProperty(value = "生成后的短链接") 37 | private String shortUrl; 38 | 39 | @ApiModelProperty(value = "短链接类型: http 请求链接,二维码,base64") 40 | private Integer type; 41 | 42 | @ApiModelProperty(value = "编号:目前作用未知") 43 | private String no; 44 | 45 | @ApiModelProperty(value = "可见性:1可见,0不可见") 46 | private Boolean visible = true; 47 | 48 | @ApiModelProperty(value = "创建时间") 49 | private LocalDateTime createTime; 50 | 51 | @ApiModelProperty(value = "过期时间:表示短链接从创建经过到使用到消亡的时间,是指失效的时间: expiration_time=create_time+有效时间") 52 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 53 | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") 54 | private LocalDateTime expirationTime; 55 | 56 | @ApiModelProperty(value = "乐观锁") 57 | private Integer version; 58 | 59 | @ApiModelProperty(value = "跟新时间 ") 60 | @TableField(fill = FieldFill.INSERT_UPDATE) 61 | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") 62 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 63 | private LocalDateTime updateTime; 64 | 65 | @ApiModelProperty(value = "是否删除:1可见,0不可见") 66 | @TableLogic 67 | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") 68 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 69 | private Boolean deleted = false; 70 | 71 | 72 | public ShortUrl(String shortUrl) { 73 | this.shortUrl = shortUrl; 74 | } 75 | 76 | 77 | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8") 78 | public LocalDateTime getCreateTime() { 79 | return createTime; 80 | } 81 | 82 | 83 | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8") 84 | public LocalDateTime getExpirationTime() { 85 | return expirationTime; 86 | } 87 | 88 | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8") 89 | public LocalDateTime getUpdateTime() { 90 | return updateTime; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /url-domain/src/main/java/com/hk/surl/domain/entity/VisitLog.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.domain.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.*; 4 | import com.fasterxml.jackson.annotation.JsonFormat; 5 | import io.swagger.annotations.ApiModel; 6 | import io.swagger.annotations.ApiModelProperty; 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | 10 | import java.io.Serializable; 11 | import java.time.LocalDateTime; 12 | 13 | /** 14 | *

15 | * 短链接访问日志 16 | *

17 | * 18 | * @author HK意境 19 | * @since 2022-05-26 20 | */ 21 | @Data 22 | @EqualsAndHashCode(callSuper = false) 23 | @TableName("tb_visit_log") 24 | @ApiModel(value="VisitLog对象", description="短链接访问日志") 25 | public class VisitLog implements Serializable { 26 | 27 | private static final long serialVersionUID = 1L; 28 | 29 | @ApiModelProperty(value = "id 号") 30 | @TableId(type = IdType.ASSIGN_ID) 31 | private String id ; 32 | 33 | @ApiModelProperty(value = "短链接") 34 | private String shortUrl; 35 | 36 | @ApiModelProperty(value = "长链接") 37 | private String longUrl; 38 | 39 | @ApiModelProperty(value = "访问者的IP地址") 40 | private String visitorIp; 41 | 42 | @ApiModelProperty(value = "请求者refer") 43 | private String referrer = "direct to access"; 44 | 45 | @ApiModelProperty(value = "请求的域名") 46 | private String requestHost; 47 | 48 | @ApiModelProperty(value = "长链接(目标链接)域名") 49 | private String targetHost; 50 | 51 | @ApiModelProperty(value = "访问设备:操作系统类型,浏览器类型") 52 | private String equipment; 53 | 54 | @ApiModelProperty(value = "访问地区:国家,省份,市级,区级") 55 | private String visitorArea; 56 | 57 | @ApiModelProperty(value = "访问方法") 58 | private String method; 59 | 60 | @ApiModelProperty(value = "访问时间") 61 | @TableField(fill = FieldFill.INSERT) 62 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 63 | private LocalDateTime createTime; 64 | 65 | @ApiModelProperty(value = "跟新时间") 66 | @TableField(fill = FieldFill.INSERT_UPDATE) 67 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 68 | private LocalDateTime updateTime; 69 | 70 | @ApiModelProperty(value = "逻辑删除:1删除,0未删除") 71 | @TableLogic 72 | private Boolean deleted; 73 | 74 | 75 | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8") 76 | public LocalDateTime getCreateTime() { 77 | return createTime; 78 | } 79 | 80 | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8") 81 | public LocalDateTime getUpdateTime() { 82 | return updateTime; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /url-domain/src/main/java/com/hk/surl/domain/mapper/AnonymousUserMapper.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.domain.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.hk.surl.domain.entity.AnonymousUser; 5 | import com.hk.surl.domain.entity.ShortUrl; 6 | import org.apache.ibatis.annotations.Mapper; 7 | import org.apache.ibatis.annotations.Param; 8 | import org.apache.ibatis.annotations.Select; 9 | 10 | import java.time.LocalDateTime; 11 | import java.util.List; 12 | 13 | 14 | /** 15 | *

16 | * 匿名用户,临时用户 Mapper 接口 17 | *

18 | * 19 | * @author HK意境 20 | * @since 2022-05-26 21 | */ 22 | @Mapper 23 | public interface AnonymousUserMapper extends BaseMapper { 24 | 25 | 26 | // 匿名用户登录 27 | AnonymousUser anonymousUserLogin(@Param("shortUrl") String shortUrl, @Param("secretKey") String secretKey); 28 | 29 | 30 | // 根据短链接查询 31 | @Select("select * from tb_anonymous_user where short_url = #{shortUrl} and deleted = false") 32 | AnonymousUser selectOneByShortUrl(ShortUrl shortUrl); 33 | } 34 | -------------------------------------------------------------------------------- /url-domain/src/main/java/com/hk/surl/domain/mapper/AppUserMapper.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.domain.mapper; 2 | 3 | 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import com.hk.surl.domain.entity.AppUser; 6 | import org.apache.ibatis.annotations.Mapper; 7 | 8 | /** 9 | *

10 | * 用户User,Sass对外提供接口的使用用户 Mapper 接口 11 | *

12 | * 13 | * @author HK意境 14 | * @since 2022-05-26 15 | */ 16 | @Mapper 17 | public interface AppUserMapper extends BaseMapper { 18 | 19 | } 20 | -------------------------------------------------------------------------------- /url-domain/src/main/java/com/hk/surl/domain/mapper/LogTranceMapper.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.domain.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.hk.surl.domain.entity.LogTrance; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | 8 | /** 9 | * @Entity com.hk.surl.common.log.LogTrance 10 | */ 11 | @Mapper 12 | public interface LogTranceMapper extends BaseMapper { 13 | 14 | } 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /url-domain/src/main/java/com/hk/surl/domain/mapper/LongUrlMapper.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.domain.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.hk.surl.domain.entity.LongUrl; 5 | import org.apache.ibatis.annotations.Mapper; 6 | import org.apache.ibatis.annotations.Select; 7 | 8 | import java.util.List; 9 | 10 | 11 | /** 12 | *

13 | * 长链接 Mapper 接口 14 | *

15 | * 16 | * @author HK意境 17 | * @since 2022-05-26 18 | */ 19 | @Mapper 20 | public interface LongUrlMapper extends BaseMapper { 21 | 22 | /** 23 | * @methodName : selectListByIds 24 | * @author : HK意境 25 | * @date : 2022/6/10 21:26 26 | * @description : 使用 xml 方式 in 集合查询,速度由于 mybatis plus 的批量查询 27 | * @Todo : 28 | * @apiNote : 根据 id 集合查询长链接对象集合 29 | * @params : 30 | * @param ids 长链接对象 id 集合列表 31 | * @return List 32 | * @throws: 33 | * @Bug : 34 | * @Modified : 35 | * @Version : 1.0.0 36 | */ 37 | List selectLongUrlListByIds(List ids); 38 | 39 | // 通过 长链接字符串获取长链接字符串 40 | @Select("select * from tb_long_url where url = #{longUrl} limit 1") 41 | LongUrl selectOneByLongUrl(String longUrl); 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /url-domain/src/main/java/com/hk/surl/domain/mapper/ShortUrlMapper.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.domain.mapper; 2 | 3 | 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import com.hk.surl.domain.entity.LongUrl; 6 | import com.hk.surl.domain.entity.ShortUrl; 7 | import org.apache.ibatis.annotations.Mapper; 8 | import org.apache.ibatis.annotations.Param; 9 | import org.apache.ibatis.annotations.Select; 10 | 11 | import java.time.LocalDateTime; 12 | import java.util.List; 13 | 14 | /** 15 | *

16 | * 短链接表 Mapper 接口 17 | *

18 | * 19 | * @author HK意境 20 | * @since 2022-05-26 21 | */ 22 | @Mapper 23 | public interface ShortUrlMapper extends BaseMapper { 24 | 25 | // 因为这里,一个长链接只能生成对应的唯一的一个短链接,但是我们的一个短链接对象是可以对应多个长链接对象的 26 | // 根据 根据长链接对象 id 关联查询 url_map 表,获取对应 短链接 id 27 | // select su.* from tb_short_url as su where su.id in ( 28 | // select m.short_url_id from tn_url_map as m where m.long_url = #{longUrl} and m.visible = true ; 29 | // ) 30 | ShortUrl selectShortUrlByLongUrl(@Param(value = "longUrl") LongUrl longUrl); 31 | 32 | 33 | // 查询指定短链接字符串的链接对象 34 | @Select("select * from tb_short_url where short_url = #{shortUrlStr} " + "and visible = true") 35 | ShortUrl selectByShortUrl(String shortUrlStr); 36 | 37 | // 查询指定时间内新增的短链接 38 | /** 39 | * @methodName : selectAllByCreateTime 40 | * @author : HK意境 41 | * @date : 2022/6/11 21:35 42 | * @description : 43 | * @Todo : 44 | * @apiNote : 45 | * @params : 46 | * @param startTime 开始时间 47 | * @param endTime 结束时间 48 | * @return List 49 | * @throws: 50 | * @Bug : 51 | * @Modified : 52 | * @Version : 1.0.0 53 | */ 54 | @Select("select * from tb_short_url where create_time between(#{startTime},#{endTime})") 55 | List selectAllByCreateTime(LocalDateTime startTime , LocalDateTime endTime); 56 | 57 | // 查询指定有效期内的短链接对象 58 | /** 59 | * @methodName : selectAllByExpirationTime 60 | * @author : HK意境 61 | * @date : 2022/6/11 21:37 62 | * @description : 查询过期时间在指定范围的短链接数量 63 | * @Todo : 64 | * @apiNote : 65 | * @params : 66 | * @param startTime 开始时间 67 | * @param expirationTime 结束时间,过期时间 68 | * @return List 69 | * @throws: 70 | * @Bug : 71 | * @Modified : 72 | * @Version : 1.0.0 73 | */ 74 | @Select("select * from tb_short_url where expiration_time between(#{startTime}, #{expirationTime}") 75 | List selectAllByExpirationTime(LocalDateTime startTime ,LocalDateTime expirationTime); 76 | 77 | 78 | 79 | 80 | 81 | 82 | } 83 | -------------------------------------------------------------------------------- /url-domain/src/main/java/com/hk/surl/domain/mapper/UrlMapMapper.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.domain.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.hk.surl.domain.entity.UrlMap; 5 | import org.apache.ibatis.annotations.Mapper; 6 | import org.apache.ibatis.annotations.Select; 7 | 8 | import java.time.LocalDateTime; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | /** 13 | *

14 | * 长链接和短链接的映射关系 Mapper 接口 15 | *

16 | * 17 | * @author HK意境 18 | * @since 2022-05-26 19 | */ 20 | @Mapper 21 | public interface UrlMapMapper extends BaseMapper { 22 | 23 | // 查询短链接id 对应的长链接 id 集合 24 | // select m.long_id from tb_url_map as m where m.short_id = #{shortId} 25 | List selectLongIdByShortId(String shortId); 26 | 27 | 28 | // 通过 短链接id 值查询所有 29 | @Select("select * from tb_url_map where short_id = #{shortId} "+ "and visible = true") 30 | List selectAllByShortId(String shortId); 31 | 32 | // 通过短链接字符串查询所有 33 | @Select("select * from tb_url_map where short_url = #{shortUrl} "+ "and visible = true") 34 | List selectByShortUrl(String shortUrl); 35 | 36 | // 通过长链接id 查询所有,但是其实是长链接对短链接是多对一的,所有查询出来也只有一个 37 | @Select("select * from tb_url_map where long_id = #{longId} "+ "and visible = true") 38 | List selectAllByLongId(String longId); 39 | 40 | // 通过长链接字符串查询所有, 但是这里其实一个长链接只对应一个短链接,所有也就相当于查询一个 41 | @Select("select * from tb_url_map where long_url = #{longUrl} "+ "and visible = true") 42 | List selectAllByLongUrl(String longUrl); 43 | 44 | // 通过长链接id, 短链接id 唯一查询一个映射对象 45 | @Select("select * from tb_url_map where short_id = #{shortId} and long_id = #{longId} " + "and visible = true") 46 | UrlMap selectByShortIdAndLongId(String shortId, String longId); 47 | 48 | // 通过长链接字符串,短链接字符串查询唯一一个映射对象 49 | @Select("select * from tb_url_mao where short_url = #{shortUrl} and long_url = #{longUrl} "+ "and visible = true" ) 50 | UrlMap selectByShortUrlAndShortUrl(String shortUrl, String longUrl); 51 | 52 | // 通过长链接MD5值 查询映射对象 53 | @Select("select * from tb_url_map where long_md = #{md} " + "and visible = true") 54 | UrlMap selectByLongMd(String md); 55 | 56 | // 查询所有,包括不可见和逻辑删除的 57 | @Select("select * from tb_url_map where deleted=true or deleted = false") 58 | List selectAllIgnoreVisibleAndDeleted(); 59 | 60 | // 查询所有,包括不可见的 61 | @Select("select * from tb_url_map where deleted = false") 62 | List selectAllIgnoreVisible(); 63 | 64 | 65 | /** 66 | * @methodName : 67 | * @author : HK意境 68 | * @date : 2022/6/11 21:17 69 | * @description : 查询指定时间内的新增映射对象,也就为指定时间内新增的短链接 70 | * @Todo : 71 | * @apiNote : 72 | * @params : 73 | * @param startTime 开始时间点 74 | * @param endTime 结束时间点 75 | * @return List 76 | * @throws: 77 | * @Bug : 78 | * @Modified : 79 | * @Version : 1.0.0 80 | */ 81 | @Select("select * from tb_url_map where create_time between(#{startTime}, #{endTime}) " + "and visible = true") 82 | List selectAllByCreateTime(LocalDateTime startTime, LocalDateTime endTime); 83 | 84 | 85 | 86 | } 87 | -------------------------------------------------------------------------------- /url-domain/src/main/java/com/hk/surl/domain/mapper/VisitLogMapper.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.domain.mapper; 2 | 3 | 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import com.hk.surl.domain.entity.VisitLog; 6 | import org.apache.ibatis.annotations.Mapper; 7 | import org.apache.ibatis.annotations.Param; 8 | import org.apache.ibatis.annotations.Select; 9 | 10 | import java.time.LocalDateTime; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | /** 15 | *

16 | * 短链接访问日志 Mapper 接口 17 | *

18 | * 19 | * @author HK意境 20 | * @since 2022-05-26 21 | */ 22 | @Mapper 23 | public interface VisitLogMapper extends BaseMapper { 24 | 25 | 26 | // 查询指定短链接的访问数据 27 | @Select("select * from tb_visit_log where short_url = #{shortUrl} order by create_time desc") 28 | List selectListByShortUrl(@Param(value = "shortUrl") String shortUrl); 29 | 30 | // 查询指定日期内的访问数据 31 | List selectListByAccessTimeOrShortUrl(@Param(value = "shortUrl")String shortUrl , 32 | @Param(value = "startTime")LocalDateTime startTime , 33 | @Param(value = "endTime")LocalDateTime endTime); 34 | 35 | // 指定时间内新增的访问用户 36 | // 查询指定时间内的访问记录,投影取出ip 地址, 判断不属于指定时间以前的ip 地址 37 | List getNewVisitDataByDatetime(@Param("shortUrl") String shortUrl, 38 | @Param("startTime")LocalDateTime startTime, @Param("endTime")LocalDateTime endTime); 39 | 40 | // 查询指定短链接创建以来的每天的访问数据 41 | List> selectListEveryDayAccessData(String shortUrl); 42 | } 43 | -------------------------------------------------------------------------------- /url-domain/src/main/java/com/hk/surl/domain/vo/ShortUrlVo.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.domain.vo; 2 | 3 | import com.baomidou.mybatisplus.annotation.*; 4 | import com.fasterxml.jackson.annotation.JsonFormat; 5 | import com.hk.surl.domain.entity.AnonymousUser; 6 | import com.hk.surl.domain.entity.ShortUrl; 7 | import io.swagger.annotations.ApiModelProperty; 8 | import lombok.Data; 9 | import org.springframework.format.annotation.DateTimeFormat; 10 | 11 | import java.io.Serializable; 12 | import java.time.LocalDateTime; 13 | 14 | /** 15 | * @author : HK意境 16 | * @ClassName : ShortUrlVo 17 | * @date : 2022/6/11 21:43 18 | * @description : 19 | * @Todo : 20 | * @Bug : 21 | * @Modified : 22 | * @Version : 1.0 23 | */ 24 | @Data 25 | public class ShortUrlVo implements Serializable { 26 | 27 | private static final long serialVersionUID = 1L; 28 | 29 | 30 | @ApiModelProperty(value = "生成后的短链接") 31 | private String shortUrl; 32 | 33 | @ApiModelProperty(value = "原始长链接") 34 | private String longUrl ; 35 | 36 | @ApiModelProperty(value = "secretKey 安全key") 37 | private String secretKey ; 38 | 39 | @ApiModelProperty(value = "短链接类型: http 请求链接,二维码,base64") 40 | private Integer type ; 41 | 42 | @ApiModelProperty(value = "创建时间") 43 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 44 | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") 45 | private LocalDateTime createTime = LocalDateTime.now(); 46 | 47 | @ApiModelProperty(value = "过期时间:表示短链接从创建经过到使用到消亡的时间,是指失效的时间: expiration_time=create_time+有效时间") 48 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 49 | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") 50 | private LocalDateTime expirationTime; 51 | 52 | @ApiModelProperty(value = "跟新时间 ") 53 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") 54 | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") 55 | private LocalDateTime updateTime = this.createTime; 56 | 57 | public ShortUrlVo() { 58 | } 59 | 60 | public ShortUrlVo(String shortUrl, String longUrl) { 61 | this.shortUrl = shortUrl; 62 | this.longUrl = longUrl; 63 | } 64 | 65 | public ShortUrlVo(String shortUrl, String longUrl, String secretKey) { 66 | this.shortUrl = shortUrl; 67 | this.longUrl = longUrl; 68 | this.secretKey = secretKey ; 69 | } 70 | 71 | 72 | public ShortUrlVo(ShortUrl shortUrl , AnonymousUser anonymousUser){ 73 | this(shortUrl.getShortUrl(), anonymousUser.getLongUrl(),anonymousUser.getSecretKey()); 74 | this.expirationTime = shortUrl.getExpirationTime(); 75 | this.type = shortUrl.getType(); 76 | } 77 | 78 | 79 | 80 | 81 | 82 | } 83 | -------------------------------------------------------------------------------- /url-domain/src/main/resources/mapper/AnonymousUserMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /url-domain/src/main/resources/mapper/AppUserMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /url-domain/src/main/resources/mapper/LogTranceMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | trance_id,business_type,level, 29 | path,ip_address,user_agent, 30 | location,parameters,request_method, 31 | operate,operator,result, 32 | code,msg,create_time, 33 | execute_time,end_time 34 | 35 | 36 | -------------------------------------------------------------------------------- /url-domain/src/main/resources/mapper/LongUrlMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 16 | 17 | -------------------------------------------------------------------------------- /url-domain/src/main/resources/mapper/ShortUrlMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 13 | -------------------------------------------------------------------------------- /url-domain/src/main/resources/mapper/UrlMapMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | -------------------------------------------------------------------------------- /url-domain/src/main/resources/mapper/VisitLogMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | 21 | 22 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /url-interface/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Short-URL 7 | com.hk.surl 8 | 1.0.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | url-interface 13 | 14 | 15 | 16 | com.hk.surl 17 | url-common 18 | 0.1.0 19 | 20 | 21 | 22 | javax.servlet 23 | javax.servlet-api 24 | 4.0.1 25 | 26 | 27 | 28 | com.hk.surl 29 | url-domain 30 | 1.0.0-SNAPSHOT 31 | compile 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /url-interface/src/main/java/com/hk/surl/api/common/FileService.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.api.common; 2 | 3 | import cn.hutool.core.io.resource.ClassPathResource; 4 | import com.hk.surl.common.exception.BaseException; 5 | import com.hk.surl.common.response.ResultCode; 6 | import io.swagger.models.auth.In; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.stereotype.Service; 9 | 10 | import javax.servlet.ServletOutputStream; 11 | import javax.servlet.http.HttpServletResponse; 12 | import java.io.FileInputStream; 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | import java.io.UnsupportedEncodingException; 16 | 17 | /** 18 | * @author : HK意境 19 | * @ClassName : FileService 20 | * @date : 2022/6/13 20:58 21 | * @description : 22 | * @Todo : 23 | * @Bug : 24 | * @Modified : 25 | * @Version : 1.0 26 | */ 27 | @Service 28 | public class FileService { 29 | 30 | /** 31 | * @methodName : downloadFile 32 | * @author : HK意境 33 | * @date : 2022/6/13 20:58 34 | * @description : 文件下载 35 | * @Todo : 36 | * @apiNote : 37 | * @params : 38 | * @param fileName 下载文件名称 39 | * @return Boolean 40 | * @throws: 41 | * @Bug : 42 | * @Modified : 43 | * @Version : 1.0.0 44 | */ 45 | public Boolean downloadFile(String fileName, HttpServletResponse response) throws IOException { 46 | 47 | // 文件输入流 48 | InputStream inputStream = null ; 49 | 50 | // 输出到响应中 51 | ServletOutputStream outputStream = null; 52 | 53 | try{ 54 | // 获取资源中的模板文件 55 | inputStream = new ClassPathResource(fileName).getStream(); 56 | 57 | response.reset(); 58 | response.setContentType("multipart/form-data"); 59 | 60 | // 设置响应头 61 | response.setHeader("Content-Disposition", 62 | "attachment; filename=" + new String(fileName.getBytes("UTF-8"), "iso8859-1") + ".xlsx"); 63 | 64 | outputStream = response.getOutputStream(); 65 | 66 | int b = 0; 67 | byte[] buffer = new byte[1024]; 68 | while ((b = inputStream.read(buffer)) != -1) { 69 | //写到输出流(out)中 70 | outputStream.write(buffer, 0, b); 71 | } 72 | 73 | return true ; 74 | }catch(Exception e){ 75 | throw new BaseException(ResultCode.RESOURCE_DOWNLOAD_OR_UPLOAD_ERROR); 76 | 77 | }finally { 78 | if (outputStream != null) { 79 | outputStream.flush(); 80 | outputStream.close(); 81 | } 82 | if (inputStream != null){ 83 | inputStream.close(); 84 | } 85 | } 86 | } 87 | 88 | 89 | } 90 | -------------------------------------------------------------------------------- /url-interface/src/main/java/com/hk/surl/api/core/IAnonymousUserService.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.api.core; 2 | 3 | 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | import com.hk.surl.domain.entity.AnonymousUser; 6 | import com.hk.surl.domain.entity.VisitLog; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * @ClassName : IAnonymousUserService 12 | * @author : HK意境 13 | * @date : 2022/5/26 14 | * @description : 匿名用户,临时用户 服务类 15 | * @Todo : 16 | * @Bug : 17 | * @Modified : 18 | * @Version : 1.0 19 | */ 20 | public interface IAnonymousUserService extends IService { 21 | 22 | // 匿名用户登录 23 | AnonymousUser anonymousLogin(String shortUrl, String secretKey); 24 | 25 | // 今日新增访问数据 26 | List getTodayNewAccessVisitData(String shortUrl); 27 | 28 | // 今日新增访问IP 29 | List getTodayNewAccessVisitor(String shortUrl); 30 | 31 | } 32 | -------------------------------------------------------------------------------- /url-interface/src/main/java/com/hk/surl/api/core/IAppUserService.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.api.core; 2 | 3 | 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | import com.hk.surl.domain.entity.AppUser; 6 | 7 | /** 8 | * @ClassName : IAppUserService 9 | * @author : HK意境 10 | * @date : 2022/5/26 11 | * @description : 用户User,Sass对外提供接口的使用用户 服务类 12 | * @Todo : 13 | * @Bug : 14 | * @Modified : 15 | * @Version : 1.0 16 | */ 17 | public interface IAppUserService extends IService { 18 | 19 | } 20 | -------------------------------------------------------------------------------- /url-interface/src/main/java/com/hk/surl/api/core/ILogTranceService.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.api.core; 2 | 3 | import com.baomidou.mybatisplus.extension.service.IService; 4 | import com.hk.surl.domain.entity.LogTrance; 5 | 6 | /** 7 | * 8 | */ 9 | public interface ILogTranceService extends IService { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /url-interface/src/main/java/com/hk/surl/api/core/ILongUrlService.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.api.core; 2 | 3 | 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | import com.hk.surl.domain.entity.LongUrl; 6 | 7 | /** 8 | * @ClassName : ILongUrlService 9 | * @author : HK意境 10 | * @date : 2022/5/26 11 | * @description : 长链接 服务类 12 | * @Todo : 13 | * @Bug : 14 | * @Modified : 15 | * @Version : 1.0 16 | */ 17 | public interface ILongUrlService extends IService { 18 | 19 | } 20 | -------------------------------------------------------------------------------- /url-interface/src/main/java/com/hk/surl/api/core/IShortUrlService.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.api.core; 2 | 3 | import com.baomidou.mybatisplus.extension.service.IService; 4 | import com.hk.surl.domain.entity.AnonymousUser; 5 | import com.hk.surl.domain.entity.ShortUrl; 6 | 7 | import java.time.LocalDateTime; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.concurrent.ExecutionException; 11 | 12 | /** 13 | * @ClassName : IShortUrlService 14 | * @author : HK意境 15 | * @date : 2022/5/26 16 | * @description :短链接表 服务类 17 | * @Todo : 18 | * @Bug : 19 | * @Modified : 20 | * @Version : 1.0 21 | */ 22 | public interface IShortUrlService extends IService { 23 | 24 | 25 | // 根据 长链接字符串 新增 短链接对象,长链接对象,映射对象 26 | Map.Entry newShortUrl(String longUrl, LocalDateTime expirationTime) throws ExecutionException, InterruptedException; 27 | 28 | // 批量创建短链接对象 29 | List batchNewShortUrl(List longUrls, LocalDateTime expirationTime) throws ExecutionException, InterruptedException; 30 | 31 | // 根据 id 删除 短链接对象,映射对象,长链接对象 32 | boolean removeShortUrlById(String id); 33 | 34 | // 删除指定短链接对象,连带删除长链接对象,映射对象 35 | Boolean removeShortUrl(ShortUrl shortUrl); 36 | 37 | // 删除指定短链接字符串的短链接对象 38 | Boolean removeShortUrlByUrl(String shortUrlStr); 39 | 40 | 41 | } 42 | -------------------------------------------------------------------------------- /url-interface/src/main/java/com/hk/surl/api/core/IUrlMapService.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.api.core; 2 | 3 | import com.baomidou.mybatisplus.extension.service.IService; 4 | import com.hk.surl.domain.entity.LongUrl; 5 | import com.hk.surl.domain.entity.UrlMap; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * @ClassName : IUrlMapService 11 | * @author : HK意境 12 | * @date : 2022/5/26 13 | * @description :长链接和短链接的映射关系 服务类 14 | * @Todo : 15 | * @Bug : 16 | * @Modified : 17 | * @Version : 1.0 18 | */ 19 | public interface IUrlMapService extends IService { 20 | 21 | // 根据 短链接字符串 查询对应的长链接对象集合 22 | List getLongUrlListByShortUrl(String shortUrl); 23 | 24 | // 根据 短链接id值 查询对应的长链接对象集合 25 | List getLongUrlListBySId(String sid); 26 | 27 | // 根据长连接 id 值查询对应的urlmap 映射对象 28 | List getUrlMapByLongId(String lid); 29 | 30 | // 根据 长链接字符串查询映射对象 31 | UrlMap getUrlMapByLongUrl(String longUrl); 32 | 33 | } 34 | -------------------------------------------------------------------------------- /url-interface/src/main/java/com/hk/surl/api/core/IVisitLogService.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.api.core; 2 | 3 | import cn.hutool.poi.excel.ExcelWriter; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | import com.hk.surl.domain.entity.VisitLog; 6 | 7 | import java.time.LocalDateTime; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | /** 12 | * @ClassName : IVisitLogService 13 | * @author : HK意境 14 | * @date : 2022/5/26 15 | * @description :短链接访问日志 服务类 16 | * @Todo : 17 | * @Bug : 18 | * @Modified : 19 | * @Version : 1.0 20 | */ 21 | public interface IVisitLogService extends IService { 22 | 23 | // 获取指定短链接的访问日志 24 | List getListByShortUrl(String shortUrl); 25 | 26 | // 获取指定时间内的访问数据 27 | List getListByDateTime(String shortUrl , LocalDateTime startTime, LocalDateTime endTime); 28 | 29 | 30 | // 获取指定短链接的访问数据:excel 文件数据 31 | // 导出指点短链接访问数据到 excel 文件种 32 | ExcelWriter exportAccessDataToExcel(String shortUrl); 33 | 34 | // 获取短链接所有的独立访问IP 35 | Map getAloneTotalVisitorIp(String shortUrl); 36 | 37 | // 获取每天的访问数据 38 | List> getEveryDayAccessData(String shortUrl); 39 | } 40 | -------------------------------------------------------------------------------- /url-interface/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=short-url 2 | 3 | server.address=localhost 4 | server.port=8888 5 | 6 | spring.mvc.servlet.path=/ 7 | 8 | # mysql 的配置 9 | spring.datasource.url=jdbc:mysql://localhost:3306/short_url?useUnicode=true&characterEncoding=utf8&useSSL=false 10 | spring.datasource.username=root 11 | spring.datasource.password=root 12 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 13 | 14 | 15 | # redis 的配置 16 | spring.redis.port=6379 17 | spring.redis.host=localhost 18 | 19 | 20 | # short-url 字符串池配置: 一共10+26+26+2=64个字符 21 | # 26个小写字母+26个大写字母+10个数字+ '-' + '_' 22 | short.url.string.pool=0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_ 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /url-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Short-URL 7 | com.hk.surl 8 | 1.0.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | url-service 13 | 0.1.0 14 | 15 | 16 | com.hk.surl 17 | url-interface 18 | 1.0.0-SNAPSHOT 19 | compile 20 | 21 | 22 | com.hk.surl 23 | url-core 24 | 1.0.0-SNAPSHOT 25 | compile 26 | 27 | 28 | 29 | org.apache.poi 30 | poi-ooxml 31 | 5.0.0 32 | 33 | 34 | org.apache.poi 35 | poi 36 | 5.0.0 37 | 38 | 39 | org.apache.poi 40 | poi-ooxml-full 41 | 5.0.0 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /url-service/src/main/java/com/hk/surl/service/core/AccessService.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.service.core; 2 | 3 | import com.hk.surl.api.core.IUrlMapService; 4 | import com.hk.surl.domain.entity.LongUrl; 5 | import org.springframework.stereotype.Service; 6 | 7 | import javax.annotation.Resource; 8 | import java.util.List; 9 | import java.util.Random; 10 | 11 | /** 12 | * @author : HK意境 13 | * @ClassName : AccessService 14 | * @date : 2022/6/12 15:46 15 | * @description : 短链接访问服务 16 | * @Todo : 17 | * @Bug : 18 | * @Modified : 19 | * @Version : 1.0 20 | */ 21 | @Service 22 | public class AccessService { 23 | 24 | // random 是线程安全的对象 : 底层内部使用了乐观锁的机制,原子操作,cas 方式 25 | private static Random random = new Random(); 26 | 27 | @Resource 28 | private IUrlMapService urlMapService ; 29 | 30 | 31 | /** 32 | * @methodName : 33 | * @author : HK意境 34 | * @date : 2022/6/12 15:48 35 | * @description : 根据短链接字符串,跳转到对应的长链接地址,如果有多个,按照权重进行跳转 36 | * @Todo : 37 | * @apiNote 根据短链接字符串,跳转到对应的长链接地址,如果有多个,按照权重进行跳转 38 | * @params 39 | * @param shortUrl 完整的短链接字符串 40 | * @return LongUrl 41 | * @throws: 42 | * @Bug : 后期有待完善跳转策略:轮询,随机,权重 43 | * @Modified : 44 | * @Version : 1.0.0 45 | */ 46 | public LongUrl getRedirectLongUrl(String shortUrl) { 47 | 48 | // 获取没有过期,可见的,未删除的短链接对应的长链接对象集合 49 | List longUrlList = urlMapService.getLongUrlListByShortUrl(shortUrl); 50 | 51 | // 根据权重进行跳转 52 | return longUrlList.get(random.nextInt(longUrlList.size())); 53 | } 54 | 55 | 56 | 57 | // 解析外部 58 | 59 | 60 | } 61 | -------------------------------------------------------------------------------- /url-service/src/main/java/com/hk/surl/service/core/AppUserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.service.core; 2 | 3 | 4 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 5 | import com.hk.surl.api.core.IAppUserService; 6 | import com.hk.surl.domain.entity.AppUser; 7 | import com.hk.surl.domain.mapper.AppUserMapper; 8 | import org.springframework.stereotype.Service; 9 | 10 | /** 11 | * @ClassName : AppUserServiceImpl 12 | * @author : HK意境 13 | * @date : 2022/5/26 15:17 14 | * @description :用户User,Sass对外提供接口的使用用户 服务实现类 15 | * @Todo : 16 | * @Bug : 17 | * @Modified : 18 | * @Version : 1.0 19 | */ 20 | @Service 21 | public class AppUserServiceImpl extends ServiceImpl implements IAppUserService { 22 | 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /url-service/src/main/java/com/hk/surl/service/core/LogTranceServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.service.core; 2 | 3 | 4 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 5 | import com.hk.surl.api.core.ILogTranceService; 6 | import com.hk.surl.domain.entity.LogTrance; 7 | import com.hk.surl.domain.mapper.LogTranceMapper; 8 | import org.springframework.stereotype.Service; 9 | 10 | /** 11 | * @ClassName : LogTranceServiceImpl 12 | * @author : HK意境 13 | * @date : 2022/6/14 18:53 14 | * @description : 15 | * @Todo : 16 | * @Bug : 17 | * @Modified : 18 | * @Version : 1.0 19 | */ 20 | @Service 21 | public class LogTranceServiceImpl extends ServiceImpl implements ILogTranceService { 22 | 23 | } 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /url-service/src/main/java/com/hk/surl/service/core/LongUrlServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.service.core; 2 | 3 | 4 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 5 | import com.hk.surl.api.core.ILongUrlService; 6 | import com.hk.surl.domain.entity.LongUrl; 7 | import com.hk.surl.domain.mapper.LongUrlMapper; 8 | import org.springframework.stereotype.Service; 9 | 10 | /** 11 | * @ClassName : LongUrlServiceImpl 12 | * @author : HK意境 13 | * @date : 2022/5/26 15:25 14 | * @description : 长链接 服务实现类 15 | * @Todo : 16 | * @Bug : 17 | * @Modified : 18 | * @Version : 1.0 19 | */ 20 | @Service 21 | public class LongUrlServiceImpl extends ServiceImpl implements ILongUrlService { 22 | 23 | } 24 | -------------------------------------------------------------------------------- /url-service/src/main/java/com/hk/surl/service/util/ExcelResolveService.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.service.util; 2 | 3 | import cn.hutool.poi.excel.ExcelReader; 4 | import cn.hutool.poi.excel.ExcelUtil; 5 | import com.hk.surl.common.entity.LongUrlDTO; 6 | import org.springframework.stereotype.Service; 7 | 8 | import java.io.*; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | /** 13 | * @author : HK意境 14 | * @ClassName : ExcelResolveService 15 | * @date : 2022/6/15 21:35 16 | * @description : excel 文件解析生成处理服务类 17 | * @Todo : 18 | * @Bug : 19 | * @Modified : 20 | * @Version : 1.0 21 | */ 22 | @Service 23 | public class ExcelResolveService { 24 | 25 | /** 26 | * @methodName : resolveImportExcel 27 | * @author : HK意境 28 | * @date : 2022/6/15 21:45 29 | * @description : 30 | * @Todo : 31 | * @apiNote 解析导入的excel 文件 32 | * @params : 33 | * @param excelStream 文件输入流 34 | * @return null 35 | * @throws: 36 | * @Bug : 37 | * @Modified : 38 | * @Version : 1.0.0 39 | */ 40 | public List resolveImportExcel(InputStream excelStream) throws IOException { 41 | 42 | // 通过输入流拿到数据 43 | ExcelReader reader = ExcelUtil.getReader(excelStream); 44 | List> read = reader.read(1); 45 | 46 | 47 | // 构造结果集 48 | List longUrls = new ArrayList<>(); 49 | for (List objects : read) { 50 | for (Object object : objects) { 51 | longUrls.add((String) object); 52 | } 53 | } 54 | 55 | return longUrls ; 56 | } 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /url-service/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=short-url 2 | 3 | server.address=localhost 4 | server.port=8888 5 | 6 | spring.mvc.servlet.path=/ 7 | 8 | # mysql 的配置 9 | spring.datasource.url=jdbc:mysql://localhost:3306/short_url?useUnicode=true&characterEncoding=utf8&useSSL=false 10 | spring.datasource.username=root 11 | spring.datasource.password=root 12 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 13 | 14 | 15 | # redis 的配置 16 | spring.redis.port=6379 17 | spring.redis.host=localhost 18 | 19 | 20 | # short-url 字符串池配置: 一共10+26+26+2=64个字符 21 | # 26个小写字母+26个大写字母+10个数字+ '-' + '_' 22 | short.url.string.pool=0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_ 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /url-service/src/test/java/com/hk/surl/service/test/ExcelTest.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.service.test; 2 | 3 | import cn.hutool.poi.excel.ExcelReader; 4 | import cn.hutool.poi.excel.ExcelUtil; 5 | import com.hk.surl.common.entity.LongUrlDTO; 6 | import org.junit.Test; 7 | 8 | import java.io.File; 9 | import java.io.FileInputStream; 10 | import java.io.FileNotFoundException; 11 | import java.io.InputStream; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | /** 16 | * @author : HK意境 17 | * @ClassName : ExcelTest 18 | * @date : 2022/6/15 22:12 19 | * @description : 20 | * @Todo : 21 | * @Bug : 22 | * @Modified : 23 | * @Version : 1.0 24 | */ 25 | public class ExcelTest { 26 | 27 | private String filePath = "F:\\JavaCode\\Short-URL\\url-web\\src\\main\\resources\\template\\longUrlTemplate.xlsx"; 28 | 29 | 30 | @Test 31 | public void read() throws FileNotFoundException { 32 | 33 | InputStream inputStream = new FileInputStream(new File(filePath)); 34 | 35 | ExcelReader reader = ExcelUtil.getReader(inputStream); 36 | List> read = reader.read(); 37 | 38 | 39 | //List> lists = ExcelUtil.getReader(inputStream).read(); 40 | 41 | List longUrls = new ArrayList<>(); 42 | 43 | for (List list : read) { 44 | for (Object s : list) { 45 | longUrls.add((String) s); 46 | } 47 | } 48 | 49 | 50 | 51 | } 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /url-system/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Short-URL 7 | com.hk.surl 8 | 1.0.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | url-system 13 | 14 | 15 | -------------------------------------------------------------------------------- /url-web/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Short-URL 7 | com.hk.surl 8 | 1.0.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | url-web 13 | 14 | jar 15 | 16 | 17 | 11 18 | 11 19 | 20 | 21 | 22 | 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-mobile 27 | 1.5.22.RELEASE 28 | 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-aop 33 | 34 | 35 | 36 | org.aspectj 37 | aspectjweaver 38 | 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-data-redis 44 | 45 | 46 | 47 | 48 | 49 | org.springdoc 50 | springdoc-openapi-ui 51 | 1.5.0 52 | 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-starter-web 57 | 58 | 59 | 60 | com.fasterxml.jackson.datatype 61 | jackson-datatype-jsr310 62 | 63 | 64 | 65 | com.hk.surl 66 | url-common 67 | 0.1.0 68 | 69 | 70 | 71 | com.hk.surl 72 | url-service 73 | 0.1.0 74 | 75 | 76 | com.hk.surl 77 | url-core 78 | 1.0.0-SNAPSHOT 79 | compile 80 | 81 | 82 | 83 | 84 | 85 | 86 | org.springframework.boot 87 | spring-boot-maven-plugin 88 | 89 | com.hk.surl.web.ShortUrlWebApplication 90 | 91 | 92 | 93 | 94 | 95 | repackage 96 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /url-web/src/main/java/com/hk/surl/web/ShortUrlWebApplication.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.web; 2 | 3 | import com.hk.surl.web.aop.EnableSysLog; 4 | import org.mybatis.spring.annotation.MapperScan; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.context.annotation.EnableAspectJAutoProxy; 8 | import org.springframework.scheduling.annotation.EnableAsync; 9 | 10 | /** 11 | * @author : HK意境 12 | * @ClassName : ShortUrlWebApplication 13 | * @date : 2022/6/1 16:26 14 | * @description : 15 | * @Todo : 16 | * @Bug : 17 | * @Modified : 18 | * @Version : 1.0 19 | */ 20 | 21 | @MapperScan(basePackages = "com.hk.surl.domain.mapper") 22 | @EnableAsync 23 | @EnableAspectJAutoProxy 24 | @SpringBootApplication(scanBasePackages = "com.hk.surl") 25 | public class ShortUrlWebApplication { 26 | 27 | public static void main(String[] args) { 28 | SpringApplication.run(ShortUrlWebApplication.class, args); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /url-web/src/main/java/com/hk/surl/web/aop/EnableSysLog.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.web.aop; 2 | 3 | 4 | import java.lang.annotation.*; 5 | 6 | /** 7 | * @Description: 启用系统日志 8 | * 9 | * @author: binghua.zheng 10 | * @Date: 2021/11/12 23:01 11 | */ 12 | @Target({ElementType.TYPE}) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Documented 15 | //@Import(AopAspectConfig.class) 16 | public @interface EnableSysLog { 17 | } 18 | -------------------------------------------------------------------------------- /url-web/src/main/java/com/hk/surl/web/aop/SysLog.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.web.aop; 2 | 3 | /** 4 | * @author : HK意境 5 | * @ClassName : SysLog 6 | * @date : 2022/6/14 10:09 7 | * @description : 系统日志注解 8 | * @Todo : 9 | * @Bug : 10 | * @Modified : 11 | * @Version : 1.0 12 | */ 13 | 14 | import com.hk.surl.web.aop.processor.DefaultProcessor; 15 | import com.hk.surl.web.aop.processor.LogProcessor; 16 | 17 | import java.lang.annotation.*; 18 | 19 | @Target({ElementType.METHOD,ElementType.TYPE}) 20 | @Retention(RetentionPolicy.RUNTIME) 21 | @Documented 22 | public @interface SysLog { 23 | 24 | /** 25 | * 业务类型 26 | * 27 | * @return string 28 | */ 29 | String businessType() default ""; 30 | 31 | /** 32 | * 方法说明 33 | * 34 | * @return string 35 | */ 36 | String method() default ""; 37 | 38 | /** 39 | * 执行的操作 40 | * 41 | */ 42 | String operate() default "" ; 43 | 44 | /** 45 | * 日志级别 1-重要; 2-普通 46 | * 47 | * @return string 48 | */ 49 | String level() default "2"; 50 | 51 | // 处理器 52 | Class processor() default DefaultProcessor.class ; 53 | 54 | 55 | } 56 | -------------------------------------------------------------------------------- /url-web/src/main/java/com/hk/surl/web/aop/SysLogAspect.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.web.aop; 2 | 3 | import com.hk.surl.common.response.ResponseResult; 4 | import com.hk.surl.domain.vo.ShortUrlVo; 5 | import com.hk.surl.service.util.AsyncTaskService; 6 | import com.hk.surl.web.aop.processor.AccessProcessor; 7 | import com.hk.surl.web.aop.processor.DefaultProcessor; 8 | import com.hk.surl.web.aop.processor.LogProcessor; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.aspectj.lang.ProceedingJoinPoint; 11 | import org.aspectj.lang.annotation.Around; 12 | import org.aspectj.lang.annotation.Aspect; 13 | import org.aspectj.lang.annotation.Pointcut; 14 | import org.aspectj.lang.reflect.MethodSignature; 15 | 16 | import javax.annotation.Resource; 17 | 18 | /** 19 | * @author : HK意境 20 | * @ClassName : SysLogAspect 21 | * @date : 2022/6/14 10:15 22 | * @description : 系统日志切面 23 | * @Todo : 24 | * @Bug : 25 | * @Modified : 26 | * @Version : 1.0 27 | */ 28 | @Slf4j 29 | @Aspect 30 | public class SysLogAspect { 31 | 32 | @Resource 33 | private AsyncTaskService taskService ; 34 | @Resource 35 | private DefaultProcessor defaultProcessor; 36 | @Resource 37 | private AccessProcessor accessProcessor ; 38 | 39 | @Pointcut("@annotation(com.hk.surl.web.aop.SysLog)") 40 | public void pointcut(){ } 41 | 42 | 43 | /** 44 | * 拦截Controller的 @SysLog 注解,记录相关请求日志信息 45 | */ 46 | @Around("pointcut()") 47 | public Object handleSysLogPoint(ProceedingJoinPoint point) throws Throwable { 48 | 49 | // 获取 处理器类型 50 | MethodSignature signature = (MethodSignature)point.getSignature(); 51 | SysLog sysLog = signature.getMethod().getAnnotation(SysLog.class); 52 | // 获取处理器类型 53 | Class processor = sysLog.processor(); 54 | 55 | // 定义返回结果 56 | Object result = null ; 57 | 58 | // 默认处理器, 首先进行默认处理 59 | result = this.defaultProcessor.process(point, null); 60 | 61 | // 判断执行结果是否正确 62 | if (processor == AccessProcessor.class){ 63 | if (result instanceof ResponseResult){ 64 | // 响应正确 65 | ResponseResult responseResult = (ResponseResult) result; 66 | result = this.accessProcessor.process(point, responseResult.getData()); 67 | } 68 | } 69 | 70 | 71 | return result ; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /url-web/src/main/java/com/hk/surl/web/aop/processor/LogProcessor.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.web.aop.processor; 2 | 3 | import com.hk.surl.domain.entity.ShortUrl; 4 | import com.hk.surl.domain.vo.ShortUrlVo; 5 | import org.aspectj.lang.ProceedingJoinPoint; 6 | 7 | /** 8 | * @author : HK意境 9 | * @ClassName : LogProcessor 10 | * @date : 2022/6/14 22:47 11 | * @description : 日志处理抽象类 12 | * @Todo : 13 | * @Bug : 14 | * @Modified : 15 | * @Version : 1.0 16 | */ 17 | public abstract class LogProcessor { 18 | 19 | public abstract Object process(ProceedingJoinPoint point, ShortUrlVo vo) throws Throwable; 20 | 21 | public abstract Object after(Object result); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /url-web/src/main/java/com/hk/surl/web/config/AopAspectConfig.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.web.config; 2 | 3 | import com.hk.surl.web.aop.SysLogAspect; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.context.annotation.EnableAspectJAutoProxy; 7 | 8 | /** 9 | * @author : HK意境 10 | * @ClassName : AopAspectConfig 11 | * @date : 2022/6/14 14:38 12 | * @description : 面向切面编配置类 13 | * @Todo : 14 | * @Bug : 15 | * @Modified : 16 | * @Version : 1.0 17 | */ 18 | @Configuration 19 | public class AopAspectConfig { 20 | 21 | @Bean 22 | public SysLogAspect sysLogAspect(){ 23 | return new SysLogAspect(); 24 | } 25 | 26 | 27 | } 28 | -------------------------------------------------------------------------------- /url-web/src/main/java/com/hk/surl/web/config/AsyncTaskConfig.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.web.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 6 | 7 | import java.util.concurrent.Executor; 8 | import java.util.concurrent.ThreadPoolExecutor; 9 | 10 | 11 | /** 12 | * @author : HK意境 13 | * @ClassName : AsyncTaskConfig 14 | * @date : 2022/6/11 13:08 15 | * @description : 异步任务配置类 16 | * @Todo : 配置线程池,线程池销毁策略等 17 | * @Bug : 18 | * @Modified : 19 | * @Version : 1.0 20 | */ 21 | @Configuration 22 | public class AsyncTaskConfig { 23 | 24 | // 获取cpu 核心数量 25 | public static Integer cpuCores = Runtime.getRuntime().availableProcessors(); 26 | 27 | // 配置线程池的名称,调用异步任务对的时候需要指定使用的executor 28 | 29 | /*** 30 | * 2、参数配置 31 | * 配置参数时需要考虑 CPU密集型任务 、 IO密集型任务 、内存使用率 、下游系统抗并发的能力 32 | * 33 | * 配置参数: 34 | * CPU密集型 CPU的核数+1 35 | * IO密集型 一般配置 2*CPU的核数 36 | * 参考公式(某大厂配置): 37 | * CPU核数/(1-阻塞系数) 阻塞系数在0.8~0.9之间 38 | * 比如8核CPU 8/(1-0.9) = 80个线程数 39 | * @return 40 | */ 41 | @Bean(name = "asyncTaskExecutor") 42 | public Executor asyncTaskExecutor(){ 43 | 44 | ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 45 | // 核心线程数: 线程池创建时,初始化的线程数 46 | executor.setCorePoolSize(AsyncTaskConfig.cpuCores * 2); 47 | // 最大线程数量: 只有在阻塞队列中任务数量满了后,才会创建救急线程 : 1.5 倍系数 48 | executor.setMaxPoolSize(executor.getCorePoolSize() + (executor.getCorePoolSize() /2) ); 49 | // 缓冲队列:用来缓冲执行任务的队列 50 | executor.setQueueCapacity(500); 51 | // 救急线程生存时间:允许救济线程空闲时间,超过生存时间,将会被销毁 52 | executor.setKeepAliveSeconds(60); 53 | // 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池 54 | executor.setThreadNamePrefix("async-task-"); 55 | // 拒绝策略: 56 | executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); 57 | // 关闭线程时,执行完全部任务才销毁 58 | executor.setWaitForTasksToCompleteOnShutdown(true); 59 | // 阻塞队列任务等待时间,超时销毁 60 | //executor.setAwaitTerminationSeconds(60); 61 | 62 | executor.initialize(); 63 | 64 | return executor ; 65 | } 66 | 67 | 68 | 69 | 70 | } 71 | -------------------------------------------------------------------------------- /url-web/src/main/java/com/hk/surl/web/config/DruidDatasourceConfig.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.web.config; 2 | 3 | import com.alibaba.druid.pool.DruidDataSource; 4 | import com.alibaba.druid.support.http.StatViewServlet; 5 | import com.alibaba.druid.support.http.WebStatFilter; 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | import org.springframework.boot.web.servlet.FilterRegistrationBean; 8 | import org.springframework.boot.web.servlet.ServletRegistrationBean; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | 12 | import javax.sql.DataSource; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | /** 17 | * @author : HK意境 18 | * @ClassName : DruidDatasourceConfig 19 | * @date : 2022/6/12 23:52 20 | * @description : 21 | * @Todo : 22 | * @Bug : 23 | * @Modified : 24 | * @Version : 1.0 25 | */ 26 | @Configuration 27 | public class DruidDatasourceConfig { 28 | 29 | @ConfigurationProperties(prefix = "spring.datasource") 30 | @Bean 31 | public DataSource druidDataSource(){ 32 | return new DruidDataSource(); 33 | } 34 | 35 | 36 | //因为Springboot内置了servlet容器,所以没有web.xml,替代方法就是将ServletRegistrationBean注册进去 37 | //加入后台监控 38 | @Bean //这里其实就相当于servlet的web.xml 39 | public ServletRegistrationBean statViewServlet(){ 40 | ServletRegistrationBean bean = 41 | new ServletRegistrationBean(new StatViewServlet(),"/druid/*"); 42 | 43 | //后台需要有人登录,进行配置 44 | //bean.addUrlMappings(); 这个可以添加映射,我们在构造里已经写了 45 | //设置一些初始化参数 46 | Map initParas = new HashMap(); 47 | initParas.put("loginUsername","admin");//它这个账户密码是固定的 48 | initParas.put("loginPassword","admin"); 49 | //允许谁能防伪 50 | initParas.put("allow","");//这个值为空或没有就允许所有人访问,ip白名单 51 | //initParas.put("allow","localhost");//只允许本机访问,多个ip用逗号,隔开 52 | //initParas.put("deny","");//ip黑名单,拒绝谁访问 deny和allow同时存在优先deny 53 | initParas.put("resetEnable","false");//禁用HTML页面的Reset按钮 54 | bean.setInitParameters(initParas); 55 | return bean; 56 | } 57 | 58 | //再配置一个过滤器,Servlet按上面的方式注册Filter也只能这样 59 | @Bean 60 | public FilterRegistrationBean webStatFilter(){ 61 | FilterRegistrationBean bean = new FilterRegistrationBean(); 62 | //可以设置也可以获取,设置一个阿里巴巴的过滤器 63 | bean.setFilter(new WebStatFilter()); 64 | bean.addUrlPatterns("/*"); 65 | //可以过滤和排除哪些东西 66 | Map initParams = new HashMap(); 67 | //把不需要监控的过滤掉,这些不进行统计 68 | initParams.put("exclusions","*.js,*.css,/druid/*"); 69 | bean.setInitParameters(initParams); 70 | return bean; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /url-web/src/main/java/com/hk/surl/web/config/LocalDateTimeConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.web.config; 2 | 3 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; 4 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | import java.time.LocalDateTime; 11 | import java.time.format.DateTimeFormatter; 12 | 13 | /** 14 | * @author : HK意境 15 | * @ClassName : LocalDateTimeConfiguration 16 | * @date : 2022/6/13 0:40 17 | * @description : 18 | * @Todo : 19 | * @Bug : 20 | * @Modified : 21 | * @Version : 1.0 22 | */ 23 | @Configuration 24 | public class LocalDateTimeConfiguration { 25 | 26 | @Value("${spring.jackson.date-format:yyyy-MM-dd HH:mm:ss}") 27 | private String pattern; 28 | 29 | 30 | @Bean 31 | public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() { 32 | return builder -> { 33 | DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern); 34 | 35 | //返回时间数据序列化 36 | builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(formatter)); 37 | //接收时间数据反序列化 38 | builder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(formatter)); 39 | }; 40 | } 41 | 42 | 43 | } 44 | -------------------------------------------------------------------------------- /url-web/src/main/java/com/hk/surl/web/config/MobileConfig.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.web.config; 2 | 3 | import com.hk.surl.common.util.LocationUtil; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.mobile.device.DeviceHandlerMethodArgumentResolver; 7 | import org.springframework.mobile.device.DeviceResolverHandlerInterceptor; 8 | import org.springframework.web.method.support.HandlerMethodArgumentResolver; 9 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 10 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * @author : HK意境 16 | * @ClassName : MobileConfig 17 | * @date : 2022/6/20 15:33 18 | * @description : 19 | * @Todo : 20 | * @Bug : 21 | * @Modified : 22 | * @Version : 1.0 23 | */ 24 | @Configuration 25 | public class MobileConfig implements WebMvcConfigurer { 26 | 27 | // 获取请求者的location 28 | @Bean 29 | public LocationUtil locationUtil(){ 30 | return new LocationUtil(); 31 | } 32 | 33 | @Bean 34 | public DeviceResolverHandlerInterceptor deviceResolverHandlerInterceptor() { 35 | return new DeviceResolverHandlerInterceptor(); 36 | } 37 | 38 | @Bean 39 | public DeviceHandlerMethodArgumentResolver 40 | deviceHandlerMethodArgumentResolver() { 41 | return new DeviceHandlerMethodArgumentResolver(); 42 | } 43 | 44 | @Override 45 | public void addInterceptors(InterceptorRegistry registry) { 46 | registry.addInterceptor(new DeviceResolverHandlerInterceptor()); 47 | } 48 | 49 | @Override 50 | public void addArgumentResolvers(List argumentResolvers) { 51 | argumentResolvers.add(new DeviceHandlerMethodArgumentResolver()); 52 | } 53 | 54 | 55 | 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /url-web/src/main/java/com/hk/surl/web/config/MybatisPlusConfig.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.web.config; 2 | 3 | import com.baomidou.mybatisplus.annotation.DbType; 4 | import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; 5 | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; 6 | import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; 7 | import org.apache.ibatis.reflection.MetaObject; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | 11 | import java.time.LocalDateTime; 12 | 13 | /** 14 | * @author : HK意境 15 | * @ClassName : MybatisPlusConfig 16 | * @date : 2022/6/1 16:39 17 | * @description : 配置分页插件,自动注入 18 | * @Todo : 19 | * @Bug : 20 | * @Modified : 21 | * @Version : 1.0 22 | */ 23 | @Configuration 24 | public class MybatisPlusConfig { 25 | 26 | // 自动注入功能 27 | @Bean 28 | public MetaObjectHandler getMetaObjectHandler(){ 29 | 30 | MetaObjectHandler metaObjectHandler = new MetaObjectHandler() { 31 | @Override 32 | public void insertFill(MetaObject metaObject) { 33 | this.strictInsertFill(metaObject,"createTime", LocalDateTime.class, LocalDateTime.now()); 34 | this.strictInsertFill(metaObject,"updateTime", LocalDateTime.class, LocalDateTime.now()); 35 | 36 | } 37 | 38 | @Override 39 | public void updateFill(MetaObject metaObject) { 40 | this.strictInsertFill(metaObject,"updateTime", LocalDateTime.class, LocalDateTime.now()); 41 | } 42 | }; 43 | 44 | return metaObjectHandler; 45 | } 46 | 47 | 48 | // 分页插件 49 | @Bean 50 | public MybatisPlusInterceptor mybatisPlusInterceptor() { 51 | MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); 52 | interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); 53 | return interceptor; 54 | } 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /url-web/src/main/java/com/hk/surl/web/config/ShortUrlGeneratorConfig.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.web.config; 2 | 3 | import com.hk.surl.core.generator.template.DefaultShortUrlGenerator; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | /** 9 | * @author : HK意境 10 | * @ClassName : ShortUrlGeneratorConfig 11 | * @date : 2022/6/11 12:50 12 | * @description : 13 | * @Todo : 14 | * @Bug : 15 | * @Modified : 16 | * @Version : 1.0 17 | */ 18 | @Configuration 19 | public class ShortUrlGeneratorConfig { 20 | 21 | @Value("${short.url.system.domain}") 22 | private String domain ; 23 | 24 | /** 25 | * 设置短链接系统域名 26 | * 将 shorUrlGenerator 将默认的短链接生成器注入到容器中, 27 | * @return ShortUrlGenerator 28 | */ 29 | @Bean 30 | public DefaultShortUrlGenerator getDefaultShortUrlGenerator(){ 31 | return new DefaultShortUrlGenerator(domain); 32 | } 33 | 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /url-web/src/main/java/com/hk/surl/web/config/WebConfig.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.web.config; 2 | 3 | import com.hk.surl.web.interceptor.FaviconInterceptor; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.ui.ModelMap; 6 | import org.springframework.web.context.request.WebRequest; 7 | import org.springframework.web.context.request.WebRequestInterceptor; 8 | import org.springframework.web.servlet.config.annotation.*; 9 | import org.springframework.web.servlet.handler.WebRequestHandlerInterceptorAdapter; 10 | 11 | /** 12 | * @author : HK意境 13 | * @ClassName : WebConfig 14 | * @date : 2022/6/12 22:16 15 | * @description : 16 | * @Todo : 17 | * @Bug : 18 | * @Modified : 19 | * @Version : 1.0 20 | */ 21 | @Configuration 22 | @EnableWebMvc 23 | public class WebConfig implements WebMvcConfigurer { 24 | 25 | /** 26 | * 跨域配置会覆盖默认的配置, 27 | * 因此需要实现addResourceHandlers方法,增加默认配置静态路径 28 | * @param registry 29 | */ 30 | @Override 31 | public void addResourceHandlers(ResourceHandlerRegistry registry) { 32 | registry.addResourceHandler("/**") 33 | .addResourceLocations("classpath:/resources/") 34 | .addResourceLocations("classpath:/static/"); 35 | } 36 | 37 | @Override 38 | public void addCorsMappings(CorsRegistry registry) { 39 | registry.addMapping("/**") 40 | // 设置允许跨域请求的域名 41 | .allowedOriginPatterns("*") 42 | // 设置允许的方法 43 | .allowedMethods("*") 44 | // 是否允许证书(cookies) 45 | .allowCredentials(true); 46 | 47 | } 48 | 49 | 50 | @Override 51 | public void addInterceptors(InterceptorRegistry registry) { 52 | 53 | registry.addInterceptor(new FaviconInterceptor()) 54 | .addPathPatterns("/favicon.ico"); 55 | } 56 | 57 | 58 | 59 | 60 | 61 | 62 | } 63 | -------------------------------------------------------------------------------- /url-web/src/main/java/com/hk/surl/web/controller/AppUserController.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.web.controller; 2 | 3 | 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | /** 8 | * @ClassName : AppUserController 9 | * @author : HK意境 10 | * @date : 2022/5/26 15:32 11 | * @description :用户User,Sass对外提供接口的使用用户 前端控制器 12 | * @Todo : 用户空间,监测用户数据的 13 | * @Bug : 14 | * @Modified : 15 | * @Version : 1.0 16 | */ 17 | @RestController 18 | @RequestMapping("/space") 19 | public class AppUserController { 20 | 21 | 22 | // 登录进入用户空间 23 | // 这里需要进行拦截器进入拦截 24 | 25 | 26 | 27 | } 28 | -------------------------------------------------------------------------------- /url-web/src/main/java/com/hk/surl/web/controller/QRCodeController.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.web.controller; 2 | 3 | import com.hk.surl.common.response.ResponseResult; 4 | import com.hk.surl.common.util.QRCodeUtil; 5 | import com.hk.surl.domain.vo.ShortUrlVo; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestParam; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | import javax.servlet.http.HttpServletResponse; 12 | import java.io.IOException; 13 | 14 | 15 | /** 16 | * @author : HK意境 17 | * @ClassName : QRCodeController 18 | * @date : 2022/6/13 19:46 19 | * @description : 二维码测试 20 | * @Todo : 21 | * @Bug : 22 | * @Modified : 23 | * @Version : 1.0 24 | */ 25 | @Slf4j 26 | @RestController(value = "/qr") 27 | public class QRCodeController { 28 | 29 | /** 30 | * @methodName : getQrCode 31 | * @author : HK意境 32 | * @date : 2022/6/13 19:52 33 | * @description : 同一个 url 链接地址生成的二维码是同一个 34 | * @Todo : 35 | * @apiNote : 生成content链接的二维码 36 | * @params : 37 | * @param content 需要跳转的url 链接 38 | * @return ResponseResult 39 | * @throws: 40 | * @Bug : 41 | * @Modified : 42 | * @Version : 1.0.0 43 | */ 44 | @GetMapping("/surl") 45 | public ResponseResult getQrCode(@RequestParam(name = "content") String content , HttpServletResponse response){ 46 | 47 | //log.info("content: {}",content); 48 | try { 49 | QRCodeUtil.createCodeToOutputStream(content, response.getOutputStream()); 50 | 51 | } catch (IOException e) { 52 | log.error("生成二维码错误,错误信息是:{}", e.getMessage()); 53 | } 54 | return ResponseResult.SUCCESS().setData(new ShortUrlVo(content, "https://blog.csdn.net/private_name/article/details/124144802")); 55 | } 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /url-web/src/main/java/com/hk/surl/web/controller/VisitLogController.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.web.controller; 2 | 3 | 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | /** 8 | * @ClassName : VisitLogController 9 | * @author : HK意境 10 | * @date : 2022/5/26 15:37 11 | * @description :短链接访问日志 前端控制器 12 | * @Todo : 13 | * @Bug : 14 | * @Modified : 15 | * @Version : 1.0 16 | */ 17 | @RestController 18 | @RequestMapping("/visit") 19 | public class VisitLogController { 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /url-web/src/main/java/com/hk/surl/web/exception/UrlWebException.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.web.exception; 2 | 3 | import com.hk.surl.common.exception.BaseException; 4 | import com.hk.surl.common.response.ResultCode; 5 | import lombok.Data; 6 | 7 | /** 8 | * @author : HK意境 9 | * @ClassName : UrlWebException 10 | * @date : 2022/6/10 9:23 11 | * @description : Web 模块自定义业务异常 12 | * @Todo : 13 | * @Bug : 14 | * @Modified : 15 | * @Version : 1.0 16 | */ 17 | @Data 18 | public class UrlWebException extends BaseException { 19 | 20 | public UrlWebException(Integer code, String msg) { 21 | super(code,msg); 22 | } 23 | 24 | 25 | public UrlWebException(ResultCode resultCode){ 26 | super(resultCode); 27 | } 28 | 29 | 30 | } 31 | -------------------------------------------------------------------------------- /url-web/src/main/java/com/hk/surl/web/interceptor/FaviconInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.web.interceptor; 2 | 3 | import org.springframework.stereotype.Component; 4 | import org.springframework.web.servlet.HandlerInterceptor; 5 | import org.springframework.web.servlet.ModelAndView; 6 | 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | 10 | /** 11 | * @author : HK意境 12 | * @ClassName : FaviconInterceptor 13 | * @date : 2022/6/13 19:13 14 | * @description : 15 | * @Todo : 16 | * @Bug : 17 | * @Modified : 18 | * @Version : 1.0 19 | */ 20 | public class FaviconInterceptor implements HandlerInterceptor { 21 | 22 | @Override 23 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 24 | 25 | String requestURI = request.getRequestURI(); 26 | // 拦截 图标请求 27 | if ("/favicon.ico".equals(requestURI)){ 28 | // 拦截不予以放行 29 | return false; 30 | }else{ 31 | return true ; 32 | } 33 | } 34 | 35 | @Override 36 | public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { 37 | HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); 38 | } 39 | 40 | @Override 41 | public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { 42 | HandlerInterceptor.super.afterCompletion(request, response, handler, ex); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /url-web/src/main/java/com/hk/surl/web/interceptor/LogAndTranceInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.web.interceptor; 2 | 3 | import org.springframework.web.servlet.HandlerInterceptor; 4 | import org.springframework.web.servlet.ModelAndView; 5 | 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | 9 | /** 10 | * @author : HK意境 11 | * @ClassName : LogAndTranceInterceptor 12 | * @date : 2022/6/14 9:22 13 | * @description : 日志和链路拦截器 14 | * @Todo : 15 | * @Bug : 16 | * @Modified : 17 | * @Version : 1.0 18 | */ 19 | public class LogAndTranceInterceptor implements HandlerInterceptor { 20 | 21 | 22 | 23 | /** 24 | * @methodName : preHandle 25 | * @author : HK意境 26 | * @date : 2022/6/14 9:36 27 | * @description : 28 | * @Todo : 29 | * @apiNote : 30 | * @params : 31 | * @param request 请求 32 | * @param response 响应 33 | * @param handler 34 | * @return boolean 是否放行 35 | * @throws: 36 | * @Bug : 37 | * @Modified : 38 | * @Version : 1.0.0 39 | */ 40 | @Override 41 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 42 | 43 | // 获取请求路径,参数,用户浏览器等信息 44 | 45 | return HandlerInterceptor.super.preHandle(request, response, handler); 46 | } 47 | 48 | @Override 49 | public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { 50 | HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); 51 | } 52 | 53 | @Override 54 | public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { 55 | HandlerInterceptor.super.afterCompletion(request, response, handler, ex); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /url-web/src/main/java/com/hk/surl/web/interceptor/LoginInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.web.interceptor; 2 | 3 | import com.hk.surl.common.exception.BaseException; 4 | import com.hk.surl.common.response.ResultCode; 5 | import com.hk.surl.domain.entity.AnonymousUser; 6 | import com.hk.surl.web.exception.UrlWebException; 7 | import org.springframework.data.redis.core.RedisTemplate; 8 | import org.springframework.web.servlet.HandlerInterceptor; 9 | import org.springframework.web.servlet.ModelAndView; 10 | 11 | import javax.annotation.Resource; 12 | import javax.servlet.http.HttpServletRequest; 13 | import javax.servlet.http.HttpServletResponse; 14 | 15 | /** 16 | * @author : HK意境 17 | * @ClassName : LoginInterceptor 18 | * @date : 2022/6/19 14:08 19 | * @description : 登录用户空间拦截器 20 | * @Todo : 21 | * @Bug : 22 | * @Modified : 23 | * @Version : 1.0 24 | */ 25 | public class LoginInterceptor implements HandlerInterceptor { 26 | 27 | // access_token 请求头中的 访问凭证名称 28 | private static String ACCESS_TOKEN = "secretKey" ; 29 | 30 | @Resource 31 | private RedisTemplate redisTemplate; 32 | 33 | 34 | @Override 35 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 36 | 37 | // 查看是否存在 短链接和 短链接密钥(RSA 派发的密钥) 38 | String secretKey = request.getHeader(ACCESS_TOKEN); 39 | 40 | // 请求头是否包含安全码 41 | if (secretKey == null || "".equals(secretKey)){ 42 | // 没有授予安全码,未认证 43 | throw new UrlWebException(ResultCode.UNAUTHENTICATED); 44 | } 45 | 46 | // 校验安全码是否正确:先在redis 缓存里面查询 47 | AnonymousUser user = (AnonymousUser) redisTemplate.opsForValue().get(secretKey); 48 | 49 | if (user == null){ 50 | // 用户登录已过期 51 | throw new UrlWebException(ResultCode.ACCESS_TOKEN_EXPIRED); 52 | } 53 | 54 | // 已经认证可以继续访问 55 | return true; 56 | } 57 | 58 | @Override 59 | public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { 60 | HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); 61 | } 62 | 63 | @Override 64 | public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { 65 | HandlerInterceptor.super.afterCompletion(request, response, handler, ex); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /url-web/src/main/resources/application-dev.properties: -------------------------------------------------------------------------------- 1 | # mysql 的配置 2 | spring.datasource.url=jdbc:mysql://127.0.0.1:3306/db_short_url?useUnicode=true&characterEncoding=utf8&useSSL=false 3 | spring.datasource.username=root 4 | spring.datasource.password=root 5 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 6 | 7 | #通过connectProperties属性来打开mergeSql功能;慢SQL记录 8 | # spring.datasource.druid.connect-properties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 9 | mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl 10 | 11 | 12 | 13 | # redis 配置 14 | spring.redis.host=localhost 15 | spring.redis.port=6379 -------------------------------------------------------------------------------- /url-web/src/main/resources/application-prod.properties: -------------------------------------------------------------------------------- 1 | server.port=80 2 | server.address=127.0.0.1 3 | server.servlet.context-path=/ 4 | 5 | # mysql 的配置 6 | spring.datasource.url=jdbc:mysql://127.0.0.1/db_short_url?useUnicode=true&characterEncoding=utf8&useSSL=false 7 | spring.datasource.username=user_short_url 8 | spring.datasource.password=3161880795@hh 9 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 10 | -------------------------------------------------------------------------------- /url-web/src/main/resources/application-test.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HK-hub/Short-URL/e666ded026b8439fad95dfb0d3d50516e7d6ad9f/url-web/src/main/resources/application-test.properties -------------------------------------------------------------------------------- /url-web/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=short-url 2 | 3 | server.address=localhost 4 | server.port=80 5 | spring.mvc.servlet.path=/ 6 | 7 | # 配置项目启动环境 8 | spring.profiles.active=dev 9 | 10 | 11 | # mysql 的配置 12 | spring.datasource.url=jdbc:mysql://localhost:3306/db_short_url?useUnicode=true&characterEncoding=utf8&useSSL=false 13 | spring.datasource.username=root 14 | spring.datasource.password=root 15 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 16 | 17 | 18 | # 使用 druid 数据源 19 | spring.datasource.druid.username=root 20 | spring.datasource.druid.password=root 21 | spring.datasource.type=com.alibaba.druid.pool.DruidDataSource 22 | spring.datasource.druid.pool-prepared-statements=true 23 | spring.datasource.druid.initial-size=10 24 | spring.datasource.druid.max-active=30 25 | spring.datasource.druid.min-idle=10 26 | spring.datasource.druid.max-wait=2000 27 | #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入 28 | #如果运行时报错 java.lang.ClassNotFoundException: org.apache.log4j.Priority 29 | #则导入 log4j 依赖即可,Maven 地址: https://mvnrepository.com/artifact/log4j/log4j 30 | spring.datasource.druid.filter.stat.enabled=true 31 | spring.datasource.druid.filter.stat.log-slow-sql=true 32 | spring.datasource.druid.filter.stat.slow-sql-millis=5000 33 | spring.datasource.druid.filter.stat.merge-sql=true 34 | spring.datasource.druid.useGlobalDataSourceStat=true 35 | #通过connectProperties属性来打开mergeSql功能;慢SQL记录 36 | # spring.datasource.druid.connect-properties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 37 | # mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl 38 | 39 | 40 | 41 | 42 | # redis 的配置 43 | spring.redis.port=6379 44 | spring.redis.host=localhost 45 | 46 | 47 | # smart-doc 配置,swagger 配置 48 | springdoc.swagger-ui.path=/swagger.html 49 | springdoc.swagger-ui.operations-sorter=method 50 | springdoc.swagger-ui.url=/smart-doc.json 51 | 52 | # json 序列化配置 53 | spring.jackson.date-format=yyyy-MM-dd HH:mm:ss 54 | 55 | 56 | # 腾讯位置服务配置 57 | short.url.system.location.key=EP7BZ-JAOKQ-NIV55-GPNDX-CBPOQ-PHB7E 58 | # 腾讯位置服务服务端请求地址 59 | short.url.system.location.url=https://apis.map.qq.com/ws/location/v1/ip?ip={IP}&key=${short.url.system.location.key}; 60 | 61 | 62 | # short-url 字符串池配置: 一共10+26+26+2=64个字符 63 | # 26个小写字母+26个大写字母+10个数字+ '-' + '_' 64 | short.url.core.string.pool=0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_ 65 | # short-url 域名系统配置: 域名后面不要加 / 符号 66 | short.url.system.domain=suio.link 67 | # longUrl 长链接模板系统 68 | short.url.system.longUrl.template=/template/longUrlTemplate.xlsx 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /url-web/src/main/resources/smart-doc.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "outPath": "src/main/resources/static/doc", 4 | "serverUrl": "http://127.0.0.1:80", 5 | "pathPrefix": "", 6 | "allInOne": true, 7 | "coverOld": true, 8 | "style":"xt256", 9 | "createDebugPage": true, 10 | "sortByTitle":false, 11 | "showAuthor":true, 12 | "inlineEnum":true, 13 | "allInOneDocFileName":"index.html", 14 | "packageFilters": "com.hk.surl.web.controller.*" 15 | 16 | } -------------------------------------------------------------------------------- /url-web/src/main/resources/static/doc/db/db_short_url_tb_app_user.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HK-hub/Short-URL/e666ded026b8439fad95dfb0d3d50516e7d6ad9f/url-web/src/main/resources/static/doc/db/db_short_url_tb_app_user.sql -------------------------------------------------------------------------------- /url-web/src/main/resources/static/doc/db/db_short_url_tb_tiny_id.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO db_short_url.tb_tiny_id (biz_type, begin_id, max_id, step, delta, remainder, remain_rate, create_time, update_time, version) VALUES ('short_url', 1013000, 1013000, 10000, 1, 10000, 0.2, '2022-05-31 16:57:14', '2022-06-08 20:45:37', 1013132); -------------------------------------------------------------------------------- /url-web/src/main/resources/static/doc/db/db_short_url_tb_url_type.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HK-hub/Short-URL/e666ded026b8439fad95dfb0d3d50516e7d6ad9f/url-web/src/main/resources/static/doc/db/db_short_url_tb_url_type.sql -------------------------------------------------------------------------------- /url-web/src/main/resources/static/doc/xt256.min.css: -------------------------------------------------------------------------------- 1 | .hljs{display:block;overflow-x:auto;color:#eaeaea;background:#000;padding:.5em}.hljs-subst{color:#eaeaea}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-builtin-name,.hljs-type{color:#eaeaea}.hljs-params{color:#da0000}.hljs-literal,.hljs-name,.hljs-number{color:red;font-weight:bolder}.hljs-comment{color:#969896}.hljs-quote,.hljs-selector-id{color:#0ff}.hljs-template-variable,.hljs-title,.hljs-variable{color:#0ff;font-weight:700}.hljs-keyword,.hljs-selector-class,.hljs-symbol{color:#fff000}.hljs-bullet,.hljs-string{color:#0f0}.hljs-section,.hljs-tag{color:#000fff}.hljs-selector-tag{color:#000fff;font-weight:700}.hljs-attribute,.hljs-built_in,.hljs-link,.hljs-regexp{color:#f0f}.hljs-meta{color:#fff;font-weight:bolder} -------------------------------------------------------------------------------- /url-web/src/main/resources/template/longUrlTemplate.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HK-hub/Short-URL/e666ded026b8439fad95dfb0d3d50516e7d6ad9f/url-web/src/main/resources/template/longUrlTemplate.xlsx -------------------------------------------------------------------------------- /url-web/src/test/java/com/hk/surl/web/test/ExcelTest.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.web.test; 2 | 3 | import cn.hutool.poi.excel.ExcelUtil; 4 | import org.junit.Test; 5 | 6 | import java.io.File; 7 | import java.io.FileInputStream; 8 | import java.io.FileNotFoundException; 9 | import java.io.InputStream; 10 | import java.util.List; 11 | 12 | /** 13 | * @author : HK意境 14 | * @ClassName : ExcelTest 15 | * @date : 2022/6/15 21:07 16 | * @description : 17 | * @Todo : 18 | * @Bug : 19 | * @Modified : 20 | * @Version : 1.0 21 | */ 22 | public class ExcelTest { 23 | 24 | private String filePath = "F:\\JavaCode\\Short-URL\\url-web\\src\\main\\resources\\template\\longUrlTemplate.xlsx"; 25 | 26 | 27 | @Test 28 | public void readExcelFile() throws FileNotFoundException { 29 | 30 | InputStream inputStream = new FileInputStream(filePath); 31 | 32 | List read = ExcelUtil.getReader(new File(filePath)).read(0, 1, String.class); 33 | //List list = ExcelUtil.getReader(inputStream).read(); 34 | 35 | read.forEach(System.out::println); 36 | 37 | } 38 | 39 | 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /url-web/src/test/java/com/hk/surl/web/test/StringTest.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.web.test; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.junit.Test; 5 | 6 | /** 7 | * @author : HK意境 8 | * @ClassName : StringTest 9 | * @date : 2022/6/9 20:40 10 | * @description : 11 | * @Todo : 12 | * @Bug : 13 | * @Modified : 14 | * @Version : 1.0 15 | */ 16 | @Slf4j 17 | public class StringTest { 18 | 19 | @Test 20 | public void subStringTest(){ 21 | 22 | String url = "http://localhost:8080/abc/agrg?greh=gteh?ferg=54"; 23 | url = url.substring(0,url.indexOf("?")); 24 | 25 | log.info("{}",url); 26 | 27 | } 28 | 29 | 30 | 31 | 32 | } 33 | -------------------------------------------------------------------------------- /url-web/src/test/java/com/hk/surl/web/test/UrlTest.java: -------------------------------------------------------------------------------- 1 | package com.hk.surl.web.test; 2 | 3 | import org.junit.Test; 4 | 5 | import java.io.IOException; 6 | import java.net.MalformedURLException; 7 | import java.net.URL; 8 | 9 | /** 10 | * @author : HK意境 11 | * @ClassName : UrlTest 12 | * @date : 2022/6/11 14:12 13 | * @description : 14 | * @Todo : 15 | * @Bug : 16 | * @Modified : 17 | * @Version : 1.0 18 | */ 19 | public class UrlTest { 20 | 21 | @Test 22 | public void URLTest() throws IOException { 23 | 24 | String longUrl = "https://blog.csdn.net/weixin_34221374/article/details/114346117?id=greghtrhtl&ur=grghh&pw=grh424515"; 25 | 26 | URL url = new URL(longUrl); 27 | System.out.println("getContent:" +url.getContent()); 28 | System.out.println("host:"+url.getHost()); 29 | System.out.println("path:" + url.getPath()); 30 | System.out.println("auth:"+url.getAuthority()); 31 | System.out.println("query:"+url.getQuery()); 32 | System.out.println("userinfo:"+ url.getUserInfo()); 33 | } 34 | 35 | 36 | } 37 | --------------------------------------------------------------------------------