java微服务实战项目股票项目搭建,后台用户和股票行情设计功能

最近在第三方测试上浪费太多时间,主要想不间断获取实时数据,本以为完成不了没想到还真测试出来了。

第三方股票接口

接下来主要做对接第三方接口,可以用聚合接口,每天有免费次数,也可以使用新浪接口或者东方接口,最近在测试新浪和东方的接口上发现,新浪行情使用的websocket方式进行数据推送,对接下来接受的数据无法乱码目前没有解决方案。在新浪上面浪费太多时间,最终也没解决,


java微服务实战项目股票项目搭建,后台用户和股票行情设计功能

然后又试了试东方,东方使用的长连接推送数据,这个测试下来是可以获取行情并且因为长连接所以也是可以实时获取数据


java微服务实战项目股票项目搭建,后台用户和股票行情设计功能


后台用户权限管理功能

  • 后台创建用户接口, 密码重置接口, 和产品和分类管理相关接口。
  • 掌握Spring Cache使用、Spring Sesssion Redis 分布式会话的集成使用。

先创建用户实体

java微服务实战项目股票项目搭建,后台用户和股票行情设计功能

 @PostMapping(value = "/addUser")
    @ApiOperation(value = "新建用户接口")
    public ApiRespResult addUser(@Valid AuthorityUser user) {

        ApiRespResult  result = null;
        try {
            AuthorityUser loginUser = getUser();
            // 用户登陆处理
            authorityUserService.addUser(user, loginUser);
            result = ApiRespResult.success();
        }catch(ComponentException e) {
            log.error(e.getMessage(), e);
            result = ApiRespResult.error(e.getErrorCodeEnum());
        }catch(Exception e) {
            log.error(e.getMessage(), e);
            result = ApiRespResult.sysError(e.getMessage());
        }
        return result;
    }
 @PostMapping(value = "/login")
    @ApiOperation(value = "用户登陆接口")
    @ApiImplicitParams({
            @ApiImplicitParam(value = "用户账号", name = "userAccount", dataType = "string", required = true),
            @ApiImplicitParam(value = "用户密码", name = "password", dataType = "string", required = true)
    })
    public ApiRespResult login(@Size(min = 1, max = 32,message = "账号长度必须为1到32")
                               @RequestParam("userAccount") String userAccount,
                               @Size(min = 1, max = 32,message = "密码长度必须为1到32")
                               @RequestParam("password")String password) {

        ApiRespResult  result = null;
        try {
            // 用户登陆处理
            AuthorityUser authorityUser = authorityUserService.userLogin(userAccount, password, getClientIP());
            // 保存用户登陆信息至session
            getSession().setAttribute(getSession().getId(), authorityUser);
            result = ApiRespResult.success();
        }catch(Exception e) {
            log.error(e.getMessage(), e);
            result = ApiRespResult.sysError(e.getMessage());
        }
        return result;
    }

1.2 Spring Session Redis实现分布式会话

微服务群会存在多个节点,如果采用随机的负载策略, 那如何确保用户的会话数据不会丢失?

这里我们通过Spring Session Redis技术方案, 基于统一缓存方式, 实现分布式会话管理。

pom.xml添加



    org.springframework.session
    spring-session-data-redis

配置项添加

# Spring 分布式session
spring:
  session:
    store-type: redis

增加注解:

在启动类中添加

@EnableRedisHttpSession
public class StockAdminUserApplication {
    ...
}

项目使用:

把以上配置好, 就和我们正常使用session一样, 这里把一些常用的session操作作了封装,

放在BaseController里面:

public class BaseController {

    /**
     * 根据账号获取用户对象
     * @return
     */
    protected AuthorityUser getUser() throws ComponentException {

        HttpSession session = getSession();
        AuthorityUser authorityUser = (AuthorityUser)session.getAttribute(session.getId());
        if(null == authorityUser) {
            // 用户不存在, 抛出异常
            throw new ComponentException(ApplicationErrorCodeEnum.ADMIN_USER_NEED_LOGIN);
        }
        return authorityUser;
    }
​
    /**
     * 获取session会话
     * @return
     */
    protected HttpSession getSession() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest().getSession();
    }
    ...
}

后台用户登陆接口实现

  1. 用户登录接口业务层实现
 @Override
    public AuthorityUser userLogin(String userAccount, String password, String clientIp) throws ComponentException {
        //查找用户
        Optional authorityUserOptional = authorityUserRepository.findOne(eqUserName(userAccount));
        if(!authorityUserOptional.isPresent()){
            throw new ComponentException(ApplicationErrorCodeEnum.ADMIN_USER_ACCOUNT_EXISTS);
        }
        //用户校验
        AuthorityUser authorityUser = authorityUserOptional.get();
        String encryptPassword = EncryptUtil.encryptSigned(password);
        if(!authorityUser.getUserPassword().equals(encryptPassword)){
            throw new ComponentException(ApplicationErrorCodeEnum.ADMIN_USER_ACCOUNT_PWD_ERROR);
        }
        //记录用户登录信息
        authorityUser.setLastLoginTime(new Date());
        authorityUser.setLastLoginIp(clientIp);
        authorityUserRepository.save(authorityUser);
        //用户的角色权限,并且拿最大角色权限
        List roleList = authorityUserRoleRepository.findAllByUserId(authorityUser.getId());
        if(roleList==null || roleList.size()==0){
            throw new ComponentException(ApplicationErrorCodeEnum.ADMIN_USER_NOT_ASSIGN_ROLES);
        }
        Long roleId = getPriortyAuthorityUserRole(roleList).getRoleId();
        authorityUser.setRoleId(roleId);
        //获取用户的角色菜单操作信息,存入缓存中
        List optList = authorityMenuOperationRepository.findByRoleId(roleId);
        if(null != optList) {
            // 根据接口的请求类型, 做分组汇总处理, 转换为MAP结构, 格式: POST-{/add, /update}
            Map> optUrlSets = optList.stream().collect(Collectors.groupingBy(AuthorityMenuOperation::getOperationIco,
                    Collectors.mapping(AuthorityMenuOperation::getOperationUri, Collectors.toSet())));
            if(optUrlSets.size() > 0) {
                // 将MAP的菜单操作信息存入至Redis缓存当中, 格式: key=userId + | + optType, value=Set...
                Cache cache = cacheManager.getCache(GlobalConstants.AUTHORITY_MENU_KEY);
                optUrlSets.forEach((opt, urls) -> {
                    // 对uri集合做遍历处理, 将权限菜单信息按指定格式存入至Redis当中
                    // 注意: set里面的数据, 是可以支持多个uri记录, 需拆分处理转换至Set集合当中
                    Set levelUrls = urls.stream().flatMap(str ->
                            Arrays.stream(str.split(",")).map(String::trim)).collect(Collectors.toSet());
                    cache.put(authorityUser.getId() + GlobalConstants.SPLIT + opt, levelUrls);
                });
            }
        }
        return authorityUser;
    }

用户登陆接口看似简单, 但里面还是要处理较多逻辑, 来分析下:

  1. 先根据传递的账号来查找用户是否存在, 不存在则抛出异常。
  2. 密码在数据库是密文, 加密判断密码是否正确, 错误则抛出异常。
  3. 记录用户的登陆信息,比如登陆时间与登陆IP。
  4. 保存菜单权限信息至缓存中。为什么放缓存? 因为要作全局菜单权限判断, 第一服务无状态化、第二提升处理性能。
  5. 菜单与操作是分开存储, 要把这两部分数据的URI统一记录下来, 通过Set集合做了去重处理, 采用Spring Cache + Redis 存储数据。

其余接口就不一一整理了。


全局权限过滤器实现

后台用户管理系统, 涉及众多菜单与操作, 并不是每个用户都可以请求访问, 而是根据用户角色访问相应其对应的菜单, 这就需要通过全局拦截器来作校验判断。


java微服务实战项目股票项目搭建,后台用户和股票行情设计功能

行情服务的功能设计

理解专业生产级行情服务的整体功能设计

掌握PostgreSQL的安装与配置(服务器目前有用,暂时还继续使用mysql存储)


java微服务实战项目股票项目搭建,后台用户和股票行情设计功能


数据库结构设计


java微服务实战项目股票项目搭建,后台用户和股票行情设计功能

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章