xy 2 gün önce
ebeveyn
işleme
a287463dd1

+ 5 - 0
bin/client_msg/bag.proto

@@ -0,0 +1,5 @@
+syntax = "proto3";
+
+option go_package = "./msg";
+
+import "common.proto";          // 直接引入

+ 329 - 0
src/server/datacenter/bag_db/bag.go

@@ -0,0 +1,329 @@
+package bagdb
+
+import (
+	"database/sql"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"log"
+	mysqlmgr "server/db/mysql"
+	"time"
+
+	_ "github.com/go-sql-driver/mysql"
+)
+
+// Item 背包中的道具
+type Item struct {
+	ID       int `json:"item_id"`
+	Quantity int `json:"quantity"`
+}
+
+// InventoryData 背包数据结构
+type InventoryData struct {
+	Slots []Item `json:"slots"`
+}
+
+// ItemInfo 道具基础信息
+type ItemInfo struct {
+	ID       int    `json:"item_id"`
+	Name     string `json:"item_name"`
+	MaxStack int    `json:"max_stack"` // 0表示不可叠加
+}
+
+// ItemRequest 添加道具请求
+type ItemRequest struct {
+	ItemID   int `json:"item_id"`
+	Quantity int `json:"quantity"`
+}
+
+// GetUserInventory 获取用户背包
+func GetUserInventory(userID string) (*InventoryData, error) {
+	var dataStr string
+	err := mysqlmgr.QueryRow("SELECT inventory_data FROM user_inventory WHERE user_id = ?", userID).Scan(&dataStr)
+	if err != nil {
+		if errors.Is(err, sql.ErrNoRows) {
+			return &InventoryData{Slots: make([]Item, 0)}, nil
+		}
+		return nil, fmt.Errorf("查询用户背包失败: %v", err)
+	}
+
+	var data InventoryData
+	if err = json.Unmarshal([]byte(dataStr), &data); err != nil {
+		return nil, fmt.Errorf("解析背包数据失败: %v", err)
+	}
+
+	return &data, nil
+}
+
+// SaveUserInventory 保存用户背包
+func SaveUserInventory(userID string, data *InventoryData) error {
+	jsonData, err := json.Marshal(data)
+	if err != nil {
+		return fmt.Errorf("序列化背包数据失败: %v", err)
+	}
+
+	_, err = mysqlmgr.Insert(`
+		INSERT INTO user_inventory (user_id, inventory_data, version) 
+		VALUES (?, ?, 1)
+		ON DUPLICATE KEY UPDATE 
+			inventory_data = VALUES(inventory_data),
+			version = version + 1`,
+		userID, string(jsonData))
+	if err != nil {
+		return fmt.Errorf("保存背包数据失败: %v", err)
+	}
+
+	return nil
+}
+
+// GetItemInfo 获取道具信息 ,::后面改成从 redis 查询 ,实现逻辑是 启动服务器时,把所有道具添加到redis库里
+func GetItemInfo(itemID int) (*ItemInfo, error) {
+	var item ItemInfo
+	err := mysqlmgr.QueryRow(`
+		SELECT item_id, item_name, max_stack 
+		FROM items 
+		WHERE item_id = ?`, itemID).
+		Scan(&item.ID, &item.Name, &item.MaxStack)
+	if err != nil {
+		return nil, fmt.Errorf("查询道具信息失败: %v", err)
+	}
+	return &item, nil
+}
+
+// AddItemsToMail 添加道具到邮箱
+func AddItemsToMail(userID string, items []ItemRequest) error {
+	// 开始事务
+	tx, err := mysqlmgr.Begin()
+	if err != nil {
+		return fmt.Errorf("开始事务失败: %v", err)
+	}
+	defer tx.Rollback()
+
+	// 为每组道具创建一封邮件
+	for _, itemReq := range items {
+		itemInfo, err := GetItemInfo(itemReq.ItemID)
+		if err != nil {
+			return err
+		}
+
+		_, err = tx.Exec(`
+			INSERT INTO user_mail 
+			(user_id, item_id, quantity, title, content, expire_time, status) 
+			VALUES (?, ?, ?, ?, ?, ?, 0)`,
+			userID,
+			itemReq.ItemID,
+			itemReq.Quantity,
+			fmt.Sprintf("获得%d个%s", itemReq.Quantity, itemInfo.Name),
+			fmt.Sprintf("由于背包空间不足,系统将%d个%s发送到您的邮箱", itemReq.Quantity, itemInfo.Name),
+			time.Now().Add(30*24*time.Hour),
+		)
+		if err != nil {
+			return fmt.Errorf("添加到邮箱失败: %v", err)
+		}
+	}
+
+	return tx.Commit()
+}
+
+// AcquireMultipleItems 获取多个道具
+func AcquireMultipleItems(userID string, itemRequests []ItemRequest) ([]ItemRequest, error) {
+	// 1. 获取用户最大格子数
+	var maxSlots int
+	err := mysqlmgr.QueryRow("SELECT max_slots FROM users WHERE user_id = ?", userID).Scan(&maxSlots)
+	if err != nil {
+		return nil, fmt.Errorf("查询用户信息失败: %v", err)
+	}
+
+	// 2. 获取当前背包数据
+	inventory, err := GetUserInventory(userID)
+	if err != nil {
+		return nil, err
+	}
+
+	// 3. 计算剩余空间
+	remainingSlots := maxSlots - len(inventory.Slots)
+	if remainingSlots < 0 {
+		remainingSlots = 0
+	}
+
+	// 4. 处理每种道具
+	var mailItems []ItemRequest
+	updatedInventory := &InventoryData{Slots: make([]Item, len(inventory.Slots))}
+	copy(updatedInventory.Slots, inventory.Slots)
+
+	for _, req := range itemRequests {
+		itemInfo, err := GetItemInfo(req.ItemID)
+		if err != nil {
+			return nil, err
+		}
+
+		remainingQuantity := req.Quantity
+
+		// 可叠加道具处理
+		if itemInfo.MaxStack > 1 {
+			// 先尝试堆叠到已有格子
+			for i := range updatedInventory.Slots {
+				if updatedInventory.Slots[i].ID == req.ItemID &&
+					updatedInventory.Slots[i].Quantity < itemInfo.MaxStack {
+					canAdd := itemInfo.MaxStack - updatedInventory.Slots[i].Quantity
+					add := min(remainingQuantity, canAdd)
+					updatedInventory.Slots[i].Quantity += add
+					remainingQuantity -= add
+					if remainingQuantity == 0 {
+						break
+					}
+				}
+			}
+		}
+
+		// 处理剩余数量
+		for remainingQuantity > 0 {
+			if remainingSlots <= 0 {
+				// 没有空格子了,剩余道具发到邮箱
+				mailItems = append(mailItems, ItemRequest{
+					ItemID:   req.ItemID,
+					Quantity: remainingQuantity,
+				})
+				remainingQuantity = 0
+				break
+			}
+
+			// 不可叠加道具或可叠加道具的新格子
+			if itemInfo.MaxStack <= 1 {
+				// 不可叠加道具,每个占一个格子
+				updatedInventory.Slots = append(updatedInventory.Slots, Item{
+					ID:       req.ItemID,
+					Quantity: 1,
+				})
+				remainingQuantity--
+				remainingSlots--
+			} else {
+				// 可叠加道具,创建新格子
+				add := min(remainingQuantity, itemInfo.MaxStack)
+				updatedInventory.Slots = append(updatedInventory.Slots, Item{
+					ID:       req.ItemID,
+					Quantity: add,
+				})
+				remainingQuantity -= add
+				remainingSlots--
+			}
+		}
+	}
+
+	// 5. 保存背包数据
+	if len(updatedInventory.Slots) != len(inventory.Slots) ||
+		len(mailItems) == 0 {
+		if err := SaveUserInventory(userID, updatedInventory); err != nil {
+			return nil, err
+		}
+	}
+
+	// 6. 如果有道具需要发到邮箱
+	if len(mailItems) > 0 {
+		if err := AddItemsToMail(userID, mailItems); err != nil {
+			return mailItems, fmt.Errorf("部分道具添加到背包,但发送到邮箱失败: %v", err)
+		}
+	}
+
+	return mailItems, nil
+}
+
+// ConsumeMultipleItems 消耗多个道具
+func ConsumeMultipleItems(userID string, itemRequests []ItemRequest) error {
+	// 1. 获取当前背包数据
+	inventory, err := GetUserInventory(userID)
+	if err != nil {
+		return err
+	}
+
+	// 2. 检查道具数量是否足够
+	itemCounts := make(map[int]int)
+	for _, req := range itemRequests {
+		itemCounts[req.ItemID] += req.Quantity
+	}
+
+	// 统计当前拥有的道具数量
+	currentCounts := make(map[int]int)
+	for _, slot := range inventory.Slots {
+		currentCounts[slot.ID] += slot.Quantity
+	}
+
+	// 检查是否足够
+	for itemID, needed := range itemCounts {
+		if currentCounts[itemID] < needed {
+			itemInfo, _ := GetItemInfo(itemID)
+			return fmt.Errorf("道具数量不足,需要%d个%s但只有%d个",
+				needed, itemInfo.Name, currentCounts[itemID])
+		}
+	}
+
+	// 3. 扣除道具
+	newSlots := make([]Item, 0, len(inventory.Slots))
+	remainingToConsume := make(map[int]int)
+	for itemID, count := range itemCounts {
+		remainingToConsume[itemID] = count
+	}
+
+	for _, slot := range inventory.Slots {
+		if consume, ok := remainingToConsume[slot.ID]; ok && consume > 0 {
+			if slot.Quantity > consume {
+				// 这个格子有剩余
+				newSlots = append(newSlots, Item{
+					ID:       slot.ID,
+					Quantity: slot.Quantity - consume,
+				})
+				remainingToConsume[slot.ID] = 0
+			} else {
+				// 完全消耗这个格子的道具
+				remainingToConsume[slot.ID] -= slot.Quantity
+			}
+		} else {
+			// 不需要消耗这个格子的道具
+			newSlots = append(newSlots, slot)
+		}
+	}
+
+	// 4. 保存背包数据
+	updatedInventory := &InventoryData{Slots: newSlots}
+	return SaveUserInventory(userID, updatedInventory)
+}
+
+func min(a, b int) int {
+	if a < b {
+		return a
+	}
+	return b
+}
+
+func example() {
+
+	// 示例1: 同时获取多种道具
+	userID := "user123"
+	itemsToAdd := []ItemRequest{
+		{ItemID: 101, Quantity: 15}, // 可叠加道具
+		{ItemID: 201, Quantity: 3},  // 不可叠加道具
+		{ItemID: 301, Quantity: 5},  // 其他道具
+	}
+
+	mailItems, err := AcquireMultipleItems(userID, itemsToAdd)
+	if err != nil {
+		log.Printf("获取道具失败: %v", err)
+	} else if len(mailItems) > 0 {
+		log.Printf("部分道具已发送到邮箱: %+v", mailItems)
+	} else {
+		log.Println("所有道具已成功添加到背包")
+	}
+
+	// 示例2: 同时消耗多种道具
+	itemsToConsume := []ItemRequest{
+		{ItemID: 101, Quantity: 5},
+		{ItemID: 201, Quantity: 1},
+	}
+
+	err = ConsumeMultipleItems(userID, itemsToConsume)
+	if err != nil {
+		log.Printf("消耗道具失败: %v", err)
+	} else {
+		log.Println("道具消耗成功")
+	}
+}

+ 1 - 0
src/server/hall/bag/bag.go

@@ -0,0 +1 @@
+package bag