项目介绍
校园招聘信息平台是一个面向高校学生和企业的双向服务平台。该系统帮助企业发布招聘信息,方便学生查询职位并投递简历,同时为学校就业部门提供就业数据分析功能。
技术栈
后端
- Spring Boot 2.x
- Spring Security
- MyBatis Plus
- MySQL 8.0
- Redis
- RabbitMQ
前端
- Vue.js 2.x
- Element UI
- Axios
- Echarts
核心功能
1. 用户模块
- 学生注册/登录
- 企业注册/登录
- 管理员登录
- 基于JWT的认证授权
2. 企业模块
- 企业信息管理
- 职位发布/管理
- 简历筛选
- 面试安排
3. 学生模块
- 个人信息维护
- 简历制作
- 职位搜索/投递
- 投递状态跟踪
4. 数据分析模块
- 就业率统计
- 薪资分布分析
- 行业/地区分布
- 就业趋势分析
系统架构
├── recruitment-system
│ ├── recruitment-common -- 公共模块
│ ├── recruitment-admin -- 管理后台
│ ├── recruitment-api -- 接口服务
│ ├── recruitment-service -- 业务逻辑
│ ├── recruitment-dao -- 数据访问
│ └── recruitment-web -- 前端页面
数据库设计
主要包含以下核心表:
-- 用户表
CREATE TABLE user (
id BIGINT PRIMARY KEY,
username VARCHAR(50),
password VARCHAR(100),
role VARCHAR(20),
create_time DATETIME
);
-- 企业表
CREATE TABLE company (
id BIGINT PRIMARY KEY,
name VARCHAR(100),
industry VARCHAR(50),
scale VARCHAR(50),
description TEXT
);
-- 职位表
CREATE TABLE position (
id BIGINT PRIMARY KEY,
company_id BIGINT,
title VARCHAR(100),
salary VARCHAR(50),
education VARCHAR(20),
description TEXT
);
-- 简历表
CREATE TABLE resume (
id BIGINT PRIMARY KEY,
student_id BIGINT,
education TEXT,
experience TEXT,
skills TEXT
);
项目亮点
1. 分布式Session
使用Redis实现分布式Session,支持集群部署
2. 消息队列
使用RabbitMQ处理异步任务:
- 简历投递通知
- 面试通知
- 系统消息推送
3. 全文检索
整合Elasticsearch实现职位搜索功能:
- 多条件组合查询
- 结果高亮显示
- 智能推荐
4. 性能优化
- Redis缓存热点数据
- MyBatis SQL优化
- 前端资源压缩
- 图片CDN加速
项目总结
通过这个项目的开发,不仅巩固了Java全栈技术,还学习了:
- 大型项目的架构设计
- 分布式系统开发经验
- 性能优化最佳实践
- 项目开发完整流程
后续优化方向
- 引入Spring Cloud实现微服务架构
- 添加大数据分析功能
- 开发移动端应用
- 优化系统性能和用户体验
Java全栈项目 - 校园招聘信息平台(一):用户模块与企业模块详解
一、用户模块详细设计
1. 数据库设计
-- 用户基础信息表
CREATE TABLE sys_user (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL COMMENT '用户名',
password VARCHAR(100) NOT NULL COMMENT '密码',
role VARCHAR(20) NOT NULL COMMENT '角色:STUDENT/COMPANY/ADMIN',
email VARCHAR(100) COMMENT '邮箱',
phone VARCHAR(20) COMMENT '手机号',
status TINYINT DEFAULT 1 COMMENT '状态:0禁用,1启用',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 学生信息表
CREATE TABLE student_info (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL COMMENT '关联用户ID',
real_name VARCHAR(50) COMMENT '真实姓名',
gender TINYINT COMMENT '性别:0女,1男',
birthday DATE COMMENT '出生日期',
school VARCHAR(100) COMMENT '学校',
major VARCHAR(100) COMMENT '专业',
education VARCHAR(20) COMMENT '学历',
graduation_year INT COMMENT '毕业年份'
);
-- 企业信息表
CREATE TABLE company_info (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL COMMENT '关联用户ID',
company_name VARCHAR(100) NOT NULL COMMENT '企业名称',
industry VARCHAR(50) COMMENT '所属行业',
scale VARCHAR(50) COMMENT '企业规模',
location VARCHAR(200) COMMENT '企业地址',
website VARCHAR(200) COMMENT '企业官网',
license_no VARCHAR(100) COMMENT '营业执照号',
description TEXT COMMENT '企业简介',
verify_status TINYINT DEFAULT 0 COMMENT '认证状态:0未认证,1已认证'
);
2. 核心功能实现
2.1 注册功能
@Service
public class UserServiceImpl implements UserService {
@Autowired
private PasswordEncoder passwordEncoder;
@Transactional
public void registerStudent(StudentRegisterDTO dto) {
// 1. 参数校验
validateRegisterParams(dto);
// 2. 检查用户名是否存在
checkUsernameExist(dto.getUsername());
// 3. 保存用户基本信息
SysUser user = new SysUser();
user.setUsername(dto.getUsername());
user.setPassword(passwordEncoder.encode(dto.getPassword()));
user.setRole("STUDENT");
userMapper.insert(user);
// 4. 保存学生详细信息
StudentInfo studentInfo = new StudentInfo();
BeanUtils.copyProperties(dto, studentInfo);
studentInfo.setUserId(user.getId());
studentInfoMapper.insert(studentInfo);
// 5. 发送注册成功邮件
sendRegisterEmail(dto.getEmail());
}
@Transactional
public void registerCompany(CompanyRegisterDTO dto) {
// 类似学生注册流程
// 额外增加企业认证材料上传
// ...
}
}
2.2 登录认证
@Service
public class AuthServiceImpl implements AuthService {
@Autowired
private JwtTokenProvider tokenProvider;
public LoginVO login(LoginDTO dto) {
// 1. 认证用户
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(dto.getUsername(), dto.getPassword())
);
// 2. 生成JWT Token
String token = tokenProvider.generateToken(authentication);
// 3. 获取用户信息
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
// 4. 返回登录信息
return LoginVO.builder()
.token(token)
.userInfo(buildUserInfo(userDetails))
.build();
}
}
2.3 JWT认证授权
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) {
try {
String jwt = getJwtFromRequest(request);
if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
Long userId = tokenProvider.getUserIdFromJWT(jwt);
UserDetails userDetails = userService.loadUserById(userId);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null,
userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception ex) {
logger.error("JWT认证失败", ex);
}
chain.doFilter(request, response);
}
}
二、企业模块详细设计
1. 数据库设计
-- 职位表
CREATE TABLE position (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
company_id BIGINT NOT NULL COMMENT '企业ID',
title VARCHAR(100) NOT NULL COMMENT '职位名称',
department VARCHAR(50) COMMENT '所属部门',
salary_min INT COMMENT '最低薪资',
salary_max INT COMMENT '最高薪资',
education VARCHAR(20) COMMENT '学历要求',
experience VARCHAR(50) COMMENT '经验要求',
description TEXT COMMENT '职位描述',
requirement TEXT COMMENT '任职要求',
status TINYINT DEFAULT 1 COMMENT '状态:0下线,1上线',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 简历投递表
CREATE TABLE resume_delivery (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
position_id BIGINT NOT NULL COMMENT '职位ID',
student_id BIGINT NOT NULL COMMENT '学生ID',
resume_id BIGINT NOT NULL COMMENT '简历ID',
status VARCHAR(20) DEFAULT 'PENDING' COMMENT '状态:PENDING/PASSED/REJECTED',
hr_notes TEXT COMMENT 'HR备注',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 面试安排表
CREATE TABLE interview (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
delivery_id BIGINT NOT NULL COMMENT '投递ID',
interview_time DATETIME NOT NULL COMMENT '面试时间',
location VARCHAR(200) COMMENT '面试地点',
type VARCHAR(20) COMMENT '面试方式:ONLINE/OFFLINE',
round INT COMMENT '面试轮次',
status VARCHAR(20) COMMENT '状态:PENDING/COMPLETED/CANCELED',
feedback TEXT COMMENT '面试反馈',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
2. 核心功能实现
2.1 企业信息管理
@Service
public class CompanyServiceImpl implements CompanyService {
@Transactional
public void updateCompanyInfo(CompanyUpdateDTO dto) {
// 1. 验证操作权限
checkCompanyPermission(dto.getCompanyId());
// 2. 更新基本信息
CompanyInfo companyInfo = new CompanyInfo();
BeanUtils.copyProperties(dto, companyInfo);
companyInfoMapper.updateById(companyInfo);
// 3. 处理企业Logo上传
if (dto.getLogoFile() != null) {
String logoUrl = fileService.uploadImage(dto.getLogoFile());
companyInfoMapper.updateLogo(dto.getCompanyId(), logoUrl);
}
}
public CompanyDetailVO getCompanyDetail(Long companyId) {
// 1. 获取企业基本信息
CompanyInfo info = companyInfoMapper.selectById(companyId);
// 2. 获取在招职位数
Integer positionCount = positionMapper.countByCompanyId(companyId);
// 3. 构建返回数据
return CompanyDetailVO.builder()
.basicInfo(info)
.positionCount(positionCount)
.build();
}
}
2.2 职位管理
@Service
public class PositionServiceImpl implements PositionService {
@Transactional
public void publishPosition(PositionPublishDTO dto) {
// 1. 验证企业权限
checkCompanyPermission(dto.getCompanyId());
// 2. 保存职位信息
Position position = new Position();
BeanUtils.copyProperties(dto, position);
positionMapper.insert(position);
// 3. 发送职位消息到消息队列
sendPositionMessage(position);
// 4. 更新搜索索引
updateSearchIndex(position);
}
public PageResult<PositionVO> queryPositions(PositionQueryDTO query) {
// 1. 构建查询条件
LambdaQueryWrapper<Position> wrapper = new QueryWrapper<Position>()
.lambda()
.eq(query.getCompanyId() != null, Position::getCompanyId, query.getCompanyId())
.like(StringUtils.isNotBlank(query.getKeyword()), Position::getTitle, query.getKeyword())
.eq(Position::getStatus, 1);
// 2. 分页查询
Page<Position> page = positionMapper.selectPage(
new Page<>(query.getPageNum(), query.getPageSize()),
wrapper
);
// 3. 转换返回结果
return PageResult.build(page, this::convertToVO);
}
}
2.3 简历筛选
@Service
public class ResumeDeliveryServiceImpl implements ResumeDeliveryService {
public PageResult<DeliveryVO> queryDeliveryList(DeliveryQueryDTO query) {
// 1. 分页查询投递记录
Page<ResumeDelivery> page = deliveryMapper.queryDeliveryList(
new Page<>(query.getPageNum(), query.getPageSize()),
query
);
// 2. 批量查询简历信息
List<Long> resumeIds = page.getRecords().stream()
.map(ResumeDelivery::getResumeId)
.collect(Collectors.toList());
Map<Long, ResumeVO> resumeMap = resumeService.batchGetResumeVO(resumeIds);
// 3. 构建返回数据
return PageResult.build(page, delivery -> {
DeliveryVO vo = new DeliveryVO();
BeanUtils.copyProperties(delivery, vo);
vo.setResume(resumeMap.get(delivery.getResumeId()));
return vo;
});
}
@Transactional
public void updateDeliveryStatus(DeliveryStatusDTO dto) {
// 1. 更新投递状态
ResumeDelivery delivery = new ResumeDelivery();
delivery.setId(dto.getDeliveryId());
delivery.setStatus(dto.getStatus());
delivery.setHrNotes(dto.getNotes());
deliveryMapper.updateById(delivery);
// 2. 发送状态变更通知
messageService.sendDeliveryStatusMessage(dto);
}
}
2.4 面试安排
@Service
public class InterviewServiceImpl implements InterviewService {
@Transactional
public void arrangeInterview(InterviewArrangeDTO dto) {
// 1. 创建面试记录
Interview interview = new Interview();
BeanUtils.copyProperties(dto, interview);
interviewMapper.insert(interview);
// 2. 更新投递状态
deliveryService.updateStatus(dto.getDeliveryId(), "INTERVIEWING");
// 3. 发送面试通知
ResumeDelivery delivery = deliveryMapper.selectById(dto.getDeliveryId());
sendInterviewNotification(delivery, interview);
}
private void sendInterviewNotification(ResumeDelivery delivery, Interview interview) {
// 1. 发送邮件通知
EmailMessage emailMessage = buildInterviewEmailMessage(delivery, interview);
messageService.sendEmail(emailMessage);
// 2. 发送短信通知
SmsMessage smsMessage = buildInterviewSmsMessage(delivery, interview);
messageService.sendSms(smsMessage);
// 3. 发送系统站内信
SystemMessage systemMessage = buildInterviewSystemMessage(delivery, interview);
messageService.sendSystemMessage(systemMessage);
}
}
3. 接口文档示例
/api/company/info:
put:
summary: 更新企业信息
parameters:
- name: dto
in: body
required: true
schema:
type: object
properties:
companyId:
type: integer
description: 企业ID
companyName:
type: string
description: 企业名称
industry:
type: string
description: 所属行业
responses:
200:
description: 更新成功
/api/position/publish:
post:
summary: 发布职位
parameters:
- name: dto
in: body
required: true
schema:
type: object
properties:
companyId:
type: integer
title:
type: string
salary:
type: string
responses:
200:
description: 发布成功
以上是用户模块和企业模块的详细设计。包含了:
- 完整的数据库表设计
- 核心业务逻辑实现
- 关键功能的代码示例
- 接口文档规范
这些模块的实现考虑了:
- 数据安全性(密码加密、JWT认证)
- 业务完整性(事务管理)
- 异步处理(消息队列)
- 通知提醒(邮件、短信、站内信)
Java全栈项目 - 校园招聘信息平台(二):学生模块与数据分析模块详解
一、学生模块详细设计
1. 数据库设计
-- 简历表
CREATE TABLE resume (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
student_id BIGINT NOT NULL COMMENT '学生ID',
title VARCHAR(100) COMMENT '简历标题',
avatar_url VARCHAR(200) COMMENT '头像URL',
expect_salary VARCHAR(50) COMMENT '期望薪资',
expect_city VARCHAR(50) COMMENT '期望城市',
expect_position VARCHAR(100) COMMENT '期望职位',
self_evaluation TEXT COMMENT '自我评价',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 教育经历表
CREATE TABLE resume_education (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
resume_id BIGINT NOT NULL COMMENT '简历ID',
school VARCHAR(100) NOT NULL COMMENT '学校名称',
major VARCHAR(100) COMMENT '专业',
degree VARCHAR(50) COMMENT '学历',
start_date DATE COMMENT '开始时间',
end_date DATE COMMENT '结束时间',
gpa VARCHAR(20) COMMENT '学分绩点',
description TEXT COMMENT '在校经历'
);
-- 项目经历表
CREATE TABLE resume_project (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
resume_id BIGINT NOT NULL,
name VARCHAR(100) NOT NULL COMMENT '项目名称',
role VARCHAR(50) COMMENT '担任角色',
start_date DATE COMMENT '开始时间',
end_date DATE COMMENT '结束时间',
description TEXT COMMENT '项目描述',
achievement TEXT COMMENT '项目成就'
);
-- 求职进度表
CREATE TABLE job_progress (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
student_id BIGINT NOT NULL,
position_id BIGINT NOT NULL,
company_id BIGINT NOT NULL,
status VARCHAR(20) COMMENT '状态:待处理/已查看/面试/通过/拒绝',
latest_time DATETIME COMMENT '最新进展时间',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
2. 核心功能实现
2.1 简历管理
@Service
public class ResumeServiceImpl implements ResumeService {
@Transactional
public Long createResume(ResumeCreateDTO dto) {
// 1. 保存基本信息
Resume resume = new Resume();
BeanUtils.copyProperties(dto, resume);
resumeMapper.insert(resume);
// 2. 保存教育经历
saveEducationList(resume.getId(), dto.getEducationList());
// 3. 保存项目经历
saveProjectList(resume.getId(), dto.getProjectList());
// 4. 处理头像上传
if (dto.getAvatarFile() != null) {
String avatarUrl = fileService.uploadImage(dto.getAvatarFile());
resumeMapper.updateAvatar(resume.getId(), avatarUrl);
}
return resume.getId();
}
public ResumeDetailVO getResumeDetail(Long resumeId) {
// 1. 获取基本信息
Resume resume = resumeMapper.selectById(resumeId);
// 2. 获取教育经历
List<ResumeEducation> educationList = educationMapper.selectByResumeId(resumeId);
// 3. 获取项目经历
List<ResumeProject> projectList = projectMapper.selectByResumeId(resumeId);
// 4. 构建返回数据
return ResumeDetailVO.builder()
.basicInfo(resume)
.educationList(educationList)
.projectList(projectList)
.build();
}
}
2.2 职位搜索
@Service
public class PositionSearchServiceImpl implements PositionSearchService {
@Autowired
private ElasticsearchRestTemplate esTemplate;
public PageResult<PositionVO> searchPositions(PositionSearchDTO dto) {
// 1. 构建搜索条件
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// 关键词搜索
if (StringUtils.isNotBlank(dto.getKeyword())) {
boolQuery.must(QueryBuilders.multiMatchQuery(dto.getKeyword())
.field("title", 3.0f)
.field("description")
.field("requirement"));
}
// 城市筛选
if (StringUtils.isNotBlank(dto.getCity())) {
boolQuery.filter(QueryBuilders.termQuery("city", dto.getCity()));
}
// 薪资范围
if (dto.getSalaryMin() != null && dto.getSalaryMax() != null) {
boolQuery.filter(QueryBuilders.rangeQuery("salaryMin")
.gte(dto.getSalaryMin())
.lte(dto.getSalaryMax()));
}
// 2. 执行搜索
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(boolQuery)
.withSort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC))
.withPageable(PageRequest.of(dto.getPageNum(), dto.getPageSize()))
.build();
SearchHits<PositionES> searchHits = esTemplate.search(searchQuery, PositionES.class);
// 3. 处理高亮显示
return PageResult.build(searchHits, this::convertToVO);
}
}
2.3 职位投递
@Service
public class JobApplicationServiceImpl implements JobApplicationService {
@Transactional
public void applyPosition(JobApplyDTO dto) {
// 1. 检查是否重复投递
checkDuplicateApply(dto.getStudentId(), dto.getPositionId());
// 2. 创建投递记录
ResumeDelivery delivery = new ResumeDelivery();
delivery.setStudentId(dto.getStudentId());
delivery.setPositionId(dto.getPositionId());
delivery.setResumeId(dto.getResumeId());
delivery.setStatus("PENDING");
deliveryMapper.insert(delivery);
// 3. 创建求职进度
JobProgress progress = new JobProgress();
progress.setStudentId(dto.getStudentId());
progress.setPositionId(dto.getPositionId());
progress.setCompanyId(dto.getCompanyId());
progress.setStatus("待处理");
progressMapper.insert(progress);
// 4. 发送投递通知
sendDeliveryNotification(delivery);
}
public List<JobProgressVO> getProgressList(Long studentId) {
// 1. 查询进度列表
List<JobProgress> progressList = progressMapper.selectByStudentId(studentId);
// 2. 批量查询职位和公司信息
Set<Long> positionIds = progressList.stream()
.map(JobProgress::getPositionId)
.collect(Collectors.toSet());
Map<Long, PositionVO> positionMap = positionService.batchGetPositionVO(positionIds);
// 3. 构建返回数据
return progressList.stream()
.map(progress -> {
JobProgressVO vo = new JobProgressVO();
BeanUtils.copyProperties(progress, vo);
vo.setPosition(positionMap.get(progress.getPositionId()));
return vo;
})
.collect(Collectors.toList());
}
}
二、数据分析模块详细设计
1. 数据库设计
-- 就业统计表
CREATE TABLE employment_stats (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
year INT NOT NULL COMMENT '统计年份',
month INT NOT NULL COMMENT '统计月份',
school_id BIGINT COMMENT '学校ID',
major_id BIGINT COMMENT '专业ID',
total_students INT COMMENT '毕业生总数',
employed_count INT COMMENT '已就业人数',
employment_rate DECIMAL(5,2) COMMENT '就业率',
avg_salary DECIMAL(10,2) COMMENT '平均薪资',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 行业分布表
CREATE TABLE industry_distribution (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
year INT NOT NULL,
industry VARCHAR(50) NOT NULL COMMENT '行业',
student_count INT COMMENT '就业人数',
salary_avg DECIMAL(10,2) COMMENT '平均薪资',
proportion DECIMAL(5,2) COMMENT '占比'
);
-- 地区分布表
CREATE TABLE region_distribution (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
year INT NOT NULL,
province VARCHAR(50) NOT NULL COMMENT '省份',
city VARCHAR(50) NOT NULL COMMENT '城市',
student_count INT COMMENT '就业人数',
salary_avg DECIMAL(10,2) COMMENT '平均薪资',
proportion DECIMAL(5,2) COMMENT '占比'
);
2. 核心功能实现
2.1 就业率统计
@Service
public class EmploymentStatsServiceImpl implements EmploymentStatsService {
public EmploymentOverviewVO calculateEmploymentStats(StatsQueryDTO query) {
// 1. 查询基础数据
List<EmploymentStats> statsList = statsMapper.queryEmploymentStats(query);
// 2. 计算总体就业率
BigDecimal totalRate = calculateTotalRate(statsList);
// 3. 计算同比/环比
Map<String, BigDecimal> compareData = calculateCompareData(query);
// 4. 构建趋势数据
List<TrendPointVO> trendData = buildTrendData(query);
return EmploymentOverviewVO.builder()
.employmentRate(totalRate)
.yearOnYear(compareData.get("yearOnYear"))
.monthOnMonth(compareData.get("monthOnMonth"))
.trendData(trendData)
.build();
}
private List<TrendPointVO> buildTrendData(StatsQueryDTO query) {
// 1. 获取时间范围
Date startDate = DateUtils.addMonths(new Date(), -12);
Date endDate = new Date();
// 2. 查询趋势数据
List<EmploymentStats> trendList = statsMapper.queryTrendData(
query.getSchoolId(),
startDate,
endDate
);
// 3. 按月份聚合数据
return trendList.stream()
.collect(Collectors.groupingBy(
stats -> stats.getYear() + "-" + stats.getMonth(),
Collectors.averagingDouble(EmploymentStats::getEmploymentRate)
))
.entrySet().stream()
.map(entry -> new TrendPointVO(entry.getKey(), entry.getValue()))
.collect(Collectors.toList());
}
}
2.2 薪资分析
@Service
public class SalaryAnalysisServiceImpl implements SalaryAnalysisService {
public SalaryAnalysisVO analyzeSalary(AnalysisQueryDTO query) {
// 1. 计算薪资区间分布
List<SalaryRangeVO> rangeList = calculateSalaryRange(query);
// 2. 计算专业薪资对比
List<MajorSalaryVO> majorList = compareMajorSalary(query);
// 3. 计算行业薪资水平
List<IndustrySalaryVO> industryList = calculateIndustrySalary(query);
return SalaryAnalysisVO.builder()
.rangeDistribution(rangeList)
.majorComparison(majorList)
.industryLevel(industryList)
.build();
}
private List<SalaryRangeVO> calculateSalaryRange(AnalysisQueryDTO query) {
// 1. 定义薪资区间
List<SalaryRange> ranges = Arrays.asList(
new SalaryRange(0, 5000),
new SalaryRange(5000, 10000),
new SalaryRange(10000, 15000),
new SalaryRange(15000, 20000),
new SalaryRange(20000, Integer.MAX_VALUE)
);
// 2. 统计各区间人数
List<Map<String, Object>> stats = deliveryMapper.statSalaryRange(
query.getYear(),
query.getSchoolId()
);
// 3. 计算占比
return calculateProportions(stats, ranges);
}
}
2.3 行业分布分析
@Service
public class IndustryAnalysisServiceImpl implements IndustryAnalysisService {
public IndustryAnalysisVO analyzeIndustryDistribution(AnalysisQueryDTO query) {
// 1. 查询行业分布数据
List<IndustryDistribution> distributionList =
distributionMapper.queryIndustryDistribution(query);
// 2. 计算TOP10行业
List<IndustryProportionVO> top10 = calculateTop10Industries(distributionList);
// 3. 计算行业发展趋势
List<IndustryTrendVO> trendList = calculateIndustryTrend(query);
return IndustryAnalysisVO.builder()
.industryTop10(top10)
.industryTrend(trendList)
.build();
}
@Cacheable(value = "industryTrend", key = "#query.year")
private List<IndustryTrendVO> calculateIndustryTrend(AnalysisQueryDTO query) {
// 1. 获取近5年数据
List<IndustryDistribution> historyData =
distributionMapper.queryHistoryDistribution(query.getYear() - 5, query.getYear());
// 2. 按行业分组
Map<String, List<IndustryDistribution>> industryMap =
historyData.stream().collect(Collectors.groupingBy(IndustryDistribution::getIndustry));
// 3. 计算增长率
return industryMap.entrySet().stream()
.map(entry -> {
String industry = entry.getKey();
List<IndustryDistribution> industryData = entry.getValue();
BigDecimal growthRate = calculateGrowthRate(industryData);
return new IndustryTrendVO(industry, growthRate);
})
.collect(Collectors.toList());
}
}
3. 数据可视化实现
// 使用ECharts实现可视化图表
const employmentChart = {
initEmploymentRate(data) {
const chart = echarts.init(document.getElementById('employment-rate'));
const option = {
title: {
text: '就业率趋势分析'
},
tooltip: {
trigger: 'axis'
},
xAxis: {
type: 'category',
data: data.map(item => item.month)
},
yAxis: {
type: 'value',
axisLabel: {
formatter: '{value}%'
}
},
series: [{
name: '就业率',
type: 'line',
data: data.map(item => item.rate),
markPoint: {
data: [
{type: 'max', name: '最大值'},
{type: 'min', name: '最小值'}
]
}
}]
};
chart.setOption(option);
},
initSalaryDistribution(data) {
const chart = echarts.init(document.getElementById('salary-dist'));
const option = {
title: {
text: '薪资分布'
},
tooltip: {
trigger: 'item',
formatter: '{b}: {c} ({d}%)'
},
series: [{
type: 'pie',
radius: ['50%', '70%'],
data: data.map(item => ({
name: item.range,
value: item.count
}))
}]
};
chart.setOption(option);
}
};
4. 数据导出功能
@Service
public class DataExportServiceImpl implements DataExportService {
public void exportEmploymentStats(ExportQueryDTO query, HttpServletResponse response) {
// 1. 查询数据
List<EmploymentStats> statsList = statsMapper.queryForExport(query);
// 2. 创建Excel工作簿
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("就业统计");
// 3. 创建表头
Row headerRow = sheet.createRow(0);
String[] headers = {"统计年月", "专业", "毕业人数", "就业人数", "就业率", "平均薪资"};
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
}
// 4. 填充数据
int rowNum = 1;
for (EmploymentStats stats : statsList) {
Row row = sheet.createRow(rowNum++);
row.createCell(0).setCellValue(stats.getYear() + "-" + stats.getMonth());
row.createCell(1).setCellValue(stats.getMajorName());
row.createCell(2).setCellValue(stats.getTotalStudents());
row.createCell(3).setCellValue(stats.getEmployedCount());
row.createCell(4).setCellValue(stats.getEmploymentRate() + "%");
row.createCell(5).setCellValue(stats.getAvgSalary());
}
// 5. 导出文件
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition", "attachment; filename=employment_stats.xlsx");
workbook.write(response.getOutputStream());
}
}
以上是学生模块和数据分析模块的详细设计。主要特点包括:
- 学生模块:
- 完整的简历管理功能
- 基于Elasticsearch的职位搜索
- 完善的投递跟踪机制
- 数据分析模块:
- 多维度的统计分析
- 可视化图表展示
- 数据导出功能
技术亮点:
- 使用Redis缓存热点数据
- 使用ECharts实现数据可视化
- 采用POI处理Excel导出
- 实现数据实时统计和离线分析