```
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful

feat(game): 宠物融合系统添加物品消耗异常处理

- 在宠物融合过程中添加物品扣除失败的错误检查
- 当物品不足时返回ErrInsufficientItems错误码

fix(pet): 宠物仓库管理功能增加数据库操作错误处理

- 在宠物释放到仓库和从仓库取出时验证数据库更新结果
- 添加宠物背包切换功能的错误检查机制

feat(fight):
This commit is contained in:
昔念
2026-03-19 14:50:11 +08:00
parent e2ac5a6325
commit b558f46d7a
10 changed files with 142 additions and 63 deletions

View File

@@ -66,7 +66,10 @@ func (h Controller) PetFusion(data *pet.C2S_PetFusion, c *player.Player) (result
// utils.CountSliceElements(data.Item1[:])
for _, v := range data.Item1 {
c.Service.Item.UPDATE(v, -1)
err := c.Service.Item.UPDATE(v, -1)
if err != nil {
return result, errorcode.ErrorCodes.ErrInsufficientItems
}
}
result = &pet.PetFusionInfo{

View File

@@ -85,7 +85,9 @@ func (h Controller) PetReleaseToWarehouse(
return nil, errorcode.ErrorCodes.ErrCannotReleaseNonWarehouse
}
player.Service.Pet.UPdateFree(data.CatchTime, 1)
if !player.Service.Pet.UPdateFree(data.CatchTime, 1) {
return nil, errorcode.ErrorCodes.ErrSystemError
}
return nil, err
}
@@ -96,7 +98,10 @@ func (h Controller) PetRetrieveFromWarehouse(
//如果背包没找到,再放入背包
if _, _, ok := player.FindPet(data.CatchTime); !ok {
player.Service.Pet.UPdateFree(data.CatchTime, 0)
if !player.Service.Pet.UPdateFree(data.CatchTime, 0) {
return nil, errorcode.ErrorCodes.ErrSystemError
}
}
return nil, 0
@@ -127,7 +132,10 @@ func (h Controller) TogglePetBagWarehouse(
if index < 0 || index >= len(player.Info.PetList) {
return result, errorcode.ErrorCodes.ErrPokemonIDMismatch
}
player.Service.Pet.UPdate(*pet)
err := player.Service.Pet.UPdate(*pet)
if err != nil {
return result, errorcode.ErrorCodes.ErrSystemError
}
player.Info.PetList = append(player.Info.PetList[:index], player.Info.PetList[index+1:]...)
}

View File

@@ -11,33 +11,34 @@ type Effect1044 struct {
node.EffectNode
damageMultiplierActive bool
multiplierDuration int
can bool
}
func (e *Effect1044) OnSkill() bool {
// 检查对手是否有能力提升状态可以吸取
var sub bool
for i, v := range e.Ctx().Opp.Prop[:] {
if v > 0 {
sub = true
e.Ctx().Our.SetProp(e.Ctx().Our, int8(i), v)
e.Ctx().Opp.SetProp(e.Ctx().Our, int8(i), 0)
if e.Ctx().Opp.SetProp(e.Ctx().Our, int8(i), 0) {
e.can = true
e.Ctx().Our.SetProp(e.Ctx().Our, int8(i), v)
}
}
}
if sub {
e.GenSub(&Effect1044_sub{}, int(e.Args()[1].IntPart()))
}
return true
}
type Effect1044_sub struct {
node.EffectNode
}
func (e *Effect1044_sub) Damage_Mul(_ *info.DamageZone) bool {
func (e *Effect1044) Damage_Mul(t *info.DamageZone) bool {
if !e.can {
return true
}
if t.Type != info.DamageType.Red {
return true
}
t.Mul(2)
return true
}
func init() {

View File

@@ -220,7 +220,9 @@ type ClientData struct {
}
func (p *ClientData) GetPlayer(userid uint32) *Player { //TODO 这里待优化,可能存在内存泄漏问题
if p.Player == nil {
p.Player = NewPlayer(p.Conn)
}
_, ok := Mainplayer.LoadOrStore(userid, p)
if !ok {
p.Player = NewPlayer(p.Conn)

View File

@@ -13,10 +13,10 @@ const (
type GoldBeanOrder struct {
*cool.Model // 复用通用Model包含ID、CreatedAt、UpdatedAt等字段
PlayerID uint64 `gorm:"not null;index:idx_order_player_id;comment:'所属玩家ID'" json:"player_id" description:"所属玩家ID"`
ExchangeNum uint32 `gorm:"not null;default:0;comment:'兑换数量(金豆/金币)'" json:"exchange_num" description:"兑换数量"`
Rate uint32 `gorm:"not null;default:0;comment:'兑换汇率如1金豆=10金币'" json:"rate" description:"兑换汇率"`
Status uint32 `gorm:"not null;default:0;comment:'状态0待处理 1完成'" json:"status" description:"状态"`
PlayerID uint64 `gorm:"not null;index:idx_order_player_id;comment:'所属玩家ID'" json:"player_id" description:"所属玩家ID"`
ExchangeNum uint32 `gorm:"not null;default:0;comment:'兑换数量(金豆/金币)'" json:"exchange_num" description:"兑换数量"`
Rate float64 `gorm:"not null;default:0;comment:'兑换比例'" json:"rate" description:"兑换比例"`
Status uint32 `gorm:"not null;default:0;comment:'状态0待处理 1完成'" json:"status" description:"状态"`
}
// -------------------------- 核心配套方法 --------------------------

View File

@@ -38,6 +38,52 @@ type Pet struct {
Data PetInfo `gorm:"type:jsonb;not null;comment:'精灵全部数据'" json:"data"`
}
// GetOffShelfFee 计算商品下架扣费规则
// 返回值allowOffShelf(是否允许下架), feeRate(扣费比例), err(错误信息)
func (tt *Pet) GetOffShelfFee() (bool, float64, error) {
// 获取当前时间
now := time.Now()
// 计算上架到现在的时长(小时)
duration := now.Sub(tt.UpdateTime.Time)
hours := duration.Hours()
// 阶段10-24小时展示期可下架但阶梯扣费
if hours >= 0 && hours < 24 {
switch {
case hours < 6:
// 0-6小时0扣费允许下架
return true, 0.0, nil
case hours < 12:
// 6-12小时扣30%,允许下架
return true, 0.3, nil
case hours < 18:
// 12-18小时扣60%,允许下架
return true, 0.6, nil
case hours < 24:
// 18-24小时扣90%,允许下架
return true, 0.9, nil
}
}
// 阶段224-30小时禁止下架
if hours >= 24 && hours < 30 {
return false, 0.0, errors.New("24-30小时交易期禁止下架")
}
// 阶段330小时后自由下架0扣费
if hours >= 30 {
return true, 0.0, nil
}
// 异常情况(上架时间在未来)
return false, 0.0, errors.New("商品未到上架时间,无法操作下架")
}
// CalculateOffShelfAmount 根据扣费比例计算实际扣费金额
func (tt *Pet) CalculateOffShelfAmount(feeRate float64) float64 {
return float64(tt.SalePrice) * feeRate
}
type Attr uint32
func (r Attr) sub() uint32 {

View File

@@ -6,6 +6,7 @@ import (
"context"
"fmt"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
@@ -37,7 +38,17 @@ func NewGoldListService(id uint32) *GoldListService {
BaseService: BaseService{userid: id,
Service: &cool.Service{Model: model.NewGoldBeanOrder()},
Service: &cool.Service{Model: model.NewGoldBeanOrder(),
ListQueryOp: &cool.QueryOp{
AddOrderby: g.MapStrStr{"updateTime": "asc"},
},
InsertParam: func(ctx context.Context) g.MapStrAny {
admin := cool.GetAdmin(ctx)
return g.MapStrAny{
"player_id": admin.UserId,
}
},
},
},
}

View File

@@ -244,11 +244,18 @@ func (s *InfoService) Save(data model.PlayerInfo) {
return
}
_, err := s.dbm_fix(s.Model).Data("data", data).Update()
if err != nil {
//todo 待实现兜底保存,现在有可能出错
s.saveToLocalFile(&data, err)
panic(err)
for i := 0; i < 3; i++ {
_, err := s.dbm_fix(s.Model).Data("data", data).Update()
if err != nil {
if i == 2 {
//todo 待实现兜底保存,现在有可能出错
s.saveToLocalFile(&data, err)
panic(err)
}
} else {
break
}
}
}

View File

@@ -24,23 +24,23 @@ func (s *ItemService) Get(min, max uint32) []model.Item {
return ttt
}
func (s *ItemService) UPDATE(id uint32, count int) {
func (s *ItemService) UPDATE(id uint32, count int) error {
if cool.Config.ServerInfo.IsVip != 0 && count < 0 {
return
return nil
}
if id == 0 {
return
return nil
}
m := s.dbm(s.Model)
ok, err := m.Where("item_id", id).Exist()
if err != nil {
panic(err)
return err
}
if ok {
_, err := s.dbm(s.Model).Where("item_id", id).Increment("item_cnt", count)
if err != nil {
panic(err)
return err
}
} else {
@@ -52,9 +52,10 @@ func (s *ItemService) UPDATE(id uint32, count int) {
"is_vip": cool.Config.ServerInfo.IsVip,
}
m.Data(data).Insert()
_, err := m.Data(data).Insert()
return err
}
return nil
}
// func (s *ItemService) UPDATEM(ids map[uint32]int) {

View File

@@ -1,7 +1,6 @@
package service
import (
"blazing/common/utils"
"blazing/cool"
basemodel "blazing/modules/base/model"
"blazing/modules/base/service"
@@ -17,7 +16,7 @@ import (
// 获取精灵信息 0是仓库,1是放生
func (s *PetService) PetInfo(flag int) []model.Pet {
var tt []model.Pet
err := s.dbm(s.Model).Where("free", flag).Scan(&tt)
err := s.dbm(s.Model).Where("free", flag).Where("is_sale", 0).Scan(&tt)
if err != nil {
return []model.Pet{}
}
@@ -42,46 +41,45 @@ func (s *PetService) PetCount(flag int) int {
}
func (s *PetService) UPdateFree(ctime uint32, free uint32) {
func (s *PetService) UPdateFree(ctime uint32, free uint32) bool {
s.dbm(s.Model).Where("catch_time", ctime).Where("is_sale", 0).Data(
res, _ := s.dbm(s.Model).Where("catch_time", ctime).Where("is_sale", 0).Data(
"free", free,
).Update()
r, _ := res.RowsAffected()
return r > 0
}
func (s *PetService) UPdatePrice(ctime uint32, Price uint32, is_sale uint32) error {
var item1 model.Pet
var feeRate float64
var err1 error
if is_sale == 1 {
t, _ := s.dbm(s.Model).Where("is_sale", 1).Count()
if t > 3 {
return fmt.Errorf("精灵数量已满")
}
} else {
s.dbm(s.Model).Where("catch_time", ctime).Scan(&item1)
_, feeRate, err1 = item1.GetOffShelfFee()
if err1 != nil {
return err1
}
}
var p model.Pet
s.dbm(s.Model).Where("catch_time", ctime).Scan(&p)
if p.SalePrice != 0 && p.SalePrice != Price && utils.IsToday(p.UpdateTime) { //说明要修改价格
return fmt.Errorf("一天只允许改价一次")
}
res0, err := s.dbm(s.Model).
res, _ := s.dbm(s.Model).
Where("catch_time", ctime). // 限定 ctime避免全表更新
Where("sale_price = ?", 0). // 只筛选 sale_price=0 的记录
//Where("sale_price = ?", 0). // 只筛选 sale_price=0 的记录
Data(g.Map{
"sale_price": Price,
"is_sale": is_sale,
}).Update()
g, _ := res.RowsAffected()
if g > 0 && is_sale == 0 && feeRate != 0 {
if err != nil {
return fmt.Errorf("修改 sale_price=0 的记录失败:%w", err)
}
affected0, _ := res0.RowsAffected()
if affected0 == 0 {
priceUpper := Price * 110 / 100 // 上限 = Price * 1.1(整数运算避免浮点误差)
priceLower := Price * 90 / 100 // 下限 = Price * 0.9
res, _ := s.dbm(s.Model).Where("catch_time", ctime).WhereBetween("sale_price", priceLower, priceUpper).Data("sale_price", Price, "is_sale", is_sale).Update()
t, _ := res.RowsAffected()
if t < 0 {
return fmt.Errorf("修改失败")
}
amount1 := item1.CalculateOffShelfAmount(feeRate)
service.NewBaseSysUserService().UpdateFreeGold(s.userid, -int64(amount1*100))
}
return nil
@@ -173,19 +171,20 @@ RETURNING max_ts;
})
}
func (s *PetService) UPdate(t model.PetInfo) {
func (s *PetService) UPdate(t model.PetInfo) error {
m := s.dbm(s.Model).Where("catch_time", t.CatchTime)
var tt *model.Pet
m.Scan(&tt)
if tt == nil {
return
return fmt.Errorf("没有此精灵")
}
tt.Data = t
_, err := m.OnConflict("catch_time").Update(tt)
if err != nil {
panic(err)
return err
}
return nil
}
func (s *PetService) PetInfo_One(cachetime uint32) *model.Pet {
@@ -311,7 +310,8 @@ func NewPetService(userid uint32) *PetService {
Service: &cool.Service{
Model: model.NewPet(),
ListQueryOp: &cool.QueryOp{
FieldEQ: []string{"player_id", "free", "is_sale"},
AddOrderby: g.MapStrStr{"updateTime": "asc"},
FieldEQ: []string{"player_id", "free", "is_sale"},
Where: func(ctx context.Context) [][]interface{} {
return [][]interface{}{