From bb41afa86c81c27b91d32174d9f976eb50413730 Mon Sep 17 00:00:00 2001 From: XinYi Song <2037158277@qq.com> Date: Thu, 10 Feb 2022 15:41:52 +0800 Subject: [PATCH] =?UTF-8?q?=E6=90=AD=E5=BB=BA=E6=99=BA=E6=85=A7=E5=9F=8E?= =?UTF-8?q?=E4=B9=A1=E9=A1=B9=E7=9B=AE=EF=BC=8C=E5=B9=B6=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?mq=E6=B6=88=E6=81=AF=E8=AE=A2=E9=98=85=EF=BC=8Cwebsocket?= =?UTF-8?q?=E6=8E=A8=E9=80=81=E6=8A=A5=E8=AD=A6=E4=BF=A1=E6=81=AF=E7=AD=89?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 33 ++ .mvn/wrapper/maven-wrapper.jar | Bin 0 -> 58727 bytes .mvn/wrapper/maven-wrapper.properties | 2 + mvnw | 316 +++++++++++++++ mvnw.cmd | 188 +++++++++ pom.xml | 220 +++++++++++ .../com/xkrs/SmartUrbanRuralApplication.java | 20 + .../com/xkrs/common/config/BeanConfig.java | 70 ++++ .../com/xkrs/common/config/CorsConfig.java | 23 ++ .../xkrs/common/config/CorsInterceptor.java | 29 ++ .../com/xkrs/common/config/MvcConfig.java | 37 ++ .../encapsulation/EncapsulationObejct.java | 59 +++ .../encapsulation/OutputEncapsulation.java | 96 +++++ .../encapsulation/PromptMessageEnum.java | 65 +++ .../com/xkrs/common/tools/GetTokenTool.java | 48 +++ .../com/xkrs/common/tools/GetVideoTool.java | 58 +++ .../com/xkrs/common/tools/JackSonTool.java | 174 ++++++++ .../xkrs/controller/EquipmentController.java | 94 +++++ .../com/xkrs/controller/TestController.java | 119 ++++++ src/main/java/com/xkrs/dao/EquipmentDao.java | 11 + src/main/java/com/xkrs/dao/FireDao.java | 31 ++ .../java/com/xkrs/model/entity/Equipment.java | 153 +++++++ src/main/java/com/xkrs/model/entity/Fire.java | 329 ++++++++++++++++ src/main/java/com/xkrs/util/DateTimeUtil.java | 230 +++++++++++ src/main/java/com/xkrs/util/Md5Util.java | 33 ++ src/main/java/com/xkrs/util/RequestUtil.java | 372 ++++++++++++++++++ .../xkrs/util/SeeSSLCloseableHttpClient.java | 64 +++ .../com/xkrs/util/TopicConsumerListener.java | 113 ++++++ src/main/java/com/xkrs/util/WeatherUtils.java | 26 ++ .../config/WebSocketBrokerConfig.java | 25 ++ .../websocket/config/WebSocketConfig.java | 16 + .../controller/WebSocketController.java | 49 +++ .../websocket/server/WebSocketServer.java | 129 ++++++ src/main/resources/application.properties | 78 ++++ src/main/resources/i18n/messages.properties | 162 ++++++++ .../resources/i18n/messages_en_US.properties | 162 ++++++++ .../resources/i18n/messages_zh_CN.properties | 162 ++++++++ 37 files changed, 3796 insertions(+) create mode 100644 .gitignore create mode 100644 .mvn/wrapper/maven-wrapper.jar create mode 100644 .mvn/wrapper/maven-wrapper.properties create mode 100644 mvnw create mode 100644 mvnw.cmd create mode 100644 pom.xml create mode 100644 src/main/java/com/xkrs/SmartUrbanRuralApplication.java create mode 100644 src/main/java/com/xkrs/common/config/BeanConfig.java create mode 100644 src/main/java/com/xkrs/common/config/CorsConfig.java create mode 100644 src/main/java/com/xkrs/common/config/CorsInterceptor.java create mode 100644 src/main/java/com/xkrs/common/config/MvcConfig.java create mode 100644 src/main/java/com/xkrs/common/encapsulation/EncapsulationObejct.java create mode 100644 src/main/java/com/xkrs/common/encapsulation/OutputEncapsulation.java create mode 100644 src/main/java/com/xkrs/common/encapsulation/PromptMessageEnum.java create mode 100644 src/main/java/com/xkrs/common/tools/GetTokenTool.java create mode 100644 src/main/java/com/xkrs/common/tools/GetVideoTool.java create mode 100644 src/main/java/com/xkrs/common/tools/JackSonTool.java create mode 100644 src/main/java/com/xkrs/controller/EquipmentController.java create mode 100644 src/main/java/com/xkrs/controller/TestController.java create mode 100644 src/main/java/com/xkrs/dao/EquipmentDao.java create mode 100644 src/main/java/com/xkrs/dao/FireDao.java create mode 100644 src/main/java/com/xkrs/model/entity/Equipment.java create mode 100644 src/main/java/com/xkrs/model/entity/Fire.java create mode 100644 src/main/java/com/xkrs/util/DateTimeUtil.java create mode 100644 src/main/java/com/xkrs/util/Md5Util.java create mode 100644 src/main/java/com/xkrs/util/RequestUtil.java create mode 100644 src/main/java/com/xkrs/util/SeeSSLCloseableHttpClient.java create mode 100644 src/main/java/com/xkrs/util/TopicConsumerListener.java create mode 100644 src/main/java/com/xkrs/util/WeatherUtils.java create mode 100644 src/main/java/com/xkrs/websocket/config/WebSocketBrokerConfig.java create mode 100644 src/main/java/com/xkrs/websocket/config/WebSocketConfig.java create mode 100644 src/main/java/com/xkrs/websocket/controller/WebSocketController.java create mode 100644 src/main/java/com/xkrs/websocket/server/WebSocketServer.java create mode 100644 src/main/resources/application.properties create mode 100644 src/main/resources/i18n/messages.properties create mode 100644 src/main/resources/i18n/messages_en_US.properties create mode 100644 src/main/resources/i18n/messages_zh_CN.properties diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..c1dd12f17644411d6e840bd5a10c6ecda0175f18 GIT binary patch literal 58727 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%0ANsA|A3-jj|r`KAo%oTajX3>^E` zq{Nq+*dAH{EQyjZw_d4E!54gka%phEHEm}XI5o%$)&Z+*4qj<_EChj#X+kA1t|O3V@_RzoBA(&rgxwAF+zhjMY6+Xi>tw<6k+vgz=?DPJS^! zei4z1%+2HDqt}Ow+|2v^3IZQkTR<&IRxc0IZ_-Di>CErQ+oFQ~G{;lJSzvh9rKkAiSGHlAB$1}ZRdR^v zs2OS)Pca>Ap(RaSs7lM2GfJ#%F`}$!)K4#RaGJ_tY}6PMzY{5uHi}HjU>Qb~wlXQ) zdd(`#gdDgN_cat+Q#1q&iH{`26k}U3UR5(?FXM>Jm{W%IKpM4Jo{`3aEHN)XI&Bwx zs}a_P|M)fwG1Tybl)Rkw#D__n_uM+eDn*}}uN4z)3dq)U)n>pIk&pbWpPt@TXlB?b z8AAgq!2_g-!QL>xdU4~4f6CB06j6@M?60$f;#gpb)X1N0YO*%fw2W`m=M@%ZGWPx; z)r*>C$WLCDX)-_~S%jEx%dBpzU6HNHNQ%gLO~*egm7li)zfi|oMBt1pwzMA$x@ zu{Ht#H}ZBZwaf0Ylus3KCZ*qfyfbTUYGuOQI9>??gLrBPf-0XB84}sCqt5Q(O$M& zoJ+1hx4Wp#z?uex+Q1crm2ai?kci;AE!yriBr}c@tQdCnhs$P-CE8jdP&uriF`WFt>D9wO9fCS0WzaqUKjV_uRWg>^hIC!n-~q=1K87NAECZb^W?R zjbI&9pJ)4SSxiq06Zasv*@ATm7ghLgGw3coL-dn6@_D-UhvwPXC3tLC)q3xA2`^D{ z&=G&aeSCN)6{2W6l@cg&2`cCja~D2N{_>ZQ)(5oSf!ns1i9szOif~I8@;2b)f2yQ5 zCqr{lGy5(^+d!<0g??wFzH^wuv=~0)g55&^7m8Ptk3y$OU|eI7 zIovLvNCoY%N(aW#=_C%GDqEO|hH3O9&iCp+LU=&CJ(=JYDGI;&ag&NKq}d;B`TonC zK+-t8V5KjcmDyMR@jvDs|7lkga4>TQej$5B+>A`@{zE&?j-QbQWk4J*eP2@%RzQ{J z?h`1~zwArwi^D7k9~%xtyf(2&$=GsP*n-fTKneej-y6y(3nNfC7|0{drDx{zz~cSs z<_+d2#ZDst@+`w{mwzmn?dM2aB;E;bS-Opq$%w@WnDwa$hUGL90u9c=as)+_6aO10 zLR|CR8nr<2DQTvkaH0QDsyn@TYCs7Nk3lN}Ix$)JM0*zf=0Ad$w9j723W#%{r8V&`{wx-8kSv#)mZ{FU%UZDIi zvbgLHyJ>z0BZe`GNM$Q;D6D48#zc9s(4^SGr>u-arE}okN62N{zuwX)@FL5>$ib=b z5Wtm~!ojD3X|g59lw%^hE?dL;c^bgVtBOkJxQR{Eb*nR1wVM&fJQ{<))bn9e3bSlu z3E-qpLbAE(S^I4mVn`?lycoV!yO!Qj_4qYgsg7tXR)Gu2%1)5FZu&lY7x>bU`eE}x zSZ5c`z~^&$9V?eEH!^Rp-Fz3WiCvEgf`Tq}CnWRZY+@jZ{2NewmyGUM6|xa3Sh7)v zj6d&NWUVqu9f-&W)tQ>Y%Ea!e76@y!Vm*aQp|wU5u<%knNvHZ!U}`fp*_)mIWba=j z*w9~{f5pD;zCmEWePjM#ERNiNjv!SnM-&rGpB9Nmiv}J+hwB&0f_+x?%*lgJFRHsqfFDPwyvh8<*xLT0u_BeEHw{q+UGj=$4udEx)Vq#sV zKB3+_C!RUKy?ac3-`+}dL2!D_2(5=8&@hBf`-AbU`-<_3>Ilqkg6qSI>9G(@Kx?g<0h0K&31$AR>R%d}{%DyXPss$&c^ja7NR z$0AN7Fl$>VpGxqHW15CjxAa6DUVmCpQNbOwBv8D^Y{bXg28> zEQE9xl?CWh0gS6%Y=G4Cy($Vb>jBb2f_dm#0_B<_Ce`|~Obt_Xp^nkR zK%o_`{h1XkWn}i|5Dp#q8D(;k;2|+{DAG{2gJgPNQ=KZ=FKY@d>QEu6W;oLsE(1}< zpnwSEj(K{Bu^#CXdi7L_$!X`QOx^tA1c{&-XTHo3G?3(H*&VM~*Aud?8%FU=dE&kV zJ$SqZoj^g@(q9x;7B30J$(-qUml{?3e+I^Cf?X0PpLr}m zS}W9`QaCwINRU&D5>j9O*j6S}R1`7{5+{d-xUlI~)U!^4+*b5tkuon-Msz03Z{{Kp zH!GAXoyr#1K;t5o#h#a%Lzj3XQGqM0TRnfu$(fsQe^wb_?W!m!+7r55q>svWN`k~T zS(gk9bi|@+8wg;dR<&0f;MpwQbY27$N{{laPQk3@3uCz$w1&jq)`uW*yn!Pe-V^%Q zR9)cW;UB~ODlwolWFAX?ik#_|v)AtHNwoq72E9Jg#v2e5SErf+7nTleI8&}%tn6hf zuz#5YtRs94Ui&E_1PakHfo+^t-{#ewhO*j5ls-zhm^C{kCARNEB1aORsxE!1SXBRz z6Oc-^#|0W6=7AJ;I|}pH#qby@i^C+Vsu9?zdtkE{0`oO_Hw|N=Lz9Is8j}R zI+8thGK?(KSZ5ZW4nQG1`v(=0Jd*0gIlavVihzo#fPaa=}(Rqdxl3^6O8K+{MqU`;1iTJ$<^k)Nms(A$j?A-wHJKvh9 zUHW3}JkE;x?FETPV8DFTxFLY8eSAd%C8vp?P_EuaMakmyFN_e?Hf|LBctnncUb}zF zIGP4WqtKCydoov~Bi<_I%y%$l+})!;SQVcP?>)9wM3q-GE6t9*LfoePBlo{gx~~e{g_XM5PQ8Y5dsuG%3Xq}I&qcY6 zTCo?<6E%)O$A2torq3-g8j3?GGd){+VHg@gM6Kw|E($M9}3HVIyL1D9321C zu#6~~h<<*=V7*ria%j^d5A;S^E;n!mOnFppfi+4)!BQ@#O2<|WH$RS~)&2Qol|@ff zFR#zmU(|jaqCXPA@q?UhrgbMO7zNXQYA@8$E+;4Bz7g=&zV-)=&08J_noLAz#ngz$ zA)8L8MrbXIDZuFsR_M(DsdX)s$}yH!*bLr{s$YWl5J?alLci=I#p`&MbL4`5bC}=2 z^8-(u4v2hs9*us}hjB!uiiY6vvv&QWJcVLTJ=SFG=lpR+S4Cd91l}oZ+B-*ehY2Ic_85)SRSa% zMEL~a3xrvH8ZnMIC!{9@pfOT7lrhxMf^8N20{CJXg}M35=`50S;6g-JYwjwj!K{^) z5Bohf6_G6z=+0V8&>F8xLbJ4mkCVu^g66#h&?tL z9odv&iW21IAh~y9D-DupKP-NcernF2(*RsFkAsM<$<>@-Cl1?&XAi4+Mh2Zm@2x#u zWH&J^1=8G|`|H2%94bnjUZyI>QACu9FS}^$lbtzzCz4AMspqGYEwFFM<%G!Oc$+;7 z3r_L!H~PR}5n8+3-&4v*fFr$uK{y_VamM0*TKn^))nQsn5U?7Iv?`4|Oy&m6himAG z%=a;2ji3f_RtDPqkwR>ISxhnS0f)E`ITo}TR!zIxPwECZy#jzo%q{BNYtd!<IP_S+=*yDOk1GgwLqe!d9esV@3$iVAm1!8RoE| zqnTz;5a)B(~~KcP)c>?+ysFAlAGF4EBor6)K{K*Kn>B(&QtMAkR^ynG%k%UbJpKM zI$}qQXXP3PISHe_vTFssbcL`irhG2zN7J((3ZFmh*bnPuiK~=#YG=820hXqOON#HI<0bvIT{z&SaqRvqaMG-d5<06zdP?-kIH{%UMR$Xn@S}Hx3 zFjg}6no}vN_512D+RIn-mo9^_Li-)WI5%VigYt{Jd!RyI%d|-LqJU$y3aJ*a$y6$1 zjyTuIF2&t>1rPlw&k5OVLhrYBvk5Vl8T(*Gd?Alqi}> z<@-`X_o@9EOB8Ik&?|;lvKHFU@#O+?T!kEf&oJUaLzN;>!}!!e1WIs(T}V#Irf$AK z42`x`z-9ogxd@%CS;D5S z2M^b;Pu)q)c&_KBO!va-4xnI57L7V@*_I_r4vU)z>xk5z6PDVqg92R7_iZH|VlO_B z#8R`5HZVn?ou>czd>gZ~s;w4ZkzVXJNP8FiezlB5JXe6Z-OLsDw%N7!(135!Vl2Lb zLYI79?U{h#W-_#W6hf`<$BQHJCu5ehv?IF+-uxUqt~j!ZW1cxfiEJal^q7~RMWQ0a z2CEaPa1_p|P6qRmmeKgas*N}@(2tH%U37-<5i(DSnVOFFxg-Sv%7&{hPeRh{U`&ufGz=V|JdYQ2sG5 zk%3JimSwQFP=Yr?u_beSG^B$nnh$4hrxb4lpTTiUFRQEZ3ulr+L3m;>;Io?D;jG6Wjj!b)nsZds<6 zX@cD%+aVr!ra~F7HYr`TB!|y-t)HSb^FQt zbo+_XP44IWJGGxg73JyhBjKMSv`77ngDOw}6Eve6ZIol$Q5s65d(1-sP{BU{1_y)7 zF8sh5A~jxRHk=wq3c5i3*e&otCd9>cstT?IQ&D4slC-&^q!ut1;WAQ}fE}Y+jU}r{ zmpSI%sW?})RAm8}$WUU+V$PmQOF5gSKOGQ2;LF-E(gd<67rYu2K| zom8mOppa%XJ6C(@I7-*opqLn73e9BMFStaBER?suJ{jte1$vA%z?$_`Em=a=(?T-q z*A=VZOQ`P{co!*UUKyV@Rd-c#*wmb7v<%rN=TGFmWmqhbj#&+?X|3bZYAjbNGTv~O zs7SIYi3VgW6@?=PGnbNNZIWaY^*+ChW&a)A$uqH8xxehwx2`<1w6mag?zuHbsVJiO$a)tQ zuBBoR>rLfhpA@)Qf`8BwRMx886%9HP5rOR%YCy9pQ|^Xw!=Mcnwx8j=(ZE)P-tJ&s zON&Nsr%14jS@K+IvrJj720NkCR*C(j&aI$EFCV)w$9M<#LdihyRKdzTjJPI|t9_S} z--#oF#;F?Y1KN%_yE);Bxv}9PWZphz_g5mReOKR`y%9UZ=n}GXWw?E$T1%NAfK1Ad z|0$Lp^;sntA>}=ybW)mkxNv1?hkZ`<8hCemcT5 zYl6$I^bhXDzPlz<>6zOy3Fu*3?>#q$;1fJ>nuxyx#&<&x6Y}j zCU&VmtCJ`;aYN+qP}nwr%s2ZQC|Z**axS^?iGu+x^{{>FIv!k0#HaXtEG=*C7kPe!mMnknbn}TKpp6Xv9 zVvq&%A3nmY^N*XTg&+=wO>(|{uTwm;ZP9@+M)6%T zwXPh-&{+aAfv^ZCzOEb;yj>A=f5Pbu)7T{9PT3u>#w*%?K8jqEF%I>A?q;E%CXn)f z|0ohNa5DMv@HVk^vT(L=HBtH*Vzo81L?)M=g7)>@j*vUx?S zxqZo23n3vn@K-Q@bx3lLT+5=fB_oz8+p?P;@*UU<-u)jb5WFEXzoc+8*EC5P6(HWr zY$mfFr=L&G>(jvl8US2fLQqTzHtAGizfR*;W4-kN2^I>L3KkXgx=e*}+i*N($}{?c zi=Q67G)oEMW{|Gdsm{)|V)5Evo}KLj%}gIe>98FFoNTLrJX z-ACRdewnT1w#Egct%wpGg~q%?!$}>$_UJPC4SP0^)G_$d4jN0jBEx}+rcd*^aDtnx zewG{`m!oSbQ?A~FZ6L{&V0hUE+b$DxjO_;oskFha>@gzy(jDnzGO>z3Tzz|i&Dakg zFid5$;SFxINis^4JzK5XIVabKoP`=ZWp|p|t{hTi8n|#XE=-rINwJ*blo?=%Se(qw zkW7x5Qs(LV5RVGxu2e&4);c73lY#0(iZo1x=MY;7mW`uUQIY+$_PqH`4a`6O#urwU zE6(FrvyExmB{c5z*YAj_P&t??F1t6TN2N!$N#~02u(t(PDVyD)$mL3hqKQ4E91N#GOIngPr&pUb-f_Z4*XV8`p1pq+mzrUlUY=4~i|3RDo;Lo36U}uwm zaOah}mO8c@%J*~~{Up7_7->8|3x<}WemgaMA}h>xD17Fey@V9;LgjQFSBS(A<+2kCP9( zlkD%;oXzWtZ_hgu0IxeTjH`6=vi|t_04Btl32=g8swD1oZguWr4|lx0RuXoDHbh27 z+ks?gkVWYnr~_{h+PzQjQ(#8kaJai4We{F!JuqCzU0t*+H{n6i3;K<>_6XUn1n)}) zJ?}JCUPYhT9S1Hi-M+$(Z**%fz7Z%IiMN6%kD>wh%r4#C?Ge4{>w9o??Vbehy9!3@ zffZs8?LGxyWQr@yB(|%~Aa>fVj3$O=i{K*f;?h-a@-ce{(cY8qByOCA1r0;NC}}gr zcC^fCa$Ot`42n>`ehclOAqBo7L&D6Mi=;M5!pd@jj$H z?U7LQWX_u7bHpBzF7L-s4*`C)`dUrbEIgKy5=QHsi7%#&WYozvQOXrNcG{~HIIM%x zV^eEHrB=(%$-FXVCvH@A@|nvmh`|agsu9s1UhmdPdKflZa7m&1G`3*tdUI5$9Z>*F zYy|l8`o!QqR9?pP4D7|Lqz&~*Rl-kIL8%z?mi`BQh9Pk9a$Z}_#nRe4NIwqEYR(W0 z1lAKVtT#ZTXK2pwfcCP%Apfo#EVU|strP=o4bbt3j zP?k0Bn$A&Xv$GTun3!izxU#IXsK1GQt;F0k`Tglr{z>v2>gCINX!vfs`aqag!S*AG5Z`y-# zUv_u&J4r;|EA`r!-gsoYGn<^nSZLH-nj1SRGc0MRG%LWVL)PckFn9z!ebIJ}eg+ix zIJo7GN;j1s$D6!({bYW)auypcB~eAWN;vhF%(l=|RR})$TOn;ldq^@8ZPi<%Xz~{Z zQQ|KAJ@JHaX!Ka2nhP%Cb^I}V6_C|e1SjOQpcPMMwfNz#U@Az|+rmH*Zn=cYJu-KR z{>f++Z~P=jm)4-7^yc#52U4qeNcBRYb!hhT3Q7Ngu5t@CvY*ygxu^Eh?2l6= zhdqN{QEaP(!p>1p1*toD!TllHH6EH~S%l9`mG62dyAd+?}1(vf@N*x^6vhEFU<-RqS7#12*q-xtU z5d|F^n%WSAQHnm-vL)4L-VvoUVvO0kvhpIg57Wf@9p;lYS5YfrG9jtrr?E<_JL{q% z7uPQ52{)aP{7<_v^&=J)?_|}Ep*`{dH-=cDt*65^%LodzPSH@+Z~;7sAL}ZECxQv+;z*f;(?k)>-Lp@jBh9%J`XotGJO(HcJc!21iZ98g zS-O!L9vpE(xMx1mf9DIcy8J5)hGpT!o|C8H4)o-_$BR!bDb^zNiWIT6UA{5}dYySM zHQT8>e*04zk1)?F99$dp5F^2Htt*jJ=( zH(#XwfEZ`EErdI~k(THhgbwNK9a(()+Ha1EBDWVRLSB?0Q;=5Y(M0?PRJ>2M#uzuD zmf5hDxfxr%P1;dy0k|ogO(?oahcJqGgVJmb=m16RKxNU3!xpt19>sEsWYvwP{J!u& zhdu+RFZ4v8PVYnwc{fM7MuBs+CsdV}`PdHl)2nn0;J!OA&)^P23|uK)87pmdZ@8~F$W)lLA}u#meb zcl7EI?ng$CAA;AN+8y~9?aon#I*BgYxWleUO+W3YsQxAUF@2;Lu-m#U?F(tFRNIYA zvXuKXpMuxLjHEn&4;#P|=^k+?^~TbcB2pzqPMEz1N%;UDcf{z2lSiwvJs(KhoK+3^2 zfrmK%Z-ShDHo^OUl@cfy#(cE=fZvfHxbQ!Chs#(vIsL%hf55_zyx>0|h2JT=|7JWo z+Uth3y@G;48O|plybV_jER4KV{y{$yL5wc#-5H&w(6~)&1NfQe9WP99*Kc+Z^!6u7 zj`vK@fV-8(sZW=(Si)_WUKp0uKT$p8mKTgi$@k}(Ng z#xPo-5i8eZl6VB8Bk%2=&`o=v+G7g|dW47~gh}b3hDtjW%w)47v#X!VYM}Z7hG1GI zj16;ufr@1^yZ*w3R&6pB8PMbuz%kQ%r=|F4+a!Gw2RBX6RD5c!3fU@+QCq#X7W@Q5 zuVQ}Uu0dzN+2mSX5)KV%CsU;2FL%B6YT`10$8JR^#;jOO1x?t()Q_gI zxpQr2HI0_^@ge0hNt&MQAI`yJ1Zhd-fpR{rdNmRkEEDu7SpB)QOP4ajV;UBZZZK<6 zWds;!f+|}iP-kqWAH#1@QisJpjcg`+s80!LhAG@(eMad|zcln~oE8}9l5!K{^zf~( zd=HArZ5+Mryc$uNa`@|GSdOX=y}8GZc-%p8W@OM)uk2DfmhQXCU1E#y3XJ>|+XdW2 z)FQLeK38}u_D(5E{GV|YT^rI4qds2{-r<@@@@SG@u&4LbC z5o|KKqVM{?wk$5>2?t*I?IHdh~gljn_2m2zqZNJEEz4Mb$o&I3_UAg#$B{0u$uF4-q}{ zzs5+k@qOe08!CGLGmy3eRrcuqsgB*B>i8c3>3=T^Hv>nL{{u)jtNc6tLbL7KxfUr; z=Pp14Nz+ggjuwd~*oRJ)xWwGwdge+~b!E%c3Gzw6`vT>CCxE0t6v5Z`tw1oKCcm68A~Dbc zgbhP6bkWwSQ=#5EsX*O9Sm^}EwmQQzt2V2phrqqe2y)w8;|&t6W?lUSOTjeU%PKXC z3Kw$|>1YrfgUf6^)h(|d9SRFO_0&Cvpk<+i83DLS_}jgt~^YFwg0XWQSKW?cnBUVU}$R9F3Uo;N#%+js-gOY@`B4+9DH zYuN|s&@2{9&>eH?p1WVQcdDx&V(%-kz&oSSnvqzcXC3VsggWet1#~bRj5lBJDo#zF zSz))FHQd8>3iSw{63m`Pgy_jkkj9LTmJ&!J(V0E~&}HJ4@nXp<(miz$sb;(I<8s!7 zZyezu!-+X81r03486gAlx@n#aKx_93DREBtNcYln*8oliQ zbh0~SkAgHXX%C6}HwN(TRwaK2k_$Y}PxKId;jYt=S1Bf<8s@(IL?k3u1(f^V%TYO1 zA_jPf*V)SLEZFWS#y>M&p$LoSk+%ubs`)H%WEZf=F)RKh&x;i)uLIGJ94~A4m$(;S z;1rQC{m>--`WHFcaFA&5#7~vz|5S;{fB(7pPnG;@$D~C0pZYNEG?B8X*GB2e4{Qk; za1oop8OvHqs1Lk6B`AuYOv4`y`IgM315iTr{VUVc9WeOG;xE z%eDQgE4rb_B%vuT>N?^K zRvPnQwG%7RjO26+DY!OXWjgBu4^!)W-+ob_G&nX++))pD->QdRCo0spZN?Y*J#@-q z)fk-fJvZYz8)GSxYc^oXYIM;Pw}ftHW+a3dis#dXx^OS^m-~FlwcVr6MXv78fNI!i z51K-2t&!&IZ4(GF=mT@;qIp!&R(I@UiWPPz)%Us&(FdAAGxZ-+6^UZ7em`J-F#_3r zLkHym@VAnZFM$J~?0b@&O`l4YXyvOQ+OqalbZ0{g{qD{neY_xno1ZpXlSJWM=Mv(~ zvK{?O>AcXpbd}+hn{~*>weZwDTURX*M^9RkOO#DUfRW1;comKg1bn+mlsrNY8XDyW zgWg9~AWb_1^D8zsD4bL(1J4oinVy0Fimrh&AC}Itl;IH*p4eU_I;SWkOI!9tAbi3B zO@0=q#LHAc>z?ve8Q&hsF(sR9lgf_99_5Kvuug<^&0}Y&m)YjI?bITGIuh}AJO|>z zc*`Mly$>TA={AIT#d%JuMpXHDt($qkc*3UTf-wS$8^awqDD^|EAeA{FoeyJfWM@QX zk>vJ4L|8DU7jg_fB^3Qvz*V$QmDl*AXdw6@KSckh#qxjLCM8Nba!dTkJgr(S@~Z0a zt8%|W!a~3zG4Y&X6xbLtt^JK5;JT($B`_9bv(BjRTfG_Y`tg3k-}%sQoY@F|=}}${ zwmW%Ub6jPd)$;NA0=b7w!^2dE-qvI4)AVr`yvkabJcGwvuQ2rAoRlTjvCC^-$2BG} ziy0<6nt8;J67rymwm&wVZ8E7Krouv2Ir@-GQ%ui6PR42KHKms3MK&Z$zp{_XAVvrd znK4cbg)Ggh5k(4SlFOM9yyRUlVH1oo%|6Lu9%ZxZW28!c9Z%H5#E?B?7H7ulcUtirB<{s@jnS(-R@we z^R#{Mn$#JXd~5sw9rU&~e3fYTx!T&hY{S<~7hviG-T$<4OPcG6eA0KOHJbTz^(`i~ z_WON4ILDLdi}Ra@cWXKLqyd0nPi06vnrU-)-{)Xp&|2gV>E{Uc>Td`@f@=WYJYZ^- zw&+fjnmyeRoK-unBVvX>g>wO3!ey<+X#z@8GNc9MD}khMO>TV{4`z zx4%!9|H6k|Ue;`M{G6d!p#LL+_@6WMpWgF7jk*%$D_JB3c%D`~YmHRJD1UNDLh;Tf zYbbKcv9R(81c4yK+g+1Ril{5w#?E}+NVz>d@n48C-T-(L?9a9W`JV*{dan-sH*P3_Hnt~iRv)}ye;7$b}^4l%ixphDK`G#b!4R4qoouT@*A zZ)kQa)e94??k7N>tqoRl>h(9DFq&92=z|F!LJrh-97EoFL|Wt2v}>(zG1*#aiYA_^ zM_&%_G^g*O8x650e>m!#MDmwRub!irY>^^|L=!4^%lBr;?}mvgP3y~^mSdKSm^R~WAt7T0_ck0mA`GS)J^SYTo6^vQ|vuM7!92&@$BhtcQ^Z4h2)aN zh~EQthyjn1(eI~$FtuHH!|x(iHU{9k40k5nPBwB)X@8Lo$P6u81EeoNOGRct%a-LM_4y3Ts z7ki0PWAO^Es6c%M*SSRn)2|NAoUsKyL%))uVx7?5lkrk`njxs4q@M~x+8%jr7xV;- z|KC=g3aTZO|y|g~oHXB6b42(|J_&fP2Y`*;L07H2d>{~JP zFNGl$MYUG(Qy3dR?9Bfdg8#peGRiVP8VYn@)6T1bj*v)s6q*7<6P(ZVm4ZnTA;rOHSd>P`_5uT0+azWdV`gIvLaJ1o*DB}&W6LCgX|BycgF5qd z!)}dT#A~4*6{1=Bd5VV(Qa2h4x9m#2X711z(ZN>i&cn`BopG*5P`CD*HfYiQmXNGk zhgqcHPBrJP$Z@PLZ4}d-8^}%X^LtUDHq&;~3}lUyrxxl@|IS={GP&6-qq&Iy5gKW- zC@$}`EEZd}DOSeSD+v_x5r_tpBWfN0gDa21p(@TAIrgWQFo7NO@slI6XOAML_lN;3 zEv~}LlMbGWKu}0s$tO-vR)wD!=olGcA?}vU;lRu4+Zf z?nCD7hBmA5`U9P#W8-*0V1=OT-NI0k&_`UZ87DbpYq_=DBdyNDchZ<|V1f%dbaa7i zf~R+6Xt%G)VXlM@8REfP3u#7UPadWYOBMsQ56fHRv!0p9R6q>Rbx!n|IY0goLb%{+ zzy|5WXk+(d@ChzOWatIV1lc1F!(uEOfEmMd;v`|$Kt3X2Uws;%@OV!E86PN?CeHV& z=4#TX{J8RWaH`)!J<8AUs#Ar{6Am^8M{S( zc%K7y2YbcLUz+*eDTXdthNE)Lm^P&*e^eV zilOS9)TVKgr9_^_M!TJ^44v<YF2NO=h(oOr5jYxVTxWk0XJ8n0{F_SOH%49WMk*Sg7`g6B(=^< z*rLAW;8I5;1?;Fh{N=f;kxjLpj}u^mD|k8lih|G4#}wEG1j`HIG( z8y;BMR3cE01e?(+k8NLR|Z+)#>qR^iMZc=BkcixWSKYmkaHpIFN?s%*74kc&wxwB zrtbYBGz9%pvV6E(uli6j)5ir%#lQkjb3dvlX*rw5tLv#Z>OZm@`Bf2t{r>u^&lRCg z11*w4A;Lyb@q~I(UQMdvrmi=)$OCVYnk+t;^r>c#G8`h!o`YcqH8gU}9po>S=du9c*l_g~>doGE0IcWrED`rvE=z~Ywv@;O-##+DMmBR>lb!~_7 zR`BUxf?+5fruGkiwwu|HbWP^Jzui=9t^Pmg#NmGvp(?!d)5EY<%rIhD=9w5u)G z%IE9*4yz9o$1)VZJQuppnkY)lK!TBiW`sGyfH16#{EV>_Im$y783ui)a;-}3CPRt- zmxO@Yt$vIOrD}k_^|B2lDb2%nl2OWg6Y)59a?)gy#YtpS+gXx?_I|RZ&XPO`M!yl7 z;2IS@aT4!^l`Tped5UGWStOw5PrH#`=se%(ox%gmJUBk18PsN$*-J8S%r51Y$i!4N zQ!rW%cgj44jA~_x%%smSTU2WG_W0c&PB$A5*kl8{$|865+lSIX~uyDT`uI7qnS!BPAg1Wwrc0e)8Usf zv9^E38H&hWSp5!@K8Qinl|)9 zEB?NMaxZK^GB!PUf1TBw+`H&jFSNI=Q@v5$Ryf-y^#IuXO#vsM5R+9@qz#z0fD0GP z9|Hj#E>?<=HTcsF$`xn`je~D&3kF1Qi%dfH{sKh!~(IpgjkDGQn zQx2F9rv{*x2$(@P9v?|JZY)^b9cd+SO6_1#63n-HAY3fE&s(G031g2@Q^a@63@o?I zE_^r%aUvMhsOi=tkW;}Shom;+Nc%cdktxtkh|>BIneNRGIK{m_1`lDB*U=m|M^HGl zWF#z8NRBduQcF-G43k2-5YrD}6~rn2DKdpV0gD%Kl{02J{G3<4zSJ1GFFSXFehumq zyPvyjMp2SLpdE5dG#@%A>+R3%AhLAwyqxjvGd{I7J`Iw{?=KKPRzyrdFeU}Qj{rm{351DoP_;vx zMo*s+!Gwgn;${(LXXO(xyI@$ULPZI|uzYR%`>MmW6Hcr1y2aM5b$grFwW_(9Fzz$Q z$&8dKNdWvBkK=iYWA|0}s1B7>8J$g*Ij_+S9vC1#jy~uA8nr)yY)a+ zoJ=e>Lp`7v3^tQN<&6UpDi{c1b}F~fJ$9r=p=@U^J_7bOck$5}ncVjYB0yEjbWrhe@E`j64yN3X?=k_F3BalH$aN zV=94?wDNv=BKLB<1*xU|65Zl!%51r5sHQ?qCggCw;$2QfCZ$lN40WPL=n^{Prf^QS zjbZ&1MRGgiZ2T)}DpiluFr#q*!AZJ$1v#d10YQ{>wQ5px!y28-1hCZ7lwvQnQYN*U zOg9BpvB0A$WUzFs+KWk1qLiGTrDT-0>DUpFl??l(FqWVz_3_Xzqg9vTpagp- zZcJ!5W?|0G%W|AJVVHJ7`u6@<4yyqMGHj@kpv`P+LV<)%PM__Rz&oq~t-*vV12@NR zoEVPz<2D>O==MlNI`;l8Gmv49&|1`FR!}2`NLRCqA{@`imLz6zrjS4ui0)O;!Pu&?KPAcX)?tDPS26uKvR(ry(p{6kiXPoZbnQ!vx6dLu zZCaj~Ocr$h##KqsD;9;ZiUwhmUd%5lrwczWr1Yn6V>+IK=>51;N7JDkrm1NY-ZBes z;FxeOTb^HAyA+~P2}WvSSu_fzt_K=(m4wUp%c*^hF zEJ+1dP0{0B8bryXR+qApLz43iu?ga<5QQxTa$1gMCBq0W=4|DTv4nY4T*-^Im%>U~ z)98;hc(d7vk0zAML$WnPWsqK>=O-FZSLI3_WQKr*PCK=(i6LelZ$$}XXrD5cb~VXz zT%egX>8e;KZs@jcD>cL9VP(Q}b0r~ST$Mc%mr1cC8mqRUQc|N^9@Weu$Z|KeczK7HhSFeFV0i)MQmwrn7CBL=p`_9n?nh320m}6-MSv3L7I*<*56GR zZ`zI^1zyC7F#*zVL@M)F2+oqxydaiQz?|ODmqs|Ub8%&KXk9P3P7<4tM?X{~!;Ygw zt=h7)AYGDO9F&wV=BhCyD9exr#YM_-<;Fo~iE>IBEXK$%;JCUAEr;lR&3S_DUy_E) z#!oCYdENVE9OaaeaIrPk-odMtvdFG;ocA#`L6AifMu0og^?Oy9F|Et9q6 z8;3_|9+Io@hqYoN;58x1K&OP!9Vd#dzhTRjB2kI?%31ceHb#Q~WqJV5lw;@b>4@Rd z={z1S`d05YdWC*RLc7sR0bVGSytn-a3`JZL3|d8KC?vj_70Vi4ohP9QbU&Q4?Zjd0 zSZA?KbqLBsJg(qj>fycto3`zN-)lDe4{Ij-QfoBn@rT_tTszA+CnM~xWmE(4zfpCQ z;zPJfl3=ctrggYM!KQg;V{J;utMMF9&BfOe!<{wU0ph?-VQ%cv3B%fFiW?6xBPdf0 zD-HhEU?0C`G@7e+b-=8fj=TP3mdz&SIQ}Nd`*G#DTz9Y@b zaoDF}Gx7ZhPzpDhi^fA7WZ)EAEFv;N2*bKp0T za0t<^1|Zc#`A+?s$!$8eO4CK~PUFECC3BwNR4f)!V&-Y>$xg(%T{MtrH|CPcO(Lf> zE_meE1?6S-qlV^p2fh! zT11Ub)hHw!_mpFDMIAFB`%Yal+`1IXV>b?%!q^Ps%8nh8wtjVGlF-!5x*D29WJ4=M zZ7X(QvKe$YZNgM(HibD7+VO5Q29?@HzS?k$c|3B@JI6dlLgu5S&LbU4=4p-Yn||z@ z4p05vq*k*pbOV9QjVTMp8`c$?t@~!$8&5AP_sz@tk%a$nWHMh-Gm{WS5+q)5W6pU# za@YZXJCLTpZ}zb=$HCYbIm->?Hu6XIBz_d7)n1+3eSLzGVoNQCTHcu9qS2@({0sxc zu<-mhx@Xz_*(S1DEL|d0`YV7uNevL*Y6|DAQmvSp{4DzPL@>hqJ?`FjvIU;<&}YEKDmFUGSBYjRmK{Km-1m%-t=fFfI9kV|POH|SxvO=P+><+1JK_lt5F6fTPf8PXU+lYEJz__** z&>`4F2F8EWE+k7ZsZx9%!?A56{lsk1juYw5zN)V+g$d^Q^Gm}fnHKA6L^36=`e;p% zp{;JD$X3%}O7qINR*2<>a422}_hmc=)-A7B-1#2v85jN5K31t0DtmqON-Dim`XIR; zOo`KRv)gtn?stp*`^f>}UDnGYGnJAbl(4srd>(5fo2#oqi>#bus86EHfeItFIu$+% z;lE|3gjQA`BXHEE5JdcjCoethN`@NEc~zm6CYf@LJ|hT^1>l}gRl7oDHMnw!*5*IC z@@Mi=gO=lZSnWln`dX^4Bd{9zYG{HNIX-87A#5OM%xu*%V?7K3j3CHcN*t!zNK4N4 z!U2?a>0`8m8}UQshILC0g6-k>8~;SRIJ?vQKDj z@U{DrstWIT7ufyRYox^&*IyHYb$3wtB}V^0sS|1OyK#sDc%sh+(gy&NT9j4Aa7J0C zPe$02TylMjad&|{_oe3`zx)Cqns?6qThYue6U=~j5+l0Po4`bX*&9V@a<-O;;vCzm z(af&;e<^}?5$7&MRW$eb*P< zX|33QmDvFSDFK-qMz|RF|Eedum@~W zt~8C1@i8@LammTr)rAgKm8X_SczCg@+@LeWpcmx;VL;iLQJ;t%Z*|XbNWUnHX|o=Q z%bsXc%bw=pk~8%3aV-w(7E$co9_cHQ$!}Ep6YcoCb7~GQBWl#4D!T8A5!P*tSl4FK zK2CX0mjmosg6TSK@-E-He{dm0?9h{&v~}OX15xgF<1-w4DCypYo22%@;uRq`ZFld- z{Uqof@a@P5dW@kfF-`1B1(!R>(DHb&$UXY%Gd+6r?w8klhP&ldzG*6#l#VuM&`)ki z)f$+Rp?YYog9u==<#MC%1daG#%3EOX9A{7$`_(s#_4mV`xZaB+6YlX`H4{}vq;)TF zo~fR@do6EZIR?413A$V6o^fq&QV7P(bB(9m1969szOosyhZRYciAWXe4@u-}s(LeJpuIkSx)XvjXmvVEseG zJvWN4s|$6r;s(3F+cgeh4DMEq??h!$eb^5h#`whT5d03qfYpol8dCim)A^NG1-H}} z!b)V8DTL2Q8@R2p`y4@CeSVj9;8B5#O?jfl-j<$Quv?Ztwp*)GvQ~|W8i6?-ZV@Lf z8$04U_1m{2|AIu+rd8KW`Qk|P1w(}d%}cjG6cxsTJ3Y&*J^_@bQgXwILWY7w zx+z)v81rZv-|mi>y#p$4S7AA760X?)P&0e{iKcWq4xvv@KA@EWjPGdt8CKvh4}p}~ zdUVzuzkBlU2Z+*hTK214><61~h~9zQ3k+-{Pv~w`#4|YdjTFKc{===9Ml7EMFmE!f zH}U3O{Z`DuJrBZbz~OjSVlD6uZSEeNK8epja_LanEh8v;_$Eg9?g*9ihMoat$#qd^ z?;x?a*y3-pW#6|kF^<$w;2^~s!fc;3D~#&#WYZfK@3;bO{MvmN?>qy%_%v`BVCgfC zdwL~(H14Gr6w(1CX|R;zhZh%?*Q{hxJH`MV2)@Jg$pbqjZeL+LO7^vwgi!@3yn@NT zU91-{;BWIi8bV-j-YR|A9Qs?M?e7Ru&Onl1(Sz(kxAw?LEbd+Le%Z43rZgb2h2m|e z^rblc;4r+}?@tC(YIBB_qpQL?_kg{;zO#6JD9{;HSUgf@zIZ)}Bh4wFZIs>meSd}f z4iF~nD$KAV6CVEw+{YOPrW~~y~Y=?snG4dE3edN$~SXh`!c_F zUsQ1M;ARz&v0mIbfP}aLWZ&cBPU+DU{l+0}_>9DZGL{@}lF6QCtgAg;EWUu`D$Evm znblG}kC!}Mw)bR~U;+S}T9TVc6lXWR!LNMm)nmxr*ORkv#&UO$_WQpt0WdX{A=bjC zV^lB~(r;y!C4$Rk0fWUR|09O?KBos@aFQjUx{ODABcj}h5~ObwM_cS>5;iI^I- zPVEP9qrox2CFbG`T5r_GwQQpoI0>mVc_|$o>zdY5vbE~B%oK26jZ)m=1nu_uLEvZ< z8QI_G?ejz`;^ap+REYQzBo}7CnlSHE_DI5qrR!yVx3J1Jl;`UaLnKp2G$R__fAe;R(9%n zC)#)tvvo-9WUBL~r_=XlhpWhM=WS6B0DItw{1160xd;M(JxX_-a&i%PXO@}rnu73_ zObHBZrH%R!#~pjEp~P?qIj4MdAx@sv;E96Doi$eO-~)oUz%Z0Tr4K`-jl06Il!9{s zdjF*1r{XU?)C(%XKPm;UnpnDGD%QL3pgo0ust~+sB0pa|v37>E1dp*Odn)n=DY;5j zDzSAkU9B6F$;|##_mrDe#%hd7pC1u`{9ZKeDdtkyl&4>H=e)Fq@}$UffPt1#cjYZg zd%O%xpg4~brEr>AnKT)kF@`cdX4tMlZ#Vk!l1Xz!G970p`Gkv^lk-|>jmt0W5Wu6woGf?hNA zXO2?BG)<{`NsYAY#3|L^x*=rS7uWU~s<*UhTC8AYc#lGP-=Aw1I)@y(<` znQb^nL~$rlDbsdAc4nc#{+$_;Z4iY;Pi0i9Q;>ZB3+IjWLg_r40-Fso^xF<*_s7Tj zujFrMH{vW3PmCndjQIscnQE%`Qj|E2kidi#c&PcWIMyH+e#7!l`<$_)*pDP$!49pY6w!bN)j8~A1wV%gIakf+vA04 zV)_Q=QMPSj6$M2Ar#KhhxsbZUOq3nZHh8m0?Fr}I6N(Fk zkhXM(f57yOa8vn^97J+g9ISPa=-**6^8ZX&g=z+m&6~x<1>)MyM&tpbWhSf8#+Pcd4rVK#)NSw>1eLKHTO z44A@sc_}Ypi#ggFRbDRFV(IhOnRU&XPrQYh9`mVMo-^U$&AwsXooSRUFqJ7)XUXCK zFpt;gJ}9QTN9xy9$=3OnRkjgUuQZ`X)!}LBm~WUIEKuK-Z%}f?2?+MKucWU<3)>9G zxsz~2pHut1AmH<@66;LdCB9+dSpojE4ggrYS?%icv*Rpi?G0Q($^`(g<1&Z){O_5B$@f#;I2-+Qa1P$a@=u-vOY5vqo z|6G67X;*A|V86ZET9OpFB&02twZtc2K}~ASoQpM_p{vJ{-XvA8UmQa4Ed%fS{D@g( zr_aY0gKw*=2SIGznXXKFo$r0x3)@bq8@4od^U(L0-jvTsK@qYOWX?2G_>N+?;r{TU2{M>V0zid zB_Zu?WSnRl@k?oE*gsgv;jH@+ z-}BDGyR-ls7$dz{e( ztv7lI2|OxNkLD4zc3xGA`!d7LiSdOys4H!8aA(_c0Nm*uLjS4TW%Z3v>am1nwQ_lI zIs85Uufd;cv-(4wi(Js;QsL#|qdv)n;r_?puaK*1>zTC@d=#sK+q1YF_Q(5B%%3TtI8&bNs_e8vIb;oc|Rk`F~u?|A?jj{c={?{Env{mW#q@8 z)#WEgt4B6b&X2?o3=b`ilz;)-h$t4;hsxPDo-%5C(7m#c9tZF-U`vcx0HnVtf_X(}4Tg}4wx(=y!@T7{)4;I_p95mBhikg-|U9z35q`|!1+Zz@97 z(PFE5jCv|=t;^=(CLqYp)k90rV4ZSiFDAhD8YOCzv{}1WDuB?epORibW36);q(Aig ze27@D?lN-ZyjuB4GsebA$;+(KGiOtCe6Bfd%GKRty>dBS1GUe}MXgnu61UdgO=m1& zE(eECPF_%J-lU{;R)eQJot;;}Wch$-8Z|lxN*AAdc;bkpbD`W}F=Z}^Cy(SKyfF#+ zQSalA%JDDAu|77$M3E|kv==3vx~pFPw_<+9xgcE#oigh*>#QsA2}sTYO7uY(h@dhR zHJBi^bb-`1?<1cGFZJa8Akzs{H^$N<)5@hlXeKwt9hD5^5K&`pdHOI92p<7XhS?>| z(5h9KYctN|H+W~Xh2N4W+yjMyBm(AdewjX?PBuRU$^J zS#+U($K6rhFFzf z0q*kJ>B6xI1qAti?H@X@dxtB7_vT+Nj@PNxr?CSK#xqE6jh5S{`nH#zzvjOId=i1X zK(Yjl!7KF(73GXYLVkQA5irn|v-ArCqwi)CM8X&m!#@NQ3bqmQlfurU4qT`zl_m^C zhpk?mfVvy9L|)*+bW8&NY4lG$@0_PKfO9+~(zrbn?wECGi7472W{H&dRPZum^Qf z73C-TR6$#q>XJgYnUgV!WkbmRas;`TY#7CxPXIEGwT6VPBDKbyr#|C2M%q|7l#Ql< zuM}j=2{D+?SxT8?ZJn&Z%cRN8Gu@y(`zV(lfj1T%g44(d#-g&@O0FL5;I9=?bW>!M z%c3J&e}GThdean-<||jUh zlLP`UeKBhhrQ?HHjM3}kfO7Z=EKB%+rs*t+nuBoeuD2yk%n32SA?-s)4+DsTV7U&K zyKQO2b2*tQT}#((=#fkb%hkRkt^%tY&VK$hcs91+hld zJ%lgC!ooILC&|(Z9$zzk=Q0*%&l7wwyf%nv=`C=OcPjb|Q%@9*XkPGFrn+bxp?t^D z!_qO=e-;bnT)^0d|Ex9X&svN9S8M&R>5l*5Df2H@r2l)VfBO@LqeVw`Fz6TSwAt^I z5Wu6A>LNnF7hq4Ow=7D7LEDv3A))d5!M=lT3ConlFN`5eTQMexVVs* zH0tx-*R+-B@&Lp`0V4j6Uy=LJmLQRY_6tH4vnV{_am%kkv|{CYkF}4Wn6U+|9Xre$ zJkO;_=dtw`@aEs|^GlO-zvpp-73H;PYk}V5RrH83G4SVkRJ0YSluQa8pKejcqB4u~ z^9^lDR|?7vEo|jITtaIFI6}1;vTI6n(d0kDGQUJuk>>sqdd7#VBF;?_dM5i<+VMEq zc>habJK}_0eEsOkdwv48d43jKMnqYFMnYDU&c?vi#Fp+S)sxo1-oVJ*g!X^^K! z>z!G8?KfU{qOnLHhaEF4QRHgOpfvoo7@=FG(2ZefYJk- zZuA9ubiTTP9jw9Uzpx8FfJBFt+NNE9dTlM!$g$|lTD za4LMNxWhw8!AV(x;U`IV-(bK@iQ%#QSmq8D$YqLgt?V#|~% z;{ST}6aQbOoewMKYzZT@8|Qq z@9SNBu1UErolMjrhJW-Id&7y<0I<+Z-lr`IHMh1;M)n@g|hx_T-maO`s{Tuhax}EjC zS;1kdL*A3BW5YZXgD|0zm)g3_3vMs>5xgHUhQDl19lfQWMcfLTsw$)amgDs>bW*Oe+$UK^`ioL%F0Ua5vb%II+EGS>*I zw)AmqcWBZpWH&Aswk_FJT=J|^Gn=MfnDTIzMdnoRUB91MeW?e>+C)g3_FDN8rN$(? zL+kH!*L}rq`MK`KDt^v4nUJg3Ce-`IW0Ph0?|}Puq5WIS_a7iEO;~mGQqqo=Ey;ND zhBXA^$ZrCc#&0}dMA&@)&TCq5PMzgJPafZCg-6$R zRqJ2+_t+dGUAY@~xPzU3`od7-(8nnuMfM-4#u`Q~`l-CUGC7u*^5VwH`ot;Ck#R1% zRr%?;!NrB$w^}NW=GGR}m!3a9bh#wXrq?fF7j-IS?E_!GaD3KYzcXhCUHhjEl-6b# zCmIF#4y@HN=^#uIz zRFl8D)Ri1<(Kr~Hoi_MtXWP8^AyTKxi1)ew88bV{*Ok8w8YLXBFW0sRJ<(vU{$ym| zz)feLQbz3k;_}2_{-bW`h~t&2$ObtlbS?k2k|5Kbu?FZLDMTVW_Z6p#A)c)`3DD?a*hxHS2Zj zcIiebfsINfWvwY7Z{YOlIQ61b`j=%6{>MPs+`()Q{wq0z0?|jwRN(1IrMQsj40BHx zvBC_Xfcr;55&}MeoP_@#nz$avCh%FJfE5NNAE~fW@L7~f8Y=?Wno31128EYOK8+O! zc4Vaj-DCsB6CPH$?pQQVbb_(tg^x{$STYM_WKLtrh-_-Hq-M%Ubpt6$mCHY!B{ISD zz}grIo^bNVDw4={SA2*nDNq5`e@ZO5r4TbQpHM)~qfD9!s0h(Jf>vYd;I~j<2fD4)_>ctbwNX6S*8>i^*4 zYKI5<4}d;hM!!N|A$@eg09J|HV;!UUVIau_I~dxZp#?a3u0G)pts6GKdCNk>FKxdh_`Xu!>zO3Kv?u+W6cYJPy!@=PuY868>3|Zg} z$7galV~M`d!q(`I{;CJsq6G9>W0}H6gVY`q7S@9s8ak1r{>}*Q0JyH&f!f8(NZxhC zkn|KS64r^A1fniFel2KkxYByk%erCx9UgFLI)`yuA)X z8SU?6kj!numPNCAj}>1ipax(t{%rxU;6`(Nqt$~Z4~76TQ$9d8l`yJ}rniII%HbH= zlS_7o!qB{55at^>N!Voer%)`KMh9Yd@Z?~nc19*hs)NGN954`O9zA&&vJHbm&|D@E za(&z6A=3NfC;>I)hlI@ulP8E@W-ziGe{iCf_mHvWGldxw8{ng-hI({EtOdALnD9zG ze)fU?I(DNt)Bzdd9Cs^>!|+2!xv1SK=I zJ+y_;=Sq-zqD~GKy@{5(my&aPgFfGY&_mayR_)?dF_^Fwc-n!UAG+fQQGfjWE-1MF YM{}PByk10KD_nuQ4E7Du?}+~TKh4V)`~Uy| literal 0 HcmV?d00001 diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..b7cb93e --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar diff --git a/mvnw b/mvnw new file mode 100644 index 0000000..8a8fb22 --- /dev/null +++ b/mvnw @@ -0,0 +1,316 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`\\unset -f command; \\command -v java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..1d8ab01 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,188 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..ee0f15b --- /dev/null +++ b/pom.xml @@ -0,0 +1,220 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.12 + + + com.xkrs + SmartUrbanRural + 0.0.1-SNAPSHOT + SmartUrbanRural + Demo project for Spring Boot + + 5.19 + 0.11.2 + 4.5.12 + 6.1.5.Final + 5.4.20.Final + UTF-8 + UTF-8 + 11 + 11 + 11 + 1.2.76 + 2.10.5 + 2.10.5 + 4.5.2 + 2.0.4 + 4.4.3 + 0.4.0 + + + + + org.springframework.boot + spring-boot-starter-aop + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-devtools + true + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-webflux + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-logging + + + + + + org.springframework.boot + spring-boot-starter-log4j2 + + + org.apache.commons + commons-pool2 + + + org.postgresql + postgresql + + + mysql + mysql-connector-java + + + io.jsonwebtoken + jjwt-api + ${jjwt.version} + + + io.jsonwebtoken + jjwt-impl + ${jjwt.version} + runtime + + + io.jsonwebtoken + jjwt-jackson + ${jjwt.version} + runtime + + + nl.basjes.parse.useragent + yauaa + ${yauaa.version} + + + org.apache.httpcomponents + httpmime + ${httpmime.version} + + + org.hibernate.validator + hibernate-validator + ${hibernate-validator.version} + + + org.hibernate + hibernate-spatial + ${hibernate-spatial.version} + + + com.alibaba + fastjson + ${fastjson.version} + + + + + io.springfox + springfox-swagger-ui + ${springfox-swagger-ui.version} + + + + io.springfox + springfox-swagger2 + ${springfox-swagger2.version} + + + + net.sf.json-lib + json-lib + 2.4 + jdk15 + + + org.apache.httpcomponents + httpclient + ${httpclient.version} + + + cn.hutool + hutool-all + ${hutool-all.version} + + + io.tus.java.client + tus-java-client + ${tus-client.version} + + + + org.springframework.boot + spring-boot-starter-activemq + 1.5.0.RELEASE + + + + org.apache.activemq + activemq-pool + 5.15.0 + + + + org.springframework.boot + spring-boot-starter-websocket + provided + + + + + + + + src/main/java + + **/*.properties + **/*.xml + + false + + + src/main/resources + + **/*.properties + **/*.xml + **/*.txt + + false + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/src/main/java/com/xkrs/SmartUrbanRuralApplication.java b/src/main/java/com/xkrs/SmartUrbanRuralApplication.java new file mode 100644 index 0000000..bb6377e --- /dev/null +++ b/src/main/java/com/xkrs/SmartUrbanRuralApplication.java @@ -0,0 +1,20 @@ +package com.xkrs; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.jms.annotation.EnableJms; + +/** + * 启动类 + * @author XinYi Song + **/ +@SpringBootApplication +//启动消息队列 +@EnableJms +public class SmartUrbanRuralApplication { + + public static void main(String[] args) { + SpringApplication.run(SmartUrbanRuralApplication.class, args); + } + +} diff --git a/src/main/java/com/xkrs/common/config/BeanConfig.java b/src/main/java/com/xkrs/common/config/BeanConfig.java new file mode 100644 index 0000000..93c5458 --- /dev/null +++ b/src/main/java/com/xkrs/common/config/BeanConfig.java @@ -0,0 +1,70 @@ +package com.xkrs.common.config; + +/** + * @Author: XinYi Song + * @Date: 2022/2/8 14:16 + */ +import org.apache.activemq.ActiveMQConnectionFactory; +import org.apache.activemq.command.ActiveMQQueue; +import org.apache.activemq.command.ActiveMQTopic; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jms.config.JmsListenerContainerFactory; +import org.springframework.jms.config.SimpleJmsListenerContainerFactory; +import org.springframework.jms.core.JmsMessagingTemplate; +import javax.jms.ConnectionFactory; +import javax.jms.Queue; +import javax.jms.Topic; + +@Configuration +public class BeanConfig { + + @Value("${spring.activemq.broker-url}") + private String brokerUrl; + + @Value("${spring.activemq.user}") + private String username; + + @Value("${spring.activemq.topic-name}") + private String password; + + @Value("${spring.activemq.topic-name}") + private String topicName; + + @Value("${spring.activemq.topic-name1}") + private String topicName1; + + @Bean(name = "topic") + public Topic topic() { + return new ActiveMQTopic(topicName); + } + + @Bean(name = "topic1") + public Topic topic1() { + return new ActiveMQTopic(topicName1); + } + + @Bean + public ConnectionFactory connectionFactory(){ + return new ActiveMQConnectionFactory(username, password, brokerUrl); + } + + @Bean + public JmsMessagingTemplate jmsMessageTemplate(){ + return new JmsMessagingTemplate(connectionFactory()); + } + + /** + * 在Topic模式中,对消息的监听需要对containerFactory进行配置 + * @param connectionFactory + * @return + */ + @Bean("topicListener") + public JmsListenerContainerFactory topicJmsListenerContainerFactory(ConnectionFactory connectionFactory){ + SimpleJmsListenerContainerFactory factory = new SimpleJmsListenerContainerFactory(); + factory.setConnectionFactory(connectionFactory); + factory.setPubSubDomain(true); + return factory; + } +} diff --git a/src/main/java/com/xkrs/common/config/CorsConfig.java b/src/main/java/com/xkrs/common/config/CorsConfig.java new file mode 100644 index 0000000..7f8c32f --- /dev/null +++ b/src/main/java/com/xkrs/common/config/CorsConfig.java @@ -0,0 +1,23 @@ +package com.xkrs.common.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import javax.annotation.Resource; + +/** + * 系统跨域配置 + * @author tajochen + */ +@Configuration +public class CorsConfig implements WebMvcConfigurer { + + @Resource + private CorsInterceptor corsInterceptor; + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(corsInterceptor); + } +} diff --git a/src/main/java/com/xkrs/common/config/CorsInterceptor.java b/src/main/java/com/xkrs/common/config/CorsInterceptor.java new file mode 100644 index 0000000..e2103c5 --- /dev/null +++ b/src/main/java/com/xkrs/common/config/CorsInterceptor.java @@ -0,0 +1,29 @@ +package com.xkrs.common.config; + +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * 跨域处理 + * @author tajochen + */ +@Component +public class CorsInterceptor implements HandlerInterceptor { + + @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + + //添加跨域CORS + response.setHeader("Access-Control-Allow-Origin", "*"); + response.setHeader("Access-Control-Allow-Credentials", "false"); + response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); + response.setHeader("Access-Control-Allow-Headers", "Content-Type , Authorization," + + "Accept,Origin,X-Requested-With"); + response.setHeader("Access-Control-Max-Age", "216000"); + response.setHeader("Content-Type","application/json;charset=UTF-8"); + return true; + } +} + diff --git a/src/main/java/com/xkrs/common/config/MvcConfig.java b/src/main/java/com/xkrs/common/config/MvcConfig.java new file mode 100644 index 0000000..edce463 --- /dev/null +++ b/src/main/java/com/xkrs/common/config/MvcConfig.java @@ -0,0 +1,37 @@ +package com.xkrs.common.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * WebMVC配置 + * @author Tajochen + */ +@Configuration +public class MvcConfig implements WebMvcConfigurer { + + /** + * 放行跨域请求 + */ + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOrigins("*") + .allowedMethods("*") + .allowedHeaders("*"); + } + + /** + * 定时任务线程池更改,防止多个任务并行 + */ + @Bean + public TaskScheduler taskScheduler() { + final ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setPoolSize(5); + return scheduler; + } +} diff --git a/src/main/java/com/xkrs/common/encapsulation/EncapsulationObejct.java b/src/main/java/com/xkrs/common/encapsulation/EncapsulationObejct.java new file mode 100644 index 0000000..e11fea7 --- /dev/null +++ b/src/main/java/com/xkrs/common/encapsulation/EncapsulationObejct.java @@ -0,0 +1,59 @@ +package com.xkrs.common.encapsulation; + +import java.io.Serializable; + +/** + * 输出信息对象 + * @author tajochen + * @param + */ +public class EncapsulationObejct implements Serializable { + + /** + * 状态码 + */ + int status; + + /** + * 提示信息 + */ + String msg; + + /** + * 数据 + */ + T data; + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + @Override + public String toString() { + return "EncapsulationObejct{" + + "status=" + status + + ", msg='" + msg + '\'' + + ", data=" + data + + '}'; + } +} diff --git a/src/main/java/com/xkrs/common/encapsulation/OutputEncapsulation.java b/src/main/java/com/xkrs/common/encapsulation/OutputEncapsulation.java new file mode 100644 index 0000000..9bee083 --- /dev/null +++ b/src/main/java/com/xkrs/common/encapsulation/OutputEncapsulation.java @@ -0,0 +1,96 @@ +package com.xkrs.common.encapsulation; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.MessageSource; +import org.springframework.context.support.ResourceBundleMessageSource; +import org.springframework.stereotype.Component; +import org.springframework.validation.FieldError; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Properties; + +/** + * 输出信息封装 + * @author tajochen + */ +@Component +public class OutputEncapsulation { + + private static final Logger logger = LoggerFactory.getLogger(OutputEncapsulation.class); + + /** + * 读取多国语言文件 + * @return + */ + public static MessageSource messageSource() { + Properties properties = new Properties(); + // 使用ClassLoader加载properties配置文件生成对应的输入流 + InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("application.properties"); + // 使用properties对象加载输入流 + try { + properties.load(in); + } catch (IOException e) { + e.printStackTrace(); + } + ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); + messageSource.setBasename("i18n/messages"); + messageSource.setDefaultEncoding("UTF-8"); + return messageSource; + } + + /** + * 封装输出数据 + * @param promptMessageEnum + * @param obj + * @return + */ + public static String outputEncapsulationObject(PromptMessageEnum promptMessageEnum, Object obj, Locale locale) { + + EncapsulationObejct encapsulationObejct = new EncapsulationObejct(); + encapsulationObejct.setStatus(promptMessageEnum.getCode()); + encapsulationObejct.setMsg(messageSource().getMessage(promptMessageEnum.getText(),null,locale)); + encapsulationObejct.setData(obj); + + ObjectMapper objectMapper = new ObjectMapper(); + // 忽略无法转换的对象 + objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS,false); + // 忽略json字符串中不识别的属性 + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + // 解决jackson无法反序列化LocalDateTime的问题,引入jsr310标准 + JavaTimeModule javaTimeModule = new JavaTimeModule(); + objectMapper.registerModule(javaTimeModule); + objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + String strByEo = ""; + try { + strByEo = objectMapper.writeValueAsString(encapsulationObejct); + } catch (JsonProcessingException e) { + e.printStackTrace(); + logger.warn(e.toString()); + } + return strByEo; + } + + /** + * 输出请求值检验错误信息 + * @param fieldErrors + * @return + */ + public static String outputEncapsulationErrorList(List fieldErrors, Locale locale){ + List errorMsg = new ArrayList<>(); + for (FieldError fieldError : fieldErrors) { + String errMessage = fieldError.getDefaultMessage().subSequence(1,fieldError.getDefaultMessage().length()-1).toString(); + errorMsg.add(messageSource().getMessage(errMessage,null,locale)); + } + return outputEncapsulationObject(PromptMessageEnum.PARAM_ILLEGAL,errorMsg,locale); + } +} diff --git a/src/main/java/com/xkrs/common/encapsulation/PromptMessageEnum.java b/src/main/java/com/xkrs/common/encapsulation/PromptMessageEnum.java new file mode 100644 index 0000000..74d5bdb --- /dev/null +++ b/src/main/java/com/xkrs/common/encapsulation/PromptMessageEnum.java @@ -0,0 +1,65 @@ +package com.xkrs.common.encapsulation; + +/** + * 提示信息枚举 + * @author tajochen + */ +public enum PromptMessageEnum{ + + // 执行成功 + SUCCESS(0, "sys.message.success"), + + + // 用户权限错误或非法操作: 1001-1999 + USER_NOT_LOGGED(1001, "sys.message.user.not_logged_in"), + USER_LOGIN_ERROR(1002, "sys.message.user.login_error"), + USER_ACCOUNT_FORBIDDEN(1003, "sys.message.user.account_forbidden"), + USER_ACCOUNT_NOT_ACTIVATED(1004, "sys.message.user.account_not_activated"), + USER_HAS_OVERTIME(1005, "sys.message.user.overtime"), + USER_NO_PERMISSION(1006,"sys.message.user.no_permission"), + USER_ALREADY_LOGGED(1007, "sys.message.user.already_logged"), + + // 请求参数错误或非法:2001-2999 + PARAM_NULL(2001, "sys.message.param.null"), + PARAM_ILLEGAL(2002, "sys.message.param.illegal"), + + // 数据返回错误:3001-3999 + DATA_NONE(3001, "sys.message.data.none"), + + DATA_WRONG(3002, "sys.message.data.wrong"), + DATA_EXIT(3003,"sys.message.exit"), + + // 操作失败:4001-4999 + PROCESS_FAIL(4001,"sys.message.process.fail"), + PROCESS_OVERTIME(4002,"sys.message.process.overtime"), + FILE_EXISTS(4003,"sys.message.file.exists"), + FILE_WRITE_ERROR(4004,"sys.message.file.write.error"), + FILE_READ_ERROR(4005,"sys.message.file.read.error"), + + // 系统内部错误或异常:5001-5999 + SYSTEM_INNER_ERROR(5001,"sys.message.system.inner_error"), + SYSTEM_ABNORMAL(5002,"sys.message.system.abnormal"), + SYSTEM_BUSY(5003,"sys.message.system.busy"), + SYSTEM_MAINTAIN(5004,"sys.message.system.maintain"), + + // 数据库错误:6001-6999 + DATABASE_ERROR(6001,"sys.message.database.error"); + + private int code; + + private String text; + + private PromptMessageEnum(int code,String text) { + this.code = code; + this.text = text; + } + + public String getText() { + return this.text; + } + + public int getCode() { + return this.code; + } + +} diff --git a/src/main/java/com/xkrs/common/tools/GetTokenTool.java b/src/main/java/com/xkrs/common/tools/GetTokenTool.java new file mode 100644 index 0000000..7ed71db --- /dev/null +++ b/src/main/java/com/xkrs/common/tools/GetTokenTool.java @@ -0,0 +1,48 @@ +package com.xkrs.common.tools; + +import com.alibaba.fastjson.JSONObject; +import com.xkrs.util.Md5Util; +import com.xkrs.util.RequestUtil; + +import java.util.Map; + +/** + * @Author: XinYi Song + * @Date: 2022/2/8 8:41 + */ +public class GetTokenTool { + public static String getToken(){ + String url1 = "https://111.26.161.203:443/admin/API/accounts/authorize"; + String json1 = "{\n" + + " \"userName\": system,\n" + + " \"ipAddress\": \"\",\n" + + " \"clientType\": \"WINPC\"\n" + + "}"; + // 第一次鉴权 + String doPostJson = RequestUtil.doPostJson(url1, json1); + Map map = JackSonTool.json2map(doPostJson); + String randomKey = (String) map.get("randomKey"); + + String admin123 = Md5Util.stringToMd5("admin123"); + String stringToMd5 = Md5Util.stringToMd5("system" + admin123); + String toMd5 = Md5Util.stringToMd5(stringToMd5); + String md5 = Md5Util.stringToMd5("system:" + "DSS:" + toMd5); + String signature = Md5Util.stringToMd5(md5 + ":" + randomKey); + + String url2 = "https://111.26.161.203:443/admin/API/accounts/authorize"; + + JSONObject jsonObject = new JSONObject(); + jsonObject.put("userName","system"); + jsonObject.put("randomKey",randomKey); + jsonObject.put("mac",""); + jsonObject.put("encryptType","MD5"); + jsonObject.put("ipAddress",""); + jsonObject.put("signature",signature); + jsonObject.put("clientType","WINPC"); + // 第二次鉴权 + String postJson = RequestUtil.doPostJson(url2, jsonObject.toString()); + Map map1 = JackSonTool.json2map(postJson); + String token = (String) map1.get("token"); + return token; + } +} diff --git a/src/main/java/com/xkrs/common/tools/GetVideoTool.java b/src/main/java/com/xkrs/common/tools/GetVideoTool.java new file mode 100644 index 0000000..3118cb0 --- /dev/null +++ b/src/main/java/com/xkrs/common/tools/GetVideoTool.java @@ -0,0 +1,58 @@ +package com.xkrs.common.tools; + +import com.alibaba.fastjson.JSONObject; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.xkrs.util.RequestUtil; + +/** + * 获取视频连接 + * @Author: XinYi Song + * @Date: 2022/2/9 16:25 + */ +public class GetVideoTool { + + public static String getVideo() throws JsonProcessingException { + String token = GetTokenTool.getToken(); + String url = "https://111.26.161.203:443/admin/API/SS/Record/GetAlarmRecords?token=" + token; + JSONObject jsonObject = new JSONObject(); + jsonObject.put("clientType","WINPC_V1"); + jsonObject.put("clientMac","30:9c:23:79:40:08"); + jsonObject.put("clientPushId",""); + jsonObject.put("project","PSDK"); + jsonObject.put("method","SS.Record.GetAlarmRecords"); + JSONObject jsonObject1 = new JSONObject(); + jsonObject1.put("alarmCode","{8348AB45-93EF-E14B-BD85-F8FCB737C655}"); + jsonObject1.put("optional","/admin/API/SS/Record/GetAlarmRecords"); + jsonObject.put("data",jsonObject1); + String s = RequestUtil.doPostJson(url, jsonObject.toString()); + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode jsonNode = objectMapper.readTree(s); + JsonNode data = jsonNode.get("data"); + JsonNode records = data.get("records").get(0); + + String urlVideo = "/admin/API/SS/Playback/StartPlaybackByFile?token=" + token; + JSONObject jsonObject2 = new JSONObject(); + jsonObject2.put("ssId",records.get("ssId").asText()); + jsonObject2.put("optional",urlVideo); + jsonObject2.put("fileName",records.get("recordName").asText()); + jsonObject2.put("startTime",records.get("startTime").asText()); + jsonObject2.put("endTime",records.get("endTime").asText()); + jsonObject2.put("recordType",records.get("recordType").asText()); + jsonObject2.put("diskId",records.get("diskId").asText()); + jsonObject2.put("recordSource",records.get("recordSource").asText()); + jsonObject2.put("channelId",records.get("channelId").asText()); + jsonObject2.put("streamId",records.get("streamId").asText()); + jsonObject2.put("enableRtsps","0"); + jsonObject2.put("nvrId",""); + JSONObject jsonObject3 = new JSONObject(); + jsonObject3.put("data",jsonObject2); + String s1 = RequestUtil.doPostJson("https://111.26.161.203:443"+urlVideo, jsonObject3.toString()); + ObjectMapper objectMapper1 = new ObjectMapper(); + JsonNode jsonNode1 = objectMapper1.readTree(s1); + JsonNode data1 = jsonNode1.get("data"); + String url1 = data1.get("url").asText(); + return url1; + } +} diff --git a/src/main/java/com/xkrs/common/tools/JackSonTool.java b/src/main/java/com/xkrs/common/tools/JackSonTool.java new file mode 100644 index 0000000..e174b4e --- /dev/null +++ b/src/main/java/com/xkrs/common/tools/JackSonTool.java @@ -0,0 +1,174 @@ +package com.xkrs.common.tools; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * 序列化工具 + * @author tajochen + */ +public class JackSonTool { + + private static final Logger logger = LoggerFactory.getLogger(JackSonTool.class); + + private JackSonTool() { + } + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + static { + OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL); + OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + OBJECT_MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + OBJECT_MAPPER.configure(JsonParser.Feature.ALLOW_COMMENTS, true); + OBJECT_MAPPER.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); + OBJECT_MAPPER.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); + OBJECT_MAPPER.getSerializerProvider().setNullValueSerializer(new JsonSerializer<>() { + @Override + public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { + jsonGenerator.writeString(""); + } + }); + // 解决jackson无法反序列化LocalDateTime的问题,引入jsr310标准 + JavaTimeModule javaTimeModule = new JavaTimeModule(); + OBJECT_MAPPER.registerModule(javaTimeModule); + OBJECT_MAPPER.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + } + + /** + * 序列化 + * @param obj + * @param + * @return + */ + public static String encode(T obj) { + if (Objects.isNull(obj)) { + return null; + } + try { + return OBJECT_MAPPER.writeValueAsString(obj); + } catch (Exception e) { + logger.error("json encode error, obj={}", obj, e); + return null; + } + } + + /** + * 反序列化 + * @param json + * @param valueType + * @param + * @return + */ + public static T decode(String json, Class valueType) { + if (!json.isEmpty() && !Objects.isNull(valueType)) { + try { + return OBJECT_MAPPER.readValue(json, valueType); + } catch (Exception e) { + logger.error("json decode fail,jsonString={}, type={}", json, valueType.getName(), e); + } + } + return null; + } + + /** + * 反序列化成list + * @param + * @param json + * @param clazz + * @return + */ + public static List decode2List(String json, Class clazz) { + if (!json.isEmpty() && !Objects.isNull(clazz)) { + try { + return OBJECT_MAPPER.readValue(json, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, clazz)); + } catch (Exception var3) { + logger.error("json decode2list fail,json={},classType={}", json, clazz.getName(), var3); + } + } + return null; + } + + /** + * 字符串转换为 Map + * + * @param jsonString + * @return + * @throws Exception + */ + public static Map json2map(String jsonString) { + + try { + return OBJECT_MAPPER.readValue(jsonString, Map.class); + } catch (JsonProcessingException e) { + e.printStackTrace(); + return null; + } + } + + /** + * 字符串转换为 Map + */ + public static Map json2map(String jsonString, Class clazz) { + Map map = null; + try { + map = OBJECT_MAPPER.readValue(jsonString, new TypeReference>() { + }); + } catch (JsonProcessingException e) { + e.printStackTrace(); + return null; + } + Map result = new HashMap(128); + for (Map.Entry entry : map.entrySet()) { + result.put(entry.getKey(), OBJECT_MAPPER.convertValue(entry.getValue(), clazz)); + } + return result; + } + + /** + * 将一个JSON字符串转换为T对象 + * @param json + * @param typeReference + * @return + */ + public static T toObject(String json, TypeReference typeReference) { + try { + return OBJECT_MAPPER.readValue(json, typeReference); + } catch (Exception e) { + throw new RuntimeException("failed to convert string to object", e); + } + } + + /** + * map转clazz对象 + * @param map + * @param clazz + * @param + * @return + */ + public static T mapToObject(Map map, Class clazz) { + String json = null; + try { + json = OBJECT_MAPPER.writeValueAsString(map); + return OBJECT_MAPPER.readValue(json, clazz); + } catch (Exception e) { + logger.error("json map2object fail,map={},ClassName={}", json, clazz.getName()); + return null; + } + } + +} diff --git a/src/main/java/com/xkrs/controller/EquipmentController.java b/src/main/java/com/xkrs/controller/EquipmentController.java new file mode 100644 index 0000000..d9ba7e7 --- /dev/null +++ b/src/main/java/com/xkrs/controller/EquipmentController.java @@ -0,0 +1,94 @@ +package com.xkrs.controller; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.xkrs.common.encapsulation.PromptMessageEnum; +import com.xkrs.common.tools.GetTokenTool; +import com.xkrs.common.tools.JackSonTool; +import com.xkrs.dao.EquipmentDao; +import com.xkrs.model.entity.Equipment; +import com.xkrs.util.DateTimeUtil; +import com.xkrs.util.RequestUtil; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +import java.util.*; + +import static com.xkrs.common.encapsulation.OutputEncapsulation.outputEncapsulationObject; + +@RestController +public class EquipmentController { + + @Resource + private EquipmentDao equipmentDao; + + @GetMapping("/getEquipment") + public String getEquipment(){ + Locale locale = LocaleContextHolder.getLocale(); + + // 鉴权拿到token + String token = GetTokenTool.getToken(); + + String urlGet = "https://111.26.161.203:443/admin/API/tree/devices"; + JSONObject jsonObject2 = new JSONObject(); + jsonObject2.put("orgCode",""); + List list = new ArrayList(); + jsonObject2.put("deviceCodes",list); + jsonObject2.put("categories",list); + String s = jsonObject2.toString(); + + String doPostJsonGetData = RequestUtil.doPostJsonGetData(urlGet, s, token); + Map json2map = JackSonTool.json2map(doPostJsonGetData); + LinkedHashMap data1 = (LinkedHashMap ) json2map.get("data"); + String json = JSON.toJSONString(data1); + Map map4 = JackSonTool.json2map(json); + List devices = (List) map4.get("devices"); + for(Map obj : devices){ + Equipment equipment = new Equipment(); + equipment.setEquipmentCode(obj.get("code").toString()); + equipment.setEquipmentName(obj.get("name").toString()); + String type = obj.get("type").toString(); + if("1".equals(type)){ + equipment.setEquipmentType("DVR"); + }else if("2".equals(type)){ + equipment.setEquipmentType("IPC"); + }else if("3".equals(type)){ + equipment.setEquipmentType("NVS"); + }else if("5".equals(type)){ + equipment.setEquipmentType("MDVR"); + }else if("6".equals(type)){ + equipment.setEquipmentType("NVR"); + }else if("9".equals(type)){ + equipment.setEquipmentType("PVR"); + }else if("10".equals(type)){ + equipment.setEquipmentType("EVS"); + }else if("12".equals(type)){ + equipment.setEquipmentType("SMART_IPC"); + }else if("14".equals(type)){ + equipment.setEquipmentType("SMART_NVR"); + }else if("21".equals(type)){ + equipment.setEquipmentType("VTT"); + }else if("26".equals(type)){ + equipment.setEquipmentType("TC"); + }else if("28".equals(type)){ + equipment.setEquipmentType("DSJ"); + }else if("29".equals(type)){ + equipment.setEquipmentType("MPT"); + }else if("34".equals(type)){ + equipment.setEquipmentType("WATCHER"); + }else if("35".equals(type)){ + equipment.setEquipmentType("MCS"); + }else { + equipment.setEquipmentType("IVSS"); + } + equipment.setEquipmentStatus(obj.get("status").toString()); + String modifyTime = DateTimeUtil.timeMillisToString(Long.parseLong(obj.get("modifyTime").toString())); + equipment.setInstallationTime(modifyTime); + equipmentDao.save(equipment); + } + return outputEncapsulationObject(PromptMessageEnum.SUCCESS, "设备入库成功!", locale); + } +} diff --git a/src/main/java/com/xkrs/controller/TestController.java b/src/main/java/com/xkrs/controller/TestController.java new file mode 100644 index 0000000..477c5fa --- /dev/null +++ b/src/main/java/com/xkrs/controller/TestController.java @@ -0,0 +1,119 @@ +package com.xkrs.controller; + +import com.alibaba.fastjson.JSONObject; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.xkrs.common.tools.GetTokenTool; +import com.xkrs.common.tools.JackSonTool; +import com.xkrs.util.Md5Util; +import com.xkrs.util.RequestUtil; +import com.xkrs.util.WeatherUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Map; + +/** + * @Author: XinYi Song + * @Date: 2022/2/8 14:32 + */ +@RestController +public class TestController { + + @GetMapping("/getTT") + public Map getTT(){ + String url1 = "https://111.26.161.203:443/admin/API/accounts/authorize"; + String json1 = "{\n" + + " \"userName\": system,\n" + + " \"ipAddress\": \"\",\n" + + " \"clientType\": \"WINPC\"\n" + + "}"; + // 第一次鉴权 + String doPostJson = RequestUtil.doPostJson(url1, json1); + Map map = JackSonTool.json2map(doPostJson); + String randomKey = (String) map.get("randomKey"); + + String admin123 = Md5Util.stringToMd5("admin123"); + String stringToMd5 = Md5Util.stringToMd5("system" + admin123); + String toMd5 = Md5Util.stringToMd5(stringToMd5); + String md5 = Md5Util.stringToMd5("system:" + "DSS:" + toMd5); + String signature = Md5Util.stringToMd5(md5 + ":" + randomKey); + + String url2 = "https://111.26.161.203:443/admin/API/accounts/authorize"; + + JSONObject jsonObject = new JSONObject(); + jsonObject.put("userName","system"); + jsonObject.put("randomKey",randomKey); + jsonObject.put("mac",""); + jsonObject.put("encryptType","MD5"); + jsonObject.put("ipAddress",""); + jsonObject.put("signature",signature); + jsonObject.put("clientType","WINPC"); + // 第二次鉴权 + String postJson = RequestUtil.doPostJson(url2, jsonObject.toString()); + Map map1 = JackSonTool.json2map(postJson); + String token = (String) map1.get("token"); + return map1; + } + + @PostMapping("/getVideo") + public String getVideo(@RequestParam("alarmCode") String alarmCode) throws JsonProcessingException { + String token = GetTokenTool.getToken(); + String url = "https://111.26.161.203:443/admin/API/SS/Record/GetAlarmRecords?token=" + token; + JSONObject jsonObject = new JSONObject(); + jsonObject.put("clientType","WINPC_V1"); + jsonObject.put("clientMac","30:9c:23:79:40:08"); + jsonObject.put("clientPushId",""); + jsonObject.put("project","PSDK"); + jsonObject.put("method","SS.Record.GetAlarmRecords"); + JSONObject jsonObject1 = new JSONObject(); + jsonObject1.put("alarmCode",alarmCode); + jsonObject1.put("optional","/admin/API/SS/Record/GetAlarmRecords"); + jsonObject.put("data",jsonObject1); + String s = RequestUtil.doPostJson(url, jsonObject.toString()); + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode jsonNode = objectMapper.readTree(s); + JsonNode data = jsonNode.get("data"); + JsonNode records = data.get("records").get(0); + + String urlVideo = "/admin/API/SS/Playback/StartPlaybackByFile?token=" + token; + JSONObject jsonObject2 = new JSONObject(); + jsonObject2.put("ssId",records.get("ssId").asText()); + jsonObject2.put("optional",urlVideo); + jsonObject2.put("fileName",records.get("recordName").asText()); + jsonObject2.put("startTime",records.get("startTime").asText()); + jsonObject2.put("endTime",records.get("endTime").asText()); + jsonObject2.put("recordType",records.get("recordType").asText()); + jsonObject2.put("diskId",records.get("diskId").asText()); + jsonObject2.put("recordSource",records.get("recordSource").asText()); + jsonObject2.put("channelId",records.get("channelId").asText()); + jsonObject2.put("streamId",records.get("streamId").asText()); + jsonObject2.put("enableRtsps","0"); + jsonObject2.put("nvrId",""); + JSONObject jsonObject3 = new JSONObject(); + jsonObject3.put("data",jsonObject2); + String s1 = RequestUtil.doPostJson("https://111.26.161.203:443"+urlVideo, jsonObject3.toString()); + ObjectMapper objectMapper1 = new ObjectMapper(); + JsonNode jsonNode1 = objectMapper1.readTree(s1); + JsonNode data1 = jsonNode1.get("data"); + String url1 = data1.get("url").asText(); + return url1; + } + + /** + * 获取天气信息的接口 + * @return + */ + @GetMapping("/getWeather") + public JsonNode getWeather(){ + JsonNode forecastWeather = WeatherUtils.getForecastWeather(); + JsonNode results = forecastWeather.path("results"); + JsonNode jsonNode = results.get(0); + JsonNode daily = jsonNode.path("daily"); + JsonNode jsonNode1 = daily.get(0); + return jsonNode1; + } +} diff --git a/src/main/java/com/xkrs/dao/EquipmentDao.java b/src/main/java/com/xkrs/dao/EquipmentDao.java new file mode 100644 index 0000000..6fdf9c7 --- /dev/null +++ b/src/main/java/com/xkrs/dao/EquipmentDao.java @@ -0,0 +1,11 @@ +package com.xkrs.dao; + +import com.xkrs.model.entity.Equipment; +import org.springframework.data.jpa.repository.JpaRepository; + +/** + * @Author: XinYi Song + * @Date: 2022/2/8 9:26 + */ +public interface EquipmentDao extends JpaRepository { +} diff --git a/src/main/java/com/xkrs/dao/FireDao.java b/src/main/java/com/xkrs/dao/FireDao.java new file mode 100644 index 0000000..53026cf --- /dev/null +++ b/src/main/java/com/xkrs/dao/FireDao.java @@ -0,0 +1,31 @@ +package com.xkrs.dao; + +import com.xkrs.model.entity.Fire; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Component; + +/** + * @Author: XinYi Song + * @Date: 2022/2/8 17:12 + */ +@Component +public interface FireDao extends JpaRepository { + + /** + * 根据报警编码查询火情信息 + * @param code + * @return + */ + Fire findByAlarmCode(String code); + + /** + * 根据报警编码修改图片信息 + * @param code + * @param picturePath + */ + @Modifying(clearAutomatically=true) + @Query(value = "update fire set picture_path = ?2 where alarm_code = ?1",nativeQuery = true) + void updatePicturePath(String code, String picturePath); +} diff --git a/src/main/java/com/xkrs/model/entity/Equipment.java b/src/main/java/com/xkrs/model/entity/Equipment.java new file mode 100644 index 0000000..39d9c69 --- /dev/null +++ b/src/main/java/com/xkrs/model/entity/Equipment.java @@ -0,0 +1,153 @@ +package com.xkrs.model.entity; + +import javax.persistence.*; + +/** + * @Author: XinYi Song + * @Date: 2022/2/8 9:01 + */ +@Entity +@Table(name = "equipment") +public class Equipment { + /** + * 主键id + */ + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "equipment_seq_gen") + @SequenceGenerator(name = "equipment_seq_gen", sequenceName = "equipment_id_seq",allocationSize = 1) + private Integer id; + + /** + * 设备编码 + */ + @Column(length = 32, columnDefinition = "varchar(32)") + private String equipmentCode; + + /** + * 设备名称 + */ + @Column(length = 65, columnDefinition = "varchar(65)") + private String equipmentName; + + /** + * 设备类别 + */ + @Column(length = 32, columnDefinition = "varchar(32)") + private String equipmentType; + + /** + * 设备状态 + */ + @Column(length = 32, columnDefinition = "varchar(32)") + private String equipmentStatus; + + /** + * 设备经度 + */ + @Column(length = 32, columnDefinition = "varchar(32)") + private String equipmentLongitude; + + /** + * 设备纬度 + */ + @Column(length = 32, columnDefinition = "varchar(32)") + private String equipmentLatitude; + + /** + * 安装时间 + */ + @Column(length = 65, columnDefinition = "varchar(65)") + private String installationTime; + + public Equipment() { + } + + public Equipment(Integer id, String equipmentCode, String equipmentName, String equipmentType, String equipmentStatus, String equipmentLongitude, String equipmentLatitude, String installationTime) { + this.id = id; + this.equipmentCode = equipmentCode; + this.equipmentName = equipmentName; + this.equipmentType = equipmentType; + this.equipmentStatus = equipmentStatus; + this.equipmentLongitude = equipmentLongitude; + this.equipmentLatitude = equipmentLatitude; + this.installationTime = installationTime; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getEquipmentCode() { + return equipmentCode; + } + + public void setEquipmentCode(String equipmentCode) { + this.equipmentCode = equipmentCode; + } + + public String getEquipmentName() { + return equipmentName; + } + + public void setEquipmentName(String equipmentName) { + this.equipmentName = equipmentName; + } + + public String getEquipmentType() { + return equipmentType; + } + + public void setEquipmentType(String equipmentType) { + this.equipmentType = equipmentType; + } + + public String getEquipmentStatus() { + return equipmentStatus; + } + + public void setEquipmentStatus(String equipmentStatus) { + this.equipmentStatus = equipmentStatus; + } + + public String getEquipmentLongitude() { + return equipmentLongitude; + } + + public void setEquipmentLongitude(String equipmentLongitude) { + this.equipmentLongitude = equipmentLongitude; + } + + public String getEquipmentLatitude() { + return equipmentLatitude; + } + + public void setEquipmentLatitude(String equipmentLatitude) { + this.equipmentLatitude = equipmentLatitude; + } + + public String getInstallationTime() { + return installationTime; + } + + public void setInstallationTime(String installationTime) { + this.installationTime = installationTime; + } + + @Override + public String toString() { + return "Equipment{" + + "id=" + id + + ", equipmentCode='" + equipmentCode + '\'' + + ", equipmentName='" + equipmentName + '\'' + + ", equipmentType='" + equipmentType + '\'' + + ", equipmentStatus='" + equipmentStatus + '\'' + + ", equipmentLongitude='" + equipmentLongitude + '\'' + + ", equipmentLatitude='" + equipmentLatitude + '\'' + + ", installationTime='" + installationTime + '\'' + + '}'; + } +} diff --git a/src/main/java/com/xkrs/model/entity/Fire.java b/src/main/java/com/xkrs/model/entity/Fire.java new file mode 100644 index 0000000..af68287 --- /dev/null +++ b/src/main/java/com/xkrs/model/entity/Fire.java @@ -0,0 +1,329 @@ +package com.xkrs.model.entity; + +import javax.persistence.*; + +/** + * @Author: XinYi Song + * @Date: 2022/2/8 15:09 + */ +@Entity +@Table(name = "fire") +public class Fire { + + /** + * 主键id + */ + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "fire_seq_gen") + @SequenceGenerator(name = "fire_seq_gen", sequenceName = "fire_id_seq",allocationSize = 1) + private Integer id; + + /** + * 设备编号 + */ + @Column(length = 32, columnDefinition = "varchar(32)") + private String deviceCode; + + /** + * 通道号 + */ + @Column(length = 11, columnDefinition = "varchar(11)") + private String channelSeq; + + /** + * 单元类型 + */ + @Column(length = 11, columnDefinition = "varchar(11)") + private String unitType; + + /** + * 节点类型 + */ + @Column(length = 11, columnDefinition = "varchar(11)") + private String nodeType; + + /** + * 通道编码 + */ + @Column(length = 32, columnDefinition = "varchar(32)") + private String nodeCode; + + /** + * 报警编码 + */ + @Column(length = 65, columnDefinition = "varchar(65)") + private String alarmCode; + + /** + * 报警状态 + */ + @Column(length = 11, columnDefinition = "varchar(11)") + private String alarmStat; + + /** + * 报警类型 + */ + @Column(length = 15, columnDefinition = "varchar(15)") + private String alarmType; + + /** + * 报警等级 + */ + @Column(length = 11, columnDefinition = "varchar(11)") + private String alarmGrade; + + /** + * 报警图片 + */ + @Column(length = 85, columnDefinition = "varchar(85)") + private String alarmPicture; + + /** + * 报警时间 + */ + @Column(length = 65, columnDefinition = "varchar(65)") + private String alarmDate; + + /** + * 报警源名称 + */ + @Column(length = 95, columnDefinition = "varchar(95)") + private String alarmSourceName; + + /** + * 报警经度 + */ + @Column(length = 32, columnDefinition = "varchar(32)") + private String alarmLongitude; + + /** + * 报警纬度 + */ + @Column(length = 32, columnDefinition = "varchar(32)") + private String alarmLatitude; + + /** + * 海拔 + */ + @Column(length = 32, columnDefinition = "varchar(32)") + private String altitude; + + /** + * 距离 + */ + @Column(length = 32, columnDefinition = "varchar(32)") + private String fireDistance; + + /** + * 火点编号 + */ + @Column(length = 32, columnDefinition = "varchar(32)") + private String fireId; + + /** + * 图片地址 + */ + private String picturePath; + + public Fire() { + } + + public Fire(Integer id, String deviceCode, String channelSeq, String unitType, String nodeType, String nodeCode, String alarmCode, String alarmStat, String alarmType, String alarmGrade, String alarmPicture, String alarmDate, String alarmSourceName, String alarmLongitude, String alarmLatitude, String altitude, String fireDistance, String fireId, String picturePath) { + this.id = id; + this.deviceCode = deviceCode; + this.channelSeq = channelSeq; + this.unitType = unitType; + this.nodeType = nodeType; + this.nodeCode = nodeCode; + this.alarmCode = alarmCode; + this.alarmStat = alarmStat; + this.alarmType = alarmType; + this.alarmGrade = alarmGrade; + this.alarmPicture = alarmPicture; + this.alarmDate = alarmDate; + this.alarmSourceName = alarmSourceName; + this.alarmLongitude = alarmLongitude; + this.alarmLatitude = alarmLatitude; + this.altitude = altitude; + this.fireDistance = fireDistance; + this.fireId = fireId; + this.picturePath = picturePath; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getDeviceCode() { + return deviceCode; + } + + public void setDeviceCode(String deviceCode) { + this.deviceCode = deviceCode; + } + + public String getChannelSeq() { + return channelSeq; + } + + public void setChannelSeq(String channelSeq) { + this.channelSeq = channelSeq; + } + + public String getUnitType() { + return unitType; + } + + public void setUnitType(String unitType) { + this.unitType = unitType; + } + + public String getNodeType() { + return nodeType; + } + + public void setNodeType(String nodeType) { + this.nodeType = nodeType; + } + + public String getNodeCode() { + return nodeCode; + } + + public void setNodeCode(String nodeCode) { + this.nodeCode = nodeCode; + } + + public String getAlarmCode() { + return alarmCode; + } + + public void setAlarmCode(String alarmCode) { + this.alarmCode = alarmCode; + } + + public String getAlarmStat() { + return alarmStat; + } + + public void setAlarmStat(String alarmStat) { + this.alarmStat = alarmStat; + } + + public String getAlarmType() { + return alarmType; + } + + public void setAlarmType(String alarmType) { + this.alarmType = alarmType; + } + + public String getAlarmGrade() { + return alarmGrade; + } + + public void setAlarmGrade(String alarmGrade) { + this.alarmGrade = alarmGrade; + } + + public String getAlarmPicture() { + return alarmPicture; + } + + public void setAlarmPicture(String alarmPicture) { + this.alarmPicture = alarmPicture; + } + + public String getAlarmDate() { + return alarmDate; + } + + public void setAlarmDate(String alarmDate) { + this.alarmDate = alarmDate; + } + + public String getAlarmSourceName() { + return alarmSourceName; + } + + public void setAlarmSourceName(String alarmSourceName) { + this.alarmSourceName = alarmSourceName; + } + + public String getAlarmLongitude() { + return alarmLongitude; + } + + public void setAlarmLongitude(String alarmLongitude) { + this.alarmLongitude = alarmLongitude; + } + + public String getAlarmLatitude() { + return alarmLatitude; + } + + public void setAlarmLatitude(String alarmLatitude) { + this.alarmLatitude = alarmLatitude; + } + + public String getAltitude() { + return altitude; + } + + public void setAltitude(String altitude) { + this.altitude = altitude; + } + + public String getFireDistance() { + return fireDistance; + } + + public void setFireDistance(String fireDistance) { + this.fireDistance = fireDistance; + } + + public String getFireId() { + return fireId; + } + + public void setFireId(String fireId) { + this.fireId = fireId; + } + + public String getPicturePath() { + return picturePath; + } + + public void setPicturePath(String picturePath) { + this.picturePath = picturePath; + } + + @Override + public String toString() { + return "Fire{" + + "id=" + id + + ", deviceCode='" + deviceCode + '\'' + + ", channelSeq='" + channelSeq + '\'' + + ", unitType='" + unitType + '\'' + + ", nodeType='" + nodeType + '\'' + + ", nodeCode='" + nodeCode + '\'' + + ", alarmCode='" + alarmCode + '\'' + + ", alarmStat='" + alarmStat + '\'' + + ", alarmType='" + alarmType + '\'' + + ", alarmGrade='" + alarmGrade + '\'' + + ", alarmPicture='" + alarmPicture + '\'' + + ", alarmDate='" + alarmDate + '\'' + + ", alarmSourceName='" + alarmSourceName + '\'' + + ", alarmLongitude='" + alarmLongitude + '\'' + + ", alarmLatitude='" + alarmLatitude + '\'' + + ", altitude='" + altitude + '\'' + + ", fireDistance='" + fireDistance + '\'' + + ", fireId='" + fireId + '\'' + + ", picturePath='" + picturePath + '\'' + + '}'; + } +} diff --git a/src/main/java/com/xkrs/util/DateTimeUtil.java b/src/main/java/com/xkrs/util/DateTimeUtil.java new file mode 100644 index 0000000..ed44995 --- /dev/null +++ b/src/main/java/com/xkrs/util/DateTimeUtil.java @@ -0,0 +1,230 @@ +package com.xkrs.util; + +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 日期时间工具 + * @author tajochen + */ +public class DateTimeUtil { + + private final static String COMMON_PATTERN_DATETIME = "yyyy-MM-dd HH:mm:ss"; + private final static String COMMON_PATTERN_DATE = "yyyy-MM-dd"; + private final static DateTimeFormatter COMMON_FORMATTER_DATETIME = DateTimeFormatter.ofPattern(COMMON_PATTERN_DATETIME); + private final static DateTimeFormatter COMMON_FORMATTER_DATE = DateTimeFormatter.ofPattern(COMMON_PATTERN_DATE); + private final static ZoneOffset DEFAULT_ZONE_OFFSET = ZoneOffset.of("+8"); + + /** + * 字符串转LocalDate + * @param date + * @return + */ + public static LocalDate stringToDate(String date) { + assert date != null; + return LocalDate.parse(date, COMMON_FORMATTER_DATE); + } + + /** + * LocalDate转字符串 + * @param date + * @return + */ + public static String dateToString(LocalDate date) { + assert date != null; + return COMMON_FORMATTER_DATE.format(date); + } + + /** + * LocalDateTime转字符串 + * @param dateTime + * @return + */ + public static String dateTimeToString(LocalDateTime dateTime) { + assert dateTime != null; + return COMMON_FORMATTER_DATETIME.format(dateTime); + } + + /** + * 字符串转LocalDateTime + * @param dateStr + * @return + */ + public static LocalDateTime stringToDateTime(String dateStr) { + assert dateStr != null; + return LocalDateTime.parse(dateStr, COMMON_FORMATTER_DATETIME); + } + + /** + * 字符串转Instant时间戳 + * @param str + * @return + */ + public static Instant stringToInstant(String str) { + assert str != null; + return stringToDateTime(str).toInstant(DEFAULT_ZONE_OFFSET); + } + + /** + * LocalDateTime转字符串(格式化) + * @param dateTime + * @param formatter + * @return + */ + public static String dateToStringFormatter(LocalDateTime dateTime, DateTimeFormatter formatter) { + assert dateTime != null; + return formatter.format(dateTime); + } + + /** + * 字符串转LocalDateTime(格式化) + * @param dateStr + * @param formatter + * @return + */ + public static LocalDateTime stringToDateTimeFormatter(String dateStr, DateTimeFormatter formatter) { + assert dateStr != null; + return LocalDateTime.parse(dateStr, formatter); + } + + /** + * 字符串转 local date + * @param dateStr + * @return + */ + public static LocalDate stringToDateFormatter(String dateStr){ + LocalDate date = LocalDate.parse(dateStr, COMMON_FORMATTER_DATE); + return date; + } + + + /** + * 日期转时间戳 + * @param dateTime + * @return + */ + public static long dateToTimeMillis(LocalDateTime dateTime) { + assert dateTime != null; + return dateTime.toInstant(DEFAULT_ZONE_OFFSET).toEpochMilli(); + } + + /** + * 时间戳转日期 + * @param timeMillis + * @return + */ + public static LocalDateTime timeMillisToDate(long timeMillis) { + Instant instant = Instant.ofEpochMilli(timeMillis); + return LocalDateTime.ofInstant(instant, DEFAULT_ZONE_OFFSET); + } + + /** + * 时间戳转时间 + * @param timeMillis + * @return + */ + public static LocalDateTime timeMillisToTime(long timeMillis) { + LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(timeMillis, 0, ZoneOffset.ofHours(8)); + return localDateTime; + } + + /** + * 时间戳转时间再转字符串 + * @param timeMillis + * @return + */ + public static String timeMillisToString(long timeMillis){ + LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(timeMillis, 0, ZoneOffset.ofHours(8)); + return COMMON_FORMATTER_DATETIME.format(localDateTime); + } + + /** + * 获取当前时间 hh:mm:ss:nnn + * @return + */ + public static LocalDateTime getNowTime() { + LocalDateTime now = LocalDateTime.now(); + return now; + } + + /** + * 获取当前日期 yyyy-MM-dd + * @return + */ + public static LocalDate getToday() { + LocalDate now = LocalDate.now(); + return now; + } + + /** + * 获取当前 Instant 时间戳 + * @return + */ + public static Instant getInstant() { + Instant timeStamp = Instant.now(); + return timeStamp; + } + + /** + * 获取当前时间 yyyy-MM-ss hh:mm:ss + * @return + */ + public static String getNowTimeStr(){ + return COMMON_FORMATTER_DATETIME.format(getNowTime()); + } + + /** + * 判断日期格式是否合法 "yyyy-MM-dd" + * @param strDate + * @return + */ + public static boolean isValidDate(String strDate) { + final int minLen = 10; + if(strDate == null || strDate.length() < minLen) { + return false; + } + //正则表达式校验日期格式 yyyy-MM-dd + String eL = "(([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-" + + "(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})" + + "(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29)(([0-9]{3}[1-9]|[0-9]{2}[1-9]" + + "[0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-" + + "(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|" + + "((0[48]|[2468][048]|[3579][26])00))-02-29)"; + Pattern pat = Pattern.compile(eL); + Matcher matcher = pat.matcher(strDate); + return matcher.matches(); + } + + /** + * 判断时间格式 格式必须为 "YYYY-MM-DD HH:mm:ss" + * @param sDateTime + * @return + */ + public static boolean isValidDateTime(String sDateTime) { + final int minLen = 19; + if ((sDateTime == null) || (sDateTime.length() < minLen)) { + return false; + } + String eL = "(((01[0-9]{2}|0[2-9][0-9]{2}|[1-9][0-9]{3})-(0?[13578]|1[02])-" + + "(0?[1-9]|[12]\\\\d|3[01]))|((01[0-9]{2}|0[2-9][0-9]{2}|[1-9][0-9]{3})-" + + "(0?[13456789]|1[012])-(0?[1-9]|[12]\\\\d|30))|((01[0-9]{2}|0[2-9][0-9]{2}|[1-9][0-9]{3})-0?2-" + + "(0?[1-9]|1\\\\d|2[0-8]))|(((1[6-9]|[2-9]\\\\d)(0[48]|[2468][048]|[13579][26])|((04|08|12|16|[2468][048]|" + + "[3579][26])00))-0?2-29)) (20|21|22|23|[0-1]?\\\\d):[0-5]?\\\\d:[0-5]?\\\\d"; + Pattern pat = Pattern.compile(eL); + Matcher matcher = pat.matcher(sDateTime); + return matcher.matches(); + } + + public static void main(String[] args) { + String mm = "1626340246"; + long l = Long.parseLong(mm); + String timeMillisToString = timeMillisToString(l); + System.out.println(timeMillisToString); + } + +} diff --git a/src/main/java/com/xkrs/util/Md5Util.java b/src/main/java/com/xkrs/util/Md5Util.java new file mode 100644 index 0000000..d54fcde --- /dev/null +++ b/src/main/java/com/xkrs/util/Md5Util.java @@ -0,0 +1,33 @@ +package com.xkrs.util; + +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * @author xkrs + */ +public class Md5Util { + + public static String stringToMd5(String content){ + byte[] secretBytes = null; + try { + secretBytes = MessageDigest.getInstance("md5").digest( + content.getBytes()); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("没有md5这个算法!"); + } + // 16进制数字 + String md5code = new BigInteger(1, secretBytes).toString(16); + // 如果生成数字未满32位,需要前面补0 + for (int i = 0; i < 32 - md5code.length(); i++) { + md5code = "0" + md5code; + } + return md5code; + } + + public static void main(String[] args) { + String s = stringToMd5("12345"); + System.out.println(s); + } +} diff --git a/src/main/java/com/xkrs/util/RequestUtil.java b/src/main/java/com/xkrs/util/RequestUtil.java new file mode 100644 index 0000000..3eee54e --- /dev/null +++ b/src/main/java/com/xkrs/util/RequestUtil.java @@ -0,0 +1,372 @@ +package com.xkrs.util; + +import com.alibaba.fastjson.JSONObject; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.http.Consts; +import org.apache.http.HttpEntity; +import org.apache.http.NameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.entity.mime.HttpMultipartMode; +import org.apache.http.entity.mime.MultipartEntityBuilder; +import org.apache.http.entity.mime.content.StringBody; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Resource; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.*; + +/** + * 上传工具 + * @author XinYi Song + **/ +public class RequestUtil { + + private static final String FIREFOX_UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:92.0) Gecko/20100101 Firefox/92.0"; + private static final String CHROME_UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " + + "(KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36"; + /** + * 模拟 get请求 + * @param url 链接 + * @param map 参数列表 + */ + public static String getStandard(String url, Map map) { + String body = ""; + // 1.拿到一个httpclient的对象 + CloseableHttpClient httpClient = HttpClients.createDefault(); + // 2.1 提交请求体 + ArrayList parameters = new ArrayList<>(); + URIBuilder builder = null; + // 装填参数 + if(map!=null){ + for (Map.Entry entry : map.entrySet()) { + parameters.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); + } + } + CloseableHttpResponse response = null; + try { + builder = new URIBuilder(url); + builder.setParameters(parameters); + // 2.设置请求方式和请求信息 + HttpGet httpGet = new HttpGet(builder.build()); + // 2.1 提交header头信息 + httpGet.addHeader("user-agent", FIREFOX_UA); + // 3.执行请求 + response = httpClient.execute(httpGet); + // 4.获取返回值 + assert response != null; + // 按指定编码转换结果实体为String类型 + body = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); + // 5.关闭连接 + response.close(); + httpClient.close(); + } catch (Exception e) { + e.printStackTrace(); + } + return body; + } + + /** + * 模拟get请求(加请求头) + * @param url + * @param param + * @param token + * @return + */ + public static String doGet(String url, Map param,String token) { + + final int okResNum = 200; + + String result = null; + CloseableHttpClient httpclient = HttpClients.createDefault(); + CloseableHttpResponse response = null; + try { + URIBuilder builder = new URIBuilder(url); + if (param != null) { + for (String key : param.keySet()) { + builder.addParameter(key, param.get(key)); + } + } + URI uri = builder.build(); + HttpGet httpGet = new HttpGet(uri); + httpGet.addHeader("user-agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36"); + httpGet.addHeader("Authorization",token); + response = httpclient.execute(httpGet); + if (response.getStatusLine().getStatusCode() == okResNum) { + result = EntityUtils.toString(response.getEntity(), "UTF-8"); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (response != null) { + response.close(); + } + httpclient.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return result; + } + + /** + * 带参数的get请求 + * @param url + * @param param + * @return String + */ + public static JsonNode doGetJsonNode(String url, Map param) { + // 创建Httpclient对象 + CloseableHttpClient httpclient = HttpClients.createDefault(); + + String resultString = ""; + JsonNode node = null; + CloseableHttpResponse response = null; + try { + // 创建uri + URIBuilder builder = new URIBuilder(url); + if (param != null) { + for (String key : param.keySet()) { + builder.addParameter(key, param.get(key)); + } + } + URI uri = builder.build(); + // 创建http GET请求 + HttpGet httpGet = new HttpGet(uri); + // 执行请求 + response = httpclient.execute(httpGet); + // 判断返回状态是否为200 + if (response.getStatusLine().getStatusCode() == 200) { + resultString = EntityUtils.toString(response.getEntity(), "UTF-8"); + ObjectMapper objectMapper = new ObjectMapper(); + //使用ObjectMapper对象对 User对象进行转换 + node = objectMapper.readTree(resultString); + + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (response != null) { + response.close(); + } + httpclient.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return node; + } + + /** + * 模拟Post请求 application/x-www-form-urlencoded + * @param url 资源地址 + * @param map 参数列表 + * @return + */ + public static String postStandard(String url, Map map) { + String body = ""; + // 创建httpclient对象 + CloseableHttpClient client = HttpClients.createDefault(); + // 创建post方式请求对象 + HttpPost httpPost = new HttpPost(url); + // 装填参数 + List nvps = new ArrayList<>(); + if(map!=null){ + for (Map.Entry entry : map.entrySet()) { + nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); + } + } + try { + // 设置参数到请求对象中 + httpPost.setEntity(new UrlEncodedFormEntity(nvps, "UTF-8")); + // 设置header报文头信息 + httpPost.setHeader("Content-type", "application/x-www-form-urlencoded"); + httpPost.setHeader("User-Agent", FIREFOX_UA); + httpPost.setHeader("Accept", "application"); + httpPost.setHeader("Accept-Encoding", "gzip, deflate"); + // 执行请求操作,并拿到结果(同步阻塞) + CloseableHttpResponse response = client.execute(httpPost); + // 获取结果实体 + HttpEntity entity = response.getEntity(); + if (entity != null) { + // 按指定编码转换结果实体为String类型 + body = EntityUtils.toString(entity, "UTF-8"); + } + EntityUtils.consume(entity); + // 释放链接 + response.close(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + client.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return body; + } + + /** + * 传送json类型的post请求 + * @param url + * @param json + * @return String + */ + public static String doPostJson(String url, String json) { + // 创建Httpclient对象 +// CloseableHttpClient httpClient = HttpClients.createDefault(); + CloseableHttpClient httpClient = SeeSSLCloseableHttpClient.getCloseableHttpClient(); + CloseableHttpResponse response = null; + String resultString = ""; + try { + // 创建Http Post请求 + HttpPost httpPost = new HttpPost(url); + // 创建请求内容 + StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON); + httpPost.setEntity(entity); + httpPost.setHeader("Content-Type","application/json"); + httpPost.setHeader("User-Agent", FIREFOX_UA); + // 执行http请求 + response = httpClient.execute(httpPost); + resultString = EntityUtils.toString(response.getEntity(), "utf-8"); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + response.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return resultString; + } + + + public static String doPostJsonGetData(String url, String json, String token) { + // 创建Httpclient对象 +// CloseableHttpClient httpClient = HttpClients.createDefault(); + CloseableHttpClient httpClient = SeeSSLCloseableHttpClient.getCloseableHttpClient(); + CloseableHttpResponse response = null; + String resultString = ""; + try { + // 创建Http Post请求 + HttpPost httpPost = new HttpPost(url); + // 创建请求内容 + StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON); + httpPost.setEntity(entity); + httpPost.setHeader("Content-Type","application/json"); + httpPost.setHeader("User-Agent", FIREFOX_UA); + httpPost.setHeader("X-Subject-Token", token); + // 执行http请求 + response = httpClient.execute(httpPost); + resultString = EntityUtils.toString(response.getEntity(), "utf-8"); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + response.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return resultString; + } + + /** + * 模拟Post请求 multipart/form-data + * @param postFile 请求文件 + * @param postUrl 请求链接 + * @param postParam 请求参数 + * @return + */ + public static Map postFile(File postFile, String postUrl, Map postParam) { + + Logger log = LoggerFactory.getLogger(RequestUtil.class); + Map resultMap = new HashMap<>(16); + CloseableHttpClient httpClient = HttpClients.createDefault(); + try{ + // 把一个普通参数和文件上传给下面这个地址 + HttpPost httpPost = new HttpPost(postUrl); + + // 设置传输参数 + MultipartEntityBuilder multipartEntity = MultipartEntityBuilder.create(); + // 解决乱码问题 + multipartEntity.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); + multipartEntity.setCharset(StandardCharsets.UTF_8); + + // 设置文件参数 ,获取文件名postFile.getName() + // 把文件转换成流对象FileBody + multipartEntity.addBinaryBody("file",postFile,ContentType.create("multipart/form-data", + Consts.UTF_8),postFile.getName()); + // 设计文件以外的参数 + Set keySet = postParam.keySet(); + for (String key : keySet) { + // 相当于 + multipartEntity.addPart(key, new StringBody(postParam.get(key), ContentType.create( + "multipart/form-data", Consts.UTF_8))); + } + HttpEntity reqEntity = multipartEntity.build(); + httpPost.setEntity(reqEntity); + // 设置header报文头信息 + httpPost.setHeader("User-Agent", FIREFOX_UA); + httpPost.setHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); + httpPost.setHeader("Accept-Encoding","gzip, deflate"); + + // 发起请求,返回请求的响应 + try (CloseableHttpResponse response = httpClient.execute(httpPost)) { + resultMap.put("statusCode", response.getStatusLine().getStatusCode()); + // 获取响应对象 + HttpEntity resEntity = response.getEntity(); + if (resEntity != null) { + // 打印响应内容 + resultMap.put("data", EntityUtils.toString(resEntity, StandardCharsets.UTF_8)); + resultMap.put("message", EntityUtils.toString(resEntity, StandardCharsets.UTF_8)); + resultMap.put("status", EntityUtils.toString(resEntity, StandardCharsets.UTF_8)); + } + // 销毁 + EntityUtils.consume(resEntity); + } catch (Exception e) { + e.printStackTrace(); + } + httpClient.close(); + } catch (IOException e1) { + e1.printStackTrace(); + } + log.info("上传结果:" + resultMap); + return resultMap; + } + + public static void main(String[] args) { + + String url1 = "https://111.26.161.203:443/admin/API/accounts/authorize"; + String json1 = "{\n" + + " \"userName\": system,\n" + + " \"ipAddress\": \"\",\n" + + " \"clientType\": \"WINPC\"\n" + + "}"; + String doPostJson = doPostJson(url1, json1); + /*JSONObject jsonObject = new JSONObject(); + jsonObject.put("userName","system"); + jsonObject.put("ipAddress",""); + jsonObject.put("clientType","WINPC");*/ + + System.out.println(doPostJson); + } +} diff --git a/src/main/java/com/xkrs/util/SeeSSLCloseableHttpClient.java b/src/main/java/com/xkrs/util/SeeSSLCloseableHttpClient.java new file mode 100644 index 0000000..75669f4 --- /dev/null +++ b/src/main/java/com/xkrs/util/SeeSSLCloseableHttpClient.java @@ -0,0 +1,64 @@ +package com.xkrs.util; + +import org.apache.http.config.Registry; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.BasicHttpClientConnectionManager; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + + +public class SeeSSLCloseableHttpClient { + + private static X509TrustManager tm = new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; + } + }; + + public static CloseableHttpClient getCloseableHttpClient() { + SSLContext ctx = null; + try { + ctx = SSLContext.getInstance("TLS"); + ctx.init(null, new TrustManager[] { tm }, null); + } catch (KeyManagementException e) { + e.printStackTrace(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + HttpClientBuilder builder = HttpClientBuilder.create(); + SSLConnectionSocketFactory sslConnectionFactory = new SSLConnectionSocketFactory(ctx, + NoopHostnameVerifier.INSTANCE); + builder.setSSLSocketFactory(sslConnectionFactory); + + Registry registry = RegistryBuilder. create() + .register("https", sslConnectionFactory).build(); + + HttpClientConnectionManager ccm1 = new BasicHttpClientConnectionManager(registry); + + builder.setConnectionManager(ccm1); + + return builder.build(); + } + +} diff --git a/src/main/java/com/xkrs/util/TopicConsumerListener.java b/src/main/java/com/xkrs/util/TopicConsumerListener.java new file mode 100644 index 0000000..a8bbcd7 --- /dev/null +++ b/src/main/java/com/xkrs/util/TopicConsumerListener.java @@ -0,0 +1,113 @@ +package com.xkrs.util; + +/** + * @Author: XinYi Song + * @Date: 2022/2/8 14:42 + */ +import com.alibaba.fastjson.JSON; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.xkrs.dao.FireDao; +import com.xkrs.model.entity.Fire; +import com.xkrs.websocket.server.WebSocketServer; +import org.springframework.jms.annotation.JmsListener; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import javax.transaction.Transactional; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +@Component +public class TopicConsumerListener { + + @Resource + private FireDao fireDao; + + /** + * topic模式的消费者 + * 订阅火情报警信息 + * @param message + */ + @JmsListener(destination="${spring.activemq.topic-name}", containerFactory="topicListener") + public void readActiveQueue(String message) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode jsonNode = objectMapper.readTree(message); + String method = jsonNode.get("method").asText(); + if("vms.alarm.msg".equals(method)){ + JsonNode info = jsonNode.get("info"); + Fire fire = new Fire(); + fire.setDeviceCode(info.get("deviceCode").asText()); + fire.setChannelSeq(info.get("channelSeq").asText()); + fire.setUnitType(info.get("unitType").asText()); + fire.setNodeType(info.get("nodeType").asText()); + fire.setNodeCode(info.get("nodeCode").asText()); + String alarmCode = info.get("alarmCode").asText(); + fire.setAlarmCode(alarmCode); + fire.setAlarmStat(info.get("alarmStat").asText()); + fire.setAlarmType(info.get("alarmType").asText()); + fire.setAlarmGrade(info.get("alarmGrade").asText()); + fire.setAlarmPicture(info.get("alarmPicture").asText()); + String modifyTime = DateTimeUtil.timeMillisToString(Long.parseLong(info.get("alarmDate").asText())); + fire.setAlarmDate(modifyTime); + fire.setAlarmSourceName(info.get("alarmSourceName").asText()); + fire.setAlarmLongitude(info.get("gpsX").asText()); + fire.setAlarmLatitude(info.get("gpsY").asText()); + fire.setAltitude(info.get("altitude").asText()); + fire.setFireDistance(info.get("fireDistance").asText()); + fire.setFireId(info.get("fsid").asText()); + Map map = new HashMap(); + map.put("deviceCode",info.get("deviceCode").asText()); + map.put("channelSeq",info.get("channelSeq").asText()); + map.put("unitType",info.get("unitType").asText()); + map.put("nodeType",info.get("nodeType").asText()); + map.put("nodeCode",info.get("nodeCode").asText()); + map.put("alarmCode",alarmCode); + map.put("alarmStat",info.get("alarmStat").asText()); + map.put("alarmType",info.get("alarmType").asText()); + map.put("alarmGrade",info.get("alarmGrade").asText()); + map.put("alarmPicture",info.get("alarmPicture").asText()); + map.put("alarmDate",modifyTime); + map.put("alarmSourceName",info.get("alarmSourceName").asText()); + map.put("alarmLongitude",info.get("gpsX").asText()); + map.put("alarmLatitude",info.get("gpsY").asText()); + map.put("altitude",info.get("altitude").asText()); + map.put("fireDistance",info.get("fireDistance").asText()); + map.put("fsid",info.get("fsid").asText()); + String jsonString = JSON.toJSONString(map); + WebSocketServer.BroadCastInfo(jsonString); + + fireDao.save(fire); + } + } + + /** + * 订阅火情图片 + * @param message + */ + @Transactional(rollbackOn = Exception.class) + @JmsListener(destination="${spring.activemq.topic-name1}", containerFactory="topicListener") + public void readActivePicture(String message) throws JsonProcessingException { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode jsonNode = objectMapper.readTree(message); + String method = jsonNode.get("method").asText(); + if("vms.notifyAlarmPicture".equals(method)){ + JsonNode info = jsonNode.get("info"); + String alarmCode = info.get("alarmCode").asText(); + Fire byAlarmCode = fireDao.findByAlarmCode(alarmCode); + if("".equals(byAlarmCode.getPicturePath()) || byAlarmCode.getPicturePath() == null){ + String picture = info.get("picture").asText(); + fireDao.updatePicturePath(alarmCode,picture); + }else { + String picture1 = byAlarmCode.getPicturePath() + "," + info.get("picture").asText(); + fireDao.updatePicturePath(alarmCode,picture1); + } + + System.out.println("topic接受到的图片:" + message); + } + } + + +} diff --git a/src/main/java/com/xkrs/util/WeatherUtils.java b/src/main/java/com/xkrs/util/WeatherUtils.java new file mode 100644 index 0000000..3885a74 --- /dev/null +++ b/src/main/java/com/xkrs/util/WeatherUtils.java @@ -0,0 +1,26 @@ +package com.xkrs.util; + +/** + * @Author: XinYi Song + * @Date: 2022/2/7 16:22 + */ +import com.fasterxml.jackson.databind.JsonNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Map; + +/** + * @description:获取地区天气工具类 + */ +public class WeatherUtils { + + public static JsonNode getForecastWeather() { + String url = "http://portalweather.comsys.net.cn/weather03/api/weatherService/getDailyWeather?cityName=大同"; + Map map = new HashMap<>(3); + JsonNode jsonNode = RequestUtil.doGetJsonNode(url, map); + return jsonNode; + } + +} diff --git a/src/main/java/com/xkrs/websocket/config/WebSocketBrokerConfig.java b/src/main/java/com/xkrs/websocket/config/WebSocketBrokerConfig.java new file mode 100644 index 0000000..bb8d377 --- /dev/null +++ b/src/main/java/com/xkrs/websocket/config/WebSocketBrokerConfig.java @@ -0,0 +1,25 @@ +package com.xkrs.websocket.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.simp.config.MessageBrokerRegistry; +import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; +import org.springframework.web.socket.config.annotation.StompEndpointRegistry; +import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; + +/** + * websocket广播配置 + * @author xkrs + */ +@Configuration +@EnableWebSocketMessageBroker +public class WebSocketBrokerConfig implements WebSocketMessageBrokerConfigurer { + @Override + public void registerStompEndpoints(StompEndpointRegistry registry) { + registry.addEndpoint("/endPointClientServer").withSockJS(); + } + + @Override + public void configureMessageBroker(MessageBrokerRegistry registry) { + registry.enableSimpleBroker("/ws"); + } +} diff --git a/src/main/java/com/xkrs/websocket/config/WebSocketConfig.java b/src/main/java/com/xkrs/websocket/config/WebSocketConfig.java new file mode 100644 index 0000000..261844d --- /dev/null +++ b/src/main/java/com/xkrs/websocket/config/WebSocketConfig.java @@ -0,0 +1,16 @@ +package com.xkrs.websocket.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; + +/** + * @author xkrs + */ +@Configuration +public class WebSocketConfig { + @Bean + public ServerEndpointExporter serverEndpointExporter(){ + return new ServerEndpointExporter(); + } +} diff --git a/src/main/java/com/xkrs/websocket/controller/WebSocketController.java b/src/main/java/com/xkrs/websocket/controller/WebSocketController.java new file mode 100644 index 0000000..1702069 --- /dev/null +++ b/src/main/java/com/xkrs/websocket/controller/WebSocketController.java @@ -0,0 +1,49 @@ +package com.xkrs.websocket.controller; + +import com.xkrs.websocket.server.WebSocketServer; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.io.IOException; + +/** + * @author XinYi Song + */ +@RestController +@RequestMapping("/api/ws") +public class WebSocketController { + + + /** + * 群发消息内容 + * @param message + * @return + */ + @RequestMapping(value="/sendAll", method= RequestMethod.GET) + public String sendAllMessage(@RequestParam(required=true) String message){ + try { + WebSocketServer.BroadCastInfo(message); + } catch (IOException e) { + e.printStackTrace(); + } + return "ok"; + } + + /** + * 指定会话ID发消息 + * @param message 消息内容 + * @param id 连接会话ID + * @return + */ + @RequestMapping(value="/sendOne", method=RequestMethod.GET) + public String sendOneMessage(@RequestParam(required=true) String message,@RequestParam(required=true) String id){ + try { + WebSocketServer.SendMessage(message,id); + } catch (IOException e) { + e.printStackTrace(); + } + return "ok"; + } +} diff --git a/src/main/java/com/xkrs/websocket/server/WebSocketServer.java b/src/main/java/com/xkrs/websocket/server/WebSocketServer.java new file mode 100644 index 0000000..60f01e5 --- /dev/null +++ b/src/main/java/com/xkrs/websocket/server/WebSocketServer.java @@ -0,0 +1,129 @@ +package com.xkrs.websocket.server; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.websocket.*; +import javax.websocket.server.ServerEndpoint; +import java.io.IOException; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author XinYi Song + */ +@ServerEndpoint(value = "/ws/asset") +@Component +public class WebSocketServer { + + public static Logger log = LoggerFactory.getLogger(WebSocketServer.class); + + @PostConstruct + public void init() { + System.out.println("websocket 加载"); + } + private static final AtomicInteger OnlineCount = new AtomicInteger(0); + /** + * concurrent包的线程安全Set,用来存放每个客户端对应的Session对象。 + */ + private static CopyOnWriteArraySet SessionSet = new CopyOnWriteArraySet(); + + + /** + * 连接建立成功调用的方法 + */ + @OnOpen + public void onOpen(Session session) { + SessionSet.add(session); + // 在线数加1 + int cnt = OnlineCount.incrementAndGet(); + log.info("有连接加入,当前连接数为:{}", cnt); + SendMessage(session, "连接成功"); + } + + /** + * 连接关闭调用的方法 + */ + @OnClose + public void onClose(Session session) { + SessionSet.remove(session); + int cnt = OnlineCount.decrementAndGet(); + log.info("有连接关闭,当前连接数为:{}", cnt); + } + + /** + * 收到客户端消息后调用的方法 + * + * @param message + * 客户端发送过来的消息 + */ + @OnMessage + public void onMessage(String message, Session session) { + log.info("来自客户端的消息:{}",message); + SendMessage(session, "收到消息,消息内容:"+message); + + } + + /** + * 出现错误 + * @param session + * @param error + */ + @OnError + public void onError(Session session, Throwable error) { + log.error("发生错误:{},Session ID: {}",error.getMessage(),session.getId()); + error.printStackTrace(); + } + + /** + * 发送消息,实践表明,每次浏览器刷新,session会发生变化。 + * @param session + * @param message + */ + public static void SendMessage(Session session, String message) { + try { + session.getBasicRemote().sendText(String.format("%s (From Server,Session ID=%s)",message,session.getId())); + } catch (IOException e) { + log.error("发送消息出错:{}", e.getMessage()); + e.printStackTrace(); + } + } + + /** + * 群发消息 + * @param message + * @throws IOException + */ + public static void BroadCastInfo(String message) throws IOException { + for (Session session : SessionSet) { + if(session.isOpen()){ + SendMessage(session, message); + } + } + } + + /** + * 指定Session发送消息 + * @param sessionId + * @param message + * @throws IOException + */ + public static void SendMessage(String message,String sessionId) throws IOException { + Session session = null; + for (Session s : SessionSet) { + if(s.getId().equals(sessionId)){ + session = s; + break; + } + } + if(session!=null){ + SendMessage(session, message); + } + else{ + log.warn("没有找到你指定ID的会话:{}",sessionId); + } + } + +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..547c22e --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,78 @@ +server.port = 6820 + +## 数据源配置 +spring.datasource.url = jdbc:postgresql://118.24.27.47:5432/smart_urban +spring.datasource.userName = fire_manage +spring.datasource.password = fire456 +spring.datasource.driverClassName = org.postgresql.Driver + +server.tomcat.uri-encoding=UTF-8 + +# 关闭spring data 的redis仓库 +spring.data.redis.repositories.enabled = false + +# jackson 配置 +spring.jackson.serialization.write-date-keys-as-timestamps=false + +## Hikari连接池设置 +spring.datasource.hikari.auto-commit = true +spring.datasource.hikari.maximum-pool-size = 100 +spring.datasource.hikari.idle-timeout = 10000 +spring.datasource.hikari.minimum-idle = 5 +spring.datasource.hikari.validation-timeout = 3000 + +## Spring Data JPA 配置 +spring.jpa.database = POSTGRESQL +spring.jpa.database-platform = org.hibernate.dialect.PostgreSQLDialect +spring.jpa.show-sql = true +# 指定 ddl mode (none, validate, create, create-drop, update) +spring.jpa.hibernate.ddl-auto = update +# 命名策略 +spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy +spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults = false + +## Redis配置 12 +#spring.cache.type = redis +#spring.redis.database = 12 +#spring.redis.host = localhost +#spring.redis.port = 6379 +#spring.redis.password=sdust2020 +#spring.redis.timeout = 10000 +#spring.redis.lettuce.pool.max-active = 100 +#spring.redis.lettuce.pool.max-wait = 10000 +#spring.redis.lettuce.pool.max-idle = 100 +#spring.redis.lettuce.pool.min-idle = 1 +#spring.redis.lettuce.shutdown-timeout = 0 5A8B67D1-3A8D-A942-8DC4-5D463BA900B3 506E8FC6-11DA-FE46-8DE4-38CCD353EA20 + +spring.activemq.broker-url=tcp://111.26.161.203:61616 +spring.activemq.packages.trust-all=true +spring.activemq.user=admin +spring.activemq.password=admin +spring.activemq.topic-name=mq.alarm.msg.topic.1 +spring.activemq.topic-name1=mq.event.msg.topic.1 +# 默认代理URL是否应该在内存中。如果指定了显式代理,则忽略此值。 +spring.activemq.in-memory=true +# 是否在回滚回滚消息之前停止消息传递。这意味着当启用此命令时,消息顺序不会被保留。 +spring.activemq.non-blocking-redelivery=false +# 等待消息发送响应的时间。设置为0等待永远。 +spring.activemq.send-timeout=0 +#默认情况下activemq提供的是queue模式,若要使用topic模式需要配置下面配置 +spring.jms.pub-sub-domain=true + +## Devtools配置 +spring.devtools.livereload.enabled = true + +## 多国语言配置 +spring.messages.basename = i18n/messages +spring.messages.encoding = UTF-8 + +# 上传文件配置 +spring.servlet.multipart.enabled=true +# 最大文件大小 +spring.servlet.multipart.max-file-size = 100MB +# 最大请求大小 +spring.servlet.multipart.max-request-size = 100MB + +spring.main.allow-bean-definition-overriding = true + + diff --git a/src/main/resources/i18n/messages.properties b/src/main/resources/i18n/messages.properties new file mode 100644 index 0000000..8cfedc5 --- /dev/null +++ b/src/main/resources/i18n/messages.properties @@ -0,0 +1,162 @@ +sys.message.success = 成功 +sys.message.null = 暂时还没有数据 +sys.message.exit = 已存在相关内容,请勿重复操作 +sys.message.user.not_logged_in = 用户未登录 +sys.message.user.login_error = 用户登陆失败 +sys.message.user.account_forbidden = 用户已被禁止 +sys.message.user.account_not_activated = 用户未激活 +sys.message.user.overtime = 用户登录超时 +sys.message.user.no_permission = 用户权限不足 +sys.message.user.already_logged = 用户已经登录 +sys.message.param.null = 参数为空 +sys.message.param.illegal = 参数非法 +sys.message.data.none = 数据为空 +sys.message.data.wrong = 数据错误 +sys.message.process.fail = 处理失败 +sys.message.process.overtime = 处理超时 +sys.message.system.inner_error = 系统内部错误 +sys.message.system.abnormal = 系统异常 +sys.message.system.busy = 系统正忙,请稍后 +sys.message.system.maintain = 系统维护中 +sys.message.database.error = 数据库错误 +sys.message.file.exists = 文件已存在 +sys.message.file.write.error = 文件写入失败 +sys.message.file.read.error = 文件读取失败 + +SysUser.id.blank = 用户id不能为空 +SysUser.userName.blank = 用户名不能为空 +SysUser.userName.length.illegal = 用户名长度不合法 +SysUser.userName.format.illegal = 用户名格式不合法 +SysUser.nickName.length.illegal = 用户昵称长度不合法 +SysUser.userCode.length.illegal = 用户编码长度不合法 +SysUser.userCode.format.illegal = 用户编码含有非法字符 +SysUser.password.blank = 用户密码不能为空 +SysUser.password.length.illegal = 用户密码长度不合法 +SysUser.password.format.illegal = 用户密码格式不合法 +SysUser.sex.num.null = 用户性别数字不能为空 +SysUser.sex.num.illegal = 用户性别数字不合法 +SysUser.telephone.format.illegal = 用户手机号码不合法 +SysUser.email.length.illegal = 用户邮箱长度不合法 +SysUser.email.format.illegal = 用户邮箱格式不合法 +SysUser.admCode.illegal = 用户行政区划代码不合法 +SysUser.address.length.illegal = 用户详细地址长度不合法 +SysUser.birthday.format.illegal = 用户出生日期必须是 yyyy-MM-dd +SysUser.personalSignature.length.illegal = 用户个性签名长度非法 +SysUser.portraitId.illegal = 用户头像id非法 +SysUser.statusCode.null = 用户当前状态编码为空 +SysUser.statusCode.illegal = 用户当前状态编码非法 +SysUser.deleteFlag.null = 用户删除标记不能是空 + +TrajectoryRecord.id.null = id 不能为空 +TrajectoryRecord.startTimeTs.null = startTimeTs 不能为空 + +SatelliteFirePoint.id.null = id 不能为空 +SatelliteFirePoint.fireCode.null = fireCode 不能为空 +SatelliteFirePoint.fireCode.length.illegal = fireCode 长度过长 +SatelliteFirePoint.countyCode.null = countyCode 不能为空 +SatelliteFirePoint.countyCode.length.illegal = countyCode 长度过长 +SatelliteFirePoint.countyName.null = countyName 不能为空 +SatelliteFirePoint.countyName.length.illegal = countyName 长度过长 +SatelliteFirePoint.satelliteType.null = satelliteType 不能为空 +SatelliteFirePoint.fireStatus.null = fireStatus 不能为空 +SatelliteFirePoint.verifyStatus.null = verifyStatus 不能为空 +SatelliteFirePoint.satelliteTimeTs.null = satelliteTimeTs 不能为空 + +PictureFirePoint.id.null = id 不能为空 +PictureFirePoint.fireCode.null = fireCode 不能为空 +PictureFirePoint.fireCode.length.illegal = fireCode 长度过长 +PictureFirePoint.pictureName.length.illegal = pictureName 长度过长 +PictureFirePoint.picturePath.null = picturePath 不能为空 +PictureFirePoint.picturePath.length.illegal = picturePath 长度过长 +PictureFirePoint.memo.length.illegal = memo 长度过长 +PictureFirePoint.size.length.illegal = size 长度过长 +PictureFirePoint.md5.null = md5 不能为空 +PictureFirePoint.md5.length.illegal = md5 长度过长 +PictureFirePoint.shootTimeTs.null = shootTimeTs 不能为空 + +PersonInvestigator.id.null = id 不能为空 +PersonInvestigator.name.null = name 不能为空 +PersonInvestigator.name.length.illegal = name 长度过长 +PersonInvestigator.code.length.illegal = code 长度过长 +PersonInvestigator.telephone.null = telephone 不能为空 +PersonInvestigator.telephone.format.illegal = telephone 格式非法 +PersonInvestigator.sex.null = sex 不能为空 +PersonInvestigator.sex.format.illegal = sex 格式非法 +PersonInvestigator.idCard.length.illegal = idCard 长度过长 +PersonInvestigator.memo.length.illegal = memo 长度过长 +PersonInvestigator.linkUserName.null = linkUserName 不能为空 +PersonInvestigator.linkUserName.format.illegal = linkUserName 格式非法 +PersonInvestigator.password.null = password 不能为空 +PersonInvestigator.password.format.illegal = password 格式非法 +InvestigateFirePoint.verifyTimeTs.null = verifyTimeTs 不能为空 + +InvestigateFirePoint.id.null = id 不能为空 +InvestigateFirePoint.fireCode.null = fireCode 不能为空 +InvestigateFirePoint.fireCode.length.illegal = fireCode 长度过长 +InvestigateFirePoint.countyCode.null = countyCode 不能为空 +InvestigateFirePoint.countyCode.length.illegal = countyCode 长度过长 +InvestigateFirePoint.countyName.null = countyName 不能为空 +InvestigateFirePoint.countyName.length.illegal = countyName 长度过长 +InvestigateFirePoint.address.null = address 不能为空 +InvestigateFirePoint.fireArea.null = fireArea 不能为空 +InvestigateFirePoint.verifyType.null = verifyType 不能为空 +InvestigateFirePoint.satelliteType.null = satelliteType 不能为空 +InvestigateFirePoint.satelliteType.length.illegal = satelliteType 长度过长 + +FileDocument.id.null = id 不能为空 +FileDocument.name.null = name 不能为空 +FileDocument.name.length.illegal = name 长度过长 +FileDocument.category.length.illegal = category 长度过长 +FileDocument.filePath.null = filePath 不能为空 +FileDocument.filePath.length.illegal = filePath 长度过长 +FileDocument.size.length.illegal = size 长度过长 +FileDocument.md5.null = md5 不能为空 +FileDocument.md5.length.illegal = md5 长度过长 +FileDocument.memo.length.illegal = memo 长度过长 +FileDocument.checkingToken.length.illegal = checkingToken 长度过长 + +AppFile.id.null = id 不能为空 +AppFile.name.null = name 不能为空 +AppFile.name.length.illegal = name 长度过长 +AppFile.appPath.null = appPath 不能为空 +AppFile.appPath.length.illegal = appPath 长度过长 +AppFile.versionNumber.null = versionNumber 不能为空 +AppFile.versionNumber.length.illegal = versionNumber 长度过长 +AppFile.versionNumber.format.illegal = versionNumber 格式不合法 +AppFile.size.null = size 不能为空 +AppFile.size.length.illegal = size 长度过长 +AppFile.md5.null = md5 不能为空 +AppFile.md5.length.illegal = md5 长度过长 +AppFile.memo.length.illegal = memo 长度过长 + +RealFirePoint.id.null = id 不能为空 +RealFirePoint.fireCode.null = fireCode 不能为空 +RealFirePoint.fireCode.length.illegal = fireCode 不合法 +RealFirePoint.countyCode.null = countyCode 不能为空 +RealFirePoint.countyCode.length.illegal = countyCode 不合法 +RealFirePoint.countyName.null = countyName 不能为空 +RealFirePoint.countyName.length.illegal = countyName 不合法 +RealFirePoint.address.null = address 不能为空 +RealFirePoint.address.length.illegal = address 不合法 +RealFirePoint.fireArea.null = fireArea 不能为空 +RealFirePoint.verifyTimeTs.null = verifyTimeTs 不能为空 +RealFirePoint.verifyType.null = verifyType 不能为空 + +InvestigationTeam.id.null = id 不能为空 +InvestigationTeam.teamName.null = teamName 不能为空 +InvestigationTeam.teamName.length.illegal = teamName 长度过长 +InvestigationTeam.memo.length.illegal = memo 长度过长 +InvestigationTeam.userName.null = userName 不能为空 +InvestigationTeam.userName.length.illegal = userName 长度过长 +InvestigationTeam.userRealName.null = userRealName 不能为空 +InvestigationTeam.userRealName.length.illegal = userRealName 长度过长 + +RelTeamInvestigator.id.null = id 不能为空 +RelTeamInvestigator.teamCode.null = teamCode 不能为空 +RelTeamInvestigator.teamCode.length.illegal = teamCode 长度过长 +RelTeamInvestigator.userName.null = userName 不能为空 +RelTeamInvestigator.userName.length.illegal = userName 长度过长 +RelTeamInvestigator.teamDeputy.null = teamDeputy 不能为空 +RelTeamInvestigator.memo.length.illegal = memo 长度过长 +RelTeamInvestigator.userRealName.null = userRealName 不能为空 +RelTeamInvestigator.userRealName.length.illegal = userRealName 长度过长 diff --git a/src/main/resources/i18n/messages_en_US.properties b/src/main/resources/i18n/messages_en_US.properties new file mode 100644 index 0000000..9b23e85 --- /dev/null +++ b/src/main/resources/i18n/messages_en_US.properties @@ -0,0 +1,162 @@ +sys.message.success = success +sys.message.null = null +sys.message.exit = data already exists +sys.message.user.not_logged_in = user not logged in +sys.message.user.login_error = user login failed +sys.message.user.account_forbidden = this account is forbidden +sys.message.user.account_not_activated = this account not activated +sys.message.user.overtime = user login timeout +sys.message.user.no_permission = user permission denied +sys.message.user.already_logged = user already logged +sys.message.param.null = parameter is null +sys.message.param.illegal = parameter is illegal +sys.message.data.none = data is none +sys.message.data.wrong = data is wrong +sys.message.process.fail = process fail +sys.message.process.overtime = process overtime +sys.message.system.inner_error = system inner error +sys.message.system.abnormal = system abnormal +sys.message.system.busy = system is busy +sys.message.system.maintain = system maintenance +sys.message.database.error = database error +sys.message.file.exists = file exists +sys.message.file.write.error = file write error +sys.message.file.read.error = file read error + +SysUser.id.blank = user id can't be blank +SysUser.userName.blank = user name can't be blank +SysUser.userName.length.illegal = user name length illegal +SysUser.userName.format.illegal = user name format illegal +SysUser.nickName.length.illegal = user nickname length illegal +SysUser.userCode.length.illegal = user code length illegal +SysUser.userCode.format.illegal = user code cantains illegal character +SysUser.password.blank = user password can't be blank +SysUser.password.length.illegal = user password length illegal +SysUser.password.format.illegal = user password format illegal +SysUser.sex.num.null = user sex num can't be blank +SysUser.sex.num.illegal = user sex num illegal +SysUser.telephone.format.illegal = user telephone format illegal +SysUser.email.length.illegal = user email length illegal +SysUser.email.format.illegal = user email format illegal +SysUser.admCode.illegal = user administrative area code illegal +SysUser.address.length.illegal = user address length illegal +SysUser.birthday.format.illegal = user birthday must be yyyy-MM-dd +SysUser.personalSignature.length.illegal = user personal signature length illegal +SysUser.portraitId.illegal = user portrait id illegal +SysUser.statusCode.null = user current status code can't be blank +SysUser.statusCode.illegal = user current status code illegal +SysUser.deleteFlag.null = user delete flag can't be null + +TrajectoryRecord.id.null = id can't be null +TrajectoryRecord.startTimeTs.null = startTimeTs can't be null + +SatelliteFirePoint.id.null = id can't be blank +SatelliteFirePoint.fireCode.null = fireCode can't be blank +SatelliteFirePoint.fireCode.length.illegal = fireCode length illegal +SatelliteFirePoint.countyCode.null = countyCode can't be blank +SatelliteFirePoint.countyCode.length.illegal = countyCode length illegal +SatelliteFirePoint.countyName.null = countyName can't be blank +SatelliteFirePoint.countyName.length.illegal = countyName length illegal +SatelliteFirePoint.satelliteType.null = satelliteType can't be blank +SatelliteFirePoint.fireStatus.null = fireStatus can't be blank +SatelliteFirePoint.verifyStatus.null = verifyStatus can't be blank +SatelliteFirePoint.satelliteTimeTs.null = satelliteTimeTs can't be blank + +PictureFirePoint.id.null = id can't be blank +PictureFirePoint.fireCode.null = fireCode can't be blank +PictureFirePoint.fireCode.length.illegal = fireCode length illegal +PictureFirePoint.pictureName.length.illegal = pictureName length illegal +PictureFirePoint.picturePath.null = picturePath can't be blank +PictureFirePoint.picturePath.length.illegal = picturePath length illegal +PictureFirePoint.memo.length.illegal = memo length illegal +PictureFirePoint.size.length.illegal = size length illegal +PictureFirePoint.md5.null = md5 can't be blank +PictureFirePoint.md5.length.illegal = md5 length illegal +PictureFirePoint.shootTimeTs.null = shootTimeTs can't be blank + +PersonInvestigator.id.null = id can't be blank +PersonInvestigator.name.null = name can't be blank +PersonInvestigator.name.length.illegal = name length illegal +PersonInvestigator.code.length.illegal = code length illegal +PersonInvestigator.telephone.null = telephone can't be blank +PersonInvestigator.telephone.format.illegal = telephone format illegal +PersonInvestigator.sex.null = sex format can't be blank +PersonInvestigator.sex.format.illegal = sex format illegal +PersonInvestigator.idCard.length.illegal = idCard length illegal +PersonInvestigator.memo.length.illegal = memo length illegal +PersonInvestigator.linkUserName.null = linkUserName can't be blank +PersonInvestigator.linkUserName.format.illegal = linkUserName format illegal +PersonInvestigator.password.null = password can't be blank +PersonInvestigator.password.format.illegal = password format +InvestigateFirePoint.verifyTimeTs.null = verifyTimeTs can't be blank + +InvestigateFirePoint.id.null = id can't be blank +InvestigateFirePoint.fireCode.null = fireCode can't be blank +InvestigateFirePoint.fireCode.length.illegal = fireCode length illegal +InvestigateFirePoint.countyCode.null = countyCode can't be blank +InvestigateFirePoint.countyCode.length.illegal = countyCode length illegal +InvestigateFirePoint.countyName.null = countyName can't be blank +InvestigateFirePoint.countyName.length.illegal = countyName length illegal +InvestigateFirePoint.address.null = address can't be blank +InvestigateFirePoint.fireArea.null = fireArea can't be blank +InvestigateFirePoint.verifyType.null = verifyType can't be blank +InvestigateFirePoint.satelliteType.null = satelliteType can't be blank +InvestigateFirePoint.satelliteType.length.illegal = satelliteType length illegal + +FileDocument.id.null = id can't be blank +FileDocument.name.null = name can't be blank +FileDocument.name.length.illegal = name length illegal +FileDocument.category.length.illegal = category length illegal +FileDocument.filePath.null = filePath can't be blank +FileDocument.filePath.length.illegal = filePath length illegal +FileDocument.size.length.illegal = size length illegal +FileDocument.md5.null = md5 can't be blank +FileDocument.md5.length.illegal = md5 length illegal +FileDocument.memo.length.illegal = memo length illegal +FileDocument.checkingToken.length.illegal = checkingToken length illegal + +AppFile.id.null = id can't be blank +AppFile.name.null = name can't be blank +AppFile.name.length.illegal = name length illegal +AppFile.appPath.null = appPath can't be blank +AppFile.appPath.length.illegal = appPath length illegal +AppFile.versionNumber.null = versionNumber can't be blank +AppFile.versionNumber.length.illegal = versionNumber length illegal +AppFile.versionNumber.format.illegal = versionNumber illegal +AppFile.size.null = size can't be blank +AppFile.size.length.illegal = size length illegal +AppFile.md5.null = md5 can't be blank +AppFile.md5.length.illegal = md5 length illegal +AppFile.memo.length.illegal = memo length illegal + +RealFirePoint.id.null = id can't be blank +RealFirePoint.fireCode.null = fireCode can't be blank +RealFirePoint.fireCode.length.illegal = fireCode illegal +RealFirePoint.countyCode.null = countyCode can't be blank +RealFirePoint.countyCode.length.illegal = countyCode +RealFirePoint.countyName.null = countyName can't be blank +RealFirePoint.countyName.length.illegal = countyName illegal +RealFirePoint.address.null = address can't be blank +RealFirePoint.address.length.illegal = address illegal +RealFirePoint.fireArea.null = fireArea can't be blank +RealFirePoint.verifyTimeTs.null = verifyTimeTs can't be blank +RealFirePoint.verifyType.null = verifyType can't be blank + +InvestigationTeam.id.null = id can't be blank +InvestigationTeam.teamName.null = teamName can't be blank +InvestigationTeam.teamName.length.illegal = teamName length illegal +InvestigationTeam.memo.length.illegal = memo length illegal +InvestigationTeam.userName.null = userName can't be blank +InvestigationTeam.userName.length.illegal = userName length illegal +InvestigationTeam.userRealName.null = userRealName can't be blank +InvestigationTeam.userRealName.length.illegal = userRealName length illegal + +RelTeamInvestigator.id.null = id can't be blank +RelTeamInvestigator.teamCode.null = teamCode can't be blank +RelTeamInvestigator.teamCode.length.illegal = teamCode length illegal +RelTeamInvestigator.userName.null = userName can't be blank +RelTeamInvestigator.userName.length.illegal = userName length illegal +RelTeamInvestigator.teamDeputy.null = teamDeputy can't be blank +RelTeamInvestigator.memo.length.illegal = memo length illegal +RelTeamInvestigator.userRealName.null = userRealName can't be blank +RelTeamInvestigator.userRealName.length.illegal = userRealName length illegal diff --git a/src/main/resources/i18n/messages_zh_CN.properties b/src/main/resources/i18n/messages_zh_CN.properties new file mode 100644 index 0000000..8cfedc5 --- /dev/null +++ b/src/main/resources/i18n/messages_zh_CN.properties @@ -0,0 +1,162 @@ +sys.message.success = 成功 +sys.message.null = 暂时还没有数据 +sys.message.exit = 已存在相关内容,请勿重复操作 +sys.message.user.not_logged_in = 用户未登录 +sys.message.user.login_error = 用户登陆失败 +sys.message.user.account_forbidden = 用户已被禁止 +sys.message.user.account_not_activated = 用户未激活 +sys.message.user.overtime = 用户登录超时 +sys.message.user.no_permission = 用户权限不足 +sys.message.user.already_logged = 用户已经登录 +sys.message.param.null = 参数为空 +sys.message.param.illegal = 参数非法 +sys.message.data.none = 数据为空 +sys.message.data.wrong = 数据错误 +sys.message.process.fail = 处理失败 +sys.message.process.overtime = 处理超时 +sys.message.system.inner_error = 系统内部错误 +sys.message.system.abnormal = 系统异常 +sys.message.system.busy = 系统正忙,请稍后 +sys.message.system.maintain = 系统维护中 +sys.message.database.error = 数据库错误 +sys.message.file.exists = 文件已存在 +sys.message.file.write.error = 文件写入失败 +sys.message.file.read.error = 文件读取失败 + +SysUser.id.blank = 用户id不能为空 +SysUser.userName.blank = 用户名不能为空 +SysUser.userName.length.illegal = 用户名长度不合法 +SysUser.userName.format.illegal = 用户名格式不合法 +SysUser.nickName.length.illegal = 用户昵称长度不合法 +SysUser.userCode.length.illegal = 用户编码长度不合法 +SysUser.userCode.format.illegal = 用户编码含有非法字符 +SysUser.password.blank = 用户密码不能为空 +SysUser.password.length.illegal = 用户密码长度不合法 +SysUser.password.format.illegal = 用户密码格式不合法 +SysUser.sex.num.null = 用户性别数字不能为空 +SysUser.sex.num.illegal = 用户性别数字不合法 +SysUser.telephone.format.illegal = 用户手机号码不合法 +SysUser.email.length.illegal = 用户邮箱长度不合法 +SysUser.email.format.illegal = 用户邮箱格式不合法 +SysUser.admCode.illegal = 用户行政区划代码不合法 +SysUser.address.length.illegal = 用户详细地址长度不合法 +SysUser.birthday.format.illegal = 用户出生日期必须是 yyyy-MM-dd +SysUser.personalSignature.length.illegal = 用户个性签名长度非法 +SysUser.portraitId.illegal = 用户头像id非法 +SysUser.statusCode.null = 用户当前状态编码为空 +SysUser.statusCode.illegal = 用户当前状态编码非法 +SysUser.deleteFlag.null = 用户删除标记不能是空 + +TrajectoryRecord.id.null = id 不能为空 +TrajectoryRecord.startTimeTs.null = startTimeTs 不能为空 + +SatelliteFirePoint.id.null = id 不能为空 +SatelliteFirePoint.fireCode.null = fireCode 不能为空 +SatelliteFirePoint.fireCode.length.illegal = fireCode 长度过长 +SatelliteFirePoint.countyCode.null = countyCode 不能为空 +SatelliteFirePoint.countyCode.length.illegal = countyCode 长度过长 +SatelliteFirePoint.countyName.null = countyName 不能为空 +SatelliteFirePoint.countyName.length.illegal = countyName 长度过长 +SatelliteFirePoint.satelliteType.null = satelliteType 不能为空 +SatelliteFirePoint.fireStatus.null = fireStatus 不能为空 +SatelliteFirePoint.verifyStatus.null = verifyStatus 不能为空 +SatelliteFirePoint.satelliteTimeTs.null = satelliteTimeTs 不能为空 + +PictureFirePoint.id.null = id 不能为空 +PictureFirePoint.fireCode.null = fireCode 不能为空 +PictureFirePoint.fireCode.length.illegal = fireCode 长度过长 +PictureFirePoint.pictureName.length.illegal = pictureName 长度过长 +PictureFirePoint.picturePath.null = picturePath 不能为空 +PictureFirePoint.picturePath.length.illegal = picturePath 长度过长 +PictureFirePoint.memo.length.illegal = memo 长度过长 +PictureFirePoint.size.length.illegal = size 长度过长 +PictureFirePoint.md5.null = md5 不能为空 +PictureFirePoint.md5.length.illegal = md5 长度过长 +PictureFirePoint.shootTimeTs.null = shootTimeTs 不能为空 + +PersonInvestigator.id.null = id 不能为空 +PersonInvestigator.name.null = name 不能为空 +PersonInvestigator.name.length.illegal = name 长度过长 +PersonInvestigator.code.length.illegal = code 长度过长 +PersonInvestigator.telephone.null = telephone 不能为空 +PersonInvestigator.telephone.format.illegal = telephone 格式非法 +PersonInvestigator.sex.null = sex 不能为空 +PersonInvestigator.sex.format.illegal = sex 格式非法 +PersonInvestigator.idCard.length.illegal = idCard 长度过长 +PersonInvestigator.memo.length.illegal = memo 长度过长 +PersonInvestigator.linkUserName.null = linkUserName 不能为空 +PersonInvestigator.linkUserName.format.illegal = linkUserName 格式非法 +PersonInvestigator.password.null = password 不能为空 +PersonInvestigator.password.format.illegal = password 格式非法 +InvestigateFirePoint.verifyTimeTs.null = verifyTimeTs 不能为空 + +InvestigateFirePoint.id.null = id 不能为空 +InvestigateFirePoint.fireCode.null = fireCode 不能为空 +InvestigateFirePoint.fireCode.length.illegal = fireCode 长度过长 +InvestigateFirePoint.countyCode.null = countyCode 不能为空 +InvestigateFirePoint.countyCode.length.illegal = countyCode 长度过长 +InvestigateFirePoint.countyName.null = countyName 不能为空 +InvestigateFirePoint.countyName.length.illegal = countyName 长度过长 +InvestigateFirePoint.address.null = address 不能为空 +InvestigateFirePoint.fireArea.null = fireArea 不能为空 +InvestigateFirePoint.verifyType.null = verifyType 不能为空 +InvestigateFirePoint.satelliteType.null = satelliteType 不能为空 +InvestigateFirePoint.satelliteType.length.illegal = satelliteType 长度过长 + +FileDocument.id.null = id 不能为空 +FileDocument.name.null = name 不能为空 +FileDocument.name.length.illegal = name 长度过长 +FileDocument.category.length.illegal = category 长度过长 +FileDocument.filePath.null = filePath 不能为空 +FileDocument.filePath.length.illegal = filePath 长度过长 +FileDocument.size.length.illegal = size 长度过长 +FileDocument.md5.null = md5 不能为空 +FileDocument.md5.length.illegal = md5 长度过长 +FileDocument.memo.length.illegal = memo 长度过长 +FileDocument.checkingToken.length.illegal = checkingToken 长度过长 + +AppFile.id.null = id 不能为空 +AppFile.name.null = name 不能为空 +AppFile.name.length.illegal = name 长度过长 +AppFile.appPath.null = appPath 不能为空 +AppFile.appPath.length.illegal = appPath 长度过长 +AppFile.versionNumber.null = versionNumber 不能为空 +AppFile.versionNumber.length.illegal = versionNumber 长度过长 +AppFile.versionNumber.format.illegal = versionNumber 格式不合法 +AppFile.size.null = size 不能为空 +AppFile.size.length.illegal = size 长度过长 +AppFile.md5.null = md5 不能为空 +AppFile.md5.length.illegal = md5 长度过长 +AppFile.memo.length.illegal = memo 长度过长 + +RealFirePoint.id.null = id 不能为空 +RealFirePoint.fireCode.null = fireCode 不能为空 +RealFirePoint.fireCode.length.illegal = fireCode 不合法 +RealFirePoint.countyCode.null = countyCode 不能为空 +RealFirePoint.countyCode.length.illegal = countyCode 不合法 +RealFirePoint.countyName.null = countyName 不能为空 +RealFirePoint.countyName.length.illegal = countyName 不合法 +RealFirePoint.address.null = address 不能为空 +RealFirePoint.address.length.illegal = address 不合法 +RealFirePoint.fireArea.null = fireArea 不能为空 +RealFirePoint.verifyTimeTs.null = verifyTimeTs 不能为空 +RealFirePoint.verifyType.null = verifyType 不能为空 + +InvestigationTeam.id.null = id 不能为空 +InvestigationTeam.teamName.null = teamName 不能为空 +InvestigationTeam.teamName.length.illegal = teamName 长度过长 +InvestigationTeam.memo.length.illegal = memo 长度过长 +InvestigationTeam.userName.null = userName 不能为空 +InvestigationTeam.userName.length.illegal = userName 长度过长 +InvestigationTeam.userRealName.null = userRealName 不能为空 +InvestigationTeam.userRealName.length.illegal = userRealName 长度过长 + +RelTeamInvestigator.id.null = id 不能为空 +RelTeamInvestigator.teamCode.null = teamCode 不能为空 +RelTeamInvestigator.teamCode.length.illegal = teamCode 长度过长 +RelTeamInvestigator.userName.null = userName 不能为空 +RelTeamInvestigator.userName.length.illegal = userName 长度过长 +RelTeamInvestigator.teamDeputy.null = teamDeputy 不能为空 +RelTeamInvestigator.memo.length.illegal = memo 长度过长 +RelTeamInvestigator.userRealName.null = userRealName 不能为空 +RelTeamInvestigator.userRealName.length.illegal = userRealName 长度过长