commit 7d022bc46f601f42ab52bdd3dda858a360bda1b4 Author: Bnavy <2471400297@qq.com> Date: Sun Oct 26 21:42:16 2025 +0800 单一 diff --git a/EmployeeSystem/Certificate.class b/EmployeeSystem/Certificate.class new file mode 100644 index 0000000..9bfcb8b Binary files /dev/null and b/EmployeeSystem/Certificate.class differ diff --git a/EmployeeSystem/Certificate.java b/EmployeeSystem/Certificate.java new file mode 100644 index 0000000..c6e6a70 --- /dev/null +++ b/EmployeeSystem/Certificate.java @@ -0,0 +1,174 @@ +import java.time.LocalDate; + +/** + * 证书类 - 负责证书信息管理 + * 遵循单一职责原则:只负责证书数据的存储和状态管理 + */ +public class Certificate { + private String certificateId; // 证书ID + private String employeeId; // 员工ID + private String courseId; // 课程ID + private String courseName; // 课程名称 + private LocalDate issueDate; // 颁发日期 + private LocalDate expiryDate; // 过期日期 + private String issuer; // 颁发机构 + private String trainer; // 培训师 + private double score; // 成绩 + private boolean active; // 是否有效 + private String certificateNumber; // 证书编号 + private String description; // 证书描述 + + /** + * 构造函数 - 创建证书 + */ + public Certificate(String certificateId, String employeeId, String courseId, + String courseName, String issuer, String trainer, double score) { + this.certificateId = certificateId; + this.employeeId = employeeId; + this.courseId = courseId; + this.courseName = courseName; + this.issuer = issuer; + this.trainer = trainer; + this.score = score; + this.issueDate = LocalDate.now(); + this.active = true; + this.certificateNumber = generateCertificateNumber(certificateId, employeeId); + this.description = ""; + } + + /** + * 生成证书编号 + */ + private String generateCertificateNumber(String certificateId, String employeeId) { + LocalDate now = LocalDate.now(); + return String.format("CERT-%s-%s-%d", + employeeId, certificateId, now.getYear() * 10000 + now.getMonthValue() * 100 + now.getDayOfMonth()); + } + + /** + * 设置证书有效期 + * @param years 有效期年数 + */ + public void setValidYears(int years) { + if (years <= 0) { + throw new IllegalArgumentException("有效期必须大于0"); + } + this.expiryDate = issueDate.plusYears(years); + } + + /** + * 检查证书是否过期 + * @return true如果证书已过期 + */ + public boolean isExpired() { + if (expiryDate == null) { + return false; // 没有设置过期日期的证书永不过期 + } + return LocalDate.now().isAfter(expiryDate); + } + + /** + * 吊销证书 + * @param reason 吊销原因 + */ + public void revokeCertificate(String reason) { + this.active = false; + this.description = reason; + } + + /** + * 恢复证书 + */ + public void reinstateCertificate() { + if (!isExpired()) { + this.active = true; + } else { + throw new IllegalStateException("已过期的证书无法恢复"); + } + } + + /** + * 获取证书状态描述 + * @return 证书状态描述 + */ + public String getStatusDescription() { + if (!active) { + return "已吊销"; + } else if (isExpired()) { + return "已过期"; + } else { + return "有效"; + } + } + + /** + * 获取剩余有效天数 + * @return 剩余有效天数,如果不过期则返回null + */ + public Long getRemainingValidDays() { + if (expiryDate == null || isExpired()) { + return null; + } + return (long) LocalDate.now().until(expiryDate).getDays(); + } + + /** + * 获取成绩等级 + * @return 成绩等级 + */ + public String getScoreGrade() { + if (score >= 90) return "优秀"; + else if (score >= 80) return "良好"; + else if (score >= 70) return "中等"; + else if (score >= 60) return "及格"; + else return "不及格"; + } + + // Getter和Setter方法 + + public String getCertificateId() { return certificateId; } + + public String getEmployeeId() { return employeeId; } + + public String getCourseId() { return courseId; } + + public String getCourseName() { return courseName; } + + public void setCourseName(String courseName) { this.courseName = courseName; } + + public LocalDate getIssueDate() { return issueDate; } + + public LocalDate getExpiryDate() { return expiryDate; } + + public String getIssuer() { return issuer; } + + public void setIssuer(String issuer) { this.issuer = issuer; } + + public String getTrainer() { return trainer; } + + public void setTrainer(String trainer) { this.trainer = trainer; } + + public double getScore() { return score; } + + public boolean isActive() { return active; } + + public String getCertificateNumber() { return certificateNumber; } + + public String getDescription() { return description; } + + public void setDescription(String description) { this.description = description; } + + @Override + public String toString() { + return "Certificate{" + + "certificateId='" + certificateId + "'" + + ", certificateNumber='" + certificateNumber + "'" + + ", courseName='" + courseName + "'" + + ", employeeId='" + employeeId + "'" + + ", score=" + score + "(" + getScoreGrade() + ")" + + ", issueDate=" + issueDate + "'" + + ", expiryDate=" + (expiryDate != null ? expiryDate : "永不过期") + "'" + + ", status=" + getStatusDescription() + "'" + + "}"; + } +} \ No newline at end of file diff --git a/EmployeeSystem/EmailService.class b/EmployeeSystem/EmailService.class new file mode 100644 index 0000000..8a8fc80 Binary files /dev/null and b/EmployeeSystem/EmailService.class differ diff --git a/EmployeeSystem/EmailService.java b/EmployeeSystem/EmailService.java new file mode 100644 index 0000000..67aca37 --- /dev/null +++ b/EmployeeSystem/EmailService.java @@ -0,0 +1,179 @@ +/** + * 邮件服务类 - 负责邮件通知功能 + * 遵循单一职责原则:只负责邮件发送相关的业务逻辑 + */ +public class EmailService { + + /** + * 发送员工入职通知邮件 + * @param employeeId 员工ID + * @param employeeName 员工姓名 + * @param employeeEmail 员工邮箱 + * @param department 部门 + * @param hireDate 入职日期字符串 + */ + public void sendWelcomeEmail(String employeeId, String employeeName, String employeeEmail, + String department, String hireDate) { + String subject = "欢迎加入我们的团队!"; + String content = String.format( + "尊敬的 %s 先生/女士:\n\n" + + "恭喜您成功加入我们的团队!\n" + + "员工ID:%s\n" + + "部门:%s\n" + + "入职日期:%s\n\n" + + "我们期待您的贡献!如有任何问题,请随时联系人力资源部门。\n\n" + + "此致\n" + + "人力资源部", + employeeName, employeeId, department, hireDate + ); + sendEmail(employeeEmail, subject, content); + } + + /** + * 发送员工离职通知邮件 + * @param employeeId 员工ID + * @param employeeName 员工姓名 + * @param employeeEmail 员工邮箱 + * @param lastWorkingDay 最后工作日字符串 + */ + public void sendFarewellEmail(String employeeId, String employeeName, String employeeEmail, + String lastWorkingDay) { + String subject = "离职通知确认"; + String content = String.format( + "尊敬的 %s 先生/女士:\n\n" + + "感谢您在公司工作期间的贡献。\n" + + "员工ID:%s\n" + + "最后工作日:%s\n\n" + + "祝您未来一切顺利!\n\n" + + "此致\n" + + "人力资源部", + employeeName, employeeId, lastWorkingDay + ); + sendEmail(employeeEmail, subject, content); + } + + /** + * 发送薪资调整通知邮件 + * @param employeeId 员工ID + * @param employeeName 员工姓名 + * @param employeeEmail 员工邮箱 + * @param oldSalary 调整前薪资 + * @param newSalary 调整后薪资 + * @param effectiveDate 生效日期字符串 + */ + public void sendSalaryAdjustmentEmail(String employeeId, String employeeName, String employeeEmail, + double oldSalary, double newSalary, String effectiveDate) { + String subject = "薪资调整通知"; + double changeAmount = newSalary - oldSalary; + double changePercentage = (changeAmount / oldSalary) * 100; + + String content = String.format( + "尊敬的 %s 先生/女士:\n\n" + + "很高兴通知您,您的薪资已进行调整。\n" + + "员工ID:%s\n" + + "调整前薪资:%.2f\n" + + "调整后薪资:%.2f\n" + + "调整金额:%.2f (%.2f%%)\n" + + "生效日期:%s\n\n" + + "感谢您的努力工作!\n\n" + + "此致\n" + + "人力资源部", + employeeName, employeeId, oldSalary, newSalary, + changeAmount, changePercentage, effectiveDate + ); + sendEmail(employeeEmail, subject, content); + } + + /** + * 发送职位变动通知邮件 + * @param employeeId 员工ID + * @param employeeName 员工姓名 + * @param employeeEmail 员工邮箱 + * @param oldPosition 原职位 + * @param newPosition 新职位 + * @param effectiveDate 生效日期字符串 + */ + public void sendPositionChangeEmail(String employeeId, String employeeName, String employeeEmail, + String oldPosition, String newPosition, String effectiveDate) { + String subject = "职位变动通知"; + String content = String.format( + "尊敬的 %s 先生/女士:\n\n" + + "很高兴通知您,您的职位已进行调整。\n" + + "员工ID:%s\n" + + "原职位:%s\n" + + "新职位:%s\n" + + "生效日期:%s\n\n" + + "祝贺您!\n\n" + + "此致\n" + + "人力资源部", + employeeName, employeeId, oldPosition, newPosition, effectiveDate + ); + sendEmail(employeeEmail, subject, content); + } + + /** + * 发送培训通知邮件 + * @param employeeId 员工ID + * @param employeeName 员工姓名 + * @param employeeEmail 员工邮箱 + * @param courseName 课程名称 + * @param trainingDate 培训日期字符串 + * @param trainer 培训师 + */ + public void sendTrainingNotificationEmail(String employeeId, String employeeName, String employeeEmail, + String courseName, String trainingDate, String trainer) { + String subject = "培训课程通知"; + String content = String.format( + "尊敬的 %s 先生/女士:\n\n" + + "您已被安排参加以下培训课程:\n" + + "员工ID:%s\n" + + "课程名称:%s\n" + + "培训日期:%s\n" + + "培训师:%s\n\n" + + "请提前做好准备。\n\n" + + "此致\n" + + "培训部门", + employeeName, employeeId, courseName, trainingDate, trainer + ); + sendEmail(employeeEmail, subject, content); + } + + /** + * 发送证书发放通知邮件 + * @param employeeId 员工ID + * @param employeeName 员工姓名 + * @param employeeEmail 员工邮箱 + * @param certificateName 证书名称 + * @param issueDate 发放日期字符串 + */ + public void sendCertificateEmail(String employeeId, String employeeName, String employeeEmail, + String certificateName, String issueDate) { + String subject = "证书发放通知"; + String content = String.format( + "尊敬的 %s 先生/女士:\n\n" + + "恭喜您获得以下证书:\n" + + "员工ID:%s\n" + + "证书名称:%s\n" + + "发放日期:%s\n\n" + + "您的证书已生成,可在系统中查看和下载。\n\n" + + "此致\n" + + "培训部门", + employeeName, employeeId, certificateName, issueDate + ); + sendEmail(employeeEmail, subject, content); + } + + /** + * 实际的邮件发送方法(模拟实现) + * 在实际应用中,这里应该集成真实的邮件发送API + */ + private void sendEmail(String toEmail, String subject, String content) { + // 模拟邮件发送过程 + System.out.println("========================================"); + System.out.println("【邮件已发送】"); + System.out.println("收件人: " + toEmail); + System.out.println("主题: " + subject); + System.out.println("内容:\n" + content); + System.out.println("========================================"); + } +} \ No newline at end of file diff --git a/EmployeeSystem/Employee.class b/EmployeeSystem/Employee.class new file mode 100644 index 0000000..6c75718 Binary files /dev/null and b/EmployeeSystem/Employee.class differ diff --git a/EmployeeSystem/Employee.java b/EmployeeSystem/Employee.java new file mode 100644 index 0000000..a34875c --- /dev/null +++ b/EmployeeSystem/Employee.java @@ -0,0 +1,142 @@ +import java.time.LocalDate; + +/** + * 员工类 - 负责员工基本信息管理 + * 遵循单一职责原则:只负责员工数据的存储和基本验证 + */ +public class Employee { + private String id; // 员工ID + private String name; // 员工姓名 + private double salary; // 薪资 + private String department; // 部门 + private String position; // 职位 + private LocalDate hireDate; // 入职日期 + private LocalDate birthDate; // 出生日期 + private String email; // 邮箱(新增属性) + private boolean active; // 是否在职 + + /** + * 构造函数 - 创建员工对象 + */ + public Employee(String id, String name, double salary, String department, + String position, LocalDate hireDate, LocalDate birthDate, String email) { + this.id = id; + this.name = name; + this.salary = salary; + this.department = department; + this.position = position; + this.hireDate = hireDate; + this.birthDate = birthDate; + this.active = true; + setEmail(email); // 使用setter进行邮箱验证 + } + + /** + * 设置邮箱 - 包含验证逻辑 + * @param email 邮箱地址 + * @throws IllegalArgumentException 如果邮箱格式无效 + */ + public void setEmail(String email) { + if (isValidEmail(email)) { + this.email = email; + } else { + throw new IllegalArgumentException("无效的邮箱格式: " + email); + } + } + + /** + * 邮箱格式验证 + * @param email 邮箱地址 + * @return true如果邮箱格式有效 + */ + private boolean isValidEmail(String email) { + if (email == null || email.trim().isEmpty()) { + return false; + } + // 基本的邮箱格式验证正则表达式 + String emailRegex = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$"; + return email.matches(emailRegex); + } + + /** + * 获取员工年龄 + * @return 员工年龄 + */ + public int getAge() { + LocalDate now = LocalDate.now(); + int age = now.getYear() - birthDate.getYear(); + if (now.getMonthValue() < birthDate.getMonthValue() || + (now.getMonthValue() == birthDate.getMonthValue() && + now.getDayOfMonth() < birthDate.getDayOfMonth())) { + age--; + } + return age; + } + + /** + * 获取员工司龄 + * @return 员工司龄(年) + */ + public int getTenure() { + LocalDate now = LocalDate.now(); + int tenure = now.getYear() - hireDate.getYear(); + if (now.getMonthValue() < hireDate.getMonthValue() || + (now.getMonthValue() == hireDate.getMonthValue() && + now.getDayOfMonth() < hireDate.getDayOfMonth())) { + tenure--; + } + return tenure; + } + + // Getter和Setter方法 - 遵循单一职责原则,只负责属性的访问和修改 + + public String getId() { return id; } + + public String getName() { return name; } + + public void setName(String name) { this.name = name; } + + public double getSalary() { return salary; } + + public void setSalary(double salary) { + if (salary >= 0) { + this.salary = salary; + } else { + throw new IllegalArgumentException("薪资不能为负数"); + } + } + + public String getDepartment() { return department; } + + public void setDepartment(String department) { this.department = department; } + + public String getPosition() { return position; } + + public void setPosition(String position) { this.position = position; } + + public LocalDate getHireDate() { return hireDate; } + + public LocalDate getBirthDate() { return birthDate; } + + public String getEmail() { return email; } + + public boolean isActive() { return active; } + + public void setActive(boolean active) { this.active = active; } + + @Override + public String toString() { + return "Employee{" + + "id='" + id + "'" + + ", name='" + name + "'" + + ", salary=" + salary + + ", department='" + department + "'" + + ", position='" + position + "'" + + ", hireDate=" + hireDate + + ", age=" + getAge() + + ", tenure=" + getTenure() + "年" + + ", email='" + email + "'" + + ", active=" + (active ? "在职" : "离职") + + "}"; + } +} \ No newline at end of file diff --git a/EmployeeSystem/EmployeeHistory$ChangeRecord.class b/EmployeeSystem/EmployeeHistory$ChangeRecord.class new file mode 100644 index 0000000..7fd1340 Binary files /dev/null and b/EmployeeSystem/EmployeeHistory$ChangeRecord.class differ diff --git a/EmployeeSystem/EmployeeHistory$ChangeType.class b/EmployeeSystem/EmployeeHistory$ChangeType.class new file mode 100644 index 0000000..6092243 Binary files /dev/null and b/EmployeeSystem/EmployeeHistory$ChangeType.class differ diff --git a/EmployeeSystem/EmployeeHistory.class b/EmployeeSystem/EmployeeHistory.class new file mode 100644 index 0000000..5867e98 Binary files /dev/null and b/EmployeeSystem/EmployeeHistory.class differ diff --git a/EmployeeSystem/EmployeeHistory.java b/EmployeeSystem/EmployeeHistory.java new file mode 100644 index 0000000..ac0ba22 --- /dev/null +++ b/EmployeeSystem/EmployeeHistory.java @@ -0,0 +1,251 @@ +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 员工历史记录类 - 负责记录员工的历史变更信息 + * 遵循单一职责原则:只负责员工变更历史的记录和管理 + */ +public class EmployeeHistory { + private final String employeeId; + private final List historyRecords; + + /** + * 构造函数 + * @param employeeId 员工ID + */ + public EmployeeHistory(String employeeId) { + this.employeeId = employeeId; + this.historyRecords = new ArrayList<>(); + } + + /** + * 记录薪资调整历史 + * @param oldSalary 调整前薪资 + * @param newSalary 调整后薪资 + * @param reason 调整原因 + * @param changedBy 操作人 + */ + public void recordSalaryChange(double oldSalary, double newSalary, String reason, String changedBy) { + String description = String.format("薪资从 %.2f 调整为 %.2f", oldSalary, newSalary); + ChangeRecord record = new ChangeRecord(ChangeType.SALARY_ADJUSTMENT, + description, reason, changedBy); + historyRecords.add(record); + } + + /** + * 记录职位变更历史 + * @param oldPosition 原职位 + * @param newPosition 新职位 + * @param reason 变更原因 + * @param changedBy 操作人 + */ + public void recordPositionChange(String oldPosition, String newPosition, String reason, String changedBy) { + String description = String.format("职位从 '%s' 变更为 '%s'", oldPosition, newPosition); + ChangeRecord record = new ChangeRecord(ChangeType.POSITION_CHANGE, + description, reason, changedBy); + historyRecords.add(record); + } + + /** + * 记录部门变更历史 + * @param oldDepartment 原部门 + * @param newDepartment 新部门 + * @param reason 变更原因 + * @param changedBy 操作人 + */ + public void recordDepartmentChange(String oldDepartment, String newDepartment, String reason, String changedBy) { + String description = String.format("部门从 '%s' 变更为 '%s'", oldDepartment, newDepartment); + ChangeRecord record = new ChangeRecord(ChangeType.DEPARTMENT_CHANGE, + description, reason, changedBy); + historyRecords.add(record); + } + + /** + * 记录员工状态变更历史(在职/离职) + * @param newStatus 新状态 + * @param reason 变更原因 + * @param changedBy 操作人 + */ + public void recordStatusChange(boolean newStatus, String reason, String changedBy) { + String description = "状态变更为 " + (newStatus ? "在职" : "离职"); + ChangeRecord record = new ChangeRecord(ChangeType.STATUS_CHANGE, + description, reason, changedBy); + historyRecords.add(record); + } + + /** + * 记录员工基本信息变更历史 + * @param fieldName 变更的字段名 + * @param oldValue 旧值 + * @param newValue 新值 + * @param changedBy 操作人 + */ + public void recordInfoChange(String fieldName, String oldValue, String newValue, String changedBy) { + String description = String.format("%s 从 '%s' 变更为 '%s'", fieldName, oldValue, newValue); + ChangeRecord record = new ChangeRecord(ChangeType.INFO_CHANGE, + description, "信息更新", changedBy); + historyRecords.add(record); + } + + /** + * 记录培训完成历史 + * @param courseName 课程名称 + * @param certificateId 证书ID + * @param score 成绩 + * @param changedBy 操作人 + */ + public void recordTrainingCompletion(String courseName, String certificateId, double score, String changedBy) { + String description = String.format("完成课程 '%s',证书ID: %s,成绩: %.2f", + courseName, certificateId, score); + ChangeRecord record = new ChangeRecord(ChangeType.TRAINING_COMPLETION, + description, "培训完成", changedBy); + historyRecords.add(record); + } + + /** + * 获取所有历史记录 + * @return 按时间倒序排列的历史记录列表 + */ + public List getAllHistory() { + List sortedRecords = new ArrayList<>(historyRecords); + sortedRecords.sort(Comparator.comparing(ChangeRecord::getChangeTime).reversed()); + return sortedRecords; + } + + /** + * 按变更类型获取历史记录 + * @param changeType 变更类型 + * @return 指定类型的历史记录列表 + */ + public List getHistoryByType(ChangeType changeType) { + return historyRecords.stream() + .filter(record -> record.getChangeType() == changeType) + .sorted(Comparator.comparing(ChangeRecord::getChangeTime).reversed()) + .collect(Collectors.toList()); + } + + /** + * 获取特定时间段内的历史记录 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 时间段内的历史记录列表 + */ + public List getHistoryByTimeRange(LocalDateTime startTime, LocalDateTime endTime) { + return historyRecords.stream() + .filter(record -> !record.getChangeTime().isBefore(startTime) && + !record.getChangeTime().isAfter(endTime)) + .sorted(Comparator.comparing(ChangeRecord::getChangeTime).reversed()) + .collect(Collectors.toList()); + } + + /** + * 获取最后N条历史记录 + * @param n 记录数量 + * @return 最近的N条历史记录 + */ + public List getLatestHistory(int n) { + List sortedRecords = new ArrayList<>(historyRecords); + sortedRecords.sort(Comparator.comparing(ChangeRecord::getChangeTime).reversed()); + return sortedRecords.stream().limit(n).collect(Collectors.toList()); + } + + /** + * 获取薪资变更次数 + * @return 薪资变更次数 + */ + public int getSalaryChangeCount() { + return (int) historyRecords.stream() + .filter(record -> record.getChangeType() == ChangeType.SALARY_ADJUSTMENT) + .count(); + } + + /** + * 获取职位变更次数 + * @return 职位变更次数 + */ + public int getPositionChangeCount() { + return (int) historyRecords.stream() + .filter(record -> record.getChangeType() == ChangeType.POSITION_CHANGE) + .count(); + } + + /** + * 获取部门变更次数 + * @return 部门变更次数 + */ + public int getDepartmentChangeCount() { + return (int) historyRecords.stream() + .filter(record -> record.getChangeType() == ChangeType.DEPARTMENT_CHANGE) + .count(); + } + + /** + * 获取培训完成次数 + * @return 培训完成次数 + */ + public int getTrainingCompletionCount() { + return (int) historyRecords.stream() + .filter(record -> record.getChangeType() == ChangeType.TRAINING_COMPLETION) + .count(); + } + + /** + * 获取员工ID + * @return 员工ID + */ + public String getEmployeeId() { + return employeeId; + } + + /** + * 获取历史记录总数 + * @return 历史记录总数 + */ + public int getTotalRecordCount() { + return historyRecords.size(); + } + + /** + * 变更类型枚举 + */ + public enum ChangeType { + SALARY_ADJUSTMENT, // 薪资调整 + POSITION_CHANGE, // 职位变更 + DEPARTMENT_CHANGE, // 部门变更 + STATUS_CHANGE, // 状态变更 + INFO_CHANGE, // 信息变更 + TRAINING_COMPLETION // 培训完成 + } + + /** + * 变更记录内部类 + */ + public static class ChangeRecord { + private final ChangeType changeType; + private final String description; + private final String reason; + private final String changedBy; + private final LocalDateTime changeTime; + + public ChangeRecord(ChangeType changeType, String description, String reason, String changedBy) { + this.changeType = changeType; + this.description = description; + this.reason = reason; + this.changedBy = changedBy; + this.changeTime = LocalDateTime.now(); + } + + public ChangeType getChangeType() { return changeType; } + public String getDescription() { return description; } + public String getReason() { return reason; } + public String getChangedBy() { return changedBy; } + public LocalDateTime getChangeTime() { return changeTime; } + + @Override + public String toString() { + return String.format("[%s] %s | 原因: %s | 操作人: %s | 时间: %s", + changeType.name(), description, reason, changedBy, changeTime); + } + } +} \ No newline at end of file diff --git a/EmployeeSystem/EmployeeSearch$DepartmentSalaryStats.class b/EmployeeSystem/EmployeeSearch$DepartmentSalaryStats.class new file mode 100644 index 0000000..692a52e Binary files /dev/null and b/EmployeeSystem/EmployeeSearch$DepartmentSalaryStats.class differ diff --git a/EmployeeSystem/EmployeeSearch.class b/EmployeeSystem/EmployeeSearch.class new file mode 100644 index 0000000..16d38bc Binary files /dev/null and b/EmployeeSystem/EmployeeSearch.class differ diff --git a/EmployeeSystem/EmployeeSearch.java b/EmployeeSystem/EmployeeSearch.java new file mode 100644 index 0000000..2b8f15e --- /dev/null +++ b/EmployeeSystem/EmployeeSearch.java @@ -0,0 +1,192 @@ +import java.time.LocalDate; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 员工搜索类 - 负责复杂的员工搜索功能 + * 遵循单一职责原则:只负责员工数据的搜索逻辑 + */ +public class EmployeeSearch { + + /** + * 按薪资范围搜索员工 + * @param employees 员工列表 + * @param minSalary 最低薪资 + * @param maxSalary 最高薪资 + * @return 符合条件的员工列表 + */ + public List searchBySalaryRange(List employees, double minSalary, double maxSalary) { + if (minSalary > maxSalary) { + throw new IllegalArgumentException("最低薪资不能大于最高薪资"); + } + + return employees.stream() + .filter(e -> e.getSalary() >= minSalary && e.getSalary() <= maxSalary) + .collect(Collectors.toList()); + } + + /** + * 按入职日期范围搜索员工 + * @param employees 员工列表 + * @param startDate 开始日期 + * @param endDate 结束日期 + * @return 符合条件的员工列表 + */ + public List searchByHireDateRange(List employees, LocalDate startDate, LocalDate endDate) { + if (startDate.isAfter(endDate)) { + throw new IllegalArgumentException("开始日期不能晚于结束日期"); + } + + return employees.stream() + .filter(e -> (!e.getHireDate().isBefore(startDate) && !e.getHireDate().isAfter(endDate))) + .collect(Collectors.toList()); + } + + /** + * 按部门搜索员工 + * @param employees 员工列表 + * @param department 部门名称 + * @return 符合条件的员工列表 + */ + public List searchByDepartment(List employees, String department) { + if (department == null || department.trim().isEmpty()) { + throw new IllegalArgumentException("部门名称不能为空"); + } + + return employees.stream() + .filter(e -> e.getDepartment().equals(department)) + .collect(Collectors.toList()); + } + + /** + * 按职位搜索员工 + * @param employees 员工列表 + * @param position 职位名称 + * @return 符合条件的员工列表 + */ + public List searchByPosition(List employees, String position) { + if (position == null || position.trim().isEmpty()) { + throw new IllegalArgumentException("职位名称不能为空"); + } + + return employees.stream() + .filter(e -> e.getPosition().equals(position)) + .collect(Collectors.toList()); + } + + /** + * 按年龄范围搜索员工 + * @param employees 员工列表 + * @param minAge 最小年龄 + * @param maxAge 最大年龄 + * @return 符合条件的员工列表 + */ + public List searchByAgeRange(List employees, int minAge, int maxAge) { + if (minAge > maxAge) { + throw new IllegalArgumentException("最小年龄不能大于最大年龄"); + } + if (minAge < 0 || maxAge < 0) { + throw new IllegalArgumentException("年龄不能为负数"); + } + + return employees.stream() + .filter(e -> e.getAge() >= minAge && e.getAge() <= maxAge) + .collect(Collectors.toList()); + } + + /** + * 按司龄搜索员工 + * @param employees 员工列表 + * @param minTenure 最小司龄(年) + * @param maxTenure 最大司龄(年) + * @return 符合条件的员工列表 + */ + public List searchByTenure(List employees, int minTenure, int maxTenure) { + if (minTenure > maxTenure) { + throw new IllegalArgumentException("最小司龄不能大于最大司龄"); + } + if (minTenure < 0 || maxTenure < 0) { + throw new IllegalArgumentException("司龄不能为负数"); + } + + return employees.stream() + .filter(e -> e.getTenure() >= minTenure && e.getTenure() <= maxTenure) + .collect(Collectors.toList()); + } + + /** + * 搜索在职员工 + * @param employees 员工列表 + * @return 在职员工列表 + */ + public List searchActiveEmployees(List employees) { + return employees.stream() + .filter(Employee::isActive) + .collect(Collectors.toList()); + } + + /** + * 搜索离职员工 + * @param employees 员工列表 + * @return 离职员工列表 + */ + public List searchInactiveEmployees(List employees) { + return employees.stream() + .filter(e -> !e.isActive()) + .collect(Collectors.toList()); + } + + /** + * 按员工姓名关键字搜索 + * @param employees 员工列表 + * @param keyword 姓名关键字 + * @return 符合条件的员工列表 + */ + public List searchByNameKeyword(List employees, String keyword) { + if (keyword == null || keyword.trim().isEmpty()) { + throw new IllegalArgumentException("搜索关键字不能为空"); + } + + String lowerKeyword = keyword.toLowerCase(); + return employees.stream() + .filter(e -> e.getName().toLowerCase().contains(lowerKeyword)) + .collect(Collectors.toList()); + } + + /** + * 组合搜索:按部门和薪资范围 + * @param employees 员工列表 + * @param department 部门名称 + * @param minSalary 最低薪资 + * @param maxSalary 最高薪资 + * @return 符合条件的员工列表 + */ + public List searchByDepartmentAndSalaryRange(List employees, + String department, + double minSalary, + double maxSalary) { + return employees.stream() + .filter(e -> e.getDepartment().equals(department)) + .filter(e -> e.getSalary() >= minSalary && e.getSalary() <= maxSalary) + .collect(Collectors.toList()); + } + + /** + * 组合搜索:按部门和职位 + * @param employees 员工列表 + * @param department 部门名称 + * @param position 职位名称 + * @return 符合条件的员工列表 + */ + public List searchByDepartmentAndPosition(List employees, + String department, + String position) { + return employees.stream() + .filter(e -> e.getDepartment().equals(department)) + .filter(e -> e.getPosition().equals(position)) + .collect(Collectors.toList()); + } + + public class DepartmentSalaryStats { + } +} \ No newline at end of file diff --git a/EmployeeSystem/EmployeeStatistics$DepartmentSalaryStats.class b/EmployeeSystem/EmployeeStatistics$DepartmentSalaryStats.class new file mode 100644 index 0000000..6547490 Binary files /dev/null and b/EmployeeSystem/EmployeeStatistics$DepartmentSalaryStats.class differ diff --git a/EmployeeSystem/EmployeeStatistics.class b/EmployeeSystem/EmployeeStatistics.class new file mode 100644 index 0000000..79edf7a Binary files /dev/null and b/EmployeeSystem/EmployeeStatistics.class differ diff --git a/EmployeeSystem/EmployeeStatistics.java b/EmployeeSystem/EmployeeStatistics.java new file mode 100644 index 0000000..7268419 --- /dev/null +++ b/EmployeeSystem/EmployeeStatistics.java @@ -0,0 +1,230 @@ +import java.time.LocalDate; +import java.time.Period; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 员工统计类 - 负责生成复杂的统计报告 + * 遵循单一职责原则:只负责员工数据的统计分析逻辑 + */ +public class EmployeeStatistics { + + /** + * 计算员工薪资总额 + * @param employees 员工列表 + * @return 薪资总额 + */ + public double calculateTotalSalary(List employees) { + return employees.stream() + .mapToDouble(Employee::getSalary) + .sum(); + } + + /** + * 计算员工平均薪资 + * @param employees 员工列表 + * @return 平均薪资 + */ + public double calculateAverageSalary(List employees) { + if (employees.isEmpty()) { + return 0; + } + return calculateTotalSalary(employees) / employees.size(); + } + + /** + * 获取最高薪资 + * @param employees 员工列表 + * @return 最高薪资 + */ + public double getMaxSalary(List employees) { + if (employees.isEmpty()) { + return 0; + } + return employees.stream() + .mapToDouble(Employee::getSalary) + .max() + .orElse(0); + } + + /** + * 获取最低薪资 + * @param employees 员工列表 + * @return 最低薪资 + */ + public double getMinSalary(List employees) { + if (employees.isEmpty()) { + return 0; + } + return employees.stream() + .mapToDouble(Employee::getSalary) + .min() + .orElse(0); + } + + /** + * 计算部门薪资分布 + * @param employees 员工列表 + * @return 部门薪资分布映射(部门 -> 薪资统计) + */ + public Map calculateDepartmentSalaryDistribution(List employees) { + return employees.stream() + .collect(Collectors.groupingBy(Employee::getDepartment)) + .entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + entry -> { + List deptEmployees = entry.getValue(); + double totalSalary = deptEmployees.stream().mapToDouble(Employee::getSalary).sum(); + double avgSalary = deptEmployees.isEmpty() ? 0 : totalSalary / deptEmployees.size(); + double maxSalary = deptEmployees.stream().mapToDouble(Employee::getSalary).max().orElse(0); + double minSalary = deptEmployees.stream().mapToDouble(Employee::getSalary).min().orElse(0); + return new DepartmentSalaryStats(totalSalary, avgSalary, maxSalary, minSalary, deptEmployees.size()); + } + )); + } + + /** + * 计算员工年龄分布 + * @param employees 员工列表 + * @return 年龄分布映射(年龄段 -> 人数) + */ + public Map calculateAgeDistribution(List employees) { + return employees.stream() + .collect(Collectors.groupingBy( + e -> { + int age = e.getAge(); + if (age < 25) return "25岁以下"; + else if (age < 35) return "25-34岁"; + else if (age < 45) return "35-44岁"; + else if (age < 55) return "45-54岁"; + else return "55岁以上"; + }, + Collectors.counting() + )); + } + + /** + * 计算员工司龄分布 + * @param employees 员工列表 + * @return 司龄分布映射(司龄段 -> 人数) + */ + public Map calculateTenureDistribution(List employees) { + return employees.stream() + .collect(Collectors.groupingBy( + e -> { + int tenure = e.getTenure(); + if (tenure < 1) return "不足1年"; + else if (tenure < 3) return "1-2年"; + else if (tenure < 5) return "3-4年"; + else if (tenure < 10) return "5-9年"; + else return "10年以上"; + }, + Collectors.counting() + )); + } + + /** + * 计算部门人数分布 + * @param employees 员工列表 + * @return 部门人数分布映射(部门 -> 人数) + */ + public Map calculateDepartmentHeadcount(List employees) { + return employees.stream() + .collect(Collectors.groupingBy(Employee::getDepartment, Collectors.counting())); + } + + /** + * 获取指定年份入职的员工数 + * @param employees 员工列表 + * @param year 年份 + * @return 该年份入职的员工数 + */ + public long getHireCountByYear(List employees, int year) { + return employees.stream() + .filter(e -> e.getHireDate().getYear() == year) + .count(); + } + + /** + * 计算性别分布(如果Employee类中有性别属性) + * 这里作为示例,假设Employee类有gender属性 + * @param employees 员工列表 + * @return 性别分布映射(性别 -> 人数) + */ + // 由于Employee类中没有性别属性,此方法作为示例保留 + public Map calculateGenderDistribution(List employees) { + // 实际实现需要在Employee类中添加gender属性 + System.out.println("注意:Employee类中暂未添加性别属性,无法计算性别分布"); + return new HashMap<>(); + } + + /** + * 获取薪资排名前N的员工 + * @param employees 员工列表 + * @param n 数量 + * @return 薪资最高的N名员工 + */ + public List getTopNSalaryEmployees(List employees, int n) { + if (n <= 0) { + throw new IllegalArgumentException("N必须为正整数"); + } + + return employees.stream() + .sorted(Comparator.comparingDouble(Employee::getSalary).reversed()) + .limit(n) + .collect(Collectors.toList()); + } + + /** + * 计算在职率 + * @param employees 员工列表 + * @return 在职率(百分比) + */ + public double calculateActiveRate(List employees) { + if (employees.isEmpty()) { + return 0; + } + long activeCount = employees.stream() + .filter(Employee::isActive) + .count(); + return (double) activeCount / employees.size() * 100; + } + + /** + * 部门薪资统计内部类 + */ + public static class DepartmentSalaryStats { + private final double totalSalary; + private final double averageSalary; + private final double maxSalary; + private final double minSalary; + private final int headcount; + + public DepartmentSalaryStats(double totalSalary, double averageSalary, + double maxSalary, double minSalary, int headcount) { + this.totalSalary = totalSalary; + this.averageSalary = averageSalary; + this.maxSalary = maxSalary; + this.minSalary = minSalary; + this.headcount = headcount; + } + + public double getTotalSalary() { return totalSalary; } + public double getAverageSalary() { return averageSalary; } + public double getMaxSalary() { return maxSalary; } + public double getMinSalary() { return minSalary; } + public int getHeadcount() { return headcount; } + + @Override + public String toString() { + return "部门薪资统计{" + + "总薪资= " + totalSalary + + ", 平均薪资= " + String.format("%.2f", averageSalary) + + ", 最高薪资= " + maxSalary + + ", 最低薪资= " + minSalary + + ", 人数= " + headcount + + "}"; + } + } +} \ No newline at end of file diff --git a/EmployeeSystem/EmployeeSystemDemo.class b/EmployeeSystem/EmployeeSystemDemo.class new file mode 100644 index 0000000..38b32ec Binary files /dev/null and b/EmployeeSystem/EmployeeSystemDemo.class differ diff --git a/EmployeeSystem/EmployeeSystemDemo.java b/EmployeeSystem/EmployeeSystemDemo.java new file mode 100644 index 0000000..1aee560 --- /dev/null +++ b/EmployeeSystem/EmployeeSystemDemo.java @@ -0,0 +1,216 @@ +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.*; + +/** + * 员工管理系统演示类 - 展示所有功能模块的使用 + */ +public class EmployeeSystemDemo { + + public static void main(String[] args) { + System.out.println("========================================"); + System.out.println("员工管理系统演示(单一职责原则实现)"); + System.out.println("========================================"); + + // 创建演示数据和服务 + List employees = createSampleEmployees(); + + // 演示基础功能 + demonstrateBasicFeatures(employees); + + // 演示进阶功能 + demonstrateAdvancedFeatures(employees); + + // 演示挑战功能 + demonstrateChallengeFeatures(employees); + + System.out.println("\n========================================"); + System.out.println("演示完成!"); + System.out.println("========================================"); + } + + /** + * 创建示例员工数据 + */ + private static List createSampleEmployees() { + List employees = new ArrayList<>(); + + employees.add(new Employee("EMP001", "张三", 15000.0, "技术部", "高级工程师", + LocalDate.of(2020, 5, 15), LocalDate.of(1990, 3, 20), "zhangsan@company.com")); + + employees.add(new Employee("EMP002", "李四", 12000.0, "技术部", "工程师", + LocalDate.of(2021, 8, 10), LocalDate.of(1995, 6, 12), "lisi@company.com")); + + employees.add(new Employee("EMP003", "王五", 18000.0, "技术部", "技术经理", + LocalDate.of(2018, 12, 20), LocalDate.of(1985, 11, 5), "wangwu@company.com")); + + employees.add(new Employee("EMP004", "赵六", 9000.0, "人事部", "人事专员", + LocalDate.of(2022, 3, 1), LocalDate.of(1998, 2, 28), "zhaoliu@company.com")); + + employees.add(new Employee("EMP005", "钱七", 13000.0, "财务部", "财务主管", + LocalDate.of(2019, 10, 8), LocalDate.of(1988, 9, 15), "qianqi@company.com")); + + return employees; + } + + /** + * 演示基础功能 + */ + private static void demonstrateBasicFeatures(List employees) { + System.out.println("\n【基础功能演示】"); + System.out.println("----------------------------------------"); + + // 1. Employee类的基本功能演示 + System.out.println("1. 员工信息和邮箱验证:"); + for (Employee emp : employees) { + System.out.println(emp); + } + + // 2. 尝试设置无效邮箱 + try { + Employee testEmp = employees.get(0); + System.out.println("\n2. 邮箱验证测试:"); + System.out.println("尝试设置无效邮箱..."); + testEmp.setEmail("invalid-email"); + } catch (IllegalArgumentException e) { + System.out.println("验证成功:" + e.getMessage()); + } + + // 3. EmailService功能演示 + System.out.println("\n3. 邮件通知服务演示:"); + EmailService emailService = new EmailService(); + + // 发送入职通知 + emailService.sendWelcomeEmail("EMP006", "孙八", "sunba@company.com", + "市场部", "2024-01-15"); + + // 发送薪资调整通知 + emailService.sendSalaryAdjustmentEmail("EMP001", "张三", "zhangsan@company.com", + 15000.0, 17000.0, "2024-02-01"); + } + + /** + * 演示进阶功能 + */ + private static void demonstrateAdvancedFeatures(List employees) { + System.out.println("\n【进阶功能演示】"); + System.out.println("----------------------------------------"); + + // 1. EmployeeSearch功能演示 + System.out.println("1. 员工搜索功能:"); + EmployeeSearch search = new EmployeeSearch(); + + // 按薪资范围搜索 + List highSalaryEmployees = search.searchBySalaryRange(employees, 13000, 20000); + System.out.println("\n薪资13000-20000的员工:"); + highSalaryEmployees.forEach(System.out::println); + + // 按部门搜索 + List techEmployees = search.searchByDepartment(employees, "技术部"); + System.out.println("\n技术部员工:"); + techEmployees.forEach(System.out::println); + + // 2. EmployeeStatistics功能演示 + System.out.println("\n2. 员工统计功能:"); + EmployeeStatistics stats = new EmployeeStatistics(); + + System.out.println("薪资统计:"); + System.out.printf("总薪资: %.2f\n", stats.calculateTotalSalary(employees)); + System.out.printf("平均薪资: %.2f\n", stats.calculateAverageSalary(employees)); + System.out.printf("最高薪资: %.2f\n", stats.getMaxSalary(employees)); + System.out.printf("最低薪资: %.2f\n", stats.getMinSalary(employees)); + + // 部门薪资分布 + System.out.println("\n部门薪资分布:"); + Map deptStats = + stats.calculateDepartmentSalaryDistribution(employees); + deptStats.forEach((dept, data) -> { + System.out.println(dept + ": " + data); + }); + + // 年龄分布 + System.out.println("\n员工年龄分布:"); + Map ageDist = stats.calculateAgeDistribution(employees); + ageDist.forEach((range, count) -> { + System.out.println(range + ": " + count + "人"); + }); + } + + /** + * 演示挑战功能 + */ + private static void demonstrateChallengeFeatures(List employees) { + System.out.println("\n【挑战功能演示】"); + System.out.println("----------------------------------------"); + + // 1. EmployeeHistory功能演示 + System.out.println("1. 员工历史记录功能:"); + EmployeeHistory empHistory = new EmployeeHistory("EMP001"); + + // 记录各种变更 + empHistory.recordSalaryChange(15000.0, 17000.0, "年度调薪", "HR-001"); + empHistory.recordPositionChange("高级工程师", "资深工程师", "晋升", "HR-001"); + empHistory.recordDepartmentChange("技术部", "研发部", "部门调整", "HR-001"); + + // 显示历史记录 + System.out.println("\n员工EMP001的历史记录:"); + List records = empHistory.getAllHistory(); + for (EmployeeHistory.ChangeRecord record : records) { + System.out.println(record); + } + + // 2. 培训管理系统功能演示 + System.out.println("\n创建培训课程:"); + TrainingCourse course1 = new TrainingCourse("COURSE001", "Java高级编程", + "Java企业级开发高级技术", 20, "张讲师", "技术部", "技术培训", 3); + + TrainingCourse course2 = new TrainingCourse("COURSE002", "项目管理基础", + "项目管理理论与实践", 15, "李讲师", "管理部", "管理培训", 2); + + TrainingManagementSystem trainingSystem = new TrainingManagementSystem(); + trainingSystem.addCourse(course1); + trainingSystem.addCourse(course2); + + // 员工报名 + System.out.println("\n员工报名课程:"); + String recordId1 = trainingSystem.enrollEmployee("EMP001", "COURSE001", + "张三", "zhangsan@company.com"); + + // 开始培训 + System.out.println("\n开始培训:"); + trainingSystem.startTraining(recordId1, LocalDate.now().minusDays(5)); + + // 完成培训(成绩合格,自动颁发证书) + System.out.println("\n完成培训并颁发证书:"); + trainingSystem.completeTraining(recordId1, LocalDate.now(), 92.5, + "张三", "zhangsan@company.com"); + + // 查看证书 + List trainingRecords = trainingSystem.getCourseTrainingRecords("COURSE001"); + Certificate certificate = null; + if (!trainingRecords.isEmpty()) { + TrainingRecord record = trainingRecords.get(0); + certificate = trainingSystem.getCertificate(record.getCertificateId()); + System.out.println("\n颁发的证书信息:"); + System.out.println(certificate); + } + + // 生成培训报告 + System.out.println("\n培训系统统计报告:"); + Map report = trainingSystem.generateTrainingReport(); + report.forEach((key, value) -> { + System.out.println(key + ": " + value); + }); + + // 3. 综合演示:员工培训历史记录 + System.out.println("\n3. 综合功能演示 - 员工培训历史:"); + EmployeeHistory empTrainingHistory = new EmployeeHistory("EMP001"); + String certId = certificate != null ? certificate.getCertificateId() : "CERT-N/A"; + empTrainingHistory.recordTrainingCompletion("Java高级编程", certId, 92.5, "TRAIN-001"); + + System.out.println("\n员工培训完成记录:"); + List changeRecords = + empTrainingHistory.getHistoryByType(EmployeeHistory.ChangeType.TRAINING_COMPLETION); + changeRecords.forEach(System.out::println); + } +} \ No newline at end of file diff --git a/EmployeeSystem/TrainingCourse.class b/EmployeeSystem/TrainingCourse.class new file mode 100644 index 0000000..2274406 Binary files /dev/null and b/EmployeeSystem/TrainingCourse.class differ diff --git a/EmployeeSystem/TrainingCourse.java b/EmployeeSystem/TrainingCourse.java new file mode 100644 index 0000000..208b8a7 --- /dev/null +++ b/EmployeeSystem/TrainingCourse.java @@ -0,0 +1,155 @@ +import java.time.LocalDate; +import java.time.Duration; + +/** + * 培训课程类 - 负责课程信息管理 + * 遵循单一职责原则:只负责课程数据的存储和基本验证 + */ +public class TrainingCourse { + private String courseId; // 课程ID + private String courseName; // 课程名称 + private String description; // 课程描述 + private int durationHours; // 课程时长(小时) + private String trainer; // 培训师 + private String department; // 所属部门 + private String category; // 课程类别 + private int credits; // 学分 + private boolean active; // 是否启用 + private LocalDate createdAt; // 创建时间 + private String requirements; // 课程要求 + private String objectives; // 课程目标 + private String materials; // 课程资料 + + /** + * 构造函数 - 创建培训课程 + */ + public TrainingCourse(String courseId, String courseName, String description, + int durationHours, String trainer, String department, + String category, int credits) { + this.courseId = courseId; + this.courseName = courseName; + this.description = description; + this.durationHours = durationHours; + this.trainer = trainer; + this.department = department; + this.category = category; + this.credits = credits; + this.active = true; + this.createdAt = LocalDate.now(); + this.requirements = ""; + this.objectives = ""; + this.materials = ""; + } + + /** + * 检查课程是否可报名 + * @return true如果课程可报名 + */ + public boolean isEnrollable() { + return active && durationHours > 0 && credits >= 0; + } + + /** + * 获取课程时长描述 + * @return 课程时长描述 + */ + public String getDurationDescription() { + if (durationHours < 1) { + return "少于1小时"; + } else if (durationHours == 1) { + return "1小时"; + } else { + return durationHours + "小时"; + } + } + + /** + * 获取课程等级(基于学分) + * @return 课程等级 + */ + public String getLevel() { + if (credits <= 1) return "初级"; + else if (credits <= 3) return "中级"; + else return "高级"; + } + + // Getter和Setter方法 + + public String getCourseId() { return courseId; } + + public String getCourseName() { return courseName; } + + public void setCourseName(String courseName) { + if (courseName == null || courseName.trim().isEmpty()) { + throw new IllegalArgumentException("课程名称不能为空"); + } + this.courseName = courseName; + } + + public String getDescription() { return description; } + + public void setDescription(String description) { this.description = description; } + + public int getDurationHours() { return durationHours; } + + public void setDurationHours(int durationHours) { + if (durationHours < 0) { + throw new IllegalArgumentException("课程时长不能为负数"); + } + this.durationHours = durationHours; + } + + public String getTrainer() { return trainer; } + + public void setTrainer(String trainer) { this.trainer = trainer; } + + public String getDepartment() { return department; } + + public void setDepartment(String department) { this.department = department; } + + public String getCategory() { return category; } + + public void setCategory(String category) { this.category = category; } + + public int getCredits() { return credits; } + + public void setCredits(int credits) { + if (credits < 0) { + throw new IllegalArgumentException("学分不能为负数"); + } + this.credits = credits; + } + + public boolean isActive() { return active; } + + public void setActive(boolean active) { this.active = active; } + + public LocalDate getCreatedAt() { return createdAt; } + + public String getRequirements() { return requirements; } + + public void setRequirements(String requirements) { this.requirements = requirements; } + + public String getObjectives() { return objectives; } + + public void setObjectives(String objectives) { this.objectives = objectives; } + + public String getMaterials() { return materials; } + + public void setMaterials(String materials) { this.materials = materials; } + + @Override + public String toString() { + return "TrainingCourse{" + + "courseId='" + courseId + "'" + + ", courseName='" + courseName + "'" + + ", category='" + category + "'" + + ", level='" + getLevel() + "'" + + ", duration=" + getDurationDescription() + + ", trainer='" + trainer + "'" + + ", credits=" + credits + + ", department='" + department + "'" + + ", active=" + (active ? "启用" : "禁用") + + "}"; + } +} \ No newline at end of file diff --git a/EmployeeSystem/TrainingManagementSystem.class b/EmployeeSystem/TrainingManagementSystem.class new file mode 100644 index 0000000..6559b62 Binary files /dev/null and b/EmployeeSystem/TrainingManagementSystem.class differ diff --git a/EmployeeSystem/TrainingManagementSystem.java b/EmployeeSystem/TrainingManagementSystem.java new file mode 100644 index 0000000..f42f0fa --- /dev/null +++ b/EmployeeSystem/TrainingManagementSystem.java @@ -0,0 +1,422 @@ +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 培训管理系统类 - 负责培训相关业务逻辑协调 + * 遵循单一职责原则:只负责培训业务流程的协调和管理 + */ +public class TrainingManagementSystem { + // 数据存储 + private final Map courses; + private final Map trainingRecords; + private final Map certificates; + private final EmailService emailService; + private final Map> employeeRecords; // 员工培训记录映射 + + /** + * 构造函数 + */ + public TrainingManagementSystem() { + this.courses = new HashMap<>(); + this.trainingRecords = new HashMap<>(); + this.certificates = new HashMap<>(); + this.emailService = new EmailService(); + this.employeeRecords = new HashMap<>(); + } + + // ====================== 课程管理功能 ====================== + + /** + * 添加课程 + * @param course 课程对象 + * @return true如果添加成功 + */ + public boolean addCourse(TrainingCourse course) { + if (courses.containsKey(course.getCourseId())) { + System.out.println("课程ID已存在: " + course.getCourseId()); + return false; + } + courses.put(course.getCourseId(), course); + System.out.println("课程添加成功: " + course.getCourseName()); + return true; + } + + /** + * 获取课程 + * @param courseId 课程ID + * @return 课程对象,如果不存在则返回null + */ + public TrainingCourse getCourse(String courseId) { + return courses.get(courseId); + } + + /** + * 更新课程信息 + * @param course 课程对象 + * @return true如果更新成功 + */ + public boolean updateCourse(TrainingCourse course) { + if (!courses.containsKey(course.getCourseId())) { + System.out.println("课程不存在: " + course.getCourseId()); + return false; + } + courses.put(course.getCourseId(), course); + System.out.println("课程更新成功: " + course.getCourseName()); + return true; + } + + /** + * 删除课程 + * @param courseId 课程ID + * @return true如果删除成功 + */ + public boolean deleteCourse(String courseId) { + if (!courses.containsKey(courseId)) { + System.out.println("课程不存在: " + courseId); + return false; + } + // 检查是否有相关的培训记录 + boolean hasRecords = trainingRecords.values().stream() + .anyMatch(record -> record.getCourseId().equals(courseId)); + if (hasRecords) { + System.out.println("无法删除课程:存在相关的培训记录"); + return false; + } + courses.remove(courseId); + System.out.println("课程删除成功: " + courseId); + return true; + } + + /** + * 获取所有启用的课程 + * @return 启用的课程列表 + */ + public List getActiveCourses() { + return courses.values().stream() + .filter(TrainingCourse::isActive) + .collect(Collectors.toList()); + } + + /** + * 按部门获取课程 + * @param department 部门 + * @return 部门的课程列表 + */ + public List getCoursesByDepartment(String department) { + return courses.values().stream() + .filter(course -> course.getDepartment().equals(department)) + .collect(Collectors.toList()); + } + + // ====================== 培训记录管理功能 ====================== + + /** + * 员工报名课程 + * @param employeeId 员工ID + * @param courseId 课程ID + * @param employeeName 员工姓名 + * @param employeeEmail 员工邮箱 + * @return 培训记录ID + */ + public String enrollEmployee(String employeeId, String courseId, + String employeeName, String employeeEmail) { + // 检查课程是否存在且可报名 + TrainingCourse course = courses.get(courseId); + if (course == null || !course.isEnrollable()) { + System.out.println("课程不存在或不可报名: " + courseId); + return null; + } + + // 生成记录ID + String recordId = generateRecordId(employeeId, courseId); + + // 创建培训记录 + TrainingRecord record = new TrainingRecord(recordId, employeeId, courseId); + trainingRecords.put(recordId, record); + + // 添加到员工培训记录映射 + employeeRecords.computeIfAbsent(employeeId, k -> new ArrayList<>()).add(record); + + // 发送报名通知邮件 + emailService.sendTrainingNotificationEmail(employeeId, employeeName, employeeEmail, + course.getCourseName(), LocalDate.now().toString(), course.getTrainer()); + + System.out.println("员工报名成功: " + employeeId + " -> " + course.getCourseName()); + return recordId; + } + + /** + * 开始培训 + * @param recordId 培训记录ID + * @param startDate 开始日期 + * @return true如果开始成功 + */ + public boolean startTraining(String recordId, LocalDate startDate) { + TrainingRecord record = trainingRecords.get(recordId); + if (record == null) { + System.out.println("培训记录不存在: " + recordId); + return false; + } + + try { + TrainingCourse course = courses.get(record.getCourseId()); + record.startTraining(startDate, course.getTrainer()); + System.out.println("培训开始成功: " + recordId); + return true; + } catch (Exception e) { + System.out.println("培训开始失败: " + e.getMessage()); + return false; + } + } + + /** + * 完成培训 + * @param recordId 培训记录ID + * @param completionDate 完成日期 + * @param score 成绩 + * @param employeeName 员工姓名 + * @param employeeEmail 员工邮箱 + * @return true如果完成成功 + */ + public boolean completeTraining(String recordId, LocalDate completionDate, double score, + String employeeName, String employeeEmail) { + TrainingRecord record = trainingRecords.get(recordId); + if (record == null) { + System.out.println("培训记录不存在: " + recordId); + return false; + } + + try { + record.completeTraining(completionDate, score); + + // 如果成绩合格,自动颁发证书 + if (record.isPassed()) { + issueCertificate(recordId, employeeName, employeeEmail); + } + + System.out.println("培训完成成功: " + recordId); + return true; + } catch (Exception e) { + System.out.println("培训完成失败: " + e.getMessage()); + return false; + } + } + + /** + * 取消培训 + * @param recordId 培训记录ID + * @param reason 取消原因 + * @return true如果取消成功 + */ + public boolean cancelTraining(String recordId, String reason) { + TrainingRecord record = trainingRecords.get(recordId); + if (record == null) { + System.out.println("培训记录不存在: " + recordId); + return false; + } + + try { + record.cancelTraining(reason); + System.out.println("培训取消成功: " + recordId); + return true; + } catch (Exception e) { + System.out.println("培训取消失败: " + e.getMessage()); + return false; + } + } + + // ====================== 证书管理功能 ====================== + + /** + * 颁发证书 + * @param recordId 培训记录ID + * @param employeeName 员工姓名 + * @param employeeEmail 员工邮箱 + * @return 证书ID + */ + public String issueCertificate(String recordId, String employeeName, String employeeEmail) { + TrainingRecord record = trainingRecords.get(recordId); + if (record == null) { + System.out.println("培训记录不存在: " + recordId); + return null; + } + + TrainingCourse course = courses.get(record.getCourseId()); + + try { + // 生成证书ID + String certificateId = generateCertificateId(record.getEmployeeId(), course.getCourseId()); + + // 创建证书 + Certificate certificate = new Certificate(certificateId, + record.getEmployeeId(), + course.getCourseId(), + course.getCourseName(), + "公司培训部门", + course.getTrainer(), + record.getScore()); + + // 设置证书有效期(默认3年) + certificate.setValidYears(3); + + // 颁发证书 + record.issueCertificate(certificateId); + certificates.put(certificateId, certificate); + + // 发送证书通知邮件 + emailService.sendCertificateEmail(record.getEmployeeId(), employeeName, employeeEmail, + course.getCourseName(), LocalDate.now().toString()); + + System.out.println("证书颁发成功: " + certificateId); + return certificateId; + } catch (Exception e) { + System.out.println("证书颁发失败: " + e.getMessage()); + return null; + } + } + + /** + * 获取证书 + * @param certificateId 证书ID + * @return 证书对象 + */ + public Certificate getCertificate(String certificateId) { + return certificates.get(certificateId); + } + + /** + * 吊销证书 + * @param certificateId 证书ID + * @param reason 吊销原因 + * @return true如果吊销成功 + */ + public boolean revokeCertificate(String certificateId, String reason) { + Certificate certificate = certificates.get(certificateId); + if (certificate == null) { + System.out.println("证书不存在: " + certificateId); + return false; + } + + certificate.revokeCertificate(reason); + System.out.println("证书吊销成功: " + certificateId); + return true; + } + + // ====================== 查询功能 ====================== + + /** + * 获取员工的所有培训记录 + * @param employeeId 员工ID + * @return 培训记录列表 + */ + public List getEmployeeTrainingRecords(String employeeId) { + return employeeRecords.getOrDefault(employeeId, new ArrayList<>()); + } + + /** + * 获取员工的有效证书 + * @param employeeId 员工ID + * @return 有效证书列表 + */ + public List getEmployeeActiveCertificates(String employeeId) { + return certificates.values().stream() + .filter(cert -> cert.getEmployeeId().equals(employeeId) && + cert.isActive() && !cert.isExpired()) + .collect(Collectors.toList()); + } + + /** + * 获取课程的所有培训记录 + * @param courseId 课程ID + * @return 培训记录列表 + */ + public List getCourseTrainingRecords(String courseId) { + return trainingRecords.values().stream() + .filter(record -> record.getCourseId().equals(courseId)) + .collect(Collectors.toList()); + } + + /** + * 生成培训统计报告 + * @return 培训统计信息 + */ + public Map generateTrainingReport() { + Map report = new HashMap<>(); + + // 总课程数 + report.put("totalCourses", courses.size()); + + // 启用课程数 + report.put("activeCourses", getActiveCourses().size()); + + // 总培训记录数 + report.put("totalRecords", trainingRecords.size()); + + // 各状态记录数 + Map statusCount = trainingRecords.values().stream() + .collect(Collectors.groupingBy(TrainingRecord::getStatus, Collectors.counting())); + report.put("statusCount", statusCount); + + // 总证书数 + report.put("totalCertificates", certificates.size()); + + // 有效证书数 + long activeCertificates = certificates.values().stream() + .filter(cert -> cert.isActive() && !cert.isExpired()) + .count(); + report.put("activeCertificates", activeCertificates); + + // 平均成绩 + Double averageScore = trainingRecords.values().stream() + .filter(record -> record.getScore() != null) + .mapToDouble(TrainingRecord::getScore) + .average() + .orElse(0); + report.put("averageScore", averageScore); + + return report; + } + + // ====================== 辅助方法 ====================== + + /** + * 生成培训记录ID + */ + private String generateRecordId(String employeeId, String courseId) { + return String.format("REC-%s-%s-%d", + employeeId, courseId, System.currentTimeMillis()); + } + + /** + * 生成证书ID + */ + private String generateCertificateId(String employeeId, String courseId) { + return String.format("CERT-%s-%s-%d", + employeeId, courseId, System.currentTimeMillis()); + } + + /** + * 获取系统中的员工数量 + * @return 员工数量 + */ + public int getEmployeeCount() { + return employeeRecords.size(); + } + + /** + * 获取即将到期的证书 + * @param days 天数 + * @return 即将到期的证书列表 + */ + public List getExpiringCertificates(int days) { + LocalDate futureDate = LocalDate.now().plusDays(days); + return certificates.values().stream() + .filter(cert -> cert.isActive() && + cert.getExpiryDate() != null && + !cert.isExpired() && + cert.getExpiryDate().isBefore(futureDate)) + .collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/EmployeeSystem/TrainingRecord$RecordStatus.class b/EmployeeSystem/TrainingRecord$RecordStatus.class new file mode 100644 index 0000000..9406fe7 Binary files /dev/null and b/EmployeeSystem/TrainingRecord$RecordStatus.class differ diff --git a/EmployeeSystem/TrainingRecord.class b/EmployeeSystem/TrainingRecord.class new file mode 100644 index 0000000..aaa6340 Binary files /dev/null and b/EmployeeSystem/TrainingRecord.class differ diff --git a/EmployeeSystem/TrainingRecord.java b/EmployeeSystem/TrainingRecord.java new file mode 100644 index 0000000..71cabb8 --- /dev/null +++ b/EmployeeSystem/TrainingRecord.java @@ -0,0 +1,189 @@ +import java.time.LocalDate; +import java.time.LocalDateTime; + +/** + * 培训记录类 - 负责员工培训记录管理 + * 遵循单一职责原则:只负责培训记录数据的存储和状态管理 + */ +public class TrainingRecord { + private String recordId; // 记录ID + private String employeeId; // 员工ID + private String courseId; // 课程ID + private LocalDate enrollmentDate; // 报名日期 + private LocalDate startDate; // 开始日期 + private LocalDate completionDate; // 完成日期 + private Double score; // 成绩 + private String certificateId; // 证书ID + private RecordStatus status; // 记录状态 + private String trainer; // 培训师 + private String notes; // 备注 + private LocalDateTime createdAt; // 创建时间 + private LocalDateTime updatedAt; // 更新时间 + + /** + * 构造函数 - 创建培训记录 + */ + public TrainingRecord(String recordId, String employeeId, String courseId) { + this.recordId = recordId; + this.employeeId = employeeId; + this.courseId = courseId; + this.enrollmentDate = LocalDate.now(); + this.status = RecordStatus.ENROLLED; + this.createdAt = LocalDateTime.now(); + this.updatedAt = LocalDateTime.now(); + this.notes = ""; + } + + /** + * 开始培训 + * @param startDate 开始日期 + * @param trainer 培训师 + */ + public void startTraining(LocalDate startDate, String trainer) { + if (this.status != RecordStatus.ENROLLED) { + throw new IllegalStateException("只有已报名的记录才能开始培训"); + } + this.startDate = startDate; + this.trainer = trainer; + this.status = RecordStatus.IN_PROGRESS; + this.updatedAt = LocalDateTime.now(); + } + + /** + * 完成培训 + * @param completionDate 完成日期 + * @param score 成绩 + */ + public void completeTraining(LocalDate completionDate, Double score) { + if (this.status != RecordStatus.IN_PROGRESS) { + throw new IllegalStateException("只有进行中的记录才能完成培训"); + } + if (score != null && (score < 0 || score > 100)) { + throw new IllegalArgumentException("成绩必须在0-100之间"); + } + this.completionDate = completionDate; + this.score = score; + this.status = RecordStatus.COMPLETED; + this.updatedAt = LocalDateTime.now(); + } + + /** + * 取消培训 + * @param reason 取消原因 + */ + public void cancelTraining(String reason) { + if (this.status == RecordStatus.COMPLETED) { + throw new IllegalStateException("已完成的培训不能取消"); + } + this.status = RecordStatus.CANCELLED; + this.notes = reason; + this.updatedAt = LocalDateTime.now(); + } + + /** + * 颁发证书 + * @param certificateId 证书ID + * @return + */ + public void issueCertificate(String certificateId) { + if (this.status != RecordStatus.COMPLETED) { + throw new IllegalStateException("只有已完成的培训才能颁发证书"); + } + if (score != null && score < 60) { + throw new IllegalStateException("成绩不合格,无法颁发证书"); + } + this.certificateId = certificateId; + this.updatedAt = LocalDateTime.now(); + } + + /** + * 判断是否合格 + * @return true如果成绩合格 + */ + public boolean isPassed() { + return score != null && score >= 60; + } + + /** + * 获取培训时长(天) + * @return 培训时长 + */ + public Long getTrainingDurationDays() { + if (startDate == null || completionDate == null) { + return null; + } + return (long) startDate.until(completionDate).getDays() + 1; + } + + /** + * 记录状态枚举 + */ + public enum RecordStatus { + ENROLLED, // 已报名 + IN_PROGRESS, // 进行中 + COMPLETED, // 已完成 + CANCELLED // 已取消 + } + + // Getter和Setter方法 + + public String getRecordId() { return recordId; } + + public String getEmployeeId() { return employeeId; } + + public String getCourseId() { return courseId; } + + public LocalDate getEnrollmentDate() { return enrollmentDate; } + + public LocalDate getStartDate() { return startDate; } + + public LocalDate getCompletionDate() { return completionDate; } + + public Double getScore() { return score; } + + public String getCertificateId() { return certificateId; } + + public RecordStatus getStatus() { return status; } + + public String getTrainer() { return trainer; } + + public String getNotes() { return notes; } + + public void setNotes(String notes) { + this.notes = notes; + this.updatedAt = LocalDateTime.now(); + } + + public LocalDateTime getCreatedAt() { return createdAt; } + + public LocalDateTime getUpdatedAt() { return updatedAt; } + + @Override + public String toString() { + return "TrainingRecord{" + + "recordId='" + recordId + "'" + + ", employeeId='" + employeeId + "'" + + ", courseId='" + courseId + "'" + + ", status='" + status.name() + "'" + + ", enrollmentDate=" + enrollmentDate + "'" + + ", completionDate=" + (completionDate != null ? completionDate : "未完成") + "'" + + ", score=" + (score != null ? score + (isPassed() ? "(合格)" : "(不合格)") : "未评分") + "'" + + ", certificateId=" + (certificateId != null ? certificateId : "未颁发") + "'" + + "}"; + } + + public void addCourse(TrainingCourse course2) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'addCourse'"); + } + + public String enrollEmployee(String string, String string2, String string3, String string4) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'enrollEmployee'"); + } + + public LocalDate getCourseTrainingRecords(String string) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getCourseTrainingRecords'"); + } +} \ No newline at end of file