protect.go 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. package service
  2. import (
  3. "context"
  4. "fmt"
  5. "time"
  6. "go-common/app/interface/main/dm/model"
  7. dm2Mdl "go-common/app/interface/main/dm2/model"
  8. "go-common/app/interface/main/dm2/model/oplog"
  9. account "go-common/app/service/main/account/api"
  10. "go-common/library/ecode"
  11. "go-common/library/log"
  12. "go-common/library/xstr"
  13. )
  14. const (
  15. _notifyUpTitle = "有新的弹幕保护申请"
  16. _notifyUpContent = `您今天新增了一些未处理弹幕保护申请,前去 #{创作中心 - 哔哩哔哩弹幕视频网 - ( ゜- ゜)つロ 乾杯~}{"http://member.bilibili.com/v/#/danmu/report/save"} 处理吧`
  17. _notifyUsrTitle = "弹幕保护申请情况更新~"
  18. _pa48 = "由于up主日理万机,您之前申请的%d条弹幕暂未受理,请稍后再次申请"
  19. _paa = `您在视频 #{%s}{"http://www.bilibili.com/video/av%d/"} 已被全部保护`
  20. _pap = `您在视频 #{%s}{"http://www.bilibili.com/video/av%d/"} 已被部分保护`
  21. _protectApplyLevel = 4
  22. _maxProtectApply = 100
  23. _paExpire = 48 * 3600
  24. )
  25. // AddProtectApply 批量申请保护弹幕
  26. func (s *Service) AddProtectApply(c context.Context, uid, cid int64, dmids []int64) (err error) {
  27. if len(dmids) == 0 {
  28. err = ecode.DMNotFound
  29. return
  30. }
  31. count, err := s.dao.PaUsrCnt(c, uid)
  32. if err != nil {
  33. log.Error("s.dao.PaUsrCnt(%d) error(%v)", uid, err)
  34. return
  35. }
  36. if (count + len(dmids)) > _maxProtectApply {
  37. err = ecode.DMPAUserLimit
  38. return
  39. }
  40. cardReply, err := s.accountSvc.Card3(c, &account.MidReq{Mid: uid})
  41. if err != nil {
  42. log.Error("s.actSvc.Card3(%d) error(%v)", uid, err)
  43. return
  44. }
  45. if cardReply.GetCard().GetLevel() < _protectApplyLevel {
  46. err = ecode.DMPAUserLevel
  47. return
  48. }
  49. dms, err := s.dms(c, model.SubTypeVideo, cid, dmids)
  50. if err != nil {
  51. return
  52. }
  53. dc := len(dmids)
  54. if len(dms) < 1 && dc == 1 {
  55. err = ecode.DMNotFound
  56. return
  57. }
  58. sub, err := s.subject(c, 1, cid)
  59. if err != nil {
  60. return
  61. }
  62. aps := make([]*model.Pa, 0, len(dms))
  63. now := time.Now().Unix()
  64. var ctime time.Time
  65. for _, dm := range dms {
  66. if dm.Mid != uid && dc == 1 {
  67. err = ecode.DMPADMNotOwner
  68. return
  69. }
  70. if dm.AttrVal(model.AttrProtect) == model.AttrYes && dc == 1 {
  71. err = ecode.DMPADMProtected
  72. return
  73. }
  74. if !dm.NeedDisplay() && dc == 1 {
  75. err = ecode.DMNotFound
  76. return
  77. }
  78. ctime, err = s.dao.ProtectApplyTime(c, dm.ID)
  79. if err != nil {
  80. log.Error("dao.ProtectApplyTime(%d) error(%v)", uid, err)
  81. continue
  82. }
  83. if now-ctime.Unix() < _paExpire {
  84. if dc == 1 {
  85. err = ecode.DMPADMLimit
  86. return
  87. }
  88. continue
  89. }
  90. ap := &model.Pa{
  91. CID: cid,
  92. UID: sub.Mid,
  93. ApplyUID: dm.Mid,
  94. AID: sub.Pid,
  95. Playtime: float32(dm.Progress) / 1000,
  96. DMID: dm.ID,
  97. Msg: dm.Content.Msg,
  98. Status: -1,
  99. Ctime: time.Now(),
  100. Mtime: time.Now(),
  101. }
  102. aps = append(aps, ap)
  103. }
  104. if len(aps) < 1 {
  105. err = ecode.DMPAFailed
  106. return
  107. }
  108. affect, err := s.dao.AddProtectApply(c, aps)
  109. if err != nil {
  110. log.Error("dao.AddProtectApply(%v) error(%v)", aps, err)
  111. return
  112. }
  113. if err = s.dao.UptUsrPaCnt(c, uid, affect); err != nil {
  114. log.Error("s.dao.UptUsrPaCnt(%d,%d) error(%v)", uid, affect, err)
  115. }
  116. return
  117. }
  118. // UptPaSwitch 保护弹幕申请开关
  119. func (s *Service) UptPaSwitch(c context.Context, uid int64, status int) (err error) {
  120. if status != 1 {
  121. status = 0
  122. }
  123. _, err = s.dao.UptPaNoticeSwitch(c, uid, status)
  124. return
  125. }
  126. // UptPaStatus 审核保护弹幕申请
  127. func (s *Service) UptPaStatus(c context.Context, mid int64, ids []int64, status int) (err error) {
  128. dmids, err := s.dao.ProtectApplyByIDs(c, mid, xstr.JoinInts(ids))
  129. if err != nil {
  130. log.Error("s.dao.ProtectApplyByIDs(%d,%s) error(%v)", mid, xstr.JoinInts(ids), err)
  131. return
  132. }
  133. if status != 1 {
  134. status = 0
  135. }
  136. if _, err = s.dao.UptPaStatus(c, mid, xstr.JoinInts(ids), status); err != nil {
  137. log.Error("s.dao.UptPaStatus(%d,%v,%d) error(%v)", mid, ids, status, err)
  138. return
  139. }
  140. if status == 0 {
  141. return
  142. }
  143. for oid, ids := range dmids {
  144. arg := &dm2Mdl.ArgEditDMAttr{
  145. Type: 1,
  146. Oid: oid,
  147. Mid: mid,
  148. Bit: dm2Mdl.AttrProtect,
  149. Value: dm2Mdl.AttrYes,
  150. Dmids: ids,
  151. Source: oplog.SourceUp,
  152. OperatorType: oplog.OperatorUp,
  153. }
  154. if err = s.dmRPC.EditDMAttr(c, arg); err != nil {
  155. log.Error("s.dmRPC.EditDMAttr(%+v) error(%v)", arg, err)
  156. return
  157. }
  158. }
  159. return
  160. }
  161. // ProtectApplies 保护弹幕申请列表
  162. func (s *Service) ProtectApplies(c context.Context, uid, aid int64, page int, sort string) (res *model.ApplyListResult, err error) {
  163. var (
  164. count int
  165. start int
  166. )
  167. if page < 1 {
  168. page = 1
  169. }
  170. res = &model.ApplyListResult{
  171. Pager: &model.Pager{},
  172. List: make([]*model.Apply, 0, model.ProtectApplyLimit),
  173. }
  174. res.List, err = s.dao.ProtectApplies(c, uid, aid, sort)
  175. if err != nil {
  176. log.Error("s.dao.PaLs(%d) error(%v)", uid, err)
  177. return
  178. }
  179. count = len(res.List)
  180. res.Pager.Current = page
  181. res.Pager.Total = count / model.ProtectApplyLimit
  182. res.Pager.Size = model.ProtectApplyLimit
  183. res.Pager.TotalCount = count
  184. if count%model.ProtectApplyLimit != 0 {
  185. res.Pager.Total++
  186. }
  187. if count == 0 {
  188. res.List = make([]*model.Apply, 0, 1)
  189. return
  190. }
  191. start = (page - 1) * model.ProtectApplyLimit
  192. if start > count {
  193. start = 0
  194. }
  195. end := start + model.ProtectApplyLimit
  196. if end > count {
  197. end = count
  198. }
  199. res.List = res.List[start:end]
  200. aids := make([]int64, 0, len(res.List))
  201. uids := make([]int64, 0, len(res.List))
  202. for _, a := range res.List {
  203. aids = append(aids, a.AID)
  204. uids = append(uids, a.ApplyUID)
  205. }
  206. infosReply, err := s.accountSvc.Infos3(c, &account.MidsReq{
  207. Mids: uids,
  208. })
  209. if err != nil {
  210. log.Error("s.actSvc.Infos2(%v) error(%v)", uids, err)
  211. err = nil
  212. }
  213. archives := s.archiveInfos(c, aids)
  214. for _, a := range res.List {
  215. v, ok := archives[a.AID]
  216. if ok {
  217. a.Pic = v.Pic
  218. a.Title = v.Title
  219. }
  220. u, ok := infosReply.GetInfos()[a.ApplyUID]
  221. if ok {
  222. a.Uname = u.GetName()
  223. }
  224. }
  225. return
  226. }
  227. // PaVideoLs 被申请保护弹幕的视频列表
  228. func (s *Service) PaVideoLs(c context.Context, uid int64) (res []*model.Video, err error) {
  229. aids, err := s.dao.ProtectAids(c, uid)
  230. if err != nil {
  231. log.Error("s.dao.ProtectArchives(%d) error(%v)", uid, err)
  232. return
  233. }
  234. archives := s.archiveInfos(c, aids)
  235. res = make([]*model.Video, 0, len(aids))
  236. for _, aid := range aids {
  237. a := new(model.Video)
  238. v, ok := archives[aid]
  239. a.Aid = aid
  240. if ok {
  241. a.Title = v.Title
  242. } else {
  243. a.Title = ""
  244. }
  245. res = append(res, a)
  246. }
  247. return
  248. }
  249. // sendProtectNotifyToUp 发送申请保护弹幕通知给up主
  250. func (s *Service) sendProtectNotifyToUp(c context.Context) (err error) {
  251. if time.Now().Format("15") != "20" {
  252. return
  253. }
  254. lk, err := s.dao.PaLock(c, "up")
  255. if err != nil {
  256. log.Error("s.dao.PaLock() error(%v)", err)
  257. return
  258. }
  259. if lk != 1 {
  260. return
  261. }
  262. uids, err := s.dao.ProtectApplyStatistics(c)
  263. if err != nil {
  264. log.Error("s.dao.PaStat() error(%v)", err)
  265. return
  266. }
  267. if len(uids) < 1 {
  268. return
  269. }
  270. m, err := s.dao.PaNoticeClose(c, uids)
  271. if err != nil {
  272. log.Error("s.dao.PaNoticeClose(%v) error(%v)", uids, err)
  273. return
  274. }
  275. if len(m) > 0 {
  276. for k, v := range uids {
  277. if _, ok := m[v]; ok {
  278. uids = append(uids[:k], uids[k+1:]...)
  279. }
  280. }
  281. }
  282. s.dao.SendNotify(c, _notifyUpTitle, _notifyUpContent, uids)
  283. return
  284. }
  285. // sendProtectNotifyToUser 发送申请保护弹幕处理结果给申请用户
  286. func (s *Service) sendProtectNotifyToUser(c context.Context) {
  287. if time.Now().Format("15") != "22" {
  288. return
  289. }
  290. incr, err := s.dao.PaLock(c, "user")
  291. if err != nil {
  292. log.Error("s.dao.PaLock() error(%v)", err)
  293. return
  294. }
  295. if incr != 1 {
  296. return
  297. }
  298. stats, err := s.dao.PaUsrStat(c)
  299. if err != nil {
  300. log.Error("s.dao.PaStat() error(%v)", err)
  301. return
  302. }
  303. aids := make([]int64, 0, len(stats))
  304. for _, stat := range stats {
  305. aids = append(aids, stat.Aid)
  306. }
  307. archives := s.archiveInfos(c, aids)
  308. userStats := make(map[int64]map[int64]*model.ApplyUserNotify)
  309. now := time.Now().Unix()
  310. untreated := make(map[int64]int)
  311. for _, stat := range stats {
  312. m, ok := userStats[stat.Aid]
  313. if !ok {
  314. m = make(map[int64]*model.ApplyUserNotify)
  315. userStats[stat.Aid] = m
  316. }
  317. n, ok := m[stat.UID]
  318. if !ok {
  319. n = &model.ApplyUserNotify{}
  320. m[stat.UID] = n
  321. }
  322. if stat.Status == 1 {
  323. n.Protect++
  324. } else {
  325. n.Unprotect++
  326. if stat.Status == -1 && (now-stat.Ctime.Unix()) > 2*24*3600 {
  327. untreated[stat.UID]++
  328. }
  329. }
  330. }
  331. for k, v := range untreated {
  332. s.dao.SendNotify(c, _notifyUsrTitle, fmt.Sprintf(_pa48, v), []int64{k})
  333. }
  334. for aid, m := range userStats {
  335. archive, ok := archives[aid]
  336. if !ok {
  337. continue
  338. }
  339. for uid, stat := range m {
  340. var content string
  341. if stat.Protect > 0 && stat.Unprotect == 0 {
  342. content = fmt.Sprintf(_paa, archive.Title, archive.Aid)
  343. }
  344. if stat.Protect > 0 && stat.Unprotect > 0 {
  345. content = fmt.Sprintf(_pap, archive.Title, archive.Aid)
  346. }
  347. if content == "" {
  348. continue
  349. }
  350. s.dao.SendNotify(c, _notifyUsrTitle, content, []int64{uid})
  351. }
  352. }
  353. }