From 4f0ebc1a4ee5625b52274d890cd67a927a5657ca Mon Sep 17 00:00:00 2001 From: Joshi <3040996759@qq.com> Date: Thu, 30 Oct 2025 15:33:39 +0800 Subject: [PATCH] =?UTF-8?q?feat(oa):=20=E6=96=B0=E5=A2=9E=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E6=B4=BB=E8=B7=83=E7=BB=9F=E8=AE=A1=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=B9=B6=E5=AE=8C=E5=96=84=E4=B8=AA=E4=BA=BA=E6=8A=A5=E5=91=8A?= =?UTF-8?q?=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增用户活跃统计模块,包括实体类、业务接口和实现、控制器及Mapper - 实现用户登录时记录当日活跃数据 - 在个人报告中增加活跃天数、报工信息、出差信息、项目信息等统计维度 - 添加工程异常统计和关键采购任务统计功能 - 完善任务信息统计,包括发放任务、承担任务及其状态分析 -优化个人报告接口,支持更全面的工作数据展示 --- .../service/impl/FadAppAuthServiceImpl.java | 7 + .../controller/system/SysLoginController.java | 6 + .../OaProjectScheduleStepController.java | 1 + .../oa/controller/OaUserActiveController.java | 101 ++++ .../com/ruoyi/oa/domain/OaUserActive.java | 54 ++ .../ruoyi/oa/domain/bo/OaUserActiveBo.java | 52 ++ .../oa/domain/dto/PersonalReportDTO.java | 229 ++++++++ .../ruoyi/oa/domain/vo/OaUserActiveVo.java | 57 ++ .../ruoyi/oa/mapper/OaUserActiveMapper.java | 15 + .../IOaProjectScheduleStepService.java | 1 + .../oa/service/IOaUserActiveService.java | 53 ++ .../OaProjectScheduleStepServiceImpl.java | 491 +++++++++++++++++- .../service/impl/OaUserActiveServiceImpl.java | 141 +++++ .../oa/service/impl/SysOaTaskServiceImpl.java | 3 + .../mapper/oa/OaUserActiveMapper.xml | 21 + 15 files changed, 1229 insertions(+), 3 deletions(-) create mode 100644 ruoyi-oa/src/main/java/com/ruoyi/oa/controller/OaUserActiveController.java create mode 100644 ruoyi-oa/src/main/java/com/ruoyi/oa/domain/OaUserActive.java create mode 100644 ruoyi-oa/src/main/java/com/ruoyi/oa/domain/bo/OaUserActiveBo.java create mode 100644 ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/OaUserActiveVo.java create mode 100644 ruoyi-oa/src/main/java/com/ruoyi/oa/mapper/OaUserActiveMapper.java create mode 100644 ruoyi-oa/src/main/java/com/ruoyi/oa/service/IOaUserActiveService.java create mode 100644 ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/OaUserActiveServiceImpl.java create mode 100644 ruoyi-oa/src/main/resources/mapper/oa/OaUserActiveMapper.xml diff --git a/fad-app/src/main/java/com/ruoyi/fadapp/service/impl/FadAppAuthServiceImpl.java b/fad-app/src/main/java/com/ruoyi/fadapp/service/impl/FadAppAuthServiceImpl.java index da555c9..7616415 100644 --- a/fad-app/src/main/java/com/ruoyi/fadapp/service/impl/FadAppAuthServiceImpl.java +++ b/fad-app/src/main/java/com/ruoyi/fadapp/service/impl/FadAppAuthServiceImpl.java @@ -15,6 +15,7 @@ import com.ruoyi.fadapp.domain.vo.NickDeptVo; import com.ruoyi.fadapp.service.IFadAppAuthService; import com.ruoyi.system.service.ISysUserService; import com.ruoyi.system.service.SysPermissionService; +import com.ruoyi.oa.service.IOaUserActiveService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -35,6 +36,7 @@ public class FadAppAuthServiceImpl implements IFadAppAuthService { private final ISysUserService userService; private final SysPermissionService permissionService; + private final IOaUserActiveService userActiveService; /** * 验证码缓存前缀 @@ -106,6 +108,11 @@ public class FadAppAuthServiceImpl implements IFadAppAuthService { LoginHelper.loginByDevice(loginUser, DeviceType.APP); String token = StpUtil.getTokenValue(); + // 记录用户当日活跃(登录) + if (user.getUserId() != null) { + userActiveService.recordTodayLogin(user.getUserId()); + } + // 构建登录结果 LoginResultVo result = new LoginResultVo(); result.setToken(token); diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java index f982a5f..f23652b 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java @@ -14,6 +14,7 @@ import com.ruoyi.system.domain.vo.RouterVo; import com.ruoyi.system.service.ISysMenuService; import com.ruoyi.system.service.ISysUserService; import com.ruoyi.system.service.SysLoginService; +import com.ruoyi.oa.service.IOaUserActiveService; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; @@ -39,6 +40,7 @@ public class SysLoginController { private final SysLoginService loginService; private final ISysMenuService menuService; private final ISysUserService userService; + private final IOaUserActiveService userActiveService; /** * 登录方法 @@ -54,6 +56,10 @@ public class SysLoginController { String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(), loginBody.getUuid()); ajax.put(Constants.TOKEN, token); + Long userId = LoginHelper.getUserId(); + if (userId != null) { + userActiveService.recordTodayLogin(userId); + } return R.ok(ajax); } diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/controller/OaProjectScheduleStepController.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/controller/OaProjectScheduleStepController.java index d67221c..61ee8a3 100644 --- a/ruoyi-oa/src/main/java/com/ruoyi/oa/controller/OaProjectScheduleStepController.java +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/controller/OaProjectScheduleStepController.java @@ -135,4 +135,5 @@ public class OaProjectScheduleStepController extends BaseController { ) { return R.ok(iOaProjectScheduleStepService.personalReport(poolId, nickName)); } + } diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/controller/OaUserActiveController.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/controller/OaUserActiveController.java new file mode 100644 index 0000000..d0072b9 --- /dev/null +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/controller/OaUserActiveController.java @@ -0,0 +1,101 @@ +package com.ruoyi.oa.controller; + +import java.util.List; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; + +import lombok.RequiredArgsConstructor; +import javax.servlet.http.HttpServletResponse; +import javax.validation.constraints.*; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import com.ruoyi.common.annotation.RepeatSubmit; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.PageQuery; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.validate.AddGroup; +import com.ruoyi.common.core.validate.EditGroup; +import com.ruoyi.common.core.validate.QueryGroup; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.oa.domain.vo.OaUserActiveVo; +import com.ruoyi.oa.domain.bo.OaUserActiveBo; +import com.ruoyi.oa.service.IOaUserActiveService; +import com.ruoyi.common.core.page.TableDataInfo; + +/** + * 用户活跃统计 + * + * @author ruoyi + * @date 2025-10-30 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/oa/userActive") +public class OaUserActiveController extends BaseController { + + private final IOaUserActiveService iOaUserActiveService; + + /** + * 查询用户活跃统计列表 + */ + @GetMapping("/list") + public TableDataInfo list(OaUserActiveBo bo, PageQuery pageQuery) { + return iOaUserActiveService.queryPageList(bo, pageQuery); + } + + /** + * 导出用户活跃统计列表 + */ + @Log(title = "用户活跃统计", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(OaUserActiveBo bo, HttpServletResponse response) { + List list = iOaUserActiveService.queryList(bo); + ExcelUtil.exportExcel(list, "用户活跃统计", OaUserActiveVo.class, response); + } + + /** + * 获取用户活跃统计详细信息 + * + * @param activeId 主键 + */ + @GetMapping("/{activeId}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long activeId) { + return R.ok(iOaUserActiveService.queryById(activeId)); + } + + /** + * 新增用户活跃统计 + */ + @Log(title = "用户活跃统计", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody OaUserActiveBo bo) { + return toAjax(iOaUserActiveService.insertByBo(bo)); + } + + /** + * 修改用户活跃统计 + */ + @Log(title = "用户活跃统计", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody OaUserActiveBo bo) { + return toAjax(iOaUserActiveService.updateByBo(bo)); + } + + /** + * 删除用户活跃统计 + * + * @param activeIds 主键串 + */ + @Log(title = "用户活跃统计", businessType = BusinessType.DELETE) + @DeleteMapping("/{activeIds}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] activeIds) { + return toAjax(iOaUserActiveService.deleteWithValidByIds(Arrays.asList(activeIds), true)); + } +} diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/OaUserActive.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/OaUserActive.java new file mode 100644 index 0000000..6c54477 --- /dev/null +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/OaUserActive.java @@ -0,0 +1,54 @@ +package com.ruoyi.oa.domain; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import java.io.Serializable; +import java.util.Date; +import java.math.BigDecimal; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 用户活跃统计对象 oa_user_active + * + * @author ruoyi + * @date 2025-10-30 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("oa_user_active") +public class OaUserActive extends BaseEntity { + + private static final long serialVersionUID=1L; + + /** + * 主键ID + */ + @TableId(value = "active_id") + private Long activeId; + /** + * 用户ID + */ + private Long userId; + /** + * 活跃日期 + */ + private Date activeDate; + /** + * 当日登录次数 + */ + private Long loginCount; + /** + * 备注 + */ + private String remark; + /** + * 删除标志:0=正常,1=已删除 + */ + @TableLogic + private Integer delFlag; + +} diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/bo/OaUserActiveBo.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/bo/OaUserActiveBo.java new file mode 100644 index 0000000..5dc88b6 --- /dev/null +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/bo/OaUserActiveBo.java @@ -0,0 +1,52 @@ +package com.ruoyi.oa.domain.bo; + +import com.ruoyi.common.core.validate.AddGroup; +import com.ruoyi.common.core.validate.EditGroup; +import lombok.Data; +import lombok.EqualsAndHashCode; +import javax.validation.constraints.*; + +import java.util.Date; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 用户活跃统计业务对象 oa_user_active + * + * @author ruoyi + * @date 2025-10-30 + */ + +@Data +@EqualsAndHashCode(callSuper = true) +public class OaUserActiveBo extends BaseEntity { + + /** + * 主键ID + */ + private Long activeId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 活跃日期 + */ + private Date activeDate; + + /** + * 当日登录次数 + */ + private Long loginCount; + + /** + * 备注 + */ + private String remark; + + +} diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/dto/PersonalReportDTO.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/dto/PersonalReportDTO.java index 8c05926..4416b40 100644 --- a/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/dto/PersonalReportDTO.java +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/dto/PersonalReportDTO.java @@ -5,6 +5,8 @@ import com.ruoyi.oa.domain.OaProjectScheduleStep; import com.ruoyi.oa.domain.SysOaProject; import com.ruoyi.oa.domain.SysOaTask; import lombok.Data; + +import java.math.BigDecimal; import java.util.List; /** @@ -20,10 +22,35 @@ public class PersonalReportDTO { /** 进度统计信息 */ private ProgressStats progressStats; + /** 用户负责的进度步骤列表 */ private List userSteps; //nickname和projectId关联的任务 private List tasks; + + /** 活跃天数 */ + private Integer activeDays; + + /** 报工统计 */ + private PersonalReportDTO.WorkReportStats workReportStats; + + /** 出差统计 */ + private PersonalReportDTO.BusinessTripStats businessTripStats; + + /** 项目统计 */ + private PersonalReportDTO.ProjectStats projectStats; + +// /** 进度统计 */ +// private PersonalReportDTO.ProgressStats progressStats; + + /** 任务统计 */ + private PersonalReportDTO.TaskStats taskStats; + + /** 工程异常统计 */ + private PersonalReportDTO.EngineeringExceptionStats exceptionStats; + + /** 关键采购任务统计 */ + private PersonalReportDTO.ProcurementTaskStats procurementStats; /** * 进度统计内部类(封装total、completed等字段) */ @@ -38,4 +65,206 @@ public class PersonalReportDTO { /** 延期进度数 */ private Long delayed; } + + + + /** + * 报工统计 + */ + @Data + public static class WorkReportStats { + /** 总报工天数 */ + private BigDecimal totalWorkDays; + /** 有效报工天数 */ + private BigDecimal validWorkDays; + /** 报工项目数量 */ + private Integer reportProjectCount; + } + + /** + * 出差统计 + */ + @Data + public static class BusinessTripStats { + /** 总出差天数 */ + private BigDecimal totalTripDays; + /** 国内出差天数 */ + private BigDecimal domesticTripDays; + /** 国外出差天数 */ + private BigDecimal foreignTripDays; + /** 出差项目数量 */ + private Integer tripProjectCount; + } + + /** + * 项目统计 + */ + @Data + public static class ProjectStats { + /** 涉及项目总数 */ + private Integer totalProjects; + /** 负责项目数量 */ + private Integer responsibleProjects; + /** 参与项目数量 */ + private Integer participatedProjects; + /** 项目清单 */ + private List projectList; + } + + /** + * 项目摘要 + */ + @Data + public static class ProjectSummary { + /** 项目ID */ + private Long projectId; + /** 项目名称 */ + private String projectName; + /** 项目编号 */ + private String projectNum; + /** 参与角色 */ + private String role; + /** 参与天数 */ + private BigDecimal participationDays; + } + +// /** +// * 进度统计 +// */ +// @Data +// public static class ProgressStats { +// /** 涉及进度总数 */ +// private Integer totalProgress; +// /** 已完成进度数 */ +// private Integer completedProgress; +// /** 进行中进度数 */ +// private Integer inProgressCount; +// /** 延期进度数 */ +// private Integer delayedProgress; +// /** 进度清单 */ +// private List progressList; +// } + + /** + * 进度摘要 + */ + @Data + public static class ProgressSummary { + /** 进度ID */ + private Long trackId; + /** 步骤名称 */ + private String stepName; + /** 项目名称 */ + private String projectName; + /** 进度状态 */ + private Long status; + /** 是否延期 */ + private Boolean isDelayed; + } + + /** + * 任务统计 + */ + @Data + public static class TaskStats { + /** 发放任务数量 */ + private Integer assignedTasks; + /** 承担任务数量 */ + private Integer undertakenTasks; + /** 已完成任务数量 */ + private Integer completedTasks; + /** 设置进行中任务数量 */ + private Integer underwayTasks; + /** 待完成任务数量 */ + private Integer unfinishedTasks; + /** 延期任务数量 */ + private Integer delayedTasks; + /** 发放任务清单 */ + private List assignedTaskList; + /** 承担任务清单 */ + private List undertakenTaskList; + } + + /** + * 任务摘要 + */ + @Data + public static class TaskSummary { + /** 任务ID */ + private Long taskId; + /** 任务主题 */ + private String taskTitle; + /** 项目名称 */ + private String projectName; + /** 任务状态 */ + private Long state; + /** 是否延期 */ + private Boolean isDelayed; + /** 延期次数 */ + private Long postponements; + } + + /** + * 工程异常统计 + */ + @Data + public static class EngineeringExceptionStats { + /** 承担异常数量 */ + private Integer totalExceptions; + /** 延期异常数量 */ + private Integer delayedExceptions; + /** 已解决异常数量 */ + private Integer resolvedExceptions; + /** 异常清单 */ + private List exceptionList; + } + + /** + * 异常摘要 + */ + @Data + public static class ExceptionSummary { + /** 异常ID */ + private Long exceptionId; + /** 异常描述 */ + private String description; + /** 项目名称 */ + private String projectName; + /** 异常状态 */ + private String status; + /** 是否延期 */ + private Boolean isDelayed; + } + + /** + * 关键采购任务统计 + */ + @Data + public static class ProcurementTaskStats { + /** 关键采购任务总数 */ + private Integer totalProcurementTasks; + /** 已完成采购任务数 */ + private Integer completedProcurementTasks; + /** 延期采购任务数 */ + private Integer delayedProcurementTasks; + /** 采购任务清单 */ + private List procurementTaskList; + } + + /** + * 采购任务摘要 + */ + @Data + public static class ProcurementTaskSummary { + /** 采购任务ID */ + private Long procurementId; + /** 采购内容 */ + private String procurementContent; + /** 项目名称 */ + private String projectName; + /** 采购状态 */ + private String status; + /** 是否延期 */ + private Boolean isDelayed; + } } \ No newline at end of file diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/OaUserActiveVo.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/OaUserActiveVo.java new file mode 100644 index 0000000..7581ed0 --- /dev/null +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/OaUserActiveVo.java @@ -0,0 +1,57 @@ +package com.ruoyi.oa.domain.vo; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.ruoyi.common.annotation.ExcelDictFormat; +import com.ruoyi.common.convert.ExcelDictConvert; +import lombok.Data; +import java.util.Date; + + + +/** + * 用户活跃统计视图对象 oa_user_active + * + * @author ruoyi + * @date 2025-10-30 + */ +@Data +@ExcelIgnoreUnannotated +public class OaUserActiveVo { + + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @ExcelProperty(value = "主键ID") + private Long activeId; + + /** + * 用户ID + */ + @ExcelProperty(value = "用户ID") + private Long userId; + + /** + * 活跃日期 + */ + @ExcelProperty(value = "活跃日期") + private Date activeDate; + + /** + * 当日登录次数 + */ + @ExcelProperty(value = "当日登录次数") + private Long loginCount; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + +} diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/mapper/OaUserActiveMapper.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/mapper/OaUserActiveMapper.java new file mode 100644 index 0000000..0317e8c --- /dev/null +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/mapper/OaUserActiveMapper.java @@ -0,0 +1,15 @@ +package com.ruoyi.oa.mapper; + +import com.ruoyi.oa.domain.OaUserActive; +import com.ruoyi.oa.domain.vo.OaUserActiveVo; +import com.ruoyi.common.core.mapper.BaseMapperPlus; + +/** + * 用户活跃统计Mapper接口 + * + * @author ruoyi + * @date 2025-10-30 + */ +public interface OaUserActiveMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/service/IOaProjectScheduleStepService.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/IOaProjectScheduleStepService.java index da20d91..5a7da49 100644 --- a/ruoyi-oa/src/main/java/com/ruoyi/oa/service/IOaProjectScheduleStepService.java +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/IOaProjectScheduleStepService.java @@ -73,4 +73,5 @@ public interface IOaProjectScheduleStepService{ List> queryProgressByHeader(); PersonalReportDTO personalReport(Long poolId, String nickName); + } diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/service/IOaUserActiveService.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/IOaUserActiveService.java new file mode 100644 index 0000000..5b1a3d8 --- /dev/null +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/IOaUserActiveService.java @@ -0,0 +1,53 @@ +package com.ruoyi.oa.service; + +import com.ruoyi.oa.domain.OaUserActive; +import com.ruoyi.oa.domain.vo.OaUserActiveVo; +import com.ruoyi.oa.domain.bo.OaUserActiveBo; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.core.domain.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 用户活跃统计Service接口 + * + * @author ruoyi + * @date 2025-10-30 + */ +public interface IOaUserActiveService { + + /** + * 查询用户活跃统计 + */ + OaUserActiveVo queryById(Long activeId); + + /** + * 查询用户活跃统计列表 + */ + TableDataInfo queryPageList(OaUserActiveBo bo, PageQuery pageQuery); + + /** + * 查询用户活跃统计列表 + */ + List queryList(OaUserActiveBo bo); + + /** + * 新增用户活跃统计 + */ + Boolean insertByBo(OaUserActiveBo bo); + + /** + * 修改用户活跃统计 + */ + Boolean updateByBo(OaUserActiveBo bo); + + /** + * 校验并批量删除用户活跃统计信息 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + boolean hasActiveToday(Long userId); + + void recordTodayLogin(Long userId); +} diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/OaProjectScheduleStepServiceImpl.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/OaProjectScheduleStepServiceImpl.java index fb02025..abbc216 100644 --- a/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/OaProjectScheduleStepServiceImpl.java +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/OaProjectScheduleStepServiceImpl.java @@ -4,9 +4,7 @@ import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ruoyi.common.core.domain.entity.SysUser; -import com.ruoyi.common.core.mapper.BaseMapperPlus; import com.ruoyi.common.helper.LoginHelper; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.core.page.TableDataInfo; @@ -17,11 +15,20 @@ import com.ruoyi.oa.domain.*; import com.ruoyi.oa.domain.bo.BatchBo; import com.ruoyi.oa.domain.dto.NodeDTO; import com.ruoyi.oa.domain.dto.PersonalReportDTO; +import com.ruoyi.oa.domain.vo.OaExpressVo; import com.ruoyi.oa.mapper.*; +import com.ruoyi.oa.mapper.OaProjectReportMapper; +import com.ruoyi.oa.mapper.SysOaTaskMapper; +import com.ruoyi.oa.mapper.OaUserActiveMapper; +import com.ruoyi.oa.mapper.OaExpressQuestionMapper; +import com.ruoyi.oa.mapper.OaRequirementsMapper; +import com.ruoyi.oa.service.IOaExpressService; import com.ruoyi.oa.service.ISysOaTaskService; import com.ruoyi.system.mapper.SysUserMapper; + +import java.math.BigDecimal; +import java.util.Date; import lombok.RequiredArgsConstructor; -import org.flowable.job.service.impl.ServiceImpl; import org.springframework.stereotype.Service; import com.ruoyi.oa.domain.bo.OaProjectScheduleStepBo; import com.ruoyi.oa.domain.vo.OaProjectScheduleStepVo; @@ -59,6 +66,18 @@ public class OaProjectScheduleStepServiceImpl implements IOaProjectScheduleStepS private final OaProjectScheduleStepMapper projectScheduleStepMapper; private final ISysOaTaskService sysOaTaskService; + + private final OaProjectReportMapper projectReportMapper; + + private final SysOaTaskMapper taskMapper; + + private final OaUserActiveMapper userActiveMapper; + + private final OaExpressQuestionMapper expressQuestionMapper; + + private final OaRequirementsMapper requirementsMapper; + + private final IOaExpressService oaExpressService; @@ -358,6 +377,7 @@ public class OaProjectScheduleStepServiceImpl implements IOaProjectScheduleStepS if (user == null) { throw new RuntimeException("未找到用户:" + nickName); } + Long userId = user.getUserId(); // 查询奖金池关联的项目ID List rels = bonusProjectRelMapper.selectList( @@ -444,9 +464,474 @@ public class OaProjectScheduleStepServiceImpl implements IOaProjectScheduleStepS result.setProgressStats(progressStats); result.setUserSteps(userSteps); // 此时userSteps已包含每个步骤对应的projectId result.setTasks(tasks); + // 统计活跃天数 + result.setActiveDays(getActiveDays(userId, startTime, endTime)); + // 1. 统计报工信息 + result.setWorkReportStats(getWorkReportStats(userId, startTime, endTime)); + + // 2. 统计出差信息 + result.setBusinessTripStats(getBusinessTripStats(userId, startTime, endTime)); + + // 3. 统计项目信息 + result.setProjectStats(getProjectStats(userId, nickName, startTime, endTime)); + +// // 4. 统计进度信息 +// result.setProgressStats(getProgressStats(nickName, startDate, endDate)); + + // 5. 统计任务信息 + result.setTaskStats(getTaskStats(userId, startTime, endTime)); + + // 6. 统计工程异常信息 + result.setExceptionStats(getExceptionStats(nickName, startTime, endTime)); + + // 7. 统计关键采购任务信息 + result.setProcurementStats(getProcurementStats(userId, startTime, endTime)); return result; } + /** + * 统计用户活跃天数 + * @param userId 用户ID + * @param startDate 开始日期 + * @param endDate 结束日期 + * @return 活跃天数 + */ + private Integer getActiveDays(Long userId, Date startDate, Date endDate) { + // 构建查询条件 + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery() + .eq(OaUserActive::getUserId, userId) + .eq(OaUserActive::getDelFlag, 0); + + // 添加时间范围条件 + if (startDate != null && endDate != null) { + wrapper.between(OaUserActive::getActiveDate, startDate, endDate); + } + + // 查询活跃记录数量 + Long count = userActiveMapper.selectCount(wrapper); + + return count != null ? count.intValue() : 0; + } + + /** + * 统计报工信息 + */ + private PersonalReportDTO.WorkReportStats getWorkReportStats(Long userId, Date startDate, Date endDate) { + PersonalReportDTO.WorkReportStats stats = new PersonalReportDTO.WorkReportStats(); + + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery() + .eq(OaProjectReport::getDelFlag, 0) + .eq(OaProjectReport::getUserId, userId); + + // 添加时间范围条件 + if (startDate != null && endDate != null) { + wrapper.between(OaProjectReport::getCreateTime, startDate, endDate); + } + + List reports = projectReportMapper.selectList(wrapper); + + // 计算报工天数(这里简化处理,实际可能需要按日期去重) + stats.setTotalWorkDays(BigDecimal.valueOf(reports.size())); + stats.setValidWorkDays(BigDecimal.valueOf(reports.size())); + + // 统计涉及的项目数量 + Set projectIds = reports.stream() + .map(OaProjectReport::getProjectId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + stats.setReportProjectCount(projectIds.size()); + + return stats; + } + + /** + * 统计出差信息 + */ + private PersonalReportDTO.BusinessTripStats getBusinessTripStats(Long userId, Date startDate, Date endDate) { + PersonalReportDTO.BusinessTripStats stats = new PersonalReportDTO.BusinessTripStats(); + + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery() + .eq(OaProjectReport::getDelFlag, 0) + .eq(OaProjectReport::getIsTrip, 1) // 出差标记 + .eq(OaProjectReport::getUserId, userId); + + // 添加时间范围条件 + if (startDate != null && endDate != null) { + wrapper.between(OaProjectReport::getCreateTime, startDate, endDate); + } + + + // 1. 统计总出差天数(按记录数统计,假设一条记录对应一天) + Long totalTripCount = projectReportMapper.selectCount(wrapper); + stats.setTotalTripDays(BigDecimal.valueOf(totalTripCount)); + + // 国外 + LambdaQueryWrapper foreignWrapper = Wrappers.lambdaQuery() + .eq(OaProjectReport::getDelFlag, 0) + .eq(OaProjectReport::getIsTrip, 1) + .eq(OaProjectReport::getUserId, userId) + .eq(OaProjectReport::getWorkType, 1); // 国外标志 + if (StringUtils.isNull(startDate) && StringUtils.isNull(endDate)) { + foreignWrapper.between(OaProjectReport::getCreateTime, startDate, endDate); + } + Long foreignTripCount = projectReportMapper.selectCount(foreignWrapper); + stats.setForeignTripDays(BigDecimal.valueOf(foreignTripCount)); + + // 3. 国内出差天数 = 总出差天数 - 国外出差天数 + stats.setDomesticTripDays(BigDecimal.valueOf(totalTripCount - foreignTripCount)); + + // 统计出差项目数量 + Set tripProjectIds = projectReportMapper.selectObjs(wrapper + .select(OaProjectReport::getProjectId)) // 只查询project_id字段 + .stream() + .filter(Objects::nonNull) + .map(obj -> (Long) obj) + .collect(Collectors.toSet()); + stats.setTripProjectCount(tripProjectIds.size()); + + return stats; + } + + /** + * 统计项目信息 + */ + private PersonalReportDTO.ProjectStats getProjectStats(Long userId, String nickName, Date startDate, Date endDate) { + PersonalReportDTO.ProjectStats stats = new PersonalReportDTO.ProjectStats(); + + // 通过报工记录查询参与的项目 + LambdaQueryWrapper reportWrapper = Wrappers.lambdaQuery() + .eq(OaProjectReport::getUserId, userId) + .eq(OaProjectReport::getDelFlag, 0); + +// if (startDate != null && endDate != null) { +// reportWrapper.between(OaProjectReport::getCreateTime, startDate, endDate); +// } + + List reports = projectReportMapper.selectList(reportWrapper); + Set participatedProjectIds = reports.stream() + .map(OaProjectReport::getProjectId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + // 查询负责的项目 + LambdaQueryWrapper projectWrapper = Wrappers.lambdaQuery() + .eq(SysOaProject::getFunctionary, nickName); + List responsibleProjectList = projectMapper.selectList(projectWrapper); + + stats.setTotalProjects(participatedProjectIds.size()); + stats.setResponsibleProjects(responsibleProjectList.size()); + stats.setParticipatedProjects(participatedProjectIds.size()); + + // 构建项目清单 + List projectList = new ArrayList<>(); + + // 添加负责的项目 + for (SysOaProject project : responsibleProjectList) { + PersonalReportDTO.ProjectSummary summary = new PersonalReportDTO.ProjectSummary(); + summary.setProjectId(project.getProjectId()); + summary.setProjectName(project.getProjectName()); + summary.setProjectNum(project.getProjectNum()); + summary.setRole("负责人"); + + // 计算参与天数 + //TODO 项目负责人是不报工的 + long participationDays = reports.stream() + .filter(report -> project.getProjectId().equals(report.getProjectId())) + .count(); + summary.setParticipationDays(BigDecimal.valueOf(participationDays)); + + projectList.add(summary); + } + + // 添加参与的其他项目 + for (Long projectId : participatedProjectIds) { + if (responsibleProjectList.stream().noneMatch(p -> p.getProjectId().equals(projectId))) { + SysOaProject project = projectMapper.selectById(projectId); + if (project != null) { + PersonalReportDTO.ProjectSummary summary = new PersonalReportDTO.ProjectSummary(); + summary.setProjectId(project.getProjectId()); + summary.setProjectName(project.getProjectName()); + summary.setProjectNum(project.getProjectNum()); + summary.setRole("参与者"); + + // 计算参与天数 + long participationDays = reports.stream() + .filter(report -> projectId.equals(report.getProjectId())) + .count(); + summary.setParticipationDays(BigDecimal.valueOf(participationDays)); + + projectList.add(summary); + } + } + } + + stats.setProjectList(projectList); + return stats; + } + + + + /** + * 统计任务信息 + */ + private PersonalReportDTO.TaskStats getTaskStats(Long userId, Date startDate, Date endDate) { + PersonalReportDTO.TaskStats stats = new PersonalReportDTO.TaskStats(); + + // 查询发放的任务(创建的任务) + LambdaQueryWrapper assignedWrapper = Wrappers.lambdaQuery() + .eq(SysOaTask::getCreateUserId, userId); + + if (startDate != null && endDate != null) { + assignedWrapper.between(SysOaTask::getBeginTime, startDate, endDate); + } + + List assignedTasks = taskMapper.selectList(assignedWrapper); + + // 查询承担的任务(被分配的任务) + LambdaQueryWrapper undertakenWrapper = Wrappers.lambdaQuery() + .eq(SysOaTask::getWorkerId, userId); + + if (startDate != null && endDate != null) { + undertakenWrapper.between(SysOaTask::getBeginTime, startDate, endDate); + } + + List undertakenTasks = taskMapper.selectList(undertakenWrapper); + + // 统计数量 + stats.setAssignedTasks(assignedTasks.size()); + stats.setUndertakenTasks(undertakenTasks.size()); + + // 统计已完成任务数量(状态为完成) + long completedAssigned = assignedTasks.stream().filter(task -> Long.valueOf(2).equals(task.getState())).count(); + long completedUndertaken = undertakenTasks.stream().filter(task -> Long.valueOf(2).equals(task.getState())).count(); + stats.setCompletedTasks((int) (completedAssigned + completedUndertaken)); + + // 统计进行中任务数量(状态为 1 等待审核) + long underwayAssigned = assignedTasks.stream().filter(task -> Long.valueOf(1).equals(task.getState())).count(); + long underwayUndertaken = undertakenTasks.stream().filter(task -> Long.valueOf(1).equals(task.getState())).count(); + stats.setUnderwayTasks((int) (underwayAssigned + underwayUndertaken)); + //未完成的数量 + long unfinishedAssigned = assignedTasks.stream().filter(task -> Long.valueOf(0).equals(task.getState())).count(); + long unfinishedUndertaken = undertakenTasks.stream().filter(task -> Long.valueOf(0).equals(task.getState())).count(); + stats.setUnfinishedTasks((int) (unfinishedAssigned + unfinishedUndertaken)); + + // 统计延期任务数量 + long delayedAssigned = assignedTasks.stream().filter(task -> Long.valueOf(15).equals(task.getState())).count(); + long delayedUndertaken = undertakenTasks.stream().filter(task -> Long.valueOf(15).equals(task.getState())).count(); + stats.setDelayedTasks((int) (delayedAssigned + delayedUndertaken)); + + // 构建任务清单 + List assignedTaskList = assignedTasks.stream() + .map(this::buildTaskSummary) + .collect(Collectors.toList()); + + List undertakenTaskList = undertakenTasks.stream() + .map(this::buildTaskSummary) + .collect(Collectors.toList()); + + stats.setAssignedTaskList(assignedTaskList); + stats.setUndertakenTaskList(undertakenTaskList); + + return stats; + } + + /** + * 构建任务摘要 + */ + private PersonalReportDTO.TaskSummary buildTaskSummary(SysOaTask task) { + PersonalReportDTO.TaskSummary summary = new PersonalReportDTO.TaskSummary(); + summary.setTaskId(task.getTaskId()); + summary.setTaskTitle(task.getTaskTitle()); + summary.setState(task.getState()); + summary.setIsDelayed(task.getPostponements() != null && task.getPostponements() > 0); + summary.setPostponements(task.getPostponements()); + + // 获取项目名称 + if (task.getProjectId() != null) { + SysOaProject project = projectMapper.selectById(task.getProjectId()); + if (project != null) { + summary.setProjectName(project.getProjectName()); + } + } + + return summary; + } + + /** + * 统计工程异常信息(基于oa_express_question表) + */ + private PersonalReportDTO.EngineeringExceptionStats getExceptionStats(String nickName, Date startDate, Date endDate) { + PersonalReportDTO.EngineeringExceptionStats stats = new PersonalReportDTO.EngineeringExceptionStats(); + + + // 查询快递问题表,通过汇报人匹配 + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery() + .eq(OaExpressQuestion::getReportBy, nickName) + .eq(OaExpressQuestion::getDelFlag, 0); + + // 添加时间范围条件(基于汇报时间) + if (startDate != null && endDate != null) { + wrapper.between(OaExpressQuestion::getReportTime, startDate, endDate); + } + + List questions = expressQuestionMapper.selectList(wrapper); + + stats.setTotalExceptions(questions.size()); + + // 统计已解决和未解决的异常 + long resolvedCount = questions.stream().filter(q -> Long.valueOf(1).equals(q.getStatus())).count(); + long unresolvedCount = questions.stream().filter(q -> Long.valueOf(0).equals(q.getStatus())).count(); + + stats.setResolvedExceptions((int) resolvedCount); + stats.setDelayedExceptions((int) unresolvedCount); // 未解决的视为延期异常 + + // 构建异常清单 + List exceptionList = questions.stream() + .map(question -> { + PersonalReportDTO.ExceptionSummary summary = new PersonalReportDTO.ExceptionSummary(); + summary.setExceptionId(question.getQuestionId()); + summary.setDescription(question.getDescription()); + summary.setStatus(Long.valueOf(1).equals(question.getStatus()) ? "已解决" : "未解决"); + summary.setIsDelayed(Long.valueOf(0).equals(question.getStatus())); // 未解决的视为延期 + + // 这里可以根据expressId关联获取项目信息 + summary.setProjectName(oaExpressService.queryById(question.getExpressId()).getProjectName()); + + return summary; + }) + .collect(Collectors.toList()); + + stats.setExceptionList(exceptionList); + return stats; + } + + /** + * 统计关键采购任务信息(基于oa_requirements表) + */ + private PersonalReportDTO.ProcurementTaskStats getProcurementStats(Long userId, Date startDate, Date endDate) { + PersonalReportDTO.ProcurementTaskStats stats = new PersonalReportDTO.ProcurementTaskStats(); + + // 查询需求表,通过负责人ID匹配 + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery() + .eq(OaRequirements::getOwnerId, userId) + .eq(OaRequirements::getDelFlag, 0); + + // 添加时间范围条件 + if (startDate != null && endDate != null) { + wrapper.between(OaRequirements::getCreateTime, startDate, endDate); + } + + List requirements = requirementsMapper.selectList(wrapper); + + stats.setTotalProcurementTasks(requirements.size()); + + // 统计已完成和延期的需求 + long completedCount = requirements.stream().filter(req -> Integer.valueOf(1).equals(req.getStatus())).count(); + stats.setCompletedProcurementTasks((int) completedCount); + + // 计算延期需求(截止日期已过但未完成的) + long delayedCount = requirements.stream() + .filter(req -> Integer.valueOf(0).equals(req.getStatus())) // 未完成 + .filter(req -> req.getDeadline() != null && req.getDeadline().before(new Date())) // 已过期 + .count(); + stats.setDelayedProcurementTasks((int) delayedCount); + + // 构建采购任务清单 + List procurementList = requirements.stream() + .map(req -> { + PersonalReportDTO.ProcurementTaskSummary summary = new PersonalReportDTO.ProcurementTaskSummary(); + summary.setProcurementId(req.getRequirementId()); + summary.setProcurementContent(req.getTitle()); + summary.setStatus(Integer.valueOf(1).equals(req.getStatus()) ? "已完成" : "进行中"); + + // 判断是否延期 + boolean isDelayed = Integer.valueOf(0).equals(req.getStatus()) && + req.getDeadline() != null && req.getDeadline().before(new Date()); + summary.setIsDelayed(isDelayed); + + // 获取项目名称 + if (req.getProjectId() != null) { + SysOaProject project = projectMapper.selectById(req.getProjectId()); + if (project != null) { + summary.setProjectName(project.getProjectName()); + } + } else { + summary.setProjectName("无关联项目"); + } + + return summary; + }) + .collect(Collectors.toList()); + + stats.setProcurementTaskList(procurementList); + return stats; + } + +// /** +// * 统计进度信息 +// */ +// private PersonalReportDTO.ProgressStats getProgressStats(String nickName, String startDate, String endDate) { +// PersonalReportDTO.ProgressStats stats = new PersonalReportDTO.ProgressStats(); +// +// // 查询用户负责的进度 +// LambdaQueryWrapper wrapper = Wrappers.lambdaQuery() +// .eq(OaProjectScheduleStep::getNodeHeader, nickName) +// .eq(OaProjectScheduleStep::getDelFlag, "0"); +// +// if (startDate != null && endDate != null) { +// wrapper.between(OaProjectScheduleStep::getCreateTime, startDate, endDate); +// } +// +// List steps = baseMapper.selectList(wrapper); +// +// stats.setTotalProgress(steps.size()); +// +// // 统计各状态的进度数量 +// long completedCount = steps.stream().filter(step -> Long.valueOf(2).equals(step.getStatus())).count(); +// long inProgressCount = steps.stream().filter(step -> Long.valueOf(1).equals(step.getStatus())).count(); +// +// // 计算延期进度(实际结束时间晚于计划结束时间) +// long delayedCount = steps.stream() +// .filter(step -> step.getActualEnd() != null && step.getPlanEnd() != null) +// .filter(step -> step.getActualEnd().after(step.getPlanEnd())) +// .count(); +// +// stats.setCompletedProgress((int) completedCount); +// stats.setInProgressCount((int) inProgressCount); +// stats.setDelayedProgress((int) delayedCount); +// +// // 构建进度清单 +// List progressList = steps.stream() +// .map(step -> { +// PersonalReportDTO.ProgressSummary summary = new PersonalReportDTO.ProgressSummary(); +// summary.setTrackId(step.getTrackId()); +// summary.setStepName(step.getStepName()); +// summary.setStatus(step.getStatus()); +// +// // 判断是否延期 +// boolean isDelayed = step.getActualEnd() != null && step.getPlanEnd() != null && +// step.getActualEnd().after(step.getPlanEnd()); +// summary.setIsDelayed(isDelayed); +// +// // 获取项目名称 +// if (step.getScheduleId() != null) { +// OaProjectSchedule schedule = scheduleMapper.selectById(step.getScheduleId()); +// if (schedule != null && schedule.getProjectId() != null) { +// SysOaProject project = projectMapper.selectById(schedule.getProjectId()); +// if (project != null) { +// summary.setProjectName(project.getProjectName()); +// } +// } +// } +// +// return summary; +// }) +// .collect(Collectors.toList()); +// +// stats.setProgressList(progressList); +// return stats; +// } + } diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/OaUserActiveServiceImpl.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/OaUserActiveServiceImpl.java new file mode 100644 index 0000000..487b7b9 --- /dev/null +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/OaUserActiveServiceImpl.java @@ -0,0 +1,141 @@ +package com.ruoyi.oa.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.core.domain.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import com.ruoyi.oa.domain.bo.OaUserActiveBo; +import com.ruoyi.oa.domain.vo.OaUserActiveVo; +import com.ruoyi.oa.domain.OaUserActive; +import com.ruoyi.oa.mapper.OaUserActiveMapper; +import com.ruoyi.oa.service.IOaUserActiveService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; +import java.time.LocalDate; +import java.sql.Date; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; + +/** + * 用户活跃统计Service业务层处理 + * + * @author ruoyi + * @date 2025-10-30 + */ +@RequiredArgsConstructor +@Service +public class OaUserActiveServiceImpl implements IOaUserActiveService { + + private final OaUserActiveMapper baseMapper; + + /** + * 查询用户活跃统计 + */ + @Override + public OaUserActiveVo queryById(Long activeId){ + return baseMapper.selectVoById(activeId); + } + + /** + * 查询用户活跃统计列表 + */ + @Override + public TableDataInfo queryPageList(OaUserActiveBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询用户活跃统计列表 + */ + @Override + public List queryList(OaUserActiveBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(OaUserActiveBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(bo.getUserId() != null, OaUserActive::getUserId, bo.getUserId()); + lqw.eq(bo.getActiveDate() != null, OaUserActive::getActiveDate, bo.getActiveDate()); + lqw.eq(bo.getLoginCount() != null, OaUserActive::getLoginCount, bo.getLoginCount()); + lqw.orderByDesc(OaUserActive::getCreateTime); + return lqw; + } + + /** + * 新增用户活跃统计 + */ + @Override + public Boolean insertByBo(OaUserActiveBo bo) { + OaUserActive add = BeanUtil.toBean(bo, OaUserActive.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setActiveId(add.getActiveId()); + } + return flag; + } + + /** + * 修改用户活跃统计 + */ + @Override + public Boolean updateByBo(OaUserActiveBo bo) { + OaUserActive update = BeanUtil.toBean(bo, OaUserActive.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(OaUserActive entity){ + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 批量删除用户活跃统计 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if(isValid){ + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteBatchIds(ids) > 0; + } + + @Override + public boolean hasActiveToday(Long userId) { + Date today = Date.valueOf(LocalDate.now()); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(OaUserActive::getUserId, userId) + .eq(OaUserActive::getActiveDate, today); + return baseMapper.selectCount(lqw) > 0; + } + + @Override + public void recordTodayLogin(Long userId) { + Date today = Date.valueOf(LocalDate.now()); + LambdaUpdateWrapper uw = Wrappers.lambdaUpdate(); + uw.eq(OaUserActive::getUserId, userId) + .eq(OaUserActive::getActiveDate, today) + .setSql("login_count = login_count + 1"); + int rows = baseMapper.update(null, uw); + if (rows == 0) { + OaUserActive entity = new OaUserActive(); + entity.setUserId(userId); + entity.setActiveDate(today); + entity.setLoginCount(1L); + baseMapper.insert(entity); + } + } +} diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/SysOaTaskServiceImpl.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/SysOaTaskServiceImpl.java index c3798d6..89f24f1 100644 --- a/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/SysOaTaskServiceImpl.java +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/SysOaTaskServiceImpl.java @@ -24,6 +24,8 @@ import com.ruoyi.oa.domain.vo.SysOaTaskVo; import com.ruoyi.oa.mapper.SysOaTaskMapper; import com.ruoyi.oa.service.ISysOaTaskService; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.*; @@ -323,6 +325,7 @@ public class SysOaTaskServiceImpl implements ISysOaTaskService { return Collections.emptyList(); } + // 增加任务开始时间在奖金池时间范围内的筛选 return baseMapper.selectList(Wrappers.lambdaQuery() .eq(SysOaTask::getWorkerId, user.getUserId()) diff --git a/ruoyi-oa/src/main/resources/mapper/oa/OaUserActiveMapper.xml b/ruoyi-oa/src/main/resources/mapper/oa/OaUserActiveMapper.xml new file mode 100644 index 0000000..1e65197 --- /dev/null +++ b/ruoyi-oa/src/main/resources/mapper/oa/OaUserActiveMapper.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + +