1. 获取所有分区 2. 根据分区获得作品列表
增加了分区以及作品的swagger接口描述,并调整到正确的项目结构中
Change-Id: I36850cd93eeb93992cad7d342f2688dd8e50e6bc
diff --git a/pom.xml b/pom.xml
index 4522235..37d24a1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -143,6 +143,18 @@
<version>2.0.1</version>
</dependency>
+ <!-- Spring Data JPA -->
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-data-jpa</artifactId>
+ </dependency>
+
+ <!-- Jackson(JSON处理) -->
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ </dependency>
+
</dependencies>
<build>
diff --git a/src/main/java/edu/bjtu/groupone/backend/api/CategoryController.java b/src/main/java/edu/bjtu/groupone/backend/api/CategoryController.java
new file mode 100644
index 0000000..eb87513
--- /dev/null
+++ b/src/main/java/edu/bjtu/groupone/backend/api/CategoryController.java
@@ -0,0 +1,26 @@
+package edu.bjtu.groupone.backend.api;
+
+import edu.bjtu.groupone.backend.domain.dto.CategoryDTO;
+import edu.bjtu.groupone.backend.service.CategoryService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/categories")
+@Tag(name = "Category", description = "分区相关接口")
+public class CategoryController {
+
+ @Autowired
+ private CategoryService categoryService;
+
+ @GetMapping
+ @Operation(summary = "获取所有分区(树形结构)", description = "返回所有分区及其子分区")
+ public ResponseEntity<List<CategoryDTO>> getCategories() {
+ return ResponseEntity.ok(categoryService.getCategoryTree());
+ }
+}
diff --git a/src/main/java/edu/bjtu/groupone/backend/api/user/UserController.java b/src/main/java/edu/bjtu/groupone/backend/api/UserController.java
similarity index 99%
rename from src/main/java/edu/bjtu/groupone/backend/api/user/UserController.java
rename to src/main/java/edu/bjtu/groupone/backend/api/UserController.java
index 4d85c18..4c08711 100644
--- a/src/main/java/edu/bjtu/groupone/backend/api/user/UserController.java
+++ b/src/main/java/edu/bjtu/groupone/backend/api/UserController.java
@@ -1,5 +1,5 @@
// src/main/java/edu/bjtu/groupone/backend/api/user/UserController.java
-package edu.bjtu.groupone.backend.api.user;
+package edu.bjtu.groupone.backend.api;
import edu.bjtu.groupone.backend.domain.dto.*;
import edu.bjtu.groupone.backend.domain.entity.User;
diff --git a/src/main/java/edu/bjtu/groupone/backend/api/WorkController.java b/src/main/java/edu/bjtu/groupone/backend/api/WorkController.java
new file mode 100644
index 0000000..a56d1cd
--- /dev/null
+++ b/src/main/java/edu/bjtu/groupone/backend/api/WorkController.java
@@ -0,0 +1,29 @@
+package edu.bjtu.groupone.backend.api;
+
+import edu.bjtu.groupone.backend.domain.dto.WorkResponse;
+import edu.bjtu.groupone.backend.service.WorkService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/api/works")
+@Tag(name = "Work", description = "作品相关接口")
+public class WorkController {
+
+ @Autowired
+ private WorkService workService;
+
+ @GetMapping
+ @Operation(summary = "获取作品列表", description = "可按分区分页查询作品")
+ public ResponseEntity<Page<WorkResponse>> getWorks(
+ @Parameter(description = "分区 ID(可选)") @RequestParam(required = false) Long categoryId,
+ @Parameter(description = "页码,从 1 开始", example = "1") @RequestParam(defaultValue = "1") int page,
+ @Parameter(description = "每页数量", example = "20") @RequestParam(defaultValue = "20") int size) {
+ return ResponseEntity.ok(workService.getWorks(categoryId, page, size));
+ }
+}
diff --git a/src/main/java/edu/bjtu/groupone/backend/config/PaginationConfig.java b/src/main/java/edu/bjtu/groupone/backend/config/PaginationConfig.java
new file mode 100644
index 0000000..e8261dd
--- /dev/null
+++ b/src/main/java/edu/bjtu/groupone/backend/config/PaginationConfig.java
@@ -0,0 +1,16 @@
+package edu.bjtu.groupone.backend.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
+
+@Configuration
+public class PaginationConfig {
+ @Bean
+ public PageableHandlerMethodArgumentResolver pageableResolver() {
+ PageableHandlerMethodArgumentResolver resolver = new PageableHandlerMethodArgumentResolver();
+ resolver.setOneIndexedParameters(true); // 启用1-based页码
+ resolver.setMaxPageSize(100); // 限制最大每页数量
+ return resolver;
+ }
+}
diff --git a/src/main/java/edu/bjtu/groupone/backend/domain/dto/CategoryDTO.java b/src/main/java/edu/bjtu/groupone/backend/domain/dto/CategoryDTO.java
new file mode 100644
index 0000000..0f65c73
--- /dev/null
+++ b/src/main/java/edu/bjtu/groupone/backend/domain/dto/CategoryDTO.java
@@ -0,0 +1,32 @@
+package edu.bjtu.groupone.backend.domain.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+@Schema(description = "分区信息")
+public class CategoryDTO {
+
+ @Schema(description = "分区 ID", example = "1")
+ private Long id;
+
+ @Schema(description = "分区名称", example = "文学艺术")
+ private String name;
+
+ @Schema(description = "子分区")
+ private List<CategoryDTO> children = new ArrayList<>();
+
+ public CategoryDTO(Long id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public CategoryDTO(Long id, String name, List<CategoryDTO> children) {
+ this.id = id;
+ this.name = name;
+ this.children = children;
+ }
+}
diff --git a/src/main/java/edu/bjtu/groupone/backend/domain/dto/WorkResponse.java b/src/main/java/edu/bjtu/groupone/backend/domain/dto/WorkResponse.java
new file mode 100644
index 0000000..bdc15e1
--- /dev/null
+++ b/src/main/java/edu/bjtu/groupone/backend/domain/dto/WorkResponse.java
@@ -0,0 +1,26 @@
+package edu.bjtu.groupone.backend.domain.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data
+@AllArgsConstructor
+@Schema(description = "作品信息")
+public class WorkResponse {
+
+ @Schema(description = "作品 ID", example = "101")
+ private Long id;
+
+ @Schema(description = "作品标题", example = "《我的世界》")
+ private String title;
+
+ @Schema(description = "作者", example = "张三")
+ private String author;
+
+ @Schema(description = "浏览量", example = "1234")
+ private Integer views;
+
+ @Schema(description = "所属分区 ID", example = "1")
+ private Long categoryId;
+}
diff --git a/src/main/java/edu/bjtu/groupone/backend/domain/entity/Category.java b/src/main/java/edu/bjtu/groupone/backend/domain/entity/Category.java
new file mode 100644
index 0000000..de02aab
--- /dev/null
+++ b/src/main/java/edu/bjtu/groupone/backend/domain/entity/Category.java
@@ -0,0 +1,59 @@
+package edu.bjtu.groupone.backend.domain.entity;
+
+import jakarta.persistence.*;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import java.util.ArrayList;
+import java.util.List;
+
+@Entity
+@Table(name = "categories") // 显式指定表名
+@Data // 自动生成 getter/setter/toString
+@NoArgsConstructor // JPA 要求无参构造
+@AllArgsConstructor // 全参构造
+@Builder // 支持 Builder 模式
+@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) // 解决 Jackson 序列化问题
+
+public class Category {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(nullable = false, length = 50) // 数据库字段约束
+ private String name;
+
+ @ManyToOne(fetch = FetchType.LAZY) // 推荐懒加载
+ @JoinColumn(
+ name = "parent_id",
+ foreignKey = @ForeignKey(name = "fk_category_parent")
+ )
+ @JsonIgnoreProperties("children") // 避免双向循环序列化
+ private Category parent;
+
+ @OneToMany(
+ mappedBy = "parent",
+ cascade = CascadeType.ALL,
+ orphanRemoval = true
+ )
+ @Builder.Default // 保证 Lombok Builder 初始化集合
+ private List<Category> children = new ArrayList<>();
+
+ // 双向关联辅助方法
+ public void addChild(Category child) {
+ children.add(child);
+ child.setParent(this);
+ }
+
+ public void removeChild(Category child) {
+ children.remove(child);
+ child.setParent(null);
+ }
+ public Category(Long id, String name, Category parent) {
+ this.id = id;
+ this.name = name;
+ this.parent = parent;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/edu/bjtu/groupone/backend/domain/entity/Work.java b/src/main/java/edu/bjtu/groupone/backend/domain/entity/Work.java
new file mode 100644
index 0000000..55e036b
--- /dev/null
+++ b/src/main/java/edu/bjtu/groupone/backend/domain/entity/Work.java
@@ -0,0 +1,32 @@
+package edu.bjtu.groupone.backend.domain.entity;
+
+import jakarta.persistence.*;
+import lombok.*;
+
+@Entity
+@Table(name = "works")
+@Data // 自动生成 getter/setter/toString
+@NoArgsConstructor // JPA 必需的无参构造
+@AllArgsConstructor // 全参构造
+@Builder // 关键修复:添加 Builder 模式
+public class Work {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(nullable = false, length = 255)
+ private String title;
+
+ @Column(nullable = false, length = 100)
+ private String author;
+
+ @Column(columnDefinition = "INT DEFAULT 0")
+ private Integer views = 0;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(
+ name = "category_id",
+ foreignKey = @ForeignKey(name = "fk_work_category")
+ )
+ private Category category;
+}
\ No newline at end of file
diff --git a/src/main/java/edu/bjtu/groupone/backend/mapper/CategoryMybatisMapper.java b/src/main/java/edu/bjtu/groupone/backend/mapper/CategoryMybatisMapper.java
new file mode 100644
index 0000000..42bb6af
--- /dev/null
+++ b/src/main/java/edu/bjtu/groupone/backend/mapper/CategoryMybatisMapper.java
@@ -0,0 +1,13 @@
+package edu.bjtu.groupone.backend.mapper;
+
+import edu.bjtu.groupone.backend.domain.entity.Category;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface CategoryMybatisMapper {
+ List<Category> findByParentId(Long parentId);
+ List<Category> findAll();
+}
diff --git a/src/main/java/edu/bjtu/groupone/backend/mapper/WorkMybatisMapper.java b/src/main/java/edu/bjtu/groupone/backend/mapper/WorkMybatisMapper.java
new file mode 100644
index 0000000..a3485d7
--- /dev/null
+++ b/src/main/java/edu/bjtu/groupone/backend/mapper/WorkMybatisMapper.java
@@ -0,0 +1,17 @@
+// WorkMapper.java
+package edu.bjtu.groupone.backend.mapper;
+
+//import edu.bjtu.groupone.backend.model.Work;
+import edu.bjtu.groupone.backend.domain.entity.Work;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable; // 正确导入
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface WorkMybatisMapper {
+ // 修正参数类型为 Spring Data 的 Pageable
+ Page<Work> findByCategoryIdIn(List<Long> categoryIds, Pageable pageable);
+}
\ No newline at end of file
diff --git a/src/main/java/edu/bjtu/groupone/backend/service/CategoryService.java b/src/main/java/edu/bjtu/groupone/backend/service/CategoryService.java
new file mode 100644
index 0000000..7b745c1
--- /dev/null
+++ b/src/main/java/edu/bjtu/groupone/backend/service/CategoryService.java
@@ -0,0 +1,42 @@
+package edu.bjtu.groupone.backend.service;
+
+import edu.bjtu.groupone.backend.domain.dto.CategoryDTO;
+import edu.bjtu.groupone.backend.domain.entity.Category;
+import edu.bjtu.groupone.backend.mapper.CategoryMybatisMapper;
+//import edu.bjtu.groupone.backend.model.Category;
+import edu.bjtu.groupone.backend.utils.TreeBuilder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Service
+public class CategoryService {
+ @Autowired
+ private CategoryMybatisMapper categoryMybatisMapper;
+
+ public List<CategoryDTO> getCategoryTree() {
+ List<Category> allCategories = categoryMybatisMapper.findAll();
+ return TreeBuilder.buildTree(allCategories);
+ }
+
+ public List<Long> getAllSubcategoryIds(Long categoryId) {
+ // 获取所有子分类(包含自身)
+ List<Long> ids = new ArrayList<>();
+ if (categoryId != null) {
+ ids.add(categoryId);
+ findSubIds(categoryId, ids);
+ }
+ return ids;
+ }
+
+ private void findSubIds(Long parentId, List<Long> result) {
+ List<Category> children = categoryMybatisMapper.findByParentId(parentId);
+ for (Category child : children) {
+ result.add(child.getId());
+ findSubIds(child.getId(), result); // 递归查找子分类
+ }
+ }
+
+}
diff --git a/src/main/java/edu/bjtu/groupone/backend/service/WorkService.java b/src/main/java/edu/bjtu/groupone/backend/service/WorkService.java
new file mode 100644
index 0000000..f3b58af
--- /dev/null
+++ b/src/main/java/edu/bjtu/groupone/backend/service/WorkService.java
@@ -0,0 +1,39 @@
+package edu.bjtu.groupone.backend.service;
+
+import edu.bjtu.groupone.backend.domain.dto.WorkResponse;
+import edu.bjtu.groupone.backend.domain.entity.Work;
+import edu.bjtu.groupone.backend.mapper.WorkMybatisMapper;
+//import edu.bjtu.groupone.backend.model.Work;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class WorkService {
+ @Autowired
+ private WorkMybatisMapper workMybatisMapper;
+
+ @Autowired
+ private CategoryService categoryService;
+
+ public Page<WorkResponse> getWorks(Long categoryId, int page, int size) {
+ List<Long> categoryIds = categoryService.getAllSubcategoryIds(categoryId);
+ Pageable pageable = PageRequest.of(page-1, size);
+ return workMybatisMapper.findByCategoryIdIn(categoryIds, pageable)
+ .map(this::convertToResponse);
+ }
+
+ private WorkResponse convertToResponse(Work work) {
+ return new WorkResponse(
+ work.getId(),
+ work.getTitle(),
+ work.getAuthor(),
+ work.getViews(),
+ work.getCategory().getId()
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/edu/bjtu/groupone/backend/utils/TreeBuilder.java b/src/main/java/edu/bjtu/groupone/backend/utils/TreeBuilder.java
new file mode 100644
index 0000000..b8bd674
--- /dev/null
+++ b/src/main/java/edu/bjtu/groupone/backend/utils/TreeBuilder.java
@@ -0,0 +1,35 @@
+package edu.bjtu.groupone.backend.utils;
+
+import edu.bjtu.groupone.backend.domain.dto.CategoryDTO;
+import edu.bjtu.groupone.backend.domain.entity.Category;
+//import edu.bjtu.groupone.backend.model.Category;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class TreeBuilder {
+ public static List<CategoryDTO> buildTree(List<Category> allCategories) {
+ // 使用明确的类型声明
+ Map<Long, CategoryDTO> dtoMap = allCategories.stream()
+ .map(c -> new CategoryDTO(c.getId(), c.getName()))
+ .collect(Collectors.toMap(
+ CategoryDTO::getId,
+ dto -> dto,
+ (existing, replacement) -> existing
+ ));
+
+ List<CategoryDTO> result = new ArrayList<>();
+ allCategories.forEach(c -> {
+ CategoryDTO dto = dtoMap.get(c.getId());
+ if (c.getParent() == null) {
+ result.add(dto);
+ } else {
+ CategoryDTO parentDto = dtoMap.get(c.getParent().getId());
+ parentDto.getChildren().add(dto);
+ }
+ });
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index db34ebe..668bc3b 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1,13 +1,15 @@
# ??MySQL??
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
-spring.datasource.url=jdbc:mysql://localhost:3306/groupone_db\
+spring.datasource.url=jdbc:mysql://localhost:3306/llksh\
?useSSL=false\
&serverTimezone=Asia/Shanghai\
&characterEncoding=utf8\
&allowPublicKeyRetrieval=true
spring.datasource.username=root
-spring.datasource.password=Rfw@2935
+spring.datasource.password=wuxiaorui123
+# src/main/resources/application.properties
+spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect
# ???????????
spring.datasource.hikari.maximum-pool-size=10
@@ -16,4 +18,12 @@
spring.datasource.hikari.connection-timeout=30000
# ???? MyBatis ?????
-mybatis.configuration.map-underscore-to-camel-case=true
\ No newline at end of file
+mybatis.configuration.map-underscore-to-camel-case=true
+
+
+
+# ?????????
+mybatis.type-aliases-package=edu.bjtu.groupone.backend.domain.entity
+
+# ????????
+mybatis.mapper-locations=classpath:mapper/*.xml
\ No newline at end of file
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 6cf60f3..2d79bf4 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -15,9 +15,9 @@
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
- url: jdbc:mysql://localhost:3306/groupone_db?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8
+ url: jdbc:mysql://localhost:3306/llksh?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8
username: root
- password: "Rfw@2935"
+ password: "wuxiaorui123"
hikari:
maximum-pool-size: 10
minimum-idle: 5
diff --git a/src/main/resources/mapper/CategoryMybatisMapper.xml b/src/main/resources/mapper/CategoryMybatisMapper.xml
new file mode 100644
index 0000000..1a8e7e0
--- /dev/null
+++ b/src/main/resources/mapper/CategoryMybatisMapper.xml
@@ -0,0 +1,19 @@
+<?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">
+<!-- 修改1:使用全限定类名 -->
+<mapper namespace="edu.bjtu.groupone.backend.mapper.CategoryMybatisMapper">
+
+ <!-- 查询所有分类 -->
+ <select id="findAll"
+ resultType="edu.bjtu.groupone.backend.domain.entity.Category">
+ SELECT * FROM categories
+ </select>
+
+ <!-- 根据父分类ID查询 -->
+ <select id="findByParentId" parameterType="Long"
+ resultType="edu.bjtu.groupone.backend.domain.entity.Category">
+ SELECT * FROM categories WHERE parent_id = #{parentId}
+ </select>
+
+</mapper>
\ No newline at end of file
diff --git a/src/main/resources/mapper/WorkMybatisMapper.xml b/src/main/resources/mapper/WorkMybatisMapper.xml
new file mode 100644
index 0000000..dd0d444
--- /dev/null
+++ b/src/main/resources/mapper/WorkMybatisMapper.xml
@@ -0,0 +1,37 @@
+<?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="edu.bjtu.groupone.backend.mapper.WorkMybatisMapper">
+
+ <!-- 结果集映射 -->
+ <resultMap id="WorkResultMap" type="Work">
+ <id property="id" column="id"/>
+ <result property="title" column="title"/>
+ <result property="description" column="description"/>
+ <result property="categoryId" column="category_id"/>
+ <result property="createTime" column="create_time"/>
+ </resultMap>
+
+ <!-- 查询所有作品 -->
+ <select id="findAll" resultMap="WorkResultMap">
+ SELECT id, title, description, category_id, create_time
+ FROM works
+ </select>
+
+ <!-- 根据分类ID列表分页查询 -->
+ <select id="findByCategoryIdIn" resultMap="WorkResultMap">
+ SELECT * FROM works
+ <where>
+ <if test="categoryIds != null and categoryIds.size() > 0">
+ category_id IN
+ <foreach item="categoryId" collection="categoryIds"
+ open="(" separator="," close=")">
+ #{categoryId}
+ </foreach>
+ </if>
+ </where>
+ ORDER BY create_time DESC
+ LIMIT #{pageable.pageSize} OFFSET #{pageable.offset}
+ </select>
+
+</mapper>
\ No newline at end of file
diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql
index 823ecdf..beb0cc7 100644
--- a/src/main/resources/schema.sql
+++ b/src/main/resources/schema.sql
@@ -1,3 +1,4 @@
+-- 使用反引号包裹保留字表名
CREATE TABLE `user` (
`user_id` INT PRIMARY KEY AUTO_INCREMENT,
`username` VARCHAR(255) NOT NULL UNIQUE,
@@ -6,27 +7,25 @@
`address` VARCHAR(255),
`role` VARCHAR(50) NOT NULL DEFAULT 'user',
`profile_pic` VARCHAR(255),
- `registration_date` DATETIME NOT NULL, -- 推荐用时间类型(而非字符串)
- `identification_number` VARCHAR(18), -- 身份证号建议用 VARCHAR(18)
+ `registration_date` DATETIME NOT NULL,
+ `identification_number` VARCHAR(18),
`avatar` VARCHAR(255),
- `isfollowed` BOOLEAN NOT NULL DEFAULT FALSE,
- INDEX `idx_user_id` (`user_id`) -- 可选:主键通常自带索引
+ `isfollowed` BOOLEAN NOT NULL DEFAULT FALSE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-
-
+-- 外键表同样使用反引号
CREATE TABLE user_follow (
- follower_id INT NOT NULL,
- followed_id INT NOT NULL,
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
- PRIMARY KEY (follower_id, followed_id),
- FOREIGN KEY (follower_id) REFERENCES user(user_id),
- FOREIGN KEY (followed_id) REFERENCES user(user_id)
+ `follower_id` INT NOT NULL,
+ `followed_id` INT NOT NULL,
+ `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (`follower_id`, `followed_id`),
+ FOREIGN KEY (`follower_id`) REFERENCES `user`(`user_id`),
+ FOREIGN KEY (`followed_id`) REFERENCES `user`(`user_id`)
);
+-- 插入语句使用反引号包裹表名和列名
INSERT INTO `user` (
- username, email, password, registration_date, identification_number, role
+ `username`, `email`, `password`, `registration_date`, `identification_number`, `role`
) VALUES (
- 'admin', 'admin@example.com', 'admin123', NOW(), 87654321, 'admin'
- );
-
+ 'admin', 'admin@example.com', 'admin123', NOW(), '87654321', 'admin'
+ );
\ No newline at end of file
diff --git a/src/test/java/edu/bjtu/groupone/backend/CategoryServiceTest.java b/src/test/java/edu/bjtu/groupone/backend/CategoryServiceTest.java
new file mode 100644
index 0000000..b09ffb2
--- /dev/null
+++ b/src/test/java/edu/bjtu/groupone/backend/CategoryServiceTest.java
@@ -0,0 +1,41 @@
+package edu.bjtu.groupone.backend;
+
+import edu.bjtu.groupone.backend.domain.entity.Category;
+import edu.bjtu.groupone.backend.mapper.CategoryMybatisMapper;
+//import edu.bjtu.groupone.backend.model.Category;
+import edu.bjtu.groupone.backend.service.CategoryService;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.*;
+
+class CategoryServiceTest {
+
+ @Mock
+ private CategoryMybatisMapper mapper;
+
+ @InjectMocks
+ private CategoryService service;
+
+ @BeforeEach
+ void setUp() {
+ MockitoAnnotations.openMocks(this);
+ }
+
+ @Test
+ void testGetAllSubcategoryIds() {
+ Category child = new Category(2L, "Child", new Category(1L, "Parent", null));
+ when(mapper.findByParentId(1L)).thenReturn(List.of(child));
+ when(mapper.findByParentId(2L)).thenReturn(List.of());
+
+ List<Long> ids = service.getAllSubcategoryIds(1L);
+
+ assertEquals(List.of(1L, 2L), ids);
+ }
+}
diff --git a/src/test/java/edu/bjtu/groupone/backend/WorkServiceTest.java b/src/test/java/edu/bjtu/groupone/backend/WorkServiceTest.java
new file mode 100644
index 0000000..85e62c6
--- /dev/null
+++ b/src/test/java/edu/bjtu/groupone/backend/WorkServiceTest.java
@@ -0,0 +1,58 @@
+package edu.bjtu.groupone.backend;
+
+import edu.bjtu.groupone.backend.domain.dto.WorkResponse;
+import edu.bjtu.groupone.backend.domain.entity.Category;
+import edu.bjtu.groupone.backend.domain.entity.Work;
+import edu.bjtu.groupone.backend.mapper.WorkMybatisMapper;
+//import edu.bjtu.groupone.backend.model.Category;
+//import edu.bjtu.groupone.backend.model.Work;
+import edu.bjtu.groupone.backend.service.CategoryService;
+import edu.bjtu.groupone.backend.service.WorkService;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.*;
+import org.springframework.data.domain.*;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.*;
+
+class WorkServiceTest {
+
+ @Mock
+ private WorkMybatisMapper mapper;
+
+ @Mock
+ private CategoryService categoryService;
+
+ @InjectMocks
+ private WorkService service;
+
+ @BeforeEach
+ void init() {
+ MockitoAnnotations.openMocks(this);
+ }
+
+ @Test
+ void testGetWorks() {
+ Long categoryId = 1L;
+ List<Long> categoryIds = List.of(categoryId);
+
+ Work mockWork = Work.builder()
+ .id(10L)
+ .title("Test Title")
+ .author("Author")
+ .views(123)
+ .category(Category.builder().id(categoryId).name("Test").build())
+ .build();
+
+ Page<Work> workPage = new PageImpl<>(List.of(mockWork));
+ when(categoryService.getAllSubcategoryIds(categoryId)).thenReturn(categoryIds);
+ when(mapper.findByCategoryIdIn(eq(categoryIds), any(Pageable.class))).thenReturn(workPage);
+
+ Page<WorkResponse> result = service.getWorks(categoryId, 1, 10);
+ assertEquals(1, result.getTotalElements());
+ assertEquals("Test Title", result.getContent().get(0).getTitle());
+ }
+}
diff --git a/src/test/resources/application-test.properties b/src/test/resources/application-test.properties
new file mode 100644
index 0000000..4fb95c7
--- /dev/null
+++ b/src/test/resources/application-test.properties
@@ -0,0 +1,5 @@
+spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
+spring.datasource.driver-class-name=org.h2.Driver
+spring.jpa.hibernate.ddl-auto=create-drop
+spring.jpa.show-sql=true
+spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
\ No newline at end of file