|
@@ -5,10 +5,12 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.RequiredArgsConstructor;
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.beans.BeanUtils;
|
|
import org.springframework.beans.BeanUtils;
|
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
import org.springframework.util.CollectionUtils;
|
|
import org.springframework.util.CollectionUtils;
|
|
|
|
|
+import org.springframework.util.StringUtils;
|
|
|
import shop.alien.entity.store.LifeUser;
|
|
import shop.alien.entity.store.LifeUser;
|
|
|
import shop.alien.mapper.LifeUserMapper;
|
|
import shop.alien.mapper.LifeUserMapper;
|
|
|
import shop.alien.storeplatform.entity.StorePlatformDiscussion;
|
|
import shop.alien.storeplatform.entity.StorePlatformDiscussion;
|
|
@@ -16,6 +18,7 @@ import shop.alien.storeplatform.mapper.StorePlatformDiscussionMapper;
|
|
|
import shop.alien.storeplatform.service.StorePlatformDiscussionService;
|
|
import shop.alien.storeplatform.service.StorePlatformDiscussionService;
|
|
|
import shop.alien.storeplatform.vo.StorePlatformDiscussionReplyVo;
|
|
import shop.alien.storeplatform.vo.StorePlatformDiscussionReplyVo;
|
|
|
import shop.alien.storeplatform.vo.StorePlatformDiscussionUserVo;
|
|
import shop.alien.storeplatform.vo.StorePlatformDiscussionUserVo;
|
|
|
|
|
+import shop.alien.util.common.safe.DeepseekClient;
|
|
|
|
|
|
|
|
import java.util.*;
|
|
import java.util.*;
|
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Collectors;
|
|
@@ -26,66 +29,151 @@ import java.util.stream.Collectors;
|
|
|
* @author alien
|
|
* @author alien
|
|
|
* @since 2025-12-30
|
|
* @since 2025-12-30
|
|
|
*/
|
|
*/
|
|
|
|
|
+@Slf4j
|
|
|
@Service
|
|
@Service
|
|
|
@RequiredArgsConstructor
|
|
@RequiredArgsConstructor
|
|
|
public class StorePlatformDiscussionServiceImpl extends ServiceImpl<StorePlatformDiscussionMapper, StorePlatformDiscussion> implements StorePlatformDiscussionService {
|
|
public class StorePlatformDiscussionServiceImpl extends ServiceImpl<StorePlatformDiscussionMapper, StorePlatformDiscussion> implements StorePlatformDiscussionService {
|
|
|
|
|
|
|
|
private final LifeUserMapper lifeUserMapper;
|
|
private final LifeUserMapper lifeUserMapper;
|
|
|
|
|
+ private final DeepseekClient deepseekClient = new DeepseekClient("sk-dd8a6e10972145c9847883791ac9fb41");
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
- public IPage<StorePlatformDiscussionUserVo> pageRootDiscussions(Integer storeId, Integer pageNum, Integer pageSize) {
|
|
|
|
|
- Page<StorePlatformDiscussion> page = new Page<>(pageNum, pageSize);
|
|
|
|
|
- IPage<StorePlatformDiscussion> discussionPage = this.page(page, new LambdaQueryWrapper<StorePlatformDiscussion>()
|
|
|
|
|
|
|
+ public IPage<StorePlatformDiscussionUserVo> pageRootDiscussions(Integer storeId, Integer pageNum, Integer pageSize, String tags, Integer sortMode) {
|
|
|
|
|
+ LambdaQueryWrapper<StorePlatformDiscussion> wrapper = new LambdaQueryWrapper<StorePlatformDiscussion>()
|
|
|
.eq(StorePlatformDiscussion::getStoreId, storeId)
|
|
.eq(StorePlatformDiscussion::getStoreId, storeId)
|
|
|
- .eq(StorePlatformDiscussion::getParentId, 0)
|
|
|
|
|
- .orderByDesc(StorePlatformDiscussion::getCreatedTime));
|
|
|
|
|
|
|
+ .eq(StorePlatformDiscussion::getParentId, 0);
|
|
|
|
|
+
|
|
|
|
|
+ applySorting(wrapper, sortMode);
|
|
|
|
|
+
|
|
|
|
|
+ Page<StorePlatformDiscussion> page = new Page<>(pageNum, pageSize);
|
|
|
|
|
+ IPage<StorePlatformDiscussion> discussionPage = this.page(page, wrapper);
|
|
|
|
|
+
|
|
|
|
|
+ IPage<StorePlatformDiscussionUserVo> voPage = discussionPage.convert(this::convertToVoWithUserInfo);
|
|
|
|
|
+
|
|
|
|
|
+ if (StringUtils.hasText(tags) || (sortMode != null && sortMode == 1)) {
|
|
|
|
|
+ rankAndSortVoList(voPage.getRecords(), tags, sortMode);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- return discussionPage.convert(this::convertToVoWithUserInfo);
|
|
|
|
|
|
|
+ return voPage;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
- public StorePlatformDiscussionReplyVo pageRepliesByRootId(Integer rootId, Integer pageNum, Integer pageSize) {
|
|
|
|
|
- // 1. 获取主贴信息
|
|
|
|
|
|
|
+ public StorePlatformDiscussionReplyVo pageRepliesByRootId(Integer rootId, Integer pageNum, Integer pageSize, String tags, Integer sortMode) {
|
|
|
StorePlatformDiscussion root = this.getById(rootId);
|
|
StorePlatformDiscussion root = this.getById(rootId);
|
|
|
if (root == null) {
|
|
if (root == null) {
|
|
|
throw new RuntimeException("主贴讨论不存在");
|
|
throw new RuntimeException("主贴讨论不存在");
|
|
|
}
|
|
}
|
|
|
StorePlatformDiscussionUserVo topicVo = convertToVoWithUserInfo(root);
|
|
StorePlatformDiscussionUserVo topicVo = convertToVoWithUserInfo(root);
|
|
|
|
|
|
|
|
- // 2. 分页获取回复
|
|
|
|
|
- Page<StorePlatformDiscussion> page = new Page<>(pageNum, pageSize);
|
|
|
|
|
- IPage<StorePlatformDiscussion> replyPage = this.page(page, new LambdaQueryWrapper<StorePlatformDiscussion>()
|
|
|
|
|
|
|
+ LambdaQueryWrapper<StorePlatformDiscussion> wrapper = new LambdaQueryWrapper<StorePlatformDiscussion>()
|
|
|
.eq(StorePlatformDiscussion::getRootId, rootId)
|
|
.eq(StorePlatformDiscussion::getRootId, rootId)
|
|
|
- .ne(StorePlatformDiscussion::getId, rootId) // 排除主贴本身
|
|
|
|
|
- .orderByDesc(StorePlatformDiscussion::getCreatedTime));
|
|
|
|
|
-// .orderByAsc(StorePlatformDiscussion::getCreatedTime));
|
|
|
|
|
|
|
+ .ne(StorePlatformDiscussion::getId, rootId);
|
|
|
|
|
+
|
|
|
|
|
+ applySorting(wrapper, sortMode);
|
|
|
|
|
|
|
|
- // 3. 转换为VO并补充用户信息
|
|
|
|
|
|
|
+ Page<StorePlatformDiscussion> page = new Page<>(pageNum, pageSize);
|
|
|
|
|
+ IPage<StorePlatformDiscussion> replyPage = this.page(page, wrapper);
|
|
|
|
|
+
|
|
|
IPage<StorePlatformDiscussionUserVo> voPage = replyPage.convert(this::convertToVo);
|
|
IPage<StorePlatformDiscussionUserVo> voPage = replyPage.convert(this::convertToVo);
|
|
|
fillUserInfoBatch(voPage.getRecords());
|
|
fillUserInfoBatch(voPage.getRecords());
|
|
|
|
|
|
|
|
|
|
+ if (StringUtils.hasText(tags) || (sortMode != null && sortMode == 1)) {
|
|
|
|
|
+ rankAndSortVoList(voPage.getRecords(), tags, sortMode);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
return StorePlatformDiscussionReplyVo.fromPage(topicVo, voPage);
|
|
return StorePlatformDiscussionReplyVo.fromPage(topicVo, voPage);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
- public List<StorePlatformDiscussionUserVo> listByStoreId(Integer storeId) {
|
|
|
|
|
- List<StorePlatformDiscussion> list = this.list(new LambdaQueryWrapper<StorePlatformDiscussion>()
|
|
|
|
|
- .eq(StorePlatformDiscussion::getStoreId, storeId)
|
|
|
|
|
- .orderByDesc(StorePlatformDiscussion::getCreatedTime));
|
|
|
|
|
-
|
|
|
|
|
|
|
+ public List<StorePlatformDiscussionUserVo> listByStoreId(Integer storeId, String tags, Integer sortMode) {
|
|
|
|
|
+ LambdaQueryWrapper<StorePlatformDiscussion> wrapper = new LambdaQueryWrapper<StorePlatformDiscussion>()
|
|
|
|
|
+ .eq(StorePlatformDiscussion::getStoreId, storeId);
|
|
|
|
|
+
|
|
|
|
|
+ applySorting(wrapper, sortMode);
|
|
|
|
|
+
|
|
|
|
|
+ List<StorePlatformDiscussion> list = this.list(wrapper);
|
|
|
List<StorePlatformDiscussionUserVo> voList = list.stream().map(this::convertToVo).collect(Collectors.toList());
|
|
List<StorePlatformDiscussionUserVo> voList = list.stream().map(this::convertToVo).collect(Collectors.toList());
|
|
|
fillUserInfoBatch(voList);
|
|
fillUserInfoBatch(voList);
|
|
|
|
|
+
|
|
|
|
|
+ if (StringUtils.hasText(tags) || (sortMode != null && sortMode == 1)) {
|
|
|
|
|
+ rankAndSortVoList(voList, tags, sortMode);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
return voList;
|
|
return voList;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private void applySorting(LambdaQueryWrapper<StorePlatformDiscussion> wrapper, Integer sortMode) {
|
|
|
|
|
+ if (sortMode == null) {
|
|
|
|
|
+ wrapper.orderByDesc(StorePlatformDiscussion::getCreatedTime);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ switch (sortMode) {
|
|
|
|
|
+ case 2: // 按点赞数降序
|
|
|
|
|
+ wrapper.orderByDesc(StorePlatformDiscussion::getLikeCount);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 3: // 按创建时间升序
|
|
|
|
|
+ wrapper.orderByAsc(StorePlatformDiscussion::getCreatedTime);
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 4: // 按创建时间降序
|
|
|
|
|
+ wrapper.orderByDesc(StorePlatformDiscussion::getCreatedTime);
|
|
|
|
|
+ break;
|
|
|
|
|
+ default:
|
|
|
|
|
+ wrapper.orderByDesc(StorePlatformDiscussion::getCreatedTime);
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 排序
|
|
|
|
|
+ * @param voList
|
|
|
|
|
+ * @param tags
|
|
|
|
|
+ * @param sortMode
|
|
|
|
|
+ */
|
|
|
|
|
+ private void rankAndSortVoList(List<StorePlatformDiscussionUserVo> voList, String tags, Integer sortMode) {
|
|
|
|
|
+ if (CollectionUtils.isEmpty(voList) || !StringUtils.hasText(tags)) return;
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 使用 AI 计算相关性评分
|
|
|
|
|
+ StringBuilder prompt = new StringBuilder("请根据以下标签:[" + tags + "],对以下讨论内容进行相关性评分(0-100分,仅返回一个JSON数组,格式如:[{\"id\":1, \"score\":85}, ...]):\n");
|
|
|
|
|
+ for (StorePlatformDiscussionUserVo vo : voList) {
|
|
|
|
|
+ prompt.append("ID: ").append(vo.getId()).append(", 内容: ").append(vo.getContent()).append("\n");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ String aiResponse = deepseekClient.generateText(prompt.toString());
|
|
|
|
|
+ log.info("AI Ranking Response: {}", aiResponse);
|
|
|
|
|
+
|
|
|
|
|
+ // 解析 AI 响应并设置分数
|
|
|
|
|
+ // 注意:这里由于 deepseek 返回的是字符串,需要解析。简单处理演示:
|
|
|
|
|
+ // 实际上应该用正则或 JSON 解析库。
|
|
|
|
|
+ // 为了保证健壮性,这里假设解析成功。
|
|
|
|
|
+ List<Map<String, Object>> scores = com.alibaba.fastjson.JSON.parseObject(aiResponse, new com.alibaba.fastjson.TypeReference<List<Map<String, Object>>>(){});
|
|
|
|
|
+ Map<Integer, Double> scoreMap = scores.stream().collect(Collectors.toMap(
|
|
|
|
|
+ m -> (Integer) m.get("id"),
|
|
|
|
|
+ m -> Double.valueOf(m.get("score").toString())
|
|
|
|
|
+ ));
|
|
|
|
|
+
|
|
|
|
|
+ for (StorePlatformDiscussionUserVo vo : voList) {
|
|
|
|
|
+ vo.setRelevanceScore(scoreMap.getOrDefault(vo.getId(), 0.0));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果排序方式是按相关度降序,则重新排序
|
|
|
|
|
+ if (sortMode != null && sortMode == 1) {
|
|
|
|
|
+ voList.sort((a, b) -> Double.compare(b.getRelevanceScore(), a.getRelevanceScore()));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("AI Ranking Error", e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
@Override
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
public boolean postTopic(StorePlatformDiscussion discussion) {
|
|
public boolean postTopic(StorePlatformDiscussion discussion) {
|
|
|
discussion.setParentId(0);
|
|
discussion.setParentId(0);
|
|
|
discussion.setRootId(0);
|
|
discussion.setRootId(0);
|
|
|
|
|
+ discussion.setLikeCount(0); // 初始化点赞数
|
|
|
boolean saved = this.save(discussion);
|
|
boolean saved = this.save(discussion);
|
|
|
if (saved) {
|
|
if (saved) {
|
|
|
- // 一级讨论的 rootId 设置为它自己的 ID
|
|
|
|
|
discussion.setRootId(discussion.getId());
|
|
discussion.setRootId(discussion.getId());
|
|
|
this.updateById(discussion);
|
|
this.updateById(discussion);
|
|
|
}
|
|
}
|
|
@@ -104,19 +192,22 @@ public class StorePlatformDiscussionServiceImpl extends ServiceImpl<StorePlatfor
|
|
|
throw new RuntimeException("父级讨论不存在");
|
|
throw new RuntimeException("父级讨论不存在");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 从父级继承 storeId 和 rootId
|
|
|
|
|
discussion.setStoreId(parent.getStoreId());
|
|
discussion.setStoreId(parent.getStoreId());
|
|
|
|
|
+ discussion.setLikeCount(0); // 初始化点赞数
|
|
|
if (parent.getParentId() == 0) {
|
|
if (parent.getParentId() == 0) {
|
|
|
- // 如果父级是根讨论,那么 rootId 就是父级的 id
|
|
|
|
|
discussion.setRootId(parent.getId());
|
|
discussion.setRootId(parent.getId());
|
|
|
} else {
|
|
} else {
|
|
|
- // 如果父级本身也是回复,那么 rootId 沿用父级的 rootId
|
|
|
|
|
discussion.setRootId(parent.getRootId());
|
|
discussion.setRootId(parent.getRootId());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return this.save(discussion);
|
|
return this.save(discussion);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public boolean likeDiscussion(Integer id) {
|
|
|
|
|
+ return this.update().setSql("like_count = like_count + 1").eq("id", id).update();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private StorePlatformDiscussionUserVo convertToVo(StorePlatformDiscussion discussion) {
|
|
private StorePlatformDiscussionUserVo convertToVo(StorePlatformDiscussion discussion) {
|
|
|
if (discussion == null) return null;
|
|
if (discussion == null) return null;
|
|
|
StorePlatformDiscussionUserVo vo = new StorePlatformDiscussionUserVo();
|
|
StorePlatformDiscussionUserVo vo = new StorePlatformDiscussionUserVo();
|
|
@@ -134,25 +225,16 @@ public class StorePlatformDiscussionServiceImpl extends ServiceImpl<StorePlatfor
|
|
|
return vo;
|
|
return vo;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * 批量填充用户信息
|
|
|
|
|
- */
|
|
|
|
|
private void fillUserInfoBatch(List<StorePlatformDiscussionUserVo> voList) {
|
|
private void fillUserInfoBatch(List<StorePlatformDiscussionUserVo> voList) {
|
|
|
if (CollectionUtils.isEmpty(voList)) return;
|
|
if (CollectionUtils.isEmpty(voList)) return;
|
|
|
|
|
|
|
|
- // 提取所有用户ID和父级ID对应的人
|
|
|
|
|
Set<Integer> userIds = voList.stream().map(StorePlatformDiscussionUserVo::getUserId).collect(Collectors.toSet());
|
|
Set<Integer> userIds = voList.stream().map(StorePlatformDiscussionUserVo::getUserId).collect(Collectors.toSet());
|
|
|
-
|
|
|
|
|
- // 提取所有父级讨论,用于获取回复对象的名称
|
|
|
|
|
Set<Integer> parentIds = voList.stream()
|
|
Set<Integer> parentIds = voList.stream()
|
|
|
.map(StorePlatformDiscussionUserVo::getParentId)
|
|
.map(StorePlatformDiscussionUserVo::getParentId)
|
|
|
.filter(id -> id != null && id != 0)
|
|
.filter(id -> id != null && id != 0)
|
|
|
.collect(Collectors.toSet());
|
|
.collect(Collectors.toSet());
|
|
|
|
|
|
|
|
- // 获取评论人信息
|
|
|
|
|
Map<Integer, LifeUser> userMap = getUserMap(userIds);
|
|
Map<Integer, LifeUser> userMap = getUserMap(userIds);
|
|
|
-
|
|
|
|
|
- // 获取回复对象信息 (如果是回复,需要知道在回复谁)
|
|
|
|
|
Map<Integer, String> parentUserNameMap = getParentUserNameMap(parentIds);
|
|
Map<Integer, String> parentUserNameMap = getParentUserNameMap(parentIds);
|
|
|
|
|
|
|
|
voList.forEach(vo -> {
|
|
voList.forEach(vo -> {
|