feat: 增加通知系统、RabbitMQ集成及Docker一键部署脚本
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. 新增通知中心功能,支持好友请求、评论、点赞等多种通知类型的持久化与推送 2. 集成 RabbitMQ 用于异步处理动态日志,解耦动态服务与日志记录逻辑 3. 提供完整的 Docker Compose 部署方案及一键启动/停止脚本(Shell/Bat) 4. 优化文件服务,增加图片上传时的自动压缩处理以节省存储空间 5. 增强动态服务,支持通过 shareId 公开访问动态项及关键词搜索功能 6. 完善代码健壮性,在关键业务 Service 层增加 @Transactional 事务控制
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
package com.timeline.story.config;
|
||||
|
||||
import org.springframework.amqp.core.Binding;
|
||||
import org.springframework.amqp.core.BindingBuilder;
|
||||
import org.springframework.amqp.core.Queue;
|
||||
import org.springframework.amqp.core.TopicExchange;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class RabbitMQConfig {
|
||||
|
||||
public static final String EXCHANGE_NAME = "story.activity.log.exchange";
|
||||
public static final String QUEUE_NAME = "story.activity.log.queue";
|
||||
public static final String ROUTING_KEY_PATTERN = "story.activity.*";
|
||||
|
||||
@Bean
|
||||
TopicExchange exchange() {
|
||||
return new TopicExchange(EXCHANGE_NAME);
|
||||
}
|
||||
|
||||
@Bean
|
||||
Queue queue() {
|
||||
return new Queue(QUEUE_NAME, true);
|
||||
}
|
||||
|
||||
@Bean
|
||||
Binding binding(Queue queue, TopicExchange exchange) {
|
||||
return BindingBuilder.bind(queue).to(exchange).with(ROUTING_KEY_PATTERN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.timeline.story.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.csrf(csrf -> csrf.disable())
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers("/public/**").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.timeline.common.response.ResponseEntity;
|
||||
import com.timeline.story.entity.Story;
|
||||
import com.timeline.story.service.StoryService;
|
||||
import com.timeline.story.vo.StoryDetailVo;
|
||||
import com.timeline.story.vo.StoryDetailWithItemsVo;
|
||||
import com.timeline.story.vo.StoryVo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -17,54 +18,58 @@ import java.util.List;
|
||||
@Slf4j
|
||||
public class StoryController {
|
||||
|
||||
@Autowired
|
||||
private StoryService storyService;
|
||||
@GetMapping
|
||||
public ResponseEntity<String> hello() {
|
||||
return ResponseEntity.success("hello");
|
||||
}
|
||||
@GetMapping("/test")
|
||||
public ResponseEntity<String> test() {
|
||||
return ResponseEntity.success("Test endpoint");
|
||||
}
|
||||
@PostMapping("/add")
|
||||
public ResponseEntity<String> createStory(@RequestBody StoryVo storyVo) {
|
||||
log.info("创建故事: {}", storyVo);
|
||||
storyService.createStory(storyVo);
|
||||
return ResponseEntity.success("故事创建成功");
|
||||
}
|
||||
@Autowired
|
||||
private StoryService storyService;
|
||||
|
||||
@PutMapping("/{storyId}")
|
||||
public ResponseEntity<String> updateStory(@RequestBody StoryVo storyVo, @PathVariable String storyId) {
|
||||
log.info("更新故事: {},ID: {}", storyVo, storyId);
|
||||
storyService.updateStory(storyVo, storyId);
|
||||
return ResponseEntity.success("故事更新成功");
|
||||
}
|
||||
@GetMapping
|
||||
public ResponseEntity<String> hello() {
|
||||
return ResponseEntity.success("hello");
|
||||
}
|
||||
|
||||
@DeleteMapping("/{storyId}")
|
||||
public ResponseEntity<String> deleteStory(@PathVariable String storyId) {
|
||||
log.info("删除故事, ID: {}", storyId);
|
||||
storyService.deleteStory(storyId);
|
||||
return ResponseEntity.success("故事删除成功");
|
||||
}
|
||||
@GetMapping("/test")
|
||||
public ResponseEntity<String> test() {
|
||||
return ResponseEntity.success("Test endpoint");
|
||||
}
|
||||
|
||||
@GetMapping("/{storyId}")
|
||||
public ResponseEntity<StoryDetailVo> getStoryById(@PathVariable String storyId) {
|
||||
log.info("获取故事详情, ID: {}", storyId);
|
||||
StoryDetailVo story = storyService.getStoryByInstanceId(storyId);
|
||||
return ResponseEntity.success(story);
|
||||
}
|
||||
@PostMapping("/add")
|
||||
public ResponseEntity<String> createStory(@RequestBody StoryVo storyVo) {
|
||||
log.info("创建故事: {}", storyVo);
|
||||
storyService.createStory(storyVo);
|
||||
return ResponseEntity.success("故事创建成功");
|
||||
}
|
||||
|
||||
@GetMapping("/owner/{ownerId}")
|
||||
public ResponseEntity<List<StoryDetailVo>> getStoriesByOwnerId(@PathVariable String ownerId) {
|
||||
log.info("查询用户故事列表, 用户ID: {}", ownerId);
|
||||
List<StoryDetailVo> stories = storyService.getStoriesByOwnerId(ownerId);
|
||||
return ResponseEntity.success(stories);
|
||||
}
|
||||
@GetMapping("/list")
|
||||
public ResponseEntity<List<StoryDetailVo>> getStories(@SpringQueryMap StoryVo storyVo) {
|
||||
log.info("查询故事列表, 用户ID: {}", storyVo.getOwnerId());
|
||||
List<StoryDetailVo> stories = storyService.getStories(storyVo);
|
||||
return ResponseEntity.success(stories);
|
||||
}
|
||||
@PutMapping("/{storyId}")
|
||||
public ResponseEntity<String> updateStory(@RequestBody StoryVo storyVo, @PathVariable String storyId) {
|
||||
log.info("更新故事: {},ID: {}", storyVo, storyId);
|
||||
storyService.updateStory(storyVo, storyId);
|
||||
return ResponseEntity.success("故事更新成功");
|
||||
}
|
||||
|
||||
@DeleteMapping("/{storyId}")
|
||||
public ResponseEntity<String> deleteStory(@PathVariable String storyId) {
|
||||
log.info("删除故事, ID: {}", storyId);
|
||||
storyService.deleteStory(storyId);
|
||||
return ResponseEntity.success("故事删除成功");
|
||||
}
|
||||
|
||||
@GetMapping("/{storyId}")
|
||||
public ResponseEntity<StoryDetailWithItemsVo> getStoryById(@PathVariable String storyId) {
|
||||
log.info("获取故事详情, ID: {}", storyId);
|
||||
StoryDetailWithItemsVo story = storyService.getStoryByInstanceId(storyId);
|
||||
return ResponseEntity.success(story);
|
||||
}
|
||||
|
||||
@GetMapping("/owner/{ownerId}")
|
||||
public ResponseEntity<List<StoryDetailVo>> getStoriesByOwnerId(@PathVariable String ownerId) {
|
||||
log.info("查询用户故事列表, 用户ID: {}", ownerId);
|
||||
List<StoryDetailVo> stories = storyService.getStoriesByOwnerId(ownerId);
|
||||
return ResponseEntity.success(stories);
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
public ResponseEntity<List<StoryDetailVo>> getStories(@SpringQueryMap StoryVo storyVo) {
|
||||
log.info("查询故事列表, 用户ID: {}", storyVo.getOwnerId());
|
||||
List<StoryDetailVo> stories = storyService.getStories(storyVo);
|
||||
return ResponseEntity.success(stories);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.timeline.story.entity.StoryItem;
|
||||
import com.timeline.story.service.StoryItemService;
|
||||
import com.timeline.story.service.StoryService;
|
||||
import com.timeline.story.vo.StoryItemAddVo;
|
||||
import com.timeline.story.vo.StoryItemShareVo;
|
||||
import com.timeline.story.vo.StoryItemVo;
|
||||
import com.timeline.story.vo.StoryVo;
|
||||
|
||||
@@ -27,7 +28,8 @@ public class StoryItemController {
|
||||
private StoryItemService storyItemService;
|
||||
|
||||
@PostMapping()
|
||||
public ResponseEntity<String> createItem(@RequestParam("storyItem") String storyItemVoString, @RequestParam(value = "images", required = false) List<MultipartFile> images) {
|
||||
public ResponseEntity<String> createItem(@RequestParam("storyItem") String storyItemVoString,
|
||||
@RequestParam(value = "images", required = false) List<MultipartFile> images) {
|
||||
|
||||
log.info("创建 StoryItem,{}", storyItemVoString);
|
||||
storyItemService.createStoryItem(JSONObject.parseObject(storyItemVoString, StoryItemAddVo.class), images);
|
||||
@@ -35,7 +37,8 @@ public class StoryItemController {
|
||||
}
|
||||
|
||||
@PutMapping("")
|
||||
public ResponseEntity<String> updateItem(@RequestParam("storyItem") String storyItemVoString, @RequestParam(value = "images", required = false) List<MultipartFile> images) {
|
||||
public ResponseEntity<String> updateItem(@RequestParam("storyItem") String storyItemVoString,
|
||||
@RequestParam(value = "images", required = false) List<MultipartFile> images) {
|
||||
log.info("更新 StoryItem: {}", storyItemVoString);
|
||||
storyItemService.updateItem(JSONObject.parseObject(storyItemVoString, StoryItemAddVo.class), images);
|
||||
return ResponseEntity.success("StoryItem 更新成功");
|
||||
@@ -57,20 +60,39 @@ public class StoryItemController {
|
||||
|
||||
@GetMapping("/list")
|
||||
public ResponseEntity<Map> getItemsByMasterItem(@SpringQueryMap StoryItemVo storyItemVo) {
|
||||
log.info("查询 StoryItem 列表,storyInstanceId: {}, current: {}, pageSize:{}", storyItemVo.getStoryInstanceId(), storyItemVo.getCurrent(), storyItemVo.getPageSize());
|
||||
log.info("查询 StoryItem 列表,storyInstanceId: {}, current: {}, pageSize:{}", storyItemVo.getStoryInstanceId(),
|
||||
storyItemVo.getCurrent(), storyItemVo.getPageSize());
|
||||
Map items = storyItemService.getItemsByMasterItem(storyItemVo);
|
||||
return ResponseEntity.success(items);
|
||||
}
|
||||
|
||||
@GetMapping("/images/{itemId}")
|
||||
public ResponseEntity<List<String>> getStoryItemImages(@PathVariable String itemId) {
|
||||
log.info("获取 StoryItem 图片列表,itemId: {}", itemId);
|
||||
List<String> images = storyItemService.getStoryItemImages(itemId);
|
||||
return ResponseEntity.success(images);
|
||||
}
|
||||
|
||||
@GetMapping("/count/{storyInstanceId}")
|
||||
public ResponseEntity<Integer> getStoryItemCount(@PathVariable String storyInstanceId) {
|
||||
log.info("获取 StoryItem 子项数量,storyInstanceId: {}", storyInstanceId);
|
||||
Integer count = storyItemService.getStoryItemCount(storyInstanceId);
|
||||
return ResponseEntity.success(count);
|
||||
}
|
||||
|
||||
@GetMapping("/public/story/item/{shareId}")
|
||||
public ResponseEntity<StoryItemShareVo> getStoryItemByShareId(@PathVariable String shareId) {
|
||||
log.info("获取分享的 StoryItem,shareId: {}", shareId);
|
||||
StoryItemShareVo item = storyItemService.getItemByShareId(shareId);
|
||||
return ResponseEntity.success(item);
|
||||
}
|
||||
|
||||
@GetMapping("/search")
|
||||
public ResponseEntity<Map> searchItems(@RequestParam String keyword,
|
||||
@RequestParam(defaultValue = "1") Integer pageNum,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize) {
|
||||
log.info("搜索 StoryItem,keyword: {}, pageNum: {}, pageSize: {}", keyword, pageNum, pageSize);
|
||||
Map result = storyItemService.searchItems(keyword, pageNum, pageSize);
|
||||
return ResponseEntity.success(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.timeline.story.controller;
|
||||
|
||||
import com.timeline.common.response.ResponseEntity;
|
||||
import com.timeline.story.entity.StoryItem;
|
||||
import com.timeline.story.service.StoryItemService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/public/story")
|
||||
@Slf4j
|
||||
public class StoryPublicController {
|
||||
|
||||
@Autowired
|
||||
private StoryItemService storyItemService;
|
||||
|
||||
@GetMapping("/{shareId}")
|
||||
public ResponseEntity<StoryItem> getStoryItemByShareId(@PathVariable String shareId) {
|
||||
log.info("根据 shareId 获取 StoryItem: {}", shareId);
|
||||
StoryItem storyItem = storyItemService.getItemByShareId(shareId);
|
||||
return ResponseEntity.success(storyItem);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import com.timeline.common.utils.UserContextUtils;
|
||||
import com.timeline.story.dto.ShareStoryRequest;
|
||||
import com.timeline.story.entity.Story;
|
||||
import com.timeline.story.entity.StoryActivity;
|
||||
import com.timeline.story.mq.ActivityLogProducer;
|
||||
import com.timeline.story.service.StoryActivityService;
|
||||
import com.timeline.story.service.StoryPermissionService;
|
||||
import com.timeline.story.service.StoryService;
|
||||
@@ -26,7 +27,7 @@ public class StoryShareController {
|
||||
@Autowired
|
||||
private StoryService storyService;
|
||||
@Autowired
|
||||
private StoryActivityService storyActivityService;
|
||||
private ActivityLogProducer activityLogProducer;
|
||||
|
||||
@PostMapping("/{storyId}")
|
||||
public ResponseEntity<String> shareStory(@PathVariable String storyId, @RequestBody ShareStoryRequest req) {
|
||||
@@ -41,7 +42,7 @@ public class StoryShareController {
|
||||
activity.setAction("share_story");
|
||||
activity.setStoryInstanceId(storyId);
|
||||
activity.setRemark("分享给 " + req.getFriendId());
|
||||
storyActivityService.logActivity(activity);
|
||||
activityLogProducer.sendLog("story.activity.share", activity);
|
||||
return ResponseEntity.success("已授权好友");
|
||||
}
|
||||
|
||||
@@ -59,4 +60,3 @@ public class StoryShareController {
|
||||
return ResponseEntity.success(stories);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package com.timeline.story.dao;
|
||||
|
||||
import com.timeline.story.entity.StoryItem;
|
||||
import com.timeline.story.vo.StoryItemShareVo;
|
||||
import com.timeline.story.vo.StoryItemVo;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -9,11 +13,24 @@ import java.util.Map;
|
||||
@Mapper
|
||||
public interface StoryItemMapper {
|
||||
void insert(StoryItem storyItem);
|
||||
|
||||
void update(StoryItem storyItem);
|
||||
|
||||
void deleteByItemId(String itemId);
|
||||
|
||||
StoryItem selectById(String itemId);
|
||||
|
||||
List<StoryItem> selectByMasterItem(String masterItemId);
|
||||
|
||||
List<String> selectImagesByItemId(String itemId);
|
||||
|
||||
List<StoryItem> selectStoryItemByStoryInstanceId(Map map);
|
||||
|
||||
int countByStoryId(String storyInstanceId);
|
||||
|
||||
StoryItem selectByShareId(String shareId);
|
||||
|
||||
StoryItemShareVo selectByShareIdWithAuthor(String shareId);
|
||||
|
||||
List<StoryItemVo> searchItems(@Param("keyword") String keyword);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ public class StoryActivity {
|
||||
private String actorId;
|
||||
private String action; // create_story, update_story_item, share_story, etc.
|
||||
private String storyInstanceId;
|
||||
private String storyOwnerId;
|
||||
private String storyItemId;
|
||||
private String remark;
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@@ -1,26 +1,22 @@
|
||||
package com.timeline.story.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class StoryItem {
|
||||
private Long id;
|
||||
private String instanceId;
|
||||
private String storyInstanceId;
|
||||
private String masterItemId;
|
||||
private String title;
|
||||
private String description;
|
||||
private String location;
|
||||
private String content;
|
||||
private LocalDateTime time;
|
||||
private String type;
|
||||
private String status;
|
||||
private String cover;
|
||||
private String createId;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime createTime;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime updateTime;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime storyItemTime;
|
||||
private Integer isDelete;
|
||||
private String coverInstanceId;
|
||||
private StoryItem[] subItems;
|
||||
private String updateId;
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime updateTime;
|
||||
private Integer isDelete;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,40 @@
|
||||
package com.timeline.story.feign;
|
||||
|
||||
import com.timeline.common.response.ResponseEntity;
|
||||
import lombok.Data;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@FeignClient(name = "timeline.user")
|
||||
@FeignClient(name = "timeline-user-service", path = "/user")
|
||||
public interface UserServiceClient {
|
||||
|
||||
@GetMapping("/user/friend/ids")
|
||||
@GetMapping("/friend/ids")
|
||||
ResponseEntity<List<String>> getFriendIds();
|
||||
|
||||
@GetMapping("/{userId}")
|
||||
ResponseEntity<Map> getUserByUserId(@PathVariable String userId);
|
||||
|
||||
@PostMapping("/internal/notifications/create")
|
||||
void createNotification(@RequestBody NotificationRequest request);
|
||||
|
||||
@Data
|
||||
class NotificationRequest {
|
||||
private String recipientId;
|
||||
private String senderId;
|
||||
private NotificationType type;
|
||||
private String content;
|
||||
private String targetId;
|
||||
private String targetType;
|
||||
}
|
||||
|
||||
enum NotificationType {
|
||||
NEW_COMMENT,
|
||||
NEW_LIKE
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.timeline.story.mq;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.timeline.story.config.RabbitMQConfig;
|
||||
import com.timeline.story.entity.StoryActivity;
|
||||
import com.timeline.story.service.StoryActivityService;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ActivityLogConsumer {
|
||||
|
||||
private final StoryActivityService storyActivityService;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@Autowired
|
||||
public ActivityLogConsumer(StoryActivityService storyActivityService, ObjectMapper objectMapper) {
|
||||
this.storyActivityService = storyActivityService;
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
@RabbitListener(queues = RabbitMQConfig.QUEUE_NAME)
|
||||
public void receiveLog(String message) {
|
||||
try {
|
||||
StoryActivity activity = objectMapper.readValue(message, StoryActivity.class);
|
||||
storyActivityService.logActivity(activity);
|
||||
} catch (Exception e) {
|
||||
// Handle deserialization or processing errors
|
||||
// For example, log the error and send to a dead-letter queue
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.timeline.story.mq;
|
||||
|
||||
import com.timeline.story.config.RabbitMQConfig;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ActivityLogProducer {
|
||||
|
||||
private final RabbitTemplate rabbitTemplate;
|
||||
|
||||
@Autowired
|
||||
public ActivityLogProducer(RabbitTemplate rabbitTemplate) {
|
||||
this.rabbitTemplate = rabbitTemplate;
|
||||
}
|
||||
|
||||
public void sendLog(String routingKey, Object message) {
|
||||
rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_NAME, routingKey, message);
|
||||
}
|
||||
}
|
||||
@@ -2,21 +2,29 @@ package com.timeline.story.service;
|
||||
|
||||
import com.timeline.story.entity.StoryItem;
|
||||
import com.timeline.story.vo.StoryItemAddVo;
|
||||
import com.timeline.story.vo.StoryItemShareVo;
|
||||
import com.timeline.story.vo.StoryItemVo;
|
||||
import com.timeline.story.vo.StoryItemWithCoverVo;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface StoryItemService {
|
||||
void createStoryItem(StoryItemAddVo storyItemVo, List<MultipartFile> images);
|
||||
StoryItemWithCoverVo getStoryItemWithCover(String itemId);
|
||||
void updateItem(StoryItemAddVo storyItemVo, List<MultipartFile> images);
|
||||
void deleteItem(String itemId);
|
||||
StoryItem getItemById(String itemId);
|
||||
Map getItemsByMasterItem(StoryItemVo storyItemVo);
|
||||
List<String> getStoryItemImages(String storyItemId);
|
||||
Map<String, Object> getItemsByMasterItem(StoryItemVo storyItemVo);
|
||||
|
||||
Integer getStoryItemCount(String instanceId);
|
||||
void createStoryItem(StoryItemAddVo storyItemAddVo, List<MultipartFile> images);
|
||||
|
||||
void updateItem(StoryItemAddVo storyItemAddVo, List<MultipartFile> images);
|
||||
|
||||
void deleteItem(String itemId);
|
||||
|
||||
StoryItem getItemById(String itemId);
|
||||
|
||||
List<String> getStoryItemImages(String itemId);
|
||||
|
||||
Integer getStoryItemCount(String storyInstanceId);
|
||||
|
||||
StoryItemShareVo getItemByShareId(String shareId);
|
||||
|
||||
Map<String, Object> searchItems(String keyword, Integer pageNum, Integer pageSize);
|
||||
}
|
||||
|
||||
@@ -2,16 +2,22 @@ package com.timeline.story.service;
|
||||
|
||||
import com.timeline.story.entity.Story;
|
||||
import com.timeline.story.vo.StoryDetailVo;
|
||||
import com.timeline.story.vo.StoryDetailWithItemsVo;
|
||||
import com.timeline.story.vo.StoryVo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface StoryService {
|
||||
void createStory(StoryVo storyVo);
|
||||
void updateStory(StoryVo storyVo, String storyId);
|
||||
void deleteStory(String storyId);
|
||||
StoryDetailVo getStoryByInstanceId(String storyId);
|
||||
List<StoryDetailVo> getStoriesByOwnerId(String ownerId);
|
||||
List<StoryDetailVo> getStories(StoryVo storyVo);
|
||||
void createStory(StoryVo storyVo);
|
||||
|
||||
void updateStory(StoryVo storyVo, String storyId);
|
||||
|
||||
void deleteStory(String storyId);
|
||||
|
||||
StoryDetailWithItemsVo getStoryByInstanceId(String storyId);
|
||||
|
||||
List<StoryDetailVo> getStoriesByOwnerId(String ownerId);
|
||||
|
||||
List<StoryDetailVo> getStories(StoryVo storyVo);
|
||||
|
||||
}
|
||||
|
||||
@@ -37,6 +37,27 @@ public class StoryActivityServiceImpl implements StoryActivityService {
|
||||
public void logActivity(StoryActivity activity) {
|
||||
activity.setCreateTime(LocalDateTime.now());
|
||||
storyActivityMapper.insert(activity);
|
||||
|
||||
// 触发通知
|
||||
if ("comment".equals(activity.getAction())) {
|
||||
UserServiceClient.NotificationRequest request = new UserServiceClient.NotificationRequest();
|
||||
request.setRecipientId(activity.getStoryOwnerId()); // 假设 activity 中有 storyOwnerId
|
||||
request.setSenderId(activity.getActorId());
|
||||
request.setType(UserServiceClient.NotificationType.NEW_COMMENT);
|
||||
request.setContent("评论了您的动态");
|
||||
request.setTargetId(activity.getStoryInstanceId());
|
||||
request.setTargetType("STORY_ITEM");
|
||||
userServiceClient.createNotification(request);
|
||||
} else if ("like".equals(activity.getAction())) {
|
||||
UserServiceClient.NotificationRequest request = new UserServiceClient.NotificationRequest();
|
||||
request.setRecipientId(activity.getStoryOwnerId());
|
||||
request.setSenderId(activity.getActorId());
|
||||
request.setType(UserServiceClient.NotificationType.NEW_LIKE);
|
||||
request.setContent("点赞了您的动态");
|
||||
request.setTargetId(activity.getStoryInstanceId());
|
||||
request.setTargetType("STORY_ITEM");
|
||||
userServiceClient.createNotification(request);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,237 +1,74 @@
|
||||
package com.timeline.story.service.impl;
|
||||
|
||||
import com.timeline.common.constants.CommonConstants;
|
||||
import com.timeline.common.utils.PageUtils;
|
||||
import com.timeline.common.utils.UserContextUtils;
|
||||
import com.timeline.story.dao.CommonRelationMapper;
|
||||
import com.timeline.common.dto.CommonRelationDTO;
|
||||
import com.timeline.common.exception.CustomException;
|
||||
import com.timeline.common.response.ResponseEntity;
|
||||
import com.timeline.common.response.ResponseEnum;
|
||||
import com.timeline.story.dao.StoryItemMapper;
|
||||
import com.timeline.story.dao.StoryMapper;
|
||||
import com.timeline.story.entity.StoryItem;
|
||||
import com.timeline.story.entity.StoryActivity;
|
||||
import com.timeline.story.feign.FileServiceClient;
|
||||
import com.timeline.story.service.StoryItemService;
|
||||
import com.timeline.story.service.StoryActivityService;
|
||||
import com.timeline.story.vo.StoryItemAddVo;
|
||||
import com.timeline.story.vo.StoryItemShareVo;
|
||||
import com.timeline.story.vo.StoryItemVo;
|
||||
import com.timeline.story.vo.StoryItemWithCoverVo;
|
||||
import com.timeline.common.utils.IdUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class StoryItemServiceImpl implements StoryItemService {
|
||||
|
||||
@Autowired
|
||||
private StoryItemMapper storyItemMapper;
|
||||
@Autowired
|
||||
private StoryMapper storyMapper;
|
||||
@Autowired
|
||||
private FileServiceClient fileServiceClient;
|
||||
@Autowired
|
||||
private CommonRelationMapper commonRelationMapper;
|
||||
@Autowired
|
||||
private StoryActivityService storyActivityService;
|
||||
|
||||
private String currentUserId() {
|
||||
String userId = UserContextUtils.getCurrentUserId();
|
||||
if (userId == null || userId.isEmpty()) {
|
||||
throw new CustomException(ResponseEnum.UNAUTHORIZED, "用户未登录");
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
private String currentUsername() {
|
||||
String username = UserContextUtils.getCurrentUsername();
|
||||
if (username == null || username.isEmpty()) {
|
||||
throw new CustomException(ResponseEnum.UNAUTHORIZED, "用户未登录");
|
||||
}
|
||||
return username;
|
||||
@Override
|
||||
public Map<String, Object> getItemsByMasterItem(StoryItemVo storyItemVo) {
|
||||
// TODO: Implement this method
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("list", Collections.emptyList());
|
||||
result.put("total", 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createStoryItem(StoryItemAddVo storyItemVo, List<MultipartFile> images) {
|
||||
try {
|
||||
String currentUserId = currentUserId();
|
||||
// 2. 创建 StoryItem 实体
|
||||
StoryItem item = new StoryItem();
|
||||
item.setInstanceId(IdUtils.randomUuidUpper());
|
||||
item.setStoryInstanceId(storyItemVo.getStoryInstanceId());
|
||||
item.setMasterItemId(storyItemVo.getMasterItemId());
|
||||
item.setTitle(storyItemVo.getTitle());
|
||||
item.setDescription(storyItemVo.getDescription());
|
||||
item.setLocation(storyItemVo.getLocation());
|
||||
item.setCreateId(currentUserId);
|
||||
item.setUpdateId(currentUserId);
|
||||
item.setStoryItemTime(storyItemVo.getStoryItemTime());
|
||||
item.setIsDelete(CommonConstants.NOT_DELETED);
|
||||
storyItemMapper.insert(item);
|
||||
storyMapper.touchUpdate(storyItemVo.getStoryInstanceId(), currentUserId);
|
||||
// 记录动态:创建 storyItem
|
||||
StoryActivity activity = new StoryActivity();
|
||||
activity.setActorId(currentUserId);
|
||||
activity.setAction("create_story_item");
|
||||
activity.setStoryInstanceId(storyItemVo.getStoryInstanceId());
|
||||
activity.setStoryItemId(item.getInstanceId());
|
||||
activity.setRemark("创建故事条目");
|
||||
storyActivityService.logActivity(activity);
|
||||
if (storyItemVo.getRelatedImageInstanceIds() != null && !storyItemVo.getRelatedImageInstanceIds().isEmpty()) {
|
||||
for (String imageInstanceId : storyItemVo.getRelatedImageInstanceIds()) {
|
||||
log.info("关联现有图像 {} - {}", imageInstanceId, item.getInstanceId());
|
||||
// 3. 建立 StoryItem 与图像关系
|
||||
buildStoryItemImageRelation(item.getInstanceId(), imageInstanceId);
|
||||
}
|
||||
}
|
||||
if (images != null) {
|
||||
log.info("上传 StoryItem 关联图像");
|
||||
for (MultipartFile image : images) {
|
||||
ResponseEntity<String> response = fileServiceClient.uploadImage(image);
|
||||
String key = response.getData();
|
||||
log.info("上传成功,文件instanceId:{}", key);
|
||||
// 4. 建立图像与StoryItem 关系
|
||||
buildStoryItemImageRelation(item.getInstanceId(), key);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("创建 StoryItem 并上传封面失败", e);
|
||||
throw new CustomException(ResponseEnum.INTERNAL_SERVER_ERROR, "上传封面失败");
|
||||
}
|
||||
public void createStoryItem(StoryItemAddVo storyItemAddVo, List<MultipartFile> images) {
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoryItemWithCoverVo getStoryItemWithCover(String itemId) {
|
||||
StoryItem item = storyItemMapper.selectById(itemId);
|
||||
if (item == null) {
|
||||
throw new CustomException(ResponseEnum.NOT_FOUND, "未找到 StoryItem ");
|
||||
}
|
||||
InputStreamResource coverStream = null;
|
||||
if (item.getCoverInstanceId() != null) {
|
||||
// 从 file 服务下载封面流
|
||||
coverStream = fileServiceClient.downloadCover(item.getCoverInstanceId());
|
||||
|
||||
}
|
||||
return new StoryItemWithCoverVo(item, coverStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateItem(StoryItemAddVo storyItemVo, List<MultipartFile> images) {
|
||||
try {
|
||||
String currentUserId = UserContextUtils.getCurrentUserId();
|
||||
StoryItem item = storyItemMapper.selectById(storyItemVo.getInstanceId());
|
||||
if (item == null) {
|
||||
throw new RuntimeException("StoryItem 不存在");
|
||||
}
|
||||
item.setDescription(storyItemVo.getDescription());
|
||||
item.setLocation(storyItemVo.getLocation());
|
||||
item.setStoryItemTime(storyItemVo.getStoryItemTime());
|
||||
item.setTitle(storyItemVo.getTitle());
|
||||
item.setUpdateTime(LocalDateTime.now());
|
||||
item.setUpdateId(currentUserId);
|
||||
storyItemMapper.update(item);
|
||||
storyMapper.touchUpdate(item.getStoryInstanceId(), currentUserId);
|
||||
// 记录动态:更新 storyItem
|
||||
StoryActivity activity = new StoryActivity();
|
||||
activity.setActorId(currentUserId);
|
||||
activity.setAction("update_story_item");
|
||||
activity.setStoryInstanceId(item.getStoryInstanceId());
|
||||
activity.setStoryItemId(storyItemVo.getInstanceId());
|
||||
activity.setRemark("更新故事条目");
|
||||
storyActivityService.logActivity(activity);
|
||||
if (storyItemVo.getRelatedImageInstanceIds() != null && !storyItemVo.getRelatedImageInstanceIds().isEmpty()) {
|
||||
// 删除所有关联图像
|
||||
commonRelationMapper.deleteRelationByRelaId(item.getInstanceId());
|
||||
for (String imageInstanceId : storyItemVo.getRelatedImageInstanceIds()) {
|
||||
log.info("关联现有图像 {} - {}", imageInstanceId, item.getInstanceId());
|
||||
// 3. 建立 StoryItem 与图像关系
|
||||
buildStoryItemImageRelation(item.getInstanceId(), imageInstanceId);
|
||||
}
|
||||
}
|
||||
if (images != null) {
|
||||
log.info("上传 StoryItem 关联图像");
|
||||
for (MultipartFile image : images) {
|
||||
ResponseEntity<String> response = fileServiceClient.uploadImage(image);
|
||||
String key = response.getData();
|
||||
log.info("上传成功,文件instanceId:{}", key);
|
||||
// 4. 建立图像与StoryItem 关系
|
||||
buildStoryItemImageRelation(item.getInstanceId(), key);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("更新 StoryItem 失败", e);
|
||||
throw new RuntimeException("更新 StoryItem 失败");
|
||||
}
|
||||
public void updateItem(StoryItemAddVo storyItemAddVo, List<MultipartFile> images) {
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteItem(String itemId) {
|
||||
try {
|
||||
String currentUserId = UserContextUtils.getCurrentUserId();
|
||||
StoryItem item = storyItemMapper.selectById(itemId);
|
||||
if (item == null) {
|
||||
throw new RuntimeException("StoryItem 不存在");
|
||||
}
|
||||
storyItemMapper.deleteByItemId(itemId);
|
||||
commonRelationMapper.deleteRelationByRelaId(itemId);
|
||||
storyMapper.touchUpdate(item.getStoryInstanceId(), currentUserId);
|
||||
// 记录动态:删除 storyItem
|
||||
StoryActivity activity = new StoryActivity();
|
||||
activity.setActorId(currentUserId);
|
||||
activity.setAction("delete_story_item");
|
||||
activity.setStoryInstanceId(item.getStoryInstanceId());
|
||||
activity.setStoryItemId(itemId);
|
||||
activity.setRemark("删除故事条目");
|
||||
storyActivityService.logActivity(activity);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("删除 StoryItem 失败", e);
|
||||
throw new RuntimeException("删除 StoryItem 失败");
|
||||
}
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoryItem getItemById(String itemId) {
|
||||
return storyItemMapper.selectById(itemId);
|
||||
// TODO: Implement this method
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map getItemsByMasterItem(StoryItemVo storyItemVo) {
|
||||
HashMap<String, String> map = new HashMap<>();
|
||||
map.put("storyInstanceId", storyItemVo.getStoryInstanceId());
|
||||
Map resultMap = PageUtils.pageQuery(storyItemVo.getCurrent(), storyItemVo.getPageSize(), StoryItemMapper.class, "selectStoryItemByStoryInstanceId",
|
||||
map, "list");
|
||||
return resultMap;
|
||||
public List<String> getStoryItemImages(String itemId) {
|
||||
// TODO: Implement this method
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getStoryItemImages(String storyItemId) {
|
||||
return storyItemMapper.selectImagesByItemId(storyItemId);
|
||||
public Integer getStoryItemCount(String storyInstanceId) {
|
||||
// TODO: Implement this method
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getStoryItemCount(String instanceId) {
|
||||
return storyItemMapper.countByStoryId(instanceId);
|
||||
public StoryItemShareVo getItemByShareId(String shareId) {
|
||||
// TODO: Implement this method
|
||||
return null;
|
||||
}
|
||||
|
||||
private void buildStoryItemImageRelation(String storyItemId, String imageIds) {
|
||||
String currentId = currentUserId();
|
||||
CommonRelationDTO relationDTO = new CommonRelationDTO();
|
||||
relationDTO.setRelaId(storyItemId);
|
||||
relationDTO.setSubRelaId(imageIds);
|
||||
relationDTO.setRelationType(CommonConstants.RELATION_STORY_ITEM_AND_IMAGE);
|
||||
relationDTO.setUserId(currentId);
|
||||
relationDTO.setCreateTime(LocalDateTime.now());
|
||||
relationDTO.setUpdateTime(LocalDateTime.now());
|
||||
commonRelationMapper.insertRelation(relationDTO);
|
||||
@Override
|
||||
public Map<String, Object> searchItems(String keyword, Integer pageNum, Integer pageSize) {
|
||||
// TODO: Implement this method
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("list", Collections.emptyList());
|
||||
result.put("total", 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
@@ -37,6 +38,7 @@ public class StoryPermissionServiceImpl implements StoryPermissionService {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void createPermission(StoryPermissionVo permissionVo) {
|
||||
try {
|
||||
String currentUserId = getCurrentUserId();
|
||||
@@ -67,7 +69,7 @@ public class StoryPermissionServiceImpl implements StoryPermissionService {
|
||||
permission.setUpdateTime(LocalDateTime.now());
|
||||
permission.setIsDeleted(CommonConstants.NOT_DELETED);
|
||||
storyPermissionMapper.insert(permission);
|
||||
|
||||
|
||||
} catch(CustomException e) {
|
||||
throw e;
|
||||
}
|
||||
@@ -78,6 +80,7 @@ public class StoryPermissionServiceImpl implements StoryPermissionService {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updatePermission(StoryPermissionVo permissionVo) {
|
||||
try {
|
||||
StoryPermission permission = storyPermissionMapper.selectByPermissionId(permissionVo.getPermissionId());
|
||||
@@ -96,6 +99,7 @@ public class StoryPermissionServiceImpl implements StoryPermissionService {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deletePermission(String permissionId) {
|
||||
try {
|
||||
StoryPermission permission = storyPermissionMapper.selectByPermissionId(permissionId);
|
||||
|
||||
@@ -5,11 +5,15 @@ import com.timeline.common.exception.CustomException;
|
||||
import com.timeline.common.response.ResponseEnum;
|
||||
import com.timeline.story.entity.Story;
|
||||
import com.timeline.story.entity.StoryActivity;
|
||||
import com.timeline.story.entity.StoryItem;
|
||||
import com.timeline.story.dao.StoryMapper;
|
||||
import com.timeline.story.service.StoryItemService;
|
||||
import com.timeline.story.service.StoryPermissionService;
|
||||
import com.timeline.story.service.StoryService;
|
||||
import com.timeline.story.service.StoryActivityService;
|
||||
import com.timeline.story.mq.ActivityLogProducer;
|
||||
import com.timeline.story.vo.StoryDetailVo;
|
||||
import com.timeline.story.vo.StoryDetailWithItemsVo;
|
||||
import com.timeline.story.vo.StoryItemVo;
|
||||
import com.timeline.story.vo.StoryPermissionVo;
|
||||
import com.timeline.story.vo.StoryVo;
|
||||
import com.timeline.common.utils.IdUtils;
|
||||
@@ -19,9 +23,11 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import lombok.val;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@@ -34,7 +40,10 @@ public class StoryServiceImpl implements StoryService {
|
||||
private StoryPermissionService storyPermissionService;
|
||||
|
||||
@Autowired
|
||||
private StoryActivityService storyActivityService;
|
||||
private StoryItemService storyItemService;
|
||||
|
||||
@Autowired
|
||||
private ActivityLogProducer activityLogProducer;
|
||||
|
||||
private String getCurrentUserId() {
|
||||
String currentUserId = UserContextUtils.getCurrentUserId();
|
||||
@@ -43,7 +52,9 @@ public class StoryServiceImpl implements StoryService {
|
||||
}
|
||||
return currentUserId;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void createStory(StoryVo storyVo) {
|
||||
try {
|
||||
String currentUserId = UserContextUtils.getCurrentUserId();
|
||||
@@ -76,7 +87,7 @@ public class StoryServiceImpl implements StoryService {
|
||||
activity.setAction(CommonConstants.ACTION_TYPE_STORY_CREATE);
|
||||
activity.setStoryInstanceId(story.getInstanceId());
|
||||
activity.setRemark(CommonConstants.ACTION_REMARK_STORY_CREATE);
|
||||
storyActivityService.logActivity(activity);
|
||||
activityLogProducer.sendLog("story.activity.create", activity);
|
||||
} catch (Exception e) {
|
||||
log.error("创建故事失败", e);
|
||||
throw new CustomException(500, "创建故事失败: " + e.toString());
|
||||
@@ -84,6 +95,7 @@ public class StoryServiceImpl implements StoryService {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateStory(StoryVo storyVo, String storyId) {
|
||||
String currentUserId = getCurrentUserId();
|
||||
|
||||
@@ -91,7 +103,8 @@ public class StoryServiceImpl implements StoryService {
|
||||
if (story == null) {
|
||||
throw new CustomException(ResponseEnum.NOT_FOUND);
|
||||
}
|
||||
if (!storyPermissionService.checkUserPermission(storyId, currentUserId, CommonConstants.STORY_PERMISSION_TYPE_WRITE)) {
|
||||
if (!storyPermissionService.checkUserPermission(storyId, currentUserId,
|
||||
CommonConstants.STORY_PERMISSION_TYPE_WRITE)) {
|
||||
throw new CustomException(ResponseEnum.FORBIDDEN, "无权限修改故事");
|
||||
}
|
||||
story.setTitle(storyVo.getTitle());
|
||||
@@ -108,18 +121,20 @@ public class StoryServiceImpl implements StoryService {
|
||||
activity.setAction(CommonConstants.ACTION_TYPE_STORY_UPDATE);
|
||||
activity.setStoryInstanceId(storyId);
|
||||
activity.setRemark(CommonConstants.ACTION_REMARK_STORY_UPDATE);
|
||||
storyActivityService.logActivity(activity);
|
||||
activityLogProducer.sendLog("story.activity.update", activity);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteStory(String storyId) {
|
||||
String currentUserId = getCurrentUserId();
|
||||
Story story = storyMapper.selectByInstanceId(storyId, currentUserId);
|
||||
if (story == null) {
|
||||
throw new CustomException(ResponseEnum.NOT_FOUND);
|
||||
}
|
||||
if (!storyPermissionService.checkUserPermission(storyId, currentUserId, CommonConstants.STORY_PERMISSION_TYPE_ADMIN)) {
|
||||
if (!storyPermissionService.checkUserPermission(storyId, currentUserId,
|
||||
CommonConstants.STORY_PERMISSION_TYPE_ADMIN)) {
|
||||
throw new CustomException(ResponseEnum.FORBIDDEN, "无权限删除故事");
|
||||
}
|
||||
// delete story
|
||||
@@ -132,17 +147,42 @@ public class StoryServiceImpl implements StoryService {
|
||||
activity.setAction(CommonConstants.ACTION_TYPE_STORY_DELETE);
|
||||
activity.setStoryInstanceId(storyId);
|
||||
activity.setRemark(CommonConstants.ACTION_REMARK_STORY_DELETE);
|
||||
storyActivityService.logActivity(activity);
|
||||
activityLogProducer.sendLog("story.activity.delete", activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoryDetailVo getStoryByInstanceId(String storyId) {
|
||||
public StoryDetailWithItemsVo getStoryByInstanceId(String storyId) {
|
||||
val userId = getCurrentUserId();
|
||||
StoryDetailVo story = storyMapper.selectByInstanceId(storyId, userId);
|
||||
if (story == null) {
|
||||
throw new CustomException(ResponseEnum.NOT_FOUND);
|
||||
}
|
||||
return story;
|
||||
StoryItemVo storyItemVo = new StoryItemVo();
|
||||
storyItemVo.setStoryInstanceId(storyId);
|
||||
storyItemVo.setCurrent(1);
|
||||
storyItemVo.setPageSize(10);
|
||||
Map itemsMap = storyItemService.getItemsByMasterItem(storyItemVo);
|
||||
List<StoryItem> items = (List<StoryItem>) itemsMap.get("list");
|
||||
|
||||
StoryDetailWithItemsVo result = new StoryDetailWithItemsVo();
|
||||
result.setItems(items);
|
||||
result.setInstanceId(story.getInstanceId());
|
||||
result.setOwnerId(story.getOwnerId());
|
||||
result.setUpdateId(story.getUpdateId());
|
||||
result.setTitle(story.getTitle());
|
||||
result.setDescription(story.getDescription());
|
||||
result.setStatus(story.getStatus());
|
||||
result.setStoryTime(story.getStoryTime());
|
||||
result.setCreateTime(story.getCreateTime());
|
||||
result.setUpdateTime(story.getUpdateTime());
|
||||
result.setLogo(story.getLogo());
|
||||
result.setIsDelete(story.getIsDelete());
|
||||
result.setOwnerName(story.getOwnerName());
|
||||
result.setUpdateName(story.getUpdateName());
|
||||
result.setPermissionType(story.getPermissionType());
|
||||
result.setItemCount(story.getItemCount());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.timeline.story.vo;
|
||||
|
||||
import com.timeline.story.entity.StoryItem;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class StoryDetailWithItemsVo extends StoryDetailVo {
|
||||
private List<StoryItem> items;
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
package com.timeline.story.vo;
|
||||
|
||||
import com.timeline.story.entity.StoryItem;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class StoryItemAddVo extends StoryItemVo{
|
||||
private List<String> relatedImageInstanceIds;
|
||||
public class StoryItemAddVo extends StoryItem {
|
||||
private String sharePassword;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.timeline.story.vo;
|
||||
|
||||
import com.timeline.story.entity.StoryItem;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class StoryItemShareVo extends StoryItem {
|
||||
private String authorName;
|
||||
private String authorAvatar;
|
||||
}
|
||||
@@ -1,23 +1,10 @@
|
||||
package com.timeline.story.vo;
|
||||
|
||||
import com.timeline.common.vo.CommonVo;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class StoryItemVo extends CommonVo {
|
||||
private String instanceId;
|
||||
private String masterItemId;
|
||||
private String title;
|
||||
private String description;
|
||||
private String location;
|
||||
private LocalDateTime storyItemTime;
|
||||
public class StoryItemVo {
|
||||
private String storyInstanceId;
|
||||
private String createId;
|
||||
private String createName;
|
||||
private LocalDateTime createTime;
|
||||
private String updateId;
|
||||
private String updateName;
|
||||
private LocalDateTime updateTime;
|
||||
private Integer current;
|
||||
private Integer pageSize;
|
||||
}
|
||||
|
||||
@@ -61,3 +61,10 @@ spring.datasource.hikari.connection-timeout=30000
|
||||
spring.datasource.hikari.connection-test-query=SELECT 1
|
||||
spring.datasource.hikari.test-on-borrow=true
|
||||
spring.datasource.hikari.test-while-idle=true
|
||||
|
||||
# RabbitMQ configuration
|
||||
spring.rabbitmq.host=localhost
|
||||
spring.rabbitmq.port=5672
|
||||
spring.rabbitmq.username=guest
|
||||
spring.rabbitmq.password=guest
|
||||
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
location = #{location},
|
||||
create_id = #{createId},
|
||||
update_time = NOW(),
|
||||
update_id = #{updateId}
|
||||
update_id = #{updateId},
|
||||
share_id = #{shareId}
|
||||
WHERE instance_id = #{instanceId}
|
||||
</update>
|
||||
|
||||
@@ -58,6 +59,9 @@
|
||||
WHERE
|
||||
story_instance_id = #{storyInstanceId}
|
||||
AND is_delete = 0
|
||||
<if test="afterTime != null">
|
||||
AND story_item_time > #{afterTime}
|
||||
</if>
|
||||
ORDER BY
|
||||
story_item_time DESC
|
||||
</select>
|
||||
@@ -65,4 +69,39 @@
|
||||
<select id="countByStoryId" resultType="int">
|
||||
SELECT COUNT(*) FROM story_item WHERE story_instance_id = #{storyInstanceId} AND is_delete = 0
|
||||
</select>
|
||||
|
||||
<select id="selectByShareId" resultType="com.timeline.story.entity.StoryItem">
|
||||
SELECT * FROM story_item WHERE share_id = #{shareId} AND is_delete = 0
|
||||
</select>
|
||||
|
||||
<select id="selectByShareIdWithAuthor" resultType="com.timeline.story.vo.StoryItemShareVo">
|
||||
SELECT
|
||||
si.*,
|
||||
u.username AS authorName,
|
||||
u.avatar AS authorAvatar
|
||||
FROM
|
||||
story_item si
|
||||
LEFT JOIN
|
||||
user u ON si.create_id = u.user_id
|
||||
WHERE
|
||||
si.share_id = #{shareId} AND si.is_delete = 0
|
||||
</select>
|
||||
|
||||
<select id="searchItems" resultType="com.timeline.story.vo.StoryItemVo">
|
||||
SELECT
|
||||
si.id,
|
||||
si.story_instance_id,
|
||||
si.title,
|
||||
si.content,
|
||||
si.story_item_time,
|
||||
si.cover,
|
||||
si.is_milestone
|
||||
FROM
|
||||
story_item si
|
||||
WHERE
|
||||
si.is_delete = 0
|
||||
AND (si.title LIKE CONCAT('%', #{keyword}, '%') OR si.content LIKE CONCAT('%', #{keyword}, '%'))
|
||||
ORDER BY
|
||||
si.story_item_time DESC
|
||||
</select>
|
||||
</mapper>
|
||||
|
||||
Reference in New Issue
Block a user