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.
353 lines
9.6 KiB
353 lines
9.6 KiB
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 |
|
}
|
|
|