|
@@ -1,16 +1,24 @@
|
|
|
package com.etotem.cfc.service;
|
|
package com.etotem.cfc.service;
|
|
|
|
|
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
|
|
import com.etotem.cfc.common.Result;
|
|
import com.etotem.cfc.common.Result;
|
|
|
import com.etotem.cfc.entity.Contact;
|
|
import com.etotem.cfc.entity.Contact;
|
|
|
import com.etotem.cfc.entity.ContactHelpLog;
|
|
import com.etotem.cfc.entity.ContactHelpLog;
|
|
|
|
|
+import com.etotem.cfc.entity.PearlInteractionLog;
|
|
|
|
|
+import com.etotem.cfc.entity.PearlInteractionReminder;
|
|
|
import com.etotem.cfc.entity.ResourceItem;
|
|
import com.etotem.cfc.entity.ResourceItem;
|
|
|
|
|
+import com.etotem.cfc.enums.ConnectionType;
|
|
|
import com.etotem.cfc.mapper.ContactHelpLogMapper;
|
|
import com.etotem.cfc.mapper.ContactHelpLogMapper;
|
|
|
import com.etotem.cfc.mapper.ContactMapper;
|
|
import com.etotem.cfc.mapper.ContactMapper;
|
|
|
|
|
+import com.etotem.cfc.mapper.PearlInteractionLogMapper;
|
|
|
|
|
+import com.etotem.cfc.mapper.PearlInteractionReminderMapper;
|
|
|
import com.etotem.cfc.mapper.ResourceItemMapper;
|
|
import com.etotem.cfc.mapper.ResourceItemMapper;
|
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
import javax.annotation.Resource;
|
|
|
|
|
+import java.time.Instant;
|
|
|
|
|
+import java.time.temporal.ChronoUnit;
|
|
|
import java.util.ArrayList;
|
|
import java.util.ArrayList;
|
|
|
import java.util.Arrays;
|
|
import java.util.Arrays;
|
|
|
import java.util.Date;
|
|
import java.util.Date;
|
|
@@ -34,40 +42,55 @@ public class ResourceService {
|
|
|
@Resource
|
|
@Resource
|
|
|
private ContactHelpLogMapper helpLogMapper;
|
|
private ContactHelpLogMapper helpLogMapper;
|
|
|
|
|
|
|
|
- /** 珍珠图聚合:人(contacts) + 技能(help_logs SKILL) + 信息/场所(resource_items) */
|
|
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private PearlInteractionLogMapper interactionLogMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private PearlInteractionReminderMapper reminderMapper;
|
|
|
|
|
+
|
|
|
|
|
+ /** 25 类连接类型 → 前端选择器/渲染配置 */
|
|
|
|
|
+ public List<Map<String, Object>> getConnectionTypes() {
|
|
|
|
|
+ List<Map<String, Object>> list = new ArrayList<>();
|
|
|
|
|
+ for (ConnectionType t : ConnectionType.values()) {
|
|
|
|
|
+ Map<String, Object> m = new LinkedHashMap<>();
|
|
|
|
|
+ m.put("code", t.name());
|
|
|
|
|
+ m.put("label", t.getLabel());
|
|
|
|
|
+ m.put("category", t.getCategory());
|
|
|
|
|
+ m.put("suggestIntervalMonth", t.getSuggestIntervalMonth());
|
|
|
|
|
+ list.add(m);
|
|
|
|
|
+ }
|
|
|
|
|
+ return list;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** 珍珠图聚合:25 类社会连接 + 历史资源(legacy PERSON/SKILL/INFO/PLACE) */
|
|
|
public Result<Map<String, Object>> getPearlResources(Long userId) {
|
|
public Result<Map<String, Object>> getPearlResources(Long userId) {
|
|
|
- // 1. 人脉:全部联系人
|
|
|
|
|
|
|
+ // 历史资源:legacy 分组的 contacts(人脉)与 skillLogs(技能)照旧
|
|
|
List<Contact> contacts = contactMapper.selectList(
|
|
List<Contact> contacts = contactMapper.selectList(
|
|
|
new LambdaQueryWrapper<Contact>().eq(Contact::getUserId, userId)
|
|
new LambdaQueryWrapper<Contact>().eq(Contact::getUserId, userId)
|
|
|
);
|
|
);
|
|
|
- List<Map<String, Object>> personItems = contacts.stream().map(c -> {
|
|
|
|
|
- Map<String, Object> m = new LinkedHashMap<>();
|
|
|
|
|
- m.put("id", c.getId());
|
|
|
|
|
- m.put("name", c.getName());
|
|
|
|
|
- m.put("avatar", c.getAvatar());
|
|
|
|
|
- m.put("relationshipType", c.getRelationshipType());
|
|
|
|
|
- m.put("description", c.getRelationshipType());
|
|
|
|
|
- return m;
|
|
|
|
|
- }).collect(Collectors.toList());
|
|
|
|
|
-
|
|
|
|
|
- // 2. 技能:SKILL 类帮助记录,按联系人聚合
|
|
|
|
|
List<ContactHelpLog> skillLogs = helpLogMapper.selectList(
|
|
List<ContactHelpLog> skillLogs = helpLogMapper.selectList(
|
|
|
new LambdaQueryWrapper<ContactHelpLog>()
|
|
new LambdaQueryWrapper<ContactHelpLog>()
|
|
|
.eq(ContactHelpLog::getUserId, userId)
|
|
.eq(ContactHelpLog::getUserId, userId)
|
|
|
.eq(ContactHelpLog::getHelpType, "SKILL")
|
|
.eq(ContactHelpLog::getHelpType, "SKILL")
|
|
|
.orderByDesc(ContactHelpLog::getHappenedAt)
|
|
.orderByDesc(ContactHelpLog::getHappenedAt)
|
|
|
);
|
|
);
|
|
|
- List<Map<String, Object>> skillItems = buildSkillItems(skillLogs);
|
|
|
|
|
-
|
|
|
|
|
- // 3+4. 信息/场所:resource_items 登记
|
|
|
|
|
List<ResourceItem> resourceItems = resourceItemMapper.selectList(
|
|
List<ResourceItem> resourceItems = resourceItemMapper.selectList(
|
|
|
new LambdaQueryWrapper<ResourceItem>()
|
|
new LambdaQueryWrapper<ResourceItem>()
|
|
|
.eq(ResourceItem::getUserId, userId)
|
|
.eq(ResourceItem::getUserId, userId)
|
|
|
- .in(ResourceItem::getType, Arrays.asList("INFO", "PLACE", "SKILL"))
|
|
|
|
|
.orderByDesc(ResourceItem::getCreatedAt)
|
|
.orderByDesc(ResourceItem::getCreatedAt)
|
|
|
);
|
|
);
|
|
|
- List<Map<String, Object>> infoItems = new ArrayList<>();
|
|
|
|
|
- List<Map<String, Object>> placeItems = new ArrayList<>();
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 25 组社会连接:按 connection_type 分组
|
|
|
|
|
+ Map<String, List<Map<String, Object>>> connGroupMap = new LinkedHashMap<>();
|
|
|
|
|
+ Map<String, String> connTypeLabel = new LinkedHashMap<>();
|
|
|
|
|
+ for (ConnectionType t : ConnectionType.values()) {
|
|
|
|
|
+ connGroupMap.put(t.name(), new ArrayList<>());
|
|
|
|
|
+ connTypeLabel.put(t.name(), t.getLabel());
|
|
|
|
|
+ }
|
|
|
|
|
+ // 兼容历史数据:connection_type 为空的按原 type 归入 INFO/PLACE legacy
|
|
|
|
|
+ List<Map<String, Object>> legacyInfo = new ArrayList<>();
|
|
|
|
|
+ List<Map<String, Object>> legacyPlace = new ArrayList<>();
|
|
|
|
|
+
|
|
|
for (ResourceItem item : resourceItems) {
|
|
for (ResourceItem item : resourceItems) {
|
|
|
Map<String, Object> m = new LinkedHashMap<>();
|
|
Map<String, Object> m = new LinkedHashMap<>();
|
|
|
m.put("id", item.getId());
|
|
m.put("id", item.getId());
|
|
@@ -75,29 +98,67 @@ public class ResourceService {
|
|
|
m.put("description", item.getDescription());
|
|
m.put("description", item.getDescription());
|
|
|
m.put("contactId", item.getContactId());
|
|
m.put("contactId", item.getContactId());
|
|
|
m.put("createdAt", item.getCreatedAt());
|
|
m.put("createdAt", item.getCreatedAt());
|
|
|
- if ("PLACE".equals(item.getType())) {
|
|
|
|
|
- placeItems.add(m);
|
|
|
|
|
|
|
+ m.put("valueScore", item.getValueScore() != null ? item.getValueScore() : 0);
|
|
|
|
|
+ m.put("priority", item.getPriority() != null ? item.getPriority() : 2);
|
|
|
|
|
+ m.put("interactionCount", item.getInteractionCount() != null ? item.getInteractionCount() : 0);
|
|
|
|
|
+ m.put("lastInteractionAt", item.getLastInteractionAt());
|
|
|
|
|
+ if (item.getLastInteractionAt() != null) {
|
|
|
|
|
+ long days = ChronoUnit.DAYS.between(
|
|
|
|
|
+ item.getLastInteractionAt().toInstant(), Instant.now());
|
|
|
|
|
+ m.put("daysSinceLast", (int) Math.max(0, days));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ m.put("daysSinceLast", null);
|
|
|
|
|
+ }
|
|
|
|
|
+ String ct = item.getConnectionType();
|
|
|
|
|
+ if (ct != null && !ct.isEmpty() && connGroupMap.containsKey(ct)) {
|
|
|
|
|
+ connGroupMap.get(ct).add(m);
|
|
|
} else {
|
|
} else {
|
|
|
- infoItems.add(m);
|
|
|
|
|
|
|
+ // 历史资源
|
|
|
|
|
+ if ("PLACE".equals(item.getType())) {
|
|
|
|
|
+ legacyPlace.add(m);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ legacyInfo.add(m);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 组装 25 组(按枚举顺序)
|
|
|
List<Map<String, Object>> groups = new ArrayList<>();
|
|
List<Map<String, Object>> groups = new ArrayList<>();
|
|
|
- groups.add(group("PERSON", "人脉", personItems));
|
|
|
|
|
- groups.add(group("SKILL", "技能", skillItems));
|
|
|
|
|
- groups.add(group("INFO", "信息", infoItems));
|
|
|
|
|
- groups.add(group("PLACE", "场所", placeItems));
|
|
|
|
|
|
|
+ for (ConnectionType t : ConnectionType.values()) {
|
|
|
|
|
+ Map<String, Object> g = new LinkedHashMap<>();
|
|
|
|
|
+ g.put("type", t.name());
|
|
|
|
|
+ g.put("typeName", t.getLabel());
|
|
|
|
|
+ g.put("category", t.getCategory());
|
|
|
|
|
+ g.put("ring", "ESSENTIAL".equals(t.getCategory()) ? "inner" : "outer");
|
|
|
|
|
+ g.put("items", connGroupMap.get(t.name()));
|
|
|
|
|
+ groups.add(g);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // legacyGroups:人脉/技能/历史信息/历史场所
|
|
|
|
|
+ List<Map<String, Object>> legacyGroups = new ArrayList<>();
|
|
|
|
|
+ legacyGroups.add(group("PERSON", "人脉", contacts.stream().map(c -> {
|
|
|
|
|
+ Map<String, Object> m = new LinkedHashMap<>();
|
|
|
|
|
+ m.put("id", c.getId());
|
|
|
|
|
+ m.put("name", c.getName());
|
|
|
|
|
+ m.put("avatar", c.getAvatar());
|
|
|
|
|
+ m.put("relationshipType", c.getRelationshipType());
|
|
|
|
|
+ return m;
|
|
|
|
|
+ }).collect(Collectors.toList())));
|
|
|
|
|
+ legacyGroups.add(group("SKILL", "技能", buildSkillItems(skillLogs)));
|
|
|
|
|
+ legacyGroups.add(group("INFO", "信息", legacyInfo));
|
|
|
|
|
+ legacyGroups.add(group("PLACE", "场所", legacyPlace));
|
|
|
|
|
|
|
|
Map<String, Object> result = new LinkedHashMap<>();
|
|
Map<String, Object> result = new LinkedHashMap<>();
|
|
|
result.put("groups", groups);
|
|
result.put("groups", groups);
|
|
|
|
|
+ result.put("legacyGroups", legacyGroups);
|
|
|
return Result.success(result);
|
|
return Result.success(result);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /** 登记资源(珍珠图:信息/场所/技能类手工登记) */
|
|
|
|
|
- public Result<ResourceItem> addResourceItem(Long userId, String type, String name,
|
|
|
|
|
- String description, Long contactId) {
|
|
|
|
|
- if (type == null || !Arrays.asList("INFO", "PLACE", "SKILL").contains(type)) {
|
|
|
|
|
- return Result.error("资源类型不合法");
|
|
|
|
|
|
|
+ /** 登记资源(珍珠图:25 类社会连接,connectionType 必填) */
|
|
|
|
|
+ public Result<ResourceItem> addResourceItem(Long userId, String type, String connectionType,
|
|
|
|
|
+ String name, String description, Long contactId) {
|
|
|
|
|
+ if (connectionType == null || !ConnectionType.isValid(connectionType)) {
|
|
|
|
|
+ return Result.error("请选择有效的连接类型");
|
|
|
}
|
|
}
|
|
|
if (name == null || name.trim().isEmpty()) {
|
|
if (name == null || name.trim().isEmpty()) {
|
|
|
return Result.error("资源名称不能为空");
|
|
return Result.error("资源名称不能为空");
|
|
@@ -111,10 +172,14 @@ public class ResourceService {
|
|
|
|
|
|
|
|
ResourceItem item = new ResourceItem();
|
|
ResourceItem item = new ResourceItem();
|
|
|
item.setUserId(userId);
|
|
item.setUserId(userId);
|
|
|
- item.setType(type);
|
|
|
|
|
|
|
+ item.setType(type != null && !type.isEmpty() ? type : connectionType);
|
|
|
|
|
+ item.setConnectionType(connectionType);
|
|
|
|
|
+ item.setPriority(2);
|
|
|
|
|
+ item.setValueScore(50);
|
|
|
item.setName(name.trim());
|
|
item.setName(name.trim());
|
|
|
item.setDescription(description != null ? description.trim() : null);
|
|
item.setDescription(description != null ? description.trim() : null);
|
|
|
item.setContactId(contactId);
|
|
item.setContactId(contactId);
|
|
|
|
|
+ item.setInteractionCount(0);
|
|
|
item.setCreatedAt(new Date());
|
|
item.setCreatedAt(new Date());
|
|
|
resourceItemMapper.insert(item);
|
|
resourceItemMapper.insert(item);
|
|
|
return Result.success(item);
|
|
return Result.success(item);
|
|
@@ -130,6 +195,145 @@ public class ResourceService {
|
|
|
return Result.success(null);
|
|
return Result.success(null);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /** 记录一次互动并更新资源统计 */
|
|
|
|
|
+ public Result<Void> addInteraction(Long userId, Long itemId, String interactionType, String content) {
|
|
|
|
|
+ ResourceItem item = resourceItemMapper.selectById(itemId);
|
|
|
|
|
+ if (item == null || !item.getUserId().equals(userId)) {
|
|
|
|
|
+ return Result.error("资源不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (interactionType == null || !Arrays.asList("PHONE", "WECHAT", "MEETING", "OTHER").contains(interactionType)) {
|
|
|
|
|
+ return Result.error("互动方式不合法");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ PearlInteractionLog log = new PearlInteractionLog();
|
|
|
|
|
+ log.setUserId(userId);
|
|
|
|
|
+ log.setResourceItemId(itemId);
|
|
|
|
|
+ log.setInteractionType(interactionType);
|
|
|
|
|
+ log.setContent(content != null ? content.trim() : null);
|
|
|
|
|
+ log.setHappenedAt(new Date());
|
|
|
|
|
+ log.setCreatedAt(new Date());
|
|
|
|
|
+ interactionLogMapper.insert(log);
|
|
|
|
|
+
|
|
|
|
|
+ item.setLastInteractionAt(log.getHappenedAt());
|
|
|
|
|
+ item.setInteractionCount((item.getInteractionCount() == null ? 0 : item.getInteractionCount()) + 1);
|
|
|
|
|
+ // 重新计算价值分(互动后价值提升)
|
|
|
|
|
+ List<PearlInteractionLog> logs = interactionLogMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<PearlInteractionLog>()
|
|
|
|
|
+ .eq(PearlInteractionLog::getResourceItemId, itemId)
|
|
|
|
|
+ .orderByDesc(PearlInteractionLog::getHappenedAt)
|
|
|
|
|
+ );
|
|
|
|
|
+ item.setValueScore(calculateValueScore(item, logs));
|
|
|
|
|
+ resourceItemMapper.updateById(item);
|
|
|
|
|
+
|
|
|
|
|
+ // 关闭该资源的 PENDING 提醒
|
|
|
|
|
+ reminderMapper.update(null,
|
|
|
|
|
+ new LambdaUpdateWrapper<PearlInteractionReminder>()
|
|
|
|
|
+ .eq(PearlInteractionReminder::getUserId, userId)
|
|
|
|
|
+ .eq(PearlInteractionReminder::getResourceItemId, itemId)
|
|
|
|
|
+ .eq(PearlInteractionReminder::getStatus, "PENDING")
|
|
|
|
|
+ .set(PearlInteractionReminder::getStatus, "DONE")
|
|
|
|
|
+ );
|
|
|
|
|
+ return Result.success(null);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** 互动历史(按时间倒序) */
|
|
|
|
|
+ public Result<List<Map<String, Object>>> getInteractionList(Long userId, Long itemId, int page, int size) {
|
|
|
|
|
+ ResourceItem item = resourceItemMapper.selectById(itemId);
|
|
|
|
|
+ if (item == null || !item.getUserId().equals(userId)) {
|
|
|
|
|
+ return Result.error("资源不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+ List<PearlInteractionLog> logs = interactionLogMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<PearlInteractionLog>()
|
|
|
|
|
+ .eq(PearlInteractionLog::getResourceItemId, itemId)
|
|
|
|
|
+ .orderByDesc(PearlInteractionLog::getHappenedAt)
|
|
|
|
|
+ .last("LIMIT " + ((page - 1) * size) + "," + size)
|
|
|
|
|
+ );
|
|
|
|
|
+ List<Map<String, Object>> list = logs.stream().map(l -> {
|
|
|
|
|
+ Map<String, Object> m = new LinkedHashMap<>();
|
|
|
|
|
+ m.put("id", l.getId());
|
|
|
|
|
+ m.put("interactionType", l.getInteractionType());
|
|
|
|
|
+ m.put("content", l.getContent());
|
|
|
|
|
+ m.put("happenedAt", l.getHappenedAt());
|
|
|
|
|
+ return m;
|
|
|
|
|
+ }).collect(Collectors.toList());
|
|
|
|
|
+ return Result.success(list);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** 手动覆盖优先级(1/2/3),覆盖后价值分同步调整 */
|
|
|
|
|
+ public Result<Void> updatePriority(Long userId, Long itemId, Integer priority) {
|
|
|
|
|
+ ResourceItem item = resourceItemMapper.selectById(itemId);
|
|
|
|
|
+ if (item == null || !item.getUserId().equals(userId)) {
|
|
|
|
|
+ return Result.error("资源不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (priority == null || priority < 1 || priority > 3) {
|
|
|
|
|
+ return Result.error("优先级不合法");
|
|
|
|
|
+ }
|
|
|
|
|
+ item.setPriority(priority);
|
|
|
|
|
+ // 手动覆盖时同步调整 valueScore 到一个合理档位
|
|
|
|
|
+ item.setValueScore(priority == 1 ? 80 : priority == 2 ? 50 : 20);
|
|
|
|
|
+ resourceItemMapper.updateById(item);
|
|
|
|
|
+ return Result.success(null);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** 待办提醒列表(PENDING) */
|
|
|
|
|
+ public Result<List<Map<String, Object>>> getReminders(Long userId) {
|
|
|
|
|
+ List<PearlInteractionReminder> reminders = reminderMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<PearlInteractionReminder>()
|
|
|
|
|
+ .eq(PearlInteractionReminder::getUserId, userId)
|
|
|
|
|
+ .eq(PearlInteractionReminder::getStatus, "PENDING")
|
|
|
|
|
+ .orderByDesc(PearlInteractionReminder::getDaysSinceLast)
|
|
|
|
|
+ );
|
|
|
|
|
+ Map<Long, ResourceItem> itemMap = new HashMap<>();
|
|
|
|
|
+ List<Long> ids = reminders.stream().map(PearlInteractionReminder::getResourceItemId).collect(Collectors.toList());
|
|
|
|
|
+ if (!ids.isEmpty()) {
|
|
|
|
|
+ resourceItemMapper.selectBatchIds(ids).forEach(i -> itemMap.put(i.getId(), i));
|
|
|
|
|
+ }
|
|
|
|
|
+ List<Map<String, Object>> list = reminders.stream().map(r -> {
|
|
|
|
|
+ Map<String, Object> m = new LinkedHashMap<>();
|
|
|
|
|
+ m.put("id", r.getId());
|
|
|
|
|
+ m.put("resourceItemId", r.getResourceItemId());
|
|
|
|
|
+ ResourceItem ri = itemMap.get(r.getResourceItemId());
|
|
|
|
|
+ m.put("name", ri != null ? ri.getName() : "");
|
|
|
|
|
+ m.put("connectionType", r.getConnectionType());
|
|
|
|
|
+ m.put("suggestIntervalMonth", r.getSuggestIntervalMonth());
|
|
|
|
|
+ m.put("daysSinceLast", r.getDaysSinceLast());
|
|
|
|
|
+ return m;
|
|
|
|
|
+ }).collect(Collectors.toList());
|
|
|
|
|
+ return Result.success(list);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** 自动计算价值分 0-100 */
|
|
|
|
|
+ public int calculateValueScore(ResourceItem item, List<PearlInteractionLog> logs) {
|
|
|
|
|
+ int score = 0;
|
|
|
|
|
+ // 1. 类型基础权重
|
|
|
|
|
+ ConnectionType ct = null;
|
|
|
|
|
+ for (ConnectionType t : ConnectionType.values()) {
|
|
|
|
|
+ if (t.name().equals(item.getConnectionType())) { ct = t; break; }
|
|
|
|
|
+ }
|
|
|
|
|
+ score += (ct != null && "ESSENTIAL".equals(ct.getCategory())) ? 30 : 20;
|
|
|
|
|
+ // 2. 近90天互动次数 × 5,上限 30
|
|
|
|
|
+ Date now = new Date();
|
|
|
|
|
+ Date ninetyDaysAgo = new Date(now.getTime() - 90L * 24 * 3600 * 1000);
|
|
|
|
|
+ int recent = 0;
|
|
|
|
|
+ if (logs != null) {
|
|
|
|
|
+ for (PearlInteractionLog l : logs) {
|
|
|
|
|
+ if (l.getHappenedAt() != null && l.getHappenedAt().after(ninetyDaysAgo)) recent++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ score += Math.min(recent * 5, 30);
|
|
|
|
|
+ // 3. 最近互动时效性
|
|
|
|
|
+ if (item.getLastInteractionAt() != null) {
|
|
|
|
|
+ long days = ChronoUnit.DAYS.between(item.getLastInteractionAt().toInstant(), Instant.now());
|
|
|
|
|
+ if (days <= 30) score += 20;
|
|
|
|
|
+ else if (days <= 90) score += 10;
|
|
|
|
|
+ else if (days <= 180) score += 5;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 4. 互动总量 × 2,上限 20
|
|
|
|
|
+ int count = item.getInteractionCount() == null ? 0 : item.getInteractionCount();
|
|
|
|
|
+ score += Math.min(count * 2, 20);
|
|
|
|
|
+ return Math.min(score, 100);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/** 能力图聚合:SKILL 类帮助记录按联系人聚合为能力节点 */
|
|
/** 能力图聚合:SKILL 类帮助记录按联系人聚合为能力节点 */
|
|
|
public Result<List<Map<String, Object>>> getAbilityMap(Long userId) {
|
|
public Result<List<Map<String, Object>>> getAbilityMap(Long userId) {
|
|
|
List<ContactHelpLog> skillLogs = helpLogMapper.selectList(
|
|
List<ContactHelpLog> skillLogs = helpLogMapper.selectList(
|