今日指数-day08实战完整代码

今日指数-day08

1. 个股最新分时行情数据

1.1 个股最新分时行情功能说明

1)个股最新分时行情功能原型

在这里插入图片描述

2)个股最新分时行情数据接口分析
功能描述:
	获取个股最新分时行情数据,主要包含:
	开盘价、前收盘价、最新价、最高价、最低价、成交金额和成交量、交易时间信息; 
服务路径:/api/quot/stock/screen/second/detail
服务方法:GET
请求参数:code //股票编码

响应数据格式:

R<StockRt>

{
    "code": 1,
    "data": {
        "tradeAmt": 58672751,//最新交易量
        "preClosePrice": 3.89,//前收盘价格
        "lowPrice": 3.89,//最低价
        "highPrice": 3.91,//最高价
        "openPrice": 3.9,//开盘价
        "tradeVol": 228625157,//交易金额
        "tradePrice": 3.9//当前价格
        "curDate": '2022-01-03 14:58'//当前日期
    }
}
2)注意事项
如果当前日期不在股票交易时间内,则查询最近的股票交易时间的数据回显

代码实现

1. 表现层
/**
     * 个股最新分时行情数据
     */
    @ApiOperation(value = "个股最新分时行情数据", notes = "个股最新分时行情数据", httpMethod = "GET")
    @GetMapping("/stock/screen/second/detail")
    public R<MarketDetialDomain> getMarketDetial(@RequestParam(name = "code" , required = true) String code){
        return service.getMarketDetial(code);
    }
2. 服务层
R<MarketDetialDomain> getMarketDetial(String code);
    @Override
    public R<MarketDetialDomain> getMarketDetial(String code) {
        // 获取最新时间
        DateTime curDate = DateTimeUtil.getLastDate4Stock(DateTime.now());
        // 制造mock数据
        curDate = DateTime.parse("2022-07-07 14:55:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date date = curDate.toDate();

        // 查询数据
        MarketDetialDomain msg = stockRtInfoMapper.getMarketDetial(date , code);

        // 封装数据
        return R.ok(msg);


    }
3. Dao层
    MarketDetialDomain getMarketDetial(@Param("date") Date date, @Param("code") String code);
<select id="getMarketDetial" resultType="com.jixu.stock.pojo.domain.MarketDetialDomain">
        select trade_amount as tradeAmt,
               pre_close_price as preClosePrice,
               min_price as lowPrice,
               max_price as highPrice,
               open_price as openPrice,
               trade_volume as tradeVol,
               cur_price as tradePrice,
               date_format(cur_time,'%Y%m%d%H%i') as curDate

        from stock_rt_info where stock_code = #{code}
                             and cur_time = #{date}
    </select>

2.个股实时交易流水查询

2.1 个股实时交易流水查询功能介绍

1)功能原型

在这里插入图片描述

2)功能接口说明
功能描述:个股交易流水行情数据查询--查询最新交易流水,按照交易时间降序取前10
服务路径:/quot/stock/screen/second
入参:code  股票编码
服务方法:GET

响应数据格式:

{
    "code": 1,
    "data": [
        {
            "date": "2022-01-03-14:58",//当前时间,精确到分
            "tradeAmt": 58672751,//交易量
            "tradeVol": 228625157,//交易金额
            "tradePrice": 3.9//交易价格
        }
    ]
}

代码实现

1. 表现层
    /**
     * 个股实时交易流水查询
     */
    @ApiOperation(value = "个股实时交易流水查询", notes = "个股实时交易流水查询", httpMethod = "GET")
    @GetMapping("/stock/screen/second")
    public R<List<Map<String,Object>>> getMarketBill(@RequestParam(name = "code" , required = true) String code){
        return service.getMarketBill(code);
2. 服务层
R<List<Map<String, Object>>> getMarketBill(@Param("code") String code);
    @Override
    public R<List<Map<String, Object>>> getMarketBill(String code) {
        // 获取最新时间
        DateTime curDate = DateTimeUtil.getLastDate4Stock(DateTime.now());
        // 创建一个格式化器来将 DateTime 转换为字符串
        DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd");
        String s = formatter.print(curDate);
        curDate = DateTime.parse(s, DateTimeFormat.forPattern("yyyy-MM-dd"));
        // 制造mock数据
        curDate = DateTime.parse("2022-07-07", DateTimeFormat.forPattern("yyyy-MM-dd"));

        Date date = curDate.toDate();

        List<Map<String, Object>> msg = stockRtInfoMapper.getMarketBill(code,date);

        return R.ok(msg);

    }
3. Dao层
List<Map<String, Object>> getMarketBill(@Param("code") String code , @Param("date") Date date);
    <select id="getMarketBill" resultType="java.util.Map">
        select date_format(cur_time, '%Y%m%d%H%i') as date,
       trade_amount                        as tradeAmt,
       trade_volume                        as tradeVol,
       cur_price                           as tradePrice
        from stock_rt_info
        where DATE(cur_time) = #{date}
          and stock_code = #{code}
        order by cur_time desc
            limit 20;
    </select>

3.拉取外盘数据功能实现

3.1功能分析

国外大盘数据采集与国内大盘数据几乎一致,目前通过sina接口无法获取国外大盘的交易量和交易金额数据,所以针对国外大盘数据,需要单独处理;

注意事项:

​ 国外大盘数据接口不提供交易量和交易金额的信息;

字段分析:

  var hq_str_b_FSSTI="富时新加坡海峡时报指数,3123.68,-2.96,-0.09";
			大盘code      大盘名称       大盘点数    涨跌值    涨幅

注意:因为外盘的开盘周期不固定的,所以我们就一天仅仅采集一次数据即可;

​ 或者针对不同的外盘,使用不同的采集计划!

采集外盘接口:http://hq.sinajs.cn/list=int_dji,int_nasdaq,int_hangseng,int_nikkei,b_FSSTI,其它详见第五天接口说明;

/**
     * 外盘数据采集
     */
    void getOutMarket();
    /**
     * 外盘数据采集
     */
    @Override
    public void getOutMarket() {
        String url = stockInfoConfig.getOutMarket() + String.join("," , stockInfoConfig.getOuter());
        ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
        int statusCodeValue = exchange.getStatusCodeValue();
        if (statusCodeValue != 200) {
            log.error("当前时间点{} , 数据采集失败 , 状态码{}", DateTime.now().toString("yyyy-MM-dd HH-mm-ss"), statusCodeValue);
            return;
        }
        String jsData = exchange.getBody();
        List<StockOuterMarketIndexInfo> list = parserStockInfoUtil.parser4StockOrMarketInfo(jsData, ParseType.OUTER);
        log.info("采集的当前大盘数据:{}", list);

        int num = stockOuterMarketIndexInfoMapper.insertOutMarketData(list);
        if (num > 0) {
            log.info("当前时间点{} , 数据插入成功", DateTime.now().toString("yyyy-MM-dd HH-mm-ss"));
            rabbitTemplate.convertAndSend("stockExchange","inner.market",new Date());
        } else {
            log.error("当前时间点{} , 数据插入失败", DateTime.now().toString("yyyy-MM-dd HH-mm-ss"));
        }


    }

mapper层

int insertOutMarketData(@Param("list") List<StockOuterMarketIndexInfo> list);
<insert id="insertOutMarketData">
        insert into stock_outer_market_index_info
        ( id,market_code,market_name
        ,cur_point,updown,rose
        ,cur_time)
        values
            <foreach collection="list" item="data" separator=",">
                (#{data.id,jdbcType=BIGINT},#{data.marketCode,jdbcType=CHAR},#{data.marketName,jdbcType=VARCHAR}
                ,#{data.curPoint,jdbcType=DECIMAL},#{data.updown,jdbcType=DECIMAL},#{data.rose,jdbcType=DECIMAL}
                ,#{data.curTime,jdbcType=TIMESTAMP})
            </foreach>
    </insert>

4.完善用户登录成功动态回显菜单栏功能

1)功能接口说明

功能描述:当前用户登录后,仅仅加载了用户表相关信息,接下来完成的功能是完善用户权限相关的信息;
服务路径:/api/login
请求方式:POST
注意事项:顶级权限(功能菜单项)的pid为0

在这里插入图片描述

对应表表结构:

sys_permissioin表:

在这里插入图片描述

接口响应数据格式:

{
    "code": 1,
    "data": {
        "id": "1237361915165020161",//用户ID
        "username": "admin",//用户名称
        "phone": "13888888888",//手机号
        "nickName": "itheima",//昵称
        "realName": "heima",//真实名称
        "sex": 1,//性别
        "status": 1,//状态
        "email": "875267425@qq.com",//邮件
        "menus": [//侧边栏权限树(不包含按钮权限)
            {
                "id": "1236916745927790564",//权限ID
                "title": "组织管理",//权限标题
                "icon": "el-icon-star-off",//权限图标(按钮权限无图片)
                "path": "/org",//请求地址
                "name": "org",//权限名称对应前端vue组件名称
                "children": [
                    {
                        "id": "1236916745927790578",
                        "title": "角色管理",
                        "icon": "el-icon-s-promotion",
                        "path": "/roles",
                        "name": "roles",
                        "children": [] // null 则前端展示失败
                    },
                    {
                        "id": "1236916745927790560",
                        "title": "菜单权限管理",
                        "icon": "el-icon-s-tools",
                        "path": "/menus",
                        "name": "menus",
                        "children": [] 
                    }
                ]
            },
            {
                "id": "1236916745927790569",
                "title": "账号管理",
                "icon": "el-icon-s-data",
                "path": "/user",
                "name": "user",
                "children": []
            }
        ],
      permissions: ["btn-user-delete",//按钮权限标识
                    "btn-log-delete",
                    "btn-user-add",
                    "btn-role-update",
                    "btn-permission-delete",]
    }
}

实现步骤:

1.根据用户名已经查询了用户信息,且做了用户信息合法性的判断;

2.如果 用户合法,则根据用户的id去数据库查询用户拥有的权限信息集合;

​ 根据用户id查询权限信息,数据要注意去重(distinct)

3.递归用户拥有的权限集合,组织出用户的目录-菜单树(不包含按钮权限)—》menus

3.获取按钮权限标识集合(获取权限集合中type=3的权限信息)—》permissions

提示:用户侧边栏信息先批量查询,然后再通过递归组装数据;

代码实现

1. 表现层
    @ApiOperation(value = "登录功能")
    // 该请求传入的参数为Json类型 , 我们需要将Json类型的数据反序列化为对象需要调用RequestBody
    @PostMapping("/login")
    public R<LoginRespVo> login(@RequestBody LoginReqVo loginReqVo){
        return UserService.login(loginReqVo);
    }
封装实体类
package com.jixu.stock.vo.resp;

import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

/**
 * @author by itheima
 * @Date 2021/12/24
 * @Description 登录后响应前端的vo
 */
@ApiModel(description = "登录后响应前端的vo")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class LoginRespVo {
    /**
     * 在前端中long类型数据过长会导致数据失真 英雌我们在传递的过程中需要将数据转化为String类型
     */
    //用户ID
    @JsonSerialize(using = ToStringSerializer.class)
    private Long id;
    //用户名称
    private String username;
    //手机号
    private String phone;
    //昵称
    private String nickName;
    //真实名称
    private String realName;
    //性别
    private Integer sex;
    //状态
    private Integer status;
    //邮件
    private String email;
    //侧边栏权限树
    private List menus;
    //按钮权限标识
    private List permissions;

}
package com.jixu.stock.pojo.domain;

import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

/**
 * @program: stock_parent
 * @description:
 * @author: jixu
 * @create: 2024-10-05 17:57
 **/

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserLoginMenusDomain {

    @JsonSerialize(using = ToStringSerializer.class)
    private Long  id;
    private String title;
    private String icon ;
    private String path;
    private String name;
    private List children;
}

2. 服务层

这个完善用户功能最大的难点是在于如何封装用户权限数据

  1. 从数据库中获取该用户的所有权限信息
  2. 将所有信息以Map的形式进行封装 用户id作为key
  3. 找到pid对应的id , 封装进另一个Map中 , 进行映射
public R<LoginRespVo> login(LoginReqVo loginReqVo);
 /**
     * 实现登录功能
     * @param loginReqVo
     * @return
     */
    @Override
    public R<LoginRespVo> login(LoginReqVo loginReqVo) {
        // 1. 判断数据是否合法
        if ( loginReqVo == null || StringUtils.isBlank(loginReqVo.getUsername()) || StringUtils.isBlank(loginReqVo.getPassword())){
            return R.error(ResponseCode.DATA_ERROR.getMessage());
        }

        // 判断验证码以及sessionId是否存在
        if ( StringUtils.isBlank(loginReqVo.getCode()) || StringUtils.isBlank(loginReqVo.getSessionId())){
            return R.error(ResponseCode.CHECK_CODE_NOT_EMPTY.getMessage());
        }

        // 校验验证码是否正确
        String redisCode = (String) redisTemplate.opsForValue().get(StockConstant.CHECK_PREFIX + loginReqVo.getSessionId());
        if ( StringUtils.isBlank(redisCode) ){
            return R.error(ResponseCode.CHECK_CODE_TIMEOUT.getMessage());
        }

        // 判断验证码是否正确
        if ( !redisCode.equalsIgnoreCase(loginReqVo.getCode()) ){
            return R.error(ResponseCode.CHECK_CODE_ERROR.getMessage());
        }

        // 2. 根据用户名查询用户数据
        SysUser sysUser = sysUserMapper.selectByName(loginReqVo.getUsername());
        if (sysUser == null){
            return R.error(ResponseCode.ACCOUNT_NOT_EXISTS.getMessage());
        }

        // 3. 判断用户数据是否正确
        if (!passwordEncoder.matches(loginReqVo.getPassword(),sysUser.getPassword())) {
            return R.error(ResponseCode.USERNAME_OR_PASSWORD_ERROR.getMessage());
        }


        // 4. 装配数据 --> 使用BeanUtils中的工具包copyProperties可以直接将前一个对象的数据装配到后一个对象
        //                前提是保证两个对象的属性名相同
        LoginRespVo loginRespVo = new LoginRespVo();
        BeanUtils.copyProperties(sysUser , loginRespVo);

        // 获取用户ID
        Long id = sysUser.getId();

        // 查询获取主要权限
        List<Map<String , Object>> menusData = sysPermissionMapper.getLoginMenus(id);
            
//        // 获取所有id
//        Set<Long> set = menusData.stream().map(item -> {
//            Long roleid = (Long) item.get("id");
//            return roleid;
//        }).collect(Collectors.toSet());

        ArrayList<UserLoginMenusDomain> userLoginMenus = new ArrayList<>();



        // permissions
        ArrayList<String> permissions = new ArrayList<>();


        // 获取所有的数据集对象
        HashMap<Long, UserLoginMenusDomain> menusDomainHashMap = new HashMap<Long, UserLoginMenusDomain>();


        // 获取用户对应岗位
        ArrayList<UserLoginMenusDomain> zeroData = new ArrayList<>();
        for (Map<String, Object> item : menusData) {
            Long pid = (Long) item.get("pid");
            Long user_id = (Long) item.get("id");
            String title = (String) item.get("title");
            String path = (String) item.get("path");
            String icon = (String) item.get("icon");
            String name = (String) item.get("name");
            String code = (String) item.get("code");
            if (!StringUtils.isBlank(code)){
                permissions.add(code);
            }
            UserLoginMenusDomain userLoginMenusDomain = new UserLoginMenusDomain();
            userLoginMenusDomain.setId(user_id);
            userLoginMenusDomain.setIcon(icon);
            userLoginMenusDomain.setPath(path);
            userLoginMenusDomain.setName(name);
            userLoginMenusDomain.setTitle(title);
            userLoginMenusDomain.setChildren(new ArrayList());
            menusDomainHashMap.put(user_id , userLoginMenusDomain);
            if (pid == 0){
                zeroData.add(userLoginMenusDomain);
            }
        }




        // 用于快速查找子菜单的映射
        Map<Long, List<UserLoginMenusDomain>> childrenMap = new HashMap<>();

        // 首先,构建一个映射,将每个父ID映射到其子菜单列表
        for (Map<String, Object> menusDatum : menusData) {
            Long pid = (Long) menusDatum.get("pid");
            // 循环获取每个对象的id
            Long id1 = (Long) menusDatum.get("id");

            // 从数据集中查询获取该循环的对象
            UserLoginMenusDomain domain = menusDomainHashMap.get(id1);
            // 如果可以查询到
            if (domain != null) {
                // 创建一个对应的映射菜单 --> pid : List<该pid对应的父对象>
                childrenMap.computeIfAbsent(pid, k -> new ArrayList<>()).add(domain);
            }
        }



        // 假设 menusDomainHashMap 已经根据菜单项的 ID 填充了 UserLoginMenusDomain 对象
        for (UserLoginMenusDomain zeroDatum : zeroData) {
            Long parentId = zeroDatum.getId();
            ArrayList<UserLoginMenusDomain> childrenList = new ArrayList<>();



            // 现在,我们可以轻松地找到每个主菜单项的子菜单
            if (childrenMap.containsKey(parentId)) {
                List<UserLoginMenusDomain> directChildren = childrenMap.get(parentId);
                for (UserLoginMenusDomain child : directChildren) {
                    // 递归地为每个子菜单设置其子菜单(如果有的话)
                    List<UserLoginMenusDomain> grandChildren = childrenMap.getOrDefault(child.getId(), new ArrayList<>());
                    child.setChildren(grandChildren);
                }
                childrenList.addAll(directChildren);
            }

            zeroDatum.setChildren(childrenList);
        }


        loginRespVo.setMenus(zeroData);
        loginRespVo.setPermissions(permissions);




        return R.ok(loginRespVo);
    }
3. Dao层
List<Map<String, Object>> getLoginMenus(@Param("id") Long id);
 <select id="getLoginMenus" resultType="java.util.Map">
        select sys_permission.id as id,
               sys_permission.title as title,
               sys_permission.icon as icon,
               sys_permission.url as path,
               sys_permission.name as name,
               sys_permission.code as code,
               sys_permission.pid as pid
        from sys_user_role
                 join sys_user on sys_user.id = sys_user_role.user_id
                 join sys_role on sys_user_role.role_id = sys_role.id
                 join sys_role_permission  on sys_user_role.role_id = sys_role_permission.role_id
                 join sys_permission  on sys_role_permission.permission_id = sys_permission.id

        where sys_user.id = #{id}
    </select>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

攒了一袋星辰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值