From 7d022bc46f601f42ab52bdd3dda858a360bda1b4 Mon Sep 17 00:00:00 2001 From: Bnavy <2471400297@qq.com> Date: Sun, 26 Oct 2025 21:42:16 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8D=95=E4=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- EmployeeSystem/Certificate.class | Bin 0 -> 4435 bytes EmployeeSystem/Certificate.java | 174 ++++++++ EmployeeSystem/EmailService.class | Bin 0 -> 3535 bytes EmployeeSystem/EmailService.java | 179 ++++++++ EmployeeSystem/Employee.class | Bin 0 -> 3297 bytes EmployeeSystem/Employee.java | 142 ++++++ .../EmployeeHistory$ChangeRecord.class | Bin 0 -> 1408 bytes .../EmployeeHistory$ChangeType.class | Bin 0 -> 1223 bytes EmployeeSystem/EmployeeHistory.class | Bin 0 -> 6780 bytes EmployeeSystem/EmployeeHistory.java | 251 +++++++++++ ...EmployeeSearch$DepartmentSalaryStats.class | Bin 0 -> 372 bytes EmployeeSystem/EmployeeSearch.class | Bin 0 -> 6410 bytes EmployeeSystem/EmployeeSearch.java | 192 ++++++++ ...oyeeStatistics$DepartmentSalaryStats.class | Bin 0 -> 1512 bytes EmployeeSystem/EmployeeStatistics.class | Bin 0 -> 6221 bytes EmployeeSystem/EmployeeStatistics.java | 230 ++++++++++ EmployeeSystem/EmployeeSystemDemo.class | Bin 0 -> 9471 bytes EmployeeSystem/EmployeeSystemDemo.java | 216 +++++++++ EmployeeSystem/TrainingCourse.class | Bin 0 -> 3790 bytes EmployeeSystem/TrainingCourse.java | 155 +++++++ EmployeeSystem/TrainingManagementSystem.class | Bin 0 -> 11612 bytes EmployeeSystem/TrainingManagementSystem.java | 422 ++++++++++++++++++ .../TrainingRecord$RecordStatus.class | Bin 0 -> 1079 bytes EmployeeSystem/TrainingRecord.class | Bin 0 -> 5031 bytes EmployeeSystem/TrainingRecord.java | 189 ++++++++ 25 files changed, 2150 insertions(+) create mode 100644 EmployeeSystem/Certificate.class create mode 100644 EmployeeSystem/Certificate.java create mode 100644 EmployeeSystem/EmailService.class create mode 100644 EmployeeSystem/EmailService.java create mode 100644 EmployeeSystem/Employee.class create mode 100644 EmployeeSystem/Employee.java create mode 100644 EmployeeSystem/EmployeeHistory$ChangeRecord.class create mode 100644 EmployeeSystem/EmployeeHistory$ChangeType.class create mode 100644 EmployeeSystem/EmployeeHistory.class create mode 100644 EmployeeSystem/EmployeeHistory.java create mode 100644 EmployeeSystem/EmployeeSearch$DepartmentSalaryStats.class create mode 100644 EmployeeSystem/EmployeeSearch.class create mode 100644 EmployeeSystem/EmployeeSearch.java create mode 100644 EmployeeSystem/EmployeeStatistics$DepartmentSalaryStats.class create mode 100644 EmployeeSystem/EmployeeStatistics.class create mode 100644 EmployeeSystem/EmployeeStatistics.java create mode 100644 EmployeeSystem/EmployeeSystemDemo.class create mode 100644 EmployeeSystem/EmployeeSystemDemo.java create mode 100644 EmployeeSystem/TrainingCourse.class create mode 100644 EmployeeSystem/TrainingCourse.java create mode 100644 EmployeeSystem/TrainingManagementSystem.class create mode 100644 EmployeeSystem/TrainingManagementSystem.java create mode 100644 EmployeeSystem/TrainingRecord$RecordStatus.class create mode 100644 EmployeeSystem/TrainingRecord.class create mode 100644 EmployeeSystem/TrainingRecord.java diff --git a/EmployeeSystem/Certificate.class b/EmployeeSystem/Certificate.class new file mode 100644 index 0000000000000000000000000000000000000000..9bfcb8b1cb7eb33933135b39428de3034a491ca1 GIT binary patch literal 4435 zcmcIm`F~W^75}~@GxIWe6Oth;A#9>FVUje~DncB3)E#3Eh`+P{izo5Uh{hs?~c^Rht_)9;@=iGD8J@nOQO9jP$xY&rQ;PZX5zSx zS9P4$@tUlD-HUJFbuZ4~th;5^R>lnI_@=w1OR3b)xmzCEswO&y+%3y9_BV9A>A_oG zCb?b5+cG9~q;w4H7}4>L7|!dsAjebR`qTROfi*6--}u2{e4!S^-zLT20w z_l843%WUt_;O|%$+z@Pv1fzXTomM;??OUY5YxWOBVw+6mp@(9F@q|*nwz@pnZ)zwF zClZ5Z8*wyDvQeyXzuD9g3k4%$RzMpE!tqVElUr?wYw%d{U^r@0b0tEtIQMI98r;E< z72Y6|M>JGA!dyPszeb$Pddx&99v-m5u_)c|3P;1%-5Lh!ZcO3-XK9)Is@U#Are ztzQ-#NT1FdSt9i|d9THF^Q4S%b3<&sGaBAifj9)MDsG0O32S`t3P=|FOpB*kgNZih zB~;ZFjCT^fSDO92WcHH^ye+tiar?))cf?e)`2+QK-ostRShHh2II2yJzvyshLN>Bwt-Aj8{riHy+$WC;ws9aRt z*wlJBu{hnwnYJjWd0b7)2{_K6TQU1(k_Y&PJ$R2HbTa*rd2d*{uEGKPQF+b= zK8;=jOVMuNei09d=n(Osh=)Y1M5hPu8~6d@29{yDfi663;4}EFfmLYN00WZ80#Ow=3 z7RUPr`^~7eWMjxoKj_J0M|Y1M*fnD>A;*hI{fDK%R7mA?eO;N=eAva=hd-;C&u17Ft+XG(NlYQt#laO zzvqMJw%WSwOm?w@G4MP5-h)3F_@m_YC&}y2T)leng=?p|#eIGExzSfHiDl&MwX?ga zjPBbrcH$z@u8!=@=u>im+BbR6U^vnvoL1wwx-Fp`X|(q=*BbZ>{;FYeZNqHGirqKO zwWJ?eP_IDxK4ZyIw^&N)qd2x%(EdhptuwG5Yk8~l8fIr1T&@0HLpj~e>DC(t2a@{-C8{VF7M@)PZ96`ywZB_M=%k5E)FQ33_VX{b`@^sq%Oa8KKvbSm&k> zQ!b&k@mMt0)T)NuCsat>H5j$R5e@S{jK3YMf_#vbkM(3TcG&D%+nijUtD+FHhKbqO z)sKQSOS^*u17?)3zp+4xZmdVT!Fl-fy0-tdJPz)^mNx^3oA`sKlD~}%6bZdiD-woN zBS}t;U>I7U zK8d10LlVV-#w1Dt)g?)|0@bc0+<|I$5}v?RPZD}y1f@xM16(Z&1nL`xVI<)T_{)=+ z@CLCoJW4qqKFVt1PvZ$SD93b6#0*+yViy1AVm9iT_idPiJ8?4>;}+InF7qZ+r=xoq z9_|M|#uJzOE8Le**M*J=2Lf-yTeBmDX-U*bQbj5$8|@D6 zna_k4Fs+4H&DCOPY38#f{SfDa<&`t#VJ(;Eb{KB|O+$izh}xVX|IJc) zyH=AK6j)~ufxIkDC5uzVtX9)XSg*i_u`UhU!CeA7M~d_wyGvoZw4rR>Zb@P8juCt$ zcY>XKKW`$63JheB%Oya53)09jo+ikPh^$wk_TUkQI&a9H1=Oq|J!@36ru6JqHM?!d z|MA?Bc2XO8W(2T>ZSc4{+KrNOpU=m`BH6=da3+VlcwAAgE8WVtjk3R>YjM8o zlXQ(1bS=qu-9gt_L04D4>rT23WE*W}y3yRyW3Jpwwu9YG-!C9eDZJs#%bO4T6rB^3kU1Y5B3@QZYT)r$%lQGu8$Rl_2k1IpzlWBe5M9=%gWvA z=U5b*`0R2WX)5UtsG#(HGjfOY~3Jda>w>Y+X;`E`PJ=Dcmjg zPug~G3N8M{wyvH!U;Jsf{P&2x)%I@_|0NVD+$)(m>GveRW3TZRUdK+HR4;ETivEeH Y2VeR(Q_wuviXu)tkO%f= 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 0000000000000000000000000000000000000000..8a8fc80855a6b87a9f93d225340a46a87188250b GIT binary patch literal 3535 zcmb_eZBr9h6h12< z;4lXT926$N5h&u|OCG+0qZ}M#;W!UE3VVXKC+Xu92gMweuyC3I^?tL}?D(94xQxtm z45$ihl>!4BPnfO3srs67!Cq=Ax1c4lPOw&<7c3RF8sSKd$!uZZQpSnPrYojgi^*D@ zYjoJn*6RE>Wud+#QOY8dUHD0`SQO6VZ;X>N=qZEIWHH&V9;&=tU+1V1tPX|g4)R$7b2~%&aK4o*5tIQQ9>X#CG zGVL!A>`KR?qY2ck49Q!g)0Cb!+Uo5ULXnvkR#F(yDA=!<}Su>U%_I~3|0c=5E6fxpN^tJp)0Y3X%oq}dtl9>^u0C*=O47f<_n zUThpAcidvri0E{Y)`6D+@uaJd_&i4oaZ-96AG$UY>K&2akyvVM7kfRT8!2foFFS(Y zWT6?MkzAS@47PjGBl4h0bl(j4{ek(OP_H*M)c4|PyEN_#^>>QB(^6x1@QFV#H%D6U zN`G7z=Ug(=^M)ow9UGQfrub#OxayV`SaJDfp~At!84VbrR0F#pmjP|?fge2#HS`Au zJi$S)%N%Oy3Ju*;hNflE3F+yX zJQkbSa6 zkcs7NigzX=KVt-m>6WJE!rH$ozhq!m@{UR43&D}L#b{<%i3UMw28SLI?@h7eHtB4_ zH5>3_RkcOJp{L6VQ>G!VTfyHZxHwI=Okj^f#dtou4XR8IYxC2QYKC;j&hav;T3L;Dp8AO;YG6-I&cG(x- zB+ZZ0g{y#cj^k>^4JPOx2+WL$xEr|J$nAENPMV*8Sy(K5t${MQz(B@<*Zi??Q3Kz= zB?dM>Z*Ya0yaE3-CJl87H8ehN=)!|;Y0r``1&8a+mP*0Szho6c#1g%m7}7+k&tuP7G+A#h>SrA9Jb@l zQl(rb8G7OZ1MlH?3bqWk@mW4#9^(rG@8eS~8}a!8q~ntgErxi!i2`J6*BR8>ICdUX zWpO)=WvXmrS-f6lEK}>_jWZB$V6$@cYCY=%^%9^q%$|f#Epn$o7NjEV4){<3KZf8e zg746>hIlQfUJg!i)rcFfSI5B3MBFUchPUc;*oTN2kg4F_#!xmQ?gedvAwj3ssjBmM z9j{Z_l7as*Povc&M#rI3&A?k2icTGFSsfp4Ih`ilCaU#ZIK~a=OM-qwlrK9V0htb? zF9on33SkQrp&zFq56-}Gd@q4gTz+TKvn8#z_4F*=bky{>jSky9BuMV?v z$ja)uC{|h7a%8PQJ#CN+wTQhl216n;B(0g@XJq&V8CsE{4HkPc+2$rP-JvQ))= z1*|LJf{%}n%kz+e;_}T8e)cB_)L-Bym-oyhB&2~4bM86ko_p?l?tRal-~avb7XY{8 z^8gmZ^rI6yMI=NFi?BrO60uuEQba01krt5=b410HDzX98!_<(I(H1cpKoENZ*o&t` zjA?jU#(e=igZ(0&74e*i0~!u$IHcjQhH+W@yoMJvyr|)bSTAXKS+;vcj8`?hCdTU; z-q7%-hPO1lt>GON@2WVez+(+52=(nU_86T>Bc15%x3g9{(WRg^ZKO;EzMPRXvSSLA z9tD9RbHvEnDKl*=&_*&j%eFFUs>4>+>@jS{&vkRzR?6(`%fyYOxS7sBXl3nTH^ytG z3@gdpM%=dcaFH@V;~pz**(($rX`K#dx~FHl>uR0~EN)m;XZZ=VZQ&WL&I}RWj6N%E zZX8Vwn%T|9V3Hm!XWDDrBIZ_sD{F)Owh`aG!5Aql(XHGTBWVp?YndEF!nC^++@hwn ztyje{o*X@!&GcxNPA~Od-q@Vhw&~R>P&vKPxi*ymT}_tv7w(Wbd5eC|6+N}BJq}m~ z^_1OhkRj!E9p@joeBE`NEwebc{__1d6p>XNumYWNt4bu$Xco>wvvS=9CJ-SHK@b)_ zwXFk!P_r{GHQ784d5@Eh{h85h++1tPl?2zMMv|E^)9es3>sW#Jbli$1I&KqjJMPeN zr)YPHxEo7#EE91Lmg`uB)jHN-t&T1%QE)@4A9|BXGhrmVvx!lm{hFuZ=7>xBhEkA= zY}Kfh95S;C8ZI6`aq;MpOYe{Wb?BQ*-+X`Z#7`G~dSh8w#{}Nju>*YqZ?%f!IzABN zLu^yf`S@eq?E^;p(~H}eMt5|yZ;!4lImF!7QLwk~TN2xUla7zDmJHGHF&@?N3D)a4 zA>t&~vk#|^eKnOI|MT2yQ-^>4^W2Y9XFj=j^t-ZS+Gh&J5G%H6qHlIZ`W!UjyGsn#7%wD$zx0(i75G27j zWYYF9+kR9CO1sAx+qBbhDrjQsU1O&!khx`cT`Rt+wfE}NxSs!j#r%2s;gP1G=8?W| zMz)4CvOAoS4dRUK5octZI3rub8QC!$=RhHe@1CWjhK!3 zlnby3O}H5g5k)hLS;Te@a}|5H0AhfvJX{@%M9-pb0(AmJKaJ+|2yQ)(`mJX%BQ$do zv!eVa5ju<6zsk-YXXg-C1Fg`|hMC+WigxtS!<;kcGltJYVV`xhYBufCoqTB0(Wk~Dfq@c|238NZiY%6t60El%;K(Lme9c>y145K^g1{K@ce@= z6}^gwIM|xkK%Ui z{S(kavD5H|+Qr*J!KX})qx=~=$V)x~125q%9CSD}51zjlOe literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..7fd1340559e105524d345690420c4a9f8a35327f GIT binary patch literal 1408 zcma)6Sx*yD6#j0vY0I!!P%I!60WHH)P+VA4B4{*eTqv3}#D}51;*fTR%nX_){sn#U zfd@_WCul;Ekl+vScYumZ+#c}Bcy3FVl!o|n&OLX&vwi2>dH;9g9e@!``r*S#A7q4u z=oLZ{qR)?h42aX97ZC|Ve$-*uk5f1;;S7U6@j%rV_-tm0OE^dNRMBRTuj@KDr&6kA zaf_iXdL_M-GBTWB(=6LCGu!CL`(kZH@$ZmTvJAp zYN(HDI=``;p6BMQI-jDO+68WxdSz%2_a0OMLmfFxm($Ti9#shO5pHIW6S$~{%c#X6838oPXcFQu8YPU%7(DA+4&J1eWgU}z|Hd2@b|Cu|wx z=#VfW;{q;9xI}wYQPbWU&G3X_CTZ*POc%_S>S(dC49>8aTXQbweqQLiZakv7>Z;hS zveF7%dF~r#TD2KQ!~4fGS9X=}WxVPT>nkcmCp|YGy-PQ-OC-}NAb8GcqB^IE?VKi} zNlRcflOV|!vTnmczCfq)7EI~O!lCqM;Zz2)a4A7o7H(x7o-Cv{1Y&3<*-jyVk8t4= zrQV@O{+SYdp^#q@!Z$*-;n-eyp&6mw$|@YMiy=I@%kL!H(LqvFQVw-iLVpk_R2Ax~ zg#ILGXH}@X68ejv$I(@#0I3rCn?T*gsJDnS(biDF>)nJe?iB5<(Q}Bq=;VyWMZd0C z+(XJ8i%X=W*g9%TQS0C&|6Q8iANp(V(iD2gcHjgWNC)UtNB`RT$si#{a2CFQEEYx> literal 0 HcmV?d00001 diff --git a/EmployeeSystem/EmployeeHistory$ChangeType.class b/EmployeeSystem/EmployeeHistory$ChangeType.class new file mode 100644 index 0000000000000000000000000000000000000000..6092243edb0c04aed27b602c707484cb4fab7c85 GIT binary patch literal 1223 zcmah}TTc^F5dKcP?QWN??ZT&o1HWB&Ft)c`TgNDfN3N=kT9-d0+TXAGVZA8#a(Wu z74)g}aGFQVDDZQlpdfG@E3+u`3(sCxn(=o{5H9fPMG8u-R zov*>YTPQcU)=(sRL8z3hJf|LgnUZZ8jqMGqnk#IS$s>y#*%u6xk<3%YRRew-rp;jqUh()n;kv zs#$4Ni`IOJEAKt^e#W_a4a0n6LK@+ zxf#L$!=;nSaG?yYV!2{-5s_$H6WNU|t61mTajGc8O#6hylEvB%w#NN$omgUu_P3Wl z3OV8D5&=R|DUgH;B&`BTu0T=@bO!h&=%fS>uHY&dMu?3N@=I-ky@TTr&Nsvuu90<% zXGatt#`RXbLwMD0Y4kl@hmg+3JNN|Q5cxxJ(oK3t$ASlNzde&YLLLXAP%ujNSk!A0 z$*)%*A?3Se=K+*`cmz=;K{^D{T!MH7x#SkaCrGC(NS7epiXc6m?oX7@*`y`ROHFi` zc~=u&Gw*J~XXfQ5{Sxv@6FufW*on|#$Ry<}y!b||@SQsQLBFJ*(D4ffj;Ooi7RGRk S;vBe*%Y-VmB4G;s3I88cW(j=& literal 0 HcmV?d00001 diff --git a/EmployeeSystem/EmployeeHistory.class b/EmployeeSystem/EmployeeHistory.class new file mode 100644 index 0000000000000000000000000000000000000000..5867e9818afbecb15e91f2e4b935c480fc7fc002 GIT binary patch literal 6780 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..692a52eb8daa8b30553e30f18f0d69839cd4715b GIT binary patch literal 372 zcmZ`#!AiqG5Ph4(#Kzc0W4$Xm)C$&vw-zr|K`45Nc;CdqrX-uP*%ta)9z^ia5AdVJ ziMI$c@Mh+{$ILKa-=A9mS2*s##=Z{+T@Rs$NWeAoypAsg!r5)PEUFDrLQ-e*F>8}t z<>p!-8vGT{o&}s~^+E!@S+3||Ri>n$WLhxeC)G-4beA_x)E;}WkZT!W2W|ilM;`hD zaoZB#&{Aqsk}`=bq~0V(np&X$4_KyHx4i}@ z{0xWxBEqn_r>QtR>@s$^JB7so_`~yeh~WpUw_lIX=&}dcvcAXJLV!cgKC?D@IDq&8 DVBb|H literal 0 HcmV?d00001 diff --git a/EmployeeSystem/EmployeeSearch.class b/EmployeeSystem/EmployeeSearch.class new file mode 100644 index 0000000000000000000000000000000000000000..16d38bc60f70b3b06863dad5cf584bd831b83285 GIT binary patch literal 6410 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..6547490c089d48102ebd05abfbe0edea03d0d02d GIT binary patch literal 1512 zcmaJ=Nlz3}5dPk5-P5)sjv%;z;5suRxNo>KE~6;wpow~Vz!Tfd^pKt&qA?~Wnt1c( za?luWMvY5!P$BpOOx)vkFkTelPZ;ZU1A{YYAFArr`>N`DRn;$l_q_nH5{EQ|&=5o= z8l_k&#WE?DOR+-1N(~AcRIHMEwG?YqtW~j2!Fml%*q~t}HmTUGVvCBc60l80v(#|~ zI~XR}8Jnk)JjJck3@pwtnfC~bcZpG7FwIXDY{MM0PYB-Gk;$1h#U&U58w}I1H!-M@ zIDLueVFq7wrc*FXX)#Q(FPCl?RvT|mktft8?6&chy^$!@E+hBWzsu|7+FM}Y91A4m zaaVx{Wg#Z2ok`l3VRkW;N1`n!c@J+)akHz@^)@kR$xP1b5IYRXrE+_^JC!*tMAGKA zk+qGEY=ex^F$W19RhXe;rWCWJm@P#$YACKa@wS|i>J%13&G7l#!&h$)UAZ%qzwoR7 z=Ft6HX9mypyXTFyI(B1^6fN}P`r4n*AOE~@arBa!wxD78ak?p_Z*4y*I&1}db?if{ zg8e!U;Gh#cc>Ub)-D`zl%15pr1D6Uet-kh0U*F*6hi>I0jq9wA7#1sNV~CW#etukZ zbIVQ(({_g~8_d3?`Sr&Bk})T zk84XjlXI3y>EWrIXr-Q2{2l88DCfAK(ai`P&sb?rySr`ze#gd9cAMW+%E{rHDD@Yf z5-#m6>gdM}(a-HiSgsaXVYyw7rUi2}?U*#a_v=SPwYCWLy6p`Qr4a6+iB82Xu@izbBni=kf#8bOq#Y(+KcQLce| zMO?qq747&4^o-L*;8;JDBTu1*gT2t?6MHc!tdkv{>>EJnh?jicXPAcV^y z7ItmFw5Pdtpcm!g3bK1K{oxpa5RC}Vqoh5kr?Ur3u#2=tlq#_uI}!W?#pGZr literal 0 HcmV?d00001 diff --git a/EmployeeSystem/EmployeeStatistics.class b/EmployeeSystem/EmployeeStatistics.class new file mode 100644 index 0000000000000000000000000000000000000000..79edf7aaecbafc5f29438a18576b6455642e825c GIT binary patch literal 6221 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 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 0000000000000000000000000000000000000000..38b32ec4d944a9309d036ced51e5b36f9127e1d4 GIT binary patch literal 9471 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..22744061b5bf9a8c237aae325f42f6e4f6baa910 GIT binary patch literal 3790 zcma)8ZFdt@5PmjIlTErwF{MB)0)o&sfQaI|MQh7TwG^<30=}>$YYnDJ*`@_VL`2?1 z1QZ11RYi&d$9fJaJ@ts+JRW}srSJ>{juyiamaO5=0fY zh}f%QpXmJ}4yZV&;!_oeg7^%FMSSkZ5fw*+n1*92zEJU{isLFysQ607$sj>bsW>hA zYZYfyoKn)RRB8*VQhik?_M3K=H+4eps?GK|D%dp1JVtCx~b1fw$hO zNsyncDG-Bhg@uIKQbZI9vv_SKRp3qqPn!!XL2b^HDJVzzg_`JT%5f{e{k-k1UoRQZUq~j|kl&d6K>5e3@vWSP0K=E2GTQNpWjmJexHIueGjjeBWb9U42v{8F5(FhPvR*JPmA@8h-XDSCwq-p?8jvd z{fKLL9xo_>h9)_FQA9Hqk%l7!$8HaQzHnsV$eojeY|?@?**Ozl>`f%&hN;h98$STt%CPYam@_jv==ulSZeWY%)80rMH)C ziy65YGv)42w??lV8aaA+w143C(BZp#e!V?3Jo>}16k)Z3TKk+q-#hVJ;r#B63wKTo zaFC2#zdUka-?;sn(yH#r&%nkyHg4U#8Dld~c0#g~W1XynTqmXH8tXKykd!s<fpV&065O?mmILNIK4HnPd+ULbuQ(w>Gq^^F|;Y4rFN8l#tYTZXce-;~D8b{ngo zO}LURn^p?gnxuS{H{$!SZI&Xy*fEloyS znxv+F018>MgmNiY7kOWRdR6o$l<3?nJkfbsltt%f;f+>%vnY?&lxN|K*7&mUM{E39 zsL^UQi$Iiz!RQbwve2TDP!?fJR$6kx&%{))jB+t5DW}5An*;n|4r4lhyl0?>xlhMT z%*HIt#Y3#aZ1(INc4i&huO6*vz)D1kvYfUZ?P6+nDr%!Q5Il`)s`B%^_yz)J;P>{! zTXv1d9_wPC<+>0rai<)fniu3-vXD|Tf17TCHD5ry-@+HUZ@@Ay(u@rJf~s{lF;R8~ zQ5~5yh{=PPGEhLDL`-nBsGKYn94u9wE0dV4+~$R+B1mwyLGH5V++{Tqs`oqIP$QQG zd}7`|5UCvl4x@|^%LuxhfUPBf?FB%2MTx$gC?5IPF1DR>J69`RUCW%VtLVB4t0|=> zZM@qZCzygP0aXB7*Zy0YnM2}RDlk@=^9MITE6gW`FO(t zV!q-Ugx49wI=Xfgf|y2d3Bo6rdB=!k=eB{qYw&6=;Nmir%hMN^;Z3gAG9o*-rd)0l zYtQB~deF+KmK=9xw2^t~%qqrbjawO&K?xJ+b=QP^&V=5f>pJ&@e9na4rSE!ISici? zGhH{h!}^`DN&3Fw3adI{Q*?dP9aeS1cGLGQ*YXFPnk^|sw0HuvOPpV%BeVh3dQ&a-X5 z?9aDt4u3WBa6if;3uN~ZijwJU52hphQTdWTD95oEM=Tyqf#)x5^CS5W!&dz0f` 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 0000000000000000000000000000000000000000..6559b6211b6803c4d2a660c1133902b7d4adc8bd GIT binary patch literal 11612 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 RIp*qIK7;Gc)pPmye*;fSw6p*K literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..9406fe764dc3ee6fc65b7cff6d5d56366f7731c8 GIT binary patch literal 1079 zcmah{?{Csj6g>}GT1xkuG1(@Yb1Et65+Cn`|J0&p8%fVc^DFs8d69rh%30SVG<9x$*7pp z=rF?>SrxO~KU5LpeqF^J_m31jW>Ae%rEHqUHiKSq8f|~y>e!v`5y2hLbNr%fA00Ua zZ<fV<5+8{>a z&4_}bW83ZW&dbTvD6Uj&?>h_=X3KL*-NU-$SM9n>5k(X!zhhWWng{l=opo)mnYB7T zT_l$pB_U52?rbu|{*ktm1`Zrh{Q(PU~sd%1q#G&&@2e7G0G#^7AUs^kXpE~h-)CRbRe^_!#}mJ4yN3m{L@ zgeP^vlR#i{p5K?462h3r0vHwvt&r!R)&u*3zy*Sz2+@v`4T-Z8;wP|l$q$IAHZ7&U zLcV}>ogd(1LO|4CLXdtyNv|!QBlP)N@fC^$a2qOC$@WE^l1TnlzeB1`E5UQ9X9x?R zNdiO!&}9L}1Q-tqFroaQq+pNssV4Ohv1)P;V^%HH!-RDPcBLQ&nR>j(7(UQIPH_(( bX%$XLZePQ@pYNKDvy0S_RDy{hJ!i|*-?zc zvOXe@qayX1j@RuthT{bdowUS%Hk{D$h8^P-^i6?&L!c*hoRaahj<*ULS`}Ky+X6Tv zT)t_?J2-2{Ih?oQf*n=ZrsJZFmvp?VqgO|toeKJO49MqJFup%BNFlj*38#nH@dq0!3~BX2;CEmhQj8w*a(Dz zet(w{h%~w*hK8|9I^ydx>RN&xw_moYpfDV9hb&C7CrE8Z#1{-Gq5LqZQ8?9fV=%hP zFOo$bBNXwi_IapCtQSy|>B6LzWg)jOK!VD(ic}9NXp6{eBcvfe5R8bIg`SW>E#8F@ z4Q1JgE+cIr8}0VSaRm?1L}Y=6`>I9;=F{v{uh5X^3VP|^Z7t-}8tqzTgqFGK9i217 z68A$xjb$Vwek1LO)}oGx+w;^CcekZ%v=vw5|Iuf~S@acq+yRf_PfCx?;<5lo`@-R< z;Y#L(HQ@`lOD!=$xmDFGHH=$hMBAV;L|w_cEzj&xslQDb2k8Joc zIl8@G6*mnfRSAk}Yra_u>RiWzt>N)3rI3Si zL{jY?)ZsB6^2ymMNmn&n+L{BM_=y7#W3dCx@^}P`ZTP7JKSRKQN73TI&(Yz)Qgqnx z3)$Z2zzRI(z~}Lp1~{-Bi#1G1w#a6`-&o`Jvy>u6)3Y9%?H}=v#AY z=gygP^U97}2YMZ76&7u1ci^|eVlo|wnWku8S-W}JbYS=Xq5d8RewV^^Bvv_l>y6E^ z-LDLv-+c4RDGe3MJk`YZ^b8%p`tPedhYlPcdUszc%z@8IBz})S*ziXO{)9i<@D~UE ziodb5B%M_CvncBGd*uL?rDy!PkaelmS32-_{DTd+a@N%3O;%sY9?Bh$)k0)Fifi^{ z9$yjqC)TlFiBMW!sTXzsQg71lvU(n_p`&M1$T$~m(m^O;U^S0;m2$e8mZ}mVsCMAX zVo5}Y&u@wKyrf}_5KC<>mTDCx4LOz1Yp6_yVtF7O?d}eSc)hiC8>-)?Bz@}gfKM)6 zsRhG3wabXC4SJ_mCK>@EYFpCY(zHxe7rGXeHsvGGI%Fi17jR32(!^92=ck%UJDc+{jo+;< zeh3#KN7^rEInsg6kxkef*?-NEt7`cnoF?tLFcNl4|X+-s&P8{ zP;`-!yaf5Ift}9@)K!i`OytjmNhrZ&?o6SNQ~60g4Rx4~dvH4?p1>C^`>T=5|3Ga` zFLW9@iK0OicRFty!07A3XN>T1l0!*?!zeMEW15}J9~;0-8lQzy%%*d7G&%<}Fqb=b z;z_>Eg;q*yUxM4RxtA=(W}C8E61Q0@HfPX?n)lBwXG=47=@E}tl zG-$JQGM#1bw_IMpJ$^?hV>d81Y4gAl*z!&zFZUh3R!J`~p=+)1<&-sO+*pIPd`d~T zS-NIW$(S3g*j|)>fPC$OtYy)U8U46ptKEGWl@}6Pi_MzoX5~JO(Fn(h7K?Hp)>+CY zaTKEYRFosas{9qDuX6QNR%mV}bPJ*WtkAqn=u3olA;712mr9@@6WT*iFe`LSCUiTY z-C3dKnb4htKFv-slEB1F=*t9!v$|E03Ee|z1ko&7^-Snqg4So%>db_`O6W6Lp+%X{ z{e(W7I0>~ZE;$KBnI}snSXIX%$_^>SQ@bEerZF*LkO!ms=;| zH1eHy$^PAHpC`S_vP8u^@N;SJoKOz-2=dB2jXP}(YFf4=NLpm5@v zI;>8fM&M2U1?n_Ib_TtiOAqis{|`ECc>ZI0soC%q 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