短链接day4

短链接管理

创建短链接数据库表

URI、URL和URN区别 :

URI 指的是一个资源 ;URL 用地址定位一个资源; URN 用名称定位一个资源。

举个例子: 去寻找一个具体的人(URI);如果用地址:XX省XX市XX区...XX单元XX室的主人 就是URL;如果用身份证号+名字去找就是URN(身份证号+名字 无法确认资源的地址) 。 在Java类库中,URI类不包含任何访问资源的方法,只能标识资源。URL类可以访问资源,可以获取指定资源的流信息。

新增短链接

由于describe在java中属于关键字,所以在实体对象中,对于describe属性应当:

记住是加了反引号的!

    /**
    * 描述
    */
    @TableField("`describe`")
    private String describe;

短链接区分大小写:将utf8m64改成utf8,相应的配置也改成utf8 bin。

将长连接hash模到短链接一定会存在冲突的问题,怎么解决?

重置。为了防止死循环,需要设置一个最大的重置次数。

由于海量数据,并且为了防止多次查询数据库,需要去查缓存,所以需要用分布式锁,从而这个用布隆过滤器实现(在用户注册时,同样使用了)。

private final RBloomFilter<String> shortUriCreateCachePenetrationBloomFilter;
    /**
     * 创建短链接
     *
     * @param requestParam
     * @return
     */
    @Override
    public ShortLinkCreateRespDTO createShortLink(ShortLinkCreateReqDTO requestParam) {
        String shortLinkSuffix=generateSuffix(requestParam);
        String fullShortUrl=requestParam.getDomain()+"/"+shortLinkSuffix;
        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{
            //数据库如果存在,则会报错,进入catch
            baseMapper.insert(shortLinkDO);
        }catch (DuplicateKeyException exp){
            //TODO 已经误判的短链接如何处理
            //第一种,短链接确实真实存在缓存中
            //第二种,短链接不一定存在缓存中

            //检查是否存在于数据库中,如果没存在,则说明布隆过滤器误判了。
            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(shortLinkDO.getFullShortUrl())
                .originUrl(requestParam.getOriginUrl())
                .gid(requestParam.getGid())
                .build();
    }

    /**
     * 获取短链接的后缀
     * @param requestParam
     * @return
     */
    private String generateSuffix(ShortLinkCreateReqDTO requestParam){
        int customGenerateCount=0;
        String shortUri;
        String originUrl = requestParam.getOriginUrl();
        while(true){
            if(customGenerateCount>10){
                throw new ServiceException("短链接频繁生成,请稍后再试");
            }
            //减小当前冲突的可能
            originUrl+=System.currentTimeMillis();

            shortUri=HashUtil.hashToBase62(originUrl);
            if(!shortUriCreateCachePenetrationBloomFilter.contains(requestParam.getDomain()+"/"+shortUri)){
                break;
            }
            customGenerateCount++;
        }
        return shortUri;
    }

 开发用户登录验证拦截器返回友好提示信息:

@RequiredArgsConstructor
public class UserTransmitFilter implements Filter {
    private final StringRedisTemplate stringRedisTemplate;
    private static final List<String> IGNORE_URI= Lists.newArrayList(
            "/api/short-link/admin/v1/user/login",
            "/api/short-link/admin/v1/user/has-username"
    );
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        String requestURI=httpServletRequest.getRequestURI();
        if(!IGNORE_URI.contains(requestURI)){
            String method=httpServletRequest.getMethod();
            if(!(Objects.equals(requestURI,"/api/short-link/admin/v1/user")&&Objects.equals(method,"POST"))){
                String username = httpServletRequest.getHeader("username");
                String token = httpServletRequest.getHeader("token");
                if (!StrUtil.isAllNotBlank(username,token)){
                    returnJson(servletResponse,JSON.toJSONString(Results.failure(new ClientException(USER_TOKEN_FAIL))));
                    return;
                }
                Object userInfoJsonStr = null;
                try{
                    userInfoJsonStr=stringRedisTemplate.opsForHash().get("login_" + username, token);
                    if(userInfoJsonStr==null){
                        throw new ClientException(USER_TOKEN_FAIL);
                    }
                }catch (Exception exp){
                    returnJson(servletResponse,JSON.toJSONString(Results.failure(new ClientException(USER_TOKEN_FAIL))));
                    return;
                }
                UserInfoDTO userInfoDTO = JSON.parseObject(userInfoJsonStr.toString(), UserInfoDTO.class);
                UserContext.setUser(userInfoDTO);
            }
        }
        try {
        filterChain.doFilter(servletRequest, servletResponse);
        } finally {
            UserContext.removeUser();
        }
    }
    private void returnJson(ServletResponse response, String json) {
        PrintWriter writer = null;
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        try {
            writer = response.getWriter();
            writer.print(json);
        } catch (IOException e) {
        } finally {
            if (writer != null)
                writer.close();
        }
    }

}

打包工具,在admin和project的pom中添加,用于联调前端,这个打包插件会让打包有一个直接可以启动的spring文件。 

<build>
        <finalName>s{project.artifactId</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

分页查询短链接列表

    /**
     * 分页查询短链接
     *
     * @param requestParam
     * @return
     */
    @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(eatch-> BeanUtil.toBean(eatch,ShortLinkPageRespDTO.class));
    }

请求的param有三个:gid、current(当前页)、size(每页数量)

由于返回响应中total有问题,这需要添加一个MySQL数据库分页插件:

@Configuration
public class DataBaseConfiguration {
    /**
     * 分页插件
     * @return
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值