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.
106 lines
5.9 KiB
106 lines
5.9 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.CreateReportRequest;
|
|
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.*;
|
|
|
|
/**
|
|
* 举报接口(V33)。
|
|
*
|
|
* <p>身份仅取自 JWT,不接受 reporterUserId。举报人本人或 ADMIN 可查看详情;
|
|
* 被举报人无法看到举报人身份。未登录 401。</p>
|
|
*/
|
|
@Tag(name = "举报", description = "用户举报与查询")
|
|
@RestController
|
|
@RequiredArgsConstructor
|
|
public class ReportController {
|
|
|
|
/** 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 = "提交举报",
|
|
description = "targetType=USER 需 targetUserId;targetType=MESSAGE 需 messageId(服务端推导发送者为被举报人)。"
|
|
+ "幂等键重放:同键同请求返回原单 200,同键不同请求 409/9606;待处理重复 409/9603;"
|
|
+ "举报自己 400/9602;限流 429/9609。")
|
|
@ApiResponses({
|
|
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "OK"),
|
|
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "参数错误(1001/9602/9607)"),
|
|
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "401", description = "未登录"),
|
|
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "403", description = "无权访问该消息(2002)"),
|
|
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "目标用户不存在(4001)"),
|
|
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "409", description = "重复举报/幂等冲突(9603/9606)"),
|
|
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "429", description = "举报限流(9609)")
|
|
})
|
|
@PostMapping("/reports")
|
|
public ResponseEntity<ApiResponse<ReportResponse>> create(
|
|
@Valid @RequestBody CreateReportRequest req, HttpServletRequest request) {
|
|
Long userId = currentUserAccessor.requireUserId();
|
|
ReportResponse data = reportService.create(userId, req, AuditContext.from(request));
|
|
return ResponseEntity.ok(ApiResponse.ok(data, requestIdAccessor.get(request)));
|
|
}
|
|
|
|
/** /reports/me 必须在 /reports/{id} 之前注册(路由回归测试覆盖) */
|
|
@Operation(summary = "我的举报列表",
|
|
description = "仅返回当前用户创建的举报;支持 status/page/size;不含其他举报人信息、联系方式、消息全文。")
|
|
@ApiResponses({
|
|
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "OK"),
|
|
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "401", description = "未登录")
|
|
})
|
|
@GetMapping("/reports/me")
|
|
public ResponseEntity<ApiResponse<PageResponse<ReportResponse>>> listMine(
|
|
@RequestParam(required = false) String status,
|
|
@RequestParam(defaultValue = "0") int page,
|
|
@RequestParam(defaultValue = "20") int size,
|
|
HttpServletRequest request) {
|
|
Long userId = currentUserAccessor.requireUserId();
|
|
Page<ReportResponse> result = reportService.listMine(userId, status, page, size);
|
|
return ResponseEntity.ok(ApiResponse.ok(PageResponse.of(
|
|
result.getContent(), result.getTotalElements(), result.getNumber(), result.getSize()),
|
|
requestIdAccessor.get(request)));
|
|
}
|
|
|
|
@Operation(summary = "我的举报详情",
|
|
description = "当前举报人本人或 ADMIN 可查看;其他用户 403/2002。"
|
|
+ "ADMIN 可见处置历史与更完整画像;被举报人无法查看举报人身份。")
|
|
@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)"),
|
|
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "举报不存在(9601)")
|
|
})
|
|
@GetMapping("/reports/{id}")
|
|
public ResponseEntity<ApiResponse<ReportResponse>> getDetail(
|
|
@PathVariable Long id, HttpServletRequest request) {
|
|
Long userId = currentUserAccessor.requireUserId();
|
|
ReportResponse data = reportService.getDetail(userId, id, isAdmin(userId));
|
|
return ResponseEntity.ok(ApiResponse.ok(data, requestIdAccessor.get(request)));
|
|
}
|
|
|
|
private boolean isAdmin(Long userId) {
|
|
return userRepository.findById(userId)
|
|
.map(u -> u.getRoles() != null
|
|
&& u.getRoles().stream().anyMatch(r -> r == User.Role.ADMIN))
|
|
.orElse(false);
|
|
}
|
|
|
|
}
|