From a62b94446a95c8fe2103ab4a8ece9aaf406111bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=94=E5=BF=B5?= <1@72wo.cn> Date: Sat, 3 Jan 2026 01:35:32 +0800 Subject: [PATCH] =?UTF-8?q?```=20feat(pet):=20=E6=B7=BB=E5=8A=A0=E7=B2=BE?= =?UTF-8?q?=E7=81=B5=E8=BF=9B=E5=8C=96=E5=8A=9F=E8=83=BD=E5=B9=B6=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E8=9E=8D=E5=90=88=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增PetELV方法实现精灵进化功能,支持分支进化选择 - 添加进化相关的数据结构定义 - 实现进化材料检查和扣除逻辑 - 优化宠物融合失败处理机制 fix(fight): 修复战斗系统和效果计算问题 - 修复NewSeIdx_11和effect_60中的伤害计算逻辑 - 修复战斗状态判断条件,避免非PVP模式下的错误处理 - 优化战斗回合处理流程,修复效果缓存清空时机 - 修复effect_69 --- common/cool/service.go | 4 +- logic/controller/fight_pvp_withplayer.go | 8 ++ logic/controller/fight_tawor.go | 4 +- logic/controller/pet_elo.go | 51 +++++++++ logic/controller/pet_fusion.go | 44 ++++++- logic/service/fight/action.go | 2 +- logic/service/fight/boss/NewSeIdx_11.go | 4 +- logic/service/fight/boss/NewSeIdx_41.go | 11 +- logic/service/fight/effect/effect_60.go | 4 +- logic/service/fight/effect/effect_69.go | 4 +- logic/service/fight/fightc.go | 15 ++- logic/service/fight/input/input.go | 2 - logic/service/fight/loop.go | 80 +++++++------ logic/service/fight/node/node.go | 3 +- logic/service/pet/BargeList.go | 9 ++ logic/service/pet/list.go | 4 +- modules/config/model/shop.go | 140 +++++++++++++++++++++++ 17 files changed, 323 insertions(+), 66 deletions(-) create mode 100644 modules/config/model/shop.go diff --git a/common/cool/service.go b/common/cool/service.go index 158cdfd5f..ab5371a44 100644 --- a/common/cool/service.go +++ b/common/cool/service.go @@ -419,8 +419,8 @@ func (s *Service) ModifyAfter(ctx context.Context, method string, param g.MapStr // } - er1 := g.DB().GetCore().ClearCache(context.TODO(), s.Model.TableName()) - println(er1) + g.DB().GetCore().ClearCache(context.TODO(), s.Model.TableName()) + // println(er1) return } diff --git a/logic/controller/fight_pvp_withplayer.go b/logic/controller/fight_pvp_withplayer.go index 1e9060a91..7ec15a17d 100644 --- a/logic/controller/fight_pvp_withplayer.go +++ b/logic/controller/fight_pvp_withplayer.go @@ -36,7 +36,15 @@ func (h Controller) OnPlayerHandleFightInvite(data *fight.HandleFightInviteInbou for _, v := range c.HavePVPinfo { if v.GetInfo().UserID == data.UserID && v.Getfightinfo().Mode == data.Mode { + resp.Result = data.Flag + if resp.Result == 0 { + + v.SendPackCmd(2502, &resp) + atomic.StoreUint32(&c.Fightinfo.Mode, 0) + + return + } // 检查邀请者的邀请是否有效(对方已取消邀请) if v.Getfightinfo().Status == 0 { resp.Result = 4 // 邀请已取消 diff --git a/logic/controller/fight_tawor.go b/logic/controller/fight_tawor.go index 3a7929ed9..623858e7a 100644 --- a/logic/controller/fight_tawor.go +++ b/logic/controller/fight_tawor.go @@ -189,7 +189,7 @@ func (h Controller) PetTawor(data *fight.StartTwarInboundInfo, c *player.Player) switch data.Head.CMD { case 2429: //试炼之塔 for _, v := range boss.TaskIds { - c.CompletedTask(int(v), 500) + c.CompletedTask(int(v), 600) } c.Info.CurrentFreshStage++ if c.Info.CurrentFreshStage >= c.Info.MaxFreshStage { @@ -198,7 +198,7 @@ func (h Controller) PetTawor(data *fight.StartTwarInboundInfo, c *player.Player) case 2415: //勇者之塔 for _, v := range boss.TaskIds { - c.CompletedTask(int(v), 600) + c.CompletedTask(int(v), 500) } c.Info.CurrentStage++ if c.Info.CurrentStage >= c.Info.MaxStage { diff --git a/logic/controller/pet_elo.go b/logic/controller/pet_elo.go index b0b429f89..e128ff9a8 100644 --- a/logic/controller/pet_elo.go +++ b/logic/controller/pet_elo.go @@ -1 +1,52 @@ package controller + +import ( + "blazing/common/data/xmlres" + "blazing/common/socket/errorcode" + "blazing/logic/service/fight" + "blazing/logic/service/fight/info" + "blazing/logic/service/pet" + "blazing/logic/service/player" + + "github.com/jinzhu/copier" +) + +// PetEVDiy 自定义分配宠物努力值(EV) +// data: 包含宠物捕获时间和EV分配数据的输入信息 +// c: 当前玩家对象 +// 返回: 分配结果和错误码 +func (h Controller) PetELV(data *pet.C2S_PET_EVOLVTION, c *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) { + _, currentPet, found := c.FindPet(data.CacthTime) + if !found { + return nil, errorcode.ErrorCodes.Err10401 + } + + flag := xmlres.PetMAP[int(currentPet.ID)].EvolvFlag + + if flag == 0 { + return nil, errorcode.ErrorCodes.ErrPokemonNotEvolveReady + } + evinfo := xmlres.EVOLVMAP[flag].Branches[data.Index-1] + + if c.Service.Item.CheakItem(uint32(evinfo.EvolvItem)) < uint32(evinfo.EvolvItemCount) { + return nil, errorcode.ErrorCodes.ErrInsufficientItemsMulti + } + if evinfo.EvolvItem != 0 { + c.Service.Item.UPDATE(uint32(evinfo.EvolvItem), -evinfo.EvolvItemCount) + } + + currentPet.ID = uint32(xmlres.EVOLVMAP[flag].Branches[data.Index-1].MonTo) + currentPet.Update(true) + currentPet.CalculatePetPane(false) + + currentPet.Update(true) + updateOutbound := &info.PetUpdateOutboundInfo{} + + var petUpdateInfo info.UpdatePropInfo + + copier.Copy(&petUpdateInfo, currentPet) + + updateOutbound.Data = append(updateOutbound.Data, petUpdateInfo) + c.SendPackCmd(2508, updateOutbound) //准备包由各自发,因为协议不一样 + return nil, -1 +} diff --git a/logic/controller/pet_fusion.go b/logic/controller/pet_fusion.go index b0b3b41d2..621fbb157 100644 --- a/logic/controller/pet_fusion.go +++ b/logic/controller/pet_fusion.go @@ -10,6 +10,7 @@ import ( "github.com/alpacahq/alpacadecimal" "github.com/gogf/gf/v2/util/grand" + "github.com/samber/lo" ) func (h Controller) PetFusion(data *pet.C2S_PetFusion, c *player.Player) (result *pet.PetFusionInfo, err errorcode.ErrorCode) { @@ -49,11 +50,34 @@ func (h Controller) PetFusion(data *pet.C2S_PetFusion, c *player.Player) (result resid := int(service.NewPetFusionService().Data(Mcatchpetinfo.ID, Auxpetinfo.ID, Mcatchpetinfo.Level+Auxpetinfo.Level)) if resid == 0 { - //todo失败降低等级 + + _, ok := lo.Find(data.GoldItem1[:], func(item uint32) bool { + return item == 300044 + }) + if c.Service.Item.CheakItem(300044) > 0 && ok { + c.Service.Item.UPDATE(300044, -1) + + } else { + if Auxpetinfo.Level > 5 { + Auxpetinfo.Level = Auxpetinfo.Level - 5 + + } else { + Auxpetinfo.Level = 1 + } + + } return &pet.PetFusionInfo{}, 0 } + for _, v := range data.Item1 { + if c.Service.Item.CheakItem(v) == 0 { + return &pet.PetFusionInfo{}, 0 + } + + } + effect := int(service.NewPetFusionMaterialService().Data(data.Item1)) + if effect == 0 { return &pet.PetFusionInfo{}, 0 } @@ -77,9 +101,23 @@ func (h Controller) PetFusion(data *pet.C2S_PetFusion, c *player.Player) (result } c.Service.Pet.PetAdd(r) println(c.Info.UserID, "进行融合", len(c.Info.PetList), Mcatchpetinfo.ID, Auxpetinfo.ID, r.ID) - c.PetDel(data.Auxcatchtime) - c.PetDel(data.Mcatchtime) + c.PetDel(data.Mcatchtime) + _, ok2 := lo.Find(data.GoldItem1[:], func(item uint32) bool { + return item == 300043 + }) + + if c.Service.Item.CheakItem(300043) > 0 && ok2 { + c.Service.Item.UPDATE(300044, -1) + } else { + + c.PetDel(data.Auxcatchtime) + + } + for _, v := range data.Item1 { + c.Service.Item.UPDATE(v, -1) + + } //todo材料扣除 return &pet.PetFusionInfo{ ObtainTime: r.CatchTime, diff --git a/logic/service/fight/action.go b/logic/service/fight/action.go index 08906a29e..b59302b1f 100644 --- a/logic/service/fight/action.go +++ b/logic/service/fight/action.go @@ -33,7 +33,7 @@ func (f *FightC) Over(c common.PlayerI, res info.EnumBattleOverReason) { cool.Logger.Debug(context.Background(), " 战斗chan已关闭") return } - if f.Info.Status != info.BattleMode.FIGHT_WITH_NPC { + if f.Info.Status != info.BattleMode.FIGHT_WITH_NPC && res == info.BattleOverReason.PlayerEscape { return } // case *action.EscapeAction: diff --git a/logic/service/fight/boss/NewSeIdx_11.go b/logic/service/fight/boss/NewSeIdx_11.go index d2e1d00da..baa839c6c 100644 --- a/logic/service/fight/boss/NewSeIdx_11.go +++ b/logic/service/fight/boss/NewSeIdx_11.go @@ -3,8 +3,6 @@ package effect import ( "blazing/logic/service/fight/info" "blazing/logic/service/fight/input" - - "github.com/alpacahq/alpacadecimal" ) // 11. 受到任何攻击都会反弹1/n的伤害给对方;(a1: n) @@ -30,7 +28,7 @@ func (e *NewSel11) Skill_Use_ex() bool { e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{ Type: info.DamageType.Fixed, - Damage: alpacadecimal.NewFromInt(int64(e.Ctx().Opp.SumDamage.IntPart())).Div(alpacadecimal.NewFromInt(int64(e.SideEffectArgs[0]))), + Damage: e.Ctx().Opp.SumDamage.Div(e.Args()[0]), }) return true } diff --git a/logic/service/fight/boss/NewSeIdx_41.go b/logic/service/fight/boss/NewSeIdx_41.go index bfc244668..37ba37d98 100644 --- a/logic/service/fight/boss/NewSeIdx_41.go +++ b/logic/service/fight/boss/NewSeIdx_41.go @@ -1,6 +1,7 @@ package effect import ( + "blazing/logic/service/fight/action" "blazing/logic/service/fight/input" ) @@ -10,10 +11,16 @@ type NewSel41 struct { NewSel0 } -func (e *NewSel41) Turn_End() { +func (e *NewSel41) Compare_Pre(fattack *action.SelectSkillAction, sattack *action.SelectSkillAction) bool { + if e.ID().GetCatchTime() != e.Ctx().Our.CurrentPet.Info.CatchTime { + return true + } + if sattack == nil { //说明有一方放弃出手,如果自身被控那也不能回血 + return true + } e.Ctx().Our.Heal(e.Ctx().Our, nil, e.Args()[0]) - + return true } func init() { input.InitEffect(input.EffectType.NewSel, 41, &NewSel41{}) diff --git a/logic/service/fight/effect/effect_60.go b/logic/service/fight/effect/effect_60.go index da8eac786..1aa32def4 100644 --- a/logic/service/fight/effect/effect_60.go +++ b/logic/service/fight/effect/effect_60.go @@ -4,8 +4,6 @@ import ( "blazing/logic/service/fight/info" "blazing/logic/service/fight/input" "blazing/logic/service/fight/node" - - "github.com/alpacahq/alpacadecimal" ) /** @@ -35,6 +33,6 @@ func (e *Effect60) OnSkill() bool { } e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{ Type: info.DamageType.Fixed, - Damage: alpacadecimal.NewFromInt(int64(e.SideEffectArgs[1]))}) + Damage: e.Args()[1]}) return true } diff --git a/logic/service/fight/effect/effect_69.go b/logic/service/fight/effect/effect_69.go index e900be6d4..d0edb3eab 100644 --- a/logic/service/fight/effect/effect_69.go +++ b/logic/service/fight/effect/effect_69.go @@ -27,7 +27,9 @@ func init() { } func (e *Effect69) OnSkill() bool { - + if !e.Hit() { + return true + } t := &Effect69_sub{ EffectNode: node.EffectNode{}, } diff --git a/logic/service/fight/fightc.go b/logic/service/fight/fightc.go index 55c325b15..22fbafbeb 100644 --- a/logic/service/fight/fightc.go +++ b/logic/service/fight/fightc.go @@ -121,6 +121,9 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction) } f.Broadcast(func(ff *input.Input) { + + ff.EffectCache = make([]input.Effect, 0) //先把上一回合数据清空,但是应该把本身延续类效果集成过来 + ff.Effect_Lost = make([]input.Effect, 0) ff.Exec(func(effect input.Effect) bool { //回合开始前 effect.Turn_Start(firstAttack, secondAttack) return true @@ -192,10 +195,10 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction) } currentSkill = originalSkill - defender.Exec(func(effect input.Effect) bool { //这个是能否使用技能 - effect.Ctx().SkillEntity = currentSkill - return effect.Action_start_ex(firstAttack, secondAttack) - }) + // defender.Exec(func(effect input.Effect) bool { //这个是能否使用技能 + // effect.Ctx().SkillEntity = currentSkill + // return effect.Action_start_ex(firstAttack, secondAttack) + // }) canUseSkill := attacker.Exec(func(effect input.Effect) bool { //这个是能否使用技能 effect.Ctx().SkillEntity = currentSkill return effect.Action_start(firstAttack, secondAttack) @@ -360,10 +363,10 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction) fighter.Player.SendPackCmd(2505, &attackValueResult) fighter.CanChange = 0 }) - println("回合结束") + //println("回合结束") if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC { if f.Opp.CurrentPet.Info.Hp <= 0 { - println("回合结束开始执行NPC动作") + //println("回合结束开始执行NPC动作") f.Opp.GetAction() //panic("AI自动技能") } diff --git a/logic/service/fight/input/input.go b/logic/service/fight/input/input.go index f010ea787..32773781c 100644 --- a/logic/service/fight/input/input.go +++ b/logic/service/fight/input/input.go @@ -228,8 +228,6 @@ func (our *Input) Parseskill(skill *action.SelectSkillAction) { return } - our.EffectCache = make([]Effect, 0) //先把上一回合数据清空,但是应该把本身延续类效果集成过来 - our.Effect_Lost = make([]Effect, 0) // our.Initeffectcache() //这里说明是延续的效果,每次复制出来一个新的就好了 //i.NewEffects = make([]Effect, 0) //这里说明是新增的效果 temparg := skill.SideEffectArgS diff --git a/logic/service/fight/loop.go b/logic/service/fight/loop.go index 12d99be93..01fb88b8f 100644 --- a/logic/service/fight/loop.go +++ b/logic/service/fight/loop.go @@ -244,56 +244,60 @@ func (f *FightC) resolveRound(p1Action, p2Action action.BattleActionI) { return } - // fmt.Println("开始结算回合") - // 动作优先级排序 b1, b2 := f.Compare(p1Action, p2Action) - switch a := b1.(type) { - + switch actionType := b1.(type) { case *action.ActiveSwitchAction: - - if f.GetInputByAction(a, false).CurrentPet.Info.Hp <= 0 { - f.GetInputByAction(a, false).CurrentPet.Info.Hp = 1 - } - if b2k, ok := b2.(*action.SelectSkillAction); ok { - if b2k.SkillEntity != nil { - if b2k.CD != nil { - f.waittime = *b2k.CD - } - } - f.enterturn(b2.(*action.SelectSkillAction), nil) - } else { - - f.enterturn(nil, nil) - } + f.handleActiveSwitchAction(actionType, b2) case *action.UseItemAction: - f.handleItemAction(a) - if f.GetInputByAction(a, false).CurrentPet.Info.Hp <= 0 { - f.GetInputByAction(a, false).CurrentPet.Info.Hp = 1 - } - if b2k, ok := b2.(*action.SelectSkillAction); ok { - if b2k.SkillEntity != nil { - if b2k.CD != nil { - f.waittime = *b2k.CD - } - } - - f.enterturn(b2.(*action.SelectSkillAction), nil) - } else { - if a1, ok := b2.(*action.UseItemAction); ok { - f.handleItemAction(a1) - - } - f.enterturn(nil, nil) - } + f.handleUseItemAction(actionType, b2) default: f.handleSkillActions(b1, b2) } } +// handleActiveSwitchAction 处理主动切换精灵动作 +func (f *FightC) handleActiveSwitchAction(switchAction *action.ActiveSwitchAction, otherAction action.BattleActionI) { + input := f.GetInputByAction(switchAction, false) + if input.CurrentPet.Info.Hp <= 0 { + input.CurrentPet.Info.Hp = 1 + } + + if skillAction, ok := otherAction.(*action.SelectSkillAction); ok { + if skillAction.SkillEntity != nil && skillAction.CD != nil { + f.waittime = *skillAction.CD + } + f.enterturn(skillAction, nil) + } else { + f.enterturn(nil, nil) + } +} + +// handleUseItemAction 处理使用道具动作 +func (f *FightC) handleUseItemAction(itemAction *action.UseItemAction, otherAction action.BattleActionI) { + f.handleItemAction(itemAction) + + input := f.GetInputByAction(itemAction, false) + if input.CurrentPet.Info.Hp <= 0 { + input.CurrentPet.Info.Hp = 1 + } + + if skillAction, ok := otherAction.(*action.SelectSkillAction); ok { + if skillAction.SkillEntity != nil && skillAction.CD != nil { + f.waittime = *skillAction.CD + } + f.enterturn(skillAction, nil) + } else { + if otherItemAction, ok := otherAction.(*action.UseItemAction); ok { + f.handleItemAction(otherItemAction) + } + f.enterturn(nil, nil) + } +} + // 使用道具的逻辑封装 func (f *FightC) handleItemAction(a *action.UseItemAction) { diff --git a/logic/service/fight/node/node.go b/logic/service/fight/node/node.go index e6a13f525..0dd41ffc3 100644 --- a/logic/service/fight/node/node.go +++ b/logic/service/fight/node/node.go @@ -32,6 +32,7 @@ type EffectNode struct { func (e *EffectNode) Alive(t ...bool) bool { if len(t) > 0 { + println("效果失效", e.id.GetEffectType(), e.ID().Suffix(), t[0]) e.alive = t[0] } @@ -70,7 +71,7 @@ func (e *EffectNode) ID(t ...input.EffectIDCombiner) input.EffectIDCombiner { func (e *EffectNode) Hit(t ...bool) bool { if len(t) > 0 { - println("效果命中", e.id.GetEffectType(), e.id.Suffix(), t[0]) + // println("效果命中", e.id.GetEffectType(), e.id.Suffix(), t[0]) e.hit = t[0] } diff --git a/logic/service/pet/BargeList.go b/logic/service/pet/BargeList.go index cf5e44756..450d3567b 100644 --- a/logic/service/pet/BargeList.go +++ b/logic/service/pet/BargeList.go @@ -35,3 +35,12 @@ type S2C_50001 struct { type S2C_9756 struct { UseEV uint32 //用掉的学习力 } + +// C2S_PET_EVOLVTION 精灵进化相关的客户端到服务端的消息结构 +type C2S_PET_EVOLVTION struct { + Head common.TomeeHeader `cmd:"2314" struc:"skip"` + CacthTime uint32 // 精灵的捕捉时间 + Index uint32 // 进化的分支索引。0代表没选择进化,1就是第一种进化形态,2就是其他分支进化形态 + // 如果没有分支进化,只有一种进化形态,Index只能为1 + // 后端直接判断进化条件的材料,执行进化并扣除材料 +} diff --git a/logic/service/pet/list.go b/logic/service/pet/list.go index 17b3ed9d4..81a5f7af0 100644 --- a/logic/service/pet/list.go +++ b/logic/service/pet/list.go @@ -35,8 +35,8 @@ type C2S_PetFusion struct { Auxcatchtime uint32 `json:"auxcatchtime" msgpack:"auxcatchtime"` // 副精灵的时间戳 Item1 [4]uint32 `json:"item1" msgpack:"item1"` // 物品序号1 - GoldItem1 uint32 `json:"gold_item1" msgpack:"gold_item1"` // 0代表未放置 金豆物品1(C#:gold_item1) - GoldItem2 uint32 `json:"gold_item2" msgpack:"gold_item2"` // 0代表未放置 金豆物品2(C#:gold_item2) + GoldItem1 [2]uint32 `json:"gold_item1" msgpack:"gold_item1"` // 0代表未放置 金豆物品1(C#:gold_item1) + } // PetFusionInfo 精灵融合结果详情(后端回包嵌套结构体) diff --git a/modules/config/model/shop.go b/modules/config/model/shop.go new file mode 100644 index 000000000..cf640b25e --- /dev/null +++ b/modules/config/model/shop.go @@ -0,0 +1,140 @@ +package model + +import ( + "blazing/cool" + "errors" + "time" +) + +// 表名常量定义:商店配置表 +const ( + TableNameShopConfig = "shop_config" // 商店配置表(记录商品信息、价格、库存、分类等) +) + +// ShopConfig 商店商品核心配置模型 +type ShopConfig struct { + *cool.Model `json:"-" gorm:"embedded"` // 嵌入通用Model(ID/创建时间/更新时间,不参与json序列化) + + // 核心字段 + ProductName string `gorm:"not null;size:128;comment:'商品名称'" json:"product_name" description:"商品名称"` + Description string `gorm:"not null;size:512;comment:'商品描述'" json:"description" description:"商品描述"` + + // 价格信息 + SeerdouPrice uint32 `gorm:"not null;default:0;comment:'骄阳豆价格'" json:"seerdou_price" description:"骄阳豆价格"` + JindouPrice uint32 `gorm:"not null;default:0;comment:'价格'" json:"jindou_price" description:"金豆价格"` + + // 库存信息 + Stock uint32 `gorm:"not null;default:0;comment:'商品库存数量(0表示无限库存)'" json:"stock" description:"库存数量"` + + // 分类信息 + CategoryID uint32 `gorm:"not null;default:0;comment:'商品分类ID'" json:"category_id" description:"分类ID"` + Category string `gorm:"not null;size:64;comment:'商品分类名称'" json:"category" description:"分类名称"` + + // 图片信息 + IconURL string `gorm:"not null;size:255;comment:'商品图标URL'" json:"icon_url" description:"图标URL"` + + // 状态信息 + IsOnSale uint32 `gorm:"not null;default:1;comment:'是否上架(0-下架 1-上架)'" json:"is_on_sale" description:"是否上架"` + + // 时间信息 + ValidStartTime time.Time `gorm:"not null;comment:'商品有效开始时间'" json:"valid_start_time" description:"有效开始时间"` + ValidEndTime time.Time `gorm:"not null;comment:'商品有效结束时间'" json:"valid_end_time" description:"有效结束时间"` + + // 限购信息 + QuotaLimit uint32 `gorm:"not null;default:0;comment:'单位时间内的限购数量(0表示不限购)'" json:"quota_limit" description:"限购数量"` + QuotaCycle uint32 `gorm:"not null;default:0;comment:'限购周期(单位:小时,0表示不限购)'" json:"quota_cycle" description:"限购周期(小时)"` + + // 备注信息 + Remark string `gorm:"size:512;default:'';comment:'商品备注'" json:"remark" description:"备注信息"` +} + +// -------------------------- 核心配套方法(遵循项目规范)-------------------------- +func (*ShopConfig) TableName() string { + return TableNameShopConfig +} + +func (*ShopConfig) GroupName() string { + return "default" +} + +func NewShopConfig() *ShopConfig { + return &ShopConfig{ + Model: cool.NewModel(), + ProductName: "", + Description: "", + SeerdouPrice: 0, + JindouPrice: 0, + Stock: 0, + CategoryID: 0, + Category: "", + IconURL: "", + IsOnSale: 1, // 默认上架 + ValidStartTime: time.Now(), + ValidEndTime: time.Now().AddDate(1, 0, 0), // 默认有效期1年 + QuotaLimit: 0, // 默认不限购 + QuotaCycle: 0, // 默认不限时 + Remark: "", + } +} + +// Validate 验证ShopConfig字段的有效性 +func (s *ShopConfig) Validate() error { + if s.ProductName == "" { + return errors.New("商品名称不能为空") + } + + if len(s.ProductName) > 128 { + return errors.New("商品名称长度不能超过128个字符") + } + + if len(s.Description) > 512 { + return errors.New("商品描述长度不能超过512个字符") + } + + if len(s.Category) > 64 { + return errors.New("商品分类名称长度不能超过64个字符") + } + + if len(s.IconURL) > 255 { + return errors.New("商品图标URL长度不能超过255个字符") + } + + if len(s.Remark) > 512 { + return errors.New("备注信息长度不能超过512个字符") + } + + if s.ValidStartTime.After(s.ValidEndTime) { + return errors.New("有效开始时间不能晚于结束时间") + } + + if s.IsOnSale != 0 && s.IsOnSale != 1 { + return errors.New("上架状态值不正确,应为0或1") + } + + if s.QuotaCycle > 0 && s.QuotaLimit == 0 { + return errors.New("设置了限购周期但未设置限购数量") + } + + return nil +} + +// IsInValidPeriod 检查当前时间是否在商品有效期内 +func (s *ShopConfig) IsInValidPeriod() bool { + now := time.Now() + return now.After(s.ValidStartTime) && now.Before(s.ValidEndTime) +} + +// IsAvailable 检查商品是否可用(上架状态且在有效期内) +func (s *ShopConfig) IsAvailable() bool { + return s.IsOnSale == 1 && s.IsInValidPeriod() +} + +// HasQuotaRestriction 检查商品是否有时间周期限购 +func (s *ShopConfig) HasQuotaRestriction() bool { + return s.QuotaLimit > 0 && s.QuotaCycle > 0 +} + +// -------------------------- 表结构自动同步 -------------------------- +func init() { + cool.CreateTable(&ShopConfig{}) +} \ No newline at end of file