需求
用戶中有人設置了賬戶余額達到閾值時,短信/郵箱進行提醒的服務。我們將需要在他賬戶余額閾值達到指定數值的時候進行短信/郵箱消息通知,允許賬戶余額閾值出現偏差的時候通知,如果某個用戶48小時內已經短信/郵箱進行過通知了,那么將不再進行通知。
剖析
- 存在兩個主題:短信通知和郵箱通知
- 存在兩種觀察者:設置了短信通知且賬戶余額到達閾值的用戶,設置了郵箱通知且賬戶余額到達閾值的用戶。
- 用spring的定時器,每10分鐘去數據庫獲取某個主題已經達到閾值且開始了該主題的提醒功能的用戶
- 用spring的@asycn注解異步短信通知,郵箱通知的相關方法
- 用redis設置用戶短信/郵箱為鍵名,設置過期時間為48小時。如果獲取不到該鍵值對,說明其在觀察者行列
代碼
觀察者父類
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
|
/** * 訂閱觀察者 * @author administrator * */ @component //標志為多例 @scope (value=configurablebeanfactory.scope_prototype) public class subscriberobserver implements observer{ private string email; private string phone; private string username; @autowired userfunctionservice userfunctionservice; @override public void update(observable o, object arg) { if (o instanceof emailalertsubject){ userfunctionservice.alertuseremail(email,username); } if (o instanceof phonealertsubject){ userfunctionservice.alertuserphone(phone,username); } } public string getemail() { return email; } public void setemail(string email) { this .email = email; } public string getphone() { return phone; } public void setphone(string phone) { this .phone = phone; } public string getusername() { return username; } public void setusername(string username) { this .username = username; } public subscriberobserver() { super (); // todo auto-generated constructor stub } } |
主題
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/** * email提醒主題 * @author administrator * */ @component public class emailalertsubject extends observable{ public void alert(){ this .setchanged(); //如果用拉的方式,這么調用 this .notifyobservers(); } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/** * 短信提醒主題 * @author administrator * */ @component public class phonealertsubject extends observable{ public void alert(){ this .setchanged(); //如果用拉的方式,這么調用 this .notifyobservers(); } } |
定時器
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
|
/** * 定時給訂閱了短信提醒和email提醒的用戶服務 * @author administrator * */ @component public class timealerttaskutil { @autowired commonuserservice commonuserservice; @autowired jedisconnectionfactory factory; @autowired emailalertsubject emailsubject; @autowired phonealertsubject phonesubject; private static final string emailkeyname = "emailalert:" ; private static final string phonekeyname = "phonealert:" ; /** * 定時獲取需要email提醒的用戶,每10分鐘調用一次 */ @scheduled (fixeddelay = 1000 * 60 * 10 ) public void alertemailtask() { // 1.獲取數據庫中達到了閾值的用戶 list<user> emails = commonuserservice.getuseralertemailandname(); // 2.查看redis中是否有達到閾值,且48小時已經通知的用戶,將其排除在觀察者行列,最終得出觀察者隊伍 list<subscriberobserver> informemail = getinformobserver(emails); // 3.創建主題,添加觀察者 addobservers(emailsubject, informemail); // 4.通知 emailsubject.alert(); // 5.將已經通知的觀察者信息存儲到reids內,設置過期時間為一天 setrediscache(emails); // 6.將觀察者從主題中移除 deleteobservers(emailsubject, informemail); } /** * 定時獲取需要短信提醒的用戶,每10分鐘調用一次 * */ @scheduled (fixeddelay = 1000 * 60 * 10 ) public void alertphonetask() { // 1.獲取數據庫中達到了閾值的用戶 list<user> phones = commonuserservice.getuseralertphoneandname(); // 2.查看redis中是否有達到閾值,且今天已經通知的用戶,將其排除在觀察者行列,最終得出觀察者隊伍 list<subscriberobserver> informphones = getinformobserver(phones); // 3.創建主題,添加觀察者 addobservers(phonesubject, informphones); // 4.通知 phonesubject.alert(); // 5.將已經通知的觀察者信息存儲到reids內,設置過期時間為一天 setrediscache(phones); // 6.將觀察者從主題中移除 deleteobservers(phonesubject, informphones); } /** * ------------------------------------------------------------------------ * ----------------------------------------------------- **/ /** * 過濾掉今天已經email提醒的用戶,返回真正需要提醒的觀察者列表 * * @param emails * @return */ private list<subscriberobserver> getinformobserver( list<user> users) { list<subscriberobserver> obs = new arraylist<subscriberobserver>(); jedis jedis = factory.getconnection().getnativeconnection(); for (user user : users) { string value; subscriberobserver observer = (subscriberobserver) springconfigtool .getbean( "subscriberobserver" ); if (user.getemail()!= null ) { value = jedis.get(emailkeyname + user.getemail()); if (value == null || !value.equals( "success" )) { observer.setemail(user.getemail()); observer.setusername(user.getname()); obs.add(observer); } } else { value = jedis.get(phonekeyname + user.getphone()); if (value == null || !value.equals( "success" )) { observer.setphone(user.getphone()); observer.setusername(user.getname()); obs.add(observer); } } } return obs; } /** * 將指定的觀察者列表添加到指定的主題 * * @param subject * @param list */ private void addobservers(observable subject, list<subscriberobserver> list) { for (subscriberobserver obs : list) { subject.addobserver(obs); } } private void deleteobservers(observable subject, list<subscriberobserver> list) { for (subscriberobserver obs : list) { subject.deleteobserver(obs); } } /** * 將列表的值作為鍵,存入redis,過期時間為48小時 * * @param list */ private void setrediscache(list<user> users) { jedis jedis = factory.getconnection().getnativeconnection(); for (user user : users) { if (user.getemail()!= null ) { jedis.set(emailkeyname + user.getemail(), "success" , "nx" , "ex" , 60 * 60 * 24 * 2 ); } else { jedis.set(phonekeyname + user.getphone(), "success" , "nx" , "ex" , 60 * 60 * 24 * 2 ); } } } } |
總結
代碼是不全面的,只是個示例而已。關于短信通知和郵箱通知的服務類和工具類并沒有給出,因為里面涉及到一些隱私參數。所以關于異步通知示例代碼沒有,但使用spring管理的@async注解和在spring進行一定的配置即可,可以在我的另外一篇博客找到關于異步通知的示例代碼。
事實上根據需求,可以使用redis的發布訂閱,或者消息隊列mq來實現類似的功能。但為了加深對設計模式的理解,所以寫了一個不是很純正的觀察者模式來模仿發布訂閱的操作。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:https://blog.csdn.net/qq_32020035/article/details/81204374