package com.mydreamjob.safety; import com.mydreamjob.audit.AuditContext; import com.mydreamjob.auth.User; import com.mydreamjob.auth.UserRepository; import com.mydreamjob.common.api.ApiResponse; import com.mydreamjob.common.api.PageResponse; import com.mydreamjob.common.exception.BusinessException; import com.mydreamjob.common.exception.ErrorCode; import com.mydreamjob.safety.dto.ReportDecisionRequest; import com.mydreamjob.safety.dto.ReportResponse; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; /** * ADMIN 举报管理接口(V33)。仅 ADMIN;COMPANY/STUDENT/未登录分别 403/403/401。 * *

处理不自动封禁、不删除消息、不修改投递/面试/评分状态。决策仅影响举报单状态与留痕。

*/ @Tag(name = "举报管理", description = "ADMIN 查看与处理举报") @RestController @RequestMapping("/admin") @RequiredArgsConstructor public class AdminReportController { /** R01-A3:身份只来自安全上下文,不接受请求参数传入的 userId。 */ private final com.mydreamjob.security.CurrentUserAccessor currentUserAccessor; /** R01-A3:requestId 只来自过滤器规范化后的唯一产出点。 */ private final com.mydreamjob.common.logging.RequestIdAccessor requestIdAccessor; private final ReportService reportService; private final UserRepository userRepository; @Operation(summary = "举报列表(ADMIN)", description = "仅 ADMIN;支持 status/targetType/category/page/size 筛选;响应脱敏。") @ApiResponses({ @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "OK"), @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "401", description = "未登录"), @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "403", description = "非 ADMIN(2002/4001)") }) @GetMapping("/reports") public ResponseEntity>> adminList( @RequestParam(required = false) String status, @RequestParam(required = false) String targetType, @RequestParam(required = false) String category, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "20") int size, HttpServletRequest request) { Long userId = requireAdmin(request); Page result = reportService.adminList(status, targetType, category, page, size); return ResponseEntity.ok(ApiResponse.ok(PageResponse.of( result.getContent(), result.getTotalElements(), result.getNumber(), result.getSize()), requestIdAccessor.get(request))); } @Operation(summary = "举报详情(ADMIN)", description = "仅 ADMIN;可见脱敏证据、MESSAGE 脱敏内容、会话上下文最小信息与处置历史;" + "不返回密码/JWT/手机号/邮箱/resumeUrl。") @ApiResponses({ @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "OK"), @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "401", description = "未登录"), @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "403", description = "非 ADMIN(2002/4001)"), @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "举报不存在(9601)") }) @GetMapping("/reports/{id}") public ResponseEntity> adminDetail(@PathVariable Long id, HttpServletRequest request) { requireAdmin(request); return ResponseEntity.ok(ApiResponse.ok(reportService.adminDetail(id), requestIdAccessor.get(request))); } @Operation(summary = "举报处理决定(ADMIN)", description = "仅 ADMIN;decision 仅 RESOLVED/REJECTED;REJECTED 必填 note;" + "仅 PENDING 可处理,重复 409/9604,乐观锁冲突 409/9601-9605;" + "处理后向举报人发送一条 REPORT_DECISION 通知(幂等)。") @ApiResponses({ @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "OK"), @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "参数错误(REJECTED 缺 note)"), @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "401", description = "未登录"), @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "403", description = "非 ADMIN(2002/4001)"), @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "举报不存在(9601)"), @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "409", description = "重复决定/非法流转(9604/9605)") }) @PostMapping("/reports/{id}/decision") public ResponseEntity> decide( @PathVariable Long id, @Valid @RequestBody ReportDecisionRequest req, HttpServletRequest request) { Long adminUserId = requireAdmin(request); User admin = userRepository.findById(adminUserId).orElseThrow(); String role = admin.getRoles() == null || admin.getRoles().isEmpty() ? (admin.getRole() == null ? "ADMIN" : admin.getRole().name()) : "ADMIN"; ReportResponse data = reportService.decide(adminUserId, role, id, req, AuditContext.from(request)); return ResponseEntity.ok(ApiResponse.ok(data, requestIdAccessor.get(request))); } private Long requireAdmin(HttpServletRequest request) { Long userId = currentUserAccessor.requireUserId(); User admin = userRepository.findById(userId).orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND)); boolean isAdmin = admin.getRoles() != null && admin.getRoles().stream().anyMatch(r -> r == User.Role.ADMIN); if (!isAdmin) { throw new BusinessException(ErrorCode.FORBIDDEN); } return userId; } }