9 changed files with 732 additions and 0 deletions
@ -0,0 +1,63 @@ |
|||
package com.mydreamjob.onboarding; |
|||
|
|||
import jakarta.persistence.Column; |
|||
import jakarta.persistence.Entity; |
|||
import jakarta.persistence.EntityListeners; |
|||
import jakarta.persistence.GeneratedValue; |
|||
import jakarta.persistence.GenerationType; |
|||
import jakarta.persistence.Id; |
|||
import jakarta.persistence.Table; |
|||
import jakarta.persistence.UniqueConstraint; |
|||
import jakarta.persistence.Version; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Builder; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
import org.springframework.data.annotation.CreatedDate; |
|||
import org.springframework.data.annotation.LastModifiedDate; |
|||
import org.springframework.data.jpa.domain.support.AuditingEntityListener; |
|||
|
|||
import java.time.LocalDateTime; |
|||
|
|||
@Data |
|||
@Builder |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
@Entity |
|||
@Table( |
|||
name = "company_knowledge", |
|||
uniqueConstraints = @UniqueConstraint( |
|||
name = "uk_company_knowledge_company", |
|||
columnNames = "company_id")) |
|||
@EntityListeners(AuditingEntityListener.class) |
|||
public class CompanyKnowledge { |
|||
|
|||
@Id |
|||
@GeneratedValue(strategy = GenerationType.IDENTITY) |
|||
private Long id; |
|||
|
|||
@Column(name = "company_id", nullable = false) |
|||
private Long companyId; |
|||
|
|||
@Column(length = 2000) |
|||
private String culture; |
|||
|
|||
@Column(name = "work_policies", length = 4000) |
|||
private String workPolicies; |
|||
|
|||
@Column(name = "onboarding_guide", length = 4000) |
|||
private String onboardingGuide; |
|||
|
|||
@Version |
|||
@Column(nullable = false) |
|||
@Builder.Default |
|||
private Long version = 0L; |
|||
|
|||
@CreatedDate |
|||
@Column(name = "created_at", nullable = false, updatable = false) |
|||
private LocalDateTime createdAt; |
|||
|
|||
@LastModifiedDate |
|||
@Column(name = "updated_at", nullable = false) |
|||
private LocalDateTime updatedAt; |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
package com.mydreamjob.onboarding; |
|||
|
|||
import org.springframework.data.jpa.repository.JpaRepository; |
|||
import org.springframework.stereotype.Repository; |
|||
|
|||
import java.util.Optional; |
|||
|
|||
@Repository |
|||
public interface CompanyKnowledgeRepository extends JpaRepository<CompanyKnowledge, Long> { |
|||
|
|||
Optional<CompanyKnowledge> findByCompanyId(Long companyId); |
|||
} |
|||
@ -0,0 +1,95 @@ |
|||
package com.mydreamjob.onboarding; |
|||
|
|||
import jakarta.persistence.Column; |
|||
import jakarta.persistence.Entity; |
|||
import jakarta.persistence.EntityListeners; |
|||
import jakarta.persistence.GeneratedValue; |
|||
import jakarta.persistence.GenerationType; |
|||
import jakarta.persistence.Id; |
|||
import jakarta.persistence.Table; |
|||
import jakarta.persistence.UniqueConstraint; |
|||
import jakarta.persistence.Version; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Builder; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
import org.springframework.data.annotation.CreatedDate; |
|||
import org.springframework.data.annotation.LastModifiedDate; |
|||
import org.springframework.data.jpa.domain.support.AuditingEntityListener; |
|||
|
|||
import java.time.LocalDateTime; |
|||
|
|||
@Data |
|||
@Builder |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
@Entity |
|||
@Table( |
|||
name = "onboarding_checklist_items", |
|||
uniqueConstraints = { |
|||
@UniqueConstraint( |
|||
name = "uk_onboarding_checklist_scope_item", |
|||
columnNames = {"scope_type", "scope_id", "item_key"}), |
|||
@UniqueConstraint( |
|||
name = "uk_onboarding_checklist_company_idempotency", |
|||
columnNames = {"company_id", "idempotency_key"}) |
|||
}) |
|||
@EntityListeners(AuditingEntityListener.class) |
|||
public class OnboardingChecklistItem { |
|||
|
|||
@Id |
|||
@GeneratedValue(strategy = GenerationType.IDENTITY) |
|||
private Long id; |
|||
|
|||
@Column(name = "scope_type", nullable = false, length = 20) |
|||
private String scopeType; |
|||
|
|||
@Column(name = "scope_id", nullable = false) |
|||
private Long scopeId; |
|||
|
|||
@Column(name = "company_id") |
|||
private Long companyId; |
|||
|
|||
@Column(name = "item_key", nullable = false, length = 80) |
|||
private String itemKey; |
|||
|
|||
@Column(nullable = false, length = 120) |
|||
private String title; |
|||
|
|||
@Column(length = 1000) |
|||
private String description; |
|||
|
|||
@Column(nullable = false, length = 30) |
|||
private String category; |
|||
|
|||
@Column(name = "sort_order", nullable = false) |
|||
@Builder.Default |
|||
private Integer sortOrder = 1000; |
|||
|
|||
@Column(nullable = false) |
|||
@Builder.Default |
|||
private Boolean required = false; |
|||
|
|||
@Column(nullable = false) |
|||
@Builder.Default |
|||
private Boolean active = true; |
|||
|
|||
@Column(name = "idempotency_key", length = 64) |
|||
private String idempotencyKey; |
|||
|
|||
@Column(name = "request_hash", columnDefinition = "CHAR(64)") |
|||
private String requestHash; |
|||
|
|||
@Version |
|||
@Column(nullable = false) |
|||
@Builder.Default |
|||
private Long version = 0L; |
|||
|
|||
@CreatedDate |
|||
@Column(name = "created_at", nullable = false, updatable = false) |
|||
private LocalDateTime createdAt; |
|||
|
|||
@LastModifiedDate |
|||
@Column(name = "updated_at", nullable = false) |
|||
private LocalDateTime updatedAt; |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
package com.mydreamjob.onboarding; |
|||
|
|||
import org.springframework.data.jpa.repository.JpaRepository; |
|||
import org.springframework.data.jpa.repository.Query; |
|||
import org.springframework.data.repository.query.Param; |
|||
import org.springframework.stereotype.Repository; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Repository |
|||
public interface OnboardingChecklistItemRepository extends JpaRepository<OnboardingChecklistItem, Long> { |
|||
|
|||
@Query(""" |
|||
SELECT item |
|||
FROM OnboardingChecklistItem item |
|||
WHERE item.active = true |
|||
AND ( |
|||
item.scopeType = 'STANDARD' |
|||
OR (item.scopeType = 'COMPANY' |
|||
AND item.scopeId = :companyId |
|||
AND item.companyId = :companyId) |
|||
) |
|||
ORDER BY item.sortOrder ASC, item.id ASC |
|||
""") |
|||
List<OnboardingChecklistItem> findEffectiveChecklistItems(@Param("companyId") Long companyId); |
|||
} |
|||
@ -0,0 +1,64 @@ |
|||
package com.mydreamjob.onboarding; |
|||
|
|||
import jakarta.persistence.Column; |
|||
import jakarta.persistence.Entity; |
|||
import jakarta.persistence.EntityListeners; |
|||
import jakarta.persistence.GeneratedValue; |
|||
import jakarta.persistence.GenerationType; |
|||
import jakarta.persistence.Id; |
|||
import jakarta.persistence.Table; |
|||
import jakarta.persistence.UniqueConstraint; |
|||
import jakarta.persistence.Version; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Builder; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
import org.springframework.data.annotation.CreatedDate; |
|||
import org.springframework.data.annotation.LastModifiedDate; |
|||
import org.springframework.data.jpa.domain.support.AuditingEntityListener; |
|||
|
|||
import java.time.LocalDateTime; |
|||
|
|||
@Data |
|||
@Builder |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
@Entity |
|||
@Table( |
|||
name = "student_onboarding_progress", |
|||
uniqueConstraints = @UniqueConstraint( |
|||
name = "uk_student_onboarding_progress_student_item", |
|||
columnNames = {"student_user_id", "item_id"})) |
|||
@EntityListeners(AuditingEntityListener.class) |
|||
public class StudentOnboardingProgress { |
|||
|
|||
@Id |
|||
@GeneratedValue(strategy = GenerationType.IDENTITY) |
|||
private Long id; |
|||
|
|||
@Column(name = "student_user_id", nullable = false) |
|||
private Long studentUserId; |
|||
|
|||
@Column(name = "item_id", nullable = false) |
|||
private Long itemId; |
|||
|
|||
@Column(nullable = false) |
|||
@Builder.Default |
|||
private Boolean completed = false; |
|||
|
|||
@Column(name = "completed_at") |
|||
private LocalDateTime completedAt; |
|||
|
|||
@Version |
|||
@Column(nullable = false) |
|||
@Builder.Default |
|||
private Long version = 0L; |
|||
|
|||
@CreatedDate |
|||
@Column(name = "created_at", nullable = false, updatable = false) |
|||
private LocalDateTime createdAt; |
|||
|
|||
@LastModifiedDate |
|||
@Column(name = "updated_at", nullable = false) |
|||
private LocalDateTime updatedAt; |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
package com.mydreamjob.onboarding; |
|||
|
|||
import org.springframework.data.jpa.repository.JpaRepository; |
|||
import org.springframework.stereotype.Repository; |
|||
|
|||
import java.util.Collection; |
|||
import java.util.List; |
|||
import java.util.Optional; |
|||
|
|||
@Repository |
|||
public interface StudentOnboardingProgressRepository |
|||
extends JpaRepository<StudentOnboardingProgress, Long> { |
|||
|
|||
List<StudentOnboardingProgress> findByStudentUserIdAndItemIdIn( |
|||
Long studentUserId, Collection<Long> itemIds); |
|||
|
|||
Optional<StudentOnboardingProgress> findByStudentUserIdAndItemId( |
|||
Long studentUserId, Long itemId); |
|||
} |
|||
@ -0,0 +1,399 @@ |
|||
package com.mydreamjob.onboarding; |
|||
|
|||
import com.mydreamjob.auth.User; |
|||
import com.mydreamjob.auth.UserRepository; |
|||
import com.mydreamjob.company.Company; |
|||
import com.mydreamjob.company.CompanyRepository; |
|||
import com.mydreamjob.job.Job; |
|||
import com.mydreamjob.job.JobRepository; |
|||
import com.mydreamjob.review.JobReview; |
|||
import com.mydreamjob.review.JobReviewDimensionScore; |
|||
import com.mydreamjob.review.JobReviewDimensionScoreRepository; |
|||
import com.mydreamjob.review.JobReviewRepository; |
|||
import org.hibernate.boot.model.relational.Namespace; |
|||
import org.hibernate.boot.model.relational.Sequence; |
|||
import org.hibernate.mapping.Table; |
|||
import org.junit.jupiter.api.BeforeEach; |
|||
import org.hibernate.tool.schema.spi.SchemaFilter; |
|||
import org.hibernate.tool.schema.spi.SchemaFilterProvider; |
|||
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.jdbc.core.JdbcTemplate; |
|||
import org.springframework.test.context.ActiveProfiles; |
|||
import org.springframework.test.context.TestPropertySource; |
|||
import org.springframework.transaction.annotation.Transactional; |
|||
|
|||
import java.time.LocalDateTime; |
|||
import java.util.List; |
|||
import java.util.Locale; |
|||
import java.util.Optional; |
|||
import java.util.Set; |
|||
import java.util.concurrent.atomic.AtomicLong; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThat; |
|||
|
|||
@SpringBootTest |
|||
@TestPropertySource(properties = { |
|||
"spring.datasource.url=jdbc:h2:mem:integrationdb_onboarding_repository;MODE=MySQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE", |
|||
"spring.datasource.driver-class-name=org.h2.Driver", |
|||
"spring.datasource.username=sa", |
|||
"spring.datasource.password=", |
|||
"spring.jpa.hibernate.ddl-auto=validate", |
|||
"spring.jpa.properties.hibernate.hbm2ddl.schema_filter_provider=com.mydreamjob.onboarding.OnboardingRepositoryIntegrationTest$OnboardingSchemaFilterProvider" |
|||
}) |
|||
@ActiveProfiles("integration-test") |
|||
@Transactional |
|||
class OnboardingRepositoryIntegrationTest { |
|||
|
|||
private static final AtomicLong PHONE_SEQ = new AtomicLong(900); |
|||
|
|||
@Autowired private OnboardingChecklistItemRepository checklistItemRepository; |
|||
@Autowired private StudentOnboardingProgressRepository progressRepository; |
|||
@Autowired private CompanyKnowledgeRepository knowledgeRepository; |
|||
@Autowired private JobReviewRepository reviewRepository; |
|||
@Autowired private JobReviewDimensionScoreRepository dimensionScoreRepository; |
|||
@Autowired private UserRepository userRepository; |
|||
@Autowired private CompanyRepository companyRepository; |
|||
@Autowired private JobRepository jobRepository; |
|||
@Autowired private JdbcTemplate jdbcTemplate; |
|||
|
|||
@MockBean private RedisTemplate<String, Object> redisTemplate; |
|||
|
|||
@BeforeEach |
|||
void clearData() { |
|||
progressRepository.deleteAll(); |
|||
knowledgeRepository.deleteAll(); |
|||
dimensionScoreRepository.deleteAll(); |
|||
reviewRepository.deleteAll(); |
|||
jobRepository.deleteAll(); |
|||
checklistItemRepository.deleteAll(); |
|||
companyRepository.deleteAll(); |
|||
userRepository.deleteAll(); |
|||
} |
|||
|
|||
@Test |
|||
void effectiveChecklistReturnsActiveStandardAndCompanyItemsInStableOrder() { |
|||
Long companyAId = createCompany("清单企业A"); |
|||
Long companyBId = createCompany("清单企业B"); |
|||
|
|||
OnboardingChecklistItem standardActive = saveChecklistItem( |
|||
"STANDARD", 0L, null, "standard-visible", "通用可见项", 100, true, null, null); |
|||
saveChecklistItem("STANDARD", 0L, null, "standard-inactive", "通用停用项", 1, false, null, null); |
|||
OnboardingChecklistItem companyFirst = saveChecklistItem( |
|||
"COMPANY", companyAId, companyAId, "company-a-first", "企业A首项", 50, true, |
|||
"company-a-first-key", "a".repeat(64)); |
|||
OnboardingChecklistItem companySecond = saveChecklistItem( |
|||
"COMPANY", companyAId, companyAId, "company-a-second", "企业A次项", 50, true, |
|||
"company-a-second-key", "b".repeat(64)); |
|||
saveChecklistItem("COMPANY", companyAId, companyAId, "company-a-inactive", "企业A停用项", |
|||
40, false, null, null); |
|||
saveChecklistItem("COMPANY", companyBId, companyBId, "company-b-visible", "企业B可见项", |
|||
10, true, "company-b-key", "c".repeat(64)); |
|||
|
|||
List<OnboardingChecklistItem> result = checklistItemRepository.findEffectiveChecklistItems(companyAId); |
|||
|
|||
assertThat(result) |
|||
.extracting(OnboardingChecklistItem::getId) |
|||
.containsExactly(companyFirst.getId(), companySecond.getId(), standardActive.getId()); |
|||
assertThat(result) |
|||
.extracting(OnboardingChecklistItem::getItemKey) |
|||
.containsExactly("company-a-first", "company-a-second", "standard-visible"); |
|||
} |
|||
|
|||
@Test |
|||
void requestHashIsMappedAsChar64() { |
|||
Long companyId = createCompany("哈希企业"); |
|||
String hash = "0123456789abcdef".repeat(4); |
|||
OnboardingChecklistItem saved = saveChecklistItem( |
|||
"COMPANY", companyId, companyId, "company-hash", "哈希映射项", 200, true, |
|||
"company-hash-key", hash); |
|||
checklistItemRepository.flush(); |
|||
|
|||
assertThat(checklistItemRepository.findById(saved.getId())) |
|||
.get() |
|||
.extracting(OnboardingChecklistItem::getRequestHash) |
|||
.isEqualTo(hash); |
|||
|
|||
String typeName = jdbcTemplate.queryForObject( |
|||
"SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS " + |
|||
"WHERE UPPER(TABLE_NAME) = 'ONBOARDING_CHECKLIST_ITEMS' " + |
|||
"AND UPPER(COLUMN_NAME) = 'REQUEST_HASH'", |
|||
String.class); |
|||
Integer columnSize = jdbcTemplate.queryForObject( |
|||
"SELECT CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.COLUMNS " + |
|||
"WHERE UPPER(TABLE_NAME) = 'ONBOARDING_CHECKLIST_ITEMS' " + |
|||
"AND UPPER(COLUMN_NAME) = 'REQUEST_HASH'", |
|||
Integer.class); |
|||
|
|||
assertThat(typeName).isNotNull().startsWith("CHAR").doesNotContain("VARCHAR"); |
|||
assertThat(columnSize).isEqualTo(64); |
|||
} |
|||
|
|||
@Test |
|||
void progressBatchAndUniqueLookupsAreScopedToStudentAndItem() { |
|||
Long studentOne = createUser(User.Role.STUDENT, "进度学生A"); |
|||
Long studentTwo = createUser(User.Role.STUDENT, "进度学生B"); |
|||
OnboardingChecklistItem firstItem = saveChecklistItem( |
|||
"STANDARD", 0L, null, "progress-item-1", "进度项1", 10, true, null, null); |
|||
OnboardingChecklistItem secondItem = saveChecklistItem( |
|||
"STANDARD", 0L, null, "progress-item-2", "进度项2", 20, true, null, null); |
|||
OnboardingChecklistItem thirdItem = saveChecklistItem( |
|||
"STANDARD", 0L, null, "progress-item-3", "进度项3", 30, true, null, null); |
|||
|
|||
LocalDateTime completedAt = LocalDateTime.of(2026, 9, 16, 8, 0); |
|||
progressRepository.save(StudentOnboardingProgress.builder() |
|||
.studentUserId(studentOne).itemId(firstItem.getId()).completed(true) |
|||
.completedAt(completedAt).build()); |
|||
progressRepository.save(StudentOnboardingProgress.builder() |
|||
.studentUserId(studentOne).itemId(secondItem.getId()).completed(false) |
|||
.completedAt(null).build()); |
|||
progressRepository.save(StudentOnboardingProgress.builder() |
|||
.studentUserId(studentTwo).itemId(firstItem.getId()).completed(true) |
|||
.completedAt(LocalDateTime.of(2026, 9, 16, 9, 0)).build()); |
|||
progressRepository.flush(); |
|||
|
|||
List<StudentOnboardingProgress> batch = progressRepository.findByStudentUserIdAndItemIdIn( |
|||
studentOne, List.of(firstItem.getId(), secondItem.getId(), thirdItem.getId())); |
|||
assertThat(batch) |
|||
.extracting(StudentOnboardingProgress::getItemId) |
|||
.containsExactlyInAnyOrder(firstItem.getId(), secondItem.getId()); |
|||
assertThat(batch) |
|||
.filteredOn(progress -> progress.getItemId().equals(firstItem.getId())) |
|||
.singleElement() |
|||
.extracting(StudentOnboardingProgress::getCompletedAt) |
|||
.isEqualTo(completedAt); |
|||
|
|||
Optional<StudentOnboardingProgress> unique = progressRepository.findByStudentUserIdAndItemId( |
|||
studentOne, firstItem.getId()); |
|||
assertThat(unique).isPresent(); |
|||
assertThat(unique.orElseThrow().getCompleted()).isTrue(); |
|||
assertThat(progressRepository.findByStudentUserIdAndItemId(studentOne, thirdItem.getId())).isEmpty(); |
|||
assertThat(progressRepository.findByStudentUserIdAndItemId(studentTwo, secondItem.getId())).isEmpty(); |
|||
} |
|||
|
|||
@Test |
|||
void companyKnowledgeLookupIsScopedToCompany() { |
|||
Long companyId = createCompany("知识企业"); |
|||
Long otherCompanyId = createCompany("其他知识企业"); |
|||
knowledgeRepository.save(CompanyKnowledge.builder() |
|||
.companyId(companyId) |
|||
.culture("重视透明沟通") |
|||
.workPolicies("弹性工作制") |
|||
.onboardingGuide("前台报到") |
|||
.build()); |
|||
knowledgeRepository.flush(); |
|||
|
|||
Optional<CompanyKnowledge> found = knowledgeRepository.findByCompanyId(companyId); |
|||
assertThat(found).isPresent(); |
|||
assertThat(found.orElseThrow().getCulture()).isEqualTo("重视透明沟通"); |
|||
assertThat(found.orElseThrow().getWorkPolicies()).isEqualTo("弹性工作制"); |
|||
assertThat(found.orElseThrow().getOnboardingGuide()).isEqualTo("前台报到"); |
|||
assertThat(knowledgeRepository.findByCompanyId(otherCompanyId)).isEmpty(); |
|||
} |
|||
|
|||
@Test |
|||
void companyReviewAggregatesIncludeOnlyPublishedApprovedReviews() { |
|||
Long companyId = createCompany("评价企业A"); |
|||
Long otherCompanyId = createCompany("评价企业B"); |
|||
Long studentOne = createUser(User.Role.STUDENT, "评价学生A"); |
|||
Long studentTwo = createUser(User.Role.STUDENT, "评价学生B"); |
|||
Long studentThree = createUser(User.Role.STUDENT, "评价学生C"); |
|||
Long hiddenStudent = createUser(User.Role.STUDENT, "评价学生D"); |
|||
Long otherStudent = createUser(User.Role.STUDENT, "评价学生E"); |
|||
|
|||
Long firstJobId = createJob(companyId, "评价岗位A1"); |
|||
Long secondJobId = createJob(companyId, "评价岗位A2"); |
|||
Long otherJobId = createJob(otherCompanyId, "评价岗位B"); |
|||
|
|||
JobReview five = saveReview(firstJobId, studentOne, 5, "[\"成长空间\",\"团队氛围\"]", |
|||
"PUBLISHED", JobReview.MODERATION_APPROVED); |
|||
JobReview four = saveReview(secondJobId, studentTwo, 4, "[\"成长空间\"]", |
|||
"PUBLISHED", JobReview.MODERATION_AUTO_APPROVED); |
|||
JobReview three = saveReview(firstJobId, studentThree, 3, "[\"团队氛围\"]", |
|||
"PUBLISHED", JobReview.MODERATION_APPROVED); |
|||
JobReview hidden = saveReview(firstJobId, hiddenStudent, 1, "[\"薪资透明\"]", |
|||
"PUBLISHED", JobReview.MODERATION_PENDING); |
|||
saveReview(otherJobId, otherStudent, 2, "[\"其他企业标签\"]", |
|||
"PUBLISHED", JobReview.MODERATION_APPROVED); |
|||
|
|||
dimensionScoreRepository.saveAll(List.of( |
|||
dimensionScore(five.getId(), "TEAM_ATMOSPHERE", 5), |
|||
dimensionScore(five.getId(), "GROWTH", 5), |
|||
dimensionScore(four.getId(), "TEAM_ATMOSPHERE", 4), |
|||
dimensionScore(four.getId(), "GROWTH", 4), |
|||
dimensionScore(three.getId(), "TEAM_ATMOSPHERE", 3), |
|||
dimensionScore(hidden.getId(), "TEAM_ATMOSPHERE", 1) |
|||
)); |
|||
dimensionScoreRepository.flush(); |
|||
|
|||
List<Object[]> summary = reviewRepository.countAndAveragePublishedRatingByCompanyId(companyId); |
|||
assertThat(summary).hasSize(1); |
|||
assertThat(((Number) summary.get(0)[0]).longValue()).isEqualTo(3); |
|||
assertThat(((Number) summary.get(0)[1]).doubleValue()).isEqualTo(4.0); |
|||
|
|||
List<Object[]> distribution = reviewRepository.findPublishedRatingDistributionByCompanyId(companyId); |
|||
assertThat(distribution).hasSize(3); |
|||
assertThat(((Number) distribution.get(0)[0]).intValue()).isEqualTo(3); |
|||
assertThat(((Number) distribution.get(0)[1]).longValue()).isEqualTo(1); |
|||
assertThat(((Number) distribution.get(1)[0]).intValue()).isEqualTo(4); |
|||
assertThat(((Number) distribution.get(1)[1]).longValue()).isEqualTo(1); |
|||
assertThat(((Number) distribution.get(2)[0]).intValue()).isEqualTo(5); |
|||
assertThat(((Number) distribution.get(2)[1]).longValue()).isEqualTo(1); |
|||
|
|||
List<String> tags = reviewRepository.findPublishedTagsByCompanyId(companyId); |
|||
assertThat(tags).hasSize(3); |
|||
assertThat(tags).allSatisfy(tag -> |
|||
assertThat(tag).doesNotContain("薪资透明", "其他企业标签")); |
|||
assertThat(tags).anySatisfy(tag -> |
|||
assertThat(tag).contains("成长空间").contains("团队氛围")); |
|||
assertThat(tags).anySatisfy(tag -> |
|||
assertThat(tag).contains("成长空间").doesNotContain("团队氛围")); |
|||
assertThat(tags).anySatisfy(tag -> |
|||
assertThat(tag).contains("团队氛围").doesNotContain("成长空间")); |
|||
|
|||
List<Object[]> dimensions = dimensionScoreRepository.aggregatePublishedDimensionsByCompanyId(companyId); |
|||
assertThat(dimensions).hasSize(2); |
|||
assertThat(dimensions.get(0)[0]).isEqualTo("GROWTH"); |
|||
assertThat(((Number) dimensions.get(0)[1]).doubleValue()).isEqualTo(4.5); |
|||
assertThat(((Number) dimensions.get(0)[2]).longValue()).isEqualTo(2); |
|||
assertThat(dimensions.get(1)[0]).isEqualTo("TEAM_ATMOSPHERE"); |
|||
assertThat(((Number) dimensions.get(1)[1]).doubleValue()).isEqualTo(4.0); |
|||
assertThat(((Number) dimensions.get(1)[2]).longValue()).isEqualTo(3); |
|||
} |
|||
|
|||
private Long createCompany(String name) { |
|||
Long ownerId = createUser(User.Role.COMPANY, name + "负责人"); |
|||
return companyRepository.save(Company.builder() |
|||
.ownerUserId(ownerId) |
|||
.name(name) |
|||
.verified(true) |
|||
.build()).getId(); |
|||
} |
|||
|
|||
private Long createUser(User.Role role, String nickname) { |
|||
User user = userRepository.save(User.builder() |
|||
.phone("175" + String.format("%08d", PHONE_SEQ.incrementAndGet())) |
|||
.nickname(nickname) |
|||
.role(role) |
|||
.roles(Set.of(role)) |
|||
.active(true) |
|||
.verified(true) |
|||
.build()); |
|||
return user.getId(); |
|||
} |
|||
|
|||
private Long createJob(Long companyId, String title) { |
|||
return jobRepository.save(Job.builder() |
|||
.companyId(companyId) |
|||
.title(title) |
|||
.description("岗位描述") |
|||
.city("上海") |
|||
.employmentType("FULL_TIME") |
|||
.status("PUBLISHED") |
|||
.build()).getId(); |
|||
} |
|||
|
|||
private OnboardingChecklistItem saveChecklistItem( |
|||
String scopeType, |
|||
Long scopeId, |
|||
Long companyId, |
|||
String itemKey, |
|||
String title, |
|||
int sortOrder, |
|||
boolean active, |
|||
String idempotencyKey, |
|||
String requestHash) { |
|||
return checklistItemRepository.save(OnboardingChecklistItem.builder() |
|||
.scopeType(scopeType) |
|||
.scopeId(scopeId) |
|||
.companyId(companyId) |
|||
.itemKey(itemKey) |
|||
.title(title) |
|||
.description(title + "说明") |
|||
.category("OTHER") |
|||
.sortOrder(sortOrder) |
|||
.required(false) |
|||
.active(active) |
|||
.idempotencyKey(idempotencyKey) |
|||
.requestHash(requestHash) |
|||
.build()); |
|||
} |
|||
|
|||
private JobReview saveReview( |
|||
Long jobId, |
|||
Long studentUserId, |
|||
int rating, |
|||
String tags, |
|||
String status, |
|||
String moderationStatus) { |
|||
return reviewRepository.save(JobReview.builder() |
|||
.jobId(jobId) |
|||
.studentUserId(studentUserId) |
|||
.rating(rating) |
|||
.content("评价内容") |
|||
.tags(tags) |
|||
.status(status) |
|||
.moderationStatus(moderationStatus) |
|||
.build()); |
|||
} |
|||
|
|||
private JobReviewDimensionScore dimensionScore(Long reviewId, String dimensionCode, int score) { |
|||
return JobReviewDimensionScore.builder() |
|||
.reviewId(reviewId) |
|||
.dimensionCode(dimensionCode) |
|||
.score(score) |
|||
.build(); |
|||
} |
|||
|
|||
public static class OnboardingSchemaFilterProvider implements SchemaFilterProvider { |
|||
|
|||
private static final Set<String> VALIDATED_TABLES = Set.of( |
|||
"onboarding_checklist_items", |
|||
"student_onboarding_progress", |
|||
"company_knowledge" |
|||
); |
|||
|
|||
@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