今日指数项目之大盘指数功能实现

1、国内大盘指数功能

1.1国内大盘指数业务分析

1.1.1 页面原型效果

查询A股大盘最新的数据:

在这里插入图片描述

国内大盘数据包含:大盘代码、大盘名称、开盘点、最新点、前收盘点、交易量、交易金额、涨跌值、涨幅、振幅、当前日期

1.1.2 相关表结构分析

大盘指数包含国内和国外的大盘数据,目前我们先完成国内大盘信数据的展示功能;

国内股票大盘数据详情表设计如下:

在这里插入图片描述

注意事项:

数据库字段类型decimal—>java中的BigDecimal

数据库字段类型bigint—> java中的Long类型

1.1.3 A股大盘指数接口说明

功能说明:

  • 获取最新国内A股大盘信息(仅包含上证和深证大盘数据);
  • 查询时间点不在正常股票交易时间内,则显示最近时间点的交易信息;
    • 比如:当前查询时间点是周一上午8点整,因为当天尚未开盘,则显示上周五最新的数据,也就是收盘时数据;

请求路径:/api/quot/index/all
请求方式:GET
参数:无

响应数据格式:

{
    "code": 1,
    "data": [
        {
            "code": "sh000001",//大盘编码
            "name": "上证指数",//指数名称
            "openPoint": 3267.81,//开盘点
            "curPoint": 3236.70,//当前点
            "preClosePoint": 3283.43,//前收盘点
            "tradeAmt": 160591,//交易量
            "tradeVol": 1741099,//交易金额
            "upDown": -46.73,//涨跌值
            "rose": -0.01.42,//涨幅
            "amplitude": 0.0164,//振幅
            "curTime": "2022-01-02 01:32"//当前时间
        },
      	{......}
    ]
}

A股大盘开盘周期:周一至周五,每天上午9:30到11:30和下午13:00到15:00;

1.1.4 响应结果实体类封装

我们约定从数据库查询的数据如果来自多张表或者单表的部分字段,则封装到domain实体类下;

domain、pojo、entity、vo类等实体类作为公共资源都维护在stock_common工程下;

package com.itheima.stock.pojo.domain;

import lombok.Data;

import java.math.BigDecimal;

/**
 * @author by itheima
 * @Date 2022/1/9
 * @Description 定义封装多内大盘数据的实体类
 */
@Data
public class InnerMarketDomain {
    /**
     * 大盘编码
     */
    private String code;
    /**
     * 大盘名称
     */
    private String name;
    /**
     * 开盘点
     */
    private BigDecimal openPoint;
    /**
     * 当前点
     */
    private BigDecimal curPoint;
    /**
     * 前收盘点
     */
    private BigDecimal preClosePoint;
    /**
     * 交易量
     */
    private Long tradeAmt;
    /**
     * 交易金额
     */
    private Long tradeVol;
    /**
     * 涨跌值
     */
    private BigDecimal upDown;
    /**
     * 涨幅
     */
    private BigDecimal rose;

    /**
     * 振幅
     */
    private BigDecimal amplitude;
    /**
     * 当前时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
    private Date curTime;
}

注意:在stock_common工程下直接导入day02\资料\domain\InnerMarketDomain.java 即可

1.2 国内大盘功能实现准备

1.2.1 股票交易时间工具类封装

项目中经常需要查询股票最近的一次交易时间点,而大盘的开盘时间又分为不同的时间段,这给我们的逻辑判断增加了复杂度,而且项目中股票是每分钟采集一次,时间需要精确到分钟,综上,我们需要在stock_common工程下维护一个公共的时间工具类:

package com.itheima.stock.utils;

import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;

/**
 * @author by itheima
 * @Date 2021/12/31
 * @Description 日期时间工具类
 */
public class DateTimeUtil {
    /**
     * 获取指定日期下股票的上一个有效交易日时间
     * @return
     */
    public static DateTime getPreviousTradingDay(DateTime dateTime){
        //获取指定日期对应的工作日
        int weekNum = dateTime.dayOfWeek().get();
        //判断所属工作日
        DateTime preDateTime=null;
        //周一,那么T-1就是周五
        if (weekNum==1){
            //日期后退3天
          preDateTime=dateTime.minusDays(3);
        }
        //周末,那么T-1就是周五
        else if (weekNum==7){
            preDateTime=dateTime.minusDays(2);
        }
        else {
            preDateTime=dateTime.minusDays(1);
        }
        return getDateTimeWithoutSecond(preDateTime);
    }

    /**
     * 判断是否是工作日
     * @return true:在工作日 false:不在工作日
     */
    public static boolean isWorkDay(DateTime dateTime){
        //获取工作日
        int weekNum = dateTime.dayOfWeek().get();
        return  weekNum>=1 && weekNum<=5;
    }

    /**
     * 获取上一天日期
     * @param dateTime
     * @return
     */
    public static DateTime getPreDateTime(DateTime dateTime){
        return dateTime.minusDays(1);
    }

    /**
     * 日期转String
     * @param dateTime 日期
     * @param pattern 日期正则格式
     * @return
     */
    public static String parseToString(DateTime dateTime,String pattern){
       return  dateTime.toString(DateTimeFormat.forPattern(pattern));
    }

    /**
     * 获取股票日期格式字符串
     * @param dateTime
     * @return
     */
    public static String parseToString4Stock(DateTime dateTime){
        return parseToString(dateTime,"yyyyMMddHHmmss");
    }

    /**
     * 获取指定日期的收盘日期
     * @param dateTime
     * @return
     */
    public static DateTime getCloseDate(DateTime dateTime){
       return dateTime.withHourOfDay(14).withMinuteOfHour(58).withSecondOfMinute(0).withMillisOfSecond(0);
    }

    /**
     * 获取指定日期的开盘日期
     * @param dateTime
     * @return
     */
    public static DateTime getOpenDate(DateTime dateTime){
       return dateTime.withHourOfDay(9).withMinuteOfHour(30).withSecondOfMinute(0).withMillisOfSecond(0);
    }

    /**
     * 获取最近的股票有效时间,精确到分钟
     * @param target
     * @return
     */
    public static String getLastDateString4Stock(DateTime target){
        DateTime dateTime = getLastDate4Stock(target);
        dateTime=getDateTimeWithoutSecond(dateTime);
        return parseToString4Stock(dateTime);
    }
    /**
     * 获取最近的股票有效时间,精确到分钟
     * @param target
     * @return
     */
    public static DateTime getLastDate4Stock(DateTime target){
        //判断是否是工作日
        if (isWorkDay(target)) {
            //当前日期开盘前
            if (target.isBefore(getOpenDate(target))) {
                target=getCloseDate(getPreviousTradingDay(target));
            }else if (isMarketOffTime(target)){
                target=target.withHourOfDay(11).withMinuteOfHour(28).withSecondOfMinute(0).withMillisOfSecond(0);
            }else if (target.isAfter(getCloseDate(target))){
                //当前日期收盘后
                target=getCloseDate(target);
            }
        }else{
            //非工作日
            target=getCloseDate(getPreviousTradingDay(target));
        }
         target = getDateTimeWithoutSecond(target);
        return target;
    }

    /**
     * 判断当前时间是否在大盘的中午休盘时间段
     * @return
     */
    public static boolean isMarketOffTime(DateTime target){
        //上午休盘开始时间
        DateTime start = target.withHourOfDay(11).withMinuteOfHour(28).withSecondOfMinute(0).withMillisOfSecond(0);
        //下午开盘时间
        DateTime end = target.withHourOfDay(13).withMinuteOfHour(0).withSecondOfMinute(0).withMillisOfSecond(0);
        if (target.isAfter(start) && target.isBefore(end)) {
            return true;
        }
        return false;
    }

    /**
     * 将秒时归零
     * @param dateTime 指定日期
     * @return
     */
    public static DateTime getDateTimeWithoutSecond(DateTime dateTime){
        DateTime newDate = dateTime.withSecondOfMinute(0).withMillisOfSecond(0);
        return newDate;
    }

    /**
     * 将秒时归零
     * @param dateTime 指定日期字符串,格式必须是:yyyy-MM-dd HH:mm:ss
     * @return
     */
    public static DateTime getDateTimeWithoutSecond(String dateTime){
        DateTime parse = DateTime.parse(dateTime, DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        return getDateTimeWithoutSecond(parse);
    }
}

说明:

在stock_common下直接导入日期工具类:今日指数\day02\资料\date工具类\DateTimeUtil.java

工具类借助jode-time日期插件实现,jode-date核心方式参考:day02\资料\date工具类\TestJodeDate.java

1.2.2 常量数据封装

股票常用的公共参数非常多,我们可以在stock_common下把他们封装到一个Value Object(vo)对象下,并通过Spring为调用方动态赋值;

本小节我们把股票大盘编码信息配置到StockInfoConfig实体类下:

package com.itheima.stock.pojo.vo;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.List;

/**
 * @author by itheima
 * @Date 2021/12/30
 * @Description
 */
@ConfigurationProperties(prefix = "stock")
@Data
public class StockInfoConfig {
    //A股大盘ID集合
    private List<String> inner;
    //外盘ID集合
    private List<String> outer;
}

在调用方stock_backend工程下定义application-stock.yml文件,并配置A股大盘和外盘的编码数据:

# 配置股票相关的参数
stock:
  inner: # A股
    - sh000001 # 上证ID
    - sz399001 #  深证ID
  outer: # 外盘
    - int_dji # 道琼斯
    - int_nasdaq # 纳斯达克
    - int_hangseng # 恒生
    - int_nikkei # 日经指数
    - b_FSSTI # 新加坡

同时在主配置文件application.yml中激活该配置:

spring:
   profiles:
   		active: stock

说明:将股票相关的配置文件独立出来,方便后期维护,且避免产生臃肿的主配置文件;

在公共配置类中加载实体VO对象:

@EnableConfigurationProperties(StockInfoConfig.class)
@Configuration
public class CommonConfig {
	//省略N行
}

1.3 国内大盘指数SQL分析

业务功能:获取最新的国内大盘的数据信息

-- 功能说明:获取最新国内A股大盘信息(上证和深证)
-- 	如果不在股票交易时间,则显示最近时间点的交易信息
-- 分析:就是根据大盘的编码查询大盘的最新交易数据
-- 大盘编码:sh000001 sz399001
SELECT 
	smi.market_code AS code,
	smi.market_name AS name,
	smi.open_point AS openPoint,
	smi.cur_point AS curPoint,
	smi.pre_close_point AS preClosePoint,
	smi.trade_amount AS tradeAmt,
	smi.trade_volume AS tradeVol,
	smi.cur_point-smi.pre_close_point AS upDown,
	(smi.cur_point-smi.pre_close_point)/smi.pre_close_point AS rose,
	(smi.max_point-smi.min_point)/smi.pre_close_point AS amplitude,
	smi.cur_time AS curTime
FROM stock_market_index_info AS smi
WHERE smi.market_code IN ('sh000001','sz399001')
ORDER BY smi.cur_time DESC LIMIT 2;
# 存在的问题:1.全表查询,效率较低,如何优化?
# 一方面为了为了避免重复数据,将时间和大盘编码作为联合唯一索引,起到
-- 唯一约束的作用 另外,借助这个索引,也避免全表查询
-- 查询最新的数据,可转化成查询最新的股票交易时间点下的数据
SELECT 
	smi.market_code AS code,
	smi.market_name AS name,
	smi.open_point AS openPoint,
	smi.cur_point AS curPoint,
	smi.pre_close_point AS preClosePoint,
	smi.trade_amount AS tradeAmt,
	smi.trade_volume AS tradeVol,
	smi.cur_point-smi.pre_close_point AS upDown,
	(smi.cur_point-smi.pre_close_point)/smi.pre_close_point AS rose,
	(smi.max_point-smi.min_point)/smi.pre_close_point AS amplitude,
	smi.cur_time AS curTime
FROM stock_market_index_info AS smi
WHERE smi.market_code IN ('sh000001','sz399001')
AND smi.cur_time ='2021-12-28 09:31:00';
# 在sql查询时,尽量避免全表查询,否则随着数据量的增加,全表查询导致的查询时间成本会不断上升!

1.4 国内大盘指数功能实现

1.4.1 定义获取A股大盘数据接口
package com.itheima.stock.controller;

import com.itheima.stock.pojo.StockBusiness;
import com.itheima.stock.service.StockService;
import com.itheima.stock.vo.resp.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Map;

/**
 * @author by itheima
 * @Date 2021/12/19
 * @Description
 */
@RestController
@RequestMapping("/api/quot")
public class StockController {

    @Autowired
    private StockService stockService;

	//其它省略.....
    /**
     * 获取国内最新大盘指数
     * @return
     */
    @GetMapping("/index/all")
    public R<List<InnerMarketDomain>> innerIndexAll(){
        return stockService.innerIndexAll();
    }
}
1.4.2 定义国内大盘数据服务

服务接口:

package com.itheima.stock.service;
import com.itheima.stock.pojo.StockBusiness;
import com.itheima.stock.vo.resp.R;

import java.util.List;
import java.util.Map;

/**
 * @author by itheima
 * @Date 2021/12/19
 * @Description 定义股票服务接口
 */
public interface StockService {
	//其它省略......
    /**
     * 获取国内大盘的实时数据
     * @return
     */
    R<List<InnerMarketDomain>> innerIndexAll();
}

服务接口实现:

package com.itheima.stock.service.impl;

import com.itheima.stock.common.domain.StockInfoConfig;
import com.itheima.stock.mapper.StockBusinessMapper;
import com.itheima.stock.mapper.StockMarketIndexInfoMapper;
import com.itheima.stock.pojo.StockBusiness;
import com.itheima.stock.service.StockService;
import com.itheima.stock.vo.resp.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;

/**
 * @author by itheima
 * @Date 2021/12/19
 * @Description
 */
@Service("stockService")
public class StockServiceImpl implements StockService {

    @Autowired
    private StockBusinessMapper stockBusinessMapper;

    @Autowired
    private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;

    @Autowired
    private StockInfoConfig stockInfoConfig;

    @Override
    public List<StockBusiness> getAllStockBusiness() {
        return stockBusinessMapper.findAll();
    }

    /**
     * 获取国内大盘的实时数据
     * @return
     */
    @Override
    public R<List<InnerMarketDomain>> innerIndexAll() {
        //1.获取国内A股大盘的id集合
        List<String> inners = stockInfoConfig.getInner();
        //2.获取最近股票交易日期
        Date lastDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        //TODO mock测试数据,后期数据通过第三方接口动态获取实时数据 可删除
        lastDate=DateTime.parse("2022-01-02 09:32:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //3.将获取的java Date传入接口
        List<InnerMarketDomain> list= stockMarketIndexInfoMapper.getMarketInfo(inners,lastDate);
        //4.返回查询结果
        return R.ok(list);
    }
}
1.4.3 定义mapper接口方法和xml

mapper下定义接口方法和xml:

    /**
     * 根据大盘的id和时间查询大盘信息
     * @param marketIds 大盘id集合
     * @param timePoint 当前时间点(默认精确到分钟)
     * @return
     */
    List<InnerMarketDomain> getMarketInfo(@Param("marketIds") List<String> marketIds, @Param("timePoint") Date timePoint);

定义mapper接口绑定SQL:

    <select id="getMarketInfo" resultType="com.itheima.stock.pojo.domain.InnerMarketDomain">
        select
            smi.market_code as code,
            smi.market_name as name,
            smi.open_point as openPoint,
            smi.cur_point as curPoint,
            smi.pre_close_point as preClosePrice,
            smi.trade_amount as tradeAmt,
            smi.trade_volume as tradeVol,
            smi.cur_point-smi.pre_close_point as upDown,
            (smi.cur_point-smi.pre_close_point)/smi.pre_close_point as rose,
            (smi.max_point-smi.min_point)/smi.pre_close_point as amplitude,
            smi.cur_time as curTime
        from stock_market_index_info as smi
        where smi.market_code in
        <foreach collection="marketIds" item="marketId"  open="(" separator="," close=")">
            #{marketId}
        </foreach>
        and smi.cur_time=#{timePoint}
    </select>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

攒了一袋星辰

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

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

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

打赏作者

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

抵扣说明:

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

余额充值