本科生:新增缓考申请列表和学分修读情况的请求以及解析;对部分方法重载方便使用;添加部分学校相关信息参数;保留恢复旧版本科生登录方式

old-package
lensfrex 2 years ago
parent 0a32c1030c
commit 25c78714c5
Signed by: lensfrex
GPG Key ID: 947ADABD8533C476
  1. 164
      mywust-core/src/main/java/cn/linghang/mywust/core/api/UndergradUrls.java
  2. 2
      mywust-core/src/main/java/cn/linghang/mywust/core/exception/ApiException.java
  3. 10
      mywust-core/src/main/java/cn/linghang/mywust/core/exception/ParseException.java
  4. 2
      mywust-core/src/main/java/cn/linghang/mywust/core/parser/graduate/GraduateStudentInfoPageParser.java
  5. 7
      mywust-core/src/main/java/cn/linghang/mywust/core/parser/undergraduate/UndergradCourseTableParser.java
  6. 31
      mywust-core/src/main/java/cn/linghang/mywust/core/parser/undergraduate/UndergradCreditStatusIndexParser.java
  7. 20
      mywust-core/src/main/java/cn/linghang/mywust/core/parser/undergraduate/UndergradCreditStatusParser.java
  8. 67
      mywust-core/src/main/java/cn/linghang/mywust/core/parser/undergraduate/UndergradExamDelayParser.java
  9. 26
      mywust-core/src/main/java/cn/linghang/mywust/core/parser/undergraduate/UndergradScoreParser.java
  10. 4
      mywust-core/src/main/java/cn/linghang/mywust/core/request/RequestFactory.java
  11. 2
      mywust-core/src/main/java/cn/linghang/mywust/core/request/library/response/Map.java
  12. 60
      mywust-core/src/main/java/cn/linghang/mywust/core/request/undergrade/BkjxRequestFactory.java
  13. 5
      mywust-core/src/main/java/cn/linghang/mywust/core/service/auth/LibraryLogin.java
  14. 26
      mywust-core/src/main/java/cn/linghang/mywust/core/service/auth/UndergraduateLogin.java
  15. 4
      mywust-core/src/main/java/cn/linghang/mywust/core/service/graduate/GraduateApiServiceBase.java
  16. 6
      mywust-core/src/main/java/cn/linghang/mywust/core/service/undergraduate/UndergradApiServiceBase.java
  17. 57
      mywust-core/src/main/java/cn/linghang/mywust/core/service/undergraduate/UndergradCreditStatusApiService.java
  18. 85
      mywust-core/src/main/java/cn/linghang/mywust/core/service/undergraduate/UndergradExamDelayApiService.java
  19. 4
      mywust-core/src/main/java/cn/linghang/mywust/core/service/undergraduate/UndergradScoreApiService.java
  20. 4
      mywust-core/src/main/java/cn/linghang/mywust/core/service/undergraduate/UndergradStudentInfoApiService.java
  21. 4
      mywust-core/src/main/java/cn/linghang/mywust/core/service/undergraduate/UndergradTrainingPlanApiService.java
  22. 2
      mywust-core/src/main/java/cn/linghang/mywust/core/util/BkjxUtil.java
  23. 33
      mywust-core/src/main/java/cn/linghang/mywust/core/util/JsoupUtil.java
  24. 32
      mywust-model/src/main/java/cn/linghang/mywust/model/global/Building.java
  25. 29
      mywust-model/src/main/java/cn/linghang/mywust/model/global/Campus.java
  26. 29
      mywust-model/src/main/java/cn/linghang/mywust/model/global/College.java
  27. 26
      mywust-model/src/main/java/cn/linghang/mywust/model/undergrad/ExamDelayApplication.java
  28. 14
      mywust-test/pom.xml
  29. 49
      mywust-test/src/test/java/CourseTableTest.java
  30. 46
      mywust-test/src/test/java/ExamInfoTest.java
  31. 45
      mywust-test/src/test/java/JwcLoginTest.java
  32. 39
      mywust-test/src/test/java/LibraryLoginTest.java
  33. 47
      mywust-test/src/test/java/ParseTest.java
  34. 38
      mywust-test/src/test/java/SchemeTest.java
  35. 39
      mywust-test/src/test/java/StudentInfoPageTest.java
  36. 2
      mywust-test/src/test/java/UnderGraduateTest.java
  37. 58
      mywust-test/src/test/java/WlsyLoginTest.java
  38. 10
      mywust-util/src/main/java/cn/linghang/mywust/util/PasswordEncoder.java
  39. 27
      mywust-util/src/main/java/cn/linghang/mywust/util/StringUtil.java
  40. 3
      pom.xml

@ -1,28 +1,188 @@
package cn.linghang.mywust.core.api; package cn.linghang.mywust.core.api;
import cn.linghang.mywust.model.global.Building;
import cn.linghang.mywust.model.global.Campus;
import cn.linghang.mywust.model.global.College;
import lombok.Getter; import lombok.Getter;
import java.util.*;
/** /**
* <p>Bkjx本科教学系统对应的API常量列表拼音咱就别吐槽了吧...</p> * <p>Bkjx本科教学系统对应的API常量列表拼音咱就别吐槽了吧...</p>
* <p>其实是本科生用的教务处系统</p> * <p>其实是本科生用的教务处系统</p>
*/ */
@Getter @Getter
public class UndergradUrls { public class UndergradUrls {
/**
* 登录cookie获取url
*/
public static final String BKJX_SESSION_COOKIE_API = "http://bkjx.wust.edu.cn/jsxsd/sso.jsp?ticket=%s"; public static final String BKJX_SESSION_COOKIE_API = "http://bkjx.wust.edu.cn/jsxsd/sso.jsp?ticket=%s";
/**
* cookie测试url空白页
*/
public static final String BKJX_TEST_API = "http://bkjx.wust.edu.cn/jsxsd/framework/blankPage.jsp"; public static final String BKJX_TEST_API = "http://bkjx.wust.edu.cn/jsxsd/framework/blankPage.jsp";
/**
* 学生信息url
*/
public static final String BKJX_STUDENT_INFO_API = "http://bkjx.wust.edu.cn/jsxsd/grxx/xsxx"; public static final String BKJX_STUDENT_INFO_API = "http://bkjx.wust.edu.cn/jsxsd/grxx/xsxx";
public static final String BKJX_EXAM_INFO_API = "http://bkjx.wust.edu.cn/jsxsd/kscj/cjcx_list"; /**
* 成绩url
*/
public static final String BKJX_SCORE_API = "http://bkjx.wust.edu.cn/jsxsd/kscj/cjcx_list";
public static final String BKJX_SCHEME_API = "http://bkjx.wust.edu.cn/jsxsd/pyfa/topyfamx"; /**
* 培养计划url
*/
public static final String BKJX_TRAINING_PLAN_API = "http://bkjx.wust.edu.cn/jsxsd/pyfa/topyfamx";
/**
* 课表url
*/
public static final String BKJX_COURSE_TABLE_API = "http://bkjx.wust.edu.cn/jsxsd/xskb/xskb_list.do"; public static final String BKJX_COURSE_TABLE_API = "http://bkjx.wust.edu.cn/jsxsd/xskb/xskb_list.do";
/**
* 学分修读计划首页url
*/
public static final String BKJX_CREDIT_STATUS_INDEX_API = "http://bkjx.wust.edu.cn/jsxsd/xxwcqk/xxwcqk_idxOnzh.do";
/**
* 学分修读计划url
*/
public static final String BKJX_CREDIT_STATUS_API = "http://bkjx.wust.edu.cn/jsxsd/xxwcqk/xxwcqkOnzh.do";
/**
* 考试活动列表url
*/
public static final String BKJX_EXAM_ACTIVITY_LIST_API = "http://bkjx.wust.edu.cn/jsxsd/kscj/hksq_query_ajax?&xnxq01id=%s";
/**
* 缓考申请url
*/
public static final String BKJX_EXAM_DELAY_APPLICATION_LIST_API = "http://bkjx.wust.edu.cn/jsxsd/kscj/hksq_list";
public static class Legacy { public static class Legacy {
public static final String BKJX_INDEX = "http://bkjx.wust.edu.cn"; public static final String BKJX_INDEX = "http://bkjx.wust.edu.cn";
public static final String BKJX_DATA_STRING_API = "http://bkjx.wust.edu.cn/Logon.do?method=logon&flag=sess"; public static final String BKJX_DATA_STRING_API = "http://bkjx.wust.edu.cn/Logon.do?method=logon&flag=sess";
public static final String BKJX_SESSION_COOKIE_API = "http://bkjx.wust.edu.cn/Logon.do?method=logon"; public static final String BKJX_SESSION_COOKIE_API = "http://bkjx.wust.edu.cn/Logon.do?method=logon";
} }
/**
* 一些需要用到的from参数一般来说是写死固定的
*/
// 别慌,大部分都是脚本自动生成的代码,肯定不会是手写的。
public static class ConstantParams {
/**
* 默认节次模式
*/
public static final String DEFAULT_TIME_MODEL = "9486203B90F3E3CBE0532914A8C03BE2";
private static final List<College> collegesTmp = new ArrayList<>(27);
public static final List<College> COLLEGES = Collections.unmodifiableList(collegesTmp);
static {
collegesTmp.add(new College("00001", "[01]资源与环境工程学院"));
collegesTmp.add(new College("00002", "[02]材料与冶金学院"));
collegesTmp.add(new College("00003", "[03]机械自动化学院"));
collegesTmp.add(new College("00004", "[04]信息科学与工程学院(人工智能学院)"));
collegesTmp.add(new College("00005", "[05]管理学院(恒大管理学院)"));
collegesTmp.add(new College("00006", "[06]法学与经济学院"));
collegesTmp.add(new College("00007", "[07]理学院"));
collegesTmp.add(new College("00008", "[08]城市建设学院"));
collegesTmp.add(new College("00009", "[09]医学院"));
collegesTmp.add(new College("00012", "[12]电子技术学院"));
collegesTmp.add(new College("00013", "[13]计算机科学与技术学院"));
collegesTmp.add(new College("00014", "[14]外国语学院"));
collegesTmp.add(new College("00015", "[15]体育学院(恒大足球学院)"));
collegesTmp.add(new College("0D5444D56D8A46EDB75633181B2042A7", "[16]图书馆"));
collegesTmp.add(new College("26057DF4B9354C6EA85860F1357FE8EB", "[17]工程训练中心"));
collegesTmp.add(new College("00018", "[18]生命科学与健康学院"));
collegesTmp.add(new College("00019", "[19]艺术与设计学院"));
collegesTmp.add(new College("00023", "[21]国际学院"));
collegesTmp.add(new College("00024", "[22]化学与化工学院"));
collegesTmp.add(new College("00025", "[23]汽车与交通工程学院"));
collegesTmp.add(new College("00026", "[24]临床学院"));
collegesTmp.add(new College("0W6z90XTY6", "[25]学生工作处"));
collegesTmp.add(new College("jkwHhwCHLl", "[51]马克思主义学院"));
collegesTmp.add(new College("0C4B597EE18B418B9AA5C52FDAB3EC73", "[52]香涛学院"));
collegesTmp.add(new College("379A57BA53DD4F25BA7582CE3180C077", "[54]公共卫生学院"));
collegesTmp.add(new College("9AA8C9B34EE74DEC8E02F4554B1B5125", "[60]研究生院"));
collegesTmp.add(new College("86AC446406A94EF09DE1F8B7C17F994A", "[98]全校"));
}
private static final List<Campus> campusTmp = new ArrayList<>(3);
public static final List<Campus> CAMPUS = Collections.unmodifiableList(campusTmp);
static {
campusTmp.add(new Campus("00001", "青山校区"));
// tmp.add(new Campus("00002", "洪山校区");
// tmp.add(new Campus("00003", "医学院");
// tmp.add(new Campus("00004", "城市学院");
// tmp.add(new Campus("00005", "东湖教学区");
campusTmp.add(new Campus("00006", "黄家湖校区"));
campusTmp.add(new Campus("00007", "附属医院"));
// tmp.add(new Campus("00008", "武钢医院");
}
/**
* 教室目前只做了黄家湖青山和附属医学院的数据其他校区在制作时相关数据都是空的
* 需要更多信息请请求相关接口获取
*/
private static final Map<Campus, List<Building>> buildingTmp = new HashMap<>(3);
public static final Map<Campus, List<Building>> BUILDINGS = Collections.unmodifiableMap(buildingTmp);
static {
Campus qingshan = CAMPUS.get(0);
List<Building> qingshanBuildings = new ArrayList<>(10);
qingshanBuildings.add(new Building("FE88C3D75F9C4C8CA50B82ACDF83530B", "科大雅苑C栋", qingshan));
qingshanBuildings.add(new Building("3B76302812FD4262B8BD703814D0D8C3", "科大雅苑A栋", qingshan));
qingshanBuildings.add(new Building("90C06D274E254A2CADC88DF1CEBD8396", "教十楼", qingshan));
qingshanBuildings.add(new Building("00001", "主楼", qingshan));
qingshanBuildings.add(new Building("00002", "教一楼", qingshan));
qingshanBuildings.add(new Building("00003", "教二楼", qingshan));
qingshanBuildings.add(new Building("00004", "教三楼", qingshan));
qingshanBuildings.add(new Building("00005", "教四楼", qingshan));
qingshanBuildings.add(new Building("00006", "教五楼", qingshan));
qingshanBuildings.add(new Building("00007", "教六楼", qingshan));
qingshanBuildings.add(new Building("00008", "图书馆", qingshan));
qingshanBuildings.add(new Building("00009", "本部东院体育场", qingshan));
qingshanBuildings.add(new Building("00010", "机械厂", qingshan));
qingshanBuildings.add(new Building("00024", "本部西院体育场", qingshan));
qingshanBuildings.add(new Building("00025", "本部北院体育场", qingshan));
qingshanBuildings.add(new Building("00029", "教七楼", qingshan));
qingshanBuildings.add(new Building("00031", "本部教二楼化学实验区", qingshan));
qingshanBuildings.add(new Building("00032", "学院实验室", qingshan));
qingshanBuildings.add(new Building("00051", "校本部停用教室", qingshan));
buildingTmp.put(qingshan, qingshanBuildings);
Campus huangjiahu = CAMPUS.get(1);
List<Building> huangjiahuBuildings = new ArrayList<>(10);
huangjiahuBuildings.add(new Building("D1521DE0E4CF4641AB4C49CA39EF5E47", "教八楼(管理学院)", huangjiahu));
huangjiahuBuildings.add(new Building("FBD19C52EC3B4A93A6268B815EFA9A5A", "教九楼(汽车学院)", huangjiahu));
huangjiahuBuildings.add(new Building("00034", "恒大楼一区", huangjiahu));
huangjiahuBuildings.add(new Building("00035", "恒大楼二区", huangjiahu));
huangjiahuBuildings.add(new Building("00036", "恒大楼三区", huangjiahu));
huangjiahuBuildings.add(new Building("00037", "黄家湖校区体育场", huangjiahu));
huangjiahuBuildings.add(new Building("00041", "黄家湖校区体育馆", huangjiahu));
huangjiahuBuildings.add(new Building("00042", "教二楼(理学院)", huangjiahu));
huangjiahuBuildings.add(new Building("00043", "教三楼(计算机学院)", huangjiahu));
huangjiahuBuildings.add(new Building("00045", "教四楼二区(外国语学院)", huangjiahu));
huangjiahuBuildings.add(new Building("00046", "教四楼一区(文法学院)", huangjiahu));
huangjiahuBuildings.add(new Building("00048", "教五楼一区", huangjiahu));
huangjiahuBuildings.add(new Building("00049", "教五楼二区", huangjiahu));
huangjiahuBuildings.add(new Building("00050", "教六楼(医学院)", huangjiahu));
huangjiahuBuildings.add(new Building("00052", "黄家湖校区停用教室", huangjiahu));
huangjiahuBuildings.add(new Building("D29468E2AD8241769F47D82913C537EA", "黄家湖校区工程训练中心", huangjiahu));
huangjiahuBuildings.add(new Building("F9A375E92D814DD3BC2EF30E33C4907B", "教七楼(艺术学院)", huangjiahu));
huangjiahuBuildings.add(new Building("DFC73608380C4079A44D4215BD81EB88", "教十一楼", huangjiahu));
buildingTmp.put(huangjiahu, huangjiahuBuildings);
Campus medicineSchool = CAMPUS.get(2);
List<Building> medicineSchoolBuildings = new ArrayList<>(1);
medicineSchoolBuildings.add(new Building("00047", "附属医院教学楼", medicineSchool));
buildingTmp.put(medicineSchool, medicineSchoolBuildings);
}
}
} }

@ -34,7 +34,7 @@ public class ApiException extends BasicException {
@Override @Override
public String toString() { public String toString() {
return "接口调用异常: " + code; return "Mywust接口调用异常: " + code + ";" + getMessage();
} }
public enum Code { public enum Code {

@ -1,5 +1,7 @@
package cn.linghang.mywust.core.exception; package cn.linghang.mywust.core.exception;
import java.util.StringJoiner;
public class ParseException extends BasicException { public class ParseException extends BasicException {
private final String rawData; private final String rawData;
@ -21,4 +23,12 @@ public class ParseException extends BasicException {
public String getRawData() { public String getRawData() {
return rawData; return rawData;
} }
@Override
public String toString() {
return new StringJoiner(", ", ParseException.class.getSimpleName() + "[", "]")
.add("message='" + getMessage() + "'")
.add("rawData='" + rawData + "'")
.toString();
}
} }

@ -36,7 +36,7 @@ public class GraduateStudentInfoPageParser implements Parser<StudentInfo> {
student.setName(JsoupUtil.getAttr(nameElement, "value")); student.setName(JsoupUtil.getAttr(nameElement, "value"));
Element birthdayElement = table.getElementById("lblcsrq"); Element birthdayElement = table.getElementById("lblcsrq");
student.setBirthday(JsoupUtil.getText(birthdayElement)); student.setBirthday(JsoupUtil.getOwnText(birthdayElement));
Element collegeElement = table.getElementById("droyx"); Element collegeElement = table.getElementById("droyx");
student.setCollege(JsoupUtil.getSelectContent(collegeElement)); student.setCollege(JsoupUtil.getSelectContent(collegeElement));

@ -2,6 +2,7 @@ package cn.linghang.mywust.core.parser.undergraduate;
import cn.linghang.mywust.core.exception.ParseException; import cn.linghang.mywust.core.exception.ParseException;
import cn.linghang.mywust.core.parser.Parser; import cn.linghang.mywust.core.parser.Parser;
import cn.linghang.mywust.core.util.JsoupUtil;
import cn.linghang.mywust.model.global.ClassRoom; import cn.linghang.mywust.model.global.ClassRoom;
import cn.linghang.mywust.model.global.Course; import cn.linghang.mywust.model.global.Course;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
@ -68,11 +69,11 @@ public class UndergradCourseTableParser implements Parser<List<Course>> {
Elements timeElements = courseElement.getElementsByAttributeValue("title", "周次(节次)"); Elements timeElements = courseElement.getElementsByAttributeValue("title", "周次(节次)");
Elements classroomElements = courseElement.getElementsByAttributeValue("title", "教室"); Elements classroomElements = courseElement.getElementsByAttributeValue("title", "教室");
courseBuilder.teachClass(classElements.isEmpty() ? "" : classElements.get(0).text()); courseBuilder.teachClass(JsoupUtil.getElementText(classElements));
courseBuilder.teacher(teacherElements.isEmpty() ? "" : teacherElements.get(0).text()); courseBuilder.teacher(JsoupUtil.getElementText(teacherElements));
ClassRoom classRoom = new ClassRoom(); ClassRoom classRoom = new ClassRoom();
classRoom.setRoom(classroomElements.isEmpty() ? "" : classroomElements.get(0).text()); classRoom.setRoom(JsoupUtil.getElementText(classroomElements));
courseBuilder.classroom(classRoom); courseBuilder.classroom(classRoom);
int weekDay = girdCount % 7; int weekDay = girdCount % 7;

@ -0,0 +1,31 @@
package cn.linghang.mywust.core.parser.undergraduate;
import cn.linghang.mywust.core.exception.ParseException;
import cn.linghang.mywust.core.parser.Parser;
import cn.linghang.mywust.core.util.JsoupUtil;
import cn.linghang.mywust.core.util.PageFormExtractor;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.util.HashMap;
import java.util.Map;
public class UndergradCreditStatusIndexParser implements Parser<Map<String, String>> {
private static final String FROM_PARAM_XPATH = "//*[@id=\"Form1\"]/table/tbody/tr/td[3]";
@Override
public Map<String, String> parse(String html) throws ParseException {
Elements paramElement = Jsoup.parse(html).selectXpath(FROM_PARAM_XPATH);
if (paramElement.isEmpty()) {
throw new ParseException("学分修读情况首页解析失败,关键元素不存在...", html);
}
Element trimmedElement = paramElement.get(0);
Elements paramElements = trimmedElement.getElementsByAttribute("name");
Map<String, String> targetMap = new HashMap<>(2);
paramElements.forEach(element -> targetMap.put(element.attr("name"), element.attr("value")));
return targetMap;
}
}

@ -0,0 +1,20 @@
package cn.linghang.mywust.core.parser.undergraduate;
import cn.linghang.mywust.core.exception.ParseException;
import cn.linghang.mywust.core.parser.Parser;
import cn.linghang.mywust.core.util.JsoupUtil;
import org.jsoup.Jsoup;
import org.jsoup.select.Elements;
public class UndergradCreditStatusParser implements Parser<String> {
@Override
public String parse(String html) throws ParseException {
// 此处只提取核心内容,内容调整由调用者自行处理
Elements mainTable = Jsoup.parse(html).selectXpath("/html/body/div/div/table[2]");
if (mainTable.isEmpty()) {
throw new ParseException("学分修读情况html解析提取失败", html);
}
return mainTable.outerHtml();
}
}

@ -0,0 +1,67 @@
package cn.linghang.mywust.core.parser.undergraduate;
import cn.linghang.mywust.core.exception.ParseException;
import cn.linghang.mywust.core.parser.Parser;
import cn.linghang.mywust.core.util.JsoupUtil;
import cn.linghang.mywust.model.undergrad.ExamDelayApplication;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class UndergradExamDelayParser implements Parser<List<ExamDelayApplication>> {
private static final String rowXpath = "//*[@id=\"Form1\"]/table/tbody/tr";
@Override
public List<ExamDelayApplication> parse(String html) throws ParseException {
Elements rows = Jsoup.parse(html).selectXpath(rowXpath);
// 列表为空,应该是换ui了,要修改了
if (rows.isEmpty()) {
throw new ParseException("解析缓考申请时发生错误:未找到相关匹配元素", html);
}
// 只有一行,就是只有表头,不用废话了,直接返回
if (rows.size() == 1) {
return new ArrayList<>();
}
// i = 1 跳过表头
List<ExamDelayApplication> applications = new ArrayList<>(rows.size());
for (int i = 1; i < rows.size(); i++) {
Element row = rows.get(i);
Elements girds = row.getElementsByTag("td");
// 成绩标识为空的话说明和缓考没啥关系
if ("".equals(JsoupUtil.getElementText(girds, 8))) {
continue;
}
ExamDelayApplication application = new ExamDelayApplication();
application.setTerm(JsoupUtil.getElementText(girds, 1));
application.setCourseNumber(JsoupUtil.getElementText(girds, 2));
application.setCourseName(JsoupUtil.getElementText(girds, 3));
application.setCourseHours(JsoupUtil.getElementText(girds, 4));
application.setCredit(JsoupUtil.getElementText(girds, 5));
application.setCourseType(JsoupUtil.getElementText(girds, 6));
application.setExamType(JsoupUtil.getElementText(girds, 7));
application.setScoreFlag(JsoupUtil.getElementText(girds, 8));
application.setDelayReason(JsoupUtil.getElementText(girds, 9));
application.setStatus(JsoupUtil.getElementText(girds, 10));
application.setAuditOpinion(JsoupUtil.getElementText(girds, 11));
application.setTime(JsoupUtil.getElementText(girds, 13));
String overdueString = JsoupUtil.getElementText(girds, 14);
application.setOverdue(overdueString.contains("是") || overdueString.contains("已") || overdueString.contains("超"));
applications.add(application);
}
return applications;
}
}

@ -38,23 +38,23 @@ public class UndergradScoreParser implements Parser<List<Score>> {
Score score = new Score(); Score score = new Score();
// 这段看着震撼,但其实很丑 // 这段看着震撼,但其实很丑
score.setId(JsoupUtil.getElementContext(girds.get(0))); score.setId(JsoupUtil.getElementText(girds.get(0)));
score.setTerm(JsoupUtil.getElementContext(girds.get(1))); score.setTerm(JsoupUtil.getElementText(girds.get(1)));
score.setCourseNumber(JsoupUtil.getElementContext(girds.get(2))); score.setCourseNumber(JsoupUtil.getElementText(girds.get(2)));
score.setCourseName(JsoupUtil.getElementContext(girds.get(3))); score.setCourseName(JsoupUtil.getElementText(girds.get(3)));
score.setGroupName(JsoupUtil.getElementContext(girds.get(4))); score.setGroupName(JsoupUtil.getElementText(girds.get(4)));
score.setScore(JsoupUtil.getElementContext(girds.get(5))); score.setScore(JsoupUtil.getElementText(girds.get(5)));
score.setFlag(JsoupUtil.getElementContext(girds.get(6))); score.setFlag(JsoupUtil.getElementText(girds.get(6)));
score.setCredit(JsoupUtil.getElementContext(girds.get(7))); score.setCredit(JsoupUtil.getElementText(girds.get(7)));
score.setCourseHours(JsoupUtil.getElementContext(girds.get(8))); score.setCourseHours(JsoupUtil.getElementText(girds.get(8)));
score.setGradePoint(JsoupUtil.getElementContext(girds.get(9))); score.setGradePoint(JsoupUtil.getElementText(girds.get(9)));
score.setEvaluateMethod(JsoupUtil.getElementContext(girds.get(11))); score.setEvaluateMethod(JsoupUtil.getElementText(girds.get(11)));
score.setKind(JsoupUtil.getElementContext(girds.get(12))); score.setKind(JsoupUtil.getElementText(girds.get(12)));
score.setCourseKind(JsoupUtil.getElementContext(girds.get(13))); score.setCourseKind(JsoupUtil.getElementText(girds.get(13)));
scores.add(score); scores.add(score);
} }

@ -38,10 +38,12 @@ public class RequestFactory {
return makeStringDataHttpRequest(url, data, cookies).addHeaders(additionalHeaders); return makeStringDataHttpRequest(url, data, cookies).addHeaders(additionalHeaders);
} }
private static final byte[] ZERO_BYTE = {0};
public static HttpRequest makeStringDataHttpRequest(String url, String stringData, String cookies) { public static HttpRequest makeStringDataHttpRequest(String url, String stringData, String cookies) {
return HttpRequest.builder() return HttpRequest.builder()
.url(makeUrl(url)) .url(makeUrl(url))
.data(stringData.getBytes(StandardCharsets.UTF_8)) .data(stringData == null ? null : stringData.getBytes(StandardCharsets.UTF_8))
.cookies(cookies) .cookies(cookies)
.build() .build()
.addHeaders(DEFAULT_HTTP_REQUEST.getHeaders()); .addHeaders(DEFAULT_HTTP_REQUEST.getHeaders());

@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data; import lombok.Data;
@Data @Data
public class Map { public class Map<S, S1> {
@JsonProperty("baseInfo") @JsonProperty("baseInfo")
private BaseInfo baseInfo; private BaseInfo baseInfo;

@ -2,14 +2,13 @@ package cn.linghang.mywust.core.request.undergrade;
import cn.linghang.mywust.core.api.UndergradUrls; import cn.linghang.mywust.core.api.UndergradUrls;
import cn.linghang.mywust.core.request.RequestFactory; import cn.linghang.mywust.core.request.RequestFactory;
import cn.linghang.mywust.model.global.Campus;
import cn.linghang.mywust.network.entitys.FormBodyBuilder; import cn.linghang.mywust.network.entitys.FormBodyBuilder;
import cn.linghang.mywust.network.entitys.HttpRequest; import cn.linghang.mywust.network.entitys.HttpRequest;
import cn.linghang.mywust.util.StringUtil; import cn.linghang.mywust.util.StringUtil;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.HashMap; import java.util.*;
import java.util.Map;
import java.util.TreeMap;
public class BkjxRequestFactory extends RequestFactory { public class BkjxRequestFactory extends RequestFactory {
public static HttpRequest sessionCookieRequest(String serviceTicket) { public static HttpRequest sessionCookieRequest(String serviceTicket) {
@ -35,11 +34,39 @@ public class BkjxRequestFactory extends RequestFactory {
formBodyBuilder.add("xsfs", "all"); formBodyBuilder.add("xsfs", "all");
byte[] postData = formBodyBuilder.buildAndToString().getBytes(StandardCharsets.UTF_8); byte[] postData = formBodyBuilder.buildAndToString().getBytes(StandardCharsets.UTF_8);
return makeHttpRequest(UndergradUrls.BKJX_EXAM_INFO_API, postData, cookies); return makeHttpRequest(UndergradUrls.BKJX_SCORE_API, postData, cookies);
} }
public static HttpRequest trainingPlanPageRequest(String cookies) { public static HttpRequest trainingPlanPageRequest(String cookies) {
return makeHttpRequest(UndergradUrls.BKJX_SCHEME_API, null, cookies); return makeHttpRequest(UndergradUrls.BKJX_TRAINING_PLAN_API, null, cookies);
}
public static HttpRequest creditStatusIndexPageRequest(String cookies) {
return makeHttpRequest(UndergradUrls.BKJX_CREDIT_STATUS_INDEX_API, null, cookies);
}
public static HttpRequest creditStatusPageRequest(String cookies, String majorId, String teachPlainId) {
FormBodyBuilder formBodyBuilder = new FormBodyBuilder(2);
// “年度专业代码”(猜的)和“教学0301执行计划id”的参数
formBodyBuilder.add("ndzydm", majorId).add("jx0301zxjhid", teachPlainId);
return makeStringDataHttpRequest(UndergradUrls.BKJX_CREDIT_STATUS_API, formBodyBuilder.buildAndToString(), cookies);
}
public static HttpRequest creditStatusPageRequest(String cookies, Map<String , String> params) {
FormBodyBuilder formBodyBuilder = new FormBodyBuilder(2);
Set<String> keys = params.keySet();
for (String key : keys) {
formBodyBuilder.add(key, params.get(key));
}
return makeStringDataHttpRequest(UndergradUrls.BKJX_CREDIT_STATUS_API, formBodyBuilder.buildAndToString(), cookies);
}
// 不带参数的学分修读情况获取,正常情况下是待带两个参数的(ndzyd和mjx0301zxjhid)
// 但是经过实际测试直接post也是可以的,也就是说post什么都是可以的
public static HttpRequest creditStatusPageRequest(String cookies) {
return makeStringDataHttpRequest(UndergradUrls.BKJX_CREDIT_STATUS_API, "", cookies);
} }
public static HttpRequest courseTablePageRequest(String term, String cookies) { public static HttpRequest courseTablePageRequest(String term, String cookies) {
@ -52,6 +79,29 @@ public class BkjxRequestFactory extends RequestFactory {
return makeHttpRequest(UndergradUrls.BKJX_COURSE_TABLE_API, queryData, cookies); return makeHttpRequest(UndergradUrls.BKJX_COURSE_TABLE_API, queryData, cookies);
} }
public static HttpRequest examActivityListRequest(String term, String cookie) {
String url = String.format(UndergradUrls.BKJX_EXAM_ACTIVITY_LIST_API, term);
return makeHttpRequest(url, null, cookie);
}
public static HttpRequest examDelayApplicationListRequest(String term, String activityId, String cookie) {
FormBodyBuilder formBodyBuilder = new FormBodyBuilder(4);
formBodyBuilder.add("xnxqid", term);
formBodyBuilder.add("cj0701id", activityId);
formBodyBuilder.add("kch", "");
formBodyBuilder.add("iswfmes", "");
return makeStringDataHttpRequest(UndergradUrls.BKJX_EXAM_DELAY_APPLICATION_LIST_API, formBodyBuilder.buildAndToString(), cookie);
}
public static HttpRequest buildingListRequest(String campus, String cookie) {
return null;
}
public static HttpRequest buildingListRequest(Campus campus, String cookie) {
return buildingListRequest(campus.id, cookie);
}
public static class Legacy { public static class Legacy {
public static HttpRequest dataStringRequest() { public static HttpRequest dataStringRequest() {
return makeHttpRequest(UndergradUrls.Legacy.BKJX_DATA_STRING_API); return makeHttpRequest(UndergradUrls.Legacy.BKJX_DATA_STRING_API);

@ -39,7 +39,10 @@ public class LibraryLogin {
} }
public boolean checkCookie(String cookies) throws IOException { public boolean checkCookie(String cookies) throws IOException {
RequestClientOption option = RequestClientOption.DEFAULT_OPTION; return this.checkCookie(cookies, null);
}
public boolean checkCookie(String cookies, RequestClientOption option) throws IOException {
HttpRequest testRequest = LibraryRequestFactory.makeHttpRequest(LibraryUrls.LIBRARY_COOKIE_TEST_URL, null, cookies); HttpRequest testRequest = LibraryRequestFactory.makeHttpRequest(LibraryUrls.LIBRARY_COOKIE_TEST_URL, null, cookies);
HttpResponse testResponse = requester.get(testRequest, option); HttpResponse testResponse = requester.get(testRequest, option);

@ -35,14 +35,14 @@ public class UndergraduateLogin {
HttpResponse sessionResponse = requester.get(sessionRequest, requestOption); HttpResponse sessionResponse = requester.get(sessionRequest, requestOption);
String cookies = sessionResponse.getCookies(); String cookies = sessionResponse.getCookies();
if (!roughCheckCookie(cookies)) { if (roughCheckCookieFail(cookies)) {
log.error("[mywust]: Cookie粗查不通过:{}", cookies); log.error("[mywust]: Cookie粗查不通过:{}", cookies);
throw new ApiException(ApiException.Code.UNKNOWN_EXCEPTION, "登录获取的Cookie无效"); throw new ApiException(ApiException.Code.UNKNOWN_EXCEPTION, "登录获取的Cookie无效");
} }
// 检查Cookie是否真正可用,同时请求一次任意接口使后续接口能够正确响应 // 检查Cookie是否真正可用,同时请求一次任意接口使后续接口能够正确响应
// 拿到Cookie后调用的第一个接口会产生302/301跳转,需要再次调用才能正确响应 // 拿到Cookie后调用的第一个接口会产生302/301跳转,需要再次调用才能正确响应
if (!checkCookies(cookies, requestOption)) { if (checkCookiesFail(cookies, requestOption)) {
log.warn("[mywust]: Cookie检查不通过:{}", cookies); log.warn("[mywust]: Cookie检查不通过:{}", cookies);
} }
@ -89,28 +89,38 @@ public class UndergraduateLogin {
HttpResponse sessionResponse = requester.get(sessionRequest, requestOption); HttpResponse sessionResponse = requester.get(sessionRequest, requestOption);
String cookies = sessionResponse.getCookies(); String cookies = sessionResponse.getCookies();
if (checkCookies(cookies, requestOption)) { if (roughCheckCookieFail(cookies)) {
log.warn("[mywust]: Cookie检查不通过:{}", cookies); log.error("[mywust]: Cookie粗查不通过:{}", cookies);
throw new ApiException(ApiException.Code.UNKNOWN_EXCEPTION, "登录获取的Cookie无效"); throw new ApiException(ApiException.Code.UNKNOWN_EXCEPTION, "登录获取的Cookie无效");
} }
// 检查Cookie是否真正可用,同时请求一次任意接口使后续接口能够正确响应
// 拿到Cookie后调用的第一个接口会产生302/301跳转,需要再次调用才能正确响应
if (checkCookiesFail(cookies, requestOption)) {
log.warn("[mywust]: Cookie检查不通过:{}", cookies);
}
return cookies; return cookies;
} }
private boolean roughCheckCookie(String cookies) { private boolean roughCheckCookieFail(String cookies) {
return cookies != null && cookies.contains("JSESSIONID") && cookies.contains("SERVERID"); return cookies == null || !cookies.contains("JSESSIONID") || !cookies.contains("SERVERID");
} }
private static final int COOKIES_ERROR_RESPONSE_LENGTH = private static final int COOKIES_ERROR_RESPONSE_LENGTH =
("<script languge='javascript'>window.location.href='https://auth.wust.edu.cn/lyuapServer/login?service=http%3A%2F%2Fbkjx.wust.edu.cn%2Fjsxsd%2Fframework%2FblankPage.jsp'</script>") ("<script languge='javascript'>window.location.href='https://auth.wust.edu.cn/lyuapServer/login?service=http%3A%2F%2Fbkjx.wust.edu.cn%2Fjsxsd%2Fframework%2FblankPage.jsp'</script>")
.length(); .length();
public boolean checkCookies(String cookies, RequestClientOption option) throws IOException { public boolean checkCookiesFail(String cookies, RequestClientOption option) throws IOException {
HttpRequest testRequest = BkjxRequestFactory.makeHttpRequest(UndergradUrls.BKJX_TEST_API, null, cookies); HttpRequest testRequest = BkjxRequestFactory.makeHttpRequest(UndergradUrls.BKJX_TEST_API, null, cookies);
HttpResponse testResponse = requester.get(testRequest, option); HttpResponse testResponse = requester.get(testRequest, option);
// 判断响应长度是否为这么多个字,登录跳转响应长度 // 判断响应长度是否为这么多个字,登录跳转响应长度
// 这种办法虽然在极端情况下可能会出错(而且还挺蠢的),但是是最快的办法中比较准确的了 // 这种办法虽然在极端情况下可能会出错(而且还挺蠢的),但是是最快的办法中比较准确的了
return Math.abs(COOKIES_ERROR_RESPONSE_LENGTH - testResponse.getBody().length) > 8; return Math.abs(COOKIES_ERROR_RESPONSE_LENGTH - testResponse.getBody().length) <= 8;
}
public boolean checkCookiesFail(String cookies) throws IOException {
return this.checkCookiesFail(cookies, null);
} }
} }

@ -33,4 +33,8 @@ public class GraduateApiServiceBase {
this.checkResponse(response); this.checkResponse(response);
} }
public void checkCookies(String cookie) throws ApiException, IOException {
this.checkCookies(cookie, null);
}
} }

@ -23,7 +23,7 @@ public abstract class UndergradApiServiceBase {
// 检查响应是否正确 // 检查响应是否正确
if (response.getBody() == null || if (response.getBody() == null ||
response.getStatusCode() != HttpResponse.HTTP_OK || response.getStatusCode() != HttpResponse.HTTP_OK ||
BkjxUtil.checkLoginFinger(response.getBody())) { BkjxUtil.hasLoginFinger(response.getBody())) {
throw new ApiException(ApiException.Code.COOKIE_INVALID); throw new ApiException(ApiException.Code.COOKIE_INVALID);
} }
@ -36,6 +36,10 @@ public abstract class UndergradApiServiceBase {
this.checkResponse(response); this.checkResponse(response);
} }
public void checkCookies(String cookie) throws ApiException, IOException {
this.checkCookies(cookie, null);
}
public abstract String getPage(String cookie, Map<String, String> params, RequestClientOption option) throws ApiException, IOException; public abstract String getPage(String cookie, Map<String, String> params, RequestClientOption option) throws ApiException, IOException;
public abstract String getPage(String cookie, Map<String, String> params) throws ApiException, IOException; public abstract String getPage(String cookie, Map<String, String> params) throws ApiException, IOException;
} }

@ -0,0 +1,57 @@
package cn.linghang.mywust.core.service.undergraduate;
import cn.linghang.mywust.core.exception.ApiException;
import cn.linghang.mywust.core.exception.ParseException;
import cn.linghang.mywust.core.parser.undergraduate.UndergradCreditStatusIndexParser;
import cn.linghang.mywust.core.request.undergrade.BkjxRequestFactory;
import cn.linghang.mywust.network.RequestClientOption;
import cn.linghang.mywust.network.Requester;
import cn.linghang.mywust.network.entitys.HttpRequest;
import cn.linghang.mywust.network.entitys.HttpResponse;
import java.io.IOException;
import java.util.Map;
public class UndergradCreditStatusApiService extends UndergradApiServiceBase {
private static final UndergradCreditStatusIndexParser parser = new UndergradCreditStatusIndexParser();
public UndergradCreditStatusApiService(Requester requester) {
super(requester);
}
@Override
public String getPage(String cookie, Map<String, String> params, RequestClientOption option) throws ApiException, IOException {
return this.getPage(cookie, option, false);
}
@Override
public String getPage(String cookie, Map<String, String> params) throws ApiException, IOException {
return this.getPage(cookie, params, null);
}
public String getPage(String cookie, RequestClientOption option, boolean quick) throws IOException, ApiException {
HttpRequest request;
if (quick) {
request = BkjxRequestFactory.creditStatusPageRequest(cookie);
} else {
HttpRequest indexRequest = BkjxRequestFactory.creditStatusIndexPageRequest(cookie);
HttpResponse indexResponse = requester.get(indexRequest, option);
this.checkResponse(indexResponse);
String indexHtml = indexResponse.getStringBody();
Map<String, String> params;
try {
params = parser.parse(indexHtml);
} catch (ParseException e) {
throw new ApiException(ApiException.Code.COOKIE_INVALID);
}
request = BkjxRequestFactory.creditStatusPageRequest(cookie, params);
}
HttpResponse response = requester.post(request, option);
this.checkResponse(response);
return response.getStringBody();
}
}

@ -0,0 +1,85 @@
package cn.linghang.mywust.core.service.undergraduate;
import cn.linghang.mywust.core.exception.ApiException;
import cn.linghang.mywust.core.request.undergrade.BkjxRequestFactory;
import cn.linghang.mywust.network.RequestClientOption;
import cn.linghang.mywust.network.Requester;
import cn.linghang.mywust.network.entitys.HttpRequest;
import cn.linghang.mywust.network.entitys.HttpResponse;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.IOException;
import java.util.Map;
/**
* 缓考申请查询
*/
public class UndergradExamDelayApiService extends UndergradApiServiceBase {
public UndergradExamDelayApiService(Requester requester) {
super(requester);
}
@Override
public String getPage(String cookie, Map<String, String> params, RequestClientOption option) throws ApiException, IOException {
if (params == null) {
throw new ApiException(ApiException.Code.INTERNAL_EXCEPTION);
}
String term = params.get("term");
String activityId = params.get("activityId");
if (term == null || activityId == null) {
throw new ApiException(ApiException.Code.INTERNAL_EXCEPTION);
}
return this.getPage(term, activityId, cookie, option);
}
@Override
public String getPage(String cookie, Map<String, String> params) throws ApiException, IOException {
return this.getPage(cookie, params, null);
}
public String getPage(String term, String activityId, String cookie, RequestClientOption option) throws ApiException, IOException {
HttpRequest request = BkjxRequestFactory.examDelayApplicationListRequest(term, activityId, cookie);
HttpResponse response = requester.post(request, option);
this.checkResponse(response);
return response.getStringBody();
}
public String getPage(String term, String activityId, String cookie) throws ApiException, IOException {
return this.getPage(term, activityId, cookie, null);
}
public ExamActivity[] getActivities(String term, String cookie, RequestClientOption option) throws ApiException, IOException {
HttpRequest request = BkjxRequestFactory.examActivityListRequest(term, cookie);
HttpResponse response = requester.get(request, option);
this.checkResponse(response);
try {
return new ObjectMapper().readValue(response.getBody(), ExamActivity[].class);
} catch (IOException e) {
e.printStackTrace();
throw new ApiException(ApiException.Code.UNKNOWN_EXCEPTION, "解析考试活动id列表失败:响应数据:" + response.getStringBody());
}
}
public ExamActivity[] getActivities(String term, String cookie) throws ApiException, IOException {
return this.getActivities(term, cookie, null);
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class ExamActivity {
@JsonProperty("cj0701id")
public String id;
@JsonProperty("cjlrmc")
public String name;
}
}

@ -34,4 +34,8 @@ public class UndergradScoreApiService extends UndergradApiServiceBase {
return new String(response.getBody()); return new String(response.getBody());
} }
public String getPage(String cookies) throws IOException, ApiException {
return this.getPage(cookies, (RequestClientOption) null);
}
} }

@ -34,4 +34,8 @@ public class UndergradStudentInfoApiService extends UndergradApiServiceBase {
return new String(response.getBody()); return new String(response.getBody());
} }
public String getPage(String cookies) throws IOException, ApiException {
return this.getPage(cookies, (RequestClientOption) null);
}
} }

@ -34,4 +34,8 @@ public class UndergradTrainingPlanApiService extends UndergradApiServiceBase {
return new String(response.getBody()); return new String(response.getBody());
} }
public String getPage(String cookies) throws IOException, ApiException {
return this.getPage(cookies, (RequestClientOption) null);
}
} }

@ -16,7 +16,7 @@ public class BkjxUtil {
* @param response 响应的字节 * @param response 响应的字节
* @return 是否需要重新登录 * @return 是否需要重新登录
*/ */
public static boolean checkLoginFinger(byte[] response) { public static boolean hasLoginFinger(byte[] response) {
if (response == null) { if (response == null) {
return false; return false;
} }

@ -10,7 +10,7 @@ public class JsoupUtil {
* @param element 元素对象 * @param element 元素对象
* @return 获取到的内容如果element为null则返回一个空的字符串 * @return 获取到的内容如果element为null则返回一个空的字符串
*/ */
public static String getElementContext(Element element) { public static String getElementText(Element element) {
return element == null ? "" : element.text(); return element == null ? "" : element.text();
} }
@ -18,6 +18,10 @@ public class JsoupUtil {
return element == null ? "" : element.outerHtml(); return element == null ? "" : element.outerHtml();
} }
public static String getOuterHtml(Elements elements) {
return elements == null ? "" : elements.outerHtml();
}
/** /**
* 从Element中拿到指定的标签值 * 从Element中拿到指定的标签值
* *
@ -39,7 +43,7 @@ public class JsoupUtil {
* @param element 元素对象 * @param element 元素对象
* @return 相应的值若element为空则返回空字符串 * @return 相应的值若element为空则返回空字符串
*/ */
public static String getText(Element element) { public static String getOwnText(Element element) {
if (element == null) { if (element == null) {
return ""; return "";
} else { } else {
@ -47,6 +51,31 @@ public class JsoupUtil {
} }
} }
/**
* 从Elements中拿到指定索引的文本内容
*
* @param elements 元素对象
* @param index 索引
* @return 相应的值其他情况均为空字符串
*/
public static String getOwnText(Elements elements, int index) {
if (elements == null) {
return "";
} else {
return index >= elements.size() ? "" : elements.get(index).ownText();
}
}
/**
* 从Elements中拿到索引中首个元素的文本内容
*
* @param elements 元素对象
* @return 相应的值其他情况均为空字符串
*/
public static String getOwnText(Elements elements) {
return getOwnText(elements, 0);
}
/** /**
* 从select类型的Element中拿取到已选中的选项值 * 从select类型的Element中拿取到已选中的选项值
* *

@ -0,0 +1,32 @@
package cn.linghang.mywust.model.global;
import java.util.StringJoiner;
public final class Building {
public final String id;
public final String name;
public final Campus campus;
public Building(String id, String name, Campus campus) {
this.id = id;
this.name = name;
this.campus = campus;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
@Override
public String toString() {
return new StringJoiner(", ", Building.class.getSimpleName() + "[", "]")
.add("id='" + id + "'")
.add("name='" + name + "'")
.add("campus=" + campus)
.toString();
}
}

@ -0,0 +1,29 @@
package cn.linghang.mywust.model.global;
import java.util.StringJoiner;
public final class Campus {
public final String id;
public final String name;
public Campus(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
@Override
public String toString() {
return new StringJoiner(", ", Campus.class.getSimpleName() + "[", "]")
.add("id='" + id + "'")
.add("name='" + name + "'")
.toString();
}
}

@ -0,0 +1,29 @@
package cn.linghang.mywust.model.global;
import java.util.StringJoiner;
public final class College {
public final String id;
public final String name;
public College(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
@Override
public String toString() {
return new StringJoiner(", ", College.class.getSimpleName() + "[", "]")
.add("id='" + id + "'")
.add("name='" + name + "'")
.toString();
}
}

@ -0,0 +1,26 @@
package cn.linghang.mywust.model.undergrad;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ExamDelayApplication {
private String term;
private String courseNumber;
private String courseName;
private String courseHours;
private String credit;
private String courseType;
private String examType;
private String scoreFlag;
private String delayReason;
private String status;
private String auditOpinion;
private String time;
private boolean overdue;
}

@ -4,20 +4,25 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>cn.linghang</groupId> <parent>
<groupId>cn.linghang</groupId>
<artifactId>mywust</artifactId>
<version>${revision}</version>
</parent>
<artifactId>mywust-test</artifactId> <artifactId>mywust-test</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>cn.linghang</groupId> <groupId>cn.linghang</groupId>
<artifactId>mywust-core</artifactId> <artifactId>mywust-core</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>${project.parent.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>cn.linghang</groupId> <groupId>cn.linghang</groupId>
<artifactId>mywust-network-okhttp</artifactId> <artifactId>mywust-network-okhttp</artifactId>
<version>1.0-SNAPSHOT</version> <version>${project.parent.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
@ -57,7 +62,6 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<properties> <properties>

@ -1,49 +0,0 @@
import cn.linghang.mywust.core.exception.BasicException;
import cn.linghang.mywust.core.parser.Parser;
import cn.linghang.mywust.core.parser.undergraduate.CourseTableParser;
import cn.linghang.mywust.core.service.undergraduate.CourseTableApiService;
import cn.linghang.mywust.model.global.Course;
import cn.linghang.mywust.network.RequestClientOption;
import cn.linghang.mywust.network.Requester;
import cn.linghang.mywust.network.okhttp.SimpleOkhttpRequester;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.List;
import java.util.Scanner;
public class CourseTableTest {
@Test
public void run() throws BasicException, IOException {
System.out.println("课表");
System.out.println("Cookie:");
Scanner scanner = new Scanner(System.in);
String cookie = scanner.nextLine();
System.out.println("使用Cookie:" + cookie);
System.out.println("学期(如2022-2023-1):");
String term = scanner.nextLine();
System.out.println("使用学期:" + term);
Requester requester = new SimpleOkhttpRequester();
CourseTableApiService service = new CourseTableApiService(requester);
RequestClientOption option = new RequestClientOption();
option.setTimeout(5);
RequestClientOption.Proxy proxy = new RequestClientOption.Proxy();
proxy.setPort(6060);
proxy.setAddress("127.0.0.1");
option.setProxy(proxy);
option.setFallowUrlRedirect(false);
Parser<List<Course>> parser = new CourseTableParser();
List<Course> courses = parser.parse(service.getCourseTablePage(term, cookie, option));
for (Course info : courses) {
System.out.println(info);
}
}
}

@ -1,46 +0,0 @@
import cn.linghang.mywust.core.exception.BasicException;
import cn.linghang.mywust.core.parser.Parser;
import cn.linghang.mywust.core.parser.undergraduate.ExamInfoParser;
import cn.linghang.mywust.core.service.undergraduate.ExamInfoApiService;
import cn.linghang.mywust.model.global.ExamInfo;
import cn.linghang.mywust.network.RequestClientOption;
import cn.linghang.mywust.network.Requester;
import cn.linghang.mywust.network.okhttp.SimpleOkhttpRequester;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.List;
import java.util.Scanner;
public class ExamInfoTest {
@Test
public void run() throws BasicException, IOException {
System.out.println("成绩获取");
System.out.println("Cookie:");
Scanner scanner = new Scanner(System.in);
String cookie = scanner.nextLine();
System.out.println("使用Cookie:" + cookie);
Requester requester = new SimpleOkhttpRequester();
ExamInfoApiService jwcService = new ExamInfoApiService(requester);
RequestClientOption option = new RequestClientOption();
option.setTimeout(5);
RequestClientOption.Proxy proxy = new RequestClientOption.Proxy();
proxy.setPort(8080);
proxy.setAddress("127.0.0.1");
option.setProxy(proxy);
option.setFallowUrlRedirect(false);
Parser<List<ExamInfo>> parser = new ExamInfoParser();
List<ExamInfo> infos = parser.parse(jwcService.getExamInfoPage(cookie, option));
for (ExamInfo info : infos) {
System.out.println(info);
}
}
}

@ -1,45 +0,0 @@
import cn.linghang.mywust.core.exception.BasicException;
import cn.linghang.mywust.core.service.auth.UnionLogin;
import cn.linghang.mywust.core.service.auth.JwcLogin;
import cn.linghang.mywust.network.RequestClientOption;
import cn.linghang.mywust.network.Requester;
import cn.linghang.mywust.network.okhttp.SimpleOkhttpRequester;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.Scanner;
public class JwcLoginTest {
@Test
public void run() throws BasicException, IOException {
System.out.println("bkjx登录测试(统一身份验证)");
System.out.println("输入账号(学号)和密码,用“ ”(空格)分割");
Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine();
String username = input.split(" ")[0];
String password = input.split(" ")[1];
System.out.println("账号:" + username);
System.out.println("密码:" + password);
Requester requester = new SimpleOkhttpRequester();
JwcLogin jwcLogin = new JwcLogin(requester);
RequestClientOption option = new RequestClientOption();
option.setTimeout(5);
option.setFallowUrlRedirect(false);
RequestClientOption.Proxy proxy = new RequestClientOption.Proxy();
proxy.setPort(6060);
proxy.setAddress("127.0.0.1");
option.setProxy(proxy);
String cookies = jwcLogin.getLoginCookie(username, password, option);
System.out.printf("获取到Cookies: %s \n", cookies);
System.out.printf("检查Cookies: %s", jwcLogin.checkCookies(cookies, RequestClientOption.DEFAULT_OPTION));
}
}

@ -1,39 +0,0 @@
import cn.linghang.mywust.core.exception.BasicException;
import cn.linghang.mywust.core.service.auth.UnionLogin;
import cn.linghang.mywust.core.service.auth.LibraryLogin;
import cn.linghang.mywust.network.RequestClientOption;
import cn.linghang.mywust.network.Requester;
import cn.linghang.mywust.network.okhttp.SimpleOkhttpRequester;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.Scanner;
public class LibraryLoginTest {
@Test
public void run() throws BasicException, IOException {
System.out.println("图书馆登陆测试");
System.out.println("输入账号(学号)和密码,用“ ”(空格)分割");
Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine();
String username = input.split(" ")[0];
String password = input.split(" ")[1];
System.out.println("账号:" + username);
System.out.println("密码:" + password);
Requester requester = new SimpleOkhttpRequester();
LibraryLogin libraryLogin = new LibraryLogin(requester);
RequestClientOption option = RequestClientOption.DEFAULT_OPTION;
String cookies = libraryLogin.getLibraryLoginCookie(username, password, option);
System.out.printf("获取到的cookies: %s \n", cookies);
System.out.printf("检查Cookies: %s", libraryLogin.checkCookie(cookies));
}
}

@ -1,12 +1,51 @@
import cn.linghang.mywust.core.exception.ApiException;
import cn.linghang.mywust.core.exception.ParseException; import cn.linghang.mywust.core.exception.ParseException;
import cn.linghang.mywust.core.parser.undergraduate.ExamInfoParser; import cn.linghang.mywust.core.parser.Parser;
import cn.linghang.mywust.core.parser.physics.PhysicsCoursePageParser;
import cn.linghang.mywust.core.parser.undergraduate.UndergradCreditStatusParser;
import cn.linghang.mywust.core.parser.undergraduate.UndergradExamDelayParser;
import cn.linghang.mywust.core.service.auth.PhysicsLogin;
import cn.linghang.mywust.core.service.auth.UndergraduateLogin;
import cn.linghang.mywust.core.service.physics.PhysicsApiService;
import cn.linghang.mywust.core.service.undergraduate.UndergradCreditStatusApiService;
import cn.linghang.mywust.core.service.undergraduate.UndergradExamDelayApiService;
import cn.linghang.mywust.model.global.Course;
import cn.linghang.mywust.model.physics.PhysicsCourse;
import cn.linghang.mywust.model.undergrad.ExamDelayApplication;
import cn.linghang.mywust.network.RequestClientOption;
import cn.linghang.mywust.network.Requester;
import cn.linghang.mywust.network.okhttp.SimpleOkhttpRequester;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import java.util.Scanner;
public class ParseTest { public class ParseTest {
public static void main(String[] args) throws IOException, ParseException { public static void main(String[] args) throws IOException, ParseException, ApiException {
ExamInfoParser parser = new ExamInfoParser(); // Scanner scanner = new Scanner(System.in);
System.out.println(parser.parse(Jsoup.connect("http://127.0.0.1/a.html").get().toString())); //
// String cookie = scanner.nextLine();
//
// System.out.println("Cookie:" + cookie);
//
// Requester requester = new SimpleOkhttpRequester();
// UndergradExamDelayApiService service = new UndergradExamDelayApiService(requester);
//
// RequestClientOption option = new RequestClientOption();
// RequestClientOption.Proxy proxy = RequestClientOption.Proxy.builder().build();
// proxy.setAddress("127.0.0.1");
// proxy.setPort(8080);
// option.setProxy(proxy);
//
// String data = service.getPage("2022-2023-1", "9FF84C58B1CA4BDBB656E4C224B617A9", cookie, option);
// System.out.println(data);
String html = Jsoup.connect("http://localhost/xf.html").get().toString();
UndergradCreditStatusParser parser = new UndergradCreditStatusParser();
String result = parser.parse(html);
System.out.println(result);
} }
} }

@ -1,38 +0,0 @@
import cn.linghang.mywust.core.exception.BasicException;
import cn.linghang.mywust.core.service.undergraduate.TrainingPlanApiService;
import cn.linghang.mywust.network.RequestClientOption;
import cn.linghang.mywust.network.Requester;
import cn.linghang.mywust.network.okhttp.SimpleOkhttpRequester;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.Scanner;
public class SchemeTest {
@Test
public void run() throws BasicException, IOException {
System.out.println("培养方案获取");
System.out.println("Cookie:");
Scanner scanner = new Scanner(System.in);
String cookie = scanner.nextLine();
System.out.println("使用Cookie:" + cookie);
RequestClientOption option = new RequestClientOption();
option.setTimeout(5);
RequestClientOption.Proxy proxy = new RequestClientOption.Proxy();
proxy.setPort(6060);
proxy.setAddress("127.0.0.1");
option.setProxy(null);
option.setFallowUrlRedirect(false);
Requester requester = new SimpleOkhttpRequester();
TrainingPlanApiService jwcService = new TrainingPlanApiService(requester);
String page = jwcService.getTrainingPlanPage(cookie, option);
System.out.println(page);
}
}

@ -1,39 +0,0 @@
import cn.linghang.mywust.core.exception.BasicException;
import cn.linghang.mywust.core.parser.Parser;
import cn.linghang.mywust.core.parser.undergraduate.StudentInfoPageParser;
import cn.linghang.mywust.core.service.undergraduate.StudentInfoApiService;
import cn.linghang.mywust.model.global.StudentInfo;
import cn.linghang.mywust.network.RequestClientOption;
import cn.linghang.mywust.network.Requester;
import cn.linghang.mywust.network.okhttp.SimpleOkhttpRequester;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.Scanner;
public class StudentInfoPageTest {
@Test
public void run() throws BasicException, IOException {
System.out.println("学生信息获取");
System.out.println("Cookie:");
Scanner scanner = new Scanner(System.in);
String cookie = scanner.nextLine();
System.out.println("使用Cookie:" + cookie);
Requester requester = new SimpleOkhttpRequester();
StudentInfoApiService jwcService = new StudentInfoApiService(requester);
RequestClientOption option = new RequestClientOption();
option.setTimeout(5);
option.setProxy(null);
option.setFallowUrlRedirect(false);
Parser<StudentInfo> parser = new StudentInfoPageParser();
StudentInfo info = parser.parse(jwcService.getStudentInfoPage(cookie, option));
System.out.println(info);
}
}

@ -1,2 +0,0 @@
public class UnderGraduateTest {
}

@ -1,58 +0,0 @@
import cn.linghang.mywust.core.exception.BasicException;
import cn.linghang.mywust.core.parser.Parser;
import cn.linghang.mywust.core.parser.physics.PhysicsCoursePageParser;
import cn.linghang.mywust.core.service.auth.PhysicsLogin;
import cn.linghang.mywust.core.service.physics.PhysicsApiService;
import cn.linghang.mywust.model.global.Course;
import cn.linghang.mywust.model.physics.PhysicsCourse;
import cn.linghang.mywust.network.RequestClientOption;
import cn.linghang.mywust.network.Requester;
import cn.linghang.mywust.network.okhttp.SimpleOkhttpRequester;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.List;
import java.util.Scanner;
public class WlsyLoginTest {
@Test
public void run() throws BasicException, IOException {
System.out.println("物理实验登陆测试");
System.out.println("输入账号(学号)和密码,用“ ”(空格)分割");
Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine();
String username = input.split(" ")[0];
String password = input.split(" ")[1];
System.out.println("账号:" + username);
System.out.println("密码:" + password);
Requester requester = new SimpleOkhttpRequester();
PhysicsLogin physicsLogin = new PhysicsLogin(requester);
RequestClientOption option = RequestClientOption.DEFAULT_OPTION;
RequestClientOption.Proxy proxy = new RequestClientOption.Proxy();
proxy.setAddress("127.0.0.1");
proxy.setPort(8080);
option.setProxy(proxy);
String cookies = physicsLogin.getLoginCookie(username, password, option);
System.out.printf("获取到的cookies: %s \n", cookies);
option.setFallowUrlRedirect(false);
PhysicsApiService api = new PhysicsApiService(requester);
String response = api.getCoursePage(cookies, option);
System.out.println(response);
Parser<List<PhysicsCourse>> parser = new PhysicsCoursePageParser();
List<PhysicsCourse> courses = parser.parse(response);
for (Course info : courses) {
System.out.println(info);
}
}
}

@ -115,7 +115,7 @@ public class PasswordEncoder {
*/ */
public static String legacyPassword(String username, String password, String dataString) { public static String legacyPassword(String username, String password, String dataString) {
String[] parts = dataString.split("#"); String[] parts = dataString.split("#");
String scode = parts[0]; StringBuilder scode = new StringBuilder(parts[0]);
String sxh = parts[1]; String sxh = parts[1];
String code = username + "%%%" + password; String code = username + "%%%" + password;
@ -123,11 +123,11 @@ public class PasswordEncoder {
StringBuilder encoded = new StringBuilder(); StringBuilder encoded = new StringBuilder();
for (int i = 0, codeLength = code.length(); i < codeLength; i++) { for (int i = 0, codeLength = code.length(); i < codeLength; i++) {
if (i < 20) { if (i < 20) {
int endIndex = parseInt(sxh.substring(i, i + 1)); int index = sxh.charAt(i) - 48;
encoded.append(code.charAt(i)).append(scode, 0, endIndex); encoded.append(code.charAt(i)).append(scode, 0, index);
scode = scode.substring(endIndex); scode.delete(0, index);
} else { } else {
encoded.append(code.substring(i)); encoded.append(code, i, codeLength);
i = codeLength; i = codeLength;
} }
} }

@ -70,4 +70,31 @@ public class StringUtil {
return Base64.encodeBase64String(rawText.toString().getBytes(StandardCharsets.UTF_8)); return Base64.encodeBase64String(rawText.toString().getBytes(StandardCharsets.UTF_8));
} }
public static String NoneNullString(String str) {
return str == null ? "" : str;
}
public static String getTermString(Date date, boolean autumn) {
Calendar calendar = new GregorianCalendar();
calendar.setTime(date);
return getTermString(calendar, autumn);
}
public static String getTermString(Calendar calendar, boolean autumn) {
int year = calendar.get(Calendar.YEAR);
int nextYear = year + 1;
// 秋季期,就是第一个学期
return String.format("%d-%d-%s", year, nextYear, autumn ? "1" : "2");
}
public static String getCurrentTermString() {
Calendar now = Calendar.getInstance();
int month = now.get(Calendar.MONTH);
// 一般八月到第二年二月算是是秋季期
return getTermString(now, month >= 8 || month < 2);
}
} }

@ -14,6 +14,7 @@
<module>mywust-model</module> <module>mywust-model</module>
<module>mywust-network</module> <module>mywust-network</module>
<module>mywust-util</module> <module>mywust-util</module>
<module>mywust-test</module>
<module>mywust-network-httpclient</module> <module>mywust-network-httpclient</module>
<module>mywust-network-okhttp</module> <module>mywust-network-okhttp</module>
</modules> </modules>
@ -28,7 +29,7 @@
</dependencies> </dependencies>
<properties> <properties>
<revision>0.0.1-fixed</revision> <revision>0.0.2-SNAPSHOT</revision>
<lombok.version>1.18.22</lombok.version> <lombok.version>1.18.22</lombok.version>

Loading…
Cancel
Save