init
This commit is contained in:
@@ -2,8 +2,10 @@ package com.timeline.story.config;
|
||||
|
||||
import feign.codec.Encoder;
|
||||
import feign.form.spring.SpringFormEncoder;
|
||||
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||
import org.springframework.cloud.openfeign.support.SpringEncoder;
|
||||
import feign.RequestInterceptor;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@@ -14,4 +16,22 @@ public class FeignConfig {
|
||||
public Encoder feignEncoder() {
|
||||
return new SpringFormEncoder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RequestInterceptor userHeaderInterceptor() {
|
||||
return template -> {
|
||||
RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
|
||||
if (attrs instanceof ServletRequestAttributes servletRequestAttributes) {
|
||||
var req = servletRequestAttributes.getRequest();
|
||||
String userId = req.getHeader("X-User-Id");
|
||||
String username = req.getHeader("X-Username");
|
||||
if (userId != null) {
|
||||
template.header("X-User-Id", userId);
|
||||
}
|
||||
if (username != null) {
|
||||
template.header("X-Username", username);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.timeline.story.controller;
|
||||
|
||||
import com.timeline.common.response.ResponseEntity;
|
||||
import com.timeline.story.dto.StoryItemActivityDto;
|
||||
import com.timeline.story.entity.StoryActivity;
|
||||
import com.timeline.story.service.StoryActivityService;
|
||||
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.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/story/activity")
|
||||
public class StoryActivityController {
|
||||
|
||||
@Autowired
|
||||
private StoryActivityService storyActivityService;
|
||||
|
||||
@GetMapping("/my-and-friends")
|
||||
public ResponseEntity<List<StoryActivity>> myAndFriends() {
|
||||
return ResponseEntity.success(storyActivityService.listMyAndFriendsActivities());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前用户拥有权限的 story 中,storyItem 的创建/更新动态
|
||||
*/
|
||||
@GetMapping("/authorized-items")
|
||||
public ResponseEntity<List<StoryItemActivityDto>> authorizedItemUpdates() {
|
||||
return ResponseEntity.success(storyActivityService.listAuthorizedItemUpdates());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.timeline.story.service.StoryService;
|
||||
import com.timeline.story.vo.StoryVo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.openfeign.SpringQueryMap;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
@@ -59,4 +60,10 @@ public class StoryController {
|
||||
List<Story> stories = storyService.getStoriesByOwnerId(ownerId);
|
||||
return ResponseEntity.success(stories);
|
||||
}
|
||||
@GetMapping("/list")
|
||||
public ResponseEntity<List<Story>> getStories(@SpringQueryMap StoryVo storyVo) {
|
||||
log.info("查询故事列表, 用户ID: {}", storyVo.getOwnerId());
|
||||
List<Story> stories = storyService.getStories(storyVo);
|
||||
return ResponseEntity.success(stories);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,11 @@ import com.alibaba.fastjson.JSONObject;
|
||||
import com.timeline.common.response.ResponseEntity;
|
||||
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.StoryItemVo;
|
||||
import com.timeline.story.vo.StoryVo;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.openfeign.SpringQueryMap;
|
||||
@@ -31,12 +34,10 @@ public class StoryItemController {
|
||||
return ResponseEntity.success("StoryItem 创建成功");
|
||||
}
|
||||
|
||||
@PutMapping("/{itemId}")
|
||||
public ResponseEntity<String> updateItem(@PathVariable String itemId,
|
||||
@RequestParam String description,
|
||||
@RequestParam String location) {
|
||||
log.info("更新 StoryItem: {}", itemId);
|
||||
storyItemService.updateItem(itemId, description, location);
|
||||
@PutMapping("")
|
||||
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 更新成功");
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,9 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/story/permission")
|
||||
@@ -31,6 +34,12 @@ public class StoryPermissionController {
|
||||
storyPermissionService.updatePermission(permissionVo);
|
||||
return ResponseEntity.success("权限更新成功");
|
||||
}
|
||||
@PostMapping("/authorize")
|
||||
public ResponseEntity<String> authorizePermission(@RequestBody StoryPermissionVo permissionVo) {
|
||||
log.info("授权权限: {}", permissionVo);
|
||||
storyPermissionService.createPermission(permissionVo);
|
||||
return ResponseEntity.success("权限授权成功");
|
||||
}
|
||||
|
||||
@DeleteMapping("/{permissionId}")
|
||||
public ResponseEntity<String> deletePermission(@PathVariable String permissionId) {
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.timeline.story.controller;
|
||||
|
||||
import com.timeline.common.response.ResponseEntity;
|
||||
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.service.StoryActivityService;
|
||||
import com.timeline.story.service.StoryPermissionService;
|
||||
import com.timeline.story.service.StoryService;
|
||||
import com.timeline.story.vo.StoryPermissionVo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/story/share")
|
||||
public class StoryShareController {
|
||||
|
||||
@Autowired
|
||||
private StoryPermissionService storyPermissionService;
|
||||
@Autowired
|
||||
private StoryService storyService;
|
||||
@Autowired
|
||||
private StoryActivityService storyActivityService;
|
||||
|
||||
@PostMapping("/{storyId}")
|
||||
public ResponseEntity<String> shareStory(@PathVariable String storyId, @RequestBody ShareStoryRequest req) {
|
||||
StoryPermissionVo vo = new StoryPermissionVo();
|
||||
vo.setStoryInstanceId(storyId);
|
||||
vo.setUserId(req.getFriendId());
|
||||
vo.setPermissionType(req.getPermissionType());
|
||||
storyPermissionService.createPermission(vo);
|
||||
|
||||
StoryActivity activity = new StoryActivity();
|
||||
activity.setActorId(UserContextUtils.getCurrentUserId());
|
||||
activity.setAction("share_story");
|
||||
activity.setStoryInstanceId(storyId);
|
||||
activity.setRemark("分享给 " + req.getFriendId());
|
||||
storyActivityService.logActivity(activity);
|
||||
return ResponseEntity.success("已授权好友");
|
||||
}
|
||||
|
||||
@GetMapping("/friends")
|
||||
public ResponseEntity<List<Story>> friendStories() {
|
||||
String uid = UserContextUtils.getCurrentUserId();
|
||||
var permissions = storyPermissionService.getPermissionsByUserId(uid);
|
||||
List<Story> stories = new ArrayList<>();
|
||||
for (var p : permissions) {
|
||||
try {
|
||||
stories.add(storyService.getStoryByInstanceId(p.getStoryInstanceId()));
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
return ResponseEntity.success(stories);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.timeline.story.dao;
|
||||
|
||||
import com.timeline.story.entity.StoryActivity;
|
||||
import com.timeline.story.dto.StoryItemActivityDto;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface StoryActivityMapper {
|
||||
void insert(StoryActivity activity);
|
||||
|
||||
List<StoryActivity> selectByActors(@Param("actorIds") List<String> actorIds);
|
||||
|
||||
/**
|
||||
* 查询当前用户拥有权限的 story 中,与 storyItem 相关的最新动态,携带 storyItem 内容
|
||||
*/
|
||||
List<StoryItemActivityDto> selectAuthorizedItemUpdates(@Param("userId") String userId);
|
||||
}
|
||||
|
||||
@@ -12,5 +12,5 @@ public interface StoryMapper {
|
||||
void deleteByInstanceId(String instanceId);
|
||||
Story selectByInstanceId(String instanceId);
|
||||
List<Story> selectByOwnerId(String ownerId);
|
||||
|
||||
void touchUpdate(String instanceId, String updateId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.timeline.story.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ShareStoryRequest {
|
||||
private String friendId;
|
||||
private Integer permissionType; // 1=读,2=写,3=管理
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.timeline.story.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 带有 storyItem 内容的动态信息,用于“好友动态 / 有权限的 storyItem 更新”接口
|
||||
*/
|
||||
@Data
|
||||
public class StoryItemActivityDto {
|
||||
// activity 基本信息
|
||||
private Long id;
|
||||
private String actorId;
|
||||
private String actorName;
|
||||
private String action;
|
||||
private String storyInstanceId;
|
||||
private String storyInstanceName;
|
||||
private String storyItemId;
|
||||
private String storyItemName;
|
||||
private String remark;
|
||||
private LocalDateTime activityTime;
|
||||
|
||||
// storyItem 内容摘要
|
||||
private String itemTitle;
|
||||
private String itemDescription;
|
||||
private String itemLocation;
|
||||
private LocalDateTime itemTime;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.timeline.story.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class StoryActivity {
|
||||
private Long id;
|
||||
private String actorId;
|
||||
private String action; // create_story, update_story_item, share_story, etc.
|
||||
private String storyInstanceId;
|
||||
private String storyItemId;
|
||||
private String remark;
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
|
||||
@@ -22,4 +22,5 @@ public class StoryItem {
|
||||
private Integer isDelete;
|
||||
private String coverInstanceId;
|
||||
private StoryItem[] subItems;
|
||||
private String updateId;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.timeline.story.feign;
|
||||
|
||||
import com.timeline.common.response.ResponseEntity;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@FeignClient(name = "timeline.user", url = "${user.service.url}")
|
||||
public interface UserServiceClient {
|
||||
|
||||
@GetMapping("/user/friend/ids")
|
||||
ResponseEntity<List<String>> getFriendIds();
|
||||
@GetMapping("/{userId}")
|
||||
ResponseEntity<Map> getUserByUserId(@PathVariable String userId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.timeline.story.service;
|
||||
|
||||
import com.timeline.story.entity.StoryActivity;
|
||||
import com.timeline.story.dto.StoryItemActivityDto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface StoryActivityService {
|
||||
void logActivity(StoryActivity activity);
|
||||
|
||||
List<StoryActivity> listMyAndFriendsActivities();
|
||||
|
||||
/**
|
||||
* 查询当前用户拥有权限的 story 中,storyItem 的更新/创建动态
|
||||
*/
|
||||
List<StoryItemActivityDto> listAuthorizedItemUpdates();
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import java.util.Map;
|
||||
public interface StoryItemService {
|
||||
void createStoryItem(StoryItemAddVo storyItemVo, List<MultipartFile> images);
|
||||
StoryItemWithCoverVo getStoryItemWithCover(String itemId);
|
||||
void updateItem(String itemId, String description, String location);
|
||||
void updateItem(StoryItemAddVo storyItemVo, List<MultipartFile> images);
|
||||
void deleteItem(String itemId);
|
||||
StoryItem getItemById(String itemId);
|
||||
Map getItemsByMasterItem(StoryItemVo storyItemVo);
|
||||
|
||||
@@ -11,5 +11,6 @@ public interface StoryService {
|
||||
void deleteStory(String storyId);
|
||||
Story getStoryByInstanceId(String storyId);
|
||||
List<Story> getStoriesByOwnerId(String ownerId);
|
||||
List<Story> getStories(StoryVo storyVo);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.timeline.story.service.impl;
|
||||
|
||||
import com.timeline.common.exception.CustomException;
|
||||
import com.timeline.common.response.ResponseEnum;
|
||||
import com.timeline.common.utils.UserContextUtils;
|
||||
import com.timeline.story.dao.StoryActivityMapper;
|
||||
import com.timeline.story.dto.StoryItemActivityDto;
|
||||
import com.timeline.story.entity.StoryActivity;
|
||||
import com.timeline.story.feign.UserServiceClient;
|
||||
import com.timeline.story.service.StoryActivityService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class StoryActivityServiceImpl implements StoryActivityService {
|
||||
|
||||
@Autowired
|
||||
private StoryActivityMapper storyActivityMapper;
|
||||
@Autowired
|
||||
private UserServiceClient userServiceClient;
|
||||
|
||||
private String currentUser() {
|
||||
String uid = UserContextUtils.getCurrentUserId();
|
||||
if (uid == null || uid.isEmpty()) {
|
||||
throw new CustomException(ResponseEnum.UNAUTHORIZED, "未获取到用户身份");
|
||||
}
|
||||
return uid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logActivity(StoryActivity activity) {
|
||||
activity.setCreateTime(LocalDateTime.now());
|
||||
storyActivityMapper.insert(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StoryActivity> listMyAndFriendsActivities() {
|
||||
String uid = currentUser();
|
||||
List<String> actorIds = new ArrayList<>();
|
||||
actorIds.add(uid);
|
||||
try {
|
||||
var resp = userServiceClient.getFriendIds();
|
||||
if (resp != null && resp.getData() != null) {
|
||||
actorIds.addAll(resp.getData());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("获取好友列表失败,按仅自己查询", e);
|
||||
}
|
||||
return storyActivityMapper.selectByActors(actorIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StoryItemActivityDto> listAuthorizedItemUpdates() {
|
||||
String uid = currentUser();
|
||||
return storyActivityMapper.selectAuthorizedItemUpdates(uid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,19 @@ 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.StoryItemVo;
|
||||
import com.timeline.story.vo.StoryItemWithCoverVo;
|
||||
@@ -33,14 +37,33 @@ 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 void createStoryItem(StoryItemAddVo storyItemVo, List<MultipartFile> images) {
|
||||
try {
|
||||
String currentUserId = currentUserId();
|
||||
// 2. 创建 StoryItem 实体
|
||||
StoryItem item = new StoryItem();
|
||||
item.setInstanceId(IdUtils.randomUuidUpper());
|
||||
@@ -49,10 +72,20 @@ public class StoryItemServiceImpl implements StoryItemService {
|
||||
item.setTitle(storyItemVo.getTitle());
|
||||
item.setDescription(storyItemVo.getDescription());
|
||||
item.setLocation(storyItemVo.getLocation());
|
||||
item.setCreateId("createId");
|
||||
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());
|
||||
@@ -92,16 +125,48 @@ public class StoryItemServiceImpl implements StoryItemService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateItem(String itemId, String description, String location) {
|
||||
public void updateItem(StoryItemAddVo storyItemVo, List<MultipartFile> images) {
|
||||
try {
|
||||
StoryItem item = storyItemMapper.selectById(itemId);
|
||||
String currentUserId = UserContextUtils.getCurrentUserId();
|
||||
StoryItem item = storyItemMapper.selectById(storyItemVo.getInstanceId());
|
||||
if (item == null) {
|
||||
throw new RuntimeException("StoryItem 不存在");
|
||||
}
|
||||
item.setDescription(description);
|
||||
item.setLocation(location);
|
||||
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 失败");
|
||||
@@ -111,12 +176,23 @@ public class StoryItemServiceImpl implements StoryItemService {
|
||||
@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 失败");
|
||||
|
||||
@@ -2,10 +2,13 @@ package com.timeline.story.service.impl;
|
||||
|
||||
import com.timeline.common.constants.CommonConstants;
|
||||
import com.timeline.common.exception.CustomException;
|
||||
import com.timeline.common.response.ResponseEntity;
|
||||
import com.timeline.common.response.ResponseEnum;
|
||||
import com.timeline.common.utils.IdUtils;
|
||||
import com.timeline.common.utils.UserContextUtils;
|
||||
import com.timeline.story.dao.StoryPermissionMapper;
|
||||
import com.timeline.story.entity.StoryPermission;
|
||||
import com.timeline.story.feign.UserServiceClient;
|
||||
import com.timeline.story.service.StoryPermissionService;
|
||||
import com.timeline.story.vo.StoryPermissionVo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -15,6 +18,7 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@@ -22,10 +26,40 @@ public class StoryPermissionServiceImpl implements StoryPermissionService {
|
||||
|
||||
@Autowired
|
||||
private StoryPermissionMapper storyPermissionMapper;
|
||||
@Autowired
|
||||
private UserServiceClient userServiceClient;
|
||||
private String getCurrentUserId() {
|
||||
String uid = UserContextUtils.getCurrentUserId();
|
||||
if (uid == null) {
|
||||
throw new CustomException(ResponseEnum.UNAUTHORIZED, "未获取到用户身份");
|
||||
}
|
||||
return uid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createPermission(StoryPermissionVo permissionVo) {
|
||||
try {
|
||||
String currentUserId = getCurrentUserId();
|
||||
if (currentUserId.equals(permissionVo.getUserId()) && permissionVo.getPermissionType() != 1) {
|
||||
throw new CustomException(ResponseEnum.BAD_REQUEST, "不能授权给自己");
|
||||
}
|
||||
StoryPermission selectByStoryAndUser = storyPermissionMapper.selectByStoryAndUser(permissionVo.getStoryInstanceId(), permissionVo.getUserId());;
|
||||
if (selectByStoryAndUser != null) {
|
||||
log.info("用户已有该故事权限,更新当前权限为:{}", permissionVo.getPermissionType());
|
||||
selectByStoryAndUser.setPermissionType(permissionVo.getPermissionType());;
|
||||
selectByStoryAndUser.setUpdateTime(LocalDateTime.now());
|
||||
storyPermissionMapper.update(selectByStoryAndUser);
|
||||
return;
|
||||
}
|
||||
// 远程调用user服务,判断用户是否存在, feign 调用
|
||||
ResponseEntity<Map> response = userServiceClient.getUserByUserId(permissionVo.getUserId());
|
||||
log.info("响应结果: {}", response.toString());
|
||||
if (response.getCode() != 200) {
|
||||
throw new CustomException(ResponseEnum.BAD_REQUEST, "远程调用失败");
|
||||
} else if (response.getData() == null) {
|
||||
throw new CustomException(ResponseEnum.BAD_REQUEST, "用户不存在");
|
||||
}
|
||||
log.info("新建故事{} 授权 {} 给 {}", permissionVo.getStoryInstanceId(), permissionVo.getPermissionType(), permissionVo.getUserId());
|
||||
StoryPermission permission = new StoryPermission();
|
||||
BeanUtils.copyProperties(permissionVo, permission);
|
||||
permission.setPermissionId(IdUtils.randomUuidUpper());
|
||||
@@ -33,7 +67,11 @@ public class StoryPermissionServiceImpl implements StoryPermissionService {
|
||||
permission.setUpdateTime(LocalDateTime.now());
|
||||
permission.setIsDeleted(CommonConstants.NOT_DELETED);
|
||||
storyPermissionMapper.insert(permission);
|
||||
} catch (Exception e) {
|
||||
|
||||
} catch(CustomException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("创建权限失败", e);
|
||||
throw new CustomException(ResponseEnum.INTERNAL_SERVER_ERROR, "创建权限失败");
|
||||
}
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
package com.timeline.story.service.impl;
|
||||
|
||||
import com.timeline.common.constants.CommonConstants;
|
||||
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.dao.StoryMapper;
|
||||
import com.timeline.story.service.StoryPermissionService;
|
||||
import com.timeline.story.service.StoryService;
|
||||
import com.timeline.story.service.StoryActivityService;
|
||||
import com.timeline.story.vo.StoryPermissionVo;
|
||||
import com.timeline.story.vo.StoryVo;
|
||||
import com.timeline.common.utils.IdUtils;
|
||||
import com.timeline.common.utils.UserContextUtils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -25,11 +30,19 @@ public class StoryServiceImpl implements StoryService {
|
||||
|
||||
@Autowired
|
||||
private StoryPermissionService storyPermissionService;
|
||||
|
||||
@Autowired
|
||||
private StoryActivityService storyActivityService;
|
||||
@Override
|
||||
public void createStory(StoryVo storyVo) {
|
||||
try {
|
||||
String currentUserId = UserContextUtils.getCurrentUserId();
|
||||
if (currentUserId == null || currentUserId.isEmpty()) {
|
||||
throw new CustomException(ResponseEnum.UNAUTHORIZED, "未获取到用户身份");
|
||||
}
|
||||
Story story = new Story();
|
||||
story.setOwnerId("test11");
|
||||
story.setOwnerId(currentUserId);
|
||||
story.setUpdateId(currentUserId);
|
||||
story.setTitle(storyVo.getTitle());
|
||||
story.setInstanceId(IdUtils.randomUuidUpper());
|
||||
story.setDescription(storyVo.getDescription());
|
||||
@@ -42,9 +55,16 @@ public class StoryServiceImpl implements StoryService {
|
||||
// 自动添加创建者权限
|
||||
StoryPermissionVo permissionVo = new StoryPermissionVo();
|
||||
permissionVo.setStoryInstanceId(story.getInstanceId());
|
||||
permissionVo.setUserId(storyVo.getOwnerId());
|
||||
permissionVo.setPermissionType(1); // 创建者权限
|
||||
permissionVo.setUserId(currentUserId);
|
||||
permissionVo.setPermissionType(CommonConstants.STORY_PERMISSION_TYPE_OWNER); // 创建者权限
|
||||
storyPermissionService.createPermission(permissionVo);
|
||||
|
||||
StoryActivity activity = new StoryActivity();
|
||||
activity.setActorId(currentUserId);
|
||||
activity.setAction("create_story");
|
||||
activity.setStoryInstanceId(story.getInstanceId());
|
||||
activity.setRemark("创建故事");
|
||||
storyActivityService.logActivity(activity);
|
||||
} catch (Exception e) {
|
||||
log.error("创建故事失败", e);
|
||||
throw new CustomException(500, "创建故事失败: " + e.toString());
|
||||
@@ -65,12 +85,19 @@ public class StoryServiceImpl implements StoryService {
|
||||
story.setUpdateTime(LocalDateTime.now());
|
||||
story.setLogo(storyVo.getLogo());
|
||||
|
||||
// 如果传入了 updateId,则更新 updateId todo: 使用线程获取用户ID
|
||||
if (storyVo.getOwnerId() != null && !storyVo.getOwnerId().isEmpty()) {
|
||||
story.setUpdateId(storyVo.getOwnerId());
|
||||
String currentUserId = UserContextUtils.getCurrentUserId();
|
||||
if (currentUserId != null && !currentUserId.isEmpty()) {
|
||||
story.setUpdateId(currentUserId);
|
||||
}
|
||||
storyMapper.update(story);
|
||||
|
||||
StoryActivity activity = new StoryActivity();
|
||||
activity.setActorId(currentUserId);
|
||||
activity.setAction("update_story");
|
||||
activity.setStoryInstanceId(storyId);
|
||||
activity.setRemark("更新故事");
|
||||
storyActivityService.logActivity(activity);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -100,4 +127,18 @@ public class StoryServiceImpl implements StoryService {
|
||||
throw new CustomException(500, "查询用户故事列表失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Story> getStories(StoryVo storyVo) {
|
||||
try {
|
||||
String currentUserId = UserContextUtils.getCurrentUserId();
|
||||
if (currentUserId == null || currentUserId.isEmpty()) {
|
||||
throw new CustomException(ResponseEnum.UNAUTHORIZED, "未获取到用户身份");
|
||||
}
|
||||
return storyMapper.selectByOwnerId(currentUserId);
|
||||
} catch (Exception e) {
|
||||
log.error("查询用户故事列表失败", e);
|
||||
throw new CustomException(500, "查询用户故事列表失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,5 +14,10 @@ public class StoryItemVo extends CommonVo {
|
||||
private String location;
|
||||
private LocalDateTime storyItemTime;
|
||||
private String storyInstanceId;
|
||||
|
||||
private String createId;
|
||||
private String createName;
|
||||
private LocalDateTime createTime;
|
||||
private String updateId;
|
||||
private String updateName;
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
|
||||
@@ -1,25 +1,52 @@
|
||||
spring.application.name=timeline.story
|
||||
spring.datasource.url=jdbc:mysql://8.137.148.196:33306/timeline?serverTimezone=UTC&allowPublicKeyRetrieval=true
|
||||
spring.datasource.url=jdbc:mysql://59.80.22.43:33306/timeline?serverTimezone=UTC&allowPublicKeyRetrieval=true
|
||||
spring.datasource.username=root
|
||||
spring.datasource.password=your_password
|
||||
spring.datasource.password=WoCloud@9ol7uj
|
||||
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
# MyBatis ??
|
||||
mybatis.mapper-locations=classpath:mapper/*.xml
|
||||
mybatis.type-aliases-package=com.timeline.story.entity
|
||||
mybatis.configuration.log4j=true
|
||||
server.port=30001
|
||||
spring.web.mvc.use-trailing-slash=true
|
||||
mybatis.mapper-locations=classpath:mapper/*.xml
|
||||
mybatis.type-aliases-package=com.timeline.user.entity
|
||||
mybatis.configuration.mapUnderscoreToCamelCase=true
|
||||
# ??????????
|
||||
logging.level.org.mybatis.spring=DEBUG
|
||||
logging.level.org.apache.ibatis=DEBUG
|
||||
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
logging.level.com.timeline.user.dao=DEBUG
|
||||
logging.level.org.mybatis=DEBUG
|
||||
|
||||
# LocalDateTime ???????
|
||||
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
|
||||
spring.jackson.time-zone=UTC
|
||||
|
||||
file.service.url=http://localhost:30002/file/
|
||||
user.service.url=http://localhost:30003/user/
|
||||
|
||||
spring.servlet.multipart.enabled=true
|
||||
spring.servlet.multipart.max-file-size=10MB
|
||||
spring.servlet.multipart.max-request-size=10MB
|
||||
spring.servlet.multipart.max-request-size=10MB
|
||||
|
||||
spring.data.redis.host=127.0.0.1
|
||||
spring.data.redis.port=36379
|
||||
spring.data.redis.password=123456
|
||||
spring.data.redis.timeout=5000
|
||||
|
||||
# HikariCP tuning to avoid stale/closed MySQL connections
|
||||
spring.datasource.hikari.max-lifetime=600000
|
||||
# 10 minutes, below MySQL wait_timeout
|
||||
spring.datasource.hikari.idle-timeout=300000
|
||||
# 5 minutes, recycle idle connections
|
||||
spring.datasource.hikari.validation-timeout=3000
|
||||
# fast validation timeout
|
||||
spring.datasource.hikari.connection-timeout=30000
|
||||
# wait up to 30s for a connection
|
||||
spring.datasource.hikari.connection-test-query=SELECT 1
|
||||
spring.datasource.hikari.test-on-borrow=true
|
||||
spring.datasource.hikari.test-while-idle=true
|
||||
|
||||
# Ensure UTF-8 encoding for logs and web layer to avoid garbled Chinese output
|
||||
logging.charset.console=UTF-8
|
||||
logging.charset.file=UTF-8
|
||||
server.tomcat.uri-encoding=UTF-8
|
||||
server.servlet.encoding.charset=UTF-8
|
||||
server.servlet.encoding.enabled=true
|
||||
server.servlet.encoding.force=true
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
<?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.StoryActivityMapper">
|
||||
|
||||
<insert id="insert" parameterType="com.timeline.story.entity.StoryActivity">
|
||||
INSERT INTO story_activity (actor_id, action, story_instance_id, story_item_id, remark, create_time)
|
||||
VALUES (#{actorId}, #{action}, #{storyInstanceId}, #{storyItemId}, #{remark}, #{createTime})
|
||||
</insert>
|
||||
|
||||
<select id="selectByActors" resultType="com.timeline.story.entity.StoryActivity">
|
||||
SELECT * FROM story_activity
|
||||
WHERE actor_id IN
|
||||
<foreach collection="actorIds" item="id" open="(" close=")" separator=",">
|
||||
#{id}
|
||||
</foreach>
|
||||
ORDER BY create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 查询当前用户拥有权限的 story 中,与 storyItem 相关的动态,联表返回 storyItem 内容 -->
|
||||
<select id="selectAuthorizedItemUpdates" resultType="com.timeline.story.dto.StoryItemActivityDto">
|
||||
SELECT
|
||||
sa.id,
|
||||
sa.actor_id AS actorId,
|
||||
u.username AS actorName,
|
||||
sa.action,
|
||||
sa.story_instance_id AS storyInstanceId,
|
||||
s.title AS storyInstanceName,
|
||||
sa.story_item_id AS storyItemId,
|
||||
si.title AS storyItemName,
|
||||
sa.remark,
|
||||
sa.create_time AS activityTime,
|
||||
si.title AS itemTitle,
|
||||
si.description AS itemDescription,
|
||||
si.location AS itemLocation,
|
||||
si.story_item_time AS itemTime
|
||||
FROM story_activity sa
|
||||
INNER JOIN story_permission sp
|
||||
ON sa.story_instance_id = sp.story_instance_id
|
||||
LEFT JOIN story_item si
|
||||
ON sa.story_item_id = si.instance_id
|
||||
LEFT JOIN story s
|
||||
ON sa.story_instance_id = s.instance_id
|
||||
LEFT JOIN user u
|
||||
ON sa.actor_id = u.user_id
|
||||
WHERE sp.user_id = #{userId}
|
||||
AND sp.is_deleted = 0
|
||||
AND sa.story_item_id IS NOT NULL
|
||||
<!-- AND sa.action IN ('create_story_item', 'update_story_item') -->
|
||||
ORDER BY sa.create_time DESC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<mapper namespace="com.timeline.story.dao.StoryItemMapper">
|
||||
|
||||
<insert id="insert">
|
||||
INSERT INTO story_item (instance_id, master_item_id, description, location, title, create_id, story_instance_id, is_delete, story_item_time)
|
||||
VALUES (#{instanceId}, #{masterItemId}, #{description}, #{location}, #{title},#{createId}, #{storyInstanceId}, #{isDelete}, #{storyItemTime})
|
||||
INSERT INTO story_item (instance_id, master_item_id, description, location, title, create_id, story_instance_id, is_delete, story_item_time, update_id)
|
||||
VALUES (#{instanceId}, #{masterItemId}, #{description}, #{location}, #{title},#{createId}, #{storyInstanceId}, #{isDelete}, #{storyItemTime}, #{updateId})
|
||||
</insert>
|
||||
|
||||
<update id="update">
|
||||
@@ -14,7 +14,8 @@
|
||||
SET description = #{description},
|
||||
location = #{location},
|
||||
create_id = #{createId},
|
||||
update_time = NOW()
|
||||
update_time = NOW(),
|
||||
update_id = #{updateId}
|
||||
WHERE instance_id = #{instanceId}
|
||||
</update>
|
||||
|
||||
@@ -35,9 +36,30 @@
|
||||
<select id="selectImagesByItemId" resultType="java.lang.String">
|
||||
SELECT sub_rela_id FROM common_relation WHERE rela_id = #{instanceId} AND rela_type = 5 AND is_delete = 0
|
||||
</select>
|
||||
<select id="selectStoryItemByStoryInstanceId" resultType="com.timeline.story.entity.StoryItem">
|
||||
SELECT * FROM story_item WHERE story_instance_id = #{storyInstanceId} AND is_delete = 0
|
||||
ORDER BY story_item_time DESC
|
||||
<select id="selectStoryItemByStoryInstanceId" resultType="com.timeline.story.vo.StoryItemVo">
|
||||
SELECT
|
||||
instance_id,
|
||||
si.description,
|
||||
si.location,
|
||||
title,
|
||||
story_instance_id,
|
||||
si.story_item_time as story_item_time,
|
||||
si.update_time,
|
||||
si.create_id AS create_id,
|
||||
si.create_time AS create_time,
|
||||
u1.username AS create_name,
|
||||
si.update_id AS update_id,
|
||||
si.update_time AS update_time,
|
||||
u2.username AS update_name
|
||||
FROM
|
||||
story_item si
|
||||
LEFT JOIN user u1 ON si.create_id = u1.user_id
|
||||
LEFT JOIN `user` u2 ON si.update_id = u2.user_id
|
||||
WHERE
|
||||
story_instance_id = #{storyInstanceId}
|
||||
AND is_delete = 0
|
||||
ORDER BY
|
||||
story_item_time DESC
|
||||
</select>
|
||||
|
||||
<select id="countByStoryId" resultType="int">
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
<select id="selectByInstanceId" resultType="com.timeline.story.entity.Story">
|
||||
SELECT
|
||||
s.*,
|
||||
u1.user_name as owner_name,
|
||||
u2.user_name as update_name,
|
||||
u1.username as owner_name,
|
||||
u2.username as update_name,
|
||||
(SELECT COUNT(*) FROM story_item si WHERE si.story_instance_id = s.instance_id AND si.is_delete = 0) as item_count
|
||||
|
||||
FROM story s
|
||||
@@ -39,15 +39,29 @@
|
||||
</select>
|
||||
|
||||
<select id="selectByOwnerId" resultType="com.timeline.story.entity.Story">
|
||||
SELECT s.*,
|
||||
u1.user_name as owner_name,
|
||||
u2.user_name as update_name,
|
||||
(SELECT COUNT(*) FROM story_item si WHERE si.story_instance_id = s.instance_id AND si.is_delete = 0) as item_count
|
||||
FROM story s
|
||||
LEFT JOIN user u1
|
||||
ON s.owner_id = u1.user_id AND u1.is_deleted = 0
|
||||
LEFT JOIN user u2 ON s.update_id = u2.user_id AND u2.is_deleted = 0
|
||||
WHERE s.owner_id = #{ownerId} AND s.is_delete = 0
|
||||
SELECT
|
||||
s.*,
|
||||
u1.username AS owner_name,
|
||||
u2.username AS update_name,
|
||||
sp.permission_type AS permission_type,
|
||||
( SELECT COUNT(*) FROM story_item si WHERE si.story_instance_id = s.instance_id AND si.is_delete = 0 ) AS item_count
|
||||
FROM
|
||||
story s
|
||||
LEFT JOIN user u1 ON s.owner_id = u1.user_id
|
||||
AND u1.is_deleted = 0
|
||||
LEFT JOIN user u2 ON s.update_id = u2.user_id
|
||||
AND u2.is_deleted = 0
|
||||
LEFT JOIN story_permission sp ON s.instance_id = sp.story_instance_id AND sp.user_id = #{owerId}
|
||||
WHERE
|
||||
s.instance_id IN ( SELECT story_instance_id FROM story_permission WHERE user_id = #{owerId} )
|
||||
AND s.is_delete = 0
|
||||
</select>
|
||||
|
||||
<update id="touchUpdate">
|
||||
UPDATE story
|
||||
SET update_id = #{updateId},
|
||||
update_time = NOW()
|
||||
WHERE instance_id = #{instanceId}
|
||||
</update>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
<insert id="insert">
|
||||
INSERT INTO story_permission (permission_id, story_instance_id, user_id, permission_type)
|
||||
VALUES (#{permissionId}, #{storyInstanceId}, #{userId}, #{permissionType})
|
||||
</insert>
|
||||
|
||||
</insert>
|
||||
<update id="update">
|
||||
UPDATE story_permission
|
||||
SET permission_type = #{permissionType},
|
||||
|
||||
Reference in New Issue
Block a user