From d37c463e8dcdc72edf992f8c010b5c9037a3dceb Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期三, 14 十月 2026 15:52:50 +0800
Subject: [PATCH] feat(hrm): 社保公积金方案支持按部门绑定,修比例输入精度
---
sql/mysql/hrm_social_security_scheme_dept.sql | 43 +++
docs/hrm_social_security_scheme_dept_frontend_integration.md | 93 ++++++++
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/HrmSocialSecuritySchemeRespVO.java | 14 +
yudao-module-hrm-api/src/main/java/cn/iocoder/yudao/module/hrm/enums/ErrorCodeConstants.java | 1
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSocialSecuritySchemeController.java | 52 ++++
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/mysql/salary/HrmSocialSecuritySchemeDeptMapper.java | 58 +++++
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/salary/HrmSocialSecuritySchemeDO.java | 18 +
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSocialSecuritySchemeService.java | 45 +++
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/HrmSocialSecuritySchemeSaveReqVO.java | 11 +
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSocialSecuritySchemeServiceImpl.java | 157 +++++++++++++
docs/sql/config_export_all_20260904.sql | 18 +
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/salary/HrmSocialSecuritySchemeDeptDO.java | 42 +++
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSalaryCalculationServiceImpl.java | 64 ++++-
13 files changed, 580 insertions(+), 36 deletions(-)
diff --git a/docs/hrm_social_security_scheme_dept_frontend_integration.md b/docs/hrm_social_security_scheme_dept_frontend_integration.md
new file mode 100644
index 0000000..7308f33
--- /dev/null
+++ b/docs/hrm_social_security_scheme_dept_frontend_integration.md
@@ -0,0 +1,93 @@
+# 绀句繚鍏Н閲戞柟妗堟敼涓恒�屾寜閮ㄩ棬缁戝畾銆� - 鍓嶇鑱旇皟鏂规
+
+## 鍙樻洿鑳屾櫙
+
+鍘熸潵绀句繚鍏Н閲戞柟妗堝彧鑳戒竴涓憳宸ヤ竴涓憳宸ュ湴鍦ㄣ�屽憳宸ョぞ淇濆叕绉噾妗f銆嶉噷缁戝畾锛屽嚑鐧惧彿浜洪�愪釜缁存姢涓嶇幇瀹炪��
+鐜板湪鏂规鏈韩鍙互鎸傘�岄�傜敤閮ㄩ棬銆嶏紝钖祫鏍哥畻鏃舵寜鍛樺伐鎵�鍦ㄩ儴闂ㄥ彇鏂规锛涘師鏉ョ殑鍛樺伐妗f淇濈暀浣滀釜鍒鐩栥��
+
+## 娑夊強椤甸潰
+
+- 绀句繚鍏Н閲戞柟妗堬紙`/hrm/salary/social-security-scheme`锛夛細琛ㄥ崟鏂板銆岄�傜敤閮ㄩ棬銆嶃�岄粯璁ょぞ淇濆熀鏁般�嶃�岄粯璁ゅ叕绉噾鍩烘暟銆嶏紝鍒楄〃鏂板銆岄�傜敤閮ㄩ棬銆嶅垪
+- 钖祫鏍哥畻锛坄/hrm/salary/calculation`锛夛細鏃犻渶鏀归�狅紝鎺ュ彛鍏ュ弬鏈潵灏辨湁 `deptId`锛岃涓鸿嚜鍔ㄧ敓鏁�
+- 鍛樺伐绀句繚鍏Н閲戞。妗堬細涓嶆敼锛岀户缁綔涓轰釜鍒鐩栧叆鍙�
+
+## API
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| POST | /hrm/salary/social-security-scheme/create | 鏂板鏂规锛屽彲甯﹂�傜敤閮ㄩ棬涓庨粯璁ゅ熀鏁� |
+| PUT | /hrm/salary/social-security-scheme/update | 淇敼鏂规锛岄�傜敤閮ㄩ棬涓烘暣缁勬浛鎹紙浼犵┖鏁扮粍鍗宠В闄ゅ叏閮ㄩ儴闂級 |
+| GET | /hrm/salary/social-security-scheme/get | 璇︽儏锛岃繑鍥� `deptIds` / `deptNames` |
+| GET | /hrm/salary/social-security-scheme/list | 鍒楄〃锛岃繑鍥� `deptIds` / `deptNames` |
+| GET | /hrm/salary/calculation/preview | 璇曠畻锛屽叆鍙� `period` + 鍙�� `deptId` |
+
+**璇锋眰鍙傛暟鏂板锛坈reate / update锛夛細**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| deptIds | Long[] | 鍚� | 閫傜敤閮ㄩ棬ID鍒楄〃锛涚┖鏁扮粍鎴栦笉浼� = 涓嶉檺瀹氶儴闂紙鍏ㄥ眬榛樿鏂规锛� |
+| defaultSocialSecurityBase | Decimal | 鍚� | 榛樿绀句繚缂磋垂鍩烘暟锛涚暀绌哄垯鐢ㄥ憳宸ュ熀鏈伐璧� |
+| defaultHousingFundBase | Decimal | 鍚� | 榛樿鍏Н閲戠即璐瑰熀鏁帮紱鐣欑┖鎴� 0 鍒欑敤绀句繚鍩烘暟 |
+
+**鍝嶅簲瀛楁鏂板锛坓et / list锛夛細**
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| deptIds | Long[] | 閫傜敤閮ㄩ棬ID鍒楄〃锛屾湭缁戝畾杩斿洖 `[]` |
+| deptNames | String | 閫傜敤閮ㄩ棬鍚嶇О锛屽涓敤銆屻�併�嶈繛鎺ワ紱鏈粦瀹氳繑鍥炵┖涓层�傚悕绉拌В鏋愬け璐ユ椂鍥為�� `ID=xxx` |
+| defaultSocialSecurityBase | Decimal | 榛樿绀句繚缂磋垂鍩烘暟锛屽彲涓� null |
+| defaultHousingFundBase | Decimal | 榛樿鍏Н閲戠即璐瑰熀鏁帮紝鍙负 null |
+
+**鍝嶅簲绀轰緥锛�**
+
+```json
+{
+ "code": 0,
+ "data": {
+ "id": 3,
+ "name": "2026骞村畞澶忕ぞ淇濇爣鍑�",
+ "effectiveDate": "2026-01-01",
+ "deptIds": [119, 129],
+ "deptNames": "鐢熶骇閮ㄣ�佷粨鍌ㄩ儴",
+ "defaultSocialSecurityBase": 5023.00,
+ "defaultHousingFundBase": 0.00,
+ "status": 0
+ }
+}
+```
+
+**閮ㄩ棬鍐茬獊鎶ラ敊锛坈ode 1030005027锛夛細**
+
+```json
+{
+ "code": 1030005027,
+ "data": null,
+ "msg": "閮ㄩ棬銆愮敓浜ч儴銆戝凡缁戝畾鏂规銆�2025骞村畞澶忕ぞ淇濇爣鍑嗐�戯紝鏃犳硶鍐嶇粦瀹氬埌鏂规銆�2026骞村畞澶忕ぞ淇濇爣鍑嗐�戙�傝鍏堝湪鍘熸柟妗堜笂鍙栨秷璇ラ儴闂紝鎴栨敼閫夊叾瀹冮儴闂�"
+}
+```
+
+## 瀛楁灞曠ず瑙勫垯
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| 閫傜敤閮ㄩ棬 | 鏂规琛ㄥ崟锛堝閫夋爲锛� | 涓嶉�夎〃绀哄叏灞�榛樿鏂规锛宲laceholder 宸插啓鏄庛�屽彧瀵规墍閫夐儴闂ㄦ湰绾х殑鍛樺伐鐢熸晥銆� |
+| 閫傜敤閮ㄩ棬 | 鏂规鍒楄〃 | 鐩存帴灞曠ず鍚庣 `deptNames`锛涗负绌烘椂鏄剧ず銆屽叏灞�榛樿銆嶆爣绛� |
+| 榛樿绀句繚鍩烘暟 / 榛樿鍏Н閲戝熀鏁� | 鏂规琛ㄥ崟 | `InputNumber`锛宍min: 0`銆乣precision: 2`锛屽厑璁哥暀绌� |
+| 绀句繚/鍏Н閲戞墸娆� | 鏍哥畻鍒楄〃銆佽瘯绠楀脊绐� | 鏃犻渶鏀瑰姩锛屽悗绔寜鏂颁紭鍏堢骇鍙栨暟 |
+
+## 涓氬姟瑙勫垯璇存槑
+
+| 鍦烘櫙 | 瑙勫垯 |
+|------|------|
+| 姣斾緥鏉ユ簮浼樺厛绾� | 鈶� 鍛樺伐绀句繚妗f鎸囧畾鐨勬柟妗� 鈶� 鍛樺伐鎵�鍦ㄩ儴闂ㄧ粦瀹氱殑鍚敤鏂规锛堢敓鏁堟棩鏈� 鈮� 鏍哥畻鏈熼棿锛屽彇鏈�鏂帮級 鈶� 鏈粦瀹氫换浣曢儴闂ㄧ殑鍚敤鏂规涓敓鏁堟棩鏈熸渶鏂拌�咃紙鍏ㄥ眬榛樿锛� 鈶� 閮芥病鏈夊垯鐢ㄤ唬鐮侀粯璁ゆ瘮渚� |
+| 缂磋垂鍩烘暟浼樺厛绾� | 绀句繚鍩烘暟锛氭。妗� 鈫� 鏂规榛樿绀句繚鍩烘暟 鈫� 鍛樺伐鍩烘湰宸ヨ祫锛涘叕绉噾鍩烘暟锛氭。妗� 鈫� 鏂规榛樿鍏Н閲戝熀鏁帮紙>0 鎵嶇敤锛夆啋 绀句繚鍩烘暟 |
+| 閮ㄩ棬瑕嗙洊鑼冨洿 | 鍙鐩栨墍閫夐儴闂�**鏈骇**鐨勫憳宸ワ紝涓嶅惈瀛愰儴闂� |
+| 涓�涓儴闂ㄧ殑褰掑睘 | 涓�涓儴闂ㄥ悓鏃跺彧鑳界粦瀹氫竴涓柟妗堬紱淇濆瓨鏃惰嫢璇ラ儴闂ㄥ凡琚埆鐨勬柟妗堝崰鐢紝杩斿洖涓婇潰閭f潯绮剧‘鎶ラ敊 |
+| 鍒犻櫎鏂规 | 鏂规鍒犻櫎鏃跺叾閮ㄩ棬鍏宠仈涓�骞惰В闄わ紝閮ㄩ棬闅忎箣绌洪棽 |
+
+## 娉ㄦ剰浜嬮」
+
+- 鏂规鐨勩�岄�傜敤閮ㄩ棬銆嶆槸**鏁寸粍鏇挎崲**璇箟锛氱紪杈戞椂瑕佹妸褰撳墠缁戝畾鐨勯儴闂ㄥ叏閮ㄥ洖鏄惧埌澶氶�夋閲屽啀鎻愪氦锛屼笉鑳藉彧鎻愪氦鏈鏂板鐨勯儴闂ㄣ��
+- 瀛橀噺鏂规榛樿娌℃湁缁戝畾浠讳綍閮ㄩ棬锛屽洜姝ら兘鏄�屽叏灞�榛樿銆嶅�欓�夛紱姝ゆ椂鏍哥畻缁撴灉涓庢敼閫犲墠瀹屽叏涓�鑷达紙涓囬�� 23 鍚嶅憳宸ュ彛寰勫凡閫愯鏍稿涓�鑷达級锛岃鐢熸晥蹇呴』鍏堢粰鏂规濉�岄粯璁ょ即璐瑰熀鏁般�嶅苟鎶婃柟妗堢粦鍒伴儴闂ㄣ��
+- 鍛樺伐绀句繚鍏Н閲戞。妗堥噷宸叉寚瀹氭柟妗堢殑鍛樺伐锛屾案杩滀紭鍏堣蛋妗f锛屾柟妗堢粦瀹氶儴闂ㄤ笉浼氭敼鍙樹粬浠殑閲戦銆�
+- 姣斾緥杈撳叆妗嗚蛋鐨勬槸銆岀櫨鍒嗘瘮鏄剧ず銆佸皬鏁板瓨鍌ㄣ�嶏細鐣岄潰濉� 8.5 琛ㄧず 8.5%锛岃惤搴� 0.085锛涙樉绀轰繚鐣� 2 浣嶇櫨鍒嗘瘮灏忔暟锛屼笌鏁版嵁搴� `decimal(5,4)` 绮惧害涓�涓�瀵瑰簲銆備釜绋庣◣鐜囬厤缃〉鐨勮〃鍗曚笌鍒楄〃鍚屽彛寰勩��
diff --git a/docs/sql/config_export_all_20260904.sql b/docs/sql/config_export_all_20260904.sql
index 21b4863..38f3d3d 100644
--- a/docs/sql/config_export_all_20260904.sql
+++ b/docs/sql/config_export_all_20260904.sql
@@ -5525,6 +5525,8 @@
`maternity_ratio` decimal(5,4) NOT NULL DEFAULT '0.0050' COMMENT '鐢熻偛淇濋櫓浼佷笟缂寸撼姣斾緥',
`housing_fund_company_ratio` decimal(5,4) NOT NULL DEFAULT '0.1200' COMMENT '鍏Н閲戜紒涓氱即绾虫瘮渚�',
`housing_fund_personal_ratio` decimal(5,4) NOT NULL DEFAULT '0.1200' COMMENT '鍏Н閲戜釜浜虹即绾虫瘮渚�',
+ `default_social_security_base` decimal(12,2) DEFAULT NULL COMMENT '榛樿绀句繚缂磋垂鍩烘暟锛堢┖鍒欑敤鍛樺伐鍩烘湰宸ヨ祫锛�',
+ `default_housing_fund_base` decimal(12,2) DEFAULT NULL COMMENT '榛樿鍏Н閲戠即璐瑰熀鏁帮紙绌烘垨 0 鍒欑敤绀句繚缂磋垂鍩烘暟锛�',
`status` tinyint NOT NULL DEFAULT '0' COMMENT '鐘舵�侊細0-鍚敤锛�1-绂佺敤',
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '澶囨敞',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '鍒涘缓鑰�',
@@ -5536,6 +5538,22 @@
KEY `idx_effective_date` (`effective_date`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='HRM绀句繚鍏Н閲戞柟妗堣〃';
+-- ============ 琛細hrm_social_security_scheme_dept锛堜粎 DDL锛屾棤鏁版嵁锛� ============
+DROP TABLE IF EXISTS `hrm_social_security_scheme_dept`;
+CREATE TABLE `hrm_social_security_scheme_dept` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+ `scheme_id` bigint NOT NULL COMMENT '绀句繚鍏Н閲戞柟妗圛D',
+ `dept_id` bigint NOT NULL COMMENT '閫傜敤閮ㄩ棬ID',
+ `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '鍒涘缓鑰�',
+ `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+ `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci 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`) USING BTREE,
+ KEY `idx_scheme_id` (`scheme_id`) USING BTREE,
+ KEY `idx_dept_id` (`dept_id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='HRM绀句繚鍏Н閲戞柟妗堥�傜敤閮ㄩ棬琛�';
+
-- ============ 琛細hrm_tax_rate_config锛堜粎 DDL锛屾棤鏁版嵁锛� ============
DROP TABLE IF EXISTS `hrm_tax_rate_config`;
CREATE TABLE `hrm_tax_rate_config` (
diff --git a/sql/mysql/hrm_social_security_scheme_dept.sql b/sql/mysql/hrm_social_security_scheme_dept.sql
new file mode 100644
index 0000000..d67d522
--- /dev/null
+++ b/sql/mysql/hrm_social_security_scheme_dept.sql
@@ -0,0 +1,43 @@
+-- 绀句繚鍏Н閲戞柟妗堬細鏀逛负銆屾寜閮ㄩ棬缁戝畾銆�
+-- 1) 鏂规琛ㄥ鍔犻粯璁ょ即璐瑰熀鏁帮紙涓嶉�夐儴闂ㄧ殑鍛樺伐鎸夊畠璁$畻锛�
+-- 2) 鏂板銆屾柟妗� - 閫傜敤閮ㄩ棬銆嶅叧鑱旇〃锛堜竴涓柟妗堝彲缁戝畾澶氫釜閮ㄩ棬锛涗竴涓儴闂ㄥ彧鑳界粦瀹氫竴涓柟妗堬紝鍞竴鎬у湪鏈嶅姟灞傛牎楠岋級
+-- 璇存槑锛欰LTER TABLE ADD COLUMN 涓嶆敮鎸� IF NOT EXISTS锛岃繖閲屽厛鏌� information_schema 鍐嶅喅瀹氭槸鍚︽墽琛�
+
+SET @db := DATABASE();
+
+-- 1.1 榛樿绀句繚缂磋垂鍩烘暟
+SET @exists := (SELECT COUNT(*) FROM information_schema.columns
+ WHERE table_schema = @db AND table_name = 'hrm_social_security_scheme'
+ AND column_name = 'default_social_security_base');
+SET @sql := IF(@exists = 0,
+ 'ALTER TABLE `hrm_social_security_scheme` ADD COLUMN `default_social_security_base` decimal(12,2) DEFAULT NULL COMMENT ''榛樿绀句繚缂磋垂鍩烘暟锛堢┖鍒欑敤鍛樺伐鍩烘湰宸ヨ祫锛�'' AFTER `housing_fund_personal_ratio`',
+ 'SELECT 1');
+PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+-- 1.2 榛樿鍏Н閲戠即璐瑰熀鏁�
+SET @exists := (SELECT COUNT(*) FROM information_schema.columns
+ WHERE table_schema = @db AND table_name = 'hrm_social_security_scheme'
+ AND column_name = 'default_housing_fund_base');
+SET @sql := IF(@exists = 0,
+ 'ALTER TABLE `hrm_social_security_scheme` ADD COLUMN `default_housing_fund_base` decimal(12,2) DEFAULT NULL COMMENT ''榛樿鍏Н閲戠即璐瑰熀鏁帮紙绌烘垨 0 鍒欑敤绀句繚缂磋垂鍩烘暟锛�'' AFTER `default_social_security_base`',
+ 'SELECT 1');
+PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+-- 2. 鏂规 - 閫傜敤閮ㄩ棬鍏宠仈琛�
+CREATE TABLE IF NOT EXISTS `hrm_social_security_scheme_dept` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+ `scheme_id` bigint NOT NULL COMMENT '绀句繚鍏Н閲戞柟妗圛D',
+ `dept_id` bigint NOT NULL COMMENT '閫傜敤閮ㄩ棬ID',
+ `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`),
+ KEY `idx_scheme_id` (`scheme_id`),
+ KEY `idx_dept_id` (`dept_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '绀句繚鍏Н閲戞柟妗堥�傜敤閮ㄩ棬';
+
+-- 鍥炴粴锛堝闇�锛夛細
+-- ALTER TABLE `hrm_social_security_scheme` DROP COLUMN `default_social_security_base`, DROP COLUMN `default_housing_fund_base`;
+-- DROP TABLE IF EXISTS `hrm_social_security_scheme_dept`;
diff --git a/yudao-module-hrm-api/src/main/java/cn/iocoder/yudao/module/hrm/enums/ErrorCodeConstants.java b/yudao-module-hrm-api/src/main/java/cn/iocoder/yudao/module/hrm/enums/ErrorCodeConstants.java
index 2133c71..f8c7833 100644
--- a/yudao-module-hrm-api/src/main/java/cn/iocoder/yudao/module/hrm/enums/ErrorCodeConstants.java
+++ b/yudao-module-hrm-api/src/main/java/cn/iocoder/yudao/module/hrm/enums/ErrorCodeConstants.java
@@ -123,6 +123,7 @@
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, "钖叕缁撴瀯姝e湪琚憳宸ヤ娇鐢紝鏃犳硶鍒犻櫎");
+ ErrorCode SOCIAL_SECURITY_SCHEME_DEPT_EXISTS = new ErrorCode(1_030_005_027, "閮ㄩ棬銆恵}銆戝凡缁戝畾鏂规銆恵}銆戯紝鏃犳硶鍐嶇粦瀹氬埌鏂规銆恵}銆戙�傝鍏堝湪鍘熸柟妗堜笂鍙栨秷璇ラ儴闂紝鎴栨敼閫夊叾瀹冮儴闂�");
// ========== 鐝绠$悊 1-030-009-000 ==========
ErrorCode SHIFT_NOT_EXISTS = new ErrorCode(1_030_009_000, "鐝涓嶅瓨鍦�");
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSocialSecuritySchemeController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSocialSecuritySchemeController.java
index caf71d4..a766dcf 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSocialSecuritySchemeController.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSocialSecuritySchemeController.java
@@ -1,11 +1,14 @@
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.HrmSocialSecuritySchemeRespVO;
import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.HrmSocialSecuritySchemeSaveReqVO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.HrmSocialSecuritySchemeDO;
import cn.iocoder.yudao.module.hrm.service.salary.HrmSocialSecuritySchemeService;
+import cn.iocoder.yudao.module.system.api.dept.DeptApi;
+import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -15,7 +18,10 @@
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
+import java.util.Collections;
import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -28,20 +34,21 @@
@Resource
private HrmSocialSecuritySchemeService schemeService;
+ @Resource
+ private DeptApi deptApi;
+
@PostMapping("/create")
@Operation(summary = "鍒涘缓绀句繚鍏Н閲戞柟妗�")
@PreAuthorize("@ss.hasPermission('hrm:social-security:create')")
public CommonResult<Long> createScheme(@Valid @RequestBody HrmSocialSecuritySchemeSaveReqVO createReqVO) {
- HrmSocialSecuritySchemeDO scheme = BeanUtils.toBean(createReqVO, HrmSocialSecuritySchemeDO.class);
- return success(schemeService.createScheme(scheme));
+ return success(schemeService.createScheme(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "鏇存柊绀句繚鍏Н閲戞柟妗�")
@PreAuthorize("@ss.hasPermission('hrm:social-security:update')")
public CommonResult<Boolean> updateScheme(@Valid @RequestBody HrmSocialSecuritySchemeSaveReqVO updateReqVO) {
- HrmSocialSecuritySchemeDO scheme = BeanUtils.toBean(updateReqVO, HrmSocialSecuritySchemeDO.class);
- schemeService.updateScheme(scheme);
+ schemeService.updateScheme(updateReqVO);
return success(true);
}
@@ -60,7 +67,13 @@
@PreAuthorize("@ss.hasPermission('hrm:social-security:query')")
public CommonResult<HrmSocialSecuritySchemeRespVO> getScheme(@RequestParam("id") Long id) {
HrmSocialSecuritySchemeDO scheme = schemeService.getScheme(id);
- return success(BeanUtils.toBean(scheme, HrmSocialSecuritySchemeRespVO.class));
+ if (scheme == null) {
+ return success(null);
+ }
+ HrmSocialSecuritySchemeRespVO respVO = BeanUtils.toBean(scheme, HrmSocialSecuritySchemeRespVO.class);
+ // 濉厖閫傜敤閮ㄩ棬
+ fillDeptInfo(List.of(respVO));
+ return success(respVO);
}
@GetMapping("/list")
@@ -70,7 +83,34 @@
public CommonResult<List<HrmSocialSecuritySchemeRespVO>> getSchemeList(
@RequestParam(value = "status", required = false) Integer status) {
List<HrmSocialSecuritySchemeDO> list = schemeService.getSchemeList(status);
- return success(BeanUtils.toBean(list, HrmSocialSecuritySchemeRespVO.class));
+ List<HrmSocialSecuritySchemeRespVO> respList = BeanUtils.toBean(list, HrmSocialSecuritySchemeRespVO.class);
+ // 濉厖閫傜敤閮ㄩ棬
+ fillDeptInfo(respList);
+ return success(respList);
+ }
+
+ /**
+ * 濉厖鏂规鐨勯�傜敤閮ㄩ棬ID涓庨儴闂ㄥ悕绉�
+ */
+ private void fillDeptInfo(List<HrmSocialSecuritySchemeRespVO> list) {
+ if (CollUtil.isEmpty(list)) {
+ return;
+ }
+ List<Long> schemeIds = list.stream().map(HrmSocialSecuritySchemeRespVO::getId).toList();
+ Map<Long, List<Long>> schemeDeptIdMap = schemeService.getSchemeDeptIdMap(schemeIds);
+ List<Long> allDeptIds = schemeDeptIdMap.values().stream().flatMap(List::stream).distinct().toList();
+ Map<Long, DeptRespDTO> deptMap = CollUtil.isEmpty(allDeptIds)
+ ? Collections.emptyMap() : deptApi.getDeptMap(allDeptIds);
+ for (HrmSocialSecuritySchemeRespVO respVO : list) {
+ List<Long> deptIds = schemeDeptIdMap.getOrDefault(respVO.getId(), Collections.emptyList());
+ respVO.setDeptIds(deptIds);
+ respVO.setDeptNames(deptIds.stream()
+ .map(deptId -> {
+ DeptRespDTO dept = deptMap.get(deptId);
+ return dept != null ? dept.getName() : "ID=" + deptId;
+ })
+ .collect(Collectors.joining("銆�")));
+ }
}
}
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/HrmSocialSecuritySchemeRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/HrmSocialSecuritySchemeRespVO.java
index 9c2462d..314e101 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/HrmSocialSecuritySchemeRespVO.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/HrmSocialSecuritySchemeRespVO.java
@@ -6,6 +6,7 @@
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
+import java.util.List;
@Schema(description = "绠$悊鍚庡彴 - 绀句繚鍏Н閲戞柟妗� Response VO")
@Data
@@ -56,6 +57,19 @@
@Schema(description = "鍏Н閲戜釜浜虹即绾虫瘮渚�", example = "0.12")
private BigDecimal housingFundPersonalRatio;
+ // ========== 缂磋垂鍩烘暟 ==========
+ @Schema(description = "榛樿绀句繚缂磋垂鍩烘暟锛堢┖鍒欑敤鍛樺伐鍩烘湰宸ヨ祫锛�", example = "5023.00")
+ private BigDecimal defaultSocialSecurityBase;
+
+ @Schema(description = "榛樿鍏Н閲戠即璐瑰熀鏁帮紙绌烘垨 0 鍒欑敤绀句繚缂磋垂鍩烘暟锛�", example = "5023.00")
+ private BigDecimal defaultHousingFundBase;
+
+ @Schema(description = "閫傜敤閮ㄩ棬ID鍒楄〃锛堜负绌鸿〃绀哄叏灞�榛樿鏂规锛�", example = "[119,129]")
+ private List<Long> deptIds;
+
+ @Schema(description = "閫傜敤閮ㄩ棬鍚嶇О锛堝涓敤銆佸垎闅旓紝涓虹┖琛ㄧず鍏ㄥ眬榛樿锛�", example = "鐢熶骇閮ㄣ�佷粨鍌ㄩ儴")
+ private String deptNames;
+
@Schema(description = "鐘舵�侊細0-鍚敤锛�1-绂佺敤", example = "0")
private Integer status;
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/HrmSocialSecuritySchemeSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/HrmSocialSecuritySchemeSaveReqVO.java
index 08099ab..0948a92 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/HrmSocialSecuritySchemeSaveReqVO.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/HrmSocialSecuritySchemeSaveReqVO.java
@@ -7,6 +7,7 @@
import java.math.BigDecimal;
import java.time.LocalDate;
+import java.util.List;
@Schema(description = "绠$悊鍚庡彴 - 绀句繚鍏Н閲戞柟妗堟柊澧�/淇敼 Request VO")
@Data
@@ -59,6 +60,16 @@
@Schema(description = "鍏Н閲戜釜浜虹即绾虫瘮渚�", example = "0.12")
private BigDecimal housingFundPersonalRatio;
+ // ========== 缂磋垂鍩烘暟 ==========
+ @Schema(description = "榛樿绀句繚缂磋垂鍩烘暟锛堢┖鍒欑敤鍛樺伐鍩烘湰宸ヨ祫锛�", example = "5023.00")
+ private BigDecimal defaultSocialSecurityBase;
+
+ @Schema(description = "榛樿鍏Н閲戠即璐瑰熀鏁帮紙绌烘垨 0 鍒欑敤绀句繚缂磋垂鍩烘暟锛�", example = "5023.00")
+ private BigDecimal defaultHousingFundBase;
+
+ @Schema(description = "閫傜敤閮ㄩ棬ID鍒楄〃锛堜负绌鸿〃绀哄叏灞�榛樿鏂规锛�", example = "[119,129]")
+ private List<Long> deptIds;
+
@Schema(description = "鐘舵�侊細0-鍚敤锛�1-绂佺敤", example = "0")
private Integer status;
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/salary/HrmSocialSecuritySchemeDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/salary/HrmSocialSecuritySchemeDO.java
index 1962eeb..4744a71 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/salary/HrmSocialSecuritySchemeDO.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/salary/HrmSocialSecuritySchemeDO.java
@@ -1,7 +1,9 @@
package cn.iocoder.yudao.module.hrm.dal.dataobject.salary;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
@@ -94,6 +96,22 @@
*/
private BigDecimal housingFundPersonalRatio;
+ // ========== 缂磋垂鍩烘暟 ==========
+ /**
+ * 榛樿绀句繚缂磋垂鍩烘暟锛堢┖鍒欑敤鍛樺伐鍩烘湰宸ヨ祫锛�
+ *
+ * updateStrategy=ALWAYS锛氭竻绌鸿瀛楁鏃惰鑳藉啓鍥� NULL
+ */
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private BigDecimal defaultSocialSecurityBase;
+ /**
+ * 榛樿鍏Н閲戠即璐瑰熀鏁帮紙绌烘垨 0 鍒欑敤绀句繚缂磋垂鍩烘暟锛�
+ *
+ * updateStrategy 鍚� {@link #defaultSocialSecurityBase}
+ */
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private BigDecimal defaultHousingFundBase;
+
/**
* 鐘舵�侊細0-鍚敤锛�1-绂佺敤
*/
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/salary/HrmSocialSecuritySchemeDeptDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/salary/HrmSocialSecuritySchemeDeptDO.java
new file mode 100644
index 0000000..fbca5b0
--- /dev/null
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/salary/HrmSocialSecuritySchemeDeptDO.java
@@ -0,0 +1,42 @@
+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.*;
+
+/**
+ * 绀句繚鍏Н閲戞柟妗堥�傜敤閮ㄩ棬 DO
+ *
+ * 涓�涓柟妗堝彲缁戝畾澶氫釜閮ㄩ棬锛涗竴涓儴闂ㄥ彧鑳界粦瀹氫竴涓柟妗堬紙鍦� Service 灞傛牎楠岋級
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@TableName("hrm_social_security_scheme_dept")
+@KeySequence("hrm_social_security_scheme_dept_seq")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class HrmSocialSecuritySchemeDeptDO extends BaseDO {
+
+ /**
+ * 涓婚敭
+ */
+ @TableId
+ private Long id;
+
+ /**
+ * 绀句繚鍏Н閲戞柟妗圛D
+ */
+ private Long schemeId;
+
+ /**
+ * 閫傜敤閮ㄩ棬ID
+ */
+ private Long deptId;
+
+}
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/mysql/salary/HrmSocialSecuritySchemeDeptMapper.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/mysql/salary/HrmSocialSecuritySchemeDeptMapper.java
new file mode 100644
index 0000000..c0d61b0
--- /dev/null
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/mysql/salary/HrmSocialSecuritySchemeDeptMapper.java
@@ -0,0 +1,58 @@
+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.HrmSocialSecuritySchemeDeptDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * 绀句繚鍏Н閲戞柟妗堥�傜敤閮ㄩ棬 Mapper
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Mapper
+public interface HrmSocialSecuritySchemeDeptMapper extends BaseMapperX<HrmSocialSecuritySchemeDeptDO> {
+
+ /**
+ * 鎸夋柟妗堟暣缁勬浛鎹細鍏堝垹闄よ鏂规鐨勫叏閮紙閫昏緫鍒犻櫎锛夊叧鑱旇
+ */
+ default void deleteBySchemeId(Long schemeId) {
+ delete(new LambdaQueryWrapperX<HrmSocialSecuritySchemeDeptDO>()
+ .eq(HrmSocialSecuritySchemeDeptDO::getSchemeId, schemeId));
+ }
+
+ default List<HrmSocialSecuritySchemeDeptDO> selectListBySchemeId(Long schemeId) {
+ return selectList(new LambdaQueryWrapperX<HrmSocialSecuritySchemeDeptDO>()
+ .eq(HrmSocialSecuritySchemeDeptDO::getSchemeId, schemeId));
+ }
+
+ default List<HrmSocialSecuritySchemeDeptDO> selectListBySchemeIds(Collection<Long> schemeIds) {
+ return selectList(new LambdaQueryWrapperX<HrmSocialSecuritySchemeDeptDO>()
+ .inIfPresent(HrmSocialSecuritySchemeDeptDO::getSchemeId, schemeIds));
+ }
+
+ default List<HrmSocialSecuritySchemeDeptDO> selectListByDeptIds(Collection<Long> deptIds) {
+ return selectList(new LambdaQueryWrapperX<HrmSocialSecuritySchemeDeptDO>()
+ .inIfPresent(HrmSocialSecuritySchemeDeptDO::getDeptId, deptIds));
+ }
+
+ default List<HrmSocialSecuritySchemeDeptDO> selectListByDeptId(Long deptId) {
+ return selectList(new LambdaQueryWrapperX<HrmSocialSecuritySchemeDeptDO>()
+ .eq(HrmSocialSecuritySchemeDeptDO::getDeptId, deptId));
+ }
+
+ /**
+ * 鏌ヨ鍏ㄩ儴宸茬粦瀹氶儴闂ㄧ殑鏂规ID锛堢敤浜庡尯鍒嗐�岄儴闂ㄤ笓灞炴柟妗堛�嶄笌銆屽叏灞�榛樿鏂规銆嶏級
+ */
+ default Set<Long> selectAllBoundSchemeIds() {
+ return selectObjs(new LambdaQueryWrapperX<HrmSocialSecuritySchemeDeptDO>()
+ .select(HrmSocialSecuritySchemeDeptDO::getSchemeId))
+ .stream().map(obj -> (Long) obj).collect(Collectors.toSet());
+ }
+
+}
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSalaryCalculationServiceImpl.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSalaryCalculationServiceImpl.java
index 6cf8b19..c2b563e 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSalaryCalculationServiceImpl.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSalaryCalculationServiceImpl.java
@@ -44,6 +44,7 @@
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -85,6 +86,8 @@
private HrmEmployeeSocialSecurityMapper employeeSocialSecurityMapper;
@Resource
private HrmSocialSecuritySchemeMapper socialSecuritySchemeMapper;
+ @Resource
+ private HrmSocialSecuritySchemeService socialSecuritySchemeService;
@Resource
private HrmNoRedisDAO noRedisDAO;
@Resource
@@ -140,6 +143,8 @@
.toList();
// 2. 閬嶅巻鍛樺伐杩涜钖叕鏍哥畻
+ // 閮ㄩ棬 -> 鏂规 鐨勮В鏋愮粨鏋滃湪鏈鏍哥畻鍐呯紦瀛橈紝閬垮厤閫愬憳宸ラ噸澶嶆煡搴�
+ Map<Long, HrmSocialSecuritySchemeDO> deptSchemeCache = new HashMap<>();
for (HrmEmployeeDO employee : employees) {
// employeeId 鏄憳宸D锛岀敤浜庡叧鑱旇柂閰。妗堛�佺ぞ淇濇。妗堛�佹牳绠楄褰�
Long employeeId = employee.getId();
@@ -165,11 +170,8 @@
// 2.3 鑾峰彇鍛樺伐绀句繚鍏Н閲戞。妗堬紙鎸夊憳宸D鏌ヨ锛屽彇鏈�鏂扮敓鏁堢殑锛�
HrmEmployeeSocialSecurityDO socialSecurity = employeeSocialSecurityMapper.selectLatestByUserId(employeeId);
- // 2.4 濡傛灉鏈夌ぞ淇濇。妗堬紝鑾峰彇缁戝畾鐨勬柟妗�
- HrmSocialSecuritySchemeDO scheme = null;
- if (socialSecurity != null && socialSecurity.getSocialSecuritySchemeId() != null) {
- scheme = socialSecuritySchemeMapper.selectById(socialSecurity.getSocialSecuritySchemeId());
- }
+ // 2.4 鑾峰彇鏂规锛氬憳宸ユ。妗堟寚瀹� > 鎵�鍦ㄩ儴闂ㄧ粦瀹� > 鍏ㄥ眬榛樿
+ HrmSocialSecuritySchemeDO scheme = resolveScheme(socialSecurity, employee, effectiveDate, deptSchemeCache);
// 2.5 鏋勫缓鏍哥畻璁板綍锛堝寘鍚墸闄よ绠楋紝閫氳繃鏂规鑾峰彇姣斾緥锛�
HrmSalaryCalculationDO calculation = buildSalaryCalculation(period, employeeId, employee, employeeSalary, socialSecurity, scheme, effectiveDate);
@@ -245,13 +247,22 @@
BigDecimal totalIncome = baseSalary.add(performanceSalary).add(mealAllowance)
.add(transportAllowance).add(otherAllowance).add(overtimePay);
- // 鑾峰彇绀句繚鍩烘暟锛氬憳宸ョぞ淇濇。妗� > 鍛樺伐鍩烘湰宸ヨ祫
+ // 鑾峰彇绀句繚鍩烘暟锛氬憳宸ョぞ淇濇。妗� > 鏂规榛樿缂磋垂鍩烘暟 > 鍛樺伐鍩烘湰宸ヨ祫
BigDecimal socialSecurityBase = (socialSecurity != null && socialSecurity.getSocialSecurityBase() != null)
- ? socialSecurity.getSocialSecurityBase() : baseSalary;
+ ? socialSecurity.getSocialSecurityBase()
+ : (scheme != null && scheme.getDefaultSocialSecurityBase() != null
+ ? scheme.getDefaultSocialSecurityBase() : baseSalary);
- // 鑾峰彇鍏Н閲戝熀鏁帮細鍛樺伐绀句繚妗f > 鍛樺伐鍩烘湰宸ヨ祫
- BigDecimal housingFundBase = (socialSecurity != null && socialSecurity.getHousingFundBase() != null)
- ? socialSecurity.getHousingFundBase() : baseSalary;
+ // 鑾峰彇鍏Н閲戝熀鏁帮細鍛樺伐绀句繚妗f > 鏂规榛樿鍏Н閲戝熀鏁帮紙澶т簬 0 鏃讹級> 绀句繚鍩烘暟
+ BigDecimal housingFundBase;
+ if (socialSecurity != null && socialSecurity.getHousingFundBase() != null) {
+ housingFundBase = socialSecurity.getHousingFundBase();
+ } else if (scheme != null && scheme.getDefaultHousingFundBase() != null
+ && scheme.getDefaultHousingFundBase().compareTo(BigDecimal.ZERO) > 0) {
+ housingFundBase = scheme.getDefaultHousingFundBase();
+ } else {
+ housingFundBase = socialSecurityBase;
+ }
// 浠庢柟妗堜腑鑾峰彇缂寸撼姣斾緥锛堜紭鍏堜娇鐢ㄦ柟妗堜腑鐨勬瘮渚嬶紝鍚﹀垯浣跨敤榛樿姣斾緥锛�
BigDecimal pensionPersonalRatio = (scheme != null && scheme.getPensionPersonalRatio() != null)
@@ -316,6 +327,30 @@
.totalWorkHours(totalWorkHours)
.status(0) // 寰呯‘璁�
.build();
+ }
+
+ /**
+ * 瑙f瀽鍛樺伐鏍哥畻鐢ㄧ殑绀句繚鍏Н閲戞柟妗�
+ *
+ * 浼樺厛绾э細鍛樺伐绀句繚妗f鎸囧畾鐨勬柟妗堬紙涓埆瑕嗙洊锛�> 鍛樺伐鎵�鍦ㄩ儴闂ㄧ粦瀹氱殑鏂规 > 鍏ㄥ眬榛樿鏂规銆�
+ * 閮藉彇涓嶅埌鏃惰繑鍥� null锛岀敱 {@link #buildSalaryCalculation} 浣跨敤榛樿姣斾緥甯搁噺銆�
+ *
+ * @param deptSchemeCache 鏈鏍哥畻鍐呫�岄儴闂� -> 鏂规銆嶇殑缂撳瓨锛堝厑璁� null 鍊硷紝鐢ㄤ簬缂撳瓨"璇ラ儴闂ㄦ棤鏂规"锛�
+ */
+ private HrmSocialSecuritySchemeDO resolveScheme(HrmEmployeeSocialSecurityDO socialSecurity, HrmEmployeeDO employee,
+ LocalDate periodDate,
+ Map<Long, HrmSocialSecuritySchemeDO> deptSchemeCache) {
+ if (socialSecurity != null && socialSecurity.getSocialSecuritySchemeId() != null) {
+ HrmSocialSecuritySchemeDO scheme = socialSecuritySchemeMapper.selectById(socialSecurity.getSocialSecuritySchemeId());
+ if (scheme != null) {
+ return scheme;
+ }
+ }
+ Long deptId = employee.getDeptId();
+ if (!deptSchemeCache.containsKey(deptId)) {
+ deptSchemeCache.put(deptId, socialSecuritySchemeService.resolveSchemeByDeptId(deptId, periodDate));
+ }
+ return deptSchemeCache.get(deptId);
}
/**
@@ -531,6 +566,8 @@
// 2. 閬嶅巻鍛樺伐瀹炴椂璁$畻钖叕
List<HrmSalaryCalculationDO> resultList = new java.util.ArrayList<>();
+ // 閮ㄩ棬 -> 鏂规 鐨勮В鏋愮粨鏋滃湪鏈棰勮鍐呯紦瀛橈紝閬垮厤閫愬憳宸ラ噸澶嶆煡搴�
+ Map<Long, HrmSocialSecuritySchemeDO> deptSchemeCache = new HashMap<>();
for (HrmEmployeeDO employee : employees) {
Long employeeId = employee.getId();
@@ -543,11 +580,8 @@
// 2.2 鑾峰彇鍛樺伐绀句繚鍏Н閲戞。妗堬紙鎸夊憳宸D鏌ヨ锛屽彇鏈�鏂扮敓鏁堢殑锛�
HrmEmployeeSocialSecurityDO socialSecurity = employeeSocialSecurityMapper.selectLatestByUserId(employeeId);
- // 2.3 濡傛灉鏈夌ぞ淇濇。妗堬紝鑾峰彇缁戝畾鐨勬柟妗�
- HrmSocialSecuritySchemeDO scheme = null;
- if (socialSecurity != null && socialSecurity.getSocialSecuritySchemeId() != null) {
- scheme = socialSecuritySchemeMapper.selectById(socialSecurity.getSocialSecuritySchemeId());
- }
+ // 2.3 鑾峰彇鏂规锛氬憳宸ユ。妗堟寚瀹� > 鎵�鍦ㄩ儴闂ㄧ粦瀹� > 鍏ㄥ眬榛樿
+ HrmSocialSecuritySchemeDO scheme = resolveScheme(socialSecurity, employee, effectiveDate, deptSchemeCache);
// 2.4 鏋勫缓鏍哥畻璁板綍锛堝疄鏃惰绠楋紝涓嶅瓨鍌級
HrmSalaryCalculationDO calculation = buildSalaryCalculation(period, employeeId, employee, employeeSalary, socialSecurity, scheme, effectiveDate);
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSocialSecuritySchemeService.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSocialSecuritySchemeService.java
index 51a52e0..859e3b0 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSocialSecuritySchemeService.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSocialSecuritySchemeService.java
@@ -1,8 +1,12 @@
package cn.iocoder.yudao.module.hrm.service.salary;
+import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.HrmSocialSecuritySchemeSaveReqVO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.HrmSocialSecuritySchemeDO;
+import java.time.LocalDate;
+import java.util.Collection;
import java.util.List;
+import java.util.Map;
/**
* 绀句繚鍏Н閲戞柟妗� Service 鎺ュ彛
@@ -12,19 +16,19 @@
public interface HrmSocialSecuritySchemeService {
/**
- * 鍒涘缓绀句繚鍏Н閲戞柟妗�
+ * 鍒涘缓绀句繚鍏Н閲戞柟妗堬紙鍚�傜敤閮ㄩ棬锛�
*
- * @param scheme 鏂规淇℃伅
+ * @param createReqVO 鏂规淇℃伅
* @return 鏂规ID
*/
- Long createScheme(HrmSocialSecuritySchemeDO scheme);
+ Long createScheme(HrmSocialSecuritySchemeSaveReqVO createReqVO);
/**
- * 鏇存柊绀句繚鍏Н閲戞柟妗�
+ * 鏇存柊绀句繚鍏Н閲戞柟妗堬紙鍚�傜敤閮ㄩ棬锛屾暣缁勬浛鎹級
*
- * @param scheme 鏂规淇℃伅
+ * @param updateReqVO 鏂规淇℃伅
*/
- void updateScheme(HrmSocialSecuritySchemeDO scheme);
+ void updateScheme(HrmSocialSecuritySchemeSaveReqVO updateReqVO);
/**
* 鍒犻櫎绀句繚鍏Н閲戞柟妗�
@@ -49,4 +53,31 @@
*/
List<HrmSocialSecuritySchemeDO> getSchemeList(Integer status);
-}
\ No newline at end of file
+ /**
+ * 鑾峰彇鏂规缁戝畾鐨勯�傜敤閮ㄩ棬ID鍒楄〃
+ *
+ * @param schemeId 鏂规ID
+ * @return 閮ㄩ棬ID鍒楄〃
+ */
+ List<Long> getSchemeDeptIds(Long schemeId);
+
+ /**
+ * 鎵归噺鑾峰彇鏂规缁戝畾鐨勯�傜敤閮ㄩ棬ID锛堟柟妗圛D -> 閮ㄩ棬ID鍒楄〃锛�
+ *
+ * @param schemeIds 鏂规ID闆嗗悎
+ * @return 鏂规ID -> 閮ㄩ棬ID鍒楄〃
+ */
+ Map<Long, List<Long>> getSchemeDeptIdMap(Collection<Long> schemeIds);
+
+ /**
+ * 鎸夐儴闂ㄨВ鏋愭牳绠楃敤鐨勭ぞ淇濆叕绉噾鏂规
+ *
+ * 浼樺厛绾э細璇ラ儴闂ㄧ粦瀹氱殑鏂规锛堢敓鏁堟棩鏈熶笉鏅氫簬鏍哥畻鏈熼棿锛屽彇鏈�鏂帮級> 鍏ㄥ眬榛樿鏂规锛堟湭缁戝畾浠讳綍閮ㄩ棬鐨勬柟妗堜腑鐢熸晥鏃ユ湡鏈�鏂拌�咃級
+ *
+ * @param deptId 鍛樺伐鎵�鍦ㄩ儴闂↖D锛堝彲涓虹┖锛�
+ * @param periodDate 鏍哥畻鏈熼棿鐨勬湀鍒濇棩鏈�
+ * @return 鏂规锛涙煡涓嶅埌杩斿洖 null锛岀敱璋冪敤鏂逛娇鐢ㄩ粯璁ゆ瘮渚嬪父閲�
+ */
+ HrmSocialSecuritySchemeDO resolveSchemeByDeptId(Long deptId, LocalDate periodDate);
+
+}
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSocialSecuritySchemeServiceImpl.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSocialSecuritySchemeServiceImpl.java
index 142653a..5be274f 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSocialSecuritySchemeServiceImpl.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSocialSecuritySchemeServiceImpl.java
@@ -1,18 +1,36 @@
package cn.iocoder.yudao.module.hrm.service.salary;
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.HrmSocialSecuritySchemeSaveReqVO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.HrmSocialSecuritySchemeDO;
+import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.HrmSocialSecuritySchemeDeptDO;
+import cn.iocoder.yudao.module.hrm.dal.mysql.salary.HrmSocialSecuritySchemeDeptMapper;
import cn.iocoder.yudao.module.hrm.dal.mysql.salary.HrmSocialSecuritySchemeMapper;
+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;
import org.springframework.validation.annotation.Validated;
+import java.time.LocalDate;
+import java.util.Collection;
import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.SOCIAL_SECURITY_SCHEME_DEPT_EXISTS;
import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.SOCIAL_SECURITY_SCHEME_NOT_EXISTS;
/**
* 绀句繚鍏Н閲戞柟妗� Service 瀹炵幇绫�
+ *
+ * 鏂规鐨勭即绾虫瘮渚嬫寜銆屾柟妗� - 閫傜敤閮ㄩ棬銆嶅叧鑱旇〃鐢熸晥锛涗竴涓儴闂ㄥ彧鑳界粦瀹氫竴涓柟妗堬紝鏈粦瀹氫换浣曢儴闂ㄧ殑鏂规浣滀负鍏ㄥ眬榛樿鏂规銆�
*
* @author 瓒呯骇绠$悊鍛�
*/
@@ -20,24 +38,42 @@
@Validated
public class HrmSocialSecuritySchemeServiceImpl implements HrmSocialSecuritySchemeService {
+ /**
+ * 鍚敤鐘舵��
+ */
+ private static final Integer STATUS_ENABLE = 0;
+
@Resource
private HrmSocialSecuritySchemeMapper schemeMapper;
+ @Resource
+ private HrmSocialSecuritySchemeDeptMapper schemeDeptMapper;
+
+ @Resource
+ private DeptApi deptApi;
+
@Override
- public Long createScheme(HrmSocialSecuritySchemeDO scheme) {
+ @Transactional(rollbackFor = Exception.class)
+ public Long createScheme(HrmSocialSecuritySchemeSaveReqVO createReqVO) {
+ HrmSocialSecuritySchemeDO scheme = BeanUtils.toBean(createReqVO, HrmSocialSecuritySchemeDO.class);
schemeMapper.insert(scheme);
+ saveSchemeDepts(scheme.getId(), createReqVO.getName(), createReqVO.getDeptIds());
return scheme.getId();
}
@Override
- public void updateScheme(HrmSocialSecuritySchemeDO scheme) {
- validateSchemeExists(scheme.getId());
- schemeMapper.updateById(scheme);
+ @Transactional(rollbackFor = Exception.class)
+ public void updateScheme(HrmSocialSecuritySchemeSaveReqVO updateReqVO) {
+ validateSchemeExists(updateReqVO.getId());
+ schemeMapper.updateById(BeanUtils.toBean(updateReqVO, HrmSocialSecuritySchemeDO.class));
+ saveSchemeDepts(updateReqVO.getId(), updateReqVO.getName(), updateReqVO.getDeptIds());
}
@Override
+ @Transactional(rollbackFor = Exception.class)
public void deleteScheme(Long id) {
validateSchemeExists(id);
+ schemeDeptMapper.deleteBySchemeId(id);
schemeMapper.deleteById(id);
}
@@ -51,10 +87,115 @@
return schemeMapper.selectListByStatus(status);
}
- private void validateSchemeExists(Long id) {
- if (schemeMapper.selectById(id) == null) {
- throw exception(SOCIAL_SECURITY_SCHEME_NOT_EXISTS);
+ @Override
+ public List<Long> getSchemeDeptIds(Long schemeId) {
+ return schemeDeptMapper.selectListBySchemeId(schemeId).stream()
+ .map(HrmSocialSecuritySchemeDeptDO::getDeptId)
+ .toList();
+ }
+
+ @Override
+ public Map<Long, List<Long>> getSchemeDeptIdMap(Collection<Long> schemeIds) {
+ if (CollUtil.isEmpty(schemeIds)) {
+ return Map.of();
+ }
+ return schemeDeptMapper.selectListBySchemeIds(schemeIds).stream()
+ .collect(Collectors.groupingBy(HrmSocialSecuritySchemeDeptDO::getSchemeId,
+ Collectors.mapping(HrmSocialSecuritySchemeDeptDO::getDeptId, Collectors.toList())));
+ }
+
+ @Override
+ public HrmSocialSecuritySchemeDO resolveSchemeByDeptId(Long deptId, LocalDate periodDate) {
+ // 1. 浼樺厛鍙栬閮ㄩ棬缁戝畾鐨勬柟妗�
+ if (deptId != null) {
+ List<Long> boundSchemeIds = schemeDeptMapper.selectListByDeptId(deptId).stream()
+ .map(HrmSocialSecuritySchemeDeptDO::getSchemeId)
+ .toList();
+ HrmSocialSecuritySchemeDO scheme = pickLatestEnabled(boundSchemeIds, periodDate);
+ if (scheme != null) {
+ return scheme;
+ }
+ }
+ // 2. 鍏滃簳鍙栧叏灞�榛樿鏂规锛氭湭缁戝畾浠讳綍閮ㄩ棬鐨勬柟妗堜腑鐢熸晥鏃ユ湡鏈�鏂拌��
+ Set<Long> boundSchemeIds = schemeDeptMapper.selectAllBoundSchemeIds();
+ List<Long> globalSchemeIds = schemeMapper.selectListByStatus(STATUS_ENABLE).stream()
+ .map(HrmSocialSecuritySchemeDO::getId)
+ .filter(id -> !boundSchemeIds.contains(id))
+ .toList();
+ return pickLatestEnabled(globalSchemeIds, periodDate);
+ }
+
+ /**
+ * 浠庡�欓�夋柟妗堜腑鍙栥�岀敓鏁堟棩鏈熶笉鏅氫簬鏍哥畻鏈熼棿銆嶇殑鏈�鏂颁竴鏉″惎鐢ㄦ柟妗�
+ *
+ * 鑻ュ叏閮ㄥ�欓�夋柟妗堥兘鏅氫簬鏍哥畻鏈熼棿锛堜緥濡傚垰寤哄ソ涓嬫湀鎵嶇敓鏁堢殑鏂规灏辨彁鍓嶆牳绠楋級锛屽垯閫�鍖栦负鍙栧叾涓敓鏁堟棩鏈熸渶鏂扮殑涓�鏉★紝
+ * 閬垮厤鏍哥畻閲戦闈欓粯钀藉埌浠g爜閲岀殑榛樿姣斾緥涓娿��
+ */
+ private HrmSocialSecuritySchemeDO pickLatestEnabled(Collection<Long> schemeIds, LocalDate periodDate) {
+ if (CollUtil.isEmpty(schemeIds)) {
+ return null;
+ }
+ List<HrmSocialSecuritySchemeDO> schemes = schemeMapper.selectList(
+ new LambdaQueryWrapperX<HrmSocialSecuritySchemeDO>()
+ .in(HrmSocialSecuritySchemeDO::getId, schemeIds)
+ .eq(HrmSocialSecuritySchemeDO::getStatus, STATUS_ENABLE)
+ .orderByDesc(HrmSocialSecuritySchemeDO::getEffectiveDate)
+ .orderByDesc(HrmSocialSecuritySchemeDO::getId));
+ if (CollUtil.isEmpty(schemes)) {
+ return null;
+ }
+ return schemes.stream()
+ .filter(scheme -> scheme.getEffectiveDate() == null
+ || !scheme.getEffectiveDate().isAfter(periodDate))
+ .findFirst()
+ .orElse(schemes.get(0));
+ }
+
+ /**
+ * 鏁寸粍鏇挎崲鏂规鐨勯�傜敤閮ㄩ棬锛氬厛鏍¢獙锛屽啀鍒犳棫琛屻�佹彃鏂拌
+ */
+ private void saveSchemeDepts(Long schemeId, String schemeName, List<Long> deptIds) {
+ List<Long> distinctDeptIds = CollUtil.isEmpty(deptIds) ? List.of()
+ : deptIds.stream().filter(Objects::nonNull).distinct().toList();
+ if (CollUtil.isNotEmpty(distinctDeptIds)) {
+ // 閮ㄩ棬蹇呴』瀛樺湪
+ deptApi.validateDeptList(distinctDeptIds);
+ // 涓�涓儴闂ㄥ彧鑳界粦瀹氫竴涓柟妗�
+ validateDeptNotBoundByOtherScheme(schemeId, schemeName, distinctDeptIds);
+ }
+ schemeDeptMapper.deleteBySchemeId(schemeId);
+ distinctDeptIds.forEach(deptId -> schemeDeptMapper.insert(
+ HrmSocialSecuritySchemeDeptDO.builder().schemeId(schemeId).deptId(deptId).build()));
+ }
+
+ /**
+ * 鏍¢獙杩欎簺閮ㄩ棬娌℃湁琚埆鐨勬柟妗堝崰鐢�
+ */
+ private void validateDeptNotBoundByOtherScheme(Long schemeId, String schemeName, List<Long> deptIds) {
+ List<HrmSocialSecuritySchemeDeptDO> bindings = schemeDeptMapper.selectListByDeptIds(deptIds);
+ if (CollUtil.isEmpty(bindings)) {
+ return;
+ }
+ // 鍚嶇О瑙f瀽鍙斁鍦ㄥ紓甯稿垎鏀紝閬垮厤姝e父淇濆瓨澶氭煡琛�
+ for (HrmSocialSecuritySchemeDeptDO binding : bindings) {
+ if (Objects.equals(binding.getSchemeId(), schemeId)) {
+ continue;
+ }
+ HrmSocialSecuritySchemeDO otherScheme = schemeMapper.selectById(binding.getSchemeId());
+ DeptRespDTO dept = deptApi.getDept(binding.getDeptId());
+ throw exception(SOCIAL_SECURITY_SCHEME_DEPT_EXISTS,
+ dept != null ? dept.getName() : "ID=" + binding.getDeptId(),
+ otherScheme != null ? otherScheme.getName() : "ID=" + binding.getSchemeId(),
+ schemeName);
}
}
-}
\ No newline at end of file
+ private HrmSocialSecuritySchemeDO validateSchemeExists(Long id) {
+ HrmSocialSecuritySchemeDO scheme = schemeMapper.selectById(id);
+ if (scheme == null) {
+ throw exception(SOCIAL_SECURITY_SCHEME_NOT_EXISTS);
+ }
+ return scheme;
+ }
+
+}
--
Gitblit v1.9.3