From 2ae58a2f2d53c220c4b02d8e9f17770f63397b1e Mon Sep 17 00:00:00 2001
From: liyong <18434998025@163.com>
Date: 星期四, 26 二月 2026 10:40:18 +0800
Subject: [PATCH] 人力资源模块迁移

---
 src/main/java/com/ruoyi/staff/pojo/PersonalAttendanceRecords.java                           |   86 ++-
 src/main/java/com/ruoyi/staff/mapper/PersonalAttendanceRecordsMapper.java                   |   22 +
 src/main/java/com/ruoyi/staff/service/impl/StaffOnJobServiceImpl.java                       |   35 +
 src/main/java/com/ruoyi/staff/pojo/StaffContract.java                                       |    1 
 src/main/java/com/ruoyi/staff/service/impl/PersonalAttendanceRecordsServiceImpl.java        |  241 +++++++++++
 src/main/java/com/ruoyi/staff/mapper/StaffLeaveMapper.java                                  |    7 
 src/main/java/com/ruoyi/staff/vo/MonthlyTurnoverRateVo.java                                 |   39 +
 src/main/java/com/ruoyi/staff/controller/StaffContractController.java                       |    4 
 src/main/java/com/ruoyi/staff/dto/StaffLeaveDto.java                                        |   10 
 src/main/java/com/ruoyi/staff/service/impl/StaffLeaveServiceImpl.java                       |   24 +
 src/main/java/com/ruoyi/staff/service/AnalyticsService.java                                 |   20 +
 src/main/resources/mapper/production/ProductOrderMapper.xml                                 |    5 
 src/main/java/com/ruoyi/staff/service/impl/PersonalAttendanceLocationConfigServiceImpl.java |   20 +
 src/main/java/com/ruoyi/staff/controller/StaffSchedulingController.java                     |    1 
 src/main/java/com/ruoyi/staff/task/PersonalAttendanceRecordsTask.java                       |   75 +++
 src/main/java/com/ruoyi/staff/dto/SaveStaffSchedulingDto.java                               |    2 
 src/main/java/com/ruoyi/staff/controller/HolidayApplicationController.java                  |    1 
 src/main/java/com/ruoyi/staff/mapper/PersonalAttendanceLocationConfigMapper.java            |   18 
 src/main/java/com/ruoyi/staff/utils/LocationUtils.java                                      |   37 +
 src/main/java/com/ruoyi/staff/mapper/StaffContractMapper.java                               |    4 
 src/main/java/com/ruoyi/staff/mapper/StaffOnJobMapper.java                                  |   28 +
 src/main/java/com/ruoyi/staff/controller/AnalyticsController.java                           |   32 +
 src/main/java/com/ruoyi/staff/controller/PersonalAttendanceLocationConfigController.java    |   49 ++
 src/main/java/com/ruoyi/staff/pojo/HolidayApplication.java                                  |    1 
 src/main/java/com/ruoyi/staff/pojo/StaffLeave.java                                          |    3 
 src/main/java/com/ruoyi/staff/pojo/PersonalAttendanceLocationConfig.java                    |   59 ++
 src/main/java/com/ruoyi/staff/controller/StaffOnJobController.java                          |    1 
 src/main/java/com/ruoyi/staff/pojo/StaffOnJob.java                                          |    2 
 src/main/java/com/ruoyi/staff/service/PersonalAttendanceRecordsService.java                 |   19 
 src/main/java/com/ruoyi/staff/vo/TotalTurnoverRateVo.java                                   |   19 
 src/main/java/com/ruoyi/staff/service/impl/AnalyticsServiceImpl.java                        |  176 ++++++++
 src/main/java/com/ruoyi/staff/service/PersonalAttendanceLocationConfigService.java          |   16 
 src/main/java/com/ruoyi/staff/dto/StaffSchedulingDto.java                                   |    1 
 src/main/java/com/ruoyi/staff/dto/PersonalAttendanceRecordsDto.java                         |   41 ++
 src/main/java/com/ruoyi/staff/controller/PersonalAttendanceRecordsController.java           |   68 +-
 35 files changed, 1,061 insertions(+), 106 deletions(-)

diff --git a/src/main/java/com/ruoyi/staff/controller/AnalyticsController.java b/src/main/java/com/ruoyi/staff/controller/AnalyticsController.java
new file mode 100644
index 0000000..cfa802c
--- /dev/null
+++ b/src/main/java/com/ruoyi/staff/controller/AnalyticsController.java
@@ -0,0 +1,32 @@
+package com.ruoyi.staff.controller;
+
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.staff.service.AnalyticsService;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+
+@RestController
+@RequestMapping("/staff/analytics")
+public class AnalyticsController {
+
+    @Resource
+    private AnalyticsService analyticsService;
+
+    @GetMapping("/reason")
+    public AjaxResult staffLeaveReasonAnalytics() {
+        return AjaxResult.success(analyticsService.staffLeaveReasonAnalytics());
+    }
+
+    @GetMapping("/monthly_turnover_rate")
+    public AjaxResult getMonthlyTurnoverRateFor12Months() {
+        return AjaxResult.success(analyticsService.getMonthlyTurnoverRateFor12Months());
+    }
+
+    @GetMapping("/total_statistic")
+    public AjaxResult getTotalStatistic() {
+        return AjaxResult.success(analyticsService.getTotalStatistic());
+    }
+}
diff --git a/src/main/java/com/ruoyi/staff/controller/HolidayApplicationController.java b/src/main/java/com/ruoyi/staff/controller/HolidayApplicationController.java
index 7ac13bd..66f1710 100644
--- a/src/main/java/com/ruoyi/staff/controller/HolidayApplicationController.java
+++ b/src/main/java/com/ruoyi/staff/controller/HolidayApplicationController.java
@@ -3,7 +3,6 @@
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ruoyi.framework.web.domain.AjaxResult;
 import com.ruoyi.staff.pojo.HolidayApplication;
-import com.ruoyi.staff.pojo.PersonalAttendanceRecords;
 import com.ruoyi.staff.service.HolidayApplicationService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
diff --git a/src/main/java/com/ruoyi/staff/controller/PersonalAttendanceLocationConfigController.java b/src/main/java/com/ruoyi/staff/controller/PersonalAttendanceLocationConfigController.java
new file mode 100644
index 0000000..fbc4fa3
--- /dev/null
+++ b/src/main/java/com/ruoyi/staff/controller/PersonalAttendanceLocationConfigController.java
@@ -0,0 +1,49 @@
+package com.ruoyi.staff.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.framework.web.domain.R;
+import com.ruoyi.staff.pojo.PersonalAttendanceLocationConfig;
+import com.ruoyi.staff.service.PersonalAttendanceLocationConfigService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 浜哄憳鎵撳崱瑙勫垯閰嶇疆 鍓嶇鎺у埗鍣�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-02-11 09:41:34
+ */
+@RestController
+@RequestMapping("/personalAttendanceLocationConfig")
+@Api(tags = "浜哄憳鎵撳崱瑙勫垯閰嶇疆")
+public class PersonalAttendanceLocationConfigController {
+
+    @Autowired
+    private PersonalAttendanceLocationConfigService personalAttendanceLocationConfigService;
+
+    @ApiOperation("鏂板/淇敼浜哄憳鎵撳崱瑙勫垯閰嶇疆")
+    @PostMapping("/add")
+    public R add(@RequestBody PersonalAttendanceLocationConfig personalAttendanceLocationConfig){
+        return R.ok(personalAttendanceLocationConfigService.saveOrUpdate(personalAttendanceLocationConfig));
+    }
+
+    @ApiOperation("鍒嗛〉鏌ヨ浜哄憳鎵撳崱瑙勫垯閰嶇疆")
+    @GetMapping("/listPage")
+    public R listPage(Page page){
+        return R.ok(personalAttendanceLocationConfigService.page(page));
+    }
+
+
+    @ApiOperation("鍒犻櫎浜哄憳鎵撳崱瑙勫垯閰嶇疆")
+    @DeleteMapping("/del")
+    public R del(@RequestBody List<Integer> ids) {
+        return R.ok(personalAttendanceLocationConfigService.removeBatchByIds(ids));
+    }
+
+}
diff --git a/src/main/java/com/ruoyi/staff/controller/PersonalAttendanceRecordsController.java b/src/main/java/com/ruoyi/staff/controller/PersonalAttendanceRecordsController.java
index 53f55fd..8750cbf 100644
--- a/src/main/java/com/ruoyi/staff/controller/PersonalAttendanceRecordsController.java
+++ b/src/main/java/com/ruoyi/staff/controller/PersonalAttendanceRecordsController.java
@@ -2,44 +2,52 @@
 
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ruoyi.framework.web.domain.AjaxResult;
-import com.ruoyi.staff.pojo.PersonalAttendanceRecords;
+import com.ruoyi.staff.dto.PersonalAttendanceRecordsDto;
 import com.ruoyi.staff.service.PersonalAttendanceRecordsService;
-import lombok.AllArgsConstructor;
-import org.springframework.beans.factory.annotation.Autowired;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
 import org.springframework.web.bind.annotation.*;
 
-@AllArgsConstructor
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * <p>
+ *  鍓嶇鎺у埗鍣�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-02-09 01:20:07
+ */
 @RestController
-@RequestMapping("/staff/personalAttendanceRecords")
+@RequestMapping("/personalAttendanceRecords")
+@Api(tags = "浜哄憳鎵撳崱绛惧埌")
 public class PersonalAttendanceRecordsController {
-    @Autowired
+    @Resource
     private PersonalAttendanceRecordsService personalAttendanceRecordsService;
-    /**
-     * 涓汉鑰冨嫟璁板綍鍒嗛〉鏌ヨ
-     */
+
+    @ApiOperation("鏂板鎵撳崱绛惧埌")
+    @PostMapping("")
+    public AjaxResult add(@RequestBody PersonalAttendanceRecordsDto personalAttendanceRecordsDto){
+        return AjaxResult.success(personalAttendanceRecordsService.add(personalAttendanceRecordsDto));
+    }
+
+    @ApiOperation("鍒嗛〉鏌ヨ鎵撳崱绛惧埌")
     @GetMapping("/listPage")
-    public AjaxResult personalAttendanceRecordsListPage(Page page, PersonalAttendanceRecords personalAttendanceRecords) {
-        return AjaxResult.success(personalAttendanceRecordsService.listPage(page, personalAttendanceRecords));
+    public AjaxResult listPage(Page page, PersonalAttendanceRecordsDto personalAttendanceRecordsDto){
+        return AjaxResult.success(personalAttendanceRecordsService.listPage(page, personalAttendanceRecordsDto));
     }
-    /**
-     * 鏂板涓汉鑰冨嫟璁板綍
-     */
-    @PostMapping("/add")
-    public AjaxResult add(@RequestBody PersonalAttendanceRecords personalAttendanceRecords) {
-        return AjaxResult.success(personalAttendanceRecordsService.save(personalAttendanceRecords));
+
+    @ApiOperation("鑾峰彇褰撳墠浜虹殑鑰冨嫟鐩稿叧鏁版嵁")
+    @GetMapping("/today")
+    public AjaxResult todayInfo(PersonalAttendanceRecordsDto personalAttendanceRecordsDto){
+        return AjaxResult.success(personalAttendanceRecordsService.todayInfo(personalAttendanceRecordsDto));
     }
-    /**
-     * 淇敼涓汉鑰冨嫟璁板綍
-     */
-    @PutMapping("/update")
-    public AjaxResult update(@RequestBody PersonalAttendanceRecords personalAttendanceRecords) {
-        return AjaxResult.success(personalAttendanceRecordsService.updateById(personalAttendanceRecords));
+
+    @ApiOperation("瀵煎嚭鎵撳崱绛惧埌")
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, PersonalAttendanceRecordsDto personalAttendanceRecordsDto) {
+        personalAttendanceRecordsService.export(response, personalAttendanceRecordsDto);
     }
-    /**
-     * 鍒犻櫎涓汉鑰冨嫟璁板綍
-     */
-    @DeleteMapping("/delete/{id}")
-    public AjaxResult delete(@PathVariable("id") Long id) {
-        return AjaxResult.success(personalAttendanceRecordsService.removeById(id));
-    }
+
 }
diff --git a/src/main/java/com/ruoyi/staff/controller/StaffContractController.java b/src/main/java/com/ruoyi/staff/controller/StaffContractController.java
index af4c90c..3a8132c 100644
--- a/src/main/java/com/ruoyi/staff/controller/StaffContractController.java
+++ b/src/main/java/com/ruoyi/staff/controller/StaffContractController.java
@@ -5,7 +5,9 @@
 import com.ruoyi.staff.pojo.StaffContract;
 import com.ruoyi.staff.service.StaffContractService;
 import io.swagger.annotations.Api;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
 
 import javax.annotation.Resource;
 
diff --git a/src/main/java/com/ruoyi/staff/controller/StaffOnJobController.java b/src/main/java/com/ruoyi/staff/controller/StaffOnJobController.java
index 8e3d6b6..5b5694b 100644
--- a/src/main/java/com/ruoyi/staff/controller/StaffOnJobController.java
+++ b/src/main/java/com/ruoyi/staff/controller/StaffOnJobController.java
@@ -16,7 +16,6 @@
 
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletResponse;
-import javax.validation.Valid;
 import java.util.List;
 
 /**
diff --git a/src/main/java/com/ruoyi/staff/controller/StaffSchedulingController.java b/src/main/java/com/ruoyi/staff/controller/StaffSchedulingController.java
index a54096a..d4fa3b3 100644
--- a/src/main/java/com/ruoyi/staff/controller/StaffSchedulingController.java
+++ b/src/main/java/com/ruoyi/staff/controller/StaffSchedulingController.java
@@ -2,7 +2,6 @@
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.ruoyi.common.utils.poi.ExcelUtil;
-import com.ruoyi.compensationperformance.pojo.CompensationPerformance;
 import com.ruoyi.framework.aspectj.lang.annotation.Log;
 import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
 import com.ruoyi.framework.web.domain.AjaxResult;
diff --git a/src/main/java/com/ruoyi/staff/dto/PersonalAttendanceRecordsDto.java b/src/main/java/com/ruoyi/staff/dto/PersonalAttendanceRecordsDto.java
new file mode 100644
index 0000000..dc81b1e
--- /dev/null
+++ b/src/main/java/com/ruoyi/staff/dto/PersonalAttendanceRecordsDto.java
@@ -0,0 +1,41 @@
+package com.ruoyi.staff.dto;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.framework.aspectj.lang.annotation.Excel;
+import com.ruoyi.staff.pojo.PersonalAttendanceRecords;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalTime;
+
+@Data
+@ExcelIgnoreUnannotated
+public class PersonalAttendanceRecordsDto extends PersonalAttendanceRecords {
+    @Excel(name = "濮撳悕", sort = 3)
+    private String staffName;
+
+    @Excel(name = "宸ュ彿", sort = 4)
+    private String staffNo;
+
+    @Excel(name = "閮ㄩ棬", sort = 2)
+    private String deptName;
+
+    private Long deptId;
+
+    //鎵撳崱鐨勭粡搴�
+    private Double longitude;
+
+    //鎵撳崱鐨勭含搴�
+    private Double latitude;
+
+    //鏍囧噯涓婄彮鏃堕棿
+    @JsonFormat(pattern = "HH:mm")
+    @DateTimeFormat(pattern = "HH:mm")
+    private LocalTime startAt;
+
+    //鏍囧噯涓嬬彮鏃堕棿
+    @JsonFormat(pattern = "HH:mm")
+    @DateTimeFormat(pattern = "HH:mm")
+    private LocalTime  endAt;
+}
diff --git a/src/main/java/com/ruoyi/staff/dto/SaveStaffSchedulingDto.java b/src/main/java/com/ruoyi/staff/dto/SaveStaffSchedulingDto.java
index 7e57745..2bff5a5 100644
--- a/src/main/java/com/ruoyi/staff/dto/SaveStaffSchedulingDto.java
+++ b/src/main/java/com/ruoyi/staff/dto/SaveStaffSchedulingDto.java
@@ -1,12 +1,10 @@
 package com.ruoyi.staff.dto;
 
-import com.baomidou.mybatisplus.annotation.TableField;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
-import javax.validation.constraints.NotNull;
 import java.io.Serializable;
 import java.time.LocalDateTime;
 import java.util.Date;
diff --git a/src/main/java/com/ruoyi/staff/dto/StaffLeaveDto.java b/src/main/java/com/ruoyi/staff/dto/StaffLeaveDto.java
index 7cdf48a..3685410 100644
--- a/src/main/java/com/ruoyi/staff/dto/StaffLeaveDto.java
+++ b/src/main/java/com/ruoyi/staff/dto/StaffLeaveDto.java
@@ -1,11 +1,8 @@
 package com.ruoyi.staff.dto;
 
-import com.fasterxml.jackson.annotation.JsonFormat;
 import com.ruoyi.framework.aspectj.lang.annotation.Excel;
 import com.ruoyi.staff.pojo.StaffLeave;
 import lombok.Data;
-
-import java.util.Date;
 
 @Data
 public class StaffLeaveDto extends StaffLeave {
@@ -95,4 +92,11 @@
      */
     @Excel(name = "绱ф�ヨ仈绯讳汉鐢佃瘽", sort = 15)
     private String emergencyContactPhone;
+
+    private int count;
+
+    /**
+     * 绂昏亴鍘熷洜鏂囨湰
+     */
+    private String reasonText;
 }
diff --git a/src/main/java/com/ruoyi/staff/dto/StaffSchedulingDto.java b/src/main/java/com/ruoyi/staff/dto/StaffSchedulingDto.java
index 727b773..a60994f 100644
--- a/src/main/java/com/ruoyi/staff/dto/StaffSchedulingDto.java
+++ b/src/main/java/com/ruoyi/staff/dto/StaffSchedulingDto.java
@@ -1,7 +1,6 @@
 package com.ruoyi.staff.dto;
 
 import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.ruoyi.framework.aspectj.lang.annotation.Excel;
diff --git a/src/main/java/com/ruoyi/staff/mapper/PersonalAttendanceLocationConfigMapper.java b/src/main/java/com/ruoyi/staff/mapper/PersonalAttendanceLocationConfigMapper.java
new file mode 100644
index 0000000..bb4ed56
--- /dev/null
+++ b/src/main/java/com/ruoyi/staff/mapper/PersonalAttendanceLocationConfigMapper.java
@@ -0,0 +1,18 @@
+package com.ruoyi.staff.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.staff.pojo.PersonalAttendanceLocationConfig;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * <p>
+ * 浜哄憳鎵撳崱瑙勫垯閰嶇疆 Mapper 鎺ュ彛
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-02-11 09:41:34
+ */
+@Mapper
+public interface PersonalAttendanceLocationConfigMapper extends BaseMapper<PersonalAttendanceLocationConfig> {
+
+}
diff --git a/src/main/java/com/ruoyi/staff/mapper/PersonalAttendanceRecordsMapper.java b/src/main/java/com/ruoyi/staff/mapper/PersonalAttendanceRecordsMapper.java
index 402bb45..38dce08 100644
--- a/src/main/java/com/ruoyi/staff/mapper/PersonalAttendanceRecordsMapper.java
+++ b/src/main/java/com/ruoyi/staff/mapper/PersonalAttendanceRecordsMapper.java
@@ -1,9 +1,31 @@
 package com.ruoyi.staff.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.staff.dto.PersonalAttendanceRecordsDto;
 import com.ruoyi.staff.pojo.PersonalAttendanceRecords;
+import com.ruoyi.staff.pojo.StaffOnJob;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
 
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * <p>
+ *  Mapper 鎺ュ彛
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-02-09 01:20:07
+ */
 @Mapper
 public interface PersonalAttendanceRecordsMapper extends BaseMapper<PersonalAttendanceRecords> {
+    IPage<PersonalAttendanceRecordsDto> listPage(Page page, @Param("params") PersonalAttendanceRecordsDto personalAttendanceRecordsDto);
+
+    List<StaffOnJob> selectStaffWithoutAttendanceRecordBeforeTime(@Param("date") LocalDate date, @Param("entryDeadline") LocalDateTime entryDeadline);
+
+    boolean existsAttendanceRecord(@Param("staffOnJobId") Long staffOnJobId, @Param("date") LocalDate date);
 }
diff --git a/src/main/java/com/ruoyi/staff/mapper/StaffContractMapper.java b/src/main/java/com/ruoyi/staff/mapper/StaffContractMapper.java
index 9545082..1d8b83f 100644
--- a/src/main/java/com/ruoyi/staff/mapper/StaffContractMapper.java
+++ b/src/main/java/com/ruoyi/staff/mapper/StaffContractMapper.java
@@ -4,13 +4,9 @@
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ruoyi.staff.dto.StaffContractDto;
-import com.ruoyi.staff.dto.StaffOnJobDto;
 import com.ruoyi.staff.pojo.StaffContract;
-import com.ruoyi.staff.pojo.StaffOnJob;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
-
-import java.util.List;
 
 @Mapper
 public interface StaffContractMapper extends BaseMapper<StaffContract> {
diff --git a/src/main/java/com/ruoyi/staff/mapper/StaffLeaveMapper.java b/src/main/java/com/ruoyi/staff/mapper/StaffLeaveMapper.java
index e8d2854..0ba632a 100644
--- a/src/main/java/com/ruoyi/staff/mapper/StaffLeaveMapper.java
+++ b/src/main/java/com/ruoyi/staff/mapper/StaffLeaveMapper.java
@@ -4,12 +4,11 @@
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ruoyi.staff.dto.StaffLeaveDto;
-import com.ruoyi.staff.dto.StaffOnJobDto;
 import com.ruoyi.staff.pojo.StaffLeave;
-import com.ruoyi.staff.pojo.StaffOnJob;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
+import java.time.LocalDate;
 import java.util.List;
 
 
@@ -18,4 +17,8 @@
     IPage<StaffLeaveDto> staffLeaveListPage(Page page, @Param("c") StaffLeaveDto staffLeaveDto);
 
     List<StaffLeaveDto> staffLeaveList(@Param("c") StaffLeaveDto staffLeaveDto);
+
+    List<StaffLeaveDto> staffLeaveReasonAnalytics();
+
+    Integer countLeaveByMonth(@Param("monthStart") LocalDate monthStart, @Param("monthEnd") LocalDate monthEnd);
 }
diff --git a/src/main/java/com/ruoyi/staff/mapper/StaffOnJobMapper.java b/src/main/java/com/ruoyi/staff/mapper/StaffOnJobMapper.java
index 1451487..de8a3c0 100644
--- a/src/main/java/com/ruoyi/staff/mapper/StaffOnJobMapper.java
+++ b/src/main/java/com/ruoyi/staff/mapper/StaffOnJobMapper.java
@@ -8,6 +8,7 @@
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
+import java.time.LocalDate;
 import java.util.List;
 
 @Mapper
@@ -16,4 +17,29 @@
     IPage<StaffOnJobDto> staffOnJobListPage(Page page, @Param("staffOnJob") StaffOnJob staffOnJob);
 
     List<StaffOnJobDto> staffOnJobList(@Param("staffOnJob") StaffOnJob staffOnJob);
-}
+
+    /**
+     * 缁熻鎸囧畾鏃ユ湡鐨勫湪鑱屽憳宸ユ暟
+     *
+     * @param date 鏃ユ湡
+     * @return 鍦ㄨ亴鍛樺伐鏁�
+     */
+    Integer countOnJobStaffByDate(@Param("date") LocalDate date);
+
+    /**
+     * 缁熻鎸囧畾鏈堜唤鐨勬柊鍏ヨ亴鍛樺伐鏁�
+     *
+     * @param monthStart 鏈堜唤寮�濮嬫棩鏈�
+     * @param monthEnd   鏈堜唤缁撴潫鏃ユ湡
+     * @return 鏂板叆鑱屽憳宸ユ暟
+     */
+    Integer countNewHireByMonth(@Param("monthStart") LocalDate monthStart, @Param("monthEnd") LocalDate monthEnd);
+
+    /**
+     * 鏍规嵁鍛樺伐濮撳悕鏌ヨ鍛樺伐淇℃伅
+     *
+     * @param staffName 鍛樺伐濮撳悕
+     * @return 鍛樺伐鏁版嵁
+     */
+    StaffOnJob selectStaffByNickName(String staffName);
+}
\ No newline at end of file
diff --git a/src/main/java/com/ruoyi/staff/pojo/HolidayApplication.java b/src/main/java/com/ruoyi/staff/pojo/HolidayApplication.java
index b76893e..40ef116 100644
--- a/src/main/java/com/ruoyi/staff/pojo/HolidayApplication.java
+++ b/src/main/java/com/ruoyi/staff/pojo/HolidayApplication.java
@@ -6,7 +6,6 @@
 import org.springframework.format.annotation.DateTimeFormat;
 
 import java.time.LocalDate;
-import java.time.LocalTime;
 
 @Data
 @TableName("holiday_application")
diff --git a/src/main/java/com/ruoyi/staff/pojo/PersonalAttendanceLocationConfig.java b/src/main/java/com/ruoyi/staff/pojo/PersonalAttendanceLocationConfig.java
new file mode 100644
index 0000000..c4808d2
--- /dev/null
+++ b/src/main/java/com/ruoyi/staff/pojo/PersonalAttendanceLocationConfig.java
@@ -0,0 +1,59 @@
+package com.ruoyi.staff.pojo;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.time.LocalTime;
+
+/**
+ * <p>
+ * 浜哄憳鎵撳崱瑙勫垯閰嶇疆
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-02-11 09:41:34
+ */
+@Getter
+@Setter
+@TableName("personal_attendance_location_config")
+@ApiModel(value = "PersonalAttendanceLocationConfig瀵硅薄", description = "浜哄憳鎵撳崱瑙勫垯閰嶇疆")
+public class PersonalAttendanceLocationConfig implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty("閮ㄩ棬id")
+    private Integer sysDeptId;
+
+    @ApiModelProperty("鍦扮偣鍚嶇О")
+    private String locationName;
+
+    @ApiModelProperty("缁忓害")
+    private Double longitude;
+
+    @ApiModelProperty("绾害")
+    private Double latitude;
+
+    @ApiModelProperty("鎵撳崱鑼冨洿")
+    private Double radius;
+
+    @ApiModelProperty("涓婄彮鏃堕棿")
+    @JsonFormat(pattern = "HH:mm")
+    @DateTimeFormat(pattern = "HH:mm")
+    private LocalTime startAt;
+
+    @ApiModelProperty("涓嬬彮鏃堕棿")
+    @JsonFormat(pattern = "HH:mm")
+    @DateTimeFormat(pattern = "HH:mm")
+    private LocalTime  endAt;
+}
diff --git a/src/main/java/com/ruoyi/staff/pojo/PersonalAttendanceRecords.java b/src/main/java/com/ruoyi/staff/pojo/PersonalAttendanceRecords.java
index cbf11b7..e3f4bd9 100644
--- a/src/main/java/com/ruoyi/staff/pojo/PersonalAttendanceRecords.java
+++ b/src/main/java/com/ruoyi/staff/pojo/PersonalAttendanceRecords.java
@@ -3,50 +3,78 @@
 import com.baomidou.mybatisplus.annotation.*;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.ruoyi.framework.aspectj.lang.annotation.Excel;
-import lombok.Data;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
 import org.springframework.format.annotation.DateTimeFormat;
 
 import java.io.Serializable;
+import java.math.BigDecimal;
 import java.time.LocalDate;
-import java.time.LocalTime;
+import java.time.LocalDateTime;
 
-@Data
+/**
+ * <p>
+ * 
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-02-09 01:20:07
+ */
+@Getter
+@Setter
 @TableName("personal_attendance_records")
+@ApiModel(value = "PersonalAttendanceRecords瀵硅薄", description = "")
 public class PersonalAttendanceRecords implements Serializable {
-    /**
-     * 搴忓彿
-     */
-    @TableId(type = IdType.AUTO)
+
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
     private Long id;
-    /**
-     * 鏃ユ湡
-     */
+
+    @ApiModelProperty("鍛樺伐鍦ㄨ亴id")
+    private Long staffOnJobId;
+
+    @ApiModelProperty("鏃ユ湡")
     @JsonFormat(pattern = "yyyy-MM-dd")
     @DateTimeFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "鏃ユ湡", sort = 1, dateFormat = "yyyy-MM-dd")
     private LocalDate date;
-    /**
-     * 绛惧埌鏃堕棿
-     */
+
+    @ApiModelProperty("宸ヤ綔寮�濮嬫椂闂�")
     @JsonFormat(pattern = "HH:mm")
     @DateTimeFormat(pattern = "HH:mm")
-    private LocalTime checkIn;
-    /**
-     * 绛鹃��鏃堕棿
-     */
+    @Excel(name = "涓婄彮鏃堕棿", sort = 5, dateFormat = "HH:mm")
+    private LocalDateTime workStartAt;
+
+    @ApiModelProperty("宸ヤ綔缁撴潫鏃堕棿")
     @JsonFormat(pattern = "HH:mm")
     @DateTimeFormat(pattern = "HH:mm")
-    private LocalTime checkOut;
-    /**
-     * 宸ヤ綔鏃堕暱
-     */
-    private String workHours;
-    /**
-     * 鐘舵��
-     */
-    private String status;
-    /**
-     * 绉熸埛ID
-     */
+    @Excel(name = "涓嬬彮鏃堕棿", sort = 6, dateFormat = "HH:mm")
+    private LocalDateTime workEndAt;
+
+    @ApiModelProperty("宸ヤ綔鏃堕暱")
+    @Excel(name = "宸ユ椂(灏忔椂)", sort = 7)
+    private BigDecimal workHours;
+
+    @ApiModelProperty("鐘舵�� 0姝e父 1杩熷埌 2鏃╅�� 3杩熷埌鏃╅�� 4缂哄嫟")
+    @Excel(name = "鐘舵��", sort = 8,readConverterExp = "0=姝e父,1=杩熷埌,2=鏃╅��,3=杩熷埌銆佹棭閫�,4=缂哄嫟")
+    private Integer status;
+
+    @ApiModelProperty("澶囨敞")
+    @Excel(name = "澶囨敞", sort = 9)
+    private String remark;
+
+    @ApiModelProperty("绉熸埛id")
     @TableField(fill = FieldFill.INSERT)
     private Long tenantId;
+
+    @ApiModelProperty("褰曞叆鏃堕棿")
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("鏇存柊鏃堕棿")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
 }
diff --git a/src/main/java/com/ruoyi/staff/pojo/StaffContract.java b/src/main/java/com/ruoyi/staff/pojo/StaffContract.java
index 9b2afee..532a59e 100644
--- a/src/main/java/com/ruoyi/staff/pojo/StaffContract.java
+++ b/src/main/java/com/ruoyi/staff/pojo/StaffContract.java
@@ -4,7 +4,6 @@
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.fasterxml.jackson.annotation.JsonFormat;
-import com.ruoyi.framework.aspectj.lang.annotation.Excel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import org.springframework.format.annotation.DateTimeFormat;
diff --git a/src/main/java/com/ruoyi/staff/pojo/StaffLeave.java b/src/main/java/com/ruoyi/staff/pojo/StaffLeave.java
index 3f5532c..f5d6326 100644
--- a/src/main/java/com/ruoyi/staff/pojo/StaffLeave.java
+++ b/src/main/java/com/ruoyi/staff/pojo/StaffLeave.java
@@ -3,13 +3,10 @@
 import com.baomidou.mybatisplus.annotation.FieldFill;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableName;
-import com.fasterxml.jackson.annotation.JsonFormat;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
-import org.springframework.format.annotation.DateTimeFormat;
 
 import java.time.LocalDateTime;
-import java.util.Date;
 
 @TableName("staff_leave")
 @Data
diff --git a/src/main/java/com/ruoyi/staff/pojo/StaffOnJob.java b/src/main/java/com/ruoyi/staff/pojo/StaffOnJob.java
index 31d74b9..785b722 100644
--- a/src/main/java/com/ruoyi/staff/pojo/StaffOnJob.java
+++ b/src/main/java/com/ruoyi/staff/pojo/StaffOnJob.java
@@ -66,7 +66,7 @@
      /**
      * 閮ㄩ棬
      */
-    private Integer sysDeptId;
+    private Long sysDeptId;
 
     /**
      * 瀹跺涵浣忓潃
diff --git a/src/main/java/com/ruoyi/staff/service/AnalyticsService.java b/src/main/java/com/ruoyi/staff/service/AnalyticsService.java
new file mode 100644
index 0000000..ee8ff83
--- /dev/null
+++ b/src/main/java/com/ruoyi/staff/service/AnalyticsService.java
@@ -0,0 +1,20 @@
+package com.ruoyi.staff.service;
+
+import com.ruoyi.staff.dto.StaffLeaveDto;
+import com.ruoyi.staff.vo.MonthlyTurnoverRateVo;
+import com.ruoyi.staff.vo.TotalTurnoverRateVo;
+
+import java.util.List;
+
+public interface AnalyticsService {
+
+    List<StaffLeaveDto> staffLeaveReasonAnalytics();
+
+    List<MonthlyTurnoverRateVo> getMonthlyTurnoverRateFor12Months();
+
+    /**
+     * 鏌ヨ鎬绘祦鍔ㄧ巼銆佹祦澶辩巼浠ュ強鍦ㄨ亴鍛樺伐鏁�
+     * @return 鎬荤粺璁$粨鏋�
+     */
+    TotalTurnoverRateVo getTotalStatistic();
+}
diff --git a/src/main/java/com/ruoyi/staff/service/PersonalAttendanceLocationConfigService.java b/src/main/java/com/ruoyi/staff/service/PersonalAttendanceLocationConfigService.java
new file mode 100644
index 0000000..2451cf9
--- /dev/null
+++ b/src/main/java/com/ruoyi/staff/service/PersonalAttendanceLocationConfigService.java
@@ -0,0 +1,16 @@
+package com.ruoyi.staff.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.staff.pojo.PersonalAttendanceLocationConfig;
+
+/**
+ * <p>
+ * 浜哄憳鎵撳崱瑙勫垯閰嶇疆 鏈嶅姟绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-02-11 09:41:34
+ */
+public interface PersonalAttendanceLocationConfigService extends IService<PersonalAttendanceLocationConfig> {
+
+}
diff --git a/src/main/java/com/ruoyi/staff/service/PersonalAttendanceRecordsService.java b/src/main/java/com/ruoyi/staff/service/PersonalAttendanceRecordsService.java
index c39b007..8b14da3 100644
--- a/src/main/java/com/ruoyi/staff/service/PersonalAttendanceRecordsService.java
+++ b/src/main/java/com/ruoyi/staff/service/PersonalAttendanceRecordsService.java
@@ -3,8 +3,25 @@
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.staff.dto.PersonalAttendanceRecordsDto;
 import com.ruoyi.staff.pojo.PersonalAttendanceRecords;
 
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * <p>
+ *  鏈嶅姟绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-02-09 01:20:07
+ */
 public interface PersonalAttendanceRecordsService extends IService<PersonalAttendanceRecords> {
-    IPage listPage(Page page, PersonalAttendanceRecords personalAttendanceRecords);
+    IPage<PersonalAttendanceRecordsDto> listPage(Page page, PersonalAttendanceRecordsDto personalAttendanceRecordsDto);
+
+    int add(PersonalAttendanceRecordsDto personalAttendanceRecordsDto);
+
+    PersonalAttendanceRecordsDto todayInfo(PersonalAttendanceRecordsDto personalAttendanceRecordsDto);
+
+    void export(HttpServletResponse response, PersonalAttendanceRecordsDto personalAttendanceRecordsDto);
 }
diff --git a/src/main/java/com/ruoyi/staff/service/impl/AnalyticsServiceImpl.java b/src/main/java/com/ruoyi/staff/service/impl/AnalyticsServiceImpl.java
new file mode 100644
index 0000000..ff74b0f
--- /dev/null
+++ b/src/main/java/com/ruoyi/staff/service/impl/AnalyticsServiceImpl.java
@@ -0,0 +1,176 @@
+package com.ruoyi.staff.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.enums.StaffLeaveReason;
+import com.ruoyi.staff.dto.StaffLeaveDto;
+import com.ruoyi.staff.mapper.StaffLeaveMapper;
+import com.ruoyi.staff.mapper.StaffOnJobMapper;
+import com.ruoyi.staff.pojo.StaffLeave;
+import com.ruoyi.staff.service.AnalyticsService;
+import com.ruoyi.staff.vo.MonthlyTurnoverRateVo;
+import com.ruoyi.staff.vo.TotalTurnoverRateVo;
+import lombok.AllArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@AllArgsConstructor
+@Service
+public class AnalyticsServiceImpl extends ServiceImpl<StaffLeaveMapper, StaffLeave> implements AnalyticsService {
+    @Autowired
+    private StaffLeaveMapper staffLeaveMapper;
+
+    @Autowired
+    private StaffOnJobMapper staffOnJobMapper;
+
+    @Override
+    public List<StaffLeaveDto> staffLeaveReasonAnalytics() {
+        List<StaffLeaveDto> dbResult = staffLeaveMapper.staffLeaveReasonAnalytics();
+
+        // 鍒涘缓涓�涓狹ap鐢ㄤ簬瀛樺偍鎵�鏈夋灇涓惧師鍥犵殑鏁伴噺锛岄粯璁ゅ�间负0
+        Map<String, Integer> reasonCountMap = new HashMap<>();
+        for (StaffLeaveReason reasonEnum : StaffLeaveReason.values()) {
+            reasonCountMap.put(reasonEnum.getCode(), 0);
+        }
+
+        // 灏嗘暟鎹簱鏌ヨ缁撴灉鍚堝苟鍒癕ap涓�
+        for (StaffLeaveDto dto : dbResult) {
+            String reasonCode = dto.getReason();
+            if (reasonCountMap.containsKey(reasonCode)) {
+                reasonCountMap.put(reasonCode, dto.getCount());
+            }
+        }
+
+        // 灏哅ap杞崲涓篖ist<StaffLeaveDto>
+        List<StaffLeaveDto> result = new ArrayList<>();
+        for (StaffLeaveReason reasonEnum : StaffLeaveReason.values()) {
+            StaffLeaveDto dto = new StaffLeaveDto();
+            dto.setReason(reasonEnum.getCode());
+            dto.setCount(reasonCountMap.get(reasonEnum.getCode()));
+            dto.setReasonText(reasonEnum.getInfo());
+            result.add(dto);
+        }
+
+        return result;
+    }
+
+    @Override
+    public List<MonthlyTurnoverRateVo> getMonthlyTurnoverRateFor12Months() {
+        List<MonthlyTurnoverRateVo> result = new ArrayList<>();
+        LocalDate now = LocalDate.now();
+        DateTimeFormatter monthFormatter = DateTimeFormatter.ofPattern("yyyy-MM");
+
+        // 璁$畻杩�12涓湀鐨勬暟鎹�
+        for (int i = 11; i >= 0; i--) {
+            LocalDate currentMonth = now.minusMonths(i);
+            LocalDate monthStart = currentMonth.withDayOfMonth(1);
+            LocalDate monthEnd = currentMonth.withDayOfMonth(currentMonth.lengthOfMonth());
+
+            MonthlyTurnoverRateVo vo = new MonthlyTurnoverRateVo();
+            vo.setMonth(currentMonth.format(monthFormatter));
+            vo.setMonthStartDate(monthStart);
+            vo.setMonthEndDate(monthEnd);
+
+            // 鏈堝垵鍛樺伐鏁帮紙涓婃湀鏈湪鑱屽憳宸ユ暟锛�
+            LocalDate lastMonthEnd = monthStart.minusDays(1);
+            Integer beginMonthStaffCount = staffOnJobMapper.countOnJobStaffByDate(lastMonthEnd);
+            vo.setBeginMonthStaffCount(beginMonthStaffCount != null ? beginMonthStaffCount : 0);
+
+            // 鏈堟湯鍛樺伐鏁�
+            Integer endMonthStaffCount = staffOnJobMapper.countOnJobStaffByDate(monthEnd);
+            vo.setEndMonthStaffCount(endMonthStaffCount != null ? endMonthStaffCount : 0);
+
+            // 鏈堝害鍏ヨ亴鍛樺伐鏁�
+            Integer newHireCount = staffOnJobMapper.countNewHireByMonth(monthStart, monthEnd);
+            vo.setNewHireCount(newHireCount != null ? newHireCount : 0);
+
+            // 鏈堝害绂昏亴鍛樺伐鏁�
+            Integer leaveCount = staffLeaveMapper.countLeaveByMonth(monthStart, monthEnd);
+            vo.setLeaveCount(leaveCount != null ? leaveCount : 0);
+
+            // 璁$畻褰撴湡骞冲潎鍦ㄨ亴浜烘暟 = (鏈堝垵鍛樺伐鏁� + 鏈堟湯鍛樺伐鏁�) / 2
+            Double averageStaffCount = (vo.getBeginMonthStaffCount() + vo.getEndMonthStaffCount()) / 2.0;
+
+            // 璁$畻娴佸け鐜囷細娴佸け鐜� = 鏈堝害绂昏亴鍛樺伐鏁� / 褰撴湡骞冲潎鍦ㄨ亴浜烘暟 * 100%
+            Double turnoverRate = 0.0;
+            if (averageStaffCount > 0) {
+                turnoverRate = (double) vo.getLeaveCount() / averageStaffCount * 100;
+                // 淇濈暀涓や綅灏忔暟
+                turnoverRate = Math.round(turnoverRate * 100.0) / 100.0;
+            }
+            vo.setTurnoverRate(turnoverRate);
+
+            // 璁$畻娴佸姩鐜囷細娴佸姩鐜� = (鏈堝害鍏ヨ亴鍛樺伐鏁� + 鏈堝害绂昏亴鍛樺伐鏁�) / 褰撴湡骞冲潎鍦ㄨ亴浜烘暟 * 100%
+            Double flowRate = 0.0;
+            if (averageStaffCount > 0) {
+                flowRate = (double) (vo.getNewHireCount() + vo.getLeaveCount()) / averageStaffCount * 100;
+                // 淇濈暀涓や綅灏忔暟
+                flowRate = Math.round(flowRate * 100.0) / 100.0;
+            }
+            vo.setFlowRate(flowRate);
+
+            result.add(vo);
+        }
+
+        return result;
+    }
+
+    @Override
+    public TotalTurnoverRateVo getTotalStatistic() {
+        TotalTurnoverRateVo result = new TotalTurnoverRateVo();
+        LocalDate now = LocalDate.now();
+
+        // 鑾峰彇褰撳墠鍦ㄨ亴鍛樺伐鏁�
+        Integer currentOnJobCount = staffOnJobMapper.countOnJobStaffByDate(now);
+        result.setCurrentOnJobCount(currentOnJobCount);
+
+        // 鑾峰彇鏈湀鐨勫紑濮嬪拰缁撴潫鏃ユ湡
+        LocalDate monthStartDate = now.withDayOfMonth(1);
+        LocalDate monthEndDate = now.withDayOfMonth(now.lengthOfMonth());
+
+        // 鑾峰彇鏈堝垵鍛樺伐鏁帮紙鍗充笂鏈堟湯鍛樺伐鏁帮級
+        Integer beginMonthStaffCount = staffOnJobMapper.countOnJobStaffByDate(monthStartDate.minusDays(1));
+        beginMonthStaffCount = beginMonthStaffCount != null ? beginMonthStaffCount : 0;
+
+        // 鑾峰彇鏈堟湯鍛樺伐鏁�
+        Integer endMonthStaffCount = staffOnJobMapper.countOnJobStaffByDate(monthEndDate);
+        endMonthStaffCount = endMonthStaffCount != null ? endMonthStaffCount : 0;
+
+        // 鑾峰彇鏈湀鏂板叆鑱屽憳宸ユ暟
+        Integer newHireCount = staffOnJobMapper.countNewHireByMonth(monthStartDate, monthEndDate);
+        newHireCount = newHireCount != null ? newHireCount : 0;
+
+        // 鑾峰彇鏈湀绂昏亴鍛樺伐鏁�
+        Integer leaveCount = staffLeaveMapper.countLeaveByMonth(monthStartDate, monthEndDate);
+        leaveCount = leaveCount != null ? leaveCount : 0;
+
+        // 璁$畻褰撴湡骞冲潎鍦ㄨ亴浜烘暟 = (鏈堝垵鍛樺伐鏁� + 鏈堟湯鍛樺伐鏁�) / 2
+        Double averageStaffCount = (beginMonthStaffCount + endMonthStaffCount) / 2.0;
+
+        // 璁$畻鎬绘祦鍔ㄧ巼 = (鍏ヨ亴浜烘暟 + 绂昏亴浜烘暟) / 褰撴湡骞冲潎鍦ㄨ亴浜烘暟 * 100%
+        Double totalFlowRate = 0.0;
+        if (averageStaffCount > 0) {
+            totalFlowRate = (double) (newHireCount + leaveCount) / averageStaffCount * 100;
+            // 淇濈暀涓や綅灏忔暟
+            totalFlowRate = Math.round(totalFlowRate * 100.0) / 100.0;
+        }
+        result.setTotalFlowRate(totalFlowRate);
+
+        // 璁$畻鎬绘祦澶辩巼 = 绂昏亴浜烘暟 / 褰撴湡骞冲潎鍦ㄨ亴浜烘暟 * 100%
+        Double totalTurnoverRate = 0.0;
+        if (averageStaffCount > 0) {
+            totalTurnoverRate = (double) leaveCount / averageStaffCount * 100;
+            // 淇濈暀涓や綅灏忔暟
+            totalTurnoverRate = Math.round(totalTurnoverRate * 100.0) / 100.0;
+        }
+        result.setTotalTurnoverRate(totalTurnoverRate);
+
+        return result;
+    }
+}
diff --git a/src/main/java/com/ruoyi/staff/service/impl/PersonalAttendanceLocationConfigServiceImpl.java b/src/main/java/com/ruoyi/staff/service/impl/PersonalAttendanceLocationConfigServiceImpl.java
new file mode 100644
index 0000000..647ed78
--- /dev/null
+++ b/src/main/java/com/ruoyi/staff/service/impl/PersonalAttendanceLocationConfigServiceImpl.java
@@ -0,0 +1,20 @@
+package com.ruoyi.staff.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.staff.mapper.PersonalAttendanceLocationConfigMapper;
+import com.ruoyi.staff.pojo.PersonalAttendanceLocationConfig;
+import com.ruoyi.staff.service.PersonalAttendanceLocationConfigService;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 浜哄憳鎵撳崱瑙勫垯閰嶇疆 鏈嶅姟瀹炵幇绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-02-11 09:41:34
+ */
+@Service
+public class PersonalAttendanceLocationConfigServiceImpl extends ServiceImpl<PersonalAttendanceLocationConfigMapper, PersonalAttendanceLocationConfig> implements PersonalAttendanceLocationConfigService {
+
+}
diff --git a/src/main/java/com/ruoyi/staff/service/impl/PersonalAttendanceRecordsServiceImpl.java b/src/main/java/com/ruoyi/staff/service/impl/PersonalAttendanceRecordsServiceImpl.java
index 1fc363b..c68be3e 100644
--- a/src/main/java/com/ruoyi/staff/service/impl/PersonalAttendanceRecordsServiceImpl.java
+++ b/src/main/java/com/ruoyi/staff/service/impl/PersonalAttendanceRecordsServiceImpl.java
@@ -2,22 +2,257 @@
 
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.exception.base.BaseException;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.project.system.domain.SysDept;
+import com.ruoyi.project.system.mapper.SysDeptMapper;
+import com.ruoyi.project.system.service.ISysDictDataService;
+import com.ruoyi.staff.dto.PersonalAttendanceRecordsDto;
+import com.ruoyi.staff.mapper.PersonalAttendanceLocationConfigMapper;
 import com.ruoyi.staff.mapper.PersonalAttendanceRecordsMapper;
+import com.ruoyi.staff.mapper.StaffOnJobMapper;
+import com.ruoyi.staff.pojo.PersonalAttendanceLocationConfig;
 import com.ruoyi.staff.pojo.PersonalAttendanceRecords;
+import com.ruoyi.staff.pojo.StaffOnJob;
 import com.ruoyi.staff.service.PersonalAttendanceRecordsService;
+import com.ruoyi.staff.utils.LocationUtils;
+import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
+import javax.servlet.http.HttpServletResponse;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.List;
+
+/**
+ * <p>
+ *  鏈嶅姟瀹炵幇绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-02-09 01:20:07
+ */
 @Service
+@Transactional(rollbackFor = Exception.class)
 public class PersonalAttendanceRecordsServiceImpl extends ServiceImpl<PersonalAttendanceRecordsMapper, PersonalAttendanceRecords> implements PersonalAttendanceRecordsService {
     @Autowired
     private PersonalAttendanceRecordsMapper personalAttendanceRecordsMapper;
 
+    @Autowired
+    private StaffOnJobMapper staffOnJobMapper;
+
+    @Autowired
+    private PersonalAttendanceLocationConfigMapper personalAttendanceLocationConfigMapper;
+
+    @Autowired
+    private ISysDictDataService dictDataService;
+
+    @Autowired
+    private SysDeptMapper sysDeptMapper;
+
     @Override
-    public IPage listPage(Page page, PersonalAttendanceRecords personalAttendanceRecords) {
-//        return personalAttendanceRecordsMapper.ListPage(page, personalAttendanceRecords);
-        return baseMapper.selectPage(page, new QueryWrapper<>(personalAttendanceRecords));
+    public int add(PersonalAttendanceRecordsDto personalAttendanceRecordsDto) {
+        // 褰撳墠鏃堕棿
+        LocalDate currentDate = LocalDate.now();
+        LocalDateTime currentDateTime = LocalDateTime.now();
+        /*鏌ヨ鍛樺伐淇℃伅*/
+        QueryWrapper<StaffOnJob> staffQueryWrapper = new QueryWrapper<>();
+        staffQueryWrapper.eq("staff_no", SecurityUtils.getUsername());
+        staffQueryWrapper.eq("staff_state", 1);//鍦ㄨ亴
+        StaffOnJob staffOnJob = staffOnJobMapper.selectOne(staffQueryWrapper);
+        if (staffOnJob == null) {
+            throw new BaseException("褰撳墠鐢ㄦ埛娌℃湁瀵瑰簲鐨勫憳宸ヤ俊鎭�");
+        }
+        /*鍒ゆ柇鎵撳崱浣嶇疆鏄惁鍦ㄨ鍒欒寖鍥村唴*/
+        List<PersonalAttendanceLocationConfig> personalAttendanceLocationConfigs = personalAttendanceLocationConfigMapper.selectList(Wrappers.<PersonalAttendanceLocationConfig>lambdaQuery()
+                .eq(PersonalAttendanceLocationConfig::getSysDeptId, staffOnJob.getSysDeptId())
+                .orderByDesc(PersonalAttendanceLocationConfig::getId));
+        if (personalAttendanceLocationConfigs == null || personalAttendanceLocationConfigs.isEmpty()) {
+            throw new BaseException("褰撳墠閮ㄩ棬娌℃湁璁剧疆鎵撳崱瑙勫垯");
+        }
+        Double punchLongitude = personalAttendanceRecordsDto.getLongitude(); //鎵撳崱鐨勭粡搴�
+        Double punchLatitude = personalAttendanceRecordsDto.getLatitude(); // 鎵撳崱鐨勭含搴�
+        if (punchLongitude == null || punchLatitude == null) {
+            throw new BaseException("鎵撳崱澶辫触锛氭湭鑾峰彇鍒版偍鐨勪綅缃俊鎭紝璇峰紑鍚畾浣嶆潈闄�");
+        }
+        //璁$畻鎵撳崱浣嶇疆涓庤�冨嫟鐐圭殑璺濈
+        PersonalAttendanceLocationConfig locationConfig = personalAttendanceLocationConfigs.get(0);//鑾峰彇鏈�鏂扮殑涓�鏉℃暟鎹�
+        double allowedRadius = locationConfig.getRadius(); // 鍏佽鐨勮寖鍥达紙绫筹級
+        double actualDistance = LocationUtils.calculateDistance(
+                punchLatitude, punchLongitude, // 鍛樺伐鎵撳崱鐨勭粡绾害
+                locationConfig.getLatitude(), locationConfig.getLongitude() // 鑰冨嫟鐐圭殑缁忕含搴�
+        );
+        //鍒ゆ柇鏄惁鍦ㄨ寖鍥村唴
+        if (actualDistance > allowedRadius) {
+            throw new BaseException(String.format("鎵撳崱澶辫触锛氭偍褰撳墠浣嶇疆璺濈鑰冨嫟鐐�%.2f绫筹紝瓒呭嚭鍏佽鑼冨洿锛�%s绫筹級", actualDistance, allowedRadius));
+        }
+        /*鍒ゆ柇鎵撳崱鏃堕棿*/
+        // 鏍规嵁鍛樺伐ID鍜屽綋鍓嶆棩鏈熸煡璇㈡墦鍗¤褰�
+        QueryWrapper<PersonalAttendanceRecords> attendanceQueryWrapper = new QueryWrapper<>();
+        attendanceQueryWrapper.eq("staff_on_job_id", staffOnJob.getId())
+                .eq("date", currentDate);
+        PersonalAttendanceRecords attendanceRecord = personalAttendanceRecordsMapper.selectOne(attendanceQueryWrapper);
+        // 鏍规嵁鑰冨嫟鏃堕棿鍒ゆ柇杩熷埌鏃╅��
+        if (attendanceRecord == null) {
+            // 涓嶅瓨鍦ㄦ墦鍗¤褰曪紝鍒涘缓鏂拌褰�
+            PersonalAttendanceRecords personalAttendanceRecords = new PersonalAttendanceRecords();
+            personalAttendanceRecords.setStaffOnJobId(staffOnJob.getId());
+            personalAttendanceRecords.setDate(currentDate);
+            personalAttendanceRecords.setWorkStartAt(currentDateTime);
+            personalAttendanceRecords.setStatus(determineAttendanceStatus(personalAttendanceRecords, true,locationConfig));
+            personalAttendanceRecords.setRemark(personalAttendanceRecords.getRemark());
+            personalAttendanceRecords.setTenantId(staffOnJob.getTenantId());
+            return personalAttendanceRecordsMapper.insert(personalAttendanceRecords);
+        } else {
+            if (attendanceRecord.getWorkEndAt() == null) {
+                // 鏇存柊宸ヤ綔缁撴潫鏃堕棿鍜屽伐浣滄椂闀�
+                attendanceRecord.setWorkEndAt(currentDateTime);
+                // 璁$畻宸ヤ綔鏃堕暱锛堢簿纭埌鍒嗛挓锛屼繚鐣�2浣嶅皬鏁帮級
+                LocalDateTime startTime = attendanceRecord.getWorkStartAt();
+                LocalDateTime endTime = attendanceRecord.getWorkEndAt();
+                // 璁$畻涓や釜鏃堕棿涔嬮棿鐨勫垎閽熸暟
+                long totalMinutes = java.time.Duration.between(startTime, endTime).toMinutes();
+                BigDecimal workHours = BigDecimal.valueOf(totalMinutes)
+                        .divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP);
+                attendanceRecord.setWorkHours(workHours);
+                // 鏇存柊鑰冨嫟鐘舵��
+                attendanceRecord.setStatus(determineAttendanceStatus(attendanceRecord, false,locationConfig));
+                return personalAttendanceRecordsMapper.updateById(attendanceRecord);
+            } else {
+                throw new BaseException("鎮ㄥ凡缁忔墦杩囧崱浜�,鏃犻渶閲嶅鎵撳崱!!!");
+            }
+        }
+    }
+
+    // 鏍规嵁瀹為檯鏃堕棿鍜屾槸鍚︿笂鐝椂闂村垽鏂�冨嫟鐘舵��
+    // 0 姝e父 1 杩熷埌 2 鏃╅�� 3 杩熷埌鏃╅�� 4 缂哄嫟
+    private Integer determineAttendanceStatus(PersonalAttendanceRecords attendanceRecord, boolean isStart,PersonalAttendanceLocationConfig locationConfig) {
+        //鍒ゆ柇鏄笂鐝墦鍗¤繕鏄笅鐝墦鍗�
+        LocalDateTime actualTime = isStart ? attendanceRecord.getWorkStartAt() : attendanceRecord.getWorkEndAt();
+        try {
+            // 鑾峰彇鑰冨嫟鏃堕棿閰嶇疆
+            LocalTime startAt = locationConfig.getStartAt();//涓婄彮鏃堕棿
+            LocalTime  endAt = locationConfig.getEndAt();//涓嬬彮鏃堕棿
+            LocalTime  timeConfig = isStart ? startAt : endAt;
+            // 瑙f瀽灏忔椂鍜屽垎閽�
+            int standardHour = timeConfig.getHour();
+            int standardMinute = timeConfig.getMinute();
+            // 鑾峰彇瀹為檯鏃堕棿鐨勬椂鍒�
+            int actualHour = actualTime.getHour();
+            int actualMinute = actualTime.getMinute();
+            // 鍒ゆ柇鐘舵��
+            if (isStart) {
+                // 涓婄彮鎵撳崱锛氳秴杩囨爣鍑嗘椂闂寸畻杩熷埌
+                if (actualHour > standardHour || (actualHour == standardHour && actualMinute > standardMinute)) {
+                    return 1; // 杩熷埌
+                }
+            } else {
+                // 涓嬬彮鎵撳崱锛氭棭浜庢爣鍑嗘椂闂寸畻鏃╅��
+                if (actualHour < standardHour || (actualHour == standardHour && actualMinute < standardMinute)) {
+                    if (attendanceRecord.getStatus() == 1) {
+                        return 3; // 杩熷埌鏃╅��
+                    }
+                    return 2; // 鏃╅��
+                }else if (attendanceRecord.getStatus() == 1) {
+                    return 1; // 涓嬬彮鎵撳崱姝e父浣嗘槸涓婄彮杩熷埌
+                }
+            }
+            return 0; // 姝e父
+        } catch (Exception e) {
+            // 濡傛灉鑾峰彇閰嶇疆澶辫触锛岄粯璁よ繑鍥炴甯哥姸鎬�
+            log.warn("鑾峰彇鑰冨嫟鏃堕棿閰嶇疆澶辫触锛屼娇鐢ㄩ粯璁ょ姸鎬侊細" + e.getMessage());
+            return 0;
+        }
+    }
+
+    @Override
+    public IPage<PersonalAttendanceRecordsDto> listPage(Page page, PersonalAttendanceRecordsDto personalAttendanceRecordsDto) {
+        boolean admin = SecurityUtils.isAdmin(SecurityUtils.getUserId());
+        if (!admin) {
+            QueryWrapper<StaffOnJob> staffQueryWrapper = new QueryWrapper<>();
+            staffQueryWrapper.eq("staff_no", SecurityUtils.getUsername());
+            staffQueryWrapper.eq("staff_state", 1);//鍦ㄨ亴
+            StaffOnJob staffOnJob = staffOnJobMapper.selectOne(staffQueryWrapper);
+            if (staffOnJob == null) {
+                return new Page<>(page.getCurrent(), page.getSize(), 0);
+            }
+            personalAttendanceRecordsDto.setStaffOnJobId(staffOnJob.getId());
+        }
+
+        return personalAttendanceRecordsMapper.listPage(page,personalAttendanceRecordsDto);
+    }
+
+    @Override
+    public PersonalAttendanceRecordsDto todayInfo(PersonalAttendanceRecordsDto personalAttendanceRecordsDto) {
+        // 鑾峰彇褰撳墠鏃ユ湡
+        LocalDate currentDate = LocalDate.now();
+
+        // 棣栧厛鏍规嵁鐢ㄦ埛ID鏌ヨ鍛樺伐淇℃伅
+        QueryWrapper<StaffOnJob> staffQueryWrapper = new QueryWrapper<>();
+        staffQueryWrapper.eq("staff_no", SecurityUtils.getUsername());
+        staffQueryWrapper.eq("staff_state", 1);//鍦ㄨ亴
+        StaffOnJob staffOnJob = staffOnJobMapper.selectOne(staffQueryWrapper);
+
+        if (staffOnJob == null) {
+            throw new BaseException("褰撳墠鐢ㄦ埛娌℃湁瀵瑰簲鐨勫憳宸ヤ俊鎭�");
+        }
+
+        // 鏍规嵁鍛樺伐ID鍜屽綋鍓嶆棩鏈熸煡璇㈡墦鍗¤褰�
+        QueryWrapper<PersonalAttendanceRecords> attendanceQueryWrapper = new QueryWrapper<>();
+        attendanceQueryWrapper.eq("staff_on_job_id", staffOnJob.getId())
+                .eq("date", currentDate);
+        PersonalAttendanceRecords attendanceRecord = personalAttendanceRecordsMapper.selectOne(attendanceQueryWrapper);
+
+        // 杩斿洖鍙傛暟
+        PersonalAttendanceRecordsDto resultDto = new PersonalAttendanceRecordsDto();
+
+        if (attendanceRecord != null) {
+            // 濡傛灉鏈夋墦鍗¤褰曪紝澶嶅埗鎵撳崱璁板綍淇℃伅
+            BeanUtils.copyProperties(attendanceRecord, resultDto);
+        }
+
+        // 鍛樺伐鐩稿叧淇℃伅
+        resultDto.setStaffName(staffOnJob.getStaffName());
+        resultDto.setStaffNo(staffOnJob.getStaffNo());
+        resultDto.setDeptId(staffOnJob.getSysDeptId() != null ? staffOnJob.getSysDeptId() : null);
+        SysDept dept = sysDeptMapper.selectDeptById(staffOnJob.getSysDeptId());
+        resultDto.setDeptName(dept != null ? dept.getDeptName() : null);
+        //鑾峰彇璇ュ憳宸ュ搴旂殑鎵撳崱瑙勫垯
+        List<PersonalAttendanceLocationConfig> personalAttendanceLocationConfigs = personalAttendanceLocationConfigMapper.selectList(Wrappers.<PersonalAttendanceLocationConfig>lambdaQuery()
+                .eq(PersonalAttendanceLocationConfig::getSysDeptId, staffOnJob.getSysDeptId())
+                .orderByDesc(PersonalAttendanceLocationConfig::getId));
+        if (personalAttendanceLocationConfigs.size()>0){
+            resultDto.setStartAt(personalAttendanceLocationConfigs.get(0).getStartAt());
+            resultDto.setEndAt(personalAttendanceLocationConfigs.get(0).getEndAt());
+        }
+        return resultDto;
+    }
+
+    @Override
+    public void export(HttpServletResponse response, PersonalAttendanceRecordsDto personalAttendanceRecordsDto) {
+        boolean admin = SecurityUtils.isAdmin(SecurityUtils.getUserId());
+        if (!admin) {
+            QueryWrapper<StaffOnJob> staffQueryWrapper = new QueryWrapper<>();
+            staffQueryWrapper.eq("staff_no", SecurityUtils.getUsername());
+            staffQueryWrapper.eq("staff_state", 1);//鍦ㄨ亴
+            StaffOnJob staffOnJob = staffOnJobMapper.selectOne(staffQueryWrapper);
+            if (staffOnJob == null) {
+                throw new ServiceException("娌℃湁鍛樺伐淇℃伅锛屾棤娉曞鍑鸿�冨嫟璁板綍");
+            }
+            personalAttendanceRecordsDto.setStaffOnJobId(staffOnJob.getId());
+        }
+        List<PersonalAttendanceRecordsDto> personalAttendanceRecords = personalAttendanceRecordsMapper.listPage(new Page<>(1, Integer.MAX_VALUE), personalAttendanceRecordsDto).getRecords();
+        ExcelUtil<PersonalAttendanceRecordsDto> util = new ExcelUtil<PersonalAttendanceRecordsDto>(PersonalAttendanceRecordsDto.class);
+        util.exportExcel(response, personalAttendanceRecords, "鑰冨嫟璁板綍瀵煎嚭");
     }
 }
diff --git a/src/main/java/com/ruoyi/staff/service/impl/StaffLeaveServiceImpl.java b/src/main/java/com/ruoyi/staff/service/impl/StaffLeaveServiceImpl.java
index 2d30dc5..7f124d8 100644
--- a/src/main/java/com/ruoyi/staff/service/impl/StaffLeaveServiceImpl.java
+++ b/src/main/java/com/ruoyi/staff/service/impl/StaffLeaveServiceImpl.java
@@ -5,14 +5,17 @@
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.ruoyi.common.exception.base.BaseException;
 import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.project.system.domain.SysUser;
+import com.ruoyi.project.system.mapper.SysUserMapper;
 import com.ruoyi.staff.dto.StaffLeaveDto;
 import com.ruoyi.staff.mapper.StaffLeaveMapper;
 import com.ruoyi.staff.mapper.StaffOnJobMapper;
+import com.ruoyi.staff.pojo.StaffLeave;
 import com.ruoyi.staff.pojo.StaffOnJob;
 import com.ruoyi.staff.service.StaffLeaveService;
 import lombok.AllArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
-import com.ruoyi.staff.pojo.StaffLeave;
 import org.springframework.transaction.annotation.Transactional;
 
 import javax.servlet.http.HttpServletResponse;
@@ -23,9 +26,14 @@
 @AllArgsConstructor
 @Service
 public class StaffLeaveServiceImpl extends ServiceImpl<StaffLeaveMapper, StaffLeave> implements StaffLeaveService {
+    @Autowired
     private StaffLeaveMapper staffLeaveMapper;
 
+    @Autowired
     private StaffOnJobMapper staffOnJobMapper;
+
+    @Autowired
+    private SysUserMapper sysUserMapper;
 
     //鏂板绂昏亴鍒楄〃鍒嗛〉鏌ヨ
     @Override
@@ -51,11 +59,20 @@
         // 鏂板绂昏亴璁板綍
         StaffLeave staffLeave = new StaffLeave();
         staffLeave.setStaffOnJobId(staffLeaveDto.getStaffOnJobId());
+        staffLeave.setReason(staffLeaveDto.getReason());
         String reason = staffLeaveDto.getReason();
-        if (!StaffLeaveReasonOther.getCode().equals(reason)){
-            staffLeave.setRemark("");
+        if (StaffLeaveReasonOther.getCode().equals(reason)){
+            staffLeave.setRemark(staffLeaveDto.getRemark());
         }
         staffLeaveMapper.insert(staffLeave);
+
+        // 鏇存柊瀵瑰簲鐢ㄦ埛鐘舵�佷负鍋滅敤
+        // 鏍规嵁鍛樺伐缂栧彿鏌ヨ鐢ㄦ埛
+        SysUser sysUser = sysUserMapper.selectUserByUserName(staffOnJob.getStaffNo());
+        if (sysUser != null) {
+            sysUser.setStatus("1");
+            sysUserMapper.updateUser(sysUser);
+        }
 
         // 鏇存柊绂昏亴鐘舵�佷负绂昏亴
         staffOnJob.setStaffState(0);
@@ -97,6 +114,5 @@
         ExcelUtil<StaffLeaveDto> util = new ExcelUtil<StaffLeaveDto>(StaffLeaveDto.class);
         util.exportExcel(response, staffLeaves, "鍛樺伐绂昏亴瀵煎嚭");
     }
-
 }
 
diff --git a/src/main/java/com/ruoyi/staff/service/impl/StaffOnJobServiceImpl.java b/src/main/java/com/ruoyi/staff/service/impl/StaffOnJobServiceImpl.java
index 6fb0cd0..9e9a4dc 100644
--- a/src/main/java/com/ruoyi/staff/service/impl/StaffOnJobServiceImpl.java
+++ b/src/main/java/com/ruoyi/staff/service/impl/StaffOnJobServiceImpl.java
@@ -21,30 +21,37 @@
 import freemarker.template.Configuration;
 import freemarker.template.Template;
 import lombok.AllArgsConstructor;
-
 import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.servlet.http.HttpServletResponse;
-import java.io.*;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringWriter;
 import java.nio.charset.StandardCharsets;
 import java.time.Instant;
 import java.time.LocalDate;
 import java.time.ZoneId;
-import java.util.*;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 @AllArgsConstructor
 @Service
 public class StaffOnJobServiceImpl extends ServiceImpl<StaffOnJobMapper, StaffOnJob>  implements IStaffOnJobService {
 
-
+    @Autowired
     private StaffOnJobMapper staffOnJobMapper;
-
+    @Autowired
     private SysPostMapper sysPostMapper;
 
+    @Autowired
     private StaffContractMapper staffContractMapper;
+    @Autowired
     private StaffLeaveMapper staffLeaveMapper;
 
 
@@ -66,6 +73,7 @@
         }
         // 鍒涘缓鍏ヨ亴鏁版嵁
         staffOnJobPrams.setContractExpireTime(staffOnJobPrams.getContractEndTime());
+        staffOnJobPrams.setStaffState(1);
         staffOnJobMapper.insert(staffOnJobPrams);
 
         // 鍒涘缓鍚堝悓璁板綍
@@ -87,7 +95,7 @@
             throw new BaseException("缂栧彿涓�"+staffOnJobParams.getStaffNo()+"鐨勫憳宸ヤ笉瀛樺湪,鏃犳硶鏇存柊!!!");
         }
 
-        String[] ignoreProperties = {"id"};//鎺掗櫎id灞炴��
+        String[] ignoreProperties = {"id"};//鎺掗櫎鏇存柊灞炴��
 
         // 鑾峰彇鏈�鏂板悎鍚屾暟鎹紝骞朵笖鏇存柊
         StaffContract contract = staffContractMapper.selectOne(Wrappers.<StaffContract>lambdaQuery()
@@ -100,9 +108,8 @@
         }
 
         // 鏇存柊鍛樺伐鏁版嵁
-        BeanUtils.copyProperties(staffOnJobParams,job,ignoreProperties);
-        job.setContractExpireTime(staffOnJobParams.getContractEndTime());
-        return staffOnJobMapper.updateById(job);
+        staffOnJobParams.setContractExpireTime(staffOnJobParams.getContractEndTime());
+        return staffOnJobMapper.updateById(staffOnJobParams);
     }
 
     //鍒犻櫎鍏ヨ亴
@@ -152,8 +159,12 @@
         StaffOnJobDto staffOnJobDto = new StaffOnJobDto();
         BeanUtils.copyProperties(staffOnJob, staffOnJobDto);
         // 鏌ヨ宀椾綅鍚嶇О
-        SysPost post = sysPostMapper.selectPostById((long) staffOnJob.getSysPostId());
-        staffOnJobDto.setPostName(post.getPostName());
+        if (staffOnJob.getSysPostId() != null) {
+            SysPost post = sysPostMapper.selectPostById(staffOnJob.getSysPostId().longValue());
+            if (post != null) {
+                staffOnJobDto.setPostName(post.getPostName());
+            }
+        }
 
         // 鏌ヨ鍚堝悓淇℃伅
         StaffContract contract = staffContractMapper.selectOne(Wrappers.<StaffContract>lambdaQuery()
@@ -173,7 +184,7 @@
     public void staffOnJobExport(HttpServletResponse response, StaffOnJob staffOnJob) {
         List<StaffOnJobDto> staffOnJobs = staffOnJobMapper.staffOnJobList(staffOnJob);
         ExcelUtil<StaffOnJobDto> util = new ExcelUtil<StaffOnJobDto>(StaffOnJobDto.class);
-        util.exportExcel(response, staffOnJobs, "鍦ㄨ亴鍛樺伐鍙拌处瀵煎嚭");
+        util.exportExcel(response, staffOnJobs, "鍛樺伐鍙拌处瀵煎嚭");
     }
 
     @Override
diff --git a/src/main/java/com/ruoyi/staff/task/PersonalAttendanceRecordsTask.java b/src/main/java/com/ruoyi/staff/task/PersonalAttendanceRecordsTask.java
new file mode 100644
index 0000000..d3a8cd0
--- /dev/null
+++ b/src/main/java/com/ruoyi/staff/task/PersonalAttendanceRecordsTask.java
@@ -0,0 +1,75 @@
+package com.ruoyi.staff.task;
+
+import com.ruoyi.staff.mapper.PersonalAttendanceRecordsMapper;
+import com.ruoyi.staff.pojo.PersonalAttendanceRecords;
+import com.ruoyi.staff.pojo.StaffOnJob;
+import com.ruoyi.staff.service.PersonalAttendanceRecordsService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 涓汉鑰冨嫟璁板綍瀹氭椂浠诲姟
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-02-09
+ */
+@Slf4j
+@Component
+public class PersonalAttendanceRecordsTask {
+
+    @Autowired
+    private PersonalAttendanceRecordsMapper personalAttendanceRecordsMapper;
+
+    @Autowired
+    private PersonalAttendanceRecordsService personalAttendanceRecordsService;
+
+    /**
+     * 姣忓ぉ鍑屾櫒鐢熸垚鏄ㄦ棩鐨勭己鍕よ褰�
+     * 瀹氭椂浠诲姟锛氭瘡澶╁噷鏅�1鐐规墽琛�
+     * 鎺掗櫎浠婂ぉ鍒氬叆鑱岀殑鍛樺伐
+     */
+    @Scheduled(cron = "0 0 1 * * ?")
+    public void generateAbsenceRecords() {
+        try {
+            // 鑾峰彇鏄ㄦ棩鏃ユ湡
+            LocalDate yesterday = LocalDate.now().minusDays(1);
+
+            // 鐩存帴鏌ヨ鏄ㄥぉ娌℃湁鑰冨嫟璁板綍鐨勫湪鑱屽憳宸ワ紙鎺掗櫎浠婂ぉ鍒氬叆鑱岀殑锛�
+            LocalDateTime todayStart = LocalDate.now().atStartOfDay();
+            List<StaffOnJob> staffWithoutAttendance = personalAttendanceRecordsMapper.selectStaffWithoutAttendanceRecordBeforeTime(yesterday, todayStart);
+
+            // 閬嶅巻娌℃湁鑰冨嫟璁板綍鐨勫憳宸ワ紝鐢熸垚缂哄嫟璁板綍
+            for (StaffOnJob staff : staffWithoutAttendance) {
+                try {
+                    boolean exists = personalAttendanceRecordsMapper.existsAttendanceRecord(staff.getId(), yesterday);
+                    if (exists) {
+                        continue;
+                    }
+
+                    PersonalAttendanceRecords absenceRecord = new PersonalAttendanceRecords();
+                    absenceRecord.setStaffOnJobId(staff.getId());
+                    absenceRecord.setDate(yesterday);
+                    absenceRecord.setStatus(4); // 璁剧疆鐘舵�佷负缂哄嫟
+                    absenceRecord.setRemark("绯荤粺鑷姩鐢熸垚-缂哄嫟");
+                    absenceRecord.setCreateTime(LocalDateTime.now());
+                    absenceRecord.setUpdateTime(LocalDateTime.now());
+                    absenceRecord.setTenantId(staff.getTenantId());
+                    personalAttendanceRecordsService.save(absenceRecord);
+
+                } catch (Exception e) {
+                    log.error("涓哄憳宸}鐢熸垚缂哄嫟璁板綍澶辫触锛歿}", staff.getStaffName(), e.getMessage(), e);
+                }
+            }
+
+            log.info("鏄ㄦ棩缂哄嫟璁板綍鐢熸垚瀹屾垚");
+        } catch (Exception e) {
+            log.error("鐢熸垚鏄ㄦ棩缂哄嫟璁板綍浠诲姟鎵ц澶辫触锛歿}", e.getMessage(), e);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ruoyi/staff/utils/LocationUtils.java b/src/main/java/com/ruoyi/staff/utils/LocationUtils.java
new file mode 100644
index 0000000..d335235
--- /dev/null
+++ b/src/main/java/com/ruoyi/staff/utils/LocationUtils.java
@@ -0,0 +1,37 @@
+package com.ruoyi.staff.utils;
+
+// 宸ュ叿绫伙細璁$畻涓や釜缁忕含搴︿箣闂寸殑璺濈锛堢悆闈㈣窛绂伙級
+public class LocationUtils {
+    private static final double EARTH_RADIUS = 6371000; // 鍦扮悆鍗婂緞锛屽崟浣嶇背
+
+    /**
+     * 璁$畻涓や釜缁忕含搴︿箣闂寸殑璺濈锛堢背锛�
+     * @param lat1 绗竴涓偣绾害
+     * @param lon1 绗竴涓偣缁忓害
+     * @param lat2 绗簩涓偣绾害
+     * @param lon2 绗簩涓偣缁忓害
+     * @return 璺濈锛堢背锛�
+     */
+    public static double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
+        // 杞姬搴�
+        double radLat1 = Math.toRadians(lat1);
+        double radLon1 = Math.toRadians(lon1);
+        double radLat2 = Math.toRadians(lat2);
+        double radLon2 = Math.toRadians(lon2);
+
+        // 宸��
+        double deltaLat = radLat1 - radLat2;
+        double deltaLon = radLon1 - radLon2;
+
+        // 鐞冮潰璺濈鍏紡
+        double distance = 2 * Math.asin(Math.sqrt(
+                Math.pow(Math.sin(deltaLat / 2), 2) +
+                        Math.cos(radLat1) * Math.cos(radLat2) *
+                                Math.pow(Math.sin(deltaLon / 2), 2)
+        ));
+        distance = distance * EARTH_RADIUS;
+        // 淇濈暀涓や綅灏忔暟
+        distance = Math.round(distance * 100) / 100.0;
+        return distance;
+    }
+}
diff --git a/src/main/java/com/ruoyi/staff/vo/MonthlyTurnoverRateVo.java b/src/main/java/com/ruoyi/staff/vo/MonthlyTurnoverRateVo.java
new file mode 100644
index 0000000..90f9c69
--- /dev/null
+++ b/src/main/java/com/ruoyi/staff/vo/MonthlyTurnoverRateVo.java
@@ -0,0 +1,39 @@
+package com.ruoyi.staff.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDate;
+
+/**
+ * 鏈堝害鍛樺伐娴佸姩鐜囧拰娴佸け鐜囩粺璁O
+ */
+@Data
+public class MonthlyTurnoverRateVo {
+    @ApiModelProperty("鏈堜唤")
+    private String month;
+
+    @ApiModelProperty("鏈堝垵鍛樺伐鏁�")
+    private Integer beginMonthStaffCount;
+
+    @ApiModelProperty("鏈堟湯鍛樺伐鏁�")
+    private Integer endMonthStaffCount;
+
+    @ApiModelProperty("鏈堝害鍏ヨ亴鍛樺伐鏁�")
+    private Integer newHireCount;
+
+    @ApiModelProperty("鏈堝害绂昏亴鍛樺伐鏁�")
+    private Integer leaveCount;
+
+    @ApiModelProperty("娴佸け鐜�(%)")
+    private Double turnoverRate;
+
+    @ApiModelProperty("娴佸姩鐜�(%)")
+    private Double flowRate;
+
+    @ApiModelProperty("鏈堜唤寮�濮嬫棩鏈�")
+    private LocalDate monthStartDate;
+
+    @ApiModelProperty("鏈堜唤缁撴潫鏃ユ湡")
+    private LocalDate monthEndDate;
+}
diff --git a/src/main/java/com/ruoyi/staff/vo/TotalTurnoverRateVo.java b/src/main/java/com/ruoyi/staff/vo/TotalTurnoverRateVo.java
new file mode 100644
index 0000000..49d2f42
--- /dev/null
+++ b/src/main/java/com/ruoyi/staff/vo/TotalTurnoverRateVo.java
@@ -0,0 +1,19 @@
+package com.ruoyi.staff.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 鍛樺伐鎬绘祦鍔ㄧ巼銆佹祦澶辩巼鍙婂湪鑱屽憳宸ユ暟缁熻VO
+ */
+@Data
+public class TotalTurnoverRateVo {
+    @ApiModelProperty("鎬绘祦鍔ㄧ巼(%)")
+    private Double totalFlowRate;
+
+    @ApiModelProperty("鎬绘祦澶辩巼(%)")
+    private Double totalTurnoverRate;
+
+    @ApiModelProperty("褰撳墠鍦ㄨ亴鍛樺伐鏁�")
+    private Integer currentOnJobCount;
+}
diff --git a/src/main/resources/mapper/production/ProductOrderMapper.xml b/src/main/resources/mapper/production/ProductOrderMapper.xml
index 6fb143a..4c2b1f8 100644
--- a/src/main/resources/mapper/production/ProductOrderMapper.xml
+++ b/src/main/resources/mapper/production/ProductOrderMapper.xml
@@ -31,9 +31,6 @@
             <if test="c.salesContractNo != null and c.salesContractNo != ''">
                 and sl.sales_contract_no like concat('%',#{c.salesContractNo},'%')
             </if>
-            <if test="c.customerName != null and c.customerName != ''">
-                and sl.customer_name like concat('%',#{c.customerName},'%')
-            </if>
             <if test="c.productCategory != null and c.productCategory != ''">
                 and slp.product_category like concat('%',#{c.productCategory},'%')
             </if>
@@ -46,7 +43,7 @@
         </where>
     </select>
     <select id="productMainByOrderId" resultType="com.ruoyi.production.dto.ProductOrderDto">
-        select po.*,
+        select po.*,F
                pwo.work_order_no,
                pwo.report_work,
                pwo.status,

--
Gitblit v1.9.3