package purchase import ( "fmt" "math/rand" "sync" "time" "bet24.com/log" "bet24.com/servers/common" "bet24.com/servers/coreservice/serviceconfig" item "bet24.com/servers/micros/item_inventory/proto" cash "bet24.com/servers/micros/money/proto" mail "bet24.com/servers/micros/userservices/proto" ) type purchaseMgr struct { sn int // 序号 numbers [][]int // 抽奖码 index int // 索引 user_list map[int]*userPurchase // 用户列表 beginTime time.Time // 开始时间 endTime time.Time // 结束时间 luckNumber int // 中奖码 status int // 状态(0=空闲 1=活动中 3=派奖 2=结束) lock *sync.RWMutex } type userPurchase struct { UserId int // 用户id Numbers []int // 号码 } func newPurchaseMgr() *purchaseMgr { mgr := new(purchaseMgr) mgr.user_list = make(map[int]*userPurchase) mgr.lock = &sync.RWMutex{} mgr.load() go mgr.refresh() log.Debug("purchase manager running") return mgr } func (this *purchaseMgr) load() { // log.Debug("purchase.load 开始加载数据") // 从数据库拉取未结束的活动数据 sn, beginTime, endTime, luckNumber, status := sysInfo() if sn <= 0 { log.Debug("purchase.load not data") return } // 获取用户数据 userList := userInfo(sn) index := len(userList) numbers := this.initNumbers(userList) this.lock.Lock() defer this.lock.Unlock() this.sn = sn this.beginTime = beginTime this.endTime = endTime this.luckNumber = luckNumber this.status = status this.user_list = userList this.index = index this.numbers = numbers //log.Debug("purchase.load 加载数据 data ok ==> sn=%d beginTime=%s endTime=%s luckNumber=%d status=%d index=%d numbers=%v", // this.sn, this.beginTime, this.end, this.luckNumber, this.status, this.index, this.numbers) } func (this *purchaseMgr) refresh() { ticker := time.NewTicker(2 * time.Second) go func(t *time.Ticker) { for { select { case <-t.C: this.check() } } }(ticker) } func (this *purchaseMgr) check() { // 派奖中(什么都不处理) if this.status == STATUS_PRIZE { return } // 活动中 if this.status == STATUS_ACTIVITY { // 当期还没结束 if this.getNow().After(this.beginTime) && this.getNow().Before(this.endTime) { return } // 结束 this.end() return } // 是否有效期 if !this.isValid() { return } // 开启活动 this.open() return } func (this *purchaseMgr) getNow() time.Time { // 转换成相同的格式 now, _ := time.Parse(common.Layout, time.Now().Format(common.Layout)) return now } // 是否有效 func (this *purchaseMgr) isValid() bool { // 开始时间 start, err := time.Parse(common.Layout, fmt.Sprintf("%s 00:00:00", time.Now().Format("2006-01-02"))) if err != nil { log.Error("purchaseMgr startTime fail %v", err) return false } // log.Debug("isValid start = %v", start) if serviceconfig.Server.PurchaseCfg.BeginHour == "" { return false } beginHour, err := time.ParseDuration(serviceconfig.Server.PurchaseCfg.BeginHour) if err != nil { log.Error("purchaseMgr beginHour fail %v", err) return false } // log.Debug("isValid beginHour = %v", beginHour) start = start.Add(beginHour) // log.Debug("isValid ==> start = %v", start) // 结束时间 end, err := time.Parse(common.Layout, fmt.Sprintf("%s 00:00:00", time.Now().Format("2006-01-02"))) if err != nil { log.Error("purchaseMgr endTime fail %v", err) return false } // log.Debug("isValid end = %v", end) if serviceconfig.Server.PurchaseCfg.EndHour == "" { return false } endHour, err := time.ParseDuration(serviceconfig.Server.PurchaseCfg.EndHour) if err != nil { log.Error("purchaseMgr endHour fail %v", err) return false } // log.Debug("isValid endHour = %v", endHour) end = end.Add(endHour) // log.Debug("isValid ==> end = %v", end) // 不在活动时间段内 if this.getNow().Before(start) || this.getNow().After(end) { //log.Debug("purchaseMgr.isValid 不在活动时间段内 start=%v end=%v this.getNow()=%v ==> %+v", // start, end, this.getNow(), this) return false } // log.Debug("purchaseMgr.isValid 活动有效期") return true } // 开启 func (this *purchaseMgr) open() { duration, err := time.ParseDuration(serviceconfig.Server.PurchaseCfg.Duration) if err != nil { log.Error("open duration fail %v", err) return } this.lock.Lock() this.sn++ this.numbers = this.initNumbers(nil) this.index = 0 this.user_list = make(map[int]*userPurchase, 0) this.beginTime = this.getNow() this.endTime = this.getNow().Add(duration) this.luckNumber = 0 this.status = STATUS_ACTIVITY this.lock.Unlock() // 写数据(开启,活动中状态) go open(this.sn, this.beginTime, this.endTime, this.luckNumber, this.status) //log.Debug("purchasemgr.open 已开启 sn=%d beginTime=%s endTime=%s status=%d numbers=%v index=%d", // this.sn, this.beginTime, this.endTime, this.status, this.numbers, this.index) return } // 初始化 func (this *purchaseMgr) initNumbers(list map[int]*userPurchase) [][]int { var numbers [][]int for i := 1; i <= serviceconfig.Server.PurchaseCfg.MaxNumber; i++ { flag := false // 已经存在的抽奖码排除 for _, j := range list { for _, v := range j.Numbers { // 该抽奖码已存在 if v == i { flag = true break } } if flag { break } } if flag { continue } var items []int items = append(items, i, 0) numbers = append(numbers, items) } count := len(numbers) // 打乱顺序 for i := count - 1; i > 0; i-- { j := rand.Intn(i) numbers[i], numbers[j] = numbers[j], numbers[i] } // 加在头部 for _, v := range list { for _, k := range v.Numbers { var items []int items = append(items, k, v.UserId) numbers = append([][]int{items}, numbers...) } } return numbers } // 结束 func (this *purchaseMgr) end() { if serviceconfig.Server.PurchaseCfg.MaxNumber <= 0 { return } // 生成随机号码 index := rand.Intn(serviceconfig.Server.PurchaseCfg.MaxNumber) this.lock.Lock() this.status = STATUS_PRIZE this.luckNumber = this.numbers[index][0] userId := this.numbers[index][1] sn := this.sn luckNumber := this.luckNumber this.lock.Unlock() // 写数据(派奖状态) update(this.sn, this.luckNumber, this.status) // 邮件派奖 this.sendPrize(userId, sn, luckNumber) this.lock.Lock() this.status = STATUS_END this.lock.Unlock() // 写数据(结束状态) go update(this.sn, this.luckNumber, this.status) log.Debug("purchasemgr.end sn=%d status=%d luckNumber=%d userId=%d", this.sn, this.status, this.luckNumber, userId) return } // 邮件派奖 func (this *purchaseMgr) sendPrize(userId, sn, luckNumber int) { for _, v := range this.user_list { if v.UserId == userId && v.UserId > 0 { var items []item.ItemPack items = append(items, item.ItemPack{ ItemId: serviceconfig.Server.PurchaseCfg.ItemId, Count: serviceconfig.Server.PurchaseCfg.AwardAmount, }) // 发送中奖通知 mail.SendSysMail(v.UserId, &mail.SysMail{ Id: 0, Title: serviceconfig.Server.PurchaseCfg.SuccessTitle, Content: fmt.Sprintf(serviceconfig.Server.PurchaseCfg.SuccessContent, sn, serviceconfig.Server.PurchaseCfg.AwardAmount), Status: 0, SourceName: "100K购", Crdate: common.GetTimeStamp(), Tools: items, }) continue } // 发送未中奖通知 mail.SendSysMail(v.UserId, &mail.SysMail{ Id: 0, Title: serviceconfig.Server.PurchaseCfg.FailTitle, Content: fmt.Sprintf(serviceconfig.Server.PurchaseCfg.FailContent, sn, luckNumber), Status: 0, SourceName: "100K购", Crdate: common.GetTimeStamp(), Tools: nil, }) } return } // 投注(返回操作结果\抽奖码) func (this *purchaseMgr) bet(userId int, ipAddress string) (int, int) { this.lock.RLock() // 活动还没开启 if this.status != STATUS_ACTIVITY { this.lock.RUnlock() return 11, 0 } // 判断是否有空闲 if this.index >= serviceconfig.Server.PurchaseCfg.MaxNumber { this.lock.RUnlock() return 12, 0 } this.lock.RUnlock() // 扣除金币 if !cash.ReduceMoney(userId, serviceconfig.Server.PurchaseCfg.GoldFee, common.LOGTYPE_PURCHASE, "100K购", fmt.Sprintf("100K购第%d期", this.sn), ipAddress) { log.Release("purchasemgr.bet userId[%d] bet[%d] not enough gold", userId, serviceconfig.Server.PurchaseCfg.GoldFee) return 13, 0 } this.lock.Lock() number := this.numbers[this.index][0] this.numbers[this.index][1] = userId this.index++ this.lock.Unlock() this.addUser(userId, number) // 写数据 go bet(userId, this.sn, number) return 1, number } // 记录用户 func (this *purchaseMgr) addUser(userId, number int) { if userId <= 0 || number <= 0 { log.Release("purchasemgr.addUser userId[%d] number[%d] 无效数据", userId, number) return } this.lock.RLock() info, ok := this.user_list[userId] this.lock.RUnlock() if !ok { info = &userPurchase{ UserId: userId, Numbers: nil, } this.lock.Lock() this.user_list[userId] = info this.lock.Unlock() } info.Numbers = append(info.Numbers, number) return } // 获取当前信息(期数\元宝数量\倒计时\状态\消耗金币) func (this *purchaseMgr) getSysInfo() (int, int, int64, int, int) { this.lock.RLock() defer this.lock.RUnlock() return this.sn, serviceconfig.Server.PurchaseCfg.AwardAmount, this.endTime.Sub(this.getNow()).Milliseconds() / 1000, this.status, serviceconfig.Server.PurchaseCfg.GoldFee } // 获取用户抽奖码信息 func (this *purchaseMgr) getUserInfo(userId int) []int { this.lock.RLock() info, ok := this.user_list[userId] this.lock.RUnlock() if !ok { return nil } return info.Numbers } // 小红点提示 func (this *purchaseMgr) checkTip() bool { this.lock.RLock() defer this.lock.RUnlock() return this.status == STATUS_ACTIVITY }