diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml index 1cb3ea684..ca7e7a116 100644 --- a/ruoyi-admin/src/main/resources/application-dev.yml +++ b/ruoyi-admin/src/main/resources/application-dev.yml @@ -194,6 +194,16 @@ sms: signature: 您的短信签名 sdk-app-id: 您的sdkAppId +--- # 短链接 +shortlink: + # 是否启用租户域名生成 + enabled: false + # 访问前缀 + api: /dev-api + # 域名或主机名 + address: http://localhost/dev-api/resource/short/link/ + # 错误页面 + errorAddress: /error --- # 三方授权 justauth: diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index ad50248bc..076434e69 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -126,6 +126,7 @@ security: # EasyRetry Job 分派 @see com.aizuda.easy.retry.client.job.core.client.JobEndPoint - /job/dispatch/v1 - /job/stop/v1 + - /resource/short/link/* # 多租户配置 tenant: diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml index 45493d3e9..3e49ee6c9 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -23,6 +23,7 @@ ruoyi-common-oss ruoyi-common-ratelimiter ruoyi-common-redis + ruoyi-common-shortlink ruoyi-common-satoken ruoyi-common-security ruoyi-common-sms diff --git a/ruoyi-common/ruoyi-common-bom/pom.xml b/ruoyi-common/ruoyi-common-bom/pom.xml index 4a2dc1ceb..35514f768 100644 --- a/ruoyi-common/ruoyi-common-bom/pom.xml +++ b/ruoyi-common/ruoyi-common-bom/pom.xml @@ -96,6 +96,13 @@ ${revision} + + + org.dromara + ruoyi-common-shortlink + ${revision} + + org.dromara diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/GlobalConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/GlobalConstants.java index ae9bc2e62..10fe478be 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/GlobalConstants.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/GlobalConstants.java @@ -36,4 +36,10 @@ public interface GlobalConstants { * 三方认证 redis key */ String SOCIAL_AUTH_CODE_KEY = GLOBAL_REDIS_KEY + "social_auth_codes:"; + + /** + * 短链接 redis key + */ + String SHORT_LINK_KEY = GLOBAL_REDIS_KEY + "short_link:"; + } diff --git a/ruoyi-common/ruoyi-common-shortlink/pom.xml b/ruoyi-common/ruoyi-common-shortlink/pom.xml new file mode 100644 index 000000000..0cfac4e94 --- /dev/null +++ b/ruoyi-common/ruoyi-common-shortlink/pom.xml @@ -0,0 +1,37 @@ + + + + org.dromara + ruoyi-common + ${revision} + + 4.0.0 + + ruoyi-common-shortlink + + + ruoyi-common-shortlink 短链接模块 + + + + + + org.dromara + ruoyi-common-core + + + + org.dromara + ruoyi-common-redis + + + + org.dromara + ruoyi-common-mybatis + + + + + diff --git a/ruoyi-common/ruoyi-common-shortlink/src/main/java/org/dromara/common/shortlink/enums/ValidityType.java b/ruoyi-common/ruoyi-common-shortlink/src/main/java/org/dromara/common/shortlink/enums/ValidityType.java new file mode 100644 index 000000000..35eb45845 --- /dev/null +++ b/ruoyi-common/ruoyi-common-shortlink/src/main/java/org/dromara/common/shortlink/enums/ValidityType.java @@ -0,0 +1,50 @@ +package org.dromara.common.shortlink.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 有效期 + * + * @author AprilWind + */ +@Getter +@AllArgsConstructor +public enum ValidityType { + + /** + * 三分钟 + */ + THREE_MINUTES(3 * 60L), + + /** + * 一个小时 + */ + ONE_HOUR(60 * 60L), + + /** + * 表示一天(24小时) + */ + ONE_DAY(24 * 60 * 60L), + + /** + * 表示三天 + */ + THREE_DAYS(3 * 24 * 60 * 60L), + + /** + * 表示七天(一周) + */ + SEVEN_DAYS(7 * 24 * 60 * 60L), + + /** + * 表示三十天(一个月) + */ + THIRTY_DAYS(30 * 24 * 60 * 60L); + + /** + * 有效期(单位:秒) + */ + private final Long validityType; + +} diff --git a/ruoyi-common/ruoyi-common-shortlink/src/main/java/org/dromara/common/shortlink/properties/ShortLinkProperties.java b/ruoyi-common/ruoyi-common-shortlink/src/main/java/org/dromara/common/shortlink/properties/ShortLinkProperties.java new file mode 100644 index 000000000..12eae1576 --- /dev/null +++ b/ruoyi-common/ruoyi-common-shortlink/src/main/java/org/dromara/common/shortlink/properties/ShortLinkProperties.java @@ -0,0 +1,35 @@ +package org.dromara.common.shortlink.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * 短链接 配置属性 + * + * @author AprilWind + */ +@Data +@ConfigurationProperties(prefix = "shortlink") +public class ShortLinkProperties { + + /** + * 客户端使用租户域名生成链接 + */ + private Boolean enabled = false; + + /** + * 访问前缀 (开启时启用,可不填写,能访问到后端即可) + */ + private String api; + + /** + * 短链接主机名或域名 + */ + private String address; + + /** + * 错误页面的主机名或域名 + */ + private String errorAddress = "/error"; + +} diff --git a/ruoyi-common/ruoyi-common-shortlink/src/main/java/org/dromara/common/shortlink/utils/ShortLinkUtils.java b/ruoyi-common/ruoyi-common-shortlink/src/main/java/org/dromara/common/shortlink/utils/ShortLinkUtils.java new file mode 100644 index 000000000..a2ed362c4 --- /dev/null +++ b/ruoyi-common/ruoyi-common-shortlink/src/main/java/org/dromara/common/shortlink/utils/ShortLinkUtils.java @@ -0,0 +1,100 @@ +package org.dromara.common.shortlink.utils; + +import cn.hutool.core.lang.Validator; +import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.dromara.common.core.constant.GlobalConstants; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.redis.utils.RedisUtils; +import org.dromara.common.shortlink.enums.ValidityType; +import org.dromara.common.shortlink.properties.ShortLinkProperties; + +import java.time.Duration; + +/** + * 短链接生成工具类 + * + * @author AprilWind + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ShortLinkUtils { + + private static final IdentifierGenerator IDENTIFIER_GENERATOR = SpringUtils.getBean(IdentifierGenerator.class); + private static final ShortLinkProperties SHORT_LINK_PROPERTIES = SpringUtils.getBean(ShortLinkProperties.class); + + /** + * 根据默认主机名(或域名)生成完整短链接 + * + * @param longUrl 完整的长链接 + * @param validityType 有效期类型枚举 + * @return 带有默认主机名(或域名)的完整短链接 + */ + public static String generateShortUrl(String longUrl, ValidityType validityType) { + return generateShortUrl(SHORT_LINK_PROPERTIES.getAddress(), longUrl, validityType); + } + + /** + * 根据指定主机名(或域名)生成完整短链接 + * + * @param address 主机名(或域名) + * @param longUrl 完整的长链接 + * @param validityType 有效期类型枚举 + * @return 带有指定主机名(或域名)的完整短链接 + */ + public static String generateShortUrl(String address, String longUrl, ValidityType validityType) { + String url = generateShortLinkIdentifier(longUrl, validityType); + return address + url; + } + + /** + * 生成短链接标识符并将长链接存储到缓存中 + * + * @param longUrl 完整的长链接 + * @param validityType 有效期类型枚举 + * @return 生成的短链接标识符 + */ + public static String generateShortLinkIdentifier(String longUrl, ValidityType validityType) { + // 使用标识符生成器生成短链接标识符 + String shortLinkIdentifier = IDENTIFIER_GENERATOR.nextId(null).toString(); + return generateShortLinkIdentifier(shortLinkIdentifier, longUrl, validityType); + } + + /** + * 生成指定短链接标识符并将长链接存储到缓存中 + * + * @param shortLinkIdentifier 指定的短链接标识符 + * @param longUrl 完整的长链接 + * @param validityType 有效期类型枚举 + * @return 生成的指定短链接标识符 + */ + public static String generateShortLinkIdentifier(String shortLinkIdentifier, String longUrl, ValidityType validityType) { + Validator.validateUrl(longUrl, "请输入有效的url"); + RedisUtils.setCacheObject(GlobalConstants.SHORT_LINK_KEY + shortLinkIdentifier, longUrl, Duration.ofSeconds(validityType.getValidityType())); + return shortLinkIdentifier; + } + + /** + * 根据短链接获取对应的完整长链接,如果找不到长链接则返回错误链接 + * + * @param shortLinkIdentifier 短链接标识符 + * @return 对应的完整长链接,如果未找到则返回错误链接 + */ + public static String getLongLink(String shortLinkIdentifier) { + return getLongLink(shortLinkIdentifier, SHORT_LINK_PROPERTIES.getErrorAddress()); + } + + /** + * 根据短链接获取对应的完整长链接,如果找不到长链接则返回指定的错误链接 + * + * @param shortLinkIdentifier 短链接标识符 + * @param errUrl 错误链接 + * @return 对应的完整长链接,如果未找到则返回错误链接 + */ + public static String getLongLink(String shortLinkIdentifier, String errUrl) { + String longLink = RedisUtils.getCacheObject(GlobalConstants.SHORT_LINK_KEY + shortLinkIdentifier); + return StringUtils.isNotEmpty(longLink) ? longLink : errUrl; + } + +} diff --git a/ruoyi-common/ruoyi-common-shortlink/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-shortlink/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 000000000..aa57e7f93 --- /dev/null +++ b/ruoyi-common/ruoyi-common-shortlink/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +org.dromara.common.shortlink.properties.ShortLinkProperties diff --git a/ruoyi-modules/ruoyi-system/pom.xml b/ruoyi-modules/ruoyi-system/pom.xml index acf33ceb3..536ee63a6 100644 --- a/ruoyi-modules/ruoyi-system/pom.xml +++ b/ruoyi-modules/ruoyi-system/pom.xml @@ -43,6 +43,12 @@ ruoyi-common-oss + + + org.dromara + ruoyi-common-shortlink + + org.dromara ruoyi-common-log diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysShortLinkController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysShortLinkController.java new file mode 100644 index 000000000..3e16125d2 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysShortLinkController.java @@ -0,0 +1,60 @@ +package org.dromara.system.controller.system; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.constraints.NotBlank; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.shortlink.enums.ValidityType; +import org.dromara.common.shortlink.properties.ShortLinkProperties; +import org.dromara.common.shortlink.utils.ShortLinkUtils; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.view.RedirectView; + +import java.net.MalformedURLException; +import java.net.URL; + +/** + * 短链接 控制层 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resource/short") +public class SysShortLinkController { + + private final ShortLinkProperties shortLinkProperties; + + /** + * 根据短链接获取长链接并重定向 + * + * @param shortLinkIdentifier 短链接标识符 + * @return 重定向视图 + */ + @GetMapping("/link/{shortLinkIdentifier}") + public RedirectView getLongLink(@PathVariable("shortLinkIdentifier") String shortLinkIdentifier) { + String longLink = ShortLinkUtils.getLongLink(shortLinkIdentifier, shortLinkProperties.getErrorAddress()); + return new RedirectView(longLink); + } + + /** + * 生成短链接并返回 + * + * @param longUrl 长链接 + * @return 完整的短链接 + */ + @PostMapping("/shorturl") + public R generateShortUrl(HttpServletRequest request, @NotBlank(message = "参数不能为空") String longUrl) throws MalformedURLException { + String shorturl; + if (Boolean.FALSE.equals(shortLinkProperties.getEnabled())) { + shorturl = ShortLinkUtils.generateShortUrl(shortLinkProperties.getAddress(), longUrl, ValidityType.THREE_DAYS); + } else { + String requestUrl = request.getRequestURL().toString().replace("shorturl", "link/"); + String host = new URL(requestUrl).getHost(); + String address = requestUrl.replace(host, host + shortLinkProperties.getApi()); + shorturl = ShortLinkUtils.generateShortUrl(address, longUrl, ValidityType.THREE_DAYS); + } + return R.ok(shorturl); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java index 1df221c32..415cbc4e8 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java @@ -22,6 +22,8 @@ import org.dromara.common.oss.core.OssClient; import org.dromara.common.oss.entity.UploadResult; import org.dromara.common.oss.enumd.AccessPolicyType; import org.dromara.common.oss.factory.OssFactory; +import org.dromara.common.shortlink.enums.ValidityType; +import org.dromara.common.shortlink.utils.ShortLinkUtils; import org.dromara.system.domain.SysOss; import org.dromara.system.domain.bo.SysOssBo; import org.dromara.system.domain.vo.SysOssVo; @@ -238,16 +240,17 @@ public class SysOssServiceImpl implements ISysOssService, OssService { } /** - * 桶类型为 private 的URL 修改为临时URL时长为120s + * 桶类型为 private 的URL 修改为临时URL时长为180s * * @param oss OSS对象 * @return oss 匹配Url的OSS对象 */ private SysOssVo matchingUrl(SysOssVo oss) { OssClient storage = OssFactory.instance(oss.getService()); - // 仅修改桶类型为 private 的URL,临时URL时长为120s + // 仅修改桶类型为 private 的URL,临时URL时长为180s if (AccessPolicyType.PRIVATE == storage.getAccessPolicy()) { - oss.setUrl(storage.getPrivateUrl(oss.getFileName(), 120)); + String privateUrl = storage.getPrivateUrl(oss.getFileName(), 180); + oss.setUrl(ShortLinkUtils.generateShortUrl(privateUrl, ValidityType.THREE_MINUTES)); } return oss; }