网约车借鉴-02
1、指定map的扩张性。默认2的n次幂
Map<String, Object> map = new HashMap<>(4);
2.通过map构建请求参数:也可以直接拼接为请求参数
Map<String, Object> map = new HashMap<>(4); map.put("originLongitude", 2222); map.put("originLatitude", 333); map.put("destinationLongitude",444); map.put("destinationLatitude", 555); String param = map.keySet().stream().map(k -> k + "={" + k + "}").collect(Collectors.joining("&")); ---------- originLatitude={originLatitude}&destinationLatitude={destinationLatitude}&originLongitude={originLongitude}&destinationLongitude={destinationLongitude}
3.将responseResult解析为指定的类
RestTemplate 指定远程调用
@Configuration public class RestTemplateHepler { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } /** * 将ResponseResult解析为指定的类 * * @param result ResponseResult * @param clazz 指定的类 * @param <T>指定的类类型 * @return 指定的类的实例 * @throws Exception 异常 */ public static <T> T parse(@Nullable ResponseResult result, Class<T> clazz) throws Exception { ObjectMapper mapper = new ObjectMapper(); return mapper.readValue(mapper.writeValueAsString(result != null ? result.getData() : null), clazz); }
4.:如果list集合不为空,将list集合赋值给newList;如果list集合为空创建一个空对象集合赋值给newList,保证list集合永远不为空,也就避免了空指针异常。
List<String> list = null; List<String> newList = Optional.ofNullable(list).orElse(Lists.newArrayList()); newList.forEach(x -> System.out.println(x));
5.调用服务类接口封装类
@Component @ConfigurationProperties(prefix = "services", ignoreInvalidFields = true)-----ignoreInvalidFields默认是false,当绑定时候出现错误是否要忽略,这种错误一般是类型转换错误 @Setter public class ServiceAddress { private List<Map<String, String>> address = new ArrayList<>(); private static final String ACCOUNT_SERVER_URL = "account"; private static final String MAP_SERVER_URL = "map"; private static final String ORDER_SERVER_URL = "order"; private static final String MESSAGE_SERVER_URL = "message"; private static final String PAY_SERVER_URL = "pay"; /** * 获取服务接口地址 * * @param key 接口名 * @return 地址 */ public String get(String key) { return address.stream().filter(m -> m.containsKey(key)).findFirst().orElse(new HashMap<>(0)).get(key);
----如果没有查询到值 就用orElse的值 返回为null 指定了hashMap的长度 } /** * 获取账号服务地址 * * @return 账号服务地址 */ public String getAccountAddress() { return get(ACCOUNT_SERVER_URL); }
yaml的配置文件 list里面赋值map
services:
address:
- account: http://localhost:8081
- dispatch: http://localhost:8082
- order: http://localhost:8083
- map: http://localhost:8084
- message: http://localhost:8085
- netty: http://localhost:8086
- pay: http://localhost:8087
- valuation: http://localhost:8088
- file: http://localhost:8089
- government: http://localhost:9999
6. 判断两个对象是否相等(存在的问题:对于以上的比较方法,如果需要根据不同类型考虑不同的方法,更重要的是,需要进行null对象判断,相对比较麻烦)
ObjectUtils.nullSafeEquals(o1,o2)
7.时间片的类:运用了方法的重载 返回Duration类,专门用来计算时间的差异 并赋值:https://blog.csdn.net/xxdw1992/article/details/112761525
@Data @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) public class TimeSlice { private LocalDateTime x; private LocalDateTime y; /** * 判断两个时间段是否有交集 * * @param m 指定时间段 * @return 有交集返回true,否则返回false */ public boolean isTimeOverlapped(TimeSlice m) { return isTimeOverlapped(m.getX(), m.getY()); } /** * 判断两个时间段是否有交集 * * @param a 指定时间段的起始时间 * @param b 指定时间段的结束时间 * @return 有交集返回true,否则返回false */ public boolean isTimeOverlapped(LocalDateTime a, LocalDateTime b) { if (a.isAfter(b) || x.isAfter(y)) { throw new RuntimeException("时间段的起始时间大于结束时间错误"); } return !(y.isBefore(a) || b.isBefore(x)); } /** * 获取两个时间段的间隔 * * @param a 指定时间段的起始时间 * @param b 指定时间段的结束时间 * @return 时间段的间隔 */ public Duration until(LocalDateTime a, LocalDateTime b) { if (!isTimeOverlapped(a, b)) { return Duration.ofSeconds(0); } LocalDateTime m = a.isAfter(x) ? a : x; LocalDateTime n = b.isBefore(y) ? b : y; return Duration.between(m, n); } /** * 获取两个时间段的间隔 * * @param m 指定时间段 * @return 时间段的间隔 */ public Duration until(TimeSlice m) { return until(m.getX(), m.getY()); }
8.日期时间的计算:当前日期+double类型的取整的秒数(plusSeconds) 用添加秒数这个方法
totalSlice.setY(totalSlice.getX().plusSeconds((long) Math.ceil(driveMeter.getTotalTime())));
9.在list.Stream的时候一定要判断是否为空
Optional.ofNullable(driveMeter.getRule().getPriceRule().getTimeRules()).orElse(new ArrayList<>()).stream().map(r -> {
10. 内部类
针对每个类型都建了相应的DTO,因为这个DTO在其他业务也用不上,而且以后文件类型还有增加的话,那DTO也会增加,仅仅因为这单个小功能产生这么多利用率不高的DTO着实不太好(论项目是如何变臃肿的),
所以我想到了一个办法,建一个外部类,然后将这些文件对应的DTO作为内部类放在里面。
因为普通的内部类持有外部类的引用,如果内部类一直在执行,则外部类就不会被GC回收,如果外部类中含有大量资源,有可能会导致内存泄漏。所以我们可以建一个静态内部类
11. 也可以用作基本类型的判断
public double getTotalDistance() { Double meters = 0D; switch (chargingCategoryEnum) { case Forecast: meters = route.getDistance(); break; case Settlement: case RealTime: meters = distance.getDistance(); break; default: break; } return Optional.ofNullable(meters).orElse(0D); }
12.关于新建类的字段问题
我们在新建类的时候,一般用包装类
13.根据duration计算天数
double totalSeconds = Duration.between(totalSlice.getX(), totalSlice.getY()).getSeconds();---得到两个时间段的秒数 int totalDays = (int) Math.ceil(totalSeconds / Duration.ofDays(1).getSeconds()) + 1;--根据秒数相处整形+1
14. 将Date类型转换为localDateTime
LocalDateTime startTime = UnitConverter.dateToLocalDateTime(driveMeter.getOrder().getReceivePassengerTime());
15. 计算相差天数
long totalSeconds = Duration.between(startTime, endTime).toMillis(); long intervalSeconds = Duration.ofDays(1).minusSeconds(1).toMillis();---一天的秒数-1*1000得到毫秒数 //计算次数 int times = (int) Math.ceil(1.0 * totalSeconds / intervalSeconds);--double/long 取整
16.比较两个数的大小
是Math.min(a, b)函数么。
如果是这个函数,意思就是比较a,b的大小,返回值为小的那个数。
17. map的使用。再循环放入map中参数的时候,要清空map
map.clear();
18. 循环便利设置结果时 空指针异常的判断
if (null == distance || null == distance.getDistance()) { throw new Exception("distance内容为空:" + result); } result.setDistance(result.getDistance() + distance.getDistance());
19.list转换为map存储
// 结果转换为Map, key 领料单+行明细 public Map<String, StoMaterial> resultToMap() { if (stoResult == null || CollectionUtils.isEmpty(stoResult.getItems())) { return null; } return stoResult.getItems().stream().collect(Collectors.toMap( item -> String.format("%s-%s", item.getOrderdiscription(), item.getStoorderitem()), item -> item)); }
20. 异步返回结果之 CompletableFuture使用
CompletableFuture能够主动设置计算的结果值(主动终结计算过程,即completable),从而在某些场景下主动结束阻塞等待。而Future由于不能主动设置计算结果值,一旦调用get()进行阻塞等待,
要么当计算结果产生,要么超时,才会返回。
CompletableFuture实现了Future
接口,并在此基础上进行了丰富的扩展,完美弥补了Future
的局限性,同时CompletableFuture
实现了对任务编排的能力。借助这项能力,可以轻松地组织不同任务的运行顺序、规则以及方式。
CompletableFuture<String> future = CompletableFuture.supplyAsync(()->{ try{ Thread.sleep(1000L); return "test"; } catch (Exception e){ return "failed test"; } }); future.complete("manual test"); System.out.println(future.join());
则可以通过get或join获取最终计算结果
如果使用该值得时候,没有计算出来,则会阻塞当前的线程。
自测结果如下:
public static void main(String[] args) throws Exception{ System.out.println(System. currentTimeMillis ()); CompletableFuture<String> future = CompletableFuture.supplyAsync(()->{ try{ Thread.sleep(5000L); return "test"; } catch (Exception e){ return "failed test"; } }); System.out.println(System. currentTimeMillis ()); Thread.sleep(2000l); System.out.println(System.currentTimeMillis()); System.out.println("zhge fangfa zhixingl laingmaio"); Thread.sleep(2000l); System.out.println("再次执行两秒"); String join = future.join(); System.out.println("---------"); System.out.println(System. currentTimeMillis ()); System.out.println("777777"); System.out.println(System. currentTimeMillis ()); System.out.println(join);
3. thenApply和thenCompose的区别 thenApply()转换的是泛型中的类型,相当于将CompletableFuture<T> 转换生成新的CompletableFuture<U> thenCompose()用来连接两个CompletableFuture,是生成一个新的CompletableFuture。 thenCombine会在两个任务都执行完成后,把两个任务的结果合并。
注意:
- 两个任务中只要有一个执行异常,则将该异常信息作为指定任务的执行结果。
- 两个任务是并行执行的,它们之间并没有先后依赖顺序。
通过在启动类上 添加注解:@EnableAsync 注解开启异步功能。 在需要异步执行的方法上面加上@Async注解。 用这个方法同步执行两个任务
返回feture可以通过get获取返回值
public Map<Long, List> asyncProcess(List<BindDeviceDO> bindDevices,List<BindStaffDO> bindStaffs, String dccId) { Map<Long, List> finalMap =null; // 返回值: Future<Map<Long, List>> asyncResult = MyService.queryMap(ids); try { finalMap = asyncResult.get(); } catch (Exception e) { ... } return finalMap; }
返回CompletableFuture 可以通过.join方法
同时执行两个线程,并把两个任务结果合并 通过join方法
//分段计价任务 CompletableFuture<List<OrderRulePriceDetail>> calcPriceDetailFuture = valuationTask.calcDetailPrice(driveMeter); //基础计价任务 CompletableFuture<OrderRulePrice> calcPriceFuture = valuationTask.calcMasterPrice(driveMeter); //计算最终价格 BigDecimal price = calcPriceDetailFuture.thenCombine(calcPriceFuture, (details, master) -> { //计算其他价格 valuationTask.calcOtherPrice(driveMeter, master, details); //计算价格合计 BigDecimal totalPrice = PriceHelper.add(master.getBasePrice(), master.getNightPrice(), master.getBeyondPrice(), master.getPathPrice(), master.getDurationPrice()); //最低消费补足 master.setSupplementPrice(BigDecimal.ZERO); if (totalPrice.compareTo(master.getLowestPrice()) < 0) { master.setSupplementPrice(PriceHelper.subtract(master.getLowestPrice(), totalPrice)); totalPrice = master.getLowestPrice(); } //动态调价 DiscountPrice discount = valuationTask.calcDiscount(driveMeter); master.setDynamicDiscount(BigDecimal.ZERO); master.setDynamicDiscountRate(0D); if (null != discount) { master.setDynamicDiscountRate(discount.getDiscount()); if (discount.getDiscount() < 0 || discount.getDiscount() > 1) { throw new RuntimeException(ERR_DISCOUNT_RATE_RANGE); } master.setDynamicDiscount(PriceHelper.min(discount.getDiscountMaxPrice(), BigDecimal.valueOf(1 - discount.getDiscount()).multiply(totalPrice))); } totalPrice = PriceHelper.subtract(totalPrice, master.getDynamicDiscount()); master.setTotalPrice(totalPrice); return master.getTotalPrice(); }).join();
本文来自博客园,作者:Jerry&Ming,转载请注明原文链接:https://www.cnblogs.com/jerry-ming/articles/16572667.html