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.
 
 
 

221 lines
9.3 KiB

package com.mydreamjob.company;
import com.mydreamjob.auth.User;
import com.mydreamjob.auth.UserRepository;
import com.mydreamjob.common.exception.BusinessException;
import com.mydreamjob.common.exception.ErrorCode;
import com.mydreamjob.company.dto.CompanyCreateRequest;
import com.mydreamjob.company.dto.CompanyPublicView;
import com.mydreamjob.company.dto.CompanyUpdateRequest;
import com.mydreamjob.job.JobService;
import com.mydreamjob.job.dto.CompanyManagedJobItem;
import com.mydreamjob.job.dto.JobSearchItem;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashMap;
import java.util.Map;
/**
* 企业管理 Service
* 角色校验优先——所有写操作在业务逻辑前先校验角色。
*/
@Slf4j
@Service
public class CompanyService {
private final com.mydreamjob.security.UserRoleGuard userRoleGuard;
private final CompanyRepository companyRepository;
private final UserRepository userRepository;
private final JobService jobService;
public CompanyService(CompanyRepository companyRepository, UserRepository userRepository,
JobService jobService,
com.mydreamjob.security.UserRoleGuard userRoleGuard) {
this.companyRepository = companyRepository;
this.userRepository = userRepository;
this.jobService = jobService;
this.userRoleGuard = userRoleGuard;
}
// ── 角色校验(最先执行)───────────────────────────────────
public void assertCompanyRole(Long userId) {
userRoleGuard.requireCompany(userId);
}
public void assertStudentRole(Long userId) {
userRoleGuard.requireStudent(userId);
}
// ── 业务方法 ──────────────────────────────────────────────
/**
* 查询当前用户绑定的企业资料(含角色校验:非 COMPANY 返回 403)
*/
public Company findCompanyByOwnerUserId(Long userId) {
assertCompanyRole(userId);
return companyRepository.findByOwnerUserId(userId).orElse(null);
}
/**
* 创建企业资料(仅 COMPANY 角色,已存在时 409)
*/
@Transactional
public Company createCompany(Long userId, CompanyCreateRequest req) {
assertCompanyRole(userId);
if (companyRepository.findByOwnerUserId(userId).isPresent()) {
throw new BusinessException(ErrorCode.COMPANY_ALREADY_EXISTS);
}
var industry = com.mydreamjob.industry.IndustryCatalog.selection(req.getIndustryCode(), req.getIndustry());
Company company = Company.builder()
.ownerUserId(userId)
.name(req.getName())
.logoUrl(req.getLogoUrl())
.description(req.getDescription())
.industry(industry.name()).industryCode(industry.code())
.companySize(normalizeCompanySize(req.getCompanySize()))
.website(req.getWebsite())
.address(req.getAddress())
.verified(false)
.build();
return companyRepository.save(company);
}
/**
* 更新企业资料(幂等:不传字段则保留原值)
*/
@Transactional
public Company updateCompany(Long userId, CompanyUpdateRequest req) {
assertCompanyRole(userId);
Company company = companyRepository.findByOwnerUserIdForUpdate(userId)
.orElseThrow(() -> new BusinessException(ErrorCode.COMPANY_NOT_FOUND));
var industry = com.mydreamjob.industry.IndustryCatalog.selection(req.getIndustryCode(), req.getIndustry());
if (req.getName() != null) company.setName(req.getName());
if (req.getLogoUrl() != null) company.setLogoUrl(req.getLogoUrl());
if (req.getDescription() != null) company.setDescription(req.getDescription());
if (req.getIndustry() != null || req.getIndustryCode() != null) {
company.setIndustry(industry.name());
company.setIndustryCode(industry.code());
}
// companySize:null/缺省保持原值;其余交给与创建共用的同一套归一化规则。
if (req.getCompanySize() != null) {
company.setCompanySize(normalizeCompanySize(req.getCompanySize()));
}
if (req.getWebsite() != null) company.setWebsite(req.getWebsite());
if (req.getAddress() != null) company.setAddress(req.getAddress());
return companyRepository.save(company);
}
/** 企业规模长度上限,与请求 DTO 的 @Size(max = 40) 保持一致。 */
private static final int MAX_COMPANY_SIZE_LENGTH = 40;
/**
* companySize 统一归一化——创建与更新**共用同一套规则**,避免两套逻辑漂移。
*
* <ul>
* <li>{@code null} / 缺省 → {@code null}(创建即存 SQL NULL;更新由调用方先判 null 以「保持原值」)</li>
* <li>空串或纯空白 → {@code null}(清空)</li>
* <li>非空 → 去掉两端空白后保存(如 {@code " 100-499人 "} → {@code "100-499人"})</li>
* <li>超过 40 字符 → 抛参数错误,**不截断**</li>
* </ul>
*/
private static String normalizeCompanySize(String value) {
if (value == null) {
return null;
}
String trimmed = value.trim();
if (trimmed.isEmpty()) {
return null;
}
if (trimmed.length() > MAX_COMPANY_SIZE_LENGTH) {
throw new BusinessException(ErrorCode.BAD_REQUEST);
}
return trimmed;
}
/**
* 将企业 Entity 转为 API 视图(排除敏感字段)
*/
public Map<String, Object> toView(Company company) {
if (company == null) return null;
Map<String, Object> view = new HashMap<>();
view.put("id", company.getId());
view.put("ownerUserId", company.getOwnerUserId());
view.put("name", company.getName());
view.put("logoUrl", company.getLogoUrl());
view.put("description", company.getDescription());
view.put("industry", company.getIndustry());
view.put("industryCode", company.getIndustryCode());
view.put("companySize", company.getCompanySize());
view.put("website", company.getWebsite());
view.put("address", company.getAddress());
view.put("verified", company.getVerified());
view.put("createdAt", company.getCreatedAt());
return view;
}
// ── 公开端:企业详情 ────────────────────────────────────────
/**
* 查询企业公开信息(匿名可访问)。
* 仅返回 6 个公开字段,不泄露 ownerUserId、联系方式、审核信息等。
*/
public CompanyPublicView getPublicCompany(Long companyId) {
Company company = companyRepository.findById(companyId)
.orElseThrow(() -> new BusinessException(ErrorCode.COMPANY_NOT_FOUND));
return toPublicView(company);
}
/**
* 将企业 Entity 转为公开视图(仅 6 个字段)。
* 不修改现有 toView() 方法。
*/
public CompanyPublicView toPublicView(Company company) {
return CompanyPublicView.builder()
.id(company.getId())
.name(company.getName())
.logoUrl(company.getLogoUrl())
.description(company.getDescription())
.industry(company.getIndustry())
.industryCode(company.getIndustryCode())
.companySize(company.getCompanySize())
.verified(company.getVerified())
.build();
}
// ── 公开端:企业岗位列表 ────────────────────────────────────
/**
* 查询企业公开岗位列表(匿名可访问)。
* 先确认企业存在,再复用已验收的 JobService.searchJobs。
*/
public Page<JobSearchItem> getCompanyJobs(Long companyId, String sort, int page, int size) {
companyRepository.findById(companyId)
.orElseThrow(() -> new BusinessException(ErrorCode.COMPANY_NOT_FOUND));
return jobService.searchJobs(
null, // keyword
null, // city
null, // employmentType
null, // salaryMin
null, // salaryMax
companyId, // companyId
sort, // sort
page, // page
size // size
);
}
/** 当前企业自身岗位管理列表;身份和企业归属均由服务端解析。 */
public Page<CompanyManagedJobItem> getMyJobs(Long userId, String status,
String keyword, String city,
int page, int size) {
Company company = findCompanyByOwnerUserId(userId);
if (company == null) throw new BusinessException(ErrorCode.COMPANY_NOT_FOUND);
return jobService.listCompanyJobs(company.getId(), status, keyword, city, page, size);
}
}