platform.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. package service
  2. import (
  3. "context"
  4. "go-common/app/admin/main/workflow/model"
  5. "go-common/app/admin/main/workflow/model/param"
  6. "go-common/app/admin/main/workflow/model/search"
  7. "go-common/library/ecode"
  8. "go-common/library/log"
  9. bm "go-common/library/net/http/blademaster"
  10. "github.com/pkg/errors"
  11. )
  12. // PlatformChallCount will return count of challenges which are backlog of an admin
  13. func (s *Service) PlatformChallCount(c context.Context, assigneeAdminID int64, permissionMap map[int8]int64) (challCount *search.ChallCount, err error) {
  14. var challSearchCommonResp *search.ChallSearchCommonResp
  15. if challCount, err = s.dao.ChallCountCache(c, assigneeAdminID); err != nil {
  16. log.Warn("s.dao.ChallCountCache(%d) error(%v)", assigneeAdminID, err)
  17. err = nil
  18. }
  19. if challCount != nil {
  20. return
  21. }
  22. // not fit cache, need to search es
  23. challCount = new(search.ChallCount)
  24. challCount.BusinessCount = make(map[int8]int64)
  25. for business, round := range permissionMap {
  26. cond := new(search.ChallSearchCommonCond)
  27. cond.Fields = []string{"id"}
  28. cond.Business = business
  29. cond.AssigneeAdminIDs = []int64{assigneeAdminID}
  30. cond.PN = 1
  31. cond.PS = 1000
  32. cond.Order = "id"
  33. cond.Sort = "desc"
  34. if round == model.FeedbackRound {
  35. cond.BusinessStates = []int64{0, 1}
  36. } else {
  37. cond.States = []int64{0}
  38. }
  39. if challSearchCommonResp, err = s.dao.SearchChallenge(c, cond); err != nil {
  40. log.Error("s.dao.SearchChallenge(%v) error(%v)", cond, err)
  41. return
  42. }
  43. challCount.BusinessCount[business] = int64(challSearchCommonResp.Page.Total)
  44. challCount.TotalCount += int64(challSearchCommonResp.Page.Total)
  45. }
  46. if err = s.dao.UpChallCountCache(c, challCount, assigneeAdminID); err != nil {
  47. log.Error("s.dao.UpChallCountCache(%d) error(%v)", assigneeAdminID, err)
  48. err = nil
  49. }
  50. return
  51. }
  52. // PlatformChallListPending will return challenges which are backlog of an admin
  53. func (s *Service) PlatformChallListPending(c context.Context, assigneeAdminID int64, permissionMap map[int8]int64, pclp *param.ChallListParam) (challPage *search.ChallListPageCommon, err error) {
  54. var (
  55. challSearchCommonResp *search.ChallSearchCommonResp
  56. cids []int64
  57. uids []int64
  58. challs map[int64]*model.Chall
  59. challLastLog map[int64]string
  60. challLastEvent map[int64]*model.Event
  61. attPaths map[int64][]string
  62. uNames map[int64]string
  63. attr *model.BusinessAttr
  64. rcids []int64
  65. exist bool
  66. ok bool
  67. pMeta map[int8]map[int64][]int64
  68. t *model.TagMeta
  69. l string
  70. gidToBus map[int64]*model.Business
  71. )
  72. rand := pclp.R
  73. log.Info("assignee_adminid(%d) call pending rand(%d)", assigneeAdminID, rand)
  74. pMetas := model.PlatformMetas()
  75. // todo: if assign type = 0 judge if admin is online
  76. if exist, err = s.dao.IsOnline(c, assigneeAdminID); err != nil {
  77. log.Info("s.dao.IsOnline(%d) error(%v)", assigneeAdminID, err)
  78. return
  79. }
  80. for i, business := range pclp.Businesses {
  81. pMeta, ok = pMetas[business]
  82. if !ok {
  83. log.Error("not read platform meta of business(%d)", business)
  84. }
  85. if attr, ok = s.busAttrCache[business]; !ok {
  86. log.Error("can not find business(%d) attr", business)
  87. continue
  88. }
  89. assignNum := pclp.AssignNum[i]
  90. // assignNum not allow over assignMax
  91. if assignNum > attr.AssignMax {
  92. assignNum = attr.AssignMax
  93. }
  94. round, ok := permissionMap[business]
  95. if !ok {
  96. log.Warn("uid(%d) not has permission of business(%d) rand(%d)", assigneeAdminID, business, rand)
  97. continue
  98. }
  99. // need get mission from redis list (not assigneed)
  100. if attr.AssignType == 0 {
  101. // assignType == 0 need judge checkin
  102. if !exist {
  103. log.Info("uid(%d) not checkin platform rand(%d)", assigneeAdminID, rand)
  104. continue
  105. }
  106. // get mission from es first
  107. cond := &search.ChallSearchCommonCond{
  108. Fields: []string{"id"},
  109. AssigneeAdminIDs: []int64{assigneeAdminID},
  110. PS: int(assignNum),
  111. PN: pclp.PN,
  112. }
  113. if round >= model.AuditRoundMin && round <= model.AuditRoundMax {
  114. cond.Business = business
  115. cond.States, ok = pMeta[0][0]
  116. if !ok {
  117. continue
  118. }
  119. }
  120. if round == model.FeedbackRound { //feedback flow
  121. cond.Business = business
  122. cond.BusinessStates, ok = pMeta[0][1]
  123. if !ok {
  124. continue
  125. }
  126. }
  127. if challSearchCommonResp, err = s.dao.SearchChallenge(c, cond); err != nil {
  128. log.Error("s.dao.SearchChallenge(%+v) error(%v)", cond, err)
  129. return
  130. }
  131. for _, r := range challSearchCommonResp.Result {
  132. cids = append(cids, r.ID)
  133. }
  134. log.Warn("uid(%d) has mission in db, cids:(%v) rand(%d)", assigneeAdminID, cids, rand)
  135. assignNum = assignNum - int8(len(challSearchCommonResp.Result))
  136. if assignNum <= 0 {
  137. log.Warn("uid(%d) cids:(%v) business(%d) round(%d) not need consume, continue rand(%d)", assigneeAdminID, cids, business, round, rand)
  138. continue
  139. }
  140. log.Warn("uid(%d) wanna consume redis business(%d) round(%d) num(%d) already has cids(%v) rand(%d)", assigneeAdminID, business, round, assignNum, cids, rand)
  141. // mission from redis
  142. rcids, err = s.dao.RedisRPOPCids(c, business, round, assignNum)
  143. if err != nil {
  144. log.Error("s.dao.RedisRPOPCids(%d,%d,%d) error(%v)", business, round, assignNum, err)
  145. err = errors.WithStack(err)
  146. return nil, err
  147. }
  148. tx := s.dao.ORM.Begin()
  149. if tx.Error != nil {
  150. return
  151. }
  152. defer func() {
  153. if r := recover(); r != nil {
  154. tx.Rollback()
  155. log.Error("s.PlatformChallListPending() panic(%v)", r)
  156. }
  157. }()
  158. // set dispatch_time if consume mission from redis
  159. if err = s.dao.TxUpChallAssignee(tx, rcids); err != nil {
  160. log.Error("s.dao.TxUpChallAssignee(%v,%d)", rcids, assigneeAdminID)
  161. return nil, err
  162. }
  163. log.Warn("uid(%d) consume cids(%v) business(%d) round(%d) rand(%d)", assigneeAdminID, rcids, business, round, rand)
  164. cids = append(cids, rcids...)
  165. // set challenge business_state to pending
  166. if err = s.dao.TxUpChallsBusStateByIDs(tx, rcids, 1, assigneeAdminID); err != nil {
  167. log.Error("s.dao.TxUpChallsBusStateByIDs(%v,%d,%d)", cids, 1, assigneeAdminID)
  168. return nil, err
  169. }
  170. if err = tx.Commit().Error; err != nil {
  171. tx.Rollback()
  172. log.Error("Failed to tx.Commit(): %v", err)
  173. return
  174. }
  175. } else if attr.AssignType == 1 { // get mission only from es search (already assigneed)
  176. cond := &search.ChallSearchCommonCond{
  177. Fields: []string{"id"},
  178. AssigneeAdminIDs: []int64{assigneeAdminID},
  179. PS: int(assignNum),
  180. PN: 1,
  181. }
  182. if round >= model.AuditRoundMin && round <= model.AuditRoundMax {
  183. cond.Business = business
  184. cond.States = pMetas[business][0][0]
  185. } else {
  186. continue //feedback flow not support assign type 0
  187. }
  188. if challSearchCommonResp, err = s.dao.SearchChallenge(c, cond); err != nil {
  189. log.Error("s.dao.SearchChallenge(%+v) error(%v)", cond, err)
  190. return
  191. }
  192. for _, r := range challSearchCommonResp.Result {
  193. cids = append(cids, r.ID)
  194. }
  195. }
  196. }
  197. log.Info("after a pending uid(%d) rand(%d)", assigneeAdminID, rand)
  198. challPage = &search.ChallListPageCommon{}
  199. if len(cids) == 0 {
  200. challPage.Items = make([]*model.Chall, 0)
  201. challPage.Page = &model.Page{
  202. Num: pclp.PN,
  203. Size: pclp.PS,
  204. Total: 0,
  205. }
  206. return
  207. }
  208. if challs, err = s.dao.Challs(c, cids); err != nil {
  209. log.Error("s.dao.Challs(%v) error(%v)", cids, err)
  210. return
  211. }
  212. if challLastLog, err = s.LastLog(c, cids, []int{model.WLogModuleChallenge, model.WLogModuleReply}); err != nil {
  213. log.Error("s.batchLastLog(%v,%v) error(%v)", cids, model.WLogModuleChallenge, err)
  214. err = nil
  215. }
  216. if attPaths, err = s.dao.AttPathsByCids(c, cids); err != nil {
  217. log.Error("s.dao.AttPathsByCids() error(%v)", err)
  218. return
  219. }
  220. cond := &search.ChallSearchCommonCond{
  221. Fields: []string{"id", "gid"},
  222. IDs: cids,
  223. PS: 1000,
  224. PN: 1,
  225. }
  226. if challSearchCommonResp, err = s.dao.SearchChallenge(c, cond); err != nil {
  227. log.Error("s.dao.SearchChallenge(%+v) error(%v)", cond, err)
  228. return
  229. }
  230. var gids []int64
  231. for _, r := range challSearchCommonResp.Result {
  232. gids = append(gids, r.Gid)
  233. }
  234. if gidToBus, err = s.dao.BusObjectByGids(c, gids); err != nil {
  235. log.Error("s.dao.BusObjectByGids(%v) error(%v)", gids, err)
  236. return
  237. }
  238. if challLastEvent, err = s.batchLastEvent(c, cids); err != nil {
  239. log.Error("s.batchLastEvent(%v) error(%v)", cids, err)
  240. return
  241. }
  242. for _, c := range challs {
  243. uids = append(uids, int64(c.AdminID))
  244. uids = append(uids, int64(c.AssigneeAdminID))
  245. }
  246. if uNames, err = s.dao.BatchUNameByUID(c, uids); err != nil {
  247. log.Error("s.dao.SearchUNameByUid(%v) error(%v)", uids, err)
  248. err = nil
  249. }
  250. challList := make([]*model.Chall, 0, len(challSearchCommonResp.Result))
  251. for _, cid := range cids {
  252. c, ok := challs[cid]
  253. if !ok {
  254. log.Warn("Invalid challenge id %d", cid)
  255. continue
  256. }
  257. // fill tag
  258. if t, err = s.tag(c.Business, c.Tid); err != nil {
  259. log.Error("s.tag(%d,%d) error(%v)", c.Business, c.Tid, err)
  260. err = nil
  261. } else {
  262. c.Tag = t.Name
  263. c.Round = t.RID
  264. }
  265. // fill last log
  266. if l, ok = challLastLog[cid]; ok {
  267. c.LastLog = l
  268. }
  269. // fill last event
  270. c.LastEvent = challLastEvent[cid]
  271. // fill attachments
  272. c.Attachments = make([]string, 0)
  273. if ps, ok := attPaths[cid]; ok {
  274. c.Attachments = ps
  275. c.FixAttachments()
  276. }
  277. //fill business object
  278. if b, ok := gidToBus[c.Gid]; ok {
  279. c.BusinessObject = b
  280. } else {
  281. log.Warn("failed to find bus object gid(%d) cid(%d)", c.Gid, c.Cid)
  282. }
  283. c.AssigneeAdminName = uNames[c.AssigneeAdminID]
  284. c.AdminName = uNames[c.AdminID]
  285. c.FromState()
  286. challList = append(challList, c)
  287. }
  288. challPage.Items = challList
  289. challPage.Page = &model.Page{
  290. Num: challSearchCommonResp.Page.Num,
  291. Size: challSearchCommonResp.Page.Size,
  292. Total: len(cids),
  293. }
  294. return
  295. }
  296. // PlatformChallListHandlingDone list handling challenges of admin
  297. func (s *Service) PlatformChallListHandlingDone(c *bm.Context, pchlp *param.ChallHandlingDoneListParam, permissionMap map[int8]int64, assigneeAdminID int64, feature int8) (challPage interface{}, err error) {
  298. pMetas := model.PlatformMetas()
  299. business := pchlp.Businesses
  300. round := permissionMap[business]
  301. if _, ok := pMetas[business]; !ok { // business not in platform
  302. err = errors.Wrap(ecode.MethodNotAllowed, "business not in platform")
  303. return
  304. }
  305. if _, ok := pMetas[business][feature]; !ok { // business not has platform state
  306. err = errors.Wrap(ecode.MethodNotAllowed, "business not has platform state")
  307. return
  308. }
  309. cond := &search.ChallSearchCommonCond{
  310. Fields: []string{"id", "gid"},
  311. Business: business,
  312. AssigneeAdminIDs: []int64{assigneeAdminID},
  313. PS: pchlp.PS,
  314. PN: pchlp.PN,
  315. Sort: pchlp.Sort,
  316. Order: pchlp.Order,
  317. }
  318. if round >= model.AuditRoundMin && round <= model.AuditRoundMax { //audit flow
  319. cond.Business = business
  320. cond.States = pMetas[business][feature][0]
  321. }
  322. if round == model.FeedbackRound { //feedback flow
  323. cond.Business = business
  324. cond.BusinessStates = pMetas[business][feature][1]
  325. }
  326. return s.ChallsWrap(c, cond)
  327. }
  328. // PlatformChallListCreated list created challenges of admin
  329. func (s *Service) PlatformChallListCreated(c context.Context, cond *search.ChallSearchCommonCond) (challPage *search.ChallListPageCommon, err error) {
  330. return s.ChallsWrap(c, cond)
  331. }
  332. // PlatformRelease admin offline
  333. func (s *Service) PlatformRelease(c context.Context, permissionMap map[int8]int64, assigneeAdminID int64) (err error) {
  334. var (
  335. challSearchCommonResp *search.ChallSearchCommonResp
  336. cids []int64
  337. attr *model.BusinessAttr
  338. ok bool
  339. )
  340. cids = make([]int64, 0)
  341. for business, round := range permissionMap {
  342. cond := &search.ChallSearchCommonCond{
  343. Fields: []string{"id"},
  344. AssigneeAdminIDs: []int64{assigneeAdminID},
  345. PN: 1,
  346. PS: 1000,
  347. }
  348. if attr, ok = s.busAttrCache[business]; !ok {
  349. log.Error("can not find business(%d) attr", business)
  350. continue
  351. }
  352. if attr.AssignType == 1 {
  353. continue
  354. } else { //任务消费 退出需要释放待处理状态的工单
  355. if round == model.FeedbackRound { //客服
  356. cond.BusinessStates = []int64{0, 1}
  357. } else {
  358. cond.States = []int64{0}
  359. }
  360. cond.Business = business
  361. if challSearchCommonResp, err = s.dao.SearchChallenge(c, cond); err != nil {
  362. log.Error("s.dao.SearchChallenge(%v) error(%v)", cond, err)
  363. return
  364. }
  365. for _, r := range challSearchCommonResp.Result {
  366. cids = append(cids, r.ID)
  367. }
  368. }
  369. }
  370. if err = s.dao.BatchResetAssigneeAdminID(cids); err != nil {
  371. return
  372. }
  373. err = s.dao.DelOnline(c, assigneeAdminID)
  374. // add report
  375. log.Info("uid(%d) offline success err(%v)", assigneeAdminID, err)
  376. return
  377. }
  378. // PlatformCheckIn admin online
  379. func (s *Service) PlatformCheckIn(c context.Context, assigneeAdminID int64) (err error) {
  380. err = s.dao.AddOnline(c, assigneeAdminID)
  381. // add report
  382. log.Info("uid(%d) online success err(%v)", assigneeAdminID, err)
  383. return
  384. }
  385. // PlatformOnlineList .
  386. func (s *Service) PlatformOnlineList(c context.Context) (err error) {
  387. var onlineAdminIDs []int64
  388. if onlineAdminIDs, err = s.dao.ListOnline(c); err != nil {
  389. return
  390. }
  391. // search login/out time, last 24h operate
  392. s.dao.LogInOutTime(c, onlineAdminIDs)
  393. return
  394. }
  395. // ChallsWrap warp challenges list result
  396. func (s *Service) ChallsWrap(c context.Context, cond *search.ChallSearchCommonCond) (challPageCommon *search.ChallListPageCommon, err error) {
  397. var (
  398. challSearchCommonResp *search.ChallSearchCommonResp
  399. challLastLog map[int64]string
  400. challLastEvent map[int64]*model.Event
  401. attPaths map[int64][]string
  402. gidToBus map[int64]*model.Business
  403. uNames map[int64]string
  404. challs map[int64]*model.Chall
  405. cids []int64
  406. uids []int64
  407. gids []int64
  408. t *model.TagMeta
  409. l string
  410. )
  411. challSearchCommonResp, err = s.dao.SearchChallenge(c, cond)
  412. if err != nil {
  413. err = errors.WithStack(err)
  414. return nil, err
  415. }
  416. cids = make([]int64, 0)
  417. uids = make([]int64, 0, len(challSearchCommonResp.Result)*2)
  418. gids = make([]int64, 0)
  419. for _, r := range challSearchCommonResp.Result {
  420. cids = append(cids, r.ID)
  421. gids = append(gids, r.Gid)
  422. }
  423. challPageCommon = new(search.ChallListPageCommon)
  424. if len(cids) == 0 {
  425. challPageCommon.Items = make([]*model.Chall, 0)
  426. challPageCommon.Page = &model.Page{
  427. Num: cond.PN,
  428. Size: cond.PS,
  429. Total: 0,
  430. }
  431. return
  432. }
  433. if challs, err = s.dao.Challs(c, cids); err != nil {
  434. log.Error("s.dao.Challs(%v) error(%v)", cids, err)
  435. return
  436. }
  437. if challLastLog, err = s.LastLog(c, cids, []int{model.WLogModuleChallenge, model.WLogModuleReply}); err != nil {
  438. log.Error("s.batchLastLog(%v,%v) error(%v)", cids, model.WLogModuleChallenge, err)
  439. err = nil
  440. }
  441. if attPaths, err = s.dao.AttPathsByCids(c, cids); err != nil {
  442. log.Error("s.dao.AttPathsByCids() error(%v)", err)
  443. return
  444. }
  445. if gidToBus, err = s.dao.BusObjectByGids(c, gids); err != nil {
  446. log.Error("s.dao.BusObjectByGids(%v) error(%v)", gids, err)
  447. return
  448. }
  449. if challLastEvent, err = s.batchLastEvent(c, cids); err != nil {
  450. log.Error("s.batchLastEvent(%v) error(%v)", cids, err)
  451. return
  452. }
  453. for _, c := range challs {
  454. uids = append(uids, int64(c.AdminID))
  455. uids = append(uids, int64(c.AssigneeAdminID))
  456. }
  457. if uNames, err = s.dao.BatchUNameByUID(c, uids); err != nil {
  458. log.Error("s.dao.SearchUNameByUid(%v) error(%v)", uids, err)
  459. err = nil
  460. }
  461. challList := make([]*model.Chall, 0, len(cids))
  462. for _, cid := range cids {
  463. c, ok := challs[cid]
  464. if !ok {
  465. log.Warn("Invalid challenge id %d", cid)
  466. continue
  467. }
  468. // fill tag
  469. if t, err = s.tag(c.Business, c.Tid); err != nil {
  470. log.Error("s.tag(%d,%d) error(%v)", c.Business, c.Tid, err)
  471. err = nil
  472. } else {
  473. c.Tag = t.Name
  474. c.Round = t.RID
  475. }
  476. // fill last log
  477. if l, ok = challLastLog[cid]; ok {
  478. c.LastLog = l
  479. }
  480. // fill last event
  481. c.LastEvent = challLastEvent[cid]
  482. // fill attachments
  483. c.Attachments = make([]string, 0)
  484. if ps, ok := attPaths[cid]; ok {
  485. c.Attachments = ps
  486. c.FixAttachments()
  487. }
  488. //fill business object
  489. if b, ok := gidToBus[c.Gid]; ok {
  490. c.BusinessObject = b
  491. } else {
  492. log.Warn("failed to find bus object gid(%d) cid(%d)", c.Gid, c.Cid)
  493. }
  494. c.AssigneeAdminName = uNames[c.AssigneeAdminID]
  495. c.AdminName = uNames[c.AdminID]
  496. c.FromState()
  497. challList = append(challList, c)
  498. }
  499. challPageCommon.Items = challList
  500. challPageCommon.Page = &model.Page{
  501. Num: challSearchCommonResp.Page.Num,
  502. Size: challSearchCommonResp.Page.Size,
  503. Total: challSearchCommonResp.Page.Total,
  504. }
  505. return
  506. }