添加Redis服务与缓存增强
引入新的Redis服务项目,并在整个解决方案中扩展缓存功能。新增WIDESEAWCS_RedisService,包含众多组件(位图、布隆过滤器、混合/Redis缓存、连接管理器、配置中心、计数器、延迟队列、地理空间、ID生成器、排行榜、分布式锁、监控、选项配置、发布/订阅、限流、序列化、会话、对象存储、流式处理及服务设置)。通过ConcurrentDictionary风格的方法(TryAdd/TryGetValue/TryRemove/TryUpdate/GetOrAdd重载)扩展ICacheService接口,并在MemoryCacheService中实现这些方法。更新HttpClientHelper以依赖ICacheService并利用缓存处理URL,同时在RouterService中注入缓存以缓存设备位置。此外,更新了解决方案/项目文件、Program.cs和appsettings.json,并添加了一些任务/控制器/源文件以及Visual Studio文档布局条目。
| | |
| | | { |
| | | "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": [ |
| | |
| | | "DocumentGroups": [ |
| | | { |
| | | "DockedWidth": 200, |
| | | "SelectedChildIndex": 3, |
| | | "SelectedChildIndex": 14, |
| | | "Children": [ |
| | | { |
| | | "$type": "Bookmark", |
| | |
| | | }, |
| | | { |
| | | "$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": "" |
| | |
| | | /// <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 |
| | | } |
| | | } |
| | |
| | | |
| | | 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; |
| | | } |
| | | } |
| | | } |
| | |
| | | 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> |
| | |
| | | /// <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); |
| | |
| | | using WIDESEAWCS_DTO.BasicInfo; |
| | | using WIDESEAWCS_QuartzJob.Models; |
| | | using WIDESEAWCS_QuartzJob.Repository; |
| | | using ICacheService = WIDESEAWCS_Core.Caches.ICacheService; |
| | | |
| | | namespace WIDESEAWCS_QuartzJob.Service |
| | | { |
| | |
| | | { |
| | | private readonly IDeviceProtocolRepository _deviceProtocolRepository; |
| | | private readonly IDeviceInfoRepository _deviceInfoRepository; |
| | | private readonly ICacheService _cacheService; |
| | | |
| | | /// <summary> |
| | | /// è·¯ç±é
ç½®ä¸å¡å± |
| | |
| | | /// <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> |
| | |
| | | { |
| | | // 䏿¬¡æ§æ¥è¯¢ææè·¯ç±æ°æ®å°å
å |
| | | List<Dt_Router> allRouters = BaseDal.QueryData(x => true); |
| | | |
| | | |
| | | // å¨å
åä¸è¿è¡è·¯å¾æç´¢ |
| | | routers = FindRoutesInMemory(startPosi, endPosi, allRouters, null); |
| | | |
| | | |
| | | if (routers.Count == 0) |
| | | { |
| | | throw new Exception($"è¯¥è·¯å¾æªé
ç½®æé
ç½®é误,è¯·æ£æ¥è®¾å¤è·¯ç±ä¿¡æ¯,èµ·ç¹:ã{startPosi}ã,ç»ç¹:ã{endPosi}ã"); |
| | |
| | | { |
| | | // 䏿¬¡æ§æ¥è¯¢æå®ç±»åçææè·¯ç±æ°æ®å°å
å |
| | | List<Dt_Router> allRouters = BaseDal.QueryData(x => x.InOutType == routeType); |
| | | |
| | | |
| | | // å¨å
åä¸è¿è¡è·¯å¾æç´¢ |
| | | routers = FindRoutesInMemory(startPosi, endPosi, allRouters, routeType); |
| | | |
| | | |
| | | if (routers.Count == 0) |
| | | { |
| | | throw new Exception($"è¯¥è·¯å¾æªé
ç½®æé
ç½®é误,è¯·æ£æ¥è®¾å¤è·¯ç±ä¿¡æ¯,èµ·ç¹:ã{startPosi}ã,ç»ç¹:ã{endPosi}ã"); |
| | |
| | | var routersByNext = allRouters |
| | | .GroupBy(r => r.NextPosi) |
| | | .ToDictionary(g => g.Key, g => g.ToList()); |
| | | |
| | | |
| | | var routersByChild = allRouters |
| | | .GroupBy(r => r.ChildPosi) |
| | | .ToDictionary(g => g.Key, g => g.ToList()); |
| | |
| | | |
| | | // 使ç¨éåè¿è¡å¹¿åº¦ä¼å
æç´¢ |
| | | Queue<(Dt_Router router, List<Dt_Router> path)> queue = new Queue<(Dt_Router, List<Dt_Router>)>(); |
| | | |
| | | |
| | | // å°ææç»ç¹è·¯ç±å å
¥éå |
| | | foreach (var endRouter in endRouters) |
| | | { |
| | |
| | | try |
| | | { |
| | | // æ¥è¯¢ä»èµ·ç¹åºåçææè·¯ç± |
| | | List<Dt_Router> routes = BaseDal.QueryData(x => x.StartPosi == startPosi, |
| | | List<Dt_Router> routes = BaseDal.QueryData(x => x.StartPosi == startPosi, |
| | | new Dictionary<string, OrderByType> { { nameof(Dt_Router.IsEnd), OrderByType.Desc } }); |
| | | |
| | | |
| | | // è¿å第ä¸ä¸ªè·¯ç± |
| | | return routes.FirstOrDefault(); |
| | | } |
| | |
| | | try |
| | | { |
| | | // æ¥è¯¢ä»èµ·ç¹åºåçæå®ç±»åè·¯ç± |
| | | List<Dt_Router> routes = BaseDal.QueryData(x => x.StartPosi == startPosi && x.InOutType == routeType, |
| | | List<Dt_Router> routes = BaseDal.QueryData(x => x.StartPosi == startPosi && x.InOutType == routeType, |
| | | new Dictionary<string, OrderByType> { { nameof(Dt_Router.IsEnd), OrderByType.Desc } }); |
| | | |
| | | |
| | | // è¿å第ä¸ä¸ªè·¯ç± |
| | | return routes.FirstOrDefault(); |
| | | } |
| | |
| | | try |
| | | { |
| | | // æ¥è¯¢ä»èµ·ç¹åºåçæå®ç±»åè·¯ç± |
| | | List<Dt_Router> routes = BaseDal.QueryData(x => x.StartPosi == startPosi && x.InOutType == routeType, |
| | | List<Dt_Router> routes = BaseDal.QueryData(x => x.StartPosi == startPosi && x.InOutType == routeType, |
| | | new Dictionary<string, OrderByType> { { nameof(Dt_Router.IsEnd), OrderByType.Desc } }); |
| | | |
| | | |
| | | if (routes.Count == 0) |
| | | return null; |
| | | |
| | |
| | | List<Dt_Router> path = new List<Dt_Router>(); |
| | | string currentPosi = startPosi; |
| | | HashSet<string> visitedPositions = new HashSet<string>(); |
| | | |
| | | |
| | | try |
| | | { |
| | | while (currentPosi != endPosi) |
| | |
| | | visitedPositions.Add(currentPosi); |
| | | |
| | | Dt_Router nextRoute = QueryNextRoute(currentPosi, endPosi, routeType); |
| | | |
| | | |
| | | if (nextRoute == null) |
| | | { |
| | | break; |
| | | } |
| | | |
| | | |
| | | path.Add(nextRoute); |
| | | currentPosi = nextRoute.NextPosi; |
| | | |
| | | |
| | | if (path.Count > 1000) |
| | | { |
| | | break; |
| | | } |
| | | } |
| | | |
| | | |
| | | if (currentPosi != endPosi) |
| | | { |
| | | return new List<Dt_Router>(); |
| | |
| | | { |
| | | return new List<Dt_Router>(); |
| | | } |
| | | |
| | | |
| | | return path; |
| | | } |
| | | |
| | |
| | | { |
| | | // å建ä¸ä¸ªå符串å表ï¼ç¨äºå卿æä½ç½® |
| | | List<string> positions = new List<string>(); |
| | | try |
| | | { |
| | | // æ¥è¯¢ææè¿å
¥è·¯ç±å¨çä½ç½® |
| | | List<string> inRouterPositions = BaseDal.QueryData(x => x.ChildPosiDeviceCode == deviceCode && x.InOutType == RouterInOutType.In.ObjToInt()).GroupBy(x => x.StartPosi).Select(x => x.Key).ToList(); |
| | | |
| | | // æ¥è¯¢ææç¦»å¼è·¯ç±å¨çä½ç½® |
| | | List<string> outRouterPositions = BaseDal.QueryData(x => x.ChildPosiDeviceCode == deviceCode && x.InOutType == RouterInOutType.Out.ObjToInt()).GroupBy(x => x.ChildPosi).Select(x => x.Key).ToList(); |
| | | |
| | | // å°è¿å
¥å离å¼è·¯ç±å¨çä½ç½®æ·»å å°åè¡¨ä¸ |
| | | positions.AddRange(inRouterPositions); |
| | | positions.AddRange(outRouterPositions); |
| | | // è¿åå»éåçä½ç½®å表 |
| | | return positions.GroupBy(x => x).Select(x => x.Key).ToList(); |
| | | } |
| | | catch |
| | | var device = _cacheService.Get<List<string>>($"DevicePositions:{deviceCode}"); |
| | | if (device.IsNullOrEmpty()) |
| | | { |
| | | |
| | | try |
| | | { |
| | | // æ¥è¯¢ææè¿å
¥è·¯ç±å¨çä½ç½® |
| | | List<string> inRouterPositions = BaseDal.QueryData(x => x.ChildPosiDeviceCode == deviceCode && x.InOutType == RouterInOutType.In.ObjToInt()).GroupBy(x => x.StartPosi).Select(x => x.Key).ToList(); |
| | | |
| | | // æ¥è¯¢ææç¦»å¼è·¯ç±å¨çä½ç½® |
| | | List<string> outRouterPositions = BaseDal.QueryData(x => x.ChildPosiDeviceCode == deviceCode && x.InOutType == RouterInOutType.Out.ObjToInt()).GroupBy(x => x.ChildPosi).Select(x => x.Key).ToList(); |
| | | |
| | | // å°è¿å
¥å离å¼è·¯ç±å¨çä½ç½®æ·»å å°åè¡¨ä¸ |
| | | positions.AddRange(inRouterPositions); |
| | | positions.AddRange(outRouterPositions); |
| | | // è¿åå»éåçä½ç½®å表 |
| | | return positions.GroupBy(x => x).Select(x => x.Key).ToList(); |
| | | } |
| | | catch |
| | | { |
| | | |
| | | } |
| | | } |
| | | else |
| | | positions = device; |
| | | // è¿åä½ç½®å表 |
| | | return positions; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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)); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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; |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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; |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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); |
| | | }); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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; } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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(); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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; |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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)); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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; |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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; } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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)); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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; } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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)); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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); |
| | | } |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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; } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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; |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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"; |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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)); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | namespace WIDESEAWCS_RedisService.Serialization |
| | | { |
| | | public interface IRedisSerializer |
| | | { |
| | | string Serialize<T>(T value); |
| | | |
| | | T? Deserialize<T>(string value); |
| | | |
| | | object? Deserialize(string value, Type type); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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)); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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(); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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); |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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> |
| | |
| | | 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 |
| | |
| | | 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) => |
| | |
| | | }); |
| | | 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(); |
| | | |
| | |
| | | |
| | | 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>(); |
| | | |
| | |
| | | |
| | | builder.Services.AddAuthorizationSetup(); |
| | | |
| | | builder.Services.AddIpPolicyRateLimitSetup(builder.Configuration);//IPLimitéæµ å¯å¨æå¡ |
| | | builder.Services.AddIpPolicyRateLimitSetup(builder.Configuration);//IPLimit���� �������� |
| | | |
| | | builder.Services.AddScoped<UseServiceDIAttribute>(); |
| | | |
| | |
| | | |
| | | builder.Services.AddControllers(o => |
| | | { |
| | | o.Filters.Add(typeof(GlobalExceptionsFilter));//å
¨å±å¼å¸¸ |
| | | o.Filters.Add(typeof(GlobalExceptionsFilter));//ȫ���쳣 |
| | | }) |
| | | .AddNewtonsoftJson(options => |
| | | { |
| | |
| | | |
| | | 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(); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # 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; |
| | | } |
| | | // å¯è½åå¨ï¼ç»§ç»æ¥è¯¢æ°æ®åº... |
| | | } |
| | | } |
| | | ``` |
| | |
| | | <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> |
| | |
| | | </EmbeddedResource> |
| | | </ItemGroup> |
| | | |
| | | <ItemGroup> |
| | | <Folder Include="Log\" /> |
| | | </ItemGroup> |
| | | |
| | | </Project> |
| | |
| | | "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": { |
| | | "Enabled": true, //æ¯å¦å¯ç¨Socketæå¡å¨ |
| | | "Port": 2000, //çå¬ç«¯å£ |
| | | "Port": 2000, //çå¬ç«¯å£ |
| | | "IpAddress": "0.0.0.0", //çå¬å°åï¼ |
| | | "Backlog": 1000, //æå¤§è¿æ¥æ° |
| | | "EncodingName": "utf-8", //ç¼ç æ¹å¼ |
| | | "AutoDetectEncoding": true, //æ¯å¦èªå¨æ£æµç¼ç |
| | | "IdleTimeoutSeconds": 0, //空é²è¶
æ¶æ¶é´ï¼åä½ç§ï¼0表示ä¸è¶
æ¶ |
| | | "EnableHeartbeat": true, //æ¯å¦å¯ç¨å¿è·³æ£æµ |
| | | "LogFilePath": "socketserver.log" //æ¥å¿æä»¶è·¯å¾ |
| | | "Backlog": 1000, //æå¤§è¿æ¥æ° |
| | | "EncodingName": "utf-8", //ç¼ç æ¹å¼ |
| | | "AutoDetectEncoding": true, //æ¯å¦èªå¨æ£æµç¼ç |
| | | "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ç |
| | | } |
| | | } |
| | | } |
| | |
| | | #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; |
| | |
| | | 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) |
| | |
| | | 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; |
| | |
| | | // å¤çä»»å¡ç¶æ |
| | | ProcessTaskState(conveyorLine, command, task, childDeviceCode); |
| | | } |
| | | |
| | | } |
| | | catch (Exception innerEx) |
| | | { |
| | |
| | | } |
| | | return Task.CompletedTask; |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// å¤çä»»å¡ç¶æ |
| | |
| | | break; |
| | | } |
| | | } |
| | | |
| | | |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | namespace WIDESEAWCS_Tasks |
| | | { |
| | | public class CheckPalletPosition |
| | | { |
| | | public string Code { get; set; } = string.Empty; |
| | | |
| | | public int WarehouseId { get; set; } = 0; |
| | | } |
| | | } |
| | |
| | | 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; |
| | |
| | | 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; |
| | |
| | | string ipAddress = robotCrane.IPAddress; |
| | | |
| | | // è·åæåå»ºç¶æ |
| | | RobotSocketState state = _socketStates.GetOrAdd(ipAddress, _ => new RobotSocketState |
| | | RobotSocketState state = _cache.GetOrAdd(ipAddress, _ => new RobotSocketState |
| | | { |
| | | IPAddress = ipAddress, |
| | | RobotCrane = robotCrane |
| | |
| | | // æ´æ°è®¾å¤ä¿¡æ¯ |
| | | state.RobotCrane = robotCrane; |
| | | |
| | | // æ£æ¥æ¯å¦æè¯¥å®¢æ·ç«¯è¿æ¥ |
| | | var clientIds = _TcpSocket.GetClientIds(); |
| | | if (!clientIds.Contains(ipAddress)) |
| | | try |
| | | { |
| | | return; |
| | | } |
| | | |
| | | // 订é
䏿¬¡ message äºä»¶ï¼å
¨å±ä¸æ¬¡ï¼ |
| | | if (Interlocked.CompareExchange(ref _eventSubscribedFlag, 1, 0) == 0) |
| | | { |
| | | _TcpSocket.MessageReceived += _TcpSocket_MessageReceived; |
| | | _TcpSocket.RobotReceived += _TcpSocket_RobotReceived; |
| | | } |
| | | |
| | | if (!state.IsEventSubscribed) |
| | | { |
| | | if (_TcpSocket._clients.TryGetValue(ipAddress, out TcpClient client)) |
| | | // æ£æ¥æ¯å¦æè¯¥å®¢æ·ç«¯è¿æ¥ |
| | | var clientIds = _TcpSocket.GetClientIds(); |
| | | if (!clientIds.Contains(ipAddress)) |
| | | { |
| | | _ = _TcpSocket.HandleClientAsync(client, robotCrane.IPAddress, _TcpSocket._cts.Token, state) |
| | | .ContinueWith(t => |
| | | { |
| | | if (t.IsFaulted) |
| | | Console.WriteLine($"HandleClientAsync error: {t.Exception?.GetBaseException().Message}"); |
| | | }, TaskContinuationOptions.OnlyOnFaulted); |
| | | state.IsEventSubscribed = true; |
| | | return; |
| | | } |
| | | } |
| | | |
| | | // è·åä»»å¡å¹¶ç¼åå°ç¶æä¸ |
| | | Dt_RobotTask? task = GetTask(robotCrane); |
| | | if (task != null) |
| | | { |
| | | state.IsSplitPallet = task.RobotTaskType == RobotTaskTypeEnum.SplitPallet.GetHashCode(); |
| | | state.IsGroupPallet = task.RobotTaskType == RobotTaskTypeEnum.GroupPallet.GetHashCode() || task.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode(); |
| | | state.CurrentTask = task; |
| | | if (task.RobotTaskTotalNum <= MaxTaskTotalNum) |
| | | // 订é
䏿¬¡ message äºä»¶ï¼å
¨å±ä¸æ¬¡ï¼ |
| | | if (Interlocked.CompareExchange(ref _eventSubscribedFlag, 1, 0) == 0) |
| | | { |
| | | // å¤çæ£å¨æ§è¡çä»»å¡ |
| | | if (state.RobotRunMode == 2 && state.RobotControlMode == 1 && state.OperStatus != "Running") |
| | | _TcpSocket.MessageReceived += _TcpSocket_MessageReceived; |
| | | _TcpSocket.RobotReceived += _TcpSocket_RobotReceived; |
| | | } |
| | | |
| | | if (!state.IsEventSubscribed) |
| | | { |
| | | if (_TcpSocket._clients.TryGetValue(ipAddress, out TcpClient client)) |
| | | { |
| | | await Task.Delay(1000); |
| | | if (state.CurrentAction == "PickFinished" && state.RobotArmObject == 1 && task.RobotTaskState != TaskRobotStatusEnum.RobotExecuting.GetHashCode()) |
| | | _ = _TcpSocket.HandleClientAsync(client, robotCrane.IPAddress, _TcpSocket._cts.Token, state) |
| | | .ContinueWith(t => |
| | | { |
| | | if (t.IsFaulted) |
| | | Console.WriteLine($"HandleClientAsync error: {t.Exception?.GetBaseException().Message}"); |
| | | }, TaskContinuationOptions.OnlyOnFaulted); |
| | | state.IsEventSubscribed = true; |
| | | } |
| | | } |
| | | |
| | | // è·åä»»å¡å¹¶ç¼åå°ç¶æä¸ |
| | | Dt_RobotTask? task = GetTask(robotCrane); |
| | | if (task != null) |
| | | { |
| | | state.IsSplitPallet = task.RobotTaskType == RobotTaskTypeEnum.SplitPallet.GetHashCode(); |
| | | state.IsGroupPallet = task.RobotTaskType == RobotTaskTypeEnum.GroupPallet.GetHashCode() || task.RobotTaskType == RobotTaskTypeEnum.ChangePallet.GetHashCode(); |
| | | state.CurrentTask = task; |
| | | if (task.RobotTaskTotalNum <= MaxTaskTotalNum) |
| | | { |
| | | // å¤çæ£å¨æ§è¡çä»»å¡ |
| | | if (state.RobotRunMode == 2 && state.RobotControlMode == 1 && state.OperStatus != "Running") |
| | | { |
| | | string taskString = $"Putbattery,{task.RobotTargetAddress}"; |
| | | bool result = await _TcpSocket.SendToClientAsync(ipAddress, taskString); |
| | | if (result) |
| | | await Task.Delay(1000); |
| | | if (state.CurrentAction == "PickFinished" && state.RobotArmObject == 1 && task.RobotTaskState != TaskRobotStatusEnum.RobotExecuting.GetHashCode()) |
| | | { |
| | | string taskString = $"Putbattery,{task.RobotTargetAddress}"; |
| | | bool result = await _TcpSocket.SendToClientAsync(ipAddress, taskString); |
| | | if (result) |
| | | { |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode(); |
| | | await _robotTaskService.UpdateRobotTaskAsync(task); |
| | | } |
| | | } |
| | | else if (state.CurrentAction == "PutFinished" && state.RobotArmObject == 0 && task.RobotTaskState != TaskRobotStatusEnum.RobotExecuting.GetHashCode()) |
| | | { |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode(); |
| | | await _robotTaskService.UpdateRobotTaskAsync(task); |
| | | } |
| | | } |
| | | else if (state.CurrentAction == "PutFinished" && state.RobotArmObject == 0 && task.RobotTaskState != TaskRobotStatusEnum.RobotExecuting.GetHashCode()) |
| | | { |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode(); |
| | | await _robotTaskService.UpdateRobotTaskAsync(task); |
| | | } |
| | | else if (state.OperStatus == "Homed" && state.RobotArmObject == 0 && task.RobotTaskState != TaskRobotStatusEnum.RobotExecuting.GetHashCode()) |
| | | { |
| | | // TODO 读å线ä½çµæ± æ¡ç ï¼åéåçµæ± æä»¤ |
| | | // éæºçæä¸¤å¤©æçæ¡ç åæ¾å°ä¸¤ä¸ªåééé¢ |
| | | // å®ä¹åç¼ï¼ä¾å¦ï¼TRAY代表æçï¼ |
| | | string prefix = "TRAY"; |
| | | |
| | | // çæä¸¤ä¸ªæçæ¡ç |
| | | string trayBarcode1 = GenerateTrayBarcode(state, prefix); |
| | | string trayBarcode2 = GenerateTrayBarcode(state, prefix); |
| | | if (!trayBarcode1.IsNullOrEmpty() && !trayBarcode2.IsNullOrEmpty()) |
| | | else if (state.OperStatus == "Homed" && state.RobotArmObject == 0 && task.RobotTaskState != TaskRobotStatusEnum.RobotExecuting.GetHashCode()) |
| | | { |
| | | string taskString = $"Pickbattery,{task.RobotSourceAddress}"; |
| | | // åé任塿令 |
| | | bool result = await _TcpSocket.SendToClientAsync(ipAddress, taskString); |
| | | if (result) |
| | | // TODO 读å线ä½çµæ± æ¡ç ï¼åéåçµæ± æä»¤ |
| | | // éæºçæä¸¤å¤©æçæ¡ç åæ¾å°ä¸¤ä¸ªåééé¢ |
| | | // å®ä¹åç¼ï¼ä¾å¦ï¼TRAY代表æçï¼ |
| | | string prefix = "TRAY"; |
| | | |
| | | // çæä¸¤ä¸ªæçæ¡ç |
| | | string trayBarcode1 = GenerateTrayBarcode(state, prefix); |
| | | string trayBarcode2 = GenerateTrayBarcode(state, prefix); |
| | | if (!trayBarcode1.IsNullOrEmpty() && !trayBarcode2.IsNullOrEmpty()) |
| | | { |
| | | // TODO å¤çæååé任塿令åçé»è¾ |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode(); |
| | | result = await _robotTaskService.UpdateRobotTaskAsync(task); |
| | | string taskString = $"Pickbattery,{task.RobotSourceAddress}"; |
| | | // åé任塿令 |
| | | bool result = await _TcpSocket.SendToClientAsync(ipAddress, taskString); |
| | | if (result) |
| | | { |
| | | // TODO å¤çæååé任塿令åçé»è¾ |
| | | task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode(); |
| | | result = await _robotTaskService.UpdateRobotTaskAsync(task); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | catch (Exception) |
| | | { |
| | | |
| | | } |
| | | finally |
| | | { |
| | | // å¯éï¼å¨è¿éå¤çä»»ä½éè¦å¨ä»»å¡å®æåæ§è¡çæ¸
çå·¥ä½ |
| | | // æ´æ°ç¼åä¸çç¶æ |
| | | _cache.AddOrUpdate(ipAddress, state); |
| | | } |
| | | } |
| | | |
| | |
| | | /// <returns></returns> |
| | | private Task<string?> _TcpSocket_RobotReceived(string clientId) |
| | | { |
| | | _socketStates.TryRemove(clientId, out _); |
| | | _cache.TryRemove(clientId, out _); |
| | | return Task.FromResult<string?>(null); |
| | | } |
| | | |
| | |
| | | "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}", |
| | |
| | | "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}" |
| | | }, |
| | |
| | | { |
| | | "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}", |
| | |
| | | { |
| | | "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": [ |
| | |
| | | "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}" |
| | | }, |
| | |
| | | "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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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": "" |
| | |
| | | "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}", |
| | |
| | | "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}" |
| | | }, |
| | |
| | | { |
| | | "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}", |
| | |
| | | { |
| | | "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": [ |
| | |
| | | "DocumentGroups": [ |
| | | { |
| | | "DockedWidth": 200, |
| | | "SelectedChildIndex": 25, |
| | | "SelectedChildIndex": 24, |
| | | "Children": [ |
| | | { |
| | | "$type": "Bookmark", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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", |
| | |
| | | }, |
| | | { |
| | | "$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" |
| | | } |
| | | ] |
| | | } |
| | |
| | | [Description("éåº")] |
| | | MESéåº = 21, |
| | | |
| | | [Description("空æçåºå")] |
| | | 空æçåºå = 22, |
| | | |
| | | [Description("ç»çæ¤é")] |
| | | ç»çæ¤é = 99, |
| | | |
| | |
| | | Task<WebResponseContent> CreateTaskInboundTrayAsync(CreateTaskDto taskDto); |
| | | |
| | | /// <summary> |
| | | /// å建空æçåºåºä»»å¡ |
| | | /// </summary> |
| | | /// <param name="taskDto"></param> |
| | | /// <returns></returns> |
| | | Task<WebResponseContent> GetOutBoundTrayTaskAsync(CreateTaskDto taskDto); |
| | | |
| | | /// <summary> |
| | | /// å åæºåæ¾è´§å®æåç©æµéç¥åæå容æå®æä¿¡å· |
| | | /// </summary> |
| | | /// <param name="input"></param> |
| | |
| | | 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; |
| | |
| | | } |
| | | } |
| | | |
| | | /// <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> |
| | |
| | | } |
| | | |
| | | /// <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> |