You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
115 lines
6.5 KiB
115 lines
6.5 KiB
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。
|
|
*
|
|
* <p>处理不自动封禁、不删除消息、不修改投递/面试/评分状态。决策仅影响举报单状态与留痕。</p>
|
|
*/
|
|
@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<ApiResponse<PageResponse<ReportResponse>>> 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<ReportResponse> 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<ApiResponse<ReportResponse>> 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<ApiResponse<ReportResponse>> 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;
|
|
}
|
|
|
|
}
|