16 changed files with 762 additions and 0 deletions
@ -0,0 +1,66 @@ |
|||
package com.mydreamjob.skill.domain; |
|||
|
|||
import jakarta.persistence.Column; |
|||
import jakarta.persistence.Entity; |
|||
import jakarta.persistence.GeneratedValue; |
|||
import jakarta.persistence.GenerationType; |
|||
import jakarta.persistence.Id; |
|||
import jakarta.persistence.Table; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Builder; |
|||
import lombok.Getter; |
|||
import lombok.NoArgsConstructor; |
|||
import lombok.Setter; |
|||
import org.hibernate.annotations.JdbcTypeCode; |
|||
import org.hibernate.type.SqlTypes; |
|||
|
|||
import java.math.BigDecimal; |
|||
import java.time.LocalDateTime; |
|||
|
|||
@Getter |
|||
@Setter |
|||
@Builder |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
@Entity |
|||
@Table(name = "occupation_skills") |
|||
public class OccupationSkill { |
|||
|
|||
@Id |
|||
@GeneratedValue(strategy = GenerationType.IDENTITY) |
|||
private Long id; |
|||
|
|||
@Column(name = "occupation_id", nullable = false) |
|||
private Long occupationId; |
|||
|
|||
@Column(name = "skill_id", nullable = false) |
|||
private Long skillId; |
|||
|
|||
@Column(name = "category_id", nullable = false) |
|||
private Long categoryId; |
|||
|
|||
@Column(name = "importance", precision = 4, scale = 2) |
|||
private BigDecimal importance; |
|||
|
|||
@Column(name = "importance_source_code", length = 20) |
|||
private String importanceSourceCode; |
|||
|
|||
@Column(name = "importance_element_id", length = 30) |
|||
private String importanceElementId; |
|||
|
|||
@JdbcTypeCode(SqlTypes.TINYINT) |
|||
@Column(name = "importance_is_proxy", nullable = false) |
|||
private Boolean importanceIsProxy; |
|||
|
|||
@Column(name = "relationship_type", nullable = false, length = 20) |
|||
private String relationshipType; |
|||
|
|||
@Column(name = "source_version_id", nullable = false) |
|||
private Long sourceVersionId; |
|||
|
|||
@Column(name = "created_at", nullable = false, updatable = false) |
|||
private LocalDateTime createdAt; |
|||
|
|||
@Column(name = "updated_at", nullable = false) |
|||
private LocalDateTime updatedAt; |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
package com.mydreamjob.skill.domain; |
|||
|
|||
import jakarta.persistence.Column; |
|||
import jakarta.persistence.Entity; |
|||
import jakarta.persistence.GeneratedValue; |
|||
import jakarta.persistence.GenerationType; |
|||
import jakarta.persistence.Id; |
|||
import jakarta.persistence.Table; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Builder; |
|||
import lombok.Getter; |
|||
import lombok.NoArgsConstructor; |
|||
import lombok.Setter; |
|||
|
|||
import java.time.LocalDateTime; |
|||
|
|||
@Getter |
|||
@Setter |
|||
@Builder |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
@Entity |
|||
@Table(name = "skills") |
|||
public class Skill { |
|||
|
|||
@Id |
|||
@GeneratedValue(strategy = GenerationType.IDENTITY) |
|||
private Long id; |
|||
|
|||
@Column(name = "code", nullable = false, length = 120) |
|||
private String code; |
|||
|
|||
@Column(name = "name_zh", nullable = false, length = 180) |
|||
private String nameZh; |
|||
|
|||
@Column(name = "name_en", length = 180) |
|||
private String nameEn; |
|||
|
|||
@Column(name = "source_type", nullable = false, length = 20) |
|||
private String sourceType; |
|||
|
|||
@Column(name = "source_code", nullable = false, length = 180) |
|||
private String sourceCode; |
|||
|
|||
@Column(name = "source_version_id", nullable = false) |
|||
private Long sourceVersionId; |
|||
|
|||
@Column(name = "sort_order", nullable = false) |
|||
private Integer sortOrder; |
|||
|
|||
@Column(name = "created_at", nullable = false, updatable = false) |
|||
private LocalDateTime createdAt; |
|||
|
|||
@Column(name = "updated_at", nullable = false) |
|||
private LocalDateTime updatedAt; |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
package com.mydreamjob.skill.domain; |
|||
|
|||
import jakarta.persistence.Column; |
|||
import jakarta.persistence.Entity; |
|||
import jakarta.persistence.GeneratedValue; |
|||
import jakarta.persistence.GenerationType; |
|||
import jakarta.persistence.Id; |
|||
import jakarta.persistence.Table; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Builder; |
|||
import lombok.Getter; |
|||
import lombok.NoArgsConstructor; |
|||
import lombok.Setter; |
|||
|
|||
import java.time.LocalDateTime; |
|||
|
|||
@Getter |
|||
@Setter |
|||
@Builder |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
@Entity |
|||
@Table(name = "skill_aliases") |
|||
public class SkillAlias { |
|||
|
|||
@Id |
|||
@GeneratedValue(strategy = GenerationType.IDENTITY) |
|||
private Long id; |
|||
|
|||
@Column(name = "skill_id", nullable = false) |
|||
private Long skillId; |
|||
|
|||
@Column(name = "alias", nullable = false, length = 180) |
|||
private String alias; |
|||
|
|||
@Column(name = "normalized_alias", nullable = false, length = 180) |
|||
private String normalizedAlias; |
|||
|
|||
@Column(name = "created_at", nullable = false, updatable = false) |
|||
private LocalDateTime createdAt; |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
package com.mydreamjob.skill.domain; |
|||
|
|||
import jakarta.persistence.Column; |
|||
import jakarta.persistence.Entity; |
|||
import jakarta.persistence.GeneratedValue; |
|||
import jakarta.persistence.GenerationType; |
|||
import jakarta.persistence.Id; |
|||
import jakarta.persistence.Table; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Builder; |
|||
import lombok.Getter; |
|||
import lombok.NoArgsConstructor; |
|||
import lombok.Setter; |
|||
|
|||
import java.time.LocalDateTime; |
|||
|
|||
@Getter |
|||
@Setter |
|||
@Builder |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
@Entity |
|||
@Table(name = "skill_categories") |
|||
public class SkillCategory { |
|||
|
|||
@Id |
|||
@GeneratedValue(strategy = GenerationType.IDENTITY) |
|||
private Long id; |
|||
|
|||
@Column(name = "occupation_id", nullable = false) |
|||
private Long occupationId; |
|||
|
|||
@Column(name = "code", nullable = false, length = 100) |
|||
private String code; |
|||
|
|||
@Column(name = "name_zh", nullable = false, length = 120) |
|||
private String nameZh; |
|||
|
|||
@Column(name = "suggestion_template_code", nullable = false, length = 30) |
|||
private String suggestionTemplateCode; |
|||
|
|||
@Column(name = "sort_order", nullable = false) |
|||
private Integer sortOrder; |
|||
|
|||
@Column(name = "created_at", nullable = false, updatable = false) |
|||
private LocalDateTime createdAt; |
|||
|
|||
@Column(name = "updated_at", nullable = false) |
|||
private LocalDateTime updatedAt; |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
package com.mydreamjob.skill.domain; |
|||
|
|||
import jakarta.persistence.Column; |
|||
import jakarta.persistence.Entity; |
|||
import jakarta.persistence.GeneratedValue; |
|||
import jakarta.persistence.GenerationType; |
|||
import jakarta.persistence.Id; |
|||
import jakarta.persistence.Table; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Builder; |
|||
import lombok.Getter; |
|||
import lombok.NoArgsConstructor; |
|||
import lombok.Setter; |
|||
|
|||
import java.time.LocalDateTime; |
|||
|
|||
@Getter |
|||
@Setter |
|||
@Builder |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
@Entity |
|||
@Table(name = "skill_industries") |
|||
public class SkillIndustry { |
|||
|
|||
@Id |
|||
@GeneratedValue(strategy = GenerationType.IDENTITY) |
|||
private Long id; |
|||
|
|||
@Column(name = "code", nullable = false, length = 80) |
|||
private String code; |
|||
|
|||
@Column(name = "name_zh", nullable = false, length = 120) |
|||
private String nameZh; |
|||
|
|||
@Column(name = "source_code", length = 120) |
|||
private String sourceCode; |
|||
|
|||
@Column(name = "sort_order", nullable = false) |
|||
private Integer sortOrder; |
|||
|
|||
@Column(name = "created_at", nullable = false, updatable = false) |
|||
private LocalDateTime createdAt; |
|||
|
|||
@Column(name = "updated_at", nullable = false) |
|||
private LocalDateTime updatedAt; |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
package com.mydreamjob.skill.domain; |
|||
|
|||
import jakarta.persistence.Column; |
|||
import jakarta.persistence.Entity; |
|||
import jakarta.persistence.GeneratedValue; |
|||
import jakarta.persistence.GenerationType; |
|||
import jakarta.persistence.Id; |
|||
import jakarta.persistence.Table; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Builder; |
|||
import lombok.Getter; |
|||
import lombok.NoArgsConstructor; |
|||
import lombok.Setter; |
|||
|
|||
import java.time.LocalDateTime; |
|||
|
|||
@Getter |
|||
@Setter |
|||
@Builder |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
@Entity |
|||
@Table(name = "skill_occupations") |
|||
public class SkillOccupation { |
|||
|
|||
@Id |
|||
@GeneratedValue(strategy = GenerationType.IDENTITY) |
|||
private Long id; |
|||
|
|||
@Column(name = "industry_id", nullable = false) |
|||
private Long industryId; |
|||
|
|||
@Column(name = "code", nullable = false, length = 100) |
|||
private String code; |
|||
|
|||
@Column(name = "name_zh", nullable = false, length = 150) |
|||
private String nameZh; |
|||
|
|||
@Column(name = "name_en", length = 180) |
|||
private String nameEn; |
|||
|
|||
@Column(name = "source_code", nullable = false, length = 150) |
|||
private String sourceCode; |
|||
|
|||
@Column(name = "source_version_id", nullable = false) |
|||
private Long sourceVersionId; |
|||
|
|||
@Column(name = "sort_order", nullable = false) |
|||
private Integer sortOrder; |
|||
|
|||
@Column(name = "created_at", nullable = false, updatable = false) |
|||
private LocalDateTime createdAt; |
|||
|
|||
@Column(name = "updated_at", nullable = false) |
|||
private LocalDateTime updatedAt; |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
package com.mydreamjob.skill.domain; |
|||
|
|||
import jakarta.persistence.Column; |
|||
import jakarta.persistence.Entity; |
|||
import jakarta.persistence.GeneratedValue; |
|||
import jakarta.persistence.GenerationType; |
|||
import jakarta.persistence.Id; |
|||
import jakarta.persistence.Table; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Builder; |
|||
import lombok.Getter; |
|||
import lombok.NoArgsConstructor; |
|||
import lombok.Setter; |
|||
|
|||
import java.time.LocalDateTime; |
|||
|
|||
@Getter |
|||
@Setter |
|||
@Builder |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
@Entity |
|||
@Table(name = "skill_source_versions") |
|||
public class SkillSourceVersion { |
|||
|
|||
@Id |
|||
@GeneratedValue(strategy = GenerationType.IDENTITY) |
|||
private Long id; |
|||
|
|||
@Column(name = "source_name", nullable = false, length = 100) |
|||
private String sourceName; |
|||
|
|||
@Column(name = "source_version", nullable = false, length = 150) |
|||
private String sourceVersion; |
|||
|
|||
@Column(name = "license", nullable = false, length = 255) |
|||
private String license; |
|||
|
|||
@Column(name = "attribution_url", nullable = false, length = 500) |
|||
private String attributionUrl; |
|||
|
|||
@Column(name = "imported_at", nullable = false) |
|||
private LocalDateTime importedAt; |
|||
|
|||
@Column(name = "created_at", nullable = false, updatable = false) |
|||
private LocalDateTime createdAt; |
|||
|
|||
@Column(name = "updated_at", nullable = false) |
|||
private LocalDateTime updatedAt; |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
package com.mydreamjob.skill.dto; |
|||
|
|||
import java.math.BigDecimal; |
|||
|
|||
public record SkillCloudSkillProjection( |
|||
Long skillId, |
|||
String skillCode, |
|||
String nameZh, |
|||
String nameEn, |
|||
String sourceType, |
|||
String sourceCode, |
|||
Long categoryId, |
|||
String categoryName, |
|||
BigDecimal importance, |
|||
String importanceSourceCode, |
|||
String importanceElementId, |
|||
Boolean importanceIsProxy, |
|||
Integer skillSortOrder, |
|||
Integer categorySortOrder |
|||
) { |
|||
} |
|||
@ -0,0 +1,71 @@ |
|||
package com.mydreamjob.skill.repository; |
|||
|
|||
import com.mydreamjob.skill.domain.OccupationSkill; |
|||
import com.mydreamjob.skill.dto.SkillCloudSkillProjection; |
|||
import org.springframework.data.jpa.repository.JpaRepository; |
|||
import org.springframework.data.jpa.repository.Query; |
|||
import org.springframework.data.repository.query.Param; |
|||
import org.springframework.transaction.annotation.Transactional; |
|||
|
|||
import java.util.List; |
|||
|
|||
public interface OccupationSkillRepository extends JpaRepository<OccupationSkill, Long> { |
|||
|
|||
long countByOccupationId(Long occupationId); |
|||
|
|||
long countByOccupationIdAndCategoryId(Long occupationId, Long categoryId); |
|||
|
|||
@Transactional(readOnly = true) |
|||
@Query(""" |
|||
SELECT new com.mydreamjob.skill.dto.SkillCloudSkillProjection( |
|||
os.skillId, |
|||
s.code, |
|||
s.nameZh, |
|||
s.nameEn, |
|||
s.sourceType, |
|||
s.sourceCode, |
|||
c.id, |
|||
c.nameZh, |
|||
os.importance, |
|||
os.importanceSourceCode, |
|||
os.importanceElementId, |
|||
os.importanceIsProxy, |
|||
s.sortOrder, |
|||
c.sortOrder |
|||
) |
|||
FROM OccupationSkill os, Skill s, SkillCategory c |
|||
WHERE os.skillId = s.id |
|||
AND os.categoryId = c.id |
|||
AND os.occupationId = :occupationId |
|||
""") |
|||
List<SkillCloudSkillProjection> findSkillProjectionsByOccupationId( |
|||
@Param("occupationId") Long occupationId); |
|||
|
|||
@Transactional(readOnly = true) |
|||
@Query(""" |
|||
SELECT new com.mydreamjob.skill.dto.SkillCloudSkillProjection( |
|||
os.skillId, |
|||
s.code, |
|||
s.nameZh, |
|||
s.nameEn, |
|||
s.sourceType, |
|||
s.sourceCode, |
|||
c.id, |
|||
c.nameZh, |
|||
os.importance, |
|||
os.importanceSourceCode, |
|||
os.importanceElementId, |
|||
os.importanceIsProxy, |
|||
s.sortOrder, |
|||
c.sortOrder |
|||
) |
|||
FROM OccupationSkill os, Skill s, SkillCategory c |
|||
WHERE os.skillId = s.id |
|||
AND os.categoryId = c.id |
|||
AND os.occupationId = :occupationId |
|||
AND os.categoryId = :categoryId |
|||
""") |
|||
List<SkillCloudSkillProjection> findSkillProjectionsByOccupationIdAndCategoryId( |
|||
@Param("occupationId") Long occupationId, |
|||
@Param("categoryId") Long categoryId); |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
package com.mydreamjob.skill.repository; |
|||
|
|||
import com.mydreamjob.skill.domain.SkillAlias; |
|||
import org.springframework.data.jpa.repository.JpaRepository; |
|||
|
|||
import java.util.Collection; |
|||
import java.util.List; |
|||
import java.util.Optional; |
|||
|
|||
public interface SkillAliasRepository extends JpaRepository<SkillAlias, Long> { |
|||
|
|||
Optional<SkillAlias> findByNormalizedAlias(String normalizedAlias); |
|||
|
|||
List<SkillAlias> findAllBySkillIdInOrderByIdAsc(Collection<Long> skillIds); |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
package com.mydreamjob.skill.repository; |
|||
|
|||
import com.mydreamjob.skill.domain.SkillCategory; |
|||
import org.springframework.data.jpa.repository.JpaRepository; |
|||
|
|||
import java.util.Optional; |
|||
|
|||
public interface SkillCategoryRepository extends JpaRepository<SkillCategory, Long> { |
|||
|
|||
boolean existsByIdAndOccupationId(Long id, Long occupationId); |
|||
|
|||
Optional<SkillCategory> findByIdAndOccupationId(Long id, Long occupationId); |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
package com.mydreamjob.skill.repository; |
|||
|
|||
import com.mydreamjob.skill.domain.SkillIndustry; |
|||
import org.springframework.data.jpa.repository.JpaRepository; |
|||
|
|||
public interface SkillIndustryRepository extends JpaRepository<SkillIndustry, Long> { |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
package com.mydreamjob.skill.repository; |
|||
|
|||
import com.mydreamjob.skill.domain.SkillOccupation; |
|||
import org.springframework.data.jpa.repository.JpaRepository; |
|||
|
|||
import java.util.Optional; |
|||
|
|||
public interface SkillOccupationRepository extends JpaRepository<SkillOccupation, Long> { |
|||
|
|||
boolean existsByIdAndIndustryId(Long id, Long industryId); |
|||
|
|||
Optional<SkillOccupation> findByIdAndIndustryId(Long id, Long industryId); |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
package com.mydreamjob.skill.repository; |
|||
|
|||
import com.mydreamjob.skill.domain.Skill; |
|||
import org.springframework.data.jpa.repository.JpaRepository; |
|||
|
|||
import java.util.Collection; |
|||
import java.util.List; |
|||
import java.util.Optional; |
|||
|
|||
public interface SkillRepository extends JpaRepository<Skill, Long> { |
|||
|
|||
Optional<Skill> findByCode(String code); |
|||
|
|||
List<Skill> findAllByIdInOrderByIdAsc(Collection<Long> ids); |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
package com.mydreamjob.skill.repository; |
|||
|
|||
import com.mydreamjob.skill.domain.SkillSourceVersion; |
|||
import org.springframework.data.jpa.repository.JpaRepository; |
|||
|
|||
public interface SkillSourceVersionRepository extends JpaRepository<SkillSourceVersion, Long> { |
|||
} |
|||
@ -0,0 +1,234 @@ |
|||
package com.mydreamjob.skill; |
|||
|
|||
import com.mydreamjob.skill.domain.Skill; |
|||
import com.mydreamjob.skill.domain.SkillAlias; |
|||
import com.mydreamjob.skill.dto.SkillCloudSkillProjection; |
|||
import com.mydreamjob.skill.repository.OccupationSkillRepository; |
|||
import com.mydreamjob.skill.repository.SkillAliasRepository; |
|||
import com.mydreamjob.skill.repository.SkillCategoryRepository; |
|||
import com.mydreamjob.skill.repository.SkillIndustryRepository; |
|||
import com.mydreamjob.skill.repository.SkillOccupationRepository; |
|||
import com.mydreamjob.skill.repository.SkillRepository; |
|||
import com.mydreamjob.skill.repository.SkillSourceVersionRepository; |
|||
import org.junit.jupiter.api.Test; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.boot.test.context.SpringBootTest; |
|||
import org.springframework.boot.test.mock.mockito.MockBean; |
|||
import org.springframework.data.redis.core.RedisTemplate; |
|||
import org.springframework.test.context.ActiveProfiles; |
|||
import org.springframework.test.context.TestPropertySource; |
|||
import org.hibernate.boot.model.relational.Namespace; |
|||
import org.hibernate.boot.model.relational.Sequence; |
|||
import org.hibernate.mapping.Table; |
|||
import org.hibernate.tool.schema.spi.SchemaFilter; |
|||
import org.hibernate.tool.schema.spi.SchemaFilterProvider; |
|||
|
|||
import java.math.BigDecimal; |
|||
import java.util.List; |
|||
import java.util.Locale; |
|||
import java.util.Optional; |
|||
import java.util.Set; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThat; |
|||
|
|||
@SpringBootTest |
|||
@TestPropertySource(properties = { |
|||
"spring.datasource.url=jdbc:h2:mem:integrationdb_skill_repository;MODE=MySQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE", |
|||
"spring.jpa.hibernate.ddl-auto=validate", |
|||
"spring.jpa.properties.hibernate.hbm2ddl.schema_filter_provider=com.mydreamjob.skill.SkillTaxonomyRepositoryTest$SkillTaxonomySchemaFilterProvider" |
|||
}) |
|||
@ActiveProfiles("integration-test") |
|||
class SkillTaxonomyRepositoryTest { |
|||
|
|||
@Autowired |
|||
private SkillSourceVersionRepository sourceVersionRepository; |
|||
|
|||
@Autowired |
|||
private SkillIndustryRepository industryRepository; |
|||
|
|||
@Autowired |
|||
private SkillOccupationRepository occupationRepository; |
|||
|
|||
@Autowired |
|||
private SkillCategoryRepository categoryRepository; |
|||
|
|||
@Autowired |
|||
private SkillRepository skillRepository; |
|||
|
|||
@Autowired |
|||
private SkillAliasRepository skillAliasRepository; |
|||
|
|||
@Autowired |
|||
private OccupationSkillRepository occupationSkillRepository; |
|||
|
|||
@MockBean |
|||
private RedisTemplate<String, Object> redisTemplate; |
|||
|
|||
@Test |
|||
void entityMappingsAreValidatedAndSeedCountsMatchV36() { |
|||
assertThat(sourceVersionRepository.count()).isEqualTo(2L); |
|||
assertThat(industryRepository.count()).isEqualTo(3L); |
|||
assertThat(occupationRepository.count()).isEqualTo(10L); |
|||
assertThat(categoryRepository.count()).isEqualTo(68L); |
|||
assertThat(skillRepository.count()).isEqualTo(72L); |
|||
assertThat(skillAliasRepository.count()).isEqualTo(72L); |
|||
assertThat(occupationSkillRepository.count()).isEqualTo(139L); |
|||
} |
|||
|
|||
@Test |
|||
void associationChecksAcceptOnlyConsistentTaxonomyPaths() { |
|||
assertThat(industryRepository.existsById(1L)).isTrue(); |
|||
assertThat(occupationRepository.existsByIdAndIndustryId(1L, 1L)).isTrue(); |
|||
assertThat(categoryRepository.existsByIdAndOccupationId(101L, 1L)).isTrue(); |
|||
|
|||
assertThat(occupationRepository.existsByIdAndIndustryId(1L, 2L)).isFalse(); |
|||
assertThat(categoryRepository.existsByIdAndOccupationId(101L, 2L)).isFalse(); |
|||
assertThat(occupationRepository.existsByIdAndIndustryId(999L, 1L)).isFalse(); |
|||
assertThat(categoryRepository.existsByIdAndOccupationId(999L, 1L)).isFalse(); |
|||
} |
|||
|
|||
@Test |
|||
void projectionUsesWholeOccupationWhenCategoryMissingAndFiltersWhenProvided() { |
|||
List<SkillCloudSkillProjection> occupationSkills = |
|||
occupationSkillRepository.findSkillProjectionsByOccupationId(1L); |
|||
|
|||
assertThat(occupationSkills).hasSize(15); |
|||
assertThat(occupationSkills) |
|||
.extracting(SkillCloudSkillProjection::skillId) |
|||
.containsExactlyInAnyOrder( |
|||
3L, 4L, 7L, 8L, 26L, 27L, 28L, 35L, |
|||
54L, 55L, 38L, 39L, 32L, 24L, 50L); |
|||
|
|||
List<SkillCloudSkillProjection> languageSkills = |
|||
occupationSkillRepository.findSkillProjectionsByOccupationIdAndCategoryId(1L, 101L); |
|||
|
|||
assertThat(languageSkills).hasSize(2); |
|||
assertThat(languageSkills) |
|||
.extracting(SkillCloudSkillProjection::skillId) |
|||
.containsExactlyInAnyOrder(3L, 4L); |
|||
assertThat(languageSkills) |
|||
.extracting(SkillCloudSkillProjection::categoryId) |
|||
.containsOnly(101L); |
|||
|
|||
assertThat(occupationSkillRepository |
|||
.findSkillProjectionsByOccupationIdAndCategoryId(2L, 101L)) |
|||
.isEmpty(); |
|||
} |
|||
|
|||
@Test |
|||
void projectionCarriesOnetProvenanceNullImportanceAndSortOrders() { |
|||
SkillCloudSkillProjection react = occupationSkillRepository |
|||
.findSkillProjectionsByOccupationIdAndCategoryId(1L, 103L) |
|||
.stream() |
|||
.filter(item -> item.skillId().equals(26L)) |
|||
.findFirst() |
|||
.orElseThrow(); |
|||
|
|||
assertThat(react.skillCode()).isEqualTo("REACT"); |
|||
assertThat(react.nameZh()).isEqualTo("React"); |
|||
assertThat(react.nameEn()).isEqualTo("React"); |
|||
assertThat(react.sourceType()).isEqualTo("O*NET"); |
|||
assertThat(react.sourceCode()).isEqualTo("15-1254.00|React|2.E.7.h"); |
|||
assertThat(react.categoryName()).isEqualTo("前端框架"); |
|||
assertThat(react.importance()).isEqualByComparingTo(new BigDecimal("4.88")); |
|||
assertThat(react.importanceSourceCode()).isEqualTo("15-1254.00"); |
|||
assertThat(react.importanceElementId()).isEqualTo("4.A.3.b.1"); |
|||
assertThat(react.importanceIsProxy()).isTrue(); |
|||
assertThat(react.skillSortOrder()).isEqualTo(26); |
|||
assertThat(react.categorySortOrder()).isEqualTo(3); |
|||
|
|||
SkillCloudSkillProjection figma = occupationSkillRepository |
|||
.findSkillProjectionsByOccupationIdAndCategoryId(1L, 104L) |
|||
.stream() |
|||
.filter(item -> item.skillId().equals(50L)) |
|||
.findFirst() |
|||
.orElseThrow(); |
|||
|
|||
assertThat(figma.sourceType()).isEqualTo("O*NET"); |
|||
assertThat(figma.sourceCode()).isEqualTo("15-1254.00|Figma|2.E.7.d"); |
|||
assertThat(figma.importance()).isNull(); |
|||
assertThat(figma.importanceSourceCode()).isNull(); |
|||
assertThat(figma.importanceElementId()).isNull(); |
|||
assertThat(figma.importanceIsProxy()).isFalse(); |
|||
} |
|||
|
|||
@Test |
|||
void aliasAndSkillLookupsReturnOnlyRequestedRecords() { |
|||
Optional<SkillAlias> normalizedAlias = skillAliasRepository.findByNormalizedAlias("react"); |
|||
assertThat(normalizedAlias).isPresent(); |
|||
assertThat(normalizedAlias.orElseThrow().getSkillId()).isEqualTo(26L); |
|||
|
|||
List<SkillAlias> aliases = skillAliasRepository |
|||
.findAllBySkillIdInOrderByIdAsc(List.of(3L, 26L)); |
|||
assertThat(aliases).hasSize(2); |
|||
assertThat(aliases) |
|||
.extracting(SkillAlias::getSkillId) |
|||
.containsExactly(3L, 26L); |
|||
|
|||
Optional<Skill> react = skillRepository.findByCode("REACT"); |
|||
assertThat(react).isPresent(); |
|||
assertThat(react.orElseThrow().getSourceType()).isEqualTo("O*NET"); |
|||
|
|||
List<Skill> skills = skillRepository.findAllByIdInOrderByIdAsc(List.of(4L, 3L)); |
|||
assertThat(skills) |
|||
.extracting(Skill::getId) |
|||
.containsExactly(3L, 4L); |
|||
} |
|||
|
|||
/** |
|||
* The legacy H2 schema has TINYINT/Boolean mismatches outside the V35 taxonomy. |
|||
* Scope validation to the seven T4 tables without changing historical migrations or entities. |
|||
*/ |
|||
public static class SkillTaxonomySchemaFilterProvider implements SchemaFilterProvider { |
|||
|
|||
private static final Set<String> VALIDATED_TABLES = Set.of( |
|||
"skill_source_versions", |
|||
"skill_industries", |
|||
"skill_occupations", |
|||
"skill_categories", |
|||
"skills", |
|||
"skill_aliases", |
|||
"occupation_skills" |
|||
); |
|||
|
|||
@Override |
|||
public SchemaFilter getCreateFilter() { |
|||
return SchemaFilter.ALL; |
|||
} |
|||
|
|||
@Override |
|||
public SchemaFilter getDropFilter() { |
|||
return SchemaFilter.ALL; |
|||
} |
|||
|
|||
@Override |
|||
public SchemaFilter getTruncatorFilter() { |
|||
return SchemaFilter.ALL; |
|||
} |
|||
|
|||
@Override |
|||
public SchemaFilter getMigrateFilter() { |
|||
return SchemaFilter.ALL; |
|||
} |
|||
|
|||
@Override |
|||
public SchemaFilter getValidateFilter() { |
|||
return new SchemaFilter() { |
|||
@Override |
|||
public boolean includeNamespace(Namespace namespace) { |
|||
return true; |
|||
} |
|||
|
|||
@Override |
|||
public boolean includeTable(Table table) { |
|||
return VALIDATED_TABLES.contains(table.getName().toLowerCase(Locale.ROOT)); |
|||
} |
|||
|
|||
@Override |
|||
public boolean includeSequence(Sequence sequence) { |
|||
return false; |
|||
} |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue