1038 lines
27 KiB
Go
1038 lines
27 KiB
Go
package service
|
||
|
||
import (
|
||
"bytes"
|
||
"context"
|
||
"fmt"
|
||
"sort"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
|
||
"go-common/app/admin/main/growup/dao"
|
||
"go-common/app/admin/main/growup/dao/resource"
|
||
"go-common/app/admin/main/growup/model"
|
||
"go-common/app/admin/main/growup/util"
|
||
|
||
"go-common/library/database/sql"
|
||
"go-common/library/ecode"
|
||
"go-common/library/log"
|
||
"go-common/library/xstr"
|
||
)
|
||
|
||
const (
|
||
awardEditable = iota + 1
|
||
awardInfoEditable
|
||
awardDivisionEditable
|
||
awardPrizeEditable
|
||
awardResourceEditable
|
||
)
|
||
|
||
const (
|
||
_awardResourceTypeRule = 1
|
||
_awardResourceTypeDetail = 3
|
||
_awardResourceTypeQ = 5
|
||
_awardResourceTypeA = 6
|
||
)
|
||
|
||
func awardEditPerms(award *model.Award) (res map[int]bool) {
|
||
var (
|
||
now = time.Now()
|
||
notDisplay = award.DisplayStatus == 1
|
||
notFinished = award.OpenStatus == 1
|
||
preCycleStart = now.Before(award.CycleStart)
|
||
)
|
||
res = map[int]bool{
|
||
awardEditable: notDisplay || notFinished,
|
||
awardInfoEditable: notDisplay || preCycleStart,
|
||
awardDivisionEditable: notDisplay || preCycleStart,
|
||
awardPrizeEditable: notDisplay || notFinished,
|
||
awardResourceEditable: notDisplay || notFinished,
|
||
}
|
||
return
|
||
}
|
||
|
||
func (s *Service) generateAwardID() (awardID int64, err error) {
|
||
awardID, err = util.NewSnowFlake().Generate()
|
||
return
|
||
}
|
||
|
||
func (s *Service) validateAwardCycle(c context.Context, awardID int64, start, end time.Time) (ok bool, err error) {
|
||
// simplified version: check all
|
||
awardBases, err := s.dao.ListAward(c)
|
||
if err != nil {
|
||
return
|
||
}
|
||
between := func(t, a, b time.Time) bool {
|
||
return !(t.Before(a) || t.After(b))
|
||
}
|
||
for _, v := range awardBases {
|
||
if awardID == v.AwardID || v.DisplayStatus != 2 {
|
||
continue
|
||
}
|
||
if between(start, v.CycleStart, v.CycleEnd) || between(end, v.CycleStart, v.CycleEnd) {
|
||
ok = false
|
||
return
|
||
}
|
||
}
|
||
ok = true
|
||
return
|
||
}
|
||
|
||
// AddAward .
|
||
func (s *Service) AddAward(c context.Context, arg *model.AddAwardArg, username string) (awardID int64, err error) {
|
||
// 1. validation
|
||
if !(arg.DisplayStatus == 1 || arg.DisplayStatus == 2) {
|
||
err = ecode.RequestErr
|
||
return
|
||
}
|
||
// 2. args
|
||
// generate awardID
|
||
awardID, err = s.generateAwardID()
|
||
if err != nil {
|
||
return
|
||
}
|
||
// cycle
|
||
start := util.ToDayStart(time.Unix(arg.CycleStart, 0))
|
||
end := util.ToDayEnd(time.Unix(arg.CycleEnd, 0))
|
||
if arg.CycleStart > 0 && arg.CycleEnd > 0 && arg.DisplayStatus == 2 {
|
||
var validCycle bool
|
||
validCycle, err = s.validateAwardCycle(c, awardID, start, end)
|
||
if err != nil {
|
||
return
|
||
}
|
||
if !validCycle {
|
||
err = ecode.Error(ecode.RequestErr, "评选周期重叠")
|
||
return
|
||
}
|
||
}
|
||
// total
|
||
totalWinner, totalBonus := 0, 0
|
||
if len(arg.Prizes) > 0 {
|
||
for _, v := range arg.Prizes {
|
||
totalWinner += v.Quota
|
||
totalBonus += v.Bonus * v.Quota
|
||
}
|
||
}
|
||
// 3. tx insert
|
||
var (
|
||
cycleStart = start.Format("2006-01-02 15:04:05")
|
||
cycleEnd = end.Format("2006-01-02 15:04:05")
|
||
announce = util.ToDayNoon(time.Unix(arg.AnnounceDate, 0))
|
||
announceDate = announce.Format("2006-01-02")
|
||
openTime = announce.Format("2006-01-02 15:04:05")
|
||
)
|
||
err = s.dao.DoInTx(c, func(tx *sql.Tx) (err error) {
|
||
_, err = dao.AddAward(tx, awardID, arg.AwardName, cycleStart, cycleEnd, announceDate, openTime,
|
||
arg.DisplayStatus, totalWinner, totalBonus, username)
|
||
if err != nil {
|
||
return
|
||
}
|
||
if len(arg.Divisions) > 0 {
|
||
fields, values := saveDivisionStr(awardID, arg.Divisions)
|
||
if values != "" {
|
||
_, err = dao.SaveDivisions(tx, fields, values)
|
||
if err != nil {
|
||
return
|
||
}
|
||
}
|
||
}
|
||
if len(arg.Prizes) > 0 {
|
||
fields, values := savePrizesStr(awardID, arg.Prizes)
|
||
if values != "" {
|
||
_, err = dao.SavePrizes(tx, fields, values)
|
||
if err != nil {
|
||
return
|
||
}
|
||
}
|
||
}
|
||
if arg.Resources != nil {
|
||
fields, values := saveResourcesStr(awardID, arg.Resources)
|
||
if values != "" {
|
||
_, err = dao.SaveResource(tx, fields, values)
|
||
if err != nil {
|
||
return
|
||
}
|
||
}
|
||
}
|
||
return nil
|
||
})
|
||
|
||
return
|
||
}
|
||
|
||
func saveWinnerStr(winnersM map[int64]*model.AwardWinner) (fields, values string) {
|
||
if len(winnersM) == 0 {
|
||
return
|
||
}
|
||
fields = "award_id,mid,division_id,prize_id,tag_id"
|
||
var buf bytes.Buffer
|
||
for _, v := range winnersM {
|
||
buf.WriteString("(")
|
||
buf.WriteString(strconv.FormatInt(v.AwardID, 10))
|
||
buf.WriteByte(',')
|
||
buf.WriteString(strconv.FormatInt(v.MID, 10))
|
||
buf.WriteByte(',')
|
||
buf.WriteString(strconv.FormatInt(v.DivisionID, 10))
|
||
buf.WriteByte(',')
|
||
buf.WriteString(strconv.FormatInt(v.PrizeID, 10))
|
||
buf.WriteByte(',')
|
||
buf.WriteString(strconv.FormatInt(v.TagID, 10))
|
||
buf.WriteString(")")
|
||
buf.WriteByte(',')
|
||
}
|
||
if buf.Len() > 0 {
|
||
buf.Truncate(buf.Len() - 1)
|
||
}
|
||
values = buf.String()
|
||
values += " ON DUPLICATE KEY UPDATE division_id=VALUES(division_id), prize_id=VALUES(prize_id), tag_id=VALUES(tag_id), is_deleted=0"
|
||
buf.Reset()
|
||
return
|
||
}
|
||
|
||
func saveDivisionStr(awardID int64, divisions []*model.AwardDivision) (fields, values string) {
|
||
if len(divisions) == 0 {
|
||
return
|
||
}
|
||
vs := make([]string, 0)
|
||
for i, v := range divisions {
|
||
vs = append(vs, fmt.Sprintf("(%d,%d,'%s',%d)", awardID, i+1, v.DivisionName, v.TagID))
|
||
}
|
||
if len(vs) > 0 {
|
||
fields = "award_id,division_id,division_name,tag_id"
|
||
values = strings.Join(vs, ",") + " ON DUPLICATE KEY UPDATE division_name=VALUES(division_name), tag_id=VALUES(tag_id), is_deleted=0"
|
||
}
|
||
return
|
||
}
|
||
|
||
func savePrizesStr(awardID int64, prizes []*model.AwardPrize) (fields, values string) {
|
||
if len(prizes) == 0 {
|
||
return
|
||
}
|
||
vs := make([]string, 0)
|
||
for i, v := range prizes {
|
||
vs = append(vs, fmt.Sprintf("(%d,%d,%d,%d)", awardID, i+1, v.Bonus, v.Quota))
|
||
}
|
||
if len(vs) > 0 {
|
||
fields = "award_id,prize_id,bonus,quota"
|
||
values = strings.Join(vs, ",") + " ON DUPLICATE KEY UPDATE bonus=VALUES(bonus), quota=VALUES(quota), is_deleted=0"
|
||
}
|
||
return
|
||
}
|
||
|
||
func delResourcesStr(awardID int64, arg *model.AwardResource) (delWhere string) {
|
||
if len(arg.QA) == 0 {
|
||
return fmt.Sprintf("award_id = %d AND resource_type IN (%d,%d)", awardID, _awardResourceTypeQ, _awardResourceTypeA)
|
||
}
|
||
idx := make([]int64, 0)
|
||
for i := range arg.QA {
|
||
idx = append(idx, int64(i+1))
|
||
}
|
||
return fmt.Sprintf("award_id = %d AND resource_type IN (%d,%d) AND resource_index NOT IN (%s)",
|
||
awardID, _awardResourceTypeQ, _awardResourceTypeA, xstr.JoinInts(idx))
|
||
}
|
||
|
||
func saveResourcesStr(awardID int64, arg *model.AwardResource) (fields, values string) {
|
||
if arg == nil {
|
||
return
|
||
}
|
||
vs := make([]string, 0)
|
||
vs = append(vs, fmt.Sprintf("(%d,%d,1,'%s')", awardID, _awardResourceTypeRule, arg.Rule))
|
||
vs = append(vs, fmt.Sprintf("(%d,%d,1,'%s')", awardID, _awardResourceTypeDetail, arg.Detail))
|
||
if len(arg.QA) > 0 {
|
||
for i, qa := range arg.QA {
|
||
vs = append(vs, fmt.Sprintf("(%d,%d,%d,'%s')", awardID, _awardResourceTypeQ, i+1, qa.Q))
|
||
vs = append(vs, fmt.Sprintf("(%d,%d,%d,'%s')", awardID, _awardResourceTypeA, i+1, qa.A))
|
||
}
|
||
}
|
||
if len(vs) > 0 {
|
||
fields = "award_id,resource_type,resource_index,content"
|
||
values = strings.Join(vs, ",") + " ON DUPLICATE KEY UPDATE content=VALUES(content), is_deleted=0"
|
||
}
|
||
return
|
||
}
|
||
|
||
// UpdateAward .
|
||
func (s *Service) UpdateAward(c context.Context, arg *model.SaveAwardArg) (err error) {
|
||
// 1.
|
||
if !(arg.DisplayStatus == 1 || arg.DisplayStatus == 2) || arg.AwardID == 0 {
|
||
return ecode.Error(ecode.RequestErr, "illegal param")
|
||
}
|
||
awardID := arg.AwardID
|
||
award, err := s.dao.Award(c, awardID)
|
||
if err != nil {
|
||
return
|
||
}
|
||
if award == nil {
|
||
err = ecode.Error(ecode.RequestErr, "illegal award_id")
|
||
return
|
||
}
|
||
//
|
||
permission := awardEditPerms(award)
|
||
if !permission[awardEditable] {
|
||
err = ecode.Error(ecode.RequestErr, "award no longer editable")
|
||
return
|
||
}
|
||
// sum
|
||
totalQuota, totalBonus := 0, 0
|
||
if len(arg.Prizes) > 0 {
|
||
for _, v := range arg.Prizes {
|
||
totalQuota += v.Quota
|
||
totalBonus += v.Bonus * v.Quota
|
||
}
|
||
}
|
||
newAward := &model.Award{
|
||
AwardName: arg.AwardName,
|
||
CycleStart: util.ToDayStart(time.Unix(arg.CycleStart, 0)),
|
||
CycleEnd: util.ToDayEnd(time.Unix(arg.CycleEnd, 0)),
|
||
AnnounceDate: time.Unix(arg.AnnounceDate, 0),
|
||
OpenTime: util.ToDayNoon(time.Unix(arg.AnnounceDate, 0)),
|
||
DisplayStatus: arg.DisplayStatus,
|
||
TotalQuota: totalQuota,
|
||
TotalBonus: totalBonus,
|
||
}
|
||
|
||
if permission[awardInfoEditable] && arg.DisplayStatus == 2 {
|
||
if !newAward.CycleStart.Equal(award.CycleStart) || !newAward.CycleEnd.Equal(award.CycleEnd) {
|
||
var ok bool
|
||
ok, err = s.validateAwardCycle(c, awardID, newAward.CycleStart, newAward.CycleEnd)
|
||
if err != nil {
|
||
return
|
||
}
|
||
if !ok {
|
||
err = ecode.Error(ecode.RequestErr, "评选周期重叠")
|
||
return
|
||
}
|
||
}
|
||
}
|
||
|
||
err = s.dao.DoInTx(c, func(tx *sql.Tx) (err error) {
|
||
// lock
|
||
award, err = dao.SelectAwardForUpdate(tx, awardID)
|
||
if err != nil {
|
||
return
|
||
}
|
||
if award == nil {
|
||
return ecode.Error(ecode.RequestErr, "award not found")
|
||
}
|
||
//// award info
|
||
if permission[awardInfoEditable] {
|
||
updateAwardStr := fmt.Sprintf(`award_name='%s', cycle_start='%s', cycle_end='%s',
|
||
announce_date='%s', open_time='%s', display_status=%d, total_quota=%d, total_bonus=%d`,
|
||
newAward.AwardName,
|
||
newAward.CycleStart.Format("2006-01-02 15:04:05"),
|
||
newAward.CycleEnd.Format("2006-01-02 15:04:05"),
|
||
newAward.AnnounceDate.Format("2006-01-02"),
|
||
newAward.OpenTime.Format("2006-01-02 15:04:05"),
|
||
newAward.DisplayStatus, newAward.TotalQuota, newAward.TotalBonus)
|
||
_, err = dao.UpdateAward(tx, awardID, updateAwardStr)
|
||
if err != nil {
|
||
return
|
||
}
|
||
}
|
||
//// divisions
|
||
if permission[awardDivisionEditable] {
|
||
switch {
|
||
case len(arg.Divisions) == 0:
|
||
_, err = dao.DelDivisionAll(tx, awardID)
|
||
if err != nil {
|
||
return
|
||
}
|
||
default:
|
||
// del
|
||
argDivisionIDs := make([]int64, 0)
|
||
for i, v := range arg.Divisions {
|
||
v.DivisionID = int64(i + 1)
|
||
argDivisionIDs = append(argDivisionIDs, v.DivisionID)
|
||
}
|
||
_, err = dao.DelDivisionsExclude(tx, awardID, argDivisionIDs)
|
||
if err != nil {
|
||
return
|
||
}
|
||
// save
|
||
fields, values := saveDivisionStr(awardID, arg.Divisions)
|
||
_, err = dao.SaveDivisions(tx, fields, values)
|
||
if err != nil {
|
||
return
|
||
}
|
||
}
|
||
}
|
||
//// prizes
|
||
if permission[awardPrizeEditable] {
|
||
switch {
|
||
case len(arg.Prizes) == 0:
|
||
_, err = dao.DelPrizeAll(tx, awardID)
|
||
if err != nil {
|
||
return
|
||
}
|
||
default:
|
||
// del
|
||
argPrizeIDs := make([]int64, 0)
|
||
for i, v := range arg.Prizes {
|
||
v.PrizeID = int64(i + 1)
|
||
argPrizeIDs = append(argPrizeIDs, v.PrizeID)
|
||
}
|
||
_, err = dao.DelPrizesExclude(tx, awardID, argPrizeIDs)
|
||
if err != nil {
|
||
return
|
||
}
|
||
fields, values := savePrizesStr(awardID, arg.Prizes)
|
||
_, err = dao.SavePrizes(tx, fields, values)
|
||
if err != nil {
|
||
return
|
||
}
|
||
}
|
||
}
|
||
//// resources
|
||
if permission[awardResourceEditable] {
|
||
switch {
|
||
case arg.Resources == nil:
|
||
_, err = dao.DelResources(tx, fmt.Sprintf("award_id = %d", awardID))
|
||
if err != nil {
|
||
return
|
||
}
|
||
default:
|
||
delWhere := delResourcesStr(awardID, arg.Resources)
|
||
_, err = dao.DelResources(tx, delWhere)
|
||
if err != nil {
|
||
return
|
||
}
|
||
fields, values := saveResourcesStr(awardID, arg.Resources)
|
||
_, err = dao.SaveResource(tx, fields, values)
|
||
if err != nil {
|
||
return
|
||
}
|
||
}
|
||
}
|
||
return nil
|
||
})
|
||
return err
|
||
}
|
||
|
||
// ListAward .
|
||
func (s *Service) ListAward(c context.Context, from, limit int) (total int64, data []*model.AwardListModel, err error) {
|
||
data = make([]*model.AwardListModel, 0)
|
||
var (
|
||
awardToWinnerC map[int64]int
|
||
awards []*model.Award
|
||
divisions []*model.AwardDivision
|
||
)
|
||
err = s.dao.DoInTx(c, func(tx *sql.Tx) (err error) {
|
||
total, err = dao.CountAward(tx)
|
||
if err != nil {
|
||
return
|
||
}
|
||
if total == 0 {
|
||
return
|
||
}
|
||
awards, err = dao.ListAward(tx, from, limit)
|
||
if err != nil {
|
||
return
|
||
}
|
||
if len(awards) == 0 {
|
||
return
|
||
}
|
||
awardIDs := make([]int64, 0)
|
||
for _, v := range awards {
|
||
awardIDs = append(awardIDs, v.AwardID)
|
||
}
|
||
divisions, err = dao.ListAwardsDivision(tx, fmt.Sprintf("award_id IN (%s)", xstr.JoinInts(awardIDs)))
|
||
if err != nil {
|
||
return
|
||
}
|
||
awardToWinnerC, err = dao.GroupCountAwardWinner(tx, fmt.Sprintf("award_id IN (%s)", xstr.JoinInts(awardIDs)))
|
||
if err != nil {
|
||
return
|
||
}
|
||
return nil
|
||
})
|
||
if err != nil || total == 0 || len(awards) == 0 {
|
||
return
|
||
}
|
||
// divisions group by award_id
|
||
var (
|
||
tagIDToName map[int64]string
|
||
awardToDivisions = make(map[int64][]*model.AwardDivision)
|
||
)
|
||
tagIDToName, err = resource.VideoCategoryIDToName(c)
|
||
if err != nil {
|
||
return
|
||
}
|
||
for _, division := range divisions {
|
||
if _, ok := awardToDivisions[division.AwardID]; !ok {
|
||
awardToDivisions[division.AwardID] = make([]*model.AwardDivision, 0)
|
||
}
|
||
awardToDivisions[division.AwardID] = append(awardToDivisions[division.AwardID], division)
|
||
}
|
||
for _, award := range awards {
|
||
v := &model.AwardListModel{
|
||
ID: award.ID,
|
||
AwardID: award.AwardID,
|
||
AwardName: award.AwardName,
|
||
CycleStart: award.CycleStart.Unix(),
|
||
CycleEnd: award.CycleEnd.Unix(),
|
||
TotalQuota: award.TotalQuota,
|
||
TotalBonus: award.TotalBonus,
|
||
AnnounceDate: award.AnnounceDate.Unix(),
|
||
OpenStatus: award.OpenStatus,
|
||
OpenTime: award.OpenTime.Unix(),
|
||
CTime: award.CTime.Unix(),
|
||
CreatedBy: award.CreatedBy,
|
||
SelectionStatus: 1,
|
||
Tags: make([]string, 0),
|
||
DivisionNames: make([]string, 0),
|
||
}
|
||
// status
|
||
if count, ok := awardToWinnerC[v.AwardID]; ok && count == v.TotalQuota {
|
||
v.SelectionStatus = 2
|
||
}
|
||
tagIDs := make([]int64, 0)
|
||
for _, division := range awardToDivisions[v.AwardID] {
|
||
tagIDs = append(tagIDs, division.TagID)
|
||
v.DivisionNames = append(v.DivisionNames, division.DivisionName)
|
||
}
|
||
for _, tagID := range tagIDs {
|
||
v.Tags = append(v.Tags, tagIDToName[tagID])
|
||
}
|
||
data = append(data, v)
|
||
}
|
||
return
|
||
}
|
||
|
||
// DetailAward .
|
||
func (s *Service) DetailAward(c context.Context, awardID int64) (data *model.AwardDetail, err error) {
|
||
data = &model.AwardDetail{}
|
||
var (
|
||
winners = 0
|
||
typeIdxContent map[int]map[int]string
|
||
)
|
||
err = s.dao.DoInTx(c, func(tx *sql.Tx) (err error) {
|
||
data.Award, err = dao.Award(tx, awardID)
|
||
if err != nil {
|
||
return
|
||
}
|
||
if data.Award == nil {
|
||
err = ecode.Errorf(ecode.RequestErr, "award not found, awardID{%d}", awardID)
|
||
return
|
||
}
|
||
data.Divisions, err = dao.ListDivision(tx, awardID)
|
||
if err != nil {
|
||
return
|
||
}
|
||
data.Prizes, err = dao.ListPrize(tx, awardID)
|
||
if err != nil {
|
||
return
|
||
}
|
||
typeIdxContent, err = dao.ListResource(tx, awardID)
|
||
if err != nil {
|
||
return
|
||
}
|
||
total, err := dao.CountAwardWinner(tx, awardID, "")
|
||
if err != nil {
|
||
return
|
||
}
|
||
winners = int(total)
|
||
return nil
|
||
})
|
||
if err != nil {
|
||
return
|
||
}
|
||
|
||
data.Award.SelectionStatus = 1
|
||
if data.Award.TotalQuota == winners {
|
||
data.Award.SelectionStatus = 2
|
||
}
|
||
// tags
|
||
if len(data.Divisions) > 0 {
|
||
var tagNames map[int64]string
|
||
tagNames, err = resource.VideoCategoryIDToName(c)
|
||
if err != nil {
|
||
return
|
||
}
|
||
for _, division := range data.Divisions {
|
||
if tagName, ok := tagNames[division.TagID]; ok {
|
||
division.Tag = tagName
|
||
}
|
||
}
|
||
}
|
||
|
||
data.Resources = &model.AwardResource{
|
||
Rule: typeIdxContent[1][1],
|
||
Detail: typeIdxContent[3][1],
|
||
QA: make([]*model.AwardQA, 0),
|
||
}
|
||
questions, answers := typeIdxContent[5], typeIdxContent[6]
|
||
if len(questions) > 0 && len(answers) > 0 {
|
||
for idx, question := range questions {
|
||
data.Resources.QA = append(data.Resources.QA, &model.AwardQA{Index: idx, Q: question, A: answers[idx]})
|
||
}
|
||
}
|
||
sort.Slice(data.Resources.QA, func(i, j int) bool {
|
||
return data.Resources.QA[i].Index < data.Resources.QA[j].Index
|
||
})
|
||
|
||
data.Award.GenStr()
|
||
|
||
return
|
||
}
|
||
|
||
// ListAwardWinner .
|
||
func (s *Service) ListAwardWinner(c context.Context, arg *model.QueryAwardWinnerArg) (total int64, res []*model.AwardWinner, err error) {
|
||
res = make([]*model.AwardWinner, 0)
|
||
if pass := s.validateQueryAwardWinner(c, arg); !pass {
|
||
return
|
||
}
|
||
where := s.queryAwardWinnerWhere(arg)
|
||
var (
|
||
winnerInfo map[int64]*model.AwardWinner // <mid, *winner>
|
||
prizeInfo map[int64]*model.AwardPrize // <prize_id, *prize>
|
||
divisionInfo map[int64]*model.AwardDivision // <division_id, *division>
|
||
mids = make([]int64, 0)
|
||
)
|
||
err = s.dao.DoInTx(c, func(tx *sql.Tx) (err error) {
|
||
total, err = dao.CountAwardWinner(tx, arg.AwardID, where)
|
||
if err != nil {
|
||
return
|
||
}
|
||
if total == 0 {
|
||
return
|
||
}
|
||
winnerInfo, err = dao.QueryAwardWinner(tx, arg.AwardID, fmt.Sprintf("%s ORDER BY id DESC LIMIT %d,%d", where, arg.From, arg.Limit))
|
||
if err != nil {
|
||
return
|
||
}
|
||
if len(winnerInfo) > 0 {
|
||
// divisions
|
||
divisionInfo, err = dao.DivisionInfo(tx, arg.AwardID)
|
||
if err != nil {
|
||
return
|
||
}
|
||
// prizes
|
||
prizeInfo, err = dao.PrizeInfo(tx, arg.AwardID)
|
||
if err != nil {
|
||
return
|
||
}
|
||
}
|
||
return nil
|
||
})
|
||
if err != nil || total == 0 || len(winnerInfo) == 0 {
|
||
return
|
||
}
|
||
categories, err := resource.VideoCategoryIDToName(c)
|
||
if err != nil {
|
||
return
|
||
}
|
||
for mid := range winnerInfo {
|
||
mids = append(mids, mid)
|
||
}
|
||
upNames, err := resource.NamesByMIDs(c, mids)
|
||
if err != nil {
|
||
return
|
||
}
|
||
for mid, info := range winnerInfo {
|
||
if division, ok := divisionInfo[info.DivisionID]; ok {
|
||
info.DivisionName = division.DivisionName
|
||
}
|
||
if prize, ok := prizeInfo[info.PrizeID]; ok {
|
||
info.Bonus = prize.Bonus
|
||
}
|
||
// tag
|
||
if data, ok := categories[info.TagID]; ok {
|
||
info.Tag = data
|
||
}
|
||
// nickname
|
||
if nickname, ok := upNames[mid]; ok {
|
||
info.Nickname = nickname
|
||
}
|
||
}
|
||
res = make([]*model.AwardWinner, 0)
|
||
for _, v := range winnerInfo {
|
||
res = append(res, v)
|
||
}
|
||
return
|
||
}
|
||
|
||
// ExportAwardWinner .
|
||
func (s *Service) ExportAwardWinner(c context.Context, arg *model.QueryAwardWinnerArg) (res []byte, err error) {
|
||
records, err := s.listAwardWinnerAll(c, arg)
|
||
if err != nil {
|
||
return
|
||
}
|
||
data := make([][]string, 0, len(records)+1)
|
||
data = append(data, model.AwardWinnerExportFields())
|
||
for _, v := range records {
|
||
data = append(data, v.ExportStrings())
|
||
}
|
||
if res, err = FormatCSV(data); err != nil {
|
||
log.Error("FormatCSV error(%v)", err)
|
||
}
|
||
return
|
||
}
|
||
|
||
func (s *Service) listAwardWinnerAll(c context.Context, arg *model.QueryAwardWinnerArg) (records []*model.AwardWinner, err error) {
|
||
records = make([]*model.AwardWinner, 0)
|
||
if pass := s.validateQueryAwardWinner(c, arg); !pass {
|
||
return
|
||
}
|
||
where := s.queryAwardWinnerWhere(arg)
|
||
var (
|
||
winnerInfo map[int64]*model.AwardWinner // <mid, *winner>
|
||
prizeInfo map[int64]*model.AwardPrize // <prize_id, *prize>
|
||
divisionInfo map[int64]*model.AwardDivision // <division_id, *division>
|
||
mids = make([]int64, 0)
|
||
)
|
||
err = s.dao.DoInTx(c, func(tx *sql.Tx) (err error) {
|
||
winnerInfo, err = dao.QueryAwardWinner(tx, arg.AwardID, fmt.Sprintf("%s ORDER BY id DESC", where))
|
||
if err != nil {
|
||
return
|
||
}
|
||
if len(winnerInfo) > 0 {
|
||
// divisions
|
||
divisionInfo, err = dao.DivisionInfo(tx, arg.AwardID)
|
||
if err != nil {
|
||
return
|
||
}
|
||
// prizes
|
||
prizeInfo, err = dao.PrizeInfo(tx, arg.AwardID)
|
||
if err != nil {
|
||
return
|
||
}
|
||
}
|
||
return nil
|
||
})
|
||
if err != nil {
|
||
return
|
||
}
|
||
if len(winnerInfo) == 0 {
|
||
return
|
||
}
|
||
categories, err := resource.VideoCategoryIDToName(c)
|
||
if err != nil {
|
||
return
|
||
}
|
||
for mid := range winnerInfo {
|
||
mids = append(mids, mid)
|
||
}
|
||
upNames, err := resource.NamesByMIDs(c, mids)
|
||
if err != nil {
|
||
return
|
||
}
|
||
for mid, info := range winnerInfo {
|
||
if division, ok := divisionInfo[info.DivisionID]; ok {
|
||
info.DivisionName = division.DivisionName
|
||
}
|
||
if prize, ok := prizeInfo[info.PrizeID]; ok {
|
||
info.Bonus = prize.Bonus
|
||
}
|
||
// tag
|
||
if data, ok := categories[info.TagID]; ok {
|
||
info.Tag = data
|
||
}
|
||
// nickname
|
||
if nickname, ok := upNames[mid]; ok {
|
||
info.Nickname = nickname
|
||
}
|
||
}
|
||
for _, v := range winnerInfo {
|
||
records = append(records, v)
|
||
}
|
||
return
|
||
}
|
||
|
||
func (s *Service) validateQueryAwardWinner(c context.Context, arg *model.QueryAwardWinnerArg) (pass bool) {
|
||
if arg.Nickname != "" {
|
||
mid, err := resource.MidByNickname(c, arg.Nickname)
|
||
if err != nil || mid == 0 {
|
||
return
|
||
}
|
||
if arg.MID == 0 {
|
||
arg.MID = mid
|
||
}
|
||
if arg.MID != mid {
|
||
log.Error("illegal mid(%d) and nickname(%s) pair", arg.MID, arg.Nickname)
|
||
return
|
||
}
|
||
}
|
||
return true
|
||
}
|
||
|
||
func (s *Service) queryAwardWinnerWhere(arg *model.QueryAwardWinnerArg) string {
|
||
str := ""
|
||
if arg.MID > 0 {
|
||
str += fmt.Sprintf(" AND mid=%d", arg.MID)
|
||
}
|
||
if arg.TagID > 0 {
|
||
str += fmt.Sprintf(" AND tag_id=%d", arg.TagID)
|
||
}
|
||
return str
|
||
}
|
||
|
||
// ReplaceAwardWinner .
|
||
func (s *Service) ReplaceAwardWinner(c context.Context, awardID, prevMID, mid int64) (err error) {
|
||
records, err := s.dao.ListAwardRecord(c, awardID, fmt.Sprintf("AND (mid=%d OR mid=%d)", prevMID, mid))
|
||
if err != nil {
|
||
return
|
||
}
|
||
midsM := make(map[int64]bool)
|
||
for _, v := range records {
|
||
midsM[v.MID] = true
|
||
}
|
||
if !midsM[prevMID] {
|
||
return ecode.Errorf(ecode.RequestErr, "mid(%d) not in award(%d)", prevMID, awardID)
|
||
}
|
||
if !midsM[mid] {
|
||
return ecode.Errorf(ecode.RequestErr, "mid(%d) not in award(%d)", mid, awardID)
|
||
}
|
||
|
||
err = s.dao.DoInTx(c, func(tx *sql.Tx) (err error) {
|
||
// lock award
|
||
award, err := dao.SelectAwardForUpdate(tx, awardID)
|
||
if err != nil {
|
||
return
|
||
}
|
||
if award == nil {
|
||
return ecode.Error(ecode.RequestErr, "award not found")
|
||
}
|
||
if award.OpenStatus != 1 {
|
||
return ecode.Error(ecode.RequestErr, "illegal operation")
|
||
}
|
||
// winner info
|
||
res, err := dao.QueryAwardWinner(tx, awardID, fmt.Sprintf("AND (mid=%d OR mid=%d)", prevMID, mid))
|
||
if err != nil {
|
||
return
|
||
}
|
||
prevWinner, ok := res[prevMID]
|
||
if !ok {
|
||
return ecode.Error(ecode.RequestErr, "invalid old winner")
|
||
}
|
||
if _, ok = res[mid]; ok {
|
||
return ecode.Error(ecode.RequestErr, "invalid new winner")
|
||
}
|
||
// replace
|
||
rows, err := dao.DelWinner(tx, awardID, fmt.Sprintf(" AND mid=%d", prevMID))
|
||
if err != nil {
|
||
return
|
||
}
|
||
if rows != 1 {
|
||
return ecode.Error(ecode.ServerErr, "failed to del old winner")
|
||
}
|
||
fields, values := saveWinnerStr(map[int64]*model.AwardWinner{
|
||
awardID: {
|
||
AwardID: awardID,
|
||
MID: mid,
|
||
DivisionID: prevWinner.DivisionID,
|
||
PrizeID: prevWinner.PrizeID,
|
||
TagID: prevWinner.TagID,
|
||
},
|
||
})
|
||
rows, err = dao.SaveWinners(tx, fields, values)
|
||
if err != nil {
|
||
return
|
||
}
|
||
if rows != 1 {
|
||
return ecode.Error(ecode.ServerErr, "failed to add new winner")
|
||
}
|
||
return
|
||
})
|
||
return
|
||
}
|
||
|
||
// SaveAwardResult .
|
||
func (s *Service) SaveAwardResult(c context.Context, arg *model.AwardResult) (err error) {
|
||
var (
|
||
awardID = arg.AwardID
|
||
winnersM = make(map[int64]*model.AwardWinner)
|
||
prizeWinnerC = make(map[int64]int)
|
||
divisionInfo map[int64]*model.AwardDivision
|
||
)
|
||
divisionInfo, err = s.dao.AwardDivisionInfo(c, awardID)
|
||
if err != nil {
|
||
return
|
||
}
|
||
for divisionI, division := range arg.Divisions {
|
||
divisionID := int64(divisionI + 1)
|
||
for prizeI, prize := range division.Prizes {
|
||
prizeID := int64(prizeI + 1)
|
||
prizeWinnerC[prizeID] += len(prize.MIDs)
|
||
for _, mid := range prize.MIDs {
|
||
if _, ok := winnersM[mid]; ok {
|
||
return ecode.Error(ecode.RequestErr, "UID重复,请重新录入")
|
||
}
|
||
winnersM[mid] = &model.AwardWinner{
|
||
MID: mid,
|
||
AwardID: awardID,
|
||
DivisionID: divisionID,
|
||
PrizeID: prizeID,
|
||
TagID: divisionInfo[divisionID].TagID,
|
||
}
|
||
}
|
||
}
|
||
}
|
||
mids := make([]int64, 0, len(winnersM))
|
||
for k := range winnersM {
|
||
mids = append(mids, k)
|
||
}
|
||
if len(winnersM) == 0 {
|
||
return ecode.Error(ecode.RequestErr, "奖项人数不符,保存失败")
|
||
}
|
||
// 是否已报名专项奖
|
||
{
|
||
var winnerRecords []*model.AwardRecord
|
||
winnerRecords, err = s.dao.ListAwardRecord(c, awardID, fmt.Sprintf("AND mid IN (%s)", xstr.JoinInts(mids)))
|
||
if err != nil {
|
||
return
|
||
}
|
||
if len(winnersM) != len(winnerRecords) {
|
||
var (
|
||
awardRecordM = make(map[int64]bool)
|
||
unSignedMIDs []int64
|
||
)
|
||
for _, rcr := range winnerRecords {
|
||
awardRecordM[rcr.MID] = true
|
||
}
|
||
for _, mid := range mids {
|
||
if !awardRecordM[mid] {
|
||
unSignedMIDs = append(unSignedMIDs, mid)
|
||
}
|
||
}
|
||
return ecode.Errorf(ecode.RequestErr, "%s 未报名专项奖,保存失败", xstr.JoinInts(unSignedMIDs))
|
||
}
|
||
}
|
||
// 是否已签约激励
|
||
{
|
||
var signedUPs map[int64]struct{}
|
||
signedUPs, err = s.dao.GetUpInfoByState(c, "up_info_video", mids, 3)
|
||
if err != nil {
|
||
return
|
||
}
|
||
if len(mids) != len(signedUPs) {
|
||
var unSignedMIDs []int64
|
||
for _, mid := range mids {
|
||
if _, ok := signedUPs[mid]; !ok {
|
||
unSignedMIDs = append(unSignedMIDs, mid)
|
||
}
|
||
}
|
||
return ecode.Errorf(ecode.RequestErr, "%s 非签约状态,保存失败", xstr.JoinInts(unSignedMIDs))
|
||
}
|
||
}
|
||
// save winners
|
||
updateAwardStr := fmt.Sprintf("open_time = '%s'", time.Unix(arg.OpenTime, 0).Format("2006-01-02 15:04:05"))
|
||
fields, values := saveWinnerStr(winnersM)
|
||
err = s.dao.DoInTx(c, func(tx *sql.Tx) (err error) {
|
||
award, err := dao.SelectAwardForUpdate(tx, awardID)
|
||
if err != nil {
|
||
return
|
||
}
|
||
if award == nil {
|
||
return ecode.Errorf(ecode.RequestErr, "award not found, awardID{%d}", awardID)
|
||
}
|
||
if award.OpenStatus != 1 {
|
||
return ecode.Error(ecode.RequestErr, "已发奖,不能再修改名单")
|
||
}
|
||
if award.TotalQuota != len(mids) {
|
||
return ecode.Error(ecode.RequestErr, "奖项人数不符,保存失败")
|
||
}
|
||
prizeInfo, err := dao.PrizeInfo(tx, awardID)
|
||
if err != nil {
|
||
return
|
||
}
|
||
for prizeID, info := range prizeInfo {
|
||
if prizeWinnerC[prizeID] != info.Quota {
|
||
return ecode.Error(ecode.RequestErr, "奖项人数不符,保存失败")
|
||
}
|
||
}
|
||
_, err = dao.UpdateAward(tx, awardID, updateAwardStr)
|
||
if err != nil {
|
||
return
|
||
}
|
||
_, err = dao.DelWinnerAll(tx, awardID)
|
||
if err != nil {
|
||
return
|
||
}
|
||
_, err = dao.SaveWinners(tx, fields, values)
|
||
if err != nil {
|
||
return
|
||
}
|
||
return nil
|
||
})
|
||
return
|
||
}
|
||
|
||
// AwardResult .
|
||
func (s *Service) AwardResult(c context.Context, awardID int64) (res *model.AwardResult, err error) {
|
||
var (
|
||
award *model.Award
|
||
winners []*model.AwardWinner
|
||
prizeInfo map[int64]*model.AwardPrize
|
||
divisionInfo map[int64]*model.AwardDivision
|
||
data = make(map[int64]map[int64][]int64)
|
||
)
|
||
err = s.dao.DoInTx(c, func(tx *sql.Tx) (err error) {
|
||
award, err = dao.Award(tx, awardID)
|
||
if err != nil {
|
||
return
|
||
}
|
||
if award == nil {
|
||
err = ecode.Errorf(ecode.RequestErr, "award not found, awardID{%d}", awardID)
|
||
return
|
||
}
|
||
winners, err = dao.AwardWinnerAll(tx, awardID)
|
||
if err != nil {
|
||
return
|
||
}
|
||
// divisions
|
||
divisionInfo, err = dao.DivisionInfo(tx, awardID)
|
||
if err != nil {
|
||
return
|
||
}
|
||
// prizes
|
||
prizeInfo, err = dao.PrizeInfo(tx, awardID)
|
||
if err != nil {
|
||
return
|
||
}
|
||
return nil
|
||
})
|
||
if err != nil {
|
||
return
|
||
}
|
||
// init
|
||
for divisionID := range divisionInfo {
|
||
data[divisionID] = make(map[int64][]int64)
|
||
for prizeID := range prizeInfo {
|
||
data[divisionID][prizeID] = make([]int64, 0)
|
||
}
|
||
}
|
||
// mids
|
||
for _, v := range winners {
|
||
data[v.DivisionID][v.PrizeID] = append(data[v.DivisionID][v.PrizeID], v.MID)
|
||
}
|
||
// res
|
||
res = &model.AwardResult{
|
||
AwardID: awardID,
|
||
OpenTime: award.OpenTime.Unix(),
|
||
AnnounceDate: award.AnnounceDate.Unix(),
|
||
CycleEnd: award.CycleEnd.Unix(),
|
||
Divisions: make([]*model.AwardDivisionResult, 0),
|
||
}
|
||
for divisionID, division := range divisionInfo {
|
||
dv := &model.AwardDivisionResult{
|
||
DivisionID: divisionID,
|
||
DivisionName: division.DivisionName,
|
||
Prizes: make([]*model.AwardPrizeResult, 0),
|
||
}
|
||
for prizeID := range prizeInfo {
|
||
mids := data[divisionID][prizeID]
|
||
dv.Prizes = append(dv.Prizes, &model.AwardPrizeResult{MIDs: mids, PrizeID: prizeID})
|
||
}
|
||
sort.Slice(dv.Prizes, func(i, j int) bool {
|
||
return dv.Prizes[i].PrizeID < dv.Prizes[j].PrizeID
|
||
})
|
||
res.Divisions = append(res.Divisions, dv)
|
||
}
|
||
sort.Slice(res.Divisions, func(i, j int) bool {
|
||
return res.Divisions[i].DivisionID < res.Divisions[j].DivisionID
|
||
})
|
||
|
||
return
|
||
}
|