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()); } } 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.*; 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)); } } 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)); } } 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; 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; /** 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; 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; } 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; 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; } 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; 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> { } 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); } 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> { 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); } 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); } 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") 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; } 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æ£å¸¸ 1è¿å° 2æ©é 3è¿å°æ©é 4缺å¤") @Excel(name = "ç¶æ", sort = 8,readConverterExp = "0=æ£å¸¸,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; } 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; 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 src/main/java/com/ruoyi/staff/pojo/StaffOnJob.java
@@ -66,7 +66,7 @@ /** * é¨é¨ */ private Integer sysDeptId; private Long sysDeptId; /** * å®¶åºä½å 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(); } 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> { } 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); } 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(); // å建ä¸ä¸ªMapç¨äºå卿ææä¸¾åå çæ°éï¼é»è®¤å¼ä¸º0 Map<String, Integer> reasonCountMap = new HashMap<>(); for (StaffLeaveReason reasonEnum : StaffLeaveReason.values()) { reasonCountMap.put(reasonEnum.getCode(), 0); } // å°æ°æ®åºæ¥è¯¢ç»æåå¹¶å°Mapä¸ for (StaffLeaveDto dto : dbResult) { String reasonCode = dto.getReason(); if (reasonCountMap.containsKey(reasonCode)) { reasonCountMap.put(reasonCode, dto.getCount()); } } // å°Map转æ¢ä¸ºList<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; } } 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 { } 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 æ£å¸¸ 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; // è§£æå°æ¶ååé 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; // ä¸çæå¡æ£å¸¸ä½æ¯ä¸çè¿å° } } return 0; // æ£å¸¸ } 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, "èå¤è®°å½å¯¼åº"); } } 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, "å工离è导åº"); } } 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 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); } } } 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; } } 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; /** * æåº¦åå·¥æµå¨ç念失çç»è®¡VO */ @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; } 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; } 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,