spotContract.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. package erms3
  2. import (
  3. "encoding/hex"
  4. "encoding/json"
  5. "math"
  6. "mtp2_if/global/app"
  7. "mtp2_if/global/e"
  8. "mtp2_if/logger"
  9. "mtp2_if/models"
  10. "mtp2_if/utils"
  11. "net/http"
  12. "time"
  13. "github.com/gin-gonic/gin"
  14. )
  15. // spotContract.go 现货合同
  16. // RecordUtcCount UTC计数(毫秒)
  17. var RecordUtcCount int = 0
  18. // SecondCount 秒计数
  19. var SecondCount int = 0
  20. // SpotPriceOrder 定价明细
  21. type SpotPriceOrder struct {
  22. Price float64 `json:"price" binding:"required"` // 价格
  23. Qty float64 `json:"qty" binding:"required"` // 数量
  24. Amount float64 `json:"amount" binding:"required"` // 金额
  25. }
  26. // SpotPointOrder 点价明细
  27. type SpotPointOrder struct {
  28. GoodsID int32 `json:"goodsid" binding:"required"` // 商品ID
  29. GoodsName string `json:"goodsname"` // 商品名称
  30. Qty float64 `json:"qty" binding:"required"` // 数量
  31. Basic float64 `json:"basic" binding:"required"` // 基差
  32. StartDate string `json:"startdate" binding:"required"` // 点价开始日期
  33. EndDate string `json:"enddate" binding:"required"` // 点价结束日期
  34. }
  35. // SoptContractDetail 合同明细信息
  36. type SoptContractDetail struct {
  37. WrStandardID int64 `json:"wrstandardid" binding:"required"` // 交易标的ID
  38. WrStandardName string `json:"wrstandardname" binding:"required"` // 交易标的名称
  39. ProductType int32 `json:"producttype" binding:"required"` // 产品类型 1:标准仓单 2:等标 3:非标
  40. ProductTypeName string `json:"producttypename"` // 产品类型名称
  41. DeliveryGoodsID int32 `json:"deliverygoodsid" binding:"required"` // 现货品种ID
  42. DeliveryGoodsName string `json:"deliverygoodsname"` // 现货品种名称
  43. DeliveryGoodsDesc string `json:"deliverygoodsdesc"` // 现货品种说明
  44. WarehouseID int32 `json:"warehouseid" binding:"required"` // 仓库ID
  45. WarehouseName int32 `json:"warehousename"` // 仓库名称
  46. UnitName string `json:"unitname" binding:"required"` // 单位名称
  47. PointDesc string `json:"pointdesc"` // 点价描述
  48. SpotPriceOrderList []SpotPriceOrder `json:"spotPriceOrderList"` // 定价列表
  49. SpotPointOrderVoList []SpotPointOrder `json:"spotPointOrderVoList"` // 点价列表
  50. }
  51. // SoptContractDetailSum 合同明细信息汇总(组织JSON使用)
  52. type SoptContractDetailSum struct {
  53. WrStandardID int64 `json:"wrstandardid" binding:"required"` // 交易标的ID
  54. WrStandardName string `json:"wrstandardname" binding:"required"` // 交易标的名称
  55. ProductType int32 `json:"producttype" binding:"required"` // 产品类型 1:标准仓单 2:等标 3:非标
  56. ProductTypeName string `json:"producttypename"` // 产品类型名称
  57. DeliveryGoodsID int32 `json:"deliverygoodsid" binding:"required"` // 现货品种ID
  58. DeliveryGoodsName string `json:"deliverygoodsname"` // 现货品种名称
  59. DeliveryGoodsDesc string `json:"deliverygoodsdesc"` // 现货品种说明
  60. WarehouseID int32 `json:"warehouseid" binding:"required"` // 仓库ID
  61. WarehouseName int32 `json:"warehousename"` // 仓库名称
  62. UnitName string `json:"unitname" binding:"required"` // 单位名称
  63. PointDesc string `json:"pointdesc"` // 点价描述
  64. SpotPriceOrderList []SpotPriceOrder `json:"spotPriceOrderList"` // 定价列表
  65. SpotPointOrderVoList []SpotPointOrder `json:"spotPointOrderVoList"` // 点价列表
  66. TotalPriceOrderQty float64 `json:"totalPriceOrderQty"` // 定价总数量
  67. TotalPriceOrderPrice float64 `json:"totalPriceOrderPrice"` // 定价总价格
  68. TotalPriceOrderAmount float64 `json:"totalPriceOrderAmount"` // 定价总金额
  69. TotalPointOrderQty float64 `json:"totalPointOrderQty"` // 点价总数量
  70. TotalQty float64 `json:"totalQty"` // 总数量
  71. }
  72. // AddSpotContractApplyReq 新增现货合同申请请求
  73. type AddSpotContractApplyReq struct {
  74. ContractNo string `json:"contractno" binding:"required"` // 现货合同编号
  75. ContractType int32 `json:"contracttype" binding:"required"` // 现货合同类型 - 1:采购 -1:销售
  76. AreaUserID int32 `json:"areauserid" binding:"required"` // 所属机构
  77. AccountID int64 `json:"accountid" binding:"required"` // 资金账户ID
  78. CustomerUserID int32 `json:"customeruserid" binding:"required"` // 客户ID
  79. CustomerAccountID int64 `json:"customeraccountid" binding:"required"` // 客户资金账户ID
  80. SignDate time.Time `json:"signdate" binding:"required"` // 签订日期
  81. LastDate time.Time `json:"lastdate" binding:"required"` // 交货时间
  82. ContractAttachment string `json:"contractattachment"` // 合同附件
  83. OriMarginPayer int32 `json:"orimarginpayer" binding:"required"` // 初始保证金支付方 -1:买方 2:卖方
  84. OriMargin float64 `json:"orimargin" binding:"required"` // 初始保证金
  85. Remark string `json:"remark"` // 备注
  86. MarketID int32 `json:"marketid" binding:"required"` // 市场ID
  87. CreatorID int32 `json:"creatorid"` // 申请人
  88. Details []SoptContractDetail `json:"details" binding:"required"` // 明细
  89. }
  90. // AddSpotContractApplyRsp 新增现货合同申请响应
  91. type AddSpotContractApplyRsp struct {
  92. SpotContractID int64 `json:"spotcontractid" binging:"required"` // 现货合同ID(345+Unix秒时间戳(10位)+xxxxxx)
  93. ContractNo string `json:"contractno" binding:"required"` // 现货合同编号
  94. }
  95. // AddSpotContractApply 新增现货合同申请
  96. // @Summary 新增现货合同申请
  97. // @Produce json
  98. // @Security ApiKeyAuth
  99. // @Param jsonBody body AddSpotContractApplyReq true "申请参数"
  100. // @Success 200 {object} AddSpotContractApplyRsp
  101. // @Failure 500 {object} app.Response
  102. // @Router /Erms3/AddSpotContractApply [post]
  103. // @Tags 风险管理v3
  104. func AddSpotContractApply(c *gin.Context) {
  105. appG := app.Gin{C: c}
  106. // 获取请求参数
  107. var req AddSpotContractApplyReq
  108. err := appG.C.ShouldBindJSON(&req)
  109. if err != nil {
  110. logger.GetLogger().Errorf("AddSpotContractApply failed: %s", err.Error())
  111. appG.Response(http.StatusBadRequest, e.INVALID_PARAMS, nil)
  112. return
  113. }
  114. // 生成申请ID
  115. spotcontractid := GenSpotContractID()
  116. // 获取当前交易日
  117. tradedate, err := GetMarketTradeDate(int(req.MarketID))
  118. if err != nil {
  119. // 返回失败
  120. logger.GetLogger().Errorf("AddSpotContractApply get market trade date failed: %s", err.Error())
  121. appG.Response(http.StatusBadRequest, e.ERROR_OPERATION_FAILED, nil)
  122. return
  123. }
  124. // 根据明细,生成JSON信息
  125. detailjson, err := BuildDetailJSON(req.Details)
  126. if err != nil {
  127. // 返回失败
  128. logger.GetLogger().Errorf("AddSpotContractApply build detail json failed: %s", err.Error())
  129. appG.Response(http.StatusBadRequest, e.ERROR_OPERATION_FAILED, nil)
  130. return
  131. }
  132. // 组建现货合同申请内容
  133. spotContractApply := models.Erms3SpotContractApply{
  134. SpotContractID: spotcontractid,
  135. TradeDate: tradedate,
  136. ContractNo: req.ContractNo,
  137. ContractType: req.ContractType,
  138. AreaUserID: req.AreaUserID,
  139. AccountID: req.AccountID,
  140. CustomerUserID: req.CustomerUserID,
  141. CustomerAccountID: req.CustomerAccountID,
  142. SignDate: req.SignDate,
  143. LastDate: req.LastDate,
  144. ContractAttachment: req.ContractAttachment,
  145. OriMarginPayer: req.OriMarginPayer,
  146. OriMargin: req.OriMargin,
  147. Remark: req.Remark,
  148. DetailJSON: detailjson,
  149. ApplyStatus: 0,
  150. ApplySrc: 2,
  151. MarketID: req.MarketID,
  152. CreatorID: req.CreatorID,
  153. }
  154. // 调用接口,插入现货合同申请信息
  155. if err := models.AddSpotContractApply(spotContractApply); err != nil {
  156. // 插入失败
  157. logger.GetLogger().Errorf("AddSpotContractApply failed: %s", err.Error())
  158. appG.Response(http.StatusBadRequest, e.ERROR_OPERATION_FAILED, nil)
  159. return
  160. }
  161. // 组织响应信息
  162. rsp := AddSpotContractApplyRsp{
  163. SpotContractID: spotcontractid,
  164. ContractNo: req.ContractNo,
  165. }
  166. // 插入成功
  167. logger.GetLogger().Debugln("AddSpotContractApply successed: %v", rsp)
  168. appG.Response(http.StatusOK, e.SUCCESS, rsp)
  169. }
  170. // GenSpotContractID 生成现货合同ID
  171. func GenSpotContractID() int64 {
  172. // 获取当前时间
  173. cur := time.Now()
  174. // UnitNano获取的是纳秒,除以1000000获取毫秒级的时间戳
  175. timestamp := cur.UnixNano() / 1000000
  176. // 除以1000获取秒级的时间戳
  177. var curutc int = int(timestamp / 1000)
  178. if RecordUtcCount == curutc {
  179. SecondCount = SecondCount + 1
  180. } else {
  181. RecordUtcCount = curutc
  182. SecondCount = 1
  183. }
  184. var id int64 = int64(345)*int64(math.Pow(10, 16)) + int64(RecordUtcCount)*int64(math.Pow(10, 6)) + int64(SecondCount)
  185. return id
  186. }
  187. // GetMarketTradeDate 获取市场交易日
  188. func GetMarketTradeDate(marketid int) (string, error) {
  189. marketrun, err := models.GetMarketRun(marketid)
  190. if err != nil {
  191. return "", err
  192. }
  193. return marketrun.Tradedate, nil
  194. }
  195. // BuildDetailJSON 组件合同明细JSON
  196. func BuildDetailJSON(details []SoptContractDetail) (string, error) {
  197. detailsums := make([]SoptContractDetailSum, 0)
  198. for _, detail := range details {
  199. var totalpriceorderqty float64 = 0
  200. var totalpriceorderprice float64 = 0
  201. var totalpriceorderamount float64 = 0
  202. var totalpointorderqty float64 = 0
  203. var totalqty float64 = 0
  204. for _, spotpriceorder := range detail.SpotPriceOrderList {
  205. totalpriceorderqty = totalpriceorderqty + spotpriceorder.Qty
  206. totalpriceorderprice = totalpriceorderprice + spotpriceorder.Price
  207. totalpriceorderamount = totalpriceorderamount + spotpriceorder.Amount
  208. }
  209. for _, spotpointorder := range detail.SpotPointOrderVoList {
  210. totalpointorderqty = totalpointorderqty + spotpointorder.Qty
  211. }
  212. totalqty = totalpriceorderqty + totalpointorderqty
  213. var detailsum SoptContractDetailSum
  214. detailsum = SoptContractDetailSum{
  215. WrStandardID: detail.WrStandardID,
  216. WrStandardName: detail.WrStandardName,
  217. ProductType: detail.ProductType,
  218. ProductTypeName: detail.ProductTypeName,
  219. DeliveryGoodsID: detail.DeliveryGoodsID,
  220. DeliveryGoodsName: detail.DeliveryGoodsName,
  221. DeliveryGoodsDesc: detail.DeliveryGoodsDesc,
  222. WarehouseID: detail.WarehouseID,
  223. WarehouseName: detail.WarehouseName,
  224. UnitName: detail.UnitName,
  225. PointDesc: detail.PointDesc,
  226. SpotPriceOrderList: detail.SpotPriceOrderList,
  227. SpotPointOrderVoList: detail.SpotPointOrderVoList,
  228. TotalPriceOrderQty: totalpriceorderqty,
  229. TotalPriceOrderPrice: totalpriceorderprice,
  230. TotalPriceOrderAmount: totalpriceorderamount,
  231. TotalPointOrderQty: totalpointorderqty,
  232. TotalQty: totalqty,
  233. }
  234. detailsums = append(detailsums, detailsum)
  235. }
  236. jsoninfo, err := json.Marshal(detailsums)
  237. if err != nil {
  238. return "", err
  239. }
  240. return string(jsoninfo), nil
  241. }
  242. // QuerySpotContractAppleFormReq 查询合同申请表单数据请求参数
  243. type QuerySpotContractAppleFormReq struct {
  244. UserID int `form:"userID" binding:"required"`
  245. }
  246. // CustomerInfo 申请单账号信息
  247. type CustomerInfo struct {
  248. Userid int64 `json:"userid" binding:"required"` // 用户ID
  249. Customername string `json:"customername"` // 名称(企业名称)
  250. Mobile string `json:"mobile"` // 手机号码
  251. AccountIDs []int `json:"accountids"` // 资金账户ID列表
  252. }
  253. // QuerySpotContractAppleFormRsp 查询合同申请表单数据返回模型
  254. type QuerySpotContractAppleFormRsp struct {
  255. OurUser CustomerInfo `json:"ouruser"` // 我方账号
  256. OppositeUsers []CustomerInfo `json:"oppositeusers"` // 对方账号列表
  257. WrStandards []models.Wrstandard `json:"wrstandards"` // 仓单标准列表
  258. WareHouseInfos []models.Warehouseinfo `json:"warehouseinfos"` // 仓库信息列表
  259. Goodses []models.GoodsIDAndName `json:"goodses"` // 合约列表
  260. }
  261. // QuerySpotContractAppleForm 查询合同申请表单数据
  262. // @Summary 查询合同申请表单数据
  263. // @Produce json
  264. // @Security ApiKeyAuth
  265. // @Param userID query int true "用户ID"
  266. // @Success 200 {object} QuerySpotContractAppleFormRsp
  267. // @Failure 500 {object} app.Response
  268. // @Router /Erms3/QuerySpotContractAppleForm [get]
  269. // @Tags 风险管理v3
  270. func QuerySpotContractAppleForm(c *gin.Context) {
  271. appG := app.Gin{C: c}
  272. // 获取请求参数
  273. var req QuerySpotContractAppleFormReq
  274. err := appG.C.ShouldBindQuery(&req)
  275. if err != nil {
  276. logger.GetLogger().Errorf("QuerySpotContractAppleForm failed: %s", err.Error())
  277. appG.Response(http.StatusBadRequest, e.INVALID_PARAMS, nil)
  278. return
  279. }
  280. rsp := QuerySpotContractAppleFormRsp{}
  281. // 获取我方用户信息
  282. ourUser, err := models.GetUserInfo(req.UserID)
  283. if err != nil {
  284. // 查询失败
  285. logger.GetLogger().Errorf("QuerySpotContractAppleForm failed: %s", err.Error())
  286. appG.Response(http.StatusBadRequest, e.ERROR_QUERY_FAIL, nil)
  287. return
  288. }
  289. rsp.OurUser.Userid = ourUser.Userid
  290. rsp.OurUser.Customername = ourUser.Customername
  291. key, _ := hex.DecodeString(utils.AESSecretKey)
  292. if len(ourUser.Mobile) > 0 {
  293. // 手机号码解密
  294. if phonenum, err := hex.DecodeString(ourUser.Mobile); err == nil { // hex -> []byte
  295. if mobile, err := utils.AESDecrypt(phonenum, key); err == nil {
  296. rsp.OurUser.Mobile = string(mobile)
  297. }
  298. }
  299. }
  300. taAccounts, err := models.GetTaAccountsByType(req.UserID, 3) // 获取内部资金账户,现货都使用内部资金账户
  301. if err != nil {
  302. // 查询失败
  303. logger.GetLogger().Errorf("QuerySpotContractAppleForm failed: %s", err.Error())
  304. appG.Response(http.StatusBadRequest, e.ERROR_QUERY_FAIL, nil)
  305. return
  306. }
  307. rsp.OurUser.AccountIDs = make([]int, 0)
  308. for _, v := range taAccounts {
  309. rsp.OurUser.AccountIDs = append(rsp.OurUser.AccountIDs, int(v.Accountid))
  310. }
  311. // 获取客户信息
  312. oppositeUsers, err := models.GetUsersByUserType(6)
  313. if err != nil {
  314. // 查询失败
  315. logger.GetLogger().Errorf("QuerySpotContractAppleForm failed: %s", err.Error())
  316. appG.Response(http.StatusBadRequest, e.ERROR_QUERY_FAIL, nil)
  317. return
  318. }
  319. rsp.OppositeUsers = make([]CustomerInfo, 0)
  320. for _, v := range oppositeUsers {
  321. // 把自己过滤掉
  322. if int(v.Userid) == req.UserID {
  323. continue
  324. }
  325. userInfo := CustomerInfo{}
  326. userInfo.Userid = v.Userid
  327. userInfo.Customername = v.Customername
  328. if len(v.Mobile) > 0 {
  329. // 手机号码解密
  330. if phonenum, err := hex.DecodeString(v.Mobile); err == nil { // hex -> []byte
  331. if mobile, err := utils.AESDecrypt(phonenum, key); err == nil {
  332. userInfo.Mobile = string(mobile)
  333. }
  334. }
  335. }
  336. // 获取客户的资金账户列表
  337. opposTaAccounts, err := models.GetTaAccountsByType(int(v.Userid), 3) // 获取内部资金账户,现货都使用内部资金账户
  338. if err != nil {
  339. // 查询失败
  340. logger.GetLogger().Errorf("QuerySpotContractAppleForm failed: %s", err.Error())
  341. appG.Response(http.StatusBadRequest, e.ERROR_QUERY_FAIL, nil)
  342. return
  343. }
  344. userInfo.AccountIDs = make([]int, 0)
  345. for _, v := range opposTaAccounts {
  346. userInfo.AccountIDs = append(userInfo.AccountIDs, int(v.Accountid))
  347. }
  348. rsp.OppositeUsers = append(rsp.OppositeUsers, userInfo)
  349. }
  350. // 获取交易标的(仓单标准)
  351. rsp.WrStandards = make([]models.Wrstandard, 0)
  352. wrStandards, err := models.GetWrstandards()
  353. if err != nil {
  354. // 查询失败
  355. logger.GetLogger().Errorf("QuerySpotContractAppleForm failed: %s", err.Error())
  356. appG.Response(http.StatusBadRequest, e.ERROR_QUERY_FAIL, nil)
  357. return
  358. }
  359. rsp.WrStandards = wrStandards
  360. // 获取仓库信息
  361. rsp.WareHouseInfos = make([]models.Warehouseinfo, 0)
  362. wareHouseInfos, err := models.GetWareHouseinfos()
  363. if err != nil {
  364. // 查询失败
  365. logger.GetLogger().Errorf("QuerySpotContractAppleForm failed: %s", err.Error())
  366. appG.Response(http.StatusBadRequest, e.ERROR_QUERY_FAIL, nil)
  367. return
  368. }
  369. rsp.WareHouseInfos = wareHouseInfos
  370. // 获取合约信息
  371. rsp.Goodses = make([]models.GoodsIDAndName, 0)
  372. goodses, err := models.GetGoodsInfosByTradeModes("15")
  373. if err != nil {
  374. // 查询失败
  375. logger.GetLogger().Errorf("QuerySpotContractAppleForm failed: %s", err.Error())
  376. appG.Response(http.StatusBadRequest, e.ERROR_QUERY_FAIL, nil)
  377. return
  378. }
  379. rsp.Goodses = goodses
  380. // 查询成功
  381. logger.GetLogger().Debugln("QuerySpotContractAppleForm successed: %v", rsp)
  382. appG.Response(http.StatusOK, e.SUCCESS, rsp)
  383. }