log.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. package service
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "math"
  7. "strconv"
  8. "strings"
  9. "time"
  10. "go-common/app/admin/main/aegis/model"
  11. "go-common/app/admin/main/aegis/model/common"
  12. "go-common/app/admin/main/aegis/model/net"
  13. "go-common/app/admin/main/aegis/model/resource"
  14. "go-common/library/ecode"
  15. "go-common/library/log"
  16. "go-common/library/queue/databus/report"
  17. "go-common/library/xstr"
  18. )
  19. // send to log service
  20. func (s *Service) sendAuditLog(c context.Context, action string, opt *model.SubmitOptions, flowres interface{}, logtype int) (err error) {
  21. // send
  22. logData := &report.ManagerInfo{
  23. Uname: opt.Uname,
  24. UID: opt.UID,
  25. Business: model.LogBusinessAudit,
  26. Type: logtype,
  27. Oid: opt.RID,
  28. Action: action,
  29. Ctime: time.Now(),
  30. Index: []interface{}{opt.BusinessID, opt.NewFlowID, opt.TaskID, strconv.Itoa(opt.Result.State)},
  31. Content: map[string]interface{}{
  32. "opt": opt,
  33. "flow": flowres,
  34. },
  35. }
  36. if err = report.Manager(logData); err != nil {
  37. log.Error("report.Manager(%+v) error(%v)", logData, err)
  38. }
  39. return
  40. }
  41. // send to log service
  42. func (s *Service) sendTaskConsumerLog(c context.Context, action string, opt *common.BaseOptions) (err error) {
  43. logData := &report.ManagerInfo{
  44. Uname: opt.Uname,
  45. UID: opt.UID,
  46. Business: model.LogBusinessTask,
  47. Type: model.LogTypeTaskConsumer,
  48. Oid: 0,
  49. Action: action,
  50. Ctime: time.Now(),
  51. Index: []interface{}{opt.BusinessID, opt.FlowID, opt.Role},
  52. }
  53. if err = report.Manager(logData); err != nil {
  54. log.Error("report.Manager(%+v) error(%v)", logData, err)
  55. }
  56. return
  57. }
  58. // send to log service
  59. func (s *Service) sendRscLog(c context.Context, acction string, opt *model.AddOption, res *net.TriggerResult, update interface{}, err error) {
  60. var rid, flowid int64
  61. if res != nil {
  62. flowid = res.NewFlowID
  63. rid = res.RID
  64. }
  65. // send
  66. logData := &report.ManagerInfo{
  67. Uname: "business",
  68. UID: 399,
  69. Business: model.LogBusinessResource,
  70. Type: model.LogTypeFromAdd,
  71. Oid: rid,
  72. Action: acction,
  73. Ctime: time.Now(),
  74. Index: []interface{}{opt.BusinessID, flowid, opt.OID},
  75. Content: map[string]interface{}{
  76. "opt": opt,
  77. "res": res,
  78. "update": update,
  79. "err": err,
  80. },
  81. }
  82. if err1 := report.Manager(logData); err1 != nil {
  83. log.Error("report.Manager(%+v) error(%v)", logData, err1)
  84. }
  85. }
  86. func (s *Service) sendRscCancleLog(c context.Context, BusinessID int64, oids []string, uid int64, username string, err error) {
  87. logData := &report.ManagerInfo{
  88. Uname: username,
  89. UID: uid,
  90. Business: model.LogBusinessResource,
  91. Type: model.LogTypeFromCancle,
  92. Oid: 0,
  93. Action: "cancle",
  94. Ctime: time.Now(),
  95. Index: []interface{}{BusinessID},
  96. Content: map[string]interface{}{
  97. "oids": oids,
  98. "err": err,
  99. },
  100. }
  101. if err1 := report.Manager(logData); err1 != nil {
  102. log.Error("report.Manager(%+v) error(%v)", logData, err1)
  103. }
  104. }
  105. func (s *Service) sendRscSubmitLog(c context.Context, action string, opt *model.SubmitOptions, res interface{}) {
  106. logData := &report.ManagerInfo{
  107. Uname: opt.Uname,
  108. UID: opt.UID,
  109. Business: model.LogBusinessResource,
  110. Type: model.LogTypeFormAuditor,
  111. Oid: opt.RID,
  112. Action: action,
  113. Ctime: time.Now(),
  114. Index: []interface{}{opt.BusinessID, opt.FlowID, opt.OID},
  115. Content: map[string]interface{}{
  116. "opt": opt,
  117. "res": res,
  118. },
  119. }
  120. if err := report.Manager(logData); err != nil {
  121. log.Error("report.Manager(%+v) error(%v)", logData, err)
  122. }
  123. }
  124. /**
  125. * 记录流程流转日志
  126. * oid=rid,action=new_flow_id, index=[net_id, old_flow_id, transition_id, from], content=submit+result
  127. * from=单个提交/批量提交/跳流程/启动/取消
  128. */
  129. func (s *Service) sendNetTriggerLog(c context.Context, pm *net.TriggerResult) (err error) {
  130. var (
  131. submitValue, resValue []byte
  132. tran string
  133. content = map[string]interface{}{}
  134. )
  135. if len(pm.TransitionID) > 0 {
  136. tran = xstr.JoinInts(pm.TransitionID)
  137. }
  138. if pm.SubmitToken != nil {
  139. if submitValue, err = json.Marshal(pm.SubmitToken); err != nil {
  140. log.Error("sendNetTriggerLog json.Marshal error(%v) submit(%v)", err, pm.SubmitToken)
  141. return
  142. }
  143. content["submit"] = string(submitValue)
  144. }
  145. if pm.ResultToken != nil {
  146. if resValue, err = json.Marshal(pm.ResultToken); err != nil {
  147. log.Error("sendNetTriggerLog json.Marshal error(%v) result(%v)", err, pm.ResultToken)
  148. return
  149. }
  150. content["result"] = string(resValue)
  151. }
  152. data := &report.ManagerInfo{
  153. Business: model.LogBusinessNet,
  154. Type: model.LogTypeNetTrigger,
  155. Oid: pm.RID,
  156. Action: strconv.FormatInt(pm.NewFlowID, 10),
  157. Ctime: time.Now(),
  158. Index: []interface{}{pm.NetID, tran, pm.From, pm.OldFlowID},
  159. Content: content,
  160. }
  161. log.Info("sendNetTriggerLog start send log(%+v)", data)
  162. report.Manager(data)
  163. return
  164. }
  165. /**
  166. * oid: 各元素id, type=level, action=禁用/创建/更新/启用, ctime=time.now, index=[net_id, ch_name, flow_id, tran_id], content=diff
  167. * level in (net/token/token_bind_flow/token_bind_transition/flow/transition/direction)
  168. * diff如下:
  169. * token: obj=name+compare+value(type)
  170. * token_bind: obj=flow_chname/tran_chname:从token_obj变成token_obj, ch_name从xx变成xx
  171. * flow: ch_name从xx变成xx, name从xx变成xx
  172. * tran: ch_name从xx变成xx, name从xx变成xx,trigger从xx变成xx,limit从xx变成xx
  173. * dir:direction从xx变成xx,order从xx变成xx,guard从xx变成xx,output从xx变成yy
  174. *
  175. */
  176. func (s *Service) sendNetConfLog(c context.Context, tp int, oper *model.NetConfOper) (err error) {
  177. data := &report.ManagerInfo{
  178. UID: oper.UID,
  179. Uname: "",
  180. Business: model.LogBusinessNetConf,
  181. Type: tp,
  182. Oid: oper.OID,
  183. Action: oper.Action,
  184. Ctime: time.Now(),
  185. Index: []interface{}{oper.NetID, oper.FlowID, oper.TranID, oper.ChName},
  186. Content: map[string]interface{}{
  187. "diff": strings.Join(oper.Diff, "\r\n"),
  188. },
  189. }
  190. log.Info("sendNetConfLog data(%+v)", data)
  191. report.Manager(data)
  192. return
  193. }
  194. //SearchAuditLogCSV 操作日志结果csv
  195. func (s *Service) SearchAuditLogCSV(c context.Context, pm *model.SearchAuditLogParam) (csv [][]string, err error) {
  196. var (
  197. res []*model.SearchAuditLog
  198. )
  199. if res, _, err = s.SearchAuditLog(c, pm); err != nil {
  200. return
  201. }
  202. csv = make([][]string, len(res)+1)
  203. csv[0] = []string{"rid", "oid", "task id", "状态", "操作时间", "操作人", "其他信息"}
  204. for i, item := range res {
  205. csv[i+1] = []string{
  206. strconv.FormatInt(item.RID, 10),
  207. item.OID,
  208. strconv.FormatInt(item.TaskID, 10),
  209. item.State,
  210. item.Stime,
  211. fmt.Sprintf("%s(%s)", item.Uname, item.Department),
  212. item.Extra,
  213. }
  214. }
  215. return
  216. }
  217. //SearchAuditLog 查询审核日志
  218. func (s *Service) SearchAuditLog(c context.Context, pm *model.SearchAuditLogParam) (res []*model.SearchAuditLog, p common.Pager, err error) {
  219. var (
  220. logs *model.SearchLogResult
  221. ridoid, udepartment map[int64]string
  222. oidrid map[string]int64
  223. )
  224. p = common.Pager{
  225. Ps: pm.Ps,
  226. Pn: pm.Pn,
  227. }
  228. //oid转换成rid查询
  229. if len(pm.OID) > 0 {
  230. if oidrid, err = s.gorm.ResIDByOID(c, pm.BusinessID, pm.OID); err != nil {
  231. log.Error("SearchAuditLog s.gorm.ResIDByOID error(%+v) pm(%+v)", err, pm)
  232. return
  233. }
  234. if len(oidrid) == 0 {
  235. return
  236. }
  237. ridoid = map[int64]string{}
  238. for oid, rid := range oidrid {
  239. pm.RID = append(pm.RID, rid)
  240. ridoid[rid] = oid
  241. }
  242. }
  243. if logs, err = s.searchAuditLog(c, pm); err != nil {
  244. err = ecode.AegisSearchErr
  245. return
  246. }
  247. p.Total = logs.Page.Total
  248. if len(logs.Result) == 0 {
  249. return
  250. }
  251. uids := []int64{}
  252. rids := []int64{}
  253. unameuids := []int64{}
  254. uidunameexist := map[int64]string{}
  255. res = make([]*model.SearchAuditLog, len(logs.Result))
  256. for i, item := range logs.Result {
  257. if item.UID > 0 {
  258. uids = append(uids, item.UID)
  259. }
  260. oid, exist := ridoid[item.OID]
  261. if !exist {
  262. rids = append(rids, item.OID)
  263. }
  264. if item.Uname != "" {
  265. uidunameexist[item.UID] = item.Uname
  266. } else {
  267. item.Uname = uidunameexist[item.UID]
  268. }
  269. if item.Uname == "" && item.UID > 0 {
  270. unameuids = append(unameuids, item.UID)
  271. }
  272. change := &model.Change{}
  273. if err = json.Unmarshal([]byte(item.Extra), &change); err != nil {
  274. log.Error("searchAuditLog json.Unmarshal error(%v) extra(%s) pm(%+v)", err, item.Extra, pm)
  275. return
  276. }
  277. flowaction, submitopt := change.GetSubmitOper()
  278. res[i] = &model.SearchAuditLog{
  279. RID: item.OID,
  280. OID: oid,
  281. TaskID: item.Int2,
  282. State: item.Str0,
  283. Stime: item.Ctime,
  284. UID: item.UID,
  285. Uname: item.Uname,
  286. Department: "",
  287. Extra: fmt.Sprintf("操作详情:[%s]%s %s", item.Action, flowaction, submitopt),
  288. }
  289. }
  290. //由搜索结果提供了rid
  291. if len(rids) > 0 {
  292. if ridoid, err = s.gorm.ResOIDByID(c, rids); err != nil {
  293. return
  294. }
  295. }
  296. unames, _ := s.http.GetUnames(c, unameuids)
  297. udepartment, _ = s.http.GetUdepartment(c, uids)
  298. for _, item := range res {
  299. if item.OID == "" {
  300. item.OID = ridoid[item.RID]
  301. }
  302. if item.Uname == "" && item.UID > 0 {
  303. item.Uname = unames[item.UID]
  304. }
  305. item.Department = udepartment[item.UID]
  306. }
  307. return
  308. }
  309. func (s *Service) trackAuditLog(c context.Context, pm *model.SearchAuditLogParam) (res []*model.TrackAudit, err error) {
  310. var (
  311. logs *model.SearchLogResult
  312. flowch map[int64]string
  313. )
  314. res = []*model.TrackAudit{}
  315. if logs, err = s.searchAuditLog(c, pm); err != nil {
  316. log.Error("trackAuditLog s.searchAuditLog error(%v) pm(%+v)", err, pm)
  317. return
  318. }
  319. res = make([]*model.TrackAudit, len(logs.Result))
  320. flows := []int64{}
  321. for i, item := range logs.Result {
  322. change := &model.Change{}
  323. if err = json.Unmarshal([]byte(item.Extra), change); err != nil {
  324. log.Error("trackAuditLog json.Unmarshal error(%v) pm(%+v)", err, pm)
  325. return
  326. }
  327. res[i] = &model.TrackAudit{
  328. Ctime: item.Ctime,
  329. FlowID: []int64{},
  330. State: "",
  331. Uname: item.Uname,
  332. }
  333. if int(item.Type) == model.LogTypeAuditCancel {
  334. res[i].State = "删除"
  335. }
  336. if change.Flow == nil {
  337. continue
  338. }
  339. if int(item.Type) == model.LogTypeAuditCancel {
  340. var one []int64
  341. one, err = xstr.SplitInts(change.Flow.OldFlowID.String())
  342. if err != nil {
  343. log.Error("trackAuditLog xstr.SplitInts(%s) error(%v)", change.Flow.OldFlowID.String(), err)
  344. err = nil
  345. continue
  346. }
  347. if len(one) == 0 {
  348. continue
  349. }
  350. res[i].FlowID = one
  351. flows = append(flows, one...)
  352. continue
  353. }
  354. flows = append(flows, change.Flow.NewFlowID)
  355. res[i].FlowID = []int64{change.Flow.NewFlowID}
  356. if change.Flow.ResultToken != nil {
  357. res[i].State = change.Flow.ResultToken.ChName
  358. }
  359. }
  360. //get flows names
  361. if len(flows) == 0 {
  362. return
  363. }
  364. if flowch, err = s.gorm.ColumnMapString(c, net.TableFlow, "ch_name", flows, ""); err != nil {
  365. log.Error("trackAuditLog s.gorm.ColumnMapString error(%v) pm(%+v)", err, pm)
  366. return
  367. }
  368. for _, item := range res {
  369. fnames := make([]string, len(item.FlowID))
  370. for i, fid := range item.FlowID {
  371. fnames[i] = flowch[fid]
  372. }
  373. item.FlowName = strings.Join(fnames, ",")
  374. }
  375. return
  376. }
  377. //TrackResource 资源信息追踪, 获取资源add/update日志,并分页,以此为基准,获取对应时间端内的资源audit日志;若add/update日志只有不超过1页,则获取全部audit日志;超过1页,最后一页会返回剩余的全部audit日志
  378. func (s *Service) TrackResource(c context.Context, pm *model.TrackParam) (res *model.TrackInfo, p common.Pager, err error) {
  379. var (
  380. obj *resource.Resource
  381. rsc []*model.TrackRsc
  382. audit []*model.TrackAudit
  383. rela [][]int
  384. LogMinTime = "2018-11-01 10:00:00"
  385. )
  386. if obj, err = s.gorm.ResourceByOID(c, pm.OID, pm.BusinessID); err != nil || obj == nil {
  387. log.Error("TrackResource s.gorm.ResourceByOID error(%v)/not found, pm(%+v)", err, pm)
  388. return
  389. }
  390. if rsc, p, err = s.searchResourceLog(c, obj.ID, pm.Pn, pm.Ps); err != nil {
  391. err = ecode.AegisSearchErr
  392. return
  393. }
  394. //超过部分不需要查询audit
  395. topn := int(math.Ceil(float64(p.Total) / float64(p.Ps)))
  396. if (topn > 0 && topn < p.Pn) || (topn <= 0 && p.Pn > 1) {
  397. return
  398. }
  399. //没有资源日志,则不查询审核日志--资源日志添加失败,还是需要展示审核日志啊,审核日志分页有规律
  400. ap := &model.SearchAuditLogParam{
  401. BusinessID: pm.BusinessID,
  402. RID: []int64{obj.ID},
  403. CtimeFrom: LogMinTime,
  404. CtimeTo: "",
  405. Ps: 1000, //一次性拿出来所有的日志
  406. }
  407. //对于audit日志,当add日志各种情况下会返回如下:no data(p.Total <= 0)---全量, 1页(p.Total <= p.Ps)---全量, 2或多页(p1=最新->p1.lasttime, p2=p1.lasttime-p2.lasttime,...pn=pn-1.lasttime-mintime)
  408. if p.Total > p.Ps { //有多页
  409. llen := len(rsc)
  410. if llen > 0 && topn > p.Pn {
  411. ap.CtimeFrom = rsc[llen-1].Ctime
  412. }
  413. if p.Pn > 1 {
  414. ap.CtimeTo = pm.LastPageTime
  415. }
  416. }
  417. if audit, err = s.trackAuditLog(c, ap); err != nil {
  418. err = ecode.AegisSearchErr
  419. return
  420. }
  421. //根据ctime聚合,以资源日志为基准
  422. llen := len(rsc) + 2
  423. rscctime := make([]string, llen)
  424. rscctime[0] = time.Now().Format("2006-01-02 15:04:05") //max
  425. for i, item := range rsc {
  426. rscctime[i+1] = item.Ctime
  427. }
  428. rscctime[llen-1] = time.Time{}.Format("2006-01-02 15:04:05") //min
  429. index := 0
  430. for i := 1; i < llen; i++ {
  431. rel := []int{}
  432. for ; index < len(audit); index++ {
  433. t := audit[index].Ctime
  434. if t >= rscctime[i] && t < rscctime[i-1] {
  435. rel = append(rel, index)
  436. continue
  437. }
  438. break
  439. }
  440. if i == llen-1 && len(rel) == 0 {
  441. continue
  442. }
  443. rela = append(rela, rel)
  444. }
  445. res = &model.TrackInfo{
  446. Add: rsc,
  447. Audit: audit,
  448. Relation: rela,
  449. }
  450. return
  451. }
  452. func (s *Service) searchAuditLog(c context.Context, pm *model.SearchAuditLogParam) (resp *model.SearchLogResult, err error) {
  453. args := &model.ParamsQueryLog{
  454. Business: model.LogBusinessAudit,
  455. Oid: pm.RID,
  456. CtimeFrom: pm.CtimeFrom,
  457. CtimeTo: pm.CtimeTo,
  458. Int2: pm.TaskID,
  459. Uname: pm.Username,
  460. }
  461. if pm.State != "" {
  462. args.Str0 = []string{pm.State}
  463. }
  464. if pm.BusinessID > 0 {
  465. args.Int0 = []int64{pm.BusinessID}
  466. }
  467. escm := model.EsCommon{
  468. Ps: pm.Ps,
  469. Pn: pm.Pn,
  470. Order: "ctime",
  471. Sort: "desc",
  472. }
  473. return s.http.QueryLogSearch(c, args, escm)
  474. }
  475. func (s *Service) auditLogByRID(c context.Context, rid int64) (ls []string, err error) {
  476. resp, err := s.searchAuditLog(c, &model.SearchAuditLogParam{
  477. RID: []int64{rid},
  478. Ps: 1000,
  479. Pn: 1})
  480. if err != nil || resp == nil {
  481. return
  482. }
  483. for _, result := range resp.Result {
  484. change := &model.Change{}
  485. if err = json.Unmarshal([]byte(result.Extra), &change); err != nil {
  486. log.Error("json.Unmarshal error(%v)", err)
  487. return
  488. }
  489. flowaction, submitopt := change.GetSubmitOper()
  490. // 时间 + 操作人 + 操作/state + 操作内容
  491. l := fmt.Sprintf("%s %s[%s] %s %s", result.Ctime, result.Uname, result.Action, flowaction, submitopt)
  492. ls = append(ls, l)
  493. }
  494. return
  495. }
  496. func (s *Service) searchWeightLog(c context.Context, taskid int64, pn, ps int) (ls []*model.WeightLog, count int, err error) {
  497. args := &model.ParamsQueryLog{
  498. Business: model.LogBusinessTask,
  499. Type: model.LogTYpeTaskWeight,
  500. Oid: []int64{taskid},
  501. Action: []string{"weight"},
  502. }
  503. escm := model.EsCommon{
  504. Pn: pn,
  505. Ps: ps,
  506. Order: "ctime",
  507. Sort: "desc",
  508. }
  509. resp, err := s.http.QueryLogSearch(c, args, escm)
  510. if err != nil || resp == nil {
  511. return
  512. }
  513. count = resp.Page.Total
  514. for _, result := range resp.Result {
  515. logitem := make(map[string]*model.WeightLog)
  516. if err = json.Unmarshal([]byte(result.Extra), &logitem); err != nil {
  517. log.Error("json.Unmarshal error(%v)", err)
  518. return
  519. }
  520. ls = append(ls, logitem["weightlog"])
  521. }
  522. return
  523. }
  524. func (s *Service) searchConsumerLog(c context.Context, bizid, flowid int64, action []string, uids []int64, ps int) (at map[int64]string, err error) {
  525. args := &model.ParamsQueryLog{
  526. Business: model.LogBusinessTask,
  527. Type: model.LogTypeTaskConsumer,
  528. Action: action,
  529. UID: uids,
  530. CtimeFrom: time.Now().Add(-24 * time.Hour * 7).Format("2006-01-02 15:04:05"),
  531. }
  532. if bizid > 0 {
  533. args.Int0 = []int64{bizid}
  534. }
  535. if flowid > 0 {
  536. args.Int1 = []int64{flowid}
  537. }
  538. escm := model.EsCommon{
  539. Order: "ctime",
  540. Sort: "desc",
  541. Pn: 1,
  542. Ps: ps,
  543. Group: "uid",
  544. }
  545. resp, err := s.http.QueryLogSearch(c, args, escm)
  546. if err != nil || resp == nil {
  547. return
  548. }
  549. at = make(map[int64]string)
  550. for _, item := range resp.Result {
  551. if ct, ok := at[item.UID]; ok {
  552. if item.Ctime > ct {
  553. at[item.UID] = item.Ctime
  554. }
  555. } else {
  556. at[item.UID] = item.Ctime
  557. }
  558. }
  559. return
  560. }
  561. func (s *Service) searchResourceLog(c context.Context, rid int64, pn, ps int) (result []*model.TrackRsc, p common.Pager, err error) {
  562. //根据ctime降序排列
  563. args := &model.ParamsQueryLog{
  564. Business: model.LogBusinessResource,
  565. Type: model.LogTypeFromAdd,
  566. Oid: []int64{rid},
  567. }
  568. p = common.Pager{
  569. Pn: pn,
  570. Ps: ps,
  571. }
  572. escm := model.EsCommon{
  573. Order: "ctime",
  574. Sort: "desc",
  575. Pn: pn,
  576. Ps: ps,
  577. }
  578. resp, err := s.http.QueryLogSearch(c, args, escm)
  579. if err != nil || resp == nil {
  580. return
  581. }
  582. p.Total = resp.Page.Total
  583. result = make([]*model.TrackRsc, len(resp.Result))
  584. for i, item := range resp.Result {
  585. extra := struct {
  586. Opt map[string]interface{} `json:"opt"`
  587. }{}
  588. if err = json.Unmarshal([]byte(item.Extra), &extra); err != nil {
  589. log.Error("ResourceLog json.Unmarshal error(%v) extra(%s)", err, item.Extra)
  590. return
  591. }
  592. result[i] = &model.TrackRsc{
  593. Ctime: item.Ctime,
  594. Content: extra.Opt["content"].(string),
  595. Detail: extra.Opt,
  596. }
  597. }
  598. //content变化,由于result是根据创建时间降序排列的,以result的最后一个为基础, 向result[0]判断
  599. content := ""
  600. for i := len(result) - 1; i >= 0; i-- {
  601. item := result[i]
  602. //固定content字段比较变化
  603. if item.Content != content {
  604. content = item.Content
  605. continue
  606. }
  607. item.Content = ""
  608. }
  609. return
  610. }