autosnippet 3.0.0 → 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +230 -324
- package/bin/api-server.js +1 -1
- package/bin/cli.js +204 -244
- package/bin/mcp-server.js +5 -3
- package/config/knowledge-base.config.js +132 -132
- package/dashboard/dist/assets/{icons-CEfgGaZi.js → icons-Cdq22n2i.js} +95 -100
- package/dashboard/dist/assets/index-ClkyPkDX.js +133 -0
- package/dashboard/dist/assets/index-t4QrJwv1.css +1 -0
- package/dashboard/dist/index.html +3 -3
- package/lib/bootstrap.js +8 -8
- package/lib/cli/AiScanService.js +86 -40
- package/lib/cli/KnowledgeSyncService.js +113 -74
- package/lib/cli/SetupService.js +439 -277
- package/lib/cli/UpgradeService.js +63 -100
- package/lib/core/AstAnalyzer.js +276 -597
- package/lib/core/ast/ProjectGraph.js +101 -40
- package/lib/core/ast/ensure-grammars.js +232 -0
- package/lib/core/ast/index.js +115 -0
- package/lib/core/ast/lang-dart.js +661 -0
- package/lib/core/ast/lang-go.js +530 -0
- package/lib/core/ast/lang-java.js +435 -0
- package/lib/core/ast/lang-javascript.js +272 -0
- package/lib/core/ast/lang-kotlin.js +423 -0
- package/lib/core/ast/lang-objc.js +388 -0
- package/lib/core/ast/lang-python.js +371 -0
- package/lib/core/ast/lang-swift.js +337 -0
- package/lib/core/ast/lang-typescript.js +503 -0
- package/lib/core/capability/CapabilityProbe.js +18 -9
- package/lib/core/constitution/Constitution.js +2 -3
- package/lib/core/constitution/ConstitutionValidator.js +65 -24
- package/lib/core/discovery/DartDiscoverer.js +534 -0
- package/lib/core/discovery/DiscovererRegistry.js +83 -0
- package/lib/core/discovery/GenericDiscoverer.js +225 -0
- package/lib/core/discovery/GoDiscoverer.js +541 -0
- package/lib/core/discovery/JvmDiscoverer.js +506 -0
- package/lib/core/discovery/NodeDiscoverer.js +466 -0
- package/lib/core/discovery/ProjectDiscoverer.js +93 -0
- package/lib/core/discovery/PythonDiscoverer.js +338 -0
- package/lib/core/discovery/SpmDiscoverer.js +5 -0
- package/lib/core/discovery/index.js +53 -0
- package/lib/core/enhancement/EnhancementPack.js +71 -0
- package/lib/core/enhancement/EnhancementRegistry.js +47 -0
- package/lib/core/enhancement/android-enhancement.js +102 -0
- package/lib/core/enhancement/django-enhancement.js +70 -0
- package/lib/core/enhancement/fastapi-enhancement.js +63 -0
- package/lib/core/enhancement/go-grpc-enhancement.js +152 -0
- package/lib/core/enhancement/go-web-enhancement.js +201 -0
- package/lib/core/enhancement/index.js +65 -0
- package/lib/core/enhancement/node-server-enhancement.js +88 -0
- package/lib/core/enhancement/react-enhancement.js +86 -0
- package/lib/core/enhancement/spring-enhancement.js +112 -0
- package/lib/core/enhancement/vue-enhancement.js +96 -0
- package/lib/core/gateway/Gateway.js +8 -9
- package/lib/core/gateway/GatewayActionRegistry.js +1 -1
- package/lib/core/permission/PermissionManager.js +12 -8
- package/lib/domain/index.js +13 -9
- package/lib/domain/knowledge/KnowledgeEntry.js +111 -101
- package/lib/domain/knowledge/KnowledgeRepository.js +0 -1
- package/lib/domain/knowledge/Lifecycle.js +22 -22
- package/lib/domain/knowledge/index.js +9 -12
- package/lib/domain/knowledge/values/Constraints.js +31 -21
- package/lib/domain/knowledge/values/Content.js +21 -13
- package/lib/domain/knowledge/values/Quality.js +31 -18
- package/lib/domain/knowledge/values/Reasoning.js +20 -12
- package/lib/domain/knowledge/values/Relations.js +37 -25
- package/lib/domain/knowledge/values/Stats.js +18 -12
- package/lib/domain/knowledge/values/index.js +4 -3
- package/lib/domain/snippet/Snippet.js +35 -10
- package/lib/external/ai/AiFactory.js +48 -16
- package/lib/external/ai/AiProvider.js +184 -90
- package/lib/external/ai/providers/ClaudeProvider.js +25 -12
- package/lib/external/ai/providers/GoogleGeminiProvider.js +59 -30
- package/lib/external/ai/providers/MockProvider.js +9 -3
- package/lib/external/ai/providers/OpenAiProvider.js +51 -29
- package/lib/external/mcp/McpServer.js +66 -36
- package/lib/external/mcp/errorHandler.js +23 -11
- package/lib/external/mcp/handlers/LanguageExtensions.js +138 -53
- package/lib/external/mcp/handlers/TargetClassifier.js +52 -16
- package/lib/external/mcp/handlers/bootstrap/pipeline/BootstrapSnapshot.js +81 -20
- package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +71 -42
- package/lib/external/mcp/handlers/bootstrap/pipeline/IncrementalBootstrap.js +9 -17
- package/lib/external/mcp/handlers/bootstrap/pipeline/ToolResultCache.js +14 -9
- package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.js +15 -7
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +352 -153
- package/lib/external/mcp/handlers/bootstrap/pipeline/tier-scheduler.js +52 -12
- package/lib/external/mcp/handlers/bootstrap/skills.js +143 -39
- package/lib/external/mcp/handlers/bootstrap.js +691 -168
- package/lib/external/mcp/handlers/browse.js +66 -22
- package/lib/external/mcp/handlers/candidate.js +118 -35
- package/lib/external/mcp/handlers/consolidated.js +49 -17
- package/lib/external/mcp/handlers/guard.js +104 -39
- package/lib/external/mcp/handlers/knowledge.js +60 -36
- package/lib/external/mcp/handlers/search.js +43 -14
- package/lib/external/mcp/handlers/skill.js +120 -45
- package/lib/external/mcp/handlers/structure.js +240 -86
- package/lib/external/mcp/handlers/system.js +42 -12
- package/lib/external/mcp/handlers/wiki.js +58 -33
- package/lib/external/mcp/tools.js +306 -123
- package/lib/http/HttpServer.js +72 -47
- package/lib/http/middleware/RateLimiter.js +5 -3
- package/lib/http/middleware/errorHandler.js +6 -1
- package/lib/http/middleware/requestLogger.js +14 -3
- package/lib/http/middleware/roleResolver.js +30 -23
- package/lib/http/routes/ai.js +387 -265
- package/lib/http/routes/auth.js +81 -61
- package/lib/http/routes/candidates.js +430 -320
- package/lib/http/routes/commands.js +289 -189
- package/lib/http/routes/extract.js +158 -125
- package/lib/http/routes/guardRules.js +309 -217
- package/lib/http/routes/knowledge.js +213 -154
- package/lib/http/routes/modules.js +578 -0
- package/lib/http/routes/monitoring.js +6 -6
- package/lib/http/routes/recipes.js +104 -93
- package/lib/http/routes/search.js +361 -305
- package/lib/http/routes/skills.js +145 -98
- package/lib/http/routes/snippets.js +42 -30
- package/lib/http/routes/spm.js +3 -405
- package/lib/http/routes/violations.js +113 -93
- package/lib/http/routes/wiki.js +211 -170
- package/lib/http/utils/routeHelpers.js +3 -1
- package/lib/http/utils/sse-sessions.js +16 -6
- package/lib/http/utils/sse.js +15 -5
- package/lib/infrastructure/audit/AuditLogger.js +5 -2
- package/lib/infrastructure/audit/AuditStore.js +10 -7
- package/lib/infrastructure/cache/CacheService.js +3 -1
- package/lib/infrastructure/cache/GraphCache.js +8 -4
- package/lib/infrastructure/cache/UnifiedCacheAdapter.js +1 -1
- package/lib/infrastructure/config/ConfigLoader.js +9 -5
- package/lib/infrastructure/config/Defaults.js +30 -10
- package/lib/infrastructure/config/Paths.js +28 -8
- package/lib/infrastructure/config/TriggerSymbol.js +22 -10
- package/lib/infrastructure/database/DatabaseConnection.js +15 -10
- package/lib/infrastructure/database/migrations/001_initial_schema.js +0 -1
- package/lib/infrastructure/external/ClipboardManager.js +6 -2
- package/lib/infrastructure/external/NativeUi.js +50 -43
- package/lib/infrastructure/external/OpenBrowser.js +14 -17
- package/lib/infrastructure/external/XcodeAutomation.js +14 -258
- package/lib/infrastructure/logging/Logger.js +46 -30
- package/lib/infrastructure/monitoring/ErrorTracker.js +7 -5
- package/lib/infrastructure/monitoring/PerformanceMonitor.js +12 -4
- package/lib/infrastructure/paths/HeaderResolver.js +25 -9
- package/lib/infrastructure/paths/PathFinder.js +34 -12
- package/lib/infrastructure/plugin/PluginManager.js +26 -8
- package/lib/infrastructure/realtime/RealtimeService.js +2 -2
- package/lib/infrastructure/vector/Chunker.js +22 -7
- package/lib/infrastructure/vector/IndexingPipeline.js +46 -22
- package/lib/infrastructure/vector/JsonVectorAdapter.js +90 -53
- package/lib/infrastructure/vector/VectorStore.js +28 -10
- package/lib/injection/ServiceContainer.js +247 -93
- package/lib/platform/ios/index.js +63 -0
- package/lib/platform/ios/routes/spm.js +437 -0
- package/lib/platform/ios/snippet/PlaceholderConverter.js +55 -0
- package/lib/platform/ios/snippet/XcodeCodec.js +112 -0
- package/lib/{service → platform/ios}/spm/DependencyGraph.js +41 -17
- package/lib/{service → platform/ios}/spm/PackageSwiftParser.js +41 -14
- package/lib/{service → platform/ios}/spm/PolicyEngine.js +9 -4
- package/lib/platform/ios/spm/SpmDiscoverer.js +122 -0
- package/lib/{service → platform/ios}/spm/SpmService.js +385 -127
- package/lib/{service/automation → platform/ios/xcode}/SaveEventFilter.js +8 -7
- package/lib/platform/ios/xcode/XcodeAutomation.js +350 -0
- package/lib/{service/automation → platform/ios/xcode}/XcodeIntegration.js +325 -145
- package/lib/repository/base/BaseRepository.js +7 -9
- package/lib/repository/knowledge/KnowledgeRepository.impl.js +98 -75
- package/lib/repository/token/TokenUsageStore.js +4 -2
- package/lib/service/automation/ActionPipeline.js +1 -1
- package/lib/service/automation/AutomationOrchestrator.js +8 -4
- package/lib/service/automation/ContextCollector.js +7 -5
- package/lib/service/automation/DirectiveDetector.js +23 -16
- package/lib/service/automation/FileWatcher.js +112 -56
- package/lib/service/automation/TriggerResolver.js +6 -4
- package/lib/service/automation/handlers/AlinkHandler.js +24 -12
- package/lib/service/automation/handlers/CreateHandler.js +19 -20
- package/lib/service/automation/handlers/DraftHandler.js +14 -8
- package/lib/service/automation/handlers/GuardHandler.js +93 -63
- package/lib/service/automation/handlers/HeaderHandler.js +1 -6
- package/lib/service/automation/handlers/SearchHandler.js +155 -88
- package/lib/service/bootstrap/BootstrapTaskManager.js +77 -35
- package/lib/service/candidate/SimilarityService.js +25 -9
- package/lib/service/chat/AnalystAgent.js +50 -24
- package/lib/service/chat/CandidateGuardrail.js +143 -17
- package/lib/service/chat/ChatAgent.js +759 -243
- package/lib/service/chat/ContextWindow.js +116 -71
- package/lib/service/chat/ConversationStore.js +77 -36
- package/lib/service/chat/EpisodicConsolidator.js +47 -23
- package/lib/service/chat/HandoffProtocol.js +98 -22
- package/lib/service/chat/Memory.js +34 -14
- package/lib/service/chat/ProducerAgent.js +40 -20
- package/lib/service/chat/ProjectSemanticMemory.js +109 -78
- package/lib/service/chat/ReasoningLayer.js +148 -70
- package/lib/service/chat/ReasoningTrace.js +44 -32
- package/lib/service/chat/TaskPipeline.js +39 -19
- package/lib/service/chat/ToolRegistry.js +48 -29
- package/lib/service/chat/WorkingMemory.js +44 -18
- package/lib/service/chat/tools.js +1096 -494
- package/lib/service/context/RecipeExtractor.js +132 -51
- package/lib/service/cursor/CursorDeliveryPipeline.js +82 -37
- package/lib/service/cursor/KnowledgeCompressor.js +25 -22
- package/lib/service/cursor/RulesGenerator.js +13 -7
- package/lib/service/cursor/SkillsSyncer.js +77 -27
- package/lib/service/cursor/TokenBudget.js +2 -2
- package/lib/service/cursor/TopicClassifier.js +54 -20
- package/lib/service/guard/ComplianceReporter.js +55 -43
- package/lib/service/guard/ExclusionManager.js +67 -29
- package/lib/service/guard/GuardCheckEngine.js +381 -86
- package/lib/service/guard/GuardFeedbackLoop.js +22 -10
- package/lib/service/guard/GuardService.js +29 -19
- package/lib/service/guard/RuleLearner.js +55 -23
- package/lib/service/guard/SourceFileCollector.js +27 -20
- package/lib/service/guard/ViolationsStore.js +43 -38
- package/lib/service/knowledge/CodeEntityGraph.js +147 -82
- package/lib/service/knowledge/ConfidenceRouter.js +12 -10
- package/lib/service/knowledge/KnowledgeFileWriter.js +147 -56
- package/lib/service/knowledge/KnowledgeGraphService.js +81 -34
- package/lib/service/knowledge/KnowledgeService.js +222 -112
- package/lib/service/module/ModuleService.js +969 -0
- package/lib/service/quality/FeedbackCollector.js +27 -15
- package/lib/service/quality/QualityScorer.js +78 -24
- package/lib/service/recipe/RecipeCandidateValidator.js +110 -44
- package/lib/service/recipe/RecipeParser.js +78 -45
- package/lib/service/search/CoarseRanker.js +43 -28
- package/lib/service/search/CrossEncoderReranker.js +32 -21
- package/lib/service/search/InvertedIndex.js +21 -7
- package/lib/service/search/MultiSignalRanker.js +90 -28
- package/lib/service/search/RetrievalFunnel.js +45 -24
- package/lib/service/search/SearchEngine.js +255 -103
- package/lib/service/skills/EventAggregator.js +32 -15
- package/lib/service/skills/SignalCollector.js +140 -64
- package/lib/service/skills/SkillAdvisor.js +79 -42
- package/lib/service/skills/SkillHooks.js +16 -14
- package/lib/service/snippet/PlaceholderConverter.js +5 -0
- package/lib/service/snippet/SnippetFactory.js +116 -99
- package/lib/service/snippet/SnippetInstaller.js +234 -62
- package/lib/service/snippet/codecs/SnippetCodec.js +67 -0
- package/lib/service/snippet/codecs/VSCodeCodec.js +102 -0
- package/lib/service/snippet/codecs/XcodeCodec.js +5 -0
- package/lib/service/wiki/WikiGenerator.js +637 -263
- package/lib/shared/DimensionCopyRegistry.js +472 -0
- package/lib/shared/LanguageService.js +399 -0
- package/lib/shared/PathGuard.js +45 -28
- package/lib/shared/RecipeReadinessChecker.js +72 -12
- package/lib/shared/constants.js +41 -41
- package/lib/shared/errors/BaseError.js +2 -2
- package/lib/shared/errors/index.js +4 -4
- package/lib/shared/similarity.js +25 -8
- package/lib/shared/token-utils.js +6 -2
- package/lib/shared/utils/common.js +12 -4
- package/package.json +49 -13
- package/scripts/bench-real-projects.mjs +256 -0
- package/scripts/build-native-ui.js +30 -30
- package/scripts/clear-old-vector-index.js +5 -35
- package/scripts/clear-vector-cache.js +7 -37
- package/scripts/collect-test-project-stats.mjs +160 -0
- package/scripts/diagnose-mcp.js +41 -32
- package/scripts/ensure-parse-package.js +6 -9
- package/scripts/generate-recipe-drafts.js +116 -77
- package/scripts/init-db.js +3 -20
- package/scripts/init-snippets.js +305 -0
- package/scripts/init-vector-db.js +173 -170
- package/scripts/install-cursor-skill.js +148 -104
- package/scripts/install-full.js +8 -21
- package/scripts/install-vscode-copilot.js +146 -145
- package/scripts/migrate-md-to-knowledge.mjs +139 -151
- package/scripts/postinstall-safe.js +5 -17
- package/scripts/recipe-audit.js +106 -82
- package/scripts/release.js +283 -323
- package/scripts/setup-mcp-config.js +60 -52
- package/scripts/verify-context-api.js +20 -20
- package/skills/autosnippet-analysis/SKILL.md +10 -6
- package/skills/autosnippet-candidates/SKILL.md +27 -26
- package/skills/autosnippet-coldstart/SKILL.md +555 -38
- package/skills/autosnippet-concepts/SKILL.md +349 -337
- package/skills/autosnippet-create/SKILL.md +5 -5
- package/skills/autosnippet-reference-dart/SKILL.md +543 -0
- package/skills/autosnippet-reference-go/SKILL.md +539 -0
- package/skills/autosnippet-reference-java/SKILL.md +534 -0
- package/skills/autosnippet-reference-jsts/SKILL.md +41 -9
- package/skills/autosnippet-reference-kotlin/SKILL.md +526 -0
- package/skills/autosnippet-reference-objc/SKILL.md +29 -6
- package/skills/autosnippet-reference-python/SKILL.md +800 -0
- package/skills/autosnippet-reference-swift/SKILL.md +70 -14
- package/skills/autosnippet-structure/SKILL.md +4 -4
- package/templates/cursor-rules/autosnippet-conventions.mdc +2 -2
- package/templates/recipes-setup/README.md +2 -2
- package/templates/recipes-setup/_template.md +1 -1
- package/dashboard/dist/assets/index-Bun3ld_J.css +0 -1
- package/dashboard/dist/assets/index-_Sk_Dmg3.js +0 -143
- package/resources/asd-entry/main.swift +0 -159
- package/scripts/build-asd-entry.js +0 -51
- package/scripts/init-xcode-snippets.js +0 -311
- package/template.json +0 -39
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
```skill
|
|
2
|
+
---
|
|
3
|
+
name: autosnippet-reference-go
|
|
4
|
+
description: Go 业界最佳实践参考。涵盖模块组织、命名约定、错误处理、接口与组合、并发(goroutine/channel)、Context、struct 设计、测试,为冷启动分析提供高质量参考标准。
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Go 最佳实践参考 (Industry Reference)
|
|
8
|
+
|
|
9
|
+
> 本 Skill 为 **autosnippet-coldstart** 的 Companion Skill。在冷启动分析 Go 项目时,请参考以下业界标准产出高质量候选。
|
|
10
|
+
> **来源**: Effective Go, Go Code Review Comments, Go Proverbs (Rob Pike), Google Go Style Guide, Uber Go Style Guide
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 1. 模块与包结构
|
|
15
|
+
|
|
16
|
+
### 核心规则
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"title": "Go: 包组织与导入规范",
|
|
21
|
+
"content": {
|
|
22
|
+
"markdown": "## Go: 包组织与导入规范\n\n### 标准模式\n```go\n// ✅ 标准项目布局 (community convention)\nmyproject/\n├── cmd/\n│ └── myapp/\n│ └── main.go // 入口,薄层,只做 wiring\n├── internal/ // 不可被外部导入\n│ ├── user/\n│ │ ├── handler.go\n│ │ ├── service.go\n│ │ └── repository.go\n│ └── order/\n├── pkg/ // 可被外部导入的公共库\n│ └── httputil/\n├── api/ // OpenAPI/proto 定义\n├── go.mod\n└── go.sum\n\n// ✅ 包名规范\npackage user // 小写、单数、简短\npackage httputil // 不用下划线或驼峰\npackage main // cmd 入口\n\n// ✅ 导入分三段,用空行分隔\nimport (\n // 标准库\n \"context\"\n \"fmt\"\n \"net/http\"\n\n // 第三方\n \"github.com/gin-gonic/gin\"\n \"go.uber.org/zap\"\n\n // 项目内部\n \"myproject/internal/user\"\n \"myproject/pkg/httputil\"\n)\n\n// ❌ 不要用 package utils/common/helpers — 包名应描述功能\n// ❌ 不要循环导入 — Go 编译器会报错\n```",
|
|
23
|
+
"pattern": "// ✅ 标准项目布局 (community convention)\nmyproject/\n├── cmd/\n│ └── myapp/\n│ └── main.go // 入口,薄层,只做 wiring\n├── internal/ // 不可被外部导入\n│ ├── user/\n│ │ ├── handler.go\n│ │ ├── service.go\n│ │ └── repository.go\n│ └── order/\n├── pkg/ // 可被外部导入的公共库\n│ └── httputil/\n├── api/ // OpenAPI/proto 定义\n├── go.mod\n└── go.sum\n\n// ✅ 包名规范\npackage user // 小写、单数、简短\npackage httputil // 不用下划线或驼峰\npackage main // cmd 入口\n\n// ✅ 导入分三段,用空行分隔\nimport (\n // 标准库\n \"context\"\n \"fmt\"\n \"net/http\"\n\n // 第三方\n \"github.com/gin-gonic/gin\"\n \"go.uber.org/zap\"\n\n // 项目内部\n \"myproject/internal/user\"\n \"myproject/pkg/httputil\"\n)\n\n// ❌ 不要用 package utils/common/helpers — 包名应描述功能\n// ❌ 不要循环导入 — Go 编译器会报错",
|
|
24
|
+
"rationale": "Go 社区推荐 cmd/internal/pkg 布局,internal/ 提供编译器级别的封装保护"
|
|
25
|
+
},
|
|
26
|
+
"description": "Go: 包组织与导入规范",
|
|
27
|
+
"kind": "fact",
|
|
28
|
+
"doClause": "Apply the Go pattern as described",
|
|
29
|
+
"language": "go",
|
|
30
|
+
"headers": [],
|
|
31
|
+
"category": "Tool",
|
|
32
|
+
"knowledgeType": "architecture",
|
|
33
|
+
"usageGuide": "### 使用场景\\n触发 `@trigger` 获取Go: 包组织与导入规范的标准实现模式。",
|
|
34
|
+
"scope": "universal",
|
|
35
|
+
"antiPattern": {
|
|
36
|
+
"bad": "package utils // 或 package common",
|
|
37
|
+
"why": "万能包会膨胀为垃圾抽屉,失去内聚性",
|
|
38
|
+
"fix": "按功能拆包:package httputil, package validate, package auth"
|
|
39
|
+
},
|
|
40
|
+
"reasoning": {
|
|
41
|
+
"whyStandard": "Go Blog: Organizing Go code; golang-standards/project-layout (community)",
|
|
42
|
+
"sources": [
|
|
43
|
+
"Effective Go - Package names",
|
|
44
|
+
"Go Blog - Organizing Go code"
|
|
45
|
+
],
|
|
46
|
+
"confidence": 0.9
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 包设计原则
|
|
52
|
+
|
|
53
|
+
| 原则 | 说明 | 示例 |
|
|
54
|
+
|------|------|------|
|
|
55
|
+
| 按功能分包 | 一个包解决一个领域问题 | `user/`, `order/`, `auth/` |
|
|
56
|
+
| 包名 = 目录名 | 保持一致 | `internal/user` → `package user` |
|
|
57
|
+
| 避免 `internal` 导出 | `internal/` 下的包仅项目内可用 | 编译器强制 |
|
|
58
|
+
| 尽量少的公开 API | 未导出符号是默认 | 小写开头 = 私有 |
|
|
59
|
+
| 包文档 `doc.go` | 大包应有 `doc.go` 文件 | `// Package user provides ...` |
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## 2. 命名约定
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"title": "Go: Effective Go 命名规范",
|
|
68
|
+
"content": {
|
|
69
|
+
"markdown": "## Go: Effective Go 命名规范\n\n### 标准模式\n```go\n// ✅ 导出名: PascalCase (UpperCamelCase)\ntype UserService struct { ... }\nfunc NewUserService(repo UserRepository) *UserService { ... }\nvar ErrNotFound = errors.New(\"not found\")\n\n// ✅ 非导出名: camelCase (lowerCamelCase)\ntype userCache struct { ... }\nfunc (s *UserService) validateInput(req *Request) error { ... }\nvar defaultTimeout = 30 * time.Second\n\n// ✅ 缩写保持全大写或全小写\nvar userID int // ✅ (非导出)\nvar UserID int // ✅ (导出)\nfunc ServeHTTP(...) // ✅ HTTP 全大写\nfunc parseURL(...) // ✅ URL 全大写\nvar xmlParser = ... // ✅ XML 全小写\n\n// ✅ 接口命名\ntype Reader interface { Read(p []byte) (n int, err error) }\ntype Stringer interface { String() string }\ntype UserRepository interface { ... } // 多方法接口按功能命名\n\n// ✅ Getter 不加 Get 前缀\nfunc (u *User) Name() string { return u.name } // ✅\nfunc (u *User) SetName(name string) { u.name = name } // ✅ Setter 加 Set\n\n// ✅ 构造函数: New + 类型名\nfunc NewServer(addr string) *Server { ... }\nfunc NewUserService(repo UserRepository) *UserService { ... }\n\n// ❌ 反模式\nfunc (u *User) GetName() string { ... } // Go 不用 Get 前缀\nvar userId int // 应为 userID\ntype IUserService interface { ... } // 不用 I 前缀\n```",
|
|
70
|
+
"pattern": "// ✅ 导出名: PascalCase (UpperCamelCase)\ntype UserService struct { ... }\nfunc NewUserService(repo UserRepository) *UserService { ... }\nvar ErrNotFound = errors.New(\"not found\")\n\n// ✅ 非导出名: camelCase (lowerCamelCase)\ntype userCache struct { ... }\nfunc (s *UserService) validateInput(req *Request) error { ... }\nvar defaultTimeout = 30 * time.Second\n\n// ✅ 缩写保持全大写或全小写\nvar userID int // ✅ (非导出)\nvar UserID int // ✅ (导出)\nfunc ServeHTTP(...) // ✅ HTTP 全大写\nfunc parseURL(...) // ✅ URL 全大写\nvar xmlParser = ... // ✅ XML 全小写\n\n// ✅ 接口命名\ntype Reader interface { Read(p []byte) (n int, err error) }\ntype Stringer interface { String() string }\ntype UserRepository interface { ... } // 多方法接口按功能命名\n\n// ✅ Getter 不加 Get 前缀\nfunc (u *User) Name() string { return u.name } // ✅\nfunc (u *User) SetName(name string) { u.name = name } // ✅ Setter 加 Set\n\n// ✅ 构造函数: New + 类型名\nfunc NewServer(addr string) *Server { ... }\nfunc NewUserService(repo UserRepository) *UserService { ... }\n\n// ❌ 反模式\nfunc (u *User) GetName() string { ... } // Go 不用 Get 前缀\nvar userId int // 应为 userID\ntype IUserService interface { ... } // 不用 I 前缀",
|
|
71
|
+
"rationale": "Go 的导出机制通过大小写控制可见性,命名约定直接影响 API 设计"
|
|
72
|
+
},
|
|
73
|
+
"description": "Go: Effective Go 命名规范",
|
|
74
|
+
"kind": "rule",
|
|
75
|
+
"doClause": "Apply the Go pattern as described",
|
|
76
|
+
"language": "go",
|
|
77
|
+
"headers": [],
|
|
78
|
+
"knowledgeType": "code-standard",
|
|
79
|
+
"usageGuide": "### 使用场景\\n触发 `@trigger` 获取Go: Effective Go 命名规范的标准实现模式。",
|
|
80
|
+
"antiPattern": {
|
|
81
|
+
"bad": "func (u *User) GetName() string",
|
|
82
|
+
"why": "Go 不用 Get 前缀 (Effective Go),直接用属性名作方法名",
|
|
83
|
+
"fix": "func (u *User) Name() string"
|
|
84
|
+
},
|
|
85
|
+
"reasoning": {
|
|
86
|
+
"whyStandard": "Effective Go - Names; Go Code Review Comments - Initialisms",
|
|
87
|
+
"sources": [
|
|
88
|
+
"Effective Go",
|
|
89
|
+
"Go Code Review Comments"
|
|
90
|
+
],
|
|
91
|
+
"confidence": 0.95
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 命名速查表
|
|
97
|
+
|
|
98
|
+
| 标识符类型 | 风格 | 示例 |
|
|
99
|
+
|-----------|------|------|
|
|
100
|
+
| 导出类型 | `PascalCase` | `UserService`, `HTTPClient` |
|
|
101
|
+
| 非导出类型 | `camelCase` | `userCache`, `httpClient` |
|
|
102
|
+
| 接口 (单方法) | 方法名 + `er` | `Reader`, `Writer`, `Stringer` |
|
|
103
|
+
| 接口 (多方法) | 功能名 | `UserRepository`, `EventBus` |
|
|
104
|
+
| 构造函数 | `New` + 类型名 | `NewServer()`, `NewRouter()` |
|
|
105
|
+
| 错误变量 | `Err` + 描述 | `ErrNotFound`, `ErrTimeout` |
|
|
106
|
+
| 错误类型 | 描述 + `Error` | `NotFoundError`, `ValidationError` |
|
|
107
|
+
| 缩写 | 全大写或全小写 | `ID`, `URL`, `HTTP`, `userID` |
|
|
108
|
+
| 包名 | 全小写单词 | `user`, `httputil` |
|
|
109
|
+
| 测试文件 | `_test.go` 后缀 | `user_test.go` |
|
|
110
|
+
|
|
111
|
+
### 命名反模式
|
|
112
|
+
|
|
113
|
+
| 反模式 | 问题 | 修正 |
|
|
114
|
+
|--------|------|------|
|
|
115
|
+
| `GetName()` | Go 不用 Get 前缀 | `Name()` |
|
|
116
|
+
| `IUserService` | I 前缀不符合 Go 风格 | `UserService` |
|
|
117
|
+
| `userId` | 缩写应全大写 | `userID` |
|
|
118
|
+
| `package utils` | 无语义包名 | `package validate` / `package httputil` |
|
|
119
|
+
| `type User_Info` | 下划线命名 | `type UserInfo` |
|
|
120
|
+
| `const MAX_SIZE` | SCREAMING_SNAKE 非 Go 风格 | `const MaxSize` (导出) 或 `maxSize` |
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## 3. 错误处理
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"title": "Go: 错误处理最佳实践",
|
|
129
|
+
"content": {
|
|
130
|
+
"markdown": "## Go: 错误处理最佳实践\n\n### 标准模式\n```go\n// ✅ 自定义错误类型\ntype NotFoundError struct {\n Resource string\n ID any\n}\n\nfunc (e *NotFoundError) Error() string {\n return fmt.Sprintf(\"%s not found: %v\", e.Resource, e.ID)\n}\n\n// ✅ Sentinel 错误 (包级别)\nvar (\n ErrNotFound = errors.New(\"not found\")\n ErrForbidden = errors.New(\"forbidden\")\n ErrConflict = errors.New(\"conflict\")\n)\n\n// ✅ 错误包装 — 添加上下文但保留原始错误\nfunc (s *UserService) FindByID(ctx context.Context, id int64) (*User, error) {\n user, err := s.repo.Get(ctx, id)\n if err != nil {\n return nil, fmt.Errorf(\"UserService.FindByID(%d): %w\", id, err)\n }\n return user, nil\n}\n\n// ✅ errors.Is / errors.As 类型检查\nif errors.Is(err, ErrNotFound) {\n // handle not found\n}\n\nvar nfErr *NotFoundError\nif errors.As(err, &nfErr) {\n log.Printf(\"resource %s not found\", nfErr.Resource)\n}\n\n// ✅ 延迟错误处理 (defer + named return)\nfunc (s *Store) Transaction(fn func(tx *Tx) error) (err error) {\n tx, err := s.db.Begin()\n if err != nil {\n return fmt.Errorf(\"begin tx: %w\", err)\n }\n defer func() {\n if err != nil {\n tx.Rollback()\n } else {\n err = tx.Commit()\n }\n }()\n return fn(tx)\n}\n\n// ❌ 反模式\n_ = doSomething() // 忽略错误\nif err != nil { panic(err) } // 不要在库代码中 panic\nreturn err // 不加上下文直接返回\n```",
|
|
131
|
+
"pattern": "// ✅ 自定义错误类型\ntype NotFoundError struct {\n Resource string\n ID any\n}\n\nfunc (e *NotFoundError) Error() string {\n return fmt.Sprintf(\"%s not found: %v\", e.Resource, e.ID)\n}\n\n// ✅ Sentinel 错误 (包级别)\nvar (\n ErrNotFound = errors.New(\"not found\")\n ErrForbidden = errors.New(\"forbidden\")\n ErrConflict = errors.New(\"conflict\")\n)\n\n// ✅ 错误包装 — 添加上下文但保留原始错误\nfunc (s *UserService) FindByID(ctx context.Context, id int64) (*User, error) {\n user, err := s.repo.Get(ctx, id)\n if err != nil {\n return nil, fmt.Errorf(\"UserService.FindByID(%d): %w\", id, err)\n }\n return user, nil\n}\n\n// ✅ errors.Is / errors.As 类型检查\nif errors.Is(err, ErrNotFound) {\n // handle not found\n}\n\nvar nfErr *NotFoundError\nif errors.As(err, &nfErr) {\n log.Printf(\"resource %s not found\", nfErr.Resource)\n}\n\n// ✅ 延迟错误处理 (defer + named return)\nfunc (s *Store) Transaction(fn func(tx *Tx) error) (err error) {\n tx, err := s.db.Begin()\n if err != nil {\n return fmt.Errorf(\"begin tx: %w\", err)\n }\n defer func() {\n if err != nil {\n tx.Rollback()\n } else {\n err = tx.Commit()\n }\n }()\n return fn(tx)\n}\n\n// ❌ 反模式\n_ = doSomething() // 忽略错误\nif err != nil { panic(err) } // 不要在库代码中 panic\nreturn err // 不加上下文直接返回",
|
|
132
|
+
"rationale": "Go 的显式错误处理是核心哲学: 'Errors are values' (Rob Pike)"
|
|
133
|
+
},
|
|
134
|
+
"description": "Go: 错误处理最佳实践",
|
|
135
|
+
"kind": "pattern",
|
|
136
|
+
"doClause": "Apply the Go pattern as described",
|
|
137
|
+
"language": "go",
|
|
138
|
+
"headers": [],
|
|
139
|
+
"knowledgeType": "best-practice",
|
|
140
|
+
"usageGuide": "### 使用场景\\n触发 `@trigger` 获取Go: 错误处理最佳实践的标准实现模式。",
|
|
141
|
+
"antiPattern": {
|
|
142
|
+
"bad": "_ = f.Close() // 或 if err != nil { return err } 无上下文",
|
|
143
|
+
"why": "忽略错误可能导致资源泄漏;无上下文的错误难以定位",
|
|
144
|
+
"fix": "if err := f.Close(); err != nil { return fmt.Errorf(\"close file: %w\", err) }"
|
|
145
|
+
},
|
|
146
|
+
"reasoning": {
|
|
147
|
+
"whyStandard": "Go Proverbs: 'Errors are values'; Go Blog: 'Error handling and Go'",
|
|
148
|
+
"sources": [
|
|
149
|
+
"Go Blog - Error handling and Go",
|
|
150
|
+
"Go Proverbs",
|
|
151
|
+
"Uber Go Style Guide"
|
|
152
|
+
],
|
|
153
|
+
"confidence": 0.95
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### 错误处理反模式
|
|
159
|
+
|
|
160
|
+
| 反模式 | 问题 | 修正 |
|
|
161
|
+
|--------|------|------|
|
|
162
|
+
| `_ = f.Close()` | 忽略 Close 错误 | `if err := f.Close(); err != nil { ... }` |
|
|
163
|
+
| `panic(err)` in library | 库不应 crash 调用方 | `return fmt.Errorf("...: %w", err)` |
|
|
164
|
+
| `return err` 无上下文 | 无法定位错误来源 | `return fmt.Errorf("operation: %w", err)` |
|
|
165
|
+
| `if err != nil { return err }` ×20 | 大量重复 | 可用 helper/表驱动/defer 减少 |
|
|
166
|
+
| `log.Fatal(err)` in library | 调用 `os.Exit(1)` | 返回 error,让 main 决定 |
|
|
167
|
+
| `err.Error() == "not found"` | 字符串比较脆弱 | `errors.Is(err, ErrNotFound)` |
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 4. 接口与组合
|
|
172
|
+
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"title": "Go: 接口设计 — 小接口 + 组合",
|
|
176
|
+
"content": {
|
|
177
|
+
"markdown": "## Go: 接口设计 — 小接口 + 组合\n\n### 标准模式\n```go\n// ✅ 小接口 — 1-2 个方法足矣\ntype Reader interface {\n Read(p []byte) (n int, err error)\n}\n\ntype Writer interface {\n Write(p []byte) (n int, err error)\n}\n\n// ✅ 组合接口\ntype ReadWriter interface {\n Reader\n Writer\n}\n\n// ✅ 在消费者侧定义接口 (Go 惯例)\n// 不要在实现侧定义!\npackage userhttp\n\n// UserFinder 仅声明本 handler 需要的方法\ntype UserFinder interface {\n FindByID(ctx context.Context, id int64) (*user.User, error)\n}\n\ntype Handler struct {\n users UserFinder // 依赖接口而非具体类型\n}\n\n// ✅ 接口自动满足 (structural typing)\n// UserService 无需显式声明 implements\ntype UserService struct { repo *UserRepo }\nfunc (s *UserService) FindByID(ctx context.Context, id int64) (*user.User, error) { ... }\n// → 自动满足 UserFinder 接口\n\n// ✅ 接口合规性校验 (编译期)\nvar _ UserFinder = (*UserService)(nil)\n\n// ✅ 函数类型适配器 (标准库模式)\ntype HandlerFunc func(http.ResponseWriter, *http.Request)\nfunc (f HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) { f(w, r) }\n\n// ❌ 反模式\ntype UserServiceInterface interface { // 不要在实现侧定义大接口\n FindByID(...) ...\n FindAll(...) ...\n Create(...) ...\n Update(...) ...\n Delete(...) ...\n}\n```",
|
|
178
|
+
"pattern": "// ✅ 小接口 — 1-2 个方法足矣\ntype Reader interface {\n Read(p []byte) (n int, err error)\n}\n\ntype Writer interface {\n Write(p []byte) (n int, err error)\n}\n\n// ✅ 组合接口\ntype ReadWriter interface {\n Reader\n Writer\n}\n\n// ✅ 在消费者侧定义接口 (Go 惯例)\n// 不要在实现侧定义!\npackage userhttp\n\n// UserFinder 仅声明本 handler 需要的方法\ntype UserFinder interface {\n FindByID(ctx context.Context, id int64) (*user.User, error)\n}\n\ntype Handler struct {\n users UserFinder // 依赖接口而非具体类型\n}\n\n// ✅ 接口自动满足 (structural typing)\n// UserService 无需显式声明 implements\ntype UserService struct { repo *UserRepo }\nfunc (s *UserService) FindByID(ctx context.Context, id int64) (*user.User, error) { ... }\n// → 自动满足 UserFinder 接口\n\n// ✅ 接口合规性校验 (编译期)\nvar _ UserFinder = (*UserService)(nil)\n\n// ✅ 函数类型适配器 (标准库模式)\ntype HandlerFunc func(http.ResponseWriter, *http.Request)\nfunc (f HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) { f(w, r) }\n\n// ❌ 反模式\ntype UserServiceInterface interface { // 不要在实现侧定义大接口\n FindByID(...) ...\n FindAll(...) ...\n Create(...) ...\n Update(...) ...\n Delete(...) ...\n}",
|
|
179
|
+
"rationale": "Go Proverb: 'The bigger the interface, the weaker the abstraction'"
|
|
180
|
+
},
|
|
181
|
+
"description": "Go: 接口设计 — 小接口 + 组合",
|
|
182
|
+
"kind": "pattern",
|
|
183
|
+
"doClause": "Apply the Go pattern as described",
|
|
184
|
+
"language": "go",
|
|
185
|
+
"headers": [],
|
|
186
|
+
"knowledgeType": "code-pattern",
|
|
187
|
+
"usageGuide": "### 使用场景\\n触发 `@trigger` 获取Go: 接口设计 — 小接口 + 组合的标准实现模式。",
|
|
188
|
+
"antiPattern": {
|
|
189
|
+
"bad": "type UserServiceInterface interface { FindByID; FindAll; Create; Update; Delete }",
|
|
190
|
+
"why": "大接口难以 mock、替换、组合;违背接口隔离原则",
|
|
191
|
+
"fix": "在消费者侧定义只包含实际需要方法的小接口"
|
|
192
|
+
},
|
|
193
|
+
"reasoning": {
|
|
194
|
+
"whyStandard": "Go Proverbs; Effective Go - Interfaces; Go Code Review Comments - Interfaces",
|
|
195
|
+
"sources": [
|
|
196
|
+
"Go Proverbs",
|
|
197
|
+
"Effective Go",
|
|
198
|
+
"Go Code Review Comments"
|
|
199
|
+
],
|
|
200
|
+
"confidence": 0.95
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### 接口设计原则
|
|
206
|
+
|
|
207
|
+
| 原则 | 说明 |
|
|
208
|
+
|------|------|
|
|
209
|
+
| 在消费者侧定义 | 不要在实现包中定义接口,在使用接口的包中定义 |
|
|
210
|
+
| 尽量小 | 1-2 方法的接口最有价值(io.Reader/Writer) |
|
|
211
|
+
| 组合优于继承 | 通过嵌入小接口组成大接口 |
|
|
212
|
+
| 隐式满足 | 不要 `implements` 关键字,自动匹配 |
|
|
213
|
+
| 编译期校验 | `var _ Interface = (*Impl)(nil)` 确认实现 |
|
|
214
|
+
| 返回具体类型 | 函数返回具体类型,接受接口参数 |
|
|
215
|
+
| `Accept interfaces, return structs` | 大多数情况下的最佳实践 |
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## 5. 并发 (Goroutine / Channel / sync)
|
|
220
|
+
|
|
221
|
+
```json
|
|
222
|
+
{
|
|
223
|
+
"title": "Go: 并发最佳实践",
|
|
224
|
+
"content": {
|
|
225
|
+
"markdown": "## Go: 并发最佳实践\n\n### 标准模式\n```go\n// ✅ 使用 errgroup 管理 goroutine 组\nimport \"golang.org/x/sync/errgroup\"\n\nfunc fetchAll(ctx context.Context, urls []string) ([]Response, error) {\n g, ctx := errgroup.WithContext(ctx)\n results := make([]Response, len(urls))\n\n for i, url := range urls {\n g.Go(func() error {\n resp, err := fetch(ctx, url)\n if err != nil {\n return err\n }\n results[i] = resp // 每个 goroutine 写独立索引,无竞争\n return nil\n })\n }\n\n if err := g.Wait(); err != nil {\n return nil, err\n }\n return results, nil\n}\n\n// ✅ Channel 作为通信机制\nfunc produce(ctx context.Context) <-chan Item {\n ch := make(chan Item)\n go func() {\n defer close(ch)\n for {\n item, err := nextItem()\n if err != nil {\n return\n }\n select {\n case ch <- item:\n case <-ctx.Done():\n return\n }\n }\n }()\n return ch\n}\n\n// ✅ sync.Once 安全初始化\nvar (\n instance *DB\n once sync.Once\n)\n\nfunc GetDB() *DB {\n once.Do(func() {\n instance = connectDB()\n })\n return instance\n}\n\n// ✅ sync.Mutex 保护共享状态\ntype SafeCounter struct {\n mu sync.Mutex\n v map[string]int\n}\n\nfunc (c *SafeCounter) Inc(key string) {\n c.mu.Lock()\n defer c.mu.Unlock()\n c.v[key]++\n}\n\n// ✅ 使用 context 控制 goroutine 生命周期\nfunc worker(ctx context.Context) {\n for {\n select {\n case <-ctx.Done():\n return // 优雅退出\n default:\n doWork()\n }\n }\n}\n\n// ❌ 反模式\ngo func() { ... }() // 裸 goroutine — 无法等待、无法取消、panic 不受控\ntime.Sleep(time.Second) // 用 sleep 做同步 → 用 channel/WaitGroup\n```",
|
|
226
|
+
"pattern": "// ✅ 使用 errgroup 管理 goroutine 组\nimport \"golang.org/x/sync/errgroup\"\n\nfunc fetchAll(ctx context.Context, urls []string) ([]Response, error) {\n g, ctx := errgroup.WithContext(ctx)\n results := make([]Response, len(urls))\n\n for i, url := range urls {\n g.Go(func() error {\n resp, err := fetch(ctx, url)\n if err != nil {\n return err\n }\n results[i] = resp // 每个 goroutine 写独立索引,无竞争\n return nil\n })\n }\n\n if err := g.Wait(); err != nil {\n return nil, err\n }\n return results, nil\n}\n\n// ✅ Channel 作为通信机制\nfunc produce(ctx context.Context) <-chan Item {\n ch := make(chan Item)\n go func() {\n defer close(ch)\n for {\n item, err := nextItem()\n if err != nil {\n return\n }\n select {\n case ch <- item:\n case <-ctx.Done():\n return\n }\n }\n }()\n return ch\n}\n\n// ✅ sync.Once 安全初始化\nvar (\n instance *DB\n once sync.Once\n)\n\nfunc GetDB() *DB {\n once.Do(func() {\n instance = connectDB()\n })\n return instance\n}\n\n// ✅ sync.Mutex 保护共享状态\ntype SafeCounter struct {\n mu sync.Mutex\n v map[string]int\n}\n\nfunc (c *SafeCounter) Inc(key string) {\n c.mu.Lock()\n defer c.mu.Unlock()\n c.v[key]++\n}\n\n// ✅ 使用 context 控制 goroutine 生命周期\nfunc worker(ctx context.Context) {\n for {\n select {\n case <-ctx.Done():\n return // 优雅退出\n default:\n doWork()\n }\n }\n}\n\n// ❌ 反模式\ngo func() { ... }() // 裸 goroutine — 无法等待、无法取消、panic 不受控\ntime.Sleep(time.Second) // 用 sleep 做同步 → 用 channel/WaitGroup",
|
|
227
|
+
"rationale": "Go Proverb: 'Don't communicate by sharing memory, share memory by communicating'"
|
|
228
|
+
},
|
|
229
|
+
"description": "Go: 并发最佳实践",
|
|
230
|
+
"kind": "pattern",
|
|
231
|
+
"doClause": "Apply the Go pattern as described",
|
|
232
|
+
"language": "go",
|
|
233
|
+
"headers": [],
|
|
234
|
+
"knowledgeType": "best-practice",
|
|
235
|
+
"usageGuide": "### 使用场景\\n触发 `@trigger` 获取Go: 并发最佳实践的标准实现模式。",
|
|
236
|
+
"antiPattern": {
|
|
237
|
+
"bad": "go func() { result = compute() }(); time.Sleep(time.Second)",
|
|
238
|
+
"why": "裸 goroutine 不可控: 无等待、无取消、panic 崩全进程; Sleep 同步不可靠",
|
|
239
|
+
"fix": "使用 errgroup / WaitGroup + context 取消 + recover"
|
|
240
|
+
},
|
|
241
|
+
"reasoning": {
|
|
242
|
+
"whyStandard": "Effective Go - Concurrency; Go Proverbs; Go Blog - Pipelines",
|
|
243
|
+
"sources": [
|
|
244
|
+
"Effective Go",
|
|
245
|
+
"Go Proverbs",
|
|
246
|
+
"Go Blog - Pipelines and cancellation"
|
|
247
|
+
],
|
|
248
|
+
"confidence": 0.95
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### 并发模式速查
|
|
254
|
+
|
|
255
|
+
| 模式 | 工具 | 适用场景 |
|
|
256
|
+
|------|------|---------|
|
|
257
|
+
| Fan-out/Fan-in | `errgroup` / channel | 并行调 N 个服务,合并结果 |
|
|
258
|
+
| Worker Pool | buffered channel + N goroutines | 限制并发数 |
|
|
259
|
+
| Pipeline | channel chain | 数据流处理 |
|
|
260
|
+
| Pub/Sub | channel | 事件广播 |
|
|
261
|
+
| Singleton | `sync.Once` | 延迟初始化单例 |
|
|
262
|
+
| 互斥访问 | `sync.Mutex` / `sync.RWMutex` | 保护共享 map/slice |
|
|
263
|
+
| 原子操作 | `sync/atomic` | 计数器/标志位 |
|
|
264
|
+
| 超时控制 | `context.WithTimeout` | API 调用/DB 查询 |
|
|
265
|
+
|
|
266
|
+
### 并发反模式
|
|
267
|
+
|
|
268
|
+
| 反模式 | 问题 | 修正 |
|
|
269
|
+
|--------|------|------|
|
|
270
|
+
| 裸 `go func()` | panic 不受控,无取消 | `errgroup` / 带 `recover` 的 wrapper |
|
|
271
|
+
| `time.Sleep` 同步 | 不可靠,浪费时间 | WaitGroup / channel / errgroup.Wait |
|
|
272
|
+
| goroutine 泄漏 | 无退出信号 | context 取消 / done channel |
|
|
273
|
+
| 锁粒度过大 | `sync.Mutex` 锁整个方法 | 缩小临界区 / 用 channel 替代 |
|
|
274
|
+
| 向 nil channel 发送 | 永久阻塞 | 初始化 channel |
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## 6. Context
|
|
279
|
+
|
|
280
|
+
```json
|
|
281
|
+
{
|
|
282
|
+
"title": "Go: context.Context 使用规范",
|
|
283
|
+
"content": {
|
|
284
|
+
"markdown": "## Go: context.Context 使用规范\n\n### 标准模式\n```go\n// ✅ Context 作为第一个参数\nfunc (s *UserService) FindByID(ctx context.Context, id int64) (*User, error) {\n // 传播到所有下游调用\n user, err := s.repo.Get(ctx, id)\n if err != nil {\n return nil, fmt.Errorf(\"find user %d: %w\", id, err)\n }\n return user, nil\n}\n\n// ✅ HTTP handler 中获取 context\nfunc (h *Handler) GetUser(w http.ResponseWriter, r *http.Request) {\n ctx := r.Context()\n user, err := h.service.FindByID(ctx, userID)\n // ...\n}\n\n// ✅ 超时控制\nctx, cancel := context.WithTimeout(ctx, 5*time.Second)\ndefer cancel() // 必须调用 cancel 释放资源\n\nresult, err := slowOperation(ctx)\nif errors.Is(err, context.DeadlineExceeded) {\n // 超时处理\n}\n\n// ✅ Context Values — 仅限请求范围元数据\ntype ctxKey string\nconst requestIDKey ctxKey = \"requestID\"\n\nfunc WithRequestID(ctx context.Context, id string) context.Context {\n return context.WithValue(ctx, requestIDKey, id)\n}\n\nfunc RequestID(ctx context.Context) string {\n id, _ := ctx.Value(requestIDKey).(string)\n return id\n}\n\n// ❌ 反模式\nfunc DoWork(id int, ctx context.Context) { } // ctx 不是第一个参数\nctx = context.WithValue(ctx, \"key\", val) // 字符串 key 可能冲突\nvar ctx context.Context // 不要存储在 struct 中\n```",
|
|
285
|
+
"pattern": "// ✅ Context 作为第一个参数\nfunc (s *UserService) FindByID(ctx context.Context, id int64) (*User, error) {\n // 传播到所有下游调用\n user, err := s.repo.Get(ctx, id)\n if err != nil {\n return nil, fmt.Errorf(\"find user %d: %w\", id, err)\n }\n return user, nil\n}\n\n// ✅ HTTP handler 中获取 context\nfunc (h *Handler) GetUser(w http.ResponseWriter, r *http.Request) {\n ctx := r.Context()\n user, err := h.service.FindByID(ctx, userID)\n // ...\n}\n\n// ✅ 超时控制\nctx, cancel := context.WithTimeout(ctx, 5*time.Second)\ndefer cancel() // 必须调用 cancel 释放资源\n\nresult, err := slowOperation(ctx)\nif errors.Is(err, context.DeadlineExceeded) {\n // 超时处理\n}\n\n// ✅ Context Values — 仅限请求范围元数据\ntype ctxKey string\nconst requestIDKey ctxKey = \"requestID\"\n\nfunc WithRequestID(ctx context.Context, id string) context.Context {\n return context.WithValue(ctx, requestIDKey, id)\n}\n\nfunc RequestID(ctx context.Context) string {\n id, _ := ctx.Value(requestIDKey).(string)\n return id\n}\n\n// ❌ 反模式\nfunc DoWork(id int, ctx context.Context) { } // ctx 不是第一个参数\nctx = context.WithValue(ctx, \"key\", val) // 字符串 key 可能冲突\nvar ctx context.Context // 不要存储在 struct 中",
|
|
286
|
+
"rationale": "context.Context 是 Go 的请求生命周期管理核心,贯穿整个调用链"
|
|
287
|
+
},
|
|
288
|
+
"description": "Go: context.Context 使用规范",
|
|
289
|
+
"kind": "rule",
|
|
290
|
+
"doClause": "Apply the Go pattern as described",
|
|
291
|
+
"language": "go",
|
|
292
|
+
"headers": [],
|
|
293
|
+
"knowledgeType": "code-standard",
|
|
294
|
+
"usageGuide": "### 使用场景\\n触发 `@trigger` 获取Go: context.Context 使用规范的标准实现模式。",
|
|
295
|
+
"antiPattern": {
|
|
296
|
+
"bad": "func DoWork(name string, ctx context.Context, id int)",
|
|
297
|
+
"why": "Go 约定 ctx 始终为第一个参数; 存储在 struct 中会导致跨请求复用",
|
|
298
|
+
"fix": "func DoWork(ctx context.Context, name string, id int)"
|
|
299
|
+
},
|
|
300
|
+
"reasoning": {
|
|
301
|
+
"whyStandard": "Go Blog - Context; Go Code Review Comments - Contexts",
|
|
302
|
+
"sources": [
|
|
303
|
+
"Go Blog - Context",
|
|
304
|
+
"Go Code Review Comments"
|
|
305
|
+
],
|
|
306
|
+
"confidence": 0.95
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Context 使用原则
|
|
312
|
+
|
|
313
|
+
| 原则 | 说明 |
|
|
314
|
+
|------|------|
|
|
315
|
+
| 第一个参数 | `func Foo(ctx context.Context, ...)` |
|
|
316
|
+
| 不要存在 struct 中 | Context 是请求级的,不应跨请求复用 |
|
|
317
|
+
| 向下传播 | 每一层都传递 ctx 到下游 |
|
|
318
|
+
| Always cancel | `defer cancel()` 紧跟 WithTimeout/WithCancel |
|
|
319
|
+
| Value 用自定义 key 类型 | 避免字符串 key 冲突 |
|
|
320
|
+
| 参数名用 `ctx` | 不要用 `c` 或 `context` |
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## 7. Struct 与方法设计
|
|
325
|
+
|
|
326
|
+
```json
|
|
327
|
+
{
|
|
328
|
+
"title": "Go: Struct 设计与方法接收者",
|
|
329
|
+
"content": {
|
|
330
|
+
"markdown": "## Go: Struct 设计与方法接收者\n\n### 标准模式\n```go\n// ✅ 构造函数模式\ntype Server struct {\n addr string\n port int\n logger *zap.Logger\n handler http.Handler\n timeout time.Duration\n}\n\n// ✅ Functional Options 模式(参数 >3 个时推荐)\ntype Option func(*Server)\n\nfunc WithPort(port int) Option {\n return func(s *Server) { s.port = port }\n}\n\nfunc WithTimeout(d time.Duration) Option {\n return func(s *Server) { s.timeout = d }\n}\n\nfunc NewServer(addr string, opts ...Option) *Server {\n s := &Server{\n addr: addr,\n port: 8080, // 默认值\n timeout: 30 * time.Second, // 默认值\n logger: zap.NewNop(),\n }\n for _, opt := range opts {\n opt(s)\n }\n return s\n}\n\n// 使用: server := NewServer(\"0.0.0.0\", WithPort(9090), WithTimeout(10*time.Second))\n\n// ✅ 指针接收者 vs 值接收者\n// 指针接收者: 需要修改字段、大 struct、实现含指针接收者的接口\nfunc (s *Server) Start() error {\n s.running = true // 修改状态 → 指针接收者\n return http.ListenAndServe(fmt.Sprintf(\"%s:%d\", s.addr, s.port), s.handler)\n}\n\n// 值接收者: 小的、不可变的类型\ntype Point struct { X, Y float64 }\nfunc (p Point) Distance(q Point) float64 {\n return math.Sqrt((p.X-q.X)*(p.X-q.X) + (p.Y-q.Y)*(p.Y-q.Y))\n}\n\n// ✅ Struct 嵌入(组合代替继承)\ntype Engine struct {\n RouterGroup // 嵌入 RouterGroup 的方法\n pool sync.Pool\n}\n\n// ❌ 反模式\ntype Config struct {\n A, B, C, D, E, F, G string // 参数爆炸\n}\nfunc NewConfig(a, b, c, d, e, f, g string) *Config { ... } // 参数不可读\n```",
|
|
331
|
+
"pattern": "// ✅ 构造函数模式\ntype Server struct {\n addr string\n port int\n logger *zap.Logger\n handler http.Handler\n timeout time.Duration\n}\n\n// ✅ Functional Options 模式(参数 >3 个时推荐)\ntype Option func(*Server)\n\nfunc WithPort(port int) Option {\n return func(s *Server) { s.port = port }\n}\n\nfunc WithTimeout(d time.Duration) Option {\n return func(s *Server) { s.timeout = d }\n}\n\nfunc NewServer(addr string, opts ...Option) *Server {\n s := &Server{\n addr: addr,\n port: 8080, // 默认值\n timeout: 30 * time.Second, // 默认值\n logger: zap.NewNop(),\n }\n for _, opt := range opts {\n opt(s)\n }\n return s\n}\n\n// 使用: server := NewServer(\"0.0.0.0\", WithPort(9090), WithTimeout(10*time.Second))\n\n// ✅ 指针接收者 vs 值接收者\n// 指针接收者: 需要修改字段、大 struct、实现含指针接收者的接口\nfunc (s *Server) Start() error {\n s.running = true // 修改状态 → 指针接收者\n return http.ListenAndServe(fmt.Sprintf(\"%s:%d\", s.addr, s.port), s.handler)\n}\n\n// 值接收者: 小的、不可变的类型\ntype Point struct { X, Y float64 }\nfunc (p Point) Distance(q Point) float64 {\n return math.Sqrt((p.X-q.X)*(p.X-q.X) + (p.Y-q.Y)*(p.Y-q.Y))\n}\n\n// ✅ Struct 嵌入(组合代替继承)\ntype Engine struct {\n RouterGroup // 嵌入 RouterGroup 的方法\n pool sync.Pool\n}\n\n// ❌ 反模式\ntype Config struct {\n A, B, C, D, E, F, G string // 参数爆炸\n}\nfunc NewConfig(a, b, c, d, e, f, g string) *Config { ... } // 参数不可读",
|
|
332
|
+
"rationale": "Functional Options 是 Go 社区公认的可扩展构造模式 (Dave Cheney)"
|
|
333
|
+
},
|
|
334
|
+
"description": "Go: Struct 设计与方法接收者",
|
|
335
|
+
"kind": "pattern",
|
|
336
|
+
"doClause": "Apply the Go pattern as described",
|
|
337
|
+
"language": "go",
|
|
338
|
+
"headers": [],
|
|
339
|
+
"knowledgeType": "code-pattern",
|
|
340
|
+
"usageGuide": "### 使用场景\\n触发 `@trigger` 获取Go: Struct 设计与方法接收者的标准实现模式。",
|
|
341
|
+
"antiPattern": {
|
|
342
|
+
"bad": "func NewServer(addr string, port int, timeout int, logger Logger, tls bool)",
|
|
343
|
+
"why": "参数列表过长,调用方需记忆位置",
|
|
344
|
+
"fix": "Functional Options: NewServer(addr, WithPort(9090), WithTimeout(...))"
|
|
345
|
+
},
|
|
346
|
+
"reasoning": {
|
|
347
|
+
"whyStandard": "Dave Cheney - Functional Options; Effective Go - Methods",
|
|
348
|
+
"sources": [
|
|
349
|
+
"Dave Cheney Blog",
|
|
350
|
+
"Effective Go - Methods"
|
|
351
|
+
],
|
|
352
|
+
"confidence": 0.9
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### 接收者选择规则
|
|
358
|
+
|
|
359
|
+
| 条件 | 使用 | 理由 |
|
|
360
|
+
|------|------|------|
|
|
361
|
+
| 修改 struct 字段 | `*T` 指针 | 值接收者是副本,修改不可见 |
|
|
362
|
+
| struct 较大 (>3 字段) | `*T` 指针 | 避免拷贝开销 |
|
|
363
|
+
| 一致性 | `*T` 指针 | 如果类型有任何指针方法,全部用指针 |
|
|
364
|
+
| 小的不可变类型 | `T` 值 | `time.Time`, `Point`, `Color` |
|
|
365
|
+
| map/slice/channel 类型 | `T` 值 | 已经是引用类型 |
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## 8. 测试
|
|
370
|
+
|
|
371
|
+
```json
|
|
372
|
+
{
|
|
373
|
+
"title": "Go: 测试最佳实践",
|
|
374
|
+
"content": {
|
|
375
|
+
"markdown": "## Go: 测试最佳实践\n\n### 标准模式\n```go\n// ✅ 表驱动测试 (Table-driven tests)\nfunc TestAdd(t *testing.T) {\n tests := []struct {\n name string\n a, b int\n expected int\n }{\n {\"positive\", 1, 2, 3},\n {\"zero\", 0, 0, 0},\n {\"negative\", -1, -2, -3},\n {\"mixed\", -1, 2, 1},\n }\n\n for _, tt := range tests {\n t.Run(tt.name, func(t *testing.T) {\n got := Add(tt.a, tt.b)\n if got != tt.expected {\n t.Errorf(\"Add(%d, %d) = %d, want %d\", tt.a, tt.b, got, tt.expected)\n }\n })\n }\n}\n\n// ✅ 使用 testify 断言 (社区标准)\nimport \"github.com/stretchr/testify/assert\"\n\nfunc TestFindByID(t *testing.T) {\n service := NewUserService(mockRepo)\n user, err := service.FindByID(context.Background(), 1)\n assert.NoError(t, err)\n assert.Equal(t, \"Alice\", user.Name)\n assert.NotNil(t, user.CreatedAt)\n}\n\n// ✅ 使用接口做依赖注入 (便于 mock)\ntype UserRepository interface {\n Get(ctx context.Context, id int64) (*User, error)\n}\n\ntype mockUserRepo struct {\n users map[int64]*User\n}\n\nfunc (m *mockUserRepo) Get(ctx context.Context, id int64) (*User, error) {\n if u, ok := m.users[id]; ok {\n return u, nil\n }\n return nil, ErrNotFound\n}\n\n// ✅ TestMain 做全局 setup/teardown\nfunc TestMain(m *testing.M) {\n setup()\n code := m.Run()\n teardown()\n os.Exit(code)\n}\n\n// ✅ 子测试 + 并行\nfunc TestAPI(t *testing.T) {\n t.Run(\"Create\", func(t *testing.T) {\n t.Parallel()\n // ...\n })\n t.Run(\"Delete\", func(t *testing.T) {\n t.Parallel()\n // ...\n })\n}\n\n// ✅ Golden file 测试\nfunc TestRender(t *testing.T) {\n got := Render(input)\n golden := filepath.Join(\"testdata\", t.Name()+\".golden\")\n if *update {\n os.WriteFile(golden, got, 0o644)\n }\n want, _ := os.ReadFile(golden)\n assert.Equal(t, string(want), string(got))\n}\n```",
|
|
376
|
+
"pattern": "// ✅ 表驱动测试 (Table-driven tests)\nfunc TestAdd(t *testing.T) {\n tests := []struct {\n name string\n a, b int\n expected int\n }{\n {\"positive\", 1, 2, 3},\n {\"zero\", 0, 0, 0},\n {\"negative\", -1, -2, -3},\n {\"mixed\", -1, 2, 1},\n }\n\n for _, tt := range tests {\n t.Run(tt.name, func(t *testing.T) {\n got := Add(tt.a, tt.b)\n if got != tt.expected {\n t.Errorf(\"Add(%d, %d) = %d, want %d\", tt.a, tt.b, got, tt.expected)\n }\n })\n }\n}\n\n// ✅ 使用 testify 断言 (社区标准)\nimport \"github.com/stretchr/testify/assert\"\n\nfunc TestFindByID(t *testing.T) {\n service := NewUserService(mockRepo)\n user, err := service.FindByID(context.Background(), 1)\n assert.NoError(t, err)\n assert.Equal(t, \"Alice\", user.Name)\n assert.NotNil(t, user.CreatedAt)\n}\n\n// ✅ 使用接口做依赖注入 (便于 mock)\ntype UserRepository interface {\n Get(ctx context.Context, id int64) (*User, error)\n}\n\ntype mockUserRepo struct {\n users map[int64]*User\n}\n\nfunc (m *mockUserRepo) Get(ctx context.Context, id int64) (*User, error) {\n if u, ok := m.users[id]; ok {\n return u, nil\n }\n return nil, ErrNotFound\n}\n\n// ✅ TestMain 做全局 setup/teardown\nfunc TestMain(m *testing.M) {\n setup()\n code := m.Run()\n teardown()\n os.Exit(code)\n}\n\n// ✅ 子测试 + 并行\nfunc TestAPI(t *testing.T) {\n t.Run(\"Create\", func(t *testing.T) {\n t.Parallel()\n // ...\n })\n t.Run(\"Delete\", func(t *testing.T) {\n t.Parallel()\n // ...\n })\n}\n\n// ✅ Golden file 测试\nfunc TestRender(t *testing.T) {\n got := Render(input)\n golden := filepath.Join(\"testdata\", t.Name()+\".golden\")\n if *update {\n os.WriteFile(golden, got, 0o644)\n }\n want, _ := os.ReadFile(golden)\n assert.Equal(t, string(want), string(got))\n}",
|
|
377
|
+
"rationale": "表驱动测试是 Go 社区的标准测试模式,减少重复并提高覆盖率"
|
|
378
|
+
},
|
|
379
|
+
"description": "Go: 测试最佳实践",
|
|
380
|
+
"kind": "pattern",
|
|
381
|
+
"doClause": "Apply the Go pattern as described",
|
|
382
|
+
"language": "go",
|
|
383
|
+
"headers": [],
|
|
384
|
+
"knowledgeType": "best-practice",
|
|
385
|
+
"usageGuide": "### 使用场景\\n触发 `@trigger` 获取Go: 测试最佳实践的标准实现模式。",
|
|
386
|
+
"antiPattern": {
|
|
387
|
+
"bad": "func TestAdd1(t *testing.T) { ... } func TestAdd2(t *testing.T) { ... }",
|
|
388
|
+
"why": "重复代码多,难以新增测试用例",
|
|
389
|
+
"fix": "使用表驱动测试 + t.Run 子测试"
|
|
390
|
+
},
|
|
391
|
+
"reasoning": {
|
|
392
|
+
"whyStandard": "Go Wiki - TableDrivenTests; Go Blog - Subtests and Sub-benchmarks",
|
|
393
|
+
"sources": [
|
|
394
|
+
"Go Wiki - TableDrivenTests",
|
|
395
|
+
"Go Blog - Subtests"
|
|
396
|
+
],
|
|
397
|
+
"confidence": 0.95
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### 测试命名约定
|
|
403
|
+
|
|
404
|
+
| 类型 | 命名 | 示例 |
|
|
405
|
+
|------|------|------|
|
|
406
|
+
| 测试文件 | `*_test.go` | `user_service_test.go` |
|
|
407
|
+
| 测试函数 | `Test` + 功能描述 | `TestFindByID` |
|
|
408
|
+
| 子测试 | 描述性名称 | `t.Run("not found", ...)` |
|
|
409
|
+
| Benchmark | `Benchmark` + 操作 | `BenchmarkSerialize` |
|
|
410
|
+
| Example | `Example` + 函数名 | `ExampleNewServer` |
|
|
411
|
+
| Fixtures | `testdata/` 目录 | `testdata/input.json` |
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
## 9. defer 使用
|
|
416
|
+
|
|
417
|
+
```json
|
|
418
|
+
{
|
|
419
|
+
"title": "Go: defer 最佳实践",
|
|
420
|
+
"content": {
|
|
421
|
+
"markdown": "## Go: defer 最佳实践\n\n### 标准模式\n```go\n// ✅ 资源清理 — 打开后立即 defer 关闭\nfunc ReadFile(name string) ([]byte, error) {\n f, err := os.Open(name)\n if err != nil {\n return nil, err\n }\n defer f.Close() // 紧跟 Open,确保不遗漏\n\n return io.ReadAll(f)\n}\n\n// ✅ Mutex unlock\nfunc (c *Cache) Get(key string) (string, bool) {\n c.mu.RLock()\n defer c.mu.RUnlock()\n v, ok := c.data[key]\n return v, ok\n}\n\n// ✅ Recover from panic (HTTP middleware)\nfunc Recovery() gin.HandlerFunc {\n return func(c *gin.Context) {\n defer func() {\n if r := recover(); r != nil {\n log.Printf(\"panic recovered: %v\\n%s\", r, debug.Stack())\n c.AbortWithStatus(http.StatusInternalServerError)\n }\n }()\n c.Next()\n }\n}\n\n// ✅ 处理 defer 中的错误\nfunc WriteFile(name string, data []byte) (err error) {\n f, err := os.Create(name)\n if err != nil {\n return err\n }\n defer func() {\n if cerr := f.Close(); err == nil {\n err = cerr // 仅在没有其他错误时上报 Close 错误\n }\n }()\n _, err = f.Write(data)\n return err\n}\n\n// ❌ 循环中 defer — 可能资源积压到函数结束\nfor _, name := range files {\n f, _ := os.Open(name)\n defer f.Close() // ❌ 全部在函数返回时才 Close\n```",
|
|
422
|
+
"pattern": "// ✅ 资源清理 — 打开后立即 defer 关闭\nfunc ReadFile(name string) ([]byte, error) {\n f, err := os.Open(name)\n if err != nil {\n return nil, err\n }\n defer f.Close() // 紧跟 Open,确保不遗漏\n\n return io.ReadAll(f)\n}\n\n// ✅ Mutex unlock\nfunc (c *Cache) Get(key string) (string, bool) {\n c.mu.RLock()\n defer c.mu.RUnlock()\n v, ok := c.data[key]\n return v, ok\n}\n\n// ✅ Recover from panic (HTTP middleware)\nfunc Recovery() gin.HandlerFunc {\n return func(c *gin.Context) {\n defer func() {\n if r := recover(); r != nil {\n log.Printf(\"panic recovered: %v\\n%s\", r, debug.Stack())\n c.AbortWithStatus(http.StatusInternalServerError)\n }\n }()\n c.Next()\n }\n}\n\n// ✅ 处理 defer 中的错误\nfunc WriteFile(name string, data []byte) (err error) {\n f, err := os.Create(name)\n if err != nil {\n return err\n }\n defer func() {\n if cerr := f.Close(); err == nil {\n err = cerr // 仅在没有其他错误时上报 Close 错误\n }\n }()\n _, err = f.Write(data)\n return err\n}\n\n// ❌ 循环中 defer — 可能资源积压到函数结束\nfor _, name := range files {\n f, _ := os.Open(name)\n defer f.Close() // ❌ 全部在函数返回时才 Close",
|
|
423
|
+
"rationale": "Go: defer 最佳实践的标准实现模式。"
|
|
424
|
+
},
|
|
425
|
+
"description": "Go: defer 最佳实践",
|
|
426
|
+
"kind": "pattern",
|
|
427
|
+
"doClause": "Apply the Go pattern as described",
|
|
428
|
+
"language": "go",
|
|
429
|
+
"headers": [],
|
|
430
|
+
"knowledgeType": "code-pattern",
|
|
431
|
+
"usageGuide": "### 使用场景\\n触发 `@trigger` 获取Go: defer 最佳实践的标准实现模式。",
|
|
432
|
+
"antiPattern": {
|
|
433
|
+
"bad": "for _, name := range files { f, _ := os.Open(name); defer f.Close() }",
|
|
434
|
+
"why": "defer 在函数退出时才执行,循环中会积压大量未关闭的文件",
|
|
435
|
+
"fix": "将循环体提取为单独函数,或在循环内手动 Close"
|
|
436
|
+
},
|
|
437
|
+
"reasoning": {
|
|
438
|
+
"whyStandard": "Effective Go - Defer; Go Blog - Defer, Panic, and Recover",
|
|
439
|
+
"sources": [
|
|
440
|
+
"Effective Go - Defer",
|
|
441
|
+
"Go Blog - Defer, Panic, and Recover"
|
|
442
|
+
],
|
|
443
|
+
"confidence": 0.9
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
---
|
|
449
|
+
|
|
450
|
+
## 10. 代码文档
|
|
451
|
+
|
|
452
|
+
```json
|
|
453
|
+
{
|
|
454
|
+
"title": "Go: godoc 注释规范",
|
|
455
|
+
"content": {
|
|
456
|
+
"markdown": "## Go: godoc 注释规范\n\n### 标准模式\n```go\n// ✅ 包注释 — doc.go 或第一个文件\n// Package user provides user account management functionality\n// including CRUD operations, authentication, and role-based access control.\npackage user\n\n// ✅ 导出符号注释 — 以符号名开头\n// UserService handles user business logic.\n// It is safe for concurrent use.\ntype UserService struct {\n repo UserRepository\n logger *zap.Logger\n}\n\n// NewUserService creates a UserService with the given repository.\n// If logger is nil, a no-op logger is used.\nfunc NewUserService(repo UserRepository, logger *zap.Logger) *UserService {\n if logger == nil {\n logger = zap.NewNop()\n }\n return &UserService{repo: repo, logger: logger}\n}\n\n// FindByID returns the user with the given ID.\n// It returns ErrNotFound if no user exists with that ID.\nfunc (s *UserService) FindByID(ctx context.Context, id int64) (*User, error) {\n // ...\n}\n\n// ✅ 示例函数 (godoc 可运行)\nfunc ExampleNewUserService() {\n svc := NewUserService(NewMemoryRepo(), nil)\n user, _ := svc.FindByID(context.Background(), 1)\n fmt.Println(user.Name)\n // Output: Alice\n}\n\n// ❌ 不要\n// This function returns the user // 不以符号名开头\n// getter for name field // 无意义注释\n```",
|
|
457
|
+
"pattern": "// ✅ 包注释 — doc.go 或第一个文件\n// Package user provides user account management functionality\n// including CRUD operations, authentication, and role-based access control.\npackage user\n\n// ✅ 导出符号注释 — 以符号名开头\n// UserService handles user business logic.\n// It is safe for concurrent use.\ntype UserService struct {\n repo UserRepository\n logger *zap.Logger\n}\n\n// NewUserService creates a UserService with the given repository.\n// If logger is nil, a no-op logger is used.\nfunc NewUserService(repo UserRepository, logger *zap.Logger) *UserService {\n if logger == nil {\n logger = zap.NewNop()\n }\n return &UserService{repo: repo, logger: logger}\n}\n\n// FindByID returns the user with the given ID.\n// It returns ErrNotFound if no user exists with that ID.\nfunc (s *UserService) FindByID(ctx context.Context, id int64) (*User, error) {\n // ...\n}\n\n// ✅ 示例函数 (godoc 可运行)\nfunc ExampleNewUserService() {\n svc := NewUserService(NewMemoryRepo(), nil)\n user, _ := svc.FindByID(context.Background(), 1)\n fmt.Println(user.Name)\n // Output: Alice\n}\n\n// ❌ 不要\n// This function returns the user // 不以符号名开头\n// getter for name field // 无意义注释",
|
|
458
|
+
"rationale": "godoc 从注释生成文档,注释以符号名开头是 Go 的核心约定"
|
|
459
|
+
},
|
|
460
|
+
"description": "Go: godoc 注释规范",
|
|
461
|
+
"kind": "rule",
|
|
462
|
+
"doClause": "Apply the Go pattern as described",
|
|
463
|
+
"language": "go",
|
|
464
|
+
"headers": [],
|
|
465
|
+
"knowledgeType": "code-standard",
|
|
466
|
+
"usageGuide": "### 使用场景\\n触发 `@trigger` 获取Go: godoc 注释规范的标准实现模式。",
|
|
467
|
+
"reasoning": {
|
|
468
|
+
"whyStandard": "Effective Go - Commentary; Go Code Review Comments - Comment Sentences",
|
|
469
|
+
"sources": [
|
|
470
|
+
"Effective Go",
|
|
471
|
+
"Go Code Review Comments"
|
|
472
|
+
],
|
|
473
|
+
"confidence": 0.95
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
---
|
|
479
|
+
|
|
480
|
+
## 11. HTTP 与 Web 模式
|
|
481
|
+
|
|
482
|
+
```json
|
|
483
|
+
{
|
|
484
|
+
"title": "Go: HTTP 服务最佳实践",
|
|
485
|
+
"content": {
|
|
486
|
+
"markdown": "## Go: HTTP 服务最佳实践\n\n### 标准模式\n```go\n// ✅ 结构化 Handler\ntype UserHandler struct {\n service UserService\n logger *zap.Logger\n}\n\nfunc (h *UserHandler) RegisterRoutes(r *gin.RouterGroup) {\n users := r.Group(\"/users\")\n {\n users.GET(\"\", h.List)\n users.GET(\"/:id\", h.GetByID)\n users.POST(\"\", h.Create)\n users.PUT(\"/:id\", h.Update)\n users.DELETE(\"/:id\", h.Delete)\n }\n}\n\n// ✅ Middleware 链\nfunc Logger(logger *zap.Logger) gin.HandlerFunc {\n return func(c *gin.Context) {\n start := time.Now()\n c.Next()\n logger.Info(\"request\",\n zap.String(\"method\", c.Request.Method),\n zap.String(\"path\", c.Request.URL.Path),\n zap.Int(\"status\", c.Writer.Status()),\n zap.Duration(\"latency\", time.Since(start)),\n )\n }\n}\n\n// ✅ Graceful Shutdown\nfunc main() {\n srv := &http.Server{Addr: \":8080\", Handler: router}\n\n go func() {\n if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {\n log.Fatalf(\"listen: %s\", err)\n }\n }()\n\n quit := make(chan os.Signal, 1)\n signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)\n <-quit\n\n ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n defer cancel()\n if err := srv.Shutdown(ctx); err != nil {\n log.Fatal(\"server shutdown:\", err)\n }\n}\n\n// ✅ 统一错误响应\ntype APIError struct {\n Code int `json:\"code\"`\n Message string `json:\"message\"`\n}\n\nfunc ErrorResponse(c *gin.Context, status int, msg string) {\n c.JSON(status, APIError{Code: status, Message: msg})\n}\n```",
|
|
487
|
+
"pattern": "// ✅ 结构化 Handler\ntype UserHandler struct {\n service UserService\n logger *zap.Logger\n}\n\nfunc (h *UserHandler) RegisterRoutes(r *gin.RouterGroup) {\n users := r.Group(\"/users\")\n {\n users.GET(\"\", h.List)\n users.GET(\"/:id\", h.GetByID)\n users.POST(\"\", h.Create)\n users.PUT(\"/:id\", h.Update)\n users.DELETE(\"/:id\", h.Delete)\n }\n}\n\n// ✅ Middleware 链\nfunc Logger(logger *zap.Logger) gin.HandlerFunc {\n return func(c *gin.Context) {\n start := time.Now()\n c.Next()\n logger.Info(\"request\",\n zap.String(\"method\", c.Request.Method),\n zap.String(\"path\", c.Request.URL.Path),\n zap.Int(\"status\", c.Writer.Status()),\n zap.Duration(\"latency\", time.Since(start)),\n )\n }\n}\n\n// ✅ Graceful Shutdown\nfunc main() {\n srv := &http.Server{Addr: \":8080\", Handler: router}\n\n go func() {\n if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {\n log.Fatalf(\"listen: %s\", err)\n }\n }()\n\n quit := make(chan os.Signal, 1)\n signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)\n <-quit\n\n ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n defer cancel()\n if err := srv.Shutdown(ctx); err != nil {\n log.Fatal(\"server shutdown:\", err)\n }\n}\n\n// ✅ 统一错误响应\ntype APIError struct {\n Code int `json:\"code\"`\n Message string `json:\"message\"`\n}\n\nfunc ErrorResponse(c *gin.Context, status int, msg string) {\n c.JSON(status, APIError{Code: status, Message: msg})\n}",
|
|
488
|
+
"rationale": "结构化 handler + 依赖注入 + middleware 链是 Go web 服务的标准架构"
|
|
489
|
+
},
|
|
490
|
+
"description": "Go: HTTP 服务最佳实践",
|
|
491
|
+
"kind": "fact",
|
|
492
|
+
"doClause": "Apply the Go pattern as described",
|
|
493
|
+
"language": "go",
|
|
494
|
+
"headers": [],
|
|
495
|
+
"knowledgeType": "architecture",
|
|
496
|
+
"usageGuide": "### 使用场景\\n触发 `@trigger` 获取Go: HTTP 服务最佳实践的标准实现模式。",
|
|
497
|
+
"reasoning": {
|
|
498
|
+
"whyStandard": "Go Blog - Writing Web Applications; Gin/Echo best practices",
|
|
499
|
+
"sources": [
|
|
500
|
+
"Go Blog",
|
|
501
|
+
"Gin Documentation",
|
|
502
|
+
"Uber Go Style Guide"
|
|
503
|
+
],
|
|
504
|
+
"confidence": 0.85
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
---
|
|
510
|
+
|
|
511
|
+
## 12. Go 特有维度 (extraDimensions)
|
|
512
|
+
|
|
513
|
+
冷启动分析 Go 项目时,除了通用维度,还应额外关注:
|
|
514
|
+
|
|
515
|
+
| 额外维度 | 寻找什么 | 候选类型 |
|
|
516
|
+
|---------|---------|---------|
|
|
517
|
+
| **错误处理模式** | sentinel error、错误包装 `%w`、自定义错误类型、`errors.Is/As` 使用 | `code-pattern` |
|
|
518
|
+
| **接口设计** | 消费者侧接口、小接口组合、编译期合规性检查 | `best-practice` |
|
|
519
|
+
| **并发模型** | goroutine 管理、channel 模式、errgroup、sync 原语 | `code-pattern` |
|
|
520
|
+
| **Context 传播** | ctx 第一参数、超时控制、Value 使用规范 | `code-standard` |
|
|
521
|
+
| **Functional Options** | WithXxx 构造模式、默认值策略 | `code-pattern` |
|
|
522
|
+
| **项目布局** | cmd/internal/pkg 分层、包命名、API 定义 | `architecture` |
|
|
523
|
+
| **测试模式** | 表驱动测试、golden file、testdata/、接口 mock | `best-practice` |
|
|
524
|
+
| **构建工具** | go.mod 依赖管理、go generate、build tags | `config` |
|
|
525
|
+
| **Web/gRPC 模式** | Handler 结构、Middleware 链、Graceful Shutdown | `architecture` |
|
|
526
|
+
| **性能** | sync.Pool、pprof 集成、避免不必要的内存分配 | `best-practice` |
|
|
527
|
+
|
|
528
|
+
---
|
|
529
|
+
|
|
530
|
+
## 关联 Skills
|
|
531
|
+
|
|
532
|
+
- **autosnippet-coldstart**: 冷启动分析模板
|
|
533
|
+
- **autosnippet-reference-java**: Java 业界最佳实践参考
|
|
534
|
+
- **autosnippet-reference-kotlin**: Kotlin 业界最佳实践参考
|
|
535
|
+
- **autosnippet-reference-python**: Python 业界最佳实践参考
|
|
536
|
+
- **autosnippet-reference-jsts**: JavaScript/TypeScript 业界最佳实践参考
|
|
537
|
+
- **autosnippet-reference-objc**: Objective-C 业界最佳实践参考
|
|
538
|
+
- **autosnippet-reference-swift**: Swift 业界最佳实践参考
|
|
539
|
+
- **autosnippet-reference-dart**: Dart (Flutter) 业界最佳实践参考
|