mirror of
https://gitee.com/zhijiantianya/ruoyi-vue-pro.git
synced 2026-10-06 07:22:59 +08:00
Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/ruoyi-vue-pro
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 307 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 91 KiB |
@@ -28,8 +28,8 @@
|
||||
| 【完整版】[ruoyi-vue-pro](https://gitee.com/zhijiantianya/ruoyi-vue-pro) | [`master`](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master/) 分支 | [`master-jdk17`](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master-jdk17/) 分支 | [`master-jdk25`](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master-jdk25/) 分支 |
|
||||
| 【精简版】[yudao-boot-mini](https://gitee.com/yudaocode/yudao-boot-mini) | [`master`](https://gitee.com/yudaocode/yudao-boot-mini/tree/master/) 分支 | [`master-jdk17`](https://gitee.com/yudaocode/yudao-boot-mini/tree/master-jdk17/) 分支 | [`master-jdk25`](https://gitee.com/yudaocode/yudao-boot-mini/tree/master-jdk25/) 分支 |
|
||||
|
||||
* 【完整版】:包括系统功能、基础设施、会员中心、数据报表、工作流程、商城系统、微信公众号、CRM、ERP、WMS、MES、HRM、FMS、PMS、IM 即时通讯、AI 大模型、IoT 物联网等功能
|
||||
* 【精简版】:只包括系统功能、基础设施功能,不包括会员中心、数据报表、工作流程、商城系统、微信公众号、CRM、ERP、WMS、MES、HRM、FMS、PMS、IM 即时通讯、AI 大模型、IoT 物联网等功能
|
||||
* 【完整版】:包括系统功能、基础设施、工作流程、支付系统、数据报表、微信公众号、商城系统、会员中心、ERP、WMS、CRM、MES、HRM、FMS、PMS、OA 协同办公、AI 大模型、IoT 物联网、IM 即时通讯等功能
|
||||
* 【精简版】:只包括系统功能、基础设施功能,不包括工作流程、支付系统、数据报表、微信公众号、商城系统、会员中心、ERP、WMS、CRM、MES、HRM、FMS、PMS、OA 协同办公、AI 大模型、IoT 物联网、IM 即时通讯等功能
|
||||
|
||||
可参考 [《迁移文档》](https://doc.iocoder.cn/migrate-module/) ,只需要 5-10 分钟,即可将【完整版】按需迁移到【精简版】
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
|
||||
* 通用模块(必选):系统功能、基础设施
|
||||
* 通用模块(可选):工作流程、支付系统、数据报表、会员中心
|
||||
* 业务系统(按需):Mall 电子商城、OA 办公自动化、ERP 企业资源计划系统、WMS 仓库管理系统、CRM 客户关系管理、CMS 内容管理系统、MES 执行制造系统、HRM 人力资源管理、FMS 财务管理、PMS 项目管理、AI 大模型平台、IoT 物联网系统、IM 即时通讯系统、Mobile 手机移动端、Report 数据大屏
|
||||
* 业务系统(按需):Mall 电子商城、ERP 企业资源计划系统、WMS 仓库管理系统、CRM 客户关系管理、CMS 内容管理系统、MES 执行制造系统、HRM 人力资源管理、FMS 财务管理、PMS 项目管理、OA 办公自动化、AI 大模型平台、IoT 物联网系统、IM 即时通讯系统、Mobile 手机移动端、Report 数据大屏
|
||||
|
||||
> 友情提示:本项目基于 RuoYi-Vue 修改,**重构优化**后端的代码,**美化**前端的界面。
|
||||
>
|
||||
@@ -147,6 +147,31 @@
|
||||
|
||||

|
||||
|
||||
### 基础设施
|
||||
|
||||
| | 功能 | 描述 |
|
||||
|-----|-----------|----------------------------------------------|
|
||||
| 🚀 | 代码生成 | 前后端代码的生成(Java、Vue、SQL、单元测试),支持 CRUD 下载 |
|
||||
| 🚀 | 系统接口 | 基于 Swagger 自动生成相关的 RESTful API 接口文档 |
|
||||
| 🚀 | 数据库文档 | 基于 Screw 自动生成数据库文档,支持导出 Word、HTML、MD 格式 |
|
||||
| | 表单构建 | 拖动表单元素生成相应的 HTML 代码,支持导出 JSON、Vue 文件 |
|
||||
| 🚀 | 配置管理 | 对系统动态配置常用参数,支持 SpringBoot 加载 |
|
||||
| ⭐️ | 定时任务 | 在线(添加、修改、删除)任务调度包含执行结果日志 |
|
||||
| 🚀 | 文件服务 | 支持将文件存储到 S3(MinIO、阿里云、腾讯云、七牛云)、本地、FTP、数据库等 |
|
||||
| 🚀 | WebSocket | 提供 WebSocket 接入示例,支持一对一、一对多发送方式 |
|
||||
| 🚀 | API 日志 | 包括 RESTful API 访问日志、异常日志两部分,方便排查 API 相关的问题 |
|
||||
| | MySQL 监控 | 监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈 |
|
||||
| | Redis 监控 | 监控 Redis 数据库的使用情况,使用的 Redis Key 管理 |
|
||||
| 🚀 | 消息队列 | 基于 Redis 实现消息队列,Stream 提供集群消费,Pub/Sub 提供广播消费 |
|
||||
| 🚀 | Java 监控 | 基于 Spring Boot Admin 实现 Java 应用的监控 |
|
||||
| 🚀 | 链路追踪 | 接入 SkyWalking 组件,实现链路追踪 |
|
||||
| 🚀 | 日志中心 | 接入 SkyWalking 组件,实现日志中心 |
|
||||
| 🚀 | 服务保障 | 基于 Redis 实现分布式锁、幂等、限流功能,满足高并发场景 |
|
||||
| 🚀 | 日志服务 | 轻量级日志中心,查看远程服务器的日志 |
|
||||
| 🚀 | 单元测试 | 基于 JUnit + Mockito 实现单元测试,保证功能的正确性、代码的质量等 |
|
||||
|
||||

|
||||
|
||||
### 工作流程
|
||||
|
||||

|
||||
@@ -198,31 +223,6 @@
|
||||
| 🚀 | 回调通知 | 查看支付回调业务的【支付】【退款】的通知结果 |
|
||||
| 🚀 | 接入示例 | 提供接入支付系统的【支付】【退款】的功能实战 |
|
||||
|
||||
### 基础设施
|
||||
|
||||
| | 功能 | 描述 |
|
||||
|-----|-----------|----------------------------------------------|
|
||||
| 🚀 | 代码生成 | 前后端代码的生成(Java、Vue、SQL、单元测试),支持 CRUD 下载 |
|
||||
| 🚀 | 系统接口 | 基于 Swagger 自动生成相关的 RESTful API 接口文档 |
|
||||
| 🚀 | 数据库文档 | 基于 Screw 自动生成数据库文档,支持导出 Word、HTML、MD 格式 |
|
||||
| | 表单构建 | 拖动表单元素生成相应的 HTML 代码,支持导出 JSON、Vue 文件 |
|
||||
| 🚀 | 配置管理 | 对系统动态配置常用参数,支持 SpringBoot 加载 |
|
||||
| ⭐️ | 定时任务 | 在线(添加、修改、删除)任务调度包含执行结果日志 |
|
||||
| 🚀 | 文件服务 | 支持将文件存储到 S3(MinIO、阿里云、腾讯云、七牛云)、本地、FTP、数据库等 |
|
||||
| 🚀 | WebSocket | 提供 WebSocket 接入示例,支持一对一、一对多发送方式 |
|
||||
| 🚀 | API 日志 | 包括 RESTful API 访问日志、异常日志两部分,方便排查 API 相关的问题 |
|
||||
| | MySQL 监控 | 监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈 |
|
||||
| | Redis 监控 | 监控 Redis 数据库的使用情况,使用的 Redis Key 管理 |
|
||||
| 🚀 | 消息队列 | 基于 Redis 实现消息队列,Stream 提供集群消费,Pub/Sub 提供广播消费 |
|
||||
| 🚀 | Java 监控 | 基于 Spring Boot Admin 实现 Java 应用的监控 |
|
||||
| 🚀 | 链路追踪 | 接入 SkyWalking 组件,实现链路追踪 |
|
||||
| 🚀 | 日志中心 | 接入 SkyWalking 组件,实现日志中心 |
|
||||
| 🚀 | 服务保障 | 基于 Redis 实现分布式锁、幂等、限流功能,满足高并发场景 |
|
||||
| 🚀 | 日志服务 | 轻量级日志中心,查看远程服务器的日志 |
|
||||
| 🚀 | 单元测试 | 基于 JUnit + Mockito 实现单元测试,保证功能的正确性、代码的质量等 |
|
||||
|
||||

|
||||
|
||||
### 数据报表
|
||||
|
||||
| | 功能 | 描述 |
|
||||
@@ -316,6 +316,14 @@
|
||||
|
||||

|
||||
|
||||
### OA 协同办公
|
||||
|
||||
演示地址:<https://doc.iocoder.cn/oa-preview/>
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### AI 大模型
|
||||
|
||||
演示地址:<https://doc.iocoder.cn/ai-preview/>
|
||||
@@ -355,23 +363,24 @@
|
||||
| `yudao-framework` | Java 框架拓展 |
|
||||
| `yudao-server` | 管理后台 + 用户 APP 的服务端 |
|
||||
| `yudao-module-system` | 系统功能的 Module 模块 |
|
||||
| `yudao-module-member` | 会员中心的 Module 模块 |
|
||||
| `yudao-module-infra` | 基础设施的 Module 模块 |
|
||||
| `yudao-module-bpm` | 工作流程的 Module 模块 |
|
||||
| `yudao-module-pay` | 支付系统的 Module 模块 |
|
||||
| `yudao-module-report` | 大屏报表 Module 模块 |
|
||||
| `yudao-module-mp` | 微信公众号的 Module 模块 |
|
||||
| `yudao-module-mall` | 商城系统的 Module 模块 |
|
||||
| `yudao-module-member` | 会员中心的 Module 模块 |
|
||||
| `yudao-module-erp` | ERP 系统的 Module 模块 |
|
||||
| `yudao-module-wms` | WMS 系统的 Module 模块 |
|
||||
| `yudao-module-crm` | CRM 系统的 Module 模块 |
|
||||
| `yudao-module-mes` | MES 系统的 Module 模块 |
|
||||
| `yudao-module-wms` | WMS 系统的 Module 模块 |
|
||||
| `yudao-module-hrm` | HRM 人力资源管理的 Module 模块 |
|
||||
| `yudao-module-fms` | FMS 财务管理的 Module 模块 |
|
||||
| `yudao-module-pms` | PMS 项目管理的 Module 模块 |
|
||||
| `yudao-module-im` | IM 即时通讯的 Module 模块 |
|
||||
| `yudao-module-oa` | OA 协同办公的 Module 模块 |
|
||||
| `yudao-module-ai` | AI 大模型的 Module 模块 |
|
||||
| `yudao-module-iot` | IoT 物联网的 Module 模块 |
|
||||
| `yudao-module-mp` | 微信公众号的 Module 模块 |
|
||||
| `yudao-module-report` | 大屏报表 Module 模块 |
|
||||
| `yudao-module-im` | IM 即时通讯的 Module 模块 |
|
||||
|
||||
### 框架
|
||||
|
||||
|
||||
+60
@@ -10,6 +10,8 @@ import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.*;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
@@ -32,6 +34,54 @@ import static cn.hutool.core.date.DatePattern.*;
|
||||
*/
|
||||
public class LocalDateTimeUtils {
|
||||
|
||||
/**
|
||||
* 按经过的时长计算天数,以 24 小时为一天,按指定精度四舍五入
|
||||
*
|
||||
* @param beginTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @param scale 保留的小数位数
|
||||
* @return 按毫秒精度计算的天数,时间缺失时返回 null
|
||||
*/
|
||||
public static BigDecimal getDaysBetween(LocalDateTime beginTime, LocalDateTime endTime, int scale) {
|
||||
if (beginTime == null || endTime == null) {
|
||||
return null;
|
||||
}
|
||||
return BigDecimal.valueOf(Duration.between(beginTime, endTime).toMillis())
|
||||
.divide(BigDecimal.valueOf(Duration.ofDays(1).toMillis()), scale, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按经过的时长计算天数,以 24 小时为一天,结果向上取整
|
||||
*
|
||||
* 与 {@link #getDaysBetweenInclusive(LocalDateTime, LocalDateTime)} 不同,本方法不按自然日期计数。
|
||||
* 例如跨午夜的 2 小时为 1 天,恰好 24 小时为 1 天,相同时刻为 0 天。
|
||||
*
|
||||
* @param beginTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @return 按毫秒精度计算的天数,时间缺失时返回 null;反向区间按相同规则向上取整
|
||||
*/
|
||||
public static Integer getDaysBetweenCeiling(LocalDateTime beginTime, LocalDateTime endTime) {
|
||||
if (beginTime == null || endTime == null) {
|
||||
return null;
|
||||
}
|
||||
return BigDecimal.valueOf(Duration.between(beginTime, endTime).toMillis())
|
||||
.divide(BigDecimal.valueOf(Duration.ofDays(1).toMillis()), 0, RoundingMode.CEILING).intValueExact();
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算包含首尾日期的自然日数,不足一天按对应自然日期计算
|
||||
*
|
||||
* @param beginTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @return 自然日数,时间缺失时返回 null;结束日期早于开始日期时返回非正数
|
||||
*/
|
||||
public static Integer getDaysBetweenInclusive(LocalDateTime beginTime, LocalDateTime endTime) {
|
||||
if (beginTime == null || endTime == null) {
|
||||
return null;
|
||||
}
|
||||
return Math.toIntExact(ChronoUnit.DAYS.between(beginTime.toLocalDate(), endTime.toLocalDate()) + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 空的 LocalDateTime 对象,主要用于 DB 唯一索引的默认值
|
||||
*/
|
||||
@@ -543,6 +593,16 @@ public class LocalDateTimeUtils {
|
||||
return date == null ? null : date.atTime(LocalTime.MAX);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定时间所在日期的结束时间
|
||||
*
|
||||
* @param time 时间
|
||||
* @return 当日结束时间;时间为空时返回 {@code null}
|
||||
*/
|
||||
public static LocalDateTime getDayEndTime(LocalDateTime time) {
|
||||
return time == null ? null : getDayEndTime(time.toLocalDate());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得自然日期范围对应的日期时间闭区间
|
||||
*
|
||||
|
||||
+8
-7
@@ -28,14 +28,14 @@ public class PageUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 对内存列表进行分页
|
||||
* 对内存列表分页,不改变列表顺序
|
||||
*
|
||||
* @param list 数据列表
|
||||
* @param pageParam 分页参数
|
||||
* @param pageParam 分页参数,支持 {@link PageParam#PAGE_SIZE_NONE} 返回全部数据
|
||||
* @param list 已排序的完整列表
|
||||
* @param <T> 数据类型
|
||||
* @return 分页结果
|
||||
* @return 分页结果,超出末页时保留总数并返回空列表
|
||||
*/
|
||||
public static <T> PageResult<T> buildPageResult(List<T> list, PageParam pageParam) {
|
||||
public static <T> PageResult<T> buildPageResult(PageParam pageParam, List<T> list) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return PageResult.empty();
|
||||
}
|
||||
@@ -43,8 +43,9 @@ public class PageUtils {
|
||||
if (PageParam.PAGE_SIZE_NONE.equals(pageParam.getPageSize())) {
|
||||
return new PageResult<>(list, total);
|
||||
}
|
||||
int fromIndex = Math.toIntExact(Math.min(getStart(pageParam), total));
|
||||
int toIndex = Math.toIntExact(Math.min((long) fromIndex + pageParam.getPageSize(), total));
|
||||
long offset = (long) (pageParam.getPageNo() - 1) * pageParam.getPageSize();
|
||||
int fromIndex = (int) Math.min(offset, total);
|
||||
int toIndex = (int) Math.min((long) fromIndex + pageParam.getPageSize(), total);
|
||||
return new PageResult<>(list.subList(fromIndex, toIndex), total);
|
||||
}
|
||||
|
||||
|
||||
+55
@@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.TimeRange;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
@@ -25,6 +26,56 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
*/
|
||||
public class LocalDateTimeUtilsTest {
|
||||
|
||||
@Test
|
||||
public void testGetDaysBetween() {
|
||||
// 准备参数
|
||||
LocalDateTime beginTime = LocalDateTime.of(2026, 12, 31, 23, 0);
|
||||
|
||||
// 调用,并断言:按时长四舍五入,跨日不改变计算规则
|
||||
assertEquals(new BigDecimal("0.0"), LocalDateTimeUtils.getDaysBetween(beginTime, beginTime, 1));
|
||||
assertEquals(new BigDecimal("0.5"), LocalDateTimeUtils.getDaysBetween(beginTime, beginTime.plusHours(12), 1));
|
||||
assertEquals(new BigDecimal("0.1"), LocalDateTimeUtils.getDaysBetween(beginTime, beginTime.plusMinutes(72), 1));
|
||||
assertEquals(new BigDecimal("0.0"), LocalDateTimeUtils.getDaysBetween(beginTime, beginTime.plusMinutes(71), 1));
|
||||
assertEquals(new BigDecimal("1.1"), LocalDateTimeUtils.getDaysBetween(beginTime, beginTime.plusHours(26), 1));
|
||||
assertEquals(new BigDecimal("-0.5"), LocalDateTimeUtils.getDaysBetween(beginTime, beginTime.minusHours(12), 1));
|
||||
assertEquals(new BigDecimal("0.50"), LocalDateTimeUtils.getDaysBetween(beginTime, beginTime.plusHours(12), 2));
|
||||
assertNull(LocalDateTimeUtils.getDaysBetween(null, beginTime, 1));
|
||||
assertNull(LocalDateTimeUtils.getDaysBetween(beginTime, null, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDaysBetweenCeiling() {
|
||||
// 准备参数
|
||||
LocalDateTime beginTime = LocalDateTime.of(2026, 12, 31, 23, 0);
|
||||
|
||||
// 调用,并断言:按时长取整,不因跨午夜额外增加一天
|
||||
assertEquals(0, LocalDateTimeUtils.getDaysBetweenCeiling(beginTime, beginTime));
|
||||
assertEquals(1, LocalDateTimeUtils.getDaysBetweenCeiling(beginTime, beginTime.plusHours(2)));
|
||||
assertEquals(1, LocalDateTimeUtils.getDaysBetweenCeiling(beginTime, beginTime.plusDays(1)));
|
||||
assertEquals(2, LocalDateTimeUtils.getDaysBetweenCeiling(beginTime, beginTime.plusDays(1).plusNanos(1_000_000)));
|
||||
assertEquals(2, LocalDateTimeUtils.getDaysBetweenCeiling(beginTime, beginTime.plusHours(26)));
|
||||
assertEquals(0, LocalDateTimeUtils.getDaysBetweenCeiling(beginTime, beginTime.minusHours(2)));
|
||||
assertEquals(-1, LocalDateTimeUtils.getDaysBetweenCeiling(beginTime, beginTime.minusHours(26)));
|
||||
assertNull(LocalDateTimeUtils.getDaysBetweenCeiling(null, beginTime));
|
||||
assertNull(LocalDateTimeUtils.getDaysBetweenCeiling(beginTime, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDaysBetweenInclusive() {
|
||||
// 准备参数
|
||||
LocalDateTime beginTime = LocalDateTime.of(2026, 12, 31, 23, 0);
|
||||
LocalDateTime endTime = LocalDateTime.of(2027, 1, 2, 1, 0);
|
||||
|
||||
// 调用,并断言:按自然日计算,包含首尾日期
|
||||
assertEquals(3, LocalDateTimeUtils.getDaysBetweenInclusive(beginTime, endTime));
|
||||
assertEquals(1, LocalDateTimeUtils.getDaysBetweenInclusive(beginTime, beginTime.minusHours(1)));
|
||||
assertEquals(-1, LocalDateTimeUtils.getDaysBetweenInclusive(endTime, beginTime));
|
||||
assertNull(LocalDateTimeUtils.getDaysBetweenInclusive(null, endTime));
|
||||
assertNull(LocalDateTimeUtils.getDaysBetweenInclusive(beginTime, null));
|
||||
assertEquals(3, LocalDateTimeUtils.getDaysBetweenInclusive(
|
||||
LocalDateTime.of(2024, 2, 28, 0, 0), LocalDateTime.of(2024, 3, 1, 0, 0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDateCompare() {
|
||||
// 准备参数
|
||||
@@ -99,6 +150,10 @@ public class LocalDateTimeUtilsTest {
|
||||
assertNull(LocalDateTimeUtils.getDayBeginTime((LocalDateTime) null));
|
||||
assertEquals(LocalDateTime.of(date, LocalTime.MAX),
|
||||
LocalDateTimeUtils.getDayEndTime(date));
|
||||
assertEquals(LocalDateTime.of(date, LocalTime.MAX),
|
||||
LocalDateTimeUtils.getDayEndTime(time));
|
||||
assertNull(LocalDateTimeUtils.getDayEndTime((LocalDateTime) null));
|
||||
assertNull(LocalDateTimeUtils.getDayEndTime((LocalDate) null));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package cn.iocoder.yudao.framework.common.util.object;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* {@link PageUtils} 的单元测试
|
||||
*/
|
||||
public class PageUtilsTest {
|
||||
|
||||
@Test
|
||||
public void testBuildPageResult() {
|
||||
// 准备参数
|
||||
PageParam pageParam = new PageParam();
|
||||
pageParam.setPageSize(2);
|
||||
List<Integer> list = Arrays.asList(1, 2, 3);
|
||||
|
||||
// 调用并断言首页和末页
|
||||
PageResult<Integer> result = PageUtils.buildPageResult(pageParam, list);
|
||||
assertEquals(3L, result.getTotal());
|
||||
assertEquals(Arrays.asList(1, 2), result.getList());
|
||||
pageParam.setPageNo(2);
|
||||
result = PageUtils.buildPageResult(pageParam, list);
|
||||
assertEquals(3L, result.getTotal());
|
||||
assertEquals(Collections.singletonList(3), result.getList());
|
||||
assertEquals(Arrays.asList(1, 2, 3), list);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildPageResult_empty() {
|
||||
PageParam pageParam = new PageParam();
|
||||
assertEquals(0L, PageUtils.buildPageResult(pageParam, Collections.emptyList()).getTotal());
|
||||
assertTrue(PageUtils.buildPageResult(pageParam, null).getList().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildPageResult_pastLastPage() {
|
||||
PageParam pageParam = new PageParam();
|
||||
pageParam.setPageNo(Integer.MAX_VALUE);
|
||||
pageParam.setPageSize(200);
|
||||
|
||||
PageResult<Integer> result = PageUtils.buildPageResult(pageParam, Arrays.asList(1, 2, 3));
|
||||
|
||||
assertEquals(3L, result.getTotal());
|
||||
assertTrue(result.getList().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildPageResult_noPagination() {
|
||||
PageParam pageParam = new PageParam();
|
||||
pageParam.setPageNo(2);
|
||||
pageParam.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
|
||||
PageResult<Integer> result = PageUtils.buildPageResult(pageParam, Arrays.asList(1, 2, 3));
|
||||
|
||||
assertEquals(3L, result.getTotal());
|
||||
assertEquals(Arrays.asList(1, 2, 3), result.getList());
|
||||
}
|
||||
|
||||
}
|
||||
+4
-3
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.SortablePageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.SortingField;
|
||||
import cn.iocoder.yudao.framework.common.util.object.PageUtils;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.util.JdbcUtils;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
@@ -44,7 +45,7 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> {
|
||||
if (PageParam.PAGE_SIZE_NONE.equals(pageParam.getPageSize())) {
|
||||
MyBatisUtils.addOrder(queryWrapper, sortingFields);
|
||||
List<T> list = selectList(queryWrapper);
|
||||
return new PageResult<>(list, (long) list.size());
|
||||
return PageUtils.buildPageResult(pageParam, list);
|
||||
}
|
||||
|
||||
// MyBatis Plus 查询
|
||||
@@ -58,7 +59,7 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> {
|
||||
// 特殊:不分页,直接查询全部
|
||||
if (PageParam.PAGE_SIZE_NONE.equals(pageParam.getPageSize())) {
|
||||
List<D> list = selectJoinList(clazz, lambdaWrapper);
|
||||
return new PageResult<>(list, (long) list.size());
|
||||
return PageUtils.buildPageResult(pageParam, list);
|
||||
}
|
||||
|
||||
// MyBatis Plus Join 查询
|
||||
@@ -81,7 +82,7 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> {
|
||||
// 特殊:不分页,直接查询全部
|
||||
if (PageParam.PAGE_SIZE_NONE.equals(pageParam.getPageSize())) {
|
||||
List<D> list = selectJoinList(clazz, lambdaWrapper);
|
||||
return new PageResult<>(list, (long) list.size());
|
||||
return PageUtils.buildPageResult(pageParam, list);
|
||||
}
|
||||
|
||||
// MyBatis Plus Join 查询
|
||||
|
||||
+25
-13
@@ -30,13 +30,21 @@ public class BannerApplicationRunner implements ApplicationRunner {
|
||||
"https://doc.iocoder.cn",
|
||||
"https://t.zsxq.com/02Yf6M7Qn");
|
||||
|
||||
// 工作流
|
||||
if (isNotPresent("cn.iocoder.yudao.module.bpm.framework.flowable.config.BpmFlowableConfiguration")) {
|
||||
System.out.println("[工作流模块 yudao-module-bpm - 已禁用][参考 https://doc.iocoder.cn/bpm/ 开启]");
|
||||
}
|
||||
// 支付平台
|
||||
if (isNotPresent("cn.iocoder.yudao.module.pay.framework.pay.config.PayConfiguration")) {
|
||||
System.out.println("[支付系统 yudao-module-pay - 已禁用][参考 https://doc.iocoder.cn/pay/build/ 开启]");
|
||||
}
|
||||
// 数据报表
|
||||
if (isNotPresent("cn.iocoder.yudao.module.report.framework.security.config.SecurityConfiguration")) {
|
||||
System.out.println("[报表模块 yudao-module-report - 已禁用][参考 https://doc.iocoder.cn/report/ 开启]");
|
||||
}
|
||||
// 工作流
|
||||
if (isNotPresent("cn.iocoder.yudao.module.bpm.framework.flowable.config.BpmFlowableConfiguration")) {
|
||||
System.out.println("[工作流模块 yudao-module-bpm - 已禁用][参考 https://doc.iocoder.cn/bpm/ 开启]");
|
||||
// 微信公众号
|
||||
if (isNotPresent("cn.iocoder.yudao.module.mp.framework.mp.config.MpConfiguration")) {
|
||||
System.out.println("[微信公众号 yudao-module-mp - 已禁用][参考 https://doc.iocoder.cn/mp/build/ 开启]");
|
||||
}
|
||||
// 商城系统
|
||||
if (isNotPresent("cn.iocoder.yudao.module.trade.framework.web.config.TradeWebConfiguration")) {
|
||||
@@ -50,10 +58,6 @@ public class BannerApplicationRunner implements ApplicationRunner {
|
||||
if (isNotPresent("cn.iocoder.yudao.module.wms.framework.web.config.WmsWebConfiguration")) {
|
||||
System.out.println("[WMS 仓库管理系统 yudao-module-wms - 已禁用][参考 https://doc.iocoder.cn/wms/build/ 开启]");
|
||||
}
|
||||
// PMS 系统
|
||||
if (isNotPresent("cn.iocoder.yudao.module.pms.framework.web.config.PmsWebConfiguration")) {
|
||||
System.out.println("[PMS 项目管理系统 yudao-module-pms - 已禁用][请在 yudao-server 中开启]");
|
||||
}
|
||||
// CRM 系统
|
||||
if (isNotPresent("cn.iocoder.yudao.module.crm.framework.web.config.CrmWebConfiguration")) {
|
||||
System.out.println("[CRM 系统 yudao-module-crm - 已禁用][参考 https://doc.iocoder.cn/crm/build/ 开启]");
|
||||
@@ -62,13 +66,21 @@ public class BannerApplicationRunner implements ApplicationRunner {
|
||||
if (isNotPresent("cn.iocoder.yudao.module.mes.framework.web.config.MesWebConfiguration")) {
|
||||
System.out.println("[MES 系统 yudao-module-mes - 已禁用][参考 https://doc.iocoder.cn/mes/build/ 开启]");
|
||||
}
|
||||
// 微信公众号
|
||||
if (isNotPresent("cn.iocoder.yudao.module.mp.framework.mp.config.MpConfiguration")) {
|
||||
System.out.println("[微信公众号 yudao-module-mp - 已禁用][参考 https://doc.iocoder.cn/mp/build/ 开启]");
|
||||
// HRM 人力资源管理系统
|
||||
if (isNotPresent("cn.iocoder.yudao.module.hrm.framework.web.config.HrmWebConfiguration")) {
|
||||
System.out.println("[HRM 人力资源管理系统 yudao-module-hrm - 已禁用][参考 https://doc.iocoder.cn/hrm/build/ 开启]");
|
||||
}
|
||||
// 支付平台
|
||||
if (isNotPresent("cn.iocoder.yudao.module.pay.framework.pay.config.PayConfiguration")) {
|
||||
System.out.println("[支付系统 yudao-module-pay - 已禁用][参考 https://doc.iocoder.cn/pay/build/ 开启]");
|
||||
// FMS 财务管理系统
|
||||
if (isNotPresent("cn.iocoder.yudao.module.fms.framework.web.config.FmsWebConfiguration")) {
|
||||
System.out.println("[FMS 财务管理系统 yudao-module-fms - 已禁用][参考 https://doc.iocoder.cn/fms/build/ 开启]");
|
||||
}
|
||||
// PMS 系统
|
||||
if (isNotPresent("cn.iocoder.yudao.module.pms.framework.web.config.PmsWebConfiguration")) {
|
||||
System.out.println("[PMS 项目管理系统 yudao-module-pms - 已禁用][参考 https://doc.iocoder.cn/pms/build/ 开启]");
|
||||
}
|
||||
// OA 办公自动化
|
||||
if (isNotPresent("cn.iocoder.yudao.module.oa.framework.config.OaProperties")) {
|
||||
System.out.println("[OA 协同办公 yudao-module-oa - 已禁用][参考 https://doc.iocoder.cn/oa/build/ 开启]");
|
||||
}
|
||||
// AI 大模型
|
||||
if (isNotPresent("cn.iocoder.yudao.module.ai.framework.web.config.AiWebConfiguration")) {
|
||||
|
||||
+76
-52
@@ -387,78 +387,102 @@ public class GlobalExceptionHandler {
|
||||
if (!message.contains("doesn't exist")) {
|
||||
return null;
|
||||
}
|
||||
// 1. 数据报表
|
||||
if (message.contains("report_")) {
|
||||
log.error("[报表模块 yudao-module-report - 表结构未导入][参考 https://cloud.iocoder.cn/report/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[报表模块 yudao-module-report - 表结构未导入][参考 https://cloud.iocoder.cn/report/ 开启]");
|
||||
}
|
||||
// 2. 工作流
|
||||
// 1. 工作流
|
||||
if (message.contains("bpm_")) {
|
||||
log.error("[工作流模块 yudao-module-bpm - 表结构未导入][参考 https://cloud.iocoder.cn/bpm/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[工作流模块 yudao-module-bpm - 表结构未导入][参考 https://cloud.iocoder.cn/bpm/ 开启]");
|
||||
}
|
||||
// 3. 微信公众号
|
||||
if (message.contains("mp_")) {
|
||||
log.error("[微信公众号 yudao-module-mp - 表结构未导入][参考 https://cloud.iocoder.cn/mp/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[微信公众号 yudao-module-mp - 表结构未导入][参考 https://cloud.iocoder.cn/mp/build/ 开启]");
|
||||
}
|
||||
// 4. 商城系统
|
||||
if (StrUtil.containsAny(message, "product_", "promotion_", "trade_")) {
|
||||
log.error("[商城系统 yudao-module-mall - 已禁用][参考 https://cloud.iocoder.cn/mall/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[商城系统 yudao-module-mall - 已禁用][参考 https://cloud.iocoder.cn/mall/build/ 开启]");
|
||||
}
|
||||
// 5. ERP 系统
|
||||
if (message.contains("erp_")) {
|
||||
log.error("[ERP 系统 yudao-module-erp - 表结构未导入][参考 https://cloud.iocoder.cn/erp/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[ERP 系统 yudao-module-erp - 表结构未导入][参考 https://cloud.iocoder.cn/erp/build/ 开启]");
|
||||
}
|
||||
// 6. WMS 仓库管理系统
|
||||
if (message.contains("wms_")) {
|
||||
log.error("[WMS 仓库管理系统 yudao-module-wms - 表结构未导入][参考 https://doc.iocoder.cn/wms/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[WMS 仓库管理系统 yudao-module-wms - 表结构未导入][参考 https://doc.iocoder.cn/wms/build/ 开启]");
|
||||
}
|
||||
// 7. CRM 系统
|
||||
if (message.contains("crm_")) {
|
||||
log.error("[CRM 系统 yudao-module-crm - 表结构未导入][参考 https://cloud.iocoder.cn/crm/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[CRM 系统 yudao-module-crm - 表结构未导入][参考 https://cloud.iocoder.cn/crm/build/ 开启]");
|
||||
}
|
||||
// 8. MES 系统
|
||||
if (message.contains("mes_")) {
|
||||
log.error("[MES 系统 yudao-module-mes - 表结构未导入][参考 https://doc.iocoder.cn/mes/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[MES 系统 yudao-module-mes - 表结构未导入][参考 https://doc.iocoder.cn/mes/build/ 开启]");
|
||||
}
|
||||
// 9. IM 即时通讯
|
||||
if (message.contains("im_")) {
|
||||
log.error("[IM 即时通讯 yudao-module-im - 表结构未导入][参考 https://doc.iocoder.cn/im/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[IM 即时通讯 yudao-module-im - 表结构未导入][参考 https://doc.iocoder.cn/im/build/ 开启]");
|
||||
}
|
||||
// 10. 支付平台
|
||||
// 2. 支付平台
|
||||
if (message.contains("pay_")) {
|
||||
log.error("[支付模块 yudao-module-pay - 表结构未导入][参考 https://cloud.iocoder.cn/pay/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[支付模块 yudao-module-pay - 表结构未导入][参考 https://cloud.iocoder.cn/pay/build/ 开启]");
|
||||
}
|
||||
// 11. AI 大模型
|
||||
// 3. 数据报表
|
||||
if (message.contains("report_")) {
|
||||
log.error("[报表模块 yudao-module-report - 表结构未导入][参考 https://cloud.iocoder.cn/report/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[报表模块 yudao-module-report - 表结构未导入][参考 https://cloud.iocoder.cn/report/ 开启]");
|
||||
}
|
||||
// 4. 微信公众号
|
||||
if (message.contains("mp_")) {
|
||||
log.error("[微信公众号 yudao-module-mp - 表结构未导入][参考 https://cloud.iocoder.cn/mp/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[微信公众号 yudao-module-mp - 表结构未导入][参考 https://cloud.iocoder.cn/mp/build/ 开启]");
|
||||
}
|
||||
// 5. 商城系统
|
||||
if (StrUtil.containsAny(message, "product_", "promotion_", "trade_")) {
|
||||
log.error("[商城系统 yudao-module-mall - 表结构未导入][参考 https://cloud.iocoder.cn/mall/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[商城系统 yudao-module-mall - 表结构未导入][参考 https://cloud.iocoder.cn/mall/build/ 开启]");
|
||||
}
|
||||
// 6. ERP 系统
|
||||
if (message.contains("erp_")) {
|
||||
log.error("[ERP 系统 yudao-module-erp - 表结构未导入][参考 https://cloud.iocoder.cn/erp/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[ERP 系统 yudao-module-erp - 表结构未导入][参考 https://cloud.iocoder.cn/erp/build/ 开启]");
|
||||
}
|
||||
// 7. WMS 仓库管理系统
|
||||
if (message.contains("wms_")) {
|
||||
log.error("[WMS 仓库管理系统 yudao-module-wms - 表结构未导入][参考 https://doc.iocoder.cn/wms/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[WMS 仓库管理系统 yudao-module-wms - 表结构未导入][参考 https://doc.iocoder.cn/wms/build/ 开启]");
|
||||
}
|
||||
// 8. CRM 系统
|
||||
if (message.contains("crm_")) {
|
||||
log.error("[CRM 系统 yudao-module-crm - 表结构未导入][参考 https://cloud.iocoder.cn/crm/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[CRM 系统 yudao-module-crm - 表结构未导入][参考 https://cloud.iocoder.cn/crm/build/ 开启]");
|
||||
}
|
||||
// 9. MES 系统
|
||||
if (message.contains("mes_")) {
|
||||
log.error("[MES 系统 yudao-module-mes - 表结构未导入][参考 https://doc.iocoder.cn/mes/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[MES 系统 yudao-module-mes - 表结构未导入][参考 https://doc.iocoder.cn/mes/build/ 开启]");
|
||||
}
|
||||
// 10. HRM 人力资源管理系统
|
||||
if (message.contains("hrm_")) {
|
||||
log.error("[HRM 人力资源管理系统 yudao-module-hrm - 表结构未导入][参考 https://doc.iocoder.cn/hrm/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[HRM 人力资源管理系统 yudao-module-hrm - 表结构未导入][参考 https://doc.iocoder.cn/hrm/build/ 开启]");
|
||||
}
|
||||
// 11. FMS 财务管理系统
|
||||
if (message.contains("fms_")) {
|
||||
log.error("[FMS 财务管理系统 yudao-module-fms - 表结构未导入][参考 https://doc.iocoder.cn/fms/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[FMS 财务管理系统 yudao-module-fms - 表结构未导入][参考 https://doc.iocoder.cn/fms/build/ 开启]");
|
||||
}
|
||||
// 12. PMS 项目管理系统
|
||||
if (message.contains("pms_")) {
|
||||
log.error("[PMS 项目管理系统 yudao-module-pms - 表结构未导入][参考 https://doc.iocoder.cn/pms/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[PMS 项目管理系统 yudao-module-pms - 表结构未导入][参考 https://doc.iocoder.cn/pms/build/ 开启]");
|
||||
}
|
||||
// 13. OA 协同办公
|
||||
if (message.contains("oa_")) {
|
||||
log.error("[OA 协同办公 yudao-module-oa - 表结构未导入][参考 https://doc.iocoder.cn/oa/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[OA 协同办公 yudao-module-oa - 表结构未导入][参考 https://doc.iocoder.cn/oa/build/ 开启]");
|
||||
}
|
||||
// 14. AI 大模型
|
||||
if (message.contains("ai_")) {
|
||||
log.error("[AI 大模型 yudao-module-ai - 表结构未导入][参考 https://cloud.iocoder.cn/ai/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[AI 大模型 yudao-module-ai - 表结构未导入][参考 https://cloud.iocoder.cn/ai/build/ 开启]");
|
||||
}
|
||||
// 12. IoT 物联网
|
||||
// 15. IoT 物联网
|
||||
if (message.contains("iot_")) {
|
||||
log.error("[IoT 物联网 yudao-module-iot - 表结构未导入][参考 https://doc.iocoder.cn/iot/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[IoT 物联网 yudao-module-iot - 表结构未导入][参考 https://doc.iocoder.cn/iot/build/ 开启]");
|
||||
}
|
||||
// 16. IM 即时通讯
|
||||
if (message.contains("im_")) {
|
||||
log.error("[IM 即时通讯 yudao-module-im - 表结构未导入][参考 https://doc.iocoder.cn/im/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[IM 即时通讯 yudao-module-im - 表结构未导入][参考 https://doc.iocoder.cn/im/build/ 开启]");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -135,7 +135,7 @@ public class HrmAttendanceStatisticsServiceImpl implements HrmAttendanceStatisti
|
||||
buildMonthSummaryList(reqVO.getYear(), reqVO.getMonth(), employeeList);
|
||||
List<HrmAttendanceMonthRecordRespVO> filteredMonthSummaryList = filterList(monthSummaryList,
|
||||
record -> Objects.equals(record.getFullAttendance(), reqVO.getFullAttendance()));
|
||||
return PageUtils.buildPageResult(filteredMonthSummaryList, reqVO);
|
||||
return PageUtils.buildPageResult(reqVO, filteredMonthSummaryList);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>yudao-module-oa</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
oa 包下,办公自动化(Office Automation),提供协同办公、行政管理和业务审批功能。
|
||||
例如说:日程计划、企业云盘、请假报销、会议室、车辆、印章、公文等等
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-system</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-infra</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-module-bpm</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- DB 相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-mybatis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Job 定时任务相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-job</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 工具类相关 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.belerweb</groupId>
|
||||
<artifactId>pinyin4j</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Test 测试相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.announcement;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.announcement.vo.OaAnnouncementPageReqVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.announcement.vo.OaAnnouncementRespVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.announcement.vo.OaAnnouncementSaveReqVO;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.announcement.OaAnnouncementDO;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.announcement.OaAnnouncementReceiverDO;
|
||||
import cn.iocoder.yudao.module.oa.service.announcement.OaAnnouncementService;
|
||||
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.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - OA 公告")
|
||||
@RestController
|
||||
@RequestMapping("/oa/announcement")
|
||||
@Validated
|
||||
public class OaAnnouncementController {
|
||||
|
||||
@Resource
|
||||
private OaAnnouncementService announcementService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
|
||||
// ==================== 我发布的 ====================
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建公告")
|
||||
@PreAuthorize("@ss.hasPermission('oa:announcement:create')")
|
||||
public CommonResult<Long> createAnnouncement(@Valid @RequestBody OaAnnouncementSaveReqVO createReqVO) {
|
||||
return success(announcementService.createAnnouncement(createReqVO, getLoginUserId()));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新公告")
|
||||
@PreAuthorize("@ss.hasPermission('oa:announcement:update')")
|
||||
public CommonResult<Boolean> updateAnnouncement(@Valid @RequestBody OaAnnouncementSaveReqVO updateReqVO) {
|
||||
announcementService.updateAnnouncement(updateReqVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除发布的公告")
|
||||
@Parameter(name = "id", description = "公告编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:announcement:delete')")
|
||||
public CommonResult<Boolean> deleteAnnouncement(@RequestParam("id") Long id) {
|
||||
announcementService.deleteAnnouncement(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/published-page")
|
||||
@Operation(summary = "获得我发布的公告分页")
|
||||
@PreAuthorize("@ss.hasPermission('oa:announcement:query')")
|
||||
public CommonResult<PageResult<OaAnnouncementRespVO>> getPublishedAnnouncementPage(
|
||||
@Valid OaAnnouncementPageReqVO pageReqVO) {
|
||||
Long userId = getLoginUserId();
|
||||
PageResult<OaAnnouncementDO> pageResult = announcementService
|
||||
.getPublishedAnnouncementPage(pageReqVO, userId);
|
||||
return success(new PageResult<>(buildAnnouncementRespVOList(pageResult.getList(), userId),
|
||||
pageResult.getTotal()));
|
||||
}
|
||||
|
||||
// ==================== 我收到的 ====================
|
||||
|
||||
@DeleteMapping("/delete-received")
|
||||
@Operation(summary = "删除接收的公告")
|
||||
@Parameter(name = "id", description = "公告编号", required = true, example = "1024")
|
||||
public CommonResult<Boolean> deleteReceivedAnnouncement(@RequestParam("id") Long id) {
|
||||
announcementService.deleteReceivedAnnouncement(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/received-page")
|
||||
@Operation(summary = "获得接收的公告分页")
|
||||
@PreAuthorize("@ss.hasPermission('oa:announcement:query')")
|
||||
public CommonResult<PageResult<OaAnnouncementRespVO>> getReceivedAnnouncementPage(
|
||||
@Valid OaAnnouncementPageReqVO pageReqVO) {
|
||||
Long userId = getLoginUserId();
|
||||
PageResult<OaAnnouncementDO> pageResult = announcementService
|
||||
.getReceivedAnnouncementPage(pageReqVO, userId);
|
||||
return success(new PageResult<>(buildAnnouncementRespVOList(pageResult.getList(), userId),
|
||||
pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@PutMapping("/update-read-status")
|
||||
@Operation(summary = "标记公告为已读")
|
||||
@Parameter(name = "id", description = "公告编号", required = true, example = "1024")
|
||||
public CommonResult<Boolean> updateAnnouncementReadStatus(@RequestParam("id") Long id) {
|
||||
announcementService.updateAnnouncementReadStatus(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/forward")
|
||||
@Operation(summary = "转发公告给下属")
|
||||
@Parameter(name = "id", description = "公告编号", required = true, example = "1024")
|
||||
public CommonResult<Integer> forwardAnnouncement(@RequestParam("id") Long id) {
|
||||
return success(announcementService.forwardAnnouncement(id, getLoginUserId()));
|
||||
}
|
||||
|
||||
// ==================== 公共详情 ====================
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得公告详情")
|
||||
@Parameter(name = "id", description = "公告编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:announcement:query')")
|
||||
public CommonResult<OaAnnouncementRespVO> getAnnouncement(@RequestParam("id") Long id) {
|
||||
Long userId = getLoginUserId();
|
||||
OaAnnouncementDO announcement = announcementService.getAnnouncement(id, userId);
|
||||
return success(buildAnnouncementRespVO(announcement, userId));
|
||||
}
|
||||
|
||||
// ==================== 拼接 VO ====================
|
||||
|
||||
/**
|
||||
* 拼接公告详情
|
||||
*
|
||||
* @param announcement 公告
|
||||
* @param loginUserId 当前登录用户编号
|
||||
* @return 公告响应
|
||||
*/
|
||||
private OaAnnouncementRespVO buildAnnouncementRespVO(OaAnnouncementDO announcement, Long loginUserId) {
|
||||
if (announcement == null) {
|
||||
return null;
|
||||
}
|
||||
return CollUtil.getFirst(buildAnnouncementRespVOList(Collections.singletonList(announcement), loginUserId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接公告响应列表
|
||||
*
|
||||
* @param announcements 公告列表
|
||||
* @param loginUserId 当前登录用户编号
|
||||
* @return 公告响应列表
|
||||
*/
|
||||
private List<OaAnnouncementRespVO> buildAnnouncementRespVOList(List<OaAnnouncementDO> announcements,
|
||||
Long loginUserId) {
|
||||
if (CollUtil.isEmpty(announcements)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 1. 查询公告接收关系、发布人、接收人和发布人部门信息
|
||||
Map<Long, List<OaAnnouncementReceiverDO>> receiverListMap = announcementService
|
||||
.getAnnouncementReceiverListMap(convertSet(announcements, OaAnnouncementDO::getId));
|
||||
Collection<Long> userIds = new ArrayList<>(convertSet(announcements,
|
||||
announcement -> NumberUtils.parseLong(announcement.getCreator())));
|
||||
receiverListMap.values().forEach(receivers -> userIds.addAll(
|
||||
convertSet(receivers, OaAnnouncementReceiverDO::getReceiverUserId)));
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
|
||||
Set<Long> subordinateUserIds = convertSet(adminUserApi.getUserListBySubordinate(loginUserId), AdminUserRespDTO::getId);
|
||||
// 2. 拼接公告、发布人、接收人和当前接收状态
|
||||
return convertList(announcements, announcementDO -> {
|
||||
OaAnnouncementRespVO announcement = BeanUtils.toBean(announcementDO, OaAnnouncementRespVO.class)
|
||||
.setPublisherUserId(NumberUtils.parseLong(announcementDO.getCreator()));
|
||||
MapUtils.findAndThen(userMap, announcement.getPublisherUserId(), publisher -> {
|
||||
announcement.setPublisherUserName(publisher.getNickname());
|
||||
announcement.setPublisherDeptId(publisher.getDeptId());
|
||||
MapUtils.findAndThen(deptMap, publisher.getDeptId(),
|
||||
dept -> announcement.setPublisherDeptName(dept.getName()));
|
||||
});
|
||||
List<OaAnnouncementReceiverDO> receivers = receiverListMap.getOrDefault(announcement.getId(),
|
||||
Collections.emptyList());
|
||||
List<Long> receiverUserIds = convertList(receivers, OaAnnouncementReceiverDO::getReceiverUserId);
|
||||
announcement.setReceiverUserIds(receiverUserIds);
|
||||
announcement.setReceiverUserNames(convertList(receiverUserIds, receiverUserId -> {
|
||||
AdminUserRespDTO receiverUser = userMap.get(receiverUserId);
|
||||
return receiverUser != null ? receiverUser.getNickname() : null;
|
||||
}));
|
||||
OaAnnouncementReceiverDO loginUserReceiver = CollUtil.findOne(receivers,
|
||||
receiver -> receiver.getReceiverUserId().equals(loginUserId));
|
||||
if (loginUserReceiver != null) {
|
||||
announcement.setReadStatus(loginUserReceiver.getReadStatus());
|
||||
}
|
||||
announcement.setForwarded(CollUtil.containsAny(receiverUserIds, subordinateUserIds));
|
||||
return announcement;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.announcement.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.oa.enums.announcement.OaAnnouncementTypeEnum;
|
||||
import cn.iocoder.yudao.module.oa.enums.schedule.OaPriorityEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
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 = "管理后台 - OA 公告分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class OaAnnouncementPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "公告标题,模糊匹配", example = "国庆节放假通知")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "公告类型", example = "1")
|
||||
@InEnum(value = OaAnnouncementTypeEnum.class, message = "公告类型必须是 {value}")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "优先级", example = "1")
|
||||
@InEnum(value = OaPriorityEnum.class, message = "优先级必须是 {value}")
|
||||
private Integer priority;
|
||||
|
||||
@Schema(description = "是否已读", example = "false")
|
||||
private Boolean readStatus;
|
||||
|
||||
@Schema(description = "发布时间范围", example = "[\"2026-09-14 00:00:00\", \"2026-09-14 23:59:59\"]")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.announcement.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - OA 公告 Response VO")
|
||||
@Data
|
||||
public class OaAnnouncementRespVO {
|
||||
|
||||
@Schema(description = "公告编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "发布人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long publisherUserId;
|
||||
|
||||
@Schema(description = "发布人用户昵称", example = "芋道源码")
|
||||
private String publisherUserName;
|
||||
|
||||
@Schema(description = "发布人部门编号", example = "1")
|
||||
private Long publisherDeptId;
|
||||
|
||||
@Schema(description = "发布人部门名称", example = "研发部")
|
||||
private String publisherDeptName;
|
||||
|
||||
@Schema(description = "公告类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "优先级", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer priority;
|
||||
|
||||
@Schema(description = "公告标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "国庆节放假通知")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "公告内容", example = "请各部门提前安排好假期值班工作。")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "相关链接", example = "https://example.com/file.pdf")
|
||||
private String url;
|
||||
|
||||
@Schema(description = "是否置顶", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
|
||||
private Boolean top;
|
||||
|
||||
@Schema(description = "接收人用户编号列表", example = "[1, 2]")
|
||||
private List<Long> receiverUserIds;
|
||||
|
||||
@Schema(description = "接收人用户昵称列表", example = "[\"张三\", \"李四\"]")
|
||||
private List<String> receiverUserNames;
|
||||
|
||||
@Schema(description = "当前接收人是否已读", example = "false")
|
||||
private Boolean readStatus;
|
||||
|
||||
@Schema(description = "当前接收人是否已转发给下属", example = "false")
|
||||
private Boolean forwarded;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64", example = "1789347600000")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.announcement.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.oa.enums.announcement.OaAnnouncementTypeEnum;
|
||||
import cn.iocoder.yudao.module.oa.enums.schedule.OaPriorityEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - OA 公告新增/修改 Request VO")
|
||||
@Data
|
||||
public class OaAnnouncementSaveReqVO {
|
||||
|
||||
@Schema(description = "公告编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "公告类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "公告类型不能为空")
|
||||
@InEnum(value = OaAnnouncementTypeEnum.class, message = "公告类型必须是 {value}")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "优先级", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "优先级不能为空")
|
||||
@InEnum(value = OaPriorityEnum.class, message = "优先级必须是 {value}")
|
||||
private Integer priority;
|
||||
|
||||
@Schema(description = "公告标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "国庆节放假通知")
|
||||
@NotBlank(message = "公告标题不能为空")
|
||||
@Size(max = 255, message = "公告标题长度不能超过 255 个字符")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "公告内容", example = "请各部门提前安排好假期值班工作。")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "相关链接", example = "https://www.iocoder.cn")
|
||||
@Size(max = 512, message = "相关链接长度不能超过 512 个字符")
|
||||
private String url;
|
||||
|
||||
@Schema(description = "是否置顶", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
|
||||
@NotNull(message = "是否置顶不能为空")
|
||||
private Boolean top;
|
||||
|
||||
}
|
||||
+260
@@ -0,0 +1,260 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.attendance;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.attendance.vo.OaAttendanceMonthReportReqVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.attendance.vo.OaAttendanceMonthReportRespVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.attendance.vo.OaAttendancePageReqVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.attendance.vo.OaAttendanceRespVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.attendance.vo.OaAttendanceUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.attendance.vo.OaAttendanceWeekReportReqVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.attendance.vo.OaAttendanceWeekReportRespVO;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.attendance.OaAttendanceDO;
|
||||
import cn.iocoder.yudao.module.oa.service.attendance.OaAttendanceService;
|
||||
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.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - OA 考勤")
|
||||
@RestController
|
||||
@RequestMapping("/oa/attendance")
|
||||
@Validated
|
||||
public class OaAttendanceController {
|
||||
|
||||
@Resource
|
||||
private OaAttendanceService attendanceService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
|
||||
// ==================== 我的考勤 ====================
|
||||
|
||||
@PostMapping("/clock")
|
||||
@Operation(summary = "执行当前用户打卡")
|
||||
public CommonResult<Long> clockAttendance() {
|
||||
return success(attendanceService.clockAttendance(getLoginUserId(), getClientIP()));
|
||||
}
|
||||
|
||||
@GetMapping("/my-page")
|
||||
@Operation(summary = "获得我的考勤分页列表")
|
||||
public CommonResult<PageResult<OaAttendanceRespVO>> getMyAttendancePage(
|
||||
@Valid OaAttendancePageReqVO pageReqVO) {
|
||||
PageResult<OaAttendanceDO> pageResult = attendanceService.getMyAttendancePage(
|
||||
pageReqVO, getLoginUserId());
|
||||
return success(new PageResult<>(buildAttendanceRespVOList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/my-today-list")
|
||||
@Operation(summary = "获得我的今日考勤列表")
|
||||
public CommonResult<List<OaAttendanceRespVO>> getMyTodayAttendanceList() {
|
||||
return success(buildAttendanceRespVOList(
|
||||
attendanceService.getTodayAttendanceList(getLoginUserId())));
|
||||
}
|
||||
|
||||
// ==================== 考勤管理 ====================
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新考勤记录")
|
||||
@PreAuthorize("@ss.hasPermission('oa:attendance:update')")
|
||||
public CommonResult<Boolean> updateAttendance(@Valid @RequestBody OaAttendanceUpdateReqVO updateReqVO) {
|
||||
attendanceService.updateAttendance(updateReqVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除考勤记录")
|
||||
@Parameter(name = "id", description = "考勤记录编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:attendance:delete')")
|
||||
public CommonResult<Boolean> deleteAttendance(@RequestParam("id") Long id) {
|
||||
attendanceService.deleteAttendance(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得考勤详情")
|
||||
@Parameter(name = "id", description = "考勤记录编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:attendance:query')")
|
||||
public CommonResult<OaAttendanceRespVO> getAttendance(@RequestParam("id") Long id) {
|
||||
OaAttendanceDO attendance = attendanceService.getAttendance(id, getLoginUserId());
|
||||
return success(buildAttendanceRespVO(attendance));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得考勤分页列表")
|
||||
@PreAuthorize("@ss.hasPermission('oa:attendance:query')")
|
||||
public CommonResult<PageResult<OaAttendanceRespVO>> getAttendancePage(
|
||||
@Valid OaAttendancePageReqVO pageReqVO) {
|
||||
PageResult<OaAttendanceDO> pageResult = attendanceService.getAttendancePage(pageReqVO, getLoginUserId());
|
||||
return success(new PageResult<>(buildAttendanceRespVOList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
// ==================== 考勤报表 ====================
|
||||
|
||||
@GetMapping("/week-report")
|
||||
@Operation(summary = "获得考勤周报")
|
||||
@PreAuthorize("@ss.hasPermission('oa:attendance:query')")
|
||||
public CommonResult<List<OaAttendanceWeekReportRespVO>> getAttendanceWeekReport(
|
||||
@Valid OaAttendanceWeekReportReqVO reqVO) {
|
||||
return success(buildAttendanceWeekReportRespVOList(
|
||||
attendanceService.getAttendanceWeekReport(reqVO, getLoginUserId())));
|
||||
}
|
||||
|
||||
@GetMapping("/month-report")
|
||||
@Operation(summary = "获得考勤月报")
|
||||
@PreAuthorize("@ss.hasPermission('oa:attendance:query')")
|
||||
public CommonResult<List<OaAttendanceMonthReportRespVO>> getAttendanceMonthReport(
|
||||
@Valid OaAttendanceMonthReportReqVO reqVO) {
|
||||
return success(buildAttendanceMonthReportRespVOList(
|
||||
attendanceService.getAttendanceMonthReport(reqVO, getLoginUserId())));
|
||||
}
|
||||
|
||||
// ==================== 拼接 VO ====================
|
||||
|
||||
/**
|
||||
* 拼接考勤详情
|
||||
*
|
||||
* @param attendance 考勤记录
|
||||
* @return 考勤响应
|
||||
*/
|
||||
private OaAttendanceRespVO buildAttendanceRespVO(OaAttendanceDO attendance) {
|
||||
if (attendance == null) {
|
||||
return null;
|
||||
}
|
||||
return CollUtil.getFirst(buildAttendanceRespVOList(Collections.singletonList(attendance)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接考勤响应列表
|
||||
*
|
||||
* @param attendances 考勤记录列表
|
||||
* @return 考勤响应列表
|
||||
*/
|
||||
private List<OaAttendanceRespVO> buildAttendanceRespVOList(List<OaAttendanceDO> attendances) {
|
||||
if (CollUtil.isEmpty(attendances)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
|
||||
convertSet(attendances, OaAttendanceDO::getUserId));
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
|
||||
convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
|
||||
return BeanUtils.toBean(attendances, OaAttendanceRespVO.class, attendance ->
|
||||
fillAttendanceUserInfo(attendance, userMap, deptMap));
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接考勤周报响应列表
|
||||
*
|
||||
* @param reports 考勤周报列表
|
||||
* @return 包含用户和部门信息的考勤周报列表
|
||||
*/
|
||||
private List<OaAttendanceWeekReportRespVO> buildAttendanceWeekReportRespVOList(
|
||||
List<OaAttendanceWeekReportRespVO> reports) {
|
||||
if (CollUtil.isEmpty(reports)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
|
||||
convertSet(reports, OaAttendanceWeekReportRespVO::getUserId));
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
|
||||
convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
|
||||
reports.forEach(report -> fillAttendanceUserInfo(report, userMap, deptMap));
|
||||
return reports;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接考勤月报响应列表
|
||||
*
|
||||
* @param reports 考勤月报列表
|
||||
* @return 包含用户和部门信息的考勤月报列表
|
||||
*/
|
||||
private List<OaAttendanceMonthReportRespVO> buildAttendanceMonthReportRespVOList(
|
||||
List<OaAttendanceMonthReportRespVO> reports) {
|
||||
if (CollUtil.isEmpty(reports)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
|
||||
convertSet(reports, OaAttendanceMonthReportRespVO::getUserId));
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
|
||||
convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
|
||||
reports.forEach(report -> fillAttendanceUserInfo(report, userMap, deptMap));
|
||||
return reports;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接考勤响应的用户和部门信息
|
||||
*
|
||||
* @param attendance 考勤响应
|
||||
* @param userMap 用户 Map
|
||||
* @param deptMap 部门 Map
|
||||
*/
|
||||
private void fillAttendanceUserInfo(OaAttendanceRespVO attendance,
|
||||
Map<Long, AdminUserRespDTO> userMap,
|
||||
Map<Long, DeptRespDTO> deptMap) {
|
||||
MapUtils.findAndThen(userMap, attendance.getUserId(), user -> {
|
||||
attendance.setUserName(user.getNickname()).setDeptId(user.getDeptId());
|
||||
MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> attendance.setDeptName(dept.getName()));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接考勤周报响应的用户和部门信息
|
||||
*
|
||||
* @param report 考勤周报响应
|
||||
* @param userMap 用户 Map
|
||||
* @param deptMap 部门 Map
|
||||
*/
|
||||
private void fillAttendanceUserInfo(OaAttendanceWeekReportRespVO report,
|
||||
Map<Long, AdminUserRespDTO> userMap,
|
||||
Map<Long, DeptRespDTO> deptMap) {
|
||||
MapUtils.findAndThen(userMap, report.getUserId(), user -> {
|
||||
report.setUserName(user.getNickname()).setDeptId(user.getDeptId());
|
||||
MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> report.setDeptName(dept.getName()));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接考勤月报响应的用户和部门信息
|
||||
*
|
||||
* @param report 考勤月报响应
|
||||
* @param userMap 用户 Map
|
||||
* @param deptMap 部门 Map
|
||||
*/
|
||||
private void fillAttendanceUserInfo(OaAttendanceMonthReportRespVO report,
|
||||
Map<Long, AdminUserRespDTO> userMap,
|
||||
Map<Long, DeptRespDTO> deptMap) {
|
||||
MapUtils.findAndThen(userMap, report.getUserId(), user -> {
|
||||
report.setUserName(user.getNickname()).setDeptId(user.getDeptId());
|
||||
MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> report.setDeptName(dept.getName()));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.attendance.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - OA 考勤月报 Request VO")
|
||||
@Data
|
||||
public class OaAttendanceMonthReportReqVO {
|
||||
|
||||
@Schema(description = "年份", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026")
|
||||
@NotNull(message = "年份不能为空")
|
||||
@Min(value = 2000, message = "年份不能小于 2000")
|
||||
private Integer year;
|
||||
|
||||
@Schema(description = "月份", requiredMode = Schema.RequiredMode.REQUIRED, example = "9")
|
||||
@NotNull(message = "月份不能为空")
|
||||
@Min(value = 1, message = "月份不能小于 1")
|
||||
@Max(value = 12, message = "月份不能大于 12")
|
||||
private Integer month;
|
||||
|
||||
@Schema(description = "用户编号", example = "1")
|
||||
private Long userId;
|
||||
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.attendance.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@Schema(description = "管理后台 - OA 考勤月报 Response VO")
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class OaAttendanceMonthReportRespVO {
|
||||
|
||||
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "用户昵称", example = "芋道")
|
||||
private String userName;
|
||||
|
||||
@Schema(description = "部门编号", example = "100")
|
||||
private Long deptId;
|
||||
|
||||
@Schema(description = "部门名称", example = "研发部")
|
||||
private String deptName;
|
||||
|
||||
@Schema(description = "上班打卡次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
|
||||
private Integer clockInCount;
|
||||
|
||||
@Schema(description = "下班打卡次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "19")
|
||||
private Integer clockOutCount;
|
||||
|
||||
@Schema(description = "正常次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "36")
|
||||
private Integer normalCount;
|
||||
|
||||
@Schema(description = "迟到次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
private Integer lateCount;
|
||||
|
||||
@Schema(description = "早退次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer earlyCount;
|
||||
|
||||
@Schema(description = "请假天数,审批通过且开始时间在当月的申请天数合计", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
private Integer leaveDays;
|
||||
|
||||
@Schema(description = "出差天数,审批通过且开始时间在当月的申请天数合计", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
|
||||
private Integer travelDays;
|
||||
|
||||
@Schema(description = "旷工天数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer absentDays;
|
||||
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.attendance.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.oa.enums.attendance.OaAttendanceStatusEnum;
|
||||
import cn.iocoder.yudao.module.oa.enums.attendance.OaAttendanceTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
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 = "管理后台 - OA 考勤分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class OaAttendancePageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "用户编号", example = "1")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "考勤类型", example = "1")
|
||||
@InEnum(value = OaAttendanceTypeEnum.class, message = "考勤类型必须是 {value}")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "考勤状态", example = "1")
|
||||
@InEnum(value = OaAttendanceStatusEnum.class, message = "考勤状态必须是 {value}")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "考勤时间范围", example = "[\"2026-09-14 00:00:00\", \"2026-09-14 23:59:59\"]")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] attendanceTime;
|
||||
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.attendance.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - OA 考勤 Response VO")
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class OaAttendanceRespVO {
|
||||
|
||||
@Schema(description = "考勤记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "用户昵称", example = "芋道")
|
||||
private String userName;
|
||||
|
||||
@Schema(description = "部门编号", example = "100")
|
||||
private Long deptId;
|
||||
|
||||
@Schema(description = "部门名称", example = "研发部")
|
||||
private String deptName;
|
||||
|
||||
@Schema(description = "考勤类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "考勤状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "考勤时间", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64", example = "1789380000000")
|
||||
private LocalDateTime attendanceTime;
|
||||
|
||||
@Schema(description = "考勤 IP", example = "127.0.0.1")
|
||||
private String attendanceIp;
|
||||
|
||||
@Schema(description = "考勤备注", example = "正常打卡")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64", example = "1789347600000")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.attendance.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.oa.enums.attendance.OaAttendanceStatusEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - OA 考勤修改 Request VO")
|
||||
@Data
|
||||
public class OaAttendanceUpdateReqVO {
|
||||
|
||||
@Schema(description = "考勤记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "考勤记录编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "考勤状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "考勤状态不能为空")
|
||||
@InEnum(value = OaAttendanceStatusEnum.class, message = "考勤状态必须是 {value}")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "考勤备注", example = "因交通拥堵迟到")
|
||||
@Size(max = 500, message = "考勤备注不能超过 500 个字符")
|
||||
private String remark;
|
||||
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.attendance.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Schema(description = "管理后台 - OA 考勤周报 Request VO")
|
||||
@Data
|
||||
public class OaAttendanceWeekReportReqVO {
|
||||
|
||||
@Schema(description = "周开始日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-09-07")
|
||||
@NotNull(message = "周开始日期不能为空")
|
||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
|
||||
private LocalDate startDate;
|
||||
|
||||
@Schema(description = "用户编号", example = "1")
|
||||
private Long userId;
|
||||
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.attendance.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - OA 考勤周报 Response VO")
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class OaAttendanceWeekReportRespVO {
|
||||
|
||||
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "用户昵称", example = "芋道")
|
||||
private String userName;
|
||||
|
||||
@Schema(description = "部门编号", example = "100")
|
||||
private Long deptId;
|
||||
|
||||
@Schema(description = "部门名称", example = "研发部")
|
||||
private String deptName;
|
||||
|
||||
@Schema(description = "每日考勤列表", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<DailyAttendance> dailyAttendances;
|
||||
|
||||
@Schema(description = "管理后台 - OA 每日考勤 Response VO")
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public static class DailyAttendance {
|
||||
|
||||
@Schema(description = "日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-09-07")
|
||||
private LocalDate date;
|
||||
|
||||
@Schema(description = "上班考勤记录编号", example = "1024")
|
||||
private Long clockInId;
|
||||
|
||||
@Schema(description = "上班打卡时间", type = "integer", format = "int64", example = "1789347600000")
|
||||
private LocalDateTime clockInTime;
|
||||
|
||||
@Schema(description = "上班打卡状态", example = "1")
|
||||
private Integer clockInStatus;
|
||||
|
||||
@Schema(description = "下班考勤记录编号", example = "1025")
|
||||
private Long clockOutId;
|
||||
|
||||
@Schema(description = "下班打卡时间", type = "integer", format = "int64", example = "1789380000000")
|
||||
private LocalDateTime clockOutTime;
|
||||
|
||||
@Schema(description = "下班打卡状态", example = "1")
|
||||
private Integer clockOutStatus;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.contact;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.contact.vo.category.OaContactCategoryRespVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.contact.vo.category.OaContactCategorySaveReqVO;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.contact.OaContactCategoryDO;
|
||||
import cn.iocoder.yudao.module.oa.service.contact.OaContactCategoryService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - OA 联系人分类")
|
||||
@RestController
|
||||
@RequestMapping("/oa/contact-category")
|
||||
@Validated
|
||||
public class OaContactCategoryController {
|
||||
|
||||
@Resource
|
||||
private OaContactCategoryService contactCategoryService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建联系人分类")
|
||||
public CommonResult<Long> createContactCategory(
|
||||
@Valid @RequestBody OaContactCategorySaveReqVO createReqVO) {
|
||||
return success(contactCategoryService.createContactCategory(createReqVO, getLoginUserId()));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新联系人分类")
|
||||
public CommonResult<Boolean> updateContactCategory(
|
||||
@Valid @RequestBody OaContactCategorySaveReqVO updateReqVO) {
|
||||
contactCategoryService.updateContactCategory(updateReqVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除联系人分类")
|
||||
@Parameter(name = "id", description = "分类编号", required = true, example = "1024")
|
||||
public CommonResult<Boolean> deleteContactCategory(@RequestParam("id") Long id) {
|
||||
contactCategoryService.deleteContactCategory(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得联系人分类")
|
||||
@Parameter(name = "id", description = "分类编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:contact:query')")
|
||||
public CommonResult<OaContactCategoryRespVO> getContactCategory(@RequestParam("id") Long id) {
|
||||
OaContactCategoryDO category = contactCategoryService.getContactCategory(id, getLoginUserId());
|
||||
return success(BeanUtils.toBean(category, OaContactCategoryRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/simple-list")
|
||||
@Operation(summary = "获得分类精简列表")
|
||||
public CommonResult<List<OaContactCategoryRespVO>> getSimpleContactCategoryList() {
|
||||
List<OaContactCategoryDO> categories = contactCategoryService.getContactCategoryList(getLoginUserId());
|
||||
return success(convertList(categories, category -> new OaContactCategoryRespVO()
|
||||
.setId(category.getId()).setName(category.getName())));
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得联系人分类列表")
|
||||
@PreAuthorize("@ss.hasPermission('oa:contact:query')")
|
||||
public CommonResult<List<OaContactCategoryRespVO>> getContactCategoryList() {
|
||||
List<OaContactCategoryDO> categories = contactCategoryService.getContactCategoryList(getLoginUserId());
|
||||
return success(BeanUtils.toBean(categories, OaContactCategoryRespVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.contact;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.contact.vo.OaContactHandleReqVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.contact.vo.OaContactPageReqVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.contact.vo.OaContactRespVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.contact.vo.OaContactSaveReqVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.contact.vo.OaContactShareReqVO;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.contact.OaContactDO;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.contact.OaContactCategoryDO;
|
||||
import cn.iocoder.yudao.module.oa.service.contact.OaContactCategoryService;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.contact.OaContactShareDO;
|
||||
import cn.iocoder.yudao.module.oa.service.contact.OaContactService;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - OA 通讯录")
|
||||
@RestController
|
||||
@RequestMapping("/oa/contact")
|
||||
@Validated
|
||||
public class OaContactController {
|
||||
|
||||
@Resource
|
||||
private OaContactService contactService;
|
||||
@Resource
|
||||
private OaContactCategoryService contactCategoryService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
// ==================== 我的联系人 ====================
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建外部联系人")
|
||||
@PreAuthorize("@ss.hasPermission('oa:contact:create')")
|
||||
public CommonResult<Long> createContact(@Valid @RequestBody OaContactSaveReqVO createReqVO) {
|
||||
return success(contactService.createContact(createReqVO, getLoginUserId()));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新外部联系人")
|
||||
@PreAuthorize("@ss.hasPermission('oa:contact:update')")
|
||||
public CommonResult<Boolean> updateContact(@Valid @RequestBody OaContactSaveReqVO updateReqVO) {
|
||||
contactService.updateContact(updateReqVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除外部联系人")
|
||||
@Parameter(name = "id", description = "联系人编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:contact:delete')")
|
||||
public CommonResult<Boolean> deleteContact(@RequestParam("id") Long id) {
|
||||
contactService.deleteContact(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/share")
|
||||
@Operation(summary = "共享外部联系人")
|
||||
public CommonResult<Boolean> shareContact(@Valid @RequestBody OaContactShareReqVO shareReqVO) {
|
||||
contactService.shareContact(shareReqVO.getContactId(), shareReqVO.getUserIds(), getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
// ==================== 共享与我 ====================
|
||||
|
||||
@DeleteMapping("/delete-received")
|
||||
@Operation(summary = "删除接收到的共享联系人")
|
||||
@Parameter(name = "contactId", description = "联系人编号", required = true, example = "1024")
|
||||
public CommonResult<Boolean> deleteReceivedContact(@RequestParam("contactId") Long contactId) {
|
||||
contactService.deleteReceivedContact(contactId, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/handle-share")
|
||||
@Operation(summary = "处理外部联系人共享")
|
||||
public CommonResult<Boolean> handleContactShare(@Valid @RequestBody OaContactHandleReqVO handleReqVO) {
|
||||
contactService.handleContactShare(handleReqVO.getContactId(), handleReqVO.getCategoryId(), getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
// ==================== 公共查询 ====================
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得外部联系人详情")
|
||||
@Parameter(name = "id", description = "联系人编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:contact:query')")
|
||||
public CommonResult<OaContactRespVO> getContact(@RequestParam("id") Long id) {
|
||||
OaContactDO contact = contactService.getContact(id, getLoginUserId());
|
||||
return success(buildContactRespVO(contact));
|
||||
}
|
||||
|
||||
@GetMapping("/my-page")
|
||||
@Operation(summary = "获得我的联系人分页")
|
||||
@PreAuthorize("@ss.hasPermission('oa:contact:query')")
|
||||
public CommonResult<PageResult<OaContactRespVO>> getMyContactPage(@Valid OaContactPageReqVO pageReqVO) {
|
||||
PageResult<OaContactDO> pageResult = contactService.getMyContactPage(pageReqVO, getLoginUserId());
|
||||
return success(new PageResult<>(buildContactRespVOList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/received-page")
|
||||
@Operation(summary = "获得共享给我的联系人分页")
|
||||
@PreAuthorize("@ss.hasPermission('oa:contact:query')")
|
||||
public CommonResult<PageResult<OaContactRespVO>> getReceivedContactPage(@Valid OaContactPageReqVO pageReqVO) {
|
||||
PageResult<OaContactDO> pageResult = contactService.getReceivedContactPage(pageReqVO, getLoginUserId());
|
||||
return success(new PageResult<>(buildContactRespVOList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/shared-page")
|
||||
@Operation(summary = "获得我共享的联系人分页")
|
||||
@PreAuthorize("@ss.hasPermission('oa:contact:query')")
|
||||
public CommonResult<PageResult<OaContactRespVO>> getSharedContactPage(@Valid OaContactPageReqVO pageReqVO) {
|
||||
PageResult<OaContactShareDO> pageResult = contactService.getSharedContactPage(pageReqVO, getLoginUserId());
|
||||
List<OaContactDO> contacts = contactService.getContactList(convertSet(pageResult.getList(), OaContactShareDO::getContactId));
|
||||
// 拼接 VO
|
||||
Map<Long, OaContactRespVO> contactMap = convertMap(buildContactRespVOList(contacts), OaContactRespVO::getId);
|
||||
return success(new PageResult<>(convertList(pageResult.getList(), share -> {
|
||||
OaContactRespVO contact = BeanUtils.toBean(contactMap.get(share.getContactId()), OaContactRespVO.class);
|
||||
contact.setShare(CollUtil.findOne(contact.getShares(), item -> ObjectUtil.equal(item.getId(), share.getId())));
|
||||
return contact;
|
||||
}), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
// ==================== 拼接 VO ====================
|
||||
|
||||
/**
|
||||
* 拼接联系人详情
|
||||
*
|
||||
* @param contact 联系人
|
||||
* @return 联系人响应
|
||||
*/
|
||||
private OaContactRespVO buildContactRespVO(OaContactDO contact) {
|
||||
if (contact == null) {
|
||||
return null;
|
||||
}
|
||||
return CollUtil.getFirst(buildContactRespVOList(Collections.singletonList(contact)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接外部联系人响应列表
|
||||
*
|
||||
* @param contacts 联系人列表
|
||||
* @return 联系人响应列表
|
||||
*/
|
||||
private List<OaContactRespVO> buildContactRespVOList(List<OaContactDO> contacts) {
|
||||
if (CollUtil.isEmpty(contacts)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 1.1 批量查询共享关系
|
||||
Map<Long, List<OaContactShareDO>> contactShareListMap = contactService.getContactShareListMap(
|
||||
convertSet(contacts, OaContactDO::getId));
|
||||
List<OaContactShareDO> shares = convertListByFlatMap(contactShareListMap.values(), List::stream);
|
||||
// 1.2 批量查询创建人和接收人信息
|
||||
Set<Long> userIds = convertSet(contacts, contact -> NumberUtils.parseLong(contact.getCreator()));
|
||||
userIds.addAll(convertSet(shares, OaContactShareDO::getUserId));
|
||||
userIds.addAll(convertSet(shares, share -> NumberUtils.parseLong(share.getCreator())));
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
|
||||
// 1.3 批量查询创建人和接收人的分类
|
||||
Set<Long> categoryIds = convertSet(contacts, OaContactDO::getCategoryId);
|
||||
categoryIds.addAll(convertSet(shares, OaContactShareDO::getCategoryId));
|
||||
Map<Long, OaContactCategoryDO> categoryMap = contactCategoryService.getContactCategoryMap(categoryIds);
|
||||
Long loginUserId = getLoginUserId();
|
||||
// 2. 拼接展示字段,接收人仅能查看自己的共享关系
|
||||
return convertList(contacts, contactDO -> {
|
||||
OaContactRespVO contact = BeanUtils.toBean(contactDO, OaContactRespVO.class)
|
||||
.setOwnerUserId(NumberUtils.parseLong(contactDO.getCreator()));
|
||||
MapUtils.findAndThen(categoryMap, contact.getCategoryId(),
|
||||
category -> contact.setCategoryName(category.getName()));
|
||||
MapUtils.findAndThen(userMap, contact.getOwnerUserId(),
|
||||
user -> contact.setOwnerUserName(user.getNickname()));
|
||||
List<OaContactShareDO> contactShares = contactShareListMap.getOrDefault(contact.getId(),
|
||||
Collections.emptyList());
|
||||
// 仅展示本人发出的共享,不将初始化的本人关联计入共享人数
|
||||
List<OaContactShareDO> visibleContactShares = filterList(contactShares,
|
||||
share -> ObjectUtil.equal(NumberUtils.parseLong(share.getCreator()), loginUserId)
|
||||
&& ObjectUtil.notEqual(share.getUserId(), loginUserId));
|
||||
contact.setShares(convertList(visibleContactShares, share -> {
|
||||
OaContactRespVO.Share shareVO = BeanUtils.toBean(share, OaContactRespVO.Share.class);
|
||||
MapUtils.findAndThen(categoryMap, share.getCategoryId(),
|
||||
category -> shareVO.setCategoryName(category.getName()));
|
||||
MapUtils.findAndThen(userMap, NumberUtils.parseLong(share.getCreator()),
|
||||
user -> shareVO.setCreatorName(user.getNickname()));
|
||||
MapUtils.findAndThen(userMap, share.getUserId(),
|
||||
user -> shareVO.setUserName(user.getNickname()).setUserAvatar(user.getAvatar()));
|
||||
return shareVO;
|
||||
}));
|
||||
OaContactShareDO currentUserShare = CollUtil.findOne(contactShares,
|
||||
share -> share.getUserId().equals(loginUserId));
|
||||
if (currentUserShare != null) {
|
||||
MapUtils.findAndThen(userMap, NumberUtils.parseLong(currentUserShare.getCreator()),
|
||||
user -> contact.setSharerName(user.getNickname()));
|
||||
contact.setHandleStatus(currentUserShare.getHandleStatus())
|
||||
.setSharedCategoryId(currentUserShare.getCategoryId());
|
||||
MapUtils.findAndThen(categoryMap, currentUserShare.getCategoryId(),
|
||||
category -> contact.setSharedCategoryName(category.getName()));
|
||||
}
|
||||
return contact;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.contact.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - OA 联系人共享处理 Request VO")
|
||||
@Data
|
||||
public class OaContactHandleReqVO {
|
||||
|
||||
@Schema(description = "联系人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "联系人编号不能为空")
|
||||
private Long contactId;
|
||||
|
||||
@Schema(description = "接收人分类编号", example = "1024")
|
||||
private Long categoryId;
|
||||
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.contact.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Schema(description = "管理后台 - OA 外部联系人分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class OaContactPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "姓名、拼音、手机或公司名称关键字", example = "张三")
|
||||
private String keyword;
|
||||
|
||||
@Schema(description = "分类编号", example = "1024")
|
||||
private Long categoryId;
|
||||
|
||||
@Schema(description = "是否已处理", example = "false")
|
||||
private Boolean handleStatus;
|
||||
|
||||
@Schema(description = "姓名拼音首字母,空字符串表示全部", example = "A")
|
||||
@Pattern(regexp = "^[A-Z]?$", message = "姓名拼音首字母必须为 A-Z")
|
||||
private String alphabet;
|
||||
|
||||
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.contact.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - OA 外部联系人 Response VO")
|
||||
@Data
|
||||
public class OaContactRespVO {
|
||||
|
||||
@Schema(description = "联系人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "创建人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long ownerUserId;
|
||||
|
||||
@Schema(description = "创建人用户昵称", example = "芋道源码")
|
||||
private String ownerUserName;
|
||||
|
||||
@Schema(description = "分类编号", example = "1024")
|
||||
private Long categoryId;
|
||||
|
||||
@Schema(description = "分类名称", example = "客户")
|
||||
private String categoryName;
|
||||
|
||||
@Schema(description = "姓名", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "姓名拼音", example = "zhangsan")
|
||||
private String pinyin;
|
||||
|
||||
@Schema(description = "性别", example = "1")
|
||||
private Integer sex;
|
||||
|
||||
@Schema(description = "手机号码", example = "13800138000")
|
||||
private String mobile;
|
||||
|
||||
@Schema(description = "邮箱", example = "yudao@example.com")
|
||||
private String email;
|
||||
|
||||
@Schema(description = "地址", example = "上海市浦东新区世纪大道 100 号")
|
||||
private String address;
|
||||
|
||||
@Schema(description = "公司名称", example = "芋道科技有限公司")
|
||||
private String companyName;
|
||||
|
||||
@Schema(description = "公司电话", example = "021-12345678")
|
||||
private String companyPhone;
|
||||
|
||||
@Schema(description = "头像地址", example = "https://example.com/image.png")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "备注", example = "请提前联系行政部确认")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "共享记录列表")
|
||||
private List<Share> shares;
|
||||
|
||||
@Schema(description = "当前行的共享关系,仅我共享的列表返回")
|
||||
private Share share;
|
||||
|
||||
@Schema(description = "分享给当前用户的共享人昵称", example = "芋道源码")
|
||||
private String sharerName;
|
||||
|
||||
@Schema(description = "当前接收人的处理状态", example = "false")
|
||||
private Boolean handleStatus;
|
||||
|
||||
@Schema(description = "当前接收人的分类编号", example = "1024")
|
||||
private Long sharedCategoryId;
|
||||
|
||||
@Schema(description = "当前接收人的分类名称", example = "客户联系人")
|
||||
private String sharedCategoryName;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64", example = "1789347600000")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "管理后台 - OA 联系人共享 Response VO")
|
||||
@Data
|
||||
public static class Share {
|
||||
|
||||
@Schema(description = "共享记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "共享接收人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "实际共享人昵称", example = "张三")
|
||||
private String creatorName;
|
||||
|
||||
@Schema(description = "共享接收人用户昵称", example = "芋道源码")
|
||||
private String userName;
|
||||
|
||||
@Schema(description = "共享接收人用户头像", example = "https://example.com/avatar.png")
|
||||
private String userAvatar;
|
||||
|
||||
@Schema(description = "分类编号", example = "1024")
|
||||
private Long categoryId;
|
||||
|
||||
@Schema(description = "接收人的分类名称", example = "客户")
|
||||
private String categoryName;
|
||||
|
||||
@Schema(description = "处理状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
|
||||
private Boolean handleStatus;
|
||||
|
||||
@Schema(description = "共享时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1789347600000")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.contact.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.Mobile;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - OA 外部联系人新增/修改 Request VO")
|
||||
@Data
|
||||
public class OaContactSaveReqVO {
|
||||
|
||||
@Schema(description = "联系人编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "分类编号", example = "1024")
|
||||
private Long categoryId;
|
||||
|
||||
@Schema(description = "姓名", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
|
||||
@NotBlank(message = "姓名不能为空")
|
||||
@Size(max = 50, message = "姓名长度不能超过 50 个字符")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "性别", example = "1")
|
||||
private Integer sex;
|
||||
|
||||
@Schema(description = "手机号码", requiredMode = Schema.RequiredMode.REQUIRED, example = "13800138000")
|
||||
@NotBlank(message = "手机号码不能为空")
|
||||
@Mobile
|
||||
@Size(max = 20, message = "手机号码长度不能超过 20 个字符")
|
||||
private String mobile;
|
||||
|
||||
@Schema(description = "邮箱", requiredMode = Schema.RequiredMode.REQUIRED, example = "user@example.com")
|
||||
@NotBlank(message = "邮箱不能为空")
|
||||
@Email(message = "邮箱格式不正确")
|
||||
@Size(max = 100, message = "邮箱长度不能超过 100 个字符")
|
||||
private String email;
|
||||
|
||||
@Schema(description = "地址", example = "上海市浦东新区世纪大道 100 号")
|
||||
@Size(max = 255, message = "地址长度不能超过 255 个字符")
|
||||
private String address;
|
||||
|
||||
@Schema(description = "公司名称", example = "芋道科技有限公司")
|
||||
@Size(max = 100, message = "公司名称长度不能超过 100 个字符")
|
||||
private String companyName;
|
||||
|
||||
@Schema(description = "公司电话", example = "021-12345678")
|
||||
@Size(max = 30, message = "公司电话长度不能超过 30 个字符")
|
||||
private String companyPhone;
|
||||
|
||||
@Schema(description = "头像地址", example = "https://example.com/image.png")
|
||||
@Size(max = 512, message = "头像地址长度不能超过 512 个字符")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "备注", example = "请提前联系行政部确认")
|
||||
@Size(max = 500, message = "备注长度不能超过 500 个字符")
|
||||
private String remark;
|
||||
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.contact.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - OA 联系人共享 Request VO")
|
||||
@Data
|
||||
public class OaContactShareReqVO {
|
||||
|
||||
@Schema(description = "联系人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "联系人编号不能为空")
|
||||
private Long contactId;
|
||||
|
||||
@Schema(description = "共享接收人用户编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2]")
|
||||
@NotEmpty(message = "共享接收人不能为空")
|
||||
private List<@NotNull(message = "共享接收人编号不能为空") Long> userIds;
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.contact.vo.category;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - OA 联系人分类 Response VO")
|
||||
@Data
|
||||
public class OaContactCategoryRespVO {
|
||||
|
||||
@Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "客户")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "显示排序", example = "10")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "创建时间", type = "integer", format = "int64", example = "1789347600000")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.contact.vo.category;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - OA 联系人分类新增/修改 Request VO")
|
||||
@Data
|
||||
public class OaContactCategorySaveReqVO {
|
||||
|
||||
@Schema(description = "分类编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "客户")
|
||||
@NotBlank(message = "分类名称不能为空")
|
||||
@Size(max = 50, message = "分类名称长度不能超过 50 个字符")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "显示排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
@NotNull(message = "显示排序不能为空")
|
||||
private Integer sort;
|
||||
|
||||
}
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.discussion;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.discussion.OaDiscussionPageReqVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.discussion.OaDiscussionRespVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.discussion.OaDiscussionSaveReqVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.vote.OaVoteOptionRespVO;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.discussion.OaDiscussionDO;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.discussion.OaDiscussionLikeDO;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.discussion.OaDiscussionReplyDO;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.discussion.OaVoteOptionDO;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.discussion.OaVoteRecordDO;
|
||||
import cn.iocoder.yudao.module.oa.service.discussion.OaDiscussionService;
|
||||
import cn.iocoder.yudao.module.oa.service.discussion.OaDiscussionReplyService;
|
||||
import cn.iocoder.yudao.module.oa.service.discussion.OaDiscussionLikeService;
|
||||
import cn.iocoder.yudao.module.oa.service.discussion.OaDiscussionVoteService;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
|
||||
|
||||
@Tag(name = "管理后台 - OA 讨论")
|
||||
@RestController
|
||||
@RequestMapping("/oa/discussion")
|
||||
@Validated
|
||||
public class OaDiscussionController {
|
||||
|
||||
@Resource
|
||||
private OaDiscussionService discussionService;
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private OaDiscussionLikeService discussionLikeService;
|
||||
@Resource
|
||||
private OaDiscussionReplyService discussionReplyService;
|
||||
@Resource
|
||||
private OaDiscussionVoteService discussionVoteService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建讨论")
|
||||
@PreAuthorize("@ss.hasPermission('oa:discussion:create')")
|
||||
public CommonResult<Long> createDiscussion(@Valid @RequestBody OaDiscussionSaveReqVO createReqVO) {
|
||||
return success(discussionService.createDiscussion(createReqVO, getLoginUserId()));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新讨论")
|
||||
@PreAuthorize("@ss.hasPermission('oa:discussion:update')")
|
||||
public CommonResult<Boolean> updateDiscussion(@Valid @RequestBody OaDiscussionSaveReqVO updateReqVO) {
|
||||
discussionService.updateDiscussion(updateReqVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除讨论")
|
||||
@Parameter(name = "id", description = "讨论编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:discussion:delete')")
|
||||
public CommonResult<Boolean> deleteDiscussion(@RequestParam("id") Long id) {
|
||||
discussionService.deleteDiscussion(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得讨论详情")
|
||||
@Parameter(name = "visit", description = "是否记录本次访问,默认不记录", example = "true")
|
||||
@Parameter(name = "id", description = "讨论编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:discussion:query')")
|
||||
public CommonResult<OaDiscussionRespVO> getDiscussion(@RequestParam("id") Long id,
|
||||
@RequestParam(value = "visit", defaultValue = "false") Boolean visit) {
|
||||
// 1.1 查询讨论
|
||||
OaDiscussionDO discussion = discussionService.validateDiscussionExists(id);
|
||||
// 1.2 按需记录本次访问
|
||||
if (Boolean.TRUE.equals(visit)) {
|
||||
discussionService.increaseDiscussionVisitCount(id);
|
||||
discussion.setVisitCount(discussion.getVisitCount() + 1);
|
||||
}
|
||||
|
||||
// 2. 拼接讨论详情
|
||||
return success(buildDiscussionRespVO(discussion));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得讨论分页")
|
||||
@PreAuthorize("@ss.hasPermission('oa:discussion:query')")
|
||||
public CommonResult<PageResult<OaDiscussionRespVO>> getDiscussionPage(
|
||||
@Valid OaDiscussionPageReqVO pageReqVO) {
|
||||
PageResult<OaDiscussionDO> pageResult = discussionService.getDiscussionPage(pageReqVO);
|
||||
return success(new PageResult<>(buildDiscussionRespVOList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/manage-page")
|
||||
@Operation(summary = "获得管理范围内的讨论分页")
|
||||
@PreAuthorize("@ss.hasPermission('oa:discussion:query')")
|
||||
public CommonResult<PageResult<OaDiscussionRespVO>> getDiscussionManagePage(
|
||||
@Valid OaDiscussionPageReqVO pageReqVO) {
|
||||
PageResult<OaDiscussionDO> pageResult = discussionService.getDiscussionManagePage(pageReqVO, getLoginUserId());
|
||||
return success(new PageResult<>(buildDiscussionRespVOList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
// ==================== 拼接 VO ====================
|
||||
|
||||
/**
|
||||
* 拼接讨论详情
|
||||
*
|
||||
* @param discussion 讨论
|
||||
* @return 讨论响应
|
||||
*/
|
||||
private OaDiscussionRespVO buildDiscussionRespVO(OaDiscussionDO discussion) {
|
||||
if (discussion == null) {
|
||||
return null;
|
||||
}
|
||||
return CollUtil.getFirst(buildDiscussionRespVOList(Collections.singletonList(discussion)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接讨论响应列表
|
||||
*
|
||||
* @param discussions 讨论列表
|
||||
* @return 讨论响应列表
|
||||
*/
|
||||
private List<OaDiscussionRespVO> buildDiscussionRespVOList(List<OaDiscussionDO> discussions) {
|
||||
if (CollUtil.isEmpty(discussions)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 1.1 批量查询回复和点赞
|
||||
Set<Long> discussionIds = convertSet(discussions, OaDiscussionDO::getId);
|
||||
Map<Long, List<OaDiscussionReplyDO>> replyListMap = convertMultiMap(
|
||||
discussionReplyService.getDiscussionReplyList(discussionIds), OaDiscussionReplyDO::getDiscussionId);
|
||||
List<OaDiscussionLikeDO> discussionLikes = discussionLikeService.getDiscussionLikeList(discussionIds);
|
||||
Map<Long, List<OaDiscussionLikeDO>> likeListMap = convertMultiMap(discussionLikes,
|
||||
OaDiscussionLikeDO::getDiscussionId);
|
||||
// 1.2 批量查询投票选项和记录
|
||||
Map<Long, List<OaVoteOptionDO>> voteOptionListMap = convertMultiMap(
|
||||
discussionVoteService.getVoteOptionList(discussionIds), OaVoteOptionDO::getDiscussionId);
|
||||
List<OaVoteRecordDO> voteRecords = discussionVoteService.getVoteRecordList(discussionIds);
|
||||
Map<Long, List<OaVoteRecordDO>> voteRecordListMap = convertMultiMap(voteRecords,
|
||||
OaVoteRecordDO::getDiscussionId);
|
||||
// 1.3 批量查询发布人、点赞人和投票人
|
||||
Set<Long> userIds = convertSet(discussions, OaDiscussionDO::getUserId);
|
||||
userIds.addAll(convertSet(discussionLikes, OaDiscussionLikeDO::getUserId));
|
||||
userIds.addAll(convertSet(voteRecords, OaVoteRecordDO::getUserId));
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
|
||||
Long loginUserId = getLoginUserId();
|
||||
// 2. 转换并拼接讨论展示字段
|
||||
return BeanUtils.toBean(discussions, OaDiscussionRespVO.class, discussion -> {
|
||||
findAndThen(userMap, discussion.getUserId(), user -> discussion.setUserName(user.getNickname()));
|
||||
discussion.setReplyCount(CollUtil.size(replyListMap.get(discussion.getId())));
|
||||
List<OaDiscussionLikeDO> likes = likeListMap.getOrDefault(discussion.getId(), Collections.emptyList());
|
||||
discussion.setLikeCount(likes.size());
|
||||
discussion.setLiked(CollUtil.findOne(likes, like -> like.getUserId().equals(loginUserId)) != null);
|
||||
discussion.setLikeUserNames(convertList(likes,
|
||||
like -> getUserNickname(userMap, like.getUserId())));
|
||||
discussion.setVoteOptions(buildVoteOptionRespVOList(
|
||||
voteOptionListMap.getOrDefault(discussion.getId(), Collections.emptyList()),
|
||||
voteRecordListMap.getOrDefault(discussion.getId(), Collections.emptyList()), userMap, loginUserId));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接投票选项响应列表
|
||||
*
|
||||
* @param voteOptions 投票选项列表
|
||||
* @param voteRecords 投票记录列表
|
||||
* @param userMap 用户 Map
|
||||
* @param loginUserId 当前登录用户编号
|
||||
* @return 投票选项响应列表
|
||||
*/
|
||||
private List<OaVoteOptionRespVO> buildVoteOptionRespVOList(List<OaVoteOptionDO> voteOptions,
|
||||
List<OaVoteRecordDO> voteRecords,
|
||||
Map<Long, AdminUserRespDTO> userMap,
|
||||
Long loginUserId) {
|
||||
Map<Long, List<OaVoteRecordDO>> optionVoteRecordListMap = convertMultiMap(voteRecords,
|
||||
OaVoteRecordDO::getOptionId);
|
||||
return BeanUtils.toBean(voteOptions, OaVoteOptionRespVO.class, voteOption -> {
|
||||
voteOption.setVoteCount(0).setVoted(false).setVoterUserNames(Collections.emptyList());
|
||||
findAndThen(optionVoteRecordListMap, voteOption.getId(), records ->
|
||||
voteOption.setVoteCount(records.size())
|
||||
.setVoted(CollUtil.findOne(records, record -> record.getUserId().equals(loginUserId)) != null)
|
||||
.setVoterUserNames(convertList(records, record -> getUserNickname(userMap, record.getUserId()))));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得用户昵称
|
||||
*
|
||||
* @param userMap 用户 Map
|
||||
* @param userId 用户编号
|
||||
* @return 用户昵称
|
||||
*/
|
||||
private static String getUserNickname(Map<Long, AdminUserRespDTO> userMap, Long userId) {
|
||||
AdminUserRespDTO user = userMap.get(userId);
|
||||
return user != null ? user.getNickname() : null;
|
||||
}
|
||||
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.discussion;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.like.OaDiscussionLikeReqVO;
|
||||
import cn.iocoder.yudao.module.oa.service.discussion.OaDiscussionLikeService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - OA 讨论点赞")
|
||||
@RestController
|
||||
@RequestMapping("/oa/discussion-like")
|
||||
@Validated
|
||||
public class OaDiscussionLikeController {
|
||||
|
||||
@Resource
|
||||
private OaDiscussionLikeService discussionLikeService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "点赞讨论或主回复")
|
||||
public CommonResult<Boolean> createDiscussionLike(@Valid @RequestBody OaDiscussionLikeReqVO reqVO) {
|
||||
discussionLikeService.createDiscussionLike(reqVO.getDiscussionId(), reqVO.getReplyId(), getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "取消点赞讨论或主回复")
|
||||
public CommonResult<Boolean> deleteDiscussionLike(@Valid OaDiscussionLikeReqVO reqVO) {
|
||||
discussionLikeService.deleteDiscussionLike(reqVO.getDiscussionId(), reqVO.getReplyId(), getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.discussion;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.reply.OaDiscussionReplyPageReqVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.reply.OaDiscussionReplyRespVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.reply.OaDiscussionReplyCreateReqVO;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.discussion.OaDiscussionLikeDO;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.discussion.OaDiscussionReplyDO;
|
||||
import cn.iocoder.yudao.module.oa.service.discussion.OaDiscussionReplyService;
|
||||
import cn.iocoder.yudao.module.oa.service.discussion.OaDiscussionLikeService;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - OA 讨论回复")
|
||||
@RestController
|
||||
@RequestMapping("/oa/discussion-reply")
|
||||
@Validated
|
||||
public class OaDiscussionReplyController {
|
||||
|
||||
@Resource
|
||||
private OaDiscussionReplyService discussionReplyService;
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private OaDiscussionLikeService discussionLikeService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建讨论回复")
|
||||
public CommonResult<Long> createDiscussionReply(
|
||||
@Valid @RequestBody OaDiscussionReplyCreateReqVO createReqVO) {
|
||||
return success(discussionReplyService.createDiscussionReply(createReqVO, getLoginUserId()));
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除讨论回复")
|
||||
@Parameter(name = "id", description = "回复编号", required = true, example = "1024")
|
||||
public CommonResult<Boolean> deleteDiscussionReply(@RequestParam("id") Long id) {
|
||||
discussionReplyService.deleteDiscussionReply(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得讨论回复分页")
|
||||
@PreAuthorize("@ss.hasPermission('oa:discussion:query')")
|
||||
public CommonResult<PageResult<OaDiscussionReplyRespVO>> getDiscussionReplyPage(
|
||||
@Valid OaDiscussionReplyPageReqVO pageReqVO) {
|
||||
PageResult<OaDiscussionReplyDO> pageResult = discussionReplyService.getDiscussionReplyPage(pageReqVO);
|
||||
// 1. 批量查询当前页楼层内的子回复
|
||||
List<OaDiscussionReplyDO> replies = new ArrayList<>(pageResult.getList());
|
||||
replies.addAll(discussionReplyService.getDiscussionReplyDescendantList(pageResult.getList()));
|
||||
|
||||
// 2. 批量拼接展示字段,再按照父回复关系组织楼层
|
||||
List<OaDiscussionReplyRespVO> replyVOs = buildDiscussionReplyRespVOList(replies);
|
||||
Map<Long, OaDiscussionReplyRespVO> replyMap = convertMap(replyVOs, OaDiscussionReplyRespVO::getId);
|
||||
replyVOs.forEach(reply -> reply.setChildren(new ArrayList<>()));
|
||||
replyVOs.forEach(reply -> findAndThen(replyMap, reply.getParentId(),
|
||||
parent -> parent.getChildren().add(reply)));
|
||||
return success(new PageResult<>(convertList(pageResult.getList(),
|
||||
reply -> replyMap.get(reply.getId())), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
// ==================== 拼接 VO ====================
|
||||
|
||||
/**
|
||||
* 拼接讨论回复响应列表
|
||||
*
|
||||
* @param replies 讨论回复列表
|
||||
* @return 讨论回复响应列表
|
||||
*/
|
||||
private List<OaDiscussionReplyRespVO> buildDiscussionReplyRespVOList(List<OaDiscussionReplyDO> replies) {
|
||||
if (CollUtil.isEmpty(replies)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 1.1 批量查询回复点赞
|
||||
Map<Long, List<OaDiscussionLikeDO>> replyLikeListMap = discussionLikeService.getReplyLikeListMap(
|
||||
convertSet(replies, OaDiscussionReplyDO::getId,
|
||||
reply -> ObjectUtil.equal(reply.getParentId(), OaDiscussionReplyDO.PARENT_ID_ROOT)));
|
||||
// 1.2 批量查询回复人、被回复人和点赞人
|
||||
Set<Long> userIds = convertSetByFlatMap(replies,
|
||||
reply -> Stream.of(reply.getUserId(), reply.getReplyUserId()));
|
||||
userIds.addAll(convertSetByFlatMap(replyLikeListMap.values(),
|
||||
likes -> likes.stream().map(OaDiscussionLikeDO::getUserId)));
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
|
||||
Long loginUserId = getLoginUserId();
|
||||
|
||||
// 2. 转换并拼接回复展示字段
|
||||
return BeanUtils.toBean(replies, OaDiscussionReplyRespVO.class, reply -> {
|
||||
findAndThen(userMap, reply.getUserId(), user -> reply.setUserName(user.getNickname()));
|
||||
findAndThen(userMap, reply.getReplyUserId(), user -> reply.setReplyUserName(user.getNickname()));
|
||||
List<OaDiscussionLikeDO> likes = replyLikeListMap.getOrDefault(reply.getId(), Collections.emptyList());
|
||||
reply.setLikeCount(likes.size())
|
||||
.setLiked(CollUtil.findOne(likes, like -> like.getUserId().equals(loginUserId)) != null)
|
||||
.setLikeUserNames(convertList(likes, like -> {
|
||||
AdminUserRespDTO user = userMap.get(like.getUserId());
|
||||
return user != null ? user.getNickname() : null;
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.discussion;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.vote.OaDiscussionVoteReqVO;
|
||||
import cn.iocoder.yudao.module.oa.service.discussion.OaDiscussionVoteService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - OA 讨论投票")
|
||||
@RestController
|
||||
@RequestMapping("/oa/discussion-vote")
|
||||
@Validated
|
||||
public class OaDiscussionVoteController {
|
||||
|
||||
@Resource
|
||||
private OaDiscussionVoteService discussionVoteService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "参与讨论投票")
|
||||
public CommonResult<Boolean> voteDiscussion(@Valid @RequestBody OaDiscussionVoteReqVO voteReqVO) {
|
||||
discussionVoteService.voteDiscussion(voteReqVO.getDiscussionId(), voteReqVO.getOptionIds(), getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.discussion;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.SortablePageParam;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.oa.enums.discussion.OaDiscussionTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
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 = "管理后台 - OA 讨论分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class OaDiscussionPageReqVO extends SortablePageParam {
|
||||
|
||||
@Schema(description = "标题", example = "产品方案讨论")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "讨论类型", example = "1")
|
||||
@InEnum(value = OaDiscussionTypeEnum.class, message = "讨论类型必须是 {value}")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "发布人用户编号", example = "1")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "创建时间范围", example = "[\"2026-09-14 00:00:00\", \"2026-09-14 23:59:59\"]")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.discussion;
|
||||
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.vote.OaVoteOptionRespVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - OA 讨论 Response VO")
|
||||
@Data
|
||||
public class OaDiscussionRespVO {
|
||||
|
||||
@Schema(description = "讨论编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "发布人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "发布人用户昵称", example = "芋道源码")
|
||||
private String userName;
|
||||
|
||||
@Schema(description = "讨论类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "下周团队会议时间征集")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "内容", example = "请大家反馈下周团队会议的时间安排。")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "附件地址列表", example = "[\"https://example.com/file.pdf\"]")
|
||||
private List<String> fileUrls;
|
||||
|
||||
@Schema(description = "访问次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Integer visitCount;
|
||||
|
||||
@Schema(description = "回复数", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
|
||||
private Integer replyCount;
|
||||
|
||||
@Schema(description = "点赞数", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
|
||||
private Integer likeCount;
|
||||
|
||||
@Schema(description = "当前用户是否已点赞", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
|
||||
private Boolean liked;
|
||||
|
||||
@Schema(description = "点赞人用户昵称列表", example = "[\"张三\", \"李四\"]")
|
||||
private List<String> likeUserNames;
|
||||
|
||||
@Schema(description = "投票是否允许多选", example = "false")
|
||||
private Boolean voteMultiple;
|
||||
|
||||
@Schema(description = "投票开始时间", type = "integer", format = "int64", example = "1789347600000")
|
||||
private LocalDateTime voteStartTime;
|
||||
|
||||
@Schema(description = "投票结束时间", type = "integer", format = "int64", example = "1789380000000")
|
||||
private LocalDateTime voteEndTime;
|
||||
|
||||
@Schema(description = "投票选项列表")
|
||||
private List<OaVoteOptionRespVO> voteOptions;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64", example = "1789347600000")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.discussion;
|
||||
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.vote.OaVoteOptionSaveReqVO;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.oa.enums.discussion.OaDiscussionTypeEnum;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - OA 讨论新增/修改 Request VO")
|
||||
@Data
|
||||
public class OaDiscussionSaveReqVO {
|
||||
|
||||
@Schema(description = "讨论编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "讨论类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "讨论类型不能为空")
|
||||
@InEnum(value = OaDiscussionTypeEnum.class, message = "讨论类型必须是 {value}")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "下周团队会议时间征集")
|
||||
@NotBlank(message = "标题不能为空")
|
||||
@Size(max = 255, message = "标题长度不能超过 255 个字符")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "内容", example = "请大家反馈下周团队会议的时间安排。")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "附件地址列表", example = "[\"https://example.com/file.pdf\"]")
|
||||
private List<@NotBlank(message = "附件地址不能为空") @Size(max = 512, message = "附件地址长度不能超过 512 个字符") String> fileUrls;
|
||||
|
||||
@Schema(description = "投票是否允许多选", example = "false")
|
||||
private Boolean voteMultiple;
|
||||
|
||||
@Schema(description = "投票开始时间", type = "integer", format = "int64", example = "1789347600000")
|
||||
private LocalDateTime voteStartTime;
|
||||
|
||||
@Schema(description = "投票结束时间", type = "integer", format = "int64", example = "1789380000000")
|
||||
private LocalDateTime voteEndTime;
|
||||
|
||||
@Schema(description = "投票选项列表")
|
||||
@Valid
|
||||
private List<@NotNull(message = "投票选项不能为空") OaVoteOptionSaveReqVO> voteOptions;
|
||||
|
||||
@JsonIgnore
|
||||
@AssertTrue(message = "投票至少需要两个选项,并设置正确的开始和结束时间")
|
||||
@Schema(hidden = true)
|
||||
public boolean isVoteConfigValid() {
|
||||
if (ObjectUtil.notEqual(OaDiscussionTypeEnum.VOTE.getType(), type)) {
|
||||
return true;
|
||||
}
|
||||
if (voteMultiple == null || voteStartTime == null || voteEndTime == null) {
|
||||
return false;
|
||||
}
|
||||
return voteEndTime.isAfter(voteStartTime) && CollUtil.size(voteOptions) >= 2;
|
||||
}
|
||||
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.like;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - OA 讨论点赞 Request VO")
|
||||
@Data
|
||||
public class OaDiscussionLikeReqVO {
|
||||
|
||||
@Schema(description = "讨论编号,与回复编号必须且只能填写一个", example = "1024")
|
||||
private Long discussionId;
|
||||
|
||||
@Schema(description = "回复编号,与讨论编号必须且只能填写一个", example = "2048")
|
||||
private Long replyId;
|
||||
|
||||
@JsonIgnore
|
||||
@AssertTrue(message = "讨论编号和回复编号必须且只能填写一个")
|
||||
@Schema(hidden = true)
|
||||
public boolean isTargetValid() {
|
||||
if (discussionId != null) {
|
||||
return replyId == null;
|
||||
}
|
||||
return replyId != null;
|
||||
}
|
||||
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.reply;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - OA 讨论回复新增 Request VO")
|
||||
@Data
|
||||
public class OaDiscussionReplyCreateReqVO {
|
||||
|
||||
@Schema(description = "讨论编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "讨论编号不能为空")
|
||||
private Long discussionId;
|
||||
|
||||
@Schema(description = "父回复编号", example = "0")
|
||||
private Long parentId;
|
||||
|
||||
@Schema(description = "回复内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "已收到,将按时完成。")
|
||||
@NotBlank(message = "回复内容不能为空")
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 楼内评论对齐源端 255 字限制,根回复使用 TEXT 正文。
|
||||
*
|
||||
* @return 评论长度是否合法
|
||||
*/
|
||||
@AssertTrue(message = "楼内评论不能超过 255 个字符")
|
||||
@JsonIgnore
|
||||
public boolean isCommentLengthValid() {
|
||||
return parentId == null || parentId == 0L || content == null || content.length() <= 255;
|
||||
}
|
||||
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.reply;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.SortablePageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Schema(description = "管理后台 - OA 讨论回复分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class OaDiscussionReplyPageReqVO extends SortablePageParam {
|
||||
|
||||
@Schema(description = "讨论编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "讨论编号不能为空")
|
||||
private Long discussionId;
|
||||
|
||||
@Schema(description = "回复人用户编号", example = "1")
|
||||
private Long userId;
|
||||
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.reply;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - OA 讨论回复 Response VO")
|
||||
@Data
|
||||
public class OaDiscussionReplyRespVO {
|
||||
|
||||
@Schema(description = "回复编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "讨论编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
||||
private Long discussionId;
|
||||
|
||||
@Schema(description = "回复人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "回复人用户昵称", example = "芋道源码")
|
||||
private String userName;
|
||||
|
||||
@Schema(description = "父回复编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
private Long parentId;
|
||||
|
||||
@Schema(description = "被回复人用户编号", example = "2")
|
||||
private Long replyUserId;
|
||||
|
||||
@Schema(description = "被回复人用户昵称", example = "管理员")
|
||||
private String replyUserName;
|
||||
|
||||
@Schema(description = "回复内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "已收到,将按时完成。")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "点赞数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Integer likeCount;
|
||||
|
||||
@Schema(description = "当前用户是否已点赞", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
|
||||
private Boolean liked;
|
||||
|
||||
@Schema(description = "点赞人用户昵称列表", example = "[\"张三\", \"李四\"]")
|
||||
private List<String> likeUserNames;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64", example = "1789347600000")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "楼层内的子回复")
|
||||
private List<OaDiscussionReplyRespVO> children;
|
||||
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.vote;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - OA 讨论投票 Request VO")
|
||||
@Data
|
||||
public class OaDiscussionVoteReqVO {
|
||||
|
||||
@Schema(description = "讨论编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "讨论编号不能为空")
|
||||
private Long discussionId;
|
||||
|
||||
@Schema(description = "投票选项编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2]")
|
||||
@NotEmpty(message = "投票选项不能为空")
|
||||
private List<@NotNull(message = "投票选项编号不能为空") Long> optionIds;
|
||||
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.vote;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - OA 投票选项 Response VO")
|
||||
@Data
|
||||
public class OaVoteOptionRespVO {
|
||||
|
||||
@Schema(description = "投票选项编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "选项标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "周五下午")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "选项颜色", example = "#409EFF")
|
||||
private String color;
|
||||
|
||||
@Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "投票数", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
|
||||
private Integer voteCount;
|
||||
|
||||
@Schema(description = "当前用户是否已选择", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
|
||||
private Boolean voted;
|
||||
|
||||
@Schema(description = "投票人用户昵称列表", example = "[\"张三\", \"李四\"]")
|
||||
private List<String> voterUserNames;
|
||||
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.vote;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - OA 投票选项新增/修改 Request VO")
|
||||
@Data
|
||||
public class OaVoteOptionSaveReqVO {
|
||||
|
||||
@Schema(description = "选项标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "周五下午")
|
||||
@NotBlank(message = "投票选项标题不能为空")
|
||||
@Size(max = 200, message = "投票选项标题长度不能超过 200 个字符")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "选项颜色", example = "#409EFF")
|
||||
@Size(max = 20, message = "投票选项颜色长度不能超过 20 个字符")
|
||||
private String color;
|
||||
|
||||
@Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
@NotNull(message = "显示顺序不能为空")
|
||||
private Integer sort;
|
||||
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.file;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.oa.service.file.OaFileFavoriteService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - 云盘收藏")
|
||||
@RestController
|
||||
@RequestMapping("/oa/file-favorite")
|
||||
@Validated
|
||||
public class OaFileFavoriteController {
|
||||
|
||||
@Resource
|
||||
private OaFileFavoriteService fileFavoriteService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "收藏文件")
|
||||
@Parameter(name = "nodeId", description = "节点编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:file:query')")
|
||||
public CommonResult<Boolean> createFileFavorite(@RequestParam("nodeId") Long nodeId) {
|
||||
fileFavoriteService.createFileFavorite(nodeId, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "取消收藏文件")
|
||||
@Parameter(name = "nodeId", description = "节点编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:file:query')")
|
||||
public CommonResult<Boolean> deleteFileFavorite(@RequestParam("nodeId") Long nodeId) {
|
||||
fileFavoriteService.deleteFileFavorite(nodeId, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.file;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.file.vo.node.*;
|
||||
import cn.iocoder.yudao.module.oa.service.file.OaFileNodeService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - 云盘文件")
|
||||
@RestController
|
||||
@RequestMapping("/oa/file-node")
|
||||
@Validated
|
||||
public class OaFileNodeController {
|
||||
|
||||
@Resource
|
||||
private OaFileNodeService fileNodeService;
|
||||
|
||||
@GetMapping("/get-storage")
|
||||
@Operation(summary = "获得本人云盘空间")
|
||||
@PreAuthorize("@ss.hasPermission('oa:file:query')")
|
||||
public CommonResult<OaFileStorageRespVO> getFileStorage() {
|
||||
return success(fileNodeService.getFileStorage(getLoginUserId()));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得云盘文件分页")
|
||||
@PreAuthorize("@ss.hasPermission('oa:file:query')")
|
||||
public CommonResult<PageResult<OaFileNodeRespVO>> getFileNodePage(@Valid OaFileNodePageReqVO pageReqVO) {
|
||||
return success(fileNodeService.getFileNodePage(pageReqVO, getLoginUserId()));
|
||||
}
|
||||
|
||||
@GetMapping("/directory-list")
|
||||
@Operation(summary = "获得本人可用目录列表")
|
||||
@PreAuthorize("@ss.hasPermission('oa:file:query')")
|
||||
public CommonResult<List<OaFileNodeRespVO>> getFileDirectoryList() {
|
||||
return success(fileNodeService.getFileDirectoryList(getLoginUserId()));
|
||||
}
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建云盘文件或目录")
|
||||
@PreAuthorize("@ss.hasPermission('oa:file:create')")
|
||||
public CommonResult<Long> createFileNode(@Valid @RequestBody OaFileNodeSaveReqVO reqVO) {
|
||||
return success(fileNodeService.createFileNode(reqVO, getLoginUserId()));
|
||||
}
|
||||
|
||||
@PutMapping("/update-name")
|
||||
@Operation(summary = "重命名云盘文件")
|
||||
@PreAuthorize("@ss.hasPermission('oa:file:update')")
|
||||
public CommonResult<Boolean> updateFileNodeName(@Valid @RequestBody OaFileNodeRenameReqVO reqVO) {
|
||||
fileNodeService.updateFileNodeName(reqVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/update-parent")
|
||||
@Operation(summary = "移动云盘文件")
|
||||
@PreAuthorize("@ss.hasPermission('oa:file:update')")
|
||||
public CommonResult<Boolean> updateFileNodeParent(@Valid @RequestBody OaFileNodeMoveReqVO reqVO) {
|
||||
fileNodeService.updateFileNodeParent(reqVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/copy")
|
||||
@Operation(summary = "复制云盘文件或目录")
|
||||
@PreAuthorize("@ss.hasPermission('oa:file:create')")
|
||||
public CommonResult<Long> copyFileNode(@Valid @RequestBody OaFileNodeCopyReqVO reqVO) {
|
||||
return success(fileNodeService.copyFileNode(reqVO, getLoginUserId()));
|
||||
}
|
||||
|
||||
@PutMapping("/recycle")
|
||||
@Operation(summary = "移入回收站")
|
||||
@Parameter(name = "id", description = "节点编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:file:delete')")
|
||||
public CommonResult<Boolean> recycleFileNode(@RequestParam("id") Long id) {
|
||||
fileNodeService.recycleFileNode(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/restore")
|
||||
@Operation(summary = "恢复回收站文件")
|
||||
@Parameter(name = "id", description = "节点编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:file:delete')")
|
||||
public CommonResult<Boolean> restoreFileNode(@RequestParam("id") Long id) {
|
||||
fileNodeService.restoreFileNode(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "彻底删除回收站文件")
|
||||
@Parameter(name = "id", description = "节点编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:file:delete')")
|
||||
public CommonResult<Boolean> deleteFileNode(@RequestParam("id") Long id) {
|
||||
fileNodeService.deleteFileNode(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得文件详情")
|
||||
@Parameter(name = "id", description = "节点编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:file:query')")
|
||||
public CommonResult<OaFileNodeRespVO> getFileNode(@RequestParam("id") Long id) {
|
||||
return success(fileNodeService.getFileNode(id, getLoginUserId()));
|
||||
}
|
||||
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.file;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.file.vo.permission.OaFilePermissionRespVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.file.vo.permission.OaFilePermissionSaveReqVO;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.file.OaFilePermissionDO;
|
||||
import cn.iocoder.yudao.module.oa.service.file.OaFilePermissionService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - 云盘共享权限")
|
||||
@RestController
|
||||
@RequestMapping("/oa/file-permission")
|
||||
@Validated
|
||||
public class OaFilePermissionController {
|
||||
|
||||
@Resource
|
||||
private OaFilePermissionService filePermissionService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得文件共享权限")
|
||||
@Parameter(name = "nodeId", description = "节点编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:file:share')")
|
||||
public CommonResult<List<OaFilePermissionRespVO>> getFilePermissionList(@RequestParam("nodeId") Long nodeId) {
|
||||
List<OaFilePermissionDO> permissions = filePermissionService.getFilePermissionList(nodeId, getLoginUserId());
|
||||
return success(BeanUtils.toBean(permissions, OaFilePermissionRespVO.class));
|
||||
}
|
||||
|
||||
@PostMapping("/save")
|
||||
@Operation(summary = "保存文件共享权限")
|
||||
@PreAuthorize("@ss.hasPermission('oa:file:share')")
|
||||
public CommonResult<Long> saveFilePermission(@Valid @RequestBody OaFilePermissionSaveReqVO reqVO) {
|
||||
return success(filePermissionService.saveFilePermission(reqVO, getLoginUserId()));
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "取消文件共享权限")
|
||||
@Parameter(name = "id", description = "权限编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:file:share')")
|
||||
public CommonResult<Boolean> deleteFilePermission(@RequestParam("id") Long id) {
|
||||
filePermissionService.deleteFilePermission(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.file.vo.node;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 云盘复制 Request VO")
|
||||
@Data
|
||||
public class OaFileNodeCopyReqVO {
|
||||
|
||||
@Schema(description = "节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "节点编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "目标目录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "目标目录编号不能为空")
|
||||
private Long parentId;
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.file.vo.node;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 云盘移动 Request VO")
|
||||
@Data
|
||||
public class OaFileNodeMoveReqVO {
|
||||
|
||||
@Schema(description = "节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "节点编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "目标目录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "目标目录编号不能为空")
|
||||
private Long parentId;
|
||||
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.file.vo.node;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.oa.enums.file.OaFileCategoryEnum;
|
||||
import cn.iocoder.yudao.module.oa.enums.file.OaFileScopeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
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)
|
||||
public class OaFileNodePageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "列表范围:my、shared、favorite、recycle", requiredMode = Schema.RequiredMode.REQUIRED, example = "my")
|
||||
@NotBlank(message = "列表范围不能为空")
|
||||
@InEnum(value = OaFileScopeEnum.class, message = "列表范围必须是 {value}")
|
||||
private String scope;
|
||||
|
||||
@Schema(description = "父目录编号,不传时查询范围内全部文件", example = "1024")
|
||||
private Long parentId;
|
||||
|
||||
@Schema(description = "名称", example = "办公用品盘点结果.pdf")
|
||||
@Size(max = 255, message = "名称长度不能超过 255 个字符")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "文件分类", example = "1")
|
||||
@InEnum(value = OaFileCategoryEnum.class, message = "文件分类必须是 {value}")
|
||||
private Integer category;
|
||||
|
||||
@Schema(description = "创建时间范围", example = "[\"2026-09-14 00:00:00\", \"2026-09-14 23:59:59\"]")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.file.vo.node;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.*;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 云盘重命名 Request VO")
|
||||
@Data
|
||||
public class OaFileNodeRenameReqVO {
|
||||
|
||||
@Schema(description = "节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "节点编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "办公用品盘点结果.pdf")
|
||||
@NotBlank(message = "名称不能为空")
|
||||
@Size(max = 255, message = "名称长度不能超过 255 个字符")
|
||||
private String name;
|
||||
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.file.vo.node;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 云盘文件 Response VO")
|
||||
@Data
|
||||
public class OaFileNodeRespVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "父目录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long parentId;
|
||||
|
||||
@Schema(description = "节点类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "办公用品盘点结果.pdf")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "临时文件地址,仅详情中具有下载权限时返回", example = "https://example.com/file.pdf")
|
||||
private String url;
|
||||
|
||||
@Schema(description = "扩展名", example = "pdf")
|
||||
private String extension;
|
||||
|
||||
@Schema(description = "文件分类", example = "1")
|
||||
private Integer category;
|
||||
|
||||
@Schema(description = "大小,单位字节", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long size;
|
||||
|
||||
@Schema(description = "节点状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "创建人", example = "1024")
|
||||
private String creator;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64", example = "1789347600000")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64", example = "1789347600000")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
@Schema(description = "当前用户权限级别", example = "1")
|
||||
private Integer level;
|
||||
|
||||
@Schema(description = "是否收藏", example = "false")
|
||||
private Boolean favorite;
|
||||
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.file.vo.node;
|
||||
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.oa.enums.file.OaFileNodeTypeEnum;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.*;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 云盘文件新增 Request VO")
|
||||
@Data
|
||||
public class OaFileNodeSaveReqVO {
|
||||
|
||||
@Schema(description = "父目录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "父目录编号不能为空")
|
||||
private Long parentId;
|
||||
|
||||
@Schema(description = "节点类型:0 文件夹、1 文件", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
@NotNull(message = "节点类型不能为空")
|
||||
@InEnum(value = OaFileNodeTypeEnum.class, message = "节点类型必须是 {value}")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "节点名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "办公用品盘点结果.pdf")
|
||||
@NotBlank(message = "节点名称不能为空")
|
||||
@Size(max = 255, message = "节点名称长度不能超过 255 个字符")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "上传文件地址,文件夹不传", example = "https://example.com/file.pdf")
|
||||
@Size(max = 2048, message = "文件地址长度不能超过 2048 个字符")
|
||||
private String url;
|
||||
|
||||
@Schema(description = "文件大小,单位字节,文件夹不传", example = "1024")
|
||||
private Long size;
|
||||
|
||||
@AssertTrue(message = "文件地址不能为空")
|
||||
@JsonIgnore
|
||||
public boolean isUrlValid() {
|
||||
return ObjUtil.notEqual(type, OaFileNodeTypeEnum.FILE.getType()) || StrUtil.isNotBlank(url);
|
||||
}
|
||||
|
||||
@AssertTrue(message = "文件大小不能为空且不能小于 0")
|
||||
@JsonIgnore
|
||||
public boolean isSizeValid() {
|
||||
return ObjUtil.notEqual(type, OaFileNodeTypeEnum.FILE.getType()) || (size != null && size >= 0);
|
||||
}
|
||||
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.file.vo.node;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 云盘空间 Response VO")
|
||||
@Data
|
||||
public class OaFileStorageRespVO {
|
||||
|
||||
@Schema(description = "已用容量,单位字节", example = "1024")
|
||||
private Long usedSize;
|
||||
|
||||
@Schema(description = "总容量,单位字节", example = "1073741824")
|
||||
private Long totalSize;
|
||||
|
||||
@Schema(description = "本人正常文件数量,不含目录和回收站", example = "1")
|
||||
private Long fileCount;
|
||||
|
||||
@Schema(description = "本人有效共享节点数量,按文件或目录去重", example = "1")
|
||||
private Long sharedCount;
|
||||
|
||||
@Schema(description = "收到的共享入口数量,不重复统计子节点", example = "1")
|
||||
private Long receivedCount;
|
||||
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.file.vo.permission;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 云盘共享权限 Response VO")
|
||||
@Data
|
||||
public class OaFilePermissionRespVO {
|
||||
|
||||
@Schema(description = "权限编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long nodeId;
|
||||
|
||||
@Schema(description = "主体类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer subjectType;
|
||||
|
||||
@Schema(description = "主体编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long subjectId;
|
||||
|
||||
@Schema(description = "权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer level;
|
||||
|
||||
@Schema(description = "是否继承", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
|
||||
private Boolean inherit;
|
||||
|
||||
@Schema(description = "到期时间", type = "integer", format = "int64", example = "1789347600000")
|
||||
private LocalDateTime expireTime;
|
||||
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.file.vo.permission;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.oa.enums.file.OaFilePermissionLevelEnum;
|
||||
import cn.iocoder.yudao.module.oa.enums.file.OaFileSubjectTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 云盘共享权限 Request VO")
|
||||
@Data
|
||||
public class OaFilePermissionSaveReqVO {
|
||||
|
||||
@Schema(description = "节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "节点编号不能为空")
|
||||
private Long nodeId;
|
||||
|
||||
@Schema(description = "共享主体", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "共享主体类型不能为空")
|
||||
@InEnum(value = OaFileSubjectTypeEnum.class, message = "共享主体类型必须是 {value}")
|
||||
private Integer subjectType;
|
||||
|
||||
@Schema(description = "主体编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "主体编号不能为空")
|
||||
private Long subjectId;
|
||||
|
||||
@Schema(description = "权限", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "权限级别不能为空")
|
||||
@InEnum(value = OaFilePermissionLevelEnum.class, message = "权限级别必须是 {value}")
|
||||
private Integer level;
|
||||
|
||||
@Schema(description = "是否继承到子目录及文件", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
|
||||
@NotNull(message = "是否继承不能为空")
|
||||
private Boolean inherit;
|
||||
|
||||
@Schema(description = "到期时间", type = "integer", format = "int64", example = "1789347600000")
|
||||
@Future(message = "到期时间必须晚于当前时间")
|
||||
private LocalDateTime expireTime;
|
||||
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.leave;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.leave.vo.OaLeaveApplySaveReqVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.leave.vo.OaLeaveApplySubmitReqVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.leave.vo.OaLeaveApplyPageReqVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.leave.vo.OaLeaveApplyRespVO;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.leave.OaLeaveApplyDO;
|
||||
import cn.iocoder.yudao.module.oa.service.leave.OaLeaveApplyService;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - 请假申请")
|
||||
@RestController
|
||||
@RequestMapping("/oa/leave-apply")
|
||||
@Validated
|
||||
public class OaLeaveApplyController {
|
||||
|
||||
@Resource
|
||||
private OaLeaveApplyService leaveApplyService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建请假申请草稿")
|
||||
@PreAuthorize("@ss.hasPermission('oa:leave-apply:create')")
|
||||
public CommonResult<Long> createLeaveApply(@Valid @RequestBody OaLeaveApplySaveReqVO createReqVO) {
|
||||
return success(leaveApplyService.createLeaveApply(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新请假申请草稿")
|
||||
@PreAuthorize("@ss.hasPermission('oa:leave-apply:create')")
|
||||
public CommonResult<Boolean> updateLeaveApply(@Valid @RequestBody OaLeaveApplySaveReqVO saveReqVO) {
|
||||
leaveApplyService.updateLeaveApply(saveReqVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/submit")
|
||||
@Operation(summary = "提交请假申请")
|
||||
@PreAuthorize("@ss.hasPermission('oa:leave-apply:create')")
|
||||
public CommonResult<Boolean> submitLeaveApply(@Valid @RequestBody OaLeaveApplySubmitReqVO submitReqVO) {
|
||||
leaveApplyService.submitLeaveApply(submitReqVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得本人请假申请分页")
|
||||
@PreAuthorize("@ss.hasPermission('oa:leave-apply:query')")
|
||||
public CommonResult<PageResult<OaLeaveApplyRespVO>> getLeaveApplyPage(@Valid OaLeaveApplyPageReqVO pageReqVO) {
|
||||
PageResult<OaLeaveApplyDO> page = leaveApplyService.getLeaveApplyPage(getLoginUserId(), pageReqVO);
|
||||
return success(new PageResult<>(buildLeaveApplyRespVOList(page.getList()), page.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得请假申请详情")
|
||||
@Parameter(name = "id", description = "申请编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:leave-apply:query')")
|
||||
public CommonResult<OaLeaveApplyRespVO> getLeaveApply(@RequestParam("id") Long id) {
|
||||
OaLeaveApplyDO application = leaveApplyService.getLeaveApply(id);
|
||||
return success(buildLeaveApplyRespVO(application));
|
||||
}
|
||||
|
||||
// ==================== 拼接 VO ====================
|
||||
|
||||
/**
|
||||
* 拼接请假申请详情
|
||||
*
|
||||
* @param application 申请
|
||||
* @return 请假申请响应
|
||||
*/
|
||||
private OaLeaveApplyRespVO buildLeaveApplyRespVO(OaLeaveApplyDO application) {
|
||||
if (application == null) {
|
||||
return null;
|
||||
}
|
||||
return CollUtil.getFirst(buildLeaveApplyRespVOList(Collections.singletonList(application)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量拼接申请人展示信息
|
||||
*
|
||||
* @param applications 申请列表
|
||||
* @return 响应列表
|
||||
*/
|
||||
private List<OaLeaveApplyRespVO> buildLeaveApplyRespVOList(List<OaLeaveApplyDO> applications) {
|
||||
if (CollUtil.isEmpty(applications)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 1. 批量查询申请人
|
||||
Map<Long, AdminUserRespDTO> users = adminUserApi.getUserMap(
|
||||
convertSet(applications, application -> Long.valueOf(application.getCreator())));
|
||||
// 2. 转换并拼接 VO
|
||||
return convertList(applications, application -> {
|
||||
OaLeaveApplyRespVO vo = BeanUtils.toBean(application, OaLeaveApplyRespVO.class);
|
||||
MapUtils.findAndThen(users, Long.valueOf(application.getCreator()), user -> vo.setCreatorName(user.getNickname()));
|
||||
return vo;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.leave.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Schema(description = "管理后台 - 请假申请分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class OaLeaveApplyPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "标题", example = "个人事务请假申请")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "审批状态", example = "-1")
|
||||
private Integer status;
|
||||
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.leave.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 请假申请 Response VO")
|
||||
@Data
|
||||
public class OaLeaveApplyRespVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "标题", example = "个人事务请假申请")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "紧急程度", example = "1")
|
||||
private Integer urgency;
|
||||
|
||||
@Schema(description = "请假类型", example = "1")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "开始时间", type = "integer", format = "int64", example = "1789347600000")
|
||||
private LocalDateTime startTime;
|
||||
|
||||
@Schema(description = "结束时间", type = "integer", format = "int64", example = "1789380000000")
|
||||
private LocalDateTime endTime;
|
||||
|
||||
@Schema(description = "天数", example = "1")
|
||||
private Integer days;
|
||||
|
||||
@Schema(description = "申请原因", example = "处理个人事务")
|
||||
private String reason;
|
||||
|
||||
@Schema(description = "审批状态", example = "-1")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "流程实例编号", example = "1024")
|
||||
private String processInstanceId;
|
||||
|
||||
@Schema(description = "附件地址列表", example = "[\"https://example.com/file.pdf\"]")
|
||||
private List<String> fileUrls;
|
||||
|
||||
@Schema(description = "申请人编号", example = "1024")
|
||||
private String creator;
|
||||
|
||||
@Schema(description = "申请人昵称", example = "张三")
|
||||
private String creatorName;
|
||||
|
||||
@Schema(description = "申请时间", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64", example = "1789347600000")
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.leave.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.dict.validation.InDict;
|
||||
import cn.iocoder.yudao.module.oa.enums.DictTypeConstants;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 请假申请新增/修改 Request VO")
|
||||
@Data
|
||||
public class OaLeaveApplySaveReqVO {
|
||||
|
||||
@Schema(description = "申请编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "个人事务请假申请")
|
||||
@NotBlank(message = "标题不能为空")
|
||||
@Size(max = 255, message = "标题不能超过 255 个字符")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "紧急程度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "紧急程度不能为空")
|
||||
@InDict(type = DictTypeConstants.APPLY_URGENCY, message = "紧急程度必须是 {value}")
|
||||
private Integer urgency;
|
||||
|
||||
@Schema(description = "请假类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "请假类型不能为空")
|
||||
@InDict(type = DictTypeConstants.LEAVE_TYPE, message = "请假类型必须是 {value}")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "开始时间", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64", example = "1789347600000")
|
||||
@NotNull(message = "开始时间不能为空")
|
||||
private LocalDateTime startTime;
|
||||
|
||||
@Schema(description = "结束时间", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64", example = "1789380000000")
|
||||
@NotNull(message = "结束时间不能为空")
|
||||
private LocalDateTime endTime;
|
||||
|
||||
@Schema(description = "申请原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "处理个人事务")
|
||||
@NotBlank(message = "申请原因不能为空")
|
||||
@Size(max = 5000, message = "申请原因不能超过 5000 个字符")
|
||||
private String reason;
|
||||
|
||||
@Schema(description = "附件地址列表", example = "[\"https://example.com/file.pdf\"]")
|
||||
@Size(max = 5, message = "附件不能超过 5 个")
|
||||
private List<@NotBlank(message = "附件地址不能为空") @Size(max = 1024, message = "附件地址不能超过 1024 个字符") String> fileUrls;
|
||||
|
||||
@AssertTrue(message = "结束时间不能早于开始时间")
|
||||
@JsonIgnore
|
||||
public boolean isTimeRangeValid() {
|
||||
// 缺失值交由字段上的必填校验处理
|
||||
return startTime == null || endTime == null || !endTime.isBefore(startTime);
|
||||
}
|
||||
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.leave.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Schema(description = "管理后台 - 请假申请提交 Request VO")
|
||||
@Data
|
||||
public class OaLeaveApplySubmitReqVO {
|
||||
|
||||
@Schema(description = "申请编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "申请编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "发起人自选审批人")
|
||||
private Map<String, List<Long>> startUserSelectAssignees;
|
||||
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.mail;
|
||||
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.mail.vo.account.*;
|
||||
import cn.iocoder.yudao.module.oa.service.mail.OaMailAccountService;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.mail.OaMailAccountDO;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
|
||||
@Tag(name = "管理后台 - 企业邮箱账号")
|
||||
@RestController
|
||||
@RequestMapping("/oa/mail-account")
|
||||
@Validated
|
||||
public class OaMailAccountController {
|
||||
|
||||
@Resource
|
||||
private OaMailAccountService mailAccountService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得本人邮箱账号列表")
|
||||
@Parameter(name = "status", description = "状态,未传时查询全部状态")
|
||||
@PreAuthorize("@ss.hasPermission('oa:mail-account:query')")
|
||||
public CommonResult<List<OaMailAccountRespVO>> getMailAccountList(@RequestParam(value = "status", required = false) Integer status) {
|
||||
List<OaMailAccountDO> accounts = mailAccountService.getMailAccountListByUserIdAndStatus(getLoginUserId(), status);
|
||||
return success(BeanUtils.toBean(accounts, OaMailAccountRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/simple-list")
|
||||
@Operation(summary = "获得当前租户邮箱地址精简列表")
|
||||
public CommonResult<List<OaMailAccountRespVO>> getSimpleMailAccountList() {
|
||||
// 1.1 查询邮箱及归属人,批量获取用户展示信息
|
||||
List<OaMailAccountDO> accounts = mailAccountService.getMailAccountListByStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
Map<Long, AdminUserRespDTO> users = adminUserApi.getUserMap(
|
||||
convertSet(accounts, account -> NumberUtils.parseLong(account.getCreator())));
|
||||
// 1.2 全员选择器只返回邮箱和姓名,不暴露登录名及连接配置
|
||||
List<OaMailAccountRespVO> result = convertList(accounts, account -> {
|
||||
OaMailAccountRespVO respVO = new OaMailAccountRespVO().setMail(account.getMail());
|
||||
MapUtils.findAndThen(users, NumberUtils.parseLong(account.getCreator()),
|
||||
user -> respVO.setUserName(user.getNickname()));
|
||||
return respVO;
|
||||
});
|
||||
|
||||
// 2. 补充已配置邮箱的启用用户
|
||||
Set<String> mails = convertSet(result, account -> account.getMail().toLowerCase(java.util.Locale.ROOT));
|
||||
List<AdminUserRespDTO> systemUsers = adminUserApi.getUserListByStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
for (AdminUserRespDTO user : systemUsers) {
|
||||
if (StrUtil.isNotBlank(user.getEmail()) && mails.add(user.getEmail().toLowerCase(java.util.Locale.ROOT))) {
|
||||
result.add(new OaMailAccountRespVO().setMail(user.getEmail()).setUserName(user.getNickname()));
|
||||
}
|
||||
}
|
||||
return success(result);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得本人邮箱账号")
|
||||
@Parameter(name = "id", description = "邮箱账号编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:mail-account:query')")
|
||||
public CommonResult<OaMailAccountRespVO> getMailAccount(@RequestParam("id") Long id) {
|
||||
OaMailAccountDO account = mailAccountService.validateMailAccount(id, getLoginUserId());
|
||||
return success(BeanUtils.toBean(account, OaMailAccountRespVO.class));
|
||||
}
|
||||
|
||||
@PostMapping("/create")
|
||||
@ApiAccessLog(requestEnable = false) // 特殊:password 密码,避免日志记录
|
||||
@Operation(summary = "绑定本人邮箱账号")
|
||||
@PreAuthorize("@ss.hasPermission('oa:mail-account:create')")
|
||||
public CommonResult<Long> createMailAccount(@Valid @RequestBody OaMailAccountSaveReqVO reqVO) {
|
||||
return success(mailAccountService.createMailAccount(reqVO, getLoginUserId()));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@ApiAccessLog(requestEnable = false) // 特殊:password 密码,避免日志记录
|
||||
@Operation(summary = "更新本人邮箱账号")
|
||||
@PreAuthorize("@ss.hasPermission('oa:mail-account:update')")
|
||||
public CommonResult<Boolean> updateMailAccount(@Valid @RequestBody OaMailAccountSaveReqVO reqVO) {
|
||||
mailAccountService.updateMailAccount(reqVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/update-default")
|
||||
@Operation(summary = "设置本人默认发件账号")
|
||||
@Parameter(name = "id", description = "邮箱账号编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:mail-account:update')")
|
||||
public CommonResult<Boolean> updateMailAccountDefault(@RequestParam("id") Long id) {
|
||||
mailAccountService.updateMailAccountDefault(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "移除本人邮箱绑定")
|
||||
@Parameter(name = "id", description = "邮箱账号编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:mail-account:delete')")
|
||||
public CommonResult<Boolean> deleteMailAccount(@RequestParam("id") Long id) {
|
||||
mailAccountService.deleteMailAccount(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/test-connection")
|
||||
@Operation(summary = "测试本人邮箱连接")
|
||||
@Parameter(name = "id", description = "邮箱账号编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:mail-account:query')")
|
||||
public CommonResult<Map<String, Boolean>> testMailAccountConnection(@RequestParam("id") Long id) {
|
||||
return success(mailAccountService.testMailAccountConnection(id, getLoginUserId()));
|
||||
}
|
||||
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.mail;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.mail.vo.message.OaMailFolderRespVO;
|
||||
import cn.iocoder.yudao.module.oa.service.mail.OaMailMessageService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - 企业邮箱文件夹")
|
||||
@RestController
|
||||
@RequestMapping("/oa/mail-folder")
|
||||
@Validated
|
||||
public class OaMailFolderController {
|
||||
|
||||
@Resource
|
||||
private OaMailMessageService mailMessageService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得邮箱文件夹列表")
|
||||
@Parameter(name = "accountId", description = "邮箱账号编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:mail-account:query')")
|
||||
public CommonResult<List<OaMailFolderRespVO>> getMailFolderList(
|
||||
@RequestParam Long accountId) {
|
||||
return success(mailMessageService.getMailFolderList(accountId, getLoginUserId()));
|
||||
}
|
||||
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.mail;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.*;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.mail.vo.message.*;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.mail.*;
|
||||
import cn.iocoder.yudao.module.oa.service.mail.OaMailMessageService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import cn.hutool.core.lang.Pair;
|
||||
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.oa.enums.mail.OaMailComposeModeEnum;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - 企业邮箱邮件")
|
||||
@RestController
|
||||
@RequestMapping("/oa/mail-message")
|
||||
@Validated
|
||||
@PreAuthorize("@ss.hasPermission('oa:mail-account:query')")
|
||||
public class OaMailMessageController {
|
||||
|
||||
@Resource
|
||||
private OaMailMessageService mailMessageService;
|
||||
|
||||
@GetMapping("/compose")
|
||||
@Operation(summary = "获得写信预填数据")
|
||||
@Parameter(name = "id", description = "邮件编号", required = true, example = "1024")
|
||||
@Parameter(name = "mode", description = "写信方式", required = true)
|
||||
public CommonResult<OaMailMessageSaveReqVO> getMailMessageCompose(@RequestParam Long id,
|
||||
@RequestParam @InEnum(value = OaMailComposeModeEnum.class, message = "写信方式必须是 {value}") String mode) {
|
||||
return success(mailMessageService.getMailMessageCompose(id, mode, getLoginUserId()));
|
||||
}
|
||||
|
||||
@PostMapping("/sync")
|
||||
@Operation(summary = "同步本人邮箱索引")
|
||||
@Parameter(name = "accountId", description = "邮箱账号编号", required = true, example = "1024")
|
||||
public CommonResult<Integer> syncMailMessageList(@RequestParam Long accountId) {
|
||||
return success(mailMessageService.syncMailMessageList(accountId, getLoginUserId()));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得邮件索引分页")
|
||||
public CommonResult<PageResult<OaMailMessageRespVO>> getMailMessagePage(@Valid OaMailMessagePageReqVO reqVO) {
|
||||
PageResult<OaMailMessageDO> pageResult = mailMessageService.getMailMessagePage(reqVO, getLoginUserId());
|
||||
return success(BeanUtils.toBean(pageResult, OaMailMessageRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得邮件详情")
|
||||
@Parameter(name = "id", description = "邮件编号", required = true, example = "1024")
|
||||
public CommonResult<OaMailMessageRespVO> getMailMessage(@RequestParam Long id) {
|
||||
OaMailMessageDO message = mailMessageService.getMailMessage(id, getLoginUserId());
|
||||
return success(BeanUtils.toBean(message, OaMailMessageRespVO.class)
|
||||
.setAttachments(BeanUtils.toBean(message.getAttachments(), OaMailMessageRespVO.Attachment.class)));
|
||||
}
|
||||
|
||||
@PutMapping("/update-read")
|
||||
@Operation(summary = "更新邮件已读状态")
|
||||
@Parameter(name = "id", description = "邮件编号", required = true, example = "1024")
|
||||
@Parameter(name = "readStatus", description = "是否已读", required = true)
|
||||
public CommonResult<Boolean> updateMailMessageRead(@RequestParam Long id,
|
||||
@RequestParam Boolean readStatus) {
|
||||
mailMessageService.updateMailMessageRead(id, readStatus, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除邮件")
|
||||
@Parameter(name = "id", description = "邮件编号", required = true, example = "1024")
|
||||
public CommonResult<Boolean> deleteMailMessage(@RequestParam Long id) {
|
||||
mailMessageService.deleteMailMessage(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/restore")
|
||||
@Operation(summary = "恢复邮件到收件箱")
|
||||
@Parameter(name = "id", description = "邮件编号", required = true, example = "1024")
|
||||
public CommonResult<Boolean> restoreMailMessage(@RequestParam Long id) {
|
||||
mailMessageService.restoreMailMessage(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping(value = "/save-draft", consumes = "application/json")
|
||||
@Operation(summary = "保存草稿")
|
||||
public CommonResult<Long> saveMailMessageDraft(@Valid @RequestBody OaMailMessageSaveReqVO reqVO) {
|
||||
return success(mailMessageService.saveMailMessageDraft(reqVO, getLoginUserId()));
|
||||
}
|
||||
|
||||
@PostMapping(value = "/send", consumes = "application/json")
|
||||
@Operation(summary = "发送邮件")
|
||||
public CommonResult<String> sendMailMessage(@Valid @RequestBody OaMailMessageSaveReqVO reqVO) {
|
||||
return success(mailMessageService.sendMailMessage(reqVO, getLoginUserId()));
|
||||
}
|
||||
|
||||
@PostMapping(value = "/save-draft", consumes = "multipart/form-data")
|
||||
@Operation(summary = "保存带附件的草稿")
|
||||
public CommonResult<Long> saveMailMessageDraftWithFiles(@Valid @RequestPart("data") OaMailMessageSaveReqVO reqVO,
|
||||
@RequestPart(value = "files", required = false) List<MultipartFile> files) {
|
||||
return success(mailMessageService.saveMailMessageDraft(reqVO.setFiles(files), getLoginUserId()));
|
||||
}
|
||||
|
||||
@PostMapping(value = "/send", consumes = "multipart/form-data")
|
||||
@Operation(summary = "发送带附件的邮件")
|
||||
public CommonResult<String> sendMailMessageWithFiles(@Valid @RequestPart("data") OaMailMessageSaveReqVO reqVO,
|
||||
@RequestPart(value = "files", required = false) List<MultipartFile> files) {
|
||||
return success(mailMessageService.sendMailMessage(reqVO.setFiles(files), getLoginUserId()));
|
||||
}
|
||||
|
||||
@GetMapping("/attachment")
|
||||
@Operation(summary = "下载本人邮件附件")
|
||||
public void getMailMessageAttachment(@RequestParam Long id, @RequestParam String part,
|
||||
HttpServletResponse response) throws IOException {
|
||||
Pair<String, byte[]> attachment = mailMessageService.getMailMessageAttachment(id, part, getLoginUserId());
|
||||
ServletUtils.writeAttachment(response, attachment.getKey(), attachment.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.mail;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.mail.vo.provider.OaMailProviderRespVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.mail.vo.provider.OaMailProviderSaveReqVO;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.mail.OaMailProviderDO;
|
||||
import cn.iocoder.yudao.module.oa.service.mail.OaMailProviderService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
|
||||
@Tag(name = "管理后台 - 企业邮箱服务配置")
|
||||
@RestController
|
||||
@RequestMapping("/oa/mail-provider")
|
||||
@Validated
|
||||
public class OaMailProviderController {
|
||||
|
||||
@Resource
|
||||
private OaMailProviderService mailProviderService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得邮箱服务配置列表")
|
||||
@Parameter(name = "status", description = "状态,未传时查询全部状态")
|
||||
@PreAuthorize("@ss.hasPermission('oa:mail-provider:query')")
|
||||
public CommonResult<List<OaMailProviderRespVO>> getMailProviderList(@RequestParam(value = "status", required = false) Integer status) {
|
||||
List<OaMailProviderDO> providers = mailProviderService.getMailProviderList(status);
|
||||
return success(BeanUtils.toBean(providers, OaMailProviderRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/simple-list")
|
||||
@Operation(summary = "获得邮箱服务配置精简列表")
|
||||
public CommonResult<List<OaMailProviderRespVO>> getSimpleMailProviderList() {
|
||||
List<OaMailProviderDO> providers = mailProviderService.getMailProviderList(CommonStatusEnum.ENABLE.getStatus());
|
||||
return success(convertList(providers, provider -> new OaMailProviderRespVO()
|
||||
.setId(provider.getId()).setName(provider.getName()).setStatus(provider.getStatus())));
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得邮箱服务配置")
|
||||
@Parameter(name = "id", description = "服务配置编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:mail-provider:query')")
|
||||
public CommonResult<OaMailProviderRespVO> getMailProvider(@RequestParam("id") Long id) {
|
||||
OaMailProviderDO provider = mailProviderService.validateMailProviderExists(id);
|
||||
return success(BeanUtils.toBean(provider, OaMailProviderRespVO.class));
|
||||
}
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建邮箱服务配置")
|
||||
@PreAuthorize("@ss.hasPermission('oa:mail-provider:create')")
|
||||
public CommonResult<Long> createMailProvider(@Valid @RequestBody OaMailProviderSaveReqVO reqVO) {
|
||||
return success(mailProviderService.createMailProvider(reqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新邮箱服务配置")
|
||||
@PreAuthorize("@ss.hasPermission('oa:mail-provider:update')")
|
||||
public CommonResult<Boolean> updateMailProvider(@Valid @RequestBody OaMailProviderSaveReqVO reqVO) {
|
||||
mailProviderService.updateMailProvider(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除邮箱服务配置")
|
||||
@Parameter(name = "id", description = "服务配置编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:mail-provider:delete')")
|
||||
public CommonResult<Boolean> deleteMailProvider(@RequestParam("id") Long id) {
|
||||
mailProviderService.deleteMailProvider(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.mail.vo.account;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "管理后台 - 企业邮箱账号 Response VO")
|
||||
public class OaMailAccountRespVO {
|
||||
|
||||
@Schema(description = "账号编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "服务配置编号", example = "1024")
|
||||
private Long providerId;
|
||||
|
||||
@Schema(description = "邮箱地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "user@example.com")
|
||||
private String mail;
|
||||
|
||||
@Schema(description = "归属人姓名", example = "张三")
|
||||
private String userName;
|
||||
|
||||
@Schema(description = "登录用户名", example = "user@example.com")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "是否默认发件账号", example = "false")
|
||||
private Boolean defaultStatus;
|
||||
|
||||
@Schema(description = "状态", example = "0")
|
||||
private Integer status;
|
||||
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.mail.vo.account;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.*;
|
||||
import lombok.Data;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.ToString;
|
||||
|
||||
@Data
|
||||
@Schema(description = "管理后台 - 企业邮箱账号新增/修改 Request VO")
|
||||
public class OaMailAccountSaveReqVO {
|
||||
|
||||
@Schema(description = "账号编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "服务配置编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "服务配置编号不能为空")
|
||||
private Long providerId;
|
||||
|
||||
@Schema(description = "邮箱地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "user@example.com")
|
||||
@NotBlank(message = "邮箱地址不能为空")
|
||||
@Email(message = "邮箱地址格式不正确")
|
||||
@Size(max = 255, message = "邮箱地址长度不能超过 {max} 个字符")
|
||||
private String mail;
|
||||
|
||||
@Schema(description = "登录用户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "user@example.com")
|
||||
@NotBlank(message = "登录用户名不能为空")
|
||||
@Size(max = 255, message = "登录用户名长度不能超过 {max} 个字符")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "密码或授权码,新增时必填,修改时留空表示不变", accessMode = Schema.AccessMode.WRITE_ONLY)
|
||||
@ToString.Exclude
|
||||
@Size(max = 4096, message = "密码或授权码长度不能超过 {max} 个字符")
|
||||
private String password;
|
||||
|
||||
@Schema(description = "是否默认发件账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
|
||||
@NotNull(message = "是否默认发件账号不能为空")
|
||||
private Boolean defaultStatus;
|
||||
|
||||
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
@NotNull(message = "状态不能为空")
|
||||
@InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}")
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 校验默认邮箱账号的启用状态
|
||||
*
|
||||
* @return 是否符合默认账号规则
|
||||
*/
|
||||
@AssertTrue(message = "默认邮箱账号必须启用")
|
||||
@JsonIgnore
|
||||
public boolean isDefaultStatusValid() {
|
||||
return !Boolean.TRUE.equals(defaultStatus) || CommonStatusEnum.ENABLE.getStatus().equals(status);
|
||||
}
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.mail.vo.message;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 邮件目录 Response VO")
|
||||
@Data
|
||||
public class OaMailFolderRespVO {
|
||||
|
||||
@Schema(description = "目录查询标识", example = "INBOX")
|
||||
private String key;
|
||||
|
||||
@Schema(description = "显示名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "收件箱")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "未读数量", example = "1")
|
||||
private Long unreadCount;
|
||||
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.mail.vo.message;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.module.oa.enums.mail.OaMailFolderKeyEnum;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Schema(description = "管理后台 - 邮件分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class OaMailMessagePageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "邮箱账号编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "邮箱账号编号不能为空")
|
||||
private Long accountId;
|
||||
|
||||
@Schema(description = "文件夹类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "INBOX")
|
||||
@NotBlank(message = "文件夹标识不能为空")
|
||||
private String folderKey;
|
||||
|
||||
@Schema(description = "主题或发件人", example = "项目评审")
|
||||
@Size(max = 200, message = "搜索关键字不能超过 200 个字符")
|
||||
private String keyword;
|
||||
|
||||
@Schema(description = "是否已读", example = "false")
|
||||
private Boolean readStatus;
|
||||
|
||||
@Schema(description = "是否有附件", example = "false")
|
||||
private Boolean hasAttach;
|
||||
|
||||
/**
|
||||
* 校验文件夹入口
|
||||
*
|
||||
* @return 是否为标准入口或合法的自定义目录编号
|
||||
*/
|
||||
@AssertTrue(message = "文件夹标识不正确")
|
||||
@JsonIgnore
|
||||
public boolean isFolderKeyValid() {
|
||||
if (StrUtil.isBlank(folderKey) || ArrayUtil.contains(OaMailFolderKeyEnum.ARRAYS, folderKey)) {
|
||||
return true; // 空值由 NotBlank 校验
|
||||
}
|
||||
return folderKey.matches("[1-9][0-9]{0,17}");
|
||||
}
|
||||
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.mail.vo.message;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 企业邮箱邮件 Response VO")
|
||||
@Data
|
||||
public class OaMailMessageRespVO {
|
||||
|
||||
@Schema(description = "邮件编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "邮箱账号编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long accountId;
|
||||
|
||||
@Schema(description = "文件夹编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long folderId;
|
||||
|
||||
@Schema(description = "远端邮件 UID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1001")
|
||||
private Long uid;
|
||||
|
||||
@Schema(description = "远端 UID 有效期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long uidValidity;
|
||||
|
||||
@Schema(description = "邮件主题", requiredMode = Schema.RequiredMode.REQUIRED, example = "项目评审会议通知")
|
||||
private String subject;
|
||||
|
||||
@Schema(description = "发件人", example = "sender@example.com")
|
||||
private String sender;
|
||||
|
||||
@Schema(description = "收件人", example = "[\"recipient@example.com\"]")
|
||||
private List<String> recipients;
|
||||
|
||||
@Schema(description = "抄送人", example = "[\"cc@example.com\"]")
|
||||
private List<String> ccs;
|
||||
|
||||
@Schema(description = "回复地址", example = "[\"reply@example.com\"]")
|
||||
private List<String> replyTos;
|
||||
|
||||
@Schema(description = "接收时间", type = "integer", format = "int64", example = "1789347600000")
|
||||
private LocalDateTime receiveTime;
|
||||
|
||||
@Schema(description = "是否已读", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
|
||||
private Boolean readStatus;
|
||||
|
||||
@Schema(description = "是否有附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
|
||||
private Boolean hasAttach;
|
||||
|
||||
@Schema(description = "邮件大小(字节)", example = "1024")
|
||||
private Integer size;
|
||||
|
||||
@Schema(description = "安全 HTML 正文", example = "<p>请查收本周工作安排。</p>")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "附件信息")
|
||||
private List<Attachment> attachments;
|
||||
|
||||
@Schema(description = "管理后台 - 邮件附件 Response VO")
|
||||
@Data
|
||||
public static class Attachment {
|
||||
|
||||
@Schema(description = "MIME 部件路径", example = "1.2")
|
||||
private String part;
|
||||
|
||||
@Schema(description = "附件名称", example = "办公用品盘点结果.pdf")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "附件大小(字节)", example = "1024")
|
||||
private Integer size;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.mail.vo.message;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.*;
|
||||
import lombok.Data;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.mail.OaMailMessageDO;
|
||||
import java.util.List;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.oa.enums.mail.OaMailComposeModeEnum;
|
||||
|
||||
@Schema(description = "管理后台 - 写信及草稿 Request VO")
|
||||
@Data
|
||||
public class OaMailMessageSaveReqVO {
|
||||
|
||||
@Schema(description = "邮箱账号编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "邮箱账号编号不能为空")
|
||||
private Long accountId;
|
||||
|
||||
@Schema(description = "原草稿编号", example = "1024")
|
||||
private Long draftId;
|
||||
|
||||
@Schema(description = "收件人", example = "[\"recipient@example.com\"]")
|
||||
private List<@NotBlank(message = "收件人地址不能为空") String> recipients;
|
||||
|
||||
@Schema(description = "抄送人", example = "[\"cc@example.com\"]")
|
||||
private List<@NotBlank(message = "抄送人地址不能为空") String> ccs;
|
||||
|
||||
@Schema(description = "主题", example = "项目评审会议通知")
|
||||
@Size(max = 65535, message = "邮件主题不能超过 65535 个字符")
|
||||
private String subject;
|
||||
|
||||
@Schema(description = "正文 HTML", example = "<p>请查收本周工作安排。</p>")
|
||||
@Size(max = 500000, message = "正文 HTML长度不能超过 {max} 个字符")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "回复或转发的原邮件编号", example = "1024")
|
||||
private Long sourceId;
|
||||
|
||||
@Schema(description = "写信方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "new")
|
||||
@InEnum(value = OaMailComposeModeEnum.class, message = "写信方式必须是 {value}")
|
||||
@NotBlank(message = "写信方式不能为空")
|
||||
private String mode;
|
||||
|
||||
@Schema(description = "保留的原邮件附件路径;空列表表示移除全部原附件")
|
||||
private List<String> attachmentParts;
|
||||
|
||||
@Schema(description = "原邮件附件,仅用于写信回显", accessMode = Schema.AccessMode.READ_ONLY)
|
||||
private List<OaMailMessageDO.Attachment> attachments;
|
||||
|
||||
@JsonIgnore
|
||||
@Schema(hidden = true)
|
||||
private List<MultipartFile> files;
|
||||
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.mail.vo.provider;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 邮箱服务配置 Response VO")
|
||||
@Data
|
||||
public class OaMailProviderRespVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "企业邮箱")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "IMAP 连接配置")
|
||||
private OaMailProviderSaveReqVO.ConnectionConfig imap;
|
||||
|
||||
@Schema(description = "SMTP 连接配置")
|
||||
private OaMailProviderSaveReqVO.ConnectionConfig smtp;
|
||||
|
||||
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
private Integer status;
|
||||
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.mail.vo.provider;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.*;
|
||||
import lombok.Data;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
@Data
|
||||
@Schema(description = "管理后台 - 企业邮箱服务配置新增/修改 Request VO")
|
||||
public class OaMailProviderSaveReqVO {
|
||||
|
||||
@Schema(description = "服务配置编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "企业邮箱")
|
||||
@NotBlank(message = "名称不能为空")
|
||||
@Size(max = 255, message = "名称长度不能超过 {max} 个字符")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "IMAP 连接配置", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "IMAP 连接配置不能为空")
|
||||
@Valid
|
||||
private ConnectionConfig imap;
|
||||
|
||||
@Schema(description = "SMTP 连接配置", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "SMTP 连接配置不能为空")
|
||||
@Valid
|
||||
private ConnectionConfig smtp;
|
||||
|
||||
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
@NotNull(message = "状态不能为空")
|
||||
@InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "管理后台 - 邮箱连接配置 Request VO")
|
||||
@Data
|
||||
public static class ConnectionConfig {
|
||||
|
||||
@Schema(description = "服务器域名", requiredMode = Schema.RequiredMode.REQUIRED, example = "imap.example.com")
|
||||
@NotBlank(message = "服务器域名不能为空")
|
||||
@Size(max = 253, message = "服务器域名长度不能超过 253 个字符")
|
||||
private String host;
|
||||
|
||||
@Schema(description = "服务器端口", requiredMode = Schema.RequiredMode.REQUIRED, example = "993")
|
||||
@NotNull(message = "服务器端口不能为空")
|
||||
@Min(value = 1, message = "服务器端口不能小于 1")
|
||||
@Max(value = 65535, message = "服务器端口不能大于 65535")
|
||||
private Integer port;
|
||||
|
||||
@Schema(description = "是否开启 SSL", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
@NotNull(message = "是否开启 SSL 不能为空")
|
||||
private Boolean sslEnable;
|
||||
|
||||
@Schema(description = "是否开启 STARTTLS", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
|
||||
@NotNull(message = "是否开启 STARTTLS 不能为空")
|
||||
private Boolean starttlsEnable;
|
||||
|
||||
/**
|
||||
* 校验邮箱连接加密方式
|
||||
*
|
||||
* @return 是否仅启用一种加密方式,空值由字段必填注解校验
|
||||
*/
|
||||
@AssertTrue(message = "请选择 SSL 或 STARTTLS,且不能同时开启")
|
||||
@JsonIgnore
|
||||
public boolean isEncryptionValid() {
|
||||
return sslEnable == null || !sslEnable.equals(starttlsEnable);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.meetingroom;
|
||||
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.meetingroom.vo.booking.*;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.meetingroom.*;
|
||||
import cn.iocoder.yudao.module.oa.service.meetingroom.*;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - 会议室预定")
|
||||
@RestController
|
||||
@RequestMapping("/oa/meeting-room-booking")
|
||||
@Validated
|
||||
public class OaMeetingRoomBookingController {
|
||||
|
||||
@Resource
|
||||
private OaMeetingRoomBookingService meetingRoomBookingService;
|
||||
@Resource
|
||||
private OaMeetingRoomService meetingRoomService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建会议室预定草稿")
|
||||
@PreAuthorize("@ss.hasPermission('oa:meeting-room-booking:create')")
|
||||
public CommonResult<Long> createMeetingRoomBooking(@Valid @RequestBody OaMeetingRoomBookingSaveReqVO reqVO) {
|
||||
return success(meetingRoomBookingService.createMeetingRoomBooking(reqVO, getLoginUserId()));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新会议室预定草稿")
|
||||
@PreAuthorize("@ss.hasPermission('oa:meeting-room-booking:update')")
|
||||
public CommonResult<Boolean> updateMeetingRoomBooking(@Valid @RequestBody OaMeetingRoomBookingSaveReqVO reqVO) {
|
||||
meetingRoomBookingService.updateMeetingRoomBooking(reqVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除会议室预定草稿")
|
||||
@Parameter(name = "id", description = "预定编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:meeting-room-booking:delete')")
|
||||
public CommonResult<Boolean> deleteMeetingRoomBooking(@RequestParam("id") Long id) {
|
||||
meetingRoomBookingService.deleteMeetingRoomBooking(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得会议室预定详情")
|
||||
@Parameter(name = "id", description = "预定编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:meeting-room-booking:query')")
|
||||
public CommonResult<OaMeetingRoomBookingRespVO> getMeetingRoomBooking(@RequestParam("id") Long id) {
|
||||
OaMeetingRoomBookingDO booking = meetingRoomBookingService.getMeetingRoomBooking(id);
|
||||
return success(buildMeetingRoomBookingRespVO(booking));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得本人的会议室预定分页")
|
||||
@PreAuthorize("@ss.hasPermission('oa:meeting-room-booking:query')")
|
||||
public CommonResult<PageResult<OaMeetingRoomBookingRespVO>> getMeetingRoomBookingPage(@Valid OaMeetingRoomBookingPageReqVO reqVO) {
|
||||
PageResult<OaMeetingRoomBookingDO> pageResult = meetingRoomBookingService.getMeetingRoomBookingPage(getLoginUserId(), reqVO);
|
||||
return success(new PageResult<>(buildMeetingRoomBookingRespVOList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@PutMapping("/submit")
|
||||
@Operation(summary = "提交会议室预定")
|
||||
@Parameter(name = "id", description = "预定编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:meeting-room-booking:create')")
|
||||
public CommonResult<Boolean> submitMeetingRoomBooking(@RequestParam("id") Long id) {
|
||||
meetingRoomBookingService.submitMeetingRoomBooking(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/cancel")
|
||||
@Operation(summary = "取消会议室预定")
|
||||
@Parameter(name = "id", description = "预定编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:meeting-room-booking:update')")
|
||||
public CommonResult<Boolean> cancelMeetingRoomBooking(@RequestParam("id") Long id) {
|
||||
meetingRoomBookingService.cancelMeetingRoomBooking(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/start")
|
||||
@Operation(summary = "开始使用会议室预定")
|
||||
@Parameter(name = "id", description = "预定编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:meeting-room-booking:update')")
|
||||
public CommonResult<Boolean> startMeetingRoomBooking(@RequestParam("id") Long id) {
|
||||
meetingRoomBookingService.startMeetingRoomBooking(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/finish")
|
||||
@Operation(summary = "完成使用会议室预定")
|
||||
@Parameter(name = "id", description = "预定编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:meeting-room-booking:update')")
|
||||
public CommonResult<Boolean> finishMeetingRoomBooking(@RequestParam("id") Long id) {
|
||||
meetingRoomBookingService.finishMeetingRoomBooking(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/schedule")
|
||||
@Operation(summary = "获得会议室日程")
|
||||
@PreAuthorize("@ss.hasAnyPermissions('oa:meeting-room:query', 'oa:meeting-room-booking:query')")
|
||||
public CommonResult<List<OaMeetingRoomBookingRespVO>> getMeetingRoomBookingListBySchedule(@Valid OaMeetingRoomBookingScheduleReqVO reqVO) {
|
||||
List<OaMeetingRoomBookingDO> bookings = meetingRoomBookingService.getMeetingRoomBookingListBySchedule(reqVO);
|
||||
return success(buildMeetingRoomBookingRespVOList(bookings));
|
||||
}
|
||||
|
||||
// ==================== 拼接 VO ====================
|
||||
|
||||
/**
|
||||
* 拼接会议室预定详情
|
||||
*
|
||||
* @param booking 会议室预定
|
||||
* @return 会议室预定响应
|
||||
*/
|
||||
private OaMeetingRoomBookingRespVO buildMeetingRoomBookingRespVO(OaMeetingRoomBookingDO booking) {
|
||||
if (booking == null) {
|
||||
return null;
|
||||
}
|
||||
return CollUtil.getFirst(buildMeetingRoomBookingRespVOList(Collections.singletonList(booking)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建会议室预定响应列表
|
||||
*
|
||||
* @param bookings 会议室预定列表
|
||||
* @return 响应列表
|
||||
*/
|
||||
private List<OaMeetingRoomBookingRespVO> buildMeetingRoomBookingRespVOList(List<OaMeetingRoomBookingDO> bookings) {
|
||||
if (CollUtil.isEmpty(bookings)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 1.1 批量查询申请人、主持人和参会人
|
||||
Set<Long> userIds = convertSet(bookings, OaMeetingRoomBookingDO::getModeratorUserId);
|
||||
userIds.addAll(convertSet(bookings, booking -> Long.valueOf(booking.getCreator()),
|
||||
booking -> NumberUtil.isLong(booking.getCreator())));
|
||||
userIds.addAll(convertSetByFlatMap(bookings, OaMeetingRoomBookingDO::getAttendeeUserIds, Collection::stream));
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
|
||||
// 1.2 批量查询房间及申请部门
|
||||
Map<Long, OaMeetingRoomDO> roomMap = meetingRoomService.getMeetingRoomMap(
|
||||
convertSet(bookings, OaMeetingRoomBookingDO::getRoomId));
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(bookings, OaMeetingRoomBookingDO::getDeptId));
|
||||
// 2. 转换并拼接展示字段
|
||||
return convertList(bookings, booking -> {
|
||||
OaMeetingRoomBookingRespVO vo = BeanUtils.toBean(booking, OaMeetingRoomBookingRespVO.class);
|
||||
MapUtils.findAndThen(roomMap, booking.getRoomId(), room ->
|
||||
vo.setRoomName(room.getName()).setRoomLocation(room.getLocation()).setRoomType(room.getType()));
|
||||
MapUtils.findAndThen(userMap, booking.getModeratorUserId(), user -> vo.setModeratorName(user.getNickname()));
|
||||
if (NumberUtil.isLong(booking.getCreator())) {
|
||||
MapUtils.findAndThen(userMap, Long.valueOf(booking.getCreator()), user -> vo.setCreatorName(user.getNickname()));
|
||||
}
|
||||
MapUtils.findAndThen(deptMap, booking.getDeptId(), dept -> vo.setDeptName(dept.getName()));
|
||||
vo.setAttendeeNames(convertList(booking.getAttendeeUserIds(), userId ->
|
||||
userMap.containsKey(userId) ? userMap.get(userId).getNickname() : userId.toString()));
|
||||
return vo;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.meetingroom;
|
||||
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.meetingroom.vo.room.*;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.meetingroom.*;
|
||||
import cn.iocoder.yudao.module.oa.service.meetingroom.*;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - 会议室")
|
||||
@RestController
|
||||
@RequestMapping("/oa/meeting-room")
|
||||
@Validated
|
||||
public class OaMeetingRoomController {
|
||||
|
||||
@Resource
|
||||
private OaMeetingRoomService meetingRoomService;
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建会议室")
|
||||
@PreAuthorize("@ss.hasPermission('oa:meeting-room:create')")
|
||||
public CommonResult<Long> createMeetingRoom(@Valid @RequestBody OaMeetingRoomSaveReqVO reqVO) {
|
||||
return success(meetingRoomService.createMeetingRoom(reqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新会议室")
|
||||
@PreAuthorize("@ss.hasPermission('oa:meeting-room:update')")
|
||||
public CommonResult<Boolean> updateMeetingRoom(@Valid @RequestBody OaMeetingRoomSaveReqVO reqVO) {
|
||||
meetingRoomService.updateMeetingRoom(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除会议室")
|
||||
@Parameter(name = "id", description = "会议室编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:meeting-room:delete')")
|
||||
public CommonResult<Boolean> deleteMeetingRoom(@RequestParam("id") Long id) {
|
||||
meetingRoomService.deleteMeetingRoom(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得会议室详情")
|
||||
@Parameter(name = "id", description = "会议室编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:meeting-room:query')")
|
||||
public CommonResult<OaMeetingRoomRespVO> getMeetingRoom(@RequestParam("id") Long id) {
|
||||
OaMeetingRoomDO room = meetingRoomService.validateMeetingRoomExists(id);
|
||||
return success(buildMeetingRoomRespVO(room));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得会议室分页")
|
||||
@PreAuthorize("@ss.hasPermission('oa:meeting-room:query')")
|
||||
public CommonResult<PageResult<OaMeetingRoomRespVO>> getMeetingRoomPage(@Valid OaMeetingRoomPageReqVO reqVO) {
|
||||
PageResult<OaMeetingRoomDO> pageResult = meetingRoomService.getMeetingRoomPage(reqVO);
|
||||
return success(new PageResult<>(buildMeetingRoomRespVOList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/bookable-page")
|
||||
@Operation(summary = "获得可预定会议室分页")
|
||||
public CommonResult<PageResult<OaMeetingRoomRespVO>> getMeetingRoomPageByBookingUserId(@Valid OaMeetingRoomPageReqVO reqVO) {
|
||||
PageResult<OaMeetingRoomDO> pageResult = meetingRoomService.getMeetingRoomPageByBookingUserId(reqVO, getLoginUserId());
|
||||
List<OaMeetingRoomRespVO> list = buildMeetingRoomRespVOList(pageResult.getList());
|
||||
return success(new PageResult<>(convertList(list, room -> new OaMeetingRoomRespVO()
|
||||
.setId(room.getId()).setName(room.getName()).setLocation(room.getLocation())
|
||||
.setType(room.getType()).setStatus(room.getStatus()).setSeatCount(room.getSeatCount())
|
||||
.setManagerName(room.getManagerName())), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
// ==================== 拼接 VO ====================
|
||||
|
||||
/**
|
||||
* 构建会议室响应
|
||||
*
|
||||
* @param room 会议室
|
||||
* @return 会议室响应
|
||||
*/
|
||||
private OaMeetingRoomRespVO buildMeetingRoomRespVO(OaMeetingRoomDO room) {
|
||||
AdminUserRespDTO user = adminUserApi.getUser(room.getManagerUserId());
|
||||
OaMeetingRoomRespVO respVO = BeanUtils.toBean(room, OaMeetingRoomRespVO.class);
|
||||
if (user != null) {
|
||||
respVO.setManagerName(user.getNickname()).setManagerPhone(user.getMobile());
|
||||
}
|
||||
return respVO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建会议室响应列表
|
||||
*
|
||||
* @param rooms 会议室列表
|
||||
* @return 响应列表
|
||||
*/
|
||||
private List<OaMeetingRoomRespVO> buildMeetingRoomRespVOList(List<OaMeetingRoomDO> rooms) {
|
||||
if (CollUtil.isEmpty(rooms)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 1. 批量查询管理员
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(rooms, OaMeetingRoomDO::getManagerUserId));
|
||||
// 2. 转换并拼接管理员姓名、电话
|
||||
return convertList(rooms, room -> {
|
||||
OaMeetingRoomRespVO vo = BeanUtils.toBean(room, OaMeetingRoomRespVO.class);
|
||||
MapUtils.findAndThen(userMap, room.getManagerUserId(), user ->
|
||||
vo.setManagerName(user.getNickname()).setManagerPhone(user.getMobile()));
|
||||
return vo;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.meetingroom.vo.booking;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
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)
|
||||
public class OaMeetingRoomBookingPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "单据编号", example = "HY20260914000001")
|
||||
private String no;
|
||||
|
||||
@Schema(description = "会议室名称", example = "101 项目讨论室")
|
||||
private String roomName;
|
||||
|
||||
@Schema(description = "会议主题", example = "产品需求评审")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "主持人姓名", example = "芋艿")
|
||||
private String moderatorName;
|
||||
|
||||
@Schema(description = "申请部门编号", example = "100")
|
||||
private Long deptId;
|
||||
|
||||
@Schema(description = "审批状态", example = "2")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "使用状态", example = "0")
|
||||
private Integer useStatus;
|
||||
|
||||
@Schema(description = "会议开始时间", example = "[\"2026-09-14 00:00:00\",\"2026-09-14 23:59:59\"]")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] startTime;
|
||||
|
||||
@Schema(description = "会议结束时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] endTime;
|
||||
|
||||
@Schema(description = "创建人编号", example = "1")
|
||||
private String creator;
|
||||
|
||||
@Schema(description = "创建时间", example = "[\"2026-09-14 00:00:00\",\"2026-09-14 23:59:59\"]")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.meetingroom.vo.booking;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 会议室预定 Response VO")
|
||||
@Data
|
||||
public class OaMeetingRoomBookingRespVO {
|
||||
|
||||
@Schema(description = "编号", example = "1024", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "会议室编号", example = "1024", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Long roomId;
|
||||
|
||||
@Schema(description = "会议主题", example = "产品需求评审", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String title;
|
||||
|
||||
@Schema(description = "会议开始时间", example = "1789351200000", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64")
|
||||
private LocalDateTime startTime;
|
||||
|
||||
@Schema(description = "会议结束时间", example = "1789354800000", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64")
|
||||
private LocalDateTime endTime;
|
||||
|
||||
@Schema(description = "主持人用户编号", example = "1", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Long moderatorUserId;
|
||||
|
||||
@Schema(description = "参会人用户编号列表", example = "[1,2]")
|
||||
private List<Long> attendeeUserIds;
|
||||
|
||||
@Schema(description = "会议提醒", example = "1", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Integer reminderType;
|
||||
|
||||
@Schema(description = "会议说明", example = "讨论产品需求和排期")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "申请备注", example = "请提前安排会议室")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "附件地址列表", example = "[\"https://www.iocoder.cn/meeting.pdf\"]")
|
||||
private List<String> fileUrls;
|
||||
|
||||
@Schema(description = "申请单号", example = "HY20260914000001", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String no;
|
||||
|
||||
@Schema(description = "会议室名称", example = "101 项目讨论室")
|
||||
private String roomName;
|
||||
|
||||
@Schema(description = "位置", example = "办公楼一层 101")
|
||||
private String roomLocation;
|
||||
|
||||
@Schema(description = "会议室类型", example = "1")
|
||||
private Integer roomType;
|
||||
|
||||
@Schema(description = "主持人姓名", example = "芋艿")
|
||||
private String moderatorName;
|
||||
|
||||
@Schema(description = "参会人姓名", example = "[\"芋艿\",\"小王\"]")
|
||||
private List<String> attendeeNames;
|
||||
|
||||
@Schema(description = "申请人", example = "芋艿")
|
||||
private String creatorName;
|
||||
|
||||
@Schema(description = "申请部门编号", example = "100")
|
||||
private Long deptId;
|
||||
|
||||
@Schema(description = "部门名称", example = "研发部门")
|
||||
private String deptName;
|
||||
|
||||
@Schema(description = "审批状态", example = "2", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "使用状态", example = "0", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Integer useStatus;
|
||||
|
||||
@Schema(description = "提交时是否需要审批", example = "false")
|
||||
private Boolean needApproval;
|
||||
|
||||
@Schema(description = "流程实例编号", example = "process-1024")
|
||||
private String processInstanceId;
|
||||
|
||||
@Schema(description = "创建时间", example = "1789347600000", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.meetingroom.vo.booking;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.oa.enums.meetingroom.*;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 会议室预定新增/修改 Request VO")
|
||||
@Data
|
||||
public class OaMeetingRoomBookingSaveReqVO {
|
||||
|
||||
@Schema(description = "编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "会议室编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "会议室不能为空")
|
||||
private Long roomId;
|
||||
|
||||
@Schema(description = "会议主题", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品需求评审")
|
||||
@NotBlank(message = "会议主题不能为空")
|
||||
@Size(max = 200, message = "会议主题长度不能超过 {max} 个字符")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "会议开始时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1789351200000", type = "integer", format = "int64")
|
||||
@NotNull(message = "开始时间不能为空")
|
||||
private LocalDateTime startTime;
|
||||
|
||||
@Schema(description = "会议结束时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1789354800000", type = "integer", format = "int64")
|
||||
@NotNull(message = "结束时间不能为空")
|
||||
private LocalDateTime endTime;
|
||||
|
||||
@Schema(description = "主持人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "主持人不能为空")
|
||||
private Long moderatorUserId;
|
||||
|
||||
@Schema(description = "参会人用户编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2]")
|
||||
@NotNull(message = "参会人不能为空")
|
||||
private List<@NotNull(message = "参会人不能为空") Long> attendeeUserIds;
|
||||
|
||||
@Schema(description = "会议提醒", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "会议提醒不能为空")
|
||||
@InEnum(value = OaMeetingRoomReminderTypeEnum.class, message = "会议提醒不正确")
|
||||
private Integer reminderType;
|
||||
|
||||
@Schema(description = "会议说明", example = "讨论产品需求和排期")
|
||||
@Size(max = 500, message = "会议说明长度不能超过 {max} 个字符")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "申请备注", example = "请提前安排会议室")
|
||||
@Size(max = 500, message = "申请备注长度不能超过 {max} 个字符")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "附件地址列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[\"https://www.iocoder.cn/meeting.pdf\"]")
|
||||
@NotNull(message = "附件地址列表不能为空")
|
||||
private List<@NotBlank(message = "附件地址列表不能为空") String> fileUrls;
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.meetingroom.vo.booking;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
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 OaMeetingRoomBookingScheduleReqVO {
|
||||
|
||||
@Schema(description = "会议室编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "会议室编号不能为空")
|
||||
private Long roomId;
|
||||
|
||||
@Schema(description = "查询开始时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-09-14 10:00:00")
|
||||
@NotNull(message = "查询开始时间不能为空")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime startTime;
|
||||
|
||||
@Schema(description = "查询结束时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-09-14 11:00:00")
|
||||
@NotNull(message = "查询结束时间不能为空")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime endTime;
|
||||
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.meetingroom.vo.room;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Schema(description = "管理后台 - 会议室分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class OaMeetingRoomPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "会议室名称", example = "101 项目讨论室")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "位置", example = "办公楼一层 101")
|
||||
private String location;
|
||||
|
||||
@Schema(description = "类型", example = "1")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "可用状态", example = "0")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "负责人姓名", example = "芋艿")
|
||||
private String managerName;
|
||||
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.meetingroom.vo.room;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 会议室 Response VO")
|
||||
@Data
|
||||
public class OaMeetingRoomRespVO {
|
||||
|
||||
@Schema(description = "编号", example = "1024", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "会议室名称", example = "101 项目讨论室", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String name;
|
||||
|
||||
@Schema(description = "位置", example = "办公楼一层 101", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String location;
|
||||
|
||||
@Schema(description = "会议室类型", example = "1", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "负责人用户编号", example = "1", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Long managerUserId;
|
||||
|
||||
@Schema(description = "可用状态", example = "0", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "会议室图片", example = "https://www.iocoder.cn/meeting-room.jpg")
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "坐席数", example = "8")
|
||||
private Integer seatCount;
|
||||
|
||||
@Schema(description = "设备列表", example = "[1,2]")
|
||||
private List<Integer> equipments;
|
||||
|
||||
@Schema(description = "允许预定", example = "true", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Boolean allowBooking;
|
||||
|
||||
@Schema(description = "预定需审批", example = "false", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Boolean needApproval;
|
||||
|
||||
@Schema(description = "可预定范围", example = "0", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Integer bookingScope;
|
||||
|
||||
@Schema(description = "可预定成员用户编号列表", example = "[1,2]")
|
||||
private List<Long> bookingUserIds;
|
||||
|
||||
@Schema(description = "显示顺序", example = "1", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "备注", example = "请提前安排会议室")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "附件地址列表", example = "[\"https://www.iocoder.cn/meeting.pdf\"]")
|
||||
private List<String> fileUrls;
|
||||
|
||||
@Schema(description = "负责人姓名", example = "芋艿")
|
||||
private String managerName;
|
||||
|
||||
@Schema(description = "负责人联系方式", example = "13800138000")
|
||||
private String managerPhone;
|
||||
|
||||
@Schema(description = "创建时间", example = "1789347600000", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.meetingroom.vo.room;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.oa.enums.meetingroom.*;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 会议室新增/修改 Request VO")
|
||||
@Data
|
||||
public class OaMeetingRoomSaveReqVO {
|
||||
|
||||
@Schema(description = "编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "会议室名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "101 项目讨论室")
|
||||
@NotBlank(message = "会议室名称不能为空")
|
||||
@Size(max = 100, message = "会议室名称长度不能超过 {max} 个字符")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "位置", requiredMode = Schema.RequiredMode.REQUIRED, example = "办公楼一层 101")
|
||||
@NotBlank(message = "位置不能为空")
|
||||
@Size(max = 255, message = "位置长度不能超过 {max} 个字符")
|
||||
private String location;
|
||||
|
||||
@Schema(description = "会议室类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "类型不能为空")
|
||||
@InEnum(value = OaMeetingRoomTypeEnum.class, message = "会议室类型不正确")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "负责人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "负责人不能为空")
|
||||
private Long managerUserId;
|
||||
|
||||
@Schema(description = "可用状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
@NotNull(message = "可用状态不能为空")
|
||||
@InEnum(value = OaMeetingRoomStatusEnum.class, message = "可用状态不正确")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "会议室图片", example = "https://www.iocoder.cn/meeting-room.jpg")
|
||||
@Size(max = 1024, message = "会议室图片长度不能超过 {max} 个字符")
|
||||
private String picUrl;
|
||||
|
||||
@Schema(description = "坐席数", example = "8")
|
||||
@Min(value = 1, message = "坐席数不能小于 {value}")
|
||||
private Integer seatCount;
|
||||
|
||||
@Schema(description = "设备列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2]")
|
||||
@NotNull(message = "设备列表不能为空")
|
||||
private List<@NotNull(message = "设备列表不能为空") Integer> equipments;
|
||||
|
||||
@Schema(description = "允许预定", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
@NotNull(message = "允许预定不能为空")
|
||||
private Boolean allowBooking;
|
||||
|
||||
@Schema(description = "预定需审批", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
|
||||
@NotNull(message = "预定需审批不能为空")
|
||||
private Boolean needApproval;
|
||||
|
||||
@Schema(description = "可预定范围", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
@NotNull(message = "可预定范围不能为空")
|
||||
@InEnum(value = OaMeetingRoomBookingScopeEnum.class, message = "可预定范围不正确")
|
||||
private Integer bookingScope;
|
||||
|
||||
@Schema(description = "可预定成员用户编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2]")
|
||||
@NotNull(message = "可预定成员不能为空")
|
||||
private List<@NotNull(message = "可预定成员不能为空") Long> bookingUserIds;
|
||||
|
||||
@Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "显示顺序不能为空")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "备注", example = "请提前安排会议室")
|
||||
@Size(max = 200, message = "备注长度不能超过 {max} 个字符")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "附件地址列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[\"https://www.iocoder.cn/meeting.pdf\"]")
|
||||
@NotNull(message = "附件地址列表不能为空")
|
||||
private List<@NotBlank(message = "附件地址列表不能为空") String> fileUrls;
|
||||
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.note;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.note.vo.category.OaNoteCategoryRespVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.note.vo.category.OaNoteCategorySaveReqVO;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.note.OaNoteCategoryDO;
|
||||
import cn.iocoder.yudao.module.oa.service.note.OaNoteCategoryService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - OA 笔记目录")
|
||||
@RestController
|
||||
@RequestMapping("/oa/note-category")
|
||||
@Validated
|
||||
public class OaNoteCategoryController {
|
||||
|
||||
@Resource
|
||||
private OaNoteCategoryService noteCategoryService;
|
||||
|
||||
// ==================== 目录维护 ====================
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建笔记目录")
|
||||
public CommonResult<Long> createNoteCategory(@Valid @RequestBody OaNoteCategorySaveReqVO createReqVO) {
|
||||
return success(noteCategoryService.createNoteCategory(createReqVO, getLoginUserId()));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新笔记目录")
|
||||
public CommonResult<Boolean> updateNoteCategory(@Valid @RequestBody OaNoteCategorySaveReqVO updateReqVO) {
|
||||
noteCategoryService.updateNoteCategory(updateReqVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除笔记目录")
|
||||
@Parameter(name = "id", description = "目录编号", required = true, example = "1024")
|
||||
public CommonResult<Boolean> deleteNoteCategory(@RequestParam("id") Long id) {
|
||||
noteCategoryService.deleteNoteCategory(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
// ==================== 目录查询 ====================
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得笔记目录")
|
||||
@Parameter(name = "id", description = "目录编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:note:query')")
|
||||
public CommonResult<OaNoteCategoryRespVO> getNoteCategory(@RequestParam("id") Long id) {
|
||||
OaNoteCategoryDO category = noteCategoryService.getNoteCategory(id, getLoginUserId());
|
||||
return success(BeanUtils.toBean(category, OaNoteCategoryRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/simple-list")
|
||||
@Operation(summary = "获得笔记目录精简列表")
|
||||
public CommonResult<List<OaNoteCategoryRespVO>> getSimpleNoteCategoryList() {
|
||||
List<OaNoteCategoryDO> categories = noteCategoryService.getNoteCategoryList(getLoginUserId());
|
||||
return success(convertList(categories, category -> new OaNoteCategoryRespVO()
|
||||
.setId(category.getId()).setName(category.getName())));
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得笔记目录列表")
|
||||
@PreAuthorize("@ss.hasPermission('oa:note:query')")
|
||||
public CommonResult<List<OaNoteCategoryRespVO>> getNoteCategoryList() {
|
||||
List<OaNoteCategoryDO> categories = noteCategoryService.getNoteCategoryList(getLoginUserId());
|
||||
return success(BeanUtils.toBean(categories, OaNoteCategoryRespVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.note;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.note.vo.OaNoteFavoriteUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.note.vo.OaNotePageReqVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.note.vo.OaNoteRespVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.note.vo.OaNoteSaveReqVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.note.vo.OaNoteShareUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.note.OaNoteCategoryDO;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.note.OaNoteDO;
|
||||
import cn.iocoder.yudao.module.oa.service.note.OaNoteCategoryService;
|
||||
import cn.iocoder.yudao.module.oa.service.note.OaNoteService;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - OA 笔记")
|
||||
@RestController
|
||||
@RequestMapping("/oa/note")
|
||||
@Validated
|
||||
public class OaNoteController {
|
||||
|
||||
@Resource
|
||||
private OaNoteService noteService;
|
||||
@Resource
|
||||
private OaNoteCategoryService noteCategoryService;
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
// ==================== 我的笔记 ====================
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建笔记")
|
||||
@PreAuthorize("@ss.hasPermission('oa:note:create')")
|
||||
public CommonResult<Long> createNote(@Valid @RequestBody OaNoteSaveReqVO createReqVO) {
|
||||
return success(noteService.createNote(createReqVO, getLoginUserId()));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新笔记")
|
||||
@PreAuthorize("@ss.hasPermission('oa:note:update')")
|
||||
public CommonResult<Boolean> updateNote(@Valid @RequestBody OaNoteSaveReqVO updateReqVO) {
|
||||
noteService.updateNote(updateReqVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除笔记")
|
||||
@Parameter(name = "id", description = "笔记编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:note:delete')")
|
||||
public CommonResult<Boolean> deleteNote(@RequestParam("id") Long id) {
|
||||
noteService.deleteNote(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-received")
|
||||
@Operation(summary = "移除收到的共享笔记")
|
||||
@Parameter(name = "id", description = "笔记编号", required = true, example = "1024")
|
||||
public CommonResult<Boolean> deleteReceivedNote(@RequestParam("id") Long id) {
|
||||
noteService.deleteReceivedNote(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/update-favorite")
|
||||
@Operation(summary = "更新笔记收藏状态")
|
||||
public CommonResult<Boolean> updateNoteFavorite(@Valid @RequestBody OaNoteFavoriteUpdateReqVO updateReqVO) {
|
||||
noteService.updateNoteFavorite(updateReqVO.getId(), updateReqVO.getFavorite(), getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/update-share")
|
||||
@Operation(summary = "更新笔记共享接收人")
|
||||
@PreAuthorize("@ss.hasPermission('oa:note:update')")
|
||||
public CommonResult<Boolean> updateNoteShare(@Valid @RequestBody OaNoteShareUpdateReqVO updateReqVO) {
|
||||
noteService.updateNoteShare(updateReqVO.getId(), updateReqVO.getReceiverUserIds(), getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
// ==================== 公共查询(我的笔记、共享给我) ====================
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得笔记详情")
|
||||
@Parameter(name = "id", description = "笔记编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:note:query')")
|
||||
public CommonResult<OaNoteRespVO> getNote(@RequestParam("id") Long id) {
|
||||
OaNoteDO note = noteService.getNote(id, getLoginUserId());
|
||||
return success(buildNoteRespVO(note));
|
||||
}
|
||||
|
||||
@GetMapping("/my-page")
|
||||
@Operation(summary = "获得我的笔记分页")
|
||||
@PreAuthorize("@ss.hasPermission('oa:note:query')")
|
||||
public CommonResult<PageResult<OaNoteRespVO>> getMyNotePage(@Valid OaNotePageReqVO pageReqVO) {
|
||||
PageResult<OaNoteDO> pageResult = noteService.getMyNotePage(pageReqVO, getLoginUserId());
|
||||
return success(new PageResult<>(buildNoteRespVOList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/received-page")
|
||||
@Operation(summary = "获得共享给我的笔记分页")
|
||||
@PreAuthorize("@ss.hasPermission('oa:note:query')")
|
||||
public CommonResult<PageResult<OaNoteRespVO>> getReceivedNotePage(@Valid OaNotePageReqVO pageReqVO) {
|
||||
PageResult<OaNoteDO> pageResult = noteService.getReceivedNotePage(pageReqVO, getLoginUserId());
|
||||
return success(new PageResult<>(buildNoteRespVOList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
// ==================== 拼接 VO ====================
|
||||
|
||||
/**
|
||||
* 拼接笔记详情
|
||||
*
|
||||
* @param note 笔记
|
||||
* @return 笔记响应
|
||||
*/
|
||||
private OaNoteRespVO buildNoteRespVO(OaNoteDO note) {
|
||||
if (note == null) {
|
||||
return null;
|
||||
}
|
||||
return CollUtil.getFirst(buildNoteRespVOList(Collections.singletonList(note)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接笔记响应列表
|
||||
*
|
||||
* @param notes 笔记列表
|
||||
* @return 笔记响应列表
|
||||
*/
|
||||
private List<OaNoteRespVO> buildNoteRespVOList(List<OaNoteDO> notes) {
|
||||
if (CollUtil.isEmpty(notes)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 1. 查询接收关系、用户及目录信息
|
||||
Map<Long, List<Long>> receiverUserIdListMap = noteService.getNoteReceiverUserIdListMap(
|
||||
convertSet(notes, OaNoteDO::getId));
|
||||
Set<Long> userIds = convertSet(notes, note -> NumberUtils.parseLong(note.getCreator()));
|
||||
receiverUserIdListMap.values().forEach(userIds::addAll);
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
|
||||
Map<Long, OaNoteCategoryDO> categoryMap = noteCategoryService.getNoteCategoryMap(convertSet(notes, OaNoteDO::getCategoryId));
|
||||
// 2. 转换笔记并拼接展示字段
|
||||
return convertList(notes, noteDO -> {
|
||||
OaNoteRespVO note = BeanUtils.toBean(noteDO, OaNoteRespVO.class)
|
||||
.setCreatorUserId(NumberUtils.parseLong(noteDO.getCreator()));
|
||||
MapUtils.findAndThen(userMap, note.getCreatorUserId(), user -> note.setCreatorUserName(user.getNickname()));
|
||||
MapUtils.findAndThen(categoryMap, note.getCategoryId(), category -> note.setCategoryName(category.getName()));
|
||||
// 持有关系包含创建人,共享名单仅展示其他接收人
|
||||
List<Long> receiverUserIds = filterList(receiverUserIdListMap.getOrDefault(note.getId(), Collections.emptyList()),
|
||||
userId -> ObjectUtil.notEqual(userId, note.getCreatorUserId()));
|
||||
note.setReceiverUserIds(receiverUserIds);
|
||||
note.setReceiverUserNames(convertList(receiverUserIds, receiverUserId -> {
|
||||
AdminUserRespDTO receiverUser = userMap.get(receiverUserId);
|
||||
return receiverUser != null ? receiverUser.getNickname() : null;
|
||||
}));
|
||||
return note;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.note.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - OA 笔记收藏状态修改 Request VO")
|
||||
@Data
|
||||
public class OaNoteFavoriteUpdateReqVO {
|
||||
|
||||
@Schema(description = "笔记编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "笔记编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "是否收藏", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
|
||||
@NotNull(message = "收藏状态不能为空")
|
||||
private Boolean favorite;
|
||||
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.note.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.oa.enums.note.OaNoteTypeEnum;
|
||||
import cn.iocoder.yudao.module.oa.enums.schedule.OaPriorityEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
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 = "管理后台 - OA 笔记分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class OaNotePageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "标题", example = "会议纪要")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "目录编号", example = "1024")
|
||||
private Long categoryId;
|
||||
|
||||
@Schema(description = "笔记类型", example = "1")
|
||||
@InEnum(value = OaNoteTypeEnum.class, message = "笔记类型必须是 {value}")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "优先级", example = "1")
|
||||
@InEnum(value = OaPriorityEnum.class, message = "优先级必须是 {value}")
|
||||
private Integer priority;
|
||||
|
||||
@Schema(description = "是否收藏", example = "false")
|
||||
private Boolean favorite;
|
||||
|
||||
@Schema(description = "创建时间范围", example = "[\"2026-09-14 00:00:00\", \"2026-09-14 23:59:59\"]")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.note.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - OA 笔记 Response VO")
|
||||
@Data
|
||||
public class OaNoteRespVO {
|
||||
|
||||
@Schema(description = "笔记编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "创建人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long creatorUserId;
|
||||
|
||||
@Schema(description = "创建人用户昵称", example = "芋道源码")
|
||||
private String creatorUserName;
|
||||
|
||||
@Schema(description = "目录编号", example = "1024")
|
||||
private Long categoryId;
|
||||
|
||||
@Schema(description = "目录名称", example = "工作")
|
||||
private String categoryName;
|
||||
|
||||
@Schema(description = "笔记类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "优先级", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer priority;
|
||||
|
||||
@Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "项目会议纪要")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "内容", example = "记录本周会议结论与待办事项。")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "是否收藏", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
|
||||
private Boolean favorite;
|
||||
|
||||
@Schema(description = "附件地址列表", example = "[\"https://example.com/file.pdf\"]")
|
||||
private List<String> fileUrls;
|
||||
|
||||
@Schema(description = "接收人用户编号列表", example = "[1, 2]")
|
||||
private List<Long> receiverUserIds;
|
||||
|
||||
@Schema(description = "接收人用户昵称列表", example = "[\"张三\", \"李四\"]")
|
||||
private List<String> receiverUserNames;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64", example = "1789347600000")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.note.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.oa.enums.note.OaNoteTypeEnum;
|
||||
import cn.iocoder.yudao.module.oa.enums.schedule.OaPriorityEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - OA 笔记新增/修改 Request VO")
|
||||
@Data
|
||||
public class OaNoteSaveReqVO {
|
||||
|
||||
@Schema(description = "笔记编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "目录编号", example = "1024")
|
||||
private Long categoryId;
|
||||
|
||||
@Schema(description = "笔记类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "笔记类型不能为空")
|
||||
@InEnum(value = OaNoteTypeEnum.class, message = "笔记类型必须是 {value}")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "优先级", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "优先级不能为空")
|
||||
@InEnum(value = OaPriorityEnum.class, message = "优先级必须是 {value}")
|
||||
@Max(value = 2, message = "笔记优先级只能为一般或重要")
|
||||
private Integer priority;
|
||||
|
||||
@Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "项目会议纪要")
|
||||
@NotBlank(message = "标题不能为空")
|
||||
@Size(max = 255, message = "标题长度不能超过 255 个字符")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "记录本周会议结论与待办事项。")
|
||||
@NotBlank(message = "笔记内容不能为空")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "附件地址列表", example = "[\"https://example.com/file.pdf\"]")
|
||||
private List<@NotBlank(message = "附件地址不能为空") @Size(max = 512, message = "附件地址长度不能超过 512 个字符") String> fileUrls;
|
||||
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.note.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - OA 笔记共享修改 Request VO")
|
||||
@Data
|
||||
public class OaNoteShareUpdateReqVO {
|
||||
|
||||
@Schema(description = "笔记编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "笔记编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "接收人用户编号列表,空列表表示取消全部共享", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2]")
|
||||
@NotNull(message = "接收人列表不能为空")
|
||||
private List<@NotNull(message = "接收人编号不能为空") Long> receiverUserIds;
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.note.vo.category;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - OA 笔记目录 Response VO")
|
||||
@Data
|
||||
public class OaNoteCategoryRespVO {
|
||||
|
||||
@Schema(description = "目录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "目录名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "工作")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "显示顺序", example = "10")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "创建时间", type = "integer", format = "int64", example = "1789347600000")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.note.vo.category;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - OA 笔记目录新增/修改 Request VO")
|
||||
@Data
|
||||
public class OaNoteCategorySaveReqVO {
|
||||
|
||||
@Schema(description = "目录编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "目录名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "工作")
|
||||
@NotBlank(message = "目录名称不能为空")
|
||||
@Size(max = 255, message = "目录名称长度不能超过 255 个字符")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
@NotNull(message = "显示顺序不能为空")
|
||||
private Integer sort;
|
||||
|
||||
}
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.officialdoc;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.officialdoc.vo.receive.OaOfficialDocReceivePageReqVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.officialdoc.vo.receive.OaOfficialDocReceiveRespVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.officialdoc.vo.receive.OaOfficialDocReceiveSaveReqVO;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.officialdoc.OaOfficialDocReceiveDO;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.officialdoc.OaOfficialDocSendDO;
|
||||
import cn.iocoder.yudao.module.oa.service.officialdoc.OaOfficialDocReceiveService;
|
||||
import cn.iocoder.yudao.module.oa.service.officialdoc.OaOfficialDocSendService;
|
||||
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.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - 公文收文")
|
||||
@RestController
|
||||
@RequestMapping("/oa/officialdoc-receive")
|
||||
@Validated
|
||||
public class OaOfficialDocReceiveController {
|
||||
|
||||
@Resource
|
||||
private OaOfficialDocReceiveService officialDocReceiveService;
|
||||
@Resource
|
||||
private OaOfficialDocSendService officialDocSendService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建公文收文")
|
||||
@PreAuthorize("@ss.hasPermission('oa:officialdoc-receive:create')")
|
||||
public CommonResult<Long> createOfficialDocReceive(@Valid @RequestBody OaOfficialDocReceiveSaveReqVO reqVO) {
|
||||
return success(officialDocReceiveService.createOfficialDocReceive(reqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新公文收文")
|
||||
@PreAuthorize("@ss.hasPermission('oa:officialdoc-receive:update')")
|
||||
public CommonResult<Boolean> updateOfficialDocReceive(@Valid @RequestBody OaOfficialDocReceiveSaveReqVO reqVO) {
|
||||
officialDocReceiveService.updateOfficialDocReceive(reqVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除公文收文")
|
||||
@Parameter(name = "id", description = "收文编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:officialdoc-receive:delete')")
|
||||
public CommonResult<Boolean> deleteOfficialDocReceive(@RequestParam("id") Long id) {
|
||||
officialDocReceiveService.deleteOfficialDocReceive(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/submit")
|
||||
@Operation(summary = "提交公文收文审批")
|
||||
@Parameter(name = "id", description = "收文编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:officialdoc-receive:update')")
|
||||
public CommonResult<Boolean> submitOfficialDocReceive(@RequestParam("id") Long id) {
|
||||
officialDocReceiveService.submitOfficialDocReceive(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/cancel")
|
||||
@Operation(summary = "撤销公文收文审批")
|
||||
@Parameter(name = "id", description = "收文编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:officialdoc-receive:update')")
|
||||
public CommonResult<Boolean> cancelOfficialDocReceive(@RequestParam("id") Long id) {
|
||||
officialDocReceiveService.cancelOfficialDocReceive(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/claim")
|
||||
@Operation(summary = "签收公文收文")
|
||||
@Parameter(name = "id", description = "收文编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:officialdoc-receive:update')")
|
||||
public CommonResult<Boolean> claimOfficialDocReceive(@RequestParam("id") Long id) {
|
||||
officialDocReceiveService.claimOfficialDocReceive(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得公文收文分页列表")
|
||||
@PreAuthorize("@ss.hasPermission('oa:officialdoc-receive:query')")
|
||||
public CommonResult<PageResult<OaOfficialDocReceiveRespVO>> getOfficialDocReceivePage(
|
||||
@Valid OaOfficialDocReceivePageReqVO pageReqVO) {
|
||||
PageResult<OaOfficialDocReceiveDO> pageResult = officialDocReceiveService.getOfficialDocReceivePage(pageReqVO, getLoginUserId());
|
||||
return success(new PageResult<>(buildOfficialDocReceiveRespVOList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得公文收文详情")
|
||||
@Parameter(name = "id", description = "收文编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:officialdoc-receive:query')")
|
||||
public CommonResult<OaOfficialDocReceiveRespVO> getOfficialDocReceive(@RequestParam("id") Long id) {
|
||||
OaOfficialDocReceiveDO receive = officialDocReceiveService.getOfficialDocReceive(id);
|
||||
return success(buildOfficialDocReceiveRespVO(receive));
|
||||
}
|
||||
|
||||
// ==================== 拼接 VO ====================
|
||||
|
||||
/**
|
||||
* 拼接收文详情及关联发文信息
|
||||
*
|
||||
* @param receive 收文
|
||||
* @return 收文详情
|
||||
*/
|
||||
private OaOfficialDocReceiveRespVO buildOfficialDocReceiveRespVO(OaOfficialDocReceiveDO receive) {
|
||||
if (receive == null) {
|
||||
return null;
|
||||
}
|
||||
return CollUtil.getFirst(buildOfficialDocReceiveRespVOList(Collections.singletonList(receive)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接公文收文的用户、部门名称及关联发文信息
|
||||
*
|
||||
* @param receives 公文收文列表
|
||||
* @return 公文收文响应列表
|
||||
*/
|
||||
private List<OaOfficialDocReceiveRespVO> buildOfficialDocReceiveRespVOList(List<OaOfficialDocReceiveDO> receives) {
|
||||
if (CollUtil.isEmpty(receives)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 1.1 批量查询关联发文,手工收文无需查询
|
||||
Map<Long, OaOfficialDocSendDO> sendMap = officialDocSendService.getOfficialDocSendMap(
|
||||
convertSet(receives, OaOfficialDocReceiveDO::getSendId));
|
||||
// 1.2 批量查询主办人及签发人
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSetByFlatMap(receives, receive -> {
|
||||
OaOfficialDocSendDO send = sendMap.get(receive.getSendId());
|
||||
return Stream.of(receive.getHandlerUserId(), send != null ? send.getSignerUserId() : null);
|
||||
}));
|
||||
// 1.3 批量查询收发文部门
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSetByFlatMap(receives, receive -> {
|
||||
OaOfficialDocSendDO send = sendMap.get(receive.getSendId());
|
||||
return Stream.of(receive.getReceiveDeptId(), send != null ? send.getSendDeptId() : null);
|
||||
}));
|
||||
// TODO DONE @AI:移除重复解释实现方式的注释,OA 同类说明同步精简。
|
||||
// 2. 转换并补充展示字段
|
||||
return convertList(receives, receive -> {
|
||||
OaOfficialDocReceiveRespVO respVO = BeanUtils.toBean(receive, OaOfficialDocReceiveRespVO.class);
|
||||
MapUtils.findAndThen(userMap, receive.getHandlerUserId(), user -> respVO.setHandlerName(user.getNickname()));
|
||||
MapUtils.findAndThen(deptMap, receive.getReceiveDeptId(), dept -> respVO.setReceiveDeptName(dept.getName()));
|
||||
MapUtils.findAndThen(sendMap, receive.getSendId(), send -> {
|
||||
MapUtils.findAndThen(deptMap, send.getSendDeptId(), dept -> respVO.setSendDeptName(dept.getName()));
|
||||
MapUtils.findAndThen(userMap, send.getSignerUserId(), user -> respVO.setSignerName(user.getNickname()));
|
||||
respVO.setIssueTime(send.getIssueTime()).setDisclosureType(send.getDisclosureType());
|
||||
});
|
||||
return respVO;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.officialdoc;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.officialdoc.vo.send.OaOfficialDocSendPageReqVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.officialdoc.vo.send.OaOfficialDocSendRespVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.officialdoc.vo.send.OaOfficialDocSendSaveReqVO;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.officialdoc.OaOfficialDocSendDO;
|
||||
import cn.iocoder.yudao.module.oa.service.officialdoc.OaOfficialDocSendService;
|
||||
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.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - 公文发文")
|
||||
@RestController
|
||||
@RequestMapping("/oa/officialdoc-send")
|
||||
@Validated
|
||||
public class OaOfficialDocSendController {
|
||||
|
||||
@Resource
|
||||
private OaOfficialDocSendService officialDocSendService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建公文发文")
|
||||
@PreAuthorize("@ss.hasPermission('oa:officialdoc-send:create')")
|
||||
public CommonResult<Long> createOfficialDocSend(@Valid @RequestBody OaOfficialDocSendSaveReqVO reqVO) {
|
||||
return success(officialDocSendService.createOfficialDocSend(reqVO, getLoginUserId()));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新公文发文")
|
||||
@PreAuthorize("@ss.hasPermission('oa:officialdoc-send:update')")
|
||||
public CommonResult<Boolean> updateOfficialDocSend(@Valid @RequestBody OaOfficialDocSendSaveReqVO reqVO) {
|
||||
officialDocSendService.updateOfficialDocSend(reqVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除公文发文")
|
||||
@Parameter(name = "id", description = "发文编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:officialdoc-send:delete')")
|
||||
public CommonResult<Boolean> deleteOfficialDocSend(@RequestParam("id") Long id) {
|
||||
officialDocSendService.deleteOfficialDocSend(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/submit")
|
||||
@Operation(summary = "提交公文发文审批")
|
||||
@Parameter(name = "id", description = "发文编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:officialdoc-send:update')")
|
||||
public CommonResult<Boolean> submitOfficialDocSend(@RequestParam("id") Long id) {
|
||||
officialDocSendService.submitOfficialDocSend(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/cancel")
|
||||
@Operation(summary = "撤销公文发文审批")
|
||||
@Parameter(name = "id", description = "发文编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:officialdoc-send:update')")
|
||||
public CommonResult<Boolean> cancelOfficialDocSend(@RequestParam("id") Long id) {
|
||||
officialDocSendService.cancelOfficialDocSend(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得公文发文分页列表")
|
||||
@PreAuthorize("@ss.hasPermission('oa:officialdoc-send:query')")
|
||||
public CommonResult<PageResult<OaOfficialDocSendRespVO>> getOfficialDocSendPage(
|
||||
@Valid OaOfficialDocSendPageReqVO pageReqVO) {
|
||||
PageResult<OaOfficialDocSendDO> pageResult = officialDocSendService.getOfficialDocSendPage(pageReqVO, getLoginUserId());
|
||||
return success(new PageResult<>(buildOfficialDocSendRespVOList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得公文发文详情")
|
||||
@Parameter(name = "id", description = "发文编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasAnyPermissions('oa:officialdoc-send:query', 'oa:officialdoc-receive:query')")
|
||||
public CommonResult<OaOfficialDocSendRespVO> getOfficialDocSend(@RequestParam("id") Long id) {
|
||||
OaOfficialDocSendDO send = officialDocSendService.getOfficialDocSend(id);
|
||||
return success(buildOfficialDocSendRespVO(send));
|
||||
}
|
||||
|
||||
// ==================== 拼接 VO ====================
|
||||
|
||||
/**
|
||||
* 拼接公文发文详情
|
||||
*
|
||||
* @param send 公文发文
|
||||
* @return 公文发文响应
|
||||
*/
|
||||
private OaOfficialDocSendRespVO buildOfficialDocSendRespVO(OaOfficialDocSendDO send) {
|
||||
if (send == null) {
|
||||
return null;
|
||||
}
|
||||
return CollUtil.getFirst(buildOfficialDocSendRespVOList(Collections.singletonList(send)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接公文发文的用户、部门名称
|
||||
*
|
||||
* @param sends 公文发文列表
|
||||
* @return 公文发文响应列表
|
||||
*/
|
||||
private List<OaOfficialDocSendRespVO> buildOfficialDocSendRespVOList(List<OaOfficialDocSendDO> sends) {
|
||||
if (CollUtil.isEmpty(sends)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 1.1 收集发文、主送和抄送部门编号
|
||||
Set<Long> deptIds = convertSet(sends, OaOfficialDocSendDO::getSendDeptId);
|
||||
sends.forEach(send -> {
|
||||
if (CollUtil.isNotEmpty(send.getMainDeptIds())) {
|
||||
deptIds.addAll(send.getMainDeptIds());
|
||||
}
|
||||
if (CollUtil.isNotEmpty(send.getCopyDeptIds())) {
|
||||
deptIds.addAll(send.getCopyDeptIds());
|
||||
}
|
||||
});
|
||||
// 1.2 批量查询签发人和部门
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(sends, OaOfficialDocSendDO::getSignerUserId));
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(deptIds);
|
||||
|
||||
// 2. 转换并补充展示字段
|
||||
return convertList(sends, send -> {
|
||||
OaOfficialDocSendRespVO respVO = BeanUtils.toBean(send, OaOfficialDocSendRespVO.class);
|
||||
MapUtils.findAndThen(userMap, send.getSignerUserId(), user -> respVO.setSignerName(user.getNickname()));
|
||||
MapUtils.findAndThen(deptMap, send.getSendDeptId(), dept -> respVO.setSendDeptName(dept.getName()));
|
||||
respVO.setMainDeptNames(convertList(send.getMainDeptIds(), id -> {
|
||||
DeptRespDTO dept = deptMap.get(id);
|
||||
return dept != null ? dept.getName() : null;
|
||||
}));
|
||||
respVO.setCopyDeptNames(convertList(send.getCopyDeptIds(), id -> {
|
||||
DeptRespDTO dept = deptMap.get(id);
|
||||
return dept != null ? dept.getName() : null;
|
||||
}));
|
||||
return respVO;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.officialdoc;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.officialdoc.vo.template.OaOfficialDocTemplatePageReqVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.officialdoc.vo.template.OaOfficialDocTemplateRespVO;
|
||||
import cn.iocoder.yudao.module.oa.controller.admin.officialdoc.vo.template.OaOfficialDocTemplateSaveReqVO;
|
||||
import cn.iocoder.yudao.module.oa.dal.dataobject.officialdoc.OaOfficialDocTemplateDO;
|
||||
import cn.iocoder.yudao.module.oa.service.officialdoc.OaOfficialDocTemplateService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
|
||||
@Tag(name = "管理后台 - 套红模板")
|
||||
@RestController
|
||||
@RequestMapping("/oa/officialdoc-template")
|
||||
@Validated
|
||||
public class OaOfficialDocTemplateController {
|
||||
|
||||
@Resource
|
||||
private OaOfficialDocTemplateService officialDocTemplateService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建套红模板")
|
||||
@PreAuthorize("@ss.hasPermission('oa:officialdoc-template:create')")
|
||||
public CommonResult<Long> createOfficialDocTemplate(@Valid @RequestBody OaOfficialDocTemplateSaveReqVO reqVO) {
|
||||
return success(officialDocTemplateService.createOfficialDocTemplate(reqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新套红模板")
|
||||
@PreAuthorize("@ss.hasPermission('oa:officialdoc-template:update')")
|
||||
public CommonResult<Boolean> updateOfficialDocTemplate(@Valid @RequestBody OaOfficialDocTemplateSaveReqVO reqVO) {
|
||||
officialDocTemplateService.updateOfficialDocTemplate(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除套红模板")
|
||||
@Parameter(name = "id", description = "模板编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:officialdoc-template:delete')")
|
||||
public CommonResult<Boolean> deleteOfficialDocTemplate(@RequestParam("id") Long id) {
|
||||
officialDocTemplateService.deleteOfficialDocTemplate(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得套红模板分页列表")
|
||||
@PreAuthorize("@ss.hasPermission('oa:officialdoc-template:query')")
|
||||
public CommonResult<PageResult<OaOfficialDocTemplateRespVO>> getOfficialDocTemplatePage(
|
||||
@Valid OaOfficialDocTemplatePageReqVO pageReqVO) {
|
||||
PageResult<OaOfficialDocTemplateDO> pageResult = officialDocTemplateService.getOfficialDocTemplatePage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, OaOfficialDocTemplateRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/simple-list")
|
||||
@Operation(summary = "获得套红模板精简列表")
|
||||
public CommonResult<List<OaOfficialDocTemplateRespVO>> getSimpleOfficialDocTemplateList() {
|
||||
List<OaOfficialDocTemplateDO> templates = officialDocTemplateService.getOfficialDocTemplateList(CommonStatusEnum.ENABLE.getStatus());
|
||||
return success(convertList(templates, template -> new OaOfficialDocTemplateRespVO()
|
||||
.setId(template.getId()).setName(template.getName())));
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得套红模板详情")
|
||||
@Parameter(name = "id", description = "模板编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oa:officialdoc-template:query')")
|
||||
public CommonResult<OaOfficialDocTemplateRespVO> getOfficialDocTemplate(@RequestParam("id") Long id) {
|
||||
OaOfficialDocTemplateDO template = officialDocTemplateService.getOfficialDocTemplate(id);
|
||||
return success(BeanUtils.toBean(template, OaOfficialDocTemplateRespVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.officialdoc.vo.receive;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Schema(description = "管理后台 - 公文收文分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class OaOfficialDocReceivePageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "公文标题", example = "关于开展办公用品盘点的通知")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "来文字号", example = "芋办〔2026〕1号")
|
||||
private String documentNo;
|
||||
|
||||
@Schema(description = "流程状态", example = "-1")
|
||||
private Integer status;
|
||||
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.officialdoc.vo.receive;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 公文收文 Response VO")
|
||||
@Data
|
||||
public class OaOfficialDocReceiveRespVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "来源发文编号", example = "1024")
|
||||
private Long sendId;
|
||||
|
||||
@Schema(description = "发文部门", example = "行政部")
|
||||
private String sendDeptName;
|
||||
|
||||
@Schema(description = "发文日期", type = "integer", format = "int64", example = "1789347600000")
|
||||
private LocalDateTime issueTime;
|
||||
|
||||
@Schema(description = "签发人", example = "张三")
|
||||
private String signerName;
|
||||
|
||||
@Schema(description = "公开类别", example = "0")
|
||||
private Integer disclosureType;
|
||||
|
||||
@Schema(description = "收文类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
private Integer receiveType;
|
||||
|
||||
@Schema(description = "收文时间", type = "integer", format = "int64", example = "1789347600000")
|
||||
private LocalDateTime receiveTime;
|
||||
|
||||
@Schema(description = "收文部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long receiveDeptId;
|
||||
|
||||
@Schema(description = "主办人用户编号", example = "1024")
|
||||
private Long handlerUserId;
|
||||
|
||||
@Schema(description = "领导批示", example = "请行政部牵头办理")
|
||||
private String instruction;
|
||||
|
||||
@Schema(description = "办理结果", example = "已完成盘点并归档")
|
||||
private String result;
|
||||
|
||||
@Schema(description = "办理期限", type = "integer", format = "int64", example = "1789347600000")
|
||||
private LocalDateTime deadlineTime;
|
||||
|
||||
@Schema(description = "内容摘要", example = "本周已完成办公用品盘点")
|
||||
private String summary;
|
||||
|
||||
@Schema(description = "备注", example = "请提前联系行政部确认")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "附件地址列表", example = "[\"https://example.com/file.pdf\"]")
|
||||
private List<String> fileUrls;
|
||||
|
||||
@Schema(description = "单据编号", example = "CC202609140001")
|
||||
private String no;
|
||||
|
||||
@Schema(description = "公文标题", example = "关于开展办公用品盘点的通知")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "来文字号", example = "芋办〔2026〕1号")
|
||||
private String documentNo;
|
||||
|
||||
@Schema(description = "密级", example = "0")
|
||||
private Integer secrecyLevel;
|
||||
|
||||
@Schema(description = "紧急程度", example = "0")
|
||||
private Integer urgencyLevel;
|
||||
|
||||
@Schema(description = "正式公文地址", example = "https://example.com/file.pdf")
|
||||
private String formalFileUrl;
|
||||
|
||||
@Schema(description = "收文部门", example = "行政部")
|
||||
private String receiveDeptName;
|
||||
|
||||
@Schema(description = "主办人", example = "张三")
|
||||
private String handlerName;
|
||||
|
||||
@Schema(description = "审批状态", example = "-1")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "办理状态", example = "0")
|
||||
private Integer handleStatus;
|
||||
|
||||
@Schema(description = "流程实例编号", example = "1024")
|
||||
private String processInstanceId;
|
||||
|
||||
@Schema(description = "创建人编号", example = "1024")
|
||||
private String creator;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64", example = "1789347600000")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
package cn.iocoder.yudao.module.oa.controller.admin.officialdoc.vo.receive;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import jakarta.validation.constraints.*;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.framework.dict.validation.InDict;
|
||||
import cn.iocoder.yudao.module.oa.enums.DictTypeConstants;
|
||||
import cn.iocoder.yudao.module.oa.enums.officialdoc.OaOfficialDocReceiveTypeEnum;
|
||||
|
||||
@Schema(description = "管理后台 - 公文收文新增/修改 Request VO")
|
||||
@Data
|
||||
public class OaOfficialDocReceiveSaveReqVO {
|
||||
|
||||
@Schema(description = "编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "公文标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "关于开展办公用品盘点的通知")
|
||||
@NotBlank(message = "公文标题不能为空")
|
||||
@Size(max = 255, message = "公文标题长度不能超过 {max} 个字符")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "来文字号", example = "芋办〔2026〕1号")
|
||||
@Size(max = 64, message = "来文字号长度不能超过 {max} 个字符")
|
||||
private String documentNo;
|
||||
|
||||
@Schema(description = "密级", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
@NotNull(message = "密级不能为空")
|
||||
@InDict(type = DictTypeConstants.OFFICIAL_DOC_SECRET_LEVEL, message = "密级必须是 {value}")
|
||||
private Integer secrecyLevel;
|
||||
|
||||
@Schema(description = "紧急程度", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
@NotNull(message = "紧急程度不能为空")
|
||||
@InDict(type = DictTypeConstants.OFFICIAL_DOC_URGENCY_LEVEL, message = "紧急程度必须是 {value}")
|
||||
private Integer urgencyLevel;
|
||||
|
||||
@Schema(description = "收文类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
@NotNull(message = "收文类型不能为空")
|
||||
@InEnum(value = OaOfficialDocReceiveTypeEnum.class, message = "收文类型必须是 {value}")
|
||||
private Integer receiveType;
|
||||
|
||||
@Schema(description = "收文时间", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64", example = "1789347600000")
|
||||
@NotNull(message = "收文时间不能为空")
|
||||
private LocalDateTime receiveTime;
|
||||
|
||||
@Schema(description = "收文部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "收文部门不能为空")
|
||||
private Long receiveDeptId;
|
||||
|
||||
@Schema(description = "主办人用户编号", example = "1024")
|
||||
private Long handlerUserId;
|
||||
|
||||
@Schema(description = "领导批示", example = "请行政部牵头办理")
|
||||
@Size(max = 2000, message = "领导批示长度不能超过 {max} 个字符")
|
||||
private String instruction;
|
||||
|
||||
@Schema(description = "办理结果", example = "已完成盘点并归档")
|
||||
@Size(max = 2000, message = "办理结果长度不能超过 {max} 个字符")
|
||||
private String result;
|
||||
|
||||
@Schema(description = "办理期限", type = "integer", format = "int64", example = "1789347600000")
|
||||
private LocalDateTime deadlineTime;
|
||||
|
||||
@Schema(description = "内容摘要", example = "本周已完成办公用品盘点")
|
||||
private String summary;
|
||||
|
||||
@Schema(description = "备注", example = "请提前联系行政部确认")
|
||||
@Size(max = 500, message = "备注长度不能超过 {max} 个字符")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "附件地址列表", example = "[\"https://example.com/file.pdf\"]")
|
||||
private List<@NotBlank(message = "附件地址不能为空") String> fileUrls;
|
||||
|
||||
@Schema(description = "正式公文地址", example = "https://example.com/file.pdf")
|
||||
@Size(max = 2048, message = "正式公文地址长度不能超过 {max} 个字符")
|
||||
private String formalFileUrl;
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user