You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1037 lines
27 KiB
1037 lines
27 KiB
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 |
|
}
|
|
|