offlineactivity.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  1. package service
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "github.com/jinzhu/gorm"
  7. "github.com/pkg/errors"
  8. "go-common/app/admin/main/growup/conf"
  9. "go-common/app/admin/main/growup/dao"
  10. "go-common/app/admin/main/growup/dao/shell"
  11. "go-common/app/admin/main/growup/model/offlineactivity"
  12. "go-common/library/ecode"
  13. "go-common/library/log"
  14. "go-common/library/net/http/blademaster"
  15. "io/ioutil"
  16. "math/rand"
  17. "sort"
  18. "strconv"
  19. "strings"
  20. "time"
  21. )
  22. const (
  23. uploadFilePath = "/data/uploadfiles/"
  24. dateFmt = "20060102"
  25. exportMaxCount = 1000
  26. )
  27. func (s *Service) offlineactivityCheckSendDbProc() {
  28. defer func() {
  29. if r := recover(); r != nil {
  30. r = errors.WithStack(r.(error))
  31. log.Error("write stat data Runtime error caught, try recover: %+v", r)
  32. go s.offlineactivityCheckSendDbProc()
  33. }
  34. }()
  35. var timer = time.NewTicker(60 * time.Second)
  36. for {
  37. select {
  38. case <-timer.C:
  39. case <-s.chanCheckDb:
  40. }
  41. s.checkResultNotSendingOrder()
  42. }
  43. }
  44. func (s *Service) offlineactivityCheckShellOrderProc() {
  45. defer func() {
  46. if r := recover(); r != nil {
  47. r = errors.WithStack(r.(error))
  48. log.Error("write stat data Runtime error caught, try recover: %+v", r)
  49. go s.offlineactivityCheckShellOrderProc()
  50. }
  51. }()
  52. var timer = time.NewTimer(10 * time.Second)
  53. var timerStart = false
  54. var resultIds []int64
  55. for {
  56. select {
  57. case orderID := <-s.chanCheckShellOrder:
  58. var all = []*offlineactivity.OfflineActivityResult{orderID}
  59. for {
  60. drain:
  61. for {
  62. select {
  63. case orderID := <-s.chanCheckShellOrder:
  64. all = append(all, orderID)
  65. if len(all) >= 100 {
  66. break drain
  67. }
  68. default:
  69. break drain
  70. }
  71. }
  72. s.checkShellOrder(context.Background(), all)
  73. if len(all) < 100 {
  74. break
  75. }
  76. all = nil
  77. }
  78. case resultID := <-s.chanCheckActivity:
  79. if !timerStart {
  80. timer.Reset(10 * time.Second)
  81. timerStart = true
  82. }
  83. resultIds = append(resultIds, resultID)
  84. case <-timer.C:
  85. // 去检查对应活动的状态
  86. timerStart = false
  87. var ids []int64
  88. ids = append(ids, resultIds...)
  89. s.checkActivityState(context.Background(), ids)
  90. // clear map
  91. resultIds = nil
  92. }
  93. }
  94. }
  95. //PreAddOfflineActivity just test add resutl
  96. func (s *Service) PreAddOfflineActivity(ctx context.Context, arg *offlineactivity.AddActivityArg) (res *offlineactivity.PreAddActivityResult, err error) {
  97. if arg == nil {
  98. return
  99. }
  100. res = new(offlineactivity.PreAddActivityResult)
  101. var bonusList = arg.BonusList
  102. var hasBonus = false
  103. res.BonusType = arg.BonusType
  104. for _, bonus := range bonusList {
  105. if bonus.TotalMoney <= 0 {
  106. err = fmt.Errorf("bonus money < 0, money=%f", bonus.TotalMoney)
  107. log.Error(err.Error())
  108. return
  109. }
  110. res.TotalMoney += bonus.TotalMoney
  111. if bonus.Filename != "" {
  112. var fullpath = uploadFilePath + bonus.Filename
  113. var filecontent []byte
  114. filecontent, err = ioutil.ReadFile(fullpath)
  115. if err != nil {
  116. log.Error("read file fail, path=%s", fullpath)
  117. return
  118. }
  119. bonus.Mids = string(filecontent)
  120. bonus.MidList = dao.ParseMidsFromString(bonus.Mids)
  121. bonus.MemberCount = int64(len(bonus.MidList))
  122. if bonus.MemberCount == 0 {
  123. log.Warn("no mid for this bonus, bonus:money=%d", bonus.TotalMoney)
  124. continue
  125. }
  126. } else {
  127. bonus.MidList = dao.ParseMidsFromString(bonus.Mids)
  128. bonus.MemberCount = int64(len(bonus.MidList))
  129. if bonus.MemberCount == 0 {
  130. log.Warn("no mid for this bonus, bonus:money=%d", bonus.TotalMoney)
  131. continue
  132. }
  133. }
  134. res.MemberCount += bonus.MemberCount
  135. hasBonus = true
  136. }
  137. // 如果一个bonus都没有,则什么也不做
  138. if !hasBonus {
  139. log.Error("no bonus")
  140. err = fmt.Errorf("没有获奖人员信息/解析文件失败(请上传csv/txt等文本格式文件)")
  141. return
  142. }
  143. return
  144. }
  145. //AddOfflineActivity add offline activity
  146. func (s *Service) AddOfflineActivity(ctx context.Context, arg *offlineactivity.AddActivityArg) (res *offlineactivity.AddActivityResult, err error) {
  147. if arg == nil {
  148. return
  149. }
  150. var bonusList = arg.BonusList
  151. var hasBonus = false
  152. var bc = ctx.(*blademaster.Context)
  153. var cookie, _ = bc.Request.Cookie("username")
  154. if cookie != nil {
  155. arg.Creator = cookie.Value
  156. }
  157. for _, bonus := range bonusList {
  158. if bonus.TotalMoney <= 0 {
  159. err = fmt.Errorf("bonus money < 0, money=%f", bonus.TotalMoney)
  160. log.Error(err.Error())
  161. return
  162. }
  163. if bonus.Filename != "" {
  164. var fullpath = uploadFilePath + bonus.Filename
  165. var filecontent []byte
  166. filecontent, err = ioutil.ReadFile(fullpath)
  167. if err != nil {
  168. log.Error("read file fail, path=%s", fullpath)
  169. return
  170. }
  171. bonus.Mids = string(filecontent)
  172. } else {
  173. bonus.MidList = dao.ParseMidsFromString(bonus.Mids)
  174. bonus.MemberCount = int64(len(bonus.MidList))
  175. if bonus.MemberCount == 0 {
  176. log.Warn("no mid for this bonus, bonus:money=%d", bonus.TotalMoney)
  177. continue
  178. }
  179. }
  180. hasBonus = true
  181. }
  182. // 如果一个bonus都没有,则什么也不做
  183. if !hasBonus {
  184. log.Error("no bonus")
  185. err = fmt.Errorf("no bonus info")
  186. return
  187. }
  188. go func() {
  189. err = s.dao.OfflineActivityAddActivity(context.Background(), arg)
  190. if err != nil {
  191. log.Error("offline add fail")
  192. return
  193. }
  194. log.Info("offline add ok")
  195. if len(s.chanCheckDb) == 0 {
  196. s.chanCheckDb <- 1
  197. }
  198. }()
  199. return
  200. }
  201. //ShellCallback shell callback
  202. func (s *Service) ShellCallback(ctx context.Context, arg *shell.OrderCallbackParam) (err error) {
  203. if arg == nil {
  204. log.Error("arg is nil")
  205. return ecode.RequestErr
  206. }
  207. var result = shell.OrderCallbackJSON{}
  208. if err = json.Unmarshal([]byte(arg.MsgContent), &result); err != nil {
  209. log.Error("msgid=%s, unmarshal msg content fail, err=%s, msgcontent=%s", err, arg.MsgID, string(arg.MsgContent))
  210. return
  211. }
  212. log.Info("order id=%s, handle shell callback, status=%s", result.ThirdOrderNo, result.Status)
  213. if result.CustomerID != conf.Conf.ShellConf.CustomID {
  214. log.Error("order id=%s, customerid not the same, give=%s, expect=%s", result.ThirdOrderNo, result.CustomerID, conf.Conf.ShellConf.CustomID)
  215. return
  216. }
  217. orderInfo, err := s.dao.ShellCallbackUpdate(ctx, &result, arg.MsgID)
  218. if err == nil {
  219. s.queueToUpdateActivityState(orderInfo.ResultID)
  220. }
  221. return
  222. }
  223. //OfflineActivityQueryActivity query activity
  224. func (s *Service) OfflineActivityQueryActivity(ctx context.Context, arg *offlineactivity.QueryActivityByIDArg) (res *offlineactivity.QueryActivityResult, err error) {
  225. var db = s.dao.OfflineActivityGetDB()
  226. // make sure it's valid
  227. var limit, offset = arg.CheckPageValidation()
  228. if arg.ExportFormat() != "" {
  229. limit = exportMaxCount
  230. }
  231. var now = time.Now()
  232. if arg.FromDate == "" {
  233. arg.FromDate = now.AddDate(0, -1, 0).Format(dateFmt)
  234. }
  235. if arg.ToDate == "" {
  236. arg.ToDate = now.Format(dateFmt)
  237. }
  238. // 1.查询 OfflineActivityInfo 与 OfflineActivityBonus 数据,然后聚合
  239. var activityList []*offlineactivity.OfflineActivityInfo
  240. var total = 1
  241. if arg.ID != 0 {
  242. if err = db.Where("id=?", arg.ID).Find(&activityList).Error; err != nil && err != gorm.ErrRecordNotFound {
  243. log.Error("fail to get from db, err=%s", err)
  244. return
  245. }
  246. total = len(activityList)
  247. } else {
  248. if arg.FromDate == "" || arg.ToDate == "" {
  249. log.Error("request error, fromdate or todate is nill")
  250. err = ecode.RequestErr
  251. return
  252. }
  253. var todate, e = time.Parse(dateFmt, arg.ToDate)
  254. err = e
  255. if err != nil {
  256. log.Error("todate format err, todate=%s, err=%s", arg.ToDate, err)
  257. return
  258. }
  259. todate = todate.AddDate(0, 0, 1)
  260. var todatestr = todate.Format(dateFmt)
  261. if err = db.Table(offlineactivity.TableOfflineActivityInfo).Where("ctime>=? and ctime<=?", arg.FromDate, todatestr).Count(&total).Error; err != nil {
  262. log.Error("fail to get from db, err=%s", err)
  263. return
  264. }
  265. if err = db.Where("ctime>=? and ctime<=?", arg.FromDate, todatestr).Order("id desc").Offset(offset).Limit(limit).Find(&activityList).Error; err != nil {
  266. log.Error("fail to get from db, err=%s", err)
  267. return
  268. }
  269. }
  270. var activityIDs []int64
  271. for _, v := range activityList {
  272. activityIDs = append(activityIDs, v.ID)
  273. }
  274. if len(activityList) == 0 {
  275. log.Warn("0 activity list")
  276. return
  277. }
  278. // 查询bonus info
  279. var bonusList []*offlineactivity.OfflineActivityBonus
  280. if err = db.Where("activity_id in (?)", activityIDs).Find(&bonusList).Error; err != nil {
  281. log.Error("fail to get from db, err=%s", err)
  282. return
  283. }
  284. res = new(offlineactivity.QueryActivityResult)
  285. var activityMap = make(map[int64]*offlineactivity.QueryActivityInfo, len(activityList))
  286. for _, v := range activityList {
  287. var info = new(offlineactivity.QueryActivityInfo)
  288. info.CopyFromActivityDB(v)
  289. activityMap[info.ID] = info
  290. }
  291. for _, v := range bonusList {
  292. var info, _ = activityMap[v.ActivityID]
  293. if info == nil {
  294. continue
  295. }
  296. info.TotalMoney += offlineactivity.GetMoneyFromDb(v.TotalMoney)
  297. info.MemberCount += v.MemberCount
  298. }
  299. for _, v := range activityList {
  300. if info, ok := activityMap[v.ID]; ok {
  301. res.Result = append(res.Result, info)
  302. }
  303. }
  304. res.PageResult = arg.ToPageResult(total)
  305. return
  306. }
  307. // OfflineActivityQueryUpBonusSummary query up bonus info
  308. func (s *Service) OfflineActivityQueryUpBonusSummary(ctx context.Context, arg *offlineactivity.QueryUpBonusByMidArg) (res *offlineactivity.QueryUpBonusByMidResult, err error) {
  309. var limit, offset = arg.CheckPageValidation()
  310. if arg.ExportFormat() != "" {
  311. limit = exportMaxCount
  312. }
  313. // 查询所有的 result for mid
  314. // 区分已结算、未结算
  315. // 已结算= 成功, 未结算= 初始、发送、等待
  316. upResult, total, err := s.dao.OfflineActivityGetUpBonusResult(ctx, true, limit, offset, "mid=?", arg.Mid)
  317. if err != nil {
  318. log.Error("get from up result fail, err=%s", err)
  319. return
  320. }
  321. res = new(offlineactivity.QueryUpBonusByMidResult)
  322. res.PageResult = arg.ToPageResult(total)
  323. var bonusMap = make(map[int64]*offlineactivity.UpSummaryBonusInfo)
  324. for _, v := range upResult {
  325. var bonusInfo, ok = bonusMap[v.Mid]
  326. if !ok {
  327. bonusInfo = &offlineactivity.UpSummaryBonusInfo{
  328. Mid: v.Mid,
  329. }
  330. bonusMap[v.Mid] = bonusInfo
  331. }
  332. switch offlineactivity.ActivityState(v.State) {
  333. case offlineactivity.ActivityStateSucess:
  334. bonusInfo.BilledMoney += offlineactivity.GetMoneyFromDb(v.BonusMoney)
  335. if bonusInfo.TmpBillTime < v.MTime {
  336. bonusInfo.TmpBillTime = v.MTime
  337. }
  338. case offlineactivity.ActivityStateInit, offlineactivity.ActivityStateSending, offlineactivity.ActivityStateWaitResult:
  339. bonusInfo.UnbilledMoney += offlineactivity.GetMoneyFromDb(v.BonusMoney)
  340. }
  341. }
  342. for _, v := range bonusMap {
  343. v.Finish()
  344. res.Result = append(res.Result, v)
  345. }
  346. return
  347. }
  348. //OfflineActivityQueryUpBonusByActivity get bonus info group by activity
  349. func (s *Service) OfflineActivityQueryUpBonusByActivity(ctx context.Context, arg *offlineactivity.QueryUpBonusByMidArg) (res *offlineactivity.QueryUpBonusByActivityResult, err error) {
  350. var limit, offset = arg.CheckPageValidation()
  351. if arg.ExportFormat() != "" {
  352. limit = exportMaxCount
  353. }
  354. // 查询所有的 result for mid
  355. // 区分已结算、未结算
  356. // 已结算= 成功, 未结算= 初始、发送、等待
  357. upResult, total, err := s.dao.OfflineActivityGetUpBonusByActivityResult(ctx, limit, offset, arg.Mid)
  358. if err != nil {
  359. log.Error("get from up result fail, err=%s", err)
  360. return
  361. }
  362. res = new(offlineactivity.QueryUpBonusByActivityResult)
  363. res.PageResult = arg.ToPageResult(total)
  364. // [mid][activity_id], 按Activity做聚合
  365. var bonusMap = make(map[int64]map[int64]*offlineactivity.UpSummaryBonusInfo)
  366. for _, v := range upResult {
  367. var bonusActivityMap, ok = bonusMap[v.Mid]
  368. if !ok {
  369. bonusActivityMap = make(map[int64]*offlineactivity.UpSummaryBonusInfo)
  370. bonusMap[v.Mid] = bonusActivityMap
  371. }
  372. bonusInfo, ok := bonusActivityMap[v.ActivityID]
  373. if !ok {
  374. bonusInfo = &offlineactivity.UpSummaryBonusInfo{
  375. Mid: v.Mid,
  376. ActivityID: v.ActivityID,
  377. }
  378. bonusActivityMap[v.ActivityID] = bonusInfo
  379. }
  380. var bonusMoney = offlineactivity.GetMoneyFromDb(v.BonusMoney)
  381. switch offlineactivity.ActivityState(v.State) {
  382. case offlineactivity.ActivityStateSucess:
  383. bonusInfo.BilledMoney += bonusMoney
  384. if bonusInfo.TmpBillTime < v.MTime {
  385. bonusInfo.TmpBillTime = v.MTime
  386. }
  387. bonusInfo.TotalBonusMoney += bonusMoney
  388. case offlineactivity.ActivityStateInit, offlineactivity.ActivityStateSending, offlineactivity.ActivityStateWaitResult:
  389. bonusInfo.UnbilledMoney += bonusMoney
  390. bonusInfo.TotalBonusMoney += bonusMoney
  391. }
  392. }
  393. for _, activityMap := range bonusMap {
  394. for _, v := range activityMap {
  395. v.Finish()
  396. res.Result = append(res.Result, v)
  397. }
  398. }
  399. return
  400. }
  401. //OfflineActivityQueryActivityByMonth activity by month
  402. func (s *Service) OfflineActivityQueryActivityByMonth(ctx context.Context, arg *offlineactivity.QueryActvityMonthArg) (res *offlineactivity.QueryActivityMonthResult, err error) {
  403. var db = s.dao.OfflineActivityGetDB()
  404. var bonusInfo []*offlineactivity.OfflineActivityBonus
  405. var limit, offset = 100, 0
  406. var lastCount = limit
  407. for limit == lastCount {
  408. var bonusInfoTmp []*offlineactivity.OfflineActivityBonus
  409. if err = db.Find(&bonusInfoTmp).Error; err != nil {
  410. log.Error("get from db fail, err=%s", err)
  411. return
  412. }
  413. bonusInfo = append(bonusInfo, bonusInfoTmp...)
  414. lastCount = len(bonusInfoTmp)
  415. offset += lastCount
  416. }
  417. var now = time.Now()
  418. var dateStr = now.Format(dateFmt)
  419. var monthDataMap = make(map[string]*offlineactivity.ActivityMonthInfo)
  420. for _, v := range bonusInfo {
  421. var date = v.CTime.Time().Format("200601")
  422. var monthData, ok = monthDataMap[date]
  423. if !ok {
  424. monthData = &offlineactivity.ActivityMonthInfo{
  425. CreateTime: date,
  426. }
  427. monthDataMap[date] = monthData
  428. monthData.GenerateDay = dateStr
  429. }
  430. monthData.AddBonus(v)
  431. }
  432. var monthInfoS []*offlineactivity.ActivityMonthInfo
  433. for _, v := range monthDataMap {
  434. monthInfoS = append(monthInfoS, v)
  435. v.Finish()
  436. }
  437. sort.Slice(monthInfoS, func(i, j int) bool {
  438. return monthInfoS[i].CreateTime > monthInfoS[j].CreateTime
  439. })
  440. var lastIndex = len(monthInfoS) - 1
  441. if lastIndex >= 0 {
  442. monthInfoS[lastIndex].TotalMoneyAccumulate = monthInfoS[lastIndex].TotalBonusMoneyMonth
  443. }
  444. for i := len(monthInfoS) - 1; i > 0; i-- {
  445. monthInfoS[i-1].TotalMoneyAccumulate = monthInfoS[i].TotalMoneyAccumulate + monthInfoS[i-1].TotalBonusMoneyMonth
  446. }
  447. res = new(offlineactivity.QueryActivityMonthResult)
  448. res.Result = monthInfoS
  449. return
  450. }
  451. /*
  452. 查询对应的activity_result表state=0 && bonus_type = 1 && ctime <= 86400
  453. for each item state=0 and activity_id = ? limit 100
  454. if 没有order_id,
  455. 生成order_id,写入数据库, result表与shellorder表
  456. 写入成功,记录等待发送
  457. if 有order id,
  458. // 说明已经经历过上一步,但是发送失败?那么加入到检查order id的队列
  459. 记录等待发送
  460. 批量进行发送-> shell
  461. if 发送成功
  462. 更新所有的item状态 -> 1
  463. else if 失败:
  464. 错误码是8002999997->order_id有重复
  465. 更新所有的item order id = '',等待重试
  466. */
  467. func (s *Service) checkResultNotSendingOrder() {
  468. var db, err = gorm.Open("mysql", conf.Conf.ORM.Growup.DSN)
  469. if err != nil {
  470. log.Error("open db fail, dsn=%s", conf.Conf.ORM.Growup.DSN)
  471. return
  472. }
  473. defer db.Close()
  474. db.LogMode(false)
  475. // 查询对应的activity_result表state=0 && bonus_type = 1 && diff(ctime) <= 86400
  476. var minTime = time.Now().Add(-time.Hour * 24)
  477. var limit = 100
  478. var lastCount = limit
  479. var lastID int64
  480. for limit == lastCount {
  481. var needSendResult = make([]*offlineactivity.OfflineActivityResult, limit)
  482. if err = db.Select("id, mid, bonus_money, order_id, activity_id, bonus_id").
  483. Where("state=0 and bonus_type=1 and ctime>=? and id>?", minTime, lastID).Limit(limit).
  484. Find(&needSendResult).Error; err != nil {
  485. log.Error("get result fail, err=%s", err)
  486. return
  487. }
  488. lastCount = len(needSendResult)
  489. var now = time.Now()
  490. var sendRequestResult []*offlineactivity.OfflineActivityResult
  491. for _, activityResult := range needSendResult {
  492. if activityResult.ID > lastID {
  493. lastID = activityResult.ID
  494. }
  495. // if 没有order_id,
  496. if activityResult.OrderID == "" {
  497. activityResult.OrderID = generateOrderID(activityResult, now)
  498. if err = s.offlineActivityUpdateResultForSend(db, activityResult); err != nil {
  499. log.Warn("fail update result, err=%s, will retry next time", err)
  500. continue
  501. }
  502. log.Info("update result ok, mid=%d, activity_id=%d, order_id=%s", activityResult.Mid, activityResult.ActivityID, activityResult.OrderID)
  503. sendRequestResult = append(sendRequestResult, activityResult)
  504. } else {
  505. // if 有order id,
  506. go func(res *offlineactivity.OfflineActivityResult) {
  507. s.chanCheckShellOrder <- res
  508. log.Info("order has id, need to check, order id=%s, mid=%d, activityid=%d, bonusid=%d",
  509. res.OrderID, res.Mid, res.ActivityID, res.BonusID)
  510. }(activityResult)
  511. }
  512. if len(sendRequestResult) >= 10 {
  513. err = s.sendRequestAndUpdate(db, sendRequestResult)
  514. if err != nil {
  515. log.Error("send request err, err=%s", err)
  516. } else {
  517. log.Info("send to shell ok, length=%d", len(sendRequestResult))
  518. }
  519. sendRequestResult = nil
  520. }
  521. }
  522. if len(sendRequestResult) > 0 {
  523. err = s.sendRequestAndUpdate(db, sendRequestResult)
  524. if err != nil {
  525. log.Error("send request err, err=%s", err)
  526. } else {
  527. log.Info("send to shell ok, length=%d", len(sendRequestResult))
  528. }
  529. }
  530. }
  531. }
  532. func (s *Service) sendRequestAndUpdate(db *gorm.DB, sendRequestResult []*offlineactivity.OfflineActivityResult) (err error) {
  533. switch {
  534. default:
  535. var res *shell.OrderResponse
  536. if res, err = s.sendShellOrder(context.Background(), sendRequestResult); err != nil || res == nil {
  537. if err != nil {
  538. log.Error("fail to send to shell order, err=%s", err)
  539. }
  540. break
  541. }
  542. var ids []int64
  543. for _, v := range sendRequestResult {
  544. ids = append(ids, v.ID)
  545. }
  546. // 需要区分是否返回 错误码是8002999997,表示有重复
  547. if res.Errno == 8002999997 {
  548. log.Error("fail to send request, err=%d, msg=%s,重复!", res.Errno, res.Msg)
  549. // 将order id 更新为""
  550. if err = db.Table(offlineactivity.TableOfflineActivityResult).Where("id in (?)", ids).
  551. Update("order_id=''").Error; err != nil {
  552. log.Error("fail to update order id for duplicate order, id=%v", ids)
  553. break
  554. }
  555. return
  556. } else if res.Errno != 0 {
  557. log.Error("fail to send request, err=%d, msg=%s", res.Errno, res.Msg)
  558. return
  559. }
  560. if err = db.Table(offlineactivity.TableOfflineActivityResult).Where("id in (?)", ids).
  561. Update("state", offlineactivity.ActivityStateWaitResult).Error; err != nil {
  562. log.Error("fail to update state, id=%v", ids)
  563. break
  564. }
  565. log.Info("send request to shell order")
  566. }
  567. return
  568. }
  569. func generateOrderID(result *offlineactivity.OfflineActivityResult, tm time.Time) string {
  570. var order = fmt.Sprintf("%s%04d%010d%04d", time.Now().Format("20060102150405"), (result.ActivityID*100+result.BonusID)%10000, result.Mid, rand.Int()%10000)
  571. if len(order) > 32 {
  572. order = order[:32]
  573. }
  574. return order
  575. }
  576. func (s *Service) offlineActivityUpdateResultForSend(db *gorm.DB, result *offlineactivity.OfflineActivityResult) (err error) {
  577. if result == nil {
  578. err = fmt.Errorf("nil pointer")
  579. return
  580. }
  581. var tx = db.Begin()
  582. defer func() {
  583. if r := recover(); r != nil || err != nil {
  584. tx.Rollback()
  585. }
  586. }()
  587. // create
  588. var query = tx.Select("order_id").Where("order_id=''").Save(result)
  589. err = db.Error
  590. if err != nil {
  591. log.Error("err save offline result, err=%s", err)
  592. return
  593. }
  594. if query.RowsAffected == 0 {
  595. var msg = fmt.Sprintf("update order fail, it may already be sent, result_id=%d, mid=%d, activity_id=%d", result.ID, result.Mid, result.ActivityID)
  596. log.Warn(msg)
  597. err = fmt.Errorf(msg)
  598. return
  599. }
  600. var shellOrder = offlineactivity.OfflineActivityShellOrder{
  601. OrderID: result.OrderID,
  602. ResultID: result.ID,
  603. }
  604. if err = tx.Save(&shellOrder).Error; err != nil {
  605. log.Error("err insert offline shell order, err=%s", err)
  606. return
  607. }
  608. return tx.Commit().Error
  609. }
  610. func (s *Service) sendShellOrder(ctx context.Context, needSendResult []*offlineactivity.OfflineActivityResult) (res *shell.OrderResponse, err error) {
  611. if len(needSendResult) == 0 {
  612. log.Warn("no need to send, len=0")
  613. return
  614. }
  615. var nowtime = time.Now()
  616. var now = time.Now().UnixNano() / int64(time.Millisecond)
  617. var request = shell.OrderRequest{
  618. ProductName: "活动奖励",
  619. NotifyURL: conf.Conf.ShellConf.CallbackURL,
  620. Rate: "1.0",
  621. SignType: "MD5",
  622. Timestamp: strconv.Itoa(int(beginOfDay(nowtime).UnixNano() / int64(time.Millisecond))),
  623. }
  624. for _, item := range needSendResult {
  625. var money = fmt.Sprintf("%0.2f", float64(offlineactivity.GetMoneyFromDb(item.BonusMoney)))
  626. var orderInfo = shell.OrderInfo{
  627. Mid: item.Mid,
  628. Brokerage: money,
  629. ThirdCoin: money,
  630. ThirdOrderNo: item.OrderID,
  631. ThirdCtime: strconv.Itoa(int(now)),
  632. }
  633. request.Data = append(request.Data, orderInfo)
  634. }
  635. //log.Info("request=%+v", request)
  636. res, err = s.shellClient.SendOrderRequest(ctx, &request)
  637. if err != nil || res == nil {
  638. log.Error("fail to send request, err=%s", err)
  639. } else {
  640. log.Info("send shell request, msg=%s, errno=%d", res.Msg, res.Errno)
  641. }
  642. return
  643. }
  644. /*
  645. 2.定单查询 - 查询定单的状态,
  646. for each state=0 and 存在order id
  647. 去贝壳查询该定单状态
  648. if 定单不存在
  649. 设置order id='', state=0 // 清除定单,等待重新发送
  650. else if 定单成功
  651. 设置state=10
  652. else if 定单失败
  653. 设置state=11
  654. */
  655. // 最大不要超过100个订单
  656. func (s *Service) checkShellOrder(ctx context.Context, values []*offlineactivity.OfflineActivityResult) (err error) {
  657. if len(values) == 0 {
  658. err = fmt.Errorf("values slice 0 length")
  659. return
  660. }
  661. var orderMap = make(map[string]*offlineactivity.OfflineActivityResult, len(values))
  662. var orderIds []string
  663. for i, v := range values {
  664. if i > 99 {
  665. break
  666. }
  667. orderIds = append(orderIds, v.OrderID)
  668. orderMap[v.OrderID] = v
  669. }
  670. var orderIDString = strings.Join(orderIds, ",")
  671. var orderCheckRequest = shell.OrderCheckRequest{
  672. Timestamp: time.Now().UnixNano() / int64(time.Millisecond),
  673. ThirdOrderNos: orderIDString,
  674. }
  675. res, err := s.shellClient.SendCheckOrderRequest(ctx, &orderCheckRequest)
  676. if err != nil {
  677. log.Error("fail to check order, order id=%s, err=%s", orderIDString, err)
  678. return
  679. }
  680. // 区分找到的订单和未找到的订单
  681. // 订单找到,更新状态
  682. for _, v := range res.Orders {
  683. log.Info("order find, orderid=%s, current status=%s", v.ThirdOrderNo, v.Status)
  684. // 删除已找到的订单
  685. delete(orderMap, v.ThirdOrderNo)
  686. var resultJSON = shell.OrderCallbackJSON{
  687. Status: v.Status,
  688. ThirdOrderNo: v.ThirdOrderNo,
  689. Mid: v.Mid,
  690. }
  691. // 更新订单结果
  692. var orderInfo, e = s.dao.ShellCallbackUpdate(ctx, &resultJSON, "checkorder")
  693. if e == nil {
  694. s.queueToUpdateActivityState(orderInfo.ID)
  695. }
  696. }
  697. // 订单未找到,重新发送请求
  698. var needSendRequest []*offlineactivity.OfflineActivityResult
  699. for _, v := range orderMap {
  700. needSendRequest = append(needSendRequest, v)
  701. }
  702. err = s.sendRequestAndUpdate(s.dao.OfflineActivityGetDB(), needSendRequest)
  703. if err != nil {
  704. log.Error("send order request fail, err=%s", err)
  705. return
  706. }
  707. return
  708. }
  709. func (s *Service) checkActivityState(ctx context.Context, values []int64) (err error) {
  710. if len(values) == 0 {
  711. log.Warn("no activity need to check")
  712. return
  713. }
  714. //
  715. upResult, err := s.dao.OfflineActivityGetUpBonusResultSelect(ctx, "distinct(activity_id) as activity_id", "id in (?)", values)
  716. for _, v := range upResult {
  717. var _, e = s.dao.UpdateActivityState(ctx, v.ActivityID)
  718. if e != nil {
  719. log.Error("err when update activity state, err=%s", e)
  720. }
  721. }
  722. return
  723. }
  724. func (s *Service) queueToUpdateActivityState(resultID int64) {
  725. s.chanCheckActivity <- resultID
  726. }
  727. func beginOfDay(t time.Time) time.Time {
  728. year, month, day := t.Date()
  729. return time.Date(year, month, day, 0, 0, 0, 0, t.Location())
  730. }