sync
This commit is contained in:
parent
ff3e0de135
commit
798e01b43f
|
@ -1,37 +1,45 @@
|
||||||
package com.dengqn.igps2xingzhe.controller;
|
package com.dengqn.igps2xingzhe.controller;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.io.FileUtil;
|
||||||
import cn.hutool.core.io.IoUtil;
|
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.KeyUtil;
|
||||||
import cn.hutool.crypto.SecureUtil;
|
import cn.hutool.crypto.SecureUtil;
|
||||||
import cn.hutool.crypto.asymmetric.AsymmetricAlgorithm;
|
import cn.hutool.crypto.asymmetric.AsymmetricAlgorithm;
|
||||||
import cn.hutool.crypto.asymmetric.KeyType;
|
import cn.hutool.crypto.asymmetric.KeyType;
|
||||||
import cn.hutool.crypto.asymmetric.RSA;
|
import cn.hutool.crypto.asymmetric.RSA;
|
||||||
|
import cn.hutool.json.JSONConfig;
|
||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
import com.dengqn.igps2xingzhe.config.IGPSport;
|
import com.dengqn.igps2xingzhe.config.IGPSport;
|
||||||
import com.dengqn.igps2xingzhe.config.XingZhe;
|
import com.dengqn.igps2xingzhe.config.XingZhe;
|
||||||
|
import com.dengqn.igps2xingzhe.vo.IGPSActivity;
|
||||||
|
import com.dengqn.igps2xingzhe.vo.IGPSActivityResp;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.catalina.security.SecurityUtil;
|
import org.apache.catalina.security.SecurityUtil;
|
||||||
import org.apache.hc.client5.http.classic.HttpClient;
|
import org.apache.hc.client5.http.classic.HttpClient;
|
||||||
import org.apache.hc.client5.http.cookie.Cookie;
|
import org.apache.hc.client5.http.cookie.Cookie;
|
||||||
import org.apache.hc.client5.http.cookie.CookieStore;
|
import org.apache.hc.client5.http.cookie.CookieStore;
|
||||||
import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
|
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.client5.http.impl.classic.CloseableHttpResponse;
|
||||||
import org.apache.hc.core5.http.ContentType;
|
import org.apache.hc.core5.http.*;
|
||||||
import org.apache.hc.core5.http.HttpEntity;
|
|
||||||
import org.apache.hc.core5.http.HttpResponse;
|
|
||||||
import org.apache.hc.core5.http.io.entity.BasicHttpEntity;
|
import org.apache.hc.core5.http.io.entity.BasicHttpEntity;
|
||||||
import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
|
import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
|
||||||
import org.apache.hc.core5.http.message.BasicHttpResponse;
|
import org.apache.hc.core5.http.message.BasicHttpResponse;
|
||||||
import org.apache.hc.core5.http.message.BasicNameValuePair;
|
import org.apache.hc.core5.http.message.BasicNameValuePair;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.*;
|
||||||
import java.io.IOException;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.KeyFactory;
|
import java.security.KeyFactory;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
@ -53,64 +61,106 @@ import java.util.Map;
|
||||||
@RequestMapping("/api/trigger")
|
@RequestMapping("/api/trigger")
|
||||||
public class Trigger {
|
public class Trigger {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private HttpClient httpClient;
|
private HttpClient httpClient;
|
||||||
@Autowired
|
@Autowired
|
||||||
private CookieStore cookieStore;
|
private CookieStore cookieStore;
|
||||||
|
@Value("${dataDir:/data}")
|
||||||
|
private String fileDir;
|
||||||
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private XingZhe xingZhe;
|
private XingZhe xingZhe;
|
||||||
@Autowired
|
@Autowired
|
||||||
private IGPSport igpSport;
|
private IGPSport igpSport;
|
||||||
|
|
||||||
@GetMapping("/sync/igps2xingzhe")
|
@GetMapping("/sync/igps2xingzhe")
|
||||||
public ResponseEntity<String> onSyncIGPS2XingZhe() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
|
public ResponseEntity<String> onSyncIGPS2XingZhe() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
|
||||||
// 1. 登录行者
|
// 1. 登录行者
|
||||||
// xingzheLogin();
|
xingzheLogin();
|
||||||
// 2. 登录igps
|
// 2. 登录igps
|
||||||
igpsLogin();
|
igpsLogin();
|
||||||
|
// 3. 获取igps活动
|
||||||
|
syncIgpsActicities();
|
||||||
|
|
||||||
return ResponseEntity.ok("ok");
|
return ResponseEntity.ok("ok");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void igpsLogin() throws IOException {
|
private void syncIgpsActicities() throws IOException {
|
||||||
// 3. igps 登录 formData
|
CloseableHttpResponse activityResp = (CloseableHttpResponse) httpClient.execute(ClassicRequestBuilder.get("https://my.igpsport.com/Activity/MyActivityList").build());
|
||||||
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(CollUtil.toList(
|
String activityRespStr = new String(IoUtil.readBytes(activityResp.getEntity().getContent()));
|
||||||
new BasicNameValuePair("username", igpSport.getUsername()),
|
IGPSActivityResp activityRespData = JSONUtil.toBean(activityRespStr, IGPSActivityResp.class, JSONConfig.create()
|
||||||
new BasicNameValuePair("password", igpSport.getPassword())
|
.setIgnoreCase(true)
|
||||||
));
|
.isIgnoreError());
|
||||||
|
for (IGPSActivity activity : activityRespData.getItem()) {
|
||||||
|
// 1. 判断是否本地有文件
|
||||||
|
String filePath = fileDir + File.separator + activity.getFileName();
|
||||||
|
|
||||||
ClassicRequestBuilder igpsLoginReq = ClassicRequestBuilder
|
if (FileUtil.exist(filePath)) {
|
||||||
.post("https://my.igpsport.com/Auth/Login")
|
log.info("文件已存在" + filePath);
|
||||||
.setEntity(formEntity);
|
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 {
|
CloseableHttpResponse downloadResp = (CloseableHttpResponse) httpClient.execute(ClassicRequestBuilder.get(downloadUrl).build());
|
||||||
// 1、获取行者平台的cookie
|
InputStream downloadInputStream = downloadResp.getEntity().getContent();
|
||||||
HttpResponse executed = httpClient.execute(ClassicRequestBuilder.get("https://www.imxingzhe.com/user/login").build());
|
long copied = IoUtil.copy(downloadInputStream, new FileOutputStream(filePath));
|
||||||
System.out.println(executed.getCode());
|
log.info("{} bytes copied", copied);
|
||||||
// 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<String, Object> loginData = new HashMap<>();
|
// 上传到xingzhe
|
||||||
loginData.put("account", xingZhe.getUsername());
|
String uploadUrl = "https://www.imxingzhe.com/api/v4/upload_fits";
|
||||||
loginData.put("password", encryptBase64);
|
HttpEntity uploadForm = MultipartEntityBuilder.create()
|
||||||
loginData.put("source", "web");
|
.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
|
CloseableHttpResponse uploadResponse = (CloseableHttpResponse) httpClient.execute(ClassicRequestBuilder.post(uploadUrl).setEntity(uploadForm).build());
|
||||||
.post("https://www.imxingzhe.com/api/v4/account/login")
|
log.info("upload result: {}", new String(IoUtil.readBytes(uploadResponse.getEntity().getContent())));
|
||||||
.setEntity(JSONUtil.toJsonPrettyStr(loginData), ContentType.APPLICATION_JSON)
|
}
|
||||||
.build());
|
}
|
||||||
log.info("xingzhe login: {}", IoUtil.read(response.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<String, Object> 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()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<IGPSActivity> item;
|
||||||
|
|
||||||
|
private Integer unit;
|
||||||
|
}
|
Loading…
Reference in New Issue