From 7cfdc69b88bf30fc9d8fe409206adcdfc6ef88fd Mon Sep 17 00:00:00 2001 From: "Zhang.H.N" Date: Wed, 6 Apr 2022 19:05:36 +0800 Subject: [PATCH] support form validation --- README.md | 2 + action/autocard.jar | Bin 14927534 -> 14929431 bytes config/application.json | 3 +- pom.xml | 2 +- .../java/org/gcszhn/autocard/AppConfig.java | 57 ++++++++++++--- .../autocard/service/AutoClockinJob.java | 1 + .../autocard/service/ClockinService.java | 66 ++++++++++++++++-- .../java/org/gcszhn/autocard/AppTest.java | 4 +- .../org/gcszhn/autocard/AutoClockinTest.java | 10 ++- 9 files changed, 127 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 8df658c..2fe86e1 100644 --- a/README.md +++ b/README.md @@ -209,6 +209,8 @@ Fork本项目,在fork后的仓库里”Setttings > Secrets > Actions > New rep 若打卡题目被更新或者你的任何信息情况有变化(如返校),请先手动打卡一次。本项目仅供学习参考。使用时请确保信息的正确性。滥用造成的后果请自行承担。 ## 更新记录 +### v1.4.3 +支持对健康打卡进行表单数据校验,检测健康打卡表单是否更新,当表单校验不通过,意味着健康打卡已经更新,请清除数据缓存`autocard_cache.json`文件并重启打卡程序。例如2022年4月6日浙江大学对表单有所更新。 ### v1.4.2 修复了多用户立即打卡时,后续用户无效的问题。支持了通过系统环境变量来配置打卡用户。 diff --git a/action/autocard.jar b/action/autocard.jar index 3fa5de9b9900ecaf9610e053af6c1ea1c6c35414..540ef72f551a580fb2fe7ff442918179e54b7027 100644 GIT binary patch delta 17625 zcmZwO1ymK=*8p(3yE_DwmhNup?naRAM!W*jotN(Jl9VnHkOt|vAe~AmAm4!Rz5jdP z@~-t;F#GI1=ggV8ckaMUb#yEuI5QRzT~z@V4i5?m2?7)5bgD zf!)oQH+XD;5*h7R#CN-az6C-40vHqy+^m^sGkTX-Di7_3$ zHl>b!frk*}ptVVW4tvAxTHPvj(rm%>a~0IZ7r~&NqWdzXzrOoDLSkevzom0}tQUrk zuzm@VSN#i?VQ~^vh1O5FR~o4xdK3K<7D)yx9tbR!NLav&8S>857;`l?9Tz;Um-DH$ z8r4r6T#FnuyS6^*$8_^5yq%&)8_bP9Oj$R~DjMVnWT9Yom&4U~dRF5<5qw;gjj`t9w>e%y2mpc{22R|13U5L=N-Kys1+s_ z)YU%(5GHM1!;*5#W zD92!dOr{Rq$T-cfi~(96ev9KudZL>{2QJZpc3Os^2k?RHG{jRlXKD_bs7KQrDO{|L z!i#INKWu#HF+U&3>k;$sxa@#*D;i%FM3AOJo!Y#s_@3LH{*59jX0dQnCXI1bxA)^N z3J)hig8-UR>L9cOSKiHJNNt3tI}-EM;9&lO`y#E-clLno0ewQ{FB^O5eN_idso71N zGV;*WANGaze@sTZQZ^|#7fv70i3uRZU9bz1R$z(O1d8C_5jktn)2Io8I>%=cenR(U zq79&FVD6Qbsp{A=I+BzXX6GkX40_mgb4|6`kmKp- z8`X+vrMXmFm->hfD&^`fK@=@&NTX$Qgs^v_825HFsf@(DI*tC5iX7PGcK?>NP7F-U z3$J&Gf>#)_?Fe@*@achSAbfTkclL8kD`w&0bD6J=Ul8lHP+uW>ddlC%RHTX-iiN*w zOR%vKj-=XFRG+q&;ni@@oZ%{a0w1K3Rsch)KzF|o>=84*%tGl%2Wd`FN{f zd-e`asDSGcvRzW43&_eXJu4b+>No9TntLJAl7!<`UH@cYy64c1xtBUKlaFsb@ zqm3N(4Z}y6<=0puij}YG3y2L=hU-eCrs%KbMDF#T`Ta!wand(L6hEIW@@BsS^LDHH z##s2QOoyW7W*gF~K`Hoiu_1Q&__`&6rk>lk9RcwM(f=AwD-ksKrQ8ETLYZASqjPe9 z&2&ZP#+NC;q8M{6cdOBC@%>K&#?I)j{KOx(m2GXR(bpIQ?^YIB=|Cz2SC&xa zUleXoHdQu58Q&zWQL^GyBwmx^gEH1A>1WcrO3c#=lT+|O_obUT=Xh$z4xAkBA4l90 zTAx|&LE0G3?jjT~bjF>;^Efm6RWxrKb$j0)B_v+AMwqOd=xVfxmLPJ^OS3kbEtsBe ztZCl1PnTGFfq(r{!M_hS5jgvZiA72I_tdS^YH#?lw#S*u;BlMgz0s=Yr&|$3gsQ1d23$`RQ=H z%b1XKKH23El8FxWCI88(>eXs=#3?FQG6vU~XNDFTXy+7q?B#TIBjS|v5KoM9z>l7n z<~wVw%Nsq@W?gZbvYQMY&ojto$Efujwrs4t&7}(RK6vqtTn&^Nv#FEK=j&zO1{;^g zxrOqGeAC4}zb;Qmt~|}BO)gY0PwdcN#5ds1W|Mi}Z_ID0iYy^?kK!iIpla99u-ACqf2be`4_3=7tJNbc~LbDeI3Q6O0tpRS-49<5a z&%iRnc?|a$`pL{ZUwf0)`vpD|b;TmQ8!_dv97y~rCxpAx;LK>Ln~nxmH}M*yg6y>& z{FG{Y=DW;2gOrn_%#dpieXmvQPzGwRWoWC5AHyK6&wQ6ms5fqjdswzEn(j)pH{a?LYKqYr~ja+)-A}>*c)}- z1=kIP9BL8?$xAt&twOe7)-a;(eKW}_fHzr|uV33a`&2lo24oA(iN;_xW?$CU1cJ`dk z$wR0ioki*BMUne-Ox*3(`g>Uc=|ilR&A?HRs?_smKZ0K@6p@=(ZrH01YU?9E4?NF6 zc&i|ltlHWfcEC}&xrT0CH&1i?9)+hysj(@lE1+n1^Dz1U4p zK~r8Q0wE5C0uip20|g$JP+$l~CSX-0l4I^LY(HQyPzjxAL}~nF2M^OeZ|? zO~H*0g{$wxm%}n@(|lno9IBNzpJ3-I1(o7(Z+aL`lGJH37kCgZWiov}bxwoH3B<=j zS^O*(Pl>iId~(QgViV>GpQR-6 zQSjG$9dQ?A|E35hqr`p_VxP58gI31ojkm>3_(G@eb{)`!lKMa!p>>kI4KCpceKhZ^bEK@@pz}J$c<8~+-kHIR zo9MoQ#xPvF)Dk)u@FD`yLt!p3E$pW*8F_1Fb0fNQTwG)8iQ|)1Dx>QRQ{yncAI~mb z$Tsh$r|x8(UDmj=%}9ip(jBUE7&3ZZ#j0;p?PKNBR1bQesP@S}Pw`M&TYEe2H zQ=i)HDu`Z2zE{lWkSE3mN9yw`^`2%@i!m17_v7>YQdu-+PGlAG36WJ0W^zl>$+Dua zp(WLuJ@CfQH|FBglk|A;>;@#6Cm}^*())z4y^<=r!SQq{Db%}TjmII)ZHl`umM&k7 zE51fE?d>&p3KL&rFlgD1;J_%n53TH-Iyar8b)$l@B*s0Xf_fT@Ro&e+HOK4JgT7$% z&ouC{T9I+lBCTx^zmkrQZEvT<(LLLr-1=bOv)f4p`|rm;ss06{0}23Dn0zoclysb5 zEZ*{}hJ3zg$|kkxI%ZThGP9|L%B_w`BP{xdx(stN z{@KVzmxAvudsFXF_$UqPx8yY>!%i=(fCSqWm&~T6%lkS!dE)J7rw7EhAs)7Bg)&L} z9w(($7p{i-r4l3d%Ocbu(PEdY7lt7vx)V99AG^?!gP*onmk#4Z`K2xT|TUhmwoewzf{tir0sfNWFWp~0sK)W@-a;<}cW?R+JzS{B;NY?`;5;p@SO zed2z>VlrwjK`>gmv^WlzJU14I2EE3djZ(Wd`M*y1GA zU^`|CqPI+hk?am>Q)Jo}L!8Rs9Q$$&m$%?j{N$(H^x|Dg z&QRDpCK|J)MU@(*ZDiXj>jwPw1IB3{yD?8_FSR-+u2=h7!6EIZbnp%C%h7(v>zlL7 za;<4NyDTGWPRN-|ZPDIqaQ>Vk8@uh~IXuyrAf^aXO>ek*810Wjxp`P__@mP<&%dI! zOp&n0Jlz=^j*ZA3$@_DbQ4CZ3ay8g1@inTUOpp1OB<3_Lyl$Gms6Y2CN( zA1sg=3O7e!TA>rZ2wJc3|-14A$I}h4#BhyBho_LUWD__2FJ795HDH zK4hAdad9H_wH#TPPnpYFdQ8*&3@NHWnvxWqMG)k0bQ&UsqT*RU;uRcSMhq8GmtBec znrGqV2jp2fSshe&p1MPc-tuB@u6~Tp#rQ>Il;dm>mUO30UmWsKK#|1JdY64JemV;r zHa*znvS(y&ilZoqFMr{GVav0lFu0i-s0x80n|2|4MznjL5;SJE>XvlLnn9Hg8nh?= z*bPxXWgvep|5l)B{#z8IasGI9jzDi$4}`GBi;U#;<;%@oJ=nAa3tZz#7ky7cALY-~ zTenpgY8sx18P2*r#7~DO83Jsi@{`FrO}{J>IoZ|cU!41v4h=wzc$vpAEY0z$VMCfN ztVKmtcm9358153U8^v9jKycgVbJVbl+hKtXg~Zw6XSOc~<>?ZC;EqVo5VZf0l0^)$ z{S~$3&aS*)oxr2E;TayxPEW3u-RyW4T{`BW>`)vZHBgJTJdALQEXO&|5g+afl&7ph0*-w<_`mg=jz*l)EA?*=b?_iJpQfe+n&lKd@_Gu*=(rRV0#Toz2w{yc$NOg1&Ixz} zUscYOF&-Y$8h5=u%0k7A>!s0rJvw(a9g}D=yKPU64rRS81oSM6DoO}=t3l#)&$a{CgZVorRUedx>gtso2ns(5|wN|4zg z?@Pt%#*SR$GV00m3c@Ta{Nk+}-?(0F;=M>2?YD2E@9M#f>`BI3A25sNKDrV3kVeD2 zQd7-tT{rqkU|DoLYTOdat?0#v8XweZkF#S&aIcz#XEU#pfR6)7!Vf%SQoOL}k z$<*{|n{#|pB`A?2Q8VMkP)&4UH1P<79mm+JhWY66U?yD}-Jt&pJ?l6l z`=_l;BXjTV$|R!5t;!T4%#Zv-$v!06#zK7P_*KtRY^UFg2%&v9zi@XTp+LUOFGls* z))(dBZF^5KW5i8z&EXgR^)pGgqSqxgf%x?78#ofb z+6tlhqK=RVb*~g!8%O5xBbkFjH+pSDeJT?>z5Mp?B2sU2o-9V*v35{F^hY!_?+QH& z4hG*U97uHvR$#6XsgUydkP(OuV$!r-L?EqS+*jYElhKY1InN!+aLt_(8S0-9vw{fM{)ly1i7(%NR6&CP!$v-I}mr z$?yJDsdW^!h0>rld6tddGb}5UGqa+r9~E9cFCXoP?#GJny^@Phm^WL9FwgJh-#E;e zGW=ko-=MaK=yZf{e+kAvq|w=6(}oZ++KAz=zT}*ZwtfB9<2uz41hUe(N9I{KIi(io zd(GPJ#h$@2HikV559;77VgAQ?FVa z4gRj-lV1+EcW_QFhgO8t1_~_PK%=mTK$3d={QRda zwYF@?a#Wu-&($vy@HERMwCDmAcysH+m?NmDQ0NV4T?g9eaD5na-A z67;&-@v!R-HO?ub)3ntWJ+)3|RGMbI!ygIJQmI{;nT07AmTelI>EYtO8*oJHHAN>D zU==H-y3fEu&FpbjqM~hK1eG)@G)3srVwE!-uW!b^VOk9nci6{eEImQeUt5W$iGC6L9QHvp4sQ_7-9akW97YG~mog|42GV89s zxj7#hS9Bby&L$%|zkm#lj`NuhF)G4bD*H`4t<@ZIwP;uVGx%uh#?Y5M6V_kjKhCSY zl?X7lbSj!c;z=bq|JufIO+>C^VJ{K$NtqvR;l&)8@O-eHP4lJE4aAQ3{qeHL*Y3nu zDUNm^J4=DV4PSKL-4;+h>)XtiFg*P*J*CZDhMWfeQHLMNjW;vP*!*cYq z-php>R^-j=+CAGQDsp=V3}Tg|Q8Ljaocu3;b_Gl(vnt|6om7Wh;|eZP3KOsSw|GpK zk1Goii=OM>RwX)QTz)Ayv=MbL^k3j~sG$_~clEoO8cObcdXNgrZ`{u0aQpf_jdl5{ z?o!asao>^CVV#YEiT+}nh05!N?CUz3stVr0`D1~G1HV_~Rxr)-FcW)!j~mQDV-Oq+aI5At>YxYzC)QopQ(`KL?X? z%Sa}}6_|HXKbSPUnL(SP{6OyhZo;U=ppT5)ja_AZ&+X~iFbKcpP@2DE`-S3@W4)PjcQdigZAL@I)Y+6#D1L3*P!&7F=myijR@ zC#uHQ*LN#?ug5??;*rb?m{zRC3PKRwnFkXn5X+UMHpZmUL#8BROJ{rDsvS_vq|O~7 z>d!mcnt1hJ)=fJd*&2C<#h7_5j$N;Aml(_yLa<*O^m(~657cL)vWDv$sG#$z&MsV< z2h@6npgdoWwwY1els!ug8S<#?mKWDf;D;GQk?ZM%aVP?X%#s(*btesoG#E=;@oJkz z$2`NIT5gnkQe2kXqAcBi5vaE_e^9w7cMV;@X#J)jt^^~p#9gS0t;}VG*if^NlRQIH za}_y0tges?E4sA~yNp#xp^}#*9{Wv$Z@&_a>sL|DRIia+S($H1Yx2B%`H`vIU(HX@ zXb3oI@SIUW+0%98<8IU@E!84ehcvxOwCi&jvUTS5l0(XePZ9-aWJaao=G|X$l+A@{ zV)0sqA*(qlTk>G@cf4C6x!nCy5i@Dr&U#>aT#E7T*7ouZ`Pu+9!cck`K5c`We8SHG z@elbMTI8V<{V#(Oe0^@UiM2wS)2TzDX}7Yp**iYafNaQ22Nq-IJYIC1*riSkm=r9H zQF3?1r6m%dkB{$h$#>SPzX8X8jrH6>UwlsX9sI7mPuhs)hhx!-m1RCnU|?sTn0h-| zzZ&_FbEdMv8de6DrjMy|^AAfI;mMR?&7Xxayk`VE#3I4|JuS!I1nTB2-tULRsrZC@ znZHHWOai@<>DT{uBe2?o83s=wpg|On%Cq$vJ?*@Q5=G|Xhz6gXW)3T>pXg+;U2g2% zx<5H(sjiP%R^0?GQx0}J6IO+mq6NY@?sl=Lx?`LAkLe+SH&HoX-UzMCHRN7r^q;tC zZuCAA5;~$;#;}gEKfA~{QhbsaJbYtYAv%ACik}Wzo^(8e7~RSbm0TM8z>#cea>utbm^bkYt zpJ_25kWK+>jQ&f;Z}%$XphlELYe_95G5n|HpRb>j-$&o)h1_lsZ&>F~q^_1&GVd{? zzE14z3m%6zXMFvuCZ=m$L4dzm8z+J;;d`O1WccEmo_55O%iD#@8T;L+(LGtDI8EE! z04{lPLxaq=LWa@edHRM5X~WGYH*JcAC?ivtpqFn=^}AFTYNDucG2r{eIpn}Somg^Q9=n3zN}G6x(^eO3`>8RVGM0RNyP!z>buvPvm0?l1?2Dd zq&f}b=xc=>xYil=>ftArpoA=CgiS`QMu-{tPh={lmyL>|ODO1Gq3?4PkhqQ%$$5;( zfryKS@!Jd;bdWGvrG(pn^18svG)nD1y%JG3gD3;H`^ z)vUtjcWF=c$s)tw55gU&Vb;w&*TnY6q471_dEZadF(g0(JLGt6HKI%viwJ`~Jq{`& z=W#{^BS={JNE#&*r&&g3>3!0m)3uR;DbLz<@3)1;ULvRQdJmi&dB-5vz5=(E2Yl3ro<-^b0C(Iv1E{t}{ z3d7!rM_=j*dOV#JZdf+Q_Z{1pTFeCzeNR@SB|Z4K(&EHnRzPfrvC>pP zx1-Iv=6JTiQ3+>3ZAS!=R$zwNwY=GpRGr4EQRB0lvVC^Ga8*}fCoD810Xyo%qx1Xe zkIjr>_Kqj2xSMC>)QLYp{ydX8MN{@`SV@;|S-)Ot45WO8iH~7w>1@tbPgUjs4K}@L zD3RWuJ;v3GNh&o^x{+XHA*!w`Z?DW%)7j>g@t}KEFo-nxOhw{GXSkep$hB&8wZOgn ztkZPU{EHE`W7zd*nf6iQ?DQBazRghbNo)K^LJU1w&1NFa4b*w2yy*j)4P8E&x5%yu zI1NqFsdH#fI&#CNw_(y2*ae-Vps3l9yU8A#`d$_$%jBmO9zGItY-z^{PqA##o^y2+ zr5wwd(dIw#FR1oAC^&xm0&4+v+40@2cw+Li*!z&5-|U(Y^Ukxn94Cy=ZyO8zVwE`p zdr-di4G`B~W?FEV&Ag<_{WZsV14eHXqQ|rpV{k7p$c|S^x7Xey82PH!F$b!*Y*vUU zBbQ%3zpv^4jC%3S3VnyMpRV#Wv|pz>?8JDX?M>gFMf*|yOE}Jv9XOAvrlY#fPk0{w zZNF4+3Z5A>(>l@|`Hf5uX_u0IPE>hMzV@cg(qZY>Jfy}^VL9gb%XE{!((Asq>z~DN z!wr6$PQpvSz7Af0vS9Twc8+ZXv93|Bxh25mv-(%+ogFEiJ&Vn9;B}~~K(?H*4pL>eH|lu$tN&Lui?EyKq-fXo2HXmx`XJ>ItEKaT2(Z|5JMM%jX4Zn_w=MeGm3rreu@04C)T@?}y&F1I`hb#{yqM5z^BWS}VvsWy(fY@hgo!MI8m2W&}b1w0!i%lyvswV=EWHG18yEKo!%t>tYk`Eru7um^^vihc=@l$?R-Cv8*PT$1qj7j?V zs~4=HNuZ^BP)PhMb`n;lJ1+Ozs4Wf67d|O&iLpgtNm(G>tITaHYX2V%~2H9ch`!fCm0oE7@(&s}v9j3=Vj{fJ(Im=5N=JS(*A znKEO7ZsO8@p~sx&7V~i_t}kR-+CCUY~Q9wx>57pcg#3R*z$>wlLIMZj>Rk>R$11v&;%U;s}Zua z5v>O2(9y&ncb0j%b%{%Gi#ZZ;u^E^rky|qe+fuoH4bo;5jrzhH}G6v?pn1 zS+MaH>541qngcTLZK_>k(6`}FCZp41EFhUQUo^KqE@!gm!!Jc-QaqdeA)%IzXOrD% z^`Qly(0i)%y+4aLs8w#MB#Z-d$<0VTiB;M$cBEKNk#dz=cqQ?x;_EhtPxA(+9r@7N z(ByWz9rF`aY1!_vb5R7H%a;nxCNUO#&|Husej+KDWfgv@*d6Oq^E{og}uEIv`s=ZXeK-_u5HB5ni-ZsHNcG@GrJka0>;s z+=zRi9H!9GU!Udt-Z0>aIyKiJ1NO^lH0!$x5 zkv3~*mfmD@^t!?vZ}P3uF{&i*kQba!1Y)v`zA43#iIdD@o2xKtNoo_oTRG*?Ujz?>g)$~ z^b)3+F;qbMDc1W(LQnVRngsm}lVm&&QflG%oon;-(J^zLAq6J3KsnW45U}R4>1^H* z1$jA>zj=FbCW*I!7c}aZc`p*whls{2YF_#yY~nW6^o~8-lE@oYl&(ZY?g!`RTmD6) zLD3EHHA+`Kl^*)V&{*Wlc&VA5?$qqJSY0?+n_zShHQ_I2wQy_+ibm|>cX97SMRgI! zNCqzuBsik&5R23(sJdQZ#|>mc_TQx|EkM6lo9NV}@&#l4P`_&?FyC&r=oI^;)1oK- zqK|J(JQMABlwiN5t-cpaVT8k(^1J$QLtl^C$WxN#ENZ{fZGtYVwUaM5q@_0y=G)bT z*QLodpcdvGHFrt-;VOR{xip#XwwB$5&qYIYIQZZWcNv}P&8DUq`T8cu9}y!~sT z2f1`VkkUh^^v>Yt=KI3=+{GLdWpNgy38uRG>64Mr=0YuB$IMU@%Nwm7YhEN1keW`o z5S#EnQ*ab{(%i{PSXL$1yyeWS_MLnLNulc2m8~mfVR+ZoC&`Vnf$D405JOL5y>fO! z3IB%yU&n^*rK%m&`b5szHNio%4~K2DG|xXdE})LzLqGm^@SodCX}Jv_ZzB!jmPC5I zuXI>g<1s)fQjhQ$n32+c46sQ5f_@CJ%4$9af@Nz^9s|7UIuC)wc=h_nWf={<$Ao+h zG_1!!orU^i0O4gW%wwR;O7}4U_v+~PI=@>RKL)gHDqtS(X6&4#sIa)z?*>k-)njsr1cp166Ex|FZmFa$3SL?FWh4Z zapdm}a*gEvolzeBdwBQJ?!N<`alcpkDQ+JYPW9pTYAla-hTp%@`}9#JI_JYBiX|f^ zVkV=&0_}-p$s!nUqe3+EK7f5G5ul*bDZto${=`5I7Lv1*1FN@#{a9jJvLZ-|vi#@q z&E4wG%}u9{-K$-{K`2gTYiEZJXr4KAGQJr)@vFt3M}5ts6$gTs>G&LrLosZ$9^Exn zSMQLTslQJ`AwmUz+%pr?BV6^}dAbZkT8AgyD!fHoAi~cN{VXG*y0vcd?a9irU|f_4 z$BS-w6f)*kzJ;&0F0le~^r{LVOwLdAAh^lad`(Sp*jg`n;}#DVUU(4W}c%E>`l>yUD!K>)g;AICvw7r&y9Eh`m17Tvs8O zlMKm!DpOwQ<#{GX;`e7sPikz2y0ezOxcYXAUddre(5xPl%KxYvE8SIZ9-SeHvNe z`yp^Xa;1vsY~n2cOZ#hI3tzW4P83!13;W=1rog?)W9_vRvQ~pyy;gI(OjK4I1XsYTk5dCxKI(bJ43~ARQRa2?(eo{8|6zo$4KrVna11z6n03 zbz6~@_1lm}sQ~KFC8f7eA88zBWNL$2;+zB17r#nrPVfpBCVWzI-o8N9;lEUNl?^Y0 zS!63p-CNDnsKR3HG%07PGMGZaFJqq;-gUd}@^ zAv%W<{ZJlgc00Xy-Lr>!8LmvW8M^AGT8ncl7%y~HO&sgzxHSN}8ZMgPR-o(7$o&E8 zvAeX%FcI2~xGRoDuts_B|hU4L5n)4!7Yz41?b{*FLCJN8*Li$ z`#5>zeB=tli0Z(vgPSBQDwJ4+?f4Pyj&vD^%-q9YOH7|9?1^yd(pA7)^d#{%`ZWrcX znJkln?)MfFy9U=p-dqkjjsI!|U2YdN_~k@_?r#@rVy$SXEWQrW3MFZfHN!`4E@@sG zv4uPrb8|#}n*51YJTzTV_`C|sjj7OfKy8eYzh82K1_OhS2X{i=I{NmLilhE!plo7U zuc5P}bUU7j35bJ3s9%{jg0iw0IpPX`EM~1gIwPNrc55(aRl|i6O1?z6zL|v?!v`pPH>+E zf#-rrt5$doZXiy_!-3#ayk0UplUz_4*P8aK8b?zI z-Cnhuq-2&G9~*w?4rd7W-^2L;_f(~4xFV;WUZhypr-o2BRd}&P5sN~wAY_FmqZA2_i0ofJ0>92q&c-QYL3}JuCV|+ph9_n_ zd=93q$>tym%HWNeE~&5dn;BjHwZNa0A?waw_q*$-cO5B3#o-(t@|MQd)0KV=I*M-1 zH(I`Ot}D-vY9g*bWW7vwtgXb|EkE8IRW98n!oBksbHNL^M5}x1RW9-mKhqB2@QY& zzyja^@Bjn=A^-`13_t;(0?+{H01N;o01JQ(zyaU_@BsJ#0stX^2tW)V0gwX70OSA) z040D5Kn-{TpaIYV=m1Xv&j9oQ1^^>~3BU|s0k8s|1K0rU01f~rfD6D4-~sRg_yGI> z0e~Pt2p|j)0f+*`0O9}%fFwW)0DM+w$^c{ma)1{Ad4K{y5ugN62B-j30crqsfCfMl zpaswd=m2y9dH{WZ0l*Mo1TY4e089a90CRu^;3dEkUJL5#R)H z2DkuR0d4?yfCs=6;05po_yBwXet_2if50062=EpV00;yG0p0};Ko}q#5CMn; zL;<1!F@RV=93UQ$07wKR0g?eJ05BjGkOoKxWB@V&S%7Rn4j>nh2gnB$015#`fMP%i zpcGIBCN1~BBW2MW!KsR_VX(mHV905F93alKh!h|uFN%U}pU z?!yCSstV9BC{X`A{062^g^2yJ!7ml^;*SmUsSve4HVCFcRQ_qegZk%oMsQXdMEZ{n z7ikc6ioZwouZn*?83(pZhbXcADd>NkT!1Lm>m7*MGmTsK9yIkY|ba!blJIPX4j_ ze>V;M|7s_Of2zUZ8IV7h^YVOg;sfr`9KwQvlKD^PZ-xFTPc-MI_+#e?=eYAFz(t1y zCKQzLf2!%E|E?zgW96nD-@rHES;rPwC@8W2RI(}lU5S)vFN>N;E{*=jIVaD8kitYL z|2vuBk6rCTVJ?*bQ+fdS@BAj}{ZkEI&4LjBu^*Idpr3sG|7B_efj@SHm4Ppk3_QEp z0tW>p^q*})w|gj;{9_djHs#eOFcS*EOi28v3f}RdiUqj(#G+(pwXipHbJNmLqkhAHw?32@*Ir4}$u~%uXN^ z#v|ePN|Ev*B!3)_C1KRK4KN-i`2W9C5R!kFg7P7MK5$loDT3F485l+U|FbX!ek@I_ zkHg3NdvE;xVf1*>u3A$<0K6}tpl~z6>={P*V8sFm((kKym7o?97_m&SIHv^u!<9TZ zt^k7ea48SYDF8Oe`=1sZMrfG7KTH05g%1VA_^|GOUakMsV1YRbA-Gh3`V#n0HWXFC z-<@NDBMKoTR1crQ56!?dJ-&cPdH?m=DggHvLik~*3&CRZ?4*e&nWSLT5(o-dvT^Ke;@Q+GhMHfC`r7DOrOxZ7RRu!=ECFgx$gM+>&VB~6GFbBK+;GB*ACm6pE zh`%1d*95JZr@zf07 zaMvM$v3v}Zf-!6U4hMV!PQL42EpRZ^k8MxDMl}#dm|uS_tWyIk$?SiH4tCbnK?G0K zLg2s)H4qY*JL`uIo`B-wQxJBAB=i!VVMS^*iEN2Vwa= zqFgl6--G??^U(AZyio`AEa3aFzyOZx*@OcN)I&I6iu`{!zwz8e2Y;SyLHj+7x_V$3 z74Z*ktl*n^2ph~e_+OH%B>yii3@b+YztD5=%?F?w>B;{>kJWHsNJR_I??c490~l-AvwvrdbU@f( zQ5pUU0mFv}1IH*C_;i~D9xT=gp@ThS|94|}C-6B($^GxpZYM+x)|~I(p~rWDNf(42 zR#Wibao}Jl29I?CT{epSJ08vt!3L*y0w;op@8#m%5K>qUslO5)zCVM*x`Azr_*aMw zeAEpQhFz2Wca~{Cgc|&_8+eOG_W)bUqVoS5iNS|GK;OA)|BJJN4;|b8I_`aXfg(7~ zzZ%#3f$2Y1gdqGYWAhp?=*&yUO UEJxD6L%xF$Rs_jx2yi_9A1g!FQ~&?~ delta 15537 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 SM?v1;O+g#=YYH4()&C!Ihx4HT diff --git a/config/application.json b/config/application.json index 5825f7e..54b8747 100644 --- a/config/application.json +++ b/config/application.json @@ -23,5 +23,6 @@ "cron":"0 10 0 * * ? *", "delay":true } - ] + ], + "formvalidation": true } \ No newline at end of file diff --git a/pom.xml b/pom.xml index 7f27f41..de2d4ea 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.gcszhn autocard - 1.4.2 + 1.4.3 jar Auto Heathy Report for Zhejiang University diff --git a/src/main/java/org/gcszhn/autocard/AppConfig.java b/src/main/java/org/gcszhn/autocard/AppConfig.java index 9156b90..d0d2bdd 100644 --- a/src/main/java/org/gcszhn/autocard/AppConfig.java +++ b/src/main/java/org/gcszhn/autocard/AppConfig.java @@ -16,10 +16,12 @@ package org.gcszhn.autocard; import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; @@ -43,12 +45,21 @@ public class AppConfig implements EnvironmentAware { /**默认字符集 */ public static final Charset APP_CHARSET = StandardCharsets.UTF_8; + /**APP缓存文件 */ + private static final String APP_CACHE = "autocard_cache.json"; + /**应用缓存 */ + private JSONObject appCache; /**JSON配置文件 */ - private JSONObject jsonConfig; + private JSONObject appConfig; /**是否为测试模式 */ private @Getter boolean testMode = false; public AppConfig() { LogUtils.printMessage("Test mode is " + testMode, LogUtils.Level.DEBUG); + try (FileInputStream fis = new FileInputStream(APP_CACHE)) { + appCache = JSON.parseObject(new String(fis.readAllBytes(), APP_CHARSET)); + } catch (Exception e) { + appCache = new JSONObject(); + } } /** * SpringBoot 2.x无法在Configuration中使用@Value,因此需要获取springboot环境 @@ -56,7 +67,7 @@ public AppConfig() { @Override public void setEnvironment(Environment env) { loadJSONConfig(env.getProperty("app.autoCard.config")); - testMode = jsonConfig.getBooleanValue("testmode"); + testMode = appConfig.getBooleanValue("testmode"); // 通过系统环境变量添加单个打卡用户 @@ -71,7 +82,7 @@ public void setEnvironment(Environment env) { global_user.put("dingtalkurl", System.getenv("AUTOCARD_DINGTALK_URL")); global_user.put("dingtalksecret", System.getenv("AUTOCARD_DINGTALK_SECRET")); global_user.put("delay", System.getenv("AUTOCARD_DELAY") != null); - jsonConfig.getJSONArray("jobs").add(global_user); + appConfig.getJSONArray("jobs").add(global_user); } } @@ -91,11 +102,11 @@ public void loadJSONConfig(String configSource) { jsonString = configSource.substring(7); } if (jsonString != null) { - jsonConfig = JSONObject.parseObject(jsonString); + appConfig = JSONObject.parseObject(jsonString); LogUtils.printMessage("用户配置已加载"); } else { - jsonConfig = new JSONObject(); - jsonConfig.put("jobs", new JSONArray()); + appConfig = new JSONObject(); + appConfig.put("jobs", new JSONArray()); } } catch (Exception e) { @@ -111,7 +122,7 @@ public void loadJSONConfig(String configSource) { */ @Bean public MailService registerMailService(ConfigurableEnvironment env) { - JSONObject mailConfig = jsonConfig.getJSONObject("mail"); + JSONObject mailConfig = appConfig.getJSONObject("mail"); MailService mailService = new MailService(); if (mailConfig != null){ String nickname = mailConfig.getString("nickname"); @@ -133,7 +144,37 @@ public MailService registerMailService(ConfigurableEnvironment env) { * @return 用户任务 */ public JSONArray getUserJobs() { - JSONArray jsonArray = jsonConfig.getJSONArray("jobs"); + JSONArray jsonArray = appConfig.getJSONArray("jobs"); return jsonArray==null?new JSONArray():jsonArray; } + + public void addCacheItem(String key, Object value) { + appCache.put(key, value); + cache(); + } + + public T getCacheItem(String key, Class type) { + return appCache.getObject(key, type); + } + + public String getCacheItem(String key) { + return appCache.getObject(key, String.class); + } + + public void removeCacheItem(String key) { + appCache.remove(key); + cache(); + } + + public void cache() { + try { + JSON.writeJSONString(new FileOutputStream(APP_CACHE), APP_CHARSET, appCache); + } catch (Exception e) { + LogUtils.printMessage("保存缓存失败", LogUtils.Level.ERROR); + } + } + + public T getConfigItem(String key, Class type) { + return appConfig.getObject(key, type); + } } \ No newline at end of file diff --git a/src/main/java/org/gcszhn/autocard/service/AutoClockinJob.java b/src/main/java/org/gcszhn/autocard/service/AutoClockinJob.java index f9a8f3e..35c88d6 100644 --- a/src/main/java/org/gcszhn/autocard/service/AutoClockinJob.java +++ b/src/main/java/org/gcszhn/autocard/service/AutoClockinJob.java @@ -98,6 +98,7 @@ public static void execute( } } } catch (Exception e) { + e.printStackTrace(); LogUtils.printMessage(e.getMessage(), LogUtils.Level.ERROR); } } diff --git a/src/main/java/org/gcszhn/autocard/service/ClockinService.java b/src/main/java/org/gcszhn/autocard/service/ClockinService.java index d3fcd7d..b6e9396 100644 --- a/src/main/java/org/gcszhn/autocard/service/ClockinService.java +++ b/src/main/java/org/gcszhn/autocard/service/ClockinService.java @@ -26,8 +26,13 @@ import org.apache.http.NameValuePair; import org.apache.http.message.BasicNameValuePair; +import org.gcszhn.autocard.AppConfig; +import org.gcszhn.autocard.utils.DigestUtils; import org.gcszhn.autocard.utils.LogUtils; import org.gcszhn.autocard.utils.StatusCode; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Scope; @@ -41,6 +46,10 @@ @Scope("prototype") @Service public class ClockinService implements Closeable { + /**时间格式化 */ + private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyyMMdd"); + /**表达校验数据缓存关键字 */ + private static final String FORM_MD5_VALUE= "FORM_MD5_VALUE"; /**打卡信息网址 */ @Value("${app.autoCard.reportUrl}") /**打卡提交网址 */ @@ -50,8 +59,9 @@ public class ClockinService implements Closeable { /**浙大通行证客户端 */ @Autowired private ZJUClientService client; - /**时间格式化 */ - private SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); + /**应用配置实例 */ + @Autowired + private AppConfig appConfig; /** * 用于访问打卡页面 * @param username 用户名 @@ -60,10 +70,55 @@ public class ClockinService implements Closeable { */ public String getPage(String username, String password) { if(client.login(username, password)) { - return client.doGetText(reportUrl); + String page1 = client.doGetText(reportUrl); + Boolean formvalidation = appConfig.getConfigItem("formvalidation", Boolean.class); + if (formvalidation!=null && !formvalidation) { + return page1; + } + String page2 = client.doGetText(reportUrl); + boolean page1Flag = formValidation(page1); + boolean page2Flag = formValidation(page2); + if ( page1Flag == (page1!=null) && page2Flag == (page2!=null)) { + LogUtils.printMessage("表单校验通过", LogUtils.Level.INFO); + return page1; + } else if (page1 != null && page2 != null && page1Flag != page2Flag) { + // 意味着两次获取的页面表单是变化的,无法作为校验依据 + LogUtils.printMessage("表单校验功能失效,已忽略校验,请联系作者", LogUtils.Level.ERROR); + return page1; + } else { + LogUtils.printMessage("表单校验失败,请检查健康打卡页面是否更新或等待一会再次尝试,若更新请删除缓存文件并重启打卡程序", LogUtils.Level.ERROR); + } } return null; } + /** + * 表单数据的MD5校验,浙大健康打卡时常更新,但后端验证不够及时,因此进行前端验证 + * @param html 表单的html页面 + * @return true为验证通过 + */ + public boolean formValidation(String html) { + try { + if (html != null) { + Document document = Jsoup.parse(html); + Element form = document.getElementsByClass("form-detail2").last(); + if (form != null) { + String digest = DigestUtils.digest(form.html(), "MD5"); + if (appConfig.getCacheItem(FORM_MD5_VALUE) == null) { + appConfig.addCacheItem(FORM_MD5_VALUE, digest); + } + if (appConfig.getCacheItem(FORM_MD5_VALUE).equals(digest)) { + return true; + } + } else { + LogUtils.printMessage("未捕获表单信息,捕获信息如下", LogUtils.Level.ERROR); + System.out.println(html); + } + } + } catch (Exception e) { + LogUtils.printMessage(e.getMessage(), e, LogUtils.Level.ERROR); + } + return false; + } /** * 用于提取已有提交信息 * @param username 用户名 @@ -107,7 +162,7 @@ public ArrayList getOldInfo(String username, String password) { infoJsonObject1.putAll(oldInfoJson); infoJsonObject1.forEach((String name, Object value)->{ switch (name) { - case "date" : value=sdf.format(new Date());break; + case "date" : value=SDF.format(new Date());break; case "bztcyy": value="";break; //地区变更需要手动打卡一次,过滤上一次的地区变更原因 } // fix bug for "是否从下列地区返回浙江错误" @@ -135,7 +190,7 @@ public StatusCode submit(String username, String password) { ArrayList info = getOldInfo(username, password); if (info==null) { LogUtils.printMessage("打卡信息获取失败", LogUtils.Level.ERROR); - statusCode.setMessage(username+", 打卡信息获取失败"); + statusCode.setMessage(username+"的打卡信息获取失败,可能是打卡更新了或网络不稳定,请查看后台打卡日志输出"); statusCode.setStatus(-1); return statusCode; } @@ -163,6 +218,7 @@ public StatusCode submit(String username, String password) { public void close() { try { client.close(); + } catch (Exception e) { LogUtils.printMessage(null, e, LogUtils.Level.ERROR); } diff --git a/src/test/java/org/gcszhn/autocard/AppTest.java b/src/test/java/org/gcszhn/autocard/AppTest.java index f85b84a..e9b39a2 100644 --- a/src/test/java/org/gcszhn/autocard/AppTest.java +++ b/src/test/java/org/gcszhn/autocard/AppTest.java @@ -27,6 +27,6 @@ @RunWith(SpringRunner.class) @SpringBootTest public abstract class AppTest { - protected static final String trueZjuPassPortUser = "***"; - protected static final String trueZjuPassPortPass = "***"; + protected static final String trueZjuPassPortUser = "****"; + protected static final String trueZjuPassPortPass = "****"; } diff --git a/src/test/java/org/gcszhn/autocard/AutoClockinTest.java b/src/test/java/org/gcszhn/autocard/AutoClockinTest.java index 4067260..db6c22a 100644 --- a/src/test/java/org/gcszhn/autocard/AutoClockinTest.java +++ b/src/test/java/org/gcszhn/autocard/AutoClockinTest.java @@ -35,7 +35,15 @@ public void afterTest() { } @Test public void getPageTest() { - Assert.assertNotNull(autoCard.getPage(trueZjuPassPortUser, trueZjuPassPortPass)); + try { + for (int i = 0; i < 2; i++) { + String page = autoCard.getPage(trueZjuPassPortUser, trueZjuPassPortPass); + Assert.assertNotNull(page); + Assert.assertTrue(autoCard.formValidation(page)); + } + } catch (Exception e) { + e.printStackTrace(); + } } @Test public void getOldInfoTest() {