Просмотр исходного кода

小程序合并订单,直播数据,订单售后,退回退款,直播数据,

yuhongqi 1 месяц назад
Родитель
Сommit
7e07388a8e

+ 3 - 1
fs-company/src/main/java/com/fs/company/controller/live/LiveDataController.java

@@ -60,7 +60,9 @@ public class LiveDataController extends BaseController
     @GetMapping("/getLiveUserDetailListBySql")
     public R getLiveUserDetailListBySql(@RequestParam Long liveId, HttpServletRequest request) {
         CompanyUser user = tokenService.getLoginUser(request).getUser();
-
+        if ("00".equals(user.getUserType())) {
+            return liveDataService.getLiveUserDetailListBySql(liveId,user.getCompanyId(),null);
+        }
         return liveDataService.getLiveUserDetailListBySql(liveId,user.getCompanyId(),user.getUserId());
     }
 

+ 1 - 1
fs-live-app/src/main/java/com/fs/live/controller/LiveController.java

@@ -77,7 +77,7 @@ public class LiveController {
 		live.setLiveId(Long.valueOf(params.get("stream_id")));
 		live.setStatus(3);
 		live.setFinishTime(LocalDateTime.now());
-		liveService.updateLiveEntity(live);
+//		liveService.updateLiveEntity(live);
 		return R.ok();
 //		{app=200149.push.tlivecloud.com, appid=1319721001, appname=live, channel_id=673,
 //				errcode=1, errmsg=The push client actively stopped the push, event_time=1755571239,

+ 18 - 2
fs-live-app/src/main/java/com/fs/live/websocket/service/WebSocketServer.java

@@ -157,7 +157,7 @@ public class WebSocketServer {
                 sendMsgVo.setNickName(fsUser.getNickname());
                 sendMsgVo.setAvatar(fsUser.getAvatar());
                 // 广播连接消息
-                broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
+                broadcastWebMessage(liveId, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
             }
 
             LiveUserFirstEntry liveUserFirstEntry = liveUserFirstEntryService.selectEntityByLiveIdUserId(liveId, userId);
@@ -242,7 +242,7 @@ public class WebSocketServer {
                 sendMsgVo.setData(JSONObject.toJSONString(liveWatchUserVO));
                 sendMsgVo.setNickName(fsUser.getNickname());
                 sendMsgVo.setAvatar(fsUser.getAvatar());
-                broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
+                broadcastWebMessage(liveId, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
             }
 
         } else {
@@ -588,6 +588,22 @@ public class WebSocketServer {
         session.getAsyncRemote().sendText(JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
     }
 
+    /**
+     * 广播消息
+     * @param liveId   直播间ID
+     * @param message  消息内容
+     */
+    public void broadcastWebMessage(Long liveId, String message) {
+        ConcurrentHashMap<Long, Session> room = getRoom(liveId);
+
+        // 普通用户房间:并行发送
+        room.forEach((k, v) -> {
+            if (v.isOpen()) {
+                sendWithRetry(v,message,1);
+            }
+        });
+    }
+
     /**
      * 广播消息
      * @param liveId   直播间ID

+ 1 - 1
fs-service/src/main/java/com/fs/erp/service/impl/JSTErpOrderServiceImpl.java

@@ -878,7 +878,7 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
         log.info("订单号: {},发货状态: {},是否发货后: {}",liveOrder.getOrderCode(),liveOrder.getStatus(),ObjectUtils.equals(liveOrder.getStatus(),2));
 
         // 发货后退款
-        if(ObjectUtils.equals(param.getOrderStatus(),2)){
+        if(ObjectUtils.equals(param.getOrderStatus(),2) || ObjectUtils.equals(param.getOrderStatus(),3)){
 
             FsJstAftersalePush fsJstAftersalePush = new FsJstAftersalePush();
             fsJstAftersalePush.setOrderId(liveOrder.getOrderCode());

+ 98 - 0
fs-service/src/main/java/com/fs/hisStore/mapper/MergedOrderMapper.java

@@ -0,0 +1,98 @@
+package com.fs.hisStore.mapper;
+
+import com.fs.hisStore.param.FsMyStoreOrderQueryParam;
+import com.fs.hisStore.vo.FsMergedOrderListQueryVO;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
+
+/**
+ * 合并订单Mapper接口
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+public interface MergedOrderMapper
+{
+    /**
+     * 查询合并的订单列表(商城订单+直播订单)
+     *
+     * @param param 查询参数
+     * @return 合并后的订单列表
+     */
+    @Select({"<script> " +
+            "SELECT * FROM ( " +
+            "  SELECT " +
+            "    o.id, " +
+            "    NULL AS order_id, " +
+            "    NULL AS live_id, " +
+            "    NULL AS after_sales_id, " +
+            "    o.order_code, " +
+            "    o.pay_price, " +
+            "    o.status, " +
+            "    o.is_package, " +
+            "    o.package_json, " +
+            "    o.item_json, " +
+            "    o.delivery_id, " +
+            "    o.finish_time, " +
+            "    o.create_time, " +
+            "    NULL AS total_num, " +
+            "    NULL AS discount_money, " +
+            "    1 AS order_type " +
+            "  FROM fs_store_order_scrm o " +
+            "  WHERE o.is_del = 0 AND o.is_sys_del = 0 " +
+            "  <if test = 'maps.status != null and maps.status != \"\"'> " +
+            "    AND o.status = #{maps.status} " +
+            "  </if> " +
+            "  <if test = 'maps.keyword != null and maps.keyword != \"\"'> " +
+            "    AND o.order_code LIKE CONCAT('%', #{maps.keyword}, '%') " +
+            "  </if> " +
+            "  <if test = 'maps.deliveryStatus != null'> " +
+            "    AND o.delivery_status = #{maps.deliveryStatus} " +
+            "  </if> " +
+            "  <if test = 'maps.userId != null'> " +
+            "    AND o.user_id = #{maps.userId} " +
+            "  </if> " +
+            "  UNION ALL " +
+            "  SELECT " +
+            "    NULL AS id, " +
+            "    o.order_id, " +
+            "    o.live_id, " +
+            "    a.id AS after_sales_id, " +
+            "    o.order_code, " +
+            "    o.pay_price, " +
+            "    o.status, " +
+            "    NULL AS is_package, " +
+            "    NULL AS package_json, " +
+            "    o.item_json, " +
+            "    o.delivery_sn AS delivery_id, " +
+            "    o.finish_time, " +
+            "    o.create_time, " +
+            "    o.total_num, " +
+            "    o.discount_money, " +
+            "    2 AS order_type " +
+            "  FROM live_order o " +
+            "  LEFT JOIN ( " +
+            "    SELECT t.*, ROW_NUMBER() OVER (PARTITION BY t.order_id ORDER BY t.create_time DESC) AS rn " +
+            "    FROM live_after_sales t " +
+            "  ) a ON o.order_id = a.order_id AND a.rn = 1 " +
+            "  WHERE o.is_del = 0 " +
+            "  <if test = 'maps.status != null and maps.status != \"\"'> " +
+            "    AND o.status = #{maps.status} " +
+            "  </if> " +
+            "  <if test = 'maps.keyword != null and maps.keyword != \"\"'> " +
+            "    AND o.order_code LIKE CONCAT('%', #{maps.keyword}, '%') " +
+            "  </if> " +
+            "  <if test = 'maps.deliveryStatus != null'> " +
+            "    AND o.delivery_status = #{maps.deliveryStatus} " +
+            "  </if> " +
+            "  <if test = 'maps.userId != null'> " +
+            "    AND o.user_id = #{maps.userId} " +
+            "  </if> " +
+            ") AS merged_orders " +
+            "ORDER BY create_time DESC " +
+            "</script>"})
+    List<FsMergedOrderListQueryVO> selectMergedOrderListVO(@Param("maps") FsMyStoreOrderQueryParam param);
+}
+

+ 24 - 0
fs-service/src/main/java/com/fs/hisStore/service/IMergedOrderService.java

@@ -0,0 +1,24 @@
+package com.fs.hisStore.service;
+
+import com.fs.hisStore.param.FsMyStoreOrderQueryParam;
+import com.fs.hisStore.vo.FsMergedOrderListQueryVO;
+
+import java.util.List;
+
+/**
+ * 合并订单Service接口
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+public interface IMergedOrderService
+{
+    /**
+     * 查询合并的订单列表(商城订单+直播订单)
+     *
+     * @param param 查询参数
+     * @return 合并后的订单列表
+     */
+    List<FsMergedOrderListQueryVO> selectMergedOrderListVO(FsMyStoreOrderQueryParam param);
+}
+

+ 90 - 0
fs-service/src/main/java/com/fs/hisStore/service/impl/MergedOrderServiceImpl.java

@@ -0,0 +1,90 @@
+package com.fs.hisStore.service.impl;
+
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONUtil;
+import com.fs.common.utils.StringUtils;
+import com.fs.hisStore.enums.OrderInfoEnum;
+import com.fs.hisStore.mapper.MergedOrderMapper;
+import com.fs.hisStore.param.FsMyStoreOrderQueryParam;
+import com.fs.hisStore.service.IMergedOrderService;
+import com.fs.hisStore.vo.FsMergedOrderListQueryVO;
+import com.fs.hisStore.vo.FsStoreOrderItemVO;
+import com.fs.store.config.StoreConfig;
+import com.fs.system.service.ISysConfigService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.List;
+
+/**
+ * 合并订单Service实现类
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+@Service
+public class MergedOrderServiceImpl implements IMergedOrderService
+{
+    @Autowired
+    private MergedOrderMapper mergedOrderMapper;
+
+    @Autowired
+    private ISysConfigService configService;
+
+    @Override
+    public List<FsMergedOrderListQueryVO> selectMergedOrderListVO(FsMyStoreOrderQueryParam param)
+    {
+        List<FsMergedOrderListQueryVO> list = mergedOrderMapper.selectMergedOrderListVO(param);
+        
+        for (FsMergedOrderListQueryVO vo : list)
+        {
+            // 处理商品JSON
+            if (StringUtils.isNotEmpty(vo.getItemJson()))
+            {
+                JSONArray jsonArray = JSONUtil.parseArray(vo.getItemJson());
+                List<FsStoreOrderItemVO> items = JSONUtil.toList(jsonArray, FsStoreOrderItemVO.class);
+                if (items != null && items.size() > 0)
+                {
+                    vo.setItems(items);
+                }
+            }
+            
+            // 处理是否可以申请售后
+            vo.setIsAfterSales(0);
+            if (vo.getStatus() != null && vo.getStatus().equals(OrderInfoEnum.STATUS_3.getValue()))
+            {
+                // 已完成订单
+                vo.setIsAfterSales(1);
+                if (vo.getFinishTime() != null)
+                {
+                    String json = configService.selectConfigByKey("his.store");
+                    if (StringUtils.isNotEmpty(json))
+                    {
+                        StoreConfig storeConfig = JSONUtil.toBean(json, StoreConfig.class);
+                        if (storeConfig != null && storeConfig.getStoreAfterSalesDay() != null && storeConfig.getStoreAfterSalesDay() > 0)
+                        {
+                            // 判断完成时间是否超过指定时间
+                            Calendar calendar = new GregorianCalendar();
+                            calendar.setTime(vo.getFinishTime());
+                            calendar.add(Calendar.DATE, storeConfig.getStoreAfterSalesDay());
+                            if (calendar.getTime().getTime() < new Date().getTime())
+                            {
+                                vo.setIsAfterSales(0);
+                            }
+                        }
+                    }
+                }
+            }
+            else if (vo.getStatus() != null && (vo.getStatus() == 1 || vo.getStatus() == 2))
+            {
+                vo.setIsAfterSales(1);
+            }
+        }
+        
+        return list;
+    }
+}
+

+ 78 - 0
fs-service/src/main/java/com/fs/hisStore/vo/FsMergedOrderListQueryVO.java

@@ -0,0 +1,78 @@
+package com.fs.hisStore.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 合并订单列表查询VO(商城订单+直播订单)
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+@Data
+public class FsMergedOrderListQueryVO implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 订单ID */
+    private Long id;
+
+    /** 订单ID(直播订单使用) */
+    private Long orderId;
+
+    /** 直播ID(直播订单使用) */
+    private Long liveId;
+
+    /** 售后ID(直播订单使用) */
+    private Long afterSalesId;
+
+    /** 订单号 */
+    private String orderCode;
+
+    /** 实际支付金额 */
+    private BigDecimal payPrice;
+
+    /** 订单状态 */
+    private Integer status;
+
+    /** 是否套餐 */
+    private Integer isPackage;
+
+    /** 套餐JSON */
+    private String packageJson;
+
+    /** 商品JSON */
+    private String itemJson;
+
+    /** 物流单号 */
+    private String deliveryId;
+
+    /** 是否可以申请售后 */
+    private Integer isAfterSales;
+
+    /** 完成时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date finishTime;
+
+    /** 创建时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+
+    /** 总数量(直播订单使用) */
+    private Integer totalNum;
+
+    /** 优惠金额(直播订单使用) */
+    private Integer discountMoney;
+
+    /** 订单类型:1-商城订单,2-直播订单 */
+    private Integer orderType;
+
+    /** 订单商品列表 */
+    private List<FsStoreOrderItemVO> items;
+}
+

+ 7 - 2
fs-service/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java

@@ -748,7 +748,11 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
             order.setPayTime(LocalDateTime.now());
             order.setIsPay("1");
             baseMapper.updateLiveOrder(order);
-            this.createOmsOrderCall(order);
+            try {
+                this.createOmsOrderCall(order);
+            } catch (Exception e) {
+                log.error("推送erp失败:{}",e.getMessage());
+            }
             return "SUCCESS";
         }catch (Exception e){
             log.info("支付错误:"+e.getMessage());
@@ -3478,8 +3482,9 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         FsStoreProductScrm fsStoreProduct = fsStoreProductService.selectFsStoreProductById(liveOrder.getProductId());
         LiveGoods goods = liveGoodsMapper.selectLiveGoodsByProductId(liveOrder.getLiveId(), liveOrder.getProductId());
         if(goods == null) return R.error("当前商品不存在");
-        if(fsStoreProduct == null) return R.error("店铺已下架商品,购买失败");
+        if(fsStoreProduct == null) return R.error("商品不存在,购买失败");
         if(fsStoreProduct.getIsShow() == 0 || goods.getStatus() == 0) return R.error("商品已下架,购买失败");
+        if(!"1".equals(fsStoreProduct.getIsAudit()) ) return R.error("商品已下架,购买失败");
         if(liveOrder.getTotalNum() == null || StringUtils.isEmpty(liveOrder.getTotalNum())) liveOrder.setTotalNum("1");
         if(goods.getStock() == null) return R.error("直播间商品库存不足");
         if(fsStoreProduct.getStock() < Integer.parseInt(liveOrder.getTotalNum()) || goods.getStock() < Integer.parseInt(liveOrder.getTotalNum())) return R.error("抱歉,这款商品已被抢光,暂时无库存~");

+ 1 - 1
fs-service/src/main/java/com/fs/live/service/impl/LiveServiceImpl.java

@@ -323,7 +323,7 @@ public class LiveServiceImpl implements ILiveService
         });
         notifyTask.setData(JSON.toJSONString(data));
 //        return R.ok("success");
-//        liveMiniprogramSubNotifyTaskMapper.insert(notifyTask);
+        liveMiniprogramSubNotifyTaskMapper.insert(notifyTask);
 
         return R.ok("success");
     }

+ 2 - 2
fs-service/src/main/java/com/fs/live/service/impl/LiveWatchConfigServiceImpl.java

@@ -75,7 +75,7 @@ public class LiveWatchConfigServiceImpl implements ILiveWatchConfigService {
     @Override
     public int insertLiveWatchConfig(String userId, String jsonConfig ,Long liveId)
     {
-        Live live = liveService.selectLiveByLiveId(liveId);
+        Live live = liveService.selectLiveDbByLiveId(liveId);
         if(live == null){
             return 0;
         }
@@ -95,7 +95,7 @@ public class LiveWatchConfigServiceImpl implements ILiveWatchConfigService {
     @Override
     public int updateLiveWatchConfig(String jsonConfig, Long liveId)
     {
-        Live live = liveService.selectLiveByLiveId(liveId);
+        Live live = liveService.selectLiveDbByLiveId(liveId);
         if(live == null){
             return 0;
         }

+ 23 - 7
fs-service/src/main/resources/mapper/live/LiveDataMapper.xml

@@ -412,15 +412,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <select id="selectLiveDataDetailBySql" resultType="com.fs.live.vo.LiveDataDetailVo">
         SELECT
             COALESCE(video_duration.total_duration, 0) AS videoDuration,
-            (COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 THEN lwu.user_id END) +  COUNT(DISTINCT CASE WHEN lwu.live_flag = 0 AND lwu.replay_flag = 1 THEN lwu.user_id END)) AS totalViewers,
+            COUNT(DISTINCT lwu.user_id) AS totalViewers,
             COUNT(DISTINCT CASE
-                WHEN lwu.online_seconds >= COALESCE(video_duration.total_duration, 0) AND video_duration.total_duration > 0
+                WHEN COALESCE(user_duration.total_duration, 0) >= 1800
                 THEN lwu.user_id
             END) AS totalCompletedCourses,
             CASE
                 WHEN COUNT(DISTINCT lwu.user_id) > 0 THEN
                     ROUND(COUNT(DISTINCT CASE
-                        WHEN lwu.online_seconds >= COALESCE(video_duration.total_duration, 0) AND video_duration.total_duration > 0
+                        WHEN COALESCE(user_duration.total_duration, 0) >= 1800
                         THEN lwu.user_id
                     END) * 100.0 / COUNT(DISTINCT lwu.user_id), 2)
                 ELSE 0
@@ -473,8 +473,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 ELSE 0
             END AS totalViewerConversionRate,
             CASE
-                WHEN COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 AND lwu.online_seconds >= 1800 THEN lwu.user_id END) > 0 THEN
-                    ROUND(order_stats.paidUsers * 100.0 / COUNT(DISTINCT CASE WHEN lwu.live_flag = 1 AND lwu.replay_flag = 0 AND lwu.online_seconds >= 1800 THEN lwu.user_id END), 2)
+                WHEN COUNT(DISTINCT CASE
+                    WHEN COALESCE(user_duration.total_duration, 0) >= 1800
+                    THEN lwu.user_id
+                END) > 0 THEN
+                    ROUND(order_stats.paidUsers * 100.0 / COUNT(DISTINCT CASE
+                        WHEN COALESCE(user_duration.total_duration, 0) >= 1800
+                        THEN lwu.user_id
+                    END), 2)
                 ELSE 0
             END AS completion30MinConversionRate,
             CASE
@@ -484,11 +490,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             END AS peakRValue,
             CASE
                 WHEN COUNT(DISTINCT CASE
-                    WHEN lwu.online_seconds >= COALESCE(video_duration.total_duration, 0) AND video_duration.total_duration > 0
+                    WHEN COALESCE(user_duration.total_duration, 0) >= 1800
                     THEN lwu.user_id
                 END) > 0 THEN
                     ROUND(order_stats.gmv / COUNT(DISTINCT CASE
-                        WHEN lwu.online_seconds >= COALESCE(video_duration.total_duration, 0) AND video_duration.total_duration > 0
+                        WHEN COALESCE(user_duration.total_duration, 0) >= 1800
                         THEN lwu.user_id
                     END), 2)
                 ELSE 0
@@ -502,6 +508,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             WHERE video_type IN (1, 2)
             GROUP BY live_id
         ) video_duration ON l.live_id = video_duration.live_id
+        LEFT JOIN (
+            SELECT
+                live_id,
+                user_id,
+                COALESCE(SUM(CASE WHEN live_flag = 1 AND replay_flag = 0 THEN COALESCE(online_seconds, 0) ELSE 0 END), 0) +
+                COALESCE(SUM(CASE WHEN live_flag = 0 AND replay_flag = 1 THEN COALESCE(online_seconds, 0) ELSE 0 END), 0) AS total_duration
+            FROM live_watch_user
+            WHERE live_id = #{liveId}
+            GROUP BY live_id, user_id
+        ) user_duration ON l.live_id = user_duration.live_id AND lwu.user_id = user_duration.user_id
         LEFT JOIN (
             SELECT
                 live_id,

+ 57 - 0
fs-user-app/src/main/java/com/fs/app/controller/live/OrderController.java

@@ -0,0 +1,57 @@
+package com.fs.app.controller.live;
+
+import com.fs.app.annotation.Login;
+import com.fs.app.controller.AppBaseController;
+import com.fs.common.core.domain.R;
+import com.fs.hisStore.param.FsMyStoreOrderQueryParam;
+import com.fs.hisStore.service.IMergedOrderService;
+import com.fs.hisStore.vo.FsMergedOrderListQueryVO;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+
+@Api("订单信息接口")
+@RestController("AppOrderController")
+@AllArgsConstructor
+@RequestMapping(value="/app/order")
+public class OrderController extends AppBaseController {
+
+    private final IMergedOrderService mergedOrderService;
+
+    @Login
+    @ApiOperation("获取我的合并订单列表(商城订单+直播订单)")
+    @GetMapping("/getMyMergedOrderList")
+    public R getMyMergedOrderList(FsMyStoreOrderQueryParam param, HttpServletRequest request)
+    {
+        // 设置分页参数
+        if (param.getPage() == null || param.getPage() < 1)
+        {
+            param.setPage(1);
+        }
+        if (param.getPageSize() == null || param.getPageSize() < 1)
+        {
+            param.setPageSize(10);
+        }
+        
+        PageHelper.startPage(param.getPage(), param.getPageSize());
+        
+        // 设置用户ID
+        param.setUserId(Long.parseLong(getUserId()));
+        
+        // 查询合并订单列表
+        List<FsMergedOrderListQueryVO> list = mergedOrderService.selectMergedOrderListVO(param);
+        
+        // 封装分页信息
+        PageInfo<FsMergedOrderListQueryVO> listPageInfo = new PageInfo<>(list);
+        
+        return R.ok().put("data", listPageInfo);
+    }
+}