// 测试数据准备
async prepareTestData_RankDistribution() {
// 创建测试员工
const profiles = [
{ name: '张三', level: 'junior' },
{ name: '李四', level: 'junior' },
{ name: '王五', level: 'intermediate' },
{ name: '赵六', level: 'intermediate' },
{ name: '钱七', level: 'intermediate' },
{ name: '孙八', level: 'senior' }
];
for (const data of profiles) {
const Profile = Parse.Object.extend('Profile');
const profile = new Profile();
profile.set('name', data.name);
profile.set('isActivated', true);
profile.set('isDeleted', false);
profile.set('data', {
realname: data.name,
hrData: {
level: data.level,
employmentStatus: 'active'
}
});
await profile.save();
}
}
// 测试用例
it('职级分布统计应该正确', async () => {
const distribution = await this.loadRankDistribution();
expect(distribution).toEqual([
{ level: '初级', count: 2, percentage: '33.3' },
{ level: '中级', count: 3, percentage: '50.0' },
{ level: '高级', count: 1, percentage: '16.7' }
]);
});
// 测试数据准备
async prepareTestData_MonthlyTrend() {
const months = [
{ month: '2025-01', hired: 5, left: 2 },
{ month: '2025-02', hired: 3, left: 1 },
{ month: '2025-03', hired: 4, left: 3 }
];
for (const data of months) {
// 创建入职员工
for (let i = 0; i < data.hired; i++) {
const profile = new Parse.Object('Profile');
profile.set('createdAt', new Date(data.month + '-15'));
profile.set('name', `入职${i+1}`);
await profile.save();
}
// 创建离职记录
for (let i = 0; i < data.left; i++) {
const profile = new Parse.Object('Profile');
profile.set('data', {
hrData: {
resignationDate: new Date(data.month + '-20')
}
});
await profile.save();
}
}
}
// 测试用例
it('入离职趋势应该正确统计', async () => {
const trend = await this.loadMonthlyTrend();
expect(trend[0]).toEqual({
month: '2025-01',
hired: 5,
left: 2
});
});
it('应该能够上传简历并提取文本', async () => {
const testFile = new File(['测试简历内容'], 'resume.txt');
const result = await this.uploadAndAnalyzeResume(testFile, '设计师');
expect(result.resume).toBeDefined();
expect(result.resume.get('resumeText')).toBe('测试简历内容');
expect(result.resume.get('status')).toBe('analyzed');
});
it('AI应该能够正确分析简历各维度', async () => {
const resumeText = `
姓名:张三
学历:本科
专业:环境艺术设计
工作经验:5年室内设计经验
技能:熟练使用Photoshop、AutoCAD、3DMax
项目经历:
1. XX住宅项目 - 主设计师
2. XX商业空间 - 设计总监
`;
const analysisRequest = {
resumeText,
jobPosition: '设计师',
jobRequirements: [
'本科及以上学历',
'3年以上设计经验',
'熟练使用设计软件'
]
};
const result = await this.doubaoAi
.analyzeResume(analysisRequest)
.toPromise();
// 验证学历维度
const educationDim = result.matchDimensions.find(d =>
d.name.includes('学历')
);
expect(educationDim.score).toBeGreaterThan(70);
// 验证技能维度
const skillsDim = result.matchDimensions.find(d =>
d.name.includes('技能')
);
expect(skillsDim.score).toBeGreaterThan(60);
// 验证项目经验维度
const projectDim = result.matchDimensions.find(d =>
d.name.includes('项目')
);
expect(projectDim.level).toBe('high');
// 验证总分
expect(result.overallScore).toBeGreaterThan(75);
// 验证推荐结论
expect(result.recommendation.level).toBe('recommend');
});
it('应该能够根据条件筛选简历', async () => {
// 创建多份简历
const resumes = [
{ score: 85, education: 'bachelor', status: 'analyzed' },
{ score: 65, education: 'college', status: 'analyzed' },
{ score: 92, education: 'master', status: 'analyzed' }
];
for (const data of resumes) {
const resume = new Parse.Object('Resume');
const analysis = new Parse.Object('ResumeAnalysis');
analysis.set('resume', resume);
analysis.set('overallScore', data.score);
await resume.save();
await analysis.save();
}
// 筛选条件:综合评分 > 80
const filteredResumes = await this.filterResumes({
minScore: 80
});
expect(filteredResumes.length).toBe(2);
expect(filteredResumes.every(r => r.score >= 80)).toBe(true);
});
it('应该能够生成月度绩效记录', async () => {
// 准备测试数据:创建员工和项目
const designer = await this.createTestDesigner();
const projects = await this.createTestProjects(designer, 5);
// 生成绩效记录
await this.generateMonthlyPerformance();
// 验证记录是否创建
const query = new Parse.Query('PerformanceRecord');
query.equalTo('profile', designer);
const record = await query.first();
expect(record).toBeDefined();
expect(record.get('projectMetrics').totalProjects).toBe(5);
});
it('绩效对比应该正确排序', async () => {
// 创建3个设计师的绩效记录
const records = [
{ name: '张三', score: 85 },
{ name: '李四', score: 92 },
{ name: '王五', score: 78 }
];
for (const data of records) {
const profile = await this.createTestProfile(data.name);
const performance = new Parse.Object('PerformanceRecord');
performance.set('profile', profile);
performance.set('overallScore', data.score);
await performance.save();
}
// 加载绩效对比
const comparison = await this.loadPerformanceComparison('employee');
// 验证排序(降序)
expect(comparison[0].name).toBe('李四');
expect(comparison[1].name).toBe('张三');
expect(comparison[2].name).toBe('王五');
});
# 在 Parse Dashboard 中依次创建以下表:
1. Resume(简历)
- 权限设置:管理员和HR可读写
- 索引:candidateName, status, jobPosition
2. ResumeAnalysis(简历分析)
- 权限设置:管理员和HR可读写
- 索引:resume, analysisDate, overallScore
3. RecruitmentProcess(招聘流程)
- 权限设置:管理员和HR可读写
- 索引:resume, status, currentStage
4. PerformanceRecord(绩效记录)
- 权限设置:管理员可读写,员工可读自己的
- 索引:profile, period, overallScore
5. ResignationRecord(离职记录)
- 权限设置:管理员和HR可读写
- 索引:profile, resignationDate, reasonCategory
6. OnboardingCheckpoint(入职检查点)
- 权限设置:管理员和HR可读写
- 索引:profile, type, dueDate, completed
# Profile 表
添加字段:
- data.hrData.mentor (Pointer<Profile>)
- data.hrData.resignationDate (Date)
- data.hrData.probationEndDate (Date)
- data.hrData.convertedDate (Date)
# Project 表
添加字段:
- data.quality.score (Number)
- data.quality.isExcellent (Boolean)
- data.clientSatisfaction (Number)
- data.timeline.expectedDate (Date)
- data.timeline.actualDate (Date)
// cloud/hr-performance.js
Parse.Cloud.define('generateMonthlyPerformance', async (request) => {
const { month, year } = request.params;
// 查询所有在职设计师
const profileQuery = new Parse.Query('Profile');
profileQuery.equalTo('isActivated', true);
profileQuery.notEqualTo('isDeleted', true);
profileQuery.equalTo('data.hrData.employmentStatus', 'active');
const profiles = await profileQuery.find({ useMasterKey: true });
const results = [];
for (const profile of profiles) {
// 查询该月的项目
const startDate = new Date(year, month - 1, 1);
const endDate = new Date(year, month, 0);
const projectQuery = new Parse.Query('Project');
projectQuery.equalTo('assignee', profile);
projectQuery.greaterThanOrEqualTo('createdAt', startDate);
projectQuery.lessThan('createdAt', endDate);
const projects = await projectQuery.find({ useMasterKey: true });
// 计算绩效指标
const metrics = calculateMetrics(projects);
const capabilities = calculateCapabilities(profile, projects);
const overallScore = calculateOverallScore(metrics, capabilities);
// 保存绩效记录
const Performance = Parse.Object.extend('PerformanceRecord');
const record = new Performance();
record.set('profile', profile);
record.set('department', profile.get('department'));
record.set('period', { year, month, quarter: Math.ceil(month / 3) });
record.set('projectMetrics', metrics);
record.set('capabilities', capabilities);
record.set('overallScore', overallScore);
await record.save(null, { useMasterKey: true });
results.push({ profileId: profile.id, score: overallScore });
}
return { success: true, count: results.length, results };
});
// 辅助函数
function calculateMetrics(projects) {
return {
totalProjects: projects.length,
completedProjects: projects.filter(p => p.get('status') === 'completed').length,
excellentProjects: projects.filter(p => p.get('data')?.quality?.isExcellent).length,
overdueProjects: projects.filter(p => {
const expected = p.get('data')?.timeline?.expectedDate;
const actual = p.get('data')?.timeline?.actualDate;
return actual && expected && actual > expected;
}).length
};
}
// cloud/hr-onboarding.js
Parse.Cloud.define('createOnboardingCheckpoints', async (request) => {
const { profileId } = request.params;
const profile = await new Parse.Query('Profile').get(profileId, { useMasterKey: true });
const hireDate = profile.get('data')?.hrData?.hireDate || new Date();
const checkpointTemplates = [
{ type: 'week1', title: '第一周访谈', days: 7 },
{ type: 'month1', title: '第一个月评估', days: 30 },
{ type: 'month2', title: '第二个月评估', days: 60 },
{ type: 'month3', title: '转正前评估', days: 85 }
];
const Checkpoint = Parse.Object.extend('OnboardingCheckpoint');
for (const template of checkpointTemplates) {
const checkpoint = new Checkpoint();
const dueDate = new Date(hireDate);
dueDate.setDate(dueDate.getDate() + template.days);
checkpoint.set('profile', profile);
checkpoint.set('title', template.title);
checkpoint.set('type', template.type);
checkpoint.set('dueDate', dueDate);
checkpoint.set('completed', false);
await checkpoint.save(null, { useMasterKey: true });
}
return { success: true, count: checkpointTemplates.length };
});
# 在 Parse Dashboard 中配置 Jobs
1. 月度绩效生成
- 任务名称:generateMonthlyPerformance
- 执行时间:每月1日 00:00
- 函数:generateMonthlyPerformance
2. 新人检查点提醒
- 任务名称:checkOnboardingDue
- 执行时间:每天 09:00
- 函数:checkOnboardingDue
// environment.ts
export const environment = {
production: false,
parseConfig: {
applicationId: 'your-app-id',
serverURL: 'https://your-parse-server.com/parse'
},
doubaoAI: {
apiKey: 'your-doubao-api-key',
modelId: 'ep-20241201234567-abcdef'
}
};
// hr-dashboard.guard.ts
@Injectable()
export class HRDashboardGuard implements CanActivate {
async canActivate(route: ActivatedRouteSnapshot): Promise<boolean> {
const profile = await this.profileService.getCurrentProfile();
// 只有 HR 和管理员可以访问
const allowedRoles = ['HR', '管理员', 'Admin'];
const roleName = profile?.get('roleName');
return allowedRoles.includes(roleName);
}
}
✅ 数据库表已创建
✅ 字段权限已配置
✅ 云函数已部署
✅ 定时任务已配置
✅ 测试数据已准备
✅ 功能测试已通过
✅ 性能测试已通过
✅ 权限测试已通过
✅ 现有员工数据补充 level 字段
✅ 现有项目数据补充 quality 字段
✅ 历史离职数据导入 ResignationRecord
✅ 生成最近3个月的绩效记录
- API 响应时间 < 2s
- AI 分析时间 < 3s
- 图表渲染时间 < 1s
- 数据库查询时间 < 500ms
- 错误率 < 0.1%
文档版本:v1.0
最后更新:2025-11-20 01:45
维护人:Cascade AI Assistant
状态:✅ 待实施