Files
bl/modules/player/service/pet.go
昔念 4906197c77 1
2026-04-25 23:05:41 +08:00

365 lines
9.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package service
import (
"blazing/cool"
basemodel "blazing/modules/base/model"
"blazing/modules/base/service"
"blazing/modules/player/model"
"context"
"fmt"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
// nextCatchTime 生成下一个唯一捕捉时间戳(原子递增 max_ts
func (s *PetService) nextCatchTime(m ...*gdb.Model) (uint32, error) {
dbm := cool.DBM(service.NewBaseSysUserService().Model)
if len(m) > 0 {
dbm = m[0]
}
sql := fmt.Sprintf(`
UPDATE %s
SET max_ts = CASE
WHEN max_ts < EXTRACT(EPOCH FROM NOW())::INT THEN EXTRACT(EPOCH FROM NOW())::INT
ELSE max_ts + 1
END
WHERE id = ? AND deleted_at IS NULL
RETURNING max_ts;`, service.NewBaseSysUserService().Model.TableName())
ret, err := dbm.Raw(sql, s.userid).All()
if err != nil {
return 0, fmt.Errorf("生成捕捉时间失败: %w", err)
}
arr := ret.Array()
if len(arr) == 0 {
return 0, fmt.Errorf("generate catch time failed: empty result")
}
return arr[0].Uint32(), nil
}
// setCatchTime 将 Pet.CatchTime 同步到 Pet.Data.CatchTime
func setCatchTime(pet *model.Pet) {
if pet != nil {
pet.Data.CatchTime = pet.CatchTime
}
}
// 获取精灵信息 0是仓库 1是放生
func (s *PetService) PetInfo(flag int) []model.Pet {
var tt []model.Pet
if err := s.dbm(s.Model).Where("free", flag).Scan(&tt); err != nil {
return nil
}
for i := range tt {
tt[i].Data.CatchTime = tt[i].CatchTime
}
return tt
}
func (s *PetService) StorageInfo(isVip int, free int) []model.Pet {
var tt []model.Pet
if err := s.dbm_fix(s.Model).Where("free", free).Where("is_vip", isVip).Scan(&tt); err != nil {
return nil
}
for i := range tt {
tt[i].Data.CatchTime = tt[i].CatchTime
}
return tt
}
func (s *PetService) PetCount(flag int) int {
ret, err := s.dbm(s.Model).Where("free", flag).Count()
if err != nil {
return 0
}
return ret
}
// GetShowPets 获取基地展示的精灵列表is_show=1
func (s *PetService) GetShowPets() []model.Pet {
var tt []model.Pet
if err := s.dbm_fix(s.Model).Where("is_show", 1).Scan(&tt); err != nil {
return nil
}
for i := range tt {
tt[i].Data.CatchTime = tt[i].CatchTime
}
return tt
}
// UpdateIsShow 更新精灵基地展示状态
func (s *PetService) UpdateIsShow(catchTime uint32, isShow int) bool {
res, err := s.dbm(s.Model).
Where("catch_time", catchTime).
Data("is_show", isShow).
Update()
if err != nil {
return false
}
r, _ := res.RowsAffected()
return r > 0
}
// SetShowCatchTimes 批量设置基地展示精灵:
// 先清空当前玩家的展示状态,再将传入 catchTime 列表置为展示。
func (s *PetService) SetShowCatchTimes(catchTimes []uint32) bool {
if _, err := s.dbm(s.Model).
Where("is_show", 1).
Data("is_show", 0).
Update(); err != nil {
return false
}
if len(catchTimes) == 0 {
return true
}
if _, err := s.dbm(s.Model).
WhereIn("catch_time", catchTimes).
Data("is_show", 1).
Update(); err != nil {
return false
}
return true
}
func (s *PetService) UpdateFree(catchTime, fromFree, toFree uint32) bool {
res, err := s.dbm(s.Model).
Where("catch_time", catchTime).
Where("free", fromFree).
Data(g.Map{
"free": toFree,
"is_show": 0, // 放回背包/放生时取消基地展示
}).
Update()
if err != nil {
return false
}
r, _ := res.RowsAffected()
return r > 0
}
func (s *PetService) UpdatePrice(catchTime, price, free uint32) error {
var item model.Pet
var feeRate float64
if free == 2 {
t, _ := s.dbm(s.Model).Where("free", 2).Count()
if t > 3 {
return fmt.Errorf("超过3个上架限制无法修改价格")
}
} else {
if err := s.dbm(s.Model).Where("catch_time", catchTime).Scan(&item); err != nil {
return err
}
_, feeRate, _ = item.GetOffShelfFee()
_ = feeRate // feeRate 仅在 non-2 分支使用,在此声明以统一作用域
}
res, _ := s.dbm(s.Model).Where("catch_time", catchTime).Data(g.Map{
"sale_price": price,
"free": free,
"is_show": 0, // 上架/下架时取消基地展示
}).Update()
affected, _ := res.RowsAffected()
if affected > 0 && free != 2 && feeRate != 0 {
amount := item.CalculateOffShelfAmount(feeRate)
service.NewBaseSysUserService().UpdateFreeGold(s.userid, -int64(amount*100))
}
return nil
}
func (s *PetService) BuyPet(pid uint32) error {
tt := NewPetService(0).PetInfoOneByID(pid)
if tt == nil {
return fmt.Errorf("pet not found")
}
if tt.IsVip != 0 {
return fmt.Errorf("pet cannot be traded")
}
if tt.Free != 2 {
return fmt.Errorf("pet is not on sale")
}
if tt.SalePrice == 0 {
return fmt.Errorf("pet sale price is not set")
}
if !tt.UpdateTime.AddDate(0, 0, 1).Before(gtime.Now()) {
return fmt.Errorf("未到购买时间")
}
return g.DB().Transaction(context.TODO(), func(ctx context.Context, tx gdb.TX) error {
useGold := int64(tt.SalePrice)*102 + int64(tt.SaleCount)*5
var user basemodel.BaseSysUser
if err := tx.Model(basemodel.BaseSysUser{}).Where("id", s.userid).Fields("free_gold").Scan(&user); err != nil {
return err
}
if user.FreeGold < useGold {
return fmt.Errorf("余额不足")
}
if _, err := tx.Model(basemodel.BaseSysUser{}).Where("id", s.userid).Increment("free_gold", -useGold); err != nil {
return err
}
res, err := tx.Model(s.Model).Where("id", tt.ID).Delete()
if err != nil {
return err
}
affected, _ := res.RowsAffected()
if affected == 0 {
return fmt.Errorf("重复订单")
}
if _, err := tx.Model(basemodel.BaseSysUser{}).Where("id", tt.PlayerID).Increment("free_gold", int64(tt.SalePrice)*98); err != nil {
return err
}
catchTime, err := s.nextCatchTime(tx.Model(service.NewBaseSysUserService().Model))
if err != nil {
return err
}
player := model.Pet{
PlayerID: s.userid,
Data: tt.Data,
CatchTime: catchTime,
Free: 0,
SalePrice: tt.SalePrice,
SaleCount: tt.SaleCount + 1,
}
player.IsVip = cool.Config.ServerInfo.IsVip
player.Data.OldCatchTime = 0
if _, err := tx.Model(s.Model).Insert(player); err != nil {
return err
}
return nil
})
}
func (s *PetService) Update(t model.PetInfo) bool {
_, err := s.dbm(s.Model).Where("catch_time", t.CatchTime).Data("data", t).Update()
return err == nil
}
func (s *PetService) PetInfoOneByCatchTime(catchTime uint32) *model.Pet {
var tt *model.Pet
if err := s.dbm(s.Model).Where("catch_time", catchTime).Scan(&tt); err != nil || tt == nil {
return nil
}
setCatchTime(tt)
return tt
}
func (s *PetService) PetInfoOneByID(id uint32) *model.Pet {
var tt *model.Pet
if err := s.dbm(s.Model).Where("id", id).Scan(&tt); err != nil || tt == nil {
return nil
}
setCatchTime(tt)
return tt
}
func (s *PetService) PetInfoOneOther(userid, catchTime uint32) *model.Pet {
var tt *model.Pet
if err := s.dbm_fix(s.Model).
Where("player_id", userid).
Where("catch_time", catchTime).
Where("is_vip", cool.Config.ServerInfo.IsVip).
Scan(&tt); err != nil || tt == nil {
return nil
}
setCatchTime(tt)
return tt
}
func (s *PetService) PetInfoOneUnscoped(catchTime uint32) *model.Pet {
var tt *model.Pet
if err := s.dbm(s.Model).Where("catch_time", catchTime).Unscoped().Scan(&tt); err != nil || tt == nil {
return nil
}
setCatchTime(tt)
return tt
}
func (s *PetService) PetDel(catchTime uint32) {
s.dbm(s.Model).Where("catch_time", catchTime).Delete()
}
func (s *PetService) PetLevelAll() []model.Pet {
var tt []model.Pet
s.dbm(s.Model).As("pp").
Wheref("(pp.data->>'Level')::INT > 100").
OrderDesc("(pp.data->>'Level')::INT").
Scan(&tt)
return tt
}
// PetAdd 精灵真正添加后的捕捉时间才是真正的时间
func (s *PetService) PetAdd(y *model.PetInfo, saleCount uint32) (uint32, error) {
if y == nil {
return 0, nil
}
catchTime, err := s.nextCatchTime()
if err != nil {
return 0, err
}
y.CatchTime = catchTime
player := model.Pet{
PlayerID: s.userid,
Data: *y,
CatchTime: catchTime,
Free: 0,
SaleCount: saleCount,
}
player.IsVip = cool.Config.ServerInfo.IsVip
if _, err := cool.DBM(s.Model).Where("player_id", s.userid).Insert(player); err != nil {
return 0, err
}
return catchTime, nil
}
type PetService struct {
BaseService
}
func petFreeFilterQuery(ctx context.Context, m *gdb.Model) *gdb.Model {
if !g.RequestFromCtx(ctx).Get("free_all").Bool() {
m = m.WhereNot("free", 0)
}
return m
}
func NewPetService(userid uint32) *PetService {
return &PetService{
BaseService: BaseService{
userid: userid,
Service: &cool.Service{
Model: model.NewPet(),
ListQueryOp: &cool.QueryOp{
AddOrderby: g.MapStrStr{"updateTime": "asc"},
FieldEQ: []string{"player_id", "free"},
Where: func(ctx context.Context) [][]interface{} {
return [][]interface{}{
{"is_vip", 0, true},
}
},
Extend: petFreeFilterQuery,
},
PageQueryOp: &cool.QueryOp{
FieldEQ: []string{"player_id", "free"},
DataFieldEQ: []string{"ID"},
Where: func(ctx context.Context) [][]interface{} {
return [][]interface{}{
{"is_vip", 0, true},
}
},
Extend: petFreeFilterQuery,
},
},
},
}
}