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