group.go 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988
  1. package service
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "math"
  7. "net/url"
  8. "sort"
  9. "strconv"
  10. "time"
  11. "go-common/app/admin/main/credit/model/blocked"
  12. "go-common/app/admin/main/workflow/model"
  13. "go-common/app/admin/main/workflow/model/param"
  14. "go-common/app/admin/main/workflow/model/search"
  15. "go-common/library/ecode"
  16. "go-common/library/log"
  17. "github.com/jinzhu/gorm"
  18. "golang.org/x/sync/errgroup"
  19. )
  20. // GroupListV3 .
  21. func (s *Service) GroupListV3(c context.Context, cond *search.GroupSearchCommonCond) (grpPage *model.GroupListPage, err error) {
  22. var (
  23. ok bool
  24. groupSearchCommonResp *search.GroupSearchCommonResp
  25. wg = &errgroup.Group{}
  26. )
  27. if groupSearchCommonResp, err = s.dao.SearchGroup(c, cond); err != nil {
  28. log.Error("Failed to s.dao.SearchGroup(%v): %v", cond, err)
  29. err = ecode.WkfSearchGroupFailed
  30. return
  31. }
  32. if len(groupSearchCommonResp.Result) == 0 {
  33. grpPage = &model.GroupListPage{
  34. Items: []*model.Group{},
  35. Page: &model.Page{},
  36. }
  37. return
  38. }
  39. eids, oids, gids, mids := []int64{}, []int64{}, []int64{}, []int64{}
  40. fut := make(map[int64]int64, len(groupSearchCommonResp.Result))
  41. for _, v := range groupSearchCommonResp.Result {
  42. gids = append(gids, v.ID)
  43. oids = append(oids, v.Oid)
  44. eids = append(eids, v.Eid)
  45. if v.Mid > 0 {
  46. mids = append(mids, v.Mid)
  47. }
  48. fut[v.ID] = v.FirstUserTid
  49. }
  50. // last chall mids
  51. cscc := &search.ChallSearchCommonCond{
  52. Fields: []string{"id", "gid", "mid"},
  53. Business: cond.Business,
  54. Gids: gids,
  55. Order: "ctime",
  56. Sort: "desc",
  57. Distinct: []string{"gid"},
  58. PS: 999,
  59. PN: 1,
  60. }
  61. var (
  62. cResp *search.ChallSearchCommonResp
  63. lastChalls = make(map[int64]*model.Chall)
  64. )
  65. if cResp, err = s.dao.SearchChallenge(c, cscc); err != nil {
  66. log.Error("s.dao.SearchChallenge(%v) error(%v)", cscc, err)
  67. } else {
  68. for _, r := range cResp.Result {
  69. if r.Mid > 0 {
  70. mids = append(mids, r.Mid)
  71. }
  72. lastChalls[r.Gid] = &model.Chall{Cid: r.ID, Mid: r.Mid}
  73. }
  74. }
  75. // group object
  76. var groups map[int64]*model.Group
  77. wg.Go(func() error {
  78. if groups, err = s.dao.Groups(c, gids); err != nil {
  79. log.Error("Failed to s.dao.Groups(%v): %v", gids, err)
  80. return err
  81. }
  82. return nil
  83. })
  84. // todo load data from db, search, external api
  85. // tag count of challenge
  86. var gidToChallTagCount map[int64]map[int64]int64
  87. wg.Go(func() error {
  88. // todo: count tag in es
  89. if gidToChallTagCount, err = s.dao.ChallTagsCountV3(c, gids); err != nil {
  90. log.Error("s.dao.ChallTagsCountV3(%v) error(%v)", gids, err)
  91. err = nil
  92. } else {
  93. log.Info("gidToChallTagCount (%+v)", gidToChallTagCount)
  94. }
  95. return nil
  96. })
  97. var grpLastLog map[int64]string
  98. wg.Go(func() error {
  99. if grpLastLog, err = s.LastLog(c, gids, []int{model.WLogModuleGroup, model.WLogModuleRoleShift}); err != nil {
  100. log.Error("s.LastLog(%v,%d) error(%v)", gids, model.WLogModuleGroup, err)
  101. err = nil
  102. }
  103. return nil
  104. })
  105. // search account
  106. var users map[int64]*model.Account
  107. wg.Go(func() error {
  108. users = s.dao.AccountInfoRPC(c, mids)
  109. //get uper group
  110. var uperTagMap map[int64][]*model.SpecialTag
  111. if uperTagMap, err = s.dao.BatchUperSpecial(c, mids); err != nil {
  112. log.Error("s.dao.BatchUperSpecial(%v) error(%v)", mids, err)
  113. err = nil
  114. } else {
  115. for id, user := range users {
  116. var st []*model.SpecialTag
  117. if st, ok = uperTagMap[id]; !ok {
  118. log.Warn("not find special tag mid(%d)", id)
  119. continue
  120. }
  121. user.SpecialTag = st
  122. }
  123. }
  124. return nil
  125. })
  126. // object table
  127. // todo: judge if need read local object
  128. var bus map[int64]*model.Business
  129. var archives map[int64]*model.Archive
  130. wg.Go(func() error {
  131. if bus, err = s.dao.BusObjectByGids(c, gids); err != nil {
  132. log.Error("s.dao.BusObjectByGids(%v) error(%v)", gids, err)
  133. err = nil
  134. }
  135. // search archive
  136. // todo: judge if need search archive
  137. aids := []int64{}
  138. for _, b := range bus {
  139. aids = append(aids, b.Oid)
  140. }
  141. if archives, err = s.dao.ArchiveRPC(c, aids); err != nil {
  142. log.Error("s.dao.ArchiveRPC(%v) error(%v)", oids, err)
  143. err = nil
  144. }
  145. return nil
  146. })
  147. // external meta
  148. var metas map[int64]*model.GroupMeta
  149. wg.Go(func() error {
  150. if metas, err = s.externalMeta(c, cond.Business, gids, oids, eids); err != nil {
  151. log.Error("s.ExternalMeta(%d,%v,%v,%v) error(%v)", cond.Business, gids, oids, eids, err)
  152. err = nil
  153. } else {
  154. log.Info("external meta (%+v)", metas)
  155. }
  156. return nil
  157. })
  158. // wait all wg.go()
  159. if err = wg.Wait(); err != nil {
  160. return
  161. }
  162. //todo make response
  163. grpPage = new(model.GroupListPage)
  164. rgs := make([]*model.Group, 0, len(groupSearchCommonResp.Result))
  165. for _, v := range groupSearchCommonResp.Result {
  166. var (
  167. rg *model.Group
  168. ok bool
  169. )
  170. if rg, ok = groups[v.ID]; !ok {
  171. log.Warn("Failed to retrive group by group id %d", v.ID)
  172. continue
  173. }
  174. if !dbCheck(cond, rg) {
  175. continue
  176. }
  177. rg.TypeID = v.TypeID
  178. // fill last log
  179. var l string
  180. if l, ok = grpLastLog[v.ID]; ok {
  181. rg.LastLog = l
  182. }
  183. // fill tag name
  184. // using tid from group row can ensure the lastest tid fetched
  185. if tid := rg.Tid; tid != 0 {
  186. var t *model.TagMeta
  187. if t, err = s.tag(rg.Business, tid); err != nil {
  188. log.Error("Failed to retrive tag by bid(%d) tag_id(%d)", rg.Business, tid)
  189. err = nil
  190. } else {
  191. rg.Tag = t.Name
  192. }
  193. }
  194. rg.ChallengeTags = model.ChallTagSlice{}
  195. var tc map[int64]int64
  196. if tc, ok = gidToChallTagCount[v.ID]; ok {
  197. total := int64(0)
  198. for _, count := range tc {
  199. total += count
  200. }
  201. for tid, c := range tc {
  202. tname := "<Unknow>"
  203. tround := int8(0)
  204. var t *model.TagMeta
  205. if t, err = s.tag(rg.Business, tid); err != nil {
  206. log.Error("Failed to retrive tag by bid(%d) tag_id(%d)", rg.Business, tid)
  207. err = nil
  208. } else {
  209. tname = t.Name
  210. tround = t.RID
  211. }
  212. ct := &model.ChallTag{
  213. ID: tid,
  214. Tag: tname,
  215. Count: c,
  216. Percent: 100,
  217. Round: tround,
  218. }
  219. if total != 0 {
  220. ct.Percent = round((float64(c) / float64(total)) * 100)
  221. }
  222. rg.ChallengeTags = append(rg.ChallengeTags, ct)
  223. }
  224. log.Warn("challenge tags of gid(%d) %+v", rg.ID, rg.ChallengeTags)
  225. } else {
  226. log.Warn("not found chall tag count of gid(%d)", v.ID)
  227. }
  228. sort.Sort(rg.ChallengeTags)
  229. // fill last producer
  230. var lc *model.Chall
  231. if lc, ok = lastChalls[rg.ID]; ok {
  232. if rg.LastProducer, ok = users[lc.Mid]; !ok {
  233. log.Warn("gid(%d) has last producer mid(%d) but not found account", rg.ID, lc.Mid)
  234. }
  235. log.Info("gid(%d) load last producer mid(%d) success (%+v)", rg.ID, lc.Mid, rg.LastProducer)
  236. } else {
  237. log.Warn("not found account of last producer gid(%d)", rg.ID)
  238. }
  239. // fill meta
  240. s.warpMeta(cond.Business, rg, metas, archives, users, bus)
  241. // oid to string
  242. rg.OidStr = strconv.FormatInt(rg.Oid, 10)
  243. // eid to string
  244. rg.EidStr = strconv.FormatInt(rg.Eid, 10)
  245. rgs = append(rgs, rg)
  246. // fill first_user_tid
  247. rg.FirstUserTid = fut[rg.ID]
  248. }
  249. grpPage.Items = rgs
  250. grpPage.Page = &model.Page{
  251. Num: groupSearchCommonResp.Page.Num,
  252. Size: groupSearchCommonResp.Page.Size,
  253. Total: groupSearchCommonResp.Page.Total,
  254. }
  255. return
  256. }
  257. // UpGroup will update a group
  258. func (s *Service) UpGroup(c context.Context, gp *param.GroupParam) (err error) {
  259. var (
  260. tx *gorm.DB
  261. l *model.WLog
  262. g *model.Group
  263. )
  264. // double write rid
  265. tMeta := &model.TagMeta{}
  266. if tMeta, err = s.tag(gp.Business, gp.Tid); err != nil {
  267. log.Error("TagListCache not found bid(%d) tag_id(%d)", gp.Business, gp.Tid)
  268. return
  269. }
  270. gp.Rid = tMeta.RID
  271. // Check group and tag is exist
  272. if g, err = s.dao.GroupByOid(c, gp.Oid, gp.Business); err != nil {
  273. log.Error("s.dao.GroupByOid(%d, %d) error(%v)", gp.Oid, gp.Business, err)
  274. return
  275. }
  276. if g == nil {
  277. log.Error("Group(%d, %d) not exist", gp.Oid, gp.Business)
  278. err = ecode.WkfGroupNotFound
  279. return
  280. }
  281. tx = s.dao.ORM.Begin()
  282. if err = tx.Error; err != nil {
  283. return
  284. }
  285. defer func() {
  286. if r := recover(); r != nil {
  287. tx.Rollback()
  288. log.Error("Service.UpGroup() panic(%v)", r)
  289. }
  290. }()
  291. if err = s.dao.TxUpGroup(tx, gp.Oid, gp.Business, gp.Tid, gp.Note, gp.Rid); err != nil {
  292. tx.Rollback()
  293. log.Error("s.TxUpGroup(%d, %d, %d, %s, %d) error(%v)", gp.Oid, gp.Business, gp.Tid, gp.Note, gp.Rid, err)
  294. return
  295. }
  296. if err = tx.Commit().Error; err != nil {
  297. tx.Rollback()
  298. log.Error("tx.Commit() error(%v)", err)
  299. return
  300. }
  301. s.task(func() {
  302. l = &model.WLog{
  303. AdminID: gp.AdminID,
  304. Admin: gp.AdminName,
  305. Oid: g.Oid,
  306. Business: g.Business,
  307. Target: g.ID,
  308. Module: model.WLogModuleGroup,
  309. Remark: fmt.Sprintf(`工单编号 %d “管理 Tag”更新为“%s”`, g.ID, tMeta.Name),
  310. Note: gp.Note,
  311. }
  312. s.writeAuditLog(l)
  313. })
  314. return
  315. }
  316. // UpGroupRole will 流转工单
  317. func (s *Service) UpGroupRole(c context.Context, grsp *param.GroupRoleSetParam) (err error) {
  318. var groups map[int64]*model.Group
  319. // Check group and tag is exist
  320. if groups, err = s.dao.Groups(c, grsp.GID); err != nil {
  321. log.Error("s.dao.Groups(%v) error(%v)", grsp.GID, err)
  322. return
  323. }
  324. if len(groups) == 0 {
  325. log.Error("Group(%v) not exist", grsp.GID)
  326. err = ecode.WkfGroupNotFound
  327. return
  328. }
  329. // check bid
  330. for _, g := range groups {
  331. if g.Business != grsp.BID {
  332. err = ecode.WkfBusinessNotConsistent
  333. return
  334. }
  335. }
  336. // check tid available
  337. var tMeta *model.TagMeta
  338. if tMeta, err = s.tag(grsp.BID, grsp.TID); err != nil {
  339. return
  340. }
  341. grsp.RID = tMeta.RID
  342. if err = s.dao.UpGroupRole(c, grsp); err != nil {
  343. log.Error("s.UpGroupRole(%+v) error(%v)", grsp, err)
  344. return
  345. }
  346. s.task(func() {
  347. s.afterSetGroupRole(grsp, groups)
  348. })
  349. return
  350. }
  351. // SetGroupResult will set a group result
  352. func (s *Service) SetGroupResult(c context.Context, grp *param.GroupResParam) (err error) {
  353. var (
  354. tx *gorm.DB
  355. g *model.Group
  356. tinyChalls map[int64]*model.TinyChall
  357. cids []int64
  358. )
  359. if g, err = s.dao.GroupByOid(c, grp.Oid, grp.Business); err != nil {
  360. log.Error("s.dao.GroupByOid() error(%v)", err)
  361. return
  362. }
  363. if g == nil {
  364. log.Error("Group(%d, %d) not exist", grp.Oid, grp.Business)
  365. err = ecode.NothingFound
  366. return
  367. }
  368. if g.State != model.Pending {
  369. log.Error("Group(%d, %d) not pending", grp.Oid, grp.Business)
  370. return
  371. }
  372. tx = s.dao.ORM.Begin()
  373. if err = tx.Error; err != nil {
  374. log.Error("s.dao.ORM.Begin() error(%v)", err)
  375. return
  376. }
  377. defer func() {
  378. if r := recover(); r != nil {
  379. tx.Rollback()
  380. log.Error("Service.SetGroupResult() panic(%v)", r)
  381. }
  382. }()
  383. // Update Group State
  384. if err = s.dao.TxUpGroupState(tx, g.ID, grp.State); err != nil {
  385. tx.Rollback()
  386. log.Error("s.txUpGroupState(%+v) error(%v)", g, err)
  387. return
  388. }
  389. // set grouo handling field to 0
  390. if err = s.dao.TxUpGroupHandling(tx, g.ID, 0); err != nil {
  391. tx.Rollback()
  392. log.Error("s.txUpGroupHandling(%+v, 0) error(%v)", g.ID, err)
  393. return
  394. }
  395. if err = tx.Commit().Error; err != nil {
  396. tx.Rollback()
  397. log.Error("tx.Commit() error(%v)", err)
  398. return
  399. }
  400. s.task(func() {
  401. var result []*search.ChallSearchCommonData
  402. cond := &search.ChallSearchCommonCond{
  403. Fields: []string{"id", "gid", "mid", "state", "ctime"},
  404. Gids: []int64{g.ID},
  405. States: []int64{int64(model.Pending)},
  406. }
  407. if result, err = s.dao.SearchChallengeMultiPage(context.Background(), cond); err != nil {
  408. log.Error("s.dao.SearchChallengeMultiPage(%+v) error(%v)", cond, err)
  409. return
  410. }
  411. tinyChalls = make(map[int64]*model.TinyChall, len(cids))
  412. for _, c := range result {
  413. cids = append(cids, c.ID)
  414. tc := &model.TinyChall{
  415. Cid: c.ID,
  416. Gid: c.Gid,
  417. Mid: c.Mid,
  418. }
  419. var ctime time.Time
  420. ctime, err = time.ParseInLocation("2006-01-02 15:04:05", c.CTime, time.Local)
  421. if err != nil {
  422. log.Error("time.Parse(%v) error(%v)", c.CTime, err)
  423. }
  424. tc.CTime.Scan(ctime)
  425. log.Info("tc.CTime.Scan(%v) get (%v) cid(%v) gid(%v)", ctime.Unix(), tc.CTime.Time(), tc.Cid, tc.Gid)
  426. if str, ok := c.State.(string); ok {
  427. st, _ := strconv.Atoi(str)
  428. tc.State = int8(st)
  429. }
  430. if f, ok := c.State.(float64); ok {
  431. tc.State = int8(math.Floor(f))
  432. }
  433. tinyChalls[c.ID] = tc
  434. }
  435. if err = s.dao.BatchUpChallByIDs(cids, uint32(grp.State), grp.AdminID); err != nil {
  436. log.Error("s.dao.TxBatchUpChallByIDs(%v,%d) error(%v)", cids, grp.State, err)
  437. return
  438. }
  439. s.afterSetGrpResult(grp, g, tinyChalls)
  440. })
  441. return
  442. }
  443. // BatchSetGroupResult will set a set of groups result
  444. func (s *Service) BatchSetGroupResult(c context.Context, bgrp *param.BatchGroupResParam) (err error) {
  445. var (
  446. tx *gorm.DB
  447. groups map[int64]*model.Group
  448. tinyChalls map[int64]*model.TinyChall
  449. gids []int64
  450. cids []int64
  451. )
  452. tx = s.dao.ORM.Begin()
  453. if err = tx.Error; err != nil {
  454. log.Error("s.dao.ORM.Begin() error(%v)", err)
  455. return
  456. }
  457. defer func() {
  458. if r := recover(); r != nil {
  459. tx.Rollback()
  460. log.Error("Service.BatchSetGroupResult() panic(%v)", r)
  461. }
  462. }()
  463. if groups, err = s.dao.TxGroupsByOidsStates(tx, bgrp.Oids, bgrp.Business, model.Pending); err != nil {
  464. log.Error("s.dao.TxGroupsByOidsStates() error(%v)", err)
  465. return
  466. }
  467. if len(groups) <= 0 {
  468. log.Warn("No pending groups found with conditon(%+v, %d, %d)", bgrp.Oids, bgrp.Business, model.Pending)
  469. return
  470. }
  471. // collect all gids
  472. for gid := range groups {
  473. gids = append(gids, int64(gid))
  474. }
  475. if err = s.dao.TxBatchUpGroupState(tx, gids, bgrp.State); err != nil {
  476. tx.Rollback()
  477. log.Error("s.TxBatchUpGroupState(%+v) error(%v)", bgrp, err)
  478. return
  479. }
  480. // Set group handling count to 0
  481. if err = s.dao.TxBatchUpGroupHandling(tx, gids, 0); err != nil {
  482. tx.Rollback()
  483. log.Error("s.TxBatchUpGroupHandling(%+v, 0) error(%v)", gids, err)
  484. return
  485. }
  486. if err = tx.Commit().Error; err != nil {
  487. tx.Rollback()
  488. log.Error("tx.Commit() error(%v)", err)
  489. return
  490. }
  491. s.task(func() {
  492. var result []*search.ChallSearchCommonData
  493. cond := &search.ChallSearchCommonCond{
  494. Fields: []string{"id", "gid", "mid", "state", "ctime"},
  495. Gids: gids,
  496. States: []int64{int64(model.Pending)},
  497. }
  498. if result, err = s.dao.SearchChallengeMultiPage(context.Background(), cond); err != nil {
  499. log.Error("s.dao.SearchChallengeMultiPage(%+v) error(%v)", cond, err)
  500. return
  501. }
  502. tinyChalls = make(map[int64]*model.TinyChall, len(cids))
  503. for _, c := range result {
  504. cids = append(cids, c.ID)
  505. tc := &model.TinyChall{
  506. Cid: c.ID,
  507. Gid: c.Gid,
  508. Mid: c.Mid,
  509. }
  510. var ctime time.Time
  511. ctime, err = time.ParseInLocation("2006-01-02 15:04:05", c.CTime, time.Local)
  512. if err != nil {
  513. log.Error("time.Parse(%v) error(%v)", c.CTime, err)
  514. }
  515. tc.CTime.Scan(ctime)
  516. log.Info("tc.CTime.Scan(%v) get (%v) cid(%v) gid(%v)", ctime.Unix(), tc.CTime.Time(), tc.Cid, tc.Gid)
  517. if str, ok := c.State.(string); ok {
  518. st, _ := strconv.Atoi(str)
  519. tc.State = int8(st)
  520. }
  521. if f, ok := c.State.(float64); ok {
  522. tc.State = int8(math.Floor(f))
  523. }
  524. tinyChalls[c.ID] = tc
  525. }
  526. if err = s.dao.BatchUpChallByIDs(cids, uint32(bgrp.State), bgrp.AdminID); err != nil {
  527. log.Error("s.dao.BatchUpChallByIDs(%v,%d) error(%v)", cids, bgrp.State, err)
  528. return
  529. }
  530. s.afterBatchSetGrpResult(bgrp, groups, tinyChalls)
  531. })
  532. return
  533. }
  534. // SetGroupState 修改未处理的工单状态
  535. func (s *Service) SetGroupState(c context.Context, gssp *param.GroupStateSetParam) (err error) {
  536. var (
  537. groups map[int64]*model.Group
  538. tinyChalls map[int64]*model.TinyChall
  539. gids, cids []int64
  540. newRid int8
  541. )
  542. //check tid 有效处理验证tid
  543. if gssp.State == model.Effective {
  544. var tmeta *model.TagMeta
  545. if tmeta, err = s.tag(gssp.Business, gssp.Tid); err != nil {
  546. return
  547. }
  548. newRid = tmeta.RID
  549. }
  550. tx := s.dao.ORM.Begin()
  551. if err = tx.Error; err != nil {
  552. log.Error("s.dao.ORM.Begin() error(%v)", err)
  553. return
  554. }
  555. defer func() {
  556. if r := recover(); r != nil {
  557. tx.Rollback()
  558. log.Error("s.SetGroupState() panic(%v)", r)
  559. }
  560. if err != nil {
  561. tx.Rollback()
  562. }
  563. }()
  564. if groups, err = s.dao.Groups(c, gssp.ID); err != nil {
  565. log.Error("s.dao.TxGroups(%v) error(%v)", gssp.ID, err)
  566. return
  567. }
  568. // ignore group if state not pending
  569. for id, g := range groups {
  570. if g.State != model.Pending || g.Business != gssp.Business || g.Rid != gssp.Rid {
  571. delete(groups, id)
  572. continue
  573. }
  574. gids = append(gids, id)
  575. }
  576. if len(gids) <= 0 {
  577. log.Warn("No settable groups found with conditon(%+v)", *gssp)
  578. err = ecode.WkfGroupNotFound
  579. return
  580. }
  581. // 有效处理同步修改tid & rid
  582. if gssp.State == model.Effective {
  583. if err = s.dao.TxSetGroupStateTid(tx, gids, gssp.State, newRid, gssp.Tid); err != nil {
  584. log.Error("s.TxSetGroupStateTid(%v,%d,%d) error(%v)", gids, gssp.State, gssp.Tid, err)
  585. return
  586. }
  587. } else {
  588. if err = s.dao.TxSimpleSetGroupState(tx, gids, gssp.State); err != nil {
  589. log.Error("s.TxSimpleSetGroupState(%v,%d,%d) error(%v)", gids, gssp.State, gssp.Tid, err)
  590. return
  591. }
  592. }
  593. // Set group handling count to 0
  594. if err = s.dao.TxBatchUpGroupHandling(tx, gids, 0); err != nil {
  595. log.Error("s.TxBatchUpGroupHandling(%+v, 0) error(%v)", gids, err)
  596. return
  597. }
  598. if err = tx.Commit().Error; err != nil {
  599. log.Error("tx.Commit() error(%v)", err)
  600. return
  601. }
  602. s.task(func() {
  603. // group bus object
  604. var gidToBus map[int64]*model.Business
  605. if gidToBus, err = s.dao.BusObjectByGids(context.Background(), gids); err != nil {
  606. log.Error("s.dao.BusObjectByGids(%v) error(%v)", gids, err)
  607. return
  608. }
  609. for _, g := range groups {
  610. g.BusinessObject = gidToBus[g.ID]
  611. }
  612. var result []*search.ChallSearchCommonData
  613. cond := &search.ChallSearchCommonCond{
  614. Fields: []string{"id", "gid", "mid", "state", "title", "oid", "tid", "business"},
  615. Gids: gids,
  616. States: []int64{int64(model.Pending)},
  617. }
  618. if result, err = s.dao.SearchChallengeMultiPage(context.Background(), cond); err != nil {
  619. log.Error("s.dao.SearchChallengeMultiPage(%+v) error(%v)", cond, err)
  620. return
  621. }
  622. tinyChalls = make(map[int64]*model.TinyChall, len(cids)) // map[id]*tc
  623. for _, c := range result {
  624. cids = append(cids, c.ID)
  625. tc := &model.TinyChall{
  626. Cid: c.ID,
  627. Gid: c.Gid,
  628. Mid: c.Mid,
  629. Title: c.Title,
  630. }
  631. if str, ok := c.State.(string); ok {
  632. st, _ := strconv.Atoi(str)
  633. tc.State = int8(st)
  634. }
  635. if f, ok := c.State.(float64); ok {
  636. tc.State = int8(math.Floor(f))
  637. }
  638. tinyChalls[c.ID] = tc
  639. }
  640. // async set challenge state
  641. if err = s.dao.BatchUpChallByIDs(cids, uint32(gssp.State), gssp.AdminID); err != nil {
  642. log.Error("s.dao.BatchUpChallByIDs(%v,%d) error(%v)", cids, gssp.State, err)
  643. return
  644. }
  645. s.afterSetGroupState(gssp, groups, tinyChalls)
  646. })
  647. return
  648. }
  649. // SetPublicReferee 移交众裁
  650. func (s *Service) SetPublicReferee(c context.Context, gspr *param.GroupStatePublicReferee) (err error) {
  651. tx := s.dao.ORM.Begin()
  652. if err = tx.Error; err != nil {
  653. log.Error("s.dao.ORM.Begin() error(%v)", err)
  654. return
  655. }
  656. defer func() {
  657. if r := recover(); r != nil {
  658. tx.Rollback()
  659. log.Error("s.SetPublicReferee() panic(%v)", r)
  660. }
  661. if err != nil {
  662. tx.Rollback()
  663. }
  664. }()
  665. var groups map[int64]*model.Group
  666. if groups, err = s.dao.TxGroups(tx, gspr.ID); err != nil {
  667. log.Error("s.dao.TxGroups(%v) error(%v)", gspr.ID, err)
  668. return
  669. }
  670. // ignore group if state not pending
  671. var gids, oids, eids []int64
  672. for id, g := range groups {
  673. if g.State != model.Pending || g.Business != gspr.Business {
  674. delete(groups, id)
  675. continue
  676. }
  677. gids = append(gids, id)
  678. oids = append(oids, g.Oid)
  679. eids = append(eids, g.Eid)
  680. }
  681. if len(gids) <= 0 {
  682. log.Warn("No pending groups found with conditon(%+v)", *gspr)
  683. err = ecode.WkfGroupNotFound
  684. return
  685. }
  686. // set state to public referee
  687. gspr.State = model.PublicReferee
  688. // start block add case
  689. // object
  690. var gidBus map[int64]*model.Business
  691. if gidBus, err = s.dao.BusObjectByGids(c, gids); err != nil {
  692. log.Error("s.dao.BusObjectByGids(%v) error(%v)", gids, err)
  693. err = ecode.WkfSetPublicRefereeFailed
  694. return
  695. }
  696. // external
  697. var metas map[int64]*model.GroupMeta
  698. if metas, err = s.externalMeta(c, gspr.Business, gids, oids, eids); err != nil {
  699. log.Error("s.SearchMeta(%d,%v,%v,%v) error(%v)", gspr.Business, gids, oids, eids, err)
  700. err = ecode.WkfSetPublicRefereeFailed
  701. return
  702. }
  703. var data []model.BlockCaseAdd
  704. for _, g := range groups {
  705. log.Info("start add case gid(%d)", g.ID)
  706. if _, ok := gidBus[g.ID]; !ok {
  707. log.Warn("gid(%d) not found bus object", g.ID)
  708. err = ecode.WkfSetPublicRefereeFailed
  709. return
  710. }
  711. bus := gidBus[g.ID]
  712. var (
  713. extra map[string]interface{}
  714. external map[string]interface{}
  715. link, title string
  716. ok bool
  717. ctime float64
  718. )
  719. if err = json.Unmarshal([]byte(bus.Extra), &extra); err != nil {
  720. log.Error("json.Unmarshal(%s) failed error(%v)", bus.Extra, err)
  721. err = ecode.WkfSetPublicRefereeFailed
  722. return
  723. }
  724. // redirect url
  725. if link, ok = extra["link"].(string); !ok {
  726. log.Error("gid(%d) assert business extra link failed", g.ID)
  727. err = ecode.WkfSetPublicRefereeFailed
  728. return
  729. }
  730. // object title
  731. if title, ok = extra["title"].(string); !ok {
  732. log.Error("gid(%d) assert business extra title failed", g.ID)
  733. err = ecode.WkfSetPublicRefereeFailed
  734. return
  735. }
  736. if _, ok = metas[g.ID]; !ok {
  737. log.Error("gid(%d) not found meta data", g.ID)
  738. err = ecode.WkfSetPublicRefereeFailed
  739. return
  740. }
  741. if external, ok = metas[g.ID].External.(map[string]interface{}); !ok {
  742. log.Error("gid(%d) external meta data assert failed", g.ID)
  743. err = ecode.WkfSetPublicRefereeFailed
  744. return
  745. }
  746. // business object ctime
  747. if ctime, ok = external["ctime"].(float64); !ok {
  748. log.Error("gid(%d) external ctime assert failed", g.ID)
  749. err = ecode.WkfSetPublicRefereeFailed
  750. return
  751. }
  752. d := model.BlockCaseAdd{
  753. RpID: g.Eid,
  754. Oid: g.Oid,
  755. Type: g.Fid,
  756. Mid: bus.Mid,
  757. Operator: gspr.AdminName,
  758. OperID: gspr.AdminID,
  759. OriginContent: bus.Title,
  760. ReasonType: g.Tid,
  761. BusinessTime: int64(ctime),
  762. OriginType: int64(blocked.OriginReply), // fixme: support multi business
  763. OriginTitle: title,
  764. OriginURL: link,
  765. }
  766. data = append(data, d)
  767. }
  768. // request credit
  769. var content []byte
  770. if content, err = json.Marshal(data); err != nil {
  771. log.Error("json.Marshal(%v) error(%v)", data, err)
  772. err = ecode.WkfSetPublicRefereeFailed
  773. return
  774. }
  775. uv := url.Values{}
  776. uv.Set("data", string(content))
  777. if err = s.dao.AddCreditCase(c, uv); err != nil {
  778. return
  779. }
  780. // set group set only
  781. if err = s.dao.TxSimpleSetGroupState(tx, gids, gspr.State); err != nil {
  782. log.Error("s.TxSimpleSetGroupState(%v,%d) error(%v)", gids, gspr.State, err)
  783. return
  784. }
  785. // set group handling count to 0
  786. if err = s.dao.TxBatchUpGroupHandling(tx, gids, 0); err != nil {
  787. log.Error("s.TxBatchUpGroupHandling(%+v, 0) error(%v)", gids, err)
  788. return
  789. }
  790. if err = tx.Commit().Error; err != nil {
  791. log.Error("tx.Commit() error(%v)", err)
  792. return
  793. }
  794. s.task(func() {
  795. // async set challenge state
  796. var (
  797. result []*search.ChallSearchCommonData
  798. cids []int64
  799. )
  800. cond := &search.ChallSearchCommonCond{
  801. Fields: []string{"id"},
  802. Gids: gids,
  803. States: []int64{int64(model.Pending)},
  804. }
  805. if result, err = s.dao.SearchChallengeMultiPage(context.Background(), cond); err != nil {
  806. log.Error("s.dao.SearchChallengeMultiPage(%+v) error(%v)", cond, err)
  807. return
  808. }
  809. for _, c := range result {
  810. cids = append(cids, c.ID)
  811. }
  812. if err = s.dao.BatchUpChallByIDs(cids, uint32(gspr.State), gspr.AdminID); err != nil {
  813. log.Error("s.dao.BatchUpChallByIDs(%v,%d) error(%v)", cids, gspr.State, err)
  814. return
  815. }
  816. s.afterSimpleSetState(gspr, groups)
  817. })
  818. return
  819. }
  820. // GroupPendingCount 当前 bid/rid 待办工单数
  821. func (s *Service) GroupPendingCount(c context.Context, cond *search.GroupSearchCommonCond) (gpc *model.GroupPendingCount, err error) {
  822. var groupSearchCommonResp *search.GroupSearchCommonResp
  823. if groupSearchCommonResp, err = s.dao.SearchGroup(c, cond); err != nil {
  824. log.Error("Failed to s.dao.SearchGroup(%v): %v", cond, err)
  825. err = ecode.WkfSearchGroupFailed
  826. return
  827. }
  828. gpc = &model.GroupPendingCount{
  829. Total: groupSearchCommonResp.Page.Total,
  830. }
  831. return
  832. }
  833. // ExternalMeta external dependency
  834. func (s *Service) externalMeta(c context.Context, business int8, gids, oids, eids []int64) (metas map[int64]*model.GroupMeta, err error) {
  835. metas = make(map[int64]*model.GroupMeta)
  836. // check if has external uri
  837. if _, ok := s.callbackCache[business]; !ok {
  838. return
  839. }
  840. uri := ""
  841. if uri = s.callbackCache[business].ExternalAPI; uri == "" {
  842. log.Warn("bid %d not found external api", business)
  843. return
  844. }
  845. // search meta
  846. //todo: common extra info
  847. var data map[string]interface{}
  848. if data, err = s.dao.CommonExtraInfo(c, business, uri, gids, oids, eids); err != nil {
  849. log.Error("s.dao.CommonExtraInfo() error(%v)", err)
  850. return
  851. }
  852. log.Info("bid(%d) external data(%v)", business, data)
  853. for gidStr, ext := range data {
  854. gid, _ := strconv.ParseInt(gidStr, 10, 64)
  855. metas[gid] = &model.GroupMeta{
  856. External: ext,
  857. }
  858. }
  859. return
  860. }
  861. // WarpMeta .
  862. func (s *Service) warpMeta(business int8, group *model.Group, metas map[int64]*model.GroupMeta, archives map[int64]*model.Archive, users map[int64]*model.Account, bus map[int64]*model.Business) {
  863. // not has external data
  864. if _, ok := metas[group.ID]; !ok {
  865. metas[group.ID] = &model.GroupMeta{}
  866. }
  867. var (
  868. b *model.Business
  869. ok bool
  870. )
  871. if b, ok = bus[group.ID]; ok {
  872. metas[group.ID].Object = b
  873. group.Defendant = users[b.Mid]
  874. metas[group.ID].Archive = archives[b.Oid]
  875. }
  876. group.MetaData = metas[group.ID]
  877. log.Info("WarpMeta gid(%d) oid(%d) eid(%d) meta.External(%+v)", group.ID, group.Oid, group.Eid, metas[group.ID].External)
  878. }
  879. // UpGroupExtra update business extra of gid, only cover business extra field
  880. func (s *Service) UpGroupExtra(c context.Context, uep *param.UpExtraParam) (err error) {
  881. if err = s.dao.UpExtraV3(uep.Gids, uep.AdminID, uep.Extra); err != nil {
  882. log.Error("s.dao.UpExtraV3(%v, %d, %v) error(%v)", uep.Gids, uep.AdminID, uep.Extra, err)
  883. }
  884. // todo: after up extra
  885. return
  886. }
  887. // dbCheck check group state & rid in db
  888. func dbCheck(cond *search.GroupSearchCommonCond, rg *model.Group) (isCheck bool) {
  889. var (
  890. isStateCheck bool
  891. isRidCheck bool
  892. )
  893. // check db state
  894. if len(cond.States) == 0 {
  895. isStateCheck = true
  896. } else {
  897. for _, state := range cond.States {
  898. if rg.State == state {
  899. isStateCheck = true
  900. break
  901. }
  902. }
  903. }
  904. // check rid
  905. if len(cond.RID) == 0 {
  906. isRidCheck = true
  907. } else {
  908. for _, rid := range cond.RID {
  909. if rg.Rid == rid {
  910. isRidCheck = true
  911. break
  912. }
  913. }
  914. }
  915. if isStateCheck && isRidCheck {
  916. isCheck = true
  917. }
  918. return
  919. }
  920. func round(num float64) int32 {
  921. return int32(num + math.Copysign(0.5, num))
  922. }