feat(分享): 新增故事分享反馈功能
All checks were successful
test/timeline-server/pipeline/head This commit looks good
All checks were successful
test/timeline-server/pipeline/head This commit looks good
实现故事分享的评论和浏览统计功能,包括: 1. 新增StoryShareFeedbackService接口及相关实现 2. 添加StoryShareComment、StoryShareMetric实体类 3. 创建story_share_comment和story_share_metric表 4. 在StoryDetailVo中增加shareConfigured和sharePublished字段 5. 扩展StoryShareConfigVo包含反馈统计数据 6. 新增公共API端点处理反馈请求 7. 实现模板样式选择功能
This commit is contained in:
@@ -6,6 +6,7 @@ CREATE TABLE IF NOT EXISTS `story_share` (
|
|||||||
`share_title` varchar(255) DEFAULT NULL COMMENT 'Curated public title',
|
`share_title` varchar(255) DEFAULT NULL COMMENT 'Curated public title',
|
||||||
`share_description` text COMMENT 'Curated public description',
|
`share_description` text COMMENT 'Curated public description',
|
||||||
`share_quote` text COMMENT 'Curated story note',
|
`share_quote` text COMMENT 'Curated story note',
|
||||||
|
`template_style` varchar(32) DEFAULT 'editorial' COMMENT 'Public template style',
|
||||||
`featured_moment_ids` text COMMENT 'Comma separated featured moment IDs',
|
`featured_moment_ids` text COMMENT 'Comma separated featured moment IDs',
|
||||||
`create_id` varchar(32) DEFAULT NULL,
|
`create_id` varchar(32) DEFAULT NULL,
|
||||||
`update_id` varchar(32) DEFAULT NULL,
|
`update_id` varchar(32) DEFAULT NULL,
|
||||||
|
|||||||
24
deploy/update_story_share_feedback.sql
Normal file
24
deploy/update_story_share_feedback.sql
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS `story_share_metric` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`share_id` varchar(64) NOT NULL,
|
||||||
|
`view_count` bigint NOT NULL DEFAULT '0',
|
||||||
|
`comment_count` bigint NOT NULL DEFAULT '0',
|
||||||
|
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_story_share_metric_share_id` (`share_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `story_share_comment` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`instance_id` varchar(64) NOT NULL,
|
||||||
|
`share_id` varchar(64) NOT NULL,
|
||||||
|
`visitor_name` varchar(40) NOT NULL,
|
||||||
|
`content` varchar(280) NOT NULL,
|
||||||
|
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
`is_delete` tinyint(1) DEFAULT '0',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_story_share_comment_instance_id` (`instance_id`),
|
||||||
|
KEY `idx_story_share_comment_share_id` (`share_id`, `is_delete`, `create_time`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||||
24
deploy/update_story_share_template.sql
Normal file
24
deploy/update_story_share_template.sql
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
SET @story_share_table_exists = (
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM information_schema.TABLES
|
||||||
|
WHERE TABLE_SCHEMA = DATABASE()
|
||||||
|
AND TABLE_NAME = 'story_share'
|
||||||
|
);
|
||||||
|
|
||||||
|
SET @story_share_template_exists = (
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = DATABASE()
|
||||||
|
AND TABLE_NAME = 'story_share'
|
||||||
|
AND COLUMN_NAME = 'template_style'
|
||||||
|
);
|
||||||
|
|
||||||
|
SET @story_share_template_stmt = IF(
|
||||||
|
@story_share_table_exists = 1 AND @story_share_template_exists = 0,
|
||||||
|
"ALTER TABLE story_share ADD COLUMN template_style varchar(32) DEFAULT 'editorial' COMMENT 'Public template style' AFTER share_quote",
|
||||||
|
'SELECT 1'
|
||||||
|
);
|
||||||
|
|
||||||
|
PREPARE story_share_template_stmt FROM @story_share_template_stmt;
|
||||||
|
EXECUTE story_share_template_stmt;
|
||||||
|
DEALLOCATE PREPARE story_share_template_stmt;
|
||||||
@@ -2,12 +2,18 @@ package com.timeline.story.controller;
|
|||||||
|
|
||||||
import com.timeline.common.response.ResponseEntity;
|
import com.timeline.common.response.ResponseEntity;
|
||||||
import com.timeline.story.service.StoryItemService;
|
import com.timeline.story.service.StoryItemService;
|
||||||
|
import com.timeline.story.service.StoryShareFeedbackService;
|
||||||
import com.timeline.story.vo.StoryItemShareVo;
|
import com.timeline.story.vo.StoryItemShareVo;
|
||||||
|
import com.timeline.story.vo.StoryShareCommentRequest;
|
||||||
|
import com.timeline.story.vo.StoryShareFeedbackVo;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@@ -18,10 +24,38 @@ public class StoryPublicController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private StoryItemService storyItemService;
|
private StoryItemService storyItemService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private StoryShareFeedbackService storyShareFeedbackService;
|
||||||
|
|
||||||
@GetMapping("/{shareId}")
|
@GetMapping("/{shareId}")
|
||||||
public ResponseEntity<StoryItemShareVo> getStoryItemByShareId(@PathVariable String shareId) {
|
public ResponseEntity<StoryItemShareVo> getStoryItemByShareId(@PathVariable String shareId) {
|
||||||
log.info("Get public story by shareId: {}", shareId);
|
log.info("Get public story by shareId: {}", shareId);
|
||||||
StoryItemShareVo storyItem = storyItemService.getItemByShareId(shareId);
|
StoryItemShareVo storyItem = storyItemService.getItemByShareId(shareId);
|
||||||
return ResponseEntity.success(storyItem);
|
return ResponseEntity.success(storyItem);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@GetMapping("/{shareId}/feedback")
|
||||||
|
public ResponseEntity<StoryShareFeedbackVo> getShareFeedback(
|
||||||
|
@PathVariable String shareId,
|
||||||
|
@RequestParam(defaultValue = "8") int limit) {
|
||||||
|
log.info("Get public story feedback: shareId={}, limit={}", shareId, limit);
|
||||||
|
return ResponseEntity.success(storyShareFeedbackService.getFeedback(shareId, limit));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{shareId}/feedback/view")
|
||||||
|
public ResponseEntity<StoryShareFeedbackVo> recordShareView(
|
||||||
|
@PathVariable String shareId,
|
||||||
|
@RequestParam(defaultValue = "8") int limit) {
|
||||||
|
log.info("Record public story view: shareId={}, limit={}", shareId, limit);
|
||||||
|
return ResponseEntity.success(storyShareFeedbackService.recordView(shareId, limit));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{shareId}/feedback/comment")
|
||||||
|
public ResponseEntity<StoryShareFeedbackVo> createShareComment(
|
||||||
|
@PathVariable String shareId,
|
||||||
|
@RequestBody StoryShareCommentRequest request,
|
||||||
|
@RequestParam(defaultValue = "8") int limit) {
|
||||||
|
log.info("Create public story comment: shareId={}, limit={}", shareId, limit);
|
||||||
|
return ResponseEntity.success(storyShareFeedbackService.createComment(shareId, request, limit));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.timeline.story.dao;
|
||||||
|
|
||||||
|
import com.timeline.story.entity.StoryShareComment;
|
||||||
|
import com.timeline.story.entity.StoryShareMetric;
|
||||||
|
import com.timeline.story.vo.StoryShareCommentVo;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface StoryShareFeedbackMapper {
|
||||||
|
void ensureMetricRow(@Param("shareId") String shareId);
|
||||||
|
|
||||||
|
void incrementViewCount(@Param("shareId") String shareId);
|
||||||
|
|
||||||
|
void incrementCommentCount(@Param("shareId") String shareId);
|
||||||
|
|
||||||
|
StoryShareMetric selectMetricByShareId(@Param("shareId") String shareId);
|
||||||
|
|
||||||
|
void insertComment(StoryShareComment comment);
|
||||||
|
|
||||||
|
List<StoryShareCommentVo> selectRecentComments(
|
||||||
|
@Param("shareId") String shareId,
|
||||||
|
@Param("limit") int limit);
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ public class StoryShare {
|
|||||||
private String shareTitle;
|
private String shareTitle;
|
||||||
private String shareDescription;
|
private String shareDescription;
|
||||||
private String shareQuote;
|
private String shareQuote;
|
||||||
|
private String templateStyle;
|
||||||
private String featuredMomentIds;
|
private String featuredMomentIds;
|
||||||
private String createId;
|
private String createId;
|
||||||
private String updateId;
|
private String updateId;
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.timeline.story.entity;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class StoryShareComment {
|
||||||
|
private Long id;
|
||||||
|
private String instanceId;
|
||||||
|
private String shareId;
|
||||||
|
private String visitorName;
|
||||||
|
private String content;
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
private Integer isDelete;
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.timeline.story.entity;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class StoryShareMetric {
|
||||||
|
private Long id;
|
||||||
|
private String shareId;
|
||||||
|
private Long viewCount;
|
||||||
|
private Long commentCount;
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.timeline.story.service;
|
||||||
|
|
||||||
|
import com.timeline.story.vo.StoryShareCommentRequest;
|
||||||
|
import com.timeline.story.vo.StoryShareFeedbackVo;
|
||||||
|
|
||||||
|
public interface StoryShareFeedbackService {
|
||||||
|
StoryShareFeedbackVo getFeedback(String shareId, int limit);
|
||||||
|
|
||||||
|
StoryShareFeedbackVo recordView(String shareId, int limit);
|
||||||
|
|
||||||
|
StoryShareFeedbackVo createComment(String shareId, StoryShareCommentRequest request, int limit);
|
||||||
|
}
|
||||||
@@ -314,7 +314,7 @@ public class AnalyticsServiceImpl implements AnalyticsService {
|
|||||||
|
|
||||||
return new ArchiveDataset(
|
return new ArchiveDataset(
|
||||||
storyData.size(),
|
storyData.size(),
|
||||||
(int) storyData.stream().filter(data -> StringUtils.hasText(data.story().getShareId())).count(),
|
(int) storyData.stream().filter(data -> isShareReady(data.story())).count(),
|
||||||
(int) itemData.stream().filter(data -> hasVideo(data.item())).count(),
|
(int) itemData.stream().filter(data -> hasVideo(data.item())).count(),
|
||||||
sortArchiveBuckets(locationBuckets),
|
sortArchiveBuckets(locationBuckets),
|
||||||
sortArchiveBuckets(tagBuckets),
|
sortArchiveBuckets(tagBuckets),
|
||||||
@@ -405,7 +405,9 @@ public class AnalyticsServiceImpl implements AnalyticsService {
|
|||||||
? item.getInstanceId()
|
? item.getInstanceId()
|
||||||
: (story.getInstanceId() + "-" + Math.abs((item.getTitle() == null ? "" : item.getTitle()).hashCode())));
|
: (story.getInstanceId() + "-" + Math.abs((item.getTitle() == null ? "" : item.getTitle()).hashCode())));
|
||||||
moment.setStoryInstanceId(story.getInstanceId());
|
moment.setStoryInstanceId(story.getInstanceId());
|
||||||
moment.setStoryShareId(story.getShareId());
|
moment.setStoryShareId(resolvePublicShareId(story));
|
||||||
|
moment.setShareConfigured(isShareReady(story));
|
||||||
|
moment.setSharePublished(isSharePublished(story));
|
||||||
moment.setStoryTitle(StringUtils.hasText(story.getTitle()) ? story.getTitle() : "Untitled story");
|
moment.setStoryTitle(StringUtils.hasText(story.getTitle()) ? story.getTitle() : "Untitled story");
|
||||||
moment.setStoryTime(formatArchiveDateTime(itemData.eventTime()));
|
moment.setStoryTime(formatArchiveDateTime(itemData.eventTime()));
|
||||||
moment.setItemInstanceId(item.getInstanceId());
|
moment.setItemInstanceId(item.getInstanceId());
|
||||||
@@ -438,6 +440,8 @@ public class AnalyticsServiceImpl implements AnalyticsService {
|
|||||||
bucket.setSubtitle(moment.getStoryTitle());
|
bucket.setSubtitle(moment.getStoryTitle());
|
||||||
bucket.setStoryInstanceId(moment.getStoryInstanceId());
|
bucket.setStoryInstanceId(moment.getStoryInstanceId());
|
||||||
bucket.setStoryShareId(moment.getStoryShareId());
|
bucket.setStoryShareId(moment.getStoryShareId());
|
||||||
|
bucket.setShareConfigured(moment.getShareConfigured());
|
||||||
|
bucket.setSharePublished(moment.getSharePublished());
|
||||||
bucket.setCoverInstanceId(moment.getCoverInstanceId());
|
bucket.setCoverInstanceId(moment.getCoverInstanceId());
|
||||||
bucket.setCoverSrc(moment.getCoverSrc());
|
bucket.setCoverSrc(moment.getCoverSrc());
|
||||||
bucket.setSampleMomentTitle(moment.getItemTitle());
|
bucket.setSampleMomentTitle(moment.getItemTitle());
|
||||||
@@ -451,7 +455,9 @@ public class AnalyticsServiceImpl implements AnalyticsService {
|
|||||||
if (momentSortValue >= bucketSortValue) {
|
if (momentSortValue >= bucketSortValue) {
|
||||||
bucket.setSubtitle(moment.getStoryTitle());
|
bucket.setSubtitle(moment.getStoryTitle());
|
||||||
bucket.setStoryInstanceId(story.getInstanceId());
|
bucket.setStoryInstanceId(story.getInstanceId());
|
||||||
bucket.setStoryShareId(story.getShareId());
|
bucket.setStoryShareId(resolvePublicShareId(story));
|
||||||
|
bucket.setShareConfigured(isShareReady(story));
|
||||||
|
bucket.setSharePublished(isSharePublished(story));
|
||||||
bucket.setCoverInstanceId(moment.getCoverInstanceId());
|
bucket.setCoverInstanceId(moment.getCoverInstanceId());
|
||||||
bucket.setCoverSrc(moment.getCoverSrc());
|
bucket.setCoverSrc(moment.getCoverSrc());
|
||||||
bucket.setSampleMomentTitle(moment.getItemTitle());
|
bucket.setSampleMomentTitle(moment.getItemTitle());
|
||||||
@@ -459,6 +465,21 @@ public class AnalyticsServiceImpl implements AnalyticsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String resolvePublicShareId(StoryDetailVo story) {
|
||||||
|
if (story == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return StringUtils.hasText(story.getPublicShareId()) ? story.getPublicShareId() : story.getShareId();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSharePublished(StoryDetailVo story) {
|
||||||
|
return story != null && (Boolean.TRUE.equals(story.getSharePublished()) || StringUtils.hasText(resolvePublicShareId(story)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isShareReady(StoryDetailVo story) {
|
||||||
|
return story != null && (Boolean.TRUE.equals(story.getShareConfigured()) || isSharePublished(story));
|
||||||
|
}
|
||||||
|
|
||||||
private List<TimelineArchiveBucketVo> sortArchiveBuckets(Map<String, TimelineArchiveBucketVo> bucketMap) {
|
private List<TimelineArchiveBucketVo> sortArchiveBuckets(Map<String, TimelineArchiveBucketVo> bucketMap) {
|
||||||
return bucketMap.values().stream()
|
return bucketMap.values().stream()
|
||||||
.sorted((left, right) -> {
|
.sorted((left, right) -> {
|
||||||
|
|||||||
@@ -9,14 +9,17 @@ import com.timeline.common.utils.IdUtils;
|
|||||||
import com.timeline.common.utils.UserContextUtils;
|
import com.timeline.common.utils.UserContextUtils;
|
||||||
import com.timeline.story.dao.StoryMapper;
|
import com.timeline.story.dao.StoryMapper;
|
||||||
import com.timeline.story.dao.StoryItemMapper;
|
import com.timeline.story.dao.StoryItemMapper;
|
||||||
|
import com.timeline.story.dao.StoryShareFeedbackMapper;
|
||||||
import com.timeline.story.dao.StoryShareMapper;
|
import com.timeline.story.dao.StoryShareMapper;
|
||||||
import com.timeline.story.entity.StoryItem;
|
import com.timeline.story.entity.StoryItem;
|
||||||
import com.timeline.story.entity.StoryShare;
|
import com.timeline.story.entity.StoryShare;
|
||||||
|
import com.timeline.story.entity.StoryShareMetric;
|
||||||
import com.timeline.story.service.StoryPermissionService;
|
import com.timeline.story.service.StoryPermissionService;
|
||||||
import com.timeline.story.service.StoryItemService;
|
import com.timeline.story.service.StoryItemService;
|
||||||
import com.timeline.story.vo.StoryDetailVo;
|
import com.timeline.story.vo.StoryDetailVo;
|
||||||
import com.timeline.story.vo.StoryItemAddVo;
|
import com.timeline.story.vo.StoryItemAddVo;
|
||||||
import com.timeline.story.vo.StoryItemShareVo;
|
import com.timeline.story.vo.StoryItemShareVo;
|
||||||
|
import com.timeline.story.vo.StoryShareCommentVo;
|
||||||
import com.timeline.story.vo.StoryItemVo;
|
import com.timeline.story.vo.StoryItemVo;
|
||||||
import com.timeline.story.vo.StoryShareConfigVo;
|
import com.timeline.story.vo.StoryShareConfigVo;
|
||||||
import com.timeline.story.vo.StoryShareMomentVo;
|
import com.timeline.story.vo.StoryShareMomentVo;
|
||||||
@@ -43,6 +46,8 @@ import java.util.Set;
|
|||||||
@Service
|
@Service
|
||||||
public class StoryItemServiceImpl implements StoryItemService {
|
public class StoryItemServiceImpl implements StoryItemService {
|
||||||
|
|
||||||
|
private static final String DEFAULT_TEMPLATE_STYLE = "editorial";
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private StoryItemMapper storyItemMapper;
|
private StoryItemMapper storyItemMapper;
|
||||||
|
|
||||||
@@ -55,6 +60,9 @@ public class StoryItemServiceImpl implements StoryItemService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private StoryShareMapper storyShareMapper;
|
private StoryShareMapper storyShareMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private StoryShareFeedbackMapper storyShareFeedbackMapper;
|
||||||
|
|
||||||
private String getCurrentUserId() {
|
private String getCurrentUserId() {
|
||||||
String currentUserId = UserContextUtils.getCurrentUserId();
|
String currentUserId = UserContextUtils.getCurrentUserId();
|
||||||
if (!StringUtils.hasText(currentUserId)) {
|
if (!StringUtils.hasText(currentUserId)) {
|
||||||
@@ -162,11 +170,15 @@ public class StoryItemServiceImpl implements StoryItemService {
|
|||||||
shareVo.setShareTitle(trimToNull(storyShare.getShareTitle()));
|
shareVo.setShareTitle(trimToNull(storyShare.getShareTitle()));
|
||||||
shareVo.setShareDescription(trimToNull(storyShare.getShareDescription()));
|
shareVo.setShareDescription(trimToNull(storyShare.getShareDescription()));
|
||||||
shareVo.setShareQuote(trimToNull(storyShare.getShareQuote()));
|
shareVo.setShareQuote(trimToNull(storyShare.getShareQuote()));
|
||||||
|
shareVo.setTemplateStyle(normalizeTemplateStyle(storyShare.getTemplateStyle()));
|
||||||
shareVo.setHeroMomentId(storyShare.getHeroMomentId());
|
shareVo.setHeroMomentId(storyShare.getHeroMomentId());
|
||||||
List<String> featuredMomentIds = parseFeaturedMomentIds(storyShare.getFeaturedMomentIds());
|
List<String> featuredMomentIds = parseFeaturedMomentIds(storyShare.getFeaturedMomentIds());
|
||||||
shareVo.setFeaturedMomentIds(featuredMomentIds);
|
shareVo.setFeaturedMomentIds(featuredMomentIds);
|
||||||
shareVo.setFeaturedMoments(loadFeaturedMoments(shareVo.getStoryInstanceId(), featuredMomentIds));
|
shareVo.setFeaturedMoments(loadFeaturedMoments(shareVo.getStoryInstanceId(), featuredMomentIds));
|
||||||
}
|
}
|
||||||
|
if (!StringUtils.hasText(shareVo.getTemplateStyle())) {
|
||||||
|
shareVo.setTemplateStyle(DEFAULT_TEMPLATE_STYLE);
|
||||||
|
}
|
||||||
|
|
||||||
if ((shareVo.getFeaturedMoments() == null || shareVo.getFeaturedMoments().isEmpty())
|
if ((shareVo.getFeaturedMoments() == null || shareVo.getFeaturedMoments().isEmpty())
|
||||||
&& StringUtils.hasText(shareVo.getInstanceId())) {
|
&& StringUtils.hasText(shareVo.getInstanceId())) {
|
||||||
@@ -187,21 +199,31 @@ public class StoryItemServiceImpl implements StoryItemService {
|
|||||||
StoryDetailVo story = getAccessibleStory(storyId, currentUserId);
|
StoryDetailVo story = getAccessibleStory(storyId, currentUserId);
|
||||||
StoryShare storyShare = storyShareMapper.selectLatestByStoryId(storyId);
|
StoryShare storyShare = storyShareMapper.selectLatestByStoryId(storyId);
|
||||||
StoryItem publishedItem = storyItemMapper.selectPublishedByStoryId(storyId);
|
StoryItem publishedItem = storyItemMapper.selectPublishedByStoryId(storyId);
|
||||||
|
boolean published = StringUtils.hasText(story.getShareId());
|
||||||
|
String publicShareId = published ? story.getShareId() : null;
|
||||||
|
String persistedShareId = StringUtils.hasText(publicShareId)
|
||||||
|
? publicShareId
|
||||||
|
: storyShare != null && StringUtils.hasText(storyShare.getShareId())
|
||||||
|
? storyShare.getShareId()
|
||||||
|
: publishedItem == null ? null : publishedItem.getShareId();
|
||||||
|
|
||||||
StoryShareConfigVo config = new StoryShareConfigVo();
|
StoryShareConfigVo config = new StoryShareConfigVo();
|
||||||
config.setStoryId(storyId);
|
config.setStoryId(storyId);
|
||||||
config.setShareId(StringUtils.hasText(story.getShareId()) ? story.getShareId() : storyShare == null ? null : storyShare.getShareId());
|
config.setShareId(persistedShareId);
|
||||||
|
config.setPublicShareId(publicShareId);
|
||||||
config.setHeroMomentId(storyShare != null && StringUtils.hasText(storyShare.getHeroMomentId())
|
config.setHeroMomentId(storyShare != null && StringUtils.hasText(storyShare.getHeroMomentId())
|
||||||
? storyShare.getHeroMomentId()
|
? storyShare.getHeroMomentId()
|
||||||
: publishedItem == null ? null : publishedItem.getInstanceId());
|
: publishedItem == null ? null : publishedItem.getInstanceId());
|
||||||
config.setTitle(storyShare == null ? null : trimToNull(storyShare.getShareTitle()));
|
config.setTitle(storyShare == null ? null : trimToNull(storyShare.getShareTitle()));
|
||||||
config.setDescription(storyShare == null ? null : trimToNull(storyShare.getShareDescription()));
|
config.setDescription(storyShare == null ? null : trimToNull(storyShare.getShareDescription()));
|
||||||
config.setQuote(storyShare == null ? null : trimToNull(storyShare.getShareQuote()));
|
config.setQuote(storyShare == null ? null : trimToNull(storyShare.getShareQuote()));
|
||||||
|
config.setTemplateStyle(storyShare == null ? DEFAULT_TEMPLATE_STYLE : normalizeTemplateStyle(storyShare.getTemplateStyle()));
|
||||||
config.setFeaturedMomentIds(storyShare == null
|
config.setFeaturedMomentIds(storyShare == null
|
||||||
? Collections.emptyList()
|
? Collections.emptyList()
|
||||||
: parseFeaturedMomentIds(storyShare.getFeaturedMomentIds()));
|
: parseFeaturedMomentIds(storyShare.getFeaturedMomentIds()));
|
||||||
config.setPublished(StringUtils.hasText(story.getShareId()));
|
config.setPublished(published);
|
||||||
config.setUpdatedAt(storyShare == null ? null : storyShare.getUpdateTime());
|
config.setUpdatedAt(storyShare == null ? null : storyShare.getUpdateTime());
|
||||||
|
hydrateShareFeedback(config, persistedShareId);
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,6 +271,7 @@ public class StoryItemServiceImpl implements StoryItemService {
|
|||||||
result.setStoryId(story.getInstanceId());
|
result.setStoryId(story.getInstanceId());
|
||||||
result.setShareId(shareId);
|
result.setShareId(shareId);
|
||||||
result.setHeroMomentId(heroItem.getInstanceId());
|
result.setHeroMomentId(heroItem.getInstanceId());
|
||||||
|
result.setTemplateStyle(normalizeTemplateStyle(request.getTemplateStyle()));
|
||||||
result.setPublicPath("/share/" + shareId);
|
result.setPublicPath("/share/" + shareId);
|
||||||
result.setPublishedAt(now);
|
result.setPublishedAt(now);
|
||||||
return result;
|
return result;
|
||||||
@@ -266,7 +289,6 @@ public class StoryItemServiceImpl implements StoryItemService {
|
|||||||
LocalDateTime now = LocalDateTime.now();
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
|
||||||
storyItemMapper.clearShareIdByStoryId(storyId, currentUserId, now);
|
storyItemMapper.clearShareIdByStoryId(storyId, currentUserId, now);
|
||||||
storyShareMapper.softDeleteByStoryId(storyId, currentUserId, now);
|
|
||||||
storyMapper.touchUpdate(storyId, currentUserId);
|
storyMapper.touchUpdate(storyId, currentUserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,6 +414,7 @@ public class StoryItemServiceImpl implements StoryItemService {
|
|||||||
storyShare.setShareTitle(trimToNull(request.getTitle()));
|
storyShare.setShareTitle(trimToNull(request.getTitle()));
|
||||||
storyShare.setShareDescription(trimToNull(request.getDescription()));
|
storyShare.setShareDescription(trimToNull(request.getDescription()));
|
||||||
storyShare.setShareQuote(trimToNull(request.getQuote()));
|
storyShare.setShareQuote(trimToNull(request.getQuote()));
|
||||||
|
storyShare.setTemplateStyle(normalizeTemplateStyle(request.getTemplateStyle()));
|
||||||
storyShare.setFeaturedMomentIds(serializeFeaturedMomentIds(request.getFeaturedMomentIds(), heroMomentId));
|
storyShare.setFeaturedMomentIds(serializeFeaturedMomentIds(request.getFeaturedMomentIds(), heroMomentId));
|
||||||
storyShare.setUpdateId(currentUserId);
|
storyShare.setUpdateId(currentUserId);
|
||||||
storyShare.setUpdateTime(now);
|
storyShare.setUpdateTime(now);
|
||||||
@@ -465,4 +488,32 @@ public class StoryItemServiceImpl implements StoryItemService {
|
|||||||
}
|
}
|
||||||
return value.trim();
|
return value.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String normalizeTemplateStyle(String templateStyle) {
|
||||||
|
if (!StringUtils.hasText(templateStyle)) {
|
||||||
|
return DEFAULT_TEMPLATE_STYLE;
|
||||||
|
}
|
||||||
|
String normalized = templateStyle.trim().toLowerCase();
|
||||||
|
return switch (normalized) {
|
||||||
|
case "cinematic", "scrapbook", "editorial" -> normalized;
|
||||||
|
default -> DEFAULT_TEMPLATE_STYLE;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hydrateShareFeedback(StoryShareConfigVo config, String shareId) {
|
||||||
|
if (config == null || !StringUtils.hasText(shareId)) {
|
||||||
|
if (config != null) {
|
||||||
|
config.setViewCount(0L);
|
||||||
|
config.setCommentCount(0L);
|
||||||
|
config.setRecentComments(Collections.emptyList());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StoryShareMetric metric = storyShareFeedbackMapper.selectMetricByShareId(shareId);
|
||||||
|
List<StoryShareCommentVo> recentComments = storyShareFeedbackMapper.selectRecentComments(shareId, 5);
|
||||||
|
config.setViewCount(metric == null || metric.getViewCount() == null ? 0L : metric.getViewCount());
|
||||||
|
config.setCommentCount(metric == null || metric.getCommentCount() == null ? 0L : metric.getCommentCount());
|
||||||
|
config.setRecentComments(recentComments == null ? Collections.emptyList() : recentComments);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -176,6 +176,7 @@ public class StoryServiceImpl implements StoryService {
|
|||||||
result.setItems(items);
|
result.setItems(items);
|
||||||
result.setInstanceId(story.getInstanceId());
|
result.setInstanceId(story.getInstanceId());
|
||||||
result.setShareId(story.getShareId());
|
result.setShareId(story.getShareId());
|
||||||
|
result.setPublicShareId(story.getPublicShareId());
|
||||||
result.setOwnerId(story.getOwnerId());
|
result.setOwnerId(story.getOwnerId());
|
||||||
result.setUpdateId(story.getUpdateId());
|
result.setUpdateId(story.getUpdateId());
|
||||||
result.setTitle(story.getTitle());
|
result.setTitle(story.getTitle());
|
||||||
@@ -190,6 +191,8 @@ public class StoryServiceImpl implements StoryService {
|
|||||||
result.setUpdateName(story.getUpdateName());
|
result.setUpdateName(story.getUpdateName());
|
||||||
result.setPermissionType(story.getPermissionType());
|
result.setPermissionType(story.getPermissionType());
|
||||||
result.setItemCount(story.getItemCount());
|
result.setItemCount(story.getItemCount());
|
||||||
|
result.setShareConfigured(story.getShareConfigured());
|
||||||
|
result.setSharePublished(story.getSharePublished());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,4 +216,4 @@ public class StoryServiceImpl implements StoryService {
|
|||||||
throw new CustomException(500, "Query stories failed");
|
throw new CustomException(500, "Query stories failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,128 @@
|
|||||||
|
package com.timeline.story.service.impl;
|
||||||
|
|
||||||
|
import com.timeline.common.exception.CustomException;
|
||||||
|
import com.timeline.common.response.ResponseEnum;
|
||||||
|
import com.timeline.common.utils.IdUtils;
|
||||||
|
import com.timeline.story.dao.StoryItemMapper;
|
||||||
|
import com.timeline.story.dao.StoryShareFeedbackMapper;
|
||||||
|
import com.timeline.story.entity.StoryItem;
|
||||||
|
import com.timeline.story.entity.StoryShareComment;
|
||||||
|
import com.timeline.story.entity.StoryShareMetric;
|
||||||
|
import com.timeline.story.service.StoryShareFeedbackService;
|
||||||
|
import com.timeline.story.vo.StoryShareCommentRequest;
|
||||||
|
import com.timeline.story.vo.StoryShareCommentVo;
|
||||||
|
import com.timeline.story.vo.StoryShareFeedbackVo;
|
||||||
|
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.LocalDateTime;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class StoryShareFeedbackServiceImpl implements StoryShareFeedbackService {
|
||||||
|
|
||||||
|
private static final int DEFAULT_LIMIT = 8;
|
||||||
|
private static final int MAX_LIMIT = 20;
|
||||||
|
private static final int MAX_VISITOR_NAME_LENGTH = 40;
|
||||||
|
private static final int MAX_COMMENT_LENGTH = 280;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private StoryItemMapper storyItemMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private StoryShareFeedbackMapper storyShareFeedbackMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StoryShareFeedbackVo getFeedback(String shareId, int limit) {
|
||||||
|
validatePublicShare(shareId);
|
||||||
|
storyShareFeedbackMapper.ensureMetricRow(shareId);
|
||||||
|
return buildFeedbackVo(shareId, limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public StoryShareFeedbackVo recordView(String shareId, int limit) {
|
||||||
|
validatePublicShare(shareId);
|
||||||
|
storyShareFeedbackMapper.ensureMetricRow(shareId);
|
||||||
|
storyShareFeedbackMapper.incrementViewCount(shareId);
|
||||||
|
return buildFeedbackVo(shareId, limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public StoryShareFeedbackVo createComment(String shareId, StoryShareCommentRequest request, int limit) {
|
||||||
|
validatePublicShare(shareId);
|
||||||
|
|
||||||
|
String visitorName = normalizeVisitorName(request == null ? null : request.getVisitorName());
|
||||||
|
String content = normalizeContent(request == null ? null : request.getContent());
|
||||||
|
|
||||||
|
StoryShareComment comment = new StoryShareComment();
|
||||||
|
comment.setInstanceId(IdUtils.randomUuid());
|
||||||
|
comment.setShareId(shareId);
|
||||||
|
comment.setVisitorName(visitorName);
|
||||||
|
comment.setContent(content);
|
||||||
|
comment.setCreateTime(LocalDateTime.now());
|
||||||
|
comment.setUpdateTime(comment.getCreateTime());
|
||||||
|
comment.setIsDelete(0);
|
||||||
|
|
||||||
|
storyShareFeedbackMapper.ensureMetricRow(shareId);
|
||||||
|
storyShareFeedbackMapper.insertComment(comment);
|
||||||
|
storyShareFeedbackMapper.incrementCommentCount(shareId);
|
||||||
|
return buildFeedbackVo(shareId, limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private StoryShareFeedbackVo buildFeedbackVo(String shareId, int limit) {
|
||||||
|
StoryShareMetric metric = storyShareFeedbackMapper.selectMetricByShareId(shareId);
|
||||||
|
List<StoryShareCommentVo> comments = storyShareFeedbackMapper.selectRecentComments(shareId, normalizeLimit(limit));
|
||||||
|
|
||||||
|
StoryShareFeedbackVo feedback = new StoryShareFeedbackVo();
|
||||||
|
feedback.setShareId(shareId);
|
||||||
|
feedback.setViewCount(metric == null || metric.getViewCount() == null ? 0L : metric.getViewCount());
|
||||||
|
feedback.setCommentCount(metric == null || metric.getCommentCount() == null ? 0L : metric.getCommentCount());
|
||||||
|
feedback.setComments(comments == null ? Collections.emptyList() : comments);
|
||||||
|
return feedback;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validatePublicShare(String shareId) {
|
||||||
|
if (!StringUtils.hasText(shareId)) {
|
||||||
|
throw new CustomException(ResponseEnum.BAD_REQUEST, "Share id is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
StoryItem item = storyItemMapper.selectByShareId(shareId);
|
||||||
|
if (item == null || !StringUtils.hasText(item.getShareId())) {
|
||||||
|
throw new CustomException(ResponseEnum.NOT_FOUND, "Public share not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int normalizeLimit(int limit) {
|
||||||
|
if (limit <= 0) {
|
||||||
|
return DEFAULT_LIMIT;
|
||||||
|
}
|
||||||
|
return Math.min(limit, MAX_LIMIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizeVisitorName(String visitorName) {
|
||||||
|
String normalized = StringUtils.hasText(visitorName) ? visitorName.trim() : "Guest";
|
||||||
|
if (normalized.length() > MAX_VISITOR_NAME_LENGTH) {
|
||||||
|
normalized = normalized.substring(0, MAX_VISITOR_NAME_LENGTH);
|
||||||
|
}
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizeContent(String content) {
|
||||||
|
if (!StringUtils.hasText(content)) {
|
||||||
|
throw new CustomException(ResponseEnum.BAD_REQUEST, "Comment content is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
String normalized = content.trim();
|
||||||
|
if (normalized.length() > MAX_COMMENT_LENGTH) {
|
||||||
|
normalized = normalized.substring(0, MAX_COMMENT_LENGTH);
|
||||||
|
}
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,4 +13,7 @@ public class StoryDetailVo extends Story {
|
|||||||
// 新增字段:故事项数量
|
// 新增字段:故事项数量
|
||||||
private Integer itemCount;
|
private Integer itemCount;
|
||||||
private Integer permissionType;
|
private Integer permissionType;
|
||||||
|
private String publicShareId;
|
||||||
|
private Boolean shareConfigured;
|
||||||
|
private Boolean sharePublished;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ public class StoryItemShareVo extends StoryItem {
|
|||||||
private String shareTitle;
|
private String shareTitle;
|
||||||
private String shareDescription;
|
private String shareDescription;
|
||||||
private String shareQuote;
|
private String shareQuote;
|
||||||
|
private String templateStyle;
|
||||||
private String heroMomentId;
|
private String heroMomentId;
|
||||||
private List<String> images;
|
private List<String> images;
|
||||||
private List<String> relatedImageInstanceIds;
|
private List<String> relatedImageInstanceIds;
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.timeline.story.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class StoryShareCommentRequest {
|
||||||
|
private String visitorName;
|
||||||
|
private String content;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.timeline.story.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class StoryShareCommentVo {
|
||||||
|
private String instanceId;
|
||||||
|
private String shareId;
|
||||||
|
private String visitorName;
|
||||||
|
private String content;
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
}
|
||||||
@@ -8,12 +8,17 @@ import java.util.List;
|
|||||||
@Data
|
@Data
|
||||||
public class StoryShareConfigVo {
|
public class StoryShareConfigVo {
|
||||||
private String shareId;
|
private String shareId;
|
||||||
|
private String publicShareId;
|
||||||
private String storyId;
|
private String storyId;
|
||||||
private String heroMomentId;
|
private String heroMomentId;
|
||||||
private String title;
|
private String title;
|
||||||
private String description;
|
private String description;
|
||||||
private String quote;
|
private String quote;
|
||||||
|
private String templateStyle;
|
||||||
private List<String> featuredMomentIds;
|
private List<String> featuredMomentIds;
|
||||||
private Boolean published;
|
private Boolean published;
|
||||||
private LocalDateTime updatedAt;
|
private LocalDateTime updatedAt;
|
||||||
|
private Long viewCount;
|
||||||
|
private Long commentCount;
|
||||||
|
private List<StoryShareCommentVo> recentComments;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.timeline.story.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class StoryShareFeedbackVo {
|
||||||
|
private String shareId;
|
||||||
|
private Long viewCount;
|
||||||
|
private Long commentCount;
|
||||||
|
private List<StoryShareCommentVo> comments;
|
||||||
|
}
|
||||||
@@ -11,5 +11,6 @@ public class StorySharePublishRequest {
|
|||||||
private String title;
|
private String title;
|
||||||
private String description;
|
private String description;
|
||||||
private String quote;
|
private String quote;
|
||||||
|
private String templateStyle;
|
||||||
private List<String> featuredMomentIds;
|
private List<String> featuredMomentIds;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ public class StorySharePublishVo {
|
|||||||
private String storyId;
|
private String storyId;
|
||||||
private String shareId;
|
private String shareId;
|
||||||
private String heroMomentId;
|
private String heroMomentId;
|
||||||
|
private String templateStyle;
|
||||||
private String publicPath;
|
private String publicPath;
|
||||||
private LocalDateTime publishedAt;
|
private LocalDateTime publishedAt;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ public class TimelineArchiveBucketVo {
|
|||||||
private String subtitle;
|
private String subtitle;
|
||||||
private String storyInstanceId;
|
private String storyInstanceId;
|
||||||
private String storyShareId;
|
private String storyShareId;
|
||||||
|
private Boolean shareConfigured;
|
||||||
|
private Boolean sharePublished;
|
||||||
private String coverInstanceId;
|
private String coverInstanceId;
|
||||||
private String coverSrc;
|
private String coverSrc;
|
||||||
private String sampleMomentTitle;
|
private String sampleMomentTitle;
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ public class TimelineArchiveMomentVo {
|
|||||||
private String key;
|
private String key;
|
||||||
private String storyInstanceId;
|
private String storyInstanceId;
|
||||||
private String storyShareId;
|
private String storyShareId;
|
||||||
|
private Boolean shareConfigured;
|
||||||
|
private Boolean sharePublished;
|
||||||
private String storyTitle;
|
private String storyTitle;
|
||||||
private String storyTime;
|
private String storyTime;
|
||||||
private String itemInstanceId;
|
private String itemInstanceId;
|
||||||
|
|||||||
@@ -40,6 +40,40 @@
|
|||||||
ORDER BY si.update_time DESC, si.create_time DESC
|
ORDER BY si.update_time DESC, si.create_time DESC
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
) AS share_id,
|
) AS share_id,
|
||||||
|
(
|
||||||
|
SELECT si.share_id
|
||||||
|
FROM story_item si
|
||||||
|
WHERE si.story_instance_id = s.instance_id
|
||||||
|
AND si.share_id IS NOT NULL
|
||||||
|
AND si.is_delete = 0
|
||||||
|
ORDER BY si.update_time DESC, si.create_time DESC
|
||||||
|
LIMIT 1
|
||||||
|
) AS public_share_id,
|
||||||
|
CASE
|
||||||
|
WHEN EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM story_item si
|
||||||
|
WHERE si.story_instance_id = s.instance_id
|
||||||
|
AND si.share_id IS NOT NULL
|
||||||
|
AND si.is_delete = 0
|
||||||
|
) THEN 1
|
||||||
|
ELSE 0
|
||||||
|
END AS share_published,
|
||||||
|
CASE
|
||||||
|
WHEN EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM story_share ss
|
||||||
|
WHERE ss.story_instance_id = s.instance_id
|
||||||
|
AND ss.is_delete = 0
|
||||||
|
) OR EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM story_item si
|
||||||
|
WHERE si.story_instance_id = s.instance_id
|
||||||
|
AND si.share_id IS NOT NULL
|
||||||
|
AND si.is_delete = 0
|
||||||
|
) THEN 1
|
||||||
|
ELSE 0
|
||||||
|
END AS share_configured,
|
||||||
u1.username AS owner_name,
|
u1.username AS owner_name,
|
||||||
u2.username AS update_name,
|
u2.username AS update_name,
|
||||||
sp.permission_type AS permission_type,
|
sp.permission_type AS permission_type,
|
||||||
@@ -70,6 +104,40 @@
|
|||||||
ORDER BY si.update_time DESC, si.create_time DESC
|
ORDER BY si.update_time DESC, si.create_time DESC
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
) AS share_id,
|
) AS share_id,
|
||||||
|
(
|
||||||
|
SELECT si.share_id
|
||||||
|
FROM story_item si
|
||||||
|
WHERE si.story_instance_id = s.instance_id
|
||||||
|
AND si.share_id IS NOT NULL
|
||||||
|
AND si.is_delete = 0
|
||||||
|
ORDER BY si.update_time DESC, si.create_time DESC
|
||||||
|
LIMIT 1
|
||||||
|
) AS public_share_id,
|
||||||
|
CASE
|
||||||
|
WHEN EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM story_item si
|
||||||
|
WHERE si.story_instance_id = s.instance_id
|
||||||
|
AND si.share_id IS NOT NULL
|
||||||
|
AND si.is_delete = 0
|
||||||
|
) THEN 1
|
||||||
|
ELSE 0
|
||||||
|
END AS share_published,
|
||||||
|
CASE
|
||||||
|
WHEN EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM story_share ss
|
||||||
|
WHERE ss.story_instance_id = s.instance_id
|
||||||
|
AND ss.is_delete = 0
|
||||||
|
) OR EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM story_item si
|
||||||
|
WHERE si.story_instance_id = s.instance_id
|
||||||
|
AND si.share_id IS NOT NULL
|
||||||
|
AND si.is_delete = 0
|
||||||
|
) THEN 1
|
||||||
|
ELSE 0
|
||||||
|
END AS share_configured,
|
||||||
u1.username AS owner_name,
|
u1.username AS owner_name,
|
||||||
u2.username AS update_name,
|
u2.username AS update_name,
|
||||||
sp.permission_type AS permission_type,
|
sp.permission_type AS permission_type,
|
||||||
@@ -99,4 +167,4 @@
|
|||||||
WHERE instance_id = #{instanceId}
|
WHERE instance_id = #{instanceId}
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
|
||||||
|
<mapper namespace="com.timeline.story.dao.StoryShareFeedbackMapper">
|
||||||
|
|
||||||
|
<insert id="ensureMetricRow">
|
||||||
|
INSERT INTO story_share_metric (share_id, view_count, comment_count, create_time, update_time)
|
||||||
|
VALUES (#{shareId}, 0, 0, NOW(), NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE update_time = VALUES(update_time)
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<update id="incrementViewCount">
|
||||||
|
UPDATE story_share_metric
|
||||||
|
SET view_count = view_count + 1,
|
||||||
|
update_time = NOW()
|
||||||
|
WHERE share_id = #{shareId}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<update id="incrementCommentCount">
|
||||||
|
UPDATE story_share_metric
|
||||||
|
SET comment_count = comment_count + 1,
|
||||||
|
update_time = NOW()
|
||||||
|
WHERE share_id = #{shareId}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<select id="selectMetricByShareId" resultType="com.timeline.story.entity.StoryShareMetric">
|
||||||
|
SELECT *
|
||||||
|
FROM story_share_metric
|
||||||
|
WHERE share_id = #{shareId}
|
||||||
|
LIMIT 1
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<insert id="insertComment">
|
||||||
|
INSERT INTO story_share_comment (
|
||||||
|
instance_id,
|
||||||
|
share_id,
|
||||||
|
visitor_name,
|
||||||
|
content,
|
||||||
|
create_time,
|
||||||
|
update_time,
|
||||||
|
is_delete
|
||||||
|
) VALUES (
|
||||||
|
#{instanceId},
|
||||||
|
#{shareId},
|
||||||
|
#{visitorName},
|
||||||
|
#{content},
|
||||||
|
#{createTime},
|
||||||
|
#{updateTime},
|
||||||
|
#{isDelete}
|
||||||
|
)
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<select id="selectRecentComments" resultType="com.timeline.story.vo.StoryShareCommentVo">
|
||||||
|
SELECT
|
||||||
|
instance_id,
|
||||||
|
share_id,
|
||||||
|
visitor_name,
|
||||||
|
content,
|
||||||
|
create_time
|
||||||
|
FROM story_share_comment
|
||||||
|
WHERE share_id = #{shareId}
|
||||||
|
AND is_delete = 0
|
||||||
|
ORDER BY create_time DESC, id DESC
|
||||||
|
LIMIT #{limit}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
@@ -37,6 +37,7 @@
|
|||||||
share_title,
|
share_title,
|
||||||
share_description,
|
share_description,
|
||||||
share_quote,
|
share_quote,
|
||||||
|
template_style,
|
||||||
featured_moment_ids,
|
featured_moment_ids,
|
||||||
create_id,
|
create_id,
|
||||||
update_id,
|
update_id,
|
||||||
@@ -50,6 +51,7 @@
|
|||||||
#{shareTitle},
|
#{shareTitle},
|
||||||
#{shareDescription},
|
#{shareDescription},
|
||||||
#{shareQuote},
|
#{shareQuote},
|
||||||
|
#{templateStyle},
|
||||||
#{featuredMomentIds},
|
#{featuredMomentIds},
|
||||||
#{createId},
|
#{createId},
|
||||||
#{updateId},
|
#{updateId},
|
||||||
@@ -66,6 +68,7 @@
|
|||||||
share_title = #{shareTitle},
|
share_title = #{shareTitle},
|
||||||
share_description = #{shareDescription},
|
share_description = #{shareDescription},
|
||||||
share_quote = #{shareQuote},
|
share_quote = #{shareQuote},
|
||||||
|
template_style = #{templateStyle},
|
||||||
featured_moment_ids = #{featuredMomentIds},
|
featured_moment_ids = #{featuredMomentIds},
|
||||||
update_id = #{updateId},
|
update_id = #{updateId},
|
||||||
update_time = #{updateTime},
|
update_time = #{updateTime},
|
||||||
|
|||||||
Reference in New Issue
Block a user