Skip to content
This repository was archived by the owner on Dec 4, 2023. It is now read-only.

Commit 7cfdc69

Browse files
committed
support form validation
1 parent 2dd7ab3 commit 7cfdc69

File tree

9 files changed

+127
-18
lines changed

9 files changed

+127
-18
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,8 @@ Fork本项目,在fork后的仓库里”Setttings > Secrets > Actions > New rep
209209
若打卡题目被更新或者你的任何信息情况有变化(如返校),请先手动打卡一次。本项目仅供学习参考。使用时请确保信息的正确性。滥用造成的后果请自行承担。
210210

211211
## 更新记录
212+
### v1.4.3
213+
支持对健康打卡进行表单数据校验,检测健康打卡表单是否更新,当表单校验不通过,意味着健康打卡已经更新,请清除数据缓存`autocard_cache.json`文件并重启打卡程序。例如2022年4月6日浙江大学对表单有所更新。
212214

213215
### v1.4.2
214216
修复了多用户立即打卡时,后续用户无效的问题。支持了通过系统环境变量来配置打卡用户。

action/autocard.jar

1.85 KB
Binary file not shown.

config/application.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@
2323
"cron":"0 10 0 * * ? *",
2424
"delay":true
2525
}
26-
]
26+
],
27+
"formvalidation": true
2728
}

pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
<groupId>org.gcszhn</groupId>
66
<artifactId>autocard</artifactId>
7-
<version>1.4.2</version>
7+
<version>1.4.3</version>
88
<packaging>jar</packaging>
99

1010
<name>Auto Heathy Report for Zhejiang University</name>

src/main/java/org/gcszhn/autocard/AppConfig.java

+49-8
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616
package org.gcszhn.autocard;
1717

1818
import java.io.FileInputStream;
19+
import java.io.FileOutputStream;
1920
import java.io.IOException;
2021
import java.nio.charset.Charset;
2122
import java.nio.charset.StandardCharsets;
2223

24+
import com.alibaba.fastjson.JSON;
2325
import com.alibaba.fastjson.JSONArray;
2426
import com.alibaba.fastjson.JSONObject;
2527

@@ -43,20 +45,29 @@
4345
public class AppConfig implements EnvironmentAware {
4446
/**默认字符集 */
4547
public static final Charset APP_CHARSET = StandardCharsets.UTF_8;
48+
/**APP缓存文件 */
49+
private static final String APP_CACHE = "autocard_cache.json";
50+
/**应用缓存 */
51+
private JSONObject appCache;
4652
/**JSON配置文件 */
47-
private JSONObject jsonConfig;
53+
private JSONObject appConfig;
4854
/**是否为测试模式 */
4955
private @Getter boolean testMode = false;
5056
public AppConfig() {
5157
LogUtils.printMessage("Test mode is " + testMode, LogUtils.Level.DEBUG);
58+
try (FileInputStream fis = new FileInputStream(APP_CACHE)) {
59+
appCache = JSON.parseObject(new String(fis.readAllBytes(), APP_CHARSET));
60+
} catch (Exception e) {
61+
appCache = new JSONObject();
62+
}
5263
}
5364
/**
5465
* SpringBoot 2.x无法在Configuration中使用@Value,因此需要获取springboot环境
5566
*/
5667
@Override
5768
public void setEnvironment(Environment env) {
5869
loadJSONConfig(env.getProperty("app.autoCard.config"));
59-
testMode = jsonConfig.getBooleanValue("testmode");
70+
testMode = appConfig.getBooleanValue("testmode");
6071

6172
// 通过系统环境变量添加单个打卡用户
6273

@@ -71,7 +82,7 @@ public void setEnvironment(Environment env) {
7182
global_user.put("dingtalkurl", System.getenv("AUTOCARD_DINGTALK_URL"));
7283
global_user.put("dingtalksecret", System.getenv("AUTOCARD_DINGTALK_SECRET"));
7384
global_user.put("delay", System.getenv("AUTOCARD_DELAY") != null);
74-
jsonConfig.getJSONArray("jobs").add(global_user);
85+
appConfig.getJSONArray("jobs").add(global_user);
7586
}
7687

7788
}
@@ -91,11 +102,11 @@ public void loadJSONConfig(String configSource) {
91102
jsonString = configSource.substring(7);
92103
}
93104
if (jsonString != null) {
94-
jsonConfig = JSONObject.parseObject(jsonString);
105+
appConfig = JSONObject.parseObject(jsonString);
95106
LogUtils.printMessage("用户配置已加载");
96107
} else {
97-
jsonConfig = new JSONObject();
98-
jsonConfig.put("jobs", new JSONArray());
108+
appConfig = new JSONObject();
109+
appConfig.put("jobs", new JSONArray());
99110
}
100111
}
101112
catch (Exception e) {
@@ -111,7 +122,7 @@ public void loadJSONConfig(String configSource) {
111122
*/
112123
@Bean
113124
public MailService registerMailService(ConfigurableEnvironment env) {
114-
JSONObject mailConfig = jsonConfig.getJSONObject("mail");
125+
JSONObject mailConfig = appConfig.getJSONObject("mail");
115126
MailService mailService = new MailService();
116127
if (mailConfig != null){
117128
String nickname = mailConfig.getString("nickname");
@@ -133,7 +144,37 @@ public MailService registerMailService(ConfigurableEnvironment env) {
133144
* @return 用户任务
134145
*/
135146
public JSONArray getUserJobs() {
136-
JSONArray jsonArray = jsonConfig.getJSONArray("jobs");
147+
JSONArray jsonArray = appConfig.getJSONArray("jobs");
137148
return jsonArray==null?new JSONArray():jsonArray;
138149
}
150+
151+
public void addCacheItem(String key, Object value) {
152+
appCache.put(key, value);
153+
cache();
154+
}
155+
156+
public <T> T getCacheItem(String key, Class<T> type) {
157+
return appCache.getObject(key, type);
158+
}
159+
160+
public String getCacheItem(String key) {
161+
return appCache.getObject(key, String.class);
162+
}
163+
164+
public void removeCacheItem(String key) {
165+
appCache.remove(key);
166+
cache();
167+
}
168+
169+
public void cache() {
170+
try {
171+
JSON.writeJSONString(new FileOutputStream(APP_CACHE), APP_CHARSET, appCache);
172+
} catch (Exception e) {
173+
LogUtils.printMessage("保存缓存失败", LogUtils.Level.ERROR);
174+
}
175+
}
176+
177+
public <T> T getConfigItem(String key, Class<T> type) {
178+
return appConfig.getObject(key, type);
179+
}
139180
}

src/main/java/org/gcszhn/autocard/service/AutoClockinJob.java

+1
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ public static void execute(
9898
}
9999
}
100100
} catch (Exception e) {
101+
e.printStackTrace();
101102
LogUtils.printMessage(e.getMessage(), LogUtils.Level.ERROR);
102103
}
103104
}

src/main/java/org/gcszhn/autocard/service/ClockinService.java

+61-5
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,13 @@
2626

2727
import org.apache.http.NameValuePair;
2828
import org.apache.http.message.BasicNameValuePair;
29+
import org.gcszhn.autocard.AppConfig;
30+
import org.gcszhn.autocard.utils.DigestUtils;
2931
import org.gcszhn.autocard.utils.LogUtils;
3032
import org.gcszhn.autocard.utils.StatusCode;
33+
import org.jsoup.Jsoup;
34+
import org.jsoup.nodes.Document;
35+
import org.jsoup.nodes.Element;
3136
import org.springframework.beans.factory.annotation.Autowired;
3237
import org.springframework.beans.factory.annotation.Value;
3338
import org.springframework.context.annotation.Scope;
@@ -41,6 +46,10 @@
4146
@Scope("prototype")
4247
@Service
4348
public class ClockinService implements Closeable {
49+
/**时间格式化 */
50+
private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyyMMdd");
51+
/**表达校验数据缓存关键字 */
52+
private static final String FORM_MD5_VALUE= "FORM_MD5_VALUE";
4453
/**打卡信息网址 */
4554
@Value("${app.autoCard.reportUrl}")
4655
/**打卡提交网址 */
@@ -50,8 +59,9 @@ public class ClockinService implements Closeable {
5059
/**浙大通行证客户端 */
5160
@Autowired
5261
private ZJUClientService client;
53-
/**时间格式化 */
54-
private SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
62+
/**应用配置实例 */
63+
@Autowired
64+
private AppConfig appConfig;
5565
/**
5666
* 用于访问打卡页面
5767
* @param username 用户名
@@ -60,10 +70,55 @@ public class ClockinService implements Closeable {
6070
*/
6171
public String getPage(String username, String password) {
6272
if(client.login(username, password)) {
63-
return client.doGetText(reportUrl);
73+
String page1 = client.doGetText(reportUrl);
74+
Boolean formvalidation = appConfig.getConfigItem("formvalidation", Boolean.class);
75+
if (formvalidation!=null && !formvalidation) {
76+
return page1;
77+
}
78+
String page2 = client.doGetText(reportUrl);
79+
boolean page1Flag = formValidation(page1);
80+
boolean page2Flag = formValidation(page2);
81+
if ( page1Flag == (page1!=null) && page2Flag == (page2!=null)) {
82+
LogUtils.printMessage("表单校验通过", LogUtils.Level.INFO);
83+
return page1;
84+
} else if (page1 != null && page2 != null && page1Flag != page2Flag) {
85+
// 意味着两次获取的页面表单是变化的,无法作为校验依据
86+
LogUtils.printMessage("表单校验功能失效,已忽略校验,请联系作者", LogUtils.Level.ERROR);
87+
return page1;
88+
} else {
89+
LogUtils.printMessage("表单校验失败,请检查健康打卡页面是否更新或等待一会再次尝试,若更新请删除缓存文件并重启打卡程序", LogUtils.Level.ERROR);
90+
}
6491
}
6592
return null;
6693
}
94+
/**
95+
* 表单数据的MD5校验,浙大健康打卡时常更新,但后端验证不够及时,因此进行前端验证
96+
* @param html 表单的html页面
97+
* @return true为验证通过
98+
*/
99+
public boolean formValidation(String html) {
100+
try {
101+
if (html != null) {
102+
Document document = Jsoup.parse(html);
103+
Element form = document.getElementsByClass("form-detail2").last();
104+
if (form != null) {
105+
String digest = DigestUtils.digest(form.html(), "MD5");
106+
if (appConfig.getCacheItem(FORM_MD5_VALUE) == null) {
107+
appConfig.addCacheItem(FORM_MD5_VALUE, digest);
108+
}
109+
if (appConfig.getCacheItem(FORM_MD5_VALUE).equals(digest)) {
110+
return true;
111+
}
112+
} else {
113+
LogUtils.printMessage("未捕获表单信息,捕获信息如下", LogUtils.Level.ERROR);
114+
System.out.println(html);
115+
}
116+
}
117+
} catch (Exception e) {
118+
LogUtils.printMessage(e.getMessage(), e, LogUtils.Level.ERROR);
119+
}
120+
return false;
121+
}
67122
/**
68123
* 用于提取已有提交信息
69124
* @param username 用户名
@@ -107,7 +162,7 @@ public ArrayList<NameValuePair> getOldInfo(String username, String password) {
107162
infoJsonObject1.putAll(oldInfoJson);
108163
infoJsonObject1.forEach((String name, Object value)->{
109164
switch (name) {
110-
case "date" : value=sdf.format(new Date());break;
165+
case "date" : value=SDF.format(new Date());break;
111166
case "bztcyy": value="";break; //地区变更需要手动打卡一次,过滤上一次的地区变更原因
112167
}
113168
// fix bug for "是否从下列地区返回浙江错误"
@@ -135,7 +190,7 @@ public StatusCode submit(String username, String password) {
135190
ArrayList<NameValuePair> info = getOldInfo(username, password);
136191
if (info==null) {
137192
LogUtils.printMessage("打卡信息获取失败", LogUtils.Level.ERROR);
138-
statusCode.setMessage(username+", 打卡信息获取失败");
193+
statusCode.setMessage(username+"的打卡信息获取失败,可能是打卡更新了或网络不稳定,请查看后台打卡日志输出");
139194
statusCode.setStatus(-1);
140195
return statusCode;
141196
}
@@ -163,6 +218,7 @@ public StatusCode submit(String username, String password) {
163218
public void close() {
164219
try {
165220
client.close();
221+
166222
} catch (Exception e) {
167223
LogUtils.printMessage(null, e, LogUtils.Level.ERROR);
168224
}

src/test/java/org/gcszhn/autocard/AppTest.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,6 @@
2727
@RunWith(SpringRunner.class)
2828
@SpringBootTest
2929
public abstract class AppTest {
30-
protected static final String trueZjuPassPortUser = "***";
31-
protected static final String trueZjuPassPortPass = "***";
30+
protected static final String trueZjuPassPortUser = "****";
31+
protected static final String trueZjuPassPortPass = "****";
3232
}

src/test/java/org/gcszhn/autocard/AutoClockinTest.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,15 @@ public void afterTest() {
3535
}
3636
@Test
3737
public void getPageTest() {
38-
Assert.assertNotNull(autoCard.getPage(trueZjuPassPortUser, trueZjuPassPortPass));
38+
try {
39+
for (int i = 0; i < 2; i++) {
40+
String page = autoCard.getPage(trueZjuPassPortUser, trueZjuPassPortPass);
41+
Assert.assertNotNull(page);
42+
Assert.assertTrue(autoCard.formValidation(page));
43+
}
44+
} catch (Exception e) {
45+
e.printStackTrace();
46+
}
3947
}
4048
@Test
4149
public void getOldInfoTest() {

0 commit comments

Comments
 (0)