post_report

Change-Id: I191da6e9ea7371e66dadead873abd057f5d08c74
diff --git a/src/main/java/com/example/g8backend/controller/PostController.java b/src/main/java/com/example/g8backend/controller/PostController.java
index e5e4eab..1e0adf4 100644
--- a/src/main/java/com/example/g8backend/controller/PostController.java
+++ b/src/main/java/com/example/g8backend/controller/PostController.java
@@ -16,9 +16,12 @@
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
+import com.example.g8backend.entity.Report;
+import com.example.g8backend.service.IReportService;
 
 import java.util.List;
 
+
 @RestController
 @RequestMapping("/post")
 @Validated
@@ -29,6 +32,8 @@
 
     @Autowired
     private PostViewMapper postViewMapper;
+    @Autowired
+    private IReportService reportService;
 
     @PostMapping("")
     public ResponseEntity<ApiResponse<Void>> createPost(@RequestBody PostCreateDTO postCreateDTO) {
@@ -191,4 +196,41 @@
         Long count = postRatingService.getRatingUserCount(postId);
         return ResponseEntity.ok(ApiResponse.success(count));
     }
+    @PostMapping("/{postId}/report")
+    public ResponseEntity<ApiResponse<String>> reportPost(
+            @PathVariable Long postId,
+            @RequestParam String reason) {
+        long userId = (long) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        try {
+            reportService.submitReport(userId, postId, reason);
+            return ResponseEntity.ok(ApiResponse.message("举报已提交"));
+        } catch (IllegalArgumentException e) {
+            return ResponseEntity.badRequest().body(ApiResponse.error(400, e.getMessage()));
+        }
+    }
+
+
+    @GetMapping("/reports")
+    public ResponseEntity<ApiResponse<List<Report>>> getReports(
+            @RequestParam(required = false) String status) {
+        List<Report> reports = reportService.getReports(status);
+        return ResponseEntity.ok(ApiResponse.success(reports));
+    }
+
+    @PutMapping("/report/{reportId}")
+    public ResponseEntity<ApiResponse<String>> resolveReport(
+            @PathVariable Long reportId,
+            @RequestParam Long adminId,     // 实际部署时可从 token 解析或改为登录信息中获取
+            @RequestParam String status,
+            @RequestParam(required = false) String notes) {
+        try {
+            reportService.resolveReport(reportId, adminId, status, notes);
+            return ResponseEntity.ok(ApiResponse.message("举报处理完成"));
+        } catch (IllegalArgumentException e) {
+            return ResponseEntity.badRequest().body(ApiResponse.error(400, e.getMessage()));
+        }
+    }
+
+
+
 }
diff --git a/src/main/java/com/example/g8backend/entity/Report.java b/src/main/java/com/example/g8backend/entity/Report.java
new file mode 100644
index 0000000..9ddfa5b
--- /dev/null
+++ b/src/main/java/com/example/g8backend/entity/Report.java
@@ -0,0 +1,23 @@
+package com.example.g8backend.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+@TableName("reports")
+public class Report {
+    @TableId(type = IdType.AUTO)
+    private Long reportId;
+    private Long postId;
+    private Long userId;
+    private String reason;
+    private String status;
+    private LocalDateTime createdAt;
+    private Long resolvedBy;
+    private LocalDateTime resolvedAt;
+    private String resolutionNotes;
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/g8backend/mapper/ReportMapper.java b/src/main/java/com/example/g8backend/mapper/ReportMapper.java
new file mode 100644
index 0000000..01e5630
--- /dev/null
+++ b/src/main/java/com/example/g8backend/mapper/ReportMapper.java
@@ -0,0 +1,16 @@
+package com.example.g8backend.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.example.g8backend.entity.Report;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
+
+@Mapper
+public interface ReportMapper extends BaseMapper<Report> {
+    // 自定义查询方法(如按状态查询)
+    @Select("SELECT * FROM reports WHERE status = #{status}")
+    List<Report> selectByStatus(@Param("status") String status);
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/g8backend/service/IReportService.java b/src/main/java/com/example/g8backend/service/IReportService.java
new file mode 100644
index 0000000..c1cf775
--- /dev/null
+++ b/src/main/java/com/example/g8backend/service/IReportService.java
@@ -0,0 +1,15 @@
+package com.example.g8backend.service;
+
+import com.example.g8backend.entity.Report;
+import java.util.List;
+
+public interface IReportService {
+    // 用户提交举报
+    boolean submitReport(Long userId, Long postId, String reason);
+
+    // 管理员处理举报
+    boolean resolveReport(Long reportId, Long adminUserId, String status, String notes);
+
+    // 获取举报列表(按状态过滤)
+    List<Report> getReports(String status);
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/g8backend/service/impl/ReportServiceImpl.java b/src/main/java/com/example/g8backend/service/impl/ReportServiceImpl.java
new file mode 100644
index 0000000..f27de25
--- /dev/null
+++ b/src/main/java/com/example/g8backend/service/impl/ReportServiceImpl.java
@@ -0,0 +1,64 @@
+package com.example.g8backend.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.example.g8backend.entity.Report;
+import com.example.g8backend.mapper.ReportMapper;
+import com.example.g8backend.service.IReportService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Service
+@RequiredArgsConstructor
+public class ReportServiceImpl implements IReportService {
+    private final ReportMapper reportMapper;
+
+    @Override
+    @Transactional
+    public boolean submitReport(Long userId, Long postId, String reason) {
+        // 检查是否已存在未处理的举报
+        Long count = reportMapper.selectCount(
+                new QueryWrapper<Report>()
+                        .eq("user_id", userId)
+                        .eq("post_id", postId)
+                        .eq("status", "pending")
+        );
+        if (count > 0) {
+            throw new IllegalArgumentException("您已举报过该帖子,请勿重复提交");
+        }
+
+        // 创建举报记录
+        Report report = new Report();
+        report.setPostId(postId);
+        report.setUserId(userId);
+        report.setReason(reason);
+        report.setStatus("pending");
+        report.setCreatedAt(LocalDateTime.now());
+        return reportMapper.insert(report) > 0;
+    }
+
+    @Override
+    @Transactional
+    public boolean resolveReport(Long reportId, Long adminUserId, String status, String notes) {
+        Report report = reportMapper.selectById(reportId);
+        if (report == null) {
+            throw new IllegalArgumentException("举报记录不存在");
+        }
+        report.setStatus(status);
+        report.setResolvedBy(adminUserId);
+        report.setResolvedAt(LocalDateTime.now());
+        report.setResolutionNotes(notes);
+        return reportMapper.updateById(report) > 0;
+    }
+
+    @Override
+    public List<Report> getReports(String status) {
+        return reportMapper.selectList(
+                new QueryWrapper<Report>().eq("status", status)
+        );
+    }
+
+
+}
\ No newline at end of file