|
@@ -1,20 +1,36 @@
|
|
package com.jkcredit.traffic.record.service;
|
|
package com.jkcredit.traffic.record.service;
|
|
|
|
|
|
import com.alibaba.fastjson.JSON;
|
|
import com.alibaba.fastjson.JSON;
|
|
|
|
+import com.alibaba.fastjson.JSONArray;
|
|
|
|
+import com.ciphergateway.ciphersuite.CipherSuiteException;
|
|
|
|
+import com.ciphergateway.ciphersuite.CipherSuiteMacException;
|
|
|
|
+import com.ciphergateway.ciphersuite.CipherSuiteUtils;
|
|
import com.jkcredit.traffic.record.constant.CommonConstant;
|
|
import com.jkcredit.traffic.record.constant.CommonConstant;
|
|
import com.jkcredit.traffic.record.model.CommonResponseObject;
|
|
import com.jkcredit.traffic.record.model.CommonResponseObject;
|
|
import com.jkcredit.traffic.record.model.MonthResult;
|
|
import com.jkcredit.traffic.record.model.MonthResult;
|
|
import com.jkcredit.traffic.record.test.AsyncPushTask;
|
|
import com.jkcredit.traffic.record.test.AsyncPushTask;
|
|
import com.jkcredit.traffic.record.util.CsvUtil;
|
|
import com.jkcredit.traffic.record.util.CsvUtil;
|
|
|
|
+import com.jkcredit.traffic.record.util.DivideDataUtil;
|
|
import com.jkcredit.traffic.record.util.FileUtil;
|
|
import com.jkcredit.traffic.record.util.FileUtil;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
+import okhttp3.*;
|
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
|
+import org.apache.spark.sql.Dataset;
|
|
|
|
+import org.apache.spark.sql.Encoders;
|
|
import org.apache.spark.sql.SparkSession;
|
|
import org.apache.spark.sql.SparkSession;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
|
+import org.springframework.scheduling.annotation.AsyncResult;
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
|
|
+import javax.xml.bind.DatatypeConverter;
|
|
import java.io.File;
|
|
import java.io.File;
|
|
|
|
+import java.io.UnsupportedEncodingException;
|
|
|
|
+import java.net.SocketTimeoutException;
|
|
import java.util.ArrayList;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.List;
|
|
|
|
+import java.util.concurrent.Future;
|
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
/**
|
|
/**
|
|
* @author xusonglin
|
|
* @author xusonglin
|
|
@@ -27,9 +43,26 @@ public class TrafficRecordsAnalyseServiceImpl implements TrafficRecordsAnalyseSe
|
|
SparkSession sparkSession;
|
|
SparkSession sparkSession;
|
|
@Autowired
|
|
@Autowired
|
|
AsyncPushTask asyncPushTask;
|
|
AsyncPushTask asyncPushTask;
|
|
|
|
+ @Value("${recordsPush.url}")
|
|
|
|
+ private String recordsPushUrl;
|
|
|
|
+ @Value("${recordsEncrypt.keyId}")
|
|
|
|
+ private String keyId;
|
|
|
|
+ @Value("${recordsEncrypt.metadata}")
|
|
|
|
+ private String metadata;
|
|
|
|
+ @Value("${recordsEncrypt.ivStr}")
|
|
|
|
+ private String ivStr;
|
|
|
|
+ @Value("${recordsEncrypt.algorithm}")
|
|
|
|
+ private String algorithm;
|
|
|
|
+ @Value("${errorRecords.path}")
|
|
|
|
+ private String errorRecordsPath;
|
|
|
|
+ @Value("${tempFolder.path}")
|
|
|
|
+ private String tempFolderPath;
|
|
|
|
+ private static final String MEDIA_TYPE = "application/json;charset=UTF-8";
|
|
|
|
+ private OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
|
|
|
|
|
|
@Override
|
|
@Override
|
|
public CommonResponseObject pushTrafficRecords(String filePath) {
|
|
public CommonResponseObject pushTrafficRecords(String filePath) {
|
|
|
|
+ log.info("开始处理");
|
|
List<File> files = new ArrayList<>();
|
|
List<File> files = new ArrayList<>();
|
|
File folder = new File(filePath);
|
|
File folder = new File(filePath);
|
|
File[] tempList = folder.listFiles();
|
|
File[] tempList = folder.listFiles();
|
|
@@ -38,17 +71,41 @@ public class TrafficRecordsAnalyseServiceImpl implements TrafficRecordsAnalyseSe
|
|
CommonConstant.ERROR_MESSAGE_FILE_NOT_EXIST);
|
|
CommonConstant.ERROR_MESSAGE_FILE_NOT_EXIST);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // 只处理 .csv结尾文件
|
|
for (int i = 0; i < tempList.length; i++) {
|
|
for (int i = 0; i < tempList.length; i++) {
|
|
if (tempList[i].isFile() && tempList[i].getName().split("\\.")[1].equals("csv")) {
|
|
if (tempList[i].isFile() && tempList[i].getName().split("\\.")[1].equals("csv")) {
|
|
files.add(tempList[i]);
|
|
files.add(tempList[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // 遍历每月的数据文件(5个)
|
|
for (int i = 0; i < files.size(); i++) {
|
|
for (int i = 0; i < files.size(); i++) {
|
|
- String fileName = folder.getName() + "_" + i;
|
|
|
|
- asyncPushTask.push(fileName, files.get(i).getAbsolutePath());
|
|
|
|
- }
|
|
|
|
|
|
+ File file = new File(tempFolderPath);
|
|
|
|
+ if (!file.exists()) {
|
|
|
|
+ file.mkdir();
|
|
|
|
+ }
|
|
|
|
+ // 临时文件夹,每份文件一个文件夹
|
|
|
|
+ String tempFolder = tempFolderPath + File.separator + files.get(i).getName().split("\\.")[0];
|
|
|
|
+ try {
|
|
|
|
+ // 大文件拆分,放入临时文件夹
|
|
|
|
+ CsvUtil.splitFile(files.get(i).getAbsolutePath(), 30, tempFolder);
|
|
|
|
+ for (int j = 1; j <= 30; j++) {
|
|
|
|
+ // 数据脱敏
|
|
|
|
+ List<MonthResult> maskResults = desensitize(j + "", tempFolder + "/temp_" + j + ".csv");
|
|
|
|
|
|
|
|
+ // 数据分割,异步推送
|
|
|
|
+ push(maskResults);
|
|
|
|
+ boolean deleteResult = FileUtil.INSTANCE.deleteFile(tempFolder + "/temp_" + j + ".csv");
|
|
|
|
+ log.info("删除临时文件:{}, 删除结果:{}", tempFolder + "/temp_" + j + ".csv", deleteResult);
|
|
|
|
+ maskResults = null;
|
|
|
|
+ }
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ e.printStackTrace();
|
|
|
|
+ }
|
|
|
|
+ boolean deleteResult = FileUtil.INSTANCE.deleteDirectory(tempFolder);
|
|
|
|
+ log.info("删除临时目录:{}, 删除结果:{}", tempFolder, deleteResult);
|
|
|
|
+ }
|
|
|
|
+ log.info("结束处理");
|
|
return new CommonResponseObject().success();
|
|
return new CommonResponseObject().success();
|
|
}
|
|
}
|
|
|
|
|
|
@@ -69,19 +126,174 @@ public class TrafficRecordsAnalyseServiceImpl implements TrafficRecordsAnalyseSe
|
|
}
|
|
}
|
|
|
|
|
|
for (int i = 0; i < files.size(); i++) {
|
|
for (int i = 0; i < files.size(); i++) {
|
|
- List<String> errorRecords = CsvUtil.readCsv(files.get(i).getAbsolutePath());
|
|
|
|
- List<MonthResult> monthResults = new ArrayList<>();
|
|
|
|
- for (String record : errorRecords) {
|
|
|
|
- MonthResult monthResult = JSON.toJavaObject(JSON.parseObject(record), MonthResult.class);
|
|
|
|
- monthResults.add(monthResult);
|
|
|
|
|
|
+ List<MonthResult> errorRecords = getMonthResult("ErrorRecord" +i, files.get(i).getAbsolutePath());
|
|
|
|
+ push(errorRecords);
|
|
|
|
+ }
|
|
|
|
+ return new CommonResponseObject().success();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private List<MonthResult> getMonthResult(String i, String filePath) {
|
|
|
|
+ Dataset<MonthResult> monthResultDataset = sparkSession
|
|
|
|
+ .read()
|
|
|
|
+ .option("header", false)
|
|
|
|
+ .csv(filePath)
|
|
|
|
+ .toDF("month", "vehicleid", "max_exTime", "sum_travel_time", "max_travel_time", "sum_feemileage",
|
|
|
|
+ "sum_fee", "sum_weight_mileage","exTimes_count", "axlecount", "vehicletype",
|
|
|
|
+ "travel_provinces_count", "transtime_count")
|
|
|
|
+ .as(Encoders.bean(MonthResult.class))
|
|
|
|
+ .coalesce(36);
|
|
|
|
+ String tempViewName = "monthDataset" + i;
|
|
|
|
+ monthResultDataset.createOrReplaceTempView(tempViewName);
|
|
|
|
+ Dataset<MonthResult> rows = sparkSession.sql("select * from " + tempViewName + " where sum_travel_time >= 0 "+
|
|
|
|
+ "and max_travel_time >=0" )
|
|
|
|
+ .as(Encoders.bean(MonthResult.class));
|
|
|
|
+// rows.write().csv("/Users/jkxy/Desktop/outPut/test" + System.currentTimeMillis());
|
|
|
|
+ List<MonthResult> results = new ArrayList<>(3000000);
|
|
|
|
+ results = rows.javaRDD().collect();
|
|
|
|
+ sparkSession.sqlContext().dropTempTable(tempViewName);
|
|
|
|
+ return results;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private List<MonthResult> desensitize(String value, String filePath) {
|
|
|
|
+ List<MonthResult> results = getMonthResult(value, filePath);
|
|
|
|
+ log.info("待推送数据数量::{}", results.size());
|
|
|
|
+ // 数据加密分区间
|
|
|
|
+ List<MonthResult> maskResults = new ArrayList<>();
|
|
|
|
+ for (int i = 0; i < results.size(); i++) {
|
|
|
|
+ // 车牌号加密;
|
|
|
|
+ String encryptPlateNumber = vehicleIdEncrypt(results.get(i).getVehicleid());
|
|
|
|
+ if (StringUtils.isBlank(encryptPlateNumber)) {
|
|
|
|
+ log.error("加密失败:{}", results.get(i).toString());
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ results.get(i).setVehicleid(encryptPlateNumber);
|
|
|
|
+ maskResults.add(divideData(results.get(i)));
|
|
|
|
+ }
|
|
|
|
+ log.info("数据脱敏完成数量:{}", results.size());
|
|
|
|
+ return maskResults;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public void push(List<MonthResult> maskResults) {
|
|
|
|
+ int times;
|
|
|
|
+ if (maskResults.size() % 10000 != 0 ) {
|
|
|
|
+ times = (maskResults.size() / 10000) + 1;
|
|
|
|
+ } else {
|
|
|
|
+ times = maskResults.size() / 10000;
|
|
|
|
+ }
|
|
|
|
+ List<List<MonthResult>> partResultsList = new ArrayList<>();
|
|
|
|
+ for (int i = 0; i < times; i++) {
|
|
|
|
+ List<MonthResult> partResults;
|
|
|
|
+ if (i == times-1) {
|
|
|
|
+ partResults = maskResults.subList(i*10000, maskResults.size());
|
|
|
|
+ } else {
|
|
|
|
+ partResults = maskResults.subList(i*10000, (i+1)*10000);
|
|
}
|
|
}
|
|
|
|
+ partResultsList.add(partResults);
|
|
|
|
+ }
|
|
|
|
+ for (List<MonthResult> partResults : partResultsList) {
|
|
try {
|
|
try {
|
|
- asyncPushTask.pushRecords(monthResults);
|
|
|
|
|
|
+ pushRecords(partResults);
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
- log.error("推送异常数据失败:", e);
|
|
|
|
- return new CommonResponseObject().failed();
|
|
|
|
|
|
+ e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- return new CommonResponseObject().success();
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 对vehicle_id字段进行加密
|
|
|
|
+ * @param vehicleId 待加密vehicleId
|
|
|
|
+ * @return 加密后vehicleId
|
|
|
|
+ */
|
|
|
|
+ private String vehicleIdEncrypt(String vehicleId) {
|
|
|
|
+ try {
|
|
|
|
+ byte[] plainData = vehicleId.getBytes("UTF-8");
|
|
|
|
+ byte[] iv = ivStr.getBytes("UTF-8");
|
|
|
|
+ Long startTime = System.currentTimeMillis();
|
|
|
|
+ // 加密
|
|
|
|
+ byte[] cipherData = CipherSuiteUtils.encrypt(plainData, algorithm, keyId, metadata, iv);
|
|
|
|
+ return DatatypeConverter.printHexBinary(cipherData);
|
|
|
|
+ } catch (UnsupportedEncodingException ue) {
|
|
|
|
+ ue.printStackTrace();
|
|
|
|
+ log.error("UnsupportedEncodingException:", ue);
|
|
|
|
+ return "";
|
|
|
|
+ } catch (CipherSuiteException cse) {
|
|
|
|
+ cse.printStackTrace();
|
|
|
|
+ log.error("CipherSuiteException:", cse);
|
|
|
|
+ return "";
|
|
|
|
+ } catch (CipherSuiteMacException csme) {
|
|
|
|
+ csme.printStackTrace();
|
|
|
|
+ log.error("CipherSuiteMacException:", csme);
|
|
|
|
+ return "";
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ e.printStackTrace();
|
|
|
|
+ log.error("Exception:", e);
|
|
|
|
+ return "";
|
|
|
|
+ } catch (Error error) {
|
|
|
|
+ error.printStackTrace();
|
|
|
|
+ log.error("Error:", error);
|
|
|
|
+ return "";
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 对数据脱敏,分区间
|
|
|
|
+ * @param monthResult 待脱敏月结果数据
|
|
|
|
+ * @return 脱敏后月结果数据
|
|
|
|
+ */
|
|
|
|
+ private MonthResult divideData(MonthResult monthResult) {
|
|
|
|
+ monthResult.setExTimes_count(DivideDataUtil.divideExTimesCount(monthResult.getExTimes_count()));
|
|
|
|
+ monthResult.setSum_fee(DivideDataUtil.divideSumFee(monthResult.getSum_fee()));
|
|
|
|
+ monthResult.setSum_travel_time(DivideDataUtil.divideSumTravelTime(monthResult.getSum_travel_time()));
|
|
|
|
+ monthResult.setMax_travel_time(DivideDataUtil.divideMaxTravelTime(monthResult.getMax_travel_time()));
|
|
|
|
+ monthResult.setSum_feemileage(DivideDataUtil.divideSumFeeMileage(monthResult.getSum_feemileage()));
|
|
|
|
+ monthResult.setSum_weight_mileage(DivideDataUtil.divideSumWeightMileage(monthResult.getSum_weight_mileage()));
|
|
|
|
+ return monthResult;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public Future<String> pushRecords(List<MonthResult> resultList) throws InterruptedException {
|
|
|
|
+ MediaType mediaType = MediaType.parse(MEDIA_TYPE);
|
|
|
|
+ RequestBody requestBody = RequestBody.create(mediaType, JSON.toJSONString(resultList));
|
|
|
|
+
|
|
|
|
+ Request okRequest = new Request.Builder()
|
|
|
|
+ .post(requestBody)
|
|
|
|
+ .url(recordsPushUrl)
|
|
|
|
+ .build();
|
|
|
|
+ OkHttpClient client = okHttpClient.newBuilder()
|
|
|
|
+ .connectTimeout(5000, TimeUnit.MILLISECONDS)
|
|
|
|
+ .readTimeout(30000, TimeUnit.MILLISECONDS)
|
|
|
|
+ .writeTimeout(30000, TimeUnit.MILLISECONDS)
|
|
|
|
+ .build();
|
|
|
|
+ String responseContext = "";
|
|
|
|
+ List<MonthResult> errorList = new ArrayList<>();
|
|
|
|
+ try {
|
|
|
|
+ Long startTime = System.currentTimeMillis();
|
|
|
|
+ Response response = client.newCall(okRequest).execute();
|
|
|
|
+ log.info("pushRecords -- costTime:{}", System.currentTimeMillis()-startTime);
|
|
|
|
+ if (response.body() != null) {
|
|
|
|
+ responseContext = response.body().string();
|
|
|
|
+ }
|
|
|
|
+ log.info("pushRecords -- responseContext:{}", responseContext);
|
|
|
|
+ response.close();
|
|
|
|
+ } catch (SocketTimeoutException ste) {
|
|
|
|
+ ste.printStackTrace();
|
|
|
|
+ errorList = resultList;
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ e.printStackTrace();
|
|
|
|
+ }
|
|
|
|
+ CommonResponseObject responseObject = JSON.toJavaObject(JSON.parseObject(responseContext), CommonResponseObject.class);
|
|
|
|
+ if (!responseObject.getCode().equals(CommonConstant.SUCCESS_CODE)) {
|
|
|
|
+ JSONArray responseErrorArray = JSON.parseArray(responseObject.getMessage());
|
|
|
|
+ errorList = responseErrorArray.toJavaList(MonthResult.class);
|
|
|
|
+ }
|
|
|
|
+ List<String> errorData = new ArrayList<>();
|
|
|
|
+ for (MonthResult monthResult : errorList) {
|
|
|
|
+ errorData.add(monthResult.toString());
|
|
|
|
+ }
|
|
|
|
+ if (errorData.size() != 0) {
|
|
|
|
+ // 异常数据写入本地文件
|
|
|
|
+ FileUtil.INSTANCE.addAll(errorData);
|
|
|
|
+ FileUtil.INSTANCE.write(errorRecordsPath+System.currentTimeMillis()+".csv");
|
|
|
|
+ }
|
|
|
|
+ return new AsyncResult<>("推送完成");
|
|
}
|
|
}
|
|
}
|
|
}
|