Java责任链模式
顾名思义,责任模式链为请求创建一系列接收者对象。 此模式基于请求的类型将请求的发送方和接收方分离。 这种模式是行为模式。
在这种模式中,通常每个接收器包含对另一个接收器的引用。如果一个对象不能处理请求,则它将相同的对象传递给下一个接收者等等。
servlet中的过滤器,struts2中的拦截器等都是采用了这种设计模式,把多个责任任务绑定在一条链chain上,由chain调用任务的执行,任务实现方法中又包含对chain的调用,这样就实现了任务与任务之间解耦,
/**
* 声明所有的风险因素
*/
public enum RiskFactor {
Area("area"),//地域因素,登陆地所在城市
DEVICE("device"),//登录设备
TOTAL("total"),//登陆总次数
TIMESLOT("timeslot"),//登陆时间段
SIMILARITY("similarity"),//密码相似度
INPUTFEATURE("inputfeature"),//输入特征
SPEED("speed");//位移速度
private String name;//风险因素的名称
//构造
RiskFactor(String name){
this.name = name;
}
}
package com.baizhi.entities;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
/**
* 评估报告,完成每次因子的评估都要更新一次评估报告
* 应⽤信息
* ⽤户唯⼀标识
* 登录地区
* 经纬度
* 登录序列号
* 评估时间
* 输⼊特征
* 地区
* 速度
* 设备
* 习惯
* 次数
* 密码
* QQ zhangsan Beijing 116.20,39.56 UUID
* 2020-03-31
* 10:10:00
* True False True False True False True
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class EvaluateReport implements Serializable {
private String applicationName;//应用信息
private String userIdentify;//y用户唯一标识
private String loginSequence;//登陆序列号
private long evaluateTime;//登陆时间
private String cityName;//登陆地区
private GeoPoint geoPoint;//经纬度
//根据各个风险因子得到的评估因子map,为true则为有风险
private Map metrics = new HashMap<>();
//构造
public EvaluateReport(String applicationName, String userIdentify, String loginSequence, long evaluateTime, String cityName, GeoPoint geoPoint) {
this.applicationName = applicationName;
this.userIdentify = userIdentify;
this.loginSequence = loginSequence;
this.evaluateTime = evaluateTime;
this.cityName = cityName;
this.geoPoint = geoPoint;
//初始化所有风险因子都是false
metrics.put(RiskFactor.AREA,false);
metrics.put(RiskFactor.DEVICE,false);
metrics.put(RiskFactor.SIMILARITY,false);
metrics.put(RiskFactor.SPEED,false);
metrics.put(RiskFactor.TIMESLOT,false);
metrics.put(RiskFactor.INPUTFEATURE,false);
metrics.put(RiskFactor.TOTAL,false);
}
@Override
public String toString(){
//风险报告
String report = metrics.keySet()
.stream()
.sorted((RiskFactor r1, RiskFactor r2) -> (r1.name().compareTo(r2.name())))
.map(r -> (metrics.get(r) + ""))
.reduce((r1, r2) -> (r1 + " " + r2))
.get();
return applicationName+" "+userIdentify+" "+loginSequence+" "+evaluateTime+" "+cityName+ " "+geoPoint.getLongtitude()+","+geoPoint.getLatitude()+" "+report;
}
//写报告,根据风险因子的值
public void signReport(RiskFactor riskFactor,boolean flag){
metrics.put(riskFactor,flag);
}
}
package com.baizhi.evaluate;
import com.baizhi.entities.EvaluateData;
import com.baizhi.entities.EvaluateReport;
import com.baizhi.entities.HistoryData;
import com.baizhi.entities.RiskFactor;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 对每个风险因子进行评估的责任抽象类
* 每个风险因子进行评估都要实现该类
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public abstract class Evaluate {
//风险因子
private RiskFactor riskFactor;
/**
*
* @param evaluateData
* @param historyData
* @param evaluateReport
* @param evaluateChain:驱动下一个Evaluate实例
*/
public abstract void eval(EvaluateData evaluateData, HistoryData historyData, EvaluateReport evaluateReport,
EvaluateChain evaluateChain);
}
package com.baizhi.evaluate;
import com.baizhi.entities.EvaluateData;
import com.baizhi.entities.EvaluateReport;
import com.baizhi.entities.HistoryData;
import java.util.List;
/**
* 责任链类,根据position调用责任,
*/
public class EvaluateChain {
//责任的下标
private Integer position = 0;
//所有需要执行的责任
private List evaluates;
public EvaluateChain(List evaluates) {
this.evaluates = evaluates;
}
/**
*
* @param evaluateData 评估数据,本次登陆的数据
* @param historyData 历史数据
* @param evaluateReport 评估报告
*/
public void doChain(EvaluateData evaluateData, HistoryData historyData, EvaluateReport evaluateReport){
if(position < evaluates.size() - 1){
//如果当前执行任务不是最后一个
//获取责任
Evaluate evaluate = evaluates.get(position);
position++;
//执行任务
evaluate.eval(evaluateData,historyData,evaluateReport,this);
}
}
}
/**
* 记录是用户的登录的历史状态
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class HistoryData implements Serializable {
Set historyCities;//历史登陆所有城市
Set historyDevices;//历史登录设备
Integer currentLoginCount;//当前登录次数
Map> timeslot;//登陆时间段(一周的时间段,(一天的时间段,次数))
Set historyPassword;//历史输入密码集合
List historyFeature;//用户输入特性集合
Long lastLoginTime;//上次登陆时间
GeoPoint lastLoginGeoPoint;//上次登陆经纬度
}
package com.baizhi.evaluate.impl;
import com.baizhi.entities.EvaluateData;
import com.baizhi.entities.EvaluateReport;
import com.baizhi.entities.HistoryData;
import com.baizhi.entities.RiskFactor;
import com.baizhi.evaluate.Evaluate;
import com.baizhi.evaluate.EvaluateChain;
import java.util.Set;
/**
* 异地登陆评估
* 本次登陆不是之前的登陆地
*/
public class AreaEvaluate extends Evaluate {
public AreaEvaluate(){
super(RiskFactor.AREA);
}
@Override
public void eval(EvaluateData evaluateData, HistoryData historyData, EvaluateReport evaluateReport, EvaluateChain evaluateChain) {
//填写评估报告
evaluateReport.signReport(getRiskFactor(),doEval(evaluateData.getCityName(),historyData.getHistoryCities()));
//调用下一个评估
evaluateChain.doChain(evaluateData,historyData,evaluateReport);
}
/**
*
* @param cityName 本次登陆城市
* @param historyCities 历史登陆所有城市
* @return
*/
public Boolean doEval(String cityName, Set historyCities){
if(historyCities == null || historyCities.size() == 0){
//如果本次登陆是第一次
return false;
}else{
return !historyCities.contains(cityName);
}
}
}
package com.baizhi.evaluate.impl;
import com.baizhi.entities.EvaluateData;
import com.baizhi.entities.EvaluateReport;
import com.baizhi.entities.HistoryData;
import com.baizhi.entities.RiskFactor;
import com.baizhi.evaluate.Evaluate;
import com.baizhi.evaluate.EvaluateChain;
import java.util.Set;
/**
* 更换设备评估
* 如果本次登录设备与前几次不一样则认为是存在风险
*/
public class DevideEvaluate extends Evaluate {
public DevideEvaluate(){
super(RiskFactor.DEVICE);
}
@Override
public void eval(EvaluateData evaluateData, HistoryData historyData, EvaluateReport evaluateReport, EvaluateChain evaluateChain) {
//填写报告
evaluateReport.signReport(getRiskFactor(),doEval(evaluateData.getDeviceInformation(),historyData.getHistoryDevices()));
}
/**
*
* @param device 本次登录设备
* @param historyDevices 历史用过的登录设备
*/
public Boolean doEval(String device, Set historyDevices){
if (historyDevices == null || historyDevices.size() == 0){
//如果是第一次登陆
return false;
}else{
return !historyDevices.contains(device);
}
}
}
package com.baizhi.evaluate.impl;
import com.baizhi.entities.EvaluateData;
import com.baizhi.entities.EvaluateReport;
import com.baizhi.entities.HistoryData;
import com.baizhi.entities.RiskFactor;
import com.baizhi.evaluate.Evaluate;
import com.baizhi.evaluate.EvaluateChain;
/**
* 最大登陆次数,超过阈值认定有风险
*/
public class TotalEvaluate extends Evaluate {
//登陆次数阈值
private Integer threshold = 0;
public TotalEvaluate(Integer threshold){
super(RiskFactor.TOTAL);
this.threshold = threshold;
}
@Override
public void eval(EvaluateData evaluateData, HistoryData historyData, EvaluateReport evaluateReport, EvaluateChain evaluateChain) {
//填写报告
evaluateReport.signReport(getRiskFactor(),doEval(historyData.getCurrentLoginCount()));
evaluateChain.doChain(evaluateData,historyData,evaluateReport);
}
/**
*
* @param currentLoginCount 当前登录次数
* @return
*/
public Boolean doEval(Integer currentLoginCount){
if(currentLoginCount == null){
//第一次登陆
return false;
}
return currentLoginCount - threshold > 0;
}
}
package com.baizhi.evaluate.impl;
import com.baizhi.entities.EvaluateData;
import com.baizhi.entities.EvaluateReport;
import com.baizhi.entities.HistoryData;
import com.baizhi.entities.RiskFactor;
import com.baizhi.evaluate.Evaluate;
import com.baizhi.evaluate.EvaluateChain;
import java.text.DecimalFormat;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 登陆时间段习惯评估
*/
public class TimeSlotEvaluate extends Evaluate {
private Integer threshold = 20;//设置登陆多少次之后再进行评估
public TimeSlotEvaluate(Integer threshold){
super(RiskFactor.TIMESLOT);
this.threshold = threshold;
}
@Override
public void eval(EvaluateData evaluateData, HistoryData historyData, EvaluateReport evaluateReport, EvaluateChain evaluateChain) {
evaluateReport.signReport(RiskFactor.TIMESLOT,doEval(evaluateData.getEvaluateTime(),historyData.getTimeslot()));
evaluateChain.doChain(evaluateData,historyData,evaluateReport);
}
public Boolean doEval(Long loginTime, Map> timeslot){
String[] WEEKS={"星期日","星期一","星期二","星期三","星期四","星期五","星期六"};
Calendar c = Calendar.getInstance();
c.setTimeInMillis(loginTime);
String dayOfWeek = WEEKS[c.get(Calendar.DAY_OF_WEEK) - 1];//星期几
DecimalFormat decimalFormat = new DecimalFormat("00");
String hourOfDay = decimalFormat.format(c.get(Calendar.HOUR_OF_DAY));//哪个小时
//用户第一次登陆
if(timeslot == null || timeslot.size() == 0){
return false;
}
//如果历史时段不包含登陆时段
if(!timeslot.containsKey(dayOfWeek)){
//拿到总登陆次数,判断是否小于阈值
Integer count = timeslot.entrySet()
.stream()
.map(t -> (t.getValue().entrySet()
.stream()
.map(k -> k.getValue())
.reduce((v1, v2) -> (v1 + v2))
.get()))
.reduce((v1, v2) -> v1 + v2)
.get();
return count >= threshold;
}else{
//如果历史时段包含登陆时段
//获取当天登陆的时段信息
Map hours = timeslot.get(dayOfWeek);
//查看当天登陆时段信息是否包含当前登录的小时时段
if (!hours.containsKey(hourOfDay)){
//如果不包含
return true;
}else{
//包含----判断是否处于当天登陆时段的前几个活跃阶段
Integer hourCount = hours.get(hourOfDay);//当前登陆时段次数
List hourList = hours.entrySet()
.stream()
.map(t -> t.getValue())
.sorted()
.collect(Collectors.toList());//拿到当天登陆时段排序后的次数列表
//获取阈值
Integer thresholdCount = hourList.get(hourList.size() * 2 / 3);
return hourCount < thresholdCount;
//计算包含阈值的所有时段,看一下当前登录时段是否在该时段之内
/*List hourList = hours.entrySet()
.stream()
.filter(t -> t.getValue() > hours.size() * 2 / 3)
.map(t->t.getKey())
.collect(Collectors.toList());
return !hourList.contains(hourOfDay);*/
}
}
}
}
详情查看https://blog.csdn.net/qq_38040765/article/details/105253449
评估数据:获取乱序的密码
历史数据: 留存⽤户的所有的历史登录乱序密码Set
package com.baizhi.evaluate.impl;
import com.baizhi.entities.EvaluateData;
import com.baizhi.entities.EvaluateReport;
import com.baizhi.entities.HistoryData;
import com.baizhi.entities.RiskFactor;
import com.baizhi.evaluate.Evaluate;
import com.baizhi.evaluate.EvaluateChain;
import java.util.*;
import java.util.stream.Collectors;
/**
* 用户登陆的密码相似度评估
*/
public class SimilarityEvaluate extends Evaluate{
//相似度阈值,相似度大于该阈值,认定没有风险,反之亦然
private Double thresholdSimilarity;
//构造
public SimilarityEvaluate(Double thresholdSimilarity){
super(RiskFactor.SIMILARITY);
this.thresholdSimilarity = thresholdSimilarity;
}
@Override
public void eval(EvaluateData evaluateData, HistoryData historyData, EvaluateReport evaluateReport, EvaluateChain evaluateChain) {
evaluateReport.signReport(getRiskFactor(),doEval(evaluateData.getOrdernessPassword(),historyData.getHistoryPassword()));
evaluateChain.doChain(evaluateData,historyData,evaluateReport);
}
/**
* 逻辑执行,
* @param loginPassword
* @param historyPassword
* @return 返回true,代表输入密码差异过大,登陆有风险
*/
public Boolean doEval(String loginPassword, Set historyPassword){
//如果用户是第一次登陆
if(historyPassword == null || historyPassword.size() == 0){
return false;
}
//获取字符袋
Set wordBag = new HashSet<>();
historyPassword.stream()
.forEach(c->{
char[] chars = c.toCharArray();
for (char aChar : chars) {
wordBag.add(aChar);//加入到词袋中
}
});
//富华维度,可以把当前输入的字符页添加到字符袋中
char[] chars = loginPassword.toCharArray();
for (Character aChar : chars) {
wordBag.add(aChar);
}
//把字符袋中的字符排序
List wordList = wordBag.stream()
.sorted()
.collect(Collectors.toList());
System.out.println("字符袋:" + wordList);
//把历史密码转化为向量
List historyVector = historyPassword.stream()
.map(p -> transformStringToVector(wordList, p))
.collect(Collectors.toList());
//把当前密码转化为向量
Integer[] loginVector = transformStringToVector(wordList, loginPassword);
//获取没有风险的相似度集合
List similarities = historyVector.stream()
.map(h -> {
Double similarity = calculateSimilarity(h, loginVector);
System.out.println("相似度:" + similarity);
return similarity;
})
.filter(s -> s > thresholdSimilarity)//过滤出有风险的相似度
.collect(Collectors.toList());
return similarities.size() == 0;
}
/**
* 把密码乱序转化为向量
* @param wordBag 所有历史密码组成的排序好的字符袋
* @param password 要转化的密码乱序
* @return 转化后的向量
*/
public Integer[] transformStringToVector(List wordBag,String password){
//转化后的向量
Integer[] vector = new Integer[wordBag.size()];
//把密码乱序转化为map集合(字符,个数)
Map passCharMap = new HashMap<>();
char[] chars = password.toCharArray();
//遍历字符数组
for (Character c : chars) {
//初始数量
Integer count = 1;
//如果map中有该字符
if (passCharMap.containsKey(c)){
count += passCharMap.get(c);
}
//放入到map中
passCharMap.put(c,count);
}
//遍历字符袋,如果密码map中有该字符,向量该字符特征的属性就+1,
for (int i = 0; i < wordBag.size(); i++) {
Character c = wordBag.get(i);
vector[i] = passCharMap.containsKey(c) ? passCharMap.get(c) : 0;
}
return vector;
}
/**
* 计算两个向量的相似度
* @param v1 v1向量
* @param v2 v2向量
* @return 返回相似度
*/
public Double calculateSimilarity(Integer[] v1,Integer[] v2){
//计算两个向量的夹角余弦,向量点乘/模的乘积
//向量的点乘:v1 * v2
Double dotMulti = 0.0;
for (int i = 0; i < v1.length; i++) {
dotMulti += v1[i] * v2[i];
}
//计算两个向量的模的平方
Integer length1 = Arrays.stream(v1)
.map(v -> v * v)
.reduce((i1, i2) -> i1 + i2)
.get();
Integer length2 = Arrays.stream(v2)
.map(v -> v * v)
.reduce((i1, i2) -> i1 + i2)
.get();
//计算余弦
return dotMulti / (Math.sqrt(length1) * Math.sqrt(length2));
}
public static void main(String[] args) {
//乱序密码
/*String pwd="123456abc";
char[] chars = pwd.toCharArray();
List characters=new ArrayList<>();
for (char aChar : chars) {
characters.add(aChar);
}
String ordernessPassword = characters.stream()
.sorted((o1, o2)-> new Integer[]{-1, 0, 1}[new Random().nextInt(3)])
.map(c -> c + "")
.reduce((v1, v2) -> v1 + v2)
.get();
System.out.println(ordernessPassword);*/
//测试密码相似度
SimilarityEvaluate similarityEvaluate = new SimilarityEvaluate(0.98);
HashSet pwds = new HashSet<>();
pwds.add("456vcfg");
pwds.add("ct12345");
pwds.add("hjk8901");
System.out.println(similarityEvaluate.doEval("vcfg451",pwds));
}
}
详情https://blog.csdn.net/qq_38040765/article/details/105278983
package com.baizhi.evaluate.impl;
import com.baizhi.entities.EvaluateData;
import com.baizhi.entities.EvaluateReport;
import com.baizhi.entities.HistoryData;
import com.baizhi.entities.RiskFactor;
import com.baizhi.evaluate.Evaluate;
import com.baizhi.evaluate.EvaluateChain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* 用户登陆输入特性
*/
public class FeatureEvaluate extends Evaluate{
public FeatureEvaluate(){
super(RiskFactor.INPUTFEATURE);
}
@Override
public void eval(EvaluateData evaluateData, HistoryData historyData, EvaluateReport evaluateReport, EvaluateChain evaluateChain) {
evaluateReport.signReport(getRiskFactor(),doEval(evaluateData.getInputFeatures(),historyData.getHistoryFeature()));
evaluateChain.doChain(evaluateData,historyData,evaluateReport);
}
/**
* 1.计算圆⼼中点
* 2.计算两两特征距离
* 3.对距离进⾏排序(升序),取2/3处作为评估距离阈值 - threshold
* 4.计算当前输⼊的特征距离中⼼点距离d
* @param loginFeature 登陆特性
* @param historyFeature 历史输入特性
* @return 有风险,true
*/
public Boolean doEval(Double[] loginFeature, List historyFeature){
//保证历史数据至少有两条
if(historyFeature == null || historyFeature.size() < 2)
return false;
//1.计算圆心,所有坐标均值
List centerVector = Arrays.stream(historyFeature.stream()
.reduce((v1, v2) -> {
Double[] sum = new Double[v1.length];//历史数据每个维度的总和数组
for (int i = 0; i < v1.length; i++) {
if (sum[i] == null)
sum[i] = 0.0;
sum[i] += v1[i] + v2[i];
}
return sum;
}).get())//所有坐标值的和
.map(s -> (s * 1.0) / historyFeature.size())
.collect(Collectors.toList());
//圆心字符串形式
String strCenterVector = String.join(",", centerVector.stream()
.map(s -> s + "")
.collect(Collectors.toList()));
System.out.println("圆心:"+strCenterVector);//展示圆心
//2.计算两两特性距离
List distanceList = new ArrayList<>();
for (int i = 0; i < historyFeature.size(); i++) {
for (int j = i + 1; j < historyFeature.size(); j++) {
distanceList.add(distance(historyFeature.get(i),historyFeature.get(j)));
}
}
System.out.println("两两距离:"+distanceList);//展示距离
//3.对距离进⾏排序(升序),取2/3处作为评估距离阈值 - threshold
List sortedDistanceList = distanceList.stream()
.sorted()
.collect(Collectors.toList());
Double thresholdDistance = sortedDistanceList.get(sortedDistanceList.size() * 2 / 3);
System.out.println("半径:" + thresholdDistance);
//4.计算当前输入特性与圆心的距离
Double distance = distance(loginFeature, centerVector.toArray(new Double[0]));
System.out.println("当前输入与圆心的距离:" + distance);
return distance > thresholdDistance;
}
/**
* 计算两点距离
* @param v1
* @param v2
* @return
*/
public Double distance(Double[] v1,Double[] v2){
Double sum = 0.0;
for (int i = 0; i < v1.length; i++) {
sum += (v1[i] - v2[i]) * (v1[i] - v2[i]);
}
return Math.sqrt(sum);
}
//测试
public static void main(String[] args) {
FeatureEvaluate inputFeatureEvaluate = new FeatureEvaluate();
ArrayList latestInputFeatures = new ArrayList<>();
latestInputFeatures.add(new Double[]{1000.0,1100.0,1800.0});
latestInputFeatures.add(new Double[]{1100.0,1120.0,1750.0});
latestInputFeatures.add(new Double[]{950.0,1250.0,2000.0});
latestInputFeatures.add(new Double[]{1200.0,1050.0,1900.0});
latestInputFeatures.add(new Double[]{1400.0,800.0,2500.0});
inputFeatureEvaluate.doEval(new Double[]{1100.0,1000.0,1750.0},latestInputFeatures);
}
}
详情:https://blog.csdn.net/qq_38040765/article/details/105280508
import com.baizhi.entities.*;
import com.baizhi.evaluate.Evaluate;
import com.baizhi.evaluate.EvaluateChain;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import static java.lang.Math.*;
/**
* 异地登录判断
* 位移速度计算
*/
public class SpeedEvaluate extends Evaluate {
private static final Double EARTH_RADIUS = 6371.393;//地球半径
private Double thresholdSpeed;//阈值速度,超过该速度被认定为有盗号风险
public SpeedEvaluate(Double thresholdSpeed){
super(RiskFactor.SPEED);
this.thresholdSpeed = thresholdSpeed;
}
@Override
public void eval(EvaluateData evaluateData, HistoryData historyData, EvaluateReport evaluateReport, EvaluateChain evaluateChain) {
evaluateReport.signReport(getRiskFactor(),doEval(evaluateData.getEvaluateTime(),evaluateData.getGeoPoint(),
historyData.getLastLoginTime(),historyData.getLastLoginGeoPoint()));
evaluateChain.doChain(evaluateData,historyData,evaluateReport);
}
/**
* 登陆位移速度计算
* @param loginTime 本次登陆时间
* @param loginGeoPoint 本次登陆经纬度
* @param lastLoginTime 上次登陆时间
* @param lastLoginGeoPoint 上次经纬度
*/
public Boolean doEval(Long loginTime, GeoPoint loginGeoPoint,Long lastLoginTime,GeoPoint lastLoginGeoPoint){
//用户第一次登陆
if (lastLoginTime == null){
return false;
}
//计算两地球面距离
Double distance = calculateDistanceByGeoPoint(loginGeoPoint, lastLoginGeoPoint);
System.out.println("两地距离:" + distance);
//计算速度
Double speed = distance / (((loginTime - lastLoginTime) * 1.0 )/(3600*1000));
System.out.println("速度:" + speed);
return speed > thresholdSpeed;
}
/**
* 根据两地经纬度计算距离
* @param g1
* @param g2
* @return
*/
public Double calculateDistanceByGeoPoint(GeoPoint g1,GeoPoint g2){
//把经纬度转换为弧度制
double w1 = toRadians(g1.getLatitude());//纬度1
double j1 = toRadians(g1.getLongtitude());//经度1
double w2 = toRadians(g2.getLatitude());//纬度2
double j2 = toRadians(g2.getLongtitude());//经度2
//利用公式计算两地距离
return EARTH_RADIUS * acos(cos(w1)*cos(w2)*cos(j1 - j2) + sin(w1)*sin(w2));
}
//测试
public static void main(String[] args) throws ParseException {
SpeedEvaluate speedEvaluate = new SpeedEvaluate(600.0);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
long evaluateTime=sdf.parse("2020-04-01 10:10:00").getTime();
GeoPoint p1=new GeoPoint();
p1.setLongtitude(116.2317);//北京
p1.setLatitude(39.5427);
long lastLoginTime=sdf.parse("2020-04-01 08:00:00").getTime();
GeoPoint p2=new GeoPoint();
p2.setLongtitude(114.14);//郑州
p2.setLatitude(34.16);
speedEvaluate.doEval(evaluateTime,p1,lastLoginTime,p2);
}
}
`