短链接管理CRUD
# 长连接压缩算法
使用26个字母 + 10个数字; 6位组合 568亿
将原始链接经过Hash函数,生成10进制,再转换为62进制;
为什么是62进制?
62进制转换后只有 大小写字母+数字;64进制会含有 / + 等字符;
10进制转62进制会 缩短字符;
短链接重复会怎么样?
不同的域名下是可以重复的,对full_shor_url 和 del_time 作唯一索引;如果插入抛出异常;
如果再重复,就要做数据清楚策略;
# 新增短链接
接口:
@PostMapping("/api/short-link/v1/create")
public Result<ShortLinkCreateRespDTO> createShortLink(@RequestBody ShortLinkCreateReqDTO requestParam) {
return Results.success(shortLinkService.createShortLink(requestParam));
}
1
2
3
4
2
3
4
@Slf4j
@Service
@RequiredArgsConstructor
public class ShortLinkServiceImpl extends ServiceImpl<ShortLinkMapper, ShortLinkDO> implements ShortLinkService {
private final RBloomFilter<String> shortUriCreateCachePenetrationBloomFilter;
@Override
public ShortLinkCreateRespDTO createShortLink(ShortLinkCreateReqDTO requestParam) {
String shortLinkSuffix = generateSuffix(requestParam);
String fullShortUrl = StrBuilder.create(requestParam.getDomain())
.append("/")
.append(shortLinkSuffix)
.toString();
ShortLinkDO shortLinkDO = ShortLinkDO.builder()
.domain(requestParam.getDomain())
.originUrl(requestParam.getOriginUrl())
.gid(requestParam.getGid())
.createdType(requestParam.getCreatedType())
.validDateType(requestParam.getValidDateType())
.validDate(requestParam.getValidDate())
.describe(requestParam.getDescribe())
.shortUri(shortLinkSuffix)
.enableStatus(0)
.fullShortUrl(fullShortUrl)
.build();
try {
baseMapper.insert(shortLinkDO);
} catch (DuplicateKeyException ex) {
// 两个请求生成了同一个短链接
LambdaQueryWrapper<ShortLinkDO> queryWrapper = Wrappers.lambdaQuery(ShortLinkDO.class)
.eq(ShortLinkDO::getFullShortUrl, fullShortUrl);
ShortLinkDO hasShortLinkDO = baseMapper.selectOne(queryWrapper);
if (hasShortLinkDO != null) {
log.warn("短链接:{} 重复入库", fullShortUrl);
throw new ServiceException("短链接生成重复");
}
}
shortUriCreateCachePenetrationBloomFilter.add(fullShortUrl);
return ShortLinkCreateRespDTO.builder()
.fullShortUrl("http://" + shortLinkDO.getFullShortUrl())
.originUrl(requestParam.getOriginUrl())
.gid(requestParam.getGid())
.build();
}
private String generateSuffix(ShortLinkCreateReqDTO requestParam) {
int customGenerateCount = 0;
String shorUri;
// 解决哈希冲突
while (true) {
if (customGenerateCount > 10) {
throw new ServiceException("短链接频繁生成,请稍后再试");
}
String originUrl = requestParam.getOriginUrl();
originUrl += System.currentTimeMillis();
shorUri = HashUtil.hashToBase62(originUrl);
if (!shortUriCreateCachePenetrationBloomFilter.contains(createShortLinkDefaultDomain + "/" + shorUri)) {
break;
}
customGenerateCount++;
}
return shorUri;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
接口测试:
# 分页查询
配置类:
@Configuration
public class DataBaseConfiguration {
/**
* 分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
接口:
@GetMapping("/api/short-link/v1/page")
public Result<IPage<ShortLinkPageRespDTO>> pageShortLink(ShortLinkPageReqDTO requestParam) {
return Results.success(shortLinkService.pageShortLink(requestParam));
}
1
2
3
4
2
3
4
@Override
public IPage<ShortLinkPageRespDTO> pageShortLink(ShortLinkPageReqDTO requestParam) {
LambdaQueryWrapper<ShortLinkDO> queryWrapper = Wrappers.lambdaQuery(ShortLinkDO.class)
.eq(ShortLinkDO::getGid, requestParam.getGid())
.eq(ShortLinkDO::getEnableStatus, 0)
.eq(ShortLinkDO::getDelFlag, 0);
IPage<ShortLinkDO> resultPage = baseMapper.selectPage(requestParam,queryWrapper);
return resultPage.convert(each->BeanUtil.toBean(each,ShortLinkPageRespDTO.class));
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
测试: