zco-claude 0.0.8__py3-none-any.whl
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.
- ClaudeSettings/DOT.claudeignore +7 -0
- ClaudeSettings/README.md +100 -0
- ClaudeSettings/commands/generate_changelog.sh +49 -0
- ClaudeSettings/commands/show_env +92 -0
- ClaudeSettings/commands/zco-clean +164 -0
- ClaudeSettings/commands/zco-git-summary +15 -0
- ClaudeSettings/commands/zco-git-tag +42 -0
- ClaudeSettings/hooks/CHANGELOG.md +157 -0
- ClaudeSettings/hooks/README.md +254 -0
- ClaudeSettings/hooks/save_chat_plain.py +148 -0
- ClaudeSettings/hooks/save_chat_spec.py +398 -0
- ClaudeSettings/rules/README.md +270 -0
- ClaudeSettings/rules/go/.golangci.yml.template +170 -0
- ClaudeSettings/rules/go/GoBuildAutoVersion.v250425.md +95 -0
- ClaudeSettings/rules/go/check-standards.sh +128 -0
- ClaudeSettings/rules/go/coding-standards.md +973 -0
- ClaudeSettings/rules/go/example.go +207 -0
- ClaudeSettings/rules/go/go-testing.md +691 -0
- ClaudeSettings/rules/go/list-comments.sh +85 -0
- ClaudeSettings/settings.sample.json +71 -0
- ClaudeSettings/skills/README.md +225 -0
- ClaudeSettings/skills/zco-docs-update/SKILL.md +381 -0
- ClaudeSettings/skills/zco-help/SKILL.md +601 -0
- ClaudeSettings/skills/zco-plan/SKILL.md +661 -0
- ClaudeSettings/skills/zco-plan-new/SKILL.md +585 -0
- ClaudeSettings/zco-scripts/co-docs-update.sh +150 -0
- ClaudeSettings/zco-scripts/test_update_plan_metadata.py +328 -0
- ClaudeSettings/zco-scripts/update-plan-metadata.py +324 -0
- zco_claude-0.0.8.dist-info/METADATA +190 -0
- zco_claude-0.0.8.dist-info/RECORD +34 -0
- zco_claude-0.0.8.dist-info/WHEEL +5 -0
- zco_claude-0.0.8.dist-info/entry_points.txt +3 -0
- zco_claude-0.0.8.dist-info/top_level.txt +1 -0
- zco_claude_init.py +1732 -0
|
@@ -0,0 +1,973 @@
|
|
|
1
|
+
# Go 项目编程标准
|
|
2
|
+
|
|
3
|
+
## 注释规范
|
|
4
|
+
|
|
5
|
+
### 注释类型区分(核心约定)
|
|
6
|
+
|
|
7
|
+
本项目使用不同的注释标记来区分注释的用途:
|
|
8
|
+
|
|
9
|
+
#### 1. 代码功能注释 `//; `
|
|
10
|
+
**用途**:解释代码的功能、逻辑、算法, 跟纯代码注释"//"区分
|
|
11
|
+
|
|
12
|
+
**使用场景**:
|
|
13
|
+
- 解释复杂的业务逻辑
|
|
14
|
+
- 说明算法实现思路
|
|
15
|
+
- 阐述为什么这样写(设计决策)
|
|
16
|
+
- 公共 API 的文档注释
|
|
17
|
+
|
|
18
|
+
**示例**:
|
|
19
|
+
```go
|
|
20
|
+
//; CalculateDiscount 根据订单金额和用户等级计算折扣
|
|
21
|
+
//; 折扣规则:
|
|
22
|
+
//; - VIP 用户:订单金额的 15%
|
|
23
|
+
//; - 普通用户:订单金额的 10%
|
|
24
|
+
//; - 订单金额 < 100 元不享受折扣
|
|
25
|
+
func CalculateDiscount(amount float64, userLevel string) float64 {
|
|
26
|
+
//; 小额订单不参与折扣活动
|
|
27
|
+
if amount < 100 {
|
|
28
|
+
return 0
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
//; 根据用户等级计算折扣率
|
|
32
|
+
var rate float64
|
|
33
|
+
switch userLevel {
|
|
34
|
+
case "VIP":
|
|
35
|
+
rate = 0.15
|
|
36
|
+
default:
|
|
37
|
+
rate = 0.10
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return amount * rate
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
#### 2. 非代码注释 `//;@`
|
|
45
|
+
**用途**:元信息、开发备注、待办事项、调试信息
|
|
46
|
+
|
|
47
|
+
**使用场景**:
|
|
48
|
+
- `//;@TODO:` - 待实现的功能
|
|
49
|
+
- `//;@FIXME:` - 需要修复的问题
|
|
50
|
+
- `//;@NOTE:` - 开发者备注
|
|
51
|
+
- `//;@HACK:` - 临时解决方案
|
|
52
|
+
- `//;@OPTIMIZE:` - 性能优化点
|
|
53
|
+
- `//;@DEPRECATED:` - 已废弃的代码
|
|
54
|
+
- `//;@DEBUG:` - 调试信息
|
|
55
|
+
- `//;@XXX:` - 需要注意的问题
|
|
56
|
+
|
|
57
|
+
**示例**:
|
|
58
|
+
```go
|
|
59
|
+
func ProcessOrder(order *Order) error {
|
|
60
|
+
//;@TODO: 添加订单金额验证
|
|
61
|
+
//;@NOTE: 这里需要考虑并发安全问题
|
|
62
|
+
|
|
63
|
+
// 计算订单总金额
|
|
64
|
+
total := calculateTotal(order)
|
|
65
|
+
|
|
66
|
+
//;@FIXME: Redis 连接偶尔会超时,需要添加重试机制
|
|
67
|
+
if err := cache.Set(order.ID, total); err != nil {
|
|
68
|
+
return err
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
//;@HACK: 临时方案,等待支付网关 API 升级后移除
|
|
72
|
+
if order.PaymentMethod == "alipay_v1" {
|
|
73
|
+
return processLegacyAlipay(order)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
//;@OPTIMIZE: 这里可以使用批量查询减少数据库访问
|
|
77
|
+
for _, item := range order.Items {
|
|
78
|
+
product, err := db.GetProduct(item.ProductID)
|
|
79
|
+
if err != nil {
|
|
80
|
+
return err
|
|
81
|
+
}
|
|
82
|
+
//; 处理商品...
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return nil
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
//;@DEPRECATED: 使用 ProcessOrderV2 替代
|
|
89
|
+
//;此函数将在 v2.0 版本移除
|
|
90
|
+
func ProcessOrderLegacy(order *Order) error {
|
|
91
|
+
// ...
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
#### 3. 注释使用原则
|
|
96
|
+
|
|
97
|
+
**强制规则(MUST)**:
|
|
98
|
+
- ✅ 所有导出的函数、类型、常量必须有文档注释(使用 `//`)
|
|
99
|
+
- ✅ 复杂逻辑必须有解释注释(使用 `//`)
|
|
100
|
+
- ✅ 所有 TODO、FIXME 等必须使用 `//;` 前缀
|
|
101
|
+
- ✅ 调试注释、临时备注必须使用 `//;` 前缀
|
|
102
|
+
- ❌ 禁止用注释注释掉代码(应该删除或使用 Git)
|
|
103
|
+
|
|
104
|
+
**推荐规则(SHOULD)**:
|
|
105
|
+
- 注释应该解释"为什么"而不是"是什么"
|
|
106
|
+
- 代码应该自解释,减少不必要的注释
|
|
107
|
+
- 注释应该与代码保持同步更新
|
|
108
|
+
|
|
109
|
+
**注释检查工具**:
|
|
110
|
+
```bash
|
|
111
|
+
# 查找所有非代码注释
|
|
112
|
+
grep -r "//;" . --include="*.go"
|
|
113
|
+
|
|
114
|
+
# 查找所有 TODO
|
|
115
|
+
grep -r "//;@TODO:" . --include="*.go"
|
|
116
|
+
|
|
117
|
+
# 查找所有需要修复的问题
|
|
118
|
+
grep -r "//;@FIXME:" . --include="*.go"
|
|
119
|
+
|
|
120
|
+
# 查找已废弃的代码
|
|
121
|
+
grep -r "//;@DEPRECATED:" . --include="*.go"
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 命名规范
|
|
127
|
+
|
|
128
|
+
### 包命名
|
|
129
|
+
|
|
130
|
+
**规则**:
|
|
131
|
+
- 使用小写单词,不使用下划线或驼峰
|
|
132
|
+
- 简短且有意义
|
|
133
|
+
- 避免使用泛化名称(util、common、base)
|
|
134
|
+
|
|
135
|
+
**示例**:
|
|
136
|
+
```go
|
|
137
|
+
✅ package user
|
|
138
|
+
✅ package payment
|
|
139
|
+
✅ package cache
|
|
140
|
+
|
|
141
|
+
❌ package userService
|
|
142
|
+
❌ package user_service
|
|
143
|
+
❌ package utils
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### 变量命名
|
|
147
|
+
|
|
148
|
+
**规则**:
|
|
149
|
+
- 使用驼峰命名法(camelCase)
|
|
150
|
+
- 导出变量使用大写开头(PascalCase)
|
|
151
|
+
- 缩写词保持一致的大小写(ID、URL、HTTP)
|
|
152
|
+
- 单字母变量仅用于短循环
|
|
153
|
+
|
|
154
|
+
**示例**:
|
|
155
|
+
```go
|
|
156
|
+
// 普通变量
|
|
157
|
+
var userName string
|
|
158
|
+
var orderCount int
|
|
159
|
+
|
|
160
|
+
// 导出变量
|
|
161
|
+
var MaxRetryCount = 3
|
|
162
|
+
var DefaultTimeout = 30 * time.Second
|
|
163
|
+
|
|
164
|
+
// 缩写词
|
|
165
|
+
var userID int64 // ✅
|
|
166
|
+
var userUrl string // ❌ 应该是 userURL
|
|
167
|
+
var HTTPClient *http.Client // ✅
|
|
168
|
+
|
|
169
|
+
// 循环变量
|
|
170
|
+
for i := 0; i < len(items); i++ { // ✅ 短循环可以用 i
|
|
171
|
+
// ...
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
for index, item := range items { // ✅ 长循环用有意义的名字
|
|
175
|
+
// ...
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### 函数命名
|
|
180
|
+
|
|
181
|
+
**规则**:
|
|
182
|
+
- 使用驼峰命名法
|
|
183
|
+
- 动词开头,表达清晰的动作
|
|
184
|
+
- 布尔返回值的函数以 `is`、`has`、`can` 等开头
|
|
185
|
+
|
|
186
|
+
**示例**:
|
|
187
|
+
```go
|
|
188
|
+
// 普通函数
|
|
189
|
+
func calculateTotal(items []Item) float64 { }
|
|
190
|
+
func sendEmail(to string, body string) error { }
|
|
191
|
+
|
|
192
|
+
// 导出函数
|
|
193
|
+
func CreateUser(name, email string) (*User, error) { }
|
|
194
|
+
func UpdateOrder(id int64, status string) error { }
|
|
195
|
+
|
|
196
|
+
// 布尔函数
|
|
197
|
+
func isValid(email string) bool { }
|
|
198
|
+
func hasPermission(user *User, resource string) bool { }
|
|
199
|
+
func canEdit(user *User) bool { }
|
|
200
|
+
|
|
201
|
+
// Getter/Setter
|
|
202
|
+
func (u *User) Name() string { return u.name } // ✅
|
|
203
|
+
func (u *User) SetName(name string) { u.name = name } // ✅
|
|
204
|
+
func (u *User) GetName() string { } // ❌ Go 不用 Get 前缀
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### 常量命名
|
|
208
|
+
|
|
209
|
+
**规则**:
|
|
210
|
+
- 导出常量使用大写开头驼峰
|
|
211
|
+
- 私有常量使用小写开头驼峰
|
|
212
|
+
- 枚举值使用类型名作为前缀
|
|
213
|
+
|
|
214
|
+
**示例**:
|
|
215
|
+
```go
|
|
216
|
+
// 导出常量
|
|
217
|
+
const (
|
|
218
|
+
MaxLoginAttempts = 5
|
|
219
|
+
DefaultPageSize = 20
|
|
220
|
+
APIVersion = "v1"
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
// 私有常量
|
|
224
|
+
const (
|
|
225
|
+
maxRetries = 3
|
|
226
|
+
defaultTimeout = 30
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
// 枚举(使用类型前缀)
|
|
230
|
+
type OrderStatus int
|
|
231
|
+
|
|
232
|
+
const (
|
|
233
|
+
OrderStatusPending OrderStatus = iota
|
|
234
|
+
OrderStatusPaid
|
|
235
|
+
OrderStatusShipped
|
|
236
|
+
OrderStatusCompleted
|
|
237
|
+
OrderStatusCancelled
|
|
238
|
+
)
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### 接口命名
|
|
242
|
+
|
|
243
|
+
**规则**:
|
|
244
|
+
- 单方法接口使用 `-er` 后缀
|
|
245
|
+
- 多方法接口使用有意义的名词
|
|
246
|
+
|
|
247
|
+
**示例**:
|
|
248
|
+
```go
|
|
249
|
+
// 单方法接口
|
|
250
|
+
type Reader interface {
|
|
251
|
+
Read(p []byte) (n int, err error)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
type Writer interface {
|
|
255
|
+
Write(p []byte) (n int, err error)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
type Closer interface {
|
|
259
|
+
Close() error
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// 多方法接口
|
|
263
|
+
type UserRepository interface {
|
|
264
|
+
Create(user *User) error
|
|
265
|
+
FindByID(id int64) (*User, error)
|
|
266
|
+
Update(user *User) error
|
|
267
|
+
Delete(id int64) error
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## 代码组织
|
|
274
|
+
|
|
275
|
+
### 文件结构
|
|
276
|
+
|
|
277
|
+
**标准文件顺序**:
|
|
278
|
+
```go
|
|
279
|
+
// 1. Package 声明
|
|
280
|
+
package user
|
|
281
|
+
|
|
282
|
+
// 2. Import 分组(标准库 -> 第三方库 -> 本项目)
|
|
283
|
+
import (
|
|
284
|
+
// 标准库
|
|
285
|
+
"context"
|
|
286
|
+
"fmt"
|
|
287
|
+
"time"
|
|
288
|
+
|
|
289
|
+
// 第三方库
|
|
290
|
+
"github.com/gin-gonic/gin"
|
|
291
|
+
"gorm.io/gorm"
|
|
292
|
+
|
|
293
|
+
// 本项目
|
|
294
|
+
"yourproject/internal/model"
|
|
295
|
+
"yourproject/pkg/logger"
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
// 3. 常量定义
|
|
299
|
+
const (
|
|
300
|
+
MaxNameLength = 50
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
// 4. 变量定义
|
|
304
|
+
var (
|
|
305
|
+
DefaultTimeout = 30 * time.Second
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
// 5. 类型定义
|
|
309
|
+
type User struct {
|
|
310
|
+
ID int64
|
|
311
|
+
Name string
|
|
312
|
+
Email string
|
|
313
|
+
CreatedAt time.Time
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// 6. 构造函数
|
|
317
|
+
func NewUser(name, email string) *User {
|
|
318
|
+
return &User{
|
|
319
|
+
Name: name,
|
|
320
|
+
Email: email,
|
|
321
|
+
CreatedAt: time.Now(),
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// 7. 接口实现和方法
|
|
326
|
+
func (u *User) Validate() error {
|
|
327
|
+
// ...
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// 8. 公共函数
|
|
331
|
+
func CreateUser(ctx context.Context, user *User) error {
|
|
332
|
+
// ...
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// 9. 私有函数
|
|
336
|
+
func validateEmail(email string) bool {
|
|
337
|
+
// ...
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### 包结构
|
|
342
|
+
|
|
343
|
+
**推荐的项目结构**:
|
|
344
|
+
```
|
|
345
|
+
project/
|
|
346
|
+
├── cmd/ # 主程序入口
|
|
347
|
+
│ └── server/
|
|
348
|
+
│ └── main.go
|
|
349
|
+
├── internal/ # 私有代码
|
|
350
|
+
│ ├── handler/ # HTTP 处理器
|
|
351
|
+
│ ├── service/ # 业务逻辑
|
|
352
|
+
│ ├── repository/ # 数据访问
|
|
353
|
+
│ └── model/ # 数据模型
|
|
354
|
+
├── pkg/ # 可导出的库
|
|
355
|
+
│ ├── logger/
|
|
356
|
+
│ └── cache/
|
|
357
|
+
├── api/ # API 定义(protobuf、OpenAPI)
|
|
358
|
+
├── config/ # 配置文件
|
|
359
|
+
├── scripts/ # 脚本文件
|
|
360
|
+
└── test/ # 测试数据、集成测试
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
**分层原则**:
|
|
364
|
+
```go
|
|
365
|
+
// ✅ 推荐:清晰的分层
|
|
366
|
+
handler -> service -> repository -> database
|
|
367
|
+
| | |
|
|
368
|
+
v v v
|
|
369
|
+
HTTP 业务逻辑 数据访问
|
|
370
|
+
|
|
371
|
+
// ❌ 禁止:跨层调用
|
|
372
|
+
handler -> repository (跳过 service)
|
|
373
|
+
handler -> database (跳过 service 和 repository)
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## 错误处理
|
|
379
|
+
|
|
380
|
+
### 错误定义
|
|
381
|
+
|
|
382
|
+
**规则**:
|
|
383
|
+
- 使用 `errors.New()` 定义简单错误
|
|
384
|
+
- 使用自定义错误类型处理复杂错误
|
|
385
|
+
- 错误信息使用小写,不以标点结尾
|
|
386
|
+
- 使用 `fmt.Errorf()` 包装错误并添加上下文
|
|
387
|
+
|
|
388
|
+
**示例**:
|
|
389
|
+
```go
|
|
390
|
+
import (
|
|
391
|
+
"errors"
|
|
392
|
+
"fmt"
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
// 简单错误
|
|
396
|
+
var (
|
|
397
|
+
ErrUserNotFound = errors.New("user not found")
|
|
398
|
+
ErrInvalidEmail = errors.New("invalid email format")
|
|
399
|
+
ErrDuplicateEmail = errors.New("email already exists")
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
// 自定义错误类型
|
|
403
|
+
type ValidationError struct {
|
|
404
|
+
Field string
|
|
405
|
+
Message string
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
func (e *ValidationError) Error() string {
|
|
409
|
+
return fmt.Sprintf("validation failed on field '%s': %s", e.Field, e.Message)
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// 错误包装
|
|
413
|
+
func GetUser(id int64) (*User, error) {
|
|
414
|
+
user, err := db.FindByID(id)
|
|
415
|
+
if err != nil {
|
|
416
|
+
//;@NOTE: 使用 %w 可以保留原始错误,支持 errors.Is() 和 errors.As()
|
|
417
|
+
return nil, fmt.Errorf("failed to get user %d: %w", id, err)
|
|
418
|
+
}
|
|
419
|
+
return user, nil
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// 错误检查
|
|
423
|
+
func ProcessUser(id int64) error {
|
|
424
|
+
user, err := GetUser(id)
|
|
425
|
+
if err != nil {
|
|
426
|
+
// 使用 errors.Is 检查特定错误
|
|
427
|
+
if errors.Is(err, ErrUserNotFound) {
|
|
428
|
+
return fmt.Errorf("cannot process: user %d not found", id)
|
|
429
|
+
}
|
|
430
|
+
return err
|
|
431
|
+
}
|
|
432
|
+
// ...
|
|
433
|
+
return nil
|
|
434
|
+
}
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### 错误处理模式
|
|
438
|
+
|
|
439
|
+
**推荐模式**:
|
|
440
|
+
```go
|
|
441
|
+
// ✅ 推荐:及早返回
|
|
442
|
+
func ProcessOrder(order *Order) error {
|
|
443
|
+
if err := validateOrder(order); err != nil {
|
|
444
|
+
return fmt.Errorf("validation failed: %w", err)
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
if err := checkInventory(order); err != nil {
|
|
448
|
+
return fmt.Errorf("inventory check failed: %w", err)
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if err := processPayment(order); err != nil {
|
|
452
|
+
return fmt.Errorf("payment processing failed: %w", err)
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
return nil
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// ❌ 避免:嵌套过深
|
|
459
|
+
func ProcessOrder(order *Order) error {
|
|
460
|
+
if err := validateOrder(order); err == nil {
|
|
461
|
+
if err := checkInventory(order); err == nil {
|
|
462
|
+
if err := processPayment(order); err == nil {
|
|
463
|
+
return nil
|
|
464
|
+
} else {
|
|
465
|
+
return err
|
|
466
|
+
}
|
|
467
|
+
} else {
|
|
468
|
+
return err
|
|
469
|
+
}
|
|
470
|
+
} else {
|
|
471
|
+
return err
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
---
|
|
477
|
+
|
|
478
|
+
## 函数设计
|
|
479
|
+
|
|
480
|
+
### 函数长度
|
|
481
|
+
|
|
482
|
+
**规则**:
|
|
483
|
+
- 单个函数 ≤ 50 行(推荐)
|
|
484
|
+
- 圈复杂度 ≤ 10
|
|
485
|
+
- 参数数量 ≤ 5 个
|
|
486
|
+
|
|
487
|
+
**重构建议**:
|
|
488
|
+
```go
|
|
489
|
+
// ❌ 函数过长
|
|
490
|
+
func ProcessOrder(order *Order) error {
|
|
491
|
+
// 100+ 行代码...
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// ✅ 拆分为多个小函数
|
|
495
|
+
func ProcessOrder(order *Order) error {
|
|
496
|
+
if err := validateOrder(order); err != nil {
|
|
497
|
+
return err
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
if err := calculateTotal(order); err != nil {
|
|
501
|
+
return err
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
if err := processPayment(order); err != nil {
|
|
505
|
+
return err
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
if err := updateInventory(order); err != nil {
|
|
509
|
+
return err
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
return nil
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
func validateOrder(order *Order) error { /* ... */ }
|
|
516
|
+
func calculateTotal(order *Order) error { /* ... */ }
|
|
517
|
+
func processPayment(order *Order) error { /* ... */ }
|
|
518
|
+
func updateInventory(order *Order) error { /* ... */ }
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
### 参数传递
|
|
522
|
+
|
|
523
|
+
**规则**:
|
|
524
|
+
- 超过 3 个参数考虑使用配置结构体
|
|
525
|
+
- 使用选项模式(Functional Options Pattern)处理可选参数
|
|
526
|
+
|
|
527
|
+
**示例**:
|
|
528
|
+
```go
|
|
529
|
+
// ❌ 参数过多
|
|
530
|
+
func CreateUser(name, email, phone, address, city, country string, age int, isActive bool) error {
|
|
531
|
+
// ...
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// ✅ 使用结构体
|
|
535
|
+
type CreateUserRequest struct {
|
|
536
|
+
Name string
|
|
537
|
+
Email string
|
|
538
|
+
Phone string
|
|
539
|
+
Address string
|
|
540
|
+
City string
|
|
541
|
+
Country string
|
|
542
|
+
Age int
|
|
543
|
+
IsActive bool
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
func CreateUser(req *CreateUserRequest) error {
|
|
547
|
+
// ...
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// ✅ 使用选项模式
|
|
551
|
+
type UserOption func(*User)
|
|
552
|
+
|
|
553
|
+
func WithPhone(phone string) UserOption {
|
|
554
|
+
return func(u *User) {
|
|
555
|
+
u.Phone = phone
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
func WithAddress(address string) UserOption {
|
|
560
|
+
return func(u *User) {
|
|
561
|
+
u.Address = address
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
func NewUser(name, email string, opts ...UserOption) *User {
|
|
566
|
+
user := &User{
|
|
567
|
+
Name: name,
|
|
568
|
+
Email: email,
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
for _, opt := range opts {
|
|
572
|
+
opt(user)
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
return user
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// 使用
|
|
579
|
+
user := NewUser("John", "john@example.com",
|
|
580
|
+
WithPhone("123456"),
|
|
581
|
+
WithAddress("123 Main St"),
|
|
582
|
+
)
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
---
|
|
586
|
+
|
|
587
|
+
## 并发编程
|
|
588
|
+
|
|
589
|
+
### Goroutine 使用
|
|
590
|
+
|
|
591
|
+
**规则**:
|
|
592
|
+
- 明确 goroutine 的生命周期
|
|
593
|
+
- 避免 goroutine 泄漏
|
|
594
|
+
- 使用 context 控制 goroutine 退出
|
|
595
|
+
|
|
596
|
+
**示例**:
|
|
597
|
+
```go
|
|
598
|
+
// ✅ 推荐:使用 context 控制
|
|
599
|
+
func StartWorker(ctx context.Context) {
|
|
600
|
+
go func() {
|
|
601
|
+
ticker := time.NewTicker(1 * time.Second)
|
|
602
|
+
defer ticker.Stop()
|
|
603
|
+
|
|
604
|
+
for {
|
|
605
|
+
select {
|
|
606
|
+
case <-ctx.Done():
|
|
607
|
+
//;@NOTE: Context 取消时退出
|
|
608
|
+
return
|
|
609
|
+
case <-ticker.C:
|
|
610
|
+
// 执行任务
|
|
611
|
+
doWork()
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}()
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// ❌ 避免:无法控制的 goroutine
|
|
618
|
+
func StartWorker() {
|
|
619
|
+
go func() {
|
|
620
|
+
for {
|
|
621
|
+
time.Sleep(1 * time.Second)
|
|
622
|
+
doWork() // 永远无法停止
|
|
623
|
+
}
|
|
624
|
+
}()
|
|
625
|
+
}
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
### Channel 使用
|
|
629
|
+
|
|
630
|
+
**规则**:
|
|
631
|
+
- 只有发送方关闭 channel
|
|
632
|
+
- 使用 buffered channel 避免阻塞
|
|
633
|
+
- 使用 select 处理多个 channel
|
|
634
|
+
|
|
635
|
+
**示例**:
|
|
636
|
+
```go
|
|
637
|
+
// ✅ 推荐模式
|
|
638
|
+
func Producer(ctx context.Context) <-chan int {
|
|
639
|
+
ch := make(chan int, 10) // buffered channel
|
|
640
|
+
|
|
641
|
+
go func() {
|
|
642
|
+
defer close(ch) // 发送方关闭 channel
|
|
643
|
+
|
|
644
|
+
for i := 0; i < 100; i++ {
|
|
645
|
+
select {
|
|
646
|
+
case <-ctx.Done():
|
|
647
|
+
return
|
|
648
|
+
case ch <- i:
|
|
649
|
+
// 发送数据
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}()
|
|
653
|
+
|
|
654
|
+
return ch
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
func Consumer(ctx context.Context, ch <-chan int) {
|
|
658
|
+
for {
|
|
659
|
+
select {
|
|
660
|
+
case <-ctx.Done():
|
|
661
|
+
return
|
|
662
|
+
case val, ok := <-ch:
|
|
663
|
+
if !ok {
|
|
664
|
+
//;@NOTE: Channel 已关闭
|
|
665
|
+
return
|
|
666
|
+
}
|
|
667
|
+
process(val)
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
### Mutex 使用
|
|
674
|
+
|
|
675
|
+
**规则**:
|
|
676
|
+
- 锁的粒度尽可能小
|
|
677
|
+
- 避免在持有锁时调用外部函数
|
|
678
|
+
- 使用 defer 确保解锁
|
|
679
|
+
|
|
680
|
+
**示例**:
|
|
681
|
+
```go
|
|
682
|
+
type SafeCounter struct {
|
|
683
|
+
mu sync.Mutex
|
|
684
|
+
count int
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// ✅ 推荐:使用 defer 解锁
|
|
688
|
+
func (c *SafeCounter) Inc() {
|
|
689
|
+
c.mu.Lock()
|
|
690
|
+
defer c.mu.Unlock()
|
|
691
|
+
c.count++
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// ✅ 推荐:减小锁粒度
|
|
695
|
+
func (c *SafeCounter) GetAndReset() int {
|
|
696
|
+
c.mu.Lock()
|
|
697
|
+
count := c.count
|
|
698
|
+
c.count = 0
|
|
699
|
+
c.mu.Unlock()
|
|
700
|
+
|
|
701
|
+
// 在锁外执行耗时操作
|
|
702
|
+
log.Printf("counter was %d", count)
|
|
703
|
+
|
|
704
|
+
return count
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// ❌ 避免:锁粒度过大
|
|
708
|
+
func (c *SafeCounter) GetAndReset() int {
|
|
709
|
+
c.mu.Lock()
|
|
710
|
+
defer c.mu.Unlock()
|
|
711
|
+
|
|
712
|
+
count := c.count
|
|
713
|
+
c.count = 0
|
|
714
|
+
|
|
715
|
+
// 在持有锁时执行耗时操作(阻塞其他 goroutine)
|
|
716
|
+
log.Printf("counter was %d", count)
|
|
717
|
+
time.Sleep(100 * time.Millisecond)
|
|
718
|
+
|
|
719
|
+
return count
|
|
720
|
+
}
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
---
|
|
724
|
+
|
|
725
|
+
## 测试规范
|
|
726
|
+
|
|
727
|
+
### 测试文件组织
|
|
728
|
+
|
|
729
|
+
**规则**:
|
|
730
|
+
- 测试文件以 `_test.go` 结尾
|
|
731
|
+
- 测试函数以 `Test` 开头
|
|
732
|
+
- 使用表驱动测试
|
|
733
|
+
|
|
734
|
+
**示例**:
|
|
735
|
+
```go
|
|
736
|
+
// user.go
|
|
737
|
+
package user
|
|
738
|
+
|
|
739
|
+
func ValidateEmail(email string) bool {
|
|
740
|
+
// ...
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// user_test.go
|
|
744
|
+
package user
|
|
745
|
+
|
|
746
|
+
import "testing"
|
|
747
|
+
|
|
748
|
+
func TestValidateEmail(t *testing.T) {
|
|
749
|
+
tests := []struct {
|
|
750
|
+
name string
|
|
751
|
+
email string
|
|
752
|
+
want bool
|
|
753
|
+
}{
|
|
754
|
+
{
|
|
755
|
+
name: "valid email",
|
|
756
|
+
email: "test@example.com",
|
|
757
|
+
want: true,
|
|
758
|
+
},
|
|
759
|
+
{
|
|
760
|
+
name: "missing @",
|
|
761
|
+
email: "testexample.com",
|
|
762
|
+
want: false,
|
|
763
|
+
},
|
|
764
|
+
{
|
|
765
|
+
name: "empty email",
|
|
766
|
+
email: "",
|
|
767
|
+
want: false,
|
|
768
|
+
},
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
for _, tt := range tests {
|
|
772
|
+
t.Run(tt.name, func(t *testing.T) {
|
|
773
|
+
got := ValidateEmail(tt.email)
|
|
774
|
+
if got != tt.want {
|
|
775
|
+
t.Errorf("ValidateEmail(%q) = %v, want %v",
|
|
776
|
+
tt.email, got, tt.want)
|
|
777
|
+
}
|
|
778
|
+
})
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
### 测试覆盖率
|
|
784
|
+
|
|
785
|
+
**要求**:
|
|
786
|
+
- 单元测试覆盖率 ≥ 80%
|
|
787
|
+
- 关键业务逻辑覆盖率 ≥ 95%
|
|
788
|
+
|
|
789
|
+
**检查命令**:
|
|
790
|
+
```bash
|
|
791
|
+
# 运行测试并查看覆盖率
|
|
792
|
+
go test -cover ./...
|
|
793
|
+
|
|
794
|
+
# 生成覆盖率报告
|
|
795
|
+
go test -coverprofile=coverage.out ./...
|
|
796
|
+
go tool cover -html=coverage.out
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
---
|
|
800
|
+
|
|
801
|
+
## 性能优化
|
|
802
|
+
|
|
803
|
+
### 字符串拼接
|
|
804
|
+
|
|
805
|
+
**规则**:
|
|
806
|
+
- 少量拼接使用 `+`
|
|
807
|
+
- 循环中使用 `strings.Builder`
|
|
808
|
+
- 格式化使用 `fmt.Sprintf()`
|
|
809
|
+
|
|
810
|
+
**示例**:
|
|
811
|
+
```go
|
|
812
|
+
// ✅ 少量拼接
|
|
813
|
+
name := firstName + " " + lastName
|
|
814
|
+
|
|
815
|
+
// ✅ 循环中拼接
|
|
816
|
+
func JoinStrings(items []string) string {
|
|
817
|
+
var builder strings.Builder
|
|
818
|
+
for _, item := range items {
|
|
819
|
+
builder.WriteString(item)
|
|
820
|
+
builder.WriteString(",")
|
|
821
|
+
}
|
|
822
|
+
return builder.String()
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// ❌ 避免:循环中使用 +
|
|
826
|
+
func JoinStrings(items []string) string {
|
|
827
|
+
result := ""
|
|
828
|
+
for _, item := range items {
|
|
829
|
+
result += item + "," // 每次都会创建新字符串
|
|
830
|
+
}
|
|
831
|
+
return result
|
|
832
|
+
}
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
### Slice 使用
|
|
836
|
+
|
|
837
|
+
**规则**:
|
|
838
|
+
- 预分配容量避免多次扩容
|
|
839
|
+
- 注意 slice 共享底层数组的问题
|
|
840
|
+
|
|
841
|
+
**示例**:
|
|
842
|
+
```go
|
|
843
|
+
// ✅ 预分配容量
|
|
844
|
+
func ProcessItems(n int) []Item {
|
|
845
|
+
items := make([]Item, 0, n) // 预分配容量
|
|
846
|
+
for i := 0; i < n; i++ {
|
|
847
|
+
items = append(items, createItem(i))
|
|
848
|
+
}
|
|
849
|
+
return items
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
// ❌ 未预分配容量
|
|
853
|
+
func ProcessItems(n int) []Item {
|
|
854
|
+
var items []Item // 会多次扩容
|
|
855
|
+
for i := 0; i < n; i++ {
|
|
856
|
+
items = append(items, createItem(i))
|
|
857
|
+
}
|
|
858
|
+
return items
|
|
859
|
+
}
|
|
860
|
+
```
|
|
861
|
+
|
|
862
|
+
---
|
|
863
|
+
|
|
864
|
+
## 代码审查清单
|
|
865
|
+
|
|
866
|
+
### 提交前检查
|
|
867
|
+
|
|
868
|
+
**必须检查项**:
|
|
869
|
+
- [ ] 代码已格式化(`go fmt`)
|
|
870
|
+
- [ ] 通过 linter 检查(`golangci-lint run`)
|
|
871
|
+
- [ ] 所有测试通过(`go test ./...`)
|
|
872
|
+
- [ ] 测试覆盖率达标(≥ 80%)
|
|
873
|
+
- [ ] 没有 race condition(`go test -race ./...`)
|
|
874
|
+
- [ ] 注释使用正确的标记(`//` vs `//;`)
|
|
875
|
+
- [ ] 所有 `//;@TODO` 和 `//;@FIXME` 都已记录
|
|
876
|
+
|
|
877
|
+
**推荐检查项**:
|
|
878
|
+
- [ ] 函数长度 ≤ 50 行
|
|
879
|
+
- [ ] 圈复杂度 ≤ 10
|
|
880
|
+
- [ ] 没有重复代码
|
|
881
|
+
- [ ] 错误处理完整
|
|
882
|
+
- [ ] 没有明显的性能问题
|
|
883
|
+
|
|
884
|
+
---
|
|
885
|
+
|
|
886
|
+
## 工具配置
|
|
887
|
+
|
|
888
|
+
### golangci-lint 配置
|
|
889
|
+
|
|
890
|
+
创建 `.golangci.yml`:
|
|
891
|
+
```yaml
|
|
892
|
+
linters:
|
|
893
|
+
enable:
|
|
894
|
+
- gofmt
|
|
895
|
+
- goimports
|
|
896
|
+
- govet
|
|
897
|
+
- errcheck
|
|
898
|
+
- staticcheck
|
|
899
|
+
- unused
|
|
900
|
+
- gosimple
|
|
901
|
+
- ineffassign
|
|
902
|
+
- deadcode
|
|
903
|
+
- typecheck
|
|
904
|
+
- gocyclo
|
|
905
|
+
- funlen
|
|
906
|
+
- gocognit
|
|
907
|
+
|
|
908
|
+
linters-settings:
|
|
909
|
+
gocyclo:
|
|
910
|
+
min-complexity: 10
|
|
911
|
+
funlen:
|
|
912
|
+
lines: 50
|
|
913
|
+
statements: 40
|
|
914
|
+
errcheck:
|
|
915
|
+
check-blank: true
|
|
916
|
+
govet:
|
|
917
|
+
check-shadowing: true
|
|
918
|
+
|
|
919
|
+
issues:
|
|
920
|
+
exclude-rules:
|
|
921
|
+
- path: _test\.go
|
|
922
|
+
linters:
|
|
923
|
+
- funlen
|
|
924
|
+
- gocyclo
|
|
925
|
+
```
|
|
926
|
+
|
|
927
|
+
### VS Code 配置
|
|
928
|
+
|
|
929
|
+
创建 `.vscode/settings.json`:
|
|
930
|
+
```json
|
|
931
|
+
{
|
|
932
|
+
"go.lintTool": "golangci-lint",
|
|
933
|
+
"go.lintOnSave": "package",
|
|
934
|
+
"go.formatTool": "gofmt",
|
|
935
|
+
"editor.formatOnSave": true,
|
|
936
|
+
"go.testOnSave": false,
|
|
937
|
+
"go.coverOnSave": false
|
|
938
|
+
}
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
---
|
|
942
|
+
|
|
943
|
+
## 总结
|
|
944
|
+
|
|
945
|
+
本编程标准的核心要点:
|
|
946
|
+
|
|
947
|
+
1. **注释区分**:
|
|
948
|
+
- `//` 用于代码功能注释
|
|
949
|
+
- `//;` 用于 TODO、FIXME、DEBUG 等元信息
|
|
950
|
+
|
|
951
|
+
2. **代码质量**:
|
|
952
|
+
- 函数长度 ≤ 50 行
|
|
953
|
+
- 圈复杂度 ≤ 10
|
|
954
|
+
- 测试覆盖率 ≥ 80%
|
|
955
|
+
|
|
956
|
+
3. **错误处理**:
|
|
957
|
+
- 使用 `fmt.Errorf()` 包装错误
|
|
958
|
+
- 及早返回,避免嵌套
|
|
959
|
+
|
|
960
|
+
4. **并发安全**:
|
|
961
|
+
- 使用 context 控制生命周期
|
|
962
|
+
- 避免 goroutine 泄漏
|
|
963
|
+
- 正确使用 mutex
|
|
964
|
+
|
|
965
|
+
5. **测试要求**:
|
|
966
|
+
- 使用表驱动测试
|
|
967
|
+
- 确保测试覆盖率
|
|
968
|
+
- 运行 race detector
|
|
969
|
+
|
|
970
|
+
**参考资源**:
|
|
971
|
+
- [Effective Go](https://golang.org/doc/effective_go)
|
|
972
|
+
- [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments)
|
|
973
|
+
- [Uber Go Style Guide](https://github.com/uber-go/guide)
|