1、IShortUrlService 接口类
public interface IShortUrlService {
/**
* 生成短链
*
* @param shortUrlRequest
* @return
*/
String shortUrlGenerator(ShortUrlRequest shortUrlRequest);
/**
* 根据短链获取完整链接
*
* @param shortUrl
* @return
*/
String getUrl(String shortUrl);
/**
* 增加短链点击量
*
* @param shortUrl
* @return
*/
Boolean addClickCount(String shortUrl);
}
2、ShortUrlServiceImpl 接口实现类
@DubboService
public class ShortUrlServiceImpl implements IShortUrlService {
private static final Logger log = LoggerFactory.getLogger(ShortUrlServiceImpl.class);
@Resource
private RedisTemplate<String, String> redisTemplate;
@Resource
private ShortUrlMapper shortUrlMapper;
@Resource
private SpringApplicationContext springApplicationContext;
/**
* 域名匹配正则表达式
*/
private static final Pattern DOMAIN_NAME_PATTERN = Pattern.compile("http(s)?://[\\w-]+[.\\w-]+(/)?");
@Override
public String shortUrlGenerator(ShortUrlRequest shortUrlRequest) {
String fullUrl = shortUrlRequest.getFullUrl();
if (ObjectUtils.isEmpty(fullUrl)) {
throw new BaseException(500, "链接不能为空");
}
String[] shortUrls = ShortUrlUtil.generator(fullUrl);
for (String shortUrl : shortUrls) {
// 判断短链是否已存在
String url = springApplicationContext.getBean(IShortUrlService.class).getUrl(shortUrl);
if (url != null) {
if (!url.equals(shortUrlRequest.getFullUrl())) {
continue;
}
return shortUrl;
}
// 保存长链接与短链接短对应关系
ShortUrl shortUrlEntity = new ShortUrl();
shortUrlEntity.setFullUrl(fullUrl);
shortUrlEntity.setShotCode(shortUrl);
shortUrlEntity.setExpirationDate(shortUrlRequest.getExpirationDate());
Matcher matcher = DOMAIN_NAME_PATTERN.matcher(fullUrl);
if (!matcher.find()) {
throw new BaseException(500, "链接解析失败");
}
String baseUrl = matcher.group();
shortUrlEntity.setBaseUrl(baseUrl);
shortUrlEntity.setSuffixUrl(fullUrl.substring(baseUrl.length() - 1));
shortUrlMapper.insert(shortUrlEntity);
return shortUrl;
}
throw new BaseException(500, "链接生成失败");
}
@Override
@Cacheable(cacheNames = "shortUrl", key = "#shortUrl", unless = "#result == null")
public String getUrl(String shortUrl) {
QueryWrapper<ShortUrl> shortUrlQw = new QueryWrapper<>();
shortUrlQw.eq("shot_code", shortUrl);
shortUrlQw.eq("del_flag", DelFlag.NORMAL);
ShortUrl url = shortUrlMapper.selectOne(shortUrlQw);
return url == null ? null : url.getFullUrl();
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean addClickCount(String shortUrl) {
QueryWrapper<ShortUrl> shortUrlQw = new QueryWrapper<>();
shortUrlQw.eq("shot_code", shortUrl);
shortUrlQw.eq("del_flag", DelFlag.NORMAL);
ShortUrl url = shortUrlMapper.selectOne(shortUrlQw);
if (url == null) {
return false;
}
url.setTotalClickCount(url.getTotalClickCount() + 1);
return shortUrlMapper.updateById(url) > 0;
}
@Scheduled(cron = "0 0 3 * * * ")
public void removeShortUrl() {
QueryWrapper<ShortUrl> queryWrapper = new QueryWrapper<>();
queryWrapper.le("expirationDate", DateUtil.getTime());
List<ShortUrl> shortUrls = shortUrlMapper.selectList(queryWrapper);
//删除缓存数据库
// for (ShortUrl shortUrl : shortUrls) {
// redisTemplate.delete(shortUrl.getShotCode());
// }
List<Long> shortUrlIds = shortUrls.stream().map(ShortUrl::getId).collect(Collectors.toList());
//删除mysql数据库
shortUrlMapper.deleteBatchIds(shortUrlIds);
// try {
// Thread.sleep(1000);
// //再次删除缓存数据库
// for (ShortUrl shortUrl : shortUrls) {
// redisTemplate.delete(shortUrl.getShotCode());
// }
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}
}
3、ShortUrlHandler 工具类
public class ShortUrlHandler implements Runnable{
private final IShortUrlService shortUrlService;
private final String shortUrl;
public ShortUrlHandler(IShortUrlService shortUrlService, String shortUrl) {
this.shortUrlService = shortUrlService;
this.shortUrl = shortUrl;
}
@Override
public void run() {
shortUrlService.addClickCount(shortUrl);
}
}
4、ShortUrlController 控制器类
@RestController
@RequestMapping("/api/shortUrl")
@Api(tags = {"C端-短链接"})
@RefreshScope
public class ShortUrlController extends BaseController {
@DubboReference
private IShortUrlService shortUrlService;
@Resource
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
//short-utl:
// prefix: http://test.gateway.yaotiao.net/coupon-provider/api/shortUrl/redirect/
@Value("${short-utl.prefix:https://gateway.jumituangou.com/coupon-provider/api/shortUrl/redirect/}")
private String prefixUrl;
@GetMapping("/generator")
@ApiOperation("生成短链")
public ResponseResult<String> shortUrlGenerator(ShortUrlRequest shortUrlRequest) {
shortUrlRequest.setExpirationDate(DateUtil.getDateAfterDay(new Date(), 60));
String shortUrl = prefixUrl + shortUrlService.shortUrlGenerator(shortUrlRequest);
return success(shortUrl);
}
@GetMapping("/redirect/{shortUrl}")
@ApiOperation("短链重定向")
@IgnoreUrlsAnnon("/api/shortUrl/") //白名单
public void redirect(@PathVariable String shortUrl, HttpServletResponse response) {
String url = shortUrlService.getUrl(shortUrl);
try {
response.sendRedirect(url);
} catch (IOException e) {
e.printStackTrace();
}
// 异步增加短链点击次数
ShortUrlHandler shortUrlHandler = new ShortUrlHandler(shortUrlService, shortUrl);
threadPoolTaskExecutor.execute(shortUrlHandler);
}
}
5、mysql 表 short_url;