Skip to content

Commit 52543d4

Browse files
committed
feat(错误处理): 实现国际化错误处理框架
refactor(仓库层): 将硬编码错误替换为预定义错误类型 docs: 添加国际化错误处理示例文档 test: 添加错误处理和仓库层测试用例 refactor(路由): 优化路由初始化逻辑 style: 统一JSON标签格式 chore: 更新依赖项
1 parent 67dbf44 commit 52543d4

File tree

18 files changed

+1436
-101
lines changed

18 files changed

+1436
-101
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,6 @@ cache/
171171
# Test files
172172
test/
173173
tests/
174-
*_test.go
175174

176175
# Documentation
177176
docs/build/

Makefile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ dev: # 开发运行
6464
test: # 运行测试
6565
@go test -v ./...
6666

67+
.PHONY: test-all
68+
test-all: # 运行所有测试用例(包括单元测试和基准测试)
69+
@echo "运行所有单元测试..."
70+
@go test -v ./...
71+
@echo "\n运行基准测试..."
72+
@go test -bench=. -v ./...
73+
@echo "\n测试完成!"
74+
6775
.PHONY: test-coverage
6876
test-coverage: # 运行测试并生成覆盖率报告
6977
@go test -v -coverprofile=coverage.out ./...
@@ -135,6 +143,7 @@ help: # 显示帮助信息
135143
@echo " run - 开发运行"
136144
@echo " dev - 开发运行"
137145
@echo " test - 运行测试"
146+
@echo " test-all - 运行所有测试用例(包括单元测试和基准测试)"
138147
@echo " test-coverage- 运行测试并生成覆盖率报告"
139148
@echo " lint - 代码检查"
140149
@echo " fmt - 格式化代码"

docs/docs.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -708,14 +708,14 @@ const docTemplate = `{
708708
}
709709
}
710710
},
711-
"/admin/delete/batch": {
711+
"/admin/delete": {
712712
"delete": {
713713
"security": [
714714
{
715715
"BearerAuth": []
716716
}
717717
],
718-
"description": "根据管理员ID列表批量删除管理员",
718+
"description": "根据用户ID批量删除用户",
719719
"consumes": [
720720
"application/json"
721721
],
@@ -725,10 +725,10 @@ const docTemplate = `{
725725
"tags": [
726726
"管理员管理"
727727
],
728-
"summary": "批量删除管理员",
728+
"summary": "批量删除用户",
729729
"parameters": [
730730
{
731-
"description": "删除管理员请求",
731+
"description": "删除用户请求",
732732
"name": "request",
733733
"in": "body",
734734
"required": true,
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# 国际化错误处理示例
2+
3+
本文档展示如何在Controller层处理Repository返回的国际化错误。
4+
5+
## 在Controller中处理Repository错误
6+
7+
### 方法1:直接处理RepositoryError
8+
9+
```go
10+
func (ac *AuthController) Login(c *gin.Context) {
11+
var u model.Admin
12+
if err := c.ShouldBindJSON(&u); err != nil {
13+
response.ParamError(c, err.Error())
14+
return
15+
}
16+
17+
admin, err := ac.AdminRepository.Login(u)
18+
if err != nil {
19+
// 检查是否为RepositoryError
20+
if repoErr, ok := err.(*common.RepositoryError); ok {
21+
// 获取本地化错误消息
22+
localizedMsg := repoErr.GetLocalizedMessage(c)
23+
24+
// 根据错误类型返回不同的HTTP状态码
25+
switch repoErr.Code {
26+
case "user_not_found", "password_incorrect":
27+
response.PasswordIncorrect(c, localizedMsg)
28+
case "user_disabled", "user_role_disabled":
29+
response.Forbidden(c, localizedMsg)
30+
case "user_not_logged_in":
31+
response.Unauthorized(c, localizedMsg)
32+
default:
33+
response.InternalServerError(c, localizedMsg)
34+
}
35+
return
36+
}
37+
38+
// 处理其他类型的错误
39+
response.InternalServerError(c, err.Error())
40+
return
41+
}
42+
43+
// 成功处理...
44+
}
45+
```
46+
47+
### 方法2:创建通用错误处理函数
48+
49+
```go
50+
// 在controller包中创建通用错误处理函数
51+
func HandleRepositoryError(c *gin.Context, err error) bool {
52+
if err == nil {
53+
return false
54+
}
55+
56+
// 检查是否为RepositoryError
57+
if repoErr, ok := err.(*common.RepositoryError); ok {
58+
localizedMsg := repoErr.GetLocalizedMessage(c)
59+
60+
switch repoErr.Code {
61+
case "user_not_found", "password_incorrect":
62+
response.PasswordIncorrect(c, localizedMsg)
63+
case "user_disabled", "user_role_disabled":
64+
response.Forbidden(c, localizedMsg)
65+
case "user_not_logged_in":
66+
response.Unauthorized(c, localizedMsg)
67+
case "no_users_found", "role_info_failed", "no_users_with_role":
68+
response.NotFound(c, localizedMsg)
69+
default:
70+
response.InternalServerError(c, localizedMsg)
71+
}
72+
return true
73+
}
74+
75+
// 处理其他类型的错误
76+
response.InternalServerError(c, err.Error())
77+
return true
78+
}
79+
80+
// 在Controller中使用
81+
func (ac *AuthController) Login(c *gin.Context) {
82+
var u model.Admin
83+
if err := c.ShouldBindJSON(&u); err != nil {
84+
response.ParamError(c, err.Error())
85+
return
86+
}
87+
88+
admin, err := ac.AdminRepository.Login(u)
89+
if HandleRepositoryError(c, err) {
90+
return
91+
}
92+
93+
// 成功处理...
94+
}
95+
```
96+
97+
## 支持的错误类型
98+
99+
| 错误代码 | 中文消息 | 英文消息 | 建议HTTP状态码 |
100+
|---------|---------|---------|---------------|
101+
| `user_not_found` | 用户不存在 | User not found | 401 |
102+
| `user_disabled` | 用户被禁用 | User is disabled | 403 |
103+
| `user_role_disabled` | 用户角色被禁用 | User role is disabled | 403 |
104+
| `password_incorrect` | 密码错误 | Incorrect password | 401 |
105+
| `user_not_logged_in` | 用户未登录 | User not logged in | 401 |
106+
| `user_not_found_by_id` | 未获取到ID为%d的用户 | User not found by ID %d | 404 |
107+
| `no_users_found` | 未获取到任何用户信息 | No user information found | 404 |
108+
| `role_info_failed` | 根据角色ID获取角色信息失败 | Failed to get role information by role ID | 500 |
109+
| `no_users_with_role` | 根据角色ID未获取到拥有该角色的用户 | No users with this role found by role ID | 404 |
110+
111+
## 语言检测
112+
113+
系统通过以下方式检测用户语言偏好:
114+
115+
1. 检查Gin Context中的`lang`字段
116+
2. 如果未设置,默认使用中文(`zh`)
117+
3. 支持的语言:`zh`(中文)、`en`(英文)
118+
119+
## 性能考虑
120+
121+
- 错误消息本地化性能:约20ns/op
122+
- 错误对象创建性能:约0.2ns/op
123+
- 建议在高频调用场景中缓存错误对象

docs/swagger.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -702,14 +702,14 @@
702702
}
703703
}
704704
},
705-
"/admin/delete/batch": {
705+
"/admin/delete": {
706706
"delete": {
707707
"security": [
708708
{
709709
"BearerAuth": []
710710
}
711711
],
712-
"description": "根据管理员ID列表批量删除管理员",
712+
"description": "根据用户ID批量删除用户",
713713
"consumes": [
714714
"application/json"
715715
],
@@ -719,10 +719,10 @@
719719
"tags": [
720720
"管理员管理"
721721
],
722-
"summary": "批量删除管理员",
722+
"summary": "批量删除用户",
723723
"parameters": [
724724
{
725-
"description": "删除管理员请求",
725+
"description": "删除用户请求",
726726
"name": "request",
727727
"in": "body",
728728
"required": true,

docs/swagger.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1830,13 +1830,13 @@ paths:
18301830
summary: 创建管理员
18311831
tags:
18321832
- 管理员管理
1833-
/admin/delete/batch:
1833+
/admin/delete:
18341834
delete:
18351835
consumes:
18361836
- application/json
1837-
description: 根据管理员ID列表批量删除管理员
1837+
description: 根据用户ID批量删除用户
18381838
parameters:
1839-
- description: 删除管理员请求
1839+
- description: 删除用户请求
18401840
in: body
18411841
name: request
18421842
required: true
@@ -1855,7 +1855,7 @@ paths:
18551855
$ref: '#/definitions/response.Response'
18561856
security:
18571857
- BearerAuth: []
1858-
summary: 批量删除管理员
1858+
summary: 批量删除用户
18591859
tags:
18601860
- 管理员管理
18611861
/admin/info:

go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ require (
2222
github.com/qiniu/go-sdk/v7 v7.25.4
2323
github.com/robfig/cron/v3 v3.0.1
2424
github.com/spf13/viper v1.21.0
25+
github.com/stretchr/testify v1.11.1
2526
github.com/swaggo/files v1.0.1
2627
github.com/swaggo/gin-swagger v1.6.1
2728
github.com/swaggo/swag v1.16.6
@@ -45,6 +46,7 @@ require (
4546
github.com/bytedance/sonic/loader v0.4.0 // indirect
4647
github.com/casbin/govaluate v1.10.0 // indirect
4748
github.com/cloudwego/base64x v0.1.6 // indirect
49+
github.com/davecgh/go-spew v1.1.1 // indirect
4850
github.com/dustin/go-humanize v1.0.1 // indirect
4951
github.com/gabriel-vasile/mimetype v1.4.11 // indirect
5052
github.com/gammazero/toposort v0.1.1 // indirect
@@ -87,6 +89,7 @@ require (
8789
github.com/ncruces/go-strftime v1.0.0 // indirect
8890
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
8991
github.com/pkg/errors v0.9.1 // indirect
92+
github.com/pmezard/go-difflib v1.0.0 // indirect
9093
github.com/quic-go/qpack v0.5.1 // indirect
9194
github.com/quic-go/quic-go v0.55.0 // indirect
9295
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
@@ -95,6 +98,7 @@ require (
9598
github.com/spf13/afero v1.15.0 // indirect
9699
github.com/spf13/cast v1.10.0 // indirect
97100
github.com/spf13/pflag v1.0.10 // indirect
101+
github.com/stretchr/objx v0.5.2 // indirect
98102
github.com/subosito/gotenv v1.6.0 // indirect
99103
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
100104
github.com/ugorji/go/codec v1.3.1 // indirect
@@ -112,6 +116,7 @@ require (
112116
golang.org/x/time v0.14.0 // indirect
113117
golang.org/x/tools v0.38.0 // indirect
114118
google.golang.org/protobuf v1.36.10 // indirect
119+
gopkg.in/yaml.v3 v3.0.1 // indirect
115120
gorm.io/driver/sqlserver v1.6.1 // indirect
116121
gorm.io/plugin/dbresolver v1.6.2 // indirect
117122
modernc.org/fileutil v1.3.40 // indirect

go.sum

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjb
270270
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
271271
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
272272
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
273+
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
273274
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
274275
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
275276
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=

internal/app/repository/admin_repository.go

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
package repository
77

88
import (
9-
"errors"
109
"fmt"
1110
"gotribe-admin/internal/pkg/common"
1211
"gotribe-admin/internal/pkg/model"
@@ -60,13 +59,13 @@ func (ar AdminRepository) Login(admin *model.Admin) (*model.Admin, error) {
6059
Preload("Roles").
6160
First(&firstAdmin).Error
6261
if err != nil {
63-
return nil, errors.New("用户不存在")
62+
return nil, common.ErrUserNotFound
6463
}
6564

6665
// 判断用户的状态
6766
adminStatus := firstAdmin.Status
6867
if adminStatus != 1 {
69-
return nil, errors.New("用户被禁用")
68+
return nil, common.ErrUserDisabled
7069
}
7170

7271
// 判断用户拥有的所有角色的状态,全部角色都被禁用则不能登录
@@ -81,13 +80,13 @@ func (ar AdminRepository) Login(admin *model.Admin) (*model.Admin, error) {
8180
}
8281

8382
if !isValidate {
84-
return nil, errors.New("用户角色被禁用")
83+
return nil, common.ErrUserRoleDisabled
8584
}
8685

8786
// 校验密码
8887
err = util.PasswordUtil.ComparePasswd(firstAdmin.Password, admin.Password)
8988
if err != nil {
90-
return &firstAdmin, errors.New("密码错误")
89+
return &firstAdmin, common.ErrPasswordIncorrect
9190
}
9291
return &firstAdmin, nil
9392
}
@@ -98,7 +97,7 @@ func (ar AdminRepository) GetCurrentAdmin(c *gin.Context) (model.Admin, error) {
9897
var newAdmin model.Admin
9998
ctxAdmin, exist := c.Get("user")
10099
if !exist {
101-
return newAdmin, errors.New("用户未登录")
100+
return newAdmin, common.ErrUserNotLoggedIn
102101
}
103102
u, _ := ctxAdmin.(model.Admin)
104103

@@ -240,7 +239,7 @@ func (ar AdminRepository) BatchDeleteAdminByIds(ids []uint) error {
240239
// 根据ID获取用户
241240
admin, err := ar.GetAdminByID(id)
242241
if err != nil {
243-
return errors.New(fmt.Sprintf("未获取到ID为%d的用户", id))
242+
return common.NewUserNotFoundByIDError(id)
244243
}
245244
admins = append(admins, admin)
246245
}
@@ -264,7 +263,7 @@ func (ar AdminRepository) GetAdminMinRoleSortsByIds(ids []uint) ([]int, error) {
264263
return []int{}, err
265264
}
266265
if len(adminList) == 0 {
267-
return []int{}, errors.New("未获取到任何用户信息")
266+
return []int{}, common.ErrNoUsersFound
268267
}
269268
var roleMinSortList []int
270269
for _, admin := range adminList {
@@ -290,12 +289,12 @@ func (ar AdminRepository) UpdateAdminInfoCacheByRoleID(roleID uint) error {
290289
var role model.Role
291290
err := common.DB.Where("id = ?", roleID).Preload("Admins").First(&role).Error
292291
if err != nil {
293-
return errors.New("根据角色ID角色信息失败")
292+
return common.ErrRoleInfoFailed
294293
}
295294

296295
admins := role.Admin
297296
if len(admins) == 0 {
298-
return errors.New("根据角色ID未获取到拥有该角色的用户")
297+
return common.ErrNoUsersWithRole
299298
}
300299

301300
// 更新用户信息缓存

0 commit comments

Comments
 (0)