diff --git a/src/main/java/com/dengqn/igps2xingzhe/config/Giant.java b/src/main/java/com/dengqn/igps2xingzhe/config/Giant.java new file mode 100644 index 0000000..6bf9b98 --- /dev/null +++ b/src/main/java/com/dengqn/igps2xingzhe/config/Giant.java @@ -0,0 +1,30 @@ +package com.dengqn.igps2xingzhe.config; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author dengqn + * @since 2024/7/20 11:23 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Configuration +@ConfigurationProperties(prefix = "user.giant") +public class Giant implements Serializable { + @Serial + private static final long serialVersionUID = 1996804203171936839L; + + private Boolean enable; + private String username; + private String password; +} diff --git a/src/main/java/com/dengqn/igps2xingzhe/config/IGPSport.java b/src/main/java/com/dengqn/igps2xingzhe/config/IGPSport.java index b61a859..99058b0 100644 --- a/src/main/java/com/dengqn/igps2xingzhe/config/IGPSport.java +++ b/src/main/java/com/dengqn/igps2xingzhe/config/IGPSport.java @@ -7,6 +7,7 @@ import lombok.NoArgsConstructor; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; +import java.io.Serial; import java.io.Serializable; /** @@ -20,6 +21,7 @@ import java.io.Serializable; @Configuration @ConfigurationProperties(prefix = "user.igps") public class IGPSport implements Serializable { + @Serial private static final long serialVersionUID = 3004387713475397922L; private String username; diff --git a/src/main/java/com/dengqn/igps2xingzhe/controller/Trigger.java b/src/main/java/com/dengqn/igps2xingzhe/controller/Trigger.java index 6d6674a..01d1ee7 100644 --- a/src/main/java/com/dengqn/igps2xingzhe/controller/Trigger.java +++ b/src/main/java/com/dengqn/igps2xingzhe/controller/Trigger.java @@ -10,8 +10,10 @@ 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.http.HttpUtil; import cn.hutool.json.JSONConfig; import cn.hutool.json.JSONUtil; +import com.dengqn.igps2xingzhe.config.Giant; import com.dengqn.igps2xingzhe.config.IGPSport; import com.dengqn.igps2xingzhe.config.XingZhe; import com.dengqn.igps2xingzhe.vo.IGPSActivity; @@ -49,6 +51,7 @@ import java.security.spec.RSAPublicKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * 同步 @@ -68,24 +71,49 @@ public class Trigger { @Value("${dataDir:/data}") private String fileDir; - + @Autowired + private Giant giant; @Autowired private XingZhe xingZhe; @Autowired private IGPSport igpSport; + private static final String GIANT_TOKEN_KEY = "GIANT_TOKEN_KEY"; + private static final Map contextCache = new ConcurrentHashMap<>(4); + @GetMapping("/sync/igps2xingzhe") public ResponseEntity onSyncIGPS2XingZhe() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { // 1. 登录行者 xingzheLogin(); // 2. 登录igps igpsLogin(); + // 3. 登录捷安特 + giantLogin(); // 3. 获取igps活动 syncIgpsActicities(); return ResponseEntity.ok("ok"); } + private void giantLogin() throws IOException { + if (!giant.getEnable()) { + return; + } + // clear cookie + httpClient.execute(ClassicRequestBuilder.get("https://ridelife.giant.com.cn/web/login.html/index.php/api/login").build()); + UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(CollUtil.toList( + new BasicNameValuePair("username", giant.getUsername()), + new BasicNameValuePair("password", giant.getPassword()) + )); + ClassicRequestBuilder igpsLoginReq = ClassicRequestBuilder + .post("https://ridelife.giant.com.cn/index.php/api/login") + .setEntity(formEntity); + CloseableHttpResponse response = (CloseableHttpResponse) httpClient.execute(igpsLoginReq.build()); + String responseData = new String(IoUtil.readBytes(response.getEntity().getContent())); + String token = JSONUtil.parseObj(responseData).getStr("user_token"); + contextCache.put(GIANT_TOKEN_KEY, token); + } + 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())); @@ -100,27 +128,49 @@ public class Trigger { log.info("文件已存在" + filePath); continue; } - // 下载文件 - String downloadUrl = "https://my.igpsport.com/fit/activity?type=0&rideid=" + activity.getRideId(); - log.info("尝试下载" + downloadUrl); - - 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); - + downloadFileFromIgps(activity, filePath); // 上传到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(); + uploadToXingZhe(activity, filePath); + // sync to giant app + uploadToGiant(filePath); + } + } - CloseableHttpResponse uploadResponse = (CloseableHttpResponse) httpClient.execute(ClassicRequestBuilder.post(uploadUrl).setEntity(uploadForm).build()); - log.info("upload result: {}", new String(IoUtil.readBytes(uploadResponse.getEntity().getContent()))); + private void downloadFileFromIgps(IGPSActivity activity, String filePath) throws IOException { + String downloadUrl = "https://my.igpsport.com/fit/activity?type=0&rideid=" + activity.getRideId(); + log.info("尝试下载" + downloadUrl); + + 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); + } + + private void uploadToXingZhe(IGPSActivity activity, String filePath) throws IOException { + 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 uploadResponse = (CloseableHttpResponse) httpClient.execute(ClassicRequestBuilder.post(uploadUrl).setEntity(uploadForm).build()); + log.info("upload to xingzhe result: {}", new String(IoUtil.readBytes(uploadResponse.getEntity().getContent()))); + } + + private void uploadToGiant(String filePath) throws IOException { + if (giant.getEnable()) { + String giantUploadUrl = "https://ridelife.giant.com.cn/index.php/api/upload_fit"; + + cn.hutool.http.HttpRequest formed = HttpUtil.createPost(giantUploadUrl) + .form("files[]", new File[]{new File(filePath)}) + .form("brand", "giant") + .form("device", "bike_computer") + .form("token", contextCache.getOrDefault(GIANT_TOKEN_KEY, "").toString()); + cn.hutool.http.HttpResponse execute = formed.execute(); + log.info("upload to giant result: {}", execute.body()); } } diff --git a/src/main/resources/application.properties.example b/src/main/resources/application.properties.example index a9a7247..5367cb1 100644 --- a/src/main/resources/application.properties.example +++ b/src/main/resources/application.properties.example @@ -12,4 +12,6 @@ user.xingzhe.pubKey: "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDmuQkBbijudDAJgfffDe user.igps.username: xxx user.igps.password: xxx - +user.giant.enable: false +user.giant.username: xxx +user.giant.password: xxx