diff --git a/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java b/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java
index bff515ed..d40d7f5e 100644
--- a/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java
+++ b/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java
@@ -1,6 +1,7 @@
package cn.iocoder.yudao.framework.datapermission.core.rule.dept;
import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
@@ -22,27 +23,22 @@ import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
/**
* 基于部门的 {@link DataPermissionRule} 数据权限规则实现
- *
+ *
* 注意,使用 DeptDataPermissionRule 时,需要保证表中有 dept_id 部门编号的字段,可自定义。
- *
+ *
* 实际业务场景下,会存在一个经典的问题?当用户修改部门时,冗余的 dept_id 是否需要修改?
* 1. 一般情况下,dept_id 不进行修改,则会导致用户看不到之前的数据。【yudao-server 采用该方案】
* 2. 部分情况下,希望该用户还是能看到之前的数据,则有两种方式解决:【需要你改造该 DeptDataPermissionRule 的实现代码】
- * 1)编写洗数据的脚本,将 dept_id 修改成新部门的编号;【建议】
- * 最终过滤条件是 WHERE dept_id = ?
- * 2)洗数据的话,可能涉及的数据量较大,也可以采用 user_id 进行过滤的方式,此时需要获取到 dept_id 对应的所有 user_id 用户编号;
- * 最终过滤条件是 WHERE user_id IN (?, ?, ? ...)
- * 3)想要保证原 dept_id 和 user_id 都可以看的到,此时使用 dept_id 和 user_id 一起过滤;
- * 最终过滤条件是 WHERE dept_id = ? OR user_id IN (?, ?, ? ...)
- *
-
+ * 1)编写洗数据的脚本,将 dept_id 修改成新部门的编号;【建议】
+ * 最终过滤条件是 WHERE dept_id = ?
+ * 2)洗数据的话,可能涉及的数据量较大,也可以采用 user_id 进行过滤的方式,此时需要获取到 dept_id 对应的所有 user_id 用户编号;
+ * 最终过滤条件是 WHERE user_id IN (?, ?, ? ...)
+ * 3)想要保证原 dept_id 和 user_id 都可以看的到,此时使用 dept_id 和 user_id 一起过滤;
+ * 最终过滤条件是 WHERE dept_id = ? OR user_id IN (?, ?, ? ...)
*/
@AllArgsConstructor
@Slf4j
@@ -60,10 +56,15 @@ public class DeptDataPermissionRule implements DataPermissionRule {
private final PermissionApi permissionApi;
+ // TODO: 2024/4/10 注意 - 如果需要降级低权的话 要把需要的表名称加在这里 并且在方法上开启数据权限 并且添加 DataPermissionConfiguration 对象
+ private static final List LOW_POWER_TABLES = Arrays.asList(
+ "bpm_process_instance_ext"
+ );
+
/**
* 基于部门的表字段配置
* 一般情况下,每个表的部门编号字段是 dept_id,通过该配置自定义。
- *
+ *
* key:表名
* value:字段名
*/
@@ -71,7 +72,7 @@ public class DeptDataPermissionRule implements DataPermissionRule {
/**
* 基于用户的表字段配置
* 一般情况下,每个表的部门编号字段是 dept_id,通过该配置自定义。
- *
+ *
* key:表名
* value:字段名
*/
@@ -113,18 +114,19 @@ public class DeptDataPermissionRule implements DataPermissionRule {
}
// 情况一,如果是 ALL 可查看全部,则无需拼接条件
- if (deptDataPermission.getAll()) {
+ // 并且 (低权 和 支持地权) 都不成立
+ if (deptDataPermission.getAll() && !((deptDataPermission.getSelf() || CollectionUtil.isNotEmpty(deptDataPermission.getDeptIds())) && LOW_POWER_TABLES.contains(tableName))) {
return null;
}
// 情况二,即不能查看部门,又不能查看自己,则说明 100% 无权限
if (CollUtil.isEmpty(deptDataPermission.getDeptIds())
- && Boolean.FALSE.equals(deptDataPermission.getSelf())) {
+ && Boolean.FALSE.equals(deptDataPermission.getSelf())) {
return new EqualsTo(null, null); // WHERE null = null,可以保证返回的数据为空
}
// 情况三,拼接 Dept 和 User 的条件,最后组合
- Expression deptExpression = buildDeptExpression(tableName,tableAlias, deptDataPermission.getDeptIds());
+ Expression deptExpression = buildDeptExpression(tableName, tableAlias, deptDataPermission.getDeptIds());
Expression userExpression = buildUserExpression(tableName, tableAlias, deptDataPermission.getSelf(), loginUser.getId());
if (deptExpression == null && userExpression == null) {
// TODO 芋艿:获得不到条件的时候,暂时不抛出异常,而是不返回数据
@@ -180,7 +182,7 @@ public class DeptDataPermissionRule implements DataPermissionRule {
public void addDeptColumn(Class extends BaseDO> entityClass, String columnName) {
String tableName = TableInfoHelper.getTableInfo(entityClass).getTableName();
- addDeptColumn(tableName, columnName);
+ addDeptColumn(tableName, columnName);
}
public void addDeptColumn(String tableName, String columnName) {
diff --git a/yudao-gateway/src/main/resources/bootstrap.yaml b/yudao-gateway/src/main/resources/bootstrap.yaml
index 55165083..ba7a42c4 100644
--- a/yudao-gateway/src/main/resources/bootstrap.yaml
+++ b/yudao-gateway/src/main/resources/bootstrap.yaml
@@ -3,7 +3,7 @@ spring:
name: gateway-server
profiles:
- active: local #local
+ active: dev #local
# active: prod
server:
port: 48080
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/procurepay/BpmOAProcurePayItemRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/procurepay/BpmOAProcurePayItemRespVO.java
index 5a4bb4ef..e5a252c0 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/procurepay/BpmOAProcurePayItemRespVO.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/procurepay/BpmOAProcurePayItemRespVO.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.procurepay;
+import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
@@ -7,6 +8,7 @@ import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;
@Schema(description = "管理后台 - 采购收款明细 Response VO")
@Data
@@ -45,6 +47,7 @@ public class BpmOAProcurePayItemRespVO {
@Schema(description = "期望交付日期")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
private LocalDateTime expectedDeliveryDate;
@Schema(description = "备注")
@@ -56,4 +59,6 @@ public class BpmOAProcurePayItemRespVO {
@Schema(description = "采购总金额大写")
private String totalMoneyChinese;
+ @Schema(description = "采购详情json数据")
+ private String procureDetailJson;
}
\ No newline at end of file
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/procurepay/BpmOAProcurePayRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/procurepay/BpmOAProcurePayRespVO.java
index b3f51e9a..1b4fbc3b 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/procurepay/BpmOAProcurePayRespVO.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/procurepay/BpmOAProcurePayRespVO.java
@@ -1,13 +1,18 @@
package cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.procurepay;
import cn.iocoder.yudao.module.bpm.controller.admin.upload.UploadUserFile;
+import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;
+
@Schema(description = "管理后台 - 采购支付 Response VO")
@Data
public class BpmOAProcurePayRespVO {
@@ -22,6 +27,8 @@ public class BpmOAProcurePayRespVO {
private String reason;
@Schema(description = "支付日期")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
private LocalDateTime payTime;
@Schema(description = "采购付款总金额", requiredMode = Schema.RequiredMode.REQUIRED)
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/procurepay/BpmOAProcurePaySaveReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/procurepay/BpmOAProcurePaySaveReqVO.java
index e3bae9f4..99bac792 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/procurepay/BpmOAProcurePaySaveReqVO.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/procurepay/BpmOAProcurePaySaveReqVO.java
@@ -10,6 +10,8 @@ import java.math.BigDecimal;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
@Schema(description = "管理后台 - 采购支付新增/修改 Request VO")
@Data
public class BpmOAProcurePaySaveReqVO {
@@ -25,6 +27,7 @@ public class BpmOAProcurePaySaveReqVO {
private String reason;
@Schema(description = "支付日期")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime payTime;
@Schema(description = "采购付款总金额", requiredMode = Schema.RequiredMode.REQUIRED)
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java
index c10b3375..3ef62b01 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java
@@ -25,7 +25,6 @@ import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUti
@RestController
@RequestMapping("/bpm/process-instance")
@Validated
-@DataPermission(enable = false)
public class BpmProcessInstanceController {
@Resource
@@ -34,6 +33,7 @@ public class BpmProcessInstanceController {
@GetMapping("/my-page")
@Operation(summary = "获得我的实例分页列表", description = "在【我的流程】菜单中,进行调用")
@PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
+ @DataPermission(enable = false)
public CommonResult> getMyProcessInstancePage(
@Valid BpmProcessInstanceMyPageReqVO pageReqVO) {
return success(processInstanceService.getMyProcessInstancePage(getLoginUserId(), pageReqVO));
@@ -42,6 +42,7 @@ public class BpmProcessInstanceController {
@PostMapping("/create")
@Operation(summary = "新建流程实例")
@PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
+ @DataPermission(enable = false)
public CommonResult createProcessInstance(@Valid @RequestBody BpmProcessInstanceCreateReqVO createReqVO) {
return success(processInstanceService.createProcessInstance(getLoginUserId(), createReqVO));
}
@@ -50,6 +51,7 @@ public class BpmProcessInstanceController {
@Operation(summary = "获得指定流程实例", description = "在【流程详细】界面中,进行调用")
@Parameter(name = "id", description = "流程实例的编号", required = true)
@PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
+ @DataPermission(enable = false)
public CommonResult getProcessInstance(@RequestParam("id") String id) {
return success(processInstanceService.getProcessInstanceVO(id));
}
@@ -57,6 +59,7 @@ public class BpmProcessInstanceController {
@DeleteMapping("/cancel")
@Operation(summary = "取消流程实例", description = "撤回发起的流程")
@PreAuthorize("@ss.hasPermission('bpm:process-instance:cancel')")
+ @DataPermission(enable = false)
public CommonResult cancelProcessInstance(@Valid @RequestBody BpmProcessInstanceCancelReqVO cancelReqVO) {
processInstanceService.cancelProcessInstance(getLoginUserId(), cancelReqVO);
return success(true);
@@ -69,6 +72,7 @@ public class BpmProcessInstanceController {
@GetMapping("/process_instance_group_name_statistics")
@Operation(summary = "根据流程名称分组,统计各个流程的具体实例数据", description = "根据流程名称分组,统计各个流程的具体实例数据")
//@PreAuthorize("@ss.hasPermission('bpm:task:update')")
+ @DataPermission(enable = false)
public CommonResult> getProcessInstancesGroupByModelName(@Valid BpmProcessInstanceStatisticsReqVO reqVO) {
List list = processInstanceService.getProcessInstancesGroupByModelName(reqVO);
return success(list);
@@ -80,6 +84,7 @@ public class BpmProcessInstanceController {
*/
@GetMapping("/process_instance_result_status_statistics")
@Operation(summary = "流程实例的状态统计查询", description = "根据流程状态(处理中,通过,不通过,取消),统计各个状态的数据量")
+ @DataPermission(enable = false)
public CommonResult> getProcessInstancesGroupByResultStatus(@Valid BpmProcessInstanceStatisticsReqVO reqVO) {
List list = processInstanceService.getProcessInstancesGroupByResultStatus(reqVO);
return success(list);
@@ -95,6 +100,7 @@ public class BpmProcessInstanceController {
@GetMapping("/getUserProcessTpo10")
@Operation(summary = "获得用户审批耗时最长Top10", description = "在工作台-给数据权限是可以查看全部数据的用户查询使用")
+ @DataPermission(enable = false)
public CommonResult> getUserProcessTpo10(@Valid BpmProcessInstanceStatisticsReqVO reqVO) {
List list = processInstanceService.getUserProcessTpo10(reqVO);
return success(list);
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/task/BpmProcessInstanceExtMapper.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/task/BpmProcessInstanceExtMapper.java
index 4916ce68..8128614e 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/task/BpmProcessInstanceExtMapper.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/task/BpmProcessInstanceExtMapper.java
@@ -8,10 +8,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessI
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceStatisticsReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceStatisticsRespVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
-import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO;
import org.apache.ibatis.annotations.Mapper;
-import org.apache.ibatis.annotations.Param;
-import org.apache.ibatis.annotations.Select;
import java.util.List;
@@ -21,7 +18,7 @@ public interface BpmProcessInstanceExtMapper extends BaseMapperX selectCCPage(Long userId, BpmProcessInstanceMyPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX()
// .eqIfPresent(BpmProcessInstanceExtDO::getStartUserId, userId)
- .likeIfPresent(BpmProcessInstanceExtDO::getCcids, "["+userId+"]")
+ .likeIfPresent(BpmProcessInstanceExtDO::getCcids, "[" + userId + "]")
.eqIfPresent(BpmProcessInstanceExtDO::getProcessDefinitionId, reqVO.getProcessDefinitionId())
.eqIfPresent(BpmProcessInstanceExtDO::getCategory, reqVO.getCategory())
.eqIfPresent(BpmProcessInstanceExtDO::getStatus, reqVO.getStatus())
@@ -53,36 +50,25 @@ public interface BpmProcessInstanceExtMapper extends BaseMapperX getProcessInstancesGroupByModelName(BpmProcessInstanceStatisticsReqVO reqVO) ;
+ List getProcessInstancesGroupByModelName(BpmProcessInstanceStatisticsReqVO reqVO);
- List getProcessInstancesGroupByResultStatus(BpmProcessInstanceStatisticsReqVO reqVO) ;
+ List getProcessInstancesGroupByResultStatus(BpmProcessInstanceStatisticsReqVO reqVO);
+
+ default PageResult selectStatisticePage(BpmProcessInstanceMyPageReqVO reqVO) {
+ //如果为空,那么查询全部
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .likeIfPresent(BpmProcessInstanceExtDO::getName, reqVO.getName())
+ .eqIfPresent(BpmProcessInstanceExtDO::getProcessDefinitionId, reqVO.getProcessDefinitionId())
+ .eqIfPresent(BpmProcessInstanceExtDO::getCategory, reqVO.getCategory())
+ .eqIfPresent(BpmProcessInstanceExtDO::getStatus, reqVO.getStatus())
+ .eqIfPresent(BpmProcessInstanceExtDO::getResult, reqVO.getResult())
+ .betweenIfPresent(BpmProcessInstanceExtDO::getCreateTime, reqVO.getCreateTime())
+ .orderByDesc(BpmProcessInstanceExtDO::getId));
- default PageResult selectStatisticePage(Long[] userIds, BpmProcessInstanceMyPageReqVO reqVO) {
- if( userIds == null ) {
- //如果为空,那么查询全部
- return selectPage(reqVO, new LambdaQueryWrapperX()
- .likeIfPresent(BpmProcessInstanceExtDO::getName, reqVO.getName())
- .eqIfPresent(BpmProcessInstanceExtDO::getProcessDefinitionId, reqVO.getProcessDefinitionId())
- .eqIfPresent(BpmProcessInstanceExtDO::getCategory, reqVO.getCategory())
- .eqIfPresent(BpmProcessInstanceExtDO::getStatus, reqVO.getStatus())
- .eqIfPresent(BpmProcessInstanceExtDO::getResult, reqVO.getResult())
- .betweenIfPresent(BpmProcessInstanceExtDO::getCreateTime, reqVO.getCreateTime())
- .orderByDesc(BpmProcessInstanceExtDO::getId));
- }else {
- return selectPage(reqVO, new LambdaQueryWrapperX()
- .likeIfPresent(BpmProcessInstanceExtDO::getName, reqVO.getName())
- .eqIfPresent(BpmProcessInstanceExtDO::getProcessDefinitionId, reqVO.getProcessDefinitionId())
- .eqIfPresent(BpmProcessInstanceExtDO::getCategory, reqVO.getCategory())
- .eqIfPresent(BpmProcessInstanceExtDO::getStatus, reqVO.getStatus())
- .eqIfPresent(BpmProcessInstanceExtDO::getResult, reqVO.getResult())
- .betweenIfPresent(BpmProcessInstanceExtDO::getCreateTime, reqVO.getCreateTime())
- .in(BpmProcessInstanceExtDO::getStartUserId,userIds)
- .orderByDesc(BpmProcessInstanceExtDO::getId));
- }
}
- List getUserProcessTpo10(BpmProcessInstanceStatisticsReqVO reqVO) ;
+ List getUserProcessTpo10(BpmProcessInstanceStatisticsReqVO reqVO);
- List selectUnfinishProcessCount(BpmProcessInstanceStatisticsReqVO reqVO) ;
+ List selectUnfinishProcessCount(BpmProcessInstanceStatisticsReqVO reqVO);
}
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/datapermission/config/DataPermissionConfiguration.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/datapermission/config/DataPermissionConfiguration.java
index 0c0c3938..140c9c14 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/datapermission/config/DataPermissionConfiguration.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/datapermission/config/DataPermissionConfiguration.java
@@ -8,20 +8,17 @@ import org.springframework.context.annotation.Configuration;
/**
* 工作流 模块的数据权限 Configuration
*
-
+
*/
-//@Configuration(proxyBeanMethods = false)
+@Configuration(proxyBeanMethods = false)
public class DataPermissionConfiguration {
-// @Bean
-// public DeptDataPermissionRuleCustomizer sysDeptDataPermissionRuleCustomizer() {
-// return rule -> {
-// // dept
-// rule.addDeptColumn(AdminUserDO.class);
-// rule.addDeptColumn(DeptDO.class, "id");
-// // user
-// rule.addUserColumn(BpmProcessInstanceExtDO.class, "start_user_id");
-// };
-// }
+ @Bean
+ public DeptDataPermissionRuleCustomizer sysDeptDataPermissionRuleCustomizer() {
+ return rule -> {
+ // user
+ rule.addUserColumn(BpmProcessInstanceExtDO.class, "start_user_id");
+ };
+ }
}
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
index ab87c40d..8a53a6aa 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
@@ -1 +1 @@
-package cn.iocoder.yudao.module.bpm.service.task;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskApproveReqVO;
import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmTaskAssignRuleMapper;
import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceExtMapper;
import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmTaskExtMapper;
import cn.iocoder.yudao.module.bpm.enums.task.BpmConstants;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum;
import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEventPublisher;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;
import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.delegate.event.FlowableCancelledEvent;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.lang.reflect.Field;
import java.text.DecimalFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
/**
* 流程实例 Service 实现类
*
* ProcessDefinition & ProcessInstance & Execution & Task 的关系:
* 1.
*
* HistoricProcessInstance & ProcessInstance 的关系:
* 1.
*
* 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例
*
*/
@Service
@Validated
@Slf4j
public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService {
@Resource
private TaskService engineTaskService;
@Resource
private BpmTaskAssignRuleMapper taskRuleMapper;
@Resource
private BpmUserGroupService userGroupService;
@Resource
private RuntimeService runtimeService;
@Resource
private BpmProcessInstanceExtMapper processInstanceExtMapper;
@Resource
@Lazy // 解决循环依赖
private BpmTaskService taskService;
@Resource
private BpmProcessDefinitionService processDefinitionService;
@Resource
private HistoryService historyService;
@Resource
private AdminUserApi adminUserApi;
@Resource
private DeptApi deptApi;
@Resource
private BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher;
@Resource
@Lazy // 解决循环依赖
private BpmMessageService messageService;
@Override
public ProcessInstance getProcessInstance(String id) {
return runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult();
}
@Override
public List getProcessInstances(Set ids) {
return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list();
}
@Override
public PageResult getMyProcessInstancePage(Long userId,
BpmProcessInstanceMyPageReqVO pageReqVO) {
// 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
PageResult pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return new PageResult<>(pageResult.getTotal());
}
// 获得流程 Task Map
List processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId);
Map> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds);
List ids = taskMap.values().stream()
.flatMap(Collection::stream)
.map(Task::getAssignee)
.collect(Collectors.toList());
List longIds = ids.stream()
.map(Long::valueOf)
.collect(Collectors.toList());
// 获得 User Map
Map userMap = adminUserApi.getUserMap(longIds);
// 转换返回
return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap, userMap);
}
@Override
@Transactional(rollbackFor = Exception.class)
public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) {
// 获得流程定义
ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId());
// 发起流程
return createProcessInstance0(userId, definition, createReqVO.getVariables(), null);
}
@Override
public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) {
// 获得流程定义
ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey());
// 发起流程
return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey());
}
@Override
public BpmProcessInstanceRespVO getProcessInstanceVO(String id) {
// 获得流程实例
HistoricProcessInstance processInstance = getHistoricProcessInstance(id);
if (processInstance == null) {
return null;
}
BpmProcessInstanceExtDO processInstanceExt = processInstanceExtMapper.selectByProcessInstanceId(id);
Assert.notNull(processInstanceExt, "流程实例拓展({}) 不存在", id);
// 获得流程定义
ProcessDefinition processDefinition = processDefinitionService
.getProcessDefinition(processInstance.getProcessDefinitionId());
Assert.notNull(processDefinition, "流程定义({}) 不存在", processInstance.getProcessDefinitionId());
BpmProcessDefinitionExtDO processDefinitionExt = processDefinitionService.getProcessDefinitionExt(
processInstance.getProcessDefinitionId());
Assert.notNull(processDefinitionExt, "流程定义拓展({}) 不存在", id);
String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId());
// 获得 User
AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())).getCheckedData();
DeptRespDTO dept = null;
if (startUser != null) {
dept = deptApi.getDept(startUser.getDeptId()).getCheckedData();
}
// 拼接结果
return BpmProcessInstanceConvert.INSTANCE.convert2(processInstance, processInstanceExt,
processDefinition, processDefinitionExt, bpmnXml, startUser, dept);
}
@Override
public void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) {
// 校验流程实例存在
ProcessInstance instance = getProcessInstance(cancelReqVO.getId());
if (instance == null) {
throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS);
}
// 只能取消自己的
if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) {
throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF);
}
// 通过删除流程实例,实现流程实例的取消,
// 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。通过历史表查询
deleteProcessInstance(cancelReqVO.getId(),
BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(cancelReqVO.getReason()));
}
/**
* 获得历史的流程实例
*
* @param id 流程实例的编号
* @return 历史的流程实例
*/
@Override
public HistoricProcessInstance getHistoricProcessInstance(String id) {
return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult();
}
@Override
public List getHistoricProcessInstances(Set ids) {
return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list();
}
@Override
public void createProcessInstanceExt(ProcessInstance instance) {
// 获得流程定义
ProcessDefinition definition = processDefinitionService.getProcessDefinition2(instance.getProcessDefinitionId());
// 插入 BpmProcessInstanceExtDO 对象
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
.setProcessInstanceId(instance.getId())
.setProcessDefinitionId(definition.getId())
.setName(instance.getProcessDefinitionName())
.setStartUserId(Long.valueOf(instance.getStartUserId()))
.setCategory(definition.getCategory())
.setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus())
.setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
processInstanceExtMapper.insert(instanceExtDO);
}
@Override
public void updateProcessInstanceExtCancel(FlowableCancelledEvent event) {
// 判断是否为 Reject 不通过。如果是,则不进行更新.
// 因为,updateProcessInstanceExtReject 方法,已经进行更新了
if (BpmProcessInstanceDeleteReasonEnum.isRejectReason((String)event.getCause())) {
return;
}
// 需要主动查询,因为 instance 只有 id 属性
// 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance
HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId());
// 更新拓展表
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
.setProcessInstanceId(event.getProcessInstanceId())
.setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置
.setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
.setResult(BpmProcessInstanceResultEnum.CANCEL.getResult());
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
// 发送流程实例的状态事件
processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
}
@Override
public void updateProcessInstanceExtComplete(ProcessInstance instance) {
// 需要主动查询,因为 instance 只有 id 属性
// 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance
HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId());
// 更新拓展表
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
.setProcessInstanceId(instance.getProcessInstanceId())
.setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置
.setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
.setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); // 如果正常完全,说明审批通过
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
Map processVariables = runtimeService.getVariables(instance.getProcessInstanceId());
String reason = (String)processVariables.get("approve_reason") ;
// 发送流程被通过的消息
messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.convert2ApprovedReq(instance,reason));
// 发送流程实例的状态事件
processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateProcessInstanceExtReject(String id, String reason) {
// 需要主动查询,因为 instance 只有 id 属性
ProcessInstance processInstance = getProcessInstance(id);
// 删除流程实例,以实现驳回任务时,取消整个审批流程
deleteProcessInstance(id, StrUtil.format(BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.format(reason)));
// 更新 status + result
// 注意,不能和上面的逻辑更换位置。因为 deleteProcessInstance 会触发流程的取消,进而调用 updateProcessInstanceExtCancel 方法,
// 设置 result 为 BpmProcessInstanceStatusEnum.CANCEL,显然和 result 不一定是一致的
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessInstanceId(id)
.setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
.setResult(BpmProcessInstanceResultEnum.REJECT.getResult());
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
// 发送流程被不通过的消息
messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, reason));
// 发送流程实例的状态事件
processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
}
private void deleteProcessInstance(String id, String reason) {
runtimeService.deleteProcessInstance(id, reason);
}
private String createProcessInstance0(Long userId, ProcessDefinition definition,
Map variables, String businessKey) {
// 校验流程定义
if (definition == null) {
throw exception(PROCESS_DEFINITION_NOT_EXISTS);
}
if (definition.isSuspended()) {
throw exception(PROCESS_DEFINITION_IS_SUSPENDED);
}
// 创建流程实例
ProcessInstance instance = runtimeService.createProcessInstanceBuilder()
.processDefinitionId(definition.getId())
.businessKey(businessKey)
.name(definition.getName().trim())
.variables(variables)
.start();
// 设置流程名字
runtimeService.setProcessInstanceName(instance.getId(), definition.getName());
// 补全流程实例的拓展表
processInstanceExtMapper.updateByProcessInstanceId(new BpmProcessInstanceExtDO().setProcessInstanceId(instance.getId())
.setFormVariables(variables));
/** 创建流程后,添加抄送人 End add by yj 2024.1.4 */
processCCToUsers(definition,instance) ;
/** 通过自己发起的流程 */
approveSelfTask(instance.getId()) ;
return instance.getId();
}
private void approveSelfTask(String processInstanceId ) {
List tasks =engineTaskService.createTaskQuery().processInstanceId(processInstanceId).list() ;
if( tasks != null && tasks.size() > 0) {
Task task = tasks.get(0) ;
String assigneeId = task.getAssignee();
//如果当前登陆用户是审批人,那么自动审批通过
if( assigneeId.equals( SecurityFrameworkUtils.getLoginUserId().toString() )) {
BpmTaskApproveReqVO reqVO = new BpmTaskApproveReqVO() ;
reqVO.setId(task.getId()) ;
reqVO.setReason(BpmConstants.AUTO_APPRAVAL);
taskService.approveTask(getLoginUserId(), reqVO);
}
}
}
/**
* 获得抄送我的流程实例的分页
*
* @param userId 用户编号
* @param pageReqVO 分页请求
* @return 流程实例的分页
*/
public PageResult getMyCCProcessInstancePage(Long userId,
BpmProcessInstanceMyPageReqVO pageReqVO) {
// 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
PageResult pageResult = processInstanceExtMapper.selectCCPage(userId, pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return new PageResult<>(pageResult.getTotal());
}
// 获得流程 Task Map
List processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId);
Map> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds);
// 转换返回
return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap,null);
}
public List getProcessInstancesGroupByModelName(BpmProcessInstanceStatisticsReqVO pageReqVO){
pageReqVO = getUserids(pageReqVO) ;
return processInstanceExtMapper.getProcessInstancesGroupByModelName(pageReqVO) ;
}
public List getProcessInstancesGroupByResultStatus(BpmProcessInstanceStatisticsReqVO pageReqVO){
pageReqVO = getUserids(pageReqVO) ;
return processInstanceExtMapper.getProcessInstancesGroupByResultStatus(pageReqVO) ;
}
public PageResult getStatisticsProcessInstancePage(@Valid BpmProcessInstanceMyPageReqVO pageReqVO) {
pageReqVO = getUserids(pageReqVO) ;
// 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
PageResult pageResult = processInstanceExtMapper.selectStatisticePage(pageReqVO.getUserIds(), pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return new PageResult<>(pageResult.getTotal());
}
// 获得流程 Task Map
List processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId);
Map> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds);
List ids = taskMap.values().stream()
.flatMap(Collection::stream)
.map(Task::getAssignee)
.collect(Collectors.toList());
List longIds = ids.stream()
.map(Long::valueOf)
.collect(Collectors.toList());
//获得 审批人 User Map
Map assigneeUserMap = adminUserApi.getUserMap(longIds);
//获得 发起人 User Map
List bpieDOs = pageResult.getList() ;
longIds = new ArrayList<>() ;
for (BpmProcessInstanceExtDO bpieDO: bpieDOs) {
longIds.add(bpieDO.getStartUserId()) ;
}
Map startUserMap = adminUserApi.getUserMap(longIds);
// 转换返回
return BpmProcessInstanceConvert.INSTANCE.convertStatisticsPage(pageResult, taskMap, assigneeUserMap,startUserMap);
}
@Resource
PermissionApi permissionApi;
/**
* 根据数据权限,查询关联操作的用户IDS
* @param pageReqVO
* @return
* @param
*/
private T getUserids(T pageReqVO) {
try {
Class clazz = (Class) pageReqVO.getClass();
Field idField = clazz.getDeclaredField("userIds");
idField.setAccessible(true); // 设置可访问性
Long[] userIds = null;
Long userId = WebFrameworkUtils.getLoginUserId();
DeptDataPermissionRespDTO deptDataPermission = permissionApi.getDeptDataPermission(userId).getCheckedData();
//查询全部
if (deptDataPermission.getAll()) {
//idField.set(pageReqVO, null); // 设置属性值
return pageReqVO;
}
// 情况二,即不能查看部门,又不能查看自己,则说明 100% 无权限
if (CollUtil.isEmpty(deptDataPermission.getDeptIds())
&& Boolean.FALSE.equals(deptDataPermission.getSelf())) {
//设置成0,一个不存在的用户Id,就查询不到数据了。
userIds = new Long[]{0L};
idField.set(pageReqVO, userIds);
return pageReqVO;
}
//情况三 至查询自己
if (deptDataPermission.getSelf()) {
userIds = new Long[]{userId};
idField.set(pageReqVO, userIds);
return pageReqVO;
}
Set deptIds = deptDataPermission.getDeptIds();
//查询部门关联的用户Id
List users = adminUserApi.getUserListByDeptIds(deptIds).getCheckedData();
List tempList = new ArrayList<>();
for (AdminUserRespDTO user : users) {
Long id = user.getId();
tempList.add(id);
}
tempList.add(userId);
userIds = tempList.stream().toArray(Long[]::new); //将临时的List集合转换成数组集合
idField.set(pageReqVO, userIds);
return pageReqVO;
}catch (Exception exception){
exception.printStackTrace();
throw exception(BPM_SYSTEM_BUG);
}
}
/**
* /创建流程后,添加抄送人 Begin add by yj 2024.1.4
* 在设计流程的时候,需要添加一个任务块 名字必须叫Activity_cc 分配权限的时候,需要选择用户组。
*
* @param definition
* @param instance
*/
private void processCCToUsers(ProcessDefinition definition, ProcessInstance instance) {
//获取bpm_task_assign_reule (Bpm 任务规则表)的流程中有没有配置抄送节点 固定抄送名称为:Activity_cc
String processDefinitionId = definition.getId() ;
List rules = taskRuleMapper.selectListByProcessDefinitionId(processDefinitionId, null);
for(BpmTaskAssignRuleDO rule :rules ){
String key = rule.getTaskDefinitionKey() ; //任务名称
Integer type = rule.getType() ;
if( !key.isEmpty() && key.equals(BpmConstants.CC_NAME) && type == 40 ) {
StringBuffer str = new StringBuffer() ;
Set options = rule.getOptions() ;
List list = new ArrayList(options);
for(Long groupId : list) {
//需要根据这个groupId,查询这个组中的用户id
BpmUserGroupDO userGroup = userGroupService.getUserGroup(groupId);
Set userIds = userGroup.getMemberUserIds() ;
List userIdList = new ArrayList(userIds);
for(Long user_id : userIdList) {
str.append("[").append(user_id).append("]") ;
}
}
String ccids = str.toString() ;
//根据processDefinitionId 将ccids保存到bpm_process_instance_ext中的ccids字段
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessDefinitionId(processDefinitionId)
.setCcids(ccids) .setProcessInstanceId(instance.getProcessInstanceId());
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
break ;
}
}
}
@Resource
private BpmTaskExtMapper taskExtMapper;
@Override
public List getUserProcessTpo10(BpmProcessInstanceStatisticsReqVO pageReqVO) {
//按审核人分组查询,统计已完成流程数量及平均耗时间
List respVOS = processInstanceExtMapper.getUserProcessTpo10(pageReqVO);
if( respVOS == null || respVOS.size() == 0) {
return null ;
}
List idList = respVOS.stream()
.map(BpmProcessFinishStatisticsRespVO::getUserId)
.collect(Collectors.toList());
//根据userId,查询UserMap
Map userMap = adminUserApi.getUserMap(idList) ;
Long[] idsArray = new Long[userMap.size()];
// 使用 map 的 keySet() 方法获取键集合
Set keys = userMap.keySet();
// 遍历键集合,将每个键添加到数组中
int index = 0;
for (Long key : keys) {
idsArray[index++] = key;
}
pageReqVO.setUserIds(idsArray) ;
//按审核人分组查询,未审批完成的流程数量
List bpmTaskExtDOs = processInstanceExtMapper.selectUnfinishProcessCount( pageReqVO ) ;
//按审核人分组查询,未完成的记录数
Map unFinfishCountCountMap = new HashMap<>();
for (BpmProcessFinishStatisticsRespVO item : bpmTaskExtDOs) {
unFinfishCountCountMap.put(item.getUserId(), item.getUnFinfishCount());
}
respVOS.forEach(respVO -> respVO.setName( userMap.get(respVO.getUserId()).getNickname()));
respVOS.forEach(respVO -> respVO.setUnFinfishCount( unFinfishCountCountMap.get(respVO.getUserId())));
//获取排行榜第一记录的耗时,作为基础数据
double num = Double.parseDouble(respVOS.get(0).getUserTime()); // 先将字符串转换为双精度浮点数
int baseNumber = (int)num; // 再通过类型转换操作符将其转换为整数
DecimalFormat df = new DecimalFormat("#.00");
respVOS.forEach(respVO -> {
//格式化流程完成率
float finfishCount = (float) respVO.getFinfishCount() ;
float unFinfishCount = respVO.getUnFinfishCount() == null ? 0: respVO.getUnFinfishCount();
float all = finfishCount + unFinfishCount ;
float result = finfishCount / all;
float rate = result * 100 ;
respVO.setCompletionRate(df.format(rate)+"%") ;
double dValue = Double.parseDouble(respVO.getUserTime()); // 将字符串转换为double类型
int roundedValue = (int) Math.round(dValue); // 使用Math.round()进行四舍五入,并转换为int类型
respVO.setUserTime(roundedValue+"") ;
//格式化进度百度比, 参照最高的数据进行百分比显示
double percentage = ((int)Double.parseDouble(respVO.getUserTime()) / (double) baseNumber) * 100;
respVO.setPercentage((int) Math.round(percentage)) ;
//设置未完成
respVO.setUnFinfishCount( Integer.valueOf((int)unFinfishCount)) ;
});
return respVOS ;
}
@Override
public BpmProcessInstanceExtDO getProcessInstanceDO(String id) {
return processInstanceExtMapper.selectByProcessInstanceId(id);
}
}
\ No newline at end of file
+package cn.iocoder.yudao.module.bpm.service.task;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskApproveReqVO;
import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmTaskAssignRuleMapper;
import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceExtMapper;
import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmTaskExtMapper;
import cn.iocoder.yudao.module.bpm.enums.task.BpmConstants;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum;
import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEventPublisher;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;
import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.delegate.event.FlowableCancelledEvent;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.lang.reflect.Field;
import java.text.DecimalFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
/**
* 流程实例 Service 实现类
*
* ProcessDefinition & ProcessInstance & Execution & Task 的关系:
* 1.
*
* HistoricProcessInstance & ProcessInstance 的关系:
* 1.
*
* 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例
*
*/
@Service
@Validated
@Slf4j
public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService {
@Resource
private TaskService engineTaskService;
@Resource
private BpmTaskAssignRuleMapper taskRuleMapper;
@Resource
private BpmUserGroupService userGroupService;
@Resource
private RuntimeService runtimeService;
@Resource
private BpmProcessInstanceExtMapper processInstanceExtMapper;
@Resource
@Lazy // 解决循环依赖
private BpmTaskService taskService;
@Resource
private BpmProcessDefinitionService processDefinitionService;
@Resource
private HistoryService historyService;
@Resource
private AdminUserApi adminUserApi;
@Resource
private DeptApi deptApi;
@Resource
private BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher;
@Resource
@Lazy // 解决循环依赖
private BpmMessageService messageService;
@Override
public ProcessInstance getProcessInstance(String id) {
return runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult();
}
@Override
public List getProcessInstances(Set ids) {
return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list();
}
@Override
public PageResult getMyProcessInstancePage(Long userId,
BpmProcessInstanceMyPageReqVO pageReqVO) {
// 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
PageResult pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return new PageResult<>(pageResult.getTotal());
}
// 获得流程 Task Map
List processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId);
Map> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds);
List ids = taskMap.values().stream()
.flatMap(Collection::stream)
.map(Task::getAssignee)
.collect(Collectors.toList());
List longIds = ids.stream()
.map(Long::valueOf)
.collect(Collectors.toList());
// 获得 User Map
Map userMap = adminUserApi.getUserMap(longIds);
// 转换返回
return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap, userMap);
}
@Override
@Transactional(rollbackFor = Exception.class)
public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) {
// 获得流程定义
ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId());
// 发起流程
return createProcessInstance0(userId, definition, createReqVO.getVariables(), null);
}
@Override
public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) {
// 获得流程定义
ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey());
// 发起流程
return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey());
}
@Override
public BpmProcessInstanceRespVO getProcessInstanceVO(String id) {
// 获得流程实例
HistoricProcessInstance processInstance = getHistoricProcessInstance(id);
if (processInstance == null) {
return null;
}
BpmProcessInstanceExtDO processInstanceExt = processInstanceExtMapper.selectByProcessInstanceId(id);
Assert.notNull(processInstanceExt, "流程实例拓展({}) 不存在", id);
// 获得流程定义
ProcessDefinition processDefinition = processDefinitionService
.getProcessDefinition(processInstance.getProcessDefinitionId());
Assert.notNull(processDefinition, "流程定义({}) 不存在", processInstance.getProcessDefinitionId());
BpmProcessDefinitionExtDO processDefinitionExt = processDefinitionService.getProcessDefinitionExt(
processInstance.getProcessDefinitionId());
Assert.notNull(processDefinitionExt, "流程定义拓展({}) 不存在", id);
String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId());
// 获得 User
AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())).getCheckedData();
DeptRespDTO dept = null;
if (startUser != null) {
dept = deptApi.getDept(startUser.getDeptId()).getCheckedData();
}
// 拼接结果
return BpmProcessInstanceConvert.INSTANCE.convert2(processInstance, processInstanceExt,
processDefinition, processDefinitionExt, bpmnXml, startUser, dept);
}
@Override
public void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) {
// 校验流程实例存在
ProcessInstance instance = getProcessInstance(cancelReqVO.getId());
if (instance == null) {
throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS);
}
// 只能取消自己的
if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) {
throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF);
}
// 通过删除流程实例,实现流程实例的取消,
// 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。通过历史表查询
deleteProcessInstance(cancelReqVO.getId(),
BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(cancelReqVO.getReason()));
}
/**
* 获得历史的流程实例
*
* @param id 流程实例的编号
* @return 历史的流程实例
*/
@Override
public HistoricProcessInstance getHistoricProcessInstance(String id) {
return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult();
}
@Override
public List getHistoricProcessInstances(Set ids) {
return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list();
}
@Override
public void createProcessInstanceExt(ProcessInstance instance) {
// 获得流程定义
ProcessDefinition definition = processDefinitionService.getProcessDefinition2(instance.getProcessDefinitionId());
// 插入 BpmProcessInstanceExtDO 对象
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
.setProcessInstanceId(instance.getId())
.setProcessDefinitionId(definition.getId())
.setName(instance.getProcessDefinitionName())
.setStartUserId(Long.valueOf(instance.getStartUserId()))
.setCategory(definition.getCategory())
.setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus())
.setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
processInstanceExtMapper.insert(instanceExtDO);
}
@Override
public void updateProcessInstanceExtCancel(FlowableCancelledEvent event) {
// 判断是否为 Reject 不通过。如果是,则不进行更新.
// 因为,updateProcessInstanceExtReject 方法,已经进行更新了
if (BpmProcessInstanceDeleteReasonEnum.isRejectReason((String)event.getCause())) {
return;
}
// 需要主动查询,因为 instance 只有 id 属性
// 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance
HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId());
// 更新拓展表
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
.setProcessInstanceId(event.getProcessInstanceId())
.setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置
.setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
.setResult(BpmProcessInstanceResultEnum.CANCEL.getResult());
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
// 发送流程实例的状态事件
processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
}
@Override
public void updateProcessInstanceExtComplete(ProcessInstance instance) {
// 需要主动查询,因为 instance 只有 id 属性
// 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance
HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId());
// 更新拓展表
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO()
.setProcessInstanceId(instance.getProcessInstanceId())
.setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置
.setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
.setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); // 如果正常完全,说明审批通过
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
Map processVariables = runtimeService.getVariables(instance.getProcessInstanceId());
String reason = (String)processVariables.get("approve_reason") ;
// 发送流程被通过的消息
messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.convert2ApprovedReq(instance,reason));
// 发送流程实例的状态事件
processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateProcessInstanceExtReject(String id, String reason) {
// 需要主动查询,因为 instance 只有 id 属性
ProcessInstance processInstance = getProcessInstance(id);
// 删除流程实例,以实现驳回任务时,取消整个审批流程
deleteProcessInstance(id, StrUtil.format(BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.format(reason)));
// 更新 status + result
// 注意,不能和上面的逻辑更换位置。因为 deleteProcessInstance 会触发流程的取消,进而调用 updateProcessInstanceExtCancel 方法,
// 设置 result 为 BpmProcessInstanceStatusEnum.CANCEL,显然和 result 不一定是一致的
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessInstanceId(id)
.setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
.setResult(BpmProcessInstanceResultEnum.REJECT.getResult());
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
// 发送流程被不通过的消息
messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, reason));
// 发送流程实例的状态事件
processInstanceResultEventPublisher.sendProcessInstanceResultEvent(
BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult()));
}
private void deleteProcessInstance(String id, String reason) {
runtimeService.deleteProcessInstance(id, reason);
}
private String createProcessInstance0(Long userId, ProcessDefinition definition,
Map variables, String businessKey) {
// 校验流程定义
if (definition == null) {
throw exception(PROCESS_DEFINITION_NOT_EXISTS);
}
if (definition.isSuspended()) {
throw exception(PROCESS_DEFINITION_IS_SUSPENDED);
}
// 创建流程实例
ProcessInstance instance = runtimeService.createProcessInstanceBuilder()
.processDefinitionId(definition.getId())
.businessKey(businessKey)
.name(definition.getName().trim())
.variables(variables)
.start();
// 设置流程名字
runtimeService.setProcessInstanceName(instance.getId(), definition.getName());
// 补全流程实例的拓展表
processInstanceExtMapper.updateByProcessInstanceId(new BpmProcessInstanceExtDO().setProcessInstanceId(instance.getId())
.setFormVariables(variables));
/** 创建流程后,添加抄送人 End add by yj 2024.1.4 */
processCCToUsers(definition,instance) ;
/** 通过自己发起的流程 */
approveSelfTask(instance.getId()) ;
return instance.getId();
}
private void approveSelfTask(String processInstanceId ) {
List tasks =engineTaskService.createTaskQuery().processInstanceId(processInstanceId).list() ;
if( tasks != null && tasks.size() > 0) {
Task task = tasks.get(0) ;
String assigneeId = task.getAssignee();
//如果当前登陆用户是审批人,那么自动审批通过
if( assigneeId.equals( SecurityFrameworkUtils.getLoginUserId().toString() )) {
BpmTaskApproveReqVO reqVO = new BpmTaskApproveReqVO() ;
reqVO.setId(task.getId()) ;
reqVO.setReason(BpmConstants.AUTO_APPRAVAL);
taskService.approveTask(getLoginUserId(), reqVO);
}
}
}
/**
* 获得抄送我的流程实例的分页
*
* @param userId 用户编号
* @param pageReqVO 分页请求
* @return 流程实例的分页
*/
public PageResult getMyCCProcessInstancePage(Long userId,
BpmProcessInstanceMyPageReqVO pageReqVO) {
// 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
PageResult pageResult = processInstanceExtMapper.selectCCPage(userId, pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return new PageResult<>(pageResult.getTotal());
}
// 获得流程 Task Map
List processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId);
Map> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds);
// 转换返回
return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap,null);
}
public List getProcessInstancesGroupByModelName(BpmProcessInstanceStatisticsReqVO pageReqVO){
pageReqVO = getUserids(pageReqVO) ;
return processInstanceExtMapper.getProcessInstancesGroupByModelName(pageReqVO) ;
}
public List getProcessInstancesGroupByResultStatus(BpmProcessInstanceStatisticsReqVO pageReqVO){
pageReqVO = getUserids(pageReqVO) ;
return processInstanceExtMapper.getProcessInstancesGroupByResultStatus(pageReqVO) ;
}
public PageResult getStatisticsProcessInstancePage(@Valid BpmProcessInstanceMyPageReqVO pageReqVO) {
// 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页
PageResult pageResult = processInstanceExtMapper.selectStatisticePage(pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return new PageResult<>(pageResult.getTotal());
}
// 获得流程 Task Map
List processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId);
Map> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds);
List ids = taskMap.values().stream()
.flatMap(Collection::stream)
.map(Task::getAssignee)
.collect(Collectors.toList());
List longIds = ids.stream()
.map(Long::valueOf)
.collect(Collectors.toList());
//获得 审批人 User Map
Map assigneeUserMap = adminUserApi.getUserMap(longIds);
//获得 发起人 User Map
List bpieDOs = pageResult.getList() ;
longIds = new ArrayList<>() ;
for (BpmProcessInstanceExtDO bpieDO: bpieDOs) {
longIds.add(bpieDO.getStartUserId()) ;
}
Map startUserMap = adminUserApi.getUserMap(longIds);
// 转换返回
return BpmProcessInstanceConvert.INSTANCE.convertStatisticsPage(pageResult, taskMap, assigneeUserMap,startUserMap);
}
@Resource
PermissionApi permissionApi;
/**
* 根据数据权限,查询关联操作的用户IDS
* @param pageReqVO
* @return
* @param
*/
private T getUserids(T pageReqVO) {
try {
Class clazz = (Class) pageReqVO.getClass();
Field idField = clazz.getDeclaredField("userIds");
idField.setAccessible(true); // 设置可访问性
Long[] userIds = null;
Long userId = WebFrameworkUtils.getLoginUserId();
DeptDataPermissionRespDTO deptDataPermission = permissionApi.getDeptDataPermission(userId).getCheckedData();
//查询全部
if (deptDataPermission.getAll()) {
//idField.set(pageReqVO, null); // 设置属性值
return pageReqVO;
}
// 情况二,即不能查看部门,又不能查看自己,则说明 100% 无权限
if (CollUtil.isEmpty(deptDataPermission.getDeptIds())
&& Boolean.FALSE.equals(deptDataPermission.getSelf())) {
//设置成0,一个不存在的用户Id,就查询不到数据了。
userIds = new Long[]{0L};
idField.set(pageReqVO, userIds);
return pageReqVO;
}
//情况三 至查询自己
if (deptDataPermission.getSelf()) {
userIds = new Long[]{userId};
idField.set(pageReqVO, userIds);
return pageReqVO;
}
Set deptIds = deptDataPermission.getDeptIds();
//查询部门关联的用户Id
List users = adminUserApi.getUserListByDeptIds(deptIds).getCheckedData();
List tempList = new ArrayList<>();
for (AdminUserRespDTO user : users) {
Long id = user.getId();
tempList.add(id);
}
tempList.add(userId);
userIds = tempList.stream().toArray(Long[]::new); //将临时的List集合转换成数组集合
idField.set(pageReqVO, userIds);
return pageReqVO;
}catch (Exception exception){
exception.printStackTrace();
throw exception(BPM_SYSTEM_BUG);
}
}
/**
* /创建流程后,添加抄送人 Begin add by yj 2024.1.4
* 在设计流程的时候,需要添加一个任务块 名字必须叫Activity_cc 分配权限的时候,需要选择用户组。
*
* @param definition
* @param instance
*/
private void processCCToUsers(ProcessDefinition definition, ProcessInstance instance) {
//获取bpm_task_assign_reule (Bpm 任务规则表)的流程中有没有配置抄送节点 固定抄送名称为:Activity_cc
String processDefinitionId = definition.getId() ;
List rules = taskRuleMapper.selectListByProcessDefinitionId(processDefinitionId, null);
for(BpmTaskAssignRuleDO rule :rules ){
String key = rule.getTaskDefinitionKey() ; //任务名称
Integer type = rule.getType() ;
if( !key.isEmpty() && key.equals(BpmConstants.CC_NAME) && type == 40 ) {
StringBuffer str = new StringBuffer() ;
Set options = rule.getOptions() ;
List list = new ArrayList(options);
for(Long groupId : list) {
//需要根据这个groupId,查询这个组中的用户id
BpmUserGroupDO userGroup = userGroupService.getUserGroup(groupId);
Set userIds = userGroup.getMemberUserIds() ;
List userIdList = new ArrayList(userIds);
for(Long user_id : userIdList) {
str.append("[").append(user_id).append("]") ;
}
}
String ccids = str.toString() ;
//根据processDefinitionId 将ccids保存到bpm_process_instance_ext中的ccids字段
BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessDefinitionId(processDefinitionId)
.setCcids(ccids) .setProcessInstanceId(instance.getProcessInstanceId());
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
break ;
}
}
}
@Resource
private BpmTaskExtMapper taskExtMapper;
@Override
public List getUserProcessTpo10(BpmProcessInstanceStatisticsReqVO pageReqVO) {
//按审核人分组查询,统计已完成流程数量及平均耗时间
List respVOS = processInstanceExtMapper.getUserProcessTpo10(pageReqVO);
if( respVOS == null || respVOS.size() == 0) {
return null ;
}
List idList = respVOS.stream()
.map(BpmProcessFinishStatisticsRespVO::getUserId)
.collect(Collectors.toList());
//根据userId,查询UserMap
Map userMap = adminUserApi.getUserMap(idList) ;
Long[] idsArray = new Long[userMap.size()];
// 使用 map 的 keySet() 方法获取键集合
Set keys = userMap.keySet();
// 遍历键集合,将每个键添加到数组中
int index = 0;
for (Long key : keys) {
idsArray[index++] = key;
}
pageReqVO.setUserIds(idsArray) ;
//按审核人分组查询,未审批完成的流程数量
List bpmTaskExtDOs = processInstanceExtMapper.selectUnfinishProcessCount( pageReqVO ) ;
//按审核人分组查询,未完成的记录数
Map unFinfishCountCountMap = new HashMap<>();
for (BpmProcessFinishStatisticsRespVO item : bpmTaskExtDOs) {
unFinfishCountCountMap.put(item.getUserId(), item.getUnFinfishCount());
}
respVOS.forEach(respVO -> respVO.setName( userMap.get(respVO.getUserId()).getNickname()));
respVOS.forEach(respVO -> respVO.setUnFinfishCount( unFinfishCountCountMap.get(respVO.getUserId())));
//获取排行榜第一记录的耗时,作为基础数据
double num = Double.parseDouble(respVOS.get(0).getUserTime()); // 先将字符串转换为双精度浮点数
int baseNumber = (int)num; // 再通过类型转换操作符将其转换为整数
DecimalFormat df = new DecimalFormat("#.00");
respVOS.forEach(respVO -> {
//格式化流程完成率
float finfishCount = (float) respVO.getFinfishCount() ;
float unFinfishCount = respVO.getUnFinfishCount() == null ? 0: respVO.getUnFinfishCount();
float all = finfishCount + unFinfishCount ;
float result = finfishCount / all;
float rate = result * 100 ;
respVO.setCompletionRate(df.format(rate)+"%") ;
double dValue = Double.parseDouble(respVO.getUserTime()); // 将字符串转换为double类型
int roundedValue = (int) Math.round(dValue); // 使用Math.round()进行四舍五入,并转换为int类型
respVO.setUserTime(roundedValue+"") ;
//格式化进度百度比, 参照最高的数据进行百分比显示
double percentage = ((int)Double.parseDouble(respVO.getUserTime()) / (double) baseNumber) * 100;
respVO.setPercentage((int) Math.round(percentage)) ;
//设置未完成
respVO.setUnFinfishCount( Integer.valueOf((int)unFinfishCount)) ;
});
return respVOS ;
}
@Override
public BpmProcessInstanceExtDO getProcessInstanceDO(String id) {
return processInstanceExtMapper.selectByProcessInstanceId(id);
}
}
\ No newline at end of file
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/resources/bootstrap.yaml b/yudao-module-bpm/yudao-module-bpm-biz/src/main/resources/bootstrap.yaml
index 90fc58af..f2da21dc 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/resources/bootstrap.yaml
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/resources/bootstrap.yaml
@@ -3,7 +3,7 @@ spring:
name: bpm-server
profiles:
- active: local #local
+ active: dev #local
# active: prod
server:
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/resources/mapper/oa/BpmOAProcurePayItemMapper.xml b/yudao-module-bpm/yudao-module-bpm-biz/src/main/resources/mapper/oa/BpmOAProcurePayItemMapper.xml
index cfa7974e..9a2e29de 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/resources/mapper/oa/BpmOAProcurePayItemMapper.xml
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/resources/mapper/oa/BpmOAProcurePayItemMapper.xml
@@ -18,6 +18,7 @@
b.reason,
b.procure_type,
b.expected_delivery_date,
+ b.procure_detail_json as procureDetailJson,
b.remarks,
b.total_money,
b.total_money_chinese
diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/bootstrap.yaml b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/bootstrap.yaml
index b28d9964..64e03794 100644
--- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/bootstrap.yaml
+++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/bootstrap.yaml
@@ -3,7 +3,7 @@ spring:
name: infra-server
profiles:
- active: local #local
+ active: dev #local
# active: prod
server:
diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java
index 19c6311e..1824c8c0 100644
--- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java
+++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java
@@ -171,6 +171,13 @@ public interface ErrorCodeConstants {
// ========== 站内信发送 1-002-028-000 ==========
ErrorCode NOTIFY_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_028_000, "模板参数({})缺失");
+ // ========== 站内信发送 1-003-001-000 ==========
+ ErrorCode SCHEDULING_NOT_EXISTS = new ErrorCode(1_003_001_000, "排班制考勤设置不存在");
+ ErrorCode PUNCH_RECORD_NOT_EXISTS = new ErrorCode(1_003_002_000, "用户打卡记录不存在");
+ ErrorCode GROUP_USER_NOT_EXISTS = new ErrorCode(1_003_003_000, "考勤组人员不存在");
+ ErrorCode GROUP_SHIFT_NOT_EXISTS = new ErrorCode(1_003_004_000, "考勤组班次不存在");
+ ErrorCode GROUP_NOT_EXISTS = new ErrorCode(1_003_005_000, "考勤组不存在");
+ ErrorCode FIXED_NOT_EXISTS = new ErrorCode(1_003_006_000, "固定班制考勤设置");
ErrorCode FORM_NOT_EXISTS = new ErrorCode(1_009_010_000, "动态表单不存在");
ErrorCode FORM_FIELD_REPEAT = new ErrorCode(1_009_010_001, "表单项({}) 和 ({}) 使用了相同的字段名({})");
ErrorCode LOG_INSTANCE_NOT_EXISTS = new ErrorCode(1_009_010_002, "日志实例的拓展不存在");
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/fixed/AttendanceFixedController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/fixed/AttendanceFixedController.java
new file mode 100644
index 00000000..0aa9db43
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/fixed/AttendanceFixedController.java
@@ -0,0 +1,95 @@
+package cn.iocoder.yudao.module.system.controller.admin.fixed;
+
+import org.springframework.web.bind.annotation.*;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.security.access.prepost.PreAuthorize;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+
+import javax.validation.constraints.*;
+import javax.validation.*;
+import javax.servlet.http.*;
+import java.util.*;
+import java.io.IOException;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
+import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
+
+import cn.iocoder.yudao.module.system.controller.admin.fixed.vo.*;
+import cn.iocoder.yudao.module.system.dal.dataobject.fixed.AttendanceFixedDO;
+import cn.iocoder.yudao.module.system.service.fixed.AttendanceFixedService;
+
+@Tag(name = "管理后台 - 固定班制考勤设置")
+@RestController
+@RequestMapping("/system/attendance/fixed")
+@Validated
+public class AttendanceFixedController {
+
+ @Resource
+ private AttendanceFixedService fixedService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建固定班制考勤设置")
+ @PreAuthorize("@ss.hasPermission('attendance:fixed:create')")
+ public CommonResult createFixed(@Valid @RequestBody AttendanceFixedSaveReqVO createReqVO) {
+ return success(fixedService.createFixed(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新固定班制考勤设置")
+ @PreAuthorize("@ss.hasPermission('attendance:fixed:update')")
+ public CommonResult updateFixed(@Valid @RequestBody AttendanceFixedSaveReqVO updateReqVO) {
+ fixedService.updateFixed(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除固定班制考勤设置")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('attendance:fixed:delete')")
+ public CommonResult deleteFixed(@RequestParam("id") Long id) {
+ fixedService.deleteFixed(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得固定班制考勤设置")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('attendance:fixed:query')")
+ public CommonResult getFixed(@RequestParam("id") Long id) {
+ AttendanceFixedDO fixed = fixedService.getFixed(id);
+ return success(BeanUtils.toBean(fixed, AttendanceFixedRespVO.class));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得固定班制考勤设置分页")
+ @PreAuthorize("@ss.hasPermission('attendance:fixed:query')")
+ public CommonResult> getFixedPage(@Valid AttendanceFixedPageReqVO pageReqVO) {
+ PageResult pageResult = fixedService.getFixedPage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, AttendanceFixedRespVO.class));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出固定班制考勤设置 Excel")
+ @PreAuthorize("@ss.hasPermission('attendance:fixed:export')")
+ @OperateLog(type = EXPORT)
+ public void exportFixedExcel(@Valid AttendanceFixedPageReqVO pageReqVO,
+ HttpServletResponse response) throws IOException {
+ pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ List list = fixedService.getFixedPage(pageReqVO).getList();
+ // 导出 Excel
+ ExcelUtils.write(response, "固定班制考勤设置.xls", "数据", AttendanceFixedRespVO.class,
+ BeanUtils.toBean(list, AttendanceFixedRespVO.class));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/fixed/vo/AttendanceFixedPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/fixed/vo/AttendanceFixedPageReqVO.java
new file mode 100644
index 00000000..a7ac1a1e
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/fixed/vo/AttendanceFixedPageReqVO.java
@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.module.system.controller.admin.fixed.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 固定班制考勤设置分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class AttendanceFixedPageReqVO extends PageParam {
+
+ @Schema(description = "考勤组id", example = "19908")
+ private Long attendanceGroupId;
+
+ @Schema(description = "周时间 1-7")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private Integer[] weekTime;
+
+ @Schema(description = "班次id", example = "16385")
+ private Long attendanceGroupShiftId;
+
+ @Schema(description = "创建时间")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime[] createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/fixed/vo/AttendanceFixedRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/fixed/vo/AttendanceFixedRespVO.java
new file mode 100644
index 00000000..b2e76400
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/fixed/vo/AttendanceFixedRespVO.java
@@ -0,0 +1,36 @@
+package cn.iocoder.yudao.module.system.controller.admin.fixed.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.util.*;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+import com.alibaba.excel.annotation.*;
+
+@Schema(description = "管理后台 - 固定班制考勤设置 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class AttendanceFixedRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23824")
+ @ExcelProperty("编号")
+ private Long id;
+
+ @Schema(description = "考勤组id", example = "19908")
+ @ExcelProperty("考勤组id")
+ private Long attendanceGroupId;
+
+ @Schema(description = "周时间 1-7")
+ @ExcelProperty("周时间 1-7")
+ private Integer weekTime;
+
+ @Schema(description = "班次id", example = "16385")
+ @ExcelProperty("班次id")
+ private Long attendanceGroupShiftId;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("创建时间")
+ private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/fixed/vo/AttendanceFixedSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/fixed/vo/AttendanceFixedSaveReqVO.java
new file mode 100644
index 00000000..48f00124
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/fixed/vo/AttendanceFixedSaveReqVO.java
@@ -0,0 +1,25 @@
+package cn.iocoder.yudao.module.system.controller.admin.fixed.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+import java.util.*;
+
+@Schema(description = "管理后台 - 固定班制考勤设置新增/修改 Request VO")
+@Data
+public class AttendanceFixedSaveReqVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23824")
+ private Long id;
+
+ @Schema(description = "考勤组id", example = "19908")
+ private Long attendanceGroupId;
+
+ @Schema(description = "周时间 1-7")
+ private Integer weekTime;
+
+ @Schema(description = "班次id", example = "16385")
+ private Long attendanceGroupShiftId;
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/group/AttendanceGroupController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/group/AttendanceGroupController.java
new file mode 100644
index 00000000..1f92fd9b
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/group/AttendanceGroupController.java
@@ -0,0 +1,95 @@
+package cn.iocoder.yudao.module.system.controller.admin.group;
+
+import org.springframework.web.bind.annotation.*;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.security.access.prepost.PreAuthorize;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+
+import javax.validation.constraints.*;
+import javax.validation.*;
+import javax.servlet.http.*;
+import java.util.*;
+import java.io.IOException;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
+import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
+
+import cn.iocoder.yudao.module.system.controller.admin.group.vo.*;
+import cn.iocoder.yudao.module.system.dal.dataobject.group.AttendanceGroupDO;
+import cn.iocoder.yudao.module.system.service.group.AttendanceGroupService;
+
+@Tag(name = "管理后台 - 考勤组")
+@RestController
+@RequestMapping("/system/attendance/group")
+@Validated
+public class AttendanceGroupController {
+
+ @Resource
+ private AttendanceGroupService groupService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建考勤组")
+ @PreAuthorize("@ss.hasPermission('attendance:group:create')")
+ public CommonResult createGroup(@Valid @RequestBody AttendanceGroupSaveReqVO createReqVO) {
+ return success(groupService.createGroup(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新考勤组")
+ @PreAuthorize("@ss.hasPermission('attendance:group:update')")
+ public CommonResult updateGroup(@Valid @RequestBody AttendanceGroupSaveReqVO updateReqVO) {
+ groupService.updateGroup(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除考勤组")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('attendance:group:delete')")
+ public CommonResult deleteGroup(@RequestParam("id") Long id) {
+ groupService.deleteGroup(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得考勤组")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('attendance:group:query')")
+ public CommonResult getGroup(@RequestParam("id") Long id) {
+ AttendanceGroupDO group = groupService.getGroup(id);
+ return success(BeanUtils.toBean(group, AttendanceGroupRespVO.class));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得考勤组分页")
+ @PreAuthorize("@ss.hasPermission('attendance:group:query')")
+ public CommonResult> getGroupPage(@Valid AttendanceGroupPageReqVO pageReqVO) {
+ PageResult pageResult = groupService.getGroupPage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, AttendanceGroupRespVO.class));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出考勤组 Excel")
+ @PreAuthorize("@ss.hasPermission('attendance:group:export')")
+ @OperateLog(type = EXPORT)
+ public void exportGroupExcel(@Valid AttendanceGroupPageReqVO pageReqVO,
+ HttpServletResponse response) throws IOException {
+ pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ List list = groupService.getGroupPage(pageReqVO).getList();
+ // 导出 Excel
+ ExcelUtils.write(response, "考勤组.xls", "数据", AttendanceGroupRespVO.class,
+ BeanUtils.toBean(list, AttendanceGroupRespVO.class));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/group/vo/AttendanceGroupPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/group/vo/AttendanceGroupPageReqVO.java
new file mode 100644
index 00000000..5e59f3be
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/group/vo/AttendanceGroupPageReqVO.java
@@ -0,0 +1,49 @@
+package cn.iocoder.yudao.module.system.controller.admin.group.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 考勤组分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class AttendanceGroupPageReqVO extends PageParam {
+
+ @Schema(description = "考勤组管理员id", example = "15633")
+ private Long userId;
+
+ @Schema(description = "群组名称", example = "李四")
+ private String groupName;
+
+ @Schema(description = "考勤类型 1固定班制 2排班制", example = "1")
+ private Integer type;
+
+ @Schema(description = "打卡类型 1考勤机 2小程序范围打卡", example = "2")
+ private Integer punchType;
+
+ @Schema(description = "经度")
+ private String longitude;
+
+ @Schema(description = "纬度")
+ private String latitude;
+
+ @Schema(description = "范围(米)")
+ private Integer scope;
+
+ @Schema(description = "是否允许外勤打卡 0否 1是")
+ private Integer fieldworkFlag;
+
+ @Schema(description = "节假日自动排休 0否 1是")
+ private Integer autoHolidaysFlag;
+
+ @Schema(description = "创建时间")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime[] createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/group/vo/AttendanceGroupRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/group/vo/AttendanceGroupRespVO.java
new file mode 100644
index 00000000..936cc28c
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/group/vo/AttendanceGroupRespVO.java
@@ -0,0 +1,60 @@
+package cn.iocoder.yudao.module.system.controller.admin.group.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.util.*;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+import com.alibaba.excel.annotation.*;
+
+@Schema(description = "管理后台 - 考勤组 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class AttendanceGroupRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22881")
+ @ExcelProperty("编号")
+ private Long id;
+
+ @Schema(description = "考勤组管理员id", example = "15633")
+ @ExcelProperty("考勤组管理员id")
+ private Long userId;
+
+ @Schema(description = "群组名称", example = "李四")
+ @ExcelProperty("群组名称")
+ private String groupName;
+
+ @Schema(description = "考勤类型 1固定班制 2排班制", example = "1")
+ @ExcelProperty("考勤类型 1固定班制 2排班制")
+ private Integer type;
+
+ @Schema(description = "打卡类型 1考勤机 2小程序范围打卡", example = "2")
+ @ExcelProperty("打卡类型 1考勤机 2小程序范围打卡")
+ private Integer punchType;
+
+ @Schema(description = "经度")
+ @ExcelProperty("经度")
+ private String longitude;
+
+ @Schema(description = "纬度")
+ @ExcelProperty("纬度")
+ private String latitude;
+
+ @Schema(description = "范围(米)")
+ @ExcelProperty("范围(米)")
+ private Integer scope;
+
+ @Schema(description = "是否允许外勤打卡 0否 1是")
+ @ExcelProperty("是否允许外勤打卡 0否 1是")
+ private Integer fieldworkFlag;
+
+ @Schema(description = "节假日自动排休 0否 1是")
+ @ExcelProperty("节假日自动排休 0否 1是")
+ private Integer autoHolidaysFlag;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("创建时间")
+ private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/group/vo/AttendanceGroupSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/group/vo/AttendanceGroupSaveReqVO.java
new file mode 100644
index 00000000..ac217805
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/group/vo/AttendanceGroupSaveReqVO.java
@@ -0,0 +1,44 @@
+package cn.iocoder.yudao.module.system.controller.admin.group.vo;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+import java.util.*;
+
+@Schema(description = "管理后台 - 考勤组新增/修改 Request VO")
+@Data
+public class AttendanceGroupSaveReqVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22881")
+ private Long id;
+
+ @Schema(description = "考勤组管理员id", example = "15633")
+ private Long userId;
+
+ @Schema(description = "群组名称", example = "李四")
+ private String groupName;
+
+ @Schema(description = "考勤类型 1固定班制 2排班制", example = "1")
+ private Integer type;
+
+ @Schema(description = "打卡类型 1考勤机 2小程序范围打卡", example = "2")
+ private Integer punchType;
+
+ @Schema(description = "经度")
+ private String longitude;
+
+ @Schema(description = "纬度")
+ private String latitude;
+
+ @Schema(description = "范围(米)")
+ private Integer scope;
+
+ @Schema(description = "是否允许外勤打卡 0否 1是")
+ private Integer fieldworkFlag;
+
+ @Schema(description = "节假日自动排休 0否 1是")
+ private Integer autoHolidaysFlag;
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/groupshift/AttendanceGroupShiftController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/groupshift/AttendanceGroupShiftController.java
new file mode 100644
index 00000000..15337204
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/groupshift/AttendanceGroupShiftController.java
@@ -0,0 +1,95 @@
+package cn.iocoder.yudao.module.system.controller.admin.groupshift;
+
+import org.springframework.web.bind.annotation.*;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.security.access.prepost.PreAuthorize;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+
+import javax.validation.constraints.*;
+import javax.validation.*;
+import javax.servlet.http.*;
+import java.util.*;
+import java.io.IOException;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
+import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
+
+import cn.iocoder.yudao.module.system.controller.admin.groupshift.vo.*;
+import cn.iocoder.yudao.module.system.dal.dataobject.groupshift.AttendanceGroupShiftDO;
+import cn.iocoder.yudao.module.system.service.groupshift.AttendanceGroupShiftService;
+
+@Tag(name = "管理后台 - 考勤组班次")
+@RestController
+@RequestMapping("/system/attendance/group-shift")
+@Validated
+public class AttendanceGroupShiftController {
+
+ @Resource
+ private AttendanceGroupShiftService groupShiftService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建考勤组班次")
+ @PreAuthorize("@ss.hasPermission('attendance:group-shift:create')")
+ public CommonResult createGroupShift(@Valid @RequestBody AttendanceGroupShiftSaveReqVO createReqVO) {
+ return success(groupShiftService.createGroupShift(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新考勤组班次")
+ @PreAuthorize("@ss.hasPermission('attendance:group-shift:update')")
+ public CommonResult updateGroupShift(@Valid @RequestBody AttendanceGroupShiftSaveReqVO updateReqVO) {
+ groupShiftService.updateGroupShift(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除考勤组班次")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('attendance:group-shift:delete')")
+ public CommonResult deleteGroupShift(@RequestParam("id") Long id) {
+ groupShiftService.deleteGroupShift(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得考勤组班次")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('attendance:group-shift:query')")
+ public CommonResult getGroupShift(@RequestParam("id") Long id) {
+ AttendanceGroupShiftDO groupShift = groupShiftService.getGroupShift(id);
+ return success(BeanUtils.toBean(groupShift, AttendanceGroupShiftRespVO.class));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得考勤组班次分页")
+ @PreAuthorize("@ss.hasPermission('attendance:group-shift:query')")
+ public CommonResult> getGroupShiftPage(@Valid AttendanceGroupShiftPageReqVO pageReqVO) {
+ PageResult pageResult = groupShiftService.getGroupShiftPage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, AttendanceGroupShiftRespVO.class));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出考勤组班次 Excel")
+ @PreAuthorize("@ss.hasPermission('attendance:group-shift:export')")
+ @OperateLog(type = EXPORT)
+ public void exportGroupShiftExcel(@Valid AttendanceGroupShiftPageReqVO pageReqVO,
+ HttpServletResponse response) throws IOException {
+ pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ List list = groupShiftService.getGroupShiftPage(pageReqVO).getList();
+ // 导出 Excel
+ ExcelUtils.write(response, "考勤组班次.xls", "数据", AttendanceGroupShiftRespVO.class,
+ BeanUtils.toBean(list, AttendanceGroupShiftRespVO.class));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/groupshift/vo/AttendanceGroupShiftPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/groupshift/vo/AttendanceGroupShiftPageReqVO.java
new file mode 100644
index 00000000..d468a872
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/groupshift/vo/AttendanceGroupShiftPageReqVO.java
@@ -0,0 +1,31 @@
+package cn.iocoder.yudao.module.system.controller.admin.groupshift.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 考勤组班次分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class AttendanceGroupShiftPageReqVO extends PageParam {
+
+ @Schema(description = "考勤组id", example = "24585")
+ private Long attendanceGroupId;
+
+ @Schema(description = "班次名称", example = "张三")
+ private String name;
+
+ @Schema(description = "班次考勤时间json[{start_tiem:HH:mm,start_check_flag:true,end_time:HH:mm,end_check_flah: true}]")
+ private String attendanceTimeJson;
+
+ @Schema(description = "创建时间")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime[] createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/groupshift/vo/AttendanceGroupShiftRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/groupshift/vo/AttendanceGroupShiftRespVO.java
new file mode 100644
index 00000000..56e65e08
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/groupshift/vo/AttendanceGroupShiftRespVO.java
@@ -0,0 +1,36 @@
+package cn.iocoder.yudao.module.system.controller.admin.groupshift.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.util.*;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+import com.alibaba.excel.annotation.*;
+
+@Schema(description = "管理后台 - 考勤组班次 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class AttendanceGroupShiftRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "5305")
+ @ExcelProperty("编号")
+ private Long id;
+
+ @Schema(description = "考勤组id", example = "24585")
+ @ExcelProperty("考勤组id")
+ private Long attendanceGroupId;
+
+ @Schema(description = "班次名称", example = "张三")
+ @ExcelProperty("班次名称")
+ private String name;
+
+ @Schema(description = "班次考勤时间json[{start_tiem:HH:mm,start_check_flag:true,end_time:HH:mm,end_check_flah: true}]")
+ @ExcelProperty("班次考勤时间json[{start_tiem:HH:mm,start_check_flag:true,end_time:HH:mm,end_check_flah: true}]")
+ private String attendanceTimeJson;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("创建时间")
+ private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/groupshift/vo/AttendanceGroupShiftSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/groupshift/vo/AttendanceGroupShiftSaveReqVO.java
new file mode 100644
index 00000000..3f6b4473
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/groupshift/vo/AttendanceGroupShiftSaveReqVO.java
@@ -0,0 +1,25 @@
+package cn.iocoder.yudao.module.system.controller.admin.groupshift.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+import java.util.*;
+
+@Schema(description = "管理后台 - 考勤组班次新增/修改 Request VO")
+@Data
+public class AttendanceGroupShiftSaveReqVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "5305")
+ private Long id;
+
+ @Schema(description = "考勤组id", example = "24585")
+ private Long attendanceGroupId;
+
+ @Schema(description = "班次名称", example = "张三")
+ private String name;
+
+ @Schema(description = "班次考勤时间json[{start_tiem:HH:mm,start_check_flag:true,end_time:HH:mm,end_check_flah: true}]")
+ private String attendanceTimeJson;
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/groupuser/AttendanceGroupUserController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/groupuser/AttendanceGroupUserController.java
new file mode 100644
index 00000000..03a10111
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/groupuser/AttendanceGroupUserController.java
@@ -0,0 +1,95 @@
+package cn.iocoder.yudao.module.system.controller.admin.groupuser;
+
+import org.springframework.web.bind.annotation.*;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.security.access.prepost.PreAuthorize;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+
+import javax.validation.constraints.*;
+import javax.validation.*;
+import javax.servlet.http.*;
+import java.util.*;
+import java.io.IOException;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
+import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
+
+import cn.iocoder.yudao.module.system.controller.admin.groupuser.vo.*;
+import cn.iocoder.yudao.module.system.dal.dataobject.groupuser.AttendanceGroupUserDO;
+import cn.iocoder.yudao.module.system.service.groupuser.AttendanceGroupUserService;
+
+@Tag(name = "管理后台 - 考勤组人员")
+@RestController
+@RequestMapping("/system/attendance/group-user")
+@Validated
+public class AttendanceGroupUserController {
+
+ @Resource
+ private AttendanceGroupUserService groupUserService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建考勤组人员")
+ @PreAuthorize("@ss.hasPermission('attendance:group-user:create')")
+ public CommonResult createGroupUser(@Valid @RequestBody AttendanceGroupUserSaveReqVO createReqVO) {
+ return success(groupUserService.createGroupUser(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新考勤组人员")
+ @PreAuthorize("@ss.hasPermission('attendance:group-user:update')")
+ public CommonResult updateGroupUser(@Valid @RequestBody AttendanceGroupUserSaveReqVO updateReqVO) {
+ groupUserService.updateGroupUser(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除考勤组人员")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('attendance:group-user:delete')")
+ public CommonResult deleteGroupUser(@RequestParam("id") Long id) {
+ groupUserService.deleteGroupUser(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得考勤组人员")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('attendance:group-user:query')")
+ public CommonResult getGroupUser(@RequestParam("id") Long id) {
+ AttendanceGroupUserDO groupUser = groupUserService.getGroupUser(id);
+ return success(BeanUtils.toBean(groupUser, AttendanceGroupUserRespVO.class));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得考勤组人员分页")
+ @PreAuthorize("@ss.hasPermission('attendance:group-user:query')")
+ public CommonResult> getGroupUserPage(@Valid AttendanceGroupUserPageReqVO pageReqVO) {
+ PageResult pageResult = groupUserService.getGroupUserPage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, AttendanceGroupUserRespVO.class));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出考勤组人员 Excel")
+ @PreAuthorize("@ss.hasPermission('attendance:group-user:export')")
+ @OperateLog(type = EXPORT)
+ public void exportGroupUserExcel(@Valid AttendanceGroupUserPageReqVO pageReqVO,
+ HttpServletResponse response) throws IOException {
+ pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ List list = groupUserService.getGroupUserPage(pageReqVO).getList();
+ // 导出 Excel
+ ExcelUtils.write(response, "考勤组人员.xls", "数据", AttendanceGroupUserRespVO.class,
+ BeanUtils.toBean(list, AttendanceGroupUserRespVO.class));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/groupuser/vo/AttendanceGroupUserPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/groupuser/vo/AttendanceGroupUserPageReqVO.java
new file mode 100644
index 00000000..ec64ead1
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/groupuser/vo/AttendanceGroupUserPageReqVO.java
@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.system.controller.admin.groupuser.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 考勤组人员分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class AttendanceGroupUserPageReqVO extends PageParam {
+
+ @Schema(description = "考勤组id", example = "21406")
+ private Long attendanceGroupId;
+
+ @Schema(description = "用户id", example = "22547")
+ private Long userId;
+
+ @Schema(description = "创建时间")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime[] createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/groupuser/vo/AttendanceGroupUserRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/groupuser/vo/AttendanceGroupUserRespVO.java
new file mode 100644
index 00000000..e53abc1b
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/groupuser/vo/AttendanceGroupUserRespVO.java
@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.module.system.controller.admin.groupuser.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.util.*;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+import com.alibaba.excel.annotation.*;
+
+@Schema(description = "管理后台 - 考勤组人员 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class AttendanceGroupUserRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31257")
+ @ExcelProperty("编号")
+ private Long id;
+
+ @Schema(description = "考勤组id", example = "21406")
+ @ExcelProperty("考勤组id")
+ private Long attendanceGroupId;
+
+ @Schema(description = "用户id", example = "22547")
+ @ExcelProperty("用户id")
+ private Long userId;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("创建时间")
+ private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/groupuser/vo/AttendanceGroupUserSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/groupuser/vo/AttendanceGroupUserSaveReqVO.java
new file mode 100644
index 00000000..df0d5031
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/groupuser/vo/AttendanceGroupUserSaveReqVO.java
@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.module.system.controller.admin.groupuser.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+import java.util.*;
+
+@Schema(description = "管理后台 - 考勤组人员新增/修改 Request VO")
+@Data
+public class AttendanceGroupUserSaveReqVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31257")
+ private Long id;
+
+ @Schema(description = "考勤组id", example = "21406")
+ private Long attendanceGroupId;
+
+ @Schema(description = "用户id", example = "22547")
+ private Long userId;
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/punchrecord/AttendancePunchRecordController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/punchrecord/AttendancePunchRecordController.java
new file mode 100644
index 00000000..c55fdd50
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/punchrecord/AttendancePunchRecordController.java
@@ -0,0 +1,95 @@
+package cn.iocoder.yudao.module.system.controller.admin.punchrecord;
+
+import org.springframework.web.bind.annotation.*;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.security.access.prepost.PreAuthorize;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+
+import javax.validation.constraints.*;
+import javax.validation.*;
+import javax.servlet.http.*;
+import java.util.*;
+import java.io.IOException;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
+import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
+
+import cn.iocoder.yudao.module.system.controller.admin.punchrecord.vo.*;
+import cn.iocoder.yudao.module.system.dal.dataobject.punchrecord.AttendancePunchRecordDO;
+import cn.iocoder.yudao.module.system.service.punchrecord.AttendancePunchRecordService;
+
+@Tag(name = "管理后台 - 用户打卡记录")
+@RestController
+@RequestMapping("/system/attendance/punch-record")
+@Validated
+public class AttendancePunchRecordController {
+
+ @Resource
+ private AttendancePunchRecordService punchRecordService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建用户打卡记录")
+ @PreAuthorize("@ss.hasPermission('attendance:punch-record:create')")
+ public CommonResult createPunchRecord(@Valid @RequestBody AttendancePunchRecordSaveReqVO createReqVO) {
+ return success(punchRecordService.createPunchRecord(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新用户打卡记录")
+ @PreAuthorize("@ss.hasPermission('attendance:punch-record:update')")
+ public CommonResult updatePunchRecord(@Valid @RequestBody AttendancePunchRecordSaveReqVO updateReqVO) {
+ punchRecordService.updatePunchRecord(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除用户打卡记录")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('attendance:punch-record:delete')")
+ public CommonResult deletePunchRecord(@RequestParam("id") Long id) {
+ punchRecordService.deletePunchRecord(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得用户打卡记录")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('attendance:punch-record:query')")
+ public CommonResult getPunchRecord(@RequestParam("id") Long id) {
+ AttendancePunchRecordDO punchRecord = punchRecordService.getPunchRecord(id);
+ return success(BeanUtils.toBean(punchRecord, AttendancePunchRecordRespVO.class));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得用户打卡记录分页")
+ @PreAuthorize("@ss.hasPermission('attendance:punch-record:query')")
+ public CommonResult> getPunchRecordPage(@Valid AttendancePunchRecordPageReqVO pageReqVO) {
+ PageResult pageResult = punchRecordService.getPunchRecordPage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, AttendancePunchRecordRespVO.class));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出用户打卡记录 Excel")
+ @PreAuthorize("@ss.hasPermission('attendance:punch-record:export')")
+ @OperateLog(type = EXPORT)
+ public void exportPunchRecordExcel(@Valid AttendancePunchRecordPageReqVO pageReqVO,
+ HttpServletResponse response) throws IOException {
+ pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ List list = punchRecordService.getPunchRecordPage(pageReqVO).getList();
+ // 导出 Excel
+ ExcelUtils.write(response, "用户打卡记录.xls", "数据", AttendancePunchRecordRespVO.class,
+ BeanUtils.toBean(list, AttendancePunchRecordRespVO.class));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/punchrecord/vo/AttendancePunchRecordPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/punchrecord/vo/AttendancePunchRecordPageReqVO.java
new file mode 100644
index 00000000..bdd7c654
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/punchrecord/vo/AttendancePunchRecordPageReqVO.java
@@ -0,0 +1,57 @@
+package cn.iocoder.yudao.module.system.controller.admin.punchrecord.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 用户打卡记录分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class AttendancePunchRecordPageReqVO extends PageParam {
+
+ @Schema(description = "考勤组管理员id", example = "11409")
+ private Long userId;
+
+ @Schema(description = "考勤组id", example = "22293")
+ private Long attendanceGroupId;
+
+ @Schema(description = "班次id", example = "10780")
+ private Long attendanceGroupShiftId;
+
+ @Schema(description = "考勤类型 1固定班制 2排班制", example = "2")
+ private Integer type;
+
+ @Schema(description = "打卡类型 1考勤机 2小程序范围打卡", example = "2")
+ private Integer punchType;
+
+ @Schema(description = "班次考勤时间json[{start_tiem:HH:mm,start_check_flag:true,end_time:HH:mm,end_check_flah: true}]")
+ private String attendanceTimeJson;
+
+ @Schema(description = "打卡状态 0正常 1迟到 2缺卡 3外勤 4补卡", example = "1")
+ private Integer status;
+
+ @Schema(description = "日期yyyy-MM-dd格式")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private String[] dayTime;
+
+ @Schema(description = "打卡时间")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime[] punchTime;
+
+ @Schema(description = "打卡备注", example = "你说的对")
+ private String remark;
+
+ @Schema(description = "图片")
+ private String image;
+
+ @Schema(description = "创建时间")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime[] createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/punchrecord/vo/AttendancePunchRecordRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/punchrecord/vo/AttendancePunchRecordRespVO.java
new file mode 100644
index 00000000..15067d80
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/punchrecord/vo/AttendancePunchRecordRespVO.java
@@ -0,0 +1,68 @@
+package cn.iocoder.yudao.module.system.controller.admin.punchrecord.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.util.*;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+import com.alibaba.excel.annotation.*;
+
+@Schema(description = "管理后台 - 用户打卡记录 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class AttendancePunchRecordRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "65")
+ @ExcelProperty("编号")
+ private Long id;
+
+ @Schema(description = "考勤组管理员id", example = "11409")
+ @ExcelProperty("考勤组管理员id")
+ private Long userId;
+
+ @Schema(description = "考勤组id", example = "22293")
+ @ExcelProperty("考勤组id")
+ private Long attendanceGroupId;
+
+ @Schema(description = "班次id", example = "10780")
+ @ExcelProperty("班次id")
+ private Long attendanceGroupShiftId;
+
+ @Schema(description = "考勤类型 1固定班制 2排班制", example = "2")
+ @ExcelProperty("考勤类型 1固定班制 2排班制")
+ private Integer type;
+
+ @Schema(description = "打卡类型 1考勤机 2小程序范围打卡", example = "2")
+ @ExcelProperty("打卡类型 1考勤机 2小程序范围打卡")
+ private Integer punchType;
+
+ @Schema(description = "班次考勤时间json[{start_tiem:HH:mm,start_check_flag:true,end_time:HH:mm,end_check_flah: true}]")
+ @ExcelProperty("班次考勤时间json[{start_tiem:HH:mm,start_check_flag:true,end_time:HH:mm,end_check_flah: true}]")
+ private String attendanceTimeJson;
+
+ @Schema(description = "打卡状态 0正常 1迟到 2缺卡 3外勤 4补卡", example = "1")
+ @ExcelProperty("打卡状态 0正常 1迟到 2缺卡 3外勤 4补卡")
+ private Integer status;
+
+ @Schema(description = "日期yyyy-MM-dd格式")
+ @ExcelProperty("日期yyyy-MM-dd格式")
+ private String dayTime;
+
+ @Schema(description = "打卡时间")
+ @ExcelProperty("打卡时间")
+ private LocalDateTime punchTime;
+
+ @Schema(description = "打卡备注", example = "你说的对")
+ @ExcelProperty("打卡备注")
+ private String remark;
+
+ @Schema(description = "图片")
+ @ExcelProperty("图片")
+ private String image;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("创建时间")
+ private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/punchrecord/vo/AttendancePunchRecordSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/punchrecord/vo/AttendancePunchRecordSaveReqVO.java
new file mode 100644
index 00000000..397e7019
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/punchrecord/vo/AttendancePunchRecordSaveReqVO.java
@@ -0,0 +1,51 @@
+package cn.iocoder.yudao.module.system.controller.admin.punchrecord.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+import java.util.*;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 用户打卡记录新增/修改 Request VO")
+@Data
+public class AttendancePunchRecordSaveReqVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "65")
+ private Long id;
+
+ @Schema(description = "考勤组管理员id", example = "11409")
+ private Long userId;
+
+ @Schema(description = "考勤组id", example = "22293")
+ private Long attendanceGroupId;
+
+ @Schema(description = "班次id", example = "10780")
+ private Long attendanceGroupShiftId;
+
+ @Schema(description = "考勤类型 1固定班制 2排班制", example = "2")
+ private Integer type;
+
+ @Schema(description = "打卡类型 1考勤机 2小程序范围打卡", example = "2")
+ private Integer punchType;
+
+ @Schema(description = "班次考勤时间json[{start_tiem:HH:mm,start_check_flag:true,end_time:HH:mm,end_check_flah: true}]")
+ private String attendanceTimeJson;
+
+ @Schema(description = "打卡状态 0正常 1迟到 2缺卡 3外勤 4补卡", example = "1")
+ private Integer status;
+
+ @Schema(description = "日期yyyy-MM-dd格式")
+ private String dayTime;
+
+ @Schema(description = "打卡时间")
+ private LocalDateTime punchTime;
+
+ @Schema(description = "打卡备注", example = "你说的对")
+ private String remark;
+
+ @Schema(description = "图片")
+ private String image;
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/scheduling/AttendanceSchedulingController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/scheduling/AttendanceSchedulingController.java
new file mode 100644
index 00000000..efa200b5
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/scheduling/AttendanceSchedulingController.java
@@ -0,0 +1,95 @@
+package cn.iocoder.yudao.module.system.controller.admin.scheduling;
+
+import org.springframework.web.bind.annotation.*;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.security.access.prepost.PreAuthorize;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+
+import javax.validation.constraints.*;
+import javax.validation.*;
+import javax.servlet.http.*;
+import java.util.*;
+import java.io.IOException;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
+import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
+
+import cn.iocoder.yudao.module.system.controller.admin.scheduling.vo.*;
+import cn.iocoder.yudao.module.system.dal.dataobject.scheduling.AttendanceSchedulingDO;
+import cn.iocoder.yudao.module.system.service.scheduling.AttendanceSchedulingService;
+
+@Tag(name = "管理后台 - 排班制考勤设置")
+@RestController
+@RequestMapping("/system/attendance/scheduling")
+@Validated
+public class AttendanceSchedulingController {
+
+ @Resource
+ private AttendanceSchedulingService schedulingService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建排班制考勤设置")
+ @PreAuthorize("@ss.hasPermission('attendance:scheduling:create')")
+ public CommonResult createScheduling(@Valid @RequestBody AttendanceSchedulingSaveReqVO createReqVO) {
+ return success(schedulingService.createScheduling(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新排班制考勤设置")
+ @PreAuthorize("@ss.hasPermission('attendance:scheduling:update')")
+ public CommonResult updateScheduling(@Valid @RequestBody AttendanceSchedulingSaveReqVO updateReqVO) {
+ schedulingService.updateScheduling(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除排班制考勤设置")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('attendance:scheduling:delete')")
+ public CommonResult deleteScheduling(@RequestParam("id") Long id) {
+ schedulingService.deleteScheduling(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得排班制考勤设置")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('attendance:scheduling:query')")
+ public CommonResult getScheduling(@RequestParam("id") Long id) {
+ AttendanceSchedulingDO scheduling = schedulingService.getScheduling(id);
+ return success(BeanUtils.toBean(scheduling, AttendanceSchedulingRespVO.class));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得排班制考勤设置分页")
+ @PreAuthorize("@ss.hasPermission('attendance:scheduling:query')")
+ public CommonResult> getSchedulingPage(@Valid AttendanceSchedulingPageReqVO pageReqVO) {
+ PageResult pageResult = schedulingService.getSchedulingPage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, AttendanceSchedulingRespVO.class));
+ }
+
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出排班制考勤设置 Excel")
+ @PreAuthorize("@ss.hasPermission('attendance:scheduling:export')")
+ @OperateLog(type = EXPORT)
+ public void exportSchedulingExcel(@Valid AttendanceSchedulingPageReqVO pageReqVO,
+ HttpServletResponse response) throws IOException {
+ pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ List list = schedulingService.getSchedulingPage(pageReqVO).getList();
+ // 导出 Excel
+ ExcelUtils.write(response, "排班制考勤设置.xls", "数据", AttendanceSchedulingRespVO.class,
+ BeanUtils.toBean(list, AttendanceSchedulingRespVO.class));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/scheduling/vo/AttendanceSchedulingPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/scheduling/vo/AttendanceSchedulingPageReqVO.java
new file mode 100644
index 00000000..a8227026
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/scheduling/vo/AttendanceSchedulingPageReqVO.java
@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.system.controller.admin.scheduling.vo;
+
+import lombok.*;
+import java.util.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 排班制考勤设置分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class AttendanceSchedulingPageReqVO extends PageParam {
+
+ @Schema(description = "第几天")
+ private Integer indexDay;
+
+ @Schema(description = "考勤组id", example = "5025")
+ private Long attendanceGroupId;
+
+ @Schema(description = "班次id", example = "9583")
+ private Long attendanceGroupShiftId;
+
+ @Schema(description = "是否休息 0否 1是")
+ private Integer restFlag;
+
+ @Schema(description = "创建时间")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime[] createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/scheduling/vo/AttendanceSchedulingRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/scheduling/vo/AttendanceSchedulingRespVO.java
new file mode 100644
index 00000000..56089990
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/scheduling/vo/AttendanceSchedulingRespVO.java
@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.module.system.controller.admin.scheduling.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import java.util.*;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+import com.alibaba.excel.annotation.*;
+
+@Schema(description = "管理后台 - 排班制考勤设置 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class AttendanceSchedulingRespVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23862")
+ @ExcelProperty("编号")
+ private Long id;
+
+ @Schema(description = "第几天")
+ @ExcelProperty("第几天")
+ private Integer indexDay;
+
+ @Schema(description = "考勤组id", example = "5025")
+ @ExcelProperty("考勤组id")
+ private Long attendanceGroupId;
+
+ @Schema(description = "班次id", example = "9583")
+ @ExcelProperty("班次id")
+ private Long attendanceGroupShiftId;
+
+ @Schema(description = "是否休息 0否 1是")
+ @ExcelProperty("是否休息 0否 1是")
+ private Integer restFlag;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("创建时间")
+ private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/scheduling/vo/AttendanceSchedulingSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/scheduling/vo/AttendanceSchedulingSaveReqVO.java
new file mode 100644
index 00000000..e6dbfd01
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/scheduling/vo/AttendanceSchedulingSaveReqVO.java
@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.system.controller.admin.scheduling.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+import java.util.*;
+import javax.validation.constraints.*;
+import java.util.*;
+
+@Schema(description = "管理后台 - 排班制考勤设置新增/修改 Request VO")
+@Data
+public class AttendanceSchedulingSaveReqVO {
+
+ @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23862")
+ private Long id;
+
+ @Schema(description = "第几天")
+ private Integer indexDay;
+
+ @Schema(description = "考勤组id", example = "5025")
+ private Long attendanceGroupId;
+
+ @Schema(description = "班次id", example = "9583")
+ private Long attendanceGroupShiftId;
+
+ @Schema(description = "是否休息 0否 1是")
+ private Integer restFlag;
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/attendance/AttendanceController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/attendance/AttendanceController.java
new file mode 100644
index 00000000..fe41559f
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/attendance/AttendanceController.java
@@ -0,0 +1,41 @@
+package cn.iocoder.yudao.module.system.controller.app.attendance;
+
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
+import cn.iocoder.yudao.module.system.controller.app.attendance.dto.AttendancePunchPageDTO;
+import cn.iocoder.yudao.module.system.controller.app.attendance.vo.AttendancePunchPageVO;
+import cn.iocoder.yudao.module.system.controller.app.dict.vo.AppDictDataRespVO;
+import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO;
+import cn.iocoder.yudao.module.system.handler.PunchHandler;
+import cn.iocoder.yudao.module.system.service.attendance.AttendanceService;
+import cn.iocoder.yudao.module.system.service.dict.DictDataService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "考勤 App - 考勤管理")
+@RestController
+@RequestMapping("/system/attendance")
+@Validated
+public class AttendanceController {
+
+ @Resource
+ private AttendanceService attendanceService;
+
+ @GetMapping("/getPunchPage")
+ @Operation(summary = "获取考勤页面")
+ public CommonResult getPunchPage(@ModelAttribute AttendancePunchPageDTO dto) {
+ Long userId = WebFrameworkUtils.getLoginUserId();
+ AttendancePunchPageVO vo = attendanceService.getPunchPage(dto.setUserId(userId));
+ return success(vo);
+ }
+
+}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/attendance/dto/AttendancePunchPageDTO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/attendance/dto/AttendancePunchPageDTO.java
new file mode 100644
index 00000000..b78bb757
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/attendance/dto/AttendancePunchPageDTO.java
@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.module.system.controller.app.attendance.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+@Data
+@Accessors(chain = true)
+public class AttendancePunchPageDTO {
+
+ @Schema(description = "经度")
+ private String longitude;
+
+ @Schema(description = "纬度")
+ private String latitude;
+
+ @Schema(description = "当前用户id", hidden = true)
+ private Long userId;
+
+}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/attendance/vo/AttendancePunchPageVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/attendance/vo/AttendancePunchPageVO.java
new file mode 100644
index 00000000..2eff07c0
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/attendance/vo/AttendancePunchPageVO.java
@@ -0,0 +1,9 @@
+package cn.iocoder.yudao.module.system.controller.app.attendance.vo;
+
+import lombok.Data;
+
+@Data
+public class AttendancePunchPageVO {
+
+
+}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/fixed/AttendanceFixedDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/fixed/AttendanceFixedDO.java
new file mode 100644
index 00000000..da0900b2
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/fixed/AttendanceFixedDO.java
@@ -0,0 +1,43 @@
+package cn.iocoder.yudao.module.system.dal.dataobject.fixed;
+
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.*;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 固定班制考勤设置 DO
+ *
+ * @author 艾楷
+ */
+@TableName("kq_attendance_fixed")
+@KeySequence("kq_attendance_fixed_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class AttendanceFixedDO extends BaseDO {
+
+ /**
+ * 编号
+ */
+ @TableId
+ private Long id;
+ /**
+ * 考勤组id
+ */
+ private Long attendanceGroupId;
+ /**
+ * 周时间 1-7
+ */
+ private Integer weekTime;
+ /**
+ * 班次id
+ */
+ private Long attendanceGroupShiftId;
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/group/AttendanceGroupDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/group/AttendanceGroupDO.java
new file mode 100644
index 00000000..1c1ee3fa
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/group/AttendanceGroupDO.java
@@ -0,0 +1,75 @@
+package cn.iocoder.yudao.module.system.dal.dataobject.group;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+/**
+ * 考勤组 DO
+ *
+ * @author 艾楷
+ */
+@TableName("kq_attendance_group")
+@KeySequence("kq_attendance_group_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class AttendanceGroupDO extends BaseDO {
+
+ /**
+ * 编号
+ */
+ @TableId
+ private Long id;
+ /**
+ * 考勤组管理员id
+ */
+ private Long userId;
+ /**
+ * 群组名称
+ */
+ private String groupName;
+ /**
+ * 考勤类型 1固定班制 2排班制
+ */
+ private Integer type;
+ /**
+ * 打卡类型 1考勤机 2小程序范围打卡
+ */
+ private Integer punchType;
+ /**
+ * 经度
+ */
+ private String longitude;
+ /**
+ * 纬度
+ */
+ private String latitude;
+ /**
+ * 范围(米)
+ */
+ private Integer scope;
+ /**
+ * 是否允许外勤打卡 0否 1是
+ */
+ private Integer fieldworkFlag;
+ /**
+ * 节假日自动排休 0否 1是
+ */
+ private Integer autoHolidaysFlag;
+
+
+ public static String getCodeByType(Integer type) {
+ if (type == 1) {
+ return "fixed";
+ } else {
+ return "scheduling";
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/groupshift/AttendanceGroupShiftDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/groupshift/AttendanceGroupShiftDO.java
new file mode 100644
index 00000000..abb2cd66
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/groupshift/AttendanceGroupShiftDO.java
@@ -0,0 +1,43 @@
+package cn.iocoder.yudao.module.system.dal.dataobject.groupshift;
+
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.*;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 考勤组班次 DO
+ *
+ * @author 艾楷
+ */
+@TableName("kq_attendance_group_shift")
+@KeySequence("kq_attendance_group_shift_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class AttendanceGroupShiftDO extends BaseDO {
+
+ /**
+ * 编号
+ */
+ @TableId
+ private Long id;
+ /**
+ * 考勤组id
+ */
+ private Long attendanceGroupId;
+ /**
+ * 班次名称
+ */
+ private String name;
+ /**
+ * 班次考勤时间json[{start_tiem:HH:mm,start_check_flag:true,end_time:HH:mm,end_check_flah: true}]
+ */
+ private String attendanceTimeJson;
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/groupuser/AttendanceGroupUserDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/groupuser/AttendanceGroupUserDO.java
new file mode 100644
index 00000000..98eb30db
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/groupuser/AttendanceGroupUserDO.java
@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.system.dal.dataobject.groupuser;
+
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.*;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 考勤组人员 DO
+ *
+ * @author 艾楷
+ */
+@TableName("kq_attendance_group_user")
+@KeySequence("kq_attendance_group_user_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class AttendanceGroupUserDO extends BaseDO {
+
+ /**
+ * 编号
+ */
+ @TableId
+ private Long id;
+ /**
+ * 考勤组id
+ */
+ private Long attendanceGroupId;
+ /**
+ * 用户id
+ */
+ private Long userId;
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/punchrecord/AttendancePunchRecordDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/punchrecord/AttendancePunchRecordDO.java
new file mode 100644
index 00000000..e7fd91c1
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/punchrecord/AttendancePunchRecordDO.java
@@ -0,0 +1,76 @@
+package cn.iocoder.yudao.module.system.dal.dataobject.punchrecord;
+
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.*;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 用户打卡记录 DO
+ *
+ * @author 艾楷
+ */
+@TableName("kq_attendance_punch_record")
+@KeySequence("kq_attendance_punch_record_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class AttendancePunchRecordDO extends BaseDO {
+
+ /**
+ * 编号
+ */
+ @TableId
+ private Long id;
+ /**
+ * 考勤组管理员id
+ */
+ private Long userId;
+ /**
+ * 考勤组id
+ */
+ private Long attendanceGroupId;
+ /**
+ * 班次id
+ */
+ private Long attendanceGroupShiftId;
+ /**
+ * 考勤类型 1固定班制 2排班制
+ */
+ private Integer type;
+ /**
+ * 打卡类型 1考勤机 2小程序范围打卡
+ */
+ private Integer punchType;
+ /**
+ * 班次考勤时间json[{start_tiem:HH:mm,start_check_flag:true,end_time:HH:mm,end_check_flah: true}]
+ */
+ private String attendanceTimeJson;
+ /**
+ * 打卡状态 0正常 1迟到 2缺卡 3外勤 4补卡
+ */
+ private Integer status;
+ /**
+ * 日期yyyy-MM-dd格式
+ */
+ private String dayTime;
+ /**
+ * 打卡时间
+ */
+ private LocalDateTime punchTime;
+ /**
+ * 打卡备注
+ */
+ private String remark;
+ /**
+ * 图片
+ */
+ private String image;
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/scheduling/AttendanceSchedulingDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/scheduling/AttendanceSchedulingDO.java
new file mode 100644
index 00000000..4ea9ab31
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/scheduling/AttendanceSchedulingDO.java
@@ -0,0 +1,47 @@
+package cn.iocoder.yudao.module.system.dal.dataobject.scheduling;
+
+import lombok.*;
+import java.util.*;
+import java.time.LocalDateTime;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.*;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+
+/**
+ * 排班制考勤设置 DO
+ *
+ * @author 艾楷
+ */
+@TableName("kq_attendance_scheduling")
+@KeySequence("kq_attendance_scheduling_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class AttendanceSchedulingDO extends BaseDO {
+
+ /**
+ * 编号
+ */
+ @TableId
+ private Long id;
+ /**
+ * 第几天
+ */
+ private Integer indexDay;
+ /**
+ * 考勤组id
+ */
+ private Long attendanceGroupId;
+ /**
+ * 班次id
+ */
+ private Long attendanceGroupShiftId;
+ /**
+ * 是否休息 0否 1是
+ */
+ private Integer restFlag;
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/fixed/AttendanceFixedMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/fixed/AttendanceFixedMapper.java
new file mode 100644
index 00000000..ac5425df
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/fixed/AttendanceFixedMapper.java
@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.module.system.dal.mysql.fixed;
+
+import java.util.*;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.module.system.dal.dataobject.fixed.AttendanceFixedDO;
+import org.apache.ibatis.annotations.Mapper;
+import cn.iocoder.yudao.module.system.controller.admin.fixed.vo.*;
+
+/**
+ * 固定班制考勤设置 Mapper
+ *
+ * @author 艾楷
+ */
+@Mapper
+public interface AttendanceFixedMapper extends BaseMapperX {
+
+ default PageResult selectPage(AttendanceFixedPageReqVO reqVO) {
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .eqIfPresent(AttendanceFixedDO::getAttendanceGroupId, reqVO.getAttendanceGroupId())
+ .betweenIfPresent(AttendanceFixedDO::getWeekTime, reqVO.getWeekTime())
+ .eqIfPresent(AttendanceFixedDO::getAttendanceGroupShiftId, reqVO.getAttendanceGroupShiftId())
+ .betweenIfPresent(AttendanceFixedDO::getCreateTime, reqVO.getCreateTime())
+ .orderByDesc(AttendanceFixedDO::getId));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/group/AttendanceGroupMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/group/AttendanceGroupMapper.java
new file mode 100644
index 00000000..7bb5f9f0
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/group/AttendanceGroupMapper.java
@@ -0,0 +1,42 @@
+package cn.iocoder.yudao.module.system.dal.mysql.group;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.system.controller.admin.group.vo.AttendanceGroupPageReqVO;
+import cn.iocoder.yudao.module.system.dal.dataobject.group.AttendanceGroupDO;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 考勤组 Mapper
+ *
+ * @author 艾楷
+ */
+@Mapper
+public interface AttendanceGroupMapper extends BaseMapperX {
+
+ default PageResult selectPage(AttendanceGroupPageReqVO reqVO) {
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .eqIfPresent(AttendanceGroupDO::getUserId, reqVO.getUserId())
+ .likeIfPresent(AttendanceGroupDO::getGroupName, reqVO.getGroupName())
+ .eqIfPresent(AttendanceGroupDO::getType, reqVO.getType())
+ .eqIfPresent(AttendanceGroupDO::getPunchType, reqVO.getPunchType())
+ .eqIfPresent(AttendanceGroupDO::getLongitude, reqVO.getLongitude())
+ .eqIfPresent(AttendanceGroupDO::getLatitude, reqVO.getLatitude())
+ .eqIfPresent(AttendanceGroupDO::getScope, reqVO.getScope())
+ .eqIfPresent(AttendanceGroupDO::getAutoHolidaysFlag, reqVO.getAutoHolidaysFlag())
+ .betweenIfPresent(AttendanceGroupDO::getCreateTime, reqVO.getCreateTime())
+ .orderByDesc(AttendanceGroupDO::getId));
+ }
+
+ /**
+ * 通过当前登录用户获取用户所在群组
+ *
+ * @param userId
+ * @return
+ */
+ List getByUserId(@Param("userId") Long userId);
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/groupshift/AttendanceGroupShiftMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/groupshift/AttendanceGroupShiftMapper.java
new file mode 100644
index 00000000..8209d587
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/groupshift/AttendanceGroupShiftMapper.java
@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.module.system.dal.mysql.groupshift;
+
+import java.util.*;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.module.system.dal.dataobject.groupshift.AttendanceGroupShiftDO;
+import org.apache.ibatis.annotations.Mapper;
+import cn.iocoder.yudao.module.system.controller.admin.groupshift.vo.*;
+
+/**
+ * 考勤组班次 Mapper
+ *
+ * @author 艾楷
+ */
+@Mapper
+public interface AttendanceGroupShiftMapper extends BaseMapperX {
+
+ default PageResult selectPage(AttendanceGroupShiftPageReqVO reqVO) {
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .eqIfPresent(AttendanceGroupShiftDO::getAttendanceGroupId, reqVO.getAttendanceGroupId())
+ .likeIfPresent(AttendanceGroupShiftDO::getName, reqVO.getName())
+ .eqIfPresent(AttendanceGroupShiftDO::getAttendanceTimeJson, reqVO.getAttendanceTimeJson())
+ .betweenIfPresent(AttendanceGroupShiftDO::getCreateTime, reqVO.getCreateTime())
+ .orderByDesc(AttendanceGroupShiftDO::getId));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/groupuser/AttendanceGroupUserMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/groupuser/AttendanceGroupUserMapper.java
new file mode 100644
index 00000000..c212ccc5
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/groupuser/AttendanceGroupUserMapper.java
@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.system.dal.mysql.groupuser;
+
+import java.util.*;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.module.system.dal.dataobject.groupuser.AttendanceGroupUserDO;
+import org.apache.ibatis.annotations.Mapper;
+import cn.iocoder.yudao.module.system.controller.admin.groupuser.vo.*;
+
+/**
+ * 考勤组人员 Mapper
+ *
+ * @author 艾楷
+ */
+@Mapper
+public interface AttendanceGroupUserMapper extends BaseMapperX {
+
+ default PageResult selectPage(AttendanceGroupUserPageReqVO reqVO) {
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .eqIfPresent(AttendanceGroupUserDO::getAttendanceGroupId, reqVO.getAttendanceGroupId())
+ .eqIfPresent(AttendanceGroupUserDO::getUserId, reqVO.getUserId())
+ .betweenIfPresent(AttendanceGroupUserDO::getCreateTime, reqVO.getCreateTime())
+ .orderByDesc(AttendanceGroupUserDO::getId));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/punchrecord/AttendancePunchRecordMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/punchrecord/AttendancePunchRecordMapper.java
new file mode 100644
index 00000000..e4a34032
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/punchrecord/AttendancePunchRecordMapper.java
@@ -0,0 +1,37 @@
+package cn.iocoder.yudao.module.system.dal.mysql.punchrecord;
+
+import java.util.*;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.module.system.dal.dataobject.punchrecord.AttendancePunchRecordDO;
+import org.apache.ibatis.annotations.Mapper;
+import cn.iocoder.yudao.module.system.controller.admin.punchrecord.vo.*;
+
+/**
+ * 用户打卡记录 Mapper
+ *
+ * @author 艾楷
+ */
+@Mapper
+public interface AttendancePunchRecordMapper extends BaseMapperX {
+
+ default PageResult selectPage(AttendancePunchRecordPageReqVO reqVO) {
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .eqIfPresent(AttendancePunchRecordDO::getUserId, reqVO.getUserId())
+ .eqIfPresent(AttendancePunchRecordDO::getAttendanceGroupId, reqVO.getAttendanceGroupId())
+ .eqIfPresent(AttendancePunchRecordDO::getAttendanceGroupShiftId, reqVO.getAttendanceGroupShiftId())
+ .eqIfPresent(AttendancePunchRecordDO::getType, reqVO.getType())
+ .eqIfPresent(AttendancePunchRecordDO::getPunchType, reqVO.getPunchType())
+ .eqIfPresent(AttendancePunchRecordDO::getAttendanceTimeJson, reqVO.getAttendanceTimeJson())
+ .eqIfPresent(AttendancePunchRecordDO::getStatus, reqVO.getStatus())
+ .betweenIfPresent(AttendancePunchRecordDO::getDayTime, reqVO.getDayTime())
+ .betweenIfPresent(AttendancePunchRecordDO::getPunchTime, reqVO.getPunchTime())
+ .eqIfPresent(AttendancePunchRecordDO::getRemark, reqVO.getRemark())
+ .eqIfPresent(AttendancePunchRecordDO::getImage, reqVO.getImage())
+ .betweenIfPresent(AttendancePunchRecordDO::getCreateTime, reqVO.getCreateTime())
+ .orderByDesc(AttendancePunchRecordDO::getId));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/scheduling/AttendanceSchedulingMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/scheduling/AttendanceSchedulingMapper.java
new file mode 100644
index 00000000..13b05f5c
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/scheduling/AttendanceSchedulingMapper.java
@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.module.system.dal.mysql.scheduling;
+
+import java.util.*;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.module.system.dal.dataobject.scheduling.AttendanceSchedulingDO;
+import org.apache.ibatis.annotations.Mapper;
+import cn.iocoder.yudao.module.system.controller.admin.scheduling.vo.*;
+
+/**
+ * 排班制考勤设置 Mapper
+ *
+ * @author 艾楷
+ */
+@Mapper
+public interface AttendanceSchedulingMapper extends BaseMapperX {
+
+ default PageResult selectPage(AttendanceSchedulingPageReqVO reqVO) {
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .eqIfPresent(AttendanceSchedulingDO::getIndexDay, reqVO.getIndexDay())
+ .eqIfPresent(AttendanceSchedulingDO::getAttendanceGroupId, reqVO.getAttendanceGroupId())
+ .eqIfPresent(AttendanceSchedulingDO::getAttendanceGroupShiftId, reqVO.getAttendanceGroupShiftId())
+ .eqIfPresent(AttendanceSchedulingDO::getRestFlag, reqVO.getRestFlag())
+ .betweenIfPresent(AttendanceSchedulingDO::getCreateTime, reqVO.getCreateTime())
+ .orderByDesc(AttendanceSchedulingDO::getId));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/handler/PunchHandler.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/handler/PunchHandler.java
new file mode 100644
index 00000000..47b2aec0
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/handler/PunchHandler.java
@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.system.handler;
+
+import cn.iocoder.yudao.module.system.service.punch.PunchService;
+import org.springframework.stereotype.Service;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @Author : AiKai
+ * @Description : 利用Spring的发现机制,将实现了IChannelLoginService的类都put到channelLoginServiceMap里面。
+ * 后面只需要根据channelType对应好 各个实现类的注解 如: @Component("sys") 就可以取出不同的业务实现类
+ **/
+@Service
+public class PunchHandler {
+ private final Map punchServiceMap = new ConcurrentHashMap<>();
+
+ public PunchHandler(Map strategyMap) {
+ this.punchServiceMap.clear();
+ strategyMap.forEach((k, v) -> this.punchServiceMap.put(k, v));
+ }
+
+ public PunchService getResource(String channelType) {
+ return punchServiceMap.get(channelType);
+ }
+
+}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/attendance/AttendanceService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/attendance/AttendanceService.java
new file mode 100644
index 00000000..f999363c
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/attendance/AttendanceService.java
@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.module.system.service.attendance;
+
+import cn.iocoder.yudao.module.system.controller.app.attendance.dto.AttendancePunchPageDTO;
+import cn.iocoder.yudao.module.system.controller.app.attendance.vo.AttendancePunchPageVO;
+
+/**
+ * 考勤 Service 接口
+ *
+ * @author 艾楷
+ */
+public interface AttendanceService {
+
+
+ AttendancePunchPageVO getPunchPage(AttendancePunchPageDTO attendancePunchPageDTO);
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/attendance/AttendanceServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/attendance/AttendanceServiceImpl.java
new file mode 100644
index 00000000..0e6f1c77
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/attendance/AttendanceServiceImpl.java
@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.system.service.attendance;
+
+import cn.iocoder.yudao.module.system.controller.app.attendance.dto.AttendancePunchPageDTO;
+import cn.iocoder.yudao.module.system.controller.app.attendance.vo.AttendancePunchPageVO;
+import cn.iocoder.yudao.module.system.dal.dataobject.group.AttendanceGroupDO;
+import cn.iocoder.yudao.module.system.handler.PunchHandler;
+import cn.iocoder.yudao.module.system.service.group.AttendanceGroupService;
+import cn.iocoder.yudao.module.system.service.punch.PunchService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+
+/**
+ * 考勤 实现类
+ */
+@Service
+@Slf4j
+public class AttendanceServiceImpl implements AttendanceService {
+ @Resource
+ private PunchHandler punchHandler;
+ @Resource
+ private AttendanceGroupService attendanceGroupService;
+
+ @Override
+ public AttendancePunchPageVO getPunchPage(AttendancePunchPageDTO attendancePunchPageDTO) {
+ AttendancePunchPageVO vo = new AttendancePunchPageVO();
+ //获取当前登录用户所在群组
+ AttendanceGroupDO activationGroup = attendanceGroupService.getByUserId(attendancePunchPageDTO.getUserId());
+ if (activationGroup == null) {
+ // TODO: 2024/4/10 不在考勤组 - 返回回去
+ return vo;
+ }
+ //判断目前是否在班次内
+ PunchService punchService = punchHandler.getResource(AttendanceGroupDO.getCodeByType(activationGroup.getType()));
+ // TODO: 2024/4/10 处理打卡
+ return null;
+ }
+}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/fixed/AttendanceFixedService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/fixed/AttendanceFixedService.java
new file mode 100644
index 00000000..9e5e6d25
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/fixed/AttendanceFixedService.java
@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.module.system.service.fixed;
+
+import java.util.*;
+import javax.validation.*;
+import cn.iocoder.yudao.module.system.controller.admin.fixed.vo.*;
+import cn.iocoder.yudao.module.system.dal.dataobject.fixed.AttendanceFixedDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+
+/**
+ * 固定班制考勤设置 Service 接口
+ *
+ * @author 艾楷
+ */
+public interface AttendanceFixedService {
+
+ /**
+ * 创建固定班制考勤设置
+ *
+ * @param createReqVO 创建信息
+ * @return 编号
+ */
+ Long createFixed(@Valid AttendanceFixedSaveReqVO createReqVO);
+
+ /**
+ * 更新固定班制考勤设置
+ *
+ * @param updateReqVO 更新信息
+ */
+ void updateFixed(@Valid AttendanceFixedSaveReqVO updateReqVO);
+
+ /**
+ * 删除固定班制考勤设置
+ *
+ * @param id 编号
+ */
+ void deleteFixed(Long id);
+
+ /**
+ * 获得固定班制考勤设置
+ *
+ * @param id 编号
+ * @return 固定班制考勤设置
+ */
+ AttendanceFixedDO getFixed(Long id);
+
+ /**
+ * 获得固定班制考勤设置分页
+ *
+ * @param pageReqVO 分页查询
+ * @return 固定班制考勤设置分页
+ */
+ PageResult getFixedPage(AttendanceFixedPageReqVO pageReqVO);
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/fixed/AttendanceFixedServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/fixed/AttendanceFixedServiceImpl.java
new file mode 100644
index 00000000..6c09d70e
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/fixed/AttendanceFixedServiceImpl.java
@@ -0,0 +1,74 @@
+package cn.iocoder.yudao.module.system.service.fixed;
+
+import org.springframework.stereotype.Service;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+import cn.iocoder.yudao.module.system.controller.admin.fixed.vo.*;
+import cn.iocoder.yudao.module.system.dal.dataobject.fixed.AttendanceFixedDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+
+import cn.iocoder.yudao.module.system.dal.mysql.fixed.AttendanceFixedMapper;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
+
+/**
+ * 固定班制考勤设置 Service 实现类
+ *
+ * @author 艾楷
+ */
+@Service("fixed")
+@Validated
+public class AttendanceFixedServiceImpl implements AttendanceFixedService {
+
+ @Resource
+ private AttendanceFixedMapper fixedMapper;
+
+ @Override
+ public Long createFixed(AttendanceFixedSaveReqVO createReqVO) {
+ // 插入
+ AttendanceFixedDO fixed = BeanUtils.toBean(createReqVO, AttendanceFixedDO.class);
+ fixedMapper.insert(fixed);
+ // 返回
+ return fixed.getId();
+ }
+
+ @Override
+ public void updateFixed(AttendanceFixedSaveReqVO updateReqVO) {
+ // 校验存在
+ validateFixedExists(updateReqVO.getId());
+ // 更新
+ AttendanceFixedDO updateObj = BeanUtils.toBean(updateReqVO, AttendanceFixedDO.class);
+ fixedMapper.updateById(updateObj);
+ }
+
+ @Override
+ public void deleteFixed(Long id) {
+ // 校验存在
+ validateFixedExists(id);
+ // 删除
+ fixedMapper.deleteById(id);
+ }
+
+ private void validateFixedExists(Long id) {
+ if (fixedMapper.selectById(id) == null) {
+ throw exception(FIXED_NOT_EXISTS);
+ }
+ }
+
+ @Override
+ public AttendanceFixedDO getFixed(Long id) {
+ return fixedMapper.selectById(id);
+ }
+
+ @Override
+ public PageResult getFixedPage(AttendanceFixedPageReqVO pageReqVO) {
+ return fixedMapper.selectPage(pageReqVO);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/group/AttendanceGroupService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/group/AttendanceGroupService.java
new file mode 100644
index 00000000..8f02d0e0
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/group/AttendanceGroupService.java
@@ -0,0 +1,62 @@
+package cn.iocoder.yudao.module.system.service.group;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.system.controller.admin.group.vo.AttendanceGroupPageReqVO;
+import cn.iocoder.yudao.module.system.controller.admin.group.vo.AttendanceGroupSaveReqVO;
+import cn.iocoder.yudao.module.system.dal.dataobject.group.AttendanceGroupDO;
+
+import javax.validation.Valid;
+
+/**
+ * 考勤组 Service 接口
+ *
+ * @author 艾楷
+ */
+public interface AttendanceGroupService {
+
+ /**
+ * 创建考勤组
+ *
+ * @param createReqVO 创建信息
+ * @return 编号
+ */
+ Long createGroup(@Valid AttendanceGroupSaveReqVO createReqVO);
+
+ /**
+ * 更新考勤组
+ *
+ * @param updateReqVO 更新信息
+ */
+ void updateGroup(@Valid AttendanceGroupSaveReqVO updateReqVO);
+
+ /**
+ * 删除考勤组
+ *
+ * @param id 编号
+ */
+ void deleteGroup(Long id);
+
+ /**
+ * 获得考勤组
+ *
+ * @param id 编号
+ * @return 考勤组
+ */
+ AttendanceGroupDO getGroup(Long id);
+
+ /**
+ * 获得考勤组分页
+ *
+ * @param pageReqVO 分页查询
+ * @return 考勤组分页
+ */
+ PageResult getGroupPage(AttendanceGroupPageReqVO pageReqVO);
+
+ /**
+ * 获取用户所在群组
+ *
+ * @param userId
+ * @return
+ */
+ AttendanceGroupDO getByUserId(Long userId);
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/group/AttendanceGroupServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/group/AttendanceGroupServiceImpl.java
new file mode 100644
index 00000000..b7c5cd92
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/group/AttendanceGroupServiceImpl.java
@@ -0,0 +1,82 @@
+package cn.iocoder.yudao.module.system.service.group;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.system.controller.admin.group.vo.AttendanceGroupPageReqVO;
+import cn.iocoder.yudao.module.system.controller.admin.group.vo.AttendanceGroupSaveReqVO;
+import cn.iocoder.yudao.module.system.dal.dataobject.group.AttendanceGroupDO;
+import cn.iocoder.yudao.module.system.dal.mysql.group.AttendanceGroupMapper;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.rmi.activation.ActivationGroup;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.GROUP_NOT_EXISTS;
+
+/**
+ * 考勤组 Service 实现类
+ *
+ * @author 艾楷
+ */
+@Service
+@Validated
+public class AttendanceGroupServiceImpl implements AttendanceGroupService {
+
+ @Resource
+ private AttendanceGroupMapper groupMapper;
+
+ @Override
+ public Long createGroup(AttendanceGroupSaveReqVO createReqVO) {
+ // 插入
+ AttendanceGroupDO group = BeanUtils.toBean(createReqVO, AttendanceGroupDO.class);
+ groupMapper.insert(group);
+ // 返回
+ return group.getId();
+ }
+
+ @Override
+ public void updateGroup(AttendanceGroupSaveReqVO updateReqVO) {
+ // 校验存在
+ validateGroupExists(updateReqVO.getId());
+ // 更新
+ AttendanceGroupDO updateObj = BeanUtils.toBean(updateReqVO, AttendanceGroupDO.class);
+ groupMapper.updateById(updateObj);
+ }
+
+ @Override
+ public void deleteGroup(Long id) {
+ // 校验存在
+ validateGroupExists(id);
+ // 删除
+ groupMapper.deleteById(id);
+ }
+
+ private void validateGroupExists(Long id) {
+ if (groupMapper.selectById(id) == null) {
+ throw exception(GROUP_NOT_EXISTS);
+ }
+ }
+
+ @Override
+ public AttendanceGroupDO getGroup(Long id) {
+ return groupMapper.selectById(id);
+ }
+
+ @Override
+ public PageResult getGroupPage(AttendanceGroupPageReqVO pageReqVO) {
+ return groupMapper.selectPage(pageReqVO);
+ }
+
+ @Override
+ public AttendanceGroupDO getByUserId(Long userId) {
+ List dos = groupMapper.getByUserId(userId);
+ if (!dos.isEmpty()) {
+ return dos.get(0);
+ }
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/groupshift/AttendanceGroupShiftService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/groupshift/AttendanceGroupShiftService.java
new file mode 100644
index 00000000..0a91cac1
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/groupshift/AttendanceGroupShiftService.java
@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.module.system.service.groupshift;
+
+import java.util.*;
+import javax.validation.*;
+import cn.iocoder.yudao.module.system.controller.admin.groupshift.vo.*;
+import cn.iocoder.yudao.module.system.dal.dataobject.groupshift.AttendanceGroupShiftDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+
+/**
+ * 考勤组班次 Service 接口
+ *
+ * @author 艾楷
+ */
+public interface AttendanceGroupShiftService {
+
+ /**
+ * 创建考勤组班次
+ *
+ * @param createReqVO 创建信息
+ * @return 编号
+ */
+ Long createGroupShift(@Valid AttendanceGroupShiftSaveReqVO createReqVO);
+
+ /**
+ * 更新考勤组班次
+ *
+ * @param updateReqVO 更新信息
+ */
+ void updateGroupShift(@Valid AttendanceGroupShiftSaveReqVO updateReqVO);
+
+ /**
+ * 删除考勤组班次
+ *
+ * @param id 编号
+ */
+ void deleteGroupShift(Long id);
+
+ /**
+ * 获得考勤组班次
+ *
+ * @param id 编号
+ * @return 考勤组班次
+ */
+ AttendanceGroupShiftDO getGroupShift(Long id);
+
+ /**
+ * 获得考勤组班次分页
+ *
+ * @param pageReqVO 分页查询
+ * @return 考勤组班次分页
+ */
+ PageResult getGroupShiftPage(AttendanceGroupShiftPageReqVO pageReqVO);
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/groupshift/AttendanceGroupShiftServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/groupshift/AttendanceGroupShiftServiceImpl.java
new file mode 100644
index 00000000..24f73428
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/groupshift/AttendanceGroupShiftServiceImpl.java
@@ -0,0 +1,74 @@
+package cn.iocoder.yudao.module.system.service.groupshift;
+
+import org.springframework.stereotype.Service;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+import cn.iocoder.yudao.module.system.controller.admin.groupshift.vo.*;
+import cn.iocoder.yudao.module.system.dal.dataobject.groupshift.AttendanceGroupShiftDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+
+import cn.iocoder.yudao.module.system.dal.mysql.groupshift.AttendanceGroupShiftMapper;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
+
+/**
+ * 考勤组班次 Service 实现类
+ *
+ * @author 艾楷
+ */
+@Service
+@Validated
+public class AttendanceGroupShiftServiceImpl implements AttendanceGroupShiftService {
+
+ @Resource
+ private AttendanceGroupShiftMapper groupShiftMapper;
+
+ @Override
+ public Long createGroupShift(AttendanceGroupShiftSaveReqVO createReqVO) {
+ // 插入
+ AttendanceGroupShiftDO groupShift = BeanUtils.toBean(createReqVO, AttendanceGroupShiftDO.class);
+ groupShiftMapper.insert(groupShift);
+ // 返回
+ return groupShift.getId();
+ }
+
+ @Override
+ public void updateGroupShift(AttendanceGroupShiftSaveReqVO updateReqVO) {
+ // 校验存在
+ validateGroupShiftExists(updateReqVO.getId());
+ // 更新
+ AttendanceGroupShiftDO updateObj = BeanUtils.toBean(updateReqVO, AttendanceGroupShiftDO.class);
+ groupShiftMapper.updateById(updateObj);
+ }
+
+ @Override
+ public void deleteGroupShift(Long id) {
+ // 校验存在
+ validateGroupShiftExists(id);
+ // 删除
+ groupShiftMapper.deleteById(id);
+ }
+
+ private void validateGroupShiftExists(Long id) {
+ if (groupShiftMapper.selectById(id) == null) {
+ throw exception(GROUP_SHIFT_NOT_EXISTS);
+ }
+ }
+
+ @Override
+ public AttendanceGroupShiftDO getGroupShift(Long id) {
+ return groupShiftMapper.selectById(id);
+ }
+
+ @Override
+ public PageResult getGroupShiftPage(AttendanceGroupShiftPageReqVO pageReqVO) {
+ return groupShiftMapper.selectPage(pageReqVO);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/groupuser/AttendanceGroupUserService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/groupuser/AttendanceGroupUserService.java
new file mode 100644
index 00000000..56b80eee
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/groupuser/AttendanceGroupUserService.java
@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.module.system.service.groupuser;
+
+import java.util.*;
+import javax.validation.*;
+import cn.iocoder.yudao.module.system.controller.admin.groupuser.vo.*;
+import cn.iocoder.yudao.module.system.dal.dataobject.groupuser.AttendanceGroupUserDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+
+/**
+ * 考勤组人员 Service 接口
+ *
+ * @author 艾楷
+ */
+public interface AttendanceGroupUserService {
+
+ /**
+ * 创建考勤组人员
+ *
+ * @param createReqVO 创建信息
+ * @return 编号
+ */
+ Long createGroupUser(@Valid AttendanceGroupUserSaveReqVO createReqVO);
+
+ /**
+ * 更新考勤组人员
+ *
+ * @param updateReqVO 更新信息
+ */
+ void updateGroupUser(@Valid AttendanceGroupUserSaveReqVO updateReqVO);
+
+ /**
+ * 删除考勤组人员
+ *
+ * @param id 编号
+ */
+ void deleteGroupUser(Long id);
+
+ /**
+ * 获得考勤组人员
+ *
+ * @param id 编号
+ * @return 考勤组人员
+ */
+ AttendanceGroupUserDO getGroupUser(Long id);
+
+ /**
+ * 获得考勤组人员分页
+ *
+ * @param pageReqVO 分页查询
+ * @return 考勤组人员分页
+ */
+ PageResult getGroupUserPage(AttendanceGroupUserPageReqVO pageReqVO);
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/groupuser/AttendanceGroupUserServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/groupuser/AttendanceGroupUserServiceImpl.java
new file mode 100644
index 00000000..f95daf34
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/groupuser/AttendanceGroupUserServiceImpl.java
@@ -0,0 +1,74 @@
+package cn.iocoder.yudao.module.system.service.groupuser;
+
+import org.springframework.stereotype.Service;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+import cn.iocoder.yudao.module.system.controller.admin.groupuser.vo.*;
+import cn.iocoder.yudao.module.system.dal.dataobject.groupuser.AttendanceGroupUserDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+
+import cn.iocoder.yudao.module.system.dal.mysql.groupuser.AttendanceGroupUserMapper;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
+
+/**
+ * 考勤组人员 Service 实现类
+ *
+ * @author 艾楷
+ */
+@Service
+@Validated
+public class AttendanceGroupUserServiceImpl implements AttendanceGroupUserService {
+
+ @Resource
+ private AttendanceGroupUserMapper groupUserMapper;
+
+ @Override
+ public Long createGroupUser(AttendanceGroupUserSaveReqVO createReqVO) {
+ // 插入
+ AttendanceGroupUserDO groupUser = BeanUtils.toBean(createReqVO, AttendanceGroupUserDO.class);
+ groupUserMapper.insert(groupUser);
+ // 返回
+ return groupUser.getId();
+ }
+
+ @Override
+ public void updateGroupUser(AttendanceGroupUserSaveReqVO updateReqVO) {
+ // 校验存在
+ validateGroupUserExists(updateReqVO.getId());
+ // 更新
+ AttendanceGroupUserDO updateObj = BeanUtils.toBean(updateReqVO, AttendanceGroupUserDO.class);
+ groupUserMapper.updateById(updateObj);
+ }
+
+ @Override
+ public void deleteGroupUser(Long id) {
+ // 校验存在
+ validateGroupUserExists(id);
+ // 删除
+ groupUserMapper.deleteById(id);
+ }
+
+ private void validateGroupUserExists(Long id) {
+ if (groupUserMapper.selectById(id) == null) {
+ throw exception(GROUP_USER_NOT_EXISTS);
+ }
+ }
+
+ @Override
+ public AttendanceGroupUserDO getGroupUser(Long id) {
+ return groupUserMapper.selectById(id);
+ }
+
+ @Override
+ public PageResult getGroupUserPage(AttendanceGroupUserPageReqVO pageReqVO) {
+ return groupUserMapper.selectPage(pageReqVO);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/punch/PunchService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/punch/PunchService.java
new file mode 100644
index 00000000..c3a7e906
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/punch/PunchService.java
@@ -0,0 +1,12 @@
+package cn.iocoder.yudao.module.system.service.punch;
+
+/**
+ * 打卡 Service 接口
+ *
+ * @author 艾楷
+ */
+public interface PunchService {
+
+
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/punchrecord/AttendancePunchRecordService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/punchrecord/AttendancePunchRecordService.java
new file mode 100644
index 00000000..7d5b9caf
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/punchrecord/AttendancePunchRecordService.java
@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.module.system.service.punchrecord;
+
+import java.util.*;
+import javax.validation.*;
+import cn.iocoder.yudao.module.system.controller.admin.punchrecord.vo.*;
+import cn.iocoder.yudao.module.system.dal.dataobject.punchrecord.AttendancePunchRecordDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+
+/**
+ * 用户打卡记录 Service 接口
+ *
+ * @author 艾楷
+ */
+public interface AttendancePunchRecordService {
+
+ /**
+ * 创建用户打卡记录
+ *
+ * @param createReqVO 创建信息
+ * @return 编号
+ */
+ Long createPunchRecord(@Valid AttendancePunchRecordSaveReqVO createReqVO);
+
+ /**
+ * 更新用户打卡记录
+ *
+ * @param updateReqVO 更新信息
+ */
+ void updatePunchRecord(@Valid AttendancePunchRecordSaveReqVO updateReqVO);
+
+ /**
+ * 删除用户打卡记录
+ *
+ * @param id 编号
+ */
+ void deletePunchRecord(Long id);
+
+ /**
+ * 获得用户打卡记录
+ *
+ * @param id 编号
+ * @return 用户打卡记录
+ */
+ AttendancePunchRecordDO getPunchRecord(Long id);
+
+ /**
+ * 获得用户打卡记录分页
+ *
+ * @param pageReqVO 分页查询
+ * @return 用户打卡记录分页
+ */
+ PageResult getPunchRecordPage(AttendancePunchRecordPageReqVO pageReqVO);
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/punchrecord/AttendancePunchRecordServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/punchrecord/AttendancePunchRecordServiceImpl.java
new file mode 100644
index 00000000..6bc54ceb
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/punchrecord/AttendancePunchRecordServiceImpl.java
@@ -0,0 +1,74 @@
+package cn.iocoder.yudao.module.system.service.punchrecord;
+
+import org.springframework.stereotype.Service;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+import cn.iocoder.yudao.module.system.controller.admin.punchrecord.vo.*;
+import cn.iocoder.yudao.module.system.dal.dataobject.punchrecord.AttendancePunchRecordDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+
+import cn.iocoder.yudao.module.system.dal.mysql.punchrecord.AttendancePunchRecordMapper;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
+
+/**
+ * 用户打卡记录 Service 实现类
+ *
+ * @author 艾楷
+ */
+@Service
+@Validated
+public class AttendancePunchRecordServiceImpl implements AttendancePunchRecordService {
+
+ @Resource
+ private AttendancePunchRecordMapper punchRecordMapper;
+
+ @Override
+ public Long createPunchRecord(AttendancePunchRecordSaveReqVO createReqVO) {
+ // 插入
+ AttendancePunchRecordDO punchRecord = BeanUtils.toBean(createReqVO, AttendancePunchRecordDO.class);
+ punchRecordMapper.insert(punchRecord);
+ // 返回
+ return punchRecord.getId();
+ }
+
+ @Override
+ public void updatePunchRecord(AttendancePunchRecordSaveReqVO updateReqVO) {
+ // 校验存在
+ validatePunchRecordExists(updateReqVO.getId());
+ // 更新
+ AttendancePunchRecordDO updateObj = BeanUtils.toBean(updateReqVO, AttendancePunchRecordDO.class);
+ punchRecordMapper.updateById(updateObj);
+ }
+
+ @Override
+ public void deletePunchRecord(Long id) {
+ // 校验存在
+ validatePunchRecordExists(id);
+ // 删除
+ punchRecordMapper.deleteById(id);
+ }
+
+ private void validatePunchRecordExists(Long id) {
+ if (punchRecordMapper.selectById(id) == null) {
+ throw exception(PUNCH_RECORD_NOT_EXISTS);
+ }
+ }
+
+ @Override
+ public AttendancePunchRecordDO getPunchRecord(Long id) {
+ return punchRecordMapper.selectById(id);
+ }
+
+ @Override
+ public PageResult getPunchRecordPage(AttendancePunchRecordPageReqVO pageReqVO) {
+ return punchRecordMapper.selectPage(pageReqVO);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/scheduling/AttendanceSchedulingService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/scheduling/AttendanceSchedulingService.java
new file mode 100644
index 00000000..2dc4ca64
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/scheduling/AttendanceSchedulingService.java
@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.module.system.service.scheduling;
+
+import java.util.*;
+import javax.validation.*;
+import cn.iocoder.yudao.module.system.controller.admin.scheduling.vo.*;
+import cn.iocoder.yudao.module.system.dal.dataobject.scheduling.AttendanceSchedulingDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+
+/**
+ * 排班制考勤设置 Service 接口
+ *
+ * @author 艾楷
+ */
+public interface AttendanceSchedulingService {
+
+ /**
+ * 创建排班制考勤设置
+ *
+ * @param createReqVO 创建信息
+ * @return 编号
+ */
+ Long createScheduling(@Valid AttendanceSchedulingSaveReqVO createReqVO);
+
+ /**
+ * 更新排班制考勤设置
+ *
+ * @param updateReqVO 更新信息
+ */
+ void updateScheduling(@Valid AttendanceSchedulingSaveReqVO updateReqVO);
+
+ /**
+ * 删除排班制考勤设置
+ *
+ * @param id 编号
+ */
+ void deleteScheduling(Long id);
+
+ /**
+ * 获得排班制考勤设置
+ *
+ * @param id 编号
+ * @return 排班制考勤设置
+ */
+ AttendanceSchedulingDO getScheduling(Long id);
+
+ /**
+ * 获得排班制考勤设置分页
+ *
+ * @param pageReqVO 分页查询
+ * @return 排班制考勤设置分页
+ */
+ PageResult getSchedulingPage(AttendanceSchedulingPageReqVO pageReqVO);
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/scheduling/AttendanceSchedulingServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/scheduling/AttendanceSchedulingServiceImpl.java
new file mode 100644
index 00000000..2319a432
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/scheduling/AttendanceSchedulingServiceImpl.java
@@ -0,0 +1,74 @@
+package cn.iocoder.yudao.module.system.service.scheduling;
+
+import org.springframework.stereotype.Service;
+import javax.annotation.Resource;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+import cn.iocoder.yudao.module.system.controller.admin.scheduling.vo.*;
+import cn.iocoder.yudao.module.system.dal.dataobject.scheduling.AttendanceSchedulingDO;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+
+import cn.iocoder.yudao.module.system.dal.mysql.scheduling.AttendanceSchedulingMapper;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
+
+/**
+ * 排班制考勤设置 Service 实现类
+ *
+ * @author 艾楷
+ */
+@Service("scheduling")
+@Validated
+public class AttendanceSchedulingServiceImpl implements AttendanceSchedulingService {
+
+ @Resource
+ private AttendanceSchedulingMapper schedulingMapper;
+
+ @Override
+ public Long createScheduling(AttendanceSchedulingSaveReqVO createReqVO) {
+ // 插入
+ AttendanceSchedulingDO scheduling = BeanUtils.toBean(createReqVO, AttendanceSchedulingDO.class);
+ schedulingMapper.insert(scheduling);
+ // 返回
+ return scheduling.getId();
+ }
+
+ @Override
+ public void updateScheduling(AttendanceSchedulingSaveReqVO updateReqVO) {
+ // 校验存在
+ validateSchedulingExists(updateReqVO.getId());
+ // 更新
+ AttendanceSchedulingDO updateObj = BeanUtils.toBean(updateReqVO, AttendanceSchedulingDO.class);
+ schedulingMapper.updateById(updateObj);
+ }
+
+ @Override
+ public void deleteScheduling(Long id) {
+ // 校验存在
+ validateSchedulingExists(id);
+ // 删除
+ schedulingMapper.deleteById(id);
+ }
+
+ private void validateSchedulingExists(Long id) {
+ if (schedulingMapper.selectById(id) == null) {
+ throw exception(SCHEDULING_NOT_EXISTS);
+ }
+ }
+
+ @Override
+ public AttendanceSchedulingDO getScheduling(Long id) {
+ return schedulingMapper.selectById(id);
+ }
+
+ @Override
+ public PageResult getSchedulingPage(AttendanceSchedulingPageReqVO pageReqVO) {
+ return schedulingMapper.selectPage(pageReqVO);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/application-dev.yaml b/yudao-module-system/yudao-module-system-biz/src/main/resources/application-dev.yaml
index 72d23f84..96835b0f 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/resources/application-dev.yaml
+++ b/yudao-module-system/yudao-module-system-biz/src/main/resources/application-dev.yaml
@@ -125,8 +125,8 @@ wx:
miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档
# appid: wx62056c0d5e8db250
# secret: 333ae72f41552af1e998fe1f54e1584a
- appid: wx63c280fe3248a3e7 # wenhualian的接口测试号
- secret: 6f270509224a7ae1296bbf1c8cb97aed
+ appid: wxea777d1b1e12eb32 # wenhualian的接口测试号
+ secret: 2db58956286099e58e959fa537e046b0
config-storage:
type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取
key-prefix: wa # Redis Key 的前缀
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/application-local.yaml b/yudao-module-system/yudao-module-system-biz/src/main/resources/application-local.yaml
index c74890d1..80e4408d 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/resources/application-local.yaml
+++ b/yudao-module-system/yudao-module-system-biz/src/main/resources/application-local.yaml
@@ -143,8 +143,8 @@ wx:
miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档
# appid: wx62056c0d5e8db250
# secret: 333ae72f41552af1e998fe1f54e1584a
- appid: wx2919e237e6018bea # wenhualian的接口测试号
- secret: ad7ab17918f6defa85a9677778eb7780
+ appid: wxea777d1b1e12eb32 # wenhualian的接口测试号
+ secret: 2db58956286099e58e959fa537e046b0
config-storage:
type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取
key-prefix: wa # Redis Key 的前缀
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/bootstrap.yaml b/yudao-module-system/yudao-module-system-biz/src/main/resources/bootstrap.yaml
index d80b600d..9498addd 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/resources/bootstrap.yaml
+++ b/yudao-module-system/yudao-module-system-biz/src/main/resources/bootstrap.yaml
@@ -3,8 +3,7 @@ spring:
name: system-server
profiles:
- active: local #local
-# active: dev
+ active: dev #local
# active: prod
server:
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/mapper/fixed/AttendanceFixedMapper.xml b/yudao-module-system/yudao-module-system-biz/src/main/resources/mapper/fixed/AttendanceFixedMapper.xml
new file mode 100644
index 00000000..871e1845
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/resources/mapper/fixed/AttendanceFixedMapper.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/mapper/group/AttendanceGroupMapper.xml b/yudao-module-system/yudao-module-system-biz/src/main/resources/mapper/group/AttendanceGroupMapper.xml
new file mode 100644
index 00000000..3ef95aaa
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/resources/mapper/group/AttendanceGroupMapper.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/mapper/groupshift/AttendanceGroupShiftMapper.xml b/yudao-module-system/yudao-module-system-biz/src/main/resources/mapper/groupshift/AttendanceGroupShiftMapper.xml
new file mode 100644
index 00000000..de293bf0
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/resources/mapper/groupshift/AttendanceGroupShiftMapper.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/mapper/groupuser/AttendanceGroupUserMapper.xml b/yudao-module-system/yudao-module-system-biz/src/main/resources/mapper/groupuser/AttendanceGroupUserMapper.xml
new file mode 100644
index 00000000..817415c0
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/resources/mapper/groupuser/AttendanceGroupUserMapper.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/mapper/punchrecord/AttendancePunchRecordMapper.xml b/yudao-module-system/yudao-module-system-biz/src/main/resources/mapper/punchrecord/AttendancePunchRecordMapper.xml
new file mode 100644
index 00000000..b564016c
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/resources/mapper/punchrecord/AttendancePunchRecordMapper.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/mapper/scheduling/AttendanceSchedulingMapper.xml b/yudao-module-system/yudao-module-system-biz/src/main/resources/mapper/scheduling/AttendanceSchedulingMapper.xml
new file mode 100644
index 00000000..511d3a24
--- /dev/null
+++ b/yudao-module-system/yudao-module-system-biz/src/main/resources/mapper/scheduling/AttendanceSchedulingMapper.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/zn-module-smartfactory/zn-module-smartfactory-biz/src/main/resources/bootstrap.yaml b/zn-module-smartfactory/zn-module-smartfactory-biz/src/main/resources/bootstrap.yaml
index 7591aed7..818d6381 100644
--- a/zn-module-smartfactory/zn-module-smartfactory-biz/src/main/resources/bootstrap.yaml
+++ b/zn-module-smartfactory/zn-module-smartfactory-biz/src/main/resources/bootstrap.yaml
@@ -3,7 +3,7 @@ spring:
name: smartfactory-server
profiles:
- active: local
+ active: dev
server:
port: 48090