feat: 人生事件和剧本收藏从 localStorage 切换为真实 API
- 后端:新增 t_event_favorite 表(UNIQUE KEY + MyBatis-Plus Entity) - 后端:LifeEventController 新增 /favorite/toggle、/favorite/page、/favorite/check 三个真实端点 - 后端:EventFavoriteService 完整 CRUD,含 toggle/分页/批量查询/状态检查 - 后端:删除旧的 favorite-placeholder mock 端点 - 前端:life-event/detail.vue 从 localStorage 假收藏切换为真实 API + localStorage 缓存防闪烁 - 前端:ScriptLibraryView.vue 从 localFavorites 切换为 checkFavoriteScript 真实 API - 前端:stores/app.js 新增 toggleEventFavorite / checkEventFavorite 方法 - 修复:审核发现的 UNIQUE 约束缺失、未使用 watch 导入、Controller 参数注解遗漏、Controller 委托 Service 构造 Response Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -134,14 +134,41 @@ const isDescriptionCollapsed = ref(false)
|
||||
const scrollTop = ref(0)
|
||||
const currentScrollTop = ref(0)
|
||||
|
||||
// 从 localStorage 读取缓存的收藏状态
|
||||
const getCachedFavoriteStatus = (id) => {
|
||||
const cached = uni.getStorageSync(`favorite_event_${id}`)
|
||||
return cached === 'true'
|
||||
}
|
||||
|
||||
// 缓存收藏状态到 localStorage
|
||||
const setCachedFavoriteStatus = (id, status) => {
|
||||
uni.setStorageSync(`favorite_event_${id}`, status ? 'true' : 'false')
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
const info = uni.getWindowInfo()
|
||||
safeAreaBottom.value = info.safeAreaInsets?.bottom || 0
|
||||
const pages = getCurrentPages()
|
||||
eventId.value = pages[pages.length - 1]?.options?.id || ''
|
||||
|
||||
// 先从缓存读取收藏状态,避免闪烁
|
||||
if (eventId.value) {
|
||||
isFavorite.value = getCachedFavoriteStatus(eventId.value)
|
||||
}
|
||||
|
||||
cachedEvent.value = uni.getStorageSync('current_life_event') || null
|
||||
if (!store.events?.length) await store.fetchEvents()
|
||||
isFavorite.value = Boolean(uni.getStorageSync(`event_favorite_${eventId.value}`))
|
||||
|
||||
// 异步校验真实收藏状态并更新缓存
|
||||
if (eventId.value) {
|
||||
const result = await store.checkEventFavorite(eventId.value)
|
||||
if (result.success) {
|
||||
const realStatus = result.data
|
||||
isFavorite.value = realStatus
|
||||
setCachedFavoriteStatus(eventId.value, realStatus)
|
||||
}
|
||||
}
|
||||
|
||||
analytics.trackPageView(pagePath, {
|
||||
life_event_id: eventId.value,
|
||||
tag_count: Array.isArray(displayEvent.value.tags) ? displayEvent.value.tags.length : 0
|
||||
@@ -305,20 +332,19 @@ const editEvent = () => {
|
||||
|
||||
const toggleFavorite = async () => {
|
||||
if (!eventId.value) return
|
||||
const next = !isFavorite.value
|
||||
const result = await store.favoriteEvent({ id: eventId.value, favorite: next })
|
||||
const result = await store.toggleEventFavorite(eventId.value)
|
||||
if (!result.success) {
|
||||
uni.showToast({ title: result.error || '收藏失败', icon: 'none' })
|
||||
return
|
||||
}
|
||||
isFavorite.value = next
|
||||
if (next) uni.setStorageSync(`event_favorite_${eventId.value}`, '1')
|
||||
else uni.removeStorageSync(`event_favorite_${eventId.value}`)
|
||||
const newStatus = result.data?.isFavorited ?? false
|
||||
isFavorite.value = newStatus
|
||||
setCachedFavoriteStatus(eventId.value, newStatus)
|
||||
analytics.track('life_event_favorite', {
|
||||
life_event_id: eventId.value,
|
||||
favorite: next
|
||||
favorite: newStatus
|
||||
}, { eventType: 'life_event', pagePath })
|
||||
uni.showToast({ title: next ? '已收藏' : '已取消收藏', icon: 'success' })
|
||||
uni.showToast({ title: newStatus ? '已收藏' : '已取消收藏', icon: 'success' })
|
||||
}
|
||||
|
||||
const chatEvent = async () => {
|
||||
|
||||
@@ -147,9 +147,9 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { useAppStore } from '../../stores/app.js'
|
||||
import { toggleFavoriteScript } from '../../services/epicScript.js'
|
||||
import { toggleFavoriteScript, checkFavoriteScript } from '../../services/epicScript.js'
|
||||
|
||||
const store = useAppStore()
|
||||
const activeType = ref('long')
|
||||
@@ -157,7 +157,7 @@ const activeStatus = ref('all')
|
||||
const keyword = ref('')
|
||||
const sortMode = ref('updated')
|
||||
const viewMode = ref('list')
|
||||
const localFavorites = ref(uni.getStorageSync('script_favorites') || {})
|
||||
const favoriteStatus = ref({})
|
||||
const activeMenuId = ref('')
|
||||
const deleteTarget = ref(null)
|
||||
const deletingScript = ref(false)
|
||||
@@ -198,6 +198,26 @@ const visibleScripts = computed(() => {
|
||||
})
|
||||
})
|
||||
|
||||
// 列表加载后批量检查收藏状态
|
||||
const loadFavoriteStatus = async () => {
|
||||
const ids = scripts.value.map(s => String(s.id)).filter(id => !id.startsWith('demo-'))
|
||||
if (!ids.length) return
|
||||
const status = {}
|
||||
await Promise.all(ids.map(async (id) => {
|
||||
try {
|
||||
status[id] = await checkFavoriteScript(id)
|
||||
} catch {
|
||||
status[id] = false
|
||||
}
|
||||
}))
|
||||
favoriteStatus.value = status
|
||||
}
|
||||
|
||||
// 监听 scripts 变化时重新加载收藏状态
|
||||
watch(scripts, () => {
|
||||
loadFavoriteStatus()
|
||||
}, { immediate: true })
|
||||
|
||||
const sortLabel = computed(() => {
|
||||
const map = { updated: '最近更新⌄', words: '字数最多⌄', progress: '进度最高⌄' }
|
||||
return map[sortMode.value] || '最近更新⌄'
|
||||
@@ -243,7 +263,7 @@ const getProgress = (script) => Math.max(0, Math.min(99, Number(script.progress
|
||||
const getInitial = (script) => (script.title || '剧').slice(0, 1)
|
||||
|
||||
const isFavorite = (script) => {
|
||||
return Boolean(script.isFavorite || script.favorite || localFavorites.value[String(script.id)])
|
||||
return Boolean(favoriteStatus.value[String(script.id)])
|
||||
}
|
||||
|
||||
const openScriptChat = (script) => {
|
||||
@@ -319,15 +339,14 @@ const toggleScriptMenu = (script) => {
|
||||
|
||||
const toggleFavorite = async (script) => {
|
||||
const wasFav = isFavorite(script)
|
||||
// 乐观更新:先改本地缓存
|
||||
const next = { ...localFavorites.value }
|
||||
// 乐观更新:先改内存状态
|
||||
const next = { ...favoriteStatus.value }
|
||||
if (wasFav) {
|
||||
delete next[String(script.id)]
|
||||
} else {
|
||||
next[String(script.id)] = true
|
||||
}
|
||||
localFavorites.value = next
|
||||
uni.setStorageSync('script_favorites', next)
|
||||
favoriteStatus.value = next
|
||||
closeScriptMenu()
|
||||
|
||||
try {
|
||||
@@ -335,14 +354,13 @@ const toggleFavorite = async (script) => {
|
||||
uni.showToast({ title: result.isFavorited ? '已收藏' : '已取消收藏', icon: 'success' })
|
||||
} catch (error) {
|
||||
// 回滚乐观更新
|
||||
const restored = { ...localFavorites.value }
|
||||
const restored = { ...favoriteStatus.value }
|
||||
if (wasFav) {
|
||||
restored[String(script.id)] = true
|
||||
} else {
|
||||
delete restored[String(script.id)]
|
||||
}
|
||||
localFavorites.value = restored
|
||||
uni.setStorageSync('script_favorites', restored)
|
||||
favoriteStatus.value = restored
|
||||
uni.showToast({ title: error?.message || '操作失败', icon: 'none' })
|
||||
}
|
||||
}
|
||||
@@ -383,10 +401,9 @@ const confirmDeleteScript = async () => {
|
||||
return
|
||||
}
|
||||
|
||||
const next = { ...localFavorites.value }
|
||||
const next = { ...favoriteStatus.value }
|
||||
delete next[id]
|
||||
localFavorites.value = next
|
||||
uni.setStorageSync('script_favorites', next)
|
||||
favoriteStatus.value = next
|
||||
const conversationId = deleteTarget.value.conversationId || deleteTarget.value.plotJson?.conversationId
|
||||
if (conversationId) uni.removeStorageSync(`script_conversation_history_${conversationId}`)
|
||||
uni.removeStorageSync(`script_chat_history_${id}`)
|
||||
|
||||
@@ -64,8 +64,28 @@ export const shareEvent = async (eventData = {}) => {
|
||||
return post('/lifeEvent/share-placeholder', eventData)
|
||||
}
|
||||
|
||||
export const favoriteEvent = async ({ id, favorite }) => {
|
||||
return post('/lifeEvent/favorite-placeholder', { id, favorite })
|
||||
/**
|
||||
* 切换人生事件收藏状态(收藏/取消收藏)
|
||||
* @param {string} eventId 事件ID
|
||||
* @returns {Promise<{success: boolean, isFavorited: boolean}>}
|
||||
*/
|
||||
export const toggleFavoriteEvent = async (eventId) => {
|
||||
const res = await post('/lifeEvent/favorite/toggle', { eventId })
|
||||
return {
|
||||
success: true,
|
||||
isFavorited: res.data?.isFavorited ?? false,
|
||||
favoriteTime: res.data?.favoriteTime ?? null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查人生事件是否已收藏
|
||||
* @param {string} eventId 事件ID
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
export const checkFavoriteEvent = async (eventId) => {
|
||||
const res = await get('/lifeEvent/favorite/check', { eventId })
|
||||
return res.data?.isFavorited ?? false
|
||||
}
|
||||
|
||||
const transformToBackendFormat = (frontendData) => {
|
||||
@@ -153,7 +173,8 @@ export default {
|
||||
assistEventWriting,
|
||||
chatAboutEvent,
|
||||
shareEvent,
|
||||
favoriteEvent,
|
||||
toggleFavoriteEvent,
|
||||
checkFavoriteEvent,
|
||||
transformToFrontendFormat,
|
||||
transformListToFrontend
|
||||
}
|
||||
|
||||
@@ -232,10 +232,19 @@ const shareEvent = async (eventData) => {
|
||||
}
|
||||
}
|
||||
|
||||
const favoriteEvent = async ({ id, favorite }) => {
|
||||
const toggleEventFavorite = async (eventId) => {
|
||||
try {
|
||||
const res = await lifeEventService.favoriteEvent({ id, favorite })
|
||||
return { success: true, data: res.data }
|
||||
const result = await lifeEventService.toggleFavoriteEvent(eventId)
|
||||
return { success: true, data: result }
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message }
|
||||
}
|
||||
}
|
||||
|
||||
const checkEventFavorite = async (eventId) => {
|
||||
try {
|
||||
const isFavorited = await lifeEventService.checkFavoriteEvent(eventId)
|
||||
return { success: true, data: isFavorited }
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message }
|
||||
}
|
||||
@@ -479,7 +488,8 @@ export const useAppStore = () => {
|
||||
assistEventWriting,
|
||||
chatAboutEvent,
|
||||
shareEvent,
|
||||
favoriteEvent,
|
||||
toggleEventFavorite,
|
||||
checkEventFavorite,
|
||||
getEventById,
|
||||
fetchScripts,
|
||||
createScript,
|
||||
|
||||
@@ -2,10 +2,13 @@ package com.emotion.controller;
|
||||
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.common.Result;
|
||||
import com.emotion.dto.request.EventFavoriteToggleRequest;
|
||||
import com.emotion.dto.request.LifeEventCreateRequest;
|
||||
import com.emotion.dto.request.LifeEventPageRequest;
|
||||
import com.emotion.dto.request.LifeEventUpdateRequest;
|
||||
import com.emotion.dto.response.EventFavoriteResponse;
|
||||
import com.emotion.dto.response.LifeEventResponse;
|
||||
import com.emotion.service.EventFavoriteService;
|
||||
import com.emotion.service.LifeEventService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
@@ -35,6 +38,9 @@ public class LifeEventController {
|
||||
@Autowired
|
||||
private LifeEventService lifeEventService;
|
||||
|
||||
@Autowired
|
||||
private EventFavoriteService eventFavoriteService;
|
||||
|
||||
/**
|
||||
* 分页查询当前用户的生命事件
|
||||
*/
|
||||
@@ -150,18 +156,6 @@ public class LifeEventController {
|
||||
return Result.success(data);
|
||||
}
|
||||
|
||||
@Operation(summary = "收藏占位接口", description = "返回收藏功能的占位响应。")
|
||||
@PostMapping(value = "/favorite-placeholder")
|
||||
public Result<Map<String, Object>> favoritePlaceholder(@RequestBody Map<String, Object> request) {
|
||||
String id = stringValue(request.get("id"), "");
|
||||
Boolean favorite = Boolean.TRUE.equals(request.get("favorite"));
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("id", id);
|
||||
data.put("favorite", favorite);
|
||||
data.put("placeholder", true);
|
||||
return Result.success(data);
|
||||
}
|
||||
|
||||
private String stringValue(Object value, String fallback) {
|
||||
if (value == null) {
|
||||
return fallback;
|
||||
@@ -170,6 +164,30 @@ public class LifeEventController {
|
||||
return text.isEmpty() ? fallback : text;
|
||||
}
|
||||
|
||||
// ==================== 人生事件收藏 ====================
|
||||
|
||||
@Operation(summary = "切换收藏", description = "收藏或取消收藏指定事件。")
|
||||
@PostMapping(value = "/favorite/toggle")
|
||||
public Result<EventFavoriteResponse> toggleFavorite(
|
||||
@Valid @RequestBody EventFavoriteToggleRequest request) {
|
||||
return Result.success(eventFavoriteService.toggleFavorite(request.getEventId()));
|
||||
}
|
||||
|
||||
@Operation(summary = "分页查询收藏", description = "分页查询当前用户收藏的事件列表。")
|
||||
@GetMapping(value = "/favorite/page")
|
||||
public Result<PageResult<LifeEventResponse>> getFavoritePage(
|
||||
@RequestParam(defaultValue = "1") long current,
|
||||
@RequestParam(defaultValue = "10") long size) {
|
||||
return Result.success(eventFavoriteService.getFavoritePage(current, size));
|
||||
}
|
||||
|
||||
@Operation(summary = "检查收藏状态", description = "检查指定事件是否已被当前用户收藏。")
|
||||
@GetMapping(value = "/favorite/check")
|
||||
public Result<EventFavoriteResponse> checkFavorite(
|
||||
@Parameter(description = "事件 ID") @RequestParam String eventId) {
|
||||
return Result.success(eventFavoriteService.checkFavorite(eventId));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<String> readTags(Object value) {
|
||||
List<String> tags = new ArrayList<>();
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.emotion.dto.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 人生事件收藏切换请求
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2026-07-19
|
||||
*/
|
||||
@Data
|
||||
public class EventFavoriteToggleRequest {
|
||||
|
||||
@NotBlank(message = "事件ID不能为空")
|
||||
private String eventId;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.emotion.dto.response;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 人生事件收藏状态响应
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2026-07-19
|
||||
*/
|
||||
@Data
|
||||
public class EventFavoriteResponse {
|
||||
|
||||
private String eventId;
|
||||
|
||||
private Boolean isFavorited;
|
||||
|
||||
private String favoriteTime;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.emotion.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.emotion.common.BaseEntity;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
/**
|
||||
* 人生事件收藏关联实体
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2026-07-19
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@TableName("t_event_favorite")
|
||||
public class EventFavorite extends BaseEntity {
|
||||
|
||||
@TableField("user_id")
|
||||
private String userId;
|
||||
|
||||
@TableField("event_id")
|
||||
private String eventId;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.emotion.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.emotion.entity.EventFavorite;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 人生事件收藏Mapper接口
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2026-07-19
|
||||
*/
|
||||
@Mapper
|
||||
public interface EventFavoriteMapper extends BaseMapper<EventFavorite> {
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.emotion.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.dto.response.EventFavoriteResponse;
|
||||
import com.emotion.dto.response.LifeEventResponse;
|
||||
import com.emotion.entity.EventFavorite;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 人生事件收藏服务接口
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2026-07-19
|
||||
*/
|
||||
public interface EventFavoriteService extends IService<EventFavorite> {
|
||||
|
||||
/**
|
||||
* 切换收藏状态(收藏/取消收藏)
|
||||
*
|
||||
* @param eventId 事件ID
|
||||
* @return 收藏状态响应
|
||||
*/
|
||||
EventFavoriteResponse toggleFavorite(String eventId);
|
||||
|
||||
/**
|
||||
* 分页查询当前用户收藏的事件列表
|
||||
*
|
||||
* @param current 当前页码
|
||||
* @param size 每页大小
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageResult<LifeEventResponse> getFavoritePage(long current, long size);
|
||||
|
||||
/**
|
||||
* 批量查询已收藏的事件ID集合
|
||||
*
|
||||
* @param eventIds 待查询的事件ID集合
|
||||
* @return 已收藏的事件ID集合
|
||||
*/
|
||||
Set<String> getFavoritedEventIds(Set<String> eventIds);
|
||||
|
||||
/**
|
||||
* 检查收藏状态
|
||||
*
|
||||
* @param eventId 事件ID
|
||||
* @return 收藏状态响应
|
||||
*/
|
||||
EventFavoriteResponse checkFavorite(String eventId);
|
||||
|
||||
/**
|
||||
* 检查指定事件是否已被当前用户收藏
|
||||
*
|
||||
* @param eventId 事件ID
|
||||
* @return 是否已收藏
|
||||
*/
|
||||
boolean isFavorited(String eventId);
|
||||
|
||||
/**
|
||||
* 获取当前用户的收藏总数
|
||||
*
|
||||
* @return 收藏总数
|
||||
*/
|
||||
long getFavoriteCount();
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package com.emotion.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.emotion.common.PageResult;
|
||||
import com.emotion.dto.response.EventFavoriteResponse;
|
||||
import com.emotion.dto.response.LifeEventResponse;
|
||||
import com.emotion.entity.EventFavorite;
|
||||
import com.emotion.mapper.EventFavoriteMapper;
|
||||
import com.emotion.service.EventFavoriteService;
|
||||
import com.emotion.service.LifeEventService;
|
||||
import com.emotion.util.UserContextHolder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 人生事件收藏服务实现
|
||||
*
|
||||
* @author huazhongmin
|
||||
* @date 2026-07-19
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class EventFavoriteServiceImpl extends ServiceImpl<EventFavoriteMapper, EventFavorite>
|
||||
implements EventFavoriteService {
|
||||
|
||||
private static final DateTimeFormatter FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
@Autowired
|
||||
private LifeEventService lifeEventService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public EventFavoriteResponse toggleFavorite(String eventId) {
|
||||
String userId = UserContextHolder.getCurrentUserId();
|
||||
EventFavoriteResponse r = new EventFavoriteResponse();
|
||||
r.setEventId(eventId);
|
||||
|
||||
if (!StringUtils.hasText(userId)) {
|
||||
r.setIsFavorited(false);
|
||||
return r;
|
||||
}
|
||||
|
||||
LambdaQueryWrapper<EventFavorite> w = new LambdaQueryWrapper<>();
|
||||
w.eq(EventFavorite::getUserId, userId)
|
||||
.eq(EventFavorite::getEventId, eventId)
|
||||
.eq(EventFavorite::getIsDeleted, 0);
|
||||
|
||||
EventFavorite existing = this.getOne(w);
|
||||
if (existing != null) {
|
||||
this.removeById(existing.getId());
|
||||
r.setIsFavorited(false);
|
||||
} else {
|
||||
EventFavorite fav = new EventFavorite();
|
||||
fav.setUserId(userId);
|
||||
fav.setEventId(eventId);
|
||||
this.save(fav);
|
||||
r.setIsFavorited(true);
|
||||
if (fav.getCreateTime() != null) {
|
||||
r.setFavoriteTime(fav.getCreateTime().format(FMT));
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<LifeEventResponse> getFavoritePage(long current, long size) {
|
||||
String userId = UserContextHolder.getCurrentUserId();
|
||||
if (!StringUtils.hasText(userId)) {
|
||||
return new PageResult<>();
|
||||
}
|
||||
|
||||
LambdaQueryWrapper<EventFavorite> w = new LambdaQueryWrapper<>();
|
||||
w.eq(EventFavorite::getUserId, userId)
|
||||
.eq(EventFavorite::getIsDeleted, 0)
|
||||
.orderByDesc(EventFavorite::getCreateTime);
|
||||
|
||||
Page<EventFavorite> page = new Page<>(current, size);
|
||||
Page<EventFavorite> result = this.page(page, w);
|
||||
|
||||
List<LifeEventResponse> records = result.getRecords().stream()
|
||||
.map(EventFavorite::getEventId)
|
||||
.map(lifeEventService::getEventById)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PageResult<LifeEventResponse> pr = new PageResult<>();
|
||||
pr.setCurrent(result.getCurrent());
|
||||
pr.setSize(result.getSize());
|
||||
pr.setTotal(result.getTotal());
|
||||
pr.setPages(result.getPages());
|
||||
pr.setRecords(records);
|
||||
return pr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getFavoritedEventIds(Set<String> eventIds) {
|
||||
String userId = UserContextHolder.getCurrentUserId();
|
||||
if (!StringUtils.hasText(userId) || eventIds == null || eventIds.isEmpty()) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
LambdaQueryWrapper<EventFavorite> w = new LambdaQueryWrapper<>();
|
||||
w.eq(EventFavorite::getUserId, userId)
|
||||
.in(EventFavorite::getEventId, eventIds)
|
||||
.eq(EventFavorite::getIsDeleted, 0);
|
||||
|
||||
return this.list(w).stream()
|
||||
.map(EventFavorite::getEventId)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventFavoriteResponse checkFavorite(String eventId) {
|
||||
EventFavoriteResponse r = new EventFavoriteResponse();
|
||||
r.setEventId(eventId);
|
||||
r.setIsFavorited(isFavorited(eventId));
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFavorited(String eventId) {
|
||||
String userId = UserContextHolder.getCurrentUserId();
|
||||
if (!StringUtils.hasText(userId) || !StringUtils.hasText(eventId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LambdaQueryWrapper<EventFavorite> w = new LambdaQueryWrapper<>();
|
||||
w.eq(EventFavorite::getUserId, userId)
|
||||
.eq(EventFavorite::getEventId, eventId)
|
||||
.eq(EventFavorite::getIsDeleted, 0);
|
||||
|
||||
return this.count(w) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFavoriteCount() {
|
||||
String userId = UserContextHolder.getCurrentUserId();
|
||||
if (!StringUtils.hasText(userId)) {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
LambdaQueryWrapper<EventFavorite> w = new LambdaQueryWrapper<>();
|
||||
w.eq(EventFavorite::getUserId, userId)
|
||||
.eq(EventFavorite::getIsDeleted, 0);
|
||||
|
||||
return this.count(w);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
CREATE TABLE t_event_favorite (
|
||||
id VARCHAR(64) PRIMARY KEY,
|
||||
user_id VARCHAR(64) NOT NULL COMMENT '用户ID',
|
||||
event_id VARCHAR(64) NOT NULL COMMENT '事件ID',
|
||||
create_by VARCHAR(64) COMMENT '创建人',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
update_by VARCHAR(64) COMMENT '更新人',
|
||||
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
is_deleted TINYINT(1) DEFAULT 0 COMMENT '是否删除:0-否 1-是',
|
||||
remarks VARCHAR(500) COMMENT '备注',
|
||||
UNIQUE KEY uk_user_event (user_id, event_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='人生事件收藏关联表';
|
||||
Reference in New Issue
Block a user