reply_report.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. package service
  2. import (
  3. "context"
  4. "fmt"
  5. "time"
  6. model "go-common/app/interface/main/reply/model/reply"
  7. artmdl "go-common/app/interface/openplatform/article/model"
  8. "go-common/app/service/main/archive/api"
  9. arcmdl "go-common/app/service/main/archive/model/archive"
  10. figmdl "go-common/app/service/main/figure/model"
  11. "go-common/library/ecode"
  12. "go-common/library/log"
  13. "go-common/library/net/metadata"
  14. "go-common/library/queue/databus/report"
  15. xtime "go-common/library/time"
  16. )
  17. const (
  18. _reportNormalCnt = 5
  19. _reportAddSecs = 5
  20. _reportMaxSecs = 180
  21. )
  22. // AddReport report a reply.
  23. func (s *Service) AddReport(c context.Context, mid, oid, rpID int64, tp, reason int8, cont, platform string, build int64, buvid string) (cd int, err error) {
  24. var (
  25. r *model.Reply
  26. now = time.Now()
  27. ip = metadata.String(c, metadata.RemoteIP)
  28. )
  29. // check subject
  30. if !model.LegalSubjectType(tp) {
  31. err = ecode.ReplyIllegalSubType
  32. return
  33. }
  34. if err = model.CheckReportReason(reason); err != nil {
  35. return
  36. }
  37. cnt, err := s.dao.Redis.GetUserReportCnt(c, mid, now)
  38. if err != nil {
  39. log.Error("AddReport failed, replyCacheDao.GetUserReportCnt(%d), err is (%v)", mid, err)
  40. return
  41. }
  42. if cnt > _reportNormalCnt {
  43. var ttl int
  44. // When report count max 5 at one day, extra add 5s to user TTL at a time, and the max user TTL is 180s.
  45. // The last report time, we set Redis key TTL for one day seconds, use one day seconds sub current TTL value is user TTL.
  46. if ttl, err = s.dao.Redis.GetUserReportTTL(c, mid, now); err != nil {
  47. log.Error("AddReport failed, replyCacheDao.GetUserReportTTL(%d), err is (%v)", mid, err)
  48. return
  49. }
  50. if ttl >= 0 {
  51. uttl := s.oneDaySec - ttl
  52. maxttl := (cnt - _reportNormalCnt) * _reportAddSecs
  53. if maxttl > _reportMaxSecs {
  54. maxttl = _reportMaxSecs
  55. }
  56. if uttl < maxttl {
  57. cd = maxttl - uttl
  58. err = ecode.ReplyReportDeniedAsCD
  59. return
  60. }
  61. }
  62. }
  63. if r, err = s.Reply(c, oid, tp, rpID); err != nil || r == nil || r.Content == nil || r.IsDeleted() {
  64. return
  65. }
  66. // upper report reply of self in upper's arc ,treat it as del
  67. if s.isUpper(c, mid, oid, tp) && r.Mid == mid {
  68. s.dao.Databus.Delete(c, mid, oid, rpID, now.Unix(), tp, false)
  69. report.User(&report.UserInfo{
  70. Mid: mid,
  71. Platform: platform,
  72. Build: build,
  73. Buvid: buvid,
  74. Business: 41,
  75. Type: int(r.Type),
  76. Oid: r.Oid,
  77. Action: model.ReportReplyDel,
  78. Ctime: time.Now(),
  79. IP: ip,
  80. Index: []interface{}{
  81. r.RpID,
  82. r.State,
  83. model.ReplyStateUpDel,
  84. },
  85. })
  86. return
  87. }
  88. // 信用评分获取,用于优先举报排序处理
  89. var score int
  90. arg := &figmdl.ArgUserFigure{Mid: mid}
  91. fig, err := s.figure.UserFigure(c, arg)
  92. if err != nil {
  93. log.Error("s.figure.UserFigure(mid:%d) error(%v)", mid, err)
  94. err = nil
  95. } else {
  96. score = 100 - int(fig.Percentage)
  97. }
  98. ctime := xtime.Time(now.Unix())
  99. rpt := &model.Report{
  100. RpID: rpID,
  101. Oid: oid,
  102. Type: tp,
  103. Mid: mid,
  104. Reason: reason,
  105. Count: 1,
  106. Content: cont,
  107. Score: score,
  108. State: model.GetReportType(reason),
  109. CTime: ctime,
  110. MTime: ctime,
  111. }
  112. rptUser := &model.ReportUser{
  113. Oid: oid,
  114. Type: tp,
  115. RpID: rpID,
  116. Mid: mid,
  117. Reason: reason,
  118. Content: cont,
  119. State: model.ReportUserStateNew,
  120. CTime: ctime,
  121. MTime: ctime,
  122. }
  123. if rpt.ID, err = s.dao.Report.InsertUser(c, rptUser); err != nil {
  124. return
  125. }
  126. if rpt.ID == 0 {
  127. err = ecode.ReplyReported
  128. return
  129. }
  130. if tp != model.SubTypeActArc && tp != model.SubTypePlaylist && tp != model.SubTypeComicSeason && tp != model.SubTypeComicEpisode {
  131. var (
  132. m = make(map[int64]string)
  133. title, link string
  134. typeid int32
  135. )
  136. m[rpID] = ""
  137. message := r.Content.Message
  138. s.dao.FilterContents(c, m)
  139. if m[rpID] != "" {
  140. message = m[rpID]
  141. }
  142. title, link, typeid, _ = s.TitleLink(c, oid, tp)
  143. if link != "" {
  144. link = fmt.Sprintf("%s#reply%d", link, rpID)
  145. }
  146. err = s.workflow.AddReport(c, oid, tp, typeid, rpID, score, reason, mid, r.Mid, r.Like, message, link, title)
  147. if err != nil {
  148. return
  149. }
  150. }
  151. if rpt.ID, err = s.dao.Report.Insert(c, rpt); err != nil {
  152. return
  153. }
  154. // set report count and set user report dao.Redis key TTL for one day seconds.
  155. if err = s.dao.Redis.SetUserReportCnt(c, mid, cnt+1, now); err != nil {
  156. log.Error("s.redis.SetUserReportCnt(%v) error(%v) or row==0", rpt, err)
  157. return
  158. }
  159. s.dao.Databus.AddReport(c, oid, rpID, tp)
  160. report.User(&report.UserInfo{
  161. Mid: rptUser.Mid,
  162. Platform: platform,
  163. Build: build,
  164. Buvid: buvid,
  165. Business: 41,
  166. Type: int(tp),
  167. Oid: oid,
  168. Action: model.ReportReplyReport,
  169. Ctime: now,
  170. IP: ip,
  171. Index: []interface{}{
  172. r.Mid,
  173. r.RpID,
  174. r.State,
  175. fmt.Sprint(reason),
  176. },
  177. Content: map[string]interface{}{
  178. "count": cnt,
  179. "score": score,
  180. "content": cont,
  181. },
  182. })
  183. log.Info("AddReport(%d) oid:%d mid:%d score:%d percentage:%v", rpt.ID, oid, mid, score, fig)
  184. return
  185. }
  186. // ReportRelated get related replies of report.
  187. func (s *Service) ReportRelated(c context.Context, mid, oid, rpid int64, tp int8, escape bool) (sub *model.Subject, root *model.Reply, related []*model.Reply, err error) {
  188. if !model.LegalSubjectType(tp) {
  189. err = ecode.ReplyIllegalSubType
  190. return
  191. }
  192. // root reply
  193. sub, root, snds, err := s.ReportReply(c, mid, oid, rpid, tp, 1, s.sndDefCnt, escape)
  194. if err != nil {
  195. return
  196. }
  197. root.Replies = snds
  198. // related reply
  199. start := root.Floor - 6
  200. end := root.Floor + 6
  201. relIDs, err := s.dao.Reply.GetIDsByFloorOffset(c, oid, tp, start, end)
  202. if err != nil {
  203. return
  204. }
  205. relMap, err := s.repliesMap(c, oid, tp, relIDs)
  206. if err != nil {
  207. return
  208. }
  209. related = make([]*model.Reply, 0, len(relMap))
  210. bs := make([]*model.Reply, 0, len(relMap))
  211. for _, rpID := range relIDs {
  212. rp, ok := relMap[rpID]
  213. if ok && rp.RpID != root.RpID {
  214. if rp.Replies, err = s.reportReplies(c, rp, 1, s.sndDefCnt); err != nil {
  215. return
  216. }
  217. related = append(related, rp)
  218. // to build replies
  219. bs = append(bs, rp)
  220. bs = append(bs, rp.Replies...)
  221. }
  222. }
  223. if err = s.buildReply(c, sub, bs, mid, escape); err != nil {
  224. return
  225. }
  226. return
  227. }
  228. // ReportReply get report reply.
  229. func (s *Service) ReportReply(c context.Context, mid, oid, rpid int64, tp int8, pn, ps int, escape bool) (sub *model.Subject, root *model.Reply, seconds []*model.Reply, err error) {
  230. if !model.LegalSubjectType(tp) {
  231. err = ecode.ReplyIllegalSubType
  232. return
  233. }
  234. if sub, err = s.subject(c, oid, tp); err != nil {
  235. return
  236. }
  237. if root, err = s.ReplyContent(c, oid, rpid, tp); err != nil {
  238. return
  239. }
  240. if root.Root != 0 {
  241. if root, err = s.ReplyContent(c, root.Oid, root.Root, root.Type); err != nil {
  242. return
  243. }
  244. }
  245. if seconds, err = s.reportReplies(c, root, pn, ps); err != nil {
  246. return
  247. }
  248. bs := make([]*model.Reply, 0, len(seconds)+1)
  249. bs = append(bs, root)
  250. bs = append(bs, seconds...)
  251. if err = s.buildReply(c, sub, bs, mid, escape); err != nil {
  252. return
  253. }
  254. return
  255. }
  256. func (s *Service) reportReplies(c context.Context, rp *model.Reply, pn, ps int) (rs []*model.Reply, err error) {
  257. var (
  258. start = (pn - 1) * ps
  259. )
  260. rs = _emptyReplies
  261. if start >= rp.Count {
  262. return
  263. }
  264. sndIDs, err := s.dao.Reply.GetIDsByRootWithoutState(c, rp.Oid, rp.RpID, rp.Type, start, ps)
  265. if err != nil {
  266. return
  267. }
  268. sndMap, err := s.repliesMap(c, rp.Oid, rp.Type, sndIDs)
  269. if err != nil {
  270. return
  271. }
  272. rs = make([]*model.Reply, 0, len(sndMap))
  273. for _, rpID := range sndIDs {
  274. rp, ok := sndMap[rpID]
  275. if ok {
  276. rs = append(rs, rp)
  277. }
  278. }
  279. return
  280. }
  281. func (s *Service) linkByOids(c context.Context, oids map[int64]string, typ int8) (err error) {
  282. if len(oids) == 0 {
  283. return
  284. }
  285. if typ == model.SubTypeActivity {
  286. err = s.workflow.TopicsLink(c, oids, false)
  287. } else {
  288. for oid := range oids {
  289. var link string
  290. switch typ {
  291. case model.SubTypeTopic:
  292. link = fmt.Sprintf("https://www.bilibili.com/topic/%d.html", oid)
  293. case model.SubTypeArchive:
  294. link = fmt.Sprintf("https://www.bilibili.com/video/av%d", oid)
  295. case model.SubTypeForbiden:
  296. link = fmt.Sprintf("https://www.bilibili.com/blackroom/ban/%d", oid)
  297. case model.SubTypeNotice:
  298. link = fmt.Sprintf("https://www.bilibili.com/blackroom/notice/%d", oid)
  299. case model.SubTypeActArc:
  300. _, link, err = s.workflow.ActivitySub(c, oid)
  301. if err != nil {
  302. return
  303. }
  304. case model.SubTypeArticle:
  305. link = fmt.Sprintf("https://www.bilibili.com/read/cv%d", oid)
  306. case model.SubTypeMusic:
  307. link = fmt.Sprintf("https://www.bilibili.com/audio/au%d", oid)
  308. case model.SubTypeMusicList:
  309. link = fmt.Sprintf("https://www.bilibili.com/audio/am%d", oid)
  310. case model.SubTypeLive:
  311. link = fmt.Sprintf("https://vc.bilibili.com/video/%d", oid)
  312. case model.SubTypeLiveAct:
  313. _, link, err = s.workflow.LiveActivityTitle(c, oid)
  314. if err != nil {
  315. return
  316. }
  317. case model.SubTypeLivePicture:
  318. link = fmt.Sprintf("https://h.bilibili.com/ywh/%d", oid)
  319. case model.SubTypeCredit:
  320. link = fmt.Sprintf("https://www.bilibili.com/judgement/case/%d", oid)
  321. case model.SubTypeDynamic:
  322. link = fmt.Sprintf("https://t.bilibili.com/%d", oid)
  323. case model.SubTypeLiveNotice:
  324. link = fmt.Sprintf("http://link.bilibili.com/p/eden/news#/newsdetail?id=%d", oid)
  325. default:
  326. return
  327. }
  328. oids[oid] = link
  329. }
  330. }
  331. return
  332. }
  333. // TitleLink TitleLink
  334. func (s *Service) TitleLink(c context.Context, oid int64, typ int8) (title, link string, typeId int32, err error) {
  335. switch typ {
  336. case model.SubTypeArchive:
  337. arg := &arcmdl.ArgAid2{
  338. Aid: oid,
  339. }
  340. var m *api.Arc
  341. m, err = s.arcSrv.Archive3(c, arg)
  342. if err != nil || m == nil {
  343. log.Error("s.arcSrv.Archive3(%v) ret:%v error(%v)", arg, m, err)
  344. return
  345. }
  346. link = fmt.Sprintf("http://www.bilibili.com/video/av%d/", oid)
  347. title = m.Title
  348. typeId = m.TypeID
  349. case model.SubTypeTopic:
  350. if title, link, err = s.workflow.TopicTitle(c, oid); err != nil {
  351. log.Error("s.noticeDao.Topic(%d) error(%v)", oid, err)
  352. return
  353. }
  354. case model.SubTypeMusic:
  355. link = fmt.Sprintf("https://www.bilibili.com/audio/au%d", oid)
  356. case model.SubTypeMusicList:
  357. link = fmt.Sprintf("https://www.bilibili.com/audio/am%d", oid)
  358. case model.SubTypeActivity:
  359. if title, link, err = s.workflow.TopicTitle(c, oid); err != nil {
  360. log.Error("s.noticeDao.Activity(%d) error(%v)", oid, err)
  361. return
  362. }
  363. case model.SubTypeForbiden:
  364. title, link, err = s.workflow.BanTitle(c, oid)
  365. if err != nil {
  366. return
  367. }
  368. case model.SubTypeNotice:
  369. title, link, err = s.workflow.NoticeTitle(c, oid)
  370. if err != nil {
  371. return
  372. }
  373. case model.SubTypeActArc:
  374. if title, link, err = s.workflow.TopicTitle(c, oid); err != nil {
  375. log.Error("s.noticeDao.ActivitySub(%d) error(%v)", oid, err)
  376. return
  377. }
  378. case model.SubTypeArticle:
  379. arg := &artmdl.ArgAids{
  380. Aids: []int64{oid},
  381. }
  382. var m map[int64]*artmdl.Meta
  383. m, err = s.articleSrv.ArticleMetas(c, arg)
  384. if err != nil || m == nil {
  385. log.Error("s.articleSrv.ArticleMetas(%v) ret:%v error(%v)", arg, m, err)
  386. return
  387. }
  388. if meta, ok := m[oid]; ok {
  389. title = meta.Title
  390. link = fmt.Sprintf("http://www.bilibili.com/read/cv%d", oid)
  391. }
  392. case model.SubTypeLive:
  393. if title, link, err = s.workflow.LiveVideoTitle(c, oid); err != nil {
  394. log.Error("s.noticeDao.LiveSmallVideo(%d) error(%v)", oid, err)
  395. return
  396. }
  397. case model.SubTypeLiveAct:
  398. if title, link, err = s.workflow.LiveActivityTitle(c, oid); err != nil {
  399. log.Error("s.noticeDao.LiveActivity(%d) error(%v)", oid, err)
  400. return
  401. }
  402. case model.SubTypeLiveNotice:
  403. if title, err = s.workflow.LiveNotice(c, oid); err != nil {
  404. log.Error("s.noticeDao.LiveNotice(%d) error(%v)", oid, err)
  405. return
  406. }
  407. link = fmt.Sprintf("http://link.bilibili.com/p/eden/news#/newsdetail?id=%d", oid)
  408. return
  409. case model.SubTypeLivePicture:
  410. if title, link, err = s.workflow.LivePictureTitle(c, oid); err != nil {
  411. log.Error("s.noticeDao.LivePiture(%d) error(%v)", oid, err)
  412. return
  413. }
  414. case model.SubTypeCredit:
  415. if title, link, err = s.workflow.CreditTitle(c, oid); err != nil {
  416. log.Error("s.noticeDao.Credit(%d) error(%v)", oid, err)
  417. return
  418. }
  419. case model.SubTypeDynamic:
  420. if title, link, err = s.workflow.DynamicTitle(c, oid); err != nil {
  421. log.Error("s.noticeDao.Dynamic(%d) error(%v)", oid, err)
  422. return
  423. }
  424. case model.SubTypeHuoniao:
  425. if title, link, err = s.workflow.HuoniaoTitle(c, oid); err != nil {
  426. log.Error("s.workflow.HuoniaoTitle(%d) error(%v)", oid, err)
  427. return
  428. }
  429. default:
  430. return
  431. }
  432. return
  433. }