354 lines
9.6 KiB
Go
354 lines
9.6 KiB
Go
package dao
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"go-common/app/service/openplatform/ticket-item/model"
|
|
"go-common/library/cache/redis"
|
|
"go-common/library/ecode"
|
|
"go-common/library/log"
|
|
"time"
|
|
|
|
"github.com/jinzhu/gorm"
|
|
)
|
|
|
|
const (
|
|
// TypeWithoutSeat 场次类型 站票
|
|
TypeWithoutSeat = 2
|
|
// TicketTypeElec 场次票类型 电子票
|
|
TicketTypeElec = 2
|
|
// scTypeNormal 普通场次
|
|
scTypeNormal = 1
|
|
// scTypePass 通票场次
|
|
scTypePass = 2
|
|
// scTypeAllPass 联票场次
|
|
scTypeAllPass = 3
|
|
)
|
|
|
|
var scTypeName = map[int32]string{
|
|
scTypeNormal: "普通场次",
|
|
scTypePass: "通票",
|
|
scTypeAllPass: "联票",
|
|
}
|
|
|
|
// RawScListByItem 批量取项目场次
|
|
func (d *Dao) RawScListByItem(c context.Context, ids []int64) (info map[int64][]*model.Screen, err error) {
|
|
info = make(map[int64][]*model.Screen)
|
|
rows, err := d.db.Model(&model.Screen{}).Where("project_id in (?)", ids).Rows()
|
|
if err != nil {
|
|
log.Error("RawScListByItem(%v) error(%v)", ids, err)
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
for rows.Next() {
|
|
var sc model.Screen
|
|
err = d.db.ScanRows(rows, &sc)
|
|
if err != nil {
|
|
log.Error("RawScListByItem(%v) scan error(%v)", ids, err)
|
|
return
|
|
}
|
|
info[sc.ProjectID] = append(info[sc.ProjectID], &sc)
|
|
}
|
|
return
|
|
}
|
|
|
|
// CacheScListByItem 缓存取项目票价
|
|
func (d *Dao) CacheScListByItem(c context.Context, ids []int64) (info map[int64][]*model.Screen, err error) {
|
|
var keys []interface{}
|
|
keyPidMap := make(map[string]int64, len(ids))
|
|
for _, id := range ids {
|
|
key := keyItemScreen(id)
|
|
if _, ok := keyPidMap[key]; !ok {
|
|
// duplicate mid
|
|
keyPidMap[key] = id
|
|
keys = append(keys, key)
|
|
}
|
|
}
|
|
conn := d.redis.Get(c)
|
|
defer conn.Close()
|
|
var data [][]byte
|
|
log.Info("MGET %v", model.JSONEncode(keys))
|
|
if data, err = redis.ByteSlices(conn.Do("mget", keys...)); err != nil {
|
|
log.Error("ItemScList MGET %v ERR: %v", model.JSONEncode(keys), err)
|
|
return
|
|
}
|
|
info = make(map[int64][]*model.Screen)
|
|
for _, d := range data {
|
|
if d != nil {
|
|
var scs []*model.Screen
|
|
json.Unmarshal(d, &scs)
|
|
info[scs[0].ProjectID] = scs
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// AddCacheScListByItem 为项目场次添加缓存
|
|
func (d *Dao) AddCacheScListByItem(c context.Context, info map[int64][]*model.Screen) (err error) {
|
|
conn := d.redis.Get(c)
|
|
defer func() {
|
|
conn.Flush()
|
|
conn.Close()
|
|
}()
|
|
var data []interface{}
|
|
var keys []string
|
|
for k, v := range info {
|
|
b, _ := json.Marshal(v)
|
|
key := keyItemScreen(k)
|
|
keys = append(keys, key)
|
|
data = append(data, key, b)
|
|
}
|
|
log.Info("MSET %v", keys)
|
|
if err = conn.Send("MSET", data...); err != nil {
|
|
return
|
|
}
|
|
for i := 0; i < len(data); i += 2 {
|
|
conn.Send("EXPIRE", data[i], CacheTimeout)
|
|
}
|
|
return
|
|
|
|
}
|
|
|
|
// RawScList 批量取场次
|
|
func (d *Dao) RawScList(c context.Context, ids []int64) (info map[int64]*model.Screen, err error) {
|
|
info = make(map[int64]*model.Screen)
|
|
rows, err := d.db.Model(&model.Screen{}).Where("id in (?)", ids).Rows()
|
|
if err != nil {
|
|
log.Error("RawScList(%v) error(%v)", ids, err)
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
for rows.Next() {
|
|
var sc model.Screen
|
|
err = d.db.ScanRows(rows, &sc)
|
|
if err != nil {
|
|
log.Error("RawScList(%v) scan error(%v)", ids, err)
|
|
return
|
|
}
|
|
info[sc.ID] = &sc
|
|
}
|
|
return
|
|
}
|
|
|
|
// CacheScList 缓存批量取场次
|
|
func (d *Dao) CacheScList(c context.Context, ids []int64) (info map[int64]*model.Screen, err error) {
|
|
var keys []interface{}
|
|
keyPidMap := make(map[string]int64, len(ids))
|
|
for _, id := range ids {
|
|
key := keyScreen(id)
|
|
if _, ok := keyPidMap[key]; !ok {
|
|
// duplicate id
|
|
keyPidMap[key] = id
|
|
keys = append(keys, key)
|
|
}
|
|
}
|
|
conn := d.redis.Get(c)
|
|
defer conn.Close()
|
|
var data [][]byte
|
|
log.Info("MGET %v", model.JSONEncode(keys))
|
|
if data, err = redis.ByteSlices(conn.Do("mget", keys...)); err != nil {
|
|
log.Error("ScList MGET %v ERR: %v", model.JSONEncode(keys), err)
|
|
return
|
|
}
|
|
info = make(map[int64]*model.Screen)
|
|
for _, d := range data {
|
|
if d != nil {
|
|
var sc model.Screen
|
|
json.Unmarshal(d, &sc)
|
|
info[sc.ID] = &sc
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// AddCacheScList 为场次添加缓存
|
|
func (d *Dao) AddCacheScList(c context.Context, info map[int64]*model.Screen) (err error) {
|
|
conn := d.redis.Get(c)
|
|
defer func() {
|
|
conn.Flush()
|
|
conn.Close()
|
|
}()
|
|
var data []interface{}
|
|
var keys []string
|
|
for k, v := range info {
|
|
b, _ := json.Marshal(v)
|
|
key := keyScreen(k)
|
|
keys = append(keys, key)
|
|
data = append(data, key, b)
|
|
}
|
|
log.Info("MSET %v", keys)
|
|
if err = conn.Send("MSET", data...); err != nil {
|
|
return
|
|
}
|
|
for i := 0; i < len(data); i += 2 {
|
|
conn.Send("EXPIRE", data[i], CacheTimeout)
|
|
}
|
|
return
|
|
|
|
}
|
|
|
|
// CreateOrUpdateScreen 创建或更新场次
|
|
func (d *Dao) CreateOrUpdateScreen(c context.Context, tx *gorm.DB, screenInfo model.Screen) (model.Screen, error) {
|
|
|
|
if screenInfo.ID == 0 {
|
|
// create
|
|
if err := tx.Create(&screenInfo).Error; err != nil {
|
|
log.Error("创建场次失败:%s", err)
|
|
tx.Rollback()
|
|
return model.Screen{}, err
|
|
}
|
|
} else {
|
|
// update
|
|
if err := tx.Model(&model.Screen{}).Where("id = ?", screenInfo.ID).Updates(map[string]interface{}{
|
|
"name": screenInfo.Name,
|
|
"status": screenInfo.Status,
|
|
"type": screenInfo.Type,
|
|
"ticket_type": screenInfo.TicketType,
|
|
"screen_type": screenInfo.ScreenType,
|
|
"delivery_type": screenInfo.DeliveryType,
|
|
"pick_seat": screenInfo.PickSeat,
|
|
"start_time": screenInfo.StartTime,
|
|
"end_time": screenInfo.EndTime,
|
|
"project_id": screenInfo.ProjectID,
|
|
}).Error; err != nil {
|
|
log.Error("更新场次失败:%s", err)
|
|
tx.Rollback()
|
|
return model.Screen{}, err
|
|
}
|
|
}
|
|
|
|
return screenInfo, nil
|
|
|
|
}
|
|
|
|
// GetOrUpdatePassSc 更新或新建通联票场次 返回id
|
|
func (d *Dao) GetOrUpdatePassSc(c context.Context, tx *gorm.DB, pid int64, tksPass []TicketPass, scStartTimes map[int32]int32,
|
|
scEndTimes map[int32]int32, scType int32, opType int32) (int64, error) {
|
|
var passScID int64
|
|
|
|
scTime, err := d.CalPassScTime(scStartTimes, scEndTimes, tksPass)
|
|
if err != nil {
|
|
tx.Rollback()
|
|
return 0, err
|
|
}
|
|
|
|
var screen model.Screen
|
|
if d.db.Where("project_id = ? and screen_type = ? and deleted_at = 0", pid, scType).First(&screen).RecordNotFound() {
|
|
// 不存在场次并且有票种存在则新建一个
|
|
if len(tksPass) > 0 {
|
|
passScreen := model.Screen{
|
|
ProjectID: pid,
|
|
Name: scTypeName[scType],
|
|
StartTime: scTime[0],
|
|
EndTime: scTime[1],
|
|
Type: TypeWithoutSeat, // 站票
|
|
TicketType: TicketTypeElec, // 电子票
|
|
DeliveryType: 1, // 默认不配送
|
|
ScreenType: scType,
|
|
Status: opType,
|
|
}
|
|
if err := tx.Create(&passScreen).Error; err != nil {
|
|
log.Error("创建通票或联票场次失败:%s", err)
|
|
tx.Rollback()
|
|
return 0, err
|
|
}
|
|
passScID = passScreen.ID
|
|
}
|
|
|
|
} else {
|
|
if len(tksPass) > 0 {
|
|
// update
|
|
if err := tx.Model(&model.Screen{}).Where("id = ?", screen.ID).Updates(map[string]interface{}{
|
|
"status": opType,
|
|
"start_time": scTime[0],
|
|
"end_time": scTime[1],
|
|
}).Error; err != nil {
|
|
log.Error("更新通票联票场次失败:%s", err)
|
|
tx.Rollback()
|
|
return 0, err
|
|
}
|
|
passScID = screen.ID
|
|
} else {
|
|
// 如果没有通票/联票 删除通票/联票场次
|
|
if err := d.DelScreen(c, tx, []int64{screen.ID}, nil, pid); err != nil {
|
|
return 0, err
|
|
}
|
|
passScID = screen.ID
|
|
}
|
|
}
|
|
|
|
return passScID, nil
|
|
}
|
|
|
|
// DelScreen 删除场次
|
|
func (d *Dao) DelScreen(c context.Context, tx *gorm.DB, oldIDs []int64, newIDs []int64, pid int64) error {
|
|
delIDs, _ := model.ClassifyIDs(oldIDs, newIDs)
|
|
for _, delID := range delIDs {
|
|
if !d.CanDelScreen(delID) {
|
|
tx.Rollback()
|
|
return ecode.TicketCannotDelSc
|
|
}
|
|
// 删除场次前把改场次下的票价一起删除
|
|
priceIDs, err := d.GetPriceIDs(delID, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := d.DelTicket(c, tx, priceIDs, nil, pid, true); err != nil {
|
|
return err
|
|
}
|
|
// 删除场次
|
|
if err := tx.Exec("UPDATE screen SET deleted_at=? WHERE id = ? AND project_id = ?", time.Now().Format("2006-01-02 15:04:05"), delID, pid).Error; err != nil {
|
|
log.Error("删除场次失败:%s", err)
|
|
tx.Rollback()
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CalPassScTime 获取通联场次的开始结束时间, 取关联场次时间的极值。返回[0]=starttime [1]=endtime
|
|
func (d *Dao) CalPassScTime(scStartTimes map[int32]int32, scEndTimes map[int32]int32, tksPass []TicketPass) ([]int32, error) {
|
|
var startTimes, endTimes, currStartTimes, currEndTimes []int32
|
|
for _, v := range tksPass {
|
|
currStartTimes = []int32{}
|
|
currEndTimes = []int32{}
|
|
for _, v2 := range v.LinkScreens {
|
|
if _, ok := scStartTimes[v2]; !ok {
|
|
log.Error("关联的场次开始时间不存在")
|
|
return []int32{}, ecode.TicketLkScTimeNotFound
|
|
}
|
|
if _, ok := scEndTimes[v2]; !ok {
|
|
log.Error("关联的场次结束时间不存在")
|
|
return []int32{}, ecode.TicketLkScTimeNotFound
|
|
}
|
|
currStartTimes = append(currStartTimes, scStartTimes[v2])
|
|
currEndTimes = append(currEndTimes, scEndTimes[v2])
|
|
}
|
|
startTimes = append(startTimes, model.Min(currStartTimes))
|
|
endTimes = append(endTimes, model.Max(currEndTimes))
|
|
}
|
|
return []int32{model.Min(startTimes), model.Max(endTimes)}, nil
|
|
|
|
}
|
|
|
|
// CanDelScreen 检查是否可以删除场次
|
|
func (d *Dao) CanDelScreen(id int64) bool {
|
|
var priceIDs []int64
|
|
|
|
// 获取场次下 所有票价id
|
|
var prices []model.TicketPrice
|
|
if err := d.db.Select("id").Where("screen_id = ? and deleted_at = 0", id).Find(&prices).Error; err != nil {
|
|
log.Error("获取场次下所有票价id失败:%s", err)
|
|
return false
|
|
}
|
|
for _, v := range prices {
|
|
priceIDs = append(priceIDs, v.ID)
|
|
}
|
|
|
|
if d.HasPromotion(priceIDs, 2) || d.StockChanged(priceIDs) {
|
|
log.Error("场次的票价下存在拼团或者库存有变动")
|
|
return false
|
|
}
|
|
return true
|
|
}
|