go-common/app/admin/main/workflow/service/challenge.go
2019-04-22 18:49:16 +08:00

1281 lines
35 KiB
Go

package service
import (
"context"
"encoding/json"
"fmt"
"strconv"
"time"
"go-common/app/admin/main/workflow/model"
"go-common/app/admin/main/workflow/model/param"
"go-common/app/admin/main/workflow/model/search"
"go-common/library/ecode"
"go-common/library/log"
"github.com/pkg/errors"
)
// SetChallResult will set a challenge result
func (s *Service) SetChallResult(c context.Context, crp *param.ChallResParam) (err error) {
var (
chall *model.Chall
g *model.Group
rows int64
)
// Check challenge and group is exist
if chall, err = s.dao.Chall(c, crp.Cid); err != nil {
log.Error("s.dao.Chall() error(%v)", err)
return
}
if chall == nil {
log.Error("Challenge(%d) not exist", crp.Cid)
err = ecode.NothingFound
return
}
chall.SetState(uint32(crp.State), 0)
chall.AdminID = crp.AdminID
tx := s.dao.ORM.Begin()
if err = tx.Error; err != nil {
log.Error("s.dao.ORM.Begin() error(%v)", err)
return
}
defer func() {
if r := recover(); r != nil {
tx.Rollback()
log.Error("wocao jingran recover le error(%v)", r)
}
}()
if rows, err = s.dao.TxUpChall(tx, chall); err != nil {
tx.Rollback()
log.Error("s.dao.TxUpChall(%v) err(%v)", chall, err)
return
}
if g, err = s.dao.GroupByID(c, chall.Gid); err != nil {
tx.Rollback()
log.Error("s.dao.GroupByID() error(%v)", err)
return
}
if g == nil {
tx.Rollback()
log.Error("Group(%d) not exist", chall.Gid)
err = ecode.NothingFound
return
}
// decrease group pending stat count
g.Handling -= int32(rows)
if g.Handling < 0 {
g.Handling = 0
}
if err = s.dao.TxUpGroupHandling(tx, g.ID, g.Handling); err != nil {
tx.Rollback()
log.Error("Failed to update group stat count(%d) all(%d) by gid %d, error(%v)",
g.Count, g.Handling, g.ID, err)
return
}
if err = tx.Commit().Error; err != nil {
tx.Rollback()
log.Error("tx.Commit() error(%v)", err)
return
}
s.task(func() {
s.afterSetChallResult(crp, chall)
})
return
}
// BatchSetChallResult will set a set of challenges result
func (s *Service) BatchSetChallResult(c context.Context, bcrp *param.BatchChallResParam) (err error) {
var (
cids []int64 // all requested cids
pcids []int64 // pending cids
gids []int64
challs map[int64]*model.Chall
groups map[int64]*model.Group
gidToDecr map[int64]int32
)
// collect cids
cids = append(cids, bcrp.Cids...)
if challs, err = s.dao.StateChalls(c, cids, model.Pending); err != nil {
log.Error("s.dao.StateChalls() error(%v)", err)
return
}
// collect gids
gidToDecr = make(map[int64]int32, len(challs))
for _, c := range challs {
pcids = append(pcids, int64(c.Cid))
gids = append(gids, int64(c.Gid))
if _, ok := gidToDecr[c.Gid]; !ok {
gidToDecr[c.Gid] = 0
}
gidToDecr[c.Gid]++
}
if groups, err = s.dao.Groups(c, gids); err != nil {
log.Error("s.dao.Groups() error(%v)", err)
return
}
tx := s.dao.ORM.Begin()
if err = tx.Error; err != nil {
log.Error("s.dao.ORM.Begin() error(%v)", err)
return
}
defer func() {
if r := recover(); r != nil {
tx.Rollback()
log.Error("wocao jingran recover le error(%v)", r)
}
}()
if err = s.dao.TxBatchUpChallByIDs(tx, pcids, bcrp.State); err != nil {
tx.Rollback()
log.Error("s.dao.TxBatchUpChallByIDs(%+v) error(%v)", bcrp, err)
return
}
// decrease pending stat count by gidToDesc
for gid, decr := range gidToDecr {
g, ok := groups[gid]
if !ok {
log.Error("Failed to retrive group by gid %d", gid)
continue
}
// decr group handling counts
g.Handling -= decr
if g.Handling < 0 {
g.Handling = 0
}
if err = s.dao.TxUpGroupHandling(tx, gid, g.Handling); err != nil {
tx.Rollback()
log.Error("Failed to update group stat count(%d) all(%d) by gid %d, error(%v)",
g.Count, g.Handling, g.ID, err)
return
}
}
if err = tx.Commit().Error; err != nil {
tx.Rollback()
log.Error("tx.Commit() error(%v)", err)
return
}
//double write state to dispatch_state field
changedChalls, err := s.dao.Challs(c, pcids)
for cid := range changedChalls {
changedChalls[cid].SetState(uint32(bcrp.State), 0)
if err = s.dao.BatchUpChallByIDs([]int64{int64(cid)}, changedChalls[cid].DispatchState, bcrp.AdminID); err != nil {
log.Error("s.dao.BatchUpChallByIDs(%v,%d,%d) error(%v)", cid, changedChalls[cid].DispatchState, bcrp.AdminID, err)
return
}
}
s.task(func() {
s.afterBatchSetChallResult(bcrp, challs)
})
return
}
// UpChallExtraV2 will update extra data of a challenge by cid
func (s *Service) UpChallExtraV2(c context.Context, cep *param.ChallExtraParam) (err error) {
var (
obj *model.Business
chall *model.Chall
)
if chall, err = s.dao.Chall(c, cep.Cid); err != nil {
log.Error("s.dao.Chall(%d) error(%v)", cep.Cid, err)
return
}
if obj, err = s.dao.LastBusRec(c, chall.Business, chall.Oid); err != nil {
log.Error("s.dao.LastBusRec(%d, %d) error(%v)", chall.Business, chall.Oid, err)
return
}
if obj == nil {
err = ecode.NothingFound
return
}
parsed := make(map[string]interface{})
if obj.Extra != "" {
if err1 := json.Unmarshal([]byte(obj.Extra), &parsed); err1 != nil {
log.Error("json.Unmarshal(%s) error(%v)", obj.Extra, err1)
}
}
tx := s.dao.ORM.Begin()
if err = tx.Error; err != nil {
log.Error("s.dao.ORM.Begin() error(%v)", err)
return
}
defer func() {
if r := recover(); r != nil {
tx.Rollback()
log.Error("s.UpChallExtra() panic(%v)", r)
}
}()
for k, v := range cep.Extra {
parsed[k] = v
}
if _, err = s.dao.TxUpChallExtraV2(tx, chall.Business, chall.Oid, cep.AdminID, parsed); err != nil {
tx.Rollback()
log.Error("s.dao.UpChallExtra(%d, %d, %v) error(%v)", cep.Cid, cep.AdminID, parsed, err)
return
}
if err = tx.Commit().Error; err != nil {
tx.Rollback()
log.Error("tx.Commit() error(%v)", err)
return
}
return
}
// BatchUpChallExtraV2 update business object extra field by business oid
func (s *Service) BatchUpChallExtraV2(c context.Context, bcep *param.BatchChallExtraParam) (err error) {
var (
challs map[int64]*model.Chall
bs *model.Business
)
if challs, err = s.dao.Challs(c, bcep.Cids); err != nil {
log.Error("s.dao.Challs(%v) error(%v)", bcep.Cids, err)
return
}
parsed := make(map[int64]map[string]interface{}, len(challs))
for cid, chall := range challs {
if bs, err = s.dao.LastBusRec(c, chall.Business, chall.Oid); err != nil {
log.Error("s.dao.LastBusRec(%d, %d) error(%v)", chall.Business, chall.Oid, err)
return
}
inner := make(map[string]interface{})
if bs.Extra != "" {
if err1 := json.Unmarshal([]byte(bs.Extra), &inner); err1 != nil {
log.Error("json.Unmarshal(%s) error(%v)", bs.Extra, err1)
}
}
parsed[cid] = inner
}
tx := s.dao.ORM.Begin()
if err = tx.Error; err != nil {
log.Error("s.dao.ORM.Begin() error(%v)", err)
return
}
for _, ex := range parsed {
for k, v := range bcep.Extra {
ex[k] = v
}
}
for cid, ex := range parsed {
chall := challs[cid]
if _, err = s.dao.TxUpChallExtraV2(tx, chall.Business, chall.Oid, bcep.AdminID, ex); err != nil {
tx.Rollback()
log.Error("s.dao.TxUpChallExtra(%d, %d, %v) error(%v)", cid, bcep.AdminID, ex, err)
return
}
}
if err = tx.Commit().Error; err != nil {
tx.Rollback()
log.Error("tx.Commit() error(%v)", err)
return
}
return
}
// UpChallExtraV3 .
func (s *Service) UpChallExtraV3(c context.Context, cep3 *param.ChallExtraParamV3) (err error) {
//todo
return
}
// ChallList will list challenges by several conditions
// Deprecated
func (s *Service) ChallList(c context.Context, cond *search.ChallSearchCommonCond) (challPage *search.ChallListPageCommon, err error) {
var (
resp *search.ChallSearchCommonResp
cids []int64
challs map[int64]*model.Chall
challLastLog map[int64]string
attPaths map[int64][]string
t *model.TagMeta
)
if resp, err = s.dao.SearchChallenge(c, cond); err != nil {
log.Error("s.dao.SearchChall() error(%v)", err)
return
}
cids = make([]int64, 0, len(resp.Result))
for _, r := range resp.Result {
cids = append(cids, r.ID)
}
if challs, err = s.dao.Challs(c, cids); err != nil {
log.Error("s.dao.Challs() error(%v)", err)
return
}
// read state from new state field
for cid := range challs {
challs[cid].FromState()
}
if challLastLog, err = s.LastLog(c, cids, []int{model.WLogModuleChallenge}); err != nil {
log.Error("s.dao.BatchLastLog() error(%v)", err)
err = nil
}
if attPaths, err = s.dao.AttPathsByCids(c, cids); err != nil {
log.Error("s.dao.AttPathsByCids() error(%v)", err)
return
}
challPage = &search.ChallListPageCommon{}
challList := make([]*model.Chall, 0, len(resp.Result))
for _, r := range resp.Result {
cid := r.ID
c, ok := challs[cid]
if !ok {
log.Warn("Invalid challenge id %d", r.ID)
continue
}
// fill tag
if t, err = s.tag(c.Business, c.Tid); err != nil {
log.Error("Failed to retrive tag by bid(%d) tag_id(%d)", c.Business, c.Tid)
err = nil
} else {
c.Tag = t.Name
c.Round = t.RID
}
// fill last log
if l, ok := challLastLog[cid]; ok {
c.LastLog = l
}
// fill attachments
c.Attachments = make([]string, 0)
if ps, ok := attPaths[cid]; ok {
c.Attachments = ps
c.FixAttachments()
}
c.FormatState()
challList = append(challList, c)
}
challPage.Items = challList
challPage.Page = &model.Page{
Num: resp.Page.Num,
Size: resp.Page.Size,
Total: resp.Page.Total,
}
return
}
// ChallListCommon will list challenges by several conditions
func (s *Service) ChallListCommon(c context.Context, cond *search.ChallSearchCommonCond) (challPage *search.ChallListPageCommon, err error) {
var (
challSearchResp *search.ChallSearchCommonResp
business int8
cids []int64
uids []int64
oids []int64
uNames map[int64]string
mids []int64
challs map[int64]*model.Chall
challLastLog map[int64]string
challLastEvent map[int64]*model.Event
attPaths map[int64][]string
archives map[int64]*model.Archive
authors map[int64]*model.Account
lastAuditLogSchRes *search.AuditLogSearchResult // 最近的稿件操作日志
lastAuditLogExtraMap map[int64]*search.ArchiveAuditLogExtra // 稿件操作日志 extra 信息
chall *model.Chall
ps []string //attachments
ok bool
l string
)
business = cond.Business
if challSearchResp, err = s.dao.SearchChallenge(c, cond); err != nil {
log.Error("s.dao.SearchChallenge() error(%v)", err)
return
}
// no result in es
if challSearchResp.Page.Total == 0 {
challPage = &search.ChallListPageCommon{}
challPage.Items = make([]*model.Chall, 0)
challPage.Page = &model.Page{
Num: challSearchResp.Page.Num,
Size: challSearchResp.Page.Size,
Total: challSearchResp.Page.Total,
}
return
}
cids = make([]int64, 0, len(challSearchResp.Result))
oids = make([]int64, 0, len(challSearchResp.Result))
for _, r := range challSearchResp.Result {
cids = append(cids, r.ID)
}
if challs, err = s.dao.Challs(c, cids); err != nil {
log.Error("s.dao.Challs(%v) error(%v)", cids, err)
return
}
// read state from new state field
for cid, c := range challs {
challs[cid].FromState()
uids = append(uids, int64(c.AdminID))
uids = append(uids, int64(c.AssigneeAdminID))
oids = append(oids, c.Oid)
mids = append(mids, c.Mid)
}
if challLastLog, err = s.LastLog(c, cids, []int{model.WLogModuleChallenge}); err != nil {
log.Error("s.batchLastLog(%v,%v) error(%v)", cids, model.WLogModuleChallenge, err)
err = nil
}
// admin unames
if uNames, err = s.dao.BatchUNameByUID(c, uids); err != nil {
log.Error("s.dao.SearchUNameByUid(%v) error(%v)", uids, err)
err = nil
}
if attPaths, err = s.dao.AttPathsByCids(c, cids); err != nil {
log.Error("s.dao.AttPathsByCids() error(%v)", err)
return
}
if challLastEvent, err = s.batchLastEvent(c, cids); err != nil {
log.Error("s.batchLastEvent(%v) error(%v)", cids, err)
return
}
switch business {
case model.ArchiveAppeal:
if archives, err = s.dao.ArchiveRPC(c, oids); err != nil {
log.Error("s.dao.ArchiveInfos(%v) error(%v)", oids, err)
err = nil
} else {
cond := &search.AuditLogGroupSearchCond{
Businesses: []int64{3},
Order: "ctime",
PS: 100,
PN: 1,
Sort: "desc",
Oids: oids,
Group: []string{"oid"},
}
if lastAuditLogSchRes, err = s.dao.SearchAuditLogGroup(c, cond); err != nil {
log.Error("s.dao.SearchAuditLogGroup(%+v) error(%v)", cond, err)
err = nil
}
lastAuditLogExtraMap = make(map[int64]*search.ArchiveAuditLogExtra)
for _, l := range lastAuditLogSchRes.Data.Result {
extra := new(search.ArchiveAuditLogExtra)
if err = json.Unmarshal([]byte(l.ExtraData), extra); err != nil {
log.Error("json.Unmarshal(%s) error(%v)", l.ExtraData, err)
continue
}
lastAuditLogExtraMap[l.Oid] = extra
}
}
case model.CreditAppeal:
authors = s.dao.AccountInfoRPC(c, mids)
case model.ArchiveAudit:
if archives, err = s.dao.ArchiveRPC(c, oids); err != nil {
log.Error("s.dao.ArchiveInfos(%v) error(%v)", oids, err)
err = nil
} else {
cond := &search.AuditLogGroupSearchCond{
Businesses: []int64{3},
Order: "ctime",
PS: 100,
PN: 1,
Sort: "desc",
Oids: oids,
Group: []string{"oid"},
}
if lastAuditLogSchRes, err = s.dao.SearchAuditLogGroup(c, cond); err != nil {
log.Error("s.dao.SearchAuditLogGroup(%+v) error(%v)", cond, err)
err = nil
}
lastAuditLogExtraMap = make(map[int64]*search.ArchiveAuditLogExtra)
for _, l := range lastAuditLogSchRes.Data.Result {
extra := new(search.ArchiveAuditLogExtra)
if err = json.Unmarshal([]byte(l.ExtraData), extra); err != nil {
log.Error("json.Unmarshal(%s) error(%v)", l.ExtraData, err)
continue
}
lastAuditLogExtraMap[l.Oid] = extra
}
}
}
challPage = &search.ChallListPageCommon{}
challList := make([]*model.Chall, 0, len(challSearchResp.Result))
for _, r := range challSearchResp.Result {
cid := r.ID
chall, ok = challs[cid]
if !ok {
log.Warn("Invalid challenge id %d", r.ID)
continue
}
// fill tag
var t *model.TagMeta
if t, err = s.tag(chall.Business, chall.Tid); err != nil {
log.Error("s.tag(%d,%d) error(%v)", chall.Business, chall.Tid, err)
err = nil
} else {
chall.Tag = t.Name
chall.Round = t.RID
}
// fill last log
if l, ok = challLastLog[cid]; ok {
chall.LastLog = l
}
// fill last event
chall.LastEvent = challLastEvent[cid]
// fill attachments
chall.Attachments = make([]string, 0)
if ps, ok = attPaths[cid]; ok {
chall.Attachments = ps
chall.FixAttachments()
}
chall.FormatState()
//fill business object
if chall.BusinessObject, err = s.dao.LastBusRec(c, chall.Business, chall.Oid); err != nil {
log.Error("s.dao.LastBusRec(%d, %d) error(%v)", chall.Business, chall.Oid, err)
err = nil
}
//todo: add challenge meta
switch business {
case model.ArchiveAppeal: // 稿件申诉
var (
archive *model.Archive
extra *search.ArchiveAuditLogExtra
)
if archive, ok = archives[chall.Oid]; !ok {
log.Warn("failed get archive info oid(%d) cid(%d)", chall.Oid, r.ID)
}
if extra, ok = lastAuditLogExtraMap[chall.Oid]; !ok {
log.Warn("not exist archive operate last audit log extra oid(%d) cid(%d)", chall.Oid, r.ID)
}
if extra != nil && archive != nil {
archive.OPName = extra.Content.UName
archive.OPContent = extra.Diff
archive.OPRemark = extra.Content.Note
}
chall.Meta = archive
case model.CreditAppeal: //小黑屋
chall.Meta = chall.BusinessObject
if _, ok = authors[chall.Mid]; ok {
chall.MName = authors[chall.Mid].Name
}
case model.ArchiveAudit: //稿件审核
var archive *model.Archive
var extra *search.ArchiveAuditLogExtra
if archive, ok = archives[chall.Oid]; !ok {
log.Warn("failed get archive info oid(%d) cid(%d)", chall.Oid, r.ID)
}
if extra, ok = lastAuditLogExtraMap[chall.Oid]; !ok {
log.Warn("not exist archive operate last audit log extra oid(%d) cid(%d)", chall.Oid, r.ID)
}
if extra != nil && archive != nil {
archive.OPName = extra.Content.UName
archive.OPContent = extra.Diff
archive.OPRemark = extra.Content.Note
}
chall.Meta = archive
}
chall.AssigneeAdminName = uNames[chall.AssigneeAdminID]
chall.AdminName = uNames[chall.AdminID]
challList = append(challList, chall)
}
challPage.Items = challList
challPage.Page = &model.Page{
Num: challSearchResp.Page.Num,
Size: challSearchResp.Page.Size,
Total: challSearchResp.Page.Total,
}
return
}
// ChallDetail will retrive challenge by cid
func (s *Service) ChallDetail(c context.Context, cid int64) (chall *model.Chall, err error) {
var (
cl map[int64]string
attPaths []string
t *model.TagMeta
bs *model.Business
lastEvent *model.Event
archives map[int64]*model.Archive
authors map[int64]*model.Account
ok bool
l string
)
if chall, err = s.dao.Chall(c, cid); err != nil || chall == nil {
log.Error("Failed to s.dao.Chall(%d) or chall not found: %v", cid, err)
err = ecode.NothingFound
return
}
// read state from new state field
chall.FromState()
if attPaths, err = s.dao.AttPathsByCid(c, cid); err != nil {
log.Error("Failed to s.dao.AttPathsByCid(%d): %v", cid, err)
err = nil
}
if cl, err = s.LastLog(c, []int64{cid}, []int{model.WLogModuleChallenge}); err != nil {
log.Error("Failed to s.dao.LastLog(%d): %v", cid, err)
err = nil
}
if lastEvent, err = s.dao.LastEventByCid(c, cid); err != nil {
log.Error("Failed to s.dao.EventsByCid(%d): %v", cid, err)
err = nil
}
if t, err = s.tag(chall.Business, chall.Tid); err != nil {
log.Error("Failed to s.tag(%d,%d) error(%v)", chall.Business, chall.Tid, err)
// fixme: to debug
err = nil
} else {
chall.Tag = t.Name
chall.Round = t.RID
}
if bs, err = s.dao.LastBusRec(c, chall.Business, chall.Oid); err != nil {
log.Error("Failed to s.dao.BusRecByCid(%d): %v", cid, err)
err = nil
}
business := chall.Business
switch business {
case model.ArchiveAppeal:
if archives, err = s.dao.ArchiveRPC(c, []int64{chall.Oid}); err != nil {
log.Error("s.dao.ArchiveInfos(%v) error(%v)", chall.Oid, err)
err = nil
}
}
switch business {
case model.ArchiveAppeal: // 稿件申诉
if chall.Meta, ok = archives[chall.Oid]; !ok {
log.Warn("failed get archive info oid(%d) cid(%d)", chall.Oid, chall.Cid)
}
case model.CreditAppeal: //小黑屋
chall.Meta = chall.BusinessObject
if authors = s.dao.AccountInfoRPC(c, []int64{chall.Mid}); authors != nil {
if _, ok = authors[chall.Mid]; ok {
chall.MName = authors[chall.Mid].Name
}
}
}
if l, ok = cl[cid]; ok {
chall.LastLog = l
} else {
log.Info("cid(%d) not found last log", cid)
}
chall.LastEvent = lastEvent
chall.BusinessObject = bs
chall.Attachments = attPaths
chall.FixAttachments()
return
}
// UpChallBusState will update business_state field of a challenge
func (s *Service) UpChallBusState(c context.Context, cid int64, assigneeAdminid int64, assigneeAdminName string, busState int8) (err error) {
// TODO(zhoujiahui): record in log?
if cid <= 0 {
err = ecode.WkfChallNotFound
return
}
if err = s.dao.UpChallBusState(c, cid, busState, assigneeAdminid); err != nil {
return
}
s.task(func() {
var (
result []*search.ChallSearchCommonData
challs []*model.Chall
)
cond := &search.ChallSearchCommonCond{
Fields: []string{"id", "oid", "business", "mid", "typeid"},
IDs: []int64{cid},
}
if result, err = s.dao.SearchChallengeMultiPage(context.Background(), cond); err != nil {
log.Error("s.dao.SearchChallengeMultiPage(%+v) error(%v)", cond, err)
return
}
for _, r := range result {
c := &model.Chall{
Cid: r.ID,
Oid: r.Oid,
Business: r.Business,
BusinessState: busState,
AssigneeAdminID: assigneeAdminid,
AssigneeAdminName: assigneeAdminName,
Mid: r.Mid,
TypeID: r.TypeID,
}
challs = append(challs, c)
}
s.afterSetBusinessState(challs)
})
return
}
// BatchUpChallBusState will update business_state field of a set of challenges
func (s *Service) BatchUpChallBusState(c context.Context, cids []int64, assigneeAdminid int64, assigneeAdminName string, busState int8) (err error) {
// TODO(zhoujiahui): record in log?
if len(cids) <= 0 {
return
}
if err = s.dao.BatchUpChallBusState(c, cids, busState, assigneeAdminid); err != nil {
return
}
s.task(func() {
var (
result []*search.ChallSearchCommonData
challs []*model.Chall
)
cond := &search.ChallSearchCommonCond{
Fields: []string{"id", "oid", "business", "mid", "typeid"},
IDs: cids,
}
if result, err = s.dao.SearchChallengeMultiPage(context.Background(), cond); err != nil {
log.Error("s.dao.SearchChallengeMultiPage(%+v) error(%v)", cond, err)
return
}
for _, r := range result {
c := &model.Chall{
Cid: r.ID,
Oid: r.Oid,
Business: r.Business,
BusinessState: busState,
AssigneeAdminID: assigneeAdminid,
AssigneeAdminName: assigneeAdminName,
Mid: r.Mid,
TypeID: r.TypeID,
}
challs = append(challs, c)
}
s.afterSetBusinessState(challs)
})
return
}
// SetChallBusState will update business_state field of a set of challenges
func (s *Service) SetChallBusState(c context.Context, bcbsp *param.BatchChallBusStateParam) (err error) {
// TODO(zhoujiahui): record in log?
if len(bcbsp.Cids) <= 0 {
return
}
if err = s.dao.BatchUpChallBusState(c, bcbsp.Cids, bcbsp.BusState, bcbsp.AssigneeAdminID); err != nil {
return
}
s.task(func() {
var (
result []*search.ChallSearchCommonData
challs []*model.Chall
)
cond := &search.ChallSearchCommonCond{
Fields: []string{"id", "oid", "business", "mid", "typeid"},
IDs: bcbsp.Cids,
}
if result, err = s.dao.SearchChallengeMultiPage(context.Background(), cond); err != nil {
log.Error("s.dao.SearchChallengeMultiPage(%+v) error(%v)", cond, err)
return
}
for _, r := range result {
c := &model.Chall{
Cid: r.ID,
Oid: r.Oid,
Business: r.Business,
BusinessState: bcbsp.BusState,
AssigneeAdminID: bcbsp.AssigneeAdminID,
AssigneeAdminName: bcbsp.AssigneeAdminName,
Mid: r.Mid,
TypeID: r.TypeID,
}
challs = append(challs, c)
}
s.afterSetBusinessState(challs)
})
return
}
// UpBusChallsBusState will update business_state field of a set of challenges with same business and oid
func (s *Service) UpBusChallsBusState(c context.Context, business, busState int8, preBusStates []int8, oid int64, assigneeAdminid int64, extra map[string]interface{}) (cids []int64, err error) {
// TODO(zhoujiahui): record in log?
tx := s.dao.ORM.Begin()
if err = tx.Error; err != nil {
log.Error("s.dao.ORM.Begin() error(%v)", err)
return
}
defer func() {
if r := recover(); r != nil {
tx.Rollback()
log.Error("wocao jingran recover le error(%v)", r)
}
}()
if cids, err = s.dao.TxChallsByBusStates(tx, business, oid, preBusStates); err != nil {
tx.Rollback()
log.Error("s.dao.TxChallsByBusStates(%d, %d, %s) error(%v)", business, oid, preBusStates, err)
return
}
if err = s.dao.TxUpChallsBusStateByIDs(tx, cids, busState, assigneeAdminid); err != nil {
tx.Rollback()
log.Error("s.dao.TxUpChallsBusStateByIDs(%s, %d) error(%v)", cids, busState, err)
return
}
if err = tx.Commit().Error; err != nil {
tx.Rollback()
log.Error("tx.Commit() error(%v)", err)
return
}
bcep := &param.BatchChallExtraParam{
Cids: cids,
AdminID: assigneeAdminid,
Extra: extra,
}
if err = s.BatchUpChallExtraV2(c, bcep); err != nil {
log.Error("s.BatchUpChallExtra(%v) error(%v)", bcep, err)
return
}
// double write to new field
challs, err := s.dao.Challs(c, cids)
if err != nil {
return
}
for cid := range challs {
challs[cid].SetState(uint32(busState), uint8(1))
if err = s.dao.ORM.Table("workflow_chall").Where("id=?", cid).Update("dispatch_state", challs[cid].DispatchState).Error; err != nil {
err = errors.Wrapf(err, "cid(%d), dispatch_state(%d)", cid, challs[cid].DispatchState)
return
}
}
return
}
// RstChallResult will reset challenge and its linked group state as Pending
func (s *Service) RstChallResult(c context.Context, crp *param.ChallRstParam) (err error) {
var (
chall *model.Chall
group *model.Group
)
if chall, err = s.dao.Chall(c, crp.Cid); err != nil || chall == nil {
log.Error("Failed to query challenge(%d) or it not exist: %v", crp.Cid, err)
return
}
if group, err = s.dao.GroupByID(c, chall.Gid); err != nil || group == nil {
log.Error("Failed to query group(%d) or it not exist: %v", chall.Gid, err)
return
}
// update new field
chall.SetState(uint32(crp.State), 0)
tx := s.dao.ORM.Begin()
if err = tx.Error; err != nil {
log.Error("s.dao.ORM.Begin() error(%v)", err)
return
}
defer func() {
if r := recover(); r != nil {
tx.Rollback()
log.Error("s.RstChallResult() panic(%v)", r)
}
}()
if err = tx.Model(chall).UpdateColumn(map[string]interface{}{
"state": crp.State,
"dispatch_state": chall.DispatchState,
}).Error; err != nil {
tx.Rollback()
log.Error("Failed to set chall(%v) as pending: %v", chall, err)
return
}
if err = tx.Model(group).UpdateColumn(map[string]interface{}{
"state": crp.State,
}).Error; err != nil {
tx.Rollback()
log.Error("Failed to set group(%v) as pending: %v", group, err)
return
}
if err = tx.Commit().Error; err != nil {
tx.Rollback()
log.Error("Failed to tx.Commit() in RstChallResult: %v", err)
return
}
s.task(func() {
cl := &model.WLog{
AdminID: crp.AdminID,
Admin: crp.AdminName,
Oid: chall.Oid,
Business: chall.Business,
Target: chall.Cid,
Module: model.WLogModuleChallenge,
Remark: fmt.Sprintf(`“工单详情编号 %d”设置为 %s 移交复审`, chall.Cid, s.StateDescr(chall.Business, 0, crp.State)),
Note: crp.Reason,
}
gl := &model.WLog{
AdminID: crp.AdminID,
Admin: crp.AdminName,
Oid: group.Oid,
Business: group.Business,
Target: group.ID,
Module: model.WLogModuleGroup,
Remark: fmt.Sprintf(`“工单编号 %d”设置为 %s 移交复审`, group.ID, s.StateDescr(chall.Business, 0, crp.State)),
Note: crp.Reason,
}
s.writeAuditLog(cl)
s.writeAuditLog(gl)
})
return
}
// BusinessList will retrive business object by cids
// Deprecated
func (s *Service) BusinessList(c context.Context, cids []int64) (cidToBus map[int64]*model.Business, err error) {
if cidToBus, err = s.dao.BatchBusRecByCids(c, cids); err != nil {
log.Error("s.dao.BatchBusRecByCids(%v) error(%v)", cids, err)
return
}
return
}
// UpChall will update challenge tid
func (s *Service) UpChall(c context.Context, cup *param.ChallUpParam) (err error) {
var (
chall *model.Chall
t *model.TagMeta
)
// Check group and tag is exist
if chall, err = s.dao.Chall(c, cup.Cid); err != nil {
log.Error("s.dao.Chall(%d) error(%v)", cup.Cid, err)
return
}
if chall == nil {
log.Error("Challenge(%d) not exist", cup.Cid)
err = ecode.WkfChallNotFound
return
}
if t, err = s.tag(chall.Business, cup.Tid); err != nil {
log.Error("bid(%d) tag_id(%d) not found in cache", chall.Business, cup.Tid)
return
}
tx := s.dao.ORM.Begin()
if err = tx.Error; err != nil {
log.Error("s.dao.ORM.Begin() error(%v)", err)
return
}
defer func() {
if r := recover(); r != nil {
tx.Rollback()
log.Error("wocao jingran recover le error(%v)", r)
}
}()
if err = s.dao.TxUpChallTag(tx, cup.Cid, cup.Tid); err != nil {
tx.Rollback()
log.Error("s.TxUpChallTag(%d, %d) error(%v)", cup.Cid, cup.Tid, err)
return
}
if err = tx.Commit().Error; err != nil {
tx.Rollback()
log.Error("tx.Commit() error(%v)", err)
return
}
s.task(func() {
l := &model.WLog{
Oid: chall.Oid,
Business: chall.Business,
Target: chall.Cid,
Module: model.WLogModuleChallenge,
AdminID: cup.AdminID,
Admin: cup.AdminName,
Remark: fmt.Sprintf(`工单编号 %d “管理 Tag”更新为“%s”`, cup.Cid, t.Name),
}
s.writeAuditLog(l)
})
return
}
// ChallListV3 .
func (s *Service) ChallListV3(c context.Context, cond *search.ChallSearchCommonCond) (challPage *search.ChallListPageCommon, err error) {
var (
challSearchResp *search.ChallSearchCommonResp
uNames map[int64]string
challs map[int64]*model.Chall
challLastLog map[int64]string
challLastEvent map[int64]*model.Event
attPaths map[int64][]string
gidToBus map[int64]*model.Business
users map[int64]*model.Account
b *model.Business
chall *model.Chall
ps []string //attachments
ok bool
meta interface{}
)
if challSearchResp, err = s.dao.SearchChallenge(c, cond); err != nil {
log.Error("s.dao.SearchChallenge() error(%v)", err)
err = ecode.WkfSearchChallFailed
return
}
// no result in es
if challSearchResp.Page.Total == 0 {
challPage = &search.ChallListPageCommon{}
challPage.Items = make([]*model.Chall, 0)
challPage.Page = &model.Page{
Num: challSearchResp.Page.Num,
Size: challSearchResp.Page.Size,
Total: challSearchResp.Page.Total,
}
return
}
cids := make([]int64, 0, len(challSearchResp.Result))
oids := make([]int64, 0, len(challSearchResp.Result))
mids := make([]int64, 0, len(challSearchResp.Result))
gids := make([]int64, 0, len(challSearchResp.Result))
uids := make([]int64, 0, len(challSearchResp.Result)*2)
for _, r := range challSearchResp.Result {
cids = append(cids, r.ID)
oids = append(oids, r.Oid)
mids = append(mids, r.Mid)
gids = append(gids, r.Gid)
}
if challs, err = s.dao.Challs(c, cids); err != nil {
log.Error("s.dao.Challs(%v) error(%v)", cids, err)
return
}
// read state from new state field
for cid, c := range challs {
challs[cid].FromState()
uids = append(uids, int64(c.AdminID))
uids = append(uids, int64(c.AssigneeAdminID))
}
if challLastLog, err = s.LastLog(c, cids, []int{model.WLogModuleChallenge}); err != nil {
log.Error("s.LastLog(%v,%v) error(%v)", cids, model.WLogModuleChallenge, err)
err = nil
}
// admin unames
if uNames, err = s.dao.BatchUNameByUID(c, uids); err != nil {
log.Error("s.dao.SearchUNameByUid(%v) error(%v)", uids, err)
err = nil
}
if attPaths, err = s.dao.AttPathsByCids(c, cids); err != nil {
log.Error("s.dao.AttPathsByCids() error(%v)", err)
return
}
if gidToBus, err = s.dao.BusObjectByGids(c, gids); err != nil {
log.Error("s.dao.BusObjectByGids(%v) error(%v)", gids, err)
return
}
if challLastEvent, err = s.batchLastEvent(c, cids); err != nil {
log.Error("s.batchLastEvent(%v) error(%v)", cids, err)
return
}
// user account
users = s.dao.AccountInfoRPC(c, mids)
// load meta
meta = s.searchChallMeta(c, cond.Business, oids, users)
challPage = &search.ChallListPageCommon{}
challList := make([]*model.Chall, 0, len(challSearchResp.Result))
for _, r := range challSearchResp.Result {
var (
t *model.TagMeta
l string
)
cid := r.ID
chall, ok = challs[cid]
if !ok {
log.Warn("Invalid challenge id %d", r.ID)
continue
}
// fill tag
if t, err = s.tag(chall.Business, chall.Tid); err != nil {
log.Error("s.tag(%d,%d) error(%v)", chall.Business, chall.Tid, err)
err = nil
} else {
chall.Tag = t.Name
chall.Round = t.RID
}
// fill last log
if l, ok = challLastLog[cid]; ok {
chall.LastLog = l
}
// fill last event
chall.LastEvent = challLastEvent[cid]
// fill attachments
chall.Attachments = make([]string, 0)
if ps, ok = attPaths[cid]; ok {
chall.Attachments = ps
chall.FixAttachments()
}
chall.FormatState()
//fill business object
if b, ok = gidToBus[chall.Gid]; ok {
chall.BusinessObject = b
}
//fill meta
s.wrapChallMeta(cond.Business, chall, meta)
chall.AssigneeAdminName = uNames[chall.AssigneeAdminID]
chall.AdminName = uNames[chall.AdminID]
if chall.Producer, ok = users[chall.Mid]; !ok {
log.Warn("failed get producer info mid(%d)", chall.Mid)
}
chall.OidStr = strconv.FormatInt(chall.Oid, 10)
challList = append(challList, chall)
}
challPage.Items = challList
challPage.Page = &model.Page{
Num: challSearchResp.Page.Num,
Size: challSearchResp.Page.Size,
Total: challSearchResp.Page.Total,
}
return
}
func (s *Service) searchChallMeta(c context.Context, business int8, oids []int64, users map[int64]*model.Account) (meta interface{}) {
var (
ok bool
err error
archives map[int64]*model.Archive
)
switch business {
case model.ArchiveAppeal, model.ArchiveAudit:
var (
resp *search.AuditLogSearchCommonResult
lastAuditLogExtraMap = make(map[int64]*search.ArchiveAuditLogExtra)
)
if archives, err = s.dao.ArchiveRPC(c, oids); err != nil {
log.Error("s.dao.ArchiveInfosV3(%v) error(%v)", oids, err)
err = nil
return archives
}
cond := &search.AuditReportSearchCond{
Business: 3,
Fields: []string{"oid", "extra_data"},
Order: "ctime",
Sort: "desc",
Oid: oids,
Distinct: "oid",
IndexTimeType: "month",
IndexTimeFrom: time.Now().AddDate(0, -6, 0),
IndexTimeEnd: time.Now(),
}
if resp, err = s.dao.SearchAuditReportLog(c, cond); err != nil {
log.Error("s.dao.SearchAuditReportLog(%+v) error(%v)", cond, err)
err = nil
}
for _, l := range resp.Result {
extra := new(search.ArchiveAuditLogExtra)
if err = json.Unmarshal([]byte(l.ExtraData), extra); err != nil {
log.Error("json.Unmarshal(%s) error(%v)", l.ExtraData, err)
continue
}
lastAuditLogExtraMap[l.Oid] = extra
}
for _, l := range resp.Result {
extra := new(search.ArchiveAuditLogExtra)
if err = json.Unmarshal([]byte(l.ExtraData), extra); err != nil {
log.Error("json.Unmarshal(%s) error(%v)", l.ExtraData, err)
continue
}
}
for oid, a := range archives {
if a.Composer, ok = users[a.Mid]; !ok {
log.Warn("failed get account info mid(%d)", a.Mid)
a.Composer = &model.Account{}
continue
}
var extra *search.ArchiveAuditLogExtra
if extra, ok = lastAuditLogExtraMap[oid]; !ok {
log.Warn("failed get audit log of archive(%d)", oid)
continue
} else {
a.OPName = extra.Content.UName
a.OPContent = extra.Diff
a.OPRemark = extra.Content.Note
}
}
}
return
}
func (s *Service) wrapChallMeta(business int8, chall *model.Chall, meta interface{}) {
switch business {
case model.ArchiveAppeal: // 稿件申诉
var (
a *model.Archive
archives map[int64]*model.Archive
ok bool
)
if archives, ok = meta.(map[int64]*model.Archive); !ok {
return
}
if a, ok = archives[chall.Oid]; !ok {
log.Warn("failed get archive info oid(%d) cid(%d)", chall.Oid, chall.Cid)
chall.Meta = struct{}{}
break
}
chall.Meta = a
case model.CreditAppeal: //小黑屋
if chall.BusinessObject == nil {
log.Warn("can not get credit appeal info oid(%d) cid(%d) business(%d)", chall.Oid, chall.Cid, chall.Business)
break
}
cMeta := &model.CreditMeta{
Business: chall.BusinessObject,
}
chall.Meta = cMeta
case model.ArchiveAudit: //稿件审核
var (
a *model.Archive
archives map[int64]*model.Archive
ok bool
)
if archives, ok = meta.(map[int64]*model.Archive); !ok {
return
}
if a, ok = archives[chall.Oid]; !ok {
log.Warn("failed get archive info oid(%d) cid(%d)", chall.Oid, chall.Cid)
}
chall.Meta = a
}
}