feat(social): add SkillStar and SkillRating controllers

This commit is contained in:
vsxd 2026-03-12 18:06:03 +08:00
parent 7af86eed21
commit dd4f8d8abe
4 changed files with 325 additions and 0 deletions

View file

@ -0,0 +1,48 @@
package com.iflytek.skillhub.controller.portal;
import com.iflytek.skillhub.auth.rbac.PlatformPrincipal;
import com.iflytek.skillhub.controller.BaseApiController;
import com.iflytek.skillhub.dto.ApiResponse;
import com.iflytek.skillhub.dto.ApiResponseFactory;
import com.iflytek.skillhub.domain.social.SkillRatingService;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
import java.util.Optional;
@RestController
@RequestMapping("/api/v1/skills")
public class SkillRatingController extends BaseApiController {
private final SkillRatingService skillRatingService;
public SkillRatingController(ApiResponseFactory responseFactory,
SkillRatingService skillRatingService) {
super(responseFactory);
this.skillRatingService = skillRatingService;
}
@PutMapping("/{skillId}/rating")
public ResponseEntity<Void> rateSkill(
@PathVariable Long skillId,
@RequestBody Map<String, Short> request,
@AuthenticationPrincipal PlatformPrincipal principal) {
Short score = request.get("score");
skillRatingService.rate(skillId, principal.userId(), score);
return ResponseEntity.noContent().build();
}
@GetMapping("/{skillId}/rating")
public ApiResponse<Map<String, Object>> getUserRating(
@PathVariable Long skillId,
@AuthenticationPrincipal PlatformPrincipal principal) {
Optional<Short> rating = skillRatingService.getUserRating(skillId, principal.userId());
Map<String, Object> data = Map.of(
"score", rating.orElse((short) 0),
"rated", rating.isPresent()
);
return ok("response.success.skill.rating.get", data);
}
}

View file

@ -0,0 +1,47 @@
package com.iflytek.skillhub.controller.portal;
import com.iflytek.skillhub.auth.rbac.PlatformPrincipal;
import com.iflytek.skillhub.controller.BaseApiController;
import com.iflytek.skillhub.dto.ApiResponse;
import com.iflytek.skillhub.dto.ApiResponseFactory;
import com.iflytek.skillhub.domain.social.SkillStarService;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/v1/skills")
public class SkillStarController extends BaseApiController {
private final SkillStarService skillStarService;
public SkillStarController(ApiResponseFactory responseFactory,
SkillStarService skillStarService) {
super(responseFactory);
this.skillStarService = skillStarService;
}
@PutMapping("/{skillId}/star")
public ResponseEntity<Void> starSkill(
@PathVariable Long skillId,
@AuthenticationPrincipal PlatformPrincipal principal) {
skillStarService.star(skillId, principal.userId());
return ResponseEntity.noContent().build();
}
@DeleteMapping("/{skillId}/star")
public ResponseEntity<Void> unstarSkill(
@PathVariable Long skillId,
@AuthenticationPrincipal PlatformPrincipal principal) {
skillStarService.unstar(skillId, principal.userId());
return ResponseEntity.noContent().build();
}
@GetMapping("/{skillId}/star")
public ApiResponse<Boolean> checkStarred(
@PathVariable Long skillId,
@AuthenticationPrincipal PlatformPrincipal principal) {
boolean starred = skillStarService.isStarred(skillId, principal.userId());
return ok("response.success.skill.star.check", starred);
}
}

View file

@ -0,0 +1,107 @@
package com.iflytek.skillhub.controller;
import com.iflytek.skillhub.auth.rbac.PlatformPrincipal;
import com.iflytek.skillhub.domain.namespace.NamespaceMemberRepository;
import com.iflytek.skillhub.domain.social.SkillRatingService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
class SkillRatingControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private SkillRatingService skillRatingService;
@MockBean
private NamespaceMemberRepository namespaceMemberRepository;
@Test
void rate_skill_returns_204() throws Exception {
PlatformPrincipal principal = new PlatformPrincipal(
"user-42",
"tester",
"tester@example.com",
"https://example.com/avatar.png",
"github",
Set.of("SUPER_ADMIN")
);
var auth = new UsernamePasswordAuthenticationToken(
principal,
null,
List.of(new SimpleGrantedAuthority("ROLE_SUPER_ADMIN"))
);
mockMvc.perform(put("/api/v1/skills/10/rating")
.with(authentication(auth))
.with(csrf())
.contentType(MediaType.APPLICATION_JSON)
.content("{\"score\": 4}"))
.andExpect(status().isNoContent());
verify(skillRatingService).rate(eq(10L), eq("user-42"), eq((short) 4));
}
@Test
void get_user_rating_returns_score() throws Exception {
PlatformPrincipal principal = new PlatformPrincipal(
"user-42",
"tester",
"tester@example.com",
"https://example.com/avatar.png",
"github",
Set.of("SUPER_ADMIN")
);
var auth = new UsernamePasswordAuthenticationToken(
principal,
null,
List.of(new SimpleGrantedAuthority("ROLE_SUPER_ADMIN"))
);
when(skillRatingService.getUserRating(eq(10L), eq("user-42")))
.thenReturn(Optional.of((short) 4));
mockMvc.perform(get("/api/v1/skills/10/rating")
.with(authentication(auth))
.with(csrf()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data.score").value(4))
.andExpect(jsonPath("$.data.rated").value(true))
.andExpect(jsonPath("$.timestamp").isNotEmpty())
.andExpect(jsonPath("$.requestId").isNotEmpty());
}
@Test
void rate_skill_unauthenticated_returns_401() throws Exception {
mockMvc.perform(put("/api/v1/skills/10/rating")
.with(csrf())
.contentType(MediaType.APPLICATION_JSON)
.content("{\"score\": 4}"))
.andExpect(status().isUnauthorized());
}
}

View file

@ -0,0 +1,123 @@
package com.iflytek.skillhub.controller;
import com.iflytek.skillhub.auth.rbac.PlatformPrincipal;
import com.iflytek.skillhub.domain.namespace.NamespaceMemberRepository;
import com.iflytek.skillhub.domain.social.SkillStarService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import java.util.List;
import java.util.Set;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
class SkillStarControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private SkillStarService skillStarService;
@MockBean
private NamespaceMemberRepository namespaceMemberRepository;
@Test
void star_skill_returns_204() throws Exception {
PlatformPrincipal principal = new PlatformPrincipal(
"user-42",
"tester",
"tester@example.com",
"https://example.com/avatar.png",
"github",
Set.of("SUPER_ADMIN")
);
var auth = new UsernamePasswordAuthenticationToken(
principal,
null,
List.of(new SimpleGrantedAuthority("ROLE_SUPER_ADMIN"))
);
mockMvc.perform(put("/api/v1/skills/10/star")
.with(authentication(auth))
.with(csrf()))
.andExpect(status().isNoContent());
verify(skillStarService).star(eq(10L), eq("user-42"));
}
@Test
void unstar_skill_returns_204() throws Exception {
PlatformPrincipal principal = new PlatformPrincipal(
"user-42",
"tester",
"tester@example.com",
"https://example.com/avatar.png",
"github",
Set.of("SUPER_ADMIN")
);
var auth = new UsernamePasswordAuthenticationToken(
principal,
null,
List.of(new SimpleGrantedAuthority("ROLE_SUPER_ADMIN"))
);
mockMvc.perform(delete("/api/v1/skills/10/star")
.with(authentication(auth))
.with(csrf()))
.andExpect(status().isNoContent());
verify(skillStarService).unstar(eq(10L), eq("user-42"));
}
@Test
void star_skill_unauthenticated_returns_401() throws Exception {
mockMvc.perform(put("/api/v1/skills/10/star")
.with(csrf()))
.andExpect(status().isUnauthorized());
}
@Test
void check_starred_returns_true() throws Exception {
PlatformPrincipal principal = new PlatformPrincipal(
"user-42",
"tester",
"tester@example.com",
"https://example.com/avatar.png",
"github",
Set.of("SUPER_ADMIN")
);
var auth = new UsernamePasswordAuthenticationToken(
principal,
null,
List.of(new SimpleGrantedAuthority("ROLE_SUPER_ADMIN"))
);
when(skillStarService.isStarred(eq(10L), eq("user-42"))).thenReturn(true);
mockMvc.perform(get("/api/v1/skills/10/star")
.with(authentication(auth))
.with(csrf()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data").value(true))
.andExpect(jsonPath("$.timestamp").isNotEmpty())
.andExpect(jsonPath("$.requestId").isNotEmpty());
}
}