12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151 |
- package service
- import (
- "context"
- "encoding/json"
- "fmt"
- "strconv"
- "sync"
- "time"
- "go-common/app/admin/main/reply/model"
- accmdl "go-common/app/service/main/account/api"
- "go-common/app/service/main/archive/api"
- arcmdl "go-common/app/service/main/archive/model/archive"
- rlmdl "go-common/app/service/main/relation/model"
- "go-common/library/database/sql"
- "go-common/library/ecode"
- "go-common/library/log"
- "go-common/library/net/metadata"
- "go-common/library/queue/databus/report"
- "go-common/library/sync/errgroup"
- xtime "go-common/library/time"
- )
- func (s *Service) reply(c context.Context, oid, rpID int64) (rp *model.Reply, err error) {
- if rp, err = s.dao.Reply(c, oid, rpID); err != nil {
- return
- }
- if rp == nil {
- err = ecode.ReplyNotExist
- return
- }
- if rp.Content, err = s.dao.ReplyContent(c, oid, rpID); err != nil {
- return
- }
- if rp.Content == nil {
- err = ecode.ReplyNotExist
- }
- return
- }
- func (s *Service) replies(c context.Context, oids, rpIDs []int64) (res map[int64]*model.Reply, err error) {
- res, missIDs, err := s.dao.RepliesCache(c, rpIDs)
- if err != nil {
- return
- }
- if len(missIDs) > 0 {
- var (
- rps map[int64]*model.Reply
- rcs map[int64]*model.ReplyContent
- miss []*model.Reply
- missOids []int64
- )
- for _, missID := range missIDs {
- for i := range rpIDs {
- if rpIDs[i] == missID {
- missOids = append(missOids, oids[i])
- break
- }
- }
- }
- if rps, err = s.dao.Replies(c, missOids, missIDs); err != nil {
- return
- }
- if rcs, err = s.dao.ReplyContents(c, missOids, missIDs); err != nil {
- return
- }
- for id, rp := range rps {
- rp.Content = rcs[id]
- res[id] = rp
- miss = append(miss, rp)
- }
- s.cache.Do(c, func(ctx context.Context) {
- s.dao.AddReplyCache(ctx, miss...)
- })
- }
- return
- }
- // ReplySearch return reply result from search.
- func (s *Service) ReplySearch(c context.Context, sp *model.SearchParams, page, pageSize int64) (res *model.SearchResult, err error) {
- if res, err = s.dao.SearchReplyV3(c, sp, page, pageSize); err != nil {
- log.Error("s.dao.SearchReplyV3(%+v,%d,%d) error(%v) ", sp, page, pageSize, err)
- return
- }
- adMap := make(map[int64]*model.SearchAdminLog)
- var ids []int64
- for i := range res.Result {
- ids = append(ids, res.Result[i].ID)
- }
- if adres, err := s.dao.SearchAdminLog(c, ids); err == nil {
- for _, data := range adres {
- adMap[data.ReplyID] = data
- }
- }
- filters := make(map[int64]string, len(res.Result))
- links := make(map[int64]string, len(res.Result))
- titles := make(map[int64]string)
- var mids []int64
- for _, data := range res.Result {
- if log, ok := adMap[data.ID]; ok && log != nil {
- data.AdminID = log.AdminID
- data.AdminName = log.AdminName
- data.OpCtime = log.CTime
- data.Opremark = log.Remark
- data.Opresult = log.Result
- }
- mids = append(mids, data.Mid)
- data.OidStr = strconv.FormatInt(data.Oid, 10)
- // reply filtered
- if len(data.Attr) > 0 {
- for _, attr := range data.Attr {
- if attr == 4 {
- filters[data.ID] = data.Message
- }
- }
- }
- // show title for top reply
- if sp.Attr == "1" && data.Title == "" {
- var link string
- data.Title, link, _ = s.TitleLink(c, data.Oid, int32(data.Type))
- data.RedirectURL = fmt.Sprintf("%s#reply%d", link, data.ID)
- } else {
- links[data.Oid] = ""
- if int32(data.Type) == model.SubTypeArchive {
- titles[data.Oid] = ""
- }
- }
- }
- ip := metadata.String(c, metadata.RemoteIP)
- stasMap, err := s.relationSvc.Stats(c, &rlmdl.ArgMids{Mids: mids, RealIP: ip})
- if err == nil {
- for i, data := range res.Result {
- if stat, ok := stasMap[data.Mid]; ok {
- res.Result[i].Stat = stat
- }
- }
- } else {
- log.Error("relationSvc.Stats error(%v)", err)
- }
- s.linkByOids(c, links, sp.Type)
- s.titlesByOids(c, titles)
- s.dao.FilterContents(c, filters)
- for _, data := range res.Result {
- if content := filters[data.ID]; content != "" {
- data.Message = content
- }
- if data.RedirectURL == "" {
- if link := links[data.Oid]; link != "" {
- data.RedirectURL = fmt.Sprintf("%s#reply%d", link, data.ID)
- }
- }
- if int32(data.Type) == model.SubTypeArchive && data.Title == "" {
- if title := titles[data.Oid]; title != "" {
- data.Title = title
- }
- }
- }
- return
- }
- func (s *Service) titlesByOids(c context.Context, titles map[int64]string) (err error) {
- var aids []int64
- for oid := range titles {
- aids = append(aids, oid)
- }
- arg := &arcmdl.ArgAids2{
- Aids: aids,
- }
- var m map[int64]*api.Arc
- m, err = s.arcSrv.Archives3(c, arg)
- for oid := range m {
- titles[oid] = m[oid].Title
- }
- return
- }
- func (s *Service) linkByOids(c context.Context, oids map[int64]string, typ int32) (err error) {
- if len(oids) == 0 {
- return
- }
- if typ == model.SubTypeActivity {
- err = s.dao.TopicsLink(c, oids, false)
- } else {
- for oid := range oids {
- var link string
- switch typ {
- case model.SubTypeTopic:
- link = fmt.Sprintf("https://www.bilibili.com/topic/%d.html", oid)
- case model.SubTypeArchive:
- link = fmt.Sprintf("https://www.bilibili.com/video/av%d", oid)
- case model.SubTypeForbiden:
- link = fmt.Sprintf("https://www.bilibili.com/blackroom/ban/%d", oid)
- case model.SubTypeNotice:
- link = fmt.Sprintf("https://www.bilibili.com/blackroom/notice/%d", oid)
- case model.SubTypeActArc:
- _, link, err = s.dao.ActivitySub(c, oid)
- if err != nil {
- return
- }
- case model.SubTypeArticle:
- link = fmt.Sprintf("https://www.bilibili.com/read/cv%d", oid)
- case model.SubTypeLiveVideo:
- link = fmt.Sprintf("https://vc.bilibili.com/video/%d", oid)
- case model.SubTypeLiveAct:
- _, link, err = s.dao.LiveActivityTitle(c, oid)
- if err != nil {
- return
- }
- case model.SubTypeLivePicture:
- link = fmt.Sprintf("https://h.bilibili.com/ywh/%d", oid)
- case model.SubTypeCredit:
- link = fmt.Sprintf("https://www.bilibili.com/judgement/case/%d", oid)
- case model.SubTypeDynamic:
- link = fmt.Sprintf("https://t.bilibili.com/%d", oid)
- default:
- return
- }
- oids[oid] = link
- }
- }
- return
- }
- // AdminEditReply edit reply content by admin.
- func (s *Service) AdminEditReply(c context.Context, adminID int64, adName string, oid, rpID int64, tp int32, msg, remark string) (err error) {
- rp, err := s.reply(c, oid, rpID)
- if err != nil {
- return
- }
- if rp.IsDeleted() {
- err = ecode.ReplyDeleted
- return
- }
- now := time.Now()
- if _, err = s.dao.UpReplyContent(c, oid, rpID, msg, now); err != nil {
- log.Error("s.content.UpMessage(%d, %d, %s, %v), err is (%v)", oid, rpID, msg, now, err)
- return
- }
- if err = s.dao.DelReplyCache(c, rpID); err != nil {
- log.Error("dao.AddReplyCache(%+v,%s) rpid(%d) error(%v)", rp, msg, err)
- }
- s.addAdminLog(c, rp.Oid, rp.ID, adminID, rp.Type, model.AdminIsNew, model.AdminIsNotReport, model.AdminOperEdit, "已修改评论内容", remark, now)
- s.cache.Do(c, func(ctx context.Context) {
- s.pubSearchReply(ctx, map[int64]*model.Reply{rp.ID: rp}, rp.State)
- })
- report.Manager(&report.ManagerInfo{
- UID: adminID,
- Uname: adName,
- Business: 41,
- Type: int(tp),
- Oid: rp.Oid,
- Ctime: now,
- Action: model.ReportActionReplyEdit,
- Index: []interface{}{
- rp.ID,
- rp.State,
- rp.State,
- },
- Content: map[string]interface{}{"remark": remark},
- })
- return
- }
- // AddTop add a top reply.
- func (s *Service) AddTop(c context.Context, adid int64, adName string, oid, rpID int64, typ int32, act uint32) (err error) {
- rp, err := s.reply(c, oid, rpID)
- if err != nil {
- return
- }
- if rp.IsFolded() {
- return ecode.ReplyFolded
- }
- if rp.Root != 0 {
- log.Error("add top reply illegal reply(oid:%v,type:%v,:rpID:%v) not root", oid, typ, rpID, err)
- return
- }
- sub, err := s.subject(c, oid, typ)
- if err != nil {
- log.Error("s.subject(%d,%d),err:%v", oid, typ)
- return
- }
- if act == model.AttrYes && sub.AttrVal(model.SubAttrTopAdmin) == model.AttrYes {
- err = ecode.ReplyHaveTop
- log.Error("Repeat to add top reply(%d,%d,%d,%d) ", rp.ID, rp.Oid, typ, sub.Attr)
- return
- }
- sub.AttrSet(act, model.SubAttrTopAdmin)
- err = sub.TopSet(rpID, 0, act)
- if err != nil {
- log.Error("sub.TopSet(%d,%d,%d) failed!err:=%v ", rp.ID, rp.Oid, 0, err)
- return
- }
- rp.AttrSet(act, model.AttrTopAdmin)
- now := time.Now()
- tx, err := s.dao.BeginTran(c)
- if err != nil {
- return
- }
- if _, err = s.dao.TxUpReplyAttr(tx, oid, rpID, rp.Attr, now); err != nil {
- tx.Rollback()
- return
- }
- if _, err = s.dao.TxUpSubAttr(tx, oid, typ, sub.Attr, now); err != nil {
- tx.Rollback()
- return
- }
- if _, err = s.dao.TxUpSubMeta(tx, sub.Oid, sub.Type, sub.Meta, now); err != nil {
- tx.Rollback()
- log.Error("dao.TxUpMeta(oid:%d,tp:%d) err(%v)", sub.Oid, sub.Type, err)
- return
- }
- if err = tx.Commit(); err != nil {
- return
- }
- if act == model.AttrYes {
- s.dao.DelIndexBySort(c, rp, model.SortByCount)
- s.dao.DelIndexBySort(c, rp, model.SortByLike)
- } else if act == model.AttrNo && rp.IsNormal() {
- s.addReplyIndex(c, rp)
- }
- s.dao.AddTopCache(c, rp)
- s.dao.AddReplyCache(c, rp)
- s.dao.DelSubjectCache(c, rp.Oid, rp.Type)
- report.Manager(&report.ManagerInfo{
- UID: adid,
- Uname: adName,
- Business: 41,
- Type: int(typ),
- Oid: oid,
- Ctime: now,
- Action: model.ReportActionReplyTop,
- Index: []interface{}{sub.Mid, act, rpID},
- })
- if act == model.AttrYes {
- s.pubEvent(c, "top", 0, sub, rp, nil)
- } else if act == model.AttrNo {
- s.pubEvent(c, "untop", 0, sub, rp, nil)
- }
- //add admin log and search log
- if act == model.AttrYes {
- s.addAdminLog(c, rp.Oid, rp.ID, adid, rp.Type, model.AdminIsNew, model.AdminIsNotReport, model.AdminOperSubTop, "管理员置顶评论", "", time.Now())
- s.cache.Do(c, func(ctx context.Context) {
- s.pubSearchReply(ctx, map[int64]*model.Reply{rp.ID: rp}, rp.State)
- })
- } else {
- s.addAdminLog(c, rp.Oid, rp.ID, adid, rp.Type, model.AdminIsNew, model.AdminIsNotReport, model.AdminOperSubTop, "管理员取消置顶评论", "", time.Now())
- s.cache.Do(c, func(ctx context.Context) {
- s.pubSearchReply(ctx, map[int64]*model.Reply{rp.ID: rp}, model.StateNormal)
- })
- }
- return
- }
- // CallbackDeleteReply delete reply by admin.
- func (s *Service) CallbackDeleteReply(ctx context.Context, adminID int64, oid, rpID int64, ftime int64, typ int32, moral int32, adminName, remark string, reason, freason int32) (err error) {
- now := time.Now()
- sub, rp, err := s.delReply(ctx, oid, rpID, model.StateDelAdmin, now)
- if err != nil {
- if ecode.ReplyDeleted.Equal(err) && rp.IsDeleted() {
- err = nil
- } else {
- log.Error("delReply(%d,%d) error(%v)", oid, rpID, err)
- return err
- }
- }
- s.delCache(ctx, sub, rp)
- s.cache.Do(ctx, func(ctx context.Context) {
- ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
- defer cancel()
- var rpt *model.Report
- if rpt, _ = s.dao.Report(ctx, oid, rpID); rpt != nil {
- if rpt.State == model.ReportStateNew || rpt.State == model.ReportStateNew2 {
- rpt.MTime = xtime.Time(now.Unix())
- if rpt.State == model.ReportStateNew {
- rpt.State = model.ReportStateDelete1
- } else if rpt.State == model.ReportStateNew2 {
- rpt.State = model.ReportStateDelete2
- }
- if _, err = s.dao.UpReportsState(ctx, []int64{rpt.Oid}, []int64{rpt.RpID}, rpt.State, now); err != nil {
- log.Error("s.dao.UpdateReport(%+v) error(%v)", rpt, err)
- }
- state := model.StateDelAdmin
- s.pubSearchReport(ctx, map[int64]*model.Report{rpt.RpID: rpt}, &state)
- }
- }
- s.pubEvent(context.Background(), model.EventReportDel, rpt.Mid, sub, rp, rpt)
- report.Manager(&report.ManagerInfo{
- UID: adminID,
- Uname: adminName,
- Business: 41,
- Type: int(typ),
- Oid: rp.Oid,
- Ctime: now,
- Action: model.ReportActionReplyDel,
- Index: []interface{}{
- rp.ID,
- rp.State,
- model.StateDelAdmin,
- },
- Content: map[string]interface{}{
- "moral": moral,
- "notify": false,
- "ftime": ftime,
- "freason": freason,
- "reason": reason,
- "remark": remark,
- },
- })
- rps := make(map[int64]*model.Reply)
- rps[rp.ID] = rp
- s.addAdminLogs(ctx, rps, adminID, typ, model.AdminIsNew, model.AdminIsReport, model.AdminOperDelete, fmt.Sprintf("已删除并封禁%s/扣除%d节操", forbidResult(ftime), moral), remark, now)
- s.pubSearchReply(ctx, rps, model.StateDelAdmin)
- })
- return
- }
- // AdminDeleteReply delete reply by admin.
- func (s *Service) AdminDeleteReply(c context.Context, adminID int64, oids, rpIDs []int64, ftime int64, typ int32, moral int32, notify bool, adminName, remark string, reason, freason int32) (err error) {
- err = s.adminDeleteReply(c, adminID, oids, rpIDs, ftime, typ, moral, notify, adminName, remark, reason, freason)
- return
- }
- func (s *Service) adminDeleteReply(c context.Context, adminID int64, oids, rpIDs []int64, ftime int64, typ int32, moral int32, notify bool, adminName, remark string, reason, freason int32) (err error) {
- var (
- lk sync.Mutex
- rps = make(map[int64]*model.Reply)
- now = time.Now()
- )
- wg := errgroup.Group{}
- wg.GOMAXPROCS(4)
- for idx := range oids {
- i := idx
- wg.Go(func() (err error) {
- var sub *model.Subject
- var rp *model.Reply
- // 针对大忽悠事件 特殊用户删除评论不让删
- var (
- tp int32
- ok bool
- exsits bool
- )
- tp, ok = s.oids[oids[i]]
- if ok && tp == typ {
- _, exsits = s.ads[adminName]
- if exsits {
- return
- }
- }
- sub, rp, err = s.delReply(c, oids[i], rpIDs[i], model.StateDelAdmin, now)
- if err != nil {
- if ecode.ReplyDeleted.Equal(err) && rp.IsDeleted() {
- err = nil
- } else {
- log.Error("delReply(%d,%d) error(%v)", oids[i], rpIDs[i], err)
- return err
- }
- }
- if rp.IsFolded() {
- s.marker.Do(c, func(ctx context.Context) {
- s.handleFolded(ctx, rp)
- })
- }
- s.delCache(c, sub, rp)
- s.pubEvent(c, "reply_del", 0, sub, rp, nil)
- rpt, _ := s.dao.Report(c, oids[i], rpIDs[i])
- if rpt != nil {
- if rpt.State == model.ReportStateNew || rpt.State == model.ReportStateNew2 {
- rpt.MTime = xtime.Time(now.Unix())
- if rpt.State == model.ReportStateNew {
- rpt.State = model.ReportStateDelete1
- } else if rpt.State == model.ReportStateNew2 {
- rpt.State = model.ReportStateDelete2
- }
- if _, err = s.dao.UpReportsState(c, []int64{rpt.Oid}, []int64{rpt.RpID}, rpt.State, now); err != nil {
- log.Error("s.dao.UpdateReport(%+v) error(%v)", rpt, err)
- }
- }
- }
- s.cache.Do(c, func(ctx context.Context) {
- // 针对大忽悠事件的特殊推送
- if _, ok := s.ads[adminName]; ok {
- if e := s.NotifyTroll(ctx, rp.Mid); e != nil {
- log.Warn("notify-troll error (%v)", e)
- }
- }
- s.dao.DelReport(ctx, rp.Oid, rp.ID)
- if rpt != nil {
- rpt.ReplyCtime = rp.CTime
- state := model.StateDelAdmin
- s.pubSearchReport(ctx, map[int64]*model.Report{rpt.RpID: rpt}, &state)
- }
- s.moralAndNotify(ctx, rp, moral, notify, rp.Mid, adminID, adminName, remark, reason, freason, ftime, false)
- })
- lk.Lock()
- rps[rp.ID] = rp
- lk.Unlock()
- report.Manager(&report.ManagerInfo{
- UID: adminID,
- Uname: adminName,
- Business: 41,
- Type: int(typ),
- Oid: rp.Oid,
- Ctime: now,
- Action: model.ReportActionReplyDel,
- Index: []interface{}{
- rp.ID,
- rp.State,
- model.StateDelAdmin,
- },
- Content: map[string]interface{}{
- "moral": moral,
- "notify": notify,
- "ftime": ftime,
- "freason": freason,
- "reason": reason,
- "remark": remark,
- },
- })
- return nil
- })
- }
- if err = wg.Wait(); err != nil {
- return
- }
- s.addAdminLogs(c, rps, adminID, typ, model.AdminIsNew, model.AdminIsNotReport, model.AdminOperDelete, fmt.Sprintf("已删除并封禁%s/扣除%d节操", forbidResult(ftime), moral), remark, now)
- s.cache.Do(c, func(ctx context.Context) {
- s.pubSearchReply(ctx, rps, model.StateDelAdmin)
- })
- return
- }
- // AdminRecoverReply recover reply by admin.
- func (s *Service) AdminRecoverReply(c context.Context, adminID int64, adName string, oid, rpID int64, typ int32, remark string) (err error) {
- rp, err := s.reply(c, oid, rpID)
- if err != nil {
- log.Error("s.reply(%d,%d) error(%v)", oid, rpID, err)
- return
- }
- now := time.Now()
- var sub *model.Subject
- if sub, rp, err = s.recReply(c, rp.Oid, rp.ID, model.StateNormal, now); err != nil {
- log.Error("s.recReply(%d,%d) error(%v)", rp.Oid, rp.ID, err)
- return
- }
- s.addAdminLog(c, rp.Oid, rp.ID, adminID, rp.Type, model.AdminIsNew, model.AdminIsNotReport, model.AdminOperRecover, "已恢复评论", remark, now)
- s.pubEvent(c, "reply_recover", 0, sub, rp, nil)
- s.cache.Do(c, func(ctx context.Context) {
- s.pubSearchReply(ctx, map[int64]*model.Reply{rp.ID: rp}, model.StateNormal)
- })
- report.Manager(&report.ManagerInfo{
- UID: adminID,
- Uname: adName,
- Business: 41,
- Type: int(typ),
- Oid: rp.Oid,
- Ctime: now,
- Action: model.ReportActionReplyRecover,
- Index: []interface{}{
- rp.ID,
- rp.State,
- model.StateNormal,
- },
- Content: map[string]interface{}{"remark": remark},
- })
- return
- }
- // AdminPassReply recover reply by admin.
- func (s *Service) AdminPassReply(c context.Context, adid int64, adName string, oids, rpIDs []int64, typ int32, remark string) (err error) {
- s.adminPassReply(c, adid, adName, oids, rpIDs, typ, remark)
- return
- }
- func (s *Service) adminPassReply(c context.Context, adid int64, adName string, oids, rpIDs []int64, typ int32, remark string) (err error) {
- now := time.Now()
- rps, err := s.replies(c, oids, rpIDs)
- if err != nil {
- return
- }
- wg, ctx := errgroup.WithContext(c)
- for _, m := range rps {
- rp := m
- wg.Go(func() (err error) {
- if rp.State == model.StatePending {
- var sub *model.Subject
- if sub, rp, err = s.recReply(ctx, rp.Oid, rp.ID, model.StateNormal, now); err != nil {
- return
- }
- s.dao.DelAuditIndex(ctx, rp)
- s.pubEvent(c, "reply_recover", 0, sub, rp, nil)
- } else {
- var (
- tx *sql.Tx
- rows int64
- )
- if tx, err = s.dao.BeginTran(ctx); err != nil {
- return
- }
- if rows, err = s.dao.TxUpdateReplyState(tx, rp.Oid, rp.ID, model.StateNormal, now); err != nil || rows == 0 {
- log.Error("dao.Reply.TxUpdateReplyState(%v,%d) error(%v)", rp, model.StateNormal, err)
- tx.Rollback()
- return
- }
- if rp.State == model.StateMonitor {
- if _, err = s.dao.TxSubDecrMCount(tx, rp.Oid, rp.Type, now); err != nil {
- log.Error("dao.Reply.TxSubDecrMCount(%v) error(%v)", rp, err)
- tx.Rollback()
- return
- }
- }
- if err = tx.Commit(); err != nil {
- log.Error("tx.Commit error(%v)", err)
- return
- }
- if err = s.dao.DelReplyCache(ctx, rp.ID); err != nil {
- log.Error("s.dao.DelReplyCache(%d,%d) error(%v)", rp.Oid, rp.ID, err)
- }
- }
- report.Manager(&report.ManagerInfo{
- UID: adid,
- Uname: adName,
- Business: 41,
- Type: int(typ),
- Oid: rp.Oid,
- Ctime: now,
- Action: model.ReportActionReplyPass,
- Index: []interface{}{
- rp.ID,
- rp.State,
- model.StateNormal,
- },
- Content: map[string]interface{}{"remark": remark},
- })
- return
- })
- }
- if err = wg.Wait(); err != nil {
- return
- }
- s.addAdminLogs(c, rps, adid, typ, model.AdminIsNew, model.AdminIsNotReport, model.AdminOperPass, "已通过评论", remark, now)
- s.cache.Do(c, func(ctx context.Context) {
- s.pubSearchReply(ctx, rps, model.StateNormal)
- })
- return
- }
- // addReplyIndex add reply index to redis.
- func (s *Service) addReplyIndex(c context.Context, rp *model.Reply) (err error) {
- var ok bool
- if rp.IsRoot() {
- if ok, err = s.dao.ExpireIndex(c, rp.Oid, rp.Type, model.SortByFloor); err == nil && ok {
- if err = s.dao.AddFloorIndex(c, rp); err != nil {
- log.Error("d.AddFloorIndex(%d,%d) error(%v)", rp.Oid, rp.Type, err)
- }
- }
- if ok, err = s.dao.ExpireIndex(c, rp.Oid, rp.Type, model.SortByCount); err == nil && ok {
- if err = s.dao.AddCountIndex(c, rp); err != nil {
- log.Error("s.AddCountIndex(%d,%d) error(%v)", rp.Oid, rp.Type, err)
- }
- }
- if ok, err = s.dao.ExpireIndex(c, rp.Oid, rp.Type, model.SortByLike); err == nil && ok {
- rpt, _ := s.dao.Report(c, rp.Oid, rp.ID)
- if err = s.dao.AddLikeIndex(c, rp, rpt); err != nil {
- log.Error("d.AddLikeIndex(%d,%d) error(%v)", rp.Oid, rp.Type, err)
- }
- }
- } else {
- if ok, err = s.dao.ExpireNewChildIndex(c, rp.Root); err == nil && ok {
- if err = s.dao.AddNewChildIndex(c, rp); err != nil {
- log.Error("d.AddFloorRootIndex(%d) error(%v)", rp.Root, err)
- }
- }
- }
- return
- }
- func (s *Service) recReply(c context.Context, oid, rpID int64, state int32, now time.Time) (sub *model.Subject, rp *model.Reply, err error) {
- if sub, rp, err = s.tranRecover(c, oid, rpID, state, now); err != nil {
- return
- }
- if rp.Content, err = s.dao.ReplyContent(c, oid, rpID); err != nil {
- return
- }
- if rp.Content == nil {
- err = ecode.ReplyNotExist
- return
- }
- if !rp.IsRoot() {
- if err = s.dao.DelReplyCache(c, rp.Root); err != nil {
- log.Error("s.dao.DelReplyCache(%d,%d) error(%v)", oid, rpID, err)
- }
- }
- if err = s.dao.DelReplyCache(c, rpID); err != nil {
- log.Error("s.dao.DelReplyCache(%d,%d) error(%v)", oid, rpID, err)
- }
- if err = s.addReplyIndex(c, rp); err != nil {
- log.Error("s.dao.DelReplyIndex(%d,%d) error(%v)", oid, rpID, err)
- }
- if err = s.dao.AddSubjectCache(c, sub); err != nil {
- log.Error("s.dao.DelSubjectCache(%+v) error(%v)", sub, err)
- }
- s.dao.SendStats(c, sub.Type, sub.Oid, sub.ACount)
- return
- }
- func (s *Service) delReply(c context.Context, oid, rpID int64, state int32, now time.Time) (sub *model.Subject, rp *model.Reply, err error) {
- if sub, rp, err = s.tranDel(c, oid, rpID, state, now); err != nil {
- if ecode.ReplyDeleted.Equal(err) && rp.IsDeleted() {
- if rp.Content, err = s.dao.ReplyContent(c, oid, rpID); err != nil {
- return
- } else if rp.Content == nil {
- err = ecode.ReplyNotExist
- return
- }
- err = ecode.ReplyDeleted
- }
- return
- }
- if rp.Content, err = s.dao.ReplyContent(c, oid, rpID); err != nil {
- return
- }
- if rp.Content == nil {
- err = ecode.ReplyNotExist
- return
- }
- return
- }
- func (s *Service) delCache(c context.Context, sub *model.Subject, rp *model.Reply) (err error) {
- if !rp.IsRoot() {
- if err = s.dao.DelReplyCache(c, rp.Root); err != nil {
- log.Error("s.dao.DelReplyCache(%d,%d) error(%v)", rp.Oid, rp.ID, err)
- }
- }
- if err = s.dao.DelReplyCache(c, rp.ID); err != nil {
- log.Error("s.dao.DelReplyCache(%d,%d) error(%v)", rp.Oid, rp.ID, err)
- }
- if err = s.dao.DelReplyIndex(c, rp); err != nil {
- log.Error("s.dao.DelReplyIndex(%d,%d) error(%v)", rp.Oid, rp.ID, err)
- }
- if err = s.dao.AddSubjectCache(c, sub); err != nil {
- log.Error("s.dao.DelSubjectCache(%+v) error(%v)", sub, err)
- }
- if rp.AttrVal(model.AttrTopAdmin) == model.AttrYes {
- s.dao.DelTopCache(c, rp.Oid, model.SubAttrTopAdmin)
- }
- if rp.AttrVal(model.AttrTopUpper) == model.AttrYes {
- s.dao.DelTopCache(c, rp.Oid, model.SubAttrTopUpper)
- }
- s.dao.SendStats(c, sub.Type, sub.Oid, sub.ACount)
- return
- }
- func (s *Service) tranRecover(c context.Context, oid, rpID int64, state int32, now time.Time) (sub *model.Subject, rp *model.Reply, err error) {
- var (
- rootRp *model.Reply
- count int32
- )
- tx, err := s.dao.BeginTran(c)
- if err != nil {
- return
- }
- if rp, err = s.dao.TxReplyForUpdate(tx, oid, rpID); err != nil {
- tx.Rollback()
- err = fmt.Errorf("s.dao.Reply(%d,%d) error(%v) ", oid, rpID, err)
- return
- }
- if rp == nil {
- tx.Rollback()
- err = ecode.ReplyNotExist
- return
- } else if rp.IsNormal() {
- tx.Rollback()
- err = ecode.ReplyActioned
- return
- }
- rows, err := s.dao.TxUpdateReplyState(tx, rp.Oid, rp.ID, state, now)
- if err != nil || rows == 0 {
- tx.Rollback()
- err = fmt.Errorf("error(%v) or rows(%d)", err, rows)
- return
- }
- rp.MTime = xtime.Time(now.Unix())
- if rp.IsRoot() {
- count = rp.RCount + 1
- } else {
- if rootRp, err = s.dao.TxReply(tx, rp.Oid, rp.Root); err != nil {
- tx.Rollback()
- return
- }
- count = 1
- }
- if rp.IsRoot() {
- rows, err = s.dao.TxIncrSubRCount(tx, rp.Oid, rp.Type, now)
- } else {
- rows, err = s.dao.TxIncrReplyRCount(tx, rp.Oid, rp.Root, now)
- }
- if err != nil || rows == 0 {
- tx.Rollback()
- err = fmt.Errorf("tranRecover increase count error(%v) or rows(%d)", err, rows)
- return
- }
- if rp.IsRoot() || (rootRp != nil && rootRp.IsNormal()) {
- if rows, err = s.dao.TxIncrSubACount(tx, rp.Oid, rp.Type, count, now); err != nil || rows == 0 {
- tx.Rollback()
- err = fmt.Errorf("TxIncrSubACount error(%v) or rows(%d)", err, rows)
- return
- }
- }
- if rp.State == model.StatePending {
- if rows, err = s.dao.TxSubDecrMCount(tx, rp.Oid, rp.Type, now); err != nil {
- tx.Rollback()
- err = fmt.Errorf("TxSubDecrMCount error(%v)", err)
- return
- }
- }
- if sub, err = s.dao.TxSubject(tx, rp.Oid, rp.Type); err != nil || sub == nil {
- tx.Rollback()
- err = fmt.Errorf(" s.dao.TxSubject(%d,%d) or rows(%d)", rp.Oid, rp.Type, rows)
- return
- }
- err = tx.Commit()
- return
- }
- func (s *Service) tranDel(c context.Context, oid, rpID int64, state int32, now time.Time) (sub *model.Subject, rp *model.Reply, err error) {
- var (
- count int32
- rootReply *model.Reply
- )
- tx, err := s.dao.BeginTran(c)
- if err != nil {
- return
- }
- rp, err = s.dao.TxReplyForUpdate(tx, oid, rpID)
- if err != nil {
- tx.Rollback()
- err = fmt.Errorf("s.dao.TxReplyForUpdate(%d,%d) error(%v) ", oid, rpID, err)
- return
- }
- if rp == nil {
- err = ecode.NothingFound
- return
- }
- if rp.AttrVal(model.AttrTopAdmin) == 1 || rp.IsDeleted() {
- if sub, err = s.dao.TxSubject(tx, rp.Oid, rp.Type); err != nil || sub == nil {
- tx.Rollback()
- err = fmt.Errorf("s.dao.TxSubject(%d,%d) error(%v)", rp.Oid, rp.Type, err)
- return
- }
- tx.Rollback()
- err = ecode.ReplyDeleted
- return
- }
- rows, err := s.dao.TxUpdateReplyState(tx, oid, rpID, state, now)
- if err != nil || rows == 0 {
- tx.Rollback()
- err = fmt.Errorf("s.dao.TxUpdateReplyState(%+v) rows:%d error(%v)", rp, rows, err)
- return
- }
- rp.MTime = xtime.Time(now.Unix())
- if rp.IsNormal() {
- if rp.IsRoot() {
- count = rp.RCount + 1
- if rows, err = s.dao.TxSubDecrACount(tx, rp.Oid, rp.Type, count, now); err != nil || rows == 0 {
- tx.Rollback()
- err = fmt.Errorf("s.dao.TxSubDecrACount(%+v) rows:%d error(%v)", rp, rows, err)
- return
- }
- rows, err = s.dao.TxDecrSubRCount(tx, rp.Oid, rp.Type, now)
- if err != nil || rows == 0 {
- tx.Rollback()
- err = fmt.Errorf("TxDecrReplyRCount(%+v) rows:%d error(%v)", rp, rows, err)
- return
- }
- } else {
- if rootReply, err = s.dao.TxReplyForUpdate(tx, rp.Oid, rp.Root); err != nil {
- tx.Rollback()
- return
- }
- if rootReply != nil {
- if rootReply.IsNormal() {
- if rows, err = s.dao.TxSubDecrACount(tx, rp.Oid, rp.Type, 1, now); err != nil || rows == 0 {
- tx.Rollback()
- err = fmt.Errorf("s.dao.TxSubDecrACount(%+v) rows:%d error(%v)", rp, rows, err)
- return
- }
- }
- _, err = s.dao.TxDecrReplyRCount(tx, rp.Oid, rp.Root, now)
- if err != nil {
- tx.Rollback()
- err = fmt.Errorf("TxDecrReplyRCount(%+v) error(%v)", rp, err)
- return
- }
- }
- }
- }
- if sub, err = s.dao.TxSubject(tx, rp.Oid, rp.Type); err != nil || sub == nil {
- tx.Rollback()
- err = fmt.Errorf("s.dao.TxSubject(%d,%d) rows:%d error(%v)", rp.Oid, rp.Type, rows, err)
- return
- }
- if rp.State == model.StatePending || rp.State == model.StateMonitor {
- if _, err = s.dao.TxSubDecrMCount(tx, rp.Oid, rp.Type, now); err != nil {
- log.Error("dao.Reply.TxSubDecrMCount(%v) error(%v)", rp, err)
- tx.Rollback()
- return
- }
- }
- if rp.AttrVal(model.AttrTopUpper) == model.AttrYes {
- rp.AttrSet(model.AttrNo, model.AttrTopUpper)
- sub.AttrSet(model.AttrNo, model.SubAttrTopUpper)
- err = sub.TopSet(0, 1, 0)
- if err != nil {
- tx.Rollback()
- log.Error("sub.TopSet(%d,%d,%d) failed!err:=%v ", rp.ID, rp.Oid, 0, err)
- return
- }
- if _, err = s.dao.TxUpSubMeta(tx, sub.Oid, sub.Type, sub.Meta, now); err != nil {
- tx.Rollback()
- log.Error("dao.TxUpMeta(oid:%d,tp:%d) err(%v) rows(%d)", sub.Oid, sub.Type, err)
- return
- }
- if rows, err = s.dao.TxUpSubAttr(tx, sub.Oid, sub.Type, sub.Attr, now); err != nil || rows == 0 {
- tx.Rollback()
- err = fmt.Errorf("s.dao.TxUpSubAttr(%+v) rows:%d error(%v)", sub, rows, err)
- return
- }
- if rows, err = s.dao.TxUpReplyAttr(tx, rp.Oid, rp.ID, rp.Attr, now); err != nil || rows == 0 {
- tx.Rollback()
- err = fmt.Errorf("s.dao.TxUpReplyAttr(%+v) rows:%d error(%v)", rp, rows, err)
- return
- }
- }
- err = tx.Commit()
- return
- }
- // ReplyTopLog ReplyTopLog
- func (s *Service) ReplyTopLog(c context.Context, sp model.LogSearchParam) (result *model.ReplyTopLogResult, err error) {
- result = &model.ReplyTopLogResult{
- Logs: []*model.ReplyTopLog{},
- }
- sp.Action = "top"
- reportData, err := s.dao.ReportLog(c, sp)
- if err != nil {
- return
- }
- result.Page = reportData.Page
- result.Sort = reportData.Sort
- result.Order = reportData.Order
- var mids []int64
- for _, data := range reportData.Result {
- mid := data.Index0
- action := data.Index1
- rpid := data.Index2
- title, link, _ := s.TitleLink(c, data.Oid, data.Type)
- var extra map[string]string
- if data.Content != "" {
- err = json.Unmarshal([]byte(data.Content), &extra)
- if err != nil {
- log.Error("MointorLog unmarshal failed!err:=%v", err)
- return
- }
- }
- result.Logs = append(result.Logs, &model.ReplyTopLog{
- Mid: mid,
- AdminID: data.AdminID,
- AdminName: data.AdminName,
- Oid: data.Oid,
- Type: data.Type,
- Remark: extra["remark"],
- CTime: data.Ctime,
- RpID: rpid,
- Action: action,
- Title: title,
- RedirectURL: link,
- })
- mids = append(mids, mid)
- }
- if len(mids) > 0 {
- var res *accmdl.InfosReply
- res, err = s.accSrv.Infos3(c, &accmdl.MidsReq{Mids: mids})
- if err != nil {
- log.Error(" s.accSrv.Infos3 (%v) error(%v)", mids, err)
- err = nil
- return
- }
- for _, log := range result.Logs {
- if user, ok := res.Infos[log.Mid]; ok {
- log.UserName = user.GetName()
- }
- }
- }
- return
- }
- // MarkAsSpam mark a reply(normal state) as spam.
- func (s *Service) MarkAsSpam(c context.Context, oids, rpIDs []int64, adminID int64, adminName, remark string) (err error) {
- rps, err := s.replies(c, oids, rpIDs)
- if err != nil {
- return
- }
- now := time.Now()
- for rpID, rp := range rps {
- if rp.State == model.StateNormal && rp.AttrVal(model.AttrGarbage) == model.AttrNo {
- var (
- rows int64
- tx *sql.Tx
- )
- tx, err = s.dao.BeginTran(c)
- if err != nil {
- return
- }
- _, err = s.dao.TxUpdateReplyState(tx, rp.Oid, rpID, model.StateGarbage, now)
- if err != nil {
- tx.Rollback()
- log.Error("s.dao.TxUpdateReplyState(%+v) error(%v)", rp, err)
- return
- }
- rp.AttrSet(model.AttrYes, model.AttrGarbage)
- _, err = s.dao.TxUpReplyAttr(tx, rp.Oid, rpID, rp.Attr, now)
- if err != nil {
- tx.Rollback()
- log.Error("s.dao.TxUpReplyAttr(%+v) rows:%d error(%v)", rp, rows, err)
- return
- }
- if err = tx.Commit(); err != nil {
- log.Error("tx.Commit(%+v) error(%v)", rp, err)
- return
- }
- if err = s.dao.DelReplyCache(c, rpID); err != nil {
- log.Error("s.dao.DelReplyCache(%+v) error(%v)", rp, err)
- }
- report.Manager(&report.ManagerInfo{
- UID: adminID,
- Uname: adminName,
- Business: 41,
- Type: int(rp.Type),
- Oid: rp.Oid,
- Ctime: now,
- Action: model.ReportActionReplyGarbage,
- Index: []interface{}{
- rp.ID,
- rp.State,
- model.StateGarbage,
- },
- })
- s.cache.Do(c, func(ctx context.Context) {
- s.addAdminLog(ctx, rp.Oid, rp.ID, adminID, rp.Type, model.AdminIsNew, model.AdminIsNotReport, model.AdminOperMarkSpam, "标记为垃圾", remark, now)
- })
- }
- }
- return
- }
- // ExportReply get exported replies by query
- func (s *Service) ExportReply(c context.Context, oid, mid int64, tp int8, state string, startTime, endTime time.Time) (data [][]string, err error) {
- if data, err = s.dao.ExportReplies(c, oid, mid, tp, state, startTime, endTime); err != nil {
- log.Error("s.dao.ExportReplies(%d,%d,%d,%s,%v,%v) error(%v)", oid, mid, tp, state, startTime, endTime)
- return
- }
- return
- }
- // ReplyList ReplyList
- func (s *Service) ReplyList(c context.Context, oids, rpids []int64) (res map[int64]*model.ReplyEx, err error) {
- res = make(map[int64]*model.ReplyEx, 0)
- replies, err := s.replies(c, oids, rpids)
- if err != nil {
- return
- }
- subjects := make(map[int32]map[int64]*model.Subject, 0)
- var roots []int64
- var rootoids []int64
- for _, data := range replies {
- sub := subjects[data.Type]
- if sub == nil {
- sub = make(map[int64]*model.Subject, 0)
- subjects[data.Type] = sub
- }
- sub[data.Oid] = nil
- if data.Root != 0 {
- rootoids = append(rootoids, data.Oid)
- roots = append(roots, data.Root)
- }
- }
- rootreplies, err := s.replies(c, rootoids, roots)
- if err != nil {
- return
- }
- for typ, data := range subjects {
- var ids []int64
- for oid := range data {
- ids = append(ids, oid)
- }
- sub, err := s.subjects(c, ids, typ)
- if err != nil {
- return res, err
- }
- subjects[typ] = sub
- }
- for _, data := range replies {
- var isUp bool
- var rootFloor int32
- sub := (subjects[data.Type])[data.Oid]
- if sub != nil && sub.Mid == data.Mid {
- isUp = true
- }
- if data.Root != 0 && rootreplies[data.Root] != nil {
- rootFloor = rootreplies[data.Root].Floor
- }
- res[data.ID] = &model.ReplyEx{*data, isUp, rootFloor}
- }
- return
- }
- // TopChildReply ...
- func (s *Service) TopChildReply(c context.Context, rootID, childID, oid int64) (err error) {
- var (
- root *model.Reply
- child *model.Reply
- ok bool
- )
- rps, err := s.dao.Replies(c, []int64{oid, oid}, []int64{rootID, childID})
- if err != nil {
- return ecode.ReplyNotExist
- }
- if root, ok = rps[rootID]; !ok {
- return ecode.ReplyNotExist
- }
- if child, ok = rps[childID]; !ok {
- return ecode.ReplyNotExist
- }
- if root.Root != 0 || child.Root != root.ID {
- return ecode.ReplyNotExist
- }
- if ok, err = s.dao.ExpireNewChildIndex(c, rootID); !ok || err != nil {
- return ecode.ReplyNotExist
- }
- if err = s.dao.TopChildReply(c, rootID, childID); err != nil {
- return ecode.ReplyNotExist
- }
- return
- }
|