MapCacheService.cs 5.99 KB
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Rcs.Application.Services;
using Rcs.Domain.Repositories;
using StackExchange.Redis;

namespace Rcs.Infrastructure.Services
{
    /// <summary>
    /// 地图缓存服务实现 - 负责地图数据的Redis缓存操作
    /// @author zzy
    /// </summary>
    public class MapCacheService : IMapCacheService
    {
        private readonly IConnectionMultiplexer _redis;
        private readonly IServiceProvider _serviceProvider;
        private readonly ILogger<MapCacheService> _logger;

        private const string MapKeyPrefix = "rcs:map:";
        private const string MapListKey = "rcs:maps";
        private const string NodeIndexSuffix = ":nodes";

        public MapCacheService(
            IConnectionMultiplexer redis,
            IServiceProvider serviceProvider,
            ILogger<MapCacheService> logger)
        {
            _redis = redis;
            _serviceProvider = serviceProvider;
            _logger = logger;
        }

        /// <summary>
        /// 从Redis获取地图完整数据
        /// @author zzy
        /// </summary>
        public async Task<MapCacheData?> GetMapAsync(Guid mapId)
        {
            var db = _redis.GetDatabase();
            var key = $"{MapKeyPrefix}{mapId}";
            var json = await db.StringGetAsync(key);

            if (json.IsNullOrEmpty)
            {
                // 缓存未命中,从数据库加载
                await UpdateMapCacheAsync(mapId);
                json = await db.StringGetAsync(key);
                if (json.IsNullOrEmpty) return null;
            }

            return JsonSerializer.Deserialize<MapCacheData>(json!);
        }

        /// <summary>
        /// 获取所有地图ID列表
        /// @author zzy
        /// </summary>
        public async Task<IEnumerable<Guid>> GetAllMapIdsAsync()
        {
            var db = _redis.GetDatabase();
            var json = await db.StringGetAsync(MapListKey);

            if (json.IsNullOrEmpty) return Enumerable.Empty<Guid>();

            var ids = JsonSerializer.Deserialize<List<string>>(json!);
            return ids?.Select(Guid.Parse) ?? Enumerable.Empty<Guid>();
        }

        /// <summary>
        /// 通过节点编码快速查找节点ID
        /// @author zzy
        /// </summary>
        public async Task<Guid?> GetNodeIdByCodeAsync(Guid mapId, string nodeCode)
        {
            var db = _redis.GetDatabase();
            var key = $"{MapKeyPrefix}{mapId}{NodeIndexSuffix}";
            var nodeIdStr = await db.HashGetAsync(key, nodeCode);

            if (nodeIdStr.IsNullOrEmpty) return null;
            return Guid.TryParse(nodeIdStr, out var nodeId) ? nodeId : null;
        }

        /// <summary>
        /// 更新单个地图缓存
        /// @author zzy
        /// </summary>
        public async Task UpdateMapCacheAsync(Guid mapId)
        {
            using var scope = _serviceProvider.CreateScope();
            var mapRepo = scope.ServiceProvider.GetRequiredService<IMapRepository>();
            var db = _redis.GetDatabase();

            var map = await mapRepo.GetWithFullDetailsAsync(mapId);
            if (map == null) return;

            var key = $"{MapKeyPrefix}{mapId}";
            var cacheData = new MapCacheData
            {
                MapId = map.MapId,
                MapCode = map.MapCode,
                MapName = map.MapName,
                MapType = (int)map.MapType,
                Version = map.Version,
                Active = map.Active,
                Nodes = map.MapNodes.Select(n => new MapNodeCache
                {
                    NodeId = n.NodeId,
                    NodeCode = n.NodeCode,
                    X = n.X,
                    Y = n.Y,
                    Theta = n.Theta,
                    Type = (int)n.Type,
                    Active = n.Active
                }).ToList(),
                Edges = map.MapEdges.Select(e => new MapEdgeCache
                {
                    EdgeId = e.EdgeId,
                    EdgeCode = e.EdgeCode,
                    FromNode = e.FromNode,
                    ToNode = e.ToNode,
                    Length = e.Length,
                    Cost = e.Cost,
                    Active = e.Active
                }).ToList(),
                Resources = map.MapResources.Select(r => new MapResourceCache
                {
                    ResourceId = r.ResourceId,
                    ResourceCode = r.ResourceCode,
                    Type = (int)r.Type,
                    Capacity = r.Capacity
                }).ToList()
            };

            await db.StringSetAsync(key, JsonSerializer.Serialize(cacheData));

            // 更新节点索引
            var nodeIndexKey = $"{MapKeyPrefix}{mapId}{NodeIndexSuffix}";
            var nodeEntries = map.MapNodes.Select(n => new HashEntry(n.NodeCode, n.NodeId.ToString())).ToArray();
            if (nodeEntries.Length > 0)
            {
                await db.KeyDeleteAsync(nodeIndexKey);
                await db.HashSetAsync(nodeIndexKey, nodeEntries);
            }

            _logger.LogDebug("[地图缓存] 已更新地图 {MapId} 缓存", mapId);
        }

        /// <summary>
        /// 删除地图缓存
        /// @author zzy
        /// </summary>
        public async Task RemoveMapCacheAsync(Guid mapId)
        {
            var db = _redis.GetDatabase();
            var key = $"{MapKeyPrefix}{mapId}";
            var nodeIndexKey = $"{MapKeyPrefix}{mapId}{NodeIndexSuffix}";

            await db.KeyDeleteAsync(key);
            await db.KeyDeleteAsync(nodeIndexKey);

            // 更新地图列表
            var mapIds = (await GetAllMapIdsAsync()).Where(id => id != mapId).Select(id => id.ToString()).ToList();
            await db.StringSetAsync(MapListKey, JsonSerializer.Serialize(mapIds));

            _logger.LogDebug("[地图缓存] 已删除地图 {MapId} 缓存", mapId);
        }
    }
}