igl 1 ano atrás
pai
commit
16ff4f1e25

+ 24 - 0
vehicle-api/src/main/java/com/chelvc/cloud/vehicle/api/constant/AttentionType.java

@@ -0,0 +1,24 @@
+package com.chelvc.cloud.vehicle.api.constant;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author: igl
+ * @date: 2023/8/14 16:13
+ */
+@Getter
+@AllArgsConstructor
+public enum AttentionType {
+
+  ATTENTION(1, "关注"),
+  FANS(2, "粉丝"),
+  FRIEND(3, "好友");
+
+  @EnumValue
+  @JsonValue
+  private int value;
+  private String desc;
+}

+ 1 - 1
vehicle-api/src/main/java/com/chelvc/cloud/vehicle/api/constant/DynamicType.java

@@ -14,7 +14,7 @@ import lombok.Getter;
 @AllArgsConstructor
 public enum DynamicType {
 
-    //ATTENTION(1, "关注"),
+    ATTENTION(1, "关注"),
     LATEST(2, "最新"),
     HOTTEST(3, "最热"),
     DISCOVER(4, "发现");

+ 25 - 0
vehicle-api/src/main/java/com/chelvc/cloud/vehicle/api/dto/AttentionDTO.java

@@ -0,0 +1,25 @@
+package com.chelvc.cloud.vehicle.api.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @author: igl
+ * @date: 2023/8/14 14:18
+ */
+@Data
+public class AttentionDTO implements Serializable {
+
+  private static final long serialVersionUID = -8364585953806128909L;
+
+  /**
+   * 时间戳
+   */
+  private Long milliseconds;
+
+  /**
+   * 用户标识
+   */
+  private Long targetUserId;
+}

+ 30 - 0
vehicle-api/src/main/java/com/chelvc/cloud/vehicle/api/dto/AttentionTotalDTO.java

@@ -0,0 +1,30 @@
+package com.chelvc.cloud.vehicle.api.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @author: igl
+ * @date: 2023/8/15 11:47
+ */
+@Data
+public class AttentionTotalDTO implements Serializable {
+
+  private static final long serialVersionUID = 3582381027065193024L;
+
+  /**
+   * 粉丝总数
+   */
+  private Long fansTotal;
+
+  /**
+   * 关注总数
+   */
+  private Long attentionTotal;
+
+  /**
+   * 好友
+   */
+  private Long friendTotal;
+}

+ 28 - 0
vehicle-api/src/main/java/com/chelvc/cloud/vehicle/api/dto/AttentionUserDTO.java

@@ -0,0 +1,28 @@
+package com.chelvc.cloud.vehicle.api.dto;
+
+import com.chelvc.cloud.uc.api.dto.UserDTO;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 扩展信息
+ *
+ * @author igl
+ * @date 2023/12/10 02:29
+ */
+@Data
+public class AttentionUserDTO implements Serializable {
+
+  private static final long serialVersionUID = -6263230284944185865L;
+
+  /**
+   * 用户信息
+   */
+  private UserDTO userDTO;
+
+  /**
+   * 关注状态(0-自己关注,对方未关注;1-对方已关注,自己未关注;2-互相关注)
+   */
+  private Integer flag;
+}

+ 25 - 0
vehicle-api/src/main/java/com/chelvc/cloud/vehicle/api/param/QueryAttentionParam.java

@@ -0,0 +1,25 @@
+package com.chelvc.cloud.vehicle.api.param;
+
+import com.chelvc.cloud.vehicle.api.constant.AttentionType;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * @author: igl
+ * @date: 2023/8/14 16:14
+ */
+@Data
+public class QueryAttentionParam implements Serializable {
+
+  private static final long serialVersionUID = -4730543402654742104L;
+
+  /**
+   * ATTENTION-关注
+   * FANS-粉丝
+   * FRIEND-好友
+   */
+  @NotNull(message = "请选择查询类型")
+  private AttentionType type;
+}

+ 1 - 1
vehicle-api/src/main/java/com/chelvc/cloud/vehicle/api/param/QueryDynamicContentParam.java

@@ -22,7 +22,7 @@ public class QueryDynamicContentParam implements Serializable {
      */
     private String keyWord;
     /**
-     * 筛选类型 ATTENTION-关注;LATEST-最新;HOTTEST-最热
+     * 筛选类型 ATTENTION-关注;LATEST-最新;HOTTEST-最热;DISCOVER-发现
      */
     @NotNull(message = "筛选类型不能为空")
     private DynamicType type;

+ 49 - 0
vehicle-api/src/main/java/com/chelvc/cloud/vehicle/api/service/AttentionService.java

@@ -0,0 +1,49 @@
+package com.chelvc.cloud.vehicle.api.service;
+
+import com.chelvc.cloud.vehicle.api.dto.AttentionTotalDTO;
+import com.chelvc.cloud.vehicle.api.dto.AttentionUserDTO;
+import com.chelvc.cloud.vehicle.api.param.QueryAttentionParam;
+import com.chelvc.framework.common.model.PagedDTO;
+import com.chelvc.framework.common.model.Pagination;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author: igl
+ * @date: 2023/8/14 10:11
+ */
+public interface AttentionService {
+
+  /**
+   * 关注用户
+   * @param userId
+   * @param targetUserId
+   */
+  void concern(Long userId, Long targetUserId);
+
+  /**
+   * 分页查看关注、粉丝、好友
+   *
+   * @param page
+   * @param param
+   * @author igl
+   * @date 2023/2/25 19:42
+   */
+  Pagination<AttentionUserDTO> page(PagedDTO page, QueryAttentionParam param, Long userId);
+
+  /**
+   * 查询用户是否关注
+   * @param userId
+   * @param userIds
+   * @return
+   */
+  Map<Long, Boolean> queryAttentionByUserIds(Long userId, List<Long> userIds);
+
+  /**
+   * 查看关注、粉丝、好友总数
+   * @author igl
+   * @date 2023/8/15 19:42
+   */
+  AttentionTotalDTO queryTotalByUserId(Long userId);
+}

+ 16 - 0
vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/dao/AttentionMapper.java

@@ -0,0 +1,16 @@
+package com.chelvc.cloud.vehicle.server.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.chelvc.cloud.vehicle.server.entity.Attention;
+import org.apache.ibatis.annotations.Mapper;
+import org.springframework.stereotype.Repository;
+
+/**
+ * @author: igl
+ * @date: 2023/8/14 10:12
+ */
+@Mapper
+@Repository
+public interface AttentionMapper extends BaseMapper<Attention> {
+
+}

+ 47 - 0
vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/entity/Attention.java

@@ -0,0 +1,47 @@
+package com.chelvc.cloud.vehicle.server.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 用户关注关系
+ * @author: igl
+ * @date: 2023/8/14 10:13
+ */
+@Data
+@TableName(value = "user_attention")
+public class Attention implements Serializable {
+
+  private static final long serialVersionUID = 3209107847982390085L;
+
+  /**
+   * 唯一标识
+   */
+  @TableId(value = "id", type = IdType.ASSIGN_ID)
+  private Long id;
+
+  /**
+   * 用户标识
+   */
+  @TableField(value = "user_id")
+  private Long userId;
+
+  /**
+   * 目标用户
+   */
+  @TableField(value = "target")
+  private Long target;
+
+  /**
+   * 创建时间
+   */
+  @TableField(value = "create_time")
+  private LocalDateTime createTime;
+
+}

+ 331 - 0
vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/service/impl/AttentionServiceImpl.java

@@ -0,0 +1,331 @@
+package com.chelvc.cloud.vehicle.server.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.chelvc.cloud.uc.api.dto.UserDTO;
+import com.chelvc.cloud.uc.api.service.UserService;
+import com.chelvc.cloud.vehicle.api.constant.AttentionType;
+import com.chelvc.cloud.vehicle.api.dto.AttentionTotalDTO;
+import com.chelvc.cloud.vehicle.api.dto.AttentionUserDTO;
+import com.chelvc.cloud.vehicle.api.param.QueryAttentionParam;
+import com.chelvc.cloud.vehicle.api.service.AttentionService;
+import com.chelvc.cloud.vehicle.server.dao.AttentionMapper;
+import com.chelvc.cloud.vehicle.server.entity.Attention;
+import com.chelvc.framework.base.exception.ResourceUnavailableException;
+import com.chelvc.framework.common.model.PagedDTO;
+import com.chelvc.framework.common.model.Pagination;
+import lombok.RequiredArgsConstructor;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+/**
+ * @author: igl
+ * @date: 2023/8/14 10:11
+ */
+@Service
+@RequiredArgsConstructor(onConstructor = @__(@Autowired))
+public class AttentionServiceImpl implements AttentionService {
+
+  private final static String ATTENTION_CACHE_KEY = "vehicle:user:attention:${userId}";
+  private final static String FANS_CACHE_KEY = "vehicle:user:fans:${userId}";
+  private final static String FRIEND_CACHE_KEY = "vehicle:user:friend:${userId}";
+  private final static String TEMP_FRIEND_CACHE_KEY = "vehicle:user:friend:${userId}";
+
+  private final RedisTemplate<String, Object> redisTemplate;
+  private final AttentionMapper attentionMapper;
+
+  @DubboReference
+  private final UserService userService;
+
+  @Override
+  public void concern(Long userId, Long targetUserId) {
+    if (userId == null || userId.equals(targetUserId)) {
+      throw new ResourceUnavailableException("不能自己关注自己");
+    }
+    LambdaQueryWrapper<Attention> wrapper = Wrappers.lambdaQuery();
+    wrapper.eq(Attention::getUserId, userId);
+    wrapper.eq(Attention::getTarget, targetUserId);
+    Attention attention = attentionMapper.selectOne(wrapper);
+    if(attention != null){
+      int i = attentionMapper.deleteById(attention.getId());
+      if(i != 1){
+        throw new ResourceUnavailableException("取消关注失败");
+      }
+      return;
+    }
+    attention = new Attention();
+    attention.setUserId(userId);
+    attention.setTarget(targetUserId);
+    attention.setCreateTime(LocalDateTime.now());
+    int insert = attentionMapper.insert(attention);
+    if(insert != 1){
+      throw new ResourceUnavailableException("关注失败");
+    }
+    /*boolean attention = attention(userId, targetUserId);
+    fans(targetUserId, userId, attention);
+    boolean b = checkAttention(targetUserId, userId);
+    if(attention && b){
+      redisTemplate.opsForZSet()
+          .add(getFriendCacheKey(userId), targetUserId, System.currentTimeMillis());
+      redisTemplate.opsForZSet()
+          .add(getFriendCacheKey(targetUserId), userId, System.currentTimeMillis());
+    } else if (!attention && b) {
+      redisTemplate.opsForZSet().remove(getFriendCacheKey(userId), targetUserId);
+      redisTemplate.opsForZSet().remove(getFriendCacheKey(targetUserId), userId);
+    }*/
+  }
+
+  public boolean checkAttention(Long userId, Long targetUserId){
+    String attentionCacheKey = getAttentionCacheKey(userId);
+    Long rank = redisTemplate.opsForZSet().rank(attentionCacheKey, targetUserId);
+    return rank != null;
+  }
+
+  @Override
+  public Pagination<AttentionUserDTO> page(PagedDTO page, QueryAttentionParam param, Long userId) {
+    if (userId == null) {
+      throw new ResourceUnavailableException("用户不存在");
+    }
+    Long pageCode = page.getPageCode();
+    Long pageSize = page.getPageSize();
+    long start = (pageCode - 1) * pageSize;
+    long end = pageCode * pageSize - 1;
+    AttentionType type = param.getType();
+    Long total = 0L;
+    List<AttentionUserDTO> list = new ArrayList<>();
+    String attentionCacheKey = getAttentionCacheKey(userId);
+    String fansCacheKey = getFansCacheKey(userId);
+    String friendCacheKey = getFriendCacheKey(userId);
+    switch (type) {
+      case ATTENTION:
+        if (Boolean.FALSE.equals(redisTemplate.hasKey(attentionCacheKey))) {
+          setAttentionCache(attentionCacheKey, userId);
+          if (Boolean.FALSE.equals(redisTemplate.hasKey(attentionCacheKey))) {
+            break;
+          }
+        }
+        total = queryCache(attentionCacheKey, list, start, end, AttentionType.ATTENTION, fansCacheKey);
+        break;
+      case FANS:
+        if (Boolean.FALSE.equals(redisTemplate.hasKey(fansCacheKey))) {
+          setFansCache(fansCacheKey, userId);
+          if (Boolean.FALSE.equals(redisTemplate.hasKey(fansCacheKey))) {
+            break;
+          }
+        }
+        total = queryCache(fansCacheKey, list, start, end, AttentionType.FANS, attentionCacheKey);
+        break;
+      case FRIEND:
+        if (Boolean.FALSE.equals(redisTemplate.hasKey(friendCacheKey))) {
+          setFriendCache(friendCacheKey, userId);
+          if (Boolean.FALSE.equals(redisTemplate.hasKey(friendCacheKey))) {
+            break;
+          }
+        }
+        total = queryCache(friendCacheKey, list, start, end, AttentionType.FRIEND, "");
+        break;
+    }
+    long quotient = total / pageSize;
+    long remainder = total % pageSize;
+    if(remainder > 0){
+      quotient += quotient;
+    }
+    return Pagination.<AttentionUserDTO>builder().total(total).pages(quotient)
+            .records(list).build();
+  }
+  public void setFriendCache(String cacheKey, Long userId){
+    String tempFriendCacheKey = getTempFriendCacheKey(userId);
+    redisTemplate.opsForZSet().intersectAndStore(getAttentionCacheKey(userId),
+            getFansCacheKey(userId), tempFriendCacheKey);
+    Set<Object> set = redisTemplate.opsForZSet().range(tempFriendCacheKey, 0, -1);
+    if (set != null && set.size() > 0) {
+      set.forEach(e ->{
+        redisTemplate.opsForZSet()
+            .add(cacheKey, e, System.currentTimeMillis());
+      });
+    }
+  }
+  @Override
+  public Map<Long, Boolean> queryAttentionByUserIds(Long userId, List<Long> userIds) {
+    if(userIds == null || userIds.size() == 0){
+      return null;
+    }
+    String attentionCacheKey = getAttentionCacheKey(userId);
+    if(Boolean.FALSE.equals(redisTemplate.hasKey(attentionCacheKey))){
+      setAttentionCache(attentionCacheKey, userId);
+      if(Boolean.FALSE.equals(redisTemplate.hasKey(attentionCacheKey))){
+        return null;
+      }
+    }
+    Map<Long, Boolean> map = new HashMap<>();
+    userIds.forEach(e -> {
+      Long rank = redisTemplate.opsForZSet().rank(attentionCacheKey, e);
+      if(rank == null){
+        map.put(e, false);
+      } else {
+        map.put(e, true);
+      }
+    });
+    return map;
+  }
+
+  @Override
+  public AttentionTotalDTO queryTotalByUserId(Long userId) {
+    if(userId == null){
+      throw new ResourceUnavailableException("用户不存在");
+    }
+    AttentionTotalDTO totalDTO = new AttentionTotalDTO();
+    String attentionCacheKey = getAttentionCacheKey(userId);
+    if(Boolean.FALSE.equals(redisTemplate.hasKey(attentionCacheKey))){
+      setAttentionCache(attentionCacheKey, userId);
+    }
+    totalDTO.setAttentionTotal(redisTemplate.opsForZSet().zCard(attentionCacheKey));
+    String fansCacheKey = getFansCacheKey(userId);
+    if(Boolean.FALSE.equals(redisTemplate.hasKey(fansCacheKey))){
+      setFansCache(fansCacheKey, userId);
+    }
+    totalDTO.setFansTotal(redisTemplate.opsForZSet().zCard(fansCacheKey));
+    String friendCacheKey = getFriendCacheKey(userId);
+    if(Boolean.FALSE.equals(redisTemplate.hasKey(friendCacheKey))){
+      setFriendCache(friendCacheKey, userId);
+    }
+    totalDTO.setFriendTotal(redisTemplate.opsForZSet().zCard(friendCacheKey));
+    return totalDTO;
+  }
+
+  public Long queryCache(String cacheKey, List<AttentionUserDTO> list, Long start,
+                         Long end, AttentionType type, String key) {
+    Long total = redisTemplate.opsForZSet().zCard(cacheKey);
+    Set<Object> set = redisTemplate.opsForZSet()
+        .reverseRange(cacheKey, start, end);
+    if (set != null && set.size() > 0) {
+      List<Long> userIds = set.stream().map(e -> (Long) e).collect(Collectors.toList());
+      Map<Long, UserDTO> map = userService.queryUserByIds(userIds);
+      set.forEach(e -> {
+        AttentionUserDTO attentionUserDTO = new AttentionUserDTO();
+        attentionUserDTO.setUserDTO(map.get((Long)e));
+        switch (type){
+          case ATTENTION:
+            Long attention = redisTemplate.opsForZSet().rank(key, e);
+            if(attention == null){
+              //对方未关注
+              attentionUserDTO.setFlag(0);
+            } else {
+              attentionUserDTO.setFlag(2);
+            }
+            break;
+          case FANS:
+            Long fans = redisTemplate.opsForZSet().rank(key, e);
+            if(fans == null){
+              //自己未关注
+              attentionUserDTO.setFlag(1);
+            } else {
+              attentionUserDTO.setFlag(2);
+            }
+            break;
+          case FRIEND:
+            attentionUserDTO.setFlag(2);
+            break;
+        }
+        list.add(attentionUserDTO);
+      });
+    }
+    return total;
+  }
+
+  public void fans(Long userId, Long fansUserId, boolean b) {
+    String cacheKey = getFansCacheKey(userId);
+    if (Boolean.TRUE.equals(redisTemplate.hasKey(cacheKey))) {
+      if (b) {
+        redisTemplate.opsForZSet()
+            .add(cacheKey, fansUserId, System.currentTimeMillis());
+      } else {
+        redisTemplate.opsForZSet().remove(cacheKey, fansUserId);
+      }
+      return;
+    }
+    setFansCache(cacheKey, userId);
+  }
+
+  public void setFansCache(String cacheKey, Long userId){
+    LambdaQueryWrapper<Attention> lwq = Wrappers.lambdaQuery();
+    lwq.eq(Attention::getTarget, userId);
+    lwq.orderByDesc(Attention::getCreateTime);
+    List<Attention> list = attentionMapper.selectList(lwq);
+    if (list != null && list.size() > 0) {
+      list.forEach(e -> {
+        LocalDateTime createTime = e.getCreateTime();
+        long milliseconds = createTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
+        redisTemplate.opsForZSet().add(cacheKey, e.getUserId(), milliseconds);
+      });
+    }
+    redisTemplate.expire(cacheKey, 60 * 60 * 24 * 7, TimeUnit.SECONDS);
+  }
+
+  public boolean attention(Long userId, Long targetUserId) {
+    String cacheKey = getAttentionCacheKey(userId);
+    if (Boolean.TRUE.equals(redisTemplate.hasKey(cacheKey))) {
+      return attentionOperate(cacheKey, userId, targetUserId);
+    }
+    setAttentionCache(cacheKey, userId);
+    return attentionOperate(cacheKey, userId, targetUserId);
+  }
+
+  public void setAttentionCache(String cacheKey, Long userId){
+    LambdaQueryWrapper<Attention> lwq = Wrappers.lambdaQuery();
+    lwq.eq(Attention::getUserId, userId);
+    lwq.orderByDesc(Attention::getCreateTime);
+    List<Attention> list = attentionMapper.selectList(lwq);
+    if (list != null && list.size() > 0) {
+      list.forEach(e -> {
+        LocalDateTime createTime = e.getCreateTime();
+        long milliseconds = createTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
+        redisTemplate.opsForZSet().add(cacheKey, e.getTarget(), milliseconds);
+      });
+    }
+    redisTemplate.expire(cacheKey, 60 * 60 * 24 * 7, TimeUnit.SECONDS);
+  }
+
+  public boolean attentionOperate(String cacheKey, Long userId, Long targetUserId) {
+    Boolean b = redisTemplate.opsForZSet()
+        .add(cacheKey, targetUserId, System.currentTimeMillis());
+    if (Boolean.FALSE.equals(b)) {
+      LambdaQueryWrapper<Attention> lwq = Wrappers.lambdaQuery();
+      lwq.eq(Attention::getUserId, userId);
+      lwq.eq(Attention::getTarget, targetUserId);
+      attentionMapper.delete(lwq);
+      redisTemplate.opsForZSet().remove(cacheKey, targetUserId);
+      return false;
+    }
+    Attention entity = new Attention();
+    entity.setUserId(userId);
+    entity.setTarget(targetUserId);
+    entity.setCreateTime(LocalDateTime.now());
+    attentionMapper.insert(entity);
+    return true;
+  }
+
+  private String getAttentionCacheKey(Long userId) {
+    return ATTENTION_CACHE_KEY.replace("${userId}", String.valueOf(userId));
+  }
+
+  private String getTempFriendCacheKey(Long userId) {
+    return TEMP_FRIEND_CACHE_KEY.replace("${userId}", String.valueOf(userId));
+  }
+
+  private String getFansCacheKey(Long userId) {
+    return FANS_CACHE_KEY.replace("${userId}", String.valueOf(userId));
+  }
+
+  private String getFriendCacheKey(Long userId) {
+    return FRIEND_CACHE_KEY.replace("${userId}", String.valueOf(userId));
+  }
+}

+ 3 - 3
vehicle-server/src/main/java/com/chelvc/cloud/vehicle/server/service/impl/DynamicContentServiceImpl.java

@@ -102,10 +102,10 @@ public class DynamicContentServiceImpl implements DynamicContentService, com.che
     IPage<DynamicContentDTO> result = null;
     if (Objects.equals(DynamicType.HOTTEST.getValue(), type)) {
       result = baseMapper.queryHot(page, queryHotWrapper(keyWord, userId));
-    } /*else if (Objects.equals(DynamicType.ATTENTION.getValue(), type)) {
+    } else if (Objects.equals(DynamicType.ATTENTION.getValue(), type)) {
       result = baseMapper
           .selectAttention(page, queryAttentionWrapper(keyWord), userId);
-    } */else if (Objects.equals(DynamicType.LATEST.getValue(), type)) {
+    } else if (Objects.equals(DynamicType.LATEST.getValue(), type)) {
       result = baseMapper
           .queryAll(page, queryAllWrapper(keyWord, userId));
     } else if (Objects.equals(DynamicType.DISCOVER.getValue(), type)) {
@@ -162,7 +162,7 @@ public class DynamicContentServiceImpl implements DynamicContentService, com.che
     lqw.like(StringUtils.isNotBlank(keyWord), "t1.text_content", keyWord);
     lqw.eq("t1.status", 0);
     lqw.inSql("t1.user_id",
-        "SELECT t2.target FROM collision_user.user_attention t2 WHERE t2.user_id = #{userId}");
+        "SELECT t2.target FROM attention t2 WHERE t2.user_id = #{userId}");
     lqw.orderByDesc("t1.create_time","t1.id");
     return lqw;
   }