用户信息管理,帖子模块

main
2991692032 1 month ago
parent f632c54b7f
commit f4a8658562

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SqlDialectMappings">
<file url="file://$PROJECT_DIR$/unilife-server/src/main/resources/db/init.sql" dialect="MySQL" />
</component>
</project>

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

File diff suppressed because it is too large Load Diff

@ -0,0 +1,18 @@
package com.unilife.config;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SwaggerConfig {
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info().title("UniLife API").version("1.0.0"));
}
}

@ -0,0 +1,86 @@
package com.unilife.controller;
import com.unilife.common.result.Result;
import com.unilife.model.dto.CreatePostDTO;
import com.unilife.model.dto.UpdatePostDTO;
import com.unilife.service.PostService;
import com.unilife.utils.BaseContext;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@Tag(name = "帖子管理")
@RestController
@RequestMapping("/posts")
@Slf4j
public class PostController {
@Autowired
private PostService postService;
@Operation(summary = "创建帖子")
@PostMapping
public Result<?> createPost(@RequestBody CreatePostDTO createPostDTO) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return postService.createPost(userId, createPostDTO);
}
@Operation(summary = "获取帖子详情")
@GetMapping("/{id}")
public Result<?> getPostDetail(@PathVariable("id") Long postId) {
// 从当前上下文获取用户ID可能为null未登录用户
Long userId = BaseContext.getId();
return postService.getPostDetail(postId, userId);
}
@Operation(summary = "获取帖子列表")
@GetMapping
public Result<?> getPostList(
@RequestParam(value = "category", required = false) Long categoryId,
@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "size", defaultValue = "10") Integer size,
@RequestParam(value = "sort", defaultValue = "latest") String sort) {
return postService.getPostList(categoryId, page, size, sort);
}
@Operation(summary = "更新帖子")
@PutMapping("/{id}")
public Result<?> updatePost(
@PathVariable("id") Long postId,
@RequestBody UpdatePostDTO updatePostDTO) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return postService.updatePost(postId, userId, updatePostDTO);
}
@Operation(summary = "删除帖子")
@DeleteMapping("/{id}")
public Result<?> deletePost(@PathVariable("id") Long postId) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return postService.deletePost(postId, userId);
}
@Operation(summary = "点赞/取消点赞帖子")
@PostMapping("/{id}/like")
public Result<?> likePost(@PathVariable("id") Long postId) {
// 从当前上下文获取用户ID
Long userId = BaseContext.getId();
if (userId == null) {
return Result.error(401, "未登录");
}
return postService.likePost(postId, userId);
}
}

@ -0,0 +1,82 @@
package com.unilife.mapper;
import com.unilife.model.entity.Post;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 访
*/
@Mapper
public interface PostMapper {
/**
*
* @param post
*/
void insert(Post post);
/**
* ID
* @param id ID
* @return
*/
Post getById(Long id);
/**
*
* @param categoryId IDnull
* @return
*/
List<Post> getListByCategory(@Param("categoryId") Long categoryId,@Param("sort")String sort);
/**
*
* @param categoryId IDnull
* @return
*/
Integer getCount(@Param("categoryId") Long categoryId);
/**
*
* @param post
*/
void update(Post post);
/**
*
* @param id ID
*/
void delete(Long id);
/**
*
* @param id ID
*/
void incrementViewCount(Long id);
/**
*
* @param id ID
*/
void incrementLikeCount(Long id);
/**
*
* @param id ID
*/
void decrementLikeCount(Long id);
/**
*
* @param id ID
*/
void incrementCommentCount(Long id);
/**
*
* @param id ID
*/
void decrementCommentCount(Long id);
}

@ -0,0 +1,28 @@
package com.unilife.model.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CreatePostDTO {
/**
*
*/
private String title;
/**
*
*/
private String content;
/**
* ID
*/
private Long categoryId;
}

@ -0,0 +1,16 @@
package com.unilife.model.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UpdateEmailDTO {
private String email;
private String code;
}

@ -0,0 +1,16 @@
package com.unilife.model.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UpdatePasswordDTO {
private String code;
private String newPassword;
}

@ -0,0 +1,28 @@
package com.unilife.model.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UpdatePostDTO {
/**
*
*/
private String title;
/**
*
*/
private String content;
/**
* ID
*/
private Long categoryId;
}

@ -0,0 +1,20 @@
package com.unilife.model.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UpdateProfileDTO {
private String nickname;
private String bio;
private Byte gender;
private String department;
private String major;
private String grade;
}

@ -0,0 +1,61 @@
package com.unilife.model.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
*
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Category implements Serializable {
private static final long serialVersionUID = 1L;
/**
* ID
*/
private Long id;
/**
*
*/
private String name;
/**
*
*/
private String description;
/**
*
*/
private String icon;
/**
*
*/
private Integer sort = 0;
/**
* 0-, 1-
*/
private Byte status = 1;
/**
*
*/
private LocalDateTime createdAt;
/**
*
*/
private LocalDateTime updatedAt;
}

@ -0,0 +1,66 @@
package com.unilife.model.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
*
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Comment implements Serializable {
private static final long serialVersionUID = 1L;
/**
* ID
*/
private Long id;
/**
* ID
*/
private Long postId;
/**
* ID
*/
private Long userId;
/**
*
*/
private String content;
/**
* ID
*/
private Long parentId;
/**
*
*/
private Integer likeCount = 0;
/**
* 0-, 1-
*/
private Byte status = 1;
/**
*
*/
private LocalDateTime createdAt;
/**
*
*/
private LocalDateTime updatedAt;
}

@ -0,0 +1,76 @@
package com.unilife.model.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
*
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Post implements Serializable {
private static final long serialVersionUID = 1L;
/**
* ID
*/
private Long id;
/**
* ID
*/
private Long userId;
/**
*
*/
private String title;
/**
*
*/
private String content;
/**
* ID
*/
private Long categoryId;
/**
*
*/
private Integer viewCount = 0;
/**
*
*/
private Integer likeCount = 0;
/**
*
*/
private Integer commentCount = 0;
/**
* 0-, 1-, 2-
*/
private Byte status = 1;
/**
*
*/
private LocalDateTime createdAt;
/**
*
*/
private LocalDateTime updatedAt;
}

@ -0,0 +1,77 @@
package com.unilife.model.vo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
*
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class PostListVO {
/**
* ID
*/
private Long id;
/**
*
*/
private String title;
/**
*
*/
private String summary;
/**
* ID
*/
private Long userId;
/**
*
*/
private String nickname;
/**
*
*/
private String avatar;
/**
* ID
*/
private Long categoryId;
/**
*
*/
private String categoryName;
/**
*
*/
private Integer viewCount;
/**
*
*/
private Integer likeCount;
/**
*
*/
private Integer commentCount;
/**
*
*/
private LocalDateTime createdAt;
}

@ -0,0 +1,87 @@
package com.unilife.model.vo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
*
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class PostVO {
/**
* ID
*/
private Long id;
/**
*
*/
private String title;
/**
*
*/
private String content;
/**
* ID
*/
private Long userId;
/**
*
*/
private String nickname;
/**
*
*/
private String avatar;
/**
* ID
*/
private Long categoryId;
/**
*
*/
private String categoryName;
/**
*
*/
private Integer viewCount;
/**
*
*/
private Integer likeCount;
/**
*
*/
private Integer commentCount;
/**
*
*/
private Boolean isLiked;
/**
*
*/
private LocalDateTime createdAt;
/**
*
*/
private LocalDateTime updatedAt;
}

@ -0,0 +1,61 @@
package com.unilife.service;
import com.unilife.common.result.Result;
import com.unilife.model.dto.CreatePostDTO;
import com.unilife.model.dto.UpdatePostDTO;
/**
*
*/
public interface PostService {
/**
*
* @param userId ID
* @param createPostDTO DTO
* @return
*/
Result createPost(Long userId, CreatePostDTO createPostDTO);
/**
*
* @param postId ID
* @param userId IDnull
* @return
*/
Result getPostDetail(Long postId, Long userId);
/**
*
* @param categoryId IDnull
* @param page
* @param size
* @param sort latest-hot-
* @return
*/
Result getPostList(Long categoryId, Integer page, Integer size, String sort);
/**
*
* @param postId ID
* @param userId ID
* @param updatePostDTO DTO
* @return
*/
Result updatePost(Long postId, Long userId, UpdatePostDTO updatePostDTO);
/**
*
* @param postId ID
* @param userId ID
* @return
*/
Result deletePost(Long postId, Long userId);
/**
* /
* @param postId ID
* @param userId ID
* @return
*/
Result likePost(Long postId, Long userId);
}

@ -0,0 +1,259 @@
package com.unilife.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.unilife.common.result.Result;
// import com.unilife.mapper.CategoryMapper;
import com.unilife.mapper.PostMapper;
import com.unilife.mapper.UserMapper;
import com.unilife.model.dto.CreatePostDTO;
import com.unilife.model.dto.UpdatePostDTO;
// import com.unilife.model.entity.Category;
import com.unilife.model.entity.Post;
import com.unilife.model.entity.User;
import com.unilife.model.vo.PostListVO;
import com.unilife.model.vo.PostVO;
import com.unilife.service.PostService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Slf4j
@Service
public class PostServiceImpl implements PostService {
@Autowired
private PostMapper postMapper;
@Autowired
private UserMapper userMapper;
// @Autowired
// private CategoryMapper categoryMapper;
@Override
public Result createPost(Long userId, CreatePostDTO createPostDTO) {
// 检查用户是否存在
User user = userMapper.getUserById(userId);
if (user == null) {
return Result.error(404, "用户不存在");
}
// 检查分类是否存在
// 注意这里假设已经创建了CategoryMapper接口实际开发中需要先创建
// Category category = categoryMapper.getById(createPostDTO.getCategoryId());
// if (category == null) {
// return Result.error(404, "分类不存在");
// }
// 创建帖子
Post post = new Post();
post.setUserId(userId);
post.setTitle(createPostDTO.getTitle());
post.setContent(createPostDTO.getContent());
post.setCategoryId(createPostDTO.getCategoryId());
post.setViewCount(0);
post.setLikeCount(0);
post.setCommentCount(0);
post.setStatus((byte) 1);
// 保存帖子
postMapper.insert(post);
Map<String, Object> data = new HashMap<>();
data.put("postId", post.getId());
return Result.success(data, "发布成功");
}
@Override
public Result getPostDetail(Long postId, Long userId) {
// 获取帖子
Post post = postMapper.getById(postId);
if (post == null) {
return Result.error(404, "帖子不存在");
}
// 增加浏览次数
postMapper.incrementViewCount(postId);
// 获取发布用户信息
User user = userMapper.getUserById(post.getUserId());
// 获取分类信息
// 注意这里假设已经创建了CategoryMapper接口实际开发中需要先创建
// Category category = categoryMapper.getById(post.getCategoryId());
// 构建返回数据
PostVO postVO = PostVO.builder()
.id(post.getId())
.title(post.getTitle())
.content(post.getContent())
.userId(post.getUserId())
.nickname(user != null ? user.getNickname() : "未知用户")
.avatar(user != null ? user.getAvatar() : null)
.categoryId(post.getCategoryId())
.categoryName("未知分类") // 实际开发中应该从category对象获取
.viewCount(post.getViewCount() + 1) // 已经增加了浏览次数
.likeCount(post.getLikeCount())
.commentCount(post.getCommentCount())
.isLiked(false) // 实际开发中应该查询用户是否点赞
.createdAt(post.getCreatedAt())
.updatedAt(post.getUpdatedAt())
.build();
return Result.success(postVO);
}
@Override
public Result getPostList(Long categoryId, Integer page, Integer size, String sort) {
// 参数校验
if (page == null || page < 1) page = 1;
if (size == null || size < 1 || size > 50) size = 10;
if (StrUtil.isBlank(sort)) sort = "latest";
// 只使用PageHelper进行分页不设置排序
PageHelper.startPage(page, size);
// 调用mapper方法传入排序参数
List<Post> posts = postMapper.getListByCategory(categoryId, sort);
// 获取分页信息
PageInfo<Post> pageInfo = new PageInfo<>(posts);
// 转换为VO
List<PostListVO> postListVOs = posts.stream().map(post -> {
User user = userMapper.getUserById(post.getUserId());
return PostListVO.builder()
.id(post.getId())
.title(post.getTitle())
.summary(generateSummary(post.getContent()))
.userId(post.getUserId())
.nickname(user != null ? user.getNickname() : "未知用户")
.avatar(user != null ? user.getAvatar() : null)
.categoryId(post.getCategoryId())
.categoryName("未知分类")
.viewCount(post.getViewCount())
.likeCount(post.getLikeCount())
.commentCount(post.getCommentCount())
.createdAt(post.getCreatedAt())
.build();
}).collect(Collectors.toList());
// 返回结果
Map<String, Object> data = new HashMap<>();
data.put("total", pageInfo.getTotal());
data.put("list", postListVOs);
data.put("pages", pageInfo.getPages());
return Result.success(data);
}
@Override
public Result updatePost(Long postId, Long userId, UpdatePostDTO updatePostDTO) {
// 获取帖子
Post post = postMapper.getById(postId);
if (post == null) {
return Result.error(404, "帖子不存在");
}
// 检查是否有权限更新
if (!post.getUserId().equals(userId)) {
return Result.error(403, "无权限更新此帖子");
}
// 检查分类是否存在
// 注意这里假设已经创建了CategoryMapper接口实际开发中需要先创建
// Category category = categoryMapper.getById(updatePostDTO.getCategoryId());
// if (category == null) {
// return Result.error(404, "分类不存在");
// }
// 更新帖子
post.setTitle(updatePostDTO.getTitle());
post.setContent(updatePostDTO.getContent());
post.setCategoryId(updatePostDTO.getCategoryId());
// 保存更新
postMapper.update(post);
return Result.success(null, "更新成功");
}
@Override
public Result deletePost(Long postId, Long userId) {
// 获取帖子
Post post = postMapper.getById(postId);
if (post == null) {
return Result.error(404, "帖子不存在");
}
// 检查是否有权限删除
if (!post.getUserId().equals(userId)) {
return Result.error(403, "无权限删除此帖子");
}
// 删除帖子(逻辑删除)
postMapper.delete(postId);
return Result.success(null, "删除成功");
}
@Override
public Result likePost(Long postId, Long userId) {
// 获取帖子
Post post = postMapper.getById(postId);
if (post == null) {
return Result.error(404, "帖子不存在");
}
// 检查用户是否已点赞
// 注意这里需要创建一个点赞表和相应的Mapper实际开发中需要先创建
boolean isLiked = false; // postLikeMapper.isLiked(postId, userId);
if (isLiked) {
// 取消点赞
// postLikeMapper.delete(postId, userId);
postMapper.decrementLikeCount(postId);
return Result.success(null, "取消点赞成功");
} else {
// 添加点赞
// postLikeMapper.insert(postId, userId);
postMapper.incrementLikeCount(postId);
return Result.success(null, "点赞成功");
}
}
/**
*
* @param content
* @return
*/
private String generateSummary(String content) {
if (StrUtil.isBlank(content)) {
return "";
}
// 去除HTML标签
content = content.replaceAll("<[^>]+>", "");
// 截取前100个字符作为摘要
int length = Math.min(content.length(), 100);
String summary = content.substring(0, length);
// 如果内容超过100个字符添加省略号
if (content.length() > 100) {
summary += "...";
}
return summary;
}
}

@ -0,0 +1,187 @@
-- UniLife数据库初始化脚本
-- 创建数据库(如果不存在)
CREATE DATABASE IF NOT EXISTS UniLife DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 使用数据库
USE UniLife;
-- 用户表
CREATE TABLE IF NOT EXISTS `users` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID',
`username` VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名',
`email` VARCHAR(100) NOT NULL UNIQUE COMMENT '邮箱地址(学校邮箱)',
`password` VARCHAR(255) NOT NULL COMMENT '密码(加密存储)',
`nickname` VARCHAR(50) NOT NULL COMMENT '昵称',
`avatar` VARCHAR(255) DEFAULT NULL COMMENT '头像URL',
`bio` TEXT DEFAULT NULL COMMENT '个人简介',
`gender` TINYINT DEFAULT 0 COMMENT '性别0-未知, 1-男, 2-女)',
`student_id` VARCHAR(20) UNIQUE DEFAULT NULL COMMENT '学号',
`department` VARCHAR(100) DEFAULT NULL COMMENT '院系',
`major` VARCHAR(100) DEFAULT NULL COMMENT '专业',
`grade` VARCHAR(20) DEFAULT NULL COMMENT '年级',
`points` INT DEFAULT 0 COMMENT '积分',
`role` TINYINT DEFAULT 0 COMMENT '角色0-普通用户, 1-版主, 2-管理员)',
`status` TINYINT DEFAULT 1 COMMENT '状态0-禁用, 1-启用)',
`is_verified` TINYINT DEFAULT 0 COMMENT '是否验证0-未验证, 1-已验证)',
`login_ip` VARCHAR(50) DEFAULT NULL COMMENT '最近登录IP',
`login_time` DATETIME DEFAULT NULL COMMENT '最近登录时间',
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
INDEX `idx_email` (`email`),
INDEX `idx_username` (`username`),
INDEX `idx_student_id` (`student_id`),
INDEX `idx_role` (`role`),
INDEX `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';
-- 分类表
CREATE TABLE IF NOT EXISTS `categories` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '分类ID',
`name` VARCHAR(50) NOT NULL UNIQUE COMMENT '分类名称',
`description` VARCHAR(255) DEFAULT NULL COMMENT '分类描述',
`icon` VARCHAR(255) DEFAULT NULL COMMENT '分类图标',
`sort` INT DEFAULT 0 COMMENT '排序',
`status` TINYINT DEFAULT 1 COMMENT '状态0-禁用, 1-启用)',
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
INDEX `idx_status` (`status`),
INDEX `idx_sort` (`sort`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='分类表';
-- 帖子表
CREATE TABLE IF NOT EXISTS `posts` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '帖子ID',
`user_id` BIGINT NOT NULL COMMENT '发布用户ID',
`title` VARCHAR(100) NOT NULL COMMENT '帖子标题',
`content` TEXT NOT NULL COMMENT '帖子内容',
`category_id` BIGINT NOT NULL COMMENT '分类ID',
`view_count` INT DEFAULT 0 COMMENT '浏览次数',
`like_count` INT DEFAULT 0 COMMENT '点赞次数',
`comment_count` INT DEFAULT 0 COMMENT '评论次数',
`status` TINYINT DEFAULT 1 COMMENT '状态0-删除, 1-正常, 2-置顶)',
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
INDEX `idx_user_id` (`user_id`),
INDEX `idx_category_id` (`category_id`),
INDEX `idx_status` (`status`),
INDEX `idx_created_at` (`created_at`),
FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='帖子表';
-- 评论表
CREATE TABLE IF NOT EXISTS `comments` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '评论ID',
`post_id` BIGINT NOT NULL COMMENT '帖子ID',
`user_id` BIGINT NOT NULL COMMENT '评论用户ID',
`content` TEXT NOT NULL COMMENT '评论内容',
`parent_id` BIGINT DEFAULT NULL COMMENT '父评论ID回复某条评论',
`like_count` INT DEFAULT 0 COMMENT '点赞次数',
`status` TINYINT DEFAULT 1 COMMENT '状态0-删除, 1-正常)',
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
INDEX `idx_post_id` (`post_id`),
INDEX `idx_user_id` (`user_id`),
INDEX `idx_parent_id` (`parent_id`),
INDEX `idx_status` (`status`),
FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`) ON DELETE CASCADE,
FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
FOREIGN KEY (`parent_id`) REFERENCES `comments` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='评论表';
-- 点赞表(用户-帖子)
CREATE TABLE IF NOT EXISTS `post_likes` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '点赞ID',
`user_id` BIGINT NOT NULL COMMENT '用户ID',
`post_id` BIGINT NOT NULL COMMENT '帖子ID',
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
UNIQUE KEY `uk_user_post` (`user_id`, `post_id`),
FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='帖子点赞表';
-- 点赞表(用户-评论)
CREATE TABLE IF NOT EXISTS `comment_likes` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '点赞ID',
`user_id` BIGINT NOT NULL COMMENT '用户ID',
`comment_id` BIGINT NOT NULL COMMENT '评论ID',
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
UNIQUE KEY `uk_user_comment` (`user_id`, `comment_id`),
FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
FOREIGN KEY (`comment_id`) REFERENCES `comments` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='评论点赞表';
-- 资源表
CREATE TABLE IF NOT EXISTS `resources` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '资源ID',
`user_id` BIGINT NOT NULL COMMENT '上传用户ID',
`title` VARCHAR(100) NOT NULL COMMENT '资源标题',
`description` TEXT DEFAULT NULL COMMENT '资源描述',
`file_url` VARCHAR(255) NOT NULL COMMENT '文件URL',
`file_size` BIGINT NOT NULL COMMENT '文件大小(字节)',
`file_type` VARCHAR(50) NOT NULL COMMENT '文件类型',
`category_id` BIGINT NOT NULL COMMENT '分类ID',
`download_count` INT DEFAULT 0 COMMENT '下载次数',
`like_count` INT DEFAULT 0 COMMENT '点赞次数',
`status` TINYINT DEFAULT 1 COMMENT '状态0-删除, 1-正常)',
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
INDEX `idx_user_id` (`user_id`),
INDEX `idx_category_id` (`category_id`),
INDEX `idx_status` (`status`),
FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='资源表';
-- 课程表
CREATE TABLE IF NOT EXISTS `courses` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '课程ID',
`user_id` BIGINT NOT NULL COMMENT '用户ID',
`name` VARCHAR(100) NOT NULL COMMENT '课程名称',
`teacher` VARCHAR(50) DEFAULT NULL COMMENT '教师姓名',
`location` VARCHAR(100) DEFAULT NULL COMMENT '上课地点',
`day_of_week` TINYINT NOT NULL COMMENT '星期几1-7',
`start_time` TIME NOT NULL COMMENT '开始时间',
`end_time` TIME NOT NULL COMMENT '结束时间',
`start_week` TINYINT NOT NULL COMMENT '开始周次',
`end_week` TINYINT NOT NULL COMMENT '结束周次',
`color` VARCHAR(20) DEFAULT NULL COMMENT '显示颜色',
`status` TINYINT DEFAULT 1 COMMENT '状态0-删除, 1-正常)',
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
INDEX `idx_user_id` (`user_id`),
FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='课程表';
-- 日程表
CREATE TABLE IF NOT EXISTS `schedules` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '日程ID',
`user_id` BIGINT NOT NULL COMMENT '用户ID',
`title` VARCHAR(100) NOT NULL COMMENT '日程标题',
`description` TEXT DEFAULT NULL COMMENT '日程描述',
`start_time` DATETIME NOT NULL COMMENT '开始时间',
`end_time` DATETIME NOT NULL COMMENT '结束时间',
`location` VARCHAR(100) DEFAULT NULL COMMENT '地点',
`is_all_day` TINYINT DEFAULT 0 COMMENT '是否全天0-否, 1-是)',
`reminder` TINYINT DEFAULT NULL COMMENT '提醒时间(分钟)',
`color` VARCHAR(20) DEFAULT NULL COMMENT '显示颜色',
`status` TINYINT DEFAULT 1 COMMENT '状态0-删除, 1-正常)',
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
INDEX `idx_user_id` (`user_id`),
INDEX `idx_start_time` (`start_time`),
FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='日程表';
-- 初始化分类数据
INSERT INTO `categories` (`name`, `description`, `icon`, `sort`, `status`) VALUES
('学习交流', '讨论学习相关话题', 'icon-study', 1, 1),
('校园生活', '分享校园生活点滴', 'icon-campus', 2, 1),
('兴趣爱好', '交流各类兴趣爱好', 'icon-hobby', 3, 1),
('求职就业', '分享求职经验和就业信息', 'icon-job', 4, 1),
('资源共享', '分享各类学习资源', 'icon-resource', 5, 1);
-- 初始化管理员账号
INSERT INTO `users` (`username`, `email`, `password`, `nickname`, `role`, `status`, `is_verified`) VALUES
('admin', 'admin@unilife.com', '123456', '系统管理员', 2, 1, 1);

@ -0,0 +1,110 @@
<?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.unilife.mapper.PostMapper">
<resultMap id="postResultMap" type="com.unilife.model.entity.Post">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="title" property="title"/>
<result column="content" property="content"/>
<result column="category_id" property="categoryId"/>
<result column="view_count" property="viewCount"/>
<result column="like_count" property="likeCount"/>
<result column="comment_count" property="commentCount"/>
<result column="status" property="status"/>
<result column="created_at" property="createdAt"/>
<result column="updated_at" property="updatedAt"/>
</resultMap>
<insert id="insert" parameterType="com.unilife.model.entity.Post" useGeneratedKeys="true" keyProperty="id">
INSERT INTO posts (
user_id, title, content, category_id, view_count, like_count, comment_count, status, created_at, updated_at
) VALUES (
#{userId}, #{title}, #{content}, #{categoryId}, #{viewCount}, #{likeCount}, #{commentCount}, #{status}, NOW(), NOW()
)
</insert>
<select id="getById" resultMap="postResultMap">
SELECT id, user_id, title, content, category_id, view_count, like_count, comment_count, status, created_at, updated_at
FROM posts
WHERE id = #{id} AND status != 0
</select>
<select id="getListByCategory" resultType="com.unilife.model.entity.Post">
SELECT * FROM posts
<where>
<if test="categoryId != null">
category_id = #{categoryId}
</if>
</where>
<choose>
<when test="sort == 'hot'">
ORDER BY view_count DESC
</when>
<when test="sort == 'likes'">
ORDER BY like_count DESC
</when>
<when test="sort == 'comments'">
ORDER BY comment_count DESC
</when>
<otherwise>
ORDER BY created_at DESC
</otherwise>
</choose>
</select>
<select id="getCount" resultType="java.lang.Integer">
SELECT COUNT(*)
FROM posts
WHERE status != 0
<if test="categoryId != null">
AND category_id = #{categoryId}
</if>
</select>
<update id="update" parameterType="com.unilife.model.entity.Post">
UPDATE posts
SET title = #{title},
content = #{content},
category_id = #{categoryId},
status = #{status},
updated_at = NOW()
WHERE id = #{id}
</update>
<update id="delete">
UPDATE posts
SET status = 0,
updated_at = NOW()
WHERE id = #{id}
</update>
<update id="incrementViewCount">
UPDATE posts
SET view_count = view_count + 1
WHERE id = #{id}
</update>
<update id="incrementLikeCount">
UPDATE posts
SET like_count = like_count + 1
WHERE id = #{id}
</update>
<update id="decrementLikeCount">
UPDATE posts
SET like_count = GREATEST(like_count - 1, 0)
WHERE id = #{id}
</update>
<update id="incrementCommentCount">
UPDATE posts
SET comment_count = comment_count + 1
WHERE id = #{id}
</update>
<update id="decrementCommentCount">
UPDATE posts
SET comment_count = GREATEST(comment_count - 1, 0)
WHERE id = #{id}
</update>
</mapper>
Loading…
Cancel
Save