-
Notifications
You must be signed in to change notification settings - Fork 0
/
user.go
152 lines (131 loc) · 3.78 KB
/
user.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package repository
import (
"context"
"database/sql"
"github.com/gevinzone/basic-go/week5/webook/internal/domain"
"github.com/gevinzone/basic-go/week5/webook/internal/repository/cache"
"github.com/gevinzone/basic-go/week5/webook/internal/repository/dao"
"time"
)
var (
ErrUserDuplicate = dao.ErrUserDuplicate
ErrUserNotFound = dao.ErrUserNotFound
)
// UserRepository 是核心,它有不同实现。
// 但是 Factory 本身如果只是初始化一下,那么它不是你的核心
type UserRepository interface {
FindByEmail(ctx context.Context, email string) (domain.User, error)
FindByPhone(ctx context.Context, phone string) (domain.User, error)
Create(ctx context.Context, u domain.User) error
FindById(ctx context.Context, id int64) (domain.User, error)
FindByWechat(ctx context.Context, openID string) (domain.User, error)
}
type CachedUserRepository struct {
dao dao.UserDAO
cache cache.UserCache
//testSignal chan struct{}
}
func NewUserRepository(dao dao.UserDAO, c cache.UserCache) UserRepository {
return &CachedUserRepository{
dao: dao,
cache: c,
}
}
func (r *CachedUserRepository) FindByWechat(ctx context.Context, openID string) (domain.User, error) {
u, err := r.dao.FindByWechat(ctx, openID)
if err != nil {
return domain.User{}, err
}
return r.entityToDomain(u), nil
}
func (r *CachedUserRepository) FindByEmail(ctx context.Context, email string) (domain.User, error) {
// SELECT * FROM `users` WHERE `email`=?
u, err := r.dao.FindByEmail(ctx, email)
if err != nil {
return domain.User{}, err
}
return r.entityToDomain(u), nil
}
func (r *CachedUserRepository) FindByPhone(ctx context.Context, phone string) (domain.User, error) {
u, err := r.dao.FindByPhone(ctx, phone)
if err != nil {
return domain.User{}, err
}
return r.entityToDomain(u), nil
}
func (r *CachedUserRepository) Create(ctx context.Context, u domain.User) error {
return r.dao.Insert(ctx, r.domainToEntity(u))
}
func (r *CachedUserRepository) FindById(ctx context.Context, id int64) (domain.User, error) {
u, err := r.cache.Get(ctx, id)
if err == nil {
// 必然是有数据
return u, nil
}
// 没这个数据
//if err == cache.ErrKeyNotExist {
// 去数据库里面加载
//}
ue, err := r.dao.FindById(ctx, id)
if err != nil {
return domain.User{}, err
}
u = r.entityToDomain(ue)
//if err != nil {
// 我这里怎么办?
// 打日志,做监控
//return domain.User{}, err
//}
go func() {
_ = r.cache.Set(ctx, u)
//r.testSignal <- struct{}{}
}()
return u, nil
// 这里怎么办? err = io.EOF
// 要不要去数据库加载?
// 看起来我不应该加载?
// 看起来我好像也要加载?
// 选加载 —— 做好兜底,万一 Redis 真的崩了,你要保护住你的数据库
// 我数据库限流呀!
// 选不加载 —— 用户体验差一点
// 缓存里面有数据
// 缓存里面没有数据
// 缓存出错了,你也不知道有没有数据
}
func (r *CachedUserRepository) domainToEntity(u domain.User) dao.User {
return dao.User{
Id: u.Id,
Email: sql.NullString{
String: u.Email,
// 我确实有手机号
Valid: u.Email != "",
},
Phone: sql.NullString{
String: u.Phone,
Valid: u.Phone != "",
},
Password: u.Password,
WechatOpenID: sql.NullString{
String: u.WechatInfo.OpenID,
Valid: u.WechatInfo.OpenID != "",
},
WechatUnionID: sql.NullString{
String: u.WechatInfo.UnionID,
Valid: u.WechatInfo.UnionID != "",
},
Ctime: u.Ctime.UnixMilli(),
}
}
func (r *CachedUserRepository) entityToDomain(u dao.User) domain.User {
return domain.User{
Id: u.Id,
Email: u.Email.String,
Password: u.Password,
Phone: u.Phone.String,
WechatInfo: domain.WechatInfo{
UnionID: u.WechatUnionID.String,
OpenID: u.WechatOpenID.String,
},
Ctime: time.UnixMilli(u.Ctime),
}
}