网约车借鉴-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();

 

 

 

posted @ 2022-08-12 09:53  Jerry&Ming  阅读(39)  评论(0)    收藏  举报