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;
///
/// 库存管理器 - 单例模式
///
public class InventoryManager : MonoSingleton
{
[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 属性
///
/// 获取所有库存项(只读)
///
public IReadOnlyList AllItems
{
get
{
lock (_lockObject)
{
return _inventoryData.inventory.AsReadOnly();
}
}
}
///
/// 库存是否已加载
///
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 初始化与加载
///
/// 异步初始化库存系统
///
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();
}
///
/// 从StreamingAssets加载(跨平台兼容)
///
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(json);
}
Debug.Log($"从StreamingAssets加载成功,共 {_inventoryData.inventory.Count} 个种类");
SaveInventoryData(); // 保存到可读写路径
}
catch (System.Exception e)
{
Debug.LogError($"JSON解析失败: {e.Message}");
Debug.Log($"原始JSON内容: {json}");
CreateDefaultInventory();
}
}
}
///
/// 检测并修复编码问题
///
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;
}
///
/// 创建默认库存(当配置文件不存在时)
///
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 公开接口
///
/// 获取库存中物品种类数量
///
/// 库存中不同类型的物品数量
public int GetItemTypeCount()
{
lock (_lockObject)
{
return _inventoryData.inventory.Count;
}
}
///
/// 获取所有物品类型的名称列表
///
/// 所有物品类型的名称数组
public string[] GetAllItemTypeNames()
{
lock (_lockObject)
{
return _inventoryData.inventory.Select(item => item.type).ToArray();
}
}
///
/// 获取所有物品类型的详细信息
/// 返回格式:类型名称 -> 数量
///
/// 字典,键为类型名称,值为数量
public Dictionary GetAllItemDetails()
{
lock (_lockObject)
{
return _inventoryData.inventory.ToDictionary(
item => item.type,
item => item.remaining
);
}
}
///
/// 获取库存概要信息
/// 返回格式:数组,每个元素是元组(类型, 数量)
///
public (string type, int quantity)[] GetInventorySummary()
{
lock (_lockObject)
{
return _inventoryData.inventory
.Select(item => (item.type, item.remaining))
.ToArray();
}
}
///
/// 获取库存统计信息
/// 包含:种类数量、总数量、平均数量等
///
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
};
}
}
///
/// 获取按数量排序的库存列表
///
/// 是否降序排列(默认true,数量多的在前)
/// 排序后的库存列表
public IReadOnlyList 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();
}
}
}
///
/// 获取库存数量范围统计
///
/// 数量阈值
/// 低于阈值、等于阈值、高于阈值的物品数量
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);
}
}
///
/// 获取指定关键字匹配的物品列表
///
/// 搜索关键字
/// 是否区分大小写(默认false)
/// 匹配的物品列表
public IReadOnlyList 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();
}
}
///
/// 获取指定类型的物品数量
///
/// 物品类型
/// 输出:数量,如果不存在则返回0
/// 是否找到该物品
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;
}
}
///
/// 更新物品数量(接口1:直接设置)
///
/// 物品类型
/// 新数量
/// 是否更新成功
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;
}
///
/// 增加物品数量(接口2:增量修改)
///
/// 物品类型
/// 变化量(可正可负)
/// 修改后的数量,如果失败返回-1
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;
}
}
///
/// 添加新物品类型
///
/// 物品类型
/// 初始数量
/// 是否添加成功
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;
}
///
/// 移除物品类型
///
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;
}
///
/// 导出当前库存为JSON字符串
///
public string ExportToJson()
{
lock (_lockObject)
{
return JsonUtility.ToJson(_inventoryData, true);
}
}
///
/// 重置为初始配置(从StreamingAssets重新加载)
///
public IEnumerator ResetToDefault()
{
Debug.Log("重置库存为初始配置...");
// 删除可读写路径的文件
if (File.Exists(_persistentFilePath))
{
File.Delete(_persistentFilePath);
}
// 重新初始化
_isInitialized = false;
yield return StartCoroutine(InitializeInventoryAsync(true));
}
#endregion
#region 保存与持久化
///
/// 保存库存数据到可读写路径
///
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}");
}
}
///
/// 异步保存库存数据
///
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
///
/// 库存统计信息
///
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 inventory = new List();
public DateTime lastModified; // 最后修改时间
}