f573edba098dd494fdd1fd3b5e9923ebcdd42db4
\345\271\263\345\217\260\345\220\210app\346\216\210\346\235\203/\345\237\272\344\272\216gw\347\232\204\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260.md
... | ... | @@ -0,0 +1,397 @@ |
1 | +我来详细分析这个授权过期提醒策略需求,并提供完整的Go语言实现方案。 |
|
2 | + |
|
3 | +## 需求分析 |
|
4 | + |
|
5 | +### 核心目标 |
|
6 | +1. **完善授权到期前后的提醒机制** |
|
7 | +2. **分阶段提醒**:到期前60天、30天、15天、到期后、超期7天 |
|
8 | +3. **差异化提醒对象**:平台管理员/租户管理员 vs 所有用户 |
|
9 | +4. **不同触发频率**:每月、每天、每次登录、每个请求 |
|
10 | +5. **提醒不阻塞业务**(除超期后的概率异常) |
|
11 | + |
|
12 | +### 策略阶段分析 |
|
13 | +| 阶段 | 时间区间 | 提醒对象 | 触发条件 | 业务影响 | |
|
14 | +|------|----------|----------|----------|----------| |
|
15 | +| 阶段1 | 到期前60天开始 | 平台/租户管理员 | 每月一次,登录时 | 不阻塞 | |
|
16 | +| 阶段2 | 到期前30天开始 | 平台/租户管理员 | 每天一次,登录时 | 不阻塞 | |
|
17 | +| 阶段3 | 到期前15天开始 | 平台/租户管理员 | 每次登录时 | 不阻塞 | |
|
18 | +| 阶段4 | 到期后0天开始 | 所有用户 + 平台/租户管理员 | 所有请求 + 登录时 | 概率阻塞 | |
|
19 | +| 阶段5 | 超期7天开始 | 所有用户 + 平台/租户管理员 | 所有请求 + 登录时 | 概率阻塞 | |
|
20 | + |
|
21 | +## Go语言实现方案 |
|
22 | + |
|
23 | +### 1. 数据结构定义 |
|
24 | + |
|
25 | +```go |
|
26 | +package main |
|
27 | + |
|
28 | +import ( |
|
29 | + "fmt" |
|
30 | + "time" |
|
31 | + "math/rand" |
|
32 | +) |
|
33 | + |
|
34 | +// 许可证信息 |
|
35 | +type License struct { |
|
36 | + ExpireTime time.Time |
|
37 | + IsValid bool |
|
38 | + LicenseType string |
|
39 | +} |
|
40 | + |
|
41 | +// 用户信息 |
|
42 | +type User struct { |
|
43 | + ID string |
|
44 | + Username string |
|
45 | + Role UserRole |
|
46 | + LastRemind map[RemindStage]time.Time |
|
47 | +} |
|
48 | + |
|
49 | +// 用户角色 |
|
50 | +type UserRole int |
|
51 | + |
|
52 | +const ( |
|
53 | + RoleNormalUser UserRole = iota |
|
54 | + RoleTenantAdmin |
|
55 | + RolePlatformAdmin |
|
56 | +) |
|
57 | + |
|
58 | +// 提醒阶段 |
|
59 | +type RemindStage int |
|
60 | + |
|
61 | +const ( |
|
62 | + Stage60DaysBefore RemindStage = iota |
|
63 | + Stage30DaysBefore |
|
64 | + Stage15DaysBefore |
|
65 | + StageAfterExpire |
|
66 | + Stage7DaysAfterExpire |
|
67 | +) |
|
68 | + |
|
69 | +// 提醒策略配置 |
|
70 | +type RemindStrategy struct { |
|
71 | + Stage RemindStage |
|
72 | + DaysFromExpire int // 正数表示到期前,负数表示到期后 |
|
73 | + TargetUsers []UserRole |
|
74 | + TriggerCondition string |
|
75 | + Frequency time.Duration |
|
76 | + MessageTemplate string |
|
77 | + BlockBusiness bool |
|
78 | + ExceptionRate float64 // 异常抛出概率(0-1) |
|
79 | +} |
|
80 | + |
|
81 | +// 提醒记录 |
|
82 | +type RemindRecord struct { |
|
83 | + UserID string |
|
84 | + Stage RemindStage |
|
85 | + RemindTime time.Time |
|
86 | + Message string |
|
87 | + IsShown bool |
|
88 | +} |
|
89 | +``` |
|
90 | + |
|
91 | +### 2. 策略配置管理 |
|
92 | + |
|
93 | +```go |
|
94 | +// 策略管理器 |
|
95 | +type RemindStrategyManager struct { |
|
96 | + strategies map[RemindStage]*RemindStrategy |
|
97 | + license *License |
|
98 | + remindHistory map[string][]*RemindRecord // userID -> records |
|
99 | +} |
|
100 | + |
|
101 | +func NewRemindStrategyManager(license *License) *RemindStrategyManager { |
|
102 | + manager := &RemindStrategyManager{ |
|
103 | + license: license, |
|
104 | + strategies: make(map[RemindStage]*RemindStrategy), |
|
105 | + remindHistory: make(map[string][]*RemindRecord), |
|
106 | + } |
|
107 | + manager.initStrategies() |
|
108 | + return manager |
|
109 | +} |
|
110 | + |
|
111 | +func (m *RemindStrategyManager) initStrategies() { |
|
112 | + // 阶段1: 到期前60天开始 |
|
113 | + m.strategies[Stage60DaysBefore] = &RemindStrategy{ |
|
114 | + Stage: Stage60DaysBefore, |
|
115 | + DaysFromExpire: 60, |
|
116 | + TargetUsers: []UserRole{RolePlatformAdmin, RoleTenantAdmin}, |
|
117 | + TriggerCondition: "登录时", |
|
118 | + Frequency: time.Hour * 24 * 30, // 每月一次 |
|
119 | + MessageTemplate: "平台许可证将在%d天后,%s到期,为避免到期后系统不能正常使用而影响业务,请在到期前及时进行许可证续期。", |
|
120 | + BlockBusiness: false, |
|
121 | + ExceptionRate: 0, |
|
122 | + } |
|
123 | + |
|
124 | + // 阶段2: 到期前30天开始 |
|
125 | + m.strategies[Stage30DaysBefore] = &RemindStrategy{ |
|
126 | + Stage: Stage30DaysBefore, |
|
127 | + DaysFromExpire: 30, |
|
128 | + TargetUsers: []UserRole{RolePlatformAdmin, RoleTenantAdmin}, |
|
129 | + TriggerCondition: "登录时", |
|
130 | + Frequency: time.Hour * 24, // 每天一次 |
|
131 | + MessageTemplate: "平台许可证将在%d天后,%s到期,为避免到期后系统不能正常使用而影响业务,请在到期前及时进行许可证续期。", |
|
132 | + BlockBusiness: false, |
|
133 | + ExceptionRate: 0, |
|
134 | + } |
|
135 | + |
|
136 | + // 阶段3: 到期前15天开始 |
|
137 | + m.strategies[Stage15DaysBefore] = &RemindStrategy{ |
|
138 | + Stage: Stage15DaysBefore, |
|
139 | + DaysFromExpire: 15, |
|
140 | + TargetUsers: []UserRole{RolePlatformAdmin, RoleTenantAdmin}, |
|
141 | + TriggerCondition: "登录时", |
|
142 | + Frequency: time.Hour * 1, // 每次登录(按小时频率) |
|
143 | + MessageTemplate: "平台许可证将在%d天后,%s到期,为避免到期后系统不能正常使用而影响业务,请在到期前及时进行许可证续期。", |
|
144 | + BlockBusiness: false, |
|
145 | + ExceptionRate: 0, |
|
146 | + } |
|
147 | + |
|
148 | + // 阶段4: 到期后 |
|
149 | + m.strategies[StageAfterExpire] = &RemindStrategy{ |
|
150 | + Stage: StageAfterExpire, |
|
151 | + DaysFromExpire: 0, |
|
152 | + TargetUsers: []UserRole{RolePlatformAdmin, RoleTenantAdmin, RoleNormalUser}, |
|
153 | + TriggerCondition: "所有请求", |
|
154 | + Frequency: time.Hour * 1, |
|
155 | + MessageTemplate: "平台许可证已于%s到期,请在7天内完成许可证续期,否则系统将在%s后不能正常使用!", |
|
156 | + BlockBusiness: true, |
|
157 | + ExceptionRate: 0.3, // 30%概率抛异常 |
|
158 | + } |
|
159 | + |
|
160 | + // 阶段5: 超期7天开始 |
|
161 | + m.strategies[Stage7DaysAfterExpire] = &RemindStrategy{ |
|
162 | + Stage: Stage7DaysAfterExpire, |
|
163 | + DaysFromExpire: -7, |
|
164 | + TargetUsers: []UserRole{RolePlatformAdmin, RoleTenantAdmin, RoleNormalUser}, |
|
165 | + TriggerCondition: "所有请求", |
|
166 | + Frequency: time.Hour * 1, |
|
167 | + MessageTemplate: "平台许可证已过期,系统不能正常使用,请联系您的系统管理员进行处理!", |
|
168 | + BlockBusiness: true, |
|
169 | + ExceptionRate: 0.5, // 50%概率抛异常 |
|
170 | + } |
|
171 | +} |
|
172 | +``` |
|
173 | + |
|
174 | +### 3. 核心业务逻辑 |
|
175 | + |
|
176 | +```go |
|
177 | +// 获取当前提醒阶段 |
|
178 | +func (m *RemindStrategyManager) getCurrentStage() RemindStage { |
|
179 | + now := time.Now() |
|
180 | + daysUntilExpire := int(m.license.ExpireTime.Sub(now).Hours() / 24) |
|
181 | + |
|
182 | + switch { |
|
183 | + case daysUntilExpire >= 60: |
|
184 | + return Stage60DaysBefore |
|
185 | + case daysUntilExpire >= 30: |
|
186 | + return Stage30DaysBefore |
|
187 | + case daysUntilExpire >= 15: |
|
188 | + return Stage15DaysBefore |
|
189 | + case daysUntilExpire >= 0: |
|
190 | + return StageAfterExpire |
|
191 | + default: |
|
192 | + if daysUntilExpire <= -7 { |
|
193 | + return Stage7DaysAfterExpire |
|
194 | + } |
|
195 | + return StageAfterExpire |
|
196 | + } |
|
197 | +} |
|
198 | + |
|
199 | +// 检查用户是否需要提醒 |
|
200 | +func (m *RemindStrategyManager) shouldRemindUser(user *User, stage RemindStage, triggerType string) bool { |
|
201 | + strategy, exists := m.strategies[stage] |
|
202 | + if !exists { |
|
203 | + return false |
|
204 | + } |
|
205 | + |
|
206 | + // 检查触发条件 |
|
207 | + if triggerType != strategy.TriggerCondition && strategy.TriggerCondition != "所有请求" { |
|
208 | + return false |
|
209 | + } |
|
210 | + |
|
211 | + // 检查用户角色 |
|
212 | + userInTarget := false |
|
213 | + for _, role := range strategy.TargetUsers { |
|
214 | + if user.Role == role { |
|
215 | + userInTarget = true |
|
216 | + break |
|
217 | + } |
|
218 | + } |
|
219 | + if !userInTarget { |
|
220 | + return false |
|
221 | + } |
|
222 | + |
|
223 | + // 检查频率限制 |
|
224 | + lastRemind, exists := user.LastRemind[stage] |
|
225 | + if exists && time.Since(lastRemind) < strategy.Frequency { |
|
226 | + return false |
|
227 | + } |
|
228 | + |
|
229 | + return true |
|
230 | +} |
|
231 | + |
|
232 | +// 生成提醒消息 |
|
233 | +func (m *RemindStrategyManager) generateMessage(stage RemindStage, user *User) string { |
|
234 | + strategy := m.strategies[stage] |
|
235 | + expireTimeStr := m.license.ExpireTime.Format("2006年01月02日 15:04:05") |
|
236 | + daysUntil := int(m.license.ExpireTime.Sub(time.Now()).Hours() / 24) |
|
237 | + |
|
238 | + switch stage { |
|
239 | + case Stage60DaysBefore, Stage30DaysBefore, Stage15DaysBefore: |
|
240 | + return fmt.Sprintf(strategy.MessageTemplate, daysUntil, expireTimeStr) |
|
241 | + case StageAfterExpire: |
|
242 | + deadline := m.license.ExpireTime.AddDate(0, 0, 7) |
|
243 | + return fmt.Sprintf(strategy.MessageTemplate, expireTimeStr, deadline.Format("2006年01月02日 15:04:05")) |
|
244 | + case Stage7DaysAfterExpire: |
|
245 | + if user.Role == RoleNormalUser { |
|
246 | + return "平台许可证已过期,系统不能正常使用,请联系您的系统管理员进行处理!" |
|
247 | + } else { |
|
248 | + return fmt.Sprintf("平台许可证已于%s到期,经多次提醒贵司未能在宽限期内完成许可证续期,系统已不能正常使用!", expireTimeStr) |
|
249 | + } |
|
250 | + default: |
|
251 | + return "许可证状态异常,请联系管理员。" |
|
252 | + } |
|
253 | +} |
|
254 | + |
|
255 | +// 处理业务阻塞逻辑 |
|
256 | +func (m *RemindStrategyManager) checkBusinessBlock(stage RemindStage) error { |
|
257 | + strategy := m.strategies[stage] |
|
258 | + if strategy.BlockBusiness && rand.Float64() < strategy.ExceptionRate { |
|
259 | + return fmt.Errorf("许可证已过期,系统不能正常使用") |
|
260 | + } |
|
261 | + return nil |
|
262 | +} |
|
263 | +``` |
|
264 | + |
|
265 | +### 4. API接口实现 |
|
266 | + |
|
267 | +```go |
|
268 | +// 登录提醒处理 |
|
269 | +func (m *RemindStrategyManager) HandleLoginRemind(user *User) (*RemindRecord, error) { |
|
270 | + stage := m.getCurrentStage() |
|
271 | + |
|
272 | + if !m.shouldRemindUser(user, stage, "登录时") { |
|
273 | + return nil, nil |
|
274 | + } |
|
275 | + |
|
276 | + // 检查业务阻塞 |
|
277 | + if err := m.checkBusinessBlock(stage); err != nil { |
|
278 | + return nil, err |
|
279 | + } |
|
280 | + |
|
281 | + message := m.generateMessage(stage, user) |
|
282 | + record := &RemindRecord{ |
|
283 | + UserID: user.ID, |
|
284 | + Stage: stage, |
|
285 | + RemindTime: time.Now(), |
|
286 | + Message: message, |
|
287 | + IsShown: false, |
|
288 | + } |
|
289 | + |
|
290 | + // 保存提醒记录 |
|
291 | + m.remindHistory[user.ID] = append(m.remindHistory[user.ID], record) |
|
292 | + user.LastRemind[stage] = time.Now() |
|
293 | + |
|
294 | + return record, nil |
|
295 | +} |
|
296 | + |
|
297 | +// 请求提醒处理 |
|
298 | +func (m *RemindStrategyManager) HandleRequestRemind(user *User) (*RemindRecord, error) { |
|
299 | + stage := m.getCurrentStage() |
|
300 | + |
|
301 | + if !m.shouldRemindUser(user, stage, "所有请求") { |
|
302 | + return nil, nil |
|
303 | + } |
|
304 | + |
|
305 | + // 检查业务阻塞 |
|
306 | + if err := m.checkBusinessBlock(stage); err != nil { |
|
307 | + return nil, err |
|
308 | + } |
|
309 | + |
|
310 | + message := m.generateMessage(stage, user) |
|
311 | + record := &RemindRecord{ |
|
312 | + UserID: user.ID, |
|
313 | + Stage: stage, |
|
314 | + RemindTime: time.Now(), |
|
315 | + Message: message, |
|
316 | + IsShown: false, |
|
317 | + } |
|
318 | + |
|
319 | + return record, nil |
|
320 | +} |
|
321 | +``` |
|
322 | + |
|
323 | +### 5. 使用示例 |
|
324 | + |
|
325 | +```go |
|
326 | +func main() { |
|
327 | + // 初始化许可证信息 |
|
328 | + expireTime := time.Now().AddDate(0, 0, 45) // 45天后到期 |
|
329 | + license := &License{ |
|
330 | + ExpireTime: expireTime, |
|
331 | + IsValid: true, |
|
332 | + LicenseType: "企业版", |
|
333 | + } |
|
334 | + |
|
335 | + // 创建策略管理器 |
|
336 | + strategyManager := NewRemindStrategyManager(license) |
|
337 | + |
|
338 | + // 模拟用户登录 |
|
339 | + adminUser := &User{ |
|
340 | + ID: "user001", |
|
341 | + Username: "admin", |
|
342 | + Role: RolePlatformAdmin, |
|
343 | + LastRemind: make(map[RemindStage]time.Time), |
|
344 | + } |
|
345 | + |
|
346 | + // 处理登录提醒 |
|
347 | + record, err := strategyManager.HandleLoginRemind(adminUser) |
|
348 | + if err != nil { |
|
349 | + fmt.Printf("业务阻塞: %v\n", err) |
|
350 | + return |
|
351 | + } |
|
352 | + |
|
353 | + if record != nil { |
|
354 | + fmt.Printf("提醒消息: %s\n", record.Message) |
|
355 | + fmt.Printf("提醒阶段: %v\n", record.Stage) |
|
356 | + fmt.Printf("提醒时间: %s\n", record.RemindTime.Format("2006-01-02 15:04:05")) |
|
357 | + } |
|
358 | + |
|
359 | + // 模拟普通用户请求 |
|
360 | + normalUser := &User{ |
|
361 | + ID: "user002", |
|
362 | + Username: "user1", |
|
363 | + Role: RoleNormalUser, |
|
364 | + LastRemind: make(map[RemindStage]time.Time), |
|
365 | + } |
|
366 | + |
|
367 | + // 处理请求提醒(在到期后阶段) |
|
368 | + requestRecord, err := strategyManager.HandleRequestRemind(normalUser) |
|
369 | + if err != nil { |
|
370 | + fmt.Printf("请求被阻塞: %v\n", err) |
|
371 | + return |
|
372 | + } |
|
373 | + |
|
374 | + if requestRecord != nil { |
|
375 | + fmt.Printf("请求提醒: %s\n", requestRecord.Message) |
|
376 | + } |
|
377 | +} |
|
378 | +``` |
|
379 | + |
|
380 | +## 系统架构建议 |
|
381 | + |
|
382 | +### 1. 存储设计 |
|
383 | +- 使用Redis缓存用户最近提醒记录 |
|
384 | +- 使用MySQL持久化提醒历史 |
|
385 | +- 许可证信息配置在配置中心 |
|
386 | + |
|
387 | +### 2. 性能优化 |
|
388 | +- 使用本地缓存减少数据库查询 |
|
389 | +- 异步记录提醒日志 |
|
390 | +- 批量处理用户提醒 |
|
391 | + |
|
392 | +### 3. 扩展性考虑 |
|
393 | +- 支持动态策略配置 |
|
394 | +- 可插拔的提醒渠道(邮件、短信、站内信) |
|
395 | +- 多租户隔离支持 |
|
396 | + |
|
397 | +这个实现方案完整覆盖了需求中的所有策略阶段,提供了灵活的配置方式和良好的扩展性,可以直接集成到现有的微服务架构中。 |
|
... | ... | \ No newline at end of file |