From 798e01b43f08b0247bbc9bc07e5eded8c662215f Mon Sep 17 00:00:00 2001 From: dqn Date: Tue, 23 Jul 2024 00:03:08 +0800 Subject: [PATCH] sync --- .../igps2xingzhe/controller/Trigger.java | 156 ++++++++++++------ .../dengqn/igps2xingzhe/vo/IGPSActivity.java | 35 ++++ .../igps2xingzhe/vo/IGPSActivityResp.java | 25 +++ 3 files changed, 163 insertions(+), 53 deletions(-) create mode 100644 src/main/java/com/dengqn/igps2xingzhe/vo/IGPSActivity.java create mode 100644 src/main/java/com/dengqn/igps2xingzhe/vo/IGPSActivityResp.java diff --git a/src/main/java/com/dengqn/igps2xingzhe/controller/Trigger.java b/src/main/java/com/dengqn/igps2xingzhe/controller/Trigger.java index 4530ea1..6d6674a 100644 --- a/src/main/java/com/dengqn/igps2xingzhe/controller/Trigger.java +++ b/src/main/java/com/dengqn/igps2xingzhe/controller/Trigger.java @@ -1,37 +1,45 @@ package com.dengqn.igps2xingzhe.controller; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.IoUtil; +import cn.hutool.core.io.file.PathUtil; +import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.KeyUtil; import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.asymmetric.AsymmetricAlgorithm; import cn.hutool.crypto.asymmetric.KeyType; import cn.hutool.crypto.asymmetric.RSA; +import cn.hutool.json.JSONConfig; import cn.hutool.json.JSONUtil; import com.dengqn.igps2xingzhe.config.IGPSport; import com.dengqn.igps2xingzhe.config.XingZhe; +import com.dengqn.igps2xingzhe.vo.IGPSActivity; +import com.dengqn.igps2xingzhe.vo.IGPSActivityResp; import lombok.extern.slf4j.Slf4j; import org.apache.catalina.security.SecurityUtil; import org.apache.hc.client5.http.classic.HttpClient; import org.apache.hc.client5.http.cookie.Cookie; import org.apache.hc.client5.http.cookie.CookieStore; import org.apache.hc.client5.http.entity.UrlEncodedFormEntity; +import org.apache.hc.client5.http.entity.mime.FileBody; +import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder; +import org.apache.hc.client5.http.entity.mime.StringBody; import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; -import org.apache.hc.core5.http.ContentType; -import org.apache.hc.core5.http.HttpEntity; -import org.apache.hc.core5.http.HttpResponse; +import org.apache.hc.core5.http.*; import org.apache.hc.core5.http.io.entity.BasicHttpEntity; import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; import org.apache.hc.core5.http.message.BasicHttpResponse; import org.apache.hc.core5.http.message.BasicNameValuePair; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import java.io.ByteArrayInputStream; -import java.io.IOException; +import java.io.*; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; @@ -53,64 +61,106 @@ import java.util.Map; @RequestMapping("/api/trigger") public class Trigger { - @Autowired - private HttpClient httpClient; - @Autowired - private CookieStore cookieStore; + @Autowired + private HttpClient httpClient; + @Autowired + private CookieStore cookieStore; + @Value("${dataDir:/data}") + private String fileDir; - @Autowired - private XingZhe xingZhe; - @Autowired - private IGPSport igpSport; + @Autowired + private XingZhe xingZhe; + @Autowired + private IGPSport igpSport; - @GetMapping("/sync/igps2xingzhe") - public ResponseEntity onSyncIGPS2XingZhe() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { - // 1. 登录行者 -// xingzheLogin(); - // 2. 登录igps - igpsLogin(); + @GetMapping("/sync/igps2xingzhe") + public ResponseEntity onSyncIGPS2XingZhe() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { + // 1. 登录行者 + xingzheLogin(); + // 2. 登录igps + igpsLogin(); + // 3. 获取igps活动 + syncIgpsActicities(); - return ResponseEntity.ok("ok"); - } + return ResponseEntity.ok("ok"); + } - private void igpsLogin() throws IOException { - // 3. igps 登录 formData - UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(CollUtil.toList( - new BasicNameValuePair("username", igpSport.getUsername()), - new BasicNameValuePair("password", igpSport.getPassword()) - )); + private void syncIgpsActicities() throws IOException { + CloseableHttpResponse activityResp = (CloseableHttpResponse) httpClient.execute(ClassicRequestBuilder.get("https://my.igpsport.com/Activity/MyActivityList").build()); + String activityRespStr = new String(IoUtil.readBytes(activityResp.getEntity().getContent())); + IGPSActivityResp activityRespData = JSONUtil.toBean(activityRespStr, IGPSActivityResp.class, JSONConfig.create() + .setIgnoreCase(true) + .isIgnoreError()); + for (IGPSActivity activity : activityRespData.getItem()) { + // 1. 判断是否本地有文件 + String filePath = fileDir + File.separator + activity.getFileName(); - ClassicRequestBuilder igpsLoginReq = ClassicRequestBuilder - .post("https://my.igpsport.com/Auth/Login") - .setEntity(formEntity); + if (FileUtil.exist(filePath)) { + log.info("文件已存在" + filePath); + continue; + } - CloseableHttpResponse response = (CloseableHttpResponse) httpClient.execute(igpsLoginReq.build()); - log.info("igps login: {}", IoUtil.read(response.getEntity().getContent())); - } + // 下载文件 + String downloadUrl = "https://my.igpsport.com/fit/activity?type=0&rideid=" + activity.getRideId(); + log.info("尝试下载" + downloadUrl); - private void xingzheLogin() throws IOException { - // 1、获取行者平台的cookie - HttpResponse executed = httpClient.execute(ClassicRequestBuilder.get("https://www.imxingzhe.com/user/login").build()); - System.out.println(executed.getCode()); - // 2、计算加密后的密码参数 - RSA rsa = new RSA(AsymmetricAlgorithm.RSA_ECB_PKCS1.getValue(), null, xingZhe.getPubKey()); - String rd = cookieStore.getCookies() - .stream().filter(a -> a.getName().equals("rd")).findFirst() - .map(Cookie::getValue).orElse(""); - String encryptBase64 = rsa.encryptBase64(xingZhe.getPassword() + ";" + rd, KeyType.PublicKey); + CloseableHttpResponse downloadResp = (CloseableHttpResponse) httpClient.execute(ClassicRequestBuilder.get(downloadUrl).build()); + InputStream downloadInputStream = downloadResp.getEntity().getContent(); + long copied = IoUtil.copy(downloadInputStream, new FileOutputStream(filePath)); + log.info("{} bytes copied", copied); - Map loginData = new HashMap<>(); - loginData.put("account", xingZhe.getUsername()); - loginData.put("password", encryptBase64); - loginData.put("source", "web"); + // 上传到xingzhe + String uploadUrl = "https://www.imxingzhe.com/api/v4/upload_fits"; + HttpEntity uploadForm = MultipartEntityBuilder.create() + .addPart("upload_file_name", new FileBody(new File(filePath))) + .addPart("title", new StringBody(activity.getFileName(), ContentType.create("text/plain", Charset.defaultCharset()))) + .addPart("device", new StringBody("3", ContentType.create("text/plain", Charset.defaultCharset()))) + .addPart("sport", new StringBody("3", ContentType.create("text/plain", Charset.defaultCharset()))) + .build(); - CloseableHttpResponse response = (CloseableHttpResponse) httpClient.execute(ClassicRequestBuilder - .post("https://www.imxingzhe.com/api/v4/account/login") - .setEntity(JSONUtil.toJsonPrettyStr(loginData), ContentType.APPLICATION_JSON) - .build()); - log.info("xingzhe login: {}", IoUtil.read(response.getEntity().getContent())); - } + CloseableHttpResponse uploadResponse = (CloseableHttpResponse) httpClient.execute(ClassicRequestBuilder.post(uploadUrl).setEntity(uploadForm).build()); + log.info("upload result: {}", new String(IoUtil.readBytes(uploadResponse.getEntity().getContent()))); + } + } + + private void igpsLogin() throws IOException { + // 3. igps 登录 formData + UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(CollUtil.toList( + new BasicNameValuePair("username", igpSport.getUsername()), + new BasicNameValuePair("password", igpSport.getPassword()) + )); + + ClassicRequestBuilder igpsLoginReq = ClassicRequestBuilder + .post("https://my.igpsport.com/Auth/Login") + .setEntity(formEntity); + + CloseableHttpResponse response = (CloseableHttpResponse) httpClient.execute(igpsLoginReq.build()); + log.info("igps login: {}", IoUtil.read(response.getEntity().getContent())); + } + + private void xingzheLogin() throws IOException { + // 1、获取行者平台的cookie + HttpResponse executed = httpClient.execute(ClassicRequestBuilder.get("https://www.imxingzhe.com/user/login").build()); + System.out.println(executed.getCode()); + // 2、计算加密后的密码参数 + RSA rsa = new RSA(AsymmetricAlgorithm.RSA_ECB_PKCS1.getValue(), null, xingZhe.getPubKey()); + String rd = cookieStore.getCookies() + .stream().filter(a -> a.getName().equals("rd")).findFirst() + .map(Cookie::getValue).orElse(""); + String encryptBase64 = rsa.encryptBase64(xingZhe.getPassword() + ";" + rd, KeyType.PublicKey); + + Map loginData = new HashMap<>(); + loginData.put("account", xingZhe.getUsername()); + loginData.put("password", encryptBase64); + loginData.put("source", "web"); + + CloseableHttpResponse response = (CloseableHttpResponse) httpClient.execute(ClassicRequestBuilder + .post("https://www.imxingzhe.com/api/v4/account/login") + .setEntity(JSONUtil.toJsonPrettyStr(loginData), ContentType.APPLICATION_JSON) + .build()); + log.info("xingzhe login: {}", IoUtil.read(response.getEntity().getContent())); + } } diff --git a/src/main/java/com/dengqn/igps2xingzhe/vo/IGPSActivity.java b/src/main/java/com/dengqn/igps2xingzhe/vo/IGPSActivity.java new file mode 100644 index 0000000..0590582 --- /dev/null +++ b/src/main/java/com/dengqn/igps2xingzhe/vo/IGPSActivity.java @@ -0,0 +1,35 @@ +package com.dengqn.igps2xingzhe.vo; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class IGPSActivity implements Serializable { + @Serial + private static final long serialVersionUID = 2974104022106192054L; + + private String RideId; + private String MemberId; + private String Title; + private String StartTime; + private String StartTimeString; + private String RideDistance; + private String TotalAscent; + private String MovingTime; + private String OpenStatus; + private String Status; + private String SportType; + + public String getFileName() { + return StrUtil.format("igpsport-{}-{}-{}.fit", RideId, StartTimeString, RideDistance); + } +} diff --git a/src/main/java/com/dengqn/igps2xingzhe/vo/IGPSActivityResp.java b/src/main/java/com/dengqn/igps2xingzhe/vo/IGPSActivityResp.java new file mode 100644 index 0000000..8ae21af --- /dev/null +++ b/src/main/java/com/dengqn/igps2xingzhe/vo/IGPSActivityResp.java @@ -0,0 +1,25 @@ +package com.dengqn.igps2xingzhe.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class IGPSActivityResp implements Serializable { + @Serial + private static final long serialVersionUID = -2545669204414828896L; + + private Integer total; + + private List item; + + private Integer unit; +}