diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/admin/AdminSkillController.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/admin/AdminSkillController.java index 08292dd8..6eb92e3f 100644 --- a/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/admin/AdminSkillController.java +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/admin/AdminSkillController.java @@ -38,10 +38,9 @@ public class AdminSkillController extends BaseApiController { @RequestBody(required = false) AdminSkillActionRequest request, @AuthenticationPrincipal PlatformPrincipal principal, HttpServletRequest httpRequest) { - var skill = skillGovernanceService.hideSkill( + var skill = skillGovernanceService.hideSkillAsAdmin( skillId, principal.userId(), - java.util.Map.of(), httpRequest.getRemoteAddr(), httpRequest.getHeader("User-Agent"), request != null ? request.reason() : null @@ -54,10 +53,9 @@ public class AdminSkillController extends BaseApiController { public ApiResponse unhideSkill(@PathVariable Long skillId, @AuthenticationPrincipal PlatformPrincipal principal, HttpServletRequest httpRequest) { - var skill = skillGovernanceService.unhideSkill( + var skill = skillGovernanceService.unhideSkillAsAdmin( skillId, principal.userId(), - java.util.Map.of(), httpRequest.getRemoteAddr(), httpRequest.getHeader("User-Agent") ); diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/portal/SkillLifecycleController.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/portal/SkillLifecycleController.java index e67582f9..f21d9f70 100644 --- a/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/portal/SkillLifecycleController.java +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/portal/SkillLifecycleController.java @@ -14,6 +14,7 @@ import com.iflytek.skillhub.service.GovernanceWorkflowAppService; import jakarta.validation.Valid; import jakarta.servlet.http.HttpServletRequest; import java.util.Map; +import java.util.Set; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -44,6 +45,7 @@ public class SkillLifecycleController extends BaseApiController { @RequestBody(required = false) AdminSkillActionRequest request, @RequestAttribute("userId") String userId, @RequestAttribute(value = "userNsRoles", required = false) Map userNsRoles, + @RequestAttribute(value = "platformRoles", required = false) Set platformRoles, HttpServletRequest httpRequest) { return ok("response.success.updated", governanceWorkflowAppService.archiveSkill( @@ -52,6 +54,7 @@ public class SkillLifecycleController extends BaseApiController { request, userId, userNsRoles, + platformRoles, AuditRequestContext.from(httpRequest))); } @@ -60,6 +63,7 @@ public class SkillLifecycleController extends BaseApiController { @PathVariable String slug, @RequestAttribute("userId") String userId, @RequestAttribute(value = "userNsRoles", required = false) Map userNsRoles, + @RequestAttribute(value = "platformRoles", required = false) Set platformRoles, HttpServletRequest httpRequest) { return ok("response.success.updated", governanceWorkflowAppService.unarchiveSkill( @@ -67,6 +71,7 @@ public class SkillLifecycleController extends BaseApiController { slug, userId, userNsRoles, + platformRoles, AuditRequestContext.from(httpRequest))); } @@ -76,6 +81,7 @@ public class SkillLifecycleController extends BaseApiController { @PathVariable String version, @RequestAttribute("userId") String userId, @RequestAttribute(value = "userNsRoles", required = false) Map userNsRoles, + @RequestAttribute(value = "platformRoles", required = false) Set platformRoles, HttpServletRequest httpRequest) { return ok("response.success.deleted", governanceWorkflowAppService.deleteVersion( @@ -84,6 +90,7 @@ public class SkillLifecycleController extends BaseApiController { version, userId, userNsRoles, + platformRoles, AuditRequestContext.from(httpRequest))); } @@ -162,6 +169,7 @@ public class SkillLifecycleController extends BaseApiController { @RequestBody(required = false) AdminSkillActionRequest request, @RequestAttribute("userId") String userId, @RequestAttribute(value = "userNsRoles", required = false) Map userNsRoles, + @RequestAttribute(value = "platformRoles", required = false) Set platformRoles, HttpServletRequest httpRequest) { return ok("response.success.updated", governanceWorkflowAppService.hideSkill( @@ -170,6 +178,7 @@ public class SkillLifecycleController extends BaseApiController { request, userId, userNsRoles, + platformRoles, AuditRequestContext.from(httpRequest))); } @@ -178,6 +187,7 @@ public class SkillLifecycleController extends BaseApiController { @PathVariable String slug, @RequestAttribute("userId") String userId, @RequestAttribute(value = "userNsRoles", required = false) Map userNsRoles, + @RequestAttribute(value = "platformRoles", required = false) Set platformRoles, HttpServletRequest httpRequest) { return ok("response.success.updated", governanceWorkflowAppService.unhideSkill( @@ -185,6 +195,7 @@ public class SkillLifecycleController extends BaseApiController { slug, userId, userNsRoles, + platformRoles, AuditRequestContext.from(httpRequest))); } } diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/portal/SkillRatingController.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/portal/SkillRatingController.java index 4b61b3df..fae3aadd 100644 --- a/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/portal/SkillRatingController.java +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/portal/SkillRatingController.java @@ -1,8 +1,11 @@ package com.iflytek.skillhub.controller.portal; import com.iflytek.skillhub.controller.BaseApiController; +import com.iflytek.skillhub.domain.shared.exception.DomainForbiddenException; import com.iflytek.skillhub.dto.ApiResponse; import com.iflytek.skillhub.dto.ApiResponseFactory; +import com.iflytek.skillhub.dto.SkillRatingRequest; +import com.iflytek.skillhub.dto.SkillRatingStatusResponse; import com.iflytek.skillhub.domain.social.SkillRatingService; import jakarta.validation.Valid; import org.springframework.web.bind.annotation.*; @@ -25,6 +28,9 @@ public class SkillRatingController extends BaseApiController { @PathVariable Long skillId, @Valid @RequestBody SkillRatingRequest request, @RequestAttribute("userId") String userId) { + if (userId == null) { + throw new DomainForbiddenException("error.auth.required"); + } skillRatingService.rate(skillId, userId, request.score()); return ok("response.success.updated", null); } diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/portal/SkillStarController.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/portal/SkillStarController.java index 396da70e..8b022e12 100644 --- a/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/portal/SkillStarController.java +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/controller/portal/SkillStarController.java @@ -1,6 +1,7 @@ package com.iflytek.skillhub.controller.portal; import com.iflytek.skillhub.controller.BaseApiController; +import com.iflytek.skillhub.domain.shared.exception.DomainForbiddenException; import com.iflytek.skillhub.dto.ApiResponse; import com.iflytek.skillhub.dto.ApiResponseFactory; import com.iflytek.skillhub.domain.social.SkillStarService; @@ -22,6 +23,9 @@ public class SkillStarController extends BaseApiController { public ApiResponse starSkill( @PathVariable Long skillId, @RequestAttribute("userId") String userId) { + if (userId == null) { + throw new DomainForbiddenException("error.auth.required"); + } skillStarService.star(skillId, userId); return ok("response.success.updated", null); } @@ -30,6 +34,9 @@ public class SkillStarController extends BaseApiController { public ApiResponse unstarSkill( @PathVariable Long skillId, @RequestAttribute("userId") String userId) { + if (userId == null) { + throw new DomainForbiddenException("error.auth.required"); + } skillStarService.unstar(skillId, userId); return ok("response.success.updated", null); } diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/service/GovernanceWorkflowAppService.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/service/GovernanceWorkflowAppService.java index 313a880f..b13d364e 100644 --- a/server/skillhub-app/src/main/java/com/iflytek/skillhub/service/GovernanceWorkflowAppService.java +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/service/GovernanceWorkflowAppService.java @@ -13,6 +13,7 @@ import com.iflytek.skillhub.dto.SkillLifecycleMutationResponse; import com.iflytek.skillhub.dto.SkillVersionRereleaseRequest; import java.io.InputStream; import java.util.Map; +import java.util.Set; import org.springframework.stereotype.Service; /** @@ -182,16 +183,18 @@ public class GovernanceWorkflowAppService { AdminSkillActionRequest request, String userId, Map userNsRoles, + Set platformRoles, AuditRequestContext auditContext) { - return skillLifecycleAppService.archiveSkill(namespace, slug, request, userId, userNsRoles, auditContext); + return skillLifecycleAppService.archiveSkill(namespace, slug, request, userId, userNsRoles, platformRoles, auditContext); } public SkillLifecycleMutationResponse unarchiveSkill(String namespace, String slug, String userId, Map userNsRoles, + Set platformRoles, AuditRequestContext auditContext) { - return skillLifecycleAppService.unarchiveSkill(namespace, slug, userId, userNsRoles, auditContext); + return skillLifecycleAppService.unarchiveSkill(namespace, slug, userId, userNsRoles, platformRoles, auditContext); } public SkillLifecycleMutationResponse deleteVersion(String namespace, @@ -199,8 +202,9 @@ public class GovernanceWorkflowAppService { String version, String userId, Map userNsRoles, + Set platformRoles, AuditRequestContext auditContext) { - return skillLifecycleAppService.deleteVersion(namespace, slug, version, userId, userNsRoles, auditContext); + return skillLifecycleAppService.deleteVersion(namespace, slug, version, userId, userNsRoles, platformRoles, auditContext); } public SkillLifecycleMutationResponse withdrawReviewVersion(String namespace, @@ -292,15 +296,17 @@ public class GovernanceWorkflowAppService { AdminSkillActionRequest request, String userId, Map userNsRoles, + Set platformRoles, AuditRequestContext auditContext) { - return skillLifecycleAppService.hideSkill(namespace, slug, request, userId, userNsRoles, auditContext); + return skillLifecycleAppService.hideSkill(namespace, slug, request, userId, userNsRoles, platformRoles, auditContext); } public SkillLifecycleMutationResponse unhideSkill(String namespace, String slug, String userId, Map userNsRoles, + Set platformRoles, AuditRequestContext auditContext) { - return skillLifecycleAppService.unhideSkill(namespace, slug, userId, userNsRoles, auditContext); + return skillLifecycleAppService.unhideSkill(namespace, slug, userId, userNsRoles, platformRoles, auditContext); } } diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/service/SkillLifecycleAppService.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/service/SkillLifecycleAppService.java index 131499ec..269294c9 100644 --- a/server/skillhub-app/src/main/java/com/iflytek/skillhub/service/SkillLifecycleAppService.java +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/service/SkillLifecycleAppService.java @@ -17,6 +17,7 @@ import com.iflytek.skillhub.dto.AdminSkillActionRequest; import com.iflytek.skillhub.dto.SkillLifecycleMutationResponse; import com.iflytek.skillhub.dto.SkillVersionRereleaseRequest; import java.util.Map; +import java.util.Set; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -60,12 +61,14 @@ public class SkillLifecycleAppService { AdminSkillActionRequest request, String userId, Map userNamespaceRoles, + Set platformRoles, AuditRequestContext auditContext) { Skill skill = findSkill(namespace, slug, userId); Skill archived = skillGovernanceService.archiveSkill( skill.getId(), userId, normalizeRoles(userNamespaceRoles), + platformRoles, auditContext.clientIp(), auditContext.userAgent(), request != null ? request.reason() : null @@ -78,12 +81,14 @@ public class SkillLifecycleAppService { String slug, String userId, Map userNamespaceRoles, + Set platformRoles, AuditRequestContext auditContext) { Skill skill = findSkill(namespace, slug, userId); Skill restored = skillGovernanceService.unarchiveSkill( skill.getId(), userId, normalizeRoles(userNamespaceRoles), + platformRoles, auditContext.clientIp(), auditContext.userAgent() ); @@ -96,6 +101,7 @@ public class SkillLifecycleAppService { String version, String userId, Map userNamespaceRoles, + Set platformRoles, AuditRequestContext auditContext) { Skill skill = findSkill(namespace, slug, userId); SkillVersion skillVersion = findVersion(skill.getId(), version); @@ -104,6 +110,7 @@ public class SkillLifecycleAppService { skillVersion, userId, normalizeRoles(userNamespaceRoles), + platformRoles, auditContext.clientIp(), auditContext.userAgent(), namespace @@ -250,12 +257,14 @@ public class SkillLifecycleAppService { AdminSkillActionRequest request, String userId, Map userNamespaceRoles, + Set platformRoles, AuditRequestContext auditContext) { Skill skill = findSkill(namespace, slug, userId); Skill hidden = skillGovernanceService.hideSkill( skill.getId(), userId, normalizeRoles(userNamespaceRoles), + platformRoles, auditContext.clientIp(), auditContext.userAgent(), request != null ? request.reason() : null @@ -268,12 +277,14 @@ public class SkillLifecycleAppService { String slug, String userId, Map userNamespaceRoles, + Set platformRoles, AuditRequestContext auditContext) { Skill skill = findSkill(namespace, slug, userId); Skill unhidden = skillGovernanceService.unhideSkill( skill.getId(), userId, normalizeRoles(userNamespaceRoles), + platformRoles, auditContext.clientIp(), auditContext.userAgent() ); diff --git a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/report/SkillReportService.java b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/report/SkillReportService.java index 40455334..67bf1aa8 100644 --- a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/report/SkillReportService.java +++ b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/report/SkillReportService.java @@ -102,7 +102,7 @@ public class SkillReportService { String userAgent) { SkillReport report = requirePendingReport(reportId); if (disposition == SkillReportDisposition.RESOLVE_AND_HIDE) { - skillGovernanceService.hideSkill(report.getSkillId(), actorUserId, java.util.Map.of(), clientIp, userAgent, comment); + skillGovernanceService.hideSkillAsAdmin(report.getSkillId(), actorUserId, clientIp, userAgent, comment); } else if (disposition == SkillReportDisposition.RESOLVE_AND_ARCHIVE) { skillGovernanceService.archiveSkillAsAdmin(report.getSkillId(), actorUserId, clientIp, userAgent, comment); } diff --git a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillGovernanceService.java b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillGovernanceService.java index 3bebb9e3..c8b5429c 100644 --- a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillGovernanceService.java +++ b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/skill/service/SkillGovernanceService.java @@ -21,6 +21,7 @@ import java.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationEventPublisher; @@ -72,18 +73,34 @@ public class SkillGovernanceService { public Skill hideSkill(Long skillId, String actorUserId, Map userNamespaceRoles, + Set platformRoles, String clientIp, String userAgent, String reason) { Skill skill = skillRepository.findById(skillId) .orElseThrow(() -> new DomainNotFoundException("error.skill.notFound", skillId)); - assertCanManageLifecycle(skill, actorUserId, userNamespaceRoles); + assertCanManageLifecycle(skill, actorUserId, userNamespaceRoles, platformRoles); + return hideSkillInternal(skill, actorUserId, clientIp, userAgent, reason); + } + + @Transactional + public Skill hideSkillAsAdmin(Long skillId, + String actorUserId, + String clientIp, + String userAgent, + String reason) { + Skill skill = skillRepository.findById(skillId) + .orElseThrow(() -> new DomainNotFoundException("error.skill.notFound", skillId)); + return hideSkillInternal(skill, actorUserId, clientIp, userAgent, reason); + } + + private Skill hideSkillInternal(Skill skill, String actorUserId, String clientIp, String userAgent, String reason) { skill.setHidden(true); skill.setHiddenAt(currentInstant()); skill.setHiddenBy(actorUserId); skill.setUpdatedBy(actorUserId); Skill saved = skillRepository.save(skill); - auditLogService.record(actorUserId, "HIDE_SKILL", "SKILL", skillId, null, clientIp, userAgent, jsonReason(reason)); + auditLogService.record(actorUserId, "HIDE_SKILL", "SKILL", skill.getId(), null, clientIp, userAgent, jsonReason(reason)); return saved; } @@ -91,12 +108,13 @@ public class SkillGovernanceService { public Skill archiveSkill(Long skillId, String actorUserId, Map userNamespaceRoles, + Set platformRoles, String clientIp, String userAgent, String reason) { Skill skill = skillRepository.findById(skillId) .orElseThrow(() -> new DomainNotFoundException("error.skill.notFound", skillId)); - assertCanManageLifecycle(skill, actorUserId, userNamespaceRoles); + assertCanManageLifecycle(skill, actorUserId, userNamespaceRoles, platformRoles); return archiveSkillInternal(skill, actorUserId, clientIp, userAgent, reason); } @@ -129,17 +147,32 @@ public class SkillGovernanceService { public Skill unhideSkill(Long skillId, String actorUserId, Map userNamespaceRoles, + Set platformRoles, String clientIp, String userAgent) { Skill skill = skillRepository.findById(skillId) .orElseThrow(() -> new DomainNotFoundException("error.skill.notFound", skillId)); - assertCanManageLifecycle(skill, actorUserId, userNamespaceRoles); + assertCanManageLifecycle(skill, actorUserId, userNamespaceRoles, platformRoles); + return unhideSkillInternal(skill, actorUserId, clientIp, userAgent); + } + + @Transactional + public Skill unhideSkillAsAdmin(Long skillId, + String actorUserId, + String clientIp, + String userAgent) { + Skill skill = skillRepository.findById(skillId) + .orElseThrow(() -> new DomainNotFoundException("error.skill.notFound", skillId)); + return unhideSkillInternal(skill, actorUserId, clientIp, userAgent); + } + + private Skill unhideSkillInternal(Skill skill, String actorUserId, String clientIp, String userAgent) { skill.setHidden(false); skill.setHiddenAt(null); skill.setHiddenBy(null); skill.setUpdatedBy(actorUserId); Skill saved = skillRepository.save(skill); - auditLogService.record(actorUserId, "UNHIDE_SKILL", "SKILL", skillId, null, clientIp, userAgent, null); + auditLogService.record(actorUserId, "UNHIDE_SKILL", "SKILL", skill.getId(), null, clientIp, userAgent, null); return saved; } @@ -147,11 +180,12 @@ public class SkillGovernanceService { public Skill unarchiveSkill(Long skillId, String actorUserId, Map userNamespaceRoles, + Set platformRoles, String clientIp, String userAgent) { Skill skill = skillRepository.findById(skillId) .orElseThrow(() -> new DomainNotFoundException("error.skill.notFound", skillId)); - assertCanManageLifecycle(skill, actorUserId, userNamespaceRoles); + assertCanManageLifecycle(skill, actorUserId, userNamespaceRoles, platformRoles); SkillStatus previousStatus = skill.getStatus(); skill.setStatus(SkillStatus.ACTIVE); @@ -167,10 +201,11 @@ public class SkillGovernanceService { SkillVersion version, String actorUserId, Map userNamespaceRoles, + Set platformRoles, String clientIp, String userAgent, String namespaceSlug) { - assertCanManageLifecycle(skill, actorUserId, userNamespaceRoles); + assertCanManageLifecycle(skill, actorUserId, userNamespaceRoles, platformRoles); if (version.getStatus() != SkillVersionStatus.DRAFT && version.getStatus() != SkillVersionStatus.REJECTED && version.getStatus() != SkillVersionStatus.SCAN_FAILED @@ -297,7 +332,12 @@ public class SkillGovernanceService { private void assertCanManageLifecycle(Skill skill, String actorUserId, - Map userNamespaceRoles) { + Map userNamespaceRoles, + Set platformRoles) { + if (platformRoles != null && + (platformRoles.contains("SUPER_ADMIN") || platformRoles.contains("SKILL_ADMIN"))) { + return; + } NamespaceRole namespaceRole = userNamespaceRoles != null ? userNamespaceRoles.get(skill.getNamespaceId()) : null; boolean canManage = skill.getOwnerId().equals(actorUserId) || namespaceRole == NamespaceRole.ADMIN