From 8d269a65a5d35cfe38dd54c2b677b6e27076a19b Mon Sep 17 00:00:00 2001
From: lixiaoping <李小平17773406760@189.cn>
Date: Mon, 21 Nov 2022 10:57:07 +0800
Subject: [PATCH 01/10] =?UTF-8?q?=E9=83=A8=E5=88=86=E5=8A=9F=E8=83=BD?=
=?UTF-8?q?=E9=87=8D=E6=9E=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pom.xml | 30 +-
.../liteflow/example/bean/PriceCalcReqVO.java | 69 +----
.../liteflow/example/bean/PriceStepVO.java | 84 ++----
.../liteflow/example/bean/ProductPackVO.java | 238 ++++++----------
.../liteflow/example/component/CheckCmp.java | 21 +-
.../liteflow/example/component/CouponCmp.java | 35 +--
.../example/component/MemberDiscountCmp.java | 12 +-
.../example/component/NodeIdConstant.java | 34 +++
.../example/component/PriceStepInitCmp.java | 60 ++--
.../component/PromotionConvertCmp.java | 80 +++---
.../example/component/SlotInitCmp.java | 25 +-
.../example/component/StepPrintCmp.java | 37 +--
.../liteflow/example/enums/PriceTypeEnum.java | 78 +++--
.../liteflow/example/slot/PriceContext.java | 266 ++++++++----------
14 files changed, 467 insertions(+), 602 deletions(-)
create mode 100644 src/main/java/com/yomahub/liteflow/example/component/NodeIdConstant.java
diff --git a/pom.xml b/pom.xml
index f2f5e22..680faef 100644
--- a/pom.xml
+++ b/pom.xml
@@ -16,6 +16,8 @@
1.8
+ 2.9.3
+ 2.0.19
3.4
4.1
@@ -26,6 +28,17 @@
spring-boot-starter-web
+
+ org.springframework.boot
+ spring-boot-starter-thymeleaf
+
+
+
+ com.yomahub
+ liteflow-spring-boot-starter
+ ${liteflow.version}
+
+
org.apache.commons
commons-lang3
@@ -38,21 +51,14 @@
${commons-collections.version}
-
- com.yomahub
- liteflow-spring-boot-starter
- 2.9.2
-
-
- org.springframework.boot
- spring-boot-starter-thymeleaf
-
-
-
com.alibaba
fastjson
- 1.2.83
+ ${fastjson.version}
+
+
+ org.projectlombok
+ lombok
diff --git a/src/main/java/com/yomahub/liteflow/example/bean/PriceCalcReqVO.java b/src/main/java/com/yomahub/liteflow/example/bean/PriceCalcReqVO.java
index cd448b8..a99f03e 100644
--- a/src/main/java/com/yomahub/liteflow/example/bean/PriceCalcReqVO.java
+++ b/src/main/java/com/yomahub/liteflow/example/bean/PriceCalcReqVO.java
@@ -3,7 +3,19 @@ package com.yomahub.liteflow.example.bean;
import com.yomahub.liteflow.example.enums.OrderChannelEnum;
import java.util.List;
-
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * 订单价格计算请求参数
+ *
+ * @author bryan.zhang
+ */
+
+@ToString
+@Getter
+@Setter
public class PriceCalcReqVO {
private Long id;
@@ -38,59 +50,4 @@ public class PriceCalcReqVO {
*/
private Long couponId;
- public Long getId() {
- return id;
- }
-
- public void setId(Long id) {
- this.id = id;
- }
-
- public String getOrderNo() {
- return orderNo;
- }
-
- public void setOrderNo(String orderNo) {
- this.orderNo = orderNo;
- }
-
- public List getProductPackList() {
- return productPackList;
- }
-
- public void setProductPackList(List productPackList) {
- this.productPackList = productPackList;
- }
-
- public OrderChannelEnum getOrderChannel() {
- return orderChannel;
- }
-
- public void setOrderChannel(OrderChannelEnum orderChannel) {
- this.orderChannel = orderChannel;
- }
-
- public String getMemberCode() {
- return memberCode;
- }
-
- public void setMemberCode(String memberCode) {
- this.memberCode = memberCode;
- }
-
- public Long getCouponId() {
- return couponId;
- }
-
- public void setCouponId(Long couponId) {
- this.couponId = couponId;
- }
-
- public boolean isOversea() {
- return oversea;
- }
-
- public void setOversea(boolean oversea) {
- this.oversea = oversea;
- }
}
diff --git a/src/main/java/com/yomahub/liteflow/example/bean/PriceStepVO.java b/src/main/java/com/yomahub/liteflow/example/bean/PriceStepVO.java
index 108c723..4455073 100644
--- a/src/main/java/com/yomahub/liteflow/example/bean/PriceStepVO.java
+++ b/src/main/java/com/yomahub/liteflow/example/bean/PriceStepVO.java
@@ -3,93 +3,51 @@ package com.yomahub.liteflow.example.bean;
import com.yomahub.liteflow.example.enums.PriceTypeEnum;
import java.math.BigDecimal;
-
+import lombok.Builder;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.ToString;
+
+/**
+ * 订单价格计算中间步骤
+ *
+ * @author bryan.zhang
+ */
+
+@Getter
+@RequiredArgsConstructor
+@ToString
+@Builder
public class PriceStepVO {
/**
* 价格类型
*/
- private PriceTypeEnum priceType;
+ private final PriceTypeEnum priceType;
/**
* 价格类型关联id
*/
- private String extId;
+ private final String extId;
/**
* 上一步的订单总价格
*/
- private BigDecimal prePrice;
+ private final BigDecimal prePrice;
/**
* 价格的变动值
*/
- private BigDecimal priceChange;
+ private final BigDecimal priceChange;
/**
* 这步价格计算后的订单总价格
*/
- private BigDecimal currPrice;
+ private final BigDecimal currPrice;
/**
* 价格步骤描述
*/
- private String stepDesc;
-
- public PriceStepVO(PriceTypeEnum priceType, String extId, BigDecimal prePrice, BigDecimal priceChange, BigDecimal currPrice, String stepDesc) {
- this.priceType = priceType;
- this.extId = extId;
- this.prePrice = prePrice;
- this.priceChange = priceChange;
- this.currPrice = currPrice;
- this.stepDesc = stepDesc;
- }
-
- public PriceTypeEnum getPriceType() {
- return priceType;
- }
-
- public void setPriceType(PriceTypeEnum priceType) {
- this.priceType = priceType;
- }
-
- public String getExtId() {
- return extId;
- }
-
- public void setExtId(String extId) {
- this.extId = extId;
- }
-
- public BigDecimal getPrePrice() {
- return prePrice;
- }
-
- public void setPrePrice(BigDecimal prePrice) {
- this.prePrice = prePrice;
- }
-
- public BigDecimal getPriceChange() {
- return priceChange;
- }
-
- public void setPriceChange(BigDecimal priceChange) {
- this.priceChange = priceChange;
- }
-
- public BigDecimal getCurrPrice() {
- return currPrice;
- }
-
- public void setCurrPrice(BigDecimal currPrice) {
- this.currPrice = currPrice;
- }
-
- public String getStepDesc() {
- return stepDesc;
- }
+ private final String stepDesc;
- public void setStepDesc(String stepDesc) {
- this.stepDesc = stepDesc;
- }
}
diff --git a/src/main/java/com/yomahub/liteflow/example/bean/ProductPackVO.java b/src/main/java/com/yomahub/liteflow/example/bean/ProductPackVO.java
index ea79c92..2a549cb 100644
--- a/src/main/java/com/yomahub/liteflow/example/bean/ProductPackVO.java
+++ b/src/main/java/com/yomahub/liteflow/example/bean/ProductPackVO.java
@@ -2,162 +2,92 @@ package com.yomahub.liteflow.example.bean;
import com.yomahub.liteflow.example.enums.CategoryEnum;
import com.yomahub.liteflow.example.enums.SkuSourceEnum;
-
import java.math.BigDecimal;
import java.util.List;
-
+import java.util.Objects;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * 商品信息
+ *
+ * @author bryan.zhang
+ */
+
+@ToString
+@Getter
+@Setter
public class ProductPackVO {
- /**
- * 这里注意下,product和sku的关系,一个商品可能有很多规格,比如Product是"NIKE运动鞋",SKU就是"NIKE运动鞋黑色40码"
- */
-
- /**
- * 商品ID
- */
- private Long productId;
-
- /**
- * 商品CODE
- */
- private String productCode;
-
- /**
- * SKU ID
- */
- private Long skuId;
-
- /**
- * SKU CODE
- */
- private String skuCode;
-
- /**
- * SKU名称
- */
- private String skuName;
-
- /**
- * 商品来源
- */
- private SkuSourceEnum skuSource;
-
- /**
- * 类目
- */
- private CategoryEnum category;
-
- /**
- * 售价
- */
- private BigDecimal salePrice;
-
- /**
- * 数量
- */
- private Integer count;
-
- /**
- * 优惠信息,一个商品可能有多个优惠信息
- */
- private List promotionList;
-
- public Long getProductId() {
- return productId;
- }
-
- public void setProductId(Long productId) {
- this.productId = productId;
- }
-
- public String getProductCode() {
- return productCode;
- }
-
- public void setProductCode(String productCode) {
- this.productCode = productCode;
- }
-
- public Long getSkuId() {
- return skuId;
- }
-
- public void setSkuId(Long skuId) {
- this.skuId = skuId;
- }
-
- public String getSkuCode() {
- return skuCode;
- }
-
- public void setSkuCode(String skuCode) {
- this.skuCode = skuCode;
- }
-
- public String getSkuName() {
- return skuName;
- }
-
- public void setSkuName(String skuName) {
- this.skuName = skuName;
- }
-
- public SkuSourceEnum getSkuSource() {
- return skuSource;
- }
-
- public void setSkuSource(SkuSourceEnum skuSource) {
- this.skuSource = skuSource;
- }
-
- public BigDecimal getSalePrice() {
- return salePrice;
- }
-
- public void setSalePrice(BigDecimal salePrice) {
- this.salePrice = salePrice;
- }
-
- public Integer getCount() {
- return count;
- }
-
- public void setCount(Integer count) {
- this.count = count;
- }
-
- public CategoryEnum getCategory() {
- return category;
- }
-
- public void setCategory(CategoryEnum category) {
- this.category = category;
- }
-
- public List getPromotionList() {
- return promotionList;
- }
-
- public void setPromotionList(List promotionList) {
- this.promotionList = promotionList;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null){
- return false;
- }else{
- if(getClass() != obj.getClass()){
- return false;
- }else{
- if(((ProductPackVO)obj).getSkuId().equals(this.getSkuId())){
- return true;
- }else{
- return false;
- }
- }
- }
- }
-
-
+ /*
+ * 这里注意下,product和sku的关系,一个商品可能有很多规格,比如Product是"NIKE运动鞋",SKU就是"NIKE运动鞋黑色40码"
+ */
+
+ /**
+ * 商品ID
+ */
+ private Long productId;
+
+ /**
+ * 商品CODE
+ */
+ private String productCode;
+
+ /**
+ * SKU ID
+ */
+ private Long skuId;
+
+ /**
+ * SKU CODE
+ */
+ private String skuCode;
+
+ /**
+ * SKU名称
+ */
+ private String skuName;
+
+ /**
+ * 商品来源
+ */
+ private SkuSourceEnum skuSource;
+
+ /**
+ * 类目
+ */
+ private CategoryEnum category;
+
+ /**
+ * 售价
+ */
+ private BigDecimal salePrice;
+
+ /**
+ * 数量
+ */
+ private Integer count;
+
+ /**
+ * 优惠信息,一个商品可能有多个优惠信息
+ */
+ private List promotionList;
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ProductPackVO that = (ProductPackVO) o;
+ return Objects.equals(skuId, that.skuId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(skuId);
+ }
}
diff --git a/src/main/java/com/yomahub/liteflow/example/component/CheckCmp.java b/src/main/java/com/yomahub/liteflow/example/component/CheckCmp.java
index a3d287f..81c3677 100644
--- a/src/main/java/com/yomahub/liteflow/example/component/CheckCmp.java
+++ b/src/main/java/com/yomahub/liteflow/example/component/CheckCmp.java
@@ -1,25 +1,28 @@
package com.yomahub.liteflow.example.component;
+import static com.yomahub.liteflow.example.component.NodeIdConstant.CHECK_CMP;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.example.bean.PriceCalcReqVO;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.stereotype.Component;
+import lombok.extern.slf4j.Slf4j;
/**
* 初始化参数检查组件
+ *
+ * @author bryan.zhang
*/
-@Component("checkCmp")
+@LiteflowComponent(id = CHECK_CMP, name = "初始化参数检查组件")
+@Slf4j
public class CheckCmp extends NodeComponent {
- private Logger log = LoggerFactory.getLogger(getClass());
-
@Override
- public void process() throws Exception {
- //拿到请求参数
+ public void process() {
+ // 拿到请求参数
PriceCalcReqVO req = this.getSlot().getRequestData();
+ log.info("请求参数:{}", req);
- /***这里Mock参数验证过程***/
+ // 这里Mock参数验证过程
log.info("参数验证完成");
}
}
diff --git a/src/main/java/com/yomahub/liteflow/example/component/CouponCmp.java b/src/main/java/com/yomahub/liteflow/example/component/CouponCmp.java
index dfe897b..d20cb89 100644
--- a/src/main/java/com/yomahub/liteflow/example/component/CouponCmp.java
+++ b/src/main/java/com/yomahub/liteflow/example/component/CouponCmp.java
@@ -1,44 +1,47 @@
package com.yomahub.liteflow.example.component;
+import static com.yomahub.liteflow.example.component.NodeIdConstant.COUPON_CMP;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.example.bean.PriceStepVO;
import com.yomahub.liteflow.example.enums.PriceTypeEnum;
import com.yomahub.liteflow.example.slot.PriceContext;
-import org.springframework.stereotype.Component;
import java.math.BigDecimal;
/**
* 优惠券抵扣计算组件
+ *
+ * @author bryan.zhang
*/
-@Component("couponCmp")
+@LiteflowComponent(id = COUPON_CMP, name = "优惠券抵扣计算组件")
public class CouponCmp extends NodeComponent {
@Override
- public void process() throws Exception {
+ public void process() {
PriceContext context = this.getContextBean(PriceContext.class);
- /**这里Mock下根据couponId取到的优惠卷面值为15元**/
+ // 这里Mock下根据couponId取到的优惠卷面值为15元
Long couponId = context.getCouponId();
- BigDecimal couponPrice = new BigDecimal(15);
+ BigDecimal couponPrice = BigDecimal.valueOf(15);
BigDecimal prePrice = context.getLastestPriceStep().getCurrPrice();
BigDecimal currPrice = prePrice.subtract(couponPrice);
- context.addPriceStep(new PriceStepVO(PriceTypeEnum.COUPON_DISCOUNT,
- couponId.toString(),
- prePrice,
- currPrice.subtract(prePrice),
- currPrice,
- PriceTypeEnum.COUPON_DISCOUNT.getName()));
+ context.addPriceStep(PriceStepVO.builder()
+ .priceType(PriceTypeEnum.COUPON_DISCOUNT)
+ .extId(couponId.toString())
+ .prePrice(prePrice)
+ .priceChange(couponPrice)
+ .currPrice(currPrice)
+ .stepDesc(PriceTypeEnum.COUPON_DISCOUNT.getName())
+ .build());
}
@Override
public boolean isAccess() {
PriceContext context = this.getContextBean(PriceContext.class);
- if(context.getCouponId() != null){
- return true;
- }else{
- return false;
- }
+ // 是否提供了优惠券
+ return context.getCouponId() != null;
}
}
diff --git a/src/main/java/com/yomahub/liteflow/example/component/MemberDiscountCmp.java b/src/main/java/com/yomahub/liteflow/example/component/MemberDiscountCmp.java
index 2bcdbe8..d78c174 100644
--- a/src/main/java/com/yomahub/liteflow/example/component/MemberDiscountCmp.java
+++ b/src/main/java/com/yomahub/liteflow/example/component/MemberDiscountCmp.java
@@ -13,6 +13,8 @@ import java.math.RoundingMode;
/**
* 会员折扣计算组件
+ *
+ * @author bryan.zhang
*/
@Component("memberDiscountCmp")
public class MemberDiscountCmp extends NodeComponent {
@@ -21,7 +23,7 @@ public class MemberDiscountCmp extends NodeComponent {
PriceContext context = this.getContextBean(PriceContext.class);
String memberCode = context.getMemberCode();
- /***这里Mock下通过memberCode去查会员等级表然后获取的会员折扣为9折的代码***/
+ // 这里Mock下通过memberCode去查会员等级表然后获取的会员折扣为9折的代码
BigDecimal memberDiscount = new BigDecimal("0.9");
//进行计算会员折扣
@@ -41,11 +43,7 @@ public class MemberDiscountCmp extends NodeComponent {
@Override
public boolean isAccess() {
PriceContext context = this.getContextBean(PriceContext.class);
- if(CollectionUtils.isNotEmpty(context.getProductPackList())
- && StringUtils.isNotBlank(context.getMemberCode())){
- return true;
- }else{
- return false;
- }
+ return CollectionUtils.isNotEmpty(context.getProductPackList())
+ && StringUtils.isNotBlank(context.getMemberCode());
}
}
diff --git a/src/main/java/com/yomahub/liteflow/example/component/NodeIdConstant.java b/src/main/java/com/yomahub/liteflow/example/component/NodeIdConstant.java
new file mode 100644
index 0000000..10f8dae
--- /dev/null
+++ b/src/main/java/com/yomahub/liteflow/example/component/NodeIdConstant.java
@@ -0,0 +1,34 @@
+package com.yomahub.liteflow.example.component;
+
+/**
+ * 流程组件Id常量声明
+ *
+ * @author 李小平
+ */
+public interface NodeIdConstant {
+ /**
+ * 初始化参数检查组件Id
+ */
+ String CHECK_CMP = "checkCmp";
+
+ /**
+ * Slot初始化组件Id
+ */
+ String SLOT_INIT_CMP = "slotInitCmp";
+
+ /**
+ * 优惠信息转换组件
+ */
+ String PROMOTION_CONVERT_CMP = "promotionConvertCmp";
+
+ /**
+ * 优惠券抵扣计算组件Id
+ */
+ String COUPON_CMP = "couponCmp";
+
+
+ /**
+ * 步骤日志生成组件Id
+ */
+ String STEP_PRINT_CMP = "stepPrintCmp";
+}
diff --git a/src/main/java/com/yomahub/liteflow/example/component/PriceStepInitCmp.java b/src/main/java/com/yomahub/liteflow/example/component/PriceStepInitCmp.java
index b581043..93cbf66 100644
--- a/src/main/java/com/yomahub/liteflow/example/component/PriceStepInitCmp.java
+++ b/src/main/java/com/yomahub/liteflow/example/component/PriceStepInitCmp.java
@@ -1,47 +1,43 @@
package com.yomahub.liteflow.example.component;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.example.bean.PriceStepVO;
-import com.yomahub.liteflow.example.bean.ProductPackVO;
import com.yomahub.liteflow.example.enums.PriceTypeEnum;
import com.yomahub.liteflow.example.slot.PriceContext;
-import org.apache.commons.collections4.CollectionUtils;
-import org.springframework.stereotype.Component;
-
import java.math.BigDecimal;
-import java.util.List;
+
+import org.apache.commons.collections4.CollectionUtils;
/**
* 价格步骤初始化器(把原价初始化进去)
+ *
+ * @author bryan.zhang
*/
-@Component("priceStepInitCmp")
+@LiteflowComponent(id = "priceStepInitCmp", name = "价格步骤初始化器")
public class PriceStepInitCmp extends NodeComponent {
- @Override
- public void process() throws Exception {
- PriceContext context = this.getContextBean(PriceContext.class);
- //初始化价格步骤
- List packList = context.getProductPackList();
- BigDecimal totalOriginalPrice = new BigDecimal(0);
- for(ProductPackVO packItem : packList){
- totalOriginalPrice = totalOriginalPrice.add(packItem.getSalePrice().multiply(new BigDecimal(packItem.getCount())));
- }
- context.addPriceStep(new PriceStepVO(PriceTypeEnum.ORIGINAL,
- null,
- null,
- totalOriginalPrice,
- totalOriginalPrice,
- PriceTypeEnum.ORIGINAL.getName()));
- context.setOriginalOrderPrice(totalOriginalPrice);
- }
+ @Override
+ public void process() throws Exception {
+ PriceContext context = this.getContextBean(PriceContext.class);
+
+ // 初始化价格步骤
+ BigDecimal totalOriginalPrice = context.calculateTotalOriginalPrice();
+
+ context.addPriceStep(PriceStepVO.builder()
+ .priceType(PriceTypeEnum.ORIGINAL)
+ .extId(null)
+ .prePrice(null)
+ .priceChange(totalOriginalPrice)
+ .currPrice(totalOriginalPrice)
+ .stepDesc(PriceTypeEnum.ORIGINAL.getName())
+ .build());
+ context.setOriginalOrderPrice(totalOriginalPrice);
+ }
- @Override
- public boolean isAccess() {
- PriceContext context = this.getContextBean(PriceContext.class);
- if(CollectionUtils.isNotEmpty(context.getProductPackList())){
- return true;
- }else{
- return false;
- }
- }
+ @Override
+ public boolean isAccess() {
+ PriceContext context = this.getContextBean(PriceContext.class);
+ return CollectionUtils.isNotEmpty(context.getProductPackList());
+ }
}
diff --git a/src/main/java/com/yomahub/liteflow/example/component/PromotionConvertCmp.java b/src/main/java/com/yomahub/liteflow/example/component/PromotionConvertCmp.java
index ac3693e..fad118d 100644
--- a/src/main/java/com/yomahub/liteflow/example/component/PromotionConvertCmp.java
+++ b/src/main/java/com/yomahub/liteflow/example/component/PromotionConvertCmp.java
@@ -1,61 +1,59 @@
package com.yomahub.liteflow.example.component;
+import static com.yomahub.liteflow.example.component.NodeIdConstant.PROMOTION_CONVERT_CMP;
+
import cn.hutool.core.collection.ListUtil;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.example.bean.ProductPackVO;
import com.yomahub.liteflow.example.bean.PromotionInfoVO;
import com.yomahub.liteflow.example.bean.PromotionPackVO;
import com.yomahub.liteflow.example.slot.PriceContext;
-import org.apache.commons.collections4.CollectionUtils;
-import org.springframework.beans.BeanUtils;
-import org.springframework.stereotype.Component;
-
import java.util.ArrayList;
import java.util.List;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.beans.BeanUtils;
/**
* 把商品包的优惠信息转换成以优惠信息为主要维度的对象,以便于后面优惠信息的计算
+ *
+ * @author bryan.zhang
*/
-@Component("promotionConvertCmp")
+@LiteflowComponent(id = PROMOTION_CONVERT_CMP, name = "优惠信息转换组件")
public class PromotionConvertCmp extends NodeComponent {
- @Override
- public void process() throws Exception {
- PriceContext context = this.getContextBean(PriceContext.class);
- List promotionPackList = new ArrayList<>();
- PromotionPackVO promotionPack = null;
- for(ProductPackVO pack : context.getProductPackList()){
- if(CollectionUtils.isEmpty(pack.getPromotionList())){
- continue;
- }
- for(PromotionInfoVO promotion : pack.getPromotionList()){
- promotionPack = new PromotionPackVO();
- promotionPack.setId(promotion.getId());
- if(promotionPackList.contains(promotionPack)){
- promotionPack = promotionPackList.get(promotionPackList.indexOf(promotionPack));
- if(promotionPack.getRelatedProductPackList().contains(pack)){
- continue;
- }else{
- promotionPack.getRelatedProductPackList().add(pack);
- }
- }else{
- BeanUtils.copyProperties(promotion,promotionPack);
- promotionPack.setRelatedProductPackList(ListUtil.toList(pack));
- promotionPackList.add(promotionPack);
- }
- }
- }
- context.setPromotionPackList(promotionPackList);
- }
+ @Override
+ public void process() throws Exception {
+ PriceContext context = this.getContextBean(PriceContext.class);
+ List promotionPackList = new ArrayList<>();
- @Override
- public boolean isAccess() {
- PriceContext context = this.getContextBean(PriceContext.class);
- if(CollectionUtils.isNotEmpty(context.getProductPackList())){
- return true;
- }else{
- return false;
- }
+ PromotionPackVO promotionPack;
+ for (ProductPackVO pack : context.getProductPackList()) {
+ if (CollectionUtils.isEmpty(pack.getPromotionList())) {
+ continue;
+ }
+ for (PromotionInfoVO promotion : pack.getPromotionList()) {
+ promotionPack = new PromotionPackVO();
+ promotionPack.setId(promotion.getId());
+ if (promotionPackList.contains(promotionPack)) {
+ promotionPack = promotionPackList.get(promotionPackList.indexOf(promotionPack));
+ if (!promotionPack.getRelatedProductPackList().contains(pack)) {
+ promotionPack.getRelatedProductPackList().add(pack);
+ }
+ } else {
+ BeanUtils.copyProperties(promotion, promotionPack);
+ promotionPack.setRelatedProductPackList(ListUtil.toList(pack));
+ promotionPackList.add(promotionPack);
+ }
+ }
}
+ context.setPromotionPackList(promotionPackList);
+ }
+
+ @Override
+ public boolean isAccess() {
+ PriceContext context = this.getContextBean(PriceContext.class);
+ return CollectionUtils.isNotEmpty(context.getProductPackList());
+ }
}
diff --git a/src/main/java/com/yomahub/liteflow/example/component/SlotInitCmp.java b/src/main/java/com/yomahub/liteflow/example/component/SlotInitCmp.java
index a26029a..43d287c 100644
--- a/src/main/java/com/yomahub/liteflow/example/component/SlotInitCmp.java
+++ b/src/main/java/com/yomahub/liteflow/example/component/SlotInitCmp.java
@@ -1,35 +1,30 @@
package com.yomahub.liteflow.example.component;
+import static com.yomahub.liteflow.example.component.NodeIdConstant.SLOT_INIT_CMP;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.example.bean.PriceCalcReqVO;
import com.yomahub.liteflow.example.slot.PriceContext;
-import org.springframework.stereotype.Component;
/**
* Slot初始化组件
+ *
+ * @author bryan.zhang
*/
-@Component("slotInitCmp")
+@LiteflowComponent(id = SLOT_INIT_CMP, name = "Slot初始化组件")
public class SlotInitCmp extends NodeComponent {
@Override
- public void process() throws Exception {
- //把主要参数冗余到slot里
+ public void process() {
+ // 把主要参数冗余到slot里
PriceCalcReqVO req = this.getRequestData();
PriceContext context = this.getContextBean(PriceContext.class);
- context.setOrderNo(req.getOrderNo());
- context.setOversea(req.isOversea());
- context.setMemberCode(req.getMemberCode());
- context.setOrderChannel(req.getOrderChannel());
- context.setProductPackList(req.getProductPackList());
- context.setCouponId(req.getCouponId());
+ context.copy(req);
}
@Override
public boolean isAccess() {
PriceCalcReqVO req = this.getSlot().getRequestData();
- if(req != null){
- return true;
- }else{
- return false;
- }
+ return req != null;
}
}
diff --git a/src/main/java/com/yomahub/liteflow/example/component/StepPrintCmp.java b/src/main/java/com/yomahub/liteflow/example/component/StepPrintCmp.java
index ad7983d..177dc77 100644
--- a/src/main/java/com/yomahub/liteflow/example/component/StepPrintCmp.java
+++ b/src/main/java/com/yomahub/liteflow/example/component/StepPrintCmp.java
@@ -1,34 +1,39 @@
package com.yomahub.liteflow.example.component;
+import static com.yomahub.liteflow.example.component.NodeIdConstant.STEP_PRINT_CMP;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.example.bean.PriceStepVO;
import com.yomahub.liteflow.example.bean.ProductPackVO;
import com.yomahub.liteflow.example.slot.PriceContext;
+import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.stereotype.Component;
-import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.MessageFormat;
/**
* 步骤日志生成组件
+ *
+ * @author bryan.zhang
*/
-@Component("stepPrintCmp")
+@LiteflowComponent(id = STEP_PRINT_CMP, name = "步骤日志生成组件")
+@Slf4j
public class StepPrintCmp extends NodeComponent {
- private Logger log = LoggerFactory.getLogger(getClass());
+
+ private static final String LOG_SEPARATOR_LINE = "|====================================================================\n";
@Override
public void process() throws Exception {
PriceContext context = this.getContextBean(PriceContext.class);
+
StringBuilder logStr = new StringBuilder();
logStr.append(MessageFormat.format("订单号[{0}]的价格计算的明细结果:\n", context.getOrderNo()));
- logStr.append("|====================================================================\n");
- for(ProductPackVO pack : context.getProductPackList()){
+ logStr.append(LOG_SEPARATOR_LINE);
+ for (ProductPackVO pack : context.getProductPackList()){
logStr.append(MessageFormat.format("| {0} [{1}] [{2}] {3} X {4}\n",
pack.getSkuName(),
pack.getProductCode(),
@@ -37,12 +42,14 @@ public class StepPrintCmp extends NodeComponent {
pack.getCount()));
}
- logStr.append("|====================================================================\n");
+ logStr.append(LOG_SEPARATOR_LINE);
for(PriceStepVO step : context.getPriceStepList()){
- logStr.append(MessageFormat.format("| [{0} : {1}]\n",step.getStepDesc(),step.getPriceChange().setScale(2, BigDecimal.ROUND_HALF_UP).toString()));
+ logStr.append(MessageFormat.format("| [{0} : {1}]\n",step.getStepDesc(),
+ step.getPriceChange().setScale(2, RoundingMode.HALF_UP).toString()));
}
- logStr.append(MessageFormat.format("| [最终价 : {0}]\n",context.getFinalOrderPrice().setScale(2, BigDecimal.ROUND_HALF_UP).toString()));
- logStr.append("|====================================================================\n");
+ logStr.append(MessageFormat.format("| [最终价 : {0}]\n",context.getFinalOrderPrice().setScale(2,
+ RoundingMode.HALF_UP).toString()));
+ logStr.append(LOG_SEPARATOR_LINE);
log.info(logStr.toString());
context.setPrintLog(logStr.toString());
}
@@ -50,11 +57,7 @@ public class StepPrintCmp extends NodeComponent {
@Override
public boolean isAccess() {
PriceContext context = this.getContextBean(PriceContext.class);
- if(CollectionUtils.isNotEmpty(context.getPriceStepList())){
- return true;
- }else{
- return false;
- }
+ return CollectionUtils.isNotEmpty(context.getPriceStepList());
}
}
diff --git a/src/main/java/com/yomahub/liteflow/example/enums/PriceTypeEnum.java b/src/main/java/com/yomahub/liteflow/example/enums/PriceTypeEnum.java
index 82e8caa..9e72369 100644
--- a/src/main/java/com/yomahub/liteflow/example/enums/PriceTypeEnum.java
+++ b/src/main/java/com/yomahub/liteflow/example/enums/PriceTypeEnum.java
@@ -1,28 +1,58 @@
package com.yomahub.liteflow.example.enums;
+import lombok.Getter;
+
+/**
+ * 订单价格类型
+ *
+ * @author bryan.zhang
+ */
+
public enum PriceTypeEnum {
- ORIGINAL(0, "原始价格"),
- MEMBER_DISCOUNT(1, "会员折扣"),
- PROMOTION_DISCOUNT(2, "促销优惠"),
- COUPON_DISCOUNT(3, "优惠券抵扣"),
- POSTAGE(4, "国内运费"),
- OVERSEAS_POSTAGE(5, "海淘运费"),
- POSTAGE_FREE(6, "实付99元免运费");
-
- private Integer code;
-
- private String name;
-
- PriceTypeEnum(Integer code, String name) {
- this.code = code;
- this.name = name;
- }
-
- public Integer getCode() {
- return code;
- }
-
- public String getName(){
- return name;
- }
+ /**
+ * 原始价格
+ */
+ ORIGINAL(0, "原始价格"),
+
+ /**
+ * 会员折扣
+ */
+ MEMBER_DISCOUNT(1, "会员折扣"),
+
+ /**
+ * 促销优惠
+ */
+ PROMOTION_DISCOUNT(2, "促销优惠"),
+
+ /**
+ * 优惠券抵扣
+ */
+ COUPON_DISCOUNT(3, "优惠券抵扣"),
+
+ /**
+ * 国内运费
+ */
+ POSTAGE(4, "国内运费"),
+
+ /**
+ * 海淘运费
+ */
+ OVERSEAS_POSTAGE(5, "海淘运费"),
+
+ /**
+ * 实付99元免运费
+ */
+ POSTAGE_FREE(6, "实付99元免运费");
+
+ @Getter
+ private final Integer code;
+
+ @Getter
+ private final String name;
+
+ PriceTypeEnum(Integer code, String name) {
+ this.code = code;
+ this.name = name;
+ }
+
}
diff --git a/src/main/java/com/yomahub/liteflow/example/slot/PriceContext.java b/src/main/java/com/yomahub/liteflow/example/slot/PriceContext.java
index d74f762..bfacebc 100644
--- a/src/main/java/com/yomahub/liteflow/example/slot/PriceContext.java
+++ b/src/main/java/com/yomahub/liteflow/example/slot/PriceContext.java
@@ -1,169 +1,123 @@
package com.yomahub.liteflow.example.slot;
+import com.yomahub.liteflow.example.bean.PriceCalcReqVO;
import com.yomahub.liteflow.example.bean.PriceStepVO;
import com.yomahub.liteflow.example.bean.ProductPackVO;
import com.yomahub.liteflow.example.bean.PromotionPackVO;
import com.yomahub.liteflow.example.enums.OrderChannelEnum;
-import org.apache.commons.collections4.CollectionUtils;
-
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.commons.collections4.CollectionUtils;
+/**
+ * 订单价格上下文
+ *
+ * @author bryan.zhang
+ */
+@Getter
+@Setter
public class PriceContext {
- /**
- * 订单号
- */
- private String orderNo;
-
- /**
- * 是否境外购
- */
- private boolean oversea;
-
- /**
- * 商品包
- */
- private List productPackList;
-
- /**
- * 订单渠道
- */
- private OrderChannelEnum orderChannel;
-
- /**
- * 会员CODE
- */
- private String memberCode;
-
- /**
- * 优惠券
- */
- private Long couponId;
-
- /**
- * 优惠信息
- */
- private List promotionPackList;
-
- /**
- * 价格步骤
- */
- private List priceStepList = new ArrayList<>();
-
- /**
- * 订单原始价格
- */
- private BigDecimal originalOrderPrice;
-
- /**
- * 订单最终价格
- */
- private BigDecimal finalOrderPrice;
-
- /**
- * 步骤日志
- */
- private String printLog;
-
- public PriceStepVO getLastestPriceStep(){
- if(CollectionUtils.isEmpty(priceStepList)){
- return null;
- }else{
- return priceStepList.get(priceStepList.size()-1);
- }
- }
-
- public String getOrderNo() {
- return orderNo;
- }
-
- public void setOrderNo(String orderNo) {
- this.orderNo = orderNo;
- }
-
- public List getProductPackList() {
- return productPackList;
- }
-
- public void setProductPackList(List productPackList) {
- this.productPackList = productPackList;
- }
-
- public OrderChannelEnum getOrderChannel() {
- return orderChannel;
- }
-
- public void setOrderChannel(OrderChannelEnum orderChannel) {
- this.orderChannel = orderChannel;
- }
-
- public String getMemberCode() {
- return memberCode;
- }
-
- public void setMemberCode(String memberCode) {
- this.memberCode = memberCode;
- }
-
- public Long getCouponId() {
- return couponId;
- }
-
- public void setCouponId(Long couponId) {
- this.couponId = couponId;
- }
-
- public List getPriceStepList() {
- return priceStepList;
- }
-
- public void setPriceStepList(List priceStepList) {
- this.priceStepList = priceStepList;
- }
-
- public void addPriceStep(PriceStepVO step){
- this.priceStepList.add(step);
- }
-
- public List getPromotionPackList() {
- return promotionPackList;
- }
-
- public void setPromotionPackList(List promotionPackList) {
- this.promotionPackList = promotionPackList;
- }
-
- public boolean isOversea() {
- return oversea;
- }
-
- public void setOversea(boolean oversea) {
- this.oversea = oversea;
- }
-
- public BigDecimal getFinalOrderPrice() {
- return finalOrderPrice;
- }
-
- public void setFinalOrderPrice(BigDecimal finalOrderPrice) {
- this.finalOrderPrice = finalOrderPrice;
- }
-
- public BigDecimal getOriginalOrderPrice() {
- return originalOrderPrice;
- }
-
- public void setOriginalOrderPrice(BigDecimal originalOrderPrice) {
- this.originalOrderPrice = originalOrderPrice;
- }
-
- public String getPrintLog() {
- return printLog;
- }
-
- public void setPrintLog(String printLog) {
- this.printLog = printLog;
- }
+ /**
+ * 订单号
+ */
+ private String orderNo;
+
+ /**
+ * 是否境外购
+ */
+ private boolean oversea;
+
+ /**
+ * 商品包
+ */
+ private List productPackList;
+
+ /**
+ * 订单渠道
+ */
+ private OrderChannelEnum orderChannel;
+
+ /**
+ * 会员CODE
+ */
+ private String memberCode;
+
+ /**
+ * 优惠券
+ */
+ private Long couponId;
+
+ /**
+ * 优惠信息
+ */
+ private List promotionPackList;
+
+ /**
+ * 价格步骤
+ */
+ private final List priceStepList = new ArrayList<>();
+
+ /**
+ * 订单原始价格
+ */
+ private BigDecimal originalOrderPrice;
+
+ /**
+ * 订单最终价格
+ */
+ private BigDecimal finalOrderPrice;
+
+ /**
+ * 步骤日志
+ */
+ private String printLog;
+
+ public PriceStepVO getLastestPriceStep() {
+ if (CollectionUtils.isEmpty(priceStepList)) {
+ return null;
+ } else {
+ return priceStepList.get(priceStepList.size() - 1);
+ }
+ }
+
+ /**
+ * 新增订单价格计算中间步骤
+ *
+ * @param step 订单价格计算中间步骤
+ */
+ public void addPriceStep(PriceStepVO step) {
+ this.priceStepList.add(step);
+ }
+
+ /**
+ * 把主要参数冗余到slot里
+ *
+ * @param req 订单价格计算请求参数
+ */
+ public void copy(PriceCalcReqVO req) {
+ this.setOrderNo(req.getOrderNo());
+ this.setOversea(req.isOversea());
+ this.setMemberCode(req.getMemberCode());
+ this.setOrderChannel(req.getOrderChannel());
+ this.setProductPackList(req.getProductPackList());
+ this.setCouponId(req.getCouponId());
+ }
+
+ /**
+ * 计算订单原始价格
+ *
+ * @return 订单原始价格
+ */
+ public BigDecimal calculateTotalOriginalPrice() {
+ BigDecimal totalOriginalPrice = BigDecimal.ZERO;
+ for (ProductPackVO packItem : this.getProductPackList()) {
+ totalOriginalPrice = totalOriginalPrice.add(
+ packItem.getSalePrice().multiply(BigDecimal.valueOf(packItem.getCount())));
+ }
+ return totalOriginalPrice;
+ }
}
--
Gitee
From a5c376458c3fbeeab2e567b2a0498903e16c50ab Mon Sep 17 00:00:00 2001
From: lixiaoping <李小平17773406760@189.cn>
Date: Mon, 21 Nov 2022 17:54:02 +0800
Subject: [PATCH 02/10] =?UTF-8?q?=E9=83=A8=E5=88=86=E5=8A=9F=E8=83=BD?=
=?UTF-8?q?=E9=87=8D=E6=9E=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../example/bean/PromotionInfoVO.java | 70 +++++------
.../example/bean/PromotionPackVO.java | 43 +++----
.../example/component/FullCutCmp.java | 108 ++++++++---------
.../example/component/FullDiscountCmp.java | 109 +++++++++---------
.../example/component/MemberDiscountCmp.java | 32 ++---
.../example/component/NodeIdConstant.java | 27 ++++-
.../component/PromotionConvertCmp.java | 15 ++-
.../example/component/RushBuyCmp.java | 2 +-
.../example/enums/PromotionTypeEnum.java | 25 +++-
9 files changed, 225 insertions(+), 206 deletions(-)
diff --git a/src/main/java/com/yomahub/liteflow/example/bean/PromotionInfoVO.java b/src/main/java/com/yomahub/liteflow/example/bean/PromotionInfoVO.java
index bb419a0..261eca6 100644
--- a/src/main/java/com/yomahub/liteflow/example/bean/PromotionInfoVO.java
+++ b/src/main/java/com/yomahub/liteflow/example/bean/PromotionInfoVO.java
@@ -1,7 +1,20 @@
package com.yomahub.liteflow.example.bean;
import com.yomahub.liteflow.example.enums.PromotionTypeEnum;
-
+import java.util.Objects;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 优惠信息
+ *
+ * @author bryan.zhang
+ */
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
public class PromotionInfoVO {
/**
@@ -24,45 +37,20 @@ public class PromotionInfoVO {
*/
private PromotionTypeEnum promotionType;
- public PromotionInfoVO() {
- }
-
- public PromotionInfoVO(Long id, String promotionCode, String promotionName, PromotionTypeEnum promotionType) {
- this.id = id;
- this.promotionCode = promotionCode;
- this.promotionName = promotionName;
- this.promotionType = promotionType;
- }
-
- public Long getId() {
- return id;
- }
-
- public void setId(Long id) {
- this.id = id;
- }
-
- public String getPromotionCode() {
- return promotionCode;
- }
-
- public void setPromotionCode(String promotionCode) {
- this.promotionCode = promotionCode;
- }
-
- public String getPromotionName() {
- return promotionName;
- }
-
- public void setPromotionName(String promotionName) {
- this.promotionName = promotionName;
- }
-
- public PromotionTypeEnum getPromotionType() {
- return promotionType;
- }
-
- public void setPromotionType(PromotionTypeEnum promotionType) {
- this.promotionType = promotionType;
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ PromotionInfoVO that = (PromotionInfoVO) o;
+ return id.equals(that.id);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id);
}
}
diff --git a/src/main/java/com/yomahub/liteflow/example/bean/PromotionPackVO.java b/src/main/java/com/yomahub/liteflow/example/bean/PromotionPackVO.java
index b598361..24bf976 100644
--- a/src/main/java/com/yomahub/liteflow/example/bean/PromotionPackVO.java
+++ b/src/main/java/com/yomahub/liteflow/example/bean/PromotionPackVO.java
@@ -1,40 +1,23 @@
package com.yomahub.liteflow.example.bean;
-import org.apache.commons.lang3.builder.EqualsBuilder;
-import org.apache.commons.lang3.builder.HashCodeBuilder;
-import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
+import lombok.Getter;
+import lombok.ToString;
-public class PromotionPackVO extends PromotionInfoVO{
+/**
+ * 优惠包信息
+ *
+ * @author bryan.zhang
+ */
+@ToString(callSuper = true)
+public class PromotionPackVO extends PromotionInfoVO {
/**
* 这个优惠活动关联的商品包
*/
- private List relatedProductPackList;
-
- public List getRelatedProductPackList() {
- return relatedProductPackList;
- }
-
- public void setRelatedProductPackList(List relatedProductPackList) {
- this.relatedProductPackList = relatedProductPackList;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null){
- return false;
- }else{
- if(getClass() != obj.getClass()){
- return false;
- }else{
- if(((PromotionPackVO)obj).getId().equals(this.getId())){
- return true;
- }else{
- return false;
- }
- }
- }
- }
+ @Getter
+ private final Set relatedProductPackSet = new HashSet<>();
}
diff --git a/src/main/java/com/yomahub/liteflow/example/component/FullCutCmp.java b/src/main/java/com/yomahub/liteflow/example/component/FullCutCmp.java
index 2d77bc7..8a3c7aa 100644
--- a/src/main/java/com/yomahub/liteflow/example/component/FullCutCmp.java
+++ b/src/main/java/com/yomahub/liteflow/example/component/FullCutCmp.java
@@ -1,5 +1,8 @@
package com.yomahub.liteflow.example.component;
+import static com.yomahub.liteflow.example.component.NodeIdConstant.FULL_CUT_CMP;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.example.bean.PriceStepVO;
import com.yomahub.liteflow.example.bean.ProductPackVO;
@@ -7,72 +10,73 @@ import com.yomahub.liteflow.example.bean.PromotionPackVO;
import com.yomahub.liteflow.example.enums.PriceTypeEnum;
import com.yomahub.liteflow.example.enums.PromotionTypeEnum;
import com.yomahub.liteflow.example.slot.PriceContext;
-import org.apache.commons.collections4.CollectionUtils;
-import org.springframework.stereotype.Component;
-
import java.math.BigDecimal;
-import java.util.List;
-import java.util.stream.Collectors;
+import java.util.Objects;
+import java.util.Optional;
+
/**
* 满减计算组件
+ *
+ * @author bryan.zhang
*/
-@Component("fullCutCmp")
+@LiteflowComponent(id = FULL_CUT_CMP, name = "满减计算组件")
public class FullCutCmp extends NodeComponent {
- @Override
- public void process() throws Exception {
- PriceContext slot = this.getContextBean(PriceContext.class);
- PromotionPackVO promotionPack = getMatchPromotion();
- /***这里Mock下根据优惠信息查到的满减信息为:满100,减5块***/
- BigDecimal triggerPrice = new BigDecimal(100);
- BigDecimal cutPrice = new BigDecimal(5);
+ /**
+ * 满减-优惠包
+ */
+ private PromotionPackVO promotionPack;
+
+ @Override
+ public void process() throws Exception {
+ if (Objects.isNull(promotionPack)) {
+ return;
+ }
- //从PromotionPack对象中取到这个优惠关联的商品信息,判断是否超过了触发满减的金额
- BigDecimal reletedProductTotalPrice = new BigDecimal(0);
- for(ProductPackVO productPack : promotionPack.getRelatedProductPackList()){
- reletedProductTotalPrice = reletedProductTotalPrice.add(productPack.getSalePrice().multiply(new BigDecimal(productPack.getCount())));
- }
- if (reletedProductTotalPrice.compareTo(triggerPrice) >= 0){
- BigDecimal prePrice = slot.getLastestPriceStep().getCurrPrice();
- BigDecimal currPrice = prePrice.subtract(cutPrice);
+ PriceContext slot = this.getContextBean(PriceContext.class);
- slot.addPriceStep(new PriceStepVO(PriceTypeEnum.PROMOTION_DISCOUNT,
- promotionPack.getId().toString(),
- prePrice,
- currPrice.subtract(prePrice),
- currPrice,
- PriceTypeEnum.PROMOTION_DISCOUNT.getName() + "[满减]"));
- }
+ // 这里Mock下根据优惠信息查到的满减信息为:满100,减5块
+ BigDecimal triggerPrice = BigDecimal.valueOf(100);
+ BigDecimal cutPrice = BigDecimal.valueOf(5);
+ // 从PromotionPack对象中取到这个优惠关联的商品信息,判断是否超过了触发满减的金额
+ BigDecimal reletedProductTotalPrice = BigDecimal.ZERO;
+ for (ProductPackVO productPack : promotionPack.getRelatedProductPackSet()) {
+ reletedProductTotalPrice = reletedProductTotalPrice.add(productPack.getSalePrice()
+ .multiply(BigDecimal.valueOf(productPack.getCount())));
}
- @Override
- public boolean isAccess() {
- //过滤出优惠信息列表中有没有满减这个活动,如果有,则进入这个组件,反义就不进入
- PromotionPackVO promotionPack = getMatchPromotion();
- if(promotionPack != null){
- return true;
- }else{
- return false;
- }
+ if (reletedProductTotalPrice.compareTo(triggerPrice) >= 0) {
+ BigDecimal prePrice = slot.getLastestPriceStep().getCurrPrice();
+ BigDecimal currPrice = prePrice.subtract(cutPrice);
+
+ slot.addPriceStep(PriceStepVO.builder()
+ .priceType(PriceTypeEnum.PROMOTION_DISCOUNT)
+ .extId(promotionPack.getId().toString())
+ .prePrice(prePrice)
+ .priceChange(currPrice.subtract(prePrice))
+ .currPrice(currPrice)
+ .stepDesc(PriceTypeEnum.PROMOTION_DISCOUNT.getName() + "[满减]")
+ .build());
}
- private PromotionPackVO getMatchPromotion(){
- PriceContext context = this.getContextBean(PriceContext.class);
+ }
- List matchList = context.getPromotionPackList().stream().filter(promotionPackVO -> {
- if(promotionPackVO.getPromotionType().equals(PromotionTypeEnum.FULL_CUT)){
- return true;
- }else{
- return false;
- }
- }).collect(Collectors.toList());
+ @Override
+ public boolean isAccess() {
+ // 过滤出优惠信息列表中有没有满减这个活动,如果有,则进入这个组件,反义就不进入
+ Optional matchPromotion = getMatchPromotion();
+ promotionPack = matchPromotion.orElse(null);
+ return matchPromotion.isPresent();
+ }
- if(CollectionUtils.isNotEmpty(matchList)){
- return matchList.get(0);
- }else{
- return null;
- }
- }
+ private Optional getMatchPromotion() {
+ PriceContext context = this.getContextBean(PriceContext.class);
+
+ return context.getPromotionPackList().stream()
+ .filter(promotionPackVO -> promotionPackVO.getPromotionType()
+ .equals(PromotionTypeEnum.FULL_CUT))
+ .findFirst();
+ }
}
diff --git a/src/main/java/com/yomahub/liteflow/example/component/FullDiscountCmp.java b/src/main/java/com/yomahub/liteflow/example/component/FullDiscountCmp.java
index 22aba7f..474da24 100644
--- a/src/main/java/com/yomahub/liteflow/example/component/FullDiscountCmp.java
+++ b/src/main/java/com/yomahub/liteflow/example/component/FullDiscountCmp.java
@@ -1,5 +1,8 @@
package com.yomahub.liteflow.example.component;
+import static com.yomahub.liteflow.example.component.NodeIdConstant.FULL_DISCOUNT_CMP;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.example.bean.PriceStepVO;
import com.yomahub.liteflow.example.bean.ProductPackVO;
@@ -7,73 +10,75 @@ import com.yomahub.liteflow.example.bean.PromotionPackVO;
import com.yomahub.liteflow.example.enums.PriceTypeEnum;
import com.yomahub.liteflow.example.enums.PromotionTypeEnum;
import com.yomahub.liteflow.example.slot.PriceContext;
-import org.apache.commons.collections4.CollectionUtils;
-import org.springframework.stereotype.Component;
-
import java.math.BigDecimal;
import java.math.RoundingMode;
-import java.util.List;
-import java.util.stream.Collectors;
+import java.util.Objects;
+import java.util.Optional;
/**
* 满折计算组件
+ *
+ * @author bryan.zhang
*/
-@Component("fullDiscountCmp")
+@LiteflowComponent(id = FULL_DISCOUNT_CMP, name = "满折计算组件")
public class FullDiscountCmp extends NodeComponent {
- @Override
- public void process() throws Exception {
- PriceContext context = this.getContextBean(PriceContext.class);
- PromotionPackVO promotionPack = getMatchPromotion();
- /***这里Mock下根据优惠信息查到的满折信息为:满200,打9折***/
- BigDecimal triggerPrice = new BigDecimal(200);
- BigDecimal discountRate = new BigDecimal("0.9");
+ /**
+ * 满折-优惠包
+ */
+ private PromotionPackVO promotionPack;
+
+ @Override
+ public void process() throws Exception {
+ if (Objects.isNull(promotionPack)) {
+ return;
+ }
- //从PromotionPack对象中取到这个优惠关联的商品信息,判断是否超过了触发满折的金额
- BigDecimal reletedProductTotalPrice = new BigDecimal(0);
- for(ProductPackVO productPack : promotionPack.getRelatedProductPackList()){
- reletedProductTotalPrice = reletedProductTotalPrice.add(productPack.getSalePrice().multiply(new BigDecimal(productPack.getCount())));
- }
- if (reletedProductTotalPrice.compareTo(triggerPrice) >= 0){
- BigDecimal prePrice = context.getLastestPriceStep().getCurrPrice();
- BigDecimal currPrice = prePrice.multiply(discountRate).setScale(2, RoundingMode.HALF_UP);
+ PriceContext context = this.getContextBean(PriceContext.class);
- context.addPriceStep(new PriceStepVO(PriceTypeEnum.PROMOTION_DISCOUNT,
- promotionPack.getId().toString(),
- prePrice,
- currPrice.subtract(prePrice),
- currPrice,
- PriceTypeEnum.PROMOTION_DISCOUNT.getName() + "[满折]"));
- }
+ // 这里Mock下根据优惠信息查到的满折信息为:满200,打9折
+ BigDecimal triggerPrice = BigDecimal.valueOf(200);
+ BigDecimal discountRate = new BigDecimal("0.9");
+ //从PromotionPack对象中取到这个优惠关联的商品信息,判断是否超过了触发满折的金额
+ BigDecimal reletedProductTotalPrice = BigDecimal.ZERO;
+ for (ProductPackVO productPack : promotionPack.getRelatedProductPackSet()) {
+ reletedProductTotalPrice = reletedProductTotalPrice.add(productPack.getSalePrice()
+ .multiply(BigDecimal.valueOf(productPack.getCount())));
}
- @Override
- public boolean isAccess() {
- //过滤出优惠信息列表中有没有满折这个活动,如果有,则进入这个组件,反义就不进入
- PromotionPackVO promotionPack = getMatchPromotion();
- if(promotionPack != null){
- return true;
- }else{
- return false;
- }
+ if (reletedProductTotalPrice.compareTo(triggerPrice) >= 0) {
+ BigDecimal prePrice = context.getLastestPriceStep().getCurrPrice();
+ BigDecimal currPrice = prePrice.multiply(discountRate)
+ .setScale(2, RoundingMode.HALF_UP);
+
+ context.addPriceStep(PriceStepVO.builder()
+ .priceType(PriceTypeEnum.PROMOTION_DISCOUNT)
+ .extId(promotionPack.getId().toString())
+ .prePrice(prePrice)
+ .priceChange(currPrice.subtract(prePrice))
+ .currPrice(currPrice)
+ .stepDesc(PriceTypeEnum.PROMOTION_DISCOUNT.getName() + "[满折]")
+ .build());
}
- private PromotionPackVO getMatchPromotion(){
- PriceContext context = this.getContextBean(PriceContext.class);
+ }
- List matchList = context.getPromotionPackList().stream().filter(promotionPackVO -> {
- if(promotionPackVO.getPromotionType().equals(PromotionTypeEnum.FULL_DISCOUNT)){
- return true;
- }else{
- return false;
- }
- }).collect(Collectors.toList());
+ @Override
+ public boolean isAccess() {
+ // 过滤出优惠信息列表中有没有满折这个活动,如果有,则进入这个组件,反义就不进入
+ Optional matchPromotion = getMatchPromotion();
+ this.promotionPack = matchPromotion.orElse(null);
- if(CollectionUtils.isNotEmpty(matchList)){
- return matchList.get(0);
- }else{
- return null;
- }
- }
+ return matchPromotion.isPresent();
+ }
+
+ private Optional getMatchPromotion() {
+ PriceContext context = this.getContextBean(PriceContext.class);
+
+ return context.getPromotionPackList().stream()
+ .filter(promotionPackVO -> promotionPackVO.getPromotionType()
+ .equals(PromotionTypeEnum.FULL_DISCOUNT))
+ .findFirst();
+ }
}
diff --git a/src/main/java/com/yomahub/liteflow/example/component/MemberDiscountCmp.java b/src/main/java/com/yomahub/liteflow/example/component/MemberDiscountCmp.java
index d78c174..52ee182 100644
--- a/src/main/java/com/yomahub/liteflow/example/component/MemberDiscountCmp.java
+++ b/src/main/java/com/yomahub/liteflow/example/component/MemberDiscountCmp.java
@@ -1,22 +1,24 @@
package com.yomahub.liteflow.example.component;
+import static com.yomahub.liteflow.example.component.NodeIdConstant.MEMBER_DISCOUNT_CMP;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.example.bean.PriceStepVO;
import com.yomahub.liteflow.example.enums.PriceTypeEnum;
import com.yomahub.liteflow.example.slot.PriceContext;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
-import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* 会员折扣计算组件
- *
+ *
* @author bryan.zhang
*/
-@Component("memberDiscountCmp")
+@LiteflowComponent(id = MEMBER_DISCOUNT_CMP, name = "会员折扣计算组件")
public class MemberDiscountCmp extends NodeComponent {
@Override
public void process() throws Exception {
@@ -26,18 +28,20 @@ public class MemberDiscountCmp extends NodeComponent {
// 这里Mock下通过memberCode去查会员等级表然后获取的会员折扣为9折的代码
BigDecimal memberDiscount = new BigDecimal("0.9");
- //进行计算会员折扣
+ // 进行计算会员折扣
BigDecimal prePrice = context.getLastestPriceStep().getCurrPrice();
- BigDecimal currPrice = prePrice.multiply(memberDiscount).setScale(2, RoundingMode.HALF_UP);
-
- //加入到价格步骤中
- context.addPriceStep(new PriceStepVO(PriceTypeEnum.MEMBER_DISCOUNT,
- memberCode,
- prePrice,
- currPrice.subtract(prePrice),
- currPrice,
- PriceTypeEnum.MEMBER_DISCOUNT.getName()));
-
+ BigDecimal currPrice = prePrice.multiply(memberDiscount)
+ .setScale(2, RoundingMode.HALF_UP);
+
+ // 加入到价格步骤中
+ context.addPriceStep(PriceStepVO.builder()
+ .priceType(PriceTypeEnum.MEMBER_DISCOUNT)
+ .extId(memberCode)
+ .prePrice(prePrice)
+ .priceChange(currPrice.subtract(prePrice))
+ .currPrice(currPrice)
+ .stepDesc(PriceTypeEnum.MEMBER_DISCOUNT.getName())
+ .build());
}
@Override
diff --git a/src/main/java/com/yomahub/liteflow/example/component/NodeIdConstant.java b/src/main/java/com/yomahub/liteflow/example/component/NodeIdConstant.java
index 10f8dae..1c0bf00 100644
--- a/src/main/java/com/yomahub/liteflow/example/component/NodeIdConstant.java
+++ b/src/main/java/com/yomahub/liteflow/example/component/NodeIdConstant.java
@@ -5,30 +5,45 @@ package com.yomahub.liteflow.example.component;
*
* @author 李小平
*/
-public interface NodeIdConstant {
+public class NodeIdConstant {
/**
* 初始化参数检查组件Id
*/
- String CHECK_CMP = "checkCmp";
+ public static final String CHECK_CMP = "checkCmp";
/**
* Slot初始化组件Id
*/
- String SLOT_INIT_CMP = "slotInitCmp";
+ public static final String SLOT_INIT_CMP = "slotInitCmp";
/**
* 优惠信息转换组件
*/
- String PROMOTION_CONVERT_CMP = "promotionConvertCmp";
+ public static final String PROMOTION_CONVERT_CMP = "promotionConvertCmp";
/**
* 优惠券抵扣计算组件Id
*/
- String COUPON_CMP = "couponCmp";
+ public static final String COUPON_CMP = "couponCmp";
/**
* 步骤日志生成组件Id
*/
- String STEP_PRINT_CMP = "stepPrintCmp";
+ public static final String STEP_PRINT_CMP = "stepPrintCmp";
+
+ /**
+ * 会员折扣计算组件Id
+ */
+ public static final String MEMBER_DISCOUNT_CMP = "memberDiscountCmp";
+
+ /**
+ * 满减计算组件Id
+ */
+ public static final String FULL_CUT_CMP = "fullCutCmp";
+
+ /**
+ * 满折计算组件Id
+ */
+ public static final String FULL_DISCOUNT_CMP = "fullDiscountCmp";
}
diff --git a/src/main/java/com/yomahub/liteflow/example/component/PromotionConvertCmp.java b/src/main/java/com/yomahub/liteflow/example/component/PromotionConvertCmp.java
index fad118d..cd38b7d 100644
--- a/src/main/java/com/yomahub/liteflow/example/component/PromotionConvertCmp.java
+++ b/src/main/java/com/yomahub/liteflow/example/component/PromotionConvertCmp.java
@@ -2,7 +2,6 @@ package com.yomahub.liteflow.example.component;
import static com.yomahub.liteflow.example.component.NodeIdConstant.PROMOTION_CONVERT_CMP;
-import cn.hutool.core.collection.ListUtil;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.example.bean.ProductPackVO;
@@ -23,8 +22,9 @@ import org.springframework.beans.BeanUtils;
public class PromotionConvertCmp extends NodeComponent {
@Override
- public void process() throws Exception {
+ public void process() {
PriceContext context = this.getContextBean(PriceContext.class);
+ // 优惠包
List promotionPackList = new ArrayList<>();
PromotionPackVO promotionPack;
@@ -36,14 +36,13 @@ public class PromotionConvertCmp extends NodeComponent {
for (PromotionInfoVO promotion : pack.getPromotionList()) {
promotionPack = new PromotionPackVO();
promotionPack.setId(promotion.getId());
- if (promotionPackList.contains(promotionPack)) {
- promotionPack = promotionPackList.get(promotionPackList.indexOf(promotionPack));
- if (!promotionPack.getRelatedProductPackList().contains(pack)) {
- promotionPack.getRelatedProductPackList().add(pack);
- }
+ int index = promotionPackList.indexOf(promotionPack);
+ if (index >= 0) {
+ promotionPack = promotionPackList.get(index);
+ promotionPack.getRelatedProductPackSet().add(pack);
} else {
BeanUtils.copyProperties(promotion, promotionPack);
- promotionPack.setRelatedProductPackList(ListUtil.toList(pack));
+ promotionPack.getRelatedProductPackSet().add(pack);
promotionPackList.add(promotionPack);
}
}
diff --git a/src/main/java/com/yomahub/liteflow/example/component/RushBuyCmp.java b/src/main/java/com/yomahub/liteflow/example/component/RushBuyCmp.java
index 67a162a..e958399 100644
--- a/src/main/java/com/yomahub/liteflow/example/component/RushBuyCmp.java
+++ b/src/main/java/com/yomahub/liteflow/example/component/RushBuyCmp.java
@@ -35,7 +35,7 @@ public class RushBuyCmp extends NodeComponent {
BigDecimal prePrice = context.getLastestPriceStep().getCurrPrice();
BigDecimal rushBuyDiscountPrice = new BigDecimal(0);
- for(ProductPackVO productPack : promotionPack.getRelatedProductPackList()){
+ for(ProductPackVO productPack : promotionPack.getRelatedProductPackSet()){
rushBuyDiscountPrice = rushBuyDiscountPrice.add(productPack.getSalePrice().subtract(rushBuyPrice)
.multiply(new BigDecimal(productPack.getCount()))).setScale(2, RoundingMode.HALF_UP);
}
diff --git a/src/main/java/com/yomahub/liteflow/example/enums/PromotionTypeEnum.java b/src/main/java/com/yomahub/liteflow/example/enums/PromotionTypeEnum.java
index b40ac0e..9a74837 100644
--- a/src/main/java/com/yomahub/liteflow/example/enums/PromotionTypeEnum.java
+++ b/src/main/java/com/yomahub/liteflow/example/enums/PromotionTypeEnum.java
@@ -1,13 +1,34 @@
package com.yomahub.liteflow.example.enums;
+import lombok.Getter;
+
+/**
+ * 优惠类型
+ *
+ * @author bryan.zhang
+ */
public enum PromotionTypeEnum {
+
+ /**
+ * 满减
+ */
FULL_CUT(1, "满减"),
+
+ /**
+ * 满折
+ */
FULL_DISCOUNT(2, "满折"),
+
+ /**
+ * 抢购
+ */
RUSH_BUY(3, "抢购");
- private Integer id;
+ @Getter
+ private final Integer id;
- private String name;
+ @Getter
+ private final String name;
PromotionTypeEnum(int id, String name){
this.id = id;
--
Gitee
From 872cb7d9f97bdd86a057b3718c14e99b1869541b Mon Sep 17 00:00:00 2001
From: lixiaoping <李小平17773406760@189.cn>
Date: Mon, 21 Nov 2022 18:00:49 +0800
Subject: [PATCH 03/10] =?UTF-8?q?=E9=83=A8=E5=88=86=E5=8A=9F=E8=83=BD?=
=?UTF-8?q?=E9=87=8D=E6=9E=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../example/component/RushBuyCmp.java | 78 ++++++++++---------
1 file changed, 40 insertions(+), 38 deletions(-)
diff --git a/src/main/java/com/yomahub/liteflow/example/component/RushBuyCmp.java b/src/main/java/com/yomahub/liteflow/example/component/RushBuyCmp.java
index e958399..6d32b93 100644
--- a/src/main/java/com/yomahub/liteflow/example/component/RushBuyCmp.java
+++ b/src/main/java/com/yomahub/liteflow/example/component/RushBuyCmp.java
@@ -1,5 +1,6 @@
package com.yomahub.liteflow.example.component;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.example.bean.PriceStepVO;
import com.yomahub.liteflow.example.bean.ProductPackVO;
@@ -7,75 +8,76 @@ import com.yomahub.liteflow.example.bean.PromotionPackVO;
import com.yomahub.liteflow.example.enums.PriceTypeEnum;
import com.yomahub.liteflow.example.enums.PromotionTypeEnum;
import com.yomahub.liteflow.example.slot.PriceContext;
-import org.apache.commons.collections4.CollectionUtils;
-import org.springframework.stereotype.Component;
+import java.util.Objects;
+import java.util.Optional;
import java.math.BigDecimal;
import java.math.RoundingMode;
-import java.util.List;
-import java.util.stream.Collectors;
+
/**
* 抢购计算组件
+ *
+ * @author bryan.zhang
*/
-@Component("rushBuyCmp")
+@LiteflowComponent(id = "rushBuyCmp", name = "抢购计算组件")
public class RushBuyCmp extends NodeComponent {
+
+ /**
+ * 抢购-优惠
+ */
+ private PromotionPackVO promotionPack;
+
@Override
public void process() throws Exception {
+ if (Objects.isNull(promotionPack)) {
+ return;
+ }
+
PriceContext context = this.getContextBean(PriceContext.class);
- PromotionPackVO promotionPack = getMatchPromotion();
- /**
+ /*
* 这里Mock下根据优惠信息查到的抢购信息为:1块钱抢购
* 这里要注意的是,实际情况下,这个抢购活动所关联的商品,每个商品的抢购价格都不同
* 这里为了Mock方便,所关联的商品,每个SKU抢购价都是1元
* ps:抢购原则上和其他优惠活动互斥,这里就不写出互斥的逻辑了,在设置参数时请注意
**/
- BigDecimal rushBuyPrice = new BigDecimal(1);
+ BigDecimal rushBuyPrice = BigDecimal.ONE;
BigDecimal prePrice = context.getLastestPriceStep().getCurrPrice();
- BigDecimal rushBuyDiscountPrice = new BigDecimal(0);
- for(ProductPackVO productPack : promotionPack.getRelatedProductPackSet()){
- rushBuyDiscountPrice = rushBuyDiscountPrice.add(productPack.getSalePrice().subtract(rushBuyPrice)
- .multiply(new BigDecimal(productPack.getCount()))).setScale(2, RoundingMode.HALF_UP);
+ BigDecimal rushBuyDiscountPrice = BigDecimal.ZERO;
+ for (ProductPackVO productPack : promotionPack.getRelatedProductPackSet()){
+ rushBuyDiscountPrice = rushBuyDiscountPrice.add(productPack.getSalePrice()
+ .subtract(rushBuyPrice)
+ .multiply(BigDecimal.valueOf(productPack.getCount())))
+ .setScale(2, RoundingMode.HALF_UP);
}
BigDecimal currPrice = prePrice.subtract(rushBuyDiscountPrice);
- context.addPriceStep(new PriceStepVO(PriceTypeEnum.PROMOTION_DISCOUNT,
- promotionPack.getId().toString(),
- prePrice,
- currPrice.subtract(prePrice),
- currPrice,
- PriceTypeEnum.PROMOTION_DISCOUNT.getName() + "[抢购]"));
-
+ context.addPriceStep(PriceStepVO.builder()
+ .priceType(PriceTypeEnum.PROMOTION_DISCOUNT)
+ .extId(promotionPack.getId().toString())
+ .prePrice(prePrice)
+ .priceChange(currPrice.subtract(prePrice))
+ .currPrice(currPrice)
+ .stepDesc(PriceTypeEnum.PROMOTION_DISCOUNT.getName() + "[抢购]")
+ .build());
}
@Override
public boolean isAccess() {
//过滤出优惠信息列表中有没有抢购这个活动,如果有,则进入这个组件,反义就不进入
- PromotionPackVO promotionPack = getMatchPromotion();
- if(promotionPack != null){
- return true;
- }else{
- return false;
- }
+ Optional matchPromotion = getMatchPromotion();
+ this.promotionPack = matchPromotion.orElse(null);
+ return matchPromotion.isPresent();
}
- private PromotionPackVO getMatchPromotion(){
+ private Optional getMatchPromotion(){
PriceContext context = this.getContextBean(PriceContext.class);
- List matchList = context.getPromotionPackList().stream().filter(promotionPackVO -> {
- if(promotionPackVO.getPromotionType().equals(PromotionTypeEnum.RUSH_BUY)){
- return true;
- }else{
- return false;
- }
- }).collect(Collectors.toList());
+ return context.getPromotionPackList().stream()
+ .filter(promotionPackVO -> promotionPackVO.getPromotionType().equals(PromotionTypeEnum.RUSH_BUY))
+ .findFirst();
- if(CollectionUtils.isNotEmpty(matchList)){
- return matchList.get(0);
- }else{
- return null;
- }
}
}
--
Gitee
From dceaa17979ea70ff85e72b2bd8e1e61770e3a9b9 Mon Sep 17 00:00:00 2001
From: lixiaoping <李小平17773406760@189.cn>
Date: Mon, 21 Nov 2022 18:02:35 +0800
Subject: [PATCH 04/10] =?UTF-8?q?=E9=83=A8=E5=88=86=E5=8A=9F=E8=83=BD?=
=?UTF-8?q?=E9=87=8D=E6=9E=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../yomahub/liteflow/example/component/PriceResultCmp.java | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/src/main/java/com/yomahub/liteflow/example/component/PriceResultCmp.java b/src/main/java/com/yomahub/liteflow/example/component/PriceResultCmp.java
index 141b94d..8402463 100644
--- a/src/main/java/com/yomahub/liteflow/example/component/PriceResultCmp.java
+++ b/src/main/java/com/yomahub/liteflow/example/component/PriceResultCmp.java
@@ -27,10 +27,6 @@ public class PriceResultCmp extends NodeComponent {
@Override
public boolean isAccess() {
PriceContext context = this.getContextBean(PriceContext.class);
- if(CollectionUtils.isNotEmpty(context.getPriceStepList())){
- return true;
- }else{
- return false;
- }
+ return CollectionUtils.isNotEmpty(context.getPriceStepList());
}
}
--
Gitee
From 928348be3ba5d25a80a334095d4342cd0a04b32d Mon Sep 17 00:00:00 2001
From: lixiaoping <李小平17773406760@189.cn>
Date: Mon, 21 Nov 2022 18:41:23 +0800
Subject: [PATCH 05/10] =?UTF-8?q?=E9=83=A8=E5=88=86=E5=8A=9F=E8=83=BD?=
=?UTF-8?q?=E9=87=8D=E6=9E=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../example/component/NodeIdConstant.java | 19 +++++++
.../example/component/OverseaPostageCmp.java | 10 ++--
.../example/component/PostageCmp.java | 49 +++++++++++--------
.../example/component/PostageCondCmp.java | 28 ++++++-----
.../example/component/PriceResultCmp.java | 1 +
5 files changed, 71 insertions(+), 36 deletions(-)
diff --git a/src/main/java/com/yomahub/liteflow/example/component/NodeIdConstant.java b/src/main/java/com/yomahub/liteflow/example/component/NodeIdConstant.java
index 1c0bf00..30c28c9 100644
--- a/src/main/java/com/yomahub/liteflow/example/component/NodeIdConstant.java
+++ b/src/main/java/com/yomahub/liteflow/example/component/NodeIdConstant.java
@@ -1,10 +1,14 @@
package com.yomahub.liteflow.example.component;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
/**
* 流程组件Id常量声明
*
* @author 李小平
*/
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class NodeIdConstant {
/**
* 初始化参数检查组件Id
@@ -46,4 +50,19 @@ public class NodeIdConstant {
* 满折计算组件Id
*/
public static final String FULL_DISCOUNT_CMP = "fullDiscountCmp";
+
+ /**
+ * 运费条件组件
+ */
+ public static final String POSTAGE_COND_CMP = "postageCondCmp";
+
+ /**
+ * 国内运费计算组件
+ */
+ public static final String POSTAGE_CMP = "postageCmp";
+
+ /**
+ * 境外购运费计算组件
+ */
+ public static final String OVERSEA_POSTAGE_CMP = "overseaPostageCmp";
}
diff --git a/src/main/java/com/yomahub/liteflow/example/component/OverseaPostageCmp.java b/src/main/java/com/yomahub/liteflow/example/component/OverseaPostageCmp.java
index bdb3112..5db373d 100644
--- a/src/main/java/com/yomahub/liteflow/example/component/OverseaPostageCmp.java
+++ b/src/main/java/com/yomahub/liteflow/example/component/OverseaPostageCmp.java
@@ -1,5 +1,8 @@
package com.yomahub.liteflow.example.component;
+import static com.yomahub.liteflow.example.component.NodeIdConstant.OVERSEA_POSTAGE_CMP;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.example.bean.PriceStepVO;
import com.yomahub.liteflow.example.enums.PriceTypeEnum;
@@ -10,15 +13,16 @@ import java.math.BigDecimal;
/**
* 境外购运费计算组件
+ * @author bryan.zhang
*/
-@Component("overseaPostageCmp")
+@LiteflowComponent(id = OVERSEA_POSTAGE_CMP, name = "境外购运费计算组件")
public class OverseaPostageCmp extends NodeComponent {
@Override
public void process() throws Exception {
PriceContext context = this.getContextBean(PriceContext.class);
- /**这里Mock境外购运费的策略是:不管多少钱,都要加上15元运费**/
- BigDecimal postage = new BigDecimal(15);
+ // 这里Mock境外购运费的策略是:不管多少钱,都要加上15元运费
+ BigDecimal postage = BigDecimal.valueOf(15);
BigDecimal prePrice = context.getLastestPriceStep().getCurrPrice();
BigDecimal currPrice = prePrice.add(postage);
diff --git a/src/main/java/com/yomahub/liteflow/example/component/PostageCmp.java b/src/main/java/com/yomahub/liteflow/example/component/PostageCmp.java
index 668e2dd..2f1a9a2 100644
--- a/src/main/java/com/yomahub/liteflow/example/component/PostageCmp.java
+++ b/src/main/java/com/yomahub/liteflow/example/component/PostageCmp.java
@@ -1,47 +1,56 @@
package com.yomahub.liteflow.example.component;
+import static com.yomahub.liteflow.example.component.NodeIdConstant.POSTAGE_CMP;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.example.bean.PriceStepVO;
import com.yomahub.liteflow.example.enums.PriceTypeEnum;
import com.yomahub.liteflow.example.slot.PriceContext;
-import org.springframework.stereotype.Component;
import java.math.BigDecimal;
/**
* 国内运费计算组件
+ *
+ * @author bryan.zhang
*/
-@Component("postageCmp")
+@LiteflowComponent(id = POSTAGE_CMP, name = "国内运费计算组件")
public class PostageCmp extends NodeComponent {
@Override
public void process() throws Exception {
PriceContext context = this.getContextBean(PriceContext.class);
- /**这里Mock运费的策略是:满99免运费,不满99需要10块钱运费**/
- BigDecimal triggerPrice = new BigDecimal(99);
- BigDecimal postage = new BigDecimal(10);
- //先把运费加上去
+ // 这里Mock运费的策略是:满99免运费,不满99需要10块钱运费
+ BigDecimal triggerPrice = BigDecimal.valueOf(99);
+ BigDecimal postage = BigDecimal.valueOf(10);
+
+ // 先把运费加上去
BigDecimal prePrice = context.getLastestPriceStep().getCurrPrice();
BigDecimal currPrice = prePrice.add(postage);
- context.addPriceStep(new PriceStepVO(PriceTypeEnum.POSTAGE,
- null,
- prePrice,
- currPrice.subtract(prePrice),
- currPrice,
- PriceTypeEnum.POSTAGE.getName()));
+ context.addPriceStep(PriceStepVO.builder()
+ .priceType(PriceTypeEnum.POSTAGE)
+ .extId(null)
+ .prePrice(prePrice)
+ .priceChange(currPrice.subtract(prePrice))
+ .currPrice(currPrice)
+ .stepDesc(PriceTypeEnum.POSTAGE.getName())
+ .build());
- //判断运费是否满99了,满了99就去掉运费
- if(prePrice.compareTo(triggerPrice) >= 0){
+ // 判断运费是否满99了,满了99就去掉运费
+ if (prePrice.compareTo(triggerPrice) >= 0) {
prePrice = context.getLastestPriceStep().getCurrPrice();
currPrice = currPrice.subtract(postage);
- context.addPriceStep(new PriceStepVO(PriceTypeEnum.POSTAGE_FREE,
- null,
- prePrice,
- currPrice.subtract(prePrice),
- currPrice,
- PriceTypeEnum.POSTAGE_FREE.getName()));
+ context.addPriceStep(PriceStepVO.builder()
+ .priceType(PriceTypeEnum.POSTAGE_FREE)
+ .extId(null)
+ .prePrice(prePrice)
+ .priceChange(currPrice.subtract(prePrice))
+ .currPrice(currPrice)
+ .stepDesc(PriceTypeEnum.POSTAGE_FREE.getName())
+ .build());
}
}
}
diff --git a/src/main/java/com/yomahub/liteflow/example/component/PostageCondCmp.java b/src/main/java/com/yomahub/liteflow/example/component/PostageCondCmp.java
index 4b920f2..b8cbb43 100644
--- a/src/main/java/com/yomahub/liteflow/example/component/PostageCondCmp.java
+++ b/src/main/java/com/yomahub/liteflow/example/component/PostageCondCmp.java
@@ -1,23 +1,25 @@
package com.yomahub.liteflow.example.component;
+import static com.yomahub.liteflow.example.component.NodeIdConstant.OVERSEA_POSTAGE_CMP;
+import static com.yomahub.liteflow.example.component.NodeIdConstant.POSTAGE_CMP;
+import static com.yomahub.liteflow.example.component.NodeIdConstant.POSTAGE_COND_CMP;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeSwitchComponent;
import com.yomahub.liteflow.example.slot.PriceContext;
-import org.springframework.stereotype.Component;
/**
* 运费条件组件
+ *
+ * @author bryan.zhang
*/
-@Component("postageCondCmp")
+@LiteflowComponent(id = POSTAGE_COND_CMP, name = "运费条件组件")
public class PostageCondCmp extends NodeSwitchComponent {
- @Override
- public String processSwitch() throws Exception {
- PriceContext context = this.getContextBean(PriceContext.class);
- //根据参数oversea来判断是否境外购,转到相应的组件
- boolean oversea = context.isOversea();
- if(oversea){
- return "overseaPostageCmp";
- }else{
- return "postageCmp";
- }
- }
+
+ @Override
+ public String processSwitch() {
+ PriceContext context = this.getContextBean(PriceContext.class);
+ // 根据参数oversea来判断是否境外购,转到相应的组件
+ return context.isOversea() ? OVERSEA_POSTAGE_CMP : POSTAGE_CMP;
+ }
}
diff --git a/src/main/java/com/yomahub/liteflow/example/component/PriceResultCmp.java b/src/main/java/com/yomahub/liteflow/example/component/PriceResultCmp.java
index 8402463..84d01ff 100644
--- a/src/main/java/com/yomahub/liteflow/example/component/PriceResultCmp.java
+++ b/src/main/java/com/yomahub/liteflow/example/component/PriceResultCmp.java
@@ -10,6 +10,7 @@ import java.math.BigDecimal;
/**
* 订单最终价格计算器
+ * @author LiXiaoPing
*/
@Component("priceResultCmp")
public class PriceResultCmp extends NodeComponent {
--
Gitee
From 4522822698f220a34d612960a32c7ef7a28d570c Mon Sep 17 00:00:00 2001
From: lixiaoping <李小平17773406760@189.cn>
Date: Mon, 21 Nov 2022 19:15:31 +0800
Subject: [PATCH 06/10] =?UTF-8?q?=E9=83=A8=E5=88=86=E5=8A=9F=E8=83=BD?=
=?UTF-8?q?=E9=87=8D=E6=9E=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 8 +++-
.../example/LiteflowExampleApplication.java | 41 ++++++++++++++++++-
.../controller/PriceExampleController.java | 22 +++++-----
.../example/enums/OrderChannelEnum.java | 35 +++++++++++++++-
.../liteflow/example/enums/SkuSourceEnum.java | 31 +++++++++++---
src/main/resources/application.properties | 2 -
src/main/resources/application.yml | 10 +++++
7 files changed, 127 insertions(+), 22 deletions(-)
delete mode 100644 src/main/resources/application.properties
create mode 100644 src/main/resources/application.yml
diff --git a/README.md b/README.md
index 8161038..07cdd39 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,9 @@
此工程是[LiteFlow](https://gitee.com/dromara/liteFlow)框架的示例工程。
-master目前是基于v2.8.0版本的
\ No newline at end of file
+## 前端UI
+
+http://localhost:8580/
+
+
+
+master目前是基于v2.9.3版本的
\ No newline at end of file
diff --git a/src/main/java/com/yomahub/liteflow/example/LiteflowExampleApplication.java b/src/main/java/com/yomahub/liteflow/example/LiteflowExampleApplication.java
index dc681c9..00308dd 100644
--- a/src/main/java/com/yomahub/liteflow/example/LiteflowExampleApplication.java
+++ b/src/main/java/com/yomahub/liteflow/example/LiteflowExampleApplication.java
@@ -1,13 +1,52 @@
package com.yomahub.liteflow.example;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Optional;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.core.env.Environment;
+/**
+ * @author bryan.zhang
+ */
@SpringBootApplication
+@Slf4j
public class LiteflowExampleApplication {
public static void main(String[] args) {
- SpringApplication.run(LiteflowExampleApplication.class, args);
+ SpringApplication app = new SpringApplication(LiteflowExampleApplication.class);
+ Environment env = app.run(args).getEnvironment();
+ logApplicationStartup(env);
+ }
+
+ private static void logApplicationStartup(Environment env) {
+ String protocol = Optional.ofNullable(env.getProperty("server.ssl.key-store")).map(key -> "https").orElse("http");
+ String serverPort = env.getProperty("server.port");
+ String contextPath = Optional
+ .ofNullable(env.getProperty("server.servlet.context-path"))
+ .filter(StringUtils::isNotBlank)
+ .orElse("/");
+ String hostAddress = "localhost";
+ try {
+ hostAddress = InetAddress.getLocalHost().getHostAddress();
+ } catch (UnknownHostException e) {
+ log.warn("The host name could not be determined, using `localhost` as fallback");
+ }
+ log.info(
+ "\n----------------------------------------------------------\n\t" +
+ "Application '{}' is running! Access URLs:\n\t" +
+ "Local: \t\t{}://localhost:{}{}\n\t" +
+ "External: \t{}://{}:{}{}\n\t" +
+ "Profile(s): \t{}" +
+ "\n----------------------------------------------------------",
+ env.getProperty("spring.application.name"),
+ protocol, serverPort, contextPath,
+ protocol, hostAddress, serverPort, contextPath,
+ env.getActiveProfiles()
+ );
}
}
diff --git a/src/main/java/com/yomahub/liteflow/example/controller/PriceExampleController.java b/src/main/java/com/yomahub/liteflow/example/controller/PriceExampleController.java
index a78e182..b493a1e 100644
--- a/src/main/java/com/yomahub/liteflow/example/controller/PriceExampleController.java
+++ b/src/main/java/com/yomahub/liteflow/example/controller/PriceExampleController.java
@@ -22,32 +22,32 @@ import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
+/**
+ * @author bryan.zhang
+ */
@Controller
public class PriceExampleController {
@Resource
private FlowExecutor flowExecutor;
- @RequestMapping(value = "/", method = RequestMethod.GET)
+ @GetMapping(value = "/")
public String index(ModelMap modelMap){
PriceCalcReqVO req = mockReq();
String json = JSON.toJSONString(req);
- modelMap.put("req",json);
+ modelMap.put("req", json);
return "index";
}
- @RequestMapping(value = "/submit", method = RequestMethod.POST)
+ @PostMapping (value = "/submit")
@ResponseBody
public String submit(@Nullable @RequestBody String reqData){
- try{
- PriceCalcReqVO req = JSON.parseObject(reqData,PriceCalcReqVO.class);
- LiteflowResponse response = flowExecutor.execute2Resp("mainChain", req, PriceContext.class);
- return response.getContextBean(PriceContext.class).getPrintLog();
- }catch (Throwable t){
- t.printStackTrace();
- return "error";
- }
+ PriceCalcReqVO req = JSON.parseObject(reqData,PriceCalcReqVO.class);
+ LiteflowResponse response = flowExecutor.execute2Resp("mainChain", req, PriceContext.class);
+
+ return response.getContextBean(PriceContext.class).getPrintLog();
+
}
private PriceCalcReqVO mockReq(){
diff --git a/src/main/java/com/yomahub/liteflow/example/enums/OrderChannelEnum.java b/src/main/java/com/yomahub/liteflow/example/enums/OrderChannelEnum.java
index ee639f2..12af0cb 100644
--- a/src/main/java/com/yomahub/liteflow/example/enums/OrderChannelEnum.java
+++ b/src/main/java/com/yomahub/liteflow/example/enums/OrderChannelEnum.java
@@ -1,17 +1,48 @@
package com.yomahub.liteflow.example.enums;
+import lombok.Getter;
+
+/**
+ * @author bryan.zhang
+ */
+
public enum OrderChannelEnum {
+ /**
+ * APP渠道
+ */
APP(1,"APP渠道"),
+
+ /**
+ * 小程序渠道
+ */
MINI_PROGRAM(2,"小程序渠道"),
+
+ /**
+ * 微信H5
+ */
WX_H5(3,"微信H5"),
+
+ /**
+ * 移动H5渠道
+ */
MOBILE_H5(4,"移动H5渠道"),
+
+ /**
+ * PC主站渠道
+ */
WEB_PORTAL(5,"PC主站渠道"),
+
+ /**
+ * 线下门店渠道
+ */
OFFLINE_STORE(5,"线下门店渠道");
- private Integer id;
+ @Getter
+ private final Integer id;
- private String name;
+ @Getter
+ private final String name;
OrderChannelEnum(int id, String name){
this.id = id;
diff --git a/src/main/java/com/yomahub/liteflow/example/enums/SkuSourceEnum.java b/src/main/java/com/yomahub/liteflow/example/enums/SkuSourceEnum.java
index 508cc74..9f058af 100644
--- a/src/main/java/com/yomahub/liteflow/example/enums/SkuSourceEnum.java
+++ b/src/main/java/com/yomahub/liteflow/example/enums/SkuSourceEnum.java
@@ -7,18 +7,39 @@
*/
package com.yomahub.liteflow.example.enums;
+import lombok.Getter;
+
/**
* 商品来源枚举
+ * @author bryan.zhang
*/
public enum SkuSourceEnum {
- RAW(0, "自购"),
- GIFT(3, "买赠"),
- ADDITION(4,"换购"),
+
+ /**
+ * 自购
+ */
+ RAW(0, "自购"),
+
+ /**
+ * 买赠
+ */
+ GIFT(3, "买赠"),
+
+ /**
+ * 换购
+ */
+ ADDITION(4,"换购"),
+
+ /**
+ * 权益商品
+ */
BENEFIT(5,"权益商品");
- private Integer code;
+ @Getter
+ private final Integer code;
- private String name;
+ @Getter
+ private final String name;
SkuSourceEnum(Integer code, String name) {
this.code = code;
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
deleted file mode 100644
index 7e09a44..0000000
--- a/src/main/resources/application.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-server.port=8580
-liteflow.ruleSource=liteflow/*.el.xml
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
new file mode 100644
index 0000000..ad72fd6
--- /dev/null
+++ b/src/main/resources/application.yml
@@ -0,0 +1,10 @@
+
+liteflow:
+ rule-source: liteflow/*.el.xml
+server:
+ port: 8580
+spring:
+ application:
+ name: liteflow-example
+ profiles:
+ active: default
--
Gitee
From 971edef04f1e857f813d50245653590187c655f9 Mon Sep 17 00:00:00 2001
From: lixiaoping <李小平17773406760@189.cn>
Date: Mon, 21 Nov 2022 19:15:43 +0800
Subject: [PATCH 07/10] =?UTF-8?q?=E9=83=A8=E5=88=86=E5=8A=9F=E8=83=BD?=
=?UTF-8?q?=E9=87=8D=E6=9E=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Client-GUI.png | Bin 0 -> 83031 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 Client-GUI.png
diff --git a/Client-GUI.png b/Client-GUI.png
new file mode 100644
index 0000000000000000000000000000000000000000..fde347fbae2bfbe1b2524fd857efbfc221336750
GIT binary patch
literal 83031
zcmcG$bx>SE*EdLn1OfyN1PJc#uEE{i-6c2-mIR03?!leG-Q5Z95Zv7vWbfp8-rfCU
zzuK>AYr81w-pfq)>C?x5=MthICyw+1=K~B343eaTh!PCUI|CS)*K2UEfhYGeDS0q3
zgfNmKUsc@Gj#hjXw0F}59wK2DILo&=*)cyV7*vmx^EtHGS@l|mw|-1HJvOsawCzOK
zLPT1Yhwn%-+@yNnYi+w!r|-K`Me
ztqI8sV!y*rREgWjOUT|ANw~N3pmuVWfXhvf3@6g%oJjuh-3eJaIW&N>e?zcEO5@H+9MhCtm})eGvvKDk?56u7fF5yf?soa5NWR0g>Q4;F6I6
z_=ZorZ@K3E55bPpTM};QQxu+Pf&YF0xPyUlKzj9W&HK>uLK>+~=$t)T{_;9-QU61=
zppNiDAX!er@2qHu;}xlsOba=$g>+y&(_=`#+b!1Z-~y{^y#o{6EzEyLX_Y>S;?(
zP6l?fzrQbviv&FFh|#Dt>Rqh0j*SuG!=a;bmjmYfepd+>X_b|dk(Op6j?#Prr6s`+
z-1cDQ=;-+La4pRjC9%#0{rW~fF>i4|iV+tdU#rzj@%%OPVTDYoR_(#WP+yl54shEO
zDeA5wj0D~1zY%Qx|IhCJ!^;1cCA3Cfn`|CKtRX)+Zf6c|Gc({kZ+iBh9v1m;+*_r1
z(2qrjT>X{1eo27^e5iU*z5B0;z963v#?Yj2wq_oya5jJw@cKkNqZi%j@#e5<<Plp(535{)WqCZ^h}5lx>v7iuOIZZD5!VmXi1k{2
zk$CpLhlMjuG;HpXDhKq3rj`YX5xYK%4+jV$n|yLs-q!(t%u9ZmAH@x6KzT=8eL16?
z=ycB+K`WFycX*9W06FM|Yj
zwsn7_vfmA_5>r7^dY*Gq1hOPvSS|8kLWLj~UVj=%=v7xO2KwZQj9jl3dE+?lBkt*$
z`DrP!Z!SVtq5V^
zc|ApY>Ov#5(hODu1pDi4aCnx!si37Wr4Vv@&h)CzBYT2Vp7OavHwSr7h_)E!-ZhjP
zD0yucm&W32XjL^^k>+jPhHb4=uKNvonTnH=;;xeTm3>QVlh2_wU`Q~VF9U&Wss+PP
ztCv-2PHWcnl+NHLc4AGuXPmXi%YI16R6JRFjNr_-lK8zOuIahFu0oSYUoW+}0k(qc
zWLn~EU|)QY4l4g~P-HY4jjbIA3FSqhdvN-xA0r&
zgo_>;*Pa78ogvD2qWdC~=A7&$)-A0UFp}-_TWx(ZSjJAq1*KIC0zukk7v`N7_;34{
z*0_tW0}eE-e|*zfb7f={KH(Faon4Q}zDf`B=3fgi
zR{6udbLkRi7QLvf()Vh&t3?FLngZ80P-q$!acaf@Ld0DmMhc|`UM$~7*&6luC{`GX
z(V;}LP5Bxu(>UIxrE@82MJq+`!)@Izk>TlV*YdCI#Iuzp1sC6^v<Fq}yMv-s*Cf
zkJD!<8)s$iVOZ*Kbi!(CPHy@mGUM!!o9P{JIbQ9G?sv)t5^v=Xf^|(D1+P#g0WvfQ
zE8(Nyi?nOKNqspLOrl)QrbsE|PSyDhB->^XHNoo_+NxyE#a}n01El4LIscMc7OT64
z#QH0wXPd@`kidm=IY|wFw=%&G5r-^k_r`5F{IImGg~sy-
zBMFGcE&YQ#`7<}NI^QOXDkE+_X4;P|#%Ha$G`eVbRixpL2cP|_=i`Z&NcozeJI$YP
zU8mQ6=y<1u4{Yv?GEDY6Fq0qhhD=(5tP^7K*S@^Cg0@M6WbcjC>)sQ3)vue0i@ij#u^0Pl7{-co
z;8_q>Dk1{d#YShMy%74=!tS}|C&71+$8NEy2;H-5B|9=U=c9uKFw}OCjo(D1XUEu_
zhc?L6AE8OoAuROBZ^tfGFBGvdJyNZKlfH_H6fKO$-02J<*h#t1!UE1mTv4V#1FbUa
z%Z!Pm4@ss{hSu~dOR59hUJLUe
zhh1B2Plj>(>uw&~wM7T2r3}x-whcWa9#RFE(6O9{Su5RspZfVZuFgF2OCqn9OjMxt{zb0-vvKlw7yY^_PqsyT4D<-z-Jqkr6LyZbI&B5xwiC
zh7yVk7m^>Og2s0*b${!$`n>GJJ3RbUaLA-FCzAE8uKNi2*e;0PowzVruk)6!xrfRm
z3J-nO8r?E3g`UA7m{%`XXyjAOr+d0dlH
z{j**8xMXG*O`XyYq5NJ50}imYlkKr9sJ$3+9@tftvp@e{)PDW9ytLuOYm;JZ6DjXn
zP-|PvK>&_658!t6R`c{;3lm+(M^pkHZsua3N`L?CzOQOE;eC^lny&V2P|PY^Sgq(2xTLq
zyD4LV#NGn`knFGeCXRD1I>25(x?!|NQJ?0W0)9riwaV&KV#Hyn1hzI4gH^Ww1{ne#ldDA&tL>edOn3Ill5Wg=Y}%cq(dq7=5M
z8gu*U@=Ndeulmz}W^Qq1th+6u73=2kx-n5+GU2ts8`~96@8yGa8yu}uCSvZzCC|8E
zUGucXTXE%;Z166nL-kvYeNjBDy6SO%@7jl_Zg9jl4+{zj5S(hfGa?{tc7D8r{gC{1
z)-8?Un~co>Si#Aha$W{q
z5&10hQl_quc;v(n#9Hz4-$B&C_Pz&n&=GhP?Mi$-94}HDCs)0n2Vw9aL%aYFD(*@(
zhq9(g;;FTyiGJJ_JzmUL7=Fvi6M3u~^#Sc`Cnx)3;)E&QB~@RV*7OPbrYWqZGpJo}
zt$MECme+OYV8l#aJH-=i0bo3avna!tn_8^Ai;0eDb#725T~W^nboo5YO_+rV9q-%K
z?q(r^o_JFvy6gClM2#|e674ejk$tzx7hr7UQ_}47_2JN0B_e-JOfR(e(&~{)nRw4_X&Wk&M?$l~)v)
z5`f5jFT9>PfNEx|U}JM(eX9ip1cpSPkOx>gQ{$#nGp39=Q!&ax@IoYCDOZ2hv;JT%
z&bz-_dT+YK>A^^$GS1}B8>LJ}Z6&nlDL@_2Oj{?{FY)rVTJ?Nef`Y5UxC6Dz(#cK-
zGN*_&d#k|Oo>j%9IG?@KP@&Q)cl6o>olken7%L80dm~oLHH76Zo72O#uG3|zn4{M4PKjZ~HH85s!D8{rfzyA~}1;`x8B=Apr!)(!vW
z9;YxyKb<8$7-~Su8O4Dx2k@xDYy!=@$0YrFoA{fIz0eF1bD-y}pdaKN}ohcXowF~s($Y!;a%G(9&t0qCYQ_B8*@sZ=>O&?D9&|^
z@B0b2<56uZpD2irqj8;4y_g;Fh#Y_u_bD&YI4NC+c(2c>QAc|(aot)F0*m&<(WAgw
z95*{`ib+Wg8AU*oLW82^CKv0eeA$U~&)bugo*t2OIjnoCAA$z1t~W&m8KLFkyaj*?
z3hIPBDAIN9)`-$fA_L$1H!8mT-!r`btsLloD~YUCcvba@M$PMbMm>xj16b;N-o=c%
zei@K17Kt;x|G(vll0-M&x0#3{A{fE@a8Q~Oewbu$h1t_yQxey?9xnpvCPUkuQPDpW
zg`&-!E(e|i>o&9JxB7qhm*#d{Jwh1&uT<6``QkDr
z#^`geP_z$p73k2Ief05_SrY$WWylJCKU3K^LP3UY$MBGyoAMHfg|2SMO#k*j*&bU_i4`Zpx?
zIW6(9zM4#!J+}dbL)nLPA~B(-_^1BF?*G=|KJC
z*RNk>cqIQg*;54jF^cT4=xT}jJbKce;_+b3yub@#3HR7bEU|PBbz)J~NFgL#N%sD=
z_Z%jnLEL$q-P2sxmnpshmzrI0gP{l>p|+Yc3vzJqBMbEeldh6r;%^DcsZlvH{tq%BF5>F#$92wpwc3y1
zk?HbmDf%TecphXjN%x~?raGh~FP0dPMnEL85!A`}F_dRsOUjRw&r@29C;pY!+LYHx
z2V@*0O4p|A=#A#K4~&M~>mMCkTcoIEy3J~}pM3#I2t>jy*Kg-%VY%$Z()T*HUPp6V
zpGb^~V&vsLx)pzT(E-fW!oosUQ#!J^?(jr@~?oVTSYSf=ukhT|RDz5Vr@Xa8h~;ti$$
zmb0YR_6Sco)luDX_W>-DfJhK2!SlgiKtSTl@I=WpO?
zC>`Pj0K-=ybshJeo+_&1C!aKyP77_7+yU%iJ9Qg4(lWWEbmA9bO$NYB-O!X(
z!x&iZUxRLfil|0?zqCCZ(DZhqAOov&u&``igM_~TgkpkbkGHnA!f`orwR!O)^}zi)
zx=;lQUiNxpVtHu7b`gNn+yuuiRyl1Pe8T_v>@Zkqb`P_Hhxoyf`)qrKax4`rVPwJ-
z8i5)OtV#TVlw7$>ik=cvQns>3l@z&Tv{LybwmN$x`SJJLx`Sj!xpZGC4oEzpO@=qa
znt;Db$ji;mji*uP_qsc~92Zz?aLBR3Us!2g7O0iyJ@46w_?Vyp`2LZRUlqUny86O#
zg?E4Qxt|%>*wDH|=I1qBUD-n)pp{)g{~cUHh=7~8e(w>MuG83!*49aIVWZn%{z*VY
zbT2)(n8=SFRhE($qp^Z}tY$|ALI+hshfn)+sLjQIhm%Ie=S|AUAkPL&-ud}C*!Sr$
z^o`wW%OE2nYMFNZM{E-Z3n1uVVPQFv`ltjP;!=a6sZzTE2&BaZOkG`Fy}7vwRRx+v
zs0P4P;NKHJMRJs-JTZkwpehPtscxy1`Mi7?+d=QoMsNuj;(7Bu52{sweT9qf{Tzsm
zgVW@+J2pK%eRXvOoXNldoVKbicrGX+u)~;^ghaS9@QrpBZ4VVtCbzM%F%xV2TV-Hn
zW##4N)fxCt(j#l{&7x`_JHdqoBhQ$FeF4XI-E+agcD|KSE2xT^JlhNhTUU*^lBIr{=5P}1k)`Rr{h)6hj`*AW4B(z$7^!k11(L{no^|ycsJ!X!L?f0
z27ylXn&oU`k+{F;(zUQ?>Oq`J(u9_5=QJt@Ua@fv+}T~`Fcms0fa`a5W_9;8GTJyQ
z<=o{XB3cK6Kq*PdPL&4E#JIS)=x8Qh-qNZ4$*C#fb^>1f@h8YFiRBI8eDB1%r39q!^4Dy7$%ML
zLjZ}*mrb1o>kF{4T>~5?mXeYJZG=$t%%~E!m1)&d1E*Xt6+mBpdJQTlCx}cIimBrXIIcm&ek$~>TKs-IhSyERvU%Rz
zmo`jExtQ3OekM4U(x7Tjd^s(67L@~Qqctsn(eh6vN&@e%t#ig6Q*;~o9fpQ$c`r?4
zURc_F@OHFb^E#?fD56bF$qPJ9E6bxQIyy2ZV7*nfp7np>ShHZI?~;I$L0Havp*;V*
zbJ%2y&E1>Oe*ch7P9h`oj{kF1??m}8x{1NZ^qjIKJ{m?bOEPj&ntNzqLym
z|C+sm!8beedz5_kuBvSK~LDepJwET;_US!LZ-!7{cGYKM8FaIVsT{
zl#AEj`M9UZN_wh?i5CWWPLnWvbwJsV+6tEtqPr+mCUv8F#LfngeQ0E5(FJn(k2~HJ2@mx>jF_{-y6xne^)YqS+JL>Yv7p
z#Wn2I<9cwY&)^na;#HmN@a&Cy4m;#G(Is2HkNF}rwrJ9yS+g{z=0K;N8h9r_{CrxX
z?O4-X`zvLYmUYOJX)dOOmco5Rgk2K6!oCd#&_2T-erhl0YtPfQO(WM$CX%ayJ0QSR
ztoT0OcJ=?NV*k_l0-pxS?8r62rg4>?4TL>3eVXfe5xO(z5CBn}eG8P+*K(DX=}V*U
zY&UB4-R^^>Dmm3}bYNcy{_q47$;}>V0|*=d?oqzHtys?cRHtj3T_x-!>BzJcQm7Uz
zuocHMsG1O4%_Yv3^a)#fN|hdQx141wZ4hhI%Dg__K0)l;^7T7Ej|xdgm}D{{e0y^T
zxdrxTVV~Q&sxL$aTjj%46gNFtKb#r-=(poccZdTY|ilhgrR+ixTCdgsDAf(TjBCkHLF&l
z`R?oA=C(JylAonF={uu3E&WIHYKbi7&C?*GFr<9G-kD`tjQ^*OZRp19fK&ok($z6xT}WWDYgF@?!i(X&
zVJet4lTwC6bU+(TcC&vZS`2N}^}pw7jH1l{umT$|IDbliXN<~n(2RK(`Ev8p?+X!l
zQA!&hu+uEWjxct6^KzYhr@!YXuYd7!^7G}AL73oL1D=!|qF$P*dF|5fY_;{8wHdS%
zLi;ilCpd{79QTU1Y5wy~e>pNTm($kO;`C$8#-v><@-Q`5{-mSc*TM-Xk}JVRhJ#^gLnDuu3`Ww%vi%Yk)T
zD`nM0o)xog!KBFcoBL0Y3{B})^Q9~Me3>l8hhIlG=%Zc_F=omaG({{31=j2{Yg#85
zDa6VSvAqi}dT?kin7HhTu`|Th_3f($j7w#h_iApfpr!-4|LFzjG$^mtD4ir3Ul>%=
z?9ffF{5>glj%xsCkt6jNO@B5O<*U%ax@2E1=a
zgR;iZ_mkaHEEiLmt(%c7C+}Ui8IvikIGj5mdZ-JRxyjB>O&!4=UxLQAGAS9Ad|N##
zinj8i-rvrp7B!c3e73?bL}MO+fiY6>voT`+T^POQvLL{8Ux>MN&~%jee7o&j=vUx{
zkwb&miXQch6-K{f#eTn^Jmbrh`grA4d=a!Guf+m5$eoZ<;2~<*%_{>`xN6#0290PG
zYelmzN))m|3pyt;WBkg)93d8=rcpFGRa>OWY?)_Wf%c((*|ftIBwaKj%x)VQk^ykq)yY
zcb(=Z+OTpSAURlO08!_SzDB3RN`f<|iV?8*dHU$~xIw_M_IMo5uDQi?zK
zlIq`bvAdK^uOADT1KhW^H2$2QIuba#y)^b=L1`nddqWP~l7y8AOJx_e)gOW${>lHb
zfs$S!{wk6tvia2s`!kp}vMJBO@RMG@WHUQ;%KQ%qq&yO(5JF}exw
zSA677M-ixLK14#Q`dFOIUe?CumJBEWmnl~uGjWv*%?8h$xK2%(64}CNgJ)*moEbm$kPNoftM&H1uuV;;%$$H>X9IH%?1~sv!
zW?R!QDoLjI1~R*Xvae!fRgYw20#<(o&;ivRUakR+KYb{u0kq>YzGHF;`2`^s&OMS#
zABPy0xtJG)a$PKQ9d~u-#6o`IY$BSk!RL$8EN;4_wHQ^#D6B>(it);-jwq;EMr4(z
z7O_`Ouu5P4f#;69G$d1&1&nlhaH*`Yxwzv6qH7hat}TrW3uYZ-0NqwO3o8m1ABHFG
z=>8%{E{<01E_RJM&LVYAqo7CiEV*6mRb)x~>bIESu#d6z)z6P%kGS2M8Jo1bsfI5~
z!2@+c^9WafUl#jV#iYN_W~KLYi*C1^BW;P~MU`_*Wt4G!@imOQNUzn!q9*~Q710A5
zbZRW(3Jsq&6z22JNq;6Rv0&DmJ2l^#VD2-`(u&@ZPjenP4y8K{ZJcvU^?F24T&A5m
z7MnH|6|)g!qk9>puYAdIdBYd6!P^{AB3Db=GfnFAv+QA`;GZ3)z6|N{>Uaj7^L5a{
z8qnGpDtEU;#H!n{VHzEV@l6>pCWVaNTT!u|6dHXFY)o0J<$5?O2nGn;EM8PH2p=-`KrLjP%C_r6OrO!awhb_iw-BC(68l*op*fkRmM92Vr
z2^!LQ{2qpfAFkk1)Lo)Oqjra$0Dc~1ulZ{)0rTCIJn&T&6+!QcQ3Ui@B022~NW3CO
z(?YL2zaZ5bMc?gxMpz6?p;Pf3lNFIS*PKrd`D!fS+ecQaB4Ok#qvO?))JVtKP)DcC
zOcfIz5kke%H$rzrD|C--xt^+oNG_uMmASgK6L&W#GD4DABw16e0sP5yFn`lO{M
zRI^4#^;hX0KWcJuJd^C{-X}{~j-m!@>ebgv&)EjfM{5&k1p
z&rYrmh=$p%s{kj@4Q@uq%i6J*nQ>(>5ZSZ~`^-pE%fvherE%0@VKfjm2vDHA;ymFN
zmeWksI+Db&yBAQ7H)0z(B%!bM$FRf+`&nPj$tl~HG^p+?Pg9C9s~vY>Pv@p}q0jnZ
z&ZxZBh2~uj+`0Rh8&^Q_rJlH@92XnX;2V_WagBz_>!Yt&%{oVK#}#Bz)z_u*$0w|$
zN+ZDDCoS$@dQ*psJS3jGsNT4qARp_zl{%c9WUh~PCVG9_xEO#_utN<0bHbXw%pk2u
z%k-6+=;Ve%HHdI{N%PC#1qP~ll%-<3V7Vr{@`zL0u;o$?2;EhsXsgFkLH;+wxUN0p
zmuz&IYkcBG<4-;9`w4`R806X_^h~(kAq-l2sWD8EV3NF4Ie%9b>3J@MR
zvDu1xL3!DQYkQg%gSC26L&4H1fnZ~&pX+0Mqs@DSaH3S=4|-=zCdzJ+)ocbV}nHO7max)Sx^M%lsI#Fuuqz-KN74S8L_LpW(3udIn?O
zUW-X>Hc?WrqChgk{y(N39~rnnH
zNgm%CpUboTJR{(Ff4PkBk&;xD!u2(Xu@MbsLmY4jZ(+W>Uv4lk72*GlX6N3BW5_Is
z(0-*L));EK)v`O@VDnyeu98)iX(WSNIO3~DL8Og&avfgP8!7HEviI{%*kT%P;Mx(<
zE#_fN{H+bs{DZ9g$uB1&)>9~+gZSdYbf}W*??|hv&Y6{GX7V>imxX>L><|%gh__ND
zJSe4V$zmgD2NtU6gej2KVA0u|1J-!Rzpt@;>BFx$wRu<9o(_gw3!Y)PSNvttfHic{
zXF21TogE#ycBz-v$B=gVW5M9sNckf2$4tCvL2qhw#MJgx;$uumu$BJ|d25c}Rxz9M
zrNW*n`HG8BnYWrks+)U
zbG%`LfT|l`;}D1RbHd%LSWcv5#nyTtEXw~ry60T^!ryR+X!Zeu3=7ji0{@@h!-hHE
zG5YX`>$$L*&N5kHgm~hfgw7bdqJ6U(Tn^RCbr__h0=F6E5+GuL@WwOhYZ>Ay7kUKl
z=+)+H0C9wvttW7I
zU+YF?G~2i@_Llg}XxM(IjgE{WTYyKJa$o;RUk7z%?Hy6Rrlh_I$;HBLEr7ve)+I#w
zvl!$IwG+WKSZja8-8KM(0>A`ZxOKM*-oE|;l(h;nK-5k6E-xwt^IaG(v`zU52$4DN
zPU=2ynGoE2{>HENj%F&N;LT5~vZPN$q9uPsVJ0=C+{cm(}
z{(r;x5^Da^tsd~roQD|Ni#{j(_2wAQbVRf`3L6={Ea3lr8h0d@JdT3ZZJ=&E?%uch
zLAoZ9x_MH2SDWYE$$PwNv%_bP<?B;4K>JmIQ9(=V^_?~@yct>LC`0~@pZ6O?TiwES
zT61dkV#FXMSGF5x)>Z9g1I3|}m@@{$5xsn1dC$I(N#j3PRSt)bz7bQgT+8;_7c@q9
z2lU6#X@`TL;h$qv(PAZ#Edgt25Ca+GaA$d6Utf7=9)91(LQFeHx`o%?MAKHGetzn%
zJX>+z#2|DB^Yfr*HD_bL#?WQ~?tcWrxbu!u_}NsHRGOzj7Jetex;EdcY*HXXoSLFG
zbJL8dQn`}oFuL5z;)cjh|2cHz>Ji<<-LW0>#S^jW3?P4l^
zs8lBJ_xHZI%VXIy$did3i(CFPP+aitNn#QH1{`
zE)}CC5p-uAjR++^C}U~Ji@77}+ac>}{qmCbBv4TMARX#nVIxr-e#UX-BX7JZu8n|v
z)K=zxY4JGWa9X*0%9X1d)Y>S0W%Umx1)^(~3A3mbUfxQ*RK9TGe3L+z?0e78
z^bK;8A9-82lZ5EX=8E`d_Q@&;sR@UNKdUU@6LbV<@5e?cx7IZ08ri>_NGXHRba`PA
zoLc_ktqi!&HD^u5>wPX`{X;Y=&?LYpc;xbBzBStJI>YJ+TYi@TM1$lEmk!L#{cew|
zJbh&SheP;DoSPxPItYR4|3~6s^&1EK
z9ci294c{L&2`VO!WcCw&1x2R%pLVzJxp7Fw$#GBjiOSrL?kIASj}S~2vLD*`W;~@Q
z|A-%cXw+q$1mm9aMrmRstw8mpKw>gAdP6WE8;vg`G?rNUu_<>O1=}QAugEI>
z8c>6bbGCzH32r1DcKw}<2730#=&*>8oZ#G2&)}KQHN!9Bx6G3Mn!7|PGlT~tkm=WpV~V9L-%CBt1`>{-daS_i)$7LW3NH358Djzk>x2>V)(ovAEyh=|M0
zq9V5^vFnYalekrONCEQ1sZ;S)EbsID|AsX@{9qL_Q6ON~zx9p^-diQme&07(QTuJ|
zRd`(c5UUsSpQ-t~Xx0bJvU#~F1c7~}n+RD=^WT*dB^*?yB+-v}j)(Zd*35a~!Kd1F
zS=a0KqJ?6t>LrLT;`MfZvyJO~QUIg=jSitOWIFUEox)wH-3vSpfj4!F%JI#3Y?bf2N)~8|-LZirF{wBFk?a
zAqsQ;iRC08WA1dxE*$rqXthgau8+3T0<>l!E>MZo*dc`@>$k?I5>E$Bj@>r807r*I
z?4!5T0**u48!($kdbUg(#$AeuT_q`ZmLyF$A3}{-jcLD?59iB#bYSFo2=2oiUd;j&
z(9=mmi#S^&u_TleN$Mt|)kU7`Mg4b@x8!n}+!~>$XAo*J>TxV^4s^NO_rRVzh#6H`
zWNHuaf#SLHe0EbwML3m;y`4Bv?BG+w%cJvS`c2}D@%6t}g|39j!tHHGh~uYWI-YtQ
ziIHM@R|L6;{e~A!L&~=Cv|eUPg-6h!7=W825;XBmFc&w+0#`eKN8D895PS&E6w7YMOq|X-jb!b_+g{c|3V|{yURAwH(UYFV-JQoQy!GwG906U4Xp!VUde4^C(8`6I
zvQ4Anmfw6jB*RLttk3oraNj(66W~15Ib)ywt4wL-d`R+DOM972WuJgaG
z6oKIhS1*C3Ad+9WBu{*3_3{TqRe5P*+-RlG+mO7!v4-2+4_{?u{D|oLan<_ieloV}
zaU$Sqpe^M;#snrz5DV^Y%}wi_^cpmzG_il&YBRm`070Faa0e#mACb*T&I-6gML==o4pWXF&f7F_-$x_2C=^1w{xYfdBRwDl03C
zm@s~xoSfWQ6xC_GUyZC+w{h{H&Wx139Luy3Xjs_xpkp5$cOh*z_w~8o@_n9dvb5|6
zkb7xA!=(TAneoN|i()Qx2O$GXg-8Tf1u*jQ;XzPep`xyQd%jD3zqadB6rtHeXN6nV
zey$O+w;5RD{RUy_s@HegyCl=1pWuyvr(&aG#r_Je{WD?w(|wNC${+_RxP~JOVpVt(s%UT04ZindV!!lz^A6I%FdI
zBhEiY!ocj9=wX{2)kqb|rtZX3R|738klWLeI@(^jW1U*78Lx+*!OP!fRa#ygH@AoE
z;ZifekE6`egsIITzK|LFT!Z~3#Hj7x%mBR7xDON1j`
zKyk^ln<}lYb;DaUc7w`Nz;!js_T}7@V}J8q?`mq|s&8ho??Y?2mY)|FjqsNO-f9_k
zpTY9TRXJ%-NY39j(!`tShEqPxSe(ts_^`y)-*^nI5)5rMeya_+BSh%7L(lIf`tfHz
z(J56=h4ckgF8)^;I;v`0UYpoldlTb)lyo>V-p{F@ssqHup&Cbb<{H!Eu4pW#?&+z$
z`3UGMdhxi}21$4Ki-=q~_&IZLZTBVS$+E*0xbtSPXe0Y5V;Y0njXW3mjXa%ITYb+8
zFUcq72Ow7~zYvcd+Vm^EAp`P_U!R}poUw`rLPgiCk?USXI-)asH
zKZvDRS)+GvitI0Bc>;>;9(Of|ORREmW>vS5P@{BoR)O0Zp|o!9IA_VKz59_*&@DZy
zLAlwS7{Cb!Dg$h0H!0btrN#dq(_#kYdOI}`Ny^`Hh6;ay=6zundatTdTgC-m(pE0+
zcQc_Piewd4X~*fSD?elIuXQ)Bo>7#)*8cKdlDF{T*KWOy(Of>*o($*ihJi0W@J$KmR`-yevBg+^OV
z)&rBgJ#Ve4SYy;i_LlL_8|sAYOdXBaQ}1`Y;3^~F=6B$SBu1lPi
z$Q#HZ(!1J3nc#+*#%-d3zuLJ~>I=Uw0?_TjTVungjig%zI6JYu^b0qius%Cm0(HL`
zCb%WP(>DUVmVZ?z16kP@^}#?a@G){x?*t}1Ay2)`n}+o?!#(@%#4%2aL8M9%b3MK_
zWrzpNg*NzCb+flB`b>9(uj}=TaKq&ZDEuV%Wng5T
zKoZ8Cl}_gS5zvcrlmIj{k5UNM8TEd+2?{5o`sjnW^4T-9xScNC$$N8Y#;Y(mI9P8C
zrKFWlf8Ws0)Y!Pa;9%t-psG)7wP1~4nO+XG>~nT|^&CvfSbKjcBxt>P>vNcVsuVxD
zA7z>F;880Y{mVB?x8>@c?MB$TEodK-x>ZeU|M+dHgnI@0v-U6#A|Wg_k`%cD3wWik
zmQB=sD7NOGvSgYy(b%GnTNjj>l%QhQvvwRr
zF7t~3CPX%Urb~M|06^$=UaA4AsvHFFE_1k!O*bKP6Rml?;xq{d4jA_IZXvsYY(S}W
zMT{P?*qf&2G{XdDj+Te-uDb{dd7Ag(M8Ig*|Vk485f
zIu3<+dk3Wy@`|6&sz&sv^fC&ME}k}{#K}5n*QI}2b?*|uIsy4D=yeB#H4m0Pzu$Ji
z*j;E`@|iUokIhdHY@s|6YO*!qkalf^7Kf)4YA@E1-O@XG2VUTHY@1?&K4W(npPb8f
z{WMtP80iMCKmW0?>}0PCHv}BJv^4BMP!g7Qap%>q?F7@Z-XRaw1QB-0}-<
zsZk>Yp?RSTGe)3-Jl8<{E7S4eAD~XC7X*~|0YJU@-p$6DmQo>mkOdS#t0_;zVZHWY
zv|0R!V0g!|iW=yk>f%VH9lH=dB9QR)j>v_*;Dpxp&6BB_{#q9|Et;%%N2HLPbK+Rh
z$=X-l&T8D5#pcz0b>C+bLqnR$ZvmhXww2|isSh;PI=Wa3B|PR7+8n+wvcM(=3*Vm9
zIi6qPsjjelTKyU%Wo3!qFuN+Cpa+CG){R(ijLhc)5zxfmc6KqWa(j5YlY-32e&Y#h
zJdcM()EC7qR^~XjZ-3}z@HsE>ouW^FnFz9%*wi_F``W&(gl`yle*phgUS6IwpMQUN
z5_=xn>rDXd!uY|mv;fub@kI%=Pe#i2{(qFb+g)EV)9>Wr5HVraNf^m^QHD|mtc;20
z(8{Wtp0{L~AmlYsh&Crt(kQNn6)btJ=R3%~b&?kLj=Q2qLk%3S!{RwsQ}Y-5WRpPQVzB
zWH`O^b7syj(t@)LnW6|vp}Pq2(P5Qstx49J$+C^1rF7G7EXh|oH*~*B$qm+!IO=zY
z2LKAj5_;yN_ZNwUs|rVF4Tk~Pi7~uvuv60PO!mD_ZDAK~;;aycq`7lY<1o&Kv+)k*
zI*zz_+N21tk6gBv#DI&f3d!$2(fprph|zSoBWamJS!1hrY{VsFXH?r33>YoszlYOv
z_q}r_T?qDCR)eTntnv{vt6Gj9vSOxLR5cB83^N}|unfyH3x-F(4alCyTdg2T&Vfrg
z0CNDya;U6`$;h&pmqL8-JO_~g^+KO*Xqaonui~D$V9GmZ9}^oB*J@I1I>g&Xt=cFi
z7+$fRPlV{jJ3!J>tsN5^`zh~=h#HzmG!!4udclK#^bJSLPuz(k1dyIB2}&=FGTv$r6_SL)GTPA|-=l
zep7=e)#RCcESlX*PC&X2|$^W{tjE9M+SQg_@
z>+w8oYIoA|(Ri4_KawBSlY$vPcM8xsVz2Y1u-OpLu3zET_
zr}HFbA9s#zDuOt@+hjbgf`f?>B4~DBUUwK%Sr)yANy+dX-Sm*P?dPE8D~=}#o*zib
zt@GO$*rnmi7&WCi(=r5=A(cuE&De;;0y3iSVok;iIf7zke6pF`R?*FV7h>&4=9K|*OrAs
zrIK$ISVYbqrE-E%zl5|*p)Y-@ppBqhc>c4Z8Z{ZuMtNlyu{YxXpzSTg;_9|8QDTr_
z2_8JSyL)hVmxAC9!5xwi+})kR-5mnK-5r7yTDW^}-kkIG>94!*bMKG4e((TQwX16H
zwbvYTj4|gbKK&W{=x~`V<&W3xU*>5uyAwDWeUT?+X|_
z_>YRGoV0+^2s2uT&V#UTQ8U${Gu9=DU>{FO6P$|qTpDB>vDm=^A7owpp{0|P_f#MLo72%Ac3BW5!l
za#t@~RB|3{HSwJyv@`D!P5#JbJ^)M|b>wA$xTCBaube@(g8lr3zK=JTbLK-MH@dk<
zs|$xF2*kA4+P?A}c|n&gw|x3DSI2<&WgxB2hI|2R`{EDEcbsP#?|5!v085x)
zUg#@;s|M13?W@~!ai1>C^$5A?GdOecvNNJ&hs;!2FB|;+t%6b00lw>^k?kh0n~TgH
zsgHDHxLz1eI5Z@@PIULO59;Tfb9XK2kMXuerkEC;$0<37(
ziGV$;^h?d-CWfdi>$Km?M$P!MWIgP6M?0BnLb4{9ccqX?&G^ieFK*<@{CvtE_}_#w
zM6Jv?&yWwUNewQES-i=Ot#>J#b0Pvy&orVw-m{8HZpfRO#}=x$(;IHFANthV_-!pC
z41?MmmsBU0XR`71w^-scO8~;m1wVx>pDQ}EKNK1c4tnaF4QG5;yi$h|Iba18EmXnE
zrq-Ak7Y&V(PBc+Q>h5398GMme4c)~Uf!zb(W4e(F8%EU$f|i5@{>+}$s=ag=(8)~<{&L8J7jIjA
zL=zC1V=}hq^lT%$v#vYk2O8pl*JJrAytaIf?SgNzV&{GrY@zv?>nuC=GbiVv&T}Vv
z`dCBOQZRr6{;GBfOVU<(&;IPFjFLG~MhcQD8{DUi8=gV8_+q{Sf6$Eeqj_Yg{Gb3Y
zt1?rm37Nci!|d%{Hl>hL`Wa}%uizRJ+a;|h)*>QD14sKvz*IfSwK(*)QJ}`bZ;rT?
zvhE`xkKTle(+pr36}2<^$Ff4i4f}yGQt-Kg6vp9C8Oz7hf)xFQqT+>*CZhpXZgB$%QS$vLXQJrM7fJ
zLuu3=)cGaNgc?NE#HtB{jqcC^g
zV06R4H2OfN!XJUP!V;wb)bNv4PVo1%tjjyeA6C9x(S1}IHkdau9ENLxDOC-L7F677
zT5CZj=1k0!={Rn3NzkUy<||E|B?P)f@IMLcv*njAXb|g|WOKE~%6H<5F~LR+<5pCX
zrlbVLrgrwEX
zrL?V@$34VTW3M<`Y6$
z>A;Mc-k+PeOJ3iaw?Oq>Qep6J8OP?wfxy5%>bAU|A9aV8uq-Y`HHV=YX27zdqb!B}vSm-T5S8ZB#x>G^>sYpMfr>(*3Xgr9siO1A7!
zuFxWn11w75kq=
z86OT;T2m4e3nq7o1;{?U9U2+oznD@UzbLrN=l-XBd7Q1=pIQuAOm{f#J&XDDx9wSr
zSf50rSH#d!!2Ckpue>s{J~PB#CrE#uC#u<#&hzjg&5VX|;I?Sjq=@B4PuNd=$qCk>
zv^1rVILoqDm(*|r*~V4u{^O|kz1q>YX58TBgE9_QBgsSmmX#+wpj0BouN!_Hu(N;3
z%jC#v9{ECkF^jy_^&F$g2~mh)G=wZ?N`lr)-eyb!Nbz~jYVd#f6jD~$2-o+eQRrLf
zaxjdh)u+#}Fw*pSFPNwP)?EjS$
zMr*=T?(uw5ex6XRTsJK(O;!X5OF(Jd7Y_bAX!RK$9&l3r8J|n&$`Px85@x+tcc9q1
zxQnQ=7hq(;1V{ERXRU@2H
z@t(#51fA288$i6RZGYHcI6OavD>&adpxr`N2yGtuv{=UU1z=dRImGh^BvhK8516P0
z#7m(1cjTYJ|2Bxv#NFM!u=nn|8IT<&0SRK|QKAk)F_OY`Z~+Ja#oUMOvrle9b++pn
zdfP6ow+}vTC~KOICxmPx(QF09sv>A)*50dRwx>M;K6*QcgbYPaq&M=spBF#}qDm74
z6tWF%o;PT
z-?Fx7Or~FmbS$gM4U^?%9sqggqqAmm88hOfnnos`qzjVuAZt;LCWRP`?@Q~disXTn
zKL{tB!M<{cwIIB2f^3eI5%1jb!Ggb}oO7&5ZOk1NCh*(&epj#=q(#?P&MFq!y>qge
zZe4oeWCJ-7Oek@CrnbIjH>BaarQPdZ&JLCfomfyGwQNa{){9=qYjdM!g)}~pWQH=+
z=z4{zD`dKrcczcD@uAmuf(SiXe7Td)+U;SU1U#+?7pIc8BObdP^RkUSrmV{iLKn#q
zPYKuRQr=A+Vimm!a^+4AO#Kpg6t3kXh=!nuG!K;sHGXI4L~W=R4;QqLv|Jd;0$b%f_q0gFhj0iRT}6>7uzsI#Hm%gBawD61P^&UNnPddwS&UESYarw>AyKi
z?TtvozE9u2;XVFoPW?%nLc7_Sj;G4`KnwDPr1fUy3Xv{_SqJe-)8%R?4vrn^K+a}0
zIjb;2j+Xw>#_q%=B;-jVU|IrV0o}fof9&PuVLJl5gbxS~-S^&2HS_cEKZSz`wXB~e
z#m}Z)P(TkIk0a6<{YZy=?75Q8>ch?2C4Wl}}b^Gi2#g!T5^Ku#e9NwpOX;Zxqp2PYJE-mgY7OC0K4{4s=
z^h7{=&|K{AVqa+DaeC8R&XW4pa%(z}(#cOlNnv1ktEyh)-!9LgfeXYRrm_3j=Zbh@
zk9Gv2Q3CPWV)(Yx{B8Sa&$!pBqj2ow{BQccMTkoIHJsx&Fz;8hm`V>8
z^<0DvN%uRmcv*lzDQZV9`XG04*Zyeb?a)z(gdY5^jBkud(aVr%oFH>e$;mfPv7Lh4
zIP>P~)JJ@HY#)zL`HwbdVX;apI)4h>HI2_DKH>I9NF1pcPmS6UBrDv_jUUCXV!0@w
zOLw(&Qyv#$D68vrB`RCel~U|5Kh57m4!(IS%GX3Wi3VhU
z(?=zdCzkwsld8fmU^1swdTJC+0JrL|1LtP@itn%)Zd=&GgL#NpU0#=3SMu%G2l+Hi
z=2m;qyQB7}$EPM6ukN&vC+~2bcBN{fyTy!q1KhilJ-Ee)c>(U>a?|#*ZGoF1r^7U#
zn``7|8QIvnH!U88u8P4<ty;l%J!<}(crJlynl2n~M<){_@;
zu@6{H5_n9yZ?JB^hH~QHg`qbs-(2rp8Ul!5#7rj)|G{WAs9XMeue}f_|IpLZkp~I5
zc?{H@%4g0pXwxHMf7ZeBS4~!qmJi_99U6s2m9X`(aRXvjMAaQL(8Ox#S5Wxprq3dE
zZI0d!e@f{2$BcAO(OI-tKRJ=Xi>S!B+T>m~!@mo6xOCZcs_v&3Z7rM-5!rL@D@}6G
zvHPS!rIV0t#~u)5Nj*Pb|@QpuQ=i?0Ns{C83;JCsT%^puaM
zCg%4Pm0>63Y(88KjxME(<73{A=-Ho9d}?ruRHi~dq9|2rg3`y~u()xfsTA)1tuZc7
zih!G)57Hd)rgsie@8Z8j^2A0MO<#e5TV~rUq5(bNgO_ASnN>0K`NiKAhlGC8A7N13yzf>KxZNC=oRzVAT(Wd1DS
zO%W6NOpZA1-sx}Gm1CDR_^mE<;|$#*BV^Yp%-AvUaP*)D(Vua1<5d)Xj-T>d+RMK1
zryBYw)Vf!BLQ=REbl7GU8$5McZ0g!#s3soYNz?X9^^qD596WzOLg{2tt;0-m?UK-2
zzCF+_9EdMt(re0LyWHS`8AhX%v}9UZYo*T&lWRg{VogGBLg+Gbe*UJ!UVD4ZPB>$y^8GIW`k
zn`DgmJV=+4XgYjwMl9M@8n%6E2#DZX3uQXA
z)IE}iRMww)sKW5@>Ti1QpHJES~!7${Lq_TIPX%o)a5&yyN;v$_{Dx|?aTJ76_2BbQMrAZ
znfBHr&f#{$a(Xy+0Upts?#i}3Oscn&$q$oU*>t!Er=twku{J58rCi*1#ossbTq?yS
z28nD((_^&1Nb{Ipc{!(=Ni}p;RfkOoh(9k(!?wD&P?F}<67!}0hpoiqs^z4In5voR
z!_CXm8>0==l~$#WUILfJrp&3#(Y0=rD-d
zL4L2D20wG{p>zT11?j}>qmb5_*7_Svbt-CzdCXWALHAFJypYyV_L>t`5N7Nb(b6Zf
za!J-O-n>B}r#-=3Sftx2rO_D|X2|fH1t+weK8w@ws>-QRL!Dnmv!A*1&LgL8^-f=^
z4rWyl@Kq;@2`3|hQ}o5BZ`3gc(_^=*RE?pV1!X4k@nGJB=`xb|Uh$hmNg!@dC&fZE
zR0k$Et&6$!`M0G9Fc;Y-Ar>^`yiB_EQg30tWEXz35_G;g1UYXWE;kSS5_9*(F&gr`
ztbYd|hBScotqwWdZ>RGbLw4x{4%!IRoQSxp{S;>$OONgYosmGOG9yPrcW%F*ybtaw
zAG%@vhw=CfqyqxNC!I6)1A-=c33<=P(iHloCUB$Iez?MY6@OJxPE6Pwj)_xqI1={$
zYRX)No$bZ1{<%xy{I7s9VJh%}6WNo+47o>$bd0eKR
zqaloxV7|wR4;dM;7;u_@Ixp@Nqy8U%&m@A`OJl>B(
zqWScEtv5O`rbZ?U7Z%62a5XPgK1ENksv-1m;WCxTPtmWTPkOP-hJBS+NsG=?S)eB6
zH2vbBi+E#&%w@A5EQPj;Y&4*vN?t-!1UO*qg90F!OmJx
zpV6(ZAy?*i8p{02X+jv{Y@PN=v<){~`Y~MxEEQ#@Q55ceSSrGBcZ;K(1;p8rw`Sw%z0UEN)Bh1I6)tGRAz;pk##apxMbhXJO<;XD+0R755@vZv%!?C-?p_Z
zBYQhWU3Gqtq8pVZ-PTt*Qo}{Dx?U}|J0A)fU$B8Jubw$nDgC
zo{`U;I=$NkU*)p=3UbNMycw7RTjvWivo
zRx&Lb({b?4pZox6gFac{62@vQso`u8E8n$+i*lCoN?eTP-AG2%Y#;``7>RzDketi3fAU3~bb0cx`c#o<
z`Qg5pxLm(T(*~16$oR?VRH#3DTTMdnVy#^SH;;_-g)iG;NX;xyxg6SJg&hO_Vp$p@
zn*hzS$!de&NGnS&(uhg%4C>0wb&O%tGOXD{Pr{Cl^BuIUTYl3E*5t`(=n5TIZ^06z
zYi+#Le^6~Y8nw>?7O3TUzBX9CQ0A9S9B}PjO6MpGPszR=(}#t^Qf29+Zg0^$i6
z>59PmmIslKPe27iQO>ktt4(-88Pyj*5syz;Bl+TULefrSS{x@*sORDE(yjVB^r_9_!MS;l&D9P^q=mbOl5~BSKf!{`-yN!kDnQC0tV6suF*FJfFGs
znHrsRiB^5X#O4XnglK%0TJ>~nyrHq>fr1*6aGJ@#y#T%za*1!og?d&oAOZ4HyQW@G
z<~j3LFJL-ezpPU2f3)Z5E;mE-FQlsClo`%Y;Lx(4yURTq=i$fQR1^DoFGg&`-9-J>A+59;
zf_^eMz)nIyL;sMP9~7wnkmFcUNUR(;^kPKVO;K-kU?u^xo7ag7Ta#$qyY1@t7dQwv
z>?!_(+~McE>?vgi@`lT^R*RFuPVh%j@>7&LhI9b4t8Krow7$@ocXg4VdQ)ro#5lZ6
zo-Oo4x-RXw78T{-tz6_T)~QhHu$>{Km6w$$c;=!pFtDJ6rnV>kEoP7fR))n-lawDt
zo!e4t6v8&Djqd!tKQIh6lpD~i1L;`d?c`&LZz8o9!WCGA2WB`5mf@6%jiQ}ci>b+F
zq#~@%He?x`-<;`B$QfBK|5i6<-P@nQtfL^Vtw^{~&vhrU(mdwa$3OIad@?wN+
zgI`IvQuSOHer0Mx#aKNLe)nLZ20l*0VMUWn(V~7=+i72dSeWtk#&hP}HY=J7r%%%F
z07#79Qr&^~`hB8dV4MKqZLP}|+-y$0=Dy?Phk#+}-vLPo5m+Le)W6kxnhzJ`2wYYE;6l)|;BY~@tc8TGYe*BqI&(zTQ
z>g~|ok0|4H-&~Fwp=%MEW^7L<`c47^7q&+{a@_}~>@U>~h5h>tnrvuh&f2-5GASKh%~Hk0Oaih^-}N*c3Q81>#va#^3Uie~Ct8k9``HA&B4Vuxa)p^p`_?!w
zjk)Uf0woO@2P=>BG`yYZr(&x#a{$FpshZQ~?9S`QI24oYiSQcBEZqgTLE?p$&`MW)
zC%#$ZFlK4@Nis#?rB%Sz#=HRUnha`C?k!{#QPZnQz}aIuKVn7~C&SHb=d$gW~cut>Je&2Jo)V_Af7$2=Kzv1%WA{>5_YwQ{8
zZzW9AP(kIWd#R#iR4<)YVke~~NaYq#>*@K0sU^-@JX41uO7F`<+Elnvm~!o3fBBtwG~!o)IE`QzzE2Fl-Hl>RsUYDf
zN`jW15_k(svIG|DJ8-^+?OFmGRMe)1`k!UHu_{Z{WE_>chXI7eBl$M(=cm!FjwdG)
zRa8+fcp&(OdeP~GqJpPZt6!M1e-=R}V0HTg^JzjZm&0F<3waNHsFKN-i2?>
z0X9oXdU6QzNbdcCT$vts86ty6D?#{y;fMYZwww%uAN#=;wiI@oM_3Y_J>)xlky-Gsfz7@*p2R*NgX`BSF
z8%P2v)~U~DK0sUfrYetKEEEg5mOM@Je}bG{%<2gM`VgSN6h6gwcXtDR*w*7tgfKq4
z;r1RRagiqm9Lr!Y^$gsCW}sWtFDFr?)oBRB{i$s-!w?kV0M*+6%%Bex=+G}qW*#7Q
zkRFXWJJ^nnsuSH!|4bNS0}MiLU5in|W;(8oq7APQ#pJ~%XH!B)er#NN_iK(RHJp?2
zf>k#M1wulJSxZ!!W$EKFwDXHL<~^72i+DsHqPN)XHuJP4=#BOUqu7u319r!Fd{3Hbbw;2Wo|V`E1;|KqHVh|oaN|C
z7VuJWjm~KIKLSiddtyL{Ie89W-DX_fif(mv<7XbM4z0$x9GQYpUT8W%5$h!WqdXn|
zWT8rVQyezE+0YxcXwRMo+!J2!8#p3bZ?|1v!tk!BsejI!f-j|iD(~J3w@Y?U{pSsy
ze}ri!foS7~X;PNDPoF!0o^)fSnE$JW0{Ez!$?$y|%S?35%PTUz|MjBxb5{h;&%2ZS
z9R%dBpIssUjAIdDKt21{kZ1k>{6Ni-02H|+#>UgLMT7S7X4I#y>Ce1rhEt=?^78Wg
zD)oOI`FQj5XV~|X*Voszma~91la5?1Hz$
zba2ZS8K>tukKSOpDH%cGrm}bq8uTs@CA`P}mDnapAw~n0im8TU#t?Ss{^{mQVRWyG
zq}D1^4eTa!uF4FTTV|oc42Q@^Mat?m#Vg0JyKD{ikaE_Xs>|}C13qd(8{c{a}ygj8Ssva6NeP=m?20$DTjzW-^GkEO+@{=8
zY->I1BD4OZinA5xzf3glN}VcyH810#FH^qoiYwUtTyIUb8iP3dt1kMaQpdLJE;nmT
zws$_x*PYQV4!G2X&w>D+&yIShh%RbJjC!CHT_Tjt_Zb?uhx^QAR!XGTVurgT_WMPi
z)Wg~D9kH)UPyzf%cu|6?Sj6k9sQbXtyl~$D@+p4ja~{ZXC;n7K1wlqKN_V8TMQ+Gx
zvS8w26<(`Q>e=rpj|7!Ko>#y9D`T_2DI=?u1a#V;v*M549|FrYO0XMi0tPBb(Yt}P
zx6to(FITU7TzwTQ@4@)*@OZvTPoH5A17LAB;8z;Wr2HA$&NoKX?sYT#0&w_~@l#8O
z#Z6aOHuxIw3n@lysQ_${yE0jfOPx|%*3(Am5wKa2s|qVdq!(36TAH!AiK%IvFU>gL
z^SsrtrcTQF9{wUDtl+~o{ejX(No)eKg$zmFofYlzK!D~avZ?}_*`p~Fjgd#4()@13
zs08m7DUC44-%055D8okv{7Yl|4nPn?Z#CoR>3Qb4Eix4V#JeqfU`iEAllXr7rtiXNo!kxzaJ`Yn=dA(%UBR%xRR>pAvGOjsM`fk?{2GRGHSMYCa2XN%+t+
zXQgRu13}Ccq*aqU0I9?iXwgIB1@xvaPaCMc6ER7?;cnCRdrbOnO6HDr-jpMe;0Q97
zm(w5N_s2;vo!ks~kER8FV+;jQEp%56N^N};w-$WowJ8x5Xyvu*sS$)v=b;3ksWs1v
zy@%527~=`ApMgt{=ox%InxDE-46X|b>b1k-A|-pSr-JtMIljWN%^Mg`9Vs3$Q;r%J
z=1lXJ!os*BmXu)|)!VM71Fa;1Q5pmyc}xEE;=O!Dv7>$1z0Y0Wx({9aau&;l-gD#a
zqJf>LjxEnIQ9y#ABwYHUbR(_jSn`!h4E>C9GRk7;6HCdO{pGeJ%3M)my}@6?_j0f5
zbQsTCt@`z1oi0C*IyrF`uPh*nL4>YG7-bXY`_M-&l`X_%-aCFwG5j~%YaFT`yFWRo
z7m^InypI;Z6e8)#G`iVMWZTcGz~hUD^E=CSt*S5LbCF0!G)~mdm*1(G4Vgfwfl7is
zeXnIp_kutuO^WgPE}EA{nlinO-tmS`9`xe)r2~tbbL0EN!^`ZJLj9QFis|)Xws2H{
z-!gG}W8`x)s0XZQ&nuj*rNQj|MY)gH+=Jir26EcGTxrI5lqh$VggAc|*<)N~Ix_u4
zl<4ir3EZT>vvMzXlUJh~y`Sxguc-OP@@<2EQx7V~RzAW*Dr6
zapfnCze@~|Y`XCYky9<3A*uxi&e8eL{Ro^g8o)=V^QF*U?T_WaGMkkR%`WoHRiwQz
zjJXIcev1*7X9%!UN_pQfCcdZEy9DI)0C<@oB!*VNX~Kwd8Ih-mWWEv_G@nYvRa875
z)1z9Jp1@-%8}RTSQS-QH;hW7#JcY7o%Q?X=LV}00&df!8OiTv
zP~HRA(dGQ|gJQAmx9A)UvX?YSXL~AD0X$T+xMO6_AjOB^?qs~Sa~X?dRb60T8=mTy
z9&a@&o=LpGqj~+nuA5Ybcv|YV3oz@gR`Bi_%4v%
zGc&`u4zb2DS;x*F06lb`(?wjf<0R8`rW&h(uS~qmc_VF4k9S8;<@+?N_QuYT#n-@>
zLx@xKu~!CZYp99EOm^M5ROspO@US}c0{ZWP3iIF(&1tqii`W(0&@723drS&7u?L;I
zrA)`wKRS0_DVqPmTp4QM%)d^t6(RT$^foGs+Vj82D@syM`bJ5djfp&?Fi8tp>fd~d
z45KOM3d!Hot_GdwVJXUSS*3EC`x;qnjNO&bbiY4=c
z)5it0RwW-m#lCejb2v?|`73wyht(x!YBw5uPFovyW2Vo*vy^#n)=2wt$eP&vNWuwA
z+IZUN#-um1iC_$X$du->Ca~Bk4S*#1@dx7>j*)%kQ^3|`YK??~-V#W6-KFqz-OTWG
zqpEnZa6PZGVlR3H5T+s#wkJufvcSm(H8$&UMH)@G>%-lFR(cYJTD&MxI&`{PJq6}V
zGfnd?NS95Hs+*A7?CtXBz~g>y5sfgEKj}}vt;N$R?Rek80@{6i`{W|0RcH=*VqX8FuXiLk~ww(zfb
z$+nexWhxVDb$j5Y`RugOzy0ym%giM;!LYVpIqemzBPh~q$2#gzqmRJ;nABx+VGN5e
zIn;_8J1;TQ@wD^vHkIs_n)Tr0v%b^%G~dWKFbLR(u;+U
zdAQ~T)ld3NjqmyV-rEHvO<
z=ZbsxhR0>WLZW^48OH`TnveG&Q2=F3=G6<
zOD3}L0s9rnJb3>RqP&uBBym1!77W&lSFWJ*HNB+du=ZjpVAUuGns}?-98Y}(nn(k`
z&OnFQ+3vGjEo`PKut@eNjkJC0LjrdM_7g=eGn7TNhX(FfvqmEnx6Lo6R((X)Ddt`S
zyDD@nJp`f#%Kj-b#gqqo8bSzl0SGl8$V)fU>XSHIB1J>ml#RZ!8}C3vW%ecIl~gn3
zo(f|usST4{+${2BmPTSw#`Jezimk^&fqpar%#D~{=h
z_b$SkFyG=X9!zZ1lq#I|XZW7(&qU6LIaWRI4(g#_q(dZEloo+j*r3r?XyZV4MAnV=
z2D-aT$W~uGhcJ3Oo4iqiH^GzVujYI|vs0-bLWZ@n+aAahgK{1Dh6g
z)xvEbz@MJ5^g^x_%rXO9NCTbUgVggQ#yi#ZK7^fxH(PzPU=nBhL9<4_BP=Kn
zdSuZj?05Phi2UXk$op6Jl2DxWqdqQz>S3hnXyJmUsVz;8fMh}1`@lc3niLgOACncV
zeq`9S>PSK=Xa^vxB9bM5$xv_7@Or%=0n*v2X>UxG9-S#>8SvHD-)rk}`^21m
zRV)lJ7~bjCNC>X|%FGsz;CQoWGQkfwe><^W7upUw{8@(URP_&Wv4@~pgyQm*oNbxl
z7|)C*?Ai*DGOT33PK1yVpXNzfka@c(3|RuKMYh(@6@&j9Z6Ujf9K1flxxE=%uXG?*
zn$GFD8^l#6&Cf(c?*KOn1^@(}qMY~Z{`c@re`yc_okHxJ-R{@3qV`X8y#oLyM&<-3
zKxN~K>N>ypQ2Ap)kRc>Y@|k?&7ISDF80ZwJI?b#<)O@Z16i~(%5)z`eU5Ed4RNU*W
zu(8F^ejoi%@tl?O_&7VOz3bBjSPjUfSdzQio$lpQe0)4CbAx!7%EY)ZmtYdGS6_@I
zoV;P@pXW;||K9mA>+UvS1#>wL`1e!y?tZc+NUL@f6=mG>^3VRO4G|HdnXS%zIG*?I
zc}GD%z=lK+=q44MK+fRo9uq&mE-mKM{kDKM=onf(+N4uFKM?KaV^g*mG*u7F2y+DWb#G6?|S!PiYPYW^X
zT@dyKk1aAzT@jF(MU3@>+YGT#Qz_0S|h&xsbQ62aTeYNB-eec@istfl;XcU#OHn-j(f3vai(hVC$)4`ZThb+=w*>
zahI0^ddr7A^cwMw_`N`K<+|Q(9NpVCC~V2N3$3QF8AAH!Cbf0l!gfN+x6F%MZ}+t8
z=@qVaGm?x=F##wfz%DJYS!>+a)ZCo#wjD<;iEKgIr`>v&X1FxpH$+M1@(BcPUlgl}
z;><2L4f+6Ba64iK@lm@YZ>WGE1g++@ab^VdPX5ULSW<0@oY8LxfGL>0F%|6cFy>}Z
zusjqnetjity2>?(yY`3$kyFI**}=CJqhpaghuYp=fl*yaN0;`j-}-bb!bDQXa01pg
zEd9V@GWEdhG4od^cjYrz^LTT?u7tX*F(5;C|ctD
z<6;YpZ0)LIYt*|q}m1B#|m>chwS$bIysQG?Ot;oO$zN#TqO@O9LK
zx8BeUUa~uR3q95OKZXScdh^O>(D;H!2PZ58B|xEEP5w2wOy;vHkD$cBomE!jzJ`dM
zKZ{qp7{CeT{lLE37sGZ|)I-|P8-9V#8^Az!OYqf}z(XyD5~!NHuq=X|5B|G9ve`$A
zNVXYt;8h(YYi%>1EG>e_o-fMt=*TH6B#WnM3jy1gPuTNe0fghpJ&nE(Se5a;=j5T{`-NBm~6mWw(b|Oi~u}K!|CtS
zKfeN8aAy%}Md9kqa553uh!20cHTec0%VrYLN*-&{Lx;2F<_cG~FB3}L;M~4RpXj%?
zpUm+lGY8UJfW3*yFQYVmB?p>q9U7@`FR1GjPzS;
zoPecL8|P$RNAO}I=q+GAkhb7sCJN3@d~cbtgPiOrEIzVzk1cq$P`tq&Q>|*3wT1H_
z>=)WzBNVfuB
z8^P`%Uv!f1LA=i%2>K0QN;1=_Q6X2GZ~Q3GezptAf0pf-X;=RRzADMk=A
zeqesQ{4DpdyoE~zZfWdLyr=?*i_h%QgeUWxuv{a;XcmM#w`ukrYsghsQ+WT4_d*ne
zw;MrIJ_1P*{c_7b4WO1MAoHvwfFB&6bo_kQOhYLrLb)}rqXuXqH*3-NOf;k#D@EQT
z`FzRG9>iw_+L)_^6D7zQ+gDv!9ym#gL*jcM(Zhhr#Zr;P*Am;bHr``h`%HhS5*;Fb
zxI2$mLjRgr664$KN0~j{`)sCnV)YFvrzOe
zl3XJ)!M9Fm1GyoajYPF3k=+QSz1BC3CtYNUBaOrs+&Q^l!oJ=qJ64wn%#81Sa-oz5
z;q|)Z0pfUJyrQKlDuv|LeEjUY09!_x@ep7W=A|s9fnwn74jOXl?*7Ag{hI-^-6Qp=
z`G*3NirM(<_oUiz2>3rxU~xHJ2AzfG-+vB8J?a<<;c_{;v+3=nb3H6g|c08JaQ@B{U>
zFNzoiKuk9ks61Mixsk}5oo?n)`+7#3uN}pWfLW)nB{9z%1GRo4?KVQy1TPEgdX3p-6AH
zNHL6wo9KXp`NNzd+&qWgwn)Via{{%dwudu(gHt{>o8W=Si8{C|0lD#6*rdum6Y1hv
z7CBYH$}vk|O_O!g5=viD1&v$z&3V2;2?kMrK#AL~ni?3zX8y0SmBuX6BgJuu)pRMh+9#y;m_MDOr*13IlL>4bPaP66?u8{YJ>#{?qW33e7
zwPjazQf!TGG+AIeyuSVIt`*<^tBNs%whIHjm<3&yiG*x4yU0jib1szSVY4cbD450j
z#JR?qa<(son`;D(%p8=DTCi&oiGhZfV3ne1vJ|`EUn0dY4Umim=<~*{u3!H$W`SCZ
zEloq&O6hq}oknU6B4UI3j|K?DN-_w+t$16@
zPxvI5I0Gk`D3vO9O*+7hygzPOnFeW+GiQ#q@yA!CkX5DiFhKkqB;iKK<}tyD`R&kU
zb}ud-aQ{J~p+9f*d}HbAEb|8Gvh3ZgMEQXkGU)%W?+I?=Zexjz%hNI6&C}%CyfHT5
z?}S&;loyGvE@D=6s!lgEOe45(I1w^tEle_w&y1{-2FE5GEvsF0-*0L9V1YL=!hgd%
zDN?UGJoZJ$Mb|Wc_=-1+FZ;w4y!$O!O7fj~9N~I_&bQrY@8ax%1
zit<+TrCpwtvX9NnAfE|59o8{YzUk9o(4OjlpCH1tgURe&8`v<+kv;N7DM7<#Ap_I5
z&AQb-3;fJ`uU2<0C+NC^692Mf3%3(s{b;zoiIJclT^Ic4r2_A0ddn`lNK+JP($j>T
zSj4~RII1O4F5;tQ)9f#N^fQ#kie(fW88*$DL9J(zz-=WrVB&R+stm#XyHgGM^=7@`
zcWN@S^y_LNyisG0<%lrf?#c?kL8a%RP+A^l9-d-6ouI?#P~TCc+KF`XSm|+xc)FbYgvO%~_f@*?+X4JSi#U8FZb1@eN+f>UpbYo7d53t#+)(s~RoXpRy
z3zAS(E1R_L#%rJ!muDd4B9t88JR}D>c+hVjEE5GdCi8vvEuCm$*WUkYjLHI~7Zw@9
zYXB7M!pr5~fGGn`_zv0S!V@!xW4^xd=9ik_jnAoRjh6USq06bF_Ocw?+bE((-=lW`V5NFN*8>h-j&{&x{ubY!%-%nS`kdq
zE8<~8Kke}m1yk(2RcEmP)_02uV&9^dMq6xsB8uqZp6mesk6#-8a^Ih#N6qe*MSXj-
zz$!>J;~6wvCk9x)k93yriBkcJ4&t1`{TgZ-DR{c`O6QC0iwCC9xG+mTb|rU$6Gg4E
z6azgr!#-KJp-~+mQfaSd_HX$Mk!y-q7a4Tsp3nKSpLt>bCubG5<@64_z3yZkOsW0S
zoDV%dNBvcbey=k++vQlRUj6&L`xeJ9g&f@<1prr~dfTxYlfK3UHt0hgM;r)+~K
zzok}S>Hlg(Qlt9KB~Ke&Ult1oa|}~b_6^DaXb7#(o9#AVS<6C`+aE8=-#52^2KYiw
zWRqwlHL;G#m9TxP_yI0w2b
z!n_X#U@SrMhe47EItFpI}A>2XM>3wpQOH%MSb6ejg
zbm0ykx>~9tOFvwR6klbseo29)RNcl0)GFR?-cwfYnasNQRMurH>}HLo%lx9?5P(0>
zaUBC(VgImDrp@6N$|}qI_RyV+t<8n6$;9VCoe`hAS`Af1UVu0_g2QsG@TO*PayZ^q
zS!0Dd%$jK}=!EJ?YP+ll$9f>Ype!O7qER^?JMmV9S$ueP_!+OMVZ+6oD?5N!(Ly#w
zOG;jU3xM7YFSP)q!e!S&ttFBG>z2prU3!+W@~Zb;>7{`N_y^Zof_6}#8T0NRd=}wZ
zf%7`v+_oAh7YWo&VzjL4c-MC0eKXKn9Y&UtbYb*rByh%oDc@uD4(n+cu7UBgCyC
z@iE$&KJ$T%*UU;7C=>BK8nVx(1ZHf_Bvb~>E1Tj{RR=7u1qs)eBE=2uyzwLhZVi+u
z8A@gJy20*?m;2h3z>EkbMl*01?pYnNTTwC3Eu`ZN3aolzs>Xu2q9Qp}vkM>Z{~%|c
zS#$CosD(ML=V|mNCiZT~{jK`fKC`A#cq^e>GT-(8V08(PVfYguy?z>>u;59u9!}7%
zY@Opwa`;EaqKWSh*BD^Xn8$0YnM?kYizO|Ri|dwRODfPSnL!V{&1UI8C156iPi*A#
z3}f`+9}&_8jITB(gtkB(nM)5>{%z2VF<<=uw7O*X5UO-72{Os2p3dew4^^o@lSHS+
zfb9x7P6;k`Li#dKN|o(`Cdb>`_>GvtrDskR<{O;JfA_l3
z{xd#|7niqbZ7{3-DFK=*fg$=3x`?(L=&xBgU0jy5)fzlyN%2uE;n@+_gM}P~dEvBI
z2U4F+oaXJPpvTV?(9>9J=L;P)VPKK^6|)6NVNJ;)NSDFQkm7cyq(Dqw29ng;!0fr9
z0|80?$>VmAGfBQk31OZ#*p!-};PeDAT@4#MKac
za8(7|GgPaU!|h{$x-*jEn|)E|IPeB4&4!YZELoq|xS2OGp?d7(^$C7kmQW?oA;CFF
z5bJJbB?`;e#yNpQ73U=CT<7`fvbs*XnJ0WCE!12Y+^*uXz7|S8dh!>f`OWn{>1wS~
z1t5uf-NQ7q0Kq=vNR+8CPF>7NsKkM@_Yrmik
z$O?G(j)Fz0#*|WQzzP!`vtS&$@Fr9_SxT)(H6U4Am++%HGVUy@965ftZ-#|U+9>sd
zLONO8{2s7v()$*h3z4J;hz(9Vxa9Ygb5+wy{8mXBF3!Z{zJ^*Mws6c$jcHtM_5^S(
zdV<#ZXmqfwPJP~o9ueGX#x#-w(Uf{ki8@zX?yBMfYzZee@Wx*Dwq{iDP@!}haLs%u
zCD=p;&W0k>8s0u#Wm(gYn~Iu8X=MaGnXCES5QxbniFu_36=m}qE}+$Ahhfe$2?3}u
zZH5Ariv}|eCl+517HZ$~UuX8b0SW=VwY3+k7Vrd?xY|5+{s(Pu8CPZZb&cX}V3C3d
zNK2P=gWv|Fr8iR2-Q6lkcQ?{qn-1wtk#3|n-5u}R>iztm=bZPP-+Mls^=ao7>so8B
zImZ}tu3F2iug_sV+~;o@P4wTjJBij`9@+hBeI7qZ=$a(6gZ`lE$8UIW_y=3F2_<__
z{;-uqasX?b?cV-J=`%~(FxHUVB6mr00>1s7;qkA0^jqW0BV*0<<;O8!_Ihixz5C}u
z;-0yEKfoJTI9B+c2Qr%d8um=`>3n-IlJ`|6V)9(@?NS5+bgCOTH#}JE>CW)HP20#c
z^3LCgj*8dK_8?7JC#+pHBT@JTwW_9`4~S9CbUzCX$N?~$y69lF8u?m
z|7@!@X$ujtzDd>;3-g(M2A;X1{2g3XR51wHSMmt#^$L;>cjpw1*EmXU_J;}uPde}|
z)M~8toSmnRwp1;?SImI&U4~V1<@xO{RIEQ(-1X-2Azb2KkP~GMvWvz1`%~7Nr#_u^
zGMX{PM*BC;hF2|3QMt{9s?gc|_XD;=n)DIf$(;9X3{JXsSEHNr@JJ-$1_`(Ps(w4x%EZ-mQ|q@0u%9jD>q%X)<}
zp2l&_6|%m-NFYFynvAs$gIFuLv9Un<6+iX#cC}~P3s%0Ry-eZxyF2|lgFx+kNoY?A
zUGzcVe0)-+DkRu&bt2b1}-?o0yG;AG1vhu`&*c7tj{
zb0_Pp(20~<74EKrqEd+pn}i-JH+{1eK9`e`WOQW?GpZA+
zhpYD{zcCSP64p{A)wQavn?@_DQ5Yt}AxbF=3Na99$;
zzaSjc3_Y|k100MOloXgE!{;z}-7Kt9`UVGJs071>@DC0D=-iD(~)F>u+D~lTuPlh$?qVErcpP$gk4B;}6tTu4
zhR8V6x#4DlAfCX>0r5M-LMP)J-hQeMUh6GQyDHCFqHK>0PrS!7KLQ}Xn5*j8AiJ&;
z%^{CuKtT>?9XvVq3~LEPr%d&r4XBvAl*ycfC)O?(RpULSjQKNWvS60g+}R}Keh>ln
z6p!rHBekxZ6}T7*!LyLiY|T%8p|{ESb<+%)N7LcmrThb%FLF^Hy$i>~nzkpLi;!|)
zUP3Rwdn)hXzkI^w%uV3qm*cWNb)2f{K$EKo@^xBh#gOY{jpYyD)t&ZD(4T8;cl^V~
z9ruLmOnR(y_U(A5#^zS3>!aE1;*{KE6AC`tH)W!fLtj5j`{-**pI2;dBoXLeM;c$@
zi>KP9G!*R!H+ioBX1JJkx20$6zcAH&0L!%t=i5vN-=2=@2snm*SAjx5-aq?~JQOkl
z$~QFeBcU~c>e4bv+`^FCtHIlPtyXXEDZn^yyfhqdQ{8(P$GftSJ5*&s179ccvO+>a
zL2u8h)V})vASDIG!H^ch_GWAb*({ZlZlz6(AK=DrY;D=rsIey$4uSn=k&y6C5#zq5
zij{5YcYSX{H!yZgDR2-QLI-0L00q}I&}L3HPn9)EQlHY$kK63ilp72ZCiI__xZFXD
zsu!vpSSh{Out>ceqel!r&|bGZgSJs}+#wZmeoR*a$BfR1=lkRi>v_Mq)s{?PAh-f8?V-Uv_s{~c@pq>DZB
zMP(N41BzRd=iWDN!g9QePsqOndk~lX-t%d+*Pq&Dv2Rdd9q~da2vE}T)wlm_G1PSZ
zoy`&twfHU(EcJeEZuk#6i%+w?C#~
zLf`*dPoy6o>?G)R|D)&vT3>z`g5Wv{+FSsd3n08eR#!ONyS-^miZb=!cPXJH#Cvux
zm4+ZeockqeXX=*FF+J*Ju2{bxVgM4W5v6^Wo&}TWcl(4rtnj8%x^lVMTa|rOvR9^l
z)1Xry?!!~dfsQfcz2jji;8C@AtD#wgOy|na5BwH`4nLg;F`rhe^Oe+DMUFlLvwS1`e5sIw-;sxf^!cED!NXb_OPs2MD|3O>jdrN6ITL{=sU5C8S
zxT@7Sg@oFqZTsB9m;ELssps=C!NA&+cE+rnvfPT$3VL0%eO8Kxs=!UklfHKT$Q#Q;
z#^<)V0HvBcoZb>h+@IRj^!R%tNDBG+4{#M!t(oAOw-@6E$xc~wt2dtd2iZ)vb
zJp^e{MUX6GK!rgQ5sdv;R5L~Ql=_#HY5RjX^)Byi{xw^KUZdEd)I1+SxtTGK{UzYQ
znGm_|2?jlx^SHgPtgP%HDrvr{g3PnJd)TZtUVN#o%k0)hJiR4>ew6K1J-u}U;qQzpa918YFS^pIzs$B6!#4-+T8w+VBsaGO
z0p`G#)8sM?q+(LtOv@x(y#IIB#~K?yhztKhor*oJ7k)kS6Sjr>MjQn;bqZVK=MtMt
z=5xu)&dv`hR;@AzUPgbVyNmnRtPs$RcBpE=m7_?^ZFG$V-txtl)iPvFqTL1xD!b0w
z>8DJSV}gRQyepQRRIExBToix%^v$#Qc)Obe@ABhUHS8_=y$qed
zhc0PB59Px<*_u2A{I+q3=}h&0Vb$CR`?m#)P+_UA9m$xTpB>{$bauv0huO2Nhl;ZS
zB$|K$mfe%85WgLX;)8#ji(VQ-+f-(8?XBq-A`@C#%vug1)
zpBi6XZSp+&^1SXV+MqNB#?bcut$Ouk{bk|6YTd-Q=ew!s@5J9aP!^O
zV`s1ow!0hVrP5!{yOi`|tDYoc8233&GlR7Pt?1vi0tqii@PrEfdjUg;JxjD&TXzC7
zqm?z%pWJOs`OwWy$p{ksdKnqrNdBIo3C70exz)?qs4GzLKNmD*O*gw56q-Lv+A}>c
zbl4NNwNBHr=sm|}JccHhd}#xy@T-1%$e(?MxS9jL#(;z7%OTh$3C-u-0fHQ|fW4~c
zqVk@prC3z6mZzRII0#A1+oS~%dedrs>U~mDk|hCgN`Rg9N7>xf{!!6~gU~0+5cUzf
zwACN|ln^*;MkBOmjQ#;z3k1gGhc3wApr1p_jILyGV*o5r$o-EsHQ!mj?6jKs
zEGf+0lC2LJg?QEHh8v>mepWiy4+a$xOk@D)5hhp$*aK@PI-JQ&eXXb=vOA0CP-Zf^
z(f-BBH_>V8iQXRYx~Gh6MKcLhhTL0geF>v#gH|s)+n@9rMuWxf*y|`*A%q>Ax1e4T
z2jR~H@6C>$t<;3jUIsB8lhuH7U17}_BaDK5!(WblVPq9zTrE#M|1DT)*6r)j`CQz$
z1SoIDV2M}nT3J|94_1kG%jA3*kyth*_dlX+pD}u)fJ`1<r6M)`-xR;#$$twIzwHd$GGY0
zrc^V6vUDhi$EOJA1+S!~4*G$h;F)SRA|nAQgu;j*GdOggh@6xLf$sSF!qgOMs$<`o
zbI0l5iJ*nzo$!R{+jkAAk`Mrl*Jw-{paxRe`ajJ{t?wTua2eDqiholRlzl44)QnOQ
zCF07SCX^W9?8J|6slRrxcyn;a0F?C!XwbvTqe@26Js%0Q4VX?@oF3HV|8>aqo5r!o
z@8(;np|g8B;{NWJDNvV(>8CFPJx=1<)+fgGaLobWU6Y~JFI${or
z3)#tPB;^b5gvL^a*!yY!B2B1S9efCcg74g4B|R6m`of#YQo_ZkMCl;Oq@}jjl?+WD
z>*oZ}CelFmZ%p*$+Knlo0GZZY&
zi0fMj%T#xyih#W)P5TY;($6OMI!*Uj$*|%Fg*SYhguEK}sC=VIO)=$CJuG&(YbS?lF_VNCowdfOa
zhO2yDwI;R9@T!vLJFND+5BhYan9QOb&d4slK#y-^)n`BqWud5p*9x_)fiK;UWm$
zgMfM@22Kt|OEhH_Tyf$WvJq
zeopc%JK{#MK@Jc&9g-A!&+taPpp)N
z)PdjZ4;yNM7WMfp_#_;5)k`7cQ7?%nRK!KP1(5_cUTzfZOh;r2Rlt~
z1U&?&`o{H#UH$e5bUR&V*ckhUP>xWd#LP+Nif|-QHFF9kpS)qHx0$SCp+U=AeM}?&
zT|vXmh((IJ+*x6?E5^hNf#UDPW>veB6czL6E13trEF_3bs4KkEsK(OC>@+P*P+?*|
zZ)?vvS3=yACDGqL>;Sm_c`^Yz824#>-%`32jgHrHC6oyo0Z&Tm@6;!;I)3~Oxurrt
zxW_$S0g&)mlW4>VB+c|L&AK49h3=_Igd#q1);+morc@L;YCOhf=%MlAp=!129GSo1R{F%Z6?03
zP9S(7yp;HhGYkk7URB!%^F=9(riXJa@XnK6gG7oo
zi<{f6Sr1crcpS$S?d#bj_W1PKJpb-Qq41<*BEfI0_gs4Rp1;yIs(8-TjNVA_IR)~J
zR$JjGmS1i3!?D-Qf(OnjOhs|oWXpT6On&fMH(9=!bErVYRFJfP8P57on84MKwjXaT
z6)HyIKc!QW+IES+X3icRes}L3TpN~G4A$&k&UBVzy7b$rbEMZ-fbH1~uu8@j_MQ$G
z%)EGB=(H&Y2FU`~o4*XJ$x(D&m8dTDYFN5XH%UT61SRQ4(1T2-0ya+J%8TaP5Q;s?r
zqOCgcdJGQ>K>B2|J$5MRZ#BeJf_%GP^!YM|VOjSaVzC+OS^D1Pa(MEf1q{EBg>TV*
zt2Us_4lJ#t40;Ix%l|5N0l`(h7v3q
zRCHsxMf`g}pZ;ld(|NR|i7ANYp%lrws<(@!G+2u2-R#y#y$|}WaI-S
zKwLk_?!(`uijk!#<&<4?ECMfPZ1JS2E<
ziTNHigl7WUf}OI`0~I|zJwF^}UA?Fe?T<<>f?PlBm7CWXmJx1DfXK=VuvFhFe3?
zAu$<7f@1!oSw=Y=dvuJTW5qMt9G=l>2!3@hwY19G*~;B)#@=4cp+(ukYn+ND#j+UG
zmjaOJNvtYCC+GPt_}rrE-_tspMPd{87FyDkt(L-Ia*y4yHAOevDoMjQKds;sU7cO9
zUK5-S-5Szy>KE*Om*pNUd?cYrRDYE&{M@~Vh>O)_BVbtED=*h!j9FuQciQGx&Ar0p
zV2uW$?1s>E9Z31F1^C?B-rv%mpA?Npqo6Ic<_xZ}if0|=4jpcE32r7k#yGvOt&^RH
zVcF!nknJz(zXD(7GHv_E-7PO;_UBkDvM=>%wTZ7$S6+)u(k*Jl{F*|hnfz@d;3SJ&
z%Gvo)pg2@3g#CGCv*kyDAQ^$qp3b#e&Og1{lvi0Bf0SPlF+4n+iRP`S)7NR8{Qc%p
zvmCh_AJ3R=Ip|jQ89SG3;)f6jD-Qom7r`9oeq5&UyptBukZHAv%jwvr&|oF+lQ|v^
z5?3W*20@?hQuHwyBDvgCGoQ`Q>Sl8QXHLy{bX0ve{xHTjKuTgRulp9{@)FHc2C
zH3u*-`uS@`muJX}RgQ}$CBr()dg!bVHkK-n_fY+>rV08z$og;U#9tRVr!$utWqXo}
z@+*grckl~fGTkl>(0m}s+kEcV-d*&PyU487V_iI~4t{vY{z&7Un-(#iTk)c&zdzc=
zW5R!nyY6w*(0WP+eaRK1g-43PZ4^htb)Fcb!4_>Y*GT0md;0j|!5TuRAjN4Py;48*
zL={JUe_zte9D+Bw6PSM-;+L1>Jgh`IxLulTCiP0sCG4=(tcR}CXUdFq+dBMoUx#$1
zQBjp6ljn=dz?0`r6KL1^7N<%3$ysKQB?sQ+<R$8Yr+4ix=C%F>{HOltsN!(cysi?iKwN{+
z<>nlN!`py-e#i3`ob2_U)Ym_M=f5IMA28!e;-gv4X}O~3#wMA|Ff@9JzIRhl)oM(e
ztHT|9X?_BBo??cE(lN<7o~gq-9p)~EyB$H~^