forked from baskbuddy/zhangshun
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
93 lines
4.6 KiB
93 lines
4.6 KiB
package com.mydreamjob.bidirectionalmatch;
|
|
|
|
import com.mydreamjob.application.Application;
|
|
import com.mydreamjob.application.ApplicationRepository;
|
|
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.JobService;
|
|
import com.mydreamjob.notification.NotificationService;
|
|
import com.mydreamjob.profile.StudentProfile;
|
|
import com.mydreamjob.profile.StudentProfileRepository;
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.transaction.annotation.Propagation;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
/**
|
|
* 匹配成功通知(Outbox 发送)。
|
|
*
|
|
* <p>由 {@code application_match_events} 中 MATCH_SUCCEEDED(PENDING) 事件驱动;
|
|
* 通知通过 NotificationService.create 幂等键落库(同一 application 每方只产生一条),
|
|
* 事件标记 SENT;任何失败保持 PENDING,由调度任务重试。重试不会重复发通知。
|
|
* <b>REQUIRES_NEW</b>:afterCommit/调度器调用时若处于已完成的同步上下文,REQUIRED 会加入已完成事务
|
|
* 导致通知不落库;独立事务保证通知总能提交。
|
|
*/
|
|
@Slf4j
|
|
@Service
|
|
@RequiredArgsConstructor
|
|
public class MatchNotificationService {
|
|
|
|
public static final String TYPE_MATCH_SUCCEEDED = "MATCH_SUCCEEDED";
|
|
|
|
private final ApplicationMatchEventRepository eventRepository;
|
|
private final ApplicationMatchRecordRepository recordRepository;
|
|
private final ApplicationRepository applicationRepository;
|
|
private final JobService jobService;
|
|
private final CompanyRepository companyRepository;
|
|
private final StudentProfileRepository profileRepository;
|
|
private final UserRepository userRepository;
|
|
private final NotificationService notificationService;
|
|
|
|
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
|
public void sendMatchSucceeded(Long applicationId) {
|
|
ApplicationMatchEvent event = eventRepository
|
|
.findByApplicationIdAndEventTypeAndDedupKey(applicationId,
|
|
ApplicationMatchEvent.EVENT_MATCH_SUCCEEDED,
|
|
ApplicationMatchEvent.EVENT_MATCH_SUCCEEDED)
|
|
.orElse(null);
|
|
if (event == null || ApplicationMatchEvent.NOTIFY_SENT.equals(event.getNotificationStatus())) {
|
|
return; // 无事件或已发送
|
|
}
|
|
ApplicationMatchRecord record = recordRepository.findByApplicationId(applicationId).orElse(null);
|
|
Application app = applicationRepository.findById(applicationId).orElse(null);
|
|
if (record == null || app == null) return;
|
|
|
|
Job job = jobService.getJob(record.getJobId());
|
|
Company company = companyRepository.findById(record.getCompanyId()).orElse(null);
|
|
String jobTitle = job.getTitle() != null ? job.getTitle() : "岗位";
|
|
String companyName = company != null && company.getName() != null ? company.getName() : "企业";
|
|
String candidateName = resolveCandidateName(record.getCandidateId());
|
|
|
|
// 求职者
|
|
notificationService.create(record.getCandidateId(), TYPE_MATCH_SUCCEEDED,
|
|
"双向匹配成功",
|
|
"你与「" + companyName + "」的岗位「" + jobTitle + "」已达成双向匹配,可与企业直聊",
|
|
"MATCH", applicationId, "match-succeeded-" + applicationId + "-candidate");
|
|
// 企业
|
|
if (company != null) {
|
|
notificationService.create(company.getOwnerUserId(), TYPE_MATCH_SUCCEEDED,
|
|
"双向匹配成功",
|
|
"候选人「" + candidateName + "」与你发布的岗位「" + jobTitle + "」已达成双向匹配,可与其直聊",
|
|
"MATCH", applicationId, "match-succeeded-" + applicationId + "-company");
|
|
}
|
|
event.setNotificationStatus(ApplicationMatchEvent.NOTIFY_SENT);
|
|
eventRepository.save(event);
|
|
log.info("MATCH_SUCCEEDED notification sent: applicationId={}", applicationId);
|
|
}
|
|
|
|
private String resolveCandidateName(Long candidateId) {
|
|
StudentProfile profile = profileRepository.findByUserId(candidateId).orElse(null);
|
|
if (profile != null && profile.getRealName() != null && !profile.getRealName().isBlank()) {
|
|
return profile.getRealName();
|
|
}
|
|
User user = userRepository.findById(candidateId).orElse(null);
|
|
if (user != null && user.getNickname() != null && !user.getNickname().isBlank()) {
|
|
return user.getNickname();
|
|
}
|
|
return "候选人";
|
|
}
|
|
}
|
|
|