feat: 线索分配需求
This commit is contained in:
parent
0a9480d6e6
commit
238d17802d
|
|
@ -0,0 +1,264 @@
|
|||
# 线索值班配置系统技术架构文档
|
||||
|
||||
## 1. 架构设计
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[用户浏览器] --> B[Spring Boot Web层]
|
||||
B --> C[Service业务层]
|
||||
C --> D[MyBatis数据访问层]
|
||||
D --> E[MySQL数据库]
|
||||
|
||||
B --> F[Thymeleaf模板引擎]
|
||||
C --> G[定时任务调度]
|
||||
C --> H[缓存层Redis]
|
||||
|
||||
subgraph "前端层"
|
||||
B
|
||||
F
|
||||
end
|
||||
|
||||
subgraph "业务层"
|
||||
C
|
||||
G
|
||||
H
|
||||
end
|
||||
|
||||
subgraph "数据层"
|
||||
D
|
||||
E
|
||||
end
|
||||
```
|
||||
|
||||
## 2. 技术描述
|
||||
|
||||
- **前端**:Thymeleaf@3.0 + Bootstrap@4.6 + jQuery@3.6 + LayUI@2.6
|
||||
- **后端**:Spring Boot@2.5.14 + MyBatis@3.5 + Apache Shiro@1.10.1
|
||||
- **数据库**:MySQL@8.0
|
||||
- **缓存**:Redis@6.2(可选,用于分配算法优化)
|
||||
|
||||
## 3. 路由定义
|
||||
|
||||
| 路由 | 用途 |
|
||||
|------|------|
|
||||
| /system/dutyConfig | 值班配置管理主页面 |
|
||||
| /system/dutyConfig/list | 获取值班配置列表数据 |
|
||||
| /system/dutyConfig/add | 新增值班配置页面 |
|
||||
| /system/dutyConfig/edit/{id} | 编辑值班配置页面 |
|
||||
| /system/dutyConfig/save | 保存值班配置 |
|
||||
| /system/dutyConfig/update | 更新值班配置 |
|
||||
| /system/dutyConfig/remove/{ids} | 删除值班配置 |
|
||||
| /system/dutyConfig/changeStatus | 修改配置状态 |
|
||||
| /system/dutyConfig/getAvailableUsers | 获取可用用户列表 |
|
||||
| /system/dutyConfig/statistics | 分配统计页面 |
|
||||
|
||||
## 4. API定义
|
||||
|
||||
### 4.1 核心API
|
||||
|
||||
值班配置管理相关接口
|
||||
|
||||
```
|
||||
GET /system/dutyConfig/list
|
||||
```
|
||||
|
||||
请求参数:
|
||||
| 参数名称 | 参数类型 | 是否必填 | 描述 |
|
||||
|----------|----------|----------|------|
|
||||
| pageNum | int | false | 页码,默认1 |
|
||||
| pageSize | int | false | 每页大小,默认10 |
|
||||
| configName | string | false | 配置名称(模糊查询) |
|
||||
| status | string | false | 状态:0-禁用,1-启用 |
|
||||
|
||||
响应数据:
|
||||
| 参数名称 | 参数类型 | 描述 |
|
||||
|----------|----------|------|
|
||||
| code | int | 响应状态码 |
|
||||
| msg | string | 响应消息 |
|
||||
| rows | array | 配置列表数据 |
|
||||
| total | int | 总记录数 |
|
||||
|
||||
```
|
||||
POST /system/dutyConfig/save
|
||||
```
|
||||
|
||||
请求参数:
|
||||
| 参数名称 | 参数类型 | 是否必填 | 描述 |
|
||||
|----------|----------|----------|------|
|
||||
| configName | string | true | 配置名称 |
|
||||
| startTime | string | true | 开始时间(HH:mm格式) |
|
||||
| endTime | string | true | 结束时间(HH:mm格式) |
|
||||
| userIds | string | true | 值班用户ID列表,逗号分隔 |
|
||||
| status | string | true | 状态:0-禁用,1-启用 |
|
||||
| remark | string | false | 备注信息 |
|
||||
|
||||
响应数据:
|
||||
| 参数名称 | 参数类型 | 描述 |
|
||||
|----------|----------|------|
|
||||
| code | int | 响应状态码 |
|
||||
| msg | string | 响应消息 |
|
||||
|
||||
示例请求:
|
||||
```json
|
||||
{
|
||||
"configName": "工作日白班",
|
||||
"startTime": "09:00",
|
||||
"endTime": "18:00",
|
||||
"userIds": "1,2,3,4",
|
||||
"status": "1",
|
||||
"remark": "工作日白班值班安排"
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
GET /system/dutyConfig/getAvailableUsers
|
||||
```
|
||||
|
||||
响应数据:
|
||||
| 参数名称 | 参数类型 | 描述 |
|
||||
|----------|----------|------|
|
||||
| code | int | 响应状态码 |
|
||||
| data | array | 可用用户列表 |
|
||||
|
||||
用户对象结构:
|
||||
| 参数名称 | 参数类型 | 描述 |
|
||||
|----------|----------|------|
|
||||
| userId | long | 用户ID |
|
||||
| userName | string | 用户姓名 |
|
||||
| loginName | string | 登录名 |
|
||||
| status | string | 用户状态 |
|
||||
|
||||
## 5. 服务器架构图
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Controller层] --> B[Service层]
|
||||
B --> C[Mapper层]
|
||||
C --> D[(MySQL数据库)]
|
||||
|
||||
B --> E[分配算法服务]
|
||||
B --> F[缓存服务]
|
||||
E --> G[轮询分配器]
|
||||
E --> H[时间匹配器]
|
||||
|
||||
subgraph "Web层"
|
||||
A
|
||||
end
|
||||
|
||||
subgraph "业务层"
|
||||
B
|
||||
E
|
||||
F
|
||||
end
|
||||
|
||||
subgraph "数据访问层"
|
||||
C
|
||||
end
|
||||
|
||||
subgraph "算法组件"
|
||||
G
|
||||
H
|
||||
end
|
||||
```
|
||||
|
||||
## 6. 数据模型
|
||||
|
||||
### 6.1 数据模型定义
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
DUTY_CONFIG ||--o{ DUTY_CONFIG_USER : contains
|
||||
SYS_USER ||--o{ DUTY_CONFIG_USER : assigned
|
||||
DUTY_CONFIG ||--o{ CLEW_ASSIGN_LOG : generates
|
||||
SYS_USER ||--o{ CLEW_ASSIGN_LOG : receives
|
||||
|
||||
DUTY_CONFIG {
|
||||
bigint id PK
|
||||
string config_name
|
||||
string start_time
|
||||
string end_time
|
||||
string status
|
||||
string remark
|
||||
datetime create_time
|
||||
datetime update_time
|
||||
string create_by
|
||||
string update_by
|
||||
}
|
||||
|
||||
DUTY_CONFIG_USER {
|
||||
bigint id PK
|
||||
bigint config_id FK
|
||||
bigint user_id FK
|
||||
int sort_order
|
||||
datetime create_time
|
||||
}
|
||||
|
||||
SYS_USER {
|
||||
bigint user_id PK
|
||||
string user_name
|
||||
string login_name
|
||||
string status
|
||||
datetime create_time
|
||||
}
|
||||
|
||||
CLEW_ASSIGN_LOG {
|
||||
bigint id PK
|
||||
bigint clew_id
|
||||
bigint config_id FK
|
||||
bigint assigned_user_id FK
|
||||
datetime assign_time
|
||||
string assign_type
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 数据定义语言
|
||||
|
||||
值班配置表(duty_config)
|
||||
```sql
|
||||
-- 创建值班配置表
|
||||
CREATE TABLE duty_config (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
|
||||
config_name VARCHAR(100) NOT NULL COMMENT '配置名称',
|
||||
start_time VARCHAR(5) NOT NULL COMMENT '开始时间(HH:mm)',
|
||||
end_time VARCHAR(5) NOT NULL COMMENT '结束时间(HH:mm)',
|
||||
status CHAR(1) DEFAULT '1' COMMENT '状态(0-禁用,1-启用)',
|
||||
remark VARCHAR(500) COMMENT '备注',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
create_by VARCHAR(64) COMMENT '创建者',
|
||||
update_by VARCHAR(64) COMMENT '更新者',
|
||||
del_flag CHAR(1) DEFAULT '0' COMMENT '删除标志(0-正常,1-删除)'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='值班配置表';
|
||||
|
||||
-- 创建值班配置用户关联表
|
||||
CREATE TABLE duty_config_user (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
|
||||
config_id BIGINT NOT NULL COMMENT '配置ID',
|
||||
user_id BIGINT NOT NULL COMMENT '用户ID',
|
||||
sort_order INT DEFAULT 0 COMMENT '排序顺序',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
INDEX idx_config_id (config_id),
|
||||
INDEX idx_user_id (user_id),
|
||||
FOREIGN KEY (config_id) REFERENCES duty_config(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='值班配置用户关联表';
|
||||
|
||||
-- 创建线索分配日志表
|
||||
CREATE TABLE clew_assign_log (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
|
||||
clew_id BIGINT COMMENT '线索ID',
|
||||
config_id BIGINT COMMENT '使用的配置ID',
|
||||
assigned_user_id BIGINT NOT NULL COMMENT '分配的用户ID',
|
||||
assign_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '分配时间',
|
||||
assign_type VARCHAR(20) DEFAULT 'AUTO' COMMENT '分配类型(AUTO-自动,MANUAL-手动)',
|
||||
remark VARCHAR(200) COMMENT '备注',
|
||||
INDEX idx_clew_id (clew_id),
|
||||
INDEX idx_assigned_user (assigned_user_id),
|
||||
INDEX idx_assign_time (assign_time)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='线索分配日志表';
|
||||
|
||||
-- 创建索引
|
||||
CREATE INDEX idx_duty_config_time ON duty_config(start_time, end_time, status);
|
||||
CREATE INDEX idx_duty_config_status ON duty_config(status, del_flag);
|
||||
|
||||
-- 插入初始数据
|
||||
INSERT INTO duty_config (config_name, start_time, end_time,{"file_path": "/Users/clunt/java/RuoYi/.trae/documents/线索值班配置系统技术架构文档.md", "content":
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
# 线索值班配置系统需求文档
|
||||
|
||||
## 1. 产品概述
|
||||
|
||||
线索值班配置系统是基于若依框架开发的线索自动分配管理模块,旨在解决当前线索只能固定分配给特定用户的问题。通过配置值班时间段和值班人员,实现线索的智能轮流分配,提高线索处理效率和公平性。
|
||||
|
||||
- 解决线索分配不均衡的问题,提升销售团队工作效率
|
||||
- 支持灵活的时间段配置和多用户轮班管理
|
||||
- 为线索跟踪系统提供智能化的分配策略
|
||||
|
||||
## 2. 核心功能
|
||||
|
||||
### 2.1 用户角色
|
||||
|
||||
| 角色 | 权限说明 | 核心权限 |
|
||||
|------|----------|----------|
|
||||
| 系统管理员 | 完整的值班配置管理权限 | 可以创建、修改、删除值班配置,管理所有用户的值班安排 |
|
||||
| 部门主管 | 部门内值班配置管理 | 可以管理本部门用户的值班配置,查看分配统计 |
|
||||
| 普通销售 | 查看权限 | 可以查看自己的值班安排和线索分配情况 |
|
||||
|
||||
### 2.2 功能模块
|
||||
|
||||
本系统包含以下核心页面:
|
||||
|
||||
1. **值班配置管理页面**:值班规则列表、配置表单、状态管理
|
||||
2. **值班人员管理页面**:人员选择、轮班顺序设置、人员状态管理
|
||||
3. **线索分配统计页面**:分配记录查看、统计报表、分配日志
|
||||
|
||||
### 2.3 页面详情
|
||||
|
||||
| 页面名称 | 模块名称 | 功能描述 |
|
||||
|----------|----------|----------|
|
||||
| 值班配置管理页面 | 配置列表 | 显示所有值班配置,支持搜索、筛选、分页查看 |
|
||||
| 值班配置管理页面 | 新增配置 | 创建新的值班配置,设置时间段、选择值班人员、设置状态 |
|
||||
| 值班配置管理页面 | 编辑配置 | 修改现有配置的时间段、人员安排、启用状态 |
|
||||
| 值班配置管理页面 | 删除配置 | 删除不需要的值班配置,支持批量删除 |
|
||||
| 值班人员管理页面 | 人员选择 | 从sys_user表获取可用用户,支持多选和搜索 |
|
||||
| 值班人员管理页面 | 轮班设置 | 设置人员轮班顺序,支持拖拽排序 |
|
||||
| 线索分配统计页面 | 分配记录 | 查看线索分配历史记录,包括分配时间、分配人员、线索信息 |
|
||||
| 线索分配统计页面 | 统计报表 | 显示各时间段的分配统计,人员工作量分析 |
|
||||
|
||||
## 3. 核心流程
|
||||
|
||||
### 管理员配置流程
|
||||
1. 管理员登录系统 → 进入值班配置管理页面
|
||||
2. 创建新的值班配置 → 设置时间段(开始时间-结束时间,精确到分钟)
|
||||
3. 选择值班人员 → 从可用用户列表中多选值班人员
|
||||
4. 设置配置状态 → 启用或禁用该配置
|
||||
5. 保存配置 → 系统验证配置有效性并保存
|
||||
|
||||
### 线索自动分配流程
|
||||
1. APP端提交新线索 → 调用appAddSave接口
|
||||
2. 系统获取当前时间 → 查询匹配的值班配置
|
||||
3. 获取当前值班人员列表 → 按轮流规则选择下一个分配人员
|
||||
4. 分配线索给选中人员 → 更新线索记录和分配日志
|
||||
5. 返回分配结果 → 记录分配统计信息
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[APP提交线索] --> B[获取当前时间]
|
||||
B --> C[查询值班配置]
|
||||
C --> D{是否有匹配配置?}
|
||||
D -->|是| E[获取值班人员列表]
|
||||
D -->|否| F[分配给默认用户]
|
||||
E --> G[按轮流规则选择人员]
|
||||
G --> H[分配线索]
|
||||
H --> I[记录分配日志]
|
||||
F --> I
|
||||
I --> J[返回结果]
|
||||
```
|
||||
|
||||
## 4. 用户界面设计
|
||||
|
||||
### 4.1 设计风格
|
||||
- **主色调**:#409EFF(蓝色)、#67C23A(绿色)
|
||||
- **辅助色**:#E6A23C(橙色)、#F56C6C(红色)
|
||||
- **按钮样式**:圆角按钮,支持悬停效果
|
||||
- **字体**:微软雅黑,主要字号14px,标题16px
|
||||
- **布局风格**:卡片式布局,顶部导航栏
|
||||
- **图标风格**:使用FontAwesome图标库,简洁现代
|
||||
|
||||
### 4.2 页面设计概览
|
||||
|
||||
| 页面名称 | 模块名称 | UI元素 |
|
||||
|----------|----------|---------|
|
||||
| 值班配置管理页面 | 配置列表 | 表格布局,蓝色标题栏,白色背景,分页组件,搜索框使用圆角设计 |
|
||||
| 值班配置管理页面 | 配置表单 | 模态框弹窗,表单使用栅格布局,时间选择器精确到分钟,多选下拉框 |
|
||||
| 值班人员管理页面 | 人员选择 | 穿梭框组件,左侧可选人员,右侧已选人员,支持搜索和批量操作 |
|
||||
| 线索分配统计页面 | 统计图表 | ECharts图表组件,柱状图和饼图展示,响应式设计 |
|
||||
|
||||
### 4.3 响应式设计
|
||||
- 采用桌面优先设计,支持1920x1080及以上分辨率
|
||||
- 表格在小屏幕下支持横向滚动
|
||||
- 表单在移动端自动调整为单列布局
|
||||
- 支持触摸操作优化,按钮间距适配手指点击
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"java.compile.nullAnalysis.mode": "automatic"
|
||||
}
|
||||
|
|
@ -7,8 +7,6 @@ import java.util.stream.Collectors;
|
|||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.ruoyi.common.constant.UserConstants;
|
||||
import com.ruoyi.common.core.domain.entity.SysRole;
|
||||
import com.ruoyi.common.core.domain.entity.SysUser;
|
||||
import com.ruoyi.common.utils.DateUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
|
|
@ -19,6 +17,7 @@ import com.ruoyi.system.domain.CompanyApp;
|
|||
import com.ruoyi.system.domain.OppoCheck;
|
||||
import com.ruoyi.system.service.IClewPhoneService;
|
||||
import com.ruoyi.system.service.ICompanyAppService;
|
||||
import com.ruoyi.system.service.IDutyConfigService;
|
||||
import com.ruoyi.system.service.ISysUserService;
|
||||
import com.ruoyi.web.core.config.GlobalLogHelper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
|
@ -29,7 +28,6 @@ import okhttp3.Response;
|
|||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.apache.tomcat.jni.User;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.ModelMap;
|
||||
|
|
@ -71,6 +69,9 @@ public class ClewController extends BaseController
|
|||
@Autowired
|
||||
private ICompanyAppService companyAppService;
|
||||
|
||||
@Autowired
|
||||
private IDutyConfigService dutyConfigService;
|
||||
|
||||
@RequiresPermissions("system:clew:view")
|
||||
@GetMapping()
|
||||
public String clew(ModelMap modelMap)
|
||||
|
|
@ -140,7 +141,6 @@ public class ClewController extends BaseController
|
|||
@ResponseBody
|
||||
public TableDataInfo listPublic(Clew clew)
|
||||
{
|
||||
SysUser sysUser=getSysUser();
|
||||
startPage();
|
||||
clew.setSaleId(2L);
|
||||
clew.setPoolStatus("01"); // 只显示公海池状态的线索
|
||||
|
|
@ -252,8 +252,34 @@ public class ClewController extends BaseController
|
|||
return AjaxResult.success();
|
||||
}
|
||||
|
||||
// 根据值班配置自动分配客服
|
||||
Long assignedUserId = null;
|
||||
try {
|
||||
assignedUserId = dutyConfigService.getNextDutyUserId();
|
||||
if (assignedUserId != null) {
|
||||
clew.setSaleId(assignedUserId);
|
||||
clew.setSourceSaleId(assignedUserId); // 记录初始分配的销售
|
||||
clew.setAssignTimes("01"); // 标记为首次分配
|
||||
log.info("线索自动分配给值班用户, 手机号:{}, 用户ID:{}", clew.getPhone(), assignedUserId);
|
||||
} else {
|
||||
// 如果没有值班配置,使用默认逻辑(可以设置为公海或默认用户)
|
||||
clew.setSaleId(1L); // 默认分配给默认用户-保无忧
|
||||
clew.setPoolStatus("01"); // 设置为公海池状态
|
||||
log.info("未找到值班配置, 线索分配到公海, 手机号:{}", clew.getPhone());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("获取值班用户失败, 线索分配到公海, 手机号:{}, 错误:{}", clew.getPhone(), e.getMessage());
|
||||
clew.setSaleId(1L); // 异常时分配给公海
|
||||
clew.setPoolStatus("01"); // 设置为公海池状态
|
||||
}
|
||||
|
||||
// 不存在手机号则入库线索
|
||||
clewService.insertClew(clew);
|
||||
|
||||
// 如果分配给了具体客服,发送短信通知
|
||||
if (assignedUserId != null && assignedUserId != 2L) {
|
||||
sendSmsNotification(assignedUserId, clew);
|
||||
}
|
||||
|
||||
// 将初始线索设置成已跟踪
|
||||
ClewPhone model = new ClewPhone();
|
||||
|
|
@ -544,4 +570,49 @@ public class ClewController extends BaseController
|
|||
return toAjax(clewService.catchClewToQuality(sourceClew));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送短信通知给值班客服
|
||||
*/
|
||||
private void sendSmsNotification(Long userId, Clew clew) {
|
||||
try {
|
||||
// 查询客服手机号
|
||||
SysUser sysUser = sysUserService.selectUserById(userId);
|
||||
if (sysUser == null || StringUtils.isEmpty(sysUser.getLoginName())) {
|
||||
log.warn("未找到用户信息或手机号为空, 用户ID:{}", userId);
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否是手机号(长度大于10位)
|
||||
boolean isPhone = sysUser.getLoginName().length() > 10;
|
||||
if (!isPhone) {
|
||||
log.info("用户登录名不是手机号,跳过短信通知, 用户ID:{}, 登录名:{}", userId, sysUser.getLoginName());
|
||||
return;
|
||||
}
|
||||
|
||||
// 排除特定来源(sourceApp == 8)
|
||||
if (ObjectUtil.equals(clew.getSourceApp(), 8L)) {
|
||||
log.info("线索来源为8,跳过短信通知, 用户ID:{}", userId);
|
||||
return;
|
||||
}
|
||||
|
||||
JSONObject smsContent = new JSONObject();
|
||||
smsContent.put("uid", "12347");
|
||||
smsContent.put("pwd", "wJgzaC0u");
|
||||
smsContent.put("mobile", sysUser.getLoginName());
|
||||
|
||||
// 根据来源设置短信内容
|
||||
if (clew.getSourceApp() != null && clew.getSourceApp() == 2L) {
|
||||
smsContent.put("content", "【值班分配】你有一条来自大象,新线索生成,客户电话:" + clew.getPhone() + "(请注意及时跟进)");
|
||||
} else {
|
||||
smsContent.put("content", "【值班分配】你有一条来自黑猫,新线索生成,客户电话:" + clew.getPhone() + "(请注意及时跟进)");
|
||||
}
|
||||
|
||||
log.info("值班分配短信通知, 用户ID:{}, 手机号:{}, 请求参数:{}", userId, sysUser.getLoginName(), smsContent.toJSONString());
|
||||
String response = HttpUtils.sendPost("http://www.aozhongyun.com/Admin/index.php/Message/send", smsContent.toJSONString());
|
||||
log.info("值班分配短信通知响应:{}", response);
|
||||
} catch (Exception e) {
|
||||
log.error("发送值班分配短信通知失败, 用户ID:{}, 错误:{}", userId, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,253 @@
|
|||
package com.ruoyi.web.controller.system;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.system.domain.DutyConfig;
|
||||
import com.ruoyi.system.service.IDutyConfigService;
|
||||
import com.ruoyi.system.service.ISysUserService;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.core.domain.entity.SysUser;
|
||||
|
||||
/**
|
||||
* 值班配置Controller
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2024-12-19
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping("/system/dutyConfig")
|
||||
public class DutyConfigController extends BaseController
|
||||
{
|
||||
private String prefix = "system/dutyConfig";
|
||||
|
||||
@Autowired
|
||||
private IDutyConfigService dutyConfigService;
|
||||
|
||||
@Autowired
|
||||
private ISysUserService userService;
|
||||
|
||||
@RequiresPermissions("system:dutyConfig:view")
|
||||
@GetMapping()
|
||||
public String dutyConfig()
|
||||
{
|
||||
return prefix + "/dutyConfig";
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询值班配置列表
|
||||
*/
|
||||
@RequiresPermissions("system:dutyConfig:list")
|
||||
@PostMapping("/list")
|
||||
@ResponseBody
|
||||
public TableDataInfo list(DutyConfig dutyConfig)
|
||||
{
|
||||
startPage();
|
||||
List<DutyConfig> list = dutyConfigService.selectDutyConfigList(dutyConfig);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出值班配置列表
|
||||
*/
|
||||
@RequiresPermissions("system:dutyConfig:export")
|
||||
@Log(title = "值班配置", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
@ResponseBody
|
||||
public AjaxResult export(DutyConfig dutyConfig)
|
||||
{
|
||||
List<DutyConfig> list = dutyConfigService.selectDutyConfigList(dutyConfig);
|
||||
ExcelUtil<DutyConfig> util = new ExcelUtil<DutyConfig>(DutyConfig.class);
|
||||
return util.exportExcel(list, "值班配置数据");
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增值班配置
|
||||
*/
|
||||
@GetMapping("/add")
|
||||
public String add(ModelMap mmap)
|
||||
{
|
||||
// 获取所有可用用户
|
||||
SysUser user = new SysUser();
|
||||
user.setStatus("0"); // 正常状态
|
||||
List<SysUser> users = userService.selectUserList(user);
|
||||
mmap.put("users", users);
|
||||
return prefix + "/add";
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增保存值班配置
|
||||
*/
|
||||
@RequiresPermissions("system:dutyConfig:add")
|
||||
@Log(title = "值班配置", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/add")
|
||||
@ResponseBody
|
||||
public AjaxResult addSave(DutyConfig dutyConfig, @RequestParam("dutyUserIds") String dutyUserIds)
|
||||
{
|
||||
// 验证时间范围
|
||||
if (dutyConfig.getStartTime() == null || dutyConfig.getEndTime() == null) {
|
||||
return error("开始时间和结束时间不能为空");
|
||||
}
|
||||
|
||||
if (dutyConfig.getStartTime().compareTo(dutyConfig.getEndTime()) >= 0) {
|
||||
return error("开始时间必须早于结束时间");
|
||||
}
|
||||
|
||||
// 检查时间段是否冲突
|
||||
if (dutyConfigService.checkTimeConflict(dutyConfig.getStartTime(), dutyConfig.getEndTime(), null)) {
|
||||
return error("该时间段与现有配置冲突,请重新选择时间");
|
||||
}
|
||||
|
||||
// 验证值班用户
|
||||
if (dutyUserIds == null || dutyUserIds.trim().isEmpty()) {
|
||||
return error("值班用户不能为空");
|
||||
}
|
||||
|
||||
dutyConfig.setCreateBy(getLoginName());
|
||||
// Service层会处理dutyUserIds参数
|
||||
return toAjax(dutyConfigService.insertDutyConfig(dutyConfig, dutyUserIds));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改值班配置
|
||||
*/
|
||||
@GetMapping("/edit/{id}")
|
||||
public String edit(@PathVariable("id") Long id, ModelMap mmap)
|
||||
{
|
||||
DutyConfig dutyConfig = dutyConfigService.selectDutyConfigById(id);
|
||||
mmap.put("dutyConfig", dutyConfig);
|
||||
|
||||
// 获取所有可用用户
|
||||
SysUser user = new SysUser();
|
||||
user.setStatus("0"); // 正常状态
|
||||
List<SysUser> users = userService.selectUserList(user);
|
||||
mmap.put("users", users);
|
||||
|
||||
return prefix + "/edit";
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改保存值班配置
|
||||
*/
|
||||
@RequiresPermissions("system:dutyConfig:edit")
|
||||
@Log(title = "值班配置", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/edit")
|
||||
@ResponseBody
|
||||
public AjaxResult editSave(DutyConfig dutyConfig, @RequestParam("dutyUserIds") String dutyUserIds)
|
||||
{
|
||||
// 验证时间范围
|
||||
if (dutyConfig.getStartTime() == null || dutyConfig.getEndTime() == null) {
|
||||
return error("开始时间和结束时间不能为空");
|
||||
}
|
||||
|
||||
if (dutyConfig.getStartTime().compareTo(dutyConfig.getEndTime()) >= 0) {
|
||||
return error("开始时间必须早于结束时间");
|
||||
}
|
||||
|
||||
// 检查时间段是否冲突(排除自己)
|
||||
if (dutyConfigService.checkTimeConflict(dutyConfig.getStartTime(), dutyConfig.getEndTime(), dutyConfig.getId())) {
|
||||
return error("该时间段与现有配置冲突,请重新选择时间");
|
||||
}
|
||||
|
||||
// 验证值班用户
|
||||
if (dutyUserIds == null || dutyUserIds.trim().isEmpty()) {
|
||||
return error("值班用户不能为空");
|
||||
}
|
||||
|
||||
dutyConfig.setUpdateBy(getLoginName());
|
||||
// Service层会处理dutyUserIds参数
|
||||
return toAjax(dutyConfigService.updateDutyConfig(dutyConfig, dutyUserIds));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除值班配置
|
||||
*/
|
||||
@RequiresPermissions("system:dutyConfig:remove")
|
||||
@Log(title = "值班配置", businessType = BusinessType.DELETE)
|
||||
@PostMapping( "/remove")
|
||||
@ResponseBody
|
||||
public AjaxResult remove(String ids)
|
||||
{
|
||||
return toAjax(dutyConfigService.deleteDutyConfigByIds(ids));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改值班配置状态
|
||||
*/
|
||||
@RequiresPermissions("system:dutyConfig:edit")
|
||||
@Log(title = "值班配置", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/changeStatus")
|
||||
@ResponseBody
|
||||
public AjaxResult changeStatus(DutyConfig dutyConfig)
|
||||
{
|
||||
dutyConfig.setUpdateBy(getLoginName());
|
||||
return toAjax(dutyConfigService.changeStatus(dutyConfig));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可用用户列表
|
||||
*/
|
||||
@GetMapping("/getAvailableUsers")
|
||||
@ResponseBody
|
||||
public AjaxResult getAvailableUsers()
|
||||
{
|
||||
SysUser user = new SysUser();
|
||||
user.setStatus("0"); // 正常状态
|
||||
List<SysUser> users = userService.selectUserList(user);
|
||||
return AjaxResult.success(users);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前值班用户
|
||||
*/
|
||||
@GetMapping("/getCurrentDutyUser")
|
||||
@ResponseBody
|
||||
public AjaxResult getCurrentDutyUser()
|
||||
{
|
||||
Long dutyUserId = dutyConfigService.getNextDutyUserId();
|
||||
if (dutyUserId != null) {
|
||||
SysUser user = userService.selectUserById(dutyUserId);
|
||||
return AjaxResult.success(user);
|
||||
}
|
||||
return AjaxResult.error("当前没有值班配置");
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看值班配置详情
|
||||
*/
|
||||
@RequiresPermissions("system:dutyConfig:view")
|
||||
@GetMapping("/detail/{id}")
|
||||
public String detail(@PathVariable("id") Long id, ModelMap mmap)
|
||||
{
|
||||
DutyConfig dutyConfig = dutyConfigService.selectDutyConfigById(id);
|
||||
|
||||
// 设置是否在当前时间范围内
|
||||
if (dutyConfig != null) {
|
||||
Date now = new Date();
|
||||
if ("1".equals(dutyConfig.getStatus()) &&
|
||||
dutyConfig.getStartTime() != null && dutyConfig.getEndTime() != null) {
|
||||
dutyConfig.setIsActive(now.compareTo(dutyConfig.getStartTime()) >= 0 &&
|
||||
now.compareTo(dutyConfig.getEndTime()) <= 0);
|
||||
} else {
|
||||
dutyConfig.setIsActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
mmap.put("dutyConfig", dutyConfig);
|
||||
return prefix + "/detail";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,194 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
|
||||
<head>
|
||||
<th:block th:include="include :: header('新增值班配置')" />
|
||||
<th:block th:include="include :: select2-css" />
|
||||
<th:block th:include="include :: datetimepicker-css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="main-content">
|
||||
<form id="form-dutyConfig-add" class="form-horizontal">
|
||||
<h4 class="form-header h4">基本信息</h4>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label is-required">配置名称:</label>
|
||||
<div class="col-sm-8">
|
||||
<input name="configName" placeholder="请输入配置名称" class="form-control" type="text" maxlength="100" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">状态:</label>
|
||||
<div class="col-sm-8">
|
||||
<label class="toggle-switch switch-solid">
|
||||
<input type="checkbox" id="status" checked>
|
||||
<span></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label is-required">开始时间:</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group date">
|
||||
<input name="startTime" class="form-control" placeholder="yyyy-MM-dd HH:mm:ss" type="text" required>
|
||||
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label is-required">结束时间:</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group date">
|
||||
<input name="endTime" class="form-control" placeholder="yyyy-MM-dd HH:mm:ss" type="text" required>
|
||||
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="form-group">
|
||||
<label class="col-xs-2 control-label is-required">值班人员:</label>
|
||||
<div class="col-xs-10">
|
||||
<select id="dutyUserIds" name="dutyUserIds" class="form-control select2-multiple" multiple required>
|
||||
<option th:each="user:${users}" th:value="${user.userId}" th:text="${user.userName}" th:disabled="${user.status == '1'}"></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="form-group">
|
||||
<label class="col-xs-2 control-label">备注:</label>
|
||||
<div class="col-xs-10">
|
||||
<textarea name="remark" maxlength="500" class="form-control" rows="3" placeholder="请输入备注信息"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-offset-5 col-sm-10">
|
||||
<button type="button" class="btn btn-sm btn-primary" onclick="submitHandler()"><i class="fa fa-check"></i>保 存</button>
|
||||
<button type="button" class="btn btn-sm btn-danger" onclick="closeItem()"><i class="fa fa-reply-all"></i>关 闭 </button>
|
||||
</div>
|
||||
</div>
|
||||
<th:block th:include="include :: footer" />
|
||||
<th:block th:include="include :: select2-js" />
|
||||
<th:block th:include="include :: datetimepicker-js" />
|
||||
<script>
|
||||
var prefix = ctx + "system/dutyConfig";
|
||||
|
||||
$("#form-dutyConfig-add").validate({
|
||||
onkeyup: false,
|
||||
rules:{
|
||||
configName:{
|
||||
required: true,
|
||||
minlength: 2,
|
||||
maxlength: 100
|
||||
},
|
||||
startTime:{
|
||||
required: true
|
||||
},
|
||||
endTime:{
|
||||
required: true
|
||||
},
|
||||
dutyUserIds:{
|
||||
required: true
|
||||
}
|
||||
},
|
||||
messages: {
|
||||
"configName": {
|
||||
required: "请输入配置名称",
|
||||
minlength: "配置名称不能小于2个字符",
|
||||
maxlength: "配置名称不能超过100个字符"
|
||||
},
|
||||
"startTime": {
|
||||
required: "请选择开始时间"
|
||||
},
|
||||
"endTime": {
|
||||
required: "请选择结束时间"
|
||||
},
|
||||
"dutyUserIds": {
|
||||
required: "请选择值班人员"
|
||||
}
|
||||
},
|
||||
focusCleanup: true
|
||||
});
|
||||
|
||||
function submitHandler() {
|
||||
if ($.validate.form()) {
|
||||
// 验证时间范围
|
||||
var startTime = $("input[name='startTime']").val();
|
||||
var endTime = $("input[name='endTime']").val();
|
||||
|
||||
if (startTime && endTime) {
|
||||
var start = new Date(startTime);
|
||||
var end = new Date(endTime);
|
||||
|
||||
if (start >= end) {
|
||||
$.modal.alertWarning("开始时间必须早于结束时间");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理状态值(0停用 1启用)
|
||||
var status = $("#status").is(':checked') == true ? "1" : "0";
|
||||
|
||||
// 处理值班人员 - 转换为逗号分隔的字符串
|
||||
var dutyUserIds = $("#dutyUserIds").val();
|
||||
if (dutyUserIds && dutyUserIds.length > 0) {
|
||||
dutyUserIds = dutyUserIds.join(',');
|
||||
} else {
|
||||
dutyUserIds = '';
|
||||
}
|
||||
|
||||
// 移除表单中的 dutyUserIds(避免重复提交)
|
||||
var data = $("#form-dutyConfig-add").serializeArray().filter(function(item) {
|
||||
return item.name !== 'dutyUserIds';
|
||||
});
|
||||
|
||||
data.push({"name": "status", "value": status});
|
||||
data.push({"name": "dutyUserIds", "value": dutyUserIds});
|
||||
|
||||
$.operate.saveTab(prefix + "/add", data);
|
||||
}
|
||||
}
|
||||
|
||||
$(function() {
|
||||
// 初始化Select2
|
||||
$('.select2-multiple').select2({
|
||||
placeholder: "请选择值班人员",
|
||||
allowClear: true
|
||||
});
|
||||
|
||||
// 初始化日期时间选择器
|
||||
$(".form-control[name='startTime']").datetimepicker({
|
||||
format: "yyyy-mm-dd hh:ii:ss",
|
||||
autoclose: true,
|
||||
todayBtn: true,
|
||||
language: 'zh-CN'
|
||||
});
|
||||
|
||||
$(".form-control[name='endTime']").datetimepicker({
|
||||
format: "yyyy-mm-dd hh:ii:ss",
|
||||
autoclose: true,
|
||||
todayBtn: true,
|
||||
language: 'zh-CN'
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
|
||||
<head>
|
||||
<th:block th:include="include :: header('值班配置详情')" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="main-content">
|
||||
<form class="form-horizontal">
|
||||
<h4 class="form-header h4">基本信息</h4>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">配置名称:</label>
|
||||
<div class="col-sm-8">
|
||||
<p class="form-control-static" th:text="${dutyConfig.configName}"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">状态:</label>
|
||||
<div class="col-sm-8">
|
||||
<p class="form-control-static">
|
||||
<span th:if="${dutyConfig.status == '1'}" class="label label-success">启用</span>
|
||||
<span th:if="${dutyConfig.status == '0'}" class="label label-danger">停用</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">开始时间:</label>
|
||||
<div class="col-sm-8">
|
||||
<p class="form-control-static" th:text="${#dates.format(dutyConfig.startTime, 'yyyy-MM-dd HH:mm:ss')}"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">结束时间:</label>
|
||||
<div class="col-sm-8">
|
||||
<p class="form-control-static" th:text="${#dates.format(dutyConfig.endTime, 'yyyy-MM-dd HH:mm:ss')}"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">是否生效:</label>
|
||||
<div class="col-sm-8">
|
||||
<p class="form-control-static">
|
||||
<span th:if="${dutyConfig.isActive}" class="label label-success">生效中</span>
|
||||
<span th:if="${!dutyConfig.isActive}" class="label label-default">未生效</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="form-group">
|
||||
<label class="col-xs-2 control-label">值班人员:</label>
|
||||
<div class="col-xs-10">
|
||||
<p class="form-control-static" th:text="${dutyConfig.dutyUserNames}"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="form-group">
|
||||
<label class="col-xs-2 control-label">备注:</label>
|
||||
<div class="col-xs-10">
|
||||
<p class="form-control-static" th:text="${dutyConfig.remark}"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h4 class="form-header h4">其他信息</h4>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">创建者:</label>
|
||||
<div class="col-sm-8">
|
||||
<p class="form-control-static" th:text="${dutyConfig.createBy}"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">创建时间:</label>
|
||||
<div class="col-sm-8">
|
||||
<p class="form-control-static" th:text="${dutyConfig.createTime != null ? #dates.format(dutyConfig.createTime, 'yyyy-MM-dd HH:mm:ss') : ''}"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">更新者:</label>
|
||||
<div class="col-sm-8">
|
||||
<p class="form-control-static" th:text="${dutyConfig.updateBy}"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">更新时间:</label>
|
||||
<div class="col-sm-8">
|
||||
<p class="form-control-static" th:text="${dutyConfig.updateTime != null ? #dates.format(dutyConfig.updateTime, 'yyyy-MM-dd HH:mm:ss') : ''}"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-offset-5 col-sm-10">
|
||||
<button type="button" class="btn btn-sm btn-danger" onclick="closeItem()"><i class="fa fa-reply-all"></i>关 闭 </button>
|
||||
</div>
|
||||
</div>
|
||||
<th:block th:include="include :: footer" />
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
|
||||
<head>
|
||||
<th:block th:include="include :: header('值班配置列表')" />
|
||||
</head>
|
||||
<body class="gray-bg">
|
||||
<div class="container-div">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 search-collapse">
|
||||
<form id="dutyConfig-form">
|
||||
<div class="select-list">
|
||||
<ul>
|
||||
<li>
|
||||
配置名称:<input type="text" name="configName"/>
|
||||
</li>
|
||||
<li>
|
||||
状态:<select name="status" th:with="type=${@dict.getType('sys_normal_disable')}">
|
||||
<option value="">所有</option>
|
||||
<option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}"></option>
|
||||
</select>
|
||||
</li>
|
||||
<li class="select-time">
|
||||
<label>开始时间: </label>
|
||||
<input type="text" class="time-input" id="startTime" placeholder="开始时间" name="params[beginStartTime]"/>
|
||||
<span>-</span>
|
||||
<input type="text" class="time-input" id="endTime" placeholder="结束时间" name="params[endStartTime]"/>
|
||||
</li>
|
||||
<li>
|
||||
<a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()"><i class="fa fa-search"></i> 搜索</a>
|
||||
<a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i class="fa fa-refresh"></i> 重置</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="btn-group-sm" id="toolbar" role="group">
|
||||
<a class="btn btn-success" onclick="$.operate.addTab()" shiro:hasPermission="system:dutyConfig:add">
|
||||
<i class="fa fa-plus"></i> 新增
|
||||
</a>
|
||||
<a class="btn btn-primary single disabled" onclick="$.operate.editTab()" shiro:hasPermission="system:dutyConfig:edit">
|
||||
<i class="fa fa-edit"></i> 修改
|
||||
</a>
|
||||
<a class="btn btn-danger multiple disabled" onclick="$.operate.removeAll()" shiro:hasPermission="system:dutyConfig:remove">
|
||||
<i class="fa fa-remove"></i> 删除
|
||||
</a>
|
||||
<a class="btn btn-warning" onclick="$.table.exportExcel()" shiro:hasPermission="system:dutyConfig:export">
|
||||
<i class="fa fa-download"></i> 导出
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 select-table table-striped">
|
||||
<table id="bootstrap-table"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<th:block th:include="include :: footer" />
|
||||
<script th:inline="javascript">
|
||||
var editFlag = [[${@permission.hasPermi('system:dutyConfig:edit')}]];
|
||||
var removeFlag = [[${@permission.hasPermi('system:dutyConfig:remove')}]];
|
||||
var prefix = ctx + "system/dutyConfig";
|
||||
|
||||
$(function() {
|
||||
var options = {
|
||||
url: prefix + "/list",
|
||||
createUrl: prefix + "/add",
|
||||
updateUrl: prefix + "/edit/{id}",
|
||||
removeUrl: prefix + "/remove",
|
||||
exportUrl: prefix + "/export",
|
||||
sortName: "createTime",
|
||||
sortOrder: "desc",
|
||||
modalName: "值班配置",
|
||||
columns: [{
|
||||
checkbox: true
|
||||
},
|
||||
{
|
||||
field: 'id',
|
||||
title: '配置ID',
|
||||
visible: false
|
||||
},
|
||||
{
|
||||
field: 'configName',
|
||||
title: '配置名称',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
field: 'startTime',
|
||||
title: '开始时间',
|
||||
sortable: true,
|
||||
formatter: function(value, row, index) {
|
||||
return $.table.tooltip(value);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'endTime',
|
||||
title: '结束时间',
|
||||
sortable: true,
|
||||
formatter: function(value, row, index) {
|
||||
return $.table.tooltip(value);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'dutyUserNames',
|
||||
title: '值班人员',
|
||||
formatter: function(value, row, index) {
|
||||
if (value && value.length > 20) {
|
||||
return '<span title="' + value + '">' + value.substring(0, 20) + '...</span>';
|
||||
}
|
||||
return value || '';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'isActive',
|
||||
title: '是否生效',
|
||||
align: 'center',
|
||||
formatter: function(value, row, index) {
|
||||
if (value) {
|
||||
return '<span class="label label-success">生效中</span>';
|
||||
} else {
|
||||
return '<span class="label label-default">未生效</span>';
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
visible: editFlag == 'hidden' ? false : true,
|
||||
title: '状态',
|
||||
align: 'center',
|
||||
formatter: function (value, row, index) {
|
||||
return statusTools(row);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
align: 'center',
|
||||
formatter: function(value, row, index) {
|
||||
var actions = [];
|
||||
actions.push('<a class="btn btn-success btn-xs ' + editFlag + '" href="javascript:void(0)" onclick="$.operate.editTab(\'' + row.id + '\')"><i class="fa fa-edit"></i>编辑</a> ');
|
||||
actions.push('<a class="btn btn-info btn-xs" href="javascript:void(0)" onclick="detail(\'' + row.id + '\')"><i class="fa fa-eye"></i>详情</a> ');
|
||||
actions.push('<a class="btn btn-danger btn-xs ' + removeFlag + '" href="javascript:void(0)" onclick="$.operate.remove(\'' + row.id + '\')"><i class="fa fa-remove"></i>删除</a>');
|
||||
return actions.join('');
|
||||
}
|
||||
}]
|
||||
};
|
||||
$.table.init(options);
|
||||
});
|
||||
|
||||
/* 值班配置状态显示 */
|
||||
function statusTools(row) {
|
||||
if (row.status == '0') {
|
||||
return '<i class=\"fa fa-toggle-off text-info fa-2x\" onclick="enable(\'' + row.id + '\')"></i> ';
|
||||
} else {
|
||||
return '<i class=\"fa fa-toggle-on text-info fa-2x\" onclick="disable(\'' + row.id + '\')"></i> ';
|
||||
}
|
||||
}
|
||||
|
||||
/* 值班配置管理-停用 */
|
||||
function disable(id) {
|
||||
$.modal.confirm("确认要停用该值班配置吗?", function() {
|
||||
$.operate.post(prefix + "/changeStatus", { "id": id, "status": "0" });
|
||||
})
|
||||
}
|
||||
|
||||
/* 值班配置管理-启用 */
|
||||
function enable(id) {
|
||||
$.modal.confirm("确认要启用该值班配置吗?", function() {
|
||||
$.operate.post(prefix + "/changeStatus", { "id": id, "status": "1" });
|
||||
})
|
||||
}
|
||||
|
||||
/* 详情 */
|
||||
function detail(id) {
|
||||
var url = prefix + '/detail/' + id;
|
||||
$.modal.openTab("值班配置详情", url);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
|
||||
<head>
|
||||
<th:block th:include="include :: header('修改值班配置')" />
|
||||
<th:block th:include="include :: select2-css" />
|
||||
<th:block th:include="include :: datetimepicker-css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="main-content">
|
||||
<form id="form-dutyConfig-edit" class="form-horizontal">
|
||||
<input name="id" th:value="${dutyConfig.id}" type="hidden">
|
||||
<h4 class="form-header h4">基本信息</h4>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label is-required">配置名称:</label>
|
||||
<div class="col-sm-8">
|
||||
<input name="configName" th:value="${dutyConfig.configName}" placeholder="请输入配置名称" class="form-control" type="text" maxlength="100" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">状态:</label>
|
||||
<div class="col-sm-8">
|
||||
<label class="toggle-switch switch-solid">
|
||||
<input type="checkbox" id="status" th:checked="${dutyConfig.status == '1'}">
|
||||
<span></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label is-required">开始时间:</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group date">
|
||||
<input name="startTime" th:value="${#dates.format(dutyConfig.startTime, 'yyyy-MM-dd HH:mm:ss')}" class="form-control" placeholder="yyyy-MM-dd HH:mm:ss" type="text" required>
|
||||
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label is-required">结束时间:</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group date">
|
||||
<input name="endTime" th:value="${#dates.format(dutyConfig.endTime, 'yyyy-MM-dd HH:mm:ss')}" class="form-control" placeholder="yyyy-MM-dd HH:mm:ss" type="text" required>
|
||||
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="form-group">
|
||||
<label class="col-xs-2 control-label is-required">值班人员:</label>
|
||||
<div class="col-xs-10">
|
||||
<select id="dutyUserIds" name="dutyUserIds" class="form-control select2-multiple" multiple required>
|
||||
<option th:each="user:${users}" th:value="${user.userId}" th:text="${user.userName}"
|
||||
th:disabled="${user.status == '1'}"
|
||||
th:selected="${dutyConfig.dutyUsers != null and #lists.contains(dutyConfig.dutyUsers.![userId], user.userId)}"></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="form-group">
|
||||
<label class="col-xs-2 control-label">备注:</label>
|
||||
<div class="col-xs-10">
|
||||
<textarea name="remark" th:text="${dutyConfig.remark}" maxlength="500" class="form-control" rows="3" placeholder="请输入备注信息"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-offset-5 col-sm-10">
|
||||
<button type="button" class="btn btn-sm btn-primary" onclick="submitHandler()"><i class="fa fa-check"></i>保 存</button>
|
||||
<button type="button" class="btn btn-sm btn-danger" onclick="closeItem()"><i class="fa fa-reply-all"></i>关 闭 </button>
|
||||
</div>
|
||||
</div>
|
||||
<th:block th:include="include :: footer" />
|
||||
<th:block th:include="include :: select2-js" />
|
||||
<th:block th:include="include :: datetimepicker-js" />
|
||||
<script>
|
||||
var prefix = ctx + "system/dutyConfig";
|
||||
|
||||
$("#form-dutyConfig-edit").validate({
|
||||
onkeyup: false,
|
||||
rules:{
|
||||
configName:{
|
||||
required: true,
|
||||
minlength: 2,
|
||||
maxlength: 100
|
||||
},
|
||||
startTime:{
|
||||
required: true
|
||||
},
|
||||
endTime:{
|
||||
required: true
|
||||
},
|
||||
dutyUserIds:{
|
||||
required: true
|
||||
}
|
||||
},
|
||||
messages: {
|
||||
"configName": {
|
||||
required: "请输入配置名称",
|
||||
minlength: "配置名称不能小于2个字符",
|
||||
maxlength: "配置名称不能超过100个字符"
|
||||
},
|
||||
"startTime": {
|
||||
required: "请选择开始时间"
|
||||
},
|
||||
"endTime": {
|
||||
required: "请选择结束时间"
|
||||
},
|
||||
"dutyUserIds": {
|
||||
required: "请选择值班人员"
|
||||
}
|
||||
},
|
||||
focusCleanup: true
|
||||
});
|
||||
|
||||
function submitHandler() {
|
||||
if ($.validate.form()) {
|
||||
// 验证时间范围
|
||||
var startTime = $("input[name='startTime']").val();
|
||||
var endTime = $("input[name='endTime']").val();
|
||||
|
||||
if (startTime && endTime) {
|
||||
var start = new Date(startTime);
|
||||
var end = new Date(endTime);
|
||||
|
||||
if (start >= end) {
|
||||
$.modal.alertWarning("开始时间必须早于结束时间");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理状态值(0停用 1启用)
|
||||
var status = $("#status").is(':checked') == true ? "1" : "0";
|
||||
|
||||
// 处理值班人员 - 转换为逗号分隔的字符串
|
||||
var dutyUserIds = $("#dutyUserIds").val();
|
||||
if (dutyUserIds && dutyUserIds.length > 0) {
|
||||
dutyUserIds = dutyUserIds.join(',');
|
||||
} else {
|
||||
dutyUserIds = '';
|
||||
}
|
||||
|
||||
// 移除表单中的 dutyUserIds(避免重复提交)
|
||||
var data = $("#form-dutyConfig-edit").serializeArray().filter(function(item) {
|
||||
return item.name !== 'dutyUserIds';
|
||||
});
|
||||
|
||||
data.push({"name": "status", "value": status});
|
||||
data.push({"name": "dutyUserIds", "value": dutyUserIds});
|
||||
|
||||
$.operate.saveTab(prefix + "/edit", data);
|
||||
}
|
||||
}
|
||||
|
||||
$(function() {
|
||||
// 初始化Select2
|
||||
$('.select2-multiple').select2({
|
||||
placeholder: "请选择值班人员",
|
||||
allowClear: true
|
||||
});
|
||||
|
||||
// 初始化日期时间选择器
|
||||
$(".form-control[name='startTime']").datetimepicker({
|
||||
format: "yyyy-mm-dd hh:ii:ss",
|
||||
autoclose: true,
|
||||
todayBtn: true,
|
||||
language: 'zh-CN'
|
||||
});
|
||||
|
||||
$(".form-control[name='endTime']").datetimepicker({
|
||||
format: "yyyy-mm-dd hh:ii:ss",
|
||||
autoclose: true,
|
||||
todayBtn: true,
|
||||
language: 'zh-CN'
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
package com.ruoyi.system.domain;
|
||||
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import com.ruoyi.common.annotation.Excel;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
|
||||
/**
|
||||
* 线索分配日志对象 clew_assign_log
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2024-12-19
|
||||
*/
|
||||
@Data
|
||||
public class ClewAssignLog extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 主键ID */
|
||||
private Long id;
|
||||
|
||||
/** 线索ID */
|
||||
@Excel(name = "线索ID")
|
||||
private Long clewId;
|
||||
|
||||
/** 使用的配置ID */
|
||||
@Excel(name = "配置ID")
|
||||
private Long configId;
|
||||
|
||||
/** 分配的用户ID */
|
||||
@Excel(name = "分配用户ID")
|
||||
private Long assignedUserId;
|
||||
|
||||
/** 分配时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "分配时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date assignTime;
|
||||
|
||||
/** 分配规则(ROUND_ROBIN轮询/DEFAULT默认) */
|
||||
@Excel(name = "分配规则")
|
||||
private String assignRule;
|
||||
|
||||
/** 备注 */
|
||||
@Excel(name = "备注")
|
||||
private String remark;
|
||||
|
||||
/** 分配用户名称(用于显示,不存储到数据库) */
|
||||
private String assignedUserName;
|
||||
|
||||
/** 配置名称(用于显示,不存储到数据库) */
|
||||
private String configName;
|
||||
|
||||
public void setId(Long id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setClewId(Long clewId)
|
||||
{
|
||||
this.clewId = clewId;
|
||||
}
|
||||
|
||||
public Long getClewId()
|
||||
{
|
||||
return clewId;
|
||||
}
|
||||
|
||||
public void setConfigId(Long configId)
|
||||
{
|
||||
this.configId = configId;
|
||||
}
|
||||
|
||||
public Long getConfigId()
|
||||
{
|
||||
return configId;
|
||||
}
|
||||
|
||||
public void setAssignedUserId(Long assignedUserId)
|
||||
{
|
||||
this.assignedUserId = assignedUserId;
|
||||
}
|
||||
|
||||
public Long getAssignedUserId()
|
||||
{
|
||||
return assignedUserId;
|
||||
}
|
||||
|
||||
public void setAssignTime(Date assignTime)
|
||||
{
|
||||
this.assignTime = assignTime;
|
||||
}
|
||||
|
||||
public Date getAssignTime()
|
||||
{
|
||||
return assignTime;
|
||||
}
|
||||
|
||||
public void setAssignRule(String assignRule)
|
||||
{
|
||||
this.assignRule = assignRule;
|
||||
}
|
||||
|
||||
public String getAssignRule()
|
||||
{
|
||||
return assignRule;
|
||||
}
|
||||
|
||||
public void setRemark(String remark)
|
||||
{
|
||||
this.remark = remark;
|
||||
}
|
||||
|
||||
public String getRemark()
|
||||
{
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setAssignedUserName(String assignedUserName)
|
||||
{
|
||||
this.assignedUserName = assignedUserName;
|
||||
}
|
||||
|
||||
public String getAssignedUserName()
|
||||
{
|
||||
return assignedUserName;
|
||||
}
|
||||
|
||||
public void setConfigName(String configName)
|
||||
{
|
||||
this.configName = configName;
|
||||
}
|
||||
|
||||
public String getConfigName()
|
||||
{
|
||||
return configName;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
package com.ruoyi.system.domain;
|
||||
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import com.ruoyi.common.annotation.Excel;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
|
||||
/**
|
||||
* 分配计数器对象 duty_assign_counter
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2024-12-19
|
||||
*/
|
||||
@Data
|
||||
public class DutyAssignCounter extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 主键ID */
|
||||
private Long id;
|
||||
|
||||
/** 配置ID */
|
||||
@Excel(name = "配置ID")
|
||||
private Long configId;
|
||||
|
||||
/** 上次分配的用户ID */
|
||||
@Excel(name = "上次分配用户ID")
|
||||
private Long lastAssignedUserId;
|
||||
|
||||
/** 分配次数 */
|
||||
@Excel(name = "分配次数")
|
||||
private Integer assignCount;
|
||||
|
||||
/** 更新时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "更新时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
|
||||
/** 配置名称(用于显示,不存储到数据库) */
|
||||
private String configName;
|
||||
|
||||
/** 上次分配用户名称(用于显示,不存储到数据库) */
|
||||
private String lastAssignedUserName;
|
||||
|
||||
public void setId(Long id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setConfigId(Long configId)
|
||||
{
|
||||
this.configId = configId;
|
||||
}
|
||||
|
||||
public Long getConfigId()
|
||||
{
|
||||
return configId;
|
||||
}
|
||||
|
||||
public void setLastAssignedUserId(Long lastAssignedUserId)
|
||||
{
|
||||
this.lastAssignedUserId = lastAssignedUserId;
|
||||
}
|
||||
|
||||
public Long getLastAssignedUserId()
|
||||
{
|
||||
return lastAssignedUserId;
|
||||
}
|
||||
|
||||
public void setAssignCount(Integer assignCount)
|
||||
{
|
||||
this.assignCount = assignCount;
|
||||
}
|
||||
|
||||
public Integer getAssignCount()
|
||||
{
|
||||
return assignCount;
|
||||
}
|
||||
|
||||
public void setUpdateTime(Date updateTime)
|
||||
{
|
||||
this.updateTime = updateTime;
|
||||
}
|
||||
|
||||
public Date getUpdateTime()
|
||||
{
|
||||
return updateTime;
|
||||
}
|
||||
|
||||
public void setConfigName(String configName)
|
||||
{
|
||||
this.configName = configName;
|
||||
}
|
||||
|
||||
public String getConfigName()
|
||||
{
|
||||
return configName;
|
||||
}
|
||||
|
||||
public void setLastAssignedUserName(String lastAssignedUserName)
|
||||
{
|
||||
this.lastAssignedUserName = lastAssignedUserName;
|
||||
}
|
||||
|
||||
public String getLastAssignedUserName()
|
||||
{
|
||||
return lastAssignedUserName;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
package com.ruoyi.system.domain;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
import com.ruoyi.common.annotation.Excel;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 值班配置对象 duty_config
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2024-01-15
|
||||
*/
|
||||
@Data
|
||||
public class DutyConfig extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 主键ID */
|
||||
private Long id;
|
||||
|
||||
/** 配置名称 */
|
||||
@Excel(name = "配置名称")
|
||||
private String configName;
|
||||
|
||||
/** 开始时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "开始时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date startTime;
|
||||
|
||||
/** 结束时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "结束时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date endTime;
|
||||
|
||||
/** 状态(0停用 1启用) */
|
||||
@Excel(name = "状态", readConverterExp = "0=停用,1=启用")
|
||||
private String status;
|
||||
|
||||
/** 备注 */
|
||||
@Excel(name = "备注")
|
||||
private String remark;
|
||||
|
||||
/** 删除标志(0正常 1删除) */
|
||||
private String delFlag;
|
||||
|
||||
/** 值班用户列表(用于显示和操作,不存储到数据库) */
|
||||
private List<DutyUser> dutyUsers;
|
||||
|
||||
/** 值班用户名称列表(用于显示,不存储到数据库) */
|
||||
private String dutyUserNames;
|
||||
|
||||
/** 是否在当前时间范围内(用于前端显示,不存储到数据库) */
|
||||
private Boolean isActive;
|
||||
|
||||
public void setConfigName(String configName)
|
||||
{
|
||||
this.configName = configName;
|
||||
}
|
||||
|
||||
public String getConfigName()
|
||||
{
|
||||
return configName;
|
||||
}
|
||||
|
||||
public void setStartTime(Date startTime)
|
||||
{
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
public Date getStartTime()
|
||||
{
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public void setEndTime(Date endTime)
|
||||
{
|
||||
this.endTime = endTime;
|
||||
}
|
||||
|
||||
public Date getEndTime()
|
||||
{
|
||||
return endTime;
|
||||
}
|
||||
|
||||
public void setStatus(String status)
|
||||
{
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getStatus()
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setDelFlag(String delFlag)
|
||||
{
|
||||
this.delFlag = delFlag;
|
||||
}
|
||||
|
||||
public String getDelFlag()
|
||||
{
|
||||
return delFlag;
|
||||
}
|
||||
|
||||
public void setDutyUsers(List<DutyUser> dutyUsers)
|
||||
{
|
||||
this.dutyUsers = dutyUsers;
|
||||
}
|
||||
|
||||
public List<DutyUser> getDutyUsers()
|
||||
{
|
||||
return dutyUsers;
|
||||
}
|
||||
|
||||
public void setDutyUserNames(String dutyUserNames)
|
||||
{
|
||||
this.dutyUserNames = dutyUserNames;
|
||||
}
|
||||
|
||||
public String getDutyUserNames()
|
||||
{
|
||||
return dutyUserNames;
|
||||
}
|
||||
|
||||
public void setIsActive(Boolean isActive)
|
||||
{
|
||||
this.isActive = isActive;
|
||||
}
|
||||
|
||||
public Boolean getIsActive()
|
||||
{
|
||||
return isActive;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
package com.ruoyi.system.domain;
|
||||
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import com.ruoyi.common.annotation.Excel;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
|
||||
/**
|
||||
* 值班用户关联对象 duty_user
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2024-12-19
|
||||
*/
|
||||
@Data
|
||||
public class DutyUser extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 主键ID */
|
||||
private Long id;
|
||||
|
||||
/** 配置ID */
|
||||
@Excel(name = "配置ID")
|
||||
private Long configId;
|
||||
|
||||
/** 用户ID */
|
||||
@Excel(name = "用户ID")
|
||||
private Long userId;
|
||||
|
||||
/** 排序顺序 */
|
||||
@Excel(name = "排序顺序")
|
||||
private Integer sortOrder;
|
||||
|
||||
|
||||
/** 用户名称(用于显示,不存储到数据库) */
|
||||
private String userName;
|
||||
|
||||
public void setId(Long id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setConfigId(Long configId)
|
||||
{
|
||||
this.configId = configId;
|
||||
}
|
||||
|
||||
public Long getConfigId()
|
||||
{
|
||||
return configId;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId)
|
||||
{
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public Long getUserId()
|
||||
{
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setSortOrder(Integer sortOrder)
|
||||
{
|
||||
this.sortOrder = sortOrder;
|
||||
}
|
||||
|
||||
public Integer getSortOrder()
|
||||
{
|
||||
return sortOrder;
|
||||
}
|
||||
|
||||
public void setUserName(String userName)
|
||||
{
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
public String getUserName()
|
||||
{
|
||||
return userName;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
package com.ruoyi.system.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import com.ruoyi.system.domain.DutyAssignCounter;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* 分配计数器Mapper接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2024-12-19
|
||||
*/
|
||||
public interface DutyAssignCounterMapper
|
||||
{
|
||||
/**
|
||||
* 查询分配计数器
|
||||
*
|
||||
* @param id 分配计数器主键
|
||||
* @return 分配计数器
|
||||
*/
|
||||
public DutyAssignCounter selectDutyAssignCounterById(Long id);
|
||||
|
||||
/**
|
||||
* 根据配置ID查询分配计数器
|
||||
*
|
||||
* @param configId 配置ID
|
||||
* @return 分配计数器
|
||||
*/
|
||||
public DutyAssignCounter selectDutyAssignCounterByConfigId(Long configId);
|
||||
|
||||
/**
|
||||
* 查询分配计数器列表
|
||||
*
|
||||
* @param dutyAssignCounter 分配计数器
|
||||
* @return 分配计数器集合
|
||||
*/
|
||||
public List<DutyAssignCounter> selectDutyAssignCounterList(DutyAssignCounter dutyAssignCounter);
|
||||
|
||||
/**
|
||||
* 新增分配计数器
|
||||
*
|
||||
* @param dutyAssignCounter 分配计数器
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertDutyAssignCounter(DutyAssignCounter dutyAssignCounter);
|
||||
|
||||
/**
|
||||
* 修改分配计数器
|
||||
*
|
||||
* @param dutyAssignCounter 分配计数器
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateDutyAssignCounter(DutyAssignCounter dutyAssignCounter);
|
||||
|
||||
/**
|
||||
* 删除分配计数器
|
||||
*
|
||||
* @param id 分配计数器主键
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteDutyAssignCounterById(Long id);
|
||||
|
||||
/**
|
||||
* 批量删除分配计数器
|
||||
*
|
||||
* @param ids 需要删除的数据主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteDutyAssignCounterByIds(String[] ids);
|
||||
|
||||
/**
|
||||
* 根据配置ID删除分配计数器
|
||||
*
|
||||
* @param configId 配置ID
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteDutyAssignCounterByConfigId(Long configId);
|
||||
|
||||
/**
|
||||
* 更新分配计数器(用于轮询算法)
|
||||
*
|
||||
* @param configId 配置ID
|
||||
* @param lastAssignedUserId 上次分配的用户ID
|
||||
* @param assignCount 分配次数
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateAssignCounter(@Param("configId") Long configId,
|
||||
@Param("lastAssignedUserId") Long lastAssignedUserId,
|
||||
@Param("assignCount") Integer assignCount);
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
package com.ruoyi.system.mapper;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import com.ruoyi.system.domain.DutyConfig;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* 值班配置Mapper接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2024-01-15
|
||||
*/
|
||||
public interface DutyConfigMapper
|
||||
{
|
||||
/**
|
||||
* 查询值班配置
|
||||
*
|
||||
* @param id 值班配置主键
|
||||
* @return 值班配置
|
||||
*/
|
||||
public DutyConfig selectDutyConfigById(Long id);
|
||||
|
||||
/**
|
||||
* 查询值班配置列表
|
||||
*
|
||||
* @param dutyConfig 值班配置
|
||||
* @return 值班配置集合
|
||||
*/
|
||||
public List<DutyConfig> selectDutyConfigList(DutyConfig dutyConfig);
|
||||
|
||||
/**
|
||||
* 根据当前时间查询有效的值班配置
|
||||
*
|
||||
* @param currentTime 当前时间
|
||||
* @return 值班配置
|
||||
*/
|
||||
public DutyConfig selectActiveDutyConfig(@Param("currentTime") Date currentTime);
|
||||
|
||||
/**
|
||||
* 新增值班配置
|
||||
*
|
||||
* @param dutyConfig 值班配置
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertDutyConfig(DutyConfig dutyConfig);
|
||||
|
||||
/**
|
||||
* 修改值班配置
|
||||
*
|
||||
* @param dutyConfig 值班配置
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateDutyConfig(DutyConfig dutyConfig);
|
||||
|
||||
/**
|
||||
* 删除值班配置
|
||||
*
|
||||
* @param id 值班配置主键
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteDutyConfigById(Long id);
|
||||
|
||||
/**
|
||||
* 批量删除值班配置
|
||||
*
|
||||
* @param ids 需要删除的数据主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteDutyConfigByIds(String[] ids);
|
||||
|
||||
/**
|
||||
* 检查时间段是否冲突
|
||||
*
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @param excludeId 排除的配置ID(用于编辑时排除自己)
|
||||
* @return 冲突的配置数量
|
||||
*/
|
||||
public int checkTimeConflict(@Param("startTime") Date startTime,
|
||||
@Param("endTime") Date endTime,
|
||||
@Param("excludeId") Long excludeId);
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
package com.ruoyi.system.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import com.ruoyi.system.domain.DutyUser;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* 值班用户关联Mapper接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2024-12-19
|
||||
*/
|
||||
public interface DutyUserMapper
|
||||
{
|
||||
/**
|
||||
* 查询值班用户关联
|
||||
*
|
||||
* @param id 值班用户关联主键
|
||||
* @return 值班用户关联
|
||||
*/
|
||||
public DutyUser selectDutyUserById(Long id);
|
||||
|
||||
/**
|
||||
* 查询值班用户关联列表
|
||||
*
|
||||
* @param dutyUser 值班用户关联
|
||||
* @return 值班用户关联集合
|
||||
*/
|
||||
public List<DutyUser> selectDutyUserList(DutyUser dutyUser);
|
||||
|
||||
/**
|
||||
* 根据配置ID查询值班用户列表
|
||||
*
|
||||
* @param configId 配置ID
|
||||
* @return 值班用户关联集合
|
||||
*/
|
||||
public List<DutyUser> selectDutyUsersByConfigId(Long configId);
|
||||
|
||||
/**
|
||||
* 根据配置ID查询值班用户列表(按排序顺序)
|
||||
*
|
||||
* @param configId 配置ID
|
||||
* @return 值班用户关联集合
|
||||
*/
|
||||
public List<DutyUser> selectDutyUsersByConfigIdOrderBySort(Long configId);
|
||||
|
||||
/**
|
||||
* 新增值班用户关联
|
||||
*
|
||||
* @param dutyUser 值班用户关联
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertDutyUser(DutyUser dutyUser);
|
||||
|
||||
/**
|
||||
* 批量新增值班用户关联
|
||||
*
|
||||
* @param dutyUsers 值班用户关联列表
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertDutyUserBatch(List<DutyUser> dutyUsers);
|
||||
|
||||
/**
|
||||
* 修改值班用户关联
|
||||
*
|
||||
* @param dutyUser 值班用户关联
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateDutyUser(DutyUser dutyUser);
|
||||
|
||||
/**
|
||||
* 删除值班用户关联
|
||||
*
|
||||
* @param id 值班用户关联主键
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteDutyUserById(Long id);
|
||||
|
||||
/**
|
||||
* 批量删除值班用户关联
|
||||
*
|
||||
* @param ids 需要删除的数据主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteDutyUserByIds(String ids);
|
||||
|
||||
/**
|
||||
* 根据配置ID删除值班用户关联
|
||||
*
|
||||
* @param configId 配置ID
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteDutyUserByConfigId(Long configId);
|
||||
|
||||
/**
|
||||
* 检查用户是否已经在配置中
|
||||
*
|
||||
* @param configId 配置ID
|
||||
* @param userId 用户ID
|
||||
* @return 结果
|
||||
*/
|
||||
public int checkUserExists(@Param("configId") Long configId, @Param("userId") Long userId);
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
package com.ruoyi.system.service;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import com.ruoyi.system.domain.DutyConfig;
|
||||
|
||||
/**
|
||||
* 值班配置Service接口
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2024-12-19
|
||||
*/
|
||||
public interface IDutyConfigService
|
||||
{
|
||||
/**
|
||||
* 查询值班配置
|
||||
*
|
||||
* @param id 值班配置主键
|
||||
* @return 值班配置
|
||||
*/
|
||||
public DutyConfig selectDutyConfigById(Long id);
|
||||
|
||||
/**
|
||||
* 查询值班配置列表
|
||||
*
|
||||
* @param dutyConfig 值班配置
|
||||
* @return 值班配置集合
|
||||
*/
|
||||
public List<DutyConfig> selectDutyConfigList(DutyConfig dutyConfig);
|
||||
|
||||
/**
|
||||
* 根据当前时间获取下一个值班用户ID
|
||||
*
|
||||
* @return 值班用户ID,如果没有匹配的配置则返回null
|
||||
*/
|
||||
public Long getNextDutyUserId();
|
||||
|
||||
/**
|
||||
* 根据当前时间查询有效的值班配置
|
||||
*
|
||||
* @param currentTime 当前时间
|
||||
* @return 值班配置
|
||||
*/
|
||||
public DutyConfig selectActiveDutyConfig(Date currentTime);
|
||||
|
||||
/**
|
||||
* 新增值班配置
|
||||
*
|
||||
* @param dutyConfig 值班配置
|
||||
* @param dutyUserIds 值班用户ID字符串(逗号分隔)
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertDutyConfig(DutyConfig dutyConfig, String dutyUserIds);
|
||||
|
||||
/**
|
||||
* 修改值班配置
|
||||
*
|
||||
* @param dutyConfig 值班配置
|
||||
* @param dutyUserIds 值班用户ID字符串(逗号分隔)
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateDutyConfig(DutyConfig dutyConfig, String dutyUserIds);
|
||||
|
||||
/**
|
||||
* 批量删除值班配置
|
||||
*
|
||||
* @param ids 需要删除的值班配置主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteDutyConfigByIds(String ids);
|
||||
|
||||
/**
|
||||
* 删除值班配置信息
|
||||
*
|
||||
* @param id 值班配置主键
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteDutyConfigById(Long id);
|
||||
|
||||
/**
|
||||
* 检查时间段是否冲突
|
||||
*
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @param excludeId 排除的配置ID(用于修改时排除自己)
|
||||
* @return 是否冲突
|
||||
*/
|
||||
public boolean checkTimeConflict(Date startTime, Date endTime, Long excludeId);
|
||||
|
||||
/**
|
||||
* 修改值班配置状态
|
||||
*
|
||||
* @param dutyConfig 值班配置
|
||||
* @return 结果
|
||||
*/
|
||||
public int changeStatus(DutyConfig dutyConfig);
|
||||
}
|
||||
|
|
@ -0,0 +1,322 @@
|
|||
package com.ruoyi.system.service.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import com.ruoyi.common.utils.DateUtils;
|
||||
import com.ruoyi.system.mapper.DutyConfigMapper;
|
||||
import com.ruoyi.system.mapper.DutyUserMapper;
|
||||
import com.ruoyi.system.mapper.DutyAssignCounterMapper;
|
||||
import com.ruoyi.system.domain.DutyConfig;
|
||||
import com.ruoyi.system.domain.DutyUser;
|
||||
import com.ruoyi.system.domain.DutyAssignCounter;
|
||||
import com.ruoyi.system.service.IDutyConfigService;
|
||||
|
||||
import com.ruoyi.common.core.text.Convert;
|
||||
|
||||
/**
|
||||
* 值班配置Service业务层处理
|
||||
*
|
||||
* @author ruoyi
|
||||
* @date 2024-01-15
|
||||
*/
|
||||
@Service
|
||||
public class DutyConfigServiceImpl implements IDutyConfigService
|
||||
{
|
||||
@Autowired
|
||||
private DutyConfigMapper dutyConfigMapper;
|
||||
|
||||
@Autowired
|
||||
private DutyUserMapper dutyUserMapper;
|
||||
|
||||
@Autowired
|
||||
private DutyAssignCounterMapper dutyAssignCounterMapper;
|
||||
|
||||
/**
|
||||
* 查询值班配置
|
||||
*
|
||||
* @param id 值班配置主键
|
||||
* @return 值班配置
|
||||
*/
|
||||
@Override
|
||||
public DutyConfig selectDutyConfigById(Long id)
|
||||
{
|
||||
DutyConfig dutyConfig = dutyConfigMapper.selectDutyConfigById(id);
|
||||
if (dutyConfig != null) {
|
||||
// 设置值班用户名称
|
||||
List<DutyUser> dutyUsers = dutyConfig.getDutyUsers();
|
||||
if (dutyUsers != null && !dutyUsers.isEmpty()) {
|
||||
String userNames = dutyUsers.stream()
|
||||
.map(DutyUser::getUserName)
|
||||
.collect(Collectors.joining(","));
|
||||
dutyConfig.setDutyUserNames(userNames);
|
||||
}
|
||||
}
|
||||
return dutyConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询值班配置列表
|
||||
*
|
||||
* @param dutyConfig 值班配置
|
||||
* @return 值班配置
|
||||
*/
|
||||
@Override
|
||||
public List<DutyConfig> selectDutyConfigList(DutyConfig dutyConfig)
|
||||
{
|
||||
List<DutyConfig> list = dutyConfigMapper.selectDutyConfigList(dutyConfig);
|
||||
// 为每个配置设置用户名称和是否激活状态
|
||||
Date now = new Date();
|
||||
for (DutyConfig config : list) {
|
||||
// 设置值班用户名称
|
||||
List<DutyUser> dutyUsers = config.getDutyUsers();
|
||||
if (dutyUsers != null && !dutyUsers.isEmpty()) {
|
||||
String userNames = dutyUsers.stream()
|
||||
.map(DutyUser::getUserName)
|
||||
.collect(Collectors.joining(","));
|
||||
config.setDutyUserNames(userNames);
|
||||
}
|
||||
|
||||
// 设置是否在当前时间范围内
|
||||
if ("1".equals(config.getStatus()) &&
|
||||
config.getStartTime() != null && config.getEndTime() != null) {
|
||||
config.setIsActive(now.compareTo(config.getStartTime()) >= 0 &&
|
||||
now.compareTo(config.getEndTime()) <= 0);
|
||||
} else {
|
||||
config.setIsActive(false);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据当前时间获取下一个值班用户ID(轮询分配)
|
||||
*
|
||||
* @return 值班用户ID,如果没有匹配的配置则返回null
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public Long getNextDutyUserId()
|
||||
{
|
||||
Date currentTime = new Date();
|
||||
DutyConfig activeDutyConfig = dutyConfigMapper.selectActiveDutyConfig(currentTime);
|
||||
|
||||
if (activeDutyConfig == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获取值班用户列表(按排序顺序)
|
||||
List<DutyUser> dutyUsers = dutyUserMapper.selectDutyUsersByConfigIdOrderBySort(activeDutyConfig.getId());
|
||||
if (dutyUsers == null || dutyUsers.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 如果只有一个用户,直接返回
|
||||
if (dutyUsers.size() == 1) {
|
||||
updateAssignCounter(activeDutyConfig.getId(), dutyUsers.get(0).getUserId());
|
||||
return dutyUsers.get(0).getUserId();
|
||||
}
|
||||
|
||||
// 查询分配计数器
|
||||
DutyAssignCounter counter = dutyAssignCounterMapper.selectDutyAssignCounterByConfigId(activeDutyConfig.getId());
|
||||
|
||||
Long nextUserId = null;
|
||||
|
||||
if (counter == null) {
|
||||
// 第一次分配,返回第一个用户
|
||||
nextUserId = dutyUsers.get(0).getUserId();
|
||||
|
||||
// 创建计数器
|
||||
counter = new DutyAssignCounter();
|
||||
counter.setConfigId(activeDutyConfig.getId());
|
||||
counter.setLastAssignedUserId(nextUserId);
|
||||
counter.setAssignCount(1);
|
||||
counter.setUpdateTime(currentTime);
|
||||
dutyAssignCounterMapper.insertDutyAssignCounter(counter);
|
||||
} else {
|
||||
// 轮询:找到上次分配用户的下一个用户
|
||||
Long lastUserId = counter.getLastAssignedUserId();
|
||||
int lastIndex = -1;
|
||||
|
||||
// 找到上次分配用户的索引
|
||||
for (int i = 0; i < dutyUsers.size(); i++) {
|
||||
if (dutyUsers.get(i).getUserId().equals(lastUserId)) {
|
||||
lastIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 计算下一个用户的索引(循环轮询)
|
||||
int nextIndex = (lastIndex + 1) % dutyUsers.size();
|
||||
nextUserId = dutyUsers.get(nextIndex).getUserId();
|
||||
|
||||
// 更新计数器
|
||||
Integer assignCount = counter.getAssignCount() == null ? 1 : counter.getAssignCount() + 1;
|
||||
dutyAssignCounterMapper.updateAssignCounter(activeDutyConfig.getId(), nextUserId, assignCount);
|
||||
}
|
||||
|
||||
return nextUserId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新分配计数器(内部方法)
|
||||
*/
|
||||
private void updateAssignCounter(Long configId, Long userId) {
|
||||
DutyAssignCounter counter = dutyAssignCounterMapper.selectDutyAssignCounterByConfigId(configId);
|
||||
if (counter == null) {
|
||||
counter = new DutyAssignCounter();
|
||||
counter.setConfigId(configId);
|
||||
counter.setLastAssignedUserId(userId);
|
||||
counter.setAssignCount(1);
|
||||
counter.setUpdateTime(new Date());
|
||||
dutyAssignCounterMapper.insertDutyAssignCounter(counter);
|
||||
} else {
|
||||
Integer assignCount = counter.getAssignCount() == null ? 1 : counter.getAssignCount() + 1;
|
||||
dutyAssignCounterMapper.updateAssignCounter(configId, userId, assignCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据当前时间查询有效的值班配置
|
||||
*
|
||||
* @param currentTime 当前时间
|
||||
* @return 值班配置
|
||||
*/
|
||||
@Override
|
||||
public DutyConfig selectActiveDutyConfig(Date currentTime)
|
||||
{
|
||||
return dutyConfigMapper.selectActiveDutyConfig(currentTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增值班配置
|
||||
*
|
||||
* @param dutyConfig 值班配置
|
||||
* @param dutyUserIds 值班用户ID字符串(逗号分隔)
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public int insertDutyConfig(DutyConfig dutyConfig, String dutyUserIds)
|
||||
{
|
||||
// 创建时间由数据库 sysdate() 自动设置,无需手动设置
|
||||
int result = dutyConfigMapper.insertDutyConfig(dutyConfig);
|
||||
|
||||
// 处理值班用户关联
|
||||
if (result > 0 && dutyUserIds != null && !dutyUserIds.trim().isEmpty()) {
|
||||
String[] userIdArray = dutyUserIds.split(",");
|
||||
List<DutyUser> dutyUsers = new ArrayList<>();
|
||||
for (int i = 0; i < userIdArray.length; i++) {
|
||||
DutyUser dutyUser = new DutyUser();
|
||||
dutyUser.setConfigId(dutyConfig.getId());
|
||||
dutyUser.setUserId(Long.parseLong(userIdArray[i].trim()));
|
||||
dutyUser.setSortOrder(i + 1);
|
||||
dutyUser.setCreateTime(DateUtils.getNowDate());
|
||||
dutyUsers.add(dutyUser);
|
||||
}
|
||||
dutyUserMapper.insertDutyUserBatch(dutyUsers);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改值班配置
|
||||
*
|
||||
* @param dutyConfig 值班配置
|
||||
* @param dutyUserIds 值班用户ID字符串(逗号分隔)
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public int updateDutyConfig(DutyConfig dutyConfig, String dutyUserIds)
|
||||
{
|
||||
dutyConfig.setUpdateTime(DateUtils.getNowDate());
|
||||
int result = dutyConfigMapper.updateDutyConfig(dutyConfig);
|
||||
|
||||
// 先删除原有的值班用户关联
|
||||
dutyUserMapper.deleteDutyUserByConfigId(dutyConfig.getId());
|
||||
|
||||
// 重新添加值班用户关联
|
||||
if (result > 0 && dutyUserIds != null && !dutyUserIds.trim().isEmpty()) {
|
||||
String[] userIdArray = dutyUserIds.split(",");
|
||||
List<DutyUser> dutyUsers = new ArrayList<>();
|
||||
for (int i = 0; i < userIdArray.length; i++) {
|
||||
DutyUser dutyUser = new DutyUser();
|
||||
dutyUser.setConfigId(dutyConfig.getId());
|
||||
dutyUser.setUserId(Long.parseLong(userIdArray[i].trim()));
|
||||
dutyUser.setSortOrder(i + 1);
|
||||
dutyUser.setCreateTime(DateUtils.getNowDate());
|
||||
dutyUsers.add(dutyUser);
|
||||
}
|
||||
dutyUserMapper.insertDutyUserBatch(dutyUsers);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除值班配置
|
||||
*
|
||||
* @param ids 需要删除的值班配置主键
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public int deleteDutyConfigByIds(String ids)
|
||||
{
|
||||
String[] configIds = Convert.toStrArray(ids);
|
||||
// 删除值班用户关联关系
|
||||
for (String configId : configIds) {
|
||||
dutyUserMapper.deleteDutyUserByConfigId(Long.parseLong(configId));
|
||||
}
|
||||
return dutyConfigMapper.deleteDutyConfigByIds(configIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除值班配置信息
|
||||
*
|
||||
* @param id 值班配置主键
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public int deleteDutyConfigById(Long id)
|
||||
{
|
||||
// 删除值班用户关联关系
|
||||
dutyUserMapper.deleteDutyUserByConfigId(id);
|
||||
return dutyConfigMapper.deleteDutyConfigById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查时间段是否冲突
|
||||
*
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @param excludeId 排除的配置ID(用于编辑时排除自己)
|
||||
* @return 是否冲突
|
||||
*/
|
||||
@Override
|
||||
public boolean checkTimeConflict(Date startTime, Date endTime, Long excludeId)
|
||||
{
|
||||
int count = dutyConfigMapper.checkTimeConflict(startTime, endTime, excludeId);
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改值班配置状态
|
||||
*
|
||||
* @param dutyConfig 值班配置
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int changeStatus(DutyConfig dutyConfig)
|
||||
{
|
||||
dutyConfig.setUpdateTime(DateUtils.getNowDate());
|
||||
return dutyConfigMapper.updateDutyConfig(dutyConfig);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.system.mapper.DutyAssignCounterMapper">
|
||||
|
||||
<resultMap type="DutyAssignCounter" id="DutyAssignCounterResult">
|
||||
<result property="id" column="id" />
|
||||
<result property="configId" column="config_id" />
|
||||
<result property="lastAssignedUserId" column="last_assigned_user_id" />
|
||||
<result property="assignCount" column="assign_count" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
<result property="configName" column="config_name" />
|
||||
<result property="lastAssignedUserName" column="last_assigned_user_name" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectDutyAssignCounterVo">
|
||||
select dac.id, dac.config_id, dac.last_assigned_user_id, dac.assign_count, dac.update_time,
|
||||
dc.config_name, u.user_name as last_assigned_user_name
|
||||
from duty_assign_counter dac
|
||||
left join duty_config dc on dac.config_id = dc.id
|
||||
left join sys_user u on dac.last_assigned_user_id = u.user_id
|
||||
</sql>
|
||||
|
||||
<select id="selectDutyAssignCounterList" parameterType="DutyAssignCounter" resultMap="DutyAssignCounterResult">
|
||||
<include refid="selectDutyAssignCounterVo"/>
|
||||
<where>
|
||||
<if test="configId != null "> and dac.config_id = #{configId}</if>
|
||||
<if test="lastAssignedUserId != null "> and dac.last_assigned_user_id = #{lastAssignedUserId}</if>
|
||||
</where>
|
||||
order by dac.update_time desc
|
||||
</select>
|
||||
|
||||
<select id="selectDutyAssignCounterById" parameterType="Long" resultMap="DutyAssignCounterResult">
|
||||
<include refid="selectDutyAssignCounterVo"/>
|
||||
where dac.id = #{id}
|
||||
</select>
|
||||
|
||||
<select id="selectDutyAssignCounterByConfigId" parameterType="Long" resultMap="DutyAssignCounterResult">
|
||||
<include refid="selectDutyAssignCounterVo"/>
|
||||
where dac.config_id = #{configId}
|
||||
</select>
|
||||
|
||||
<insert id="insertDutyAssignCounter" parameterType="DutyAssignCounter" useGeneratedKeys="true" keyProperty="id">
|
||||
insert into duty_assign_counter
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="configId != null">config_id,</if>
|
||||
<if test="lastAssignedUserId != null">last_assigned_user_id,</if>
|
||||
<if test="assignCount != null">assign_count,</if>
|
||||
update_time
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="configId != null">#{configId},</if>
|
||||
<if test="lastAssignedUserId != null">#{lastAssignedUserId},</if>
|
||||
<if test="assignCount != null">#{assignCount},</if>
|
||||
sysdate()
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
<update id="updateDutyAssignCounter" parameterType="DutyAssignCounter">
|
||||
update duty_assign_counter
|
||||
<trim prefix="SET" suffixOverrides=",">
|
||||
<if test="configId != null">config_id = #{configId},</if>
|
||||
<if test="lastAssignedUserId != null">last_assigned_user_id = #{lastAssignedUserId},</if>
|
||||
<if test="assignCount != null">assign_count = #{assignCount},</if>
|
||||
update_time = sysdate()
|
||||
</trim>
|
||||
where id = #{id}
|
||||
</update>
|
||||
|
||||
<update id="updateAssignCounter">
|
||||
update duty_assign_counter
|
||||
set last_assigned_user_id = #{lastAssignedUserId},
|
||||
assign_count = #{assignCount},
|
||||
update_time = sysdate()
|
||||
where config_id = #{configId}
|
||||
</update>
|
||||
|
||||
<delete id="deleteDutyAssignCounterById" parameterType="Long">
|
||||
delete from duty_assign_counter where id = #{id}
|
||||
</delete>
|
||||
|
||||
<delete id="deleteDutyAssignCounterByIds" parameterType="String">
|
||||
delete from duty_assign_counter where id in
|
||||
<foreach item="id" collection="array" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</delete>
|
||||
|
||||
<delete id="deleteDutyAssignCounterByConfigId" parameterType="Long">
|
||||
delete from duty_assign_counter where config_id = #{configId}
|
||||
</delete>
|
||||
|
||||
</mapper>
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.system.mapper.DutyConfigMapper">
|
||||
|
||||
<resultMap type="DutyConfig" id="DutyConfigResult">
|
||||
<result property="id" column="id" />
|
||||
<result property="configName" column="config_name" />
|
||||
<result property="startTime" column="start_time" />
|
||||
<result property="endTime" column="end_time" />
|
||||
<result property="status" column="status" />
|
||||
<result property="remark" column="remark" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
<result property="delFlag" column="del_flag" />
|
||||
<collection property="dutyUsers" ofType="DutyUser" column="id" select="com.ruoyi.system.mapper.DutyUserMapper.selectDutyUsersByConfigIdOrderBySort"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectDutyConfigVo">
|
||||
select id, config_name, start_time, end_time, status, remark, create_by, create_time, update_by, update_time, del_flag from duty_config
|
||||
</sql>
|
||||
|
||||
<select id="selectDutyConfigList" parameterType="DutyConfig" resultMap="DutyConfigResult">
|
||||
<include refid="selectDutyConfigVo"/>
|
||||
<where>
|
||||
del_flag = '0'
|
||||
<if test="configName != null and configName != ''"> and config_name like concat('%', #{configName}, '%')</if>
|
||||
<if test="startTime != null "> and start_time = #{startTime}</if>
|
||||
<if test="endTime != null "> and end_time = #{endTime}</if>
|
||||
<if test="status != null and status != ''"> and status = #{status}</if>
|
||||
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
|
||||
AND date_format(create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d')
|
||||
</if>
|
||||
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
|
||||
AND date_format(create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d')
|
||||
</if>
|
||||
</where>
|
||||
order by create_time desc
|
||||
</select>
|
||||
|
||||
<select id="selectDutyConfigById" parameterType="Long" resultMap="DutyConfigResult">
|
||||
<include refid="selectDutyConfigVo"/>
|
||||
where id = #{id}
|
||||
</select>
|
||||
|
||||
<select id="selectActiveDutyConfig" parameterType="java.util.Date" resultMap="DutyConfigResult">
|
||||
<include refid="selectDutyConfigVo"/>
|
||||
where status = '1'
|
||||
and del_flag = '0'
|
||||
and #{currentTime} between start_time and end_time
|
||||
order by create_time desc
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
<select id="checkTimeConflict" resultType="int">
|
||||
select count(1) from duty_config
|
||||
where status = '1'
|
||||
and del_flag = '0'
|
||||
and (
|
||||
(#{startTime} between start_time and end_time)
|
||||
or (#{endTime} between start_time and end_time)
|
||||
or (start_time between #{startTime} and #{endTime})
|
||||
or (end_time between #{startTime} and #{endTime})
|
||||
)
|
||||
<if test="excludeId != null">
|
||||
and id != #{excludeId}
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<insert id="insertDutyConfig" parameterType="DutyConfig" useGeneratedKeys="true" keyProperty="id">
|
||||
insert into duty_config
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="configName != null and configName != ''">config_name,</if>
|
||||
<if test="startTime != null">start_time,</if>
|
||||
<if test="endTime != null">end_time,</if>
|
||||
<if test="status != null">status,</if>
|
||||
<if test="remark != null">remark,</if>
|
||||
<if test="createBy != null">create_by,</if>
|
||||
<if test="delFlag != null">del_flag,</if>
|
||||
create_time
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="configName != null and configName != ''">#{configName},</if>
|
||||
<if test="startTime != null">#{startTime},</if>
|
||||
<if test="endTime != null">#{endTime},</if>
|
||||
<if test="status != null">#{status},</if>
|
||||
<if test="remark != null">#{remark},</if>
|
||||
<if test="createBy != null">#{createBy},</if>
|
||||
<if test="delFlag != null">#{delFlag},</if>
|
||||
sysdate()
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
<update id="updateDutyConfig" parameterType="DutyConfig">
|
||||
update duty_config
|
||||
<trim prefix="SET" suffixOverrides=",">
|
||||
<if test="configName != null and configName != ''">config_name = #{configName},</if>
|
||||
<if test="startTime != null">start_time = #{startTime},</if>
|
||||
<if test="endTime != null">end_time = #{endTime},</if>
|
||||
<if test="status != null">status = #{status},</if>
|
||||
<if test="remark != null">remark = #{remark},</if>
|
||||
<if test="updateBy != null">update_by = #{updateBy},</if>
|
||||
<if test="delFlag != null">del_flag = #{delFlag},</if>
|
||||
update_time = sysdate()
|
||||
</trim>
|
||||
where id = #{id}
|
||||
</update>
|
||||
|
||||
<delete id="deleteDutyConfigById" parameterType="Long">
|
||||
update duty_config set del_flag = '1' where id = #{id}
|
||||
</delete>
|
||||
|
||||
<delete id="deleteDutyConfigByIds" parameterType="java.lang.String">
|
||||
update duty_config set del_flag = '1' where id in
|
||||
<foreach item="id" collection="array" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</delete>
|
||||
|
||||
</mapper>
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.system.mapper.DutyUserMapper">
|
||||
|
||||
<resultMap type="DutyUser" id="DutyUserResult">
|
||||
<result property="id" column="id" />
|
||||
<result property="configId" column="config_id" />
|
||||
<result property="userId" column="user_id" />
|
||||
<result property="sortOrder" column="sort_order" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="userName" column="user_name" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectDutyUserVo">
|
||||
select du.id, du.config_id, du.user_id, du.sort_order, du.create_time,
|
||||
u.user_name
|
||||
from duty_user du
|
||||
left join sys_user u on du.user_id = u.user_id
|
||||
</sql>
|
||||
|
||||
<select id="selectDutyUserList" parameterType="DutyUser" resultMap="DutyUserResult">
|
||||
<include refid="selectDutyUserVo"/>
|
||||
<where>
|
||||
<if test="configId != null "> and du.config_id = #{configId}</if>
|
||||
<if test="userId != null "> and du.user_id = #{userId}</if>
|
||||
</where>
|
||||
order by du.config_id, du.sort_order
|
||||
</select>
|
||||
|
||||
<select id="selectDutyUserById" parameterType="Long" resultMap="DutyUserResult">
|
||||
<include refid="selectDutyUserVo"/>
|
||||
where du.id = #{id}
|
||||
</select>
|
||||
|
||||
<select id="selectDutyUsersByConfigId" parameterType="Long" resultMap="DutyUserResult">
|
||||
<include refid="selectDutyUserVo"/>
|
||||
where du.config_id = #{configId}
|
||||
order by du.sort_order
|
||||
</select>
|
||||
|
||||
<select id="selectDutyUsersByConfigIdOrderBySort" parameterType="Long" resultMap="DutyUserResult">
|
||||
<include refid="selectDutyUserVo"/>
|
||||
where du.config_id = #{configId}
|
||||
order by du.sort_order asc
|
||||
</select>
|
||||
|
||||
<select id="checkUserExists" resultType="int">
|
||||
select count(1) from duty_user
|
||||
where config_id = #{configId} and user_id = #{userId}
|
||||
</select>
|
||||
|
||||
<insert id="insertDutyUser" parameterType="DutyUser" useGeneratedKeys="true" keyProperty="id">
|
||||
insert into duty_user
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="configId != null">config_id,</if>
|
||||
<if test="userId != null">user_id,</if>
|
||||
<if test="sortOrder != null">sort_order,</if>
|
||||
<if test="createTime != null">create_time,</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="configId != null">#{configId},</if>
|
||||
<if test="userId != null">#{userId},</if>
|
||||
<if test="sortOrder != null">#{sortOrder},</if>
|
||||
<if test="createTime != null">#{createTime},</if>
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
<insert id="insertDutyUserBatch" parameterType="java.util.List">
|
||||
insert into duty_user(config_id, user_id, sort_order, create_time) values
|
||||
<foreach collection="list" item="item" separator=",">
|
||||
(#{item.configId}, #{item.userId}, #{item.sortOrder}, #{item.createTime})
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
<update id="updateDutyUser" parameterType="DutyUser">
|
||||
update duty_user
|
||||
<trim prefix="SET" suffixOverrides=",">
|
||||
<if test="configId != null">config_id = #{configId},</if>
|
||||
<if test="userId != null">user_id = #{userId},</if>
|
||||
<if test="sortOrder != null">sort_order = #{sortOrder},</if>
|
||||
</trim>
|
||||
where id = #{id}
|
||||
</update>
|
||||
|
||||
<delete id="deleteDutyUserById" parameterType="Long">
|
||||
delete from duty_user where id = #{id}
|
||||
</delete>
|
||||
|
||||
<delete id="deleteDutyUserByIds" parameterType="String">
|
||||
delete from duty_user where id in
|
||||
<foreach item="id" collection="array" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</delete>
|
||||
|
||||
<delete id="deleteDutyUserByConfigId" parameterType="Long">
|
||||
delete from duty_user where config_id = #{configId}
|
||||
</delete>
|
||||
|
||||
</mapper>
|
||||
Loading…
Reference in New Issue