Compare commits

...

9 Commits

  1. 17
      .flattened-pom.xml
  2. 19
      README.md
  3. 58
      mywust-core/.flattened-pom.xml
  4. 32
      mywust-core/pom.xml
  5. 5
      mywust-core/src/main/java/cn/linghang/mywust/core/Mywust.java
  6. 5
      mywust-core/src/main/java/cn/linghang/mywust/core/MywustFactory.java
  7. 2
      mywust-core/src/main/java/cn/linghang/mywust/core/api/GraduateUrls.java
  8. 2
      mywust-core/src/main/java/cn/linghang/mywust/core/api/LibraryUrls.java
  9. 2
      mywust-core/src/main/java/cn/linghang/mywust/core/api/PhysicsSystemUrls.java
  10. 2
      mywust-core/src/main/java/cn/linghang/mywust/core/api/UndergradUrls.java
  11. 2
      mywust-core/src/main/java/cn/linghang/mywust/core/api/UnionAuthUrls.java
  12. 45
      mywust-core/src/main/java/cn/linghang/mywust/core/exception/ApiException.java
  13. 22
      mywust-core/src/main/java/cn/linghang/mywust/core/parser/graduate/GraduateScoreParser.java
  14. 72
      mywust-core/src/main/java/cn/linghang/mywust/core/parser/undergraduate/ExamInfoParser.java
  15. 4
      mywust-core/src/main/java/cn/linghang/mywust/core/parser/undergraduate/UndergradCourseTableParser.java
  16. 72
      mywust-core/src/main/java/cn/linghang/mywust/core/parser/undergraduate/UndergradScoreParser.java
  17. 23
      mywust-core/src/main/java/cn/linghang/mywust/core/parser/undergraduate/UndergradStudentInfoPageParser.java
  18. 2
      mywust-core/src/main/java/cn/linghang/mywust/core/parser/undergraduate/UndergradTrainingPlanPageParser.java
  19. 18
      mywust-core/src/main/java/cn/linghang/mywust/core/request/RequestFactory.java
  20. 4
      mywust-core/src/main/java/cn/linghang/mywust/core/request/auth/UnionAuthRequestFactory.java
  21. 14
      mywust-core/src/main/java/cn/linghang/mywust/core/request/graduate/GraduateRequestFactory.java
  22. 16
      mywust-core/src/main/java/cn/linghang/mywust/core/request/library/LibraryRequestFactory.java
  23. 12
      mywust-core/src/main/java/cn/linghang/mywust/core/request/physics/PhysicsSystemRequestFactory.java
  24. 35
      mywust-core/src/main/java/cn/linghang/mywust/core/request/undergrade/BkjxRequestFactory.java
  25. 24
      mywust-core/src/main/java/cn/linghang/mywust/core/service/auth/GraduateLogin.java
  26. 61
      mywust-core/src/main/java/cn/linghang/mywust/core/service/auth/JwcLogin.java
  27. 12
      mywust-core/src/main/java/cn/linghang/mywust/core/service/auth/LibraryLogin.java
  28. 3
      mywust-core/src/main/java/cn/linghang/mywust/core/service/auth/PhysicsLogin.java
  29. 116
      mywust-core/src/main/java/cn/linghang/mywust/core/service/auth/UndergraduateLogin.java
  30. 35
      mywust-core/src/main/java/cn/linghang/mywust/core/service/auth/UnionLogin.java
  31. 43
      mywust-core/src/main/java/cn/linghang/mywust/core/service/captcha/solver/DdddOcrCaptchaSolver.java
  32. 11
      mywust-core/src/main/java/cn/linghang/mywust/core/service/captcha/solver/LocalPaddleOcrCaptchaSolver.java
  33. 8
      mywust-core/src/main/java/cn/linghang/mywust/core/service/graduate/GraduateApiServiceBase.java
  34. 7
      mywust-core/src/main/java/cn/linghang/mywust/core/service/graduate/GraduateCourseTableApiService.java
  35. 6
      mywust-core/src/main/java/cn/linghang/mywust/core/service/graduate/GraduateScoreApiService.java
  36. 6
      mywust-core/src/main/java/cn/linghang/mywust/core/service/graduate/GraduateStudentInfoApiService.java
  37. 6
      mywust-core/src/main/java/cn/linghang/mywust/core/service/graduate/GraduateTrainingPlanApiService.java
  38. 33
      mywust-core/src/main/java/cn/linghang/mywust/core/service/undergraduate/CourseTableApiService.java
  39. 13
      mywust-core/src/main/java/cn/linghang/mywust/core/service/undergraduate/UndergradApiServiceBase.java
  40. 49
      mywust-core/src/main/java/cn/linghang/mywust/core/service/undergraduate/UndergradCourseTableApiService.java
  41. 23
      mywust-core/src/main/java/cn/linghang/mywust/core/service/undergraduate/UndergradScoreApiService.java
  42. 17
      mywust-core/src/main/java/cn/linghang/mywust/core/service/undergraduate/UndergradStudentInfoApiService.java
  43. 17
      mywust-core/src/main/java/cn/linghang/mywust/core/service/undergraduate/UndergradTrainingPlanApiService.java
  44. 2
      mywust-core/src/main/java/cn/linghang/mywust/core/util/BkjxUtil.java
  45. 25
      mywust-core/src/main/java/cn/linghang/mywust/core/util/JsoupUtil.java
  46. 16
      mywust-model/.flattened-pom.xml
  47. 14
      mywust-model/pom.xml
  48. 2
      mywust-model/src/main/java/cn/linghang/mywust/model/global/Score.java
  49. 16
      mywust-network-httpclient/.flattened-pom.xml
  50. 19
      mywust-network-httpclient/pom.xml
  51. 40
      mywust-network-okhttp/.flattened-pom.xml
  52. 53
      mywust-network-okhttp/pom.xml
  53. 6
      mywust-network-okhttp/src/main/java/cn/linghang/mywust/network/okhttp/RedirectInterceptor.java
  54. 123
      mywust-network-okhttp/src/main/java/cn/linghang/mywust/network/okhttp/SimpleOkhttpRequester.java
  55. 22
      mywust-network/.flattened-pom.xml
  56. 13
      mywust-network/pom.xml
  57. 90
      mywust-network/src/main/java/cn/linghang/mywust/network/RequestClientOption.java
  58. 23
      mywust-network/src/main/java/cn/linghang/mywust/network/Requester.java
  59. 66
      mywust-network/src/main/java/cn/linghang/mywust/network/entitys/HttpRequest.java
  60. 54
      mywust-network/src/main/java/cn/linghang/mywust/network/entitys/HttpResponse.java
  61. 6
      mywust-test/pom.xml
  62. 12
      mywust-test/src/test/java/ParseTest.java
  63. 28
      mywust-util/.flattened-pom.xml
  64. 6
      mywust-util/pom.xml
  65. 8
      mywust-util/src/main/java/cn/linghang/mywust/util/StringUtil.java
  66. 71
      pom.xml

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.linghang</groupId>
<artifactId>mywust</artifactId>
<version>0.0.1-fixed</version>
<packaging>pom</packaging>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

@ -51,7 +51,17 @@ JDK版本:1.8及以上,推荐JDK 11及以上
## 使用说明
> 待补充,先留个坑
### 在项目中引用mywust-core
目前该项目发布在Github Package上,如遇网络问题请自备工具解决
关于如何在自己的项目中引用:
- Maven项目请参阅:[使用 Apache Maven 注册表](https://docs.github.com/zh/packages/working-with-a-github-packages-registry/working-with-the-apache-maven-registry)
- Gradle项目请参阅:[使用 Gradle 注册表](https://docs.github.com/zh/packages/working-with-a-github-packages-registry/working-with-the-gradle-registry)
~~未来有可能会考虑自建Maven仓库~~
## 目录规范
@ -63,7 +73,12 @@ JDK版本:1.8及以上,推荐JDK 11及以上
## 使用到的开源库
> 待补充,先留个坑
- HTML解析:[jsoup](https://jsoup.org/license) (MIT License)
- Json解析:[jackson](https://github.com/FasterXML) (Apache License 2.0)
- 代码简化:[lombok](https://projectlombok.org/) (MIT License)
- 日志输出:[slf4j](https://www.slf4j.org/) (MIT License)
- 各种工具:[guava](https://guava.dev/) (Apache License 2.0)
- 编解码库:[apache commons codec](https://commons.apache.org/proper/commons-codec/) (Apache License 2.0)
## 开源协议

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.linghang</groupId>
<artifactId>mywust-core</artifactId>
<version>0.0.1-fixed</version>
<dependencies>
<dependency>
<groupId>cn.linghang</groupId>
<artifactId>mywust-network</artifactId>
<version>0.0.1-fixed</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>cn.linghang</groupId>
<artifactId>mywust-util</artifactId>
<version>0.0.1-fixed</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.15.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>cn.linghang</groupId>
<artifactId>mywust-model</artifactId>
<version>0.0.1-fixed</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.14.0-rc1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.0-rc1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

@ -5,7 +5,7 @@
<parent>
<artifactId>mywust</artifactId>
<groupId>cn.linghang</groupId>
<version>0.0.1-dev</version>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -14,45 +14,32 @@
<dependency>
<groupId>cn.linghang</groupId>
<artifactId>mywust-network</artifactId>
<version>0.0.1-dev</version>
<version>${project.parent.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>cn.linghang</groupId>
<artifactId>mywust-util</artifactId>
<version>0.0.1-dev</version>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>net.sourceforge.jregex</groupId>
<artifactId>jregex</artifactId>
<version>1.2_01</version>
<version>${slf4j.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.15.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/oro/oro -->
<dependency>
<groupId>oro</groupId>
<artifactId>oro</artifactId>
<version>2.0.8</version>
<version>${jsoup.version}</version>
</dependency>
<dependency>
<groupId>cn.linghang</groupId>
<artifactId>mywust-model</artifactId>
<version>0.0.1-dev</version>
<version>${project.parent.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
@ -70,8 +57,11 @@
</dependencies>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<slf4j.version>2.0.3</slf4j.version>
<jsoup.version>1.15.3</jsoup.version>
</properties>
</project>

@ -1,5 +0,0 @@
package cn.linghang.mywust.core;
public class Mywust {
}

@ -1,5 +0,0 @@
package cn.linghang.mywust.core;
public class MywustFactory {
}

@ -1,6 +1,6 @@
package cn.linghang.mywust.core.api;
public class Graduate {
public class GraduateUrls {
public static final String GRADUATE_CAPTCHA_API = "http://59.68.177.189/pyxx/PageTemplate/NsoftPage/yzm/createyzm.aspx";
public static final String GRADUATE_LOGIN_API = "http://59.68.177.189/pyxx/login.aspx";

@ -1,6 +1,6 @@
package cn.linghang.mywust.core.api;
public class Library {
public class LibraryUrls {
public static final String LIBRARY_SESSION_COOKIE_API = "https://libsys.wust.edu.cn/meta-local/opac/cas/rosetta?ticket=%s";
public static final String LIBRARY_INDEX_URL = "https://libsys.wust.edu.cn/meta-local/opac/cas/rosetta";

@ -1,6 +1,6 @@
package cn.linghang.mywust.core.api;
public class PhysicsSystem {
public class PhysicsSystemUrls {
public static final String PHYSICS_LOGIN_INDEX = "http://wlsy.wust.edu.cn/Page/BI/BI000.aspx";
public static final String PHYSICS_LOGIN_COOKIES_API = "http://wlsy.wust.edu.cn/Page/BI/BI000.aspx";

@ -7,7 +7,7 @@ import lombok.Getter;
* <p>其实是本科生用的教务处系统</p>
*/
@Getter
public class Bkjx {
public class UndergradUrls {
public static final String BKJX_SESSION_COOKIE_API = "http://bkjx.wust.edu.cn/jsxsd/sso.jsp?ticket=%s";
public static final String BKJX_TEST_API = "http://bkjx.wust.edu.cn/jsxsd/framework/blankPage.jsp";

@ -1,6 +1,6 @@
package cn.linghang.mywust.core.api;
public class UnionAuth {
public class UnionAuthUrls {
/**
* 统一认证登录验证的api地址请求之后可进一步获取service ticket来对具体的服务进行登录获取Cookies
*/

@ -12,6 +12,14 @@ public class ApiException extends BasicException {
this.code = code;
}
public static ApiException create(Code code, String message) {
return new ApiException(code, message);
}
public static ApiException create(Code code) {
return new ApiException(code);
}
public Code getCode() {
return code;
}
@ -40,6 +48,11 @@ public class ApiException extends BasicException {
*/
NETWORK_EXCEPTION(-2, "网络错误..."),
/**
* 输入参数错误
*/
PARAM_WRONG_EXCEPTION(-3, "输入参数错误..."),
// --------------------------------
// 统一认证的异常(本科生、图书馆)
@ -58,6 +71,33 @@ public class ApiException extends BasicException {
*/
UNI_LOGIN_USER_BANNED(100102, "统一认证登录: 用户被封禁"),
/**
* 用户账号停用
*/
UNI_LOGIN_USER_DISABLED(100103, "统一认证登录: 用户已停用"),
/**
* 用户账号需要更改密码
*/
UNI_LOGIN_NEED_CHANGE_PASSWORD(100104, "统一认证登录: 用户账号密码需要修改"),
// 下面的几个几乎不会遇到,但是从官网源码来看是有可能的
/**
* 用户账号不唯一
*/
UNI_LOGIN_USER_NOT_ONLY(100105, "统一认证登录: 用户不唯一,请联系学校处理"),
/**
* 用户未注册
*/
UNI_LOGIN_NO_REGISTER(100106, "统一认证登录: 用户未注册"),
/**
* 用户账号设置了TFA验证不能直接用密码账号登录
*/
UNI_LOGIN_NEED_TFA(100107, "统一认证登录: 用户账号需要TFA二步验证"),
// --------------------------------
// 共有异常码:cookie无效
@ -69,6 +109,11 @@ public class ApiException extends BasicException {
// --------------------------------
// 本科生API异常代码
/**
*
*/
BKJX_LEGACY_LOGIN_PASSWORD_WRONG(110101, "本科教学系统-旧版登录方式:密码错误"),
/**
* 需要评教
*/

@ -2,7 +2,7 @@ package cn.linghang.mywust.core.parser.graduate;
import cn.linghang.mywust.core.exception.ParseException;
import cn.linghang.mywust.core.parser.Parser;
import cn.linghang.mywust.model.global.ExamInfo;
import cn.linghang.mywust.model.global.Score;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
@ -11,13 +11,13 @@ import org.jsoup.select.Elements;
import java.util.ArrayList;
import java.util.List;
public class GraduateExamInfoParser implements Parser<List<ExamInfo>> {
public class GraduateScoreParser implements Parser<List<Score>> {
@Override
public List<ExamInfo> parse(String html) throws ParseException {
public List<Score> parse(String html) throws ParseException {
Document document = Jsoup.parse(html);
Elements scoreElements = document.getElementsByClass("GridViewRowStyle");
List<ExamInfo> examInfos = new ArrayList<>(scoreElements.size());
List<Score> scores = new ArrayList<>(scoreElements.size());
for (Element scoreElement : scoreElements) {
Elements infoGirds = scoreElement.getElementsByTag("td");
@ -27,16 +27,16 @@ public class GraduateExamInfoParser implements Parser<List<ExamInfo>> {
scoreElements.removeIf(element -> element.hasClass("pagestopr"));
ExamInfo examInfo = new ExamInfo();
Score score = new Score();
examInfo.setCourseName(infoGirds.get(0).text());
examInfo.setCredit(infoGirds.get(1).text());
examInfo.setTerm(infoGirds.get(2).text());
examInfo.setScore(infoGirds.get(3).text());
score.setCourseName(infoGirds.get(0).text());
score.setCredit(infoGirds.get(1).text());
score.setTerm(infoGirds.get(2).text());
score.setScore(infoGirds.get(3).text());
examInfos.add(examInfo);
scores.add(score);
}
return examInfos;
return scores;
}
}

@ -1,72 +0,0 @@
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.global.ExamInfo;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
public class ExamInfoParser implements Parser<List<ExamInfo>> {
private static final Logger log = LoggerFactory.getLogger(ExamInfoParser.class);
@Override
public List<ExamInfo> parse(String html) throws ParseException {
Elements rows = Jsoup.parse(html).selectXpath(ExamInfoXpath.EXAM_INFO_ROWS_XPATH);
if (rows.isEmpty()) {
throw new ParseException(html);
}
List<ExamInfo> examInfos = new ArrayList<>(rows.size());
try {
for (Element row : rows) {
// 提取出当前行的所有格子
Elements girds = row.getElementsByTag("td");
// 如果这行格子数少于6个,即到了“成绩”的那个格子就没了,那就没啥意义了,直接跳过,不理了
if (girds.size() < 6) {
continue;
}
ExamInfo examInfo = new ExamInfo();
// 这段看着震撼,但其实很丑
examInfo.setId(JsoupUtil.getElementContext(girds.get(0)));
examInfo.setTerm(JsoupUtil.getElementContext(girds.get(1)));
examInfo.setCourseNumber(JsoupUtil.getElementContext(girds.get(2)));
examInfo.setCourseName(JsoupUtil.getElementContext(girds.get(3)));
examInfo.setGroupName(JsoupUtil.getElementContext(girds.get(4)));
examInfo.setScore(JsoupUtil.getElementContext(girds.get(5)));
examInfo.setFlag(JsoupUtil.getElementContext(girds.get(6)));
examInfo.setCredit(JsoupUtil.getElementContext(girds.get(7)));
examInfo.setCourseHours(JsoupUtil.getElementContext(girds.get(8)));
examInfo.setGradePoint(JsoupUtil.getElementContext(girds.get(9)));
examInfo.setEvaluateMethod(JsoupUtil.getElementContext(girds.get(11)));
examInfo.setKind(JsoupUtil.getElementContext(girds.get(12)));
examInfo.setCourseKind(JsoupUtil.getElementContext(girds.get(13)));
examInfos.add(examInfo);
}
} catch (Exception e) {
log.warn("解析成绩页面时发生错误:{}", e.getMessage());
log.warn("终止解析,返回已解析数据");
}
return examInfos;
}
}
final class ExamInfoXpath {
public static final String EXAM_INFO_ROWS_XPATH = "//*[@id=\"dataList\"]/tbody/tr";
}

@ -15,8 +15,8 @@ import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class CourseTableParser implements Parser<List<Course>> {
private static final Logger log = LoggerFactory.getLogger(CourseTableParser.class);
public class UndergradCourseTableParser implements Parser<List<Course>> {
private static final Logger log = LoggerFactory.getLogger(UndergradCourseTableParser.class);
private static final String COURSE_SPLIT_STR = "---------------------";

@ -0,0 +1,72 @@
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.global.Score;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
public class UndergradScoreParser implements Parser<List<Score>> {
private static final Logger log = LoggerFactory.getLogger(UndergradScoreParser.class);
@Override
public List<Score> parse(String html) throws ParseException {
Elements rows = Jsoup.parse(html).selectXpath(UndergradScoreXpath.EXAM_INFO_ROWS_XPATH);
if (rows.isEmpty()) {
throw new ParseException(html);
}
List<Score> scores = new ArrayList<>(rows.size());
try {
for (Element row : rows) {
// 提取出当前行的所有格子
Elements girds = row.getElementsByTag("td");
// 如果这行格子数少于6个,即到了“成绩”的那个格子就没了,那就没啥意义了,直接跳过,不理了
if (girds.size() < 6) {
continue;
}
Score score = new Score();
// 这段看着震撼,但其实很丑
score.setId(JsoupUtil.getElementContext(girds.get(0)));
score.setTerm(JsoupUtil.getElementContext(girds.get(1)));
score.setCourseNumber(JsoupUtil.getElementContext(girds.get(2)));
score.setCourseName(JsoupUtil.getElementContext(girds.get(3)));
score.setGroupName(JsoupUtil.getElementContext(girds.get(4)));
score.setScore(JsoupUtil.getElementContext(girds.get(5)));
score.setFlag(JsoupUtil.getElementContext(girds.get(6)));
score.setCredit(JsoupUtil.getElementContext(girds.get(7)));
score.setCourseHours(JsoupUtil.getElementContext(girds.get(8)));
score.setGradePoint(JsoupUtil.getElementContext(girds.get(9)));
score.setEvaluateMethod(JsoupUtil.getElementContext(girds.get(11)));
score.setKind(JsoupUtil.getElementContext(girds.get(12)));
score.setCourseKind(JsoupUtil.getElementContext(girds.get(13)));
scores.add(score);
}
} catch (Exception e) {
log.warn("解析成绩页面时发生错误:{}", e.getMessage());
log.warn("终止解析,返回已解析数据");
}
return scores;
}
}
final class UndergradScoreXpath {
public static final String EXAM_INFO_ROWS_XPATH = "//*[@id=\"dataList\"]/tbody/tr";
}

@ -2,13 +2,14 @@ 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.global.StudentInfo;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
public class StudentInfoPageParser implements Parser<StudentInfo> {
public class UndergradStudentInfoPageParser implements Parser<StudentInfo> {
public StudentInfo parse(String html) throws ParseException {
Document page = Jsoup.parse(html);
@ -18,34 +19,34 @@ public class StudentInfoPageParser implements Parser<StudentInfo> {
}
Elements studentElements = table.selectXpath(StudentInfoXpath.STUDENT_NUMBER);
String studentNumber = studentElements.isEmpty() ? null : studentElements.get(0).text().replace("学号:", "");
String studentNumber = JsoupUtil.getElementText(studentElements).replace("学号:", "");
Elements collegeElements = table.selectXpath(StudentInfoXpath.COLLEGE);
String college = collegeElements.isEmpty() ? null : collegeElements.get(0).text().replace("院系:", "");
String college = JsoupUtil.getElementText(collegeElements).replace("院系:", "");
Elements majorElements = table.selectXpath(StudentInfoXpath.MAJOR);
String major = majorElements.isEmpty() ? null : majorElements.get(0).text().replace("专业:", "");
String major = JsoupUtil.getElementText(majorElements).replace("专业:", "");
Elements classElements = table.selectXpath(StudentInfoXpath.CLASS);
String clazz = classElements.isEmpty() ? null : classElements.get(0).text().replace("班级:", "");
String clazz = JsoupUtil.getElementText(classElements).replace("班级:", "");
Elements nameElements = table.selectXpath(StudentInfoXpath.NAME);
String name = nameElements.isEmpty() ? null : nameElements.get(0).text();
String name = JsoupUtil.getElementText(nameElements);
Elements sexElements = table.selectXpath(StudentInfoXpath.SEX);
String sex = sexElements.isEmpty() ? null : sexElements.get(0).text();
String sex = JsoupUtil.getElementText(sexElements);
Elements birthdayElements = table.selectXpath(StudentInfoXpath.BIRTHDAY);
String birthday = birthdayElements.isEmpty() ? null : birthdayElements.get(0).text();
String birthday = JsoupUtil.getElementText(birthdayElements);
Elements hometownElements = table.selectXpath(StudentInfoXpath.HOMETOWN);
String hometown = hometownElements.isEmpty() ? null : hometownElements.get(0).text();
String hometown = JsoupUtil.getElementText(hometownElements);
Elements nationalityElements = table.selectXpath(StudentInfoXpath.NATIONALITY);
String nationality = nationalityElements.isEmpty() ? null : nationalityElements.get(0).text();
String nationality = JsoupUtil.getElementText(nationalityElements);
Elements idNumberElements = table.selectXpath(StudentInfoXpath.ID_NUMBER);
String idNumber = idNumberElements.isEmpty() ? null : idNumberElements.get(0).text();
String idNumber = JsoupUtil.getElementText(idNumberElements);
return StudentInfo.builder()
.studentNumber(studentNumber)

@ -5,7 +5,7 @@ import cn.linghang.mywust.core.parser.Parser;
import org.jsoup.Jsoup;
import org.jsoup.select.Elements;
public class TrainingPlanPageParser implements Parser<String> {
public class UndergradTrainingPlanPageParser implements Parser<String> {
@Override
public String parse(String html) throws ParseException {

@ -3,6 +3,7 @@ package cn.linghang.mywust.core.request;
import cn.linghang.mywust.network.entitys.HttpRequest;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Map;
public class RequestFactory {
@ -29,6 +30,23 @@ public class RequestFactory {
.addHeaders(DEFAULT_HTTP_REQUEST.getHeaders());
}
public static HttpRequest makeHttpRequest(String url, String data) {
return makeStringDataHttpRequest(url, data, null);
}
public static HttpRequest makeHttpRequest(String url, String data, String cookies, Map<String, String> additionalHeaders) {
return makeStringDataHttpRequest(url, data, cookies).addHeaders(additionalHeaders);
}
public static HttpRequest makeStringDataHttpRequest(String url, String stringData, String cookies) {
return HttpRequest.builder()
.url(makeUrl(url))
.data(stringData.getBytes(StandardCharsets.UTF_8))
.cookies(cookies)
.build()
.addHeaders(DEFAULT_HTTP_REQUEST.getHeaders());
}
public static URL makeUrl(String url) {
try {
return new URL(url);

@ -1,6 +1,6 @@
package cn.linghang.mywust.core.request.auth;
import cn.linghang.mywust.core.api.UnionAuth;
import cn.linghang.mywust.core.api.UnionAuthUrls;
import cn.linghang.mywust.core.request.RequestFactory;
import cn.linghang.mywust.network.entitys.HttpRequest;
import cn.linghang.mywust.util.StringUtil;
@ -19,7 +19,7 @@ public class UnionAuthRequestFactory extends RequestFactory {
String queryString = StringUtil.generateQueryString(requestForm);
return makeHttpRequest(UnionAuth.UNION_AUTH_API, queryString.getBytes(StandardCharsets.UTF_8));
return makeHttpRequest(UnionAuthUrls.UNION_AUTH_API, queryString.getBytes(StandardCharsets.UTF_8));
}
public static HttpRequest loginTicketRequest(String redirectUrl, String service) {

@ -1,7 +1,7 @@
package cn.linghang.mywust.core.request.graduate;
import cn.linghang.mywust.captcha.SolvedImageCaptcha;
import cn.linghang.mywust.core.api.Graduate;
import cn.linghang.mywust.core.api.GraduateUrls;
import cn.linghang.mywust.core.request.RequestFactory;
import cn.linghang.mywust.network.entitys.HttpRequest;
import cn.linghang.mywust.util.StringUtil;
@ -12,7 +12,7 @@ import java.util.Map;
public class GraduateRequestFactory extends RequestFactory {
public static HttpRequest captchaRequest() {
return makeHttpRequest(Graduate.GRADUATE_CAPTCHA_API);
return makeHttpRequest(GraduateUrls.GRADUATE_CAPTCHA_API);
}
private static final Map<String,String> LOGIN_CONST_PARAMS = new HashMap<>(5);
@ -33,22 +33,22 @@ public class GraduateRequestFactory extends RequestFactory {
byte[] requestData = StringUtil.generateQueryString(params).getBytes(StandardCharsets.UTF_8);
return makeHttpRequest(Graduate.GRADUATE_LOGIN_API, requestData, captcha.getBindInfo());
return makeHttpRequest(GraduateUrls.GRADUATE_LOGIN_API, requestData, captcha.getBindInfo());
}
public static HttpRequest studentInfoRequest(String cookie) {
return makeHttpRequest(Graduate.GRADUATE_STUDENT_INFO_API, null, cookie);
return makeHttpRequest(GraduateUrls.GRADUATE_STUDENT_INFO_API, null, cookie);
}
public static HttpRequest courseTableRequest(String cookie) {
return makeHttpRequest(Graduate.GRADUATE_COURSE_TABLE_API, null, cookie);
return makeHttpRequest(GraduateUrls.GRADUATE_COURSE_TABLE_API, null, cookie);
}
public static HttpRequest examScoreInfoRequest(String cookie) {
return makeHttpRequest(Graduate.GRADUATE_SCORE_API, null, cookie);
return makeHttpRequest(GraduateUrls.GRADUATE_SCORE_API, null, cookie);
}
public static HttpRequest trainingPlanPageRequest(String cookie) {
return makeHttpRequest(Graduate.GRADUATE_TRAINING_PLAN_PAGE_API, null, cookie);
return makeHttpRequest(GraduateUrls.GRADUATE_TRAINING_PLAN_PAGE_API, null, cookie);
}
}

@ -1,27 +1,31 @@
package cn.linghang.mywust.core.request.library;
import cn.linghang.mywust.core.api.Library;
import cn.linghang.mywust.core.api.LibraryUrls;
import cn.linghang.mywust.core.request.RequestFactory;
import cn.linghang.mywust.network.entitys.HttpRequest;
public class LibraryRequestFactory extends RequestFactory {
public static HttpRequest sessionCookieRequest(String serviceTicket) {
return makeHttpRequest(String.format(Library.LIBRARY_SESSION_COOKIE_API, serviceTicket));
return makeHttpRequest(String.format(LibraryUrls.LIBRARY_SESSION_COOKIE_API, serviceTicket));
}
public static HttpRequest indexRequest() {
return makeHttpRequest(Library.LIBRARY_INDEX_URL);
return makeHttpRequest(LibraryUrls.LIBRARY_INDEX_URL);
}
public static HttpRequest currentLoanRequest(String cookie) {
return makeHttpRequest(Library.LIBRARY_CURRENT_LOAN_API, null, cookie);
return makeHttpRequest(LibraryUrls.LIBRARY_CURRENT_LOAN_API, null, cookie);
}
public static HttpRequest loanHistoryRequest(String cookie) {
return makeHttpRequest(Library.LIBRARY_LOAN_HISTORY_API, null, cookie);
return makeHttpRequest(LibraryUrls.LIBRARY_LOAN_HISTORY_API, null, cookie);
}
public static HttpRequest overdueSoonRequest(String cookie) {
return makeHttpRequest(Library.LIBRARY_OVERDUE_SOON_API, null, cookie);
return makeHttpRequest(LibraryUrls.LIBRARY_OVERDUE_SOON_API, null, cookie);
}
public static HttpRequest bookInfoRequest(String cookie) {
return makeHttpRequest(LibraryUrls.LIBRARY_OVERDUE_SOON_API, null, cookie);
}
}

@ -1,6 +1,6 @@
package cn.linghang.mywust.core.request.physics;
import cn.linghang.mywust.core.api.PhysicsSystem;
import cn.linghang.mywust.core.api.PhysicsSystemUrls;
import cn.linghang.mywust.core.request.RequestFactory;
import cn.linghang.mywust.network.entitys.HttpRequest;
import cn.linghang.mywust.util.StringUtil;
@ -11,16 +11,16 @@ import java.util.Map;
public class PhysicsSystemRequestFactory extends RequestFactory {
public static HttpRequest loginIndexRequest() {
return makeHttpRequest(PhysicsSystem.PHYSICS_LOGIN_INDEX);
return makeHttpRequest(PhysicsSystemUrls.PHYSICS_LOGIN_INDEX);
}
public static HttpRequest loginCookiesRequest(String username, String password, String cookies) {
byte[] queryData = StringUtil.generateQueryString(makeLoginQueryParam(username, password)).getBytes(StandardCharsets.UTF_8);
return makeHttpRequest(PhysicsSystem.PHYSICS_LOGIN_COOKIES_API, queryData, cookies);
return makeHttpRequest(PhysicsSystemUrls.PHYSICS_LOGIN_COOKIES_API, queryData, cookies);
}
public static HttpRequest mainIndexRequest(String cookies) {
return makeHttpRequest(PhysicsSystem.PHYSICS_MAIN_INDEX_URL, null, cookies);
return makeHttpRequest(PhysicsSystemUrls.PHYSICS_MAIN_INDEX_URL, null, cookies);
}
public static HttpRequest physicsSystemIndexRequest(String redirect, String cookies) {
@ -38,11 +38,11 @@ public class PhysicsSystemRequestFactory extends RequestFactory {
indexParam.put("__ASYNCPOST", "true");
byte[] fromData = StringUtil.generateQueryString(indexParam).getBytes(StandardCharsets.UTF_8);
return makeHttpRequest(PhysicsSystem.PHYSICS_COURSE_INDEX_URL, fromData, cookies);
return makeHttpRequest(PhysicsSystemUrls.PHYSICS_COURSE_INDEX_URL, fromData, cookies);
}
public static HttpRequest physicsCourseRequest(String cookies) {
return makeHttpRequest(PhysicsSystem.PHYSICS_COURSE_API, null, cookies);
return makeHttpRequest(PhysicsSystemUrls.PHYSICS_COURSE_API, null, cookies);
}
private static Map<String, String> makeLoginQueryParam(String username, String password) {

@ -1,22 +1,23 @@
package cn.linghang.mywust.core.request.undergrade;
import cn.linghang.mywust.core.api.Bkjx;
import cn.linghang.mywust.core.api.UndergradUrls;
import cn.linghang.mywust.core.request.RequestFactory;
import cn.linghang.mywust.network.entitys.FormBodyBuilder;
import cn.linghang.mywust.network.entitys.HttpRequest;
import cn.linghang.mywust.util.StringUtil;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
public class BkjxRequestFactory extends RequestFactory {
public static HttpRequest sessionCookieRequest(String serviceTicket) {
return makeHttpRequest(String.format(Bkjx.BKJX_SESSION_COOKIE_API, serviceTicket));
return makeHttpRequest(String.format(UndergradUrls.BKJX_SESSION_COOKIE_API, serviceTicket));
}
public static HttpRequest studentInfoRequest(String cookies) {
return makeHttpRequest(Bkjx.BKJX_STUDENT_INFO_API, null, cookies);
return makeHttpRequest(UndergradUrls.BKJX_STUDENT_INFO_API, null, cookies);
}
public static HttpRequest examScoreInfoRequest(String cookies, String time, String courseKind, String courseName) {
@ -34,11 +35,11 @@ public class BkjxRequestFactory extends RequestFactory {
formBodyBuilder.add("xsfs", "all");
byte[] postData = formBodyBuilder.buildAndToString().getBytes(StandardCharsets.UTF_8);
return makeHttpRequest(Bkjx.BKJX_EXAM_INFO_API, postData, cookies);
return makeHttpRequest(UndergradUrls.BKJX_EXAM_INFO_API, postData, cookies);
}
public static HttpRequest trainingPlanPageRequest(String cookies) {
return makeHttpRequest(Bkjx.BKJX_SCHEME_API, null, cookies);
return makeHttpRequest(UndergradUrls.BKJX_SCHEME_API, null, cookies);
}
public static HttpRequest courseTablePageRequest(String term, String cookies) {
@ -48,6 +49,28 @@ public class BkjxRequestFactory extends RequestFactory {
byte[] queryData = StringUtil.generateQueryString(params).getBytes(StandardCharsets.UTF_8);
return makeHttpRequest(Bkjx.BKJX_COURSE_TABLE_API, queryData, cookies);
return makeHttpRequest(UndergradUrls.BKJX_COURSE_TABLE_API, queryData, cookies);
}
public static class Legacy {
public static HttpRequest dataStringRequest() {
return makeHttpRequest(UndergradUrls.Legacy.BKJX_DATA_STRING_API);
}
public static HttpRequest ticketRedirectRequest(String encode) {
Map<String, String> queryParams = new HashMap<>();
queryParams.put("userAccount", "");
queryParams.put("userPassword", "");
queryParams.put("encoded", encode);
String queryString = StringUtil.generateQueryString(queryParams);
Map<String, String> extendHeaders = new HashMap<>();
extendHeaders.put("Referer", "http://bkjx.wust.edu.cn/");
extendHeaders.put("Origin", "http://bkjx.wust.edu.cn");
return makeHttpRequest(UndergradUrls.Legacy.BKJX_SESSION_COOKIE_API, queryString.getBytes(StandardCharsets.UTF_8))
.addHeaders(extendHeaders);
}
}
}

@ -2,8 +2,9 @@ package cn.linghang.mywust.core.service.auth;
import cn.linghang.mywust.captcha.SolvedImageCaptcha;
import cn.linghang.mywust.captcha.UnsolvedImageCaptcha;
import cn.linghang.mywust.core.api.GraduateUrls;
import cn.linghang.mywust.core.exception.ApiException;
import cn.linghang.mywust.core.exception.BasicException;
import cn.linghang.mywust.core.request.RequestFactory;
import cn.linghang.mywust.core.request.graduate.GraduateRequestFactory;
import cn.linghang.mywust.core.service.captcha.solver.CaptchaSolver;
import cn.linghang.mywust.network.RequestClientOption;
@ -28,7 +29,7 @@ public class GraduateLogin {
this.captchaSolver = captchaSolver;
}
public String getLoginCookie(String username, String password, RequestClientOption option) throws IOException, BasicException {
public String getLoginCookie(String username, String password, RequestClientOption option) throws IOException, ApiException {
// 请求获取验证码
HttpRequest captchaImageRequest = GraduateRequestFactory.captchaRequest();
HttpResponse captchaImageResponse = requester.get(captchaImageRequest, option);
@ -54,6 +55,23 @@ public class GraduateLogin {
// 使用当初通过验证码得到的cookie来作为登录cookie,至于是否真正可行待验证
return captchaImageResponse.getCookies();
}
public void checkCookies(String cookie, RequestClientOption option) throws ApiException, IOException {
HttpRequest request = RequestFactory.makeHttpRequest(GraduateUrls.GRADUATE_INDEX_TEST_API, null, cookie);
HttpResponse response = requester.get(request, option);
this.checkResponse(response);
}
public void checkResponse(HttpResponse response) throws ApiException {
// 检查响应是否正确
if (response.getBody() == null ||
response.getStatusCode() != HttpResponse.HTTP_OK ||
new String(response.getBody()).contains("name=\"_ctl0:txtpassword\"")) {
throw new ApiException(ApiException.Code.COOKIE_INVALID);
}
}
}
class ImageUtil {
@ -87,7 +105,7 @@ class ImageUtil {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "png", outputStream);
return byteArrayInputStream.readAllBytes();
return outputStream.toByteArray();
} catch (Exception e) {
throw new ApiException(ApiException.Code.INTERNAL_EXCEPTION);
}

@ -1,61 +0,0 @@
package cn.linghang.mywust.core.service.auth;
import cn.linghang.mywust.core.api.Bkjx;
import cn.linghang.mywust.core.api.UnionAuth;
import cn.linghang.mywust.core.exception.BasicException;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
public class JwcLogin {
private static final Logger log = LoggerFactory.getLogger(JwcLogin.class);
private final Requester requester;
private final UnionLogin unionLogin;
public JwcLogin(Requester requester) {
this.requester = requester;
this.unionLogin = new UnionLogin(requester);
}
public String getLoginCookie(String username, String password, RequestClientOption requestOption) throws IOException, BasicException {
// 获取service ticket以进行进一步的登录
String serviceTicket = unionLogin.getServiceTicket(username, password, UnionAuth.Service.BKJX_SSO_SERVICE, requestOption);
// 获取登录cookie(session)
HttpRequest sessionRequest = BkjxRequestFactory.sessionCookieRequest(serviceTicket);
HttpResponse sessionResponse = requester.get(sessionRequest, requestOption);
String cookies = sessionResponse.getCookies();
if (roughCheckCookie(cookies)) {
throw new BasicException();
}
return cookies;
}
private boolean roughCheckCookie(String cookies) {
return cookies == null || !cookies.contains("JSESSIONID") || !cookies.contains("SERVERID");
}
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>")
.length();
public boolean checkCookies(String cookies, RequestClientOption option) throws IOException {
HttpRequest testRequest = BkjxRequestFactory.makeHttpRequest(Bkjx.BKJX_TEST_API, null, cookies);
HttpResponse testResponse = requester.get(testRequest, option);
// 判断响应长度是否为这么多个字,登录跳转响应长度
// 这种办法虽然在极端情况下可能会出错(而且还挺蠢的),但是是最快的办法中比较准确的了
return testResponse.getBody().length != COOKIES_ERROR_RESPONSE_LENGTH;
}
}

@ -1,8 +1,8 @@
package cn.linghang.mywust.core.service.auth;
import cn.linghang.mywust.core.api.Library;
import cn.linghang.mywust.core.api.UnionAuth;
import cn.linghang.mywust.core.exception.BasicException;
import cn.linghang.mywust.core.api.LibraryUrls;
import cn.linghang.mywust.core.api.UnionAuthUrls;
import cn.linghang.mywust.core.exception.ApiException;
import cn.linghang.mywust.core.request.library.LibraryRequestFactory;
import cn.linghang.mywust.network.RequestClientOption;
import cn.linghang.mywust.network.Requester;
@ -21,9 +21,9 @@ public class LibraryLogin {
this.unionLogin = new UnionLogin(requester);
}
public String getLibraryLoginCookie(String username, String password, RequestClientOption requestOption) throws BasicException, IOException {
public String getLoginCookie(String username, String password, RequestClientOption requestOption) throws ApiException, IOException {
// 获取service ticket以进行进一步的登录
String serviceTicket = unionLogin.getServiceTicket(username, password, UnionAuth.Service.LIBRARY_SSO_SERVICE, requestOption);
String serviceTicket = unionLogin.getServiceTicket(username, password, UnionAuthUrls.Service.LIBRARY_SSO_SERVICE, requestOption);
// 获取登录cookie(session)
HttpRequest sessionRequest = LibraryRequestFactory.sessionCookieRequest(serviceTicket);
@ -40,7 +40,7 @@ public class LibraryLogin {
public boolean checkCookie(String cookies) throws IOException {
RequestClientOption option = RequestClientOption.DEFAULT_OPTION;
HttpRequest testRequest = LibraryRequestFactory.makeHttpRequest(Library.LIBRARY_COOKIE_TEST_URL, null, cookies);
HttpRequest testRequest = LibraryRequestFactory.makeHttpRequest(LibraryUrls.LIBRARY_COOKIE_TEST_URL, null, cookies);
HttpResponse testResponse = requester.get(testRequest, option);
// 响应居然是JSON,好评!

@ -2,6 +2,7 @@ package cn.linghang.mywust.core.service.auth;
import cn.linghang.mywust.core.exception.ApiException;
import cn.linghang.mywust.core.exception.BasicException;
import cn.linghang.mywust.core.exception.ParseException;
import cn.linghang.mywust.core.parser.physics.PhysicsIndexPageParser;
import cn.linghang.mywust.core.request.physics.PhysicsSystemRequestFactory;
import cn.linghang.mywust.network.RequestClientOption;
@ -24,7 +25,7 @@ public class PhysicsLogin {
this.requester = requester;
}
public String getLoginCookie(String username, String password, RequestClientOption requestClientOption) throws IOException, BasicException {
public String getLoginCookie(String username, String password, RequestClientOption requestClientOption) throws IOException, ApiException, ParseException {
// 直接登录,ASP.NET_SessionId其实在这步就能获取到,不需要再请求一遍首页获取
HttpRequest loginCookieRequest = PhysicsSystemRequestFactory.loginCookiesRequest(username, password, null);
HttpResponse loginCookieResponse = requester.post(loginCookieRequest, requestClientOption);

@ -0,0 +1,116 @@
package cn.linghang.mywust.core.service.auth;
import cn.linghang.mywust.core.api.UndergradUrls;
import cn.linghang.mywust.core.api.UnionAuthUrls;
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 cn.linghang.mywust.util.PasswordEncoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
public class UndergraduateLogin {
private static final Logger log = LoggerFactory.getLogger(UndergraduateLogin.class);
private final Requester requester;
private final UnionLogin unionLogin;
public UndergraduateLogin(Requester requester) {
this.requester = requester;
this.unionLogin = new UnionLogin(requester);
}
public String getLoginCookie(String username, String password, RequestClientOption requestOption) throws IOException, ApiException {
// 获取service ticket以进行进一步的登录
String serviceTicket = unionLogin.getServiceTicket(username, password, UnionAuthUrls.Service.BKJX_SSO_SERVICE, requestOption);
// 获取登录cookie(session)
HttpRequest sessionRequest = BkjxRequestFactory.sessionCookieRequest(serviceTicket);
HttpResponse sessionResponse = requester.get(sessionRequest, requestOption);
String cookies = sessionResponse.getCookies();
if (!roughCheckCookie(cookies)) {
log.error("[mywust]: Cookie粗查不通过:{}", cookies);
throw new ApiException(ApiException.Code.UNKNOWN_EXCEPTION, "登录获取的Cookie无效");
}
// 检查Cookie是否真正可用,同时请求一次任意接口使后续接口能够正确响应
// 拿到Cookie后调用的第一个接口会产生302/301跳转,需要再次调用才能正确响应
if (!checkCookies(cookies, requestOption)) {
log.warn("[mywust]: Cookie检查不通过:{}", cookies);
}
return cookies;
}
/**
* <p>旧版的登录方式既相当于直接登录<a href="http://bkjx.wust.edu.cn">bkjx系统</a>不建议使用</p>
* <p>注意这种登录方式的密码和新版可能是不一样的</p>
* <p>不过不论使用哪种登录方式获取到的cookie都是可用的</p>
*
* @return 获取到的Cookies
*/
@Deprecated
public String getLoginCookieLegacy(String username, String password, RequestClientOption requestOption) throws IOException, ApiException {
// 获取某段神秘的dataStr(反正官网代码是这么叫的)
HttpRequest dataStringRequest = BkjxRequestFactory.Legacy.dataStringRequest();
HttpResponse dataStringResponse = requester.post(dataStringRequest, requestOption);
if (dataStringResponse.getBody() == null) {
log.warn("[mywust]: 本科教学系统旧版登录方式:获取dataStr时发生错误");
throw new ApiException(ApiException.Code.UNKNOWN_EXCEPTION);
}
String dataString = new String(dataStringResponse.getBody());
// 获取登录ticket
String encoded = PasswordEncoder.legacyPassword(username, password, dataString);
HttpRequest ticketRequest = BkjxRequestFactory.Legacy.ticketRedirectRequest(encoded);
ticketRequest.setCookies(dataStringResponse.getCookies());
HttpResponse ticketResponse = requester.post(ticketRequest, requestOption);
if (ticketResponse.getBody() == null) {
log.warn("[mywust]: 本科教学系统旧版登录方式:获取登录ticket时发生错误");
throw new ApiException(ApiException.Code.UNKNOWN_EXCEPTION);
}
// 使用跳转得到的链接获取cookies
String sessionRedirect = ticketResponse.getHeaders().get("Location");
if (sessionRedirect == null) {
throw new ApiException(ApiException.Code.BKJX_LEGACY_LOGIN_PASSWORD_WRONG);
}
HttpRequest sessionRequest = BkjxRequestFactory.makeHttpRequest(sessionRedirect);
HttpResponse sessionResponse = requester.get(sessionRequest, requestOption);
String cookies = sessionResponse.getCookies();
if (checkCookies(cookies, requestOption)) {
log.warn("[mywust]: Cookie检查不通过:{}", cookies);
throw new ApiException(ApiException.Code.UNKNOWN_EXCEPTION, "登录获取的Cookie无效");
}
return cookies;
}
private boolean roughCheckCookie(String cookies) {
return cookies != null && cookies.contains("JSESSIONID") && cookies.contains("SERVERID");
}
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>")
.length();
public boolean checkCookies(String cookies, RequestClientOption option) throws IOException {
HttpRequest testRequest = BkjxRequestFactory.makeHttpRequest(UndergradUrls.BKJX_TEST_API, null, cookies);
HttpResponse testResponse = requester.get(testRequest, option);
// 判断响应长度是否为这么多个字,登录跳转响应长度
// 这种办法虽然在极端情况下可能会出错(而且还挺蠢的),但是是最快的办法中比较准确的了
return Math.abs(COOKIES_ERROR_RESPONSE_LENGTH - testResponse.getBody().length) > 8;
}
}

@ -1,14 +1,12 @@
package cn.linghang.mywust.core.service.auth;
import cn.linghang.mywust.core.exception.ApiException;
import cn.linghang.mywust.core.exception.BasicException;
import cn.linghang.mywust.core.request.auth.UnionAuthRequestFactory;
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 cn.linghang.mywust.util.PasswordEncoder;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -27,7 +25,7 @@ public class UnionLogin {
this.requester = requester;
}
public String getServiceTicket(String username, String password, String serviceUrl, RequestClientOption requestOption) throws IOException, BasicException {
public String getServiceTicket(String username, String password, String serviceUrl, RequestClientOption requestOption) throws IOException, ApiException {
String encodedPassword = PasswordEncoder.encodePassword(password);
// 获取ticket granting ticket(TGT),以获取ServiceTicket
@ -36,7 +34,7 @@ public class UnionLogin {
String redirectAddress = unionAuthResponse.getHeaders().get("Location");
if (redirectAddress == null) {
throw new ApiException(this.analyzeFailReason(unionAuthResponse.getBody()));
throw analyzeFailReason(unionAuthResponse.getBody());
}
// 获取服务ticket(service ticket,ST)
@ -53,21 +51,34 @@ public class UnionLogin {
return new String(serviceTicketResponseData);
}
private ApiException.Code analyzeFailReason(byte[] response) {
private ApiException analyzeFailReason(byte[] response) {
try {
JsonNode dataNode = new ObjectMapper().readTree(response).get("data");
switch (dataNode.get("code").asText()) {
String code = new ObjectMapper().readTree(response).get("data").get("code").asText();
switch (code) {
case "PASSERROR":
return ApiException.Code.UNI_LOGIN_PASSWORD_WRONG;
case "FALSE":
return new ApiException(ApiException.Code.UNI_LOGIN_PASSWORD_WRONG);
case "NOUSER":
return ApiException.Code.UNI_LOGIN_USER_NOT_EXISTS;
return new ApiException(ApiException.Code.UNI_LOGIN_USER_NOT_EXISTS);
case "USERLOCK":
return ApiException.Code.UNI_LOGIN_USER_BANNED;
return new ApiException(ApiException.Code.UNI_LOGIN_USER_BANNED);
case "USERDISABLED":
return new ApiException(ApiException.Code.UNI_LOGIN_USER_DISABLED);
case "ISMODIFYPASS":
return new ApiException(ApiException.Code.UNI_LOGIN_NEED_CHANGE_PASSWORD);
case "USERNOTONLY":
return new ApiException(ApiException.Code.UNI_LOGIN_USER_NOT_ONLY);
case "NOREGISTER":
return new ApiException(ApiException.Code.UNI_LOGIN_NO_REGISTER);
case "TWOVERIFY":
return new ApiException(ApiException.Code.UNI_LOGIN_NEED_TFA);
default:
return ApiException.Code.UNKNOWN_EXCEPTION;
log.warn("未知的原因:{}", code);
return new ApiException(ApiException.Code.UNKNOWN_EXCEPTION, "未知的错误原因:" + code);
}
} catch (Exception e) {
return ApiException.Code.UNKNOWN_EXCEPTION;
log.warn("分析失败原因出错:{}, 响应:{}", e, new String(response));
return new ApiException(ApiException.Code.UNKNOWN_EXCEPTION, e.toString());
}
}
}

@ -0,0 +1,43 @@
package cn.linghang.mywust.core.service.captcha.solver;
import cn.linghang.mywust.captcha.SolvedImageCaptcha;
import cn.linghang.mywust.captcha.UnsolvedImageCaptcha;
import cn.linghang.mywust.core.exception.ApiException;
import cn.linghang.mywust.core.request.RequestFactory;
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.Base64;
public class DdddOcrCaptchaSolver implements CaptchaSolver {
private final String baseUrl;
private final Requester requester;
public DdddOcrCaptchaSolver(String baseUrl, Requester requester) {
this.baseUrl = baseUrl;
this.requester = requester;
}
@Override
public SolvedImageCaptcha solve(UnsolvedImageCaptcha unsolvedImageCaptcha) throws ApiException {
try {
SolvedImageCaptcha solvedImageCaptcha = new SolvedImageCaptcha(unsolvedImageCaptcha);
String result = this.ocr(unsolvedImageCaptcha.getImage());
solvedImageCaptcha.setResult(result);
return solvedImageCaptcha;
} catch (IOException e) {
throw new ApiException(ApiException.Code.NETWORK_EXCEPTION);
}
}
private String ocr(byte[] data) throws IOException {
HttpRequest request = RequestFactory.makeHttpRequest(baseUrl + "/ocr/b64/text", Base64.getEncoder().encode(data));
HttpResponse response = requester.post(request);
return response.getStringBody();
}
}

@ -1,11 +0,0 @@
package cn.linghang.mywust.core.service.captcha.solver;
import cn.linghang.mywust.captcha.SolvedImageCaptcha;
import cn.linghang.mywust.captcha.UnsolvedImageCaptcha;
public class LocalPaddleOcrCaptchaSolver implements CaptchaSolver {
@Override
public SolvedImageCaptcha solve(UnsolvedImageCaptcha unsolvedImageCaptcha) {
return null;
}
}

@ -1,6 +1,6 @@
package cn.linghang.mywust.core.service.graduate;
import cn.linghang.mywust.core.api.Graduate;
import cn.linghang.mywust.core.api.GraduateUrls;
import cn.linghang.mywust.core.exception.ApiException;
import cn.linghang.mywust.core.request.RequestFactory;
import cn.linghang.mywust.network.RequestClientOption;
@ -10,10 +10,10 @@ import cn.linghang.mywust.network.entitys.HttpResponse;
import java.io.IOException;
public class GraduateApiService {
public class GraduateApiServiceBase {
protected final Requester requester;
public GraduateApiService(Requester requester) {
public GraduateApiServiceBase(Requester requester) {
this.requester = requester;
}
@ -28,7 +28,7 @@ public class GraduateApiService {
}
public void checkCookies(String cookie, RequestClientOption option) throws ApiException, IOException {
HttpRequest request = RequestFactory.makeHttpRequest(Graduate.GRADUATE_INDEX_TEST_API, null, cookie);
HttpRequest request = RequestFactory.makeHttpRequest(GraduateUrls.GRADUATE_INDEX_TEST_API, null, cookie);
HttpResponse response = requester.get(request, option);
this.checkResponse(response);

@ -9,14 +9,17 @@ import cn.linghang.mywust.network.entitys.HttpResponse;
import java.io.IOException;
public class GraduateCourseTableApiService extends GraduateApiService{
public class GraduateCourseTableApiService extends GraduateApiServiceBase {
public GraduateCourseTableApiService(Requester requester) {
super(requester);
}
public String getCourseTablePage(String cookie, RequestClientOption option) throws IOException, ApiException {
public String getPage(String cookie, RequestClientOption option) throws IOException, ApiException {
HttpRequest request = GraduateRequestFactory.courseTableRequest(cookie);
request.addHeaders("Referer", "http://59.68.177.189/pyxx/pygl/kbcx_xs.aspx");
request.addHeaders("Origin", "http://59.68.177.189");
HttpResponse response = requester.get(request, option);
super.checkResponse(response);

@ -9,14 +9,16 @@ import cn.linghang.mywust.network.entitys.HttpResponse;
import java.io.IOException;
public class GraduateScoreApiService extends GraduateApiService{
public class GraduateScoreApiService extends GraduateApiServiceBase {
public GraduateScoreApiService(Requester requester) {
super(requester);
}
public String getCourseTablePage(String cookie, RequestClientOption option) throws IOException, ApiException {
public String getPage(String cookie, RequestClientOption option) throws IOException, ApiException {
HttpRequest request = GraduateRequestFactory.examScoreInfoRequest(cookie);
request.addHeaders("Referer", "http://59.68.177.189/pyxx/leftmenu.aspx");
HttpResponse response = requester.get(request, option);
super.checkResponse(response);

@ -9,14 +9,16 @@ import cn.linghang.mywust.network.entitys.HttpResponse;
import java.io.IOException;
public class GraduateStudentInfoApiService extends GraduateApiService {
public class GraduateStudentInfoApiService extends GraduateApiServiceBase {
public GraduateStudentInfoApiService(Requester requester) {
super(requester);
}
public String getStudentInfoPage(String cookie, RequestClientOption option) throws ApiException, IOException {
public String getPage(String cookie, RequestClientOption option) throws ApiException, IOException {
HttpRequest request = GraduateRequestFactory.studentInfoRequest(cookie);
request.addHeaders("Referer", "http://59.68.177.189/pyxx/leftmenu.aspx");
HttpResponse response = requester.get(request, option);
super.checkResponse(response);

@ -9,14 +9,16 @@ import cn.linghang.mywust.network.entitys.HttpResponse;
import java.io.IOException;
public class GraduateTrainingPlanApiService extends GraduateApiService{
public class GraduateTrainingPlanApiService extends GraduateApiServiceBase {
public GraduateTrainingPlanApiService(Requester requester) {
super(requester);
}
public String getCourseTablePage(String cookie, RequestClientOption option) throws IOException, ApiException {
public String getPage(String cookie, RequestClientOption option) throws IOException, ApiException {
HttpRequest request = GraduateRequestFactory.trainingPlanPageRequest(cookie);
request.addHeaders("Referer", "http://59.68.177.189/pyxx/leftmenu.aspx");
HttpResponse response = requester.get(request, option);
super.checkResponse(response);

@ -1,33 +0,0 @@
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.CourseTableParser;
import cn.linghang.mywust.core.request.undergrade.BkjxRequestFactory;
import cn.linghang.mywust.model.global.Course;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.List;
public class CourseTableApiService extends UndergraduateApiService {
public CourseTableApiService(Requester requester) {
super(requester);
}
public String getCourseTablePage(String term, String cookies, RequestClientOption requestClientOption) throws IOException, ApiException {
HttpRequest request = BkjxRequestFactory.courseTablePageRequest(term, cookies);
HttpResponse response = requester.post(request, requestClientOption);
super.checkResponse(response);
return new String(response.getBody());
}
}

@ -1,9 +1,8 @@
package cn.linghang.mywust.core.service.undergraduate;
import cn.linghang.mywust.core.api.Bkjx;
import cn.linghang.mywust.core.api.UndergradUrls;
import cn.linghang.mywust.core.exception.ApiException;
import cn.linghang.mywust.core.request.RequestFactory;
import cn.linghang.mywust.core.request.undergrade.BkjxRequestFactory;
import cn.linghang.mywust.core.util.BkjxUtil;
import cn.linghang.mywust.network.RequestClientOption;
import cn.linghang.mywust.network.Requester;
@ -11,11 +10,12 @@ import cn.linghang.mywust.network.entitys.HttpRequest;
import cn.linghang.mywust.network.entitys.HttpResponse;
import java.io.IOException;
import java.util.Map;
public class UndergraduateApiService {
public abstract class UndergradApiServiceBase {
protected final Requester requester;
public UndergraduateApiService(Requester requester) {
public UndergradApiServiceBase(Requester requester) {
this.requester = requester;
}
@ -30,9 +30,12 @@ public class UndergraduateApiService {
}
public void checkCookies(String cookie, RequestClientOption option) throws ApiException, IOException {
HttpRequest request = RequestFactory.makeHttpRequest(Bkjx.BKJX_TEST_API, null, cookie);
HttpRequest request = RequestFactory.makeHttpRequest(UndergradUrls.BKJX_TEST_API, null, cookie);
HttpResponse response = requester.get(request, option);
this.checkResponse(response);
}
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;
}

@ -0,0 +1,49 @@
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 java.io.IOException;
import java.util.Map;
public class UndergradCourseTableApiService extends UndergradApiServiceBase {
private static final String[] NECESSARY_PARAMS = {"term"};
public UndergradCourseTableApiService(Requester requester) {
super(requester);
}
@Override
public String getPage(String cookie, Map<String, String> params, RequestClientOption option) throws ApiException, IOException {
for (String key : NECESSARY_PARAMS) {
if (params.get(key) == null) {
throw new ApiException(ApiException.Code.PARAM_WRONG_EXCEPTION);
}
}
return this.getPage(params.get(NECESSARY_PARAMS[0]), 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 cookies, RequestClientOption requestClientOption) throws IOException, ApiException {
HttpRequest request = BkjxRequestFactory.courseTablePageRequest(term, cookies);
HttpResponse response = requester.post(request, requestClientOption);
super.checkResponse(response);
return new String(response.getBody());
}
public String getPage(String term, String cookies) throws IOException, ApiException {
return getPage(term, cookies, null);
}
}

@ -1,27 +1,32 @@
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.ExamInfoParser;
import cn.linghang.mywust.core.request.undergrade.BkjxRequestFactory;
import cn.linghang.mywust.model.global.ExamInfo;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.List;
import java.util.Map;
public class ExamInfoApiService extends UndergraduateApiService {
public class UndergradScoreApiService extends UndergradApiServiceBase {
public ExamInfoApiService(Requester requester) {
public UndergradScoreApiService(Requester requester) {
super(requester);
}
public String getExamInfoPage(String cookies, RequestClientOption requestClientOption) throws IOException, ApiException {
@Override
public String getPage(String cookie, Map<String, String> params, RequestClientOption option) throws ApiException, IOException {
return this.getPage(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 cookies, RequestClientOption requestClientOption) throws IOException, ApiException {
HttpRequest request = BkjxRequestFactory.examScoreInfoRequest(cookies, "", "", "");
HttpResponse response = requester.post(request, requestClientOption);

@ -8,14 +8,25 @@ import cn.linghang.mywust.network.entitys.HttpRequest;
import cn.linghang.mywust.network.entitys.HttpResponse;
import java.io.IOException;
import java.util.Map;
public class StudentInfoApiService extends UndergraduateApiService {
public class UndergradStudentInfoApiService extends UndergradApiServiceBase {
public StudentInfoApiService(Requester requester) {
public UndergradStudentInfoApiService(Requester requester) {
super(requester);
}
public String getStudentInfoPage(String cookies, RequestClientOption requestOption) throws IOException, ApiException {
@Override
public String getPage(String cookie, Map<String, String> params, RequestClientOption option) throws ApiException, IOException {
return this.getPage(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 cookies, RequestClientOption requestOption) throws IOException, ApiException {
HttpRequest request = BkjxRequestFactory.studentInfoRequest(cookies);
HttpResponse response = requester.get(request, requestOption);

@ -8,14 +8,25 @@ import cn.linghang.mywust.network.entitys.HttpRequest;
import cn.linghang.mywust.network.entitys.HttpResponse;
import java.io.IOException;
import java.util.Map;
public class TrainingPlanApiService extends UndergraduateApiService {
public class UndergradTrainingPlanApiService extends UndergradApiServiceBase {
public TrainingPlanApiService(Requester requester) {
public UndergradTrainingPlanApiService(Requester requester) {
super(requester);
}
public String getTrainingPlanPage(String cookies, RequestClientOption requestClientOption) throws IOException, ApiException {
@Override
public String getPage(String cookie, Map<String, String> params, RequestClientOption option) throws ApiException, IOException {
return this.getPage(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 cookies, RequestClientOption requestClientOption) throws IOException, ApiException {
HttpRequest request = BkjxRequestFactory.trainingPlanPageRequest(cookies);
HttpResponse response = requester.get(request, requestClientOption);

@ -1,7 +1,5 @@
package cn.linghang.mywust.core.util;
import cn.linghang.mywust.core.api.Bkjx;
import java.nio.charset.StandardCharsets;
/**

@ -1,6 +1,7 @@
package cn.linghang.mywust.core.util;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
public class JsoupUtil {
/**
@ -61,4 +62,28 @@ public class JsoupUtil {
.ownText();
}
}
/**
* 取元素集合中第一个元素的文本当elements为null或数量为0时返回空字符串
*
* @param elements 元素集合
* @return 第一个元素的文本
*/
public static String getElementText(Elements elements) {
return getElementText(elements, 0);
}
/**
* 取元素集合中指定索引元素的文本当elements为null时或index超出集合的大小时返回空字符串
*
* @param elements 元素集合
* @return 对应索引元素的文本
*/
public static String getElementText(Elements elements, int index) {
if (elements == null) {
return "";
} else {
return index >= elements.size() ? "" : elements.get(index).text();
}
}
}

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.linghang</groupId>
<artifactId>mywust-model</artifactId>
<version>0.0.1-fixed</version>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

@ -5,23 +5,15 @@
<parent>
<artifactId>mywust</artifactId>
<groupId>cn.linghang</groupId>
<version>0.0.1-dev</version>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mywust-model</artifactId>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>compile</scope>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

@ -20,7 +20,7 @@ import lombok.NoArgsConstructor;
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ExamInfo {
public class Score {
/**
* 序号id在某些场景下可能会有用
*/

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.linghang</groupId>
<artifactId>mywust-network-httpclient</artifactId>
<version>0.0.1-fixed</version>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

@ -3,15 +3,24 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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>
<groupId>cn.linghang</groupId>
<parent>
<artifactId>mywust</artifactId>
<groupId>cn.linghang</groupId>
<version>${revision}</version>
</parent>
<artifactId>mywust-network-httpclient</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<distributionManagement>
<repository>
<id>github</id>
<name>GitHub LingHangStudio Apache Maven Packages</name>
<url>https://maven.pkg.github.com/LingHangStudio/mywust</url>
</repository>
</distributionManagement>
</project>

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.linghang</groupId>
<artifactId>mywust-network-okhttp</artifactId>
<version>0.0.1-fixed</version>
<dependencies>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.14.9</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>cn.linghang</groupId>
<artifactId>mywust-network</artifactId>
<version>0.0.1-fixed</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>cn.linghang</groupId>
<artifactId>mywust-util</artifactId>
<version>0.0.1-fixed</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

@ -2,64 +2,55 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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>
<groupId>cn.linghang</groupId>
<parent>
<artifactId>mywust</artifactId>
<groupId>cn.linghang</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mywust-network-okhttp</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<okhttp.version>3.14.9</okhttp.version>
<slf4j.version>1.7.36</slf4j.version>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.14.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.13.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.0-rc1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.13.4</version>
<version>${okhttp.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>cn.linghang</groupId>
<artifactId>mywust-network</artifactId>
<version>0.0.1-dev</version>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>cn.linghang</groupId>
<artifactId>mywust-util</artifactId>
<version>0.0.1-dev</version>
<version>${project.parent.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
<distributionManagement>
<repository>
<id>github</id>
<name>GitHub LingHangStudio Apache Maven Packages</name>
<url>https://maven.pkg.github.com/LingHangStudio/mywust</url>
</repository>
</distributionManagement>
</project>

@ -13,9 +13,9 @@ public class RedirectInterceptor implements Interceptor {
Response response = chain.proceed(request);
String location = response.headers().get("Location");
if (location != null) {
System.out.println(("Request redirected to:" + location));
}
// if (location != null) {
// System.out.println(("Request redirected to:" + location));
// }
return response;
}

@ -1,11 +1,10 @@
package cn.linghang.mywust.network.okhttp;
import cn.linghang.mywust.network.entitys.HttpRequest;
import cn.linghang.mywust.network.entitys.HttpResponse;
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 cn.linghang.mywust.util.StringUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -13,7 +12,6 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
@ -33,7 +31,7 @@ import java.util.concurrent.TimeUnit;
*
* @author lensfrex
* @create 2022-10-15 09:49
* @edit 2022-10-19 21:30
* @edit 2023-01-21 15:35
*/
public class SimpleOkhttpRequester implements Requester {
private static final Logger log = LoggerFactory.getLogger(SimpleOkhttpRequester.class);
@ -46,7 +44,7 @@ public class SimpleOkhttpRequester implements Requester {
* 默认的多例模式的SimpleOkhttpRequester构造方法
*/
public SimpleOkhttpRequester() {
this(new OkHttpClient(), false);
this(false);
}
/**
@ -55,11 +53,12 @@ public class SimpleOkhttpRequester implements Requester {
* @param useSingletonClient 是否使用单例模式
*/
public SimpleOkhttpRequester(boolean useSingletonClient) {
this(new OkHttpClient(), useSingletonClient);
this.useSingletonClient = useSingletonClient;
this.setRootClient();
}
/**
* 使用特定okhttpClient对象并指定是否使用单例模式的SimpleOkhttpRequester私有构造方法
* 使用特定okhttpClient对象作为rootClient仅在rootClient为null时起作用并指定是否使用单例模式的SimpleOkhttpRequester私有构造方法
*
* @param okHttpClient okhttpClient对象
* @param useSingletonClient 是否使用单例模式
@ -70,50 +69,94 @@ public class SimpleOkhttpRequester implements Requester {
}
/**
* 指定client参数选项的SimpleOkhttpRequester构造方法默认不使用单例模式
* 指定client参数选项的SimpleOkhttpRequester构造方法仅在rootClient为null时起作用默认不使用单例模式
*
* @param requestClientOption client参数选项
* @param requestClientOption client参数选项为null时使用默认选项
*/
public SimpleOkhttpRequester(RequestClientOption requestClientOption) {
this(requestClientOption, false);
}
/**
* 指定client参数选项的SimpleOkhttpRequester构造方法并且指定是否使用单例模式
* 指定client参数选项的SimpleOkhttpRequester构造方法仅在rootClient为null时起作用并且指定是否使用单例模式
*
* @param requestClientOption client参数选项
* @param requestClientOption client参数选项为null时使用默认选项
* @param useSingletonClient 是否使用单例模式
*/
public SimpleOkhttpRequester(RequestClientOption requestClientOption, boolean useSingletonClient) {
this(requestClientOption, useSingletonClient, null);
}
/**
* 指定client参数选项的SimpleOkhttpRequester构造方法仅在rootClient为null时起作用并且指定是否使用单例模式以及CookieJar
*
* @param requestClientOption client参数选项为null时使用默认选项
* @param useSingletonClient 是否使用单例模式
* @param cookieJar 指定的cookieJar
*/
public SimpleOkhttpRequester(RequestClientOption requestClientOption, boolean useSingletonClient, CookieJar cookieJar) {
this.useSingletonClient = useSingletonClient;
this.setRootClient(buildClient(new OkHttpClient.Builder(), requestClientOption));
if (rootClient == null) {
synchronized (SimpleOkhttpRequester.class) {
if (rootClient == null ) {
rootClient = this.buildClient(new OkHttpClient.Builder(), requestClientOption, cookieJar);
}
}
}
}
/**
* 设置指定的client为根client仅在rootClient为null时设置
*
* @param okHttpClient 传入的client
*/
private void setRootClient(OkHttpClient okHttpClient) {
if (rootClient == null) {
synchronized (SimpleOkhttpRequester.class) {
if (rootClient == null) {
if (rootClient == null ) {
rootClient = okHttpClient;
}
}
}
}
/**
* 设置一个新的根client只在rootClient为null时设置保证根client只有一个
*/
private void setRootClient() {
if (rootClient == null) {
synchronized (SimpleOkhttpRequester.class) {
if (rootClient == null) {
rootClient = new OkHttpClient();
}
}
}
}
/**
* 构建一个okhttpClient
*
* @param builder okhttpClient.Builder
* @param requestClientOption client参数选项
* @param requestClientOption client参数选项当null时为默认的option
* @param cookieJar 提供的CookieJar当为null时不设置
* @return 构建好的client
*/
private OkHttpClient buildClient(OkHttpClient.Builder builder, RequestClientOption requestClientOption) {
private OkHttpClient buildClient(OkHttpClient.Builder builder, RequestClientOption requestClientOption, CookieJar cookieJar) {
if (requestClientOption == null) {
requestClientOption = new RequestClientOption();
}
builder.callTimeout(requestClientOption.getTimeout(), TimeUnit.SECONDS)
.readTimeout(requestClientOption.getTimeout(), TimeUnit.SECONDS)
.connectTimeout(requestClientOption.getTimeout(), TimeUnit.SECONDS)
.followRedirects(requestClientOption.isFallowUrlRedirect())
.addInterceptor(new RedirectInterceptor())
.sslSocketFactory(TrustAllCert.getSSLSocketFactory(), TrustAllCert.getX509TrustManager())
.hostnameVerifier(TrustAllCert.getHostnameVerifier());
.addInterceptor(new RedirectInterceptor());
// 是否忽略SSL错误
if (requestClientOption.isIgnoreSSLError()) {
builder.sslSocketFactory(TrustAllCert.getSSLSocketFactory(), TrustAllCert.getX509TrustManager())
.hostnameVerifier(TrustAllCert.getHostnameVerifier());
}
// 设置代理
RequestClientOption.Proxy proxyOption = requestClientOption.getProxy();
@ -124,6 +167,11 @@ public class SimpleOkhttpRequester implements Requester {
builder.proxy(proxy);
}
// 设置CookieJar
if (cookieJar != null) {
builder.cookieJar(cookieJar);
}
return builder.build();
}
@ -139,14 +187,9 @@ public class SimpleOkhttpRequester implements Requester {
return rootClient;
}
return buildClient(rootClient.newBuilder(), requestClientOption);
return buildClient(rootClient.newBuilder(), requestClientOption, null);
}
/**
* json的content-type
*/
private static final String JSON_CONTENT_TYPE = "application/json; charset=utf-8";
/**
* 默认的content-typeform
*/
@ -273,11 +316,15 @@ public class SimpleOkhttpRequester implements Requester {
* @throws IOException 如果网络请求有异常
*/
private HttpResponse doRequest(RequestMethod requestMethod, HttpRequest httpRequest, RequestClientOption requestClientOption) throws IOException {
if (requestClientOption == null) {
requestClientOption = RequestClientOption.DEFAULT_OPTION;
}
OkHttpClient client = getOkhttpClient(requestClientOption);
Request request = this.buildRequest(requestMethod, httpRequest);
log.debug("------------Do Request------------");
log.debug("Request Options: {}" ,requestClientOption);
log.debug("Request Options: {}", requestClientOption);
log.debug("Request: {}", request);
log.debug("Headers: {}", request.headers());
@ -289,18 +336,19 @@ public class SimpleOkhttpRequester implements Requester {
return this.doRequest(RequestMethod.GET, httpRequest, requestClientOption);
}
@Override
public HttpResponse get(HttpRequest httpRequest) throws IOException {
return this.doRequest(RequestMethod.GET, httpRequest, null);
}
@Override
public HttpResponse post(HttpRequest request, RequestClientOption requestClientOption) throws IOException {
return this.doRequest(RequestMethod.POST, request, requestClientOption);
}
@Override
public HttpResponse postJson(HttpRequest request, Object requestBody, RequestClientOption requestClientOption) throws IOException {
String json = new ObjectMapper().writeValueAsString(requestBody);
request.setData(json.getBytes(StandardCharsets.UTF_8));
request.getHeaders().put("Content-Type", JSON_CONTENT_TYPE);
return this.doRequest(RequestMethod.POST, request, requestClientOption);
public HttpResponse post(HttpRequest request) throws IOException {
return this.doRequest(RequestMethod.POST, request, null);
}
@Override
@ -308,8 +356,19 @@ public class SimpleOkhttpRequester implements Requester {
return this.doRequest(RequestMethod.PUT, request, requestClientOption);
}
@Override
public HttpResponse put(HttpRequest request) throws IOException {
return this.doRequest(RequestMethod.PUT, request, null);
}
@Override
public HttpResponse delete(HttpRequest request, RequestClientOption requestClientOption) throws IOException {
return this.doRequest(RequestMethod.DELETE, request, requestClientOption);
}
@Override
public HttpResponse delete(HttpRequest request) throws IOException {
return this.doRequest(RequestMethod.DELETE, request, null);
}
}

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.linghang</groupId>
<artifactId>mywust-network</artifactId>
<version>0.0.1-fixed</version>
<dependencies>
<dependency>
<groupId>cn.linghang</groupId>
<artifactId>mywust-util</artifactId>
<version>0.0.1-fixed</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

@ -5,28 +5,23 @@
<parent>
<artifactId>mywust</artifactId>
<groupId>cn.linghang</groupId>
<version>0.0.1-dev</version>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mywust-network</artifactId>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<dependency>
<groupId>cn.linghang</groupId>
<artifactId>mywust-util</artifactId>
<version>0.0.1-dev</version>
<version>${project.parent.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>

@ -1,27 +1,105 @@
package cn.linghang.mywust.network;
import lombok.Builder;
import lombok.Data;
@Data
public class RequestClientOption {
private Proxy proxy;
private long timeout;
private boolean fallowUrlRedirect = false;
private boolean fallowUrlRedirect;
private boolean ignoreSSLError;
@Data
@Builder
public static class Proxy {
private String address;
private int port;
}
public RequestClientOption() {
this.proxy = null;
this.timeout = 5;
this.fallowUrlRedirect = false;
this.ignoreSSLError = true;
}
public RequestClientOption(Proxy proxy, long timeout, boolean fallowUrlRedirect, boolean ignoreSSLError) {
this.proxy = proxy;
this.timeout = timeout;
this.fallowUrlRedirect = fallowUrlRedirect;
this.ignoreSSLError = ignoreSSLError;
}
public static final RequestClientOption DEFAULT_OPTION = newDefaultOption();
private static RequestClientOption newDefaultOption() {
RequestClientOption option = new RequestClientOption();
option.setTimeout(5);
option.setProxy(null);
option.setFallowUrlRedirect(false);
return new ConstantRequestClientOption();
}
/**
* 常量化的RequestClientOption对其使用setter时不会起任何作用
*/
public static class ConstantRequestClientOption extends RequestClientOption {
@Override
public Proxy getProxy() {
return super.getProxy();
}
@Override
public long getTimeout() {
return super.getTimeout();
}
@Override
public boolean isFallowUrlRedirect() {
return super.isFallowUrlRedirect();
}
@Override
public void setProxy(Proxy proxy) {}
@Override
public void setTimeout(long timeout) {}
@Override
public void setFallowUrlRedirect(boolean fallowUrlRedirect) {}
}
public static RequestClientOptionBuilder builder() {
return new RequestClientOptionBuilder();
}
public static final class RequestClientOptionBuilder {
private final RequestClientOption requestClientOption;
private RequestClientOptionBuilder() {
requestClientOption = new RequestClientOption();
}
public RequestClientOptionBuilder proxy(Proxy proxy) {
requestClientOption.setProxy(proxy);
return this;
}
public RequestClientOptionBuilder timeout(long timeout) {
requestClientOption.setTimeout(timeout);
return this;
}
public RequestClientOptionBuilder fallowUrlRedirect(boolean fallowUrlRedirect) {
requestClientOption.setFallowUrlRedirect(fallowUrlRedirect);
return this;
}
public RequestClientOptionBuilder ignoreSSLError(boolean ignoreSSLError) {
requestClientOption.setIgnoreSSLError(ignoreSSLError);
return this;
}
return option;
public RequestClientOption build() {
return requestClientOption;
}
}
}

@ -21,50 +21,47 @@ public interface Requester {
* 发送Get请求
*
* @param request 请求体
* @param requestClientOption 请求选项
* @param requestClientOption 请求选项为null时使用默认的选项
* @return 响应数据
* @throws IOException 如果网络请求有异常
*/
HttpResponse get(HttpRequest request, RequestClientOption requestClientOption) throws IOException;
HttpResponse get(HttpRequest request) throws IOException;
/**
* 发送Post请求
*
* @param request 请求体
* @param requestClientOption 请求选项
* @param requestClientOption 请求选项为null时使用默认的选项
* @return 响应数据
* @throws IOException 如果网络请求有异常
*/
HttpResponse post(HttpRequest request, RequestClientOption requestClientOption) throws IOException;
/**
* 发送Post请求并将对象自动封装成json
*
* @param request 请求体
* @param requestClientOption 请求选项
* @return 响应数据
* @throws IOException 如果网络请求有异常
*/
HttpResponse postJson(HttpRequest request, Object requestBody, RequestClientOption requestClientOption) throws IOException;
HttpResponse post(HttpRequest request) throws IOException;
/**
* 发送Put请求
*
* @param request 请求体
* @param requestClientOption 请求选项
* @param requestClientOption 请求选项为null时使用默认的选项
* @return 响应数据
* @throws IOException 如果网络请求有异常
*/
HttpResponse put(HttpRequest request, RequestClientOption requestClientOption) throws IOException;
HttpResponse put(HttpRequest request) throws IOException;
/**
* 发送Delete请求
*
* @param request 请求体
* @param requestClientOption 请求选项
* @param requestClientOption 请求选项为null时使用默认的选项
* @return 响应数据
* @throws IOException 如果网络请求有异常
*/
HttpResponse delete(HttpRequest request, RequestClientOption requestClientOption) throws IOException;
HttpResponse delete(HttpRequest request) throws IOException;
}

@ -1,12 +1,15 @@
package cn.linghang.mywust.network.entitys;
import lombok.Builder;
import lombok.Data;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
@Data
@Builder
public class HttpRequest {
private static final Map<String, String> DEFAULT_HEADERS = initDefaultHeaders();
@ -33,6 +36,17 @@ public class HttpRequest {
headers = new HashMap<>(DEFAULT_HEADERS);
}
public HttpRequest(URL url, Map<String, String> headers, String cookies, byte[] data) {
this.url = url;
this.cookies = cookies;
this.data = data;
this.headers = headers;
if (headers == null) {
this.headers = new HashMap<>(DEFAULT_HEADERS);
}
}
public HttpRequest addHeaders(Map<String, String> headers) {
this.headers.putAll(headers);
return this;
@ -43,6 +57,18 @@ public class HttpRequest {
return this;
}
public String getStringData() {
return new String(this.data);
}
public void setData(byte[] data) {
this.data = data;
}
public void setData(String stringData) {
this.data = stringData.getBytes(StandardCharsets.UTF_8);
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("HttpRequest{");
@ -57,44 +83,4 @@ public class HttpRequest {
sb.append('}');
return sb.toString();
}
public static HttpRequestBuilder builder() {
return new HttpRequestBuilder();
}
public static class HttpRequestBuilder {
private final HttpRequest httpRequest;
private HttpRequestBuilder() {
httpRequest = new HttpRequest();
}
public static HttpRequestBuilder aHttpRequest() {
return new HttpRequestBuilder();
}
public HttpRequestBuilder url(URL url) {
httpRequest.setUrl(url);
return this;
}
public HttpRequestBuilder headers(Map<String, String> headers) {
httpRequest.setHeaders(headers);
return this;
}
public HttpRequestBuilder cookies(String cookies) {
httpRequest.setCookies(cookies);
return this;
}
public HttpRequestBuilder data(byte[] data) {
httpRequest.setData(data);
return this;
}
public HttpRequest build() {
return httpRequest;
}
}
}

@ -1,10 +1,17 @@
package cn.linghang.mywust.network.entitys;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.nio.charset.StandardCharsets;
import java.util.Map;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class HttpResponse {
public static final int HTTP_OK = 200;
public static final int HTTP_RESOURCE_CREATED = 201;
@ -26,6 +33,18 @@ public class HttpResponse {
private byte[] body;
public String getStringBody() {
return new String(body);
}
public void setBody(byte[] body) {
this.body = body;
}
public void setBody(String stringBody) {
this.body = stringBody.getBytes(StandardCharsets.UTF_8);
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("HttpResponse{");
@ -39,39 +58,4 @@ public class HttpResponse {
sb.append('}');
return sb.toString();
}
public static HttpResponseBuilder builder() {
return new HttpResponseBuilder();
}
public static final class HttpResponseBuilder {
private final HttpResponse httpResponse;
private HttpResponseBuilder() {
httpResponse = new HttpResponse();
}
public static HttpResponseBuilder aHttpResponse() {
return new HttpResponseBuilder();
}
public HttpResponseBuilder headers(Map<String, String> headers) {
httpResponse.setHeaders(headers);
return this;
}
public HttpResponseBuilder cookies(String cookies) {
httpResponse.setCookies(cookies);
return this;
}
public HttpResponseBuilder body(byte[] body) {
httpResponse.setBody(body);
return this;
}
public HttpResponse build() {
return httpResponse;
}
}
}

@ -11,7 +11,7 @@
<dependency>
<groupId>cn.linghang</groupId>
<artifactId>mywust-core</artifactId>
<version>0.0.1-dev</version>
<version>0.0.1-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
@ -61,8 +61,8 @@
</dependencies>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

@ -0,0 +1,12 @@
import cn.linghang.mywust.core.exception.ParseException;
import cn.linghang.mywust.core.parser.undergraduate.ExamInfoParser;
import org.jsoup.Jsoup;
import java.io.IOException;
public class ParseTest {
public static void main(String[] args) throws IOException, ParseException {
ExamInfoParser parser = new ExamInfoParser();
System.out.println(parser.parse(Jsoup.connect("http://127.0.0.1/a.html").get().toString()));
}
}

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.linghang</groupId>
<artifactId>mywust-util</artifactId>
<version>0.0.1-fixed</version>
<dependencies>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

@ -5,15 +5,15 @@
<parent>
<artifactId>mywust</artifactId>
<groupId>cn.linghang</groupId>
<version>0.0.1-dev</version>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mywust-util</artifactId>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

@ -3,6 +3,7 @@ package cn.linghang.mywust.util;
import com.google.common.base.Joiner;
import org.apache.commons.codec.binary.Base64;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
@ -19,7 +20,12 @@ public class StringUtil {
public static String generateQueryString(Map<String, String> queryParams) {
// 自动对value值进行url编码
Map<String, String> urlEncodedQueryParams = new TreeMap<>(REPEATABLE_COMPARATOR);
queryParams.forEach((k, v) -> urlEncodedQueryParams.put(k, URLEncoder.encode(v, StandardCharsets.UTF_8)));
queryParams.forEach((k, v) -> {
try {
urlEncodedQueryParams.put(k, URLEncoder.encode(v, StandardCharsets.UTF_8.name()));
} catch (UnsupportedEncodingException ignored) {
}
});
return Joiner.on('&')
.useForNull("")

@ -7,27 +7,84 @@
<groupId>cn.linghang</groupId>
<artifactId>mywust</artifactId>
<packaging>pom</packaging>
<version>0.0.1-dev</version>
<version>${revision}</version>
<modules>
<module>mywust-core</module>
<module>mywust-model</module>
<module>mywust-network</module>
<module>mywust-util</module>
<module>mywust-network-httpclient</module>
<module>mywust-network-okhttp</module>
</modules>
<dependencies>
<dependency>
<groupId>org.jetbrains.externalAnnotations.junit</groupId>
<artifactId>junit</artifactId>
<version>4.12-an1</version>
<scope>test</scope>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<revision>0.0.1-fixed</revision>
<lombok.version>1.18.22</lombok.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<distributionManagement>
<repository>
<id>github</id>
<name>GitHub LingHangStudio Apache Maven Packages</name>
<url>https://maven.pkg.github.com/LingHangStudio/mywust</url>
</repository>
</distributionManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>1.3.0</version>
<configuration>
</configuration>
<executions>
<!-- enable flattening -->
<execution>
<id>flatten</id>
<phase>process-resources</phase>
<goals>
<goal>flatten</goal>
</goals>
</execution>
<!-- ensure proper cleanup -->
<execution>
<id>flatten.clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Loading…
Cancel
Save