video.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. package service
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "strings"
  7. "time"
  8. "go-common/app/job/main/videoup/model/archive"
  9. "go-common/app/job/main/videoup/model/message"
  10. "go-common/app/job/main/videoup/model/redis"
  11. xsql "go-common/library/database/sql"
  12. "go-common/library/log"
  13. )
  14. // bvc2VuConsumer is bvc 2 videoup message consumer.
  15. func (s *Service) bvc2VuConsumer() {
  16. defer s.wg.Done()
  17. var (
  18. msgs = s.bvc2VuSub.Messages()
  19. err error
  20. c = context.TODO()
  21. )
  22. for {
  23. func() {
  24. msg, ok := <-msgs
  25. if !ok {
  26. log.Error("s,bvc2VuSub.Message closed")
  27. return
  28. }
  29. defer s.Rescue(string(msg.Value))
  30. msg.Commit()
  31. s.bvc2VuMo++
  32. m := &message.BvcVideo{}
  33. if err = json.Unmarshal(msg.Value, m); err != nil {
  34. log.Error("json.Unmarshal(%v) error(%v)", string(msg.Value), err)
  35. return
  36. }
  37. if time.Now().Unix()-m.Timestamp > s.c.BvcConsumeTimeout {
  38. log.Info("bvcMessage consume delayed! key(%s) value(%s) partition(%d) offset(%d) route(%s) commit start", msg.Key, msg.Value, msg.Partition, msg.Offset, m.Route)
  39. s.bvc2VuDelayMo++
  40. }
  41. log.Info("bvcMessage key(%s) value(%s) partition(%d) offset(%d) route(%s) commit start", msg.Key, msg.Value, msg.Partition, msg.Offset, m.Route)
  42. s.promDatabus.Incr(m.Route)
  43. switch m.Route {
  44. case message.RouteUploadInfo:
  45. s.promVideoS.Incr("xcode_sd")
  46. s.uploadInfo(c, m)
  47. case message.RouteXcodeSDFail:
  48. err = s.xcodeSDFail(c, m)
  49. case message.RouteXcodeSdFinish:
  50. s.promVideoE.Incr("xcode_sd")
  51. s.promVideoS.Incr("first_round")
  52. err = s.xcodeSDFinish(c, m)
  53. case message.RouteXcodeHDFail:
  54. log.Info("bvcMessage key(%s) value(%s) partition(%d) offset(%d) route(%s) before enter func", msg.Key, msg.Value, msg.Partition, msg.Offset, m.Route)
  55. err = s.xcodeHDFail(c, m)
  56. case message.RouteXcodeHDFinish:
  57. s.promVideoE.Incr("xcode_hd")
  58. s.promVideoS.Incr("dispatch")
  59. err = s.xcodeHDFinish(c, m)
  60. case message.RouteDispatchRunning:
  61. err = s.dispatchRunning(c, m)
  62. case message.RouteDispatchFinish:
  63. s.promVideoE.Incr("dispatch")
  64. err = s.dispatchFinish(c, m)
  65. case message.RouteVideoshotpv:
  66. err = s.videoshotPv(c, m)
  67. default:
  68. log.Warn("bvc2VuConsumer unknown route(%s) message(%s)", m.Route, msg.Value)
  69. }
  70. if err == nil {
  71. log.Info("bvcMessage key(%s) value(%s) partition(%d) offset(%d) commit", msg.Key, msg.Value, msg.Partition, msg.Offset)
  72. } else {
  73. log.Error("bvcMessage key(%s) value(%s) partition(%d) offset(%d) no commit error(%v)", msg.Key, msg.Value, msg.Partition, msg.Offset, err)
  74. }
  75. }()
  76. }
  77. }
  78. func (s *Service) videoshotPv(c context.Context, m *message.BvcVideo) (err error) {
  79. var count = len(m.ImgURLs)
  80. if count == 0 {
  81. return
  82. }
  83. var v *archive.Video
  84. if v, err = s.arc.NewVideo(c, m.Filename); err != nil {
  85. return
  86. }
  87. if v == nil {
  88. log.Warn("filename(%s) videoshotpv video not exist", m.Filename)
  89. return
  90. }
  91. s.arc.AddVideoShot(c, v.Cid, count)
  92. // double write...
  93. //s.videoshotAdd(v.Cid, m.BinURL, m.ImgURLs)
  94. return
  95. }
  96. func (s *Service) uploadInfo(c context.Context, m *message.BvcVideo) (err error) {
  97. s.redis.AddFilename(c, m.Filename)
  98. log.Info("filename(%s) upload success", m.Filename)
  99. return
  100. }
  101. func (s *Service) xcodeSDFail(c context.Context, m *message.BvcVideo) (err error) {
  102. var (
  103. v *archive.Video
  104. a *archive.Archive
  105. )
  106. if v, a, err = s.archiveVideo(c, m.Filename); err != nil {
  107. log.Error("s.archiveVideo(%s) error(%v)", m.Filename, err)
  108. return
  109. }
  110. if v.XcodeState >= archive.VideoXcodeSDFail {
  111. // NOTE: xcodeFail=1, xcodeState must uploadInfo=0
  112. log.Warn("archive(%d) video(%s) already(%d)", a.Aid, m.Filename, v.XcodeState)
  113. return // NOTE: is or not return???
  114. }
  115. v.Status = archive.VideoStatusXcodeFail
  116. v.XcodeState = archive.VideoXcodeSDFail
  117. v.FailCode = archive.XcodeFailCodes[m.FailInfo]
  118. // begin transcation
  119. var (
  120. tx *xsql.Tx
  121. change bool
  122. )
  123. if tx, err = s.arc.BeginTran(c); err != nil {
  124. log.Error("s.arc.BeginTran archive(%d) filename(%s) error(%v)", a.Aid, m.Filename, err)
  125. return
  126. }
  127. log.Info("archive(%d) filename(%s) begin sd_fail transcation a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
  128. if err = s.tranVideo(c, tx, a, v); err != nil {
  129. tx.Rollback()
  130. log.Error("s.tranVideo(%d, %s) error(%v)", a.Aid, v.Filename, err)
  131. return
  132. }
  133. log.Info("archive(%d) filename(%s) sd_fail tranVideo fininsh a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
  134. if change, err = s.tranArchive(c, tx, a, v, nil); err != nil {
  135. tx.Rollback()
  136. log.Error("s.tranArchive(%d, %s) error(%v)", a.Aid, v.Filename, err)
  137. return
  138. }
  139. log.Info("archive(%d) filename(%s) sd_fail tranArchive fininsh a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
  140. if err = tx.Commit(); err != nil {
  141. log.Error("tx.Commit(%d, %s) error(%v)", a.Aid, v.Filename, err)
  142. return
  143. }
  144. log.Info("archive(%d) filename(%s) end sd_fail transcation a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
  145. if change {
  146. s.sendMsg(c, a, v)
  147. }
  148. return
  149. }
  150. func (s *Service) xcodeSDFinish(c context.Context, m *message.BvcVideo) (err error) {
  151. var (
  152. v *archive.Video
  153. a *archive.Archive
  154. )
  155. if v, a, err = s.archiveVideo(c, m.Filename); err != nil {
  156. log.Error("s.archiveVideo(%s) error(%v)", m.Filename, err)
  157. return
  158. }
  159. // if v.Playurl == m.PlayURL && v.Duration == m.Duration { // NOTE: check playurl&duration or xcode_state???
  160. if v.XcodeState >= archive.VideoXcodeSDFinish {
  161. // NOTE: sdFinish=2, xcideState must uploadInfo=0||xcodesdfail=1
  162. log.Warn("archive(%d) video(%s) already(%d)", a.Aid, m.Filename, v.XcodeState)
  163. return // NOTE: is or not return???
  164. }
  165. // if video already deleted, no dispatch no update
  166. if v.Status == archive.VideoStatusDelete {
  167. log.Info("xcodeSDFinish archive(%d) video(%s) video already deleted", a.Aid, m.Filename)
  168. v.Status = archive.VideoStatusDelete
  169. } else {
  170. v.Status = archive.VideoStatusWait // NOTE: default -1
  171. }
  172. // if archive already deleted, video state should be?
  173. if a.State == archive.StateForbidUpDelete {
  174. log.Info("xcodeSDFinish archive(%d) video(%s) archive already deleted", a.Aid, m.Filename)
  175. v.Status = archive.VideoStatusDelete
  176. }
  177. v.XcodeState = archive.VideoXcodeSDFinish
  178. v.Playurl = m.PlayURL
  179. v.Duration = m.Duration
  180. // begin transcation
  181. var (
  182. tx *xsql.Tx
  183. )
  184. if tx, err = s.arc.BeginTran(c); err != nil {
  185. log.Error("s.arc.BeginTran archive(%d) filename(%s) error(%v)", a.Aid, m.Filename, err)
  186. return
  187. }
  188. log.Info("archive(%d) filename(%s) begin sd_finish transcation a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
  189. if err = s.tranVideo(c, tx, a, v); err != nil {
  190. tx.Rollback()
  191. log.Error("s.tranVideo(%d, %s) error(%v)", a.Aid, v.Filename, err)
  192. return
  193. }
  194. if v.Status != archive.VideoStatusDelete {
  195. log.Info("archive(%d) filename(%s) sd_finish tranVideo fininsh a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
  196. if _, err = s.tranArchive(c, tx, a, v, nil); err != nil {
  197. tx.Rollback()
  198. log.Error("s.tranArchive(%d, %s) error(%v)", a.Aid, v.Filename, err)
  199. return
  200. }
  201. }
  202. if err = s.tranArcCover(c, tx, a, v); err != nil {
  203. tx.Rollback()
  204. log.Error("s.tranArcCover(%d, %s) error(%v)", a.Aid, v.Filename, err)
  205. return
  206. }
  207. log.Info("archive(%d) filename(%s) sd_finish tranArchive fininsh a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
  208. if _, err = s.arc.TranVideoOper(c, tx, a.Aid, v.ID, v.Status, v.Attribute); err != nil {
  209. tx.Rollback()
  210. log.Error("s.arc.TranVideoOper(%d, %d, %d) error(%v)", a.Aid, v.ID, v.Status, err)
  211. return
  212. }
  213. if err = tx.Commit(); err != nil {
  214. log.Error("tx.Commit(%d, %s) error(%v)", a.Aid, v.Filename, err)
  215. return
  216. }
  217. if s.canDo(a.Mid) {
  218. s.syncRetry(context.TODO(), a.Aid, 0, redis.ActionForVideocovers, a.Cover, a.Cover)
  219. }
  220. log.Info("archive(%d) filename(%s) end sd_finish transcation a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
  221. return
  222. }
  223. func (s *Service) xcodeHDFail(c context.Context, m *message.BvcVideo) (err error) {
  224. log.Info("xcode hd fail filename (%s)", m.Filename)
  225. var (
  226. v *archive.Video
  227. a *archive.Archive
  228. )
  229. if v, a, err = s.archiveVideo(c, m.Filename); err != nil {
  230. log.Error("s.archiveVideo(%s) error(%v)", m.Filename, err)
  231. return
  232. }
  233. if v.XcodeState >= archive.VideoXcodeHDFail {
  234. // NOTE: hdfail=3, xcodeState must uploadInfo=0||xcodesdfail=1||sdfinish=2
  235. log.Warn("archive(%d) video(%s) already(%d)", a.Aid, m.Filename, v.XcodeState)
  236. return // NOTE: is or not return???
  237. }
  238. v.Status = archive.VideoStatusXcodeFail
  239. v.XcodeState = archive.VideoXcodeHDFail
  240. v.FailCode = archive.XcodeFailCodes[m.FailInfo]
  241. // begin transcation
  242. var (
  243. tx *xsql.Tx
  244. change bool
  245. )
  246. if tx, err = s.arc.BeginTran(c); err != nil {
  247. log.Error("s.arc.BeginTran archive(%d) filename(%s) error(%v)", a.Aid, m.Filename, err)
  248. return
  249. }
  250. log.Info("archive(%d) filename(%s) begin hd_fail transcation a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
  251. if err = s.tranVideo(c, tx, a, v); err != nil {
  252. tx.Rollback()
  253. log.Error("s.tranVideo(%d, %s) error(%v)", a.Aid, v.Filename, err)
  254. return
  255. }
  256. log.Info("archive(%d) filename(%s) hd_fail tranVideo fininsh a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
  257. if change, err = s.tranArchive(c, tx, a, v, nil); err != nil {
  258. tx.Rollback()
  259. log.Error("s.tranArchive(%d, %s) error(%v)", a.Aid, v.Filename, err)
  260. return
  261. }
  262. log.Info("archive(%d) filename(%s) hd_fail tranArchive fininsh a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
  263. if err = tx.Commit(); err != nil {
  264. log.Error("tx.Commit(%d, %s) error(%v)", a.Aid, v.Filename, err)
  265. return
  266. }
  267. log.Info("archive(%d) filename(%s) end hd_fail transcation a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
  268. if change {
  269. s.sendMsg(c, a, v)
  270. }
  271. return
  272. }
  273. func (s *Service) xcodeHDFinish(c context.Context, m *message.BvcVideo) (err error) {
  274. var (
  275. v *archive.Video
  276. a *archive.Archive
  277. )
  278. if v, a, err = s.archiveVideo(c, m.Filename); err != nil {
  279. log.Error("s.archiveVideo(%s) error(%v)", m.Filename, err)
  280. return
  281. }
  282. if v.XcodeState >= archive.VideoXcodeHDFinish {
  283. // NOTE: hdFinish=3, xcodeState must uploadInfo=0||xcodesdfail=1||sdfinish=2||hdfail=3
  284. log.Warn("archive(%d) video(%s) already(%d)", a.Aid, m.Filename, v.XcodeState)
  285. return // NOTE: is or not return???
  286. }
  287. // make sure filename not exist in redis, otherwise videoup can not submit!!!
  288. s.redis.DelFilename(c, m.Filename)
  289. log.Info("filename(%s) del_filename from redis success", m.Filename)
  290. // start deal hd finish
  291. v.XcodeState = archive.VideoXcodeHDFinish
  292. v.Resolutions = m.Resolutions
  293. v.Filesize = m.Filesize
  294. v.Duration = m.Duration
  295. v.Dimensions = fmt.Sprintf("%d,%d,%d", m.Width, m.Height, m.Rotate)
  296. // begin transcation
  297. var (
  298. tx *xsql.Tx
  299. )
  300. if tx, err = s.arc.BeginTran(c); err != nil {
  301. log.Error("s.arc.BeginTran archive(%d) filename(%s) error(%v)", a.Aid, m.Filename, err)
  302. return
  303. }
  304. log.Info("archive(%d) filename(%s) begin hd_finish transcation a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
  305. if err = s.tranVideo(c, tx, a, v); err != nil {
  306. tx.Rollback()
  307. log.Error("s.tranVideo(%d, %s) error(%v)", a.Aid, v.Filename, err)
  308. return
  309. }
  310. log.Info("archive(%d) filename(%s) hd_finish tranVideo fininsh a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
  311. // only hd5???
  312. var attr int32
  313. if strings.Contains(m.Resolutions, "hdflv2") || strings.Contains(m.Resolutions, "112") {
  314. attr = archive.AttrYes
  315. } else {
  316. attr = archive.AttrNo
  317. }
  318. if _, err = s.arc.TxUpAttrBit(tx, a.Aid, attr, archive.AttrBitHasHD5); err != nil {
  319. tx.Rollback()
  320. log.Error("s.arc.TxUpAttrBit(%d, %d, hd5) error(%v)", a.Aid, attr, err)
  321. return
  322. }
  323. log.Info("archive(%d) filename(%s) hd_finish attrBitHD5 fininsh a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
  324. if err = tx.Commit(); err != nil {
  325. log.Error("tx.Commit(%d, %s) error(%v)", a.Aid, v.Filename, err)
  326. return
  327. }
  328. log.Info("archive(%d) filename(%s) end hd_finish transcation a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
  329. return
  330. }
  331. func (s *Service) dispatchRunning(c context.Context, m *message.BvcVideo) (err error) {
  332. var (
  333. v *archive.Video
  334. a *archive.Archive
  335. )
  336. if v, a, err = s.archiveVideo(c, m.Filename); err != nil {
  337. log.Error("s.archiveVideo(%s) error(%v)", m.Filename, err)
  338. return
  339. }
  340. if v.XcodeState >= archive.VideoDispatchRunning {
  341. // NOTE: dispathRun=4, xcodeState must uploadInfo=0||xcodefail=1||sdfinish=2||xcodehdfail=3||hdFinish=4
  342. log.Warn("archive(%d) video(%s) already(%d)", a.Aid, m.Filename, v.XcodeState)
  343. return // NOTE: is or not return???
  344. }
  345. v.XcodeState = archive.VideoDispatchRunning
  346. // begin transcation
  347. var (
  348. tx *xsql.Tx
  349. )
  350. if tx, err = s.arc.BeginTran(c); err != nil {
  351. log.Error("s.arc.BeginTran archive(%d) filename(%s) error(%v)", a.Aid, m.Filename, err)
  352. return
  353. }
  354. log.Info("archive(%d) filename(%s) begin dispatch_run transcation a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
  355. if err = s.tranVideo(c, tx, a, v); err != nil {
  356. tx.Rollback()
  357. log.Error("s.tranVideo(%d, %s) error(%v)", a.Aid, v.Filename, err)
  358. return
  359. }
  360. log.Info("archive(%d) filename(%s) dispatch_run tranVideo fininsh a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
  361. if _, err = s.tranArchive(c, tx, a, v, nil); err != nil {
  362. tx.Rollback()
  363. log.Error("s.tranArchive(%d, %s) error(%v)", a.Aid, v.Filename, err)
  364. return
  365. }
  366. log.Info("archive(%d) filename(%s) dispatch_run tranArchive fininsh a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
  367. if err = tx.Commit(); err != nil {
  368. log.Error("tx.Commit(%d, %s) error(%v)", a.Aid, v.Filename, err)
  369. return
  370. }
  371. log.Info("archive(%d) filename(%s) end dispatch_run transcation a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
  372. return
  373. }
  374. func (s *Service) dispatchFinish(c context.Context, m *message.BvcVideo) (err error) {
  375. var (
  376. v *archive.Video
  377. a *archive.Archive
  378. )
  379. if v, a, err = s.archiveVideo(c, m.Filename); err != nil {
  380. log.Error("s.archiveVideo(%s) error(%v)", m.Filename, err)
  381. return
  382. }
  383. if v.XcodeState >= archive.VideoDispatchFinish {
  384. // NOTE: dispathFinish=5, xcodeState must uploadInfo=0||xcodefail=1||sdfinish=2||hdFinish=3||dispathRun=4
  385. log.Warn("archive(%d) video(%s) already(%d)", a.Aid, m.Filename, v.XcodeState)
  386. return // NOTE: is or not return???
  387. }
  388. v.XcodeState = archive.VideoDispatchFinish
  389. // begin transcation
  390. var (
  391. tx *xsql.Tx
  392. sChange, rChange bool
  393. )
  394. if tx, err = s.arc.BeginTran(c); err != nil {
  395. log.Error("s.arc.BeginTran archive(%d) filename(%s) error(%v)", a.Aid, m.Filename, err)
  396. return
  397. }
  398. log.Info("archive(%d) filename(%s) begin dispatch_finish transcation a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
  399. if err = s.tranVideo(c, tx, a, v); err != nil {
  400. tx.Rollback()
  401. log.Error("s.tranVideo(%d, %s) error(%v)", a.Aid, v.Filename, err)
  402. return
  403. }
  404. log.Info("archive(%d) filename(%s) dispatch_finish tranVideo fininsh a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
  405. if sChange, err = s.tranArchive(c, tx, a, v, nil); err != nil {
  406. tx.Rollback()
  407. log.Error("s.tranArchive(%d, %s) error(%v)", a.Aid, v.Filename, err)
  408. return
  409. }
  410. log.Info("archive(%d) filename(%s) dispatch_finish tranArchive fininsh a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
  411. var round int8
  412. if round, err = s.tranRound(c, tx, a); err != nil {
  413. tx.Rollback()
  414. return
  415. }
  416. rChange = round != a.Round
  417. log.Info("archive(%d) filename(%s) dispatch_finish tranRound fininsh old_round(%d) new_round(%d)", a.Aid, v.Filename, a.Round, round)
  418. a.Round = round
  419. if sChange || rChange {
  420. if err = s.tranArchiveOper(tx, a); err != nil {
  421. tx.Rollback()
  422. return
  423. }
  424. }
  425. log.Info("archive(%d) filename(%s) dispatch_finish round_opr fininsh round(%d)", a.Aid, v.Filename, a.Round)
  426. if err = tx.Commit(); err != nil {
  427. log.Error("tx.Commit(%d, %s) error(%v)", a.Aid, v.Filename, err)
  428. return
  429. }
  430. log.Info("archive(%d) filename(%s) end dispatch_finish transcation a_state(%d) v_status(%d)", a.Aid, v.Filename, a.State, v.Status)
  431. if sChange {
  432. //稿件二审in/out量监控 每个aid 只统计一次 (1,自动过审2,手动审核需分开处理)(多P也只统计一次) start,end
  433. var had bool
  434. if archive.NormalState(a.State) {
  435. //monitor second_round 自动开放 in/out diff
  436. s.promVideoS.Incr("second_round")
  437. s.promVideoE.Incr("second_round")
  438. //auto open
  439. s.syncBVC(c, a)
  440. s.sendAuditMsg(c, message.RouteAutoOpen, a.Aid)
  441. if is, _ := s.IsUpperFirstPass(c, a.Mid, a.Aid); is {
  442. go s.sendNewUpperMsg(c, a.Mid, a.Aid)
  443. }
  444. } else if had, _ = s.redis.SetMonitorCache(c, a.Aid); had {
  445. s.promVideoS.Incr("second_round")
  446. }
  447. s.sendMsg(c, a, v)
  448. if a.State == archive.StateForbidFixed {
  449. s.addClickToRedis(c, a.Aid)
  450. }
  451. }
  452. return
  453. }