需求
用戶中有人設(shè)置了賬戶余額達(dá)到閾值時(shí),短信/郵箱進(jìn)行提醒的服務(wù)。我們將需要在他賬戶余額閾值達(dá)到指定數(shù)值的時(shí)候進(jìn)行短信/郵箱消息通知,允許賬戶余額閾值出現(xiàn)偏差的時(shí)候通知,如果某個(gè)用戶48小時(shí)內(nèi)已經(jīng)短信/郵箱進(jìn)行過通知了,那么將不再進(jìn)行通知。
剖析
- 存在兩個(gè)主題:短信通知和郵箱通知
- 存在兩種觀察者:設(shè)置了短信通知且賬戶余額到達(dá)閾值的用戶,設(shè)置了郵箱通知且賬戶余額到達(dá)閾值的用戶。
- 用spring的定時(shí)器,每10分鐘去數(shù)據(jù)庫獲取某個(gè)主題已經(jīng)達(dá)到閾值且開始了該主題的提醒功能的用戶
- 用spring的@asycn注解異步短信通知,郵箱通知的相關(guān)方法
- 用redis設(shè)置用戶短信/郵箱為鍵名,設(shè)置過期時(shí)間為48小時(shí)。如果獲取不到該鍵值對,說明其在觀察者行列
代碼
觀察者父類
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 //標(biāo)志為多例 @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(); //如果用拉的方式,這么調(diào)用 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(); //如果用拉的方式,這么調(diào)用 this .notifyobservers(); } } |
定時(shí)器
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
|
/** * 定時(shí)給訂閱了短信提醒和email提醒的用戶服務(wù) * @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:" ; /** * 定時(shí)獲取需要email提醒的用戶,每10分鐘調(diào)用一次 */ @scheduled (fixeddelay = 1000 * 60 * 10 ) public void alertemailtask() { // 1.獲取數(shù)據(jù)庫中達(dá)到了閾值的用戶 list<user> emails = commonuserservice.getuseralertemailandname(); // 2.查看redis中是否有達(dá)到閾值,且48小時(shí)已經(jīng)通知的用戶,將其排除在觀察者行列,最終得出觀察者隊(duì)伍 list<subscriberobserver> informemail = getinformobserver(emails); // 3.創(chuàng)建主題,添加觀察者 addobservers(emailsubject, informemail); // 4.通知 emailsubject.alert(); // 5.將已經(jīng)通知的觀察者信息存儲(chǔ)到reids內(nèi),設(shè)置過期時(shí)間為一天 setrediscache(emails); // 6.將觀察者從主題中移除 deleteobservers(emailsubject, informemail); } /** * 定時(shí)獲取需要短信提醒的用戶,每10分鐘調(diào)用一次 * */ @scheduled (fixeddelay = 1000 * 60 * 10 ) public void alertphonetask() { // 1.獲取數(shù)據(jù)庫中達(dá)到了閾值的用戶 list<user> phones = commonuserservice.getuseralertphoneandname(); // 2.查看redis中是否有達(dá)到閾值,且今天已經(jīng)通知的用戶,將其排除在觀察者行列,最終得出觀察者隊(duì)伍 list<subscriberobserver> informphones = getinformobserver(phones); // 3.創(chuàng)建主題,添加觀察者 addobservers(phonesubject, informphones); // 4.通知 phonesubject.alert(); // 5.將已經(jīng)通知的觀察者信息存儲(chǔ)到reids內(nèi),設(shè)置過期時(shí)間為一天 setrediscache(phones); // 6.將觀察者從主題中移除 deleteobservers(phonesubject, informphones); } /** * ------------------------------------------------------------------------ * ----------------------------------------------------- **/ /** * 過濾掉今天已經(jīng)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,過期時(shí)間為48小時(shí) * * @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 ); } } } } |
總結(jié)
代碼是不全面的,只是個(gè)示例而已。關(guān)于短信通知和郵箱通知的服務(wù)類和工具類并沒有給出,因?yàn)槔锩嫔婕暗揭恍╇[私參數(shù)。所以關(guān)于異步通知示例代碼沒有,但使用spring管理的@async注解和在spring進(jìn)行一定的配置即可,可以在我的另外一篇博客找到關(guān)于異步通知的示例代碼。
事實(shí)上根據(jù)需求,可以使用redis的發(fā)布訂閱,或者消息隊(duì)列mq來實(shí)現(xiàn)類似的功能。但為了加深對設(shè)計(jì)模式的理解,所以寫了一個(gè)不是很純正的觀察者模式來模仿發(fā)布訂閱的操作。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。
原文鏈接:https://blog.csdn.net/qq_32020035/article/details/81204374