From e4432d49f9d74e69cc71ed1141ebcd30c817e998 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期六, 17 十月 2026 20:34:24 +0800
Subject: [PATCH] feat(hrm): 新增我的考勤状态接口并开放全员打卡

---
 docs/project-business/data/roles.json                                                                                      |    2 
 yudao-module-hrm-api/src/main/java/cn/iocoder/yudao/module/hrm/enums/HrmAttendanceClockTypeEnum.java                       |   15 ++
 docs/project-business/data/modules.json                                                                                    |    2 
 yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceRecordController.java  |   43 ++++--
 yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRecordService.java              |   10 +
 yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmAttendanceMyStatusRespVO.java |   67 +++++++++++
 yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRecordServiceImpl.java          |  166 +++++++++++++++++++++++++++
 yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmAttendanceRecordRespVO.java   |    6 +
 8 files changed, 295 insertions(+), 16 deletions(-)

diff --git a/docs/project-business/data/modules.json b/docs/project-business/data/modules.json
index 4c1407b..dc2a3ff 100644
--- a/docs/project-business/data/modules.json
+++ b/docs/project-business/data/modules.json
@@ -83,7 +83,7 @@
     {"key": "hrm", "name": "浜哄姏璧勬簮", "color": "#9db85a", "tag": "HRM", "flow": null,
      "positioning": "鍛樺伐鍏ヨ浆璋冪涓庤�冨嫟钖叕",
      "problem": "浜轰簨娴佺▼瀹℃壒鍖栥�佺鑱屾暟鎹笉涓�(浜ゆ帴)銆佺畻钖嚜鍔ㄥ寲",
-     "features": ["鍛樺伐妗f(鍏ヨ亴鑷姩寤虹郴缁熻处鍙�)", "璇峰亣/鍔犵彮/璋冨矖/绂昏亴 BPM 瀹℃壒", "绂昏亴浜ゆ帴鏄犲皠 expandUserIds", "鑰冨嫟(鎵撳崱路瑙勫垯路鎺掔彮路琛ュ崱瀹℃壒路鏈堝害姹囨��)", "钖叕(搴斿彂-绀句繚鍏Н閲�-涓◣-璇峰亣鎵f路1.5/2/3鍊嶅姞鐝垂路鍙戞斁鍙拌处)", "鍩硅璇佷功(SQL鍏堣寰呭疄鐜�)"],
+     "features": ["鍛樺伐妗f(鍏ヨ亴鑷姩寤虹郴缁熻处鍙�)", "璇峰亣/鍔犵彮/璋冨矖/绂昏亴 BPM 瀹℃壒", "绂昏亴浜ゆ帴鏄犲皠 expandUserIds", "鑰冨嫟(绉诲姩绔垜鐨勮�冨嫟鐘舵�伮锋墦鍗÷疯鍒櫬锋帓鐝疯ˉ鍗″鎵孤锋湀搴︽眹鎬�)", "钖叕(搴斿彂-绀句繚鍏Н閲�-涓◣-璇峰亣鎵f路1.5/2/3鍊嶅姞鐝垂路鍙戞斁鍙拌处)", "鍩硅璇佷功(SQL鍏堣寰呭疄鐜�)"],
      "objects": ["鍛樺伐", "璇峰亣鍗�", "绂昏亴鐢宠", "浜ゆ帴鏄犲皠", "鑰冨嫟璁板綍", "钖叕鏍哥畻"],
      "roles": ["HR", "閮ㄩ棬涓荤", "鍛樺伐鏈汉"],
      "upstream": [], "downstream": ["鍚勬ā鍧�(鏁版嵁鍙鎬ф墿灞�)", "system(璐﹀彿)"],
diff --git a/docs/project-business/data/roles.json b/docs/project-business/data/roles.json
index 4980d2b..3a0b7ae 100644
--- a/docs/project-business/data/roles.json
+++ b/docs/project-business/data/roles.json
@@ -18,7 +18,7 @@
     {"role": "璁惧鍛�/缁翠慨宸�", "modules": ["MES-DV"], "operations": ["鐐规淇濆吇", "缁翠慨宸ュ崟", "宸ヨ棰嗙敤"], "data": ["mes_dv_*", "mes_tm_*"], "scope": "chargeUserId 褰掑睘"},
     {"role": "瀹㈡湇/鍞悗宸ョ▼甯�", "modules": ["鍞悗"], "operations": ["宸ュ崟鍙楃悊娲惧伐", "缁翠慨", "閫�璐х敵璇�"], "data": ["after_sale_*"], "scope": "琛岀骇鏁版嵁鏉冮檺鍥涜鍥撅細鎴戣礋璐�/鎴戝弬涓�/涓嬪睘璐熻矗/鍏ㄩ儴(aftersales:view-all)"},
     {"role": "璐㈠姟", "modules": ["ERP", "CRM"], "operations": ["鏀舵鍗�", "浠樻鍗�", "寮�绁ㄥ鏍�", "缁撶畻璐︽埛"], "data": ["erp_finance_*", "crm_invoice", "crm_receivable"], "scope": "financeUserId 褰掑睘"},
-    {"role": "HR", "modules": ["HRM"], "operations": ["鍛樺伐妗f鍏ヨ浆璋冪", "鑰冨嫟瑙勫垯鎺掔彮", "钖叕鏍哥畻鍙戞斁", "绂昏亴浜ゆ帴"], "data": ["hrm_*"], "scope": "閮ㄩ棬鑼冨洿 dataScope"},
+    {"role": "HR", "modules": ["HRM"], "operations": ["鍛樺伐妗f鍏ヨ浆璋冪", "鑰冨嫟瑙勫垯鎺掔彮(鏈汉鎵撳崱鍏ㄥ憳鍙敤锛屼笉璁炬潈闄愮偣)", "钖叕鏍哥畻鍙戞斁", "绂昏亴浜ゆ帴"], "data": ["hrm_*"], "scope": "閮ㄩ棬鑼冨洿 dataScope"},
     {"role": "绠$悊灞�", "modules": ["BI"], "operations": ["鏌ョ湅閲囪喘閿�鍞�/浠撳簱/鐢熶骇璁惧/璐ㄩ噺/浜哄姏浜斿ぇ涓婚澶у睆"], "data": ["bi_dashboard"], "scope": "鍙"}
   ]
 }
diff --git a/yudao-module-hrm-api/src/main/java/cn/iocoder/yudao/module/hrm/enums/HrmAttendanceClockTypeEnum.java b/yudao-module-hrm-api/src/main/java/cn/iocoder/yudao/module/hrm/enums/HrmAttendanceClockTypeEnum.java
index ee77e9f..99bd961 100644
--- a/yudao-module-hrm-api/src/main/java/cn/iocoder/yudao/module/hrm/enums/HrmAttendanceClockTypeEnum.java
+++ b/yudao-module-hrm-api/src/main/java/cn/iocoder/yudao/module/hrm/enums/HrmAttendanceClockTypeEnum.java
@@ -28,4 +28,19 @@
         return ARRAYS;
     }
 
+    /**
+     * 鏍规嵁绫诲瀷鑾峰彇鍚嶇О锛屾湭鐭ョ被鍨嬭繑鍥� null
+     */
+    public static String getNameByType(Integer type) {
+        if (type == null) {
+            return null;
+        }
+        for (HrmAttendanceClockTypeEnum value : values()) {
+            if (value.getType().equals(type)) {
+                return value.getName();
+            }
+        }
+        return null;
+    }
+
 }
\ No newline at end of file
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceRecordController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceRecordController.java
index 3d677dc..0528f3e 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceRecordController.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceRecordController.java
@@ -8,12 +8,14 @@
 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.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;
@@ -27,11 +29,13 @@
 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 java.io.IOException;
+import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.util.*;
 import java.util.stream.Collectors;
@@ -39,6 +43,7 @@
 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.*;
 
@@ -59,36 +64,46 @@
 
     @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 exception(EMPLOYEE_NOT_EXISTS,
+                    "褰撳墠鐧诲綍璐﹀彿鏈叧鑱斿憳宸ユ。妗堬紝鏃犳硶鎵撳崱銆傝鑱旂郴绠$悊鍛樺湪銆愬憳宸ョ鐞嗐�戜腑灏嗚鐧诲綍璐﹀彿缁戝畾鍒板憳宸ャ��");
         }
         HrmAttendanceRecordDO record = attendanceRecordService.clockIn(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);
+        return success(buildClockRespVO(record, employee));
     }
 
     @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 exception(EMPLOYEE_NOT_EXISTS, "褰撳墠鐢ㄦ埛鏈叧鑱斿憳宸ヤ俊鎭紝鏃犳硶鎵撳崱");
+            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());
@@ -96,7 +111,7 @@
                 respVO.setDeptName(dept.getName());
             }
         }
-        return success(respVO);
+        return respVO;
     }
 
     private Long getLoginUserId() {
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmAttendanceMyStatusRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmAttendanceMyStatusRespVO.java
new file mode 100644
index 0000000..f5d1198
--- /dev/null
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmAttendanceMyStatusRespVO.java
@@ -0,0 +1,67 @@
+package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+@Schema(description = "绠$悊鍚庡彴 - 鎴戠殑鑰冨嫟鐘舵�� Response VO")
+@Data
+public class HrmAttendanceMyStatusRespVO {
+
+    @Schema(description = "鐧诲綍鐢ㄦ埛ID", example = "464")
+    private Long userId;
+
+    @Schema(description = "鍛樺伐妗fID", example = "233")
+    private Long employeeId;
+
+    @Schema(description = "鍛樺伐濮撳悕", example = "闃垮崪鏉滃厠鐑湪路鍚炬媺渚濇湪")
+    private String employeeName;
+
+    @Schema(description = "閮ㄩ棬ID", example = "131")
+    private Long deptId;
+
+    @Schema(description = "閮ㄩ棬鍚嶇О", example = "閽㈢粨鏋勮溅闂�")
+    private String deptName;
+
+    @Schema(description = "鑰冨嫟鏃ユ湡", example = "2026-10-17")
+    private LocalDate date;
+
+    @Schema(description = "鏈嶅姟鍣ㄦ椂闂�", example = "2026-10-17 18:30:00")
+    private LocalDateTime serverTime;
+
+    @Schema(description = "鏄惁宸ヤ綔鏃�")
+    private Boolean workDay;
+
+    @Schema(description = "浼戞伅鏃ュ師鍥狅紝濡傦細鍥藉簡鑺傘�佸懆鍏�")
+    private String restDayReason;
+
+    @Schema(description = "褰撳墠鐢熸晥鐨勮�冨嫟瑙勫垯锛屾湭閰嶇疆鏃朵负 null")
+    private HrmAttendanceRuleRespVO rule;
+
+    @Schema(description = "褰撴棩鑰冨嫟璁板綍锛屾湭鎵撳崱鏃朵负 null")
+    private HrmAttendanceRecordRespVO record;
+
+    @Schema(description = "褰撳墠鍙墦鍗$姸鎬�")
+    private Clockable clockable;
+
+    @Schema(description = "鎵撳崱鐘舵��")
+    @Data
+    public static class Clockable {
+
+        @Schema(description = "鏄惁鍙笂鐝墦鍗�")
+        private Boolean clockIn;
+
+        @Schema(description = "鏄惁鍙笅鐝墦鍗�")
+        private Boolean clockOut;
+
+        @Schema(description = "涓嬩竴姝ュ姩浣滐細in-涓婄彮鎵撳崱锛宱ut-涓嬬彮鎵撳崱锛宯one-鏃犻渶鎵撳崱")
+        private String nextAction;
+
+        @Schema(description = "鎻愮ず鏂囨", example = "宸蹭笂鐝墦鍗★紝鍙笅鐝墦鍗�")
+        private String message;
+
+    }
+
+}
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmAttendanceRecordRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmAttendanceRecordRespVO.java
index 7b91afa..aa44d1e 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmAttendanceRecordRespVO.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmAttendanceRecordRespVO.java
@@ -55,11 +55,17 @@
     @DictFormat("hrm_clock_type")
     private Integer clockInType;
 
+    @Schema(description = "涓婄彮鎵撳崱绫诲瀷鍚嶇О", example = "姝e父")
+    private String clockInTypeName;
+
     @Schema(description = "涓嬬彮鎵撳崱绫诲瀷")
     @ExcelProperty(value = "涓嬬彮鎵撳崱绫诲瀷", converter = DictConvert.class)
     @DictFormat("hrm_clock_type")
     private Integer clockOutType;
 
+    @Schema(description = "涓嬬彮鎵撳崱绫诲瀷鍚嶇О", example = "鏃╅��")
+    private String clockOutTypeName;
+
     @Schema(description = "宸ヤ綔鏃堕暱锛堝皬鏃讹級")
     @ExcelProperty("宸ヤ綔鏃堕暱")
     private BigDecimal workHours;
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRecordService.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRecordService.java
index 25cfd62..184295a 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRecordService.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRecordService.java
@@ -1,6 +1,7 @@
 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;
@@ -37,6 +38,15 @@
      */
     HrmAttendanceRecordDO getAttendanceRecordByUserIdAndDate(Long userId, LocalDate date);
 
+    /**
+     * 鑾峰緱褰撳墠鐧诲綍浜虹殑鑰冨嫟鐘舵�侊細鑰冨嫟瑙勫垯 + 鎸囧畾鏃ユ湡鐨勬墦鍗$姸鎬�
+     *
+     * @param loginUserId 鐧诲綍鐢ㄦ埛ID锛堢郴缁熺敤鎴稩D锛屼笉鏄憳宸D锛�
+     * @param date        鑰冨嫟鏃ユ湡锛屼负绌烘椂鍙栨湇鍔″櫒褰撳ぉ
+     * @return 鎴戠殑鑰冨嫟鐘舵��
+     */
+    HrmAttendanceMyStatusRespVO getMyStatus(Long loginUserId, LocalDate date);
+
     /** 鍒涘缓瀵煎叆鐨勮�冨嫟璁板綍 */
     void createImportedRecord(HrmAttendanceRecordDO record);
 
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRecordServiceImpl.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRecordServiceImpl.java
index 60ba60d..92f745d 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRecordServiceImpl.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRecordServiceImpl.java
@@ -2,7 +2,11 @@
 
 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.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.dal.dataobject.attendance.HrmAttendanceRecordDO;
 import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmAttendanceRuleDO;
 import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmAttendanceExceptionDO;
@@ -11,7 +15,10 @@
 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;
@@ -19,6 +26,7 @@
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
+import java.time.DayOfWeek;
 import java.time.Duration;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
@@ -45,6 +53,8 @@
     private HrmAttendanceRuleService attendanceRuleService;
     @Resource
     private HrmAttendanceHolidayService attendanceHolidayService;
+    @Resource
+    private DeptApi deptApi;
 
     @Override
     public PageResult<HrmAttendanceRecordDO> getAttendanceRecordPage(HrmAttendanceRecordPageReqVO pageReqVO) {
@@ -67,6 +77,162 @@
     }
 
     @Override
+    public HrmAttendanceMyStatusRespVO getMyStatus(Long loginUserId, LocalDate date) {
+        LocalDate queryDate = date != null ? date : LocalDate.now();
+        // 1. 鐧诲綍鐢ㄦ埛 -> 鍛樺伐妗f
+        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);

--
Gitblit v1.9.3