소스 검색

增加“查询企业风管期货商品信息”接口

zhou.xiaoning 4 년 전
부모
커밋
7475474f1c
7개의 변경된 파일345개의 추가작업 그리고 12개의 파일을 삭제
  1. 50 0
      controllers/ermcp/qryGoods.go
  2. 145 9
      controllers/quote/history.go
  3. 47 1
      docs/docs.go
  4. 47 1
      docs/swagger.json
  5. 30 1
      docs/swagger.yaml
  6. 20 0
      models/ermcpGoods.go
  7. 6 0
      routers/router.go

+ 50 - 0
controllers/ermcp/qryGoods.go

@@ -0,0 +1,50 @@
+package ermcp
+
+import (
+	"mtp2_if/global/app"
+	"mtp2_if/global/e"
+	"mtp2_if/logger"
+	"mtp2_if/models"
+	"net/http"
+
+	"github.com/gin-gonic/gin"
+)
+
+// GetErmcpGoodsReq 查询企业风管期货商品信息请求参数
+type GetErmcpGoodsReq struct {
+	LastID int `form:"lastID"`
+}
+
+// GetErmcpGoods 查询企业风管期货商品信息
+// @Summary 查询企业风管期货商品信息
+// @Produce json
+// @Security ApiKeyAuth
+// @Param lastID query int false "已存末尾商品编号"
+// @Success 200 {object} models.Goods
+// @Failure 500 {object} app.Response
+// @Router /Ermcp/GetErmcpGoods [get]
+// @Tags 企业风险管理(app)
+func GetErmcpGoods(c *gin.Context) {
+	appG := app.Gin{C: c}
+
+	// 获取请求参数
+	var req GetErmcpGoodsReq
+	if err := appG.C.ShouldBindQuery(&req); err != nil {
+		logger.GetLogger().Errorf("GetErmcpGoods failed: %s", err.Error())
+		appG.Response(http.StatusBadRequest, e.INVALID_PARAMS, nil)
+		return
+	}
+
+	// 获取数据
+	goodses, err := models.GetErmcpGoodses(req.LastID)
+	if err != nil {
+		// 查询失败
+		logger.GetLogger().Errorf("GetErmcpGoods failed: %s", err.Error())
+		appG.Response(http.StatusBadRequest, e.ERROR_QUERY_FAIL, nil)
+		return
+	}
+
+	// 查询成功返回
+	logger.GetLogger().Debugln("GetErmcpGoods successed: %v", goodses)
+	appG.Response(http.StatusOK, e.SUCCESS, goodses)
+}

+ 145 - 9
controllers/quote/history.go

@@ -2,6 +2,7 @@ package quote
 
 import (
 	"encoding/json"
+	"fmt"
 	"mtp2_if/global/app"
 	"mtp2_if/global/e"
 	"mtp2_if/logger"
@@ -25,6 +26,7 @@ type HistoryData struct {
 	HoldVolume    int       `json:"hv"` // 持仓量
 	Settle        float64   `json:"s"`  // 结算价,日线周期(包括)以上才有
 	TimeStamp     time.Time `json:"ts"` // 时间
+	IsFill        bool      `json:"f"`  // 是否补充数据
 }
 
 // QueryHistoryDatasReq 查询行情历史数据请求参数
@@ -132,10 +134,11 @@ type QueryTSDataReq struct {
 // QueryTSDataRsp 分时图数据查询返回模型
 type QueryTSDataRsp struct {
 	GoodsCode    string        `json:"goodsCode"`    // 商品代码
+	OutGoodsCode string        `json:"outGoodsCode"` // 外部商品代码
 	DecimalPlace int           `json:"decimalPlace"` // 小数位
 	TradeDate    string        `json:"tradeDate"`    // 交易日
-	StartTime    string        `json:"startTime"`    // 开始时间
-	EndTime      string        `json:"endTime"`      // 结束时间
+	StartTime    time.Time     `json:"startTime"`    // 开始时间
+	EndTime      time.Time     `json:"endTime"`      // 结束时间
 	PreSettle    float32       `json:"preSettle"`    // 昨结
 	HistoryDatas []HistoryData `json:"historyDatas"` // 历史数据
 }
@@ -143,7 +146,7 @@ type QueryTSDataRsp struct {
 // QueryTSData 分时图数据查询
 // @Summary 分时图数据查询
 // @Produce json
-// @Param GoodsCode query string true "商品代码"
+// @Param goodsCode query string true "商品代码"
 // @Success 200 {object} QueryTSDataRsp
 // @Failure 500 {object} app.Response
 // @Router /Quote/QueryTSData [get]
@@ -159,8 +162,6 @@ func QueryTSData(c *gin.Context) {
 		return
 	}
 
-	var queryTSDataRsp QueryTSDataRsp
-
 	// FIXME: - 一些不常变化的数据(如市场信息、商品信息等)应缓存到Redis中, 或缓存到服务内存
 	market, err := models.GetMarketByGoodsCode(req.GoodsCode)
 	if err != nil {
@@ -225,11 +226,28 @@ func QueryTSData(c *gin.Context) {
 			}
 		}
 	}
+
+	// 获取商品信息
+	goods, err := models.GetGoodsByGoodsCode(req.GoodsCode)
+	if goods == nil {
+		logger.GetLogger().Errorf("QueryTSData failed: %s", err.Error())
+		appG.Response(http.StatusBadRequest, e.ERROR_GET_GOODS_FAILED, nil)
+		return
+	}
+
+	// 构建返回数据
+	queryTSDataRsp := QueryTSDataRsp{
+		GoodsCode:    goods.Goodscode,
+		OutGoodsCode: goods.Outgoodscode,
+		TradeDate:    marketRun.Tradedate,
+		DecimalPlace: int(goods.Decimalplace),
+	}
+
 	// 构建分时图可直接使用的开休市数据
 	// 这里有一个知识点:TRADEWEEKDAY 与 STARTWEEKDAY,以及 TRADEWEEKDAY 与 ENDWEEKDAY 之间相差最多一天(管理端限制),
 	//                所以目前并不支持正真的周五夜盘模式。我们在实现时不用做得太复杂。
 	// 当前交易日(周几)对应的开休市计划
-	tradeDate, err := time.ParseInLocation("2006-01-02", marketRun.Tradedate, time.Local)
+	tradeDate, err := time.ParseInLocation("20060102", marketRun.Tradedate, time.Local)
 	if err != nil {
 		logger.GetLogger().Errorf("QueryTSData failed: %s", err.Error())
 		appG.Response(http.StatusBadRequest, e.ERROR_QUERY_FAIL, nil)
@@ -237,7 +255,7 @@ func QueryTSData(c *gin.Context) {
 	}
 	curWeekRunSteps := make([]map[string]interface{}, 0)
 	for _, v := range runSteps {
-		tradeWeekDay := v["tradeweekday"].(int)
+		tradeWeekDay := int(v["tradeweekday"].(float64))
 		if tradeWeekDay == int(tradeDate.Weekday()) {
 			curWeekRunSteps = append(curWeekRunSteps, v)
 		}
@@ -248,14 +266,132 @@ func QueryTSData(c *gin.Context) {
 		appG.Response(http.StatusBadRequest, e.ERROR_GET_RUNSTEP_FAILED, nil)
 		return
 	}
-	// 按 SECTIONID 顺序排序(管理端会按时间顺序添加开休市计划)
+	// 按 SECTIONID 顺序排序
 	sort.Slice(curWeekRunSteps, func(i int, j int) bool {
 		return curWeekRunSteps[i]["sectionid"].(int) < curWeekRunSteps[j]["sectionid"].(int)
 	})
+
 	// 把各开休市时间段转化为真实的时间
-	//
+	// 关于开休市计划的时间顺序:管理端会按时间顺序添加开休市计划,所以交易日开始时间为第一条记录的开始时间,结束时间为最后一条记录的结束时间
+	// 关于目标商品的交易日问题:目前只能从商品所属市场获取当前交易日,这样有两个问题,一是不能按常规显示最后一个有历史数据的交易日;二是目前所有外部商品的开休市计划都是一样的。
+	timeFormat := "20060102 15:04"
+	// 开始时间
+	startInterval := getTradeDay(int(curWeekRunSteps[0]["tradeweekday"].(float64)), int(curWeekRunSteps[0]["startweekday"].(float64)))
+	queryTSDataRsp.StartTime, _ = time.ParseInLocation(timeFormat, fmt.Sprintf("%s %s", marketRun.Tradedate, curWeekRunSteps[0]["starttime"].(string)), time.Local)
+	if startInterval != 0 {
+		duration, _ := time.ParseDuration(fmt.Sprintf("%dh", startInterval*24))
+		queryTSDataRsp.StartTime = queryTSDataRsp.StartTime.Add(duration)
+	}
+	// 结束时间
+	index := len(curWeekRunSteps) - 1
+	endInterval := getTradeDay(int(curWeekRunSteps[index]["tradeweekday"].(float64)), int(curWeekRunSteps[index]["endweekday"].(float64)))
+	queryTSDataRsp.EndTime, _ = time.ParseInLocation(timeFormat, fmt.Sprintf("%s %s", marketRun.Tradedate, curWeekRunSteps[index]["endtime"].(string)), time.Local)
+	if endInterval != 0 {
+		duration, _ := time.ParseDuration(fmt.Sprintf("%dh", endInterval*24))
+		queryTSDataRsp.EndTime = queryTSDataRsp.EndTime.Add(duration)
+	}
+	fmt.Printf("开始时间:%s 结束时间:%s\n", queryTSDataRsp.StartTime.Format(timeFormat), queryTSDataRsp.EndTime.Format(timeFormat))
+
+	// 获取目标时间段的历史数据
+	// 这里要注意:由于交易库和行情库由于GoodsCode大小写不一定对得上,所以在使用交易库的商品查询行情数据时间,都要使用OutGoodsCode字段
+	cycleDatas, err := models.GetHistoryCycleDatas(models.CycleTypeMinutes1, queryTSDataRsp.OutGoodsCode, &queryTSDataRsp.StartTime, &queryTSDataRsp.EndTime, 0, true)
+	if err != nil {
+		logger.GetLogger().Errorf("QueryTSData failed: %s", err.Error())
+		appG.Response(http.StatusBadRequest, e.ERROR_QUERY_FAIL, nil)
+		return
+	}
+	// ==================== 补数据(补休市数据和缺少的数据)====================
+	// 补数据第一步:如果第一数据不是开始时间的,需要补数据
+	if len(cycleDatas) > 0 {
+		sources := time.Unix(int64(cycleDatas[0].ST), 0)
+		diff := sources.Sub(queryTSDataRsp.StartTime)
+		if diff.Minutes() > 0 {
+			minute := int(diff.Minutes())
+			for i := 0; i < minute; i++ {
+				st := cycleDatas[0].ST - i*60
+				stt := time.Unix(int64(st), 0).Format("2006-01-02 15:04:05")
+				cycleDatas = append(cycleDatas, models.CycleData{
+					GC:    cycleDatas[0].GC,
+					Open:  cycleDatas[0].Open,
+					High:  cycleDatas[0].High,
+					Low:   cycleDatas[0].Low,
+					Close: cycleDatas[0].Close,
+					TV:    cycleDatas[0].TV,
+					TT:    cycleDatas[0].TT,
+					HV:    cycleDatas[0].HV,
+					SP:    cycleDatas[0].SP,
+					ST:    st,
+					SST:   stt,
+				})
+			}
+		}
+		// 接时间戳排序
+		sort.Slice(cycleDatas, func(i int, j int) bool {
+			return cycleDatas[i].ST < cycleDatas[j].ST
+		})
+	}
+	// 补数据第二步:按尾部的时间(当前服务器时间或最后休市时间)进行全补
+	if len(cycleDatas) > 0 {
+		s, _ := models.GetServerTime()
+		endTime, _ := time.ParseInLocation("2006/01/02 15:04:05", *s, time.Local)
+		if endTime.After(queryTSDataRsp.EndTime) {
+			endTime = queryTSDataRsp.EndTime
+		}
+
+		// 判断是否需要补数据,与上一条数据的间距不是一分钟
+		index := len(cycleDatas) - 1
+		sources := time.Unix(int64(cycleDatas[index].ST), 0)
+		diff := sources.Sub(endTime)
+		if diff.Minutes() > 0 {
+			minute := int(diff.Minutes())
+			for i := 0; i < minute; i++ {
+				st := cycleDatas[index].ST + i*60
+				stt := time.Unix(int64(st), 0).Format("2006-01-02 15:04:05")
+				cycleDatas = append(cycleDatas, models.CycleData{
+					GC:    cycleDatas[index].GC,
+					Open:  cycleDatas[index].Open,
+					High:  cycleDatas[index].High,
+					Low:   cycleDatas[index].Low,
+					Close: cycleDatas[index].Close,
+					TV:    cycleDatas[index].TV,
+					TT:    cycleDatas[index].TT,
+					HV:    cycleDatas[index].HV,
+					SP:    cycleDatas[index].SP,
+					ST:    st,
+					SST:   stt,
+				})
+			}
+		}
+		// 接时间戳排序
+		sort.Slice(cycleDatas, func(i int, j int) bool {
+			return cycleDatas[i].ST < cycleDatas[j].ST
+		})
+	}
+	// 补数据第三步:补中间数据
 
 	// 查询成功
 	logger.GetLogger().Debugln("QueryTSData successed: %v", queryTSDataRsp)
 	appG.Response(http.StatusOK, e.SUCCESS, queryTSDataRsp)
 }
+
+// getTradeDay 获取结算计划中天数间隔的方法
+//	- tradeDay: 交易日周几
+//	- weekDay: 开始或结束周几
+//	- Returns: 天数间隔
+func getTradeDay(tradeDay, weekDay int) int {
+	if tradeDay == weekDay {
+		return 0
+	}
+	if weekDay == 0 {
+		weekDay = 7
+	}
+	betWeekDay := weekDay - tradeDay
+	if betWeekDay < 0 {
+		betWeekDay = betWeekDay + 7
+	}
+	if betWeekDay >= 4 {
+		betWeekDay = betWeekDay - 7
+	}
+
+	return betWeekDay
+}

+ 47 - 1
docs/docs.go

@@ -784,6 +784,44 @@ var doc = `{
                 }
             }
         },
+        "/Ermcp/GetErmcpGoods": {
+            "get": {
+                "security": [
+                    {
+                        "ApiKeyAuth": []
+                    }
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "企业风险管理(app)"
+                ],
+                "summary": "查询企业风管期货商品信息",
+                "parameters": [
+                    {
+                        "type": "integer",
+                        "description": "已存末尾商品编号",
+                        "name": "lastID",
+                        "in": "query"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/models.Goods"
+                        }
+                    },
+                    "500": {
+                        "description": "Internal Server Error",
+                        "schema": {
+                            "$ref": "#/definitions/app.Response"
+                        }
+                    }
+                }
+            }
+        },
         "/Ermcp/QryReportDayExposure": {
             "get": {
                 "security": [
@@ -4810,7 +4848,7 @@ var doc = `{
                     {
                         "type": "string",
                         "description": "商品代码",
-                        "name": "GoodsCode",
+                        "name": "goodsCode",
                         "in": "query",
                         "required": true
                     }
@@ -16159,6 +16197,10 @@ var doc = `{
                     "description": "收盘价",
                     "type": "number"
                 },
+                "f": {
+                    "description": "是否补充数据",
+                    "type": "boolean"
+                },
                 "h": {
                     "description": "最高价",
                     "type": "number"
@@ -16215,6 +16257,10 @@ var doc = `{
                         "$ref": "#/definitions/quote.HistoryData"
                     }
                 },
+                "outGoodsCode": {
+                    "description": "外部商品代码",
+                    "type": "string"
+                },
                 "preSettle": {
                     "description": "昨结",
                     "type": "number"

+ 47 - 1
docs/swagger.json

@@ -768,6 +768,44 @@
                 }
             }
         },
+        "/Ermcp/GetErmcpGoods": {
+            "get": {
+                "security": [
+                    {
+                        "ApiKeyAuth": []
+                    }
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "企业风险管理(app)"
+                ],
+                "summary": "查询企业风管期货商品信息",
+                "parameters": [
+                    {
+                        "type": "integer",
+                        "description": "已存末尾商品编号",
+                        "name": "lastID",
+                        "in": "query"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/models.Goods"
+                        }
+                    },
+                    "500": {
+                        "description": "Internal Server Error",
+                        "schema": {
+                            "$ref": "#/definitions/app.Response"
+                        }
+                    }
+                }
+            }
+        },
         "/Ermcp/QryReportDayExposure": {
             "get": {
                 "security": [
@@ -4794,7 +4832,7 @@
                     {
                         "type": "string",
                         "description": "商品代码",
-                        "name": "GoodsCode",
+                        "name": "goodsCode",
                         "in": "query",
                         "required": true
                     }
@@ -16143,6 +16181,10 @@
                     "description": "收盘价",
                     "type": "number"
                 },
+                "f": {
+                    "description": "是否补充数据",
+                    "type": "boolean"
+                },
                 "h": {
                     "description": "最高价",
                     "type": "number"
@@ -16199,6 +16241,10 @@
                         "$ref": "#/definitions/quote.HistoryData"
                     }
                 },
+                "outGoodsCode": {
+                    "description": "外部商品代码",
+                    "type": "string"
+                },
                 "preSettle": {
                     "description": "昨结",
                     "type": "number"

+ 30 - 1
docs/swagger.yaml

@@ -7671,6 +7671,9 @@ definitions:
       c:
         description: 收盘价
         type: number
+      f:
+        description: 是否补充数据
+        type: boolean
       h:
         description: 最高价
         type: number
@@ -7712,6 +7715,9 @@ definitions:
         items:
           $ref: '#/definitions/quote.HistoryData'
         type: array
+      outGoodsCode:
+        description: 外部商品代码
+        type: string
       preSettle:
         description: 昨结
         type: number
@@ -8724,6 +8730,29 @@ paths:
       summary: 查询商品交割关系表
       tags:
       - 交割服务
+  /Ermcp/GetErmcpGoods:
+    get:
+      parameters:
+      - description: 已存末尾商品编号
+        in: query
+        name: lastID
+        type: integer
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            $ref: '#/definitions/models.Goods'
+        "500":
+          description: Internal Server Error
+          schema:
+            $ref: '#/definitions/app.Response'
+      security:
+      - ApiKeyAuth: []
+      summary: 查询企业风管期货商品信息
+      tags:
+      - 企业风险管理(app)
   /Ermcp/QryReportDayExposure:
     get:
       parameters:
@@ -11255,7 +11284,7 @@ paths:
       parameters:
       - description: 商品代码
         in: query
-        name: GoodsCode
+        name: goodsCode
         required: true
         type: string
       produces:

+ 20 - 0
models/ermcpGoods.go

@@ -0,0 +1,20 @@
+package models
+
+import "mtp2_if/db"
+
+// GetErmcpGoodses 企业风管专用获取商品信息的方法
+func GetErmcpGoodses(lastID int) ([]Goods, error) {
+	engine := db.GetEngine()
+
+	goodses := make([]Goods, 0)
+	session := engine.Table("GOODS").Where("GOODSSTATUS = 3")
+	if lastID != 0 {
+		session = session.And("GOODSID > ?", lastID)
+	}
+	session = session.Asc("GOODSID")
+	if err := session.Find(&goodses); err != nil {
+		return nil, err
+	}
+
+	return goodses, nil
+}

+ 6 - 0
routers/router.go

@@ -155,6 +155,8 @@ func InitRouter() *gin.Engine {
 	{
 		// 查询行情历史数据
 		quoteR.GET("/QueryHistoryDatas", quote.QueryHistoryDatas)
+		// 查询行情历史数据
+		quoteR.GET("/QueryTSData", quote.QueryTSData)
 	}
 	// ************************ 检索服务 ************************
 	searchR := apiR.Group("Search")
@@ -352,6 +354,10 @@ func InitRouter() *gin.Engine {
 		ermcpR.GET("/QryReportMonthSpot", ermcp.QryReportMonthSpot)
 		ermcpR.GET("/QryReportMonthSpotDetail", ermcp.QryReportMonthSpotDetail)
 		ermcpR.GET("/QueryExposureHedgePositionDetail", ermcp.QueryExposureHedgePositionDetail)
+
+		// 期货相关
+		// 查询企业风管期货商品信息
+		ermcpR.GET("/GetErmcpGoods", ermcp.GetErmcpGoods)
 	}
 
 	return r