EnergyEfficiencyManagement/Assets/Zion/Scripts/Utils/InventoryManager.cs

636 lines
18 KiB
C#
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.

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;
/// <summary>
/// 库存管理器 - 单例模式
/// </summary>
public class InventoryManager : MonoSingleton<InventoryManager>
{
[Header("配置文件")]
[SerializeField] private string fileName = "inventory.json";
[Header("事件")]
public UnityEvent onInventoryLoaded; // 库存加载完成事件
public UnityEvent onInventoryUpdated; // 库存更新事件
// 库存数据
private InventoryData _inventoryData = new InventoryData();
private string _persistentFilePath; // 可读写路径
private string _streamingAssetsPath; // 只读路径
private bool _isInitialized = false;
private readonly object _lockObject = new object(); // 线程安全锁
#region
/// <summary>
/// 获取所有库存项(只读)
/// </summary>
public IReadOnlyList<InventoryItem> AllItems
{
get
{
lock (_lockObject)
{
return _inventoryData.inventory.AsReadOnly();
}
}
}
/// <summary>
/// 库存是否已加载
/// </summary>
public bool IsInitialized => _isInitialized;
#endregion
#region Unity
protected override void Initialize()
{
// 初始化路径
_streamingAssetsPath = Path.Combine(Application.streamingAssetsPath, fileName);
_persistentFilePath = Path.Combine(Application.persistentDataPath, fileName);
}
private IEnumerator Start()
{
yield return StartCoroutine(InitializeInventoryAsync());
}
private void OnApplicationQuit()
{
// 退出时自动保存
SaveInventoryData();
}
#endregion
#region
/// <summary>
/// 异步初始化库存系统
/// </summary>
public IEnumerator InitializeInventoryAsync(bool forceReload = false)
{
if (_isInitialized && !forceReload)
yield break;
Debug.Log($"初始化库存系统...");
//Debug.Log($"StreamingAssets路径: {_streamingAssetsPath}");
//Debug.Log($"可读写路径: {_persistentFilePath}");
bool loadSuccess = false;
// 策略2: 如果可读写路径失败从StreamingAssets加载
if (!loadSuccess)
{
yield return StartCoroutine(LoadFromStreamingAssets());
}
_isInitialized = true;
onInventoryLoaded?.Invoke();
}
/// <summary>
/// 从StreamingAssets加载跨平台兼容
/// </summary>
private IEnumerator LoadFromStreamingAssets()
{
// 使用UnityWebRequest加载
UnityWebRequest request = UnityWebRequest.Get(_streamingAssetsPath);
yield return request.SendWebRequest();
if (request.result != UnityWebRequest.Result.Success)
{
Debug.LogError($"从StreamingAssets加载失败: {request.error}");
CreateDefaultInventory();
}
else
{
// 获取文本并处理编码
string json = request.downloadHandler.text;
// 尝试检测并修复编码问题
json = FixEncodingIfNeeded(json);
//Debug.Log($"原始JSON内容: {json}");
try
{
lock (_lockObject)
{
_inventoryData = JsonUtility.FromJson<InventoryData>(json);
}
Debug.Log($"从StreamingAssets加载成功共 {_inventoryData.inventory.Count} 个种类");
SaveInventoryData(); // 保存到可读写路径
}
catch (System.Exception e)
{
Debug.LogError($"JSON解析失败: {e.Message}");
Debug.Log($"原始JSON内容: {json}");
CreateDefaultInventory();
}
}
}
/// <summary>
/// 检测并修复编码问题
/// </summary>
private string FixEncodingIfNeeded(string text)
{
// 检测是否是乱码(中文变问号等)
if (string.IsNullOrEmpty(text))
return text;
// 尝试检测UTF-8 BOM
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(text);
// 检查是否有UTF-8 BOM (EF BB BF)
if (bytes.Length >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF)
{
// 去掉BOM
return System.Text.Encoding.UTF8.GetString(bytes, 3, bytes.Length - 3);
}
return text;
}
/// <summary>
/// 创建默认库存(当配置文件不存在时)
/// </summary>
private void CreateDefaultInventory()
{
lock (_lockObject)
{
_inventoryData.inventory.Clear();
_inventoryData.inventory.Add(new InventoryItem("红外测温仪", 99));
_inventoryData.inventory.Add(new InventoryItem("检测试纸-A", 23));
_inventoryData.inventory.Add(new InventoryItem("扳手", 3));
_inventoryData.lastModified = DateTime.Now;
}
Debug.Log("创建默认库存数据");
SaveInventoryData();
}
#endregion
#region
/// <summary>
/// 获取库存中物品种类数量
/// </summary>
/// <returns>库存中不同类型的物品数量</returns>
public int GetItemTypeCount()
{
lock (_lockObject)
{
return _inventoryData.inventory.Count;
}
}
/// <summary>
/// 获取所有物品类型的名称列表
/// </summary>
/// <returns>所有物品类型的名称数组</returns>
public string[] GetAllItemTypeNames()
{
lock (_lockObject)
{
return _inventoryData.inventory.Select(item => item.type).ToArray();
}
}
/// <summary>
/// 获取所有物品类型的详细信息
/// 返回格式:类型名称 -> 数量
/// </summary>
/// <returns>字典,键为类型名称,值为数量</returns>
public Dictionary<string, int> GetAllItemDetails()
{
lock (_lockObject)
{
return _inventoryData.inventory.ToDictionary(
item => item.type,
item => item.remaining
);
}
}
/// <summary>
/// 获取库存概要信息
/// 返回格式:数组,每个元素是元组(类型, 数量)
/// </summary>
public (string type, int quantity)[] GetInventorySummary()
{
lock (_lockObject)
{
return _inventoryData.inventory
.Select(item => (item.type, item.remaining))
.ToArray();
}
}
/// <summary>
/// 获取库存统计信息
/// 包含:种类数量、总数量、平均数量等
/// </summary>
public InventoryStats GetInventoryStatistics()
{
lock (_lockObject)
{
if (_inventoryData.inventory.Count == 0)
{
return new InventoryStats
{
itemTypeCount = 0,
totalQuantity = 0,
averageQuantity = 0,
maxQuantity = 0,
minQuantity = 0
};
}
var quantities = _inventoryData.inventory.Select(item => item.remaining).ToList();
return new InventoryStats
{
itemTypeCount = _inventoryData.inventory.Count,
totalQuantity = quantities.Sum(),
averageQuantity = (int)quantities.Average(),
maxQuantity = quantities.Max(),
minQuantity = quantities.Min(),
lastModified = _inventoryData.lastModified
};
}
}
/// <summary>
/// 获取按数量排序的库存列表
/// </summary>
/// <param name="descending">是否降序排列默认true数量多的在前</param>
/// <returns>排序后的库存列表</returns>
public IReadOnlyList<InventoryItem> GetSortedItems(bool descending = true)
{
lock (_lockObject)
{
if (descending)
{
return _inventoryData.inventory
.OrderByDescending(item => item.remaining)
.ToList()
.AsReadOnly();
}
else
{
return _inventoryData.inventory
.OrderBy(item => item.remaining)
.ToList()
.AsReadOnly();
}
}
}
/// <summary>
/// 获取库存数量范围统计
/// </summary>
/// <param name="threshold">数量阈值</param>
/// <returns>低于阈值、等于阈值、高于阈值的物品数量</returns>
public (int belowThreshold, int atThreshold, int aboveThreshold) GetQuantityStats(int threshold = 10)
{
lock (_lockObject)
{
int below = 0, at = 0, above = 0;
foreach (var item in _inventoryData.inventory)
{
if (item.remaining < threshold) below++;
else if (item.remaining == threshold) at++;
else above++;
}
return (below, at, above);
}
}
/// <summary>
/// 获取指定关键字匹配的物品列表
/// </summary>
/// <param name="keyword">搜索关键字</param>
/// <param name="caseSensitive">是否区分大小写默认false</param>
/// <returns>匹配的物品列表</returns>
public IReadOnlyList<InventoryItem> SearchItems(string keyword, bool caseSensitive = false)
{
if (string.IsNullOrEmpty(keyword))
return _inventoryData.inventory.AsReadOnly();
lock (_lockObject)
{
StringComparison comparison = caseSensitive ?
StringComparison.Ordinal :
StringComparison.OrdinalIgnoreCase;
return _inventoryData.inventory
.Where(item => item.type.IndexOf(keyword, comparison) >= 0)
.ToList()
.AsReadOnly();
}
}
/// <summary>
/// 获取指定类型的物品数量
/// </summary>
/// <param name="type">物品类型</param>
/// <param name="quantity">输出数量如果不存在则返回0</param>
/// <returns>是否找到该物品</returns>
public bool TryGetItemQuantity(string type, out int quantity)
{
lock (_lockObject)
{
var item = _inventoryData.inventory.Find(x => x.type == type);
if (item != null)
{
quantity = item.remaining;
return true;
}
quantity = 0;
return false;
}
}
/// <summary>
/// 更新物品数量接口1直接设置
/// </summary>
/// <param name="type">物品类型</param>
/// <param name="newQuantity">新数量</param>
/// <returns>是否更新成功</returns>
public bool UpdateItemQuantity(string type, int newQuantity)
{
if (newQuantity < 0)
{
Debug.LogWarning($"数量不能为负数: {type}");
return false;
}
lock (_lockObject)
{
var item = _inventoryData.inventory.Find(x => x.type == type);
if (item == null)
{
// 如果没有找到,创建一个新项
_inventoryData.inventory.Add(new InventoryItem(type, newQuantity));
Debug.Log($"添加新物品: {type}, 数量: {newQuantity}");
}
else
{
item.remaining = newQuantity;
}
_inventoryData.lastModified = DateTime.Now;
}
// 保存到文件
SaveInventoryData();
onInventoryUpdated?.Invoke();
return true;
}
/// <summary>
/// 增加物品数量接口2增量修改
/// </summary>
/// <param name="type">物品类型</param>
/// <param name="delta">变化量(可正可负)</param>
/// <returns>修改后的数量,如果失败返回-1</returns>
public int AddItemQuantity(string type, int delta)
{
lock (_lockObject)
{
var item = _inventoryData.inventory.Find(x => x.type == type);
if (item == null && delta < 0)
{
Debug.LogWarning($"尝试减少不存在的物品: {type}");
return -1;
}
if (item == null)
{
// 创建新项
item = new InventoryItem(type, delta);
_inventoryData.inventory.Add(item);
}
else
{
int newQuantity = item.remaining + delta;
if (newQuantity < 0)
{
Debug.LogWarning($"数量不能为负数: {type}, 当前: {item.remaining}, 尝试减少: {delta}");
return -1;
}
item.remaining = newQuantity;
}
_inventoryData.lastModified = DateTime.Now;
int result = item.remaining;
// 异步保存
Task.Run(() => SaveInventoryDataAsync());
onInventoryUpdated?.Invoke();
return result;
}
}
/// <summary>
/// 添加新物品类型
/// </summary>
/// <param name="type">物品类型</param>
/// <param name="initialQuantity">初始数量</param>
/// <returns>是否添加成功</returns>
public bool AddNewItemType(string type, int initialQuantity = 0)
{
if (initialQuantity < 0)
{
Debug.LogWarning($"初始数量不能为负数: {type}");
return false;
}
lock (_lockObject)
{
if (_inventoryData.inventory.Exists(x => x.type == type))
{
Debug.LogWarning($"物品类型已存在: {type}");
return false;
}
_inventoryData.inventory.Add(new InventoryItem(type, initialQuantity));
_inventoryData.lastModified = DateTime.Now;
}
SaveInventoryData();
onInventoryUpdated?.Invoke();
return true;
}
/// <summary>
/// 移除物品类型
/// </summary>
public bool RemoveItemType(string type)
{
lock (_lockObject)
{
int removed = _inventoryData.inventory.RemoveAll(x => x.type == type);
if (removed > 0)
{
_inventoryData.lastModified = DateTime.Now;
SaveInventoryData();
onInventoryUpdated?.Invoke();
return true;
}
}
return false;
}
/// <summary>
/// 导出当前库存为JSON字符串
/// </summary>
public string ExportToJson()
{
lock (_lockObject)
{
return JsonUtility.ToJson(_inventoryData, true);
}
}
/// <summary>
/// 重置为初始配置从StreamingAssets重新加载
/// </summary>
public IEnumerator ResetToDefault()
{
Debug.Log("重置库存为初始配置...");
// 删除可读写路径的文件
if (File.Exists(_persistentFilePath))
{
File.Delete(_persistentFilePath);
}
// 重新初始化
_isInitialized = false;
yield return StartCoroutine(InitializeInventoryAsync(true));
}
#endregion
#region
/// <summary>
/// 保存库存数据到可读写路径
/// </summary>
public void SaveInventoryData()
{
try
{
string json;
lock (_lockObject)
{
json = JsonUtility.ToJson(_inventoryData, true);
}
// 确保目录存在
string directory = Path.GetDirectoryName(_persistentFilePath);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
File.WriteAllText(_persistentFilePath, json);
Debug.Log($"库存数据已保存: {_persistentFilePath}");
}
catch (Exception e)
{
Debug.LogError($"保存库存数据失败: {e.Message}");
}
}
/// <summary>
/// 异步保存库存数据
/// </summary>
private async Task SaveInventoryDataAsync()
{
try
{
string json;
lock (_lockObject)
{
json = JsonUtility.ToJson(_inventoryData, true);
}
string directory = Path.GetDirectoryName(_persistentFilePath);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
await File.WriteAllTextAsync(_persistentFilePath, json);
}
catch (Exception e)
{
Debug.LogError($"异步保存库存数据失败: {e.Message}");
}
}
#endregion
/// <summary>
/// 库存统计信息
/// </summary>
public struct InventoryStats
{
public int itemTypeCount; // 物品种类数量
public int totalQuantity; // 总数量
public int averageQuantity; // 平均数量
public int maxQuantity; // 最大数量
public int minQuantity; // 最小数量
public DateTime lastModified; // 最后修改时间
public override string ToString()
{
return $"种类: {itemTypeCount}, 总数: {totalQuantity}, 平均: {averageQuantity}, 最大: {maxQuantity}, 最小: {minQuantity}, 最后更新: {lastModified:yyyy-MM-dd HH:mm:ss}";
}
}
}
[System.Serializable]
public class InventoryItem
{
public string type; // 物品类型
public int remaining; // 剩余数量
public InventoryItem(string type, int remaining)
{
this.type = type;
this.remaining = remaining;
}
}
[System.Serializable]
public class InventoryData
{
public List<InventoryItem> inventory = new List<InventoryItem>();
public DateTime lastModified; // 最后修改时间
}