wanshenmean
2026-03-02 bfd4fd8e4a05a681ec10a47992294cf752a764c4
添加Redis服务与缓存增强

引入新的Redis服务项目,并在整个解决方案中扩展缓存功能。新增WIDESEAWCS_RedisService,包含众多组件(位图、布隆过滤器、混合/Redis缓存、连接管理器、配置中心、计数器、延迟队列、地理空间、ID生成器、排行榜、分布式锁、监控、选项配置、发布/订阅、限流、序列化、会话、对象存储、流式处理及服务设置)。通过ConcurrentDictionary风格的方法(TryAdd/TryGetValue/TryRemove/TryUpdate/GetOrAdd重载)扩展ICacheService接口,并在MemoryCacheService中实现这些方法。更新HttpClientHelper以依赖ICacheService并利用缓存处理URL,同时在RouterService中注入缓存以缓存设备位置。此外,更新了解决方案/项目文件、Program.cs和appsettings.json,并添加了一些任务/控制器/源文件以及Visual Studio文档布局条目。
已添加40个文件
已修改17个文件
3723 ■■■■■ 文件已修改
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/v18/DocumentLayout.json 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Core/Caches/ICacheService.cs 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Core/Caches/MemoryCacheService.cs 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Core/Http/HTTP/HttpClientHelper.cs 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/Service/RouterService.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Bitmap/IBitmapService.cs 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Bitmap/RedisBitmapService.cs 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Bitmap/RedisBloomFilterService.cs 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Cache/HybridCacheService.cs 266 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Cache/RedisCacheService.cs 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Configuration/IConfigurationCenterService.cs 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Configuration/RedisConfigurationCenterService.cs 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Connection/IRedisConnectionManager.cs 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Connection/RedisConnectionManager.cs 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Counter/ICounterService.cs 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Counter/RedisCounterService.cs 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/DelayQueue/IDelayQueueService.cs 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/DelayQueue/RedisDelayQueueService.cs 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Extensions/RedisServiceSetup.cs 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Geo/IGeoLocationService.cs 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Geo/RedisGeoLocationService.cs 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/IdGenerator/IDistributedIdGenerator.cs 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/IdGenerator/RedisDistributedIdGenerator.cs 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Leaderboard/ILeaderboardService.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Leaderboard/RedisLeaderboardService.cs 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Lock/IDistributedLockService.cs 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Lock/RedisDistributedLockService.cs 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Monitoring/IRedisMonitorService.cs 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Monitoring/RedisMonitorService.cs 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Options/RedisOptions.cs 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/PubSub/IMessageQueueService.cs 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/PubSub/RedisMessageQueueService.cs 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/RateLimiting/IRateLimitingService.cs 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/RateLimiting/RedisRateLimitingService.cs 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Serialization/IRedisSerializer.cs 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Serialization/NewtonsoftRedisSerializer.cs 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Session/ISessionStorage.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Session/RedisSessionStorage.cs 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Storage/IObjectStorageService.cs 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Storage/RedisObjectStorageService.cs 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Stream/IStreamProcessingService.cs 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Stream/RedisStreamProcessingService.cs 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/WIDESEAWCS_RedisService.csproj 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server.sln 178 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Program.cs 44 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Redis使用案例.md 380 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/WIDESEAWCS_Server.csproj 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/appsettings.json 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/CommonConveyorLineNewJob.cs 47 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLine/CheckPalletPosition.cs 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotJob.cs 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/v18/DocumentLayout.backup.json 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/v18/DocumentLayout.json 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_Common/StockEnum/StockStatusEmun.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_ITaskInfoService/ITaskService.cs 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/.vs/WIDESEAWCS_Server/v18/DocumentLayout.json
@@ -5,6 +5,50 @@
    {
      "AbsoluteMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_tasks\\robotjob\\robotjob.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|solutionrelative:wideseawcs_tasks\\robotjob\\robotjob.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_tasks\\socketserver\\tcpsocketserver.clients.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|solutionrelative:wideseawcs_tasks\\socketserver\\tcpsocketserver.clients.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{F9886971-C3B2-4334-B014-D5109F2041DE}|WIDESEAWCS_RedisService\\WIDESEAWCS_RedisService.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_redisservice\\cache\\hybridcacheservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{F9886971-C3B2-4334-B014-D5109F2041DE}|WIDESEAWCS_RedisService\\WIDESEAWCS_RedisService.csproj|solutionrelative:wideseawcs_redisservice\\cache\\hybridcacheservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{F9886971-C3B2-4334-B014-D5109F2041DE}|WIDESEAWCS_RedisService\\WIDESEAWCS_RedisService.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_redisservice\\connection\\redisconnectionmanager.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{F9886971-C3B2-4334-B014-D5109F2041DE}|WIDESEAWCS_RedisService\\WIDESEAWCS_RedisService.csproj|solutionrelative:wideseawcs_redisservice\\connection\\redisconnectionmanager.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{487FA45B-EA1A-4ACA-BB5B-0F6708F462C0}|WIDESEAWCS_Server\\WIDESEAWCS_Server.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_server\\appsettings.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}",
      "RelativeMoniker": "D:0:0:{487FA45B-EA1A-4ACA-BB5B-0F6708F462C0}|WIDESEAWCS_Server\\WIDESEAWCS_Server.csproj|solutionrelative:wideseawcs_server\\appsettings.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{F9886971-C3B2-4334-B014-D5109F2041DE}|WIDESEAWCS_RedisService\\WIDESEAWCS_RedisService.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_redisservice\\extensions\\redisservicesetup.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{F9886971-C3B2-4334-B014-D5109F2041DE}|WIDESEAWCS_RedisService\\WIDESEAWCS_RedisService.csproj|solutionrelative:wideseawcs_redisservice\\extensions\\redisservicesetup.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{F9886971-C3B2-4334-B014-D5109F2041DE}|WIDESEAWCS_RedisService\\WIDESEAWCS_RedisService.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_redisservice\\cache\\rediscacheservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{F9886971-C3B2-4334-B014-D5109F2041DE}|WIDESEAWCS_RedisService\\WIDESEAWCS_RedisService.csproj|solutionrelative:wideseawcs_redisservice\\cache\\rediscacheservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{487FA45B-EA1A-4ACA-BB5B-0F6708F462C0}|WIDESEAWCS_Server\\WIDESEAWCS_Server.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_server\\redis\u4F7F\u7528\u6848\u4F8B.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}",
      "RelativeMoniker": "D:0:0:{487FA45B-EA1A-4ACA-BB5B-0F6708F462C0}|WIDESEAWCS_Server\\WIDESEAWCS_Server.csproj|solutionrelative:wideseawcs_server\\redis\u4F7F\u7528\u6848\u4F8B.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{487FA45B-EA1A-4ACA-BB5B-0F6708F462C0}|WIDESEAWCS_Server\\WIDESEAWCS_Server.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_server\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{487FA45B-EA1A-4ACA-BB5B-0F6708F462C0}|WIDESEAWCS_Server\\WIDESEAWCS_Server.csproj|solutionrelative:wideseawcs_server\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{BFFDD936-2E61-4D3A-ABFE-7CF77FE0B184}|WIDESEAWCS_Core\\WIDESEAWCS_Core.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_core\\http\\http\\httpclienthelper.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{BFFDD936-2E61-4D3A-ABFE-7CF77FE0B184}|WIDESEAWCS_Core\\WIDESEAWCS_Core.csproj|solutionrelative:wideseawcs_core\\http\\http\\httpclienthelper.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_tasks\\conveyorlinenewjob\\conveyorline\\checkpalletposition.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|solutionrelative:wideseawcs_tasks\\conveyorlinenewjob\\conveyorline\\checkpalletposition.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|d:\\git\\shanmeixinnengyuan\\code\\wcs\\wideseawcs_server\\wideseawcs_tasks\\conveyorlinenewjob\\commonconveyorlinenewjob.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{294E4915-0241-4C8C-BA99-7588B945863A}|WIDESEAWCS_Tasks\\WIDESEAWCS_Tasks.csproj|solutionrelative:wideseawcs_tasks\\conveyorlinenewjob\\commonconveyorlinenewjob.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    }
  ],
  "DocumentGroupContainers": [
@@ -14,7 +58,7 @@
      "DocumentGroups": [
        {
          "DockedWidth": 200,
          "SelectedChildIndex": 3,
          "SelectedChildIndex": 14,
          "Children": [
            {
              "$type": "Bookmark",
@@ -30,13 +74,156 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 1,
              "Title": "TcpSocketServer.Clients.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Clients.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Clients.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Clients.cs",
              "RelativeToolTip": "WIDESEAWCS_Tasks\\SocketServer\\TcpSocketServer.Clients.cs",
              "ViewState": "AgIAAEkAAAAAAAAAAAAIwAkAAAAZAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-03-02T04:08:38.428Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 3,
              "Title": "RedisConnectionManager.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_RedisService\\Connection\\RedisConnectionManager.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_RedisService\\Connection\\RedisConnectionManager.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_RedisService\\Connection\\RedisConnectionManager.cs",
              "RelativeToolTip": "WIDESEAWCS_RedisService\\Connection\\RedisConnectionManager.cs",
              "ViewState": "AgIAAD0AAAAAAAAAAAAuwE8AAAAIAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-03-02T03:52:53.567Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 2,
              "Title": "HybridCacheService.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_RedisService\\Cache\\HybridCacheService.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_RedisService\\Cache\\HybridCacheService.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_RedisService\\Cache\\HybridCacheService.cs",
              "RelativeToolTip": "WIDESEAWCS_RedisService\\Cache\\HybridCacheService.cs",
              "ViewState": "AgIAAOkAAAAAAAAAAAAqwPoAAAAjAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-03-02T03:39:18.734Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 5,
              "Title": "RedisServiceSetup.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_RedisService\\Extensions\\RedisServiceSetup.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_RedisService\\Extensions\\RedisServiceSetup.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_RedisService\\Extensions\\RedisServiceSetup.cs",
              "RelativeToolTip": "WIDESEAWCS_RedisService\\Extensions\\RedisServiceSetup.cs",
              "ViewState": "AgIAADgAAAAAAAAAAAAmwCIAAAAMAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-03-02T03:33:54.548Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 8,
              "Title": "Program.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Server\\Program.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Server\\Program.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Server\\Program.cs",
              "RelativeToolTip": "WIDESEAWCS_Server\\Program.cs",
              "ViewState": "AgIAACQAAAAAAAAAAAAqwDYAAAAeAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-03-02T03:16:21.765Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 6,
              "Title": "RedisCacheService.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_RedisService\\Cache\\RedisCacheService.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_RedisService\\Cache\\RedisCacheService.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_RedisService\\Cache\\RedisCacheService.cs",
              "RelativeToolTip": "WIDESEAWCS_RedisService\\Cache\\RedisCacheService.cs",
              "ViewState": "AgIAABoAAAAAAAAAAAAmwCkAAAAoAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-03-02T03:06:06.952Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 7,
              "Title": "Redis\u4F7F\u7528\u6848\u4F8B.md",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Server\\Redis\u4F7F\u7528\u6848\u4F8B.md",
              "RelativeDocumentMoniker": "WIDESEAWCS_Server\\Redis\u4F7F\u7528\u6848\u4F8B.md",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Server\\Redis\u4F7F\u7528\u6848\u4F8B.md",
              "RelativeToolTip": "WIDESEAWCS_Server\\Redis\u4F7F\u7528\u6848\u4F8B.md",
              "ViewState": "AgIAAAgBAAAAAAAAAAAAABcBAAAPAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001818|",
              "WhenOpened": "2026-03-02T02:59:29.028Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 9,
              "Title": "HttpClientHelper.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Core\\Http\\HTTP\\HttpClientHelper.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Core\\Http\\HTTP\\HttpClientHelper.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Core\\Http\\HTTP\\HttpClientHelper.cs",
              "RelativeToolTip": "WIDESEAWCS_Core\\Http\\HTTP\\HttpClientHelper.cs",
              "ViewState": "AgIAABQAAAAAAAAAAADwvygAAABLAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-03-02T02:58:50.075Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 10,
              "Title": "CheckPalletPosition.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\ConveyorLineNewJob\\ConveyorLine\\CheckPalletPosition.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Tasks\\ConveyorLineNewJob\\ConveyorLine\\CheckPalletPosition.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\ConveyorLineNewJob\\ConveyorLine\\CheckPalletPosition.cs",
              "RelativeToolTip": "WIDESEAWCS_Tasks\\ConveyorLineNewJob\\ConveyorLine\\CheckPalletPosition.cs",
              "ViewState": "AgIAAAAAAAAAAAAAAAAAAAYAAAAeAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-28T08:42:47.236Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 4,
              "Title": "appsettings.json",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Server\\appsettings.json",
              "RelativeDocumentMoniker": "WIDESEAWCS_Server\\appsettings.json",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Server\\appsettings.json",
              "RelativeToolTip": "WIDESEAWCS_Server\\appsettings.json",
              "ViewState": "AgIAACUAAAAAAAAAAAAYwCsAAAAeAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001642|",
              "WhenOpened": "2026-02-28T05:50:10.851Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 11,
              "Title": "CommonConveyorLineNewJob.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\ConveyorLineNewJob\\CommonConveyorLineNewJob.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Tasks\\ConveyorLineNewJob\\CommonConveyorLineNewJob.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\ConveyorLineNewJob\\CommonConveyorLineNewJob.cs",
              "RelativeToolTip": "WIDESEAWCS_Tasks\\ConveyorLineNewJob\\CommonConveyorLineNewJob.cs",
              "ViewState": "AgIAAEoAAAAAAAAAAAAAwGAAAABTAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-28T05:48:06.526Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 0,
              "Title": "RobotJob.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\RobotJob\\RobotJob.cs",
              "RelativeDocumentMoniker": "WIDESEAWCS_Tasks\\RobotJob\\RobotJob.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WCS\\WIDESEAWCS_Server\\WIDESEAWCS_Tasks\\RobotJob\\RobotJob.cs",
              "RelativeToolTip": "WIDESEAWCS_Tasks\\RobotJob\\RobotJob.cs",
              "ViewState": "AgIAAAEAAAAAAAAAAAAuwBoAAAAAAAAAAAAAAA==",
              "ViewState": "AgIAADgAAAAAAAAAAAAtwEIAAAAPAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-05T05:38:04.031Z",
              "EditorCaption": ""
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Core/Caches/ICacheService.cs
@@ -60,5 +60,54 @@
        /// <param name="key">缓存Key</param>
        /// <returns></returns>
        string? Get(string key);
        #region ConcurrentDictionary风格方法
        /// <summary>
        /// å°è¯•添加,仅当Key不存在时添加成功
        /// </summary>
        bool TryAdd(string key, string value, int expireSeconds = -1);
        /// <summary>
        /// å°è¯•添加对象,仅当Key不存在时添加成功
        /// </summary>
        bool TryAdd<T>(string key, T value, int expireSeconds = -1) where T : class;
        /// <summary>
        /// å°è¯•获取值,返回是否存在
        /// </summary>
        bool TryGetValue(string key, out string? value);
        /// <summary>
        /// å°è¯•获取对象,返回是否存在
        /// </summary>
        bool TryGetValue<T>(string key, out T? value) where T : class;
        /// <summary>
        /// å°è¯•移除并返回被移除的值
        /// </summary>
        bool TryRemove(string key, out string? value);
        /// <summary>
        /// å°è¯•更新,仅当Key存在时更新
        /// </summary>
        bool TryUpdate(string key, string newValue, int expireSeconds = -1);
        /// <summary>
        /// èŽ·å–æˆ–æ·»åŠ ï¼šKey存在则返回现有值,不存在则添加并返回新值
        /// </summary>
        string GetOrAdd(string key, string value, int expireSeconds = -1);
        /// <summary>
        /// èŽ·å–æˆ–æ·»åŠ ï¼ˆå·¥åŽ‚æ–¹æ³•ï¼‰ï¼šKey存在则返回现有值,不存在则通过工厂方法生成值并添加
        /// </summary>
        string GetOrAdd(string key, Func<string, string> valueFactory, int expireSeconds = -1);
        /// <summary>
        /// èŽ·å–æˆ–æ·»åŠ å¯¹è±¡
        /// </summary>
        T GetOrAdd<T>(string key, Func<string, T> valueFactory, int expireSeconds = -1) where T : class;
        #endregion
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Core/Caches/MemoryCacheService.cs
@@ -144,5 +144,80 @@
            keys.ToList().ForEach(item => _cache.Remove(item));
        }
        public bool TryAdd(string key, string value, int expireSeconds = -1)
        {
            if (Exists(key)) return false;
            return Add(key, value, expireSeconds);
        }
        public bool TryAdd<T>(string key, T value, int expireSeconds = -1) where T : class
        {
            if (Exists(key)) return false;
            return AddObject(key, value, expireSeconds);
        }
        public bool TryGetValue(string key, out string? value)
        {
            value = _cache.Get(key)?.ToString();
            return value != null;
        }
        public bool TryGetValue<T>(string key, out T? value) where T : class
        {
            if (_cache.TryGetValue(key, out object? obj) && obj != null)
            {
                value = obj as T ?? JsonConvert.DeserializeObject<T>(obj.ToString() ?? "");
                return value != null;
            }
            value = default;
            return false;
        }
        public bool TryRemove(string key, out string? value)
        {
            value = _cache.Get(key)?.ToString();
            if (value == null) return false;
            _cache.Remove(key);
            return true;
        }
        public bool TryUpdate(string key, string newValue, int expireSeconds = -1)
        {
            if (!Exists(key)) return false;
            Remove(key);
            Add(key, newValue, expireSeconds);
            return true;
        }
        public string GetOrAdd(string key, string value, int expireSeconds = -1)
        {
            var existing = _cache.Get(key)?.ToString();
            if (existing != null) return existing;
            Add(key, value, expireSeconds);
            return value;
        }
        public string GetOrAdd(string key, Func<string, string> valueFactory, int expireSeconds = -1)
        {
            var existing = _cache.Get(key)?.ToString();
            if (existing != null) return existing;
            var value = valueFactory(key);
            Add(key, value, expireSeconds);
            return value;
        }
        public T GetOrAdd<T>(string key, Func<string, T> valueFactory, int expireSeconds = -1) where T : class
        {
            if (_cache.TryGetValue(key, out object? obj) && obj != null)
            {
                if (obj is T t) return t;
                var deserialized = JsonConvert.DeserializeObject<T>(obj.ToString() ?? "");
                if (deserialized != null) return deserialized;
            }
            var value = valueFactory(key);
            AddObject(key, value, expireSeconds);
            return value;
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Core/Http/HTTP/HttpClientHelper.cs
@@ -10,16 +10,19 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using WIDESEAWCS_Core.Caches;
namespace WIDESEA_Core
{
    public class HttpClientHelper
    {
        private readonly IHttpClientFactory _httpClientFactory;
        private readonly ICacheService _cache;
        public HttpClientHelper(IHttpClientFactory httpClientFactory, IConfiguration configuration = null)
        public HttpClientHelper(IHttpClientFactory httpClientFactory, ICacheService cache, IConfiguration configuration = null)
        {
            _httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
            _cache = cache ?? throw new ArgumentNullException(nameof(cache));
        }
        /// <summary>
@@ -32,6 +35,7 @@
        /// <returns></returns>
        public HttpResponseResult Post(string url, string content, string contentType = "application/json", HttpRequestConfig? config = null)
        {
            url = _cache.Get(url);
            HttpResponseResult httpResponseResult = ExecuteAsync(async (client) =>
            {
                var request = new HttpRequestMessage(HttpMethod.Post, url);
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/Service/RouterService.cs
@@ -11,6 +11,7 @@
using WIDESEAWCS_DTO.BasicInfo;
using WIDESEAWCS_QuartzJob.Models;
using WIDESEAWCS_QuartzJob.Repository;
using ICacheService = WIDESEAWCS_Core.Caches.ICacheService;
namespace WIDESEAWCS_QuartzJob.Service
{
@@ -21,6 +22,7 @@
    {
        private readonly IDeviceProtocolRepository _deviceProtocolRepository;
        private readonly IDeviceInfoRepository _deviceInfoRepository;
        private readonly ICacheService _cacheService;
        /// <summary>
        /// è·¯ç”±é…ç½®ä¸šåС层
@@ -28,10 +30,11 @@
        /// <param name="BaseDal"></param>
        /// <param name="deviceProtocolRepository"></param>
        /// <param name="deviceInfoRepository"></param>
        public RouterService(IRouterRepository BaseDal, IDeviceProtocolRepository deviceProtocolRepository, IDeviceInfoRepository deviceInfoRepository) : base(BaseDal)
        public RouterService(IRouterRepository BaseDal, IDeviceProtocolRepository deviceProtocolRepository, IDeviceInfoRepository deviceInfoRepository, ICacheService cacheService) : base(BaseDal)
        {
            _deviceProtocolRepository = deviceProtocolRepository;
            _deviceInfoRepository = deviceInfoRepository;
            _cacheService = cacheService;
        }
        /// <summary>
@@ -327,6 +330,10 @@
        {
            // åˆ›å»ºä¸€ä¸ªå­—符串列表,用于存储所有位置
            List<string> positions = new List<string>();
            var device = _cacheService.Get<List<string>>($"DevicePositions:{deviceCode}");
            if (device.IsNullOrEmpty())
            {
            try
            {
                // æŸ¥è¯¢æ‰€æœ‰è¿›å…¥è·¯ç”±å™¨çš„位置
@@ -345,6 +352,9 @@
            {
            }
            }
            else
                positions = device;
            // è¿”回位置列表
            return positions;
        }
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Bitmap/IBitmapService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
namespace WIDESEAWCS_RedisService.Bitmap
{
    public interface IBitmapService
    {
        bool SetBit(string key, long offset, bool value);
        bool GetBit(string key, long offset);
        long BitCount(string key);
        long BitCount(string key, long start, long end);
    }
    public interface IBloomFilterService
    {
        /// <summary>
        /// æ·»åŠ å…ƒç´ åˆ°å¸ƒéš†è¿‡æ»¤å™¨
        /// </summary>
        void Add(string filterName, string value);
        /// <summary>
        /// æ£€æŸ¥å…ƒç´ æ˜¯å¦å¯èƒ½å­˜åœ¨
        /// </summary>
        bool MayExist(string filterName, string value);
        /// <summary>
        /// æ‰¹é‡æ·»åŠ 
        /// </summary>
        void AddRange(string filterName, IEnumerable<string> values);
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Bitmap/RedisBitmapService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,46 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using WIDESEAWCS_RedisService.Connection;
using WIDESEAWCS_RedisService.Options;
namespace WIDESEAWCS_RedisService.Bitmap
{
    public class RedisBitmapService : IBitmapService
    {
        private readonly IRedisConnectionManager _connectionManager;
        private readonly RedisOptions _options;
        private readonly ILogger<RedisBitmapService> _logger;
        public RedisBitmapService(
            IRedisConnectionManager connectionManager,
            IOptions<RedisOptions> options,
            ILogger<RedisBitmapService> logger)
        {
            _connectionManager = connectionManager;
            _options = options.Value;
            _logger = logger;
        }
        private string BuildKey(string key) => $"{_options.KeyPrefix}bitmap:{key}";
        public bool SetBit(string key, long offset, bool value)
        {
            return _connectionManager.GetDatabase().StringSetBit(BuildKey(key), offset, value);
        }
        public bool GetBit(string key, long offset)
        {
            return _connectionManager.GetDatabase().StringGetBit(BuildKey(key), offset);
        }
        public long BitCount(string key)
        {
            return _connectionManager.GetDatabase().StringBitCount(BuildKey(key));
        }
        public long BitCount(string key, long start, long end)
        {
            return _connectionManager.GetDatabase().StringBitCount(BuildKey(key), start, end);
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Bitmap/RedisBloomFilterService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,96 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using WIDESEAWCS_RedisService.Connection;
using WIDESEAWCS_RedisService.Options;
using System.Text;
namespace WIDESEAWCS_RedisService.Bitmap
{
    public class RedisBloomFilterService : IBloomFilterService
    {
        private readonly IRedisConnectionManager _connectionManager;
        private readonly RedisOptions _options;
        private readonly ILogger<RedisBloomFilterService> _logger;
        private const int HashCount = 5;
        private const long BitSize = 1_000_000;
        public RedisBloomFilterService(
            IRedisConnectionManager connectionManager,
            IOptions<RedisOptions> options,
            ILogger<RedisBloomFilterService> logger)
        {
            _connectionManager = connectionManager;
            _options = options.Value;
            _logger = logger;
        }
        private string BuildKey(string key) => $"{_options.KeyPrefix}bloom:{key}";
        public void Add(string filterName, string value)
        {
            var db = _connectionManager.GetDatabase();
            var fullKey = BuildKey(filterName);
            var offsets = GetHashOffsets(value);
            foreach (var offset in offsets)
                db.StringSetBit(fullKey, offset, true);
        }
        public bool MayExist(string filterName, string value)
        {
            var db = _connectionManager.GetDatabase();
            var fullKey = BuildKey(filterName);
            var offsets = GetHashOffsets(value);
            return offsets.All(offset => db.StringGetBit(fullKey, offset));
        }
        public void AddRange(string filterName, IEnumerable<string> values)
        {
            foreach (var value in values) Add(filterName, value);
        }
        private long[] GetHashOffsets(string value)
        {
            var offsets = new long[HashCount];
            var bytes = Encoding.UTF8.GetBytes(value);
            for (int i = 0; i < HashCount; i++)
            {
                uint hash = MurmurHash(bytes, (uint)i);
                offsets[i] = hash % BitSize;
            }
            return offsets;
        }
        private static uint MurmurHash(byte[] data, uint seed)
        {
            const uint c1 = 0xcc9e2d51;
            const uint c2 = 0x1b873593;
            uint h1 = seed;
            int len = data.Length;
            int blocks = len / 4;
            for (int i = 0; i < blocks; i++)
            {
                uint k1 = BitConverter.ToUInt32(data, i * 4);
                k1 *= c1; k1 = RotateLeft(k1, 15); k1 *= c2;
                h1 ^= k1; h1 = RotateLeft(h1, 13); h1 = h1 * 5 + 0xe6546b64;
            }
            uint tail = 0;
            int tailIdx = blocks * 4;
            switch (len & 3)
            {
                case 3: tail ^= (uint)data[tailIdx + 2] << 16; goto case 2;
                case 2: tail ^= (uint)data[tailIdx + 1] << 8; goto case 1;
                case 1: tail ^= data[tailIdx]; tail *= c1; tail = RotateLeft(tail, 15); tail *= c2; h1 ^= tail; break;
            }
            h1 ^= (uint)len;
            h1 ^= h1 >> 16; h1 *= 0x85ebca6b;
            h1 ^= h1 >> 13; h1 *= 0xc2b2ae35;
            h1 ^= h1 >> 16;
            return h1;
        }
        private static uint RotateLeft(uint x, byte r) => (x << r) | (x >> (32 - r));
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Cache/HybridCacheService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,266 @@
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using WIDESEAWCS_Core.Caches;
using WIDESEAWCS_RedisService.Connection;
using WIDESEAWCS_RedisService.Options;
using WIDESEAWCS_RedisService.Serialization;
namespace WIDESEAWCS_RedisService.Cache
{
    public class HybridCacheService : ICacheService
    {
        private readonly IMemoryCache _memoryCache;
        private readonly IRedisConnectionManager _connectionManager;
        private readonly IRedisSerializer _serializer;
        private readonly RedisOptions _options;
        private readonly ILogger<HybridCacheService> _logger;
        private bool _disposed;
        private bool RedisAvailable => _connectionManager.IsConnected;
        public HybridCacheService(
            IMemoryCache memoryCache,
            IRedisConnectionManager connectionManager,
            IRedisSerializer serializer,
            IOptions<RedisOptions> options,
            ILogger<HybridCacheService> logger)
        {
            _memoryCache = memoryCache;
            _connectionManager = connectionManager;
            _serializer = serializer;
            _options = options.Value;
            _logger = logger;
        }
        private string BuildKey(string key) => $"{_options.KeyPrefix}{key}";
        public bool Exists(string key)
        {
            if (_memoryCache.TryGetValue(BuildKey(key), out _)) return true;
            if (!RedisAvailable) return false;
            try
            {
                return _connectionManager.GetDatabase().KeyExists(BuildKey(key));
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "Redis Exists失败, key={Key}", key);
                return false;
            }
        }
        public bool Add(string key, string value, int expireSeconds = -1, bool isSliding = false)
        {
            var fullKey = BuildKey(key);
            SetMemoryCache(fullKey, value, expireSeconds, isSliding);
            if (!RedisAvailable)
            {
                _logger.LogWarning("Redis不可用,仅使用内存缓存, key={Key}", key);
                return true;
            }
            try
            {
                var expiry = expireSeconds > 0 ? TimeSpan.FromSeconds(expireSeconds) : (TimeSpan?)null;
                var result = _connectionManager.GetDatabase().StringSet(fullKey, value, expiry);
                _logger.LogInformation("Redis写入成功: key={Key}, result={Result}", fullKey, result);
                return result;
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "Redis Add失败, key={Key}", key);
                return _options.FallbackToMemory;
            }
        }
        public bool AddObject(string key, object value, int expireSeconds = -1, bool isSliding = false)
        {
            return Add(key, _serializer.Serialize(value), expireSeconds, isSliding);
        }
        public void AddOrUpdate(string key, string value, int expireSeconds = -1, bool isSliding = false)
        {
            Add(key, value, expireSeconds, isSliding);
        }
        public void AddOrUpdate(string key, object value, int expireSeconds = -1, bool isSliding = false)
        {
            AddObject(key, value, expireSeconds, isSliding);
        }
        public bool Remove(string key)
        {
            var fullKey = BuildKey(key);
            _memoryCache.Remove(fullKey);
            if (!RedisAvailable) return true;
            try
            {
                return _connectionManager.GetDatabase().KeyDelete(fullKey);
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "Redis Remove失败, key={Key}", key);
                return true;
            }
        }
        public void Remove(IEnumerable<string> keys)
        {
            foreach (var key in keys) Remove(key);
        }
        public T? Get<T>(string key) where T : class
        {
            var fullKey = BuildKey(key);
            if (_memoryCache.TryGetValue(fullKey, out string? cached) && cached != null)
                return _serializer.Deserialize<T>(cached);
            if (!RedisAvailable) return default;
            try
            {
                var value = _connectionManager.GetDatabase().StringGet(fullKey);
                if (value.IsNullOrEmpty) return default;
                var str = value.ToString();
                SetMemoryCache(fullKey, str, 300, false);
                return _serializer.Deserialize<T>(str);
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "Redis Get<T>失败, key={Key}", key);
                return default;
            }
        }
        public object? Get(Type type, string key)
        {
            var fullKey = BuildKey(key);
            if (_memoryCache.TryGetValue(fullKey, out string? cached) && cached != null)
                return _serializer.Deserialize(cached, type);
            if (!RedisAvailable) return null;
            try
            {
                var value = _connectionManager.GetDatabase().StringGet(fullKey);
                if (value.IsNullOrEmpty) return null;
                var str = value.ToString();
                SetMemoryCache(fullKey, str, 300, false);
                return _serializer.Deserialize(str, type);
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "Redis Get(Type)失败, key={Key}", key);
                return null;
            }
        }
        public string? Get(string key)
        {
            var fullKey = BuildKey(key);
            if (_memoryCache.TryGetValue(fullKey, out string? cached))
                return cached;
            if (!RedisAvailable) return null;
            try
            {
                var value = _connectionManager.GetDatabase().StringGet(fullKey);
                if (value.IsNullOrEmpty) return null;
                var str = value.ToString();
                SetMemoryCache(fullKey, str, 300, false);
                return str;
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "Redis Get失败, key={Key}", key);
                return null;
            }
        }
        private void SetMemoryCache(string fullKey, string value, int expireSeconds, bool isSliding)
        {
            var entryOptions = new MemoryCacheEntryOptions();
            if (expireSeconds > 0)
            {
                if (isSliding)
                    entryOptions.SetSlidingExpiration(TimeSpan.FromSeconds(expireSeconds));
                else
                    entryOptions.SetAbsoluteExpiration(TimeSpan.FromSeconds(expireSeconds));
            }
            _memoryCache.Set(fullKey, value, entryOptions);
        }
        #region ConcurrentDictionary风格方法
        public bool TryAdd(string key, string value, int expireSeconds = -1)
        {
            if (Exists(key)) return false;
            return Add(key, value, expireSeconds);
        }
        public bool TryAdd<T>(string key, T value, int expireSeconds = -1) where T : class
        {
            if (Exists(key)) return false;
            return AddObject(key, value, expireSeconds);
        }
        public bool TryGetValue(string key, out string? value)
        {
            value = Get(key);
            return value != null;
        }
        public bool TryGetValue<T>(string key, out T? value) where T : class
        {
            value = Get<T>(key);
            return value != null;
        }
        public bool TryRemove(string key, out string? value)
        {
            value = Get(key);
            if (value == null) return false;
            Remove(key);
            return true;
        }
        public bool TryUpdate(string key, string newValue, int expireSeconds = -1)
        {
            if (!Exists(key)) return false;
            Add(key, newValue, expireSeconds);
            return true;
        }
        public string GetOrAdd(string key, string value, int expireSeconds = -1)
        {
            var existing = Get(key);
            if (existing != null) return existing;
            Add(key, value, expireSeconds);
            return value;
        }
        public string GetOrAdd(string key, Func<string, string> valueFactory, int expireSeconds = -1)
        {
            var existing = Get(key);
            if (existing != null) return existing;
            var value = valueFactory(key);
            Add(key, value, expireSeconds);
            return value;
        }
        public T GetOrAdd<T>(string key, Func<string, T> valueFactory, int expireSeconds = -1) where T : class
        {
            var existing = Get<T>(key);
            if (existing != null) return existing;
            var value = valueFactory(key);
            AddObject(key, value, expireSeconds);
            return value;
        }
        #endregion
        public void Dispose()
        {
            if (_disposed) return;
            _disposed = true;
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Cache/RedisCacheService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,174 @@
using Microsoft.Extensions.Logging;
using StackExchange.Redis;
using WIDESEAWCS_Core.Caches;
using WIDESEAWCS_RedisService.Connection;
using WIDESEAWCS_RedisService.Options;
using WIDESEAWCS_RedisService.Serialization;
using Microsoft.Extensions.Options;
namespace WIDESEAWCS_RedisService.Cache
{
    public class RedisCacheService : ICacheService
    {
        private readonly IRedisConnectionManager _connectionManager;
        private readonly IRedisSerializer _serializer;
        private readonly RedisOptions _options;
        private readonly ILogger<RedisCacheService> _logger;
        private bool _disposed;
        public RedisCacheService(
            IRedisConnectionManager connectionManager,
            IRedisSerializer serializer,
            IOptions<RedisOptions> options,
            ILogger<RedisCacheService> logger)
        {
            _connectionManager = connectionManager;
            _serializer = serializer;
            _options = options.Value;
            _logger = logger;
        }
        private string BuildKey(string key) => $"{_options.KeyPrefix}{key}";
        private IDatabase Db => _connectionManager.GetDatabase();
        public bool Exists(string key)
        {
            return Db.KeyExists(BuildKey(key));
        }
        public bool Add(string key, string value, int expireSeconds = -1, bool isSliding = false)
        {
            var fullKey = BuildKey(key);
            var expiry = expireSeconds > 0 ? TimeSpan.FromSeconds(expireSeconds) : (TimeSpan?)null;
            return Db.StringSet(fullKey, value, expiry);
        }
        public bool AddObject(string key, object value, int expireSeconds = -1, bool isSliding = false)
        {
            return Add(key, _serializer.Serialize(value), expireSeconds, isSliding);
        }
        public void AddOrUpdate(string key, string value, int expireSeconds = -1, bool isSliding = false)
        {
            Add(key, value, expireSeconds, isSliding);
        }
        public void AddOrUpdate(string key, object value, int expireSeconds = -1, bool isSliding = false)
        {
            AddObject(key, value, expireSeconds, isSliding);
        }
        public bool Remove(string key)
        {
            return Db.KeyDelete(BuildKey(key));
        }
        public void Remove(IEnumerable<string> keys)
        {
            var redisKeys = keys.Select(k => (RedisKey)BuildKey(k)).ToArray();
            Db.KeyDelete(redisKeys);
        }
        public T? Get<T>(string key) where T : class
        {
            var value = Db.StringGet(BuildKey(key));
            if (value.IsNullOrEmpty) return default;
            return _serializer.Deserialize<T>(value!);
        }
        public object? Get(Type type, string key)
        {
            var value = Db.StringGet(BuildKey(key));
            if (value.IsNullOrEmpty) return null;
            return _serializer.Deserialize(value!, type);
        }
        public string? Get(string key)
        {
            var value = Db.StringGet(BuildKey(key));
            return value.IsNullOrEmpty ? null : value.ToString();
        }
        public void Dispose()
        {
            if (_disposed) return;
            _disposed = true;
        }
        public bool TryAdd(string key, string value, int expireSeconds = -1)
        {
            var fullKey = BuildKey(key);
            var expiry = expireSeconds > 0 ? TimeSpan.FromSeconds(expireSeconds) : (TimeSpan?)null;
            return Db.StringSet(fullKey, value, expiry, When.NotExists);
        }
        public bool TryAdd<T>(string key, T value, int expireSeconds = -1) where T : class
        {
            return TryAdd(key, _serializer.Serialize(value), expireSeconds);
        }
        public bool TryGetValue(string key, out string? value)
        {
            var val = Db.StringGet(BuildKey(key));
            value = val.IsNullOrEmpty ? null : val.ToString();
            return !val.IsNullOrEmpty;
        }
        public bool TryGetValue<T>(string key, out T? value) where T : class
        {
            var val = Db.StringGet(BuildKey(key));
            if (val.IsNullOrEmpty) { value = default; return false; }
            value = _serializer.Deserialize<T>(val!);
            return value != null;
        }
        public bool TryRemove(string key, out string? value)
        {
            var fullKey = BuildKey(key);
            value = Db.StringGet(fullKey).ToString();
            if (value == null) return false;
            return Db.KeyDelete(fullKey);
        }
        public bool TryUpdate(string key, string newValue, int expireSeconds = -1)
        {
            var fullKey = BuildKey(key);
            if (!Db.KeyExists(fullKey)) return false;
            var expiry = expireSeconds > 0 ? TimeSpan.FromSeconds(expireSeconds) : (TimeSpan?)null;
            return Db.StringSet(fullKey, newValue, expiry, When.Exists);
        }
        public string GetOrAdd(string key, string value, int expireSeconds = -1)
        {
            var fullKey = BuildKey(key);
            var existing = Db.StringGet(fullKey);
            if (!existing.IsNullOrEmpty) return existing.ToString();
            var expiry = expireSeconds > 0 ? TimeSpan.FromSeconds(expireSeconds) : (TimeSpan?)null;
            Db.StringSet(fullKey, value, expiry, When.NotExists);
            return Db.StringGet(fullKey).ToString();
        }
        public string GetOrAdd(string key, Func<string, string> valueFactory, int expireSeconds = -1)
        {
            var fullKey = BuildKey(key);
            var existing = Db.StringGet(fullKey);
            if (!existing.IsNullOrEmpty) return existing.ToString();
            var value = valueFactory(key);
            var expiry = expireSeconds > 0 ? TimeSpan.FromSeconds(expireSeconds) : (TimeSpan?)null;
            Db.StringSet(fullKey, value, expiry, When.NotExists);
            return Db.StringGet(fullKey).ToString();
        }
        public T GetOrAdd<T>(string key, Func<string, T> valueFactory, int expireSeconds = -1) where T : class
        {
            var fullKey = BuildKey(key);
            var existing = Db.StringGet(fullKey);
            if (!existing.IsNullOrEmpty) return _serializer.Deserialize<T>(existing!)!;
            var value = valueFactory(key);
            var expiry = expireSeconds > 0 ? TimeSpan.FromSeconds(expireSeconds) : (TimeSpan?)null;
            Db.StringSet(fullKey, _serializer.Serialize(value), expiry, When.NotExists);
            return value;
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Configuration/IConfigurationCenterService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
namespace WIDESEAWCS_RedisService.Configuration
{
    public interface IConfigurationCenterService
    {
        /// <summary>
        /// è®¾ç½®é…ç½®é¡¹
        /// </summary>
        bool Set(string section, string key, string value);
        /// <summary>
        /// èŽ·å–é…ç½®é¡¹
        /// </summary>
        string? Get(string section, string key);
        /// <summary>
        /// èŽ·å–æ•´ä¸ªé…ç½®èŠ‚
        /// </summary>
        Dictionary<string, string> GetSection(string section);
        /// <summary>
        /// åˆ é™¤é…ç½®é¡¹
        /// </summary>
        bool Delete(string section, string key);
        /// <summary>
        /// è®¢é˜…配置变更
        /// </summary>
        void Subscribe(string section, Action<string, string> onChange);
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Configuration/RedisConfigurationCenterService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StackExchange.Redis;
using WIDESEAWCS_RedisService.Connection;
using WIDESEAWCS_RedisService.Options;
namespace WIDESEAWCS_RedisService.Configuration
{
    public class RedisConfigurationCenterService : IConfigurationCenterService
    {
        private readonly IRedisConnectionManager _connectionManager;
        private readonly RedisOptions _options;
        private readonly ILogger<RedisConfigurationCenterService> _logger;
        public RedisConfigurationCenterService(
            IRedisConnectionManager connectionManager,
            IOptions<RedisOptions> options,
            ILogger<RedisConfigurationCenterService> logger)
        {
            _connectionManager = connectionManager;
            _options = options.Value;
            _logger = logger;
        }
        private string BuildKey(string section) => $"{_options.KeyPrefix}config:{section}";
        public bool Set(string section, string key, string value)
        {
            var db = _connectionManager.GetDatabase();
            var result = db.HashSet(BuildKey(section), key, value);
            // å‘布变更通知
            _connectionManager.GetSubscriber()
                .Publish(RedisChannel.Literal($"{_options.KeyPrefix}config:change:{section}"), key);
            return result;
        }
        public string? Get(string section, string key)
        {
            var val = _connectionManager.GetDatabase().HashGet(BuildKey(section), key);
            return val.IsNullOrEmpty ? null : val.ToString();
        }
        public Dictionary<string, string> GetSection(string section)
        {
            var entries = _connectionManager.GetDatabase().HashGetAll(BuildKey(section));
            return entries.ToDictionary(e => e.Name.ToString(), e => e.Value.ToString());
        }
        public bool Delete(string section, string key)
        {
            return _connectionManager.GetDatabase().HashDelete(BuildKey(section), key);
        }
        public void Subscribe(string section, Action<string, string> onChange)
        {
            _connectionManager.GetSubscriber()
                .Subscribe(RedisChannel.Literal($"{_options.KeyPrefix}config:change:{section}"), (_, msg) =>
                {
                    var changedKey = msg.ToString();
                    var value = Get(section, changedKey) ?? string.Empty;
                    onChange(changedKey, value);
                });
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Connection/IRedisConnectionManager.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
using StackExchange.Redis;
namespace WIDESEAWCS_RedisService.Connection
{
    public interface IRedisConnectionManager : IDisposable
    {
        IDatabase GetDatabase(int db = -1);
        IServer GetServer();
        ISubscriber GetSubscriber();
        ConnectionMultiplexer GetConnection();
        bool IsConnected { get; }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Connection/RedisConnectionManager.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,111 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StackExchange.Redis;
using System.Linq;
using WIDESEAWCS_RedisService.Options;
namespace WIDESEAWCS_RedisService.Connection
{
    public class RedisConnectionManager : IRedisConnectionManager
    {
        private readonly RedisOptions _options;
        private readonly ILogger<RedisConnectionManager> _logger;
        private readonly Lazy<ConnectionMultiplexer> _connection;
        private bool _disposed;
        public bool IsConnected
        {
            get
            {
                try
                {
                    // å¼ºåˆ¶è®¿é—®Value来触发连接创建
                    var connected = _connection.Value.IsConnected;
                    _logger.LogDebug("IsConnected检查: IsValueCreated={IsValueCreated}, IsConnected={Connected}",
                        _connection.IsValueCreated, connected);
                    return connected;
                }
                catch (Exception ex)
                {
                    _logger.LogWarning(ex, "IsConnected检查失败");
                    return false;
                }
            }
        }
        public RedisConnectionManager(IOptions<RedisOptions> options, ILogger<RedisConnectionManager> logger)
        {
            _options = options.Value;
            _logger = logger;
            _logger.LogInformation("RedisConnectionManager构造开始, ConnectionString={ConnectionString}, Enabled={Enabled}",
                _options.ConnectionString, _options.Enabled);
            _connection = new Lazy<ConnectionMultiplexer>(CreateConnection);
            _logger.LogInformation("RedisConnectionManager构造完成, Lazy已创建");
        }
        private ConnectionMultiplexer CreateConnection()
        {
            try
            {
                _logger.LogInformation("开始创建Redis连接, ConnectionString={ConnectionString}", _options.ConnectionString);
                var configOptions = ConfigurationOptions.Parse(_options.ConnectionString);
                configOptions.AbortOnConnectFail = false;
                configOptions.ConnectRetry = _options.ConnectRetry;
                configOptions.DefaultDatabase = _options.DefaultDatabase;
                _logger.LogInformation("ConfigurationOptions解析完成, EndPoints={EndPoints}", string.Join(",", configOptions.EndPoints.Select(e => e.ToString())));
                if (_options.EnableSentinel && _options.SentinelEndpoints.Count > 0)
                {
                    configOptions.ServiceName = _options.SentinelMasterName;
                    foreach (var endpoint in _options.SentinelEndpoints)
                    {
                        configOptions.EndPoints.Add(endpoint);
                    }
                }
                var connection = ConnectionMultiplexer.Connect(configOptions);
                connection.ConnectionFailed += (_, e) =>
                    _logger.LogError("Redis连接失败: {FailureType}", e.FailureType);
                connection.ConnectionRestored += (_, e) =>
                    _logger.LogInformation("Redis连接恢复: {EndPoint}", e.EndPoint);
                _logger.LogInformation("Redis连接成功: {EndPoints}", string.Join(",", configOptions.EndPoints));
                return connection;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Redis连接创建失败");
                throw;
            }
        }
        public IDatabase GetDatabase(int db = -1)
        {
            return _connection.Value.GetDatabase(db == -1 ? _options.DefaultDatabase : db);
        }
        public IServer GetServer()
        {
            var endpoints = _connection.Value.GetEndPoints();
            return _connection.Value.GetServer(endpoints[0]);
        }
        public ISubscriber GetSubscriber()
        {
            return _connection.Value.GetSubscriber();
        }
        public ConnectionMultiplexer GetConnection()
        {
            return _connection.Value;
        }
        public void Dispose()
        {
            if (_disposed) return;
            _disposed = true;
            if (_connection.IsValueCreated)
                _connection.Value.Dispose();
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Counter/ICounterService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
namespace WIDESEAWCS_RedisService.Counter
{
    public interface ICounterService
    {
        /// <summary>
        /// é€’增
        /// </summary>
        long Increment(string key, long value = 1);
        /// <summary>
        /// é€’减
        /// </summary>
        long Decrement(string key, long value = 1);
        /// <summary>
        /// èŽ·å–å½“å‰å€¼
        /// </summary>
        long GetCount(string key);
        /// <summary>
        /// é‡ç½®è®¡æ•°å™¨
        /// </summary>
        bool Reset(string key);
        /// <summary>
        /// è®¾ç½®å¸¦è¿‡æœŸæ—¶é—´çš„计数器
        /// </summary>
        long IncrementWithExpiry(string key, TimeSpan expiry, long value = 1);
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Counter/RedisCounterService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,57 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using WIDESEAWCS_RedisService.Connection;
using WIDESEAWCS_RedisService.Options;
namespace WIDESEAWCS_RedisService.Counter
{
    public class RedisCounterService : ICounterService
    {
        private readonly IRedisConnectionManager _connectionManager;
        private readonly RedisOptions _options;
        private readonly ILogger<RedisCounterService> _logger;
        public RedisCounterService(
            IRedisConnectionManager connectionManager,
            IOptions<RedisOptions> options,
            ILogger<RedisCounterService> logger)
        {
            _connectionManager = connectionManager;
            _options = options.Value;
            _logger = logger;
        }
        private string BuildKey(string key) => $"{_options.KeyPrefix}counter:{key}";
        public long Increment(string key, long value = 1)
        {
            return _connectionManager.GetDatabase().StringIncrement(BuildKey(key), value);
        }
        public long Decrement(string key, long value = 1)
        {
            return _connectionManager.GetDatabase().StringDecrement(BuildKey(key), value);
        }
        public long GetCount(string key)
        {
            var val = _connectionManager.GetDatabase().StringGet(BuildKey(key));
            return val.IsNullOrEmpty ? 0 : (long)val;
        }
        public bool Reset(string key)
        {
            return _connectionManager.GetDatabase().KeyDelete(BuildKey(key));
        }
        public long IncrementWithExpiry(string key, TimeSpan expiry, long value = 1)
        {
            var db = _connectionManager.GetDatabase();
            var fullKey = BuildKey(key);
            var result = db.StringIncrement(fullKey, value);
            if (result == value)
                db.KeyExpire(fullKey, expiry);
            return result;
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/DelayQueue/IDelayQueueService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
namespace WIDESEAWCS_RedisService.DelayQueue
{
    public interface IDelayQueueService
    {
        /// <summary>
        /// æ·»åŠ å»¶è¿Ÿä»»åŠ¡
        /// </summary>
        bool Enqueue(string queueName, string message, TimeSpan delay);
        /// <summary>
        /// èŽ·å–åˆ°æœŸçš„ä»»åŠ¡
        /// </summary>
        List<string> DequeueDue(string queueName, int count = 10);
        /// <summary>
        /// ç§»é™¤ä»»åŠ¡
        /// </summary>
        bool Remove(string queueName, string message);
        /// <summary>
        /// èŽ·å–é˜Ÿåˆ—é•¿åº¦
        /// </summary>
        long GetQueueLength(string queueName);
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/DelayQueue/RedisDelayQueueService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,60 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StackExchange.Redis;
using WIDESEAWCS_RedisService.Connection;
using WIDESEAWCS_RedisService.Options;
namespace WIDESEAWCS_RedisService.DelayQueue
{
    public class RedisDelayQueueService : IDelayQueueService
    {
        private readonly IRedisConnectionManager _connectionManager;
        private readonly RedisOptions _options;
        private readonly ILogger<RedisDelayQueueService> _logger;
        public RedisDelayQueueService(
            IRedisConnectionManager connectionManager,
            IOptions<RedisOptions> options,
            ILogger<RedisDelayQueueService> logger)
        {
            _connectionManager = connectionManager;
            _options = options.Value;
            _logger = logger;
        }
        private string BuildKey(string key) => $"{_options.KeyPrefix}delay:{key}";
        public bool Enqueue(string queueName, string message, TimeSpan delay)
        {
            var score = DateTimeOffset.UtcNow.Add(delay).ToUnixTimeMilliseconds();
            return _connectionManager.GetDatabase().SortedSetAdd(BuildKey(queueName), message, score);
        }
        public List<string> DequeueDue(string queueName, int count = 10)
        {
            var db = _connectionManager.GetDatabase();
            var fullKey = BuildKey(queueName);
            var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
            var entries = db.SortedSetRangeByScore(fullKey, 0, now, take: count);
            var result = new List<string>();
            foreach (var entry in entries)
            {
                if (db.SortedSetRemove(fullKey, entry))
                    result.Add(entry.ToString());
            }
            return result;
        }
        public bool Remove(string queueName, string message)
        {
            return _connectionManager.GetDatabase().SortedSetRemove(BuildKey(queueName), message);
        }
        public long GetQueueLength(string queueName)
        {
            return _connectionManager.GetDatabase().SortedSetLength(BuildKey(queueName));
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Extensions/RedisServiceSetup.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,77 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System.Linq;
using WIDESEAWCS_Core.Caches;
using WIDESEAWCS_RedisService.Bitmap;
using WIDESEAWCS_RedisService.Cache;
using WIDESEAWCS_RedisService.Configuration;
using WIDESEAWCS_RedisService.Connection;
using WIDESEAWCS_RedisService.Counter;
using WIDESEAWCS_RedisService.DelayQueue;
using WIDESEAWCS_RedisService.IdGenerator;
using WIDESEAWCS_RedisService.Leaderboard;
using WIDESEAWCS_RedisService.Geo;
using WIDESEAWCS_RedisService.Lock;
using WIDESEAWCS_RedisService.Monitoring;
using WIDESEAWCS_RedisService.Options;
using WIDESEAWCS_RedisService.PubSub;
using WIDESEAWCS_RedisService.RateLimiting;
using WIDESEAWCS_RedisService.Serialization;
using WIDESEAWCS_RedisService.Session;
using WIDESEAWCS_RedisService.Storage;
using WIDESEAWCS_RedisService.Stream;
namespace WIDESEAWCS_RedisService.Extensions
{
    public static class RedisServiceSetup
    {
        public static IServiceCollection AddRedisSetup(this IServiceCollection services, IConfiguration configuration)
        {
            var section = configuration.GetSection("RedisConfig");
            services.Configure<RedisOptions>(section);
            var options = section.Get<RedisOptions>() ?? new RedisOptions();
            if (!options.Enabled)
            {
                // Redis未启用,使用内存缓存
                services.AddMemoryCache();
                return services;
            }
            // ç§»é™¤å·²æœ‰çš„ICacheService注册(MemoryCacheService),避免冲突
            var existingDescriptor = services.FirstOrDefault(d => d.ServiceType == typeof(ICacheService));
            if (existingDescriptor != null)
            {
                services.Remove(existingDescriptor);
            }
            // åŸºç¡€è®¾æ–½
            services.AddMemoryCache();
            services.AddSingleton<IRedisSerializer, NewtonsoftRedisSerializer>();
            services.AddSingleton<IRedisConnectionManager, RedisConnectionManager>();
            // ç¼“存(替换原有MemoryCacheService)
            services.AddSingleton<ICacheService, HybridCacheService>();
            // åŠŸèƒ½æ¨¡å—
            services.AddSingleton<IDistributedLockService, RedisDistributedLockService>();
            services.AddSingleton<ICounterService, RedisCounterService>();
            services.AddSingleton<IMessageQueueService, RedisMessageQueueService>();
            services.AddSingleton<IDelayQueueService, RedisDelayQueueService>();
            services.AddSingleton<IStreamProcessingService, RedisStreamProcessingService>();
            services.AddSingleton<ILeaderboardService, RedisLeaderboardService>();
            services.AddSingleton<IGeoLocationService, RedisGeoLocationService>();
            services.AddSingleton<IBitmapService, RedisBitmapService>();
            services.AddSingleton<IBloomFilterService, RedisBloomFilterService>();
            services.AddSingleton<IObjectStorageService, RedisObjectStorageService>();
            services.AddSingleton<IDistributedIdGenerator, RedisDistributedIdGenerator>();
            services.AddSingleton<IRateLimitingService, RedisRateLimitingService>();
            services.AddSingleton<IConfigurationCenterService, RedisConfigurationCenterService>();
            services.AddSingleton<ISessionStorage, RedisSessionStorage>();
            services.AddSingleton<IRedisMonitorService, RedisMonitorService>();
            return services;
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Geo/IGeoLocationService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,43 @@
namespace WIDESEAWCS_RedisService.Geo
{
    public interface IGeoLocationService
    {
        /// <summary>
        /// æ·»åŠ åœ°ç†ä½ç½®
        /// </summary>
        bool Add(string key, double longitude, double latitude, string member);
        /// <summary>
        /// èŽ·å–æˆå‘˜ä½ç½®
        /// </summary>
        GeoPosition? GetPosition(string key, string member);
        /// <summary>
        /// è®¡ç®—两个成员之间的距离(米)
        /// </summary>
        double? GetDistance(string key, string member1, string member2);
        /// <summary>
        /// æœç´¢æŒ‡å®šåŠå¾„内的成员
        /// </summary>
        List<GeoRadiusResult> SearchRadius(string key, double longitude, double latitude, double radiusMeters, int count = 10);
        /// <summary>
        /// ç§»é™¤æˆå‘˜
        /// </summary>
        bool Remove(string key, string member);
    }
    public class GeoPosition
    {
        public double Longitude { get; set; }
        public double Latitude { get; set; }
    }
    public class GeoRadiusResult
    {
        public string Member { get; set; } = string.Empty;
        public double? Distance { get; set; }
        public GeoPosition? Position { get; set; }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Geo/RedisGeoLocationService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StackExchange.Redis;
using WIDESEAWCS_RedisService.Connection;
using WIDESEAWCS_RedisService.Options;
namespace WIDESEAWCS_RedisService.Geo
{
    public class RedisGeoLocationService : IGeoLocationService
    {
        private readonly IRedisConnectionManager _connectionManager;
        private readonly RedisOptions _options;
        private readonly ILogger<RedisGeoLocationService> _logger;
        public RedisGeoLocationService(
            IRedisConnectionManager connectionManager,
            IOptions<RedisOptions> options,
            ILogger<RedisGeoLocationService> logger)
        {
            _connectionManager = connectionManager;
            _options = options.Value;
            _logger = logger;
        }
        private string BuildKey(string key) => $"{_options.KeyPrefix}geo:{key}";
        public bool Add(string key, double longitude, double latitude, string member)
        {
            return _connectionManager.GetDatabase().GeoAdd(BuildKey(key), longitude, latitude, member);
        }
        public GeoPosition? GetPosition(string key, string member)
        {
            var pos = _connectionManager.GetDatabase().GeoPosition(BuildKey(key), member);
            if (pos == null) return null;
            return new GeoPosition { Longitude = pos.Value.Longitude, Latitude = pos.Value.Latitude };
        }
        public double? GetDistance(string key, string member1, string member2)
        {
            return _connectionManager.GetDatabase().GeoDistance(BuildKey(key), member1, member2, GeoUnit.Meters);
        }
        public List<GeoRadiusResult> SearchRadius(string key, double longitude, double latitude, double radiusMeters, int count = 10)
        {
            var results = _connectionManager.GetDatabase()
                .GeoRadius(BuildKey(key), longitude, latitude, radiusMeters, GeoUnit.Meters,
                    count, Order.Ascending, GeoRadiusOptions.WithDistance | GeoRadiusOptions.WithCoordinates);
            return results.Select(r => new GeoRadiusResult
            {
                Member = r.Member.ToString(),
                Distance = r.Distance,
                Position = r.Position.HasValue
                    ? new GeoPosition { Longitude = r.Position.Value.Longitude, Latitude = r.Position.Value.Latitude }
                    : null
            }).ToList();
        }
        public bool Remove(string key, string member)
        {
            return _connectionManager.GetDatabase().SortedSetRemove(BuildKey(key), member);
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/IdGenerator/IDistributedIdGenerator.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
namespace WIDESEAWCS_RedisService.IdGenerator
{
    public interface IDistributedIdGenerator
    {
        /// <summary>
        /// ç”Ÿæˆè‡ªå¢žID
        /// </summary>
        long NextId(string sequenceName);
        /// <summary>
        /// ç”Ÿæˆå¸¦æ—¥æœŸå‰ç¼€çš„ID(如 20260228000001)
        /// </summary>
        string NextIdWithDate(string sequenceName);
        /// <summary>
        /// èŽ·å–å½“å‰åºåˆ—å€¼
        /// </summary>
        long GetCurrentId(string sequenceName);
        /// <summary>
        /// é‡ç½®åºåˆ—
        /// </summary>
        bool Reset(string sequenceName);
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/IdGenerator/RedisDistributedIdGenerator.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,56 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using WIDESEAWCS_RedisService.Connection;
using WIDESEAWCS_RedisService.Options;
namespace WIDESEAWCS_RedisService.IdGenerator
{
    public class RedisDistributedIdGenerator : IDistributedIdGenerator
    {
        private readonly IRedisConnectionManager _connectionManager;
        private readonly RedisOptions _options;
        private readonly ILogger<RedisDistributedIdGenerator> _logger;
        public RedisDistributedIdGenerator(
            IRedisConnectionManager connectionManager,
            IOptions<RedisOptions> options,
            ILogger<RedisDistributedIdGenerator> logger)
        {
            _connectionManager = connectionManager;
            _options = options.Value;
            _logger = logger;
        }
        private string BuildKey(string name) => $"{_options.KeyPrefix}id:{name}";
        public long NextId(string sequenceName)
        {
            return _connectionManager.GetDatabase().StringIncrement(BuildKey(sequenceName));
        }
        public string NextIdWithDate(string sequenceName)
        {
            var datePrefix = DateTime.Now.ToString("yyyyMMdd");
            var dailyKey = $"{BuildKey(sequenceName)}:{datePrefix}";
            var db = _connectionManager.GetDatabase();
            var seq = db.StringIncrement(dailyKey);
            // æ¯æ—¥é¦–次生成时设置过期时间(48小时,留余量)
            if (seq == 1)
                db.KeyExpire(dailyKey, TimeSpan.FromHours(48));
            return $"{datePrefix}{seq:D6}";
        }
        public long GetCurrentId(string sequenceName)
        {
            var val = _connectionManager.GetDatabase().StringGet(BuildKey(sequenceName));
            return val.IsNullOrEmpty ? 0 : (long)val;
        }
        public bool Reset(string sequenceName)
        {
            return _connectionManager.GetDatabase().KeyDelete(BuildKey(sequenceName));
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Leaderboard/ILeaderboardService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
namespace WIDESEAWCS_RedisService.Leaderboard
{
    public interface ILeaderboardService
    {
        /// <summary>
        /// æ·»åŠ æˆ–æ›´æ–°æˆå‘˜åˆ†æ•°
        /// </summary>
        bool AddOrUpdate(string boardName, string member, double score);
        /// <summary>
        /// å¢žåŠ æˆå‘˜åˆ†æ•°
        /// </summary>
        double IncrementScore(string boardName, string member, double increment);
        /// <summary>
        /// èŽ·å–æŽ’åï¼ˆä»Žé«˜åˆ°ä½Žï¼Œ0开始)
        /// </summary>
        long? GetRank(string boardName, string member);
        /// <summary>
        /// èŽ·å–æˆå‘˜åˆ†æ•°
        /// </summary>
        double? GetScore(string boardName, string member);
        /// <summary>
        /// èŽ·å–æŽ’è¡Œæ¦œï¼ˆä»Žé«˜åˆ°ä½Žï¼‰
        /// </summary>
        List<LeaderboardEntry> GetTopN(string boardName, int count);
        /// <summary>
        /// èŽ·å–æŒ‡å®šæŽ’åèŒƒå›´
        /// </summary>
        List<LeaderboardEntry> GetRange(string boardName, long start, long stop);
        /// <summary>
        /// ç§»é™¤æˆå‘˜
        /// </summary>
        bool Remove(string boardName, string member);
        /// <summary>
        /// èŽ·å–æŽ’è¡Œæ¦œæ€»äººæ•°
        /// </summary>
        long GetCount(string boardName);
    }
    public class LeaderboardEntry
    {
        public string Member { get; set; } = string.Empty;
        public double Score { get; set; }
        public long Rank { get; set; }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Leaderboard/RedisLeaderboardService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,75 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StackExchange.Redis;
using WIDESEAWCS_RedisService.Connection;
using WIDESEAWCS_RedisService.Options;
namespace WIDESEAWCS_RedisService.Leaderboard
{
    public class RedisLeaderboardService : ILeaderboardService
    {
        private readonly IRedisConnectionManager _connectionManager;
        private readonly RedisOptions _options;
        private readonly ILogger<RedisLeaderboardService> _logger;
        public RedisLeaderboardService(
            IRedisConnectionManager connectionManager,
            IOptions<RedisOptions> options,
            ILogger<RedisLeaderboardService> logger)
        {
            _connectionManager = connectionManager;
            _options = options.Value;
            _logger = logger;
        }
        private string BuildKey(string key) => $"{_options.KeyPrefix}board:{key}";
        public bool AddOrUpdate(string boardName, string member, double score)
        {
            return _connectionManager.GetDatabase().SortedSetAdd(BuildKey(boardName), member, score);
        }
        public double IncrementScore(string boardName, string member, double increment)
        {
            return _connectionManager.GetDatabase().SortedSetIncrement(BuildKey(boardName), member, increment);
        }
        public long? GetRank(string boardName, string member)
        {
            return _connectionManager.GetDatabase().SortedSetRank(BuildKey(boardName), member, Order.Descending);
        }
        public double? GetScore(string boardName, string member)
        {
            return _connectionManager.GetDatabase().SortedSetScore(BuildKey(boardName), member);
        }
        public List<LeaderboardEntry> GetTopN(string boardName, int count)
        {
            return GetRange(boardName, 0, count - 1);
        }
        public List<LeaderboardEntry> GetRange(string boardName, long start, long stop)
        {
            var entries = _connectionManager.GetDatabase()
                .SortedSetRangeByRankWithScores(BuildKey(boardName), start, stop, Order.Descending);
            return entries.Select((e, i) => new LeaderboardEntry
            {
                Member = e.Element.ToString(),
                Score = e.Score,
                Rank = start + i
            }).ToList();
        }
        public bool Remove(string boardName, string member)
        {
            return _connectionManager.GetDatabase().SortedSetRemove(BuildKey(boardName), member);
        }
        public long GetCount(string boardName)
        {
            return _connectionManager.GetDatabase().SortedSetLength(BuildKey(boardName));
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Lock/IDistributedLockService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,34 @@
namespace WIDESEAWCS_RedisService.Lock
{
    public interface IDistributedLockService
    {
        /// <summary>
        /// èŽ·å–åˆ†å¸ƒå¼é”
        /// </summary>
        /// <param name="lockKey">锁的Key</param>
        /// <param name="expiry">锁过期时间</param>
        /// <param name="waitTimeout">等待获取锁的超时时间</param>
        /// <returns>锁Token,释放时需要传入;获取失败返回null</returns>
        string? AcquireLock(string lockKey, TimeSpan expiry, TimeSpan? waitTimeout = null);
        /// <summary>
        /// é‡Šæ”¾åˆ†å¸ƒå¼é”
        /// </summary>
        bool ReleaseLock(string lockKey, string lockToken);
        /// <summary>
        /// ç»­æœŸé”
        /// </summary>
        bool RenewLock(string lockKey, string lockToken, TimeSpan expiry);
        /// <summary>
        /// ä½¿ç”¨é”æ‰§è¡Œæ“ä½œ
        /// </summary>
        T? ExecuteWithLock<T>(string lockKey, TimeSpan expiry, Func<T> action, TimeSpan? waitTimeout = null);
        /// <summary>
        /// ä½¿ç”¨é”æ‰§è¡Œæ“ä½œï¼ˆæ— è¿”回值)
        /// </summary>
        bool ExecuteWithLock(string lockKey, TimeSpan expiry, Action action, TimeSpan? waitTimeout = null);
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Lock/RedisDistributedLockService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,112 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StackExchange.Redis;
using WIDESEAWCS_RedisService.Connection;
using WIDESEAWCS_RedisService.Options;
namespace WIDESEAWCS_RedisService.Lock
{
    public class RedisDistributedLockService : IDistributedLockService
    {
        private readonly IRedisConnectionManager _connectionManager;
        private readonly RedisOptions _options;
        private readonly ILogger<RedisDistributedLockService> _logger;
        // Lua脚本:安全释放锁(只有持有者才能释放)
        private const string ReleaseLockScript = @"
            if redis.call('get', KEYS[1]) == ARGV[1] then
                return redis.call('del', KEYS[1])
            else
                return 0
            end";
        // Lua脚本:安全续期锁
        private const string RenewLockScript = @"
            if redis.call('get', KEYS[1]) == ARGV[1] then
                return redis.call('pexpire', KEYS[1], ARGV[2])
            else
                return 0
            end";
        public RedisDistributedLockService(
            IRedisConnectionManager connectionManager,
            IOptions<RedisOptions> options,
            ILogger<RedisDistributedLockService> logger)
        {
            _connectionManager = connectionManager;
            _options = options.Value;
            _logger = logger;
        }
        private string BuildKey(string key) => $"{_options.KeyPrefix}lock:{key}";
        public string? AcquireLock(string lockKey, TimeSpan expiry, TimeSpan? waitTimeout = null)
        {
            var fullKey = BuildKey(lockKey);
            var token = Guid.NewGuid().ToString("N");
            var db = _connectionManager.GetDatabase();
            var deadline = DateTime.UtcNow + (waitTimeout ?? TimeSpan.Zero);
            do
            {
                if (db.StringSet(fullKey, token, expiry, When.NotExists))
                    return token;
                if (waitTimeout == null) return null;
                Thread.Sleep(50);
            }
            while (DateTime.UtcNow < deadline);
            return null;
        }
        public bool ReleaseLock(string lockKey, string lockToken)
        {
            var db = _connectionManager.GetDatabase();
            var result = db.ScriptEvaluate(
                ReleaseLockScript,
                new RedisKey[] { BuildKey(lockKey) },
                new RedisValue[] { lockToken });
            return (long)result == 1;
        }
        public bool RenewLock(string lockKey, string lockToken, TimeSpan expiry)
        {
            var db = _connectionManager.GetDatabase();
            var result = db.ScriptEvaluate(
                RenewLockScript,
                new RedisKey[] { BuildKey(lockKey) },
                new RedisValue[] { lockToken, (long)expiry.TotalMilliseconds });
            return (long)result == 1;
        }
        public T? ExecuteWithLock<T>(string lockKey, TimeSpan expiry, Func<T> action, TimeSpan? waitTimeout = null)
        {
            var token = AcquireLock(lockKey, expiry, waitTimeout);
            if (token == null) return default;
            try
            {
                return action();
            }
            finally
            {
                ReleaseLock(lockKey, token);
            }
        }
        public bool ExecuteWithLock(string lockKey, TimeSpan expiry, Action action, TimeSpan? waitTimeout = null)
        {
            var token = AcquireLock(lockKey, expiry, waitTimeout);
            if (token == null) return false;
            try
            {
                action();
                return true;
            }
            finally
            {
                ReleaseLock(lockKey, token);
            }
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Monitoring/IRedisMonitorService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
namespace WIDESEAWCS_RedisService.Monitoring
{
    public interface IRedisMonitorService
    {
        /// <summary>
        /// èŽ·å–Redis服务器信息
        /// </summary>
        Dictionary<string, string> GetServerInfo();
        /// <summary>
        /// èŽ·å–å†…å­˜ä½¿ç”¨ä¿¡æ¯
        /// </summary>
        RedisMemoryInfo GetMemoryInfo();
        /// <summary>
        /// å¥åº·æ£€æŸ¥
        /// </summary>
        bool HealthCheck();
        /// <summary>
        /// èŽ·å–æ•°æ®åº“Key数量
        /// </summary>
        long GetDbSize();
        /// <summary>
        /// èŽ·å–å®¢æˆ·ç«¯è¿žæŽ¥æ•°
        /// </summary>
        long GetClientCount();
    }
    public class RedisMemoryInfo
    {
        public long UsedMemory { get; set; }
        public string UsedMemoryHuman { get; set; } = string.Empty;
        public long MaxMemory { get; set; }
        public string MaxMemoryHuman { get; set; } = string.Empty;
        public double UsagePercent { get; set; }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Monitoring/RedisMonitorService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,91 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using WIDESEAWCS_RedisService.Connection;
using WIDESEAWCS_RedisService.Options;
namespace WIDESEAWCS_RedisService.Monitoring
{
    public class RedisMonitorService : IRedisMonitorService
    {
        private readonly IRedisConnectionManager _connectionManager;
        private readonly RedisOptions _options;
        private readonly ILogger<RedisMonitorService> _logger;
        public RedisMonitorService(
            IRedisConnectionManager connectionManager,
            IOptions<RedisOptions> options,
            ILogger<RedisMonitorService> logger)
        {
            _connectionManager = connectionManager;
            _options = options.Value;
            _logger = logger;
        }
        public Dictionary<string, string> GetServerInfo()
        {
            var server = _connectionManager.GetServer();
            var info = server.Info();
            var result = new Dictionary<string, string>();
            foreach (var group in info)
            {
                foreach (var item in group)
                    result[$"{group.Key}:{item.Key}"] = item.Value;
            }
            return result;
        }
        public RedisMemoryInfo GetMemoryInfo()
        {
            var server = _connectionManager.GetServer();
            var info = server.Info("memory");
            var memorySection = info.FirstOrDefault();
            var memInfo = new RedisMemoryInfo();
            if (memorySection == null) return memInfo;
            var dict = memorySection.ToDictionary(x => x.Key, x => x.Value);
            if (dict.TryGetValue("used_memory", out var used))
                memInfo.UsedMemory = long.Parse(used);
            if (dict.TryGetValue("used_memory_human", out var usedH))
                memInfo.UsedMemoryHuman = usedH;
            if (dict.TryGetValue("maxmemory", out var max))
                memInfo.MaxMemory = long.Parse(max);
            if (dict.TryGetValue("maxmemory_human", out var maxH))
                memInfo.MaxMemoryHuman = maxH;
            if (memInfo.MaxMemory > 0)
                memInfo.UsagePercent = Math.Round((double)memInfo.UsedMemory / memInfo.MaxMemory * 100, 2);
            return memInfo;
        }
        public bool HealthCheck()
        {
            try
            {
                var db = _connectionManager.GetDatabase();
                var pong = db.Ping();
                return pong.TotalMilliseconds < 5000;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Redis健康检查失败");
                return false;
            }
        }
        public long GetDbSize()
        {
            return _connectionManager.GetServer().DatabaseSize();
        }
        public long GetClientCount()
        {
            var info = _connectionManager.GetServer().Info("clients");
            var section = info.FirstOrDefault();
            if (section == null) return 0;
            var dict = section.ToDictionary(x => x.Key, x => x.Value);
            return dict.TryGetValue("connected_clients", out var count) ? long.Parse(count) : 0;
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Options/RedisOptions.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,49 @@
namespace WIDESEAWCS_RedisService.Options
{
    public class RedisOptions
    {
        public bool Enabled { get; set; } = true;
        public string ConnectionString { get; set; } = "127.0.0.1:6379,defaultDatabase=0,connectTimeout=5000,abortConnect=false";
        public string InstanceName { get; set; } = "WIDESEAWCS:";
        public int DefaultDatabase { get; set; } = 0;
        public bool EnableSentinel { get; set; } = false;
        public string SentinelMasterName { get; set; } = "mymaster";
        public List<string> SentinelEndpoints { get; set; } = new();
        public int PoolSize { get; set; } = 10;
        public int ConnectRetry { get; set; } = 3;
        public string SerializerType { get; set; } = "Newtonsoft";
        public bool FallbackToMemory { get; set; } = true;
        public string KeyPrefix { get; set; } = "wcs:";
        public MonitoringOptions Monitoring { get; set; } = new();
        public EvictionOptions Eviction { get; set; } = new();
    }
    public class MonitoringOptions
    {
        public bool Enabled { get; set; } = false;
        public int SlowLogThresholdMs { get; set; } = 100;
        public int HealthCheckIntervalSeconds { get; set; } = 30;
    }
    public class EvictionOptions
    {
        public int DefaultExpirationSeconds { get; set; } = 3600;
        public string MaxMemoryPolicy { get; set; } = "allkeys-lru";
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/PubSub/IMessageQueueService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
namespace WIDESEAWCS_RedisService.PubSub
{
    public interface IMessageQueueService
    {
        /// <summary>
        /// å‘布消息
        /// </summary>
        long Publish(string channel, string message);
        /// <summary>
        /// è®¢é˜…频道
        /// </summary>
        void Subscribe(string channel, Action<string, string> handler);
        /// <summary>
        /// å–消订阅
        /// </summary>
        void Unsubscribe(string channel);
        /// <summary>
        /// æŽ¨é€æ¶ˆæ¯åˆ°å¯é é˜Ÿåˆ—(List)
        /// </summary>
        long Enqueue(string queueName, string message);
        /// <summary>
        /// ä»Žå¯é é˜Ÿåˆ—弹出消息
        /// </summary>
        string? Dequeue(string queueName, TimeSpan? timeout = null);
        /// <summary>
        /// èŽ·å–é˜Ÿåˆ—é•¿åº¦
        /// </summary>
        long GetQueueLength(string queueName);
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/PubSub/RedisMessageQueueService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,64 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StackExchange.Redis;
using WIDESEAWCS_RedisService.Connection;
using WIDESEAWCS_RedisService.Options;
namespace WIDESEAWCS_RedisService.PubSub
{
    public class RedisMessageQueueService : IMessageQueueService
    {
        private readonly IRedisConnectionManager _connectionManager;
        private readonly RedisOptions _options;
        private readonly ILogger<RedisMessageQueueService> _logger;
        public RedisMessageQueueService(
            IRedisConnectionManager connectionManager,
            IOptions<RedisOptions> options,
            ILogger<RedisMessageQueueService> logger)
        {
            _connectionManager = connectionManager;
            _options = options.Value;
            _logger = logger;
        }
        private string BuildKey(string key) => $"{_options.KeyPrefix}queue:{key}";
        public long Publish(string channel, string message)
        {
            var sub = _connectionManager.GetSubscriber();
            return sub.Publish(RedisChannel.Literal($"{_options.KeyPrefix}{channel}"), message);
        }
        public void Subscribe(string channel, Action<string, string> handler)
        {
            var sub = _connectionManager.GetSubscriber();
            sub.Subscribe(RedisChannel.Literal($"{_options.KeyPrefix}{channel}"), (ch, msg) =>
            {
                handler(ch!, msg!);
            });
        }
        public void Unsubscribe(string channel)
        {
            var sub = _connectionManager.GetSubscriber();
            sub.Unsubscribe(RedisChannel.Literal($"{_options.KeyPrefix}{channel}"));
        }
        public long Enqueue(string queueName, string message)
        {
            return _connectionManager.GetDatabase().ListLeftPush(BuildKey(queueName), message);
        }
        public string? Dequeue(string queueName, TimeSpan? timeout = null)
        {
            var val = _connectionManager.GetDatabase().ListRightPop(BuildKey(queueName));
            return val.IsNullOrEmpty ? null : val.ToString();
        }
        public long GetQueueLength(string queueName)
        {
            return _connectionManager.GetDatabase().ListLength(BuildKey(queueName));
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/RateLimiting/IRateLimitingService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
namespace WIDESEAWCS_RedisService.RateLimiting
{
    public interface IRateLimitingService
    {
        /// <summary>
        /// å›ºå®šçª—口限流
        /// </summary>
        bool IsAllowed(string key, int maxRequests, TimeSpan window);
        /// <summary>
        /// æ»‘动窗口限流
        /// </summary>
        bool IsAllowedSliding(string key, int maxRequests, TimeSpan window);
        /// <summary>
        /// ä»¤ç‰Œæ¡¶é™æµ
        /// </summary>
        bool TryAcquireToken(string key, int maxTokens, int refillRate, TimeSpan refillInterval);
        /// <summary>
        /// èŽ·å–å‰©ä½™è¯·æ±‚æ•°
        /// </summary>
        long GetRemainingRequests(string key, int maxRequests, TimeSpan window);
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/RateLimiting/RedisRateLimitingService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,113 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StackExchange.Redis;
using WIDESEAWCS_RedisService.Connection;
using WIDESEAWCS_RedisService.Options;
namespace WIDESEAWCS_RedisService.RateLimiting
{
    public class RedisRateLimitingService : IRateLimitingService
    {
        private readonly IRedisConnectionManager _connectionManager;
        private readonly RedisOptions _options;
        private readonly ILogger<RedisRateLimitingService> _logger;
        private const string FixedWindowScript = @"
            local key = KEYS[1]
            local limit = tonumber(ARGV[1])
            local window = tonumber(ARGV[2])
            local current = tonumber(redis.call('GET', key) or '0')
            if current < limit then
                redis.call('INCR', key)
                if current == 0 then
                    redis.call('PEXPIRE', key, window)
                end
                return 1
            end
            return 0";
        public RedisRateLimitingService(
            IRedisConnectionManager connectionManager,
            IOptions<RedisOptions> options,
            ILogger<RedisRateLimitingService> logger)
        {
            _connectionManager = connectionManager;
            _options = options.Value;
            _logger = logger;
        }
        private string BuildKey(string key) => $"{_options.KeyPrefix}rate:{key}";
        public bool IsAllowed(string key, int maxRequests, TimeSpan window)
        {
            var db = _connectionManager.GetDatabase();
            var result = db.ScriptEvaluate(
                FixedWindowScript,
                new RedisKey[] { BuildKey(key) },
                new RedisValue[] { maxRequests, (long)window.TotalMilliseconds });
            return (long)result == 1;
        }
        public bool IsAllowedSliding(string key, int maxRequests, TimeSpan window)
        {
            var db = _connectionManager.GetDatabase();
            var fullKey = BuildKey(key);
            var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
            var windowMs = (long)window.TotalMilliseconds;
            var tran = db.CreateTransaction();
            tran.SortedSetRemoveRangeByScoreAsync(fullKey, 0, now - windowMs);
            tran.SortedSetAddAsync(fullKey, now.ToString(), now);
            tran.KeyExpireAsync(fullKey, window.Add(TimeSpan.FromSeconds(1)));
            tran.Execute();
            var count = db.SortedSetLength(fullKey);
            if (count > maxRequests)
            {
                db.SortedSetRemove(fullKey, now.ToString());
                return false;
            }
            return true;
        }
        public bool TryAcquireToken(string key, int maxTokens, int refillRate, TimeSpan refillInterval)
        {
            var db = _connectionManager.GetDatabase();
            var fullKey = BuildKey($"token:{key}");
            var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
            var script = @"
                local key = KEYS[1]
                local max = tonumber(ARGV[1])
                local rate = tonumber(ARGV[2])
                local interval = tonumber(ARGV[3])
                local now = tonumber(ARGV[4])
                local info = redis.call('HMGET', key, 'tokens', 'last')
                local tokens = tonumber(info[1]) or max
                local last = tonumber(info[2]) or now
                local elapsed = now - last
                local refill = math.floor(elapsed / interval) * rate
                tokens = math.min(max, tokens + refill)
                if tokens > 0 then
                    tokens = tokens - 1
                    redis.call('HMSET', key, 'tokens', tokens, 'last', now)
                    redis.call('PEXPIRE', key, interval * max / rate * 2)
                    return 1
                end
                return 0";
            var result = db.ScriptEvaluate(script,
                new RedisKey[] { fullKey },
                new RedisValue[] { maxTokens, refillRate, (long)refillInterval.TotalMilliseconds, now });
            return (long)result == 1;
        }
        public long GetRemainingRequests(string key, int maxRequests, TimeSpan window)
        {
            var db = _connectionManager.GetDatabase();
            var val = db.StringGet(BuildKey(key));
            if (val.IsNullOrEmpty) return maxRequests;
            return Math.Max(0, maxRequests - (long)val);
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Serialization/IRedisSerializer.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,11 @@
namespace WIDESEAWCS_RedisService.Serialization
{
    public interface IRedisSerializer
    {
        string Serialize<T>(T value);
        T? Deserialize<T>(string value);
        object? Deserialize(string value, Type type);
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Serialization/NewtonsoftRedisSerializer.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
using Newtonsoft.Json;
namespace WIDESEAWCS_RedisService.Serialization
{
    public class NewtonsoftRedisSerializer : IRedisSerializer
    {
        private static readonly JsonSerializerSettings _settings = new()
        {
            ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
            NullValueHandling = NullValueHandling.Ignore,
            DateFormatString = "yyyy-MM-dd HH:mm:ss"
        };
        public string Serialize<T>(T value)
        {
            return JsonConvert.SerializeObject(value, _settings);
        }
        public T? Deserialize<T>(string value)
        {
            if (string.IsNullOrEmpty(value)) return default;
            return JsonConvert.DeserializeObject<T>(value, _settings);
        }
        public object? Deserialize(string value, Type type)
        {
            if (string.IsNullOrEmpty(value)) return null;
            return JsonConvert.DeserializeObject(value, type, _settings);
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Session/ISessionStorage.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,12 @@
namespace WIDESEAWCS_RedisService.Session
{
    public interface ISessionStorage
    {
        bool Set(string sessionId, string key, string value, TimeSpan? expiry = null);
        string? Get(string sessionId, string key);
        bool Remove(string sessionId, string key);
        bool DestroySession(string sessionId);
        bool SessionExists(string sessionId);
        bool RefreshSession(string sessionId, TimeSpan expiry);
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Session/RedisSessionStorage.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,63 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StackExchange.Redis;
using WIDESEAWCS_RedisService.Connection;
using WIDESEAWCS_RedisService.Options;
namespace WIDESEAWCS_RedisService.Session
{
    public class RedisSessionStorage : ISessionStorage
    {
        private readonly IRedisConnectionManager _connectionManager;
        private readonly RedisOptions _options;
        private readonly ILogger<RedisSessionStorage> _logger;
        public RedisSessionStorage(
            IRedisConnectionManager connectionManager,
            IOptions<RedisOptions> options,
            ILogger<RedisSessionStorage> logger)
        {
            _connectionManager = connectionManager;
            _options = options.Value;
            _logger = logger;
        }
        private string BuildKey(string sessionId) => $"{_options.KeyPrefix}session:{sessionId}";
        public bool Set(string sessionId, string key, string value, TimeSpan? expiry = null)
        {
            var db = _connectionManager.GetDatabase();
            var fullKey = BuildKey(sessionId);
            db.HashSet(fullKey, key, value);
            if (expiry.HasValue)
                db.KeyExpire(fullKey, expiry);
            return true;
        }
        public string? Get(string sessionId, string key)
        {
            var val = _connectionManager.GetDatabase().HashGet(BuildKey(sessionId), key);
            return val.IsNullOrEmpty ? null : val.ToString();
        }
        public bool Remove(string sessionId, string key)
        {
            return _connectionManager.GetDatabase().HashDelete(BuildKey(sessionId), key);
        }
        public bool DestroySession(string sessionId)
        {
            return _connectionManager.GetDatabase().KeyDelete(BuildKey(sessionId));
        }
        public bool SessionExists(string sessionId)
        {
            return _connectionManager.GetDatabase().KeyExists(BuildKey(sessionId));
        }
        public bool RefreshSession(string sessionId, TimeSpan expiry)
        {
            return _connectionManager.GetDatabase().KeyExpire(BuildKey(sessionId), expiry);
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Storage/IObjectStorageService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,40 @@
namespace WIDESEAWCS_RedisService.Storage
{
    public interface IObjectStorageService
    {
        /// <summary>
        /// å­˜å‚¨å¯¹è±¡ï¼ˆHash结构)
        /// </summary>
        bool SetObject<T>(string key, T value, TimeSpan? expiry = null) where T : class;
        /// <summary>
        /// èŽ·å–å¯¹è±¡
        /// </summary>
        T? GetObject<T>(string key) where T : class;
        /// <summary>
        /// è®¾ç½®å¯¹è±¡çš„æŸä¸ªå­—段
        /// </summary>
        bool SetField(string key, string field, string value);
        /// <summary>
        /// èŽ·å–å¯¹è±¡çš„æŸä¸ªå­—æ®µ
        /// </summary>
        string? GetField(string key, string field);
        /// <summary>
        /// åˆ é™¤å¯¹è±¡
        /// </summary>
        bool Delete(string key);
        /// <summary>
        /// åˆ é™¤å¯¹è±¡çš„æŸä¸ªå­—段
        /// </summary>
        bool DeleteField(string key, string field);
        /// <summary>
        /// åˆ¤æ–­å¯¹è±¡æ˜¯å¦å­˜åœ¨
        /// </summary>
        bool Exists(string key);
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Storage/RedisObjectStorageService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,71 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StackExchange.Redis;
using WIDESEAWCS_RedisService.Connection;
using WIDESEAWCS_RedisService.Options;
using WIDESEAWCS_RedisService.Serialization;
namespace WIDESEAWCS_RedisService.Storage
{
    public class RedisObjectStorageService : IObjectStorageService
    {
        private readonly IRedisConnectionManager _connectionManager;
        private readonly IRedisSerializer _serializer;
        private readonly RedisOptions _options;
        private readonly ILogger<RedisObjectStorageService> _logger;
        public RedisObjectStorageService(
            IRedisConnectionManager connectionManager,
            IRedisSerializer serializer,
            IOptions<RedisOptions> options,
            ILogger<RedisObjectStorageService> logger)
        {
            _connectionManager = connectionManager;
            _serializer = serializer;
            _options = options.Value;
            _logger = logger;
        }
        private string BuildKey(string key) => $"{_options.KeyPrefix}obj:{key}";
        public bool SetObject<T>(string key, T value, TimeSpan? expiry = null) where T : class
        {
            var db = _connectionManager.GetDatabase();
            var fullKey = BuildKey(key);
            var json = _serializer.Serialize(value);
            return db.StringSet(fullKey, json, expiry);
        }
        public T? GetObject<T>(string key) where T : class
        {
            var val = _connectionManager.GetDatabase().StringGet(BuildKey(key));
            return val.IsNullOrEmpty ? default : _serializer.Deserialize<T>(val!);
        }
        public bool SetField(string key, string field, string value)
        {
            return _connectionManager.GetDatabase().HashSet(BuildKey(key), field, value);
        }
        public string? GetField(string key, string field)
        {
            var val = _connectionManager.GetDatabase().HashGet(BuildKey(key), field);
            return val.IsNullOrEmpty ? null : val.ToString();
        }
        public bool Delete(string key)
        {
            return _connectionManager.GetDatabase().KeyDelete(BuildKey(key));
        }
        public bool DeleteField(string key, string field)
        {
            return _connectionManager.GetDatabase().HashDelete(BuildKey(key), field);
        }
        public bool Exists(string key)
        {
            return _connectionManager.GetDatabase().KeyExists(BuildKey(key));
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Stream/IStreamProcessingService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
namespace WIDESEAWCS_RedisService.Stream
{
    public interface IStreamProcessingService
    {
        /// <summary>
        /// å‘送消息到Stream
        /// </summary>
        string AddMessage(string streamKey, Dictionary<string, string> fields);
        /// <summary>
        /// åˆ›å»ºæ¶ˆè´¹è€…组
        /// </summary>
        bool CreateConsumerGroup(string streamKey, string groupName, string startId = "$");
        /// <summary>
        /// è¯»å–消息(消费者组模式)
        /// </summary>
        List<StreamEntry> ReadGroup(string streamKey, string groupName, string consumerName, int count = 10);
        /// <summary>
        /// ç¡®è®¤æ¶ˆæ¯
        /// </summary>
        long Acknowledge(string streamKey, string groupName, params string[] messageIds);
        /// <summary>
        /// èŽ·å–Stream长度
        /// </summary>
        long GetStreamLength(string streamKey);
        /// <summary>
        /// è£å‰ªStream
        /// </summary>
        long TrimStream(string streamKey, int maxLength);
    }
    public class StreamEntry
    {
        public string Id { get; set; } = string.Empty;
        public Dictionary<string, string> Fields { get; set; } = new();
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Stream/RedisStreamProcessingService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,76 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StackExchange.Redis;
using WIDESEAWCS_RedisService.Connection;
using WIDESEAWCS_RedisService.Options;
namespace WIDESEAWCS_RedisService.Stream
{
    public class RedisStreamProcessingService : IStreamProcessingService
    {
        private readonly IRedisConnectionManager _connectionManager;
        private readonly RedisOptions _options;
        private readonly ILogger<RedisStreamProcessingService> _logger;
        public RedisStreamProcessingService(
            IRedisConnectionManager connectionManager,
            IOptions<RedisOptions> options,
            ILogger<RedisStreamProcessingService> logger)
        {
            _connectionManager = connectionManager;
            _options = options.Value;
            _logger = logger;
        }
        private string BuildKey(string key) => $"{_options.KeyPrefix}stream:{key}";
        public string AddMessage(string streamKey, Dictionary<string, string> fields)
        {
            var entries = fields.Select(f => new NameValueEntry(f.Key, f.Value)).ToArray();
            var id = _connectionManager.GetDatabase().StreamAdd(BuildKey(streamKey), entries);
            return id.ToString();
        }
        public bool CreateConsumerGroup(string streamKey, string groupName, string startId = "$")
        {
            try
            {
                return _connectionManager.GetDatabase()
                    .StreamCreateConsumerGroup(BuildKey(streamKey), groupName, startId, true);
            }
            catch (RedisServerException ex) when (ex.Message.Contains("BUSYGROUP"))
            {
                _logger.LogWarning("消费者组 {Group} å·²å­˜åœ¨", groupName);
                return true;
            }
        }
        public List<StreamEntry> ReadGroup(string streamKey, string groupName, string consumerName, int count = 10)
        {
            var entries = _connectionManager.GetDatabase()
                .StreamReadGroup(BuildKey(streamKey), groupName, consumerName, ">", count);
            return entries?.Select(e => new StreamEntry
            {
                Id = e.Id.ToString(),
                Fields = e.Values.ToDictionary(v => v.Name.ToString(), v => v.Value.ToString())
            }).ToList() ?? new List<StreamEntry>();
        }
        public long Acknowledge(string streamKey, string groupName, params string[] messageIds)
        {
            var ids = messageIds.Select(id => new RedisValue(id)).ToArray();
            return _connectionManager.GetDatabase().StreamAcknowledge(BuildKey(streamKey), groupName, ids);
        }
        public long GetStreamLength(string streamKey)
        {
            return _connectionManager.GetDatabase().StreamLength(BuildKey(streamKey));
        }
        public long TrimStream(string streamKey, int maxLength)
        {
            return _connectionManager.GetDatabase().StreamTrim(BuildKey(streamKey), maxLength);
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/WIDESEAWCS_RedisService.csproj
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <TargetFramework>net6.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
    </PropertyGroup>
    <ItemGroup>
        <PackageReference Include="StackExchange.Redis" Version="2.7.33" />
        <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
        <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="6.0.1" />
        <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="6.0.0" />
        <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
    </ItemGroup>
    <ItemGroup>
        <ProjectReference Include="..\WIDESEAWCS_Core\WIDESEAWCS_Core.csproj" />
    </ItemGroup>
</Project>
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server.sln
@@ -64,92 +64,270 @@
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WIDESEAWCS_BasicInfoService", "WIDESEAWCS_BasicInfoService\WIDESEAWCS_BasicInfoService.csproj", "{FFAB2C76-1C9E-4006-95C8-A0B2AA53139D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WIDESEAWCS_RedisService", "WIDESEAWCS_RedisService\WIDESEAWCS_RedisService.csproj", "{F9886971-C3B2-4334-B014-D5109F2041DE}"
EndProject
Global
    GlobalSection(SolutionConfigurationPlatforms) = preSolution
        Debug|Any CPU = Debug|Any CPU
        Debug|x64 = Debug|x64
        Debug|x86 = Debug|x86
        Release|Any CPU = Release|Any CPU
        Release|x64 = Release|x64
        Release|x86 = Release|x86
    EndGlobalSection
    GlobalSection(ProjectConfigurationPlatforms) = postSolution
        {487FA45B-EA1A-4ACA-BB5B-0F6708F462C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {487FA45B-EA1A-4ACA-BB5B-0F6708F462C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {487FA45B-EA1A-4ACA-BB5B-0F6708F462C0}.Debug|x64.ActiveCfg = Debug|Any CPU
        {487FA45B-EA1A-4ACA-BB5B-0F6708F462C0}.Debug|x64.Build.0 = Debug|Any CPU
        {487FA45B-EA1A-4ACA-BB5B-0F6708F462C0}.Debug|x86.ActiveCfg = Debug|Any CPU
        {487FA45B-EA1A-4ACA-BB5B-0F6708F462C0}.Debug|x86.Build.0 = Debug|Any CPU
        {487FA45B-EA1A-4ACA-BB5B-0F6708F462C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {487FA45B-EA1A-4ACA-BB5B-0F6708F462C0}.Release|Any CPU.Build.0 = Release|Any CPU
        {487FA45B-EA1A-4ACA-BB5B-0F6708F462C0}.Release|x64.ActiveCfg = Release|Any CPU
        {487FA45B-EA1A-4ACA-BB5B-0F6708F462C0}.Release|x64.Build.0 = Release|Any CPU
        {487FA45B-EA1A-4ACA-BB5B-0F6708F462C0}.Release|x86.ActiveCfg = Release|Any CPU
        {487FA45B-EA1A-4ACA-BB5B-0F6708F462C0}.Release|x86.Build.0 = Release|Any CPU
        {294E4915-0241-4C8C-BA99-7588B945863A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {294E4915-0241-4C8C-BA99-7588B945863A}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {294E4915-0241-4C8C-BA99-7588B945863A}.Debug|x64.ActiveCfg = Debug|Any CPU
        {294E4915-0241-4C8C-BA99-7588B945863A}.Debug|x64.Build.0 = Debug|Any CPU
        {294E4915-0241-4C8C-BA99-7588B945863A}.Debug|x86.ActiveCfg = Debug|Any CPU
        {294E4915-0241-4C8C-BA99-7588B945863A}.Debug|x86.Build.0 = Debug|Any CPU
        {294E4915-0241-4C8C-BA99-7588B945863A}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {294E4915-0241-4C8C-BA99-7588B945863A}.Release|Any CPU.Build.0 = Release|Any CPU
        {294E4915-0241-4C8C-BA99-7588B945863A}.Release|x64.ActiveCfg = Release|Any CPU
        {294E4915-0241-4C8C-BA99-7588B945863A}.Release|x64.Build.0 = Release|Any CPU
        {294E4915-0241-4C8C-BA99-7588B945863A}.Release|x86.ActiveCfg = Release|Any CPU
        {294E4915-0241-4C8C-BA99-7588B945863A}.Release|x86.Build.0 = Release|Any CPU
        {6236BFFF-173D-44A8-9FC3-7C001EA30347}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {6236BFFF-173D-44A8-9FC3-7C001EA30347}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {6236BFFF-173D-44A8-9FC3-7C001EA30347}.Debug|x64.ActiveCfg = Debug|Any CPU
        {6236BFFF-173D-44A8-9FC3-7C001EA30347}.Debug|x64.Build.0 = Debug|Any CPU
        {6236BFFF-173D-44A8-9FC3-7C001EA30347}.Debug|x86.ActiveCfg = Debug|Any CPU
        {6236BFFF-173D-44A8-9FC3-7C001EA30347}.Debug|x86.Build.0 = Debug|Any CPU
        {6236BFFF-173D-44A8-9FC3-7C001EA30347}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {6236BFFF-173D-44A8-9FC3-7C001EA30347}.Release|Any CPU.Build.0 = Release|Any CPU
        {6236BFFF-173D-44A8-9FC3-7C001EA30347}.Release|x64.ActiveCfg = Release|Any CPU
        {6236BFFF-173D-44A8-9FC3-7C001EA30347}.Release|x64.Build.0 = Release|Any CPU
        {6236BFFF-173D-44A8-9FC3-7C001EA30347}.Release|x86.ActiveCfg = Release|Any CPU
        {6236BFFF-173D-44A8-9FC3-7C001EA30347}.Release|x86.Build.0 = Release|Any CPU
        {7F200FE8-CAF6-4131-BD25-8D438FE0ABAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {7F200FE8-CAF6-4131-BD25-8D438FE0ABAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {7F200FE8-CAF6-4131-BD25-8D438FE0ABAC}.Debug|x64.ActiveCfg = Debug|Any CPU
        {7F200FE8-CAF6-4131-BD25-8D438FE0ABAC}.Debug|x64.Build.0 = Debug|Any CPU
        {7F200FE8-CAF6-4131-BD25-8D438FE0ABAC}.Debug|x86.ActiveCfg = Debug|Any CPU
        {7F200FE8-CAF6-4131-BD25-8D438FE0ABAC}.Debug|x86.Build.0 = Debug|Any CPU
        {7F200FE8-CAF6-4131-BD25-8D438FE0ABAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {7F200FE8-CAF6-4131-BD25-8D438FE0ABAC}.Release|Any CPU.Build.0 = Release|Any CPU
        {7F200FE8-CAF6-4131-BD25-8D438FE0ABAC}.Release|x64.ActiveCfg = Release|Any CPU
        {7F200FE8-CAF6-4131-BD25-8D438FE0ABAC}.Release|x64.Build.0 = Release|Any CPU
        {7F200FE8-CAF6-4131-BD25-8D438FE0ABAC}.Release|x86.ActiveCfg = Release|Any CPU
        {7F200FE8-CAF6-4131-BD25-8D438FE0ABAC}.Release|x86.Build.0 = Release|Any CPU
        {C2D3D138-9109-481B-8BEB-A27597890B2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {C2D3D138-9109-481B-8BEB-A27597890B2C}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {C2D3D138-9109-481B-8BEB-A27597890B2C}.Debug|x64.ActiveCfg = Debug|Any CPU
        {C2D3D138-9109-481B-8BEB-A27597890B2C}.Debug|x64.Build.0 = Debug|Any CPU
        {C2D3D138-9109-481B-8BEB-A27597890B2C}.Debug|x86.ActiveCfg = Debug|Any CPU
        {C2D3D138-9109-481B-8BEB-A27597890B2C}.Debug|x86.Build.0 = Debug|Any CPU
        {C2D3D138-9109-481B-8BEB-A27597890B2C}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {C2D3D138-9109-481B-8BEB-A27597890B2C}.Release|Any CPU.Build.0 = Release|Any CPU
        {C2D3D138-9109-481B-8BEB-A27597890B2C}.Release|x64.ActiveCfg = Release|Any CPU
        {C2D3D138-9109-481B-8BEB-A27597890B2C}.Release|x64.Build.0 = Release|Any CPU
        {C2D3D138-9109-481B-8BEB-A27597890B2C}.Release|x86.ActiveCfg = Release|Any CPU
        {C2D3D138-9109-481B-8BEB-A27597890B2C}.Release|x86.Build.0 = Release|Any CPU
        {BFFDD936-2E61-4D3A-ABFE-7CF77FE0B184}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {BFFDD936-2E61-4D3A-ABFE-7CF77FE0B184}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {BFFDD936-2E61-4D3A-ABFE-7CF77FE0B184}.Debug|x64.ActiveCfg = Debug|Any CPU
        {BFFDD936-2E61-4D3A-ABFE-7CF77FE0B184}.Debug|x64.Build.0 = Debug|Any CPU
        {BFFDD936-2E61-4D3A-ABFE-7CF77FE0B184}.Debug|x86.ActiveCfg = Debug|Any CPU
        {BFFDD936-2E61-4D3A-ABFE-7CF77FE0B184}.Debug|x86.Build.0 = Debug|Any CPU
        {BFFDD936-2E61-4D3A-ABFE-7CF77FE0B184}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {BFFDD936-2E61-4D3A-ABFE-7CF77FE0B184}.Release|Any CPU.Build.0 = Release|Any CPU
        {BFFDD936-2E61-4D3A-ABFE-7CF77FE0B184}.Release|x64.ActiveCfg = Release|Any CPU
        {BFFDD936-2E61-4D3A-ABFE-7CF77FE0B184}.Release|x64.Build.0 = Release|Any CPU
        {BFFDD936-2E61-4D3A-ABFE-7CF77FE0B184}.Release|x86.ActiveCfg = Release|Any CPU
        {BFFDD936-2E61-4D3A-ABFE-7CF77FE0B184}.Release|x86.Build.0 = Release|Any CPU
        {861C4D0B-A478-48DB-A0FA-AE70F5BA210A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {861C4D0B-A478-48DB-A0FA-AE70F5BA210A}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {861C4D0B-A478-48DB-A0FA-AE70F5BA210A}.Debug|x64.ActiveCfg = Debug|Any CPU
        {861C4D0B-A478-48DB-A0FA-AE70F5BA210A}.Debug|x64.Build.0 = Debug|Any CPU
        {861C4D0B-A478-48DB-A0FA-AE70F5BA210A}.Debug|x86.ActiveCfg = Debug|Any CPU
        {861C4D0B-A478-48DB-A0FA-AE70F5BA210A}.Debug|x86.Build.0 = Debug|Any CPU
        {861C4D0B-A478-48DB-A0FA-AE70F5BA210A}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {861C4D0B-A478-48DB-A0FA-AE70F5BA210A}.Release|Any CPU.Build.0 = Release|Any CPU
        {861C4D0B-A478-48DB-A0FA-AE70F5BA210A}.Release|x64.ActiveCfg = Release|Any CPU
        {861C4D0B-A478-48DB-A0FA-AE70F5BA210A}.Release|x64.Build.0 = Release|Any CPU
        {861C4D0B-A478-48DB-A0FA-AE70F5BA210A}.Release|x86.ActiveCfg = Release|Any CPU
        {861C4D0B-A478-48DB-A0FA-AE70F5BA210A}.Release|x86.Build.0 = Release|Any CPU
        {9FBC654C-51DE-422D-9E1E-6A38268DE1E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {9FBC654C-51DE-422D-9E1E-6A38268DE1E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {9FBC654C-51DE-422D-9E1E-6A38268DE1E2}.Debug|x64.ActiveCfg = Debug|Any CPU
        {9FBC654C-51DE-422D-9E1E-6A38268DE1E2}.Debug|x64.Build.0 = Debug|Any CPU
        {9FBC654C-51DE-422D-9E1E-6A38268DE1E2}.Debug|x86.ActiveCfg = Debug|Any CPU
        {9FBC654C-51DE-422D-9E1E-6A38268DE1E2}.Debug|x86.Build.0 = Debug|Any CPU
        {9FBC654C-51DE-422D-9E1E-6A38268DE1E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {9FBC654C-51DE-422D-9E1E-6A38268DE1E2}.Release|Any CPU.Build.0 = Release|Any CPU
        {9FBC654C-51DE-422D-9E1E-6A38268DE1E2}.Release|x64.ActiveCfg = Release|Any CPU
        {9FBC654C-51DE-422D-9E1E-6A38268DE1E2}.Release|x64.Build.0 = Release|Any CPU
        {9FBC654C-51DE-422D-9E1E-6A38268DE1E2}.Release|x86.ActiveCfg = Release|Any CPU
        {9FBC654C-51DE-422D-9E1E-6A38268DE1E2}.Release|x86.Build.0 = Release|Any CPU
        {F302E6D6-5A95-4D22-8DC2-21BE2CB30275}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {F302E6D6-5A95-4D22-8DC2-21BE2CB30275}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {F302E6D6-5A95-4D22-8DC2-21BE2CB30275}.Debug|x64.ActiveCfg = Debug|Any CPU
        {F302E6D6-5A95-4D22-8DC2-21BE2CB30275}.Debug|x64.Build.0 = Debug|Any CPU
        {F302E6D6-5A95-4D22-8DC2-21BE2CB30275}.Debug|x86.ActiveCfg = Debug|Any CPU
        {F302E6D6-5A95-4D22-8DC2-21BE2CB30275}.Debug|x86.Build.0 = Debug|Any CPU
        {F302E6D6-5A95-4D22-8DC2-21BE2CB30275}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {F302E6D6-5A95-4D22-8DC2-21BE2CB30275}.Release|Any CPU.Build.0 = Release|Any CPU
        {F302E6D6-5A95-4D22-8DC2-21BE2CB30275}.Release|x64.ActiveCfg = Release|Any CPU
        {F302E6D6-5A95-4D22-8DC2-21BE2CB30275}.Release|x64.Build.0 = Release|Any CPU
        {F302E6D6-5A95-4D22-8DC2-21BE2CB30275}.Release|x86.ActiveCfg = Release|Any CPU
        {F302E6D6-5A95-4D22-8DC2-21BE2CB30275}.Release|x86.Build.0 = Release|Any CPU
        {5777BDEC-4726-4425-85F2-A090524F692D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {5777BDEC-4726-4425-85F2-A090524F692D}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {5777BDEC-4726-4425-85F2-A090524F692D}.Debug|x64.ActiveCfg = Debug|Any CPU
        {5777BDEC-4726-4425-85F2-A090524F692D}.Debug|x64.Build.0 = Debug|Any CPU
        {5777BDEC-4726-4425-85F2-A090524F692D}.Debug|x86.ActiveCfg = Debug|Any CPU
        {5777BDEC-4726-4425-85F2-A090524F692D}.Debug|x86.Build.0 = Debug|Any CPU
        {5777BDEC-4726-4425-85F2-A090524F692D}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {5777BDEC-4726-4425-85F2-A090524F692D}.Release|Any CPU.Build.0 = Release|Any CPU
        {5777BDEC-4726-4425-85F2-A090524F692D}.Release|x64.ActiveCfg = Release|Any CPU
        {5777BDEC-4726-4425-85F2-A090524F692D}.Release|x64.Build.0 = Release|Any CPU
        {5777BDEC-4726-4425-85F2-A090524F692D}.Release|x86.ActiveCfg = Release|Any CPU
        {5777BDEC-4726-4425-85F2-A090524F692D}.Release|x86.Build.0 = Release|Any CPU
        {09D05F35-CEA2-48D9-86D0-FB95982BA511}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {09D05F35-CEA2-48D9-86D0-FB95982BA511}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {09D05F35-CEA2-48D9-86D0-FB95982BA511}.Debug|x64.ActiveCfg = Debug|Any CPU
        {09D05F35-CEA2-48D9-86D0-FB95982BA511}.Debug|x64.Build.0 = Debug|Any CPU
        {09D05F35-CEA2-48D9-86D0-FB95982BA511}.Debug|x86.ActiveCfg = Debug|Any CPU
        {09D05F35-CEA2-48D9-86D0-FB95982BA511}.Debug|x86.Build.0 = Debug|Any CPU
        {09D05F35-CEA2-48D9-86D0-FB95982BA511}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {09D05F35-CEA2-48D9-86D0-FB95982BA511}.Release|Any CPU.Build.0 = Release|Any CPU
        {09D05F35-CEA2-48D9-86D0-FB95982BA511}.Release|x64.ActiveCfg = Release|Any CPU
        {09D05F35-CEA2-48D9-86D0-FB95982BA511}.Release|x64.Build.0 = Release|Any CPU
        {09D05F35-CEA2-48D9-86D0-FB95982BA511}.Release|x86.ActiveCfg = Release|Any CPU
        {09D05F35-CEA2-48D9-86D0-FB95982BA511}.Release|x86.Build.0 = Release|Any CPU
        {35054AA5-CF40-4F38-9414-C76742C29382}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {35054AA5-CF40-4F38-9414-C76742C29382}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {35054AA5-CF40-4F38-9414-C76742C29382}.Debug|x64.ActiveCfg = Debug|Any CPU
        {35054AA5-CF40-4F38-9414-C76742C29382}.Debug|x64.Build.0 = Debug|Any CPU
        {35054AA5-CF40-4F38-9414-C76742C29382}.Debug|x86.ActiveCfg = Debug|Any CPU
        {35054AA5-CF40-4F38-9414-C76742C29382}.Debug|x86.Build.0 = Debug|Any CPU
        {35054AA5-CF40-4F38-9414-C76742C29382}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {35054AA5-CF40-4F38-9414-C76742C29382}.Release|Any CPU.Build.0 = Release|Any CPU
        {35054AA5-CF40-4F38-9414-C76742C29382}.Release|x64.ActiveCfg = Release|Any CPU
        {35054AA5-CF40-4F38-9414-C76742C29382}.Release|x64.Build.0 = Release|Any CPU
        {35054AA5-CF40-4F38-9414-C76742C29382}.Release|x86.ActiveCfg = Release|Any CPU
        {35054AA5-CF40-4F38-9414-C76742C29382}.Release|x86.Build.0 = Release|Any CPU
        {9E4BFF47-52BF-4FD8-9CC7-3763BF19D9E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {9E4BFF47-52BF-4FD8-9CC7-3763BF19D9E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {9E4BFF47-52BF-4FD8-9CC7-3763BF19D9E0}.Debug|x64.ActiveCfg = Debug|Any CPU
        {9E4BFF47-52BF-4FD8-9CC7-3763BF19D9E0}.Debug|x64.Build.0 = Debug|Any CPU
        {9E4BFF47-52BF-4FD8-9CC7-3763BF19D9E0}.Debug|x86.ActiveCfg = Debug|Any CPU
        {9E4BFF47-52BF-4FD8-9CC7-3763BF19D9E0}.Debug|x86.Build.0 = Debug|Any CPU
        {9E4BFF47-52BF-4FD8-9CC7-3763BF19D9E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {9E4BFF47-52BF-4FD8-9CC7-3763BF19D9E0}.Release|Any CPU.Build.0 = Release|Any CPU
        {9E4BFF47-52BF-4FD8-9CC7-3763BF19D9E0}.Release|x64.ActiveCfg = Release|Any CPU
        {9E4BFF47-52BF-4FD8-9CC7-3763BF19D9E0}.Release|x64.Build.0 = Release|Any CPU
        {9E4BFF47-52BF-4FD8-9CC7-3763BF19D9E0}.Release|x86.ActiveCfg = Release|Any CPU
        {9E4BFF47-52BF-4FD8-9CC7-3763BF19D9E0}.Release|x86.Build.0 = Release|Any CPU
        {A14242DD-DA06-4DC3-8598-1761AA7C76D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {A14242DD-DA06-4DC3-8598-1761AA7C76D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {A14242DD-DA06-4DC3-8598-1761AA7C76D1}.Debug|x64.ActiveCfg = Debug|Any CPU
        {A14242DD-DA06-4DC3-8598-1761AA7C76D1}.Debug|x64.Build.0 = Debug|Any CPU
        {A14242DD-DA06-4DC3-8598-1761AA7C76D1}.Debug|x86.ActiveCfg = Debug|Any CPU
        {A14242DD-DA06-4DC3-8598-1761AA7C76D1}.Debug|x86.Build.0 = Debug|Any CPU
        {A14242DD-DA06-4DC3-8598-1761AA7C76D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {A14242DD-DA06-4DC3-8598-1761AA7C76D1}.Release|Any CPU.Build.0 = Release|Any CPU
        {A14242DD-DA06-4DC3-8598-1761AA7C76D1}.Release|x64.ActiveCfg = Release|Any CPU
        {A14242DD-DA06-4DC3-8598-1761AA7C76D1}.Release|x64.Build.0 = Release|Any CPU
        {A14242DD-DA06-4DC3-8598-1761AA7C76D1}.Release|x86.ActiveCfg = Release|Any CPU
        {A14242DD-DA06-4DC3-8598-1761AA7C76D1}.Release|x86.Build.0 = Release|Any CPU
        {7279A2AE-8D1F-4E66-A73A-01AF7927A336}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {7279A2AE-8D1F-4E66-A73A-01AF7927A336}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {7279A2AE-8D1F-4E66-A73A-01AF7927A336}.Debug|x64.ActiveCfg = Debug|Any CPU
        {7279A2AE-8D1F-4E66-A73A-01AF7927A336}.Debug|x64.Build.0 = Debug|Any CPU
        {7279A2AE-8D1F-4E66-A73A-01AF7927A336}.Debug|x86.ActiveCfg = Debug|Any CPU
        {7279A2AE-8D1F-4E66-A73A-01AF7927A336}.Debug|x86.Build.0 = Debug|Any CPU
        {7279A2AE-8D1F-4E66-A73A-01AF7927A336}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {7279A2AE-8D1F-4E66-A73A-01AF7927A336}.Release|Any CPU.Build.0 = Release|Any CPU
        {7279A2AE-8D1F-4E66-A73A-01AF7927A336}.Release|x64.ActiveCfg = Release|Any CPU
        {7279A2AE-8D1F-4E66-A73A-01AF7927A336}.Release|x64.Build.0 = Release|Any CPU
        {7279A2AE-8D1F-4E66-A73A-01AF7927A336}.Release|x86.ActiveCfg = Release|Any CPU
        {7279A2AE-8D1F-4E66-A73A-01AF7927A336}.Release|x86.Build.0 = Release|Any CPU
        {83F18A31-5983-4587-A0B2-414BF70E50B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {83F18A31-5983-4587-A0B2-414BF70E50B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {83F18A31-5983-4587-A0B2-414BF70E50B5}.Debug|x64.ActiveCfg = Debug|Any CPU
        {83F18A31-5983-4587-A0B2-414BF70E50B5}.Debug|x64.Build.0 = Debug|Any CPU
        {83F18A31-5983-4587-A0B2-414BF70E50B5}.Debug|x86.ActiveCfg = Debug|Any CPU
        {83F18A31-5983-4587-A0B2-414BF70E50B5}.Debug|x86.Build.0 = Debug|Any CPU
        {83F18A31-5983-4587-A0B2-414BF70E50B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {83F18A31-5983-4587-A0B2-414BF70E50B5}.Release|Any CPU.Build.0 = Release|Any CPU
        {83F18A31-5983-4587-A0B2-414BF70E50B5}.Release|x64.ActiveCfg = Release|Any CPU
        {83F18A31-5983-4587-A0B2-414BF70E50B5}.Release|x64.Build.0 = Release|Any CPU
        {83F18A31-5983-4587-A0B2-414BF70E50B5}.Release|x86.ActiveCfg = Release|Any CPU
        {83F18A31-5983-4587-A0B2-414BF70E50B5}.Release|x86.Build.0 = Release|Any CPU
        {266D07B7-3648-4F3D-818A-89EDA7D84C58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {266D07B7-3648-4F3D-818A-89EDA7D84C58}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {266D07B7-3648-4F3D-818A-89EDA7D84C58}.Debug|x64.ActiveCfg = Debug|Any CPU
        {266D07B7-3648-4F3D-818A-89EDA7D84C58}.Debug|x64.Build.0 = Debug|Any CPU
        {266D07B7-3648-4F3D-818A-89EDA7D84C58}.Debug|x86.ActiveCfg = Debug|Any CPU
        {266D07B7-3648-4F3D-818A-89EDA7D84C58}.Debug|x86.Build.0 = Debug|Any CPU
        {266D07B7-3648-4F3D-818A-89EDA7D84C58}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {266D07B7-3648-4F3D-818A-89EDA7D84C58}.Release|Any CPU.Build.0 = Release|Any CPU
        {266D07B7-3648-4F3D-818A-89EDA7D84C58}.Release|x64.ActiveCfg = Release|Any CPU
        {266D07B7-3648-4F3D-818A-89EDA7D84C58}.Release|x64.Build.0 = Release|Any CPU
        {266D07B7-3648-4F3D-818A-89EDA7D84C58}.Release|x86.ActiveCfg = Release|Any CPU
        {266D07B7-3648-4F3D-818A-89EDA7D84C58}.Release|x86.Build.0 = Release|Any CPU
        {206FDF79-9BF3-433A-B7FF-627287BBD760}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {206FDF79-9BF3-433A-B7FF-627287BBD760}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {206FDF79-9BF3-433A-B7FF-627287BBD760}.Debug|x64.ActiveCfg = Debug|Any CPU
        {206FDF79-9BF3-433A-B7FF-627287BBD760}.Debug|x64.Build.0 = Debug|Any CPU
        {206FDF79-9BF3-433A-B7FF-627287BBD760}.Debug|x86.ActiveCfg = Debug|Any CPU
        {206FDF79-9BF3-433A-B7FF-627287BBD760}.Debug|x86.Build.0 = Debug|Any CPU
        {206FDF79-9BF3-433A-B7FF-627287BBD760}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {206FDF79-9BF3-433A-B7FF-627287BBD760}.Release|Any CPU.Build.0 = Release|Any CPU
        {206FDF79-9BF3-433A-B7FF-627287BBD760}.Release|x64.ActiveCfg = Release|Any CPU
        {206FDF79-9BF3-433A-B7FF-627287BBD760}.Release|x64.Build.0 = Release|Any CPU
        {206FDF79-9BF3-433A-B7FF-627287BBD760}.Release|x86.ActiveCfg = Release|Any CPU
        {206FDF79-9BF3-433A-B7FF-627287BBD760}.Release|x86.Build.0 = Release|Any CPU
        {8C2CC25B-DE5D-433E-A550-63864C7A716D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {8C2CC25B-DE5D-433E-A550-63864C7A716D}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {8C2CC25B-DE5D-433E-A550-63864C7A716D}.Debug|x64.ActiveCfg = Debug|Any CPU
        {8C2CC25B-DE5D-433E-A550-63864C7A716D}.Debug|x64.Build.0 = Debug|Any CPU
        {8C2CC25B-DE5D-433E-A550-63864C7A716D}.Debug|x86.ActiveCfg = Debug|Any CPU
        {8C2CC25B-DE5D-433E-A550-63864C7A716D}.Debug|x86.Build.0 = Debug|Any CPU
        {8C2CC25B-DE5D-433E-A550-63864C7A716D}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {8C2CC25B-DE5D-433E-A550-63864C7A716D}.Release|Any CPU.Build.0 = Release|Any CPU
        {8C2CC25B-DE5D-433E-A550-63864C7A716D}.Release|x64.ActiveCfg = Release|Any CPU
        {8C2CC25B-DE5D-433E-A550-63864C7A716D}.Release|x64.Build.0 = Release|Any CPU
        {8C2CC25B-DE5D-433E-A550-63864C7A716D}.Release|x86.ActiveCfg = Release|Any CPU
        {8C2CC25B-DE5D-433E-A550-63864C7A716D}.Release|x86.Build.0 = Release|Any CPU
        {FFAB2C76-1C9E-4006-95C8-A0B2AA53139D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {FFAB2C76-1C9E-4006-95C8-A0B2AA53139D}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {FFAB2C76-1C9E-4006-95C8-A0B2AA53139D}.Debug|x64.ActiveCfg = Debug|Any CPU
        {FFAB2C76-1C9E-4006-95C8-A0B2AA53139D}.Debug|x64.Build.0 = Debug|Any CPU
        {FFAB2C76-1C9E-4006-95C8-A0B2AA53139D}.Debug|x86.ActiveCfg = Debug|Any CPU
        {FFAB2C76-1C9E-4006-95C8-A0B2AA53139D}.Debug|x86.Build.0 = Debug|Any CPU
        {FFAB2C76-1C9E-4006-95C8-A0B2AA53139D}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {FFAB2C76-1C9E-4006-95C8-A0B2AA53139D}.Release|Any CPU.Build.0 = Release|Any CPU
        {FFAB2C76-1C9E-4006-95C8-A0B2AA53139D}.Release|x64.ActiveCfg = Release|Any CPU
        {FFAB2C76-1C9E-4006-95C8-A0B2AA53139D}.Release|x64.Build.0 = Release|Any CPU
        {FFAB2C76-1C9E-4006-95C8-A0B2AA53139D}.Release|x86.ActiveCfg = Release|Any CPU
        {FFAB2C76-1C9E-4006-95C8-A0B2AA53139D}.Release|x86.Build.0 = Release|Any CPU
        {F9886971-C3B2-4334-B014-D5109F2041DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {F9886971-C3B2-4334-B014-D5109F2041DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {F9886971-C3B2-4334-B014-D5109F2041DE}.Debug|x64.ActiveCfg = Debug|Any CPU
        {F9886971-C3B2-4334-B014-D5109F2041DE}.Debug|x64.Build.0 = Debug|Any CPU
        {F9886971-C3B2-4334-B014-D5109F2041DE}.Debug|x86.ActiveCfg = Debug|Any CPU
        {F9886971-C3B2-4334-B014-D5109F2041DE}.Debug|x86.Build.0 = Debug|Any CPU
        {F9886971-C3B2-4334-B014-D5109F2041DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {F9886971-C3B2-4334-B014-D5109F2041DE}.Release|Any CPU.Build.0 = Release|Any CPU
        {F9886971-C3B2-4334-B014-D5109F2041DE}.Release|x64.ActiveCfg = Release|Any CPU
        {F9886971-C3B2-4334-B014-D5109F2041DE}.Release|x64.Build.0 = Release|Any CPU
        {F9886971-C3B2-4334-B014-D5109F2041DE}.Release|x86.ActiveCfg = Release|Any CPU
        {F9886971-C3B2-4334-B014-D5109F2041DE}.Release|x86.Build.0 = Release|Any CPU
    EndGlobalSection
    GlobalSection(SolutionProperties) = preSolution
        HideSolutionNode = FALSE
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Program.cs
@@ -27,14 +27,15 @@
using WIDESEAWCS_Server.Filter;
using WIDESEAWCS_Server.HostedService;
using WIDESEAWCS_Tasks.SocketServer;
using WIDESEAWCS_RedisService.Extensions;
using WIDESEAWCS_WCSServer.Filter;
var builder = WebApplication.CreateBuilder(args);
// 1、配置服务容器
// 1�����÷�������
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()).ConfigureContainer<ContainerBuilder>(builder =>
{
    builder.RegisterModule(new AutofacModuleRegister());//带有接口层的服务注入
    builder.RegisterModule(new AutofacModuleRegister());//���нӿڲ�ķ���ע��
    builder.RegisterModule(new QuartzJobAutofacModuleRegister());
    builder.RegisterModule<AutofacPropertityModuleReg>();//
}).ConfigureAppConfiguration((hostingContext, config) =>
@@ -45,20 +46,21 @@
});
builder.ConfigureApplication();
// 2、配置服务
builder.Services.AddSingleton(new AppSettings(builder.Configuration));//注册
builder.Services.AddAllOptionRegister();//读取配置文件
// 2�����÷���
builder.Services.AddSingleton(new AppSettings(builder.Configuration));//ע��
builder.Services.AddAllOptionRegister();//��ȡ�����ļ�
builder.Services.AddMemoryCacheSetup();//缓存
builder.Services.AddSqlsugarSetup();//SqlSugar å¯åŠ¨æœåŠ¡
builder.Services.AddInitializationHostServiceSetup();//应用初始化服务注入
builder.Services.AddHostedService<SeedDataHostedService>();//初始化数据库
builder.Services.AddMemoryCacheSetup();//����
builder.Services.AddRedisSetup(builder.Configuration);//Redis缓存
builder.Services.AddSqlsugarSetup();//SqlSugar ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½
builder.Services.AddInitializationHostServiceSetup();//Ӧ�ó�ʼ������ע��
builder.Services.AddHostedService<SeedDataHostedService>();//��ʼ�����ݿ�
builder.Services.AddDbSetup();//Db å¯åŠ¨æœåŠ¡
builder.Services.AddDbSetup();//Db ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½
builder.Services.AddScoped<QuartzJobCreateDataTabel>();//任务调度 æ³¨å…¥åˆ›å»ºQuartzJob数据库表类
builder.Services.AddHostedService<QuartzJobDataTableHostedService>();//任务调度 æ˜ å°„QuartzJob数据库表
builder.Services.AddScoped<QuartzJobCreateDataTabel>();//������� ×¢ï¿½ë´´ï¿½ï¿½QuartzJob���ݿ����
builder.Services.AddHostedService<QuartzJobDataTableHostedService>();//������� Ó³ï¿½ï¿½QuartzJob���ݿ��
builder.Services.AddWebSocketSetup();
@@ -70,11 +72,11 @@
builder.Services.AddSwaggerSetup();
builder.Services.AddJobSetup();//任务调度 æ³¨å…¥åå°„获取依赖对象
builder.Services.AddJobSetup();//������� ×¢ï¿½ë·´ï¿½ï¿½ï¿½È¡ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½
builder.Services.AddHttpContextSetup();
builder.Services.AddHostedService<QuartzJobHostedService>();//任务调度 å¯åŠ¨æœåŠ¡
builder.Services.AddHostedService<QuartzJobHostedService>();//������� ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½
builder.Services.AddSingleton<TcpSocketServer>();
builder.Services.AddHostedService<SocketServerHostedService>();
@@ -89,7 +91,7 @@
builder.Services.AddAuthorizationSetup();
builder.Services.AddIpPolicyRateLimitSetup(builder.Configuration);//IPLimit限流 å¯åŠ¨æœåŠ¡
builder.Services.AddIpPolicyRateLimitSetup(builder.Configuration);//IPLimit���� ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½
builder.Services.AddScoped<UseServiceDIAttribute>();
@@ -99,7 +101,7 @@
builder.Services.AddControllers(o =>
{
    o.Filters.Add(typeof(GlobalExceptionsFilter));//全局异常
    o.Filters.Add(typeof(GlobalExceptionsFilter));//ȫ���쳣
})
.AddNewtonsoftJson(options =>
{
@@ -120,17 +122,17 @@
var app = builder.Build();
// 3、配置中间件
app.UseMiniProfiler();//性能分析器
app.ConfigureApplication();//配置文件
app.UseApplicationSetup();//启动配置
// 3��������
app.UseMiniProfiler();//���ܷ�����
app.ConfigureApplication();//�����ļ�
app.UseApplicationSetup();//��������
app.UseAllServicesMiddle(builder.Services);
app.UseSession();
app.UseSwaggerAuthorized();
app.UseSwaggerMiddle(() => Assembly.GetExecutingAssembly().GetManifestResourceStream("WIDESEAWCS_Server.index.html") ?? throw new Exception("未找到WIDESEAWCS_Server.index.html文件"));
app.UseSwaggerMiddle(() => Assembly.GetExecutingAssembly().GetManifestResourceStream("WIDESEAWCS_Server.index.html") ?? throw new Exception("δ�ҵ�WIDESEAWCS_Server.index.html�ļ�"));
app.UseIpLimitMiddle();
app.UseApiLogMiddleware();
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/RedisʹÓð¸Àý.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,380 @@
# Redis æœåŠ¡ä½¿ç”¨æ¡ˆä¾‹
## 1. ç¼“存(ICacheService)
通过构造函数注入 `ICacheService`,HybridCacheService è‡ªåŠ¨å®žçŽ° L1(内存) + L2(Redis) åŒå±‚缓存。
```csharp
public class MyService
{
    private readonly ICacheService _cache;
    public MyService(ICacheService cache)
    {
        _cache = cache;
    }
    // ç¼“存字符串,60秒过期
    public void CacheString()
    {
        _cache.Add("token:10001", "abc123", 60);
        var token = _cache.Get("token:10001");
    }
    // ç¼“存对象
    public void CacheObject()
    {
        var user = new { Id = 1, Name = "张三" };
        _cache.AddObject("user:1", user, 300);
        var cached = _cache.Get<dynamic>("user:1");
    }
    // åˆ é™¤ç¼“å­˜
    public void RemoveCache()
    {
        _cache.Remove("user:1");
        _cache.Remove(new[] { "token:10001", "token:10002" });
    }
}
```
## 2. åˆ†å¸ƒå¼é”ï¼ˆIDistributedLockService)
```csharp
public class OrderService
{
    private readonly IDistributedLockService _lock;
    public OrderService(IDistributedLockService lockService)
    {
        _lock = lockService;
    }
    // æ–¹å¼ä¸€ï¼šæ‰‹åŠ¨èŽ·å–å’Œé‡Šæ”¾é”
    public void ProcessOrder(string orderId)
    {
        var token = _lock.AcquireLock($"order:{orderId}", TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(5));
        if (token == null)
        {
            Console.WriteLine("获取锁失败,其他进程正在处理");
            return;
        }
        try
        {
            // ä¸šåŠ¡é€»è¾‘...
        }
        finally
        {
            _lock.ReleaseLock($"order:{orderId}", token);
        }
    }
    // æ–¹å¼äºŒï¼šè‡ªåŠ¨ç®¡ç†é”çš„ç”Ÿå‘½å‘¨æœŸ
    public void ProcessOrderAuto(string orderId)
    {
        var success = _lock.ExecuteWithLock($"order:{orderId}", TimeSpan.FromSeconds(30), () =>
        {
            // ä¸šåŠ¡é€»è¾‘...
        });
    }
}
```
## 3. è®¡æ•°å™¨ï¼ˆICounterService)
```csharp
public class StatisticsService
{
    private readonly ICounterService _counter;
    public StatisticsService(ICounterService counter)
    {
        _counter = counter;
    }
    // ä»»åŠ¡è®¡æ•°
    public void OnTaskCompleted()
    {
        _counter.Increment("task:completed");
        var total = _counter.GetCount("task:completed");
    }
    // å¸¦è¿‡æœŸæ—¶é—´çš„计数(如:每小时请求数)
    public void OnApiRequest()
    {
        var count = _counter.IncrementWithExpiry("api:hourly", TimeSpan.FromHours(1));
    }
}
```
## 4. å‘布/订阅(IMessageQueueService)
```csharp
public class NotificationService
{
    private readonly IMessageQueueService _mq;
    public NotificationService(IMessageQueueService mq)
    {
        _mq = mq;
    }
    // å‘布消息
    public void NotifyTaskComplete(int taskId)
    {
        _mq.Publish("task:complete", $"{{\"taskId\":{taskId}}}");
    }
    // è®¢é˜…消息
    public void SubscribeTaskEvents()
    {
        _mq.Subscribe("task:complete", (channel, message) =>
        {
            Console.WriteLine($"收到任务完成通知: {message}");
        });
    }
    // ç®€å•队列:入队/出队
    public void UseQueue()
    {
        _mq.Enqueue("pending-tasks", "task_001");
        var task = _mq.Dequeue("pending-tasks");
    }
}
```
## 5. é™æµï¼ˆIRateLimitingService)
```csharp
public class ApiController
{
    private readonly IRateLimitingService _rateLimiter;
    public ApiController(IRateLimitingService rateLimiter)
    {
        _rateLimiter = rateLimiter;
    }
    // å›ºå®šçª—口限流:每分钟最多100次
    public bool CheckRateLimit(string clientIp)
    {
        return _rateLimiter.IsAllowed($"api:{clientIp}", 100, TimeSpan.FromMinutes(1));
    }
    // æ»‘动窗口限流
    public bool CheckSlidingRateLimit(string clientIp)
    {
        return _rateLimiter.IsAllowedSliding($"api:{clientIp}", 100, TimeSpan.FromMinutes(1));
    }
    // ä»¤ç‰Œæ¡¶é™æµï¼šæ¡¶å®¹é‡50,每秒补充10个
    public bool CheckTokenBucket(string clientIp)
    {
        return _rateLimiter.TryAcquireToken($"api:{clientIp}", 50, 10, TimeSpan.FromSeconds(1));
    }
}
```
## 6. åˆ†å¸ƒå¼ID生成器(IDistributedIdGenerator)
```csharp
public class TaskService
{
    private readonly IDistributedIdGenerator _idGen;
    public TaskService(IDistributedIdGenerator idGen)
    {
        _idGen = idGen;
    }
    // è‡ªå¢žID
    public long CreateTask()
    {
        var id = _idGen.NextId("task");
        return id; // 1, 2, 3...
    }
    // å¸¦æ—¥æœŸå‰ç¼€çš„ID
    public string CreateTaskCode()
    {
        var code = _idGen.NextIdWithDate("task");
        return code; // 20260302000001
    }
}
```
## 7. æŽ’行榜(ILeaderboardService)
```csharp
public class LeaderboardDemo
{
    private readonly ILeaderboardService _board;
    public LeaderboardDemo(ILeaderboardService board)
    {
        _board = board;
    }
    public void Demo()
    {
        // æ·»åŠ /更新分数
        _board.AddOrUpdate("efficiency", "设备A", 95.5);
        _board.AddOrUpdate("efficiency", "设备B", 88.0);
        // å¢žåŠ åˆ†æ•°
        _board.IncrementScore("efficiency", "设备A", 2.0);
        // èŽ·å–æŽ’åå’ŒTop N
        var rank = _board.GetRank("efficiency", "设备A"); // 0 = ç¬¬ä¸€å
        var top10 = _board.GetTopN("efficiency", 10);
    }
}
```
## 8. å¯¹è±¡å­˜å‚¨ï¼ˆIObjectStorageService)
```csharp
public class DeviceService
{
    private readonly IObjectStorageService _storage;
    public DeviceService(IObjectStorageService storage)
    {
        _storage = storage;
    }
    public void Demo()
    {
        // å­˜å‚¨å®Œæ•´å¯¹è±¡
        var device = new { Id = 1, Name = "堆垛机01", Status = "Running" };
        _storage.SetObject("device:1", device, TimeSpan.FromMinutes(30));
        // è¯»å–对象
        var cached = _storage.GetObject<dynamic>("device:1");
        // Hash字段操作
        _storage.SetField("device:1:props", "temperature", "36.5");
        var temp = _storage.GetField("device:1:props", "temperature");
    }
}
```
## 9. é…ç½®ä¸­å¿ƒï¼ˆIConfigurationCenterService)
```csharp
public class ConfigDemo
{
    private readonly IConfigurationCenterService _config;
    public ConfigDemo(IConfigurationCenterService config)
    {
        _config = config;
    }
    public void Demo()
    {
        // è®¾ç½®é…ç½®
        _config.Set("system", "MaxTaskCount", "100");
        _config.Set("system", "EnableAutoDispatch", "true");
        // è¯»å–配置
        var maxTask = _config.Get("system", "MaxTaskCount");
        var allConfig = _config.GetSection("system");
        // ç›‘听配置变更
        _config.Subscribe("system", (key, value) =>
        {
            Console.WriteLine($"配置变更: {key} = {value}");
        });
    }
}
```
## 10. ç›‘控(IRedisMonitorService)
```csharp
public class MonitorDemo
{
    private readonly IRedisMonitorService _monitor;
    public MonitorDemo(IRedisMonitorService monitor)
    {
        _monitor = monitor;
    }
    public void Demo()
    {
        // å¥åº·æ£€æŸ¥
        var healthy = _monitor.HealthCheck();
        // å†…存信息
        var mem = _monitor.GetMemoryInfo();
        Console.WriteLine($"内存使用: {mem.UsedMemoryHuman}, ä½¿ç”¨çއ: {mem.UsagePercent}%");
        // è¿žæŽ¥æ•°å’ŒKey数量
        var clients = _monitor.GetClientCount();
        var dbSize = _monitor.GetDbSize();
    }
}
```
## 11. Session存储(ISessionStorage)
```csharp
public class SessionDemo
{
    private readonly ISessionStorage _session;
    public SessionDemo(ISessionStorage session)
    {
        _session = session;
    }
    public void Demo()
    {
        var sid = Guid.NewGuid().ToString("N");
        // è®¾ç½®Session数据,30分钟过期
        _session.Set(sid, "userId", "10001", TimeSpan.FromMinutes(30));
        _session.Set(sid, "role", "admin");
        // è¯»å–
        var userId = _session.Get(sid, "userId");
        // ç»­æœŸ
        _session.RefreshSession(sid, TimeSpan.FromMinutes(30));
        // é”€æ¯
        _session.DestroySession(sid);
    }
}
```
## 12. å¸ƒéš†è¿‡æ»¤å™¨ï¼ˆIBloomFilterService)
```csharp
public class BloomFilterDemo
{
    private readonly IBloomFilterService _bloom;
    public BloomFilterDemo(IBloomFilterService bloom)
    {
        _bloom = bloom;
    }
    // é˜²æ­¢ç¼“存穿透
    public void Demo()
    {
        // é¢„热:将已有数据加入过滤器
        _bloom.AddRange("task:ids", new[] { "T001", "T002", "T003" });
        // æŸ¥è¯¢å‰å…ˆæ£€æŸ¥
        if (!_bloom.MayExist("task:ids", "T999"))
        {
            // ä¸€å®šä¸å­˜åœ¨ï¼Œç›´æŽ¥è¿”回,避免查询数据库
            return;
        }
        // å¯èƒ½å­˜åœ¨ï¼Œç»§ç»­æŸ¥è¯¢æ•°æ®åº“...
    }
}
```
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/WIDESEAWCS_Server.csproj
@@ -73,6 +73,7 @@
        <ProjectReference Include="..\WIDESEAWCS_SystemServices\WIDESEAWCS_SystemServices.csproj" />
        <ProjectReference Include="..\WIDESEAWCs_TaskInfoService\WIDESEAWCs_TaskInfoService.csproj" />
        <ProjectReference Include="..\WIDESEAWCS_Tasks\WIDESEAWCS_Tasks.csproj" />
        <ProjectReference Include="..\WIDESEAWCS_RedisService\WIDESEAWCS_RedisService.csproj" />
    </ItemGroup>
    <ItemGroup>
@@ -105,4 +106,8 @@
      </EmbeddedResource>
    </ItemGroup>
    <ItemGroup>
      <Folder Include="Log\" />
    </ItemGroup>
</Project>
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/appsettings.json
@@ -41,8 +41,8 @@
  "DBSeedEnable": false,
  "QuartzDBSeedEnable": false,
  "LogDeubgEnable": false, //是否记录调试日志
  "PrintSql": true, //打印SQL语句
  "LogAOPEnable": true, //是否记录AOP日志
  "PrintSql": false, //打印SQL语句
  "LogAOPEnable": false, //是否记录AOP日志
  "WebSocketEnable": true, //是否开启WebSocket服务
  "WebSocketPort": 9296, //WebSocket服务端口
  "SocketServer": {
@@ -55,5 +55,34 @@
    "IdleTimeoutSeconds": 0,   //空闲超时时间,单位秒,0表示不超时
    "EnableHeartbeat": true,   //是否启用心跳检测
    "LogFilePath": "socketserver.log"  //日志文件路径
  },
  "CheckPalletPositions": [
    {
      "Code": "11068",
      "WarehouseId": 1
    }
  ],
  "RedisConfig": {
    "Enabled": true, //是否启用Redis,false时仅使用内存缓存
    "ConnectionString": "127.0.0.1:6379,password=P@ssw0rd,defaultDatabase=0,connectTimeout=5000,abortConnect=false", //Redis连接字符串
    "InstanceName": "WIDESEAWCS:", //实例名称,用于区分不同应用
    "DefaultDatabase": 0, //默认数据库索引(0-15)
    "EnableSentinel": false, //是否启用哨兵模式
    "SentinelMasterName": "mymaster", //哨兵主节点名称
    "SentinelEndpoints": [], //哨兵节点地址列表,如 ["sentinel1:26379","sentinel2:26379"]
    "PoolSize": 10, //连接池大小
    "ConnectRetry": 3, //连接失败重试次数
    "SerializerType": "Newtonsoft", //序列化方式:Newtonsoft
    "FallbackToMemory": true, //Redis不可用时是否降级到内存缓存
    "KeyPrefix": "wcs:", //全局Key前缀,用于隔离不同系统的数据
    "Monitoring": {
      "Enabled": false, //是否启用监控
      "SlowLogThresholdMs": 100, //慢查询阈值(毫秒)
      "HealthCheckIntervalSeconds": 30 //健康检查间隔(秒)
    },
    "Eviction": {
      "DefaultExpirationSeconds": 3600, //默认缓存过期时间(秒)
      "MaxMemoryPolicy": "allkeys-lru" //内存淘汰策略:allkeys-lru, volatile-lru, noeviction等
    }
  }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/CommonConveyorLineNewJob.cs
@@ -18,10 +18,15 @@
#endregion << ç‰ˆ æœ¬ æ³¨ é‡Š >>
using AutoMapper;
using Microsoft.Extensions.Configuration;
using Quartz;
using SqlSugar;
using System.Text.Json;
using WIDESEA_Core;
using WIDESEAWCS_Common.TaskEnum;
using WIDESEAWCS_Core;
using WIDESEAWCS_Core.Helper;
using WIDESEAWCS_DTO.TaskInfo;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_QuartzJob;
@@ -36,15 +41,17 @@
        private readonly ITaskExecuteDetailService _taskExecuteDetailService;
        private readonly IRouterService _routerService;
        private readonly IMapper _mapper;
        ConveyorLineDispatchHandler _conveyorLineDispatch;
        private ConveyorLineDispatchHandler _conveyorLineDispatch;
        private readonly HttpClientHelper _httpClientHelper;
        public CommonConveyorLineNewJob(ITaskService taskService, ITaskExecuteDetailService taskExecuteDetailService, IRouterService routerService, IMapper mapper)
        public CommonConveyorLineNewJob(ITaskService taskService, ITaskExecuteDetailService taskExecuteDetailService, IRouterService routerService, IMapper mapper, HttpClientHelper httpClientHelper)
        {
            _taskService = taskService;
            _taskExecuteDetailService = taskExecuteDetailService;
            _routerService = routerService;
            _mapper = mapper;
            _conveyorLineDispatch = new ConveyorLineDispatchHandler(_taskService, _taskExecuteDetailService, _routerService, _mapper);
            _httpClientHelper = httpClientHelper;
        }
        public Task Execute(IJobExecutionContext context)
@@ -73,6 +80,38 @@
                        try
                        {
                            ConveyorLineTaskCommandNew command = conveyorLine.ReadCustomer<ConveyorLineTaskCommandNew>(childDeviceCode);
                            #region æ£€æŸ¥ç‰¹å®šä½ç½®æ˜¯å¦æœ‰æ‰˜ç›˜
                            var checkPalletPositions = App.Configuration.GetSection("CheckPalletPositions")
                                .Get<List<CheckPalletPosition>>() ?? new List<CheckPalletPosition>();
                            if (checkPalletPositions.Any(x => x.Code == childDeviceCode))
                            {
                                if (command.CV_State.ObjToBool())
                                {
                                    var existingTask = _taskService.Repository.QueryFirst(x => x.TargetAddress == childDeviceCode);
                                    if (existingTask.IsNullOrEmpty())
                                    {
                                        var position = checkPalletPositions.FirstOrDefault(x => x.Code == childDeviceCode);
                                        var responseResult = _httpClientHelper.Post<WebResponseContent>("GetOutBoundTrayTaskAsync", new CreateTaskDto()
                                        {
                                            WarehouseId = position.WarehouseId,
                                            TargetAddress = childDeviceCode
                                        }.Serialize());
                                        if (responseResult.IsSuccess && responseResult.Data.Status)
                                        {
                                            var wmsTask = JsonSerializer.Deserialize<List<WMSTaskDTO>>(responseResult.Data.Data.Serialize());
                                            if (wmsTask != null)
                                                _taskService.ReceiveWMSTask(wmsTask);
                                        }
                                    }
                                }
                            }
                            #endregion
                            if (command == null || command.PLC_STB != 1)
                            {
                                return;
@@ -97,7 +136,6 @@
                                // å¤„理任务状态
                                ProcessTaskState(conveyorLine, command, task, childDeviceCode);
                            }
                        }
                        catch (Exception innerEx)
                        {
@@ -112,7 +150,6 @@
            }
            return Task.CompletedTask;
        }
        /// <summary>
        /// å¤„理任务状态
@@ -158,7 +195,5 @@
                    break;
            }
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLine/CheckPalletPosition.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,9 @@
namespace WIDESEAWCS_Tasks
{
    public class CheckPalletPosition
    {
        public string Code { get; set; } = string.Empty;
        public int WarehouseId { get; set; } = 0;
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotJob.cs
@@ -7,6 +7,7 @@
using WIDESEAWCS_Common.HttpEnum;
using WIDESEAWCS_Common.TaskEnum;
using WIDESEAWCS_Core;
using WIDESEAWCS_Core.Caches;
using WIDESEAWCS_Core.Helper;
using WIDESEAWCS_Core.Http;
using WIDESEAWCS_DTO.Stock;
@@ -26,20 +27,22 @@
        private const int MaxTaskTotalNum = 48;
        private readonly TcpSocketServer _TcpSocket;
        private static readonly ConcurrentDictionary<string, RobotSocketState> _socketStates = new();
        //private static readonly ConcurrentDictionary<string, RobotSocketState> _socketStates = new();
        private static int _eventSubscribedFlag;
        private readonly ITaskService _taskService;
        private readonly IRobotTaskService _robotTaskService;
        private readonly ICacheService _cache;
        private static IRobotTaskService _latestRobotTaskService = null!;
        private static ITaskService _latestTaskService = null!;
        public RobotJob(TcpSocketServer TcpSocket, IRobotTaskService RobottaskService, ITaskService TaskService)
        public RobotJob(TcpSocketServer TcpSocket, IRobotTaskService RobottaskService, ITaskService TaskService, ICacheService cache)
        {
            _TcpSocket = TcpSocket;
            _robotTaskService = RobottaskService;
            _taskService = TaskService;
            _cache = cache;
            _latestRobotTaskService = RobottaskService;
            _latestTaskService = TaskService;
@@ -57,7 +60,7 @@
            string ipAddress = robotCrane.IPAddress;
            // èŽ·å–æˆ–åˆ›å»ºçŠ¶æ€
            RobotSocketState state = _socketStates.GetOrAdd(ipAddress, _ => new RobotSocketState
            RobotSocketState state = _cache.GetOrAdd(ipAddress, _ => new RobotSocketState
            {
                IPAddress = ipAddress,
                RobotCrane = robotCrane
@@ -65,6 +68,9 @@
            // æ›´æ–°è®¾å¤‡ä¿¡æ¯
            state.RobotCrane = robotCrane;
            try
            {
            // æ£€æŸ¥æ˜¯å¦æœ‰è¯¥å®¢æˆ·ç«¯è¿žæŽ¥
            var clientIds = _TcpSocket.GetClientIds();
@@ -149,6 +155,17 @@
                }
            }
        }
            catch (Exception)
            {
            }
            finally
            {
                // å¯é€‰ï¼šåœ¨è¿™é‡Œå¤„理任何需要在任务完成后执行的清理工作
                // æ›´æ–°ç¼“存中的状态
                _cache.AddOrUpdate(ipAddress, state);
            }
        }
        //临时测试用
        private static string GenerateTrayBarcode(RobotSocketState state, string prefix = "")
@@ -176,7 +193,7 @@
        /// <returns></returns>
        private Task<string?> _TcpSocket_RobotReceived(string clientId)
        {
            _socketStates.TryRemove(clientId, out _);
            _cache.TryRemove(clientId, out _);
            return Task.FromResult<string?>(null);
        }
Code/WMS/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/v18/DocumentLayout.backup.json
@@ -3,24 +3,44 @@
  "WorkspaceRootPath": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\",
  "Documents": [
    {
      "AbsoluteMoniker": "D:0:0:{7DC26D42-D8EE-46F0-BA66-A13457086885}|WIDESEA_StockService\\WIDESEA_StockService.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_stockservice\\stockserivce.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{7DC26D42-D8EE-46F0-BA66-A13457086885}|WIDESEA_StockService\\WIDESEA_StockService.csproj|solutionrelative:widesea_stockservice\\stockserivce.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{7D7534D4-51D9-46DC-A6B7-6430042F4E12}|WIDESEA_TaskInfoService\\WIDESEA_TaskInfoService.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_taskinfoservice\\taskservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{7D7534D4-51D9-46DC-A6B7-6430042F4E12}|WIDESEA_TaskInfoService\\WIDESEA_TaskInfoService.csproj|solutionrelative:widesea_taskinfoservice\\taskservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{5F260E03-095A-4870-8419-5B72CB62929E}|WIDESEA_IBasicService\\WIDESEA_IBasicService.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_ibasicservice\\ilocationinfoservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{5F260E03-095A-4870-8419-5B72CB62929E}|WIDESEA_IBasicService\\WIDESEA_IBasicService.csproj|solutionrelative:widesea_ibasicservice\\ilocationinfoservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
      "AbsoluteMoniker": "D:0:0:{929DF936-042C-4EEC-8722-A831FC2F0AEA}|WIDESEA_DTO\\WIDESEA_DTO.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_dto\\stock\\stockdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{929DF936-042C-4EEC-8722-A831FC2F0AEA}|WIDESEA_DTO\\WIDESEA_DTO.csproj|solutionrelative:widesea_dto\\stock\\stockdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{7DC26D42-D8EE-46F0-BA66-A13457086885}|WIDESEA_StockService\\WIDESEA_StockService.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_stockservice\\stockserivce.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{7DC26D42-D8EE-46F0-BA66-A13457086885}|WIDESEA_StockService\\WIDESEA_StockService.csproj|solutionrelative:widesea_stockservice\\stockserivce.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{D81A65B5-47D1-40C1-8FDE-7D24FF003F51}|WIDESEA_WMSServer\\WIDESEA_WMSServer.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_wmsserver\\controllers\\taskinfo\\taskcontroller.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{D81A65B5-47D1-40C1-8FDE-7D24FF003F51}|WIDESEA_WMSServer\\WIDESEA_WMSServer.csproj|solutionrelative:widesea_wmsserver\\controllers\\taskinfo\\taskcontroller.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{CE0DB91F-5A68-448E-A419-4C26B5039F51}|WIDESEA_ITaskInfoService\\WIDESEA_ITaskInfoService.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_itaskinfoservice\\itaskservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{CE0DB91F-5A68-448E-A419-4C26B5039F51}|WIDESEA_ITaskInfoService\\WIDESEA_ITaskInfoService.csproj|solutionrelative:widesea_itaskinfoservice\\itaskservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{D81A65B5-47D1-40C1-8FDE-7D24FF003F51}|WIDESEA_WMSServer\\WIDESEA_WMSServer.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_wmsserver\\controllers\\taskinfo\\taskcontroller.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{D81A65B5-47D1-40C1-8FDE-7D24FF003F51}|WIDESEA_WMSServer\\WIDESEA_WMSServer.csproj|solutionrelative:widesea_wmsserver\\controllers\\taskinfo\\taskcontroller.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
      "AbsoluteMoniker": "D:0:0:{111BD7AA-9749-4506-9772-79F9EF14754C}|WIDESEA_Core\\WIDESEA_Core.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_core\\baserepository\\repositorybase.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{111BD7AA-9749-4506-9772-79F9EF14754C}|WIDESEA_Core\\WIDESEA_Core.csproj|solutionrelative:widesea_core\\baserepository\\repositorybase.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{AF8F3D65-1D75-4B8F-AFD9-4150E591C44D}|WIDESEA_Common\\WIDESEA_Common.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_common\\stockenum\\stockstatusemun.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{AF8F3D65-1D75-4B8F-AFD9-4150E591C44D}|WIDESEA_Common\\WIDESEA_Common.csproj|solutionrelative:widesea_common\\stockenum\\stockstatusemun.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{00CE9885-9F24-4B6C-A7E8-0DE8C9ED7128}|WIDESEA_Model\\WIDESEA_Model.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_model\\models\\stock\\dt_stockinfo.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{00CE9885-9F24-4B6C-A7E8-0DE8C9ED7128}|WIDESEA_Model\\WIDESEA_Model.csproj|solutionrelative:widesea_model\\models\\stock\\dt_stockinfo.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{929DF936-042C-4EEC-8722-A831FC2F0AEA}|WIDESEA_DTO\\WIDESEA_DTO.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_dto\\task\\createtaskdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{929DF936-042C-4EEC-8722-A831FC2F0AEA}|WIDESEA_DTO\\WIDESEA_DTO.csproj|solutionrelative:widesea_dto\\task\\createtaskdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{5F260E03-095A-4870-8419-5B72CB62929E}|WIDESEA_IBasicService\\WIDESEA_IBasicService.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_ibasicservice\\ilocationinfoservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{5F260E03-095A-4870-8419-5B72CB62929E}|WIDESEA_IBasicService\\WIDESEA_IBasicService.csproj|solutionrelative:widesea_ibasicservice\\ilocationinfoservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{AF8F3D65-1D75-4B8F-AFD9-4150E591C44D}|WIDESEA_Common\\WIDESEA_Common.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_common\\locationenum\\locationstatusenum.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
@@ -43,10 +63,6 @@
      "RelativeMoniker": "D:0:0:{7DC26D42-D8EE-46F0-BA66-A13457086885}|WIDESEA_StockService\\WIDESEA_StockService.csproj|solutionrelative:widesea_stockservice\\stockinfoservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{929DF936-042C-4EEC-8722-A831FC2F0AEA}|WIDESEA_DTO\\WIDESEA_DTO.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_dto\\stock\\stockdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{929DF936-042C-4EEC-8722-A831FC2F0AEA}|WIDESEA_DTO\\WIDESEA_DTO.csproj|solutionrelative:widesea_dto\\stock\\stockdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{D81A65B5-47D1-40C1-8FDE-7D24FF003F51}|WIDESEA_WMSServer\\WIDESEA_WMSServer.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_wmsserver\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{D81A65B5-47D1-40C1-8FDE-7D24FF003F51}|WIDESEA_WMSServer\\WIDESEA_WMSServer.csproj|solutionrelative:widesea_wmsserver\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
@@ -61,14 +77,6 @@
    {
      "AbsoluteMoniker": "D:0:0:{111BD7AA-9749-4506-9772-79F9EF14754C}|WIDESEA_Core\\WIDESEA_Core.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_core\\helper\\http\\httpresponseresult.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{111BD7AA-9749-4506-9772-79F9EF14754C}|WIDESEA_Core\\WIDESEA_Core.csproj|solutionrelative:widesea_core\\helper\\http\\httpresponseresult.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{929DF936-042C-4EEC-8722-A831FC2F0AEA}|WIDESEA_DTO\\WIDESEA_DTO.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_dto\\task\\createtaskdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{929DF936-042C-4EEC-8722-A831FC2F0AEA}|WIDESEA_DTO\\WIDESEA_DTO.csproj|solutionrelative:widesea_dto\\task\\createtaskdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{00CE9885-9F24-4B6C-A7E8-0DE8C9ED7128}|WIDESEA_Model\\WIDESEA_Model.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_model\\models\\stock\\dt_stockinfo.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{00CE9885-9F24-4B6C-A7E8-0DE8C9ED7128}|WIDESEA_Model\\WIDESEA_Model.csproj|solutionrelative:widesea_model\\models\\stock\\dt_stockinfo.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{D11C804C-2FF4-4C18-A3EE-2F0574427BB3}|WIDESEA_BasicService\\WIDESEA_BasicService.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_basicservice\\locationinfoservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
@@ -89,10 +97,6 @@
    {
      "AbsoluteMoniker": "D:0:0:{929DF936-042C-4EEC-8722-A831FC2F0AEA}|WIDESEA_DTO\\WIDESEA_DTO.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_dto\\gradingmachine\\outputdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{929DF936-042C-4EEC-8722-A831FC2F0AEA}|WIDESEA_DTO\\WIDESEA_DTO.csproj|solutionrelative:widesea_dto\\gradingmachine\\outputdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{111BD7AA-9749-4506-9772-79F9EF14754C}|WIDESEA_Core\\WIDESEA_Core.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_core\\baserepository\\repositorybase.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{111BD7AA-9749-4506-9772-79F9EF14754C}|WIDESEA_Core\\WIDESEA_Core.csproj|solutionrelative:widesea_core\\baserepository\\repositorybase.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    }
  ],
  "DocumentGroupContainers": [
@@ -105,6 +109,19 @@
          "SelectedChildIndex": 24,
          "Children": [
            {
              "$type": "Document",
              "DocumentIndex": 6,
              "Title": "StockStatusEmun.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_Common\\StockEnum\\StockStatusEmun.cs",
              "RelativeDocumentMoniker": "WIDESEA_Common\\StockEnum\\StockStatusEmun.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_Common\\StockEnum\\StockStatusEmun.cs",
              "RelativeToolTip": "WIDESEA_Common\\StockEnum\\StockStatusEmun.cs",
              "ViewState": "AgIAACQAAAAAAAAAAAAgwDoAAAAeAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-28T06:15:43.313Z",
              "EditorCaption": ""
            },
            {
              "$type": "Bookmark",
              "Name": "ST:128:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
            },
@@ -113,8 +130,12 @@
              "Name": "ST:0:0:{40ea2e6b-2121-4bb8-a43e-c83c04b51041}"
            },
            {
              "$type": "Bookmark",
              "Name": "ST:0:0:{aa2115a1-9712-457b-9047-dbb71ca2cdd2}"
            },
            {
              "$type": "Document",
              "DocumentIndex": 5,
              "DocumentIndex": 10,
              "Title": "LocationStatusEnum.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_Common\\LocationEnum\\LocationStatusEnum.cs",
              "RelativeDocumentMoniker": "WIDESEA_Common\\LocationEnum\\LocationStatusEnum.cs",
@@ -126,7 +147,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 8,
              "DocumentIndex": 13,
              "Title": "IStockService.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_IStockService\\IStockService.cs",
              "RelativeDocumentMoniker": "WIDESEA_IStockService\\IStockService.cs",
@@ -138,7 +159,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 7,
              "DocumentIndex": 12,
              "Title": "StockController.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_WMSServer\\Controllers\\Stock\\StockController.cs",
              "RelativeDocumentMoniker": "WIDESEA_WMSServer\\Controllers\\Stock\\StockController.cs",
@@ -150,7 +171,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 11,
              "DocumentIndex": 15,
              "Title": "Program.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_WMSServer\\Program.cs",
              "RelativeDocumentMoniker": "WIDESEA_WMSServer\\Program.cs",
@@ -162,7 +183,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 14,
              "DocumentIndex": 18,
              "Title": "HttpResponseResult.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_Core\\Helper\\HTTP\\HttpResponseResult.cs",
              "RelativeDocumentMoniker": "WIDESEA_Core\\Helper\\HTTP\\HttpResponseResult.cs",
@@ -174,7 +195,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 13,
              "DocumentIndex": 17,
              "Title": "HttpRequestConfig.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_Core\\Helper\\HTTP\\HttpRequestConfig.cs",
              "RelativeDocumentMoniker": "WIDESEA_Core\\Helper\\HTTP\\HttpRequestConfig.cs",
@@ -186,7 +207,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 12,
              "DocumentIndex": 16,
              "Title": "HttpClientHelper.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_Core\\Helper\\HTTP\\HttpClientHelper.cs",
              "RelativeDocumentMoniker": "WIDESEA_Core\\Helper\\HTTP\\HttpClientHelper.cs",
@@ -198,7 +219,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 21,
              "DocumentIndex": 23,
              "Title": "OutPutDto.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_DTO\\GradingMachine\\OutPutDto.cs",
              "RelativeDocumentMoniker": "WIDESEA_DTO\\GradingMachine\\OutPutDto.cs",
@@ -210,7 +231,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 20,
              "DocumentIndex": 22,
              "Title": "InputDto.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_DTO\\GradingMachine\\InputDto.cs",
              "RelativeDocumentMoniker": "WIDESEA_DTO\\GradingMachine\\InputDto.cs",
@@ -222,19 +243,20 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 22,
              "DocumentIndex": 5,
              "Title": "RepositoryBase.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_Core\\BaseRepository\\RepositoryBase.cs",
              "RelativeDocumentMoniker": "WIDESEA_Core\\BaseRepository\\RepositoryBase.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_Core\\BaseRepository\\RepositoryBase.cs",
              "RelativeToolTip": "WIDESEA_Core\\BaseRepository\\RepositoryBase.cs",
              "ViewState": "AgIAALgDAAAAAAAAAAAhwMsDAAAZAAAAAAAAAA==",
              "ViewState": "AgIAALgDAAAAAAAAAAAawMsDAAAZAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-14T08:55:16.1Z"
              "WhenOpened": "2026-02-14T08:55:16.1Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 19,
              "DocumentIndex": 21,
              "Title": "IRepository.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_Core\\BaseRepository\\IRepository.cs",
              "RelativeDocumentMoniker": "WIDESEA_Core\\BaseRepository\\IRepository.cs",
@@ -246,7 +268,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 18,
              "DocumentIndex": 20,
              "Title": "StockViewService.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_StockService\\StockViewService.cs",
              "RelativeDocumentMoniker": "WIDESEA_StockService\\StockViewService.cs",
@@ -258,7 +280,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 2,
              "DocumentIndex": 9,
              "Title": "ILocationInfoService.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_IBasicService\\ILocationInfoService.cs",
              "RelativeDocumentMoniker": "WIDESEA_IBasicService\\ILocationInfoService.cs",
@@ -270,7 +292,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 17,
              "DocumentIndex": 19,
              "Title": "LocationInfoService.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_BasicService\\LocationInfoService.cs",
              "RelativeDocumentMoniker": "WIDESEA_BasicService\\LocationInfoService.cs",
@@ -282,19 +304,20 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 16,
              "DocumentIndex": 7,
              "Title": "Dt_StockInfo.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_Model\\Models\\Stock\\Dt_StockInfo.cs",
              "RelativeDocumentMoniker": "WIDESEA_Model\\Models\\Stock\\Dt_StockInfo.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_Model\\Models\\Stock\\Dt_StockInfo.cs",
              "RelativeToolTip": "WIDESEA_Model\\Models\\Stock\\Dt_StockInfo.cs",
              "ViewState": "AgIAAEAAAAAAAAAAAAAkwFIAAAAeAAAAAAAAAA==",
              "ViewState": "AgIAAEAAAAAAAAAAAAAkwFIAAAAuAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-11T01:38:37.887Z"
              "WhenOpened": "2026-02-11T01:38:37.887Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 9,
              "DocumentIndex": 14,
              "Title": "StockInfoService.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_StockService\\StockInfoService.cs",
              "RelativeDocumentMoniker": "WIDESEA_StockService\\StockInfoService.cs",
@@ -306,7 +329,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 6,
              "DocumentIndex": 11,
              "Title": "IStockInfoService.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_IStockService\\IStockInfoService.cs",
              "RelativeDocumentMoniker": "WIDESEA_IStockService\\IStockInfoService.cs",
@@ -318,74 +341,78 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 4,
              "DocumentIndex": 3,
              "Title": "TaskController.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_WMSServer\\Controllers\\TaskInfo\\TaskController.cs",
              "RelativeDocumentMoniker": "WIDESEA_WMSServer\\Controllers\\TaskInfo\\TaskController.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_WMSServer\\Controllers\\TaskInfo\\TaskController.cs",
              "RelativeToolTip": "WIDESEA_WMSServer\\Controllers\\TaskInfo\\TaskController.cs",
              "ViewState": "AgIAACcAAAAAAAAAAAAIwDcAAABAAAAAAAAAAA==",
              "ViewState": "AgIAAD0AAAAAAAAAAAAUwFUAAABJAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-09T01:23:19.844Z"
              "WhenOpened": "2026-02-09T01:23:19.844Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 15,
              "DocumentIndex": 8,
              "Title": "CreateTaskDto.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_DTO\\Task\\CreateTaskDto.cs",
              "RelativeDocumentMoniker": "WIDESEA_DTO\\Task\\CreateTaskDto.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_DTO\\Task\\CreateTaskDto.cs",
              "RelativeToolTip": "WIDESEA_DTO\\Task\\CreateTaskDto.cs",
              "ViewState": "AgIAABEAAAAAAAAAAADwvw4AAAARAAAAAAAAAA==",
              "ViewState": "AgIAABsAAAAAAAAAAAAywA4AAAARAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-06T07:58:13.932Z"
              "WhenOpened": "2026-02-06T07:58:13.932Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 3,
              "DocumentIndex": 4,
              "Title": "ITaskService.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_ITaskInfoService\\ITaskService.cs",
              "RelativeDocumentMoniker": "WIDESEA_ITaskInfoService\\ITaskService.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_ITaskInfoService\\ITaskService.cs",
              "RelativeToolTip": "WIDESEA_ITaskInfoService\\ITaskService.cs",
              "ViewState": "AgIAAC0AAAAAAAAAAAAAAEkAAAAIAAAAAAAAAA==",
              "ViewState": "AgIAAEAAAAAAAAAAAAAUwFEAAAAVAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-06T07:00:19.697Z"
              "WhenOpened": "2026-02-06T07:00:19.697Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 1,
              "DocumentIndex": 0,
              "Title": "TaskService.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_TaskInfoService\\TaskService.cs",
              "RelativeDocumentMoniker": "WIDESEA_TaskInfoService\\TaskService.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_TaskInfoService\\TaskService.cs",
              "RelativeToolTip": "WIDESEA_TaskInfoService\\TaskService.cs",
              "ViewState": "AgIAADkAAAAAAAAAAAAUwEkAAAAoAAAAAAAAAA==",
              "ViewState": "AgIAAAEBAAAAAAAAAAAxwPABAAAAAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-06T06:34:59.734Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 10,
              "DocumentIndex": 1,
              "Title": "StockDTO.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_DTO\\Stock\\StockDTO.cs",
              "RelativeDocumentMoniker": "WIDESEA_DTO\\Stock\\StockDTO.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_DTO\\Stock\\StockDTO.cs",
              "RelativeToolTip": "WIDESEA_DTO\\Stock\\StockDTO.cs",
              "ViewState": "AgIAACMAAAAAAAAAAAAAADoAAAASAAAAAAAAAA==",
              "ViewState": "AgIAACMAAAAAAAAAAAAAADYAAAAuAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-06T02:56:51.397Z"
              "WhenOpened": "2026-02-06T02:56:51.397Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 0,
              "DocumentIndex": 2,
              "Title": "StockSerivce.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_StockService\\StockSerivce.cs",
              "RelativeDocumentMoniker": "WIDESEA_StockService\\StockSerivce.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_StockService\\StockSerivce.cs",
              "RelativeToolTip": "WIDESEA_StockService\\StockSerivce.cs",
              "ViewState": "AgIAACQAAAAAAAAAAAAYwIgAAAAdAAAAAAAAAA==",
              "ViewState": "AgIAAAAAAAAAAAAAAAAAAD8AAAApAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-06T01:53:49.077Z",
              "EditorCaption": ""
Code/WMS/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/v18/DocumentLayout.json
@@ -3,24 +3,44 @@
  "WorkspaceRootPath": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\",
  "Documents": [
    {
      "AbsoluteMoniker": "D:0:0:{7DC26D42-D8EE-46F0-BA66-A13457086885}|WIDESEA_StockService\\WIDESEA_StockService.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_stockservice\\stockserivce.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{7DC26D42-D8EE-46F0-BA66-A13457086885}|WIDESEA_StockService\\WIDESEA_StockService.csproj|solutionrelative:widesea_stockservice\\stockserivce.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{7D7534D4-51D9-46DC-A6B7-6430042F4E12}|WIDESEA_TaskInfoService\\WIDESEA_TaskInfoService.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_taskinfoservice\\taskservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{7D7534D4-51D9-46DC-A6B7-6430042F4E12}|WIDESEA_TaskInfoService\\WIDESEA_TaskInfoService.csproj|solutionrelative:widesea_taskinfoservice\\taskservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{5F260E03-095A-4870-8419-5B72CB62929E}|WIDESEA_IBasicService\\WIDESEA_IBasicService.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_ibasicservice\\ilocationinfoservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{5F260E03-095A-4870-8419-5B72CB62929E}|WIDESEA_IBasicService\\WIDESEA_IBasicService.csproj|solutionrelative:widesea_ibasicservice\\ilocationinfoservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
      "AbsoluteMoniker": "D:0:0:{929DF936-042C-4EEC-8722-A831FC2F0AEA}|WIDESEA_DTO\\WIDESEA_DTO.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_dto\\stock\\stockdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{929DF936-042C-4EEC-8722-A831FC2F0AEA}|WIDESEA_DTO\\WIDESEA_DTO.csproj|solutionrelative:widesea_dto\\stock\\stockdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{7DC26D42-D8EE-46F0-BA66-A13457086885}|WIDESEA_StockService\\WIDESEA_StockService.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_stockservice\\stockserivce.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{7DC26D42-D8EE-46F0-BA66-A13457086885}|WIDESEA_StockService\\WIDESEA_StockService.csproj|solutionrelative:widesea_stockservice\\stockserivce.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{D81A65B5-47D1-40C1-8FDE-7D24FF003F51}|WIDESEA_WMSServer\\WIDESEA_WMSServer.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_wmsserver\\controllers\\taskinfo\\taskcontroller.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{D81A65B5-47D1-40C1-8FDE-7D24FF003F51}|WIDESEA_WMSServer\\WIDESEA_WMSServer.csproj|solutionrelative:widesea_wmsserver\\controllers\\taskinfo\\taskcontroller.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{CE0DB91F-5A68-448E-A419-4C26B5039F51}|WIDESEA_ITaskInfoService\\WIDESEA_ITaskInfoService.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_itaskinfoservice\\itaskservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{CE0DB91F-5A68-448E-A419-4C26B5039F51}|WIDESEA_ITaskInfoService\\WIDESEA_ITaskInfoService.csproj|solutionrelative:widesea_itaskinfoservice\\itaskservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{D81A65B5-47D1-40C1-8FDE-7D24FF003F51}|WIDESEA_WMSServer\\WIDESEA_WMSServer.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_wmsserver\\controllers\\taskinfo\\taskcontroller.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{D81A65B5-47D1-40C1-8FDE-7D24FF003F51}|WIDESEA_WMSServer\\WIDESEA_WMSServer.csproj|solutionrelative:widesea_wmsserver\\controllers\\taskinfo\\taskcontroller.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
      "AbsoluteMoniker": "D:0:0:{111BD7AA-9749-4506-9772-79F9EF14754C}|WIDESEA_Core\\WIDESEA_Core.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_core\\baserepository\\repositorybase.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{111BD7AA-9749-4506-9772-79F9EF14754C}|WIDESEA_Core\\WIDESEA_Core.csproj|solutionrelative:widesea_core\\baserepository\\repositorybase.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{AF8F3D65-1D75-4B8F-AFD9-4150E591C44D}|WIDESEA_Common\\WIDESEA_Common.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_common\\stockenum\\stockstatusemun.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{AF8F3D65-1D75-4B8F-AFD9-4150E591C44D}|WIDESEA_Common\\WIDESEA_Common.csproj|solutionrelative:widesea_common\\stockenum\\stockstatusemun.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{00CE9885-9F24-4B6C-A7E8-0DE8C9ED7128}|WIDESEA_Model\\WIDESEA_Model.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_model\\models\\stock\\dt_stockinfo.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{00CE9885-9F24-4B6C-A7E8-0DE8C9ED7128}|WIDESEA_Model\\WIDESEA_Model.csproj|solutionrelative:widesea_model\\models\\stock\\dt_stockinfo.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{929DF936-042C-4EEC-8722-A831FC2F0AEA}|WIDESEA_DTO\\WIDESEA_DTO.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_dto\\task\\createtaskdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{929DF936-042C-4EEC-8722-A831FC2F0AEA}|WIDESEA_DTO\\WIDESEA_DTO.csproj|solutionrelative:widesea_dto\\task\\createtaskdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{5F260E03-095A-4870-8419-5B72CB62929E}|WIDESEA_IBasicService\\WIDESEA_IBasicService.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_ibasicservice\\ilocationinfoservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{5F260E03-095A-4870-8419-5B72CB62929E}|WIDESEA_IBasicService\\WIDESEA_IBasicService.csproj|solutionrelative:widesea_ibasicservice\\ilocationinfoservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{AF8F3D65-1D75-4B8F-AFD9-4150E591C44D}|WIDESEA_Common\\WIDESEA_Common.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_common\\locationenum\\locationstatusenum.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
@@ -43,10 +63,6 @@
      "RelativeMoniker": "D:0:0:{7DC26D42-D8EE-46F0-BA66-A13457086885}|WIDESEA_StockService\\WIDESEA_StockService.csproj|solutionrelative:widesea_stockservice\\stockinfoservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{929DF936-042C-4EEC-8722-A831FC2F0AEA}|WIDESEA_DTO\\WIDESEA_DTO.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_dto\\stock\\stockdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{929DF936-042C-4EEC-8722-A831FC2F0AEA}|WIDESEA_DTO\\WIDESEA_DTO.csproj|solutionrelative:widesea_dto\\stock\\stockdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{D81A65B5-47D1-40C1-8FDE-7D24FF003F51}|WIDESEA_WMSServer\\WIDESEA_WMSServer.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_wmsserver\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{D81A65B5-47D1-40C1-8FDE-7D24FF003F51}|WIDESEA_WMSServer\\WIDESEA_WMSServer.csproj|solutionrelative:widesea_wmsserver\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
@@ -61,14 +77,6 @@
    {
      "AbsoluteMoniker": "D:0:0:{111BD7AA-9749-4506-9772-79F9EF14754C}|WIDESEA_Core\\WIDESEA_Core.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_core\\helper\\http\\httpresponseresult.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{111BD7AA-9749-4506-9772-79F9EF14754C}|WIDESEA_Core\\WIDESEA_Core.csproj|solutionrelative:widesea_core\\helper\\http\\httpresponseresult.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{929DF936-042C-4EEC-8722-A831FC2F0AEA}|WIDESEA_DTO\\WIDESEA_DTO.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_dto\\task\\createtaskdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{929DF936-042C-4EEC-8722-A831FC2F0AEA}|WIDESEA_DTO\\WIDESEA_DTO.csproj|solutionrelative:widesea_dto\\task\\createtaskdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{00CE9885-9F24-4B6C-A7E8-0DE8C9ED7128}|WIDESEA_Model\\WIDESEA_Model.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_model\\models\\stock\\dt_stockinfo.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{00CE9885-9F24-4B6C-A7E8-0DE8C9ED7128}|WIDESEA_Model\\WIDESEA_Model.csproj|solutionrelative:widesea_model\\models\\stock\\dt_stockinfo.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{D11C804C-2FF4-4C18-A3EE-2F0574427BB3}|WIDESEA_BasicService\\WIDESEA_BasicService.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_basicservice\\locationinfoservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
@@ -89,10 +97,6 @@
    {
      "AbsoluteMoniker": "D:0:0:{929DF936-042C-4EEC-8722-A831FC2F0AEA}|WIDESEA_DTO\\WIDESEA_DTO.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_dto\\gradingmachine\\outputdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{929DF936-042C-4EEC-8722-A831FC2F0AEA}|WIDESEA_DTO\\WIDESEA_DTO.csproj|solutionrelative:widesea_dto\\gradingmachine\\outputdto.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    },
    {
      "AbsoluteMoniker": "D:0:0:{111BD7AA-9749-4506-9772-79F9EF14754C}|WIDESEA_Core\\WIDESEA_Core.csproj|d:\\git\\shanmeixinnengyuan\\code\\wms\\widesea_wmsserver\\widesea_core\\baserepository\\repositorybase.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
      "RelativeMoniker": "D:0:0:{111BD7AA-9749-4506-9772-79F9EF14754C}|WIDESEA_Core\\WIDESEA_Core.csproj|solutionrelative:widesea_core\\baserepository\\repositorybase.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
    }
  ],
  "DocumentGroupContainers": [
@@ -102,7 +106,7 @@
      "DocumentGroups": [
        {
          "DockedWidth": 200,
          "SelectedChildIndex": 25,
          "SelectedChildIndex": 24,
          "Children": [
            {
              "$type": "Bookmark",
@@ -118,7 +122,19 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 5,
              "DocumentIndex": 6,
              "Title": "StockStatusEmun.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_Common\\StockEnum\\StockStatusEmun.cs",
              "RelativeDocumentMoniker": "WIDESEA_Common\\StockEnum\\StockStatusEmun.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_Common\\StockEnum\\StockStatusEmun.cs",
              "RelativeToolTip": "WIDESEA_Common\\StockEnum\\StockStatusEmun.cs",
              "ViewState": "AgIAACQAAAAAAAAAAAAgwDoAAAAeAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-28T06:15:43.313Z"
            },
            {
              "$type": "Document",
              "DocumentIndex": 10,
              "Title": "LocationStatusEnum.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_Common\\LocationEnum\\LocationStatusEnum.cs",
              "RelativeDocumentMoniker": "WIDESEA_Common\\LocationEnum\\LocationStatusEnum.cs",
@@ -130,7 +146,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 8,
              "DocumentIndex": 13,
              "Title": "IStockService.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_IStockService\\IStockService.cs",
              "RelativeDocumentMoniker": "WIDESEA_IStockService\\IStockService.cs",
@@ -142,7 +158,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 7,
              "DocumentIndex": 12,
              "Title": "StockController.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_WMSServer\\Controllers\\Stock\\StockController.cs",
              "RelativeDocumentMoniker": "WIDESEA_WMSServer\\Controllers\\Stock\\StockController.cs",
@@ -154,7 +170,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 11,
              "DocumentIndex": 15,
              "Title": "Program.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_WMSServer\\Program.cs",
              "RelativeDocumentMoniker": "WIDESEA_WMSServer\\Program.cs",
@@ -166,7 +182,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 14,
              "DocumentIndex": 18,
              "Title": "HttpResponseResult.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_Core\\Helper\\HTTP\\HttpResponseResult.cs",
              "RelativeDocumentMoniker": "WIDESEA_Core\\Helper\\HTTP\\HttpResponseResult.cs",
@@ -178,7 +194,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 13,
              "DocumentIndex": 17,
              "Title": "HttpRequestConfig.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_Core\\Helper\\HTTP\\HttpRequestConfig.cs",
              "RelativeDocumentMoniker": "WIDESEA_Core\\Helper\\HTTP\\HttpRequestConfig.cs",
@@ -190,7 +206,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 12,
              "DocumentIndex": 16,
              "Title": "HttpClientHelper.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_Core\\Helper\\HTTP\\HttpClientHelper.cs",
              "RelativeDocumentMoniker": "WIDESEA_Core\\Helper\\HTTP\\HttpClientHelper.cs",
@@ -202,7 +218,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 21,
              "DocumentIndex": 23,
              "Title": "OutPutDto.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_DTO\\GradingMachine\\OutPutDto.cs",
              "RelativeDocumentMoniker": "WIDESEA_DTO\\GradingMachine\\OutPutDto.cs",
@@ -214,7 +230,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 20,
              "DocumentIndex": 22,
              "Title": "InputDto.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_DTO\\GradingMachine\\InputDto.cs",
              "RelativeDocumentMoniker": "WIDESEA_DTO\\GradingMachine\\InputDto.cs",
@@ -226,19 +242,19 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 22,
              "DocumentIndex": 5,
              "Title": "RepositoryBase.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_Core\\BaseRepository\\RepositoryBase.cs",
              "RelativeDocumentMoniker": "WIDESEA_Core\\BaseRepository\\RepositoryBase.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_Core\\BaseRepository\\RepositoryBase.cs",
              "RelativeToolTip": "WIDESEA_Core\\BaseRepository\\RepositoryBase.cs",
              "ViewState": "AgIAALgDAAAAAAAAAAAhwMsDAAAZAAAAAAAAAA==",
              "ViewState": "AgIAALgDAAAAAAAAAAAawMsDAAAZAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-14T08:55:16.1Z"
            },
            {
              "$type": "Document",
              "DocumentIndex": 19,
              "DocumentIndex": 21,
              "Title": "IRepository.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_Core\\BaseRepository\\IRepository.cs",
              "RelativeDocumentMoniker": "WIDESEA_Core\\BaseRepository\\IRepository.cs",
@@ -250,7 +266,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 18,
              "DocumentIndex": 20,
              "Title": "StockViewService.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_StockService\\StockViewService.cs",
              "RelativeDocumentMoniker": "WIDESEA_StockService\\StockViewService.cs",
@@ -262,7 +278,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 2,
              "DocumentIndex": 9,
              "Title": "ILocationInfoService.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_IBasicService\\ILocationInfoService.cs",
              "RelativeDocumentMoniker": "WIDESEA_IBasicService\\ILocationInfoService.cs",
@@ -274,7 +290,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 17,
              "DocumentIndex": 19,
              "Title": "LocationInfoService.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_BasicService\\LocationInfoService.cs",
              "RelativeDocumentMoniker": "WIDESEA_BasicService\\LocationInfoService.cs",
@@ -286,19 +302,19 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 16,
              "DocumentIndex": 7,
              "Title": "Dt_StockInfo.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_Model\\Models\\Stock\\Dt_StockInfo.cs",
              "RelativeDocumentMoniker": "WIDESEA_Model\\Models\\Stock\\Dt_StockInfo.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_Model\\Models\\Stock\\Dt_StockInfo.cs",
              "RelativeToolTip": "WIDESEA_Model\\Models\\Stock\\Dt_StockInfo.cs",
              "ViewState": "AgIAAEAAAAAAAAAAAAAkwFIAAAAeAAAAAAAAAA==",
              "ViewState": "AgIAAEAAAAAAAAAAAAAkwFIAAAAuAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-11T01:38:37.887Z"
            },
            {
              "$type": "Document",
              "DocumentIndex": 9,
              "DocumentIndex": 14,
              "Title": "StockInfoService.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_StockService\\StockInfoService.cs",
              "RelativeDocumentMoniker": "WIDESEA_StockService\\StockInfoService.cs",
@@ -310,7 +326,7 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 6,
              "DocumentIndex": 11,
              "Title": "IStockInfoService.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_IStockService\\IStockInfoService.cs",
              "RelativeDocumentMoniker": "WIDESEA_IStockService\\IStockInfoService.cs",
@@ -322,76 +338,76 @@
            },
            {
              "$type": "Document",
              "DocumentIndex": 4,
              "DocumentIndex": 3,
              "Title": "TaskController.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_WMSServer\\Controllers\\TaskInfo\\TaskController.cs",
              "RelativeDocumentMoniker": "WIDESEA_WMSServer\\Controllers\\TaskInfo\\TaskController.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_WMSServer\\Controllers\\TaskInfo\\TaskController.cs",
              "RelativeToolTip": "WIDESEA_WMSServer\\Controllers\\TaskInfo\\TaskController.cs",
              "ViewState": "AgIAACcAAAAAAAAAAAAIwDcAAABAAAAAAAAAAA==",
              "ViewState": "AgIAAD0AAAAAAAAAAAAUwFUAAABJAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-09T01:23:19.844Z"
            },
            {
              "$type": "Document",
              "DocumentIndex": 15,
              "DocumentIndex": 8,
              "Title": "CreateTaskDto.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_DTO\\Task\\CreateTaskDto.cs",
              "RelativeDocumentMoniker": "WIDESEA_DTO\\Task\\CreateTaskDto.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_DTO\\Task\\CreateTaskDto.cs",
              "RelativeToolTip": "WIDESEA_DTO\\Task\\CreateTaskDto.cs",
              "ViewState": "AgIAABEAAAAAAAAAAADwvw4AAAARAAAAAAAAAA==",
              "ViewState": "AgIAABsAAAAAAAAAAAAywA4AAAARAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-06T07:58:13.932Z"
            },
            {
              "$type": "Document",
              "DocumentIndex": 3,
              "DocumentIndex": 4,
              "Title": "ITaskService.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_ITaskInfoService\\ITaskService.cs",
              "RelativeDocumentMoniker": "WIDESEA_ITaskInfoService\\ITaskService.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_ITaskInfoService\\ITaskService.cs",
              "RelativeToolTip": "WIDESEA_ITaskInfoService\\ITaskService.cs",
              "ViewState": "AgIAAC0AAAAAAAAAAAAAAEkAAAAIAAAAAAAAAA==",
              "ViewState": "AgIAAEAAAAAAAAAAAAAUwFEAAAAVAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-06T07:00:19.697Z"
            },
            {
              "$type": "Document",
              "DocumentIndex": 1,
              "DocumentIndex": 0,
              "Title": "TaskService.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_TaskInfoService\\TaskService.cs",
              "RelativeDocumentMoniker": "WIDESEA_TaskInfoService\\TaskService.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_TaskInfoService\\TaskService.cs",
              "RelativeToolTip": "WIDESEA_TaskInfoService\\TaskService.cs",
              "ViewState": "AgIAADkAAAAAAAAAAAAUwEkAAAAoAAAAAAAAAA==",
              "ViewState": "AgIAANcAAAAAAAAAAAAgwBYBAABGAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-06T06:34:59.734Z"
              "WhenOpened": "2026-02-06T06:34:59.734Z",
              "EditorCaption": ""
            },
            {
              "$type": "Document",
              "DocumentIndex": 10,
              "DocumentIndex": 1,
              "Title": "StockDTO.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_DTO\\Stock\\StockDTO.cs",
              "RelativeDocumentMoniker": "WIDESEA_DTO\\Stock\\StockDTO.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_DTO\\Stock\\StockDTO.cs",
              "RelativeToolTip": "WIDESEA_DTO\\Stock\\StockDTO.cs",
              "ViewState": "AgIAACMAAAAAAAAAAAAAADoAAAASAAAAAAAAAA==",
              "ViewState": "AgIAACMAAAAAAAAAAAAAADYAAAAuAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-06T02:56:51.397Z"
            },
            {
              "$type": "Document",
              "DocumentIndex": 0,
              "DocumentIndex": 2,
              "Title": "StockSerivce.cs",
              "DocumentMoniker": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_StockService\\StockSerivce.cs",
              "RelativeDocumentMoniker": "WIDESEA_StockService\\StockSerivce.cs",
              "ToolTip": "D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_StockService\\StockSerivce.cs",
              "RelativeToolTip": "WIDESEA_StockService\\StockSerivce.cs",
              "ViewState": "AgIAACUAAAAAAAAAAAAowD8AAAApAAAAAAAAAA==",
              "ViewState": "AgIAAAAAAAAAAAAAAAAAAD8AAAApAAAAAAAAAA==",
              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
              "WhenOpened": "2026-02-06T01:53:49.077Z",
              "EditorCaption": ""
              "WhenOpened": "2026-02-06T01:53:49.077Z"
            }
          ]
        }
Code/WMS/WIDESEA_WMSServer/WIDESEA_Common/StockEnum/StockStatusEmun.cs
@@ -56,6 +56,9 @@
        [Description("退库")]
        MES退库 = 21,
        [Description("空托盘库存")]
        ç©ºæ‰˜ç›˜åº“å­˜ = 22,
        [Description("组盘撤销")]
        ç»„盘撤销 = 99,
Code/WMS/WIDESEA_WMSServer/WIDESEA_ITaskInfoService/ITaskService.cs
@@ -79,6 +79,13 @@
        Task<WebResponseContent> CreateTaskInboundTrayAsync(CreateTaskDto taskDto);
        /// <summary>
        /// åˆ›å»ºç©ºæ‰˜ç›˜å‡ºåº“任务
        /// </summary>
        /// <param name="taskDto"></param>
        /// <returns></returns>
        Task<WebResponseContent> GetOutBoundTrayTaskAsync(CreateTaskDto taskDto);
        /// <summary>
        /// å †åž›æœºå–放货完成后物流通知化成分容柜完成信号
        /// </summary>
        /// <param name="input"></param>
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
@@ -3,6 +3,7 @@
using SqlSugar;
using System.Text.Json;
using WIDESEA_Common.LocationEnum;
using WIDESEA_Common.StockEnum;
using WIDESEA_Common.TaskEnum;
using WIDESEA_Core;
using WIDESEA_Core.BaseRepository;
@@ -270,6 +271,44 @@
            }
        }
        /// <summary>
        /// åˆ›å»ºç©ºæ‰˜ç›˜å‡ºåº“任务
        /// </summary>
        /// <param name="taskDto"></param>
        /// <returns></returns>
        public async Task<WebResponseContent> GetOutBoundTrayTaskAsync(CreateTaskDto taskDto)
        {
            try
            {
                var stockInfo = await _stockInfoService.Repository.QueryFirstAsync(x => x.LocationDetails.WarehouseId == taskDto.WarehouseId && x.LocationDetails.LocationStatus == LocationStatusEnum.InStock.GetHashCode() && x.StockStatus == StockStatusEmun.空托盘库存.GetHashCode());
                if(stockInfo == null)
                    return WebResponseContent.Instance.Error("未找到对应的库存信息");
                var task = new Dt_Task()
                {
                    WarehouseId = stockInfo.LocationDetails.WarehouseId,
                    PalletCode = stockInfo.PalletCode,
                    PalletType = stockInfo.PalletType,
                    SourceAddress = stockInfo.LocationCode,
                    CurrentAddress = stockInfo.LocationCode,
                    NextAddress = taskDto.TargetAddress,
                    TargetAddress = taskDto.TargetAddress,
                    Roadway = stockInfo.LocationDetails.RoadwayNo,
                    TaskType = TaskTypeEnum.OutEmpty.GetHashCode(),
                    TaskStatus = TaskStatusEnum.New.GetHashCode(),
                    Grade = 1,
                    TaskNum = await BaseDal.GetTaskNo(),
                    Creater = "system",
                };
                 var taskDtos = _mapper.Map<List<WMSTaskDTO>>(task);
                return WebResponseContent.Instance.OK("查询成功", taskDtos);
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"查询任务失败: {ex.Message}");
            }
        }
        #region åˆ†å®¹æŸœæŽ¥å£
        /// <summary>
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs
@@ -79,6 +79,17 @@
        }
        /// <summary>
        /// åˆ›å»ºç©ºæ‰˜ç›˜å‡ºåº“任务
        /// </summary>
        /// <param name="taskDto"></param>
        /// <returns></returns>
        [HttpGet, HttpPost, Route("GetOutBoundTrayTask"), AllowAnonymous]
        public async Task<WebResponseContent?> GetOutBoundTrayTaskAsync([FromBody] CreateTaskDto taskDto)
        {
            return await Service.GetOutBoundTrayTaskAsync(taskDto);
        }
        /// <summary>
        /// å †åž›æœºå–放货完成后物流通知化成分容柜完成信号
        /// </summary>
        /// <param name="input"></param>