PortfolioQueryController.java
package com.taxonomy.portfolio.controller;
import com.taxonomy.portfolio.dto.PortfolioDtos.ConflictView;
import com.taxonomy.portfolio.dto.PortfolioDtos.ProjectPortfolioView;
import com.taxonomy.portfolio.dto.PortfolioDtos.ReviewConflictRequest;
import com.taxonomy.portfolio.service.PortfolioAggregationService;
import com.taxonomy.portfolio.service.ProjectConflictService;
import com.taxonomy.workspace.service.WorkspaceContext;
import com.taxonomy.workspace.service.WorkspaceResolver;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/api/projects/{projectId}")
@Tag(name = "Consolidated Project Portfolio")
public class PortfolioQueryController {
private final PortfolioAggregationService aggregationService;
private final ProjectConflictService conflictService;
private final WorkspaceResolver workspaceResolver;
public PortfolioQueryController(PortfolioAggregationService aggregationService,
ProjectConflictService conflictService,
WorkspaceResolver workspaceResolver) {
this.aggregationService = aggregationService;
this.conflictService = conflictService;
this.workspaceResolver = workspaceResolver;
}
@GetMapping("/portfolio")
@Operation(summary = "Build the consolidated requirement/solution/product portfolio")
public ProjectPortfolioView portfolio(@PathVariable Long projectId) {
RequestScope scope = scope();
return aggregationService.build(projectId, scope.username(), scope.context());
}
@PostMapping("/conflicts/detect")
@Operation(summary = "Create deterministic, human-reviewable conflict hypotheses")
public List<ConflictView> detectConflicts(@PathVariable Long projectId) {
RequestScope scope = scope();
return conflictService.detect(projectId, scope.username(), scope.context());
}
@GetMapping("/conflicts")
@Operation(summary = "List project conflict hypotheses and decisions")
public List<ConflictView> listConflicts(@PathVariable Long projectId) {
RequestScope scope = scope();
return conflictService.list(projectId, scope.username(), scope.context());
}
@PatchMapping("/conflicts/{conflictId}")
@Operation(summary = "Confirm, reject or resolve a conflict hypothesis")
public ConflictView reviewConflict(@PathVariable Long projectId,
@PathVariable Long conflictId,
@RequestBody ReviewConflictRequest request) {
RequestScope scope = scope();
return conflictService.review(
projectId, conflictId, request, scope.username(), scope.context());
}
private RequestScope scope() {
return new RequestScope(
workspaceResolver.resolveCurrentUsername(),
workspaceResolver.resolveCurrentContext());
}
private record RequestScope(String username, WorkspaceContext context) {
}
}