feat(hrm): 同步新疆中亿装配的人力资源模块到北方科技
代码来源:dev_新疆_中亿装配 分支的 yudao-module-hrm / yudao-module-hrm-api,
带来加班工资配置、合同文档生成、员工类别、社保大病/长期护理比例、
考勤规则生效日期区间、APP 打卡状态接口、薪资计算增强等能力。
配套 SQL(必须同步执行,否则 HRM 功能不可用):
- sql/mysql/hrm_sync_from_zyjc_20261024.sql
新建 hrm_overtime_wage_config;hrm_employee 增加 employee_category(默认 1-普通员工);
hrm_attendance_rule 增加生效日期区间;社保方案增加大病医疗/长期护理比例;
放开 hrm_attendance_holiday.start_date/end_date 非空约束
- sql/mysql/hrm_menu_sync_from_zyjc_20261024.sql
补 35 条菜单与按钮权限(HRM 权限点 72 → 106),默认不自动授权给角色
同时修复两个问题:
1) 打卡提示文案失效:EMPLOYEE_NOT_EXISTS 的错误码文案不含 {} 占位符,
原写法 exception(code, 长文案) 被 ServiceExceptionUtil 判为「参数过多」并丢弃
自定义文案,前端只能看到「员工不存在」。改用 ServiceException 直接构造。
2) 节假日配置不可用:北方库的 start_date/end_date 为 NOT NULL,而中亿版 DO 只写
单日 date 字段,新增节假日会抛 Field 'start_date' doesn't have a default value。
通过 DDL 放宽为可空解决。
已知差异(相对北方科技原有实现,本次未保留):
- 操作日志(LogRecordConstants 已无任何引用)
- 全局免审批
- 节假日时间段配置(回退为单日 date)
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- ============================================================ |
| | | -- ä»ãæ°çä¸äº¿è£
é
ã忥 HRM è½åå°ãæ°çåæ¹ç§æãââ èåæ°æ® |
| | | -- çææ¥æï¼2026-10-24 |
| | | -- |
| | | -- 说æï¼ |
| | | -- 1. å
± 1 个èå页ï¼å çå·¥èµç®¡çï¼+ 34 个æé®æéç¹ï¼å
¨é¨ä¸º HRM 模å |
| | | -- 2. èå ID ä¸ä¸äº¿è£
é
ä¿æä¸è´ï¼å·²ç¡®è®¤åæ¹ç§æåºä¸è¿äº ID åæªè¢«å ç¨ï¼ï¼ |
| | | -- 便äºåç»ä¸¤ç«¯å¯¹ç
§ä¸åæ¥ |
| | | -- 3. ä½¿ç¨ INSERT IGNOREï¼é夿§è¡ä¸ä¼æ¥ä¸»é®å²çª |
| | | -- 4. ãé»è®¤ä¸èªå¨ææç»ä»»ä½è§è²ã |
| | | -- - è¶
级管çåï¼super_adminï¼æ¬æ¥å°±ä¸åè§è²èåéå¶ï¼å¯ç´æ¥çå° |
| | | -- - å
¶ä½è§è²è¯·å¨ãç³»ç»ç®¡ç â è§è²ç®¡çã䏿éå¾éï¼é¿å
æèªé
¬/èå¤ |
| | | -- 管çæé䏿¬¡æ§å¼æ¾ç»å
¨é¨æ®éåå·¥ |
| | | -- 5. è¥ç¡®éç¨ SQL æ¹éææï¼åèæä»¶æ«å°¾çæ³¨éæ¨¡æ¿ |
| | | -- ============================================================ |
| | | |
| | | INSERT IGNORE INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6026,'å çå·¥èµç®¡ç','',2,4,6020,'overtime-wage','ep:clock','hrm/salary/overtime-wage/index','HrmOvertimeWage',0,0x01,0x01,0x01,'1','2026-10-13 17:13:58','1','2026-10-14 10:52:27',0x00),(60261,'å çå·¥èµæ¥è¯¢','hrm:overtime-wage:query',3,1,6026,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-13 17:13:58','1','2026-10-14 10:52:27',0x00),(60262,'å çå·¥èµæ°å¢','hrm:overtime-wage:create',3,2,6026,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-13 17:13:58','1','2026-10-14 10:52:28',0x00),(60263,'å çå·¥èµä¿®æ¹','hrm:overtime-wage:update',3,3,6026,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-13 17:13:58','1','2026-10-14 10:52:29',0x00),(60264,'å çå·¥èµå é¤','hrm:overtime-wage:delete',3,4,6026,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-13 17:13:58','1','2026-10-14 10:52:31',0x00),(602800,'èå¤è§åæ¥è¯¢','hrm:attendance-rule:query',3,10,6013,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602801,'èå¤è®°å½æ¥è¯¢','hrm:attendance-record:query',3,10,6011,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602802,'èå¤å¼å¸¸æ¥è¯¢','hrm:attendance-exception:query',3,10,6012,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602803,'èå¤å¼å¸¸ä¿®æ¹','hrm:attendance-exception:update',3,11,6012,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602804,'èå¤å¼å¸¸å é¤','hrm:attendance-exception:delete',3,12,6012,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602805,'è忥é
ç½®æ¥è¯¢','hrm:attendance-holiday:query',3,10,6014,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602806,'èå¤ç»è®¡æ¥è¯¢','hrm:attendance-summary:query',3,10,6015,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602807,'请åç³è¯·å¯¼åº','hrm:leave:export',3,10,6003,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602808,'è°å²ç³è¯·æ¥è¯¢','hrm:transfer:query',3,10,6004,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602809,'è°å²ç³è¯·å¯¼åº','hrm:transfer:export',3,11,6004,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602810,'离èç³è¯·æ¥è¯¢','hrm:resignation:query',3,10,6005,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602811,'离èç³è¯·å¯¼åº','hrm:resignation:export',3,11,6005,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602812,'å çç³è¯·æ¥è¯¢','hrm:overtime:query',3,10,1065211,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602813,'åå导å
¥','hrm:employee-contract:import',3,10,1063105,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602814,'èªé
¬æ ¸ç®æ¥è¯¢','hrm:salary-calculation:query',3,10,6021,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602815,'èªé
¬æ ¸ç®å é¤','hrm:salary-calculation:delete',3,11,6021,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602816,'èªèµåæ¾æ¥è¯¢','hrm:salary-payment:query',3,10,6022,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602817,'èªèµåæ¾çæ','hrm:salary-payment:generate',3,11,6022,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602818,'èªèµåæ¾å é¤','hrm:salary-payment:delete',3,12,6022,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602819,'ç¤¾ä¿æ¡£æ¡æ¥è¯¢','hrm:employee-social-security:query',3,10,6023,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602820,'ç¤¾ä¿æ¹æ¡æ¥è¯¢','hrm:social-security-scheme:query',3,10,602445,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602821,'社ä¿é
ç½®æ¥è¯¢','hrm:social-security-config:query',3,30,6020,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602822,'社ä¿é
ç½®æ°å¢','hrm:social-security-config:create',3,31,6020,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602823,'社ä¿é
置修æ¹','hrm:social-security-config:update',3,32,6020,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602824,'社ä¿é
ç½®å é¤','hrm:social-security-config:delete',3,33,6020,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602825,'个ç¨ç¨çæ¥è¯¢','hrm:tax-rate-config:query',3,10,6024,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602826,'èªé
¬ç»ææ¥è¯¢','hrm:salary-structure:query',3,40,6020,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602827,'èªé
¬ç»ææ°å¢','hrm:salary-structure:create',3,41,6020,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602828,'èªé
¬ç»æä¿®æ¹','hrm:salary-structure:update',3,42,6020,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00),(602829,'èªé
¬ç»æå é¤','hrm:salary-structure:delete',3,43,6020,'','','',NULL,0,0x01,0x01,0x01,'1','2026-10-22 18:41:02','1','2026-10-22 18:41:02',0x00); |
| | | -- ------------------------------------------------------------ |
| | | -- å¯éï¼æ¹éææç»æå®è§è²ï¼é»è®¤æ³¨éï¼æéå¯ç¨ï¼ |
| | | -- 注æ system_role_menu æ tenant_id åæ®µï¼å¿
é¡»æ¾å¼å 1ï¼ |
| | | -- å¦å该è§è²å¨ç§æ· 1 ä¸ä»ç¶æ¥ä¸å°è¿äºèåã |
| | | -- ------------------------------------------------------------ |
| | | -- INSERT IGNORE INTO `system_role_menu` (`role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) |
| | | -- SELECT 2, m.`id`, '1', NOW(), '1', NOW(), b'0', 1 |
| | | -- FROM `system_menu` m |
| | | -- WHERE m.`id` IN (6026,60261,60262,60263,60264,602800,602801,602802,602803,602804, |
| | | -- 602805,602806,602807,602808,602809,602810,602811,602812,602813, |
| | | -- 602814,602815,602816,602817,602818,602819,602820,602821,602822, |
| | | -- 602823,602824,602825,602826,602827,602828,602829); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- ============================================================ |
| | | -- ä»ãæ°çä¸äº¿è£
é
ã忥 HRM è½åå°ãæ°çåæ¹ç§æãââ è¡¨ç»æåæ´ |
| | | -- çææ¥æï¼2026-10-24 |
| | | -- |
| | | -- 说æï¼ |
| | | -- 1. æ¬èæ¬ãåªåè¡¨ç»æåæ´ãï¼ä¸å«ä»»ä½ä¸å¡æ°æ® |
| | | -- 2. ä¸å¡æ°æ®ï¼åå·¥ãé¨é¨ãå²ä½ãèå¤è§åãèªèµãç¤¾ä¿æ¹æ¡çï¼ |
| | | -- ä¿çåæ¹ç§æèªå·±çï¼ä¸ä»å
¶ä»ä¼ä¸è¿ç§» |
| | | -- 3. é
å¥ä»£ç ï¼yudao-module-hrm / yudao-module-hrm-api ç±ä¸äº¿è£
é
忝忥 |
| | | -- 4. ãæ§è¡ä¸æ¬¡å³å¯ãALTER TABLE ADD COLUMN ä¸å¹çï¼é夿§è¡ä¼æ¥ |
| | | -- Duplicate column nameï¼å±æ£å¸¸ç°è±¡ï¼CREATE TABLE å·²ç¨ IF NOT EXISTS |
| | | -- 5. åéæ°æ®èªå¨å
¼å®¹ï¼ |
| | | -- - employee_category é»è®¤ 1ï¼æ®éåå·¥ï¼ |
| | | -- - èå¤è§å effective_*_date 为空表示ä¸éï¼åè§åç»§ç»çæ |
| | | -- - ç¤¾ä¿æ°æ¯ä¾å段为空ï¼ä¸å½±ååæè®¡ç® |
| | | -- ============================================================ |
| | | |
| | | -- ------------------------------------------------------------ |
| | | -- 1. å çå·¥èµé
ç½®è¡¨ï¼æ°å¢åè½ï¼èªé
¬ â å çå·¥èµï¼ |
| | | -- ------------------------------------------------------------ |
| | | CREATE TABLE IF NOT EXISTS `hrm_overtime_wage_config` ( |
| | | `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'é
ç½®ID', |
| | | `post_id` bigint NOT NULL COMMENT 'å²ä½IDï¼å
³è system_post.id', |
| | | `hourly_rate` decimal(10,2) NOT NULL DEFAULT '20.00' COMMENT 'å çæ¶èªï¼å
/å°æ¶ï¼ï¼é»è®¤ 20', |
| | | `max_overtime_hours` decimal(6,1) DEFAULT NULL COMMENT 'æ¯ææå¤§å çæ¶é¿ä¸éï¼å°æ¶ï¼ï¼ä¸ºç©ºè¡¨ç¤ºä¸é', |
| | | `status` tinyint NOT NULL DEFAULT '0' COMMENT 'ç¶æï¼0-å¯ç¨ï¼1-ç¦ç¨', |
| | | `remark` varchar(500) DEFAULT NULL COMMENT '夿³¨', |
| | | `creator` varchar(64) DEFAULT '' COMMENT 'å建è
', |
| | | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'å建æ¶é´', |
| | | `updater` varchar(64) DEFAULT '' COMMENT 'æ´æ°è
', |
| | | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ´æ°æ¶é´', |
| | | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT 'æ¯å¦å é¤', |
| | | PRIMARY KEY (`id`), |
| | | UNIQUE KEY `uk_post_id` (`post_id`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='HRMå²ä½å çå·¥èµé
置表'; |
| | | |
| | | -- ------------------------------------------------------------ |
| | | -- 2. å工类å«ï¼é»è®¤ 1-æ®éåå·¥ï¼åé 168 æ¡èªå¨å¡«å
为æ®éåå·¥ï¼ |
| | | -- ------------------------------------------------------------ |
| | | ALTER TABLE `hrm_employee` |
| | | ADD COLUMN `employee_category` tinyint NOT NULL DEFAULT 1 COMMENT 'å工类å«ï¼1-æ®éåå·¥ï¼2-æå·¥ï¼3-管ç' AFTER `employee_status`; |
| | | |
| | | -- ------------------------------------------------------------ |
| | | -- 3. èå¤è§åçææ¥æåºé´ï¼ç©ºè¡¨ç¤ºä¸éï¼åéè§åæåé»è¾ç»§ç»çæï¼ |
| | | -- ------------------------------------------------------------ |
| | | ALTER TABLE `hrm_attendance_rule` |
| | | ADD COLUMN `effective_start_date` date DEFAULT NULL COMMENT 'çæå¼å§æ¥æï¼ç©ºè¡¨ç¤ºä¸éï¼' AFTER `overtime_threshold`, |
| | | ADD COLUMN `effective_end_date` date DEFAULT NULL COMMENT 'çæç»ææ¥æï¼ç©ºè¡¨ç¤ºä¸éï¼' AFTER `effective_start_date`; |
| | | |
| | | -- ------------------------------------------------------------ |
| | | -- 4. 社ä¿å
¬ç§¯éæ¹æ¡ï¼æ°å¢å¤§ç
å»çãé¿ææ¤ç两项ä¿é©æ¯ä¾ |
| | | -- ------------------------------------------------------------ |
| | | ALTER TABLE `hrm_social_security_scheme` |
| | | ADD COLUMN `serious_illness_company_ratio` decimal(10,4) DEFAULT NULL COMMENT '大ç
å»çä¼ä¸ç¼´çº³æ¯ä¾' AFTER `maternity_ratio`, |
| | | ADD COLUMN `serious_illness_personal_ratio` decimal(10,4) DEFAULT NULL COMMENT '大ç
å»ç个人缴纳æ¯ä¾' AFTER `serious_illness_company_ratio`, |
| | | ADD COLUMN `long_term_care_company_ratio` decimal(10,4) DEFAULT NULL COMMENT 'é¿ææ¤çä¼ä¸ç¼´çº³æ¯ä¾' AFTER `serious_illness_personal_ratio`, |
| | | ADD COLUMN `long_term_care_personal_ratio` decimal(10,4) DEFAULT NULL COMMENT 'é¿ææ¤ç个人缴纳æ¯ä¾' AFTER `long_term_care_company_ratio`; |
| | | |
| | | -- ------------------------------------------------------------ |
| | | -- 5. ãå¿
é¡»ãèåæ¥è¡¨æ¾å¼ start_date / end_date é空约æ |
| | | -- |
| | | -- èæ¯ï¼åæ¹ç§æåå
æ¯æãè忥æ¶é´æ®µãï¼start_date/end_dateï¼NOT NULLï¼ï¼ |
| | | -- ä¸äº¿è£
é
ç HrmAttendanceHolidayDO åªæåæ¥ date åæ®µï¼ä¸åè¿ä¸¤ä¸ªåã |
| | | -- è¥ä¿ç NOT NULLï¼æ°å¢/导å
¥è忥ä¼ç´æ¥æ |
| | | -- Field 'start_date' doesn't have a default value â å端æ¾ç¤ºãç³»ç»å¼å¸¸ã |
| | | -- ç°ææ°æ®ï¼ä¸¤è¾¹ hrm_attendance_holiday å为 0 æ¡ï¼æ¾å¼çº¦æä¸æ¶åæ°æ®è¿ç§» |
| | | -- ------------------------------------------------------------ |
| | | ALTER TABLE `hrm_attendance_holiday` |
| | | MODIFY COLUMN `start_date` date DEFAULT NULL COMMENT 'å¼å§æ¥æï¼ä¸äº¿ç代ç ä¸åæ¤åï¼ä¿çå
¼å®¹åæ¹ååºé´ç»æï¼', |
| | | MODIFY COLUMN `end_date` date DEFAULT NULL COMMENT 'ç»ææ¥æï¼ä¸äº¿ç代ç ä¸åæ¤åï¼ä¿çå
¼å®¹åæ¹ååºé´ç»æï¼'; |
| | |
| | | ErrorCode ATTENDANCE_EXCEPTION_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_030_004_004, "èå¤å¼å¸¸ç³è¯·æäº¤å¤±è´¥ï¼åå ï¼ä¸æ¯èç¨¿ç¶æ"); |
| | | ErrorCode ATTENDANCE_RULE_NOT_EXISTS = new ErrorCode(1_030_004_005, "èå¤è§åä¸åå¨"); |
| | | ErrorCode ATTENDANCE_RULE_NAME_EXISTS = new ErrorCode(1_030_004_006, "èå¤è§ååç§°å·²åå¨"); |
| | | // å·²åºå¼ï¼2026-10-16ï¼ï¼åä¸ä¸ªé¨é¨å
许é
ç½®å¤æ¡èå¤è§åï¼å¤ç次ï¼ï¼ä¸åæ ¡éªé¨é¨å¯ä¸ï¼ä¿ç该é误ç ä»
为å
¼å®¹åå²å¼ç¨ |
| | | ErrorCode ATTENDANCE_RULE_DEPT_EXISTS = new ErrorCode(1_030_004_007, "该é¨é¨å·²é
ç½®èå¤è§å"); |
| | | ErrorCode ATTENDANCE_HOLIDAY_NOT_EXISTS = new ErrorCode(1_030_004_008, "è忥é
ç½®ä¸åå¨ï¼ID={}ï¼ï¼å¯è½å·²è¢«ä»äººå é¤ãè¯·å·æ°å表åéè¯"); |
| | | ErrorCode ATTENDANCE_HOLIDAY_RANGE_OVERLAP = new ErrorCode(1_030_004_009, "æ¥æåºé´ã{} ~ {}ãä¸å·²æé
ç½®ã{}ãï¼{} ~ {}ï¼éå ï¼æ æ³ä¿åãè¯·è°æ´æ¬æ¬¡æ¥æåºé´é¿å¼è¯¥é
ç½®ï¼æç´æ¥ç¼è¾/å é¤å·²æé
ç½®ååä¿å"); |
| | | ErrorCode ATTENDANCE_HOLIDAY_NOT_EXISTS = new ErrorCode(1_030_004_008, "è忥é
ç½®ä¸åå¨"); |
| | | ErrorCode ATTENDANCE_HOLIDAY_DATE_EXISTS = new ErrorCode(1_030_004_009, "è¯¥æ¥æè忥é
置已åå¨"); |
| | | ErrorCode ATTENDANCE_CLOCK_FAIL_NO_RULE = new ErrorCode(1_030_004_010, "æå¡å¤±è´¥ï¼æªæ¾å°èå¤è§å"); |
| | | ErrorCode ATTENDANCE_CLOCK_FAIL_HOLIDAY = new ErrorCode(1_030_004_011, "æå¡å¤±è´¥ï¼å½æ¥ä¸ºä¼æ¯æ¥"); |
| | | ErrorCode ATTENDANCE_CLOCK_FAIL_NOT_WORKDAY = new ErrorCode(1_030_004_012, "æå¡å¤±è´¥ï¼å½æ¥ä¸æ¯å·¥ä½æ¥"); |
| | |
| | | ErrorCode ATTENDANCE_EXCEPTION_UPDATE_AUDIT_STATUS_FAIL = new ErrorCode(1_030_004_014, "æ´æ°èå¤å¼å¸¸å®¡æ¹ç¶æå¤±è´¥ï¼åå ï¼ä¸æ¯å®¡æ¹ä¸ç¶æ"); |
| | | ErrorCode ATTENDANCE_EXCEPTION_BPM_PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1_030_004_015, "èå¤å¼å¸¸å®¡æ¹æµç¨ä¸åå¨"); |
| | | ErrorCode ATTENDANCE_CLOCK_ALREADY_CLOCKED = new ErrorCode(1_030_004_016, "å·²æè¿å¡ï¼è¯·å¿é夿å¡"); |
| | | ErrorCode ATTENDANCE_HOLIDAY_DATE_RANGE_INVALID = new ErrorCode(1_030_004_017, "ç»ææ¥æã{}ãä¸è½æ©äºå¼å§æ¥æã{}ãï¼æ æ³ä¿åãè¯·éæ°éæ©æ£ç¡®çæ¥æåºé´"); |
| | | ErrorCode ATTENDANCE_RULE_EFFECTIVE_DATE_INVALID = new ErrorCode(1_030_004_017, "çæå¼å§æ¥æä¸è½æäºçæç»ææ¥æ"); |
| | | |
| | | // ========== èªé
¬ç®¡ç 1-030-005-000 ========== |
| | | ErrorCode SALARY_STRUCTURE_NOT_EXISTS = new ErrorCode(1_030_005_000, "èªé
¬ç»æä¸åå¨"); |
| | |
| | | ErrorCode SALARY_PAYMENT_HAS_PAID_DETAIL = new ErrorCode(1_030_005_024, "å é¤å¤±è´¥ï¼åå ï¼åå¨å·²åæ¾æç»ï¼è¯·å
æ¤é"); |
| | | ErrorCode SALARY_STRUCTURE_NAME_EXISTS = new ErrorCode(1_030_005_025, "èªé
¬ç»æåç§°å·²åå¨"); |
| | | ErrorCode SALARY_STRUCTURE_IN_USE = new ErrorCode(1_030_005_026, "èªé
¬ç»ææ£å¨è¢«å工使ç¨ï¼æ æ³å é¤"); |
| | | ErrorCode OVERTIME_WAGE_CONFIG_NOT_EXISTS = new ErrorCode(1_030_005_027, "å²ä½å çå·¥èµé
ç½®ä¸åå¨"); |
| | | ErrorCode OVERTIME_WAGE_CONFIG_POST_EXISTS = new ErrorCode(1_030_005_028, "该å²ä½å·²é
ç½®å çå·¥èµï¼è¯·å¿é夿·»å "); |
| | | ErrorCode CONTRACT_TEMPLATE_TYPE_NOT_SUPPORT = new ErrorCode(1_030_005_029, "䏿¯æçå忍¡æ¿ç±»å"); |
| | | ErrorCode CONTRACT_TEMPLATE_LOAD_FAILED = new ErrorCode(1_030_005_030, "å忍¡æ¿å 载失败"); |
| | | ErrorCode EMPLOYEE_REGULARIZE_NEED_LABOR_CONTRACT = new ErrorCode(1_030_005_031, "该åå·¥å½ååå䏿¯å³å¨ååï¼æ æ³è½¬æ£"); |
| | | ErrorCode SALARY_CALCULATION_UPDATE_FAIL = new ErrorCode(1_030_005_032, "ä¿®æ¹å¤±è´¥ï¼åå ï¼ä»
å¾
ç¡®è®¤ç¶æå¯ä¿®æ¹ï¼è¯·å
æ¤é确认"); |
| | | |
| | | // ========== çæ¬¡ç®¡ç 1-030-009-000 ========== |
| | | ErrorCode SHIFT_NOT_EXISTS = new ErrorCode(1_030_009_000, "çæ¬¡ä¸åå¨"); |
| | |
| | | |
| | | public static final Integer[] ARRAYS = Arrays.stream(values()).map(HrmAttendanceClockTypeEnum::getType).toArray(Integer[]::new); |
| | | |
| | | @Override |
| | | public Integer[] array() { |
| | | return ARRAYS; |
| | | } |
| | | |
| | | /** |
| | | * æ ¹æ®ç±»åè·ååç§°ï¼æªç¥ç±»åè¿å null |
| | | * æ ¹æ®ç±»åè·å¾åç§°ï¼ç¨äºçé¢å±ç¤ºï¼å¦ my-status è¿åï¼ |
| | | */ |
| | | public static String getNameByType(Integer type) { |
| | | if (type == null) { |
| | |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public Integer[] array() { |
| | | return ARRAYS; |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.hrm.enums; |
| | | |
| | | import lombok.Getter; |
| | | import lombok.RequiredArgsConstructor; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * åå·¥ç±»å«æä¸¾ï¼ç¨äºå çå·¥èµçå·®å¼åæ ¸ç®ã |
| | | * |
| | | * <ul> |
| | | * <li>æ®éåå·¥ï¼å çå·¥èµæå²ä½é
ç½®çåºå®æ¶èªï¼é»è®¤ 20 å
/å°æ¶ï¼æ ¸ç®</li> |
| | | * <li>æå·¥ï¼å çèªèµæ 1:1 æ ¸ç®ï¼ä¸å¹³æ¶æ¶èªä¸è´ï¼åºæ¬å·¥èµ / 21.75 / 8ï¼</li> |
| | | * <li>管çï¼ä¸è®¡å çå·¥èµ</li> |
| | | * </ul> |
| | | */ |
| | | @Getter |
| | | @RequiredArgsConstructor |
| | | public enum HrmEmployeeCategoryEnum { |
| | | |
| | | NORMAL(1, "æ®éåå·¥"), |
| | | TECHNICIAN(2, "æå·¥"), |
| | | MANAGER(3, "管ç"); |
| | | |
| | | private final Integer category; |
| | | private final String name; |
| | | |
| | | public static HrmEmployeeCategoryEnum valueOfCategory(Integer category) { |
| | | if (category == null) { |
| | | return null; |
| | | } |
| | | return Arrays.stream(values()).filter(item -> item.category.equals(category)).findFirst().orElse(null); |
| | | } |
| | | |
| | | public static String getName(Integer category) { |
| | | HrmEmployeeCategoryEnum value = valueOfCategory(category); |
| | | return value == null ? "" : value.name; |
| | | } |
| | | |
| | | } |
| | |
| | | <artifactId>yudao-spring-boot-starter-excel</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- Word å忍¡æ¿æ¸²æï¼poi-tlï¼ --> |
| | | <dependency> |
| | | <groupId>com.deepoove</groupId> |
| | | <artifactId>poi-tl</artifactId> |
| | | <version>1.12.2</version> |
| | | </dependency> |
| | | |
| | | <!-- Test æµè¯ç¸å
³ --> |
| | | <dependency> |
| | | <groupId>cn.iocoder.boot</groupId> |
| | |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; |
| | | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; |
| | | import cn.iocoder.yudao.framework.operatelog.core.util.OperateLogUtils; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceMyStatusRespVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceRecordImportExcelVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceRecordImportReqVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceRecordPageReqVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceRecordRespVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceMyStatusRespVO; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmAttendanceRecordDO; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeDO; |
| | | import cn.iocoder.yudao.module.hrm.enums.HrmAttendanceClockTypeEnum; |
| | | import cn.iocoder.yudao.module.hrm.service.attendance.HrmAttendanceRecordService; |
| | | import cn.iocoder.yudao.module.hrm.service.employee.HrmEmployeeService; |
| | | import cn.iocoder.yudao.module.system.api.dept.DeptApi; |
| | | import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; |
| | | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| | | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; |
| | | import com.mzt.logapi.starter.annotation.LogRecord; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.Parameter; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jakarta.annotation.Resource; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import jakarta.validation.Valid; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.*; |
| | |
| | | import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | | import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY; |
| | | import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.EMPLOYEE_NOT_EXISTS; |
| | | import static cn.iocoder.yudao.module.hrm.enums.LogRecordConstants.*; |
| | | |
| | | @Tag(name = "管çåå° - HRM èå¤è®°å½") |
| | | @RestController |
| | |
| | | |
| | | @PostMapping("/clock-in") |
| | | @Operation(summary = "ä¸çæå¡") |
| | | @PreAuthorize("@ss.hasPermission('hrm:attendance-record:clock')") |
| | | public CommonResult<HrmAttendanceRecordRespVO> clockIn(@RequestParam("location") String location) { |
| | | Long loginUserId = getLoginUserId(); |
| | | HrmEmployeeDO employee = employeeService.getEmployeeByUserId(loginUserId); |
| | | if (employee == null) { |
| | | throw exception(EMPLOYEE_NOT_EXISTS, |
| | | "å½åç»å½è´¦å·æªå
³èå工档æ¡ï¼æ æ³æå¡ã请è系管çåå¨ãå工管çãä¸å°è¯¥ç»å½è´¦å·ç»å®å°åå·¥ã"); |
| | | throw new ServiceException(EMPLOYEE_NOT_EXISTS.getCode(), |
| | | "å½åç»å½è´¦å·æªå
³èå工信æ¯ï¼æ æ³æå¡ã请è系管çåå¨ãå工管çãä¸å°è¯¥ç»å½è´¦å·ç»å®å°åå·¥ã"); |
| | | } |
| | | HrmAttendanceRecordDO record = attendanceRecordService.clockIn(employee.getId(), LocalDateTime.now(), location); |
| | | return success(buildClockRespVO(record, employee)); |
| | | } |
| | | |
| | | @PostMapping("/clock-out") |
| | | @Operation(summary = "ä¸çæå¡") |
| | | public CommonResult<HrmAttendanceRecordRespVO> clockOut(@RequestParam("location") String location) { |
| | | Long loginUserId = getLoginUserId(); |
| | | HrmEmployeeDO employee = employeeService.getEmployeeByUserId(loginUserId); |
| | | if (employee == null) { |
| | | throw exception(EMPLOYEE_NOT_EXISTS, |
| | | "å½åç»å½è´¦å·æªå
³èå工档æ¡ï¼æ æ³æå¡ã请è系管çåå¨ãå工管çãä¸å°è¯¥ç»å½è´¦å·ç»å®å°åå·¥ã"); |
| | | } |
| | | HrmAttendanceRecordDO record = attendanceRecordService.clockOut(employee.getId(), LocalDateTime.now(), location); |
| | | return success(buildClockRespVO(record, employee)); |
| | | } |
| | | |
| | | @GetMapping("/my-status") |
| | | @Operation(summary = "è·å¾å½åç»å½äººçèå¤ç¶æ") |
| | | @Parameter(name = "date", description = "è夿¥æï¼æ ¼å¼ yyyy-MM-ddï¼é»è®¤æå¡å¨å½å¤©", example = "2026-10-17") |
| | | public CommonResult<HrmAttendanceMyStatusRespVO> getMyStatus( |
| | | @RequestParam(value = "date", required = false) |
| | | @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY) LocalDate date) { |
| | | return success(attendanceRecordService.getMyStatus(getLoginUserId(), date)); |
| | | } |
| | | |
| | | /** |
| | | * æå»ºæå¡è¿å VOï¼åæ®µä¸ my-status.record ä¿æä¸è´ |
| | | */ |
| | | private HrmAttendanceRecordRespVO buildClockRespVO(HrmAttendanceRecordDO record, HrmEmployeeDO employee) { |
| | | HrmAttendanceRecordRespVO respVO = BeanUtils.toBean(record, HrmAttendanceRecordRespVO.class); |
| | | respVO.setClockInTypeName(HrmAttendanceClockTypeEnum.getNameByType(record.getClockInType())); |
| | | respVO.setClockOutTypeName(HrmAttendanceClockTypeEnum.getNameByType(record.getClockOutType())); |
| | | respVO.setUserName(employee.getName()); |
| | | if (record.getDeptId() != null) { |
| | | DeptRespDTO dept = deptApi.getDept(record.getDeptId()); |
| | |
| | | respVO.setDeptName(dept.getName()); |
| | | } |
| | | } |
| | | return respVO; |
| | | return success(respVO); |
| | | } |
| | | |
| | | @PostMapping("/clock-out") |
| | | @Operation(summary = "ä¸çæå¡") |
| | | @PreAuthorize("@ss.hasPermission('hrm:attendance-record:clock')") |
| | | public CommonResult<HrmAttendanceRecordRespVO> clockOut(@RequestParam("location") String location) { |
| | | Long loginUserId = getLoginUserId(); |
| | | HrmEmployeeDO employee = employeeService.getEmployeeByUserId(loginUserId); |
| | | if (employee == null) { |
| | | throw new ServiceException(EMPLOYEE_NOT_EXISTS.getCode(), |
| | | "å½åç»å½è´¦å·æªå
³èå工信æ¯ï¼æ æ³æå¡ã请è系管çåå¨ãå工管çãä¸å°è¯¥ç»å½è´¦å·ç»å®å°åå·¥ã"); |
| | | } |
| | | HrmAttendanceRecordDO record = attendanceRecordService.clockOut(employee.getId(), LocalDateTime.now(), location); |
| | | HrmAttendanceRecordRespVO respVO = BeanUtils.toBean(record, HrmAttendanceRecordRespVO.class); |
| | | respVO.setUserName(employee.getName()); |
| | | if (record.getDeptId() != null) { |
| | | DeptRespDTO dept = deptApi.getDept(record.getDeptId()); |
| | | if (dept != null) { |
| | | respVO.setDeptName(dept.getName()); |
| | | } |
| | | } |
| | | return success(respVO); |
| | | } |
| | | |
| | | @GetMapping("/my-status") |
| | | @Operation(summary = "è·å¾å½åç»å½äººçèå¤ç¶æ", description = "App æå¡é¡µä½¿ç¨ï¼è¿å彿¥èå¤è§åãæå¡è®°å½ä¸å¯æå¡ç¶æ") |
| | | @Parameter(name = "date", description = "è夿¥æï¼æ ¼å¼ yyyy-MM-ddï¼é»è®¤æå¡å¨å½å¤©", example = "2026-10-17") |
| | | public CommonResult<HrmAttendanceMyStatusRespVO> getMyStatus( |
| | | @RequestParam(value = "date", required = false) |
| | | @org.springframework.format.annotation.DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date) { |
| | | return success(attendanceRecordService.getMyStatus(getLoginUserId(), date)); |
| | | } |
| | | |
| | | private Long getLoginUserId() { |
| | | return cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId(); |
| | | } |
| | | return cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId(); } |
| | | |
| | | @GetMapping("/page") |
| | | @Operation(summary = "è·å¾èå¤è®°å½å页") |
| | |
| | | @PostMapping("/import") |
| | | @Operation(summary = "导å
¥èå¤è®°å½") |
| | | @PreAuthorize("@ss.hasPermission('hrm:attendance-record:import')") |
| | | @LogRecord(type = HRM_ATTENDANCE_RECORD_TYPE, subType = HRM_ATTENDANCE_RECORD_IMPORT_SUB_TYPE, bizNo = "0", |
| | | success = HRM_ATTENDANCE_RECORD_IMPORT_SUCCESS) |
| | | public CommonResult<String> importExcel(@Valid HrmAttendanceRecordImportReqVO importReqVO) throws IOException { |
| | | List<HrmAttendanceRecordImportExcelVO> list = ExcelUtils.read(importReqVO.getFile(), HrmAttendanceRecordImportExcelVO.class); |
| | | if (CollUtil.isEmpty(list)) { |
| | | // è®°å½å¯¼å
¥ç»ææ±æ»ï¼ä¾ @LogRecord ç success ææ¡å¼ç¨ |
| | | OperateLogUtils.putImportSummary(0, null, null); |
| | | return success("没æå¯å¯¼å
¥çæ°æ®"); |
| | | } |
| | | int successCount = 0; |
| | | int createCount = 0; // æ°å¢çè®°å½æ° |
| | | int updateCount = 0; // æ´æ°çè®°å½æ°ï¼ä»
å¾éãæ¯ææ´æ°ãæ¶æä¼èµ°æ´æ°ï¼ |
| | | Set<String> missingNames = new LinkedHashSet<>(); |
| | | Set<String> duplicateNames = new LinkedHashSet<>(); |
| | | Set<String> invalidRows = new LinkedHashSet<>(); |
| | |
| | | .location(item.getLocation()).remark(item.getRemark()) |
| | | .dataSource(2).build(); |
| | | attendanceRecordService.createImportedRecord(record); |
| | | createCount++; |
| | | } else if (Boolean.TRUE.equals(importReqVO.getUpdateSupport())) { |
| | | record.setClockInTime(item.getClockInTime()); |
| | | record.setClockOutTime(item.getClockOutTime()); |
| | |
| | | record.setLocation(item.getLocation()); |
| | | record.setRemark(item.getRemark()); |
| | | attendanceRecordService.updateImportedRecord(record); |
| | | updateCount++; |
| | | } |
| | | successCount++; |
| | | } |
| | |
| | | if (!invalidRows.isEmpty()) { |
| | | msg.append("ï¼ä»¥ä¸è®°å½æ°æ®ä¸å®æ´ï¼æªè½å¯¼å
¥ï¼").append(String.join("ã", invalidRows)); |
| | | } |
| | | // è®°å½å¯¼å
¥ç»ææ±æ»ï¼ä¾ @LogRecord ç success ææ¡å¼ç¨ |
| | | OperateLogUtils.putImportSummary(createCount, updateCount, |
| | | missingNames.size() + duplicateNames.size() + invalidRows.size()); |
| | | return success(msg.toString()); |
| | | } |
| | | |
| | |
| | | @Schema(description = "ç±»åï¼1-伿¯æ¥ï¼2-工使¥", example = "1") |
| | | private Integer type; |
| | | |
| | | @Schema(description = "æ¯å¦æ³å®è忥ï¼0-å¦ï¼1-æ¯", example = "1") |
| | | private Integer isLegal; |
| | | @Schema(description = "å¼å§æ¥æ", example = "2024-01-01") |
| | | private LocalDate startDate; |
| | | |
| | | @Schema(description = "æ¥æåºé´ï¼ä¼ [å¼å§æ¥æ, ç»ææ¥æ]ï¼è¿åä¸é
ç½®æ¶é´æ®µæäº¤éçæ°æ®", example = "[\"2026-01-01\", \"2026-12-31\"]") |
| | | private LocalDate[] date; |
| | | @Schema(description = "ç»ææ¥æ", example = "2024-12-31") |
| | | private LocalDate endDate; |
| | | |
| | | } |
| | |
| | | @ExcelProperty("è忥åç§°") |
| | | private String name; |
| | | |
| | | @Schema(description = "å¼å§æ¥æ") |
| | | @ExcelProperty("å¼å§æ¥æ") |
| | | private LocalDate startDate; |
| | | |
| | | @Schema(description = "ç»ææ¥æ") |
| | | @ExcelProperty("ç»ææ¥æ") |
| | | private LocalDate endDate; |
| | | @Schema(description = "æ¥æ") |
| | | @ExcelProperty("æ¥æ") |
| | | private LocalDate date; |
| | | |
| | | @Schema(description = "ç±»åï¼1-伿¯æ¥ï¼2-工使¥") |
| | | @ExcelProperty(value = "ç±»å", converter = DictConvert.class) |
| | |
| | | @NotBlank(message = "è忥åç§°ä¸è½ä¸ºç©º") |
| | | private String name; |
| | | |
| | | @Schema(description = "å¼å§æ¥æ", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-10") |
| | | @NotNull(message = "å¼å§æ¥æä¸è½ä¸ºç©º") |
| | | private LocalDate startDate; |
| | | |
| | | @Schema(description = "ç»ææ¥æ", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-17") |
| | | @NotNull(message = "ç»ææ¥æä¸è½ä¸ºç©º") |
| | | private LocalDate endDate; |
| | | @Schema(description = "æ¥æ", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-10") |
| | | @NotNull(message = "æ¥æä¸è½ä¸ºç©º") |
| | | private LocalDate date; |
| | | |
| | | @Schema(description = "ç±»åï¼1-伿¯æ¥ï¼2-工使¥", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "ç±»åä¸è½ä¸ºç©º") |
| | |
| | | @DictFormat("hrm_clock_type") |
| | | private Integer clockInType; |
| | | |
| | | @Schema(description = "ä¸çæå¡ç±»ååç§°", example = "æ£å¸¸") |
| | | private String clockInTypeName; |
| | | |
| | | @Schema(description = "ä¸çæå¡ç±»å") |
| | | @ExcelProperty(value = "ä¸çæå¡ç±»å", converter = DictConvert.class) |
| | | @DictFormat("hrm_clock_type") |
| | | private Integer clockOutType; |
| | | |
| | | @Schema(description = "ä¸çæå¡ç±»ååç§°", example = "æ©é") |
| | | @Schema(description = "ä¸çæå¡ç±»ååç§°ï¼æ£å¸¸/è¿å°/缺å¡ï¼") |
| | | private String clockInTypeName; |
| | | |
| | | @Schema(description = "ä¸çæå¡ç±»ååç§°ï¼æ£å¸¸/æ©é/缺å¡ï¼") |
| | | private String clockOutTypeName; |
| | | |
| | | @Schema(description = "工使¶é¿ï¼å°æ¶ï¼") |
| | |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.time.LocalTime; |
| | | |
| | |
| | | @ExcelProperty("å çèµ·ç®æ¶é¿") |
| | | private BigDecimal overtimeThreshold; |
| | | |
| | | @Schema(description = "çæå¼å§æ¥æï¼ç©ºè¡¨ç¤ºä¸éï¼") |
| | | @ExcelProperty("çæå¼å§æ¥æ") |
| | | private LocalDate effectiveStartDate; |
| | | |
| | | @Schema(description = "çæç»ææ¥æï¼ç©ºè¡¨ç¤ºä¸éï¼") |
| | | @ExcelProperty("çæç»ææ¥æ") |
| | | private LocalDate effectiveEndDate; |
| | | |
| | | @Schema(description = "ç¶æï¼0-å¯ç¨ï¼1-ç¦ç¨") |
| | | @ExcelProperty(value = "ç¶æ", converter = DictConvert.class) |
| | | @DictFormat("hrm_rule_status") |
| | |
| | | import jakarta.validation.constraints.NotBlank; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalTime; |
| | | |
| | | @Schema(description = "管çåå° - èå¤è§åæ°å¢/ä¿®æ¹ Request VO") |
| | |
| | | @Schema(description = "å çèµ·ç®æ¶é¿ï¼å°æ¶ï¼", example = "1") |
| | | private BigDecimal overtimeThreshold; |
| | | |
| | | @Schema(description = "çæå¼å§æ¥æï¼ç©ºè¡¨ç¤ºä¸éï¼", example = "2026-01-01") |
| | | private LocalDate effectiveStartDate; |
| | | |
| | | @Schema(description = "çæç»ææ¥æï¼ç©ºè¡¨ç¤ºä¸éï¼", example = "2026-12-31") |
| | | private LocalDate effectiveEndDate; |
| | | |
| | | @Schema(description = "ç¶æï¼0-å¯ç¨ï¼1-ç¦ç¨", example = "0") |
| | | private Integer status; |
| | | |
| | |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeContractSaveReqVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeContractTerminateReqVO; |
| | | import cn.iocoder.yudao.module.hrm.service.employee.HrmEmployeeContractService; |
| | | import cn.iocoder.yudao.module.hrm.service.employee.contract.ContractDocumentService; |
| | | import cn.iocoder.yudao.module.hrm.service.employee.contract.ByteArrayMultipartFile; |
| | | import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi; |
| | | import cn.iocoder.yudao.module.system.service.storage.SystemStorageBlobService; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.Parameter; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.io.IOException; |
| | | import java.io.InputStream; |
| | | import java.math.BigDecimal; |
| | | import java.net.URLEncoder; |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.time.LocalDate; |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.CONTRACT_TEMPLATE_LOAD_FAILED; |
| | | import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.CONTRACT_TEMPLATE_TYPE_NOT_SUPPORT; |
| | | |
| | | @Tag(name = "管çåå° - HRM åå·¥åå") |
| | | @RestController |
| | |
| | | |
| | | @Resource |
| | | private HrmEmployeeContractService contractService; |
| | | @Resource |
| | | private ContractDocumentService contractDocumentService; |
| | | @Resource |
| | | private SystemStorageBlobService storageBlobService; |
| | | @Resource |
| | | private StorageAttachmentApi storageAttachmentApi; |
| | | |
| | | @PostMapping("/create") |
| | | @Operation(summary = "å建åå·¥åå") |
| | |
| | | return success(contractService.getExpiringCount()); |
| | | } |
| | | |
| | | @PostMapping("/generate") |
| | | @Operation(summary = "çæååå¹¶åå为ååéä»¶") |
| | | @Parameter(name = "id", description = "ååID", required = true) |
| | | @Parameter(name = "templateType", description = "模æ¿ç±»åï¼1-å³å¨åå(ç®ç) 2-å³å¨åå(æ å) 3-å³å¡åå", required = true) |
| | | @PreAuthorize("@ss.hasPermission('hrm:employee-contract:query')") |
| | | public CommonResult<Long> generateContract(@RequestParam("id") Long id, |
| | | @RequestParam("templateType") Integer templateType) { |
| | | byte[] bytes = contractDocumentService.generateDocx(id, templateType); |
| | | String fileName = contractDocumentService.buildFileName(id, templateType); |
| | | MultipartFile file = new ByteArrayMultipartFile("file", fileName, |
| | | "application/vnd.openxmlformats-officedocument.wordprocessingml.document", bytes); |
| | | Long blobId = storageBlobService.upload(Collections.singletonList(file), false).get(0).getId(); |
| | | storageAttachmentApi.bindAttachments("file", "hrm_employee_contract", id, Collections.singletonList(blobId)); |
| | | return success(blobId); |
| | | } |
| | | |
| | | @GetMapping("/generate-docx") |
| | | @Operation(summary = "çæå¹¶ä¸è½½ååææ¡£ï¼docxï¼") |
| | | @Parameter(name = "id", description = "ååID", required = true) |
| | | @Parameter(name = "templateType", description = "模æ¿ç±»åï¼1-å³å¨åå(ç®ç) 2-å³å¨åå(æ å) 3-å³å¡åå", required = true) |
| | | @PreAuthorize("@ss.hasPermission('hrm:employee-contract:query')") |
| | | public void generateDocx(@RequestParam("id") Long id, |
| | | @RequestParam("templateType") Integer templateType, |
| | | HttpServletResponse response) throws IOException { |
| | | byte[] bytes = contractDocumentService.generateDocx(id, templateType); |
| | | String fileName = URLEncoder.encode(contractDocumentService.buildFileName(id, templateType), StandardCharsets.UTF_8); |
| | | response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document"); |
| | | response.setHeader("Content-Disposition", "attachment; filename=" + fileName + "; filename*=UTF-8''" + fileName); |
| | | response.getOutputStream().write(bytes); |
| | | } |
| | | |
| | | @GetMapping("/download-template") |
| | | @Operation(summary = "ä¸è½½ååç©ºç½æ¨¡æ¿", description = "1-å³å¨åå(ç®ç) 2-å³å¨åå(æ å) 3-å³å¡åå 4-éä¼è¿èåå") |
| | | @Parameter(name = "type", description = "模æ¿ç±»åï¼1-å³å¨åå(ç®ç) 2-å³å¨åå(æ å) 3-å³å¡åå 4-éä¼è¿èåå", required = true) |
| | | @PreAuthorize("@ss.hasPermission('hrm:employee-contract:query')") |
| | | public void downloadTemplate(@RequestParam("type") Integer type, HttpServletResponse response) throws IOException { |
| | | String path; |
| | | String fileName; |
| | | String contentType; |
| | | if (type == 1) { |
| | | path = "template/contract/original/labor_simple.docx"; |
| | | fileName = "å³å¨åå(ç®ç).docx"; |
| | | contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; |
| | | } else if (type == 2) { |
| | | path = "template/contract/original/labor_standard.docx"; |
| | | fileName = "å³å¨åå(æ å).docx"; |
| | | contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; |
| | | } else if (type == 3) { |
| | | path = "template/contract/original/service.doc"; |
| | | fileName = "å³å¡åå.doc"; |
| | | contentType = "application/msword"; |
| | | } else if (type == 4) { |
| | | path = "template/contract/original/retire.docx"; |
| | | fileName = "éä¼è¿èåå.docx"; |
| | | contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; |
| | | } else { |
| | | throw exception(CONTRACT_TEMPLATE_TYPE_NOT_SUPPORT); |
| | | } |
| | | try (InputStream in = getClass().getClassLoader().getResourceAsStream(path)) { |
| | | if (in == null) { |
| | | throw exception(CONTRACT_TEMPLATE_LOAD_FAILED); |
| | | } |
| | | byte[] bytes = in.readAllBytes(); |
| | | String enc = URLEncoder.encode(fileName, StandardCharsets.UTF_8); |
| | | response.setContentType(contentType); |
| | | response.setHeader("Content-Disposition", "attachment; filename=" + enc + "; filename*=UTF-8''" + enc); |
| | | response.getOutputStream().write(bytes); |
| | | } |
| | | } |
| | | |
| | | @GetMapping("/import-template") |
| | | @Operation(summary = "ä¸è½½åå·¥åå导å
¥æ¨¡æ¿") |
| | | @PreAuthorize("@ss.hasPermission('hrm:employee-contract:import')") |
| | |
| | | List<HrmEmployeeImportExcelVO> list = Arrays.asList( |
| | | HrmEmployeeImportExcelVO.builder().name("å¼ ä¸").gender(1).phone("13800138000") |
| | | .deptName("ç åé¨").postName("é¨é¨ç»ç,项ç®ç»ç").roleNames("æ®é管çå") |
| | | .hireDate(LocalDate.of(2020, 3, 1)).employeeStatus(1) |
| | | .idCard("320102199001150011").birthday(LocalDate.of(1990, 1, 15)) |
| | | .hireDate("2020-03-01").employeeStatus(1) |
| | | .idCard("320102199001150011").birthday("1990-01-15") |
| | | .nation("æ±æ").marriageStatus("å·²å©").age(36).education("æ¬ç§").politicalStatus("ä¸å
±å
å") |
| | | .email("zhangsan@example.com").build() |
| | | ); |
| | |
| | | return success(employeeService.importEmployeeList(list, importReqVO)); |
| | | } |
| | | |
| | | @PutMapping("/regular") |
| | | @Operation(summary = "å工转æ£", description = "ä»
ãå³å¨ååãåå·¥å¯è½¬æ£ï¼å³å¡ååä¸è½¬æ£ã转æ£åªæ¹ç¶æ+è½¬æ£æ¥æï¼ä¸å«è°å²") |
| | | @Parameter(name = "id", description = "åå·¥ID", required = true) |
| | | @PreAuthorize("@ss.hasPermission('hrm:employee:update')") |
| | | public CommonResult<Boolean> regularizeEmployee(@RequestParam("id") Long id) { |
| | | employeeService.regularizeEmployee(id); |
| | | return success(true); |
| | | } |
| | | |
| | | @GetMapping("/regularize-reminders") |
| | | @Operation(summary = "è·å¾å¾
è½¬æ£æéå表", description = "è¯ç¨ä¸ãä¸å½å为å³å¨ååãè¯ç¨æå¨ days 天å
å°æçåå·¥") |
| | | @PreAuthorize("@ss.hasPermission('hrm:employee:query')") |
| | | public CommonResult<List<HrmEmployeeRespVO>> getRegularizeReminders( |
| | | @RequestParam(value = "days", required = false, defaultValue = "7") Integer days) { |
| | | List<HrmEmployeeDO> list = employeeService.getRegularizeReminderList(days); |
| | | return success(BeanUtils.toBean(list, HrmEmployeeRespVO.class)); |
| | | } |
| | | |
| | | } |
| | |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDate; |
| | | |
| | | /** |
| | |
| | | private String roleNames; |
| | | |
| | | @ExcelProperty("å
¥èæ¥æ") |
| | | private LocalDate hireDate; |
| | | private String hireDate; |
| | | |
| | | @ExcelProperty(value = "åå·¥ç¶æ", converter = DictConvert.class) |
| | | @DictFormat("hrm_employee_status") |
| | | private Integer employeeStatus; |
| | | |
| | | @ExcelProperty(value = "å工类å«", converter = DictConvert.class) |
| | | @DictFormat("hrm_employee_category") |
| | | private Integer employeeCategory; |
| | | |
| | | @ExcelProperty("身份è¯å·") |
| | | private String idCard; |
| | | |
| | | @ExcelProperty("åºçæ¥æ") |
| | | private LocalDate birthday; |
| | | private String birthday; |
| | | |
| | | @ExcelProperty("æ°æ") |
| | | private String nation; |
| | |
| | | @ExcelProperty("夿³¨") |
| | | private String remark; |
| | | |
| | | @ExcelProperty("åºæ¬å·¥èµ") |
| | | private BigDecimal baseSalary; |
| | | |
| | | @ExcelProperty("弿·é¶è¡") |
| | | private String bankName; |
| | | |
| | | @ExcelProperty("é¶è¡è´¦å·") |
| | | private String bankAccount; |
| | | |
| | | @ExcelProperty("å±
ä½å°å") |
| | | private String address; |
| | | |
| | | } |
| | |
| | | import cn.idev.excel.annotation.ExcelProperty; |
| | | import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; |
| | | import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; |
| | | import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | |
| | | |
| | | @Schema(description = "æ§å«") |
| | | @ExcelProperty(value = "æ§å«", converter = DictConvert.class) |
| | | @DictFormat("system_user_sex") |
| | | @DictFormat("hrm_gender") |
| | | private Integer gender; |
| | | |
| | | @Schema(description = "åºçæ¥æ") |
| | |
| | | private LocalDate birthday; |
| | | |
| | | @Schema(description = "ææºå·ç ") |
| | | @ExcelProperty("ææºå·ç ") |
| | | @ExcelProperty("ææºå·") |
| | | private String phone; |
| | | |
| | | @Schema(description = "é®ç®±") |
| | |
| | | @DictFormat("hrm_employee_status") |
| | | private Integer employeeStatus; |
| | | |
| | | @Schema(description = "å工类å«") |
| | | @ExcelProperty(value = "å工类å«", converter = DictConvert.class) |
| | | @DictFormat("hrm_employee_category") |
| | | private Integer employeeCategory; |
| | | |
| | | @Schema(description = "å¹´åä½é¢ï¼å¤©ï¼") |
| | | @ExcelProperty("å¹´åä½é¢") |
| | | private BigDecimal annualLeaveBalance; |
| | |
| | | private List<Long> roleIds; |
| | | |
| | | @Schema(description = "è§è²åç§°å表") |
| | | @ExcelProperty("è§è²") |
| | | @ExcelProperty("ç¨æ·è§è²") |
| | | private String roleNames; |
| | | |
| | | @Schema(description = "弿·é¶è¡") |
| | |
| | | @ExcelProperty("å建æ¶é´") |
| | | private LocalDateTime createTime; |
| | | |
| | | @Schema(description = "éä»¶å表") |
| | | private List<StorageBlobRespDTO> attachmentList; |
| | | |
| | | } |
| | |
| | | @NotNull(message = "åå·¥ç¶æä¸è½ä¸ºç©º") |
| | | private Integer employeeStatus; |
| | | |
| | | @Schema(description = "å工类å«ï¼1-æ®éåå·¥ï¼2-æå·¥ï¼3-管ç", example = "1") |
| | | private Integer employeeCategory; |
| | | |
| | | @Schema(description = "å¹´åä½é¢ï¼å¤©ï¼", example = "5") |
| | | private BigDecimal annualLeaveBalance; |
| | | |
| | |
| | | @Valid |
| | | private List<HrmEmployeeEmergencyContactSaveReqVO> emergencyContacts; |
| | | |
| | | @Schema(description = "éä»¶ blobId å表", example = "[1, 2, 3]") |
| | | private List<Long> blobIds; |
| | | |
| | | } |
| | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Schema(description = "管çåå° - åå·¥ç®åä¿¡æ¯ Response VO") |
| | | @Data |
| | | public class HrmEmployeeSimpleRespVO { |
| | |
| | | @Schema(description = "åå·¥å§å") |
| | | private String name; |
| | | |
| | | @Schema(description = "é¨é¨ID") |
| | | private Long deptId; |
| | | |
| | | @Schema(description = "é¨é¨åç§°") |
| | | private String deptName; |
| | | |
| | | @Schema(description = "å²ä½IDå表") |
| | | private List<Long> postIds; |
| | | |
| | | @Schema(description = "å²ä½åç§°ï¼å¤ä¸ªç¨éå·åéï¼") |
| | | private String postName; |
| | | |
| | | @Schema(description = "åå·¥ç¶æ") |
| | | private Integer employeeStatus; |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.hrm.controller.admin.salary; |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.HrmOvertimeWageConfigRespVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.HrmOvertimeWageConfigSaveReqVO; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.HrmOvertimeWageConfigDO; |
| | | import cn.iocoder.yudao.module.hrm.service.salary.HrmOvertimeWageConfigService; |
| | | import cn.iocoder.yudao.module.system.api.dept.PostApi; |
| | | import cn.iocoder.yudao.module.system.api.dept.dto.PostRespDTO; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.Parameter; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jakarta.annotation.Resource; |
| | | import jakarta.validation.Valid; |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.DeleteMapping; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.PutMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | | |
| | | @Tag(name = "管çåå° - HRM å çå·¥èµç®¡ç") |
| | | @RestController |
| | | @RequestMapping("/hrm/salary/overtime-wage") |
| | | @Validated |
| | | public class HrmOvertimeWageConfigController { |
| | | |
| | | @Resource |
| | | private HrmOvertimeWageConfigService overtimeWageConfigService; |
| | | @Resource |
| | | private PostApi postApi; |
| | | |
| | | @PostMapping("/create") |
| | | @Operation(summary = "å建å²ä½å çå·¥èµé
ç½®") |
| | | @PreAuthorize("@ss.hasPermission('hrm:overtime-wage:create')") |
| | | public CommonResult<Long> createOvertimeWageConfig(@Valid @RequestBody HrmOvertimeWageConfigSaveReqVO reqVO) { |
| | | return success(overtimeWageConfigService.createOvertimeWageConfig( |
| | | BeanUtils.toBean(reqVO, HrmOvertimeWageConfigDO.class))); |
| | | } |
| | | |
| | | @PutMapping("/update") |
| | | @Operation(summary = "æ´æ°å²ä½å çå·¥èµé
ç½®") |
| | | @PreAuthorize("@ss.hasPermission('hrm:overtime-wage:update')") |
| | | public CommonResult<Boolean> updateOvertimeWageConfig(@Valid @RequestBody HrmOvertimeWageConfigSaveReqVO reqVO) { |
| | | overtimeWageConfigService.updateOvertimeWageConfig(BeanUtils.toBean(reqVO, HrmOvertimeWageConfigDO.class)); |
| | | return success(true); |
| | | } |
| | | |
| | | @DeleteMapping("/delete") |
| | | @Operation(summary = "å é¤å²ä½å çå·¥èµé
ç½®") |
| | | @Parameter(name = "id", description = "é
ç½®ç¼å·", required = true) |
| | | @PreAuthorize("@ss.hasPermission('hrm:overtime-wage:delete')") |
| | | public CommonResult<Boolean> deleteOvertimeWageConfig(@RequestParam("id") Long id) { |
| | | overtimeWageConfigService.deleteOvertimeWageConfig(id); |
| | | return success(true); |
| | | } |
| | | |
| | | @GetMapping("/get") |
| | | @Operation(summary = "è·å¾å²ä½å çå·¥èµé
ç½®") |
| | | @Parameter(name = "id", description = "é
ç½®ç¼å·", required = true) |
| | | @PreAuthorize("@ss.hasPermission('hrm:overtime-wage:query')") |
| | | public CommonResult<HrmOvertimeWageConfigRespVO> getOvertimeWageConfig(@RequestParam("id") Long id) { |
| | | return success(convert(overtimeWageConfigService.getOvertimeWageConfig(id))); |
| | | } |
| | | |
| | | @GetMapping("/list") |
| | | @Operation(summary = "è·å¾å²ä½å çå·¥èµé
ç½®å表") |
| | | @PreAuthorize("@ss.hasPermission('hrm:overtime-wage:query')") |
| | | public CommonResult<List<HrmOvertimeWageConfigRespVO>> getOvertimeWageConfigList( |
| | | @RequestParam(value = "postId", required = false) Long postId, |
| | | @RequestParam(value = "status", required = false) Integer status) { |
| | | return success(convertList(overtimeWageConfigService.getOvertimeWageConfigList(postId, status))); |
| | | } |
| | | |
| | | private HrmOvertimeWageConfigRespVO convert(HrmOvertimeWageConfigDO config) { |
| | | if (config == null) { |
| | | return null; |
| | | } |
| | | return convertList(Collections.singletonList(config)).get(0); |
| | | } |
| | | |
| | | private List<HrmOvertimeWageConfigRespVO> convertList(List<HrmOvertimeWageConfigDO> list) { |
| | | List<HrmOvertimeWageConfigRespVO> voList = BeanUtils.toBean(list, HrmOvertimeWageConfigRespVO.class); |
| | | List<Long> postIds = voList.stream().map(HrmOvertimeWageConfigRespVO::getPostId) |
| | | .filter(Objects::nonNull).distinct().collect(Collectors.toList()); |
| | | if (CollUtil.isNotEmpty(postIds)) { |
| | | Map<Long, PostRespDTO> postMap = postApi.getPostMap(postIds); |
| | | voList.forEach(vo -> { |
| | | PostRespDTO post = postMap.get(vo.getPostId()); |
| | | if (post != null) { |
| | | vo.setPostName(post.getName()); |
| | | } |
| | | }); |
| | | } |
| | | return voList; |
| | | } |
| | | |
| | | } |
| | |
| | | |
| | | @PostMapping("/save") |
| | | @Operation(summary = "æ¹éä¿åèªé
¬æ ¸ç®è®°å½") |
| | | @PreAuthorize("@ss.hasPermission('hrm:salary-calculation:save')") |
| | | @PreAuthorize("@ss.hasPermission('hrm:salary-calculation:calculate')") |
| | | public CommonResult<Boolean> saveSalaryCalculation(@Valid @RequestBody HrmSalaryCalculationSaveReqVO saveReqVO) { |
| | | // 转æ¢ä¸º DO å表 |
| | | List<HrmSalaryCalculationDO> calculationList = saveReqVO.getList().stream() |
| | |
| | | return success(true); |
| | | } |
| | | |
| | | @PutMapping("/update") |
| | | @Operation(summary = "ä¿®æ¹èªé
¬æ ¸ç®è®°å½", description = "ä»
å¾
ç¡®è®¤ç¶æå¯ä¿®æ¹ï¼å®åå·¥èµç±åç«¯ææ¶å
¥/æ£é¤é¡¹éç®") |
| | | @PreAuthorize("@ss.hasPermission('hrm:salary-calculation:calculate')") |
| | | public CommonResult<Boolean> updateSalaryCalculation(@Valid @RequestBody HrmSalaryCalculationRespVO updateReqVO) { |
| | | HrmSalaryCalculationDO calculation = BeanUtils.toBean(updateReqVO, HrmSalaryCalculationDO.class); |
| | | salaryCalculationService.updateSalaryCalculation(calculation); |
| | | return success(true); |
| | | } |
| | | |
| | | } |
| | |
| | | |
| | | @PostMapping("/create") |
| | | @Operation(summary = "å建社ä¿å
¬ç§¯éé
ç½®") |
| | | @PreAuthorize("@ss.hasPermission('hrm:social-security:create')") |
| | | @PreAuthorize("@ss.hasPermission('hrm:social-security-config:create')") |
| | | public CommonResult<Long> createSocialSecurityConfig(@Valid @RequestBody HrmSocialSecurityConfigSaveReqVO createReqVO) { |
| | | HrmSocialSecurityConfigDO config = BeanUtils.toBean(createReqVO, HrmSocialSecurityConfigDO.class); |
| | | return success(socialSecurityConfigService.createSocialSecurityConfig(config)); |
| | |
| | | |
| | | @PutMapping("/update") |
| | | @Operation(summary = "æ´æ°ç¤¾ä¿å
¬ç§¯éé
ç½®") |
| | | @PreAuthorize("@ss.hasPermission('hrm:social-security:update')") |
| | | @PreAuthorize("@ss.hasPermission('hrm:social-security-config:update')") |
| | | public CommonResult<Boolean> updateSocialSecurityConfig(@Valid @RequestBody HrmSocialSecurityConfigSaveReqVO updateReqVO) { |
| | | HrmSocialSecurityConfigDO config = BeanUtils.toBean(updateReqVO, HrmSocialSecurityConfigDO.class); |
| | | socialSecurityConfigService.updateSocialSecurityConfig(config); |
| | |
| | | @DeleteMapping("/delete") |
| | | @Operation(summary = "å é¤ç¤¾ä¿å
¬ç§¯éé
ç½®") |
| | | @Parameter(name = "id", description = "é
ç½®ID", required = true) |
| | | @PreAuthorize("@ss.hasPermission('hrm:social-security:delete')") |
| | | @PreAuthorize("@ss.hasPermission('hrm:social-security-config:delete')") |
| | | public CommonResult<Boolean> deleteSocialSecurityConfig(@RequestParam("id") Long id) { |
| | | socialSecurityConfigService.deleteSocialSecurityConfig(id); |
| | | return success(true); |
| | |
| | | @GetMapping("/get") |
| | | @Operation(summary = "è·å社ä¿å
¬ç§¯éé
ç½®") |
| | | @Parameter(name = "id", description = "é
ç½®ID", required = true) |
| | | @PreAuthorize("@ss.hasPermission('hrm:social-security:query')") |
| | | @PreAuthorize("@ss.hasPermission('hrm:social-security-config:query')") |
| | | public CommonResult<HrmSocialSecurityConfigRespVO> getSocialSecurityConfig(@RequestParam("id") Long id) { |
| | | HrmSocialSecurityConfigDO config = socialSecurityConfigService.getSocialSecurityConfig(id); |
| | | return success(BeanUtils.toBean(config, HrmSocialSecurityConfigRespVO.class)); |
| | |
| | | |
| | | @GetMapping("/list") |
| | | @Operation(summary = "è·å社ä¿å
¬ç§¯éé
ç½®å表") |
| | | @PreAuthorize("@ss.hasPermission('hrm:social-security:query')") |
| | | @PreAuthorize("@ss.hasPermission('hrm:social-security-config:query')") |
| | | public CommonResult<List<HrmSocialSecurityConfigRespVO>> getSocialSecurityConfigList() { |
| | | List<HrmSocialSecurityConfigDO> list = socialSecurityConfigService.getSocialSecurityConfigList(); |
| | | return success(BeanUtils.toBean(list, HrmSocialSecurityConfigRespVO.class)); |
| | |
| | | @GetMapping("/get-effective") |
| | | @Operation(summary = "è·åçæç社ä¿å
¬ç§¯éé
ç½®") |
| | | @Parameter(name = "effectiveDate", description = "çææ¥æ", required = true) |
| | | @PreAuthorize("@ss.hasPermission('hrm:social-security:query')") |
| | | @PreAuthorize("@ss.hasPermission('hrm:social-security-config:query')") |
| | | public CommonResult<HrmSocialSecurityConfigRespVO> getEffectiveSocialSecurityConfig( |
| | | @RequestParam("effectiveDate") LocalDate effectiveDate) { |
| | | HrmSocialSecurityConfigDO config = socialSecurityConfigService.getEffectiveSocialSecurityConfig(effectiveDate); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Schema(description = "管çåå° - å²ä½å çå·¥èµé
ç½® Response VO") |
| | | @Data |
| | | public class HrmOvertimeWageConfigRespVO { |
| | | |
| | | @Schema(description = "é
ç½®ç¼å·") |
| | | private Long id; |
| | | |
| | | @Schema(description = "å²ä½ID") |
| | | private Long postId; |
| | | |
| | | @Schema(description = "å²ä½åç§°") |
| | | private String postName; |
| | | |
| | | @Schema(description = "å çæ¶èªï¼å
/å°æ¶ï¼") |
| | | private BigDecimal hourlyRate; |
| | | |
| | | @Schema(description = "æ¯ææå¤§å çæ¶é¿ä¸éï¼å°æ¶ï¼ï¼ä¸ºç©ºè¡¨ç¤ºä¸é") |
| | | private BigDecimal maxOvertimeHours; |
| | | |
| | | @Schema(description = "ç¶æï¼0-å¯ç¨ï¼1-ç¦ç¨") |
| | | private Integer status; |
| | | |
| | | @Schema(description = "夿³¨") |
| | | private String remark; |
| | | |
| | | @Schema(description = "å建æ¶é´") |
| | | private LocalDateTime createTime; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.DecimalMin; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import jakarta.validation.constraints.Size; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | @Schema(description = "管çåå° - å²ä½å çå·¥èµé
ç½®æ°å¢/ä¿®æ¹ Request VO") |
| | | @Data |
| | | public class HrmOvertimeWageConfigSaveReqVO { |
| | | |
| | | @Schema(description = "é
ç½®ç¼å·", example = "1") |
| | | private Long id; |
| | | |
| | | @Schema(description = "å²ä½ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "å²ä½ä¸è½ä¸ºç©º") |
| | | private Long postId; |
| | | |
| | | @Schema(description = "å çæ¶èªï¼å
/å°æ¶ï¼ï¼é»è®¤ 20", requiredMode = Schema.RequiredMode.REQUIRED, example = "20.00") |
| | | @NotNull(message = "å çæ¶èªä¸è½ä¸ºç©º") |
| | | @DecimalMin(value = "0", message = "å çæ¶èªä¸è½å°äº 0") |
| | | private BigDecimal hourlyRate; |
| | | |
| | | @Schema(description = "æ¯ææå¤§å çæ¶é¿ä¸éï¼å°æ¶ï¼ï¼ä¸ºç©ºè¡¨ç¤ºä¸é", example = "36.0") |
| | | @DecimalMin(value = "0", message = "æå¤§å çæ¶é¿ä¸è½å°äº 0") |
| | | private BigDecimal maxOvertimeHours; |
| | | |
| | | @Schema(description = "ç¶æï¼0-å¯ç¨ï¼1-ç¦ç¨", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | @NotNull(message = "ç¶æä¸è½ä¸ºç©º") |
| | | private Integer status; |
| | | |
| | | @Schema(description = "夿³¨") |
| | | @Size(max = 500, message = "夿³¨ä¸è½è¶
è¿ 500 个å符") |
| | | private String remark; |
| | | |
| | | } |
| | |
| | | @Schema(description = "çè²ä¿é©ä¼ä¸ç¼´çº³æ¯ä¾", example = "0.005") |
| | | private BigDecimal maternityRatio; |
| | | |
| | | // ========== 大ç
å»çæ¯ä¾ ========== |
| | | @Schema(description = "大ç
å»çä¼ä¸ç¼´çº³æ¯ä¾", example = "0.005") |
| | | private BigDecimal seriousIllnessCompanyRatio; |
| | | |
| | | @Schema(description = "大ç
å»ç个人缴纳æ¯ä¾", example = "0.005") |
| | | private BigDecimal seriousIllnessPersonalRatio; |
| | | |
| | | // ========== é¿ææ¤çæ¯ä¾ ========== |
| | | @Schema(description = "é¿ææ¤çä¼ä¸ç¼´çº³æ¯ä¾", example = "0.003") |
| | | private BigDecimal longTermCareCompanyRatio; |
| | | |
| | | @Schema(description = "é¿ææ¤ç个人缴纳æ¯ä¾", example = "0.003") |
| | | private BigDecimal longTermCarePersonalRatio; |
| | | |
| | | // ========== å
¬ç§¯éæ¯ä¾ ========== |
| | | @Schema(description = "å
¬ç§¯éä¼ä¸ç¼´çº³æ¯ä¾", example = "0.12") |
| | | private BigDecimal housingFundCompanyRatio; |
| | |
| | | @Schema(description = "çè²ä¿é©ä¼ä¸ç¼´çº³æ¯ä¾", example = "0.005") |
| | | private BigDecimal maternityRatio; |
| | | |
| | | // ========== 大ç
å»çæ¯ä¾ ========== |
| | | @Schema(description = "大ç
å»çä¼ä¸ç¼´çº³æ¯ä¾", example = "0.005") |
| | | private BigDecimal seriousIllnessCompanyRatio; |
| | | |
| | | @Schema(description = "大ç
å»ç个人缴纳æ¯ä¾", example = "0.005") |
| | | private BigDecimal seriousIllnessPersonalRatio; |
| | | |
| | | // ========== é¿ææ¤çæ¯ä¾ ========== |
| | | @Schema(description = "é¿ææ¤çä¼ä¸ç¼´çº³æ¯ä¾", example = "0.003") |
| | | private BigDecimal longTermCareCompanyRatio; |
| | | |
| | | @Schema(description = "é¿ææ¤ç个人缴纳æ¯ä¾", example = "0.003") |
| | | private BigDecimal longTermCarePersonalRatio; |
| | | |
| | | // ========== å
¬ç§¯éæ¯ä¾ ========== |
| | | @Schema(description = "å
¬ç§¯éä¼ä¸ç¼´çº³æ¯ä¾", example = "0.12") |
| | | private BigDecimal housingFundCompanyRatio; |
| | |
| | | private String name; |
| | | |
| | | /** |
| | | * æ¥æï¼å
¼å®¹åé NOT NULL åçåä½å段ï¼å§ç»åå
¥ startDateï¼ |
| | | * æ¥æ |
| | | */ |
| | | private LocalDate date; |
| | | |
| | | /** |
| | | * å¼å§æ¥æ |
| | | */ |
| | | private LocalDate startDate; |
| | | |
| | | /** |
| | | * ç»ææ¥æ |
| | | */ |
| | | private LocalDate endDate; |
| | | |
| | | /** |
| | | * ç±»åï¼1-伿¯æ¥ï¼2-工使¥ |
| | |
| | | private BigDecimal overtimeThreshold; |
| | | |
| | | /** |
| | | * çæå¼å§æ¥æï¼ç©ºè¡¨ç¤ºä¸éï¼ |
| | | */ |
| | | private LocalDate effectiveStartDate; |
| | | |
| | | /** |
| | | * çæç»ææ¥æï¼ç©ºè¡¨ç¤ºä¸éï¼ |
| | | */ |
| | | private LocalDate effectiveEndDate; |
| | | |
| | | /** |
| | | * ç¶æï¼0-å¯ç¨ï¼1-ç¦ç¨ |
| | | */ |
| | | private Integer status; |
| | |
| | | private Integer employeeStatus; |
| | | |
| | | /** |
| | | * åå·¥ç±»å« |
| | | * |
| | | * 1-æ®éåå·¥ï¼2-æå·¥ï¼3-管çï¼ç¨äºå çå·¥èµå·®å¼åæ ¸ç® |
| | | */ |
| | | private Integer employeeCategory; |
| | | |
| | | /** |
| | | * å¹´åä½é¢ï¼å¤©ï¼ |
| | | */ |
| | | private BigDecimal annualLeaveBalance; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.hrm.dal.dataobject.salary; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import com.baomidou.mybatisplus.annotation.KeySequence; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.*; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | /** |
| | | * å²ä½å çå·¥èµé
ç½® DO |
| | | * |
| | | * <p>ç¨äºãå çå·¥èµç®¡çãï¼ä¸åå²ä½ï¼å·¥ç§ï¼é
ç½®ä¸åçå çæ¶èªä¸æå¤§å çæ¶é¿ä¸éã |
| | | * æ ¸ç®æ¶æ®éåå·¥ææ¤é
ç½®çåºå®æ¶èªè®¡å çå·¥èµã |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @TableName("hrm_overtime_wage_config") |
| | | @KeySequence("hrm_overtime_wage_config_seq") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @ToString(callSuper = true) |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class HrmOvertimeWageConfigDO extends BaseDO { |
| | | |
| | | /** |
| | | * é
ç½®ID |
| | | */ |
| | | @TableId |
| | | private Long id; |
| | | |
| | | /** |
| | | * å²ä½ID |
| | | * |
| | | * å
³è PostDO#getId() |
| | | */ |
| | | private Long postId; |
| | | |
| | | /** |
| | | * å çæ¶èªï¼å
/å°æ¶ï¼ï¼é»è®¤ 20 |
| | | */ |
| | | private BigDecimal hourlyRate; |
| | | |
| | | /** |
| | | * æ¯ææå¤§å çæ¶é¿ä¸éï¼å°æ¶ï¼ |
| | | * |
| | | * 为空表示ä¸éå¶ |
| | | */ |
| | | private BigDecimal maxOvertimeHours; |
| | | |
| | | /** |
| | | * ç¶æï¼0-å¯ç¨ï¼1-ç¦ç¨ |
| | | */ |
| | | private Integer status; |
| | | |
| | | /** |
| | | * 夿³¨ |
| | | */ |
| | | private String remark; |
| | | |
| | | } |
| | |
| | | */ |
| | | private BigDecimal maternityRatio; |
| | | |
| | | // ========== 大ç
å»çæ¯ä¾ ========== |
| | | /** |
| | | * 大ç
å»çä¼ä¸ç¼´çº³æ¯ä¾ |
| | | */ |
| | | private BigDecimal seriousIllnessCompanyRatio; |
| | | /** |
| | | * 大ç
å»ç个人缴纳æ¯ä¾ |
| | | */ |
| | | private BigDecimal seriousIllnessPersonalRatio; |
| | | |
| | | // ========== é¿ææ¤çæ¯ä¾ ========== |
| | | /** |
| | | * é¿ææ¤çä¼ä¸ç¼´çº³æ¯ä¾ |
| | | */ |
| | | private BigDecimal longTermCareCompanyRatio; |
| | | /** |
| | | * é¿ææ¤ç个人缴纳æ¯ä¾ |
| | | */ |
| | | private BigDecimal longTermCarePersonalRatio; |
| | | |
| | | // ========== å
¬ç§¯éæ¯ä¾ ========== |
| | | /** |
| | | * å
¬ç§¯éä¼ä¸ç¼´çº³æ¯ä¾ |
| | |
| | | public interface HrmAttendanceHolidayMapper extends BaseMapperX<HrmAttendanceHolidayDO> { |
| | | |
| | | default PageResult<HrmAttendanceHolidayDO> selectPage(HrmAttendanceHolidayPageReqVO reqVO) { |
| | | LambdaQueryWrapperX<HrmAttendanceHolidayDO> query = new LambdaQueryWrapperX<HrmAttendanceHolidayDO>() |
| | | return selectPage(reqVO, new LambdaQueryWrapperX<HrmAttendanceHolidayDO>() |
| | | .likeIfPresent(HrmAttendanceHolidayDO::getName, reqVO.getName()) |
| | | .eqIfPresent(HrmAttendanceHolidayDO::getType, reqVO.getType()) |
| | | .eqIfPresent(HrmAttendanceHolidayDO::getIsLegal, reqVO.getIsLegal()); |
| | | // æ¥æçéï¼é
ç½®æ¶é´æ®µä¸æ¥è¯¢åºé´æäº¤éå³å½ä¸ |
| | | LocalDate[] dateRange = reqVO.getDate(); |
| | | if (dateRange != null && dateRange.length >= 2 && dateRange[0] != null && dateRange[1] != null) { |
| | | query.le(HrmAttendanceHolidayDO::getStartDate, dateRange[1]) |
| | | .ge(HrmAttendanceHolidayDO::getEndDate, dateRange[0]); |
| | | } |
| | | return selectPage(reqVO, query.orderByDesc(HrmAttendanceHolidayDO::getStartDate) |
| | | .orderByDesc(HrmAttendanceHolidayDO::getId)); |
| | | .betweenIfPresent(HrmAttendanceHolidayDO::getDate, reqVO.getStartDate(), reqVO.getEndDate()) |
| | | .orderByDesc(HrmAttendanceHolidayDO::getDate)); |
| | | } |
| | | |
| | | /** |
| | | * æ¥è¯¢æ¶é´æ®µä¸ [startDate, endDate] æäº¤éçé
ç½® |
| | | * ä¼ å
¥åä¸å¤©ï¼date, dateï¼æ¶ï¼å³æ¥è¯¢è¦ç该æ¥çé
ç½® |
| | | */ |
| | | default List<HrmAttendanceHolidayDO> selectListByDateRange(LocalDate startDate, LocalDate endDate) { |
| | | default HrmAttendanceHolidayDO selectByDate(LocalDate date) { |
| | | return selectOne(HrmAttendanceHolidayDO::getDate, date); |
| | | } |
| | | |
| | | default List<HrmAttendanceHolidayDO> selectListByDateBetween(LocalDate startDate, LocalDate endDate) { |
| | | return selectList(new LambdaQueryWrapperX<HrmAttendanceHolidayDO>() |
| | | .le(HrmAttendanceHolidayDO::getStartDate, endDate) |
| | | .ge(HrmAttendanceHolidayDO::getEndDate, startDate) |
| | | .orderByAsc(HrmAttendanceHolidayDO::getStartDate) |
| | | .orderByAsc(HrmAttendanceHolidayDO::getId)); |
| | | .ge(HrmAttendanceHolidayDO::getDate, startDate) |
| | | .le(HrmAttendanceHolidayDO::getDate, endDate) |
| | | .orderByAsc(HrmAttendanceHolidayDO::getDate)); |
| | | } |
| | | |
| | | } |
| | |
| | | .orderByAsc(HrmAttendanceRuleDO::getName)); |
| | | } |
| | | |
| | | /** |
| | | * æ¥è¯¢é¨é¨ä¸å·²å¯ç¨çè§åï¼ææ°å建çæå¨åé¢ |
| | | * |
| | | * åä¸é¨é¨å¯é
ç½®å¤æ¡å¯ç¨è§åï¼å¤ç次ï¼ï¼ç±è°ç¨æ¹ææå¡æ¶é´å¹é
çæ¬¡ |
| | | */ |
| | | default List<HrmAttendanceRuleDO> selectEnabledListByDeptId(Long deptId) { |
| | | return selectList(new LambdaQueryWrapperX<HrmAttendanceRuleDO>() |
| | | .eq(HrmAttendanceRuleDO::getDeptId, deptId) |
| | | .eq(HrmAttendanceRuleDO::getStatus, 0) |
| | | .orderByDesc(HrmAttendanceRuleDO::getId)); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.hrm.dal.mysql.salary; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.HrmOvertimeWageConfigDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | import java.util.Collection; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * å²ä½å çå·¥èµé
ç½® Mapper |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Mapper |
| | | public interface HrmOvertimeWageConfigMapper extends BaseMapperX<HrmOvertimeWageConfigDO> { |
| | | |
| | | default List<HrmOvertimeWageConfigDO> selectListByCondition(Long postId, Integer status) { |
| | | return selectList(new LambdaQueryWrapperX<HrmOvertimeWageConfigDO>() |
| | | .eqIfPresent(HrmOvertimeWageConfigDO::getPostId, postId) |
| | | .eqIfPresent(HrmOvertimeWageConfigDO::getStatus, status) |
| | | .orderByDesc(HrmOvertimeWageConfigDO::getId)); |
| | | } |
| | | |
| | | default HrmOvertimeWageConfigDO selectByPostId(Long postId) { |
| | | return selectOne(HrmOvertimeWageConfigDO::getPostId, postId); |
| | | } |
| | | |
| | | /** |
| | | * æ¥è¯¢å¤ä¸ªå²ä½ä¸ãå¯ç¨ãçå çå·¥èµé
ç½®ï¼ç¨äºèªé
¬æ ¸ç®æ¶æåå·¥å²ä½å¹é
ã |
| | | */ |
| | | default List<HrmOvertimeWageConfigDO> selectEnabledListByPostIds(Collection<Long> postIds) { |
| | | return selectList(new LambdaQueryWrapperX<HrmOvertimeWageConfigDO>() |
| | | .inIfPresent(HrmOvertimeWageConfigDO::getPostId, postIds) |
| | | .eq(HrmOvertimeWageConfigDO::getStatus, 0)); |
| | | } |
| | | |
| | | } |
| | |
| | | } |
| | | |
| | | // 2. æ ¡éªåç±»åæµç¨å®ä¹æ¯å¦åå¨ |
| | | // å
¨å±å
审æ¹ï¼ä¸å¡æ ¡éªéè¿åç´æ¥æ§è¡å®¡æ¹éè¿é»è¾ï¼æ éæµç¨æ¨¡åã |
| | | if (bpmProcessInstanceApi.isGlobalAutoApprove()) { |
| | | leaveApplicationMapper.updateById(HrmLeaveApplicationDO.builder() |
| | | .id(id) |
| | | .processInstanceId("") |
| | | .status(HrmAuditStatusEnum.PROCESS.getStatus()) |
| | | .build()); |
| | | updateLeaveApplicationAuditStatus(id, HrmAuditStatusEnum.APPROVE.getStatus()); |
| | | return; |
| | | } |
| | | |
| | | validateApproveCategoryAndProcessDefinition(); |
| | | |
| | | // 3. æ ¡éªä¼ å
¥çæµç¨å®ä¹ Key æ¯å¦å±äºè¯¥åç±»ä¸å¤äºæ¿æ´»ç¶æ |
| | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void submit(Long id, String processDefinitionKey, Long loginUserId) { |
| | | HrmOvertimeApplicationDO application = requireOwnedDraft(id, loginUserId); |
| | | // å
¨å±å
审æ¹ï¼ä¸å¡æ ¡éªéè¿åç´æ¥æ§è¡å®¡æ¹éè¿é»è¾ï¼æ éæµç¨æ¨¡åã |
| | | if (bpmProcessInstanceApi.isGlobalAutoApprove()) { |
| | | mapper.updateById(HrmOvertimeApplicationDO.builder().id(id).processInstanceId("") |
| | | .status(HrmAuditStatusEnum.PROCESS.getStatus()).build()); |
| | | updateAuditStatus(id, HrmAuditStatusEnum.APPROVE.getStatus()); |
| | | return; |
| | | } |
| | | |
| | | validateProcessDefinitions(); |
| | | ProcessDefinition definition = bpmProcessDefinitionService.getActiveProcessDefinition(processDefinitionKey); |
| | | if (definition == null || !BPM_PROCESS_DEFINITION_KEY.equals(definition.getKey())) { |
| | |
| | | if (ObjUtil.notEqual(application.getStatus(), HrmAuditStatusEnum.DRAFT.getStatus())) { |
| | | throw exception(RESIGNATION_APPLICATION_SUBMIT_FAIL_NOT_DRAFT); |
| | | } |
| | | // å
¨å±å
审æ¹ï¼ä¸å¡æ ¡éªéè¿åç´æ¥æ§è¡å®¡æ¹éè¿é»è¾ï¼æ éæµç¨æ¨¡åã |
| | | if (bpmProcessInstanceApi.isGlobalAutoApprove()) { |
| | | resignationApplicationMapper.updateById(HrmResignationApplicationDO.builder() |
| | | .id(id) |
| | | .processInstanceId("") |
| | | .status(HrmAuditStatusEnum.PROCESS.getStatus()) |
| | | .build()); |
| | | updateResignationApplicationAuditStatus(id, HrmAuditStatusEnum.APPROVE.getStatus()); |
| | | return; |
| | | } |
| | | |
| | | validateApproveCategoryAndProcessDefinition(); |
| | | ProcessDefinition processDefinition = bpmProcessDefinitionService.getActiveProcessDefinition(processDefinitionKey); |
| | | if (processDefinition == null) { |
| | |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.approval.vo.HrmTransferApplicationRespVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.approval.vo.HrmTransferApplicationSaveReqVO; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.approval.HrmTransferApplicationDO; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeDO; |
| | | import cn.iocoder.yudao.module.hrm.dal.mysql.approval.HrmTransferApplicationMapper; |
| | | import cn.iocoder.yudao.module.hrm.service.employee.HrmEmployeeService; |
| | | import cn.iocoder.yudao.module.hrm.dal.redis.HrmNoRedisDAO; |
| | | import cn.iocoder.yudao.module.hrm.enums.HrmAuditStatusEnum; |
| | | import cn.iocoder.yudao.module.hrm.enums.HrmTransferTypeEnum; |
| | |
| | | @Resource |
| | | private AdminUserApi adminUserApi; |
| | | @Resource |
| | | private HrmEmployeeService employeeService; |
| | | @Resource |
| | | private BpmProcessInstanceApi bpmProcessInstanceApi; |
| | | @Resource |
| | | private BpmCategoryService bpmCategoryService; |
| | |
| | | } |
| | | // 3. çæç³è¯·åå· |
| | | String no = generateUniqueNo(); |
| | | // 4. 设置ç³è¯·äººä¿¡æ¯ |
| | | // 4. 设置è°å²äººï¼åå·¥IDï¼ï¼ä¸ºç©ºæ¶åå½åç»å½ç¨æ·å¯¹åºçåå·¥ |
| | | Long userId = createReqVO.getUserId(); |
| | | if (userId == null) { |
| | | userId = getCurrentUserId(); |
| | | HrmEmployeeDO currentEmployee = employeeService.getEmployeeByUserId(getCurrentUserId()); |
| | | userId = currentEmployee != null ? currentEmployee.getId() : null; |
| | | } |
| | | AdminUserRespDTO user = adminUserApi.getUser(userId); |
| | | if (user == null) { |
| | | userId = getCurrentUserId(); |
| | | if (userId != null && employeeService.getEmployee(userId) == null) { |
| | | throw exception(EMPLOYEE_NOT_EXISTS); |
| | | } |
| | | // 5. æå
¥ |
| | | HrmTransferApplicationDO application = BeanUtils.toBean(createReqVO, HrmTransferApplicationDO.class); |
| | |
| | | } |
| | | HrmTransferApplicationRespVO respVO = BeanUtils.toBean(application, HrmTransferApplicationRespVO.class); |
| | | if (application.getUserId() != null) { |
| | | AdminUserRespDTO user = adminUserApi.getUser(application.getUserId()); |
| | | if (user != null) { |
| | | respVO.setUserName(user.getNickname()); |
| | | HrmEmployeeDO employee = employeeService.getEmployee(application.getUserId()); |
| | | if (employee != null) { |
| | | respVO.setUserName(employee.getName()); |
| | | } |
| | | } |
| | | fillDeptNames(respVO, application); |
| | |
| | | .map(HrmTransferApplicationDO::getUserId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toSet()); |
| | | Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds); |
| | | // userId åçæ¯åå·¥IDï¼æå工表æ¥è¯¢å§å |
| | | Map<Long, String> employeeNameMap = new HashMap<>(); |
| | | for (Long userId : userIds) { |
| | | HrmEmployeeDO employee = employeeService.getEmployee(userId); |
| | | if (employee != null) { |
| | | employeeNameMap.put(userId, employee.getName()); |
| | | } |
| | | } |
| | | Set<Long> deptIds = pageResult.getList().stream() |
| | | .flatMap(app -> Stream.of(app.getCurrentDeptId(), app.getTargetDeptId())) |
| | | .filter(Objects::nonNull) |
| | |
| | | Map<Long, PostRespDTO> postMap = postApi.getPostMap(postIds); |
| | | List<HrmTransferApplicationRespVO> list = pageResult.getList().stream().map(app -> { |
| | | HrmTransferApplicationRespVO vo = BeanUtils.toBean(app, HrmTransferApplicationRespVO.class); |
| | | if (app.getUserId() != null && userMap.containsKey(app.getUserId())) { |
| | | vo.setUserName(userMap.get(app.getUserId()).getNickname()); |
| | | if (app.getUserId() != null && employeeNameMap.containsKey(app.getUserId())) { |
| | | vo.setUserName(employeeNameMap.get(app.getUserId())); |
| | | } |
| | | if (app.getCurrentDeptId() != null && deptMap.containsKey(app.getCurrentDeptId())) { |
| | | vo.setCurrentDeptName(deptMap.get(app.getCurrentDeptId()).getName()); |
| | |
| | | if (ObjUtil.notEqual(application.getStatus(), HrmAuditStatusEnum.DRAFT.getStatus())) { |
| | | throw exception(TRANSFER_APPLICATION_SUBMIT_FAIL_NOT_DRAFT); |
| | | } |
| | | // å
¨å±å
审æ¹ï¼ä¸å¡æ ¡éªéè¿åç´æ¥æ§è¡å®¡æ¹éè¿é»è¾ï¼æ éæµç¨æ¨¡åã |
| | | if (bpmProcessInstanceApi.isGlobalAutoApprove()) { |
| | | transferApplicationMapper.updateById(HrmTransferApplicationDO.builder() |
| | | .id(id) |
| | | .processInstanceId("") |
| | | .status(HrmAuditStatusEnum.PROCESS.getStatus()) |
| | | .build()); |
| | | updateTransferApplicationAuditStatus(id, HrmAuditStatusEnum.APPROVE.getStatus()); |
| | | return; |
| | | } |
| | | |
| | | validateApproveCategoryAndProcessDefinition(); |
| | | ProcessDefinition processDefinition = bpmProcessDefinitionService.getActiveProcessDefinition(processDefinitionKey); |
| | | if (processDefinition == null) { |
| | |
| | | } |
| | | |
| | | // 2. æ ¡éªæµç¨å®ä¹åå¨ä¸ä¸ºæ¿æ´»ç¶æ |
| | | // å
¨å±å
审æ¹ï¼ä¸å¡æ ¡éªéè¿åç´æ¥æ§è¡å®¡æ¹éè¿é»è¾ï¼æ éæµç¨æ¨¡åã |
| | | if (bpmProcessInstanceApi.isGlobalAutoApprove()) { |
| | | attendanceExceptionMapper.updateById(HrmAttendanceExceptionDO.builder() |
| | | .id(id) |
| | | .processInstanceId("") |
| | | .status(HrmAuditStatusEnum.PROCESS.getStatus()) |
| | | .build()); |
| | | updateAttendanceExceptionAuditStatus(id, HrmAuditStatusEnum.APPROVE.getStatus()); |
| | | return; |
| | | } |
| | | |
| | | if (processDefinitionKey != null) { |
| | | bpmProcessDefinitionService.getActiveProcessDefinition(processDefinitionKey); |
| | | } |
| | |
| | | PageResult<HrmAttendanceHolidayDO> getAttendanceHolidayPage(HrmAttendanceHolidayPageReqVO pageReqVO); |
| | | |
| | | /** |
| | | * è·å¾æ¶é´æ®µä¸æå®æ¥æèå´æäº¤éçè忥å表 |
| | | * è·å¾æå®æ¥æèå´çè忥å表 |
| | | * |
| | | * @param startDate å¼å§æ¥æ |
| | | * @param endDate ç»ææ¥æ |
| | |
| | | |
| | | @Override |
| | | public Long createAttendanceHoliday(HrmAttendanceHolidaySaveReqVO createReqVO) { |
| | | validateDateRange(createReqVO); |
| | | validateDateRangeNoOverlap(createReqVO.getStartDate(), createReqVO.getEndDate(), null); |
| | | // æ ¡éªæ¥ææ¯å¦å·²åå¨é
ç½® |
| | | validateAttendanceHolidayDateExists(createReqVO.getDate()); |
| | | HrmAttendanceHolidayDO attendanceHoliday = BeanUtils.toBean(createReqVO, HrmAttendanceHolidayDO.class); |
| | | attendanceHoliday.setDate(createReqVO.getStartDate()); |
| | | if (attendanceHoliday.getIsLegal() == null) { |
| | | attendanceHoliday.setIsLegal(0); |
| | | } |
| | |
| | | @Override |
| | | public void updateAttendanceHoliday(HrmAttendanceHolidaySaveReqVO updateReqVO) { |
| | | validateAttendanceHolidayExists(updateReqVO.getId()); |
| | | validateDateRange(updateReqVO); |
| | | validateDateRangeNoOverlap(updateReqVO.getStartDate(), updateReqVO.getEndDate(), updateReqVO.getId()); |
| | | // å¦ææ¥æåæ´ï¼æ ¡éªæ°æ¥ææ¯å¦å·²åå¨é
ç½® |
| | | HrmAttendanceHolidayDO oldHoliday = attendanceHolidayMapper.selectById(updateReqVO.getId()); |
| | | if (!oldHoliday.getDate().equals(updateReqVO.getDate())) { |
| | | validateAttendanceHolidayDateExists(updateReqVO.getDate()); |
| | | } |
| | | HrmAttendanceHolidayDO updateObj = BeanUtils.toBean(updateReqVO, HrmAttendanceHolidayDO.class); |
| | | updateObj.setDate(updateReqVO.getStartDate()); |
| | | attendanceHolidayMapper.updateById(updateObj); |
| | | } |
| | | |
| | |
| | | |
| | | private void validateAttendanceHolidayExists(Long id) { |
| | | if (attendanceHolidayMapper.selectById(id) == null) { |
| | | throw exception(ATTENDANCE_HOLIDAY_NOT_EXISTS, id); |
| | | throw exception(ATTENDANCE_HOLIDAY_NOT_EXISTS); |
| | | } |
| | | } |
| | | |
| | | private void validateDateRange(HrmAttendanceHolidaySaveReqVO reqVO) { |
| | | if (reqVO.getEndDate().isBefore(reqVO.getStartDate())) { |
| | | throw exception(ATTENDANCE_HOLIDAY_DATE_RANGE_INVALID, reqVO.getEndDate(), reqVO.getStartDate()); |
| | | } |
| | | } |
| | | |
| | | private void validateDateRangeNoOverlap(LocalDate startDate, LocalDate endDate, Long excludeId) { |
| | | for (HrmAttendanceHolidayDO exist : attendanceHolidayMapper.selectListByDateRange(startDate, endDate)) { |
| | | if (excludeId != null && excludeId.equals(exist.getId())) { |
| | | continue; |
| | | } |
| | | throw exception(ATTENDANCE_HOLIDAY_RANGE_OVERLAP, startDate, endDate, |
| | | exist.getName(), exist.getStartDate(), exist.getEndDate()); |
| | | private void validateAttendanceHolidayDateExists(LocalDate date) { |
| | | if (attendanceHolidayMapper.selectByDate(date) != null) { |
| | | throw exception(ATTENDANCE_HOLIDAY_DATE_EXISTS); |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | @Override |
| | | public List<HrmAttendanceHolidayDO> getAttendanceHolidayListByDateRange(LocalDate startDate, LocalDate endDate) { |
| | | return attendanceHolidayMapper.selectListByDateRange(startDate, endDate); |
| | | return attendanceHolidayMapper.selectListByDateBetween(startDate, endDate); |
| | | } |
| | | |
| | | @Override |
| | | public boolean isWorkDay(LocalDate date) { |
| | | // 1. æ¥è¯¢è¦çè¯¥æ¥æçè忥é
ç½®ï¼é
ç½®æ¶é´æ®µä¸å
许éå ï¼è³å¤å½ä¸ä¸æ¡ï¼ |
| | | List<HrmAttendanceHolidayDO> holidays = attendanceHolidayMapper.selectListByDateRange(date, date); |
| | | if (!holidays.isEmpty()) { |
| | | // 1. æ¥è¯¢è忥é
ç½® |
| | | HrmAttendanceHolidayDO holiday = attendanceHolidayMapper.selectByDate(date); |
| | | if (holiday != null) { |
| | | // æé
ç½®æ¶ï¼æé
ç½®å¤æ |
| | | return HrmAttendanceHolidayTypeEnum.WORK_DAY.getType().equals(holidays.get(0).getType()); |
| | | return HrmAttendanceHolidayTypeEnum.WORK_DAY.getType().equals(holiday.getType()); |
| | | } |
| | | // 2. æ é
ç½®æ¶ï¼æå¨æ«å¤æ |
| | | DayOfWeek dayOfWeek = date.getDayOfWeek(); |
| | |
| | | package cn.iocoder.yudao.module.hrm.service.attendance; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceMyStatusRespVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceRecordPageReqVO; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmAttendanceRecordDO; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmAttendanceExceptionDO; |
| | |
| | | */ |
| | | HrmAttendanceRecordDO getAttendanceRecordByUserIdAndDate(Long userId, LocalDate date); |
| | | |
| | | /** |
| | | * è·å¾å½åç»å½äººçèå¤ç¶æï¼èå¤è§å + æå®æ¥æçæå¡ç¶æ |
| | | * |
| | | * @param loginUserId ç»å½ç¨æ·IDï¼ç³»ç»ç¨æ·IDï¼ä¸æ¯åå·¥IDï¼ |
| | | * @param date è夿¥æï¼ä¸ºç©ºæ¶åæå¡å¨å½å¤© |
| | | * @return æçèå¤ç¶æ |
| | | */ |
| | | HrmAttendanceMyStatusRespVO getMyStatus(Long loginUserId, LocalDate date); |
| | | |
| | | /** å建导å
¥çèå¤è®°å½ */ |
| | | void createImportedRecord(HrmAttendanceRecordDO record); |
| | | |
| | |
| | | */ |
| | | void supplementClockIn(HrmAttendanceExceptionDO exception); |
| | | |
| | | /** |
| | | * è·å¾å½åç»å½äººçèå¤ç¶æï¼App æå¡é¡µä½¿ç¨ï¼ |
| | | * |
| | | * @param loginUserId ç»å½ç¨æ·IDï¼ç³»ç»ç¨æ·ï¼éåå·¥IDï¼ |
| | | * @param date è夿¥æï¼ä¸ºç©ºæ¶åæå¡å¨å½å¤© |
| | | * @return æçèå¤ç¶æ |
| | | */ |
| | | cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceMyStatusRespVO |
| | | getMyStatus(Long loginUserId, LocalDate date); |
| | | |
| | | } |
| | |
| | | package cn.iocoder.yudao.module.hrm.service.attendance; |
| | | |
| | | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceMyStatusRespVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceRecordPageReqVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceMyStatusRespVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceRecordRespVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceRuleRespVO; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmAttendanceHolidayDO; |
| | | import cn.iocoder.yudao.module.hrm.enums.HrmAttendanceHolidayTypeEnum; |
| | | import cn.iocoder.yudao.module.system.api.dept.DeptApi; |
| | | import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmAttendanceRecordDO; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmAttendanceRuleDO; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmAttendanceExceptionDO; |
| | |
| | | import cn.iocoder.yudao.module.hrm.enums.HrmAttendanceClockTypeEnum; |
| | | import cn.iocoder.yudao.module.hrm.enums.HrmAttendanceDataSourceEnum; |
| | | import cn.iocoder.yudao.module.hrm.enums.HrmAttendanceExceptionTypeEnum; |
| | | import cn.iocoder.yudao.module.hrm.enums.HrmAttendanceHolidayTypeEnum; |
| | | import cn.iocoder.yudao.module.hrm.service.employee.HrmEmployeeService; |
| | | import cn.iocoder.yudao.module.system.api.dept.DeptApi; |
| | | import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | |
| | | } |
| | | |
| | | @Override |
| | | public HrmAttendanceMyStatusRespVO getMyStatus(Long loginUserId, LocalDate date) { |
| | | LocalDate queryDate = date != null ? date : LocalDate.now(); |
| | | // 1. ç»å½ç¨æ· -> åå·¥æ¡£æ¡ |
| | | HrmEmployeeDO employee = employeeService.getEmployeeByUserId(loginUserId); |
| | | if (employee == null) { |
| | | throw exception(EMPLOYEE_NOT_EXISTS, |
| | | "å½åç»å½è´¦å·æªå
³èå工档æ¡ï¼æ æ³æå¡ã请è系管çåå¨ãå工管çãä¸å°è¯¥ç»å½è´¦å·ç»å®å°åå·¥ã"); |
| | | } |
| | | HrmAttendanceMyStatusRespVO respVO = new HrmAttendanceMyStatusRespVO(); |
| | | respVO.setUserId(loginUserId); |
| | | respVO.setEmployeeId(employee.getId()); |
| | | respVO.setEmployeeName(employee.getName()); |
| | | respVO.setDeptId(employee.getDeptId()); |
| | | if (employee.getDeptId() != null) { |
| | | DeptRespDTO dept = deptApi.getDept(employee.getDeptId()); |
| | | if (dept != null) { |
| | | respVO.setDeptName(dept.getName()); |
| | | } |
| | | } |
| | | respVO.setDate(queryDate); |
| | | respVO.setServerTime(LocalDateTime.now()); |
| | | |
| | | // 2. æ¯å¦å·¥ä½æ¥ + 伿¯åå |
| | | boolean workDay = attendanceHolidayService.isWorkDay(queryDate); |
| | | respVO.setWorkDay(workDay); |
| | | if (!workDay) { |
| | | respVO.setRestDayReason(getRestDayReason(queryDate)); |
| | | } |
| | | |
| | | // 3. å½åçæè§åï¼é¨é¨è§åä¼å
ï¼å
¶æ¬¡å
¨å±è§åï¼ |
| | | HrmAttendanceRuleDO rule = attendanceRuleService.getAttendanceRuleByDeptId(employee.getDeptId()); |
| | | if (rule != null) { |
| | | HrmAttendanceRuleRespVO ruleRespVO = BeanUtils.toBean(rule, HrmAttendanceRuleRespVO.class); |
| | | if (rule.getDeptId() != null) { |
| | | DeptRespDTO ruleDept = deptApi.getDept(rule.getDeptId()); |
| | | if (ruleDept != null) { |
| | | ruleRespVO.setDeptName(ruleDept.getName()); |
| | | } |
| | | } |
| | | respVO.setRule(ruleRespVO); |
| | | } |
| | | |
| | | // 4. 彿¥èå¤è®°å½ |
| | | HrmAttendanceRecordDO record = attendanceRecordMapper.selectByUserIdAndDate(employee.getId(), queryDate); |
| | | if (record != null) { |
| | | respVO.setRecord(buildRecordRespVO(record, employee)); |
| | | } |
| | | |
| | | // 5. å½å坿å¡ç¶æ |
| | | respVO.setClockable(buildClockable(workDay, rule, record, queryDate)); |
| | | return respVO; |
| | | } |
| | | |
| | | /** |
| | | * 伿¯æ¥åå ï¼ä¼å
åè忥é
ç½®åç§°ï¼æªé
ç½®æ¶æææç»åºâå¨å
/卿¥â |
| | | */ |
| | | private String getRestDayReason(LocalDate date) { |
| | | List<HrmAttendanceHolidayDO> holidays = attendanceHolidayService.getAttendanceHolidayListByDateRange(date, date); |
| | | for (HrmAttendanceHolidayDO holiday : holidays) { |
| | | if (HrmAttendanceHolidayTypeEnum.REST_DAY.getType().equals(holiday.getType())) { |
| | | return holiday.getName(); |
| | | } |
| | | } |
| | | DayOfWeek dayOfWeek = date.getDayOfWeek(); |
| | | if (dayOfWeek == DayOfWeek.SATURDAY) { |
| | | return "å¨å
"; |
| | | } |
| | | if (dayOfWeek == DayOfWeek.SUNDAY) { |
| | | return "卿¥"; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * æå»ºæå¡è®°å½è¿å VOï¼è¡¥é½æå¡ç±»ååç§°ãåå·¥ä¸é¨é¨ä¿¡æ¯ |
| | | */ |
| | | private HrmAttendanceRecordRespVO buildRecordRespVO(HrmAttendanceRecordDO record, HrmEmployeeDO employee) { |
| | | HrmAttendanceRecordRespVO recordRespVO = BeanUtils.toBean(record, HrmAttendanceRecordRespVO.class); |
| | | recordRespVO.setClockInTypeName(HrmAttendanceClockTypeEnum.getNameByType(record.getClockInType())); |
| | | recordRespVO.setClockOutTypeName(HrmAttendanceClockTypeEnum.getNameByType(record.getClockOutType())); |
| | | if (employee != null) { |
| | | recordRespVO.setUserName(employee.getName()); |
| | | recordRespVO.setPhone(employee.getPhone()); |
| | | } |
| | | if (record.getDeptId() != null) { |
| | | DeptRespDTO dept = deptApi.getDept(record.getDeptId()); |
| | | if (dept != null) { |
| | | recordRespVO.setDeptName(dept.getName()); |
| | | } |
| | | } |
| | | return recordRespVO; |
| | | } |
| | | |
| | | /** |
| | | * æ ¹æ®å·¥ä½æ¥ãèå¤è§åä¸å½æ¥è®°å½è®¡ç®å¯æå¡ç¶æ |
| | | */ |
| | | private HrmAttendanceMyStatusRespVO.Clockable buildClockable(boolean workDay, HrmAttendanceRuleDO rule, |
| | | HrmAttendanceRecordDO record, LocalDate date) { |
| | | HrmAttendanceMyStatusRespVO.Clockable clockable = new HrmAttendanceMyStatusRespVO.Clockable(); |
| | | if (!workDay) { |
| | | clockable.setClockIn(false); |
| | | clockable.setClockOut(false); |
| | | clockable.setNextAction("none"); |
| | | clockable.setMessage("仿¥ä¼æ¯ï¼æ éæå¡"); |
| | | return clockable; |
| | | } |
| | | if (rule == null) { |
| | | clockable.setClockIn(false); |
| | | clockable.setClockOut(false); |
| | | clockable.setNextAction("none"); |
| | | clockable.setMessage("æªé
ç½®èå¤è§åï¼è¯·è系管çå"); |
| | | return clockable; |
| | | } |
| | | // åªæä¸çå¡ã没æä¸çå¡ï¼å½æ¥å·²ç»æï¼ä¸åå
è®¸ç»§ç»æå¡ |
| | | if (record != null && record.getClockInTime() == null && record.getClockOutTime() != null) { |
| | | clockable.setClockIn(false); |
| | | clockable.setClockOut(false); |
| | | clockable.setNextAction("none"); |
| | | clockable.setMessage("仿¥ç¼ºä¸çå¡ï¼å·²å®æä¸çæå¡"); |
| | | return clockable; |
| | | } |
| | | // ä»å¤©è¿æ²¡æä¸çå¡ |
| | | if (record == null || record.getClockInTime() == null) { |
| | | clockable.setClockIn(true); |
| | | clockable.setClockOut(false); |
| | | clockable.setNextAction("in"); |
| | | clockable.setMessage("å¯ä¸çæå¡"); |
| | | // ä»
å½å¤©æ¥è¯¢æ¶æç¤ºå·²è¿å°æ¶é¿ |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | if (rule.getWorkStartTime() != null && date.equals(now.toLocalDate())) { |
| | | LocalDateTime workStart = LocalDateTime.of(date, rule.getWorkStartTime()); |
| | | int lateAllowMinutes = rule.getLateAllowMinutes() != null ? rule.getLateAllowMinutes() : 0; |
| | | if (now.isAfter(workStart.plusMinutes(lateAllowMinutes))) { |
| | | long lateMinutes = Duration.between(workStart, now).toMinutes(); |
| | | clockable.setMessage("å·²è¶
è¿ä¸çæ¶é´ " + lateMinutes + " åé"); |
| | | } |
| | | } |
| | | return clockable; |
| | | } |
| | | // å·²æä¸çå¡ãæªæä¸çå¡ |
| | | if (record.getClockOutTime() == null) { |
| | | clockable.setClockIn(false); |
| | | clockable.setClockOut(true); |
| | | clockable.setNextAction("out"); |
| | | clockable.setMessage("å·²ä¸çæå¡ï¼å¯ä¸çæå¡"); |
| | | return clockable; |
| | | } |
| | | // ä¸ä¸çå¡é½å·²æ |
| | | clockable.setClockIn(false); |
| | | clockable.setClockOut(false); |
| | | clockable.setNextAction("none"); |
| | | clockable.setMessage("仿¥å·²å®ææå¡"); |
| | | return clockable; |
| | | } |
| | | |
| | | @Override |
| | | public void createImportedRecord(HrmAttendanceRecordDO record) { |
| | | // æèå¤è§åéç®æå¡ç±»å䏿¶é¿ï¼ä¸ä¿¡ä»» Excel å¡«åçå¼ |
| | | fillClockTypes(record); |
| | |
| | | if (record.getDeptId() == null) { |
| | | return; |
| | | } |
| | | // ä¸çæå¡ç±»åï¼æäº ä¸çæ¶é´+è¿å°å
许 记为è¿å°ï¼æä¸çæå¡æ¶é´å¹é
çæ¬¡ï¼ |
| | | if (record.getClockInTime() != null) { |
| | | HrmAttendanceRuleDO rule = attendanceRuleService.getAttendanceRuleByDeptId( |
| | | record.getDeptId(), record.getClockInTime()); |
| | | if (rule != null && rule.getWorkStartTime() != null) { |
| | | int lateAllowMinutes = rule.getLateAllowMinutes() != null ? rule.getLateAllowMinutes() : 0; |
| | | LocalTime lateThreshold = rule.getWorkStartTime().plusMinutes(lateAllowMinutes); |
| | | LocalTime clockInOnly = record.getClockInTime().toLocalTime(); |
| | | record.setClockInType(clockInOnly.isAfter(lateThreshold) |
| | | ? HrmAttendanceClockTypeEnum.LATE.getType() |
| | | : HrmAttendanceClockTypeEnum.NORMAL.getType()); |
| | | } |
| | | HrmAttendanceRuleDO rule = attendanceRuleService.getAttendanceRuleByDeptId(record.getDeptId(), record.getDate()); |
| | | if (rule == null) { |
| | | return; |
| | | } |
| | | // ä¸çæå¡ç±»åï¼æ©äº ä¸çæ¶é´-æ©éå
许 记为æ©éï¼æä¸çæå¡æ¶é´å¹é
çæ¬¡ï¼ |
| | | if (record.getClockOutTime() != null) { |
| | | HrmAttendanceRuleDO rule = attendanceRuleService.getAttendanceRuleByDeptId( |
| | | record.getDeptId(), record.getClockOutTime()); |
| | | if (rule != null && rule.getWorkEndTime() != null) { |
| | | int earlyAllowMinutes = rule.getEarlyAllowMinutes() != null ? rule.getEarlyAllowMinutes() : 0; |
| | | LocalTime earlyThreshold = rule.getWorkEndTime().minusMinutes(earlyAllowMinutes); |
| | | LocalTime clockOutOnly = record.getClockOutTime().toLocalTime(); |
| | | record.setClockOutType(clockOutOnly.isBefore(earlyThreshold) |
| | | ? HrmAttendanceClockTypeEnum.EARLY.getType() |
| | | : HrmAttendanceClockTypeEnum.NORMAL.getType()); |
| | | } |
| | | // ä¸çæå¡ç±»åï¼æäº ä¸çæ¶é´+è¿å°å
许 记为è¿å° |
| | | if (record.getClockInTime() != null && rule.getWorkStartTime() != null) { |
| | | int lateAllowMinutes = rule.getLateAllowMinutes() != null ? rule.getLateAllowMinutes() : 0; |
| | | LocalTime lateThreshold = rule.getWorkStartTime().plusMinutes(lateAllowMinutes); |
| | | LocalTime clockInOnly = record.getClockInTime().toLocalTime(); |
| | | record.setClockInType(clockInOnly.isAfter(lateThreshold) |
| | | ? HrmAttendanceClockTypeEnum.LATE.getType() |
| | | : HrmAttendanceClockTypeEnum.NORMAL.getType()); |
| | | } |
| | | // ä¸çæå¡ç±»åï¼æ©äº ä¸çæ¶é´-æ©éå
许 记为æ©é |
| | | if (record.getClockOutTime() != null && rule.getWorkEndTime() != null) { |
| | | int earlyAllowMinutes = rule.getEarlyAllowMinutes() != null ? rule.getEarlyAllowMinutes() : 0; |
| | | LocalTime earlyThreshold = rule.getWorkEndTime().minusMinutes(earlyAllowMinutes); |
| | | LocalTime clockOutOnly = record.getClockOutTime().toLocalTime(); |
| | | record.setClockOutType(clockOutOnly.isBefore(earlyThreshold) |
| | | ? HrmAttendanceClockTypeEnum.EARLY.getType() |
| | | : HrmAttendanceClockTypeEnum.NORMAL.getType()); |
| | | } |
| | | } |
| | | |
| | |
| | | .divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP); |
| | | record.setWorkHours(workHours); |
| | | } |
| | | // å çæ¶é¿ï¼æèå¤è§åçæ åä¸çæ¶é´è®¡ç®è¶
é¢é¨åï¼æä¸çæå¡æ¶é´å¹é
çæ¬¡ï¼ |
| | | // å çæ¶é¿ï¼æèå¤è§åçæ åä¸çæ¶é´è®¡ç®è¶
é¢é¨å |
| | | if (record.getClockOutTime() != null && record.getDeptId() != null) { |
| | | HrmAttendanceRuleDO rule = attendanceRuleService.getAttendanceRuleByDeptId( |
| | | record.getDeptId(), record.getClockOutTime()); |
| | | HrmAttendanceRuleDO rule = attendanceRuleService.getAttendanceRuleByDeptId(record.getDeptId(), record.getDate()); |
| | | if (rule != null && rule.getWorkEndTime() != null) { |
| | | LocalTime workEnd = rule.getWorkEndTime(); |
| | | LocalDate outDate = record.getClockOutTime().toLocalDate(); |
| | |
| | | if (employee == null) { |
| | | throw exception(EMPLOYEE_NOT_EXISTS); |
| | | } |
| | | // ææ¬æ¬¡æå¡æ¶é´å¹é
çæ¬¡ï¼åä¸ä¸ªé¨é¨å¯è½é
ç½®äºå¤æ¡è§åï¼ |
| | | HrmAttendanceRuleDO rule = attendanceRuleService.getAttendanceRuleByDeptId(employee.getDeptId(), clockTime); |
| | | HrmAttendanceRuleDO rule = attendanceRuleService.getAttendanceRuleByDeptId(employee.getDeptId(), clockDate); |
| | | if (rule == null) { |
| | | throw exception(ATTENDANCE_CLOCK_FAIL_NO_RULE); |
| | | } |
| | |
| | | if (employee == null) { |
| | | throw exception(EMPLOYEE_NOT_EXISTS); |
| | | } |
| | | // ææ¬æ¬¡æå¡æ¶é´å¹é
çæ¬¡ï¼åä¸ä¸ªé¨é¨å¯è½é
ç½®äºå¤æ¡è§åï¼ |
| | | HrmAttendanceRuleDO rule = attendanceRuleService.getAttendanceRuleByDeptId(employee.getDeptId(), clockTime); |
| | | HrmAttendanceRuleDO rule = attendanceRuleService.getAttendanceRuleByDeptId(employee.getDeptId(), clockDate); |
| | | if (rule == null) { |
| | | throw exception(ATTENDANCE_CLOCK_FAIL_NO_RULE); |
| | | } |
| | |
| | | attendanceRecordMapper.updateById(record); |
| | | } |
| | | |
| | | // ==================== æçèå¤ç¶æï¼App æå¡é¡µï¼ ==================== |
| | | |
| | | @Override |
| | | public HrmAttendanceMyStatusRespVO getMyStatus(Long loginUserId, LocalDate date) { |
| | | LocalDate queryDate = date != null ? date : LocalDate.now(); |
| | | // 1. ç»å½ç¨æ· -> åå·¥æ¡£æ¡ |
| | | HrmEmployeeDO employee = employeeService.getEmployeeByUserId(loginUserId); |
| | | if (employee == null) { |
| | | throw new ServiceException(EMPLOYEE_NOT_EXISTS.getCode(), |
| | | "å½åç»å½è´¦å·æªå
³èå工档æ¡ï¼æ æ³æå¡ã请è系管çåå¨ãå工管çãä¸å°è¯¥ç»å½è´¦å·ç»å®å°åå·¥ã"); |
| | | } |
| | | HrmAttendanceMyStatusRespVO respVO = new HrmAttendanceMyStatusRespVO(); |
| | | respVO.setUserId(loginUserId); |
| | | respVO.setEmployeeId(employee.getId()); |
| | | respVO.setEmployeeName(employee.getName()); |
| | | respVO.setDeptId(employee.getDeptId()); |
| | | if (employee.getDeptId() != null) { |
| | | DeptRespDTO dept = deptApi.getDept(employee.getDeptId()); |
| | | if (dept != null) { |
| | | respVO.setDeptName(dept.getName()); |
| | | } |
| | | } |
| | | respVO.setDate(queryDate); |
| | | respVO.setServerTime(LocalDateTime.now()); |
| | | |
| | | // 2. æ¯å¦å·¥ä½æ¥ + 伿¯åå |
| | | boolean workDay = attendanceHolidayService.isWorkDay(queryDate); |
| | | respVO.setWorkDay(workDay); |
| | | if (!workDay) { |
| | | respVO.setRestDayReason(getRestDayReason(queryDate)); |
| | | } |
| | | |
| | | // 3. å½åçæè§åï¼é¨é¨è§åä¼å
ï¼å
¶æ¬¡å
¨å±è§åï¼ææ¥æå¹é
çæåºé´ï¼ |
| | | HrmAttendanceRuleDO rule = attendanceRuleService.getAttendanceRuleByDeptId(employee.getDeptId(), queryDate); |
| | | if (rule != null) { |
| | | HrmAttendanceRuleRespVO ruleRespVO = BeanUtils.toBean(rule, HrmAttendanceRuleRespVO.class); |
| | | if (rule.getDeptId() != null) { |
| | | DeptRespDTO ruleDept = deptApi.getDept(rule.getDeptId()); |
| | | if (ruleDept != null) { |
| | | ruleRespVO.setDeptName(ruleDept.getName()); |
| | | } |
| | | } |
| | | respVO.setRule(ruleRespVO); |
| | | } |
| | | |
| | | // 4. 彿¥èå¤è®°å½ |
| | | HrmAttendanceRecordDO record = attendanceRecordMapper.selectByUserIdAndDate(employee.getId(), queryDate); |
| | | if (record != null) { |
| | | respVO.setRecord(buildRecordRespVO(record, employee)); |
| | | } |
| | | |
| | | // 5. å½å坿å¡ç¶æ |
| | | respVO.setClockable(buildClockable(workDay, rule, record, queryDate)); |
| | | return respVO; |
| | | } |
| | | |
| | | /** |
| | | * 伿¯æ¥åå ï¼ä¼å
åè忥é
ç½®åç§°ï¼æªé
ç½®æ¶æææç»åºâå¨å
/卿¥â |
| | | */ |
| | | private String getRestDayReason(LocalDate date) { |
| | | List<HrmAttendanceHolidayDO> holidays = attendanceHolidayService.getAttendanceHolidayListByDateRange(date, date); |
| | | for (HrmAttendanceHolidayDO holiday : holidays) { |
| | | if (HrmAttendanceHolidayTypeEnum.REST_DAY.getType().equals(holiday.getType())) { |
| | | return holiday.getName(); |
| | | } |
| | | } |
| | | DayOfWeek dayOfWeek = date.getDayOfWeek(); |
| | | if (dayOfWeek == DayOfWeek.SATURDAY) { |
| | | return "å¨å
"; |
| | | } |
| | | if (dayOfWeek == DayOfWeek.SUNDAY) { |
| | | return "卿¥"; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * æå»ºæå¡è®°å½è¿å VOï¼è¡¥é½æå¡ç±»ååç§°ä¸åå·¥ä¿¡æ¯ |
| | | */ |
| | | private HrmAttendanceRecordRespVO buildRecordRespVO(HrmAttendanceRecordDO record, HrmEmployeeDO employee) { |
| | | HrmAttendanceRecordRespVO recordRespVO = BeanUtils.toBean(record, HrmAttendanceRecordRespVO.class); |
| | | recordRespVO.setClockInTypeName(HrmAttendanceClockTypeEnum.getNameByType(record.getClockInType())); |
| | | recordRespVO.setClockOutTypeName(HrmAttendanceClockTypeEnum.getNameByType(record.getClockOutType())); |
| | | if (employee != null) { |
| | | recordRespVO.setUserName(employee.getName()); |
| | | } |
| | | return recordRespVO; |
| | | } |
| | | |
| | | /** |
| | | * 计ç®å½å坿å¡ç¶æ |
| | | */ |
| | | private HrmAttendanceMyStatusRespVO.Clockable buildClockable(boolean workDay, HrmAttendanceRuleDO rule, |
| | | HrmAttendanceRecordDO record, LocalDate date) { |
| | | HrmAttendanceMyStatusRespVO.Clockable clockable = new HrmAttendanceMyStatusRespVO.Clockable(); |
| | | if (!workDay) { |
| | | clockable.setClockIn(false); |
| | | clockable.setClockOut(false); |
| | | clockable.setNextAction("none"); |
| | | clockable.setMessage("仿¥ä¼æ¯ï¼æ éæå¡"); |
| | | return clockable; |
| | | } |
| | | if (rule == null) { |
| | | clockable.setClockIn(false); |
| | | clockable.setClockOut(false); |
| | | clockable.setNextAction("none"); |
| | | clockable.setMessage("æªé
ç½®èå¤è§åï¼è¯·è系管çå"); |
| | | return clockable; |
| | | } |
| | | // åªæä¸çå¡ã没æä¸çå¡ï¼å½æ¥å·²ç»æï¼ä¸åå
è®¸ç»§ç»æå¡ |
| | | if (record != null && record.getClockInTime() == null && record.getClockOutTime() != null) { |
| | | clockable.setClockIn(false); |
| | | clockable.setClockOut(false); |
| | | clockable.setNextAction("none"); |
| | | clockable.setMessage("仿¥ç¼ºä¸çå¡ï¼å·²å®æä¸çæå¡"); |
| | | return clockable; |
| | | } |
| | | // ä»å¤©è¿æ²¡æä¸çå¡ |
| | | if (record == null || record.getClockInTime() == null) { |
| | | clockable.setClockIn(true); |
| | | clockable.setClockOut(false); |
| | | clockable.setNextAction("in"); |
| | | clockable.setMessage("å¯ä¸çæå¡"); |
| | | // ä»
å½å¤©æ¥è¯¢æ¶æç¤ºå·²è¿å°æ¶é¿ |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | if (rule.getWorkStartTime() != null && date.equals(now.toLocalDate())) { |
| | | LocalDateTime workStart = LocalDateTime.of(date, rule.getWorkStartTime()); |
| | | int lateAllowMinutes = rule.getLateAllowMinutes() != null ? rule.getLateAllowMinutes() : 0; |
| | | if (now.isAfter(workStart.plusMinutes(lateAllowMinutes))) { |
| | | long lateMinutes = Duration.between(workStart, now).toMinutes(); |
| | | clockable.setMessage("å·²è¶
è¿ä¸çæ¶é´ " + lateMinutes + " åé"); |
| | | } |
| | | } |
| | | return clockable; |
| | | } |
| | | // å·²æä¸çå¡ãæªæä¸çå¡ |
| | | if (record.getClockOutTime() == null) { |
| | | clockable.setClockIn(false); |
| | | clockable.setClockOut(true); |
| | | clockable.setNextAction("out"); |
| | | clockable.setMessage("å·²ä¸çæå¡ï¼å¯ä¸çæå¡"); |
| | | return clockable; |
| | | } |
| | | // ä¸ä¸çå¡é½å·²æ |
| | | clockable.setClockIn(false); |
| | | clockable.setClockOut(false); |
| | | clockable.setNextAction("none"); |
| | | clockable.setMessage("仿¥å·²å®ææå¡"); |
| | | return clockable; |
| | | } |
| | | |
| | | } |
| | |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmAttendanceRuleDO; |
| | | import jakarta.validation.Valid; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | |
| | | /** |
| | |
| | | List<HrmAttendanceRuleDO> getAttendanceRuleList(); |
| | | |
| | | /** |
| | | * æ ¹æ®é¨é¨IDè·åèå¤è§å |
| | | * ä¼å
å¹é
é¨é¨è§åï¼å
¶æ¬¡æ¥æ¾å
¨å±è§åï¼deptId=nullï¼ |
| | | * æ ¹æ®é¨é¨ID + è夿¥æè·åçæçèå¤è§å |
| | | * ä¼å
å¹é
é¨é¨è§åï¼å
¶æ¬¡æ¥æ¾å
¨å±è§åï¼deptId=nullï¼ï¼ |
| | | * åé¨é¨åå¨å¤æ¡è§åæ¶ï¼åãçæå¼å§æ¥æãææä¸è¦çè¯¥æ¥æç飿¡ã |
| | | * |
| | | * @param deptId é¨é¨ID |
| | | * @param date è夿¥æï¼ä¸ºç©ºæ¶æå½å¤©ï¼ |
| | | * @return èå¤è§å |
| | | */ |
| | | HrmAttendanceRuleDO getAttendanceRuleByDeptId(Long deptId); |
| | | |
| | | /** |
| | | * æ ¹æ®é¨é¨ID + æå¡æ¶é´è·åèå¤è§å |
| | | * |
| | | * åä¸ä¸ªé¨é¨å
许é
ç½®å¤æ¡èå¤è§åï¼å¤ç次ï¼ï¼æ¤æ¶ææå¡æ¶é´å¹é
çæ¬¡ï¼ |
| | | * ä¼å
åçæ¬¡æ¶é´çªå£å
å«è¯¥æå¡æ¶é´çè§åï¼é½ä¸å
嫿¶åæ¶é´çªå£ææ¥è¿ç䏿¡ã |
| | | * |
| | | * @param deptId é¨é¨ID |
| | | * @param clockTime æå¡æ¶é´ï¼ä¸ºç©ºæ¶å该é¨é¨ä¸çæ¶é´ææ©ç䏿¡è§å |
| | | * @return èå¤è§å |
| | | */ |
| | | HrmAttendanceRuleDO getAttendanceRuleByDeptId(Long deptId, LocalDateTime clockTime); |
| | | HrmAttendanceRuleDO getAttendanceRuleByDeptId(Long deptId, java.time.LocalDate date); |
| | | |
| | | } |
| | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import com.google.common.annotations.VisibleForTesting; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceRulePageReqVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceRuleSaveReqVO; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmAttendanceRuleDO; |
| | | import cn.iocoder.yudao.module.hrm.dal.mysql.attendance.HrmAttendanceRuleMapper; |
| | | import cn.iocoder.yudao.module.system.api.dept.DeptApi; |
| | | import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.Duration; |
| | | import java.time.LocalDateTime; |
| | | import java.time.LocalTime; |
| | | import java.util.Comparator; |
| | | import java.util.HashSet; |
| | | import java.time.LocalDate; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.*; |
| | |
| | | /** |
| | | * èå¤è§åé
ç½® Service å®ç°ç±» |
| | | * |
| | | * 说æï¼åä¸é¨é¨å
许é
ç½®å¤æ¡è§åï¼æãçæå¼å§æ¥æ / çæç»ææ¥æãåºé´åºåï¼ |
| | | * å¹é
æ¶åè¦çç®æ æ¥æãä¸çæå¼å§æ¥æææç飿¡ï¼æ æ¥æçè§ä¸ºé¿æææï¼ä¼å
级æä½ï¼ã |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Service |
| | | @Validated |
| | | public class HrmAttendanceRuleServiceImpl implements HrmAttendanceRuleService { |
| | | |
| | | /** è§åç¶æï¼0-å¯ç¨ */ |
| | | private static final Integer ENABLED_STATUS = 0; |
| | | |
| | | @Resource |
| | | private HrmAttendanceRuleMapper attendanceRuleMapper; |
| | | @Resource |
| | | private DeptApi deptApi; |
| | | |
| | | @Override |
| | | public Long createAttendanceRule(HrmAttendanceRuleSaveReqVO createReqVO) { |
| | | // æ ¡éªè§ååç§°æ¯å¦éå¤ |
| | | // æ ¡éªè§ååç§°æ¯å¦éå¤ï¼åä¸é¨é¨å
è®¸å¤æ¡è§åï¼ä¸åéå¶é¨é¨å¯ä¸ï¼ |
| | | validateAttendanceRuleNameUnique(null, createReqVO.getName()); |
| | | // 注ï¼åä¸ä¸ªé¨é¨å
许é
ç½®å¤æ¡è§åï¼å¤ç次ï¼ï¼è¿éä¸åæ ¡éªé¨é¨å¯ä¸ |
| | | validateEffectiveDateRange(createReqVO.getEffectiveStartDate(), createReqVO.getEffectiveEndDate()); |
| | | HrmAttendanceRuleDO attendanceRule = BeanUtils.toBean(createReqVO, HrmAttendanceRuleDO.class); |
| | | // 设置é»è®¤å¼ |
| | | if (attendanceRule.getLateAllowMinutes() == null) { |
| | |
| | | validateAttendanceRuleExists(updateReqVO.getId()); |
| | | // æ ¡éªè§ååç§°æ¯å¦éå¤ |
| | | validateAttendanceRuleNameUnique(updateReqVO.getId(), updateReqVO.getName()); |
| | | // 注ï¼åä¸ä¸ªé¨é¨å
许é
ç½®å¤æ¡è§åï¼å¤ç次ï¼ï¼è¿éä¸åæ ¡éªé¨é¨å¯ä¸ |
| | | validateEffectiveDateRange(updateReqVO.getEffectiveStartDate(), updateReqVO.getEffectiveEndDate()); |
| | | HrmAttendanceRuleDO updateObj = BeanUtils.toBean(updateReqVO, HrmAttendanceRuleDO.class); |
| | | attendanceRuleMapper.updateById(updateObj); |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * æ ¡éªçææ¥æåºé´ï¼å¼å§æ¥æä¸è½æäºç»ææ¥æ |
| | | */ |
| | | private void validateEffectiveDateRange(LocalDate startDate, LocalDate endDate) { |
| | | if (startDate != null && endDate != null && startDate.isAfter(endDate)) { |
| | | throw exception(ATTENDANCE_RULE_EFFECTIVE_DATE_INVALID); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public HrmAttendanceRuleDO getAttendanceRule(Long id) { |
| | | return attendanceRuleMapper.selectById(id); |
| | |
| | | } |
| | | |
| | | @Override |
| | | public HrmAttendanceRuleDO getAttendanceRuleByDeptId(Long deptId) { |
| | | return getAttendanceRuleByDeptId(deptId, null); |
| | | } |
| | | |
| | | @Override |
| | | public HrmAttendanceRuleDO getAttendanceRuleByDeptId(Long deptId, LocalDateTime clockTime) { |
| | | // 1. ä»åå·¥æå¨é¨é¨å¼å§é级å䏿¥æ¾ï¼é
ç½®å¨ä¸çº§é¨é¨çè§å对å
¶åé¨é¨çæ |
| | | Set<Long> visitedDeptIds = new HashSet<>(); |
| | | Long currentDeptId = deptId; |
| | | while (currentDeptId != null && currentDeptId != 0 && visitedDeptIds.add(currentDeptId)) { |
| | | List<HrmAttendanceRuleDO> deptRules = attendanceRuleMapper.selectEnabledListByDeptId(currentDeptId); |
| | | public HrmAttendanceRuleDO getAttendanceRuleByDeptId(Long deptId, LocalDate date) { |
| | | LocalDate targetDate = date != null ? date : LocalDate.now(); |
| | | // 1. å
æ¥æ¾é¨é¨ä¸å±è§åï¼è¦çç®æ æ¥æçï¼çæå¼å§æ¥æææçä¼å
ï¼ |
| | | if (deptId != null) { |
| | | List<HrmAttendanceRuleDO> deptRules = selectEffectiveRules(deptId, targetDate); |
| | | if (!deptRules.isEmpty()) { |
| | | // åä¸é¨é¨å¯é
ç½®å¤æ¡è§åï¼å¤ç次ï¼ï¼ææå¡æ¶é´å¹é
çæ¬¡ |
| | | return matchRuleByClockTime(deptRules, clockTime); |
| | | return deptRules.get(0); |
| | | } |
| | | DeptRespDTO dept = deptApi.getDept(currentDeptId); |
| | | if (dept == null) { |
| | | break; |
| | | } |
| | | currentDeptId = dept.getParentId(); |
| | | } |
| | | // 2. æ¥æ¾å
¨å±è§åï¼deptId 为 nullï¼ |
| | | List<HrmAttendanceRuleDO> globalRules = attendanceRuleMapper.selectList(new LambdaQueryWrapperX<HrmAttendanceRuleDO>() |
| | | .isNull(HrmAttendanceRuleDO::getDeptId) |
| | | .eq(HrmAttendanceRuleDO::getStatus, ENABLED_STATUS) |
| | | .orderByDesc(HrmAttendanceRuleDO::getId)); |
| | | List<HrmAttendanceRuleDO> globalRules = selectEffectiveRules(null, targetDate); |
| | | if (!globalRules.isEmpty()) { |
| | | return matchRuleByClockTime(globalRules, clockTime); |
| | | return globalRules.get(0); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * 夿¡è§åï¼åä¸ä¸ªé¨é¨å¤çæ¬¡ï¼æ¶ï¼ææå¡æ¶é´éçæ¬¡ï¼ |
| | | * 1. æå¡æ¶é´è½å¨æä¸ªç次ç [ä¸çæ¶é´, ä¸çæ¶é´] å
ï¼å°±åè¿ä¸æ¡ |
| | | * 2. é½ä¸å¨çªå£å
æ¶ï¼åçæ¬¡æ¶é´çªå£ç¦»æå¡æ¶é´æè¿ç䏿¡ |
| | | * 3. ä»ç¶å¹¶åæ¶ï¼æä¸çæ¶é´ãç¼å·æåºï¼ä¿è¯ç»æç¨³å®ï¼ä¸ä¼éæ°æ®åºè¿åé¡ºåºæ¼ç§»ï¼ |
| | | * æå¡æ¶é´ä¸ºç©ºï¼ä¸å
³å¿çæ¬¡ï¼æ¶ï¼åä¸çæ¶é´ææ©ç䏿¡ã |
| | | * æ¥è¯¢å¨æå®æ¥æçæçè§å |
| | | * |
| | | * @param deptId é¨é¨IDï¼null 表示å
¨å±è§å |
| | | * @param date ç®æ æ¥æ |
| | | */ |
| | | @VisibleForTesting |
| | | static HrmAttendanceRuleDO matchRuleByClockTime(List<HrmAttendanceRuleDO> rules, LocalDateTime clockTime) { |
| | | if (rules.size() == 1) { |
| | | return rules.get(0); |
| | | private List<HrmAttendanceRuleDO> selectEffectiveRules(Long deptId, LocalDate date) { |
| | | LambdaQueryWrapperX<HrmAttendanceRuleDO> wrapper = new LambdaQueryWrapperX<>(); |
| | | if (deptId == null) { |
| | | wrapper.isNull(HrmAttendanceRuleDO::getDeptId); |
| | | } else { |
| | | wrapper.eq(HrmAttendanceRuleDO::getDeptId, deptId); |
| | | } |
| | | if (clockTime == null) { |
| | | return rules.stream().min(Comparator |
| | | .comparing(HrmAttendanceRuleDO::getWorkStartTime, |
| | | Comparator.nullsLast(Comparator.<LocalTime>naturalOrder())) |
| | | .thenComparing(HrmAttendanceRuleDO::getId)) |
| | | .orElse(rules.get(0)); |
| | | } |
| | | LocalTime time = clockTime.toLocalTime(); |
| | | return rules.stream().min(Comparator |
| | | .comparingLong((HrmAttendanceRuleDO rule) -> minutesToRuleWindow(rule, time)) |
| | | .thenComparing(HrmAttendanceRuleDO::getWorkStartTime, |
| | | Comparator.nullsLast(Comparator.<LocalTime>naturalOrder())) |
| | | .thenComparing(HrmAttendanceRuleDO::getId)) |
| | | .orElse(rules.get(0)); |
| | | wrapper.eq(HrmAttendanceRuleDO::getStatus, 0); |
| | | // çæå¼å§æ¥æä¸ºç©ºï¼ä¸éï¼æ <= ç®æ æ¥æ |
| | | wrapper.and(w -> w.isNull(HrmAttendanceRuleDO::getEffectiveStartDate) |
| | | .or().le(HrmAttendanceRuleDO::getEffectiveStartDate, date)); |
| | | // çæç»ææ¥æä¸ºç©ºï¼ä¸éï¼æ >= ç®æ æ¥æ |
| | | wrapper.and(w -> w.isNull(HrmAttendanceRuleDO::getEffectiveEndDate) |
| | | .or().ge(HrmAttendanceRuleDO::getEffectiveEndDate, date)); |
| | | // çæå¼å§æ¥æè¶æä¼å
级è¶é«ï¼åå¼å§æ¥ææ¶åå
å建ç |
| | | wrapper.orderByDesc(HrmAttendanceRuleDO::getEffectiveStartDate); |
| | | wrapper.orderByAsc(HrmAttendanceRuleDO::getId); |
| | | return attendanceRuleMapper.selectList(wrapper); |
| | | } |
| | | |
| | | /** |
| | | * æå¡æ¶é´å°è§åçæ¬¡çªå£çè·ç¦»ï¼åéï¼ï¼è½å¨çªå£å
为 0ï¼ç次æ¶é´æªé
ç½®æ¶è®°ä¸ºæå¤§ã |
| | | * æ¯æè·¨å¤©çæ¬¡ï¼å¦ 22:00-06:00ï¼åªè¦ end <= start å°±æè·¨å¤©å¤çï¼ã |
| | | */ |
| | | private static long minutesToRuleWindow(HrmAttendanceRuleDO rule, LocalTime time) { |
| | | LocalTime start = rule.getWorkStartTime(); |
| | | LocalTime end = rule.getWorkEndTime(); |
| | | if (start == null || end == null) { |
| | | return Long.MAX_VALUE; |
| | | } |
| | | boolean crossDay = !end.isAfter(start); |
| | | boolean inWindow = crossDay |
| | | ? !time.isBefore(start) || !time.isAfter(end) |
| | | : !time.isBefore(start) && !time.isAfter(end); |
| | | if (inWindow) { |
| | | return 0; |
| | | } |
| | | return Math.min(minutesBetween(time, start), minutesBetween(time, end)); |
| | | } |
| | | |
| | | /** 两个æ¶å»çåéå·®ï¼æä¸å¤© 24 å°æ¶åæè¿çç¯åè·ç¦»ï¼23:50 ä¸ 00:10 åªå·® 20 åéï¼ */ |
| | | private static long minutesBetween(LocalTime one, LocalTime other) { |
| | | long minutes = Math.abs(Duration.between(one, other).toMinutes()); |
| | | return Math.min(minutes, 24 * 60 - minutes); |
| | | } |
| | | |
| | | } |
| | | } |
| | |
| | | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.framework.operatelog.core.util.OperateLogUtils; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeContractPageReqVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeContractRespVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeContractSaveReqVO; |
| | |
| | | import cn.iocoder.yudao.module.system.api.dept.DeptApi; |
| | | import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; |
| | | import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi; |
| | | import com.mzt.logapi.starter.annotation.LogRecord; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.context.annotation.Lazy; |
| | | import org.springframework.stereotype.Service; |
| | |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.*; |
| | | import static cn.iocoder.yudao.module.hrm.enums.LogRecordConstants.*; |
| | | |
| | | /** |
| | | * åå·¥åå Service å®ç°ç±» |
| | |
| | | } |
| | | |
| | | @Override |
| | | @LogRecord(type = HRM_EMPLOYEE_CONTRACT_TYPE, subType = HRM_EMPLOYEE_CONTRACT_IMPORT_SUB_TYPE, bizNo = "0", |
| | | success = HRM_EMPLOYEE_CONTRACT_IMPORT_SUCCESS) |
| | | public HrmEmployeeContractImportRespVO importContractList(List<HrmEmployeeContractImportExcelVO> importContracts, |
| | | HrmEmployeeContractImportReqVO importReqVO) { |
| | | // 1. è¿æ»¤åå·¥å§å为空çè¡ |
| | |
| | | respVO.getFailureNames().put(name, ex.getMessage()); |
| | | } |
| | | } |
| | | // è®°å½å¯¼å
¥ç»ææ±æ»ï¼ä¾ @LogRecord ç success ææ¡å¼ç¨ |
| | | OperateLogUtils.putImportSummary(respVO.getCreateNames(), respVO.getUpdateNames(), |
| | | respVO.getFailureNames()); |
| | | return respVO; |
| | | } |
| | | |
| | |
| | | HrmEmployeeImportRespVO importEmployeeList(List<HrmEmployeeImportExcelVO> importEmployees, |
| | | HrmEmployeeImportReqVO importReqVO); |
| | | |
| | | /** |
| | | * å工转æ£ï¼ä»
ãå³å¨ååãå¯è½¬æ£ï¼å³å¡ååä¸å¯ï¼ï¼ç¶æ è¯ç¨ -> å¨èï¼è®°å½è½¬æ£æ¥æ |
| | | * |
| | | * @param id åå·¥ID |
| | | */ |
| | | void regularizeEmployee(Long id); |
| | | |
| | | /** |
| | | * è·å¾å¾
è½¬æ£æéå表ï¼è¯ç¨ä¸ãä¸å½å为å³å¨ååãè¯ç¨æå¨ days 天å
å°æçåå·¥ |
| | | * |
| | | * @param days æåå¤©æ° |
| | | * @return åå·¥å表 |
| | | */ |
| | | List<HrmEmployeeDO> getRegularizeReminderList(Integer days); |
| | | |
| | | } |
| | |
| | | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.framework.operatelog.core.util.OperateLogUtils; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeEducationSaveReqVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeEmergencyContactSaveReqVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeImportExcelVO; |
| | |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeSaveReqVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeSimpleRespVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeWorkHistorySaveReqVO; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeContractDO; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeDO; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeEducationDO; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeEmergencyContactDO; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeWorkHistoryDO; |
| | | import cn.iocoder.yudao.module.hrm.dal.mysql.employee.HrmEmployeeContractMapper; |
| | | import cn.iocoder.yudao.module.hrm.dal.mysql.employee.HrmEmployeeEducationMapper; |
| | | import cn.iocoder.yudao.module.hrm.dal.mysql.employee.HrmEmployeeEmergencyContactMapper; |
| | | import cn.iocoder.yudao.module.hrm.dal.mysql.employee.HrmEmployeeMapper; |
| | |
| | | import cn.iocoder.yudao.module.system.api.dept.dto.PostRespDTO; |
| | | import cn.iocoder.yudao.module.system.api.permission.RoleApi; |
| | | import cn.iocoder.yudao.module.system.api.permission.dto.RoleRespDTO; |
| | | import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi; |
| | | import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO; |
| | | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| | | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserCreateReqDTO; |
| | | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserUpdateReqDTO; |
| | | import com.mzt.logapi.starter.annotation.LogRecord; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.context.annotation.Lazy; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.util.ArrayList; |
| | | import java.util.Collection; |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.LinkedHashSet; |
| | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; |
| | | import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.*; |
| | | import static cn.iocoder.yudao.module.hrm.enums.LogRecordConstants.*; |
| | | |
| | | /** |
| | | * åå·¥ä¿¡æ¯ Service å®ç°ç±» |
| | |
| | | |
| | | @Resource |
| | | private HrmEmployeeMapper employeeMapper; |
| | | @Resource |
| | | private HrmEmployeeContractMapper contractMapper; |
| | | @Resource |
| | | private HrmEmployeeEducationMapper educationMapper; |
| | | @Resource |
| | |
| | | private AdminUserApi adminUserApi; |
| | | @Resource |
| | | private RoleApi roleApi; |
| | | @Resource |
| | | private StorageAttachmentApi storageAttachmentApi; |
| | | @Resource |
| | | @Lazy // èªæ³¨å
¥éå»¶è¿å è½½ï¼å¦å bean 被代çå
è£
åæ³¨å
¥åå§çæ¬å¯¼è´å¯å¨å¤±è´¥ |
| | | private HrmEmployeeService self; |
| | |
| | | } |
| | | employeeMapper.insert(employee); |
| | | Long employeeId = employee.getId(); |
| | | // ç»å®éä»¶ |
| | | storageAttachmentApi.bindAttachments("file", "hrm_employee", employeeId, createReqVO.getBlobIds()); |
| | | // 7. åæ¥æ·»å æè²ç»å |
| | | if (CollUtil.isNotEmpty(createReqVO.getEducations())) { |
| | | createReqVO.getEducations().forEach(educationVO -> { |
| | |
| | | syncWorkHistoryList(employeeId, updateReqVO.getWorkHistories()); |
| | | // åæ¥ç´§æ¥è系人 |
| | | syncEmergencyContactList(employeeId, updateReqVO.getEmergencyContacts()); |
| | | // æ´æ°éä»¶ |
| | | storageAttachmentApi.updateAttachments("file", "hrm_employee", employeeId, updateReqVO.getBlobIds()); |
| | | } |
| | | |
| | | private void syncEducationList(Long employeeId, List<HrmEmployeeEducationSaveReqVO> newList) { |
| | |
| | | educationMapper.deleteByEmployeeId(id); |
| | | workHistoryMapper.deleteByEmployeeId(id); |
| | | emergencyContactMapper.deleteByEmployeeId(id); |
| | | // å é¤éä»¶ |
| | | storageAttachmentApi.deleteAttachmentsByRecord("hrm_employee", id); |
| | | // å é¤åå·¥ |
| | | employeeMapper.deleteById(id); |
| | | } |
| | |
| | | respVO.setRoleNames(roles.stream().map(RoleRespDTO::getName).collect(Collectors.joining(","))); |
| | | } |
| | | } |
| | | // å¡«å
éä»¶ |
| | | respVO.setAttachmentList(storageAttachmentApi.listAttachments("hrm_employee", id)); |
| | | return respVO; |
| | | } |
| | | |
| | |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toSet()); |
| | | Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(deptIds); |
| | | // è§£æå²ä½IDï¼post_ids åçæ¯ JSON æ°ç»å符串ï¼å¦ [38]ï¼ï¼å¹¶æ¹éå è½½å²ä½åç§° |
| | | Map<Long, List<Long>> empPostIdsMap = new LinkedHashMap<>(); |
| | | Set<Long> allPostIds = new LinkedHashSet<>(); |
| | | for (HrmEmployeeDO emp : list) { |
| | | List<Long> postIdList = parseEmployeePostIds(emp.getPostIds()); |
| | | empPostIdsMap.put(emp.getId(), postIdList); |
| | | allPostIds.addAll(postIdList); |
| | | } |
| | | Map<Long, PostRespDTO> postMap = CollUtil.isEmpty(allPostIds) |
| | | ? Collections.emptyMap() : postApi.getPostMap(allPostIds); |
| | | return list.stream().map(emp -> { |
| | | HrmEmployeeSimpleRespVO vo = new HrmEmployeeSimpleRespVO(); |
| | | vo.setId(emp.getId()); |
| | | vo.setEmployeeNo(emp.getEmployeeNo()); |
| | | vo.setName(emp.getName()); |
| | | vo.setEmployeeStatus(emp.getEmployeeStatus()); |
| | | vo.setDeptId(emp.getDeptId()); |
| | | if (emp.getDeptId() != null && deptMap.containsKey(emp.getDeptId())) { |
| | | vo.setDeptName(deptMap.get(emp.getDeptId()).getName()); |
| | | } |
| | | // å²ä½ä¿¡æ¯ï¼ç¨äºè°å²çåºæ¯èªå¨å¸¦åºåå²ä½ï¼ |
| | | List<Long> postIdList = empPostIdsMap.getOrDefault(emp.getId(), Collections.emptyList()); |
| | | vo.setPostIds(postIdList); |
| | | if (CollUtil.isNotEmpty(postIdList)) { |
| | | vo.setPostName(postIdList.stream() |
| | | .map(postMap::get) |
| | | .filter(Objects::nonNull) |
| | | .map(PostRespDTO::getName) |
| | | .collect(Collectors.joining(","))); |
| | | } |
| | | return vo; |
| | | }).collect(Collectors.toList()); |
| | | } |
| | | |
| | | /** |
| | | * è§£æåå·¥çå²ä½IDå符串ï¼JSON æ°ç»æ ¼å¼ï¼å¦ [38] æ [1,2]ï¼ |
| | | */ |
| | | |
| | | /** |
| | | * è§£æåå·¥å²ä½IDå符串ï¼post_ids å JSON æ°ç»ï¼å¦ [38] æ [1,2]ï¼ |
| | | */ |
| | | private java.util.List<Long> parseEmployeePostIds(String postIds) { |
| | | if (StrUtil.isBlank(postIds)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | // post_ids å½¢å¦ "[38]" æ "[1,2]"ï¼å»æä¸æ¬å·åæéå·è§£æ |
| | | return Arrays.stream(postIds.replace("[", "").replace("]", "").split(",")) |
| | | .map(String::trim) |
| | | .filter(StrUtil::isNotBlank) |
| | | .map(s -> { |
| | | try { |
| | | return Long.valueOf(s); |
| | | } catch (NumberFormatException ex) { |
| | | return null; |
| | | } |
| | | }) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public HrmEmployeeDO getEmployeeByUserId(Long userId) { |
| | |
| | | } |
| | | |
| | | @Override |
| | | @LogRecord(type = HRM_EMPLOYEE_TYPE, subType = HRM_EMPLOYEE_IMPORT_SUB_TYPE, bizNo = "0", |
| | | success = HRM_EMPLOYEE_IMPORT_SUCCESS) |
| | | public HrmEmployeeImportRespVO importEmployeeList(List<HrmEmployeeImportExcelVO> importEmployees, |
| | | HrmEmployeeImportReqVO importReqVO) { |
| | | // 1. æ ¡éªé空 |
| | |
| | | respVO.getFailureNames().put(name, ex.getMessage()); |
| | | } |
| | | } |
| | | // è®°å½å¯¼å
¥ç»ææ±æ»ï¼ä¾ @LogRecord ç success ææ¡å¼ç¨ |
| | | OperateLogUtils.putImportSummary(respVO.getCreateNames(), respVO.getUpdateNames(), |
| | | respVO.getFailureNames()); |
| | | return respVO; |
| | | } |
| | | |
| | |
| | | if (StrUtil.isBlank(importEmployee.getPhone())) { |
| | | throw exception(EMPLOYEE_IMPORT_FIELD_REQUIRED, "ææºå·"); |
| | | } |
| | | if (importEmployee.getHireDate() == null) { |
| | | throw exception(EMPLOYEE_IMPORT_FIELD_REQUIRED, "å
¥èæ¥æ"); |
| | | LocalDate hireDate = parseImportDate(importEmployee.getHireDate()); |
| | | if (hireDate == null) { |
| | | throw exception(EMPLOYEE_IMPORT_FIELD_REQUIRED, "å
¥èæ¥æ(æ ¼å¼ï¼2015 æ 2015-01-01)"); |
| | | } |
| | | HrmEmployeeSaveReqVO reqVO = new HrmEmployeeSaveReqVO(); |
| | | reqVO.setName(importEmployee.getName()); |
| | |
| | | reqVO.setDeptId(deptId); |
| | | reqVO.setPostIds(CollUtil.isEmpty(postIds) ? null : new ArrayList<>(postIds)); |
| | | reqVO.setRoleIds(CollUtil.isEmpty(roleIds) ? null : new ArrayList<>(roleIds)); |
| | | reqVO.setHireDate(importEmployee.getHireDate()); |
| | | reqVO.setHireDate(hireDate); |
| | | reqVO.setEmployeeStatus(importEmployee.getEmployeeStatus() != null |
| | | ? importEmployee.getEmployeeStatus() : 1); // é»è®¤å¨è |
| | | reqVO.setEmail(importEmployee.getEmail()); |
| | | reqVO.setBirthday(importEmployee.getBirthday()); |
| | | reqVO.setBirthday(parseImportDate(importEmployee.getBirthday())); |
| | | reqVO.setIdCard(importEmployee.getIdCard()); |
| | | reqVO.setNation(importEmployee.getNation()); |
| | | reqVO.setMarriageStatus(importEmployee.getMarriageStatus()); |
| | |
| | | reqVO.setEducation(importEmployee.getEducation()); |
| | | reqVO.setPoliticalStatus(importEmployee.getPoliticalStatus()); |
| | | reqVO.setRemark(importEmployee.getRemark()); |
| | | reqVO.setEmployeeCategory(importEmployee.getEmployeeCategory()); |
| | | reqVO.setBaseSalary(importEmployee.getBaseSalary()); |
| | | reqVO.setBankName(importEmployee.getBankName()); |
| | | reqVO.setBankAccount(importEmployee.getBankAccount()); |
| | | reqVO.setAddress(importEmployee.getAddress()); |
| | | return reqVO; |
| | | } |
| | | |
| | |
| | | return roleIds; |
| | | } |
| | | |
| | | /** |
| | | * 容éè§£æå¯¼å
¥æ¥æææ¬ï¼æ¯æï¼2015 / 2015å¹´ / 2015-01 / 2015-01-02 / 2015/1/2 / 2015.1.2 / |
| | | * 2015-01-02 00:00:00 / 20150102ï¼ä»¥å Excel æ¥æåºåå·ï¼è§£æä¸åºè¿å nullã |
| | | */ |
| | | private static LocalDate parseImportDate(String raw) { |
| | | if (StrUtil.isBlank(raw)) { |
| | | return null; |
| | | } |
| | | String s = raw.trim(); |
| | | // 廿æ¶é´é¨å |
| | | int sp = s.indexOf(' '); |
| | | if (sp > 0) { |
| | | s = s.substring(0, sp); |
| | | } |
| | | // ä¸æå¹´ææ¥ / ææ / ç¹ / ä¸å线 ç»ä¸æ - åé |
| | | s = s.replace("å¹´", "-").replace("æ", "-").replace("æ¥", "") |
| | | .replace("/", "-").replace(".", "-").replace("_", "-").trim(); |
| | | s = s.replaceAll("^-+|-+$", ""); |
| | | if (StrUtil.isBlank(s)) { |
| | | return null; |
| | | } |
| | | try { |
| | | if (s.matches("\\d+")) { |
| | | int len = s.length(); |
| | | if (len == 4) { // 2015 |
| | | return LocalDate.of(Integer.parseInt(s), 1, 1); |
| | | } |
| | | if (len == 5) { // Excel æ¥æåºåå·ï¼1900 æ¥æç³»ç»ï¼åºå 1899-12-30ï¼ |
| | | return LocalDate.of(1899, 12, 30).plusDays(Long.parseLong(s)); |
| | | } |
| | | if (len == 6) { // 201506 |
| | | return LocalDate.of(Integer.parseInt(s.substring(0, 4)), Integer.parseInt(s.substring(4, 6)), 1); |
| | | } |
| | | if (len == 8) { // 20150601 |
| | | return LocalDate.of(Integer.parseInt(s.substring(0, 4)), Integer.parseInt(s.substring(4, 6)), Integer.parseInt(s.substring(6, 8))); |
| | | } |
| | | return null; |
| | | } |
| | | String[] parts = s.split("-"); |
| | | if (parts.length == 2) { // 2015-06 |
| | | return LocalDate.of(Integer.parseInt(parts[0]), Integer.parseInt(parts[1]), 1); |
| | | } |
| | | if (parts.length >= 3) { // 2015-06-01 |
| | | return LocalDate.of(Integer.parseInt(parts[0]), Integer.parseInt(parts[1]), Integer.parseInt(parts[2])); |
| | | } |
| | | } catch (Exception e) { |
| | | return null; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | // ========== è½¬æ£ ========== |
| | | |
| | | @Override |
| | | public void regularizeEmployee(Long id) { |
| | | validateEmployee(id); |
| | | HrmEmployeeContractDO contract = findCurrentContract(id); |
| | | // ä»
ãå³å¨ååãå¯è½¬æ£ï¼å³å¡ååä¸è½¬æ£ |
| | | if (contract == null || !Integer.valueOf(1).equals(contract.getContractType())) { |
| | | throw exception(EMPLOYEE_REGULARIZE_NEED_LABOR_CONTRACT); |
| | | } |
| | | HrmEmployeeDO updateObj = new HrmEmployeeDO(); |
| | | updateObj.setId(id); |
| | | updateObj.setEmployeeStatus(1); // 1-å¨è |
| | | updateObj.setRegularDate(LocalDate.now()); |
| | | employeeMapper.updateById(updateObj); |
| | | } |
| | | |
| | | @Override |
| | | public List<HrmEmployeeDO> getRegularizeReminderList(Integer days) { |
| | | int aheadDays = days == null ? 7 : days; |
| | | LocalDate limit = LocalDate.now().plusDays(aheadDays); |
| | | // è¯ç¨ä¸çåå·¥ |
| | | List<HrmEmployeeDO> trials = employeeMapper.selectList( |
| | | new LambdaQueryWrapperX<HrmEmployeeDO>().eq(HrmEmployeeDO::getEmployeeStatus, 2)); |
| | | List<HrmEmployeeDO> result = new ArrayList<>(); |
| | | for (HrmEmployeeDO employee : trials) { |
| | | HrmEmployeeContractDO contract = findCurrentContract(employee.getId()); |
| | | // ä»
å³å¨ååãä¸è¯ç¨æå³å°å°æ |
| | | if (contract == null || !Integer.valueOf(1).equals(contract.getContractType())) { |
| | | continue; |
| | | } |
| | | LocalDate probationEnd = contract.getProbationEndDate(); |
| | | if (probationEnd != null && !probationEnd.isAfter(limit)) { |
| | | result.add(employee); |
| | | } |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | /** æ¥è¯¢åå·¥å½åçæåå */ |
| | | private HrmEmployeeContractDO findCurrentContract(Long employeeId) { |
| | | List<HrmEmployeeContractDO> list = contractMapper.selectListByEmployee(employeeId); |
| | | if (CollUtil.isEmpty(list)) { |
| | | return null; |
| | | } |
| | | for (HrmEmployeeContractDO contract : list) { |
| | | if (Boolean.TRUE.equals(contract.getIsCurrent())) { |
| | | return contract; |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.hrm.service.employee.contract; |
| | | |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import java.io.ByteArrayInputStream; |
| | | import java.io.File; |
| | | import java.io.FileOutputStream; |
| | | import java.io.IOException; |
| | | import java.io.InputStream; |
| | | |
| | | /** |
| | | * åèæ°ç»ç MultipartFileï¼æå
åä¸çåèå
è£
æ MultipartFileï¼ |
| | | * 便äºå¤ç¨ system ç SystemStorageBlobService.upload(...) å卿件并æ¿å° blobIdã |
| | | */ |
| | | public class ByteArrayMultipartFile implements MultipartFile { |
| | | |
| | | private final String name; |
| | | private final String originalFilename; |
| | | private final String contentType; |
| | | private final byte[] content; |
| | | |
| | | public ByteArrayMultipartFile(String name, String originalFilename, String contentType, byte[] content) { |
| | | this.name = name; |
| | | this.originalFilename = originalFilename; |
| | | this.contentType = contentType; |
| | | this.content = content == null ? new byte[0] : content; |
| | | } |
| | | |
| | | @Override |
| | | public String getName() { |
| | | return name; |
| | | } |
| | | |
| | | @Override |
| | | public String getOriginalFilename() { |
| | | return originalFilename; |
| | | } |
| | | |
| | | @Override |
| | | public String getContentType() { |
| | | return contentType; |
| | | } |
| | | |
| | | @Override |
| | | public boolean isEmpty() { |
| | | return content.length == 0; |
| | | } |
| | | |
| | | @Override |
| | | public long getSize() { |
| | | return content.length; |
| | | } |
| | | |
| | | @Override |
| | | public byte[] getBytes() { |
| | | return content; |
| | | } |
| | | |
| | | @Override |
| | | public InputStream getInputStream() { |
| | | return new ByteArrayInputStream(content); |
| | | } |
| | | |
| | | @Override |
| | | public void transferTo(File dest) throws IOException { |
| | | try (FileOutputStream fos = new FileOutputStream(dest)) { |
| | | fos.write(content); |
| | | } |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.hrm.service.employee.contract; |
| | | |
| | | /** |
| | | * åå·¥ååææ¡£çæ Service |
| | | * |
| | | * æåå + åå·¥æ°æ®ï¼å¥ç¨ Word 模æ¿çæå³å¨åå / å³å¡åå docxã |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | public interface ContractDocumentService { |
| | | |
| | | /** |
| | | * çæåå docx |
| | | * |
| | | * @param contractId ååç¼å· |
| | | * @param templateType 模æ¿ç±»åï¼1-å³å¨åå(ç®ç) 2-å³å¨åå(æ å) 3-å³å¡åå |
| | | * @return docx åè |
| | | */ |
| | | byte[] generateDocx(Long contractId, Integer templateType); |
| | | |
| | | /** |
| | | * çææä»¶å |
| | | */ |
| | | String buildFileName(Long contractId, Integer templateType); |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.hrm.service.employee.contract; |
| | | |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeContractDO; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeDO; |
| | | import cn.iocoder.yudao.module.hrm.dal.mysql.employee.HrmEmployeeContractMapper; |
| | | import cn.iocoder.yudao.module.hrm.dal.mysql.employee.HrmEmployeeMapper; |
| | | import com.deepoove.poi.XWPFTemplate; |
| | | import jakarta.annotation.Resource; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.io.ByteArrayOutputStream; |
| | | import java.io.InputStream; |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDate; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.time.temporal.ChronoUnit; |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.CONTRACT_TEMPLATE_LOAD_FAILED; |
| | | import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.CONTRACT_TEMPLATE_TYPE_NOT_SUPPORT; |
| | | |
| | | /** |
| | | * åå·¥ååææ¡£çæ Service å®ç°ç±» |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Slf4j |
| | | @Service |
| | | public class ContractDocumentServiceImpl implements ContractDocumentService { |
| | | |
| | | /** å³å¨ååï¼ç®çÂ·ç¤¾ä¿æç°ï¼ */ |
| | | public static final int TYPE_LABOR_SIMPLE = 1; |
| | | /** å³å¨ååï¼æ åÂ·å®æ¹å¶å¼ï¼ */ |
| | | public static final int TYPE_LABOR_STANDARD = 2; |
| | | /** å³å¡åå */ |
| | | public static final int TYPE_SERVICE = 3; |
| | | |
| | | private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyyå¹´MMæddæ¥"); |
| | | |
| | | private static final Map<Integer, String> TEMPLATE_PATHS = Map.of( |
| | | TYPE_LABOR_SIMPLE, "template/contract/contract_labor_simple.docx", |
| | | TYPE_LABOR_STANDARD, "template/contract/contract_labor_standard.docx", |
| | | TYPE_SERVICE, "template/contract/contract_service.docx"); |
| | | |
| | | private static final Map<Integer, String> TEMPLATE_NAMES = Map.of( |
| | | TYPE_LABOR_SIMPLE, "å³å¨åå(ç®ç)", |
| | | TYPE_LABOR_STANDARD, "å³å¨åå(æ å)", |
| | | TYPE_SERVICE, "å³å¡åå"); |
| | | |
| | | @Resource |
| | | private HrmEmployeeContractMapper contractMapper; |
| | | @Resource |
| | | private HrmEmployeeMapper employeeMapper; |
| | | |
| | | @Override |
| | | public byte[] generateDocx(Long contractId, Integer templateType) { |
| | | HrmEmployeeContractDO contract = contractMapper.selectById(contractId); |
| | | if (contract == null) { |
| | | throw exception(CONTRACT_TEMPLATE_LOAD_FAILED); |
| | | } |
| | | HrmEmployeeDO employee = contract.getEmployeeId() == null ? null |
| | | : employeeMapper.selectById(contract.getEmployeeId()); |
| | | Map<String, Object> vars = buildVariables(contract, employee); |
| | | |
| | | String path = TEMPLATE_PATHS.get(templateType); |
| | | if (path == null) { |
| | | throw exception(CONTRACT_TEMPLATE_TYPE_NOT_SUPPORT); |
| | | } |
| | | try (InputStream in = getClass().getClassLoader().getResourceAsStream(path)) { |
| | | if (in == null) { |
| | | throw exception(CONTRACT_TEMPLATE_LOAD_FAILED); |
| | | } |
| | | XWPFTemplate template = XWPFTemplate.compile(in).render(vars); |
| | | ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| | | template.write(out); |
| | | template.close(); |
| | | return out.toByteArray(); |
| | | } catch (Exception e) { |
| | | log.error("[generateDocx][åå({}) 模æ¿({}) çæå¤±è´¥]", contractId, templateType, e); |
| | | throw exception(CONTRACT_TEMPLATE_LOAD_FAILED); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public String buildFileName(Long contractId, Integer templateType) { |
| | | HrmEmployeeContractDO contract = contractMapper.selectById(contractId); |
| | | String no = contract != null && contract.getContractNo() != null ? contract.getContractNo() : String.valueOf(contractId); |
| | | String name = TEMPLATE_NAMES.getOrDefault(templateType, "åå"); |
| | | return no + "-" + name + ".docx"; |
| | | } |
| | | |
| | | private Map<String, Object> buildVariables(HrmEmployeeContractDO contract, HrmEmployeeDO employee) { |
| | | Map<String, Object> vars = new HashMap<>(); |
| | | // ç²æ¹ï¼å
¬å¸ï¼ââ ç®ååç¾çº¦ä¸»ä½ï¼å
¶ä½ç空åç»å¯æ¥å
¥å
¬å¸é
ç½® |
| | | vars.put("companyName", nvl(contract.getSignCompany())); |
| | | vars.put("companyAddress", ""); |
| | | vars.put("legalPerson", ""); |
| | | vars.put("companyPhone", ""); |
| | | vars.put("creditCode", ""); |
| | | // 乿¹ï¼åå·¥ï¼ |
| | | vars.put("employeeName", employee != null ? nvl(employee.getName()) : ""); |
| | | vars.put("gender", employee != null ? genderText(employee.getGender()) : ""); |
| | | vars.put("age", employee != null ? nvl(employee.getAge()) : ""); |
| | | vars.put("idCard", employee != null ? nvl(employee.getIdCard()) : ""); |
| | | vars.put("householdAddress", employee != null ? nvl(employee.getAddress()) : ""); |
| | | vars.put("address", employee != null ? nvl(employee.getAddress()) : ""); |
| | | vars.put("phone", employee != null ? nvl(employee.getPhone()) : ""); |
| | | vars.put("emergencyPhone", ""); |
| | | // åå |
| | | vars.put("contractNo", nvl(contract.getContractNo())); |
| | | vars.put("postName", ""); |
| | | vars.put("postDuty", ""); |
| | | vars.put("workPlace", ""); |
| | | vars.put("contractStart", formatDate(contract.getStartDate())); |
| | | vars.put("contractEnd", formatDate(contract.getEndDate())); |
| | | vars.put("contractMonths", monthsBetween(contract.getStartDate(), contract.getEndDate())); |
| | | vars.put("probationStart", formatDate(contract.getProbationStartDate())); |
| | | vars.put("probationEnd", formatDate(contract.getProbationEndDate())); |
| | | vars.put("probationMonths", monthsBetween(contract.getProbationStartDate(), contract.getProbationEndDate())); |
| | | vars.put("probationDays", daysBetween(contract.getProbationStartDate(), contract.getProbationEndDate())); |
| | | vars.put("birthday", employee != null ? formatDate(employee.getBirthday()) : ""); |
| | | // å¹´ææ¥ï¼ä¾ã___å¹´___æ___æ¥ãéæ ¼å¡«åï¼ |
| | | vars.put("contractStartY", y(contract.getStartDate())); |
| | | vars.put("contractStartM", mo(contract.getStartDate())); |
| | | vars.put("contractStartD", d(contract.getStartDate())); |
| | | vars.put("contractEndY", y(contract.getEndDate())); |
| | | vars.put("contractEndM", mo(contract.getEndDate())); |
| | | vars.put("contractEndD", d(contract.getEndDate())); |
| | | vars.put("probationStartY", y(contract.getProbationStartDate())); |
| | | vars.put("probationStartM", mo(contract.getProbationStartDate())); |
| | | vars.put("probationStartD", d(contract.getProbationStartDate())); |
| | | vars.put("probationEndY", y(contract.getProbationEndDate())); |
| | | vars.put("probationEndM", mo(contract.getProbationEndDate())); |
| | | vars.put("probationEndD", d(contract.getProbationEndDate())); |
| | | vars.put("probationSalary", nvl(contract.getProbationSalary())); |
| | | vars.put("regularSalary", nvl(contract.getRegularSalary())); |
| | | vars.put("baseSalary", nvl(contract.getRegularSalary())); |
| | | vars.put("performanceSalary", ""); |
| | | vars.put("socialSecuritySalary", ""); |
| | | vars.put("signDate", formatDate(contract.getSignDate())); |
| | | // å³å¡ååéçå¯é项 |
| | | vars.put("payType", "ä¸"); |
| | | vars.put("payDay", ""); |
| | | vars.put("workVolume", ""); |
| | | vars.put("workAmount", ""); |
| | | vars.put("piecePrice", ""); |
| | | vars.put("noticeCompensation", ""); |
| | | vars.put("copies", "两"); |
| | | vars.put("eachCopies", "ä¸"); |
| | | return vars; |
| | | } |
| | | |
| | | private static String nvl(Object o) { |
| | | return o == null ? "" : o.toString(); |
| | | } |
| | | |
| | | private static String formatDate(LocalDate d) { |
| | | return d == null ? "" : d.format(DATE_FMT); |
| | | } |
| | | |
| | | private static String monthsBetween(LocalDate start, LocalDate end) { |
| | | if (start == null || end == null) { |
| | | return ""; |
| | | } |
| | | long months = ChronoUnit.MONTHS.between(start, end); |
| | | return String.valueOf(Math.max(months, 0)); |
| | | } |
| | | |
| | | private static String daysBetween(LocalDate start, LocalDate end) { |
| | | if (start == null || end == null) { |
| | | return ""; |
| | | } |
| | | long days = ChronoUnit.DAYS.between(start, end); |
| | | return String.valueOf(Math.max(days, 0)); |
| | | } |
| | | |
| | | private static String y(LocalDate date) { |
| | | return date == null ? "" : String.format("%04d", date.getYear()); |
| | | } |
| | | |
| | | private static String mo(LocalDate date) { |
| | | return date == null ? "" : String.format("%02d", date.getMonthValue()); |
| | | } |
| | | |
| | | private static String d(LocalDate date) { |
| | | return date == null ? "" : String.format("%02d", date.getDayOfMonth()); |
| | | } |
| | | |
| | | private static String genderText(Integer sex) { |
| | | if (sex == null) { |
| | | return ""; |
| | | } |
| | | return sex == 1 ? "ç·" : sex == 2 ? "女" : ""; |
| | | } |
| | | |
| | | // å
¼å®¹ BigDecimal ç´æ¥ toString |
| | | private static String nvl(BigDecimal v) { |
| | | return v == null ? "" : v.toPlainString(); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.hrm.service.salary; |
| | | |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.HrmOvertimeWageConfigDO; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * å²ä½å çå·¥èµé
ç½® Service |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | public interface HrmOvertimeWageConfigService { |
| | | |
| | | Long createOvertimeWageConfig(HrmOvertimeWageConfigDO config); |
| | | |
| | | void updateOvertimeWageConfig(HrmOvertimeWageConfigDO config); |
| | | |
| | | void deleteOvertimeWageConfig(Long id); |
| | | |
| | | HrmOvertimeWageConfigDO getOvertimeWageConfig(Long id); |
| | | |
| | | List<HrmOvertimeWageConfigDO> getOvertimeWageConfigList(Long postId, Integer status); |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.hrm.service.salary; |
| | | |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.HrmOvertimeWageConfigDO; |
| | | import cn.iocoder.yudao.module.hrm.dal.mysql.salary.HrmOvertimeWageConfigMapper; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.OVERTIME_WAGE_CONFIG_NOT_EXISTS; |
| | | import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.OVERTIME_WAGE_CONFIG_POST_EXISTS; |
| | | |
| | | /** |
| | | * å²ä½å çå·¥èµé
ç½® Service å®ç°ç±» |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Service |
| | | @Validated |
| | | public class HrmOvertimeWageConfigServiceImpl implements HrmOvertimeWageConfigService { |
| | | |
| | | /** |
| | | * é»è®¤å çæ¶èªï¼å
/å°æ¶ï¼ |
| | | */ |
| | | private static final BigDecimal DEFAULT_HOURLY_RATE = new BigDecimal("20"); |
| | | |
| | | @Resource |
| | | private HrmOvertimeWageConfigMapper overtimeWageConfigMapper; |
| | | |
| | | @Override |
| | | public Long createOvertimeWageConfig(HrmOvertimeWageConfigDO config) { |
| | | validatePostUnique(null, config.getPostId()); |
| | | // æªå¡«åæ¶èªæ¶ï¼ä½¿ç¨é»è®¤ 20 å
/å°æ¶ |
| | | if (config.getHourlyRate() == null) { |
| | | config.setHourlyRate(DEFAULT_HOURLY_RATE); |
| | | } |
| | | overtimeWageConfigMapper.insert(config); |
| | | return config.getId(); |
| | | } |
| | | |
| | | @Override |
| | | public void updateOvertimeWageConfig(HrmOvertimeWageConfigDO config) { |
| | | validateExists(config.getId()); |
| | | validatePostUnique(config.getId(), config.getPostId()); |
| | | overtimeWageConfigMapper.updateById(config); |
| | | } |
| | | |
| | | @Override |
| | | public void deleteOvertimeWageConfig(Long id) { |
| | | validateExists(id); |
| | | overtimeWageConfigMapper.deleteById(id); |
| | | } |
| | | |
| | | @Override |
| | | public HrmOvertimeWageConfigDO getOvertimeWageConfig(Long id) { |
| | | return overtimeWageConfigMapper.selectById(id); |
| | | } |
| | | |
| | | @Override |
| | | public List<HrmOvertimeWageConfigDO> getOvertimeWageConfigList(Long postId, Integer status) { |
| | | return overtimeWageConfigMapper.selectListByCondition(postId, status); |
| | | } |
| | | |
| | | private void validateExists(Long id) { |
| | | if (id == null || overtimeWageConfigMapper.selectById(id) == null) { |
| | | throw exception(OVERTIME_WAGE_CONFIG_NOT_EXISTS); |
| | | } |
| | | } |
| | | |
| | | private void validatePostUnique(Long id, Long postId) { |
| | | if (postId == null) { |
| | | return; |
| | | } |
| | | HrmOvertimeWageConfigDO config = overtimeWageConfigMapper.selectByPostId(postId); |
| | | if (config != null && !config.getId().equals(id)) { |
| | | throw exception(OVERTIME_WAGE_CONFIG_POST_EXISTS); |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | */ |
| | | void saveSalaryCalculationList(List<HrmSalaryCalculationDO> calculationList); |
| | | |
| | | /** |
| | | * ä¿®æ¹åæ¡èªé
¬æ ¸ç®è®°å½ï¼ä»
å¾
ç¡®è®¤ç¶æå¯ä¿®æ¹ï¼å®åå·¥èµç±å端éç®ï¼ |
| | | * |
| | | * @param calculation æ ¸ç®è®°å½ |
| | | */ |
| | | void updateSalaryCalculation(HrmSalaryCalculationDO calculation); |
| | | |
| | | } |
| | |
| | | import cn.iocoder.yudao.module.hrm.dal.mysql.salary.HrmSalaryStructureMapper; |
| | | import cn.iocoder.yudao.module.hrm.dal.redis.HrmNoRedisDAO; |
| | | import cn.iocoder.yudao.module.system.api.dept.DeptApi; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.hutool.json.JSONUtil; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.HrmOvertimeWageConfigDO; |
| | | import cn.iocoder.yudao.module.hrm.dal.mysql.salary.HrmOvertimeWageConfigMapper; |
| | | import cn.iocoder.yudao.module.hrm.enums.HrmEmployeeCategoryEnum; |
| | | import cn.iocoder.yudao.module.hrm.enums.HrmOvertimeTypeEnum; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.stereotype.Service; |
| | |
| | | private HrmOvertimeApplicationMapper overtimeApplicationMapper; |
| | | @Resource |
| | | private HrmSalaryStructureMapper salaryStructureMapper; |
| | | @Resource |
| | | private HrmOvertimeWageConfigMapper overtimeWageConfigMapper; |
| | | @Resource |
| | | private HrmLeaveTypeConfigMapper leaveTypeConfigMapper; |
| | | @Resource |
| | |
| | | HrmSocialSecuritySchemeDO scheme, |
| | | LocalDate effectiveDate) { |
| | | // 计ç®åºåèªèµ |
| | | BigDecimal baseSalary = employeeSalary.getBaseSalary() != null ? employeeSalary.getBaseSalary() : BigDecimal.ZERO; |
| | | // åºæ¬å·¥èµç»ä¸åãå工信æ¯ãä¸ç»´æ¤çåºæ¬èªèµï¼èªé
¬æ¡£æ¡éç baseSalary ä¸åä½ä¸ºæ ¸ç®æ¥æºï¼ |
| | | // é¿å
å工信æ¯ä¸èªé
¬æ¡£æ¡ä¸ä¸è´æ¶ï¼æ ¸ç®ç»æä¸å工档æ¡å¯¹ä¸ä¸ï¼ |
| | | BigDecimal baseSalary = employee.getBaseSalary() != null ? employee.getBaseSalary() : BigDecimal.ZERO; |
| | | BigDecimal performanceSalary = employeeSalary.getPerformanceSalary() != null ? employeeSalary.getPerformanceSalary() : BigDecimal.ZERO; |
| | | BigDecimal mealAllowance = employeeSalary.getMealAllowance() != null ? employeeSalary.getMealAllowance() : BigDecimal.ZERO; |
| | | BigDecimal transportAllowance = employeeSalary.getTransportAllowance() != null ? employeeSalary.getTransportAllowance() : BigDecimal.ZERO; |
| | |
| | | } |
| | | |
| | | /** |
| | | * æ±æ»å½æå®¡æ¹éè¿çå çç³è¯·ãå°æ¶å·¥èµæåºæ¬å·¥èµ / 21.75 / 8 计ç®ï¼ |
| | | * 工使¥é»è®¤ 1.5 åã伿¯æ¥é»è®¤ 2 åãæ³å®è忥é»è®¤ 3 åï¼åå¯ç±èªé
¬ç»æè¦çã |
| | | * æ±æ»å½æå®¡æ¹éè¿çå çç³è¯·ï¼è®¡ç®å çå·¥èµä¸å çæ¶é¿ã |
| | | * |
| | | * <p>æãå工类å«ãå·®å¼åæ ¸ç®ï¼ |
| | | * <ul> |
| | | * <li>æå·¥ï¼å çèªèµæ 1:1 æ ¸ç®ï¼ä¸å¹³æ¶æ¶èªä¸è´ï¼åºæ¬å·¥èµ / 21.75 / 8ï¼ï¼</li> |
| | | * <li>æ®éåå·¥ï¼æå²ä½é
ç½®çåºå®å çæ¶èªï¼é»è®¤ 20 å
/å°æ¶ï¼æ ¸ç®ï¼</li> |
| | | * <li>管çï¼ä¸è®¡å çå·¥èµï¼</li> |
| | | * </ul> |
| | | * å½ä¸å²ä½é
ç½®æ¶ï¼æé
ç½®çæå¤§å çæ¶é¿ä¸é对å çæ¶é¿å°é¡¶ï¼ |
| | | * æªå½ä¸ä»»ä½å²ä½é
ç½®ä¸éæå·¥/ç®¡çæ¶ï¼åéæ§é»è¾ï¼èªé
¬ç»æåç / å çç±»åé»è®¤åçï¼ã |
| | | */ |
| | | private OvertimeSalary calculateOvertimeSalary(HrmEmployeeDO employee, HrmEmployeeSalaryDO employeeSalary, |
| | | String period, BigDecimal baseSalary) { |
| | |
| | | if (CollUtil.isEmpty(applications)) { |
| | | return new OvertimeSalary(BigDecimal.ZERO, BigDecimal.ZERO); |
| | | } |
| | | Long structureId = employeeSalary.getSalaryStructureId() != null |
| | | ? employeeSalary.getSalaryStructureId() : employee.getSalaryStructureId(); |
| | | HrmSalaryStructureDO structure = structureId == null ? null : salaryStructureMapper.selectById(structureId); |
| | | BigDecimal workdayMultiplier = structure != null && structure.getOvertimePayRatio() != null |
| | | ? structure.getOvertimePayRatio() : HrmOvertimeTypeEnum.WORKDAY.getSalaryMultiplier(); |
| | | BigDecimal restDayMultiplier = structure != null && structure.getRestDayOvertimePayRatio() != null |
| | | ? structure.getRestDayOvertimePayRatio() : HrmOvertimeTypeEnum.REST_DAY.getSalaryMultiplier(); |
| | | BigDecimal legalHolidayMultiplier = structure != null && structure.getLegalHolidayOvertimePayRatio() != null |
| | | ? structure.getLegalHolidayOvertimePayRatio() : HrmOvertimeTypeEnum.LEGAL_HOLIDAY.getSalaryMultiplier(); |
| | | BigDecimal hourlySalary = baseSalary.divide(new BigDecimal("21.75"), 8, RoundingMode.HALF_UP) |
| | | // 管çç±»å«ä¸è®¡å çå·¥èµ |
| | | HrmEmployeeCategoryEnum category = HrmEmployeeCategoryEnum.valueOfCategory(employee.getEmployeeCategory()); |
| | | if (category == HrmEmployeeCategoryEnum.MANAGER) { |
| | | return new OvertimeSalary(BigDecimal.ZERO, BigDecimal.ZERO); |
| | | } |
| | | // åå·¥å²ä½å½ä¸çå çå·¥èµé
ç½®ï¼åæ¶èªæé«ãä¸éæå®½ç䏿¡ï¼ |
| | | OvertimeWageRule wageRule = resolveOvertimeWageRule(employee); |
| | | // å¹³æ¶æ¶èª = åºæ¬å·¥èµ / 21.75 / 8 |
| | | BigDecimal baseHourlySalary = baseSalary.divide(new BigDecimal("21.75"), 8, RoundingMode.HALF_UP) |
| | | .divide(new BigDecimal("8"), 8, RoundingMode.HALF_UP); |
| | | |
| | | BigDecimal totalHours = BigDecimal.ZERO; |
| | | BigDecimal totalPay = BigDecimal.ZERO; |
| | | for (HrmOvertimeApplicationDO application : applications) { |
| | | BigDecimal hours = application.getDuration() == null ? BigDecimal.ZERO : application.getDuration(); |
| | | HrmOvertimeTypeEnum type = HrmOvertimeTypeEnum.valueOfType(application.getOvertimeType()); |
| | | if (type == null || hours.signum() <= 0) { |
| | | continue; |
| | | if (category == HrmEmployeeCategoryEnum.TECHNICIAN || wageRule != null) { |
| | | // æå·¥ï¼1:1ï¼ä¸å¹³æ¶æ¶èªä¸è´ï¼ï¼æ®éåå·¥å½ä¸é
ç½®ï¼æé
ç½®åºå®æ¶èªï¼åä¸åºåå çç±»ååç |
| | | BigDecimal hourlySalary = category == HrmEmployeeCategoryEnum.TECHNICIAN |
| | | ? baseHourlySalary : wageRule.hourlyRate(); |
| | | for (HrmOvertimeApplicationDO application : applications) { |
| | | BigDecimal hours = application.getDuration() == null ? BigDecimal.ZERO : application.getDuration(); |
| | | HrmOvertimeTypeEnum type = HrmOvertimeTypeEnum.valueOfType(application.getOvertimeType()); |
| | | if (type == null || hours.signum() <= 0) { |
| | | continue; |
| | | } |
| | | totalHours = totalHours.add(hours); |
| | | } |
| | | BigDecimal multiplier = switch (type) { |
| | | case WORKDAY -> workdayMultiplier; |
| | | case REST_DAY -> restDayMultiplier; |
| | | case LEGAL_HOLIDAY -> legalHolidayMultiplier; |
| | | }; |
| | | totalHours = totalHours.add(hours); |
| | | totalPay = totalPay.add(hourlySalary.multiply(hours).multiply(multiplier)); |
| | | // æå²ä½é
ç½®å°é¡¶å çæ¶é¿ |
| | | BigDecimal paidHours = wageRule != null && wageRule.maxOvertimeHours() != null |
| | | && totalHours.compareTo(wageRule.maxOvertimeHours()) > 0 |
| | | ? wageRule.maxOvertimeHours() : totalHours; |
| | | totalPay = hourlySalary.multiply(paidHours); |
| | | } else { |
| | | // å
¼å®¹æ§é»è¾ï¼æ å²ä½é
ç½®ãä¸éæå·¥/管ç |
| | | Long structureId = employeeSalary.getSalaryStructureId() != null |
| | | ? employeeSalary.getSalaryStructureId() : employee.getSalaryStructureId(); |
| | | HrmSalaryStructureDO structure = structureId == null ? null : salaryStructureMapper.selectById(structureId); |
| | | BigDecimal workdayMultiplier = structure != null && structure.getOvertimePayRatio() != null |
| | | ? structure.getOvertimePayRatio() : HrmOvertimeTypeEnum.WORKDAY.getSalaryMultiplier(); |
| | | BigDecimal restDayMultiplier = structure != null && structure.getRestDayOvertimePayRatio() != null |
| | | ? structure.getRestDayOvertimePayRatio() : HrmOvertimeTypeEnum.REST_DAY.getSalaryMultiplier(); |
| | | BigDecimal legalHolidayMultiplier = structure != null && structure.getLegalHolidayOvertimePayRatio() != null |
| | | ? structure.getLegalHolidayOvertimePayRatio() : HrmOvertimeTypeEnum.LEGAL_HOLIDAY.getSalaryMultiplier(); |
| | | for (HrmOvertimeApplicationDO application : applications) { |
| | | BigDecimal hours = application.getDuration() == null ? BigDecimal.ZERO : application.getDuration(); |
| | | HrmOvertimeTypeEnum type = HrmOvertimeTypeEnum.valueOfType(application.getOvertimeType()); |
| | | if (type == null || hours.signum() <= 0) { |
| | | continue; |
| | | } |
| | | BigDecimal multiplier = switch (type) { |
| | | case WORKDAY -> workdayMultiplier; |
| | | case REST_DAY -> restDayMultiplier; |
| | | case LEGAL_HOLIDAY -> legalHolidayMultiplier; |
| | | }; |
| | | totalHours = totalHours.add(hours); |
| | | totalPay = totalPay.add(baseHourlySalary.multiply(hours).multiply(multiplier)); |
| | | } |
| | | } |
| | | return new OvertimeSalary(totalHours.setScale(1, RoundingMode.HALF_UP), |
| | | totalPay.setScale(2, RoundingMode.HALF_UP)); |
| | | } |
| | | |
| | | /** |
| | | * è§£æåå·¥å²ä½å¯¹åºçå çå·¥èµè§åï¼æ¶èªåå½ä¸é
ç½®ä¸çæå¤§å¼ï¼ |
| | | * ä¸éåâæå®½âï¼å卿ªè®¾ç½®ä¸éçé
ç½®åè§ä¸ºä¸éï¼å¦ååæå¤§å¼ï¼ã |
| | | */ |
| | | private OvertimeWageRule resolveOvertimeWageRule(HrmEmployeeDO employee) { |
| | | if (StrUtil.isEmpty(employee.getPostIds())) { |
| | | return null; |
| | | } |
| | | List<Long> postIds; |
| | | try { |
| | | postIds = JSONUtil.toList(employee.getPostIds(), Long.class); |
| | | } catch (Exception ex) { |
| | | return null; |
| | | } |
| | | if (CollUtil.isEmpty(postIds)) { |
| | | return null; |
| | | } |
| | | List<HrmOvertimeWageConfigDO> configs = overtimeWageConfigMapper.selectEnabledListByPostIds(postIds); |
| | | if (CollUtil.isEmpty(configs)) { |
| | | return null; |
| | | } |
| | | BigDecimal hourlyRate = null; |
| | | BigDecimal maxHours = null; |
| | | boolean unlimited = false; |
| | | for (HrmOvertimeWageConfigDO config : configs) { |
| | | if (config.getHourlyRate() != null |
| | | && (hourlyRate == null || config.getHourlyRate().compareTo(hourlyRate) > 0)) { |
| | | hourlyRate = config.getHourlyRate(); |
| | | } |
| | | if (config.getMaxOvertimeHours() == null) { |
| | | unlimited = true; |
| | | } else if (!unlimited && (maxHours == null || config.getMaxOvertimeHours().compareTo(maxHours) > 0)) { |
| | | maxHours = config.getMaxOvertimeHours(); |
| | | } |
| | | } |
| | | if (hourlyRate == null) { |
| | | return null; |
| | | } |
| | | return new OvertimeWageRule(hourlyRate, unlimited ? null : maxHours); |
| | | } |
| | | |
| | | private record OvertimeWageRule(BigDecimal hourlyRate, BigDecimal maxOvertimeHours) {} |
| | | |
| | | private record OvertimeSalary(BigDecimal hours, BigDecimal pay) {} |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void updateSalaryCalculation(HrmSalaryCalculationDO calculation) { |
| | | HrmSalaryCalculationDO exist = salaryCalculationMapper.selectById(calculation.getId()); |
| | | if (exist == null) { |
| | | throw exception(SALARY_CALCULATION_NOT_EXISTS); |
| | | } |
| | | // ä»
å¾
ç¡®è®¤ç¶æå¯ä¿®æ¹ï¼å·²ç¡®è®¤éå
æ¤éï¼å·²åæ¾ä¸å¯æ¹ |
| | | if (exist.getStatus() == null || !Objects.equals(exist.getStatus(), 0)) { |
| | | throw exception(SALARY_CALCULATION_UPDATE_FAIL); |
| | | } |
| | | // 以ä¸å段ä¸å
许éè¿ä¿®æ¹æ¥å£åæ´ï¼ä¸å¾ä»¥åºä¸ä¸ºå |
| | | calculation.setNo(exist.getNo()); |
| | | calculation.setPeriod(exist.getPeriod()); |
| | | calculation.setUserId(exist.getUserId()); |
| | | calculation.setDeptId(exist.getDeptId()); |
| | | calculation.setStatus(exist.getStatus()); |
| | | calculation.setConfirmTime(exist.getConfirmTime()); |
| | | calculation.setConfirmUserId(exist.getConfirmUserId()); |
| | | calculation.setPaymentTime(exist.getPaymentTime()); |
| | | // éé¢å段为 null æ¶ç»ä¸æ 0 å¤çï¼MyBatis-Plus ç updateById é»è®¤å¿½ç¥ nullï¼ |
| | | // å¦åä¼åºç°ãå®åå·¥èµæ 0 计ç®ã使ç»ä»ä¿çæ§å¼ãçä¸ä¸è´ |
| | | calculation.setBaseSalary(nvl(calculation.getBaseSalary())); |
| | | calculation.setPerformanceSalary(nvl(calculation.getPerformanceSalary())); |
| | | calculation.setOvertimePay(nvl(calculation.getOvertimePay())); |
| | | calculation.setMealAllowance(nvl(calculation.getMealAllowance())); |
| | | calculation.setTransportAllowance(nvl(calculation.getTransportAllowance())); |
| | | calculation.setOtherAllowance(nvl(calculation.getOtherAllowance())); |
| | | calculation.setSocialSecurityDeduction(nvl(calculation.getSocialSecurityDeduction())); |
| | | calculation.setHousingFundDeduction(nvl(calculation.getHousingFundDeduction())); |
| | | calculation.setTaxDeduction(nvl(calculation.getTaxDeduction())); |
| | | calculation.setOtherDeduction(nvl(calculation.getOtherDeduction())); |
| | | calculation.setLeaveDeduction(nvl(calculation.getLeaveDeduction())); |
| | | // å®åå·¥èµç±å端éç®ï¼é¿å
åç«¯ä¼ å
¥å¼ä¸æç»ä¸ä¸è´ |
| | | calculation.setActualSalary(calcActualSalary(calculation)); |
| | | salaryCalculationMapper.updateById(calculation); |
| | | } |
| | | |
| | | /** |
| | | * 计ç®å®åå·¥èµ = æ¶å
¥é¡¹å计 - æ£é¤é¡¹å计ï¼ä¿ç两ä½å°æ° |
| | | */ |
| | | private static BigDecimal calcActualSalary(HrmSalaryCalculationDO c) { |
| | | BigDecimal income = nvl(c.getBaseSalary()) |
| | | .add(nvl(c.getPerformanceSalary())) |
| | | .add(nvl(c.getOvertimePay())) |
| | | .add(nvl(c.getMealAllowance())) |
| | | .add(nvl(c.getTransportAllowance())) |
| | | .add(nvl(c.getOtherAllowance())); |
| | | BigDecimal deduction = nvl(c.getSocialSecurityDeduction()) |
| | | .add(nvl(c.getHousingFundDeduction())) |
| | | .add(nvl(c.getTaxDeduction())) |
| | | .add(nvl(c.getOtherDeduction())) |
| | | .add(nvl(c.getLeaveDeduction())); |
| | | return income.subtract(deduction).setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private static BigDecimal nvl(BigDecimal v) { |
| | | return v == null ? BigDecimal.ZERO : v; |
| | | } |
| | | |
| | | /** |
| | | * å
é¨çæåæ¾å°è´¦æ¹æ³ï¼ä¾èªå¨çæè°ç¨ï¼ |
| | | */ |