From 571ccc18671ef45c6403496e8d99efec82168083 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期五, 26 六月 2026 10:21:02 +0800
Subject: [PATCH] 1.人员排班同步请假信息 2.人员排班同步调休信息
---
docs/personal_shift_holiday_linkage.md | 142 +++++++++++++++++++++++
src/main/java/com/ruoyi/staff/dto/PerformanceShiftMapDto.java | 5
src/main/java/com/ruoyi/staff/mapper/PersonalShiftMapper.java | 7 +
src/main/java/com/ruoyi/staff/service/impl/PersonalShiftServiceImpl.java | 143 +++++++++++++++++++++--
src/main/resources/mapper/staff/PersonalShiftMapper.xml | 18 +++
5 files changed, 298 insertions(+), 17 deletions(-)
diff --git a/docs/personal_shift_holiday_linkage.md b/docs/personal_shift_holiday_linkage.md
new file mode 100644
index 0000000..911bcf4
--- /dev/null
+++ b/docs/personal_shift_holiday_linkage.md
@@ -0,0 +1,142 @@
+# 浜哄憳鎺掔彮璇峰亣浼戞伅鑱斿姩
+
+## 娑夊強椤甸潰
+
+- 浜哄憳鎺掔彮绠$悊椤甸潰锛堟湀搴︽帓鐝煡鐪嬶級
+
+## API
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| GET | /personalShift/page | 浜哄憳鎺掔彮鏈堝害鍒嗛〉鏌ヨ锛堝凡鏂板璇峰亣浼戞伅鑱斿姩锛� |
+
+**璇锋眰鍙傛暟锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| size | Integer | 鏄� | 鍒嗛〉澶у皬 |
+| current | Integer | 鏄� | 褰撳墠椤电爜 |
+| time | String | 鏄� | 鏃堕棿锛堟牸寮忥細yyyy-MM-dd HH:mm:ss锛� |
+| userName | String | 鍚� | 鍛樺伐濮撳悕锛堟ā绯婃煡璇級 |
+| sysDeptId | Integer | 鍚� | 閮ㄩ棬ID |
+
+**鍝嶅簲缁撴瀯鍙樺寲锛�**
+
+```json
+{
+ "code": 200,
+ "msg": "鎿嶄綔鎴愬姛",
+ "data": {
+ "page": {
+ "records": [
+ {
+ "name": "鍛樺伐濮撳悕",
+ "userId": "鍛樺伐ID",
+ "department": "閮ㄩ棬",
+ "shiftTime": null,
+ "monthlyAttendance": {
+ "鏃�": 5,
+ "涓�": 3,
+ "浼戞伅": 2,
+ "totalAttendance": 8
+ },
+ "list": [
+ {
+ "id": "鎺掔彮璁板綍ID",
+ "shift": "鏃�",
+ "time": "2026-06-01 00:00:00",
+ "isHoliday": false
+ },
+ {
+ "id": "鎺掔彮璁板綍ID",
+ "shift": "浼戞伅",
+ "time": "2026-06-05 00:00:00",
+ "isHoliday": true
+ }
+ ],
+ "holidayDates": ["2026-06-05", "2026-06-06"]
+ }
+ ]
+ },
+ "headerList": [...]
+ }
+}
+```
+
+### 鏂板瀛楁璇存槑
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| list[].isHoliday | Boolean | 璇ユ棩鏈熸槸鍚︿负瀹℃牳閫氳繃鐨勮鍋囨棩鏈� |
+| list[].shift | String | 濡傛湁璇峰亣鍒欐樉绀�"浼戞伅"锛屽惁鍒欐樉绀哄師鐝 |
+| holidayDates | List<String> | 璇ュ憳宸ュ綋鏈堟墍鏈夊鏍搁�氳繃鐨勮鍋囨棩鏈熷垪琛紙鏍煎紡锛歽yyy-MM-dd锛� |
+| monthlyAttendance.浼戞伅 | Integer | 褰撴湀璇峰亣浼戞伅澶╂暟缁熻 |
+
+## 鍓嶇淇敼鐐�
+
+### 1. 琛ㄦ牸娓叉煋閫昏緫淇敼
+
+鍦ㄦ覆鏌撴帓鐝〃鏍兼椂锛岄渶瑕佹牴鎹� `isHoliday` 瀛楁鍒ゆ柇鏄惁涓鸿鍋囦紤鎭棩鏈燂紝骞舵樉绀虹壒娈婃牱寮忋��
+
+```html
+<el-table-column v-for="day in headerList" :key="day.headerTime" :label="day.headerTime">
+ <template #default="{ row }">
+ <!-- 鎵惧埌瀵瑰簲鏃ユ湡鐨勬帓鐝暟鎹� -->
+ <div
+ v-for="item in row.list.filter(i => i.time.startsWith(day.headerTime.split(' ')[0]))"
+ :key="item.id"
+ :class="{ 'holiday-cell': item.isHoliday }"
+ >
+ {{ item.shift }}
+ </div>
+ </template>
+</el-table-column>
+```
+
+### 2. 鏍峰紡淇敼
+
+璇峰亣浼戞伅鏃ユ湡闇�瑕佺壒娈婃牱寮忔爣璁般��
+
+```css
+.holiday-cell {
+ background-color: #fff3cd;
+ color: #856404;
+ border: 1px solid #ffc107;
+ padding: 4px 8px;
+ border-radius: 4px;
+}
+```
+
+### 3. 鏁版嵁澶勭悊
+
+```js
+// 鍦ㄨ幏鍙栨暟鎹悗锛屽鐞嗚鍋囨棩鏈熸樉绀�
+handleShiftData(data) {
+ data.page.records.forEach(row => {
+ // row.holidayDates 鍖呭惈鎵�鏈夎鍋囨棩鏈�
+ // row.list 涓� isHoliday=true 鐨勯」琛ㄧず璇峰亣浼戞伅
+ // row.monthlyAttendance.浼戞伅 琛ㄧず褰撴湀璇峰亣浼戞伅澶╂暟
+ });
+}
+```
+
+### 4. 鍙�夛細璇峰亣璇︽儏寮圭獥
+
+濡傞渶鏌ョ湅璇峰亣璇︽儏锛屽彲閫氳繃 `holidayDates` 鍒楄〃灞曠ず銆�
+
+```html
+<el-tooltip v-if="row.holidayDates.length > 0" placement="top">
+ <template #content>
+ <div>璇峰亣鏃ユ湡锛�</div>
+ <div v-for="date in row.holidayDates" :key="date">{{ date }}</div>
+ </template>
+ <el-tag type="warning" size="small">{{ row.holidayDates.length }}澶╄鍋�</el-tag>
+</el-tooltip>
+```
+
+## 娉ㄦ剰浜嬮」
+
+- 璇峰亣鏁版嵁鏉ユ簮浜� `HolidayApplication` 琛紝鍙湁鐘舵�佷负 `APPROVED`锛堝鏍搁�氳繃锛夌殑璇峰亣璁板綍鎵嶄細琚悓姝�
+- 璇峰亣鏃ユ湡鑼冨洿鍐咃紝鍘熺彮娆′細琚浛鎹负"浼戞伅"鏄剧ず
+- 鏈堝害缁熻涓殑"浼戞伅"澶╂暟浼氬崟鐙粺璁¤鍋囦紤鎭ぉ鏁�
+- 鍑哄嫟澶╂暟缁熻锛堟棭/涓�/鏅�/澶滐級涓嶄細缁熻璇峰亣浼戞伅鏃ユ湡
\ No newline at end of file
diff --git a/src/main/java/com/ruoyi/staff/dto/PerformanceShiftMapDto.java b/src/main/java/com/ruoyi/staff/dto/PerformanceShiftMapDto.java
index ccb0590..aed7ab5 100644
--- a/src/main/java/com/ruoyi/staff/dto/PerformanceShiftMapDto.java
+++ b/src/main/java/com/ruoyi/staff/dto/PerformanceShiftMapDto.java
@@ -23,4 +23,9 @@
private List<Map<String, Object>> list = new ArrayList<>();
private List<Map<Object, Object>> headerList = new ArrayList<>();
+
+ /**
+ * 璇峰亣鏃ユ湡鍒楄〃锛堝鏍搁�氳繃鐨勮鍋囨棩鏈燂紝鏍煎紡锛歽yyy-MM-dd锛�
+ */
+ private List<String> holidayDates = new ArrayList<>();
}
diff --git a/src/main/java/com/ruoyi/staff/mapper/PersonalShiftMapper.java b/src/main/java/com/ruoyi/staff/mapper/PersonalShiftMapper.java
index 8ae881f..5d2009e 100644
--- a/src/main/java/com/ruoyi/staff/mapper/PersonalShiftMapper.java
+++ b/src/main/java/com/ruoyi/staff/mapper/PersonalShiftMapper.java
@@ -31,4 +31,11 @@
List<Map<String, Object>> performanceShiftYearList(@Param("time") String time, @Param("userName") String userName, @Param("sysDeptId") Integer sysDeptId);
List<PerformanceShiftMapDto> performanceShiftList(@Param("time") String time, @Param("userName") String userName, @Param("sysDeptId") Integer sysDeptId);
+
+ /**
+ * 鏌ヨ鍛樺伐鐨勮鍋囦俊鎭紙浠巃pproval_instance鐨刦orm_config瑙f瀽锛�
+ * @param staffIds 鍛樺伐ID鍒楄〃
+ * @return Map: staff_id, form_config
+ */
+ List<Map<String, Object>> selectStaffHolidayDates(@Param("staffIds") List<Long> staffIds);
}
diff --git a/src/main/java/com/ruoyi/staff/service/impl/PersonalShiftServiceImpl.java b/src/main/java/com/ruoyi/staff/service/impl/PersonalShiftServiceImpl.java
index 81a3545..3da6ffb 100644
--- a/src/main/java/com/ruoyi/staff/service/impl/PersonalShiftServiceImpl.java
+++ b/src/main/java/com/ruoyi/staff/service/impl/PersonalShiftServiceImpl.java
@@ -29,6 +29,7 @@
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
+import java.util.stream.Collectors;
/**
@@ -116,13 +117,89 @@
IPage<PerformanceShiftMapDto> mapIPage = baseMapper.performanceShiftPage(page, time, userName, sysDeptId);
//鏌ヨ鎵�鏈夌彮娆�(鎵撳崱瑙勫垯)
List<PersonalAttendanceLocationConfig> personalAttendanceLocationConfigs = personalAttendanceLocationConfigMapper.selectList(null);
+
+ // 鑾峰彇褰撴湀鏃堕棿鑼冨洿
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+ DateTimeFormatter formatters = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+ LocalDateTime localDateTime = LocalDateTime.parse(time, formatters);
+ LocalDate firstDayOfMonth = localDateTime.toLocalDate().withDayOfMonth(1);
+ LocalDate lastDayOfMonth = localDateTime.toLocalDate().with(TemporalAdjusters.lastDayOfMonth());
+
+ // 鏀堕泦鎵�鏈夊憳宸D
+ List<Long> staffIds = mapIPage.getRecords().stream()
+ .filter(dto -> dto.getUserId() != null)
+ .map(dto -> Long.valueOf(dto.getUserId()))
+ .distinct()
+ .collect(Collectors.toList());
+
+ // 鏌ヨ褰撴湀瀹℃牳閫氳繃鐨勮鍋囪褰曪紙浠巃pproval_instance鐨刦orm_config瑙f瀽锛�
+ Map<Long, List<String>> staffHolidayMap = new HashMap<>();
+ if (!staffIds.isEmpty()) {
+ // 閫氳繃鍏宠仈鏌ヨ鑾峰彇鍛樺伐璇峰亣淇℃伅
+ List<Map<String, Object>> holidayRecords = baseMapper.selectStaffHolidayDates(staffIds);
+
+ // 鏋勫缓鍛樺伐-璇峰亣鏃ユ湡鏄犲皠锛堜粠form_config JSON瑙f瀽锛�
+ for (Map<String, Object> record : holidayRecords) {
+ Long staffId = ((Number) record.get("staff_id")).longValue();
+ String formConfig = (String) record.get("form_config");
+
+ if (formConfig != null && !formConfig.isEmpty()) {
+ try {
+ // 瑙f瀽form_config JSON
+ Map<String, Object> formMap = JackSonUtil.unmarshal(formConfig, Map.class);
+ Object formPayloadObj = formMap.get("formPayload");
+
+ if (formPayloadObj instanceof Map) {
+ Map<String, Object> formPayload = (Map<String, Object>) formPayloadObj;
+ Object dateRangeObj = formPayload.get("00"); // 璇峰亣鏃ユ湡瀛楁key涓�"00"
+
+ if (dateRangeObj instanceof List) {
+ List<String> dateRange = (List<String>) dateRangeObj;
+ if (dateRange.size() >= 2) {
+ String startDateTime = dateRange.get(0);
+ String endDateTime = dateRange.get(1);
+
+ // 瑙f瀽鏃ユ湡锛堟牸寮忥細yyyy-MM-dd HH:mm:ss锛�
+ LocalDate startDate = LocalDate.parse(startDateTime.substring(0, 10));
+ LocalDate endDate = LocalDate.parse(endDateTime.substring(0, 10));
+
+ // 杩囨护褰撴湀鑼冨洿鍐呯殑璇峰亣鏃ユ湡
+ List<String> dates = getDatesBetween(startDate, endDate).stream()
+ .filter(date -> {
+ LocalDate d = LocalDate.parse(date);
+ return !d.isBefore(firstDayOfMonth) && !d.isAfter(lastDayOfMonth);
+ })
+ .collect(Collectors.toList());
+
+ if (!dates.isEmpty()) {
+ staffHolidayMap.computeIfAbsent(staffId, k -> new ArrayList<>()).addAll(dates);
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ // JSON瑙f瀽澶辫触锛岃烦杩囨璁板綍
+ }
+ }
+ }
+ }
+
for (PerformanceShiftMapDto i : mapIPage.getRecords()) {
+ // 璁剧疆璇峰亣鏃ユ湡鍒楄〃
+ if (i.getUserId() != null) {
+ Long staffId = Long.valueOf(i.getUserId());
+ List<String> holidayDates = staffHolidayMap.getOrDefault(staffId, new ArrayList<>());
+ i.setHolidayDates(holidayDates);
+ }
+
List<String> shiftTimes = StrUtil.split(i.getShiftTime(), ";");
if(CollUtil.isEmpty(shiftTimes)){
continue;
}
double totalAttendance = 0;//鎬诲嚭鍕ゅぉ鏁�
List<Map<String, Object>> map = new ArrayList<>();
+ // 鑾峰彇璇ュ憳宸ョ殑璇峰亣鏃ユ湡闆嗗悎
+ Set<String> holidayDateSet = new HashSet<>(i.getHolidayDates());
// 鍒嗗壊鏃ユ湡
for (String shiftTime : shiftTimes) {
i.setShiftTime(null);
@@ -135,34 +212,51 @@
hashMap.put("id", shiftTimeAndShift.get(2));
hashMap.put("shift", shiftTimeAndShift.get(1));
hashMap.put("time", shiftTimeAndShift.get(0));
+
+ // 鎻愬彇鏃ユ湡閮ㄥ垎锛堟牸寮忥細yyyy-MM-dd HH:mm:ss -> yyyy-MM-dd锛�
+ String workTimeStr = shiftTimeAndShift.get(0);
+ String workDate = workTimeStr.length() >= 10 ? workTimeStr.substring(0, 10) : workTimeStr;
+
+ // 濡傛灉璇ユ棩鏈熸湁瀹℃牳閫氳繃鐨勮鍋囪褰曪紝璁剧疆isHoliday涓簍rue锛屽苟灏嗙彮娆¤涓轰紤鎭�
+ if (holidayDateSet.contains(workDate)) {
+ hashMap.put("isHoliday", true);
+ hashMap.put("shift", "浼戞伅");
+ } else {
+ hashMap.put("isHoliday", false);
+ }
+
map.add(hashMap);
i.setList(map);
- //姹囨�荤殑鍚勭彮娆$粺璁℃暟鎹�
- for (PersonalAttendanceLocationConfig personalAttendanceLocationConfig : personalAttendanceLocationConfigs) {
- if (!i.getMonthlyAttendance().containsKey(personalAttendanceLocationConfig.getShift())){
- i.getMonthlyAttendance().put(personalAttendanceLocationConfig.getShift(), 0);
+
+ // 缁熻鐝鏁版嵁
+ if (holidayDateSet.contains(workDate)) {
+ // 濡傛灉鏄鍋囨棩鏈燂紝缁熻涓轰紤鎭紙鍙粺璁′竴娆★級
+ if (!i.getMonthlyAttendance().containsKey("浼戞伅")){
+ i.getMonthlyAttendance().put("浼戞伅", 0);
}
- if (personalAttendanceLocationConfig.getShift().equals(shiftTimeAndShift.get(1))) {
- BigDecimal bigDecimal = new BigDecimal(i.getMonthlyAttendance().get(personalAttendanceLocationConfig.getShift()).toString());
- i.getMonthlyAttendance().put(personalAttendanceLocationConfig.getShift(), bigDecimal.add(new BigDecimal("1")));
+ i.getMonthlyAttendance().put("浼戞伅", new BigDecimal(i.getMonthlyAttendance().get("浼戞伅").toString()).add(new BigDecimal("1")));
+ } else {
+ // 涓嶆槸璇峰亣鏃ユ湡锛岀粺璁″師鐝
+ for (PersonalAttendanceLocationConfig personalAttendanceLocationConfig : personalAttendanceLocationConfigs) {
+ if (!i.getMonthlyAttendance().containsKey(personalAttendanceLocationConfig.getShift())){
+ i.getMonthlyAttendance().put(personalAttendanceLocationConfig.getShift(), 0);
+ }
+ if (personalAttendanceLocationConfig.getShift().equals(shiftTimeAndShift.get(1))) {
+ BigDecimal bigDecimal = new BigDecimal(i.getMonthlyAttendance().get(personalAttendanceLocationConfig.getShift()).toString());
+ i.getMonthlyAttendance().put(personalAttendanceLocationConfig.getShift(), bigDecimal.add(new BigDecimal("1")));
+ }
}
- }
- //缁熻鎬诲嚭鍕ゅぉ鏁�(鏃�/涓�/鏅�/澶�)閮界畻鍑哄嫟,鍏朵綑閮芥槸浼戞伅
- if (shiftTimeAndShift.get(1).contains("鏃�") ||
+ // 缁熻鎬诲嚭鍕ゅぉ鏁�(鏃�/涓�/鏅�/澶�)閮界畻鍑哄嫟
+ if (shiftTimeAndShift.get(1).contains("鏃�") ||
shiftTimeAndShift.get(1).contains("涓�") ||
shiftTimeAndShift.get(1).contains("鏅�") ||
shiftTimeAndShift.get(1).contains("澶�")) {
- i.getMonthlyAttendance().put("totalAttendance", totalAttendance += 1);
+ i.getMonthlyAttendance().put("totalAttendance", totalAttendance += 1);
+ }
}
}
}
// 鑾峰彇header鏃堕棿
- DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
- DateTimeFormatter formatters = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
- // 灏嗗瓧绗︿覆鏃堕棿杞崲涓� LocalDateTime 绫诲瀷鏃堕棿
- LocalDateTime localDateTime = LocalDateTime.parse(time, formatters);
- LocalDate firstDayOfMonth = localDateTime.toLocalDate().withDayOfMonth(1);
- LocalDate lastDayOfMonth = localDateTime.toLocalDate().with(TemporalAdjusters.lastDayOfMonth());
List<LocalDateTime> localDateTimesBetween = getLocalDateTimesBetween(firstDayOfMonth.atStartOfDay(), lastDayOfMonth.atStartOfDay());
List<Object> list1 = new ArrayList<>();
for (LocalDateTime dateTime : localDateTimesBetween) {
@@ -178,6 +272,21 @@
return resultMap;
}
+ /**
+ * 鑾峰彇涓や釜鏃ユ湡涔嬮棿鐨勬墍鏈夋棩鏈燂紙鏍煎紡锛歽yyy-MM-dd锛�
+ * 娉ㄦ剰锛氱粨鏉熸棩鏈熶笉鍖呭惈鍦ㄥ唴锛堣鍋囨棩鏈熻寖鍥存槸寮�濮嬫棩鏈熷埌缁撴潫鏃ユ湡锛屼笉鍚粨鏉熸棩鏈燂級
+ */
+ private List<String> getDatesBetween(LocalDate startDate, LocalDate endDate) {
+ List<String> dates = new ArrayList<>();
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+ LocalDate current = startDate;
+ while (current.isBefore(endDate)) {
+ dates.add(current.format(formatter));
+ current = current.plusDays(1);
+ }
+ return dates;
+ }
+
@Override
public void performanceShiftUpdate(PersonalShift personalShift) {
baseMapper.update(new PersonalShift(), Wrappers.<PersonalShift>lambdaUpdate()
diff --git a/src/main/resources/mapper/staff/PersonalShiftMapper.xml b/src/main/resources/mapper/staff/PersonalShiftMapper.xml
index 7d80392..9ece2d1 100644
--- a/src/main/resources/mapper/staff/PersonalShiftMapper.xml
+++ b/src/main/resources/mapper/staff/PersonalShiftMapper.xml
@@ -103,4 +103,22 @@
order by s.create_time
</select>
+ <!-- 鏌ヨ鍛樺伐鐨勮鍋囦俊鎭紙浠巃pproval_instance鐨刦orm_config瑙f瀽锛� -->
+ <!-- 鍏宠仈璺緞: staff_on_job.staff_no = sys_user.user_name, sys_user.user_id = approval_instance.applicant_id -->
+ <select id="selectStaffHolidayDates" resultType="java.util.Map">
+ SELECT
+ soj.id AS staff_id,
+ ai.form_config
+ FROM staff_on_job soj
+ INNER JOIN sys_user su ON su.user_name = soj.staff_no
+ INNER JOIN approval_instance ai ON ai.applicant_id = su.user_id
+ WHERE soj.id IN
+ <foreach collection="staffIds" item="staffId" open="(" separator="," close=")">
+ #{staffId}
+ </foreach>
+ AND ai.business_type IN (14, 19)
+ AND ai.status = 'APPROVED'
+ AND ai.deleted = 0
+ </select>
+
</mapper>
--
Gitblit v1.9.3