sync_orders.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. package jobs
  2. import (
  3. "context"
  4. "errors"
  5. "gadmin/config"
  6. "gadmin/internal/admin/consts"
  7. "gadmin/internal/gorm/model"
  8. "gadmin/internal/gorm/query"
  9. "gadmin/utility/player"
  10. "github.com/jinzhu/now"
  11. "github.com/sirupsen/logrus"
  12. "gorm.io/gorm"
  13. "gorm.io/gorm/clause"
  14. "os"
  15. "sync"
  16. "time"
  17. )
  18. var SyncOrders = new(jSyncOrders)
  19. const DelaySecond = 7200 // 延迟拉取2小時内的订单,防止订单未支付而过后又支付完成的情况 (ios)
  20. const DelayId = 50 // 延迟拉取前N个订单,防止订单未支付而过后又支付完成的情况 (安卓)
  21. type jSyncOrders struct {
  22. sync.Mutex
  23. }
  24. // Run
  25. // 存在问题:
  26. // 1. 通过尾数U...关联时,如果存在多个相同的关联,无法准备识别应该关联哪个
  27. // 2. 有个别是U无法准确关联 绝大部分是U+1
  28. // 3. 过早的订单存在直接无法关联问题
  29. func (j *jSyncOrders) Run() {
  30. logrus.Info("SyncOrders Run.....")
  31. if os.Getenv("GIN_MODE") != "release" && os.Getenv("ADMIN_IS_LOCAL") != "1" {
  32. logrus.Warnf("测试环境禁止同步")
  33. return
  34. }
  35. j.Lock()
  36. defer j.Unlock()
  37. //游戏服订单相关表
  38. //virtualpayorder表示游戏内订单 InGameOrders
  39. //wxpay_iosorder表示游戏外订单 OutOfGameOrders
  40. //wxpay_order存储订单状态
  41. for serverId, _ := range config.GDBGroup {
  42. j.syncInGameOrders(serverId)
  43. j.syncOutOfGameOrders(serverId)
  44. }
  45. //for serverId, _ := range config.GDBGroup {
  46. // j.syncIos(serverId)
  47. // j.syncAndroid(serverId)
  48. //}
  49. }
  50. func (j *jSyncOrders) syncInGameOrders(serverId int) {
  51. logrus.Infof("Load Sync In-Game-Orders; ServerId %d .....", serverId)
  52. //获取游戏服id
  53. DB, err := player.GetDBByServerID(serverId)
  54. if err != nil {
  55. logrus.Warningf("syncAndroid GetDBByServerID,err:%v", err)
  56. return
  57. }
  58. //PayOrderAndriod 原名virtualpayorder 表示游戏内订单
  59. //PayOrderIos 原名wxpay_iosorder 表示游戏外订单
  60. var (
  61. ctx = context.TODO()
  62. o = query.Use(config.GDBGroup[DB]).PayOrderAndriod
  63. m = o.WithContext(ctx)
  64. orderList []model.PayOrderAndriod
  65. lastOrder model.Order
  66. )
  67. //获取游戏内最后一笔订单时间/id
  68. lastOrderSyncInfo, err := j.getLastOrderIdByOrderWay(serverId, consts.InGameOrders)
  69. if err != nil {
  70. logrus.Warnf("getLastOrderTime err . %v", err)
  71. return
  72. }
  73. //获取近两个小时内的订单
  74. if lastOrderSyncInfo.ID != 0 {
  75. m = m.Where(o.CreateTime.Gte(lastOrderSyncInfo.LastCreateTime.Add(time.Hour * -2)))
  76. }
  77. if err = m.Scan(&orderList); err != nil {
  78. logrus.Warnf("orderList Scan err . %v", err)
  79. return
  80. }
  81. if len(orderList) == 0 {
  82. logrus.Warnf("没有需要同步的订单")
  83. return
  84. }
  85. uIds := make([]int64, 0)
  86. uIdsMap := make(map[int64]struct{})
  87. for _, order := range orderList {
  88. if _, ok := uIdsMap[order.Playerid]; ok {
  89. continue
  90. }
  91. uIdsMap[order.Playerid] = struct{}{}
  92. uIds = append(uIds, order.Playerid)
  93. }
  94. firstOrders, err := o.WithContext(ctx).Select(o.Playerid).Group(o.Playerid).Where(o.Playerid.In(uIds...)).Having(o.Playerid.Count().Eq(1)).Find()
  95. if err != nil {
  96. return
  97. }
  98. newOrder := make(map[int64]struct{})
  99. for _, order := range firstOrders {
  100. newOrder[order.ID] = struct{}{}
  101. }
  102. for _, order := range orderList {
  103. var (
  104. isNew int32 = 0
  105. status int32 = consts.OrderStatusNo
  106. orderSn = ""
  107. goodsID = int32(order.GoodsID)
  108. orderDate = ""
  109. payAt time.Time
  110. createdAt time.Time
  111. )
  112. if _, ok := newOrder[order.ID]; ok {
  113. isNew = 1
  114. }
  115. status = consts.OrderStateSuccess
  116. payAt = order.CreateTime
  117. orderDate = payAt.Format("2006-01-02")
  118. createdAt = payAt
  119. if payAt.IsZero() {
  120. payAt = time.Now().In(time.Local)
  121. }
  122. channelID := player.GetUserChannel(order.Playerid)
  123. userCreatedAt := player.GetUserStamp(order.Playerid)
  124. paymentType := consts.OrderPaymentType
  125. if order.Platform == 1 {
  126. paymentType = consts.OrderPaymentTypeWx
  127. } else if order.Platform == 2 {
  128. paymentType = consts.OrderPaymentTypeDy
  129. }
  130. platform := consts.OrderPlatform
  131. // 0:苹果游戏外微信支付 1:安卓游戏内支付 2:安卓游戏外微信支付 3:苹果游戏内支付
  132. if order.PayMethod == 3 {
  133. platform = consts.OrderPlatformIos
  134. } else if order.PayMethod == 1 || order.PayMethod == 2 {
  135. platform = consts.OrderPlatformAndroid
  136. }
  137. lastOrder = model.Order{
  138. ServerID: int32(serverId),
  139. Platform: int32(platform),
  140. PaymentType: int32(paymentType),
  141. RelatID: int32(order.ID),
  142. PlayerID: order.Playerid,
  143. GoodsID: goodsID,
  144. OrderSn: orderSn,
  145. OutTradeNo: order.BillNo,
  146. Money: float64(order.Balance-order.PayedBalance) / 100,
  147. IsNew: isNew,
  148. Status: status,
  149. Date: orderDate,
  150. PayAt: payAt,
  151. CreatedAt: createdAt,
  152. ChannelID: channelID,
  153. UserCreatedAt: userCreatedAt,
  154. Flag: player.GetUserFlag(serverId, order.Playerid),
  155. }
  156. if err = j.saveOrder(lastOrder); err != nil {
  157. logrus.Warnf("Android saveOrder Scan err: %v; order:%+v; lastOrder:%+v;\n", err, order, lastOrder)
  158. return
  159. }
  160. }
  161. if err = j.setLastOrderTime(serverId, lastOrder, consts.InGameOrders, lastOrderSyncInfo.ID); err != nil {
  162. logrus.Warnf("setLastOrderTime Scan err . %v", err)
  163. return
  164. }
  165. }
  166. func (j *jSyncOrders) syncOutOfGameOrders(serverId int) {
  167. logrus.Infof("Load Sync Out-Of-Game-Orders; ServerId: %d .....", serverId)
  168. lastOrderSyncInfo, err := j.getLastOrderIdByOrderWay(serverId, consts.OutOfGameOrders)
  169. if err != nil {
  170. logrus.Warnf("getLastOrderTime err . %v", err)
  171. return
  172. }
  173. DB, err := player.GetDBByServerID(serverId)
  174. if err != nil {
  175. logrus.Warningf("syncIos GetDBByServerID,err:%v", err)
  176. return
  177. }
  178. var (
  179. ctx = context.TODO()
  180. o = query.Use(config.GDBGroup[DB]).PayOrderIos
  181. m = o.WithContext(ctx)
  182. orderList []model.PayOrderIos
  183. lastOrder model.Order
  184. )
  185. //获取近两个小时内的订单
  186. if lastOrderSyncInfo.ID != 0 {
  187. m = m.Where(o.CreateTime.Gte(lastOrderSyncInfo.LastCreateTime.Add(time.Hour * -2)))
  188. }
  189. if err = m.Scan(&orderList); err != nil {
  190. logrus.Warnf("orderList Scan err . %v", err)
  191. return
  192. }
  193. if len(orderList) == 0 {
  194. logrus.Warnf("没有需要同步的订单")
  195. return
  196. }
  197. uIds := make([]int64, 0)
  198. uIdsMap := make(map[int64]struct{})
  199. for _, order := range orderList {
  200. if _, ok := uIdsMap[order.PlayerID]; ok {
  201. continue
  202. }
  203. uIdsMap[order.PlayerID] = struct{}{}
  204. uIds = append(uIds, order.PlayerID)
  205. }
  206. //查找第一次下单的用户
  207. newOrders := make(map[int64]struct{})
  208. firstOrders, err := o.WithContext(ctx).Select(o.PlayerID).Group(o.PlayerID).Where(o.PlayerID.In(uIds...)).Having(o.PlayerID.Count().Eq(1)).Find()
  209. if err != nil {
  210. return
  211. }
  212. for _, order := range firstOrders {
  213. newOrders[order.PlayerID] = struct{}{}
  214. }
  215. for _, order := range orderList {
  216. var (
  217. isNew int32 = 0
  218. )
  219. if _, ok := newOrders[order.PlayerID]; ok {
  220. isNew = 1
  221. }
  222. PayAt := time.Unix(0, 0).In(time.Local)
  223. if order.SuccessTime != "" {
  224. PayAt, _ = now.ParseInLocation(time.Local, order.SuccessTime)
  225. }
  226. if order.TradeState != "SUCESS" {
  227. continue
  228. }
  229. channelID := player.GetUserChannel(order.PlayerID)
  230. userCreatedAt := player.GetUserStamp(order.PlayerID)
  231. paymentType := consts.OrderPaymentType
  232. if order.Platform == 1 {
  233. paymentType = consts.OrderPaymentTypeWx
  234. } else if order.Platform == 2 {
  235. paymentType = consts.OrderPaymentTypeDy
  236. }
  237. platform := consts.OrderPlatform
  238. //order.Phone 1苹果 2安卓
  239. if order.Phone == 1 {
  240. platform = consts.OrderPlatformIos
  241. } else if order.Phone == 2 {
  242. platform = consts.OrderPlatformAndroid
  243. }
  244. lastOrder = model.Order{
  245. ServerID: int32(serverId),
  246. Platform: int32(platform),
  247. RelatID: int32(order.ID),
  248. PaymentType: int32(paymentType),
  249. PlayerID: order.PlayerID,
  250. GoodsID: order.GoodsID,
  251. OrderSn: order.OutTradeNo,
  252. OutTradeNo: order.TransactionID,
  253. Money: float64(order.Total) / 100,
  254. IsNew: isNew,
  255. Status: consts.OrderStatusSuccess,
  256. Date: order.CreateTime.Format("2006-01-02"),
  257. PayAt: PayAt,
  258. CreatedAt: order.CreateTime,
  259. ChannelID: channelID,
  260. UserCreatedAt: userCreatedAt,
  261. Flag: player.GetUserFlag(serverId, order.PlayerID),
  262. }
  263. if err = j.saveOrder(lastOrder); err != nil {
  264. logrus.Warnf("IOS saveOrder Scan err: %v; order:%+v; lastOrder:%+v;\n", err, order, lastOrder)
  265. return
  266. }
  267. }
  268. if err = j.setLastOrderTime(serverId, lastOrder, consts.OutOfGameOrders, lastOrderSyncInfo.ID); err != nil {
  269. logrus.Warnf("setLastOrderTime Scan err . %v", err)
  270. return
  271. }
  272. }
  273. //func (j *jSyncOrders) syncAndroid(serverId int) {
  274. // logrus.Info("load SyncAndroid .....")
  275. // lastId, err := j.getLastOrderId(serverId, consts.OrderPlatformAndroid)
  276. // if err != nil {
  277. // logrus.Warnf("getLastOrderTime err . %v", err)
  278. // return
  279. // }
  280. //
  281. // DB, err := player.GetDBByServerID(serverId)
  282. // if err != nil {
  283. // logrus.Warningf("syncAndroid GetDBByServerID,err:%v", err)
  284. // return
  285. // }
  286. //
  287. // var (
  288. // o = query.Use(config.GDBGroup[DB]).PayOrderAndriod
  289. // m = o.WithContext(context.TODO())
  290. // orderLst []model.PayOrderAndriod
  291. // lastOrder model.Order
  292. // )
  293. //
  294. // if lastId > 0 {
  295. // m = m.Where(o.ID.Gte(lastId))
  296. // }
  297. //
  298. // if err = m.Scan(&orderLst); err != nil {
  299. // logrus.Warnf("orderLst Scan err . %v", err)
  300. // return
  301. // }
  302. //
  303. // if len(orderLst) == 0 {
  304. // logrus.Warnf("没有需要同步的订单")
  305. // return
  306. // }
  307. //
  308. // for _, order := range orderLst {
  309. // var (
  310. // isNew int32 = 0
  311. // status int32 = consts.OrderStatusNo
  312. // dao = query.Use(config.GDBGroup[DB]).PayOrderAndriod
  313. // orderSn = ""
  314. // goodsID = int32(order.GoodsID)
  315. // orderDate = ""
  316. // payAt time.Time
  317. // createdAt time.Time
  318. // )
  319. //
  320. // first, err := dao.Where(dao.Playerid.Eq(order.Playerid)).First()
  321. // if err == nil && first != nil {
  322. // if first.ID == order.ID {
  323. // isNew = 1
  324. // }
  325. // }
  326. //
  327. // status = consts.OrderStateSuccess
  328. //
  329. // payAt = order.CreateTime
  330. // orderDate = payAt.Format("2006-01-02")
  331. // createdAt = payAt
  332. //
  333. // if payAt.IsZero() {
  334. // payAt = time.Now().In(time.Local)
  335. // }
  336. //
  337. // channelID := player.GetUserChannel(order.Playerid)
  338. // userCreatedAt := player.GetUserStamp(order.Playerid)
  339. //
  340. // lastOrder = model.Order{
  341. // ServerID: int32(serverId),
  342. // Platform: consts.OrderPlatformAndroid,
  343. // RelatID: int32(order.ID),
  344. // PaymentType: consts.OrderPaymentTypeWx,
  345. // PlayerID: order.Playerid,
  346. // GoodsID: goodsID,
  347. // OrderSn: orderSn,
  348. // OutTradeNo: order.BillNo,
  349. // Money: float64(order.Balance-order.PayedBalance) / 100,
  350. // IsNew: isNew,
  351. // Status: status,
  352. // Date: orderDate,
  353. // PayAt: payAt,
  354. // CreatedAt: createdAt,
  355. // ChannelID: channelID,
  356. // UserCreatedAt: userCreatedAt,
  357. // Flag: player.GetUserFlag(serverId, order.Playerid),
  358. // }
  359. //
  360. // if err = j.saveOrder(lastOrder); err != nil {
  361. // logrus.Warnf("Android saveOrder Scan err: %v; order:%+v; lastOrder:%+v;\n", err, order, lastOrder)
  362. // return
  363. // }
  364. // }
  365. //
  366. // if err = j.setLastOrderTime(serverId, consts.OrderPlatformAndroid, lastOrder); err != nil {
  367. // logrus.Warnf("setLastOrderTime Scan err . %v", err)
  368. // return
  369. // }
  370. //}
  371. //
  372. //func (j *jSyncOrders) syncIos(serverId int) {
  373. // logrus.Info("load SyncIos .....")
  374. // lastTime, err := j.getLastOrderTime(serverId, consts.OrderPlatformIos)
  375. // if err != nil {
  376. // logrus.Warnf("getLastOrderTime err . %v", err)
  377. // return
  378. // }
  379. //
  380. // DB, err := player.GetDBByServerID(serverId)
  381. // if err != nil {
  382. // logrus.Warningf("syncIos GetDBByServerID,err:%v", err)
  383. // return
  384. // }
  385. //
  386. // var (
  387. // o = query.Use(config.GDBGroup[DB]).PayOrderIos
  388. // m = o.WithContext(context.TODO())
  389. // orderLst []model.PayOrderIos
  390. // lastOrder model.Order
  391. // )
  392. //
  393. // if lastTime.Unix() > 0 {
  394. // m = m.Where(o.CreateTime.Gte(lastTime.Add(-time.Second * DelaySecond)))
  395. // }
  396. //
  397. // if err = m.Scan(&orderLst); err != nil {
  398. // logrus.Warnf("orderLst Scan err . %v", err)
  399. // return
  400. // }
  401. //
  402. // if len(orderLst) == 0 {
  403. // logrus.Warnf("没有需要同步的订单")
  404. // return
  405. // }
  406. //
  407. // for _, order := range orderLst {
  408. // var (
  409. // isNew int32 = 0
  410. // status int32 = consts.OrderStatusNo
  411. // dao = query.Use(config.GDBGroup[DB]).PayOrderIos
  412. // )
  413. //
  414. // first, err := query.Use(config.GDBGroup[DB]).PayOrderIos.Select(dao.ALL).Where(dao.TradeState.Eq("SUCESS"), dao.PlayerID.Eq(order.PlayerID)).First()
  415. // if err == nil && first != nil {
  416. // if first.ID == order.ID {
  417. // isNew = 1
  418. // }
  419. // }
  420. //
  421. // payAt, err := now.ParseInLocation(time.Local, order.SuccessTime)
  422. // if err != nil {
  423. // payAt = time.Now().In(time.Local)
  424. // logrus.Errorf("order successtime parse err: %v; payAt: %v;\n", err, payAt)
  425. // }
  426. //
  427. // if order.TradeState == "SUCESS" {
  428. // status = consts.OrderStatusSuccess
  429. // } else if order.TradeState == "CLOSED" {
  430. // status = consts.OrderStatusCancel
  431. // } else if order.TradeState == "NOTENOUGH" {
  432. // status = consts.OrderStatusNotEnough
  433. // } else {
  434. // status = consts.OrderStatusOther
  435. // }
  436. //
  437. // channelID := player.GetUserChannel(order.PlayerID)
  438. // userCreatedAt := player.GetUserStamp(order.PlayerID)
  439. //
  440. // lastOrder = model.Order{
  441. // ServerID: int32(serverId),
  442. // Platform: consts.OrderPlatformIos,
  443. // RelatID: int32(order.ID),
  444. // PaymentType: consts.OrderPaymentTypeWx,
  445. // PlayerID: order.PlayerID,
  446. // GoodsID: order.GoodsID,
  447. // OrderSn: order.OutTradeNo,
  448. // OutTradeNo: order.TransactionID,
  449. // Money: float64(order.Total) / 100,
  450. // IsNew: isNew,
  451. // Status: status,
  452. // Date: order.CreateTime.Format("2006-01-02"),
  453. // PayAt: payAt,
  454. // CreatedAt: order.CreateTime,
  455. // ChannelID: channelID,
  456. // UserCreatedAt: userCreatedAt,
  457. // Flag: player.GetUserFlag(serverId, order.PlayerID),
  458. // }
  459. //
  460. // if err = j.saveOrder(lastOrder); err != nil {
  461. // logrus.Warnf("IOS saveOrder Scan err: %v; order:%+v; lastOrder:%+v;\n", err, order, lastOrder)
  462. // return
  463. // }
  464. // }
  465. //
  466. // if err = j.setLastOrderTime(serverId, consts.OrderPlatformIos, lastOrder); err != nil {
  467. // logrus.Warnf("setLastOrderTime Scan err . %v", err)
  468. // return
  469. // }
  470. //}
  471. // getLastOrderTime 获取下次同步订单的截止时间,主要用于ios
  472. func (j *jSyncOrders) getLastOrderTime(serverId int, platform int32) (last time.Time, err error) {
  473. var (
  474. q = query.Use(config.DB).OrdersSync
  475. models model.OrdersSync
  476. )
  477. if err = q.Where(q.Platform.Eq(platform), q.ServerID.Eq(int32(serverId))).Scan(&models); err != nil {
  478. return
  479. }
  480. return models.LastCreateTime, nil
  481. }
  482. // getLastOrderTime 获取下次同步订单的截止ID,主要用于安卓
  483. func (j *jSyncOrders) getLastOrderId(serverId int, platform int32) (last int64, err error) {
  484. var (
  485. q = query.Use(config.DB).OrdersSync
  486. models model.OrdersSync
  487. )
  488. if err = q.Where(q.Platform.Eq(platform), q.ServerID.Eq(int32(serverId))).Scan(&models); err != nil {
  489. return
  490. }
  491. if models.LastID == 0 {
  492. return 0, nil
  493. }
  494. return models.LastID - DelayId, nil
  495. }
  496. func (j *jSyncOrders) getLastOrderIdByOrderWay(serverId int, orderWay int) (lastOrder *model.OrdersSync, err error) {
  497. var (
  498. q = query.Use(config.DB).OrdersSync
  499. )
  500. res, err := q.Where(q.ServerID.Eq(int32(serverId)), q.OrderWay.Eq(int32(orderWay))).First()
  501. if err != nil {
  502. if errors.Is(err, gorm.ErrRecordNotFound) {
  503. return &model.OrdersSync{}, nil
  504. }
  505. return
  506. }
  507. return res, nil
  508. }
  509. func (j *jSyncOrders) setLastOrderTime(serverId int, data model.Order, orderWay int, lastSyncInfoId int64) (err error) {
  510. var (
  511. q = query.Use(config.DB).OrdersSync
  512. )
  513. syncData := &model.OrdersSync{
  514. LastID: int64(data.RelatID),
  515. LastCreateTime: data.CreatedAt,
  516. UpdatedAt: time.Now(),
  517. }
  518. if lastSyncInfoId != 0 {
  519. _, err := q.Where(q.ID.Eq(lastSyncInfoId)).Updates(syncData)
  520. if err != nil {
  521. return err
  522. }
  523. } else {
  524. syncData.ServerID = int32(serverId)
  525. syncData.OrderWay = int32(orderWay)
  526. err := q.Create(syncData)
  527. if err != nil {
  528. return err
  529. }
  530. }
  531. return
  532. }
  533. func (j *jSyncOrders) saveOrder(data model.Order) (err error) {
  534. //var (
  535. // q = query.Use(config.DB).Order
  536. // models model.Order
  537. //)
  538. //
  539. //if err = q.Where(q.ServerID.Eq(data.ServerID), q.RelatID.Eq(data.RelatID), q.Platform.Eq(data.Platform)).Scan(&models); err != nil {
  540. // return err
  541. //}
  542. //
  543. //if models.ID > 0 {
  544. // if _, err = query.Use(config.DB).Order.Where(q.RelatID.Eq(data.RelatID), q.Platform.Eq(data.Platform)).Updates(&data); err != nil {
  545. // return err
  546. // }
  547. // return nil
  548. //} else {
  549. // return query.Use(config.DB).Order.Create(&data)
  550. //}
  551. return query.Use(config.DB).Order.Clauses(clause.OnConflict{UpdateAll: true}).Create(&data)
  552. }