短信短链及跳转设计

63 阅读2分钟

废话不多说,直接上代码,直接可用

/**
 * <p>
 * 短连接表
 * </p>
 *
 * @author system
 * @since 2024-09-11
 */
@Getter
@Setter
@Accessors(chain = true)
@TableName("sms_short_url")
public class SmsShortUrl extends Model<SmsShortUrl> {

    private static final long serialVersionUID = 1L;

    /**
     * 主键
     */
      @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    /**
     * 原始连接
     */
    @TableField("org_url")
    private String orgUrl;

    /**
     * 生成带参数的原始连接
     */
    @TableField("org_generate_url")
    private String orgGenerateUrl;

    /**
     * 短网址url
     */
    @TableField("short_url")
    private String shortUrl;

    /**
     * 短网址标记
     */
    @TableField("short_target")
    private String shortTarget;

    /**
     * 业务参数
     */
    @TableField("biz_param")
    private String bizParam;

    /**
     * 状态(0:无效,1:有效)
     */
    @TableField("status")
    private Integer status;

    /**
     * 到期时间
     */
    @TableField("invalid_time")
    private LocalDateTime invalidTime;

    /**
     * 创建时间
     */
    @TableField("create_time")
    private LocalDateTime createTime;

    /**
     * 更新时间
     */
    @TableField("update_time")
    private LocalDateTime updateTime;

    /**
     * 逻辑删除字段 0未删除 1已删除
     */
    @TableField("is_delete")
    @TableLogic
    private Integer isDelete;

    /**
     * 修改人
     */
    @TableField("update_user")
    private String updateUser;


    @Override
    public Serializable pkVal() {
        return this.id;
    }

}

/**
 * <p>
 * 短连接表 服务类
 * </p>
 *
 * @author system
 * @since 2024-09-11
 */
public interface SmsShortUrlService extends IService<SmsShortUrl> {

    /**
     * 生成短链接
     * @param shortUrlReq
     * @return
     */
    String getShortUrl(ShortUrlReq shortUrlReq);

    /**
     * 根据短网址解析原网址
     */
    ShortUrlVO getByShortUrl(String shortUrl);

    /**
     * 根据短网址target标记解析原网址
     */
    ShortUrlVO getByTarget(String shortTarget);
}


/**
 * <p>
 * 短连接表 服务实现类
 * </p>
 *
 * @author system
 * @since 2024-09-11
 */
@Slf4j
@Service
public class SmsShortUrlServiceImpl extends ServiceImpl<SmsShortUrlMapper, SmsShortUrl> implements SmsShortUrlService {

    @Resource
    private RedisCommand redisCommand;

    @Value("${shortUrl.server:t.eleshell.com}")
    private String shortUrlServer;

    private final static String chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

    @Override
    public String getShortUrl(ShortUrlReq shortUrlReq) {
        String orgUrl = shortUrlReq.getOrgUrl();
        String newOrgUrl = UrlUtils.formatUrl(shortUrlReq.getOrgUrl(), shortUrlReq.getBizParam());
        Long id = IdGeneratorUtil.nextId();
        String target = toShortTarget(id);

        SmsShortUrl smsShortUrl = new SmsShortUrl();
        smsShortUrl.setId(id);
        smsShortUrl.setOrgUrl(orgUrl);
        smsShortUrl.setOrgGenerateUrl(newOrgUrl);
        smsShortUrl.setShortUrl(shortUrlServer + "/" + target);
        smsShortUrl.setShortTarget(target);
        smsShortUrl.setBizParam(shortUrlReq.getBizParam());
        save(smsShortUrl);

        String key = RedisKey.SHORT_URL + target;
        redisCommand.set(key, JSON.toJSONString(smsShortUrl), 1, TimeUnit.DAYS);
        return target;
    }

    @Override
    public ShortUrlVO getByShortUrl(String shortUrl) {
        if (!shortUrl.contains(shortUrlServer)) {
            throw new BusinessException("短链接不合法");
        }

        int lastSlashIndex = shortUrl.lastIndexOf('/');
        if (lastSlashIndex == -1) {
            return null;
        }
        return getByTarget(shortUrl.substring(lastSlashIndex + 1));
    }

    @Override
    public ShortUrlVO getByTarget(String shortTarget) {
        String srcUrl = redisCommand.get(RedisKey.SHORT_URL + shortTarget);
        if (!StringUtils.isEmpty(srcUrl)) {
            return JSON.parseObject(srcUrl, ShortUrlVO.class);
        }
        SmsShortUrl smsShortUrl = getOne(Wrappers.lambdaQuery(SmsShortUrl.class)
                .eq(SmsShortUrl::getShortTarget, shortTarget)
                .last(" limit 1"));
        if (!ObjectUtil.isNull(smsShortUrl)) {
            return BeanUtil.toBean(smsShortUrl, ShortUrlVO.class);
        }
        return null;
    }

    public String toShortTarget(long number) {
        // 将数字转换为字符串以进行哈希
        String input = Long.toString(number);

        // 使用MD5哈希(可以选择SHA-256或其他算法)
        String hash = SecureUtil.md5(input);

        // 从哈希结果中取前8位字符
        return hash.substring(0, 8);
    }
}

/**
 * <p>
 * 短连接表 前端控制器
 * </p>
 *
 * @author system
 * @since 2024-09-11
 */
@Slf4j
@RestController
@RequestMapping("/shortUrl")
@RequiredArgsConstructor
public class SmsShortUrlController {

    private final SmsShortUrlService smsShortUrlService;

    @ApiOperation(value = "生成短链接")
    @PostMapping(value = "/getShortUrl")
    public ResultData<String> getShortUrl(@RequestBody @Validated ShortUrlReq shortUrlReq) {
        return ResultData.ok(smsShortUrlService.getShortUrl(shortUrlReq));
    }

    @ApiOperation(value = "根据短网址解析原网址")
    @GetMapping(value = "/getByShortUrl")
    public ResultData<ShortUrlVO> getByShortUrl(@RequestParam("shortUrl") String shortUrl) {
        return ResultData.ok(smsShortUrlService.getByShortUrl(shortUrl));
    }

    @ApiOperation(value = "通过生成后的target标记解析原网址")
    @GetMapping(value = "/getByTarget")
    public ResultData<ShortUrlVO> getByTarget(@RequestParam("shortTarget") String shortTarget) {
        return ResultData.ok(smsShortUrlService.getByTarget(shortTarget));
    }

}

@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("/")
@Api(tags = "重定向链接")
public class RedirectController {

    private final SmsShortUrlService smsShortUrlService;

    @ApiOperation(value = "跳转链接")
    @GetMapping(value = "/{target}")
    public ResultData<Void> linkTo(@PathVariable("target") String target, HttpServletResponse response) {
        try {
            ShortUrlVO shortUrlVO = smsShortUrlService.getByTarget(target);
            if(ObjectUtil.isNull(shortUrlVO) || StrUtil.isEmpty(shortUrlVO.getOrgUrl())) {
                return ResultData.ok();
            }
            String encodeRedirectURL = response.encodeRedirectURL(shortUrlVO.getOrgUrl());
            log.info("#linkTo encodeRedirectURL:{}", encodeRedirectURL);
            response.sendRedirect(encodeRedirectURL);
        } catch (IOException e) {
            log.error("#linkTo redirect error:", e);
        }
        return ResultData.ok();
    }
}